Ir para conteúdo

Diferenca entre layout da memoria virtual e ELF sections


unc4nny

Posts Recomendados

Ola, eu recentemente comecei a me embananar com certos conceitos. Por exemplo, eu sei (acho que sei pelo menos) que todo processo tem uma memoria virtual, e a o layout dessa memoria virtual eh dividido em nos segmentos data, bss, stack, heap, etc... Mas um arquivo ELF tambem eh divido em secoes. E algumas secoes tem o mesmo nome dos segmentos da memoria virtual (.data, .text, etc). No post tem duas imagens uma com o layout da memoria e outro representando as ELF sections

Minhas duvidas:

  1. Qual a diferenca entre os segmentos da memoria virtual e as secoes de um arquivo ELF?
  2. As secoes que tem o mesmo nome sao na vdd as mesmas?
  3. Eles se complementam?
  4. Na imagem da memoria, a gnt ve que tem espacos nao alocados. As ELF section sao carregadas neles?

Obg desde ja!

unnamed.png

Elf-layout.png

Link para o comentário
Compartilhar em outros sites

Em 23/03/2020 em 18:24, unc4nny disse:

o layout dessa memoria virtual eh dividido em nos segmentos data, bss, stack, heap, etc... Mas um arquivo ELF tambem eh divido em secoes. E algumas secoes tem o mesmo nome dos segmentos da memoria virtual (.data, .text, etc).

Olha, eu não sou um grande especialista e toda vez que olho no Linux parece que tá tudo diferente mas acho que só tá te faltando o conceito de mapeamento (mapping). No caso do ELF os segmentos (no arquivo) são mapeados para a memória, ou seja, têm seu conteúdo copiado e ganham endereços lá.

Só que cada segmento pode possuir várias seções. Por exemplo, vou compilar um programa que imprime seu próprio PID e também tem uma string (pra forçar a criação da seção .data no binário):

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
  
int main(void) {
  char s[] = "Meu PID:";
  
  printf("%s %d\n", s, getpid());
  while (1);
  
  return 0;
}

Podemos compilar e analisar como ficaram os segmentos e as seções no binário (no arquivo, antes de ser carregado, ou seja, antes de ter suas seções mapeadas para a memória):

$ gcc -no-pie -o hello hello.c
$ readelf -l hello

Elf file type is EXEC (Executable file)
Entry point 0x401050
There are 11 program headers, starting at offset 64

