Как запустить машинный код как функцию в c

#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;
}