Zoom Icon

Corso UIC Avanzato 11 Pipsqueak

From UIC Archive

UIC Avanzato 11 Pipsqueak

Contents


Corso UIC Avanzato 11 Pipsqueak
Author: Pipsqueak
Email: [email protected]
Website: Home page
Date: 28/06/2001 (dd/mm/yyyy)
Level: Working brain required
Language: Italian Flag Italian.gif
Comments:



Tools

  • W32Dasm
  • Borland Turobo Assembler 5.0


Link e Riferimenti

Questo è il Corsi UIC Avanzati n°01 disponibile alla pagina Corsi per Studenti



Introduzione

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)

Ho lasciato di proposito gli op code nel listato W32dasm perché mi sembra utile vedere quanto occupa ogni singola istruzione.


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

Prima parte

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 28 (256) 0040100E 8BC8 mov ecx, eax // --> inizio loop 00401010 69C000100000 imul eax, 00001000 //<-- moltiplica per 212 (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 28 e 2 e12, 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.

Seconda parte

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.

Terza parte

// ---> 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)


Quarta parte

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.

Quinta parte

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.

Sesta parte

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

Settima parte

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

Ottava parte

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

I documenti qui pubblicati sono da considerarsi pubblici e liberamente distribuibili, a patto che se ne citi la fonte di provenienza. Tutti i documenti presenti su queste pagine sono stati scritti esclusivamente a scopo di ricerca, nessuna di queste analisi è stata fatta per fini commerciali, o dietro alcun tipo di compenso. I documenti pubblicati presentano delle analisi puramente teoriche della struttura di un programma, in nessun caso il software è stato realmente disassemblato o modificato; ogni corrispondenza presente tra i documenti pubblicati e le istruzioni del software oggetto dell'analisi, è da ritenersi puramente casuale. Tutti i documenti vengono inviati in forma anonima ed automaticamente pubblicati, i diritti di tali opere appartengono esclusivamente al firmatario del documento (se presente), in nessun caso il gestore di questo sito, o del server su cui risiede, può essere ritenuto responsabile dei contenuti qui presenti, oltretutto il gestore del sito non è in grado di risalire all'identità del mittente dei documenti. Tutti i documenti ed i file di questo sito non presentano alcun tipo di garanzia, pertanto ne è sconsigliata a tutti la lettura o l'esecuzione, lo staff non si assume alcuna responsabilità per quanto riguarda l'uso improprio di tali documenti e/o file, è doveroso aggiungere che ogni riferimento a fatti cose o persone è da considerarsi PURAMENTE casuale. Tutti coloro che potrebbero ritenersi moralmente offesi dai contenuti di queste pagine, sono tenuti ad uscire immediatamente da questo sito.

Vogliamo inoltre ricordare che il Reverse Engineering è uno strumento tecnologico di grande potenza ed importanza, senza di esso non sarebbe possibile creare antivirus, scoprire funzioni malevole e non dichiarate all'interno di un programma di pubblico utilizzo. Non sarebbe possibile scoprire, in assenza di un sistema sicuro per il controllo dell'integrità, se il "tal" programma è realmente quello che l'utente ha scelto di installare ed eseguire, né sarebbe possibile continuare lo sviluppo di quei programmi (o l'utilizzo di quelle periferiche) ritenuti obsoleti e non più supportati dalle fonti ufficiali.