Backup Magic 1.0.0

Backup Magic, un programma che promette miracoli nell'esecuzione
di copie di riserva di ogni tipo di dati.

'Please enter your name and license number [..] '
Se si potessero trovare in un luogo comune tutti questi programmatori
non riuscirebbero a comunicare tra loro: o parlerebbero nello stesso
istante (dicendo le stesse cose) o non parlerebbero affatto.
Veramente.
Dire che si assomigliano tutti Φ dir poco...

Ok, a parte l'introduzione inseriamo un bel nome e un numero fasullo,
e premiamo 'OK'. Una MessageBox (strano...) ci avverte che abbiamo
inserito un codice non valido per quel nome, bla, bla, bla...
Entriamo in SoftIce e impostiamo un breakpoint (bpx) per MessageBoxA.
Ritorniamo al nostro programmino e inseriamo di nuovo i dati per 
cliccare 'OK'. 
SoftIce riappare e noi premiamo F11. Clicchiamo su 'OK' e ritorneremo
in SoftIce. Annotiamoci l'indirizzo ma notiamo anche ci≥ che c'Φ 
poche righe pi∙ sotto... un 'ret'.
BΦ, perchΦ non farsi un giretto? Premiamo F10 fino a ritornare da 
questa chiamata e annotiamo anche questo indirizzo, potrα servirci.
Per adesso possiamo uscire da SoftIce e aprire il nostro 
disassemblatore preferito, nel quale caricheremo il file e ci 
recheremo al primo indirizzo che ci siamo annotati. (45FF18)
Effettivamente c'Φ la chiamata alla MessageBoxA ma se risaliamo
di un p≥ il codice osserviamo che invece di un salto condizionale
troviamo solo l'inizio della chiamata.

Dunque abbiamo fatto bene ad annotare anche l'altro indirizzo!
Rechiamoci a quest'ultimo (46D7E8) e arriveremo giusto dopo la
chiamata che visualizza la MessageBox di errore.
Valutiamo un attimo la situazione: la chiamata contenente il 
riferimento a MessageBox aveva alcune sub-chiamate prima.
Vale la pena di analizzarle? 
Per rispondere immedesimiamoci nel programmatore, avido di soldi
che probabilmente non arriveranno:

Quesito: - Il programmatore ha pensato a ottimizzare il codice?
	(il che equivale a utilizzare un solo riferimento a MessageBox
	 per tutte le MessageBox possibili)

La risposta in molti casi Φ: NO, il programmatore pensa solamente a
un modo sbrigativo e non ottimizza QUASI MAI il suo codice.

Adesso che sappiamo questo proviamo a risalire un poco il codice e
scopriamo un riferimento condizionale da 46D6BA.
Andiamo a vedere cos'Φ e scopriamo che Φ del tipo je xxxxxxxx.
Chissa che...
BINGO! Due righe pi∙ sopra c'Φ una chiamata il cui valore in uscita
(tipicamente posizionato in eax) viene verificato (con test al, al).
Ricordiamoci solo il salto condizionale: je -> jump if zero ovvero
salta all'indirizzo specificato se l'istruzione test al, al verifica
che al Φ uguale a zero. Noi quindi abbiamo bisogno di avere in 
ritorno un valore diverso da zero (tipicamente 1).
Ok, andiamo al riferimento della chiamata e guardiamo inanzitutto 
quanto Φ lunga. Poco. Molto bene.
Cerchiamo, partendo dalla fine, se c'Φ qualche istruzione che azzera
eax. C'Φ. E giusto sopra c'Φ un riferimento incondizionale...
Andiamo a vedere e vediamo strane istruzioni tipo  
mov dword ptr fs:[eax], esp
Il pi∙ delle volte sono da ignorare in quanto NON sono volute dal 
programmatore stesso ma dal suo compilatore, quindi cerchiamo
altrove il nostro codice.
Avanziamo e osserviamo due chiamate la seconda delle quali Φ seguita
dall'istruzione sete al, che non centra nulla con la sete di acqua.
Quest'istruzione verifica il flag Zero e assegna 1 ad al se il valore
del suddetto flag Φ 1. Il valore di eax viene poi salvato spostando
tutto in ebx (molto importante).
La chiamata precedente a sete al pu≥ essere di due tipi:
pu≥ essere una routine di confronto oppure pu≥ contenere al suo 
interno sia la routine per il calcolo del codice che per il suo
controllo.

