Keygenner G*e*r*b*V*i*e*w v9.20

Introduction:


Dans ce tutoriel on va voir de l'aléatoire entre deux nombres, de la gestion de liste noire, et on va faire un peu de code ripping pour notre keygen.


Les outils


Pour appliquer ce tutoriel il vous faudra...




I: Démarrage:


On installe le programme et on l'exécute pour voir comment ça se passe.

1


Un nag-screen apparaît, on a 30 jours d'évaluation gratuits.
On clique sur le bouton 'I've already bought a license' la fenêtre d'enregistrement s'ouvre, on rentre n'importe quoi puis on valide.
Une messagebox apparaît pour nous notifier que notre sérial est incorrect, OK.


The serial number you've entered is not valid. Please check it again.

On scanne l'exécutable principal (gerbview.exe) avec DiE (Detect It Easy) pour voir s'il est compressé, on ne dirait pas, super.




II: Trouver la routine d'enregistrement


Chargez l'application dans le débogueur.
On va regarder en premier les strings pour se faire une idée de ce fichier:
Clic droit>Search for>All referenced strings


1


Ctrl+F, ou bien: clique droit>Search for text
On rentre "serial number" dans le champ de recherche, on coche "Ignore case" puis on valide, on tombe ici:


1


On double-clique sur le premier string "Serial number" qui nous amène sur sa position: 012C9261.
Ensuite on remonte un peut cette procédure jusqu'a atteindre sont début, puis on y place un breakpoint.
Après on appuie sur F9 pour lancer le programme, ensuite on rouvre la fenêtre d'enregistrement et on essaye de valider un sérial erroné.
Le débogueur break immédiatement.


1


Il n'y a plus qu'a tracer pour voir ce qu'il se passe, allez hop!


III: Comprendre la routine d'enregistrement


On trace tranquillement à coup de F8 comme d'habitude.
On passe sur un appel d'API de mfc100u, puis un autre, on remarque que c'est des appels via ordinal.
Après le deuxième appel, ça semble passé notre sérial en argument dans une sous-routine, puis un peu plus bas on a un saut conditionnel.
Allons voir avec F7 quel est le rôle de cette routine.


1



III.a: Construction du serial


Une fois dans la routine, on continue de tracer avec F8.
Le programme copie notre sérial dans un buffer.


1


On continue, le contenu du sérial dans le buffer passe en majuscule avec wcsupr.
Puis le string du buffer est transmis dans une sous-routine, allons-y jeter un oeil.


1


Cette sous-routine récupère la longueur de notre sérial.
Ensuite on a une vérification de longueur, si la taille est de zéro on sort, sinon on continue
Puis on arrive dans une boucle qui vérifie que le dernier caractère de notre sérial n'est pas un espace.
Si oui, le programme enlève l'espace, et reboucle pour vérifier qu'il n'y en a pas un autre.
Une fois cette fonction "anti-espace" passée, on sort de la routine.


1


Une fois de retour, on arrive sur de multiples comparaisons de caractères.
En premier on a MOVZX EDX, WORD PTR SS:[local.48]. Cela place l'adresse de notre buffer sérial dans EDX.
Sur la ligne suivante, on compare la première lettre du buffer contre 47. (C'est de l'hexa, la lettre: G)
Si c'est bon, on continue, sinon le saut conditionnel nous pousse vers la sortie.
On va forcer le flag dans la fenêtre des registres pour continuer de parcourir cette partie et voir ce qu'il demande d'autre.


1



Tout de suite après on a un autre MOVZX mais avec EAX, toujours avec un pointeur sur notre sérial mais avec [local.48]+2.
+2 et non pas +1 pour le caractère suivant, car notre string est en unicode!.
Cette fois-ci il regarde si la seconde lettre correspond a 'B'.
Ensuite il regarde si la troisième lettre correspond a '5'.
Puis si la quatrième lettre du sérial contient un tiret '-'.
Puis si la septième lettre du sérial contient un tiret '-'.
Puis si la treizième lettre du sérial contient un tiret '-'.

Une fois toutes ses vérifications effectuées, la longueur de notre sérial est encore récupérée puis comparée à 15 (21 en décimal)


1


Après ça, les passes de validité de la structure du sérial sont terminées.
On sait maintenant que notre sérial doit avoir la forme: GB5-XX-YYYYY-ZZZZZZZZ


III.b: L'algorithme


