Python ctypes:将字符串强制转换为函数?

Python ctypes:将字符串强制转换为函数?,python,c,casting,ctypes,metasploit,Python,C,Casting,Ctypes,Metasploit,我正在阅读这篇文章,对给定的Python程序感到惊讶: 从ctypes导入* 外壳代码='\xfc\xe8\x89\x00\x00….' memorywithshell=创建字符串缓冲区(外壳代码,len(外壳代码)) 外壳=铸造(带外壳的内存,CFUNCTYPE(c_void_p)) shell() 外壳代码被缩短。有人能解释一下发生了什么事吗?我对Python和C都很熟悉,我试着阅读了模块,但还有两个主要问题: 外壳代码中存储了什么? 我知道这与C有关(在文章中,它是Metasploit

我正在阅读这篇文章,对给定的Python程序感到惊讶:

从ctypes导入*
外壳代码='\xfc\xe8\x89\x00\x00….'
memorywithshell=创建字符串缓冲区(外壳代码,len(外壳代码))
外壳=铸造(带外壳的内存,CFUNCTYPE(c_void_p))
shell()
外壳代码被缩短。有人能解释一下发生了什么事吗?我对Python和C都很熟悉,我试着阅读了模块,但还有两个主要问题:

  • 外壳代码中存储了什么?
    我知道这与C有关(在文章中,它是Metasploit的外壳代码,并选择了不同的ASCII表示法),但我无法确定它是C源代码(可能不是),还是源于某种编译(哪个?)

  • 根据第一个问题,演员阵容中发生了什么魔法

      • 如果我没有弄错的话,
        外壳代码
        ,包含特定于体系结构的编译代码,大致翻译为函数调用。(不是架构专家,代码被截断了…)

      • 因此,一旦您使用
        create\u string\u buffer
        创建了一个C风格的字符串,您就可以愚弄python,让它认为它是一个带有
        cast
        调用的函数。Python然后执行最初包含在
        外壳代码中的代码


      这里有一个有用的链接:

      让我们不要忘记,为了获得可执行代码,必须将其转换为您的机器能够理解的格式。您在那里所做的是提供一系列字节码,这些字节码可以被您的机器解释,因此您可以告诉您的机器执行它。通过提供最终字节码,您实际上跳过了编译器的工作;这种技术在实时编译器中很常见,在程序运行时必须创建可执行代码。 因此,这实际上与C(或Python,或任何其他语言)几乎没有关系,但与该代码预期运行的体系结构的细节有着巨大的关系


      那里的第一个字节码是
      CLD
      (0xfc),后面是
      调用
      指令(0xe8),该指令使代码根据字节码序列中接下来4个字节中指定的偏移量跳转到地址,依此类推。

      看看这个外壳码,我从(它弹出一个MessageBoxA)将其删除:

      分解main函数,查看调用
      real\u函数
      函数
      之间的区别:

      (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)
      
      查看eax中地址继续的数据的前3个字节:

      (gdb) x/3x $eax
      0x402000 <shellcode>:   0xfc    0x33    0xd2
      (gdb)                    ^-------^--------^---- the first 3 bytes of the 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中嵌入汇编有点奇怪,但现在我知道发生了什么:)
      (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)
      
      (gdb) x/3x $eax
      0x402000 <shellcode>:   0xfc    0x33    0xd2
      (gdb)                    ^-------^--------^---- the first 3 bytes of the shellcode
      
      (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>
         ....
      
      #!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()