Windows是个分层的系统,上层总是用下层提供个服务来完成自己的绝大部分操作,就以我们很常见的从磁盘上读文件为例:

当用户程序调用WindowsAPI函数ReadFile来读取文件时,改函数会调用NTDLL.DLL中的NtReadFile函数,而NTDLL.DLL中的NtReadFile仅仅是通过SSDT调用了内核中的函数,这些内核中的函数则通过HAL来直接访问硬盘,读出数据。见下图:

当我们想对API函数的结果进行自定义来隐藏某些东西,或者对某些东西表示好奇,想进行观察时,我们就必须想办法了。一个较常用的办法是我们修改某些东西,让程序在调用原API时,实际上调用的是我们自己写的函数,这样我们就有了控制权,比如我们可以调用原来的函数,然后将结果过滤后返回给原来程序!下面就来讨论一下怎么才能让对API的调用转换为对我们的函数的调用。首先,在用户模式下,没个程序都有自己的进程空间,程序也只能在他自己的空间里活动。所以,要让程序执行我们的代码,我们必须把我们的代码弄到程序的地址空间里面。这就要求我们必须把函数放在一个动态链接库里,然后把这个.dll注入到目标地址空间。这中注入一般是三种方法:使用SetWindowsHookEx函数,然后第一个参数设置为WH_MSGFILTER|WH_KEYBOARD,因为每个程序都要接受消息或者键盘输入的。第二种是使用注册表,在HKLM/SoftWare/MicroSoft/Windows NT/Current Version/AppInit_Dlls里面设置dll文件的地址,如果程序加载user32.dll,就会加载键值里列的那个dll(比如卡巴哥哥就在那个键值里写了自己的键值,然后System Repair Engineer哥哥又对卡巴哥哥表示怀疑),第三种是用CreateRemoteThread函数,用LoadLibrary函数作为线程函数,在指定进程中创建个线程,很明显 该线程是作用是载入dll。不过需要注意的是,我们传给线程函数 即LoadLibrary函数的参数,必须也在指定进程中。真巧,Windows也提供了这种技术,具体代码在后面给出(事先说一下,卡巴哥哥对这种技术意见很大)。

上面只提供了将dll加载到进程的方法,我们必须在做些工作,才能达到最终的HOOk目的,比如在dll的初始化中调用安装HOOK的函数,具体参见《Windows核心编程》。下面重点说说怎么HOOK指定函数。

当我们想对API函数的结果进行自定义来隐藏某些东西,或者对某些东西表示好奇,想进行观察时,我们就必须想办法了。一个较常用的办法是我们修改某些东西,让程序在调用原API时,实际上调用的是我们自己写的函数,这样我们就有了控制权,比如我们可以调用原来的函数,然后将结果过滤后返回给原来程序!下面就来讨论一下怎么才能让对API的调用转换为对我们的函数的调用。首先,在用户模式下,没个程序都有自己的进程空间,程序也只能在他自己的空间里活动。所以,要让程序执行我们的代码,我们必须把我们的代码弄到程序的地址空间里面。这就要求我们必须把函数放在一个动态链接库里,然后把这个.dll注入到目标地址空间。这中注入一般是三种方法:使用SetWindowsHookEx函数,然后第一个参数设置为WH_MSGFILTER|WH_KEYBOARD,因为每个程序都要接受消息或者键盘输入的。第二种是使用注册表,在HKLM/SoftWare/MicroSoft/Windows NT/Current Version/AppInit_Dlls里面设置dll文件的地址,如果程序加载user32.dll,就会加载键值里列的那个dll(比如卡巴哥哥就在那个键值里写了自己的键值,然后System Repair Engineer哥哥又对卡巴哥哥表示怀疑),第三种是用CreateRemoteThread函数,用LoadLibrary函数作为线程函数,在指定进程中创建个线程,很明显 该线程是作用是载入dll。不过需要注意的是,我们传给线程函数 即LoadLibrary函数的参数,必须也在指定进程中。真巧,Windows也提供了这种技术,具体代码在后面给出(事先说一下,卡巴哥哥对这种技术意见很大)。
        上面只提供了将dll加载到进程的方法,我们必须再做些工作,才能达到最终的HOOK目的,比如在dll的初始化中调用安装HOOK的函数,具体参见《Windows核心编程》。下面重点说说怎么HOOK指定函数。
         首先,我们需要对Windows PE文件结构有所了解(PE文件是可移植、可执行文件,我们常见的.exe和.dll文件都是PE文件),对PE的了解会对你对系统的了解有很大的帮助(具体参见《peering Inside The PE》和《An In-Depth Look into The Win32 Portable Executable File Format》这里对作者哥哥表示感谢)。当PE文件由Windows加载器加载进内存时,它在内存中被称为模块(module)。文件被映射到的内存的起始地址被称为HMODULE。这是需要记住的一点:给你一个HMODULE,你就知道在那个地址处到底有什么样的数据结构,并且你可以根据PE文件的知识找到内存中所有其它的数据结构。这个强大的功能可以被用作其它用途,例如我们在讨论的拦截API。
        PE文件一个非常好的地方就是它的数据结构在磁盘上与在内存中一样。加载一个可执行文件到内存(例如通过调用LoadLibrary函数)主要就是把PE文件中的某个部分映射到地址空间中。因此像IMAGE_NT_HEADERS这样的数据结构在磁盘上和在内存中是一样的。如果你知道如何在一个PE文件中找到某些内容,你几乎可以确定当文件被加载进内存时可以找到同样的信息。
       注意到PE文件并不是作为单一的内存映射文件被映射进内存的这一点非常重要。相反,Windows加载器查看PE文件并确定文件中的哪些部分需要被映射。当映射进内存时,文件中的高偏移相对于内存中的高地址。某项内容在磁盘文件中的偏移可能与它被加载进内存之后的偏移不同,但是将磁盘文件中的偏移转换成内存偏移需要的所有信息都存在(见下图)
 
        

