Wal*_*ter 16 python linux ctypes mprotect
我正在尝试从Linux上的纯Python代码调用内联机器代码.为此,我将代码嵌入到字节文字中
code = b"\x55\x89\xe5\x5d\xc3"
Run Code Online (Sandbox Code Playgroud)
然后调用mprotect()via ctypes以允许执行包含代码的页面.最后,我尝试ctypes用来调用代码.这是我的完整代码:
#!/usr/bin/python3
from ctypes import *
# Initialise ctypes prototype for mprotect().
# According to the manpage:
# int mprotect(const void *addr, size_t len, int prot);
libc = CDLL("libc.so.6")
mprotect = libc.mprotect
mprotect.restype = c_int
mprotect.argtypes = [c_void_p, c_size_t, c_int]
# PROT_xxxx constants
# Output of gcc -E -dM -x c /usr/include/sys/mman.h | grep PROT_
# #define PROT_NONE 0x0
# #define PROT_READ 0x1
# #define PROT_WRITE 0x2
# #define PROT_EXEC 0x4
# #define PROT_GROWSDOWN 0x01000000
# #define PROT_GROWSUP 0x02000000
PROT_NONE = 0x0
PROT_READ = 0x1
PROT_WRITE = 0x2
PROT_EXEC = 0x4
# Machine code of an empty C function, generated with gcc
# Disassembly:
# 55 push %ebp
# 89 e5 mov %esp,%ebp
# 5d pop %ebp
# c3 ret
code = b"\x55\x89\xe5\x5d\xc3"
# Get the address of the code
addr = addressof(c_char_p(code))
# Get the start of the page containing the code and set the permissions
pagesize = 0x1000
pagestart = addr & ~(pagesize - 1)
if mprotect(pagestart, pagesize, PROT_READ|PROT_WRITE|PROT_EXEC):
raise RuntimeError("Failed to set permissions using mprotect()")
# Generate ctypes function object from code
functype = CFUNCTYPE(None)
f = functype(addr)
# Call the function
print("Calling f()")
f()
Run Code Online (Sandbox Code Playgroud)
这段代码段落在最后一行.
为什么我会遇到段错误?该mprotect()呼叫信号的成功,所以我应该被允许在网页中执行代码.
有没有办法修复代码?我真的可以用纯Python和当前进程调用机器代码吗?
(一些进一步的评论:我并不是真的想要实现一个目标 - 我试图了解事情是如何工作的.我也尝试使用2*pagesize而不是pagesize在mprotect()调用中排除我的5字节代码落在的情况一个页面边界 - 无论如何都应该是不可能的.我使用Python 3.1.3进行测试.我的机器是一个32位i386盒子.我知道一个可能的解决方案是从纯Python代码创建一个ELF共享对象并加载它通过ctypes,但这不是我正在寻找的答案:)
编辑:以下C版本的代码工作正常:
#include <sys/mman.h>
char code[] = "\x55\x89\xe5\x5d\xc3";
const int pagesize = 0x1000;
int main()
{
mprotect((int)code & ~(pagesize - 1), pagesize,
PROT_READ|PROT_WRITE|PROT_EXEC);
((void(*)())code)();
}
Run Code Online (Sandbox Code Playgroud)
编辑2:我在代码中发现了错误.这条线
addr = addressof(c_char_p(code))
Run Code Online (Sandbox Code Playgroud)
首先创建一个char*指向bytes实例开头的ctypes code. addressof()应用于此指针不会返回此指针指向的地址,而是返回指针本身的地址.
我设法找出实际获取代码开头地址的最简单方法是
addr = addressof(cast(c_char_p(code), POINTER(c_char)).contents)
Run Code Online (Sandbox Code Playgroud)
一个更简单的解决方案的提示将不胜感激:)
修复这一行使上面的代码"工作"(意味着它什么也没做,而不是segfaulting ...).
我对此进行了快速调试,事实证明指向code未正确构造的指针,并且在传递ffi_call()调用代码的函数指针之前,某些内部ctypes正在进行修改.
这是ffi_call_unix64()(我在64位)中的行,其中函数指针被保存到%r11:
57 movq %r8, %r11 /* Save a copy of the target fn.
Run Code Online (Sandbox Code Playgroud)
当我执行你的代码时,这是在%r11尝试调用之前加载的值:
(gdb) x/5b $r11
0x7ffff7f186d0: -108 24 -122 0 0
Run Code Online (Sandbox Code Playgroud)
这是构造指针并调用函数的修复:
raw = b"\x55\x89\xe5\x5d\xc3"
code = create_string_buffer(raw)
addr = addressof(code)
Run Code Online (Sandbox Code Playgroud)
现在,当我运行它时,我在该地址看到正确的字节,并且该函数执行正常:
(gdb) x/5b $r11
0x7ffff7f186d0: 0x55 0x89 0xe5 0x5d 0xc3
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
1581 次 |
| 最近记录: |