Воробьёвы

(-:

№30. В догонку о фрезеровщиках... тьфу... профилировщиках

КРИМИНАЛЬНОЕ ЧТИВО

Тихо! Тсс... Сегодня вы читаете строго секретный выпуск "Низкоуровневого программирования". Кто-то обещал со временем поиметь матрицу, помните? Если на улице к вам подойдут два агента... Танк, у нас есть программа кунг-фу? А, есть программа IDA? И это еще круче кунг-фу? Тогда - БАНЗАААЙ!

...Шарясь по ставшему любимым сайту Интела, кто мог не заинтересоваться их компиляторами и утилитами? Кому, кроме производителя процессора, известны ВСЕ тонкости? Правильно, никому. Вывод - интелловские компиляторы просто обязаны быть самыми-самыми. Работать быстро, создавать самый оптимальный код, как по времени, так и по размеру. Похоже, так оно и есть... Intel обещает настройку на любой процессор, средства контроля производительности, и так далее, и тому подобное. Представляете, указав при компиляции СИшной программы процессор Pentium II, а потом - Pentium IV, получим 2 РАЗНЫХ результата - каждый скомпилирован оптимально для выбранного процессора! А еще есть очень ценная утилитка (маленькая такая... 74 метра :)), способная дать детальную информацию о каждой процедуре в вашей DLL, о любом участке кода - скорость выполнения, оптимальность... ЧУДО!? Нет, это не чудо, мать твою! Это... VTune, мать твою! (если Тарантино предъявит права на эту фразу - я этого не писал! :))

Можно долго ходить кругами и облизываться, и хотеть, и скачать, несмотря на размер, если бы не одно НО: это не freeware! :(

Естественно, скачать сей продукт можно, но работать он не будет - надо покупать... или? Или читать наши статьи! Не перевелись еще на Руси добры молодцы и красны девицы! ;)

"… ибо его оружие - мозг - не требовало особого
ухода, спецхранения и работало в любых условиях."
В. Головачев "Схрон"

Автор: VOLODYA [HI-TECH]

Обзор некоторых защит на основе FLEXlm SDK

Итак, я предполагаю, что читатель имеет твердое представление об ассемблере, неплохо владеет Си, прекрасно разбирается в том, как компилировать и линковать программы. Также, очевидно, необходимо уметь пользоваться Visual Studio.

Данная статья написана как довесок к моей основной статье о профилировщиках. Здесь рассказывается, как устранить некоторые проблемы инсталляции и работы программ , использующих для своего функционирования набор API-функций, предоставляемых пакетом FlexLm. Так уж получилось, что подавляющее большинство профилировщиков, средств покрытия кода (т.е. средств, показывающих, сколько веток if и других циклов выполняется при нормальном течении хода проги, это очень полезно, т.к. показывает, насколько ваша прога "готова" к нестандартным изменениям ситуации, как тщательно вы подготовились к неприятностям, т.к. если ветки, которые получают управление редко, небрежно написаны, то в результате можно получить ой-ой-ой!) и выявления утечек памяти используют именно FlexLm. Среди них – Intel Compiler/Vtune environment, все продукты семейства DevPartner от Numega, и все опробованные мной продукты от Rational.

Рассмотрим сперва Intel Compiler, единственный реальный конкурент MS cl.exe. Закачать его можно с ftp://download.intel.com/software/products/. Последняя версия на момент написания -W_CC_P_7.0.073.exe. «Вес» пакета – 74 Мб.

Итак, закачиваем и инсталлируем (помните, в обязательном порядке должно иметь место наличие присутствия Visual Studio 6/Net). При попытке инсталляции получаем что-то вроде FlexLm license error. Теперь мы и подобрались к сути проблемы.

Итак, FlexLm – это творчество компании GLOBEtrotter Software. Фиговина эта требует присутствия .lic файла (действие которого может быть ограниченным во времени), без которого работать не хочет ну никак. Компания, как это модно сейчас, выпускает FlexLm SDK, где имеются API-функции для работы с lic-файлом, которые надо втиснуть в исходный код, скомпилировать с соответствующими .h-файлами и слинковать с соответствующими библиотеками.

Только в качестве иллюстрации: #include "lmpolicy.h" //компиляция с нужным файлом оглавления LP_HANDLE *lp_handle; status = lp_checkout(LPCODE, policy, feature, version, num_lic, license_file_path, &lp_handle) //здесь зовем нужные функции

Библиотеки для разных случаев используются разные. Скажем, для линковки мультипоточного варианта (multithread) будут использоваться одни библиотеки, если приложение представлено одним единственным процессом – другие. Ниже дан пример из FlexLm SDK версии 8.0:

  FLEXlm Object File FLEXlm Client Libraries MSVC++ Libraries
Standard lm_new.obj lmgr.lib libcmt.lib(/MT) oldnames.lib kernel32.lib user32.lib netapi32.lib advapi32.lib gdi32.lib comdlg32.lib comctl32.lib wsock32.lib
Add for CRO   libcrvs.lib libsb.lib  
Add for static FLEXlock   flock.lib  
For /MD Replace lm_new.obj with lm_new_md. obj Replace Standard lmgr.lib with lmgr_md.lib Replace Standard libcmt.lib with msvcrt.lib (/MD)
For CRO /MD   Add to Standard: libcrvs_md.lib libsb_md.lib Replace Standard libcmt.lib with msvcrt.lib (/MD)

Видно, что здесь есть много интересного. Сразу появляется желание скачать этот самый SDK и маленько с ним разобраться. Ничего невозможного - http://linux20368.dn.net/crackz/Flexlm.htm, т.к. с родного сайта компании вы уже ничего не скачаете - доступ закрыт, однако.

Теперь о том, как это ломать. Варианта два – 1-й - разобраться с форматом .lic файла, подправить оригинальные API-функции, словом вдумчиво ковырять FlexLm SDK (это уже и было проделано и в сети есть много англоязычных статей на эту тему), 2-й – лично мне больше по душе – ковырять конкретное приложение, которое неумело использует функции FlexLm. Есть идиоты, которые вообще выносят эти API-функции в отдельную .dll, вместо статической линковки, и это несмотря на предупреждения, красным по белому:

Caution: Use of the FLEXlm DLL on Windows remains strongly discouraged

А ведь если функции вынесены в dll, то кракеру поймать их ничего не стоит. Пример не за горами.

Итак, теперь к делу.

Выкачиваем компилятор. Пробуем запустить – ругается, нужен lic-файл. Его, ессно, нет. Начинаем копать. Существует достаточно много подходов ко взлому конкретного приложения. Кто к каким привык. Стандартом, как правило, считается постановка bpx на что-нибудь вроде MessageBoxExW или DialogBoxIndirectParamA, затем исследуется дизассемблированный код ВЫШЕ точки «всплыва» отладчика и определяется, какой из условных переходов, привел к таким ругательствам со стороны проги.

MessageBoxExW

Для постановки точки останова следует всегда выбирать MessageBoxExW вместо MessageBoxA или тому подобных. Дизассемблируйте user32.dll и увидите, что MessageBoxA, W, ExA являются просто переходниками к ExW. Такое имеет место быть только в чисто юникодовских системах, таких как Windows NT, 2000, XP. К 95/98 это не относится, а если что-то такое и имеет место быть, то с точностью до наоборот. Данное правило подходит не только к MessageBox, оно соблюдается и для многих других WinAPI функций – ASCII-варианты являются просто переходниками к юникодовским

