How save the address of original function and call this later?

464 views Asked by At

I'm trying call the original function in a api hook that prevents dll injections by LdrLoadDll() function, but everytime when i try load a dll diferent of that are filtered, the application crashes and not is possible call original function. Seems that i making something wrong when will save the "original_function" before the hook.

I'm testing on Windows 7 x64, injecting a 32bit dll in a 32bit application (code below).

How fix this?

program Project1;

{$APPTYPE CONSOLE}
{$R *.res}

uses
  Windows,
  SysUtils;

type
  NTSTATUS = Cardinal;
  PUNICODE_STRING = ^UNICODE_STRING;

  UNICODE_STRING = packed record
    Length: Word;
    MaximumLength: Word;
    Buffer: PWideChar;
  end;

const
  STATUS_ACCESS_DENIED = NTSTATUS($C0000022);

var
  Old_LdrLoadDll: function(szcwPath: PWideChar; dwFlags: DWORD;
    pUniModuleName: PUNICODE_STRING; pResultInstance: PPointer)
    : NTSTATUS; stdcall;

function LdrLoadDll(szcwPath: PWideChar; dwFlags: DWORD;
  pUniModuleName: PUNICODE_STRING; pResultInstance: PPointer)
  : NTSTATUS; stdcall;
begin
  Result := Old_LdrLoadDll(szcwPath, dwFlags, pUniModuleName, pResultInstance);
end;

procedure PatchCode(Address: Pointer; const NewCode; Size: Integer);
var
  OldProtect: DWORD;
begin
  if VirtualProtect(Address, Size, PAGE_EXECUTE_READWRITE, OldProtect) then
  begin
    Move(NewCode, Address^, Size);
    FlushInstructionCache(GetCurrentProcess, Address, Size);
    VirtualProtect(Address, Size, OldProtect, @OldProtect);
  end;
end;

type
  PInstruction = ^TInstruction;

  TInstruction = packed record
    Opcode: Byte;
    Offset: Integer;
  end;

procedure RedirectProcedure(OldAddress, NewAddress: Pointer);
var
  NewCode: TInstruction;
begin
  NewCode.Opcode := $E9;
  NewCode.Offset := NativeInt(NewAddress) - NativeInt(OldAddress) -
    SizeOf(NewCode);
  PatchCode(OldAddress, NewCode, SizeOf(NewCode));
end;

function NewLdrLoadDll(szcwPath: PWideChar; dwFlags: DWORD;
  pUniModuleName: PUNICODE_STRING; pResultInstance: PPointer)
  : NTSTATUS; stdcall;
begin
  if (pos('111', pUniModuleName.Buffer) > 0) or
    (pos('222', pUniModuleName.Buffer) > 0) then

    Result := STATUS_ACCESS_DENIED
  else
    Result := LdrLoadDll(szcwPath, dwFlags, pUniModuleName, pResultInstance);
end;

begin
  @Old_LdrLoadDll := GetProcAddress(GetModuleHandle('ntdll.dll'), 'LdrLoadDll');
  try
    RedirectProcedure(GetProcAddress(GetModuleHandle('ntdll.dll'),
      'LdrLoadDll'), @NewLdrLoadDll);
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
  Readln;

end.
2

There are 2 answers

2
AudioBubble On BEST ANSWER

Here is one working code made after follow @GolezTrol suggestions:

program Project1;

{$APPTYPE CONSOLE}
{$R *.res}

uses
  Windows,
  SysUtils;

type
  NTSTATUS = Cardinal;
  PUNICODE_STRING = ^UNICODE_STRING;

  UNICODE_STRING = packed record
    Length: Word;
    MaximumLength: Word;
    Buffer: PWideChar;
  end;

const
  STATUS_ACCESS_DENIED = NTSTATUS($C0000022);

var
  Old_LdrLoadDll: function(szcwPath: PWideChar; dwFlags: DWORD;
    pUniModuleName: PUNICODE_STRING; pResultInstance: PPointer)
    : NTSTATUS; stdcall;

