Ich versuche, Inline-Maschinencode aus reinem Python-Code unter Linux aufzurufen. Zu diesem Zweck bette ich den Code in ein Byte-Literal ein
code = b"\x55\x89\xe5\x5d\xc3"
und rufen dann mprotect()
über ctypes
um die Ausführung der Seite, die den Code enthält, zu ermöglichen. Schließlich versuche ich, die ctypes
um den Code aufzurufen. Hier ist mein vollständiger Code:
#!/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()
Dieser Code schlägt in der letzten Zeile fehl.
-
Warum bekomme ich einen Segfault? Die
mprotect()
Aufruf signalisiert Erfolg, so dass es mir erlaubt sein sollte, Code auf der Seite auszuführen. -
Gibt es eine Möglichkeit, den Code zu korrigieren? Kann ich den Maschinencode tatsächlich in reinem Python und innerhalb des aktuellen Prozesses aufrufen?
(Einige weitere Bemerkungen: Ich versuche nicht wirklich, ein Ziel zu erreichen - ich versuche zu verstehen, wie die Dinge funktionieren. Ich habe auch versucht, die 2*pagesize
代わりに pagesize
en el mprotect()
aufzurufen, um auszuschließen, dass meine 5 Bytes Code auf eine Seitengrenze fallen - was ohnehin unmöglich sein sollte. Ich habe Python 3.1.3 für die Tests verwendet. Mein Rechner ist ein 32-bit i386 System. Ich weiß, dass eine mögliche Lösung darin besteht, ein ELF-Shared Object aus reinem Python-Code zu erstellen und es über ctypes
aber das ist nicht die Antwort, die ich suche :)
bearbeiten : Die folgende C-Version des Codes funktioniert einwandfrei:
#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)();
}
Bearbeiten 2 : Ich habe den Fehler in meinem Code gefunden. Die Zeile
addr = addressof(c_char_p(code))
erstellt zunächst eine ctypes char*
die auf den Anfang der bytes
Instanz code
. addressof()
auf diesen Zeiger angewendet wird, gibt nicht die Adresse zurück, auf die dieser Zeiger zeigt, sondern die Adresse des Zeigers selbst.
Der einfachste Weg, den ich gefunden habe, um die Adresse des Codebeginns zu erhalten, ist
addr = addressof(cast(c_char_p(code), POINTER(c_char)).contents)
Für Hinweise auf eine einfachere Lösung wären wir dankbar :)
Wenn Sie diese Zeile korrigieren, wird der obige Code "funktionieren" (d.h. er tut nichts, anstatt einen Segfault auszulösen...).