Еще один вариант – это прогонять программу под каким-нибудь API-монитором, вроде BoundsChecker от NuMega. Реальных конкурентов у него нет, одни слишком медленные и тупые, вторые, может и быстрые, но для хакера совершенно непригодны. На этом этапе, если приложение использует какие-то API-функции из определенного рода библиотек (например, flexlm-библиотек ), то это все станет очевидным.

Можно даже и не пользоваться API-монитором, т.е. динамическим анализом программы. Вполне хватит и статического, если не используется явное связывание (explicit linking). Для статического анализа лучше всего использовать dumbin или dependency walker. Оба от MS.

Explicit linking

Динамическую библиотеку можно вызывать двумя способами – явным и неявным связыванием. Неявное связывание – наиболее широко используемый способ. Здесь с самого начала компилятору строго говорится, что вот так-то и так-то, вот это вот считать dll и поступать с ней вот так, а не иначе. Явное связывание – это явная задача в исходном коде инструкций вида LoadLibraryA для загрузки конкретной библиотеки (например, kernel32.dll), а потом GetProcAddress для возвращения адреса конкретной функции (например, IsDebuggerPresent ), а потом что-то вроде call [адрес, возвращенный GetProcAddress]. Ясно, что неявное связывание оставляет следы в таблице импорта, которые легко обнаружить, например, dumpbin. Явное связывание в таблице импорта не отображается. Подробнее – Джеффери Рихтер «Windows для профессионалов»

Очевидно, что любой из видов анализа рано или поздно приведет вас к анализу дизассемблированного кода. Лучше всего использовать IDA. Аналогов ей нет все равно. Вообще, я лично с презрением отношусь ко всяким заявлениям типа «следует юзать только HIEW, потому что настоящие кульные хацкеры юзают только HIEW и ничего больше». Надо уметь пользоваться всем, что под руку попадается.

Так вот, не важно, какой из вышеперечисленных методов используется, так или иначе, мы с вами вышли на файл ChkLic.dll, который экспортирует одну, ну жутко интересную функцию - _CheckValidLicense. Дальше выбора два – либо делать патч непосредственно в dll, либо в зовущем файле – setup.exe. Код выглядит так:

 Setup.exe - 40,960 bytes

_text:0040175D loc_0_40175D:                           ; CODE XREF: _WinMain@16+744j
; явное связывание в наглядном варианте. В таблице импорта setup.exe следов вызова
; функции не будет, однако рапорт BoundsChecker покажет, что имел место вызов .dll и
; вызывалась соответствующая функция
_text:0040175D                 push    offset aChklic_dll    ; lpLibFileName
_text:00401762                 call    ds:LoadLibraryA
_text:00401768                 mov     esi, eax
_text:0040176A                 push    offset a_checkvalidlicense ; lpProcName
_text:0040176F                 push    esi                ; hModule
_text:00401770                 call    ds:GetProcAddress
_text:00401776                 cmp     esi, ebx
_text:00401778                 jz      loc_0_40192E
_text:0040177E                 cmp     eax, ebx
_text:00401780                 jz      loc_0_40192E
_text:00401786                 push    ebx
_text:00401787                 call    eax  ; _CheckValidLicense
_text:00401789                 test    eax, eax
_text:0040178B                 jnz     loc_0_4019A3  ;заменить на jz

Можно считать, что с первой линией обороны мы справились. Заметьте, что защита flexlm пакету не помогла вообще, мы даже и не узнали, есть ли она там. Авторам надо было хоть немного читать то, что написано в SDK. А ведь ситуация могла бы и изменится, если бы использовалась статическая линковка. Но на каждую хитрую жопу есть хрен с винтом. Выход очень прост. Далее, в файлах icl.exe (интеловский компилятор для IA-32) и ecl.exe (IA-64 вариант) уже никакие dll не используются, и, все равно, это ничем не помогло разработчикам. Здесь можно работать по следующей методике. Сперва нам надо узнать, какая версия flexlm используется, использовать технологию IDA FLAIR, исследовать код вблизи мест проверки lic-файла и взломать прогу.

IDA LAIR

Ильфак Гюильфанов написал замечательную статью о том как в IDA реализован механизм определения библиотечных функций – «Как IDA распознает стандартные функции?». Статью можно найти на pilorama.com.ru, повторяться не хочу.

Для определения версии flexlm можно использовать любой текстовый фильтр, либо тот, который найдет требуемое слово в бинарнике (Search and Replace – kickme.to/FOSI), либо тот, который вывалит все текстовые строки бинарника (мой собственный, например – wasm.ru – filter, "исходники").

Так или иначе:


24/12/2002 5:33:52 PM
Search String: flexlm
Replace String:
Path: C:\Downloads\comp\
File Mask: *.*

Processing file : C:\Downloads\comp\chklic.exe

