Introduction:
Jamais 2 sans 3, histoire de terminer la boucle après mon tuto sur le keygenning de m*I*R*C, et celui du patching de A*V*S V*i*d*e*o C*o*n*v*e*r*t*e*r...
Voilà celui sur le serial-fish.
Les outils:
OllyDbg v2.01 (http://ollydbg.de/)
PEiD (http://peid.has.it/)
Le soft (https://web.archive.org/web/20210424...SRSetup.exe)
I: Démarrage:
On scan l'appli avec PEiD pour regarder si elle est compressée, etc...
Ce n'est pas le cas. ok, impec, on gagnera du temps.
On lance l'application pour voir à quoi ça ressemble.
On va la chercher dans la zone de notification Windows (dans le coin inférieur droit de l'écran.)
On fait un clic droit, puis on clic sur "About".
La boite de dialogue pour s'enregistrer s'affiche, on a trois champs: Nom, E-Mail, Clé.
On rentre n'importe quoi.
On obtient le message: Your license info is invalid!
Bon allez, on en a assez vu, on passe à la suite.
II: Trouver la routine d'enregistrement:
On charge l'exécutable principal dans notre débogueur, puis on regarde les strings datas référence on cherche à tout hasard "Your license".
Aucun résultat.
On change de tactique: lançons totalement l'exécutable dans le débogueur (F9)
On va sur la fenêtre d'enregistrement, puis on entre un sérial bidon, et on valide.
La fenêtre pour nous dire que le sérial n'est pas bon s'affiche.
Ne cliquez pas sur OK, retournez dans le débogueur pour le mettre en pause. (F12)
Ensuite faites Alt+K ou bien cliquez sur le bouton 'K' dans le menu de la toolbar du débogueur pour afficher la fenêtre d'appels de la pile.
On peut voir MessageBoxExW, et MessageBoxW.
On double click sur MessageBoxExW qui nous emmène sur ça position dans user32.
Notre débogueur et "en pause" une ligne au-dessus, on pose un breakpoint en dessous de l'api MessageBoxExW.
Ensuite on appuie sur F9 pour reprendre l'exécution, puis, aussitôt on rebreak, voilà.
Maintenant sortons de la avec F8 (step over), l'adresse de retour est aussi affichée dans la fenêtre de la pile.
On quitte les offsets en 7E3E de user32 pour ceux en 5F86 de mfc42u.
On arrive dans mfc42u, on regarde un peu la procédure dans lequel nous sommes, on peut voir MessageBoxW, mais rien d'autre d'intéressant.
On va placer un breakpoint en haut de la procédure.
Ça serait bien de sortir de mfc42u pour retourner sur ScnRec.exe et voir ou on est.
On va utiliser la technique du RET pour remonter les procédures:
On remplace le début de la procédure par l'instruction: RETN (retour)
On place un breakpoint dessus, puis on lance et on fait en sorte que ça break dessus il n'y a plus qu'a voir quelle est notre retour, poser un breakpoint dans la nouvelle procédure, restaurer l'ancien code que l'on a modifier puis reprendre l'exécution du programme et répéter l'opération si besoin.
Certains préfèrent descendre et voir où ça tombe plutôt que de 'remonter' mais chacun son truc.
Bref on arrive donc a remonter sur une procédure plus petite qui n'a rien d'intéressant, et qui est appelée encore une fois de beaucoup trop d'endroits.
Donc on va refaire la technique.
On change de plage d'offset pour arriver sur 0040 (on est de retour sur ScnRec.exe), le code semble un peu plus parlant lui aussi.
On va placer un breakpoint au début de la procédure, mais cette fois si on va commencer à tracer, voir ce que ça donne déjà.
Nous sommes maintenant en haut de la procédure, on trace.
Les trois premiers sauts 'JZ' regardent si un de nos champs (Nom, E-Mail, Clé), a été laissé vide.
Si c'est le cas, on saute juste en dessous de notre MessageBox 'mauvais sérial'.
On se rapproche de notre appel vers la MessageBox 'mauvais sérial', il ne reste plus qu'une procédure avant le saut.
Toujours rien de probant pour le moment, allons donc dans cette dernière procédure avec F7 (step into)
On arrive ici, on continue à tracer en step over (F8).
En 0x4281D1, après la procédure CALL 00427FA0, on s'aperçoit qu'on a un sérial dans le registre.
On va donc poser un breakpoint sur le call, lancer l'exécution, re-rentrer nos infos pour que ça rebreak, puis faire un step into (F7) une fois sur l'instruction CALL 00427FA0, pour aller voir l'intérieur.
III: La routine d'enregistrement:
On arrive ici, on trace à coup de F8 (step over), sachant que de toute manière on s'en fout un peu du fonctionnement de la routine, ici on veut juste un endroit pour voir notre sérial.
On arrive sur une fonction de comparaison dans la fin de la grande boucle de notre procédure.
Notre sérial est comparé.
Et même contre une multitude d'autres sérials générés pendant cette boucle, puis on sort finalement de là.
On retrouve des sauts conditionnels puis on sort et on comprend finalement que ça ne s'annonce pas compliqué pour du patching.
Mais bon on et pas là pour ça dans ce tutoriel, on va donc voir pour le serial-fish.
L'endroit parfait pour récupérer notre sérial c'est sur l'api de comparaison. (MSVCRT.wcscmp)
Juste avant, il fait un PUSH EAX pour envoyer le sérial dans la pile.
Plein de gens on déjà abordé le "self-keygenning" avec le serial-fish, on va donc partir sur un sniffer.
IV: Faire un serial-sniffer:
T'en qu'à faire, on va faire ça en asm.
sniffer.asm:
.486 .model flat, stdcall option casemap :none ; case sensitive include \masm32\include\user32.inc include \masm32\include\kernel32.inc include \masm32\include\windows.inc include \masm32\include\comdlg32.inc includelib \masm32\lib\user32.lib includelib \masm32\lib\kernel32.lib includelib \masm32\lib\comdlg32.lib DlgProc PROTO :DWORD,:DWORD,:DWORD,:DWORD Unicode2Ansi PROTO :DWORD,:DWORD Zero PROTO :DWORD,:DWORD FullHook PROTO :DWORD CreateTheProcess PROTO :DWORD .const IDD_DIALOG equ 1000 IDB_PATCH equ 1001 IDB_QUIT equ 1002 IDC_SERIAL equ 1003 IDC_STATIC equ 1004 .data ; Dialog texts szTitle db "ZD Soft Screen Recorder v11.3.0.0 *Serial Sniffer*",0 szErrCaption db "Err0r",0 szErrNotFOund db "Target not found!",13,10 db "put this sniffer in same dir as ScnRec.exe!",0 szIDBPatch db "LAUNCh aPP/gET SERiAL",0 szIDBExit db "eXIT",0 szLblSer db "Serial:",0 ; App Details szTarget db "ScnRec.exe",0 hookbytes dw 0FEEBh ; EB FE .data? hInstance dd ? ;dd can be written as dword CommandLine dd ? hWnd dd ? tcont CONTEXT <> SerialThtreadID dd ? szSerUni db 1024 dup(?) szSerial db 1024 dup(?) pinfo PROCESS_INFORMATION <> sinfo STARTUPINFO <> .code start: invoke GetModuleHandle, NULL mov hInstance, eax invoke DialogBoxParam, hInstance,IDD_DIALOG, 0, ADDR DlgProc, 0 invoke ExitProcess, eax DlgProc proc hWin :DWORD, uMsg :DWORD, wParam :DWORD, lParam :DWORD LOCAL ff32:WIN32_FIND_DATA pushad mov eax,uMsg .if uMsg == WM_INITDIALOG push hWin pop hWnd ; 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_PATCH,ADDR szIDBPatch invoke SetDlgItemText,hWin,IDB_QUIT,ADDR szIDBExit invoke SetDlgItemText,hWin,IDC_STATIC,ADDR szLblSer .elseif uMsg == WM_COMMAND mov eax,wParam and eax,0FFFFh .if wParam == IDB_PATCH invoke FindFirstFile,ADDR szTarget,ADDR ff32 ; File to sniff is not in same dir .if eax != INVALID_HANDLE_VALUE ; If ok kill the process in case already running invoke TerminateProcess,pinfo.hProcess,0 invoke TerminateThread,SerialThtreadID,0 ; Spawn the process invoke CreateTheProcess, ADDR szTarget invoke FullHook,42F762h ; OEP mov eax, offset SerialThtread invoke CreateThread,0,0,eax,0,0,offset SerialThtreadID .else invoke MessageBox,NULL,ADDR szErrNotFOund,ADDR szErrCaption,MB_ICONERROR ; Soft Not found .endif .elseif wParam == IDB_QUIT jmp @close .endif .elseif uMsg == WM_CLOSE @close: invoke TerminateProcess,pinfo.hProcess,0 invoke TerminateThread,SerialThtreadID,0 invoke EndDialog,hWin,0 .endif xor eax,eax ret SerialThtread: invoke FullHook,428122h ;At 0x428122, Our serial is compared and EAX contain the good serial. ;0042811A |. 50 |PUSH EAX ; /string2 = "AAAAA-AAAAA-AAAAA-AAAAA-AAAAA" ;0042811B |. 8D85|LEA EAX,[EBP+2A64] ; | ;00428121 |. 50 |PUSH EAX ; |string1 = "PXAX4-EFJ8J-LH8F4-CBRBV-470X4" ;00428122 |. FF15|CALL DWORD PTR DS:[<&MS ; \MSVCRT.wcscmp mov tcont.ContextFlags,CONTEXT_FULL invoke GetThreadContext,pinfo.hThread, offset tcont; Context of running thread so we can get the value in eax mov eax,tcont.regEax invoke ReadProcessMemory,pinfo.hProcess,eax, offset szSerUni,40h,0 ; Read out our serial invoke Unicode2Ansi,addr szSerUni,addr szSerial ;convert unicode to ansi invoke SetDlgItemText, hWnd,IDC_SERIAL, offset szSerial ; Send it back invoke TerminateProcess,pinfo.hProcess,0 ; Screen Recorder is killed after getting the Serial invoke ExitThread,0 DlgProc endp Unicode2Ansi proc iString,ouptbuf invoke lstrlen,ouptbuf invoke WideCharToMultiByte,CP_ACP,0,iString,-1,ouptbuf,eax,0,0 invoke WideCharToMultiByte,CP_ACP,0,iString,-1,ouptbuf,eax,0,0 ret Unicode2Ansi endp FullHook proc hookaddr:DWORD LOCAL threadcontext:CONTEXT LOCAL backup:DWORD LOCAL backupb:WORD mov eax,hookaddr mov backup,eax invoke ReadProcessMemory,pinfo.hProcess,hookaddr,addr backupb,2h,0 invoke WriteProcessMemory,pinfo.hProcess,hookaddr,offset hookbytes,2h,0 invoke Zero,addr threadcontext,sizeof threadcontext invoke ResumeThread,pinfo.hThread invoke Sleep,100 mov threadcontext.ContextFlags,CONTEXT_FULL @@: invoke GetThreadContext,pinfo.hThread,addr threadcontext mov eax,hookaddr cmp eax,threadcontext.regEip jnz @B invoke SuspendThread,pinfo.hThread invoke WriteProcessMemory,pinfo.hProcess,backup,addr backupb,2h,0 ret FullHook endp CreateTheProcess proc processexetocreate:DWORD invoke CreateProcess,processexetocreate,processexetocreate,0,0,0,CREATE_SUSPENDED,0,0,offset sinfo,offset pinfo ret CreateTheProcess endp Zero proc uses eax ecx edi address:DWORD,sizetozero:DWORD xor eax,eax mov ecx,sizetozero mov edi,address rep stosb ret Zero endp end start
base.rc:
;This Resource Script was generated by WinAsm Studio. #include "/masm32/include/resource.h" #define IDD_DIALOG 1000 #define IDB_PATCH 1001 #define IDB_QUIT 1002 #define IDC_SERIAL 1003 #define IDC_STATIC 1004 IDD_DIALOG DIALOGEX 0,0,216,70 FONT 8,"Tahoma" STYLE 0x80c80880 EXSTYLE 0x00000008 BEGIN CONTROL "",IDB_PATCH,"Button",0x10010001,30,49,124,14,0x00000000 CONTROL "",IDB_QUIT,"Button",0x10000000,160,49,50,14,0x00000000 CONTROL "",IDC_STATIC,"Static",0x50000000,3,31,24,10,0x00000000 CONTROL "",IDC_SERIAL,"Edit",0x10000840,31,28,180,12,0x00000200 END
make.bat:
@echo off \masm32\bin\rc /v base.rc \masm32\bin\ml.exe /c /coff /Cp /nologo sniffer.asm \masm32\bin\link.exe /SUBSYSTEM:WINDOWS /RELEASE /VERSION:4.0 /OUT:Sniffer.exe sniffer.obj base.res del base.res del sniffer.obj pause
Une fois compilé, ça ressemble à ça:
Notre code lance l'appli, l'utilisateur entre ce qu'il veut dans le soft, puis on récupère le sérial contenu dans EAX au moment de la comparaison finale pour le transmettre dans notre appli, et on kill le soft.
Il n'y a plus qu'a rouvrir le soft sans notre sniffer, et essayer le sérial récupérer.
Pas de surprise, ça marche.
V: Conclusion
On a vu ici comment faire un serial-sniffer.
L'avantage de cette méthode c'est que l’on peut aussi l'appliquer à des applications qui sont compressées (à condition qu'elles soient mal protégées en mémoire.)
Par exemple Q*u*a*l*c*o*m*m P*r*e*m*i*u*m T*o*o*l v1.5, est protégé par VMProtect, mais reste facilement keygennable/serial-fishable en mémoire car le développeur a mal implémenté le truc.
Xylitol, 25/04/2021
Copyright (C)- xtx Team (2021)