Keygenner m*I*R*C v7.64

Introduction:


J'ai eu l'idée de faire ce tuto suite au PDF de Horgh sur le serial-fishing de la version v6.35 qui date de 2008, donc on va refaire ça, mais sur la version actuel v7.64 (Décembre 2020)
Je ne rentrerais pas dans les détails du patchage du web check, car c'est déjà bien assez long comme ça, donc je vous laisse vous débrouiller pour ça. Faites-en un tuto!
Néanmoins si vous bloquez, jeter un coup d'oeil à ma source.


Les outils:



I: Démarrage:


On scanne l'appli avec PEiD pour regarder si elle est compressée, etc...
Ce n'est pas le cas. ok, impec.


1

On regarde un coup KANAL, sans trop de prétention vu que l'appli est un client irc, et qu'elle embarque probablement des libs crypto pour gérer le SSL, etc...


1

99 signatures de crypto trouver, effectivement ça fait beaucoup.
On verra bien.


II: Trouver la routine d'enregistrement:


On charge l'application dans notre débogueur, puis on la lance directement avec F9.
Une fois lancée, on va dans la boite de dialogue pour rentrer son numéro de série, on met n'importe quoi puis on valide.


1

On retourne sur le débogueur puis on le met en pause avec F12 ou bien depuis le bouton du menu.
Ensuite on presse ALT+F9 (Execute till user code) puis on retourne sur m*I*R*C pour cliquer sur le bouton du dialogue 'mauvais sérial'


1

Le débogueur break.
Nous sommes maintenant juste en dessous de USER32.DialogBoxParamW


1

Il ne reste plus qu'à remonter au début de la procédure pour essayer de savoir d'où l'on vient
On remonte jusqu'au PUSH EBP, on regarde la fenêtre de dump, et la... Il y a beaucoup trop de monde qui appelle cette procédure.


1

On va donc descendre pour mieux remonter, continuons à tracer en F8 jusqu'à la fin de la procédure, une fois sur l'instruction de retour atteint, on verra bien ou on arrive.
Et on débarque en 0052CD41, juste en dessous de notre appel


1

On va maintenant remonter cette procédure depuis le début.
On croise notre sérial en remontant au passage, d'où vient-il ?
On va changer de méthodologie du coup, à la place de remonter les procédures on va tenter de tracer le sérial. Dans la fenêtre de dump, aller à l'offset du PUSH qui contient notre sérial puis faire un clic-droit breakpoint > Hardware (ou bien Shift+F5)


1

On va laisser le breakpoint en lecture/écriture, et juste sur le 1er byte, (voir si ça donne quelque chose.)


1

On va relancer l'exécution et entrer un sérial bidon, voilà on break dans comctl32, maintenant on désactive notre breakpoint hardware, ALT+H pour ouvrir la fenêtre, sélectionner le breakpoint, clique droit puis 'disable'
Cela évitera que l'on rebreak dessus de manière non désirée, on le réactivera si besoin.


1

Maintenant on tapote F8 pour faire du step over et sortir de comctl32 jusqu'à ce que l'on retourne dans le code de l'exécutable principal.
On arrive finalement en 0052D539, qui apparemment a réécrit notre mémoire contenant le bon sérial par notre faux sérial ('1337' dans mon cas)


1

Le bon sérial n'est peut être pas encore calculé on va donc descendre tranquillement cette procédure.
On pourrait aussi remettre notre breakpoint hardware et continuer a breaker jusqu'à ce que le sérial apparaisse, mais bon...
Donc on continue de tapoté F8, et on observe, notre sérial apparaitra pour la première fois un peu plus loin juste après avoir traversé une procédure.


1

Allons voir l'intérieur de cette procédure avec clic droit>Follow


1

On survol rapidement la procédure, on observe des boucles au début, puis vers la fin un string "%ld-%ld" suivit d'un call, surement pour faire du formatage du sérial, ça a l'air d'être ce que l'on cherchait.
On pose un breakpoint au début en 0052AAA0 et on va pouvoir commencer à tracer ça.


III: Comprendre la routine d'enregistrement:


