第33章:隐藏进程-API代码修改技术(下)
前面的方法会频繁的进行脱钩和挂钩操作,并且在多线程环境中容易导致代码错误.因此使用 "热补丁" 技术就很必要了.
热补丁(Hot Patch/Fix ) 又称为7字节代码修改技术.
可以看到,前面七个字节并无实际意义.微软使用这种方法就是为了方便在不关机的情况下临时修改库文件,重启时修改的目标库文件会被完全替代.
前五个字节可以被用来当作远跳转 jmp( E9 ) xxxxxxxx ,后两个字节被用作 短跳转 jmp( EB ) xxxx .
此种方法可以尽可能的减少对有效代码的修改,使得当线程在修改时,另一个线程仍能正常得执行代码.
只需要在上一节的代码中添加这些代码即可.
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) { char szCurProc[MAX_PATH] = {0,}; char *p = NULL; GetModuleFileNameA(NULL, szCurProc, MAX_PATH); p = strrchr(szCurProc, '\\'); if( (p != NULL) && !_stricmp(p+1, "HideProc2.exe") ) return TRUE; // change privilege SetPrivilege(SE_DEBUG_NAME, TRUE); switch( fdwReason ) { case DLL_PROCESS_ATTACH : // hook hook_by_hotpatch("kernel32.dll", "CreateProcessA", (PROC)NewCreateProcessA); hook_by_hotpatch("kernel32.dll", "CreateProcessW", (PROC)NewCreateProcessW); hook_by_code("ntdll.dll", "ZwQuerySystemInformation", (PROC)NewZwQuerySystemInformation, g_pOrgZwQSI); //注意此处,使用的是5字节的代码修改技术 break; // 因为它不像其它 API 被很多进程所使用. case DLL_PROCESS_DETACH : // unhook unhook_by_hotpatch("kernel32.dll", "CreateProcessA"); unhook_by_hotpatch("kernel32.dll", "CreateProcessW"); unhook_by_code("ntdll.dll", "ZwQuerySystemInformation", g_pOrgZwQSI); break; } return TRUE; }
BOOL unhook_by_hotpatch(LPCSTR szDllName, LPCSTR szFuncName) //脱钩 { FARPROC pFunc; DWORD dwOldProtect; PBYTE pByte; BYTE pBuf[5] = { 0x90, 0x90, 0x90, 0x90, 0x90 }; //nop *5 BYTE pBuf2[2] = { 0x8B, 0xFF }; // mov edi,edi pFunc = (FARPROC)GetProcAddress(GetModuleHandleA(szDllName), szFuncName); pByte = (PBYTE)pFunc; if( pByte[0] != 0xEB ) return FALSE; VirtualProtect((LPVOID)pFunc, 5, PAGE_EXECUTE_READWRITE, &dwOldProtect); //修改节区5个字节的属性. // 1. NOP (0x90) memcpy((LPVOID)((DWORD)pFunc - 5), pBuf, 5); // 2. MOV EDI, EDI (0x8BFF) memcpy(pFunc, pBuf2, 2); VirtualProtect((LPVOID)pFunc, 5, dwOldProtect, &dwOldProtect); return TRUE; }
BOOL hook_by_hotpatch(LPCSTR szDllName, LPCSTR szFuncName, PROC pfnNew) //上钩 { FARPROC pFunc; DWORD dwOldProtect, dwAddress; BYTE pBuf[5] = { 0xE9, 0, }; BYTE pBuf2[2] = { 0xEB, 0xF9 }; PBYTE pByte; pFunc = (FARPROC)GetProcAddress(GetModuleHandleA(szDllName), szFuncName); pByte = (PBYTE)pFunc; if( pByte[0] == 0xEB ) return FALSE; VirtualProtect((LPVOID)((DWORD)pFunc - 5), 7, PAGE_EXECUTE_READWRITE, &dwOldProtect); //修改7个字节的属性 // 1. NOP (0x90) dwAddress = (DWORD)pfnNew - (DWORD)pFunc; //不用减去本条指令的长度,因为 pFunc 本身就多了5字节 memcpy(&pBuf[1], &dwAddress, 4); memcpy((LPVOID)((DWORD)pFunc - 5), pBuf, 5); //写入相应地址.// 2. MOV EDI, EDI (0x8BFF) memcpy(pFunc, pBuf2, 2); //短跳转,相距本条指令的下一条指令7字节 VirtualProtect((LPVOID)((DWORD)pFunc - 5), 7, dwOldProtect, &dwOldProtect); //恢复属性 return TRUE; }
BOOL WINAPI NewCreateProcessA( LPCTSTR lpApplicationName, LPTSTR lpCommandLine, LPSECURITY_ATTRIBUTES lpProcessAttributes, LPSECURITY_ATTRIBUTES lpThreadAttributes, BOOL bInheritHandles, DWORD dwCreationFlags, LPVOID lpEnvironment, LPCTSTR lpCurrentDirectory, LPSTARTUPINFO lpStartupInfo, LPPROCESS_INFORMATION lpProcessInformation ) { BOOL bRet; FARPROC pFunc; // original API 调用 pFunc = GetProcAddress(GetModuleHandleA("kernel32.dll"), "CreateProcessA"); pFunc = (FARPROC)((DWORD)pFunc + 2); bRet = ((PFCREATEPROCESSA)pFunc)(lpApplicationName, // ASCII 和 Unicode 的函数版本只有这里不一样. lpCommandLine, lpProcessAttributes, lpThreadAttributes, bInheritHandles, dwCreationFlags, lpEnvironment, lpCurrentDirectory, lpStartupInfo, lpProcessInformation); // 向生成的子进程注入 stealth2.dll if( bRet ) InjectDll2(lpProcessInformation->hProcess, STR_MODULE_NAME); return bRet; }
值得注意的是:
①并非所有的 API 都支持这个技术.
② ntdll.dll 的 API 代码都比较短,且内部代码地址无依赖性,因此可以将原 API 备份到用户内存区域,然后使用5字节修改技术,修改原 API 的起始部分,使其跳转到备份的 API 执行代码.