“熱補丁”(hot patch)是微軟提出的一種安全Hook的機制,也是為了方便開發者對某些API函數進行下鈎子。這種方法不同於普通的Inline hook更改首部的五個字節,而是更改首部的七個字節。為什么是七個字節呢?下邊我們來講一下這個的原理。

我們可以看到CreateProcessW函數的首字節為 mov edi,edi(88 FF),這句匯編意思就是將edi的值放入edi,實際上並沒有什么用,我們還看到在這個API上邊有大段無用的字節。這就給了我們一種新的Hook思路,即將前兩個字節改為短跳轉指令(EB E9),使其跳到函數上邊五字節處,然后再將這五個字節改為長跳轉指令(E9 xxxxxxxx)。這樣,即使Hook失敗,也不影響函數的繼續執行。
接下來我們用代碼來實現這一功能:
// HotPatch.cpp : 定義控制台應用程序的入口點。 // #include "stdafx.h" #include <Windows.h> typedef BOOL(WINAPI *pfnCreateprocessW)( LPCTSTR lpApplicationName, LPTSTR lpCommandLine, LPSECURITY_ATTRIBUTES lpProcessAttributes, LPSECURITY_ATTRIBUTES lpThreadAttributes, BOOL bInheritHandles, DWORD dwCreationFlags, LPVOID lpEnvironment, LPCTSTR lpCurrentDirectory, LPSTARTUPINFO lpStartupInfo, LPPROCESS_INFORMATION lpProcessInformation ); BOOL SetPrivilege(LPCTSTR lpszPrivilege, BOOL bEnablePrivilege); BOOL HOOKByHotpatch(LPWSTR wzDllName, LPCSTR szFuncName, PROC pfnNewFunc); BOOL UnhookByHotpatch(LPWSTR wzDllName, LPCSTR szFuncName); BOOL WINAPI NewCreateProcessW( LPCTSTR lpApplicationName, LPTSTR lpCommandLine, LPSECURITY_ATTRIBUTES lpProcessAttributes, LPSECURITY_ATTRIBUTES lpThreadAttributes, BOOL bInheritHandles, DWORD dwCreationFlags, LPVOID lpEnvironment, LPCTSTR lpCurrentDirectory, LPSTARTUPINFO lpStartupInfo, LPPROCESS_INFORMATION lpProcessInformation ); int main() { SetPrivilege(SE_DEBUG_NAME, TRUE); //hook HOOKByHotpatch(L"kernel32.dll", "CreateProcessW", (PROC)NewCreateProcessW); Sleep(1000); STARTUPINFO si = { 0 }; si.cb = sizeof(si); si.dwFlags = STARTF_USESHOWWINDOW; si.wShowWindow = SW_SHOW; PROCESS_INFORMATION pi; //創建進程 TCHAR cmdLine[MAXBYTE] = L"notepad.exe"; BOOL bOk = CreateProcess(NULL, cmdLine, NULL, NULL, FALSE, NULL, NULL, NULL, &si, &pi); Sleep(1000); UnhookByHotpatch(L"kernel32.dll", "CreateProcessW"); return 0; } //設置權限 BOOL SetPrivilege(LPCTSTR lpszPrivilege, BOOL bEnablePrivilege) { TOKEN_PRIVILEGES TokenPrivileges;//權限令牌 HANDLE TokenHandle = NULL; //權限令牌句柄 LUID Luid; if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &TokenHandle)) { printf("OpenProcessToken error: %u\n", GetLastError()); return FALSE; } if (!LookupPrivilegeValue(NULL, // lookup privilege on local system lpszPrivilege, // privilege to lookup &Luid)) // receives LUID of privilege { printf("LookupPrivilegeValue error: %u\n", GetLastError()); return FALSE; } TokenPrivileges.PrivilegeCount = 1; TokenPrivileges.Privileges[0].Luid = Luid; if (bEnablePrivilege) TokenPrivileges.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; else TokenPrivileges.Privileges[0].Attributes = 0; // Enable the privilege or disable all privileges. //調整權限 if (!AdjustTokenPrivileges(TokenHandle, FALSE, &TokenPrivileges, sizeof(TOKEN_PRIVILEGES), (PTOKEN_PRIVILEGES)NULL, (PDWORD)NULL)) { printf("AdjustTokenPrivileges error: %u\n", GetLastError()); return FALSE; } if (GetLastError() == ERROR_NOT_ALL_ASSIGNED) { printf("The token does not have the specified privilege. \n"); return FALSE; } return TRUE; } //Hootpatch,將函數首字節改為 short jmp(EB F9) BOOL HOOKByHotpatch(LPWSTR wzDllName, LPCSTR szFuncName, PROC pfnNewFunc) { FARPROC pOrgFuncAddr = NULL; DWORD dwOldProtect, dwAddress; BYTE pBuf[5] = { 0xE9, 0, }; BYTE pBuf2[2] = { 0xEB, 0xF9 }; PBYTE pByte; pOrgFuncAddr = (FARPROC)GetProcAddress(GetModuleHandle(wzDllName), szFuncName); pByte = (PBYTE)pOrgFuncAddr; //判斷是否被勾 if (pByte[0] == 0xEB) return FALSE; //將前五字節代碼改為可讀可寫 VirtualProtect((LPVOID)((DWORD)pOrgFuncAddr - 5), 7, PAGE_EXECUTE_READWRITE, &dwOldProtect); // 1. NOP (0x90) //將前五字節改為E8 xxxxxxxx dwAddress = (DWORD)pfnNewFunc - (DWORD)pOrgFuncAddr; memcpy(&pBuf[1], &dwAddress, 4); memcpy((LPVOID)((DWORD)pOrgFuncAddr - 5), pBuf, 5); // 2. MOV EDI, EDI (0x8BFF) //將函數前兩個字節改為EB F9 memcpy(pOrgFuncAddr, pBuf2, 2); VirtualProtect((LPVOID)((DWORD)pOrgFuncAddr - 5), 7, dwOldProtect, &dwOldProtect); return TRUE; } BOOL UnhookByHotpatch(LPWSTR wzDllName, LPCSTR szFuncName) { FARPROC pHookFunc = NULL; DWORD dwOldProtect; PBYTE pByte; BYTE pBuf[5] = { 0x90, 0x90, 0x90, 0x90, 0x90 }; BYTE pBuf2[2] = { 0x8B, 0xFF }; pHookFunc = (FARPROC)GetProcAddress(GetModuleHandle(wzDllName), szFuncName); pByte = (PBYTE)pHookFunc; if (pByte[0] != 0xEB) return FALSE; VirtualProtect((LPVOID)pHookFunc, 5, PAGE_EXECUTE_READWRITE, &dwOldProtect); // 1. NOP (0x90) memcpy((LPVOID)((DWORD)pHookFunc - 5), pBuf, 5); // 2. MOV EDI, EDI (0x8BFF) memcpy(pHookFunc, pBuf2, 2); VirtualProtect((LPVOID)pHookFunc, 5, dwOldProtect, &dwOldProtect); return TRUE; } BOOL WINAPI NewCreateProcessW( LPCTSTR lpApplicationName, LPTSTR lpCommandLine, LPSECURITY_ATTRIBUTES lpProcessAttributes, LPSECURITY_ATTRIBUTES lpThreadAttributes, BOOL bInheritHandles, DWORD dwCreationFlags, LPVOID lpEnvironment, LPCTSTR lpCurrentDirectory, LPSTARTUPINFO lpStartupInfo, LPPROCESS_INFORMATION lpProcessInformation ) { MessageBox(NULL, L"Hook", L"", 0); return TRUE; }
需要注意的是使用這一方法鈎取的適用條件(NOP*5指令+MOV ESI,ESI),使用的時候一定要反匯編看一下目標函數是否滿足條件。
