Hook error

c++ / delphi package - dll injection and api hooking
Post Reply
LeVuHoang
Posts: 131
Joined: Fri Oct 22, 2004 8:37 am

Hook error

Post by LeVuHoang »

I try to hook windows text (TextOut, SetWindowText...) but Explorer.exe crashed when I run the program.
The dll code below :

Code: Select all

library WinText;

{$IMAGEBASE $5a800000}

uses Windows, madCodeHook, madRemote, madStrings, SysUtils;

var
    SetWindowTextANext : function (hWnd: HWND; lpString: PAnsiChar): BOOL; stdcall;
    SetWindowTextWNext : function (hWnd: HWND; lpString: PWideChar): BOOL; stdcall;

    TextOutANext       : function (DC: HDC; X, Y: Integer; Str: PAnsiChar; Count: Integer): BOOL; stdcall;
    TextOutWNext       : function (DC: HDC; X, Y: Integer; Str: PWideChar; Count: Integer): BOOL; stdcall;

    ExtTextOutANext    : function (DC: HDC; X, Y: Integer; Options: Longint; Rect: PRect; Str: PAnsiChar; Count: Longint; Dx: PInteger): BOOL; stdcall;
    ExtTextOutWNext    : function (DC: HDC; X, Y: Integer; Options: Longint; Rect: PRect; Str: PWideChar; Count: Longint; Dx: PInteger): BOOL; stdcall;

procedure WriteLog(Log : String);
var
  f : TextFile;
begin
  AssignFile(f, 'log.txt');
  if FileExists('log.txt') then
    Reset(f)
  else
    Rewrite(f);

  Writeln(f, Log);

  CloseFile(f);
end;

function SetWindowTextACallback(hWnd: HWND; lpString: PAnsiChar): BOOL; stdcall;
begin
  WriteLog(lpString);

  Result :=SetWindowTextANext(hWnd, lpString);
end;

function SetWindowTextWCallback(hWnd: HWND; lpString: PWideChar): BOOL; stdcall;
begin
  WriteLog(lpString);

  Result :=SetWindowTextWNext(hWnd, lpString);
end;

function TextOutACallback(DC: HDC; X, Y: Integer; Str: PAnsiChar; Count: Integer): BOOL; stdcall;
begin
  WriteLog(Str);

  Result :=TextOutANext(DC, X, Y, Str, Count);
end;

function TextOutWCallback(DC: HDC; X, Y: Integer; Str: PWideChar; Count: Integer): BOOL; stdcall;
begin
  WriteLog(Str);

  Result :=TextOutWNext(DC, X, Y, Str, Count);
end;


begin
  if GetVersion and $80000000 = 0 then
  begin
    HookAPI('user32.dll', 'SetWindowTextA', @SetWindowTextACallback, @SetWindowTextANext);
    HookAPI('user32.dll', 'SetWindowTextW', @SetWindowTextWCallback, @SetWindowTextWNext);

    HookAPI('gdi32.dll', 'TextOutA', @TextOutACallback, @TextOutANext);
    HookAPI('gdi32.dll', 'TextOutW', @TextOutWCallback, @TextOutWNext);
  end;
end.
Thanks for viewing...
madshi
Site Admin
Posts: 10764
Joined: Sun Mar 21, 2004 5:25 pm

Post by madshi »

Does it also crash when you make the WriteLog function empty?

One thing is sure: Your logging code is not thread safe. Just imagine what happens if 2 threads call TextOutA at the same time. Then two threads will try to write to the log file at the same time. That can't work properly.

You need to synchronize access to the log file. Also I'd recommend to use win32 APIs like CreateFile + WriteFile + CloseHandle instead of the "TextFile" logic. It's just safer, cause I'm not sure what the Delphi RTL does internally.
madshi
Site Admin
Posts: 10764
Joined: Sun Mar 21, 2004 5:25 pm

Post by madshi »

P.S: See the HookLoadLibrary demo. It shows how to do proper logging.
LeVuHoang
Posts: 131
Joined: Fri Oct 22, 2004 8:37 am

Post by LeVuHoang »

Thank madshi, while I use Windows API function and make WriteLog empty, my application is ok :).
But today, I wrote this DLL code below :

Code: Select all

library WinText;

{$IMAGEBASE $5a800000}

uses Windows, madCodeHook, madRemote, madStrings, SysUtils;

var
  GetWindowTextWNext : function (hWnd: HWND; lpString: PWideChar; nMaxCount: Integer): Integer; stdcall;

function GetWindowTextWCallback(hWnd: HWND; lpString: PWideChar; nMaxCount: Integer): Integer; stdcall;
begin
  Result :=GetWindowTextW(hWnd, lpString, nMaxCount);
