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

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

 

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

ContractedBlock.gifExpandedBlockStart.gifDebugView输出
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;

ContractedBlock.gifExpandedBlockStart.gif 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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值