Maintenant qu'on a notre breakpoint posé au début, on relance avec F9 puis on valide à nouveau notre sérial bidon, le débogueur break.
On continue à tracer avec F8 en regardant ce qu'il se passe, arrivé en 0052AC00, les calculs commencent.


1

Petit rappel pour vous dire que tous les calculs que l'on va faire ici sont à faire en hexadécimal.

MOVZX ESI,WORD PTR DS:[EDX*2+EDI]
Notre nom (dans mon cas "Xylitol") et contenu dans EDI qui pointe en 0136D24C
EDX contient "3" qui a été passé via l'instruction MOV EDX,3 juste avant la boucle.
Le programme fait donc: 3 (edx) multiplier par 2 = 6 + l'adresse de EDI de mon string 0136D24C = 136D252 qui correspond à la lettre 'i' au niveau de la position dans mon string data en 0136D24C
Cette ligne place donc dans ESI la valeur: 69 (la lettre 'i')

IMUL ESI,DWORD PTR SS:[ECX*4+ESP+14]
ESP pointe en 0136C060, c'est un pointeur pour notre sérial (dans mon cas "1337".) Il additionne + 14 a 0136C060 ce qui donne le résultat: 136C074. Une adresse de pointeur qui pointe sur une table en hard utilisée pendant la boucle.
Pour notre première boucle, ECX et pour le moment initialisé à zéro, il fait donc 0 multiplié par 4 = 0
On garde donc 136C074 comme résultat, qui est l’adresse de "0B" dans la table en hard.


1

ESI (69) multiplié par 0B (le caractère en position 136C074 dans la table)
Le contenu de ESI après opération est maintenant de: 483

INC ECX
Le contenu de ECX qui était à zéro jusqu'à présent est incrémenté de 1, dans cette boucle, ECX sert à se déplacer dans la table pour la prochaine multiplication.
Rappelez vous l'instruction précédente, avec [ECX*4+TABLE] en gros, on utilise la table du 4 pour se déplacer dans la table en hard.

ADD EBP,ESI
On additionne dans EBP: EBP+ESI
C'est notre première boucle, donc pour le moment EBP est à zéro, 0+483 = 483

CMP ECX,26
Compare ECX avec 26 (rappel que c'est de l'hexa, donc 38 en décimale)
Ici cette instruction sert à savoir si on va être en dehors de la table (la table contenant 39 caractères)

JLE SHORT 0052AC13
Si c'est bon, on saute en 0052AC13 (INC EDX)

XOR ECX,ECX
Si ce n’est pas bon, on arrive sur cette instruction qui réinitialise ECX à zéro, car on a atteint la fin de la table.

INC EDX
On incrémente de 1 EDX, qui a donc maintenant une valeur de 4

CMP EDX,EAX
On compare 4 avec EAX qui contient 7 (la longueur de mon nom)

JL SHORT 0052AC00
Si notre compteur a atteint 7, on a fini la boucle, sinon on reboucle.

Le résultat final est donc stocké dans EBP (dans mon cas: 13AA, qui correspond à 5034 en décimale)
La première partie de sérial est calculée.

La deuxième boucle de calcul et assez similaire.


1

MOVZX ESI,WORD PTR DS:[ECX*2+EDI-2]
EDI pointe toujours en 0136D24C qui est un pointeur sur mon nom 'Xylitol'
ECX contient "3" qui a été passé via l'instruction MOV ECX,3 avant cette boucle.
on fait donc: 3*2 = 6 + 0136D24C - 2 = 136D250
A l'adresse 136D250 on est sur la lettre 'l' dans la position de la chaine de mon nom.
on place donc dans ESI: 6C

MOVZX EBP,WORD PTR DS:[ECX*2+EDI]
Ici on fait ecx (3) multiplier par 2 = 6 + EDI (0136D24C, toujours notre nom) = 136D252
En 136D252 on est sur la lettre 'i' dans la position de la chaine de mon nom.
On place donc dans EBP: 69

IMUL ESI,EBP
On multiplie dans ESI: ESI*EBP, c'est à dire dans notre première boucle: 6C*69 = 2C4C

