另类挂钩-RING3数据包监视(Delphi版)

it2022-05-05  130

标 题: 【原创】另类挂钩-RING3数据包监视作 者: qihoocom时 间: 2009-02-01,15:20链 接: http://bbs.pediy.com/showthread.php?t=81204

 

闲着无聊,在看雪上看到这个帖子。Hook NtDeviceIoControlFile 果然够另类,于是迅速翻成Delphi的代码,效果还不错:

DebugView输出00007368  23.14766884  [4048] [HOOK] NDIC_Hook dll loaded.  00007369  23.14779282  [4048] [HOOK] Lock "NtDeviceIoControlFile" for HOOK.  00007370  23.14781952  [4048] [HOOK] Base=719C0000, Thunk=0000127C, ID=F  00007371  23.14791679  [4048] [HOOK] Orign[0x719C12B8]=0x7C92D8E3, new Addr=0x04DEA3C4  00008751  28.35400581  [4048] [HTTP Send] Length = 822  00008752  28.35421753  [4048] GET / HTTP/1.1   00008753  28.35421753  [4048] Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, application/x-shockwave-flash, */*   00008754  28.35421753  [4048] Accept-Language: zh-cn   00008755  28.35421753  [4048] User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1) ; .NET CLR 2.0.50727; CIBA; TheWorld)   00008756  28.35421753  [4048] UA-CPU: x86   00008757  28.35421753  [4048] Accept-Encoding: gzip, deflate   00008758  28.35421753  [4048] Host: www.baidu.com   00008759  28.35421753  [4048] Connection: Keep-Alive   00008760  28.35421753  [4048] Cookie: BAIDUID=F07CEBAE4F4B5A6DE8A3D73BDA7CBB34:FG=1;  00008761  28.35421753  [4048]    00008783  28.39130974  [4048] [HTTP Recv] Length = 1024  00008784  28.39136696  [4048] HTTP/1.1 200 OK   00008785  28.39136696  [4048] Date: Sun, 01 Feb 2009 14:43:22 GMT   00008786  28.39136696  [4048] Server: BWS/1.0   00008787  28.39136696  [4048] Content-Length: 2029   00008788  28.39136696  [4048] Content-Type: text/html   00008789  28.39136696  [4048] Cache-Control: private   00008790  28.39136696  [4048] Expires: Sun, 01 Feb 2009 14:43:22 GMT   00008791  28.39136696  [4048] Content-Encoding: gzip   00008792  28.39136696  [4048]    00008793  28.39136696  [4048] ?  00008814  28.42530823  [4048] [HTTP Send] Length = 796  00008815  28.42535210  [4048] GET /js/bdsug.js?v=1.0.1.0 HTTP/1.1   00008816  28.42535210  [4048] Accept: */*   00008817  28.42535210  [4048] Referer: http://www.baidu.com/   00008818  28.42535210  [4048] Accept-Language: zh-cn   00008819  28.42535210  [4048] User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1) ; .NET CLR 2.0.50727; CIBA; TheWorld)   00008820  28.42535210  [4048] UA-CPU: x86   00008821  28.42535210  [4048] Accept-Encoding: gzip, deflate   00008822  28.42535210  [4048] If-Modified-Since: Mon, 19 Jan 2009 13:18:00 GMT   00008823  28.42535210  [4048] If-None-Match: "1599-49747d88"   00008824  28.42535210  [4048] Host: www.baidu.com   00008825  28.42535210  [4048] Connection: Keep-Alive   00008826  28.42535210  [4048] Cookie: BAIDUID=F07CEBAE4F4B5A6DE8A3D73BDA7CBB34:FG=1;00008827  28.42535210  [4048]    00008840  28.45828247  [4048] [HTTP Recv] Length = 1024  00008844  28.45885849  [4048] HTTP/1.1 304 Not Modified   00008845  28.45885849  [4048] Date: Sun, 01 Feb 2009 14:43:22 GMT   00008846  28.45885849  [4048] Server: Apache/1.3.27   00008847  28.45885849  [4048] ETag: "1599-49747d88"   00008848  28.45885849  [4048]    

两个GET和返回数据都抓下来了。

 

关键代码如下:

