GUIDA Reverse Engineering, CrackMe #4: kgenme1.0

DispatchCode

Moderatore
Staff Forum
Utente Èlite
2,207
1,844
CPU
Intel I9-10900KF 3.75GHz 10x 125W
Dissipatore
Gigabyte Aorus Waterforce X360 ARGB
Scheda Madre
Asus 1200 TUF Z590-Plus Gaming ATX DDR4
HDD
1TB NVMe PCI 3.0 x4, 1TB 7200rpm 64MB SATA3
RAM
DDR4 32GB 3600MHz CL18 ARGB
GPU
Nvidia RTX 3080 10GB DDR6
Audio
Integrata 7.1 HD audio
Monitor
LG 34GN850
PSU
Gigabyte P850PM
Case
Phanteks Enthoo Evolv X ARGB
Periferiche
MSI Vigor GK30, mouse Logitech
Net
FTTH Aruba, 1Gb (effettivi: ~950Mb / ~480Mb)
OS
Windows 10 64bit / OpenSUSE Tumbleweed

aolvos's kgenme1.0​

Autore: aolvos


E' il quarto articolo che scrivo che ha come argomento CrackMe/KeygenMe, gli altri potete trovarli nelle Guide per gli utenti, con altri articoli/guide.

Quello che presento oggi è particolarmente interessante secondo me. Si tratta di un keygenme, come il precedente; lo scopo principale è quindi quello di trovare il seriale e non di patchare il software modificando i controlli. La differenza con il precedente è la difficoltà: questo è classificato come 3.7 (il precedente era 2.0).

0. Prime analisi​



Il primo controllo che faccio, come sempre, è con software come RGD Packer detector o PEiD. In questo caso ho scelto RDG, in quanto PEiD non individuava correttamente il compilatore:

rdg_screen.png

nulla di anomalo, se non l'utilizzo di isDebuggerPresent() che ci viene segnalato e l'utilizzo di MSVC++ (più o meno so che API aspettarmi).
Oltre a ciò sappiamo anche che è un sw per x86.

1. Esecuzione con x32dbg​



L'applicazione si presenta in questo modo:

screen_app.png

inserendo un seriale errato il messaggio di errore che compare è il seguente:

seriale_errato.png

A questo punto ho scelto un approccio differente rispetto alle altre volte. Invece di cercare le stringhe, ho messo un breakpoint (bp) sulla funzione GetWindowTextW. Una volta individuata ho lanciato il programma, in modo da farlo caricare; mettere un bp prima significherebbe ricevere un sacco di interruzioni.
A finestra caricata, ho piazzato un bp sul RET della funzione:

getwindowtext_ret.png

a questo punto premendo F8 si segue l'indirizzo di ritorno e ci si ritrova qui:

getwindowtext_call.png

l'indirizzo contrassegnato con il rettangolino nero è la posizione corrente di EIP, l'istruzione dopo alla CALL. Da notare che infatti la funzione è stata chiamata usando il registro EDI, nel quale è presente il suo indirizzo.

Poco sotto vengono calcolate le lunghezze dei due input (user e pwd):

Codice:
00AF165E | 8DBC24 A0000000           | lea edi,dword ptr ss:[esp+A0]                                             |
00AF1665 | 8D4F 02                   | lea ecx,dword ptr ds:[edi+2]                                              | ecx:L"SDA-SDAS-DASD-ADSA", edi+2:L"fsdfdda"
00AF1668 | 0F1F8400 00000000         | nop dword ptr ds:[eax+eax],eax                                            |
00AF1670 | 66:8B07                   | mov ax,word ptr ds:[edi]                                                  | edi:L"dfsdfdda"
00AF1673 | 83C7 02                   | add edi,2                                                                 | edi:L"dfsdfdda"
00AF1676 | 66:85C0                   | test ax,ax                                                                |
00AF1679 | 75 F5                     | jne kgenme.AF1670                                                         |
00AF167B | 2BF9                      | sub edi,ecx                                                               | edi:L"dfsdfdda", ecx:L"SDA-SDAS-DASD-ADSA"
00AF167D | 8D9424 C8000000           | lea edx,dword ptr ss:[esp+C8]                                             |
00AF1684 | D1FF                      | sar edi,1                                                                 | edi:L"dfsdfdda"
00AF1686 | 8D4A 02                   | lea ecx,dword ptr ds:[edx+2]                                              | ecx:L"SDA-SDAS-DASD-ADSA"
00AF1689 | 0F1F80 00000000           | nop dword ptr ds:[eax],eax                                                |
00AF1690 | 66:8B02                   | mov ax,word ptr ds:[edx]                                                  |
00AF1693 | 83C2 02                   | add edx,2                                                                 |
00AF1696 | 66:85C0                   | test ax,ax                                                                |
00AF1699 | 75 F5                     | jne kgenme.AF1690                                                         |
00AF169B | 2BD1                      | sub edx,ecx                                                               | ecx:L"SDA-SDAS-DASD-ADSA"

Il codice prosegue con:

