22.6 API拦截的一个例子

22.6.1 通过覆盖代码来拦截API

(1)实现过程

  ①在内存中对要拦截的函数(假设是Kernel32.dll中的ExitProcess)进行定位,从而得到它的内存地址。

  ②把这个函数的起始的几个字节保存在我们自己的内存中。

  ③用CPU的一条JUMP指令来覆盖这个函数起始的几个字节,这条JUMP指令用来跳转到我们替代函数的内存地址。当然,我们的替代函数的函数签名必须与要拦截的函数的函数签名完全相同,即所有的参数必须相同,返回值必须相同,调用约定也必须相同。

  ④现在,当线程调用被拦截函数时,跳转指令实际上跳转到我们的替代函数。这时,就可以执行我们想要执行的任何代码。

  ⑤为了撤销对函数的拦截,我们必须把第2步中保存下来的字节放回被拦截函数起始的几个字节中。

  ⑥我们调用被拦截函数(现在己经不再对它进行拦截了),让该函数执行它正常处理。

  ⑦当原来的函数返回时,我们再次执行第2步和第3步,这样的替代函数将来还会被调用到。

(2)主要缺点

  ①因CPU的指令在x86、x64、IA-64等上各不相同,所以该方法严重依赖CPU。

  ②而且在抢占式、多线程环境下根本不能工作,因为一个线程覆盖另一个函数起始位置的代码需要一定时间,在这个过程中,另一个线程可能试图调用同一个函数,其结构可能是灾难性的!)

22.6.2 通过修改模块的导入段来拦截API

(1)要解决上述两个不足,可以用另一种拦截API的方法,即通过修改模块的导入段来拦截API,这种方法不仅容易,而且也相当健壮。

(2)为了拦截特定的函数,我们需要修改模块导入段的IAT表,让线程调用该导入函数时,重新跳转到我们指定的函数地址去。(利用PEView工具查看)

(3)内存中的IAT表:——修改其内容的自定义函数:ReplaceIATEntryInOneMod函数

  ①举例:拦截MyAppExe.exe模块中对ExitProcess函数的调用

PROC pfnOrig = GetProcAddress(GetModuleHandle("Kernel32"),"ExitProcess");

HModule HmodCaller = GetModuleHandle("MyAppExe.exe");

ReplaceIATEntryInOneMod("Kernel32.dll",pfnOrig,MyExitProcess,hmodCaller);

  ②ReplaceIATEntryInOneMod只能修改一个模块的指定API。如果同一个地址空间中有另一个DLL也调用了相同的API(如多个DLL都调用了ExitProcess函数),则需要用ReplaceIATEntryInAllMods函数。

  ③当调用Replace*AllMods后如果调用LoadLibrary载入一个新的DLL时,新载入的DLL仍有可能会调用ExitProcess,所以我们必须同时拦截LoadLibrary*函数,并为新载入的DLL调用Replace*InOnMod,但考虑到这个DLL又依赖其他静态链接的DLL时,而他们也可能调用ExitProcess,这时当调用LoadLibrary*的同时,将没有机会更新其他这些Dll的IAT表,所以简单的一种方案就是载入一个新DLL时,调用Replace*InAllMods来替代Replace*InOneMod。

  ④如果是调用GetProcAddress函数来获取ExitProcess的地址时仍然会成功,所以还必须拦截GetProcAddress函数。

(4)内存中的导出表(EAT)—修改其内容的自定义函数:ReplaceEATEntryInOneMod函数

【LastMessageBox info】示例程序——用于拦截别的进程对MessageBox的调用

(1)利用Windows钩子技术来注入DLL

(2)必须同时拦截MessageBoxA和MessageBoxW两个函数(位于User32.dll)

(3)CAPIHook类的构造函数里记录哪些API被拦截下来,析构里会重置为原来API的地址。

(4)当CAPIHook对象被构造时,要拦截的函数所在的模块必须己经被载入。而延时载入模块会直到函数被调用该模块才会被载入,所以CAPIHook类无法处理延迟载入的模块。

//动态链接库端的文件

//APIHook.h

