Избегать байтов 0xFF в шеллкоде, используя CALL для чтения RIP? - PullRequest
1 голос
/ 21 апреля 2019

Я пытаюсь написать заглушку декодера и сталкиваюсь с ограничением на 0xFF как плохой символ.Я использую метод jmp-call-pop, чтобы получить адрес моего закодированного шелл-кода в регистр.Вот соответствующий фрагмент:

401012: e8 eb ff ff ff          call   0x401002

Кажется, что call всегда будет использовать 0xFF в своих байтах.Есть ли другая инструкция, которая при выполнении помещает rip в стек и переходит к другому фрагменту кода?Я попытался просто поместить адрес в стек вручную, но это приводит к нулевому байту, потому что мои адреса имеют длину 3 байта и должны быть дополнены.


Запрещенные байты в моем машинном коде:

  • 00
  • FF

1 Ответ

4 голосов
/ 21 апреля 2019

call rel32 - единственное относительное кодирование (а косвенные или дальние jmp редко бывают полезны), так что да, конечно, старший байт (ы) всегда будет 00 или FF, если вы не прыгаете очень далеко, потому что именно так работает дополнение 2.

Самоизменяющийся код будет одним из вариантов (но тогда у вас есть проблема курицы / яйца с получением указателя на ваш код). В зависимости от механизма эксплойта у вас может быть указатель (рядом) с вашим кодом в RSP. Таким образом, вы можете просто lea rax, [rsp+44] / push rax / jmp ...

Но x86-64 не нуждается в идиоме jmp / call / pop. Обычно вы можете просто jmp над вашими данными и затем использовать REA-относительный LEA с отрицательным rel32, но это, конечно, также будет 0xFF байт.


Вы можете использовать REA-относительный 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 - это код ASCII для буквы 'f'. Я намеренно не выбрал 'f', пытаясь избежать 0xFF: P Но в любом случае, выберите любую 4-байтовую строку, которая вам нравится.

Младший байт rel32 будет выше в зависимости от того, как далеко он должен пройти, поэтому выбирайте мудро.


На самом деле делает call где-то поблизости:

Вы можете использовать вышеупомянутый RIP-относительный трюк исправления LEA + для создания самоизменяющегося кода, например inc byte [rax] чтобы превратить 0xFE в 0xFF. Или dword sub - сразу с 0x11111111, или что-то может быть полезно для исправления rel32

call r/m64 и jmp r/m64 оба непригодны для непосредственного использования, поскольку сами коды операций FF /2 и FF /4

Если вы хотите вернуться, вероятно, проще всего исправить call rel32 или call rax. Но было бы также возможно использовать REA-относительный LEA для вычисления адреса возврата в регистре и нажать его, затем jmp rel8 или jmp rax или что-то еще.

...