Source file: /~heha/basteln/PC/USB2LPT/usb2lpt.zip/src/sys/amd64.asm

; Hilfs-Quelltext für den fehlenden Inline-Assembler bei AMD64-Plattform des C-Compilers.
; Nur Debugregister-Anzapfung - die HAL von AMD64 hat keine Portzugriffsfunktionen.
; Funktioniert natürlich noch nicht! Die ISR muss erst disassembliert werden.
; Und, nicht zu vergessen, der PatchGuard ist auch noch abzuschießen!!
; Bis zu Windows 7 scheint es da brauchbare Lösungen zu geben (2015)
; Da mir jedoch kein einziger Fall eines lohnenswerten
; closed-source-64-Bit-Zugriffstreiber bekannt ist, bleibt's ungelöst.

.data?
Usage_Len	equ	32
extern Usage:word		;Array[32]
; Der Wert 0 bedeutet: freies Debugregister
; Der Wert 1 weist beim Treiber-Start besetzte Debugregister aus
; (diese werden jedoch bei UCB_ForceDebReg trotzdem verwendet)
; Ungerade Zahlen (Bit 0 gesetzt) für temporär ausgeschaltete Portadressen
; Die Indizes 0..3 gelten für DR0..DR3, alle anderen sind Reservepositionen
; Dieses Array kann mit "rep scasw" schnell nach dem passenden Index durchsucht werden.

OldInt1		dq	?
OldInt2E	dq	?	;Wird der noch verwendet?
OldSysCall	dq	?

SaveGS		dw	?	; Zeiger KPCR
		dw	?	; Ausrichtung
extern UsageBits:dword		; 0-Bits für temporär ausgeschaltete Portadressen
extern DebRegStolen:dword	;3 globale Zähler für "geklaute" Debugregister
				; Index 0: Timer
				; Index 1: Int2E
				; Index 2: SysCall
.code
extern DispatchHook:proc	;Parameter: CL=Index (0..3), DX=Portadresse
	;Weiterhin: R8B = Typ, R9 = Zeiger zu Quell/Zieldaten, Stack = Iterationen

;*****************************
;** Debug-Register und Int1 **
;*****************************/
; Auf Mehrprozessormaschinen müssen in _allen_ Prozessoren
; die Debugregister gesetzt/gelesen/geprüft werden.

; IRQL >= DISPATCH_LEVEL
; Der KDPC (1. Argument) wird nicht mehr benötigt.
;void EachProcessorDpc(KDPC*Dpc, PVOID Context, PVOID Arg1, PVOID Arg2)
EachProcessorDpc proc
	push	r9			;Arg2 = Zeiger auf Affinitätsmaske
	 mov	rcx,r8			;Arg1 = Argument
	 call	rdx			;Context = Prozedurzeiger
	pop	rcx
	movzx	eax,byte ptr gs:[184h]	;KeGetCurrentProcessorNumber() für AMD64
	lock btr dword ptr[rcx],eax	;Für diesen Prozessor als erledigt markieren
	ret
EachProcessorDpc endp


; IRQL == DISPATCH_LEVEL! (Prozessor darf nicht wechseln.)
; PE: CL = Interrupt-Nummer
; PA: RAX = Adresse aus IDT (des aktuellen Prozessors)
; VR: EAX,ECX
;void* GetIdtAddr(UCHAR nr)
GetIdtAddr proc private
	movzx	rax,cl
	shl	eax,4
	mov	rcx,gs:[56]		;KPCR.IdtBase
	add	rcx,rax
	mov	eax,[rcx+8]		;High-Teil
	shl	rax,16
	mov	ax,[rcx+6]		;vorletztes WORD
	shl	rax,16
	mov	ax,[rcx+0]		;letztes WORD
	ret
GetIdtAddr endp

; IRQL == DISPATCH_LEVEL! (Prozessor darf nicht wechseln.)
;void SetIdtAddr(UCHAR nr)
; PE: RAX=neue Adresse für INT
;     CL: Interrupt-Nummer
; PA: -
; VR: RCX,RDX,RAX,R8
SetIdtAddr proc private
	movzx	rdx,cl
	mov	rcx,cr0
	shl	edx,4
	mov	r8,rcx
	btr	rcx,16			; Supervisor Mode Write Protect
	mov	cr0,rcx
	mov	rcx,gs:[56]		;KPCR.IdtBase
	add	rcx,rdx
	mov	[rcx+0],ax		;unterstes WORD
	shr	rax,16
	mov	[rcx+6],ax		;nächstes WORD
	shr	rax,16
	mov	[rcx+8],eax		;High-Teil
	mov	cr0,r8
	ret
