remoção de conteúdo antigo da aula 8

This commit is contained in:
Blau Araujo 2025-04-01 09:43:10 -03:00
parent 957cc38a87
commit 24e648fdec
10 changed files with 0 additions and 417 deletions

Binary file not shown.

View file

@ -1,99 +0,0 @@
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define LINE_LEN 256 // Quantidade de bytes para as linhas.
#define PATH_LEN 256 // Quantidade de bytes para os caminhos.
#define PERM_LEN 5 // Quantidade de bytes para as permissões.
#define PROG_END 5 // Fim das linhas do programa.
typedef unsigned long u64t;
typedef int i32t;
void read_maps(void);
int main(void) {
read_maps();
return 0;
}
/*
* Lê e imprime o conteúdo de /proc/self/maps filtrando
* as faixas dos segmentos de memória.
*/
void read_maps(void) {
// Define um descritor de arquivos para ler /proc/self/maps...
FILE *fd = fopen("/proc/self/maps", "r");
// Terminar no caso de erro...
if (!fd) {
perror("Erro ao abrir /proc/self/maps");
exit(EXIT_FAILURE);
}
char line[LINE_LEN]; // Linhas lidas do arquivo.
u64t start; // Endereço inicial.
u64t end; // Endereço final.
char perm[PERM_LEN]; // Permissões.
char path[PATH_LEN]; // Caminho do arquivo carregado.
i32t current_line = 1; // Linha atual.
// Itera as linhas de /proc/self/maps...
while (fgets(line, sizeof(line), fd)) {
// Zera a string em 'path'...
path[0] = '\0';
/*
* Analisa a linha conforme os campos de /proc/self/maps:
* ADDR_START-ADDR_END PERM FILE_OFFSET DEVICE INODE FILE_PATH
*/
sscanf(line, "%lx-%lx %4s %*s %*s %*s %255[^\n]", &start, &end, perm, path);
// Impressão dos segmentos do código...
if (current_line <= PROG_END) {
printf("0x%lx-0x%lx %s --> ", start, end, perm);
switch (current_line) {
case 1:
puts("Tabela de cabeçalhos do programa");
break;
case 2:
puts("Código do programa (.text)");
break;
case 3:
puts("Dados constantes (.rodata)");
break;
case 4:
puts("Tabela de dados globais e dinâmicos");
break;
case 5:
puts("Variáveis globais e estáticas (.data e .bss)");
break;
}
current_line++;
continue;
}
// Impressão da faixa do HEAP...
if (strstr(path, "[heap]")) {
printf("0x%lx-0x%lx %s --> [HEAP]\n", start, end, perm);
continue;
}
// Impressão da faixa da STACK...
if (strstr(path, "[stack]")) {
printf("0x%lx-0x%lx %s --> [STACK]\n", start, end, perm);
continue;
}
// Demais linhas com caminho...
if (path[0] != '\0') printf("0x%lx-0x%lx %s --> %s\n", start, end, perm, path);
}
// Fecha o descritor de arquivos...
fclose(fd);
}

Binary file not shown.

View file

