#c #machine-code
#c #машинный код
Вопрос:
система: Windows 10 ошибка компилятора: MinGW: ошибка сегментации
Я пытаюсь запустить машинный код как функцию на c . Вот мой код:
#include <iostream>
int main()
{
int(*fun_ptr)(void) = ((int(*)())("xB8x0Cx00x00x00xC3"));
std::cout << fun_ptr();
return 0;
}
В онлайн-компиляторах, таких как ideone.com программа успешно печатает 12 и завершает работу. На моем компьютере я получаю ошибку «Ошибка сегментации». Кто-нибудь может мне помочь?
Комментарии:
1. mov eax,0xc ret
2. 64-разрядная архитектура x64
3. если вы добавляете a
int fun() { return 12; }
в свой код и помещаете его указатель вместо своего «шеллкода», видите ли вы те же инструкции при его декомпиляции? см. godbolt.org/z/nM859j например4. Просто идея: строковая константа хранится в сегменте данных. Попытка запустить это как код может быть запрещена (для безопасности. Это не сегмент кода.) В противном случае это было бы слишком просто для жестокого кода, не так ли? (Хотя, не знаю, почему вы запустили это в онлайн-компиляторе …)
5. @OznOg Это была моя первая идея. Я сделал наоборот: искал вывод
int f() { return 12; }
в проводнике компилятора. Игнорируя бесполезноеrbp
/rsp
бесполезное, это выглядит не так уж плохо.
Ответ №1:
Строковый литерал, такой как "xB8x0Cx00x00x00xC3"
, является объектом со статической продолжительностью хранения [lex.string]/15. Компилятор обычно помещает такие строковые литеральные объекты в .rdata
раздел вашего двоичного файла, то есть в доступную только для чтения неисполняемую память. Как следствие, попытка выполнить байты строкового литерала приведет к нарушению доступа. Если вы хотите выполнить байты машинного кода, содержащиеся в объекте глобального массива, вы должны убедиться, что ваш объект выделен в разделе, который является исполняемым. Например (нацеливание на Windows с помощью Visual C ):
#include <iostream>
#pragma section("runstuff", read, execute)
__declspec(allocate("runstuff"))
const unsigned char code[] = {
0xB8, 0x0C, 0x0, 0x0, 0x0, 0xC3
};
int main()
{
auto fun_ptr = reinterpret_cast<int(*)()>(amp;code[0]);
std::cout << fun_ptr();
return 0;
}
Обратите внимание, что подобные вещи по своей сути не переносимы и в лучшем случае имеют поведение, определяемое реализацией. Если во время сборки вы знаете, какой машинный код хотите запустить, рассмотрите возможность использования ассемблера и просто свяжите результирующий объектный файл с вашим исполняемым файлом. Если вы хотите динамически генерировать машинный код в Windows, вам придется выделить исполняемую память. Для этого либо создайте достаточно большой массив в исполняемой (а также доступной для записи) памяти (например, аналогично моему примеру выше), в который вы можете поместить свой код, либо динамически выделяйте исполняемую память, например, используя VirtualAlloc
или используя HeapAlloc
из кучи с установленным флагом executable . Вы также захотите быть в курсе FlushInstructionCache
API…
Ответ №2:
Вы можете сделать это с помощью встроенного ассемблера:
#include <iostream>
int code() {
__asm (
".byte 0xB8, 0x0C, 0x00, 0x00, 0x00"
);
}
int main() {
std::cout << code() << std::endl;
return 0;
}
Ответ №3:
Я нашел метод:
#include <iostream>
#include <windows.h>
using namespace std;
int main() {
unsigned char bytes[] = "xB8x0Cx00x00x00xC3";
HANDLE mem_handle = CreateFileMappingA(INVALID_HANDLE_VALUE, NULL, PAGE_EXECUTE_READWRITE, 0, sizeof(bytes), NULL);
void *mem_map = MapViewOfFile(mem_handle, FILE_MAP_ALL_ACCESS | FILE_MAP_EXECUTE, 0x0, 0x0, sizeof(bytes));
memcpy(mem_map, bytes, sizeof(bytes));
int result = ((int (*)(void))mem_map)();
cout << "argument:n" << result << 'n';
return 0;
}