Offset 0x680dd - $Id: flexlm.c,v 1.10.2.1 2002/10/18 20:06:49 mtoguchi Exp $
Offset 0x6e781 - yyyyyyyyyyyy@(#) FLEXlm v7.2i (liblmgr.a),
Copyright (C) 1988-2001 Globetrotter Software, Inc.

Верхняя строчка оставлена для хохмы, это вообще одуреть. Строки отчета cvs попали в бинарник! Это ж какое качество компилятора! Ладно, зато теперь нам точно известно, какая версия использовалась. Это дает возможность использовать средства FLAIR для получения файла сигнатур, который поможет элементарно найти нужные строки кода в дизассемблированном листинге. Ниже приведен пример создания файла сигнатур для FlexLM SDK 8.0.

E:\flexlm\v8.0\i86_n3>pcf lmgr.lib lmgr
lmgr.lib: skipped 0, total 142

E:\flexlm\v8.0\i86_n3>pcf lmgr_md.lib lmgr_md
lmgr_md.lib: skipped 0, total 142

E:\flexlm\v8.0\i86_n3>sigmake -n"FlexLm Win32 v8.0
Standard&Multi-thread" lmgr.pat+lmgr_md.pat lmgr.sig

В первой строке запускается парсер для lib-файлов coff-формата (elf-формат идет под Linux). В результате имеем pat-файл. В данном случае запускаем парсер второй раз для получения мультипоточного варианта библиотеки. Потом используется утилита sigmake для объединения полученных pat-файлов в один sig-файл, ключ –n дает файлу название, под которым его видит конечный пользователь. Процесс опознавания библиотечных функций не безупречен, часто появляется файл коллизий. Опять таки, отсылаю к статье Ильфака. Файл коллизий редактируется очень просто. В этом файле представлены функции, которые имеют сходные CRC. Теперь уже нам с вами нужно решать, что с ними делать (ставить "-" для появления имени функции в виде комментария, "+" для обычного вида, не ставить ничего для полного исключения функции из файла сигнатур). Я предпочитаю включать ту, что выполняет более общее действие. Пример:

+_l_swap                                             58             734E
558B4C24108BEC83EC60538B450CC745FC0000000056578B34888B7C880485F6
_swap                                                58             734E
558B4C24108BEC83EC60538B450CC745FC0000000056578B34888B7C880485F6

Здесь поставлен знак «+» перед функцией _l_swap, так как думается мне, что эта функция более напоминает при анализе бинарника функцию flexlm SDK. Аналогично расставляются знаки и для других пар (или большего количества) функций, вызвавших коллизии.

После редактирования .exc файла просто повторите:

E:\flexlm\v8.0\i86_n3>sigmake -n"FlexLm Win32 v8.0
Standard&Multi-thread" lmgr.pat+lmgr_md.pat lmgr.sig

Утилита сама поймет, что делать с файлом коллизий на этот раз. В завершение процесса нужно сжать файл сигнатур утилитой zipsig и поместить его в директорию /sig папки IDA.

Дизассемблируем файл icl.exe и ecl.exe. Применяем полученный файл сигнатур (я приложил sig-файлы для FlexLM SDK v6.0/7.2i/8.0). У меня было опознано свыше девятисот(!) библиотечных функций. Теперь мы сравнительно легко придем к нужному результату:

 icl.exe - 684,032 bytes


_text:0040105A loc_0_40105A:                           ; CODE XREF: _main+350j
_text:0040105A                 mov     eax, dword_0_488B24
_text:0040105F                 call    sub_0_4083F4
_text:00401064                 mov     eax, offset aFfrpsz  ; "FFrpsZ"
_text:00401069                 mov     edx, 21h
; легко можно увидеть, что эта функция "скатывается" к FlexLMMainCheck
; думаю, название снимает все вопросы
_text:0040106E                 call    sub_0_404C0E  ;mov eax,0
_text:00401073                 test    eax, eax
_text:00401075                 jnz     short loc_0_40107F


ecl.exe - 692,224 bytes

_text:0040305A loc_0_40305A:                           ; CODE XREF: _main+350j
_text:0040305A                 mov     eax, dword_0_48AB44
_text:0040305F                 call    sub_0_40BD51
_text:00403064                 mov     eax, offset aFfrpsz  ; "FFrpsZ"

_text:00403069                 mov     edx, 21h
_text:0040306E                 call    sub_0_40837E  ;mov eax,0
_text:00403073                 test    eax, eax
_text:00403075                 jnz     short loc_0_40307F

Теперь у нас есть компилятор. Самое время взломать профилировщик.

Используя методы, описанные выше, мы легко сможем обнаружить, что ругательство при запуске VTune выдает AnProdInfo.dll, опираясь на ресурсы из AnProdInfoRes.dll. Дополнительно, чтобы облегчить себе жизнь, нужно использовать команду Soft-Ice – stack – она покажет последовательность вызовов функций и мы сможем вычислить искомую.

STACK – проход стека, FPO и т.п.

Кадр стека (т.е. адресация по ebp либо инструкции enter/leave) - великая вещь, своим появлением он обязан относительно "скудным" возможностям адресации процессоров до 386-го (тогда с адресацией было туговато, не всякий регистр подходил), где в качестве индексного регистра разрешалось использовать только si,di,bx,bp. Теперь в качестве индексного можно использовать любой 32-разрядный регистр, например esp. Тогда кадр стека становится ненужным (т.к. используется адресация переменных по esp ± xxh - такой подход называется frame pointer omission (FPO)), а это есть хорошо для прикладных программистов и не есть хорошо для нас с Вами, т.к. при esp-подходе теряется большая часть символьной информации (например, команда S-Ice stack становится бесполезной, если в функции отсутствует кадр стека). Если разработчик программы - лопух и забыл убрать отладочную информацию, то мы сможем выловить с помощью dumpbin /FPO информацию о количестве переданных параметров и т.п.

Пример: с помощью Search&Replace я задал искать слово .debug (есть иногда такая секция в PE-файле) в директории Windows\system. Нашлось файлов 5. Вот часть дампа со случайно взятого файла:

dumpbin.exe /FPO sh30w32.dll >fpo.txt
Dump of file sh30w32.dll
File Type: DLL
FPO Data (222)
                                                    Use Has  Frame
RVA      Proc Size   Locals  Regs   Prolog  BP  SEH  Type   Params
00001000        96        0     2       2   N   N    fpo    4  _MemPoolShrink@4
00001060       144        8     4      21   Y   N    fpo    0

Уже сейчас мы можем выяснить размер процедуры, количество переданных параметров, количество локальных переменных и т.п. Если Вы не очень уютно себя чувствуете с кадром стека - почитайте Рэндалла Хайда и проиграйтесь с отладчиком - многое прояснится. Может (и справедливо!) возникнуть еще один вопрос: "Хорошо, я знаю адрес процедуры, количество параметров и прочую ерунду. А КАК мне сделать, чтобы это узнал и Soft-Ice?" Ответ см. ниже - он есть, а пока методику поняли? Ищем секцию отладки в PE-файле (F8,F6 в HIEW) - если есть, она будет называться как-нибудь вроде .debug. Далее извлекаем ее (см. ниже), переводим в понятный S-Ice'у формат (см. ниже ) и работаем дальше.

Результат:

W_VT_P_61_0011.EXE - закачать с intel.com, размер - свыше 100 мб.

AnProdInfo.dll

.text:602341C0                 call    sub_60232466 ->xor eax,eax
 .text:602341C5                 xor     ebx, ebx
 .text:602341C7                 add     esp, 0Ch
 .text:602341CA                 cmp     eax, ebx
 .text:602341CC                 mov     [esi], eax
 .text:602341CE                 jz      loc_6023429D
 .text:602341D4                 mov     [ebp+0Ch], ebx
 .text:602341D7                 cmp     eax, 0FFFFFFEBh
 .text:602341DA                 mov     [ebp-4], ebx
 .text:602341DD                 jz      short loc_602341F2
 .text:602341DF                 cmp     eax, 0FFFFFFF6h
 .text:602341E2                 jz      short loc_602341EB
 .text:602341E4                 push    0D1h
 .text:602341E9                 jmp     short loc_602341F7

«Однако за время пути собака могла подрасти». Пока писалась эта статья, я совершенно случайно накопал еще один update от Intel на тему Vtune –

ftp://download.intel.com/software/products/vtune/downloads/.

Что он делает, в двух словах и не рассказать. В своей статье о профилировщиках я его не описываю тоже, отчасти потому, что работать с ним страшновато. Это надстройка над JavaMachine, да еще к ней прибабахан MySQL-сервер. Для чего все это нужно, мне сказать сложновато. Естественно, все это дело тормозит жутко, а потому вдумчиво его ковырять на предмет радостных сообщений защиты мне было недосуг.

Поэтому здесь я скорее предпочту описать методики, как можно поймать интересующий код, чем давать готовые решения, хотя парочка решений, конечно же, будет.

Итак, немного поиграв с пакетом, имеем стандартный MessageBox со стандартными фразами. Что здесь можно сделать?

Вещь первая. Если строки сообщения защиты находятся в ресурсах PE-файла, то надо воспользоваться редактором ресурсов (например, Restorator 2.52 – wasm.ru), чтобы узнать их порядковый номер. Пример:

123      The GUI license for VTune(TM) Enterprise Analyzer for Web
         Applications is invalid.  Please contact technical support.
   

Здесь, "123" – это произвольный (т.е. разработчик брал от фонаря) порядковый номер ресурса, который в дальнейшем будет использован функцией LoadString. Номер этот будет передан функции через стек (таков закон ). Т.е. будет использоваться инструкция push + шестнадцатеричное представление "123" (0x7B). Такой шаблон легко найти в HIEW – в режиме ассемблерного поиска надо ввести push ?7B (отметьте знак вопроса – он означает, что перед 7В может идти что угодно). Чаще всего, вхождений будет несколько, однако что стоит найти правильное?

 
 .text:10024387                 push    7Bh             ; uID
 .text:10024389                 mov     [esp+4Ch+var_4], esi
 .text:1002438D                 mov     ecx, [eax+8]
 .text:10024390                 push    ecx             ; hInstance
 .text:10024391                 lea     ecx, [esp+50h+var_34]
 .text:10024395                 call    sub_10010A10   ;к LoadString

А дальше, опять таки нужно воспользоваться командой stack в Soft-Ice, чтобы понять, какая последовательность вызовов функций привела к столь плачевному результату.

Вещь вторая. Коль уж вхождение найдено, часто оказывается необходимым ставить bpx не на MessageBox и ему подобные, а на конкретные адреса. Т.к. VTune – продукт, содержащий множество .dll, это автоматически означает, что будет производится перерасчет адресов загрузки .dll (подробнее – моя статья о профилировщиках или многочисленные статьи Мэтта Питрека и Джона Робинса в сети). А это, в свою очередь, означает, что адреса, которые мы увидим в IDA/WDasm/HIEW и т.п. НЕ будут соответствовать адресам в памяти, а предсказать адрес, по которому dll будет загружена, достаточно сложно, т.к. точно не известен порядок, в котором приложение загружает эти dll. Однако выход здесь прост. Старый кракерский трюк с int 3. int 3 имеет опкод 0CCh. Влепите этот опкод в требуемое место программы (не забыв выровнять количество байт и выписать эти самые байты, потому что потом их придется восстанавливать в Soft-Ice). Теперь для того, чтобы Sof-Ice отловил этот опкод, надо активировать опцию i3here on.

i3here on

Soft-Ice является отладчиком класса last-chance notification. Это означает, что он предоставляет отладчикам класса first-chance notification (таким, как отладчик VisualStudio) шанс обработать исключительную ситуацию, перед тем как брать бразды правления в свои руки. Что такое "последнее предупреждение" и "первое предупреждение" в двух словах не рассказать, это связано с реализацией SEH (structured exception handling) в Windows и знакомо специалистам по C++ как блоки try/catch. Да, кстати, почитайте в документации к Soft-Ice, что такое опция faults off. Она во многом определяет поведение Soft-Ice во время исключительных ситуаций. Есть также неплохая книжка Джеффери Рихтера – “Windows для профессионалов”. Там тоже очень неплохо рассказывается об исключениях. Интересующихся низкоуровневой организацией SEH в Windows (на уровне внутренних структур данных) отсылаю к статьям Мэтта Питрека.

После срабатывания точки останова и «всплыва» Soft-Ice необходимо поправить EIP (r eip = требуемое значение) так, чтобы он показывал ПРЯМО на int 3, а не на инструкцию ПОСЛЕ нее (там, где, собственно, и произошел всплыв). После того как поправили EIP, с помощью инструкции "a" или "e" поправьте байты по тому адресу (на те самые, которые выписывали, помните?).

Рано или поздно, исследуя внутренности VTune Enterprise можно наткнуться на функцию ?IsJavaEnabled@CTahoeLicenseMgr@@QAE_NXZ. Это одна из функций защиты приложения. Функция экспортируется библиотекой VTEGUIShared.dll и "спускается" к

?IsFeatureEnabled@CTahoeLicenseMgr@@QAE_NW4SPFeatureEnum@@@Z
 .text:1002D780                 mov     eax, [ecx+4]
 .text:1002D783                 mov     ecx, [esp+arg_0]
 .text:1002D787                 and     eax, ecx
;Очень изящный и приятный код. Это пример хорошей оптимизации. Смотрите,
;вообще нет  условных переходов, которые для современных CPU не
;представляют собой ничего хорошего.
;Вместо этого используется инструкция вычитания с заемом - более
;подробно см. статьи
;Касперски. А здесь, недолго мороча голову:
.text:1002D789                 neg     eax  --> mov eax,1; nop
 .text:1002D78B                 sbb     eax, eax
 .text:1002D78D                 neg     eax
 .text:1002D78F                 retn    4

Вроде все методики расписаны. Единственная вещь, которая может вызвать легкое смущение – это странный вид имени импортируемой функции. Это – наглядный пример искажения ("замангления") имен.

Искажение имен (mangling)

Также иногда встречается термин "украшение".

Искажение имен применяется компилятором для разрешения своих внутренних конфликтов. Часто сложно отличить имена функций друг от друга. Проблема становится особенно актуальной в ООП-языков программирования, где становится возможной перегрузка функций (когда функции имеют одинаковое название, но выполняют более-менее разные действия) и даже операторов. Вот компилятор и изворачивается. Каждый компилятор (даже одного и того же языка) имеет собственную схему искажения имен. Схема искажения имен компилятора Borland сравнительно неплохо описана П.В. Румянцевым в своей "Работа с файлами в Win32". Схема искажения имен компилятора от MS, насколько я знаю, нигде особо не описывалась (хотя, может, стоит статьи Питрека поглядеть?) Однако есть утилита undname.exe, входящая в состав VisualStudio. Вся эта утилита, позволяющая, как и IDA, "разманглять" символьные имена, является переходником к функции UnDecorateSymbolName из imagehlp.dll. Функция вполне документирована, однако, представляет собой всего лишь переходник к уже недокументированной функции __unDName из msvcrt.dll. Советую изучить код функции, если вас интересует проблема искажения имен.

Теперь можно поговорить о взломе продуктов компании Rational Software (www.rational.com). Это ближайшие конкуренты фирмы NuMega, однако продукты их подходят только для программиста, а не для хакера. Но ведь и мы с вами не только тем занимаемся, что программы ломаем?

Основной пакет – PurifyPlus. Состоит из Purify/Quantify/PureCoverage. Все они тесно интегрируются с VS.NET. Purify – средство выявления утечек памяти и других мелких гадостей, Quantify – весьма неплохой профилировщик, PureCoverage – средство покрытия кода. Всем этим программам есть аналоги от NuMega (TrueTime, TrueCoverage и т.п.), однако в последнее время нужный инструмент от NuMegaне всегда легко найти. С сайта скачать невозможно ничего (в отличие от rational.com, пока!), в сети выловить тоже тяжеловато.

PurifyPlus активно использует flexlm (на момент написания статьи – версия 7.0f), о чем сам и говорит. Взломать эту защиту не составляет никакой сложности. Нетрудно установить, что ругательство ведется из rscommonui.dll. Вообще забавно наблюдать, как все FlexAPI-функции засовываются в одну-единственную на всю программу несчастную функцию, да еще и возвращающую результат типа bool. А потом еще говорят, что flex криво написан. Он-то, может, и криво написан, но используют его еще кривее! Что ж стоит подделать ответ функции, поменять нолик на единичку или наоборот? Да ничего! И никаких больше проверок! А ведь метод, который я предлагаю здесь, неверен в принципе! Здесь мы боремся со следствием, а причина (.lic-файл) остается нетронутой. А борьба со следствием чревата тем, что всегда есть риск проворонить где-нибудь какой-нибудь подлый call и получить уже полные старания фантазии разработчика, вплоть до самоуничтожения программы. Однако сейчас код код пишется настолько неаккуратно, что многие защиты можно взломать, не прилагая особых умственных усилий. В самом деле, все чаще встречаются программы, использующие для защиты либо примитивнейшие самопальные алгоритмы, возвращающие 1 или 0 в результате, либо использующие пакеры/криптовщики, но это уже совсем другая песня…

Патч:

RationalPurify.2002.05.20.468.000.exe

purifyw.exe - 3,240,020 bytes

_text:00407F33 loc_0_407F33:             ; CODE XREF: sub_0_407F20+Aj
_text:00407F33                 call    sub_0_4E2060
_text:00407F38                 call    sub_0_5DBD5F
_text:00407F3D                 test    eax, eax
_text:00407F3F                 jz      short loc_0_407F68
_text:00407F41                 call    sub_0_4E26C0
_text:00407F46                 test    eax, eax
_text:00407F48                 jz      short loc_0_407F55
_text:00407F4A                 call    sub_0_4E20F0; funny proc - mov eax,1
_text:00407F4F                 test    eax, eax
_text:00407F51                 jnz     short loc_0_407F68
_text:00407F53                 pop     esi
_text:00407F54                 retn


RationalQuantify.2002.05.20.468.000.exe

quantifyw.exe - 2,621,528 bytes

_text:00428C23 loc_0_428C23:                           ; CODE XREF: sub_0_428C10+Aj
_text:00428C23                 call    sub_0_491130
_text:00428C28                 call    sub_0_56E8EF
_text:00428C2D                 test    eax, eax
_text:00428C2F                 jz      short loc_0_428C58
_text:00428C31                 call    sub_0_4917D0
_text:00428C36                 test    eax, eax
_text:00428C38                 jz      short loc_0_428C45
_text:00428C3A                 call    sub_0_4911C0 ; funny proc - mov eax,1
_text:00428C3F                 test    eax, eax
_text:00428C41                 jnz     short loc_0_428C58
_text:00428C43                 pop     esi
_text:00428C44                 retn


RationalPureCoverage.2002.05.20.468.000.exe

coveragew.exe - 2,527,320 bytes

_text:00405773 loc_0_405773:                           ; CODE XREF: sub_0_405760+Aj
_text:00405773                 call    sub_0_489800
_text:00405778                 call    sub_0_559E7F
_text:0040577D                 test    eax, eax
_text:0040577F                 jz      short loc_0_4057A8
_text:00405781                 call    sub_0_489EA0
_text:00405786                 test    eax, eax
_text:00405788                 jz      short loc_0_405795
_text:0040578A                 call    sub_0_489890 ; funny proc - mov eax,1
_text:0040578F                 test    eax, eax
_text:00405791                 jnz     short loc_0_4057A8
_text:00405793                 pop     esi
_text:00405794                 retn

Вот, собственно, и все о защите FlexLm. То есть, о защите писать еще можно долго и нудно, однако о крайне бездарном ее применении в приложениях, я думаю, вы уже имеете весьма неплохое представление. За сим разрешите откланяться.

Современные профилировщики – что выбрать?

Автор: VOLODYA [HI-TECH]

Лирическое отступление (или вступление?)

Данная статья написана мной, когда потребовалось разобраться в профилировке программы, что это такое и зачем это нужно. В русскоязычном Интернете практически отсутствует информация на эту тему (за единственным, правда, исключением – статьей Стивена Е. Сайпа «Профилировщики программ Си++», капитально устаревшей). В англоязычной сети ситуация получше, но тоже далека от идеала. Именно поэтому данная статья и появилась.

Итак, перед началом я предполагаю, что читатель имеет твердое представление об ассемблере, неплохо владеет Си, прекрасно разбирается в том, как компилировать и линковать программы. Также, очевидно, необходимо уметь пользоваться Visual Studio.

Существует множество профилировщиков, т.е. утилит, которые позволяют вам определить (запуская «подопытную» программу), где в ней присутствуют узкие места. Под словом «узкие» здесь понимается «вызывающие значительное снижение скорости выполнения». В общем-то, вполне очевидно, что можно определить это все и по исходному коду, однако, если величина программы переходит некоторый порог, скажем 100 кб исходных кодов, то без профилировщика здесь уже не обойтись, т.к. в голове это все уже не удержать. Можно, конечно, им и не пользоваться, но тогда появляются о-очень уродливые и медленные шедевры.

Итак, программа должна быть специально подготовлена для анализа (хотя в некоторых случаях в этом нет необходимости, см. ниже), запущена под профилировщиком, который выдаст кучу цифири о времени выполнения различных функций, затем все это прилежно анализируется и код переписывается так, чтобы обеспечить более приемлемое быстродействие.

VTune

Безусловный лидер. Очень много полезных функций и возможностей. Правда за это приходится платить быстродействием и дисковым пространством. Краткая характеристика профилировщика дана в таблице:

CHARACTERISTICS CSAMPLING CCALL GRAPH CCOUNTER MONITOR
Intrusiveness (вставки в код) Non-intrusive. Вставок в код не происходит, периодически просматриваются активные в данный момент инструкции Intrusive. Делает вставки в код (в пролог и эпилог каждой функции) для определения производительности Non-intrusive. Используются счетчики производительности, встроенные в современные процессоры
System-wide performance data (данные о производительности системы в целом) Выдает данные о всей системе в целом Применим только к данному конкретному приложению. Выдает данные о всей системе в целом
Parent and child function relationship (отношение родительских и дочерних функций) Учитывает лишь время, проведенное в конкретной функции Учитывается все. Соотнесения информации, полученной со счетчиков с кодом/функциями исследуемого приложения не происходит
Program flow (ход программы) Выдает статистический анализ вызовов функций приложения. Очень полезно! Вкупе с Sampling дает множество очень полезной информации об исследуемом приложении в виде древа вызова функций Не определяет ничего, т.к. не соотнесено ни с каким приложением
System and micro-architecture-level performance data Информация на уровне микроархитектуры – например, количество кеш-промахов (т.е. когда затребованные CPU данные в кеше не находились), количество времени, потраченное на выполнение конкретных инструкций и т.п. Не сообщает информацию о проблемах на уровне микроархитектуры Информация на уровне микроархитектуры

Поскольку я не системный администратор, я использую VTune лишь для анализа конкретных приложений, не интересуясь быстродействием системы. Если машина тормозит – надо удалить все лишнее и вставить новый процессор – вот и вся недолга. Поэтому весь дальнейший анализ будет исходить именно из этой предпосылки. Особо обратите внимание на графы таблицы, помеченные серым – это самые главные опции при анализе приложения.

Работа ведется в так называемом "проекте". Принцип такой же, как и Visual Studio. Сначала создается проект, а потом уж производятся определенные действия. Проект надо должным образом приготовить к использованию. Т.к. call graph осуществляет вставки в исполняемый код, то создатели VTune рекомендуют линковать программу с ключом /FIXED:NO, независимо от того, exe это файл, или dll.

FIXED:NO

Прочитайте спецификацию на PE-файл (MSDN). Здесь я обращу внимание всего на одно понятие - image base. Воспользуемся неизменным HIEW. Выберите любой файл в директории Windows (убедившись, что присутствует сигнатура PE00) и вызовите просмотр заголовка (F8):

...

Image base 00400000 ¦ Subsystem Console

...

Image base означает "базовый (иногда говорят "предпочтительный") адрес загрузки" - HIEW прибавляет число в поле Image base к обычным внутрифайловым смещениям для получения того адреса, по которому данная строка будет располагаться в оперативной памяти (этой операцией управляет строка в ini-файле ShowOffset = Local). IDA/Wdasm используют тот же механизм. Вы будете ставить bpx address в Soft-Ice именно по этим адресам. Однако значение этого поля не исчерпывается прибавлением к внутрифайловым смещениям определенного значения (для Win-EXE-PE - это 0х400000 по умолчанию, для DLL-PE - 0х10000000) - как Вы знаете, файл загружается в собственное адресное пространство размером в 2Gb и начальный адрес загрузки определяется image base (но НЕ ниже 0х400000). Relocations (fixups) под Windows никуда не исчезли, они были, есть и будут. А теперь представьте, что два файла, принадлежащие одному процессу, имеют один и тот же базовый адрес загрузки (допустим, приложение имеет 2 dll, и обе они имеют один и тот же адрес по умолчанию). Что же произойдет в этом случае? Возникнет коллизия и WinLoader будет вынужден производить перерасчет адресов, что неизбежно приведет к увеличению времени на загрузку. Перерасчет адресов определенных инструкций осуществляется с помощью информации из секции .reloc. Секция в норме не присутствует в exe-файлах, т.к. в основном, базовый адрес загрузки для них является и единственно возможным (dll же, как правило, очень редко когда бывают загружены по базовому адресу, поэтому практически во всех dll есть reloc-секция). Если VTune требует переопределения базового адреса загрузки, то это означает, что программа будет перемещена в памяти, а без секции .reloc это невозможно. Подробнее – статьи Мэтта Питрека на msdn или его собственном сайте - http://www.wheaty.net/.

Опция /fixed:no добавляется в опциях проекта Properties -> Linker -> Command line -> Additional options. В версиях Visual Studio до .NET существовала опция линкера /PROFILE. Теперь /fixed:no стала ее заменой.

Хотя говорить об исключении профилировщика из Visual Studio .NET все же не стоит. С VS.NET поставляется приложение ACT (Application Center Test), позволяющее оценивать производительность Web-страниц на основе VBScript. Лично я не могу сказать что-то хорошее об этом языке (уж лучше JavaScript, по многим причинам, например, по соображениям кроссплатформенности), поэтому ACT здесь не рассматривается. У MS также существует профилировщик под SQL-сервер (поставляется вместе с SQL Server) и анализатор производительности под IIS для ASP-страниц.

Также VTune требуется база данных о символах программы – pdb-файл. pdb/dbg/map-файлы используются для символьной отладки программы.

Символьная отладка

Существуют так называемые СИМВОЛЬНЫЕ отладчики. Они позволяют видеть инструкции, подобные call 1234567, как, например, call _time! SIce является как раз таким отладчиком. Что нужно отладчику для символьной отладки (т.е., такой отладки, когда мы видим осмысленные имена функций, а, если есть родной файл - исходник, то и строки исходного текста)? Ответ – файл с отладочной информацией. На данный момент времени существует минимум три MS-формата таких файлов (Borland, Watcom и т.п., скорее всего, используют собственные разработки)

map-файл – простой текстовый файл, с документированным и абсолютно понятным форматом. Сперва идет адрес строки, а потом имя функции. Немного устарел, однако по-прежнему генерируется Visual Studio (о конкурентах не знаю). Существуют утилиты для перевода этого файла в формат, понятный отладчикам или дизассемблерам, например, такой как .sym.

dbg-файл – файл, имеющий внутренний формат (уже не текстовый). Формат полностью документирован Microsoft, однако Visual Studio непосредственно в отдельный файл его генерировать не умеет (не хочет?), однако ей можно приказать запихнуть эту отладочную информацию непосредственно в исполняемый файл, откуда ее можно потом извлечь, с помощью утилиты rebase. Полученный dbg-файл понимает IDA, его можно перевести в map-формат и т.п.

pdb-файл – самая новая разновидность файла с отладочной информаций. Формат полностью недокументирован. Недавно Microsoft выпустила DIA SDK, входящий в состав Visual Studio .NET (..\Visual Studio SDKs\DIA SDK), где описывается интерфейс взаимодействия с pdb-файлом (до этого приходилось использовать dbghelp.dll). Также много полезной информации по данному вопросу можно найти в последних статьях Джона Роббинса и Мэтта Питрека. Также очень полезной является книга Свена Шрайбера "Недокументированные возможности Windows 2000", где этот формат неплохо освещен. К сожалению, даже IDA 4.30 не понимает последние версии pdb-формата, говоря что-то вроде "Unknown codeviewinformation format: RSDS", так что имеет смысл разобраться с DIASDKи, кто знает, может написать свой плагин к IDA?

Не помешает, кстати, повнимательнее присмотреться к содержимому каталогов \Util16 из дистрибутива Soft-Ice. Может, чего интересного углядите.

Установки эти в VS.NET задаются в свойствах линкера. Не забудьте это сделать!

Подопытным кроликом у нас будет мой собственный фильтр текстовых строк для бинарников – filter.c. Полный текст фильтра прилагается. Здесь я буду рассматривать только клочки исходного кода.

Уже говорилось, что можно обойтись и без профилировщика. Вот, например, так:

#include...;

startTime = clock ( );
 ...;   //функция, время выполнения которой необходимо знать
endTime = clock ( );

printf ("\n\nProgram completed.\n\n");
printf ("Execution time in seconds: %f\n\n",
(double) (endTime - startTime) /CLOCKS_PER_SEC);

Использование этого метода на больших проектах превращается в головную боль. Целесообразнее использовать профилировщик.

Итак, компилируем и линкуем filter.c (помните о ключе /fixed:no и pdb-файлах). VTune встраивается в VC.NET, поэтому можно "позвать" его и оттуда, но лучше работать отдельно – приложение ресурсоемкое.

Общая философия работы проста – выделить функции приложения, занимающие больше всего процессорного времени и попытаться их как-то оптимизировать. В этой статье будет рассказываться только о нахождении таковых. Оптимизация – тема, требующая отдельной статьи, а может, и книги.

Для того, чтобы проанализировать быстродействие программы, будем использовать опции "Sampling" и "Call graph". При запуске VTune предложит выбрать нужный тип проекта – см. рисунок. Я обычно выбираю более универсальное решение, которое позволит собрать максимум информации о программе – Quick Performance Analysis Wizard. Учтите, что процесс этот очень ресурсоемкий, профилировщик сильно тормозит, поэтому нужна сильная машина, которая сможет это вытянуть.

{картинка поскипана}

Выдается следующее диалоговое окошко, в котором предлагается указать задать аргументы командной строки (если таковые имеются). Мой filter – простой консольник и аргументы командной строки в нем активно используются. Задаем, например, -a 4 C:\WINNT\notepad.exe – это выведет все ASCII-строки, содержащиеся в notepad.exe длиной не менее 4-х символов на экран. Терпеливо подождем, пока выполнятся многочисленные прогоны программы для сбора всех необходимых данных. Наконец-то!

{картинка поскипана}

Заметьте, что метод Sampling выводит информацию обо ВСЕХ процессах, активных в данный момент времени на данной машине. В списке всех процессов надо найти подопытную программу и только тогда можно будет получить отчет о времени выполнения различных ее функций. Отчет может быть представлен в виде графика или таблицы. График нагляднее, таблица полезнее. Думаю, сначала стоит внимательно изучить график, а затем уже переходить к таблице. Для этого разберемся, что показано на графике и каков смысл легенды.

Создатели VTune рекомендуют начинать с показателя CPI - cycles per instruction. Этот показатель отражает количество тактов проца, потраченное на обработку инструкций конкретной функции. Ассемблерщики знают, что, скажем, операции умножения, деления, операции, использующие сложные модели адресации памяти, выполняются дольше, условные переходы – вообще напасть для современных процессоров, хорошо оптимизированный код старается обходиться без них и т.д., и т.п. Так вот, диапазон значений по CPI в Vtune составляет

OK Плохо
0.75 4

Если CPI высок, это означает, что код надо переписать с использованием оптимизирующих техник, например, заменить возведение в степень двойки битовым сдвигом, использовать хитрые мат-алгоритмы для замены умножения и деления и т.п. В принципе, хороший компилятор (т.е. компилятор от MS или от Intel) способен сделать это все за вас (только активируйте все опции оптимизации для Release-конфигурации), однако есть пара интересных приемов, облегчающих жизнь компилятора.

Оптимизация

Умение оптимально писать код приходит не сразу. Это долгий процесс, нет, вероятно, это бесконечный процесс. Я лишь дам пару-тройку отправных точек. У Intel на сайте есть великолепнейшие пособия по оптимизации, не поленитесь, загляните – это пособия перекрывают многие "самопальные" рукописи. Есть сайт x86.org – там тоже много чего интересного лежит. Ну и, наконец, just to give you a flavour, нечто вроде немного поправленной таблички от Paul Hsieh – одного из очень неплохих специалистов по оптимизации программ.

Так делать НЕ надо

А вот так – надо. Только комментируйте такие места.

x = y % 32

x = y & 31

x = y * 8

x = y << 3

if (x>=0 && x<8 && y>=0 && y<8)

{

...

}

if ( (unsigned) (x|y)) <8)

