在可执行内存中运行代码

时间:2013-12-06 18:16:42

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

我有这个代码,它会将一些字节码写入可执行内存并(尝试)运行它。无论如何,除了内核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);

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

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

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

内核oops:

[   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 ]---

2 个答案:

答案 0 :(得分:2)

首先让我感到震惊的是“召唤”指令。它主要是零。这看起来不像printk的地址。

我认为这里发生的是你编译(或组装)这些指令但没有链接它们。编译/汇编编译器时,很乐意创建跳转指令来调用其他文件中定义的函数,但它不可能知道这些函数在最终二进制文件中的最终位置。这取决于链接器。

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

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

编辑:

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

尝试打印'字节码'的地址。指令指针应指向超过该字节的几个字节,这可能有助于您精确定位失败的确切指令。

答案 1 :(得分:2)

您的call指令编码不正确。

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

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

位移为0xffffffff8abcdef0 - 0xffffffff1234567d = 0x78888873

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