Pegasus Mail: MailBox Encryption


27/06/2000

by "Ritz"

 

 

UIC's Home Page

Published by Quequero


Bravo ritz, questo � un ottimo esempio di reversing, ovvero ci� che tutti gli studenti UIC dovrebbero essere in grado di fare.

 
UIC's form
E-mail: [email protected] 
IRC chan: #crack-it
UIC's form

Difficolt�

( )NewBies ( )Intermedio (x)Avanzato ( )Master

 

Scarica qui l'allegato

In questo tute cercheremo di personalizzarci un pochetto il client mail per win Pegasus Mail (v3.12c) per aumentarne la sicurezza e proteggere la nostra mailbox da occhi un po' troppo indiscreti ;).

NOTA INTRODUTTIVA: se state leggendo questo tute solo perche' volete crackare il prg in questione vi conviene lasciar perdere da subito, visto che la protezione non centra nulla con i byte scritti sotto e che Pegasus mail e' pure freeware.


Pegasus Mail: MailBox Encryption
Written by Ritz

Introduzione

Salve a tutti.
Come molti avranno avuto occasione di notare, i vari client mail che girano sotto Windoz offrono vari livelli di "protezione" (chiamiamola cosi') delle mail in entrata, a seconda naturalmente del prog che si sta prendendo in considerazione, dove col termine protezione intendo la capacita' del programma stesso di tenere a bada eventuali curiosi che volessero vedere cosa ci arriva ogni giorno.
Un esempio di programma che offre una buona protezione e' secondo me Calypso, in quanto mantiene tutte le mail *cifrate* in un'unico file di mailbox, e chiede una password all'avvio, in mancanza della quale non parte.
Dall'altro lato, esistono vari altri programmi che invece in quanto a protezione non si sprecano troppo, e uno di questi e' Pegasus Mail, il quale salva *ogni* mail in entrata come file a se stante in una data cartella (\mail) lasciando tutto completamente in chiaro (header compreso). Penso sia superfluo dire che tale prog all'avvio non chiede una password.
E' vero che tra le sue opzioni avanzate ne esiste una chiamata "Encryption", ma non penso serva a molto visto che le mail in ogni caso non vengono crittate.
Dopotutto si tratta di un prg freeware (non si puo' aver tutto dalla vita;) ), e dato allora che gli autori non si sono preoccupati troppo di offrire una certa sicurezza della mailbox, mi pare una bella idea mettere a posto le cose da soli.

 

Tool usati e documenti utili

 

. SoftICE (a me e' servito non poco per debuggare la dll)
. TASM
. Hex Workshop
. HIEW
. OpGen by -NeuRaL_NoiSE
. WIN32 API Reference
. "PE-Crypters : uno sguardo da vicino al c.d. "formato" PE", by Kill3xx
. "The PE file format", by B. Luevelsmeyer

Essay

PRELIMINARI

Prima di tutto sara' meglio studiare il programma. Configurato un nuovo account, scarichiamo un po' di posta e usciamo da Pegasus Mail (d'ora in poi PMail).
Andiamo nella sua cartella di installazione di default, entriamo in \Mail e proviamo ad aprire con un editor di testo i vari file cnm che abbiamo davanti. Ebbene si', ognuno di essi e' una delle e-mail che abbiamo ricevuto, che come si nota facilmente sono state salvate totalmente in chiaro (un file per ogni e-mail).
A questo punto abbiamo gia' le idee abbastanza chiare sulla situazione.
Come operare ora? Potremmo agire in 2 modi:

1- Chiedere all'avvio una pass senza la quale il prog non parte.

2- Chiedere all'avvio una chiave di decifratura della mailbox che in precedenza avremo diligentemente cifrato.

Il problema della soluzione 1 e' che la mailbox stessa rimarrebbe in chiaro una volta usciti dal prog, a meno di non scrivere un algoritmo di cifratura/decifratura che non chieda una chiave all'avvio del prg (il che non mi piace troppo).
Se scegliessimo la 2, pero', avremmo un altro problema. Supponendo infatti che un curiosone immetta la chiave sbagliata, il prg decifrera' la mailbox in modo errato e quindi le mail non saranno leggibili, ma il prog si avviera' in ogni caso e sara' possibile scaricare in chiaro la posta dal proprio pop (se la pw di connessione e' stata salvata dall'incauto proprietario).

Entrambi i casi presentano dei problemi, quindi... uniamo le 2 possibilita'.

Mi spiego meglio.

All'avvio, il programma dovra' chiedere sia una pass per poter essere avviato che una chiave per poter decrittare le mail. L'intruso per poter leggere la posta che abbiamo salvato dovra' conoscere entrambi gli elementi. Se infatti scrivera' la pw corretta ma la chiave sbagliata, la mailbox verra' decrittata in modo errato e quindi sara' illeggilbile, se invece la chiave e' corretta ma la pw sbagliata, PMail nemmeno si avviera'.

Naturalmente, all'uscita del prg sara' necessario rimanipolare il tutto con la stessa chiave immessa all'avvio, sia che essa sia giusta, sia sbagliata, in quanto altrimenti si rischia di rendere la stessa mailbox leggibile da tutti (aprendo i relativi file) in caso di chiave esatta o, peggio, illeggibile persino da parte del proprietario in caso di chiave sbagliata (in quanto le mail risulterebbero cosi' manipolate con ben 2 chiavi). Se non si facesse cosi', un errore nella battitura della stessa o un "intruso" che conosce la pw potrebbe mandarci tutta la nostra amata posta ad cazzum, il che non e' bene.

Riassumendo, dobbiamo:

1- Chiedere una pw.
2- Chiedere una chiave.
3- Se la pw e' corretta, avviare il prog decrittanto la mailbox tramite la chiave data, altrimenti uscire.
4- Aspettare la chiusura del prog.
5- Rimanipolare la mailbox con la chiave data all'avvio.
6- Uscire dal prg.

Possiamo fare tutto questo in 2 modi diversi.

1- Tramite un eseguibile.
2- Tramite una dll.

Personalmente, ho provato entrambi i metodi, quello secondo me sicuramente migliore (o, meglio, + "elegante") e' il 2, che trattero' di seguito, dato anche che per applicare il metodo 1 basta conoscere tutti i concetti relativi al 2, ma non viceversa.

EXE REVERSING & DLL CODING

Come appenda deciso, per raggiungere lo scopo dovremo creare una dll nostra che abbia una funzione di encrypt e decrypt (se l'algo e' costituito da semplicissime operazioni tipo xor le 2 funz possono essere unite). La funzione di decrypt deve essere chiamata appena PMail viene avviato, quella di decrypt subito prima che il suo processo sia terminato.
Di conseguenza bisognera' modificare la IT dell'exe stesso per aggiungere la nostra dll tra i moduli importati e le nostre 2 funz.
E qui sia il tute di Kill3xx che HIEW risultano praticamente indispensabili.
Apriamo winpm-32.exe e osserviamo la IT, o, meglio, la sua parte iniziale (modalita' Hex, F8, F7, Import). Ecco cosa abbiamo davanti:

.004BA000: 04 A1 0B 00-EC FF 61 05-2C 8D 58 05-7C AD 0B 00 � ��a ,X |�
.004BA010: E8 A1 0B 00-CC A2 0B 00-2C 8D 58 05-2C 8D 58 05 � Ì¢ ,X ,X
.004BA020: 86 AD 0B 00-DC A2 0B 00-EC A2 0B 00-2C 8D 58 05 †ï¿½ Ü¢ � ,X
.004BA030: C4 8D 58 05-90 AD 0B 00-04 A3 0B 00-1C A3 0B 00 �X ï¿½ � �
.004BA040: 2C 8D 58 05-2C 8D 58 05-9D AD 0B 00-E8 A4 0B 00 ,X ,X ï¿½ �
.004BA050: B4 A6 0B 00-2C 8D 58 05-CC A7 58 05-AA AD 0B 00 �� ,X ̧X ��
.004BA060: BC A6 0B 00-C4 A6 0B 00-2C 8D 58 05-2C 8D 58 05 1/4� Ħ ,X ,X
.004BA070: B2 AD 0B 00-CC A6 0B 00-D4 A6 0B 00-2C 8D 58 05 �� ̦ Ô¦ ,X
.004BA080: 68 A6 58 05-BF AD 0B 00-DC A6 0B 00-E4 A6 0B 00 h�X �� ܦ �
.004BA090: 40 98 58 05-60 9A 58 05-CC AD 0B 00-F4 A6 0B 00 @˜X `šX Ì­ ��
.004BA0A0: 04 A7 0B 00-90 A3 58 05-2C 8D 58 05-D9 AD 0B 00 � ï¿½X ,X Ù­
.004BA0B0: DC A7 0B 00-B4 A8 0B 00-2C 8D 58 05-2C 8D 58 05 ܧ �� ,X ,X
.004BA0C0: E3 AD 0B 00-D0 A8 0B 00-EC A8 0B 00-54 7B 65 05 � Ш � T{e
.004BA0D0: 14 00 62 05-EF AD 0B 00-2C AB 0B 00-6C AD 0B 00 b � ," l�
.004BA0E0: 2C 8D 58 05-2C 8D 58 05-FA AD 0B 00-74 AD 0B 00 ,X ,X �� t�
.004BA0F0: 00 00 00 00-00 00 00 00-00 00 00 00-00 00 00 00
.004BA100: 00 00 00 00-04 AE 0B 00-1E AE 0B 00-3A AE 0B 00 (r) (r) :(r)


Questa schermata e' un array di strutture del tipo IMAGE_IMPORT_DESCRIPTOR. Ognuna di tali strutture occupa 5 dw, e ce n'e' una per ogni modulo importato. Tale tipo di struttura e' costituito da:

1- OriginalFirstThunk: RVA ad un array di strutture (dw) di tipo IMAGE_THUNK_DATA che termina con un elemento NULL, ognuna delle quali punta a una funzione importata.
2- TimeDateStamp: non ci interessa, serve per le funzioni bound
3- ForwarderChain: non ci interessa nemmeno questa, serve per le funzioni forwarded.
4- Name: punta al nome del modulo (la cui stringa e' NULL terminated).
5- FirstThunk: array *indispensabile* simile a OriginalFirstThunk tranne per il fatto che qui gli IMAGE_THUNK_DATA verranno patchati dal loader di Windoz con gli indirizzi delle rispettive funzioni (l'insieme dei vari FirstHunk una volta patchati costituisce la IAT).

Non metto tutta la IT altrimenti occuperei byte inutili, al massimo guardatevela da HIEW, da cui potete facilmente verificare i vari IMAGE_IMPORT_DESCRIPTOR.

A questo punto abbiamo gia' capito come si puo' fare ad aggiungere la nostra funzione: sara' necessario aggiungere un nostro descriptor a quelli gia' esistenti cercando un posto dove poter mettere i Thunk, il nome del nostro modulo e quelli delle funzioni importate.
Una soluzione potrebbe essere quella di aggiungere 5 dw alla fine dei descriptor esistenti e usare quelle, bene avviso subito di non farlo:) in quanto se si facesse cosi' sarebbe necessario patchare (oltre ai descriptor precedenti) *tutti* i thunk (e non sono pochi) e tutti i vari jmp dword [] nella sezione .text responsabili della chiamata alle varie API richieste.
Il problema e' che alla fine dei descriptor ci sono le 5 dw NULL che sono indispensabili e poi iniziano subito gli OriginalFirstThunk. Si potrebbero spostare in su i descriptor, il problema e' che la IT inizia proprio all'inizio della sezione .idata che la contiene, quindi non abbiamo spazio. Andiamo alla fine di .idata... non c'e' spazio a sufficienza nemmeno li'.
Andiamo allora alla fine della sezione .rdata, ecco cosa vediamo

.004B9090: 00 00 00 00-00 00 00 00-00 00 00 00-00 00 00 00
.004B90A0: 00 00 00 00-00 00 00 00-00 00 00 00-00 00 00 00
.004B90B0: 00 00 00 00-00 00 00 00-00 00 00 00-00 00 00 00
.004B90C0: 00 00 00 00-00 00 00 00-00 00 00 00-00 00 00 00
.004B90D0: 00 00 00 00-00 00 00 00-00 00 00 00-00 00 00 00
.004B90E0: 00 00 00 00-00 00 00 00-00 00 00 00-00 00 00 00
.004B90F0: 00 00 00 00-00 00 00 00-00 00 00 00-00 00 00 00
.004B9100: 00 00 00 00-00 00 00 00-00 00 00 00-00 00 00 00
.004B9110: 00 00 00 00-00 00 00 00-00 00 00 00-00 00 00 00
.004B9120: 00 00 00 00-00 00 00 00-00 00 00 00-00 00 00 00
.004B9130: 00 00 00 00-00 00 00 00-00 00 00 00-00 00 00 00
.004B9140: 00 00 00 00-00 00 00 00-00 00 00 00-00 00 00 00
.004B9150: 00 00 00 00-00 00 00 00-00 00 00 00-00 00 00 00
.004B9160: 00 00 00 00-00 00 00 00-00 00 00 00-00 00 00 00
.004B9170: 00 00 00 00-00 00 00 00-00 00 00 00-00 00 00 00
.004B9180: 00 00 00 00-00 00 00 00-00 00 00 00-00 00 00 00
.004B9190: 00 00 00 00-00 00 00 00-00 00 00 00-00 00 00 00
.004B91A0: 00 00 00 00-00 00 00 00-00 00 00 00-00 00 00 00
.004B91B0: 00 00 00 00-00 00 00 00-00 00 00 00-00 00 00 00
.004B91C0: 00 00 00 00-00 00 00 00-00 00 00 00-00 00 00 00
.004B91D0: 00 00 00 00-00 00 00 00-00 00 00 00-00 00 00 00
.004B91E0: 00 00 00 00-00 00 00 00-00 00 00 00-00 00 00 00
.004B91F0: 00 00 00 00-00 00 00 00-00 00 00 00-00 00 00 00


Hum... niente male, mi sa proprio che i nuovi descriptor li metteremo li';).

--------------------------
PICCOLA NOTA: affinche' la IT sia valida basta che i descriptor costituiscano tutti un blocco compatto, non e' obbligatorio che poi i thunk, i nomi dei moduli e delle funzioni occupino posizioni precise, basta naturalmente aggiurnare l'IT RVA nella DataDirectory.
--------------------------
ALTRA PICCOLA NOTA: i vari thunk che puntano al nome della sezione non puntano all'inizio della stringa del nome, bensi' una word + indietro: tale word serve infatti a specificare l'ordinal della funzione, ma non sempre e' utilizzata (solo se l'IMAHE_THINK_DATA che la punta ha il byte + alto acceso). La stringa del nome inoltre e' NULL terminated.
--------------------------

Fatte le piccole note, spostiamo i descriptor esistenti in su, aggiungiamo il nostro, patchamo i vari RVA, aggiungiamo i nostri thunk, il nostro nome della dll e i nomi delle funzione da importare e naturalmente aggiorniamo il valore dell'RVA della IT nella DataDir (si trova 128 byte avanti rispetto all'inizio della Signature.
Fatto questo, possiamo cancellare i vecchi descriptor. Ecco allora come si presenta l'inizio della IT nel file patchato.

.004B9040: 04 A1 0B 00-EC FF 61 05-2C 8D 58 05-7C AD 0B 00 � ��a ,X |�
.004B9050: E8 A1 0B 00-CC A2 0B 00-2C 8D 58 05-2C 8D 58 05 � Ì¢ ,X ,X
.004B9060: 86 AD 0B 00-DC A2 0B 00-EC A2 0B 00-2C 8D 58 05 †ï¿½ Ü¢ � ,X
.004B9070: C4 8D 58 05-90 AD 0B 00-04 A3 0B 00-1C A3 0B 00 �X ï¿½ � �
.004B9080: 2C 8D 58 05-2C 8D 58 05-9D AD 0B 00-E8 A4 0B 00 ,X ,X ï¿½ �
.004B9090: B4 A6 0B 00-2C 8D 58 05-CC A7 58 05-AA AD 0B 00 �� ,X ̧X ��
.004B90A0: BC A6 0B 00-C4 A6 0B 00-2C 8D 58 05-2C 8D 58 05 1/4� Ħ ,X ,X
.004B90B0: B2 AD 0B 00-CC A6 0B 00-D4 A6 0B 00-2C 8D 58 05 �� ̦ Ô¦ ,X
.004B90C0: 68 A6 58 05-BF AD 0B 00-DC A6 0B 00-E4 A6 0B 00 h�X �� ܦ �
.004B90D0: 40 98 58 05-60 9A 58 05-CC AD 0B 00-F4 A6 0B 00 @˜X `šX Ì­ ��
.004B90E0: 04 A7 0B 00-90 A3 58 05-2C 8D 58 05-D9 AD 0B 00 � ï¿½X ,X Ù­
.004B90F0: DC A7 0B 00-B4 A8 0B 00-2C 8D 58 05-2C 8D 58 05 ܧ �� ,X ,X
.004B9100: E3 AD 0B 00-D0 A8 0B 00-EC A8 0B 00-54 7B 65 05 � Ш � T{e
.004B9110: 14 00 62 05-EF AD 0B 00-2C AB 0B 00-6C AD 0B 00 b � ," l�
.004B9120: 2C 8D 58 05-2C 8D 58 05-FA AD 0B 00-74 AD 0B 00 ,X ,X �� t�
.004B9130: 70 91 0B 00-00 00 00 00-00 00 00 00-60 91 0B 00 p' `'
.004B9140: 7C 91 0B 00-00 00 00 00-00 00 00 00-00 00 00 00 |'
.004B9150: 00 00 00 00-00 00 00 00-00 00 00 00-00 00 00 00
.004B9160: 43 52 59 50-54 2E 44 4C-4C 00 00 00-00 00 00 00 CRYPT.DLL
.004B9170: A0 91 0B 00-A7 91 0B 00-00 00 00 00-A0 91 0B 00 ' �' '
.004B9180: A7 91 0B 00-00 00 00 00-00 00 00 00-00 00 00 00 �'
.004B9190: 00 00 00 00-00 00 00 00-00 00 00 00-00 00 00 00
.004B91A0: 00 00 46 75-6E 63 00 00-00 4D 61 69-6C 43 72 79 Func MailCry
.004B91B0: 70 74 00 00-00 00 00 00-00 00 00 00-00 00 00 00 pt


Adesso rimane da fare solo un'ultima cosa: dobbiamo aggiungere in coda ai jump dword [] presenti alla fine della sezione .text quelli relativi alle nostre funzioni importate, in modo che alla fine risulti cosi'

.004AA48C: FF2544AD4B00 jmp d,[0004BAD44]
.004AA492: FF2548AD4B00 jmp d,[0004BAD48]
.004AA498: FF254CAD4B00 jmp d,[0004BAD4C]
.004AA49E: FF2550AD4B00 jmp d,[0004BAD50]
.004AA4A4: FF2554AD4B00 jmp d,[0004BAD54]
.004AA4AA: FF2558AD4B00 jmp d,[0004BAD58]
.004AA4B0: FF255CAD4B00 jmp d,[0004BAD5C]
.004AA4B6: FF2560AD4B00 jmp d,[0004BAD60]
.004AA4BC: FF2564AD4B00 jmp d,[0004BAD64]
.004AA4C2: 50           push eax
.004AA4C3: 6AFF         push 0FF
.004AA4C5: 2574AD4B00   and eax,0004BAD74 ;" K�t"
.004AA4CA: DC4000       fadd q,[eax][00000]
.004AA4CD: FF257C914B00 jmp d,[0004B917C]
.004AA4D3: FF2580914B00 jmp d,[0004B9180]


Dove jmp d,[0004B9180] salta al thunk corrispondente a MailCrypt, mentre jmp d,[0004B9180] salta a quello di Func (la quale cmq e' solo una dimostrazione che noi non useremo).

Molto bene, ora per quanto riguarda il discorso IT abbiamo finito. Per chiamare una delle nostre funzioni bastera' da un punto qualsiasi del codice fare una call a 4AA4CD o 4AA4D3, a seconda rispettivamente che si vogliano chiamata Func o MailCrypt.

Detto questo, passiamo alla creazione vera e propria della dll. I src completi li trovate nell'allegato, nella spiegazione non li incollero' tutti.

All'avvio il prg chiamera' la funzione MailCrypt, che quindi dovra' creare una modal dialog box (per le differenze tra modal, modeless, approfondimenti vari sulle dialog box e cosi' via rimando ai tute di Iczelion) dove poter immettere pw e key. Nel mio esempio pw e key vanno inserite nella stessa edit box, non perche' non avevo voglia di crearne 2:) ma per il fatto che in tal modo possiamo fare in modo ad ex che se viene usato un nick o una stringa di una certa lunghezza una parte serva per la pw e l'altra per la key, oppure che i byte pari siano una cosa e quelli dispari un'altra, e cosi' via... insomma mi sembra una buona scelta.

Prima di tutto allora ci serve uno script rc per definire le risorse, dove dovremo mettere la dialog, un'edit box, un bottone, un testo da scrivere subito prima della casella dove inserire il testo (del tipo "Key", anche se sappiamo che non e' una semplice key) e se vogliamo pure un'icona.

Creato il file, possiamo iniziare a scrivere la dll vera e propria. Ecco di seguito alcuni passi + improtanti dei src.

----------==========----------

PUBLICDLL    MailCrypt
PUBLICDLL    Func

ICON_SMALL    equ     0

----------==========----------

Questo sara' necessario metterlo prima della sezione .data, come si capisce facilmente serve a indicare le funzioni esportate dalla dll stessa. Sopra ho messo anche ICON_SMALL equ 0 (che servira' per l'icona) visto che in W32.inc non l'ho trovata, cmq nei src ho aggiunto la voce al file inc.

----------==========----------

filename    db         'c:\pmail\mail\*.cnm',0
Key          db         30 dup(00h)
path          db         'c:\pmail\mail\',0
dllname     db        'Crypt.dll',0
dummy     dd        0
sizemail     dd        0
databuffer dd        0
nomifile     dd        0
chkdw      dd        0
dummyb   dd        0
counti       db        0
count        db         0
FileStruct    WIN32_FIND_DATA    <?>

----------==========----------

Qui troviamo alcuni data che serviranno in seguito. filename servira' per cercare i file delle mail (cnm), FileStruct punta a una struttura che ci servira' quando dovremo cercare i file, Key e' il testo inserito (pw + key), databuffer e nomifile serviranno come puntatori a buffer allocati con VirtualAlloc, per il resto e' tutto molto semplice.

----------==========----------

.code
DllEntry Proc hInstDLL:HINSTANCE, reason:DWORD, rsvd:DWORD
    mov eax, TRUE
ret
DllEntry Endp

----------==========----------

Questa e' la funzione di entrypoint della dll, la si puo' chiamare come si preferisce, essa prende 3 parametri (di cui cmq il 3� e' riservato). Alla fine del codice asm sara' poi necerrario scrivere end <nome_funzione_di_entrypoint>.
hInstDLL e' il module handle della dll stessa, reason puo' assumere 4 valori diversi:

- DLL_PROCESS_ATTACH: quando la dll e' iniettata per la prima volta nell'address space del processo.
- DLL_PROCESS_DETACH: quando la dll e' scaricata dall'address space del processo.
- DLL_THREAD_ATTACH: quando il processo crea un nuovo thread.
- DLL_THREAD_DETACH: quando un thread del processo viene distrutto.

Se in eax viene resituito TRUE la DLL continuera' nell'esecuzione, altrimenti non verra' caricata.

----------==========----------

MailCrypt Proc checkbyte:DWORD

    push    checkbyte
    pop    chkdw
    cmp     dword ptr chkdword, 23h
    jnz    copacabana

    call    GetModuleHandleA, offset dllname    
    mov    _hInst, eax
    xor    eax, eax
    mov    ax, IDD_DIALOG1        
    call    DialogBoxParamA, _hInst, eax, NULL, offset DlgProc, NULL
    ret

----------==========----------

Ed ecco qui la nostra bella funzioncina. Come vedete essa riceve un parametro, checkdw. Ho deciso di far cosi' per una ragione molto semplice: sebbene abbia messo come esempio 2 funzioni diverse nella dll e le abbia inserite entrambe nella IT dell'exe, in questo esempio ne utilizzeremo solo una, visto che il processo di cifratura e' estremamente banale (infatti poi sta a ognuno il compito di implementarlo a dovere, altrimenti questo si chiamerebbe tool, non tutorial di everse engineering), e quindi dovremo passare alla stressa funzione parametri diversi a seconda che vogliamo decrittare la mail perche' il prog e' appena avviato o vogliamo rimanipolarla con la chiave inserita all'inizio se stiamo uscendo dal prg stesso. Nel caso sopra, se il parametro passato, chkdword, e' 0x23, allora dovremo chiedere la chiave di decrypt, altrimenti significa che il prg e' terminato e dobbiamo ricifrare tutto.

Per prendere la key (se il prg e' stato appena avviato) chiamiano prima di tutto GetModuleHandleA per ricevere l'handle della dll (attenzione che il parametro passato non deve essere NULL come per gli exe, altrimenti ci viene restituito l'handle del modulo chiamante e non della dll), quindi visualizziamo la modal box con DialogBoxParamA, che quindi passa l'esecuzione a DlgProc, che avra' il compito di esaminare i messaggi che arrivano e comportarsi di conseguenza. In particolare, quando viene premuto il tasto "OK" verra' chiamata la funzione Crypt, cuore del prg vero e proprio (wow che bella espressione).

----------==========----------

Crypt proc _hDlg:dword

    call    GetDlgItemTextA, _hDlg, IDC_EDIT1, offset Key, 32h
   
    cmp    dword ptr [Key+5], 'ztiR'
    jz     ecchime

    call    EndDialog, __hwnd, NULL
    call    ExitProcess

copacabana:
    xor     ecx, ecx
    mov    edi, offset FileStruct
beach:
    mov    byte ptr [edi+ecx], 0
    inc    ecx
    cmp    ecx, 100
    jl     beach
    mov     byte ptr counti, 0

----------==========----------

Il codice qui si capisce molto facilmente, viene preso il testo, quindi i char 6, 7, 8 e 9 vengono confrontati con la dw "Ritz" (e la fantasia dilaga), se coincidono si prosegue con la key vera e propria, altrimenti il prg va direttamente al creatore.
Avrete capito che qui avviene un check della pw. Come sempre l'esempio e' estremamente banale, l'algo per il check ognuno lo sviluppa come meglio crede, un check del genere non e' degno nemmeno di WinZip, ma e' solo un esempio... come sempre, largo alla fantasia.
Se il check passa si saltano via copacabana e beach, questo perche' esse servono solo quando il prg termina la sua esecuzione e si deve ricifrare la mailbox, infatti esse sono solo addette al riazzeramento del buffer che contiene la struttura che serve per trovare i file. Si salta infatti qui:

----------==========----------

ecchime:
    call    VirtualAlloc, NULL, 10000, MEM_COMMIT, PAGE_READWRITE
    mov    nomifile, eax
    add    nomifile, 14
    xor     eax, eax
    xor     ecx, ecx
    xor    ebx, ebx
    xor     edx, edx

    call     FindFirstFileA, offset filename, offset FileStruct

    mov    ebx, nomifile
    sub     ebx, 11

ancora:
    inc    count
    add     ebx, 11
    mov    ecx, 11
    mov    esi, offset [fd_cFileName+FileStruct]
    mov    edi, ebx
    repz    movsb

    call     FindNextFileA, eax, offset FileStruct

    test     eax, eax
    jnz     ancora
    sub     counti, 11

girazzo:
    add    counti, 11
    mov    ebx, nomifile
    add    ebx, counti
    sub    ebx, 14
    mov    ecx, 14
    mov    esi, offset path
    mov    edi, ebx
    repz    movsb
    mov    edx, nomifile
    add    edx, counti
    sub    edx, 14

    call     CreateFileA, edx, GENERIC_READWRITE, NULL, NULL, OPEN_EXISTING, NULL, NULL
    cmp    eax, INVALID_HANDLE_VALUE
    jnz    tutto_ok
   
    call    MessageBoxA, NULL, offset err_open_txt, offset err_open_cap, MB_ICONSTOP
    call    EndDialog, __hwnd, NULL
    call    ExitProcess

tutto_ok:
    mov     dword ptr handlez, eax

----------==========----------

Ho usato VirtualAlloc per riservare lo spazio dove vengono accodati i nomi dei file, questo sia per rendere la dll + piccola, sia perche' in questo modo si puo' riservare lo spazio che si vuole (in 10k stanno moltissimi nomi accodati) senza avere il problema di non averne abbastanza.
VirtualAlloc, infatti, puo' riservare una zona di memoria nel virtual address space del processo chiamante. Ecco la sintassi.

LPVOID VirtualAlloc(

LPVOID lpAddress,        // indirizzo della regione da riservare
DWORD dwSize,        // grandezza della regione (in byte)
DWORD flAllocationType,    // tipo di allocazione
DWORD flProtect         // tipo di protezione
);

Il 1� par se lasciato a NULL dice a Win di determinare da solo la regione da allocare. Il 2� par indica in byte la grandezza della regione, il 3� il tipo di allocazione (con MEM_COMMIT la regione viene allocata in modo da poter essere utilizzata subito), il 4� par indica che tipo di accesso desideriamo.
Il valore di ritorno e' un puntatore alla regione allocata.
Una volta che la regione non ci serve + bastera' liberarla con VirtualFree.
Allocato lo spazio necessario, bisogna scrivere la routine che trova uno ad uno tutti i file cnm e li accoda lasciando tra un nome e l'altro un byte NULL; questo e' stato possibile utilizzando 2 particolari API, FindFirstFileA e FindNextFileA.
Eccone di seguito la sintassi.

HANDLE FindFirstFile(

LPCTSTR lpFileName,            // puntatore al nome file da cercare
LPWIN32_FIND_DATA lpFindFileData     // puntatore alle info di ritorno
);

BOOL FindNextFile(

HANDLE hFindFile,            // handle di ricerca
LPWIN32_FIND_DATA lpFindFileData     // puntatore alle info di ritorno
);

La prima cerca in una dir un file il cui nome corrisponda a quello passato nel 1� parametro (nella dll lpFileName e' una stringa del tipo 'c:\pmail\mail\*.cnm', da notare che vengono accettati i caratteri jolly), mentre il 2� punta a una particolare struttuta di questo tipo:

typedef struct _WIN32_FIND_DATA { // wfd
DWORD dwFileAttributes;
FILETIME ftCreationTime;
FILETIME ftLastAccessTime;
FILETIME ftLastWriteTime;
DWORD nFileSizeHigh;
DWORD nFileSizeLow;
DWORD dwReserved0;
DWORD dwReserved1;
TCHAR cFileName[ MAX_PATH ];
TCHAR cAlternateFileName[ 14 ];
} WIN32_FIND_DATA;

Di cui a noi interessa cFileName, che quindi si trova a 44 byte (0x2C) dall'inizio della struttura stessa (si tratta di una stringa NULL-terminated), visto che a sua volta FILETIME e' una truttura del tipo

FILETIME        struct
ft_dwLowDateTime    DWORD    0         ;low-order 32 bits
ft_dwHighDateTime    DWORD    0         ;high-order 32 bits
FILETIME        ends

Per quanto riguarda FindNextFileA, invece, essa ha bisogno dell'handle di ricerca restituito da FindFirstFileA e del solito puntatore per le info di ritorno. Quando non trova + file, restituisce NULL.

Per quanto riguarda copacabana, essa sara' utile nel decrypt per riazzerare la struttura FIND_DATA.

Fatto questo sara' necessario, dopo aver aperto uno a uno i file, leggerli, decrittarli e sovrascriverli con le solite API CreateFileA, GetFileSize (per allocare il buffer necessario contenere i dati), ReadFile, SetFilePointer, WriteFile e CloseHandle, ecco la parte di codice interessata:

----------==========----------

    call     GetFileSize, dword ptr handlez, NULL
    mov    dword ptr sizemail, eax

    call    VirtualAlloc, NULL, eax, MEM_COMMIT, PAGE_READWRITE
    mov    databuffer, eax

    call     ReadFile, dword ptr handlez, databuffer, dword ptr sizemail, offset dummy, NULL

    mov    ecx, dword ptr sizemail
    mov    dl, byte ptr [Key]
    mov    esi, databuffer

xora:
    xor    byte ptr [ecx+esi], dl
    dec    ecx
    test    ecx, ecx
    jnz    xora

    mov     dword ptr dummy, 0

    call    SetFilePointer, handlez, NULL, NULL, FILE_BEGIN
    call     WriteFile, handlez, databuffer, dword ptr sizemail, offset dummy, NULL

    call     CloseHandle, handlez
    call    VirtualFree, databuffer, sizemail, MEM_DECOMMIT

    dec     count
    jnz    girazzo

    call    VirtualFree, nomifile, 10000, MEM_DECOMMIT

    ret
Crypt Endp

----------==========----------

Da notare l'elementare operazione di decrypt (se e' degna di tale nome) che semplicemente xora ogni byte di ogni file col 1� char inserito.
Quanto il file e' stato riscritto si chiude l'handle relativo e (tramite count) se tutti i file sono a posto si esce dalla call, altrimenti si passa al file successivo.

Per quanto riguarda invece la funzione di decrypt, in questo caso essa e' contenuta in MailCrypt, solo che inizia da copacabana, quindi essa semplicemente rimanipola tutta la mailbox nella stessa maniera iniziale e, una volta finito, torna all'exe chiamante, questo e' possibile farlo solo in *questo* caso, se le operazioni di encrypting fossero state + complicate si sarebbe dovuto scrivere una procedura apposita di decrypt (come ho gia' detto)... spero almeno che facciate cosi';).

Prima di compilare la dll sara' necessario scrivere un file def che contenga le funzioni che vogliamo esportare

---------- Crypt.def ----------

LIBRARY Flaf
EXPORTS MailCrypt Func

------------- EOF -------------

Bene... anche la dll e' stata scritta, ora basta semplicemente modificare l'exe in modo che appena si avvia il prg (diciamo pure all'entrypoint:) ) e prima di tutte le chiamate a ExitProcess (che per nostra fortuna sono solamente una) venga chiamata la nostra funzione MailCrypt, stando attenti a pushare 0x23 all'avvio e un qualsiasi valore != 23 alla fine, lo spazio lo trovate dove volete, qui non penso siano necessarie spiegazioni, ecco cmq la situazione di winpm-32.exe patchato completamente:

.004AA4CD: FF257C914B00 jmp d,[0004B917C]
.004AA4D3: FF2580914B00 jmp d,[0004B9180]
.004AA4D9: 0000 add [eax],al
.004AA4DB: 6A23 push 023
.004AA4DD: E8F1FFFFFF call .0004AA4D3 -------- (1);MailCrypt
.004AA4E2: A117B14A00 mov eax,[0004AB117]
.004AA4E7: E9196BF5FF jmp .000401005 -------- (2);Si torna all'inizio
.004AA4EC: 0000 add [eax],al
.004AA4EE: 6A00 push 000
.004AA4F0: E8DEFFFFFF call .0004AA4D3 -------- (3);MailCrypt
.004AA4F5: 6A00 push 000
.004AA4F7: E86CF8FFFF call .0004A9D68 -------- (4);ExitProcess
.004AA4FC: 0000 add [eax],al


All'inizio avviene un jmp a 4AA4DB, alla fine un jmp a 4AA4EE. Sarebbe meglio anche aggiungere pure l'istro a 4AA4E2 che e' stata sovrascritta all'entrypoint dal jmp 4AA4DB.

Non resta che crittare la mailbox, patchare l'exe, implementare come meglio si desidera la dll, lanciare l'exe e divertirsi mentre come per magia nessuno riesce piu' a trovare gli url di quelle 10 foto che ogni giorno la vostra amata ml porno vi invia;).

Fine.

Non mi pare il caso di dilungarsi su come ottenere lo stesso risultato tramite un exe, la cosa e' molto meno funzionale, almeno a parer mio, bisognerebbe eseguire il nostro exe, crittare la mail, avviare il processo di winpm-32.exe, aspettare che finisca, rimettere tutto a posto e uscire... la dll mi sembra un modo + pulito di operare.

Considerazioni finali e saluti

Non penso proprio che vi siate divertiti troppo a leggere questo tute;) cmq chi e' arrivato in fondo avra' capito (se ce ne fosse ancora bisogno) che non sempre (o, meglio, quasi mai) i programmi sono come li vogliamo noi, quindi per quanto possibile cercate di far fare al software quello che *voi* volete. Se una funzione di un prg non vi piace e serve soltanto a rallentarlo, toglietela, se vi piacerebbe poter usufruire di una funzione non implementata, aggiungetela voi, insomma il reverse engineering serve proprio a questo, abbiamo a disposizione programmi veramente potenti per far cio', da mostri come IDA a prog. apparentemente piu' semplici come HIEW, senza il quale pero' ad esempio il lavoro sulla IT sarebbe stato mooolto piu' difficile (provate voi a farlo solo con HexWorkShop) e che ci permettono di fare praticamente quello che vogliamo, senza dover limitarci al solito cambiamento jz/jnz/jmp/nop... personalmente lavori di questo tipo devo ammettere che mi interessano piu' del puro cracking delle varie protezioni dei prgz, salvo naturalmente casi particolari.
Detto questo, passiamo ai dovuti saluti finali.

Innanzi tutto devo ringraziare enormemente Kill3xx, per il fatto che ha sempre una risposta a ogni domanda (e non gliene faccio poche) ed e' pure sempre estremamente disponibile a starmi a sentire... in questa situazione, in particolare, per quanto riguarda la sistemazione della IT e per l'utilissimo testo indicato all'inizio di questo tute (gran parte delle info sulla IT sono state prese da li'). Grazie di tutto killo:).
Una grande tnx anche a Yado per i vari suggerimenti dati (auguri anche per il crypter).

Un saluto poi va a Quequero, [aLT255], TiN_MaN, LittleJohn, BlackDruid, +MaLaTTiA, NeuRaL_NoiSE, Brigante, cod, xOA, AndreaGeddon, Int19, GuZuRa (il 1� risolutore del mio crkme), Spin0ne, GR!SU (che non sento da un po'), ZeroByte, BUG,  NikDH etc etc etc.

Byz all!!

Ritz

Disclaimer

Le informazioni contenute in questo file sono a puro scopo didattico. L'autore non incoraggia chi volesse utilizzarle a scopi illegali.

UIC's page of reverse engineering, scegli dove andare:
Home   Anonimato   Assembly    CrackMe   ContactMe   Forum   Iscrizione
Lezioni    Links   Linux   NewBies   News   Playstation
Tools   Tutorial   Search   UIC Faq
UIC