)s64W@(08dж▄ ∙ ┤Uрjr10b.lj *c33E(s43W0№№№№№№xxxxxxxxx0000№№№№№№*c34E(s49W Tр~└~└№А№А°°ЁЁрр*c35E(s274W $0+╨└р└р└р└рА└А└А?А>~?                         °°°°ЁЁЁЁрррррр└└└└АААААА     А     А     А     А     А||№№°°°°°°ЁЁЁЁрррррр└└*c36E(s139W!)lxxxxx{└{р?{Ё{° {°№x°°x0°x°x№x■xxx?z{{А{р{Ё{Ёy°x№x|x|x|`x№ {° {Ё {р{└{xxxxxx*c37E(s201W$&%░Ё °?№>■|~~x№?°°Ё°Ё°р°└№?└~~А■А?№°Ё><|xЁЁ?└ррр Ё┴ °┴∙°ГЁ№р|р|р|>р|<Ё№x∙°° °Ё ЁЁрр?└└*c38E(s103Wp?р р р р°└ррЁЁ°№?№■~№?№?Я№┐№■№№№°■Ё~рПЁ? °? №■■■№?Ё*c39E(s38W ,А><<xpЁр*c40E(s92W &@А└?└А■№№№№№№№№№№№№№№№№№№№№№№№№■А?└└А*c41E(s92W &@8|■ А?А└└└└└└└└└└└└└└└└└└└└└└└└└└?АА ■|8*c42E(s38W HА Ёр?└АА9└0└*c43E(s73WXЁЁЁЁЁЁЁ  Ё  Ё  Ё  ЁЁЁЁЁЁЁЁЁ*c44E(s27W $■№№°°ЁЁЁрр└*c45E(s24W @ └ └ └ └*c46E(s22W(№№№№№№*c47E(s127W %t >|x°ЁЁр└└АА><|x°Ёрр└└А><|xЁЁ`*c48E(s106WXЁ№■? ?  А?А~А~А№└№└№└№└№└№└№└№└№└№└№└№└■└~А?А А? ? ■№Ё*c49E(s72W Xррррр?рр р р р ряр╧рррррррррррррррр*c50E(s103WXЁ№?■? ? А? А└?└└└└└└АА??~■№°°Ё Ё Ё? р р  └  └*c51E(s106WXЁ?№■  0А?АААА?■№■ А?└└└└└?└`А  А   ■ №°р*c52E(s100WX?     ┐┐???????8?8?? р └ А  ?????*c53E(s74WX  ■■№№АА???р°№■■  ■■ ■ № ° Ё р?А*c54E(s103WX№№°°Ёрр└А?0?№?  А А└■└№р№р№р№р■р└ └? А? А №Ё*c55E(s100WX? └ └ └  А  А?~~№№№°°ЁЁррр└└А?А?■■*c56E(s106WXЁ№■?  А~А№└№└№└№└|А~А? ■№■? ~А№└№└№└№└№└■└  └ А? ■№Ё*c57E(s103WXЁ№ ? А? А └└■р№р№р№р№р~р└? └? └ А АЯА?~№№°°ЁЁр*c58E(s36W8№№№№№№№№№№№№*c59E(s41W4№№№№№№■№№°°ЁЁЁрр└*c60E(s112WМ8°°°└ №?Ё А■?° р р?°■ А?Ё№ └°°°8*c61E(s52W X  Ё  Ё  Ё  Ё  Ё  Ё  Ё  Ё*c62E(s112WМр°  └Ё■ Ар№  р?°?° р №р А■Ё └ °р*c63E(s74W H А └ └├└└└А>|xxx└?└└└??????*c64E(s221W()и А Ё? №   №?АЁ└└рАЁ°x<№<< pЁ DEBUG - S F000 : 0000 L FFFF "Illegal Function call" F000 : 63F4 Рис. 6.1. Запуск программы DEBUG и поиск сообщения Значение адреса искомого текстового объкта говорит о том, что найденное сообщение расположено недалеко от начального параграфа размещения Бэйсика F600. Если представить 20-ти битовый адрес F63F4 в сегментированном виде, используя в качестве базы параграф F600, то будем иметь адрес F600:03F4. Таким образом, относительное смещение от начала Бэйсика составляет только 3F4/16 или 1012/16 байтов. Вводя команду D F600 : 03E0 (вывод на экран содержимого области памяти, начиная с адреса - т.е. области памяти, начинающейся несколько раньше найденного сообщения - относительно базового адреса параграфа F600) можно отобразить содержимое близлежащего пространства памяти, включающего область найденного сообщения. На рисунке 6.2. представлен результат выполнения этой команды. Программа DEBUG выводит на устройство отображения - в шестнадцатеричном и символьном (ASCII) форматах - содержимое участка памяти длиной 80 байт, причем этот участок памяти содержит искомое сообщение. Нам таким образом представлено искомое сообщение, а также целый ряд других сообщений об ошибках Бэйсика, т.е. именно то, что - как предполагалось - должно находиться где-то поблизости. При необходимости с помощью программы DEBUG можно продолжить отображение на экране последующих участков памяти; для этого следует ввести команду D, не указывая адреса начала ячейки. Рисунок 6.2. иллюстрирует ряд возможных особенностей, которые следует иметь в виду, используя команду "D" в рамках программы DEBUG. Одна из этих особенностей состоит в том, что в левую часть поля вывода помещается шестнадцатеричная информация, а в правую - информация в коде ASCII. Вторая особенность заключается в том, что кодовая комбинация не имеющая символьного представления в коде ASCII изображается в правой части поля вывода с помощью точки. Отсюда, в частности, вытекает важное следствие: для того, чтобы использовать эхо-печать программа DEBUG контролирует поступающие на ее вход кодовые комбинации, не имеющие представления в коде ASCII; эти комбинации могут быть восприняты принтером в качестве управляющего кода. Вследствие этого, нельзя расчитывать, что правая часть поля вывода (информация в коде ASCII) будет давать объективную картину. Все, что может быть приведено к стандартным печатным символам, или является печатным символом (со старшим битом в единичном состоянии) будет отпечатано, все остальное будет преобразовано в точки.(Здесь уместно заметить, что программа DiskLook, входящая в состав пакета программ, записанного на дискету, прилагаемую к настоящей книге, отображает в точности все шестнадцатеричные коды и коды ASCII, поскольку выводит информацию только на экран и не использует эхо-печать). До сих пор мы рассматривали способы отображения данных, или поиска априори известных данных, хранящихся в ПЗУ с помощью программы DEBUG. Ниже мы будем решать существенно более сложную задачу - задачу деассемблирования и интерпретации программного кода на машинном языке. 6.3. Анализ содержимого ПЗУ - метод деассемблирования Наш следующий шаг в решении задачи исследования содержимого ПЗУ и использования программы DEBUG состоит в знакомстве с особенностями процесса деассемблирования. Команда "U" программы DEBUG - означает "деассемблирование" - осущеcтвляет преобразование произвольных кодов памяти в мнемонические коды языка ассемблера. Деассемблер реализует: перевод абсолютных шестнадцатеричных кодов программ, в коды команд на языке ассемблера (например: AX или DS). Деассемблер не делает две вещи. Первое. Деассемблер не интерпретирует смысл программы и не обучает пользователя. Для понимания листинга, выдаваемого деассемблером, необходимо либо знать язык ассемблера, либо иметь желание покопаться в справочном руководстве и, установив смысл мнемонических обозначений, постараться понять суть дела. (Для этой цели могут быть использованы следующие источники: "Руководство по языку ассемблера фирмы IBM", справочники по системе команд микропроцессора 8086/8088, такие как "Учебное пособие по микропроцессору 8086" С.Морса или "Описание микропроцессора 8086" Ректора и Алекси). Для понимания программы на языке ассемблера (или программы, приведенной к языку ассемблера), знания одного языка Ассемблер недостаточно, необходимо также иметь некоторое представление о функциональной ориентации программы. Постижение пограммы представляет собой занимательное умственое упражнение, своего рода головоломку. Со временем эта процедура становится все проще и проще, отчасти потому, что времени на поиск смыслового описания команды уходит все меньше и меньше, а отчасти потому, что усваиваются приемы программирования на ассемблере и способы достижения целей. Далее мы остановимся на этих вопросах более подробно. Второе, что не под силу программе деассемблера - это установка так называемой абсолютной синхронизации. Известно, что команды машинного языка для микропроцессора INTEL 8086/8088 имеют переменную длину - от одного до шести байтов. После того как деассемблеру сообщена конкретная позиция памяти он приступает к процедуре прямого декодирования, не отличая кодов команд от данных. Достаточно ошибиться в выборе исходной позиции памяти (например, попасть не на границу между командами или в область данных) и результат окажется неверным. Если начальная точка набора команд известна, то никаких проблем не возникает. Если какие-либо предварительные сведения отсутствуют, то следует провести необходимое исследование. Первое, что необходимо сделать - это выбрать исходную точку в наборе команд и запустить операцию деассемблирования. После этого следует предпринять попытку интерпретировать результат работы деассемблера. Если осмысленные программные участки отсутствуют (ниже мы приводим некоторые соображения по их выявлению),то следует повторить процедуру, сместив начальную точку на один, два или три байта по отношению к предыдущей попытке. Мы таким образом пытаемся отыскать точку синхронизации, расположенную на стыке двух команд, после чего может быть восстановлен оригинальный набор команд. Если очередная попытка закончилась неудачно, то следует попробовать еще раз. Поскольку большинство команд имеют длину один или два байта, то точку синхронизации отыскать как правило несложно. Кроме того, необходимо учитывать, что процессу деассемблирования присущ элемент самосинхронизации. Даже если в качестве начальной точки для деассемблирования выбрана точка, расположенная внутри команды, то часто случается, что процесс деассемблирования при обработке последуюющих команд самопроизвольно попадает в нужную точку. После этого все идет гладко, по крайней мере в сторону увеличения адресов. (В обратную сторону это приходится делать вручную). Выполняя деассемблирование программы необходимо внимательно следить за данными (это нечто противоположное командам), которые деассемблер превращает в причудливые ассемблерные конструкции. Основной и наиболее быстрый способ обнаружения данных состоит в использовании команды "D", для представления информации в шестнадцатеричном коде и коде ASCII. Здесь в первую очередь следует обращать внимание на самые очевидные вещи - содержательно осмысленную последовательность символов ASCII (фразы), такую, например, как "неверный вызов функции". После этого можно приступать к выявлению более тонких признаков данных. В обычных программах (т.е. в программах, размещаемых вне ПЗУ) наличие полей, заполненных шестнадцатеричными нулями является признаком рабочей области данных, то есть такой области, которая будет использоваться в ходе выполнения программы; в процессе деассемблирования данные этой области неактуальны. ПЗУ не может использоваться для хранения рабочих данных, поскольку запись в эту память невозможна. Таким образом, наличие полей, состоящих из шестнадцатеричных нулей нетипично для ПЗУ-программ. Признаком возможного наличия данных являются байты или двухбайтовые слова, для которых второй или старший байт содержит 0 или F. Дело в том, что значительное число констант программ являются малыми числами, положительными (т.е. начинающихся с 0), либо отрицательными (т.е. начинающихся с F). Если будет обнаружена последовательность байтов,