unc4nny Posted March 23, 2020 at 09:24 PM Share Posted March 23, 2020 at 09:24 PM 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: Qual a diferenca entre os segmentos da memoria virtual e as secoes de um arquivo ELF? As secoes que tem o mesmo nome sao na vdd as mesmas? Eles se complementam? Na imagem da memoria, a gnt ve que tem espacos nao alocados. As ELF section sao carregadas neles? Obg desde ja! Link to comment Share on other sites More sharing options...
Fernando Mercês Posted March 25, 2020 at 03:40 AM Share Posted March 25, 2020 at 03:40 AM 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 to comment Share on other sites More sharing options...
unc4nny Posted March 25, 2020 at 01:34 PM Author Share Posted March 25, 2020 at 01:34 PM 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 to comment Share on other sites More sharing options...
Fernando Mercês Posted March 25, 2020 at 03:52 PM Share Posted March 25, 2020 at 03:52 PM É 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 to comment Share on other sites More sharing options...
unc4nny Posted March 25, 2020 at 10:05 PM Author Share Posted March 25, 2020 at 10:05 PM 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 to comment Share on other sites More sharing options...
Recommended Posts
Archived
This topic is now archived and is closed to further replies.