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
Nenhum comentário:
Postar um comentário