Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/67.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python类型和函数调用_Python_C_Assembly_Ctypes_X86 64 - Fatal编程技术网

Python类型和函数调用

Python类型和函数调用,python,c,assembly,ctypes,x86-64,Python,C,Assembly,Ctypes,X86 64,我的朋友开发了一个用于x86的小型概念验证汇编程序。我决定也为x86_64移植它,但我立即遇到了一个问题 我用C写了一小段程序,然后编译并输出代码。之后,我将其插入python脚本,因此x86_64代码是正确的: from ctypes import cast, CFUNCTYPE, c_char_p, c_long buffer = ''.join(map(chr, [ #0000000000000000 <add>: 0x55, #

我的朋友开发了一个用于x86的小型概念验证汇编程序。我决定也为x86_64移植它,但我立即遇到了一个问题

我用C写了一小段程序,然后编译并输出代码。之后,我将其插入python脚本,因此x86_64代码是正确的:

from ctypes import cast, CFUNCTYPE, c_char_p, c_long

buffer = ''.join(map(chr, [ #0000000000000000 <add>:
  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区域。当然,除非在.text中使用写保护


但是,为什么那个保护执行官到处都是呢?您的.text部分是写保护的,这不是很有帮助吗?

python甚至允许这样的使用吗?我应该学会它然后

我想口译员不希望任何寄存器被更改。如果您计划像这样使用汇编程序输出,请尝试保存在函数中使用的寄存器


顺便说一句,x86_64的调用约定不同于常规x86。如果丢失堆栈指针对齐并混合使用其他工具生成的外部对象,您可能会遇到问题。

我认为,如果不先将分配的内存设置为可执行内存,您就无法自由执行它。我从未亲自尝试过,但您可能希望检查unix函数mprotect:

VirtualProtect似乎在windows上也做了同样的事情:


与我的朋友做了一些研究,发现这是一个特定于平台的问题。我们怀疑,在某些平台上,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 <add>:
    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))
如上所述,这是由于分配的页面被标记为不可执行。较新的处理器支持这一点,并且它被支持它的操作系统用作附加的安全层。其思想是防止某些缓冲区溢出攻击。常见的攻击是使堆栈变量溢出,重写返回地址以指向插入的代码。对于不可执行堆栈,现在只会产生segfault,而不会控制进程。堆内存也存在类似的攻击

要绕过它,您需要更改保护。这只能在页对齐内存上执行,因此您可能需要将代码更改为以下内容:

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上不起作用,buy可能在其他Unix BSD、OsX上起作用?

我只想到了一种更简单的方法,但最近没有涉及到mprotect。直接为程序映射可执行空间。现在python有一个模块来完成这项工作,尽管我没有找到获取代码地址的方法。简而言之,您应该通过调用mmap来分配内存,而不是使用字符串缓冲区和间接设置执行标志。这更简单、更安全,您可以确保现在只能执行您的代码。

尽管我以前在其他地方找到过它,但这确实是正确的,只是略有变化。我用自己的答案来解释,甚至更好的答案。valloc很有用,注意,在此之后EXEC位不会被清除。但是我可能对这两个方面都不感兴趣。ctypes会注意我的调用约定是否正确,gcc输出的代码就足够了。至于如何更改寄存器,我认为x86_64的调用约定是说子程序可以自由更改大多数寄存器。这绝对是在这个主题上见过的最好的例子!