Типы Python и вызовы функций

Мой друг создал небольшой пробный ассемблер, работающий на x86. Я решил портировать его и на x86_64, но тут же возникла проблема.

Я написал небольшой фрагмент программы на C, затем скомпилировал и скопировал код. После этого я вставил его в свой скрипт python, поэтому код x86_64 правильный:

from ctypes import cast, CFUNCTYPE, c_char_p, c_long

buffer = ''.join(map(chr, [ #0000000000000000 <add>:
  0x55,                     # push   %rbp
  0x48, 0x89, 0xe5,         # mov    %rsp,%rbp
  0x48, 0x89, 0x7d, 0xf8,   # mov    %rdi,-0x8(%rbp)
  0x48, 0x8b, 0x45, 0xf8,   # mov    -0x8(%rbp),%rax
  0x48, 0x83, 0xc0, 0x0a,   # add    $0xa,%rax
  0xc9,                     # leaveq 
  0xc3,                     # retq
]))

fptr = cast(c_char_p(buffer), CFUNCTYPE(c_long, c_long))
print fptr(1234)

Итак, почему этот скрипт продолжает делать ошибки сегментации всякий раз, когда я его запускаю?

У меня еще есть вопрос о mprotect и об отсутствии флага выполнения. Говорят, что он защищает от большинства основных уязвимостей безопасности, таких как переполнение буфера. Но какова настоящая причина его использования? Вы можете просто продолжать писать, пока не нажмете .text, а затем вставьте свои инструкции в красивую PROT_EXEC-область. Если, конечно, вы не используете защиту от записи в .text

Но тогда почему вообще этот PROT_EXEC везде? Разве вам не поможет то, что ваш раздел .text защищен от записи?

Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
7
0
3 846
5
Перейти к ответу Данный вопрос помечен как решенный

Ответы 5

Допускает ли Python такое использование? Тогда я должен выучить это ...

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

Кстати, соглашение о вызовах x86_64 отличается от обычного x86. У вас могут возникнуть проблемы, если вы потеряете выравнивание указателя стека и смешаете внешние объекты, созданные с помощью других инструментов.

ctypes заботится о том, чтобы мои соглашения о вызовах были правильными, достаточно, чтобы код был выведен gcc. Что касается изменения регистров, я думал, что соглашения о вызовах x86_64 говорят, что подпрограмма может свободно изменять большинство регистров.

Cheery 09.11.2008 12:00

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

http://linux.about.com/library/cmd/blcmdl2_mprotect.htm

VirtualProtect, похоже, делает то же самое в Windows:

http://msdn.microsoft.com/en-us/library/aa366898(VS.85).aspx

Хотя я и раньше находил это где-то в другом месте, это действительно правильно, но с небольшими отклонениями. Объясняю это в собственном ответе.

Cheery 09.11.2008 03:22

Провели небольшое исследование с моим другом и выяснили, что это проблема, связанная с платформой. Мы подозреваем, что на некоторых платформах память malloc mmaps без PROT_EXEC, а на других есть.

Следовательно, необходимо впоследствии изменить уровень защиты с помощью mprotect.

Хромая вещь, потребовалось время, чтобы понять, что делать.

from ctypes import (
    cast, CFUNCTYPE, c_long, sizeof, addressof, create_string_buffer, pythonapi
)

PROT_NONE, PROT_READ, PROT_WRITE, PROT_EXEC = 0, 1, 2, 4
mprotect = pythonapi.mprotect

buffer = ''.join(map(chr, [ #0000000000000000 <add>:
    0x55,                     # push   %rbp
    0x48, 0x89, 0xe5,         # mov    %rsp,%rbp
    0x48, 0x89, 0x7d, 0xf8,   # mov    %rdi,-0x8(%rbp)
    0x48, 0x8b, 0x45, 0xf8,   # mov    -0x8(%rbp),%rax
    0x48, 0x83, 0xc0, 0x0a,   # add    $0xa,%rax
    0xc9,                     # leaveq 
    0xc3,                     # retq
]))

pagesize = pythonapi.getpagesize()
cbuffer = create_string_buffer(buffer)#c_char_p(buffer)
addr = addressof(cbuffer)
size = sizeof(cbuffer)
mask = pagesize - 1
if mprotect(~mask&addr, mask&addr + size, PROT_READ|PROT_WRITE|PROT_EXEC) < 0:
    print "mprotect failed?"
else:
    fptr = cast(cbuffer, CFUNCTYPE(c_long, c_long))
    print repr(fptr(1234))

абсолютно лучший пример, который когда-либо видел по этой теме!

mtasic85 05.11.2009 16:14
Ответ принят как подходящий

Как упоминалось в Винсент, это связано с тем, что выделенная страница помечена как неисполняемая. Новые процессоры поддерживают этот функциональность, и он используется в качестве дополнительного уровня безопасности ОС, которые его поддерживают. Идея состоит в том, чтобы защитить себя от определенных атак переполнения буфера. Например. Распространенная атака - переполнение переменной стека, переписывание адреса возврата, чтобы он указывал на код, который вы вставили. С неисполняемым стеком это теперь вызывает только segfault, а не контроль над процессом. Подобные атаки существуют и для кучи памяти.

Чтобы обойти это, нужно переделать защиту. Это может быть выполнено только в памяти, выровненной по страницам, поэтому вам, вероятно, придется изменить свой код на что-то вроде следующего:

libc = CDLL('libc.so')

# Some constants
PROT_READ = 1
PROT_WRITE = 2
PROT_EXEC = 4

def executable_code(buffer):
    """Return a pointer to a page-aligned executable buffer filled in with the data of the string provided.
    The pointer should be freed with libc.free() when finished"""

    buf = c_char_p(buffer)
    size = len(buffer)
    # Need to align to a page boundary, so use valloc
    addr = libc.valloc(size)
    addr = c_void_p(addr)

    if 0 == addr:  
        raise Exception("Failed to allocate memory")

    memmove(addr, buf, size)
    if 0 != libc.mprotect(addr, len(buffer), PROT_READ | PROT_WRITE | PROT_EXEC):
        raise Exception("Failed to set protection on buffer")
    return addr

code_ptr = executable_code(buffer)
fptr = cast(code_ptr, CFUNCTYPE(c_long, c_long))
print fptr(1234)
libc.free(code_ptr)

Примечание. Перед освобождением страницы рекомендуется снять флаг исполняемого файла. Большинство библиотек C фактически не возвращают память в ОС после завершения, а хранят ее в своем собственном пуле. Это может означать, что они будут повторно использовать страницу в другом месте, не сбрасывая бит EXEC, в обход преимущества безопасности.

Также обратите внимание, что это довольно непереносимо. Я тестировал его на Linux, но не на других ОС. Он не будет работать в Windows, но может подойти для других unix (BSD, OsX?).

Даже лучший ответ. valloc полезен, например, обратите внимание, что бит EXEC не очищается после этого. Но, возможно, меня не интересует ни один из аспектов.

Cheery 09.11.2008 11:45

Я только что придумал более простой подход, но в последнее время он не включает mprotect. Просто mmap исполняемое пространство для программы напрямую. В наши дни у python есть модуль для этого, хотя я не нашел способа получить адрес кода. Короче говоря, вы должны выделить память, вызывая mmap, вместо использования строковых буферов и косвенной установки флага выполнения. Это проще и безопаснее, вы можете быть уверены, что только ваш код может быть выполнен сейчас.

Другие вопросы по теме