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

-----------------------------------------------------------------------------