đŸ’Ÿ Archived View for heyplzlookat.me â€ș articles â€ș embed-exec.gmi captured on 2024-08-18 at 17:24:39. Gemini links have been rewritten to link to archived content

View Raw

More Information

âŹ…ïž Previous capture (2024-07-08)

-=-=-=-=-=-=-

Embarquer un exécutable dans une application C

Il existe plusieurs façons d’embarquer des fichiers dans un code source C. La mĂ©thode suivante Ă  l’avantage d’ĂȘtre indĂ©pendante du compilateur utilisĂ© et donc plus portable.

Codes sources

L’archive complùte est disponible ici :

embed_binaries_c.tar.zst

emb.c :

#include <stdio.h>

int main(void)
{
    printf("Hello World !\n");
    return 0;
}

embb.h :

unsigned char emb[] = {
  0x7f, 0x45, 0x4c, 0x46, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x3e, 0x00, 0x01, 0x00, 0x00, 0x00,
  0x50, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 

};
unsigned int emb_len = 15952;

main.c :

#define _GNU_SOURCE
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <sys/mman.h>
#include "embb.h"

int
main(void)
{
    int fd = memfd_create("embb", 0);
    if (fd == -1)
        fprintf(stderr, "fd error %d\n", errno);

    int bw = write(fd, emb, emb_len);
    if (bw != emb_len)
        fprintf(stderr, "bytes write %d\n", bw);

    char *newargv[] = { "emb", NULL };
    char *newenvp[] = { NULL };

    fexecve(fd, newargv, newenvp);
    perror("fexecve");
    fprintf(stderr, "fexecve error %d\n", errno);

    return 0;
}

Compilation

make embb main

On peut alors tester les exĂ©cutables produits avec `./emb` et `./main` dans le terminal. Les exĂ©cutables donnent alors la mĂȘme sortie `Hello World !`, cela signifie que tout s’est dĂ©roulĂ© comme prĂ©vu.

Explications

Du cĂŽtĂ© du fichier emb.c, il n’y a pas grand-chose Ă  dire, le programme se contente d’afficher `Hello World !`.

Dans le fichier embb.h il y a un tableau de type `unsigned char` remplit de valeurs hexadĂ©cimaless et une variable de type `unsigned int`. À bien regarder, la valeur de cette variable correspond Ă  la taille du fichier emb en octets. C’est aussi la taille du tableau. En fait, embb.h est un hex dump formatĂ© dans le format C de l’exĂ©cutable emb. Chaque valeur hexadĂ©cimale du tableau est un octet du fichier emb. Ainsi le fichier est stockĂ© tel-quel dans un format accessible via du code C. Pour cela, l’outil xxdÂč est utilisĂ© avec la commande `xxd -i emb > embb.h`.

Enfin, le fichier main.c est le plus complexe. Le cƓur du programme est constituĂ© de trois parties :

memfd_create

#define _GNU_SOURCE
#include <sys/mman.h>
#include <stdio.h>
#include <errno.h>

int fd = memfd_create("embb", 0);
if (fd == -1)
    fprintf(stderr, "fd error %d\n", errno);

`memfd_create`ÂČ va crĂ©er un fichier anonyme dans la mĂ©moire RAM et renvoyer un file descriptor du fichier qui permet d’y accĂ©der. Attention, un file descriptor est de type `int`, c’est une abstraction des fichiers diffĂ©rente d’un file pointer de type `FILE *`. Les deux derniĂšres lignes s’assurent que l’appel systĂšme s’est dĂ©roulĂ© sans encombres.

write

#include <unistd.h>
#include <stdio.h>
#include "embb.h"

int bw = write(fd, emb, emb_len);
if (bw != emb_len)
    fprintf(stderr, "bytes write %d\n", bw);

`write`Âł va Ă©crire le contenu du tableau contenu dans le fichier embb.h dans notre fichier anonyme nouvellement crĂ©Ă© et ainsi recrĂ©er le fichier emb d’origine dans la mĂ©moire RAM. LĂ  encore, les deux derniĂšres lignes nous signalent les Ă©ventuelles erreurs.

fexecve

#include <unistd.h>
#include <stdio.h>
#include <errno.h>

char *newargv[] = { "emb", NULL };
char *newenvp[] = { NULL };

fexecve(fd, newargv, newenvp);
perror("fexecve");
fprintf(stderr, "fexecve error %d\n", errno);

`fexecve`⁎ va Ă  enfin exĂ©cuter notre binaire, qui se trouve dans la mĂ©moire RAM, en lui donnant des arguments et un environnement spĂ©cifiĂ©s respectivement par `newargv` et `newenvp`. Les deux tableaux de chaĂźnes de caractĂšre doivent se finir par la valeur `NULL` et en gĂ©nĂ©ral le premier Ă©lĂ©ment des arguments est le nom de l’exĂ©cutable appelĂ©, sinon sa valeur doit ĂȘtre `NULL`. Si l’appel de l’exĂ©cutable fonctionne correctement, le programme appelant ne se termine jamais et se transforme pour laisser la place Ă  l’exĂ©cutable appelĂ©. Dans le cas d’une erreur lors de l’appel, le programme continue et les deux derniĂšres ligne affichent un message d’erreur correspondant au problĂšme. Pour Ă©viter que le programme appelant ne soit remplacĂ© par le programme appelĂ©, il est possible d’utiliser l’appel systĂšme `fork`⁔.

Références

Âč xxd

ÂČ memfd_create

Âł write

⁎ fexecve

⁔ fork

Retour au gemlog

Retour à l’accueil

Commentaires (0)

Pas encore de commentaires

Écrire un commentaire