• IAT Hook


    IAT hook

    一丶IAT

    1.什么是 IAT表.

    熟悉PE结构的应该知道.IAT 是导入表.
    其IAT表如下:

    typedef struct _IMAGE_IMPORT_DESCRIPTOR {
        union {
            DWORD   Characteristics;            
            DWORD   OriginalFirstThunk;       指向INT表 4个字节一组.是RVA指向名字跟序号  
        } DUMMYUNIONNAME;
        DWORD   TimeDateStamp;           
           
    
        DWORD   ForwarderChain;                 
        DWORD   Name;
        DWORD   FirstThunk;                  在文件中跟INT表一样.这是IAT                 
    } IMAGE_IMPORT_DESCRIPTOR;
    typedef IMAGE_IMPORT_DESCRIPTOR UNALIGNED *PIMAGE_IMPORT_DESCRIPTOR;
    
    

    我们知道PE有两种状态.第一种.在文件中的状态. 所以才有 VA 转 FOA等等的互相转换.
    扯多了.
    在文件状态. IAT表(firstThunk)跟 INT表一样.都是指向一个很大的表.这个表里面是4个字节进行存储.存储的是Rva. 这些RVA分别指向 导入序号以及以0结尾的字符串.

    如果在内存状态.则INT表跟上面说的文件状态一样指向 导入序号.以及导入的函数名字.
    而IAT此时不同了.IAT此时就是保存着INT指向的导入函数的地址了.

    如果还不理解.看下以前关于IAT博客.
    https://www.cnblogs.com/iBinary/p/9740757.html

    如下图所示:

    其实IAT就是保存函数地址.

    2.怎么进行HOOK

    熟悉了IAT 那么HOOK就很简单了.首先你要会解析PE.
    原理就是:
    1.编写DLL.注入到你想HOOK的程序中.
    2.编写DLL,DLL里面获取你HOOK程序的 ImageBase以及各种头(DOS,NT,FILE,OPT)
    3.DLL 里面通过OPT的数据目录第一项.得到导入表RVA.加上ImageBase定位到导入表
    4.循环遍历导入表.导入表是一行04个字节.最后一项为0
    5.通过导入表找到IAT表.继续遍历IAT表.
    6.判断IAT中的函数地址,是否是你要进行HOOK的函数地址.
    是: 则进行替换函数地址.比如替换为你的.一定要注入调用约定.
    不是: 继续循环.
    在IAT表中没找到.说明没在这个导入表中.导入表+1(一个导入表结构大小)
    继续循环 4 5 6步.
    说的比较复杂,其实原理很简单.

    首先第一步.准备一个测试程序.测试程序调用MessageBoxA.
    且加载我们的DLL(当然你编写的DLL一般是注入的别的进程中.我这里演示就直接加载自己进行HOOK自己).

    测试程序如下:

    
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <windows.h>
    
    int main()
    {
    	getchar();
    	::MessageBoxA(NULL, "没有HOOK", NULL, NULL);
    	LoadLibrary("IAThook.dll");      //加载HOOK的DLL
    	getchar();
    	
    //HOOK后进行测试的程序.
    	__asm
    	{
    		push 0
    		push 0
    		push 0
    		push 0
    		call dword ptr ds : [MessageBoxA] ; 
    	}
    
    	__asm
    	{
    		push 0
    		push 0
    		push 0
    		push 0
    		call dword ptr ds : [MessageBoxA] ;
    	}
    
    	__asm
    	{
    		push 0
    		push 0
    		push 0
    		push 0
    		call dword ptr ds : [MessageBoxA] ;
    	}
    	
    }
    

    上面的内敛汇编是因为我自己HOOK自己了可以.但是都在第一次调用MessageBox函数的时候编译器会保存这个MessageBox函数的地址.就算我HOOK完毕了.它再次调用还是调用的以前的.所以我先改成下面这样.

    HOOK的DLL

    // dllmain.cpp : 定义 DLL 应用程序的入口点。
    #include "framework.h"
    #include <windows.h>
    
    
    //创建相同函数指针.
    typedef int (WINAPI *PfnMsgA)(
    	_In_opt_ HWND hWnd,
    	_In_opt_ LPCSTR lpText,
    	_In_opt_ LPCSTR lpCaption,
    	_In_ UINT uType);
    
    PfnMsgA g_OldPfnMsgA = nullptr;
    
    
    int WINAPI MyMessageBox(_In_opt_ HWND hWnd,_In_opt_ LPCSTR lpText,_In_opt_ LPCSTR lpCaption,_In_ UINT uType)
    {
    
    	char szHookText[] = "IBinary -> Iat Hook";
    	if (g_OldPfnMsgA != nullptr)
    	{
    		return g_OldPfnMsgA(hWnd, szHookText, lpCaption, uType);//调用以前的
    	}
    	return 0;
    }
    void SetIatHook()
    {
    	MessageBoxA(NULL, "开始进行HOOK", NULL, NULL);
    	PVOID pHookAddress = nullptr;
    	pHookAddress = GetProcAddress(GetModuleHandleA("user32.dll"), "MessageBoxA"); //你要HOOK的函数.
    	if (nullptr == pHookAddress)
    	{
    		OutputDebugString(TEXT("获取函数地址失败"));
    		MessageBoxA(NULL, "获取函数地址失败HOOK", NULL, NULL);
    
    		return;
    	}
    	g_OldPfnMsgA =(PfnMsgA) pHookAddress; //保存旧的函数指针.
    	//解析PE头.寻找IAT.
    
    	HMODULE hModImageBase = GetModuleHandle(NULL);//获取当前的ImagBase
    	PIMAGE_DOS_HEADER pDosHead =(PIMAGE_DOS_HEADER)(DWORD)hModImageBase; //获取DOS头
    	DWORD dwTemp = (DWORD)pDosHead + (DWORD)pDosHead->e_lfanew;
    	PIMAGE_NT_HEADERS pNtHead = (PIMAGE_NT_HEADERS)dwTemp;
    	PIMAGE_FILE_HEADER pFileHead = (PIMAGE_FILE_HEADER)& pNtHead->FileHeader;
    	PIMAGE_OPTIONAL_HEADER pOptHead = (PIMAGE_OPTIONAL_HEADER)& pNtHead->OptionalHeader;
    
    	//寻找导出表的位置.
    	DWORD dwExportLocal = pOptHead->DataDirectory[1].VirtualAddress; //找到导出表偏移.
    	//定位到导出表
    	dwTemp = (DWORD)GetModuleHandle(NULL) + dwExportLocal;
    	PIMAGE_IMPORT_DESCRIPTOR   pImport = (PIMAGE_IMPORT_DESCRIPTOR)dwTemp;
    	PIMAGE_IMPORT_DESCRIPTOR   pCurrent = pImport;
    	DWORD *pFirstThunk; //导入表子表,也就是IAT存储函数地址的表.
    	//遍历导入表
    
    	while (pCurrent->Characteristics && pCurrent->FirstThunk != NULL)
    	{
    		dwTemp = pCurrent->FirstThunk + (DWORD)GetModuleHandle(NULL);//找到导入表
    		pFirstThunk = (DWORD *)dwTemp; //加上偏移才是真正的导入表.
    		while (*(DWORD*)pFirstThunk != NULL)
    		{
    			//遍历子表
    			if (*(DWORD*)pFirstThunk == (DWORD)g_OldPfnMsgA)
    			{
    				//找到要修改的导入表了//修改内存保护属性.写入我们新的函数地址.
    				DWORD oldProtected;
    				VirtualProtect(pFirstThunk,0x1000, PAGE_EXECUTE_READWRITE,&oldProtected);
    				dwTemp = (DWORD)MyMessageBox;
    				memcpy(pFirstThunk, (DWORD *)&dwTemp,4); //将变量中保存的函数地址拷贝到导入表中.
    				VirtualProtect(pFirstThunk,0x1000,oldProtected,&oldProtected);
    			}
    			pFirstThunk++; //继续遍历.
    		}
    		pCurrent++; //每次是加一个导入表结构.
    	}
    
    }
    
    void UnIatHook()
    {
    	/*
    	  1.遍历导入表.恢复导入表即可.
    	*/
    
    	MessageBoxA(NULL, "开始进行HOOK", NULL, NULL);
    	PVOID pHookAddress = nullptr;
    	pHookAddress = MyMessageBox;
    	if (nullptr == pHookAddress)
    	{
    		OutputDebugString(TEXT("获取函数地址失败"));
    		MessageBoxA(NULL, "恢复函数地址失败HOOK", NULL, NULL);
    		return;
    	}
    	
    	//解析PE头.寻找IAT.
    
    	HMODULE hModImageBase = GetModuleHandle(NULL);//获取当前的ImagBase
    	PIMAGE_DOS_HEADER pDosHead = (PIMAGE_DOS_HEADER)(DWORD)hModImageBase; //获取DOS头
    	DWORD dwTemp = (DWORD)pDosHead + (DWORD)pDosHead->e_lfanew;
    	PIMAGE_NT_HEADERS pNtHead = (PIMAGE_NT_HEADERS)dwTemp;
    	PIMAGE_FILE_HEADER pFileHead = (PIMAGE_FILE_HEADER)& pNtHead->FileHeader;
    	PIMAGE_OPTIONAL_HEADER pOptHead = (PIMAGE_OPTIONAL_HEADER)& pNtHead->OptionalHeader;
    
    	//寻找导出表的位置.
    	DWORD dwExportLocal = pOptHead->DataDirectory[1].VirtualAddress; //找到导出表偏移.
    	//定位到导出表
    	dwTemp = (DWORD)GetModuleHandle(NULL) + dwExportLocal;
    	PIMAGE_IMPORT_DESCRIPTOR   pImport = (PIMAGE_IMPORT_DESCRIPTOR)dwTemp;
    	PIMAGE_IMPORT_DESCRIPTOR   pCurrent = pImport;
    	DWORD* pFirstThunk; //导入表子表
    	//遍历导入表
    
    	while (pCurrent->Characteristics && pCurrent->FirstThunk != NULL)
    	{
    		dwTemp = pCurrent->FirstThunk + (DWORD)GetModuleHandle(NULL);
    		pFirstThunk = (DWORD*)dwTemp; //加上偏移才是真正的导入表.
    		while (*(DWORD*)pFirstThunk != NULL)
    		{
    			//遍历子表
    			if (*(DWORD*)pFirstThunk == (DWORD)MyMessageBox) //如果是我们的函数地址.则进行恢复.
    			{
    				//找到要修改的导入表了//修改内存保护属性.写入我们新的函数地址.
    				DWORD oldProtected;
    				VirtualProtect(pFirstThunk, 0x1000, PAGE_EXECUTE_READWRITE, &oldProtected);
    				dwTemp = (DWORD)GetProcAddress(GetModuleHandleA("user32.dll"), "MessageBoxA");
    				memcpy(pFirstThunk, (DWORD*)& dwTemp, 4); //将变量中保存的函数地址拷贝到导入表中.
    				VirtualProtect(pFirstThunk, 0x1000, oldProtected, &oldProtected);
    			}
    			pFirstThunk++; //继续遍历.
    		}
    		pCurrent++; //每次是加一个导入表结构.
    	}
    
    
    }
    BOOL APIENTRY DllMain( HMODULE hModule,
                           DWORD  ul_reason_for_call,
                           LPVOID lpReserved
                         )
    {
        switch (ul_reason_for_call)
        {
        case DLL_PROCESS_ATTACH:
    		SetIatHook();
    		break;
        case DLL_THREAD_ATTACH:
    		break;
        case DLL_THREAD_DETACH:
    
    		break;
        case DLL_PROCESS_DETACH:
            break;
        }
        return TRUE;
    }
    
    
    

    测试代码如下:

  • 相关阅读:
    控件(View)之TextView, Button, ImageButton, ImageView, CheckBox, RadioButton, AnalogClock, DigitalClock【转】
    Android 基础知识
    Intent 的用法
    Layout 和 Menu【转】
    第一讲 深入struts的配置
    第二讲 struts的拦截器
    第三讲 struts验证框架
    vscode Python 无法导入beautifulsoup4解决方案 (bs4报错:vscode unresolved import 'beautifulsoup4')
    BUUCTF RE [GUETCTF2019]re
    Typora破解复现
  • 原文地址:https://www.cnblogs.com/iBinary/p/10975839.html
Copyright © 2020-2023  润新知