【问题标题】:Running code inside executable memory在可执行内存中运行代码
【发布时间】:2013-12-06 18:16:42
【问题描述】:

我有这段代码可以将一些字节码写入可执行内存并(尝试)运行它。无论如何,除了内核 oops 之外,我什么也没得到,而且我不确定到底出了什么问题。

我使用 as 将代码开头的 4 条 ASM 指令“翻译”为等效的字节码。

顺便说一句,__asm__ __volatile__ 块按预期工作。

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/slab.h>

static int __init hello_init(void){

    int64_t ret;
    char *fmt = "\n\n\n%s\n\n\n";
    char *s = "Hello world!";

    void (*test)(void);

    unsigned char *op_1 = "\x48\x8B\x3C\x25";
    unsigned char *op_2 = "\x48\x8B\x34\x25";
    unsigned char *op_3 = "\x48\x8B\x04\x25\x02\x00\x00\x00";
    unsigned char *call = "\xE8\x00\x00\x00";
    unsigned char *ret_call = "\xC3";

    unsigned int pos = 0;

    /*
    __asm__ __volatile__ (
        "movq %1, %%rdi;"
        "movq %0, %%rsi;"
        "movq $2, %%rax;"

        "call printk;"

        :
        : "m" (s), "m" (fmt)
        :
    );
    */

    unsigned char *bytecode = __vmalloc(
        strlen(op_1) + sizeof(void *) +
        strlen(op_2) + sizeof(void *) +
        strlen(op_3) +
        strlen(call) + sizeof(void *) +
        strlen(ret_call),
        GFP_KERNEL, PAGE_KERNEL_EXEC
    );

    //movq <fmt>, %%rdi
    memcpy(bytecode + pos, op_1, strlen(op_1));
    pos += strlen(op_1);
    memcpy(bytecode + pos, &fmt, sizeof(void *));
    pos += sizeof(void *);

    //movq <s>, %%rsi
    memcpy(bytecode + pos, op_2, strlen(op_2));
    pos += strlen(op_2);
    memcpy(bytecode + pos, &s, sizeof(void *));
    pos += sizeof(void *);

    //movq 2, %%rax
    memcpy(bytecode + pos, op_3, strlen(op_3));
    pos += strlen(op_3);

    //call printk
    memcpy(bytecode + pos, call, strlen(call));
    pos += strlen(call);
    memcpy(bytecode + pos, &printk, sizeof(void *));
    pos += sizeof(void *);

    //ret
    memcpy(bytecode + pos, ret_call, strlen(ret_call));

    test = bytecode;

    test();

    return 0;
}

static void __exit hello_cleanup(void){}

module_init(hello_init);
module_exit(hello_cleanup);

我的问题是我做错了什么?

编辑:我根据建议更新了我的代码。这是新版本,内核哎呀。

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/slab.h>

static int __init hello_init(void){

    int64_t ret;
    char *fmt = "\n\n\n%s\n\n\n";
    char *s = "Hello world!";

    void (*test)(void);

    unsigned char op_1[] = { 0x48, 0x8B, 0x3C, 0x25 }; //movq <addr>, %rdi
    unsigned char op_2[] = { 0x48, 0x8B, 0x34, 0x25 }; //movq <addr>, %rsi
    unsigned char op_3[] = { 0x48, 0x8B, 0x04, 0x25, 0x02, 0x00, 0x00, 0x00 }; //movq 2, %rax
    unsigned char call[] = { 0xE8, 0x00, 0x00, 0x00 }; //call <printk addr>
    unsigned char ret_call[] = { 0xC3 }; //<ret>

    unsigned int pos = 0;

    /*
    __asm__ __volatile__ (
        "movq %1, %%rdi;"
        "movq %0, %%rsi;"
        "movq $2, %%rax;"

        "call printk;"

        :
        : "m" (s), "m" (fmt)
        :
    );
    */

    void *bytecode = __vmalloc(
        sizeof(op_1) + sizeof(void *) +
        sizeof(op_2) + sizeof(void *) +
        sizeof(op_3) +
        sizeof(call) + sizeof(void *) +
        sizeof(ret_call),
        GFP_KERNEL, PAGE_KERNEL_EXEC
    );

    //movq <fmt>, %%rdi
    memcpy(bytecode + pos, op_1, sizeof(op_1));
    pos += sizeof(op_1);
    memcpy(bytecode + pos, &fmt, sizeof(void *));
    pos += sizeof(void *);

    //movq <s>, %%rsi
    memcpy(bytecode + pos, op_2, sizeof(op_2));
    pos += sizeof(op_2);
    memcpy(bytecode + pos, &s, sizeof(void *));
    pos += sizeof(void *);

    //movq 2, %%rax
    memcpy(bytecode + pos, op_3, sizeof(op_3));
    pos += sizeof(op_3);

    //call printk
    memcpy(bytecode + pos, call, sizeof(call));
    pos += sizeof(call);
    memcpy(bytecode + pos, &printk, sizeof(void *));
    pos += sizeof(void *);

    //ret
    memcpy(bytecode + pos, ret_call, sizeof(ret_call));

    test = bytecode;

    test();

    return 0;
}