Program Headers:
  Type           Offset             VirtAddr           PhysAddr
                 FileSiz            MemSiz              Flags  Align
  PHDR           0x0000000000000040 0x0000000000400040 0x0000000000400040
                 0x0000000000000268 0x0000000000000268  R      0x8
  INTERP         0x00000000000002a8 0x00000000004002a8 0x00000000004002a8
                 0x000000000000001c 0x000000000000001c  R      0x1
      [Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
  LOAD           0x0000000000000000 0x0000000000400000 0x0000000000400000
                 0x0000000000000470 0x0000000000000470  R      0x1000
  LOAD           0x0000000000001000 0x0000000000401000 0x0000000000401000
                 0x00000000000001dd 0x00000000000001dd  R E    0x1000
  LOAD           0x0000000000002000 0x0000000000402000 0x0000000000402000
                 0x0000000000000148 0x0000000000000148  R      0x1000
  LOAD           0x0000000000002e10 0x0000000000403e10 0x0000000000403e10
                 0x0000000000000228 0x0000000000000230  RW     0x1000
  DYNAMIC        0x0000000000002e20 0x0000000000403e20 0x0000000000403e20
                 0x00000000000001d0 0x00000000000001d0  RW     0x8
  NOTE           0x00000000000002c4 0x00000000004002c4 0x00000000004002c4
                 0x0000000000000044 0x0000000000000044  R      0x4
  GNU_EH_FRAME   0x000000000000200c 0x000000000040200c 0x000000000040200c
                 0x000000000000003c 0x000000000000003c  R      0x4
  GNU_STACK      0x0000000000000000 0x0000000000000000 0x0000000000000000
                 0x0000000000000000 0x0000000000000000  RW     0x10
  GNU_RELRO      0x0000000000002e10 0x0000000000403e10 0x0000000000403e10
                 0x00000000000001f0 0x00000000000001f0  R      0x1

 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
   03     .init .plt .text .fini
   04     .rodata .eh_frame_hdr .eh_frame
   05     .init_array .fini_array .dynamic .got .got.plt .data .bss
   06     .dynamic
   07     .note.ABI-tag .note.gnu.build-id
   08     .eh_frame_hdr
   09
   10     .init_array .fini_array .dynamic .got

Perceba que os segmentos de 02 a 05 são do tipo LOAD. Isto significa que eles serão mapeados/carregados/copiados para a memória.

Na sequência você qual seção pertence a cada segmento. Daria pra dizer o seguinte então:

  • A .text tá no segmento 03, vai ser mapeada em 401000, que tem permissão de leitura e execução (R E).
  • A .rodata tá no segmento 04, vai ser mapeada em 402000, que tem permissão de leitura somente (R).
  • A .data e .bss estão no segmento 05, vão ser mapeadas em 403e10, que tem permissão de leitura e escrita (R W).

Só que seções diferentes vão para regiões diferentes da memória do processo, como a imagem que você postou ilustra, então esse mapeamento não é um pra um. Pra checar, executa o programa e pega o PID:

$ ./hello
Meu PID: 2186

Noutra aba:

$ cat /proc/2186/maps
00400000-00401000 r--p 00000000 08:01 131608                             /tmp/hello
00401000-00402000 r-xp 00001000 08:01 131608                             /tmp/hello
00402000-00403000 r--p 00002000 08:01 131608                             /tmp/hello
00403000-00404000 r--p 00002000 08:01 131608                             /tmp/hello
00404000-00405000 rw-p 00003000 08:01 131608                             /tmp/hello
01620000-01641000 rw-p 00000000 00:00 0                                  [heap]
7f9efaa89000-7f9efaaab000 r--p 00000000 08:01 664113                     /usr/lib/x86_64-linux-gnu/libc-2.28.so
7f9efaaab000-7f9efabf3000 r-xp 00022000 08:01 664113                     /usr/lib/x86_64-linux-gnu/libc-2.28.so
7f9efabf3000-7f9efac3f000 r--p 0016a000 08:01 664113                     /usr/lib/x86_64-linux-gnu/libc-2.28.so
7f9efac3f000-7f9efac40000 ---p 001b6000 08:01 664113                     /usr/lib/x86_64-linux-gnu/libc-2.28.so
7f9efac40000-7f9efac44000 r--p 001b6000 08:01 664113                     /usr/lib/x86_64-linux-gnu/libc-2.28.so
7f9efac44000-7f9efac46000 rw-p 001ba000 08:01 664113                     /usr/lib/x86_64-linux-gnu/libc-2.28.so
7f9efac46000-7f9efac4c000 rw-p 00000000 00:00 0
7f9efac71000-7f9efac72000 r--p 00000000 08:01 664105                     /usr/lib/x86_64-linux-gnu/ld-2.28.so
7f9efac72000-7f9efac90000 r-xp 00001000 08:01 664105                     /usr/lib/x86_64-linux-gnu/ld-2.28.so
7f9efac90000-7f9efac98000 r--p 0001f000 08:01 664105                     /usr/lib/x86_64-linux-gnu/ld-2.28.so
7f9efac98000-7f9efac99000 r--p 00026000 08:01 664105                     /usr/lib/x86_64-linux-gnu/ld-2.28.so
7f9efac99000-7f9efac9a000 rw-p 00027000 08:01 664105                     /usr/lib/x86_64-linux-gnu/ld-2.28.so
7f9efac9a000-7f9efac9b000 rw-p 00000000 00:00 0
7ffcd5fbf000-7ffcd5fe0000 rw-p 00000000 00:00 0                          [stack]
7ffcd5feb000-7ffcd5fee000 r--p 00000000 00:00 0                          [vvar]
7ffcd5fee000-7ffcd5ff0000 r-xp 00000000 00:00 0                          [vdso]

Veja que temos 5 segmentos no hello, além de heap e stack. Mas eram 4 segmentos que seriam mapeados né? É, mas algumas seções deles foram mapeadas com permissões diferentes, exigindo outros segmentos. Para ver quais, pode usar o gdb, attachando no processo:

$ gdb -q -p 2186
Attaching to process 2186
Reading symbols from /tmp/hello...(no debugging symbols found)...done.
Reading symbols from /lib/x86_64-linux-gnu/libc.so.6...(no debugging symbols found)...done.
Reading symbols from /lib64/ld-linux-x86-64.so.2...(no debugging symbols found)...done.
0x000000000040116b in main ()
(gdb) info file
Symbols from "/tmp/hello".
Native process:
	Using the running image of attached process 2186.
	While running this, GDB does not access memory from...
Local exec file:
	`/tmp/hello', file type elf64-x86-64.
	Entry point: 0x401050
	0x00000000004002a8 - 0x00000000004002c4 is .interp
	0x00000000004002c4 - 0x00000000004002e4 is .note.ABI-tag
	0x00000000004002e4 - 0x0000000000400308 is .note.gnu.build-id
	0x0000000000400308 - 0x0000000000400324 is .gnu.hash
	0x0000000000400328 - 0x00000000004003a0 is .dynsym
	0x00000000004003a0 - 0x00000000004003e6 is .dynstr
	0x00000000004003e6 - 0x00000000004003f0 is .gnu.version
	0x00000000004003f0 - 0x0000000000400410 is .gnu.version_r
	0x0000000000400410 - 0x0000000000400440 is .rela.dyn
	0x0000000000400440 - 0x0000000000400470 is .rela.plt
	0x0000000000401000 - 0x0000000000401017 is .init
	0x0000000000401020 - 0x0000000000401050 is .plt
	0x0000000000401050 - 0x00000000004011d1 is .text
	0x00000000004011d4 - 0x00000000004011dd is .fini
	0x0000000000402000 - 0x000000000040200b is .rodata
	0x000000000040200c - 0x0000000000402048 is .eh_frame_hdr
	0x0000000000402048 - 0x0000000000402148 is .eh_frame
	0x0000000000403e10 - 0x0000000000403e18 is .init_array
	0x0000000000403e18 - 0x0000000000403e20 is .fini_array
	0x0000000000403e20 - 0x0000000000403ff0 is .dynamic
	0x0000000000403ff0 - 0x0000000000404000 is .got
	0x0000000000404000 - 0x0000000000404028 is .got.plt
	0x0000000000404028 - 0x0000000000404038 is .data
	0x0000000000404038 - 0x0000000000404040 is .bss

Taí, todas as seç˜ões daqueles 4 segmentos do tipo LOAD (PT_LOAD o nome certo hehe) e seus devidos endereços de início e sim. Você pode fazer um paralelo com o conteúdo do /proc/<pid>/maps e ver que faz sentido, por exemplo, a .data estar em 404028 já que a .got.plt também precisa ficar numa região com leitura e escrita e esta é listada antes da data no segmento 05. Já a .got, originalmente (no arquivo), num segmento com rw, foi para uma região somente com leitura.

Resumindo, na prática o número de regiões de memória (segmentos na imagem que você postou) pode ser outro. Pode ser confuso mesmo, porque tem segmento e seção no arquivo, depois segmento em memória e seção na memória, ou seja, 4 coisas, mas o mapeamento não é um pra um.

Espero que ajude. Brincando mais com gdb, readelf, etc aí e fazendo mais testes tenho certeza que vai ficar mais claro. ? 

Abraço!

Link para o comentário
Compartilhar em outros sites

Muito boa a explicacao, deu pra dar uma clareada absurda. Soh mais algumas duvidas:

9 hours ago, Fernando Mercês said:

a .data estar em 404028 já que a .got.plt também precisa ficar numa região com leitura e escrita

Pq a .got.plt precisa da permissao de escrita, alem da leitura? Seria por causa da relocacao em runtime, ou eu to viajando?

 

9 hours ago, Fernando Mercês said:

Você pode fazer um paralelo com o conteúdo do /proc/<pid>/maps

Bixo, esse arquivo parece mto interessante, vou pesquisar sobre ele, parece o tipo de coisa que eu preciso saber sobre. Tem alguma leitura que vc possa me direcionar sobre ele?

mto obg, mano, ajudou bagarai!!

Link para o comentário
Compartilhar em outros sites

É nóis! Obrigado pelas perguntas também! ?

2 horas atrás, unc4nny disse:

Pq a .got.plt precisa da permissao de escrita, alem da leitura? Seria por causa da relocacao em runtime, ou eu to viajando?

Sim, os endereços nela são resolvidos em loading time e o loader a preenche. Eu não lembro se falo especificamente desta seção, mas já viu esta aula do CERO? De qualquer forma, tem o livro "Learning Linux Binary Analysis" do elfmaster que tem mais informações. Eu sei pouco sobre este processo, só entendo o básico mesmo.

2 horas atrás, unc4nny disse:

Bixo, esse arquivo parece mto interessante, vou pesquisar sobre ele, parece o tipo de coisa que eu preciso saber sobre. Tem alguma leitura que vc possa me direcionar sobre ele?

Sinceramente, eu encontrei esse arquivo por minha conta quando tava tentando dumpar a memória de um processo no contexto de forense. Nunca vi documentação sobre ele, mas acho que o comando pmap o utiliza, então pode ser outra forma de buscar também, além do fonte do kernel em si claro.

Abraço!

Link para o comentário
Compartilhar em outros sites

Faz tempo que eu vi o CERO, to aproveitando o tempo livre da quarentena pra re-assistir kkkkkk. Mas eu acho que fala sim, se eu nao me engano foi no seu video que aprendi sobre isso pela primeira vez, dai eu fui dar uma pesquisada sobre, mas faz tempo tambem, esses ultimos semestres foram foda, tive que focar quase que 100% na facul e deixei isso de lado, dai to voltando aos poucos. Esse cara aqui da um gas legal nessa parte de linkadores se vc tiver interessado (acho que sao 20 posts): https://www.airs.com/blog/archives/38

Vou dar uma olhada nesse livro tambem, vlw irmao! Eh noissss, manda muito

Link para o comentário
Compartilhar em outros sites

Arquivado

Este tópico foi arquivado e está fechado para novas respostas.

  • Quem Está Navegando   0 membros estão online

    • Nenhum usuário registrado visualizando esta página.
×
×
  • Criar Novo...