unit  uNtDeviceIoControl; {   原理:Hook ntdll!NtDeviceIoControlFile     拦截 AFD_RECV 和 AFD_SEND 对TCP包进行监视,     通过比较 send 函数的 Buffer 是否为 GET/POST     recv 函数的 Buffer 是否为 HTTP,来判断HTTP包     代码思路相当清晰,又有注释(原来就有),我就不多说了。 } interface uses   SysUtils, Windows; const   AFD_RECV  =  $ 12017 ;   AFD_SEND  =  $1201f;   HTTP_GET: AnsiString  =   ' GET  ' ;   HTTP_POST: AnsiString  =   ' POST  ' ;   HTTP_RESPONSE: AnsiString  =   ' HTTP ' ; type   NTSTATUS  =  DWORD;   PVOID  =  Pointer;   _AFD_WSABUF  =   record     len: DWORD;     buf: PAnsiChar;    end ;   TAFD_WSABUF  =  _AFD_WSABUF;   PAFD_WSABUF  =  ^TAFD_WSABUF;   _AFD_INFO  =   record     BufferArray: PAFD_WSABUF;     BufferCount: DWORD;     AfdFlags: DWORD;     TdiFlags: DWORD;    end ;   TAFD_INFO  =  _AFD_INFO;   PAFD_INFO  =  ^TAFD_INFO;   _IO_STATUS_BLOCK  =   record      // union  {     Status: NTSTATUS;     //    PVOID Pointer;     // }     Information: ULONG_PTR;    end ;   IO_STATUS_BLOCK  =  _IO_STATUS_BLOCK;   PIO_STATUS_BLOCK  =  ^IO_STATUS_BLOCK;   TIoStatusBlock  =  IO_STATUS_BLOCK;   PIoStatusBlock  =  ^TIoStatusBlock;   PIO_APC_ROUTINE  =   procedure (ApcContext: PVOID; IoStatusBlock: PIO_STATUS_BLOCK; Reserved: ULONG);  stdcall ;   PIMAGE_IMPORT_DESCRIPTOR  =  ^_IMAGE_IMPORT_DESCRIPTOR;   PImageImportDescriptor  =  PIMAGE_IMPORT_DESCRIPTOR;   _IMAGE_IMPORT_DESCRIPTOR  =   packed   record     CharacteristicsOrOriginalFirstThunk: DWord;     TimeDateStamp: DWord;     ForwarderChain: DWord;     Name: DWord;     FirstThunk: DWord;    end ;   PIMAGE_THUNK_DATA  =  ^_IMAGE_THUNK_DATA;   PImageThunkData  =  PIMAGE_THUNK_DATA;   _IMAGE_THUNK_DATA  =   packed   record      case  Integer  of        0  : (ForwarderString: DWord);        1  : (Function_: DWord);        2  : (Ordinal: DWord);        3  : (AddressOfData: DWord);    end ; function  NT_SUCCESS(Status: NTSTATUS): BOOL; { $EXTERNALSYM NT_SUCCESS } var   OldNtDeviceIoControl: DWORD; procedure  SuperHookDeviceIoControl(); implementation function  NT_SUCCESS(Status: NTSTATUS): BOOL; begin    // Result : =  Status  >=   0 ;   Result : =  Status  <  $ 80000000 ; end ; // /// ///  LookupSendPacket ///  检查Send包 ///  目前实现了过滤HTTP请求(GET AND POST) /// // function  LookupSendPacket(Buffer: Pointer; Len: DWORD): Boolean; begin   Result : =  False;    //  过滤长度太小的包    if  (Len  <   10 then  Exit;    //  检查是不是GET或POST    if  ( CompareMem(Buffer, @HTTP_GET[ 1 ],  4 )      or  CompareMem(Buffer, @HTTP_POST[ 1 ],  4 ) )  then    begin     Result : =  True;    end ; end ; // /// ///  LookupRecvPacket /// ///  检查Recv包 ///  在这里可以实现Recv包查字典功能 ///  目前实现了过滤HTTP返回数据包的功能 /// /// /// function  LookupRecvPacket(Buffer: Pointer; Len: DWORD): Boolean; begin   Result : =  False;    if  (Len  <   10 then  Exit;    if  ( CompareMem(Buffer, @HTTP_RESPONSE[ 1 ],  4 ) )  then    begin     Result : =  True;    end ; end ; {  HOOK 函数  } // /// ///  NtDeviceIoControlFile的HOOK函数 ///  ws2_ 32 .dll的send , recv最终会调用到mswsock.dll内的数据发送函数 ///  mswsock.dll会调用NtDeviceIoControl向TDI Client驱动发送Send Recv指令 ///  我们在这里做拦截,可以过滤所有的TCP 收发包(UDP之类亦可,不过要更改指令) /// // ///  Compatibility: NT3, NT4, W2K, WXP, 2K3 function  NewNtDeviceIoControlFile(     FileHandle : THANDLE;     Event : THANDLE;     ApcRoutine : PIO_APC_ROUTINE;     ApcContext : PVOID;     IoStatusBlock : PIO_STATUS_BLOCK;     IoControlCode : ULONG;     InputBuffer : PVOID;     InputBufferLength : ULONG;     OutputBuffer : PVOID;     OutputBufferLength : ULONG   ): NTSTATUS;  stdcall ; var   AfdInfo: PAFD_INFO;   Buffer: PAnsiChar;   Len: DWORD; begin    //  先调用原始函数    asm     push  OutputBufferLength     push  OutputBuffer     push  InputBufferLength     push  InputBuffer     push  IoControlCode     push  IoStatusBlock     push  ApcContext     push  ApcRoutine     push  Event     push  FileHandle     call  OldNtDeviceIoControl     mov   Result, eax    end ;    //  如果原始函数失败了(例如RECV无数据)    if  (Not NT_SUCCESS(Result))  then    begin     Exit;    end ;    //  检查是否为TCP收发指令    if  (IoControlCode  <>  AFD_SEND)      and  (IoControlCode  <>  AFD_RECV)  then    begin     Exit;    end ;    //  访问AFD INFO结构,获得SEND或RECV的BUFFER信息    //  这里可能是有问题的BUFFER,因此我们要加TRY EXCEPT    try      //  从 InputBuffer 得到 Buffer 和 Len     AfdInfo : =  PAFD_INFO(InputBuffer);     Buffer : =  AfdInfo.BufferArray.buf;     Len : =  AfdInfo.BufferArray.len;      case  IoControlCode  of       AFD_SEND:          if  ( LookupSendPacket(Buffer, Len) )  then          begin            //  输出包内容           OutputDebugString(PChar(Format( ' [HTTP Send] Length = %d ' , [Len])));           OutputDebugString(PChar(Format( ' %s ' , [StrPas(Buffer)])));          end ;       AFD_RECV:          if  ( LookupRecvPacket(Buffer, Len) )  then          begin            //  输出包内容           OutputDebugString(PChar(Format( ' [HTTP Recv] Length = %d ' , [Len])));           OutputDebugString(PChar(Format( ' %s ' , [StrPas(Buffer)])));          end ;      end ;    except    end ; end ; // /// ///   Hook mswsock.dll导出表的Ntdll!NtDeviceIoControlFile ///   并过滤其对TDI Cilent的请求来过滤封包 ///   稳定,隐蔽,RING3下最底层的包过滤~ /// // procedure  SuperHookDeviceIoControl(); var   hMod: HMODULE;   pDosHeader: PImageDosHeader;   pNtHeaders: PImageNtHeaders;   ImportDescriptor: PImageImportDescriptor;   ThunkData: PImageThunkData;   dll_name, func_name: PAnsiChar;   iNum: Integer;   lpAddr: Pointer;   myaddr, btw: DWORD; begin    // 得到ws2_ 32 .dll的模块基址   hMod : =  LoadLibrary( ' mswsock.dll ' );    if  (hMod  =   0 then    begin     OutputDebugString(PChar(Format( ' LoadLibrary(%s)失败! ' , [ ' mswsock.dll ' ])));     Exit;    end ;    // 得到DOS头   pDosHeader : =  PImageDosHeader(hMod);    // 如果DOS头无效    if  ( pDosHeader^.e_magic  <>  IMAGE_DOS_SIGNATURE )  then    begin     Exit;    end ;    // 得到NT头   pNtHeaders : =  PImageNtHeaders(hMod  +  DWORD(pDosHeader^._lfanew));    // 如果NT头无效    if  ( pNtHeaders^.Signature  <>  IMAGE_NT_SIGNATURE )  then    begin     Exit;    end ;    // 检查输入表数据目录是否存在    if  (pNtHeaders^.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress  =   0 )      or  (pNtHeaders^.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size  =   0 then    begin     Exit;    end ;    // OutputDebugString(PChar(Format( ' [HOOK] lock mswsock.dll, waiting ' , [])));    // 得到输入表描述指针   ImportDescriptor : =  PImageImportDescriptor(hMod  +  pNtHeaders^.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);    // 检查每个输入项    while  (ImportDescriptor^.FirstThunk  <>   0 do    begin      //  检查输入表项是否为ntdll.dll     dll_name : =  PAnsiChar(hMod  +  ImportDescriptor^.Name);      //  如果不是,则跳到下一个处理      if  (StrIComp(dll_name,  ' ntdll.dll ' <>   0 then      begin       ImportDescriptor : =  PImageImportDescriptor(DWORD(ImportDescriptor)  +  SizeOf(_IMAGE_IMPORT_DESCRIPTOR));       Continue;      end ;     ThunkData : =  PImageThunkData(hMod  +  ImportDescriptor^.CharacteristicsOrOriginalFirstThunk);     iNum : =   1 ;      while  (ThunkData^.Function_  <>   0 do      begin        //  检查函数是否为NtDeviceIoControlFile       func_name : =  PAnsiChar(hMod  +  ThunkData^.AddressOfData  +   2 );        // OutputDebugString(PChar(Format( ' [HOOK] find API: %s ' , [StrPas(func_name)])));        if  (StrIComp(func_name ,  ' NtDeviceIoControlFile ' =   0 then        begin         OutputDebugString(PChar(Format( ' [HOOK] Lock "%s" for HOOK. ' , [StrPas(func_name)])));          //          //  如果是,那么记录原始函数地址          //  HOOK我们的函数地址          //          //  序号     RVA     偏移  Name          //    9A  D8E3  CCE3  NtDeviceIoControlFile         myaddr : =  DWORD(@NewNtDeviceIoControlFile);         lpAddr : =  Pointer(hMod  +  ImportDescriptor^.FirstThunk  +  DWORD(iNum - 1 ) * 4 );         OldNtDeviceIoControl : =  PDWORD(lpAddr)^;         OutputDebugString(PChar(Format( ' [HOOK] Base=%0.8X, Thunk=%0.8X, ID=%X ' , [hMod, ImportDescriptor^.FirstThunk, iNum - 1 ])));         OutputDebugString(PChar(Format( ' [HOOK] Orign[0x%0.8X]=0x%0.8X, new Addr=0x%0.8X ' , [DWORD(lpAddr), PDWORD(lpAddr)^, myaddr])));         WriteProcessMemory(GetCurrentProcess(), lpAddr, @myaddr,  4 , btw);         Exit;        end ;       Inc(iNum);       ThunkData : =  PImageThunkData(DWORD(ThunkData)  +  SizeOf(_IMAGE_THUNK_DATA));      end ;     Inc(ImportDescriptor);    end ; end ; end .

 