SetIdtAddr endp

; IRQL == DISPATCH_LEVEL! (Prozessor darf nicht wechseln.)
;void GetInts(void)
GetInts proc
	push	rdi
	 mov	cl,1
	 call	GetIdtAddr
	 lea	rdx,[NewInt1]
	 cmp	rax,rdx			; schon gehookt?
	 je	@f			; nichts tun! (Schleifen vermeiden)
	 lea	rdi,[OldInt1]
	 stosq
	 mov	cl,2Eh
	 call	GetIdtAddr
	 stosq
	 mov	ecx,0C0000082h		; IA32_LSTAR
	 rdmsr
	 stosq
@@:	pop	rdi
	ret
GetInts endp


;void SetSysEnter(void)
;PE: RAX = zu setzende Adresse für SysEnter-Befehl
SetSysEnter proc private
	mov	rcx,0C0000082h		; IA32_LSTAR
	xor	rdx,rdx			; sonst GPF!
	wrmsr
	ret
SetSysEnter endp


; IRQL == DISPATCH_LEVEL! (Prozessor darf nicht wechseln.)
;void HookInts(int unused)
; Faule Annahme: bei INT1 befindet sich bereits ein gültiger Gate-Deskriptor
; VR: RAX,RDX,RCX,R8; EFlags unverändert
; Bei Hyperthreading hat jeder Prozessor eine eigene IDT.
HookInts proc
	pushfq
	 cli
	 cmp	OldInt1,0
	 je	@f
	 lea	rax,[NewInt1]		; ISR-Anfangsadresse
	 mov	cl,1
	 call	SetIdtAddr
;	 lea	rax,[NewInt2E]
;	 mov	cl,2Eh
;	 call	SetIdtAddr
;	 lea	rax,[NewSysCall]
;	 call	SetSysEnter
@@:	popfq
	ret
HookInts endp

; IRQL == DISPATCH_LEVEL! (Prozessor darf nicht wechseln.)
;void UnhookInts(int unused)
; VR: RAX,RDX,RCX,R8; EFlags unverändert
UnhookInts proc
	pushfq
	 cli
	 push	rsi
	  lea	rsi,[OldInt1]
	  lodsq
	  or	rax,rax
	  jz	@f
	  mov	cl,1
	  call	SetIdtAddr
;	  lodsq
;	  mov	cl,2Eh
;	  call	SetIdtAddr
;	  lodsq
;	  call	SetSysEnter
@@:	 pop	rsi
	popfq
	ret
UnhookInts endp

; Nur sinnvoll mit IRQL >= DISPATCH_LEVEL!
;void cyLoadDR(void)
; Debugregister des aktuellen Prozessors laden/wiederherstellen, VR: RAX,RBX,RCX,RDX,RSI,Flags
; Liefert ECX!=0 wenn sich die Debugregister dabei verändern
cyLoadDR proc private
	xor	ecx,ecx		; Bit-Sammler
	lea	rsi,[Usage]	; im Ring 0 immer CLD
	mov	rax,cr4
	bts	eax,3		; PENTIUM Debug Extension (DE) aktivieren
	mov	cr4,rax
	setnc	cl		; CL=1 wenn Debug Extension ausgeschaltet war
	mov	rbx,DR7
	xor	rax,rax		; obere 48 Bits löschen
	lodsw			; 16 Bits laden
	or	ah,ah		; mit (gültiger) Adresse gefüllt?
	jz	@f
	mov	rdx,DR0
	mov	DR0,rax
	xor	edx,eax
	or	ecx,edx		; ECX!=0 wenn DR0 verändert wurde
	or	ebx,000E0202h
	btr	ebx,16		; DR7=xxxxxxxx xxxx1110 xxxxxx1x xxxxxx1x
@@:	lodsw
	or	ah,ah
	jz	@f
	mov	rdx,DR1
	mov	DR1,rax
	xor	edx,eax
	or	ecx,edx		; ECX!=0 wenn DR1 verändert wurde
	or	ebx,00E00208h
	btr	ebx,20		; DR7=xxxxxxxx 1110xxxx xxxxxx1x xxxx1xxx
