# Execução de Programas

## Privilégios de Execução

Para impedir que os programas do usuário acessem ou modifiquem dados críticos do sistema operacional, o Windows suporta dois níveis de execução de código: modo usuário e modo kernel, mais conhecidos por seus nomes em inglês: *user mode* e *kernel mode*.

Os programas comuns rodam em *user mode*, enquanto os serviços internos do SO e *drivers* rodam em *kernel mode*.

Apesar de o Windows e outros sistemas operacionais modernos trabalharem com somente estes dois níveis de privilégios de execução, os processadores Intel e compatíveis suportam quatro níveis, também chamados de anéis (*rings*), numerados de 0 a 3, onde o anel zero é o mais privilegiado. Para *kernel mode* é utilizado o *ring* 0 e para *user mode*, o *ring* 3.

Programas rodando em *user mode* tampouco possuem acesso ao hardware do computador. Essencialmente, todos estes fatores combinados fazem com que os programas rodando neste privilégio de execução não gerem erros fatais como a famosa "tela azul", também chamada de BSOD - **B**lue **S**creen **O**f **D**eath.

Passa que toda a parte legal acontece em *kernel mode*, sendo assim, um processo (na verdade uma *thread*) rodando em *user mode* pode executar tarefas em *kernel mode* através da API do Windows, que funciona como uma interface para tal. Essa comunicação é ilustrada no diagrama a seguir:

![Diagrama simplificado da execução de programas no Windows](https://2831222458-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-L8zFFHkPxQb-_1XSGR9%2Fuploads%2Fgit-blob-b40cb9a15c181e3d70f0bb55364c4114c34e5fb6%2F06_execucao_programas.png?alt=media)

## Dependências

Quando alguém cria um programa, em muitos casos se utiliza de funções de bibliotecas (ou *libraries* em inglês), também chamadas de DLLs (*Dynamic-Link Library*). Sendo assim, analise o seguinte simples programa em C:

```c
#include <stdio.h>

int main(void) {
    printf("Olá, mundo!\n");
}
```

Este programa utiliza a função *printf()*, que não precisou ser implementada por quem o programou. Quem escreveu o código só precisou chamar a função, já que esta está definida no arquivo *stdio.h*.

Quando compilado, este programa terá uma dependência da biblioteca de C (arquivo **msvcrt.dll** no Windows) pois o código da *printf()* está nela.

Esta arquitetura garante que diferentes programadores e programadoras usem tal função e que ela terá sempre o mesmo comportamento se usada da mesma forma. Mas você já parou para pensar como a função *printf()* de fato escreve na tela? Como ela lidaria com as diferentes placas de vídeo, por exemplo?

O fato é que a *printf()* não escreve diretamente na tela. Na verdade, a biblioteca de C, que contém a implementação da *printf()*, pede ao *kernel* através de uma função de sua API para que ele escreva na tela. O *kernel*, por sua vez, utiliza o *driver* da placa de vídeo que conhece a placa e a escrita acontece. Sendo assim, temos, neste caso um EXE que chama uma função de uma DLL que chama o *kernel*. Estudaremos mais a frente como essas chamadas acontecem.

## *Loader*

Quando um programa é executado (por exemplo, com um duplo-clique no Windows), ele é copiado para a memória e um **processo** é criado para ele. Dizemos então que um processo está rodando, mas esta afirmação não é muito precisa: na verdade, todo processo no Windows possui pelo menos uma *thread* e ela sim é que roda. O processo funciona como um "contêiner" que contém várias informações sobre o programa rodando e suas *threads*.

Quem cria esse processo em memória é um componente do sistema operacional Windows chamado de *image loader*, presente na biblioteca **ntdll.dll**.

> O código do *loader* roda **antes** do código do programa a ser carregado. É um código comum a todos os processos executados no Windows.

Dentre as funções do *loader* estão:

* Ler os cabeçalhos do arquivo PE a ser executado e alocar a memória necessária para a imagem como um todo, suas seções, etc.
  * As seções são mapeadas para a memória, respeitando-se suas permissões.
* Ler a tabela de importações do arquivo PE a fim de carregar as DLLs requeridas por este e que ainda não foram carregadas em memória. Esse processo também é chamado de **resolução de dependências**.
* Preencher a IAT com os endereços das funções importadas.
* Carregar módulos adicionais em tempo de execução, se assim for pedido pelo executável principal (também chamado de módulo principal).
* Manter uma lista de módulos carregados por um processo.
* Transferir a execução para o *entrypoint* (EP) do programa, que é quando ele de fato começa a rodar.