我们主要关心的就是其中的“导入表”,其中描述了导入函数的名称和地址,每当程序要调用dll中的导出函数时,都会从这个表中查指定函数的地址。如果我们把这个保存后改成我们函数地址,就是传说中的API HOOK了,由于保存了原函数地址,我们可以在我们的函数中调用原函数,然后过滤其结果。
下面我们就来大致了解具体实现:
         PE文件开头是传统的MS-DOS文件头,其中前面的一部分被称为IMAGE_DOS_HEADER。此结构中最重要的两个域是e_magic和e_lfanew。e_lfanew域保存的是真正的PE文件头的偏移。e_magic域需要被设置成0x5A4D。它被定义为IMAGE_DOS_SIGNATURE。如果用ASCII码表示,0x5A4D就是“MZ”,这是Mark Zbikowski的姓名缩写,他是最初的MS-DOS设计者之一。
         IMAGE_NT_HEADERS结构是存储PE文件细节的主要位置。它的偏移由文件开头的IMAGE_DOS_HEADER结构的e_lfanew域给出。实际有两种版本的IMAGE_NT_HEADER结构,一种供32位可执行文件使用,另一种供64位使用。它们之间的差别非常小,因此我在讨论中把它们看作相同的结构。
IMAGE_NT_HEADER结构由三个域组成:
typedef struct _IMAGE_NT_HEADERS {
DWORD Signature;
IMAGE_FILE_HEADER FileHeader;
IMAGE_OPTIONAL_HEADER OptionalHeader;
} IMAGE_NT_HEADERS, *PIMAGE_NT_HEADERS;
         在一个合法的PE文件中,Signature域被设置成0x00004550。用ASCII码表示为“PE/0/0”。它被定义为IMAGE_NT_SIGNATURE。第二个域是一个类型为IMAGE_FILE_HEADER的结构,这个结构在PE文件出现之前就已经出现了。它包含了关于文件的一些基本信息。最重要的是,其中有一个域指明了跟在这个结构后面的可选文件头的大小。在PE文件中,这个可选文件头是必须的,虽然他被称为IMAGE_OPTIONAL_HEADER。我们所关心的导入表也和他有莫大的关系!(由于IMAGE_OPTIONAL_HEADER的成员较多,这里就不列举了,想研究的哥哥可以看上面说的书,或者MSDN!)在IMAGE_OPTIONAL_HEADER中,有个IMAGE_DATA_DIRECTORY结构的数组,这个数组就是可执行文件中重要位置的地址簿。其中索引为IMAGE_DIRECTORY_ENTRY_IMPORT的元素便指出了导入表的RAV(相对虚拟地址),再加上PE文件的基址,就是导入表了(呵呵,有点复杂……。没办法,为PE格式写头文件的哥哥Michael J.O’Leary 估计特别喜欢冗长的,描述性的名称,以及嵌套很深的结构和宏)。
        下面的代码描述了怎么定位导入表:
