• 设为首页
  • 点击收藏
  • 手机版
    手机扫一扫访问
    迪恩网络手机版
  • 关注官方公众号
    微信扫一扫关注
    公众号

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

原作者: [db:作者] 来自: [db:来源] 收藏 邀请

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

 

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

]    

两个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 < 10then 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 < 10then 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 = 0then
  
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 = 0then
  
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 <> 0do
  
begin
    
// 检查输入表项是否为ntdll.dll
    dll_name :
= PAnsiChar(hMod + ImportDescriptor^.Name);
    
// 如果不是,则跳到下一个处理
    
if (StrIComp(dll_name, 'ntdll.dll'<> 0then
    
begin
      ImportDescriptor :
= PImageImportDescriptor(DWORD(ImportDescriptor) + SizeOf(_IMAGE_IMPORT_DESCRIPTOR));
      Continue;
    
end;

    ThunkData :
= PImageThunkData(hMod + ImportDescriptor^.CharacteristicsOrOriginalFirstThunk);
    iNum :
= 1;
    
while (ThunkData^.Function_ <> 0do
    
begin
      
// 检查函数是否为NtDeviceIoControlFile
      func_name :
= PAnsiChar(hMod + ThunkData^.AddressOfData + 2);
      
//OutputDebugString(PChar(Format('[HOOK] find API: %s', [StrPas(func_name)])));
      
if (StrIComp(func_name , 'NtDeviceIoControlFile'= 0then
      
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_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;

鲜花

握手

雷人

路过

鸡蛋
该文章已有0人参与评论

请发表评论

全部评论

专题导读
上一篇:
delphi7调用webserviceJava传入参数为空发布时间:2022-07-18
下一篇:
delphi - 获取Teamview ID,口令发布时间:2022-07-18
热门推荐
阅读排行榜

扫描微信二维码

查看手机版网站

随时了解更新最新资讯

139-2527-9053

在线客服(服务时间 9:00~18:00)

在线QQ客服
地址:深圳市南山区西丽大学城创智工业园
电邮:jeky_zhao#qq.com
移动电话:139-2527-9053

Powered by 互联科技 X3.4© 2001-2213 极客世界.|Sitemap