- 第22章 插入DLL和挂接API
第22章 插入DLL和挂接API
22.0 简介
由于Windows的进程地址空间是相对独立的,你对一个进程的内存地址(比如说是0x0012ABDC)的修改不会影响到另一个进程的地址空间的地址0x0012ABDC。而Win98是全部进程共享2GB的地址空间,情况就又不一样了。
独立的机制也不是没有缺点的,毕竟进程之间有互相通信的需要,这种情况还不少:
- 当想要为另一个进程创建的窗口建立子类的时候。
- 当你需要调试的时候。
- 当你需要挂接其他线程的时候。
下面提到的手段,可以把DLL插入到另一个进程的地址空间。由于在这个时候已经暂时地破坏了两个进程之间的一部分独立性,对于另一个进程来说这是非常严肃的情形,需要严阵以待。
22.1 插入DLL的第一个例子:SetWindowLong
如果要为另一个进程创建的窗口建立一个子类,那么就能改变窗口的行为特性。而这样的操作使用函数SetWindowLongPtr就可以完成,它可以替换内存块中的窗口过程地址,让它指向一个新的WinProc。当你使用下面的代码的时候,送往窗口hwnd的消息就改送去MySubclassProc了。
SetWindowsLongPtr(hwnd, GWLP_WNDPROC, MySubclassProc);
然而这会遇到一个问题,建立子类的过程地址不在目标的进程的地址空间中。为了避免这个问题的产生,应该让系统知道MySubclassProc函数的地址在目标进程的地址空间中。然后在调用旧的Windproc之前,让系统执行一次上下文转换。不过Microsoft并没有提供这个功能,原因有:
- 应用程序很少为其他的进程的线程创建的窗口建立子类,而只是为了自己的线程这样做,Windows的内存结构并不阻止这种情形。
- 切换活动进程需要占用许多CPU时间。
- 新的进程需要执行MySubclassProc中的代码。系统究竟应该使用新线程还是旧线程呢?
显然这个问题并没有万全之策,所以Microsoft决定不让SetWindowLongPtr改变另一个进程创建的窗口过程。
对于这种涉及到进程的地址空间边界的问题,新的窗口过程的代码必须被放入目标进程的地址空间,这样就可以调用SetWindowLongPtr函数改变目标进程的窗口地址了。
22.2 使用注册表来插入DLL
Windows系统使用注册表作为自己的配置文件,该表注册表就可以改变Windows的行为特性。对于下面的注册表的关键字中,包含了一个DLL文件名或者一组DLL文件名(用逗号或者空格隔开,所以DLL的名字也不应该是带有空格的)。这些DLL中的第一个DLL是可以带路径的,但是其他带路径的DLL就被忽略。因此最好DLL的名字不要带有空格。由于这个原因最好将DLL放入Windows系统的目录中,这样就不必设定了路径,例如(C:\MyLib.dll)。
HKEY_LOCAL_MACHINE\SoftWare\Microsoft\Windows NT\CurrentVersion\Windows\AppInst_Dlls
在重新启动Windows并初始化之后,系统就保存这个关键字的值。在User32.dll被映射到内存中后,它将收到一个DLL_PROCESS_ATTACH的通知。一旦这个通知被处理,User32.dll就会检测上面的注册表关键字的值,对字符串中指定的每个DLL调用LoadLibrary函数,每个DLL的DllMain就被触发,其中参数fdwReason的值是DLL_PROCESS_ATTACH。这样所有库就都能被初始化了。
由于这些DLL的加载时期非常的早,所以调用其它DLL的函数时就应该格外的小心:调用Kernel32的库的内容估计被问题,但是其他的DLL的函数就不一定了。上面的LoadLibrary的操作不会对每个DLL是否成功做检查。
这种方式比较方便,不过也有相对的不足:
- 你需要重启系统。
- 你的DLL只会映射到使用User32.dll的进程中。所有基于GUI的应用程序都使用User32.dll,不过系统中也存在很多CUI程序。这种方式对CUI程序是不起作用的。
- 你的DLL会映射到所有GUI线程中。一旦你的DLL中出了什么差错(比如进入死循环),影响的范围可就大了。DLL应该插入尽量少的进程中。
- 还有DLL中导出的东西(比如函数)的作用是临时性的,用完就必须卸载掉。但是这样用这种方式插入DLL的话,即使引用DLL的函数的线程结束了,DLL也不会被卸载。最好让DLL在使用的时候才保持插入的状态。
22.3 使用Windows挂钩来插入DLL
挂钩也是将DLL插入进程的地址空间的一个办法,只要使用SetWindowHookEx。例如下面的代码:
HHOOK hHook = SetWindowHookEx(WH_GETMESSAGE, GetMsgProc, hInstDll, 0);
参数WH_GETMESSAGE指明的挂钩的类型,参数GetMsgProc指向一个函数地址,窗口用它处理一个消息时系统应该调用的地址(在目标地址空间中),参数hInstDll指明包含GetMsgProc的DLL,也是DLL被映射到的进程的地址空间中的虚拟内存的地址,最后一个参数0指明要挂接的线程。如果最后一个参数是0,那么系统会挂接所有的GUI线程。其中的过程是:
- 一个线程准备将一条消息发送到一个窗口。
- 系统查看现场上是否已经安装了WH_GETMESSAGE挂钩。
- 系统查看包含GetMsgProc函数的DLL是否被映射到目标进程的地址空间中。
- 如果DLL未被映射,系统将强制DLL映射到进程B的地址空间,并将进程B中的DLL的映像的引用计数递增1。
- 当DLL的hinstDll用于进程B时,系统查看该函数,并检查改DLL的hinstDll是否与进程A所处的位置相同。
- 如果相同,那么系统DLL的地址相同,那么GetMsgProc函数的位置也是相同的,系统只需要调用它即可。
- 如果不同系统必须确定原进程的地址空间中GetMsgProc来确定。这个地址可以用一个公式:
GetMsgProcB = instDllB + GetMsgProcA - hinstA
来求得。
- 系统将目标进程中的DLL的映像的引用计数递增1。
- 系统调用目标进程的地址空间中的GetMsgProc函数。
- 在GetMsgProc函数返回的时候,系统将进程B的DLL映像的引用计数递减1。
当DLL被挂接的时候,是整个DLL都被挂接进去的,不是只有GetMsgProc函数这部分。就相当于在目标进程中对DLL调用了LoadLibrary,效果基本相同。
若要为另一个进程的线程创建的窗口创建子类,首先可以在创建该窗口的挂钩上设置一个WH_GETMESSAGE挂钩,然后当GetMsgProc函数被调用的时候,就可以使用SetWindowLongPtr函数来创建新的窗口子类。当然,子类的过程必须与GetMsgProc函数位于同一个DLL中。
当不再需要DLL的时候,下面的函数可以将DLL删除,方法是调用下面的函数:
BOOL UnHookWindowsHookEx(HHOOK hhook);
当一个线程调用这个函数的时候,系统会遍历它插入过这个DLL的所有进程的列表,并对DLL的引用计数递减1。根据引用计数的性质,这个引用计数为0时,DLL就会从这个进程的地址空间中删掉。这意味着挂钩不会立即取消,一定的寿命期内都会保持有效。
22.4 使用远程线程来插入DLL
插入DLL的第三种方法是使用远程线程,可以灵活。不过这个方法也用上了好些Windows特性,如进程、线程、线程同步、虚拟内存管理、DLL和Unicode等。由于Windows的进程间是内存隔离的,不能随便对其他线程做任何事,不过这也是有例外的,为了调试程序等等。不过任何函数都可以调用这些函数。
22.4.1 Inject Library 示例应用程序
22.4.2 Image Walk DLL
22.5 使用特洛伊木马来插入DLL
22.6 将DLL作为调试程序来插入
22.7 用Windows 98上的内存映射文件插入代码
22.8 用CreateProcess插入代码
22.9 挂接API的一个实例
22.9.1 通过改写代码来挂接API
22.9.2 通过操作模块的输入节来挂接API
22.9.3 LastMsgBoxInfo 示例应用程序
标签:挂接,重造,22,DLL,插入,地址,线程,进程 来源: https://www.cnblogs.com/leoTsou/p/13737496.html
本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享; 2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关; 3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关; 4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除; 5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。