╤Єєфюяхфш 

├ыртэр  ёЄЁрэшЎр ╤ыєўрщэр  ёЄЁрэшЎр

╩└╥┼├╬╨╚╚:

└тЄюьюсшыш└ёЄЁюэюьш ┴шюыюуш ├хюуЁрЇш ─юь ш ёрф─Ёєушх  ч√ъш─Ёєуюх╚эЇюЁьрЄшър╚ёЄюЁш ╩єы№ЄєЁр╦шЄхЁрЄєЁр╦юушър╠рЄхьрЄшър╠хфшЎшэр╠хЄрыыєЁуш ╠хїрэшър╬сЁрчютрэшх╬їЁрэр ЄЁєфр╧хфруюушър╧юышЄшър╧Ёртю╧ёшїюыюуш ╨хышуш ╨шЄюЁшър╤юЎшюыюуш ╤яюЁЄ╤ЄЁюшЄхы№ёЄтю╥хїэюыюуш ╥єЁшчь╘шчшър╘шыюёюЇш ╘шэрэё√╒шьш ╫хЁўхэшх▌ъюыюуш ▌ъюэюьшър▌ыхъЄЁюэшър






Fastcall 5 ёЄЁрэшЎр




; Знчит, MyFunc действительно состоятельно выделял пять вызово malloc

 

call _free

add esp, 4

; Освобожде пять, выделенную MyFunc для возрщения знчения

 

mov esp, ebp

pop ebp

; Зкрыве кдр стек

 

retn

; Тки обрзо, протип MyFunc выглядит тк:

; char* MyFunc(int a)

 

main endp

 

 

MyFunc proc near ; CODE XREF: main+9p

 

var_4 = dword ptr -4

arg_0 = dword ptr 8

 

push ebp

mov ebp, esp

; Открыве кдр стек

 

push ecx

; Резервируе пять под локльные переенные

 

push 64h ; size_t

call _malloc

add esp, 4

; Выделяе 0x64 бйт пяти из кучи либо для собственных нужд функции, либо

; для возрщения результт. Поскольку из нлиз код вызывющей функции н

; уже известно, что MyFunc возврщет укзтель, очень вероятно, что вызов

; malloc выделяет пять кк рз для этой цели.

; Впроче, вызовов malloc ожет быть и несколько, укзтель возврщется

; только н один из них

 

mov [ebp+var_4], eax

; Зпоине укзтель в локльной переенной var_4

 

push 10h ; int

; Переде функции __ltoa ргуент 0x10 (крйний спрв) – требуея систе

; исчисления для перевод числ

 

mov eax, [ebp+var_4]

; Згруже в EAX содержиое укзтеля н выделенную из кучи пять

 

push eax ; char *

; Переде функции ltoa укзтель н буфер для возрщения результт

 

mov ecx, [ebp+arg_0]

; Згруже в EAX знчение ргуент arg_0

 

push ecx ; __int32

; Переде функции ltoa ргуент arg_0 – знчение тип int

 

call __ltoa

add esp, 0Ch

; Функция ltoa переводит число в строку и зписывет ее в буфер по переднноу

; укзтелю

 

mov eax, [ebp+var_4]

; Возврще укзтель н регион пяти, выделенный сой MyFunc из кучи, и

; содержщий результт рботы ltoa

 

mov esp, ebp

pop ebp

; Зкрыве кдр стек

 

retn

MyFunc endp

Листинг 104

 

::Возврт знчений через глобльные переенные. "Мыльную оперу" перепевов с возрщение укзтелей продолжет серия "Возрщение знчений через глобльные переенные (и/или укзтеля н глобльные переенные)". Вообще-то глобльные переенные – плохой тон и ткой стиль прогрировния хрктерен в основно для прогристов с ышление, необртио исклеченны идеологий Бцик с его недорзвиты ехнизо вызов подпрогр.

Подробнее об идентификции глобльных переенных рсскзывется в одноиенно рзделе днной глвы, здесь же ы сосредоточи нши усилия иенно н изучении ехнизов возврщения знчений через глобльные переенные.

