[Delphi] Keylogger buffering probleem...

Pagina: 1
Acties:

  • jopiek
  • Registratie: September 2000
  • Laatst online: 30-03 18:57

jopiek

Tja... 'ns ff denken.

Topicstarter
Ik heb een applicatie waarvoor het makkelijk is om een windows keyhook te gebruiken: het moet gebruikt worden tijdens presentaties en als het programma open staat moet het programma ook alle toetsen eerst zelf binnenkrijgen voor het deze eventueel verder door laat.

So far so good, werkt allemaal redelijk, ik krijg de toetsen binnen via een dll welke de feitelijke keyhook is, ook de communicatie met het programma in Delphi verloopt zonder verdere problemen, maar nu wil ik eigenlijk de toetsen bufferen. Ik krijg het echter niet voor elkaar om de toetsen event-driven naar het hoofdprogramma te sturen en heb dus een timertje om te kijken of er een toets binnengekomen is. Ik maak gebruik van een string waarin ik de keycodes als char opsla. Om een fifo idee te krijgen delete ik na verwerking het eerste caracter.

Dit is de code ervan:
code:
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
declaratie:
  THookRec = packed record
    TheHookHandle : HHOOK;
    TheAppWinHandle : HWND;
    TheCtrlWinHandle : HWND;
    TheKeyCount : DWORD;
    ShiftPressed : bool;
    KeyBuffer : string;
  end;

in het timer event:
procedure TMainApplication.KeyboardHookTimerTimer(Sender: TObject);
var i : integer;
    bufferstring : string;
    key : cardinal;

begin
  //buffer verwerken
  if lpHookRec^.KeyBuffer <> '' then
  begin
    bufferstring := lpHookRec^.KeyBuffer;
    while length(bufferstring) > 0 do
    begin
      key := ord(bufferstring[1]);
      ProjectorScreen.VerwerkKeyboardMessages(key);
      Delete(bufferstring, 1, 1);
      lpHookRec^.KeyBuffer := bufferstring;
    end;
  end;
  Label1.Caption := IntToStr(lpHookRec^.TheKeyCount) + ' Keys Logged';
end;


het stuk code lpHookRec^.KeyBuffer := bufferstring; levert een in invalid pointer operation op... ik mag er blijkbaar zo niet naar toe schrijven...

Heeft iemand een oplossing of een alternatief voor mijn probleem?

[ Voor 3% gewijzigd door jopiek op 03-08-2005 15:55 ]

Cogito Ergo Credo


  • Tomatoman
  • Registratie: November 2000
  • Laatst online: 07:02

Tomatoman

Fulltime prutser

Kan het zijn dat KeyBuffer (regel 9) geen string maar een PChar is? In dat geval schrijf je op regel 27 naar een stuk geheugen dat niet door jouw code wordt beheerd. Het kan zijn dat dat geheugen read-only is, maar het kan ook zijn dat je bijvoorbeeld een Delphi (reference counted) string probeert weg te schrijven in te klein stuk geheugen. Gevolg: geheugencorruptie in de DLL.

[ Voor 169% gewijzigd door Tomatoman op 03-08-2005 16:28 . Reden: Oeps, verkeerde post aangepast ]

Een goede grap mag vrienden kosten.


  • jopiek
  • Registratie: September 2000
  • Laatst online: 30-03 18:57

jopiek

Tja... 'ns ff denken.

Topicstarter
tomatoman schreef op woensdag 03 augustus 2005 @ 16:10:
Kan het zijn dat KeyBuffer (regel 9) geen string maar een PChar is? In dat geval schrijf je op regel 27 naar een stuk geheugen dat niet door jouw code wordt beheerd. Het kan zijn dat dat geheugen read-only is, maar het kan ook zijn dat je bijvoorbeeld een Delphi (reference counted) string probeert weg te schrijven in te klein stuk geheugen. Gevolg: geheugencorruptie in de DLL.
De declaratie in de dll is als volgt:

