Издательский дом ООО "Гейм Лэнд"ЖУРНАЛ ХАКЕР #91, ИЮЛЬ 2006 г.

Экстремальная оптимизация

Крис Касперски

Хакер, номер #091, стр. 091-120-2


push offset file_name ; имя исполняемого файла с аргументами

call system ; прямой вызов функции (почему так — см. врезку)

add esp, 4 ; выталкиваем аргументы из стека (можно сделать позже)

То же самое относится и к функциям файлового ввода/вывода, преобразованиям данных и т. д., и т. п. Никто же не будет спорить, что вызов fopen намного короче, чем CreateFile, а скорость исполнения у них практически та же самая, тем более что библиотека MSVCRT.DLL всегда присутствует в памяти, поскольку используются системные процессы. Windows просто спроецирует ее на наше адресное пространство — вот и все! Никакого увеличения потребляемой памяти не произойдет!

Наибольший выигрыш достигается на задачах, требующих перевода двоичных данных в ASCII-представление или наоборот. Собственно говоря, программирование на ассемблере и начинается с вывода на экран числа, заданного в двоичной форме. Конечно, «вручную» разработанная и оптимизированная функция намного быстрее стандартного sprintf. Однако очень редко можно встретить программу, расходующую основное время на преобразование данных, поэтому использование библиотечных функций сокращает размер и время разработки программы.

Приведенный ниже пример распечатывает число, содержащееся в регистре EAX в шестнадцатеричной, десятичной и восьмеричной форме, автоматически дописывая ведущие нули, растягивающие число до 4-х разрядов. А теперь попробуй осуществить то же самое без использования библиотек и сравни размер полученного кода!

«Фрагмент программы, принимающий число в регистре EAX и выводящий его на экран в шестнадцатеричной, десятеричной и восьмеричной формах»

mov eax, 666h ; число, которое необходимо вывести на экран

; // переводим число в hex, dec и oct системы исчисления в ASCII-представлении

sub esp, 60h ; резервируем память под буфер, куда пойдет результат

mov ebx, esp ; сохраняем указатель на буфер в регистре EBX

push eax ; \

push eax ; + - передаем число для преобразования функции sprintf

push eax ; /

push offset s ; передаем в стек указатель на строку спецификаторов

push ebx ; передаем указатель на буфер для получения результата

call sprintf ; прямой вызов функции sprintf

; // вывод преобразованных данных на экран через диалоговое окно

xor eax,eax ; eax := 0

push eax ; uType

push eax ; lpCaption

push ebx ; lpText (наши преобразованные данные)

push eax ; hWnd

call ds:[MessageBoxA] ; косвенный вызов API-функции MessageBox

add esp, 60h + (5*4) ; выталкиваем аргументы из стека и уничтожаем буфер

...

...

...

s db "%04X hex == %04d dec == %04o oct",0

; строка спецификаторов

Вызов API-функций из ассемблерный вставок

При вызове API и DLL-функций из ассемблерных вставок возникает множество проблем, довольно туманно описанных в документации, прилагаемой к компилятору. Возьмем, к примеру, Microsoft Visual C++ и попробуем вызвать функцию GetVersion так, как будто мы сделали это на чистом ассемблере:

__asm{

call GetVersion ; прямой вызов API-функции

}

Компилируем файл с настройками по умолчанию и запускаем. Программа тут же рушится. Почему? Смотрим в дизассемблере:

Назад на стр. 091-120-1  Содержание  Вперед на стр. 091-120-3