@ -1,229 +0,0 @@
#+title: Curso Básico da Linguagem C
#+subtitle: Aula 4: Layout de memória
#+author: Blau Araujo
#+startup: show2levels
#+options: toc:3
* Aula 4: Layout de memória
- [[][Vídeo desta aula]]
** Introdução
No fundo, esta é uma aula sobre variáveis. Mas, principalmente para quem está
iniciando na programação em C, dizer que variáveis são como gavetas onde nós
guardamos e acessamos dados aleatoriamente é quase a mesma coisa que tentar
formar cirurgiões com o Jogo da Operação.
#+CAPTION: Jogo da Operação
[[./jogo-taz.png]]
Isso funciona muito bem com crianças em alfabetização, mas não com quem está
se preparando para assumir responsabilidades programando numa linguagem que
deixa por conta da pessoa que programa todos os cuidados com as potenciais
falhas e vulnerabilidades que podem por em risco, não apenas o programa que
está sendo escrito, como também todo o sistema em que ele será executado.
Então, nós podemos definir variáveis como elementos da linguagem que serão
associados a valores manipuláveis na memória, podemos dizer que esses valores
serão dimensionados conforme seus tipos (assunto da aula passada), que eles
serão encontrados em endereços específicos na memória... Enfim, mas nada
disso fará sentido, nem dará uma noção realista das implicações do poder que
nós temos em mãos quando somos autorizados, pela linguagem C, a manipular
quase que livremente o espaço de memória.
Em grande parte, é a falta dessa noção que nos leva a situações de risco,
como (se prepare para o inglês):
- Heap Overflow
- Stack Overflow
- Buffer Overflow
- Use-After-Free (UAF)
- Double Free
- Dangling Pointer
- Memory Leak
- Uninitialized Memory Access
- Out-of-Bounds Read/Write
- Null Pointer Dereference
- Stack Corruption
- Heap Corruption
- Race Conditions
E depois, vão dizer que a linguagem C é insegura, propensa a vulnerabilidades
de memória... E por aí vai. Sim, a linguagem C não tem mecanismos que nos
impeçam de cometer erros, mas não é ela que comete os erros.
Por isso, este talvez seja o vídeo mais longo do nosso curso. Nele, nós vamos
demonstrar como o sistema operacional, o nosso GNU/Linux, lida com a execução
de programas, especificamente no que diz respeito ao espaço de memória que é
disponibilizado para eles.
** Processos e memória
- O kernel gerencia a execução de programas através de /processos/.
- Processos são estruturas de dados associadas a cada um dos programas
que estão sendo executados.
- Uma parte central dessa estrutura de dados é o /layout de memória/, que
é uma faixa de endereços mapeada pelo sistema para que os programas possam
acessar a memória através de endereços adjacentes.
#+begin_quote
Essa faixa de endereços é /virtual/ porque são endereços que não correspondem
aos endereços reais da memória física e, por isso mesmo, os programas terão
acesso a uma faixa contínua de endereços em vez de localizações espalhadas
e dispersas ao longo dos endereços reais da memória.
#+end_quote
** O espaço de endereços (layout de memória)
A faixa de endereços atribuída a um processo é dividida em vários /segmentos
de memória/ com finalidades específicas.
#+caption: Layout de memória
[[./mem-layout.png]]
*** Dados copiados do binário do programa
Nos endereços mais baixos da memória virtual, serão copiados os dados
presentes nas /seções/ dos binários dos nossos programas:
- =.text=: O conteúdo executável do programa (código).
- =.rodata=: Dados constantes.
- =.data=: Dados globais e estáticos inicializados.
- =.bss=: Dados globais e estáticos não inicializados.
*** Dados dinâmicos
A região intermediária dos endereços mapeados, chamada de /heap/, é reservada
ao uso com:
- Dados que requeiram espaços alocados dinamicamente ao longo da execução
do programa (com a função =malloc=, por exemplo).
- Mapeamento do conteúdo de arquivos e grandes volumes de dados.
- Conteúdo de bibliotecas carregadas dinamicamente, como a =glibc=, o
carregador dinâmico (=ld-linux=) e a biblioteca =vdso=, do Linux.
#+begin_quote
A localização dos dados dinâmicos é aleatória dentro da faixa do /heap/
que, conforme a necessidade, se expande na direção dos endereços mais
altos, ou seja, em direção à pilha.
#+end_quote
*** Pilha (stack)
Os endereços mais altos da memória virtual são reservados à /pilha de
execução/ do programa. Uma /pilha/, ou /stack/, é uma estrutura onde os dados
são, literalmente, empilhados uns sobre os outros. No GNU/Linux, a base
da pilha está no seu endereço mais alto, enquanto que os novos dados serão
empilhados na direção dos endereços mais baixos.
Ao ser iniciada, a pilha recebe, da sua base para o topo:
- Lista das variáveis exportadas para o processo (/ambiente/ / /envp/).
- Lista dos argumentos de linha de comando que invocaram o programa (/argv/).
- Um valor inteiro relativo à quantidade de argumentos (/argc/).
No caso de programas escritos em C, ao serem iniciados, o dado no topo da
pilha, a quantidade de argumentos, é removido e, a partir daí, são
empilhados os dados locais da função =main=, o que inclui:
- Variáveis declaradas nos parâmetros da função.
- Variáveis declaradas no corpo da função.
À medida em que o programa é executado, os dados das outras funções
chamadas também serão empilhados até serem removidos após seus respectivos
términos.
** Resumo do mapeamento de memória
O kernel expõe diversas informações sobre os processos em execução na
forma de arquivos de texto no diretório virtual =/proc=. Nele, cada processo
terá um diretório e, nesses diretórios, nós encontramos o arquivo =maps=,
que contém uma versão resumida de todas as faixas de endereços mapeados.
Para visualizar o mapeamento de um processo de número =PID=:
#+begin_example
cat /proc/PID/maps
#+end_example
** Programa =memlo.c=
Para demonstrar como os dados de um programa são mapeados na memória virtual,
nós vamos utilizar o programma =memlo.c=:
#+begin_src c
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#define APGL_HINT 0x4c475041
#define BLAU_HINT 0x55414c42
int bss_var;
int data_var = APGL_HINT;
const int ro_data = BLAU_HINT;
int func(void) {
return 42;
}
int main(int argc, char **argv, char **envp) {
static int lsni_var;
static int lsi_var = BLAU_HINT;
int lni_var;
int li_var = APGL_HINT;
char *str_ptr = "Salve!";
void *heap_ptr = malloc(16);
puts("[stack]");
printf("%p início do vetor envp (%s)\n", *envp, *envp);
printf("%p início do vetor argv (%s)\n", *argv, *argv);
printf("%p envp ponteiro para envp (%p)\n", envp, *envp);
printf("%p argv ponteiro para argv (%p)\n", argv, *argv);
printf("%p lni_var variável não inicializada\n", &lni_var);
printf("%p li_var variável inicializada\n", &li_var);
printf("%p &str_ptr ponteiro com endereço da string (%p)\n", &str_ptr, "Salve!");
printf("%p &heap_ptr ponteiro com endereço na heap (%p)\n", &heap_ptr, heap_ptr);
printf("%p argc quantidade de argumentos (%d)\n", &argc, argc);
puts("[mmap]");
printf("%p malloc() função da glibc\n", (void *)malloc);
printf("%p printf() função da glibc\n", (void *)printf);
puts("[heap]");
printf("%p heap_ptr espaço alocado dinamicamente na heap\n", heap_ptr);
puts("[.bss]");
printf("%p lsni_var variável local estática não inicializada\n", &lsni_var);
printf("%p bss_var variável global não inicializada\n", &bss_var);
puts("[.data]");
printf("%p lsi_var variável local estática inicializada\n", &lsi_var);
printf("%p data_var variável global inicializada\n", &data_var);
puts("[.rodata]");
printf("%p str_ptr endereço de uma string (%s)\n", str_ptr, str_ptr);
printf("%p ro_data constante global\n", &ro_data);
puts("[.text]");
printf("%p main() função main\n", (void *)main);
printf("%p func() função func\n", (void *)func);
free(heap_ptr);
sleep(300);
return EXIT_SUCCESS;
}
#+end_src
#+begin_quote
A análise do programa em si ficará como parte dos execícios desta aula.
#+end_quote

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 357 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 101 KiB