code:
1
2
3
4
5
6
7
8
9
10
type
  PHookRec = ^THookRec;
  THookRec = packed record
    TheHookHandle : HHOOK;
    TheAppWinHandle : HWND;
    TheCtrlWinHandle : HWND;
    TheKeyCount : DWORD;
    ShiftPressed : bool;
    KeyBuffer : string;
  end;


Ik vermoed iid dat het een read only stuk geheugen is, maar waarom is me even een raadsel...

[ Voor 6% gewijzigd door jopiek op 03-08-2005 16:25 ]

Cogito Ergo Credo


  • Tomatoman
  • Registratie: November 2000
  • Laatst online: 07:02

Tomatoman

Fulltime prutser

Je kunt niet zomaar een string uit de dll gebruiken in een andere applicatie. Dat kan alleen als je een hiervoor geschikte memory manager gebruikt. Uit de Delphi helpfiles:
On Windows, if a DLL exports routines that pass long strings or dynamic arrays as parameters or function results (whether directly or nested in records or objects), then the DLL and its client applications (or DLLs) must all use the ShareMem unit. The same is true if one application or DLL allocates memory with New or GetMem which is deallocated by a call to Dispose or FreeMem in another module. ShareMem should always be the first unit listed in any program or library uses clause where it occurs.

ShareMem is the interface unit for the BORLANDMM.DLL memory manager, which allows modules to share dynamically allocated memory. BORLANDMM.DLL must be deployed with applications and DLLs that use ShareMem. When an application or DLL uses ShareMem, its memory manager is replaced by the memory manager in BORLANDMM.DLL.

Een goede grap mag vrienden kosten.


  • riezebosch
  • Registratie: Oktober 2001
  • Laatst online: 13:09
Waarom krijg je het niet voor elkaar de toetsen 'event-driven' naar je hoofdprogramma te sturen? Wat bedoel je dan met dat de communicatie hiertussen wel goed loopt? Hoe ik het zelf gedaan heb: In de DLL zit een functie om de hook te zetten, met als parameter de handle van het hoofdprogramma. Deze handle sla ik in een stukje shared-memory in de DLL op, zodat ik vanuit alle instanties van de DLL een SendMessage naar het hoofdprogramma kan doen. Die kan deze dan weer in z'n message-callback opvangen en verder afhandelen. Dan heb je toch je event-driven situatie? Heb je dan nog een buffer nodig?

Canon EOS 400D + 18-55mm F3.5-5.6 + 50mm F1.8 II + 24-105 F4L + 430EX Speedlite + Crumpler Pretty Boy Back Pack


  • LordLarry
  • Registratie: Juli 2001
  • Niet online

LordLarry

Aut disce aut discede

Ik zou dan wel PostMessage ipv SendMessage gebruiken in het voorbeeld van riezebosch, anders is er geen sprake van een buffer.

We adore chaos because we like to restore order - M.C. Escher


  • Tomatoman
  • Registratie: November 2000
  • Laatst online: 07:02

Tomatoman

Fulltime prutser

Je gebruikt nu trouwens een timer om regelmatig te controleren op binnengekomen toetsenbordacties - dat is in feite polling. Dat is niet erg efficiënt, al kan het weinig kwaad. Een elegantere methode is wellicht een callbackmechanisme. De dll roept bij iedere toetsenbordactie een callbackfunctie in de applicatie aan, waardoor er niet constant gecontroleerd (gepolld) hoeft te worden of er actie ondernomen moet worden.

Gedeelde unit met declaraties:
Delphi:
1
2
3
type
  TKeyCallback = procedure(AppWnd, CtrlWnd: HWND; Shift: Boolean;
    KeyCount: Cardinal; const KeyBuffer: PChar);

In de DLL:
Delphi:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var
  KeyCallback: TKeyCallback = nil;

