Ir para conteúdo

ipax

Apoiador Byte
  • Postagens

    2
  • Registro em

Posts postados por ipax

  1. Opa, muito interessante a iniciativa, obrigado por ter compartilhado ?

    Como já tem um tempinho que postou e ninguém respondeu, tirei uns minutinhos agora pela manha para analisar e resolver.

    O crackme é bem simples e consiste de 2 funções principais (anti-debug e o algoritmo em si).

    1- anti-debug, ispresent() com a função ptrace().

    000005df  6a00               push    0x0 {var_10}
    000005e1  6a01               push    0x1 {var_14}
    000005e3  6a00               push    0x0 {var_18}
    000005e5  6a00               push    0x0 {var_1c}
    000005e7  e874feffff         call    ptrace
    000005ec  83c410             add     esp, 0x10
    000005ef  83f8ff             cmp     eax, 0xffffffff

    Traduzindo para C essa função, teriamos algo assim:

    int ispresent()
    {
     int pval;
     if (ptrace(PTRACE_TRACEME, 0, 1, 0) == 0xffffffff) 
        printf("Não vai nem me pagar uma cerveja\n");
        pval = 1;
     else
        pval = 0;
        
     return pval;    
    }

    PTRACE_TRACEME - Indicate that this process is to be traced by its parent
    RETURN VALUE - On  error,  all requests return -1, and errno is set appropriately

    Quando utilizamos a função ptrace() com o request PTRACE_TRACEME, os valores de pid_t pid, void *addr e void *data são ignorados, portanto os valores utilizados na função poderiam ser qualquer um ? 

    2- algoritmo, nadaaqui() - Onde a mágica acontece ?️‍♂️

    Tentando ser o mais sucinto possível, o programa armazena em um float o valor 720300.0 e divide esse valor (incial) e o resultado subsequente por cada "numero" enviado pelo usuário.

    Em C seria mais ou menos assim:

    long double nadaaqui(char *userInput)
    {
      float key = 720300.0;
    
      while ( *userInput )
        key = key / (long double)(*userInput++ - 48);
    
      return key;
    }

    A atribuição de key pode ter sido feita de diversas formas no código original, no exemplo acima utilizei -48 por conta dos caracteres que são enviados no userInput.
    Um observação importante é que o programa só aceita números 0-9, portanto como o que é enviado é um char teriamos de acordo com a tabela ASCII:

    Oct    Dec    Hex   Char
    060    48     30      0
    061     49      31      1
    062    50     32       2
    063    51      33       3
    064    52     34       4
    ...

    Acredito que na implementação original o autor utilizou isdigit() (ctype.h) para fazer essa verificação.

    0x000006bc <+93>:    call   0x470 <__ctype_b_loc@plt>

    De qualquer forma, se enviarmos 0 como char ele seria 48 em decimal, então 48-48 = 0. No código, val = val / (long double)0;
    Caso enviassemos 1 como char, seria 49, então 49-48 = 1. No código, val = val / (long double)1;
    Deu para entender a idéia.

    3- main()

    Como tinha comentado, o código é bem simples e main() executa algumas funções.

    1- Verifica se o programa está sendo debugado (Pode deixar que eu pago a breja :D)
    2- Verifica se a sequência digitada contém caracteres inválidos (Somente números são permitidos).
    3- Verifica se o resultado de nadaaqui() == 1.0, sendo assim, a chave é validada ou não.

    0000073d  d9e8               fld1    
    0000073f  dfe9               fucomip st0, st1
    00000741  ddd8               fstp    st0, st0

    Com isso essa lógica em mente, escrevi o keygen ?:

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <math.h>
    #include <time.h>
    #include <stdbool.h>
    
    long double validateNumber(int, float);
    int nothingToCheck();
    
    int main()
    {
        struct timespec ts;
        clock_gettime(CLOCK_MONOTONIC, &ts);
        srand((time_t)ts.tv_nsec);
    
        nothingToCheck();
    
        return 0;
    }
    
    int randomNumber()
    {
        return (rand() % 6) + 2;
    }
    
    long double validateNumber(int num, float result)
    {
        float key;
        key = result / (long double)num;
    
        if (roundf(key) != key)
            return false;
    
        return key;
    }
    
    int nothingToCheck()
    {
        char num[32];
        float result = 720300.0;
        int counter, valid;
    
        counter = valid = 0;
    
        while (!valid)
        {
            num[counter] = randomNumber();
    
            if (validateNumber(num[counter], result))
            {
                result = validateNumber(num[counter], result);
                counter++;
            }
            else
                continue;
    
            if (result == 1.000000)
                valid = 1;
    
        }
        counter++;
        num[counter] = '\0';
    
        printf("Key: ");
    
        for (int i = 0; i < strlen(num) - 1; i++)
            printf("%d", num[i]);
    
        printf("\n");
    
        return true;
    }

    Compilado com a flag: -lm (obrigatória por conta do roundf())

    Algumas considerações sobre o keygen: 

    Para ganhos de performance, utilizei um seed de nanoseconds para o rand() gerar números aleatórios entre 2 e 7 e utilizar como userInput, visto que divisões por 0 daria inf e por ´1 daria o mesmo número. As divisões por 8 e 9 nunca resultariam em um resultado sem dízima portando inválidos. Com certeza existe uma outra forma de fazer esse cálculo mas não atrapalhou no resultado final.

    ipax@core:~/crackme$ ./mbin-hehe-keygen
    Key: 523257777

    ipax@core:~/crackme$ ./hehe 523257777
    Isso aí!! A chave é válida!

    ?‍♂️

    Como uma brincadeira extra, tentei gerar uma lista das combinações ou keys possíveis e cheguei ao número de 5338.

    Muito divertido e como falei, obrigado por compartilhar.

    Envie mais! ?

     

    • Curtir 1
    • l33t 1
×
×
  • Criar Novo...