Source file: /~heha/basteln/PC/USB2LPT/usb2lpt.zip/alt/2005/vlpt.c

#include "usb2lpt.h"

/***********************
 ** Globale Variablen **
 ***********************/

static LARGE_INTEGER OldInt1;	/* bleibt Null ohne Pentium */
static LARGE_INTEGER IDT;
static WORD SaveDS,SaveFS;	// Für ISR zum Restaurieren
/*static*/ ULONG CurThreadPtr;	// Get_Cur_Thread_Handle-Ersatz
// Strukturzeiger für 2. Kernel-Stack (Win9x) C0013E78
// Unter W2K/WXP ist der aktuelle Thread durch FS: adressiert
// ...dann ist diese Variable = Null.

void NewInt1(void);
#define Usage_Len 32		// >=4, die ersten vier (auch) via DR0..DR3
static WORD Usage[Usage_Len];	// Überwachte Portadressen (von DR0..DR3)
// Der Wert 0 bedeutet: freies Debugregister
// Der Wert 1 weist beim Treiber-Start besetzte Debugregister aus
static ULONG UsageBits;

static PDEVICE_EXTENSION X4DR[Usage_Len]; // zugehörige USB2LPT-Geräte
//static PDEVICE_EXTENSION TimerX;	// Für 1-s-Timer genutztes Gerät
static MYTIMER debset;	// Debugregister-Setz-Zeitgeber (periodisch)
// Der Timer dient zur Feststellung "geklauter" Debugregister.

/*****************************
 ** Debug-Register und Int1 **
 *****************************/
/* Zu tun: Auf Mehrprozessormaschinen müssen in _allen_ Prozessoren
 * die Debugregister gesetzt/gelesen/geprüft werden.
 * Ein NTDDK-Treiber kann dazu KeSetTargetProcessorDpc benutzen?
 */

static void __declspec(naked) SetHook1(void) {
/* PE: CX:EAX=neue Adresse für INT1 */
/* PA: CX:EAX=alte Adresse von INT1 */
 _asm{	sidt	[IDT]
	mov	edx,cr0
	push	edx
	 btr	edx,16			/* Supervisor Mode Write Protect */
	 mov	cr0,edx
	 mov	edx,[dword ptr IDT+2]
	 xchg	ax,[edx+0x08]		/* Low-Teil Offset */
	 rol	eax,16
	 xchg	ax,[edx+0x0E]		/* High-Teil Offset */
	 rol	eax,16
	 xchg	cx,[edx+0x0A]		/* Segment */
	pop	edx
	mov	cr0,edx
	ret
 }
}

static void __declspec(naked) SwapID(void) {	/* Pentium-Test */
 _asm{	pushfd
	pop	eax
	btc	eax,21
	push	eax
	popfd
	ret
 }
}

static void __declspec(naked) HookInt1(void) {
/* Faule Annahme: bei INT1 befindet sich bereits ein gültiger Gate-Deskriptor
/* VR: EAX,CX,EDX; EFlags unverändert */
// NICHT MULTIPROZESSOR-SICHER, weil anderer Prozessor gerade Int1 ausführen
// könnte (und ich weiß nicht, wie man ihn daran hindert)
 _asm{	pushfd
	 cli
	 mov	eax,offset NewInt1
	 mov	cx,cs
	 cmp	[dword ptr OldInt1],0
	 jz	no_586
	 call	SetHook1
no_586:
	popfd
	ret
 }
}

static void __declspec(naked) UnhookInt1(void) {
/* VR: EAX,CX,ESI; EFlags unverändert */
// NICHT MULTIPROZESSOR-SICHER, weil anderer Prozessor gerade Int1 ausführen
// könnte (und ich weiß nicht, wie man ihn daran hindert)
 _asm{	pushfd
	 cli
	 mov	eax,[dword ptr OldInt1]
	 mov	cx,[word ptr OldInt1+4]
	 or	eax,eax
	 jz	no_586
	 call	SetHook1
no_586:
	popfd
	ret
 }
}

