Linux で純粋な Python コードからインライン マシン コードを呼び出そうとしています。この目的のために、コードをバイトリテラルに埋め込みます
code = b"\x55\x89\xe5\x5d\xc3"
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()
このコードは、最後の行でセグメンテーション違反を起こします。
セグメンテーション違反が発生するのはなぜですか? 呼び出しは
mprotect()
成功を通知するので、ページでコードを実行することを許可する必要があります。コードを修正する方法はありますか?純粋な Python と現在のプロセス内で実際にマシン コードを呼び出すことはできますか?
(さらにいくつかの発言:私は実際に目標を達成しようとしているわけではありません-私は物事がどのように機能するかを理解しようとしています。また、5バイトのコードが落ちるケースを除外するために、2*pagesize
代わりにpagesize
呼び出しで使用しようとしましたmprotect()
ページ境界 -- とにかく不可能なはずです. テストには Python 3.1.3 を使用しました. 私のマシンは 32 ビットの i386 ボックスです. 考えられる解決策の 1 つは、純粋な 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)();
}
編集 2 : コードにエラーが見つかりました。この線
addr = addressof(c_char_p(code))
まず、インスタンスchar*
の先頭を指すctypes を作成します。 このポインターに適用されると、このポインターが指しているアドレスではなく、ポインター自体のアドレスが返されます。bytes
code
addressof()
コードの先頭のアドレスを実際に取得するために私が見つけた最も簡単な方法は
addr = addressof(cast(c_char_p(code), POINTER(c_char)).contents)
より簡単な解決策のヒントをいただければ幸いです:)
この行を修正すると、上記のコードが「機能」します (つまり、segfaulting の代わりに何もしません...)。