问题描述
实际上我正在尝试执行由 Asm x64 中的一个小程序创建的 shellcode,问题是它总是给我一个分段错误错误,即使我的 shellcode 是干净的并且我已经用正确的 C 编译了程序方式。
汇编代码:
global _start
section .text
_start:
jmp code
string: db "Hello world",0xa
code:
add al,1
xor rdi,rdi
add rdi,1
lea rsi,[rel string]
xor rdx,rdx
add rdx,12
syscall
xor rax,rax
add rax,60
xor rdi,rdi
syscall
来自程序集的 Shellcode:
\xeb\x0c\x48\x65\x6c\x6c\x6f\x20\x77\x6f\x72\x6c\x64\x0a\x04\x01\x48\x31\xff\x48\x83\xc7\x01\x48\x8d\x35\xe4\xff\xff\xff\x48\x31\xd2\x48\x83\xc2\x0c\x0f\x05\x48\x31\xc0\x48\x83\xc0\x3c\x48\x31\xff\x0f\x05
C 代码:
#include<stdio.h>
#include<string.h>
unsigned char code[] = "\xeb\x0c\x48\x65\x6c\x6c\x6f\x20\x77\x6f\x72\x6c\x64\x0a\x04\x01\x48\x31\xff\x48\x83\xc7\x01\x48\x8d\x35\xe4\xff\xff\xff\x48\x31\xd2\x48\x83\xc2\x0c\x0f\x05\x48\x31\xc0\x48\x83\xc0\x3c\x48\x31\xff\x0f\x05";
int main() {
printf("Shellcode Length: %d\n",strlen(code));
int (*ret)() = (int(*)())code;
ret();
}
C 编译命令:
gcc -fno-stack-protector -z execstack execSC.c -o execSC
即使我尝试做一些像 "\x90" 这样简单的事情,我也有同样的问题
如果我尝试用 Python 运行相同的 shellcode,它可以工作并且不会给我分段错误错误
import ctypes,mmap,sys
# Check Python version
if sys.version_info >= (3,0):
def b(string,charset='latin-1'):
if isinstance(string,bytes) and not isinstance(string,str):
return (string)
else:
return bytes(string,charset)
else:
def b(string):
return bytes(string)
def create_shellcode_function (shellcode_str):
shellcode_bytes = b(shellcode_str)
# Allocate memory with a RWX private anonymous mmap
exec_mem = mmap.mmap(-1,len(shellcode_bytes),prot = mmap.PROT_READ | mmap.PROT_WRITE | mmap.PROT_EXEC,flags = mmap.MAP_ANONYMOUS | mmap.MAP_PRIVATE)
# Copy shellcode from bytes object to executable memory
exec_mem.write(shellcode_bytes)
# Cast the memory to a C function object
ctypes_buffer = ctypes.c_int.from_buffer(exec_mem)
function = ctypes.CFUNCTYPE( ctypes.c_int64 )(ctypes.addressof(ctypes_buffer))
function._avoid_gc_for_mmap = exec_mem
# Return pointer to shell code function in executable memory
return function
shellcode = "shellcode"
create_shellcode_function(shellcode)()
解决方法
您的 Python 代码指定了 mmap.PROT_EXEC
,这使得您编写 shell 代码区域的内存可执行。但是,unsigned char code[] = "...";
直接写入可执行文件,稍后加载到不可执行内存中,这会阻止您执行 shell 代码。
现在,您指定了 -fno-stack-protector -z execstack
,但 shell 代码不在堆栈中,因此这些选项并不重要。
如您所见 on Compiler Explorer,带有 shell 代码的字符串字面量位于 .data
部分,该部分未标记为可执行。
C : int (*ret)() = (int(*)())code;
ASM: mov QWORD PTR [rbp-0x8],0x404060
所以我们知道 code
位于地址 0x404060
,它属于 .data
部分。
来自 readelf -S
的输出:
Section Headers:
[Nr] Name Type Address Offset
Size EntSize Flags Link Info Align
[ 0] NULL 0000000000000000 00000000
0000000000000000 0000000000000000 0 0 0
[14] .text PROGBITS 0000000000401050 00001050
00000000000001a5 0000000000000000 AX 0 0 16
[23] .got.plt PROGBITS 0000000000404000 00003000
0000000000000028 0000000000000008 WA 0 0 8
[24] .data PROGBITS 0000000000404040 00003040
0000000000000054 0000000000000000 WA 0 0 32
[25] .bss NOBITS 0000000000404094 00003094
0000000000000004 0000000000000000 WA 0 0 1
Key to Flags:
W (write),A (alloc),X (execute),...
所以,存储可执行代码的.text
的标志是AX
(SHF_ALLOC | SHF_EXECINSTR
),所以允许执行,但是{{1}的标志},您的 shell 代码的存储位置是 .data
,没有 WA
- 所以不允许执行。
但是,如果您将 X
放在 unsigned char code[] = "...";
函数中,everything will run fine 因为现在 shell 代码将在堆栈中:
main
但是,使用 ASM generation compiler returned: 0
Execution build compiler returned: 0
Program returned: 0
Hello world
编译时仍然会崩溃,这可以通过将 -O3
声明为 code
(https://godbolt.org/z/TG8aGe6c4) 来修复。
现在这个代码:
volatile
编译为:
int (*ret)() = (int(*)())code;
ret();