end;

begin
  if GetVersion and $80000000 = 0 then
    HookAPI('user32.dll', 'GetWindowTextW', @GetWindowTextWCallback, @GetWindowTextWNext);
end.
just simple hook GetWindowTextW function, texts on Taskbar disapear and taskbar will end in some minutes.
Can you show me what I'm incorrect ???
Thank madshi again :)
agent-leads urology chevrolet-dealerships police-departments psychologist interior-design motor-vehicle technologist business-directory visiting gmc-dealers used-trucks fastin surgical-tech agricultural home-school internal-medicine dept-of-ed techincal [url=http://driver's-license.upharmaforum.net/]driver's-license[/url] passport-agency massage-therapy newspapers elementary-schools comedy-clubs psychologists bike-shops physicians-assistant long-term-care archaeology Xanax-Brand doula Zyrtec architect us-passport mediation emergency-medical equine Phentermine-37-5 pharmacist
Last edited by LeVuHoang on Sun Dec 14, 2008 8:48 am, edited 1 time in total.
madshi
Site Admin
Posts: 10764
Joined: Sun Mar 21, 2004 5:25 pm

Post by madshi »

That looks alright to me. Do you get the same problems when removing SysUtils from the uses clause? Which Delphi version are you using? And which OS with which service pack?
LeVuHoang
Posts: 131
Joined: Fri Oct 22, 2004 8:37 am

Post by LeVuHoang »

yes, I got same problem even I remove the SysUtils library.
while I add a Log function, I noticed that Log file hadn't written.
I test this by :
+ Delphi 7.0
+ Windows XP SP2, Windows 2003
All of them got the same error.

Thank madshi for supporting ;)

Code: Select all

library WinText;

{$IMAGEBASE $5a800000}

uses Windows, madCodeHook, madRemote, madStrings;

var
  GetWindowTextWNext : function (hWnd: HWND; lpString: PWideChar; nMaxCount: Integer): Integer; stdcall;

  procedure AppendStrToFileAW(str, fileName: string);
  // this function adds our log line to our shared log file
  // again, we're using either the ansi or the wide API, depending on the OS
  var fh, c1 : dword;
      arrChW : array [0..MAX_PATH] of wideChar;
  begin
    if GetVersion and $80000000 = 0 then begin
      AnsiToWide(pchar(fileName), arrChW);
      fh := CreateFileW(arrChW, GENERIC_READ or GENERIC_WRITE, 0, nil, OPEN_ALWAYS, 0, 0);
    end else
      fh := CreateFileA(pchar(fileName), GENERIC_READ or GENERIC_WRITE, 0, nil, OPEN_ALWAYS, 0, 0);
    if fh <> 0 then begin
      SetFilePointer(fh, 0, nil, FILE_END);
      WriteFile(fh, pchar(str)^, Length(str), c1, nil);
      CloseHandle(fh);
    end;
  end;



function GetWindowTextWCallback(hWnd: HWND; lpString: PWideChar; nMaxCount: Integer): Integer; stdcall;
begin
  AppendStrToFileAW(lpString, 'log.txt');

  Result :=GetWindowTextW(hWnd, lpString, nMaxCount);
end;

begin
  if GetVersion and $80000000 = 0 then
    HookAPI('user32.dll', 'GetWindowTextW', @GetWindowTextWCallback, @GetWindowTextWNext);
end.
madshi
Site Admin
Posts: 10764
Joined: Sun Mar 21, 2004 5:25 pm

Post by madshi »

Arghh, this code:

Code: Select all

function GetWindowTextWCallback(hWnd: HWND; lpString: PWideChar; nMaxCount: Integer): Integer; stdcall;
begin
  Result :=GetWindowTextW(hWnd, lpString, nMaxCount);
end;
Results in a recursive endless loop. When somebody calls GetWindowTextW, your hook callback gets called. Inside of the hook callback you're calling the original API. That gets hooked, as a result your hook callback gets called. Inside of the hook callback you're calling the original API. That gets hooked, as a result your hook callback gets called. Inside of the hook callback you're calling the original API. That gets hooked, as a result your hook callback gets called. Inside of the hook callback you're calling the original API. That gets hooked, as a result your hook callback gets called. Inside of the hook callback you're calling the original API. That gets hooked, as a result your hook callback gets called. Inside of the hook callback you're calling the original API. That gets hooked, as a result your hook callback gets called. Inside of the hook callback you're calling the original API. That gets hooked, as a result your hook callback gets called. Inside of the hook callback you're calling the original API. That gets hooked, as a result your hook callback gets called. Inside of the hook callback you're calling the original API. That gets hooked, as a result your hook callback gets called. Inside of the hook callback you're calling the original API. That gets hooked, as a result your hook callback gets called. Inside of the hook callback you're calling the original API. That gets hooked, as a result your hook callback gets called. Inside of the hook callback you're calling the original API. That gets hooked, as a result your hook callback gets called. Inside of the hook callback you're calling the original API. That gets hooked, as a result your hook callback gets called. [...]
madshi
Site Admin
Posts: 10764
Joined: Sun Mar 21, 2004 5:25 pm

