I want to create a demo to explain processors rings and system calls to students. I was thinking in a presentation doing something like this:
I can use Linux or Windows, whatever is easier
Any ideas on where i can find some code that help me? what protected instructions could I use?
Thanks!
One attention-catching demonstration could be powering the computer off.
Unfortunately power management is a complex matter, it involves the ACPI specification that are rather abstract and long, further more they are not the first attempt.
ACPI is complex because different vendors require different actions to manage an aspect of the computer, anyway if you happen to have an Intel chipset (series 200 is recommended but other series should work too) we can skip most of the ACPI layer and use the datasheet instead.
You should check this program on the final hardware before using it in a class.
ACPI defines four global system state, G0-G3, where G3 is mechanical power off (i.e. the plug has been pulled, the battery removed) and G2 is soft-off.
Only G2 can be entered by the software, and this is done by entering the S5 sleep state.
The sleep state is controlled by the PCH (the Intel chipset) through an IO register (PM1a_CNT_BLK), this register is located in the ACPI block defined in the PCI configuration space of the device 31 function 2 (the PM controller).
One should read the base address of the block and then add four (4) to get the address of the register of interest.
I won't do this programmatically, instead the assembly program is expecting a symbol with that address.
To retrieve the address of the register PM1a_CNT_BLK one can use /proc/ioports as follow:
sudo cat /proc/ioports | grep 'PM1a_CNT_BLK' | cut -f3 -d' ' | cut -f1 -d'-'
This gives the hex address of the register. If none is printed out, than it is likely that the chipset is not supported.
In may case the address is 1804.
The sleep state is controlled by bits 10:12 (SLP_TYP) of the register and by bit 13 (SLP_EN).
SLP_TYP is a 3-bit value to select the state to enter (S5 is 7) and bit 13 is the enable bit.
The register has other value that must be preserved, so a read-modify-write operation must be done.
Using the in and out instructions is not possible a Ring 3 unless the TSS of the process has an IOPL (IO Privilege Level) of 3 (or the ports has been enabled in the IO ports map).
The IOPL tell which rings can use in and out, a value of X means all rings at X or less can.
This program attempt to power down the computer and optionally set the IOPL to the given value (through the symbol IOPL):
BITS 64
GLOBAL _start
SECTION .text
_start:
;Set the IOPL, only if greater than 0 (since 0 is the default)
%if IOPL > 0
lea rsi, [rsp-80h] ;We don't care about the pt_regs struct and we use the RED ZONE
mov edi, IOPL ;IOPL to set
mov eax, 172
syscall ;Set iopl
and eax, 0fh ;Just keep the last nibble, it can be 0 (success), 10 (invalid IOPL) or 15 (insufficient OS permissions)
test eax, eax ;Test for errors
mov edi, eax ;We exit with status 10 or 15 if the iopl syscall failed
jnz .exit
%endif
;Power off the PC
mov dx, PM1a_CNT_BLK
in eax, dx ;Read the current value
and eax, 0ffffc003h ;Clear SLP_TYP and SLP_EN
or eax, (7 << 10) | (1 << 13) ;Set SLP_TYP to 7 and SLP_EN to 1
out dx, eax ;Power off
;This is just for safety, execution should STOP BEFORE arriving here. This exits the process with status
;0
xor edi, edi
;Exit the process with a numerical status as specified in RDI
.exit:
mov eax, 60
syscall
It can be assembled with nasm po.asm -DPM1a_CNT_BLK=$1 -DIOPL=$2 -felf64 -o po.o where $1 is the port address of PM1a_CNT_BLK as found above but prefixed with 0x (in my case it becomes 0x1804) and $2 is a number (0-3) to set the IOPL to.
The IOPL is set iif it is not 0, since 0 is the default value (i.e. only Ring 0 can use in and out)
Note: Pass sensible values to the symbols or the program won't assemble.
This is interesting in the following ways:
in.in.I've made a git repository with the code and a script built.sh that you can use to build and run different version of the program.
This script is useful because it converts the exit status of po into an user-friendly string, suitable for experimenting.
The script expects the PM1a_CNT_BLK address as the first argument (with the 0x prefix) and the IOPL as the second one.
Use it like:
./build.sh 0x1804 0
./build.sh 0x1804 3
sudo ./build.sh 0x1804 2
sudo ./build.sh 0x1804 4
sudo ./build.sh 0x1804 3
Of course, change the register address.
The IOPL trick is just... a trick. It doesn't really make a program running at Ring 0, it is useful for debugging but not much more.
In order to run a code at Ring 0 you need an LKM (Loadable kernel module).
In the same repository I've included an lkm dir with an example of a LKM.
Upon loading the module attempt to shutdown the computer (instantaneously).
The code is minimal:
#include <linux/module.h> /* Needed by all modules */
#include <linux/kernel.h> /* Needed for KERN_INFO */
#include <linux/fs.h> /* Needed for KERN_INFO */
#include <asm/io.h> /* Needed for inl and outl */
#define PM1a_CNT_BLK 0x1804
unsigned char bytes[10];
int __init lkm_init(void)
{
unsigned int pm1a;
printk(KERN_INFO "I'm going to power the computer off");
pm1a = inl(PM1a_CNT_BLK);
pm1a = ( pm1a & 0xffffc003 ) | ( 7 << 10 ) | ( 1 << 13 );
outl(pm1a, PM1a_CNT_BLK);
printk(KERN_WARNING "Powering off failed");
return 0;
}
static void __exit lkm_exit(void)
{
}
module_init(lkm_init);
module_exit(lkm_exit);
MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("M.Bloom");
MODULE_DESCRIPTION("Attempt to power down the computer");
To make the LKM, first edit the PM1a_CNT_BLK define, then run make in the same dir (you'll need the kernel headers), the Makefile is standard one for the LKMs.
To load the module use insmod po as root (this is an OS security mechanism).
I've compiled but not tested this LKM since I had already started writing this answer.
You may eventually fix it, use dmesg to check the output of the module.
You can use the LKM as a skeleton to run code a Ring 0, though when dealing with memory you must be aware of how Linux handles virtual memory.
One final note, if you are going to check/use this programs, be sure to close all applications, run a sync and, if willing, switch to run level 1 (with systemd it is systemctl isolate rescue) or at least stop all the critical services.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With