/************************************************************************
Module: APIHook.h
Notices: Copyright(c) 2008 Jeffrey Richter & Christophe Nasarre
************************************************************************/ #pragma once
#include <windows.h>
//////////////////////////////////////////////////////////////////////////
class CAPIHook{
public:
//拦截将被注入的进程所有模块中指定的API
CAPIHook(PSTR pszCalleeModName, //被拦截函数所在的模块(DLL)
PSTR pszFuncName, //要被拦截函数的名称
PROC pfnHook); //拦截函数(也叫替代函数)地址 //从所有模块中卸载一个函数(即拦截函数)
~CAPIHook(); //返回最初的、被拦截函数的地址
operator PROC(){ return (m_pfnOrig); } //是否连当前调用CAPIHook的模块(即该DLL本身)也要拦截?
//这个变量在ReplaceIATEntryInAllMods中会使用到,这里被声明为static
static BOOL ExcludeAPIHookMod; public:
//调用真正的GetProcAddress
static FARPROC WINAPI GetProcAddressRaw(HMODULE hmod, PCSTR pszProcName); private:
static CAPIHook* sm_pHead; //链表中,第一个被拦截函数
CAPIHook* m_pNext; //下一个被拦截函数 PCSTR m_pszCalleeModName; //含被拦截函数的模块名称(ANSI)
PCSTR m_pszFuncName; //被拦截的函数名称(ANSI)
PROC m_pfnOrig; //在被调用者中原始的函数,也就是被拦截的函数
PROC m_pfnHook; //拦截函数——用来替代m_pfnOrig private:
//在一个模块的导入表中替换指定符号(函数)的地址
//即hmodCaller模块导入了pszCalleeModeName模块里的pfnOrg函数。现在要将hmodCaller导入表中的
//pfnOrg函数替换为pfnHook函数。
static void WINAPI ReplaceIATEntryInOneMod(PCSTR pszCalleeModName, //被调用模块名称
PROC pfnOrig, //被拦截函数地址,pszCalleeModeName模块中
PROC pfnHook, //拦截函数(替换函数)地址
HMODULE hmodCaller); //调用模块,即要修改导入段的模块 //替换当前进程中,所有模块的导入表中指定符号地址为拦截函数的地址
static void WINAPI ReplaceIATEntryInAllMods(PCSTR pszCalleeModName, PROC pfnOrig, PROC pfnHook); //在一个模块的导出表中替换指定符号(函数)的地址
static void WINAPI ReplaceEATEntryInOneMods(HMODULE hmod, PCSTR pszFunctionName, PROC pfnNew); private:
//当DLL是在被拦截以后加载进来时,使用下面这个函数
static void WINAPI FixupNewlyLoadedModuled(HMODULE hmod, DWORD dwFlags); //当Dlls后在被加载进行时使用
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); //如果拦截函数要需面,返回被替换函数的真实地址(被保存在链表里)
static FARPROC WINAPI GetProcAddress(HMODULE hmod, PCSTR pszProcName); private:
//实例化下面这些函数
static CAPIHook sm_LoadLibraryA;
static CAPIHook sm_LoadLibrarayW;
static CAPIHook sm_LoadLibraryExA;
static CAPIHook sm_LoadLibrarayExW;
static CAPIHook sm_GetProcAddress;
};

//APIHook.cpp