HMODULE hMod=::GetModuleHandle(NULL);//这里为示例,取得当前模块的句柄!
IMAGE_DOS_HEADER* pDosHeader=(IMAGE_DOS_HEADER*)hMod;
IMAGE_NT_HEADERS* pNtHeader=( IMAGE_NT_HEADER*)((BYTE*)hMod+pDosHeader->e_lfanew);
IMAGE_OPTIONAL_HEADER* pOptHeader=(IMAGE_OPTIONAL_HEADER*)(&pNtHeader-> OptionalHeader);
IMAGE_IMPORT_DESCRIPTOR* pImportDesc=(IMAGE_IMPORT_DESCRIPTOR*)((BYTE*)hMod+pOptHeader->/
  DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);
         这样就得到了指向导出表的指针了,其实Windows还提供有专门的函数来或得PE文件中的各个节,该函数是PVOID ImageDirectoryEntryToData(
PVOID Base;  //指向镜像的基址,记模块句柄
BOOLEN MappedAsImage;//这里为TRUE,表示系统将文件映射为镜像。
USHORT DirectoryEntry;// IMAGE_DATA_DIRECTORY数组的索引号
PULONG Size;//指向ULONG型的指针,用与返回数据大小。
)关于该函数的具体参见MSDN!
        现在,让我们在来仔细看看导入表里是啥样子吧:
typedef struct _IMAGE_IMPORT_DESCRIPTOR {
    union {
        DWORD   Characteristics;     
        DWORD   OriginalFirstThunk; 
           };
    DWORD   TimeDateStamp;    
    DWORD   ForwarderChain;  
    DWORD   Name;
    DWORD   FirstThunk;    
} IMAGE_IMPORT_DESCRIPTOR;
         在这个结构中,我们关心的是FirstThunk成员,他指出了导入地址表的RVA,导入地址表里有我们最关心的东西——导入函数的地址,就是我们要真正修改的东西!另外就是Name成员了,它是导入的DLL的名称字符串的RVA,我们会需要这个DLL的名称的!
        下面就来研究一下导入地址表的结构,这个结构里面实际上只有一个共用体其结构如下
typedef struct _IMAGE_THUNK_DATA64 {
    union {
        ULONGLONG ForwarderString;
        ULONGLONG Function;
        ULONGLONG Ordinal;
        ULONGLONG AddressOfData;
    } u1;
} IMAGE_THUNK_DATA64;
然后又将IMAGE_THUNK_DATA64定义为了IMAGE_THUNK_DATA,在程序运行时,这里面存的就是函数地址,就是我们要修改的对象。

OK,现在是时候举个小例子来说明一下下了,下面的代码HOOK了当前模块中的MessageBoxA函数,将其指向我们自己的MyMsgBox函数。

#include<windows.h>
#include<stdio.h>
//下面的宏,将相对虚拟地址转换为虚拟地址
#define RVATOVA(hMod,RVA) ((BYTE*)hMod+RVA)

BOOL SetHook(HMODULE hMod,PROC fnNewFun);//实现挂钩的函数
typedef int(WINAPI *PFNMESSAGEBOX)(HWND,LPCSTR,LPCSTR,UINT nType);//定义出MessageBoxA的类型,方便我们对原函数的调用

PROC g_orgOldFun=(PROC)MessageBoxA;//保存原函数地址
int WINAPI MyMsgBox(HWND hWnd,LPCSTR lpText,LPCSTR lpCaption,UINT uType)//我们自定义的函数,用来HOOKMessageBoxA函数
{
 return((PFNMESSAGEBOX)g_orgOldFun)(hWnd,"Our new function is called!",lpCaption,uType);//通过类型强制,调用原函数
}//这里我们只是改变了参数,然后调用原函数
void main()
{
 ::MessageBoxA(NULL,"the original function is called","Result",0);//原函数
 SetHook(::GetModuleHandle(NULL),(PROC)MyMsgBox);//设置挂钩
 ::MessageBoxA(NULL,"the original function is called","Result",0);//挂钩后,对原函数的调用已经转换为对我们函数的调用
}