@@:	lodsw
	or	ah,ah
	jz	@f
	mov	rdx,DR2
	mov	DR2,rax
	xor	edx,eax
	or	ecx,edx		; ECX!=0 wenn DR2 verändert wurde
	or	ebx,0E000220h
	btr	ebx,24		; DR7=xxxx1110 xxxxxxxx xxxxxx1x xx1xxxxx
@@:	lodsw
	or	ah,ah
	jz	@f
	mov	rdx,DR3
	mov	DR3,rax
	xor	edx,eax
	or	ecx,edx		; ECX!=0 wenn DR3 verändert wurde
	or	ebx,0E0000280h
	btr	ebx,28		; DR7=1110xxxx xxxxxxxx xxxxxx1x 1xxxxxxx
@@:	mov	rdx,DR7
	mov	DR7,rbx
	xor	edx,ebx
	or	ecx,edx		; ECX!=0 wenn DR7 verändert wurde
	ret
cyLoadDR endp

; Nur sinnvoll mit IRQL >= DISPATCH_LEVEL!
;BOOLEAN LoadDR(int unused)
LoadDR proc
	push	rsi
	push	rbx
	 call	cyLoadDR
	pop	rbx
	pop	rsi
	add	ecx,-1		; Returnwert nach CY (gesetzt wenn ECX!=0)
	setc	al		; für sog. Hochsprachen...
	ret
LoadDR endp


;void PrepareDR(void)
; PA: [OldInt.Int1]=Adresse von INT1
; Weiterhin SaveGS
iPrepareDR proc
	call	GetInts
	mov	[SaveGS],gs
; markiert die bereits vor dem Treiber-Start verwendeten Debugregister
; als "nicht verwendungsfähig für uns".  Aufzurufen beim Treiber-Start
	lea	rdx,[Usage]
	mov	rax,DR7
	push	4
	pop	rcx
@@:	test	al,3		; Lx oder Gx gesetzt? (Also in Benutzung?)
	setnz	byte ptr[rdx]	; wenn ja, auf 1 setzen, sonst 0 lassen
	shr	eax,2		; nächstes Debugregister
	add	rdx,2		; nächstes Usage-Word
	loop	@b
; KeInitializeTimer(&debset.tmr);
; KeInitializeDpc(&debset.dpc,SetDebDpc,NULL);
iPrepareDR endp

;********************************
;* API (öffentliche Funktionen) *
;********************************

; Scans the USHORT array for given USHORT and returns a pointer to entry; returns NULL if not found
;USHORT* ScanMemW(SIZE_T l /*RCX*/, const USHORT*p /*RDX*/, USHORT a /*R8*/)
ScanMemW proc
	xchg	rdx,rdi
	mov	eax,r8d
	repne	scasw
	lea	rax,[rdi-2]
	je	@f
	xor	rax,rax
@@:	xchg	rdi,rdx
	ret
ScanMemW endp

; Diese Routine wird mit ECX = Debugregister-Nummer (0..3) aufgerufen
; Nur sinnvoll mit IRQL >= DISPATCH_LEVEL!
;void UnloadDR(UCHAR debregnumber)
UnloadDR proc
	mov	rdx,DR7
	mov	eax,0FFFCFFF0h	; Maske für DR7, 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	edx,eax		; Bits weg, Debugregister frei
	mov	DR7,rdx
	xor	edx,edx		; High-Teil sollte Null sein
	shr	cl,1		; wieder zurück
	jnz	@f
	mov	DR0,rdx		; hübsch machen (nicht erforderlich, aber macht das Debuggen übersichtlicher)
@@:	loop	@f
	mov	DR1,rdx
@@:	loop	@f
	mov	DR2,rdx
@@:	loop	@f
	mov	DR3,rdx
@@:	ret
UnloadDR endp


;************************************************************
;** Abfangen von READ_PORT_UCHAR und WRITE_PORT_UCHAR (NT) **
;************************************************************
; Gibt's bei AMD64 gar nicht!!

;***************************
;** Portzugriffe und Trap **
;***************************

NewInt2E proc private
	pushfq
	push	rax
	push	rcx
	push	rdx
	push	rbx
	push	rsi
	 call	cyLoadDR
	 jnc	@f
	 lock inc [DebRegStolen+4]
@@:	pop	rsi
	pop	rbx
	pop	rdx
	pop	rcx
	pop	rax
	popfq
	jmp	OldInt2E
NewInt2E endp

NewSysCall proc private
	pushfq
	push	rax
	push	rcx
	push	rdx
	push	rbx
	push	rsi
	 call	cyLoadDR
	 jnc	@f
	 lock inc [DebRegStolen+8]
