Sort Reversing | ||
Data |
by "Winebag" |
|
07/01/2004 |
Published by Quequero | |
|
Grazie wine, io non c'avrei mica mai pensato a reversarmi il Sort :)) |
|
.... |
|
.... |
Difficoltà |
( )NewBies (x)Intermedio ( )Avanzato ( )Master |
Questa è una descrizione di un algoritmo di ordinamento, vista dalla prospettiva del reverser. Esso non ha NESSUNA utilità pratica, quindi non troverete come sproteggere un determinato programma, né sorgenti di particolare interesse, solo un piccolo esempio di come possa essere utilizzato il reversing per scopi un attimino diversi dal solito.
Introduzione |
Tools usati |
URL o FTP del programma |
C:\WINDOWS\Command\SORT.exe :-)
Notizie sul programma |
Questo comando serve ad ordinare una serie di stringhe, ottenute o da tastiera o da un file, e a visualizzarle sullo STDOUT(che può essere ad esempio rediretto ad un file). Per una spiegazione più approfondita: SORT /?
Essay |
les si, dword ptr
[bp+argv]
les bx, es:[bx+si]<--es:bx punta ad argv[i] dove i è
l'indice contenuto in una variabile locale
cmp byte ptr es:[bx], 2Fh ; '/'<--controlla se il primo
carattere è /
jz loc_0_A7C
loc_0_A7A:
jmp short loc_0_AEF<--se no
esce dal ciclo
loc_0_A7C:
mov al, es:[bx+1]<--in al il secondo carattere
cbw
cmp ax, 72h ; 'r'
jz loc_0_AC9
ja loc_0_A94
sub al, 2Bh ; '+'
jz loc_0_A9C
sub al, 14h ; '?'
jz loc_0_A59 <--stampa le opzioni di
sort e esce dal programma
sub al, 13h ; 'R'
jz loc_0_AC9
...
loc_0_AC9: ;
se è stato digitato /r o
/R ovvero se si vuole l'ordine inverso
mov bx, [bp+var_2]<-- questo contiene l'indice i
add bx, bx
add bx, bx<--moltiplica bx per 4, la dimensione degli
elementi dell'array argv, puntatori a stringhe(char *)
mov es, [bp+ArgvSegment]
les bx, es:[bx+si]<--questo punta alla stringa argv[i]
cmp byte ptr es:[bx+2], 0<--controlla che non contenga
nulla oltre a /r (o /R)
jnz loc_0_A94
or byte ptr [bp+var_4], 1<--setta un bit di questa
variabile locale, inizializzata a 0
jmp loc_0_A5C<--torna all'inizio del ciclo
...
loc_0_A9C:
; /+n ,con n numero intero indica
che si vuole partire dalla posizione n
mov bx, [bp+var_2]
add bx, bx
add bx, bx
les si, dword ptr [bp+argv]
mov ax, es:[bx+si]<--argv[i]
mov dx, es:[bx+si+2]<--il segmento puntato da argv
inc ax<--
inc ax<--salta i primi 2 caratteri, cioè /+
push dx
push ax
call atoi <--trasforma la stringa in un intero
add sp, 4
mov word_0_19C8, ax <--lo salva in questa variabile
cmp ax, 1
jb err <--se è <1 stampa un messaggio d'errore
dec word_0_19C8<--questo perché per la gente normale si parte da 1,
mentre ai programmatori serve partire da 0
or byte ptr [bp+var_4], 2<-- setta un bit nella variabile flag
jmp short loc_0_A5C<--torna all'inizio del ciclo
Dopo aver finito il ciclo, vengono usati gli altri parametri, per
eventualmente redirigere input o output, cosa che per i nostri scopi possiamo
anche tralasciare. Poco più in basso viene chiamata la funzione che effettuerà
tutto il lavoro:
mov bx, [bp+var_4]<--questa
è la variabile flag in cui si tiene conto delle opzioni selezionate
add bx, bx<--utilizzata come indice in un array di offset
mov ax, [bx+0AEh]<--questo array contiene indirizzi di 4
procedure
mov word_0_1A00, ax<--viene salvato quello che ci
interessa in una variabile che servirà dopo
call sub_0_7A2<--questa è la procedura che fa tutto
In questa procedura troviamo per prima cosa un ciclo che effettua l'input,
chiamando dopo alcuni passaggi il servizio 3Fh dell'interrupt 21h, che legge una
stringa(terminata da CR o LF) da un file (o dalla riga di comando), che verrà
salvata in un array, finché non trova il carattere 1Ah (EOF, da tastiera
CTRL+Z):
loc_0_7B1: ; qui
comincia il ciclo
sub ax, ax
mov [bp+var_8], ax
mov [bp+var_A], ax
cmp word_0_42, 2000h<--guarda che l'array non contenga
più di 2000h stringhe
jnb loc_0_822<--non ci preoccupiamo di questa
eventualità
lea ax, [bp+var_20A]<--buffer locale
push ss
push ax
call sub_0_2CC <--effettua l'input,mettendo la stringa
nello stack
add sp, 4
or ax, ax<--la funzione restituisce 0 quando trova l'EOF
jnz loc_0_822<--esci dal ciclo
mov ax, word_0_42
inc word_0_42<--incrementa il contatore
mov [bp+var_20C], ax<--e lo
salva in una variabile locale
lea ax, [bp+var_20A]
push ss
push ax
call sub_0_5B4E <--salva la stringa nello heap, eventualmente
allocando nuova memoria
add sp, 4
mov [bp+var_A], ax<--salva
l'indirizzo della stringa in una variabile locale
mov [bp+var_8], dx
mov es, word_1458<--segmento in cui si trova l'array di stringhe
mov bx, [bp+var_20C]<--utilizza il contatore come offset
add bx, bx
add bx, bx
mov es:[bx+0], ax<--e salva l'indirizzo della stringa
mov es:[bx+2], dx
or dx, ax
jnz loc_0_822<--esci dal ciclo in caso d'errore
...
Adesso che abbiamo sistemato il nostro input, non ci resta che ordinarlo; vediamo come viene invocata la funzione che effettuerà questo lavoro:
loc_0_822:
mov ax, [bp+var_8]
or ax, [bp+var_A]
jnz loc_0_7B1<--se è uscito dal ciclo a causa di un
errore
cmp word_0_42, 0
jz loc_0_89B<--array vuoto
cmp word_0_42, 1
jbe loc_0_852<--array con un solo elemento, non ha senso
ordinarlo
push word_0_1A00<--qui
avevamo salvato precedentemente l'offset di una funzione, a seconda dell'opzione
selezionata
mov ax, 4
push ax<--dimensione di ogni oggetto dell'array
push word_0_42<--lunghezza
mov ax, 0<--offset dell'array di stringhe da ordinare
mov cx, 5C7h<--segmento
push cx
push ax
call sub_0_D88<--questa effettuerà l'ordinamento
In realtà questa procedura si limiterà ad aggiustare i parametri per
passarli alla funzione di offset B9Ch, che ho rinominato sort:
push bp
mov bp, sp
cmp [bp+arg_4], 1
jbe loc_0_DAD
push [bp+arg_8]<--offset della funzione selezionata
push [bp+arg_6]<--dimensione di ogni oggetto
mov ax, [bp+arg_4]<--lunghezza
dec ax
mul [bp+arg_6]
add ax, [bp+arg_0]<--offset array[lunghezza-1], ovvero dell'ultimo
elemento
mov dx, [bp+arg_2]<--segmento
push dx
push ax
push dx
push [bp+arg_0]<--offset del primo elemento
call sort
; cs:0B9Ch
...
Prima di analizzare a fondo l'algoritmo, diamo uno sguardo alle funzioni di
supporto. La funzione sort si serve di una procedura per confrontare due oggetti
(variabile a seconda delle opzioni) una che scambia due oggetti, e una che
verifica se l'array è ordinato, restituendo TRUE o FALSE (1 o 0).
Dal momento
che questo programma è a 16 bit, un puntatore è definito da due variabili:
segmento e offset, quindi nel corso delle spiegazioni mi riferirò sempre al
concetto astratto di puntatore, perciò dirò ad esempio che una funzione
riceve 2 puntatori, mentre in realtà riceve 4 valori.
Detto questo possiamo passare al codice: la prima funzione che vediamo è quella
di offset 9Ch, che prende in input due stringhe e restituisce 0 se sono uguali,
un valore negativo se la prima precede la seconda, viceversa negativo
analogamente a strcmp del C, ma non nello stesso modo:
loc_0_A4:
les bx, [bp+arg_4]<--puntatore alla seconda stringa
inc word ptr [bp+arg_4]
mov bl, es:[bx]<--i-esimo carattere
sub bh, bh
les si, dword_0_19CA<--??
les si, [bp+arg_0]<--??
les di, dword_0_19CA<--array di 256 char
mov al, es:[bx+di]
<--elemento dell'array usando
l'i-esimo carattere come indice
mov cx, es
les bx, [bp+arg_0]<--prima stringa
mov bl, es:[bx]<--i-esimo carattere della prima stringa
sub bh, bh
mov es, cx
mov dl, es:[bx+di]<--utilizzato come indice
sub dh, dh
sub ah, ah
sub dx, ax<--primo meno secondo
jnz loc_0_DF<--se diversi esci dal ciclo
les bx, [bp+arg_0]
inc [bp+arg_0]
cmp es:[bx], ah
jnz loc_0_A4<--continua fino alla fine della prima
stringa
loc_0_DF:
mov ax, dx<--restituisce la differenza
...
Direi che è tutto abbastanza chiaro, l'array ausiliario è costruito in modo da equiparare maiuscole e minuscole: ad esempio agli indici corrispondenti ai valori ASCII di e,E,è,é si trova sempre la E. Questa funzione non viene invocata direttamente da sort, ma da una di quelle 4 funzioni di cui abbiamo parlato in precedenza che andiamo ad analizzare partendo dal caso più semplice, ovvero quando non ci sono opzioni selezionate:
sub_0_91A:
push bp
mov bp, sp
les bx, [bp+arg_4]<--secondo parametro, di tipo char **
cioè l'indirizzo dell'array di puntatori
push word ptr es:[bx+2]<--viene pushato il primo
puntatore puntato
push word ptr es:[bx] <--cioè una stringa
les bx, [bp+arg_0]
push word ptr es:[bx+2]<--lo stesso per il primo
parametro
push word ptr es:[bx]
call cmp
<--la funzione vista sopra a cui vengono passati
i parametri nello stesso ordine
mov sp, bp
pop bp
retn
Il codice è abbastanza semplice, anche se non ho spiegato molto bene la faccenda dei puntatori; aggiungo una nota per chi ha poca dimestichezza col c: quando parleremo dei puntatori utilizzati da sort, ci riferiremo al tipo char**, dato che sort ordina un array di stringhe, cioè un array di indirizzi di array di caratteri, ogni variabile puntatore rappresenta l'indirizzo di uno specifico elemento di questo array; sub_0_91A deve però passare a cmp una stringa, ovvero un indirizzo di un array di char, ovvero un elemento dell'array su cui lavora sort, quindi spiegato perché non si limita a passare quello che riceve. Adesso vediamo come si comporterebbe se avessimo selezionato l'opzione /r ovvero ordinamento inverso:
les bx, [bp+arg_0]
push word ptr es:[bx+2]
push word ptr es:[bx]
les bx, [bp+arg_4]
push word ptr es:[bx+2]
push word ptr es:[bx]
call cmp<--uguale a sopra ma con i parametri invertiti
Questo se avessimo scelto l'opzione /+n, ovvero ordina a partire dall'n-esimo carattere
sub_0_956:
push bp
mov bp, sp
sub sp, 0Ah
les bx, [bp+arg_0]<--prende il primo parametro
mov ax, es:[bx]<--ottiene il puntatore al primo carattere
della stringa
mov dx, es:[bx+2]
mov [bp+var_6], ax
mov [bp+var_4], dx
push dx
push ax
call strlen
<-- calcola la lunghezza
add sp, 4
mov [bp+var_2], ax<--e la mette in una variabile
locale
mov ax, word_0_19C8<--questo è il valore di n salvato in
precedenza
cmp [bp+var_2], ax
jbe loc_0_982<--salta se strlen<=n
mov [bp+var_2], ax
<--altrimenti sovrascrive la variabile
locale con n
loc_0_982:
mov ax, [bp+var_2]<--prende il contenuto di questa
variabile, ovvero il minore tra strlen e n
add [bp+var_6], ax<--e lo
usa per spostare il puntatore(se
la stringa è più corta di n, punterà a una stringa vuota)
les bx, [bp+arg_4]<--lo stesso per la seconda stringa
mov ax, es:[bx]
mov dx, es:[bx+2]
mov [bp+var_A], ax
mov [bp+var_8], dx
push dx
push ax
call strlen
add sp, 4
mov [bp+var_2], ax
mov ax, word_0_19C8
cmp [bp+var_2], ax
jbe loc_0_9AE
mov [bp+var_2], ax
loc_0_9AE:
mov ax, [bp+var_2]
add [bp+var_A], ax
push [bp+var_8]<--questo è il puntatore all'n-esimo char
della seconda
push [bp+var_A]<--(o a una stringa vuota)
push [bp+var_4]<--e questo si riferisce alla prima
push [bp+var_6]
call cmp
<--questa effettuerà il confronto
Se avessimo scelto contemporaneamente le due opzioni, la funzione di confronto sarebbe stata questa:
sub_0_9C8:
push bp
mov bp, sp
push [bp+arg_6]
push [bp+arg_4]
push [bp+arg_2]
push [bp+arg_0]
call sub_0_956
<--pusha i parametri nello stesso ordine alla funzione
vista sopra
neg ax<--ma inverte il risultato
mov sp, bp
pop bp
retn
Adesso diamo un occhiata alla funzione che scambia due elementi dell'array di stringhe, all'offset DB2:
mov cx, [bp+arg_8]<--questa è la dimensione
in byte degli elementi, in questo caso 4 trattandosi di puntatori
jcxz loc_0_DDD<--esci se è nulla
lds si, [bp+arg_0]
les bx, [bp+arg_4]
shr cx, 1
jnb loc_0_DD1<--salta se è pari
lodsb<--altrimenti scambia il primo byte
xchg al, es:[bx]<--utilizzando al come variabile temporanea
inc bx
mov [si-1], al
jcxz loc_0_DDD
loc_0_DD1:
lodsw<--scambia coppie di word
xchg ax, es:[bx]
add bx, 2
mov [si-2], ax
loop loc_0_DD1<--fino alla fine
Adesso vediamo la funzione che verifica se l'array è ordinato, essa
riceve in input gli indirizzi del primo e dell'ultimo elemento del tratto di
array da controllare, la dimensione in byte degli elementi, e l'offset della
procedura di confronto:
push bp
mov bp, sp
jmp short loc_0_B8E<--salta all'interno del ciclo
; --------------------------------------
loc_0_B6F:
; inizio del ciclo
mov dx, [bp+arg_2]
add ax, [bp+size]
push dx
push ax
push dx
push [bp+arg_0]
call [bp+compare]<--confronta un elemento con il suo
successivo
mov sp, bp
mov sp, bp
or ax, ax
jle loc_0_B88<--se è minore o uguale continua
xor ax, ax<--altrimenti non è ordinato e restituisce 0
pop bp
retn
; -----------------------------------
loc_0_B88:
mov ax, [bp+size]
add [bp+arg_0], ax
loc_0_B8E:
mov ax, [bp+arg_0]<--offset dell'elemento da cui deve
partire, incrementato ad ogni ciclo
cmp [bp+arg_4], ax<--offset dell'ultimo elemento
ja loc_0_B6F<--se non è arrivato alla fine ripete il
ciclo
mov ax, 1<--se ha finito e non è uscito prima, allora è
ordinato
pop bp
retn
Ora che abbiamo visto le procedure di supporto possiamo passare ad analizzare a fondo l'algoritmo. La funzione sort prende come argomenti: l'indirizzo del primo e dell'ultimo elemento dell'array da ordinare(F e L),la dimensione (4), e l'indirizzo della funzione di confronto(scelto tra le 4 illustrate sopra) e utilizza come variabili locali 3 puntatori: uno che percorre l'array dall'inizio, uno dalla fine (chiamati da me con molta fantasia I e J :-) e uno che identifica un elemento in mezzo(M). Per semplicità userò il simbolo <<, per indicare che una stringa precede un'altra; A<<B vuol dire compare(A,B)<0, quindi che la stringa *A precede in ordine alfabetico *B, in alcuni casi potrei utilizzare tale scrittura direttamente con le stringhe: A<<B vorrà dire compare(&A,&B)<0, ma sarà chiaro dal contesto a quale delle 2 mi starò riferendo.
push bp
mov bp, sp
sub sp, 0Eh
push di
push si
mov [bp+xchg], offset xchg<--abbiamo visto sopra come
funziona
cmp [bp+size], 2
jz loc_0_BB2<--??
jmp loc_0_D74<--salta all'ingresso del ciclo
loc_0_BB2:
mov [bp+xchg], offset xchg
jmp loc_0_D74
...
loc_0_D74:
mov ax, [bp+L]
mov cx, [bp+F]
cmp ax, cx
jbe RET
jmp loop<--continua il ciclo se F<=L
while(F<=L)
...
loop:
mov bx, [bp+segF]
mov [bp+I], cx ;I=F
mov [bp+segI], bx
mov si, ax
mov di, [bp+segL]
mov [bp+J], ax ;J=L
mov [bp+segJ], di
sub ax, cx<-distanza tra I e J
sub dx, dx
div [bp+size]<--numero di elementi tra I e J
shr ax, 1<--diviso 2
mul [bp+size]<--ritorna ad essere una distanza in byte
add ax, cx<--e viene aggiunta all'offset del primo
mov [bp+M], ax <--quindi M punterà
all'elemento fisicamente a metà
mov [bp+segM], bx<--(in caso di numero di elementi pari,
al primo dei 2 medi)
push bx
push ax
push di
push si
mov si, ax
mov di, bx
call [bp+compare]
; compare(J,M)
add sp, 8
or ax, ax
jge loc_0_C05<--se J>>M va tutto bene
push [bp+size]<--altrimenti scambia *J e *M (L e J a
questo punto sono equivalenti)
push di
push si
push [bp+segL]
push [bp+L]
call [bp+xchg]
; xchg (L,M)
add sp, 0Ah
loc_0_C05:
push [bp+segI]
push [bp+I]
push [bp+segJ]
push [bp+J]
call [bp+compare]
; compare(J,I)
add sp, 8
or ax, ax
jge loc_0_C30<--se J>>I OK
push [bp+size]<--se no
bisogna scambiarli
push [bp+segI]
push [bp+I]
push [bp+segJ]
push [bp+J]
call [bp+xchg]
; xchg(J,I)
add sp, 0Ah
loc_0_C30:
push [bp+segI]
push [bp+I]
push [bp+segM]
push [bp+M]
call [bp+compare] ; compare(M,I)
add sp, 8
or ax, ax
jge loc_0_C5B<--lo stesso per M ed I
push [bp+size]
push [bp+segI]
push [bp+I]
push [bp+segM]
push [bp+M]
call [bp+xchg]
; xchg(M,I)
add sp, 0Ah
loc_0_C5B: ; CODE XREF:
mov ax, [bp+J]
sub ax, [bp+I]
mov cx, [bp+size]
add cx, cx
cmp ax, cx
jbe Ret_
; j-i <= 2*size
<--esci se ci sono
3 o meno di 3 elementi
push [bp+compare]
push [bp+size]
push [bp+segJ]
push [bp+J]
push [bp+segI]
push [bp+I]
call Sorted
<--guarda se per qualche ragione è già
ordinato
add sp, 0Ch
or ax, ax
jz loc_0_C89
<-- se no ti tocca ordinarlo!!
Ret_:
jmp RET
Il codice qui sopra si riferisce al ciclo più esterno, che continua finché F<=L, ovvero finché non è stato ordinato tutto; all'inizio di ogni ciclo inizializza I e J rispettivamente a F e L, poi assegna a M l'indirizzo dell'elemento a metà nel modo visto sopra; quindi opera tre confronti in modo da ordinare *I,*M e *J.
I | M | J |
Dove le celle bianche <<=M, mentre quelle nere >>M.
Se ci fossero solo questi 3 elementi o meno, oppure se l'array fosse ordinato la funzione ritornerebbe, altrimenti continuerebbe ad eseguire il codice che stiamo per andare a vedere. Anche se può non sembrare troppo utile questa serie di controlli, è quello che assicura il corretto funzionamento di un algoritmo ricorsivo(come vedremo in seguito): questa funzione è capace di ordinare array sufficientemente piccoli.
loc_0_C89:
mov ax, [bp+size]
add [bp+I], ax
; i++ <--incrementa il puntatore i
della grandezza di un elemento
mov ax, [bp+I]
cmp [bp+J], ax
ja loc_0_CCA
; if i<j <--se l'array non è
stato ancora percorso tutto
...
loc_0_CCA:
push [bp+segM]
push [bp+M]
push [bp+segI]
push ax
call [bp+compare]
; compare(I,M)
add sp, 8
or ax, ax
jle loc_0_C89
;if(I<<=M)
Questo ciclo continua finché non trova un elemento >>M, nel disegno finché non trova una casella nera (a dire il vero c'è anche l'eventualità che esca perché I ha raggiunto J, ma di questo ce ne preoccupiamo dopo). Faccio notare che M essendo <<=M punta in realtà ad una casella bianca.
I | M | J |
loc_0_CDE:
mov ax, [bp+I]
cmp [bp+J], ax
jbe loc_0_C8F
; if i>=j <--esci dal ciclo se i
ha raggiunto j
push [bp+segM]
push [bp+M]
push [bp+segJ]
push [bp+J]
call [bp+compare]
; compare(J,M)
add sp, 8
or ax, ax
jg loc_0_D33
<--continua se J>>M
...
loc_0_D33: ; CODE XRE
mov ax, [bp+size]
sub [bp+J], ax
; j--
<--fa puntare J all'elemento precedente
jmp short loc_0_CDE<--torna all'inizio del ciclo
Questo ciclo lavora dall'altra parte finché non trova un "bianco":
I | M | J |
A questo punto, se non ha finito l'array, si troverà con un bianco dove serve un nero, e viceversa e quindi occorrerà scambiarli:
push [bp+size]
push [bp+segJ]
push [bp+J]
push [bp+segI]
push [bp+I]
call [bp+xchg]
; xchg(I,J)
add sp, 0Ah
mov ax, [bp+J]
mov dx, [bp+segJ]
cmp [bp+M], ax
jnz loc_0_D21
cmp [bp+segM], dx
jz loc_0_D24
; if(J==M)
loc_0_D21:
jmp loc_0_C8F<--ricomincia a percorrere l'array con I
; --------------------------------------------------------
loc_0_D24:
mov ax, [bp+I]
<--Abbiamo detto che *M è bianco, quindi se
in questo scambio l'avessimo spostato
mov dx, [bp+segI]
mov [bp+M], ax
mov [bp+segM], dx
; M=I<--Dovremmo sistemare il puntatore
jmp loc_0_C8F
Quindi queste operazioni continueranno ciclicamente, e quando I avrà raggiunto J l'array sarà diviso in due porzioni:
I J |
mov ax, [bp+I]
cmp [bp+J], ax
ja loc_0_CCA
; if i<j
mov ax, [bp+L]<--qui I==J ed essi puntano al primo
elemento della parte nera
mov cx, [bp+I]
sub ax, cx ; L-I
sub cx, [bp+F]
; I-F
cmp ax, cx
jnb loc_0_CA9
<--se I si trova nella prima metà
jmp loc_0_D3B
<--se I si trova nella seconda metà
loc_0_CA9:
;la parte bianca è più corta
mov ax, [bp+F]
mov dx, [bp+segF]
mov [bp+I], ax
mov [bp+segI], dx
; i=f <--sposta I all'inizio
mov ax, [bp+J]
mov dx, [bp+segJ]
mov [bp+F], ax
; f=j <--Sposta il
proprio limite sinistro
a J (inizio parte nera)
mov [bp+segF], dx
mov ax, [bp+size]
sub [bp+J], ax
;J-- <--fa puntare J all'ultimo elemento
della parte bianca
jmp loc_0_D56
...
loc_0_D3B:
;la parte nera è più corta
mov ax, [bp+L]
mov dx, [bp+segL]
mov [bp+J], ax
mov [bp+segJ], dx
; j=l <--Sposta J all'ultimo posto
mov ax, [bp+I]
mov dx, [bp+segI]
sub ax, [bp+size]
mov [bp+L], ax
mov [bp+segL], dx
;l=i-1 <--Sposta il
proprio limite destro a
I-1(ultimo bianco)
loc_0_D56:
mov ax, [bp+I]
cmp [bp+J], ax
jbe loc_0_D74
<--se I==J evita di ordinarlo(tanto ha 1
solo elemento)
push [bp+compare]
; if i<j
push [bp+size]
push [bp+segJ]
push [bp+J]
push [bp+segI]
push ax
call sort
<--Ordina la parte definita sopra
add sp, 0Ch
loc_0_D74:<--continua a lavorare sull'altra parte
mov ax, [bp+L]
mov cx, [bp+F]
cmp ax, cx
jbe RET
<-- non credo che serva a molto a basso livello
jmp loop<--ciclo più esterno
A questo punto, avendo separato le due parti in questo modo, è possibile
ordinarle separatamente; la procedura sort valuta la lunghezza delle due parti e
tiene la più lunga per sè, mentre fa ordinare la più corta da un'altra
procedura sort, mediante una chiamata ricorsiva; quindi a forza di dividere in
due, si arriverà ad avere parti così piccole da essere ordinate con un solo
passaggio, per quanto visto sopra; in questo modo si può dimostrare che si
possono ordinare array di lunghezza arbitraria.
Così abbiamo finito l'analisi di quest'algoritmo che se non mi sbaglio si
chiama quicksort, ed è uno tra i più usati; adesso vi lascio con i sorgenti di
un programmino che ordina la sua riga di comando: funziona esattamente come sort,
a parte il fatto che non fa input né da file né da console, perché
giustamente non avevo voglia di sbattermi ulteriormente per ottenere un array di
stringhe quando c'è argv che è molto comodo; supporta anche le stesse opzioni
(/R /+n /?) a patto che le inseriate prima degli argomenti da ordinare, spero di
non aver fatto grossi errori come algoritmo,se poi c'è qualche piccolo bug, non
è certo un problema :-).
//----------------------------------------Sort.cpp---------------------------------------------
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int n;
typedef int(*COMP)(char **,char**);
char tab[256]={0x0,0x1,0x2,0x3,0x4,0x5,0x6,0x7,0x8,0x9,0x0,0xB,0xC,0xD,0xE,0xF,
0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F,
0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x29,0x2A,0x2B,0x2C,0x2D,0x2E,0x2F,
0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39,0x3A,0x3B,0x3C,0x3D,0x3E,0x3F,
0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4A,0x4B,0x4C,0x4D,0x4E,0x4F,
0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5A,0x5B,0x5C,0x5D,0x5E,0x5F,
0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4A,0x4B,0x4C,0x4D,0x4E,0x4F,
0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57,0x58,0x59,0x5A,0x7B,0x7C,0x7D,0x7E,0x7F,
0x43,0x55,0x45,0x41,0x41,0x41,0x41,0x43,0x45,0x45,0x45,0x49,0x49,0x49,0x41,0x41,
0x45,0x41,0x41,0x4F,0x4F,0x4F,0x55,0x55,0x59,0x4F,0x55,0x4F,0x24,0x4F,0x9E,0x24,
0x41,0x49,0x4F,0x55,0x4E,0x4E,0xA6,0xA7,0x3F,0xA9,0xAA,0xAB,0xAC,0x21,0x22,0x22,
0xB0,0xB1,0xB2,0xB3,0xB4,0x41,0x41,0x41,0xB8,0xB9,0xBA,0xBB,0xBC,0x24,0x24,0xBF,
0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0x41,0x41,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0x24,
0x44,0x44,0x45,0x45,0x45,0xD5,0x49,0x49,0x49,0xD9,0xDA,0xDB,0xDC,0xDD,0x49,0xDF,
0x4F,0x53,0x4F,0x4F,0x4F,0x4F,0xE6,0xE7,0xE8,0x55,0x55,0x55,0x59,0x59,0xEE,0xEF,
0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF};
int cmp(char *str1,char *str2){
int i=0;
char c1,c2;
do{
c1=tab[str1[i]];
c2=tab[str2[i]];
c1-=c2;
}
while(!c1 && str1[i++]);
return c1;
}
int compare(char **str1,char **str2){
return cmp(*str1,*str2);
}
int comparer(char **str1,char **str2){
return cmp(*str2,*str1);
}
int comparen(char **str1,char **str2){
char *s1,*s2;
int l=strlen(*str1);
s1=&(*str1)[(l<=n)?l:n];
l=strlen(*str2);
s2=&(*str2)[(l<=n)?l:n];
return cmp(s1,s2);
}
int comparenr(char **str1,char **str2){
return comparen(str2,str1);
}
bool sorted(char **a,char **b,COMP compare){
while(a<b && compare(a,a+1)<=0)a++;
return a>=b;
}
void xchg(char **a,char **b){
char *temp=*a;
*a=*b;
*b=temp;
}
void sort(char **f,char **l,COMP compare){
char **m,**i,**j;
while(f<=l){
i=f;
j=l;
m=i+((j-i)>>1);
if(compare(j,m)<0)xchg(j,m);
if(compare(j,i)<0)xchg(j,i);
if(compare(m,i)<0)xchg(m,i);
if((j-i)<=2 || sorted(i,j,compare)) return;
i++;
while(i<j){
while(i<j && compare(i,m)<=0)i++;
while(i<j && compare(j,m)>0)j--;
if(i<j){
xchg(i,j);
if(j==m)m=i;
}
}
if(l-i>=i-f){
i=f;
f=j--;
}
else{
j=l;
l=i-1;
}
if(i<j)sort(i,j,compare);
}
}
void _sort(char **ptr,int l,COMP cmp){
sort(ptr,&ptr[l-1],cmp);
}
main(int argc, char **argv)
{
argv++;
argc--;
if(!argc)return -1;
int flag=0;
COMP c[4]={&compare,&comparer,&comparen,&comparenr};
while(**argv=='/' && --argc){
switch((*argv)[1]){
case '?':
printf("Ordina la sua riga di comando e invia il risultato allo schermo\n\n");
printf("SORT [/R] [/+n] dati da ordinare\n\n");
printf(" /R\t\t\tUsa il tipo di ordinamento inverso:\n");
printf("\t\t\tordina i dati da Z a A e da 9 a 0\n");
printf(" /+n\t\t\tOrdina il file in base al carattere\n");
printf("\t\t\tspecificato nella colonna n\n");
return 0;
case 'r':flag|=1;break;
case 'R':flag|=1;break;
case '+':flag|=2;
n=atoi(*argv+2);
n--;
if(n>=0)break;
default:printf("SORT: Opzione non valida");
return -1;
}
argv++;
}
_sort(argv,argc,c[flag]);
for (int i=0;i<argc;i++)printf("%s\n",argv[i]);
}
//-------------------------------------------------------------------------------------------------
Ciao a tutti
Winebag
Note finali |
Spero che il tutorial vi sia piaciuto e che possiate trarre spunto per spingere la vostra curiosità oltre al semplice cracking, magari con qualcosa di un po' più utile di questo.
Disclaimer |
Vorrei ricordare che il software va comprato e non rubato, dovete registrare il vostro prodotto dopo il periodo di valutazione. Non mi ritengo responsabile per eventuali danni causati al vostro computer determinati dall'uso improprio di questo tutorial. Questo documento è stato scritto per invogliare il consumatore a registrare legalmente i propri programmi, e non a fargli fare uso dei tantissimi file crack presenti in rete, infatti tale documento aiuta a comprendere lo sforzo immane che ogni singolo programmatore ha dovuto portare avanti per fornire ai rispettivi consumatori i migliori prodotti possibili.
Noi reversiamo al solo scopo informativo e di miglioramento del linguaggio Assembly.