{

...

}

char array[z];

for (i=0; i<z; i++)

{

array[i]=2;

}

char array[z];

char * p = &array[0];

for (i=0; i<z; i++)

{

*p++=2;

}

Используйте алгоритм быстрой сортировки

Используйте сортировку объединением или лучевую сортировку

(www.nr.com)

Кодить, кодить, кодить

Иногда перед этим не мешает подумать.



Ну и, наконец, если показатели нормальные, а время выполнения неудовлетворительно все равно, то есть смысл подумать об алгоритме. Может быть, как-нибудь переписать программу. Вообще, по правилам хорошего тона, программа должна быть основательно пересмотрена минимум дважды. Есть замечательная книга Алена Голуба "Правила программирования на C/C++". Она доступна для скачивания где-то в русской зоне Интернета. Очень рекомендую. Загляните так же на сайт http://www.kalinin.ru.

Забыл сказать, что VTune поддерживает и мультипроцессорность. Черта полезная. Современные ОС вроде Win2000 полностью поддерживают мультипроцессорность и от программ, скомпилированных с ключом /MT или /MD на мультипроцессорных платформах можно ждать сильного повышения производительности. Говоря обобщенно, дабы не вступать в долгие дискуссии (тема-то действительно спорная), мультизадачные приложения на единственном процессоре выполняются медленнее, поэтому для простых приложений на единственном процессоре мультипоточность ни к чему. Однако, с точки зрения пользователя, если приложение выполняет какую-нибудь ресурсоемкую операцию, то есть смысл вынести ее в отдельный поток, чтобы имелась возможность прекратить этот поток, если надоест. Скажем, если бы проверка грамматики в Word выполнялась бы не фоново, то компакт-диск с этим продуктом расколотили бы на голове у создателя.

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

