assembly - 使用 CALL 读取 RIP 避免 shellcode 中的 0xFF 字节?

标签 assembly x86-64 shellcode machine-code

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

401012: e8 eb ff ff ff          call   0x401002

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


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

  • 00
  • FF

最佳答案

call rel32 是唯一的相对编码(indirect 或 far jmp 很少有用),所以当然高字节总是 00 或 FF 除非你跳转 < em>非常很远,因为这就是 2 的补码的工作原理。

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

但是 x86-64 不需要 jmp/call/pop 习惯用法。通常你可以对你的数据进行 jmp,然后使用 RIP-relative LEA 和一个负的 rel32,但是那当然也会有 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 的低字节会更高,这取决于它要达到的距离,所以请明智地选择。


实际上正在对附近的某个地方进行调用:

您可以使用上述 RIP 相关的 LEA + 修复技巧来创建自修改代码,例如inc byte [rax]0xFE 转换为 0xFF。或双字 sub - 立即与 0x11111111 或其他可能对修复 rel32

有用的东西

call r/m64jmp r/m64两者都直接无法使用,因为操作码本身是 FF/2FF/4

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

关于assembly - 使用 CALL 读取 RIP 避免 shellcode 中的 0xFF 字节?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55778839/

相关文章:

assembly - 如何获取 VESA BIOS 信息

c++ - 体系结构 x86_64 的 undefined symbol 静态类成员错误

assembly - 确保跳转的字母数字 Shellcode

android - 有时 gcc 在编译 arm abi 的 C++ 代码时会将第一个参数放入 R1 寄存器?

c++ - 优化算术编码器

c - 堆栈、数据和指令段在哪里实现?

linux - shell 的 Ubuntu 16.04 汇编代码

c - 为什么没有打印出来?

C代码解释

c - 如何从 Assembly 调用 C 函数?