Ассемблерные извращения Крис Касперски Хакер, номер #096, стр. 096-140-1 Натягиваем стек по-хакерски Ассемблер предоставляет практически неограниченную свободу для самовыражения и всевозможных извращений, что выгодно отличает его от языков высокого уровня. Вот мы и воспользуемся этой возможностью, извратившись не по-детски и сотворив со стеком то, о чем приплюснутый Си только мечтает. Турбопередача стековых аргументов Передачу аргументов через стек можно существенно ускорить, в случае если аргументы представляют собой константу, известную еще на стадии трансляции. Классический способ передачи выглядит так: Классический способ передачи стековых аргументов 00000000: 6869060000 push 000000669 00000005: 6899090000 push 000000999 0000000A: 6896060000 push 000000696 0000000F: E852060000 call 000000666 Довольно расточительное (в плане процессорных тактов) решение, особенно если функция вызывается многократно. При этом операнды команды PUSH перегоняются из секции .text (находящейся в кодовой кэш-памяти первого уровня) в область стека, находящуюся в кэш-памяти данных. Ну и зачем гонять их туда и обратно, когда аргументы можно использовать непосредственно по месту хранения? Усовершенствованный пример выглядит так: .code MOV EBP, ESP MOV ESP, offset func_arg + 4 CALL my_func MOV ESP, EBP .data func_arg DD 00h, 696h, 999h, 669h И хотя размер кода после оптимизации не только не сократился, но даже увеличился (14h байт до оптимизации и 1Eh - после), мы сохранили немного стековой памяти и сократили время выполнения. Причем чем больше аргументов передается функции, тем в более выигрышном положении оказывается оптимизированный вариант, поскольку неоптимизированный вынужден тратить на каждый аргумент один дополнительный байт! 00000000: 8BEC mov ebp, esp 00000002: BC66000000 mov esp, 000000013 00000007: E80E000000 call 000000666 0000000C: 8BE5 mov esp, ebp … 0000000E: 00 00 00 00 96 06 00 00 ? 99 09 00 00 69 06 00 00 0000001E: Несколько замечаний по поводу. Первое. Операционные системы семейства Windows NT (к которым принадлежат Windows 2000, Windows XP, Windows Vista, Windows Server 2003 и Windows Server Longhorn) гарантируют целостность содержимого стека выше его вершины (для адресов меньших, чем ESP), поэтому переносят такие извращения безо всякого ущерба для работоспособности программы. Операционные системы семейства Windows 9x ведут себя иначе, бесцеремонно используя все, что находится выше ESP в целях «производственной необходимости», что ведет к искажению секции данных и последующему краху программы. Поэтому все, что было сказано здесь, распространяется только на NT. Замечание номер два. Перед аргументами необходимо оставить двойное слово (а в 64-битном режиме — четвертное) для сохранения адреса возврата. При этом секция данных, где находится это слово, должна быть доступна на запись. Если же функция вызывается из одного единственного места и адрес возврата известен заранее, ничего не мешает положить его рядом с аргументами. Но тогда функцию придется пускать командой jump, а не call, что еще больше увеличивает производительность: Вызов функции с предопределенным адресом возврата командой JMP |