- Les sections n'existent pas dans le contexte d'un processus en cours d'exécution, uniquement des segments.
mprotect
peut être utilisé pour changer les permissions des pages letext
le segment est mappé. Voici un didacticiel sur la façon d'y parvenir :Écriture d'un programme C x86_64 à mutation automatique- d'après les notes sur le
mprotect
page de manuel :Sous Linux, il est toujours permis d'appeler mprotect() sur n'importe quelle adresse dans l'espace d'adressage d'un processus (à l'exception de la zone vsyscall du noyau). En particulier, il peut être utilisé pour modifier les mappages de code existants afin qu'ils soient accessibles en écriture .
Les informations de section sont stockées dans la table d'en-tête de section. La table d'en-tête de section est un tableau d'en-têtes de section. La table d'en-tête de section n'est mappée à aucun segment et n'est pas analysée par le chargeur de programme. Le chargeur utilise les informations de segment uniquement lors du mappage d'un programme dans la mémoire virtuelle.
Les segments - et non les sections - ont des autorisations, et celles-ci sont stockées dans l'en-tête du programme du segment dans le p_flags
champ. Les en-têtes de programme résident dans la table d'en-tête de programme du binaire.
Tout cela est documenté dans les chapitres 4 et 5 de l'ABI System V (générique).
Dans la sortie ci-dessous, nous pouvons voir les autorisations associées à chaque segment sous le flags
colonne :
$ readelf -l /bin/ls
Elf file type is EXEC (Executable file)
Entry point 0x404890
There are 9 program headers, starting at offset 64
Program Headers:
Type Offset VirtAddr PhysAddr
FileSiz MemSiz Flags Align
PHDR 0x0000000000000040 0x0000000000400040 0x0000000000400040
0x00000000000001f8 0x00000000000001f8 R E 8
INTERP 0x0000000000000238 0x0000000000400238 0x0000000000400238
0x000000000000001c 0x000000000000001c R 1
[Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
LOAD 0x0000000000000000 0x0000000000400000 0x0000000000400000
0x0000000000019d44 0x0000000000019d44 R E 200000
LOAD 0x0000000000019df0 0x0000000000619df0 0x0000000000619df0
0x0000000000000804 0x0000000000001570 RW 200000
DYNAMIC 0x0000000000019e08 0x0000000000619e08 0x0000000000619e08
0x00000000000001f0 0x00000000000001f0 RW 8
NOTE 0x0000000000000254 0x0000000000400254 0x0000000000400254
0x0000000000000044 0x0000000000000044 R 4
GNU_EH_FRAME 0x000000000001701c 0x000000000041701c 0x000000000041701c
0x000000000000072c 0x000000000000072c R 4
GNU_STACK 0x0000000000000000 0x0000000000000000 0x0000000000000000
0x0000000000000000 0x0000000000000000 RW 10
GNU_RELRO 0x0000000000019df0 0x0000000000619df0 0x0000000000619df0
0x0000000000000210 0x0000000000000210 R 1
Section to Segment mapping:
Segment Sections...
00
01 .interp
02 .interp .note.ABI-tag .note.gnu.build-id .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rela.dyn .rela.plt .init .plt .text .fini .rodata .eh_frame_hdr .eh_frame
03 .init_array .fini_array .jcr .dynamic .got .got.plt .data .bss
04 .dynamic
05 .note.ABI-tag .note.gnu.build-id
06 .eh_frame_hdr
07
08 .init_array .fini_array .jcr .dynamic .got
Le
.rodata
La section des fichiers ELF contient des parties du segment de texte qui ne sont pas destinées à être modifiées.
C'est faux. L'ensemble text
segment est Lecture/Exécution.
Par défaut, toutes les pages de cette section sont en lecture seule, et toute tentative de modification déclenchera une erreur de protection générale.
C'est faux. Les segments, et non les sections, sont mappés sur les pages (d'où le Align
valeurs) et avoir des permissions (d'où le Flags
valeurs).
Plus d'informations peuvent être trouvées ici :
- http://duartes.org/gustavo/blog/post/how-the-kernel-manages-your-memory/
- https://lwn.net/Articles/631631/
- http://nairobi-embedded.org/040_elf_sec_seg_vma_mappings.html#section-segment-vma-mappings
Du manuel :
Sous Linux, il est toujours permis d'appeler mprotect() sur n'importe quelle adresse dans l'espace d'adressage d'un processus (à l'exception de la zone vsyscall du noyau). En particulier, il peut être utilisé pour modifier les mappages de code existants en inscriptibles.
Voici un exemple de programme à démontrer.
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#define PAGE_SIZE 4096
const unsigned char rodata[3*PAGE_SIZE] = {1,2,3};
int main(void)
{
printf("rodata = %p\n", rodata);
uintptr_t page_base = ((uintptr_t)rodata / PAGE_SIZE + 1) * PAGE_SIZE;
unsigned char *p = (unsigned char *)rodata + PAGE_SIZE;
//*p = '!'; // this would cause a segfault
puts("Before mprotect:");
system("cat /proc/$PPID/maps");
if (mprotect((void*)page_base, 1, PROT_READ | PROT_WRITE) < 0) {
perror("mprotect");
return 1;
}
puts("After mprotect:");
system("cat /proc/$PPID/maps");
*p = '!';
return 0;
}
Bien sûr, toutes les données que vous écrivez sur la page resteront en mémoire. Linux voit que le processus écrit sur une page actuellement mappée en lecture seule et en fait une copie. Au moment de l'écriture, le noyau ne distingue pas cela de la copie sur écriture après qu'un processus ait bifurqué. Vous pouvez observer cela en bifurquant, en écrivant dans un processus et en lisant dans l'autre :l'autre processus ne verra pas l'écriture puisqu'il s'agit d'une écriture dans la mémoire du processus d'écriture, et non dans la mémoire du processus de lecture.
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <unistd.h>
#define PAGE_SIZE 4096
const unsigned char rodata[3*PAGE_SIZE] = {0};
void writer(char *p)
{
if (mprotect(p, 1, PROT_READ | PROT_WRITE) < 0) {
perror("mprotect");
return 1;
}
puts("After mprotect:");
system("cat /proc/$PPID/maps");
*p = 1;
printf("wrote %d\n", *p);
}
void reader(char *p)
{
printf("read %d\n", *p);
}
int main(void)
{
printf("rodata = %p\n", rodata);
uintptr_t page_base = (((uintptr_t)rodata / PAGE_SIZE + 1) * PAGE_SIZE);
volatile char *p = (volatile char *)page_base;
//*p = '!'; // this would cause a segfault
puts("Before mprotect:");
system("cat /proc/$PPID/maps");
if (fork() == 0) {
writer(p);
} else {
sleep(1);
reader(p);
}
return 0;
}
Je soupçonne qu'il existe des correctifs de renforcement qui empêchent un processus de modifier ses propres mappages de mémoire, mais je n'en ai pas à proposer.