Turns out, you have to perform "make modules" and "make modules_install" to create environment for building LKM. (Linux's so difficult..) The kernel directory specified from module makefile was Not the source folder of kernel. It was a folder created in "/lib/modules" folder by "make modules_install." Pretty interesting.
That "build" folder is what module makefile wanted..
Now if I compile using this makefile script...
obj-m := bin_handler.o
KERNEL_DIR = /lib/modules/6.7.4/build
SUBDIR = `pwd`
all:
$(MAKE) -C $(KERNEL_DIR) M=$(SUBDIR) modules
clean:
$(MAKE) -C $(KERNEL_DIR) M=$(SUBDIR) clean
.. and run the kernel and insert the module..
My first module!
Done! Now we have to make exe binary loader from scratch. :)
Let's start with small things. What we need to do is make functions that load the exe file, and register the function to the kernel. Adding the functions is simple - we just make a linux_binfmt structure and register using register_binfmt() function.
static int load_exe_binary(struct linux_binprm *bprm);
static int load_exe_library(struct file *file);
static int exe_core_dump(struct coredump_params *cprm);
static struct linux_binfmt exe_format = {
.module = THIS_MODULE ,
.load_binary = load_exe_binary ,
.load_shlib = load_exe_library,
#ifdef CONFIG_COREDUMP
.core_dump = exe_core_dump,
#endif
};
ngl I just copied this from linux elf loader
The essential functions are just left blank for now. Implementing the functions will be my first priority of this project.
// what actually matters most
static int load_exe_binary(struct linux_binprm *bprm) {
return 0;
}
#ifdef CONFIG_USELIB
static int load_exe_library(struct file *file) {
return 0;
}
#else
// left blank
static int load_exe_library(struct file *file) { return 0; }
#endif
static int exe_core_dump(struct coredump_params *cprm) {
return 0;
}
Registering the table is very simple; you just register the linux_binfmt structure in module init.
static int __init bin_module_init(void) {
printk(KERN_INFO"Registering my custom binfmt handler...");
register_binfmt(&exe_format);
if(sys_tbl_set_binary_handler(&exe_format , "mysyscalltable") == false) {
printk("Failed detecting suitable system call table!\n");
}
return 0;
}
static void __exit bin_module_exit(void) {
unregister_binfmt(&exe_format);
printk(KERN_INFO"Bye! from custom binfmt handler!");
}
module_init(bin_module_init);
module_exit(bin_module_exit);
MODULE_LICENSE("GPL");
Now we need to figure out what must handler do. If we don't know what we should make, we can do nothing but just larking around meaninglessly with codes.. I just made this test code to get a glance of how this works..
struct mz_file_header {
u16 signature;
u16 extra_bytes;
u16 page_count;
u16 header_size;
u16 minimum_allocation;
u16 maximum_allocation;
u16 initial_ss;
u16 initial_sp;
u16 checksum;
u16 initial_ip;
u16 initial_cs;
u16 relocation_table;
u16 overlay;
u16 overlay_info;
};
// what actually matters most
static int load_exe_binary(struct linux_binprm *bprm) {
struct mz_file_header header;
// check for basic information
memcpy(&header , bprm->buf , return 0;
}
This code just detects whether the file header's signature is the signature of exe file. If it is an exe file, it returns zero. If it's not, it returns ENOEXEC. The result was quite interesting. Obviously nothing happened, but the loader was detected and returned success.
A small step
Now I need to analyze the exe file format.. and modify the context switching code.