DLL远程线程注入
代码远程线程注入
DLL远程线程注入,一言以蔽之:在被注入进程中创建一个线程,线程函数的作用是加载指定DLL。
代码远程线程注入,实质是自定义线程函数,然后将线程函数代码和线程函数中用到的数据分别注入到目标进程,最后将数据作为线程函数的参数创建远程线程,执行自定义操作。
示例
不依赖自定义dll,在被注入进程中创建线程实现弹窗,窗口标题为被注入进程的位置。
线程函数:
- 线程函数要做的事情是弹窗,弹窗需要得到
MessageBoxA
在被注入进程中的位置,因此需要加载user32.dll,还需要字符串"MessageBoxA"。加载要用到Kernel32.dll中的LoadLibrary
、GetProcAddress
,还需要字符串"user32.dll",在标题显示被注入进程的位置需要Kernal32.dll中的GetModuleFileName
获取进程当前的运行目录。 - 上述用到的数据,在注入函数创建线程函数时被封装为一个结构体作为参数传给线程函数。
- 线程函数获得数据,调用被注入进程中的
MessageBoxA
弹窗
注入函数:
- 获取线程函数要用到的Kernel32.dll中的导出函数地址,用函数指针存放其在本进程中的地址,Kernel32.dll在各进程中位置相同,所以可用其在本进程的地址代替在被注入进程中的地址
- 上述所有线程函数用到的函数地址、数据等,再加一个弹窗要显示的字符串,封装到一个结构体DATA中。
- 将结构体、线程函数的代码注入进程
- 创建远程线程,将结构体作为参数传给线程函数。
源码
// 不依赖dll的远程线程注入
#include <windows.h>
#include <stdio.h>
#define STRLEN 20
typedef struct _DATA{
// 4字节指针,存放指向函数的内存地址
FARPROC fpLoadLibrary;
FARPROC fpGetProcAddress;
FARPROC fpGetModuleFileName;
char strUser32Dll[STRLEN]; // 保存"user32.dll"字符串
char strMessageBox[STRLEN]; // 保存"MessageBoxA"字符串
char showStr[STRLEN]; // 保存MessageBox弹出的字符串
} DATA, *PDATA;
// 远程线程函数
// 因为要在线程中调用MessageBox,而user32.dll在各进程中位置不同,所以需获得其在被注入进程中的地址
DWORD WINAPI RemoteThreadFunc(LPVOID lpParam){
PDATA pData = (PDATA)lpParam;
// 声明函数指针
HMODULE (__stdcall *MyLoadLibrary)(LPCTSTR);
FARPROC (__stdcall *MyGetProcAddress)(HMODULE, LPCTSTR);
int (__stdcall *MyMessageBox)(HWND, LPCTSTR, LPCTSTR, UINT);
DWORD (__stdcall *MyGetModuleFileName)(HMODULE, LPTSTR, DWORD);
// Kernel32.dll在各进程中位置相同,所以可用其在本进程的地址代替在被注入进程中的地址
MyLoadLibrary = (HMODULE (__stdcall *)(LPCTSTR))pData->fpLoadLibrary;
MyGetProcAddress = (FARPROC (__stdcall *)(HMODULE, LPCTSTR))pData->fpGetProcAddress;
MyGetModuleFileName = (DWORD (__stdcall *)(HMODULE, LPTSTR, DWORD))pData->fpGetModuleFileName;
// 加载user32.dll
HMODULE hModule = MyLoadLibrary(pData->strUser32Dll);
// 获取user32.dll中MessageBoxA的地址
MyMessageBox = (int (__stdcall *)(HWND, LPCTSTR, LPCTSTR, UINT))MyGetProcAddress(hModule, pData->strMessageBox);
char szModuleFileName[MAX_PATH] = {0};
MyGetModuleFileName(NULL, szModuleFileName, MAX_PATH);
// 弹窗
MyMessageBox(NULL, pData->showStr, szModuleFileName, MB_OK);
return 0;
}
// 注入函数
VOID Inject(DWORD dwPid){
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPid);
if (hProcess == NULL){
printf("failed to open process.\n");
return ;
}
DATA myData = {0};
// 获取Kernel32.dll中的导出函数,用函数指针存放其在本进程中的地址
myData.fpLoadLibrary = (FARPROC)GetProcAddress(GetModuleHandle("kernel32.dll"), "LoadLibraryA");
myData.fpGetProcAddress = (FARPROC)GetProcAddress(GetModuleHandle("kernel32.dll"), "GetProcAddress");
myData.fpGetModuleFileName = (FARPROC)GetProcAddress(GetModuleHandle("kernel32.dll"), "GetModuleFileNameA");
// 存入所需字符串,线程函数将Load user32.dll,并获取MessageBoxA在被注入进程中的位置
lstrcpy(myData.strUser32Dll, "user32.dll");
lstrcpy(myData.strMessageBox, "MessageBoxA");
lstrcpy(myData.showStr, "non dll inject success.");
// 在目标进程中申请空间
LPVOID lpData = VirtualAllocEx(hProcess, NULL, sizeof(myData), MEM_COMMIT, PAGE_READWRITE);
if (!lpData){
printf("alloc mem failed.\n");
printf("%x", GetLastError());
return;
}
// 将myData结构体写入目标进程
if(!WriteProcessMemory(hProcess, lpData, &myData, sizeof(myData), NULL)){
printf("write mem failed.\n");
return;
}
// 远程线程函数代码大小
DWORD dwThradFuncSize = 0x4000;
// 为将被注入的线程函数代码申请空间
LPVOID lpCode = VirtualAllocEx(hProcess, NULL, dwThradFuncSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
if(!lpCode){
printf("alloc mem failed 2.\n");
return;
}
// 注入,注意C才能用&RemoteThreadFunc取线程函数地址,C++不能
if(!WriteProcessMemory(hProcess, lpCode, &RemoteThreadFunc, dwThradFuncSize, NULL)){
printf("write mem failed 2.\n");
return;
}
// 创建远程线程
HANDLE hThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)lpCode, lpData, 0, NULL);
if (hThread){
printf("non dll inject success.\n");
}
else{
return;
}
WaitForSingleObject(hThread, INFINITE);
CloseHandle(hThread);
CloseHandle(hProcess);
}
int main() {
Inject(19328);
return 0;
}
演示
被注入进程delete.exe
看很多人说VC好像需要使用release方式,不过我的CLion用release和debug都可以成功注入