【问题标题】:Avoiding 0xFF bytes in shellcode using CALL to read RIP?使用 CALL 读取 RIP 避免 shellcode 中的 0xFF 字节?
【发布时间】:2019-09-10 17:28:07
【问题描述】:

我正在尝试编写解码器存根,但我遇到了将 0xFF 作为坏字符的限制。我正在使用jmp-call-pop 方法将我编码的shellcode 的地址放入一个寄存器。这是相关的sn-p:

401012: e8 eb ff ff ff          call   0x401002

似乎call 将始终在其字节中使用0xFF。是否有另一条指令在执行时会将rip 压入堆栈并跳转到另一段代码?我尝试手动将地址推送到堆栈上,但这会导致空字节,因为我的地址有 3 个字节长并且需要填充。


我的机器代码中不允许的字节是:

  • 00
  • FF

【问题讨论】:

  • 还有哪些字节被禁止?
  • @fuz:我假设只是0FF 基于未提及任何其他问题的问题。但好点;我在问题中添加了一个部分,如果还有更多,则 OP 应该编辑。我的 lea/sub 答案确实使用了几个超出低 ASCII 范围的 8x 字节。
  • @PeterCordes 请不要在基于假设的问题中添加诸如“不允许的字节”部分之类的部分。只要 OP 没有说明允许哪些确切字节,哪些不允许,您就不能假设所有其他字符都可以。
  • @fuz:我认为最好将不允许的字节的隐式声明明确,但我认为你是对的,如果事实证明这更有可能只是浪费人们的时间回答实际要求不同。因此,如果人们确实基于此编写答案,而这不是 OP 所需要的,他们可能会编辑并可能使问题无效。 OTOH,我认为这组特定的限制很有趣,可以回答,无论它是否有助于 OP。如果他们有其他要求,他们应该一开始就这么说。

标签: assembly x86-64 shellcode machine-code


【解决方案1】:

call rel32 是唯一的相对编码(间接或远 jmp 很少有用),所以是的,当然高字节将始终为 00 或 FF,除非您正在跳跃非常 很远,因为这就是 2 的补码的工作原理。

自我修改代码将是一种选择(但您会遇到获取代码指针的鸡/蛋问题)。 根据漏洞利用机制,您可能在 RSP 中有一个指向(附近)您的代码的指针。因此您可能只是 lea rax, [rsp+44] / push rax / jmp ...

但是 x86-64 不需要 jmp/call/pop 习语。通常你可以只用jmp覆盖你的数据,然后使用带有负值rel32的RIP-relative LEA,但这当然也有0xFF字节。


您可以将 RIP 相对 LEA 与安全的 rel32 一起使用,然后对其进行更正:

    lea    rsi, [rel anchor + 0x66666666]      ; or  [RIP + 0x66666666]
    sub    rsi, 0x66666666
    ;...
    xor    eax,eax
    mov    al,1        ; __NR_write = 1  x86-64 Linux
    mov    edi, eax
    lea    edx, [rax-1 + msglen]
    syscall            ; write(1, msg, msglen)

    lea    eax, [rdi-1 + 60]       ; __NR_exit
    syscall            ; sys_exit(1)

anchor:
    msg: db     "Hello World", 0xa
    msglen equ $-msg

使用 NASM 汇编和使用 objdump -drwC -Mintel 反汇编的机器代码:

$ asm-link -dn rel.asm                   # a helper script to assmble+link and disassemble
+ nasm -felf64 -Worphan-labels rel.asm
+ ld -o rel rel.o
ld: warning: cannot find entry symbol _start; defaulting to 0000000000401000

rel:     file format elf64-x86-64


Disassembly of section .text:

0000000000401000 <anchor-0x1e>:
  401000:       48 8d 35 7d 66 66 66    lea    rsi,[rip+0x6666667d]        # 66a67684 <__bss_start+0x66665684>
  401007:       48 81 ee 66 66 66 66    sub    rsi,0x66666666
  40100e:       31 c0                   xor    eax,eax
  401010:       b0 01                   mov    al,0x1
  401012:       89 c7                   mov    edi,eax
  401014:       8d 50 0b                lea    edx,[rax+0xb]
  401017:       0f 05                   syscall 
  401019:       8d 47 3b                lea    eax,[rdi+0x3b]
  40101c:       0f 05                   syscall 

000000000040101e <anchor>:
  40101e:       48                      rex.W
   ... ASCII data that isn't real machine code
  401029:       0a                      .byte 0xa

peter@volta:/tmp$ ./rel 
Hello World

$ strace ./rel 
execve("./rel", ["./rel"], 0x7ffd09467720 /* 55 vars */) = 0
write(1, "Hello World\n", 12Hello World
)           = 12
exit(1)                                 = ?
+++ exited with 1 +++

有趣的是,0x66 是字母 'f' 的 ASCII 码。我在试图避开0xFF 时并没有故意选择'f' :P 但无论如何,选择你喜欢的任何4字节字符串。

rel32 的低字节会更高,这取决于它必须达到的距离,所以要明智地选择。


实际上在附近的某个地方做一个call

您可以使用上述 RIP-relative LEA + fixup 技巧来创建自修改代码,例如inc byte [rax]0xFE 变成0xFF。或者一个 dword sub-immediate 和 0x11111111 或者一些对修复 rel32 有用的东西

call r/m64jmp r/m64都不能直接使用,因为操作码本身是FF /2FF /4

如果您想返回,修复call rel32call rax 可能是最简单的。但也可以使用 RIP 相对 LEA 计算寄存器中的返回地址并将其推送,然后是 jmp rel8jmp rax 或其他。

【讨论】:

    猜你喜欢
    • 2018-05-25
    • 2012-05-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-10-01
    • 1970-01-01
    • 2017-09-29
    相关资源
    最近更新 更多