
DLLs e seus riscos de Segurança
- Gabriel Flauzino
- Segurança da informação
- 28 de março de 2025
DLL (Dynamic Link Libraries) é uma biblioteca compartilhada do sistema operacional windows que tem como função facilitar e otimizar a execução de diversas funções, nas quais podem ser utilizadas por diversos softwares ao mesmo tempo quando utilizada de forma dinâmica.
As DLLs trazem funções de API para o windows, facilitando o desenvolvimento de software ao permitir que aplicações reutilizem código pré-existente e interajam com o sistema operacional, tendo em vista que a aplicação pode utilizar de bibliotecas nativas ou de terceiros para realizar tarefas especificas dentro do sistema, como a criação de serviços do Windows, gerenciamento de processos e threads, manipulação de arquivos e comunicação de rede.
Tendo em vista que este não é um recurso by-yourself, a execução desses binários dependem da execução de um outro software, dessa forma, há alguns tipos de chamada:
- Implicit Linking: O binário carrega as funções em memória, dessa forma, cria uma tabela de importação (IAT - Import Address Table) no qual já é listada antes da execução, logo, o sistema controla quando a DLL é importada, não necessitando de uma função dentro do código.
- StaticLink: O código da biblioteca é incorporado diretamente no binário durante a compilação, eliminando a necessidade de um arquivo DLL separado. Dessa forma, o programa não depende de bibliotecas externas para rodar. Como consequência, o binário final pode ser maior e qualquer falha na biblioteca exigirá a recompilação e redistribuição do software.
- RuntimeLink: é um processo onde o ator importa a DLL durante a execução do processo, sem estar declarada, nesse caso, em análises estáticas, nem todas as funções importadas irão aparecer, tendo em vista que o ator irá fazer o import dentro de alguma função do binário. Métodos que definem o Runtime:
- LoadLibraty - Processo de execução dinâmico, no qual o software realiza a chamada da DLL durante a execução do processo. Essa função faz o load da dll durante a execução e retorna um handle.
- GetProcAddress - Processo de execução dinâmico, no qual o software realiza a chamada da DLL durante a execução do processo. Essa função pega o endereço da função da DLL após ela ter sido carregada pelo LoadLibrary e retorna um ponteiro para uma função ou variável importada da DLL para o processo.
A estrutura de uma DLL é dividida em dois módulos:
- Funções externas: funções que serão importadas por outros softwares.
- Funções internas: funções que garantem o funcionamento da DLL e que não podem ser importadas por outros processos.
Esses módulos e funções são definidos por 3 arquivos que constituem essa formação: o header, o código da DLL e o arquivo de definição de módulos que serão exportados.
Header: Arquivo com a extensão “.h” no qual tem a declaração das funções que podem ser exportadas:
// Definição de macro para exportar ou importar funções, dependendo de quem estiver compilando
#ifdef MYDLL_EXPORTS
#define MYDLL_API __declspec(dllexport)
#else
#define MYDLL_API __declspec(dllimport)
#endif
// Funções exportadas (externas) que poderão ser usadas por outros programas
extern "C" {
MYDLL_API int Add(int a, int b); // Soma dois números
MYDLL_API int Subtract(int a, int b); // Subtrai dois números
}
Código da DLL: contém o código de módulos internos, externos e a função DLLmain, no qual é responsável pelos processos de call da DLL, o arquivo possui a extensão “.cpp”.
// MyDll.cpp
#include "MyDll.h"
#include <windows.h>
// Módulo interno: Funções não exportadas (privadas)
namespace InternalModule {
// Função interna privada para multiplicar
int Multiply(int a, int b) {
return a * b;
}
// Função interna privada para dividir
int Divide(int a, int b) {
if (b != 0) {
return a / b;
} else {
return 0; // Evitar divisão por zero
}
}
}
// Módulo externo: Funções exportadas (públicas)
extern "C" {
// Função para somar dois números, exportada
MYDLL_API int Add(int a, int b) {
return a + b;
}
// Função para subtrair dois números, exportada
MYDLL_API int Subtract(int a, int b) {
return a - b;
}
}
// Função especial DllMain chamada pelo sistema quando a DLL é carregada ou descarregada
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) {
switch (ul_reason_for_call) {
case DLL_PROCESS_ATTACH:
// Código quando o processo é anexado (DLL carregada)
break;
case DLL_THREAD_ATTACH:
// Código quando uma nova thread é criada
break;
case DLL_THREAD_DETACH:
// Código quando uma thread é finalizada
break;
case DLL_PROCESS_DETACH:
// Código quando o processo é desligado (DLL descarregada)
break;
}
return TRUE; // Indica que a DLL foi carregada com sucesso
}
Arquivo de definição: código de extensão .def no qual realiza a definição de quais funções vão estar acessíveis a outros processos, realizando o controle, sendo assim, mesmo que haja outras funções declaradas nos arquivos .h e .cpp, caso não esteja nas definições de export, não poderão ser utilizadas por outros binários.
; MyDll.def
LIBRARY "MyDll" ; Nome da DLL
EXPORTS
Add ; Exporta a função Add
Subtract ; Exporta a função Subtract
Conceito e funcionamento da IAT (Import Address Table):
Ao realizar a compilação de programas utilizando Implicit Linking, o linker realiza um “de-para” das funções utilizadas no programa e as DLLs das quais a função está agregada. Após essa compilação, o software forma a IAT, no qual possui todas as DLLs e as funções que serão utilizadas pelo código.
Ao executar o software, o Windows carrega a DLL na memória (caso ainda não esteja carregada por outro processo) e mapeia suas funções em endereços de memória. Após esse mapeamento, o Windows Loader preenche a IAT com os endereços reais das funções importadas. Dessa forma, quando o programa chama uma função da DLL, ele acessa o ponteiro armazenado na IAT para executar a função necessária.
Resumindo, a IAT é uma tabela com duas partes principais, o endereço de memória (Definido pelo loader do sistema operacional (processo de execução da DLL em memória) e o nome da função necessária pelo processo (no qual é definido durante a compilação).

- Problemas que podem ser identificados e riscos de segurança:
- DLL Hijacking: O ator malicioso realiza um processo onde altera a ordem de execução de dll de um processo vulnerável ao ataque, tornando o processo malicioso Stealth
- Árvore de procura de uma DLL caso o path completo dela não seja declarado no código da aplicação (passando somente o nome, não o caminho completo):
- O diretório do qual a aplicação foi lançada
- O diretório de sistema (C:\Windows\System32)
- A versão 16-bit do diretório de sistema
- O diretório do Windows (C:\Windows)
- O diretório atual
- Dll Search Mode off:
- O diretório do qual a aplicação foi lançada
- O diretório atual
- O diretório de sistema (C:\Windows\System32)
- A versão 16-bit do diretório de sistema
- O diretório do Windows (C:\Windows)
- Árvore de procura de uma DLL caso o path completo dela não seja declarado no código da aplicação (passando somente o nome, não o caminho completo):
- DLL injection: utiliza funções para fazer com que um processo carregue uma DLL maliciosa.
- DLL Hijacking: O ator malicioso realiza um processo onde altera a ordem de execução de dll de um processo vulnerável ao ataque, tornando o processo malicioso Stealth
Por fim, agradecemos a leitura e esperamos que este post tenha te ajudado de alguma maneira! Caso tenha alguma dúvida, entre em contato conosco pelo Telegram , Facebook ou Instagram ! Veja mais posts no IronLinux !