IMUL ESI,DWORD PTR SS:[EDX*4+ESP+14]
Rappelez vous précédemment, on l'a déjà vu [EDX*4+ESP+14] sert à choper notre position dans la table en hard, on est au début de la table en 136C074 (le caractère 0B)
On multiplie ESI par 0B, cela donne: 1E744

INC EDX
Incrémente EDX de +1 pour se déplacer au prochain caractère dans la table la prochaine fois.

ADD EBX,ESI
On additionne dans EBX: EBX+ESI
C'est notre première boucle, donc pour le moment EBX est à zéro, 0+1E744 = 1E744

CMP EDX,26
Comparaison pour voir si on est en dehors de la table.

XOR EDX,EDX
Si ce n’est pas bon, on arrive sur cette instruction qui réinitialise EDX à zéro, car on a atteint la fin de la table.

INC ECX
Si c'est bon, on ajoute +1 dans ECX, ce qui fait ECX = 4

CMP ECX,EAX
On compare 4 avec EAX qui contient 7 (la longueur de notre nom)

JL SHORT 0052AC40
Si ECX contient la même longueur que notre nom, alors on a fini la boucle, sinon on continue a boucler.


Le résultat final est donc stocké dans EBX (pour mon cas: 88DB8, en décimale: 560568)
Le calcul du sérial est maintenant terminé.
Plus bas, le programme prend le contenu de EBX, ainsi que EBP, puis assemble le sérial en décimal.


1

Ce qui donne pour moi: 5034-560568

Maintenant il n'y a plus qu'à coder le keygen !


IV: Faire un keygen:


Vu que cet algorithme et rudimentaire j'ai opté pour l'asm.
J'ai aussi utiliser le plugin Asm2Clipboard pour récupérer facilement les deux routines depuis Olly.

kg.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\include\masm32.inc
include         \masm32\macros\macros.asm

includelib      \masm32\lib\user32.lib
includelib      \masm32\lib\kernel32.lib
includelib      \masm32\lib\masm32.lib
include         crc32.inc

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

patch MACRO offsetAdr,_bytes,_byteSize
 invoke SetFilePointer,hTarget,offsetAdr,NULL,FILE_BEGIN
   .if eax==0FFFFFFFFh
     invoke CloseHandle,hTarget
     invoke SetDlgItemText,hWin,IDC_SERIAL,addr szFileBusy
     ret
   .endif
 invoke WriteFile,hTarget,addr _bytes,_byteSize,addr BytesWritten,FALSE
ENDM

.const
IDC_NAME        equ 1001
IDC_SERIAL      equ 1002
IDC_CHECKBOX    equ 1003
IDB_CANCEL      equ 1004
IDB_PATCH       equ 1005
IDC_GROUPBOX    equ 1006

.data
; Keygen details
szTable         db  0Bh,06h,11h,0Ch,0Ch,0Eh,05h,0Ch,10h
                db  0Ah,0Bh,06h,0Eh,0Eh,04h,0Bh,06h,0Eh
                db  0Eh,04h,0Bh,09h,0Ch,0Bh,0Ah,08h,0Ah
                db  0Ah,10h,08h,04h,06h,0Ah,0Ch,010h,08h
                db  0Ah,04h,010h,0
szFormat        db  "%d-%d",0

; App detail
szSerialDialog  db "mIRC Registration",0

; Dialog details
szEnterName     db  "Entre un nom",0
szTooLong       db  "Trop long",0
szMinCharsPlz   db  "4 caractères minimum",0
sziNFO          db  "PATCH FIRST! THEN USE KG!",0
szTitle         db  "mIRC v7.64 kg + patch",0
szGroupBox      db  "nAME ^ sERIAL",0
szIDBBackup     db  "bCK",0
szIDBExit       db  "eXiT",0
szIDBPatch      db  "pATCH",0
szNotFound      db  "mirc.exe non trouvé",0
szWrongSize     db  "Mauvaise taille",0
szBadCRC32      db  "CRC32 incorrecte!",0
szFileBusy      db  "Fichier ouvert?",0
szSucess        db  "PATCHÉ !",0