static BOOLEAN __declspec(naked) LoadDR(void) {
/* Debugregister laden/wiederherstellen, VR: EAX */
// Liefert TRUE sowie CY=1 wenn sich die Debugregister dabei verändern
// NICHT MULTIPROZESSOR-SICHER, weil Debugregister des anderen Prozessors
// unerreichbar (und ich weiß nicht, wie)
 _asm{	pushad
	 xor	ecx,ecx		// Bit-Sammler
	 mov	esi,offset Usage// im Ring 0 immer CLD (?)
	 _emit	0x0F		// mov eax,cr4
	 _emit	0x20
	 _emit	0xE0
	 BTS	eax,3		/* PENTIUM Debug Extension (DE) aktivieren */
	 _emit	0x0F		// mov cr4,eax
	 _emit	0x22
	 _emit	0xE0
	 setnc	cl		// CL=1 wenn Debug Extension ausgeschaltet war
	 mov	ebx,DR7
	 xor	eax,eax
	 lodsw
	 or	ah,ah		// mit (gültiger) Adresse gefüllt?
	 jz	no0
	 mov	edx,DR0
	 mov	DR0,eax
	 sub	edx,eax
	 or	edx,ecx
	 setnz	cl		// CL=1 wenn DR0 verändert wurde oder CL=1 war
	 or	ebx,0x000E0202
	 btr	ebx,16		/* DR7=xxxxxxxx xxxx1110 xxxxxx1x xxxxxx1x */
no0:	 lodsw
	 or	ah,ah
	 jz	no1
	 mov	edx,DR1
	 mov	DR1,eax
	 sub	edx,eax
	 or	edx,ecx
	 setnz	cl		// CL=1 wenn DR1 verändert wurde oder CL=1 war
	 or	ebx,0x00E00208
	 btr	ebx,20		/* DR7=xxxxxxxx 1110xxxx xxxxxx1x xxxx1xxx */
no1:	 lodsw
	 or	ah,ah
	 jz	no2
	 mov	edx,DR2
	 mov	DR2,eax
	 sub	edx,eax
	 or	edx,ecx
	 setnz	cl		// CL=1 wenn DR2 verändert wurde oder CL=1 war
	 or	ebx,0x0E000220
	 btr	ebx,24		/* DR7=xxxx1110 xxxxxxxx xxxxxx1x xx1xxxxx */
no2:	 lodsw
	 or	ah,ah
	 jz	no3
	 mov	edx,DR3
	 mov	DR3,eax
	 sub	edx,eax
	 or	edx,ecx
	 setnz	cl		// CL=1 wenn DR3 verändert wurde oder CL=1 war
	 or	ebx,0xE0000280
	 btr	ebx,28		/* DR7=1110xxxx xxxxxxxx xxxxxx1x 1xxxxxxx */
no3:	 mov	edx,DR7
	 mov	DR7,ebx
	 sub	edx,ebx
	 or	edx,ecx
	 setnz	cl		// CL=1 wenn DR7 verändert wurde oder CL=1 war
	 shr	ecx,1		// Returnwert nach CY
	popad
	setc	al		// für sog. Hochsprachen...
	ret
 }
}

static VOID SetDebDpc(IN PKDPC dpc,PVOID x,PVOID a,PVOID b) {
 if (LoadDR()) {
  int i;	// In allen Zählern inkrementieren
  for (i=0;i<Usage_Len;i++) if (X4DR[i]) X4DR[i]->ac.steal++;
  Vlpt_KdPrint2(("Debugregister gemaust!\n"));
 }
}

void PrepareDR(void) {
/* PA: [OldInt1]=Adresse von INT1, aber nur beim Pentium */
// Weiterhin SaveDS, SaveFS, CurThreadPtr
 _asm{	sidt	[IDT]
	mov	esi,[dword ptr IDT+2]	/* BASE */
// Sucht ab OldInt0 den Befehl "mov edi,[xxxxxxxx]"; der Wert ist der
// globale Zeiger für den aktuellen Thread (Stack-Tausch) (Win98)
	mov	di,[esi+6]	// High-Teil Int0 (bei Int1 stört SoftICE)
	rol	edi,16
	mov	di,[esi]	// Low-Teil
	mov	eax,0xC766006A	// So geht's bei W2K los
	scasd
	je	l2		// keine Bytefolge suchen, alles FS-adressierbar
	mov	ecx,100h
	mov	al,0x8B		// Bytefolge 8B 3D suchen
l1:	repne	scasb
	jne	l2
	cmp	byte ptr [edi],0x3D
	jne	l1
	mov	eax,[edi+1]
	jmp	l3
l2:	xor	eax,eax		// Null für WinNT
l3:	mov	[CurThreadPtr],eax	// gleichzeitig Win9x-Kennung
// holt den OldInt1-Zeiger, aber nur, wenn's ein Pentium ist
 	call	SwapID
	mov	edi,eax
	call	SwapID
	cmp	edi,eax
	jz	no_586		// kein Pentium
	mov	edi,offset OldInt1
	cld
	mov	ax,[esi+0x08]		/* Low-Teil Offset */
	stosw
	mov	ax,[esi+0x0E]		/* High-Teil Offset */
	stosw
	movzx	eax,word ptr [esi+0x0A]	/* Segment */
	stosd
	mov	[SaveDS],ds	// Win9x: 30h	WinNT:
	mov	[SaveFS],fs	// Win9x: 78h	WinNT:
// markiert die bereits vor dem Treiber-Start verwendeten Debugregister
// als "nicht verwendungsfähig für uns".  Aufzurufen beim Treiber-Start
// NICHT MULTIPROZESSOR-SICHER
 	mov	esi,offset Usage
	mov	eax,DR7
	push	4
	pop	ecx
l:	test	al,3		// Lx oder Gx gesetzt? (Also in Benutzung?)
	setnz	[esi]		// wenn ja, auf 1 setzen, sonst 0 lassen
	shr	eax,2		// nächstes Debugregister
	add	esi,2		// nächstes Usage-Word
	loop	l
 }
// Debugregister-Diebstahl zyklisch rückgängig machen (WinXP)
// Besser wäre es, die Ursache auszumachen...
 KeInitializeTimer(&debset.tmr);
 KeInitializeDpc(&debset.dpc,SetDebDpc,NULL);
no_586:;
}