Фктически, все глобльные переенные ожно рсстривть кк неявные ргуенты кждой вызывеой функции и в то же врея – кк возврщеые знчения. Любя функция ожет произвольны обрзо читть и одифицировть их, приче, ни "передч", ни "возрщение" глобльных переенных не "видны" нлизо код вызывющей функции, - для этого необходио тщтельно исследовть вызывеую – нипулирует ли он с глобльныи переенныи и если д, то с ккии. Можно зйти и с обртной стороны, - просотро сегент днных нйти все глобльные переенные, определить их сещение и, пройдясь контекстны поиско по всеу фйлу, выявить функции, которые н них ссылются (подробнее с. "Идентификция глобльных переенных :: перекрестные ссылки").



Поио глобльных, еще существуют и сттические переенные. Они тк же рсполгются в сегенте днных, но непосредственно доступны только объявившей их функции. Точнее, огрничение нложено не н си переенных, н их иен. Чтобы предоствить други функция доступ к собственны сттически переенны достточно передть укзтель. К счстью, этот трюк не создет хкер никких пробле (хоть некоторые злопыхтели и объявляют его "прорехой в зщите"), - отсутствие непосредственного доступ к "чужи" сттически переенны и необходиость взиодействовть с функцией-влделицей через предскзуеый интерфейс (возрщенный укзтель), позволяет рзбить прогру н отдельные незвисиые одули, кждый из которых ожет быть пронлизировн отдельно. Чтобы не быть голословны, продеонстрируе это н следующе приере:

 

#include <stdio.h>

 

char* MyFunc(int a)

{

static char x[7][16]={"Понедельник", "Вторник", "Сред", "Четверг", "Пятниц",

"Суббот", "Воскресенье"};

return &x[a-1][0];

}

 

main()



{

printf("%s\n",MyFunc(6));

}

Листинг 105 Приер, деонстрирующий возврт знчения через глобльные сттические переенные

 

Результт копиляции копиляторо Microsoft Visual C++ 6.0 c нстройки по уолчнию выглядит тк:

 

MyFunc proc near ; CODE XREF: main+5p

 

arg_0 = dword ptr 8

 

push ebp

mov ebp, esp

; Открыве кдр стек

 

mov eax, [ebp+arg_0]

; Згруже в EAX знчение ргуент arg_0

 

sub eax, 1

; Уеньше EAX н единицу. Это косвенно свидетельствует о то, что arg_0 –

; не укзтель, хотя тетические оперции нд укзтеляи в Си рзрешены

; и ктивно используются

 

shl eax, 4

; Уноже (arg_0 –1) н 16. Битовый сдвиг впрво н четыре рвносилен 24 == 16

 

add eax, offset aPonedelNik ; "Понедельник"

; Склдыве полученное знчение с бзовы укзтеле н тблицу строк,

; рсположенных в сегенте днных. А в сегенте днных нходятся либо

; сттические, либо глобльные переенные.

; Поскольку, знчение ргуент arg_0 уножеся н некоторую величину

; (в днно случе н 16), ожно предположить, что ы иее дело с

; двухерны ссиво. В днно случе – ссиво строк фиксировнной длины.

; Тки обрзо, в EAX содержится укзтель н строку с индексо arg_0 – 1

; Или, другии слови, – с индексо arg_0, считя с одного.

 

pop ebp

; Зкрыве кдр стек, возврщя в регистре EAX укзтель н соответствующий

; элеент ссив.

; Кк ы види, нет никкой принципильной рзницы ежду возврщение укзтеля

; н регион пяти, выделенный из кучи, с возрщение укзтеля н сттические

; переенные, рсположенные в сегенте днных.

 

retn

MyFunc endp

 

 

main proc near ; CODE XREF: start+AFp

push ebp

mov ebp, esp

; Открыве кдр стек

 

push 6

; Переде функции MyFunc знчение тип int

; (шестой день – суббот)

 

call MyFunc

add esp, 4

; Вызыве MyFunc

 

push eax

; Переде возрщенное MyFunc знчение функции printf

; Судя по строке спецификторов, это – укзтель н строку

 

push offset aS ; "%s\n"

call _printf

add esp, 8

 

pop ebp

; Зкрыве кдр стек

 

retn

main endp

 

aPonedelNik db 'Понедельник',0,0,0,0,0 ; DATA XREF: MyFunc+Co

