Fishing/Sniffing Z*D S*o*f*t S*c*r*e*e*n R*e*c*o*r*d*e*r v11.3.0.0

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:



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.


1

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".


1

La boite de dialogue pour s'enregistrer s'affiche, on a trois champs: Nom, E-Mail, Clé.
On rentre n'importe quoi.


1

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.


1

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.


1

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.


1

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.


1

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.


1

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.


1

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à.


1

Nous sommes maintenant en haut de la procédure, on trace.


1

Les trois premiers sauts 'JZ' regardent si un de nos champs (Nom, E-Mail, Clé), a été laissé vide.


1

Si c'est le cas, on saute juste en dessous de notre MessageBox 'mauvais sérial'.


1

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)


1

On arrive ici, on continue à tracer en step over (F8).


1

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.


1


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.


1

On arrive sur une fonction de comparaison dans la fin de la grande boucle de notre procédure.


1

Notre sérial est comparé.


1

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.


1

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.


1

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:

1

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.


1

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)

XHTML valide 1.1 CSS Valide !