{ toevoegen aan de exports clause van de DLL: }
procedure SetKeyCallback(AKeyCallback: TKeyCallback);
begin
  KeyCallback := AKeyCallback;
end;

{ de code die reageert op keystrokes: }
...
  if KeyCallback <> nil then
    with HookRec do
      KeyCallback(TheAppWinHandle, TheCtrlWinHandle, ShiftPressed,
        KeyCount, KeyBuffer);
...


In de applicatie:
Delphi:
1
2
3
4
5
6
7
8
9
10
11
12
13
{ Hier worden de keystrokes afgehandeld }
procedure KeyHandler(AppWnd, CtrlWnd: HWND; Shift: Boolean; KeyCount: Cardinal;
  const KeyBuffer: PChar);
begin
  { doe wat met de binnenkomende keys }
end;

{ Zorg dat de door de DLL geëxporteerde functies in de uses clause staan }
initialization
  SetKeyCallback(KeyHandler);
finalization
  SetKeyCallback(nil); // niet vergeten!
end.

Een goede grap mag vrienden kosten.


  • jopiek
  • Registratie: September 2000
  • Laatst online: 30-03 18:57

jopiek

Tja... 'ns ff denken.

Topicstarter
tomatoman schreef op woensdag 03 augustus 2005 @ 17:08:
Je gebruikt nu trouwens een timer om regelmatig te controleren op binnengekomen toetsenbordacties - dat is in feite polling. Dat is niet erg efficiënt, al kan het weinig kwaad. Een elegantere methode is wellicht een callbackmechanisme. De dll roept bij iedere toetsenbordactie een callbackfunctie in de applicatie aan, waardoor er nog constant gecontrolleerd (gepolld) hoef te worden of er actie ondernomen moet worden.

Gedeelde unit met declaraties:
Delphi:
1
2
3
type
  TKeyCallback = procedure(AppWnd, CtrlWnd: HWND; Shift: Boolean;
    KeyCount: Cardinal; const KeyBuffer: PChar);

In de DLL:
Delphi:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var
  KeyCallback: TKeyCallback = nil;

{ toevoegen aan de exports clause van de DLL: }
procedure SetKeyCallback(AKeyCallback: TKeyCallback);
begin
  KeyCallback := AKeyCallback;
end;

{ de code die reageert op keystrokes: }
...
  if KeyCallback <> nil then
    with HookRec do
      KeyCallback(TheAppWinHandle, TheCtrlWinHandle, ShiftPressed,
        KeyCount, KeyBuffer);
...


In de applicatie:
Delphi:
1
2
3
4
5
6
7
8
9
10
11
12
13
{ Hier worden de keys afgehandeld: }
procedure KeyHandler(AppWnd, CtrlWnd: HWND; Shift: Boolean; KeyCount: Cardinal;
  const KeyBuffer: PChar);
begin
  { doe wat met de binnenkomende keys }
end;

{ Zorg dat de door de DLL geëxporteerde functies in de uses clause staan }
initialization
  SetKeyCallback(KeyHandler);
finalization
  SetKeyCallback(nil); // niet vergeten!
end.
Hmm da's idd een stuk handiger, heb niet veel ervaring met DLL's en alle mastering, unleashed en nogwattes pillen liggen op m'n kamer in Enschede :s Maar dan heb ik eigenlijk de buffer ook niet nodig, ik stuud de key dan gewoon meteen door...

Cogito Ergo Credo


  • Tomatoman
  • Registratie: November 2000
  • Laatst online: 07:02

Tomatoman

Fulltime prutser

jopiek schreef op woensdag 03 augustus 2005 @ 17:24:
[...]
Maar dan heb ik eigenlijk de buffer ook niet nodig, ik stuud de key dan gewoon meteen door...
Inderdaad, probleem opgelost :)

Een goede grap mag vrienden kosten.


  • LordLarry
  • Registratie: Juli 2001
  • Niet online

