当前位置:  开发笔记 > 编程语言 > 正文

Python ctypes和函数调用

如何解决《Pythonctypes和函数调用》经验,为你挑选了2个好方法。

我的朋友制作了一个在x86上工作的小型概念验证汇编程序.我决定将它移植到x86_64,但我立即遇到了问题.

我在C中编写了一小段程序,然后编译并objdumped代码.之后我将它插入到我的python脚本中,因此x86_64代码是正确的:

from ctypes import cast, CFUNCTYPE, c_char_p, c_long

buffer = ''.join(map(chr, [ #0000000000000000 :
  0x55,                     # push   %rbp
  0x48, 0x89, 0xe5,         # mov    %rsp,%rbp
  0x48, 0x89, 0x7d, 0xf8,   # mov    %rdi,-0x8(%rbp)
  0x48, 0x8b, 0x45, 0xf8,   # mov    -0x8(%rbp),%rax
  0x48, 0x83, 0xc0, 0x0a,   # add    $0xa,%rax
  0xc9,                     # leaveq 
  0xc3,                     # retq
]))

fptr = cast(c_char_p(buffer), CFUNCTYPE(c_long, c_long))
print fptr(1234)

现在,为什么每次运行它时这个脚本都会继续执行分段错误?

我还有一个关于mprotect和没有执行标志的问题.据说可以防止缓冲区溢出等大多数基本安全漏洞.但它使用的真正原因是什么?你可以继续写,直到你点击.text,然后将你的指令注入一个漂亮的PROT_EXEC -area.当然,除非你在.text中使用写保护

但那么,为什么到处都有PROT_EXEC?它的.text段是否被写保护不是很有帮助吗?



1> Brian..:

正如vincent所提到的,这是由于分配的页面被标记为不可执行.较新的处理器支持此功能,并且它被支持它的操作系统用作增加的安全层.这个想法是为了防止某些缓冲区溢出攻击.例如.常见的攻击是溢出堆栈变量,重写返回地址以指向您插入的代码.使用不可执行的堆栈,现在只产生段错误,而不是控制进程.堆内存也存在类似的攻击.

要绕过它,你需要改变保护.这只能在页面对齐的内存上执行,因此您可能需要将代码更改为如下所示:

libc = CDLL('libc.so')

# Some constants
PROT_READ = 1
PROT_WRITE = 2
PROT_EXEC = 4

def executable_code(buffer):
    """Return a pointer to a page-aligned executable buffer filled in with the data of the string provided.
    The pointer should be freed with libc.free() when finished"""

    buf = c_char_p(buffer)
    size = len(buffer)
    # Need to align to a page boundary, so use valloc
    addr = libc.valloc(size)
    addr = c_void_p(addr)

    if 0 == addr:  
        raise Exception("Failed to allocate memory")

    memmove(addr, buf, size)
    if 0 != libc.mprotect(addr, len(buffer), PROT_READ | PROT_WRITE | PROT_EXEC):
        raise Exception("Failed to set protection on buffer")
    return addr

code_ptr = executable_code(buffer)
fptr = cast(code_ptr, CFUNCTYPE(c_long, c_long))
print fptr(1234)
libc.free(code_ptr)

注意:在释放页面之前取消设置可执行标志可能是个好主意.大多数C库在完成后实际上并没有将内存返回给操作系统,而是将它保存在自己的池中.这可能意味着他们将在不清除EXEC位的情况下在其他地方重用该页面,从而绕过了安全性优势.

另请注意,这是相当不便携的.我在linux上测试过它,但在其他任何操作系统上都没有.它不适用于Windows,可以在其他unix上购买(BSD,OsX?).



2> Cheery..:

与我的朋友做了一些研究,发现这是一个特定于平台的问题.我们怀疑在某些平台上malloc mmaps内存没有PROT_EXEC,而其他平台则有.

因此有必要在事后用mprotect改变保护等级.

拉扯的事情,花了一段时间才知道该怎么做.

from ctypes import (
    cast, CFUNCTYPE, c_long, sizeof, addressof, create_string_buffer, pythonapi
)

PROT_NONE, PROT_READ, PROT_WRITE, PROT_EXEC = 0, 1, 2, 4
mprotect = pythonapi.mprotect

buffer = ''.join(map(chr, [ #0000000000000000 :
    0x55,                     # push   %rbp
    0x48, 0x89, 0xe5,         # mov    %rsp,%rbp
    0x48, 0x89, 0x7d, 0xf8,   # mov    %rdi,-0x8(%rbp)
    0x48, 0x8b, 0x45, 0xf8,   # mov    -0x8(%rbp),%rax
    0x48, 0x83, 0xc0, 0x0a,   # add    $0xa,%rax
    0xc9,                     # leaveq 
    0xc3,                     # retq
]))

pagesize = pythonapi.getpagesize()
cbuffer = create_string_buffer(buffer)#c_char_p(buffer)
addr = addressof(cbuffer)
size = sizeof(cbuffer)
mask = pagesize - 1
if mprotect(~mask&addr, mask&addr + size, PROT_READ|PROT_WRITE|PROT_EXEC) < 0:
    print "mprotect failed?"
else:
    fptr = cast(cbuffer, CFUNCTYPE(c_long, c_long))
    print repr(fptr(1234))

推荐阅读
yzh148448
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有