User login

SecIT na Facebooku



Recent comments

Reply to comment

4.2
Average: 4.2 (5 votes)
Your rating: None

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

Reply

The content of this field is kept private and will not be shown publicly.
  • Lines and paragraphs break automatically.

More information about formatting options

CAPTCHA
This question is for testing whether you are a human visitor and to prevent automated spam submissions.
Image CAPTCHA
Enter the characters shown in the image.

Links

SecIT.sk fórum

IRC kanál: #secit
IRC server: shell.websupport.sk
Port: SSL: 9998, bez SSL: 6670
SecIT.sk na Facebooku

Štatistika fóra

Home Total posts 2957
Total topics 405
Members Total members 1470
Our newest member kenboo

© SecIT.sk - info(at)secit.sk - Všetky práva vyhradené. Žiaden obsah umiestnený na našom portáli a fóre sa nesmie kopírovať!