Воробьёвы

(-:

№10.1. DZebug: руководство юZверя, часть 1

БРЕДИСЛОВИЕ BY NYRON

... В 945-ом году отправился князь Игорь к программистам за данью. Когда программисты узнали размеры дани, их лица сразу стали озабоченными, и они побили Игоря и его дружину. Тогда жена Игоря Ольга с огнем и мечом пошла на программистов. Отдавайте, говорит, законную дань, а не желаете, так поставте на каждую тачку нашу новую навороченную ОСь. Обрадовались программисты, что могут отделаться малым, и их лица опять стали веселыми. А Ольга приказала в каждую ОСь зашить BUG. Программисты инсталлировали ОСь, и BUG уничтожил все ихние данные...

И дело даже не в том, что жалко программистов, а в том, что история учит, какими б не казались крутыми ОСы, нужно уметь работать не зависимо от програмного обеспечения. Ведь сила не в мегагерцах и не гигабайтах, и даже не в DZеньгах, сила - она в ньютонах...

ПРИВЕТСТВУЮ ВАС, 3904 ШТ. ДZЕНСТВУЮЩИХ БРАТЬЕВ И СЕСТЕР!

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

А мне они сказали: крутись, как хочешь, но чтоб в воскресенье - был очередной номер.

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

Ну что, поехали?

ПРЕДЫДУЩИЕ ВЫПУСКИ РАССЫЛКИ

По идее, у не то, чтобы повторение - мать учения... просто не проштудировав материал прыдыдущих номеров, вам будет весьма затруднительно "въехать" в номер текущий...

Выпуск 2 - про систему счисления.

Выпуск 3 - про порядок загрузки компьютера.

Выпуск 4 - про регистры.

Выпуск 5 - про программу в памяти.

Выпуск 6 - про прерывания.

Выпуск 7 - программируем и отлаживаем.

Выпуск 8 - "ломаем" игры, разборки со стеком, циклами, оптимизацией.

Выпуск 9 - разборки с процедурами и CX = 10000h

Кстати, у кого проблемы с интернетом... хм... это только ваши проблемы и к нам по этому поводу обращаться не стОит... не стоИт...

(C) NYRON (из прошлого выпуска скопировано правильно)

ПРО DZEBUG (У МЕНЯ ОН ИМЕННО ПОД ТАКИМ ИМЕНЕМ ПРОХОДИТ)

Прога эта неимоверна крута и сильна! Она позволяет позволяет писать программы и вмешиваться в ход выполнения программ на самом низком, можно сказать "на самом дZенском" уровне! С помощью нее можно отображать и изменять значения регистров :), запускать и останавливать выполнение программы в любой момент :), вносить изменения в программу :), работать с винтом на физическом уровне :); работать с машинным кодом, ассемблировать и дизассемблировать его с той легкостью, которая присуща только продуктам корпорации Micro$oft...

ЗАПУСК DZEBUG'А

Я знаю только два способа запуска DEBUG с файлом.

1. С командной строки:

      debug имяфайла.тип <Enter> 

2. Без командной строки:

      debug  

Тут вы получите приглашение DZEBUG в виде черточки "-".

Далее следуют команды:

      -n имяфайла.тип <Enter> 
      -l  

Ууупс! DZEBUG загрузил вашу программу и готов к работе :)))

ОТОБРАЖЕНИЕ И ИЗМЕНЕНИЕ ЗНАЧЕНИЙ РЕГИСТРОВ

Первым делом мы просмотрим содержимое регистров, используя команду R. В качестве объекта извращения (Serrgio сегодня будет извращаться несколько иначе) - ваша же прога из #7 п. 4...

Если вы ввели R без параметров, значения регистров будут выведены примерно так:

     AX=0000 BX=0000 CX=0043 DX=0000 SP=FFFE BP=0000 SI=0000 DI=0000 
     DS=16BB ES=16BB SS=16BB CS=16BB IP=0100 NV UP DI PL NZ NA PO NC 
     15A3:0100 30C0  XOR AL,AL 

CX содержит длину файла (0043h или 67d). Если размер файла превышает 64К, то BX будет содержать старшую часть размера файла. Это очень важно знать при исрользовании команды Write - размер файла содержится именно в этих регистрах.

Запомните: когда файл находится в памяти, DZebug не знает его размер. При записи данные о размере берутся из регистров CX и BX. Страшно даже подумать, что может произойти, если мы по ошибке введем команду Write, а в это время BX и CX будут содержать FFFF! Страшно, но приятно...

