Adunarea tutorial rapid
Conţinut
|
Lecţia 1 Adunarea | |
---|---|
Autor: | Păianjen |
E-mail: | spider_xx87 (AT) hotmail (dot) com |
Site-ul: | http://bigspider.has.it/~~V |
Data: | 01/09 / 2008 (zz / ll / aaaa) |
Nivel: | |
Limba: | Italian |
Comentarii: |
Introducere
Scrie introducerea este întotdeauna cea mai grea parte, pentru că întotdeauna se termină la care cititorul se plictiseste cu lucruri care au venit să se aştepte.
În acest tutorial vom vorbi despre limbajul de asamblare (dar ar trebui!). Pentru un Inversor , au o familiaritate cu bine această limbă este esenţială, deoarece, nu are la dispoziţie fişierele sursă ale programelor pe care doriţi să studieze / modifica / sparge, lista de dezasamblor (sau depanator ), este singurul lucru aveţi.
Pentru cei care doresc să înveţe să program în limbaj de asamblare, va găsi aici informaţii suficiente pentru a obţine o idee de cum merg lucrurile, dar cu siguranţă nu atât de multe încât să puteţi începe scrierea de cod. Pentru detalii, a se vedea trimiterile de la sfârşitul de tutorial.
Tutorial este împărţită în trei părţi: în primul rând, vom vedea la o privire arhitectura de procesoare din a doua, mult mai substanţiale şi poate fi folosit ca un manual de referinţă simplu, vom analiza toate instrucţiunile principale, apoi vom vedea cod un pic ", şi încercaţi să înţelegeţi ceea ce face (pe care, practic, este domeniul principal de activitate al unei Inversor).
Cerinţe preliminare
Ar fi frumos dacă toată lumea ar putea intelege acest tutorial, dar nu vă pot ajuta, dar să ia unele condiţie.
Nu este necesar, de către cititor, nu au cunoştinţe de limbaj de asamblare (de orice procesor). Cu toate acestea, îşi va asuma o oarecare experienţă de programare în limbaje de nivel înalt (şi ar fi de cel puţin neobişnuit, deşi nu neapărat o prostie, încercând să înveţe de asamblare, ca prima limbă).
Ai nevoie de o familiaritate cu bun binar şi hexazecimal de numerotare, şi se schimbă de la o bază la alta, care nu au avut familiarizarea cu aceste concepte sugerăm să vă întoarce aici, după ce a citit câteva tutoriale despre el.
Nuff spus, acum vom începe să analizăm arhitectura AMD64 (în continuare pur şi simplu x64 ). Vom începe cu prima parte un "pic mai mult teoretică, în care nu vom vedea o singură linie de cod, promit că voi încerca să fiu scurt, nu aşa poate sări peste această parte ... În a doua parte, vom vedea toate instrucţiunile de bază, prin urmare, asigurat codul va veni. În cele din urmă vom vedea exemple de cod de asamblare produs de către un compilator, deoarece de învăţare pentru a înţelege codul este cea mai importanta abilitate a unui Inversor.
Eseu
x86 şi x64
Arhitectura x64 este, în esenţă o extensie a IA32 precedent, Intel, că arhitectura, în toate PC-urile dall'80386 dupa, (încă departe de a fi dispărut). Nell'IA32 dimensiunea standard a fost de 32 de biţi de operanzi (ca şi în trecut a fost 16-bit), arhitectura x64 registrele de uz general (şi adrese de memorie), sunt 64 de biţi.
Introducerea de procesoare pe 64 de biţi, AMD64 nu a fost singurul concurent în joc, deoarece IA64 Intel sa propus o cu totul diferită, dar a renunţat la compatibilitate (de exemplu compatibilitatea cu procesoarele anterioare). Este cunoscut faptul că, în industria calculatoarelor, sfinţenia, acest principiu nu poate fi pus în discuţie, AMD64 a prins şi, în prezent, Intel a făcut, de asemenea, compatibile cu procesoarele sale în mod substanţial (Intel, AMD punerea în aplicare a are multe nume: EM64T, Intel IA32e, Intel 64).
De acum voi folosi numele x64 şi x86 indică, respectiv, AMD64, IA32. Termenul x86-16 arhitecturii va fi folosit pentru a desemna la 16-biţi procesoare Intel all'IA32 mai devreme (de exemplu, fabrica de la Intel 8086 80286).
Biţi şi multiplii săi
Chiar şi copiii ştiu echivalenţa 1 octet = 8 biţi. Cele mai frecvent utilizate în multipli de bytes sunt după cum urmează:
- cuvinte = 2 octeţi = 16 biţi
- doubleword (DWORD) = 2 cuvinte = 4 octeţi = 32 biţi
- quadword (qdord) = 2 4 = dword cuvântul = 8 octeţi = 64 biţi
Mai rar auzi de oWord ( octaword ), adică 2 quadword, de asemenea, numit quadword dublu , uneori fword (6 bytes) sau chiar Tbytes (zece octeţi, 10 octeţi).
Întregi semnate şi nesemnate
Cele întregi pot fi de două tipuri: nesemnate ( numere întregi fără semn ) sau cu semnul ( numere intregi semnate ).Dimensiunea întregi pot fi de 8, 16, 32, 64 de biţi, mai rar, 128-biţi (x64 în înmulţiri şi diviziuni).
Un intreg fara semn de n biţi poate reprezenta toate numerele între 0 şi 2 N - 1 . Deci, un întreg 8-biţi fără semn, care este la cel mai 255, 16-bit este mai mare de 65535, 4 miliarde de euro de 32-biţi, pe 64 de biţi de peste 18 de miliarde de miliarde. Reprezentarea binară este de obicei o, pentru care cel mai mare număr care poate fi reprezentată este cea formata de toate 1 biţi. Un întreg semnat de n biţi, în schimb, poate reprezenta toate numerele între -2 n -1 şi 2 n -1- 1. Deci, se merge la 8 biţi -128 - 127, 16-bit -32768 - 32767, 32-biţi la un număr mai mic de -2-2 miliarde, 64-bit mai puţin de -9 miliarde de euro la peste 9 miliarde de miliarde de miliarde de euro. Semnat numere întregi folosind reprezentarea complement faţă de 2 a spus. Reprezentarea numerelor pozitive (şi zero) rămâne aceeaşi, dar din moment ce numerele pozitive sunt limitate la jumătate din posibilele n-biţi valori, toate (şi numai), numerele de non-negative au bitul cel mai semnificativ ("cea mai din stânga ") este egal cu 0, numerele cu bitul cel mai semnificativ este de 1, de fapt, numerele negative. Bitul cel mai semnificativ este numit, pentru acest motiv, bitul de semn. În întreg 8, 16, 32, 64 biti, bitul de semn are, respectiv, indicele de 7, 15, 31, 63. reprezentarea binară a unui număr negativ de complement 2 este foarte simplu de a obţine: de a reprezenta în binar număr negativ - x , scrie doar x , invertit, toate biţi, apoi se adaugă 1. De exemplu, lucrul cu 8-biţi, să presupunem că doriţi să scrieţi -6. Reprezentarea binară a 6 este de 00000110 (am scrie, de asemenea, de biţi zero să subliniez faptul că suntem folosind 8 biţi). Se obţine prin inversarea toate biţi 11111001, 11111010 este obţinut prin adăugarea 1. Aceasta este reprezentarea binară de 8-biţi -6. Notă o proprietate importantă: suma de reprezentările din 6 şi 100 de milioane de -6 se obţine, care, în zecimal este egal cu 28; acest număr are 9 biţi, aşa că, dacă tronchiamo de biţi mai puţin de 8 semnificative pe care le obţine 0 (de la dimensiunea operaţiunilor de procesorul are întotdeauna un număr fix de biţi, trunchiere este implicită). Faptul că suma a două numere opuse 0 fata este esenţială, şi în aceasta constă utilitatea complementului 2 este. ar trebui să devină competenţi, cu aceste concepte.
Un intreg fara semn de n biţi poate reprezenta toate numerele între 0 şi 2 N - 1 . Deci, un întreg 8-biţi fără semn, care este la cel mai 255, 16-bit este mai mare de 65535, 4 miliarde de euro de 32-biţi, pe 64 de biţi de peste 18 de miliarde de miliarde. Reprezentarea binară este de obicei o, pentru care cel mai mare număr care poate fi reprezentată este cea formata de toate 1 biţi. Un întreg semnat de n biţi, în schimb, poate reprezenta toate numerele între -2 n -1 şi 2 n -1- 1. Deci, se merge la 8 biţi -128 - 127, 16-bit -32768 - 32767, 32-biţi la un număr mai mic de -2-2 miliarde, 64-bit mai puţin de -9 miliarde de euro la peste 9 miliarde de miliarde de miliarde de euro. Semnat numere întregi folosind reprezentarea complement faţă de 2 a spus. Reprezentarea numerelor pozitive (şi zero) rămâne aceeaşi, dar din moment ce numerele pozitive sunt limitate la jumătate din posibilele n-biţi valori, toate (şi numai), numerele de non-negative au bitul cel mai semnificativ ("cea mai din stânga ") este egal cu 0, numerele cu bitul cel mai semnificativ este de 1, de fapt, numerele negative. Bitul cel mai semnificativ este numit, pentru acest motiv, bitul de semn. În întreg 8, 16, 32, 64 biti, bitul de semn are, respectiv, indicele de 7, 15, 31, 63. reprezentarea binară a unui număr negativ de complement 2 este foarte simplu de a obţine: de a reprezenta în binar număr negativ - x , scrie doar x , invertit, toate biţi, apoi se adaugă 1. De exemplu, lucrul cu 8-biţi, să presupunem că doriţi să scrieţi -6. Reprezentarea binară a 6 este de 00000110 (am scrie, de asemenea, de biţi zero să subliniez faptul că suntem folosind 8 biţi). Se obţine prin inversarea toate biţi 11111001, 11111010 este obţinut prin adăugarea 1. Aceasta este reprezentarea binară de 8-biţi -6. Notă o proprietate importantă: suma de reprezentările din 6 şi 100 de milioane de -6 se obţine, care, în zecimal este egal cu 28; acest număr are 9 biţi, aşa că, dacă tronchiamo de biţi mai puţin de 8 semnificative pe care le obţine 0 (de la dimensiunea operaţiunilor de procesorul are întotdeauna un număr fix de biţi, trunchiere este implicită). Faptul că suma a două numere opuse 0 fata este esenţială, şi în aceasta constă utilitatea complementului 2 este. ar trebui să devină competenţi, cu aceste concepte.
Zero-extensie şi semn de extindere
Cele operaţii aritmetice dintre numere reprezentate cu acelaşi număr de biţi sunt efectuate la mod obişnuit; înţeles, însă, de multe ori a fi nevoie să efectueze tranzacţii între operanzi de dimensiuni diferite. În acest caz, operand de dimensiuni mai mici trebuie să fie convertite (implicit sau explicit), la dimensiunea dreapta.
În cazul tranzacţiilor între întreg nesemnat , acest lucru este banal: prefix doar numărul, cu o mulţime de biţi de dimensiuni mai mici 0 Cum de multe sunt necesare pentru a obtine marimea potrivita: reprezentare de 6 la 8 biţi este de 00000110, 16-bit este 0000000000000110. Acest proces este numit zero prelungire .
În cazul de ansamblu a semnat , cu toate acestea, acest lucru nu funcţionează. Mai precis, acesta funcţionează numai pentru numere pozitive. Dar reprezentarea de -6 este 11111010, cu zero extensie se obţine 0000000011111010, şi anume, 250 zecimal nu este exact acelaşi lucru. Motivul este semnificaţia specială a bit de semn şi funcţionare a complementului 2 este. Reprezentarea de -6 la 16 de biţi este 1111111111111010, extinderea şi anume cel cu 8-biţi în loc de cifre 1 0. În general, extinderea de numere intregi semnate se realizează prin completarea toţi biţii suplimentare cu valori egale cu bitul de semn. Acest mecanism se numeşte semnul de extindere .
Înregistrările
În cadrul CPU, există un număr mare de registre pentru funcţiile cele mai disparate. Un registru este numai o celulă de memorie, cel mai rapid conţinute în procesor. Ele sunt folosite pentru a stoca starea de procesor, continuu modificată ca urmare a aplicării a instrucţiunilor.
Să vedem cum set este compus din registre de arhitectura x86, şi apoi a se vedea modul în care a fost extins în 64 de biţi.
Registre X86
Un prim grup de registre este constituit de către Registrul General Purpose S (GPRS), că este, registrele de uz general, numit asa deoarece, în mod ideal, poate fi folosit pentru orice scop.
Formatul general al unui registru de uz general (ia ca exemplu EAX ), este după cum urmează:
EAX | ||
AX | ||
AH | AL |
Liniile ar fi imaginat ca suprapuse: registrul intreaga este EAX , de biţi mai puţin semnificativi 16 ("dreapta" în reprezentarea numerică) forma AX , de biţi mai puţin semnificativi 8 formează AL (litera L vine de la Low ). Cele 8 biţi cele mai semnificative ale AX formează, în schimb, registrul AH (în cazul în care H vine de la mare ).
Există 8 pe 32 de biţi registre de uz general: EAX , ebx , ECX , EDX , ESI , EDI , EBP , ESP . E de nume a fost adaugat cu arhitectura IA32 introducerea ca o extensie a precedente 16-bit.
Noi folosim adesea jurnalele indică toate majuscule sau toate mici, în timp ce notaţia EAX cu prima literă mică indică fieAX sau EAX . În mod similar, folosind notaţiile ebx , ECX , şi aşa mai departe.
Ebx , ECX si EDX au acelaşi format ca EAX văzut doar de mai sus, astfel încât sottoregistri de ebx sunt BX , BH , BL , cele din ECX sunt CX , CH , CL , cei de EDX , în cele din urmă, sunt DX , DH , DL .
În mod normal, fiecare din aceste registre pot fi folosite pentru orice scop, deşi există o serie de excepţii: vom analiza în timp util. Registrul EAX este de asemenea, numit de acumulatoare , din motive istorice, mai mult decât orice altceva, deşi există o serie de situaţii în care utilizarea sa este necesară şi altele în care este de preferat cea a altor registre.Pentru o mai mică măsură, acest lucru este adevărat, de asemenea, de ebx , ECX , EDX .
Pentru alte registre cu scop general 32-biţi ( ESI , EDI , EBP , ESP ) nu poate accesa sottoregistri de 8 biţi, în timp ce sottoregistri de 16 biţi, în ordine, SI , DI , BP , SP .
ESI şi EDI (a cărui stea acronime pentru Index Sursa şi Index de destinaţie ), poate fi folosit pentru orice scop, ca şi cele anterioare, dar ele au un suport special pentru a fi utilizate ca indici pentru operaţiunile de coarde (scanare, copiere, comparaţie). Sottoregistri respectiv la 16 de biţi sunt SI şi DI , în timp ce există sottoregistri 8-biţi (după cum a subliniat deja).
EBP şi ESP ( Indicator de bază şi Stack Pointer ) sunt utilizate în managementul de stivă (mai multe despre mai târziu).ESP indică poziţia curentă în stivă, şi EBP inclina, de obicei, de bază a stivei, adică, să fie clar, zona de memorie în cazul în care găsi informaţii locale de o funcţie (variabilele procedura normală, nu sunt vizibile din exterior). De fapt, nimic nu împiedică să utilizaţi EBP în alte scopuri, dar acest lucru nu este foarte frecventă. Cele omologii 16-biţi sunt BP si SP .
În cele din urmă, setul de registru cu scop general arhitectura e x86 prevede:
- 8-byte registre ( AH , AL , BH , BL , CH , CL , DH , DL );
- 8 înregistrează un cuvânt ( AX , BX , CX , DX , SI , DI , BP , SP );
- 8 1 registre DWORD ( EAX , ebx , ECX , EDX , ESI , EDI , EBP , ESP ).
Un registru separat este EIP (cu subregister IP ), " indicator de instrucţiuni : întotdeauna indică următoarea instrucţiune de a executa. EIP nu pot citi sau scrie ca şi alte înregistrări, dar pot fi citite sau modificate indirect, cu instrucţiunile de Fluxul de control care vom vedea.
Un registru foarte important este registrul EFLAGS . Acest jurnal este un domeniu putin, format de un număr mare desteaguri , ossiavalori binar (0 sau 1) cu scopuri diferite. Aici este o diagramă:
| 31 | 30 | 29 | 28 | 27 | 26 | 25 | 24 | 23 | 22 | 21 | 20 | 19 | 18 | 17 | 16 | 15 | 14 | 13 12 | 11 | 10 | 09 | 08 | 07 | 06 | 05 | 04 | 03 | 02 | 01 | 00 |
| | | | | | | | | | | | | | | | |
FLAG ID (ID) -------------- ---- + | | | | | | | | | | | | | | | |
Interrupt Virtual Până + ------- | | | | | | | | | | | | | | |
Virtual Interrupt Flag ------------- + | | | | | | | | | | | | | |
-------------------- Aliniere de check --- + | | | | | | | | | | | | |
Virtual-8086 modul ------------------------ + | | | | | | | | | | | |
CV pavilion --------------------------------- + | | | | | | | | | | |
operativ Nested --------------------------------------- + | | | | | | | | | |
I / O nivelul de privilegii ------------------------------------ + | | | | | | | | |
---------------------------------------- preaplin de pavilion ------- + | | | | | | | |
-------------------------------- Direcţia de pavilion + ----------------- | | | | | | |
---------------------- Întreruperea Activează de pavilion ----------------------- + | | | | | |
------------------ Trap Flag ------------------------------------------ + | | | | |
| | | | |
--------------------------------------------- Flag Contul ------------------ + | | | |
------------------------- Zero Flag ----------------------------------------- + | | |
Indicator Carry auxiliară - -------------------------------------------------- --------- + | |
------------------------------------ paritate de pavilion ---------------------------------------- + |
------ Carry Flag -------------------------------------------------- --------------------------- +
| | | | | | | | | | | | | | | | |
FLAG ID (ID) -------------- ---- + | | | | | | | | | | | | | | | |
Interrupt Virtual Până + ------- | | | | | | | | | | | | | | |
Virtual Interrupt Flag ------------- + | | | | | | | | | | | | | |
-------------------- Aliniere de check --- + | | | | | | | | | | | | |
Virtual-8086 modul ------------------------ + | | | | | | | | | | | |
CV pavilion --------------------------------- + | | | | | | | | | | |
operativ Nested --------------------------------------- + | | | | | | | | | |
I / O nivelul de privilegii ------------------------------------ + | | | | | | | | |
---------------------------------------- preaplin de pavilion ------- + | | | | | | | |
-------------------------------- Direcţia de pavilion + ----------------- | | | | | | |
---------------------- Întreruperea Activează de pavilion ----------------------- + | | | | | |
------------------ Trap Flag ------------------------------------------ + | | | | |
| | | | |
--------------------------------------------- Flag Contul ------------------ + | | | |
------------------------- Zero Flag ----------------------------------------- + | | |
Indicator Carry auxiliară - -------------------------------------------------- --------- + | |
------------------------------------ paritate de pavilion ---------------------------------------- + |
------ Carry Flag -------------------------------------------------- --------------------------- +
În afară de un număr de steaguri utile, de fapt, numai pentru programarea de sistem, nu sunt 6 (de opţiuni de stare ), care sunt stabilite la 1 sau 0, în funcţie de rezultatul de mai multe instrucţiuni. Este important de ştiut.
Carry Flag ( CF , bit 0) - Carry flag. Este setat la 1 atunci când există un transport (în cazul unei sume) sau un împrumut (în cazul de scădere) de la bitul cel mai semnificativ a unei operaţiuni. De exemplu, EAX conţine valoarea hexazecimală 0x9000000 şi execută instrucţiunea ADD EAX, EAX , rezultatul (0x120000000) este trunchiat la 32 de biţi (0x20000000), şi prezenţa de report este semnalat prin setarea procesorului sa Carry Flag 1. În operaţiunile de schimbare sau de rotaţie, cu toate acestea, sensul este diferit, dar vom vedea mai tarziu.
Paritate de pavilion ( PF , bit 2) - Flag de egalitate. Este setat la octetul cel mai puţin semnificativ de rezultatul multe operaţiuni conţine un număr par de biţi 1. Acesta este, în general, utilizat în sistemele de transmisie de date, cum ar fi sistemul de control, pentru cei care sunt incepatori, poate fi suficientă pentru a cunoaşte existenţa acesteia. Utilizarea efectivă este destul de rar.
Drapelul Carry auxiliar ( AF , bit 4) - este setat la 1 atunci când există un transport sau un împrumut de la exploatarea pic treime dintr-un BCD ( Binary Coded Decimal ), zero în caz contrar. Utilizare este rară, acest indicator poate fi cu siguranţă ignorate de către începători.
Zero Flag ( ZF , bit 6) - este setat la 1 în cazul în care rezultatul unei operaţii este zero, în caz contrar acesta este şters.
Conectaţi-vă Flag ( SF , bit 7) - Flag a semnului. Este pus la 1 în cazul în care, după o operaţie aritmetică, bitul cel mai semnificativ (bitul de semn) este de 1. În caz contrar, acesta este şters.
Drapelul preaplin ( A , bit 11) - este setat la 1 atunci când rezultatul unei operaţii este prea mare ( overflow ) sau prea mici ( underflow ), să fie în registrul de destinaţie, este eliminat altfel. Mai precis, acesta este setat la 1 în cazul în care bitul de semn (cel mai semnificativ) din rezultatul este diferit de cel de ambii operanzi. În cazul sumelor de scaderi şi este uşor să te convingă că această condiţie este echivalentă cu un preaplin / underflow.
Importanţa steagurile de stare e să fie clar atunci când vom discuta despre direcţiile de control al debitului (dar sunt, de asemenea, utile pentru alte instrucţiuni).
Un alt pavilion demn de reţinut (clasificat ca opţiuni de control, deoarece modifică comportamentul de procesor) esteDirecţia de pavilion (DF, bit 10). Acesta vă permite să decideţi direcţia în care tranzacţiile sunt efectuate pe un şir de caractere, care este, în cazul în care busteni ESI şi EDI sunt incrementat sau decrementat cu fiecare repetiţie.Operaţiunile şir nu sunt tratate.
Registrele speciale de gestionare a memoriei sunt segmentul Registrul S, 16-bit înregistrează conţine un selector de segment. Segmentele permit să gestioneze modelele de memorie segmentat (appunto. ..), care au fost utilizate de obicei în sistemele de multiprocess pentru a izola fiecare proces de la celălalt într-un mod transparent. Ele sunt CS(segment de cod), DS (Data Segment), ES (Extra Segment), FS , GS , SS (segmentul stivă). Segmentul de cod este segmentul care conţine codul de, în timp ce altele sunt pentru segmente de date; DS este implicit pentru cele mai multe instrucţiuni (de altă parte, pentru a fi folosite, de obicei, trebuie să fie dat în mod explicit la asamblare, care le va codifica în mod corespunzător în cod maşină). SS este segmentul care conţine stiva. FS şi GS segmente sunt date suplimentare, ale căror nume au semnificaţii speciale şi ar trebui să continue doar secvenţa alfabetică, au fost introduse în Intel 80386.
Alte registre sunt utilizate pentru utilizarea avansată Consola de depanare în Registrul S ( DR0 , DR1 , DR2 , DR3 , DR6 , şiDR7 ), utilizate în principal de către depanator, şi de control registrul S ( CR0 la CR4 ), folosite numai la nivelul sistemului de operare şi niciodată de la aplicarea normală.
X64 înregistrează
Arhitectura AMD64 este o extensie dell'IA32 concepute pentru a menţine compatibilitatea înapoi. Procesoare x64 poate rula pe 32 de biţi aplicaţii în sistemele de operare pe 64 de biţi, dar se pot comporta chiar ca pe 32 de biţi (procesoare şi apoi executaţi un sistem de operare pe 32-biţi), deşi acest lucru nu poate fi cel mai bun mod de a utiliza banii . Cu toate acestea, pentru a exploata potenţialul adevărat al arhitecturii x64 este necesară pentru a compila programe special pentru aceasta arhitectura si au un sistem de 64-biţi de operare, astfel încât procesorul poate mergela 64 de biţi.
În 64-bit, procesoare x64 oferă un set de extensii.
Toate Scop Registrul general S, L ' indicator de instrucţiuni registrul de steagurile sunt 64 de biţi în loc de 32.
Se adaugă 8 GPR e, pentru un total de 16. Lipsa de înregistrări a fost unul din principalele motive pentru regândirea arhitecturii în tranziţia de la x86 la x64.
Înregistrările devin Rax , RBX , RCX , RDX , CDI , CDI , PFR , RSP . Cele 8 registre extinse sunt pur şi simplu, numerotate de la R8 la R15 . Pentru fiecare registru poate accesa, de asemenea, sottoregistri de 32 -, 16 -, 8-biţi cuprinse în mai puţin semnificativă. Deoarece Rax a RSP numele sunt cele deja observate (de exemplu, EAX , AX , AL ), dar, de asemenea, să devină adresabile de 8 biţi de RSI , CDI , PFR , RSP , cu numele SIL , DIL , BPL , SPL , respectiv . Sottoregistri la 32, 16, 8-biţi registre de la R8 la R15 sunt numite prin adăugarea de sufixe D , W , B , respectiv, care sunt in mod natural Dword, Word şi Byte. De exemplu, subregister 16-bit R8 este numit R8W .
Puteţi accesa în continuare registre AH , BH , CH şi DH , dar nu se poate amesteca aceste înregistrări într-o singură declaraţie, cu extins (adică cele care nu există în arhitectura x86).
Schematic, GPR "s devin:
- 16 - 64-bit registre: Rax , RBX , RCX , RDX , PFR , RSI , CDI , RSP , R8 , R9 , R10 , R11 , R12 , R13 , R14 , R15 ;
- 16 la 32-biţi registre: EAX , ebx , ECX , EDX , EBP , ESI , EDI , ESP , R8D , R9D , R10D , R11D , R12D , R13D , R14D , R15D ;
- 16 la 16-biţi registre: AX , BX , CX , DX , BP , SI , DI , SP , R8W , R9W , R10W , R11W , R12W , R13W , R14W , R15W ;
- 16 registre formate de 8 biţi ai registrelor: AL , BL , CL , DL , BPL , SIL , DIL , SPL , R8B , R9B , R10B , R11B , R12B ,R13B , R14B , R15B ;
- 4 8-biţi registre în biţi cele mai semnificative ale AX , BX , CX si DX , şi anume: AH , BH , CH , DH .
Chiar registrul EIP este extinsă în PAR , şi registrul EFLAGS este prelungit RFLAGS .
Spre deosebire de arhitectura x86, x64 oferă posibilitatea de a folosi PAR la traseu, adică citi sau a scrie locaţii de memorie, care departe de PAR, la un maxim de 2 GB. Vom vedea mai târziu pe abordarea detaliile legate de PAR .
Notaţia Rax este adesea folosit pentru a desemna, fie Rax , EAX sau AX , notaţii similare există, desigur, pentru toate celelalte înregistrări.
Registrele de segment nu sunt aproape, cum este utilizat în modul 64-bit, deoarece managementul memoriei prin segmentarea devine inutilă în cazul în care dimensiunea de indicii este de 64 de biţi (ceea ce înseamnă un spaţiu de adrese de 2 64 octeţi, care este, foarte multe ). Noi considerăm doar registrele CS , FS şi GS , dar intr-o importanta mult mai puţin decât în x86. Alte Segmentul Registrul lui sunt pur şi simplu ignorate.
Pentru 32-bit x64
În x64, dimensiunea implicită a operanzi este, de cele mai multe instrucţiuni, 32 de biti. Cele codificări maşini lingvistice ale 64-bit ale instrucţiunile impune ca adăugarea unui prefix special pentru codare de învăţământ, acest cod permite accesul la set extins de registre.
Spre deosebire de operaţiunile de sottoregistri 8 şi 16 biţi, care acţionează numai pe subregister lăsând neschimbate alte 56 sau 48 de biţi, modificările la registrele de 32-biţi a reseta 32 de biţi cele mai semnificative ale registrului de membru (în jargonul tehnic, sunt zero, -extinsă ). De exemplu, presupunând că starea iniţială este următoarea:
Rax = 0002_0001_8000_2201
RBX = 0002_0002_0123_3301
Presupunând că aţi face variantele 64, 32, 16 şi 8 biţi ADD (instrucţiuni care se adaugă al doilea operand la prima, salvarea rezultatul la primul), aveţi următoarele opţiuni:
ADD RBX, Rezultat Rax: = RBX 0004_0003_8123_5502
ADD ebx, EAX Rezultat: = RBX 0000_0000_8123_5502
ADD BX, AX Rezultat: = RBX 0002_0002_0123_5502
ADD BL, AL Rezultat: = RBX 0002_0002_0123_3302
Al doilea rând este evidenţiată pentru a sublinia un comportament diferit de versiunea pe 32 de biţi, comparativ cu celălalt.
Memorie
Din punct de vedere al cererii, memoria poate fi privit pur şi simplu ca o serie contiguă de bytes, fiecare cu propria sa adresa, care este capabil de a citi sau a scrie. De fapt, o adresă conţinută într-un pointer este o adresă virtuală (adresa virtuala ), care trebuie să treacă prin etapele de traducere pentru a obţine adresa fizică ( adresa fizică ), adică adresa reală în memorie. Traducerea este realizată în mod transparent pentru aplicaţii.
Segmentarea
În modele care exploatează memorie de segmentare , fiecare acces de memorie specifică (în mod explicit sau implicit), un segment selector (conţinută într-un registru segment ), hardware-ul utilizat pentru a cunoaşte în cazul în caresegmentul este in memorie ( de bază ), quant "este mare ( limita ) si tipul de acces permis. În cazul în care accesul este în afara dimensiunea segmentului, sau este un tip care nu este permis (de exemplu, a scrie un segment de read-only), hardware-ul generează un raport de excepţie pentru anomalie (care, dacă nu a reuşit corespunzător, provoacăaccident , care este, închiderea forţată de către sistemul de operare).
De segmentare a fost foarte popular în sistemele vechi, dar este aproape depăşită în sistemele moderne de x86, care utilizează un model de memorie plat , cu segmente cu baza 0 şi limita de 4 GB, care este de fapt echivalent cu segmentarea dezactivarea, care este de ce este a fost eliminat aproape în totalitate arhitectura x64.
Paging
Paginare (paginare), în schimb, oferă un sistem de protecţie mai granular, permiţându-vă să specificaţi permisiunile pentru fiecare pagină , adică un bloc de memorie de mari dimensiuni, de obicei, 4 KB (4096 bytes). De asemenea, vă permite să remaparea de memorie fizică, în orice fel: el crede că cererea se adresează unui rând (în loc numai ei ştiu, lumi virtuale), poate fi la orice locaţie în RAM, sau poate chiar au fost eliminate şi stocate pe hard disk pentru a face loc pentru altceva, şi vor fi raportate în RAM atunci când cererea vrea să-l accesaţi din nou (de paginare se datorează sistemelor de operare moderne se poate "emula", RAM mai mult decât există într-adevăr, deşi cu o degradare de performanţă). Toate se întâmplă acest lucru în mod transparent la cerere.
Ordinul de bytes
În cazul datelor de dimensiunea unui octet (sau date compozite ca siruri de caractere ASCII, care sunt pur si simplu tablou de octeţi), există doar o singură comandă este posibil, în memorie: fiecare octet are poziţia sa unică.
Cu toate acestea, în cazul de cuvânt dimensiune de date (2 octeţi), DWORD (4 bytes) sau QWORD (8 octeţi) Există două convenţii principale: big-endian si little-endian .
Convenţia de la big-endian , octeţii sunt scrise de la cel mai semnificativ la semnificative, astfel că forma lor de concatenare simplu datele complete. De exemplu, dacă adresa de memorie 0x402000 este DWORD 0x11223344, în memorie, nu ar fi, în ordine, 0x11 octeţi (la 0x402000), 0x22 (la 0x402001), 0x33 (la 0x402002) , 0x44 (adresa 0x402003). Notaţia big-endian este standard pentru comunicaţii de reţea, dar este, de asemenea, utilizat în unele procesoare.
Convenţia de la little-endian , dar bytes dat un multiplu de octeţi sunt scrise de la cel în mod semnificativ la cel mai important, adică în ordine inversă în memorie. DWORD la 0x402000 0x11223344 vor fi stocate astfel: 0x44 (adresa 0x402000), 0x33 (adresa 0x402001), 0x22 (adresa 0x402002), 0x11 (adresa 0x402003).
Toate x86 şi x64 utilizează little-endian convenţie, deci va trebui să te obişnuieşti cu octeţi inversează.
Un avantaj de notaţie little-endian este aceasta: aceleaşi date este reprezentat corect în dimensiuni diferite simultan.De exemplu, DWORD 0x00000055 la 0x402000 devine: 55 00 00 00. Citind cuvântul, la aceeaşi adresă se arată: 55 00, care este little-endian 0x0055; citirea unui octet 0x55 este citit. Acest lucru poate fi foarte convenabil pentru cei care sunt de programare în asamblare.
Unele procesoare a adoptat, de asemenea, notaţiile mai complicate ( mixt-endian ). Ştii, nu există nici o limită la nebunia umană.
Doar din curiozitate: numele de little-endian şi big-endian sunt derivate din Calatoriile lui Gulliver de Jonathan Swift, care a raportat capătul mic (la sfârşitul putin) si mare (sfârşitul mare) de ou, subiectul de dispută între două popoare aflate în conflict de Lilliput şi Blefuscu, care a izbucnit ouăle la capete diferite (ok, ma opresc ... rupe ouă dvs. cu această ştire!).
Stivui
Stivă este o zonă de memorie disponibil pentru fiecare proces pentru a stoca informaţii temporare.
O stiva este o structura de date de tip LIFO ( Last In First Out ), adică o structură în care informaţia este eliminat în ordinea inversă a că de inserţie. Ultimul element împins pe indicatorul de stivă este conţinută în registrul RSP (de exemplu RSP dacă suntem x64, ESP pe x86, SP în vechiul x86-16). În modelele x86 de memorie segmentate, este util să ştim că segmentul de stiva este conţinută în SS şi că toate trimiterile care conţin PFR sau RSP se face în mod implicit, prin segmentul de SS .
Stivă poate fi utilizat pentru a stoca datele într-un registru care urmează să fie folosit mai târziu, în scopul de a elibera registrul în alte scopuri. Datorită structurii LIFO, datele sunt returnate în ordine inversă faţă de modul în care acestea au fost incluse. Deci, dacă am salvat, în ordine, Rax , CSR şi R14 , apoi ripristinerò prima R14 , apoi CSR şi, în final Rax .Vom vedea mai jos instrucţiuni PUSH şi POP , care sunt folosite pentru a manipula stiva.
O altă funcţie a stivei este de a gestiona de control al fluxului în apeluri de proceduri. Atunci când o procedură de apel, de fapt, trebuie să salvaţi adresa de care trebuie să înceapă să fie difuzate la finalul procedurii. Aceasta adresa de revenire ( adresa de retur ) este salvat pe stivă implicit educaţie CALL pentru apeluri de proceduri, şi este eliminat din învăţământ RET , care este întotdeauna ultima a unei proceduri.
Parametrii au trecut la o procedură sunt uneori introduse pe stiva de apelantului (şi, uneori, sunt, totuşi, a trecut prin registre ... doar sa de-a lungul!). În cele din urmă, spaţiul stiva, care este rezervat pentru variabile locale ale unei proceduri de (cele care există numai în timpul executării procedurii în sine şi încetează să existe după încheierea).
Vom reveni mai târziu pentru a vorbi mai în detaliu de stivă, instrucţiuni, PUSH şi POP , instrucţiuni, de control al fluxului şi convenţiile de asteptare.
Limbaj de asamblare
În cele din urmă încep să vorbească de limbaj de asamblare.
Sintaxa pentru a fi utilizate în acest tutorial este faptul că de asamblare MASM, cu toate acestea, nu este dificil să se adapteze la sintaxa de asamblatori de celelalte, în care caz, vă rugăm să consultaţi manualele lor de a afla diferenţele.
Sintaxa de bază
Sintaxa de asamblare este, în general, foarte simplu. Fiecare instrucţiune are un nume care este numit mnemonice , deoarece practic numai rolul de a reaminti non-binare de codare în limbaj maşină. Codul numeric care reprezintă declaraţia se spune Opcode (contractie de cod operaţional ).
Fiecare instrucţiune poate avea operanzi 0, 1 sau 2, în cazuri rare 3. Declaraţie completă, într-o linie de la sine în fişierul sursă este format de către mnemonică şi lista de operand. Între mnemonic şi lista operand, desigur, este nevoie de cel puţin un spaţiu, operanzii sunt separate prin virgule. De exemplu, instrucţiunea ADD are doi parametri, care (printre altele), pot fi două registre. Declaraţia este valabilă:
adauga Rax , RBX
Această declaraţie se adaugă registrul RBX de a înregistra Rax , de economisire a rezultat în Rax . Avem un stres constant de toate instrucţiunile cu doi operanzi: dacă are sens să vorbim despre sursă şi destinaţie (de exemplu, în cazul în care adresa de magazine procesor rezultat), sintaxa MASM impune ca destinaţie este întotdeauna primul operand (acolo asamblori de adoptare a AT & T, cu sintaxa operanzi inversată; sunt folosite în Linux). Deci, mai multe instrucţiuni vor fi ca:
- mnemotehnică DST , src
Numărul şi tipul de operanzii sunt specificate în detaliu în manuale (a se vedea bibliografia), dar pentru toate instrucţiunile de bază le vom vedea mai târziu.
Tipuri de operanzi şi sinopsis
Instrucţiunile pot necesita diferite tipuri de operanzi. Cele principale sunt următoarele: un registru, o valoare de memorie, o imediat (de exemplu, o constantă numerică) sau o adresă relativă (care este în plus faţă de " Instruction Pointer ).
Operanzii pot fi de dimensiuni diferite, astfel încât mai târziu, atunci când vom descrie sinteză a instrucţiunilor se va specifica, care sunt întotdeauna cele permise.
Vom începe cu câteva observaţii generale cu privire la formatul de asamblare şi de instruire, această secţiune continuă cu o introducere lungă pentru a instrucţiunilor de asamblare: analiza, cu un anumit grad de detaliere, patruzeci de instrucţiuni de asamblare, un subset relativ mic de sute de instruire existente, dar care acoperă cea mai mare aproape de instrucţiuni pe care le puteţi vedea în practică.
Nu te obosi să memorezi totul la o dată, nu este necesară, este posibil (şi de fapt, destul de usor), numai de practica.Dar nu se grăbesc să secţiunea următoare.
Format din operanzii din memorie
Operanzii din memorie sunt afişate în MASM, dar, de asemenea, în lista de mai multe dezasamblori, între paranteze drepte. De exemplu, instrucţiunea MOV Rax, [RBX] copie Rax QWORD cuprinse adresa de memorie indicat de către RBX .Am observat că al doilea operand nu conţine nicio informaţie cu privire la dimensiunea, cu toate acestea, educaţie MOVcelor doi operanzi au întotdeauna aceeaşi dimensiune, atunci dimensiunea trebuie să fie aceeaşi ca Rax , respectiv 64 de biţi. În alte cazuri, cu toate acestea, nu ar fi nici o ambiguitate, de exemplu, educaţie INC [RBX] , care creşte valoarea indicat de către ebx , există în versiunea 8, 16, 32, 64-bit. În acest caz, atât în x64, x86, care, în dimensiunea implicită este de 32 de biţi. Pentru a specifica o dimensiune diferită de cea implicită, precedată de expresia operand ca " byte ptr "," cuvântul ptr "," DWORD PTR "sau" ptr QWORD ". De exemplu, INC byte ptr [ebx] creşte octet indicat de către ebx , INC QWORD ptr [ebx] creşte un QWORD, şi aşa mai departe.
Formatul adresei de general de un operand în memorie (adică una care poate fi cuprinsă în paranteze pătrate), în x86 este următoarea:
- baza + index * scală + deplasare
În cazul în care baza şi indicele sunt două pe 32 de biţi (registre, dar indicele nu poate fi ESP ), factorul de scara poate fi 1, 2, 4 sau 8 (în ambele 1, desigur, nu este indicat), deplasarea este un întreg cu semn de la 8, 16 sau 32 de biţi. De exemplu:
MOV RDX , QWORD ptr [ ebx + ecx * 8 + 24 ]
În x64 puteţi utiliza acelaşi tip de abordarea de bază , indicele , scară şi de deplasare , de bază şi indicele sunt, de obicei,de uz general înregistrează orice 64-biţi, dar puteţi folosi, de asemenea, pe 32 de biţi registre (dar nu de 32 şi un 64-bit, desigur), caz în care se poate adresa doar o parte din spatiul de adrese (4 GB). Deplasarea rămâne un întreg semnat de 32 de biţi mai puţin, care nu mai este posibil pentru o scriere de tip MOV EAX, [ adresa ], în cazul în care adresa este o adresă absolută în memorie.
x64 introduce o formă de adresă nu este prezent în x86: adresare relativă în PAR (denumită în italiană nu este mare, spune anglofonă PAR adresare relativă ). În acest caz, nu este disponibil doar o deplasare de 32 de biţi, care procesorul adaugă la PAR de următoarea instrucţiune. În acest fel, vă puteţi adresa cu date care distanţa până la 2 GB de anchetă, care este de obicei suficient pentru aplicatii normale (imaginea unui executabil care nu este greu depăşeşte niciodată această dimensiune!). Un avantaj al acestei forme de adresare este de a face codul independent de localizarea efectivă în memorie, în timp ce adresele absolute de relocare este posibil (de încărcare adică într-o altă adresă decât se preconizează) impune frontal pentru a corecta toate adresele absolută (deşi, în realitate, această operaţiune nu a fost deosebit de scump).
Alinierea de date
Datele din memorie ar trebui să fie întotdeauna aliniate. Un lucru este aliniat corect atunci când adresa acesteia în memorie este un multiplu de dimensiunea sa (aici mă refer doar la date a căror dimensiune este de putere a lui 2). Prin urmare, pentru un octet nu a existat niciodată sunt probleme, dar ar trebui să fie un cuvânt într-o adresă divizibil cu 2 (de exemplu, cu ultimul bit 0), o valoare DWORD într-o adresă divizibil cu 4 (ultimele două de biţi sunt 0), un QWORD la o adresă divizibil cu 8 (ultimele trei biti 0), şi aşa mai departe.
Motiv pentru care este convenabil să se alinieze de date este în câştig de eficienţă: astfel, se accesează procesor mai rapid. Accesul la un DWORD nu sunt aliniate, de exemplu, forţează procesorul să facă două cereri de memorie în loc de unul.
În Windows 64-bit, cu toate acestea, alinierea stivei este necesară pentru apelurile API, astfel încât acesta devine mai mult decât o problemă de performanţă.
MOV instrucţiuni
Instrucţiunea MOV este una dintre cele mai obisnuite instructiuni de asamblare şi. Formatul general este după cum urmează:
MOV DST , src
şi ceea ce educaţia nu este să copiaţi dst de date, care este inclusă în src . Cei doi operanzi sunt neapărat de aceeaşi mărime.
În forma sa cea mai simplă, DST şi src sunt registre sau un registru şi un operand de memorie (dar nu şi doi operanzi din memorie):
MOV RCX , R12 , R12, în copie RCX
MOV RCX , QWORD ptr [ esp ] ; copie RCX QWORD partea de sus a stivei
mov byte ptr [ RSI ] , r12b ; copiază cel mai semnificativ octet de R12
, adresa indicată de RSI
MOV RCX , QWORD ptr [ esp ] ; copie RCX QWORD partea de sus a stivei
mov byte ptr [ RSI ] , r12b ; copiază cel mai semnificativ octet de R12
, adresa indicată de RSI
În cazul în care ora de vară este un registru de 8, 16, 32 sau 64 de biţi, src poate fi, de asemenea, un efect imediat de aceeaşi mărime. Dacă dst este un operand în memorie de 8, 16, 32-bit, src poate fi un efect imediat de aceeaşi dimensiune, în timp ce în cazul în care ora de vară este un operand pe 64-biţi de memorie, src poate fi încă o imediat cu 32-bit a semnat, care se extinde cu semn ( semn-extins ) 64-biţi şi de copiat la dst .
mov la , 12h şi scrie un număr de 8-biţi în
MOV este , 1234h , scrie un număr de 16-biţi este în
mov ecx , 11223344h , scrie un număr de 32-biţi în ecx
, (şi resetează cele 32 de biţi cele mai semnificative ale RCX !)
MOV R14 , 1122334455667788h ; R14 a scris într-un 64-biţi
mov byte ptr [ EBP ] , 64h ; iniţializează un octet în memorie
mov cuvânt ptr [ EBP ] , 12345 , iniţializează un cuvânt în memorie
mov dword ptr [ EBP ] , 401000h ; initializa un DWORD în memorie
mov QWORD ptr [ EBP ] , - 5 ; iniţializează un QWORD în memorie
, cu un 32-bit a semnat întreg
mov QWORD ptr [ EBP ] , 100000000h ; GREŞIT! Al doilea operand
, nu poate fi interpretată ca
un întreg pe 32 de biţi a semnat!
MOV este , 1234h , scrie un număr de 16-biţi este în
mov ecx , 11223344h , scrie un număr de 32-biţi în ecx
, (şi resetează cele 32 de biţi cele mai semnificative ale RCX !)
MOV R14 , 1122334455667788h ; R14 a scris într-un 64-biţi
mov byte ptr [ EBP ] , 64h ; iniţializează un octet în memorie
mov cuvânt ptr [ EBP ] , 12345 , iniţializează un cuvânt în memorie
mov dword ptr [ EBP ] , 401000h ; initializa un DWORD în memorie
mov QWORD ptr [ EBP ] , - 5 ; iniţializează un QWORD în memorie
, cu un 32-bit a semnat întreg
mov QWORD ptr [ EBP ] , 100000000h ; GREŞIT! Al doilea operand
, nu poate fi interpretată ca
un întreg pe 32 de biţi a semnat!
Dacă ora de vară este de AL , AX , EAX , sau Rax , puteţi citi, de asemenea, o valoare în memorie, la o adresă dat pe 64 de biţi (desigur, pe x86 puteţi face doar cu o adresă de 32-biţi). MOV este singura instrucţiune poate avea o valoare ca un operand în memorie, cu o adresă de la 64 de biţi.
mov la , byte ptr [ 1122334455667788h ] , citeşte un octet în memoria
MOV Rax , QWORD ptr [ 1122334455667788h ] ; citi un QWORD în memorie
mov RBX , QWORD ptr [ 1122334455667788h ] ; GREŞIT!
MOV Rax , QWORD ptr [ 1122334455667788h ] ; citi un QWORD în memorie
mov RBX , QWORD ptr [ 1122334455667788h ] ; GREŞIT!
Desigur, în practică, este foarte rar de a scrie adresa, în acest fel, pentru că puteţi utiliza abordarea PAR legate . În x86, cu toate acestea, este destul de comună pentru a avea acces la date globale.
În schimb, în cazul în care src este AL , AX , EAX , sau Rax , ora de vară poate fi o valoare în memoria de care este dat adresa (de exemplu: MOV byte ptr [1122334455667788h], la ).
Există, de asemenea, variaţii educaţie MOV pentru a citi sau a scrie segmentul Registrul S, Registrul Debug sau S registru de control al lui.
Instrucţiunea MOV nu se modifica registry RFLAGS .
PUSH şi POP instrucţiuni
PUSH şi POP sunt cele două principale instrucţiuni pentru manipularea stivei. Atât ia un parametru; PUSH nevoie pentru a pune ceva în stivă POP să-l aducă.
Instrucţiunea PUSH decrements registrul RSP de o valoare egală cu 8 în x64, x86 la 4, apoi salvează valoarea de operand la adresa indicată de RSP . Operand de PUSH poate fi un registru, o memorie sau o valoare imediată. În x64, prompt, nu poate fi de 64 de biţi, dar poate fi un întreg pe 32 de biţi a semnat, care este semn-a extins şi introduse pe stiva de 64-bit valoare.
împinge RDX , RDX salvează stiva
Apasă QWORD ptr [ Rax ] , salvaţi pe stivă valoarea indicat de către EAX
Apasă 12 ; salvează stivă valoarea de 12 (imediat 8-biţi este, însă
, valoarea împins pe stiva are încă 64 bit
împinge 12345678h ; salvează stiva QWORD 0x0000000012345678
Apasă QWORD ptr [ Rax ] , salvaţi pe stivă valoarea indicat de către EAX
Apasă 12 ; salvează stivă valoarea de 12 (imediat 8-biţi este, însă
, valoarea împins pe stiva are încă 64 bit
împinge 12345678h ; salvează stiva QWORD 0x0000000012345678
Instrucţiunea PUSH de lucru cu 32-bit x86 este regula, dar nu există în x64. În schimb, există atât în educaţie arhitectura PUSH de lucru cu 16-biţi, dar are, de obicei, lipsită de sens, deoarece lasă stiva nu este aliniat (dar, de exemplu, nimic nu împiedică să utilizaţi 4 PUSH 16-biţi şi apoi alegeţi unul QWORD) .
Operand POP este în schimb un registru sau o locaţie de memorie de 16, 32 sau 64 de biţi (32-bit versiune nu există în x86 x64, pe de altă parte, există, desigur, pe 64 de biţi).
POP copiaza valoarea de pe stivă în operandul destinaţie, apoi creşte RSP (8, 4 sau 2 pentru POP la 64, 32, 16 biţi).
pop Rax , Rax salvează valoarea în partea de sus a stivei
pop QWORD ptr [ CDI ] , cu excepţia [CDI], valoarea la partea de sus a stivei
pop QWORD ptr [ CDI ] , cu excepţia [CDI], valoarea la partea de sus a stivei
Începătorii sunt adesea confundate de faptul că PUSH (= a pus ceva pe stiva) scade RSP , în timp ce POP trepte (= ia ceva din stiva). Stivă creste spate, asa ca mai bine te obişnuieşti cu el.
PUSH şi POP sunt adesea folosite în perechi, pentru a salva unul sau mai multe valori de pe stivă şi a restabili mai târziu:
împinge RBX ; Salvare RBX
apăsare RSI , RSI salvează
apăsare CDI , CDI salvare
, ...
, [cod care utilizează RBX, CDI şi RSI]
; ...
pop CDI , CDI Salvare
pop RSI , RSI Salvare
pop RBX , RBX salvat
apăsare RSI , RSI salvează
apăsare CDI , CDI salvare
, ...
, [cod care utilizează RBX, CDI şi RSI]
; ...
pop CDI , CDI Salvare
pop RSI , RSI Salvare
pop RBX , RBX salvat
Reţineţi că ordinea de POP este opusă celei de PUSH .
Instrucţiunea PUSH este, de asemenea, utilizat pentru a trece parametrii la un apel de funcţie, atunci când au trecut prin registre (a se vedea mai târziu de asteptare convenţii).
Pentru a şterge unul sau mai multe elemente din stivă fără a le citi, puteţi utiliza pur şi simplu instrucţiunea ADD(adăugaţi 8 * n octeţi la RSP în echivalent 64 de biţi de eliminarea n elemente).
LEA
Educaţie LEA ( Adresa de încărcare efectivă ) este conectat, într-un sens, educaţie MOV . LEA nevoie de doi parametri, primul este un registru de 16, 32 sau 64 de biţi. Al doilea este o valoare în memorie.
De fapt, operand memoria este doar fictivă, deoarece, în realitate, nu există acces la RAM a procesorului. LEA în registrul de destinaţie singura copie adresa operandului în memorie.
De exemplu:
lea Rax , [ RBX + RCX ] , pune suma de Rax şi RBX RCX
Lea RBX , [ etichetă ] ; RBX pune eticheta de adresa (pe 64 de biţi, aceasta
, va fi codificat cu ajutorul RIP-adresare relativă)
, această declaraţie ocupa 5 bytes, în timp ce
echivalent cu MOV ocupă 9.
lea EDX , [ EDX + EDX * 8 ] ; EDX multiplica de 9
Lea RBX , [ etichetă ] ; RBX pune eticheta de adresa (pe 64 de biţi, aceasta
, va fi codificat cu ajutorul RIP-adresare relativă)
, această declaraţie ocupa 5 bytes, în timp ce
echivalent cu MOV ocupă 9.
lea EDX , [ EDX + EDX * 8 ] ; EDX multiplica de 9
Ultimul exemplu arată utilizarea de LEA de a efectua calcule simple, care, alternativ, ar fi nevoie de mai mult de o singură instrucţiune. În plus, LEA are proprietatea, uneori de dorit să nu modifice orice pavilion S.
De fapt, dimensiunea operand prima şi dimensiunea registrelor utilizate în abordarea de al doilea operand poate fi diferit. Dacă primul operand este mai mic, rezultatul este trunchiat, în cazul în care aceasta este mai mare, este zero extins :
lea AX , [ RBX + CDI ] , pune securea în partea de jos 16 de biţi RBX CDI +
lea RBX , [ eax + ecx * 2 + 15 ] ; nimic pentru a explica aici :)
lea RBX , [ eax + ecx * 2 + 15 ] ; nimic pentru a explica aici :)
XCHG
Instrucţiuni XCHG ( eXchange ) necesită doi operanzi, care pot fi sau două registre, un registru şi un operand de memorie. Cei doi operanzi pot fi mare 8, 16, 32 sau 64 de biţi, dar trebuie să fie de aceeaşi dimensiune. Educaţie XCHGvaloarea de schimb a doi operanzi.
xchg Rax , RDX , Rax şi swap RDX
xchg cl , byte ptr [ Rax ] , swap cl cu octetul indicat de către Rax
xchg ecx , 12 ; GREŞIT! Nici operand poate fi o clipă!
xchg cl , byte ptr [ Rax ] , swap cl cu octetul indicat de către Rax
xchg ecx , 12 ; GREŞIT! Nici operand poate fi o clipă!
NOP
Instrucţiuni NOP ( nr de operare ) este o simplă care există ... De fapt, treaba lui este de a face absolut nimic (cu excepţia, desigur, creşterea RIP pentru a sări la următorul!). Opcode educaţie NOP este 0x90, un octet, iar acest lucru îl face deosebit de util pentru scopuri de inginerie inversă sau de cracare. Pentru a contracara educaţie (cum ar fi un salt condiţionată), poate fi înlocuit cu o serie de NOP exact atâta timp cât situaţia (şterge declaraţie, de fapt, să submineze alinierea a fişierului).
Math Instrucţiuni
ADD şi SUB
Instrucţiunile ADD şi SUB vă permite să efectuaţi plus şi sume. Formatul general este:
ADD '' DST '' , '' src '' , adaugă src la ora de vară, salvarea rezultat în DST
SUB '' ora de vară '' , '' src '' ; scade la src dst, salvarea rezultat în DST
SUB '' ora de vară '' , '' src '' ; scade la src dst, salvarea rezultat în DST
în cazul în care ora de vară poate fi un registru sau o valoare de memorie, src -un registru, o valoare de memorie sau o constanta imediat cu mare inscriere de pana la 32 de biţi (limită se aplică, chiar dacă ora de vară este de 64 de biti). srcşi DST , cu toate acestea, nu pot ambii operanzi în memorie, şi trebuie să fie de aceeaşi mărime (cu excepţia cazului în care src este o imediat, caz în care, în cazul în care aceasta este mai mică, este pur şi simplu extins cu semnul care urmează să fie adăugat / scăzute la ora de vară ). Exemple:
adauga Rax , RBX , RBX pentru a adăuga Rax
adăuga ecx , EAX , EAX la ecx add (şi resetează 32 de biţi ridicat de RCX!)
sub SP , cuvântul ptr [ R12 ] ; scade cuvântul în [r12w], un SP
sub a , ah , ah scade de la
add dx , 1234h , adaugă valoare la dreapta 16-bit
sub PFR , - 5 , scade de la -5 la PFR
Adauga QWORD ptr [ Rax ] , - 100000 , -100000 adaugă la QWORD în [Rax]
adauga RSI , - 3000000000 , GREŞIT! Imediată nu este în 32-biţi
adăuga ecx , EAX , EAX la ecx add (şi resetează 32 de biţi ridicat de RCX!)
sub SP , cuvântul ptr [ R12 ] ; scade cuvântul în [r12w], un SP
sub a , ah , ah scade de la
add dx , 1234h , adaugă valoare la dreapta 16-bit
sub PFR , - 5 , scade de la -5 la PFR
Adauga QWORD ptr [ Rax ] , - 100000 , -100000 adaugă la QWORD în [Rax]
adauga RSI , - 3000000000 , GREŞIT! Imediată nu este în 32-biţi
Dacă al doilea operand este imediată, ambele codificări de instruire au mai scurtă în cazul în care destinaţia este AL ,AX , EAX , sau Rax .
Atât de educaţie şi ADD că educaţia SUB modifica toate cele 6 opţiuni de stare e asa de acord cu rezultatul operaţiei.
Uneori, instrucţiunea SUB este folosit pentru a reseta un registru detaşat de la sine, dar pentru acest scop, se recomandă (şi cel mai frecvent), educaţie logic XOR .
GNM
Instrucţiuni GNM ( nega) are doar un operand, o valoare de registru sau o memorie de 8, 16, 32 sau 64 de biţi; GNMefectueaza complement de 2 de operand sale, care calculează opus. Evident, aceasta are sens doar pentru a întregi semnate.
GNM Rax , Rax calculează opus al
GNM QWORD ptr [ RSP ] ; calculează opus de pariu QWORD RSP
neg r13b ; calculeaza inversul octetul cel mai puţin semnificativ de R13
GNM QWORD ptr [ RSP ] ; calculează opus de pariu QWORD RSP
neg r13b ; calculeaza inversul octetul cel mai puţin semnificativ de R13
GNM stabileşte drapelul Carry la 0, dacă valoarea operand este 0, altfel setaţi la 1. Alte opţiuni de stare ( A , SF , ZF , AF , şi PF ), sunt setate pentru a conveni asupra rezultatului.
ADC şi SBB
Instrucţiunile ADC şi SBB (respectiv cu ADD Carry şi scade cu Imprumuta ) au aceeaşi sintaxă pentru ADD şi SUB , dar, de asemenea, aceeaşi funcţie. Singura diferenţă este că ADC adaugă în continuare de la 1 la rezultat, dacă pavilion Carryeste 1; în mod similar, SBB scade 1 daca CF este de 1. În schimb, ei se comporta exact ca ADD şi SUB dacă CF este 0.
Instrucţiuni ADC şi SBB sunt folosite pentru a contabiliza report ( să ) sau de împrumut ( împrumut ), în cazul de adăugiri sau scaderi in unele parti. De exemplu, dacă doriţi să adăugaţi Rax : RBX (de exemplu, concatenarea de Rax şi RBX , un număr de 128-biţi), cu RCX : RDX , puteţi folosi codul de mai jos:
adauga RBX , RDX , suma de 32 de biţi mai mică
ADC Rax , RCX , însumează 32 de biţi superioare, plus orice report
ADC Rax , RCX , însumează 32 de biţi superioare, plus orice report
Pentru scădere, în loc de:
sub RBX , RDX , scade mai mică 32 de biţi
SBB Rax , RCX , scade la 32 de biţi superioare, minus 1 în cazul în care împrumutul a fost
SBB Rax , RCX , scade la 32 de biţi superioare, minus 1 în cazul în care împrumutul a fost
INC şi decembrie
INC şi decembrie ( incrementare şi decrementare ) au doar un operand, care poate fi un registru sau un operand de memorie. Efectul acestor instrucţiuni este de a adăuga ( INC ) sau scădea ( decembrie ) 1 operand, ca şi instrucţiunileADD şi SUB în cazul în care al doilea operand este 1. Cu toate acestea, ele au o codificare mai scurtă.
Singura diferenţă între ADD şi SUB este că INC şi decembrie păstra pavilion Carry , toate celelalte opţiuni sunt modificate în acelaşi mod.
MUL şi IMUL
Instrucţiunile MUL şi IMUL ( Multiply ) sunt utilizate pentru a efectua înmulţiri, respectiv întregi fără semn sau semnate.
Instrucţiunea MUL are doar un operand, o valoare de registru sau o memorie de 8, 16, 32 sau 64 biti. În funcţie de mărimea operand, MUL efectuează produsul dintre valoarea operandului şi AL , AX , EAX , sau Rax , respectiv, iar rezultatul este stocat în AX , DX: AX , EDX: EAX , RDX: Rax , în cazul în care notaţie cu un colon indică concatenare.Registrele de ţintă au o dimensiune dublă, deoarece, în general, produsul a două numere de n -biţi nevoile de 2 n biţi pentru a fi conţinute în întregime. Exemple:
mai multe BH , BH * pentru a calcula, salvaţi rezultatul în ax
Mul r15w ; Calculează x * r15w, rezultat în DX: AX
Mul dword ptr [ R12 ] ; calculează EAX * [R12], rezultat în EDX: EAX
Mul Rax ; calculează Rax pătrat, rezultat RDX: Rax
Mul 12 ; GREŞIT! Operand nu poate fi imediată.
Mul r15w ; Calculează x * r15w, rezultat în DX: AX
Mul dword ptr [ R12 ] ; calculează EAX * [R12], rezultat în EDX: EAX
Mul Rax ; calculează Rax pătrat, rezultat RDX: Rax
Mul 12 ; GREŞIT! Operand nu poate fi imediată.
Instrucţiuni IMUL este mult mai flexibil, şi poate să apară cu operanzi una, două sau trei.
In forma la un (valoare registru sau o memorie de 8, 16, 32 sau 64 de biţi) operand, IMUL loperando multiplică valoarea de AL , AX , EAX , sau Rax (în funcţie de mărimea a operandului), şi magazine rezultat în AX , DX: AX , EDX: EAX, sau RDX: Rax : cu alte cuvinte, acesta funcţionează ca MUL , numai că de multiplicare se face având în vedere ca operanzi întregi semnate. Exemplu:
IMUL RBX , RBX la Rax se multiplica, ducand la RDX: Rax
În formă de doi operanzi, primul este tinta, un registru de 16, 32 sau 64 de biţi, şi a doua valoare într-un registru sau o memorie de dimensiune egală, sau o valoare imediat (dacă operandul imediat este mai mic, este semn-extins , dacă operanzii sunt 64 de biţi, în imediata este încă de până la 32 de biţi). În acest caz, destinaţia se înmulţeşte cu al doilea operand şi rezultatul este salvat în ţintă.
IMUL cx , r15w ; multiplica CX pentru r15w
IMUL RDX , QWORD ptr [ RSP ] , înmulţită cu RDX [RSP]
IMUL pb , 10 , înmulţit cu 10 bp
IMUL Rax , 123456789h ; GREŞIT! Imediat prea mare pentru 32-biţi
IMUL RDX , QWORD ptr [ RSP ] , înmulţită cu RDX [RSP]
IMUL pb , 10 , înmulţit cu 10 bp
IMUL Rax , 123456789h ; GREŞIT! Imediat prea mare pentru 32-biţi
În formularul de trei-operand, este prima tinta, un registru de 16, 32 sau 64 de biţi, al doilea este o valoare registru sau o memorie de dimensiuni egale, iar a treia este o imediat (din nou, în cazul în care imediat este de 8 operanzi biţi sunt mai mari, imediat este semn-extins , nu sunt permise imediat 64-bit). Instrucţiuni multiplică apoi operand doilea şi al treilea şi magazine rezultat în primul.
IMUL RBX , Rax , - 99 ; calculează * Rax (-99) şi salvează rezultatul în RBX
Am observat că în formele de două şi trei operanzi, destinaţia nu este cea mai mare de produse, deci, dacă rezultatul este prea mare pentru a intra sau de sosire, v-ar lua doar jumatate cel puţin semnificativ.
În cazul de MUL sau IMUL cu un operand, Drapelul Carry şi " Drapelul de preaplin sunt setat la 1, în cazul în care partea de sus a rezultatul nu este 0, şi zero în caz contrar. Pentru două şi trei operanzi forme de IMUL , CF şi " A 1 se face în caz de depăşire, şi anume în cazul în care rezultatul este mai mare decât registrul de destinaţie (de fapt, nu foarte diferit de la caz la 1 adresa).
Alte opţiuni de stare s ( SF , ZF , AF , şi PF ) sunt nedefinite dupa MUL sau IMUL , deci nu ar trebui să se bazeze pe valoarea lor.
DIV şi IDIV; CBW, CWD, Originala, CQO
Instrucţiunile DIV şi IDIV performante divizii, respectiv, cu şi fără un semn. Ambele accepta un set de instrucţiuni singur operand, un registru sau o valoare de memorie de 8, 16, 32 sau 64 de biţi reprezintă împărţitor. Dividendul este de două ori dimensiunea a divizorului, şi poate fi AX , DX: AX , EDX: EAX , sau RDX: Rax , respectiv.
Dacă împărţitorul este de 8 biţi, rezultatul merge în AL şi restul în AH , în cazul în care are 16 de biţi, rezultat în AX şi restul în DX , are 32 de biţi, rezultatul în EAX şi restul în EDX , în cazul în care acesta 64-biţi, în cele din urmă, rezultatul ar trebui să fie în Rax şi restul în RDX .
Cele două situaţii generează o excepţie în două cazuri: dacă împărţitorul este 0 (deoarece nu are sens pentru a împărţi cu 0) şi, dacă rezultatul este prea mare pentru a încăpea în registrul de destinaţie ( overflow ). Toate steagurile de stare sunt s nedefinite dupa DIV sau IDIV .
Dacă doriţi să partajaţi Rax înregistraţi pentru un alt 64-biţi (nesemnate), trebuie să vă asiguraţi că registrul RDX este 0, altfel suna la preaplin (în plus faţă de rezultatele neaşteptate). Cu toate acestea, dacă vă face o divizie cu semn, este necesar să se extindă semn de Rax în toate biţi de RDX , în scopul de a construi dividendul la 128 de biti. Acest lucru se face cu educaţia CQO ( Conversia quadword la Octaword ), care apare de obicei chiar inainte de IDIV . Taxele la biţi 32, 16 şi 8 sunt, respectiv, CDQ ( Conversie doubleword la quadword : extends EAX în EDX: EAX ), CWD ( Conversia Word pentru a dubla cuvântul : se extinde AX în DX <: AX ) şi CBW ( Conversia Byte în Word : se extinde AL în AX ). Nici una dintre aceste instrucţiuni schimba steaguri.
Nu există nici o formă de diviziune pentru imediat, asa ca daca ai nevoie de un set de separator, trebuie să-l încărcaţi într-un registru şi să-l utilizaţi ca un divizor.
Exemple (presupunând că nu împărţitorul este 0):
XOR RDX , RDX , RDX la zero
MOV ebx , 10 , 10, în RBX pune
div RBX ; decalajul (nesemnate), EAX la 10
, rezultatul va fi Rax, RDX în restul
CDQ , semnul se extinde la EAX EDX: EAX
idiv dword ptr [ R13 ] ; decalajului EDX: EAX la DWORD indicat de R13
MOV ebx , 10 , 10, în RBX pune
div RBX ; decalajul (nesemnate), EAX la 10
, rezultatul va fi Rax, RDX în restul
CDQ , semnul se extinde la EAX EDX: EAX
idiv dword ptr [ R13 ] ; decalajului EDX: EAX la DWORD indicat de R13
Instrucţiuni logice
Instrucţiunile logice sunt acele instrucţiuni care operează la nivel de bit, cele mai frecvente sunt ŞI , SAU , XOR , NU , deschimburi şi rotaţii .
ŞI, SAU, XOR
ŞI , SAU , şi XOR ( SAU exclusiv ) sunt operaţiuni binare (de exemplu, cu doi operanzi), care are aceeaşi sintaxă pentruADD şi SUB . Aceste operaţiuni acţionează prin efectuarea operaţiunilor între biţi corespunzătoare de operanzi lor.
ŞI setată la 1 în toate ţintă numai acei biţi i astfel încât biţi corespunzătoare ale ambii operanzi sunt: 1.
SAU seturi de la 1 în destinaţie, toţi i şi doar acele biţi astfel încât cel puţin unul dintre biţi corespunzătoare din celelalte două operanzi este 1.
XOR face o destinaţie în toate şi doar acele biţi, astfel încât biţi corespunzătoare a doi operanzi sunt diferite.
Presupunând că valorile iniţiale ale două registre de 8 biti, în binar, sunt 10110010 şi 11100111; apoi:
ŞI 10110010 11100111 = ------------ 10100010 | SAU 10110010 11100111 = ------------ 11110111 | XOR 10110010 11100111 = ------------ 01010101 |
Aceste instrucţiuni sunt adesea folosite pentru a manipula biţi individuală: ŞI şterge toate biţi, în al doilea operand sunt 0 şi lasă neschimbat de altă parte; o SAU stabileşte la 1 toţi biţii care sunt de 1 la al doilea operand şi lasă neschimbat cealaltă, o XOR inversează toate biţi care sunt de 1 la al doilea operand şi lasă altora neschimbat.
Instrucţiuni XOR este, de asemenea, frecvent utilizat pentru a şterge un registru, care efectuează o XOR între registru şi el însuşi. Cu toate acestea, XOR are efect secundar de a modifica aceste steaguri, în cazul, destul de rare, este necesar să se păstreze steaguri, se poate recurge la un simplu MOV , care are însă dezavantajul de a avea o codificare mai mult.
O proprietate utilă a operaţiunii XOR este după cum urmează: două operaţiuni XOR la aceeaşi valoare sunt anulate (adică este întotdeauna adevărat că un XOR b XOR b = a ), şi acest lucru îl face potrivit pentru sistemele de criptare simple.
Toate aceste instructiuni stabilesc indicatorii SF , ZF , şi PF se bazează pe rezultatul, întotdeauna resetate DE , şi CF(din motive evidente nu poate genera pase!), în timp ce valoarea de pavilion AF este nedefinit.
Exemple:
şi Rax , RBX şi la , 11111011b ; pic mai mic treilea la zero sau Rax , 16 , 16 în binar este de 10000, apoi setaţi de-a cincea ultima biţi XOR BX , - 1 , -1 are toate 1 biţi, şi apoi de mers înapoi toate biţi de bx sau ecx , dword ptr [ PFR ] XOR la , r8b XOR EDX , EDX , EDX la zero (în 64 de biţi de resetare everythink RDX) şi R15 , 102030405h ; GREŞIT! Imediată este prea mare!
NU
Instruire NU , spre deosebire de alte operaţiuni logice, are doar un operand, o valoare registru sau o memorie de 8, 16, 32 sau 64 biti. Această instrucţiune se completează cu 1 a operandului său, adică inversează toate biţi.Schimbarea registrul de destinaţie este astfel echivalent cu un XOR cu o valoare cu toate 1 biţi, cu toate acestea, instrucţiuni NU nu se modifica nici steaguri.
nu RDX nu dword ptr [ Rax * 4 + RSI ] nu 15 ; GREŞIT! Evident, operand nu poate fi imediat!
SHL, SHR, SAL, SAR
Instrucţiunile SHL şi SHR ( Shift stânga şi Shift dreapta ), care efectuează operaţiunea de o schimbare de valoare.
Schimbarea este de a transfera biţi anumit număr de posturi. De exemplu, în cazul în care un registru conţine valoarea binar 0 1 1 011 0 1 , trecerea la stânga o pozitie este un 1011 0 1 0; trecerea la stânga de două poziţii, 1011 0 1 00 (primii doi biti sunt "împinse . out ", în timp ce cei doi biţi de la partea de jos sunt umplute cu 0 schimbare dreapta de 1 bit este 0 0 1 1 011 0 , trecerea la dreapta de 2 biţi este 00 0 1 1011 (de data aceasta sunt împinse din cele mai puţin biţi semnificative, zerouri şi se adaugă de la stânga).
SHL şi SHR avea 2 operanzi: primul este o valoare registru sau o memorie (8, 16, 32 sau 64 biţi), şi este tinta, al doilea operand poate fi registrul CL , sau o imediat 8-biţi fără semn ( care este, între 0 şi 255), şi reprezintă numărul de posturi pe care le doresc de shiftare destinaţie. Din al doilea operand sunt luate doar mai mici de 5 biţi (sau 6, dacă obiectivul are 64 de biţi), astfel încât contorul a numărului de posturi este între 0 şi 31 (între 0 şi 63, dacă obiectivul are 64 de biţi).
SHL şi SHR , cu un contor de diferit de la 0 modificarea steagurile (pentru detalii, poate fi văzut în utilizare), în special, CFeste setat egal cu ultimul bit, care a fost împins afară.
Exemple:
SHL Rax , 1
SHR ebx , cl
SHL cuvânt ptr [ R12 ] , 13
SHR cl , la ; GREŞIT! Valabilă decât registru operand este cl
SHR ebx , cl
SHL cuvânt ptr [ R12 ] , 13
SHR cl , la ; GREŞIT! Valabilă decât registru operand este cl
Din punct de vedere al aritmeticii, o schimbare la stânga de n biţi este echivalentă cu înmulţirea unui număr nesemnat de 2 n ; o schimbare de drept de n biţi este echivalentă cu o divizie (cu trunchiere) de un număr nesemnat de 2 n .Utilizarea de schimbare pentru a evita înmulţiri şi (mai ales), diviziunile este mult mai eficient. Evident, schimbarea poate fi utilizat în alte scopuri de manipulare biţi
Dacă vrei să faci înmulţiri şi divizărilor de puteri de 2 numere de semnat, folosind instrucţiunile de SAL ( Shift aritmetică stânga ) şi SAR ( Shift dreapta Aritmetical ). Sintaxa de operanzi este acelaşi, iar funcţiile sunt, de asemenea, similare.De fapt, SAL este un alias de SHL şi nu diferă în nici un fel (Opcode este unul, nu sunt două instrucţiuni diferite). SAR , în schimb, se comporta ca SHR , cu diferenţa că biţi care "intra" din stânga sunt umplute cu valoarea operandului originale bitul de semn. Deci, în timp ce trecerea prin dreptul de un pic de 10011010 este 01001101, trecerea de aritmetică este 11001101. Reţineţi că binar 10011010 egal zecimal -102, în timp ce 11001101 este egal cu -51 (jumătate de preţ!).
În fapt, rezultatul a SAR , cu contor n este egal cu cel de IDIV cu divizor 2 N, numai pentru numerele pozitive sau atunci când divizia este exactă; fapt, în timp ce SAR trunchiază întotdeauna implicit, IDIV trunchiază spre 0 (şi apoi un exces în cazul numere negative). De exemplu, dacă utilizaţi IDIV -9 pentru a împărţi cu 4, rezultatul este de -2 şi restul -1 (notă de faptul că acest lucru nu este în concordanţă cu definiţia matematică a compartimentării, cu restul, în care restul este întotdeauna pozitiv). În schimb utilizarea SAR shiftare -9 de 2 biţi, rezultatul este -3 (şi restul, care în acest caz nu este calculat în mod explicit, este de 3).
va Rax , 2 ; schimbare aritmetică de 2, echivalentul a împărţi cu 4
Lei şi ROR
De rotaţii sunt foarte similare cu trecerea, sintaxa de lei ( Rotire la stânga ) şi ROR ( Rotire la dreapta ) este aceeaşi caSHL şi SHR . Diferenţa este că biţi "împins" de o scădere pe de altă parte. Cu acelaşi exemplu de mai sus, de rotaţie la stânga de un bit de 0 1 1 011 0 1 este un 1011 0 1 0 . Rotaţie la dreapta este unul 0th primul 1011th 0 . Chiar şi în acest caz, CF este setat ca ultimul bit împins afară.
De control al declaraţiilor
CMP şi TEST
Educaţie CMP ( Compară ) are exact aceeaşi sintaxă a SUB . În fapt, efectuează, de asemenea, aceeaşi operaţie, cu excepţia pentru un detaliu : nu modifică registrul de destinaţie. Singurul efect este de a actualiza starea de pavilion , bazata pe rezultatul scădere între primul şi al doilea operand. De obicei, este folosit pentru a compara două numere înainte de o instrucţiune de salt condiţionat (a se vedea mai jos).
CMP Rax , RBX , Rax şi RBX compara
Instrucţiuni TEST , în mod similar, are aceeaşi sintaxă şi funcţia aceeaşi educaţie şi , din nou, fără a schimba registrul de destinaţie, dar numai schimbarea de pavilion S. Acesta poate fi folosit, de exemplu, pentru a vedea dacă o anumită valoare pic a este 1 sau 0:
teste pentru , 8 ; ZF sectă, dacă al patrulea bit ultima este 0
O utilizare tipică este de a verifica dacă un registru este 0 sau nu:
test de RCX , RCX , RCX stabileşte ZF este 0 dacă
CMP şi TEST sunt utile mai ales cu instrucţiuni de salt condiţionat.
Instrucţiuni pentru datele de conversie a
MOVZX, MOVSX, MOVSXD
Instrucţiunile MOVZX ( MOVE cu zero eXtinse ), MOVSX ( MOVE cu semnul eXtinde ), MOVSD ( MOVE cu semnul eXtinse doubleword ) sunt destinate pentru a face conversii vă dau mai mică în date mai mari. Toate nevoie de doi parametri, primul (de destinaţie) este un registru, al doilea (sursa), un registru sau o valoare de memorie.
Primul parametru de MOVZX şi MOVSX . poate fi de 16, 32 sau 64 de biţi, în timp ce al doilea poate fi de 8 sau 16 biti (dar nu poate fi atât de 16 biţi) MOVZX zero extinde sursă şi salvează rezultatul în ţara de destinaţie; MOVSX foloseşte în schimb, un semn de extindere . Exemple:
Al doilea operand nu poate , fi 32-bit
Instrucţiuni MOVSXD există numai în x64, şi serveşte pentru a extinde (cu semn), un număr întreg de la 32 la 64 de biţi.Primul operand este, aşadar, un registru de 64-biţi, a doua valoare într-un registru sau o memorie de 32-biţi. Exemplu:
Rax movsxd , ecx , ecx extinde în Rax cu semnul
Nu există nici o instrucţiune este conceput pentru a face o prelungire , cu nici un semn. Cea mai mare atenţie se va şti deja de ce: doar un simplu MOV cu 32-bit destinaţie. De fapt, în 64 de biţi, operaţii pe 32-biţi registre sunt zero-extins în mod automat, astfel încât să puteţi face sens pentru educaţie (la prima vedere o prostie), ca aceasta:
mov ecx , ecx , ecx la ecx copie (în fapt: cu zero-extends RCX)
Instrucţiuni pentru controlul fluxului de
Instrucţiunile de flux de control sunt acele instrucţiuni care vă permit să schimbaţi " Pointer de instrucţiuni , care efectuează un apel de procedură ( CALL / RET ), incodizionato un salt ( JMP ), sau un salt condiţionat ( J cc ).
Declaraţiile de control al debitului (excl. RET ) acceptă un parametru care este cel mai frecvent în forma unei adrese în raport cu PAR . Această deplasare poate fi la fel de mare ca 32-bit (semnat), astfel încât această formă de adresare vă permite să specificaţi destinaţii departe de curent până la 2 GB.
Asamblorii, totuşi, să ia sarcina de a calcula corect deplasarea , permiţându-vă să specificaţi destinaţie printr-un nume simbolic care eticheta (eticheta).
Câteva exemple vor fi văzut în secţiunile de mai târziu.
JMP
JMP ( JUMP ) efectuează un salt necondiţionat la operandului destinaţie specificată. În forma sa cea mai comuna, operand este o deplasare de 8-biţi ( aproape de salt ) sau 32-biţi ( salt scurt ). Apropiat şi salt scurt vă permite să săriţi peste numai în acelaşi segment, salt la (care noi nu facem dobânzii), cu toate acestea, vă permite să sari la o destinaţie într-un alt segment.
În asamblorii MASM şi alte, desigur, nu se scrie direct la deplasarea , ci indică mai degrabă numele de o etichetă, lăsând sarcina de codificare de asamblare. De exemplu, o buclă infinită ar putea avea această structură:
început : şi aceasta este o etichetă
...
; [cod, care face ceva]
...
JMP începe ; sări în sus
...
; [cod, care face ceva]
...
JMP începe ; sări în sus
Alternativ, operand o JMP poate fi un registru sau o valoare de memorie, care este destinaţia de salt (de exemplu, noua valoare a RIP ):
lea RCX , etichetă , sarcina RCX un pointer
JMP RCX , RCX sare la adresa conţinută în
JMP RCX , RCX sare la adresa conţinută în
Sau:
JMP QWORD ptr [Rax] sare la adresa conţinută în [Rax]
CUC
Familia lui Manualul J CC ( Salt daca starea ) vă permite să efectua salturi condiţionate, adică sare, care sunt făcute, dacă o condiţie este adevărată, altfel ignorate. Condiţie este de a verifica starea de unul sau mai multe steaguri de stare . Ca JMP , chiar J CC au un singur operand, care poate fi doar o deplasare relativă (astfel încât acestea pot fi doarde scurtă şi în apropiere ).
Sa vedem lista de instrucţiuni de ramură condiţionale, în total sunt 16 instrucţiuni diferite, dar mnemonice sunt mult mai eficiente, din cauza mai multe sinonime:
Mnemonic | Condiţie | Explicaţie |
JO | A = 1 | Salt dacă preaplin |
JNO | A = 0 | Salt dacă nu preaplin |
JB JC JNAE | CF = 1 | Salt în cazul în jos Salt dacă Carry Salt daca nu mai mare sau egală |
JNB JNC JAE | CF = 0 | Salt în cazul în care nu se situeze sub Jumpt dacă nu realizează Salt în cazul în care mai mare sau egală |
JZ JE | ZF = 1 | Salt dacă Zero Salt în cazul în care egalitatea |
Jnz JNE | ZF = 0 | Salt în cazul în care nu este zero Salt dacă nu egal |
JNA JBE | CF = 1 sau ZF = 1 | Salt daca nu Deasupra Salt daca mai mic sau egal |
JNBE JA | CF = 0 si ZF = 0 | Salt daca nu mai mic sau egal Jump if de mai sus |
JS | SF = 1 | Salt în cazul în care semnul |
JNS | SF = 0 | Salt dacă nu semneze |
JP JPE | PF = 2 | Salt dacă Paritatea Salt dacă Paritatea Chiar |
JNP JPO | PF = 0 | Salt daca nu Paritatea Salt dacă Paritatea Odd |
JL JNGE | SF! = A | Salt dacă mai Salt sau egal, dacă nu mai |
JGE JNL | SF = A | Salt daca mai mare sau egal Salt daca nu mai puţin |
JNG JLE | ZF = 1 sau SF! = A | Salt în cazul în care nu mai Salt dacă mai mic sau egal |
JNLE JG | ZF = 0 şi SF = A | Salt daca nu mai mic sau egal Salt dacă este mai mare |
S-au inventat mai multe sinonime pot alege cea mai adecvată în funcţie de sensul de cod, în scopul de a îmbunătăţi lizibilitatea a sursei. Desigur dezasamblori nu face întotdeauna o alegere mai consistent în rândul alias-uri disponibile, deoarece acestea nu pot înţelege sensul codului.
Numele multora dintre aceste declaraţii fac sens numai dacă sunt, după o instrucţiune de comparaţie ( CMP sau, desigur, SUB ), în timp ce alţii care specifică doar steaguri de control (de exemplu, JC , JO şi JP ).
Vă rugăm să reţineţi că termenii de mai sus / mai mari şi mai jos / mai puţin nu sunt complet sinonime: de fapt, de mai sus si mai jos face sens, după o comparaţie de numere întregi fără semn, şi mai mare şi mai puţin sens, după o comparaţie a întregi semnate.
Iată câteva exemple:
CMP Rax , RBX , Rax şi RBX compara
JE etichetă ; salt dacă egal
SHR CL , 1 , dreptul de schimbare de 1 bit
JC ciudat , Salt dacă pavilion carry este 1, adică în cazul în care bitul cel mai puţin semnificativ
; semnificativ a avut CL 1 ( de exemplu, CL a fost ciudat)
XOR ebx , r12d JP paritate , sari în cazul în care, după XOR, BL are un număr par de biţi 1 adăuga bx , dx JZ la zero , sari dacă suma este 0 , (de exemplu, dacă cele două numere s-au opus) CMP RCX , RDX JNA mai mult , salt, dacă nu mai mare de RDX RCX , dacă noi le considerăm ca nesemnat jng mai mult , salt, dacă nu mai mare de RDX RCX , dacă le consideră cu semnul sau , cl JS semn ; salt în cazul în care bitul 7 din (bitul de semn) este egal cu 1 , (acest lucru este echivalent cu faptul că, înainte de SAU, pic , semn de cel puţin una din cele două instrucţiunile pentru a fi 1, adică, , totuşi, că cel puţin un a doua este negativă)
JE etichetă ; salt dacă egal
SHR CL , 1 , dreptul de schimbare de 1 bit
JC ciudat , Salt dacă pavilion carry este 1, adică în cazul în care bitul cel mai puţin semnificativ
; semnificativ a avut CL 1 ( de exemplu, CL a fost ciudat)
XOR ebx , r12d JP paritate , sari în cazul în care, după XOR, BL are un număr par de biţi 1 adăuga bx , dx JZ la zero , sari dacă suma este 0 , (de exemplu, dacă cele două numere s-au opus) CMP RCX , RDX JNA mai mult , salt, dacă nu mai mare de RDX RCX , dacă noi le considerăm ca nesemnat jng mai mult , salt, dacă nu mai mare de RDX RCX , dacă le consideră cu semnul sau , cl JS semn ; salt în cazul în care bitul 7 din (bitul de semn) este egal cu 1 , (acest lucru este echivalent cu faptul că, înainte de SAU, pic , semn de cel puţin una din cele două instrucţiunile pentru a fi 1, adică, , totuşi, că cel puţin un a doua este negativă)
Cu instrucţiunile de salt condiţionat poate crea cât mai multe la nivel înalt construcţii.
Cod ca
IF (a == b) apoi
ceva;
ELSE
altceva;
ceva;
ELSE
altceva;
Devine ceva de genul (presupunând Rax şi RBX sunt în , şi b ):
CMP Rax , RBX jne altceva ceva : ; ... ; ramura cod, dacă ; ... JMP după altceva : , ... , cod ELSE sucursalei ; ... după :
O bucla de a repeta de 100 de ori codul este scris, de obicei, după cum urmează:
mov ecx , 100 ; ecx urmă de contor (nu este nevoie de a utiliza RCX). Utilizarea
, ca un contor de la acest registru nu este obligatorie
, dar este un standard de facto.
ciclul :
, ...
, cod în bucla
; ...
dec ecx ; decrements contra
jnz bucla , salt în cazul în care nu au fost încă de la 0
, ca un contor de la acest registru nu este obligatorie
, dar este un standard de facto.
ciclul :
, ...
, cod în bucla
; ...
dec ecx ; decrements contra
jnz bucla , salt în cazul în care nu au fost încă de la 0
Desigur, se poate traduce în limbaj de asamblare construi orice nivel ridicat.
CALL si RET
Instrucţiuni CALL si RET ( Revenire ) sunt utilizate pentru punerea în aplicare a apeluri de proceduri.
Chiar şi Educaţie CALL versiuni sunt aproape şi cei nu , vom analiza doar primul caz.
Declaraţia CALL pune stiva PAR ( PAR în 64 de biţi, EIP în x86), din următoarea instrucţiune (Această adresă se numeşteadresa expeditorului , adresa expeditorului). Deci, nu aceeaşi educaţie JMP , saltul de la adresa specificată de operandului sale unice. De asemenea, în acest caz, operand poate fi o deplasare de 32 de biţi (de exemplu, pentru cei care programul, o etichetă ), un registru sau o valoare de memorie.
Instrucţiunea RET poate continua executarea de la următoarea instrucţiune în CALL . RET are adresa de returnare de pe stivă şi stabileşte ca noi PAR .
RET pot lua, de asemenea, un argument pentru o nesemnat imediată 16-bit, care se adaugă la RSP , după eliminarea din stivă adresa de retur. De fapt, în funcţie de convenţia de asteptare, stiva apelantului unele sau toate dintre parametrii cerute de apel de funcţie; instrucţiuni RET este responsabil, prin urmare, de a creşte RSP pentru a le elimina.
În secţiunea următoare ne vom uita la două convenţii de asteptare.
Dacă funcţia numit returnează o valoare, standard este să se întoarcă în registrul Rax .
Prima ta listare
Să analizăm acum un program simplu pentru a înţelege ceea ce face. Descarcă aici ataşament. Acelaşi C, sursa este compilat pentru x86, x64 şi. Să ne considerăm primul 32-biţi, iar apoi analiza codul de 64-biţi.
Produsul a fost demontat de către AID Pro 5.1, dar orice versiune este bine (dacă nu este prea vechi). De utilizare a capacităţilor de dezasamblor este dincolo de sfera de aplicare a acestui tutorial.
Versiunea x86
Codul de procedura principal al prima_lezione_86.exe, este după cum urmează:
. textul : 00401020 _wmain proc aproape ; COD Xref: ___ tmainCRTStartup 10 A # p.
. de text : 00401020
. textul : 00401020 var_14 = dword ptr - 14h
. textul : 00401020 var_10 = dword ptr - 10h
. textul : 00401020 var_C = dword ptr - 0Ch
. textul : 00401020 var_8 = dword ptr - 8
. textul : 00401020 var_4 COD xref: _wmain ENDP
. de text : 00401020
. textul : 00401020 var_14 = dword ptr - 14h
. textul : 00401020 var_10 = dword ptr - 10h
. textul : 00401020 var_C = dword ptr - 0Ch
. textul : 00401020 var_8 = dword ptr - 8
. textul : 00401020 var_4 COD xref: _wmain ENDP
Înainte de a citi explicaţia de cod, încercaţi doar "pentru a înţelege exact ceea ce se intampla, dar nu vă faceţi griji dacă nu.
Să începem cu primele linii:
. textul : 00401020 apăsare EBP
. textul : 00401021 MOV EBP , ESP
. textul : 00401023 sub ESP , 14h
. textul : 00401021 MOV EBP , ESP
. textul : 00401023 sub ESP , 14h
Primele două linii de a crea aşa-numitele cadre stack . De fapt, valoarea de ESP s-ar putea modifica în timpul execuţiei, dar este, în stivă, care va pune datele locale. Pentru a avea o referinţă stabilă, valoarea iniţială a ESP este copiat deEBP , astfel că vârful la baza stivei. Toate referirile viitoare la variabilele de pe stivă, de fapt, avea loc prin intermediulEBP .
A treia instrucţiune scade 14h (scrisoarea h indică hexazecimal , care este un număr hexazecimal), sau 20 la registrulESP . În practică, crearea de spaţiu pentru variabilele locale ale marimea 5 DWORD, cât de repede devine clar:
.text:00401026 mov [ebp+var_4], 0Ah
.text:0040102D mov [ebp+var_8], 19h
.text:00401034 mov [ebp+var_10], 1Eh
.text:0040103B mov [ebp+var_C], 0Ch
.text:00401042 mov [ebp+var_14], 23h
.text:0040102D mov [ebp+var_8], 19h
.text:00401034 mov [ebp+var_10], 1Eh
.text:0040103B mov [ebp+var_C], 0Ch
.text:00401042 mov [ebp+var_14], 23h
AID a redenumit gratie variabilele cu numele simbolice (nu foarte semnificative, desigur, dar ele sunt o referinţă mai bună de numere simple). Acest cod initializeaza variabilele locale var_4 , var_8 , var_C , var_10 , var_14 cu valorile 10 (0Ah), 25 (19h), 12 (0Ch), 30 (1Eh), 35 (23h).
. textul : 00401049 MOV EAX , [ EBP + var_4 ]
. Text : 0040104C sub EAX , [ EBP + var_10 ]
. Text : 0040104F mov [ EBP + var_4 ] , EAX
. Text : 0040104C sub EAX , [ EBP + var_10 ]
. Text : 0040104F mov [ EBP + var_4 ] , EAX
Acest cod pune în EAX valoarea de var_4 , scade apoi valoarea de var_10 , stochează în cele din urmă rezultatul înapoi în var_4 .
. textul : 00401052 mov ecx , [ EBP + var_4 ]
. Text : 00401055 SHL ecx , 1
. textul : 00401057 mov [ EBP + var_10 ] , ecx
. Text : 00401055 SHL ecx , 1
. textul : 00401057 mov [ EBP + var_10 ] , ecx
Aici, din nou, valoarea de var_4 o copiere în ecx , shifta la stânga cu un bit (şi apoi prin înmulţirea 2), şi în variabilavar_10 .
. textul : 0040105A MOV EAX , [ EBP + var_C ]
. Text : 0040105D CDQ
. textul : 0040105E sub EAX , EDX
. textul : 00401060 SAR EAX , 1
. Text : 0040105D CDQ
. textul : 0040105E sub EAX , EDX
. textul : 00401060 SAR EAX , 1
Aici el a EAX valoare var_C (care, amintesc, conţine 12). Educaţia CDQ extinde semn de EAX în EDX , dar EAX este pozitiv, astfel încât efectul este doar de a şterge EDX . Educaţie sub scade EDX la EAX , iar din EDX este zero, este declaraţia inutil. Este comun pentru a găsi codul inutile, compilatoare nu poate deduce întotdeauna ceea ce un om, poate, este evident (de fapt, aici, compilatorul nu a realizat numai de pozitivitate var_C , şi a produs un cod generic care ar funcţiona, chiar dacă var_C a fost negativ). Ultima linie, pur şi simplu, shifta la dreapta de un pic (între schimbare de logică şi aritmetică schimbare, în acest caz, nu face nici o diferenţă, fiind EAX pozitiv). Cu alte cuvinte, în EAX se pune valoarea de var_C împărţit de 2 (fără odihnă), unul în limbajul C ar var_C / 2 .
. textul : 00401062 MOV EAX , [ EBP + var_C ]
. Text : 00401065 si EAX , 80000001h
. textul : 0040106B JNS scurte loc_401072
. Text : 00401065 si EAX , 80000001h
. textul : 0040106B JNS scurte loc_401072
Aici, din nou, să copiaţi var_C în EDX . "L şi închide toate biţi, cu excepţia bitul de semn şi cel mai puţin semnificativ bit.În acest caz, ştim că EDX este pozitiv, pentru care bitul de semn este 0 (cu excepţia doar bitul cel mai puţin semnificativ este echivalent cu a lua restul de divizia de 2). Prin urmare, semnul de pavilion este setat la 0. Apoi, instrucţiuni JNSmereu sari la 00401072, astfel încât aceste trei declaraţii nu sunt executate:
. textul : 0040106D dec EDX
. de text : 0040106E sau EDX , 0FFFFFFFEh
. textul : 00401071 inc EDX
. de text : 0040106E sau EDX , 0FFFFFFFEh
. textul : 00401071 inc EDX
Doar din curiozitate, să vedem ce înseamnă acestea. Amintiţi-vă că acest cod este executat numai în cazul în care numărul iniţial a fost negativ (ceea ce este imposibil, în acest caz). Instrucţiuni sau set de la 1, toate biţi, cu excepţia ultimului. dec inversează cu siguranţă ultimul bit (Poate alţii, dar niciodată nu minte). Vă prezentăm două cazuri: atunci când EDX este chiar, apoi, după dec va fi impar (de exemplu, are ultimul bit 1), apoi după " acum , toate biţi la 1, ceea ce înseamnă că EDX conţine -1, aşa după creşteri ulterioare, va conţine 0. Dacă, totuşi, EDX este ciudat, cu dec devin egale, după " sau EDX va conţine 0xFFFFFFFE, adică -2, -1, care devine a crescut. În concluzie, în cazul în care var_Ceste pozitiv (sau zero) şi egal, nu poate veni aici şi EDX va conţine 0; dacă var_C este pozitiv şi ciudat, nu ai venit aici, dar EDX va conţine 1, în cazul în care var_C a fost negativ şi a constituit, EDX va fi în continuare 0 şi dacă var_C este negativ şi ciudat, EDX , în cele din urmă, va conţine -1. Ce este aceasta? Simplu! Este operatorul % (modul) din C. La sfârşitul EDX va conţine întotdeauna rezultatul var_C 2% .
. textul : 00401072 loc_401072 : ; COD Xref: _wmain 4 B # j
. textul : 00401072 add EAX , EDX
. textul : 00401074 mov [ EBP + var_C ] , EAX
. textul : 00401072 add EAX , EDX
. textul : 00401074 mov [ EBP + var_C ] , EAX
Însumaţi EDX la EAX , apoi salvaţi toate in var_C . Am ştiut că înainte de EAX conţine var_C / 2 , în timp ce EDX conţinevar_C 2% . Toate cod de la 00401074 la 0040105A, atunci, înseamnă:
var_C = ( var_C / 2 ) + ( var_C % 2 )
Mutarea pe.
.text:00401077 mov eax, [ebp+var_14]
.text:0040107A push eax
.text:0040107B mov ecx, [ebp+var_C]
.text:0040107E push ecx
.text:0040107F mov edx, [ebp+var_10]
.text:00401082 push edx
.text:00401083 mov eax, [ebp+var_8]
.text:00401086 push eax
.text:00401087 mov ecx, [ebp+var_4]
.text:0040108A push ecx
.text:0040108B call sub_401000
.text:00401090 add esp, 14h
.text:0040107A push eax
.text:0040107B mov ecx, [ebp+var_C]
.text:0040107E push ecx
.text:0040107F mov edx, [ebp+var_10]
.text:00401082 push edx
.text:00401083 mov eax, [ebp+var_8]
.text:00401086 push eax
.text:00401087 mov ecx, [ebp+var_4]
.text:0040108A push ecx
.text:0040108B call sub_401000
.text:00401090 add esp, 14h
Acest cod pune stiva, toate cele 5 variabile, apoi apelează funcţia de la 401000.
Convenţia de asteptare a Win32 este convenţie cel mai comun stdcall , care prevede că toţi parametrii sunt transmise pe stivă în ordine inversă (ultima la prima). Prin urmare, var_4 este primul parametru, var_8 al doilea, var_10 al treilea,var_C al patrulea, var_14 . a cincea şi ultima Vom vedea în curând funcţia de cod sub_401000 .
De fapt, convenţia de asteptare stdcall necesită funcţia de apel pentru a curăţa stivă (prin educaţie ret ). În acest caz, însă, stiva este pus în aplicare de către apelantului pentru a adăuga ESP, 14h , care se adaugă 20 la stiva (20 de bytes este tocmai spaţiul ocupat de 5 parametri DWORD).
. textul : 00401093 apăsare EAX
. textul : 00401094 apăsare de compensare Format , "% d"
. de text : 00401099 apel DS : printf . textul : 0040109F add ESP , 8
. textul : 00401094 apăsare de compensare Format , "% d"
. de text : 00401099 apel DS : printf . textul : 0040109F add ESP , 8
Aici vom vedea un apel la funcţia de biblioteca C, printf , care afiseaza ceva pe ecran în funcţie de şirul de format.Primul parametru este, de fapt, şirul de format ("% d", care înseamnă "a imprima un întreg"), următorul (unul în acest caz) sunt valorile care urmează să fie transmise la funcţia. În acest caz, este trecut EAX , ultima modificare a EAX -am văzut este la linia 00401083, dar în timp a existat un apel de funcţie ... şi doar în EAX funcţiilor face valoarea lor de întoarcere, astfel încât ne aşteptăm ca EAX a fost modificat în funcţie.
. textul : 004010A2 XOR EAX , EAX
. textul : 004010A4 MOV ESP , EBP
. textul : 004010A6 pop EBP
. textul : 004010A7 RETN
. textul : 004010A4 MOV ESP , EBP
. textul : 004010A6 pop EBP
. textul : 004010A7 RETN
Acest cod de procedură şterge EAX (valoarea de răspuns a funcţiei de curent), apoi eliberaţi cadrul stivă (practic nu invers a liniilor de la 401020 la 401021).
Vedem singurul lucru pe care am lăsat afară, şi anume funcţia de sub_401000 :
. textul : 00401000 sub_401000 proc aproape ; COD Xref: _wmain 6 B # p.
. de text : 00401000
. textul : 00401000 arg_0 = dword ptr 8
. de text : 00401000 arg_4 = dword ptr 0Ch
. textul : 00401000 arg_8 = dword ptr 10h
. de text : 00401000 arg_C = dword ptr 14h
. textul : 00401000 arg_10
. de text : 00401000
. textul : 00401000 arg_0 = dword ptr 8
. de text : 00401000 arg_4 = dword ptr 0Ch
. textul : 00401000 arg_8 = dword ptr 10h
. de text : 00401000 arg_C = dword ptr 14h
. textul : 00401000 arg_10
În afară de crearea de cod de obicei şi de eliberare a cadrului stivei , vom vedea că este pus în EAX valoarea primului argument, deci sunt adunate toate celelalte 4. Această caracteristică sofisticat, pe scurt, nu face nimic, dar adaugă cele 5 argumente :)
Versiunea x64
Vom vedea acum codul de 64-biţi. Nu voi comenta mai mult de linie cu linie, dar mă voi concentra doar asupra diferenţelor din altă versiune.
. textul : 0000000140001030 sub_140001030 proc apropiat ; Xref COD: # 115 p. sub_140001178
. textul : 0000000140001030 ; DATE Xref: vma: ExceptionDir # sau.
. de text : 0000000140001030
. textul : 0000000140001030 var_38 = dword ptr - 38h
. textul : 0000000140001030 = var_28 DWORD PTR - 28h
. textul : 0000000140001030 var_24 = dword ptr - 24
. textul : 0000000140001030 var_20 = dword ptr - 20h
. textul : 0000000140001030 var_1C = dword ptr - 1CH
. textul : 0000000140001030 var_18 = dword ptr - 18pm
. textul : 0000000140001030 = arg_0 DWORD PTR 8
. textul : 0000000140001030 arg_8 sub_140001030 ENDP
. textul : 0000000140001030 ; DATE Xref: vma: ExceptionDir # sau.
. de text : 0000000140001030
. textul : 0000000140001030 var_38 = dword ptr - 38h
. textul : 0000000140001030 = var_28 DWORD PTR - 28h
. textul : 0000000140001030 var_24 = dword ptr - 24
. textul : 0000000140001030 var_20 = dword ptr - 20h
. textul : 0000000140001030 var_1C = dword ptr - 1CH
. textul : 0000000140001030 var_18 = dword ptr - 18pm
. textul : 0000000140001030 = arg_0 DWORD PTR 8
. textul : 0000000140001030 arg_8 sub_140001030 ENDP
Aceasta, cu toate acestea, este codul de funcţie de suma (de data aceasta numit sub_140001030 ):
. textul : 0000000140001000 sub_140001000 proc apropiat ; COD Xref: sub_140001030 8 A # p.
. textul : 0000000140001000
. textul : 0000000140001000 arg_0 = dword ptr 8
. textul : 0000000140001000 arg_8 = dword ptr 10h
. textul : 0000000140001000 arg_10 = dword ptr 18h
. de text : 0000000140001000 arg_18 = dword ptr 20h
. textul : 0000000140001000 arg_20
. textul : 0000000140001000
. textul : 0000000140001000 arg_0 = dword ptr 8
. textul : 0000000140001000 arg_8 = dword ptr 10h
. textul : 0000000140001000 arg_10 = dword ptr 18h
. de text : 0000000140001000 arg_18 = dword ptr 20h
. textul : 0000000140001000 arg_20
În corpul principal al funcţiei nu există diferenţe majore (în afară de cele evidente, datorită arhitecturii). Numai demn de reţinut este în aceste linii:
.text:0000000140001089 mov eax, [rsp+58h+var_20]
.text:000000014000108D cdq
.text:000000014000108E and eax, 1
.text:0000000140001091 xor eax, edx
.text:0000000140001093 sub eax, edx
.text:0000000140001095 mov ecx, eax
.text:000000014000108D cdq
.text:000000014000108E and eax, 1
.text:0000000140001091 xor eax, edx
.text:0000000140001093 sub eax, edx
.text:0000000140001095 mov ecx, eax
Ei sunt rânduri de la 00401062 la 00401074 de listare vechi. De data aceasta a folosit un compilator mai inteligent mod de a calcula var_20 2% . Noi considerăm 2 cazuri aici: dacă var_20 este pozitiv, atunci CDQ curata EDX , a Xor şi educaţie sub- nu schimba EAX , atunci situaţia este numai util şi EAX, 1 : este nevoie de cel putin semnificativ, iar apoi calculeaza var_20% 2 . Dacă, în schimb, var_20 este negativ, apoi, după CDQ valoarea EDX va fi -1; şi extrage cel putin semnificativ. Dacă este 0 (numar negativ egal), apoi XOR (inversoare toate biţi), rezultatele la -1, precum şi de instruiresub , scăderea a două valori egale, rezultate în 0. În cazul în care, în cele din urmă, cel puţin semnificativ bit este 1 (număr impar negativ), apoi XOR pune în EAX . valoarea de -2 (de exemplu, în binar, 11111111111111111111111111111110 ... vă amintiţi complement de 2, nu?) sub scade -1 , apoi trepte de 1, iar rezultatul final va fi -1. Deci, încă o dată codul este echivalent cu calculul de var_20 2% . Compilator, cu toate acestea, a fost un pic "mai inteligent decât înainte şi a evitat salt condiţionat (o regulă bună de optimizare este aceasta: ai prefera să cod fără salturi condiţionate).
Dacă aveţi dificultăţi în urma acestor argumente, este numai pentru că nu sunteţi familiarizaţi cu instrucţiunile de asamblare, şi de inginerie inversă vă obligă să examineze codul pentru a smulge în sensul de mult ascuns (Ne pare rau, nu am putut rezista ... în afară de cuvinte stupide mari, Conceptul este adevărat).
Mai atent (dacă sunteţi încă lectură), va fi observat altă diferenţă semnificativă în comparaţie cu versiunea x86: schimbarea convenţiile de asteptare si, prin urmare, codul de creaţie şi eliberaţi cadrul stivei . În Win64, având în vedere disponibilitatea crescută a înregistrărilor, vom adopta apel convenţie fastcall . Se estimează că primele 4 parametrii sunt transmise prin intermediul registrelor în loc de prin stiva. În special, primele patru parametri ar trebui să fie, în ordine, în RCX , RDX , R8 , R9 . Dacă există şi alţii, merg pe stiva. De fapt, spaţiul de pe stiva pentru parametrii trecut prin registre sunt încă confidenţiale, cu excepţia faptului că acesta rămâne disponibil pentru functia numita, care poate salva parametrii (dar daca el nu are nevoie, nu o poate face). Funcţia sub_140001000, de exemplu, salvează toate cele 4 parametri în spaţiul prevăzut pe liniile 140 001 000 - 1400 0100E (chiar dacă ar putea ajuta, deoarece trebuie să adăugaţi-le pe toate în doar EAX ..., dar acest cod nu este optimizat). Deoarece pregătirea spaţiul de pe stiva inainte de fiecare apel ar fi prea costisitoare în termeni de performanţă (şi Convenţia se numeste fastcall şislowcall ), este alocată o dată şi pentru totdeauna, în timp ce crearea cadrului stivă în primele rânduri, nu, spaţiu suficient pentru apelurile ulterioare, astfel încât pentru fiecare apel, reutilizează acelaşi spaţiu pe stiva. Din acest motiv, parametrii nu sunt introduse pe stiva cu instrucţiuni apăsare (care limitează spaţiul nou), dar sunt plasate în spaţiul alocat deja printr-o instrucţiune simplă MOV (un exemplu este 1400010A4 linie, care este pus în stivă parametru al cincilea apel următoare).
O consecinţă a lipsei de împingere şi pop registrul este că RSP are aceeaşi valoare, în funcţie, acesta devine inutil la menţinerea unui alt jurnal pentru a indica baza de stivă (aşa cum se face cu Convenţia de la locul stdcall , în cazul în care registrul EBP este condamnat să fie un pointer constant pe tot parcursul vieţii a funcţiei).
Referinte
Pentru arhitectura x64, puteţi face referire la manualele de la AMD şi Intel: Documentaţie pe AMD64
documentaţia Intel
pentru informaţii detaliate cu privire la convenţiile de asteptare pe Win64, puteţi citi acest dall'MSDN documentaţie .
Pentru informaţii cu privire la programarea de asamblare pe Win32, se referă la secţiunile Adunării şi Programare şiIczelion site-ului , un pic "datat, dar încă foarte bine. Cea mai recentă versiune poate fi descărcat de pe Masm32http://www.masm32.com/
Pentru o introducere în programare în asamblare a x64/Win64, acest tutorial este pentru tine .
documentaţia Intel
pentru informaţii detaliate cu privire la convenţiile de asteptare pe Win64, puteţi citi acest dall'MSDN documentaţie .
Pentru informaţii cu privire la programarea de asamblare pe Win32, se referă la secţiunile Adunării şi Programare şiIczelion site-ului , un pic "datat, dar încă foarte bine. Cea mai recentă versiune poate fi descărcat de pe Masm32http://www.masm32.com/
Pentru o introducere în programare în asamblare a x64/Win64, acest tutorial este pentru tine .
Note finale
Aici se încheie această lecţie pe ansamblul. Sper că nu am făcut steril si plictisitor un subiect care, prin natura sa, este interesanta si foarte creativ.
Vă mulţumim Pnluck primul comandat de mine această lecţie: multumesc :)
Quequero salut faptul că, după toţi aceşti ani în continuare pentru a rula cabină (dar eu vă asigur, nu este obtinerea vechi, nu-i aşa! ... E doar că el a inceput mai devreme).
salut pe toţi cei care continuă să facă parte din această comunitate, doar de acest fel din Italia, dar, de asemenea, că cei care acum latitano absorbit din viaţa reală.
salut pe toţi cei care continuă să facă parte din această comunitate, doar de acest fel din Italia, dar, de asemenea, că cei care acum latitano absorbit din viaţa reală.
Multumesc tuturor.
Disclaimer
Documentele publicate sunt considerate publice şi distribuit în mod liber, atât timp cât acestea citează sursa. Toate documentele de pe aceste pagini au fost scrise exclusiv în scopuri de cercetare, nici una dintre aceste analize a fost făcut pentru scopuri comerciale, sau pe orice tip de compensare. Documentele publicate arată o analiză pur teoretică a structurii unui program, în orice caz, software-ul de fapt, a fost dezasamblate sau modificate, toată corespondenţa şi documentele publicate în această instrucţiuni de software de a fi analizate, să fie considerate ca fiind pur aleatorie. Toate documentele sunt trimise anonim şi a publicat în mod automat, drepturile acestor lucrări aparţin exclusiv la semnatar al documentului (dacă este cazul), în orice caz, operatorul acestui site sau serverul care găzduieşte poate fi considerat responsabil pentru conţinutul aici, în plus, operatorul nu este în măsură să identifice identitatea expeditorului a documentelor. Toate documentele şi fişierele de pe acest site nu are nici un fel de garanţie, de aceea este recomandat tuturor lectura sau de performanţă, personalul nu isi asuma responsabilitatea pentru abuzul de astfel de documente şi / sau a fişierelor , este corect să adaug că orice referire la fapte sau de prejudiciu este pur întâmplătoare. Toţi cei care ar putea fi considerat punct de vedere moral ofensat de conţinutul acestor pagini, în care sunt necesare pentru a ieşi imediat din acest site.
Noi, de asemenea, reamintim că Inginerie inversă este un instrument tehnologic de mare putere şi importanţă, fără ea nu ar fi posibil să se creeze anti-virus, malware şi de a găsi funcţiile nu declarate în cadrul unui program pentru uz public. Nu ar fi posibil să se descopere, în absenţa unui control sigur integritatea sistemului în cazul în care "acest" program este într-adevăr ceea ce utilizatorul a ales pentru a instala şi rula, nici nu ar fi posibil să se continue dezvoltarea acestor programe (sau "utilizarea acestor dispozitive), care sunt considerate depăşite şi nu este susţinută de către surse oficiale.
Noi, de asemenea, reamintim că Inginerie inversă este un instrument tehnologic de mare putere şi importanţă, fără ea nu ar fi posibil să se creeze anti-virus, malware şi de a găsi funcţiile nu declarate în cadrul unui program pentru uz public. Nu ar fi posibil să se descopere, în absenţa unui control sigur integritatea sistemului în cazul în care "acest" program este într-adevăr ceea ce utilizatorul a ales pentru a instala şi rula, nici nu ar fi posibil să se continue dezvoltarea acestor programe (sau "utilizarea acestor dispozitive), care sunt considerate depăşite şi nu este susţinută de către surse oficiale.
Niciun comentariu:
Trimiteți un comentariu