Я написал короткую программу для чтения файла obj для Windows, поиска раздела .text и запуска в нем кода. Для этого я выполняю следующие вызовы функций Windows API (Полный код [gist.github.com], для заинтересованных):
HANDLE FileHandle = CreateFile("lib.obj",
GENERIC_READ | GENERIC_EXECUTE,
FILE_SHARE_READ, 0,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
HANDLE MappingHandle = CreateFileMapping(FileHandle, 0, PAGE_EXECUTE_READ, 0, 0, 0);
void *Address = MapViewOfFile(MappingHandle, FILE_MAP_EXECUTE | FILE_MAP_READ,
0, 0, 0);
Затем я нахожу секцию .text в файле и приводю указатель на код к указателю на функцию в C ++ и просто вызываю функцию. Это на самом деле оказалось работать для меня.
Я сделал ошибку, не вызывая FlushInstructonCache для диапазона виртуальной памяти, сопоставленной с файлом?
Я спрашиваю это, потому что я недавно читал VirtualAlloc документация и примечания внизу:
При создании региона, который будет исполняемым, вызывающая программа несет ответственность за обеспечение когерентности кэша посредством соответствующего вызова FlushInstructionCache как только код был установлен на месте. В противном случае попытки выполнить код из новой исполняемой области могут привести к непредсказуемым результатам.
Возможно ли, что мой код заставит процессор выполнять старые инструкции в кеше команд?
На MapViewOfFile или же CreateFileMapping страницы.
Если вы загружаете только содержимое файла в память, используя MapViewOfFile
, должно быть хорошо без.
Если вы изменяете содержимое в памяти, вам необходимо очистить кэш инструкций перед выполнением кода, так как он МОЖЕТ существовать в кеше в неизмененном виде, а затем МОЖЕТ выполняться без ваших изменений.
Я использую слово МОЖЕТ из-за двух вещей:
от архитектуры процессора зависит, обнаружит ли процессор записи в памяти, которую он собирается выполнить [некоторые процессоры даже не имеют оборудования для регистрации записей в данных, которые находятся в кэшах инструкций — потому что это настолько редко, что это очень маловероятно].
потому что трудно предсказать, что может быть в кеше — процессоры
иметь всевозможные «умные» способы предварительной выборки и вообще «заполнять» кэши.
Очевидно, что VirtualAlloc
имеет нулевой шанс на сохранение данных, которые вы хотели, поэтому он упоминается там, потому что вы ВСЕГДА пишете в него перед выполнением.
Модификации включают, например, «исправление абсолютных адресов» (то, что вам нужно сделать, если вы хотите завершить проект, который загружает что-то сложное, чтобы выполнить его), или если вы пишете отладчик, когда вы устанавливаете точку останова путем замены инструкция с INT 3
инструкция по x86.
Второй случай «модификации» — это если вы выгружаете файл и загружаете другой файл (например, «тот же» файл, но перестроенный, например), и в этом случае ранее выполненный код все еще может находиться в кэше, и вы получаете таинственное «почему мои изменения не сделали то, что я ожидаю»
Других решений пока нет …