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

时间:2019-04-21 00:17:29

标签: assembly x86-64 shellcode machine-code

我正在尝试编写一个解码器存根,并且遇到对0xFF的限制,因为它是错误字符。我正在使用jmp-call-pop方法将编码的Shellcode的地址保存到寄存器中。这是相关的代码段:

401012: e8 eb ff ff ff          call   0x401002

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


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

  • 00
  • FF

1 个答案:

答案 0 :(得分:4)

call rel32是唯一的相对编码(间接或远距离的jmp很少使用),因此,是的,当然,高字节始终为00或FF,除非您要跳过非常< / em>很远,因为2的补码就是这样工作的。

自修改代码是一种选择(但是,那么就遇到了获取代码指针的鸡肋/鸡蛋问题)。 取决于漏洞利用机制,您可能有一个指向(接近)RSP中代码的指针。因此,您可能只是lea rax, [rsp+44] / push rax / jmp ... < / p>

但是x86-64不需要jmp / call / pop习惯用法。通常,您可以jmp处理数据,然后使用带有负rel32的RIP相对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码。尝试避免使用'f'时,我并不是故意选择0xFF:P但是无论如何,请选择您喜欢的4字节字符串。

rel32的低字节会更高,具体取决于它必须达到的距离,因此请明智地选择。


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

您可以使用上述相对于RIP的LEA + fixup技巧来创建自修改代码,例如inc byte [rax]0xFE变成0xFF。或以sub开头的双字0x11111111或修复rel32的东西可能有用

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

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