Skip to content
Commits on Source (4)
.*.cmd
*.o
*.ko
*.mod
*.mod.c
modules.order
Module.symvers
/bin
obj-m += src/
PWD := $(CURDIR)
all: module hax
module:
make -C /vm/9pfs/lkm-arch/root/lib/modules/6.0.8-arch1-1-tinyvm/build M=$(PWD) modules
hax: build-hax_read build-hax_exec build-set_cr
bin:
mkdir bin
%.o: src/%.c
gcc -o $@ -c $<
build-%: %.o bin
gcc -o bin/$* $<
obj-m += control-registers.o
control-registers-m := module_main.o register.o debug_file.o hax_file.o
#include "debug_file.h"
#include "register.h"
#include "register_ioctl.h"
static void to_bitstring(uint64_t val, char* out) {
out[64] = 0;
for(unsigned int i = 0; i < 64; i++) {
out[63 - i] = '0' + ((val >> i) & 1);
}
}
#define SPRINTF_BUF_SIZE 768
static ssize_t read_impl(struct file *f, char __user *ubuf, size_t buf_size, loff_t *u_pos) {
char temp_buf[SPRINTF_BUF_SIZE];
int temp_len;
int total_len = 0;
if(f->f_pos > 0) {
return 0;
}
temp_len = sprintf_cr0(get_cr0(), temp_buf, SPRINTF_BUF_SIZE);
if(copy_to_user(ubuf + total_len, temp_buf, temp_len) > 0) {
return -1;
}
total_len += temp_len;
temp_len = snprintf(temp_buf, SPRINTF_BUF_SIZE, "CR2: 0x%p", (void *)get_cr2());
temp_buf[temp_len++] = '\n';
temp_buf[temp_len++] = 0;
if(copy_to_user(ubuf + total_len, temp_buf, temp_len) > 0) {
return -1;
}
total_len += temp_len;
temp_len = sprintf_cr3(get_cr3(), temp_buf, SPRINTF_BUF_SIZE);
if(copy_to_user(ubuf + total_len, temp_buf, temp_len) > 0) {
return -1;
}
total_len += temp_len;
temp_len = sprintf_cr4(get_cr4(), temp_buf, SPRINTF_BUF_SIZE);
if(copy_to_user(ubuf + total_len, temp_buf, temp_len) > 0) {
return -1;
}
total_len += temp_len;
temp_len = snprintf(temp_buf, SPRINTF_BUF_SIZE, "CR8: ");
to_bitstring(get_cr8(), temp_buf + temp_len);
temp_len = strnlen(temp_buf, SPRINTF_BUF_SIZE);
temp_buf[temp_len++] = '\n';
temp_buf[temp_len++] = 0;
if(copy_to_user(ubuf + total_len, temp_buf, temp_len) > 0) {
return -1;
}
total_len += temp_len;
*u_pos += total_len;
return total_len;
}
static long ioctl_impl(struct file *f, unsigned int cmd, unsigned long arg) {
enum command command = (enum command)cmd;
switch(command) {
case COMMAND_WP:
set_wp(arg);
break;
case COMMAND_SMAP:
set_smap(arg);
break;
case COMMAND_SMEP:
set_smep(arg);
break;
default:
return -ENOTTY;
}
return 0;
}
struct file_operations debug_fops = {
.read = read_impl,
.unlocked_ioctl = ioctl_impl
};
#ifndef debug_file_h_INCLUDED
#define debug_file_h_INCLUDED
#include <linux/fs.h>
extern struct file_operations debug_fops;
#endif
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/param.h>
#include <unistd.h>
#include "hax_ioctl.h"
int function(int arg) {
// printf("Hello from Userspace :)! [arg=%d]\n", arg);
return 420;
}
int main(int argc, char **argv) {
int dev_fd = open("/dev/hack_registers", O_RDWR);
if(dev_fd == -1) {
printf("Error opening device file: %s\n", strerror(errno));
return -1;
}
if(ioctl(dev_fd, IOCTL_EXEC_USER, function) == -1) {
close(dev_fd);
printf("Error sending ioctl to device file: %s\n", strerror(errno));
return -2;
}
printf("Sending ioctl was successful!\n");
close(dev_fd);
return 0;
}
#include "hax_file.h"
#include "hax_ioctl.h"
/* static void to_bitstring(uint64_t val, char* out) {
out[64] = 0;
for(unsigned int i = 0; i < 64; i++) {
out[63 - i] = '0' + ((val >> i) & 1);
}
} */
#define SPRINTF_BUF_SIZE 768
static ssize_t read_impl(struct file *f, char __user *ubuf, size_t buf_size, loff_t *u_pos) {
// char temp_buf[SPRINTF_BUF_SIZE];
int temp_len;
int total_len = 0;
if(f->f_pos > 0) {
return 0;
}
temp_len = 0;
total_len += temp_len;
*u_pos += total_len;
return total_len;
}
static long ioctl_impl(struct file *f, unsigned int cmd, unsigned long arg) {
enum command command = (enum command)cmd;
switch(command) {
case COMMAND_READ_USER:
pr_info("User pointer: 0x%px\n", (void *)arg);
pr_info("%s", (user_buf)arg);
break;
case COMMAND_EXEC_USER:
pr_info("User pointer: 0x%px\n", (void *)arg);
pr_info("%d", ((user_fun)arg)(12));
break;
default:
return -ENOTTY;
}
return 0;
}
struct file_operations hax_fops = {
.read = read_impl,
.unlocked_ioctl = ioctl_impl
};
#ifndef hax_file_h_INCLUDED
#define hax_file_h_INCLUDED
#include <linux/fs.h>
extern struct file_operations hax_fops;
#endif
#ifndef hax_ioctl_h_INCLUDED
#define hax_ioctl_h_INCLUDED
enum state {
STATE_UNKNOWN = -1,
STATE_OFF = 0,
STATE_ON = 1
};
typedef char* user_buf;
typedef int (*user_fun)(int arg);
#define IOCTL_ID_HAX 69
#define IOCTL_READ_USER _IOW(IOCTL_ID_HAX, 0, user_buf)
#define IOCTL_EXEC_USER _IOW(IOCTL_ID_HAX, 1, user_fun)
enum command {
COMMAND_UNKNOWN = -1,
COMMAND_READ_USER = IOCTL_READ_USER,
COMMAND_EXEC_USER = IOCTL_EXEC_USER
};
#endif
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/param.h>
#include <unistd.h>
#include "hax_ioctl.h"
static char string[] = "Hello from Userspace :)!\n";
int main(int argc, char **argv) {
int dev_fd = open("/dev/hack_registers", O_RDWR);
if(dev_fd == -1) {
printf("Error opening device file: %s\n", strerror(errno));
return -1;
}
if(ioctl(dev_fd, IOCTL_READ_USER, string) == -1) {
close(dev_fd);
printf("Error sending ioctl to device file: %s\n", strerror(errno));
return -2;
}
printf("Sending ioctl was successful!\n");
close(dev_fd);
return 0;
}
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
MODULE_LICENSE("GPL");
MODULE_AUTHOR("wirklichniemand");
MODULE_DESCRIPTION("control-register test");
#include "debug_file.h"
static char debug_devname[] = "control_register_debug";
static int debug_devnum;
#include "hax_file.h"
static char hax_devname[] = "control_register_hax";
static int hax_devnum;
static int __init init_mod(void) {
pr_info("control-register test says hi!\n");
debug_devnum = register_chrdev(0, debug_devname, &debug_fops);
hax_devnum = register_chrdev(0, hax_devname, &hax_fops);
return 0;
}
static void __exit exit_mod(void) {
unregister_chrdev(debug_devnum, debug_devname);
unregister_chrdev(hax_devnum, hax_devname);
pr_info("control-register test says bai!\n");
}
module_init(init_mod);
module_exit(exit_mod);
#include "register.h"
static char str_true[] = "true";
static char str_false[] = "false";
static struct cr0_state parse_cr0(uint32_t val) {
return (struct cr0_state){
.protection_enable = (val >> 0) & 1,
.monitor_coprocessor = (val >> 1) & 1,
.emulation = (val >> 2) & 1,
.task_switched = (val >> 3) & 1,
.extension_type = (val >> 4) & 1,
.numeric_error = (val >> 5) & 1,
.write_protect = (val >> 16) & 1,
.alignment_mask = (val >> 18) & 1,
.no_write_through = (val >> 29) & 1,
.cache_disable = (val >> 30) & 1,
.paging = (val >> 31) & 1,
};
}
struct cr0_state get_cr0(void) {
uint64_t val = 0;
asm(
".intel_syntax\n"
"mov %0, %%cr0\n"
".att_syntax\n"
: "=r" (val)
:
:
);
return parse_cr0(val);
}
static char cr0_print_format[] =
"CR0:\n"
" - protection_enable: %s\n"
" - monitor_coprocessor: %s\n"
" - emulation: %s\n"
" - task_switched: %s\n"
" - extension_type: %s\n"
" - numeric_error: %s\n"
" - write_protect: %s\n"
" - alignment_mask: %s\n"
" - no_write_through: %s\n"
" - cache_disable: %s\n"
" - paging: %s\n";
int sprintf_cr0(struct cr0_state s, char *buf, ssize_t n) {
return snprintf(
buf, n, cr0_print_format,
s.protection_enable ? str_true : str_false,
s.monitor_coprocessor ? str_true : str_false,
s.emulation ? str_true : str_false,
s.task_switched ? str_true : str_false,
s.extension_type ? str_true : str_false,
s.numeric_error ? str_true : str_false,
s.write_protect ? str_true : str_false,
s.alignment_mask ? str_true : str_false,
s.no_write_through ? str_true : str_false,
s.cache_disable ? str_true : str_false,
s.paging ? str_true : str_false
);
};
uint64_t get_cr1(void) {
uint64_t val = 0;
asm(
".intel_syntax\n"
"mov %0, %%cr1\n"
".att_syntax\n"
: "=r" (val)
:
:
);
return val;
}
uint64_t get_cr2(void) {
uint64_t val = 0;
asm(
".intel_syntax\n"
"mov %0, %%cr2\n"
".att_syntax\n"
: "=r" (val)
:
:
);
return val;
}
static struct cr3_state parse_cr3(uint64_t val) {
return (struct cr3_state){
.page_level_write_through = (val >> 3) & 1,
.page_level_cache_disable = (val >> 4) & 1,
.page_directory_base = (void *)(val & 0xFFFFFFFFFFFFF000),
};
}
struct cr3_state get_cr3(void) {
uint64_t val = 0;
asm(
".intel_syntax\n"
"mov %0, %%cr3\n"
".att_syntax\n"
: "=r" (val)
:
:
);
return parse_cr3(val);
}
void set_cr3(uint64_t val) {
asm(
".intel_syntax\n"
"mov %%cr3, %0\n"
".att_syntax\n"
:
: "r" (val)
:
);
}
static char cr3_print_format[] =
"CR3:\n"
" - page_level_write_through: %s\n"
" - page_level_cache_disable: %s\n"
" - page_directory_base: 0x%p\n";
int sprintf_cr3(struct cr3_state s, char *buf, ssize_t n) {
return snprintf(
buf, n, cr3_print_format,
s.page_level_write_through ? str_true : str_false,
s.page_level_cache_disable ? str_true : str_false,
s.page_directory_base
);
};
static struct cr4_state parse_cr4(uint32_t val) {
return (struct cr4_state){
.virtual_mode_extensions = (val >> 0) & 1,
.protected_mode_virtual_interrupts = (val >> 1) & 1,
.time_stamp_disable = (val >> 2) & 1,
.debugging_extensions = (val >> 3) & 1,
.page_size_extensions = (val >> 4) & 1,
.physical_address_extension = (val >> 5) & 1,
.machine_check_enable = (val >> 6) & 1,
.page_global_enable = (val >> 7) & 1,
.perf_mon_counter_enable = (val >> 8) & 1,
.os_unmasked_simd_float_exceptions = (val >> 10) & 1,
.user_mode_instruction_prevention = (val >> 11) & 1,
.linear_addres_57_bit = (val >> 12) & 1,
.vmx_enable = (val >> 13) & 1,
.smx_enable = (val >> 14) & 1,
.fsgbase_enable = (val >> 16) & 1,
.pcid_enable = (val >> 17) & 1,
.extended_states_enable = (val >> 18) & 1,
.key_locker_enable = (val >> 19) & 1,
.smep_enable = (val >> 20) & 1,
.smap_enable = (val >> 21) & 1,
.user_mode_page_protection_keys = (val >> 22) & 1,
.control_flow_enforcement_enable = (val >> 23) & 1,
.supervisor_page_protection_keys = (val >> 24) & 1,
};
}
struct cr4_state get_cr4(void) {
uint64_t val = 0;
asm(
".intel_syntax\n"
"mov %0, %%cr4\n"
".att_syntax\n"
: "=r" (val)
:
:
);
return parse_cr4(val);
}
static char cr4_print_format[] =
"CR4:\n"
" - virtual_mode_extensions: %s\n"
" - protected_mode_virtual_interrupts: %s\n"
" - time_stamp_disable: %s\n"
" - debugging_extensions: %s\n"
" - page_size_extensions: %s\n"
" - physical_address_extension: %s\n"
" - machine_check_enable: %s\n"
" - page_global_enable: %s\n"
" - perf_mon_counter_enable: %s\n"
" - os_unmasked_simd_float_exceptions: %s\n"
" - user_mode_instruction_prevention: %s\n"
" - linear_addres_57_bit: %s\n"
" - vmx_enable: %s\n"
" - smx_enable: %s\n"
" - fsgbase_enable: %s\n"
" - pcid_enable: %s\n"
" - extended_states_enable: %s\n"
" - key_locker_enable: %s\n"
" - smep_enable: %s\n"
" - smap_enable: %s\n"
" - user_mode_page_protection_keys: %s\n"
" - control_flow_enforcement_enable: %s\n"
" - supervisor_page_protection_keys: %s\n";
int sprintf_cr4(struct cr4_state s, char *buf, ssize_t n) {
return snprintf(
buf, n, cr4_print_format,
s.virtual_mode_extensions ? str_true : str_false,
s.protected_mode_virtual_interrupts ? str_true : str_false,
s.time_stamp_disable ? str_true : str_false,
s.debugging_extensions ? str_true : str_false,
s.page_size_extensions ? str_true : str_false,
s.physical_address_extension ? str_true : str_false,
s.machine_check_enable ? str_true : str_false,
s.page_global_enable ? str_true : str_false,
s.perf_mon_counter_enable ? str_true : str_false,
s.os_unmasked_simd_float_exceptions ? str_true : str_false,
s.user_mode_instruction_prevention ? str_true : str_false,
s.linear_addres_57_bit ? str_true : str_false,
s.vmx_enable ? str_true : str_false,
s.smx_enable ? str_true : str_false,
s.fsgbase_enable ? str_true : str_false,
s.pcid_enable ? str_true : str_false,
s.extended_states_enable ? str_true : str_false,
s.key_locker_enable ? str_true : str_false,
s.smep_enable ? str_true : str_false,
s.smap_enable ? str_true : str_false,
s.user_mode_page_protection_keys ? str_true : str_false,
s.control_flow_enforcement_enable ? str_true : str_false,
s.supervisor_page_protection_keys ? str_true : str_false
);
};
uint64_t get_cr8(void) {
uint64_t val = 0;
asm(
".intel_syntax\n"
"mov %0, %%cr8\n"
".att_syntax\n"
: "=r" (val)
:
:
);
return val;
}
void set_wp(bool state) {
pr_info("set_wp(%s)\n", state?"true":"false");
if(state) {
asm(
".intel_syntax\n"
"mov %%rax, %%cr0\n"
"or %%rax, 0b10000000000000000\n"
"mov %%cr0, %%rax\n"
".att_syntax\n"
:
:
: "rax"
);
} else {
asm(
".intel_syntax\n"
"mov %%rax, %%cr0\n"
"and %%rax, 0xFFFFFFFFFFFEFFFF\n"
"mov %%cr0, %%rax\n"
".att_syntax\n"
:
:
: "rax"
);
}
return;
}
void set_smap(bool state) {
pr_info("set_smap(%s)\n", state?"true":"false");
if(state) {
asm(
".intel_syntax\n"
"mov %%rax, %%cr4\n"
"or %%rax, 0b1000000000000000000000\n"
"mov %%cr4, %%rax\n"
".att_syntax\n"
:
:
: "rax"
);
} else {
asm(
".intel_syntax\n"
"mov %%rax, %%cr4\n"
"and %%rax, 0xFFFFFFFFFFDFFFFF\n"
"mov %%cr4, %%rax\n"
".att_syntax\n"
:
:
: "rax"
);
}
return;
}
void set_smep(bool state) {
pr_info("set_smep(%s)\n", state?"true":"false");
if(state) {
asm(
".intel_syntax\n"
"mov %%rax, %%cr4\n"
"or %%rax, 0b1000000000000000000000\n"
"mov %%cr4, %%rax\n"
".att_syntax\n"
:
:
: "rax"
);
} else {
asm(
".intel_syntax\n"
"mov %%rax, %%cr4\n"
"and %%rax, 0xFFFFFFFFFFEFFFFF\n"
"mov %%cr4, %%rax\n"
".att_syntax\n"
:
:
: "rax"
);
}
return;
}
#ifndef register_h_INCLUDED
#define register_h_INCLUDED
#include <linux/module.h>
struct cr0_state {
bool protection_enable;
bool monitor_coprocessor;
bool emulation;
bool task_switched;
bool extension_type;
bool numeric_error;
bool write_protect;
bool alignment_mask;
bool no_write_through;
bool cache_disable;
bool paging;
};
struct cr0_state get_cr0(void);
int sprintf_cr0(struct cr0_state s, char *buf, ssize_t n);
uint64_t get_cr1(void);
uint64_t get_cr2(void);
struct cr3_state {
bool page_level_write_through;
bool page_level_cache_disable;
void *page_directory_base;
};
struct cr3_state get_cr3(void);
void set_cr3(uint64_t val);
int sprintf_cr3(struct cr3_state s, char *buf, ssize_t n);
struct cr4_state {
bool virtual_mode_extensions;
bool protected_mode_virtual_interrupts;
bool time_stamp_disable;
bool debugging_extensions;
bool page_size_extensions;
bool physical_address_extension;
bool machine_check_enable;
bool page_global_enable;
bool perf_mon_counter_enable;
bool os_unmasked_simd_float_exceptions;
bool user_mode_instruction_prevention;
bool linear_addres_57_bit;
bool vmx_enable;
bool smx_enable;
bool fsgbase_enable;
bool pcid_enable;
bool extended_states_enable;
bool key_locker_enable;
bool smep_enable;
bool smap_enable;
bool user_mode_page_protection_keys;
bool control_flow_enforcement_enable;
bool supervisor_page_protection_keys;
};
struct cr4_state get_cr4(void);
int sprintf_cr4(struct cr4_state s, char *buf, ssize_t n);
uint64_t get_cr8(void);
struct efer_state {
bool syscall_enable;
bool linear_mode_enable;
bool linear_mode_active;
bool execute_disable;
};
struct efer_state get_efer(void);
int sprintf_efer(struct efer_state s, char *buf, ssize_t n);
void set_wp(bool state);
void set_smap(bool state);
void set_smep(bool state);
#endif
#ifndef register_ioctl_h_INCLUDED
#define register_ioctl_h_INCLUDED
enum state {
STATE_UNKNOWN = -1,
STATE_OFF = 0,
STATE_ON = 1
};
#define IOCTL_ID_REGISTER 42
#define IOCTL_SET_WP _IOW(IOCTL_ID_REGISTER, 0, enum state)
#define IOCTL_SET_SMAP _IOW(IOCTL_ID_REGISTER, 1, enum state)
#define IOCTL_SET_SMEP _IOW(IOCTL_ID_REGISTER, 2, enum state)
enum command {
COMMAND_UNKNOWN = -1,
COMMAND_WP = IOCTL_SET_WP,
COMMAND_SMAP = IOCTL_SET_SMAP,
COMMAND_SMEP = IOCTL_SET_SMEP
};
#endif
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/param.h>
#include <unistd.h>
#include "register_ioctl.h"
enum state parse_state(char *state_in) {
static const char state_off[] = "off";
static const char state_on[] = "on";
static size_t state_off_len = sizeof(state_off) - 1;
static size_t state_on_len = sizeof(state_on) - 1;
size_t state_in_len = strnlen(state_in, 16);
if(strncmp(state_off, state_in, MAX(state_off_len, state_in_len)) == 0) {
return STATE_OFF;
} else if(strncmp(state_on, state_in, MAX(state_on_len, state_in_len)) == 0) {
return STATE_ON;
}
return STATE_UNKNOWN;
}
enum command parse_command(char *command_in) {
static const char command_wp[] = "wp";
static const char command_smap[] = "smap";
static const char command_smep[] = "smep";
static size_t command_wp_len = sizeof(command_wp) - 1;
static size_t command_smap_len = sizeof(command_smap) - 1;
static size_t command_smep_len = sizeof(command_smep) - 1;
size_t command_in_len = strnlen(command_in, 16);
if(strncmp(command_wp, command_in, MAX(command_wp_len, command_in_len)) == 0) {
return COMMAND_WP;
} else if(strncmp(command_smap, command_in, MAX(command_smap_len, command_in_len)) == 0) {
return COMMAND_SMAP;
} else if(strncmp(command_smep, command_in, MAX(command_smep_len, command_in_len)) == 0) {
return COMMAND_SMEP;
}
return COMMAND_UNKNOWN;
}
int main(int argc, char **argv) {
if(argc < 3) {
printf("Error: Not enough arguments!\n");
return -1;
}
enum command command = parse_command(argv[1]);
if(command == COMMAND_UNKNOWN) {
printf("Error: Command not recognized (%s)!\n", argv[1]);
return -2;
}
enum state state = parse_state(argv[2]);
if(state == STATE_UNKNOWN) {
printf("Error: State not recognized (%s)!\n", argv[2]);
return -3;
}
int dev_fd = open("/dev/control_registers", O_RDWR);
if(dev_fd == -1) {
printf("Error opening device file: %s\n", strerror(errno));
return -4;
}
if(ioctl(dev_fd, command, state) == -1) {
close(dev_fd);
printf("Error sending ioctl to device file: %s\n", strerror(errno));
return -5;
}
printf("Sending ioctl was successful!\n");
close(dev_fd);
return 0;
}