Le JAE nous envoi directement dans une sous-procédure, pas le temps de niaiser!
Cette procédure est une boucle qui crée une 'table' de 1024 bytes, elle servira pour plus tard dans la routine.
en fin de boucle on remarquera: MOV DWORD PTR DS:[EAX*4+7BBA50],ECX
7BBA50 étant l'offset ou démarre la création de la table, et EAX le compteur qui lui permet de se déplacer dedans pour écrire au fur et à mesure.

1


On va juste retenir 7BBA50 dans un coin de notre tête.


1


On sort de cette sous-routine puis on continue de tracer et on arrive sur la vérification de la première partie de notre sérial 'XX'
La routine s'attend a rencontrer un décimal supérieur ou égale à 128 (0x80 en hex), contre 'XX' stocké dans EDX.
'XX' n'étant pas dans la table hexadécimale, EDX contiendra zéro.
Un exemple de première partie valide pourrait être: GB5-FF-YYYYY-ZZZZZZZZ ou bien simplement GB5-80-YYYYY-ZZZZZZZZ


1


On continue de tracer, et on arrive sur la vérification de 'YYYYY'.
Alors ici pour passer la vérification on doit être supérieur ou égal à 10000 (0x2710 en hex).
Contrairement à la première partie, on n'a pas l'utilisation de wcstoul, donc pas d'hex dans cette partie du sérial, le programme attend du décimal.
Un exemple de deuxième partie valide pourrait être: GB5-FF-99999-ZZZZZZZZ
Ou bien simplement GB5-80-10000-ZZZZZZZZ


1


Ensuite notre deuxième partie est comparée à une blacklist.
Si notre numéro correspond à ceux de cette liste, on est éjecté:

10218, 10224, 11297, 11396, 11597, 20255, 65205, 65619, 65620, 66563, 66564, 97387

Juste après on observe une sous-procédure avec trois arguments, dont notre sérial en Arg2, ainsi que 0x0C (12) en hard pour Arg3.
Rentrons dans cette routine.


1


Cette routine traite individuellement les 12 premiers caractères du sérial pour calculer la dernière partie.
On a des opérations bit à bit comme AND, XOR, et du décalage droite/gauche avec SHR, SHL.
Le résultat influe sur la position dans la table, qui donnera notre sérial final.


1


Une fois sorti de cette procédure, on retrouve un peu plus bas wcstoul, donc de l'hex.
Puis une comparaison entre notre dernière partie de notre sérial et le résultat du dernier calcul.
Si ce n'est pas bon, on ne prend pas le jump, eax passe a zéro via un xor et on sort.


1


Maintenant de retour dans notre procédure du début, on comprend que si notre sérial était correct ce jump aurait été pris.


1


IV: Faire un Keygen


Il n'y a plus qu'a.
On n’a pas trainé sur le fonctionnement de la génération de la table, on va juste riper la routine ainsi que la dernière partie pour la génération du troisième code.
La dernière fois, sur mon tutoriel du keygenning m*I*R*C j'avais utilisé asm2clipboard pour faire ça, pour changer on va utiliser IDA.
C'est relativement simple, ouvrer gerbview.exe dans IDA puis depuis le menu: File > Produce file > Create ASM file.
Ou bien simplement ALT+F10.
Ensuite il n'y a plus qu'à copier la partie qui nous intéresse dans notre keygen et faire les raccords.


keygen.asm:

.486
.model flat, stdcall
option casemap :none ; case sensitive

include        \masm32\include\windows.inc
include        \masm32\include\user32.inc
include        \masm32\include\kernel32.inc
include        \masm32\macros\macros.asm

includelib     \masm32\lib\user32.lib
includelib     \masm32\lib\kernel32.lib

DlgProc        PROTO :DWORD,:DWORD,:DWORD,:DWORD

.const
; Dialog members
IDB_GEN                 equ 1002
IDB_QUIT                equ 1003
IDC_SERIAL              equ 1004
IDC_DATE_STATIC         equ 1005

; Constants
LIMIT_LOW_Y             equ 10000
LIMIT_HIGH_Y            equ 99999
MASK_Y                  equ 00001FFFFh
OFFSET_BUFFER_Y         equ 4
CHUNK_SIZE_Y            equ 5

LIMIT_LOW_X             equ 128
LIMIT_HIGH_X            equ 255
MASK_X                  equ 00000007Fh
OFFSET_CODE_X           equ 0
OFFSET_BUFFER_X         equ 6
CHUNK_SIZE_X            equ 2

