刚写完了一个全局的inline hook 库, 易用性感觉还不是很好。 但是"全局"2字是亮点。
先说一下全局的思路。
首先遍历当前所有的进程。当然我们想到的大多数方法是用一些枚举进程的api来做,我想到了另一种方法。暴力穷举,这样可以把隐藏进程也给揪出来。
//暴力穷举进程(进程ID都是4的整数倍,而且小于0x0000ffff)
HANDLE hProcess = 0;
for (DWORD dwProcessID = 0; dwProcessID<=0x0000FFFF; dwProcessID+=4)
{
hProcess =::OpenProcess(
PROCESS_QUERY_INFORMATION | // Required by Alpha
PROCESS_CREATE_THREAD | // For CreateRemoteThread
PROCESS_VM_OPERATION | // For VirtualAllocEx/VirtualFreeEx
PROCESS_VM_WRITE | // For WriteProcessMemory
PROCESS_VM_READ , // For CreateRemoteThread
FALSE, dwProcessID);
if (hProcess == 0)
{
continue;
}
//有进程句柄了。。远程线程注入啥的。。。
}
那么问题来了,新创建的进程如何hook? 我见到过很多所谓的全局hook 仅仅是 SetWindowsHook 挂了一个消息钩子 这样的烂钩子不要也罢。
我想到了hook CreateProcess 于是就想了想创建进程的方法 WinExec ShellExcute 等等 用OD简单的跟踪了一下 发现最终都会调用Kernel32!CreateProcessInternalW函数 好了 就勾他吧。
在 我们的 MyCreateProcessInternalW中如何处理才能让新建立的线程也能Load我们的dll呢
方法有很多 我挑了一种最简单的 先用CREATE_SUSPENDED属性挂起进程 然后修改入口点 然后构造shellcode 完成load我们的dll的任务 最后在返回入口点之前把入口点修改过的代码恢复过去。
具体代码如下(shellcode代码写的太挫了 - -!):
BOOL __stdcall MyCreateProcessInternalW(
HANDLE hToken,
LPCTSTR lpApplicationName,
LPTSTR lpCommandLine,
LPSECURITY_ATTRIBUTES lpProcessAttributes,
LPSECURITY_ATTRIBUTES lpThreadAttributes,
BOOL bInheritHandles,
DWORD dwCreationFlags,
LPVOID lpEnvironment,
LPCTSTR lpCurrentDirectory,
LPSTARTUPINFO lpStartupInfo,
LPPROCESS_INFORMATION lpProcessInformation ,
PHANDLE hNewToken)
{
//以CREATE_SUSPENDED属性先挂起进程
BOOL bRet = (BOOL)g_ApiInfo[Index_CreateProcessInternalW].InvokeApi( hToken,
lpApplicationName,
lpCommandLine,
lpProcessAttributes,
lpThreadAttributes,
bInheritHandles,
dwCreationFlags|CREATE_SUSPENDED,
lpEnvironment,
lpCurrentDirectory,
lpStartupInfo,
lpProcessInformation,
hNewToken);
if (bRet == FALSE)
{
return bRet;
}
//得到入口点。
DWORD dwEntry = GetProcessEntryPoint(lpProcessInformation->hProcess);
if (dwEntry == 0)
{
ResumeThread(lpProcessInformation->hThread);
return TRUE;
}
DWORD dwLoadLibrary = (DWORD)GetProcAddress(GetModuleHandle("kernel32.dll"), "LoadLibraryA");
DWORD dwVirtualProtect = (DWORD)GetProcAddress(GetModuleHandle("kernel32.dll"), "VirtualProtect");
//保存前5个字节 以后好恢复
BYTE bSave[5] = {0};
DWORD dwRead = 0;
bRet = ReadProcessMemory(lpProcessInformation->hProcess, (LPVOID)dwEntry, bSave, 5, &dwRead);
//编写shellcode
/*cc
9c pushfd
0040101D 50 push eax ; 保存eax寄存器 ;;data offset + 0
0040101E 834424 08 FB add dword ptr ss:[esp+8],-5 ; 修改跳回去的地址为原入口点(在ret之前修正入口点)
00401023 E8 0C000000 call 00401034 ; 将后面的字符串push进去
00401028 6D 79 68 6F 6F 6B 2E 64 6C 6C 00 00 "myhook.dll"
00401034 E8 3E0D407C call kernel32.LoadLibraryA ; 加载dll ;;data offset + 24
00401039 6A 00 push 0 ; 修改入口点页属性 这里是申请一个空间
0040103B 54 push esp ; old 属性地址 即0的地址
0040103C 6A 40 push 40 ; PAGE_EXECUTE_READWRITE
0040103E 6A 05 push 5
00401040 68 99999999 push 99999999 ; 入口点地址 ;;data offset + 36
00401045 E8 860A407C call kernel32.VirtualProtect ; 修改入口点前5字节属性为可写 否则崩 ;;data offset + 41
0040104A 58 pop eax ; 平衡堆栈
0040104B B8 99999999 mov eax,99999999 ;;data offset 47
00401050 C600 11 mov byte ptr ds:[eax],11 ;恢复入口点的5字节 ;;data offset 53
00401053 40 inc eax
00401054 C700 88888808 mov dword ptr ds:[eax],8888888 ;;data offset 57
0040105A 58 pop eax
9d popfd
0040105B C3 retn
*/
//第一个字节是为了用WinDbg调试而添加的,调试完成了就置为0x90了
BYTE shellcode[66] = { 0x90, 0x9c, 0x50, 0x83, 0x44, 0x24, 0x08, 0xfb, 0xe8, 0x0c, 0x00, 0x00, 0x00, 0x6d, 0x79, 0x68,
0x6f, 0x6f, 0x6b, 0x2e, 0x64, 0x6c, 0x6c, 0x00, 0x00, 0xe8, 0x00, 0x00, 0x00, 0x00,
0x6a, 0x00, 0x54, 0x6a, 0x40, 0x6a, 0x05, 0x68, 0x00, 0x00, 0x00, 0x00,
0xe8, 0x00, 0x00, 0x00, 0x00, 0x58,
0xb8, 0x00, 0x00, 0x00, 0x00, 0xc6, 0x00, 0x00, 0x40, 0xc7, 0x00, 0x00, 0x00, 0x00, 0x00,
0x58, 0x9d, 0xc3};
//申请空间
LPVOID lpRomateAddress = VirtualAllocEx(lpProcessInformation->hProcess, 0, 66, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
//修改call library地址
PBYTE pOffset = (PBYTE)shellcode + 2;
*(DWORD*)(pOffset+24) = dwLoadLibrary - ((DWORD)lpRomateAddress + 2 + 23) - 5;
*(DWORD*)(pOffset+36) = dwEntry;
*(DWORD*)(pOffset+41) = dwVirtualProtect -((DWORD)lpRomateAddress + 2 + 40) -5;
*(DWORD*)(pOffset+47) = dwEntry;
*(BYTE*)(pOffset+53) = bSave[0];
*(DWORD*)(pOffset+57) = *(DWORD*)(bSave+1);
//写入进程空间
bRet = WriteProcessMemory(lpProcessInformation->hProcess, lpRomateAddress, shellcode, 66, 0);
//修改前5字节
//call xxx
BYTE bSet[5] = {0};
bSet[0] = 0xe8;
*(DWORD*)(bSet+1) = (DWORD)lpRomateAddress - dwEntry - 5;
bRet = WriteProcessMemory(lpProcessInformation->hProcess, LPVOID(dwEntry), bSet, 5, 0);
ResumeThread(lpProcessInformation->hThread);
return TRUE;
}
见过一些帖子说 从入口点开始找0xe8(相对地址 call 指令) 然后修改他的代码 让他跳到我们构造的shellcode中 然后xxxx 这个方法感觉还是不太好 就像附件中我给的一个 最简单PE.exe 这个例子就挂不起来 还是修改入口点比较通用。
在.h文件中用到 了一个 GetOpcodeSize函数 貌似是海风大侠的 谢谢了 否则就得用反汇编引擎。
ps 一般来说这样的需求要用WinDbg来调试子进程。 OllyDbg不太好用。
看雪招聘平台创建简历并且简历完整度达到90%及以上可获得500看雪币~