如果您看一下简单DLL注入的以下工作代码:
//Open the target process with read , write and execute priviledges
Process = OpenProcess(PROCESS_CREATE_THREAD|PROCESS_QUERY_INFORMATION|PROCESS_VM_READ|PROCESS_VM_WRITE|PROCESS_VM_OPERATION, FALSE, ID);
//Get the address of LoadLibraryA
LoadLibrary = (LPVOID)GetProcAddress(GetModuleHandle("kernel32.dll"), "LoadLibraryA");
// Allocate space in the process for our DLL
Memory = (LPVOID)VirtualAllocEx(Process, NULL, strlen(dll)+1, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
// Write the string name of our DLL in the memory allocated
WriteProcessMemory(Process, (LPVOID)Memory, dll, strlen(dll)+1, NULL);
// Load our DLL
CreateRemoteThread(Process, NULL, NULL, (LPTHREAD_START_ROUTINE)LoadLibrary, (LPVOID)Memory, NULL, NULL);
//Let the program regain control of itself
CloseHandle(Process);
让我感到困惑的是,GetProcAddress
会返回当前流程的LoadLibraryA
功能地址,如何将其作为参数传递给CreateRemoteThread
和期望目标流程来运行它?
答案 0 :(得分:19)
它偶然起作用。这是一个非常常见的事故,Microsoft为确保操作系统DLL(如kernel32.dll)具有不与任何其他DLL冲突的基址做了大量工作。 kernel32.dll在进程初始化时很早就加载了进一步增强,所以它必须争取获得其首选基地址的可能性很小。
你会轻易逃脱。值得注意的是,这个在过去出现了错误,有一个XP安全更新oops导致gdi32.dll被重新定位并使许多机器在启动时崩溃。正确的方法是相当痛苦的,CreateToolhelp32Snapshot()+ Module32First / Next()找到重定位偏移量并不是很大的乐趣。坦率地说,如果操作系统像这样“怪异”,你可能根本就不应该这样做。
答案 1 :(得分:2)
LoadLibraryA
位于kernel32.dll
,这个模块总是加载到每个进程中,并且恰好也会在每个进程中加载到同一个地址。
答案 2 :(得分:1)
地址空间布局随机化(ASLR)是Windows处理的反漏洞利用缓解功能,它允许地址重定位,以帮助防止攻击者确定内存中利用内容的地址(停止地址/偏移的硬编码) 。但是,Windows模块仅在每个会话中更改其地址。
如果你有一个使用kernel32.dll的进程(并非所有进程都使用kernel32.dll,我会在几分钟内进一步解释),例程的地址可能是55AA1122(&# 39;是一个无效的示例地址)。现在,使用kernel32.dll的下一个进程将为55AA1122提供与前一个相同例程的相同地址....仅当进程具有相同的体系结构时。
32位进程将具有相同的kernel32.dll导出地址,以及其他Windows模块导出(例如NTDLL,USER32等)。 64位进程将具有与32位进程不同的地址,但是64位进程也将具有Windows模块的相同地址!
远程线程创建不是"意外",Microsoft故意实现它。为什么? Microsoft在Windows本身使用它,也用于异步过程调用。微软还经常根据自己的惯例对事物进行热补丁作为反转技巧,或者如果他们将源代码丢失到他们自己的项目中,哈哈。
现在关于将kernel32.dll加载到进程中,它只被加载到使用Win32 API的进程中。这包括世界上99%的程序,但是可以编译不使用它的本机进程。但这会强制您完全使用Native API而不使用Win32 API,而名为smss.exe的Windows进程就是这样做的。您还可以编译本机DLL,它们甚至没有正常的Win32 API DLL Entry例程。
简而言之,Windows模块例程的地址每次启动都会更改一次。它将保持不变,直到下次重启,依此类推。对于每个进程,32位进程都有自己的Windows模块共享地址,64位进程也是如此。因此,除非使用32位Kernel32.dll LoadLibraryA地址,否则不能使用64位进程的LoadLibraryA地址同时针对32位进程执行DLL注入。更好的想法是使用LdrLoadDll,或者只是反射DLL加载器存根的shell代码注入。
答案 3 :(得分:0)
如果启动Visual Studio,请创建一个空项目,添加新的main.cpp文件,并写下:
#include <windows.h>
void main()
{
}
然后编译该程序,不要指望所创建的可执行文件什么也不做。
也许Visual C ++编译器没有在目标文件中写下任何命令,因为源代码中没有命令,但是链接器确实在可执行文件{{1}的主过程的开头写下了命令}调用LoadLibrary
,user32.dll
,kernel32.dll
等。
因此,无论此可执行文件的源代码是什么,最初使用Visual Studio C ++编写的每个应用程序都以相同的顺序对gdi32.dll
进行多次相同的调用。 / p>
源代码仅确定在LoadLibrary
调用之后应该出现的命令。
因此,每个Visual Studio C ++应用程序都在执行开始时加载LoadLibrary
中的LoadLibrary
,因此kernel32.dll
在所有相对于进程地址的进程中具有相同的入口点或地址。
理论上,如果您将某些未加载kernel32.dll LoadLibrary
过程的kernel32.lib链接到程序的内存中,则可以使注入程序(尝试注入程序的恶意程序)失败。 / p>
如果您的程序在执行过程中无法动态加载dll,则注入器无法导致您的程序在执行过程中动态加载某些dll,因为假定执行此操作的过程LoadLibrary
不存在于进程内存中。 / p>
因此,由注入程序创建的远程线程无法执行受害者内存中不存在的LoadLibrary
。
但是,攻击者可能会使用VirtualAllocEx在受害者的内存中创建一些块,然后在WriteProcessMemory中创建一些可执行代码,然后使用CreateRemoteThread来执行它。