Sociálne siete

SecIT.sk na Facebooku SecIT.sk na Google+ SecIT.sk na Twitteri

Podporte nás


V prípade, že Vám obsah nášho portálu niekedy nejakým spôsobom pomohol, či bol pre Vás prínosom prosím podporte jeho chod ľubovoľnou čiastkou. Ďakujeme!

Štítky

Vyhľadávanie

You are here

Domov
Upozornenie: Obsah je licenčne chránený a bez písomných súhlasov autora článku a vlastníka webovej stránky nesmie byť v žiadnej forme ďalej kopírovaný a šírený v pôvodnom, či v akokoľvek upravenom stave.

Instrukce REP RET

V několika minulých dnech jsem se intenzivně zabýval zjišťováním, jakže to funguje ochrana jednoho nejmenovaného bezpečnostního produktu. Tato činnost často obnáší použití disassembleru a debuggeru a tento případ nebyl výjimkou. Během práce jsem si všiml přítomnosti zvláštní instrukce REP RET. Kdybych ji neviděl na vlastní oči, pokládal bych ji za neplatnou. Ale protože ji zřejmě procesor "sežere", udělal jsem malé pátrání, abych se dozvěděl, co tato instrukce provádí.

Rozklad

Záhadná instrukce se skládá ze dvou částí. Pokud se na ně podíváme odděleně, nic nás nepřekvapí. Jak REP, tak RET dávají smysl. Pravda, prefix REP musí být následován nějakou instrukcí, ale jak uvidíme v následujícím odstavci, RET by mezi takové patřit rozhodně neměla.

Příznak REP říká procesoru, aby následující instrukci prováděl pořád dokola a při každé obrátce snížil hodnotu v registru ECX (CX na 16-bitové platformě, RCX na 64-bitové) o jedničku. Cyklus skončí, jakmile hodnota registru dosáhne nuly. Proto se tento příznak využívá například při kopírování bloku paměti, práci s řetězci či jiných cyklických operacích.

Instrukci RET se také jinak říká návrat z podprogramu. Procesor vybere z vrcholu zásobníku slovo (v bajtech odpovídá velikosti adresy) a zapíše jej do registru EIP (IP na 16-bitové platformě, RIP na 64-bitové). Protože tento registr vždy obsahuje adresu instrukce, která se má začít vykonávat, dojde ke skoku na adresu, jež se nacházela na vrcholu zásobníku. Existuje též varianta, která po načtení hodnoty do xIP odstraní z vrcholu zásobníku dalších n bajtů. Nazývá se RET n.

Sémantika

Už tedy víme, k čemu slouží příznak REP a instrukce RET, použijeme-li je zvlášť. Ale jakou má sémantiku instrukce, která vznikne jejich spojením (REP RET)? Má vybírat slova ze zásobníku, dokud nebude xCX obsahovat nulu a na adresu v posledním odebraném skočit? Nebo snížit registr o jedničku, vybrat slovo ze zásobníku, použít jej jako adresu pro skok a dál v opakování nepokračovat, protože skok změnil adresu aktuální instrukce? Ani jedna odpověď není správná.

Dnešní procesory jsou velmi složité a pro zvýšení rychlosti využívají vyrovnávacích pamětí (L1, L2, L3 cache a další) či techniky zpracovávání více instrukcí najednou zvané pipelining či superskalární pipelining. Procesor nenačítá instrukce přesně tak, jak mu velí změny hodnoty v registru xIP, ale snaží se předem odhadnout, kudy program poběží a tyto instrukce si načíst (a případně předzpracovat) dříve, než ve skutečnosti potřebuje (prefetching). Moderní procesory též zpracovávají více instrukcí najednou - zatímco se jedna dekóduje, druhá může zapisovat do registru a třetí číst z paměti.

Aby mohl úspěšně načítat instrukce "dopředu", procesor musí nějakým způsobem uhodnout, kudy se tok programu bude ubírat. Problém zde mohou způsobovat například podmíněné skoky - v době jejich "přednačtení" nemusí být známo, jestli je podmínka skoku splněna, nebo ne. V takovém případě se některé procesory pokusí další tok programu odhadnout a načíst a předzpracovat instrukce z příslušné adresy. Pokud se odhad ukáže jako nesprávný, předzpracované instrukce jsou zahozeny a pocesor se vrátí do stavu před provedením podmíněného skoku (či jinou instrukcí, která vyžaduje hádání).

