Création du packer de fichiers ELF x86_64 pour Linux

Présentation


Cet article dĂ©crira la crĂ©ation d'un simple packer de fichiers exĂ©cutables pour linux x86_64. Il est supposĂ© que le lecteur est familiarisĂ© avec le langage de programmation C, le langage d'assemblage pour l'architecture x86_64 et les fichiers ELF du pĂ©riphĂ©rique. Afin de garantir la clartĂ©, la gestion des erreurs a Ă©tĂ© supprimĂ©e du code dans l'article et les implĂ©mentations de certaines fonctions n'ont pas Ă©tĂ© affichĂ©es, le code complet peut ĂȘtre trouvĂ© en cliquant sur les liens vers github ( chargeur , packer ).

L'idée est la suivante: nous transférons le fichier ELF vers le packer, et nous en obtenons un nouveau avec la structure suivante en sortie:
En-tĂȘte ELF
Titre du programme
Segment de codeTéléchargeur de fichiers ELF emballé
Fichier ELF emballé
256 octets de données aléatoires

Pour la compression, il a été décidé d'utiliser l'algorithme de Huffman, pour le chiffrement - AES-CTR avec une clé de 256 bits, à savoir l'implémentation de kokke tiny-AES-c . 256 octets de données aléatoires sont utilisés pour initialiser la clé AES et le vecteur d'initialisation à l'aide d'un générateur de nombres pseudo aléatoires, comme indiqué ci-dessous:

for(int i = 0; i < 32; i++) { seed = (1103515245*seed + 12345) % 256; key[i] = buf[seed]; } 

Cette dĂ©cision est due Ă  la volontĂ© de compliquer la rĂ©tro-ingĂ©nierie. À ce jour, j'ai rĂ©alisĂ© que la complication est insignifiante, mais je n'ai pas commencĂ© Ă  l'enlever, car je ne voulais pas y consacrer temps et Ă©nergie.

Bootloader


Tout d'abord, le travail du chargeur de dĂ©marrage sera considĂ©rĂ©. Le chargeur ne doit pas avoir de dĂ©pendances, donc toutes les fonctions nĂ©cessaires de la bibliothĂšque C standard devront ĂȘtre Ă©crites indĂ©pendamment (l'implĂ©mentation de ces fonctions est disponible par rĂ©fĂ©rence ). Il doit Ă©galement ĂȘtre indĂ©pendant de sa position.

Fonction _Start


Le chargeur de démarrage démarre à partir de la fonction _start, qui passe simplement argc et argv à main:

 .extern main .globl _start .text _start: movq (%rsp), %rdi movq %rsp, %rsi addq $8, %rsi call main 

Fonction principale


Le fichier main.c commence par définir plusieurs variables externes:

 extern void* loader_end; //    , .   //  ELF . extern size_t payload_size; //   ELF  extern size_t key_seed; //     // -   . extern size_t iv_seed; //     // -     

Tous sont déclarés comme externes afin de trouver la position des caractÚres correspondant aux variables (Elf64_Sym) dans le packer et changer leurs valeurs.

La fonction principale elle-mĂȘme est assez simple. La premiĂšre Ă©tape consiste Ă  initialiser les pointeurs vers un fichier ELF compressĂ©, un tampon de 256 octets et vers le haut de la pile. Ensuite, le fichier ELF est dĂ©chiffrĂ© et dĂ©veloppĂ©, puis il est placĂ© au bon endroit dans la mĂ©moire Ă  l'aide de la fonction load_elf, et enfin, la valeur du registre rsp revient Ă  son Ă©tat d'origine, et un saut au point d'entrĂ©e du programme se produit:

 #define SET_STACK(sp) __asm__ __volatile__ ("movq %0, %%rsp"::"r"(sp)) #define JMP(addr) __asm__ __volatile__ ("jmp *%0"::"r"(addr)) int main(int argc, char **argv) { uint8_t *payload = (uint8_t*)&loader_end; //    // ELF  uint8_t *entropy_buf = payload + payload_size; //   256- //  void *rsp = argv-1; //     struct AES_ctx ctx; AES_init_ctx_iv(&ctx, entropy_buf, key_seed, iv_seed); //  AES AES_CTR_xcrypt_buffer(&ctx, payload, payload_size); //  ELF memset(&ctx, 0, sizeof(ctx)); //   AES size_t decoded_payload_size; //  ELF char *decoded_payload = huffman_decode((char*)payload, payload_size, &decoded_payload_size); //     ELF  , //   ET_EXEC  NULL. void *load_addr = elf_load_addr(rsp, decoded_payload, decoded_payload_size); load_addr = load_elf(load_addr, decoded_payload); //  ELF  , //    //  . memset(decoded_payload, 0, decoded_payload_size); //   ELF munmap(decoded_payload, decoded_payload_size); //   //    //  ELF     AES AES_init_ctx_iv(&ctx, entropy_buf, key_seed, iv_seed); AES_CTR_xcrypt_buffer(&ctx, payload, payload_size); memset(&ctx, 0, sizeof(ctx)); SET_STACK(rsp); //    JMP(load_addr); //       } 

