如何在 C 中将包含在字符串中的 Address 分配给指针

How to assign an Address, contained inside a string, to a pointer in C

提问人:Denis Berezniuk 提问时间:8/13/2023 更新时间:8/13/2023 访问量:86

问:

我目前正在尝试开发一个 Linux Ubuntu 内核模块(作为我任务的一部分),它基本上是在地址上安装一个观察点,指定为模块的字符串参数 *(char)。

目前,我正处于将此地址分配给所需指针的阶段,但是......每次我尝试通过递增或添加此地址的值(以前从十六进制转换为十进制转换为 int)来执行此操作时,我根本无法正确分配此地址。我知道存在手动分配,但这不是我被告知要做的。请帮我找到至少一些方法来安装指针的确切地址,而无需对其进行硬编码。

顺便说一句,由于某种原因,每次重新启动计算机时,地址都会更改。

我的代码:

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;

程序的完整代码:

#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);

我一直将指针保留为类型(void *),但是,它并没有真正帮助。我也尝试过使用kallsyms_lookup_name函数来完成此任务(需要额外的代码才能在 kerner v.5 之后使用它),但它也无济于事,仅返回0x0地址。

C 指针 内核 模块 内存地址

评论

3赞 datenwolf 8/13/2023
一些提示: - 对空指针进行指针算术会调用 UB。- 你有很多东西。我怀疑你认为这意味着什么。- 地址不断变化,因为内核地址空间布局随机化 (KASLR)volatilesvolatile
0赞 0___________ 8/13/2023
unsigned char *address = (char *)0; address += simple_strtoul(.....)
0赞 stark 8/13/2023
若要将字符串中的值用作内核模块中的指针,该值必须是有效的内核虚拟地址。它不能来自用户进程。
0赞 Denis Berezniuk 8/13/2023
使用 sscanf 不是更好吗?顺便说一句,unsigned char 方法也不起作用
0赞 Denis Berezniuk 8/13/2023
我不在乎验证,我被赋予了一个任务,我需要完成它。稍后,我将尝试找到一种方法进行验证

答:

-1赞 X0rw 8/13/2023 #1

据我了解,您正在尝试将字符串地址“0x1233232”分配给指针,我编写了这个简单的代码,但由于我在用户模式下对其进行测试,因此我无法随机分配用于测试的地址,因此我分配了它并将其转换为字符串,然后将其从字符串转换为指针

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);

我很确定您测试了同样的事情,也许问题出在 char 数组上,所以请检查它是否以“\0”结尾