Entwickler-Ecke

Windows API - hook problem...


mtin - Fr 27.05.05 19:03
Titel: hook problem...
ich hoffe mal das ist hier so halbwegs richtig...

also ich hatte vor sowas wie einen leetspeak-converter zu schreiben, den man anmachen kann und dann normal mit der tastatur in jedem programm schreibt und das wird in echtzeit übersetzt(h gedrückt->schreibe |-|).
Dazu hab ich mir zuerst mal nen paar hook tutorials angeschaut, und mir das von

http://delphi.about.com/library/bluc/text/uc063001a.htm

runtergeladen. Nachdem ich das dann in Delphi importiert und Compiliert hatte, ging der LocalHook erstmal gut, nur der Global Hook lieferte mir jeden Tastendruck als 4 Buchstaben (TTTTEEEESSSSTTTT), außerdem ging er scheinbar nur wenn das Programm angewählt war. So wie ich das aber verstanden habe müsste ein Global Hook IMMER funktionieren und nicht nur wenn das Programm im Vordergrund ist. :roll:
Ich bin jetzt also so weit das meine Buchstaben in einer log.txt gespeichert werden, allerdings nur wenn das prog angewählt ist.
Wie komm ich jetzt weiter?
(erstmal das das wirklich global funktioniert, dann das nich der buchstabe den ich drücke ankommt sondern meine 1337 übersetzung)

hoffentlich frag ich nich zuviel :oops:

hier noch der code der dll:


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:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
library HookDll;


uses
  SysUtils,
  Classes,windows;

