反注入技术:防范非法 Call 调用的探讨

DLL 注入是一种常见的技术,用于向目标进程注入外部的动态链接库(DLL),以执行某些特定的操作。这种技术在恶意软件、游戏作弊等场景中被广泛使用,因此,研究和实施一些反注入技术对于提高应用程序的安全性是至关重要的。本文将介绍一种基于返回地址检测的反注入技术的实现,以防范非法的 DLL 注入。

1. 引言

在实际应用中,很多恶意注入行为都是通过劫持目标函数的调用来实现的。因此,通过检测调用栈上的返回地址,我们可以辨别调用者的身份,从而判断注入是否合法。

2. 技术细节

2.1 基本原理

通过使用 Detours 库,我们可以实现对目标函数的挂钩(hooking)。挂钩后,所有对目标函数的调用都会被重定向到我们指定的挂钩函数,这是作弊方可能会采用的一种方法。在目标函数返回前,我们可以进行一些检测,例如检查返回地址是否在合法的范围内,从而判断是否存在非法注入。

通过汇编原理我们知道,对于 call 指令其返回地址是下一条指令的地址,这会存储在程序计数寄存器(PC)中。以便于返回时,恢复控制流程。一个合法的程序过程调用,目标函数的返回地址应该位于合法的上级调用者函数的地址空间(栈帧)内,如果发生了外部注入,钩子例程通常会修改目标函数的开头几个字节,使得其导向攻击者所期望的函数入口地址上,并在执行相关处理后重新执行原函数,来达到劫持正常函数的返回值或处理流程的目的。但是,我们在原函数返回前(或者自己再实现一个返回地址导向挂钩)可以进行一些对返回地址的判断,如果程序被挂钩,返回地址在钩子函数的地址空间内,而不存在于正常例程的空间范围内。

利用返回地址检测可以在一定程度上辨别出非法的调用过程,但不是绝对和安全的防御,因为攻击者可以修改寄存器或者返回地址或者判断条件等来达到对安全手段绕过,本文只是作为一种普及讲解。并且为了安全起见,只给出最简单的判断流程,实际使用中需要更为精细化的检测流程。

2.2 实现步骤

以下是实现基于返回地址检测的反注入技术的主要步骤:

  1. 定义目标函数和挂钩函数: 在代码中定义目标函数(例如 TargetFunction)、挂钩函数(例如 HookedFunction)、返回地址计算函数、函数地址模式匹配函数。

  2. 初始化 Detours 库: 使用 Detours 库提供的函数初始化挂钩,通过模拟挂钩非法劫持原函数的执行流程。

    DetourTransactionBegin();
    DetourUpdateThread(GetCurrentThread());
    DetourAttach(&OriginalFunction, HookedFunction);
    DetourTransactionCommit();
    
  3. 调用目标函数: 此时,任何对目标函数的调用都会触发挂钩函数。

    int result = TargetFunction(42);
    
  4. 在原函数返回前执行检测流程:计算返回地址和合法的地址空间(mian 函数的入口地址到返回地址之间的空间)来判断是否存在非法调用。

    PVOID thisAddress = _ReturnAddress();
    size_t searchSize = 0x100;
    PVOID EndAddress = nullptr;
    if (!RelGetFunctionRange(thisAddress, &EndAddress, &searchSize))printf("NofoundAddress.\n");// 判断返回地址是否在内部调用方(main)范围内
    if (targetCallPtr <= lowerBounds || targetCallPtr >= uperBounds || classEndPtr != uperBounds)
    {printf("Target Function call is invalid!\n");
    }
    

  5. 卸载挂钩: 在检测完成后,卸载挂钩,使目标函数恢复正常调用。

    DetourTransactionBegin();
    DetourUpdateThread(GetCurrentThread());
    DetourDetach(&OriginalFunction, HookedFunction);
    DetourTransactionCommit();
    

3. 方法验证

3.1 验证代码

