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