quarta-feira, 25 de abril de 2012

Injetando funções em aplicações

Veremos aqui uma técnica muito comum entre programadores de trainers/bots/macros que é a injeção de códigos em aplicações já compiladas sem alterar o estado fisíco do arquivo da aplicação. Caso queira pesquisar mais sobre o assunto procure por "inject dll", "hook function" e ainda "hooking function" tendo em base o "hook".

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

segunda-feira, 9 de abril de 2012

Tibia MC Patcher 9.51+

Tibia MC Patcher 9.51+

Software que ativa ou desativa o recurso de abrir mais de uma instância (multi cliente) do Tibia.


Download:

TibiaMC.7z (9kb)

Att, Gilson Fabiano

sexta-feira, 6 de abril de 2012

Criando Dialogs com C++ em ambientes Windows

Criar dialogs (janelas) em ambientes Windows é algo fácil e muitos programadores não se interessam pelo C++ por não ter essa facilidade como no Delphi e Visual Studio, por exemplo.
Mal sabem eles que é muito mais fácil do que imaginam.

Existem várias formas fáceis para criação de dialogs, tanto em matéria de performace, quanto em tamanho em bytes.
Usaremos aqui um editor de recursos (ResEdit) e um IDE (Code::Blocks) usando um compilador GCC 4.6.1, com um conjunto de funções da API do Windows: CreateDialog, DialogBox, EndDialog, PostQuitMessage, IsDialogMessage, GetMessage, TranslateMessage, DispatchMessage, DialogProc, entre outras.

Caso ainda não tenha um ambiente de desenvolvimento leia esse artigo escrito pelo meu amigo Thiago Suchorski, nele é ensinado passo a passo como faze-lo.

Nosso projeto terá 4 arquivos (mais abaixo está para download o projeto todo):
  • main.cpp -- arquivo com nossas funções.
  • resource.h -- cabeçalho do recusro, onde são definidos os IDs.
  • resource.rc -- arquivo com os recursos.
  • manifest.xml -- arquivo de manifest, responsável pelo uso dos temas na dialog

No main.cpp segue o código:

#include <windows.h>
#include "resource.h"

HINSTANCE hInstance;

/* função de eventos da janela sobre */
BOOL CALLBACK DialogProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    switch (uMsg)
    {
        case WM_CLOSE:
            EndDialog(hwnd, 0);
            return TRUE;

        case WM_COMMAND:
            switch(LOWORD(wParam))
            {
                case ID_ABOUT_OK:
                    EndDialog(hwnd, 0);
                    return TRUE;
            }
    }
    return FALSE;

}
/* função de eventos da janela principal    */
BOOL CALLBACK WindowProcedure(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    switch(uMsg)
    {
        case WM_CLOSE:
            PostQuitMessage(0);
            return TRUE;

        case WM_COMMAND:
            switch(LOWORD(wParam))
            {
                case ID_OK:
                case IDM_SAIR1:
                    PostQuitMessage(0);
                    return TRUE;

                case IDM_SOBRE1:
                    DialogBox(hInstance, MAKEINTRESOURCE(IDD_DIALOG2), hwnd, (DLGPROC)DialogProc);
                    return TRUE;

                default:
                    return TRUE;
            }
    }

    return FALSE;
}
int APIENTRY WinMain(HINSTANCE hInst, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
{
    hInstance = hInst;
    HWND hWnd = CreateDialog(hInstance, MAKEINTRESOURCE(IDD_DIALOG1), NULL, WindowProcedure);
    if (!hWnd) return 1;
    MSG Msg;
    while(GetMessage(&Msg, NULL, 0, 0) && hWnd)
    {
       if(!IsDialogMessage(hWnd, &Msg))
       {
           TranslateMessage(&Msg);
           DispatchMessage(&Msg);
       }
    }
    return Msg.wParam;
}

Vamos destacar as funções CreateDialog e DialogBox com suas respectivas funções de tratamento de eventos. Mas porque usar EndDialog ao invés de PostQuitMessage na DialogProc da DialogBox? Porquê a PostQuitMessage indica que o aplicativo está sendo finalizado e não que a janela está sendo fechada, por isso o uso da EndDialog. Mas o uso da PostQuitMessage nas funções de tratamento da DialogBox é válido.

Arquivo resource.h

#ifndef IDC_STATIC
#define IDC_STATIC (-1)
#endif

#define IDD_DIALOG1                             100
#define IDR_MENU1                               101
#define IDD_DIALOG2                             102
#define ID_OK                                   1000
#define ID_ABOUT_OK                             1001
#define IDM_SOBRE1                              40000
#define IDM_SAIR1                               40001

E o resource.rc

#include <windows.h>
#include <commctrl.h>
#include <richedit.h>
#include "resource.h"
//
// Menu resources
//
LANGUAGE LANG_PORTUGUESE, SUBLANG_PORTUGUESE
IDR_MENU1 MENU
{
    POPUP "Arquivo"
    {
        MENUITEM "Sair", IDM_SAIR1
    }
    POPUP "Ajuda"
    {
        MENUITEM "Sobre", IDM_SOBRE1
    }
}
//
// Dialog resources
//
LANGUAGE LANG_PORTUGUESE, SUBLANG_PORTUGUESE
IDD_DIALOG1 DIALOG 0, 0, 186, 76
STYLE DS_3DLOOK | DS_CENTER | DS_MODALFRAME | DS_SHELLFONT | WS_CAPTION | WS_VISIBLE | WS_POPUP | WS_SYSMENU
CAPTION "Tela de exemplo"
MENU IDR_MENU1
FONT 8, "Ms Shell Dlg"
{
    DEFPUSHBUTTON   "Fechar", ID_OK, 69, 58, 50, 14
    GROUPBOX        "Informação", IDC_STATIC, 4, 4, 178, 48
    CTEXT           "Um simples exemplo de como criar telas com c++ facilmente sem o uso do visual studio =)", IDC_STATIC, 11, 21, 166, 23, SS_CENTER
}
LANGUAGE LANG_PORTUGUESE, SUBLANG_PORTUGUESE
IDD_DIALOG2 DIALOG 0, 0, 143, 68
STYLE DS_3DLOOK | DS_CENTER | DS_MODALFRAME | DS_SHELLFONT | WS_CAPTION | WS_VISIBLE | WS_POPUP | WS_SYSMENU
CAPTION "Sobre"
FONT 8, "Ms Shell Dlg"
{
    DEFPUSHBUTTON   "OK", ID_ABOUT_OK, 46, 50, 50, 14
    CTEXT           "Sobre o autor\n\nNome: Gilson Fabiano\nEmail: theafien@gmail.com", IDC_STATIC, 4, 4, 135, 35, SS_CENTER
}
//
// Manifest resources
//
LANGUAGE LANG_PORTUGUESE, SUBLANG_PORTUGUESE
1                  RT_MANIFEST    ".\\manifest.xml"

E por fim o manifest.xml:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
  <dependency>
    <dependentAssembly>
      <assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="*" publicKeyToken="6595b64144ccf1df" language="*"/>
    </dependentAssembly>
  </dependency>
  <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
    <security>
      <requestedPrivileges>
        <requestedExecutionLevel
          level="asInvoker"
          uiAccess="false"/>
      </requestedPrivileges>
    </security>
  </trustInfo>
</assembly>

Se você for um bom programador e seguir toda a lógica e fluxo dos códigos irá entender o funcionamento, do contrário releia até entender.

Downloads


Erros de português revisado pelo André Luiz.

Att, Gilson Fabiano.