Codice:
00AF169F | 83FA 04                   | cmp edx,4                                                                 |
00AF16A2 | 0F8C BC000000             | jl kgenme.AF1764                                                          |
00AF16A8 | 83FF 13                   | cmp edi,13                                                                | edi:L"dfsdfdda"
00AF16AB | 0F8C B3000000             | jl kgenme.AF1764                                                          |
00AF16B1 | 8D8C24 C8000000           | lea ecx,dword ptr ss:[esp+C8]                                             |
00AF16B8 | E8 03FEFFFF               | call kgenme.AF14C0                                                        |
00AF16BD | 8BD7                      | mov edx,edi                                                               | edi:L"dfsdfdda"
00AF16BF | 8D8C24 A0000000           | lea ecx,dword ptr ss:[esp+A0]                                             |
00AF16C6 | 8BF0                      | mov esi,eax                                                               |
00AF16C8 | E8 F3FDFFFF               | call kgenme.AF14C0                                                        |
00AF16CD | 3BF0                      | cmp esi,eax                                                               |
00AF16CF | 0F85 8F000000             | jne kgenme.AF1764                                                         |

sappiamo che in EDX c'è la lunghezza dell'username inserito. Viene confrontato con il valore 4 e se è inferiore avviene un JL a un indirizzo più in basso. In poche parole, viene saltato tutto, andando al messaggio di errore; il discorso è analogo per il controllo sotto, dove viene verificato se EDI è inferiore a 0x13 (19 in decimale). EDI è la lunghezza della key, che non può quini essere inferiore a 19.

Durante la fase di input abbiamo ricevuto un altro aiuto: digitando la key in automatico vengono inseriti dei "trattini" (il meno) ogni 4 caratteri della chiave. Quindi abbiamo 16 caratteri + 3 trattini che li separano.

Sino a qui tutto bene. A questo punto siamo a un check successivo; viene chiamata una funzione (0xAF14C0) passando come input prima l'username e poi la password.
Il ritorno viene salvato in EAX e poi salvato in ESI, e confrontato successivamente con EAX. Cosa possiamo aspettarci? Visto che la funzione è la stessa, verranno manipolati gli input (user e pwd) in modo tale da generare un qualche tipo di numero/hash che deve avere il medesimo valore per essere valido (il JNE salta al messaggio di errore se non sono uguali).

2. Algoritmo di verifica​



Ahimè, andando a guardare quella funzione scopro che non è proprio semplicissima... almeno non da leggere, ecco. Fa uso del set SIMD (Single Instruction, Multiple Data). Si tratta di quelle istruzioni che introducono il "parallelismo a livello di istruzioni"; in questo caso il set è SSE.
Riporto il codice per intero sotto spoiler (tenetevi forte):