Если мы хотим изменить значение одного из регистров, мы вводим R и имя регистра. Давайте поместим в AX слово *UCK (те, кто не знакои с английским пусть спросят у своих умных друзей, что означает это слово).

     -R AX

Вывалится:

     AX 0000 
     : 

":" - это приглашение ввести новое значение. Мы отвечаем *UCK

     :*UCK

Вывалится:

     ^Error 

DZebug выдал сообщение об ошибке.

Тут дело даже не в том, что DZебугу не понравилось слово, которое мы ввели. Ему в сущности глубоко наплевать, какое значение мы хотим занести в регистр, главное - чтобы цифры были HEX'овые. И если F является таковой, то U - нет. DZебуг заботливо указал нам на нее значком " ^ ".

Ну и фиг с ним. Попробуем ввести что-нибудь более пристойное, например D3E0:

     -R AX 
     AX 0000 
     :D3E0 

Теперь, если мы просмотрим регистры, мы увидим следующее:

     AX=D3E0 BX=0000 CX=0043 DX=0000 SP=FFFE BP=0000 SI=0000 DI=0000 
     DS=16BB ES=16BB SS=16BB CS=16BB IP=0100 NV UP DI PL NZ NA PO NC 
     15A3:0100 30C0  XOR AL,AL 

Вы увидите, что ничего не изменилось кроме регистра AX. Ему присвоили новое значение, как мы помним.

Еще один важный момент: команда Register может использоваться только для 16-битных регистров (AX, BX и т. д.). Она не может изменять значения 8-битных регистров (AH, AL, BH и т. д.). Например, чтобы изменить AH, вы должны ввести новое значение в регистр AX с новым AH и старым значением AL.

Помедитируйте немного и поехали дальше...

ДАМП ПАМЯТИ

Если вы не очень хорошо умеете читать машинные команды процессора, команду Dump можно использовать для вывода на экран данных (тексты, флаги и т. д.). Для вывода кода лучше использовать команду Unassemble.

Если мы теперь введем команду Dump, DZEBUG определит начало программы. Для этого он использует регистр DS, и, так как это COM-файл, начинает вывод с адреса DS:0100. Он выведет 80h (128d) байт данных (или то количество, которое вы сами определите)... Следующее употребление команды Dump отобразит следующие 80h байт и так далее.

Например, первая команда Dump выведет 80h байт начиная с адреса DS:0100, вторая команда выведет 80h байт начиная с адреса DS:0180...

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

Например, запись D DS:BX не верна. Загрузив нашу программу и введя команду Dump, мы увидим очень непонятные буквы и цифры.

Вы наверное уже знаете, что эти буквы и цифры - не что иное как наша программа. Невероятно, но это так.

Вы видите, что выводимые командой Dump данные разделены на три части.

Самая левая содержит адрес первого байта в строчке. Ее формат - сегмент:смещение.

Следующая колонка содержит шестнадцатеричные данные по указанному адресу. Каждая строчка содержит 16 байт данных.

Третья колонка - это ASCII представление данных. Отображаются только стандартные символы ASCII. Специальные символы IBMPC не отображаются, вместо них выводятся точки ".". Это делает поиск простого текста более удобным.

Dump не может выводить данные, выходящие за границы сегмента. Например, команда

   
      -D 0100 L F000 <Enter>

правильная (выводятся байты начиная с DS:0100 до DS:F0FF), а команда

      -D 9000 L 8000 <Enter>

некорректна (8000h +9000h = 11000h - выходит за границу сегмента).

Так как 64К - это 10000h то невозможно задать этот адрес четырьмя шестнадцатеричными цифрами, поэтому DEBUG использует 0000 для задания 10000h.

Чтобы вывести на экран весь сегмент - введите

      -D 0 L 0. 

Помедитируйте немного и поехали дальше...

ПОИСК БАЙТОВ

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

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

Шестнадцатеричные и символьные данные могут чередоваться в любом порядке, например команда

     -S 0 L 100 12 34 'abc' 56 <Enter>

- правильная, в результате произойдет поиск от DS:0000 до DS:00FF последовательности 12h 34h a b c 56h.

Символы верхнего регистра отличаются от символов нижнего регистра. Например, 'ABC'- это не то же самое, что 'Abc' или 'abc' или другие комбинации верхних и нижних регистров. Однако, 'ABC' и "ABC" считаются одинаковыми, потому что кавычки - это всего лишь разделитель.

