一、前言
dll文件我们知道,它是不能自己运行的,只能被加载到其它进程中去执行代码。
而利用这个特性,我们就能够使用dll来完成注入操作,方法的原理为:
- 将我们的代码写在一个dll文件中
- 强迫目标进程加载我们的dll文件
一旦我们的dll文件被加载进入了目标进程,那我们自然可以为所欲为了!因为dll与它在一个进程内,你可以任意访问其内部的地址。
如果不用注入,虽然也能实现动态修改,但会更加麻烦:
- 使用
VirtualQueryEx
、VirtualProtectEx
函数查询、修改目标进程内存的权限。 - 使用
ReadProcressMemory
、WriteProcessMemory
函数读取、写入目标进程内存中的数据。
由于不在一个进程内,这些函数实际上都是在跨进程操作,并且这种方式想要让目标进程执行自己的代码非常麻烦,一般只会用于小块数据的修改。
而dll不一样,一旦被加载了,是可以执行我们自己的代码的:动态库与静态库。
dll文件也有一个入口函数,一旦被加载,就会执行该函数,我们就可以在里面写一些自己的代码来操作该进程。
二、dll注入方式
一般情况下,程序加载dll有以下三种方式:
- 进程创建时,会加载其输入表中dll,也就是静态输入。
- 进程主动调用
LoadLibrary
加载,也就是动态加载。 - 通过系统机制要求,比如输入法为什么能在任意程序中使用?这就是系统机制导致的,它允许某些特性功能的模块直接加载到目标进程中。
1.输入表
首先是第一种方式,静态输入,通过修改输入表完成。
当一个进程被创建后,它并不会直接到exe本身去执行代码,首先被执行的是ntdll.dll
中的LdrInitializeThunk
函数。
这个ntdll.dll
是一个非常重要的基础模块,前面也提到过,它并不会依赖于你的可执行文件是否导入,它在进程的创建阶段就会被自动映射到新进程中。
而这个LdrInitializeThunk
函数又会调用LdrpInitializeProcess
函数对进出做一些必要的初始化功能。
LdrpInitializeProcess
函数又会调用LdrpWalkImportDescriptor
对输入表进行处理,也就是加载输入表中的模块。
所以只要我们在输入表被处理之前对其进行干预,为输入表增加一个项目,使其指向要加载的目标dll,或者替换原输入表中的DLL并对其函数调用进行转发,那么就能实现加载dll的功能了!
简单起见,这里我自己写一个dll,在vs自动生成的代码中添加一个导出函数、并在被注入的时候弹出一个提示框即可:
然后生成32位的,下面我们的目标就是将这个生成的32位dll注入到前面那个TraceMe32.exe
程序中去。