注入系列——DLL注入

我想从DLL注入和在内存中映射和执行DLL的不同技术开始,因为我经常听到这个技术,而且非常有兴趣,不过一直没机会深入探索。

我们首先从定义DLL并理解它们的组件开始。

DLL的意思就是动态链接库(Dynamic-LinkLibraries),它是包含很多函数和数据的一种模块,可以被其他模块调用(应用或DLL)。完整的DLL文档,可以在微软的网站上找到,链接在这里。

DLL可以定义两类函数,导出函数和内部函数。导出函数可以被其他模块调用,也可以在定义它们的DLL中调用,而内部函数只能在定义它们的DLL中调用。

加载DLL的每个进程都将其映射到其虚拟地址空间。在进程将DLL加载到其虚拟地址后,它可以调用导出的DLL函数。

系统维护着每个DLL的进程引用计数。当线程加载DLL时,引用计数加1。当进程终止时,或者引用计数变为零时(仅运行时动态链接),DLL将从进程的虚拟地址空间中卸载。

只要出现以下任何一个事件,系统就会调用入口函数:

1.进程加载DLL。对于使用加载时动态链接的进程,DLL在进程初始化期间加载。对于使用运行时链接的进程,DLL在LoadLibrary或LoadLibraryEx返回之前加载。

2.进程卸载DLL。当进程终止或调用FreeLibrary函数并且引用计数变为零时,将卸载DLL。

3.在已加载DLL的进程中创建新线程。

4.已加载DLL的进程的线程正常终止,而不是使用TerminateThread或TerminateProcess来终止。

进程或线程引起函数被调用时,系统便会调用入口函数。这使得DLL可以使用入口函数在调用进程的虚拟地址空间中分配内存或打开进程可访问的句柄。

根据Microsoft的文档,有两种方法可以调用DLL中的函数:

·加载时动态链接:对导出的DLL函数进行显式调用,就好像它们是本地函数一样。这要求您将模块与包含这些函数的DLL的导入库链接。导入库为系统提供加载DLL所需的信息,并在加载应用程序时找到导出的DLL函数。

·运行时动态链接:使用LoadLibrary或LoadLibraryEx函数在运行时加载DLL。加载DLL后,模块调用GetProcAddress函数来获取导出的DLL函数的地址。该模块使用GetProcAddress返回的函数指针调用导出的DLL函数。这样就不需要导入库了。

当应用程序调用LoadLibrary或LoadLibraryEx函数时,系统会尝试查找DLL。如果搜索成功,系统将DLL模块映射到进程的虚拟地址空间并增加引用计数。如果对LoadLibrary或LoadLibraryEx的调用指定了一个DLL,其代码已经映射到调用进程的虚拟地址空间,则该函数只返回DLL的句柄并递增DLL引用计数。

当线程调用LoadLibrary或LoadLibraryEx时,系统就会调用入口函数。如果系统找不到DLL或者入口函数返回FALSE,那么LoadLibrary或LoadLibraryEx返回NULL。如果LoadLibrary或LoadLibraryEx成功,它将返回DLL模块的句柄。该进程可以使用此句柄来识别调用GetProcAddress,FreeLibrary或FreeLibraryAndExitThread的函数中的DLL。

GetModuleHandle函数返回GetProcAddress,FreeLibrary或FreeLibraryAndExitThread中使用的句柄。该进程可以使用GetProcAddress来获取DLL中导出函数的地址,这个DLL使用LoadLibrary,LoadLibraryEx或GetModuleHandle返回的DLL模块句柄。

动态链接库的可选入口,由BOOLWINAPIDllMain()函数定义。当系统启动或终止进程或线程时,它使用进程的第一个线程为每个加载的DLL调用入口函数。当使用LoadLibrary和FreeLibrary函数加载或卸载DLL时,系统也会调用DLL的入口函数。

DllMain是库定义的函数名的占位符。您必须指定生成DLL时使用的实际名称。在初始进程启动期间或调用LoadLibrary之后,系统会扫描加载的DLL列表以查找进程。对于尚未使用DLL_PROCESS_ATTACH值调用的每个DLL,系统调用DLL的入口函数。此调用是在线程引起进程地址空间发生改变时进行的,例如进程的主线程或调用LoadLibrary的线程。