Теперь посмотрим на метод "Call graph". Еще раз напомню, Sampling собирает данные по всем активным процессам. Call graph – только по конкретному подопытному приложению. Здесь большое значение играет цвет функции.

{картинка поскипана}

Итак, функции представлены в виде дерева. Оранжевый цвет функции говорит о том, что именно в ней проводится большая часть времени. Толщина стрелки, соединяющей функции и ее цвет тоже играют большую роль. О назначении цвета, думаю, догадаетесь сами, толщина стрелки означает время, проведенное в вызывающей функции. Также на графике показывается какая функция "позвала" остальные, время, затраченное на выполнение конкретной функции и т.п. Здесь тоже много чего можно узнать. Здесь мне представляются важными следующие показатели:

calls

Число вызовов дочерней функции родительской функцией

self time

Время (microseconds) проведенное внутри функции. Не включает в себя время, затраченное на вызовы других функций.

total time

Время (microseconds) проведенное внутри функции. Включает в себя и время проведенное в дочерних функциях, т.е. ПОЛНОЕ время всей функции и всех порожденных ею функциях.

callers

Число "родителей", вызвавших данную функцию.

callees

Число дочерних функций, которые были вызваны данной функцией.

У VTune есть еще одна очень приятная возможность, начисто отсутствующая у конкурентов - статический анализ ассемблерного кода (File -> Open Static Module Viewer). Здесь возможности вообще изумительные – можно посмотреть время выполнения конкретных инструкций на конкретном процессоре (разумеется, Intel-линейки), сразу получить совет как можно устранить ту или иную задержку, узнать, что значит тот или иной термин, словом прелесть. Ассемблерщики оценят! Справедливости ради замечу, что у AMD тоже есть нечто подобное – CodeAnalyst, версия 1.2 на момент написания статьи. С ним я не игрался, т.к. у меня Intel’овский процессор. Однако профилировщик свободно доступен для закачки с сайта AMD, так что ноу проблем!

