即使两个程序都遵守shellcode执行所需的相对寻址指令,并且两者都在执行时打印所需的消息,但 2nd Sample 在用作shellcode时失败 . 谁能解释一下这种行为?奇怪的是,与第一个相同的 3rd sample 也失败了 .
输出: Sample 1 Hello World
其他样本(2和3)打印垃圾值
Sample 1
global _start
section .text
_start:
jmp widen
pHworld:
pop rsi
xor rax,rax
mov al,1
mov rdi,rax
mov rdx,rdi
add rdx,11
syscall
;Exit
xor rax,rax
mov rax,60
xor rdi,rdi
syscall
widen:
call pHworld
Hworld db "Hello World",0xa
Sample 2
global _start
section .text
_start:
call pHworld
Hworld db "Hello World",0xa
pHworld:
pop rsi
xor rax,rax
mov al,1
mov rdi,rax
mov rdx,rdi
add rdx,11
syscall
;Exit
xor rax,rax
mov rax,60
xor rdi,rdi
syscall
Sample 3
global _start
section .text
_start:
jmp label1
label1:
call pHworld
Hworld db "Hello World",0xa
pHworld:
pop rsi
xor rax,rax
mov al,1
mov rdi,rax
mov rdx,rdi
add rdx,11
syscall
;Exit
xor rax,rax
mov rax,60
xor rdi,rdi
syscall
无法休息我的好奇心,我尝试了另一种变化,即使我的objdump没有任何0x00,这也会失败(打印垃圾值) . Sample 4
global _start
section .text
pHworld:
pop rsi
xor rax,rax
mov al,1
mov rdi,rax
mov rdx,rdi
add rdx,11
syscall
xor rax,rax
xor rdi,rdi
mov al,60
syscall
l1:
call pHworld
Hworld db "Hello World", 0xa
_start:
jmp l1
enter code here
Objdump of sample4
./hworld2.s: file format elf64-x86-64
Disassembly of section .text:
0000000000400080 :
400080: 5e pop rsi
400081: 48 31 c0 xor rax,rax
400084: b0 01 mov al,0x1
400086: 48 89 c7 mov rdi,rax
400089: 48 89 fa mov rdx,rdi
40008c: 48 83 c2 0b add rdx,0xb
400090: 0f 05 syscall
400092: 48 31 c0 xor rax,rax
400095: 48 31 ff xor rdi,rdi
400098: b0 3c mov al,0x3c
40009a: 0f 05 syscall
000000000040009c :
40009c: e8 df ff ff ff call 400080
00000000004000a1 :
4000a1: 48 rex.W
4000a2: 65 gs
4000a3: 6c ins BYTE PTR es:[rdi],dx
4000a4: 6c ins BYTE PTR es:[rdi],dx
4000a5: 6f outs dx,DWORD PTR ds:[rsi]
4000a6: 20 57 6f and BYTE PTR [rdi+0x6f],dl
4000a9: 72 6c jb 400117
4000ab: 64 fs
4000ac: 0a eb or ch,bl
00000000004000ad :
4000ad: eb ed jmp 40009c
1 回答
TL;DR :使用shellcode,您希望避免编码0x00字节,否则当代码用作漏洞利用的字符串时,它们将在第一个0x00处截断 . 这有效地缩短了您的代码 .
在漏洞利用之外运行时,额外的0x00字节不会导致问题,因为它们没有转换为字符串 . 执行指令与任何普通可执行文件一样 .
JMP / CALL / POP方法中JMP指令的原因是消除在生成的代码中插入不需要的0x00字节 . 64位代码中的JMP具有
rel8
和rel32
编码 . 在64位代码中CALL只有rel32
编码 . 这意味着如果您在64位代码中使用CALL在内存中进行小的传输,它将被编码为32位零扩展目标 . 零扩展会导致不需要的0x00值放在shell代码中 . 在64位代码中,这个CALL指令:将被编码为:
由于JMP指令支持
rel8
(相对字节位移),如果目标距离不超过127个字节,则NASM可以在内存中生成JMP指令(有符号字节为-128到127) . 这个JMP指令:将被编码为:
所以没有额外的零 . 您可能会问为什么JMP / CALL / POP方法中的CALL指令有效 . 原因是负值的符号扩展到高位字节 . 用1填充高位不会产生额外的0x00字节,因此它可以工作 . 这个CALL指令:
将被编码为:
请注意,额外的字节不是0.这就是为什么在内存中较早地调用某个位置可以避免生成零 .
样本2
如果我们记住以上内容,我们只需要检查样本2的生成代码,以查看出错的地方 .
objdump -D ./sample2 -Mintel
生成:所以我们看到CALL的问题是用额外的零编码,这就是为什么你需要一个传统的JMP / CALL / POP .
mov eax, 60
存在第二个问题,因为它编码额外的字节 . 我觉得你打算用mov al, 60
样本3
objdump -D ./sample3 -Mintel
生成:与样本2相同的问题类型 .
样本1
objdump -D ./sample1 -Mintel
生成:虽然你说
sample 1
有效但它仍然有一个需要纠正的问题 .mov rax, 60
需要mov al, 60
.