zoukankan      html  css  js  c++  java
  • 逆向project第004篇:令计算器程序显示汉字(下)

    一、前言

            钩子技术是一项很有有用价值的技术。在Windows下HOOK技术的方法比較多,使用比較灵活,常见的应用层的HOOK方法有Inline HOOK(详见《反病毒攻防研究第012篇:利用Inline HOOK实现主动防御》)、IAT HOOK、Windows钩子……HOOK技术涉及到了DLL相关的知识(详见《反病毒攻防研究第009篇:DLL注入(上)——DLL文件的编写》)。由于HOOK其它进程的时候须要訪问该进程的地址空间,使用DLL是必定的。HOOK技术也涉及到了注入的知识(详见《反病毒攻防研究第010篇:DLL注入(中)——DLL注入与卸载器的编写》),想要把完毕HOOK功能的DLL载入到目标进程空间中就要使用注入的知识了。而这次令计算器显示汉字,就正是须要使用IAT HOOK技术,通过DLL注入,来达成目的。

     

    二、IAT HOOK的基本原理

            在PE结构的IMAGE_IMPORT_DESCRIPTOR中,有两个IMAGE_THUNK_DATA结构体,第一个为导入名字表,第二个为导入地址表(IAT)。两个结构体在文件其中是没有区别的,可是当PE文件被装载内存后,第二个IMAGE_THUNK_DATA的值会被修正,该值为一个RVA,该RVA加上映像基址后,虚拟地址就保存了真正的导入函数的入口地址。

            在这个描写叙述中我们知道,假设想要对IAT进行HOOK,那么大概须要三个步骤,首先是获取要HOOK函数的地址,第二步是找到该函数所保存的IAT中的地址,最后一步是把IAT中的地址改动为HOOK函数的地址,这样就完毕了IAT HOOK。以下举例来说明一下。

            比方根据我们这次的目的,想要在计算器中显示汉字,那么根据上一篇文章的分析,我们知道,须要HOOK的模块为user32.dll中的SetWindowTextW()函数,那么首先是获得该函数的地址,第二步是找到SetWindowTextW()所保存的IAT地址,最后一步是把IAT中的SetWindowTextW()函数的地址改动为HOOK函数的地址。

     

    三、IAT HOOK的编码实现

            这里依然要建立一个简单的DLL文件,然后定义好DLL文件的主函数,并定义一个hook_iat()函数,在DLL被进程载入的时候,首先保存原始API函数的地址,便于之后的恢复,然后让DLL文件去调用hook_iat()函数;当进程被卸载的时候,利用之前保存的API函数地址,恢复原始状态。代码例如以下:

    BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
    {
            switch( fdwReason )
            {
                    // 当DLL被某进程载入时DllMain被调用
                    case DLL_PROCESS_ATTACH : 
                            // 保存原始的API函数地址
                            g_pOrgFunc = GetProcAddress(GetModuleHandleW(L"user32.dll"), 
                                                       "SetWindowTextW");
                            // 运行钩子函数
                            hook_iat("user32.dll", g_pOrgFunc, (PROC)MySetWindowTextW);
                            break;
                    // 当DLL被某进程卸载时DllMain被调用
                    case DLL_PROCESS_DETACH :
                            // 解除钩子函数
                            hook_iat("user32.dll", (PROC)MySetWindowTextW, g_pOrgFunc);
                            break;
            }
            return TRUE;
    }

            在遍历某程序的导入表时是通过文件映射来完毕的,可是当一个可运行文件已经被Windows装载器装载如内存后,就能够省去CreateFile()、CreateFileMapping()等诸多繁琐的步骤(參见《反病毒攻防研究第004篇:利用缝隙实现代码的植入》),取而代之的是通过简单的GetModuleHandle()函数就能够得到exe文件的模块映像地址,并能够非常easy地获取DLL文件导入表的虚拟地址。

            整个程序的完整代码例如以下:

    #include "stdio.h"
    #include "stdafx.h"
    #include "wchar.h"
    #include "windows.h"
    
    typedef BOOL (WINAPI *PFSETWINDOWTEXTW)(HWND hWnd, LPWSTR lpString);
    
    FARPROC g_pOrgFunc = NULL;    //用于保存原始API函数地址
    
    // SetWindowTextW的钩取函数
    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是宽字符版本号(占用两个字节)字符串
                    if( L'0' <= lpString[i] && lpString[i] <= L'9' )
                    {
                            temp[0] = lpString[i];
                            nIndex = _wtoi(temp);
                            lpString[i] = pNum[nIndex];
                    }
            }
    
            // 字符转换完毕后,再调用SetWindowTextW函数用于显示
            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)	
            // 定位PE结构
            pAddr += *((DWORD*)&pAddr[0x3C]);
            // dwRVA = RVA to IMAGE_IMPORT_DESCRIPTOR Table
            dwRVA = *((DWORD*)&pAddr[0x80]);
    
            // pImportDesc = VA to IMAGE_IMPORT_DESCRIPTOR Table
            pImportDesc = (PIMAGE_IMPORT_DESCRIPTOR)((DWORD)hMod+dwRVA);
    
            for( ; pImportDesc->Name; pImportDesc++ )
            {
                    // szLibName = VA to IMAGE_IMPORT_DESCRIPTOR.Name
                    szLibName = (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 API
                            for( ; pThunk->u1.Function; pThunk++ )
                            {
                                    if( pThunk->u1.Function == (DWORD)pfnOrg )
                                    {
                                            // 更改内存属性为PAGE_EXECUTE_READWRITE
                                            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;
    }

            以上代码已经给出了具体的凝视,代码是对PE文件的基本操作,这里不再赘述。

    四、測试程序的效果

            将上述程序编译链接成功后,会得到一个DLL文件,使用DLL载入器(详见《反病毒攻防研究第010篇:DLL注入(中)——DLL注入与卸载器的编写》),将这个DLL文件注入到一个计算器程序中,然后按下计算器中的每一个数字,例如以下图所看到的:


    图1 注入后的计算器程序

            可见我们的程序注入是成功的。

    五、小结

            利用钩子技术可以实现非常多非常有趣的功能,当然这样的技术不管是在正邪双方面都会被利用到。在此我当然希望各位读者可以善用钩子技术,毕竟假设将这样的思想运用到自己身边的软件中,就会实现非常多非常有意思的效果,丰富我们的生活。须要特别说明的是,假设程序是通过调用LoadLibrary()与GetProcAddress()函数来使用某个函数的话,上面的HOOK代码是无能为力的。要解决这样的问题,就须要对这两个函数也进行HOOK,就行达到目的了。
  • 相关阅读:
    Github注册过程以及对管理软件的了解
    进度总结
    总体心得
    学车后的领悟
    打工心得
    关于传统文化的对话实践计划书
    软件工程-课程总结
    结对编程项目---四则运算
    作业三
    目前流行的源程序版本管理软件和项目管理软件都有哪些?各有什么优缺点
  • 原文地址:https://www.cnblogs.com/blfshiye/p/4380791.html
Copyright © 2011-2022 走看看