1
votes

Comment exécuter du code machine en tant que fonction en C ++

système: Windows 10 compilateur: MinGW erreur: erreur de segmentation

J'essaie d'exécuter du code machine en tant que fonction en c ++. Voici mon code:

#include <iostream>

int main()
{
    int(*fun_ptr)(void) = ((int(*)())("\xB8\x0C\x00\x00\x00\xC3"));
    std::cout << fun_ptr();
    return 0;
}

Dans les compilateurs en ligne comme le programme ideone.com, imprimez avec succès 12 et sortez. Dans mon ordinateur, je reçois une erreur "Segmentation fault". Quelqu'un peut-il m'aider?


11 commentaires

mov eax, 0xc ret


Architecture 64 bits x64


si vous ajoutez un int fun () {return 12; } dans votre code et mettez son pointeur à la place de votre "shellcode", voyez-vous les mêmes instructions lors de sa décompilation? voir godbolt.org/z/nM859j par exemple


Juste une idée: une constante de chaîne est stockée dans un segment de données. Essayer de l'exécuter en tant que code pourrait être interdit (pour des raisons de sécurité. Ce n'est pas un segment de code.) Sinon, ce serait trop facile pour un code violent, n'est-ce pas? (Cependant, aucune idée de la raison pour laquelle vous l'avez exécuté sur un compilateur en ligne ...)


@OznOg C'était ma première idée. Je l'ai fait vice versa: j'ai cherché la sortie de int f () {return 12; } dans l'Explorateur de compilateurs. Ignorant le violon inutile rbp / rsp , cela n'a pas l'air si mal.


Non, je vois "\ x55 \ x48 \ x89 \ xE5 \ xB8 \ x0C \ x00 \ x00 \ x00 \ x5D \ xC3" mais cela ne fonctionne pas non plus


Il n'y a donc aucun moyen d'exécuter le code machine saisi par l'utilisateur en C ++?


-fno-set-stack-executable ne fonctionne pas


jetez un œil à stackoverflow.com/questions/38878823/… semble se retrouver avec quelque chose de similaire à votre problème


mal essayer de désactiver la protection de l'exécution des données


après avoir désactivé le code dep ne fonctionne toujours pas


3 Réponses :


3
votes

Une chaîne littérale telle que "\ xB8 \ x0C \ x00 \ x00 \ x00 \ xC3" est un objet de durée de stockage statique [lex.string] / 15 . Un compilateur placera généralement de tels objets littéraux de chaîne dans la section .rdata de votre binaire, c'est-à-dire dans une mémoire non exécutable en lecture seule. Par conséquent, essayer d'exécuter les octets d'un littéral de chaîne entraînera une violation d'accès. Si vous souhaitez exécuter des octets de code machine contenus dans un objet tableau global, vous devez vous assurer que votre objet est alloué dans une section exécutable. Par exemple (ciblage de Windows avec 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(*)()>(&code[0]);
    std::cout << fun_ptr();
    return 0;
}

Notez que des éléments comme celui-ci ne sont pas par nature portables et ont au mieux un comportement défini par l'implémentation. Si vous savez au moment de la construction quel code machine vous souhaitez exécuter, envisagez d'utiliser un assembleur et de simplement lier le fichier objet résultant à votre exécutable. Si vous souhaitez générer dynamiquement du code machine sous Windows, vous devrez allouer de la mémoire exécutable. Pour ce faire, créez un tableau suffisamment grand dans la mémoire exécutable (et également inscriptible) (par exemple, de manière analogue à mon exemple ci-dessus) dans lequel vous pouvez placer votre code, ou allouez dynamiquement de la mémoire exécutable, par exemple en utilisant VirtualAlloc ou en utilisant HeapAlloc à partir d'un Heap avec l'indicateur exécutable défini . Vous voudrez également être conscient de la API FlushInstructionCache


0 commentaires

0
votes

J'ai trouvé une méthode:

#include <iostream>
#include <windows.h>

using namespace std;

int main() {
    unsigned char bytes[] = "\xB8\x0C\x00\x00\x00\xC3";

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


0 commentaires

1
votes

Vous pouvez le faire en utilisant un assembleur en ligne:

#include <iostream>

int code() {
    __asm (
        ".byte 0xB8, 0x0C, 0x00, 0x00, 0x00"
    );
}

int main() {
    std::cout << code() << std::endl;
    return 0;
}


0 commentaires