Codice:
00AF14C0 | 55                        | push ebp                                                                  |
00AF14C1 | 8BEC                      | mov ebp,esp                                                               |
00AF14C3 | 83EC 08                   | sub esp,8                                                                 |
00AF14C6 | 53                        | push ebx                                                                  |
00AF14C7 | 57                        | push edi                                                                  |
00AF14C8 | 8BFA                      | mov edi,edx                                                               |
00AF14CA | 33C0                      | xor eax,eax                                                               |
00AF14CC | 33D2                      | xor edx,edx                                                               |
00AF14CE | 897D F8                   | mov dword ptr ss:[ebp-8],edi                                              |
00AF14D1 | 8BD9                      | mov ebx,ecx                                                               | ecx:L"dfsdfdda"
00AF14D3 | 85FF                      | test edi,edi                                                              |
00AF14D5 | 0F8E D7000000             | jle kgenme.AF15B2                                                         |
00AF14DB | 83FF 08                   | cmp edi,8                                                                 |
00AF14DE | 0F82 BC000000             | jb kgenme.AF15A0                                                          |
00AF14E4 | 833D 7853AF00 02          | cmp dword ptr ds:[AF5378],2                                               |
00AF14EB | 0F8C AF000000             | jl kgenme.AF15A0                                                          |
00AF14F1 | 0F2835 0034AF00           | movaps xmm6,xmmword ptr ds:[AF3400]                                       |
00AF14F8 | 8BCF                      | mov ecx,edi                                                               | ecx:L"dfsdfdda"
00AF14FA | 83E1 F8                   | and ecx,FFFFFFF8                                                          | ecx:L"dfsdfdda"
00AF14FD | 8D7A 07                   | lea edi,dword ptr ds:[edx+7]                                              |
00AF1500 | 894D FC                   | mov dword ptr ss:[ebp-4],ecx                                              |
00AF1503 | 0F57ED                    | xorps xmm5,xmm5                                                           |
00AF1506 | 0F57E4                    | xorps xmm4,xmm4                                                           |
00AF1509 | 56                        | push esi                                                                  |
00AF150A | 66:0F1F4400 00            | nop word ptr ds:[eax+eax],ax                                              |
00AF1510 | 66:0F6EC0                 | movd xmm0,eax                                                             |
00AF1514 | 8D77 FE                   | lea esi,dword ptr ds:[edi-2]                                              |
00AF1517 | 66:0F70C8 00              | pshufd xmm1,xmm0,0                                                        |
00AF151C | 8D4F 01                   | lea ecx,dword ptr ds:[edi+1]                                              | ecx:L"dfsdfdda"
00AF151F | F3:0F7E0443               | movq xmm0,qword ptr ds:[ebx+eax*2]                                        |
00AF1524 | 8D57 FF                   | lea edx,dword ptr ds:[edi-1]                                              |
00AF1527 | 66:0F3833C0               | pmovzxwd xmm0,xmm0                                                        |
00AF152C | 66:0FFECE                 | paddd xmm1,xmm6                                                           |
00AF1530 | 66:0F6EDE                 | movd xmm3,esi                                                             |
00AF1534 | 66:0F3840C1               | pmulld xmm0,xmm1                                                          |
00AF1539 | 66:0FFEE8                 | paddd xmm5,xmm0                                                           |
00AF153D | 66:0F6ED1                 | movd xmm2,ecx                                                             | ecx:L"dfsdfdda"
00AF1541 | 66:0F6EC7                 | movd xmm0,edi                                                             |
00AF1545 | 83C7 08                   | add edi,8                                                                 |
00AF1548 | 66:0F62D8                 | punpckldq xmm3,xmm0                                                       |
00AF154C | F3:0F7E4443 08            | movq xmm0,qword ptr ds:[ebx+eax*2+8]                                      |
00AF1552 | 83C0 08                   | add eax,8                                                                 |
00AF1555 | 66:0F6ECA                 | movd xmm1,edx                                                             |
00AF1559 | 66:0F62CA                 | punpckldq xmm1,xmm2                                                       |
00AF155D | 66:0F62D9                 | punpckldq xmm3,xmm1                                                       |
00AF1561 | 66:0F3833C0               | pmovzxwd xmm0,xmm0                                                        |
00AF1566 | 66:0F3840C3               | pmulld xmm0,xmm3                                                          |
00AF156B | 66:0FFEE0                 | paddd xmm4,xmm0                                                           |
00AF156F | 3B45 FC                   | cmp eax,dword ptr ss:[ebp-4]                                              |
00AF1572 | 7C 9C                     | jl kgenme.AF1510                                                          |
00AF1574 | 8B7D F8                   | mov edi,dword ptr ss:[ebp-8]                                              |
00AF1577 | 66:0FFEE5                 | paddd xmm4,xmm5                                                           |
00AF157B | 0F28C4                    | movaps xmm0,xmm4                                                          |
00AF157E | 66:0F73D8 08              | psrldq xmm0,8                                                             |
00AF1583 | 66:0FFEE0                 | paddd xmm4,xmm0                                                           |
00AF1587 | 0F10C4                    | movups xmm0,xmm4                                                          |
00AF158A | 66:0F73D8 04              | psrldq xmm0,4                                                             |
00AF158F | 66:0FFEE0                 | paddd xmm4,xmm0                                                           |
00AF1593 | 66:0F7EE2                 | movd edx,xmm4                                                             |
00AF1597 | 5E                        | pop esi                                                                   |
00AF1598 | 3BC7                      | cmp eax,edi                                                               |
00AF159A | 7D 16                     | jge kgenme.AF15B2                                                         |
00AF159C | 0F1F40 00                 | nop dword ptr ds:[eax],eax                                                |
00AF15A0 | 8D48 01                   | lea ecx,dword ptr ds:[eax+1]                                              | ecx:L"dfsdfdda"
00AF15A3 | 0FB70443                  | movzx eax,word ptr ds:[ebx+eax*2]                                         |
00AF15A7 | 0FAFC1                    | imul eax,ecx                                                              | ecx:L"dfsdfdda"
00AF15AA | 03D0                      | add edx,eax                                                               |
00AF15AC | 8BC1                      | mov eax,ecx                                                               | ecx:L"dfsdfdda"
00AF15AE | 3BC7                      | cmp eax,edi                                                               |
00AF15B0 | 7C EE                     | jl kgenme.AF15A0                                                          |
00AF15B2 | 5F                        | pop edi                                                                   |
00AF15B3 | 0FB6C2                    | movzx eax,dl                                                              |
00AF15B6 | 5B                        | pop ebx                                                                   |
00AF15B7 | 8BE5                      | mov esp,ebp                                                               |
00AF15B9 | 5D                        | pop ebp                                                                   |
00AF15BA | C3                        | ret                                                                       |

questo set fa uso dei registri XMM, da 0 a 15 in x86. Sono dei registri di 128-bit. Un'istruzione come questa pmulld xmm0,xmm3 significa testualmente "Multiply Packed Integers and Store Low Result". Come funziona? Bhe in sostanza consente di fare operazioni su quattro valori a 32bit nello stesso momento! Quindi non ci sarà una moltiplicazione (o una somma, dipende dall'istruzione) di un solo valore con un'altro, ma di più valori nello stesso momento.

Istruzioni come queste velocizzano molto un'operazione. Immaginatevi qualcosa di semplice come due vettori di int a 32-bit che vanno sommati e i vari risultati vanno messe nelle rispettive i-esime posizioni; quindi v1[0] + v2[0] andrà in v3[0] e così via.
Un'operazione come questa fiene fatto nello stesso momento su 4 posizioni invece che su 1 sola.

