ITPub博客

首页 > 应用开发 > IT综合 > APIHOOK实例剖析 (转)

APIHOOK实例剖析 (转)

原创 IT综合 作者:worldblog 时间:2007-12-12 12:47:43 0 删除 编辑
APIHOOK实例剖析 (转)[@more@]

  apiHOOK实例剖析

 关于APIHOOK的基础知识有很多,如dll的相关知识、Hook的相关知识、系统进程与线程之间的联系等。具体可以看我的另两篇文章:".NET/develop/article/15/15080.shtm">我的Dll(动态链接库)学习笔记" 和 "ASP?Id=15121">我的Hook学习笔记"。:)下面进入这篇文章的重点,根据APIHook源码进行APIHook的剖析。
 
一、APIHOOK之dll部分
 
//////////////////////////////// APIHook_Dll.cpp ////////////////////////////////////////
//  rivershan写于2002.9.23  //
/////////////////////////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "APIHook_Dll.h"

#include
#include

#pragma comment(lib,"ImageHlp") //定义全局共享数据段

#pragma data_seg("Shared")
HMODULE hmodDll=NULL;
HHOOK hHook=NULL;

#pragma data_seg()

#pragma comment(linker,"/Section:Shared,rws") //设置全局共享数据段的属性

///////////////////////////////////// DllMain 函数 /////////////////////////////////////////
//dll的入口点
BOOL APIENTRY DllMain( HMODULE hModule,
  Dword  ul_reason_for_call,
  LPVOID lpReserved
  )
{
 switch(ul_reason_for_call)
 {
 case DLL_PROCESS_ATTACH:
 //if(sHook) 
 
 case DLL_PROCESS_DETACH:
 UnInstallHook();
 break;
 }
 hmodDll=hModule;
  return TRUE;
}

///////////////////////////////////// HookOneAPI 函数 /////////////////////////////////////////
//进行IAT转换的关键函数,其参数含义:
//pszCalleeModuleName:需要hook的模块名
//pfnOriginApiAddress:要替换的自己API函数的地址
//pfnDummyFuncAddress:需要hook的模块名的地址
//hModCallerModule:我们要查找的模块名称,如果没有被赋值,
// 将会被赋值为枚举的程序所有调用的模块

void WINAPI HookOneAPI(LPCTSTR pszCalleeModuleName,PROC pfnOriginApiAddress,
  PROC pfnDummyFuncAddress,HMODULE hModCallerModule)
{
 ULONG size;

 //获取指向PE文件中的Import中IMAGE_DIRECTORY_DESCRIPTOR数组的指针

 PIMAGE_IMPORT_DESCRIPTOR pImportDesc = (PIMAGE_IMPORT_DESCRIPTOR)
 ImageDirectoryEntryToData(hModCallerModule,TRUE,IMAGE_DIRECTORY_ENTRY_IMPORT,&size);

 if (pImportDesc == NULL)
 return;

 //查找记录,看看有没有我们想要的DLL

 for (;pImportDesc->Name;pImportDesc++)
 {
 LPSTR pszDllName = (LPSTR)((PBYTE)hModCallerModule+pImportDesc->Name);
 if (lstrcmpiA(pszDllName,pszCalleeModuleName) == 0)
 break;
 }

 if (pImportDesc->Name == NULL)
 {
 return;
 }

 //寻找我们想要的函数

 PIMAGE_THUNK_DATA pThunk =
 (PIMAGE_THUNK_DATA)((PBYTE)hModCallerModule+pImportDesc->FirstThunk);//IAT
 for (;pThunk->u1.Function;pThunk++)
 {
 //ppfn记录了与IAT表项相应的函数的地址

 PROC * ppfn= (PROC *)&pThunk->u1.Function; 
 if (*ppfn == pfnOriginApiAddress)
 {
 //如果地址相同,也就是找到了我们想要的函数,进行改写,将其指向我们所定义的函数

 WriteProcessMemory(GetCurrentProcess(),ppfn,&(pfnDummyFuncAddress),
 sizeof(pfnDummyFuncAddress),NULL);
 return;
 }
 }
}

