可爱的HOOk技术(一)

         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 //



 

       

 

 

 

 

 

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值