@@:	pop	rsi
	pop	rbx
	pop	rdx
	pop	rcx
	pop	rax
	popfq
	jmp	OldSysCall
NewSysCall endp

; Stackaufbau 	Vista64
;EBP+	Register-NT
;Der genaue Stackaufbau ist anscheinend bei Win64 nicht wichtig;
;es gibt keinen Bezug zu einer gepushten Struktur im KPCS, TEB o.ä. Windows-Strukturen.

;EFlags-Aufbau
;21 20  19  18 17 16 15 14 13-12 11 10  9  8  7  6 5  4 3  2 1  0
;ID VIP VIF AC VM RF  0 NT IOPL  OF DF IF TF SF ZF 0 AF 0 PF 1 CF

NewInt1 proc private
	push	rax
	push	rbp
	 mov	rbp,DR6
	 mov	eax,[UsageBits]
	 and	eax,0Fh		;nur die unteren 4 Bits repräsentieren Debugregister
	 and	eax,ebp
	 jnz	@f		;wir sind dran
	pop	rbp
	pop	rax
	jmp	OldInt1
;BUGBUG: Diese ISR funktioniert nicht für User-Mode-Traps! (swapgs fehlt)
;Ergibt ein Sicherheitsloch, wenn Portzugriffe möglich sein sollten.
;Dies würde jedoch wiederum ein verändertes TSS mit IOPM erfordern, vielleicht.
@@:	not	eax
	and	rax,rbp		; "Unsere" Bits in DR6 löschen
	mov	DR6,rax
	bsf	eax,ebp		; Merken, welches Debugregister es war (0..3) -> EAX
	xchg	[rsp+8],rax	; RAX restaurieren
	sub	rsp,0F0h
	push	r11
	push	r10
	push	r9
	push	r8
	push	rdx
	push	rcx
	push	rax
	lea	rbp,[rsp+50h]	;mir sin ma a bissel Kode sparen
	sub	rsp,28h		;Der Stack muss eine gewisse Ausrichtung aufweisen!
	mov	byte ptr[rbp-55h],1
	cld
	stmxcsr [rbp-54h]		;(Wird das Gleitkomma-Geraffel benötigt??)
	ldmxcsr gs:[180h]
	movdqa	[rbp-10h],xmm0
	movdqa	[rbp],xmm1
	movdqa	[rbp+10h],xmm2
	movdqa	[rbp+20h],xmm3
	movdqa	[rbp+30h],xmm4
	movdqa	[rbp+40h],xmm5
	sti
	mov	cl,[rbp+0E0h]		; Debugregister-Nummer (0..3), EDX stimmt schon/noch!
	mov	rax,[rbp+0E8h]		; RIP
	mov	ax,[rax-2]	;Opcode mit 1 Präfix - UNSAUBER, erwischt Mehrfachpräfixe sowie REP nicht
	cmp	al,066h
	setne	al		;1 wenn ohne Präfix
	xchg	ah,al
	cmp	al,0EEh		;OUT dx,al
	jz	io8
	cmp	al,0EFh		;OUT dx,eax oder OUT dx,ax
	jz	io16
	or	ah,16
	cmp	al,0ECh		;IN al,dx
	jz	io8
	cmp	al,0EDh		;IN eax,dx oder IN ax,dx
	jz	io16
	mov	ah,32		;unsupported!
	jmp	a2
io16:	add	ah,1		;1 (9) oder 2 (10)
	jmp	a2
io8:	and	ah,not 1	;Präfixtestergebnis töten
a2:	xchg	ah,al
	mov	r8b,al
	lea	r9,[rbp-50h]	;4. Parameter zeigt auf Client_RAX
	push	1		;5. Parameter stets 1
	call	DispatchHook
	ldmxcsr	[rbp-54h]
	movdqa	xmm0,[rbp-10h]
	movdqa	xmm1,[rbp]
	movdqa	xmm2,[rbp+10h]
	movdqa	xmm3,[rbp+20h]
	movdqa	xmm4,[rbp+30h]
	movdqa	xmm5,[rbp+40h]
	add	rsp,30h		;zusätzlich Stack von DispatchHook() aufräumen
	pop	rax
	pop	rcx
	pop	rdx
	pop	r8
	pop	r9
	pop	r10
	pop	r11
	add	rsp,0F0h
	pop	rbp
	add	rsp,8		; "Fehlercode" übergehen
	iretq

NewInt1 endp

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