/************************************************************************
Module: APIHook.cpp
Notices: Copyright(c) 2008 Jeffrey Richter & Christophe Nasarre
************************************************************************/ #include "../../CommonFiles/CmnHdr.h"
#include <ImageHlp.h>
#pragma comment(lib, "ImageHlp") #include "APIHook.h"
#include "../../CommonFiles/ToolHelp.h"
#include <strsafe.h> ////////////////////////////////////////////////////////////////////////// //CAPIHook中保存函数对象的链表头部
CAPIHook* CAPIHook::sm_pHead = NULL; //默认调用CAPHook()的模块不被拦截
BOOL CAPIHook::ExcludeAPIHookMod = TRUE; //////////////////////////////////////////////////////////////////////////
CAPIHook::CAPIHook(PSTR pszCalleeModName, PSTR pszFuncName, PROC pfnHook){
//注意:只有要拦截的模块被加载时,函数才能被拦截。一个解决方案是,把函数名称
// 保存起来。然后在LoadLibrary*函数的拦截处理器中分析CAPIHook各实例
// 检查pszCalleeModName模块是否是要被拦截的模块 m_pNext = sm_pHead; //下一个节点
sm_pHead = this; //因为每拦截一个函数都要创建一个CAPIHook的实例 //将被拦截函数信息保存起来
m_pszCalleeModName = pszCalleeModName;
m_pszFuncName = pszFuncName;
m_pfnHook = pfnHook;
HMODULE hmod = GetModuleHandleA(pszCalleeModName);
m_pfnOrig = GetProcAddressRaw(hmod, m_pszFuncName); //如果函数不存在,则退出,这种情况发生在模块未被加载进来
if (m_pfnOrig == NULL){
wchar_t szPathname[MAX_PATH];
GetModuleFileNameW(NULL, szPathname, _countof(szPathname));
wchar_t sz[];
StringCchPrintfW(sz, _countof(sz),
TEXT("[%4u - %s] 找不到 %S\r\n"),
GetCurrentProcessId(),szPathname,pszFuncName);
OutputDebugString(sz);
return;
} //拦截当前被载入的所有模块中指定的函数
ReplaceIATEntryInAllMods(m_pszCalleeModName, m_pfnOrig, m_pfnHook);
} //////////////////////////////////////////////////////////////////////////
CAPIHook::~CAPIHook(){
//取消拦截
ReplaceIATEntryInAllMods(m_pszCalleeModName, m_pfnHook, m_pfnOrig); //删除链表
CAPIHook* p = sm_pHead;
if (p == this){ //删除头结点
sm_pHead = p->m_pNext;
} else{
BOOL bFound = FALSE; //遍历链表,并删除当前结点
for (; !bFound && (p->m_pNext != NULL);p = p->m_pNext){
if (p->m_pNext == this){
p->m_pNext = p->m_pNext->m_pNext;
bFound = TRUE;
}
}
}
} //////////////////////////////////////////////////////////////////////////
//返回包含指定内存地址的模块句柄
static HMODULE ModuleFromAddress(PVOID pv){
MEMORY_BASIC_INFORMATION mbi;
return ((VirtualQuery(pv, &mbi, sizeof(mbi)) != )
? (HMODULE)mbi.AllocationBase : NULL);
} //////////////////////////////////////////////////////////////////////////
//如果模块没被加载,处理一些非预期的异常
LONG WINAPI InvalidReadExceptionFilter(PEXCEPTION_POINTERS pep){
//处理所有非预期的异常,因为在这种情况下,我们根本不修补任何模块
LONG lDisposition = EXCEPTION_EXECUTE_HANDLER; //表示可以进入__except模块处理了 //注意:pep->ExceptionRecord->ExceptionCode的值可能为0xC0000005
return (lDisposition);
}
//////////////////////////////////////////////////////////////////////////
void CAPIHook::ReplaceIATEntryInOneMod(PCSTR pszCalleeModName,
PROC pfnCurrent, PROC pfnNew, HMODULE hmodCaller){
//获得hmodCaller模块的导入表
ULONG ulSize; //当传入ImageDirectorEntryToData的是一个一个无效的模块句柄时,从而会
//产生一个0xC0000005的异常。如Explorer进程(Windows资源管理器)的另一
//个线程快速动态载入和卸装DLL,这时可能导致该函数异常。我们需要捕获异
//常来保护代码。
PIMAGE_IMPORT_DESCRIPTOR pImportDesc = NULL;
__try{
pImportDesc = (PIMAGE_IMPORT_DESCRIPTOR)ImageDirectoryEntryToData(
hmodCaller,TRUE,IMAGE_DIRECTORY_ENTRY_IMPORT,&ulSize); }
__except (InvalidReadExceptionFilter(GetExceptionInformation())){
//这里什么也不错,但当hmodCaller为无效句柄时,此时可以保证线程仍能
//正常运行,但pImportDesc为NULL。
} if (pImportDesc == NULL)
return; //模块没有导入段或还未被加载。 //查找导入段中被调用函数的地址
for (; pImportDesc->Name; pImportDesc++){
//模块导入段的所有字符串都是以ANSI格式保存的
PSTR pszModName = (PSTR)((PBYTE)hmodCaller + pImportDesc->Name);
if (lstrcmpiA(pszModName,pszCalleeModName) == ){ //获得调用者导入表中要被拦截函数的地址
PIMAGE_THUNK_DATA pThunk = (PIMAGE_THUNK_DATA)
((PBYTE)hmodCaller + pImportDesc->FirstThunk);
//将要被拦截函数的地址替换为新的拦截函数的地址 //替当前函数替换为新函数的地址
for (; pThunk->u1.Function;pThunk++){ //获得函数地址
PROC* ppfn = (PROC*)&pThunk->u1.Function; //是要查找的函数?
BOOL bFound = (*ppfn == pfnCurrent);
if (bFound){
if (!WriteProcessMemory(GetCurrentProcess(),ppfn,&pfnNew,
sizeof(pfnNew),NULL)&&(ERROR_NOACCESS == GetLastError())) {
//该内存写保护
DWORD dwOldProtect;
if (VirtualProtect(ppfn,sizeof(pfnNew),PAGE_EXECUTE_WRITECOPY,
&dwOldProtect)){
WriteProcessMemory(GetCurrentProcess(), ppfn, &pfnNew,
sizeof(pfnNew), NULL);
VirtualProtect(ppfn, sizeof(pfnNew), dwOldProtect,
&dwOldProtect);
}
}
return; //做完,退出
}
} } //有些编译器会在同一模块中生成多个导入段。要循环各导入段,直至找到并修改为地址
}
} //////////////////////////////////////////////////////////////////////////
void CAPIHook::ReplaceEATEntryInOneMods(HMODULE hmod, PCSTR pszFunctionName, PROC pfnNew){
//获得hmod的导出表
ULONG ulSize; PIMAGE_EXPORT_DIRECTORY pExportDir = NULL;
__try{
pExportDir = (PIMAGE_EXPORT_DIRECTORY)ImageDirectoryEntryToData(
hmod, TRUE, IMAGE_DIRECTORY_ENTRY_EXPORT, &ulSize); }
__except (InvalidReadExceptionFilter(GetExceptionInformation())){
//这里什么也不错,但当hmod为无效句柄时,此时可以保证线程仍能
//正常运行,但pExportDir为NULL。
} if (pExportDir == NULL)
return; //模块没有导入段或还未被加载。 PDWORD pdwNamesRvas = (PDWORD)((PBYTE)hmod + pExportDir->AddressOfNames);
PWORD pwNameOrdinals = (PWORD)((PBYTE)hmod + pExportDir->AddressOfNameOrdinals);
PDWORD pdwFunctionAddresses = (PDWORD)((PBYTE)hmod + pExportDir->AddressOfFunctions); //查找导出段中被调用函数的地址
for (DWORD n = ; n < pExportDir->NumberOfNames; n++){
//模块导入段的所有字符串都是以ANSI格式保存的
PSTR pszFuncName = (PSTR)((PBYTE)hmod + pdwNamesRvas[n]);
if (lstrcmpiA(pszFuncName, pszFuncName) != )
continue; //找到指定的函数
WORD ordinal = pwNameOrdinals[n]; //获取函数的地址
PROC* ppfn = (PROC*)&pdwFunctionAddresses[ordinal]; //将新的地址写入RVA
pfnNew = (PROC)((PBYTE)pfnNew - (PBYTE)hmod); //将被拦截函数替换为替换函数
if (!WriteProcessMemory(GetCurrentProcess(), ppfn, &pfnNew,
sizeof(pfnNew), NULL) && (ERROR_NOACCESS == GetLastError())) {
//该内存写保护
DWORD dwOldProtect;
if (VirtualProtect(ppfn, sizeof(pfnNew), PAGE_EXECUTE_WRITECOPY,
&dwOldProtect)){
WriteProcessMemory(GetCurrentProcess(), ppfn, &pfnNew,
sizeof(pfnNew), NULL);
VirtualProtect(ppfn, sizeof(pfnNew), dwOldProtect,
&dwOldProtect);
}
}
break;
}
} //////////////////////////////////////////////////////////////////////////
void CAPIHook::ReplaceIATEntryInAllMods(PCSTR pszCalleeModName, PROC pfnCurrent, PROC pfnNew){
HMODULE hmodThisMod = ExcludeAPIHookMod ?
ModuleFromAddress(ReplaceIATEntryInAllMods) : NULL; //是否连该DLL本身的模块也替换,
//默认是被排除的,即本DLL不替换。 //获取进程模块列表
CToolhelp th(TH32CS_SNAPMODULE, GetCurrentProcessId()); MODULEENTRY32 me = { sizeof(me) };
for (BOOL bOk = th.ModuleFirst(&me); bOk;bOk = th.ModuleNext(&me)){ //注意:我们不替换自己的模块
if (me.hModule != hmodThisMod){
//替换函数
ReplaceIATEntryInOneMod(
pszCalleeModName, pfnCurrent, pfnNew, me.hModule);
}
}
} //////////////////////////////////////////////////////////////////////////
//当DLL是在被拦截以后加载进来时,使用下面这个函数
void WINAPI CAPIHook::FixupNewlyLoadedModuled(HMODULE hmod, DWORD dwFlags){
//如果一个模块最近被加载,拦截相应的API
if ((hmod != NULL) &&
(hmod !=ModuleFromAddress(FixupNewlyLoadedModuled)) &&//不要拦截自己的模块
((dwFlags & LOAD_LIBRARY_AS_DATAFILE) ==) &&
((dwFlags & LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE) == ) &&
((dwFlags & LOAD_LIBRARY_AS_IMAGE_RESOURCE) == )
){ // 如果某个对象的原函数地址为NULL,可能是延迟加载等原因导致原来DLL不在内存中而引起的
// 因此在这里根据模块名获得函数现在的地址
for (CAPIHook* p = sm_pHead; p != NULL;p=p->m_pNext){
if (p->m_pfnOrig !=NULL){
//本来只需对这个被加载的DLL修复那些被拦截的函数地址即可,
//但新载入的DLL,如果又静态链接(注意,不是动态)了其他DLL,这另外的这个DLL也调
//用了要被拦截的函数时,就产生了链接依赖。所以可以简单地调用替换所有模块的这个函数
ReplaceIATEntryInAllMods(p->m_pszCalleeModName, p->m_pfnOrig, p->m_pfnHook);
} else{
#ifdef _DEBUG
//这里不应该就结束了
wchar_t szPathname[MAX_PATH];
GetModuleFileNameW(NULL, szPathname, _countof(szPathname));
wchar_t sz[];
StringCchPrintfW(sz, _countof(sz),
TEXT("[%4u - %s]找不到 %S\r\n"),
GetCurrentProcessId(),szPathname,p->m_pszCalleeModName);
OutputDebugString(sz);
#endif
}
}
}
} //当Dlls后在被加载进行时使用
HMODULE WINAPI CAPIHook::LoadLibraryA(PCSTR pszModulePath){
HMODULE hmod = ::LoadLibraryA(pszModulePath);
FixupNewlyLoadedModuled(hmod, );
return (hmod);
} HMODULE WINAPI CAPIHook::LoadLibraryW(PCWSTR pszModulePath){
HMODULE hmod = ::LoadLibraryW(pszModulePath);
FixupNewlyLoadedModuled(hmod, );
return (hmod);
}
HMODULE WINAPI CAPIHook::LoadLibraryExA(PCSTR pszModulePath, HANDLE hFile, DWORD dwFlags)
{
HMODULE hmod = ::LoadLibraryExA(pszModulePath,hFile,dwFlags);
FixupNewlyLoadedModuled(hmod, dwFlags);
return (hmod);
}
HMODULE WINAPI CAPIHook::LoadLibraryExW(PCWSTR pszModulePath, HANDLE hFile, DWORD dwFlags)
{
HMODULE hmod = ::LoadLibraryExW(pszModulePath, hFile, dwFlags);
FixupNewlyLoadedModuled(hmod, dwFlags);
return (hmod);
}
//////////////////////////////////////////////////////////////////////////
//获得GetProcAddess函数的真实地址
FARPROC CAPIHook::GetProcAddressRaw(HMODULE hmod, PCSTR pszProcName){
return (::GetProcAddress(hmod, pszProcName)); //调用全局的API
}
//////////////////////////////////////////////////////////////////////////
//拦截通过对GetProcAddress调用而直接获得要被拦截函数地址的
//获取被Hook函数被保存起来的真实地址
FARPROC WINAPI CAPIHook::GetProcAddress(HMODULE hmod, PCSTR pszProcName){
//获取函数的真实地址
FARPROC pfn = GetProcAddressRaw(hmod, pszProcName); //函数是否是己被hook过的呢?
CAPIHook* p = sm_pHead; for (; (pfn != NULL) && (p != NULL);p=p->m_pNext){
if (pfn == p->m_pfnOrig){//函数地址匹配
pfn = p->m_pfnHook;
break;
}
}
return (pfn);
} //////////////////////////////////////////////////////////////////////////
//拦截LoadLibrary和GetProcAddress函数
CAPIHook CAPIHook::sm_LoadLibraryA("Kernel32.dll", "LoadLibraryA",
(PROC)CAPIHook::LoadLibraryA); CAPIHook CAPIHook::sm_LoadLibrarayW("Kernel32.dll", "LoadLibraryW",
(PROC)CAPIHook::LoadLibraryW); CAPIHook CAPIHook::sm_LoadLibraryExA("Kernel32.dll", "LoadLibraryExA",
(PROC)CAPIHook::LoadLibraryExA); CAPIHook CAPIHook::sm_LoadLibrarayExW("Kernel32.dll", "LoadLibraryExW",
(PROC)CAPIHook::LoadLibraryExW);

