使用CreateRemoteThread进行DLL注入

时间:2014-03-30 21:28:50

标签: c++ visual-studio visual-c++ dll dll-injection

如果您看一下简单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和期望目标流程来运行它?

4 个答案:

答案 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}的主过程的开头写下了命令}调用LoadLibraryuser32.dllkernel32.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来执行它。