//查找所挂钩的进程所应用的dll模块的

BOOL WINAPI HookAllAPI(LPCTSTR pszCalleeModuleName,PROC pfnOriginApiAddress,
  PROC pfnDummyFuncAddress,HMODULE hModCallerModule)
{
 if (pszCalleeModuleName == NULL)
 {
 return FALSE;
 }
 if (pfnOriginApiAddress == NULL)
 {
 return FALSE;
 }
 //如果没传进来要挂钩的模块名称,枚举被挂钩进程的所有引用的模块,
 //并对这些模块进行传进来的相应函数名称的查找
 
 if (hModCallerModule == NULL)
 {
 MEMORY_BASIC_INFORMATION mInfo;
 HMODULE hModHookDLL;
 HANDLE hSnapshot;
 MODULEENTRY32 me = {sizeof(MODULEENTRY32)};
 //MODULEENTRY32:描述了一个被指定进程所应用的模块的struct

 VirtualQuery(HookOneAPI,&mInfo,sizeof(mInfo));
 hModHookDLL=(HMODULE)mInfo.AllocationBase;
 
 hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE,0);
 BOOL bOk = Module32First(hSnapshot,&me);
 while (bOk)
 {
 if (me.hModule != hModHookDLL)
 {
 hModCallerModule = me.hModule;//赋值
 //me.hModule:指向当前被挂钩进程的每一个模块
 HookOneAPI(pszCalleeModuleName,pfnOriginApiAddress,
 pfnDummyFuncAddress,hModCallerModule);
 }
 bOk = Module32Next(hSnapshot,&me);
 }
 return TRUE; 
 }
 //如果传进来了,进行查找
 else
 {
 HookOneAPI(pszCalleeModuleName,pfnOriginApiAddress,
 pfnDummyFuncAddress,hModCallerModule);
 return TRUE;
 }
 return FALSE;
}

//////////////////////////////////// UnhookAllAPIHooks 函数 /////////////////////////////////////
//通过使pfnDummyFuncAddress与pfnOriginApiAddress相等的方法,取消对IAT的修改
BOOL WINAPI UnhookAllAPIHooks(LPCTSTR pszCalleeModuleName,PROC pfnOriginApiAddress,
  PROC pfnDummyFuncAddress,HMODULE hModCallerModule)
{
 PROC temp;
 temp = pfnOriginApiAddress;
 pfnOriginApiAddress = pfnDummyFuncAddress;
 pfnDummyFuncAddress = temp;
 return HookAllAPI(pszCalleeModuleName,pfnOriginApiAddress,
 pfnDummyFuncAddress,hModCallerModule);
}

////////////////////////////////// GetMsgProc 函数 ////////////////////////////////////////
//钩子子程。与其它钩子子程不大相同,没做什么有意义的事情,继续调用下一个钩子子程,形成循环
LRESULT CALLBACK GetMsgProc(int code,WPARAM wParam,LPARAM lParam)
{
 return CallNextHookEx(hHook,code,wParam,lParam);
}

