Анимация
JavaScript
|
Главная Библионтека 0000:0100 start 0000:0112 BreakHere 0000:0114 BytesToDecrypt Program entry point at 0000:0100 Такой формат поддерживают большинство отладчиков, в том числе и популярнейший Soft-Ice, в поставку которого входит утилита "msym", запускаемая с указанием имени конвертируемого map-файла в командной стоке. Полученный sym-файл необходимо разместить в одной директории с отлаживаемой программой, загружаемой в загрузчик без указания расширения, т.е., например, так "WLDR Crypt". В противном случае символьная информация не будет загружена! Затем необходимо установить точку останова командой "bpx BreakHere" и покинуть отладчик командной "x". Спустя секунду его окно вновь появиться на экране, извещая о достижении процессором контрольной точки. Посмотрев на значения регистров, отображаемых по умолчанию вверху экрана, можно выяснить, что содержимое SI равно 0x12E. С другой стороны, это же значение можно вычислить «в уме», не прибегая к отладчику. Команда MOV в строке 0x106 загружает в регистр SI смещение 0x114, откуда командой LODSW считывается количество расшифровываемых байт - 0x18, при этом содержимое SI увеличивается на размер слова - два байта. Отсюда, в момент завершения цикла расшифровки значение SI будет равно 0x114+0x18+0x2 = 0x12E. Вычислив адрес перехода в строке 0x112, рекомендуется создать соответствующую перекрестную ссылку (from: 0x122; to: 0x12E) и добавить комментарий к строке 0x112 ("Переход по адресу 012E"). Создание перекрестной ссылки автоматически дизассемблирует код, начиная с адреса seg000:012E и до конца файла.
Назначение команды "CALL $+3" (где $ обозначает текущее значение регистра указателя команд IP) состоит в заталкивании в стек содержимого регистра IP, откуда впоследствии оно может быть извлечено в любой регистр общего назначения. Необходимость подобного трюка объясняется тем, что в микропроцессорах серии Intel 80x86 регистр IP не входит в список непосредственно адресуемых и читать его значение могут лишь команды, изменяющие ход выполнения программы, в том числе и call. Для облегчения анализа листинга можно добавить к стокам 0x12E и 0x131 комментарий - "MOV CX, IP", или еще лучше - сразу вычислить и подставить непосредственное значение - "MOV CX,0x131". Команда "POP SI" в строке 0x132 снимает слово из стека и помещает его в регистр SI. Прокручивая экран дизассемблера вверх в строке 0x10В можно обнаружить парную ей инструкцию "PUSH SI", заносящую в стек смещение первого расшифровываемого байта. После этого становится понятным смысл последующих команд "MOV DI, 0x100\SUB CX,SI\REPE MOVSB". Они перемещают начало расшифрованного фрагмента по адресу, начинающегося со смещения 0x100. Такая операция характерна для «конвертных» защит, накладывающихся на уже откомпилированный файл, который перед запуском должен быть размещен по своим «родным» адресам. Перед началом перемещения в регистр CX заносится длина копируемого блока, вычисляемая путем вычитания смещения первого расшифрованного байта от смещения второй команды перемещающего кода. В действительности, истинная длина на три байта короче и по идее от полученного значения необходимо вычесть три. Однако, такое несогласование не нарушает работоспособности, поскольку содержимое ячеек памяти, лежащих за концом расшифрованного фрагмента, не определено и может быть любым. Пара команд "0x136:PUSH DI" и "0x13B:RETN" образуют аналог инструкции "CALL DI" - "PUSH" заталкивает адрес возврата в стек, а "RETN" извлекает его оттуда и передает управление по соответствующему адресу. Зная значение DI (оно равно 0x100) можно было бы добавить еще одну перекрестную ссылку ("from:0x13B; to:0x100") и комментарий к строке :0x13B - "Переход по адресу 0x100", но ведь к этому моменту по указанным адресам расположен совсем другой код! Поэтому, логически правильнее добавить перекрестную ссылку "from:0x13B; to:0x116" и комментарий "Переход по адресу 0x116". Сразу же после создания новой перекрестной ссылки IDA попытается дизассемблировать зашифрованный код, в результате чего получится следующее: seg000:0116 loc 0 116: seg000:0116 seg000:0119 seg000:011A seg000:011C seg000:011D seg000:011E seg000:0121 seg000:0123 seg000:0124 seg000:0127 seg000:0129 seg000:012 9;- seg000:012B seg000:012C seg000:012D seg000:012E;- outsb stos movsw db db db ; CODE XREF: seg000:013Bu byte ptr [bx-24h], cl word ptr es:[edi]
Непосредственное дизассемблирование зашифрованного кода невозможно -предварительно его необходимо расшифровать. Подавляющее большинство дизассемблеров не могут модифицировать анализируемый текст налету и до загрузки в дизассемблер исследуемый файл должен быть полностью расшифрован. На практике, однако, это выглядит несколько иначе - прежде чем расшифровывать необходимо выяснить алгоритм расшифровки, проанализировав доступную часть файла. Затем выйти из дизассемблера, тем или иным способом расшифровать «секретный» фрагмент, вновь загрузить файл в дизассемблер (причем предыдущие результаты дизассемблирования окажутся утеряны) и продолжить его анализ до тех пор, пока не встретится еще один зашифрованный фрагмент, после чего описанный цикл «выход из дизассемблера -расшифровка - загрузка - анализ» повторяется вновь. Достоинство IDA заключается в том, что она позволяет выполнить ту же задачу значительно меньшими усилиями, никуда не выходя из дизассемблера. Это достигается за счет наличия механизма виртуальной памяти, подробно описанного в главе «Виртуальная память». Если не вдаваться в технические тонкости, упрощенно можно изобразить IDA в виде «прозрачной» виртуальной машины, оперирующей с физической памятью компьютера. Для модификации ячеек памяти необходимо знать их адрес, состоящий из пары чисел - сегмента и смещения. Слева каждой строки указывается ее смещение и имя сегмента, например "seg000:0116". Узнать базовый адрес сегмента по его имени можно, открыв окно «Сегменты» выбрав в меню «View» пункт «Segments». = [И] Program Segmentation = Name Start End Align Base Type Cls 32es ss ds seg000 00000100 0000013C byte 1000 pub CODE N FFFF FFFF 1000 00010100 0001013C Рисунок 8 Окно «Сегменты» Искомый адрес находится в столбце "Base" и для наглядности на приведенной копии экрана выделен жирным шрифтом. Обратится к любой ячейке сегмента поможет конструкция "[segment:offset]", а для чтения и модификации ячеек предусмотрены функции Byte и PatchByte соответственно. Их вызов может выглядеть, например, так: a=Byte([0x1000,0x100]) - читает ячейку, расположенную по смещению 0x100 в сегменте с базовым адресом 0x1000; PatchByte([0x1000,0x100],0x27) - присваивает значение 0x27 ячейке памяти, расположенной по смещению 0x100 в сегменте с базовым адресом 0x1000. Как следует из названия функций, они манипулируют с ячейками размером в один байт. Существуют так же функции, манипулирующие целыми словами, подробнее о них можно прочесть в главе «Виртуальная память». Знания этих двух функций вполне достаточно для написания скрипта -расшифровщика при условии, что читатель знаком с языком Си. Реализация IDA-Си не полностью поддерживается стандарта - подробнее об этом рассказывается в главе «Язык скриптов IDA-Си», здесь же достаточно заметить, что IDA не позволяет разработчику задавать тип переменной и определяет его автоматически по ее первому использованию, а объявление осуществляется ключевым словом "auto". Например, "auto MyVar, s0" объявляет две переменных - MyVar и s0. Для создания скрипта необходимо нажать комбинацию клавиш <Shift-F2> или выбрать в меню "File" пункт "IDC Command" и в появившемся окне диалога ввести исходный текст программы: Notepad Enter IDC statement(s) auto a; for (a=0x116;a<0x12E;a++) PatchByte([0x1000,a], Byte([0x1000,a])0x66); Рисунок 9 Встроенный редактор скриптов auto a; for (a=0x116;a<0x12E;a++) PatchByte([0x1000,a],Byte([0x1000,a])0x66); a) исходный текст скрипта - расшифровщика Пояснение: как было показано выше алгоритм расшифровщика сводится к последовательному преобразованию каждой ячейки зашифрованного фрагмента операцией XOR 0x66, (см. ниже - выделено жирным шрифтом) seg000:010C seg000:010F xor byte ptr [si], 66h inc si 0 1 2 3 [ 4 ] 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 |