La réinitialisation de l'état d'AES et du fichier ELF décompressé est effectuée à des fins de sécurité - de sorte que la clé et les données déchiffrées ne sont stockées en mémoire que pour la durée d'utilisation.

Ensuite, nous considérerons l'implémentation de certaines fonctions.

load_elf


J'ai pris cette fonction de l'utilisateur github avec le surnom bediger de son rĂ©fĂ©rentiel userlandexec et je l'ai finalisĂ©e, car la fonction d'origine s'est Ă©crasĂ©e sur des fichiers comme ET_DYN. L'Ă©chec est dĂ» au fait que la valeur du premier argument de l'appel systĂšme mmap a Ă©tĂ© dĂ©finie sur NULL et que l'adresse a Ă©tĂ© renvoyĂ©e assez prĂšs du programme principal, lors d'appels ultĂ©rieurs Ă  mmap et de la copie de segments aux adresses renvoyĂ©es par eux, le code du programme principal a Ă©tĂ© Ă©crasĂ© et une erreur de segmentation s'est produite. Par consĂ©quent, il a Ă©tĂ© dĂ©cidĂ© d'ajouter l'adresse de dĂ©part en tant que paramĂštre Ă  la fonction load_elf. La fonction elle-mĂȘme passe par tous les en-tĂȘtes de programme, alloue de la mĂ©moire (son nombre doit ĂȘtre un multiple de la taille de la page) pour les segments PT_LOAD du fichier ELF, copie leur contenu dans les zones de mĂ©moire allouĂ©es et dĂ©finit les droits de lecture, d'Ă©criture et d'exĂ©cution correspondants sur ces zones:

 //      #define PAGEUP(x) (((unsigned long)x + 4095)&(~4095)) //      #define PAGEDOWN(x) ((unsigned long)x&(~4095)) void* load_elf(void *load_addr, void *mapped) { Elf64_Ehdr *ehdr = mapped; Elf64_Phdr *phdr = mapped + ehdr->e_phoff; void *text_segment = NULL; unsigned long initial_vaddr = 0; unsigned long brk_addr = 0; for(size_t i = 0; i < ehdr->e_phnum; i++, phdr++) { unsigned long rounded_len, k; void *segment; //   PT_LOAD,    if(phdr->p_type != PT_LOAD) continue; if(text_segment != 0 && ehdr->e_type == ET_DYN) { //  ET_DYN phdr->p_vaddr    , //        //    ,      //     load_addr = text_segment + phdr->p_vaddr - initial_vaddr; load_addr = (void*)PAGEDOWN(load_addr); } else if(ehdr->e_type == ET_EXEC) { //  ET_EXEC phdr->p_vaddr     load_addr = (void*)PAGEDOWN(phdr->p_vaddr); } //        rounded_len = phdr->p_memsz + (phdr->p_vaddr % 4096); rounded_len = PAGEUP(rounded_len); //        segment = mmap(load_addr, rounded_len, PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0); if(ehdr->e_type == ET_EXEC) load_addr = (void*)phdr->p_vaddr; else load_addr = segment + (phdr->p_vaddr % 4096); //         memcpy(load_addr, mapped + phdr->p_offset, phdr->p_filesz); if(!text_segment) { text_segment = segment; initial_vaddr = phdr->p_vaddr; } unsigned int protflags = 0; if(phdr->p_flags & PF_R) protflags |= PROT_READ; if(phdr->p_flags & PF_W) protflags |= PROT_WRITE; if(phdr->p_flags & PF_X) protflags |= PROT_EXEC; mprotect(segment, rounded_len, protflags); //   // , ,  k = phdr->p_vaddr + phdr->p_memsz; if(k > brk_addr) brk_addr = k; } if (ehdr->e_type == ET_EXEC) { brk(PAGEUP(brk_addr)); //  ET_EXEC ehdr->e_entry     load_addr = (void*)ehdr->e_entry; } else { //  ET_DYN ehdr->e_entry    , //           load_addr = (void*)ehdr + ehdr->e_entry; } return load_addr; //       } 

