汇编调试命令debug的详细用法

  在学习16位汇编时,debug是常用的调试工具,但很多基本用法,帮助中并未提及。本文中的基本概念是指命令语法中需要提供给命令的参数,在命令帮助中并未提供任何提示。经查资料和试验,特记录如下:

  一、基本概念:

  DOS5.0之前debug是com格式,从5.0之后就改为exe格式了,本文是指的是DOS6.22中的debug.exe格式。debug可加载exe或com文件,并显示内存中内容或变量值,还可以显示cpu的寄存器值。可以显示可执行文件的机器码(汇编的方式),只支持8086/8088(8087)指令,并且可以通过单步执行汇编代码,检测程序的逻辑错误,并可修改汇编代码或内存数据。

  如果想更多支持,可使用DR-DOS7.0自带的debug,可支持到pentium指令级,更多选择:Microsoft的CodeView、Borland的TurboDebug或Debug32等。

  1、命令语法:

  dos下输入:debug 可执行文件名称,然后进入命令提示符界面(横线-),输入:命令 参数。

  例如:

  c:\>debug hello.exe(回车)

  -?(回车)  显示帮助

  -r(回车)   显示cpu寄存器值

  

  2、参数分隔符

  debug中参数之间的分隔符可用“空格”或“逗号”,效果一致,如:

  -u 100 110等于-u 100,110

  -d 0100:0000 0010等于-d 0100:0000,0010

  3、value的表示

  无论是debug的参数中含有的地址,立即数或汇编代码中的数字,都是十六进制,无需加前缀或后缀,范围只能是0~4位。

  字符需要引号,如:'a'

  4、debug命令和代码不区分大小写。

  5、地址(address)表示

  1)完全地址形式(段地址segment:偏移地址offset):段地址可通过段寄存器名称和立即地址表示,如地址可通过DS:0010和0100:0010两种方式表示。

  2)默认段地址形式(偏移地址offset):-d 0100(默认段地址是DS)。

  3)部分指令,参数中地址的形式是:=address,这是因为参数中可添加多个数值参数,为了区分,其中一个用=address的方式。如:-g =0003 0008(0003代表开始地址,0008代表断点偏移地址)

  6、range的写法

  参数range有2种写法:

  1)开始地址和结束地址,如:

  -d 0100:0000 0010(开始地址与结束地址位于同一个段,不能跨段)

  -d 0000 0010 (起始都是默认段地址DS)

  2)开始地址和长度,如:

  -d 0100:0000 L 0080(开始地址和长度,字母L大小写均可)

  7、list的写法

  用分隔符把各项罗列出来,如-f 0100:0110 1f 2f 3f ff(用数值列表轮流填充)

  8、string的表示

  'string'或"string"都行,表示一串字符,实质相当于字符list

  9、路径path规范

  文件的路径可写成完整路径,如当前目录,可省略路径

  10、加载汇编代码

  可通过debug < hello.asm的方式加载命令行代码,就相当于手动输入每一行(最后必须有Q命令)。当然,debug < hello.asm > output.txt,也可将输出的信息发送到文件output.txt。

;hello.asm文件
a 100
mov ax,5
mov bx,10
add ax,bx
int 20
(blank line)
Q

  11、Debug初始状态

  Debug加载时,Dos位于内存的最低地址(640K)端,紧接着是Debug,最后是debug载入的程序;

  所有段寄存器都设置成可用内存的最低段地址处;

  IP设置成0100h(为psp留空间);

  当前段的最后256字节留给栈;

  BX:CX的值代表载入程序的长度(BX代表高字节,CX代表低字节);

  Flag寄存器值:NV UP EI PL NZ NA PO NC

  二、命令用法:

  首先Q命令,-q(回车),退出debug命令。

  1、N(指定文件名称)

  语法:n [pathname] [arglist]

  pathname指定写出(W)或载入(L)的文件名称(包括路径和文件名),如载入hello.exe。

  arglist相当于执行上述hello执行时需要的参数(本例没有),如dir c:\中c:\就是arglist。debug是需要将程序载入后模拟执行的,所以如果执行程序所需参数也要加上。

  -N c:\c\hello.exe
  -L

  也可直接通过debug c:\c\hello.exe载入。

  2、R(寄存器操作)

  语法:r [register](r和reg之间的空格可以省略)

  R显示所有寄存器的值,R reg显示置顶寄存器得值,并可修改其值。reg指16位寄存AX、CS、F(flag寄存器)等,不能操作al,ah非16位寄存器。

