《逆向工程核心原理》<04-32> 通过DLL注入实现IAT钩取的技术

来源:互联网 发布:16年广东省的经济数据 编辑:程序博客网 时间:2024/06/06 02:17

原理: 通过DLL注入实现IAT钩取的技术

(技术流程: IAT-DLL注入-CreatRemoteThread.etc)
在保持运行代码不变的前提下, 将IAT保存的API起始地址变为用户函数的起始地址

Detail:先向目标进程(calc.exe)注入用户DLL(hookiat.dll), 然后在calc.exe进程的IAT区域更改4个字节大小的地址来钩取IAT

代码:

代码(hookiat.cpp):

01- DllMain()
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 IAT钩取            //   用hookiat.MySetWindowText()钩取user32.SetWindowTextW()            //      即将user32.SetWindowTextW()的地址更改为hookiat.MySetWindowTextW()            hook_iat("user32.dll", g_pOrgFunc, (PROC)MySetWindowTextW);            break;        case DLL_PROCESS_DETACH :            // # unhook IAT"脱钩"            //   将calc.exe的IAT恢复原值            hook_iat("user32.dll", (PROC)MySetWindowTextW, g_pOrgFunc);            break;    }    return TRUE;}
02- MySetWindowTextW()
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是宽字符(2byte)字符串        if( L'0' <= lpString[i] && lpString[i] <= L'9' )        {            temp[0] = lpString[i];            nIndex = _wtoi(temp);            lpString[i] = pNum[nIndex];        }    }    // 调用user32.SetWindowTextW() API    //   (修改lpString缓冲区的内容)    return ((PFSETWINDOWTEXTW)g_pOrgFunc)(hWnd, lpString);}

注: DllMain()中只钩取or脱钩了IAT, 即只修改了地址, 而参数不发生变化.
又因参数相同, 所以MySetWindowTextW()参数lpString是原本SetWindowTextW()的字符串指针
该指针指向一块存放输出显示的字符串的缓冲区.

03- hook_iat()
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;    // 1 读取PE文件头信息, 并查找IAT的位置    // 1-1 从ImageBase开始, 经由PE签名找到IDT    // 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]);    // pAddr == ImageBase + ImageBase:[0x3C] == 01000000 + F0    // dwRVA = RVA to IMAGE_IMPORT_DESCRIPTOR Table    dwRVA = *((DWORD*)&pAddr[0x80]);    // dwRVA == pAddr:[0x80] == [01000000 + 0xF0 + 0x80] == 170    // pImportDesc = VA to IMAGE_IMPORT_DESCRIPTOR Table    pImportDesc = (PIMAGE_IMPORT_DESCRIPTOR)((DWORD)hMod+dwRVA);    // pImportDesc == ImageBase + dwRVA == (*p)(01000000 + 170) == VA to IDT    for( ; pImportDesc->Name; pImportDesc++ )    {        // 1-2 通过遍历IDT中IID, 比较IID-> Name 与 DllName 是否一致        // szLibName = VA to IMAGE_IMPORT_DESCRIPTOR.Name        szLibName = (LPCSTR)((DWORD)hMod + pImportDesc->Name);        if( !_stricmp(szLibName, szDllName) ) //若一致, 则找到所需IID的地址        {            // 1-3 通过IID-> FirstThunk, 得到IAT            // pThunk = IMAGE_IMPORT_DESCRIPTOR.FirstThunk            //        = VA to IAT(Import Address Table)            pThunk = (PIMAGE_THUNK_DATA)((DWORD)hMod +                                          pImportDesc->FirstThunk);            // pThunk->u1.Function = VA to API            for( ; pThunk->u1.Function; pThunk++ )            {                // 1-4 通过遍历IAT的函数, 比较IAT-> Function 与 pfnOrg 是否一致                if( pThunk->u1.Function == (DWORD)pfnOrg ) // 若一致, 则找到所需函数的地址                {                    // 2 得到IAT函数地址, 下面要修改(hooking)它的值                    // 更改内存属性为E/R/W                    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;}

Debug:

Debug_calc.exe:

0. 选定目标API    PEview-> IAT 查看导入的API1. OD-> 所有模块间的调用-> 搜索"SetWindowTextW" 下断2. 断下后, 查看栈 VA:[07FB40]值为07FB5C, 数据窗口跟随, 其值为"0"字符串3. f9运行, 输入任意数字, 断下后查看栈 VA:[07F7F0]值为07F80C, 其值为输入的数字字符串4. 修改字符串, 注意小端逆序5. f9运行, 框内的显示已被修改

Debug_calc.exe(hooked):

1. od-> Attach calc.exe-> 中断于新Dll-> f92. cmd-> InjectDll.exe i <PID> hookiat.dll-> od断在hookiat.dll3. 查找DllMain()    od-> 查找 当前模块中的名称-> GetProcAddress 双击跟随    GetProcAddress() && GetModuleHandleW() 函数及参数与Dllmain()的一致    VA[01F01130] mov eax,dword ptr ss:[esp+0x8] 为 DllMain()函数起始地址
DllMain():
    VA[01F01155] push hookiat.01F01000; 压入MySetWindowTextW()地址    VA[01F0115A] push EAX;              eax == SetQWindowTextW()地址    VA[01F01160] call hook.01F01090;    调用hook_iat()函数
注: 优化掉"user32.dll"字符串参数, 被硬编码到hook_iat()函数
hook_iat():
// 1-1 从ImageBase开始, 经由PE签名找到IDTVA[01F010A1] mov edi,eax; VA[01F010A3] mov eax,dword ptr ds:[edi+0x3C];     // pAddr == ImageBase + ImageBase:[0x3C] == 01000000 + F0VA[01F010A6] mov eax,dword ptr ds:[eax+edi+0x80]; // dwRVA == pAddr:[0x80] == [01000000 + 0xF0 + 0x80] == 170VA[01F010AD] add eax,edi;                         // pImportDesc == ImageBase + dwRVA == (*p)(01000000 + 170) == VA to IDT// 1-2 通过遍历IDT中IID, 比较IID-> Name 与 DllName 是否一致VA[01F010CA] call hookiat.01F075CA;               // 调用stricmp()函数// 1-3 通过IID-> FirstThunk, 得到IAT(实际是 IAT上第一个函数的地址)VA[01F010D6] mov esi,dword ptr ds:[ebx+0x4];VA[01F010D9] add esi,edi;// 1-4 通过遍历IAT的函数, 比较IAT-> Function 与 pfnOrg 是否一致VA[01F010E0] cmp dword ptr ds:[esi],ebp;// 2 得到IAT函数地址, 下面要修改(hooking)它的值VA[01F01117] mov dword ptr ds:[esi],edx;          // edx为MySetWindowTextW() esi指向SetWindowTextW()
MySetWindowTextW():
  1. OD-> 所有模块间的调用-> 搜索”SetWindowTextW” 下断 f9后输入
VA[01002628] call dword ptr ds:[<&USER32.SetWindowTextW>]; // 钩取后, user32.SetWindowTextW()的地址 变为 hookiat.MySetWindowTextW()的地址
VA[01E91063] mov cx,word ptr ds:[eax*2+0x1E9927C];//将阿拉伯数字转换为韩文数字(字符串)VA[01E9107D] dword ptr ds:[01E9B6B8];// ds:[1000B6B8]为user32.SetWindowTextW()的地址
0 0