; Patch details
TargetName      db  "mirc.exe",0
BackupName      db  "mirc.exe.backup",0
TargetCRC32  	dd  7EB0130Eh ; CRC32 of mirc.exe
TargetSize      dd  5833992 ; File size of mirc.exe

WBuffer1        db  0EBh ;JZ SHORT 004795F9 -> JMP SHORT 004795F9
PatchOffset1	dd  000789D2h ;004795D2
WBuffer2        db  0E9h,0AAh,00h,00h,00h,90h ;JNE 0052D6E1 -> JMP 0052D6E1
PatchOffset2	dd  0012CA32h ;0052D632 
WBuffer3        db  090h,090h,090h,090h,090h,090h ;JZ 0052BAC2 -> NOP
PatchOffset3	dd  0012ADD4h ;0052B9D4
WBuffer4        db  090h,090h,090h,090h,090h,090h ;JNZ 0052BAC2 -> NOP
PatchOffset4	dd  0012AE0Dh ;0052BA0D
WBuffer5        db  090h,090h ;JZ SHORT 0052BA45 -> NOP
PatchOffset5	dd  0012AE2Ah ;0052BA2A
WBuffer6        db  090h,090h ;JNZ SHORT 0052BAC2 -> NOP
PatchOffset6	dd  0012AE43h ;0052BA43
WBuffer7        db  090h,090h ;JZ SHORT 0052BAC2 -> NOP
PatchOffset7	dd  0012AEB6h ;0052BAB6

.data?
hInstance       dd      ? ;dd can be written as dword
szSize          DWORD   ?
szName          db      100h dup(?)
szSerial        db      100h dup(?)
hTarget         HINSTANCE ?
BytesWritten	db      ?
windhand        dd      ? ; Window handle of mirc

.code
start:
	invoke	GetModuleHandle, NULL
	mov	hInstance, eax
	invoke	DialogBoxParam, hInstance, 101, 0, ADDR DlgProc, 0
	invoke	ExitProcess, eax

DlgProc	proc hWin :DWORD,
       uMsg  :DWORD,
       wParam  :DWORD,
       lParam  :DWORD
