Техника и философия хакерских атак
Продолжение...
(Впрочем, не факт, что версия 6.03 его найдет, но для простоты будем считать, что hiew к моменту чтения этого опуса уже научился поддерживать и такую адресацию). Ясно, что 0x01 - это переменная размером в байт. Отмечаем это (карандашом в блокноте, т.к. hiew все еще не поддерживает комментариев) и переходим к ячейке 0x2. Вновь нажимаем f6 и изучаем код, манипулирующий с этим адресом. Пусть он выглядит следующим образом:
00000000: C3 retn 00000001: 00E8 add al,ch 00000003: 61 popa 00000004: 06 push es 00000005: A00100 mov al,[00001] 00000008: E8F7FF call 000000002 -------- (1) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Чтобы привести код в удобочитаемое состояние, достаточно перейти по адресу 0x2, для чего можно нажать '1' (переход по ссылке) или даже <F5> "02".
00000002: E86106 call 000000666 00000005: A00100 mov al,[00001] 00000008: E8F7FF call 000000002 -------- (1)
Конечно, пример является надуманным, но тем не менее технику работы с примитивными дизассемблерами он иллюстрирует неплохо. Замечу, что "примитивный дизассемблер" вовсе не оскорбление, а соответсвующий класс программ, которые выполняют только декодирование инструкций, перекладывая все остальное на плечи пользователя.
Если приведенный пример действительно является com-файлом, то скорее всего hiew не сможет правильно найти ссылки, потому что неправильно вычислит адреса. Это неудивительно, если вспомнить, что com-файлы загружаются в память со смещения 0x100 в текущем сегменте. Пусть оригинальный файл выглядел так:
0x100: A10401 mov ax,[00104h] -¬ 0x103: C3 retn ¦ 0x104: 1234 DB 3412h <----
Hiew же дизассемблирует его как:
00000000: A10401 mov ax,[00104] --¬ 00000003: C3 retn ¦ 00000004: 1234 adc dh,[si] ¦ ........ ¦ 00000104: xxxx <----
Разница в том, что ссылка во втором случае указывает "в космос", но никак на переменную 0x4. Это можно исправить, указав hiew-у вручную начальное смещения файла. Выше мы уже сталкивались с этим при анализе PE-файлов. В терминологии SEN-а это называется базированием и может быть задано в любой момент анализа (а не только до загрузки, как во многих других дизассемблерах).
Нажимаем Ctrl-F5 и вводим число 0x100. Теперь код выглядит следующим образом:
00000100: A10401 mov ax,[00104] -----¬ 00000103: C3 retn ¦ 00000104: 1234 adc dh,[si] <----- 00000106: 0100 add [bx][si],ax 00000108: E8F7FF call 000000102 -------- (1)
И все ссылки при этом работают правильно. Заметим, что базирование никак не влияет на вызовы процедур и переходы, поскольку в архитектуре процессоров intel они относительные.
Рассмотрим более сложный пример, в котором код и данные размещены в разных сегментах. hiew ничего не знает о последних и поэтому неверно вычисляет все ссылки. Рассмотрим, например, любую программу на Паскале. Загрузим ее в hiew и перейдем в точку входа (F8, F4, но об этом чуть позже).
000000CE: 2E9A00007100 call 00071:00000 -------- (1) 000000D4: 9A0D000F00 call 0000F:0000D -------- (2) 000000D9: 55 push bp
Поскольку после загрузки файла регистр DS указывает на сегмент PSP, то приложение должно настроить его самостоятельно. В программах, компилированных turbo-pascal, это происходит в модуле SYSTEM (самый первый вызов). Входим в него нажатием '1' и изучаем следующий код:
000007A0: BA0D01 mov dx,0010D ;"" 000007A3: 8EDA mov ds,dx
Что такое 0x10D? Это смещение в памяти, которое отличается от смещения в файле только длиной заголовка. На деле, в отличие от PE файлов, загрузчик DOS-EXE пропускает заголовок, тем самым экономя немного памяти. Длину заголовка узнать несложно (значение поля Paragraphs in header нужно умножить на 0x10). Аналогично поступим и со значением DS. Итого 0x10D0+ 0x90 == 0x1160 смещение сегмента данных в файле.
Смотрим на код, стоящий ниже:
000007A0: BA0D01 mov dx,0010D ;"" 000007A3: 8EDA mov ds,dx 000007A5: 8C063800 mov [00038],es
Чтобы узнать, на какую ячейку ссылается [0x038], надо к последней добавить 0x1160. Не правда ли, утомительно? Было бы гораздо лучше, если бы hiew выполнял такой пересчет автоматически. Попробуем для этой цели использовать базирование. Установим курсор на адрес 0x1160 и нажмем Alt-F5. Теперь надо добиться, чтобы текущее смещение равнялось нулю. Очевидно, для этого необходимо задать базирование, равное по модулю, но противоположное по знаку. Т.е. '-0x1160'. Однако hiew поддерживает и относительные смещения, отмеченные префиксом '*'. Это действительно удобно и избавляет от лишних математических вычислений. При этом Ctrl-F5,Ctrl-F5 действует аналогично '*0'.
Мы добились того, что смещения в сегменте данных начинаются с нуля, но... Маленькое, но грустное "но". Взгляните на сегмент кода:
FFFFEF6F: 2E9A00007100 call 00071:00000 -------- (2) FFFFEF75: 9A0D000F00 call 0000F:0000D -------- (3) FFFFEF7A: 55 push bp FFFFEF7B: 89E5 mov bp,sp FFFFEF7D: 31C0 xor ax,ax
Печально, не правда ли? Впрочем, это не так актуально, поскольку в кодовом сегменте большинство смещений относительные и будут нормально функционировать независимо от базирования.
Так-то оно так, но Турбо-Паскаль имеет странную привычку располагать некоторые данные в кодовом сегменте. Взгляните:
FFFFEF31: 10 82 A2 A5-A4 A8 E2 A5-20 AF A0 70-AE AB EC 3A Введите паpоль: FFFFEF41: 20 04 59 75-4B 69 12 8F-A0 70 AE AB-EC 20 AD A5 YuKiПаpоль не
И вот обращение к этой строке:
FFFFEF88: BF6602 mov di,00266 ;"f" FFFFEF8B: 1E push ds FFFFEF8C: 57 push di FFFFEF8D: BF0000 mov di,00000 ;" " ^^^^^^^^^^^^^^^^^^ FFFFEF90: 0E push cs FFFFEF91: 57 push di FFFFEF92: 31C0 xor ax,ax FFFFEF94: 50 push ax FFFFEF95: 9A70067100 call 00071:00670 --------(5)
Кто бы мог подумать, что в кодовом сегменте эта строка располагается с нулевым смещением! Однако это действительно так, в чем можно убедиться: F5,90\Ctrl-F5,Ctrl-F5:
00000000: 10 82 A2 A5-A4 A8 E2 A5-20 AF A0 70-AE AB EC 3A Введите паpоль: 00000010: 20 04 59 75-4B 69 12 8F-A0 70 AE AB-EC 20 AD A5 YuKiПаpоль не
Но теперь "уползли" все смещения в сегменте данных. И необходимо при первом же обращении к нему вернуть все на место. Как-то неаккуратно получается. К тому же маловероятно, чтобы это было как-то исправлено в последующих версиях. Автор hiew-а изначально рассчитывал на поддержку только одного сегмента. Теперь же, когда DOS файлы уходят в прошлое, это ограничение выгядит уже не таким существенным.
Поиск
К полной луне призывы... Шаи-Хулуд поднялся, чтобы ее увидеть; Красная ночь, сумеречное небо, Кровавая смерть - ты погиб. Возносим молитвы луне: она круглая... Счастье всегда будет с нами, Потому, что мы нашли то, что искали, В стране с твердой землей.
Ф. Херберт. "Дюна".
HIEW обладает развитым средством поиска, не имеющим аналогов. С его помощью можно искать как целые команды, так и любые вхождения. В качестве маски используется символ '?', означающий любую последовательность символов, включая пустую. Иначе говоря, этот символ является полным аналогом dos-символа '*'.
Это позволяет искать любые команды и их сочетания в ассемблерском тексте. Мощность и возможности этого механизма не имеют равных ни среди отчественных, ни среди зарубежных утилит. Даже IDA не представляет такого сервиса.
SEN в очередной раз продемонстрировал свои способности и оставил всех конкурентов далеко позади. Заметим, что эта возможность впервые появилась еще в версии 4.0, когда подражатели находились еще в зачаточном состоянии.
Как известно, наипервейшая задача хакера - локализовать защитный механизм в сотнях килобайт исполняемого кода. Это не так просто, как может показаться на первый взгляд (при условии что полный анализ программы просто нереален ввиду огромных трудозатрат на дизассемблирование и изучение).
Поэтому взломщики используют различные хитрости, которые при всем их разнообразии сводятся к одному: поиску уникальной последовательности команд, которая предположительно присутствует в защитном механизме. Например, типичной будет конструкция :
if (!IsValidUser()) abort();
или
if (IsValidUser()) { ; // нормальное выполнение программы } else abort();
Достаточно много вариантов, не правда ли? Перибирать их вручную было бы крайне утомительно. Используем поиск по шаблону. Для этого в режиме дизассемблера два раза нажмем F7 и введем следующую последовательность:
г=[Forward /Full ]===================================================¬ ¦ ASCII: -------------------- ¦ ¦ ¦ ¦ Hex: ------------------------------------------------------------¦
г= Pentium(R) Pro Assembler =====================================¬ ¦ call ?;or ax,ax;j?-------------------------------------------- ¦ L================================================================-
Hiew найдет приблизительно следующий код:
.0001140C: 9A3E172114 call 001:0173E -------- (4) .00011411: 0BC0 or ax,ax .00011413: 7405 je .00001141A -------- (5)
Разумеется, возможно, что call 001:0173E на самом деле не имеет никакого отношения к защите (так скорее всего и будет), но тем не менее ожидается, что в программе не так уж много вышеприведенных комбинаций, и в любом случе это сужает поиск.
Однако для файла мегабайт в десять длиной такое заявление может вызвать лишь легкую улыбку. Возможно, hiew найдет несколько сотен подходящих вариантов: легче заплатить пару долларов и приобрести легальную версию, чем их все анализировать. Но не будем спешить (хотя хакерство не повод работать на ворованном программном обеспечении). Все, что нам нужно, - собрать доступную информацию о защите и правильно задать команду поиска. Допустим, мы знаем, что в случае неудачи защита выводит сообщение с известным смещением, предположим, что 0x406666. Тогда, быть может, нам поможет следующая комбинация:
г=[Forward /Full ]===================================================¬ ¦ ASCII: -------------------- ¦ ¦ ¦ ¦ Hex: ------------------------------------------------------------¦
г= Pentium(R) Pro Assembler =====================================¬ ¦ call ?;or eax,eax;j?;;? 66664000------------------------------ ¦ L================================================================-
При этом ';;' интерпретируется как любое множество произвольных команд, включая пустое. При этом шансы, что найденный фрагмент окажется именно тем защитным механизмом, который мы ищем, очень велик. Если же нет, то не отчаивайтесь, а попробуйте еще один раз.
Очень приятно, что hiew продолжает поиск не с текущей позиции курсора, а с места последнего найденного вхождения. Это дает нам некоторую мобильность в перемещении по файлу.
FindNext сбрасывается при перемещении курсора в начало файла по Ctrl-Home, (или в конец по Ctrl-End). Так же разумеется FindFirst (F7) и непонятно откуда взявшееся Goto (F5). Последнее иногда вынуждает на неприятные "путешествия" по файлу "вручную" (кирпич на Page Down).
При этом искать можно как во всем файле (по умолчанию), так и в выделенном блоке. Конечно, это полезная возможность, позволяющая работать с фрагментом файла, например, с защитной процедурой.
К сожалению, в строке поиска нельзя задавать логические конструкции типа 'AND', 'OR' и другие. Между тем эта возможность очень полезна и в последнее время поддерживается многими популярными системами.
Манипуляции с блоками
"Соединение невежества и знания, соединение дикости и культуры - не начинается ли оно с того чувства достоинства, с которым мы относимся к своей смерти?"
Ф. Херберт "Дюна".
Выше мы затронули способность hiew-а работать с выделенными фрагментами текста (блоками). Рассмотрим теперь это подробнее. Для того чтобы выделить фрагмент, необходимо однократно нажать Gray-'*'. Теперь перемещения по файлу с помощью клавишей управления курсором будут приводить к выделению фрагмента и заливке его по умолчанию бордовым цветом. Повторное нажатие Gray-'*' прекращает выделение и активирут клавиши управления блоком.
Это Запись\Чтение блока в файл, а также заливка его некоторым значением. Рассмотрим окно записи:
г===================== Write block to file ======================¬ ¦ Block: 000001FF-000002EF length:000000F1/241 ¦ ¦ File: ------------------------------------------------------ ¦ ¦ Offset: ........ ( Hexadecimal ) ¦ ¦ Table: As Is г====== Select table =======¬ ¦ L=================¦ As Is ¦==================- ¦ Windows-1251 ¦ ¦ Koi-8 ¦ L===========================-
Приятной особенностью является возможность записи в файл с произвольным смещением. Это, дейстительно, насущная потребность любого кодокопателя. Скажем, вы решили заменить шрифты в файле или для какой-то цели дописать в его конец маленькую программу (скажем, вирус). При этом можно выбрать любую кодировку. Да, я не оговорился: "любую", и это следует понимать буквально вплоть до кодировки племен индейцев Северной Америки. Ниже описывается структура файла hiew.xlt, который позволяет это делать.
Замечательно, что все смещения и длина блока выражены шестнадцатиричными цифрами. Это попросту удобно и практично, поскольку большинству хакеров десятичная система нравится куда меньше.
То же самое наблюдается и в окне чтения блока. Взгляните:
г===================== Read block from file =====================¬ ¦ Block: 000001FF-000002EF length:000000F1/241 ¦ ¦ File: ------------------------------------------------------ ¦ ¦ Offset: ........ ( Hexadecimal ) ¦ ¦ Table: As Is ¦ L================================================================-
Загрузка блока - процесс, обратный записи. И на редкость бесполезный. За мою сознательную жизнь я эту возможность ни разу не использовал. Не то чтобы мой опыт был каким-либо важным показателем, но я действительно не могу придумать ситуацию, в которой эта возможность была бы необходима, поэтому не будем на ней останавливаться, а перейдем сразу к "заливке":
г====================================================================¬ ¦ ASCII: -------------------- ¦ ¦ ¦ ¦ Hex: ------------------------------------------------------------¦ L====================================================================-
Это окно вызывается по Alt-F3. Никаких проблем управление им вызвать не должно. Заметим только, что все операции с блоком являются необратимыми и лучше семь раз подумать, чем нажать на кнопку. Резервной копии ведь за вас никто не сделает.
Поддержка LE/PE/NE/LX/NLM форматов
Понятие прогресса служит защитным механизмом, отгораживающим нас от ужасов будущего.
Ф.Херберт. "Дюна".
Вообще-то шестнадцатиричный редактор идеологически должен быть платформенно-независимым. Введением в него дизассемблера SEN нарушил эту традицию, привязав его к линейке 80x86 процессоров фирмы Intel. Однако в этих пределах hiew все же оставался кросс-платформенным, одинаково хорошо работая со всеми форматами файлов всех существующих операционных систем. Точнее даже, никак не работая, поскольку первые версии не отличали структурированных файлов от простых бинарных.
Десяток лет назад особо поддерживать было еще нечего. В то время господствовали com и exe файлы. Первые вообще являлись бинарным образом, а вторые имели крайне простую структуру, которая относилась скорее к загрузке файла в память и была прозрачна для работы с последним "вживую". Я имею в виду таблицу перемещаемых элементов.
С другой стороны, сегментами кода и данных управляло само приложение непосредственно. Файл все еще оставался простым образом памяти. Это видно хотя бы по тому, что половина полей заголовка выражается в секторах диска (когда-то это упрощало загрузку, но сегодня может вызвать разве что легкую улыбку).
Неудивительно, что вся поддержка DOS-EXE свелась к простому отображению заголовка в удобочитаемом виде.
г========[ MZ-header ]=========¬ ¦ Signature 4D5A ¦ ¦ Bytes on last page 01C0 ¦ ¦ Pages in file 0009 ¦ ¦ Relocations 001D ¦ ¦ Paragraphs in header 0009 ¦ ¦ Minimum memory 0436 ¦ ¦ Maximum memory A436 ¦ ¦ SS:SP 0149:4000 ¦ ¦ Checksum 0000 ¦ ¦ CS:IP 0000:003F ¦ ¦ Relocation table adress 001C ¦ ¦ Overlay number 0000 ¦ ¦ Overlay length 00000B1F ¦ ¦ NewExe offset 00000000 ¦ ¦ >Entry point 000000CF ¦ L==============================-
Пояснять значения полей здесь нет смысла - это гораздо лучше расписано в руководстве программиста для MS-DOS. Ознакомившись с последним, можно попытаться отредактировать поля, когда в этом возникнет необходимость. hiew позволяет сделать это с комфортом, избавляя от некоторых рутиных вычислений. Так, например, F2 автоматически вычисляет значения полей Pages in file и Bytes on last page (это бывает необходимо при манипуляции с размером файла, чаще всего "отрезании" того мусора, который так любят оставлять в конце некоторые распаковщики). При этом hiew никак не учитывает значения поля Overlay length, что может привести к некоторым проблемам и является досадным багом, который автор ухитрился до сих пор не исправить (вероятнее всего потому, что никто из пользователей hiew этого и не заметил; а если кто и заметил, так не имел Интернета, чтобы ему об этом сообщить).
Другим приятным сервисом является возможность быстрого перехода в точку входа (F5) и в начало кодового сегмента (F4). Заметим на всякий случай, что это не взаимозаменяемые понятия и редкие exe-файлы начинают выполнение с нуля.
Клавиша F2 поможет быстро перейти в начало оверлея. Это, пожалуй, все, что можно сказать про поддержку old-dos форматов. Предлагаемый набор сервиса типичен для современных hex-редакторов и скопирован практически всеми конкурентами.
Диаметрально противоположно обстоит дело с поддержкой PE-файлов. Выше мы уже неоднократно сталкивались с этим, а сейчас рассмотрим подробнее:
г===========================[ PE-header ]============================¬ ¦ Count of sections 4 ¦ Machine(014C) intel386 ¦ ¦ Symbol table 00000000[00000000] ¦ TimeStamp 36CC1C56 ¦ ¦ Size of optional header 00E0 ¦ Magic optional header 010B ¦ ¦ Linker version 6.00 ¦ OS version 4.00 ¦ ¦ Image version 0.00 ¦ Subsystem version 4.00 ¦ ¦ Entrypoint RVA 00001390 ¦ Size of code 00001000 ¦ ¦ Size of init data 00003000 ¦ Size of uninit data 00000000 ¦ ¦ Size of image 00005000 ¦ Size of headers 00001000 ¦ ¦ Base of code 00001000 ¦ Base of data 00002000 ¦ ¦ Image base 00400000 ¦ Subsystem(0003) Windows char ¦ ¦ Section alignment 00001000 ¦ File alignment 00001000 ¦ ¦ Stack 00100000/00001000 ¦ Heap 00100000/00001000 ¦ ¦ Checksum 00000000 ¦ Number of directories 16 ¦ L====================================================================-
Заметим, насколько усложнился заголовок. Самое интересное, что он не особенно хорошо документирован фиромой MicroSoft (что и понятно: это все же внутренняя структура операционной системы и чем меньше приложения о ней будут знать, тем лучше).
Когда же сведений, описываемых документацией, начинает не хватать, хакеры обычно обращаются к winnt.h - настоящей сокровищнице, где все структуры хоть и бедно комментированы, но все же хоть как-то расписаны.
Неприятным моментом будет отсутствие возможности редактирования заголовка непосредственно из этого экрана. То ли SEN посчитал, что среднему хакеру это не нужно, то ли просто поленился (кто знает...), - но если дело до этого дойдет, то придется, вооружившись SDK и MSDN, орудовать в hex-редакторе вручную. Или писать свою собствнную утилиту, поскольку такая необходимость возникает достаточно часто.
Аналогично поступил автор и с показом флагов (F2). Смотреть можно сколько угодно, а редактировать - нет. Печально.
г===================================================================¬ ¦ Characteristics 010F ¦ DLL flag 0000 ¦ ¦ 0: Relocations stripped :Yes ¦ 0: Process initialization :No ¦ ¦ 1: Executable image :Yes ¦ 1: Process termination :No ¦ ¦ 2: Line numbers stripped :Yes ¦ 2: Thread initialization :No ¦ ¦ 3: Local symbols stripped :Yes ¦ 3: Thread termination :No ¦ ¦ 4: reserved :No ¦ ¦ ¦ 5: reserved :No ¦ Loader flag 00000000 ¦ ¦ 6: 16 bit machine :No ¦ 0: Break on load :No ¦ ¦ 7: Bytes reversed lo :No ¦ 1: Debug on load :No ¦ ¦ 8: 32 bit machine :Yes ¦ ¦ ¦ 9: Debug stripped :No ¦ ¦ ¦ 10: Patch :No ¦ ¦ ¦ 11: reserved :No ¦ ¦ ¦ 12: System file :No ¦ ¦ ¦ 13: File is DLL :No ¦ ¦ ¦ 14: reserved :No ¦ ¦ ¦ 15: Bytes reversed hi :No ¦ ¦ L===================================================================-
На этом фоне довольно качественной выглядит навигация по секциям PE-файла (F6) или, в терминологии hiew, по таблице объектов. При этом выдается дополнительная информация о каждой секции. При этом, к сожалению, вновь отсутствует возможность редактирования и все флаги показаны не в бинарном, а в шестнадцатиричном виде. Последнее вынуждает выполнять все расчеты в уме. Действительно, какие атрибуты имеет секция .text? 0х60000020 можно разложить на 0x20+0x40000000+0x20000000. (Если читатель не понял, откуда взялись эти цифры, то пусть переведет 0x60000020 в двоичное представление, оно будет таким: 00000110000000000000000000100000b. Теперь уже нетрудно вычислить, что 0100000b == 0x20; 010000000000000000000000000b == 0x20000000 и 0100000000000000000000000000b == 0х40000000. Достаточно тривиальные вычисления, которые любой хакер производит в уме даже без помощи калькулятора). ~~15 Получается, что секция .text имеет следующие атрибуты - Code | Can be discarded | Not cachable. Было бы куда нагляднее, конечно, представить всю эту информацию сразу в удобочитаемом виде. Но, автор признается, что не любит плодить монстров, да и не так уже трудно все эти вычисления выполнить в уме, который стремительно усыхает в наш бурный век думающих машин и автоматических калькуляторов.
Другой вопрос, что утомительно было бы держать в уме все значения флагов (кому приятно запоминать эту чисто справочную информацию?). К счастью, автор это предусмотрел и включил в контекстную помощь соответствующий пункт, который можно увидеть на рисунке, приведенном ниже:
г===========================[ PE-header ]============================¬ ¦ ¦ ¦ г=Number Name VirtSize RVA PhysSize Offset Flag===¬ ¦ ¦ ¦ 1 .text 000004FA 00001000 00001000 00001000 60000020 ¦ ¦ ¦ ¦ 2 .rdata 0000051A 00002000 00001000 00002000 40000040 ¦ ¦ ¦ ¦ 3 .data 00000168 00003000 00001000 00003000 C0000040 ¦ ¦ ¦ ¦ 4 .rsrc 000000F0 00004000 00001000 00004000 40000040 ¦ ¦ ¦ ¦ г========================[PE Object flags]=========================¬ L ¦ ¦ -------------------- Flag for object ------------------------- ¦ L= C¦ 0x00000004 Used for 16-bit offset code. ¦ ¦ 0x00000020 Code. ¦ ¦ 0x00000040 Initialized data. ¦
Кроме отображения секций, hiew еще умеет читать IMAGE_DATA_DIRECTORY и представлять ее в удобочитаемом виде. Это действительно наиболее важный элемент структуры PE-файла, необходимый для поиска таблиц экспорта\импорта и ресурсов. Оставим временно в стороне ресурсы и обратим внимание на таблицу импортируемых функций. Фактически изучение любого приложения начинается с ее анализа. Какие функции вызывает программа? Какие загружает DLL? Ставить ли точку останова на GetWindowTextA или GetDlgItemTextA? На все эти вопросы можно найти ответ, просмотрев список импортируемых функций.
Естественно, что при изучении DLL нас в первую очередь будет интересовать, наоборот, экспорт и соответствующая ему секция. Конечно, мне могут возразить, что для этого существуют специальные программы наподобие dumpbin, которые делают всю работу за нас, генерируя удобный список, а в hiew-е еще придется полазить по каждой секции вручную. И уж совсем, казалось бы, не к месту разговор о ресурсах, в которых с первого взгляда ни одному смертному разобраться не дано. К тому же существуют великолепные визуальные редакторы ресурсов наподобие популярного Borland ResourceWorkShop.
Так-то оно так, да только на первый взгляд. "Популярные и великолепные" редакторы оказываются неспособными поддерждать новые элементы Win98 (например календарь) и при этом просто необратимо портят ресурс (особенно это относится к Борландовскому редактору).
Относительно же таблиц экспорта\импорта разница между "структурированным" листингом и "живым" предствлением AS IS не так уж и велика. Действительно, взгляните на рисунок. Не нужно большой сноровки, что бы бегло пробежаться глазами по знакомым функциям MSVCRT. Впрочем, для "гурманов" Сусликов включил в пакет несколько специальных программ *dump, позволяющих более детально исследовать формат файла.
г= Name RVA Size =¬ ¦ Export 00000000 00000000 ¦ ¦ Import 000020E0 00000064 ¦ ¦ Resource 00004000 00000010 ¦ ¦ Exception 00000000 00000000 ¦ ¦ Security 00000000 00000000 ¦ ¦ Fixups 00000000 00000000 ¦ ¦ Debug 00000000 00000000 ¦ ¦ Description 00000000 00000000 ¦ ¦ GlobalPtr 00000000 00000000 ¦ ¦ TLS 00000000 00000000 ¦ ¦ Load config 00000000 00000000 ¦ .004029F0: 00 00 00¦ (reserved) 00000000 00000000 ¦49 00 MFC42.DLL I .00402A00: 5F 5F 43¦ (reserved) 00002000 000000A4 ¦6C 65 __CxxFrameHandle .00402A10: 72 00 B2¦ (reserved) 00000000 00000000 ¦5F 5F r -sprintf U __ .00402A20: 64 6C 6C¦ (reserved) 00000000 00000000 ¦6E 65 dllonexit Ж_one .00402A30: 78 69 74¦ (reserved) 00000000 00000000 ¦00 00 xit MSVCRT.dll .00402A40: D3 00 5FL================================-74 46 L _exit H _XcptF
Однако если бы hiew только и мог, что отображать некоторые структуры PE файлов, то эта возможность скорее всего осталась бы так и не замеченной на фоне таких конкурентов, как dumpbin, делающий, кстати, это значительно лучше hiew-а.
На самом деле уникальность последнего заключается в другом - в возможности просматривать непосредственно имена вызываемых функций (или ординалы, если в DLL отсутствует символьная информация) в окне дизассемблера. (К сожалению, встроенный ассемблер все еще не поддерживает этой возможности).
Чтобы понять, насколько это удобно, нужно поработать с hiew-ом хотя бы пару часов. Сравните qview и hiew. Не правда ли второй значительно информативнее и просто практичнее? Смысл же команд первого совершенно непонятен и загадочен. Даже если перейти по косвенному адресу, все равно там мы ничего не увидим, ибо содержимое этой секции не определено до загрузки файла.
Должен сказать, что автор допустил несколько досадных упущений, которые заметны уже в первые полчаса работы (как он сам до сих пор этого не заметил?!) HIEW никак не учитывает косвенной адресации, которую так "любят" использовать все компиляторы, особенно оптимизирующие компиляторы от MicroSoft.
В результате имя вызываемой функции по-прежнему остается загадкой. Тут на помощь приходит недокументированная особенность, заключающася в том, что секция адресов во многих случаях совпадает с секцией имен. Несмотря на то что она в любом случае будет затерта загрузчиком, это дает нам возможность определить по косвенному вызову ординал и имя функции. Как это сделать, подробно рассказывают другие книги, здесь я лишь посоветую обратится по RVA адресу (если он не равен нулю). Например, в нашем случае он равен 0х02B56 и по нему располагается строка 'AppendMenuA', таким образом mov ebx, [0004021E8] следует читать как mov ebx,AppendMenuA. Если же секция адресов пуста (или там содержится мусор), то необходимо вычислить индексы элемента от ее начала и обратиться к секции имен или просто скопировать ее самостоятельно поверх первой. Как уже отмечалось, ее содежание некритично и лишь помогает при анализе программы в дизассемблере.
Qview: 00001730: FF250C214000 jmp d,[00040210C] 000019AE: FF25D8214000 jmp d,[0004021D8] 00001372: 8B1DE8214000 mov ebx,[0004021E8] Hiew: .00401730: FF250C214000 jmp MFC42.4673 .004019AE: FF25D8214000 jmp __CxxFrameHandler ;MSVCRT.dll .00401372: 8B1DE8214000 mov ebx,[0004021E8] ^^^^^^^^^^^
Вторым по распространенности после PE-файлов является LE формат, который широко используется в VxD файлах. HIEW позволяет осуществлять навигацию по различным его структурам, а также обеспечивает удобное представление некоторых из них.
Заметим, что далеко не все дизассемблеры поддерживают LE-формат файлов, а те, которые все же его поддерживают, ужасно медленно работают. hiew на этом фоне выглядит весьма прогрессивно, и предоставляемых им возможностей вполне хватит для серьезной работы с драйверами виртуальных устройств. Невозможно представить себе надежную защиту, не использующую собственный vxd, особенно когда речь идет об обмене данных, например, с электронным ключем или с ключевой дискетой (к огромному моему удивлению, до сих пор встречаются разработчики, не расставшиеся с этим пережитком прошлого).
Однако vxd полезны не только как собственно драйверы устройств. Код, размещенный в них, работает в нулевом кольце защиты и практически без ограничений. Это открывает широкое поле для фантазии разработчиков. Действительно, трудно представить себе мало-мальски серьезный защитный механизм, не выходящий за пределы третьего кольца.
Впрочем, анализ vxd файлов ненамного сложнее любого другого, и ничего в этом таинственного и загадочного нет. Достаточно заглянуть в SDK, чтобы хотя бы в общих чертах ознакомиться с форматом файла, и уже можно начинать вгрызаться в него дизассемблерм. Hiew-ом, например.
Как видно из рисунка, LE-заголовок ненамного сложнее своего PE-собрата и не должен вызвать особых трудностей. Назначение большинства полей понятно из их названия, и только несколько моментов могут потребовать уточнения в SDK. Между прочим, MicroSoft не очень-то открыто предоставляет сей формат на общественное растерзание. Описания довольно скудные и отрывочные. Несколько статей по этому поводу в MSDN только возбуждают аппетит и разжигают любопытство, но никак не проясняют ситуацию.
Фактически анализ формата приходится выполнять каждому кодокопателю самостоятельно. И в этом значительную помощь могут оказать hiew и прилагаемая к нему программ ledump. К последней мы еще вернемся, а возможности hiew-а опишем прямо сейчас:
г============================[ LE-header ]=============================¬ ¦ Object table count 2 ¦ Page count 4 ¦ ¦ Starting EIP 00000000:00000000 ¦ Page size 00001000 ¦ ¦ Starting ESP 00000000:00000000 ¦ Bytes in last page 00000490 ¦ ¦ Fixup section size 000002D8 ¦ Loader section size 00000053 ¦ ¦ Object table 000000C4/00000144 ¦ Object page 000000F4/00000174 ¦ ¦ Object iterat 00000000/00000000 ¦ Resident name 00000104/00000184 ¦ ¦ Resource 00000000/00000000 ¦ Number of resource 0 ¦ ¦ Directives 00000000/00000000 ¦ Number of directive 0 ¦ ¦ Fixup page 00000117/00000197 ¦ Fixup records 0000012B/000001AB ¦ ¦ Import module 000003EF/0000046F ¦ Number of module 0 ¦ ¦ Import proc 000003EF/0000046F ¦ Entry table 0000010D/0000018D ¦ ¦ Data pages offset 00000600 ¦ Number preload pages 3 ¦ ¦ Non-resident name 00003A90 ¦ Bytes in non-resident 51 ¦ ¦ Instance in preload 00000000 ¦ Instance in demand 00000000 ¦ ¦ Auto data 00000000 ¦ Heap/Stack 00000000/00000000 ¦ ¦ Debug info 00000000/00000000 ¦ Debug length 00000000 ¦ L======================================================================-
Заглянув в помощь, просто поражаешься возможностям навигации. Можно переходить к LE заголовку, точке входа, таблице объектов и импорта, DDB секции, таблице страниц и ресурсов, кроме того, резидентных и нерезидентных имен, да всего и не перечислишь!
Немного печально, однако, что Hiew показывает все вышеперечисленное "AS IS" и не делает никаких попыток улучшить читабельность. Можно, конечно, воспользоваться для этой цели соответствующими утилитами, но в однозадачной MS-DOS это крайне неудобно. Легче уж смириться с тем, что есть, и разбирать все структуры вручную, надеясь, что автор все же пойдет когда-нибудь навстречу своим клиентам и реализует недостающий сервис.
Между тем hiew все же отображает по крайней мере флаги заголовка, а это уже не мало. Во всяком случае не придется копаться в битовых полях, что ускорит работу.
Также hiew правильно дизассемблирует все вызовы VMM, что, между прочим, является не такой уж тривиальной задачей. Qview, например, это делает совершенно неправильно, чем и вводит пользователя в заблуждение.
Сравните, как дизассемблировали hiew и qview один и тот же фргамент. Увы, но этот пример не в пользу qview-а, который я все же люблю несмотря ни на что, хотя и должен признать, что он безнадежно проигрывает hiew-у в этом отношении и вряд ли в ближайшее время ситуация изменится в лучшую сторону.
Hiew: .000002E3: CD208D000100 VMMcall Save_Client_State .000002E9: 5F pop edi Qview: 000046E3: CD20 int 20 000046E5: 8D00 lea eax,dword ptr [eax] 000046E7: 0100 add dword ptr [eax],eax 000046E9: 5F pop edi
К сожалению, SEN допустил досадную ошибку (с каким программистом этого не случается!), и его творение спотыкается при попытке ассемблирования VMM-вызова. Взгляните на экран, показанный ниже, - он наглядно иллюстрирует мои слова. Hiew "съедает" аргумент и отказывается анализировать любой введенный вручную. Как символьный, так и цифровой.
г= Pentium(R) Pro Assembler =====================================¬ ¦ VMMcall------------------------------------------------------- ¦ L================================================================-
Печально, конечно, но тут речь идет не более чем о програмнмой ошибке, которая, вероятно, будет устранена в ближайших версиях. Пока это не сделано, ассемблировать придется "вручную". А для этого придется познакомиться с форматом вызова VMM.
Заглянем в MSDN: там по этому поводу содержится много полезных статей и информации. Но для начала обратим внимание на базовую функцию VMMCall:
VMMCall Service, Parameters
Она ассемблируется следующим образом:
INT 0x20 DW Parameters DW Service
Рассмотрим это на примере, показанном ниже. Все настолько просто, что никаких проблем вызвать не должно. Если вы чего-то не понимаете, то обратитесь к MSDN. Там есть множество примеров, подробно комментирующих вышесказанное.
.00002665: CD2066661234 VxDcall 3412.6666 ^^^^"""" """" ^^^^
Заслуга автора hiew в том, что его продукт представляет это в удобочитаемом виде. Откуда же он берет символьную информацию? Ведь ее явным образом не содержится в файле! Верно, не содержится, поэтому-то автор и создал файл hiew.vmm приблизительно следующего содержания:
[048B.VCACHE] ^^^^ Get_Version Register GetSize
Формат его очевиден и не требует особых комментариев, достаточно взглянуть на расположенную ниже строку:
.00002665: CD2000008B04 VxDcall VCACHE.Get_Version ^^^^
Разумеется, этот файл можно как угодно перекраивать под свой вкус и потребности. В частности, вносить поддержку новых vxd, используемых, скажем, защитой, или написанных вами.
При этом vxd с ординалом сервиса 0x01 иначе еще называется (в терминологии архитектуры Win32) - VMM "Virtual-Memory Manager". На самом деле это все тот же vxd, и вызов его происходит аналогично:
.00012161: CD2001000100 VMMcall Get_Cur_VM_Handle ^^^^ .00002665: CD2000008B04 VxDcall VCACHE.Get_Version ^^^^
Доказательством этого служит следующая строка из hiew.vmm "[0001.VMM]". Вообще-то с названием файла, автор, похоже, допустил еще одну досадную ошибку, немного сбивающую с толку. Все же это не 'hiew.vmm', а 'hiew.vxd'. Надеюсь, что в последующих версиях это будет исправлено.
Мы еще не упомянули о таком понятии, как VxDjmp. Он вызывается аналогично, с маленьким исключением - старший бит параметра в этом случае равен единице, а не нулю. Вот, взгляните сами:
.00005040: CD201C801700 VxDjmp SHELL.Update_User_Activity ^ .00005048: CD2048810100 VMMjmp RegOpenKey ^ Hiew правильно интерпретирует значение этого бита, в противном случае он неверно дизассемблировал бы вызов и не смог определить функцию. Это еще раз подчеркивает, что автор проделал большую работу, и в благодарность ему можно простить мелкие ошибки. Любопытно, что вместо этого их склонны упорно не замечать, - иначе трудно объяснить, почему они продержались вплоть до 6.03 версии. Пассивный нынче народ стал, однако...
На этом я заканчиваю описание поддержки LE-формата. Если кому-то оно покажется неполным, пожалуйста, обратитесь к соответствующей документации. После некоторых размышлений я решил не описывать остальные форматы, чтобы не повторяться. Не думаю, чтобы там было что-то особо интересное.
Калькулятор
"Врагу, которым восхищаешься, легче вселить в тебя ужас".
Ф.Херберт. "Дюна".
Необходимость встроенного калькулятора сегодня сомнений ни у кого не вызывает. Хакеру настолько часто приходится возиться с разными системами счисления, битовыми масками, относительными переходами, что от всего этого голова пойдет кругом, если потребуется проводить вычисления в уме.
Впервые полноценный калькулятор, насколько мне помнится, появился в qview-е, намного опередив конкурентов. В свое время это даже послужило причиной отказа от hiew-а, у которого такой калькулятор появился относительно недавно и, к сожалению, сильно проигрывает qview-скому.
Признаться, мне непонятна позиция автора в этом вопросе. Почему бы ему если не опередить, то хотя бы просто догнать конкурентов? Тем более, что основное различие как раз и состоит в отстутствии поддержки битовой системы счисления. Т.е. как раз того, ради чего калькулятор в большинстве случаев и нужен. Битовые операции в основом приходится выполнять при разборе различных флагов и атрибутов.
К счастью, во всем остальном калькулятор hiew-а ни в чем не уступает своим собратьям и поддерживает все типовые логические и математические операции, которые подробно будут описаны ниже:
г================ Calculator ================¬ ¦ (0xFF ^ 0x80 | 128) > 0------------------- ¦ ¦ Signed: 1 ¦ ¦ Unsigned: 1 . . ¦ ¦ Binary: 00000000000000000000000000000001 ¦ ¦ Hex: 00000001 " " ¦ L============================================-
Части: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15