//LastMsgBoxInfoLib.h

/************************************************************************
Module: LastMsgBoxInfoLib.h
Notices: Copyright(c) 2008 Jeffrey Richter & Christophe Nasarre
************************************************************************/
#pragma once #include <windows.h> #ifndef LASTMSGBOXINFOLIBAPI
#define LASTMSGBOXINFOLIBAPI extern "C" __declspec(dllimport)
#endif //////////////////////////////////////////////////////////////////////////
LASTMSGBOXINFOLIBAPI BOOL WINAPI LastMsgBoxInfo_HookAllApps(BOOL bInstall, DWORD dwThreadId); //////////////////////////////////////////////////////////////////////////

//LastMsgBoxInfoLib.cpp

/************************************************************************
Module: LastMsgBoxInfoLib.cpp
NOtices: Copyright(c) 2008 Jeffrey Richter & Christophe Nasarre
************************************************************************/
#include "../../CommonFiles/CmnHdr.h"
#include <tchar.h>
#include "APIHook.h" #define LASTMSGBOXINFOLIBAPI extern "C" __declspec(dllexport)
#include "LastMsgBoxInfoLib.h"
#include <strsafe.h> //////////////////////////////////////////////////////////////////////////
//被拦截函数的原型
//MessageBoxA
typedef int(WINAPI* PFNMESSAGEBOXA)(HWND hWnd, PCSTR pszText,
PCSTR pszCaption,UINT uType); //MessageBoxW
typedef int(WINAPI* PFNMESSAGEBOXW)(HWND hWnd, PCWSTR pszText,
PCWSTR pszCaption, UINT uType); //外部变量
extern CAPIHook g_MessageBoxA;
extern CAPIHook g_MessageBoxW; //////////////////////////////////////////////////////////////////////////
HHOOK g_hHook = NULL; //////////////////////////////////////////////////////////////////////////
//这个函数将MessageBox信息发送给我们的主对话框
void SendLastMsgBoxInfo(BOOL bUnicode, PVOID pvCaption, PVOID pvText, int nResult){
//获取弹出“MessageBox”进程的路径
wchar_t szProcessPathname[MAX_PATH];
GetModuleFileNameW(NULL, szProcessPathname, MAX_PATH); //NULL—获取调用该函数的当前模块 //将返回值转为“可识别”的字符串
PCWSTR pszResult = L"(未知)";
switch (nResult)
{
case IDOK: pszResult = L"确定"; break;
case IDCANCEL: pszResult = L"取消"; break;
case IDABORT: pszResult = L"中止"; break;
case IDRETRY: pszResult = L"重试"; break;
case IDIGNORE: pszResult = L"忽略"; break;
case IDYES: pszResult = L"是"; break;
case IDNO: pszResult = L"否"; break;
case IDCLOSE: pszResult = L"关闭"; break;
case IDHELP: pszResult = L"帮助"; break;
case IDTRYAGAIN:pszResult = L"重试"; break;
case IDCONTINUE:pszResult = L"继续"; break;
} //创建要发送到主对话框的字符串
wchar_t sz[];
StringCchPrintfW(sz, _countof(sz), bUnicode ?
L"进程:(%d) %s\r\n 标题:%s\r\n 内容:%s\r\n 选择结果:%s\r\n":
L"进程:(%d) %s\r\n 标题:%S\r\n 内容:%S\r\n 选择结果:%s\r\n",
GetCurrentProcessId(),szProcessPathname,pvCaption,pvText,pszResult); //将字符串发送给主对话框
COPYDATASTRUCT cds = { , ((DWORD)wcslen(sz) + )*sizeof(wchar_t), sz };
FORWARD_WM_COPYDATA(FindWindow(NULL, TEXT("Last MessageBox Info")), NULL,
&cds,SendMessage);
} //////////////////////////////////////////////////////////////////////////
//MessageBoxW的替换函数
int WINAPI Hook_MessageBoxW(HWND hWnd, PCWSTR pszText, LPCWSTR pszCaption, UINT uType){
//调用原MMessageBoxW
int nRet = ((PFNMESSAGEBOXW)(PROC)g_MessageBoxW)
(hWnd,pszText,pszCaption,uType); //将“MessageBox”的信息发给我们的主对话框程序
SendLastMsgBoxInfo(TRUE,(PVOID)pszCaption,(PVOID)pszText,nRet); //返回选择结果给调用者
return (nRet);
} //////////////////////////////////////////////////////////////////////////
//MessageBoxA的替换函数
int WINAPI Hook_MessageBoxA(HWND hWnd, PCSTR pszText, LPCSTR pszCaption, UINT uType){
//调用原MessageBoxA
int nRet = ((PFNMESSAGEBOXA)(PROC)g_MessageBoxA)
(hWnd, pszText, pszCaption, uType); //将“MessageBox”的信息发给我们的主对话框程序
SendLastMsgBoxInfo(FALSE, (PVOID)pszCaption, (PVOID)pszText, nRet); //返回选择结果给调用者
return (nRet);
} //////////////////////////////////////////////////////////////////////////
//挂钩MessageBoxA和MessageBoxW函数
CAPIHook g_MessageBoxA("user32.dll", "MessageBoxA", (PROC)Hook_MessageBoxA);
CAPIHook g_MessageBoxW("user32.dll", "MessageBoxW", (PROC)Hook_MessageBoxW); //////////////////////////////////////////////////////////////////////////
//钩子程序
static LRESULT WINAPI GetMsgProc(int code, WPARAM wParam, LPARAM lParam){
return (CallNextHookEx(g_hHook, code, wParam, lParam));
} //////////////////////////////////////////////////////////////////////////
//返回包含包含指定内存地址的模块句柄
static HMODULE ModuleFromAddress(PVOID pv){ MEMORY_BASIC_INFORMATION mbi;
return ((VirtualQuery(pv, &mbi, sizeof(mbi)) != )
? (HMODULE)mbi.AllocationBase:NULL);
} //////////////////////////////////////////////////////////////////////////
BOOL WINAPI LastMsgBoxInfo_HookAllApps(BOOL bInstall, DWORD dwThreadId){
BOOL bOk; if (bInstall){
chASSERT(g_hHook == NULL);//钩子未安装,防止被安装两次 //安装Windows钩子
g_hHook = SetWindowsHookEx(WH_GETMESSAGE, GetMsgProc,
ModuleFromAddress(LastMsgBoxInfo_HookAllApps), dwThreadId);
bOk = (g_hHook != NULL);
} else{
chASSERT(g_hHook != NULL);//钩子己被安装
bOk = UnhookWindowsHookEx(g_hHook);
g_hHook = NULL;
} return (bOk);
}
////////////////////////////////////文件结束///////////////////////////////

