【问题标题】:Fail to change CS register value from kernel mode. invalid opcode: 0000无法从内核模式更改 CS 寄存器值。无效的操作码:0000
【发布时间】:2019-12-10 08:35:09
【问题描述】:

从带有这个 sn-p 的模块代码中,我正在尝试更改 CS 寄存器的值:

asm("pushq %rax");
asm("mov $0x10,%rax");
asm("mov %rax,%cs");
asm("popq %rax");

实际上CS寄存器包含段选择器-$0x10,也是内核宏__KERNEL_CS返回的值。我想做的是用完全相同的值重写 CS 寄存器。不幸的是我得到了错误,没有 sn-p 错误就不存在了。

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

【问题讨论】:

  • 这似乎是一个奇怪的问题,但您是否在某些时候使用了 R13 等其他寄存器而不是 RAX?
  • 是的,我也使用了 RBX 和 R13。输出相同。
  • 几分钟。我会发布
  • 只是好奇;你为什么要这样做?我不认为以相同的值重新加载 CS 不会产生任何架构效果。除非您担心在此之前 CS 可能不会设置为 __KERNEL_CS
  • @PeterCordes :我也很想知道这一点。这里可能存在 XY 问题。

标签: gcc linux-kernel x86-64 inline-assembly att


【解决方案1】:

没有要写入csmov 指令。来自Intel® 64 and IA-32 architectures software developer’s manualMOV spec

MOV 指令不能用于加载 CS 寄存器。尝试这样做会导致无效的操作码异常 (#UD)。

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

【讨论】:

    【解决方案2】:

    您不能使用带有MOV 的选择器直接加载CS。您将需要使用 RETFQIRETQ 指令来更改 CS 并转到 64 位偏移量。来自指令集参考:

    MOV 指令不能用于加载 CS 寄存器。尝试这样做会导致无效的操作码异常 (#UD)。要加载 CS 寄存器,请使用 far JMP、CALL 或 RET 指令。

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

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

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

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

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

    asm ("push $0x10\n\t"
         "push $1f\n\t"
         "retfq\n\t"
         "1:");
    

    请务必注意,您需要将 RETF 覆盖为 Quadword 大小,因为 64 位代码中的默认 RETF 假定返回地址为 32 位,我们要指定我们想要的返回到 64 位地址。来自RET 的英特尔文档:

    在 64 位模式下,该指令的默认操作大小是堆栈地址大小,即 64 位。这适用于近回报,而不是远回报; 远返回的默认操作大小为 32 位

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


    脚注:

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

      asm ("push %[sel]\n\t"
           "push $1f\n\t"
           "rex.W ljmp *(%%rsp)\n\t"
           "1:add $16, %%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 指针存储在 FAR JMP 指令之后,要跳转的指令位于标签 2: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 个字节。

    【讨论】:

    • 是否应该使用asm goto 以某种方式让编译器知道执行不会恢复?或者至少一个"memory" clobber 以确保编译器不会在asm 之后重新排序分配(这意味着如果执行不返回,则存储永远不会发生。)
    • @PeterCordes :我不这么认为,因为用作跳转目标的标签总是在内联程序集中。在所有情况下,内联汇编中的代码实际上都在继续。唯一发生的事情是 CS 正在重新加载。执行总是返回。我没有看到您在哪里查看它不返回的情况? FAR JMP 始终指向内联程序集中的标签。在 64 位代码中,始终忽略 CS 的限制和基数,因此 FAR JMP 将始终指向相同的线性地址。 32 位代码中的差异故事,尽管您可以更改 CS,它可能会转到其他地方。
    • @PeterCordes :但我对 OP 的一个问题是,他为什么要以这种方式重新加载 CS,除非权限级别发生变化。
    • @MichaelPetch:哦,我错过了它只是重新加载相同的值,而不是实际跳转到 32 位代码段或其他东西。我不明白为什么这会有用,除非可能是出于微架构的原因,比如延迟或指令重新排序障碍,比如lfence。对于您实际更改 cs 的其他情况,至少使用"memory" 破坏者是个好主意。
    猜你喜欢
    • 2021-01-21
    • 2023-03-27
    • 2012-04-24
    • 2019-08-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-04-12
    • 1970-01-01
    相关资源
    最近更新 更多