//////////////////////////////////// InstallHook 函数 /////////////////////////////////////
//安装或卸载钩子,BOOL IsHook参数是标志位
//对要钩哪个API函数进行初始化
//我们这里装的钩子类型是WH_GETMESSAGE
void __declspec(dllexport) WINAPI InstallHook(BOOL IsHook,DWORD dwThreadId)
{
 if(IsHook)
 {
 hHook=SetwindowsHookEx(WH_GETMESSAGE,(HOOKPROC)GetMsgProc,hmodDll,dwThreadId);
 
 //GetProcAddress(GetModuleHandle("GDI32.dll"),"ExtTextOutA"):取得要钩的函数在所在dll中的地址
 
 HookAllAPI("GDI32.dll",GetProcAddress(GetModuleHandle("GDI32.dll"),
 "TextOutW"),(PROC)&H_TextOutW,NULL);
 HookAllAPI("GDI32.dll",GetProcAddress(GetModuleHandle("GDI32.dll"),
 "TextOutA"),(PROC)&H_TextOutA,NULL);
 }
 else
 {
 UnInstallHook();
 UnhookAllAPIHooks("GDI32.dll",GetProcAddress(GetModuleHandle("GDI32.dll"),
 "TextOutW"),(PROC)&H_TextOutW,NULL);
 UnhookAllAPIHooks("GDI32.dll",GetProcAddress(GetModuleHandle("GDI32.dll"),
 "TextOutA"),(PROC)&H_TextOutA,NULL);
 }
}

///////////////////////////////////// UnInstallHook 函数 ////////////////////////////////////
//卸载钩子
BOOL WINAPI UnInstallHook()
{
 UnhookWindowsHookEx(hHook);
 return TRUE;
}

///////////////////////////////////// H_TextOutA 函数 /////////////////////////////////////////
//我们的替换函数,可以在里面实现我们所要做的功能
//这里我做的是显示一个对话框,指明是替换了哪个函数
BOOL WINAPI H_TextOutA(HDC hdc,int nXStart,int nYStart,LPCSTR lpString,int cbString)
{
 MessageBox(NULL,"TextOutA","APIHook_Dll ---rivershan",MB_OK);
 TextOutA(hdc,nXStart,nYStart,lpString,cbString);//返回原来的函数,以显示字符
 return TRUE;
}

///////////////////////////////////// H_TextOutW 函数 /////////////////////////////////////////
//同上
BOOL WINAPI H_TextOutW(HDC hdc,int nXStart,int nYStart,LPCWSTR lpString,int cbString)
{
 MessageBox(NULL,"TextOutW","APIHook_Dll ---rivershan",MB_OK);
 TextOutW(hdc,nXStart,nYStart,lpString,cbString);//返回原来的函数,以显示字符
 return TRUE;
}

**********************************************************************************************
**********************************************************************************************

//////////////////////////////// APIHook_Dll.h ////////////////////////////////////////
//  rivershan写于2002.9.23  //
/////////////////////////////////////////////////////////////////////////////////////////

//dll头文件,用于声明函数

void __declspec(dllexport) WINAPI InstallHook(BOOL,DWORD);
BOOL WINAPI UnInstallHook();
LRESULT CALLBACK GetMsgProC(int code,WPARAM wParam,LPARAM lParam);

void WINAPI HookOneAPI(LPCTSTR pszCalleeModuleName,PROC pfnOriginApiAddress,
  PROC pfnDummyFuncAddress,HMODULE hModCallerModule);
BOOL WINAPI HookAllAPI(LPCTSTR pszCalleeModuleName,PROC pfnOriginApiAddress,
  PROC pfnDummyFuncAddress,HMODULE hModCallerModule);
BOOL WINAPI UnhookAllAPIHooks(LPCTSTR pszCalleeModuleName,PROC pfnOriginApiAddress,
  PROC pfnDummyFuncAddress,HMODULE hModCallerModule);

BOOL WINAPI H_TextOutA(HDC, int, int, LPCSTR, int);
BOOL WINAPI H_TextOutW(HDC, int, int, LPCWSTR, int);
BOOL WINAPI H_ExtTextOutA(HDC, int, int, UINT, CONST RECT *,LPCSTR, UINT, CONST INT *);
BOOL WINAPI H_ExtTextOutW(HDC, int, int, UINT, CONST RECT *,LPCWSTR, UINT, CONST INT *);

**********************************************************************************************
**********************************************************************************************

;APIHook_Dll之def文件
LIBRARY APIHook_Dll.dll
EXPORT
 InstallHook
 
