另类挂钩-RING3数据包监视(Delphi版)
标 题: 【原创】另类挂钩-RING3数据包监视
作 者: qihoocom
时 间: 2009-02-01,15:20
链 接: http://bbs.pediy.com/showthread.php?t=81204
闲着无聊,在看雪上看到这个帖子。Hook NtDeviceIoControlFile 果然够另类,于是迅速翻成Delphi的代码,效果还不错:
DebugView输出
关键代码如下:
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.
{
原理: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_*