我有这个代码,它会将一些字节码写入可执行内存并(尝试)运行它。无论如何,除了内核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 ]---
答案 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位,则必须使用间接调用。