#include <iostream>
#include <Windows.h>
#include <intrin.h>
#include "detours.h"#pragma intrinsic(_ReturnAddress)
#pragma comment(lib, "detours.lib")// 模拟原函数声明
typedef int(WINAPI* OriginalFunctionType)(int);// 导出函数
extern "C" {  // only need to export C interface if// used by C++ source code__declspec(dllexport) int main(); //主函数的声明__declspec(dllexport) int WINAPI TargetFunction(int param);
}// 原始函数指针
PVOID OriginalFunction = nullptr;typedef struct FuncAddressInfo {PVOID CallFunctionAddress;PVOID BaseAddress;BOOL IsValidCall;
}FuncAddressInfo;// 获取函数区间范围(用于非调试模式)
BOOL RelGetFunctionRange(LPVOID functionAddress,LPVOID* searchAddress,size_t* stackSearchSize
)
{// 计算结束位置unsigned char refCode[2] = { 0 };for (size_t i = 0; i < *stackSearchSize; i++){memcpy(&refCode, (unsigned char*)(functionAddress) + i, sizeof(refCode));switch (refCode[0]){case 0xc3:case 0xc2:if (refCode[1] == 0xcc){*stackSearchSize = i + 1;*searchAddress = reinterpret_cast<PVOID>(i + reinterpret_cast<DWORD>(functionAddress));}break;}if (*searchAddress != nullptr) break;}return TRUE;
}// 定义目标函数
__declspec(dllexport) int WINAPI TargetFunction(int param) {printf("TargetFunction called with param: %d\n",param);size_t searchSize = 0x100;PVOID EndAddress = nullptr;PVOID thisAddress = _ReturnAddress();if (!RelGetFunctionRange(thisAddress, &EndAddress, &searchSize))printf("NofoundAddress.\n");printf("lastCallAddress: 0x%01X  AtEndAddress: 0x%01X\n", reinterpret_cast<DWORD>(thisAddress),reinterpret_cast<DWORD>(EndAddress));// 获取 main 函数入口地址PVOID mainStartAddress = &main;// 计算函数出口地址PVOID mainEndAddress = nullptr;searchSize = 0x100;if (!RelGetFunctionRange(mainStartAddress, &mainEndAddress, &searchSize))printf("NofoundAddress.\n");printf("mainStartAddress: 0x%01X  mainEndAddress: 0x%01X\n", reinterpret_cast<DWORD>(mainStartAddress), reinterpret_cast<DWORD>(mainEndAddress));const DWORD lowerBounds = reinterpret_cast<DWORD>(mainStartAddress);const DWORD uperBounds = reinterpret_cast<DWORD>(mainEndAddress);const DWORD targetCallPtr = reinterpret_cast<DWORD>(thisAddress);const DWORD classEndPtr = reinterpret_cast<DWORD>(EndAddress);// 判断返回地址是否在内部调用方(main)范围内if (targetCallPtr <= lowerBounds || targetCallPtr >= uperBounds || classEndPtr != uperBounds){printf("Target Function call is invalid!\n");}else {printf("Target Function call is valid!\n");}return param * 2;
}// 挂钩函数
int WINAPI HookedFunction(int param) {// 进行钩子毒化处理// ... ...// // 调用原始函数int result = ((OriginalFunctionType)OriginalFunction)(param);// 在这里可以添加其他处理逻辑return result;
}__declspec(dllexport) int main() {// 获取要挂钩的目标函数地址OriginalFunction = &TargetFunction;// 初始化 Detours 库DetourTransactionBegin();DetourUpdateThread(GetCurrentThread());DetourAttach(&OriginalFunction, HookedFunction);DetourTransactionCommit();// 此时调用目标函数将触发挂钩int result = TargetFunction(42);// 卸载挂钩DetourTransactionBegin();DetourUpdateThread(GetCurrentThread());DetourDetach(&OriginalFunction, HookedFunction);DetourTransactionCommit();// 此时调用目标函数将正常执行流程result = TargetFunction(42);system("pause");return 0;
}

3.2 测试过程

运行就可以看出,挂钩时候 call 的下一条指令地址不在 mian 的地址范围内,并且回溯上级调用者的返回地址不是 mian 的返回地址。(这里以MSVC release 版本进行模式匹配的,debug 版本会有跳板函数,需要更多的处理才能获得地址,并且返回值的机器码不止 0xc2cc, 0xc3cc,我这里保留了热补丁区域,所以前后都有有 0xcc )

首先找到未挂钩时候的 call 返回地址(0x391269)和上级函数返回地址(0x39127F),位于 main 中:

和检测的结果比对(源代码里面用的是 0xc3cc,应该改成 0xc355 才对,所以检测跑后面去了,但不影响结果):

