无论如何要将包含编译代码而不是文件路径的字符串传递给 ctypes.CDLL?
Anyway to pass string containing compiled code instead of file path to ctypes.CDLL?
背景
我试图在 python 中调用 C 函数并发现了 ctypes 库(我对 C 和 python 的 ctypes 都很陌生),动机(无论多么愚蠢)是为了使 python 代码的速度与 c++ 相当或在竞争网站上足够接近。我已经编写了 C 代码并使用以下命令创建了一个共享库 cc -fPIC -shared -o lib.so test.c
并使用以下代码将其导入到带有 ctypes 的 python 中:
import ctypes
def main():
clib = ctypes.CDLL('./lib.so')
# ... irrelevant code below ...
main()
问题
问题是这段代码需要在我无法控制的环境中运行,即:
- 尝试创建文件时权限被拒绝
- 无法访问互联网
我已经尝试过的
- 我尝试将我的
lib.so
放在 github 上并下载它,但由于上述原因,此解决方案失败。
- 我试图在我的机器上 pickle
clib
变量,希望我可以将序列化代码存储在程序本身的字符串中,然后在受限环境中解开。但这不起作用,因为 pickle 无法序列化 clib 对象。
我想到的最后一个解决方案是在程序中将lib.so
的内容存储在一个字符串中,但是问题出现了:
#...
def main():
lib_contents = b"contents of the lib.so file"
clib = ctypes.CDLL(lib_contents) # passing the contents of the file instead of the file path
# ...
如何实现上述解决方案或任何替代解决方案?
编辑:建议的答案无效(我不知道我在这里做什么,所以很可能是我出了问题)。这是我 运行ning 从问题和接受的答案中复制的代码:
import ctypes
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
with open("./libsum.so", "rb") as file:
raw = file.read()
code = ctypes.create_string_buffer(raw)
# Get the address of the code
address = addressof(c_char_p(code))
# Get the start of the page containing the code and set the permissions
pagesize = 0x1000
pagestart = address & ~(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(address)
# Call the function
print("Calling f()")
f()
我收到以下错误:
Traceback (most recent call last):
File "/home/user/main.py", line 36, in <module>
address = addressof(c_char_p(code))
TypeError: bytes or integer address expected instead of c_char_Array_15697 instance
from ctypes import *
# int add(int x, int y)
# {
# return (x+y);
# }
code = b'\x55\x48\x89\xe5\x89\x7d\xfc\x89\x75\xf8\x8b\x55\xfc\x8b\x45' \
b'\xf8\x01\xd0\x5d\xc3'
copy = create_string_buffer(code)
address = addressof(copy)
aligned = address & ~0xfff
size = 0x2000
prototype = CFUNCTYPE(c_int, c_int, c_int)
add = prototype(address)
pythonapi.mprotect(c_void_p(aligned), size, 7)
print(add(20, 30))
说明:代码是用cc -shared -o libadd.so add.c
编译为共享库,二进制代码是用objdump -S
提取的。它被放置在一个二进制字符串(字节)对象中。使用 create_string_buffer()
创建的副本(适合检索其地址)被创建,其地址被检索并调用 mprotect()
与缓冲区分配区域对应的 2 个虚拟页面和保护值 7 ( == 读取 + 写入 + 执行)。此时该函数已准备就绪并被调用(add(20, 30)
)。打印出结果50。
背景
我试图在 python 中调用 C 函数并发现了 ctypes 库(我对 C 和 python 的 ctypes 都很陌生),动机(无论多么愚蠢)是为了使 python 代码的速度与 c++ 相当或在竞争网站上足够接近。我已经编写了 C 代码并使用以下命令创建了一个共享库 cc -fPIC -shared -o lib.so test.c
并使用以下代码将其导入到带有 ctypes 的 python 中:
import ctypes
def main():
clib = ctypes.CDLL('./lib.so')
# ... irrelevant code below ...
main()
问题
问题是这段代码需要在我无法控制的环境中运行,即:
- 尝试创建文件时权限被拒绝
- 无法访问互联网
我已经尝试过的
- 我尝试将我的
lib.so
放在 github 上并下载它,但由于上述原因,此解决方案失败。 - 我试图在我的机器上 pickle
clib
变量,希望我可以将序列化代码存储在程序本身的字符串中,然后在受限环境中解开。但这不起作用,因为 pickle 无法序列化 clib 对象。
我想到的最后一个解决方案是在程序中将lib.so
的内容存储在一个字符串中,但是问题出现了:
#...
def main():
lib_contents = b"contents of the lib.so file"
clib = ctypes.CDLL(lib_contents) # passing the contents of the file instead of the file path
# ...
如何实现上述解决方案或任何替代解决方案?
编辑:建议的答案无效(我不知道我在这里做什么,所以很可能是我出了问题)。这是我 运行ning 从问题和接受的答案中复制的代码:
import ctypes
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
with open("./libsum.so", "rb") as file:
raw = file.read()
code = ctypes.create_string_buffer(raw)
# Get the address of the code
address = addressof(c_char_p(code))
# Get the start of the page containing the code and set the permissions
pagesize = 0x1000
pagestart = address & ~(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(address)
# Call the function
print("Calling f()")
f()
我收到以下错误:
Traceback (most recent call last):
File "/home/user/main.py", line 36, in <module>
address = addressof(c_char_p(code))
TypeError: bytes or integer address expected instead of c_char_Array_15697 instance
from ctypes import *
# int add(int x, int y)
# {
# return (x+y);
# }
code = b'\x55\x48\x89\xe5\x89\x7d\xfc\x89\x75\xf8\x8b\x55\xfc\x8b\x45' \
b'\xf8\x01\xd0\x5d\xc3'
copy = create_string_buffer(code)
address = addressof(copy)
aligned = address & ~0xfff
size = 0x2000
prototype = CFUNCTYPE(c_int, c_int, c_int)
add = prototype(address)
pythonapi.mprotect(c_void_p(aligned), size, 7)
print(add(20, 30))
说明:代码是用cc -shared -o libadd.so add.c
编译为共享库,二进制代码是用objdump -S
提取的。它被放置在一个二进制字符串(字节)对象中。使用 create_string_buffer()
创建的副本(适合检索其地址)被创建,其地址被检索并调用 mprotect()
与缓冲区分配区域对应的 2 个虚拟页面和保护值 7 ( == 读取 + 写入 + 执行)。此时该函数已准备就绪并被调用(add(20, 30)
)。打印出结果50。