Как я могу вызвать встроенный машинный код в Python в Linux?

Я пытаюсь вызвать встроенный машинный код из чистого кода Python в Linux. Для этого я встраиваю код в байтовый литерал

code = b"\x55\x89\xe5\x5d\xc3"

, а затем вызываю mprotect () через 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()

Ошибка сегментации в последней строке.

  1. Почему я получаю ошибку сегментации? Вызов mprotect () сигнализирует об успехе, поэтому мне должно быть разрешено выполнять код на странице.

  2. Есть ли способ исправить код? Могу ли я на самом деле вызвать машинный код в чистом Python и внутри текущего процесса?

(Еще несколько замечаний: я действительно не пытаюсь достичь цели - я пытаюсь понять, как все работает. Я также пытался используйте 2 * размер страницы вместо размер страницы в вызове mprotect () , чтобы исключить случай, когда мои 5 байтов кода попадают на границу страницы, что В любом случае это должно быть невозможно. Я использовал Python 3.1.3 для тестирования. Моя машина - 32-битный i386. Я знаю, что одним из возможных решений было бы создать общий объект ELF из чистого кода Python и загрузить его через ctypes , но это не тот ответ, который я ищу :)

Изменить : следующая версия кода C работает нормально:

#include 

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)();
}

Edit 2 : Я обнаружил ошибку в своем коде. Строка

addr = addressof(c_char_p(code))

сначала создает ctypes char * , указывающий на начало байтов экземпляра кода . addressof () , примененный к этому указателю, возвращает не адрес, на который указывает этот указатель, а скорее адрес самого указателя.

Самый простой способ, который мне удалось найти, чтобы получить адрес начало кода -

addr = addressof(cast(c_char_p(code), POINTER(c_char)).contents)

Подсказки для более простого решения будут оценены :)

Исправление этой строки заставляет приведенный выше код "работать" (то есть ничего не делает вместо segfaulting ...).

16
задан Walter 26 May 2011 в 20:01
поделиться