问题描述
我正在尝试编写一个解码器存根,并且遇到了对0xFF
的限制,因为该字符是错误字符.我正在使用jmp-call-pop
方法将编码的shellcode的地址保存到寄存器中.这是相关的代码段:
I'm trying to write a decoder stub and I'm running into a restriction on 0xFF
as a bad character. I'm using the jmp-call-pop
method to get the address of my encoded shellcode into a register. Here's the relevant snippet:
401012: e8 eb ff ff ff call 0x401002
似乎call
将始终在其字节中使用0xFF
.是否有另一条指令在执行时会将rip
压入堆栈并跳转到另一段代码?我尝试过手动将地址推入堆栈,但这会导致一个空字节,因为我的地址长3个字节,需要填充.
It seems like call
will always use 0xFF
in its bytes. Is there another instruction that, when executed, will push rip
onto the stack and jump to another section of code? I've tried just pushing the address onto the stack manually, but that results in a null byte because my addresses are 3 bytes long and need to be padded.
我的机器代码中不允许的字节为:
Disallowed bytes in my machine code are:
- 00
- FF
推荐答案
call rel32
是唯一的相对编码(间接或远距离jmp很少有用),因此,高字节当然总是00或FF,除非您将非常跳得很远,因为那是2的补码的工作原理.
call rel32
is the only relative encoding (and indirect or far jmp are rarely useful), so yes of course the high byte(s) will always be 00 or FF unless you're jumping very far away, because that's how 2's complement works.
自修改代码将是一种选择(但是,那么就遇到了获取代码指针的鸡肋/鸡蛋问题). 根据漏洞利用机制,您可能在RSP中有一个指向(接近)代码的指针.因此,您可能只是lea rax, [rsp+44]
/push rax
/jmp ...
Self-modifying code would be one option (but then you have a chicken/egg problem of getting a pointer to your code). Depending on the exploit mechanism, you might have a pointer to (near) your code in RSP. So you could maybe just lea rax, [rsp+44]
/ push rax
/ jmp ...
但是x86-64不需要jmp/call/pop惯用语.通常,您只需要对数据进行jmp
,然后使用带有负rel32
的RIP相对LEA,但是当然也将具有0xFF
字节.
But x86-64 has no need for the jmp/call/pop idiom. Normally you can just jmp
over your data and then use RIP-relative LEA with a negative rel32
, but that will of course also have 0xFF
bytes.
您可以将相对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
进行反汇编的机器代码:
machine code from assembling with NASM and disassembling with 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字节字符串.
Amusingly, 0x66
is the ASCII code for the letter 'f'
. I didn't intentionally pick 'f'
when trying to avoid 0xFF
:P But anyway, choose whatever 4-byte string you like.
rel32
的低字节会更高,具体取决于它必须达到的距离,因此请明智地选择.
The low byte of the rel32
will be higher depending on how far it has to reach, so choose wisely.
您可以使用以上相对于RIP的LEA + fixup技巧来创建自修改代码,例如inc byte [rax]
将0xFE
转换为0xFF
.或用0x11111111
直接加上sub
的双字或其他一些有用的方法来修复rel32
You can use the above RIP-relative LEA + fixup trick to create self-modifying code, e.g. inc byte [rax]
to turn 0xFE
into 0xFF
. Or a dword sub
-immediate with 0x11111111
or something could be useful to fixup a rel32
call r/m64
和 jmp r/m64
都不能直接使用,因为操作码本身是FF /2
和FF /4
call r/m64
and jmp r/m64
are both unusable directly, because the opcodes themselves are FF /2
and FF /4
如果要返回,可能最容易修复call rel32
或call rax
.但是也可以使用相对RIP的LEA来计算寄存器中的返回地址,然后将其推入,然后按jmp rel8
或jmp rax
等.
If you want to return, it's probably easiest to fixup a call rel32
or call rax
. But it would be possible to also use RIP-relative LEA to calculate a return address in a register and push it, then jmp rel8
or jmp rax
or whatever.
这篇关于避免使用CALL读取RIP的shellcode中的0xFF字节?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!