Friday, September 16, 2011

Hook ntdll!NtDeviceIoControlFile to capture network packets

unit uNtDeviceIoControl;interfaceuses  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();implementationfunction NT_SUCCESS(Status: NTSTATUS): BOOL;begin  //Result := Status >= 0;  Result := Status < $80000000;end;//////////////////////////////////////////////////////////////////////////////// LookupSendPacket////// Check Send Packets/// Currently implements filter http requests(GET AND POST)/////////////////////////////////////////////////////////////////////////////function LookupSendPacket(Buffer: Pointer; Len: DWORD): Boolean;begin  Result := False;  // drop small packets  if (Len < 10) then Exit;  // check if it is GET or POST.  if ( CompareMem(Buffer, @HTTP_GET[1], 4)    or CompareMem(Buffer, @HTTP_POST[1], 4) ) then  begin    Result := True;  end;end;//////////////////////////////////////////////////////////////////////////////// LookupRecvPacket////// Check Recv Packets/// Currently implements filter http response packets./////////////////////////////////////////////////////////////////////////////////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 functions }//////////////////////////////////////////////////////////////////////////////// The hooked NtDeviceIoControlFile function./// "send" & "recv" functions in ws2_32.dll finally will call functions in mswsock.dll,/// & mswsock.dll will call NtDeviceIoControl to send "send" or "recv" commands to TDI Client driver./// So if we hook it here, we can filter all tcp packets.//////////////////////////////////////////////////////////////////////////////// Compatibility: NT3, NT4, W2K, WXP, 2K3function 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  // call the original function first.  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;  // if original function failed  if (Not NT_SUCCESS(Result)) then  begin    Exit;  end;  // check whether it is TCP send or recv.  if (IoControlCode <> AFD_SEND)    & (IoControlCode <> AFD_RECV) then  begin    Exit;  end;  try    // obtain Buffer & Len from InputBuffer    AfdInfo := PAFD_INFO(InputBuffer);    Buffer := AfdInfo.BufferArray.buf;    Len := AfdInfo.BufferArray.len;    case IoControlCode of      AFD_SEND:        if ( LookupSendPacket(Buffer, Len) ) then        begin          // output packet content.          OutputDebugString(PChar(Format('[HTTP Send] Length = %d', [Len])));          OutputDebugString(PChar(Format('%s', [StrPas(Buffer)])));        end;      AFD_RECV:        if ( LookupRecvPacket(Buffer, Len) ) then        begin          // output packet content.          OutputDebugString(PChar(Format('[HTTP Recv] Length = %d', [Len])));          OutputDebugString(PChar(Format('%s', [StrPas(Buffer)])));        end;    end;  except  end;end;////////////////////////////////////////////////////////////////////////////////  Setup hook to Ntdll!NtDeviceIoControlFile in mswsock.dll import table/////////////////////////////////////////////////////////////////////////////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  hMod := LoadLibrary('mswsock.dll');  if (hMod = 0) then  begin    OutputDebugString(PChar(Format('LoadLibrary(%s)失败!', ['mswsock.dll'])));    Exit;  end;  // obtain DOS header  pDosHeader := PImageDosHeader(hMod);  if ( pDosHeader^.e_magic <> IMAGE_DOS_SIGNATURE ) then  begin    Exit;  end;  // obtain NT header  pNtHeaders := PImageNtHeaders(hMod + DWORD(pDosHeader^._lfanew));  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;  ImportDescriptor := PImageImportDescriptor(hMod + pNtHeaders^.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);  while (ImportDescriptor^.FirstThunk <> 0) do  begin    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      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)])));        //        // if found NtDeviceIoControlFile, save original function address.        // then setup our hook function address.        //        // ID	 RVA	 Offset  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.

No comments:

Post a Comment