LordLarry

Aut disce aut discede

Een callback werkt niet met globale hooks. De DLL wordt geinjecteerd in een ander process. Die pointer die je denkt mee te geven komt ten eerste alleen in de DLL aan in je eigen process en niet in de andere processen (daarom wordt er geklooid met memory mapped files. Daarnaast is de pointer ook helemaal niet geldig in een ander process.

Een buffer, waar en hoe je die ook maakt, heeft bovendien als voordeel dat je log applicatie windows zo min mogelijk vertraagt. Anders zou men bij elke toetsaanslag in windows eerst moeten wachten tot jouw applicatie dingen heeft gedaan. En dan laat ik nog buiten beschouwing wat er met windows zou gebeuren als jouw applicatie zou crashen.

De oplossing van riezebosch (met PostMessage ipv SendMessage) is naar mijn mening de beste oplossing en bied je ook nog eens een buffer aan zonder extra moeite.

We adore chaos because we like to restore order - M.C. Escher


  • Tomatoman
  • Registratie: November 2000
  • Laatst online: 07:02

Tomatoman

Fulltime prutser

LordLarry schreef op woensdag 03 augustus 2005 @ 18:04:
Een callback werkt niet met globale hooks. De DLL wordt geinjecteerd in een ander process.
Hmmm, daar had ik niet bij stilgestaan :|. Ik vrees dat je gelijk hebt. In dat geval lijkt PostMessage inderdaad de beste oplossing.

Een goede grap mag vrienden kosten.


  • jopiek
  • Registratie: September 2000
  • Laatst online: 30-03 18:57

jopiek

Tja... 'ns ff denken.

Topicstarter
wat dat betreft is windows niet heel handig opgezet :s

Cogito Ergo Credo


  • LordLarry
  • Registratie: Juli 2001
  • Niet online

LordLarry

Aut disce aut discede

Het geheugen van processen onderling afschermen is juist gedaan om de stabiliteit te verhogen. Veel volwassen OSsen doen dat. Dat het in dit geval niet handig is vind ik geen excus om dan maar iedereen overal maar in te laten rommelen.

/edit
Hoe weet je bovendien of het onder een ander OS wel beter geregeld is?!

[ Voor 16% gewijzigd door LordLarry op 03-08-2005 20:52 ]

We adore chaos because we like to restore order - M.C. Escher


  • jopiek
  • Registratie: September 2000
  • Laatst online: 30-03 18:57

jopiek

Tja... 'ns ff denken.

Topicstarter
LordLarry schreef op woensdag 03 augustus 2005 @ 20:51:
Het geheugen van processen onderling afschermen is juist gedaan om de stabiliteit te verhogen. Veel volwassen OSsen doen dat. Dat het in dit geval niet handig is vind ik geen excus om dan maar iedereen overal maar in te laten rommelen.

/edit
Hoe weet je bovendien of het onder een ander OS wel beter geregeld is?!
ik hb niet veel ervaring met het programmeren onder niet Windows, maar iig is het in mijn ogen lelijk om bijvoorbeeld zo'n keyhook in elke applicatie te laden... een generiek keyhandler systeem door het os zou me toch iets handiger lijken...

Cogito Ergo Credo


  • LordLarry
  • Registratie: Juli 2001
  • Niet online

LordLarry

Aut disce aut discede

Die zijn er ook, maar dan in de vorm van drivers.

We adore chaos because we like to restore order - M.C. Escher


  • jopiek
  • Registratie: September 2000
  • Laatst online: 30-03 18:57

jopiek

Tja... 'ns ff denken.

Topicstarter
LordLarry schreef op donderdag 04 augustus 2005 @ 11:17:
Die zijn er ook, maar dan in de vorm van drivers.
da's waar, maar op nu voor een dergelijke applicatie de driver aan te passen lijkt me niet echt geschikt...

Cogito Ergo Credo


  • riezebosch
  • Registratie: Oktober 2001
  • Laatst online: 13:09
jopiek schreef op donderdag 04 augustus 2005 @ 01:54:
[...]


ik hb niet veel ervaring met het programmeren onder niet Windows, maar iig is het in mijn ogen lelijk om bijvoorbeeld zo'n keyhook in elke applicatie te laden... een generiek keyhandler systeem door het os zou me toch iets handiger lijken...
Met een LowLevelKeyboardHook gebeurd dat ook. Heb het getest, en m'n DLL werd dan niet in elk proces geladen. Dat was ook de reden dat ik wel met SendMessage ipv PostMessage moest werken, omdat ik bij een toetsaanslag ook gegevens over het control en het window waar deze op uitgevoerd werd op wilde slaan. Als je dan met PostMessage werkt, kan dit scherm (door de toets) al gesloten zijn.

Het nadeel dat ik met LowLevelKeyboardHook had, was dat ik niet in het process van het actieve programma zat te frutten, en daardoor extra moeite moest doen om rechten te hebben om die gegevens uit te lezen. Standaard kan je namelijk niet GetFocus doen in een andere applicatie. Dan moet je eerst je eigen thread aan die van de actieve applicatie knopen met AttachThreadInput. Maar als ik het niet met een LowLevelKeyboardHook deed, bleef m'n programma hangen omdat bij de GetFocus eerst gewacht werd tot de toetsenbordinvoer afgehandeld was, en ik dat afhandelen van de toetsenbordinvoer liet wachten tot ik het resultaat van GetFocus terughad 8)7

Maar als je dit niet allemaal nodig hebt, is een PostMessage vanuit een KeyboardHook inderdaad een prima oplossing :)