; Нличие перекрестной ссылки только н одну функцию, подскзывет, что тип

; этой переенной – static

 

aVtornik db 'Вторник',0,0,0,0,0,0,0,0,0

aSreda db 'Сред',0,0,0,0,0,0,0,0,0,0,0

aCetverg db 'Четверг',0,0,0,0,0,0,0,0,0

aPqtnica db 'Пятниц',0,0,0,0,0,0,0,0,0

aSubbota db 'Суббот',0,0,0,0,0,0,0,0,0

aVoskresenE db 'Воскресенье',0,0,0,0,0

aS db '%s',0Ah,0 ; DATA XREF: main+Eo

Листинг 106

 

А теперь срвни предыдущий приер с нстоящии глобльныи переенныи:

 

#include <stdio.h>

 

int a;

int b;

int c;

 

MyFunc()

{

c=a+b;

}

 

main()

{

a=0x666;

b=0x777;

MyFunc();

printf("%x\n",c);

}

Листинг 107 Приер, деонстрирующий возврт знчения через глобльные переенные

 

main proc near ; CODE XREF: start+AFp

push ebp

mov ebp, esp

; Открыве кдр стек

 

call MyFunc

; Вызыве MyFunc. Обртите вниние – функции явно ничего не передется

; и ничего не возврщется. Потоу, ее прототип выглядит

; (по предврительны зключение) тк:

; void MyFunc()

 

call Sum

; Вызыве функцию Sum, явно не приниющую и не возврщющую никких знчений

; Ее предврительный прототип выглядит тк: void Sum()

 

mov eax, c

; Згруже в EAX знчение глобльной переенной 'c'

; Сотри в сегент днных, - тк-тк, вот он переення 'c', рвня нулю

; Однко этоу знчению нельзя доверять – быть ожет, ее уже успели изенить

; рнее вызвнные функции.

; Предположение о одификции подкрепляется прой перекрестных ссылок,

; одн из которых укзывет н функцию Sum. Суффикс 'w', звершющий

; перекрестную ссылку, говорит о то, что Sum зписывет в переенную 'c'

; ккое-то знчение. Ккое? Это ожно узнть из нлиз код сой Sum.

 

push eax

; Переде знчение, возрщенное функцией Sum, через глобльную переенную 'c'

; функции printf.

; Судя по строке спецификторов, ргуент иеет тип int

 

push offset asc_406030 ; "%x\n"

call _printf

add esp, 8

; Выводи возврщенный Sum результт н теринл

 

pop ebp

; Зкрыве кдр стек

 

retn

main endp

 

 

Sum proc near ; CODE XREF: main+8p

; Функция Sum не приниет через стек никких ргуентов!

 

push ebp

mov ebp, esp

; Открыве кдр стек

 

mov eax, a

; Згруже в EAX знчение глобльной переенной 'a'

; Нходи 'a' в сегенте днных, - г, есть перекрестня ссылк н MyFunc,

; которя что-то зписывет в переенную 'a'.

; Поскольку, вызов MyFunc предшествовл вызову Sum, ожно скзть, что MyFunc

; возвртил в 'a' некоторое знчение

 

add eax, b

; Склдыве EAX (хрнящий знчение глобльной переенной 'a') с содержиы

; глобльной переенной 'b'

; (все, скзнное выше относительно 'a', спрведливо и для 'b')

 

mov c, eax

; Поеще результт сложения a+b в переенную 'c'

; Кк ы уже зне (из нлиз функции main), функция Sum в переенной 'c'

; возврщет результт своих вычислений. Теперь ы узнли – кких иенно.

 

pop ebp

; Зкрыве кдр стек

 

retn

Sum endp

 

MyFunc proc near ; CODE XREF: main+3p

push ebp

mov ebp, esp

; Открыве кдр стек

 

mov a, 666h

; Присвиве глобльной переенной 'a' знчение 0x666

 

mov b, 777h

; Присвиве глобльной переенной 'b' знчение 0x777

; Кк ы выяснили из нлиз двух предыдущих функций – функция MyFunc

; возврщет в переенных и b результт своих вычислений

; Теперь ы определили ккой иенно, весте с те согли рзобрться

; кк три функции взиодействуют друг с друго.

; main() вызывет MyFunc(), т иницилизирует глобльные переенные 'a' и 'b',

