Estava procurando rever algumas coisas de engenharia reversa e fui ‘brincar’ com serial Fishing. Serial fishing é possível quando a aplicação, ao tentar verificar se o serial digitado foi fornecido corretamente, gera o serial original para fazer uma comparação. Vamos ver como podemos tirar proveito disto.

Como o serial gerado pela aplicação esta residente na memória, basta abrir o processo e pegar ele bonitinho na memória. A parte mais emocionante é encontrar o serial na memória (isto se ele estiver lá). Para fins de uso pessoal estudo, achando tendo o serial na memória já basta, porém como bons nerds atoas que somos, nós escrevemos um código que pegue e exiba o serial na tela.

O programa que estava testar é o jogo Xadrez Master na versão 5.8.6.0, ao abri-lo no OllyDbg e digitar qualquer nome e serial, é exibida uma mensagem informando que o serial está inválida. Sem fechar a janela, voltei para o Olly, dei pause no processo e então pude analisar o seguinte na memória:

CPU Dump:
0012EA48  |00F2F1A8  ASCII "Maycon Maia Vitali (0ut0fBound)"
0012EA4C  |00F36868  ASCII "XAD-BXLT4-TLKXJ"
0012EA50  |00F1C8A4  ASCII "XAD"
0012EA54  |00F2F18C  ASCII "AABBCCDDEEFF"
0012EA58  |00EDCB64  ASCII "AAB"
0012EA5C  |00F1CEAC  ASCII "Maycon Maia Vitali (0ut0fBound)"
0012EA60  |00F2F170  ASCII "XAD-BXLT4-TLKXJ"
0012EA64  |00F1CE90  ASCII "AABBCCDDEEFF"
0012EA68  |00ED1D64  ASCII "AABBCCDDEEFF"
0012EA6C  |00ED1D80  ASCII "AABBCCDDEEFF"
0012EA70  |00F1CE74  ASCII "AABBCCDDEEFF"
0012EA74  |00ED1D0C  ASCII "Maycon Maia Vitali (0ut0fBound)"
0012EA78  |00ED1D38  ASCII "Maycon Maia Vitali (0ut0fBound)"

Estes dados foram obtidos da pilha, e podemos notar o serial XAD-BXLT4-TLKXJ para o nome Maycon Maia Vitali (0ut0fBound) em diversos endereços diferentes. Vamos pegar só um exemplo:

0012EA48  |00F2F1A8  ASCII "Maycon Maia Vitali (0ut0fBound)"
0012EA4C  |00F36868  ASCII "XAD-BXLT4-TLKXJ"

Neste caso, nos endereços 0012EA48 e 0012EA4C temos respectivamente os endereços de memória onde estão o nome e serial válidos. Portanto nosso “Serial Fisher” deve obter antes os endereços das strings na memória para depois le-las em sí.

Sem muitas firulas, segue um código-rápido que fiz para fazer Serial Fishing desse software. Lembrando que é necessário digitar um serial qualquer e pressionar o botão de registrar e, sem fechar a caixa da mensagem informando serial inválido, rodar o “fisher”:

/**
 * Serial fisher para RKSoft Xadrez Master 5.8.6.0
 * Por Maycon M. Vitali (0ut0fBound)
 *
 * ATENÇÃO: Este código foi criado para fins de estudo, caso deseje
 * obter o software é necessário que se page a licensa estipulada
 * pelo fabricante.
 */
 
#include <stdio.h>
#include <string.h>
#include <windows.h>
 
#define NAME_PTR_ADDR   0x0012EA48
#define SERIAL_PTR_ADDR 0x0012EA4C
 
#define TITULO_JANELA "Xadrez Master"
 
#define MAX_SIZE 100
 
int main()
{
    HWND cmeHandle;                // Handle da janela do processo
    DWORD cmePid;                  // PID do processo crackme
    HANDLE cmdProcHandle;          // Handle do processo ( depois de aberto )
 
    long lngAddrNome, lngAddrSerial;
    char strNome[MAX_SIZE], strSerial[MAX_SIZE];
 
    char strMensagem[MAX_SIZE*2 + 20];
 
    // Procuramos pela janela do crackme
    if (!(cmeHandle = FindWindow(0, TITULO_JANELA)))
    {
        MessageBoxA(
            0,
            "Execute o xadrez.exe e tente registrar com qualquer serial antes.",
            "Serial Fishing",
            0
        );
        exit(-1);
    }
 
    // Pega e abre processo
    GetWindowThreadProcessId(cmeHandle, &cmePid);
    cmdProcHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, cmePid);
 
    // Obtém os endereços das informações na pilha
    ReadProcessMemory(
      cmdProcHandle, (void *)NAME_PTR_ADDR, &lngAddrNome, sizeof(lngAddrNome), NULL
    );
    ReadProcessMemory(
      cmdProcHandle, (void *)SERIAL_PTR_ADDR, &lngAddrSerial, sizeof(lngAddrSerial), NULL
    );
 
    // Pega os valores nos endereços obtidos
    ReadProcessMemory(
      cmdProcHandle, (const void *)lngAddrNome, strNome, sizeof(strNome) - 1, NULL
    );
    ReadProcessMemory(
      cmdProcHandle, (const void *)lngAddrSerial, strSerial, sizeof(strSerial) - 1, NULL
    );
 
    snprintf (
      strMensagem, sizeof(strMensagem) - 1, "Nome: %s\nSerial: %s", strNome, strSerial
    );
 
    MessageBoxA(0, strMensagem, "Enjoy!", 0);
 
    printf ("Nome ...: %s\n", strNome);
    printf ("Serial .: %s\n", strSerial);
 
    return 0;
}

Como dito no comentário, não forneci esse código com fins de prejudicar a equipe que desenvolve a ferramenta. Tentarei futuramente disponibilizar análise de malwares e afins para não ter problemas futuros.