Попробуем найти слово 'пИво'. У нас получится следующее:

     -S 0 L 0 'пИво' <Enter>
     - 

Ну нет в этой программе пИва!

Попробуем пИво поискать в другом месте, а в нашей программе лучше поищем слово B7 20 B5 06:

     -S 0 L 0 B7 20 B5 06 <Enter>
     15A3:0110 
     - 

Итак, по адресу 15A3:0170 DZebug нашел слово B7 20 B5 06.

Опять же: значение сегмента у вас может быть иным, но смещение должно быть такое же...

Если мы выведем данные на экран, то по этому адресу мы найдем строку '. ......=...0..'. Мы можем попробовать найти строчку '_¶¦_:-', или еще какую-нибудь нездоровую последовательность символов.

Если мы захотим найти все места, где употребляется команда Int 10h (в машинном коде эта команда имеет вид CD 10), мы сделаем следующее:

     -S 0 L 0 cd 10 <Enter>

и получим дли-и-инную простыню:

     15A3:010E 
     15A3:011A 
     15A3:0126 
     15A3:0132 
     15A3:013E 
     15A3:0AE6 
     15A3:0F23 
     - 

DZEBUG нашел последовательность CD 10 по этим адресам. Это не значит, что все CD 10 - это команды Int 10h. Просто по указанным адресам расположенны искомые данные. Это может быть (чаще всего) инструкция, но это также может быть и адрес, вторая часть инструкции JMP и т.д. Вы должны внимательно изучить код программы на данном участке памяти, чтобы быть уверенным, что это действительно INT 10.

Вы ведь не думаете, что комп все сделает за вас? Конечно, компьютеры заменили человека во многих сферах деятельности, преимущественно там, где нужно работать нижними полушариями мозга, а там, где нужны еще и верхние полушария, без человека ну никак не обойтись! И это - звучит гордо!

Помедитируйте немного и поехали дальше...

СРАВНИВАНИЕ УЧАСТКОВ ПАМЯТИ

Архиполезнейшая штука! Лично я долго над ней дZенствовал :).

Команда сompare берет два заданных участка памяти и сравнивает их, байт за байтом. Если два адреса содержат разную информацию, они выводятся на экран вместе с их содержимым. Для примера мы сравним 2 байта DS:0100 с DS:0200 .

   
   -С 0100 L 8 0200 
   15A3:0100 30 0E 15A3:0200 
   15A3:0101 C0 00 15A3:0201 

Все 2 байта различны, поэтому они все выведены на экран. Если какие-нибудь байты окажутся одинаковыми, то они не будут выводится на экран. Если две области окажутся полностью одинаковыми, DEBUG просто ответит новым приглашением. Это очень удобный способ сравнения данных из памяти с данными из файла или ROM-BIOS :)

ДИZАССЕМБЛИРОВАНИЕ

Unassemble - основная команда, которую вы будете использовать при отладке. Эта команда берет машинный код и преобразует его в инструкции ассемблера. Способ задания адреса такой же, как и в предыдущих командах, с одной лишь разницей: поскольку мы теперь будем работать с кодом (предыдущие команды в основном предназначены для работы с данными), регистр по умолчанию - CX. В .COM программах это делает небольшое отличие, если только вы сами не очистите DS. Однако в .EXE файлах это черевато трудностями, потому что изначально регистрам CS и DS присвоены разные значения.

     -u 
     15A3:0100 XOR AL,AL 
     15A3:0102 MOV BH,10 
     15A3:0104 MOV CH,05 
     15A3:0106 MOV CL,10 
     15A3:0108 MOV DH,10 
     15A3:010A MOV DL,3E 
     15A3:010C MOV AH,06 
     15A3:010E INT 10 
     15A3:0110 MOV BH,20 
     15A3:0112 MOV CH,06 
     15A3:0114 MOV CL,11 
     15A3:0116 MOV DH,0F 
     15A3:0118 MOV DL,3D 
     15A3:011A INT 10 
     15A3:011C MOV BH,30 
     15A3:011E MOV CH,07 

Мы видим уже знакомую нам программу. Если мы опять введем "u", то DZebug выдаст нам очередную порцию кода. В нашем случае все обошлось благополучно, но бывает и так, когда вы не знаете, что вы в данный момент дизассемблируете - действительно код программы, или же ее данные. DZebug сделает все, что вы ему прикажете. Если вы скажете дизассемблировать данные, он сделает это, ничего не заметив.