LOCAL ff32:WIN32_FIND_DATA
LOCAL pFileMem: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,IDC_GROUPBOX,ADDR szGroupBox
    invoke SetDlgItemText,hWin,IDC_CHECKBOX,ADDR szIDBBackup
    invoke SetDlgItemText,hWin,IDB_CANCEL,ADDR szIDBExit
    invoke SetDlgItemText,hWin,IDB_PATCH,ADDR szIDBPatch
    ; Display patch info in IDC_LISTBOX
    invoke SetDlgItemText,hWin,IDC_SERIAL,addr sziNFO
    ; Init CRC32 table
    call InitCRC32Table
	; Check IDC_CHECKBOX for file backup
	invoke SendDlgItemMessage,hWin,IDC_CHECKBOX,BM_SETCHECK,1,0
	.elseif	uMsg == WM_COMMAND
        mov eax,wParam
        mov edx,eax
        shr edx,16
        and eax,0FFFFh
          .if edx == EN_CHANGE
              .if eax == IDC_NAME
              ;Keygen procedure start here
                invoke GetDlgItemText,hWin,IDC_NAME,addr szName,sizeof szName
                invoke lstrlen, ADDR szName
                mov szSize, eax
                .if szSize == 0
                    invoke SetDlgItemText,hWin,IDC_SERIAL,addr szEnterName
                .elseif eax > 100
                    invoke SetDlgItemText,hWin,IDC_SERIAL,addr szTooLong
                .elseif eax < 4
                    invoke SetDlgItemText,hWin,IDC_SERIAL,addr szMinCharsPlz
                .elseif
                xor ebx,ebx
                xor edx,edx
                push ebp ; hWin
                mov ecx,3
                ;First loop for first serial part
                L000:
                  MOVZX ESI,byte PTR DS:[szName+ecx]
                  mov al,byte ptr [szTable+ecx-3]
                  movzx eax,al
                  IMUL ESI,eax
                  INC ECX
                  ADD EBX,ESI
                  CMP ECX,26h
                  JLE L007
                  XOR ECX,ECX
                L007:
                  INC EDX
                  CMP EDX,szSize
                  JL L000
                mov eax,ebx
                push eax
                xor ebx,ebx
                mov ecx,3
                ;Second loop for second serial part
                L008:
                  MOVZX ESI,byte PTR DS:[szName+ecx-1]
                  MOVZX EBP,byte PTR DS:[szName+ecx]
                  IMUL ESI,EBP
                  mov al,byte ptr [szTable+ecx-3]
                  movzx eax,al ;
                  IMUL ESI,eax
                  INC EDX
                  ADD EBX,ESI
                  CMP EDX,26h
                  JLE L009
                  XOR EDX,EDX
                L009:
                  INC ECX
                  CMP ECX,szSize
                  JL L008
                pop eax
                pop ebp ; hWin
                invoke wsprintf,addr szSerial,addr szFormat, eax, ebx
                invoke SetDlgItemText,hWin,IDC_SERIAL,addr szSerial
                ; Try to send name^serial infos to mIRC.
                invoke FindWindow, NULL, addr szSerialDialog
                  .if eax
                      mov windhand, eax
                      ; 132: control ID for the 'name' edit control on mirc dialog
                      invoke SendDlgItemMessageA,windhand,132,WM_SETTEXT,0,addr szName
                      ; 134: control ID for the 'serial' edit control on mirc dialog
                      invoke SendDlgItemMessageA,windhand,134,WM_SETTEXT,0,addr szSerial
                  .endif
                .endif
              .endif
          .endif
	.if	wParam == IDB_PATCH
    ;Patch procedure start here
        invoke FindFirstFile,ADDR TargetName,ADDR ff32
        ; File to patch is not in same dir
        .if eax == INVALID_HANDLE_VALUE
           invoke SetDlgItemText,hWin,IDC_SERIAL,addr szNotFound
        .else
        mov eax,TargetSize
            ; File size is incorrect
            .if ff32.nFileSizeLow != eax
                invoke SetDlgItemText,hWin,IDC_SERIAL,addr szWrongSize
            ; Filesize is correct
            .else
            mov pFileMem,InputFile(ADDR TargetName)
            invoke CRC32,pFileMem,ff32.nFileSizeLow
            mov edx,TargetCRC32
            ; Calculated CRC32 does not match
            .if eax != edx
               invoke SetDlgItemText,hWin,IDC_SERIAL,addr szBadCRC32
            .else
            invoke GetFileAttributes,addr TargetName
            ; The file is read-only, so let's try to set it to read/write
                .if eax!=FILE_ATTRIBUTE_NORMAL
                    invoke SetFileAttributes,addr TargetName,FILE_ATTRIBUTE_NORMAL
                .endif
              ; Everything's okay, so let's patch the file
              invoke CreateFile,addr TargetName,GENERIC_READ+GENERIC_WRITE,FILE_SHARE_READ+FILE_SHARE_WRITE,\
                                                NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL
             .if eax!=INVALID_HANDLE_VALUE
                    mov hTarget,eax
            ;Before patching check if backup
        invoke SendDlgItemMessage,hWin,IDC_CHECKBOX,BM_GETCHECK,0,0
        .if eax==BST_CHECKED
            invoke CopyFile, addr TargetName, addr BackupName, TRUE
           .else
        .endif
        ; Start patches to the file
        patch PatchOffset1,WBuffer1,1
        patch PatchOffset2,WBuffer2,6
        patch PatchOffset3,WBuffer3,6
        patch PatchOffset4,WBuffer4,6
        patch PatchOffset5,WBuffer5,2
        patch PatchOffset6,WBuffer6,2
        patch PatchOffset7,WBuffer7,2
        invoke CloseHandle,hTarget
        invoke SetDlgItemText,hWin,IDC_SERIAL,addr szSucess
        invoke GetDlgItem,hWin,IDB_PATCH
        invoke EnableWindow, eax, FALSE
        .endif
        .endif
	.endif
	.endif
	.elseif	wParam == IDB_CANCEL
	; Exit button, so send a close message
         invoke EndDialog,hWin,0
	.endif
	.elseif	uMsg == WM_CLOSE
         invoke EndDialog,hWin,0
	.endif
	xor	eax,eax
	ret
