无法从内核模式更改 CS 寄存器值。无效操作码:0000

Fail to change CS register value from kernel mode. invalid opcode: 0000

从包含此代码片段的模块代码中,我正在尝试更改 CS 寄存器的值:

asm("pushq %rax");
asm("mov [=10=]x10,%rax");
asm("mov %rax,%cs");
asm("popq %rax");

实际上 CS 寄存器包含段选择器 - [=13=]x10,也是内核宏返回的值 __KERNEL_CS。我想做的是用完全相同的值重写 CS 寄存器。不幸的是我得到了错误,没有代码片段错误就消失了。

Aug  1 20:26:37 myhost kernel: [ 2905.693297] invalid opcode: 0000 [#1] SMP 
Aug  1 20:26:37 myhost kernel: [ 2905.694223] CPU: 0 PID: 7140 Comm: insmod Tainted: P           OE   4.4.0-148-generic #174~14.04.1-Ubuntu
Aug  1 20:26:37 myhost kernel: [ 2905.694362] task: ffff88007a0edb00 ti: ffff880068c54000 task.ti: ffff880068c54000
Aug  1 20:26:37 myhost kernel: [ 2905.694420] RIP: 0010:[<ffffffffc114e114>]  [<ffffffffc114e114>] hello_init+0x44/0xe0 [hello_module]
Aug  1 20:26:37 myhost kernel: [ 2905.694497] RSP: 0018:ffff880068c57ca0  EFLAGS: 00010282
Aug  1 20:26:37 myhost kernel: [ 2905.694540] RAX: 0000000000000010 RBX: ffffffff81e15080 RCX: 0000000000005768
Aug  1 20:26:37 myhost kernel: [ 2905.694595] RDX: 000000000000b1e4 RSI: 0000000000000246 RDI: 0000000000000246
Aug  1 20:26:37 myhost kernel: [ 2905.694649] RBP: ffff880068c57cc0 R08: 3231203a657a6973 R09: 6461202c66373a37
Aug  1 20:26:37 myhost kernel: [ 2905.694703] R10: 203a737365726464 R11: 0000000000000363 R12: ffff8800b7e7e980
Aug  1 20:26:37 myhost kernel: [ 2905.694757] R13: 0000000000000000 R14: ffffffffc114e0d0 R15: ffff880068c57eb0
Aug  1 20:26:37 myhost kernel: [ 2905.694813] FS:  00007f8c1c36b740(0000) GS:ffff88013fa00000(0000) knlGS:0000000000000000
Aug  1 20:26:37 myhost kernel: [ 2905.694875] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
Aug  1 20:26:37 myhost kernel: [ 2905.694920] CR2: 00005575a6b20248 CR3: 0000000075efc000 CR4: 0000000000060670
Aug  1 20:26:37 myhost kernel: [ 2905.694975] Stack:
Aug  1 20:26:37 myhost kernel: [ 2905.694995]  0000000000000000 007f000081e15080 ffff88013fa0c000 ffffffff81e15080
Aug  1 20:26:37 myhost kernel: [ 2905.695065]  ffff880068c57d38 ffffffff8100216f ffff880068c57eb0 ffff880068c57d10
Aug  1 20:26:37 myhost kernel: [ 2905.695133]  0000000000000246 0000000000000002 ffffffff811eab7f ffff88013b003c00
Aug  1 20:26:37 myhost kernel: [ 2905.695201] Call Trace:
Aug  1 20:26:37 myhost kernel: [ 2905.695230]  [<ffffffff8100216f>] do_one_initcall+0xcf/0x200
Aug  1 20:26:37 myhost kernel: [ 2905.695281]  [<ffffffff811eab7f>] ? kmem_cache_alloc_trace+0x1af/0x220
Aug  1 20:26:37 myhost kernel: [ 2905.695338]  [<ffffffff8118d293>] ? do_init_module+0x27/0x1d2
Aug  1 20:26:37 myhost kernel: [ 2905.695387]  [<ffffffff8118d2cc>] do_init_module+0x60/0x1d2
Aug  1 20:26:37 myhost kernel: [ 2905.695432]  [<ffffffff8110b42d>] load_module+0x145d/0x1b50
Aug  1 20:26:37 myhost kernel: [ 2905.695480]  [<ffffffff81107b60>] ? __symbol_put+0x40/0x40
Aug  1 20:26:37 myhost kernel: [ 2905.695528]  [<ffffffff812137a1>] ? kernel_read+0x41/0x60
Aug  1 20:26:37 myhost kernel: [ 2905.695574]  [<ffffffff8110bcee>] SYSC_finit_module+0x7e/0xa0
Aug  1 20:26:37 myhost kernel: [ 2905.695620]  [<ffffffff8110bd2e>] SyS_finit_module+0xe/0x10
Aug  1 20:26:37 myhost kernel: [ 2905.697884]  [<ffffffff8182d61b>] entry_SYSCALL_64_fastpath+0x22/0xcb
Aug  1 20:26:37 myhost kernel: [ 2905.700094] Code: eb 03 c0 0f 01 45 ee 0f b7 75 ee 48 8b 4d f0 48 c7 c7 9d f0 14 c1 31 c0 89 f2 e8 73 eb 03 c0 8c 5d ec 41 55 49 c7 c5 10 00 00 00 <49> 8e cd 41 5d 8c 4d ea 0f b7 55 ec 0f b7 75 ea 48 c7 c7 b8 f0 
Aug  1 20:26:37 myhost kernel: [ 2905.704976] RIP  [<ffffffffc114e114>] hello_init+0x44/0xe0 [hello_module]
Aug  1 20:26:37 myhost kernel: [ 2905.707358]  RSP <ffff880068c57ca0>
Aug  1 20:26:37 myhost kernel: [ 2905.719667] ---[ end trace 48f04fe6e7ff0ed6 ]---

已更新 R13 注册

Aug  1 21:05:25 myhost kernel: [  146.818158] invalid opcode: 0000 [#1] SMP 
Aug  1 21:05:25 myhost kernel: [  146.818699] CPU: 1 PID: 5108 Comm: insmod Tainted: P           OE   4.4.0-148-generic #174~14.04.1-Ubuntu
Aug  1 21:05:25 myhost kernel: [  146.818778] task: ffff880097a45b00 ti: ffff880085d58000 task.ti: ffff880085d58000
Aug  1 21:05:25 myhost kernel: [  146.818810] RIP: 0010:[<ffffffffc109e114>]  [<ffffffffc109e114>] hello_init+0x44/0xe0 [hello_module]
Aug  1 21:05:25 myhost kernel: [  146.818854] RSP: 0018:ffff880085d5bca0  EFLAGS: 00010282
Aug  1 21:05:25 myhost kernel: [  146.818880] RAX: 000000000000001f RBX: ffffffff81e15080 RCX: 0000000000002298
Aug  1 21:05:25 myhost kernel: [  146.818911] RDX: 00000000000051a2 RSI: 0000000000000246 RDI: 0000000000000246
Aug  1 21:05:25 myhost kernel: [  146.818942] RBP: ffff880085d5bcc0 R08: 3231203a657a6973 R09: 6461202c66373a37
Aug  1 21:05:25 myhost kernel: [  146.818973] R10: 203a737365726464 R11: 0000000000000353 R12: ffff880085cc0b60
Aug  1 21:05:25 myhost kernel: [  146.819004] R13: 0000000000000010 R14: ffffffffc109e0d0 R15: ffff880085d5beb0
Aug  1 21:05:25 myhost kernel: [  146.819037] FS:  00007fd564d44740(0000) GS:ffff88013fa40000(0000) knlGS:0000000000000000
Aug  1 21:05:25 myhost kernel: [  146.819072] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
Aug  1 21:05:25 myhost kernel: [  146.819097] CR2: 0000558a2aa16248 CR3: 0000000085d5c000 CR4: 0000000000060670
Aug  1 21:05:25 myhost kernel: [  146.819128] Stack:
Aug  1 21:05:25 myhost kernel: [  146.819139]  0000000000000000 007f000081e15080 ffff88013fa4c000 ffffffff81e15080
Aug  1 21:05:25 myhost kernel: [  146.819178]  ffff880085d5bd38 ffffffff8100216f ffff880085d5beb0 ffff880085d5bd10
Aug  1 21:05:25 myhost kernel: [  146.819217]  0000000000000246 0000000000000002 ffffffff811eab7f ffff88013b003c00
Aug  1 21:05:25 myhost kernel: [  146.819255] Call Trace:
Aug  1 21:05:25 myhost kernel: [  146.819273]  [<ffffffff8100216f>] do_one_initcall+0xcf/0x200
Aug  1 21:05:25 myhost kernel: [  146.819301]  [<ffffffff811eab7f>] ? kmem_cache_alloc_trace+0x1af/0x220
Aug  1 21:05:25 myhost kernel: [  146.819332]  [<ffffffff8118d293>] ? do_init_module+0x27/0x1d2
Aug  1 21:05:25 myhost kernel: [  146.819359]  [<ffffffff8118d2cc>] do_init_module+0x60/0x1d2
Aug  1 21:05:25 myhost kernel: [  146.819385]  [<ffffffff8110b42d>] load_module+0x145d/0x1b50
Aug  1 21:05:25 myhost kernel: [  146.819412]  [<ffffffff81107b60>] ? __symbol_put+0x40/0x40
Aug  1 21:05:25 myhost kernel: [  146.819440]  [<ffffffff812137a1>] ? kernel_read+0x41/0x60
Aug  1 21:05:25 myhost kernel: [  146.819466]  [<ffffffff8110bcee>] SYSC_finit_module+0x7e/0xa0
Aug  1 21:05:25 myhost kernel: [  146.819492]  [<ffffffff8110bd2e>] SyS_finit_module+0xe/0x10
Aug  1 21:05:25 myhost kernel: [  146.820820]  [<ffffffff8182d61b>] entry_SYSCALL_64_fastpath+0x22/0xcb
Aug  1 21:05:25 myhost kernel: [  146.822117] Code: eb 0e c0 0f 01 45 ee 0f b7 75 ee 48 8b 4d f0 48 c7 c7 9d f0 09 c1 31 c0 89 f2 e8 73 eb 0e c0 8c 5d ec 41 55 49 c7 c5 10 00 00 00 <49> 8e cd 41 5d 8c 4d ea 0f b7 55 ec 0f b7 75 ea 48 c7 c7 b8 f0 
Aug  1 21:05:25 myhost kernel: [  146.824981] RIP  [<ffffffffc109e114>] hello_init+0x44/0xe0 [hello_module]
Aug  1 21:05:25 myhost kernel: [  146.826376]  RSP <ffff880085d5bca0>
Aug  1 21:05:25 myhost kernel: [  146.833469] ---[ end trace 6525f2f63d2f58dd ]---

没有 mov 指令写入 cs。来自 Intel® 64 and IA-32 architectures software developer’s manual, MOV spec

The MOV instruction cannot be used to load the CS register. Attempting to do so results in an invalid opcode exception (#UD).

您需要跳远才能更改 cs,检查 ch.5.8 中的限制以更改 cs

你不能直接用选择器加载CS,用MOV. You will need to use a RETFQ or IRETQ指令改变CS并转到64位-位偏移量。来自指令集参考:

The MOV instruction cannot be used to load the CS register. Attempting to do so results in an invalid opcode exception (#UD). To load the CS register, use the far JMP, CALL, or RET instruction.

您收到#UD(无效操作码),这就是您的模块崩溃的原因。

在 64 位代码中,没有 FAR CALL or FAR JMP 将选择器和 64 位偏移量作为操作数(立即值),因为没有 ptr16:64 变体。

在 32 位模式下,您可以使用 ljmpl [=18=]x10, $offset,但在 64 位代码中,您必须使用替代方法1,例如 RETFQ (或者 IRETQ 如果您需要更改权限级别)。您可以使用这样的代码:

asm ("push %[sel]\n\t"
     "push f\n\t"
     "retfq\n\t"
     "1:"
     :
     : [sel]"i"(__KERNEL_CS));

如果你想像你的问题一样使用基本的内联汇编,你需要将连续的 ASM 语句放在一个语句中。允许编译器在您的个人 ASM 语句之间生成代码,这不是您想要的。一个基本的 ASM 版本是:

asm ("push [=11=]x10\n\t"
     "push f\n\t"
     "retfq\n\t"
     "1:");

重要的是要注意,您需要覆盖 RETF 以达到 Quadword 大小,因为 64 位代码中的默认值 RETF 假定 32 位 return 地址,我们要指定我们要 return 到 64 位地址。来自 RET 的英特尔文档:

In 64-bit mode, the default operation size of this instruction is the stack-address size, i.e. 64 bits. This applies to near returns, not far returns; the default operation size of far returns is 32 bits.

此代码通过将选择器压入堆栈来模拟到下一条指令的 FAR JMP2 后跟指令之后的偏移量(标签 1:retfqretfq 将弹出偏移量和选择器并将控制转移到恰好是下一条指令的地址。


脚注:

  • 1可以创建一个长度为 10 字节的变量,其中包括一个用于跳转到的偏移量的 QWORD 和一个用于选择器的 WORD .然后,您可以使用 JMP m16:64 跳转变体。要使用 AT&T 语法在 GCC 的内联汇编中编码 JMP m16:m64,您可以这样做:

    asm ("push %[sel]\n\t"
         "push f\n\t"
         "rex.W ljmp *(%%rsp)\n\t"
         "1:add , %%rsp\n\t"
         :
         : [sel]"i"(__KERNEL_CS));
    

    代码在堆栈上构建一个 16:64 (selector:offset) 指针,并通过 RSP 对其进行间接 FAR JMP。 16:64地址指向FAR JMP之后的指令。 rex.W 前缀促进 FAR JMP,以便使用 64 位偏移而不是 32 位偏移对指针进行解码。然后清理堆栈,从堆栈中删除指针。

    我不知道上下文或您更改 CS 的原因,但如果代码经常被调用,那么您可能希望发出 16:64 代码本身的指针。在下面的示例中,16:64 指针存储在 FAR JMP 指令之后,跳转到的指令位于标签 2:after the 16:64指针):

    asm ("rex.W ljmp *1f\n\t"
         /* The pointer is being stored with the code.
            The jump will be to the label after the pointer itself at label '2:'. */
         "1:  .quad 2f\n\t"
         "    .word %c[sel]\n\t"
         "2:\n\t"
         :
         : [sel]"i"(__KERNEL_CS));
    
  • 2Linux内核本身没有Red Zone所以直接压栈是安全的与内联汇编。在用户模式代码中,您必须调整 RSP 以避免破坏 RSP.

  • 正下方的 128 个字节