Skip to content

Commit

Permalink
Improve IAT reconstruction w.r.t. forwarded ole32 APIs
Browse files Browse the repository at this point in the history
combase is not available on XP and older systems, so binaries don't run
there if the forwards aren't detected and replaced with ole32.
  • Loading branch information
Hendi48 committed Dec 27, 2021
1 parent 45799d9 commit cd34239
Show file tree
Hide file tree
Showing 3 changed files with 42 additions and 37 deletions.
4 changes: 1 addition & 3 deletions Debugger.pas
Original file line number Diff line number Diff line change
Expand Up @@ -731,9 +731,7 @@ function TDebugger.OnSoftwareBreakpoint(var DebugEv: TDebugEvent): DWORD;
FGuardStart := FImageBase + FPESections[0].VirtualAddress;
FGuardEnd := FImageBase + FBaseOfData;
VirtualProtectEx(FProcess.hProcess, Pointer(FGuardStart), FGuardEnd - FGuardStart, PAGE_NOACCESS, OldProt);

if AncientVer then
Log(ltInfo, 'Please wait, API call tracing in progress...');
Log(ltInfo, 'Please wait, call site tracing might take a while...');

FFirstRealAPI := GetProcAddress(mK32, 'GetSystemTimeAsFileTime');
//FFirstRealAPI := GetProcAddress(mK32, 'HeapCreate');
Expand Down
73 changes: 39 additions & 34 deletions Dumper.pas
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ TDumper = class
procedure CollectNTFwd; overload;
procedure CollectForwards(Fwds: TForwardDict; hModReal, hModScan: HMODULE); overload;
procedure GatherModuleExportsFromRemoteProcess(M: PRemoteModule);
function GetLocalProcAddr(hModule: HMODULE; ProcName: PAnsiChar): Pointer;
function RPM(Address: NativeUInt; Buf: Pointer; BufSize: NativeUInt): Boolean;
public
constructor Create(const AProcess: TProcessInformation; AImageBase, AOEP, AIAT: UIntPtr);
Expand All @@ -57,8 +58,6 @@ constructor TDumper.Create(const AProcess: TProcessInformation; AImageBase, AOEP
if FIAT > $70000000 then
raise Exception.Create('Wrong IAT address');

//allocconsole;

if Win32MajorVersion > 5 then
begin
FUsrPath := PChar(ExtractFilePath(ParamStr(0)) + 'mmusr32.dll');
Expand Down Expand Up @@ -104,6 +103,7 @@ procedure TDumper.CollectForwards(Fwds: TForwardDict; hModReal, hModScan: HMODUL
a: PCardinal;
Fwd: PAnsiChar;
hMod: HMODULE;
ProcAddr: Pointer;
begin
if hModScan = 0 then
hModScan := hModReal;
Expand All @@ -115,13 +115,15 @@ procedure TDumper.CollectForwards(Fwds: TForwardDict; hModReal, hModScan: HMODUL
begin
Fwd := PAnsiChar(ModScan + a^); // e.g. NTDLL.RtlAllocateHeap
Posi := Pos(AnsiString('.'), Fwd);
if (Length(Fwd) in [10..60]) and (((Posi > 0) and (Posi < 15)) or (Pos(AnsiString('api-ms-win'), Fwd) > 0)) and (Pos(AnsiString('.#'), Fwd) = 0) then
if (Length(Fwd) in [10..90]) and (((Posi > 0) and (Posi < 15)) or (Pos(AnsiString('api-ms-win'), Fwd) > 0)) and (Pos(AnsiString('.#'), Fwd) = 0) then
begin
hMod := GetModuleHandleA(PAnsiChar(Copy(Fwd, 1, Posi - 1)));
if hMod > 0 then
begin
Fwds.AddOrSetValue(GetProcAddress(hMod, PAnsiChar(Copy(Fwd, Posi + 1, 50))), PByte(hModReal) + a^);
//Log(ltInfo, Format('%s @ %p', [PAnsiChar(Copy(Fwd, Posi + 1, 50)), GetProcAddress(hMod, PAnsiChar(Copy(Fwd, Posi + 1, 50)))]));
// Not using the normal GetProcAddress because it can return apphelp hooks (e.g., CoCreateInstance when running as admin)
ProcAddr := GetLocalProcAddr(hMod, PAnsiChar(Copy(Fwd, Posi + 1, 50)));
Fwds.AddOrSetValue(ProcAddr, PByte(hModReal) + a^);
//Log(ltInfo, Format('%s @ %p', [PAnsiChar(Copy(Fwd, Posi + 1, 50)), ProcAddr]));
end;
end;
Inc(a);
Expand Down Expand Up @@ -178,7 +180,6 @@ function TDumper.Process: TPEHeader;
Section, Strs, RangeChecker: PByte;
Descriptors: PImageImportDescriptor;
ImportSect: PPESection;
NotZero: Boolean;
begin
// Read header from memory
GetMem(Section, $1000);
Expand Down Expand Up @@ -225,7 +226,7 @@ function TDumper.Process: TPEHeader;
repeat
if ME.hModule <> FImageBase then
begin
//Writeln(IntToHex(ME.hModule, 8), ' : ', ME.modBaseSize, ' : ', string(ME.szModule));
//Log(ltInfo, IntToHex(ME.hModule, 8) + ' : ' + IntToHex(ME.modBaseSize, 4) + ' : ' + string(ME.szModule));
New(RM);
RM.Base := ME.modBaseAddr;
RM.EndOff := ME.modBaseAddr + ME.modBaseSize;
Expand All @@ -249,11 +250,11 @@ function TDumper.Process: TPEHeader;
end
else
begin
// Some kernel32-function are forwarded to ntdll - restore the original address
// Some kernel32 functions are forwarded to ntdll - restore the original address
if FForwards.TryGetValue(a^, Fwd) then
a^ := Fwd
;//else if FForwardsOle32.TryGetValue(a^, Fwd) then
// a^ := Fwd;
else if FForwardsOle32.TryGetValue(a^, Fwd) then
a^ := Fwd;
RangeChecker := a^;
end;

Expand All @@ -268,6 +269,7 @@ function TDumper.Process: TPEHeader;

Addresses.Add(RM.Name, TList<PPointer>.Create);
DLLNames.Add(RM.Name);
//Log(ltInfo, Format('Adding %s because of %p', [RM.Name, a^]));
end;

if RM.ExportTbl.ContainsKey(a^) then
Expand All @@ -281,7 +283,7 @@ function TDumper.Process: TPEHeader;
Inc(a);
end;

ImportSect := PE.CreateSection('.import', $3000);
ImportSect := PE.CreateSection('.import', $1000);

Section := AllocMem(ImportSect.Header.SizeOfRawData);
Pointer(Descriptors) := Section; // Map the Descriptors array to the start of the section
Expand Down Expand Up @@ -314,7 +316,7 @@ function TDumper.Process: TPEHeader;
FillChar((Section + ImportSect.Header.SizeOfRawData - $1000)^, $1000, 0);
Strs := Section + Diff;
Pointer(Descriptors) := Section;
Log(ltInfo, 'Increased import section size to ' + IntToHex(ImportSect.Header.SizeOfRawData, 4));
//Log(ltInfo, 'Increased import section size to ' + IntToHex(ImportSect.Header.SizeOfRawData, 4));
end;
end;
end;
Expand All @@ -326,33 +328,12 @@ function TDumper.Process: TPEHeader;
Size := DLLNames.Count * SizeOf(TImageImportDescriptor);
end;

while ImportSect.Header.SizeOfRawData > $1000 do
begin
NotZero := False;
for i := ImportSect.Header.SizeOfRawData - $1000 to ImportSect.Header.SizeOfRawData - 1 do
if Section[i] <> 0 then
begin
NotZero := True;
Break;
end;

if not NotZero then
begin
Dec(ImportSect.Header.SizeOfRawData, $1000);
Dec(ImportSect.Header.Misc.VirtualSize, $1000);
Dec(PE.NTHeaders.OptionalHeader.SizeOfImage, $1000);
ReallocMem(ImportSect.Data, ImportSect.Header.SizeOfRawData);
end
else
Break;
end;

Pointer(Descriptors) := nil;
DLLNames.Free;
Addresses.Free;
for RM in Modules.Values do
begin
FreeAndNil(RM.ExportTbl);
RM.ExportTbl.Free;
Dispose(RM);
end;
Modules.Free;
Expand Down Expand Up @@ -394,6 +375,30 @@ procedure TDumper.GatherModuleExportsFromRemoteProcess(M: PRemoteModule);
FreeMem(Exp);
end;

function TDumper.GetLocalProcAddr(hModule: HMODULE; ProcName: PAnsiChar): Pointer;
var
Exp: PImageExportDirectory;
Off: PByte;
a, n: PCardinal;
o: PWord;
i: Integer;
begin
with PImageNtHeaders(hModule + Cardinal(PImageDosHeader(hModule)._lfanew)).OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT] do
begin
Exp := Pointer(hModule + VirtualAddress);
Off := PByte(Exp) - VirtualAddress;
end;

Pointer(a) := Off + Exp.AddressOfFunctions;
Pointer(n) := Off + Exp.AddressOfNames;
Pointer(o) := Off + Exp.AddressOfNameOrdinals;
for i := 0 to Exp.NumberOfNames - 1 do
if AnsiStrComp(PAnsiChar(Off + n[i]), ProcName) = 0 then
Exit(Pointer(hModule + a[o[i]]));

Result := nil;
end;

function TDumper.RPM(Address: NativeUInt; Buf: Pointer; BufSize: NativeUInt): Boolean;
begin
Result := ReadProcessMemory(FProcess.hProcess, Pointer(Address), Buf, BufSize, BufSize);
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,5 @@ Functions:
* Shrink: Deletes all sections that are no longer needed (if you unvirtualized or if your binary does not use virtualization).

Note: The tool focuses on cleanness of the resulting binaries. Things such as VM anti-dump are explicitly *not* fixed.

Important: Never activate any compatibility mode options for Magicmida or for the target you're unpacking. It would very likely screw up the unpacking process due to shimming.

0 comments on commit cd34239

Please sign in to comment.