//测试程序

//LastMsgBoxInfo.cpp

/************************************************************************
Module: LastMsgBoxInfo.cpp
Notices: Copyright(c) 2008 Jeffrey Richter & Christophe Nasarre
************************************************************************/ #include "../../CommonFiles/CmnHdr.h"
#include <tchar.h>
#include "resource.h"
#include "../22_LastMsgBoxInfoLib/LastMsgBoxInfoLib.h" #pragma comment(lib,"../../Debug/22_LastMsgBoxInfoLib.lib") //////////////////////////////////////////////////////////////////////////
BOOL Dlg_OnInitDialog(HWND hwnd, HWND hwndFocus, LPARAM lParam){
chSETDLGICONS(hwnd, IDI_LASTMSGBOXINFO);
SetDlgItemText(hwnd, IDC_INFO,
TEXT("正在等待“MessageBox”被关闭"));
return (TRUE);
} //////////////////////////////////////////////////////////////////////////
void Dlg_OnSize(HWND hWnd, UINT state, int cx, int cy){
SetWindowPos(GetDlgItem(hWnd, IDC_INFO), NULL, , , cx, cy, SWP_NOZORDER);
} //////////////////////////////////////////////////////////////////////////
void Dlg_OnCommand(HWND hwnd, int id, HWND hwndCtrl, UINT codeNotity){
switch (id)
{
case IDCANCEL:
EndDialog(hwnd, id);
break;
}
} //////////////////////////////////////////////////////////////////////////
BOOL Dlg_OnCopyData(HWND hWnd, HWND hWndFrom, PCOPYDATASTRUCT pcds){ //将被拦截的进程发送给我们的消息对话框信息显示出来
SetDlgItemTextW(hWnd, IDC_INFO, (PCWSTR)pcds->lpData);
return (TRUE);
} //////////////////////////////////////////////////////////////////////////
INT_PTR WINAPI Dlg_Proc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam){
switch (uMsg)
{
chHANDLE_DLGMSG(hWnd, WM_INITDIALOG, Dlg_OnInitDialog);
chHANDLE_DLGMSG(hWnd, WM_SIZE, Dlg_OnSize);
chHANDLE_DLGMSG(hWnd, WM_COMMAND, Dlg_OnCommand);
chHANDLE_DLGMSG(hWnd, WM_COPYDATA, Dlg_OnCopyData);
} return (FALSE);
} //////////////////////////////////////////////////////////////////////////
int WINAPI _tWinMain(HINSTANCE hInstExe, HINSTANCE, PTSTR pszCmdLine, int){
DWORD dwThreadId = ; //为系统中所有线程安装钩子
LastMsgBoxInfo_HookAllApps(TRUE, dwThreadId); //TRUE为安装钩子,0表示所有线程
DialogBox(hInstExe, MAKEINTRESOURCE(IDD_LASTMSGBOXINFO), NULL, Dlg_Proc);
LastMsgBoxInfo_HookAllApps(FALSE, ); //FALSE为卸载钩子
return ();
}