A parte questa splendida notizia, direi di proseguire... e soffermarci sulla prima parte:

Codice:
00AF14C8 | 8BFA                      | mov edi,edx                                                               |
00AF14CA | 33C0                      | xor eax,eax                                                               |
00AF14CC | 33D2                      | xor edx,edx                                                               |
00AF14CE | 897D F8                   | mov dword ptr ss:[ebp-8],edi                                              |
00AF14D1 | 8BD9                      | mov ebx,ecx                                                               | ecx:L"dfsdfdda"
00AF14D3 | 85FF                      | test edi,edi                                                              |
00AF14D5 | 0F8E D7000000             | jle kgenme.AF15B2                                                         |
00AF14DB | 83FF 08                   | cmp edi,8                                                                 |
00AF14DE | 0F82 BC000000             | jb kgenme.AF15A0                                                          |

EDI contiene la lunghezza della stringa (che può essere user o pwd); prima viene verificato che sia diversa da 0. Successivamente avviene un altro controllo con il valore 8; se è minore di 8 salta all'indirizzo indicato da JB.

A quell'indirizzo si trova questo (riporto sempre il codice per chiarezza, ma potete vederlo dal listato lungo li sopra):
Codice:
00AF15A0 | 8D48 01                   | lea ecx,dword ptr ds:[eax+1]                                              | ecx:L"dfsdfdda"
00AF15A3 | 0FB70443                  | movzx eax,word ptr ds:[ebx+eax*2]                                         |
00AF15A7 | 0FAFC1                    | imul eax,ecx                                                              | ecx:L"dfsdfdda"
00AF15AA | 03D0                      | add edx,eax                                                               |
00AF15AC | 8BC1                      | mov eax,ecx                                                               | ecx:L"dfsdfdda"
00AF15AE | 3BC7                      | cmp eax,edi                                                               |
00AF15B0 | 7C EE                     | jl kgenme.AF15A0                                                          |

il mio sospetto è che essendo inferiore a 8 non riesca ad applicare le operazioni usando i registri XMM, e quindi c'è questo blocco "personalizzato". Da notare che sappiamo che l'username è lungo minimo 4 caratteri e massimo 8, quindi direi rientri in qusesto caso.

LEA non fa altro che calcolare la somma di EAX+1, quindi in ECX al primo ciclo ci sarà 1. EAX viene usato come indice e sommato alla base della stringa (che si trova in EBX). Non vi sarà sfuggito però che moltiplica il valore 2, e quindi non sembra essere un normale array di caratteri.
In realtà lo è, si tratta di UTF-8 però: GetWindowTextW indica proprio questo. Quindi ogni carattere occupa 2-byte.
L'istruxione successiva moltiplica EAX (il carattere letto) con la sua posizione nella stringa + 1. E prosegue sino alla fine.

In sostanza quel codice è traducibile come:

C:
    int i = -1;
    int sum = 0;
  
    while(i++ < len)
        sum += (key[i] * (i+1));

dove la somma è in EDX.

A questo punto non ci resta che comprendere il set SSE4... almeno abbastanza da capire cosa avviene.
Ho utilizzato spesso google poichè non ricordavo i significati delle varie operazioni, come potete immaginare. Non riporto tutto il codice nuovamente, ma solo una parte:

Codice:
00AF14FD | 8D7A 07                   | lea edi,dword ptr ds:[edx+7]                                              |
00AF1500 | 894D FC                   | mov dword ptr ss:[ebp-4],ecx                                              |
00AF1503 | 0F57ED                    | xorps xmm5,xmm5                                                           |
00AF1506 | 0F57E4                    | xorps xmm4,xmm4                                                           |
00AF1509 | 56                        | push esi                                                                  |
00AF150A | 66:0F1F4400 00            | nop word ptr ds:[eax+eax],ax                                              |
00AF1510 | 66:0F6EC0                 | movd xmm0,eax                                                             |
00AF1514 | 8D77 FE                   | lea esi,dword ptr ds:[edi-2]                                              |
00AF1517 | 66:0F70C8 00              | pshufd xmm1,xmm0,0                                                        |
00AF151C | 8D4F 01                   | lea ecx,dword ptr ds:[edi+1]                                              | ecx:L"dfsdfdda"
00AF151F | F3:0F7E0443               | movq xmm0,qword ptr ds:[ebx+eax*2]                                        |
00AF1524 | 8D57 FF                   | lea edx,dword ptr ds:[edi-1]                                              |
00AF1527 | 66:0F3833C0               | pmovzxwd xmm0,xmm0                                                        |
00AF152C | 66:0FFECE                 | paddd xmm1,xmm6                                                           |

