反注入技术:防范非法 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,一经查实,立即删除!

相关文章

python中的字体英文名_获取中文字体的英文名字

(方法在分割线后面&#xff0c;前面叙事)今天用了很久电脑&#xff0c;突然就觉得看着Windows下Chrome的字体觉得很不舒服&#xff0c;跟Mac下的差太远了&#xff0c;于是就开始折腾怎么设置浏览器字体。先讲一下流程&#xff0c;我的操作方案是&#xff1a;下载自己喜欢的字体…

java汉字如何通过字节传输,求助,java中怎么用字节流读写汉字

该楼层疑似违规已被系统折叠 隐藏此楼查看此楼import java.io.*;public class ReadAndWrite{public static void main(String args[]){FileInputStream fin;FileOutputStream fout;int ch;try{int b;fin new FileInputStream(FileDescriptor.in);fout new FileOutputStream(F…

python 元类的call总结_Python 类与元类的深度挖掘 I【经验】

上一篇介绍了 Python 枚举类型的标准库&#xff0c;除了考虑到其实用性&#xff0c;还有一个重要的原因是其实现过程是一个非常好的学习、理解 Python 类与元类的例子。因此接下来两篇就以此为例&#xff0c;深入挖掘 Python 中类与元类背后的机制。翻开任何一本 Python 教程&a…

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

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

python中的pow怎么使用_python中的pow函数怎么用

描述pow() 方法返回 xy(x的y次方) 的值。语法以下是 math 模块 pow() 方法的语法:import mathmath.pow( x, y )内置的 pow() 方法pow(x, y[, z])函数是计算x的y次方&#xff0c;如果z在存在&#xff0c;则再对结果进行取模&#xff0c;其结果等效于pow(x,y) %z注意&#xff1a;…

nginx 配置php版本号,隐藏Apache、nginx和PHP的版本号的配置方法

最近提示说有漏洞&#xff0c;暴露apache、nginx和php的版本号。网上搜了下&#xff0c;整理的方法如下&#xff1a;首先说apache在http.conf文件里添加下面两行&#xff0c;默认是没有的ServerSignature OffServerTokens ProdServerSignature出现在Apache所产生的像404页面、目…

python爬取电子病历_利用 BERT 模型解析电子病历

项目原始地址项目地址本项目改编自此 Github 项目&#xff0c;鸣谢作者。问题描述我们希望能从患者住院期间的临床记录来预测该患者未来30天内是否会再次入院&#xff0c;该预测可以辅助医生更好的选择治疗方案并对手术风险进行评估。在临床中治疗手段常见而预后情况难以控制管…

python获取坐标颜色,python – 根据一组坐标的数据着色地图

您的第一种方法称为Voronoi diagramm对于使用D3库的javascipt,这种图表有一个解决方案为了使这个解决方案完整,我在这里粘贴M.Bostock示例中的代码var w 1280,h 800;var projection d3.geo.azimuthal().mode("equidistant").origin([-98, 38]).scale(1400).transl…

最少交换次数python_leetcode第200周赛第三题leetcode1536. 排布二进制网格的最少交换次数...

leetcode1536. 排布二进制网格的最少交换次数给你一个 n x n 的二进制网格 grid&#xff0c;每一次操作中&#xff0c;你可以选择网格的 相邻两行 进行交换。一个符合要求的网格需要满足主对角线以上的格子全部都是 0 。请你返回使网格满足要求的最少操作次数&#xff0c;如果无…

php常用功能代码,10段PHP常用功能代码(1)_PHP教程

1、使用PHP Mail函数发送Email$to "viralpatel.netgmail.com"; $subject "VIRALPATEL.net"; $body "Body of your message here you can use HTML too. e.g. &#xfe64;br&#xfe65; &#xfe64;b&#xfe65; Bold &#xfe64;/b&#xfe6…

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

电工学习网&#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系统蓝…

服务器访问oracle数据库,Oracle数据库的访问——通过不同服务器名对数据库的访问...

服务器端完成配置后&#xff0c;现在客户端可以通过不同的网络服务名配置来访问这个数据库&#xff0c;下面是一个配置示范&#xff1a;EYGLE(DESCRIPTION (ADDRESS_LIST (ADDRESS (PROTOCOL TCP)(HOST 172.16.33.11)(PORT 1521)))(CONNECT_DATA (SERVICE_NAME eygle)))JU…

oracle质数怎么算,借花献佛之使用Oracle sql求质数(笔记)

首先声明一点&#xff0c;文章内容从itpub论坛上看到的&#xff0c;原文链接 http://www.itpub.net/thread-1849398-1-1.html&#xff0c;本文主要是记录下笔记&#xff0c;原文中有更详细的分析。使用sql求质素没什么实用价值&#xff0c;重要的是思路。(一)最简单的方法思路&…

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

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

Oracle distinct后加as,【大话IT】为何加distinct之后就不走索引了

还是一样&#xff0c;16:45:44 SQL> l1 select site_id,2 count(*) sendnum3 from4 (select site_id,5 ewb_no6 from prod_send t7 where scan_time > to_date(2015-05-15, yyyy-mm-dd)8 and scan_time < to_date(2015-05-16, yyyy-mm-…

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

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

matlab 图像矢量量化,MATLAB环境下基于矢量量化的说话人识别系统(1)

第21卷第6期湖 北 工 业 大 学 学 报2006年12月Vol.21No.6 Journal of Hubei Univer sity of Technology Dec.2006[收稿日期]2006-10-13[作者简介]宋 敏(1979-),女,湖北武汉人,湖北工业大学硕士研究生,研究方向:计算机语音技术应用.[文章编号]1003-4684(2006)1220027203MATLAB …

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

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