BOOL SetHook(HMODULE hMod,PROC fnNewFun)//设置挂钩函数的详细实现
{
 IMAGE_DOS_HEADER* pDosHeader=(IMAGE_DOS_HEADER*)hMod;//DOS 文件头
 IMAGE_NT_HEADERS* pNtHeader=( IMAGE_NT_HEADERS*)RVATOVA(hMod,pDosHeader->e_lfanew);//NT文件头
 IMAGE_OPTIONAL_HEADER* pOptHeader=(IMAGE_OPTIONAL_HEADER*)(&pNtHeader-> OptionalHeader);//取得可选文件头
 IMAGE_IMPORT_DESCRIPTOR* pImportDesc=(IMAGE_IMPORT_DESCRIPTOR*)RVATOVA(hMod,pOptHeader->/
  DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);//取得导入表
 while(pImportDesc->FirstThunk)//该循环找出指定模块,因为MessageBoxA函数在User32.dll文件中,所以我们就查找user32.dll
 {
  char* pszDllName=(char*)((BYTE*)hMod+pImportDesc->Name);
  if(lstrcmpiA(pszDllName,"user32.dll")==0)
  {break;}
  pImportDesc++;
 }
 if(pImportDesc->FirstThunk)//该模块修改原来地址,是HOOK的具体实现部分
 {
  IMAGE_THUNK_DATA* pThunk=(IMAGE_THUNK_DATA*)((BYTE*)hMod+pImportDesc->FirstThunk);
  while(pThunk->u1.Function)
  {
   DWORD* lpAddr=(DWORD*)&(pThunk->u1.Function);
   if(*lpAddr==(DWORD)g_orgOldFun)
   {
    DWORD* lpNewProc=(DWORD*)fnNewFun;
    DWORD dwOldFlag;//下面几行代码改变导入表的内存属性,不然是写不进去的
    MEMORY_BASIC_INFORMATION mbi;
    ::VirtualQuery(lpAddr,&mbi,sizeof(mbi));
    ::VirtualProtect(lpAddr,4,PAGE_READWRITE,&dwOldFlag);
    ::WriteProcessMemory(::GetCurrentProcess(),lpAddr,&lpNewProc,4,NULL);
    ::VirtualProtect(lpAddr,4,dwOldFlag,0);//恢复原来的属性
    return TRUE;
   }
   pThunk++;
  }
 }
 return FALSE;
}

上面的代码注释的已经较为详细,相信大家都能看的差不多了。这就是传说中的HOOK技术了!下面为大家提供一个类,用来实现HOOK功能,(该类引子Windows核心编程)不过不幸的是这种用户级的HOOK现在基本没有什么杀伤力了。不过,又有幸的是我们也有内核级HOOK这种有杀伤力的技术。以后再慢慢写吧……

下面类的实现代码中,为了防止新加载的模块中有对我们要HOOK函数的调用,我们HOOK了LoadLibrary族函数。hook后的函数首先调用原来的函数加载模块,再对模块实施HOOK。代码详细如下

///APIHOOK.H

/******************************************************************************
Module:  APIHook.h
Notices: Copyright (c) 2000 Jeffrey Richter
******************************************************************************/

#pragma once

///

