поместите содержимое array.array в кучу - PullRequest
1 голос
/ 21 июля 2010

У меня есть простой байтовый массив, который я заполнил x86 -программой.Что мне нужно выполнить во время выполнения.

"""
    Produces a simple callable procedure which returns a constant.
"""
from array import array

simple = array('B')

# mov rax, 0x10
simple.extend((0x81, 0xc0, 0x10, 0x0, 0x0, 0x0))
# ret
simple.append(0xc3)

Теперь, чтобы запустить этот процесс, мне нужно разгрузить его в область памяти в моем процессе с флагами PROT_EXEC.Также необходимо знать адрес этой области памяти, чтобы я мог ее назвать.Как я могу сделать то, что я только что описал?

from ctypes import CFUNCTYPE, c_int

procedure = CFUNCTYPE(c_int)(program.address)
print "result correct: %r" % (procedure() == 0x10)
print "result: %r" % procedure()

Кроме того, это может быть полезно сделать:

program[2] = 15

print "result correct: %r" % (procedure() == 15)
print "result: %r" % procedure()

1 Ответ

1 голос
/ 25 июля 2010

Я решил это самостоятельно. Может быть, в любом случае сказать особо нечего.

Я сделал библиотеку для этого. Это небольшая оболочка команды linux mmap.

модуля mmap, предоставленного python, было недостаточно. Я не мог получить адрес из объекта. Вместо этого я должен был предоставить свой собственный модуль для этого.

# -*- coding: utf-8 -*-
from ctypes import (
    pythonapi, c_void_p, c_size_t, c_int, c_uint64,
    c_byte, cast, POINTER, memmove, string_at,
)
import errno

mmap = pythonapi.mmap
mmap.restype = c_void_p
mmap.argtypes = [c_void_p, c_size_t, c_int, c_int, c_int, c_uint64]

munmap = pythonapi.munmap
munmap.restype = c_int
munmap.argtypes = [c_void_p, c_size_t]

errno_location = pythonapi.__errno_location
errno_location.restype = POINTER(c_int)

errormessage = lambda: errno.errorcode[errno_location()[0]]

PROT_NONE = 0
PROT_READ = 1
PROT_WRITE = 2
PROT_EXEC = 4

MAP_SHARED = 1
MAP_PRIVATE = 2
MAP_ANONYMOUS = 0x20

class RawData(object):
    "Allocated with mmap -call, no file handles."
    def __init__(self, length, prot):
        flags = MAP_PRIVATE | MAP_ANONYMOUS
        self.address = mmap(None, length, prot, flags, -1, 0)
        if 0 == self.address:
            raise Exception(errormessage())
        self.__length = length
        self.__accessor = cast(self.address, POINTER(c_byte))

    def __len__(self):
        return self.__length

    def __getitem__(self, key):
        assert key < len(self)
        return self.__accessor[key]

    def __setitem__(self, key, value):
        assert key < len(self)
        self.__accessor[key] = value

    def close(self):
        "the mapped memory must be freed manually"
        if 0 != munmap(self.address, len(self)):
            raise Exception(errormessage())

    def poke(self, offset, data):
        "poke data (from a tuple) into requested offset"
        for i, byte in enumerate(data):
            self[offset+i] = byte

    def upload(self, data, offset=0):
        "upload the data from a string"
        data = data.tostring()
        assert offset+len(data) <= len(self)
        memmove(self.address+offset, data, len(data))

    def tostring(self):
        return string_at(self.address, len(self))

__all__ = [
    'PROT_NONE',
    'PROT_READ',
    'PROT_WRITE',
    'PROT_EXEC',
    'RawData',
]

Я также написал вспомогательную библиотеку для целых чисел с прямым порядком байтов, которая дополняет цепочку инструментов:

# -*- coding: utf-8 -*-

TWOPOWER32 = 1 << 32
TWOPOWER64 = 1 << 64

TWOPOWER31 = TWOPOWER32 >> 1
TWOPOWER63 = TWOPOWER64 >> 1

def uint32(value):
    assert 0 <= value < TWOPOWER32
    return (
        value >> 0 & 255,
        value >> 8 & 255,
        value >> 16 & 255,
        value >> 24 & 255
    )

def uint64(value):
    assert 0 <= value < TWOPOWER64
    return (
        value >> 0 & 255,
        value >> 8 & 255,
        value >> 16 & 255,
        value >> 24 & 255,
        value >> 32 & 255,
        value >> 40 & 255,
        value >> 48 & 255,
        value >> 56 & 255
    )

def int32(value):
    assert -TWOPOWER31 <= value < TWOPOWER31
    return uint32((TWOPOWER32 + value) & (TWOPOWER32-1))

def int64(value):
    assert -TWOPOWER63 <= value < TWOPOWER63
    return uint64((TWOPOWER64 + value) & (TWOPOWER64-1))

__all__ = ['uint32', 'int32', 'uint64', 'int64']

Это простые вещи. Вот пример использования:

from ctypes import CFUNCTYPE, c_int
from array import array
#... bunch of imports

simple = array('B')

# x86 and x64 machine code (MOV eax, 0x10; RET)
simple.extend((0x81, 0xc0) + int32(0x10))
simple.append(0xc3)

program = RawData(len(simple), PROT_READ|PROT_WRITE|PROT_EXEC)
program.upload(simple)

procedure = CFUNCTYPE(c_int)(program.address)
print "result:", procedure()

# alters the first instruction
program.poke(2, int32(123))
print "result:", procedure()

# transforms that first instruction into (NOP,NOP,NOP,NOP,NOP,NOP)
program.poke(0, [0x90]*6)
print "result:", procedure()

Думаю, мне будет весело с этим. http://hg.boxbase.org/ будет в конечном итоге размещать этот модуль.

Я использую код операции и ссылки на инструкции для выбора инструкций. Вот несколько таких ссылок:

...