思路:将主线程挂起后获取到主线程的eip,然后将eip修改为shellcode的地址恢复线程运行,当shellcode执行完成后跳转到旧eip处继续执行。
1 typedef VOID(__stdcall *PFN_CALL)(const VOID *pvIn, VOID *pvOut); 2 3 BOOL CallForThread(DWORD dwThreadId, PFN_CALL pfnCall, const VOID *pvIn, VOID *pvOut) 4 { 5 _ASSERT(pfnCall != NULL); 6 _ASSERT(dwThreadId != ::GetCurrentThreadId()); 7 8 // 打开线程 9 HANDLE hThread = ::OpenThread(THREAD_ALL_ACCESS, FALSE, dwThreadId); 10 11 if (hThread == NULL) 12 { 13 return FALSE; 14 } 15 16 // 获取线程上下文 17 CONTEXT context = { 0 }; 18 19 ::SuspendThread(hThread); 20 context.ContextFlags = CONTEXT_ALL; 21 ::GetThreadContext(hThread, &context); 22 23 // 申请shellcode空间 24 VOID *pvShellCode = ::VirtualAlloc(NULL, 0x1000, MEM_COMMIT, PAGE_EXECUTE_READWRITE); 25 26 if (pvShellCode == NULL) 27 { 28 ::ResumeThread(hThread); 29 ::CloseHandle(hThread); 30 hThread = NULL; 31 32 return FALSE; 33 } 34 35 // 填充shellcode 36 DWORD dwExecuteFinishFlag = 0; 37 BYTE bShellCode[] = { 38 0x60, // pushad 39 0x68, 0x00, 0x00, 0x00, 0x00, // push pvOut 40 0x68, 0x00, 0x00, 0x00, 0x00, // push pvIn 41 0xB8, 0x00, 0x00, 0x00, 0x00, // mov eax, pfnCall 42 0xFF, 0xD0, // call eax 43 0xC7, 0x05, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, // mov dword ptr [执行完成标志指针], 1 44 0x61, // popad 45 0x68, 0x00, 0x00, 0x00, 0x00, // push 线程旧Eip 46 0xC3 // ret 47 }; 48 49 *(DWORD *)(&bShellCode[2]) = (DWORD)pvOut; 50 *(DWORD *)(&bShellCode[7]) = (DWORD)pvIn; 51 *(DWORD *)(&bShellCode[12]) = (DWORD)pfnCall; 52 *(DWORD *)(&bShellCode[20]) = (DWORD)&dwExecuteFinishFlag; 53 *(DWORD *)(&bShellCode[30]) = context.Eip; 54 memcpy_s(pvShellCode, 0x1000, bShellCode, sizeof (bShellCode)); 55 56 // 修改Eip执行 57 context.Eip = (DWORD)pvShellCode; 58 context.ContextFlags = CONTEXT_ALL; 59 ::SetThreadContext(hThread, &context); 60 ::ResumeThread(hThread); 61 ::PostThreadMessage(dwThreadId, 0, (WPARAM)0, (LPARAM)0); 62 63 // 等待执行完成标志置位 64 while (dwExecuteFinishFlag == 0) 65 { 66 ::Sleep(10); 67 } 68 69 // 判断ShellCode代码是否全部执行完成 70 DWORD dwShellCodeBeg = (DWORD)pvShellCode; 71 DWORD dwShellCodeEnd = (DWORD)pvShellCode + sizeof (bShellCode)-1; 72 73 while (TRUE)/*while-1*/ 74 { 75 // 获取Eip 76 context.ContextFlags = CONTEXT_ALL; 77 ::GetThreadContext(hThread, &context); 78 79 // 检查当前Eip是否在shellcode中 80 if (context.Eip < dwShellCodeBeg || context.Eip > dwShellCodeEnd) 81 { 82 break; 83 } 84 else 85 { 86 ::Sleep(10); 87 } 88 }/*end of while-1*/ 89 90 ::VirtualFree(pvShellCode, 0x1000, MEM_FREE); 91 pvShellCode = NULL; 92 ::CloseHandle(hThread); 93 hThread = NULL; 94 95 return TRUE; 96 }