Non ci resta che verificare...
E' solo una routine di controllo...
Quindi la chiamata prima di questa Φ quella che calcola il codice...
Bene, andiamo a vedere e scopriamo questo loop:

:0044AFB9 33C9                    xor ecx, ecx
:0044AFBB 8A4C06FF                mov cl, byte ptr [esi+eax-01]
:0044AFBF 03C8                    add ecx, eax
:0044AFC1 0FB77DFE                movzx edi, word ptr [ebp-02]
:0044AFC5 0FAFCF                  imul ecx, edi
:0044AFC8 69C9B2000000            imul ecx, 000000B2
:0044AFCE 03D9                    add ebx, ecx
:0044AFD0 40                      inc eax
:0044AFD1 4A                      dec edx
:0044AFD2 75E5                    jne 0044AFB9

Anche qui dovrebbero trovarsi nello stesso luogo tutti i 
programmatori che non capiscono che certe cose i compilatori le
traducono in ASM allo stesso modo...
Comunque noi siamo alla ricerca di un codice, non del programmatore
perci≥ mettiamoci al lavoro e cerchiamo di capire cosa avviene.

In poche parole il loop provvede a:
- azzerare ecx
- porre in cl una lettera del nome
- aggiungere a questa lettera il valore della sua posizione
- moltiplicare il nuovo valore per una costante (661)
- moltiplicare il risultato per 178
- aggiungere il risultato ad un checksum (azzerato inizialmente)
- ripetere per ogni lettera

Alla fine del loop non si deve far altro che verificare il contenuto
di ebx e annotarsi il valore: Φ il corrispondente esadecimale del
codice corretto.

Ora, mi sembra una protezione abbastanza semplice e perci≥ possiamo
provare a riprodurla in un nostro programma:
(Scritto per essere assemblato con TASM 5.0)

----Tagliare qui----------------------------------------------------
Ideal
Model Tiny
p386
CodeSeg
Org 100h

Inizio:
        mov ah, 9
        lea dx, [Intro]
        int 21h		; Visualizza l'introduzione

        inc ah
        lea dx, [L_Max]
        int 21h		; Riceve il nome utente

        mov bl, [L_Eff] 
        mov [byte ptr Buff+bx], 0 ; Rimuove l'ultimo carattere (0Dh)

        lea esi, [Buff] ; Offset del nome in esi
        xor ebx, ebx    ; Prepara gli altri registri
        xor eax, eax
        inc eax

        xor edx, edx
        mov dl, [L_Eff]

; Routine per il calcolo del codice
Loop1:
        xor ecx, ecx
        mov cl, [byte ptr esi+eax-01]
        add ecx, eax
        movzx edi, [word ptr Const1]
        imul ecx, edi
        imul ecx, 0B2h
        add ebx, ecx
        inc eax
        dec edx
        jne Loop1

        mov eax, ebx    ; in ebx c'Φ il codice calcolato
        xor ebx, ebx
        mov bl, 8       ; Ripetiamo 8 volte
        xor ecx, ecx    
        mov cl, 10      ; Fattore di divisione

; Routine per la conversione hex->dec
Dump:
        xor edx, edx
        cdq
        idiv ecx
        add dl, 30h
        mov [Codice+bx-1], dl
        dec bx
        jne Dump

        mov ah, 9
        lea dx, [Cod] ; Visualizza il codice calcolato
        int 21h
        int 20h


Intro db 'T H E _ D U X',13,10,'Key Generator per Backup Magic',13,10
      db '26 Giugno 1999.',13,10, 'Inserire il nome : $'
L_Max db 16
L_Eff db 0
Buff  db 16 dup (0) ; Buffer per memorizzare il nome
Const1 dw 661
Cod db 13,10,'Codice : '
Codice db '00000000$' ; Qui verrα memorizzato il codice calcolato
End Inizio

--e qui--------------------------------------------------------------

The_Dux