Canon EOS 400D + 18-55mm F3.5-5.6 + 50mm F1.8 II + 24-105 F4L + 430EX Speedlite + Crumpler Pretty Boy Back Pack


  • jopiek
  • Registratie: September 2000
  • Laatst online: 30-03 18:57

jopiek

Tja... 'ns ff denken.

Topicstarter
riezebosch schreef op donderdag 04 augustus 2005 @ 14:47:
[...]


Met een LowLevelKeyboardHook gebeurd dat ook. Heb het getest, en m'n DLL werd dan niet in elk proces geladen. Dat was ook de reden dat ik wel met SendMessage ipv PostMessage moest werken, omdat ik bij een toetsaanslag ook gegevens over het control en het window waar deze op uitgevoerd werd op wilde slaan. Als je dan met PostMessage werkt, kan dit scherm (door de toets) al gesloten zijn.

Het nadeel dat ik met LowLevelKeyboardHook had, was dat ik niet in het process van het actieve programma zat te frutten, en daardoor extra moeite moest doen om rechten te hebben om die gegevens uit te lezen. Standaard kan je namelijk niet GetFocus doen in een andere applicatie. Dan moet je eerst je eigen thread aan die van de actieve applicatie knopen met AttachThreadInput. Maar als ik het niet met een LowLevelKeyboardHook deed, bleef m'n programma hangen omdat bij de GetFocus eerst gewacht werd tot de toetsenbordinvoer afgehandeld was, en ik dat afhandelen van de toetsenbordinvoer liet wachten tot ik het resultaat van GetFocus terughad 8)7

Maar als je dit niet allemaal nodig hebt, is een PostMessage vanuit een KeyboardHook inderdaad een prima oplossing :)
Hmm dat valt weer mee dan, mijn applicatie moet gewoon simpelweg alle key's even checken en een paar ervan afvangen, in de huidige oude applicatie (niet door mij geschreven) werd gewoon een key event gebruikt en dat zorgde ervoor dat als programma ff geen focus had je een probleem had... Het programma is bedoeld voor karoake achtige doeleinden, dus als dan je publiek er opeens last van heeft is het erg irri :)