var CurrentHook: HHook;
    KeyArray: array[0..19of byte;
    KeyArrayPtr: integer;
    CurFile: file of byte;
{
GlobalKeyboardHook
------------
This is the hook procedure to be loaded from hooks.exe when you
try and create a global hook. It is similar in structure to that defined
in hook.dpr for creating a local hook, but this time it does not beep!
Instead it stores each key pressed in an array of bytes (20 long). Whenever
this array gets full, it writes it to a file, log.txt and starts again.
}

function GlobalKeyBoardHook(code: integer; wParam: word; lParam: longword): longword; stdcall;
begin
    if code<0 then begin  //if code is <0 your keyboard hook should always run CallNextHookEx instantly and
       GlobalKeyBoardHook:=CallNextHookEx(CurrentHook,code,wParam,lparam); //then return the value from it.
       Exit;
    end;
    //firstly, is the key being pressed, and is it between A and Z
    //note that wParam contains the scan code of the key (which for a-z is the same as the ascii value)
    if ((lParam and KF_UP)=0and (wParam>=65and (wParam<=90then begin
         //store the keycode in the list of keys pressed and increase the pointer
         KeyArray[KeyArrayPtr]:=wParam;
         KeyArrayPtr:=KeyArrayPtr+1;
         //if 20 keys have been recorded, save them to log.txt and start again
         if KeyArrayPtr>19 then begin
            assignfile(CurFile,'log.txt');
            if fileexists('log.txt')=false then rewrite(CurFile) else reset(CurFile); //if log.txt exists, add to it, otherwise recreate it
            blockwrite(CurFile,KeyArray[0],20);
            closefile(CurFile);
            KeyArrayPtr:=0;
         end;
    end;
    CallNextHookEx(CurrentHook,code,wParam,lparam);  //call the next hook proc if there is one
    GlobalKeyBoardHook:=0//if GlobalKeyBoardHook returns a non-zero value, the window that should get
                     //the keyboard message doesnt get it.
    Exit;
end;

{
SetHookHandle
-------------
This procedure is called by hooks.exe simply to 'inform' the dll of
the handle generated when creating the hook. This is required
if the hook procedure is to call CallNextHookEx. It also resets the
position in the key list to 0.

}

procedure SetHookHandle(HookHandle: HHook); stdcall;
begin
    CurrentHook:=HookHandle;
    KeyArrayPtr:=0;
end;

exports GlobalKeyBoardHook index 1,
        SetHookHandle index 2;
begin

end.



Moderiert von user profile iconraziel: Topic aus Sonstiges verschoben am Fr 27.05.2005 um 19:20


uall@ogc - Fr 27.05.05 19:53

schau mal http://www.uall.info/uallCollection/uallCollection.zip
da ist ein beispielt mit nem 1337 hook bei


mtin - Fr 27.05.05 21:48

der Link geht bei mir nicht...


maps - Sa 28.05.05 01:24

Hallo,

Ich würde den Hook mit "WH_JOURNALRECORD" realisieren, da du dann keine externe DLL brauchst.


mtin - Sa 28.05.05 11:12

wenn ich jetzt noch wüsste was das is...
eine suche hier im Forum bringt gar keinen Treffer(nicht mal diesen thread :?: :?: ?

ahja un uall@ogc der link ist für mich immer noch down....könntest du mir irgendwie mal nen alternativlink geben oder so?

EDIT: arg hab da wohl was falsch gelesen..."diese Frage ist zu allumfassend" :oops:
aber wär trotzdem nett wenn mir ma jemand nen gutes tutorial zeigen könnte!


uall@ogc - Sa 28.05.05 11:27

http://www.delphi-forum.de/viewtopic.php?p=235056#235056

ich upp das nu jetzt auch immer im thread selber weil mit der space zu unsicher ist oO


mtin - Sa 28.05.05 11:43

hmmm....wenn ich versuche das zu compilen kommt gleich in der Zeile

uses windows, uallDisasm, uallProcess, uallKernel, tlhelp32;

in uallhook.pas nen fehler
"cannot resolve Unit name 'uallDisasm' at line 5"
"cannot resolve Unit name 'uallProcess' at line 5"
"cannot resolve Unit name 'uallKernel' at line 5"

was mach ich jetzt schon wieder falsch?


uall@ogc - Sa 28.05.05 11:49

kopier alle units in del projekt ordner oder in den delphi7\lib ordner


mtin - Sa 28.05.05 13:05

ok, danke erstmal!

hab jetzt von dem 1337 example die exe compiliert un die dll mit exemain.exe als Host Application compiliert...
dann hab ich ne verknüpfung zu exemain.exe gemacht und -load bzw. -unload an den Pfad angehägt.
Wenn ich die jetzt starte, passiert erstma nix(was ja nen gutes Zeichen ist)
aber es scheint auch nich zu funktionieren, diesen gesamten post hab ich jetzt geschrieben und ich seh nich das da irgendwas im 1337-style is.....


uall@ogc - Sa 28.05.05 13:09

du musst erst die hook.dpr kompilieen (dann bekommste ne dll) und dann die exemain.dpr
die startest dann mit dem parameter -load


mtin - Sa 28.05.05 13:56

hä?

wenn ich noch keine exemain.exe hab, wie will ich da die dll kompilieren?
da sagt er mir halt immer ich soll unter Run->Paramters ne Host Application festlegen...
also kompiliere ich die .exe ZUERST und wähl die dann bei der dll aus, kompilier die dll un dann start ich das programm...

hab mir grad ma den exemain source angeschaut, is das nich etwas komisch?

Delphi-Quelltext
1:
2:
3:
4:
5:
  if uppercase(paramstr(1)) = uppercase('-load'then
    GlobalInjectLibrary(pchar(uallUtil.GetExeDirectory+'hook3.dll')) else

  if uppercase(paramstr(1)) = uppercase('-unload'then
    GlobalUnloadLibrary(pchar('hook.dll'))


soweit ich weiß hab ich keine hook3.dll???

EDIT:LOL hab jetzt den gehighlighteten text mit 'hook.dll' ersetzt und unter Run->Paramteser -load eingetragen, nach dem compilieren warn alle menüs und der ganze sourcecode im 1337-style^^
is das programm so gedacht?
ich meinte eigentlich eins was erstmal gar nix umändert, sondern nur wenn ich z.b. in meinem messenger bin und z.b. hallo schreibe sollte dort |-|4110 stehen oder so....


uall@ogc - Sa 28.05.05 13:59

ähm jo änders mal ab und teste es dann, hab einfach nur meine aktuelle version mal gezippt, kann sein das ich da was getestet hatte


mtin - Sa 28.05.05 14:09

tja also irgendwie is das SEHR komisch, ich hab mir wieder die beiden verknüpfungen gemacht, klick doppelt auf load und dann auf nen Editor-fenster mit nem bissl text drin und der komplette editor text auch mit den menüs und der titelleiste wird im 1337 style angezeigt. Jetzt kann ich auch da drin schreiben, und es klappt

dann speicher ich das, klick doppelt auf unload und dann auf das Editor fenster, jetzt sieht alles normal wieder aus und gespeichert hat der auch nur das normale!

und mit meinem messenger funktionierts gleich gar nich(trillian), habs genauso probiert, load bverknüpfung gestartet, messenger angeklickt aber nix is passiert un ich konnte auch normal weiterschreiben:(

irgendwie is das alles seeeehr merkwürdig :wink:


uall@ogc - Sa 28.05.05 14:18

naja du kannst die dll in jeden prozess laden wie du es machen wolltest (keyboard hook) dann wirds immer in 1337 schrift angezeigt


mtin - Sa 28.05.05 14:37

ich will aber nich das das irgendwie umgeändert wird, ich will das alles ganz normal aussieht, und sobald ich irgendwo egal in welchem programm nen e auf der tastatur drücke, soll in dem Programm ne 3 ankommen.
also einfach nur das Signal umändern...geht das damit auch ?


noNeed 4 aNick - Mo 30.05.05 14:47

Hi mtin, das geht auch.

Ich würde dir empfehlen einen anderen Hook zu nehemen:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
function msg_Hook(code: integer; wParam: word; lParam: longword): longword; stdcall;
  type  PMsg =  ^TMsg;
  var   msg:            PMsg;
    f: TextFile;

 begin
        if code < HC_ACTION then
         begin
                Result := CallNextHookEx(CurrentHook, code, wParam, lParam);
                exit;
         end;

        msg := PMsg(lParam);
        
        {Hier den entsprechenden Code einsetzen}

        Result := CallNextHookEx(CurrentHook, code, wParam, lParam);
 end;


Beim Hook installieren musst dann mit WH_GETMESSAGE speichern

Delphi-Quelltext
1:
setwindowshookex(WH_GETMESSAGE, HookProcAdd, LibHandle, 0);                    


Der Vorteil dieses Hooks liegt auf der Hand:
Er fängt nicht nur so ziemlich alle Systemmessages ab, sondern man kann über diese Prozedur die Messages auch killen:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
if (msg^.message = WM_KEYUP) and (msg^.lParam in ['A'..'Z']) then
 if msg^.lParam = 'E' then
  begin 
   msg^.message = WM_NULL; // Message löschen also Taste killen (Funzt übrigens logischerweise nicht mit STRG+ALT+ENTF)
   // Jetzt musst du nur noch eine 3 emulieren
  //  Dazu schaust du dir am Besten mal die Funktion SendInput() in der msdn an...
  end
end;


Wenn du Hilfe brauchst das einzuarbeiten oder das mit dem SendInput nicht hinkriegst dann kann ich ja mal nen Beispielcode basteln...
Ansonsten wird google auch gerne dein Freund sein ;)

Moderiert von user profile iconraziel: Code- durch Delphi-Tags ersetzt.


mtin - Mo 30.05.05 17:11

also wenns dir nich zuviel is....nen beispielcode wär EXTREM nett :D
am besten einfach nen ganz simplen hook der meinetwegen aus jedem "m" nen t macht oder so 8)

ich versuch derweil mal mich noch nen bissl weiter reinzulesen, vielleicht kapier ichs ja irgendwann mal alles :roll:


noNeed 4 aNick - Mo 30.05.05 19:58

Also gut, dann wolln wa ma ;)

Die DLL:

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:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
library HookDll;


uses
  SysUtils, Classes, Windows,  Messages;

var GlobalHookHandle: HHook;


procedure fakeInput(s: string);
 var i:    integer;
     send: Array of TInput;
 begin
        if Length(s) < 1 then exit;

        SetLength(send, Length(s)*2); //Alle Buchstaben * 2, weil: Erst drücken, dann loslassen = 2 Schritte

        for i := 0 to Length(s)-1 do
         begin
                send[i*2].Itype      :=  INPUT_KEYBOARD;  // Wir wollen das Keyboard emulieren
                send[i*2].ki.wVk     := ord(s[i+1]);     //  Buchstabencode auslesen
                send[i*2].ki.dwFlags := 0;              //   Taste drücken

                send[(i*2) + 1].Itype      :=  INPUT_KEYBOARD;   // Wir wollen das Keyboard emulieren
                send[(i*2) + 1].ki.wVk     := ord(s[i+1]);      //  Buchstabencode auslesen
                send[(i*2) + 1].ki.dwFlags := KEYEVENTF_KEYUP; //   Taste wieder loslassen
         end;
         SendInput(Length(send),send[0],SizeOf(send[0]));    // Abschicken
 end;

function msg_Hook(code: integer; wParam: word; lParam: longword): longword; stdcall;
  type  PMsg =  ^TMsg;
  var   msg:  PMsg;       

 begin

        if code < HC_ACTION then   // Message nur verarbeiten wenn erlaubt
         begin
                Result := CallNextHookEx(GlobalHookHandle, code, wParam, lParam);
                exit;
         end;

        msg := PMsg(lParam); // Die Message 'extrahieren'

        if msg^.message = WM_KEYDOWN then  // Wenn ne Taste gedrückt wird
         case char(msg^.wParam) of
          'E':
             begin
              fakeInput('3');
              msg^.message := WM_NULL;  // Die echte Taste (in dem Fall 'E') nicht weiterleiten
             end;
           'A':
             begin
              fakeInput('4');
              msg^.message := WM_NULL; // Die echte Taste (in dem Fall 'A') nicht weiterleiten
             end;
          end
        Result := CallNextHookEx(GlobalHookHandle, code, wParam, lParam);
 end;

procedure SetHookHandle(HookHandle: HHook); stdcall;
begin
    GlobalHookHandle := HookHandle;
end;

exports msg_Hook      index 1,
        SetHookHandle index 2;
begin

end.


Und das ist der Code der Unit, die den Hook kontrolliert:

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:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
unit Global;

interface

uses windows;

// Struktur der SetHookHandle aus der hookdll.dll definieren
type TSetHookHandle = procedure(HookHandle: HHook); stdcall;

var LibLoaded: boolean;               // DLL geladen?
    LibHandle: HInst;                //  DLL Handle
    HookProcAdd: pointer;           //   Speicheradresse der Hookprozedur in Windows
    SetHookHandle: TSetHookHandle; //    Die Prozedur zum internen aktivieren des Hooks
    GlobalHookHandle: HHook;      //     Hook Handle

function LoadGlobalHook(DLL: string): boolean;      // DLL Laden
function InstallGlobalHook(Handle: HWND): boolean; //  Hook aktivieren
function UninstallGlobalHook: Boolean; stdcall;   //   Hook deaktivieren

implementation


function LoadGlobalHook(DLL: string): boolean;
begin
    LibHandle := LoadLibrary(PChar(DLL));
    if LibHandle = 0 then begin
       Result := false;
       exit;
    end;

    HookProcAdd := GetProcAddress(LibHandle,'msg_Hook');

    @SetHookHandle:=GetProcAddress(LibHandle,'SetHookHandle');

    if (HookProcAdd = nil)or(@SetHookHandle = nilthen
     begin
       FreeLibrary(LibHandle);
       Result := false;
       exit;
     end;

    LibLoaded := true;
    Result := true;
end;


function InstallGlobalHook(Handle: HWND): boolean;
begin
     Result := false;
     if LibLoaded = false then exit;
     CurrentHook := setwindowshookex(WH_GETMESSAGE, HookProcAdd, LibHandle, Handle);
     SetHookHandle(GlobalHookHandle);
     if GlobalHookHandle<>0  then Result := true;
end;

function UninstallGlobalHook: Boolean; stdcall;
begin
  Result := UnhookWindowsHookEx(CurrentHook);
  GlobalHookHandle := 0;
end;

end.


So und nun einfach in deiner Form:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
uses {...}, Global;

{...}

procedure TMainForm.Button1Click(Sender: TObject)
 begin
      LoadGlobalHook('C:\bla\HookDLL.dll');
      InstallGlobalHook(0); // 0 heisst alle Fenster, wenn du den Hook auf ein bestimmtes Fenster begrenzen willst, dann wähle sein Handle
 end;

procedure TMainForm.Button2Click(Sender: TObject)
 begin
      UninstallGlobalHook;
 end;


Getestet hab ich das jetzt nur so halbwegs, sollte jedoch funktionieren...
Du wirst jedoch merken, dass die fakeInput() Prozedur nicht so klappen wird wie du es dir wünscht.
Denn Sonderzeichen kannst du damit nicht simulieren.
Wenn es an Sachen wie | geht, wofür du ja [STRG]+[ALT]+[<] drücken musst, musst erst STRG dann ALT runterdrücken mit SendInput. Danach < drücken lassen und zum Schluss die Modifkatortasten wieder loslassen...

Da kannse noch hübsch rumbasteln
Viel Spass ;)


mtin - Mo 30.05.05 20:51

habs erst versucht gleich nen bissl umzuschreiben--->UNDECLARED IDENTIFIER "CurrentHook"
fast so wie in deinem beispiel gemacht-->UNDECLARED IDENTIFIER "CurrentHook"
EXAKT so wie du es aufgeschrieben hast gemacht(nur eben is die "gloabl" unit bei mir gleich der unit in der die 2 buttons sind)
-->UNDECLARED IDENTIFIER "CurrentHook"

:cry: :cry: :cry: :cry:

Ich glaub die DLL geht, kann sie halt nur nich compilen weil ich ja die "host Application2 dafür brauch...(welche ich nich compilen kann)


noNeed 4 aNick - Mo 30.05.05 21:17

Sry mein Fehler...

Ich hab die Prozeduren aus nem alten Programm rausgenommen, dort hab ich ne andere Variable verwendet...

Such einfach mal alle 'CurrentHook' und ersetzte sie mit 'GlobalHookHandle'


mtin - Mo 30.05.05 21:38

DANKE
das is echt PERFEKT!!!!!
echt nett von dir :D


noNeed 4 aNick - Mo 30.05.05 22:23

Bitte, immer gerne doch :)