Virtual-File-System/VfsApiDigger.pas

164 lines
5.2 KiB
ObjectPascal

unit VfsApiDigger;
(*
Description: Provides means for detecting real WinAPI functions addresses, bypassing proxy dlls and
other low level code routines.
*)
(***) interface (***)
uses
SysUtils, Windows,
Utils, DataLib, PatchForge;
(* Determines real exported API addresses for all specified DLL handles. If DLL imports function
with the same name, as the exported one, then imported one is treated as real function.
Example: kernel32.ReadProcessMemory can be a bridge to imported kernelbase.ReadProcessMemory.
If DLL handle was processed earlier, it's skipped *)
procedure FindOutRealSystemApiAddrs (const DllHandles: array of integer);
(* Returns real code address, bypassing possibly nested simple redirection stubs like JMP [...] or JMP XXX. *)
function GetRealAddress (CodeOrRedirStub: pointer): {n} pointer;
(* Enhanced version of kernel32.GetProcAddress, traversing bridge chains and using info, gained by FindOutRealSystemApiAddrs earlier *)
function GetRealProcAddress (DllHandle: integer; const ProcName: string): {n} pointer;
(***) implementation (***)
var
(* Map of DLL handle => API name => Real api address *)
{O} DllRealApiAddrs: {O} TObjDict {OF TDict};
procedure FindOutRealSystemApiAddrs (const DllHandles: array of integer);
const
PE_SIGNATURE_LEN = 4;
type
PImageImportDirectory = ^TImageImportDirectory;
TImageImportDirectory = packed record
RvaImportLookupTable: integer;
TimeDateStamp: integer;
ForwarderChain: integer;
RvaModuleName: integer;
RvaImportAddressTable: integer;
end;
PHintName = ^THintName;
THintName = packed record
Hint: word;
Name: array [0..MAXLONGINT - 5] of char;
end;
var
ImportDirInfo: PImageDataDirectory;
ImportDir: PImageImportDirectory;
ImportLookupTable: Utils.PEndlessIntArr;
ImportAddrTable: Utils.PEndlessIntArr;
DllApiRedirs: {U} TDict {of pointer};
DllHandle: integer;
i, j: integer;
begin
ImportDirInfo := nil;
ImportDir := nil;
ImportLookupTable := nil;
ImportAddrTable := nil;
DllApiRedirs := nil;
// * * * * * //
for i := 0 to high(DllHandles) do begin
DllHandle := DllHandles[i];
ImportDirInfo := @PImageOptionalHeader(DllHandle + PImageDosHeader(DllHandle)._lfanew + PE_SIGNATURE_LEN + sizeof(TImageFileHeader)).DataDirectory[1];
DllApiRedirs := DllRealApiAddrs[Ptr(DllHandle)];
if DllApiRedirs = nil then begin
DllApiRedirs := DataLib.NewDict(NOT Utils.OWNS_ITEMS, DataLib.CASE_SENSITIVE);
DllRealApiAddrs[Ptr(DllHandle)] := DllApiRedirs;
// Found valid import directory in Win32 PE
if ((ImportDirInfo.Size > 0) and (ImportDirInfo.VirtualAddress <> 0)) then begin
ImportDir := pointer(DllHandle + integer(ImportDirInfo.VirtualAddress));
while ImportDir.RvaImportLookupTable <> 0 do begin
ImportLookupTable := pointer(DllHandle + ImportDir.RvaImportLookupTable);
ImportAddrTable := pointer(DllHandle + ImportDir.RvaImportAddressTable);
j := 0;
while (j >= 0) and (ImportLookupTable[j] <> 0) do begin
if ImportLookupTable[j] > 0 then begin
DllApiRedirs[pchar(@PHintName(DllHandle + ImportLookupTable[j]).Name)] := Ptr(ImportAddrTable[j]);
end;
Inc(j);
end;
Inc(ImportDir);
end; // .while
end; // .if
end; // .if
end; // .for
end; // .procedure FindOutRealSystemApiAddrs
function GetRealAddress (CodeOrRedirStub: pointer): {n} pointer;
const
MAX_DEPTH = 100;
var
Depth: integer;
begin
{!} Assert(CodeOrRedirStub <> nil);
result := CodeOrRedirStub;
Depth := 0;
while Depth < MAX_DEPTH do begin
// JMP DWORD [PTR]
if pword(result)^ = PatchForge.OPCODE_JMP_PTR_CONST32 then begin
result := ppointer(integer(result) + sizeof(word))^;
// JXX SHORT CONST8
end else if PatchForge.IsShortJumpConst8Opcode(pbyte(result)^) then begin
result := pointer(integer(result) + sizeof(byte) + pshortint(integer(result) + sizeof(byte))^);
// JMP NEAR CONST32
end else if pbyte(result)^ = PatchForge.OPCODE_JMP_CONST32 then begin
result := pointer(integer(result) + sizeof(PatchForge.TJumpCall32Rec) + pinteger(integer(result) + sizeof(byte))^);
// JXX (conditional) NEAR CONST32
end else if PatchForge.IsNearJumpConst32Opcode(pword(result)^) then begin
result := pointer(integer(result) + sizeof(word) + sizeof(integer) + pinteger(integer(result) + sizeof(word))^);
// Regular code
end else begin
break;
end; // .else
Inc(Depth);
end; // .while
end; // .function GetRealAddress
function GetRealProcAddress (DllHandle: integer; const ProcName: string): {n} pointer;
var
{Un} DllApiRedirs: {U} TDict {OF pointer};
begin
DllApiRedirs := DllRealApiAddrs[Ptr(DllHandle)];
result := nil;
// * * * * * //
if DllApiRedirs <> nil then begin
result := DllApiRedirs[ProcName];
end;
if result = nil then begin
result := Windows.GetProcAddress(DllHandle, pchar(ProcName));
end;
if result <> nil then begin
result := GetRealAddress(result);
end;
end; // .function GetRealProcAddress
begin
DllRealApiAddrs := DataLib.NewObjDict(Utils.OWNS_ITEMS);
end.