SSDT_Helper for Delphi v1(Ring3下查看SSDT HOOK的Delphi版)
来源:互联网 发布:现在做淘宝玩吗 编辑:程序博客网 时间:2024/05/20 05:55
SSDT(System Services Descriptor Table) 这几年被讲烂了,各大论坛、Blog相关文章都是成堆成堆的。检测SSDT HOOK的代码也相当多,只不过放眼望去都是C的。实在不忍自己苦苦捍卫的Delphi就此末落(D2009倒是让人振奋了一把),于是把自己感兴趣的几个代码都给翻成了Delphi的,现在拿来共享。
不会驱动,这方面涉水也比较浅,只能写个查看SSDT的小程序。最初是看到 Ring3下用ZwSystemDebugControl获取和恢复SSDT 这篇文章,感觉不错!于是有了这个SSDT_Helper for Delphi,如图:
这是装了 Daemon Tools 后的SSDT,它HOOK了注册表相关的几个函数。0x011C这个是因为装了金山清理专家,忽略之...
代码稍微有点长,具体部分都有注释,我就不多啰唆了:
program SSDT_Helper;
(***************************************************************
*
* SSDT_Helper for Delphi (2009) @Version: 1.0
*
* 通过搜索 SSDT 并和 ZwSystemDebugControl 获取的内容相比较
* 找出不同的SSDT项。
*
* 方法来自《Ring3下用ZwSystemDebugControl获取和恢复SSDT》
* http://blog.csdn.net/DryFisHH/archive/2007/12/29/2002517.aspx
*
* @Author: 木桩 (2009)
***************************************************************)
{$APPTYPE CONSOLE}
uses
SysUtils,
Windows,
Classes,
NtTypes in 'NtTypes.pas';
{ Types }
type
// 用于存储 SSDT 内容的结构
_SSDT_Record = record
Address: DWORD;
OrignAddress: DWORD;
FunctionName: AnsiString;
end;
TSSDT_Record = _SSDT_Record;
PSSDT_Record = ^TSSDT_Record;
TSSDT_Array = array of TSSDT_Record;
var
KernelName: String; // 内核名
KernelBase: DWORD; // 内核基址
RVA_KeServiceDescriptorTable: DWORD; // RVA of KeServiceDescriptorTable
RVA_KiServiceTable: DWORD; // RVA of KiServiceTable
ServiceCount: DWORD; // 条目总数
SSDT: TSSDT_Array;
i, dwCount: Cardinal;
{$REGION '- ImageFixupEntry -'}
// 用于读取 ImageFixupEntry 数据
function _ImageFixupEntry_Type(aFixupEntry: WORD): BYTE;
begin
Result := (aFixupEntry shr 12) and $0F;
end;
function _ImageFixupEntry_Offset(aFixupEntry: WORD): WORD;
begin
Result := aFixupEntry and $0FFF;
end;
{$ENDREGION}
{$REGION '- SSDT Functions -'}
// #define RVATOVA(base,offset) ((PVOID)((DWORD)(base)+(DWORD)(offset)))
function RVA2VA(base, offset: DWORD): DWORD;
begin
Result := base + offset;
end;
{****************************************************************
*
* 根据提供的 ModuleBase 设置DOS头、NT头以及重定位节的指针
*
****************************************************************}
function SetModuleHeaders(ModuleBase: DWORD;
var lpDosHeader: PImageDosHeader; // DOS头
var lpNtHeaders: PImageNtHeaders; // NT头
var lpBaseReloc: PImageBaseRelocation): Boolean;
begin
Result := False;
lpDosHeader := PImageDosHeader(ModuleBase);
//如果DOS头无效
if ( lpDosHeader^.e_magic <> IMAGE_DOS_SIGNATURE ) then
begin
Exit;
end;
lpNtHeaders := PImageNtHeaders(ModuleBase + DWORD(lpDosHeader^._lfanew));
//如果NT头无效
if ( lpNtHeaders^.Signature <> IMAGE_NT_SIGNATURE ) then
begin
Exit;
end;
// 检查重定位节是否存在
if (lpNtHeaders^.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress <> 0)
and (lpNtHeaders^.FileHeader.Characteristics and IMAGE_FILE_RELOCS_STRIPPED = 0) then
begin
lpBaseReloc := PImageBaseRelocation(RVA2VA(ModuleBase, lpNtHeaders^.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress));
Result := True;
end;
end;
{****************************************************************
*
* 通过 ZwQuerySystemInformation(SystemModuleInformation)
* 取内核名称与基址
*
****************************************************************}
function GetKernelNameAndBase(): NTSTATUS;
var
uReturn: ULONG;
pBuffer: PDWORD;
aModule: PSYSTEM_MODULE_INFORMATION;
tmpName: PAnsiChar;
begin
// 取内核名和基址
uReturn := 0;
// 第一次取缓冲区长度
Result := ZwQuerySystemInformation(SystemModuleInformation, @uReturn, 0, @uReturn);
if (uReturn = 0) then
begin
Exit;
end;
pBuffer := AllocMem(uReturn);
try
// 取内容
Result := ZwQuerySystemInformation(SystemModuleInformation, pBuffer, uReturn, nil);
if ( Result <> 0 ) then
begin
Exit;
end;
// 第一个就是内核模块
aModule := PSystemModuleInformation(DWORD(pBuffer) + 4);
tmpName := @aModule^.ImageName[aModule^.ModuleNameOffset];
KernelName := String(StrPas(tmpName)); // 名字
KernelBase := DWORD(aModule^.Base); // 基址
finally
FreeMem(pBuffer);
end;
end;
{****************************************************************
*
* 根据 KeServiceDescriptorTable 的RVA查找 KiServiceTable
× 照搬 《ring3下用ZwSystemDebugControl获取和恢复SSDT》
*
****************************************************************}
function FindKiServiceTable(
hModuleBase,
ImageBase: DWORD;
lpBaseReloc: PImageBaseRelocation;
RVA_KSDT: DWORD): DWORD; // RVA of KeServiceDescriptorTable
var
bFirstChunk: Boolean;
pFixupEntry: PWORD;
i: Cardinal;
dwPointerRva, dwPointsToRva: DWORD;
tmpCode: WORD;
begin
Result := 0;
bFirstChunk := True;
// 1st IMAGE_BASE_RELOCATION.VirtualAddress of ntoskrnl is 0
while ((bFirstChunk) or (lpBaseReloc^.VirtualAddress <> 0)) do
begin
bFirstChunk := False;
pFixupEntry := PWORD(DWORD(lpBaseReloc) + SizeOf(IMAGE_BASE_RELOCATION));
// 读入一个重定位表项
i := 0;
while ( i < ((lpBaseReloc^.SizeOfBlock-SizeOf(IMAGE_BASE_RELOCATION))shr 1) ) do
begin
if ( _ImageFixupEntry_Type(pFixupEntry^) = IMAGE_REL_BASED_HIGHLOW ) then
begin
// 计算RVA
dwPointerRva := lpBaseReloc^.VirtualAddress + _ImageFixupEntry_Offset(pFixupEntry^);
// DONT_RESOLVE_DLL_REFERENCES flag means relocs aren't fixed
dwPointsToRva := PDWORD(hModuleBase+dwPointerRva)^ - ImageBase;
// does this reloc point to KeServiceDescriptorTable.Base?
if (dwPointsToRva = RVA_KeServiceDescriptorTable) then
begin
// check for mov [mem32],imm32. we are trying to find
// "mov ds:_KeServiceDescriptorTable.Base, offset _KiServiceTable"
// from the KiInitSystem.
tmpCode := PWORD(hModuleBase + dwPointerRva-2)^;
//fLogList.add(Format('[%0.8X] KSDT Code: %0.4X = $05c7', [dwPointerRva, tmpCode]));
if (tmpCode = $05C7) then
begin
// should check for a reloc presence on KiServiceTable here
// but forget it
Result := PDWORD(hModuleBase + dwPointerRva + 4)^ - ImageBase;
Exit;
end;
end;
end;
Inc(i);
pFixupEntry := PWORD(DWORD(pFixupEntry) + 2);
end; // end of while ( i < (pbr^.SizeOfBlock-
lpBaseReloc := PImageBaseRelocation(DWORD(lpBaseReloc)+ lpBaseReloc^.SizeOfBlock);
end; // end of while ((bFirstChunk) or
end;
{****************************************************************
*
* 从引出表获取函数名称
*
****************************************************************}
procedure GetExportName(MoudleName: String);
type
PDwordArray = ^TDwordArray;
TDwordArray = array[0..8192] of DWORD;
var
hModuleBase: THandle;
lpDosHeader: PImageDosHeader;
lpNtHeaders: PImageNtHeaders;
lpExportDirectory: PImageExportDirectory;
arrayOfFunctionNames, arrayOfFunctionAddresses: PDwordArray;
arrayOfFunctionOrdinals: PWordArray;
functionOrdinal, functionAddress: DWORD;
i: Cardinal;
funcName: PAnsiChar;
Index: Cardinal;
begin
hModuleBase := GetModuleHandle(PChar(MoudleName));
if (SetModuleHeaders(hModuleBase, lpDosHeader, lpNtHeaders, PImageBaseRelocation(lpExportDirectory))) then
if (lpNtHeaders^.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress <> 0) then
begin
lpExportDirectory := PImageExportDirectory(hModuleBase + lpNtHeaders^.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);
arrayOfFunctionAddresses := PDwordArray(hModuleBase + DWORD(lpExportDirectory^.AddressOfFunctions));
arrayOfFunctionNames := PDwordArray(hModuleBase + DWORD(lpExportDirectory^.AddressOfNames));
arrayOfFunctionOrdinals := PWordArray(hModuleBase + DWORD(lpExportDirectory^.AddressOfNameOrdinals));
for i := 0 to lpExportDirectory^.NumberOfNames - 1 do
begin
funcName := PAnsiChar(hModuleBase + arrayOfFunctionNames[i]);
functionOrdinal := lpExportDirectory^.Base + arrayOfFunctionOrdinals[i] - 1;
functionAddress := hModuleBase + arrayOfFunctionAddresses[functionOrdinal];
if (funcName^ = 'N') and (PAnsiChar(funcName+1)^ = 't') then
begin
Index := PWORD(functionAddress + 1)^;
if (Index > lpExportDirectory^.NumberOfNames) then Continue;
SSDT[Index].FunctionName := StrPas(funcName);
//WriteLn(Format('(%0.4X) %s', [Index, SSDT[Index].FunctionName]));
end;
end;
end;
end;
{$ENDREGION}
{$REGION '- dump SSDT -'}
{****************************************************************
*
* 从内存地址直接读取 SSDT (可能是被HOOK过的)
*
****************************************************************}
procedure BlackSSDT();
var
tmpArray: array of DWORD;
QueryBuff: TMEMORY_CHUNKS;
i: Cardinal;
Ret: NTSTATUS;
begin
// 读取内存中的 SSDT
SetLength(tmpArray, ServiceCount);
QueryBuff.Address := KernelBase + RVA_KiServiceTable;
QueryBuff.Data := tmpArray;
QueryBuff.Length := ServiceCount * 4;
Ret := ZwSystemDebugControl(SysDbgReadVirtual, @QueryBuff, SizeOf(TMEMORY_CHUNKS), nil, 0, @i);
//WriteLn(Format('ZwSystemDebugControl() = %0.8X, Address(%0.8X), Count(%d)', [Ret, QueryBuff.Address, ServiceCount]));
for i := 0 to ServiceCount - 1 do
begin
SSDT[i].Address := tmpArray[i];
end;
end;
{****************************************************************
*
* 读取 SSDT
*
****************************************************************}
procedure GetSSDT();
var
hKernel: THandle;
lpDosHeader: PImageDosHeader;
lpNtHeaders: PImageNtHeaders;
lpBaseReloc: PImageBaseRelocation;
ImageBase, ImageEndSize: DWORD;
pService: PDWORD;
begin
// 取内核基址
if (GetKernelNameAndBase() <> 0) then
begin
WriteLn(Format('ERROR: 取内核基址错误 > [%0.8X] %s', [KernelBase, KernelName]));
Exit;
end;
// DONT_RESOLVE_DLL_REFERENCES方式载入内核准备读取
hKernel := LoadLibraryEx(PChar(KernelName), 0, DONT_RESOLVE_DLL_REFERENCES);
if (hKernel = 0) then
begin
WriteLn(Format('加载 %s 失败,可能是被改了名字。', [KernelName]));
Exit;
end;
try
// 取 KeServiceDescriptorTable RVA
RVA_KeServiceDescriptorTable := DWORD(GetProcAddress(hKernel, 'KeServiceDescriptorTable')) - hKernel;
// 检查文件头
if (SetModuleHeaders(hKernel, lpDosHeader, lpNtHeaders, lpBaseReloc)) then
begin
ImageBase := lpNtHeaders^.OptionalHeader.ImageBase;
// 查找 KiServiceTable
RVA_KiServiceTable := FindKiServiceTable(hKernel, ImageBase, lpBaseReloc, RVA_KeServiceDescriptorTable);
// 根据文件搜索 SSDT 内容
ServiceCount := 0;
SetLength(SSDT, 1000); // 设置一个较大的长度,后面调整
ImageEndSize := ImageBase + lpNtHeaders^.OptionalHeader.SizeOfImage;
pService := PDWORD(hKernel + RVA_KiServiceTable);
while ( pService^ < ImageEndSize ) do
begin
// 导出 SSDT 表项
SSDT[ServiceCount].OrignAddress := pService^-ImageBase + KernelBase;
//WriteLn(Format('(%0.4X) Ori:0x%0.8X', [ServiceCount, SSDT[ServiceCount].OrignAddress]));
Inc(ServiceCount);
pService := PDWORD(DWORD(pService) + 4);
end;
// 确定SSDT大小
SetLength(SSDT, ServiceCount);
end;
except on E: Exception do
WriteLn(Format('ERROR: %s', [E.Message]));
end;
FreeLibrary(hKernel);
end;
{$ENDREGION}
{$REGION '- 附加函数 -'}
// 提升权限
function EnabledDebugPrivilege(const bEnabled: Boolean): Boolean;
var
hToken: THandle;
tp: TOKEN_PRIVILEGES;
a: DWORD;
const
SE_DEBUG_NAME = 'SeDebugPrivilege';
begin
Result := False;
if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, hToken)) then
begin
tp.PrivilegeCount := 1;
LookupPrivilegeValue(nil, SE_DEBUG_NAME, tp.Privileges[0].Luid);
if bEnabled then
tp.Privileges[0].Attributes := SE_PRIVILEGE_ENABLED
else
tp.Privileges[0].Attributes := 0;
a := 0;
AdjustTokenPrivileges(hToken, False, tp, SizeOf(tp), nil, a);
Result := GetLastError = ERROR_SUCCESS;
CloseHandle(hToken);
end;
end;
{$ENDREGION}
begin
try
// 提升权限,不然用BlackSSDT()中的ZwSystemDebugControl无法取到数据
if Not EnabledDebugPrivilege(True) then
begin
WriteLn(Format('SetDebugPrivilege 失败!', []));
end;
// 从文件中读取真实的SSDT
GetSSDT();
// 读取内存中的SSDT,用于对比
BlackSSDT();
// 从ntdll读取导出函数名
GetExportName('ntdll.dll');
// 输出SSDT
WriteLn('SSDT(System Services Descriptor Table)');
WriteLn(Format('KernelBase = 0x%0.8X, KernelName = %s', [KernelBase, KernelName]));
WriteLn('------------------------------------------------------------------');
dwCount := 0;
// 检查参数
if ParamCount <= 0 then
begin
// 列出可疑SSDT
for i := 0 to ServiceCount - 1 do
begin
if (SSDT[i].Address <> SSDT[i].OrignAddress) then
begin
WriteLn(Format('0x%0.4X 0x%0.8X 0x%0.8X %s', [i, SSDT[i].Address, SSDT[i].OrignAddress, SSDT[i].FunctionName]));
Inc(dwCount);
end;
end;
if (dwCount = 0) then
begin
WriteLn('no SSDT Hook.');
end;
end
else if (Pos('-a', ParamStr(1)) > 0) then
begin
// -a 有参数,显示完整SSDT
for i := 0 to ServiceCount - 1 do
begin
if (SSDT[i].Address = SSDT[i].OrignAddress) then
WriteLn(Format('0x%0.4X 0x%0.8X 0x%0.8X %s', [i, SSDT[i].Address, SSDT[i].OrignAddress, SSDT[i].FunctionName]))
else
begin
WriteLn(Format('0x%0.4X 0x%0.8X [0x%0.8X] %s', [i, SSDT[i].Address, SSDT[i].OrignAddress, SSDT[i].FunctionName]));
Inc(dwCount);
end;
end;
if (dwCount = 0) then
begin
WriteLn('no SSDT Hook.');
end;
end;
WriteLn('------------------------------------------------------------------');
ReadLn;
except on E: Exception do
WriteLn('ERROR: ' + E.Message);
end;
end.
完整工程源码:SSDT_Helper_src.rar
可执行文件:SSDT_Helper.rar
这个程序比较简单,r3下用ZwSystemDebugControl读内存,从文件搜SSDT...都是些常见的办法,而且由于只简单比较了一下SSDT地址,这样是检测不出Inline HOOK的。为了能检测Inline Hook,下一步应该比较每个函数头部的10几字节看有没有变化,等有时间了把这个功能给加上吧。
另外,恢复SSDT可以用ZwSystemDebugControl写回从文件读取的原始SSDT,实际就是上面代码里 BlackSSDT() 的逆过程而已。
- SSDT_Helper for Delphi v1(Ring3下查看SSDT HOOK的Delphi版)
- SSDT Hook For Delphi
- HOOK SSDT AND HOOK Shadow SSDT FOR DELPHI
- Delphi实现SSDT Hook
- RING3下SSDT原始地址的获取
- RING3下SSDT原始地址的获取
- ring3下的IAT HOOK
- Ring0 inline hook For Delphi.
- 截取指定程序的网络封包 for delphi hook api
- 转帖 SSDT HOOK拦截远线程的创建(下)
- SSDT HOOK拦截远线程的创建(下)
- DELPHI --HOOK
- HOOK delphi
- HOOK-DELPHI
- 用Delphi在2000和XP/2003下从Ring3进入Ring0的无驱动解决方法
- Delphi Hook的相关问题
- HOOK API Lib 0.1 For Delphi
- HOOK API Lib 0.1 For Delphi
- 运用Detours库hook API
- 一些有用网址合集
- 使用synchronized需要注意的一个问题
- 菜鸟之学习51单片机(四)流水灯的实现
- R的几种编辑器的选择与配置
- SSDT_Helper for Delphi v1(Ring3下查看SSDT HOOK的Delphi版)
- 费茨定律---用户操作代价最小化的基础
- 总帐模块表结构
- Oracle BIEE (Business Intelligence) 11g 11.1.1.6.0 学习(2)RPD资料档案库创建
- Linux(RedHat,Centos)上scrapy详尽安装笔记 【转】
- (12)空间不足,留个亡羊补牢的机会给自己。
- Oracle BIEE (Business Intelligence) 11g 11.1.1.6.0 学习(3)创建一个简单的分析
- 【白书】第一章习题
- android 自定义箭头控件