Hook-通过修改IAT实现

selph
selph
发布于 2020-10-16 / 913 阅读
0
0

Hook-通过修改IAT实现

原理介绍

上一节学习了通过调试API进行Hook,调试API 改变的是某一个点的时候的线程上下文相关内容,比如函数参数,缓冲区,函数返回地址等

这一节讲手动修改PE结构中的IAT来进行 Hook,通过修改IAT进行Hook实际上是修改进程的IAT地址,然后让目标函数执行的时候,执行我们自己的函数

因为要操作目标进程的内容,所以注入一个dll会比较方便,注入的dll会有目标进程的全部权限,而且也能很好的把Hook函数放到目标进程中去,当然,代码注入也是可以,就是比较麻烦,这里用dll注入来实现

这里还是以64位的记事本为例,目标是Hook WriteFile API,来让每次执行的时候弹个框(因为只是练习Hook,所以不用太复杂的功能)

首先我们要先了解一下64位下的IAT与32位有什么区别

遍历64位IAT函数名称和地址

在定位64位IAT的时候,遇到了点坑,那就是忽视了32位和64位区别所导致的,后来我专门来遍历一下64位进程的IAT了解

首先要了解一个API:GetModuleHandle,参数只有1个,是目标模块名,这里填NULL表示当前进程,返回的是一个模块句柄,也就是当前进程的首地址

以此为基础,我们就可以开始定位IAT了,下面源代码是定位64位当前进程IAT函数名称和地址(具体关于IAT相关理论在我之前的笔记里有详细分析,这里不啰嗦了)

要注意的区别

  • 64位程序的基地址是Unsigned Long Long类型,用DWORD64表示就行

  • 运行中的程序,IAT里记录的是VA而不是RVA

  • 运行中的程序,INT里记录的是RVA而不是VA

源代码:

#include<stdio.h>
#include<Windows.h>

int main() {
	//获取当前exe的首地址
	HANDLE hMod = GetModuleHandle(NULL);//可以访问到当前线程在内存中的映像首地址,也就是基地址
	
	//获取当前exe的NT结构
	PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)hMod;
	PIMAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)((DWORD64)hMod + pDos->e_lfanew);
	
	//获取当前exe的数据目录表导入表信息
	PIMAGE_OPTIONAL_HEADER pOptional = &pNt->OptionalHeader;
	PIMAGE_DATA_DIRECTORY pDir = &pOptional->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
	PIMAGE_IMPORT_DESCRIPTOR pImpDes = PIMAGE_IMPORT_DESCRIPTOR(pDir->VirtualAddress + (DWORD64)hMod);

	//遍历导入表
	for (; pImpDes->Name; pImpDes++) {
		DWORD64 dwDllName = pImpDes->Name + (DWORD64)hMod;
		printf("DLL名称:%s\r\n", dwDllName);
		
		PIMAGE_THUNK_DATA pThunk = (PIMAGE_THUNK_DATA)(pImpDes->FirstThunk + (DWORD64)hMod);//导入地址表
		PIMAGE_THUNK_DATA pINT = (PIMAGE_THUNK_DATA)(pImpDes->OriginalFirstThunk + (DWORD64)hMod);//INT
		if (pThunk->u1.Ordinal != 0) {
			for (; pThunk->u1.Function; pThunk++,pINT++) {
				PIMAGE_IMPORT_BY_NAME dwFunName = (PIMAGE_IMPORT_BY_NAME)(pINT->u1.AddressOfData + (DWORD64)hMod);
				DWORD64* dwFunAddr = &(pThunk->u1.Function);
				printf("函数名称:%-30s", dwFunName->Name);
				printf("函数地址:%p\r\n", *dwFunAddr);

			}
		}
		printf("\r\n------------------\r\n\r\n");

	}
	CloseHandle(hProcess);
	return 0;
}

效果演示

image-20201015111032683

通过修改IAT进行Hook

这里还是以64位的记事本为例,目标是Hook WriteFile API,来让每次执行的时候弹个框(因为只是练习Hook,所以不用太复杂的功能)

这里的难点在于如何判断自己是否成功定位到了目标的IAT,关于修改IAT只需要改一下访问权限直接修改即可

这里对遍历IAT不熟悉的话,最好去写个64位程序来遍历自身的IAT来练习练习

其他想说的,都在代码里:

dllmain.dll

#include<windows.h>
#include<stdio.h>

SIZE_T g_pOrgFuncAddr = 0;
BOOL IAT_Hook(LPCSTR DLLname, PROC szOrgFuncAddr, PROC szMyFuncAddr);

//我们自己的Hook函数
BOOL WINAPI MyWriteFile(
    _In_ HANDLE hFile,
    _In_reads_bytes_opt_(nNumberOfBytesToWrite) LPCVOID lpBuffer,
    _In_ DWORD nNumberOfBytesToWrite,
    _Out_opt_ LPDWORD lpNumberOfBytesWritten,
    _Inout_opt_ LPOVERLAPPED lpOverlapped
) {
    MessageBox(NULL,L"警告",L"ERROR",MB_OK);
    return WriteFile(hFile,lpBuffer,nNumberOfBytesToWrite,lpNumberOfBytesWritten,lpOverlapped);
}

BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
        //保存原始API地址
        g_pOrgFuncAddr = (SIZE_T)GetProcAddress(GetModuleHandleA("kernel32.dll"),"WriteFile");//WriteFile在kernel32.dll里!!
        IAT_Hook("kernel32.dll",(PROC)g_pOrgFuncAddr,(PROC)MyWriteFile);
        break;
    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
        IAT_Hook("kernel32.dll", (PROC)MyWriteFile, (PROC)g_pOrgFuncAddr);
        break;
    }
    return TRUE;
}



BOOL IAT_Hook(LPCSTR DLLname, PROC szOrgFuncAddr, PROC szMyFuncAddr) {
    HMODULE hModule = GetModuleHandle(NULL);//NULL表示返回进程本身的首地址

    //定位到NT头
    PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)hModule;
    PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)((SIZE_T)hModule + pDosHeader->e_lfanew);
    
    //定位到映像地址和导入地址表
    DWORD64 szImageBase = pNtHeader->OptionalHeader.ImageBase;
    DWORD64 szImpRva = (DWORD64)pNtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;
    
    //导入表VA
    PIMAGE_IMPORT_DESCRIPTOR pImpDes = (PIMAGE_IMPORT_DESCRIPTOR)(szImageBase + szImpRva);
    
    //查找欲HOOK的dll
    while (pImpDes->Name) {
        DWORD64 szNameAddr = szImageBase + pImpDes->Name;//获取dll名称VA

        char szName[MAXBYTE] = { 0 };
        strcpy_s(szName, (char*)szNameAddr);//提取字符串
        //MessageBoxA(0, szName, 0, 0);
	
        //判断dll是否是我们要Hook的dll
        if (strcmp(_strlwr(szName), DLLname) == 0) {
            //此时选中的就是指定的dll
            //遍历指定函数
            PIMAGE_THUNK_DATA pThunk = (PIMAGE_THUNK_DATA)(pImpDes->FirstThunk + (DWORD64)hModule);
            while (pThunk->u1.Function) {
                if (pThunk->u1.Function == (DWORD64)szOrgFuncAddr) {
                    //修改访问权限
                    DWORD dwOldProtect;
                    VirtualProtectEx(GetCurrentProcess(),(LPVOID)&pThunk->u1.Function, 8, PAGE_EXECUTE_READWRITE, &dwOldProtect);//&dwOldProtect这个参数必须有,不然就会失败!!
                    //修改IAT
                    pThunk->u1.Function = (DWORD64)szMyFuncAddr;
                    break;
                }
                pThunk++;
            }
            break;
        }
        pImpDes++;
    }
    if (pImpDes->Name == NULL){
        return FALSE;
    }
    return TRUE;
}

IATHook.cpp

这里就是简单的远线程dll注入:

#include<stdio.h>
#include<windows.h>
//这里写个dll注入,来加载Hook DLL


BOOL DllInject(LPCSTR WndClassName,LPCSTR szDllPath) {
	//获取进程句柄
	HWND hWnd = FindWindowA(WndClassName,NULL);
	DWORD dwPid = 0;
	GetWindowThreadProcessId(hWnd, &dwPid);
	HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS,FALSE,dwPid);



	//获取LoadLibraryA地址
	LPVOID pFunAddr = GetProcAddress(LoadLibraryA("kernel32.dll"), "LoadLibraryA");

	//向目标写入dll全路径
	SIZE_T sSize = strlen(szDllPath);
	LPVOID pDllPath = VirtualAllocEx(hProcess,NULL,sSize,MEM_COMMIT,PAGE_READWRITE);
	WriteProcessMemory(hProcess,pDllPath,szDllPath,sSize,NULL);
	
	//创建远程线程进行注入
	HANDLE hThread = CreateRemoteThread(hProcess,NULL,NULL,(LPTHREAD_START_ROUTINE)pFunAddr,pDllPath,0,NULL);
	WaitForSingleObject(hThread, INFINITE);
	CloseHandle(hThread);
	CloseHandle(hProcess);

	return TRUE;
}

int main() {
	DllInject("notepad", "C:\\Users\\helloworld\\source\\ReversecoreBook\\IATHook\\x64\\Debug\\IATHookdll.dll");

	return 0;
}

效果演示

image-20201015110132043

总结

这个练习,我之前做过一次IAT Hook一个32位的记事本的,当时很快就做出来了,这次花了很长很长时间,再此做个自我反思:这次我存在的问题是:遇到问题的时候,没能冷静下来逐步分析反而在那不停的调试,下次要想办法提醒自己先静下来!

这次花时间长主要是因为注入的DLL,我不好确认问题出在哪里,对64位IAT不熟悉就是应该先动手练一次再去写DLL!

然后就是对使用的API,比如那个VirtualProtectEx不熟悉,最后一个参数不能写NULL!

还有一个问题就是,我要找WriteFile这个API,我从user32.dll里找了半天,其实是在kernel32.dll中,太不冷静了唉

冷静很重要,清醒的头脑更具有创造力


评论