A aplicação que iremos modificar têm o seguinte código fonte:
#include <stdio.h>
#include <stdlib.h>
int T;
void get_number()
{
printf("Enter Number: ");
scanf("%d", &T);
}
int main()
{
system("title my program");
do
{
get_number();
}
while (T);
return 0;
}
Nosso programa pede um número de entrada, caso esse número seja 0 ele sai, do contrário repete, e assim sucessivamente. Certo?Continuando...
Iremos injetar um código que imprima na tela "You number entered are: <numero digitado>" toda vez que a get_number() for chamado. Para isso precisaremos saber os endereços da printf e a variável T na memória (O depurador usado aqui é o ollydbg).
Descobrindo os endereços
Para descobrirmos o endereço da função printf, vá em Search for > All intermodular calls,Na janela "search" procure por printf e siga sua referência.
Iremos nos deparar com nossa função get_number(), apertando a tecla espaço iremos ver o endereço que se refere a printf, que no nosso caso é 0x00401A8C.
Agora falta só o endereço da variável T. Se olharmos direito veremos o endereço após a chamada da printf, onde começa as instruções da scanf, que é a referência da váriavel e o formato que iremos extrair. Então, BINGO! Temos também o endereço da variável T que no nosso caso é 0x00404008.
Procurando um local para injetar
Precisamos saber também onde iremos injetar nosso código para que fique similar a:do
{
get_number();
printf("You number entered are: %d", T);
}
while (T);Analizando as instruções
Encontramos um lugar perfeito para injetar nosso código, olhe bem no endereço 0x00401B4D a instrução MOV EAX, DWORD PTR DS:[program.404008] ela ocupa 5 bytes, a quantidade que precisamos.
Nota: Iremos sobrepor a instrução e adiciona-la em nosso código para não quebrar o fluxo original do programa.
Fazendo os códigos
Agora que temos os endereços e o local onde iremos injetar, só codificar!Primeiro nosso código a ser injetado.
typedef void t_call(...);
#define _printf(...) ((t_call*)0x00401A8C)(__VA_ARGS__) // nossa printf
int get_number_hook()
{
asm(".intel_syntax noprefix\n"); // muda a sintaxe pro intel
// adicionar -masm=intel no build
_printf((char*)0xFFFFFFFF, *((int*)0x00404008)) // nossa chamada, com a variavel T
asm("mov eax, dword ptr ds:[0x404008]"); // instrução sobreposta pelo nosso jmp
asm("mov edx, 0x00401B52");
asm("jmp edx");
}
void get_number_hook_end(){}; // apenas para saber onde o get_number_hook termina
Nossas funções de injetar e alocar os dados na memória.
void hook(HANDLE hProc, DWORD address, DWORD funaddr) // injeta nosso código
{
char JMP[5] = {0};
JMP[0] = 0xE9;
*(PDWORD)&JMP[1] = funaddr - address - 5;
DWORD OldProtect;
VirtualProtectEx(hProc, address, 5, PAGE_EXECUTE_WRITECOPY, &OldProtect);
WriteProcessMemory(hProc, address, &JMP, 5, NULL);
VirtualProtectEx(hProc, address, 5, OldProtect, NULL);
}
DWORD alloc(HANDLE hProcess, PVOID pointer, DWORD size) // aloca os dados
{
LPVOID addr = VirtualAllocEx(hProcess, 0, size, MEM_COMMIT | MEM_RESERVE ,PAGE_EXECUTE_READWRITE);
WriteProcessMemory(hProcess, addr, pointer, size, NULL);
return addr;
}Dando uma atenção em *(PDWORD)&JMP[1] = funaddr - address - 5. Como o JMP é relativo a sua posição atual, é subtraido da posição de destimo a posição atual mais os 5 bytes da instrução. O resto do código é alto explicativo.
Faltando apenas nossa função principal, o main:
int main()
{
DWORD pID;
HWND hWnd = FindWindow(NULL, "my program");
if (hWnd && GetWindowThreadProcessId(hWnd, &pID))
{
HANDLE hProc = OpenProcess(PROCESS_ALL_ACCESS, 0, pID);
if (hProc)
{
DWORD straddr, funaddr;
printf("HWND: %d\n" , hWnd );
printf("PID: %d\n" , pID );
printf("HANDLE: %d\n", hProc);
printf("Injetando get_number_hook()");
char *my_str = "You number entered are: %d\n";
// aloca nosso texto e retorna o endereço
straddr = alloc(hProc, my_str, strlen(my_str));
// aloca nosso código e retorna o endereço
funaddr = alloc(hProc, &get_number_hook, (DWORD)((DWORD)&get_number_hook_end - (DWORD)&get_number_hook));
// lembra do _printf((char*)0xFFFFFFFF...) ?
// muda ao endereço do 0xFFFFFFFF para nosso texto
WriteProcessMemory(hProc, funaddr + 0x0F, &straddr, 4, NULL);
// injeta nosso código em 0x00401B4D
hook(hProc, 0x00401B4D, funaddr);
CloseHandle(hProc);
}
}
return 0;
}Dando a atenção devida em WriteProcessMemory(hProc, funaddr + 0x0F, &straddr, 4, NULL), onde mudaremos o endereço para nosso texto alocado anteriormente. Para entender o porque o do funaddr + 0x0F veja a imagem do nosso código compilado e alocado:
Olhe a quantidade de bytes que temos que pular até chegar no endereço que queremos mudar. 15 bytes sendo 0x0F o mesmo que 15 em hexadecimal. Sendo assim temos a posição da função alocada mais 15 bytes.
Downloads
Código fonte e executáveis (5kb)Conclusão
Muitas coisas podem ser feita com essa técnica, o que mostrei aqui foi apenas o básico. Boa sorte em suas aplicações. ;)Att, Gilson Fabiano




