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