Destination             equ word ptr -0C0h

; Initialized data section
.data
; App detail
szSerialDialog          db "Serial Number",0
szClassSerial           db "#32770",0

; Dialog details
szTitle                 db "Software Companions GerbView v9.20 *keygen*",0
szIDBExit               db "cLOSE!",0
szIDBGen                db "gENERATE!",0
szDateStatic            db "o9/11/2021",0


hexadecimal_digits      db "0123456789ABCDEF",0
Forbidden_Numbers       DWORD 10218, 10224, 11297, 11396,
                              11597, 20255, 65205, 65619,
                              65620, 66563, 66564, 97387,
                              (-1)

SerialHardPart          db "GB5-",0


; Uninitialized data section
.data?
Buffer                  db 20 dup (?)
hInstance               dd ?
timeData                dd ?
windhand                dd ? ; Window handle of the registration dlg
szserialtemp            db 50 dup (?)

HexBuffer               db 20 dup (?)
DecBuffer               db 10 dup (?)

RandomHexFinal          db 20 dup (?)
RandomDecFinal          db 10 dup (?)

seedY                   dd ?
seedX                   dd ?

presentY                dd ?
presentX                dd ?

valueY                  dd ?
valueX                  dd ?

dword_7EBA50            dd ?

; Program code section
.code
start:
    ; This function is the entrypoint of the program. 
    invoke GetModuleHandle,NULL
    mov hInstance,eax
    invoke DialogBoxParam,hInstance,101,0,addr DlgProc,0
    invoke ExitProcess,eax

CopyChunk proc chunkSize:DWORD, destination:DWORD, source:DWORD
    ; This procedure copies a chunk of data of given size from a source
    ; in memory to a destination.
    push esi
    push edi
    mov esi,source
    mov edi,destination
    mov ecx,chunkSize
CopyChunk_Loop:
    cmp ecx,0
    jz CopyChunk_End
    sub ecx,1
    mov al,[esi]
    mov [edi],al
    add esi,1
    add edi,1
    jmp CopyChunk_Loop
CopyChunk_End:
CopyChunk_return:
    pop edi
    pop esi
    ret
CopyChunk endp

WriteDecimal proc integer:DWORD, buffer:DWORD
             LOCAL numberTen:DWORD
    ; This procedure produces a string with the decimal representation of
    ; an unsigned 32-bit integer into the provided buffer, padded with zeros
    mov [numberTen], 10
    mov ecx,buffer
    mov al,'0'
    mov edx,9
WriteDecimal_ForwardLoop:
    cmp edx,0
    jz WriteDecimal_ForwardEnd
    sub edx,1
    mov [ecx],al
    add ecx,1
    jmp WriteDecimal_ForwardLoop
WriteDecimal_ForwardEnd:
    xor al,al
    mov [ecx],al
    mov eax,integer
WriteDecimal_BackwardLoop:
    sub ecx,1
    xor edx, edx
    div numberTen
    add dl,'0'
    mov [ecx],dl
    cmp eax,0
    jnz WriteDecimal_BackwardLoop
WriteDecimal_BackwardEnd:
WriteDecimal_return:
    ret
WriteDecimal endp

WriteHex proc integer:DWORD, buffer:DWORD
    ; This procedure produces a string with the hex representation of an
    ; unsigned 32-bit integer into the provided buffer, padded with zeros 
    mov ecx,buffer
    mov al,[hexadecimal_digits]
    mov edx,8
WriteHex_ForwardLoop:
    cmp edx,0
    jz WriteHex_ForwardEnd
    sub edx,1
    mov [ecx],al
    add ecx,1
    jmp WriteHex_ForwardLoop
WriteHex_ForwardEnd:
    xor al,al
    mov [ecx],al
    mov eax,integer
    push ebx
    lea ebx,[hexadecimal_digits]
WriteHex_BackwardLoop:
    sub ecx,1
    mov edx,eax
    and edx,00Fh
    shr eax,4
    mov dl,[ebx+edx]
    mov [ecx],dl
    cmp eax,0
    jnz WriteHex_BackwardLoop
WriteHex_BackwardEnd:
    pop ebx
WriteHex_return:
    ret
WriteHex endp

NextRandomX proc
; This procedure calculate the first part of the serial
    mov edx, seedX
NextRandomX_Loop:
    add presentX, edx
    mov eax, presentX
    and eax, MASK_X
    add eax, LIMIT_LOW_X
    cmp eax, LIMIT_HIGH_X
    ja NextRandomX_Loop