-r(显示所有寄存器的值)
-r ax(显示ax的值,并在冒号:处提示输入新值,直接回车则表示不修改)
ax 0000
:
-r f(显示标志寄存器的值,并在断线-处提示输入新值,直接回车则表示不修改)
NV UP EI PL NZ NA PO NC  -ZR AC(新值是表示每个标志位的值,顺序不限,值个数不限)

  标志位的值对应的代码如下:

            标志位            设置1            设置0
 ---------------          --------------- -------------------
       Overflow   of  =    OV (OVerflow)   NV [No oVerflow]

      Direction   df  =    DN (decrement)  UP (increment)

      Interrupt   if  =    EI  (enabled)   DI (disabled)

           Sign   sf  =    NG (negative)   PL (positive)

           Zero   zf  =    ZR   [zero]     NZ  [ Not zero]

Auxiliary Carry   af  =    AC              NA  [ No AC ]

         Parity   pf  =    PE  (even)      PO  (odd)

          Carry   cf  =    CY  [Carry]     NC  [ No Carry]

  3、D(显示块数据)

  语法:d [range]

  不指定range,默认当前位置开始(一开始是DS:0000),显示128个字节的数据;可指定range,range语法参考文章开头。

-d 0100:0000 0010(结束地址段地址与开始相同,不能跨段)
-d 0000 0010 (起始都是默认段地址)
-d 0100:0000 L 0080(开始地址和长度)

  4、E(修改块数据)

  语法:d address [list]

  默认段为DS。

  如果不指定list就根据提示(提示显示的是当前地址的原数据,原点后输入新数据),从指定地址address开始,逐字节输入数据,每输入一个字节按一次空格进行下一次输入。要停止输入时直接按回车。

  或通过list,直接指定数据(用空格隔开)或字符串(单引号或双引号)。

-d 0(从偏移地址0处,手动输入,原数据是0e,圆点.后输入新数据)
4dc5:0000 0e.
-d 0 "hello"(从偏移地址0处,依次数据为字符h e l l o)
-d 0 12 34 56 ef

  5、U(反编译)

  语法:d [range]

  默认段为CS。从指定地址处开始将机器码转换成汇编代码,地址必须是合理的地址(因机器码可以看成数据,也可以看成代码)。

4ddd:0000 B8DC4D mov ax,4DDC
4ddd:0003 8ed8   mov ds,ax
4ddd:0005 B440   mov ax,40

  如上代码,此处就不能从0002处开始反汇编,虽然也能解析成代码,但后面的代码都没有意义。

  如果range中通过L指定反编译的范围,L后面的数值是大概指字节数,为什么是“大概”,因为指定长度的位置的代码可能没有完全显示当前的指令,需要在向后显示几个字节。

  如上代码:u 0000 L 4就需要再向后一个字节,因为该处的代码没显示完。

  6、A(输入汇编代码)

  语法:a [address]

  默认段为CS。

  从指定地址address处写汇编代码,只支持8086/8088(8087)指令,也支持db,dw两个伪指令,最后一行直接回车结束汇编代码输入模式。

-a 100
4ddd:1000  mov ax,4ddc
4ddd:1003  mov ds,ax
4ddd:1005 (直接回车结束编辑)
-

-a 110
-4ddd:0110 db "hello" (定义数据)

  7、G(输入汇编代码)

  语法:g [=address]  [addresses]

  默认段为CS,不指定参数=address则表示从当前位置,不指定参数addresses则表示执行到程序最后。

  =address位起点地址,起点位置要是有效的位置(原因如同u指令);addresses为断点位置,可以指定10个,多个断点间用空格分割。不指定断点则执行到程序最后。

  因为2个都是地址,为了区分,前一个加等号=。

  断点只一次有效,为什么要指定多个断点呢,因为可能不确定程序的执行方向,多设置几个断点。

  程序执行完毕后,再g则退出debug。

