IAT即Import Address Table 是PE文件的输入地址表,一个程序在运行时会加载很多模块,调用很多API函数,但这些函数不一定要程序本身实现。比如调用MessageboxA来显示一个对话框,我们并没有编写MessageboxA的实现过程,它其实是在User32.dll这个库中实现的,而程序运行时通过导入它的地址来调用。
程序映像被加载到内存时,下面这张PE结构图很重要。
为了说明简洁,这里修改的是程序自身的IAT,如果hook其它进程的IAT,使用到远程线程,后面亦附实例代码。下面是IAT hook的实现代码:
#include <stdio.h> #include <windows.h> #include <Dbghelp.h> #pragma comment(lib, “Dbghelp.lib”) #pragma comment(lib, “User32.lib”) typedef int(__stdcall *OLD_MessageBox)(HWND hWnd, LPCTSTR lpText, LPCTSTR lpCaption, UINT uType); OLD_MessageBox g_procOldMessageBox = NULL; int __stdcall HOOK_MessageBox(HWND hWnd, LPCTSTR lpText, LPCTSTR lpCaption, UINT uType) { //printf(“%s\t%d\r\n”, __FUNCTION__, __LINE__); return g_procOldMessageBox(hWnd, LPCTSTR(“Yeah, I get it.”), LPCTSTR(“hooked”), uType); } int replace_IAT(const char *pDllName, const char *pApiName, bool bReplace) { HANDLE hProcess = ::GetModuleHandle(NULL); DWORD dwSize = 0; /* 1.get IAT pointer of all dlls of a PE file. */ PIMAGE_IMPORT_DESCRIPTOR pImageImport = (PIMAGE_IMPORT_DESCRIPTOR)ImageDirectoryEntryToData(hProcess, TRUE, IMAGE_DIRECTORY_ENTRY_IMPORT, &dwSize); if (NULL == pImageImport) { return -1; } PIMAGE_IMPORT_BY_NAME pImageImportByName = NULL; PIMAGE_THUNK_DATA pImageThunkOriginal = NULL; PIMAGE_THUNK_DATA pImageThunkReal = NULL; while (pImageImport) { /* 2.get IAT pointer of a specified dll, such as User32.dll. */ if (0 == _strcmpi((char*)((PBYTE)hProcess + pImageImport->Name), pDllName)) { break; } ++pImageImport; } if (NULL == pImageImport) { return -1; } /* 3.get OriginalFirstThunk and FirstThunk pointer = array name. */ pImageThunkOriginal = (PIMAGE_THUNK_DATA)((PBYTE)hProcess + pImageImport->OriginalFirstThunk); pImageThunkReal = (PIMAGE_THUNK_DATA)((PBYTE)hProcess + pImageImport->FirstThunk); /* 4.walk around the two array. */ while (pImageThunkOriginal->u1.Function) { /* search by function name. */ if ((pImageThunkOriginal->u1.Ordinal & IMAGE_ORDINAL_FLAG) != IMAGE_ORDINAL_FLAG) { /* get function name. */ pImageImportByName = (PIMAGE_IMPORT_BY_NAME)((PBYTE)hProcess + pImageThunkOriginal->u1.AddressOfData); if (0 == _strcmpi(pApiName, (char*)pImageImportByName->Name)) { /* change the memory R/D privilege. */ MEMORY_BASIC_INFORMATION mbi_thunk; VirtualQuery(pImageThunkReal, &mbi_thunk, sizeof(MEMORY_BASIC_INFORMATION)); VirtualProtect(mbi_thunk.BaseAddress, mbi_thunk.RegionSize, PAGE_READWRITE, &mbi_thunk.Protect); if (true == bReplace) { g_procOldMessageBox = (OLD_MessageBox)pImageThunkReal->u1.Function; pImageThunkReal->u1.Function = (DWORD)HOOK_MessageBox; } else { pImageThunkReal->u1.Function = (DWORD)g_procOldMessageBox; } DWORD dwOldProtect; VirtualProtect(mbi_thunk.BaseAddress, mbi_thunk.RegionSize, mbi_thunk.Protect, &dwOldProtect); break; } } ++pImageThunkOriginal; ++pImageThunkReal; } return 0; } int main() { replace_IAT(“User32.dll”, “MessageBoxA”, true); MessageBoxA(NULL, “MessageBoxA has been hooked.”, “hooked”, MB_OK); replace_IAT(“User32.dll”, “MessageBoxA”, false); MessageBoxA(NULL, “MessageBoxA has been unhooked.”, “unhooked”, MB_OK); return 0; }
运行效果图:
附远程线程注入示例代码:
#include <Windows.h> #include "tlhelp32.h" #include "stdio.h" #include "tchar.h" #define PROCESS_NAME L"iexplore.exe" #define THREAD_SIZE 4096 //传递参数的结构 typedef struct _REMOTE_PARAMETER { TCHAR m_msgContent[MAX_PATH]; TCHAR m_msgTitle[MAX_PATH]; DWORD m_dwMessageBoxAddr; } RemotePara, *PRemotePara; //获取API参数及地址 void GetMessageBoxParameter(PRemotePara pRemotePara) { HMODULE hUser32 = LoadLibrary(L"User32.dll"); pRemotePara->m_dwMessageBoxAddr = (DWORD)GetProcAddress(hUser32, "MessageBoxW"); wcscat_s(pRemotePara->m_msgContent, L"Hello salary!\0"); wcscat_s(pRemotePara->m_msgTitle, L"Hello\0"); FreeLibrary(hUser32); } //远程线程处理例程 DWORD WINAPI RemoteThreadProc(PRemotePara pRemotePara) { typedef int (WINAPI * MESSAGEBOXW)(HWND, LPCWSTR, LPCWSTR, UINT); MESSAGEBOXW MessageBoxPtr; MessageBoxPtr = (MESSAGEBOXW)pRemotePara->m_dwMessageBoxAddr; MessageBoxPtr(NULL, pRemotePara->m_msgContent, pRemotePara->m_msgTitle, MB_OK); return 0; } int main() { //提升进程权限 LUID luidTmp; HANDLE hToken; TOKEN_PRIVILEGES tkp; if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) { _tprintf(L"OpenProcessToken Failed ! \n"); return -1; } if (!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &luidTmp)) { _tprintf(L"LookupPrivilegeValue Failed ! \n"); CloseHandle(hToken); return -1; } tkp.PrivilegeCount = 1; tkp.Privileges[0].Luid = luidTmp; tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; if (!AdjustTokenPrivileges(hToken, FALSE, &tkp, sizeof(tkp), NULL, NULL)) { _tprintf(L"AdjustTokenPrivileges Failed ! \n"); CloseHandle(hToken); return -1; } //获取进程Id DWORD dwProcessId; PROCESSENTRY32 pe32; pe32.dwSize = sizeof(PROCESSENTRY32); HANDLE hProcessShot; hProcessShot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); if (hProcessShot == INVALID_HANDLE_VALUE) { return -1; } if (!Process32First(hProcessShot, &pe32)) return -1; for (int i = 0; Process32Next(hProcessShot, &pe32); i++) { if (wcscmp(pe32.szExeFile, PROCESS_NAME) == 0) { dwProcessId = pe32.th32ProcessID; break; } } CloseHandle(hProcessShot); _tprintf(L"CurrentProcessId:[%d]\n", dwProcessId); //打开进程 HANDLE hProcess; hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessId); if (hProcess == NULL) { _tprintf(L"OpenProcess failed!\n", dwProcessId); } //分配远程进程空间-函数使用 LPVOID pRemoteThread = VirtualAllocEx(hProcess, NULL,THREAD_SIZE, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); if (NULL == pRemoteThread) { _tprintf(L"VirtualAllocEx Failed!\n"); CloseHandle(hProcess); return -1; } //远程地址空间写入代码 if (FALSE == WriteProcessMemory(hProcess, pRemoteThread, &RemoteThreadProc, THREAD_SIZE, 0)) { _tprintf(L"WriteProcessMemory Failed!\n"); VirtualFreeEx(hProcess, pRemoteThread, 0, MEM_RELEASE); CloseHandle(hProcess); return -1; } //分配远程进程空间-函数的参数使用 PRemotePara pRemotePara = (PRemotePara)VirtualAllocEx(hProcess, NULL, sizeof(RemotePara), MEM_COMMIT, PAGE_READWRITE); if (NULL == pRemotePara) { _tprintf(L"VirtualAllocEx Failed!\n"); VirtualFreeEx(hProcess, pRemoteThread, 0, MEM_RELEASE); CloseHandle(hProcess); return -1; } RemotePara remotePara; ZeroMemory(&remotePara, sizeof(RemotePara)); GetMessageBoxParameter(&remotePara); //写入参数 if (FALSE == WriteProcessMemory(hProcess, pRemotePara, &remotePara, sizeof(RemotePara), 0)) { _tprintf(L"WriteProcessMemory Failed!\n"); VirtualFreeEx(hProcess, pRemoteThread, 0, MEM_RELEASE); VirtualFreeEx(hProcess, pRemotePara, 0, MEM_RELEASE); CloseHandle(hProcess); return -1; } HANDLE hThread = NULL; DWORD dwThreadId = 0; //创建远程线程 hThread = CreateRemoteThread(hProcess, NULL, 0, (DWORD(WINAPI *)(LPVOID))pRemoteThread, pRemotePara, 0, &dwThreadId); if (NULL == hThread) { _tprintf(L"CreateRemoteThread Failed!%d\n", GetLastError()); } else { _tprintf(L"Code Inject Success!\n"); } WaitForSingleObject(hThread, INFINITE); CloseHandle(hThread); VirtualFreeEx(hProcess, pRemoteThread, 0, MEM_RELEASE); VirtualFreeEx(hProcess, pRemotePara, 0, MEM_RELEASE); CloseHandle(hProcess); return 0; }
当然,这样子做36x这些安全工具是会报警的,并且目前来看IAT hook这种RING 3的技术也没什么太大价值,只能算多一种思路。