11° Corso UIC

Data

by PiPsqueak

 

28/06/2001

UIC's Home Page

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


Borland Turobo Assembler 5.0

W32dasm

 

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.