View file

@ -1,67 +0,0 @@
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#define APGL_HINT 0x4c475041
#define BLAU_HINT 0x55414c42
int bss_var;
int data_var = APGL_HINT;
const int ro_data = BLAU_HINT;
int func(void) {
return 42;
}
int main(int argc, char **argv, char **envp) {
static int lsni_var;
static int lsi_var = BLAU_HINT;
int lni_var;
int li_var = APGL_HINT;
char *str_ptr = "Salve!";
void *heap_ptr = malloc(16);
puts("[stack]");
printf("%p início do vetor envp (%s)\n", *envp, *envp);
printf("%p início do vetor argv (%s)\n", *argv, *argv);
printf("%p envp ponteiro para envp (%p)\n", envp, *envp);
printf("%p argv ponteiro para argv (%p)\n", argv, *argv);
printf("%p lni_var variável não inicializada\n", &lni_var);
printf("%p li_var variável inicializada\n", &li_var);
printf("%p &str_ptr ponteiro com endereço da string (%p)\n", &str_ptr, "Salve!");
printf("%p &heap_ptr ponteiro com endereço na heap (%p)\n", &heap_ptr, heap_ptr);
printf("%p argc quantidade de argumentos (%d)\n", &argc, argc);
puts("[mmap]");
printf("%p malloc() função da glibc\n", (void *)malloc);
printf("%p printf() função da glibc\n", (void *)printf);
puts("[heap]");
printf("%p heap_ptr espaço alocado dinamicamente na heap\n", heap_ptr);
puts("[.bss]");
printf("%p lsni_var variável local estática não inicializada\n", &lsni_var);
printf("%p bss_var variável global não inicializada\n", &bss_var);
puts("[.data]");
printf("%p lsi_var variável local estática inicializada\n", &lsi_var);
printf("%p data_var variável global inicializada\n", &data_var);
puts("[.rodata]");
printf("%p str_ptr endereço de uma string (%s)\n", str_ptr, str_ptr);
printf("%p ro_data constante global\n", &ro_data);
puts("[.text]");
printf("%p main() função main\n", (void *)main);
printf("%p func() função func\n", (void *)func);
free(heap_ptr);
sleep(300);
return EXIT_SUCCESS;
}

View file

@ -1,12 +0,0 @@
#include <stdio.h>
int b = 0x5a;
int main(void) {
int a = 0x58;
printf("a: %d @ %p\n", a, &a);
printf("b: %d @ %p\n", b, &b);
return 0;
}

View file

@ -1,10 +0,0 @@
#include <stdio.h>
int main(void) {
int a = 17, b = 25;
printf("a: %d @ %p\n", a, &a);
printf("b: %d @ %p\n", b, &b);
return 0;
}