Jak se podle mého zdroje píše v manuálech AMD, implementace dopředného načítání instrukcí se dostane do potíží, nachází-li se těsně po instrukci podmíněného skoku instrukce návratu z procedury. Procesor přestane přednačítávat další instrukce, dokud se nevykoná RET, nebo se tok programu nepřesměruje do jiných míst. Má-li však taková instrukce před sebou prefix REP, k "záseku" nedochází, a tedy výkon procesoru není ztracen.

Na závěr ještě uvedu zdrojovou URL a příslušnou citaci:


Here's the principle. The processor tries to fetch the next few
instructions to be executed, so that it can start the process of decoding
and executing them. It even does this with jump and return instructions,
guessing where the program will head next.


What AMD says here is that, if a ret instruction immediately follows a
conditional jump instruction, their predictor cannot figure out where the
ret instruction is going. The pre-fetching has to stop until the ret
actually executes, and only then will it be able to start looking ahead
again.


The "rep ret" trick apparently works around the problem, and lets the
predictor do its job. The "rep" has no effect on the instruction.

http://coding.derkeiler.com/Archive/Assembler/comp.lang.asm.x86/2006-03/msg00041.html

Komentáre

zdravim, vyborne vysvetleni a moc pekny clanek, jenom bych chtel podotknout, ze (tedy, alespon podle toho co je mi znamo) navratova adresa nelezi primo na vrcholu zasobnikoveho ramce, nicmene by mela byt mezi SFP(saved framepointer,nebo tak nejak) a ukazately na parametry funkce... nicmene, jestli je to vazane treba konkretne jenom na AMD a to to dela trochu odlisne nez x86, beru vse zpet.... kazdopadne, diky za povedeny clanek...

Zdravím,

Instrukce RET funguje tak, že vybere data na vrcholu zásobníku (za standardní situace na x86 to je 32 bitů), interpretuje je jako adresu a skočí na ni. Takže návratová adresa v době, kdy se vykonává instrukce RET je přímo na vrcholu zásobníku.

Framepointer (na x86 registr EBP) se ukládá na začátku podprogramu. Slouží k tomu, aby se snadno dalo přistupovat k hodnotám parametrů a lokálních proměnných (na rozdíl od registru ESP se totiž při vykonávání normálního podprogramu "nehýbe"). Situace je přitom taková, že:

  • [EBP] obsahuje předchozí hodnotu framepointeru
  • [EBP+4] obsahuje návratovou adresu (bude tam skákat instrukce RET)
  • [EBP+8] obsahuje hodnotu prvního parametru, použije-li se volací konvence STDCALL
  • [EBP-x] obsahuje hodnoty lokálních proměnných

Asi ale také záleží, jak moc velké optimalizace překladač při generování strojového kódu provádí. Může třeba dojít k názoru, že lokálních proměnných je tak málo, že se nevyplatí je ukládat na zásobník...

Takže máte pravdu, že návratová adresa neleží na vrcholu zásobníkového rámce. Leží na vrcholu zásobníku v okamžiku, kdy se vykonává instrukce RET. Tehdy už zásobníkový rámec pro daný podprogram (který je právě ukončován) neexistuje.

Aaano ano, omlouvam se, moje chybka:) Samozrejme, pri vykonavani RET ukazuje ESP primo na navratovou adresu, jelikoz EBP uz je obnoveno a lokalni promenne jsou odstraneny... Myslel jsem to, presne jak rikate, neni na vrcholu ramce ale pri ukoncovani funkce a odstranovani jejiho ramce se na vrchol samozrejme dostane :)
Mejte se


Podporte nás


Páčil sa Vám tento článok? Ak áno, prosím podporte nás ľubovoľnou čiastkou. Ďakujeme!


ITC manažer Security-portal.cz Soom.cz
Hoax.cz Antivirove centrum Crypto-world.info