IAT_Hook

 

说明:
利用IAT表对函数进行替换。把MessageBox()替换成自己定义的MyMessageBox(),不再出现消息框,而是输出字符或其他内容。

IAT表可以理解为一个数组。

目的:找到IMAGE_IMPORT_DESCRIPTOR结构体。

过程:
根据图片算偏移。。
1.找到IMAGE_DOS_HEADER,即文件开始的首地址。其他e_lfanew是一个偏移量。首地址+偏移量=下一结构的首地址 。

2.图看出,IMAGE_OPTIONAL_HEADER 在IMAGE_NT_HEADERS中,获取IMAGE_OPTIONAL_HEADER 。
注意:IMAGE_NT_HEADERS中有两个结构体,如果偏移IMAGE_NT_HEADERS的大小,就要再减去IMAGE_OPTIONAL_HEADER 的大小。否则会偏移多了。

3.IMAGE_IMPORT_DESCRIPTOR *img_import_desc =
(IMAGE_IMPORT_DESCRIPTOR *)((BYTE*)img_dos_header + img_optional_header->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);
注意:每次的偏移都从头部开始,并且都是按字节数来偏移,所以要强转 (BYTE*)img_dos_header。
VirtualAddress 数据的RVA,其本质就是偏移量。

4.此时可以得到IMAGE_IMPORT_DESCRIPTOR ,即IAT,首先要寻找要替换函数在哪个DLL中,通过不断搜索img_import_desc->FirstThunk,
找到USER32.dll。因为MessageBox在个dll中。
char *szImpartDesc = (char *)((BYTE *)img_dos_header + img_import_desc->Name);
(strcmp(szImpartDesc, "USER32.dll") == 0
注意:返回的函数名是ASCII。可以通过DWORD(MessageBox)取得函数名,和char *进行转换。获取代表函数的字符串。

5.
IMAGE_THUNK_DATA *img_thunk_data = (IMAGE_THUNK_DATA *)((BYTE*)hModule + img_import_desc->FirstThunk);
找到函数表,同时img_thunk_data->u1.Function可以返回函数名。
6.内存权限的修改。
上面已经找到函数所在的地址。可以img_thunk_data->u1.Function = (DWORD)MyMessageBox;
重点:此时会发现,该内存无法访问。

那么,就要修改内存的权限。为可读可写
DWORD *lpAddr = &(img_thunk_data->u1.Function);
//改变虚拟地址的读写权限
DWORD dwOldProtect;
MEMORY_BASIC_INFORMATION mbi;

VirtualQuery(lpAddr, &mbi, sizeof(mbi));
VirtualProtect(lpAddr, sizeof(DWORD), PAGE_READWRITE, &dwOldProtect);

//更换函数地址
img_thunk_data->u1.Function = (DWORD)MyMessageBox;

//权限还原
VirtualProtect(lpAddr, sizeof(DWORD), dwOldProtect, NULL);

6.函数替换
img_thunk_data->u1.Function = (DWORD)MyMessageBox;
注意点:
1.替换的函数与原函数,必须返回类型、函数参数一致。即函数名不同,其他完全相同。
2.在MyMessageBox中,不可再调用MessageBox,会造成递归。因为系统中的MessageBox已经被替换,再调用就是调用自己定义的MyMessageBox。
3.还原函数。在函数之前,可以先定义一个全局的DWORD类型的变量,进行原函数的保存。在不用的时候,进行还原。


另外:
通过函数ImageDirectoryEntryToData可以直接获取到IMAGE_IMPORT_DESCRIPTOR的首地址,不需要按字节进行计算。
IMAGE_IMPORT_DESCRIPTOR pImportDesc=
(PIMAGE_IMPORT_DESCRIPTOR)ImageDirectoryEntryToData(hModCallerModule,TRUE,IMAGE_DIRECTORY_ENTRY_IMPORT,&size);

函数原型:
PVOID WINAPI ImageDirectoryEntryToData(
_In_ PVOID Base,
_In_ BOOLEAN MappedAsImage,
_In_ USHORT DirectoryEntry,
_Out_ PULONG Size
);

 

源代码:

#include "stdafx.h"
#include <Windows.h>

int WINAPI MyMessageBox(
    _In_opt_ HWND hWnd,
    _In_opt_ LPCWSTR lpText,
    _In_opt_ LPCWSTR lpCaption,
    _In_ UINT uType)
{
//    //使用消息框,地址冲突。
//    try
//    {
//        MessageBox(NULL, L"My MessageBox!", L"Please!", MB_OK);
//    }
//    catch (...)
//    {
//        printf("Error=%d", GetLastError());
//    }
//    //输出一句话,不冲突
    //printf("Hook Sucess!");
    return 0;
}

//替换函数要与原函数返回类型、参数一致
/*int MyMessageBox()
{
    printf("Hook Sucess!");
    return 0;
}*/

bool SetMyHook()
{
    //获取DOS头
    HANDLE hModule = GetModuleHandle(NULL);
    IMAGE_DOS_HEADER *img_dos_header = (IMAGE_DOS_HEADER*)hModule;


    //从DOS头直接到OPTIONAL_HEADER。减sizeof(IMAGE_OPTIONAL_HEADER),因为此结构体包含在IMAGE_NT_HEADERS中,偏移多了。
    IMAGE_OPTIONAL_HEADER *img_optional_header = (IMAGE_OPTIONAL_HEADER *)((BYTE *)img_dos_header + img_dos_header->e_lfanew + sizeof(IMAGE_NT_HEADERS)-sizeof(IMAGE_OPTIONAL_HEADER));

    //从IMAGE_OPTIONAL_HEADER到IAT表
    IMAGE_IMPORT_DESCRIPTOR *img_import_desc = (IMAGE_IMPORT_DESCRIPTOR *)((BYTE*)img_dos_header + img_optional_header->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);

    //不断搜索IAT表的数据
    while (img_import_desc->FirstThunk)
    {
        char *szImpartDesc = (char *)((BYTE *)img_dos_header + img_import_desc->Name);
        //注意:IAT表的函数名是ASCII
        if (strcmp(szImpartDesc, "USER32.dll") == 0)
        {
            break;
        }
        img_import_desc++;
    }
    if (img_import_desc->FirstThunk)
    {
        //取真实MessageBox的地址。后面此地址替换
        DWORD dwMsgAdrr = (DWORD)MessageBox;

        //函数表
        IMAGE_THUNK_DATA *img_thunk_data = (IMAGE_THUNK_DATA *)((BYTE*)hModule + img_import_desc->FirstThunk);

        //搜索函数表,找到Messagebox的函数地址
        while (img_import_desc->FirstThunk)
        {
            if (img_thunk_data->u1.Function == dwMsgAdrr)
            {
                DWORD *lpAddr = &(img_thunk_data->u1.Function);

                //改变虚拟地址的读写权限
                DWORD    dwOldProtect;
                MEMORY_BASIC_INFORMATION mbi;

                VirtualQuery(lpAddr, &mbi, sizeof(mbi));
                VirtualProtect(lpAddr, sizeof(DWORD), PAGE_READWRITE, &dwOldProtect);
                
                //更换函数地址
                img_thunk_data->u1.Function = (DWORD)MyMessageBox;

                //权限还原
                VirtualProtect(lpAddr, sizeof(DWORD), dwOldProtect, NULL);
                return true;
            }
            img_thunk_data++;
        }
    }
    return false;
}

int _tmain(int argc, _TCHAR* argv[])
{
    //正常消息框
    MessageBox(NULL, L"MessageBox!", L"Pleadse!", MB_OK);

    bool isSucc=SetMyHook();
    if (!isSucc)
    {
        printf("Error!");
        return -1;
    }
    //hook后,应不在出现此消息框
    MessageBox(NULL, L"MessageBox!", L"Pleadse!", MB_OK);
    getchar();
    return 0;
}

 

posted @ 2017-09-25 11:58  gd_沐辰  阅读(598)  评论(0编辑  收藏  举报