这里是源码 r3_Hook_NtDeviceIoControl_src.rar,Delphi 2007/2009编译通过。 

使用方法:用附带的 DLL_Inject.exe 插入 NDIC_Hook.dll 到浏览器中,可以使用DebugView看到HTTP的信息。

 

补充:从ReactOS的AFD中导出的完整定义,对应NtDeviceIoControlFile中的IoControlCode,可以把之前的这个替换掉了:) AFD_RECV = $12017; AFD_SEND = $1201f;

IOCTL_AFD_* const   IOCTL_AFD_BIND = $00012003;   IOCTL_AFD_CONNECT = $00012007;   IOCTL_AFD_START_LISTEN = $0001200B;   IOCTL_AFD_WAIT_FOR_LISTEN = $0001200C;   IOCTL_AFD_ACCEPT = $00012010;   IOCTL_AFD_RECV = $00012017;   IOCTL_AFD_RECV_DATAGRAM = $0001201B;   IOCTL_AFD_SEND = $0001201F;   IOCTL_AFD_SEND_DATAGRAM = $00012023;   IOCTL_AFD_SELECT = $00012024;   IOCTL_AFD_DISCONNECT = $0001202B;   IOCTL_AFD_GET_SOCK_NAME = $0001202F;   IOCTL_AFD_GET_PEER_NAME = $00012033;   IOCTL_AFD_GET_TDI_HANDLES = $00012037;   IOCTL_AFD_SET_INFO = $0001203B;   IOCTL_AFD_GET_CONTEXT = $0001203F;   IOCTL_AFD_SET_CONTEXT = $00012043;   IOCTL_AFD_SET_CONNECT_DATA = $00012047;   IOCTL_AFD_SET_CONNECT_OPTIONS = $0001204B;   IOCTL_AFD_SET_DISCONNECT_DATA = $0001204F;   IOCTL_AFD_SET_DISCONNECT_OPTIONS = $00012053;   IOCTL_AFD_GET_CONNECT_DATA = $00012057;   IOCTL_AFD_GET_CONNECT_OPTIONS = $0001205B;   IOCTL_AFD_GET_DISCONNECT_DATA = $0001205F;   IOCTL_AFD_GET_DISCONNECT_OPTIONS = $00012063;   IOCTL_AFD_SET_CONNECT_DATA_SIZE = $0001206B;   IOCTL_AFD_SET_CONNECT_OPTIONS_SIZE = $0001206F;   IOCTL_AFD_SET_DISCONNECT_DATA_SIZE = $00012073;   IOCTL_AFD_SET_DISCONNECT_OPTIONS_SIZE = $00012077;   IOCTL_AFD_GET_INFO = $0001207B;   IOCTL_AFD_EVENT_SELECT = $00012087;   IOCTL_AFD_DEFER_ACCEPT = $0001208F;   IOCTL_AFD_GET_PENDING_CONNECT_DATA = $000120A7;   IOCTL_AFD_ENUM_NETWORK_EVENTS = $0001208B;

转载于:https://www.cnblogs.com/bits/archive/2009/03/01/Ring3_Hook-NtDeviceIoControl.html


最新回复(0)