0

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.

  • 3
    A few hints: - doing pointer arithmetic on a null pointer invokes UB. - you've got so much `volatiles` in there. I doubt `volatile` means what you think it means. - the address keeps changing because *Kernel Address Space Layout Randomization* (KASLR) – datenwolf Aug 12 '23 at 20:44
  • `unsigned char *address = (char *)0; address += simple_strtoul(.....)` – 0___________ Aug 12 '23 at 21:40
  • To use the value from a string as a pointer in the kernel module the value must be a valid kernel virtual address. It can't be from a user process. – stark Aug 13 '23 at 01:13
  • Wouldn't it be better to use sscanf? By the way, unsigned char method doesn't work either – Denis Berezniuk Aug 13 '23 at 09:09
  • I don't care about validation, I was given a task, and I need to complete it. Later, I will try to find a method to validate – Denis Berezniuk Aug 13 '23 at 09:10
  • So, what prevents you from directly get an integer of necessay size? Let kernel do what it alredy can do, i.e. converting strings to numbers. You don't need to write code for that. Just use `module_param(address, unsigned long, ...)`. What's the problem? – 0andriy Aug 18 '23 at 19:47
  • In case you want to waste your time... Your conditional for `0x` is duplicate. `simple_strto*()` does it already for you. – 0andriy Aug 18 '23 at 19:48

1 Answers1

-1

as i understand, you are trying to assign a String address "0x1233232" to a pointer, i wrote this simple code but since i'm testing it in user-mode i can't randomly assign an address for testing so i allocated it and turned it into a string then casted it into a pointer from a string

char str_addr[100];//where we are going to store the address as a string
int i = malloc(4);//allocating 4 bytes
snprintf(str_addr,sizeof(str_addr),"%p",&i);//converting the address of i to a string 
printf("allocated address string: %s\n",str_addr);

int *p = (int *)str_addr;//casting the string address to a pointer
printf("pointed address:%s\n",  p);

am pretty sure you tested the same thing maybe the problem is from the char array, so check if it ends with a '\0'

X0rw
  • 1
  • 1