static void _fastcall SetTimerX() {
 if (!KeSetTimerEx(&debset.tmr,RtlConvertLongToLargeInteger(100*-10000),
   100,&debset.dpc)) TRAP();
}

int _stdcall AllocDR(WORD adr,PDEVICE_EXTENSION X) {
// Belegung eines E/A-Debugregisters und andere Arbeiten
 int i;
 if (adr&0x3) return -1;	// Fehlerkode: falsche Adresse
 if (!(adr&0xFF00)) return -1;	// Board-Register sind ebenfalls unzulässig
 if (!OldInt1.QuadPart) return -6;	// Kein Pentium
// if (!CurThreadPtr) return -7;	// Keine Anzapfung möglich
 for (i=0; i<Usage_Len; i++) if (adr==Usage[i]) return -2; // Schon benutzt
 i=4; if (X->uc.flags&UC_Debugreg) i=0;
 for (; i<Usage_Len; i++) if (!Usage[i]) {
  Usage[i]=adr;
  if (!(UsageBits&0xF) && i<4) {
   HookInt1();
   SetTimerX();
  }
  UsageBits|=1<<i;
  X4DR[i]=X;
  LoadDR();			// Rückgabewert egal, sollte TRUE sein
  return i;
 }
 return -3;			// Kein Debugregister frei
}

int _stdcall FreeDRN(int dr) {	// Freigabe einer Debugregister-Nummer
 if ((unsigned)dr>=Usage_Len) return -4;// Ungültiges "Handle"
 if (!(Usage[dr]&0xFF00)) return -5;	// Debugregister nicht in Benutzung
 X4DR[dr]=NULL;
 UsageBits&=~(1<<dr);
 if (dr<4) {
  _asm{
  	mov	ecx,dr
	mov	edx,DR7
	mov	eax,0xFFFCFFF0	// Maske für DR0, HiWord/LoWord vertauscht
	shl	cl,1		// Nr. mal zwei
	shl	eax,cl		// HiWord (künftiges LoWord) richtig
	shl	ax,cl		// LoWord (künftiges HiWord) richtig
	ror	eax,16		// Vertauschen HiWord/LoWord
	and	eax,edx		// Bits weg, Debugregister frei
	mov	DR7,eax
	xor	eax,eax
	shr	cl,1		// wieder zurück
	jnz	no0
	mov	DR0,eax		// hübsch machen (nicht erforderlich)
no0:	loop	no1
	mov	DR1,eax
no1:	loop	no2
	mov	DR2,eax
no2:	loop	no3
	mov	DR3,eax
no3:
  }
  if (!(UsageBits&0xF)) {	// Kein Debugregister beteiligt?
   KeCancelTimer(&debset.tmr);	// Ganz zurückziehen
   UnhookInt1();
  }
 }
 Usage[dr]=0;			// Freigabe
 return dr;			// okay
}

/************************************************************
 ** Abfangen von READ_PORT_UCHAR und WRITE_PORT_UCHAR (NT) **
 ************************************************************/