//resource.h

//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ 生成的包含文件。
// 供 22_LastMsgBoxInfo.rc 使用
//
#define IDD_LASTMSGBOXINFO 101
#define IDI_LASTMSGBOXINFO 102
#define IDC_INFO 1001 // Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 103
#define _APS_NEXT_COMMAND_VALUE 40001
#define _APS_NEXT_CONTROL_VALUE 1001
#define _APS_NEXT_SYMED_VALUE 101
#endif
#endif

//.rc文件

// Microsoft Visual C++ generated resource script.
//
#include "resource.h" #define APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 2 resource.
//
#include "winres.h" /////////////////////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS /////////////////////////////////////////////////////////////////////////////
// 中文(简体,中国) resources #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_CHS)
LANGUAGE LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED #ifdef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// TEXTINCLUDE
// TEXTINCLUDE
BEGIN
"resource.h\0"
END TEXTINCLUDE
BEGIN
"#include ""winres.h""\r\n"
"\0"
END TEXTINCLUDE
BEGIN
"\r\n"
"\0"
END #endif // APSTUDIO_INVOKED /////////////////////////////////////////////////////////////////////////////
//
// Dialog
// IDD_LASTMSGBOXINFO DIALOG , , ,
STYLE DS_SETFONT | DS_CENTER | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME
CAPTION "Last MessageBox Info"
FONT , "MS Shell Dlg"
BEGIN
EDITTEXT IDC_INFO,,,,,ES_MULTILINE | ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_READONLY | WS_VSCROLL | WS_HSCROLL
END /////////////////////////////////////////////////////////////////////////////
//
// Icon
// // Icon with lowest ID value placed first to ensure application icon
// remains consistent on all systems.
IDI_ICON1 ICON "LastMsgBoxInfo.ico"
#endif // 中文(简体,中国) resources
///////////////////////////////////////////////////////////////////////////// #ifndef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 3 resource.
// /////////////////////////////////////////////////////////////////////////////
#endif // not APSTUDIO_INVOKED

