I am currently trying to develop a Linux Ubuntu Kernel Module (as part of my task), which is basically installs a watchpoint on address, specified as a string parameter *(char ) to the module.
Currently, I am at the stage of assigning this address to required pointer, however... Every time I attempt to do it via incrementing or adding value of this address (previously converted from hex to decimal into int), I cannot assign this address correctly at all. I know there exists manual assigning, but that is not what I am told to do. Please, help me find at least some method to install exact address to a pointer, without hardcoding it.
By the way, the address changes every time I rebooted the machine, for some reason.
My code:
char *address = NULL;
volatile void *zero_address = 0;
volatile long long unsigned int actual_address;
**...**
if(starts_with(address, "0x"))
{
actual_address = simple_strtoul(address + 2, NULL, 16);
} else {
actual_address = simple_strtoul(address, NULL, 16);
}
printk(KERN_INFO "Address in int = %llu", actual_address);
for (volatile long long unsigned int i = 0; i < actual_address; i++, zero_address++);
// zero_address = zero_address + actual_address;
printk(KERN_INFO "Address converted = %p", zero_address);
write_attributes.bp_addr = (long long unsigned int) zero_address;
Full code of the program:
#include <linux/module.h> /* Needed by all modules */
#include <linux/kernel.h> /* Needed for KERN_INFO, KERN_ALERT */
#include <linux/init.h> /* Needed for the macros */
#include <linux/stat.h>
#include <linux/moduleparam.h> /* Needed for module_param */
#include <linux/perf_event.h>
#include <linux/hw_breakpoint.h>
#include <linux/version.h>
#include <linux/string.h>
#include <linux/math.h>
//https://tldp.org/LDP/lkmpg/2.6/lkmpg.pdf
//https://stackoverflow.com/questions/19725900/watch-a-variable-memory-address-change-in-linux-kernel-and-print-stack-trace
//https://github.com/xcellerator/linux_kernel_hacking/issues/3
#define DRIVER_AUTHOR "Denys Berezniuk"
#define DRIVER_DESC "Test Driver"
/*
* On Linux kernels 5.7+, kallsyms_lookup_name() is no longer exported,
* so we have to use kprobes to get the address.
* Full credit to @f0lg0 for the idea.
*/
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,7,0)
#define KPROBE_LOOKUP 1
#include <linux/kprobes.h>
static struct kprobe kp = {
.symbol_name = "kallsyms_lookup_name"
};
unsigned long int *kallsyms_lookup_name_custom(const char *);
#endif
MODULE_LICENSE("GPL");
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
struct perf_event * __percpu *write_watchpoint;
struct perf_event * __percpu *read_watchpoint;
char *address = NULL;
volatile void *zero_address = 0;
volatile long long unsigned int actual_address;
struct perf_event_attr write_attributes;
struct perf_event_attr read_attributes;
module_param(address, charp, /*0000*/ S_IRUGO /*| S_IWUGO*/);
MODULE_PARM_DESC(address, "Address to place a Watchpoint on");
static void write_handler(struct perf_event *bp, struct perf_sample_data *data, struct pt_regs *regs)
{
//printk(KERN_INFO "%s value is changed\n", address);
//dump_stack();
//printk(KERN_INFO "Dump stack from write_handler\n");
}
bool starts_with(const char *a, const char *b)
{
if(strncmp(a, b, strlen(b)) == 0) return 1;
return 0;
}
int __init initialize_driver(void)
{
#ifdef KPROBE_LOOKUP
/*register_kprobe(&kp);
kallsyms_lookup_name_custom = (unsigned long int (*)(const char *)) kp.addr;
unregister_kprobe(&kp);*/
typedef unsigned long (*kallsyms_lookup_name_t)(const char *name);
kallsyms_lookup_name_t kallsyms_lookup_name;
register_kprobe(&kp);
kallsyms_lookup_name = (kallsyms_lookup_name_t) kp.addr;
unregister_kprobe(&kp);
#endif
printk(KERN_ALERT "Driver loaded\nAddress is: '%s'\n", address);
hw_breakpoint_init(&write_attributes);
//write_attributes.bp_addr = kallsyms_lookup_name(address);
if(starts_with(address, "0x"))
{
actual_address = simple_strtoul(address + 2, NULL, 16);
} else {
actual_address = simple_strtoul(address, NULL, 16);
}
printk(KERN_INFO "Address in int = %llu", actual_address);
for (volatile long long unsigned int i = 0; i < actual_address; i++, zero_address++);
// zero_address = zero_address + actual_address;
printk(KERN_INFO "Address converted = %p", zero_address);
write_attributes.bp_addr = (long long unsigned int) zero_address;
write_attributes.bp_len = HW_BREAKPOINT_LEN_1;
write_attributes.bp_type = HW_BREAKPOINT_W; //| HW_BREAKPOINT_R;
write_watchpoint = register_wide_hw_breakpoint(&write_attributes, (perf_overflow_handler_t) write_handler, NULL);
if(IS_ERR((void __force *) write_watchpoint))
{
int result = PTR_ERR((void __force *) write_watchpoint);
printk(KERN_ALERT "Watchpoint registration failed\n");
return result;
}
printk(KERN_INFO "HW Watchpoint (Breakpoint) for %s write installed (0x%p)\n", address, (void*)write_attributes.bp_addr);
return 0;
}
void __exit driver_removal(void)
{
printk(KERN_ALERT "Driver removed.\n");
}
module_init(initialize_driver);
module_exit(driver_removal);
I have been keeping my pointer as type (void *), however, it doesn't really help. I have also tried using kallsyms_lookup_name function for this task (additional code was required to use it past kerner v.5), but it doesn't help either, only returning address of 0x0.