DlgProc	endp

Patch proc hWnd:HWND
	local status:DWORD
Patch EndP

end start

crc32.inc:

CRC32            PROTO :DWORD,:DWORD

.data? CRCtable dd 256 DUP (?)
.code
align 4
; ллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллл ; These are MASM versions of Donkey's GoAsm CRC-32 procedures, ; adapted from routines by Thomas Bleeker (MadWizard),table- ; driven and fast ; ллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллл
InitCRC32Table proc uses ebx esi edi mov edi, offset CRCtable - 4 xor ecx, ecx
M1: mov eax, ecx mov ebx, 0EDB88320h add edi, 4 mov esi, 8 @@: shr eax, 1 sbb edx, edx and edx, ebx xor eax, edx dec esi jnz @B inc ecx mov [edi], eax cmp ecx, 256 jb M1
mov eax,offset CRCtable ret InitCRC32Table endp
align 4
CRC32 proc uses ebx esi edi lpBuffer, cbBuffer
mov esi, lpBuffer mov edi, offset CRCtable mov edx, cbBuffer
shr edx, 1 or ecx, -1 xor eax, eax
@@: mov al, [esi] xor al, cl shr ecx, 8 mov ebx, [edi+4*eax] xor ecx, ebx
mov al, [esi+1] xor al, cl shr ecx, 8 mov ebx, [edi+4*eax] add esi,2 xor ecx, ebx
dec edx jnz @B
test dword ptr[cbBuffer], 1 jz @F mov al, [esi] xor al, cl inc esi shr ecx, 8 mov ebx, [edi+4*eax] xor ecx, ebx @@:
mov eax, ecx not eax ret
CRC32 endp

base.rc:

;This Resource Script was generated by WinAsm Studio.

#define IDC_NAME 1001
#define IDC_SERIAL 1002
#define IDC_CHECKBOX 1003
#define IDB_CANCEL 1004
#define IDB_PATCH 1005
#define IDC_GROUPBOX 1006

101 DIALOGEX 0,0,151,70
CAPTION ""
FONT 8,"Tahoma"
STYLE 0x80c80880
EXSTYLE 0x00000000
BEGIN
    CONTROL "",IDC_GROUPBOX,"Button",0x50008007,3,3,144,47,0x00000000
    CONTROL "",IDC_NAME,"Edit",0x10010080,13,15,123,12,0x00000200
    CONTROL "",IDC_SERIAL,"Edit",0x10000840,13,31,123,12,0x00000200
    CONTROL "",IDC_CHECKBOX,"Button",0x50010003,3,55,27,10,0x00000000
    CONTROL "",IDB_CANCEL,"Button",0x10000000,97,52,50,14,0x00000000
    CONTROL "",IDB_PATCH,"Button",0x10000000,30,52,50,14,0x00000000
END

make.bat:

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

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

1



Une fois le sérial généré via le keygen celui-ci est transmit directement dans les champs nom/sérial de la fenêtre d'enregistrement de m*I*R*C via l'api SendDlgItemMessageA.
Pour récupérer les ID des deux contrôles de la fenêtre j'ai utilisé Au3Info.

V: Patcher la vérification de somme de contrôle


J'ai inclus dans mon code, une fonctionnalité de patcher en plus du keygen.
m*I*R*C vérifie l'authenticité du sérial en ligne, il va falloir patcher ça, je vous laisse découvrir comment.
Le truc en plus c'est que dès que l'on modifie quelque chose dans le code et que l'on sauvegarde le binaire, et bien.. Celui-ci ne s'ouvre plus!
Alors comment qu'on fait?
Il y a plusieurs moyens d'arriver à ses fins: on peut dégrossir à l'aveugle en traçant depuis le début le programme avec F8, et voir sur quelle procédure on a un ExitProcess. Ensuite recommencer le traçage en affinant le processus de traçage.
Ou bien faire du traçage à l'envers pour savoir d'où on vient (on va voir ça)
Charger votre m*I*R*C patché dans Ollydbg, puis faites un clic droit>Search for>All intermodular calls


1

