A forum for reverse engineering, OS internals and malware analysis 

Forum for discussion about user-mode development.
 #32794  by pointer
 Wed Apr 10, 2019 11:21 pm
I have the following code able to hook EnumWindows() function and works fine. But target application (3rd's application) crash when the dll is injected. Then this could be solved, for example, making this code prevent the target application enumerate only all windows of my software and then call original EnumWindows() function to enumerate others windows?
Code: Select all
library Project1;

uses
  Windows,
  SysUtils,
  Classes,
  Patch;

{$R *.res}

var
  ThreadId: LongWord;
  ThreadHandle: Integer;

function ThreadFunc(Parameter: Pointer): Integer;
begin
  PatchEnumWindows(True);
  Result := 0;
end;

procedure DLLEntryPoint(dwReason: DWORD);
begin
  case dwReason of
    DLL_PROCESS_ATTACH:
      begin
        ThreadHandle := BeginThread(nil, 0, @ThreadFunc, nil, 0, ThreadId);
      end;

  end;
end;

begin
  DllProc := @DLLEntryPoint;
  DLLEntryPoint(DLL_PROCESS_ATTACH);

end.
----------------------------------------------------------------------------------------------------------------------------------------------
Code: Select all
unit Patch;

interface

procedure PatchEnumWindows(Patch: Boolean);

implementation

uses SysUtils, SyncObjs, Windows;

const
  INSTR_SIZE = 6;

var
  OldEnumWindows: array [0 .. INSTR_SIZE - 1] of Byte;
  EnumWindowsPatched: Boolean = False;

function PatchedEnumWindows(EnumWindowsProc: Pointer; Param: Pointer)
  : BOOL; stdcall;
begin
  // You have to replace original EnumWindowsProc to yourself
  Result := True;
end;

procedure ApiRedirect(OrigFunction, NewFunction: Pointer; var Old);
const
  TEMP_JMP: array [0 .. INSTR_SIZE - 1] of Byte = ($E9, $90, $90, $90,
    $90, $C3);
var
  JmpSize: DWORD;
  JMP: array [0 .. INSTR_SIZE - 1] of Byte;
  OldProtect: DWORD;
begin
  Move(TEMP_JMP, JMP, INSTR_SIZE);
  JmpSize := DWORD(NewFunction) - DWORD(OrigFunction) - 5;
  if not VirtualProtect(LPVOID(OrigFunction), INSTR_SIZE,
    PAGE_EXECUTE_READWRITE, OldProtect) then
    raise Exception.CreateFmt('%s', [SysErrorMessage(GetLastError)]);
  Move(OrigFunction^, Old, INSTR_SIZE);
  Move(JmpSize, JMP[1], 4);
  Move(JMP, OrigFunction^, INSTR_SIZE);
  VirtualProtect(LPVOID(OrigFunction), INSTR_SIZE, OldProtect, nil);
end;

procedure PatchEnumWindows(Patch: Boolean);
var
  OrigEnumWindows: Pointer;
begin
  if Patch <> EnumWindowsPatched then
  begin
    OrigEnumWindows := GetProcAddress(GetModuleHandle('user32.dll'),
      'EnumWindows');
    if Patch then
    begin
      ApiRedirect(OrigEnumWindows, @PatchedEnumWindows, OldEnumWindows);
    end
    else
    begin
      Move(OldEnumWindows, OrigEnumWindows, INSTR_SIZE);
    end;
    EnumWindowsPatched := Patch;
  end;
end;

end.
I hope that you can help me :)
 #32830  by Brock
 Tue Apr 16, 2019 12:42 pm
Why are you creating a thread in DLLMain just to patch an API? Do this directly in DLLMain and get rid of your handle leaking BeginThread call. Threads also don't run until DLLMain is complete because loader lock is held, so no sense in delaying the patch either. Also, inside the ugly patch routine at the very least check result of first VirtualProtect call and then after re-protect VirtualProtect call use FlushInstructionCache. Plenty of good Delphi examples of this online even