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