因为在调用入口函数时Kernel32.dll肯定会被加载到进程地址空间中,所以在Kernel32.dll中调用函数不会导致在执行初始化代码之前使用DLL。因此,入口函数可以调用Kernel32.dll中不加载其他DLL的函数。

现在我们已经定义了一些我研究时发现有用的结构,让我们深入研究。

对于本系列中的大多数技术,我们需要做的第一件事就是找到我们想要注入的目标进程。遍历和列举进程的方法有几种,但我发现最有用的是ProcessWalking。它使用CreateToolhelp32Snapshot函数拍摄快照,快照中包含进程列表,并包含每个当前正在执行的进程的信息。您可以使用Process32First函数获取列表中第一个进程的信息。获取列表中的第一个进程后,可以使用Process32Next函数遍历进程列表来查找后面的进程,直到找到你的目标进程(例如putty.exe)或者只是将该进程添加到向量中并打印出所有的进程。找到目标进程后,我们使用OpenProcess函数来获取进程的句柄(示例如下)。

您可以在此处找到完整源代码(作为独立工具)。我们还将它作为辅助函数合并到我们的DLL注入工具中。

可以在以下代码片段中看到传统注入技术的示例:

简要分析一下在这个LoadLibrary示例中我们所做的事情(稍后我们将深入探讨并对其中的某些部分进行扩展):

我们的第一个参数是HANDLEhProcess,它可以由OpenProcess函数定义(见下文),第二个参数是LPCSTRdllpath,它定义了完整的dll路径,例子如下:

HANDLEhProcess=OpenProcess(PROCESS_VM_READ|PROCESS_VM_WRITE|PROCESS_VM_OPERATION|PROCESS_CREATE_THREAD|PROCESS_QUERY_INFORMATION,FALSE,pe32.th32ProcessID);

一旦我们有了进程句柄,我们就可以使用VirtualAllocEx在目标进程的虚拟内存空间中分配一个内存区域。

LPVOIDlpBaseAddress=VirtualAllocEx(hProcess,NULL,strlen(dllpath),MEM_COMMIT|MEM_RESERVE,PAGE_EXECUTE_READWRITE);

然后,我们使用WriteProcessMemory函数通过DLLpayload的完整路径将数据写入进程。

WriteProcessMemory(hProcess,lpBaseAddress,dllpath,strlen(dllpath),NULL);

然后,我们找到LoadLibrary的地址,并使用GetProcAddress和GetModuleHandle函数存储该内存地址。

LPVOIDlpLibraryAddress=(LPVOID)GetProcAddress(GetModuleHandle(L"kernel32.dll"),"LoadLibraryA");

最后,我们在目标进程中执行LoadLibrary函数,使用CreateRemoteThread函数将DLLpayload作为库进行强制加载。我们也可以使用NtCreateThreadEx或RtlCreateUserThread,这些都是未记录的函数,但思想是一样的。由于CreateRemoteThread执行LoadLibrary函数,目标进程将立即使用DLL_PROCESS_ATTACH函数执行DLL的DllMain入口函数。

CreateRemoteThread(hProcess,NULL,(LPTHREAD_START_ROUTINE)lpLibraryAddress,lpBaseAddress,NULL,NULL);

使用这种方法,我们必须定义DLLMain入口函数,也很简单,示例如下:

BOOLWINAPIDllMain(HINSTANCEhinstDLL,DWORDfdwReason,LPVOIDlpvReserved){

::MessageBox(NULL,L"HelloWorld!",L"TestDLL",MB_OK);

returnTRUE;

}

注意:这只是基本的POC,不过如果你看一下动态链接库最佳实践,你会看到DllMain函数就是用来在编译时初始化静态数据结构和成员的,创建和初始化同步对象,分配内存并初始化动态数据结构。不过,有些函数应该避免使用,例如User32.dll和Gdi32.dll。因为,在User32.dll中实现的MessageBox函数可能会导致问题(崩溃或死锁)。

第一个例子比较基础,而且动静很大,因为CreateRemoteThread(大多数安全产品都会定位)和LoadLibrary函数调用更新了该进程中的数据结构,所以,任何监视进程的人(使用ProcMon)都可以看到每个进程中加载的每个DLL的名称。

替代方案就是在远程进程中为DLL的实际内容分配足够的空间,但这很棘手,因为我们必须手动解析并将二进制映射到内存中。我们将在下一节探讨这种替代方案-反射DLL注入(敬请期待)。

