Справочник функций

Ваш аккаунт

Войти через: 
Забыли пароль?
Регистрация
Информацию о новых материалах можно получать и без регистрации:

Почтовая рассылка

Подписчиков: -1
Последний выпуск: 19.06.2015

Техника и философия хакерских атак

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

Продолжение...

(Впрочем, не факт, что версия 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

Оставить комментарий

Комментарий:
можно использовать BB-коды
Максимальная длина комментария - 4000 символов.
 
Реклама на сайте | Обмен ссылками | Ссылки | Экспорт (RSS) | Контакты
Добавить статью | Добавить исходник | Добавить хостинг-провайдера | Добавить сайт в каталог