NextRandomX_Return:
    mov [valueX], eax
    ret
NextRandomX endp

NextRandomY proc
    ; This procedure calculates the second part of the serial
    ; And check it against a list of forbidden numbers.
    mov edx,seedY
NextRandomY_Loop:
    add presentY,edx
    mov eax,presentY
    and eax,MASK_Y
    add eax,LIMIT_LOW_Y
    cmp eax,LIMIT_HIGH_Y
    ja NextRandomY_Loop
    lea ecx,[Forbidden_Numbers]
NextRandomY_ForbiddenLoop:
    mov edx,[ecx]
    cmp edx,0
    jl NextRandomY_ForbiddenEnd
    cmp edx,eax
    jz NextRandomY_ForbiddenLoop_Reset
    add ecx,4
    jmp NextRandomY_ForbiddenLoop
NextRandomY_ForbiddenLoop_Reset:
    mov edx,seedY
    jmp NextRandomY_Loop
NextRandomY_ForbiddenEnd:
NextRandomY_End:
NextRandomY_return:
    mov [valueY],eax
    ret
NextRandomY endp

DlgProc proc    hWin    :DWORD,
                uMsg    :DWORD,
                wParam  :DWORD,
                lParam  :DWORD

    .if uMsg == WM_INITDIALOG
        ; Set the dialog controls texts. Done here in the code instead of resource
        ; file to reduce the required bytes (strings in the rc file are UNICODE not ANSI)
        invoke SetWindowText,hWin,addr szTitle ; Set the window title text
        invoke SetDlgItemText,hWin,IDB_GEN,addr szIDBGen
        invoke SetDlgItemText,hWin,IDB_QUIT,addr szIDBExit
        invoke SetDlgItemText,hWin,IDC_DATE_STATIC,addr szDateStatic
        ; Initialize the random seeds
        invoke GetSystemTimeAsFileTime,addr timeData
        lea eax,[timeData]
        mov eax,[eax]
        or eax,1
        mov seedX,eax
        ror eax,8
        or eax,1
        mov seedY,eax
        ; Initialize the present data for X and Y
        xor eax,eax
        mov presentX,eax
        mov presentY,eax
    .elseif uMsg == WM_COMMAND
        .if wParam == IDB_GEN

            invoke lstrcat,addr szserialtemp,addr SerialHardPart
            INVOKE NextRandomX
            INVOKE WriteHex, valueX, ADDR HexBuffer
            INVOKE CopyChunk, CHUNK_SIZE_X, ADDR RandomHexFinal, ADDR HexBuffer + OFFSET_BUFFER_X
            invoke lstrcat,addr szserialtemp,addr RandomHexFinal
            invoke lstrcat,addr szserialtemp,chr$('-')
            ; This is generating a number,
            ; In this example it can be any number superior to 10000 and inferior to 99999.
            ; The number is then checked against a list of forbidden numbers.
            ; This should output a 5 chars long result like: 46257
            invoke NextRandomY
            invoke WriteDecimal,valueY,addr DecBuffer
            invoke CopyChunk,CHUNK_SIZE_Y,addr RandomDecFinal,addr DecBuffer+OFFSET_BUFFER_Y
            invoke lstrcat,addr szserialtemp,addr RandomDecFinal
            invoke lstrcat,addr szserialtemp,chr$('-')
            ; We generate the table.
            call sub_6394B0 
            ; We generate the last part.
            push 0Ch
            lea eax, [szserialtemp]
            push eax
            push 0
            call sub_639530
            ; Retrieve EAX.
            invoke WriteHex,EAX,addr Buffer
            invoke lstrcat,addr szserialtemp,addr Buffer
            ; Send serial to app
            invoke FindWindow,addr szClassSerial,addr szSerialDialog
            .if eax
                mov windhand, eax
                invoke SendDlgItemMessageA,windhand,1140,WM_SETTEXT,0,addr szserialtemp
            .endif
            invoke SetDlgItemTextA,hWin,IDC_SERIAL,addr szserialtemp
            invoke RtlZeroMemory,addr szserialtemp,sizeof szserialtemp
            invoke RtlZeroMemory,addr Buffer,sizeof Buffer
          
        .elseif wParam == IDB_QUIT
            invoke EndDialog,hWin,0
        .endif
    .elseif uMsg == WM_CLOSE
        invoke EndDialog,hWin,0
    .endif
    xor eax,eax
    ret