class CAPIHook {
public:
   // Hook a function in all modules
   CAPIHook(PSTR pszCalleeModName, PSTR pszFuncName, PROC pfnHook,
      BOOL fExcludeAPIHookMod);

// Unhook a function from all modules
   ~CAPIHook();

// Returns the original address of the hooked function
   operator PROC() { return(m_pfnOrig); }

public:
   // Calls the real GetProcAddress
   static FARPROC WINAPI GetProcAddressRaw(HMODULE hmod, PCSTR pszProcName);

private:
   static PVOID sm_pvMaxAppAddr; // Maximum private memory address
   static CAPIHook* sm_pHead;    // Address of first object
   CAPIHook* m_pNext;            // Address of next  object

PCSTR m_pszCalleeModName;     // Module containing the function (ANSI)
   PCSTR m_pszFuncName;          // Function name in callee (ANSI)
   PROC  m_pfnOrig;              // Original function address in callee
   PROC  m_pfnHook;              // Hook function address
   BOOL  m_fExcludeAPIHookMod;   // Hook module w/CAPIHook implementation?

private:
   // Replaces a symbol's address in a module's import section
   static void WINAPI ReplaceIATEntryInAllMods(PCSTR pszCalleeModName,
      PROC pfnOrig, PROC pfnHook, BOOL fExcludeAPIHookMod);

// Replaces a symbol's address in all module's import sections
   static void WINAPI ReplaceIATEntryInOneMod(PCSTR pszCalleeModName,
      PROC pfnOrig, PROC pfnHook, HMODULE hmodCaller);

private:
   // Used when a DLL is newly loaded after hooking a function
   static void    WINAPI FixupNewlyLoadedModule(HMODULE hmod, DWORD dwFlags);

// Used to trap when DLLs are newly loaded
   static HMODULE WINAPI LoadLibraryA(PCSTR  pszModulePath);
   static HMODULE WINAPI LoadLibraryW(PCWSTR pszModulePath);
   static HMODULE WINAPI LoadLibraryExA(PCSTR  pszModulePath,
      HANDLE hFile, DWORD dwFlags);
   static HMODULE WINAPI LoadLibraryExW(PCWSTR pszModulePath,
      HANDLE hFile, DWORD dwFlags);

// Returns address of replacement function if hooked function is requested
   static FARPROC WINAPI GetProcAddress(HMODULE hmod, PCSTR pszProcName);

private:
   // Instantiates hooks on these functions
   static CAPIHook sm_LoadLibraryA;
   static CAPIHook sm_LoadLibraryW;
   static CAPIHook sm_LoadLibraryExA;
   static CAPIHook sm_LoadLibraryExW;
   static CAPIHook sm_GetProcAddress;
};

End of File //
/APIHOOK.cpp///

/******************************************************************************
Module:  APIHook.cpp
Notices: Copyright (c) 2000 Jeffrey Richter
******************************************************************************/

#include "../CmnHdr.h"
#include <ImageHlp.h>
#pragma comment(lib, "ImageHlp")

#include "APIHook.h"
#include "../04-ProcessInfo/Toolhelp.h"

///

// When an application runs on Windows 98 under a debugger, the debugger
// makes the module's import section point to a stub that calls the desired
// function. To account for this, the code in this module must do some crazy
// stuff. These variables are needed to help with the crazy stuff.

// The highest private memory address (used for Windows 98 only)
PVOID CAPIHook::sm_pvMaxAppAddr = NULL;
const BYTE cPushOpCode = 0x68;   // The PUSH opcode on x86 platforms

///

// The head of the linked-list of CAPIHook objects
CAPIHook* CAPIHook::sm_pHead = NULL;

///

CAPIHook::CAPIHook(PSTR pszCalleeModName, PSTR pszFuncName, PROC pfnHook,
   BOOL fExcludeAPIHookMod) {

if (sm_pvMaxAppAddr == NULL) {
      // Functions with address above lpMaximumApplicationAddress require
      // special processing (Windows 98 only)
      SYSTEM_INFO si;
      GetSystemInfo(&si);
      sm_pvMaxAppAddr = si.lpMaximumApplicationAddress;
   }

m_pNext  = sm_pHead;    // The next node was at the head
   sm_pHead = this;        // This node is now at the head

// Save information about this hooked function
   m_pszCalleeModName   = pszCalleeModName;
   m_pszFuncName        = pszFuncName;
   m_pfnHook            = pfnHook;
   m_fExcludeAPIHookMod = fExcludeAPIHookMod;
   m_pfnOrig            = GetProcAddressRaw(
      GetModuleHandleA(pszCalleeModName), m_pszFuncName);
   chASSERT(m_pfnOrig != NULL);  // Function doesn't exist

if (m_pfnOrig > sm_pvMaxAppAddr) {
      // The address is in a shared DLL; the address needs fixing up
      PBYTE pb = (PBYTE) m_pfnOrig;
      if (pb[0] == cPushOpCode) {
         // Skip over the PUSH op code and grab the real address
         PVOID pv = * (PVOID*) &pb[1];
         m_pfnOrig = (PROC) pv;
      }
   }

// Hook this function in all currently loaded modules
   ReplaceIATEntryInAllMods(m_pszCalleeModName, m_pfnOrig, m_pfnHook,
      m_fExcludeAPIHookMod);
}

///

