Learn & practice AWS Hacking:<imgsrc="/.gitbook/assets/arte.png"alt=""data-size="line">[**HackTricks Training AWS Red Team Expert (ARTE)**](https://training.hacktricks.xyz/courses/arte)<imgsrc="/.gitbook/assets/arte.png"alt=""data-size="line">\
Learn & practice GCP Hacking: <imgsrc="/.gitbook/assets/grte.png"alt=""data-size="line">[**HackTricks Training GCP Red Team Expert (GRTE)**<imgsrc="/.gitbook/assets/grte.png"alt=""data-size="line">](https://training.hacktricks.xyz/courses/grte)
<details>
<summary>Support HackTricks</summary>
* Check the [**subscription plans**](https://github.com/sponsors/carlospolop)!
* **Join the** 💬 [**Discord group**](https://discord.gg/hRep4RUj7f) or the [**telegram group**](https://t.me/peass) or **follow** us on **Twitter** 🐦 [**@hacktricks\_live**](https://twitter.com/hacktricks\_live)**.**
* **Share hacking tricks by submitting PRs to the** [**HackTricks**](https://github.com/carlospolop/hacktricks) and [**HackTricks Cloud**](https://github.com/carlospolop/hacktricks-cloud) github repos.
</details>
{% endhint %}
## Program Headers
Descreve ao carregador como carregar o ELF na memória:
```bash
readelf -lW lnstat
Elf file type is DYN (Position-Independent Executable file)
Entry point 0x1c00
There are 9 program headers, starting at offset 64
Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
PHDR 0x000040 0x0000000000000040 0x0000000000000040 0x0001f8 0x0001f8 R 0x8
INTERP 0x000238 0x0000000000000238 0x0000000000000238 0x00001b 0x00001b R 0x1
[Requesting program interpreter: /lib/ld-linux-aarch64.so.1]
LOAD 0x000000 0x0000000000000000 0x0000000000000000 0x003f7c 0x003f7c R E 0x10000
O programa anterior tem **9 cabeçalhos de programa**, então, o **mapeamento de segmentos** indica em qual cabeçalho de programa (de 00 a 08) **cada seção está localizada**.
### PHDR - Cabeçalho do Programa
Contém as tabelas de cabeçalho do programa e os metadados em si.
### INTERP
Indica o caminho do carregador a ser usado para carregar o binário na memória.
### LOAD
Esses cabeçalhos são usados para indicar **como carregar um binário na memória.**\
Por exemplo, o segundo tem um tamanho de 0x1190, deve estar localizado em 0x1fc48 com permissões de leitura e escrita e será preenchido com 0x528 a partir do deslocamento 0xfc48 (não preenche todo o espaço reservado). Esta memória conterá as seções `.init_array .fini_array .dynamic .got .data .bss`.
Indica a configuração RELRO (Relocation Read-Only) do binário. Esta proteção marcará como somente leitura certas seções da memória (como a `GOT` ou as tabelas `init` e `fini`) após o programa ter sido carregado e antes de começar a ser executado.
No exemplo anterior, está copiando 0x3b8 bytes para 0x1fc48 como somente leitura, afetando as seções `.init_array .fini_array .dynamic .got .data .bss`.
Observe que RELRO pode ser parcial ou total, a versão parcial não protege a seção **`.plt.got`**, que é usada para **lazy binding** e precisa desse espaço de memória para ter **permissões de escrita** para gravar o endereço das bibliotecas na primeira vez que sua localização é pesquisada.
It also indicates the location, offset, permissions but also the **tipo de dado** it section has.
### Meta Sections
* **String table**: Contém todas as strings necessárias pelo arquivo ELF (mas não as realmente usadas pelo programa). Por exemplo, contém nomes de seções como `.text` ou `.data`. E se `.text` está no offset 45 na tabela de strings, usará o número **45** no campo **name**.
* Para encontrar onde está a tabela de strings, o ELF contém um ponteiro para a tabela de strings.
* **Symbol table**: Contém informações sobre os símbolos, como o nome (offset na tabela de strings), endereço, tamanho e mais metadados sobre o símbolo.
* **`.bss`**: Variáveis globais deixadas não inicializadas (ou inicializadas para zero). Variáveis aqui são automaticamente inicializadas para zero, evitando assim que zeros inúteis sejam adicionados ao binário.
* **`.rodata`**: Variáveis globais constantes (seção somente leitura).
* **`.tdata`** e **`.tbss`**: Como .data e .bss quando variáveis locais de thread são usadas (`__thread_local` em C++ ou `__thread` em C).
* **`.dynamic`**: Veja abaixo.
## Symbols
Symbols é uma localização nomeada no programa que pode ser uma função, um objeto de dados global, variáveis locais de thread...
```
readelf -s lnstat
Symbol table '.dynsym' contains 49 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
1: 0000000000001088 0 SECTION LOCAL DEFAULT 12 .init
2: 0000000000020000 0 SECTION LOCAL DEFAULT 23 .data
3: 0000000000000000 0 FUNC GLOBAL DEFAULT UND strtok@GLIBC_2.17 (2)
4: 0000000000000000 0 FUNC GLOBAL DEFAULT UND s[...]@GLIBC_2.17 (2)
5: 0000000000000000 0 FUNC GLOBAL DEFAULT UND strlen@GLIBC_2.17 (2)
6: 0000000000000000 0 FUNC GLOBAL DEFAULT UND fputs@GLIBC_2.17 (2)
7: 0000000000000000 0 FUNC GLOBAL DEFAULT UND exit@GLIBC_2.17 (2)
8: 0000000000000000 0 FUNC GLOBAL DEFAULT UND _[...]@GLIBC_2.34 (3)
9: 0000000000000000 0 FUNC GLOBAL DEFAULT UND perror@GLIBC_2.17 (2)
10: 0000000000000000 0 NOTYPE WEAK DEFAULT UND _ITM_deregisterT[...]
11: 0000000000000000 0 FUNC WEAK DEFAULT UND _[...]@GLIBC_2.17 (2)
12: 0000000000000000 0 FUNC GLOBAL DEFAULT UND putc@GLIBC_2.17 (2)
[...]
```
Cada entrada de símbolo contém:
* **Nome**
* **Atributos de vinculação** (fraco, local ou global): Um símbolo local só pode ser acessado pelo próprio programa, enquanto os símbolos globais são compartilhados fora do programa. Um objeto fraco é, por exemplo, uma função que pode ser substituída por outra diferente.
* **Tipo**: NOTYPE (nenhum tipo especificado), OBJECT (variável de dados global), FUNC (função), SECTION (seção), FILE (arquivo de código-fonte para depuradores), TLS (variável local de thread), GNU\_IFUNC (função indireta para realocação)
* **Índice da Seção** onde está localizado
* **Valor** (endereço na memória)
* **Tamanho**
## Seção Dinâmica
```
readelf -d lnstat
Dynamic section at offset 0xfc58 contains 28 entries:
O diretório NEEDED indica que o programa **precisa carregar a biblioteca mencionada** para continuar. O diretório NEEDED é completado uma vez que a **biblioteca compartilhada está totalmente operacional e pronta** para uso.
## Relocações
O carregador também deve realocar dependências após tê-las carregado. Essas realocações são indicadas na tabela de realocação nos formatos REL ou RELA e o número de realocações é dado nas seções dinâmicas RELSZ ou RELASZ.
```
readelf -r lnstat
Relocation section '.rela.dyn' at offset 0xaa0 contains 23 entries:
Se o **programa for carregado em um lugar diferente** do endereço preferido (geralmente 0x400000) porque o endereço já está em uso ou por causa de **ASLR** ou qualquer outro motivo, uma relocação estática **corrige ponteiros** que tinham valores esperando que o binário fosse carregado no endereço preferido.
A relocação também pode referenciar um símbolo externo (como uma função de uma dependência). Como a função malloc da libC. Então, o carregador ao carregar a libC em um endereço verificando onde a função malloc está carregada, escreverá esse endereço na tabela GOT (Global Offset Table) (indicada na tabela de relocação) onde o endereço de malloc deve ser especificado.
A seção PLT permite realizar vinculação preguiçosa, o que significa que a resolução da localização de uma função será realizada na primeira vez que for acessada.
Assim, quando um programa chama malloc, na verdade chama a localização correspondente de `malloc` no PLT (`malloc@plt`). Na primeira vez que é chamado, resolve o endereço de `malloc` e o armazena, então na próxima vez que `malloc` for chamado, esse endereço é usado em vez do código PLT.
Depois que o programa foi carregado, é hora de ele ser executado. No entanto, o primeiro código que é executado **não é sempre a função `main`**. Isso ocorre porque, por exemplo, em C++ se uma **variável global é um objeto de uma classe**, esse objeto deve ser **inicializado****antes** que a main seja executada, como em:
```cpp
#include <stdio.h>
// g++ autoinit.cpp -o autoinit
class AutoInit {
public:
AutoInit() {
printf("Hello AutoInit!\n");
}
~AutoInit() {
printf("Goodbye AutoInit!\n");
}
};
AutoInit autoInit;
int main() {
printf("Main\n");
return 0;
}
```
Note que essas variáveis globais estão localizadas em `.data` ou `.bss`, mas nas listas `__CTOR_LIST__` e `__DTOR_LIST__` os objetos a serem inicializados e destruídos são armazenados para manter o controle deles.
A partir do código C, é possível obter o mesmo resultado usando as extensões GNU:
```c
__attributte__((constructor)) //Add a constructor to execute before
__attributte__((destructor)) //Add to the destructor list
```
De uma perspectiva de compilador, para executar essas ações antes e depois da função `main`, é possível criar uma função `init` e uma função `fini` que seriam referenciadas na seção dinâmica como **`INIT`** e **`FIN`**. e são colocadas nas seções `init` e `fini` do ELF.
A outra opção, como mencionado, é referenciar as listas **`__CTOR_LIST__`** e **`__DTOR_LIST__`** nas entradas **`INIT_ARRAY`** e **`FINI_ARRAY`** na seção dinâmica e o comprimento dessas é indicado por **`INIT_ARRAYSZ`** e **`FINI_ARRAYSZ`**. Cada entrada é um ponteiro de função que será chamado sem argumentos.
Além disso, também é possível ter um **`PREINIT_ARRAY`** com **ponteiros** que serão executados **antes** dos ponteiros **`INIT_ARRAY`**.
### Ordem de Inicialização
1. O programa é carregado na memória, variáveis globais estáticas são inicializadas em **`.data`** e as não inicializadas são zeradas em **`.bss`**.
2. Todas as **dependências** para o programa ou bibliotecas são **inicializadas** e o **linking dinâmico** é executado.
3. Funções **`PREINIT_ARRAY`** são executadas.
4. Funções **`INIT_ARRAY`** são executadas.
5. Se houver uma entrada **`INIT`**, ela é chamada.
6. Se for uma biblioteca, dlopen termina aqui; se for um programa, é hora de chamar o **ponto de entrada real** (função `main`).
## Armazenamento Local de Thread (TLS)
Eles são definidos usando a palavra-chave **`__thread_local`** em C++ ou a extensão GNU **`__thread`**.
Cada thread manterá uma localização única para essa variável, de modo que apenas a thread pode acessar sua variável.
Quando isso é usado, as seções **`.tdata`** e **`.tbss`** são usadas no ELF. Que são como `.data` (inicializado) e `.bss` (não inicializado), mas para TLS.
Cada variável terá uma entrada no cabeçalho TLS especificando o tamanho e o deslocamento TLS, que é o deslocamento que será usado na área de dados local da thread.
O `__TLS_MODULE_BASE` é um símbolo usado para se referir ao endereço base do armazenamento local de thread e aponta para a área na memória que contém todos os dados locais de thread de um módulo.