[dll注入实现IAT勾取] 计算器显示中文

勾取dll源码详解:

  • 首先在创建时候来保存原始IAT地址到全局变量,然后通过Hook_iat函数来进行iat函数的勾取
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.serwindowTextW();
            hook_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;
}
  • hook_iat参数1,dll名称,
  • 参数2,原始的API地址(需要被勾取的)
  • 参数3,自己写的替换API地址的函数(勾取函数)
// hook_iat
//   负责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;
    //*首先进行PE文件头信息的读取*/
    // 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 Table
    dwRVA = *((DWORD*)&pAddr[0x80]);

    // pImportDesc = VA to IMAGE_IMPORT_DESCRIPTOR Table 如果想要找到IAT首先要找到导入表对应的位置
    pImportDesc = (PIMAGE_IMPORT_DESCRIPTOR)((DWORD)hMod+dwRVA);
    /*通过循环来比较找到user32.dll中的导入表结构*/
    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 )
                {
                    // 更改内存属性为E/R/W
                    //由于计算器原有IAT内存区域是只可读的
                    //所以勾取之前通过VirtualProtect函数将相应的IAT的内存区域更改为可读写模式
                    VirtualProtect((LPVOID)&pThunk->u1.Function, 
                                   4, 
                                   PAGE_EXECUTE_READWRITE, 
                                   &dwOldProtect);

                    //修改IAT值(勾取)把原有指向user32.dll/Setwindowtext的值指向我们自己的函数MySetWindowTextW
                    pThunk->u1.Function = (DWORD)pfnNew;
                    
                    // 恢复内存属性
                    VirtualProtect((LPVOID)&pThunk->u1.Function, 
                                   4, 
                                   dwOldProtect, 
                                   &dwOldProtect);                        

                    return TRUE;
                }
            }
        }
    }

    return FALSE;
}
  • 自写的勾取函数主要功能是将阿拉伯数字转换为中文数字,具体如下:
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];
        }
    }

    // 调用user32.setwindowsTextW()api
    //  修改lpstring缓冲区内的内容
    return ((PFSETWINDOWTEXTW)g_pOrgFunc)(hWnd, lpString);
}

全dll代码:

// include
#include "stdio.h"
#include "wchar.h"
#include "windows.h"


// typedef
typedef BOOL (WINAPI *PFSETWINDOWTEXTW)(HWND hWnd, LPWSTR lpString);


// globals
FARPROC 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是宽字符的字符串
        if( L'0' <= lpString[i] && lpString[i] <= L'9' )
        {
            temp[0] = lpString[i];
            nIndex = _wtoi(temp);
            lpString[i] = pNum[nIndex];
        }
    }

    // 调用user32.setwindowsTextW()api
    //  修改lpstring缓冲区内的内容
    return ((PFSETWINDOWTEXTW)g_pOrgFunc)(hWnd, lpString);
}


// hook_iat
//   负责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;
    //*首先进行PE文件头信息的读取*/
    // 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 Table
    dwRVA = *((DWORD*)&pAddr[0x80]);

    // pImportDesc = VA to IMAGE_IMPORT_DESCRIPTOR Table 如果想要找到IAT首先要找到导入表对应的位置
    pImportDesc = (PIMAGE_IMPORT_DESCRIPTOR)((DWORD)hMod+dwRVA);
    /*通过循环来比较找到user32.dll中的导入表结构*/
    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 )
                {
                    // 更改内存属性为E/R/W
                    //由于计算器原有IAT内存区域是只可读的
                    //所以勾取之前通过VirtualProtect函数将相应的IAT的内存区域更改为可读写模式
                    VirtualProtect((LPVOID)&pThunk->u1.Function, 
                                   4, 
                                   PAGE_EXECUTE_READWRITE, 
                                   &dwOldProtect);

                    //修改IAT值(勾取)把原有指向user32.dll/Setwindowtext的值指向我们自己的函数MySetWindowTextW
                    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.serwindowTextW();
            hook_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;
}

 

然后我们通过一个DLL注入程序来注入我们的DLL

BOOL InjectDll(DWORD dwPID, LPCTSTR szDllName)
{
    HANDLE hProcess, hThread;
    LPVOID pRemoteBuf;
    DWORD dwBufSize = (DWORD)(_tcslen(szDllName) + 1) * sizeof(TCHAR);
    LPTHREAD_START_ROUTINE pThreadProc;
    //#1.使用dwPID获取目标进程句柄
    if ( !(hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPID)) )
    {
        DWORD dwErr = GetLastError();
        return FALSE;
    }
    //#2.在目标进程内存中分配注入szdllName大小的内存
    pRemoteBuf = VirtualAllocEx(hProcess, NULL, dwBufSize, MEM_COMMIT, PAGE_READWRITE);

    //#3.将dll路径写入分配的内存
    WriteProcessMemory(hProcess, pRemoteBuf, (LPVOID)szDllName, dwBufSize, NULL);
     
    //#4.获取loadLibraryW()API的地址
    pThreadProc = (LPTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandle(L"kernel32.dll"), "LoadLibraryW");

    //#5.在计算器进程中运行线程
    hThread = CreateRemoteThread(hProcess, NULL, 0, pThreadProc, pRemoteBuf, 0, NULL);
    WaitForSingleObject(hThread, INFINITE);    

    CloseHandle(hThread);
    CloseHandle(hProcess);

    return TRUE;
} 

有注入就有卸载,那我们一起来看看DLL卸载的源码解析:

hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, dwPID)

使用

CreateToolhelp32Snapshot
可以获取加载到进程的模块信息,将获取的hSnapshot 句柄传递给Module32First/Module32Next函数后,即可设置与MODLUEENTRY32结构体相关的模块信息
循环比较出需要卸载的Dll文件名称
bMore = Module32First(hSnapshot, &me);
    for( ;bMore ;bMore = Module32Next(hSnapshot, &me) )
    {
        if( !_tcsicmp(me.szModule, szDllName) || !_tcsicmp(me.szExePath, szDllName) )
        {
            bFound = TRUE;
            break;
        }
    }

获取目标进程的句柄

hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPID)

在目标进程中载入远程线程

    pThreadProc = (LPTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandle(L"kernel32.dll"), "FreeLibrary");
    hThread = CreateRemoteThread(hProcess, NULL, 0, pThreadProc, me.modBaseAddr, 0, NULL);
    WaitForSingleObject(hThread, INFINITE);    

完整DLL卸载函数如下:

BOOL EjectDll(DWORD dwPID, LPCTSTR szDllName)
{
    BOOL bMore = FALSE, bFound = FALSE;
    HANDLE hSnapshot, hProcess, hThread;
    MODULEENTRY32 me = { sizeof(me) };
    LPTHREAD_START_ROUTINE pThreadProc;
    //dwPID =进程ID
    //使用TH32CS_SNAPMODULE参数,获取加载到进程的DLL名称
    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;
}
原文地址:https://www.cnblogs.com/hanhandaren/p/11499073.html