大家好,又见面了,我是你们的朋友全栈君。
读《Windows核心编程》笔记一 DLL注入和API拦截
在Windows中,每个进程相互独立,都有自己的私有的地址空间,程序中使用的指针都是进程自己地址空间的一个内存地址,无法创建也没法使用其他进程的指针。这种机制使得各个进程之间不会相互影响,万一自己出现了问题,也不会影响到其他的进程。对用户来说,系统更加的稳定了,但是对于开发人员来说,会使我们很难编写能够与其他进程通信的应用程序或对其他进程进行操控的引用程序。
程序运行是由dll/exe等文件加载并执行的,运行过程中也可以动态的加载其他的DLL。假如,我们可以使应用程序在加载dll时加载我们自己写的DLL,那么我们就可以在我们的DLL中做任何我们想做的事情,可以访问进程的任何私有地址空间。下面就来介绍如何让程序加载我们自己的DLL:
1.使用注册表
2.使用Windows挂钩
3.使用远程线程
4.使用木马DLL,即让程序加载我们伪装的DLL
5.通过修改线程内存地址的机器指令
1.使用注册表
这是最简单的方法,通过系统注册表来达到注入DLL的目的,系统注册表中有个AppInit_DLLs键值,在如下路径:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows
AppInit_DLLs有可能包含一个或多个DLL的文件名(通过空格或逗号分隔),第一个DLL的文件名可以包含路径,但其他DLL包含的路径将被忽略。所以我们最好是将自己的DLL放在系统目录,然后在注册表中直接指定文件名即可。添加好DLL后,把键值LoadAppInit_DLLs也改为1.这样就大功告成了,每当新的进程启动的时候,会去加载系统的User32.dll,User32.dll在处理DLL_PROCESS_DETACH通知时,就会调用LoadLibrary来载入我们之前填写在AppInit_DLLs中的所有DLL,并调用每一个DLL的DllMain函数。
不过这样的注入方式只适用于那些GUI程序,因为需要依赖程序是否映射User32.dll,对于那些终端CUI应用程序则没法使用。
2.使用Windows挂钩
Windows提供的一种机制可以让我们的一个DLL注入到另一个进程的地址空间,那就是安装WH_GETMESSAGE挂钩,例如:
HHOOK hHook = SetWindowsHookEx(WH_GETMESSAGE, MyMsgProc, hInstDll, 0);
MyMsgProc是我们自己的消息处理过程,hInstDll的值是进程地址空间中DLL被映射后的虚拟内存地址,最后一个参数0表示给所有的GUI线程安装挂钩。假设有一个线程给某个窗口发一条消息,系统会先检查你有没有安装WH_GETMESSAGE挂钩,然后把MyMsgProc所在的DLL映射至进程空间,并调用MyMsyProc函数。由于系统将挂钩函数所在DLL映射到进程地址空间时,会映射整个DLL,而不仅仅只是MyMsgProc,这就意味着DLL内的所有函数都存在于进程B中。这时我们可以用SetWindowLongPtr去派生一个子类窗口,这样就可以截获窗口的处理过程。
3.使用远程线程来注入DLL
DLL注入技术唯一的目标就是让别的程序加载我们的DLL,这样我们就可以在我们自己的DLL中做任何我们想做的事情。但是我们无法轻易的控制别人进程的线程,因此就要求我们在目标进程中创建一个新的线程。幸运的是,微软直接给我们提供了这样的API,那就是CreateRemoteThread函数,它使得我们可以在别的程序中创建一个新的线程,函数原型如下:
HANDLE WINAPI CreateRemoteThread(
__in HANDLE hProcess,
__in LPSECURITY_ATTRIBUTES lpThreadAttributes,
__in SIZE_T dwStackSize,
__in LPVOID lpParameter,
__in DWORD dwCreationFlags,
__out LPDWORD lpThreadId
);
其实这个函数与CreateThread函数是一样的,只不过多了一个hProcess参数。lpStartAddress这个函数的内存地址应该存在于远程进程空间中,因为线程函数不可能在别的进程的地址空间中。
好了,现在我们实现了在别的进程中创建一个线程,那么怎么让线程去执行LoadLibrary函数,又怎么加载我们自己的DLL呢?实际上我们可以让lpStartAddress参数直接就是LoadLibrary函数的地址,这样就不用担心线程怎么去执行了。
由于每一个进程在加载Kernel32.dll时,基本上都被映射到了同一个地址。这样我们可以使用GetProcAddress来获取LoadLibrary在Kernel32中的偏移地址,再加上kernel32在自己进程的base地址:
PTHREAD_START_ROUTINE pfnThreadRtn = (PTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandle(_T(“Kernel32”)), “LoadLibraryW”);
HANDLE hThread = CreateRemoteThread(hProcessRemote, NULL, 0, pfnThreadRtn, “MyLib.Dll”, 0, NULL);
“MyLib.Dll”这串字符串需要通过VirtualAlloEx在远程进程中分配一块内存,然后通过WriteProcessMemory写入那块新申请的内存,然后把参数”MyLib.Dll”替换掉,这样就可以了,现在我们的DLL就已经进入到了远程的进程中,可以随心所欲了。
4.使用木马DLL,即让程序加载我们伪装的DLL
5.通过修改线程内存地址的机器指令
这两种方式下回分解
发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/145651.html原文链接:https://javaforall.cn
【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛
【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...