Однако обзор VTune был бы неполон, если бы мы не затронули тему профилировки интернет-приложений. К таковым здесь будем относить ASP-/Java-страницы, ISAPI-dll, а также CGI-программы, написанные на компилируемых (а не интерпретируемых, как Perl – хотя, как известно, сейчас уже можно получать Perl-exe-файлы) языках.

CGI-приложения на компилируемых языках

Сейчас стало модным писать CGI приложения на Perl. Спору нет, язык действительно очень приятный. Уже существует достаточная база утилит (Visual Perl, например, или OptiPerl), прекрасная документация, множество примеров и пояснений. Однако что делать тем, кто в силу самых различных причин вынужден писать CGI-программы с использованием C/C++? Отладка и профилировка таких программ – дело ох, какое сложное. Здесь существует несколько трюков, которые позволяют облегчить жизнь разработчику. Лично я не люблю вставку printf в код в отладочных целях – не та степень интерактивности, не та степень контроля над программой, поэтому:

Компилирование приложения со вставкой __asm int 3; ПОСЛЕ получения программой CGI QUERY_STRING. Как известно, int 3 вызывает всплытие отладчика – таким образом, мы можем продолжать отладку как ни в чем не бывало.

Вставка в код sleep-функций (например, функцию Win-API Sleep(DWORD)). Поток на какое-то время "засыпает" и за это время нам надо сделать "Attach to process" в отладчике. Потом – по накатанной дорожке.

