Entwickler-Ecke

Windows API - Access Violation bei convertierung von PWideChar nach String


Mr_Sven - Mo 11.05.09 12:02
Titel: Access Violation bei convertierung von PWideChar nach String
Hallo Leute,

Habe ein problem mit zwei geHookten WIn API Funktionen (ExtTextOutW und TextOutW).
Ich will von einem Programm die Textausgabe abfangen, da es keine Handles erzeugt, die man auslesen kann.

Also habe ich die beiden funktionen gehookt, leider schmiert das Progamm ab und zu mit einer Access Violation ab.

Vieleicht könnt ihr mit ja helfen:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
function MyExtTextOutW (DC: HDC; X, Y: Integer; Options: Longint; Rect: PRect; Str: PWideChar; Count: Longint; Dx: PInteger): BOOL; stdcall;
var
 st:string;
begin        
 if Count > 0 then
  begin
   log('ExtTextOutW');
   st := Str;              //  <<<---- Access Violation
   if (length(st) >= Count) then
    ProcessText(substr(st,0,Count));
  end;        
 result:= TExtTextOutW(oldExtTextOutW)(dc, x, y, options, rect, str, count, dx);
end;


BenBE - Mo 11.05.09 12:05

Bitte WideString verwenden ...


Mr_Sven - Mo 11.05.09 13:37

ok, habs mal abgeändert, aber der fehler tritt wieder an der selben stelle auf.


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
function MyExtTextOutW (DC: HDC; X, Y: Integer; Options: Longint; Rect: PRect; Str: PWideChar; Count: Longint; Dx: PInteger): BOOL; stdcall;
var
 st:string;
 wst:widestring;
begin
 if Count > 0 then
  begin
   wst := Str;              //  <<<---- Access Violation
   st := WideStringToString(wst,CP_ACP);
   if (length(st) >= Count) then
    ProcessText(substr(st,0,Count));
  end;
 result:= TExtTextOutW(oldExtTextOutW)(dc, x, y, options, rect, str, count, dx);
end;


Delete - Mo 11.05.09 13:46

Und wenn Du Str mal anders benennst?


Mr_Sven - Mo 11.05.09 13:52

Ändert glaube ich nix, habe den funktionsaufruf direkt aus dern WIndows.pas kopiert

Delphi-Quelltext
1:
2:
3:
{$EXTERNALSYM ExtTextOutW}
function ExtTextOutW(DC: HDC; X, Y: Integer; Options: Longint;
  Rect: PRect; Str: PWideChar; Count: Longint; Dx: PInteger): BOOL; stdcall;


Kann ich irgentwie überprüfen ob ich den Pointer zu Str lesen darf oder nicht?


BenBE - Mo 11.05.09 13:55

VirtualQuery oder IsBadReadPtr. Das ist aber keine Lösung, sondern allein ein Mittel zum Debuggen ...


Mr_Sven - Mo 11.05.09 14:07

Also so ungefähr?

werde es erstmal probiern.


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
function MyExtTextOutW (DC: HDC; X, Y: Integer; Options: Longint; Rect: PRect; Str: PWideChar; Count: Longint; Dx: PInteger): BOOL; stdcall;
var
 st:string;
 wst:widestring;
begin
 if Count > 0 then
  begin
   if IsBadReadPtr(Str,count) then
    log('IsBadReadPtr')
   else
    begin
     wst := Str;              //  <<<---- Exception
     st := WideStringToString(wst,CP_ACP);
     if (length(st) >= Count) then
      ProcessText(substr(st,0,Count));
    end;
  end;
 result:= TExtTextOutW(oldExtTextOutW)(dc, x, y, options, rect, str, count, dx);
end;


---Moderiert von user profile iconNarses: Beiträge zusammengefasst---

Funktioniert auch nicht.


Xentar - Mo 11.05.09 14:47

Wie rufst du die denn auf? Vielleicht stimmt da schcon irgendwas nicht.


Mr_Sven - Mo 11.05.09 14:53

So wie ich es gepostet habe


BenBE - Mo 11.05.09 16:07

Ich sehe keine Aufrufe in deinen Posts. Nur Implementierungen.


Mr_Sven - Mo 11.05.09 16:09

Du meinst wie ich die Funktion MyExtTextOutW aufrufe?


Delphi-Quelltext
1:
2:
 HookCode(GetProcAddr(GetModuleHandle('Gdi32.dll'),'ExtTextOutW'),@MyExtTextOutW,oldExtTextOutW);
 HookCode(GetProcAddr(GetModuleHandle('Gdi32.dll'),'TextOutW'),   @MyTextOutW,   oldTextOutW);


Mr_Sven - Di 12.05.09 09:32

Ok, also IsBadStringPtrW funtioniert auch nicht.

Habe jetzt mal WideCharToString genommen und das ganze mal in einem Debugger an geschaut.

Der Fehler tritt in der Prozedur _LStrFromPWChar auf:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
unit System;
.........
procedure _LStrFromPWChar(var Dest: AnsiString; Source: PWideChar);
asm
        XOR     ECX,ECX
        TEST    EDX,EDX
        JE      @@5
        PUSH    EDX