che cosa avviene qui? Se lo capite spiegatemelo ?
Scherzi a parte, da debugger si comprende meglio, potendo vedere il contenuto dei registri. Al termine dell MOVQ vengono spostati 4 caratteri in XMM0. Questi occupano metà della dimensione del registro; viene usata la PMOVZXWD per effettuare una MOV estendendo con 0 la WORD portandola a una DWORD. Facendo un esempio con la stringa che vedete nel commento sul codice qui sopra, il registro si presenta così XMM0 = 00000000000000000064007300660064. Dopo la PMOV la situazione è questa XMM0 = 00000064000000730000006600000064
I byte che vedete sono i primi 4 caratteri della stringa. In XMM1 vengono poi spostati gli indici 00000004000000030000000200000001. Leggendo da destra vedete che 0x64 ha indice 1, 0x66 ha indice 2, 0x73 ha indice 3 e 0x64 ha indice 4, quindi in memoria (o meglio, nel registro) è salvata come "dsfd" (little endian).

Questi due registri vengono moltiplicati e il risultato viene poi mantenuto in XMM5:

Codice:
00AF1534 | 66:0F3840C1               | pmulld xmm0,xmm1                                                          |
00AF1539 | 66:0FFEE8                 | paddd xmm5,xmm0                                                           |

dopo la moltiplicazione, XMM0 si presenta come 0000019000000159000000CC00000064.
La parte dopo è analoga - ricorda un unrolled loop, ma solo per logica. In sostanza è lo stesso codice visto nel blocco sopra (quando la lunhezza è inferiore a 8). O meglio, non è lo stesso codice, ma il risultato è il medesimo.

Piccola nota: trattandosi di 19 caratteri, vengono lette due "tranche" da 8 (viene fatto 1 ciclo) per un totale di 16 caratteri. I 3 che rimangono entrano nell'if visto sopra, quello della lunghzza infeiore a 8 caratteri.

3. Primo keygen: generiamo le stesse somme​



A questo punto siamo in grado di riprodurre questa parte e generare dei seriali che superino il controllo sull'uguaglianza delle moltiplicazioni e somme fatte sui caratteri.
Il controllo è il JNE riportato sotto:

Codice:
00AF16B1 | 8D8C24 C8000000           | lea ecx,dword ptr ss:[esp+C8]                                             |
00AF16B8 | E8 03FEFFFF               | call kgenme.AF14C0                                                        |
00AF16BD | 8BD7                      | mov edx,edi                                                               |
00AF16BF | 8D8C24 A0000000           | lea ecx,dword ptr ss:[esp+A0]                                             |
00AF16C6 | 8BF0                      | mov esi,eax                                                               |
00AF16C8 | E8 F3FDFFFF               | call kgenme.AF14C0                                                        |
00AF16CD | 3BF0                      | cmp esi,eax                                                               |
00AF16CF | 0F85 8F000000             | jne kgenme.AF1764                                                         |

Come detto nel paragrafo precedente ESI e EAX contengono i due valori numerici che devono essere identici. Dobbiamo creare la procedura che abbiamo più o meno compreso sopra.

Innanzitutto la password:
C:
int gen_password(char *key)
{
    for(int i=0; i<PWD_LEN; i++)
        key[i] = alphabet[rand() % 0x24]; // numbers and A-Z chars
  
    key[4]  = '-';
    key[9]  = '-';
    key[14] = '-';
  
    return check_value(key, PWD_LEN);
}

l'alfabeto sono sia numeri che lettere (maiuscole e minuscole).
Poi l'username:

C:
int gen_username(char *user, int *len, int pwd_n) {
  
    while(true) {
        *len = rand() % 4 + 4;
      
        for(int i=0; i<*len; i++) {
            user[i] = alphabet[rand() % 0x3e]; // all chars
        }
      
        user[*len] = 0;
      
        int user_n = check_value(user, *len);
        if(user_n == pwd_n) return user_n;
    }
  
    return -1; // never reached (I hope :P )
}

questo è un pò "forza bruta" in quanto cicla sino a che non trova una combinazione numerica uguale alla password. Ho scelto questo tipo di verifica perchè più efficiente rispetto al contrario (la pwd è più lunga).

La funzione che restituisce il numero è:

C:
int check_value(char *key, int len)
{
    int i = -1;
    int sum = 0;
  
    while(i++ < len)
        sum += (key[i] * (i+1));
  
    return sum & 0xFF;
}

l'ultima AND è dovuta al fatto che il numero viene convertito (nel listato asm di sopra) e l'ampiezza massima che deve avere è appunto quella.
Da notare la semplicità rispetto al codice precedente (in asm), anche perchè non prendo chunk da 4 caratteri.

Il codice completo è questo:
C:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <stdbool.h>

#define  USER_LEN   9
#define  PWD_LEN    19