CAPIHook::~CAPIHook() {

// Unhook this function from all modules
   ReplaceIATEntryInAllMods(m_pszCalleeModName, m_pfnHook, m_pfnOrig,
      m_fExcludeAPIHookMod);

// Remove this object from the linked list
   CAPIHook* p = sm_pHead;
   if (p == this) {     // Removing the head node
      sm_pHead = p->m_pNext;
   } else {

BOOL fFound = FALSE;

// Walk list from head and fix pointers
      for (; !fFound && (p->m_pNext != NULL); p = p->m_pNext) {
         if (p->m_pNext == this) {
            // Make the node that points to us point to the our next node
            p->m_pNext = p->m_pNext->m_pNext;
            break;
         }
      }
      chASSERT(fFound);
   }
}

///

// NOTE: This function must NOT be inlined
FARPROC CAPIHook::GetProcAddressRaw(HMODULE hmod, PCSTR pszProcName) {

return(::GetProcAddress(hmod, pszProcName));
}

///

// Returns the HMODULE that contains the specified memory address
static HMODULE ModuleFromAddress(PVOID pv) {

MEMORY_BASIC_INFORMATION mbi;
   return((VirtualQuery(pv, &mbi, sizeof(mbi)) != 0)
      ? (HMODULE) mbi.AllocationBase : NULL);
}

///

void CAPIHook::ReplaceIATEntryInAllMods(PCSTR pszCalleeModName,
   PROC pfnCurrent, PROC pfnNew, BOOL fExcludeAPIHookMod) {

HMODULE hmodThisMod = fExcludeAPIHookMod
      ? ModuleFromAddress(ReplaceIATEntryInAllMods) : NULL;

// Get the list of modules in this process
   CToolhelp th(TH32CS_SNAPMODULE, GetCurrentProcessId());

MODULEENTRY32 me = { sizeof(me) };
   for (BOOL fOk = th.ModuleFirst(&me); fOk; fOk = th.ModuleNext(&me)) {

// NOTE: We don't hook functions in our own module
      if (me.hModule != hmodThisMod) {

// Hook this function in this module
         ReplaceIATEntryInOneMod(
            pszCalleeModName, pfnCurrent, pfnNew, me.hModule);
      }
   }
}

///

void CAPIHook::ReplaceIATEntryInOneMod(PCSTR pszCalleeModName,
   PROC pfnCurrent, PROC pfnNew, HMODULE hmodCaller) {

// Get the address of the module's import section
   ULONG ulSize;
   PIMAGE_IMPORT_DESCRIPTOR pImportDesc = (PIMAGE_IMPORT_DESCRIPTOR)
      ImageDirectoryEntryToData(hmodCaller, TRUE,
      IMAGE_DIRECTORY_ENTRY_IMPORT, &ulSize);

if (pImportDesc == NULL)
      return;  // This module has no import section

// Find the import descriptor containing references to callee's functions
   for (; pImportDesc->Name; pImportDesc++) {
      PSTR pszModName = (PSTR) ((PBYTE) hmodCaller + pImportDesc->Name);
      if (lstrcmpiA(pszModName, pszCalleeModName) == 0)
         break;   // Found
   }

if (pImportDesc->Name == 0)
      return;  // This module doesn't import any functions from this callee

// Get caller's import address table (IAT) for the callee's functions
   PIMAGE_THUNK_DATA pThunk = (PIMAGE_THUNK_DATA)
      ((PBYTE) hmodCaller + pImportDesc->FirstThunk);

// Replace current function address with new function address
   for (; pThunk->u1.Function; pThunk++) {

// Get the address of the function address
      PROC* ppfn = (PROC*) &pThunk->u1.Function;

// Is this the function we're looking for?
      BOOL fFound = (*ppfn == pfnCurrent);

if (!fFound && (*ppfn > sm_pvMaxAppAddr)) {

// If this is not the function and the address is in a shared DLL,
         // then maybe we're running under a debugger on Windows 98. In this
         // case, this address points to an instruction that may have the
         // correct address.

PBYTE pbInFunc = (PBYTE) *ppfn;
         if (pbInFunc[0] == cPushOpCode) {
            // We see the PUSH instruction, the real function address follows
            ppfn = (PROC*) &pbInFunc[1];

// Is this the function we're looking for?
            fFound = (*ppfn == pfnCurrent);
         }
      }

if (fFound) {
         // The addresses match, change the import section address
         WriteProcessMemory(GetCurrentProcess(), ppfn, &pfnNew,
            sizeof(pfnNew), NULL);
         return;  // We did it, get out
      }
   }

// If we get to here, the function is not in the caller's import section
}

