|
11° Corso UIC |
||
|
Data |
by PiPsqueak |
|
|
28/06/2001 |
Published
by Quequero |
|
|
"Pere che il pesco faccia mele" Lino Banfi |
Pip, potevi ottimizzare moltissimo il PE in modo da rendere il programma piccolissimo, il codice è cmq estremamente ottimizzato e mi complimento perchè in linea di massima hai fatto quasi esattamente tutto ciò che volevo facessi, quindi doppiamente bravo :) |
in un film del quale purtroppo non ricordo il titolo |
|
.... |
E-mail: pipsqueak@libero.it PiPsqueak, UIN, canale IRC/Efnet frequentato |
.... |
|
Difficoltà |
(X)NewBies ( )Intermedio ( )Avanzato ( )Master |
|
Corso 11 UIC. Ottimizzazione di un programma in assembler. Da quello che si è capito, per ottimizzazione si intende cambiare ogni singola istruzione in modo tale che occupi meno spazio possibile. (Come l'ho intesa io il codice deve essere inserito all' interno di un altro programma, quindi non ho tenuto conto della dimensione effettiva dell' eseguibile, ma della sola sezione .code)
11° corso UIC
Written by PiPsqueak
|
Introduzione |
Ho lasciato di proposito gli op code nel listato W32dasm perché mi sembra utile vedere quanto occupa ogni singola istruzione.
|
Tools usati |
|
URL o FTP del programma |
Sezione
Lezioni UIC
|
Notizie sul programma |
Programma
che misura il tempo impiegato a generare un numero più o meno casuale
|
Essay |
Questo è il mio primo tutorial,
è tanto che non programmo quindi se ho cazziato tutto non arrabbiatevi (almeno
ci ho provato).
Saltiamo a pie pari la prima parte e andiamo subito alla sezione .code
parte
1
:00401000
E857010000 Call
GetTickCount
:00401005
50 push eax <-- salviamo il tempo alla partenza
:00401006 8BC8 mov ecx, eax
:00401008
69C000010000 imul eax,
00000100 <-- moltiplica per 2^8 (256)
:0040100E
8BC8 mov ecx, eax
----> inizio loop
:00401010
69C000100000 imul eax,
00001000 <-- moltiplica per 2^12 (4096)
:00401016 52 push edx
:00401017 8BD1 mov edx, ecx
:00401019
5A pop edx
:0040101A
E2F4 loop 00401010
GetTickCount ritorna
una DWORD dei millisec trascorsi dall' avvio di Windows.
L' istruzione alla
terza riga è inutile, in quanto ecx viene sovrascritto 2 istruzioni dopo,
POTREMMO toglierla, ma la lasciamo in quanto il Que ci ha detto che non si puote
cancellare niente.
Le 2 imul ...
moltiplicano eax per delle costanti: 256 e 4096. Entrambe i numeri possono
essere scomposti in potenze di 2, rispettivamente 2^8 e 2^12, possiamo quindi
sostituire tranquillamente le due operazioni con 2 shift a sinistra.
imul eax, 00000100
---> shl eax, 08 ---
69C000010000 ----> C1E008
imul eax, 00001000
---> shl eax, 12 ---
69C000100000 ----> C1E00C
Il processo matematico
è semplice, pensiamolo in decimale: es. 10d shiftato a destra diventa 100
(10*101) lo stesso discorso vale per i binari 1b shift dx 1 = 10b ce
tradotto in decimale corrisponde alla moltiplicazione dei pani e...ehm... per
due.
Caliamo un velo
pietoso sulle seguenti tre istruzioni, e passiamo al loop che pur essendo un
istruzione "lenta" da eseguire occupa poco spazio.
parte 2
:0040101C
B800000000 mov eax,
00000000 <--
azzera eax
:00401021
2BD2 sub edx, edx <-- azzera
edx
:00401023 8BC1 mov eax, ecx
:00401025
0F31 rdtsc <-- vedi sotto
:00401027
25FFFF0000 and eax,
0000FFFF <--
azzera la parte alta di eax
:0040102C 8BC8 mov ecx, eax
:0040102E 51 push ecx
:0040102F 53 push ebx
:00401030 50
push eax
:00401031 52 push edx
:00401032 56 push esi
:00401033 57 push edi
Velocemente :
mov
eax, 00000000 ---> xor eax, eax --- B800000000 -->
33C0
Mi sembra chiaro, or
esclusivo di 2 num uguali è sempre 0.
sub
edx, edx ---> xor edx, edx oppure cdq (eax>=0) --- 2BD2 ---> 99 (mi pare)
Possiamo azzerare edx
con la xor se eax è negativo, oppure (come in questo caso) con cdq (Convert
Doubleword to Quadword: EDX:EAX := sign-extend of EAX) se eax>=0.
mov
eax, ecx ---> xchg eax,ecx --- 8BC1 --> 91
Inutile perché dopo
tre istruzioni vanno persi entrambe. In questo caso, comunque, non ci interessa
preservare il contenuto di ecx quindi possiamo scambiarli invece che copiarli
and eax, 0000FFFF
---> cwde --- 25FFFF0000 --> 98
Per azzerare la parte
alta di eax mantenendo invariata la metà bassa possiamo ricorrere ancora a una
conversione attraverso cwde (Convert Word to Doubleword: EAX = sign-extend of
AX)
push ecx
ebx eax edx esi edi ---> pushad
pushad salva tutti i
registi sopraccitati nonché ESP, EBP
Per la cronaca,
l’istruzione rdtsc – Read Time-Stamp counter ritorna un numero a 64-bit in
EDX:EAX.
parte 3
----> inizio loop
:00401034
A350204000 mov dword ptr
[calcbuf+4], eax
:00401039
8B1550204000 mov edx, dword
ptr [calcbuf]
:0040103F
0F31 rdtsc
:00401041
03D0 add edx, eax
:00401043 A354204000 mov dword ptr [calcbuf], eax
:00401048 010550204000 add dword ptr [calcbuf], eax
:0040104E 891558204000 mov dword ptr [calcbuf+8], edx
:00401054 8B1550204000 mov edx, dword ptr [calcbuf]
:0040105A 03C2 add eax, edx
:0040105C A358204000 mov dword ptr
[calcbuf+8], eax
:00401061 A154204000 mov eax, dword ptr [00402054]
:00401066
E2CC loop 00401034
Una maniera (non che
ne abbia in mente altre) per segare buona parte di codice (e anche velocizzare
il tutto) è assegnare [calcbuf] a un registro per es. ebx, che come in questo
caso non è utilizzato.
lea
ebx,calcbuf
il prog. diventa :
:00401021
BB50204000 lea ebx,calcbuf <-- istruzione
aggiunta
----> inizio loop
:00401026
8903 mov dword ptr [ebx], eax
:00401028
92 xchg eax,edx
:00401029
0F31 rdtsc
:0040102B
03D0 add edx, eax
:0040102D 894304 mov dword ptr [ebx+04], eax
:00401030 0103 add
dword ptr [ebx], eax
:00401032 895308 mov dword ptr [ebx+08], edx
:00401035 8B13 mov edx, dword ptr [ebx]
:00401037 03C2 add eax, edx
:00401039 894308 mov dword ptr [ebx+08], eax
:0040103C 8B4304 mov eax, dword ptr [ebx+04]
:0040103F
E2E5 loop 00401026
Abbiamo cambiato anche
mov edx, dword ptr
[calcbuf] ---> xchg eax,edx
eax va comunque persa
l'istruzione seguente (anche edx)
parte 4
:00401068
5F pop edi
:00401069
5E pop esi
:0040106A
5A pop edx
:0040106B 58 pop eax
:0040106C 5B pop ebx
:0040106D
59 pop ecx
:0040106E
B900000000 mov ecx,
00000000
Jump at Address:
|:00401083(C)
|
:00401073 8B8150204000 mov eax, dword ptr [ecx+00402050]
:00401079 898150204000 mov dword ptr [ecx+00402050], eax
:0040107F 41 inc ecx
:00401080 83F963 cmp ecx, 00000063 <-- 99d
:00401083 75EE jne 00401073
pop edi etc.. ---> popad (op code 61)
Come nel caso della
push esiste anche popad
mov
ecx, 00000000 ---> xor ecx,ecx --- B900000000 ---> 33C9
Solito.
Possiamo sostituire il
jne con un loop e approfittare dell' azzeramento di ecx per sommargli il num.
di ripetizioni del ciclo.
Come sopra possiamo
utilizzare il registro ebx per puntare a calbuf.
lea
ebx, calcbuf
che
diventa :
:00401041 61 popad
:00401042 33C9 xor ecx, ecx
:00401044
BB50204000 lea ebx, calcbuf
:00401049
83C163 add ecx,
00000063 <-- 99d
:0040104C
8B040B mov eax, dword
ptr [ebx+ecx]
:0040104F
89040B mov dword ptr
[ebx+ecx], eax
:00401052
E2F8 loop 0040104C
Dato
che il loop non fa
niente ed ecx ha solo il ruolo di indice per il puntatore, che il ciclo venga
effettuato in un senso o nell’ altro ha poca importanza.
parte 5
:00401085
8B0D50204000 mov ecx, dword
ptr [calcbuf]
:0040108B 81E1FFFF0000 and ecx, 0000FFFF
:00401091
0F31 rdtsc
----> inizio loop
:00401093 210550204000 and dword ptr [calcbuf], eax
:00401099 010554204000 add dword ptr [calcbuf+4], eax
:0040109F 8B1D54204000 mov ebx, dword ptr [calcbuf+4]
:004010A5 03C3 add eax, ebx
:004010A7 A350204000 mov dword ptr [calcbuf], eax
:004010AC F72550204000 mul dword ptr [calcbuf]
:004010B2 F72550204000 mul dword ptr [calcbuf]
:004010B8 F72550204000 mul dword ptr [calcbuf]
:004010BE F72550204000 mul dword ptr [calcbuf]
:004010C4 F72550204000 mul dword ptr [calcbuf]
:004010CA F72550204000 mul dword ptr [calcbuf]
:004010D0 83C001 add eax, 00000001
:004010D3 03C0 add eax, eax
:004010D5 F73550204000 div dword ptr [calcbuf]
:004010DB C1155820400087 rcl dword ptr [calcbuf], 87
:004010E2
E2AF loop 00401093
come prima diventa :
:00401055
8B0B mov ecx, dword
ptr [ebx]
:00401057
91 xchg eax,ecx
:00401058
98 cwde
:00401059 91 xchg eax,ecx
:0040105A 0F31 rdtsc
----> inizio loop
:0040105C 2103 and dword ptr [ebx], eax
:0040105E 014304 add dword ptr [ebx+04], eax
:00401061 034304 add eax,
dword ptr [ebx+04]
:00401064 8903 mov dword ptr [ebx], eax
:00401066 F723 mul dword ptr [ebx]
:00401068 F723 mul dword ptr [ebx]
:0040106A F723 mul dword ptr [ebx]
:0040106C F723 mul dword ptr [ebx]
:0040106E F723 mul dword ptr [ebx]
:00401070 F723 mul dword ptr [ebx]
:00401072 40 inc eax
:00401073 D1E0 shl eax, 1
:00401075 F733 div dword ptr [ebx]
:00401077
C1530887 rcl dword ptr
[ebx+08], 87 <-- rol che passa dal CF
:0040107B E2DF loop 0040105C
and ecx, 0000FFFF ---> xchg eax,ecx
cwde
xchg eax,ecx
Appoggiandoci su eax
azzeriamo la parte alta di ecx.
add eax, 00000001 ---> inc eax
no problem spero.
parte 6
:004010E4
8B0D50204000 mov ecx, dword
ptr [calcbuf]
:004010EA 81E1FFFF0000 and ecx, 0000FFFF
--->inizio loop
:004010F0 8BD0 mov edx, eax
:004010F2 8BF3 mov esi, ebx
:004010F4 8BDA mov ebx, edx
:004010F6 8BC6 mov eax, esi
:004010F8 E2F6 loop 004010F0
mov ecx, dword ptr [calcbuf] ---> mov ecx, dword
ptr [ebx] --- 8B0D50204000 ---> 8B0B
vedi sopra.
and
ecx, 0000FFFF ---> xchg eax,ecx
cwde
xchg eax,ecx
idem.
Cambiamo le prime 2 mov
(anche solo la 1a) del ciclo con 2 xchg e guadagniamo 1 byte.
Variamo solo mov edx,
eax e mov esi, ebx perché ai fini del ciclo non hanno nessuna influenza e prima
della fine vengono sovrascritti.
:00401085
91 xchg eax,ecx
:00401086
98 cwde
:00401087
91 xchg eax,ecx
--->inizio loop
:00401088
92 xchg eax,edx
:00401089
87F3 xchg ebx, esi
:0040108B
8BDA mov ebx, edx
:0040108D
8BC6 mov eax, esi
:0040108F
E2F7 loop 00401088
parte 7
:004010FA E85D000000 Call GetTickCount
:004010FF 8BD8 mov ebx, eax
:00401101 58 pop eax
:00401102 2BD8 sub ebx,
eax
Qui l' unica cosa e
cambiare la mov con una xchg e recuperare 1 byte
parte 8
push ebx
push offset tempo
push offset buffer
call wsprintf
push MB_OK OR MB_ICONSTOP
push offset titolo
push offset buffet
push 0
call MessageBox
push dword ptr [calcbuf]
push offset testo
push offset buf2
call wsprintf
push MB_OK OR MB_ICONQUESTION
push offset titol0
push offset buf2
push 0
call MessageBox
push 0
call
ExitProcess
Potremmo anche qui usare dei registri per puntare
ai dati (o comunque all’area dei dati) ma il guadagno sarebbe poco quindi
secondo me non ne vale la pena.
|
Note finali |
Ho
un torcicollo di merda e ho dovuto scrivere tutto il tute con una tastiera
senza la g, la h, lo 0 e il . del tastierino numerico, nonché il fottutissimo
Alt. Ringrazio i miei gatti Droga e Sete per non aver cagato nella sabbietta
mentre stavo diGGitando il documento.
|
Disclaimer |
Anche se in questo caso è inutile: 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.