二、APIHOOK之exe部分

//////////////////////////// APIHook_EXEDlg.cpp /////////////////////////////////////////
//  rivershan写于2002.9.23  //
/////////////////////////////////////////////////////////////////////////////////////////


#include "stdafx.h"
#include "APIHook_EXE.h"
#include "APIHook_EXEDlg.h"
#include "APIHook_Dll.h"

#ifdef _DEbug
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CAPIHook_EXEDlg dialog

CAPIHook_EXEDlg::CAPIHook_EXEDlg(CWnd* pParent /*=NULL*/)
: CDialog(CAPIHook_EXEDlg::IDD, pParent)
{
 //{{AFX_DATA_INIT(CAPIHook_EXEDlg)
 // NOTE: the ClassWizard will add member initialization here
 //}}AFX_DATA_INIT
 // Note that LoadIcon does not require a subsequent DestroyIcon in win32
 m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}

void CAPIHook_EXEDlg::DoDataExchange(CDataExchange* pDX)
{
 CDialog::DoDataExchange(pDX);
 //{{AFX_DATA_MAP(CAPIHook_EXEDlg)
 // DDX_Control(pDX, IDC_EDIT1, m_Edit);
 //}}AFX_DATA_MAP
}

BEGIN_MESSAGE_MAP(CAPIHook_EXEDlg, CDialog)
//{{AFX_MSG_MAP(CAPIHook_EXEDlg)
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
 ON_BN_CLICKED(IDC_BUTTON_OUT, OnButtonOut)
 ON_BN_CLICKED(IDC_BUTTON_BEGIN, OnButtonBegin)
 ON_BN_CLICKED(IDC_BUTTON_STOP, OnButtonStop)
 //}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CAPIHook_EXEDlg message handlers

BOOL CAPIHook_EXEDlg::OnInitDialog()
{
 CDialog::OnInitDialog();
 
 // Set the icon for this dialog.  The framework does this automatically
 // when the application's main window is not a dialog
 SetIcon(m_hIcon, TRUE); // Set big icon
 SetIcon(m_hIcon, FALSE); // Set small icon
 
 // TODO: Add extra initialization here
 
 return TRUE;  // return TRUE  unless you set the focus to a control
}

// If you add a minimize button to your dialog, you will need the code below
//  to draw the icon.  For MFC applications using the document/view model,
//  this is automatically done for you by the framework.

void CAPIHook_EXEDlg::OnPaint()
{
 if (IsIconic())
 {
 CPaintDC dc(this); // device context for painting
 
 SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0);
 
 // Center icon in client rectangle
 int cxIcon = GetSystemMetrics(SM_CXICON);
 int cyIcon = GetSystemMetrics(SM_CYICON);
 CRect rect;
 GetClientRect(&rect);
 int x = (rect.Width() - cxIcon + 1) / 2;
 int y = (rect.Height() - cyIcon + 1) / 2;
 
 // Draw the icon
 dc.DrawIcon(x, y, m_hIcon);
 }
 else
 {
 CDialog::OnPaint();
 }
}

// The system calls this to obtain the cursor to display while the user drags
//  the minimized window.
HCURSOR CAPIHook_EXEDlg::OnQueryDragIcon()
{
 return (HCURSOR) m_hIcon;
}
///////////////////////////////////// OnButtonOut 函数 //////////////////////////////////////
//使用TextOut函数
void CAPIHook_EXEDlg::OnButtonOut()
{
 // TODO: Add your control notification handler code here
 HDC hdc = ::GetDC(GetSafeHwnd());
 ::TextOutA(hdc,0,0,"APIHOOK_EXE ---rivershan",30);
 UpdateWindow();
}

