65.9K
CodeProject 正在变化。 阅读更多。
Home

API 挂钩

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.83/5 (13投票s)

2011年3月11日

CPOL

5分钟阅读

viewsIcon

84105

downloadIcon

2651

使用 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 后进行日志记录
© . All rights reserved.