heb trouwens een aardige tutorial opgesnord: http://www.delphi-forum.de/viewtopic.php?t=101

[ Voor 4% gewijzigd door jopiek op 05-08-2005 17:17 ]

Cogito Ergo Credo


  • riezebosch
  • Registratie: Oktober 2001
  • Laatst online: 13:09
Als het je maar om een paar keys afvangen gaat, is het waarschijnlijk nog veel makkelijker om een hotkey te registreren :)

Canon EOS 400D + 18-55mm F3.5-5.6 + 50mm F1.8 II + 24-105 F4L + 430EX Speedlite + Crumpler Pretty Boy Back Pack


  • Tomatoman
  • Registratie: November 2000
  • Laatst online: 07:02

Tomatoman

Fulltime prutser

riezebosch schreef op vrijdag 05 augustus 2005 @ 18:00:
Als het je maar om een paar keys afvangen gaat, is het waarschijnlijk nog veel makkelijker om een hotkey te registreren :)
Hoe registreer je Ctrl+Alt+Karaoke? :+

Een goede grap mag vrienden kosten.


  • riezebosch
  • Registratie: Oktober 2001
  • Laatst online: 13:09
tomatoman schreef op vrijdag 05 augustus 2005 @ 18:23:
[...]
Hoe registreer je Ctrl+Alt+Karaoke? :+
Haha :) Er staat dat het bedoeld is voor karaoke achtige doeleinden. Dus ik neem aan dat de TS bij een bepaalde toetsencombo een bepaalde actie wil doen ongeacht of z'n programma actief is. Daarvoor zijn hotkeys zeer geschikt (tenzij je teveel combo's wilt doen natuurlijk).

Canon EOS 400D + 18-55mm F3.5-5.6 + 50mm F1.8 II + 24-105 F4L + 430EX Speedlite + Crumpler Pretty Boy Back Pack


  • jopiek
  • Registratie: September 2000
  • Laatst online: 30-03 18:57

jopiek

Tja... 'ns ff denken.

Topicstarter
riezebosch schreef op vrijdag 05 augustus 2005 @ 20:14:
[...]

Haha :) Er staat dat het bedoeld is voor karaoke achtige doeleinden. Dus ik neem aan dat de TS bij een bepaalde toetsencombo een bepaalde actie wil doen ongeacht of z'n programma actief is. Daarvoor zijn hotkeys zeer geschikt (tenzij je teveel combo's wilt doen natuurlijk).
Ik gebruik helaas vooral de up/down pgup/pgdown en shifttoetsen, daarnaast de 'r' en numerieke toetsen. Ik maak een remake van een brak ander programma en wil graag dat de toetsen identiek zijn...

Cogito Ergo Credo


  • riezebosch
  • Registratie: Oktober 2001
  • Laatst online: 13:09
Maar moet het dan perse óók werken als jouw programma niet actief is? Want anders heb je helemaal geen hook of hotkeys of wat dan ook nodig ;)

Canon EOS 400D + 18-55mm F3.5-5.6 + 50mm F1.8 II + 24-105 F4L + 430EX Speedlite + Crumpler Pretty Boy Back Pack


  • jopiek
  • Registratie: September 2000
  • Laatst online: 30-03 18:57

jopiek

Tja... 'ns ff denken.

Topicstarter
riezebosch schreef op zaterdag 06 augustus 2005 @ 13:39:
Maar moet het dan perse óók werken als jouw programma niet actief is? Want anders heb je helemaal geen hook of hotkeys of wat dan ook nodig ;)
Ik liep juist bij het bestaande programma er tegenaan dat het irritant is om steeds te moeten focussen op de applicatie, dus dat is de reden, ik heb wel genoeg ervaring met programmeren in Delphi (sinds 1997) om de standaardoplossingen eerst te gebruiken, maar iig bedankt voor het meedenken!

Cogito Ergo Credo

Pagina: 1