如何在Linux上调用Python中的内联机器码? [英] How can I call inlined machine code in Python on Linux?
本文介绍了如何在Linux上调用Python中的内联机器码?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!
问题描述
我正在尝试从Linux上的纯Python代码调用内联机器码。为此,我将代码嵌入到字节文字中
code = b"x55x89xe5x5dxc3"
,然后通过ctypes
调用mprotect()
以允许执行包含代码的页面。最后,我尝试使用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"x55x89xe5x5dxc3"
# 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()
此代码在最后一行出现分段错误。
为什么会出现段错误?
mprotect()
调用表示成功,因此应该允许我执行页面中的代码。有没有办法修复代码?我真的可以在纯Python中和当前进程内部调用机器代码吗?
(一些进一步的评论:我并不是真的想实现一个目标--我只是想了解事物是如何工作的。我还尝试在mprotect()
调用中使用2*pagesize
而不是pagesize
,以排除我的5个字节的代码落在页面边界上的情况--无论如何这应该是不可能的。我使用了Python3.1.3进行测试。我的机器是一台32位i386机顶盒。我知道一种可能的解决方案是从纯Python代码创建一个ELF共享对象,并通过ctypes
加载它,但这不是我想要的答案:)
编辑:以下C版本的代码运行正常:
#include <sys/mman.h>
char code[] = "x55x89xe5x5dxc3";
const int pagesize = 0x1000;
int main()
{
mprotect((int)code & ~(pagesize - 1), pagesize,
PROT_READ|PROT_WRITE|PROT_EXEC);
((void(*)())code)();
}
编辑2:我在代码中发现错误。行
addr = addressof(c_char_p(code))
首先创建指向bytes
实例code
开头的ctypechar*
。addressof()
应用于此指针不返回此指针所指向的地址,而是返回指针本身的地址。
我设法弄清楚实际获得代码开头地址的最简单方法是
addr = addressof(cast(c_char_p(code), POINTER(c_char)).contents)
建议使用更简单的解决方案将不胜感激:)
修复此行将使上面的代码"工作"(意味着它不执行任何操作,而不是执行分段错误...)。
推荐答案
我对此进行了快速调试,结果发现指向code
的指针为
构造不正确,内部某个地方的ctype正在运行
在将函数指针传递给调用
代码。
ffi_call_unix64()
(我使用64位)中保存函数指针的行
进入%r11
:
57 movq %r8, %r11 /* Save a copy of the target fn.
当我执行您的代码时,下面是刚刚加载到%r11
中的值
它尝试调用:
(gdb) x/5b $r11
0x7ffff7f186d0: -108 24 -122 0 0
以下是构造指针和调用函数的修复:
raw = b"x55x89xe5x5dxc3"
code = create_string_buffer(raw)
addr = addressof(code)
现在,当我运行它时,我在该地址看到了正确的字节,函数
执行正常:
(gdb) x/5b $r11
0x7ffff7f186d0: 0x55 0x89 0xe5 0x5d 0xc3
这篇关于如何在Linux上调用Python中的内联机器码?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!
查看全文