const char alphabet[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";

int check_value(char *key, int len)
{
    int i = -1;
    int sum = 0;
  
    while(i++ < len)
        sum += (key[i] * (i+1));
  
    return sum & 0xFF;
}

int gen_password(char *key)
{
    for(int i=0; i<PWD_LEN; i++)
        key[i] = alphabet[rand() % 0x24]; // numbers and A-Z chars
  
    key[4]  = '-';
    key[9]  = '-';
    key[14] = '-';
  
    return check_value(key, PWD_LEN);
}

int gen_username(char *user, int *len, int pwd_n) {
  
    while(true) {
        *len = rand() % 4 + 4;
      
        for(int i=0; i<*len; i++) {
            user[i] = alphabet[rand() % 0x3e]; // all chars
        }
      
        user[*len] = 0;
      
        int user_n = check_value(user, *len);
        if(user_n == pwd_n) return user_n;
    }
  
    return -1; // never reached (I hope :P )
}

int main() {
    srand(time(NULL));
  
    int userlen = 0;
  
    char username[USER_LEN] = {'\0'};
    char password[PWD_LEN]  = {'\0'};
  
    int pwd_n = gen_password(password);
    int usr_n = gen_username(username, &userlen, pwd_n);
  
    printf("Username: %s\tUser value: %X\n", username, usr_n);
    printf("Password: %s\tPwd value: %X\n", password, pwd_n);

  
    return 0;
}

Di seguito alcuni output:
Codice:
Username: PVSph User value: BD
Password: HYLK-XV6K-NPHP-I8NN   Pwd value: BD

Username: NclYYgh       User value: BB
Password: O3LZ-3SKN-F57Q-IO15   Pwd value: BB

Username: pQbbf5R       User value: 3A
Password: RFT6-588B-BDYC-WMUZ   Pwd value: 3A

Già che c'è il programma con un input errato, lo eseguo, mettendo un bp sul JNE. In questo modo mi assicuro che salti (essendo sicuramente diversi i valori numerici calcolati):

input_errato.png

Ora prendo il primo input generato li sopra e lo inserisco:

input_quasi_corretto.png

come si nota questa volta il salto non avviene, quindi la somma è giusta!
Se non fosse...

4. Completamento del keygen​



Eh, se non fosse che poco più sotto c'è un altro controllo (proprio superato quel JNE):

Codice:
00AF16D5 | 0FB78424 A4000000         | movzx eax,word ptr ss:[esp+A4]                                            |
00AF16DD | BA 05000000               | mov edx,5                                                                 |
00AF16E2 | 0FB7B424 A6000000         | movzx esi,word ptr ss:[esp+A6]                                            |
00AF16EA | 03F0                      | add esi,eax                                                               |
00AF16EC | 0FB78424 A2000000         | movzx eax,word ptr ss:[esp+A2]                                            |
00AF16F4 | 03F0                      | add esi,eax                                                               |
00AF16F6 | 0FB78424 A0000000         | movzx eax,word ptr ss:[esp+A0]                                            |
00AF16FE | 03F0                      | add esi,eax                                                               |
00AF1700 | C1EE 02                   | shr esi,2                                                                 |
00AF1703 | 0F1F40 00                 | nop dword ptr ds:[eax],eax                                                |
00AF1707 | 66:0F1F8400 00000000      | nop word ptr ds:[eax+eax],ax                                              |
00AF1710 | 0FB78454 A2000000         | movzx eax,word ptr ss:[esp+edx*2+A2]                                      |
00AF1718 | 0FB78C54 A0000000         | movzx ecx,word ptr ss:[esp+edx*2+A0]                                      |
00AF1720 | 03C8                      | add ecx,eax                                                               |
00AF1722 | 0FB78454 A6000000         | movzx eax,word ptr ss:[esp+edx*2+A6]                                      |
00AF172A | 03C8                      | add ecx,eax                                                               |
00AF172C | 0FB78454 A4000000         | movzx eax,word ptr ss:[esp+edx*2+A4]                                      |
00AF1734 | 03C8                      | add ecx,eax                                                               |
00AF1736 | C1E9 02                   | shr ecx,2                                                                 |
00AF1739 | 3BF1                      | cmp esi,ecx                                                               |
00AF173B | 75 27                     | jne kgenme.AF1764                                                         |
00AF173D | 83C2 05                   | add edx,5                                                                 |
00AF1740 | 3BD7                      | cmp edx,edi                                                               |
00AF1742 | 7E CC                     | jle kgenme.AF1710                                                         |
00AF1744 | 6A 40                     | push 40                                                                   |
00AF1746 | 68 A433AF00               | push kgenme.AF33A4                                                        | AF33A4:L"Success!"
00AF174B | 68 B833AF00               | push kgenme.AF33B8                                                        | AF33B8:L"Registration successful!"
00AF1750 | FF7424 1C                 | push dword ptr ss:[esp+1C]                                                |
00AF1754 | FF15 9030AF00             | call dword ptr ds:[<&MessageBoxW>]                                        |
00AF175A | 6A 00                     | push 0                                                                    |
00AF175C | FF15 D830AF00             | call dword ptr ds:[<&PostQuitMessage>]                                    |
00AF1762 | EB 16                     | jmp kgenme.AF177A                                                         |
00AF1764 | 6A 10                     | push 10                                                                   |
00AF1766 | 68 5833AF00               | push kgenme.AF3358                                                        | AF3358:L"Error!"
00AF176B | 68 6833AF00               | push kgenme.AF3368                                                        | AF3368:L"You've entered invalid data!"

Concentriamoci sulla prima parte:

Codice:
00AF16D5 | 0FB78424 A4000000         | movzx eax,word ptr ss:[esp+A4]                                            |
00AF16DD | BA 05000000               | mov edx,5                                                                 |
00AF16E2 | 0FB7B424 A6000000         | movzx esi,word ptr ss:[esp+A6]                                            |
00AF16EA | 03F0                      | add esi,eax                                                               |
00AF16EC | 0FB78424 A2000000         | movzx eax,word ptr ss:[esp+A2]                                            |
00AF16F4 | 03F0                      | add esi,eax                                                               |
00AF16F6 | 0FB78424 A0000000         | movzx eax,word ptr ss:[esp+A0]                                            |
00AF16FE | 03F0                      | add esi,eax                                                               |
00AF1700 | C1EE 02                   | shr esi,2                                                                 |

SS:[ESP+A0] è il nostro seriale che c'è sullo stack, è la prima posizione. Lo riporto per comodità HYLK-XV6K-NPHP-I8NN. Quindi il primo carattere che viene preso è 'L', seguito da 'K'. I due caratteri vengono sommati, e il risultato è in ESI.
Viene fatta la medesima cosa con 'Y' e 'H'. Fatto ciò viene fatto lo shift di 2bit verso destra, quindi una divisione per 4.

Proseguiamo con il secondo blocco:

Codice:
00AF1710 | 0FB78454 A2000000         | movzx eax,word ptr ss:[esp+edx*2+A2]                                      |
00AF1718 | 0FB78C54 A0000000         | movzx ecx,word ptr ss:[esp+edx*2+A0]                                      |
00AF1720 | 03C8                      | add ecx,eax                                                               |
00AF1722 | 0FB78454 A6000000         | movzx eax,word ptr ss:[esp+edx*2+A6]                                      |
00AF172A | 03C8                      | add ecx,eax                                                               |
00AF172C | 0FB78454 A4000000         | movzx eax,word ptr ss:[esp+edx*2+A4]                                      |
00AF1734 | 03C8                      | add ecx,eax                                                               |
00AF1736 | C1E9 02                   | shr ecx,2                                                                 |

EDX veniva inizializzato nel blocco precedente a 5, ora sappiamo perchè. Vengono saltati i primi 5 caratteri; ovviamente, trattandosi di 2-byte per carattere, viene moltiplicato per 2 (ESI+EDX*2+A2 e gli altri). L'operazione è del tutto analoga alla precedente. Vengono sommati tra loro i caratteri del secondo chunk del seriale. Il risultato è ancora diviso per 4.

Il controllo procede al chunk successivo; quando ESI (primo chunk) è diverso da ECX (quello corrente) la verifica termina, e viene restituito "false", non superando il check sulla password.
A questo punto ho aggiornato il codice, e la versione finale è quella di seguito:

C:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <stdbool.h>

#define  USER_LEN   9
#define  PWD_LEN    19

const char alphabet[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";

int check_value(char *key, int len)
{
    int i = -1;
    int sum = 0;
  
    while(i++ < len)
        sum += (key[i] * (i+1));
  
    return sum & 0xFF;
}

bool is_valid(char *key, int len) {
    int esi = (key[0]+key[1]+key[2]+key[3]) >> 2;
  
    return  (esi == (key[5]+key[6]+key[7]+key[8]) >> 2) &&
            (esi == (key[10]+key[11]+key[12]+key[13]) >> 2) &&
            (esi == (key[15]+key[16]+key[17]+key[18]) >> 2);
}

int gen_password(char *key)
{
    do {
        for(int i=0; i<PWD_LEN; i++)
            key[i] = alphabet[rand() % 0x24]; // numbers and A-Z chars
      
        key[4]  = '-';
        key[9]  = '-';
        key[14] = '-';
    } while(!is_valid(key, PWD_LEN));
    return check_value(key, PWD_LEN);
}

int gen_username(char *user, int *len, int pwd_n) {
  
    while(true) {
        *len = rand() % 4 + 4;
      
        for(int i=0; i<*len; i++) {
            user[i] = alphabet[rand() % 0x3e]; // all chars
        }
      
        user[*len] = 0;
      
        int user_n = check_value(user, *len);
        if(user_n == pwd_n) return user_n;
    }
  
    return -1; // never reached (I hope :P )
}

int main() {
    srand(time(NULL));
  
    int userlen = 0;
  
    char username[USER_LEN] = {'\0'};
    char password[PWD_LEN]  = {'\0'};
  
    int pwd_n = gen_password(password);
    int usr_n = gen_username(username, &userlen, pwd_n);
  
    printf("Username: %s\tUser value: %X\n", username, usr_n);
    printf("Password: %s\tPwd value: %X\n", password, pwd_n);

    return 0;
}

Questi sono alcuni seriali generati:

Codice:
Username: z2l4  User value: F2
Password: 3VM1-ZB09-8KK6-4O0S   Pwd value: F2

Username: ttrL5Z        User value: 7
Password: Q8JG-FFV9-M9Z8-Y1DK   Pwd value: 7

Username: 9gVrr User value: B
Password: LJAT-1JUY-PIFI-PJ8Y   Pwd value: B

Username: d0TdLuh       User value: 62
Password: C9EJ-XG46-IE1K-MN59   Pwd value: 62

E questi sono gli screenshot dopo aver inserito alcuni dei codici mostrati qui sopra:

success1.png success2.png success3.png

5. Conclusione​


Anche questo articolo è giunto al termine! Spero sia stato interessante. Se avete domande, come sempre... ;)
 

aldinik

Nuovo Utente
47
3
Tutto molto affascinante, ho letto un po' di guide ma purtroppo ho un po' di difficoltà. Ho provato con un piccolo software che ho prelevato in internet ma non riesco ad andare avanti.
 
  • Mi piace
Reazioni: DispatchCode

DispatchCode

Moderatore
Staff Forum
Utente Èlite
2,207
1,844
CPU
Intel I9-10900KF 3.75GHz 10x 125W
Dissipatore
Gigabyte Aorus Waterforce X360 ARGB
Scheda Madre
Asus 1200 TUF Z590-Plus Gaming ATX DDR4
HDD
1TB NVMe PCI 3.0 x4, 1TB 7200rpm 64MB SATA3
RAM
DDR4 32GB 3600MHz CL18 ARGB
GPU
Nvidia RTX 3080 10GB DDR6
Audio
Integrata 7.1 HD audio
Monitor
LG 34GN850
PSU
Gigabyte P850PM
Case
Phanteks Enthoo Evolv X ARGB
Periferiche
MSI Vigor GK30, mouse Logitech
Net
FTTH Aruba, 1Gb (effettivi: ~950Mb / ~480Mb)
OS
Windows 10 64bit / OpenSUSE Tumbleweed
Tutto molto affascinante, ho letto un po' di guide ma purtroppo ho un po' di difficoltà. Ho provato con un piccolo software che ho prelevato in internet ma non riesco ad andare avanti.

Ciao, ti ringrazio!

Ovviamente se si tratta di software commerciali, non possiamo parlarne. Se hai però domande relative all'argomento, nel mio piccolo, ti risponderò volentieri.

Se hai scaricato un Crackme da internet e vuoi qualche dritta puoi aprire un topic invece.

Ps. se guardi nelle "Guide degli utenti" in questa sezione vedrai anche altri crackme.
Penso di proseguire con qualche altra pubblicazione prima o poi, sono solo in un periodo un po' full.
 

aldinik

Nuovo Utente
47
3
Carissimo, grazie per la risosta. Pensa che ho avuto difficoltà anche a seguire alcune guide con diverse tipologie di crackme ( serial, nag, trial, upx1 e file ) però ovviamente poi guardando e riguardando attentamente mi ritrovavo con i risultati attesi e ci mancherebbe c'è la guida. Solo che poi come vado per testare su altro e devo procedere completamente da solo mi pare tutto diverso e mi blocco quasi subito. Probabilmete ho bisogno di qualche guida un po' più per beginner. Quando avrai più tempo se non ti è di disturbo vorrei seguirti in maniera più approfondita. Se posso ti disturbo anche in pvt.
Buon notte
 

DispatchCode

Moderatore
Staff Forum
Utente Èlite
2,207
1,844
CPU
Intel I9-10900KF 3.75GHz 10x 125W
Dissipatore
Gigabyte Aorus Waterforce X360 ARGB
Scheda Madre
Asus 1200 TUF Z590-Plus Gaming ATX DDR4
HDD
1TB NVMe PCI 3.0 x4, 1TB 7200rpm 64MB SATA3
RAM
DDR4 32GB 3600MHz CL18 ARGB
GPU
Nvidia RTX 3080 10GB DDR6
Audio
Integrata 7.1 HD audio
Monitor
LG 34GN850
PSU
Gigabyte P850PM
Case
Phanteks Enthoo Evolv X ARGB
Periferiche
MSI Vigor GK30, mouse Logitech
Net
FTTH Aruba, 1Gb (effettivi: ~950Mb / ~480Mb)
OS
Windows 10 64bit / OpenSUSE Tumbleweed
Carissimo, grazie per la risosta. Pensa che ho avuto difficoltà anche a seguire alcune guide con diverse tipologie di crackme ( serial, nag, trial, upx1 e file ) però ovviamente poi guardando e riguardando attentamente mi ritrovavo con i risultati attesi e ci mancherebbe c'è la guida. Solo che poi come vado per testare su altro e devo procedere completamente da solo mi pare tutto diverso e mi blocco quasi subito. Probabilmete ho bisogno di qualche guida un po' più per beginner. Quando avrai più tempo se non ti è di disturbo vorrei seguirti in maniera più approfondita. Se posso ti disturbo anche in pvt.
Buon notte

Se hai un Crackme con una nag, linkamelo in pvt, appena posso me lo guardo e magari lo uso per un articolo. ?

Se mi scrivi in pvt ti rispondo, comunque puoi anche aprire un topic, magari può interessare altri e ne nasce un qualcosa di utile per altri; non sono attività illegali se non si va a violare una qualche licenza.
 

Entra

oppure Accedi utilizzando
Discord Ufficiale Entra ora!