[DUG] DLL memory leaks

Paul Heinz paul at accredo.co.nz
Mon Aug 8 09:45:59 NZST 2011


Ross wrote: 

> I didn't know that was possible.  I'll investigate.

PE Explorer is probably one of the best tools for this. It's also
written in Delphi I believe.
You can download a fully functional 30-day trial version.
http://www.heaventools.com/download-pe-explorer.htm

Alternatively, if you want to point me at the DLL, I can have a look at
it's import table for you.

Import Address Table (or IAT) patching is a pretty standard albeit
'low-level' technique. 

If you look for sample code around the web, it's often more involved
that it needs to be since it's often written in the context of injecting
DLLs into other processes you don't control to patch their main import
table.

Since you'll be patching the import table of a DLL you've loaded into
your own process, it's much simpler.

Here's the routine I use. It's using HInstance rather than the module
handle for a loaded DLL since I used for patching the main executable's
import table. You'll want to pass the module handle of your DLL by
calling GetModuleHandle. It returns the current IAT entry so you can
easily forward chain.

There's a whole bunch of stanard PE table record type definitions this
code depends on which I've left out.

function InstallWin32Hook(const ModuleName, FuncName: string; HookFunc:
Pointer): Pointer;
var
  NTHeader: PImageNTHeaders;
  ImportDesc: PImageImportDescriptor;
  Thunk: PImageThunkData;
begin
  Result := GetProcAddress(GetModuleHandle(PChar(ModuleName)),
PChar(FuncName));
  if Result <> nil then
    begin
      NTHeader := PImageNTHeaders(DWord(HInstance) +
PImageDosHeader(HInstance).e_lfanew);
      ImportDesc := PImageImportDescriptor(DWord(HInstance) +
 
NTHeader.OptionalHeader.DataDirectory[ImageDirectoryEntryImport].Virtual
Address);

      while ImportDesc.Name <> 0 do begin
        if StriComp(PAnsiChar(DWord(HInstance) + ImportDesc.Name),
PAnsiChar(AnsiString(ModuleName))) = 0 then
          begin
            Thunk := PImageThunkData(DWord(HInstance) +
DWord(ImportDesc.FirstThunk));
            while Thunk.Funct <> nil do begin
              if Thunk.Funct = Result then
                Thunk.Funct := HookFunc;
              Inc(Thunk)
          end
        end;
        Inc(ImportDesc)
      end
    end
end;

And here's how its used with a sample shim:

var
  OldRaiseException: procedure (dwExceptionCode, dwExceptionFlags,
nNumberOfArguments: DWord;
    const lpArguments: DWord); stdcall;

OldRaiseException := InstallWin32Hook(Kernel32, 'RaiseException',
@HookRaiseException);

procedure HookRaiseException(dwExceptionCode, dwExceptionFlags,
nNumberOfArguments: DWord;
  const lpArguments: DWord); stdcall;
begin
  {
    If this is a real Delphi exception and has the arguments as we
expect from
    System._RaiseExcept then we can crack the argument structure and
determine
    if we need to take a stack trace. Note that this may change from
version to
    version of Delphi.
  }
  if (dwExceptionCode = cDelphiException) and (lpArguments <> 0) and
(nNumberOfArguments = 7) and
      not InHookRaise then
    with PDelphiExceptionArguments(lpArguments)^ do
      try
        InHookRaise := True;
        if not IgnoreException(ExceptObject) then
          HookStackTrace(ExceptAddress, EBP, ESP, False);
      finally
        InHookRaise := False;
      end;

  if Assigned(OldRaiseException) then
    OldRaiseException(dwExceptionCode, dwExceptionFlags,
nNumberOfArguments, lpArguments);
end;

Cheers,
  Paul.



More information about the Delphi mailing list