Нормально спроектированные отладчики обязаны допускать, чтобы программа имела возможность получать на вход аргументы командной строки. Поэтому просто вставьте QUERY_STRING БЕЗ знака "?" в command-line arguments опцию VTune или Visual Studio Debugger и наслаждайтесь. При необходимости можно добавить переменные окружения CONTENT_TYPE, CONTENT_LENGHT и т.п (Win + break -> Advanced -> Environment Variables)

Работоспособность первого метода под IIS у меня вызывает сильные сомнения. Да, действительно, исключительная ситуация происходит, однако отладчик Visual Studio (под Soft-Ice я не пробовал) не активируется. Почти наверняка это происходит из-за модели работы IIS – см. статью Skonnard "Understanding the IIS Architecture" на MSDN.

Да, и еще одна вещь, омрачающая жизнь разработчиков CGI-приложений под Win32. Для каждого http-запроса Windows создает новый процесс. А это есть очень нехорошо, т.к. создание процесса под Windows – вещь сложная, долгая и ресурсоемкая. Здесь отправляю к статье Osterlund "What Goes On Inside Windows 2000: Solving the Mysteries of the Loader". Можно, конечно, слегка снизить время загрузки, использовав специальные утилиты Мэтта Питрека, или, скажем, "привязать" exe-файл к конкретной ОС утилитой bind, входящей в состав Visual Studio (bind –u filename), но это не изменит принцип. Остается только одна альтернатива, если действительно важна производительность - использовать Fast-CGI (www.fastcgi.com).