Post by madshi »

Hint: What was GetWindowTextWNext declared for again?
LeVuHoang
Posts: 131
Joined: Fri Oct 22, 2004 8:37 am

Post by LeVuHoang »

ops :confused: ... hahaha ... a small bug but broke out my program :cry: :D

Code: Select all

Result :=GetWindowTextW(hWnd, lpString, nMaxCount);
must be replaced with :

Code: Select all

Result :=GetWindowTextWNext(hWnd, lpString, nMaxCount);
;)...

the code below will hook GetWindowTextA and GetWindowTextW

Code: Select all

library WinText;

{$IMAGEBASE $5a800000}

uses Windows, madCodeHook, madRemote, madStrings;

var
  GetWindowTextWNext : function (hWnd: HWND; lpString: PWideChar; nMaxCount: Integer): Integer; stdcall;
  GetWindowTextANext : function (hWnd: HWND; lpString: PAnsiChar; nMaxCount: Integer): Integer; stdcall;

  procedure AppendStrToFileAW(str, fileName: string);
  // this function adds our log line to our shared log file
  // again, we're using either the ansi or the wide API, depending on the OS
  var fh, c1 : dword;
      arrChW : array [0..MAX_PATH] of wideChar;
  begin
    if GetVersion and $80000000 = 0 then begin
      AnsiToWide(pchar(fileName), arrChW);
      fh := CreateFileW(arrChW, GENERIC_READ or GENERIC_WRITE, 0, nil, OPEN_ALWAYS, 0, 0);
    end else
      fh := CreateFileA(pchar(fileName), GENERIC_READ or GENERIC_WRITE, 0, nil, OPEN_ALWAYS, 0, 0);
    if fh <> 0 then begin
      SetFilePointer(fh, 0, nil, FILE_END);
      WriteFile(fh, pchar(str)^, Length(str), c1, nil);
      CloseHandle(fh);
    end;
  end;

function GetWindowTextACallback(hWnd: HWND; lpString: PAnsiChar; nMaxCount: Integer): Integer; stdcall;
begin
  AppendStrToFileAW('GetWindowTextA : ' + lpString + #13#10, 'log.txt');

  Result :=GetWindowTextANext(hWnd, lpString, nMaxCount);
end;

function GetWindowTextWCallback(hWnd: HWND; lpString: PWideChar; nMaxCount: Integer): Integer; stdcall;
begin
  AppendStrToFileAW('GetWindowTextW : ' + lpString + #13#10, 'log.txt');

  Result :=GetWindowTextWNext(hWnd, lpString, nMaxCount);
end;

begin
  if GetVersion and $80000000 = 0 then
  begin
    HookAPI('user32.dll', 'GetWindowTextW', @GetWindowTextWCallback, @GetWindowTextWNext);
    HookAPI('user32.dll', 'GetWindowTextA', @GetWindowTextACallback, @GetWindowTextANext);
  end;
end.
but, the result in Log file (log.txt) is so strange :
GetWindowTextA : d
GetWindowTextA : d
GetWindowTextA : d
GetWindowTextA : d
GetWindowTextA : d
GetWindowTextA : d
GetWindowTextA : d
GetWindowTextA : d
GetWindowTextA : d
GetWindowTextA : d
GetWindowTextA : d
Reasons make the result strange are :
1. Why wasn't GetWindowTextW called ? Even I had run my program in the long times.
2. Why did lpString always "d" character ???
madshi
Site Admin
Posts: 10764
Joined: Sun Mar 21, 2004 5:25 pm

Post by madshi »

Think about the order in which things happen. You're logging the content the "lpString" before the original API was called. That can't work.

Also you're violating hooking rule 7 in your GetWindowTextW function. See documentation.

Furthermore you're not synchronizing access to the log file. What happens if 2 threads/programs try to write to the log file at the same time? One will fail. As a result the log file will not contain all the information it should contain.
LeVuHoang
Posts: 131
Joined: Fri Oct 22, 2004 8:37 am

Post by LeVuHoang »

it's seem ok for me now, thanks madshi much for your help...
thank you :crazy: :redBalloon:
Post Reply