[Win32] Entering Ring 0 (Kernel Mode)!

Post your FreeBASIC source, examples, tips and tricks here. Please don’t post code without including an explanation.
Post Reply
Cherry
Posts: 358
Joined: Oct 23, 2007 12:06
Location: Austria
Contact:

[Win32] Entering Ring 0 (Kernel Mode)!

Post by Cherry »

This is some sample code which shows how it is possible to execute a sub with kernel privileges (in this example, access to cr0 is shown, but some other things could easily be done to, like "Asm hlt" in order to freeze the computer!).

Code: Select all

#Include "windows.bi"

Dim Shared NtSystemDebugControl As Function(As Integer, As Any Ptr, As Dword, As Any Ptr, As Dword, As Dword Ptr) As Long

Type CALL_GATE Field = 1
	addrlo As Word
	Seg As Word
	arg:5 As Byte
	u:3 As Byte
	typ:5 As Byte
	dpl:2 As Byte
	pres:1 As Byte
	addrhi As Word
End Type

Type gdtrStruct Field = 1
	limit As Word
	base As Dword
End Type

Type VIRTUAL1 Field = 1
	A As Any Ptr
	B As Any Ptr
	C As Dword
End Type

#Define Virtual(_a_, _b_, _c_, _d_) Scope: Var v = Type<VIRTUAL1>((_a_), (_b_), (_c_)): NtSystemDebugControl((_d_), @v, SizeOf(v), 0, 0, 0): End Scope

' This sub will be executed in kernel mode!
Sub Ring0(cs As Dword, text As ZString Ptr)
	*text = !"Hello World from Ring 0 \n"
	' Store value of control register 0 in eax (which is normally not allowed) in order to prove we are in Ring 0!
	Asm mov eax, cr0
	' Return to Ring 3
	Asm
		leave
		.byte 0xCA ' retf 4
		.word 4
	End Asm
End Sub

Function Main() As Integer
	NtSystemDebugControl = GetProcAddress(LoadLibrary("ntdll"), "NtSystemDebugControl")
	
	Dim As TOKEN_PRIVILEGES pv, po
	pv.PrivilegeCount = 1
	pv.Privileges(0).Attributes = SE_PRIVILEGE_ENABLED
	Dim As HANDLE t
	Dim As Dword no
	
	' This will enable NtSystemDebugControl usage
	LookupPrivilegeValue(0, SE_DEBUG_NAME, @pv.Privileges(0).Luid)
	OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, @t)
	AdjustTokenPrivileges(t, 0, @pv, SizeOf(po), @po, @no)
	
	' This ensures that on multi cpu/core systems we patch the right GDT for right cpu
	SetThreadAffinityMask(GetCurrentThread(), 1)
	SleepEx(100, FALSE)
	
	' We read GDT table
	Dim As LDT_ENTRY gdt(1000)
	Dim As gdtrStruct gdtr
	Asm sgdt [gdtr]
	
	' Find free spot
	Virtual(gdtr.base, @gdt(0), gdtr.limit, 8)
	Dim As Integer gate
	For i As Integer = 1 To 99
		If gdt(i).HighWord.Bits.Pres = 0 Then
			gate = i
			Exit For
		EndIf
	Next
	
	' Construct Call Gate pointing to Ring0 proc and write it there
	Var addr = CUInt(@Ring0)
	Dim As CALL_GATE g = Type(addr And &hFFFF, 8, 1, 0, 12, 3, 1, addr Shr 16)
	Virtual(gdtr.base + gate * 8, @g, 8, 9)
	
	' Quite ugly way to do far call 
	Dim As Word farcall(3) = {0, 0, gate Shl 3}
	Dim As ZString Ptr param = CAllocate(100)
	Dim As Long result
	
	' Switch from Ring 3 to Ring 0 is just normal call ;)
	Asm
		push [param]
		call fword Ptr [farcall]
		mov [result], eax
	End Asm
	
	' Cleanup Call Gate from GDT
	Dim As LongInt c = 0
	Virtual(gdtr.base + gate * 8, @c, 8, 9)
	
	Print !"\n" & *param & !"\nCR0 = " & Hex(result, 8)
	Sleep
	
	DeAllocate(param)
	
	Return 0
End Function

End(Main())
USE AT YOUR OWN RISK!!!

greetings, Cherry
BasicScience
Posts: 489
Joined: Apr 18, 2008 4:09
Location: Los Angeles, CA
Contact:

Post by BasicScience »

Could this be used to turn off all maskable interrupts, so the CPU would remain on a single dedicated task?

I ask because that was the strategy frequently used for A/D, D/A running at 10 KHz or higher under DOS. A fixed-length A/D D/A task was set-up, and the CPU was dedicated to acquisition/stimulus until the task was completed. Then interrupts were enabled. Nowadays, we have to use I/O boards with huge FIFO buffers to deal with the erratic processor availability under Windows. I've been told kernel privileges would be needed to write a routine for uninterrupted I/O under Windows. (Then again, maybe it is easier to fork-out a couple of hundred bucks for a decent I/O board with FIFO buffers).
Cherry
Posts: 358
Joined: Oct 23, 2007 12:06
Location: Austria
Contact:

Post by Cherry »

Yes, of course.