Для этого Intel написала еще одно приложение – Intel Enterprise Analyzer for Web Applications. Это "нашлепка" на Perfomance Analyzer, превращающее VTune в одну здоровенную среду профилировки. Enterprise Analyzer ОЧЕНЬ ресурсоемок, требует установки JDK, запускает MySQL и даже Tomcat. Все это дело тормозит просто жутко и весьма сложно в настройке. Поэтому для профилировки таких решений (managed code) рекомендуется использовать Java-решения от NuMega или продукты от Rational с активированной опцией "managed code". Также фирма Microsoft (как всегда молодцы) предлагает свои решения для профилировки ASP-программ. Процесс установки и отладки достаточно детально описан в книге "Perfomance testing Microsoft .NET Web applications".

В завершение всего хочется сказать, что фирма Intel создала классный интерактивный курс по Vtune. Он доступен на Web-сайте Intel, а также поставляется вместе с VTune. После того, как прочтете сие, поиграйтесь с этим курсом и можете смело считать себя специалистом по профилировщикам.

Пару слов о недостатках. VTune – продукт глючный, часто бывают заскоки. Иногда встречаются непонятные проколы, вроде требования предъявить .dsp-файл, в то время как VS.NET (а у VTune заявлена совместимость с VS.NET) уже НЕ использует .dsp/.dsw файлы, заменяя их своими экзотическими .sln/.suo/.ncb/.vcproj-файлами. Более подробно об этом см. "Использование VC.NET" Кейт Грегори. Поэтому имеет смысл часто проверять ftp-сайт Intel на новые версии программы.

Ну вот, вроде все. Теперь со спокойной совестью можно переходить к PurifyPlus.

Rational Quantify

Что ж, продукт быстрый, достаточно легкий в использовании. Тесно интегрирован с VS.NET. По возможностям явно проигрывает VTune, но меня лично приводит в ужас мысль о профилировании действительно ресурсоемкой программы с помощью VTune – это будет слишком медленно и глючно. За время использования VTune у меня глючил очень много раз, Quantify – ни одного. Стабильная и быстрая утилита.

Итак, Quantify предоставляет информацию только в виде Call graph. Иными словами мы можем видеть то же дерево выполнения программы с наиболее ресурсоемкими функциями (см. рисунок ниже). Никакой предварительной подготовки программы не требуется, правда и к исходному коду доступа нет, не говоря уж о встроенном дизассемблере и т.п. Нет возможности получить советы по оптимизации как в VTune, нет встроенной помощи, словом, отсутствуют все те вещи, что мы так ценим в справочной системе. Однако фирма Rational создала пару достаточно неплохих pdf-файлов с описанием пакетов. Требуется регистрация, однако что стоит зарегистрироваться?

{картинка поскипана}

Принцип тот же, что и у VTune. Ничего сложного. Единственное, что может немного смутить, это графа F+D time в таблице. F+D означает function+descendents, т.е. родительская функция + дочерние. Иными словами, это эквивалент total time в VTune. Во всем остальном используется схожая терминология.

Quantify способен производить профилировку Java/VB/C/C++/C#/J#. По-моему, список весьма неплох. Работать можно.

За сим мы распрощаемся с Quantify.

NuMega TrueTime

Забавно отметить, что сама Microsoft в своей книге "Perfomance testing Microsoft .NET Web applications" рекомендует использовать DevPartner 7.0 для тестирования и профилировки managed-code. Честно говоря, DevPartner производит не самое лучшее впечатление. Те части DevPartner, над которыми работал Мэтт Питрек – BoundsChecker – производят очень приятное впечатление. Все работает быстро, красиво, четко. Хотя, отдельные глюки все же присутствуют. Например, несмотря на великолепную интеграцию DevPartner с Visual Studio, данные об исходном файле не появляются все равно. И это происходит даже тогда, когда BoundsChecker (ныне Error Detection) вызывается прямо из активной в данный момент solution-сессии. О TrueTime и TrueCoverage я уж из вежливости умолчу. Если, в случае Rational, для того, чтобы разобраться с продуктом и получить осмысленные результаты у меня ушло несколько минут, то, пробившись пару часов над TrueTime и не получив ничего, кроме времени выполнения Win-API-функций (интересно, а зачем оно мне?), я сдался. Видно, что создатели Purify очень неплохо продумали свои программы, в то время как NuMega создала монстра, которому очень сложно указать, что ты от него, собственно, хочешь.

Итак, если есть желание по-прежнему использовать продукты NuMega, то хорошей идеей будет использование BoundsChecker. Продукт на высоте. Я не проверял его работоспособность с C#/VB, однако, думаю, и здесь он не подведет. TrueTime/TrueCoverage лучше не пользуйтесь – берите продукты от Rational.

Подведение итогов

Увы, в одной короткой статье нельзя осветить все. К сожалению, лишь очень бегло освещены профилировщики managed-code, совершенно не уделено внимание Java-байт-кодам, а также решениям NuMega в этой области (JCheck и т.п.). Однако надеюсь, путеводная нить у вас появилась.

Да, чуть не забыл. Если возникают какие-то проблемы при инсталляции программ – то, возможно, ответ вы найдете на wasm.ru в статье "Обзор некоторых защит на основе FlexLm SDK".

Успехов в работе.

Благодарности

Автор выражает свою искреннюю признательность vkim за ценные технические советы и Андрею Бабию за разбор программных алгоритмов. Также моя признательность редакторам Alexela и CyberManiac.

VOLODYA [HI-TECH]

Заключение

А, не, настоящему дзенскому ассемблерщику это все не надо. Это какая программа должна быть на 100К и больше?

Не, не используйте профилировщики, и не ломайте FLEXlm - оно того не стоит! ;)

(И если Intel ... , то мы этого не писали :))

И да пребудет с вами сила, братья по безумию!