On va commencer par trier toute cette mosaïque chaotique par destination. On sait que pour quitter un programme on utilise l'API ExitProcess, on scroll donc sur kernel32 et on va chercher voir si y'a pas un ExitProcess qui traine dans le coin. (y'a des plugins pour faire ça, mais on va partir du principe que pas de plugin)


1

On l'a trouver, on double clique dessus pour sauter sur la destination, on arrive en 006004CB, qui est une petite procédure appelée de 4 endroits.


1

Soit vous posez des breakpoints sur les 4 appels, vous lancez et regardez qui break, soit vous posez un breakpoint sur le début de la procédure vous modifiez l'instruction par un RETN et vous lancez l'appli/regarder où ça renvoie.
Moi j'aime bien la technique du RETN donc c'est ce que je vais faire.
Voici la première procédure de remontée:


1

Rien de spécial, l'instruction JNE nous renvoi un peu plus loin, puis on va reboucler plus loin dans le code pour revenir dans cette même procédure.
On continue à remonter à coup de RET/Breakpoint/Reload/Run, et on arrive sur une petite procédure, avec toujours rien de spécial.


1

On continue, et la on commence à être haut: on voit l'appel de GetStartupInfoW et de GetCommandLineW. Cela ne sert à rien d'aller plus loin.
On s'aperçoit qu'on est appelé d'en bas de la procédure, et qu'il y a autre appel de procédure en dessous, elle renvoie dans une partie du code que l'on a déjà remonté via RET, donc on sait que ça va aussi sur un ExitProcess.
Le résultat du saut n'importe donc pas, il doit donc se passer autre chose dans une procédure voisine qui renvoie "ailleurs" si c'est bon, et si c'est pas bon "retourne" pour aller dans les ExitProcess.
On pose un breakpoint sur la procédure au-dessus du JNE (0060A6AE), on lance, et on F7 pour aller à l'intérieur et voir ce qu'il se passe.


1

Une fois dans cette procédure on trace tranquillement avec F8
Puis on arrive sur un saut que le programme prend pour sortir de la procédure!
On remarquera aux alentours que plusieurs jump nous font quitter aussi cette procédure.


1

On pose un breakpoint sur l'appel de la procédure juste au-dessus de notre instruction JZ. (le CALL 00546600)
On relance/rebreak et F7 dedans, puis on continue à tracer l'intérieur.
Arrivé à un moment, plus bas dans la proc, on retombe sur le même cas de figure d'appels de procédures et de sauts conditionnels qui quittent la routine.
le saut est pris, il faut donc investiguer ce qu'il se passe avant: dans le CALL 00546A9D


1

La procédure là est un peu plus petite, on a 2 sauts conditionnels et 2 RETN.
Allons voir la procédure juste avant les sauts.


1


Ça y et on le tient!
Ici l'utilisation de CheckSumMappedFile contre un FileMap, appelé via la pile.


1


La valeur est récupérée puis comparée dans le second jump, si ce n’est pas bon, on ne saute pas


1


Si on force le saut, et que l'on exécute: le programme se lance.
On a donc au choix:
Soit patcher JZ SHORT 004795F9 (qui est avant) en JMP
Ou soit patcher JE SHORT 004795F9 en JMP
En premier choix c’est mieux de patcher le premier saut, dans tous les cas c'est juste un byte à changer: 74 en EB (jmp)

VI: Conclusion


On a pu voir ici un programme assez connu avec finalement un système de licence très rudimentaire, mais une reconnaissance du sérial est effectuée en ligne, cela leur permet de garder le "contrôle"
En plus, ils ont ajouté une protection anti-patching pour éviter toute modification.
La vérification en ligne rend se programme inadapté pour les personnes souhaitant en faire une utilisation hors-ligne pour se connecter à un ircd local, mais c'est pas grave au final tout se résume à remplacer quelques lignes de code.
L'algorithme usité pour la génération du sérial en fait une appli adaptée pour ceux souhaitant débuter dans le keygenning, car simple à comprendre. La protection par checksum et quant à elle un peu plus réaliste que le reste et intéressante à voir.


Xylitol, 11/04/2021




Copyright (C)- xtx Team (2021)

XHTML valide 1.1 CSS Valide !