API 挂钩






4.83/5 (13投票s)
使用 API 挂钩嗅探网络
引言
Network snoop 介绍了构建网络嗅探器的基础知识,用于捕获通过 TCP 套接字使用 send
和 recv
API 发送的所有信息。显然,这两个 API 是要被 hook 以获取有关发送/接收数据的信息。这段代码更多的是以 Network snoop 为例向读者介绍 API hooking。读者可以修改代码以 hook 与 UDP 套接字相关的 API(这就是为什么读者需要了解套接字和网络知识)。
背景
在我们开始之前,读者需要了解套接字编程、Windows hook 和一点汇编语言编程的基础知识。
Using the Code
附带的代码是使用 VS2010 express edition 构建的。阅读本文时应参考此代码。另外,还附带了 64 位 API hooking 代码,但**未经测试**。
我们将从介绍用于发送和接收信息的套接字 API 开始,它们是 send
和 recv
。附带的代码将尝试 hook/Hijack 这些 API,并尝试记录通过它们传递的所有信息。
DLL 模块中提到了以下代码
struct NetSnoop{
NetSnoop()
{
_beginthread(sendThread,0,0);
_beginthread(recvThread,0,0);
}
...
}
g_NetSnoop;
注意全局声明的变量 g_NetSnoop
,该对象将在每次 DLL 加载到新/不同的进程时被创建(并调用其构造函数,该构造函数将调用 sendThread
和 recvThread
)。为了让 DLL 加载到不同的进程中,我们使用 Windows hook API(代码中提到:NetSnoop.cpp)。
SetWindowsHookEx(WH_CBT,hookFunction,h,0);
这里,变量 h
持有前面提到的 DLL 的模块句柄。调用 SetWindowsHookEx
将导致属于调用者桌面的所有线程加载传递给它的模块的 DLL,在这种情况下是
HMODULE h=LoadLibraryA("NetSnoopDll.dll");
读者需要了解 Windows hook。
另一种方法是调用(不使用 hook)CreateRemoteThread
。您可以调用上面的函数 sendThread
和 recvThread
,请在 MSDN 上查阅 CreateRemoteThread
。(此方法未在代码中使用。)
现在进入最精彩的部分:API hooking (APIHook.cpp)。
我们将讨论 send
API 的 API hooking,其他 API 都一样,请始终参考代码,这很重要。
函数 void sendThread(void*)
将执行 API hack,注意该函数是从 struct NetSnoop
的构造函数(通过不同的线程)**或通过** API CreateRemoteThread
**调用**的。
请遵循代码。
我们首先获取函数的地址并使用 VirtualProtect
。
请务必使用 VirtualProtect
来更改已提交页面的保护,否则您将收到一个异常(386 内存段保护!)。查阅 MSDN 关于 VirtualProtect
的说明。
VirtualProtect(send,sizeof(Trap_Send),PAGE_EXECUTE_READWRITE,&dPermission);
后面的代码是不言自明的。
请使用函数调用的反汇编来更好地理解其工作原理,您会注意到在函数地址处添加了以下代码
MOV EAX,function adress;
JMP EAX;
我们**使用操作码**将以下代码注入函数调用地址
64 位代码在附件中。在我们修改函数地址处的指令之前,我们首先存储原始指令,以便在需要时恢复它们。
请记住:32 位 DLL 不能加载/注入到 64 位进程空间中,反之亦然,这就是为什么您需要一个单独的 64 位应用程序/DLL 来嗅探 64 位进程的网络流量。
工作原理
当被 hook 的函数(在本例中为 send
)被调用时,指令指针 (EIP) 会指向函数地址并从那里开始执行(嗯……任何函数调用都是这样工作的)。
我们在该位置放置了一个 JMP
指令。这将导致 EIP
跳转到函数 Mysend
,请注意,此函数具有与原始函数相同的签名和调用约定,以避免任何堆栈损坏。
一旦 EIP
被路由到函数 Mysend
(如附带代码所示)
- 记录任何需要记录的内容
- 恢复函数的原始指令
- 调用原始函数以完成功能
在 send
函数的 API hooking 中,请注意,应用程序会调用 send
函数两次
- 由应用程序调用,通过
JMP
指令,EIP
被路由到Mysend
- 在
Mysend
内部,恢复原始指令后(如下所示)
代码片段来自 APIHook.cpp, int WSAAPI Mysend(...)
...
DWORD dPermission=0;
VirtualProtect(send,sizeof(Trap_Send),PAGE_EXECUTE_READWRITE,&dPermission);
memcpy(send,StoreOriginal_Send,sizeof(StoreOriginal_Send)); //restore it
int ret=send(s,buf,len,flags); //we call the original function.
if(bExit==false) //repatch the function (to trap the next call to it),
//bExit is set to true to indicate exit.
{
memcpy(send, Trap_Send, sizeof(Trap_Send)); //repatch
}
因此,为了维护堆栈,RET
指令也会被调用两次
- 从
Mysend
内部调用的原始send
函数返回后 - 从
Mysend
返回后
恢复(在析构函数中完成)
另一件重要的事情是,在应用程序退出时恢复所有内容。
这一点很重要,当应用程序(NetworkSnoop
)退出时,Hook 将由代码释放(或者当应用程序关闭/终止时,如果 Hook 仍然激活,则由操作系统释放)。
当应用程序被 unhook 时,注入的 DLL 会被移除,如果代码未被恢复,JMP
指令将导致 EIP
跳转到一个无效的内存位置(因为 DLL 已被移除),从而导致崩溃。
应用程序在全局对象的析构函数中恢复所有内容,析构函数在 DLL 卸载期间被调用,请参考代码。
~NetSnoop()
{
bExit=true; //set this flag to true to indicate unloading is in progress
//(to avoid repatch indicated in previous code snippet)
while(uiInUse!=0) //this maintains a count of trap function being called
//from different threads
Sleep(500);
//restore original
DWORD dPermission=0;
VirtualProtect(send,sizeof(Trap_Send),PAGE_EXECUTE_READWRITE,&dPermission);
memcpy(send,StoreOriginal_Send,sizeof(StoreOriginal_Send)); //restore it
VirtualProtect(recv,sizeof(Trap_recv),PAGE_EXECUTE_READWRITE,&dPermission);
memcpy(recv,StoreOriginal_recv,sizeof(StoreOriginal_recv)); //restore it
}
调试调用
读者需要编写下面提到的一个简单的控制台应用程序,并通过反汇编来调试 send 调用,以更好地理解 JMP
指令的引入。
#include<windows.h>
#include<WinSock2.h>
int main()
{
MessageBoxA(0,"","",0); //provide a message processing loop via message box
//(for DLL injection via Hook, a message processing loop is required)
int z=send(0,0,0,0); //function from Ws2_32.lib (add this to your lib section),
//pass any dummy values, the idea is to understand via
//disassembly the introduction of JMP instruction
//after the hook has injected the DLL
return z;
}
关注点
最棒的是 NetworkSnoop
使用 Windows Hook 来介绍 API hooking。这允许您 hook 任何进程中的任何函数,前提是您的 hook 将 DLL 注入其中。为了让 hook 生效,目标线程必须有一个消息循环。我希望您喜欢这篇关于 Network snoop 的简短文章。API hook 技术被广泛使用,最著名的是 FRAPS (www.fraps.com),它使用该技术 hook DirectX 调用以获取游戏性能信息。
历史
- 2011 年 3 月 11 日:初始发布
- 2011 年 5 月 2 日:代码修正,以便在调用 recv API 后进行日志记录