static void __declspec(naked) FindAddr(void) {
//PE: DX=Portadresse
//PA: NZ wenn nicht gefunden
//    EDI=X4DR-Eintrag, ESI=Index, EDX unverändert
 _asm{	cmp	dh,1		// Niemals <100h trappen!
	jc	exi		// NZ!
	mov	eax,edx
	and	al,0xFC		// Vier aufeinanderfolgende Adressen
	push	Usage_Len
	pop	ecx
	cld			// ???
	mov	edi,offset Usage
	repne	scasw
	jne	exi
	push	Usage_Len-1
	pop	esi		// Index ermitteln
	sub	esi,ecx
	mov	edi,X4DR[esi*4]
	test	BYTE PTR[edi]DEVICE_EXTENSION.f,No_Function
exi:	ret
 }
}

static void __declspec(naked) MyWritePortUchar(PBYTE a,BYTE b) {
// Gleiche Signatur wie WRITE_PORT_UCHAR()
 _asm{	mov	edx,[esp+4]	// a
	mov	eax,[esp+8]	// b
	pushad
	 call	FindAddr
	 jnz	l1
	 inc	[edi]DEVICE_EXTENSION.ac.wpu
	 push	[esp+1Ch]	// EAX
	 push	edx
	 push	edi
	 call	HandleOut	// blockiert Thread gelegentlich
	popad
	ret	8
l1:
	popad
	out	dx,al
	ret	8
 }
}

static BYTE __declspec(naked) MyReadPortUchar(PBYTE a) {
// Gleiche Signatur wie READ_PORT_UCHAR()
 _asm{	mov	edx,[esp+4]	// a
	pushad
	 call	FindAddr
	 jnz	l1
	 inc	[edi]DEVICE_EXTENSION.ac.rpu
	 push	edx
	 push	edi
	 call	HandleIn	// blockiert Thread immer für ca. 1 ms
	 movzx	eax,al
	 mov	[esp+1Ch],eax	// Client_EAX (ganzes EAX wie Original)
	popad
	ret	4
l1:
	popad
	xor	eax,eax
	in	al,dx
	ret	4
 }
}

static BYTE save[10];	// Platz für die zwei weggepatchen JMP

extern void __declspec(naked) HookSyscalls(void) {
// Dieser Hook geht davon aus, dass kein anderer auf die gleiche Idee kommt!
// NICHT MULTIPROZESSOR-SICHER, weil anderer Prozessor gerade die zu patchende
// Funktion ausführen könnte (und ich weiß nicht, wie man ihn daran hindert)
 _asm{	mov	edx,cr0
	push	edx
	pushfd
	 btr	edx,16			// Supervisor Mode Write Protect
	 cli				// höchstes IRQL, keine ISR darf funken!
	 mov	cr0,edx
	 mov	edx,[WRITE_PORT_UCHAR]
	 mov	al,0E9h			// JMP rel.
	 xchg	[edx],al
	 mov	[save],al
	 mov	eax,offset MyWritePortUchar-5
	 sub	eax,edx
	 xchg	[edx+1],eax
	 mov	dword ptr[save+1],eax
	 mov	edx,[READ_PORT_UCHAR]
	 mov	al,0E9h			// JMP rel.
	 xchg	[edx],al
	 mov	[save+5],al
	 mov	eax,offset MyReadPortUchar-5
	 sub	eax,edx
	 xchg	[edx+1],eax
	 mov	dword ptr[save+6],eax
	popfd
	pop	edx
	mov	cr0,edx
	ret
 }
}

extern void __declspec(naked) UnhookSyscalls(void) {
// NICHT MULTIPROZESSOR-SICHER, weil anderer Prozessor gerade die zu patchende
// Funktion ausführen könnte (und ich weiß nicht, wie man ihn daran hindert)
 _asm{	mov	edx,cr0
	push	edx
	pushfd
	 btr	edx,16			// Supervisor Mode Write Protect
	 cli				// höchstes IRQL, keine ISR darf funken!
	 mov	cr0,edx
	 mov	edx,[WRITE_PORT_UCHAR]
	 mov	al,[save]
	 mov	[edx],al
	 mov	eax,dword ptr[save+1]
	 mov	[edx+1],eax
	 mov	edx,[READ_PORT_UCHAR]
	 mov	al,[save+5]
	 mov	[edx],al
	 mov	eax,dword ptr[save+6]
	 mov	[edx+1],eax
	popfd
	pop	edx
	mov	cr0,edx
	ret
 }
}

/***************************
 ** Portzugriffe und Trap **
 ***************************/
//#define CurXX 0xC0013E78
//#define CurVM 0xC0013EEC