@@0:    CMP     CX,[EDX+0]      //  <<<---- Access Violation
        JE      @@4
        CMP     CX,[EDX+2]
        JE      @@3
        CMP     CX,[EDX+4]
        JE      @@2
        CMP     CX,[EDX+6]
        JE      @@1
        ADD     EDX,8
        JMP     @@0
@@1:    ADD     EDX,2
@@2:    ADD     EDX,2
@@3:    ADD     EDX,2
@@4:    MOV     ECX,EDX
        POP     EDX
        SUB     ECX,EDX
        SHR     ECX,1
@@5:    JMP     _LStrFromPWCharLen
end;


BenBE - Di 12.05.09 09:40

Kannst Du bitte mal mit dem CPU-Fenster schauen, wie die Ursprüngliche Register-Belegung beim Prozedureinsprung ist und was auf dem Stack liegt (obersten 32 Einträge).

Ach ja: Warum machst Du einen Code Hook, statt einem IAT-Hook auf die besagten APIs? Welche Version der uallCollection nutzt Du?


Mr_Sven - Di 12.05.09 10:03

Problem nummer 1: ich kann leider nur die Exeption Reports des gehookten Programms verwenden.
Problem nummer 2: In dem Report sind die ganzen Entrypoints andere, habe ne weile gebraucht bis ich die fehlerhafte stelle im Code gefunden habe.
Problem nummer 3: Der Stack dump der da angezeigt wird ist irgendwie nicht das was ich erwartet habe.

Aber:

Habe den Code ein wenig umgebaut:

Delphi-Quelltext
1:
2:
3:
4:
5:
   log('MyExtTextOutW');
   logW(Str,Count);
   log('MyExtTextOutW 1');
   st := WideCharToString(Str);
   log('MyExtTextOutW 2');


Das Steht in meinem Log File:

Normaler Eintrag ohne Exception:
MyExtTextOutW
b l a h
MyExtTextOutW 1
MyExtTextOutW 2

Eintrag mit Exception:

MyExtTextOutW
b l a h
MyExtTextOutW 1

Also Str kann ich über nen FileStream ohne Exception in eine Datei Schreiben.

Die Version der uallCollection finde ich leider nirgens.

Was meinst du mit IAT-Hook?


BenBE - Di 12.05.09 10:20

IAT = Import Address Table.
Code Hooks waren bei uall die, die den Source umgeschrieben haben, während IAT nur die Import Address Table angepasst haben. IAT sind etwas schonender für das Programm was verbogen wird.


Mr_Sven - Di 12.05.09 10:43

Hm, habe für nen IAT-Hook nicht wirklich nen beispiel gefunden.

Ich bin gerade am überlegen, ob ich die Umwandlung von dem PWideChar in einen String mal testweise über einen Stream mache, aber das ist natürlich ganz schön aufwendig.

oder ob man das irgentwie über inc(pointer) machen kann, so etwa:

Delphi-Quelltext
1:
2:
s := s + char(pointer^);
inc(pointer);


Mr_Sven - Di 12.05.09 11:43

ich glaube ich habe das problem gelößt,
Habe den code mal wie folgt um gebaut:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
function PWideCharToString(pwch:PWideChar;count:integer):string;
var
 i:integer;
begin
 result:='';
 for i:=0 to count - 1 do
  begin
   result:=result+pwch^;
   inc(pwch);
  end;
end;

function MyExtTextOutW (DC: HDC; X, Y: Integer; Options: Longint; Rect: PRect; Str: PWideChar; Count: Longint; Dx: PInteger): BOOL; stdcall;
var
 st:string;
begin
 if Count > 0 then
  begin
   log('MyExtTextOutW');
   logW(Str,Count);
   log('MyExtTextOutW 1');
   st := PWideCharToString(Str,Count);
   log('MyExtTextOutW 2');        
   st := WideCharToString(Str);
   log('MyExtTextOutW 3');
   if (length(st) >= Count) then
    ProcessText(substr(st,0,Count));
  end;
 result:= TExtTextOutW(oldExtTextOutW)(dc, x, y, options, rect, str, count, dx);
end;


Bei der Exception sah das file dann so aus:

MyExtTextOutW
b l a h
MyExtTextOutW 1
MyExtTextOutW 2

Ich lass das jetzt mal eine weile laufen und melde mich wenns klappt


Boldar - Di 12.05.09 15:13

Es gab da mal ein Beispiel eines shellhooks glaube ich, google doch mal danach...

Edit:
Hier wars [http://www.michael-puff.de/Developer/Delphi/Importe/toms/]

(Nur zum Thema, wie man dass sonst noch machen kann, dass geht auch in Richtung IAT-Hook)


Mr_Sven - Di 12.05.09 16:33

So, habe das problem nun gelöst,
ich denke das WideCharToString damit nicht klar kommt, weil der PWideChar Wert in ExtTextOutW und TextOutW nicht mit #0 endet, dadurch kann er die länge nicht bestimmen und läuft manchmal in einen Speicherbereich den er nicht lesen darf.

Da bei ExtTextOutW und TextOutW glücklicherweise die länge des Textes übergeben wird, kann ich das über PWideCharToString(Str,Count) sauber umwandeln.

Gruß Sven