
Me perguntaram sobre como funciona injeção de DLL (bibliotecas dinâmicas), e bom... Esse é o modelo mais simples/bacana pra vc que tem interesse em fazer engenharia reversa em jogos antigos, seja pra estudo, ou pra projetos pessoais.
Hoje em dia é possível usar injetores prontos ou até mesmo programas como o CheatEngine pra vc fazer injeção de uma DLL em um jogo, mas com o tempo isso se torna repetitivo e chato, e uma das melhores opções é vc criar seu próprio injetor/launcher.
Mas como isso funciona? Bom, todo processo possui o seguinte POV: armazena memória na RAM, executa instruções na CPU. (segue a reflexão com o fluxo abaixo)
┌───────────────┐
│ CPU │
│ (instruction) │
└───────┬───────┘
│ virtual address (VA)
▼
┌───────────────┐
│ MMU │
│ │
│ TLB lookup │
│ (VA → PA ?) │
└───────┬───────┘
hit │ miss
│
┌─────────▼─────────┐
│ Physical Address │
│ (PA) │
└─────────┬─────────┘
│
▼
┌───────────────┐
│ RAM │
│ (data load / │
│ store) │
└───────────────┘
E como a gente abusa disso? O Windows possui uma função nativa chamada LoadLibrary (kernel32.dll), essa função é muito utilizada para fazer carregamento de bibliotecas de forma dinâmica, e é exatamente oq iremos fazer aqui.
Pensa no seguinte fluxo original:
Oq a gente quer fazer é o seguinte:
Esse ultimo passo é o mais importante, a thread q vc vai criar irá chamar a função LoadLibrary passando como argumento o nome da sua DLL.
Oq isso significa? vc está forçando o jogo a carregar dinamicamente a biblioteca q vc criou. Pode correr pro abraço
Código do injetor seguindo os passos acima:
1#include <Windows.h> 2#include <stdio.h> 3 4int main() { 5 char cmdLine[] = "SPEED2.EXE /s"; 6 char dllPath[] = "nfsu2.dll"; 7 8 STARTUPINFOA si{}; 9 PROCESS_INFORMATION pi{}; 10 si.cb = sizeof(si); 11 12 if (!CreateProcessA( 13 nullptr, 14 cmdLine, 15 nullptr, nullptr, 16 FALSE, 17 CREATE_SUSPENDED, 18 nullptr, nullptr, 19 &si, &pi)) 20 { 21 printf("CreateProcess failed: %lu\n", GetLastError()); 22 return 0; 23 } 24 25 SIZE_T len = strlen(dllPath) + 1; 26 27 void* remoteBuf = VirtualAllocEx( 28 pi.hProcess, 29 nullptr, 30 len, 31 MEM_COMMIT | MEM_RESERVE, 32 PAGE_READWRITE); 33 34 WriteProcessMemory( 35 pi.hProcess, 36 remoteBuf, 37 dllPath, 38 len, 39 nullptr); 40 41 HMODULE k32 = GetModuleHandleA("kernel32.dll"); 42 auto pLoadLibraryA = 43 (LPTHREAD_START_ROUTINE)GetProcAddress(k32, "LoadLibraryA"); 44 45 HANDLE hThread = CreateRemoteThread( 46 pi.hProcess, 47 nullptr, 48 0, 49 pLoadLibraryA, 50 remoteBuf, 51 0, 52 nullptr); 53 54 WaitForSingleObject(hThread, INFINITE); 55 56 CloseHandle(hThread); 57 VirtualFreeEx(pi.hProcess, remoteBuf, 0, MEM_RELEASE); 58 59 ResumeThread(pi.hThread); 60 61 CloseHandle(pi.hThread); 62 CloseHandle(pi.hProcess); 63 64 return 1; 65}
Código exemplo de uma biblioteca qualquer: (Aqui no caso ela só inicia e repassa o comando pra uma função q mostra uma caixa de mensagem na tela.)
1#include <Windows.h> 2 3DWORD WINAPI MainThread(LPVOID) { 4 MessageBoxW(nullptr, L"Injected DLL loaded!", L"Mod", MB_OK | MB_ICONINFORMATION); 5 return 0; 6} 7 8BOOL WINAPI DllMain(HMODULE hModule, DWORD reason, LPVOID) { 9 if (reason == DLL_PROCESS_ATTACH) { 10 DisableThreadLibraryCalls(hModule); 11 CreateThread(nullptr, 0, MainThread, nullptr, 0, nullptr); 12 } 13 return TRUE; 14}
Este é um dos meios mais básicos e conhecidos para se fazer injeção de bibliotecas, mas o céu é o limite.
Você pode ir atrás de evoluir sua técnica para escrever um injetor manual, que ao invés de usar LoadLibrary (kernel32.dll), você simplesmente aloca memória no processo alvo com o tamanho da sua DLL, copia os headers PE, e escreve as funções lá, é um trabalho braçal mas isso te permite sofisticar muito mais o seu projeto.