DlgProc endp

sub_6394B0 proc near
; This procedure generate the table
var_C= dword ptr -0Ch
var_8= dword ptr -8
var_4= dword ptr -4

push    ebp
mov     ebp, esp
sub     esp, 0Ch
mov     [ebp+var_4], 0
jmp     short loc_6394C8

loc_6394BF:
mov     eax, [ebp+var_4]
add     eax, 1
mov     [ebp+var_4], eax

loc_6394C8:
cmp     [ebp+var_4], 100h
jge     short loc_639525
mov     ecx, [ebp+var_4]
shl     ecx, 18h
mov     [ebp+var_8], ecx
mov     [ebp+var_C], 0
jmp     short loc_6394EC

loc_6394E3:
mov     edx, [ebp+var_C]
add     edx, 1
mov     [ebp+var_C], edx

loc_6394EC:
cmp     [ebp+var_C], 8
jge     short loc_639516
mov     eax, [ebp+var_8]
and     eax, 80000000h
jz      short loc_63950C
mov     ecx, [ebp+var_8]
shl     ecx, 1
xor     ecx, 4C11DB7h
mov     [ebp+var_8], ecx
jmp     short loc_639514

loc_63950C:
mov     edx, [ebp+var_8]
shl     edx, 1
mov     [ebp+var_8], edx

loc_639514:
jmp     short loc_6394E3

loc_639516:
mov     eax, [ebp+var_4]
mov     ecx, [ebp+var_8]
mov     dword_7EBA50[eax*4], ecx
jmp     short loc_6394BF

loc_639525:
mov     esp, ebp
pop     ebp
retn
sub_6394B0 endp

sub_639530 proc near
; This procedure calculates the third (last) part of the serial
var_8= dword ptr -8
var_4= dword ptr -4
arg_0= dword ptr  8
arg_4= dword ptr  0Ch
arg_8= dword ptr  10h

push    ebp
mov     ebp, esp
sub     esp, 8
mov     [ebp+var_8], 0
jmp     short loc_639548

loc_63953F:
mov     eax, [ebp+var_8]
add     eax, 1
mov     [ebp+var_8], eax

loc_639548:
mov     ecx, [ebp+var_8]
cmp     ecx, [ebp+arg_8]
jge     short loc_639585
mov     edx, [ebp+arg_0]
shr     edx, 18h
mov     eax, [ebp+arg_4]
movzx   ecx, word ptr [eax]
xor     edx, ecx
and     edx, 0FFh
mov     [ebp+var_4], edx
mov     edx, [ebp+arg_4]
add     edx, 1
mov     [ebp+arg_4], edx
mov     eax, [ebp+arg_0]
shl     eax, 8
mov     ecx, [ebp+var_4]
xor     eax, dword_7EBA50[ecx*4]
mov     [ebp+arg_0], eax
jmp     short loc_63953F

loc_639585:
mov     eax, [ebp+arg_0]
mov     esp, ebp
pop     ebp
retn
sub_639530 endp

end start

keygendlg.rc:

;This Resource Script was generated by WinAsm Studio.

#define IDB_GEN 1002
#define IDB_QUIT 1003
#define IDC_SERIAL 1004
#define IDC_DATE_STATIC 1005

101 DIALOGEX 10,10,184,43
FONT 8,"MS Shell Dlg"
STYLE 0x80c80880
EXSTYLE 0x00000000
BEGIN
    CONTROL "",IDB_QUIT,"Button",0x10010000,127,28,51,13,0x00000000
    CONTROL "",IDC_SERIAL,"Edit",0x50010801,3,9,177,12,0x00000200
    CONTROL "",IDB_GEN,"Button",0x10010000,70,28,51,13,0x00000000
    CONTROL "",IDC_DATE_STATIC,"Static",0x58000000,3,34,44,9,0x00000000
END

make.bat:

@echo off
\masm32\bin\rc /v keygendlg.rc
\masm32\bin\ml.exe /c /coff /Cp /nologo keygen.asm
\masm32\bin\link.exe /SUBSYSTEM:WINDOWS /RELEASE /VERSION:4.0 /OUT:keygen.exe keygen.obj keygendlg.res
del keygendlg.res
del keygen.obj
pause

Une fois compilé, ça ressemble à ça:

1



On test une clé générée.


1


Et voilà c'est fini !




Xylitol, o9/11/2021




Copyright (C)- xtx Team (2021)

XHTML valide 1.1 CSS Valide !