///

// Hook LoadLibrary functions and GetProcAddress so that hooked functions
// are handled correctly if these functions are called.

CAPIHook CAPIHook::sm_LoadLibraryA  ("Kernel32.dll", "LoadLibraryA",  
   (PROC) CAPIHook::LoadLibraryA, TRUE);

CAPIHook CAPIHook::sm_LoadLibraryW  ("Kernel32.dll", "LoadLibraryW",  
   (PROC) CAPIHook::LoadLibraryW, TRUE);

CAPIHook CAPIHook::sm_LoadLibraryExA("Kernel32.dll", "LoadLibraryExA",
   (PROC) CAPIHook::LoadLibraryExA, TRUE);

CAPIHook CAPIHook::sm_LoadLibraryExW("Kernel32.dll", "LoadLibraryExW",
   (PROC) CAPIHook::LoadLibraryExW, TRUE);

CAPIHook CAPIHook::sm_GetProcAddress("Kernel32.dll", "GetProcAddress",
   (PROC) CAPIHook::GetProcAddress, TRUE);

///

void CAPIHook::FixupNewlyLoadedModule(HMODULE hmod, DWORD dwFlags) {

// If a new module is loaded, hook the hooked functions
   if ((hmod != NULL) && ((dwFlags & LOAD_LIBRARY_AS_DATAFILE) == 0)) {

for (CAPIHook* p = sm_pHead; p != NULL; p = p->m_pNext) {
         ReplaceIATEntryInOneMod(p->m_pszCalleeModName,
            p->m_pfnOrig, p->m_pfnHook, hmod);
      }
   }
}

///

HMODULE WINAPI CAPIHook::LoadLibraryA(PCSTR pszModulePath) {

HMODULE hmod = ::LoadLibraryA(pszModulePath);
   FixupNewlyLoadedModule(hmod, 0);
   return(hmod);
}

///

HMODULE WINAPI CAPIHook::LoadLibraryW(PCWSTR pszModulePath) {

HMODULE hmod = ::LoadLibraryW(pszModulePath);
   FixupNewlyLoadedModule(hmod, 0);
   return(hmod);
}

///

HMODULE WINAPI CAPIHook::LoadLibraryExA(PCSTR pszModulePath,
   HANDLE hFile, DWORD dwFlags) {

HMODULE hmod = ::LoadLibraryExA(pszModulePath, hFile, dwFlags);
   FixupNewlyLoadedModule(hmod, dwFlags);
   return(hmod);
}

///

HMODULE WINAPI CAPIHook::LoadLibraryExW(PCWSTR pszModulePath,
   HANDLE hFile, DWORD dwFlags) {

HMODULE hmod = ::LoadLibraryExW(pszModulePath, hFile, dwFlags);
   FixupNewlyLoadedModule(hmod, dwFlags);
   return(hmod);
}

///

FARPROC WINAPI CAPIHook::GetProcAddress(HMODULE hmod, PCSTR pszProcName) {

// Get the true address of the function
   FARPROC pfn = GetProcAddressRaw(hmod, pszProcName);

// Is it one of the functions that we want hooked?
   CAPIHook* p = sm_pHead;
   for (; (pfn != NULL) && (p != NULL); p = p->m_pNext) {

if (pfn == p->m_pfnOrig) {

// The address to return matches an address we want to hook
         // Return the hook function address instead
         pfn = p->m_pfnHook;
         break;
      }
   }

return(pfn);
}

End of File //