You can execute ANY instructions. The code inside the sub "Ring0" has the same rights as the Windows kernel itself! There are no "privileged instructions" any more, all are allowed.
1000101
Posts: 2556
Joined: Jun 13, 2005 23:14
Location: SK, Canada

Post by 1000101 »

BasicScience, when a hardware service request happens (int 0-15), the CPU must signal that it is done performing the interrupt request (iret). No other interrupts will occur until the CPU is done servicing the active one (nothing can interrupt a hardware interrupt). iirc from the good-old-dos-days.
MichaelW
Posts: 3500
Joined: May 16, 2006 22:34
Location: USA

Post by MichaelW »

For hardware interrupts on a PC the interrupt controller enforces priorities. Assuming that the CPU has hardware interrupts enabled, a higher-priority interrupt can interrupt a lower-priority interrupt. AFAIK the interrupt priorities are programmable, and programs that used the serial ports at high data rates often increased the serial port interrupt’s priority. Under Windows, the intended method of improving the response time or work rate for a process or thread that performs time-critical tasks is to increase the priority level. Windows provides 31 priority levels, with 1 being the lowest, 7 being normal and 31 being the highest. The threads of a process operating at REALTIME_PRIORITY_CLASS (levels 16-31) can effectively monopolize the system. I would be surprised if the Windows serial port services did not take advantage of an increased priority level.
BasicScience
Posts: 489
Joined: Apr 18, 2008 4:09
Location: Los Angeles, CA
Contact:

Post by BasicScience »

@1000101,

I think the timing issue I was facing related to polling for events with software loop (not hardware IRQ), and the jitter created by using such a simple-minded approach.

The timing issues were at two different levels:

1) During low-level A/D, D/A for individual sample/Output at 10 - 20 KHz in ASM using IN AL, DX and OUT DX,AL, then polling for A/D done conversion bit to get the next sample. This was done on a proprietary A/D, D/A, Clk board (LabMaster from Scientific Solutions, not the serial port). The loop timing was controlled by waiting for a ClkOvflow (then check A/D conv done, then get sample.. etc). Anyway, to avoid A/D overflow (ie triggering the next A/D start before getting the current value)... it was necessary to mask interrupts with CLI.

2) At QB level code, another timing loop was used to control the start for a bust of A/D , D/A. For example, do 1024 Sample/Output at 10 KHz, then wait 200 msec and repeat. It was possible to achieve +/- 1 msec accuracy for the repeat interval under DOS.

Now with Windows.... I was completely blown away by task #1, and have relied on commercial packages that provide DLL drivers, which I am able access from FB thanks to help from this forum. Task #2 is something I would like to solve more elegantly with FB. The klutzy work-around I use is to set up a HUGE buffer, say to sample for 60 sec at 20 KHz, then save to disk those portions of the buffer that I really want. All I really needed was short 20 msec epochs of Sample/Output at 50 KHz, say once every 100 msec, but to have these epochs start within 1 msec precision. With compiled FB using SLEEP or TIMER and polling for elapsed time to start the next acquisition burst, I get rare pauses of up to 5 - 10 msec because WinXP goes off on some tangent.
Rens
Posts: 256
Joined: Jul 06, 2005 21:09

Post by Rens »

@Cherry,

I am wondering if you can peek addresses in the low memory with your code. I mean something like peeking address &H4F0 which is the intra-application communications area. I think i can do some useful stuff with it.

If this is possible can you give me an example of how to do this.
MichaelW
Posts: 3500
Joined: May 16, 2006 22:34
Location: USA

Post by MichaelW »

I posted a relatively simple method here:

http://www.freebasic.net/forum/viewtopic.php?t=11014
12val12newakk
Posts: 35
Joined: Nov 14, 2019 17:04

Re: [Win32] Entering Ring 0 (Kernel Mode)!

Post by 12val12newakk »

in win 10 x64 ltsc
Compiler output:
C:\FreeBASIC\BAS\FBIDETEMP.bas(26) error 4: Duplicated definition, Virtual in '#Define Virtual(_a_, _b_, _c_, _d_) Scope: Var v = Type<VIRTUAL1>((_a_), (_b_), (_c_)): NtSystemDebugControl((_d_), @v, SizeOf(v), 0, 0, 0): End Scope'
C:\FreeBASIC\BAS\FBIDETEMP.bas(53) warning 4(1): Suspicious pointer assignment
C:\FreeBASIC\BAS\FBIDETEMP.bas(76) error 17: Syntax error, found 'Virtual' in 'Virtual(gdtr.base, @gdt(0), gdtr.limit, 8)'
C:\FreeBASIC\BAS\FBIDETEMP.bas(88) error 17: Syntax error, found 'Virtual' in 'Virtual(gdtr.base + gate * 8, @g, 8, 9)'
C:\FreeBASIC\BAS\FBIDETEMP.bas(104) error 17: Syntax error, found 'Virtual' in 'Virtual(gdtr.base + gate * 8, @c, 8, 9)'
srvaldez
Posts: 3373
Joined: Sep 25, 2005 21:54

Re: [Win32] Entering Ring 0 (Kernel Mode)!

Post by srvaldez »

if you rename Virtual to something like Virtual_ it will compile with warnings in 32-bit but not in 64-bit
it seem to be a translation of https://www.codeproject.com/Articles/27 ... c-approach
Post Reply