函数介绍
百度百科
CreateRemoteThread是一个Windows API函数,它能够创建一个在其它进程地址空间中运行的线程(也称:创建远程线程)
这个函数很好理解,即控制其它进程来运行一个线程!由于是控制其它进程运行所以函数的参数以及需要的数据必须存在目标进程中
,否则必须将数据写入到目标进程中,写入数据的过程如下:
-
CreateToolhelp32Snapshot
获取目标进程PID -
AdjustTokenPrivileges
提升当前进程Debug
权限 -
OpenProcess
获取目标进程句柄 -
VirtualAllocEx
在目标进程中分配空间 -
WriteProcessMemory
将数据写入到目标进程 -
VirtualFreeEx
释放空间以及CloseHandle
关闭句柄
特别要注意WriteProcessMemory
的第三个参数是一个指针类型,指向需要写入的数据的首地址
参数含义
int WINAPI CreateRemoteThread(HANDLE hProcess,LPSECURITY_ATTRIBUTES lpThreadAttributes,SIZE_T dwStackSize,LPTHREAD_START_ROUTINE lpStartAddress,LPVOID lpParameter,DWORD dwCreationFlags,LPDWORD lpThreadId);/*hProcess:目标进程句柄lpThreadAttributes:线程的属性通常设为NULLdwStackSize:线程栈初始大小,通常设为0表示系统默认lpStartAddress:线程函数地址,函数指针类型lpParameter:传递给线程函数的参数dwCreationFlags:线程创建标志,通常设为0lpThreadId:返回线程ID,通常设为NULL*/
特别注意第四个参数的类型是函数指针类型,可以用LPTHREAD_START_ROUTINE
表示,也可以typedef
自定义函数指针
代码注入
前置技能
写代码之前必须要知道很多的操作系统库是伴随系统启动而启动的,它们的基地址固定,譬如Kernel32
、User32
、GDI
、Ntdll
等
注入思路
用计算器进程做实验,让计算器进程自己调用MessageBox
弹窗失败过程 - 由于
User32
地址固定,即进程间的库函数地址相同,所以先获取当前进程中的MessageBoxA
的地址(相当于获取了目标进程中该函数的地址),然后创建一个结构体存放MessageBoxA
的四个参数,将该结构体写入到目标进程(不要写入指针,必须写入真实字符,因为当前进程的指针指向的数据在目标进程中是没有的),最后用CreateRemoteThread
创建远程线程调用MessageBoxA
,最终失败了,失败原因未知!用 OD 附加目标进程发现参数是成功写入的! - 使用内联汇编写一段完整运行的代码,写入目标进程再创建远程线程执行,尚未实验!
- 直接向远程进程空间中写入类似 ShellCode 的字节码,然后创建远程线程执行,尚未实验!
成功思路
- 由于
CreateRemoteThread
传入多个参数时无法正确调用,所以我参考网上的一个思路,自写线程函数然后在线程函数中分配函数指针
以及函数参数
注意事项
注意整数
与指针
类型的强制转换 完整代码(附带注释)
#include#include #include /* 自定义函数指针 */typedef int (*MY_MESSAGEBOX)(HWND, LPCTSTR, LPCTSTR, DWORD);/* 写入的数据 */typedef struct Param{ char caption[22]; DWORD dwFunc;} param, *Pparam;/* 写入的函数代码,主要用于赋予函数指针地址以及定义参数调用方式 */DWORD threadProc(LPVOID lpdata){ param *remoteparam = (param *)lpdata; /* 初始化之前定义函数指针 */ MY_MESSAGEBOX messagebox; messagebox = (MY_MESSAGEBOX)remoteparam->dwFunc; messagebox(NULL, remoteparam->caption, remoteparam->caption, 0); messagebox(NULL, remoteparam->caption, remoteparam->caption, 0); return 0;}/* 根据进程名称获取进程的PID */int getPID(char *target){ PROCESSENTRY32 pe32; MODULEENTRY32 me32; HANDLE hProcess, hSnapshot_proc, hSnapshot_mod; pe32.dwSize = sizeof(pe32); hSnapshot_proc = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); if (Process32First(hSnapshot_proc, &pe32)) { do { if (!strcmp(pe32.szExeFile, target)) { return pe32.th32ProcessID; } } while (Process32Next(hSnapshot_proc, &pe32)); } CloseHandle(hSnapshot_proc); return 0;}/* 获取当前进程的Debug权限 */BOOL SetProcessPrivilege(char *lpName, BOOL opt){ HANDLE tokenhandle; TOKEN_PRIVILEGES NewState; if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &tokenhandle)) { LookupPrivilegeValue(NULL, lpName, &NewState.Privileges[0].Luid); NewState.PrivilegeCount = 1; NewState.Privileges[0].Attributes = opt != 0 ? 2 : 0; AdjustTokenPrivileges(tokenhandle, FALSE, &NewState, sizeof(NewState), NULL, NULL); CloseHandle(tokenhandle); return 1; } else { return 0; }}int main(int argc, char *argv[]){ HANDLE hThread, hProcess = NULL; int pid; param data; SetProcessPrivilege("SeDebugPrivilege", 1); /* 获取目标进程PID,根据PID打开进程获取目标进程句柄 */ pid = getPID("calc.exe"); if (pid) { hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid); } if (hProcess == NULL) { MessageBox(NULL, "OpenProcess Failed!", "Wrong", MB_OK); } /* 在目标进程中为线程函数那一段代码分配空间并写入数据 */ LPVOID remoteFunc = VirtualAllocEx(hProcess, NULL, 4096, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); if (!WriteProcessMemory(hProcess, remoteFunc, &threadProc, 4096, NULL)) { MessageBox(NULL, "WriteProcessMemory Failed!", "Wrong", MB_OK); VirtualFreeEx(hProcess, remoteFunc, 4096, MEM_RELEASE); CloseHandle(hProcess); } /* 获取MessageBoxA的地址赋值给param结构体 */ data.dwFunc = (DWORD)GetProcAddress(GetModuleHandle("user32"), "MessageBoxA"); /* LPTHREAD_START_ROUTINE */ strcpy(data.caption, "Inject Code Success!"); /* 在目标进程中分配结构体的空间并写入数据 */ LPVOID remoteData = VirtualAllocEx(hProcess, NULL, sizeof(data), MEM_COMMIT, PAGE_READWRITE); if (!WriteProcessMemory(hProcess, remoteData, &data, sizeof(data), NULL)) { MessageBox(NULL, "WriteProcessMemory Failed!", "Wrong", MB_OK); VirtualFreeEx(hProcess, remoteData, sizeof(data), MEM_RELEASE); CloseHandle(hProcess); } /* 启动远程线程调用写入的线程函数,线程函数会自动获取参数并调用函数指针指向的函数 */ hThread = CreateRemoteThread(hProcess, NULL, 0, remoteFunc, remoteData, 0, NULL); /* 释放空间并关闭句柄 */ VirtualFreeEx(hProcess, remoteFunc, 4096, MEM_RELEASE); VirtualFreeEx(hProcess, remoteData, sizeof(data), MEM_RELEASE); if (hThread == NULL) { MessageBox(NULL, "CreateRemoteThread Failed!", "Wrong", MB_OK); CloseHandle(hProcess); } WaitForSingleObject(hThread, INFINITE); CloseHandle(hThread); CloseHandle(hProcess); SetProcessPrivilege("SeDebugPrivilege", 0); return 0;}
编译命令gcc Inject_code1.c -o Inject_code1
运行截图
先打开计算器双击运行程序
计算器进程成功弹窗
总结
其实代码注入比直接 DLL 注入要复杂很多,我是强烈建议用 DLL 注入的,这代码注入写得我脑壳疼!QAQDLL注入
前置技能
写代码之前必须要知道很多的操作系统库是伴随系统启动而启动的,它们的基地址固定,譬如Kernel32
、User32
、GDI
、Ntdll
等
注入思路
比起代码注入这注入思路就简单许多了,先获取你想要注入的 DLL 完整路径,然后将路径写入到目标进程空间,最后启动远程线程在目标进程中调用LoadLibraryA
就行了 注意事项
需要注意的有两点,一是位数统一,32位程序调32位dll注32位进程,64位同理
,当然也有办法让32位程序注64位进程,尚未实验!二是注意释放资源,在目标进程中加载了dll,就应该启动一个远程线程帮忙卸载dll
。我为了方便没去卸载dll,采用了注销电脑法! 完整代码
先编译一个 dll,方便之后注入用,dll 代码如下:#include#include BOOL WINAPI DllMain(HINSTANCE hinstDll, DWORD dwReason, LPVOID lpvRevered){ MessageBox(NULL, TEXT("This is DllMain!"), TEXT("Tips"), MB_OK); switch (dwReason) { case DLL_PROCESS_ATTACH: /* 只有第一次会进到这里,之后再次Attach不会产生DLL_PROCESS_ATTACH,只会增加DLL的调用次数 */ MessageBox(NULL, TEXT("Process Load Dll Success!"), TEXT("Tips"), MB_OK); break; case DLL_PROCESS_DETACH: MessageBox(NULL, TEXT("Process Unload Dll Success!"), TEXT("Tips"), MB_OK); break; case DLL_THREAD_ATTACH: MessageBox(NULL, TEXT("Thread load Dll Success!"), TEXT("Tips"), MB_OK); break; case DLL_THREAD_DETACH: MessageBox(NULL, TEXT("Thread Unload Dll Success!"), TEXT("Tips"), MB_OK); break; } return TRUE;}
使用gcc test.c -shared -o test.dll
编译成 dll 文件,下面是注入进程的代码
#include#include #include /* typedef DWORD (*My_LoadLibrary)(char *); */int getPID(char *target){ PROCESSENTRY32 pe32; MODULEENTRY32 me32; HANDLE hProcess, hSnapshot_proc, hSnapshot_mod; pe32.dwSize = sizeof(pe32); hSnapshot_proc = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); if (Process32First(hSnapshot_proc, &pe32)) { do { if (!strcmp(pe32.szExeFile, target)) { return pe32.th32ProcessID; } } while (Process32Next(hSnapshot_proc, &pe32)); } CloseHandle(hSnapshot_proc); return 0;}BOOL SetProcessPrivilege(char *lpName, BOOL opt){ HANDLE tokenhandle; TOKEN_PRIVILEGES NewState; if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &tokenhandle)) { LookupPrivilegeValue(NULL, lpName, &NewState.Privileges[0].Luid); NewState.PrivilegeCount = 1; NewState.Privileges[0].Attributes = opt != 0 ? 2 : 0; AdjustTokenPrivileges(tokenhandle, FALSE, &NewState, sizeof(NewState), NULL, NULL); CloseHandle(tokenhandle); return 1; } else { return 0; }}int main(int argc, char *argv[]){ HANDLE hThread, hProcess = NULL; DWORD remoteMod; LPVOID lpbuffer; int pid; char dllname[MAX_PATH] = "C:\\MyFiles\\Csrc\\Inject\\test.dll"; lpbuffer = dllname; SetProcessPrivilege("SeDebugPrivilege", 1); pid = getPID("calc.exe"); if (pid) { hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid); } if (hProcess == NULL) { MessageBox(NULL, "Wrong", "Wrong", MB_OK); } PTHREAD_START_ROUTINE lib0 = (PTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandle("Kernel32"), "LoadLibraryA"); /* LoadLibraryA而不是LoadLibrary */ LPVOID remoteData = VirtualAllocEx(hProcess, NULL, sizeof(dllname), MEM_COMMIT, PAGE_READWRITE); /* lpbuffer或者&dllname */ if (!WriteProcessMemory(hProcess, remoteData, lpbuffer, sizeof(dllname), NULL)) { MessageBox(NULL, "Wrong", "Wrong", MB_OK); VirtualFreeEx(hProcess, remoteData, sizeof(dllname), MEM_RELEASE); CloseHandle(hProcess); } /* CreateRemoteThread的第三个参数是函数指针类型,可以用typedef自定义函数指针 */ hThread = CreateRemoteThread(hProcess, NULL, 0, lib0, remoteData, 0, NULL); VirtualFreeEx(hProcess, remoteData, sizeof(dllname), MEM_RELEASE); if (hThread == NULL) { MessageBox(NULL, "Wrong", "Wrong", MB_OK); CloseHandle(hProcess); } WaitForSingleObject(hThread, INFINITE); CloseHandle(hThread); CloseHandle(hProcess); SetProcessPrivilege("SeDebugPrivilege", 0); return 0;}
编译命令gcc Inject_dll2.c -o Inject_dll2
运行截图
先启动计算器进程双击运行程序
计算器进程成功弹窗