可爱的HOOk技术(一)相关推荐

  1. android socket_盘点Android常用Hook技术

    Android平台开发测试过程中,Hook技术是每个开发人员都常用的技术.可以用于绕过系统限制.修改别人发布的代码.动态化.调用隐藏API.插件化.组件化.自动化测试.沙箱等等. Hook如果要跨进程 ...

  2. Hook技术之4 在自己的进程中注入一个Dll到别人的进程

    与其说是一种技术,不如说是一种技术思想.它使用了前面讲的那些Hook手段,来达到自己的目的. 其应用环境是这样的,自己有一个进程在运行,这个进程是自己可以控制的,但由于业务的需要,自己又要执行另一个应 ...

  3. 【Android 插件化】Hook 插件化框架 ( Hook 技术 | 代理模式 | 静态代理 | 动态代理 )

    Android 插件化系列文章目录 [Android 插件化]插件化简介 ( 组件化与插件化 ) [Android 插件化]插件化原理 ( JVM 内存数据 | 类加载流程 ) [Android 插件 ...

  4. HOOK技术-满足我们程序的偷窥欲

    在Windows中,虚拟内存技术使一个进程内的代码访问另一个进程内的代码变得不那么容易.当然,这个不容易带来了很强的健壮性.因为即使本进程由于内存改写错误,导致程序崩溃,但另一个进程依然相当安全.   ...

  5. opengl源码 实现无缝切换图片过场_手把手讲解 Android hook技术实现一键换肤

    前言 产品大佬又提需求啦,要求app里面的图表要实现白天黑夜模式的切换,以满足不同光线下都能保证足够的图表清晰度. 怎么办?可能解决的办法很多,你可以给图表view增加一个toggle方法,参数Str ...

  6. Android Art Hook 技术方案

    Android Art Hook 技术方案 by 低端码农 at 2015.4.13 www.im-boy.net 0x1 开始 Anddroid上的ART从5.0之后变成默认的选择,可见ART的重要 ...

  7. 采用个hook技术对writefile函数进行拦截(2)

    http://www.cnblogs.com/zhxfl/archive/2011/11/03/2233846.html 这个是笔者之前写过的WriteFile HOOK代码 必须补充对这几个函数的H ...

  8. Hook技术之Hook Activity

    一.Hook技术概述 Hook技术的核心实际上是动态分析技术,动态分析是指在程序运行时对程序进行调试的技术.众所周知,Android系统的代码和回调是按照一定的顺序执行的,这里举一个简单的例子,如图所 ...

  9. 高级Linux Kernel Inline Hook技术分析与实现

    2019独角兽企业重金招聘Python工程师标准>>> ==Ph4nt0m Security Team==Issue 0x03, Phile #0x03 of 0x07|=----- ...

  10. linux 进程 inline hook,高级Linux Kernel Inline Hook技术分析与实现

    高级Linux Kernel Inline Hook技术分析与实现 时间:2015/6/28作者:网管联盟 一.简述 目前流行和成熟的kernel inline hook技术就是修改内核函数的opco ...

最新文章

  1. 寻找项目中顶级Vue对象 (一)
  2. 交流电的有效值rms值_交流电路中的电源
  3. 检测到基于堆栈的缓冲区溢出_检测到堆栈粉碎
  4. Sublime Text3常用基本操作
  5. ASP获取上月本月下月的第一天和最后一天
  6. Spring Boot+Ext JS准前后端框架应用的会话(Session)处理
  7. spark入门(1)
  8. 文件和base64编码的相互转换
  9. 接口测试面试题及参考答案(汇总),真香
  10. RS232引脚定义及串口通信中与USB等转接
  11. html 图片 气泡,微信气泡的图片铺满怎么做的?canvas做出来吗?css呢?
  12. 验房师专用验房项目验收内容
  13. python多久可以入门_python自学要多久能学会
  14. ViewOverlay 浮层
  15. android_rooting_tools 项目介绍(CVE-2012-4220)
  16. ios html格式转换,如何使用HTML模版和iOS中的UIPrintPageRenderer来生成PDF文档
  17. 亚洲前十的大数据公司都在做些什么?
  18. python起源,概念
  19. 2. 编写一个程序,判断用户输入的是正数还是负数
  20. vue查看所有的路由信息

热门文章

  1. matlab实现混沌系统最大李雅普诺夫指数
  2. 硬件设计基础--电路仿真EDA软件
  3. 联想换机助手_三星s换机助手下载-三星S换机助手 安卓版v3.6.07.11-PC6安卓网
  4. 用c语言编程点亮7个二极管,单片机c语言编程二极管_单片机c语言编程_单片机c语言编程led...
  5. 《程序设计基础课程设计》实验报告
  6. SPSS实现游程检验
  7. 使用Pycharm打包应用程序
  8. fontawesome 助手
  9. 基于SSM的企业人事管理系统
  10. 【常用办公软件有那些】万彩办公大师教程丨屏幕放大镜的使用