第22章 DLL注入和API拦截(3)的更多相关文章

  1. 第22章 DLL注入和API拦截(2)

    22.4 使用远程线程来注入DLL 22.4.1 概述 (1)远程线程注入是指一个进程在另一个进程中创建线程,然后载入我们编写的DLL,并执行该DLL代码的技术.其基本思路是通过CreateRemot ...

  2. 第22章 DLL注入和API拦截(1)

    22.1 注入的一个例子(跨进程子类化窗口) ①子类化窗口可以改变窗口的行为,让发往该窗口的消息重新发到我们指定的过程来处理.但这种行为只能在本进程中(如A),对于从一个进程(如B)去子类化另一个进程 ...

  3. DLL注入_拦截技术之Hook方式

    后卫大师教你进程注入 首先提一下,由于文章完全是我手写,所以打不了太多,请包含,由于我已经提供了源代码,所以我在这里详细讲一下理论,至于想看代码的下载代码就可以了.代码中关于注入的部分做了详细的注释. ...

  4. Win32环境下代码注入与API钩子的实现(转)

    本文详细的介绍了在Visual Studio(以下简称VS)下实现API钩子的编程方法,阅读本文需要基础:有操作系统的基本知识(进程管理,内存管理),会在VS下编写和调试Win32应用程序和动态链接库 ...

  5. Win32环境下代码注入与API钩子的实现

    本文详细的介绍了在Visual Studio(以下简称VS)下实现API钩子的编程方法,阅读本文需要基础:有操作系统的基本知识(进程管理,内存管理),会在VS下编写和调试Win32应用程序和动态链接库 ...

  6. 20145319 《网络渗透》免考—API拦截技术

    20145319 <网络渗透>免考-API拦截技术 概述 本次实验在window环境下进行,主要通过编写hook代码和注入程序,将我们的hook代码通过dll文件的形式注入到目标中,拦截其 ...

  7. <ReversingEngineering>关于windows32位系统下的dll注入技术经验汇

    上个学期把自己闷在图书馆一直在看关于逆向工程技术方面的书,从入门到初级,现在也敢说自己一条腿已经迈进了这片知识的大门里,因为该博客刚开通先将一些经验记录下来,也是留给自己一方面做个参照. <逆向 ...

  8. DLL注入新姿势:反射式DLL注入研究

    在分析koadic渗透利器时,发现它有一个注入模块,其DLL注入实现方式和一般的注入方式不一样.搜索了一下发现是由HarmanySecurity的Stephen Fewer提出的ReflectiveD ...

  9. Dll注入技术之消息钩子

    转自:黑客反病毒 DLL注入技术之消息钩子注入 消息钩子注入原理是利用Windows 系统中SetWindowsHookEx()这个API,他可以拦截目标进程的消息到指定的DLL中导出的函数,利用这个 ...