elf_load_addr


Cette fonction pour les fichiers ELF ET_EXEC renvoie NULL, car les fichiers de ce type doivent ĂȘtre situĂ©s aux adresses qui y sont spĂ©cifiĂ©es. Pour les fichiers ET_DYN, l'adresse Ă©gale Ă  la diffĂ©rence entre l'adresse de base du programme principal (c'est-Ă -dire le chargeur de dĂ©marrage), la quantitĂ© de mĂ©moire requise pour placer l'ELF dans la mĂ©moire et 4096, 4096 - l'espace nĂ©cessaire pour ne pas placer le fichier ELF juste Ă  cĂŽtĂ© du programme principal sont d'abord calculĂ©s. AprĂšs avoir calculĂ© cette adresse, il est vĂ©rifiĂ© si la zone de mĂ©moire se croise, de l'adresse donnĂ©e Ă  l'adresse de base du programme principal, avec la zone du dĂ©but du fichier ELF dĂ©compressĂ© jusqu'Ă  sa fin. En cas d'intersection, l'adresse est retournĂ©e Ă©gale Ă  la diffĂ©rence entre l'adresse de dĂ©but de l'ELF dĂ©compressĂ© et la quantitĂ© de mĂ©moire requise pour le placer, sinon l'adresse prĂ©cĂ©demment calculĂ©e est retournĂ©e.

L'adresse de base du programme est trouvĂ©e en extrayant l'adresse des en-tĂȘtes de programme du vecteur auxiliaire (vecteur auxiliaire ELF), qui se trouve aprĂšs les pointeurs vers les variables d'environnement dans la pile, et en soustrayant la taille de l'en-tĂȘte ELF:

       ---------------------------------------------------------------------------    -> [ argc ] 8 [ argv[0] ] 8 [ argv[1] ] 8 [ argv[..] ] 8 * x [ argv[n – 1] ] 8 [ argv[n] ] 8 (= NULL) [ envp[0] ] 8 [ envp[1] ] 8 [ envp[..] ] 8 [ envp[term] ] 8 (= NULL) [ auxv[0] (Elf64_auxv_t) ] 16 [ auxv[1] (Elf64_auxv_t) ] 16 [ auxv[..] (Elf64_auxv_t) ] 16 [ auxv[term] (Elf64_auxv_t) ] 16 (= AT_NULL) [  ] 0 - 16 [    ] >= 0 [   ] >= 0 [   ] 8 (= NULL) <    > 0 --------------------------------------------------------------------------- 

La structure par laquelle chaque élément du vecteur auxiliaire est décrit a la forme:

 typedef struct { uint64_t a_type; //   union { uint64_t a_val; //  } a_un; } Elf64_auxv_t; 

Une des valeurs a_type valides est AT_PHDR, a_val pointera alors vers les en-tĂȘtes du programme. Voici le code de la fonction elf_load_addr:

 void* elf_base_addr(void *rsp) { void *base_addr = NULL; unsigned long argc = *(unsigned long*)rsp; char **envp = rsp + (argc+2)*sizeof(unsigned long); //    //   while(*envp++); //        Elf64_auxv_t *aux = (Elf64_auxv_t*)envp; //    //  for(; aux->a_type != AT_NULL; aux++) { //        if(aux->a_type == AT_PHDR) { //   ELF ,     //      base_addr = (void*)(aux->a_un.a_val – sizeof(Elf64_Ehdr)); break; } } return base_addr; } size_t elf_memory_size(void *mapped) { Elf64_Ehdr *ehdr = mapped; Elf64_Phdr *phdr = mapped + ehdr->e_phoff; size_t mem_size = 0, segment_len; for(size_t i = 0; i < ehdr->e_phnum; i++, phdr++) { if(phdr->p_type != PT_LOAD) continue; segment_len = phdr->p_memsz + (phdr->p_vaddr % 4096); mem_size += PAGEUP(segment_len); } return mem_size; } void* elf_load_addr(void *rsp, void *mapped, size_t mapped_size) { Elf64_Ehdr *ehdr = mapped; if(ehdr->e_type == ET_EXEC) return NULL; size_t mem_size = elf_memory_size(mapped) + 0x1000; void *load_addr = elf_base_addr(rsp); if(mapped < load_addr && mapped + mapped_size > load_addr - mem_size) load_addr = mapped; return load_addr - mem_size; } 

Description du script de l'Ă©diteur de liens


Il est nĂ©cessaire de dĂ©finir les caractĂšres des variables externes dĂ©crites ci-dessus et de s'assurer Ă©galement que le code et les donnĂ©es du chargeur aprĂšs compilation sont dans la mĂȘme section .text. Cela est nĂ©cessaire pour extraire facilement le code machine du chargeur en coupant simplement le contenu de cette section du fichier. Pour atteindre ces objectifs, le script de l'Ă©diteur de liens suivant a Ă©tĂ© Ă©crit:

 ENTRY(_start) SECTIONS { . = 0; .text :{ *(.text) *(.text.startup) *(.data) *(.rodata) payload_size = .; QUAD(0) key_seed = .; QUAD(0) iv_seed = .; QUAD(0) loader_end = .; } } 

Il vaut la peine d'expliquer que QUAD (0) place 8 octets de zéros, au lieu desquels le packer substituera des valeurs spécifiques. Pour découper le code machine, un petit utilitaire a été écrit qui écrit également au début du code machine le décalage du point d'entrée vers le chargeur de démarrage depuis le début du chargeur de démarrage, le décalage des valeurs des caractÚres payload_size, key_seed et iv_seed depuis le début du chargeur de démarrage. Le code de cet utilitaire est disponible ici . Ceci termine la description du chargeur de démarrage.

Emballeur direct


Considérez la fonction principale du packer. Il utilise deux arguments de ligne de commande: le nom du fichier d'entrée est argv [1] et le nom du fichier de sortie est argv [2]. Tout d'abord, le fichier d'entrée est affiché en mémoire et vérifié pour la compatibilité avec le packer. Le packer fonctionne avec seulement deux types de fichiers ELF: ET_EXEC et ET_DYN, et uniquement avec ceux compilés statiquement. La raison de l'introduction de cette restriction était le fait que différents systÚmes Linux ont différentes versions de bibliothÚques partagées, c'est-à-dire la probabilité qu'un programme compilé dynamiquement ne démarre pas sur un systÚme autre que le systÚme parent est assez élevée. Le code correspondant dans la fonction principale:

 size_t mapped_size; void *mapped = map_file(argv[1], &mapped_size); if(check_elf(mapped) < 0) return 1; 

AprÚs cela, si le fichier d'entrée réussit le contrÎle de compatibilité, il est compressé:

 size_t comp_size; uint8_t *comp_buf = huffman_encode(mapped, &comp_size); 

Ensuite, l'état AES est généré et le fichier ELF compressé est chiffré. L'état d'AES est déterminé par la structure suivante:

 #define AES_ENTROPY_BUFSIZE 256 typedef struct { uint8_t entropy_buf[AES_ENTROPY_BUFSIZE]; // 256-  size_t key_seed; //      size_t iv_seed; //       struct AES_ctx ctx; //  AES-CTR } AES_state_t; 

Code correspondant en principal:

 AES_state_t aes_st; for(int i = 0; i < AES_ENTROPY_BUFSIZE; i++) state.entropy_buf[i] = rand() % 256; state.key_seed = rand(); state.iv_seed = rand(); AES_init_ctx_iv(&state.ctx, state.entropy_buf, state.key_seed, state.iv_seed); AES_CTR_xcrypt_buffer(&aes_st.ctx, comp_buf, comp_size); 

AprÚs cela, la structure qui stocke les informations sur le chargeur de démarrage est initialisée, les valeurs payload_size, key_seed et iv_seed dans le chargeur de démarrage sont remplacées par celles générées à l'étape précédente, aprÚs quoi l'état AES est réinitialisé. Les informations sur le chargeur de démarrage sont stockées dans la structure suivante:

 typedef struct { char *loader_begin; //      size_t entry_offset; //       size_t *payload_size_patch_offset; //     // ELF    size_t *key_seed_pacth_offset; //     //       size_t *iv_seed_patch_offset; //     //     //    size_t loader_size; //     } loader_t; 

Code correspondant en principal:

 loader_t loader; init_loader(&loader); *loader.payload_size_patch_offset = comp_size; *loader.key_seed_pacth_offset = aes_st.key_seed; *loader.iv_seed_patch_offset = aes_st.iv_seed; memset(&aes_st.ctx, 0, sizeof(aes_st.ctx)); 

Dans la derniĂšre partie, nous crĂ©ons un fichier de sortie, Ă©crivons un en-tĂȘte ELF, un en-tĂȘte de programme, un code de chargeur, un fichier ELF compressĂ© et chiffrĂ© et un tampon de 256 octets:

 int out_fd = open(argv[2], O_WRONLY | O_CREAT | O_TRUNC, 0755); //  //   write_elf_ehdr(out_fd, &loader); //  ELF  write_elf_phdr(out_fd, &loader, comp_size); //    write(out_fd, loader.loader_begin, loader.loader_size); //   write(out_fd, comp_buf, comp_size); //     ELF write(out_fd, aes_st.entropy_buf, AES_ENTROPY_BUFSIZE); //  // 256-  

Le code principal du packer se termine ici, puis les fonctions suivantes seront considĂ©rĂ©es: la fonction d'initialisation des informations sur le chargeur de dĂ©marrage, la fonction d'Ă©criture de l'en-tĂȘte ELF et la fonction d'Ă©criture de l'en-tĂȘte du programme.

Initialisation des informations du chargeur de démarrage


Le code machine du chargeur est intégré dans l'exécutable du packer à l'aide du code simple ci-dessous:

 .data .globl _loader_begin .globl _loader_end _loader_begin: .incbin "loader" _loader_end: 

Afin de déterminer son adresse en mémoire, les variables suivantes sont déclarées dans le fichier main.c:

 extern void* _loader_begin; extern void* _loader_end; 

Ensuite, considérez la fonction init_loader. Tout d'abord, les valeurs suivantes y sont lues de maniÚre séquentielle: le décalage du point d'entrée depuis le début du chargeur de démarrage (entry_offset), le décalage de la taille du fichier ELF compressé depuis le début du chargeur de démarrage (payload_size_patch_offset), le décalage de la valeur initiale du générateur pour la clé depuis le début du chargeur de démarrage (key_seed_patch le vecteur initial du décalage) initialisation depuis le début du chargeur de démarrage (iv_seed_patch_offset). Ensuite, l'adresse du chargeur est ajoutée aux trois derniÚres valeurs, donc lorsque vous déréférencer des pointeurs et leur attribuer des valeurs, nous remplacerons les zéros attribués au stade de la mise en page (QUAD (0)) par les valeurs dont nous avons besoin.

 void init_loader(loader_t *l) { void *loader_begin = (void*)&_loader_begin; l->entry_offset = *(size_t*)loader_begin; loader_begin += sizeof(size_t); l->payload_size_patch_offset = *(void**)loader_begin; loader_begin += sizeof(void*); l->key_seed_pacth_offset = *(void**)loader_begin; loader_begin += sizeof(void*); l->iv_seed_patch_offset = *(void**)loader_begin; loader_begin += sizeof(void*); l->payload_size_patch_offset = (size_t)l->payload_size_patch_offset + loader_begin; l->key_seed_pacth_offset = (size_t)l->key_seed_pacth_offset + loader_begin; l->iv_seed_patch_offset = (size_t)l->iv_seed_patch_offset + loader_begin; l->loader_begin = loader_begin; l->loader_size = (void*)&_loader_end - loader_begin; } 


write_elf_ehdr


 void write_elf_ehdr(int fd, loader_t *loader) { //  ELF  Elf64_Ehdr ehdr; memset(ehdr.e_ident, 0, sizeof(ehdr.e_ident)); memcpy(ehdr.e_ident, ELFMAG, SELFMAG); ehdr.e_ident[EI_CLASS] = ELFCLASS64; ehdr.e_ident[EI_DATA] = ELFDATA2LSB; ehdr.e_ident[EI_VERSION] = EV_CURRENT; ehdr.e_ident[EI_OSABI] = ELFOSABI_NONE; ehdr.e_type = ET_DYN; ehdr.e_machine = EM_X86_64; ehdr.e_version = EV_CURRENT; ehdr.e_entry = sizeof(Elf64_Ehdr) + sizeof(Elf64_Phdr) + loader->entry_offset; ehdr.e_phoff = sizeof(Elf64_Ehdr); ehdr.e_shoff = 0; ehdr.e_flags = 0; ehdr.e_ehsize = sizeof(Elf64_Ehdr); ehdr.e_phentsize = sizeof(Elf64_Phdr); ehdr.e_phnum = 1; ehdr.e_shentsize = sizeof(Elf64_Shdr); ehdr.e_shnum = 0; ehdr.e_shstrndx = 0; write(fd, &ehdr, sizeof(ehdr)); //     return 0; } 

Ici, l'initialisation standard de l'en-tĂȘte ELF a lieu et son enregistrement ultĂ©rieur dans un fichier, la seule chose Ă  laquelle vous devez faire attention est le fait que dans les fichiers ETF ELF, le segment dĂ©crit par le premier en-tĂȘte de programme comprend non seulement le code exĂ©cutable, mais Ă©galement l'en-tĂȘte ELF et tous les en-tĂȘtes programmes. Par consĂ©quent, son dĂ©calage depuis le dĂ©but doit ĂȘtre Ă©gal Ă  zĂ©ro, la taille doit ĂȘtre la somme de la taille de l'en-tĂȘte ELF, de tous les en-tĂȘtes de programme et du code exĂ©cutable, et le point d'entrĂ©e est dĂ©terminĂ© comme la somme de la taille de l'en-tĂȘte ELF, de la taille de tous les en-tĂȘtes de programme et du dĂ©calage par rapport au dĂ©but du code exĂ©cutable.

write_elf_phdr


 void write_elf_phdr(int fd, loader_t *loader, size_t payload_size) { //    Elf64_Phdr phdr; phdr.p_type = PT_LOAD; phdr.p_offset = 0; phdr.p_vaddr = 0; phdr.p_paddr = 0; phdr.p_filesz = sizeof(Elf64_Ehdr) + sizeof(Elf64_Phdr) + loader->loader_size + payload_size + AES_ENTROPY_BUFSIZE; phdr.p_memsz = phdr.p_filesz; phdr.p_flags = PF_R | PF_W | PF_X; phdr.p_align = 0x1000; write(fd, &phdr, sizeof(phdr)); //      } 

Ici, l'en-tĂȘte du programme est initialisĂ© puis Ă©crit dans un fichier. Vous devez faire attention au dĂ©calage par rapport au dĂ©but du fichier et Ă  la taille du segment dĂ©crit par l'en-tĂȘte du programme. Comme dĂ©crit dans le paragraphe prĂ©cĂ©dent, le segment dĂ©crit par cet en-tĂȘte comprend non seulement le code exĂ©cutable, mais Ă©galement l'en-tĂȘte ELF et l'en-tĂȘte de programme. Nous rendons Ă©galement le segment avec du code exĂ©cutable accessible pour l'Ă©criture, cela est dĂ» au fait que l'implĂ©mentation AES utilisĂ©e dans le chargeur de dĂ©marrage crypte et dĂ©crypte les donnĂ©es «en place».

Quelques faits sur le travail de l'emballeur


Lors des tests, il a été remarqué que les programmes compilés statiquement avec glibc vont en segfault au démarrage, sur cette instruction:

  movq% fs: 0x28,% rax 

Je n'ai pas pu comprendre pourquoi cela se produit, je serai heureux si vous partagez des informations Ă  ce sujet. Au lieu de glibc, vous pouvez utiliser musl-libc, tout fonctionne sans problĂšme. En outre, le packer a Ă©tĂ© testĂ© avec des programmes golang compilĂ©s statiquement, par exemple, un serveur http. Pour les plantages statiques complets des programmes golang, les indicateurs suivants doivent ĂȘtre utilisĂ©s:

  CGO_ENABLED = 0 go build -a -ldflags '-extldflags "-static"'. 

La derniĂšre chose avec laquelle le packer a Ă©tĂ© testĂ© Ă©tait les fichiers EL_ ET_DYN sans Ă©diteur de liens dynamique. Certes, lorsque vous travaillez avec ces fichiers, la fonction elf_load_addr peut Ă©chouer. En pratique, il peut ĂȘtre coupĂ© du chargeur de dĂ©marrage et utiliser une adresse fixe, par exemple 0x10000.

Conclusion


Ce packer, évidemment, n'a pas de sens à utiliser pour son usage prévu, car les fichiers protégés par lui sont assez facilement décryptés. L'objectif de ce projet était de mieux maßtriser l'utilisation des fichiers ELF, la pratique de leur génération, ainsi que la préparation de la création d'un packer plus complet.

Source: https://habr.com/ru/post/fr483368/


All Articles