THE END
1.详解JavaWeb项目中DLL文件动态加载方法动态加载DLL文件的方法 在JavaWeb项目中,我们可以使用System.load()方法或者System.loadLibrary()方法来动态加载DLL文件。下面分别介绍这两种方法。 使用System.load()方法 System.load()方法用于加载指定路径的DLL文件。以下是一个使用System.load()方法的示例: https://blog.51cto.com/u_15702012/12830001
2.高效文件处理:Pythonpathlib实战指南在使用Python处理文件路径时,强烈建议使用pathlib。 pathlib以面向对象的方式处理文件路径,既避免了很多陷阱,也能使执行许多路径的相关操作变得更容易。 本篇总结了常用的使用pathlib进行文件路径处理的方法。 1. 常用操作 首先介绍如何使用pathlib来完成一些常规的文件路径相关操作。 https://blog.csdn.net/fenfenfen520a/article/details/144423375
3.System.loadLibrary()的使用方法汇总.HuajianHsiu当使用System.loadLibrary()调用 Dll,两种方法: 1.设定环境变量。 比如:所编辑的Dll在目录“D:/cppProjects/nativecode/release”内,将这个路径复制添加到电脑的环境变量中的path变量内即可。 2.设定项目属性。(开发推荐) 右击项目名|选择属性properties|在左边列表内选择“Java Build Path”|在右边选项卡用选择“sohttps://www.iteye.com/blog/huajianhsiu-1828242
4.Python中pathlib库的使用pathlib 提供了多个类来表示不同类型的路径,其中最常用的是 Path 类。Path 类实例化后可以用于访问文件和目录的属性、方法,比如文件大小和修改时间,以及创建、重命名、删除等操作。使用 pathlib 库可以使得代码更加易读和可维护,尤其在涉及到多平台的应用时,可以避免手动拼接文件路径的麻烦和错误。1、获取当前路径https://baijiahao.baidu.com/s?id=1763596157090095701&wfr=spider&for=pc
5.Android包体积优化方案动态资源管理系统Android社区而我们这里,由于so文件不存在于apk当中,而是需要动态下载,所以我们显然不能直接使用系统的System.loadLibrary方法加载so文件。 而动态加载so的方法,在热修复和插件化框架中,已经比较成熟了,我们参考了市面上的开源框架后,选择了腾讯的Tinker框架的加载方案,即使用反射classloader 将 so 包的路径写入 nativeLibraryPathElehttp://androidos.net.cn/doc/2023/6/4/802.html
6.loadlibrary使用的方法有哪些问答在使用loadlibrary函数加载动态链接库时,有以下三种方法:1. 静态链接:将动态链接库直接包含在可执行文件中,使得程序在启动时就能访问动态链接库中的函数和数据。静态链接是最简单的加载动态链接库https://www.yisu.com/ask/16417239.html
7.厉害了,Python也能使用动态链接库腾讯云开发者社区动态链接库的调用方法很多,任你挑选。 代码语言:javascript 复制 CDLL(xx.dll)OleDLL(xx.dll)PyDLL(xx.dll)WinDLL(xx.dll)cdll.LoadLibrary(xx.dll)oledll.LoadLibrary(xx.dll)pydll.LoadLibrary(xx.dll)windll.LoadLibrary(xx.dll)#也可使用链接库读取器LibraryLoader,它也同样支持上面的八种方式LibraryLoader(Chttps://cloud.tencent.com/developer/article/2015957
8.好学编程:六种打包Python代码方法优缺点分析这是怎么回事呢?那么问题来了,sqlalchemy这个库在初始化的时候是不需要显示导入引擎库的,它自己有一个create_engine()的函数来初始化,这个字符串是使用者根据规则来自己填写的。其实解决的方法很简单,我们只要在显式导入pymysql这个库即可。现在我们导入这个库:import pymysql https://zhuanlan.zhihu.com/p/662816631
9.详解Java中native方法的使用javanative是与C++联合开发的时候用的!使用native关键字说明这个方法是原生函数,也就是这个方法是用C/C++语言实现的,并且被编译成了DLL,由java去调用。本文给大家介绍java 中native方法使用,感兴趣的朋友一起看看吧 今天在网上学习时碰到有关于 native修饰符所修饰的方法,上网查了查,觉得很有意思记录一下 https://m.jb51.net/article/195760.htm
10.PyTorch自然语言处理入门与实战本书首先对自然语言处理进行了概述,并介绍了Python自然语言处理基础;然后介绍了什么是PyTorch和PyTorch的基本使用方法;接着介绍了多种机器学习技术及其在自然语言处理中的应用,包括RNN、词嵌入、Seq2seq、注意力机制、Transformer 和预训练语言模型;最后介绍了用自然语言处理实现的两个项目,即“中文地址解析”和“诗句https://www.epubit.com/bookDetails?code=UB7da415821f9de&id=UB7da415821f9de
11.软件测试试题库(通用7套)第二种为编程的.时候使用LoadLibrary,FreeLibrary,GetProcAddress来动态装载程序。则编好的程序使用库文件时,是程序自己主动载入。 10、白盒测试有哪些覆盖方法? 答:语句覆盖,判定覆盖,条件覆盖,判定/条件覆盖,组合覆盖,路径覆盖。 11、进程与线程的区别? https://www.unjs.com/zuixinxiaoxi/ziliao/20170720000008_1398848.html
12.基于FPGA和USB2.0协议的通用数据传输设计AET设计中在此基础上封装了读寄存器、写寄存器、读FIFO、写FIFO四个函数,并以DLL形式提供给用户。上层使用的是MATLAB平台,需要在该平台上配置MEX编译器,并使用loadlibrary函数以加载动态链接库,加载后便可以调用DLL里提供的读写函数了。DLL也能被其他平台调用。http://www.chinaaet.com/article/3000024514
13.java如何调用pythonjava教程java 调用 python 代码的方法:使用 java native interface (jni):加载 python 解释器、获取引用、执行代码、获取结果。使用 python for java (jython):导入库、创建解释器、执行代码、获取结果。 如何在 Java 中调用 Python Java 和 Python 是两种流行的编程语言,有时需要在同一个应用程序中使用它们。Java 可以调用https://www.php.cn/faq/830246.html
14.Delphi中LoadLibrary的使用心得API如果进行FreeLibrary,刚好这个类以外别的地方也加载过这个DLL,也要用这个DLL时,岂不就会有问题了?想了各种各样的检测方法和管理方法,依然觉得有逻辑漏洞。那么能不能不手动FreeLibrahttps://www.2ccc.com/news/Html/?481.html
15.LoadLibraryA函数(libloaderapi.h)模块的名称。 这可以是库模块(.dll 文件)或可执行模块(.exe 文件)。 如果指定的模块是可执行模块,则不会加载静态导入;而是使用DONT_RESOLVE_DLL_REFERENCES标志LoadLibraryEx加载模块。 指定的名称是模块的文件名,与库模块本身中存储的名称无关,由模块定义 (.def) 文件中的LIBRARY关键字指定。 https://docs.microsoft.com/zh-cn/windows/win32/api/libloaderapi/nf-libloaderapi-loadlibrarya
16.如果一个exe使用LoadLibrary函数加载了自己吾爱破解众所周知,exe文件可以使用LoadLibrary函数可以加载一个dll并和GetProcAddress函数连用即可获取到dll中导出函数https://www.52pojie.cn/thread-1935537-1-1.html
17.如果我使用loadlibraryexw()加载相同的.dlltwitce,则会发生什么handle = ::LoadLibraryExW(dllpath, NULL, LOAD_WITH_ALTERED_SEARCH_PATH) 当ASLR(DynamicBase)打开时,它会返回相同的地址吗? 看答案 没有什么不同而不是没有aslr。第二个LoadLibraryex调用将返回与第一个呼叫相同的手柄,并且DLL的使用计数递增。 所以DLL没有加载“两次”。是在第一个呼叫上加载。 除了不同https://www.pianshen.com/question/78471527993/
18.ctypes通过cdll.msvcrt 调用的标准 C 函数,可能会导致调用一个过时的,与当前 Python 所不兼容的函数。因此,请尽量使用标准的 Python 函数,而不要使用 msvcrt 模块。在Linux 中,要求指定文件名 包括 扩展名来加载库,因此不能使用属性访问的方式来加载库。 你应当使用 dll 加载器的 LoadLibrary() 方法,或是应当通过调用http://docs.python.org/zh-cn/3/library/ctypes.html