static void __exit hello_cleanup(void){}

module_init(hello_init);
module_exit(hello_cleanup);

内核糟糕:

[   96.439303] hello: module license 'unspecified' taints kernel.
[   96.441001] invalid opcode: 0000 [#1] SMP 
[   96.441009] Modules linked in: hello(POF+) snd_hrtimer(F) vboxsf(OF) vboxvideo(OF) drm bnep rfcomm bluetooth joydev(F) snd_intel8x0 snd_ac97_codec ac97_bus snd_pcm(F) snd_page_alloc(F) snd_seq_midi(F) snd_seq_midi_event(F) snd_rawmidi(F) snd_seq(F) snd_seq_device(F) snd_timer(F) ppdev(F) snd(F) parport_pc(F) mac_hid psmouse(F) vboxguest(OF) i2c_piix4 microcode(F) soundcore(F) serio_raw(F) lp(F) parport(F) vesafb(F) hid_generic usbhid hid ahci(F) libahci(F) e1000(F)
[   96.441032] CPU 0 
[   96.441043] Pid: 2300, comm: insmod Tainted: PF          O 3.8.0-29-generic #42-Ubuntu innotek GmbH VirtualBox/VirtualBox
[   96.441051] RIP: 0010:[<ffffc90000182008>]  [<ffffc90000182008>] 0xffffc90000182007
[   96.441058] RSP: 0018:ffff880039a2de08  EFLAGS: 00010282
[   96.441066] RAX: ffffc90000182000 RBX: ffffffffa017e018 RCX: ffffc90000182000
[   96.441074] RDX: 8948559066666666 RSI: ffffc90000182000 RDI: 0a0a0a73250a0a0a
[   96.441082] RBP: ffff880039a2de10 R08: 0000000000000163 R09: 0000000000000001
[   96.441086] R10: 000000000000362a R11: 0000000000000000 R12: ffffffffa002e000
[   96.441094] R13: 0000000000000000 R14: 0000000000000001 R15: ffffffffa017e000
[   96.441102] FS:  00007f83ffea9740(0000) GS:ffff88003fc00000(0000) knlGS:0000000000000000
[   96.441110] CS:  0010 DS: 0000 ES: 0000 CR0: 000000008005003b
[   96.441118] CR2: 00007fa90d1c2d20 CR3: 0000000038230000 CR4: 00000000000006f0
[   96.441133] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
[   96.441238] DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000400
[   96.441246] Process insmod (pid: 2300, threadinfo ffff880039a2c000, task ffff880038802e80)
[   96.441254] Stack:
[   96.441261]  ffffffffa002e088 ffff880039a2de40 ffffffff8100215a ffffffffa017e018
[   96.441269]  ffffffffa017e050 ffff880039a2def0 0000000000000001 ffff880039a2dee0
[   96.441277]  ffffffff810bff59 ffffffff810bbbc0 ffff880035eace48 ffffffff00000001
[   96.441285] Call Trace:
[   96.441310]  [<ffffffffa002e088>] ? hello_init+0x88/0x1000 [hello]
[   96.441326]  [<ffffffff8100215a>] do_one_initcall+0x12a/0x180
[   96.441344]  [<ffffffff810bff59>] load_module+0xef9/0x1560
[   96.441357]  [<ffffffff810bbbc0>] ? unset_module_init_ro_nx+0x80/0x80
[   96.441373]  [<ffffffff810c0685>] sys_init_module+0xc5/0xf0
[   96.441389]  [<ffffffff816d561d>] system_call_fastpath+0x1a/0x1f
[   96.441393] Code: <ff> ff ff ff 48 8b 34 25 2d d0 17 a0 ff ff ff ff 48 8b 04 25 02 00 
[   96.441408] RIP  [<ffffc90000182008>] 0xffffc90000182007
[   96.441419]  RSP <ffff880039a2de08>
[   96.441427] ---[ end trace 157c8f367de5a7f1 ]---

【问题讨论】:

  • 内存字/字节对齐可能是个问题。
  • 您正在分配一个块并将代码复制到那里。所以我要检查的第一件事是,在调试器中查看该复制代码的反汇编是否符合我的预期。
  • 为什么不在内核中尝试在用户空间中执行此操作?原理都是一样的,调试会容易很多。
  • 此外,您不能使用以空字符结尾的字符串和包含字节值 0 的二进制数据,它本身就是空值,至少如果您想使用 strlen() 函数则不行!为什么不定义一个包含所有操作码的初始化器的无符号字符数组?
  • 我仍然不喜欢调用指令的置换(?)字段中的零,看起来好像您从尚未完全链接的东西中抓取了操作码。无论如何,这段代码应该如何知道printk 的地址?如果这是一个位移,您是否意识到当您将操作码移动到不同的内存位置时该值需要更改?

标签: c linux linux-kernel x86-64 kernel-module


【解决方案1】:

您的call 指令编码不正确。

call 指令由一个 E8 字节和一个 32 位相对位移(即使在 64 位模式下也是 32 位)组成。 这个位移是从call 之后的指令开始到目标地址的偏移量。 例如,在以下代码中:

0xffffffff12345678  E8 73 88 88 78  call printk
0xffffffff1234567d  C3              ret
...
printk:
0xffffffff8abcdef0  ...             ...

位移是0xffffffff8abcdef0 - 0xffffffff1234567d = 0x78888873

如果位移不适合 32 位,则必须使用间接调用。

【讨论】:

  • 有趣!函数地址和E8 之间的距离(差异)是否可能大于可以用 32 位值表示的最大值?如果是这样,它是如何处理的?而且,如果这种情况可能发生,发生的可能性有多大?
  • 见最后一句话。如果可能的话,如果你总是使用间接调用,你的代码会更容易。
  • 间接调用是指将偏移量移动到寄存器并调用寄存器,对吗?
  • 或者更好的问题是“我可以直接拨打地址吗?” “直接”是指不计算位移,而只是做类似call 0x87654321abcdefff 的事情?
  • call 变体在documentation 中进行了描述。
【解决方案2】:

让我印象深刻的第一件事是“呼叫”指令。大部分都是零。这看起来不像是 printk 的地址。

我认为这里发生的事情是您编译(或组装)了这些指令,但没有链接它们。虽然编译/汇编编译器会很高兴地创建跳转指令来调用其他文件中定义的函数,但它不可能知道这些函数将在最终二进制文件中的什么位置结束。这取决于链接器。

您从未运行过链接器,因此调用指令的目的地从未解析,您正在查看对 NULL 的调用。

至于修复,如果这只是一个实验,我倾向于手动查找 printk 的地址(System.map 可能会在那里提供帮助)并使用那个。

编辑:

内核错误显示“操作码无效”,因此很明显您生成的指令有问题。它显然对某处的 0 字节不满意。

尝试打印“字节码”的地址。指令指针应该指向过去几个字节,这可能有助于您查明失败的确切指令。

【讨论】:

  • 不,'call'指令在第63行填充了printk的地址,memcpy(bytecode + pos, &amp;printk, sizeof(void *));
  • 你也可以添加内核恐慌消息吗?这通常包含很多关于实际发生的事情的信息。
  • 我更新了代码并添加了内核哎呀,请看一下:)
猜你喜欢
  • 1970-01-01
  • 2021-04-28
  • 2017-06-27
  • 2011-01-02
  • 2013-05-13
  • 1970-01-01
  • 2013-03-08
  • 2013-08-08
  • 1970-01-01
相关资源
最近更新 更多