///////////////////////////////////// OnButtonBegin 函数 ////////////////////////////////////
//开始挂钩,这里我们挂的是自身这个APIHook_EXE这个程序
void CAPIHook_EXEDlg::OnButtonBegin()
{
 DWORD dwThreadId = GetWindowThreadProcessId(m_hWnd,NULL);//获得自身进程ID
 InstallHook(TRUE,dwThreadId);
}

///////////////////////////////////// OnButtonStop 函数 ////////////////////////////////////
//取消挂钩
void CAPIHook_EXEDlg::OnButtonStop()
{
 InstallHook(FALSE,0);
}

三、APIHOOK之集成

1. 用 VC++新建一个 Win32 Dynamic-Link Library 程序,命名为 APIHook_Dll。接下来选择第二项 A Simple DLL Project;
2. 新建一头文件,命名为 APIHook_Dll.h。删除工程中 APIHook_Dll.cpp文件中原来的内容,然后把上面的 APIHook_Dll.cpp 和 APIHook_Dll.h文件的内容全部复制到新建的这个工程的 .cpp及 .h文件中来;
3. 新建一 Text文件,命名为 APIHook_Dll.def。复制上面的def文件内容。
4. 编译;
5. 新建一 MFC APPWizard(exe)程序,命名为 APIHook_EXE。接着选择第三项,基于对话框的程序,其它默认;
6. 删除原来对话框上的控件,然后新建三个按钮ID分别为:IDC_BUTTON_BEGIN、IDC_BUTTON_STOP、IDC_BUTTON_OUT,Caption分别为:Bigin Hook、Stop Hook、Text Out。不要让这三个按钮出于对话框客户区的最上面就行;
7. 拷贝 APIHook_Dll.h文件到 APIHook_EXE程序目录下,然后加到 APIHook_EXE的头文件夹中。
8. 删除工程中 APIHook_EXEDlg.cpp文件中原来的内容,然后把上面的 APIHook_EXEDlg.cpp文件的内容全部复制到新建的这个工程的 .cpp文件中来;
9. 打开 Project->Setting菜单,选择第四项link,在 object/library moduls里添加我们的dll的lib文件的路径:..APIHook_DllDebugAPIHook_Dll.lib;
10. 编译;
11. 把 APIHook_Dll.dll文件放在 APIHook_Dll.exe程序的同一个文件夹内;
12. 运行程序,点击 Bigin Hook按钮,开始挂钩。再点击 Text Out按钮会跳出对话框并且会在程序中显示所要显示的字。点击 Stop Hook然后在点击 Text Out按钮就没有对话框出现了。

四、一些说明

1、我这个 HookAPI是使用了 Jeffrey Richter的改写程序的 IAT来实现的,也可以用跳转函数入口点的方法来实现,这个我没做研究。:)

2、我的一些心得:

 所谓 HookAPI,就是改写程序的 IAT,再调用我自己写的用于替换原API函数的函数。在我们自己写的API函数中,我们可以进行我们想要的工作。之后呢,可以把原来的函数传回去,也可以不传回去,只要你设计好了就行。

 而所谓调用自己的函数,就是把原函数参数都传给我的替换函数。我们就可以利用这些参数去干我们想做的事。而系统呢,我想由于微软设置的这个钩子的目的(我这么认为的),所以不会去检查替换函数是否就是原函数,只要参数、返回值符合条件就行,要不会出错。替换函数的返回值最好是原函数,否则有可能会出错

 HookAPI时,exe程序起到的作用就是进行Hook,把dll注入到要Hook的程序,并且传回要挂接的进程的ID或者全局钩子,以便查询所要挂接的模块的IAT。如果不注入进去,系统不会让你去查询IAT的。DLL做的事情是确定要挂接哪个函数和这个函数在哪个DLL中等。

   rivershan 原创于 2002-9-23


来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/10752043/viewspace-991967/,如需转载,请注明出处,否则将追究法律责任。

上一篇: 优先队列 (转)
请登录后发表评论 登录
全部评论
  • 博文量
    6241
  • 访问量
    2405184