逆向工程核心原理读书笔记-API钩取之计算器显示中文数字
来源:互联网 发布:手机制作报纸软件 编辑:程序博客网 时间:2024/06/05 21:54
我们通过一个示例来练习向计算机进程插入用户的DLL文件,钩取负责向计算器显示文本的SetWindowTextW,使得计算器中显示中文数字而不是原来的阿拉伯数字。钩取前后的原理图如下所示。
下面我们先测试一下代码。
运行calc.exe并查看PID。
在命令行窗口中输入命令与参数。
在计算器中任意输入一些数字,发现变成了中文数字。
我们来分析一下源代码,看看是怎么实现的。
InjectDll.cpp源代码与DLL注入的代码基本结构类似。
#include "stdio.h"#include "windows.h"#include "tlhelp32.h"#include "winbase.h"#include "tchar.h"void usage(){printf("\nInjectDll.exe by ReverseCore\n" "- blog : http://www.reversecore.com\n" "- email : reversecore@gmail.com\n\n" "- USAGE : InjectDll.exe <i|e> <PID> <dll_path>\n\n");}BOOL InjectDll(DWORD dwPID, LPCTSTR szDllName){HANDLE hProcess, hThread;LPVOID pRemoteBuf; DWORD dwBufSize = (DWORD)(_tcslen(szDllName) + 1) * sizeof(TCHAR);LPTHREAD_START_ROUTINE pThreadProc;if ( !(hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPID)) ) { DWORD dwErr = GetLastError();return FALSE; }pRemoteBuf = VirtualAllocEx(hProcess, NULL, dwBufSize, MEM_COMMIT, PAGE_READWRITE);WriteProcessMemory(hProcess, pRemoteBuf, (LPVOID)szDllName, dwBufSize, NULL);pThreadProc = (LPTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandle(L"kernel32.dll"), "LoadLibraryW");hThread = CreateRemoteThread(hProcess, NULL, 0, pThreadProc, pRemoteBuf, 0, NULL);WaitForSingleObject(hThread, INFINITE);CloseHandle(hThread);CloseHandle(hProcess);return TRUE;} BOOL EjectDll(DWORD dwPID, LPCTSTR szDllName){BOOL bMore = FALSE, bFound = FALSE;HANDLE hSnapshot, hProcess, hThread;MODULEENTRY32 me = { sizeof(me) };LPTHREAD_START_ROUTINE pThreadProc;if( INVALID_HANDLE_VALUE == (hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, dwPID)) )return FALSE;bMore = Module32First(hSnapshot, &me);for( ;bMore ;bMore = Module32Next(hSnapshot, &me) ){if( !_tcsicmp(me.szModule, szDllName) || !_tcsicmp(me.szExePath, szDllName) ){bFound = TRUE;break;}}if( !bFound ){CloseHandle(hSnapshot);return FALSE;}if( !(hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPID)) ){CloseHandle(hSnapshot);return FALSE;}pThreadProc = (LPTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandle(L"kernel32.dll"), "FreeLibrary");hThread = CreateRemoteThread(hProcess, NULL, 0, pThreadProc, me.modBaseAddr, 0, NULL);WaitForSingleObject(hThread, INFINITE);CloseHandle(hThread);CloseHandle(hProcess);CloseHandle(hSnapshot);return TRUE;}DWORD _EnableNTPrivilege(LPCTSTR szPrivilege, DWORD dwState){DWORD dwRtn = 0;HANDLE hToken;if (OpenProcessToken(GetCurrentProcess(),TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)){LUID luid;if (LookupPrivilegeValue(NULL, szPrivilege, &luid)){BYTE t1[sizeof(TOKEN_PRIVILEGES) + sizeof(LUID_AND_ATTRIBUTES)];BYTE t2[sizeof(TOKEN_PRIVILEGES) + sizeof(LUID_AND_ATTRIBUTES)];DWORD cbTP = sizeof(TOKEN_PRIVILEGES) + sizeof (LUID_AND_ATTRIBUTES);PTOKEN_PRIVILEGES pTP = (PTOKEN_PRIVILEGES)t1;PTOKEN_PRIVILEGES pPrevTP = (PTOKEN_PRIVILEGES)t2;pTP->PrivilegeCount = 1;pTP->Privileges[0].Luid = luid;pTP->Privileges[0].Attributes = dwState;if (AdjustTokenPrivileges(hToken, FALSE, pTP, cbTP, pPrevTP, &cbTP))dwRtn = pPrevTP->Privileges[0].Attributes;} CloseHandle(hToken);}return dwRtn;}int _tmain(int argc, TCHAR* argv[]){ if( argc != 4 ) { usage(); return 1; }// adjust privilege_EnableNTPrivilege(SE_DEBUG_NAME, SE_PRIVILEGE_ENABLED); // InjectDll.exe <i|e> <PID> <dll_path> if( !_tcsicmp(argv[1], L"i") ) InjectDll((DWORD)_tstoi(argv[2]), argv[3]); else if(!_tcsicmp(argv[1], L"e") ) EjectDll((DWORD)_tstoi(argv[2]), argv[3]);return 0;}下面详细讲解hookiat.dll的源代码(hookiat.cpp)。
DLLMain的代码非常简单。在DLL_PROCESS_ATTACH事件中先获取user32.SetWindowTextW的地址,然后保存到全局变量g_pOrgFunc中,后面脱钩会用到这个地址。
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved){switch( fdwReason ){case DLL_PROCESS_ATTACH : // 保存原始API的地址 g_pOrgFunc = GetProcAddress(GetModuleHandle(L"user32.dll"), "SetWindowTextW"); // # hook // 用hookiat.MySetWindowText钩取user32.SetWindowTextWhook_iat("user32.dll", g_pOrgFunc, (PROC)MySetWindowTextW);break;case DLL_PROCESS_DETACH : // # unhook // 将calc.exe的IAT恢复原值 hook_iat("user32.dll", (PROC)MySetWindowTextW, g_pOrgFunc);break;}return TRUE;}下面看看MySetWindowTextW函数。lpString参数是一块缓冲区,该缓冲区用来存放要输出显示的字符串。for循环将存放在lpString的阿拉伯数字字符串转换为中文数字字符串。for循环结束后,最后再调用函数指针g_pOrgFunc。
BOOL WINAPI MySetWindowTextW(HWND hWnd, LPWSTR lpString){ wchar_t* pNum = L"零一二三四五六七八九"; wchar_t temp[2] = {0,}; int i = 0, nLen = 0, nIndex = 0; nLen = wcslen(lpString); for(i = 0; i < nLen; i++) { // 将阿拉伯数字转换为中文数字 // lpString是宽字符版本(2个字节)字符串 if( L'0' <= lpString[i] && lpString[i] <= L'9' ) { temp[0] = lpString[i]; nIndex = _wtoi(temp); lpString[i] = pNum[nIndex]; } } // 调用user32.SetWindowTextW // (修改lpString缓冲区中的内容) return ((PFSETWINDOWTEXTW)g_pOrgFunc)(hWnd, lpString);}接下来分析hook_iat函数,它负责具体钩取API。
BOOL hook_iat(LPCSTR szDllName, PROC pfnOrg, PROC pfnNew){HMODULE hMod;LPCSTR szLibName;PIMAGE_IMPORT_DESCRIPTOR pImportDesc; PIMAGE_THUNK_DATA pThunk;DWORD dwOldProtect, dwRVA;PBYTE pAddr; // hMod, pAddr = ImageBase of calc.exe // = VA to MZ signature (IMAGE_DOS_HEADER)hMod = GetModuleHandle(NULL);pAddr = (PBYTE)hMod; // pAddr = VA to PE signature (IMAGE_NT_HEADERS)pAddr += *((DWORD*)&pAddr[0x3C]); // dwRVA = RVA to IMAGE_IMPORT_DESCRIPTOR TabledwRVA = *((DWORD*)&pAddr[0x80]); // pImportDesc = VA to IMAGE_IMPORT_DESCRIPTOR TablepImportDesc = (PIMAGE_IMPORT_DESCRIPTOR)((DWORD)hMod+dwRVA);for( ; pImportDesc->Name; pImportDesc++ ){ // szLibName = VA to IMAGE_IMPORT_DESCRIPTOR.NameszLibName = (LPCSTR)((DWORD)hMod + pImportDesc->Name);if( !_stricmp(szLibName, szDllName) ){ // pThunk = IMAGE_IMPORT_DESCRIPTOR.FirstThunk // = VA to IAT(Import Address Table)pThunk = (PIMAGE_THUNK_DATA)((DWORD)hMod + pImportDesc->FirstThunk); // pThunk->u1.Function = VA to APIfor( ; pThunk->u1.Function; pThunk++ ){if( pThunk->u1.Function == (DWORD)pfnOrg ){ VirtualProtect((LPVOID)&pThunk->u1.Function, 4, PAGE_EXECUTE_READWRITE, &dwOldProtect); pThunk->u1.Function = (DWORD)pfnNew; VirtualProtect((LPVOID)&pThunk->u1.Function, 4, dwOldProtect, &dwOldProtect);return TRUE;}}}}return FALSE;}首先从ImageBase开始经由PE签名找到IDT。
pImportDesc变量中存储着IMAGE_IMPORT_DESCRIPTOR结构体的起始地址,后者是calc.exe进程IDT的第一个结构体。IDT是由IMAGE_IMPORT_DESCRIPTOR结构体组成的数组。若想查找到IAT,先要查找到这个位置。使用PEView查看该地址(00012B80+01000000=01012B80),如图所示。
在for循环中通过比较查找到user32.dll的IMAGE_IMPORT_DESCRIPTOR结构体地址,从上图可以看出最终pImportDesc的值为01012BF4。接下来进入user32的IAT,pImportDesc->FirstThunk成员所指的就是IAT。使用PEView查看该地址(000010A4+01000000=010010A4),如图所示。
接下来又在for循环中查找SetWindowTextW的IAT地址(01001110),然后修改它的值。因为计算器进程的IAT内存区域是只读的,所以需要使用VirtualProtect在钩取之前将相应的区域改为可读写的,钩取之后再改回来。
完整的代码如下。
// include#include "stdio.h"#include "wchar.h"#include "windows.h"// typedeftypedef BOOL (WINAPI *PFSETWINDOWTEXTW)(HWND hWnd, LPWSTR lpString);// globalsFARPROC g_pOrgFunc = NULL;BOOL WINAPI MySetWindowTextW(HWND hWnd, LPWSTR lpString){ wchar_t* pNum = L"零一二三四五六七八九"; wchar_t temp[2] = {0,}; int i = 0, nLen = 0, nIndex = 0; nLen = wcslen(lpString); for(i = 0; i < nLen; i++) { // 将阿拉伯数字转换为中文数字 // lpString是宽字符版本(2个字节)字符串 if( L'0' <= lpString[i] && lpString[i] <= L'9' ) { temp[0] = lpString[i]; nIndex = _wtoi(temp); lpString[i] = pNum[nIndex]; } } // 调用user32.SetWindowTextW // (修改lpString缓冲区中的内容) return ((PFSETWINDOWTEXTW)g_pOrgFunc)(hWnd, lpString);}BOOL hook_iat(LPCSTR szDllName, PROC pfnOrg, PROC pfnNew){HMODULE hMod;LPCSTR szLibName;PIMAGE_IMPORT_DESCRIPTOR pImportDesc; PIMAGE_THUNK_DATA pThunk;DWORD dwOldProtect, dwRVA;PBYTE pAddr; // hMod, pAddr = ImageBase of calc.exe // = VA to MZ signature (IMAGE_DOS_HEADER)hMod = GetModuleHandle(NULL);pAddr = (PBYTE)hMod; // pAddr = VA to PE signature (IMAGE_NT_HEADERS)pAddr += *((DWORD*)&pAddr[0x3C]); // dwRVA = RVA to IMAGE_IMPORT_DESCRIPTOR TabledwRVA = *((DWORD*)&pAddr[0x80]); // pImportDesc = VA to IMAGE_IMPORT_DESCRIPTOR TablepImportDesc = (PIMAGE_IMPORT_DESCRIPTOR)((DWORD)hMod+dwRVA);for( ; pImportDesc->Name; pImportDesc++ ){ // szLibName = VA to IMAGE_IMPORT_DESCRIPTOR.NameszLibName = (LPCSTR)((DWORD)hMod + pImportDesc->Name);if( !_stricmp(szLibName, szDllName) ){ // pThunk = IMAGE_IMPORT_DESCRIPTOR.FirstThunk // = VA to IAT(Import Address Table)pThunk = (PIMAGE_THUNK_DATA)((DWORD)hMod + pImportDesc->FirstThunk); // pThunk->u1.Function = VA to APIfor( ; pThunk->u1.Function; pThunk++ ){if( pThunk->u1.Function == (DWORD)pfnOrg ){ // 更改为可读写模式VirtualProtect((LPVOID)&pThunk->u1.Function, 4, PAGE_EXECUTE_READWRITE, &dwOldProtect); // 修改IAT的值 pThunk->u1.Function = (DWORD)pfnNew; VirtualProtect((LPVOID)&pThunk->u1.Function, 4, dwOldProtect, &dwOldProtect);return TRUE;}}}}return FALSE;}BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved){switch( fdwReason ){case DLL_PROCESS_ATTACH : // 保存原始API的地址 g_pOrgFunc = GetProcAddress(GetModuleHandle(L"user32.dll"), "SetWindowTextW"); // # hook // 用hookiat.MySetWindowText钩取user32.SetWindowTextWhook_iat("user32.dll", g_pOrgFunc, (PROC)MySetWindowTextW);break;case DLL_PROCESS_DETACH : // # unhook // 将calc.exe的IAT恢复原值 hook_iat("user32.dll", (PROC)MySetWindowTextW, g_pOrgFunc);break;}return TRUE;}
0 0
- 逆向工程核心原理读书笔记-API钩取之计算器显示中文数字
- 逆向工程核心原理读书笔记-API钩取之计算器显示中文数字
- 逆向工程核心原理读书笔记-API钩取之记事本小写转大写
- 逆向工程核心原理读书笔记-API钩取之隐藏进程(一)
- 逆向工程核心原理读书笔记-API钩取之隐藏进程(二)
- 逆向工程核心原理读书笔记-API钩取之IE浏览器连接控制
- 逆向工程核心原理读书笔记-API钩取之记事本小写转大写
- 逆向工程核心原理读书笔记-API钩取之隐藏进程(一)
- 逆向工程核心原理读书笔记-API钩取之隐藏进程(二)
- 逆向工程核心原理读书笔记-API钩取之IE浏览器连接控制
- 《逆向工程核心原理》<04-30> 通过Debug修改代码实现API钩取的技术
- 《逆向工程核心原理》<04-34> 高级全局API钩取
- 《逆向工程核心原理》<04-33> 通过DLL注入修改API代码实现API钩取的技术
- 逆向工程核心原理读书笔记-代码注入
- 逆向工程核心原理读书笔记-代码注入
- 《逆向工程核心原理》
- 《逆向工程核心原理》
- 逆向工程核心原理
- 【USACO1.3】牛式 Prime Cryptarithm
- Codeforces-Round 394#C-Dasha and Password'
- git操作远程仓库(三)---团队合作
- 源码编译方式安装Asterisk(一)
- 幻灯片功能(moveElement函数)
- 逆向工程核心原理读书笔记-API钩取之计算器显示中文数字
- python 带分页
- 2017新开始
- 历届试题 矩阵翻硬币 (大整数)
- 根据指定的id显示相应的<section>,同事隐藏其他部分(showSection函数)
- POJ 1837 Balance(天平问题 01背包变形)
- 不定宽高的弹出框自适应居中
- .NET Framework - C# 7.0 中的新增功能
- nosql--简介