; зте main() вызывет Sum(), поещющя суу 'a' и 'b' в глобльную 'c',

; нконец, main() берет эту 'c' и передет ее через стек printf

; для вывод н экрн.

; Уф! Кк все зпутно, ведь это простейший приер из трех функций!

; Что же говорить о рельной прогре, в которой этих функций тысячи, приче

; порядок вызов и поведение кждой из них длеко не тк очевидны!

 

pop ebp

 

 

retn

MyFunc endp

 

a dd 0 ; DATA XREF: MyFunc+3w Sum+3r

b dd 0 ; DATA XREF: MyFunc+Dw Sum+8r

c dd 0 ; DATA XREF: Sum+Ew main+Dr

; Судя по перекрестны ссылк – все три переенные глобльные, т.к. к

; кждой из них иеет непосредственный доступ более одной функции.

Листинг 108

 

::возврт знчений через флги процессор. Для большинств ссеблерных функций хрктерно использовние регистр флгов процессор для возврщения результт успешности выполнения функции. По общепринятоу соглшению устновленный флг перенос (CF) свидетельствует об ошибке, второе есто по популярности зниет флг нуля (ZF), остльные флги прктически вообще не используются.

Устновк флг перенос осуществляется кондой STC или любой тетической оперцией, приводящей к обрзовнию перенос (нприер, CMP a, b где a < b), сброс – кондой CLC или соответствующей тетической оперцией.

Проверк флг перенос обычно осуществляется условныи переходи JC xxx и JNC xxx, соответственно исполняющихся при нличии и отсутствии перенос. Условные переходы JB xxx и JNB xxx – их синтксические синониы, дющие при ссеблировнии идентичный код.

 

#include <stdio.h>

 

// Функция сообщения об ошибке деления

Err(){ printf("-ERR: DIV by Zero\n");}

 

// Вывод результт деления н экрн

Ok(int a){printf("%x\n",a);}

 

 

// Ассеблерня функция деления.

// Делит EAX н EBX, возврщя чстное в EAX, остток – в EDX

// При попытке деления н ноль устнвливет флг перенос

__declspec(naked) MyFunc()

{

__asm{

xor edx,edx ; Обнуляе EDX, т.е. конд div ожидет делиого в EDX:EAX

test ebx,ebx ; Проверк делителя н рвенство нулю

jz _err ; Если делитель рвен нулю, перейти к ветке _err

 

div ebx ; Дели EDX:EAX н EBX (EBX зведоо не рвен нулю)

 

ret ; Выход в с возврто чстного в EAX и осттк в EDX

 

_err: ; // Эт ветк получет упрвление при попытке деления н ноль

stc ; устнвливе флг перенос, сигнлизируя об ошибке и...

ret ; ...выходи

}

}

 

// Обертк для MyFunc

// Приние дв ргуент через стек – делиое и делитель

// и выводи результт деления (или сообщение об ошибке) н экрн

__declspec(naked) MyFunc_2(int a, int b)

{

__asm{

mov eax,[esp+4] ; Згруже в EAX содержиое ргуент 'a'

mov ebx,[esp+8] ; Згруже в EDX содержиое ргуент 'b'

 

call MyFunc ; Пытеся делить a/b

jnc _ok ; Если флг перенос сброшен выводи результт, инче…

 

call Err ; …сообщение об ошибке

 

ret ; Возврщеся

_ok:

push eax ; Переде результт деления и…

call Ok ; …выводи его н экрн

add esp,4 ; Вычище з собой стек

 

ret ; Возврщеся

}

}

 

main(){MyFunc_2(4,0);}

Листинг 109

 

 


mylektsii.ru - ╠юш ╦хъЎшш - 2015-2018 уюф. (0.041 ёхъ.)┬ёх ьрЄхЁшры√ яЁхфёЄртыхээ√х эр ёрщЄх шёъы■ўшЄхы№эю ё Ўхы№■ ючэръюьыхэш  ўшЄрЄхы ьш ш эх яЁхёыхфє■Є ъюььхЁўхёъшї Ўхыхщ шыш эрЁє°хэшх ртЄюЁёъшї яЁрт ╧юцрыютрЄ№ё  эр ьрЄхЁшры