function LdrLoadDll(szcwPath: PWideChar; dwFlags: DWORD;
  pUniModuleName: PUNICODE_STRING; pResultInstance: PPointer)
  : NTSTATUS; stdcall;
begin
  Result := Old_LdrLoadDll(szcwPath, dwFlags, pUniModuleName, pResultInstance);
end;

type
  PInstruction = ^TInstruction;

  TInstruction = packed record
    Opcode: Byte;
    Offset: Integer;
  end;

//======= Structure to store original function ======== 

type
  TSaveOriginal = packed record
    Addr: Pointer;
    Bytes: array [0 .. SizeOf(TInstruction)] of Byte;
  end;

  PSaveOriginal = ^TSaveOriginal;

var
  SaveOriginal: TSaveOriginal;

//====================================================

procedure PatchCode(Address: Pointer; const NewCode; Size: Integer;
  SaveOriginal: PSaveOriginal);
var
  OldProtect: DWORD;
begin
  if VirtualProtect(Address, Size, PAGE_EXECUTE_READWRITE, OldProtect) then
  begin
    //======== Saving original function =========
    if Assigned(SaveOriginal) then
    begin
      SaveOriginal^.Addr := Address;
      Move(Address^, SaveOriginal^.Bytes, Size);
    end;
    //===========================================
    Move(NewCode, Address^, Size);
    FlushInstructionCache(GetCurrentProcess, Address, Size);
    VirtualProtect(Address, Size, OldProtect, @OldProtect);
  end;
end;

procedure RedirectProcedure(OldAddress, NewAddress: Pointer);
var
  NewCode: TInstruction;
begin
  NewCode.Opcode := $E9;
  NewCode.Offset := NativeInt(NewAddress) - NativeInt(OldAddress) -
    SizeOf(NewCode);
  PatchCode(OldAddress, NewCode, SizeOf(NewCode), @SaveOriginal);
end;

procedure UndoRedirectProcedure(const SaveOriginal: TSaveOriginal);
var
  OldProtect: Cardinal;
begin
  if not VirtualProtect(SaveOriginal.Addr, SizeOf(TInstruction),
    PAGE_EXECUTE_READWRITE, OldProtect) then
    RaiseLastOSError;
  Move(SaveOriginal.Bytes, SaveOriginal.Addr^, SizeOf(TInstruction));
  if not VirtualProtect(SaveOriginal.Addr, SizeOf(TInstruction), OldProtect,
    OldProtect) then
    RaiseLastOSError;
end;

function NewLdrLoadDll(szcwPath: PWideChar; dwFlags: DWORD;
  pUniModuleName: PUNICODE_STRING; pResultInstance: PPointer)
  : NTSTATUS; stdcall;
begin
  if (pos('111', pUniModuleName.Buffer) > 0) or
     (pos('222', pUniModuleName.Buffer) > 0) then

    Result := STATUS_ACCESS_DENIED
  else
  begin

    UndoRedirectProcedure(SaveOriginal); // Restore original function

    @Old_LdrLoadDll := SaveOriginal.Addr;

    Result := LdrLoadDll(szcwPath, dwFlags, pUniModuleName, pResultInstance); // Call original function

    RedirectProcedure(GetProcAddress(GetModuleHandle('ntdll.dll'),
      'LdrLoadDll'), @NewLdrLoadDll); // Hook again
  end;
end;

begin
  try
    RedirectProcedure(GetProcAddress(GetModuleHandle('ntdll.dll'),
      'LdrLoadDll'), @NewLdrLoadDll); 
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
  Readln;

end.
6
Alex Guteniev On

I suggest that you don't use home-made hooking, and get an existing library for this.

Microsoft Detours is now free and open source. And as it builds to DLLs, you can use it with Delphi.

You will need C++ compiler to build it from source, but then again Visual Studio Community is free.