РАЗМЕЩЕНИЕ ДАННЫХ В ПАМЯТИ

Команда Enter используется для размещения данных в памяти. Она имеет два режима: Display/Modify и Replace. Отличия между ними в расположении помещаемых данных - в самой комаеде Enter или после приглашения.

Если вы ввели E <адрес>, вы будете находиться в режиме Display/Modify. DZEBUG предложит вам изменить значение байта, отображая его текущее значение. Вы можете ввести один или два шестнадцатеричных символа. Если вы нажмете пробел, DZEBUG не будет изменять текущий байт, а перейдет к следующему. Если вы зашли далеко, нажатие минуса "-" возвратит на один байт назад.

      -E 100 <Enter>
      15A3:0100   30.41   C0.42   B7.43   10.     B5.45 
      15A3:0105   05.46   B1.40 10.- 
      15A3:0106   40.47   10. 

В нашем примере мы ввели E 100. Dzebug ответил адресом и значением байта по этому адресу (30). Мы ввели 41, и DZebug автоматически перешел к следующему байту данных (C0). Опять, мы вводим 42, и DEBUG переходит к следующему байту (B7). Мы изменили его на 43. По адресу 104 байт 10 нас вполне удовлетворяет, поэтому мы нажали пробел. DEBUG не изменил его значение и перешел к следующему байту.

После ввода 40 в позиции 105 мы обнаружили, что ввели неправильное значение. Нажимаем минус, и DEBUG возвращается на одну позицию назад, отображая адрес и содержимое. Обратите внимание, что оно отличается от первоначального (B5) и имеет значение, которое мы ввели (40). Мы вводим правильное значение и завершаем работку нажатием ENTER.

Как вы видите, это утомительная работа, особенно когда вы работаете с большими объемами данных или с ASCII-текстом - вы должны знать шестнадцатеричное значение каждого символа. В этом случае можно воспользоваться режимом Replase.

Режим Display/Modify предназначен для изменения значения небольшого количества байт по различным смещениям. Replase предназначен для изменения любого количества подряд идущих байт.

Данные можно вводить как в символьном так и в шестнадцатеричном формате, и все байты можно ввести за один раз, не ожидая приглажения отладчика. Если вы хотите разместить строку 'Wind0yZ must Die', заканчивающуюся на 0, по адресу 100, то вы должны ввести : E 100 'Wind0yZ must Die' 0

Люди!!! То что вы сейчас сделали, подобно самоубийству!!! вместо МАТРИЦЫ вы только что поимели свою собственную программу. Вы отредактировали ее КОД!!! Причем самым извращенным образом!!!

Если теперь вы введет команду U, то увидите ЭТО:

      15A3:0100 57     PUSH DI 
      15A3:0101 69     DB 69 
      15A3:0102 6E     DB 6E 
      15A3:0103 64     DB 64 
      15A3:0104 30795A XOR [BX+DI+5A],BH 
      15A3:0107 206D75 AND [DI+75],CH 
      15A3:010A 7374   JNB 0180 
      15A3:010C 204469 AND [SI+69],AL 

А если вам не дай Бог взбредет в голову ввести команду G, то... Ну вобщем сами увидите. Как и вкоманде Search, данные в символьном и HEX формате могут чередоваться в любом порядке. Это наиболее удобный способ размещения больших объемов данных в память.

РАЗМЕЩЕНИЕ ДАННЫХ ОДИНАКОВОГО ЗНАЧЕНИЯ

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

Например, чтобы очистить 32К (8000h) памяти, вы всего лишь длжны ввести команду:

  
   -F 0 L 8000 0 <Enter>

В итоге все байты памяти начиная с DS:0000 и до DS:8000 будут обнулены. Если бы вместо нуля мы ввели '1234' , то память была бы заполнена повторяющейся последовательностью '123412341234', и так далее. Обычно, для задания небольших объемов данных лучше использовать команду Enter, потому что ошибка в длине при вызове команды Fill может наделать много бед. Команда Enter изменяет значения только тех байт, которые вы непосредственно укажете, что позволяет минимизировать вероятность ошибки.

Помедитируйте немного и поехали дальше...

ПРОДОЛЖЕНИЕ СЛЕДУЕТ!