外壳代码适用于Python,但不适用于C

2024-06-24 11:52:23 发布

您现在位置:Python中文网/ 问答频道 /正文

实际上,我正在尝试执行一个由Asm x64中的一个小程序创建的外壳代码,问题是它总是给我一个分段错误,即使我的外壳代码是干净的,并且我已经用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

来自程序集的外壳代码:

\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运行相同的外壳代码,它会工作,不会给我分段错误

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)()

Tags: 代码stringbytescodefunction外壳ctypesmmap
1条回答
网友
1楼 · 发布于 2024-06-24 11:52:23

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的标志是AXSHF_ALLOC | SHF_EXECINSTR),因此允许执行,但是存储shell代码的.data的标志是WA,不X,因此不允许执行


但是,如果将unsigned char code[] = "...";放在main函数中,everything will run fine,因为现在shell代码将在堆栈上:

ASM generation compiler returned: 0
Execution build compiler returned: 0
Program returned: 0
Hello world

但是,使用-O3编译时仍然会崩溃,这可以通过将code声明为volatilehttps://godbolt.org/z/TG8aGe6c4)来修复

现在这个代码:

int (*ret)() = (int(*)())code;
ret();

汇编至:

main:
        push    rbx
        sub     rsp, 64
        ...
        # Move the stack pointer to `rbx`
        mov     rbx, rsp
        ... # rbx is never assigned to
        # Print the shell code length
        call    printf

        xor     eax, eax
        # Now start executing
        # data on the stack, since
        # rbx == rsp
        call    rbx

        add     rsp, 64
        xor     eax, eax
        pop     rbx
        ret

相关问题 更多 >