老手请飘过,写给新手看的低级东西
一、步骤
1、CreateThread
要搞懂远程线程注入技术,首先应该用过CreateThread创建多线程。
如果对这个函数怎么使用没搞懂下面的内容就不用看了,如果对这个函数理解了那么继续走
2、如何加载一个DLL
要在目标进程注入一个DLL,也就是要将DLL加载到目标进程,我们平时在使用DLL时,是怎么加载的?
通过LoadLibrary加载。那么我们要将一个DLL注入目标进程,可以理解为我们要在目标进程执行LoadLibrary("xxxdll")
3、如何在目标进程执行LoadLibrary
如果是在自己的进程,我们直接调用LoadLibrary就可以加载一个DLL,现在的问题是我们要在其它进程
执行LoadLibrary,这就要借助创建远程线程
4、如何创建远程线程
调用下面这个函数,就可以创建远程线程,我们看下msdn对这个函数的描述:
The CreateRemoteThread function creates a thread that runs in the virtual address space of another process.
HANDLE CreateRemoteThread(
HANDLE hProcess, // handle to process
LPSECURITY_ATTRIBUTES lpThreadAttributes, // SD
SIZE_T dwStackSize, // initial stack size
LPTHREAD_START_ROUTINE lpStartAddress, // thread function
LPVOID lpParameter, // thread argument
DWORD dwCreationFlags, // creation option
LPDWORD lpThreadId // thread identifier
);
其它参数不多说了,如果不懂可以百度这个函数用法,说说第四个参数
LPTHREAD_START_ROUTINE lpStartAddress,其实这个参数和CreateThread的第三个参数一样,LPTHREAD_START_ROUTINE原型如下:
DWORD WINAPI ThreadProc(
LPVOID lpParameter // thread data
);
说白了第四个参数就是要指定,线程跑起来时,要执行的代码在哪里,这就给我们注入dll带来了机会啊,我们可以把目标进程中LoadLibrary
函数在内存中的地址作为CreateRemoteThread的第四个参数,那么线程运行时,就去执行LoadLibrary这个函数了。
补充下为什么可以把LoadLibrary这个函数的地址,作为第四个参数,我们来看下LoadLibrary的原型:
HMODULE LoadLibrary(
LPCTSTR lpFileName // file name of module
);
发现没,LoadLibrary在MSDN中给出的原型只少了个WINAPI,但是Windows所有的API默认都是WINAPI啊,(WINAPI表示调用约定)
返回值:DWORD 和 HMODULE 本质就是个整数
参数:LPVOID 和 LPCTSTR 都是个指针
这就是为什么可以把LoadLibrary作为CreateRemoteThread函数的第三个参数,这样我们就完美利用CreateRemoteThread创建一个
远程线程,并执行LoadLibrary,去加载我们要注入的DLL了。
那要如何获取目标进程中的LoadLibrary函数地址了,很简单我们获取自己进程中的LoadLibrary的地址即可,LoadLibrary属于kernel32模块
这个模块在每个进程中加载的基地址都一样,所以LoadLibrary在自己进程中的地址和在目标进程中的地址是一致的。
5 解决LoadLibrary的参数问题
在第四步中我们解决了如何跨进程调用LoadLibrary这个问题,现在的问题是,执行LoadLibrary,我们要给它一个参数,
这个参数用来指明DLL的路径。我们可以利用CreateRemoteThread的第五个参数LPVOID lpParameter,来解决。
lpParameter这个参数在远程线程执行时,会传递给LoadLibrary (LoadLibrary就是远程线程),作为LoadLibrary的第一参数。
但是这里有一个问题,我们调用CreateRemoteThread时,是在自己的进程调用,那么传递lpParameter这个参数时,不能使用自己进程
虚拟地址空间的地址,那要怎么解决这个问题
6 VirtualAllocEx
在Step5中,碰到了新的问题,如何传递LoadLibrary的参数,因为这个参数必须是目标进程中的一个虚拟地址。
我们需要用到VirtualAllocEx这个函数,这个函数可以在目标进程申请内存,我们通过调用这个函数,在目标
进程中申请一段内存,然后把DLL的路径,写到这段内存中,比如d:\xxx\xxoo.dll
这个函数的返回值,就是申请到的内存的起始地址。所以我们可以把返回值作为CreateRemoteThread的第5个参数,这样解决了给
LoadLibrary传递参数的问题。
7 其它一些细节问题看代码注释
二、注意事项
如果你要注入的目标进程是64位程序,那么你的注入进程必须是64位的,而且要注入的DLL也必须是64位的,否则 CreateRemoteThread会 失败,GetLastError返回错误代码:5。写一个完善的注入代码要考虑的问题很多
三、部分关键代码如下,代码有参考这个博客:
https://www.cnblogs.com/BoyXiao/archive/2011/08/11/2134367.html
代码网上一大把,关键是搞清楚思路,如果需要完整代码可以联系QQ 844255657, 这里没有把所有代码贴上来了,
因为网上太多了。
int _tmain(int argc, _TCHAR* argv[])
{
if (!AdjustProcessTokenPrivilege()){
MyOutputDebugString("提权失败\n");
}
DWORD dwPid = GetPidByName("explorer.exe");
if (dwPid == 0){
MyOutputDebugString("GetPidByName failed\n");
return -1;
}
MyOutputDebugString("explorer.exe pid=%d\n", dwPid);
HANDLE hProcess;
hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPid);
if (hProcess == NULL)
{
MyOutputDebugString("打开进程失败!!!!");
return -1;
}
//1.在远程进程中分配内存
LPVOID pszRemoteBuffer = (char *)VirtualAllocEx(hProcess, NULL, MAX_PATH, MEM_COMMIT, PAGE_READWRITE);
if (pszRemoteBuffer == NULL)
{
MyOutputDebugString("申请远程空间失败");
return -1;
}
//2.在远程申请的内存空间中写入DLL的路径,后面这块数据当做远程线程的参数
SIZE_T dwWriten = 0;
const char * dllPath = "D:\\VS\\DllToInject\\x64\\Debug\\UtilDll.dll";
if (!WriteProcessMemory(hProcess, pszRemoteBuffer, (LPVOID)dllPath, strlen(dllPath) + 1, &dwWriten))
{
MyOutputDebugString("写入内存失败");
return -1;
}
//3.获取远程进程中LoadLibry的地址, 这里使用当前进程Kernel32模块的基地址当做远程进程中的kernel32的基地址,这不是巧合
//kernel32在每个进程中的基地址都一样,可能是操作系统为进程加载kernel32时,直接将已经加载了的kernel32映射到需要这个模块的进程中
//pfnLoadLibrary 就是远程线程, 所以远程线程执行就是在对方进程调用LoadLibrary,
//如果是Unicode程序,GetProcAddress需要使用LoadLibraryW
HMODULE hMouDle = GetModuleHandle("Kernel32");
PTHREAD_START_ROUTINE pfnLoadLibrary = (PTHREAD_START_ROUTINE)GetProcAddress(hMouDle, "LoadLibraryA");
if (pfnLoadLibrary == NULL)
{
MyOutputDebugString("获取LoadLibrary地址失败!!!");
return -1;
}
//4.创建远程线程
DWORD dwThreadId = 0;
HANDLE hThread = CreateRemoteThread(hProcess, NULL, 0, pfnLoadLibrary, pszRemoteBuffer, 0, &dwThreadId);
if (hThread == NULL)
{
MyOutputDebugString("创建远程线程失败 %d\n", GetLastError());
return -1;
}
//5 等待远程线程退出
DWORD exitCode = 0;
DWORD dwRet = 0;
WaitForSingleObject(hThread, INFINITE);
dwRet = GetExitCodeThread(hThread, &exitCode);
//6 检查退出码,如果退出码等于0,说明LoadLibrary失败了
if (dwRet == 0){
MyOutputDebugString("GetExitCodeThread failed\n");
return -1;
}
if (exitCode == 0){
MyOutputDebugString("LoadLibrary %s failed\n", dllPath);
}
VirtualFreeEx(hProcess, pszRemoteBuffer, MAX_PATH, MEM_DECOMMIT);
::CloseHandle(hThread);
::CloseHandle(hProcess);
getchar();
//6 释放注入的DLL, 需要再次创建远程线程执行FreeLibrary
return 0;
}