我正在阅读 Tips for Evading Anti-Virus During Pen Testing 文章,对给定的 Python 程序感到惊讶:

from ctypes import *
shellcode = '\xfc\xe8\x89\x00\x00....'

memorywithshell = create_string_buffer(shellcode, len(shellcode))
shell = cast(memorywithshell, CFUNCTYPE(c_void_p))
shell()

缩短了shellcode。有人可以解释发生了什么吗?我熟悉 Python 和 C,我尝试阅读 ctypes 模块,但还有两个主要问题:
  • shellcode 中存储了什么?
    我知道这与 C 有关(在文章中它是来自 Metasploit 的 shellcode 并且选择了不同的 ASCII 符号),但我无法确定它是 C 源代码(可能不是)还是源自某种编译(哪一个?)。
  • 根据第一个问题, Actor 期间发生了什么魔法?
  • 最佳答案

    看看这个 shellcode,我从 here 中提取它(它弹出一个 MessageBoxA):

    #include <stdio.h>
    
    typedef void (* function_t)(void);
    
    unsigned char shellcode[] =
        "\xFC\x33\xD2\xB2\x30\x64\xFF\x32\x5A\x8B"
        "\x52\x0C\x8B\x52\x14\x8B\x72\x28\x33\xC9"
        "\xB1\x18\x33\xFF\x33\xC0\xAC\x3C\x61\x7C"
        "\x02\x2C\x20\xC1\xCF\x0D\x03\xF8\xE2\xF0"
        "\x81\xFF\x5B\xBC\x4A\x6A\x8B\x5A\x10\x8B"
        "\x12\x75\xDA\x8B\x53\x3C\x03\xD3\xFF\x72"
        "\x34\x8B\x52\x78\x03\xD3\x8B\x72\x20\x03"
        "\xF3\x33\xC9\x41\xAD\x03\xC3\x81\x38\x47"
        "\x65\x74\x50\x75\xF4\x81\x78\x04\x72\x6F"
        "\x63\x41\x75\xEB\x81\x78\x08\x64\x64\x72"
        "\x65\x75\xE2\x49\x8B\x72\x24\x03\xF3\x66"
        "\x8B\x0C\x4E\x8B\x72\x1C\x03\xF3\x8B\x14"
        "\x8E\x03\xD3\x52\x33\xFF\x57\x68\x61\x72"
        "\x79\x41\x68\x4C\x69\x62\x72\x68\x4C\x6F"
        "\x61\x64\x54\x53\xFF\xD2\x68\x33\x32\x01"
        "\x01\x66\x89\x7C\x24\x02\x68\x75\x73\x65"
        "\x72\x54\xFF\xD0\x68\x6F\x78\x41\x01\x8B"
        "\xDF\x88\x5C\x24\x03\x68\x61\x67\x65\x42"
        "\x68\x4D\x65\x73\x73\x54\x50\xFF\x54\x24"
        "\x2C\x57\x68\x4F\x5F\x6F\x21\x8B\xDC\x57"
        "\x53\x53\x57\xFF\xD0\x68\x65\x73\x73\x01"
        "\x8B\xDF\x88\x5C\x24\x03\x68\x50\x72\x6F"
        "\x63\x68\x45\x78\x69\x74\x54\xFF\x74\x24"
        "\x40\xFF\x54\x24\x40\x57\xFF\xD0";
    
    void real_function(void) {
        puts("I'm here");
    }
    
    int main(int argc, char **argv)
    {
        function_t function = (function_t) &shellcode[0];
    
        real_function();
        function();
        return 0;
    }
    

    在任何调试器下编译它,我将使用 gdb:
    > gcc shellcode.c -o shellcode
    > gdb -q shellcode.exe
    Reading symbols from shellcode.exe...done.
    (gdb)
    >
    

    反汇编 main 函数,看看调用 real_functionfunction 的区别:
    (gdb) disassemble main
    Dump of assembler code for function main:
       0x004013a0 <+0>:     push   %ebp
       0x004013a1 <+1>:     mov    %esp,%ebp
       0x004013a3 <+3>:     and    $0xfffffff0,%esp
       0x004013a6 <+6>:     sub    $0x10,%esp
       0x004013a9 <+9>:     call   0x4018e4 <__main>
       0x004013ae <+14>:    movl   $0x402000,0xc(%esp)
       0x004013b6 <+22>:    call   0x40138c <real_function> ; <- here we call our `real_function`
       0x004013bb <+27>:    mov    0xc(%esp),%eax
       0x004013bf <+31>:    call   *%eax                    ; <- here we call the address that is loaded in eax (the address of the beginning of our shellcode)
       0x004013c1 <+33>:    mov    $0x0,%eax
       0x004013c6 <+38>:    leave
       0x004013c7 <+39>:    ret
    End of assembler dump.
    (gdb)
    

    有两个 call ,我们在 <main+31> 处做一个断点,看看 eax 加载了什么:
    (gdb) break *(main+31)
    Breakpoint 1 at 0x4013bf
    (gdb) run
    Starting program: shellcode.exe
    [New Thread 2856.0xb24]
    I'm here
    
    Breakpoint 1, 0x004013bf in main ()
    (gdb) disassemble
    Dump of assembler code for function main:
       0x004013a0 <+0>:     push   %ebp
       0x004013a1 <+1>:     mov    %esp,%ebp
       0x004013a3 <+3>:     and    $0xfffffff0,%esp
       0x004013a6 <+6>:     sub    $0x10,%esp
       0x004013a9 <+9>:     call   0x4018e4 <__main>
       0x004013ae <+14>:    movl   $0x402000,0xc(%esp)
       0x004013b6 <+22>:    call   0x40138c <real_function>
       0x004013bb <+27>:    mov    0xc(%esp),%eax
    => 0x004013bf <+31>:    call   *%eax                    ; now we are here
       0x004013c1 <+33>:    mov    $0x0,%eax
       0x004013c6 <+38>:    leave
       0x004013c7 <+39>:    ret
    End of assembler dump.
    (gdb)
    

    查看eax中地址继续的数据的前3个字节:
    (gdb) x/3x $eax
    0x402000 <shellcode>:   0xfc    0x33    0xd2
    (gdb)                    ^-------^--------^---- the first 3 bytes of the shellcode
    

    因此,CPU 将 call 0x402000 ,我们的 shell 代码在 0x402000 的开头,让我们在 0x402000 处反汇编:
    (gdb) disassemble 0x402000
    Dump of assembler code for function shellcode:
       0x00402000 <+0>:     cld
       0x00402001 <+1>:     xor    %edx,%edx
       0x00402003 <+3>:     mov    $0x30,%dl
       0x00402005 <+5>:     pushl  %fs:(%edx)
       0x00402008 <+8>:     pop    %edx
       0x00402009 <+9>:     mov    0xc(%edx),%edx
       0x0040200c <+12>:    mov    0x14(%edx),%edx
       0x0040200f <+15>:    mov    0x28(%edx),%esi
       0x00402012 <+18>:    xor    %ecx,%ecx
       0x00402014 <+20>:    mov    $0x18,%cl
       0x00402016 <+22>:    xor    %edi,%edi
       0x00402018 <+24>:    xor    %eax,%eax
       0x0040201a <+26>:    lods   %ds:(%esi),%al
       0x0040201b <+27>:    cmp    $0x61,%al
       0x0040201d <+29>:    jl     0x402021 <shellcode+33>
       ....
    

    如您所见,shellcode 只不过是汇编指令,唯一的不同在于您编写这些指令的方式,它使用特殊技术使其更具可移植性,例如从不使用固定地址。

    相当于上述程序的python:
    #!python
    
    from ctypes import *
    
    shellcode_data = "\
    \xFC\x33\xD2\xB2\x30\x64\xFF\x32\x5A\x8B\
    \x52\x0C\x8B\x52\x14\x8B\x72\x28\x33\xC9\
    \xB1\x18\x33\xFF\x33\xC0\xAC\x3C\x61\x7C\
    \x02\x2C\x20\xC1\xCF\x0D\x03\xF8\xE2\xF0\
    \x81\xFF\x5B\xBC\x4A\x6A\x8B\x5A\x10\x8B\
    \x12\x75\xDA\x8B\x53\x3C\x03\xD3\xFF\x72\
    \x34\x8B\x52\x78\x03\xD3\x8B\x72\x20\x03\
    \xF3\x33\xC9\x41\xAD\x03\xC3\x81\x38\x47\
    \x65\x74\x50\x75\xF4\x81\x78\x04\x72\x6F\
    \x63\x41\x75\xEB\x81\x78\x08\x64\x64\x72\
    \x65\x75\xE2\x49\x8B\x72\x24\x03\xF3\x66\
    \x8B\x0C\x4E\x8B\x72\x1C\x03\xF3\x8B\x14\
    \x8E\x03\xD3\x52\x33\xFF\x57\x68\x61\x72\
    \x79\x41\x68\x4C\x69\x62\x72\x68\x4C\x6F\
    \x61\x64\x54\x53\xFF\xD2\x68\x33\x32\x01\
    \x01\x66\x89\x7C\x24\x02\x68\x75\x73\x65\
    \x72\x54\xFF\xD0\x68\x6F\x78\x41\x01\x8B\
    \xDF\x88\x5C\x24\x03\x68\x61\x67\x65\x42\
    \x68\x4D\x65\x73\x73\x54\x50\xFF\x54\x24\
    \x2C\x57\x68\x4F\x5F\x6F\x21\x8B\xDC\x57\
    \x53\x53\x57\xFF\xD0\x68\x65\x73\x73\x01\
    \x8B\xDF\x88\x5C\x24\x03\x68\x50\x72\x6F\
    \x63\x68\x45\x78\x69\x74\x54\xFF\x74\x24\
    \x40\xFF\x54\x24\x40\x57\xFF\xD0"
    
    shellcode = c_char_p(shellcode_data)
    
    function = cast(shellcode, CFUNCTYPE(None))
    function()
    

    关于python - ctypes:将字符串转换为函数?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/14414830/

    10-12 16:36