Gemini Ver. 1.50
Gemini e' un sistema di protezione per l'ambiente DOS prodotto dalla TC Informatica (non mi risulta che tale sistema venga anche adoperato per l'ambiente Windows, quindi se avete qualche notizia in merito prego contattarmi tramite e-mail).
Principalmente il sistema possiede 3 tipi di protezione:
1. Disco chiave
2. Criptazione degli eseguibili
3. Compressione degli eseguibili
Possono essere adoperate anche tutte e tre le protezioni contemporaneamente.
Nel caso in cui venga aggiunta la prima protezione il software all'avvio richiede l'inserimento di un disco chiave.
La preparazione di questo discho chiave si basa principalmente su una stringa scelta dall'utente/programmatore interessato a proteggere il software.
Con la stessa stringa si ottengono anche con software diversi lo stesso tipo di disco chiave e quindi, puo' essere usato comodamente con piu' programmi. La duplicazione di questo disco e' resa impossibile dalla presenza di settori danneggiati fisicamente.
Spesso e volentieri tale tipo di protezione viene accompagnato dalla criptazione degli eseguibili la cui decriptazione viene eseguita durante l'esecuzione del programma.
Il limiti imposto da questa criptazione e' costituito dalla quantita' massima di memoria convenzionale a disposizione sul sistema.
La rimozione del disco chiave e' piuttosto semplice ma cio' che impedisce di scrivere una patch per questo programma e' la criptazione dell'eseguibile.
L'ideale sarebbe quindi usare un software TSR che apporti tali modifiche.
Alla fine della decriptazione dell'eseguibile viene passato il controllo alle istruzioni che controllano la presenza del disco chiave tramite un salto FAR a SEGMENTO:0000
Le istruzioni che troveremo saranno le seguenti:
:0000 B8 00 30 MOV AX, 3000 ; Acquisisce il numero di
:0002 CD 21 INT 21h ; versione del
; sistema operativo
Successivamente viene determinato dalle routine il numero dell'unita' da cui viene eseguito il programma con una chiamata all'INT 21 funzione 19
Addentrandoci con SoftIce all'interno del programma giungeremo alle seguenti istruzioni:
:1055 50 PUSH AX
:1056 E8 F8 FD CALL 0E51
:1059 83 C4 04 ADD SP, 4
:105C 3D 05 00 CMP AX, 0005
:105F 76 0F JBE 1070
La routine che verifica la presenza o meno del disco chiave richiede come passaggio di parametri due WORD, di cui l'ultima viene contiene nella parte bassa il numero dell'unita' logica su cui leggere la chiave. A noi pero' non interessa affatto quali sono i parametri passati a tale sub-routine ma bense' bypassare questi controlli e patchare i software.
Pertanto come fare?
L'unica soluzione e' quella di inserire un TSR che faccia questo lavoro ogni volta che noi mandiamo in esecuzione qualche software protetto con il disco chiave.
Conoscendo l'uso indiscrimanato dell'INT 21 che ne fa questo programma (se eseguite il codice passo passo con SoftIce o con qualsiasi altro debugger ve ne accorgerete, ma consiglio vivamente SoftIce in quanto spesso si trova qualche chiamata all'INT 03 sparsa all'interno del codice) l'ideale sarebbe approfittarne anche noi.
Abbiamo diverse soluzioni.. scegliete quella che fa comodo piu' a voi!
19 - GetCurrentDrive
25 - SetInterruptVector
35 - GetInterruptVector
Queste sono quelle che mi vengono al momento in mente, e che si ritrovano nel codice della protezione.. ma la 25 e la 35 le sconsiglio vivamente in quanto vengono utilizzate spesso anche all'interno dei software per modificare la tabella dei vettori di interrupt anche se preferisco di gran lunga l'approcio manuale per modificare tale tabella scrivendo direttamente i byte interessati nella memoria.
Riprendendo il discorso principale resta a nostra disposizione la funzione 19
In poche parole il nostro TSR funziona cosi':
Legge dalla Interrupt Table la DWORD che punta all'INT 21
Salva all'interno del proprio codice questa DWORD
Modifica l'Interrupt Table facendo puntare l'INT 21 al proprio codice
Termina il programma tramite la funzione 31 specificando in AL il codice di uscita (il valore di ritorno di solito 1 tutto ok 0 errore) e in DX la quantita' di memoria richiesta in paragrafi (64*16=1024 sono pure troppi!)
L'esecuzione del software da sproteggere non viene eseguita in quanto questo sistema e' valido per tutti gli eseguibili protetti con tale versione di Gemini (... con lo stesso TSR sono stati sprotetti parecchi software di un sviluppatore di cui non faccio il nome e spero vivamente che non legga questo breve tutorial... altrimenti mi becco una denuncia da 2 societa' :( )
Ora analizziamo cosa deve fare l'INT 21:
Controllo del valore nel registro AH
Se il valore in AH e' diverso da 19 passiamo il controllo a INT21 con un bel FAR JUMP
AH contiene il valore da noi desiderato ? (... 19 o 30 per verificare se gia' presente...) Si... bene ora dovremmo controllare i segmenti alla ricerca del codice da patchare.. ma per nostra fortuna la protezione e' una semplice immagine binaria in cui il registro DS e ES hanno lo stesso valore di CS.. quindi non ci resta che controllare direttamente i codici presenti in ES:[1055] e byte successivi. Se tale codice e' uguale a quello da noi trovato... cosa aspettiamo a modificarlo?
Apportate le relative modifiche.
Possiamo sostituire in :105F il JBE 1070 in un SHORT JUMP rappresentato da EB xx dove xx esprime il valore da sommare o sottrarre al registro IP
Se bisogna sommare o sottrarre viene specificato nel 7. bit
0 Addizionare
1 Sottrarre
Gli altri 7 bit (6..0) stabiliscono il valore
Possiamo sostituire :1056 (la CALL) con 3 NOP oppure con MOV AX, 05 che si traduce con B8 05 00 (evitando l'accesso al floppy disk!)
Insomma possiamo fare quello che piu' ci piace, l'importante e' che sia fatto il JUMP a 1070.
Ulteriori informazioni su GEMINI:
Durante la protezione viene richiesto un livello che va da 1 a 5 per cambiare il valore ogni volta... ma e' solo un banale CMP AX, 0005 quindi se facciamo un CMP AX, AX (39 C0) la condizione sara' sempre soddisfatta!
Io scelgo quest'ultimo metodo... il CMP AX, AX ed elimino la CALL che e' fastidiosa!
-----------------------------------------------------------------------------
; Gemini 1.5 Patch
;
; Chi lo desidera... se lo programmi per esercizio il controllo della riga
; di comando x verificare se ci sono parametri per rimuovere dalla memoria
; il patch!
..MODEL TINY
..8086
..CODE
ORG 100h
start:
PUSH CS ; Salva CS
POP DS ; Ripristina DS.. che sara' CS
MOV AH, 09h ; Stampa una stringa
LEA DX, msg ; Puntata da DS:DX e terminante con $
INT 21h ; Esegue
MOV AH, 30h ; Legge la versione del sistema
MOV AL, 90h ; Bau! NOP NOP
INT 21h ; operativo... Restituisce in AX
CMP CX, 0000h ; la versione e BX e CX 0
JNE EndProgram ; Quindi se CX non e' 0 ci sta il crack!
;
; Sei ancora qui ?? Allora devo mettere la mia routine!
XOR AX, AX
PUSH AX
POP DS
MOV AX, WORD PTR DS:[21h*4] ; Legge l'offset dell'INT 21
MOV WORD PTR CS:[Off21], AX ; Lo salva
MOV AX, WORD PTR DS:[21h*4]+2 ; Legge il segmento dell'INT 21
MOV WORD PTR CS:[Seg21], AX ; Lo salva
MOV AX, OFFSET NEWINT21 ; Carica in AX l'indirizzo del ns
; gestore INT21
CLI ; Disabilitiamo gli INTERRUPT
MOV WORD PTR DS:[21h*4], AX ; Salviamo l'offset
MOV WORD PTR DS:[21h*4]+2, CS ; Salviamo il segmento
STI ; Riabilitiamo gli INTERRUPT
; Ok... ora l'INT 21 e' ns!
MOV AX, 3101h ; Esci e resta in memoria
MOV DX, 0020h
INT 21h
EndProgram:
RET
NEWINT21:
PUSHF ; Ebbene si! i flag e' meglio non
; toccarli
CMP AH, 19h
JZ CheckCODE ; Patchare... vediamo un po
CMP AH, 30h ; Ah == 30h
JNZ FarJump ; Salta se non e' uguale
CMP AL, 90h ; AL == 90h
JNZ FarJump ; Non e' uguale... forse e' meglio
; lasciare il controllo ad OS
MOV CX, 0001h ; .. BX 0001
POPF ; Ripristina i flag
IRET ; Termina l'INT
; Ma allora me lo fate apposta... vediamo un po' se troviamo quello che vogliamo
CheckCODE:
PUSH ES ; Ok salviamo tutti i registri
PUSH DS ; che e' utile
PUSH AX ; Possiamo usare PUSHA e POPA
PUSH BX ; ma perche' ci piace piu' PUSH
PUSH CX ; e POP
PUSH DX ;
PUSH SI ;
PUSH DI ;
PUSH CS
POP DS ; DS:SI codice di gemini
LEA SI, gemini_code ; da trovare...
MOV DI, 1055h ; ES:SI codice da controllare
MOV CX, 06h ; 06x02 -> 12 byte da controllare
REP CMPSW ; Controlla 6 WORD! :
JNE EsciDaQui ; NON SONO UGUALI!
LEA SI, my_code ; Invece sono uguali!
MOV DI, 1055h ; e mo lo patchiamo
MOV CX, 06h ; 12 byte
REP MOVSW ; Ciao ciao protezione!!
EsciDaQui:
POP DI ; Ripristiniamo i registri che
POP SI ; avevamo salvato
POP DX ;
POP CX ;
POP BX ;
POP AX ;
POP DS ;
POP ES ;
FarJump: POPF ; OK.. ripristina i flag
FarJMP: DB 0EAh ; Far JUMP
Off21: DW ? ; Offset
Seg21: DW ? ; Segmento
msg:
DB 'GEMINI Ver 1.50 Patch!',0Ah,0Dh,'$' ; Ma perche' la stringa deve terminare
; con $ ? Che passava per la mente a
; quelli di Micro$oft (?? forse ho
; capito.. li pagavano male!)
DB 'cod'
gemini_code:
DB 050h ; Push AX - AX contiene il valore dell'unita' logica!
; On entry: BP+2 Valore dell'unita' logica
; BP+4 BOOOOOOOOO
DB 0E8h, 0F8h, 0FDh ; Call 0E51
DB 083h, 0C4h, 04h ; ADD SP, 4 - Incrementa il puntatore dello stack
; avendo passato alla funzione 2 parametri
; Ps: BP+2 e non BP+0 in quanto con una NEAR CALL
; viene aggiunta nello STACK una word di ritorno
DB 03Dh, 05h, 00h ; AX == 5 ??
DB 076h, 0Fh ; Se AX == 5 o minore di 5 vai a 1070
my_code:
DB 050h ; Push AX ... OK non mi serve ma lo lasciamo
DB 090h, 090h, 090h ; NIENTE CALL
DB 083h, 0C4h, 04h ; Ok... lasciamo stare lo stack ADD SP, 4
DB 039h, 0C0h, 090h ; AX == AX ? VERISSIMO!
DB 076h, 0Fh ; Se AX == AX o minore di AX vai a 1070 ??
; Che cosa ?? Non ci fate caso... funziona sempre
; in quanto i due valori confrontati sono identici
; dato che sono gli stessi registri !!
END start
-----------------------------------------------------------------------------