挂钩时的地址的分析是类似的,但需要挂断点,拦截脱钩过程,或者用 system("pause) 暂停一下。

4. 结论

通过在目标函数的调用路径上添加检测机制,我们可以有效地防范非法的 DLL 注入。这种技术对于保护应用程序免受外部注入的威胁具有一定的效果。然而,需要注意的是,这只是一种基础的防御机制,更高级的恶意注入技术可能需要更复杂的防御手段。

5. 扩展阅读

  • Microsoft Detours 官方文档
  • Capstone 反汇编库
  • Windows API 文档(_ReturnAddress)

更新于:2023.12.24

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/503573.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

tp5 php跨域,TP5.1解决跨域

TP5.1解决跨域博客说明文章所涉及的资料来自互联网整理和个人总结&#xff0c;意在于个人学习和经验汇总&#xff0c;如有什么地方侵权&#xff0c;请联系本人删除&#xff0c;谢谢&#xff01;介绍在前后端分离开发的时候就会遇到跨域的问题&#xff0c;在本地调试的时候可能不…

如何避免_如何避免变频器受负载冲击

电工学习网&#xff1a;www.diangon.com关注电工学习网官方微信公众号“电工电气学习”&#xff0c;收获更多经验知识。为了保障变频器的安全运行&#xff0c;避免变频器受负载冲击&#xff0c;必须做好以下几点:㈠尽量保证变频器有充足的加减速时间变频器在开机或升速时&#…

哪种语言 连接 oracle,Go语言连接Oracle(就我这个最全)

综合参考了网上挺多的方案倒腾了半天终于连接好了Go都出来这么多年了还没有个Oracle的官方驱动。。。过程真的很蛋疼。。一度想放弃直接连ODBC首先交代一下运行环境和工具版本&#xff1a;WIN10MINGW64ORACLE INSTANCCLIENT_18_3 x64Jetbrins Goland看完这篇文章&#xff0c;…

补丁程序正在运行_针对微软4月14日更新补丁会导致蓝屏问题的检测及解决方法...

近期&#xff0c;我们接连收到用户求助&#xff0c;在使用电脑过程中会突然出现蓝屏问题&#xff0c;经火绒工程师分析发现&#xff0c;大部分用户出现蓝屏问题&#xff0c;是因为安装了微软于4月14日推送的补丁所致(详见下图)。目前微软方面表示正在调查相关问题。Win10系统蓝…

商城html源码_Java开源商城源码推荐,从菜鸡到大神,永远绕不开的商城系统

每个Java程序员&#xff0c;从懵逼菜鸡&#xff0c;再到懵懂菜鸟&#xff0c;再到小鸟&#xff0c;大鸟&#xff0c;最后到技术大神&#xff0c;始终绕不开商城系统&#xff0c;里面蕴含了大量的业务&#xff0c;涉及到了大量的知识点和解决方案。今天锋哥介绍一款Java开源商城…

cpu只能单通道是什么表现_【小白入门】为什么要组内存双通道?

更新时间&#xff1a;2020年5月11日 内容提要&#xff1a; 1.内存双通道的原理 2.如何组双通道很多小白在购买内存的时候&#xff0c;不知道该购买一根单16G还是两根单8G&#xff0c;看完本篇文章你将知道内存双通道的优势。1.内存双通道的原理选择两根单8G组成双通道&#xff…

雷云3灯光配置文件_雷蛇的哪种键盘最适合入手?3款最佳雷蛇键盘推荐。

更新时间2020.8.6本次主要内容是雷蛇的三款不同价位的雷蛇键盘推荐&#xff0c;有需要的小伙伴可以看一下哦&#xff0c;也许你想要入手的键盘就在其中。---------------------------------雷蛇黑寡妇蜘蛛精英版--------------------------------------黑寡妇蜘蛛精英版在猎魂光…

某些您可以编辑的区域交叠在一起 可能不能同时显示_DX200操作要领—修改与编辑程序(三十九)...

3.5 修改程序3.5.1 程序的调出1. 选择主菜单中的【程序内容】2. 选择【程序选择】–显示程序一览表。3. 选择要调出的程序3.5.2 程序相关画面程序相关画面有下面5种&#xff0c;可以确认/编辑每个程序的设定或登录。•程序标题画面显示和编辑注释、登录日期、编辑禁止的状态等。…

求二叉树中以x为根的子树的深度_还在玩耍的你,该总结啦!(本周小结之二叉树)...

给「代码随想录」一个星标吧&#xff01;❝有学习就要有总结❞本周小结本周赶上了十一国庆&#xff0c;估计大家已经对本周末没什么概念了&#xff0c;但是我们该做总结还是要做总结的。本周的主题其实是「简单但并不简单」&#xff0c;本周所选的题目大多是看一下就会的题目&a…

钉钉功能介绍_平棉集团组织召开阿里钉钉办公系统基础功能培训会

4月11日上午&#xff0c;平棉集团在总部26楼多媒体会议室组织召开阿里钉钉办公系统基础功能培训会&#xff0c;邀请河南一一信息技术公司经理杨杉前来授课。集团公司董事长张先顺及公司领导陈亚民、王仲山、王向阳、陶尚林&#xff0c;各生产经营单位主管销售工作的负责人和公司…

qemu搭建arm运行linux内核,centos使用qemu搭建ARM64运行环境

准备工作(1) linux 内核源码&#xff0c; 从github上获取git clone https://github.com/torvalds/linuxmake kernelversion(2) 交叉编译工具&#xff0c;从linaro官网(www.linaro.org)上获取解压后设置环境变量即可xz -d gcc-linaro-xxx.tar.xztar -xvf gcc-linaro-xxx.tarexpo…

java 某年某月中第几周 开始时间和结束时间_重磅!库里又要签下一超级大合同!4年2亿啊!退役时间也定了...

好家伙&#xff01;现在的超级巨星都喜欢提前续约了&#xff0c;继詹姆斯与湖人签下两年8500万美元顶薪协议后&#xff0c;库里也有望达成这一成就。当地时间周一训练结束后&#xff0c;他接受采访谈到自己的续约问题&#xff0c;表示已经和球队交流过&#xff0c;同时明确表态…

linux list 添加失败,linux运维实战案例之Argument list too long错误与解决方法

1、错误现象这是一台Mysql数据库服务器&#xff0c;在系统中运行了很多定时任务&#xff0c;今天通过crontab命令又添加了一个计划任务&#xff0c;退出时发生了如下报错&#xff1a;#crontab -e编辑完成后&#xff0c;保存退出&#xff0c;就出现下面如下图所示错误&#xff1…

软件项目周报模板_一份高质量的职场工作周报,要这样写

周报是会呼吸的痛&#xff0c;它会在每个周五来回滚动。几乎每个职场人都会为周报所折腰&#xff0c;因为周报看起来是小事&#xff0c;但又不仅仅是小事。周报&#xff0c;是职场人士对一周工作的总结和记录&#xff0c;是展示自己工作状态和成果的重要载体&#xff0c;是领导…

linux 多线程客户端服务端通信,[转载]多线程实现服务器和客户端、客户端和客户端通信;需要代码,留言...

一、实验名称动手打造自己的 IM二、实验目的1本次实验旨在锻炼大家的Socket编程能力&#xff0c;以日常生活中广泛使用的IM软件为背景&#xff0c;培养大家对于网络编程的兴趣。2、通过本次实验&#xff0c;培养linux环境下网络编程能力&#xff0c;使得我们对网络应用层的网络…

linux 设置ssh并发度,在linux如何使用ControlPersist加快SSH连接速度的问题

不经意间我们又来到了文章的学习&#xff0c;在众多学习中&#xff0c;我们的文章也许不起眼&#xff0c;但是想必大家都有很多问题吧&#xff0c;所以重要的下面我们就来讲解一下&#xff0c;大家一定要认真看奥&#xff01;&#xff01;Linux系统进行服务器配置管理迁移的时候…

linux下c标准库位置,C 标准库 IO 使用详解

其实输入与输出对于不管什么系统的设计都是异常重要的&#xff0c;比如设计 C 接口函数&#xff0c;首先要设计好输入参数、输出参数和返回值&#xff0c;接下来才能开始设计具体的实现过程。C 语言标准库提供的接口功能很有限&#xff0c;不像 Python 库。不过想把它用好也不容…

linux docker查看容器状态,Docker容器状态命令行工具——Ctop

Ctop是和Linux top展示效果类似的一个容器状态监视工具&#xff0c;Ctop可以动态的显示容器的cpu、内存、网络的使用情况。一共有两个叫Ctop的命令行工具&#xff0c;分别由GO和Python实现。Python实现的版本功能更强大一些。GO实现版本安装Linux$ wget https://github.com/bci…

plsql表设置主键_对复制实施主键约束

作者&#xff1a;Pedro Gomes 译&#xff1a;徐轶韬在本文中&#xff0c;我们介绍一个配置选项&#xff0c;该选项控制复制通道是否允许创建没有主键的表。这延续了我们最近在复制安全性方面的工作&#xff0c;在该工作中&#xff0c;我们允许用户强制执行权限检查和/或强制执行…

一键刷入twrp_小米/红米手机到手了该怎么解锁和刷 twrp

资源准备&#xff1a;1.百度搜索小米解锁申请&#xff0c;进行申请解锁并下载解锁工具。如图。文件夹里有对应的驱动&#xff0c;要安装好。2.对应机型的 twrp。在 w大的微博下找(wzsx150)或者在酷安找或者去twrp官网。3.edxp相关的包(两个)(不需要框架的可以忽略)4.手(第一步&…