O formato de arquivo PE

A Microsoft criou o formato de arquivo PE (Portable Executable) para arquivos executáveis que rodam dentro do sistema Windows. O formato PE tem tudo que o Windows PE loader precisa para executar o código do arquivo.

Headers e Seções

O formato PE contém vários headers: metadados ou outras informações no inicio do arquivo, que mostram para o sistema e outros softwares o que fazer com o seu conteúdo. O DOS header contém informações necessárias para o MS-DOS e versões antigas do Windows e só existem por razões legadas. O cabeçalho PE contém informações utilizadas pelo PE loader, como a arquitetura da CPU em que o executável foi compilado para ser executado e metadados como o timestamp de compilação do arquivo. O cabeçalho PE também inclui o cabeçalho opcional, que indica informações importantes, como o endereço de memória base do PE (o endereço de memória no qual o PE será mapeado na memória), o tamanho do código dentro do executável e o SO de destino no qual o executável será executado. O cabeçalho “opcional” já não é mais tão opcional nos sistemas Windows modernos.

O arquivo PE também inclui o cabeçalho de seção, que contém metadados relacionados com cada uma das seções do arquivo (onde o conteúdo real do arquivo é armazenado), tais como o tamanho da seção, o endereço e outras caraterísticas. Finalmente, a maioria dos arquivos PE contém pelo menos algumas das seguintes seções:

.text
O principal código executável do arquivo

.rdata

Dados somente de leitura, como variáveis estáticas e constantes

.bss

Dados não inicializados, como variáveis que ainda não possuem um valor

.data

Variáveis não incorporadas nas seções .rdata e .bss, como variáveis globais

.rsrc

Recursos que serão carregados pelo executável em tempo de execução, como imagens, fontes e outros arquivos de suporte

.idata

A tabela de endereços de importação

.edata

A tabela de endereços de exportação

Imports e Exports

As seções .idata e .edata são dois dos componentes mais importantes de um arquivo PE. A seção .idata contém informações sobre as funções que o arquivo PE importará em tempo de execução. Quando o arquivo PE for executado, o programa carregará as bibliotecas e funções referenciadas aqui na memória e criará sua tabela de endereços de importação (IAT), que mapeia as funções importadas da API do Windows para seus endereços na memória.

A seção .edata contém informações sobre as funções que o arquivo PE exporta para outros programas, que podem ser importados e carregados na memória para uso próprio. É comum que os arquivos de DLL, por exemplo, contenham uma lista de funções exportadas. Assim como as importações, as exportações têm sua própria tabela, chamada de tabela de endereços de exportação.

Na prática, as seções .edata e .idata frequentemente estão dentro da seção .rdata.

O processo de carregamento do PE

Quando você inicia um arquivo executável, como por exemplo o Firefox, o que acontece é o seguinte:

1 - O Windows cria uma nova estrutura de dados EPROCESS para o programa Firefox e atribui um novo ID de processo.

2 - O Windows inicializa a memória virtual necessária para o processo, cria a estrutura PEB e carrega duas bibliotecas que quase todos os processos do Windows exigem: ntdll.dll e kernel32.dll. Em seguida, ele se prepara para carregar o arquivo PE do Firefox, inicializando o PE loader.

3 - O PE loader analisa os cabeçalhos DOS, PE e opcionais do arquivo PE para reunir todas as informações necessárias para executar o arquivo com êxito.

4 - O PE loader analisa o cabeçalho da seção para se preparar para mapear essas seções na memória. O PE loader mapeia cada seção na memória virtual dentro do novo processo.

5 - O PE loader carrega todas as bibliotecas referenciadas na seção de importações (geralmente .idata ou .rdata) e resolve todos os endereços das funções necessárias. Todos os endereços são então armazenados no IAT dentro do processo.

6 - Uma nova thread é criada dentro do processo atual, e o loader executa os primeiros bytes de código no executável (geralmente na seção .text).

Cada seção do arquivo PE é mapeada individualmente na memória, mas aparece expandida na memória virtual, pois geralmente há regiões de memória entre cada seção.

Last updated