随机推荐

  1. PHP遍历目录四种方法

    学习SPL的时候,遇到了DirectoryIterator这个目录类,谢了一下遍历目录的方法.于是总结一下遍历目录的四种写法 如下: <?php /* * 方法一:利用SPL的目录类,这个很简单 ...

  2. git 删除错误提交的commit

    方法: 根据–soft –mixed –hard,会对working tree和index和HEAD进行重置:    git reset --mixed:此为默认方式,不带任何参数的git reset ...

  3. j2ee log4j集中式日志解决方案logpool-v0.4发布说明

    logpool v0.4发布说明: 1.持久化采用mongodb非结构化存储实现,以满足后续调整的灵活需要:

  4. 通用javascript方法

    //将序列化成json格式后日期(毫秒数)转成日期格式 YYYY-MM-DD HH:MI:SS function ChangeDateFormat(cellval, type) { var date ...

  5. SharePoint 2013 点击"关注" 报错

    现象: 点击"关注" 报错. 解决办法: 1.确保bin文件夹下的.dll版本与web.config一致. 2.设置user porfile权限. 2.重启iis 结果如下:

  6. Powershell Remove "Limited Access" - 金大昊(jindahao)

    对于有多级web获取getlist会报错:Exception calling “GetList” with “1” argument $SPWeb = Get-SPWeb -Identity http ...

  7. java中判断字符串是否为数字的方法

    一: //1.用JAVA自带的函数 public static boolean isNumeric(String str){ for (int i = 0; i < str.length(); ...

  8. bash shell命令(2)

    在上篇<bash shell命令(1)>中,介绍了几种简单的linux shell命令,今天继续介绍bash shell命令 本文地址:http://www.cnblogs.com/arc ...

  9. Silverlight项目笔记2:.svc处理程序映射缺失导致的WCF RIA Services异常

    在确定代码.编译结果和数据库都正常的情况下,无法从数据库取到数据.错误提示:Sysyem.Net.WebException:远程服务器返回了错误:NotFound,监听发现请求数据库的服务异常,访问相 ...

  10. iOS-多线程之NSOperation

    前言 这篇文章主要讲NSOperation的使用. What 使用NSOperation和NSOperationQueue进行多线程开发类似于线程池,只要将一个NSOperation(实际开发中需要使 ...