我朋友制作了一个小证据概念汇编工作x86。我决定它x86_64为好,但是我马上打一个问题。

我写了一小块在C程序,然后汇编和objdumped的代码。在那之后我插到我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)

现在,为什么这个剧本不断做段错误,每当我运行了它?

我有一个问题是关于使和没有执行的标志。据说这是对保护最基本的安全漏洞,如缓冲区超支。但什么是真正的原因是在使用?你能继续写作,直到你打的.文本,然后再注入您的指示成一个很好的,PROT_EXEC区域。当然,除非你使用一个写信的保护。文本

但是,为什么有这PROT_EXEC无处不在?不这只是帮助极大,你的.文部分编写的保护?

有帮助吗?

解决方案

作为 文森特 所提到的,这是由于分配的页面被标记为不可执行的。支持这种较新的处理器 功能, 和它作为一个附加层的安全操作系统的支持。这个想法是要防止某些缓冲区溢出的攻击。例如。共同的攻击是溢出一堆变量,改写退回的地址点代码你有插入。与一个不可执行的堆这现在只产生一个出现段错误,而不是控制进程。类似的攻击,也存在对堆的记忆。

绕过它,你需要修改的保护。这只能执行页准的记忆,所以你可能需要改变你的代码喜欢的东西如下:

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上,而不是在任何其他操作系统。它不会的工作窗口,买可以做其他的unix,其情形并(BSD,Os x?).

其他提示

与我的朋友做了一些研究,发现这是一个特定于平台的问题。我们怀疑在某些平台上,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    <*>xa,%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))

我认为如果不先将其设置为可执行文件,就无法自由执行任何已分配的内存。我从未尝试过自己,但你可能想检查一下unix函数 mprotect

http://linux.about.com/library/cmd/blcmdl2_mprotect.htm

VirtualProtect 似乎在Windows上做同样的事情:

http://msdn.microsoft.com/ EN-US /库/ aa366898(VS.85)的.aspx

python甚至允许这样的使用吗?我应该学习它......

我认为解释器不希望任何寄存器被更改。如果打算像这样使用汇编器输出,请尝试保存函数内使用的寄存器。

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

我认为只有最简单的方法,但最近不涉及mprotect。明确地直接mmap程序的可执行空间。这些天python有一个完成这个的模块,虽然我没有找到获取代码地址的方法。简而言之,您将分配内存调用mmap而不是使用字符串缓冲区并间接设置执行标志。这样更简单,更安全,您可以确保现在只能执行您的代码。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top