【发布时间】: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