-g =0002 0008 0011
从0002处执行,可在0008或0011处中断,先执行到哪就在哪停止,执行完毕后清除断点,下一次g指令时,这些断点无效。

  8、P(单步或指定步数)

  语法:p [=address]  [number]

  默认段为CS,不指定参数=address则表示从当前位置,不指定参数number则表示执行1步。

  =address位起点地址,起点位置要是有效的位置(原因如同u指令);

  number是运行指令的条数,不指定,默认是1条。

  p指令遇到子程序或中断程序,直接运行子程序和中断程序,不进入子程序或中断程序内部单步执行,相当于调试高级程序中的Step Over

  9、T(单步或指定步数

  语法:t [=address]  [number]

  默认段为CS,不指定参数=address则表示从当前位置,不指定参数number则表示执行1步

  =address位起点地址,起点位置要是有效的位置(原因如同u指令);

  number是运行指令的条数,不指定,默认是1条。

  p指令遇到子程序或中断程序,要单步进入子程序和中断程序内部,相当于调试高级程序中的Step Into

  10、M(复制数据块

  语法:m range  address

  默认段为DS。

  复制range的数据到起始点位address的内存处。如果range用了段前缀,address的段前缀也需指定,否则range和address可能不在一个段。

-m 4ddc:0000 3 5
这里的range所在段是4ddc,adress所在段为ds。
-m 4ddc:0000 0003 4ddc:0005
range和adress在同一个段

  11、S(搜索数据块

  语法:s range list

  默认段为DS。

  搜索到了,显示搜索到的地址,多处显示多处地址。

两种range的写法:
-s fe00:0 L ffff "BIOS"
-s fe00:0 ffff "BIOS"
FE00:0021
FE00:006F

数据区域
-d fe00:0
FE00:0000 41 77 61 72 64 20 53 6F-66 74 77 61 72 65 49 42  Award SoftwareIB
FE00:0010 4D 20 43 4F 4D 50 41 54-49 42 4C 45 20 34 38 36  M COMPATIBLE 486
FE00:0020 20 42 49 4F 53 20 43 4F-50 59 52 49 47 48 54 20   BIOS COPYRIGHT
FE00:0030 41 77 61 72 64 20 53 6F-66 74 77 61 72 65 20 49  Award Software I
FE00:0040 6E 63 2E 6F 66 74 77 61-72 65 20 49 6E 63 2E 20  nc.oftware Inc.
FE00:0050 41 77 03 0C 04 01 01 6F-66 74 77 E9 12 14 20 43  Aw.....oftw... C
FE00:0060 1B 41 77 61 72 64 20 4D-6F 64 75 6C 61 72 20 42  .Award Modular B
FE00:0070 49 4F 53 20 76 34 2E 35-31 50 47 00 DB 32 EC 33  IOS v4.51PG..2.3

  12、C(比较数据块)

  语法:c range list

  默认段为DS。

  比较两个数据块的数据是否一致,将不一致的地址和各自的数据显示出来。

  

  13、F(填充数据块)

  语法:f range list

  默认段为DS。

  用list数据填充区域range,如果list的长度小于range的长度,则重复填充。跟MASM中的db 100 dup("ha")方式一致,就是填充50个"ha"。

  

  14、I和O(端口读写)

  语法: i port 

      o port byte(byte的值是16位整数)

  对端口进行读写(端口的定义请查阅其他博文),如读取CMOS芯片的小时:

-o 70 04
-i 71
08(当前小时是8)
-

  15、H(16位整数的加减)

  语法: h value1  value2

  计算value1+value2和value1-value2,2个结果同时显示,对标志位不影响。

-h aaa 531   
0FDB  0579(前一个是和,后一个是差)
-         

  16、L(加载数据)

  语法:L [address] [drive] [firstsector] [number]

  如程序执行完毕,可通过L可再次加载程序,如g指令后可通过L重新开始。

  加载有3种形式,BX和CX共同表示文件长度,如文件长123456H字节,BX=0012H,CX=3456H。

  L(直接加载N指定的文件到CS:0100)

  L DS:200(加载N指定的文件到DS:0200

  L 100 2 A 5(从C盘0Ah逻辑扇区开始加载5个扇区到CS:0100,L address drvie firstSector number,drive从0开始表示A盘,1=B、2=C以此类推,每个扇区521Byte)

  MBR储存在C盘0扇区,可加载查看(MBR最后2个字节是55AA,地址为0100+01FF)

  

  17、W(向磁盘写数据)

  语法:w [address] [drive] [firstsector] [number]

  使用方法基本同L。

  BX和CX共同表示文件长度,如文件长123456H字节,BX=0012H,CX=3456H;执行W时需通过R指令设置BX、CX数值。

  R BX 0(设置文件长度)

  R CX 20(设置文件长度)

  W(从CS:0100开始,写20H字节到文件)

  W 0(从CS:0000开始,写20H字节到文件)

  W DS:200(从DS:0200开始,写20H字节到文件

  W 100 2 0 1(从CS:0100开始,写1个扇区数据,到C盘0逻辑扇区。可以用来写MBR程序到磁盘或软盘

  18、XA、XD、XM、XS(内存操作)

  语法: 

  未完待续...

posted @ 2023-08-28 23:02  美洲象  阅读(1443)  评论(0编辑  收藏  举报