void __declspec(naked) NewInt1(void) {
 _asm{	push	0		// "Fehlerkode"
 	pushad
	 mov	eax,DR6
	 mov	ecx,cs:[UsageBits]
	 and	ecx,0Fh
	 test	ecx,eax		// Zugriff auf eine "unserer" Adressen?
	 jnz	access03	// ja
	popad			// nein, andere Software ist dran
	add	esp,4
	jmp	fword ptr cs:[OldInt1]	//weiter (zu SoftICE o.ä.)
access03:
//bei jedem Portzugriff (LPT) steht in DX die Portadresse
//In AL steht das Datenbyte (nur bei OUT)
	 not	ecx
	 and	ecx,eax		// "Unsere" Bits in DR6 löschen
	 mov	DR6,ecx
	 bsf	esi,eax		// Merken, welches Debugregister es war
/* Kontext-Retten (Win9x) Start */
	cld
	mov	eax,cs:[CurThreadPtr]
	or	eax,eax
	jz	winnt
	mov	di,cs:[SaveDS]
	mov	eax,cs:[eax]	// Cur_Thread_Handle
	mov	ebp,esp		//CRS adressierbar machen (ab EIP -4!)
	test	byte ptr [ebp+0x2E],2	//V86-Mode?
	jz	protmode
//Opcode beschaffen; Adreßrechnung je nach V86- oder Protmode
// V86: Segmentregister sind schon auf dem Stack gerettet
	mov	bx,cs
	mov	es,bx
	movzx	ebx,word ptr [ebp+0x28]	//Real-Mode-CS
	shl	ebx,4
	add	ebx,[ebp+0x24]		//Real-Mode-Offset
	jmp	fromvm
//Annahme: Zugriff im Protected Mode mit lesbarem Code-Segment
protmode:
	mov	ebx,cs:[eax+0x14]	// Momentane Virtuelle Maschine CurVM
	cmp	ebp,cs:[ebx+8]	// VM_ClientRegs
// Etwas sauberer ist der Test des Kodesegment-Registers auf dem Stack...
	jnz	pm0		// Ungleich? Intra-Ring-Aufruf!
// PM3: Bei Aufruf aus Ring3 heraus die V86-Segmentregister nachbilden
	mov	[ebp+0x38],es
	mov	[ebp+0x3C],ds
	mov	[ebp+0x40],fs
	mov	[ebp+0x44],gs	// V86-kompatibel ablegen
	 les	ebx,fword ptr [ebp+0x24] //Code-Zeiger (FAR48)
// Stack-Umschaltung bei Ring-Übergang...
fromvm:
	 mov	ds,di
	 add	eax,0x4C
	 xchg	esp,[eax]	// Stack (nicht aber EBP!) umschalten
	 push	eax
	 jmp	saved
pm0:
// PM0: Keine Ring- und Stackumschaltung! Aber trotzdem Register retten!
	inc	dword ptr [ebp+0x20]	// Fehlerkode als Kennung missbrauchen
	push	ds
	push	es
	push	fs
	push	gs
	 les	ebx,fword ptr [ebp+0x24] //Code-Zeiger (FAR48)
	 mov	ds,di
saved:
	 mov	fs,[SaveFS]
	 mov	al,es:[ebx-1]		//Opcode (nur 1 Byte)
	 mov	es,di
	 mov	gs,di
/* Kontext-Retten (Win9x) Ende */
//DevExt-Zeiger beschaffen für Rückruf
	 mov	edi,X4DR[esi*4]
//Richtung des Portzugriffs (Lesen oder Schreiben) ermitteln
	 cmp	al,0xEE			//OUT dx,al
	 jne	no_outb
	 inc	[edi]DEVICE_EXTENSION.ac.out
	 sti
	 push	[ebp+0x1C]		//Client_EAX
	 push	edx
	 push	edi
	 call	HandleOut
	 jmp	supported		//mit AL=geschriebenes Byte
no_outb:
	 cmp	al,0xEC			//IN al,dx
	 jne	no_inb
	 inc	[edi]DEVICE_EXTENSION.ac.in
	 sti
	 push	edx
	 push	edi
	 call	HandleIn		//Thread blockieren...???
	 mov	[ebp+0x1C],al		//Byte modifizieren ("Client_AL")
	 jmp	supported
no_inb:
	 inc	[edi]DEVICE_EXTENSION.ac.fail
#if DBG
 	 int 3
#endif
supported:
	 cli
/* Kontext-Wiederherstellen (Win9x) Start */
	 bt	dword ptr [ebp+0x20],0	//Ohne Ringübergang?
	 jnc	trans
	pop	gs
	pop	fs
	pop	es
	pop	ds
	jmp	nopop
trans:
	 pop	eax
	 xchg	esp,[eax]	// Stack rückschalten
	 test	byte ptr [ebp+0x2E],2	//V86-Mode?
	 jnz	nopop
	mov	gs,[ebp+0x44]
	mov	fs,[ebp+0x40]
	mov	ds,[ebp+0x3C]
	mov	es,[ebp+0x38]
nopop:
/* Kontext-Wiederherstellen (Win9x) Ende */
	popad
	add	esp,4		// "Fehlerkode" übergehen
	iretd


winnt:
	mov	[esp+0x20],esi	// als "Fehlerkode" retten
	popad			// Stack umstapeln
	push	ebp		// NT ist bekloppt: Nutzt PUSHAD nicht
	push	ebx
	push	esi
	push	edi
	push	fs
	mov	fs,cs:[SaveFS]
	push	fs:dword ptr[0]
	//mov	fs:dword ptr[0],-1
	push	0
	push	eax
	push	ecx
	push	edx
	push	ds
	push	es
	push	gs
	 mov	di,cs:[SaveDS]
	 sub	esp,0x30
	 mov	ds,di
	 mov	ebp,esp
	 test	byte ptr[ebp+0x72],2	// V86?
	 jz	nt1
	 lea	esi,[ebp+0x7C]
	 lodsd
	 mov	[ebp+0x34],eax
	 lodsd
	 mov	[ebp+0x38],eax
	 lodsd
	 mov	[ebp+0x50],eax
	 lodsd
	 mov	[ebp+0x30],eax
	 mov	ax,cs
	 mov	es,ax
	 movzx	ebx,word ptr [ebp+0x6C]	//Real-Mode-CS
	 shl	ebx,4
	 add	ebx,[ebp+0x68]		//Real-Mode-Offset
	 jmp	nt2
nt1:
	 les	ebx,[ebp+0x68]
nt2:
	 mov	esi,fs:[0x124]	//TEB
	 add	esi,0x128
	 push	dword ptr[esi]	// alten Rahmenzeiger retten
	 mov	[esi],ebp	// neuen Rahmenzeiger setzen
	 mov	al,[ebp+0x6C]	//CS
	 and	al,1
	 mov	[esi+0x134-0x128],al	// Privileg setzen
	 push	esi
	 mov	esi,[ebp+0x64]
//-------------------------
	 mov	al,es:[ebx-1]		//Opcode (nur 1 Byte)
	 mov	es,di
	 mov	gs,di
//DevExt-Zeiger beschaffen für Rückruf
	 mov	edi,X4DR[esi*4]
//Richtung des Portzugriffs (Lesen oder Schreiben) ermitteln
	 cmp	al,0xEE			//OUT dx,al
	 jne	nt_no_outb
	 inc	[edi]DEVICE_EXTENSION.ac.out
	 sti
	 push	dword ptr[ebp+0x44]		//Client_EAX
	 push	edx
	 push	edi
	 call	HandleOut
	 jmp	nt_supported		//mit AL=geschriebenes Byte
nt_no_outb:
	 cmp	al,0xEC			//IN al,dx
	 jne	nt_no_inb
	 inc	[edi]DEVICE_EXTENSION.ac.in
	 sti
	 push	edx
	 push	edi
	 call	HandleIn		//Thread blockieren...???
	 mov	[ebp+0x44],al		//Byte modifizieren ("Client_AL")
	 jmp	nt_supported
nt_no_inb:
	 inc	[edi]DEVICE_EXTENSION.ac.fail
#if DBG
 	 int 3
#endif
nt_supported:
	 cli
//-------------------------
	 pop	esi
	 pop	dword ptr[esi]		// Rahmenzeiger wiederherstellen
	 add	esp,0x30
	test	byte ptr[ebp+0x72],2	// V86?
	jz	nt3
	add	esp,12
	jmp	nt4
nt3:	pop	gs
	pop	es
	pop	ds
nt4:	pop	edx
	pop	ecx
	pop	eax
	pop	ebx		// Dummy-Lesen
	pop	fs:dword ptr[0]
	pop	fs
	pop	edi
	pop	esi
	pop	ebx
	pop	ebp
	add	esp,4
	iretd
 }
}

Detected encoding: ANSI (CP1252)4
Wrong umlauts? - Assume file is ANSI (CP1252) encoded