中国DOS联盟论坛

中国DOS联盟

-- 联合DOS 推动DOS 发展DOS --
联盟域名:www.cn-dos.net 论坛域名:www.cn-dos.net/forum
游客 | 登录 | 注册 | 会员 | 搜索 | 中国DOS联盟
中国DOS联盟论坛
现在时间是 2026-08-07 16:57
48,041 主题排行 / 350,128 发帖 / 今日 3 篇 / 48,252 会员排行
网络日志(Blog) » 【Redtek】 个人网志(学习笔记)
可打印版本  29,510 / 130
第76楼 redtek 发表于 2006-12-29 22:32
金牌会员 发帖 1,147 积分 2,902
【80x86汇编语言学习笔记】
转移指令

可以修改IP或同时修改CS和IP的指令统称为转移指令。
转移指令就是可以控制CPU执行内存中某处的代码的指令……


短转移IP的修改范围: -128~127
近转移IP的修改范围: -32768~32767

8086的CPU转移指令分为以下几类:

  无条件转移指令(JMP)
  条件转移指令
  循环指令(LOOP)
  过程
  中断


操作符 offset



Debiug ……


0B49:0000 B80000 MOV AX,0000
0B49:0003 B80300 MOV AX,0003


offset操作符取得了标号start和s的偏移地址。


复制start指令到s0处



复制的过程……


-r
AX=0000 BX=0000 CX=0010 DX=0000 SP=0000 BP=0000 SI=0000 DI=0000
DS=0B39 ES=0B39 SS=0B49 CS=0B49 IP=0000 NV UP EI PL NZ NA PO NC
0B49:0000 8BC3 MOV AX,BX
-t

AX=0000 BX=0000 CX=0010 DX=0000 SP=0000 BP=0000 SI=0000 DI=0000
DS=0B39 ES=0B39 SS=0B49 CS=0B49 IP=0002 NV UP EI PL NZ NA PO NC
0B49:0002 BE0000 MOV SI,0000
-t

AX=0000 BX=0000 CX=0010 DX=0000 SP=0000 BP=0000 SI=0000 DI=0000
DS=0B39 ES=0B39 SS=0B49 CS=0B49 IP=0005 NV UP EI PL NZ NA PO NC
0B49:0005 BF0E00 MOV DI,000E
-t

AX=0000 BX=0000 CX=0010 DX=0000 SP=0000 BP=0000 SI=0000 DI=000E
DS=0B39 ES=0B39 SS=0B49 CS=0B49 IP=0008 NV UP EI PL NZ NA PO NC
0B49:0008 2E CS:
0B49:0009 8B04 MOV AX, CS:0000=C38B
-t

AX=C38B BX=0000 CX=0010 DX=0000 SP=0000 BP=0000 SI=0000 DI=000E
DS=0B39 ES=0B39 SS=0B49 CS=0B49 IP=000B NV UP EI PL NZ NA PO NC
0B49:000B 2E CS:
0B49:000C 8905 MOV ,AX CS:000E=9090
-t

AX=C38B BX=0000 CX=0010 DX=0000 SP=0000 BP=0000 SI=0000 DI=000E
DS=0B39 ES=0B39 SS=0B49 CS=0B49 IP=000E NV UP EI PL NZ NA PO NC
0B49:000E 8BC3 MOV AX,BX
-t

AX=0000 BX=0000 CX=0010 DX=0000 SP=0000 BP=0000 SI=0000 DI=000E
DS=0B39 ES=0B39 SS=0B49 CS=0B49 IP=0010 NV UP EI PL NZ NA PO NC



JMP指令

段间转移、段内短转移、段内近转移

范围是指字节。

jmp short s  段内短转移
jmp near ptr 段内近转移
jmp far ptr s 段间转移

段间转移




0B49:0000 B80000 MOV AX,0000
0B49:0003 BB0000 MOV BX,0000
0B49:0006 EA0B01490B JMP 0B49:010B


机器码: EA0B01490B 是目的地址在指令中的存储顺序。
(存放次序->低地址:高地址)即逆序存放。

低地址16位,高地址16位。
高地址代表转移的段地址,低地址代表偏移量。
0B49:010B 目标转移地址在机器码中段地址与偏移地址是逆序存放,段与偏移地址中的高位与低位也是逆序存放~:)

看来编译器在将指令(不管是高级语言还是汇编),最终全是机器码。
只不过这个机器码,不同的机器码代表不同的指令,机器码后面跟着数据,
这个数据是前或后(带符号数)或是所赋的值,然后CPU执行到当前指令时将当前IP+当前指令的长度,就算出下一条指令的地址,然后将 ip指过去。
这个时候CPU再执行已取回的指令,然后执行。
当遇到要转移的指令时,算出要转移到哪里,然后再次修改IP的值指向转移的地址(无论它是8位还是16位还是32位-段间转移),
然后执行IP所指向的指令。
如果当前执行的指令不是转移指令,则执行完后就执行上一次取这次指令时已修改的IP的值,所以它直接执行就行了。

如果用批处理做一个极简单的编译器,那么一个是机器码(这是固定),机器码后面跟着不同的数据,需要编译器来计算它们前后的位移量。

理论上可以实现(至于批处理文件--文本文件无法直接存二进制数据),
但也不是没有办法~:)
且VBS可以向文件写入二进制数据,那么完全可以用VBS做编译器。
乱想……

简单的说,段内转移,段间转移,近转移这些名词,实际上只不过不同的机器码后面跟着8位(段内近转移),它是带符号数,也就是表示这么多寻址能力,当然是近转移。在CPU看来,只不过是 机器码后面跟着一个8位地址。

而近转移,只不过就是一个机器码后面跟着一个16位的地址数据。
16位所能表示出来的带符号数也就那么多。机器还是一样。

而被MASM编译器喜欢的语法,什么JMP FAR PTR 的段间转移,看这指令太长,到了机器码那里还不是: 机器码,后面跟着32位的地址数据。
而这个地址数据还是按照INTEL喜欢的逆序存放,低位后面是高位。
而那个32位的转移地址也是一样,先低后高,不管它什么段地址:偏移量,
到了最后还是段*16+偏移量,所以那个高位(后面的)自然就是表示段,用低位表示偏移量。反正到了CPU那里最后还是物理地址,这些段和偏移量只不过是读着方便和编程方便,它是给我们方便而看的。

按照书上的解释再举一反三的拔开那些迷雾……


寻找机器码,先看看 mov 指令的机器码与寄存器它们的表示:


Debug分析如下:

C:\Masm50>debug
-A
0AF5:0100 MOV AX,0
0AF5:0103 MOV BX,0
0AF5:0106 MOV CX,0
0AF5:0109 MOV DX,0
0AF5:010C

-U 100 109
0AF5:0100 B80000 MOV AX,0000
0AF5:0103 BB0000 MOV BX,0000
0AF5:0106 B90000 MOV CX,0000
0AF5:0109 BA0000 MOV DX,0000


得到 MOV AX 的机器码是 B8
   MOV BX   …… 是 BB
   MOV CX   …… 是 B9
   MOV DX   …… 是 BA (这些将来被CPU执行的时候都是二进制0011..所表示的“高”、“低”的电讯号…(先瞎想)

假如要开发向BX寄存器赋 0123H (23存在低位,01存在高位),它们存储状态的表示是 2301H,先读高位再读低位是为了视觉上的方便。而Inter要表示的是低位内存地址存低位,高位内存地址存高位。

现在写 MOV CX 这一个命令的编译器。向CX寄存器赋值 0123H(10进制291)。……(略……)
调用Debug……
理论上相当于

MOV AX,0123


理论上是这样,但Echo不能直接写入二进制到文件。什么语言能写,什么语言就能够实现。

或是



用Debug装入还是 mov ax,0123


转移地址在内存中的jmp指令的两种格式

从内存单元地址处开始存放着一个字,是转移的目的偏移地址。


1) jmp word ptr 内存单元地址(段内转移)

2) jmp dword ptr 内存单元地址(段间转移)



Debug 对比指令


C:\Masm50>debug p170.exe
-r
AX=0000 BX=0000 CX=0015 DX=0000 SP=0000 BP=0000 SI=0000 DI=0000
DS=0B39 ES=0B39 SS=0B49 CS=0B49 IP=0000 NV UP EI PL NZ NA PO NC
0B49:0000 B82301 MOV AX,0123
-u
0B49:0000 B82301 MOV AX,0123
0B49:0003 A30000 MOV ,AX
0B49:0006 C70602000000 MOV WORD PTR ,0000
0B49:000C FF2E0000 JMP FAR
0B49:0010 B8004C MOV AX,4C00
0B49:0013 CD21 INT 21
0B49:0015 05508D ADD AX,8D50
0B49:0018 46 INC SI
0B49:0019 8050E83E ADC BYTE PTR ,3E
0B49:001D 0D83C4 OR AX,C483
-t

AX=0123 BX=0000 CX=0015 DX=0000 SP=0000 BP=0000 SI=0000 DI=0000
DS=0B39 ES=0B39 SS=0B49 CS=0B49 IP=0003 NV UP EI PL NZ NA PO NC
0B49:0003 A30000 MOV ,AX DS:0000=20CD
-t

AX=0123 BX=0000 CX=0015 DX=0000 SP=0000 BP=0000 SI=0000 DI=0000
DS=0B39 ES=0B39 SS=0B49 CS=0B49 IP=0006 NV UP EI PL NZ NA PO NC
0B49:0006 C70602000000 MOV WORD PTR ,0000 DS:0002=9FFF
-t

AX=0123 BX=0000 CX=0015 DX=0000 SP=0000 BP=0000 SI=0000 DI=0000
DS=0B39 ES=0B39 SS=0B49 CS=0B49 IP=000C NV UP EI PL NZ NA PO NC
0B49:000C FF2E0000 JMP FAR DS:0000=0123
-t

AX=0123 BX=0000 CX=0015 DX=0000 SP=0000 BP=0000 SI=0000 DI=0000
DS=0B39 ES=0B39 SS=0B49 CS=0000 IP=0123 NV UP EI PL NZ NA PO NC
0000:0123 F0 LOCK
0000:0124 F3 REPZ
0000:0125 EE OUT DX,AL
-q



使程序中的jmp指令执行后,CS:IP指向程序的第一条指令,在data段中应该定义哪些数据?


assume cs:code

data segment
db 0ffh ; 界标置
dw 0
db 0ffh ; 边界标置~:)

data ends

code segment
start: mov ax,data
mov ds,ax
mov bx,0
jmp word ptr

code ends
end start



使jmp指令执行后,CS:IP指向程序的第一条指令



假如内存 2000:1000 BE 00 06 00 00 00 ...
跟据下面指令(不运行代码)直接写出结果 (CS)=? IP=?




低位:高位,所以 CS=0006,IP=00BE


JCZX指令

cx=0时退出,8位位移,相当于 if((cx)==0) jmp short 标号。

利用jcxz指令,实现在内存2000H段中查找第一个值为0的字节,找到后将它的偏移地址存储在dx中。




运行前提交“安排”数据



Loop指令

(cx)=(cx)-1 if (cx) 不等于 0 ... (ip)=(ip)+8位有符号数位移。
相当于C表示的: (cx) -- ;

Debug分析代码执行过程……




写视频缓冲区(直接写屏)彩色字符串



运行视图

内存地址: B8000H~~BFFFFH 共32KB的空间,为80*25彩色字符模式的显示缓冲区。它分为8页。

低位:字符ASCII,高位:色值(RGB)二进制表示0~7个位的“开关”。

[ Last edited by redtek on 2006-12-30 at 01:32 PM ]

附件
1.GIF (6.08 KiB)
第77楼 redtek 发表于 2006-12-31 03:57
金牌会员 发帖 1,147 积分 2,902
【80x86汇编语言学习笔记】
call和ret指令

ret指令用栈中的数据,修改ip的内容,从而实现近转移;
retf指令用栈中的数据,修改cs和ip的内容,从而实现远转移。

CPU执行ret指令时,进行下面两步操作:

  (ip)=((ss)*16+(sp))
  (sp)=(sp)+2


CPU执行retf指令时:进行下面四步操作:

  (ip)=((ss)*16+(sp))
  (sp)=(sp)+2
  (cs)=((ss)*16+(sp))
  (sp)=(sp)+2


CPU执行ret指令时,相当于进行:
  pop ip

CPU执行retf指令时,相当于进行:
  pop ip
  pop cs

ret



retf使用





AX=0000 BX=0000 CX=0026 DX=0000 SP=000C BP=0000 SI=0000 DI=0000
DS=0B39 ES=0B39 SS=0B49 CS=0B4A IP=0012 NV UP EI PL NZ NA PO NC
0B4A:0012 BB0000 MOV BX,0000
-t

AX=0000 BX=0000 CX=0026 DX=0000 SP=000C BP=0000 SI=0000 DI=0000
DS=0B39 ES=0B39 SS=0B49 CS=0B4A IP=0015 NV UP EI PL NZ NA PO NC
0B4A:0015 CB RETF
-t

AX=0000 BX=0000 CX=0026 DX=0000 SP=0010 BP=0000 SI=0000 DI=0000
DS=0B39 ES=0B39 SS=0B49 CS=0B4A IP=0000 NV UP EI PL NZ NA PO NC
0B4A:0000 B8004C MOV AX,4C00


根据PUSH压栈次序对调发现:

当执行retf时,CPU处理的时候是先计算低位,再计算高位。
所以,压栈是先进后出,所以要先压高位再压低位,才可以保证retf执行时先恢复高位后恢复低位,否则出错(相返)。


实现从内存 1000:0000 处开始执行指令




Debug跟踪结果


AX=0000 BX=0000 CX=0021 DX=0000 SP=0000 BP=0000 SI=0000 DI=0000
DS=0B39 ES=0B39 SS=0B49 CS=0B4A IP=0000 NV UP EI PL NZ NA PO NC
0B4A:0000 B8490B MOV AX,0B49
-t

AX=0B49 BX=0000 CX=0021 DX=0000 SP=0000 BP=0000 SI=0000 DI=0000
DS=0B39 ES=0B39 SS=0B49 CS=0B4A IP=0003 NV UP EI PL NZ NA PO NC
0B4A:0003 8ED0 MOV SS,AX
-t

AX=0B49 BX=0000 CX=0021 DX=0000 SP=0010 BP=0000 SI=0000 DI=0000
DS=0B39 ES=0B39 SS=0B49 CS=0B4A IP=0008 NV UP EI PL NZ NA PO NC
0B4A:0008 B80010 MOV AX,1000
-t

AX=1000 BX=0000 CX=0021 DX=0000 SP=0010 BP=0000 SI=0000 DI=0000
DS=0B39 ES=0B39 SS=0B49 CS=0B4A IP=000B NV UP EI PL NZ NA PO NC
0B4A:000B 50 PUSH AX
-t

AX=1000 BX=0000 CX=0021 DX=0000 SP=000E BP=0000 SI=0000 DI=0000
DS=0B39 ES=0B39 SS=0B49 CS=0B4A IP=000C NV UP EI PL NZ NA PO NC
0B4A:000C B80000 MOV AX,0000
-t

AX=0000 BX=0000 CX=0021 DX=0000 SP=000E BP=0000 SI=0000 DI=0000
DS=0B39 ES=0B39 SS=0B49 CS=0B4A IP=000F NV UP EI PL NZ NA PO NC
0B4A:000F 50 PUSH AX
-t

AX=0000 BX=0000 CX=0021 DX=0000 SP=000C BP=0000 SI=0000 DI=0000
DS=0B39 ES=0B39 SS=0B49 CS=0B4A IP=0010 NV UP EI PL NZ NA PO NC
0B4A:0010 CB RETF
-t

AX=0000 BX=0000 CX=0021 DX=0000 SP=0010 BP=0000 SI=0000 DI=0000
DS=0B39 ES=0B39 SS=0B49 CS=1000 IP=0000 NV UP EI PL NZ NA PO NC
1000:0000 83D200 ADC DX,+00



call指令

CPU执行call指令时,进行两步操作:

  将当前的IP或CS和IP压入栈中;
  转移。


call不能实现短转移。call指令实现转移的方法和jmp指令的原理相同。


call 标号(将当前的IP压栈后,转到标号处执行指令)

CPU执行此种格式的call指令时,进行如下操作:

  1) (sp)=(sp-2)
     (ss)*16+(sp)=(ip)
  2) (IP)=(IP)+16位位移

16位位移由编译程序在编译时算出

相当于 CPU 执行指令 "call 标号" 时进行:
  
  push IP
  jmp near ptr 标号


下面程序执行后,ax中的数值为多少?



答:执行过程如下:
  
  1) CPU读入 mov ax,0 ,然后将 ip 指向下一条指令
  2) 执行 mov ax,0 以后,读入当前ip所指向的指令(call s)
  3) 读入 call s,然后将ip指向下一条指令 inc ax(它的IP是6)
  3) 执行 call s 指令-》将当前IP值压栈(6),然后执行跳转……S标号……
  4) CPU读入指令 pop ax ,将ip指向下一条指令。
  5) ……最后 AX=0006 

表面上看上去不应该是6,而应该是当前 call s 所在的指令ip地址,但实际上按照CPU执行以及设置IP的次序,它依上述所示。


转移的目的地址在指令中的call指令


指令 "call far ptr 标号" 实现的是段间转移。

CPU执行此种格式的call指令时,进行如下操作:

  1) (sp)=(sp)-2       先压高位
     ((ss)*16+(sp))=(cs)
     (sp)=(sp)-2
     ((ss)*16+(sp))=(IP)   再压低位
  2) (CS)=标号所在段的段地址
     (IP)=标号所在段的偏多地址

相当于:

  push CS
  push IP
  jmp far ptr 标号


分析




转移地址在寄存器中的call指令


指令格式: call 16位寄存器


功能:

  (sp)=(sp-2)
  ((ss)*16+(sp))=(IP)
  (IP)=(16位寄存器)

CPU执行 call 16位reg时,相当于进行:

  push IP
  jmp 16位寄存器

实验



1) 压当前IP到栈(只压IP),压了ip是为了Call完以后回来时执行ip指向的地址指令
2) jmp 取指令16位寄存器中要求跳转的地址




转移地址在内存中的call指令


两种格式:

1) call word ptr 内存单元地址
2) call dword ptr 内存单元地址]

这两种格式都是先压入IP,然后取要跳转的指定内存单元地址。

call word ptr ...




call dword ptr 内存单元地址

dword是32位,所以所取的内存单元地址中足够“包含”(段:偏移量)地址。
但CPU处理次序是先低位再高位,所以必须先压(段)再压(偏移:就是IP),当CPU处理时才可以先处理低地址再高地址

所以,dword内存单元中(双字)排列次序:低位:高位
在sp中存放的是ip地址(16位)(字)

当CPU执行 call dword ptr 内存单元地址时,相当于进行:

  push CS
  push IP
  jmp dword ptr 内存单元地址

即:栈内的数据存的是Call完以后回来时要运行的指令地址(也要保持将来先取到低位再取到高位)
  而内存单元内存的数据(低位:高低)是指要 jmp dword ptr 的地址~:)






最后 bx中的值是8


call和ret配合使用




当执行完“子程序段”时,由 ret 指令将call调用前压入栈中的IP再弹出来,这样就恢复的调用前的IP,程序继续执行~:)

关于call与ret




mul乘法指令

两个相乘的数,要么都是8位,要么都是16位。

计算:

  如果是8位,一个默认放在AH中,另一个放在8位寄存器或是内存字节单元中。
  如果是16位,一个默认在AX中,另一个放在16位寄存器或内存字单元中。

结果:


  如果是8位乘法,结果默认放在AL中。
  如果是16位乘法,结果高位默认放在DX中,低位在AX中存放。

格式: mul reg 或 mul 内存单元


内存单地可以用不同的寻址方式给出:

  mul byte ptr ds:
  含义:(ax)=(dl)*((ds)*16+0)

mul word ptr
含义:(ax)=((ax)*(ds)*16+(bx)+(si)+8) 结果的低16位
   (dx)=((ax)*(ds)*16+(bx)+(si)+8) 结果的高16位

计算 100*10000




模块化程序设计
参数和结果传递的问题

计算data段中第一组数据的3次方,结果保存在后面一组dword单元中



结果


-d ds:0
0B49:0000 01 00 02 00 03 00 04 00-05 00 06 00 07 00 08 00
0B49:0010 01 00 00 00 08 00 00 00-1B 00 00 00 40 00 00 00
0B49:0020 7D 00 00 00 D8 00 00 00-57 01 00 00 00 02 00 00


批量数据传送

将批量数据放到内存中,然后将它们所在的内存空间的首地址放在寄存器中,传递给需要的子程序。

即,相当于是按址传送数据。刚才上面的寄存器传送最终结果,相当于按值似递。在c、vb中均有类似的说明~:0


将data段中的字符串转为大写



Debug 过程


C:\Masm50>debug p190.exe
-r
AX=0000 BX=0000 CX=002A DX=0000 SP=0000 BP=0000 SI=0000 DI=0000
DS=0B39 ES=0B39 SS=0B49 CS=0B4A IP=0000 NV UP EI PL NZ NA PO NC
0B4A:0000 B8490B MOV AX,0B49
-u
0B4A:0000 B8490B MOV AX,0B49
0B4A:0003 8ED8 MOV DS,AX
0B4A:0005 BE0000 MOV SI,0000
0B4A:0008 B90C00 MOV CX,000C
0B4A:000B E80500 CALL 0013
0B4A:000E B8004C MOV AX,4C00
0B4A:0011 CD15 INT 15
0B4A:0013 8024DF AND BYTE PTR ,DF
0B4A:0016 46 INC SI
0B4A:0017 E2FA LOOP 0013
0B4A:0019 C3 RET
0B4A:001A E1E2 LOOPZ FFFE
0B4A:001C F786E1E8DDE2 TEST WORD PTR ,E2DD
-g 000e

AX=0B49 BX=0000 CX=0000 DX=0000 SP=0000 BP=0000 SI=000C DI=0000
DS=0B49 ES=0B39 SS=0B49 CS=0B4A IP=000E NV UP EI PL NZ NA PE NC
0B4A:000E B8004C MOV AX,4C00
-d ss:0
0B49:0000 43 4F 4E 56 45 52 53 41-54 49 4F 4E 00 00 00 00 CONVERSATION....



编写子程序注意的问题:

  子程序开始: 子程序中使用的寄存器入栈
         子程序内容
         子程序中使用的寄存器出栈
         返回(ret,retf)

子程序例程:用子程序转所有字母为大写



上面子程序内,使用栈先保存当前寄存器状态,然后再执行子程序内部计算过程,计算完后再弹出保存的值,不与调用者冲突。


-d ss:0
0B49:0000 57 4F 52 44 00 55 4E 49-58 00 57 49 4E 44 00 47 WORD.UNIX.WIND.G
0B49:0010 4F 4F 44 00 00 00 00 00-00 00 00 00 00 00 00 00 OOD.............



编写子程序

要求:在指定的位置,用指定的颜色,显示一个用0结束的字符串。
   dh=行号(取值0~24),dl=列号(取值范围0~79)
   cl=颜色, ds:si指向字符串的首地址

   在屏幕的8行3列,用绿色显示 data 段中的字符串。



上面显示方式是直接写显存(直接写屏)方式,在CMD下运行编译的.exe无效,Debug内运行有效。MS-DOS 环境下直接运行编译的.exe文件有效。

[ Last edited by redtek on 2007-1-1 at 02:02 PM ]

附件
1.GIF (11.55 KiB)
第78楼 redtek 发表于 2007-01-02 04:04
金牌会员 发帖 1,147 积分 2,902
【80x86汇编语言学习笔记】
解决除法溢出问题

例如下面代码将会溢出:(1000 / 1)




子程序描述:

  名称: divdw
  功能: 进行不会产生溢出的除法运算。被除数为dword型,除数为word型,结果为dword型。

下面公式将可能产生溢出的除法运算,转变为多个不会产生溢出的除法运算。

公式: X/N=int(H/N)*6636 + /N]

    商:(被除数高16位 / 除数) * 65536 + / 除数


返回:(dx)=结果的高16位
   (ax)=结果的低16位
   (cx)=余数


根据公式写出子程序以及完整代码:




结果



[ Last edited by redtek on 2007-1-3 at 01:46 PM ]
第79楼 220110 发表于 2007-01-02 12:09
荣誉版主 发帖 313 积分 718
推荐你看<<黑客反调试技术>>.

PS:这不是一本黑客技术的书,着重是反调试技术.从正常软件生产来看掌握调试技术是必要的,但从不道德的角度,反调试就是破解,反编译.这书有相当的难度.
第80楼 redtek 发表于 2007-01-04 02:37
金牌会员 发帖 1,147 积分 2,902
非常感谢220110版主推荐~:)

在当当网找到一本《黑客反汇编揭秘》,不知道是不是这本书?

还有一本叫做《黑客调试技术揭秘》,作者:(美)卡斯帕克尔。
不知道《黑客反调试技术揭秘》和《黑客调试技术揭秘》这两本是不是指同一本书?

《黑客调试技术揭秘》的目录:



[ Last edited by redtek on 2007-1-3 at 01:43 PM ]
第81楼 redtek 发表于 2007-01-04 03:22
金牌会员 发帖 1,147 积分 2,902
【80x86汇编语言学习笔记】
子程序

数值显示

将数据用十进制形式显示到屏幕上,进行两步工作:

  1) 将二进制信息存储的数据转变为十进制形式的字符串;
  2) 显示十进制表式的字符串

以数字表示的16进制(如 1 , 2, 3, ... )与数字的ASCII码之间相差30H,即1H:如果希望显示1,则必须1H+30H才等于它的ASCII码。

编程,将数据12666以十进制的形式在屏幕的8行3列,用绿色显示出来。



因为使用了要显示的数字除以10,做为分解的每一个位余数+30H就等于要显示的这个数字的ASCII,所以出来的是倒序了。

(所以相代码未完,需要显示前倒序一次就变成可以显示的正确的数字了)

这回可以正序显示了~:)




[ Last edited by redtek on 2007-1-4 at 04:53 PM ]
第82楼 amao 发表于 2007-01-04 04:08
中级用户 发帖 152 积分 316
对我来说有如看天书啊。
第83楼 redtek 发表于 2007-01-05 03:09
金牌会员 发帖 1,147 积分 2,902
哈哈……
我每往后学一点儿都像看天书一样~:)
不过难就难吧,一天一点儿,总有掌握它的这一天~:)
第84楼 redtek 发表于 2007-01-05 07:10
金牌会员 发帖 1,147 积分 2,902
【80x86汇编语言学习笔记】
实验 寻址方式在结构化数据访问中的应用




代码如下:



运行结果


[ Last edited by redtek on 2007-1-5 at 04:28 PM ]
第85楼 9527 发表于 2007-01-06 00:27
银牌会员 发帖 438 积分 1,185 来自 北京
redtek兄,我也想学学,能把你现在看的书名或者电子书还有网站告诉我吗?期待中ing........
第86楼 redtek 发表于 2007-01-06 03:52
金牌会员 发帖 1,147 积分 2,902
因为书名相同但出版社不同的书很多,所以列出在当当网可以查到的详细关于这本书的介绍信息~:)


(下面这本书如果是自学,极难。整本都是望而生畏的内容,很可能看几眼就再也不想学汇编了。)
(但其内容非常详细,教学方式适合学校有专业老师辅导。)
(但用某些汇编快速入门的书自学一段时间后,还必须要看这本书,因其内容很全)


Intel 80X86/Pentium汇编语言程序设计(第二版)
http://product.dangdang.com/product.aspx?product_id=8936113




(下面这本书适合在没有任何人帮助你的情况下,完全自学汇编语言)
(很少有望而生畏的内容--因全是入门内容)
(不适合学校教学之用--因为适合完全自学,所以此书教学速度极慢)

汇编语言
http://product.dangdang.com/product.aspx?product_id=8732001



(下面这本书内容非常深,从最简单的十几句汇编语言写一个极微小的“操作系统”到完成一个操作系统的每一步底层)
(看完这本书就比较精通操作系统底层并可以自己写较简单的操作系统了……)
(要求熟悉汇编与C语言--但主要以C语言介绍如何写操作系统)


自己动手写操作系统
http://product.dangdang.com/product.aspx?product_id=9024751




(希望精通磁盘和准备自己从头到尾的写一个Linux操作系统出来,看这本书)

Linux 0.01内核分析与操作系统设计(附CD—ROM光盘一张)——创作你自己的操作系统
http://product.dangdang.com/product.aspx?product_id=8956136




建议不要找电子版的书。电子版的书不适合出门带着看、爬在床上看、坐在椅子上在任何地方看、随时翻着看……

其它资料就是lxmxn在这个网志里介绍的那个网站和Google.com上可以找到的网站~:)

一定要花钱买,强迫自己要不就学出来!要不就白花钱惩罚自己~:)
如果不强迫自己花钱买,很可能遇到难的地方就主动放弃了~:)
第87楼 9527 发表于 2007-01-06 04:11
银牌会员 发帖 438 积分 1,185 来自 北京
谢谢redtek兄提供学习资料,是啊,有时候不给自己一点压力是不成啦,会变的很懒散很颓废很是没用啊
第88楼 redtek 发表于 2007-01-06 04:17
金牌会员 发帖 1,147 积分 2,902
【80x86汇编语言学习笔记】
标置寄存器

ZF标志
flag的第6位,记录相关指令执行后其结果是否为0。0-ZF=1 1-ZF=0

add,mul,div,inc,or,and等运算指令,都会影响标志寄存器。
mov,push,pop等指令对标志寄存器没有影响。

PF标志
flag的第2位是PF,奇偶标志位。记录相关指令执行后,结果的所有二进制位中1的个数是否为偶数,偶数:PF=1,奇数:PF=0

SF标志
flag的第7位是SF,符号标志位。记录相关指令执行后,结果是否为负。负:SF=1,非负:SF=0
它是对有符号数运算结果的一种记录,它记录数据的正负。对于无符号数据计算,SF的值没有意义。


FLAG标置位寄存器结构:



在8086 CPU中flag的1、3、5、12、13、14、15没有使用。
在Debug中是 “NV UP EI PL NZ NA PO NC” 次序出现的。
其标置为1还是为0,在Debug中是以其它字母缩写表示的。为1或是为0表示的字母都不一样。

例如:第6位ZF标志(是否为0。结果为零=1,结果非零=0)
在 Debug 中表示其“原始”状态,即均为0时的状态:


NV UP EI PL NZ NA PO NC


NZ就是CF标置的当前表示状态。

至于NZ为什么要出现在从右数向左数的第6位(表面上看是第4位),因为是从FLAG寄存器的低位0表示。
而1、3、5位在8086CPU中未使用,所以“空”过它们,ZF标志自然就出现在了表面上的第4位(实际第6位)。

ZF:零标置位。
   当结果为0时,ZF=1,用ZR表示(出现在Debug中)。
   当结果为1时,ZF=0,用NZ表示。


记忆其特点:

当看到ZR时,表示结果为0,ZF零标置位当结果为0时为真,所以ZF=1。
当看到NZ时,表示结果非0,ZF零标置位当结果非0时为伪,所以ZF=0。


写出下面每条指令执行后,ZF、PF、SF等标志位的值




CF标志
flag的第0位是CF,进位标志位。
在进行无符号数运算的时候,它记录了运算结果的最高有效位向更高的进位值,或从更高位的借位值。


-a
0AF5:0100 mov al,97
0AF5:0102 sub al,98
0AF5:0104 sub al,al
0AF5:0106
-r
AX=0000 BX=0000 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=0AF5 ES=0AF5 SS=0AF5 CS=0AF5 IP=0100 NV UP EI PL NZ NA PO NC
0AF5:0100 B097 MOV AL,97
-t

AX=0097 BX=0000 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=0AF5 ES=0AF5 SS=0AF5 CS=0AF5 IP=0102 NV UP EI PL NZ NA PO NC
0AF5:0102 2C98 SUB AL,98
-t

AX=00FF BX=0000 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=0AF5 ES=0AF5 SS=0AF5 CS=0AF5 IP=0104 NV UP EI NG NZ AC PE CY
0AF5:0104 28C0 SUB AL,AL
-t

AX=0000 BX=0000 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=0AF5 ES=0AF5 SS=0AF5 CS=0AF5 IP=0106 NV UP EI PL ZR NA PE NC



OF标志
flag的第11位是OF,溢出标志位。
OF记录了有符号数运算的结果是否发生了溢出。溢出:OF=1,没有溢出:OF=0。
对于有符号数而言。


CPU在执行add等指令的时候,包含了两种含义:

  1) 无符号数运算
     CPU用CF位来记录是否产生了进位。

  2) 有符号数运算
     CPU用OF位来记录是否产生了溢出。
     
     用SF位来记录结果的符号。

写出下面每条指令执行后,ZF、PF、SF、CF、OF等标志位的值。






DF标志和串传送指令

flag的第10位是DF,方向标志位。
在串处理指令中,控制每次操作后si,di的增减。


DF=0  每次操作后SI,DI递增。
DF=1  每次操作后SI,DI递减。



标置     值为1的标记   值为0的标记
------------------------
DF       DN        UP


格式: movsb
功能: 执行movsb指令相当于进行下面几步操作。

1) ((es)*16+(di))=((ds)*16+(si))
2) 如果DF=0则: (si)=(si)+1
            (di)=(di)+1
   如果DF=1则: (si)=(si)-1
            (di)=(di)-1

movsb的功能是将ds:si指向内存单元中的字节送入es:di中,然后根据标志寄存器DF位的值,将si和di递增或递减。

一般movsb(byte)和movsw(word)都和rep配合使用。



用汇编语言来描述 rep movsb 的功能:
  
  s: movsb
loop s



8086 CPU 提供下面两条指令对DF位进行设置:

  cld 指令: 将标志寄存器的DF位置0
  std 指令: 将标志寄存器的DF位置1



例程:将 data 段中的每一个字符串复制到它后面的空间中。



在 Debug 中 cld 与 rep movsb 指令为:




例程:用串传送指令,将F000H段中的最后16个字符复制到data段中。




-d es:0
0B49:0000 EA 5B E0 00 F0 30 34 2F-30 32 2F 30 34 00 FC 00 .



编程:编写一个子程序,将包含任何字符,以0结尾的字符串中的小写字母变成大写字母。

名称: letterc
功能:同上
参数:ds:si 指向字符串首地址

需要进行转化的是字符串中的小写字母‘a’~‘z’,而不是其他字符。

) 小写字母范围: 97 ~~ (97+26-1)
          97 ~~ 122

) 结束标置:   0





调试代码过程中发现一个有意思的错误:)


AX=0B49 BX=0000 CX=0000 DX=0000 SP=FFFE BP=0000 SI=0033 DI=0000
DS=0B49 ES=0B39 SS=0B49 CS=0B4D IP=0024 NV UP EI PL NZ NA PE CY
0B4D:0024 B500 MOV CH,00
-t

AX=0B49 BX=0000 CX=0000 DX=0000 SP=FFFE BP=0000 SI=0033 DI=0000
DS=0B49 ES=0B39 SS=0B49 CS=0B4D IP=0026 NV UP EI PL NZ NA PE CY
0B4D:0026 E2EA LOOP 0012
-d ds:0
0B49:0000 41 42 45 47 49 4E 4E 45-52 27 53 20 41 4C 4C 2D ABEGINNER'S ALL-
0B49:0010 50 55 52 50 4F 53 45 20-53 59 4D 42 4F 4C 49 43 PURPOSE SYMBOLIC
0B49:0020 20 49 4E 53 54 52 55 43-54 49 4F 4E 20 43 4F 44 INSTRUCTION COD
0B49:0030 45 2E 00 00 00 00 00 00-00 00 00 00 00 00 00 00 E...............
0B49:0040 B8 49 0B 8E D8 BE 00 00-E8 05 00 B8 00 4C CD 21 .I...........L.!
0B49:0050 B5 00 8A 0C 80 F9 61 72-0A 80 F9 7A 77 05 80 E1 ......ar...zw...
0B49:0060 DF 88 0C 46 B5 00 E2 EA-C3 2A E4 40 50 8B C3 05 ...F.....*.@P...
0B49:0070 0C 00 52 50 E8 19 46 83-C4 04 50 8D 86 00 FF 50 ..RP..F...P....P
-r
AX=0B49 BX=0000 CX=0000 DX=0000 SP=FFFE BP=0000 SI=0033 DI=0000
DS=0B49 ES=0B39 SS=0B49 CS=0B4D IP=0026 NV UP EI PL NZ NA PE CY
0B4D:0026 E2EA LOOP 0012
-t

AX=0B49 BX=0000 CX=FFFF DX=0000 SP=FFFE BP=0000 SI=0033 DI=0000
DS=0B49 ES=0B39 SS=0B49 CS=0B4D IP=0012 NV UP EI PL NZ NA PE CY


当 loop 在递减 CX 寄存器的值的时候发生的上面错误。

所以,代码中不使用 loop ... cx了,重新使用 jcxz 命令。




遇到一个问题:

在汇编编辑器中,"Beginner”的 'B' 打头字母无论如何更新总是小写方式,后发现在编辑器中 “格式设定” 小写,问题在这里。

将其设置成 “不处理” 即可首字母随意设置。

Debug 调试结果:


-d ss:0
0B49:0000 42 65 67 69 6E 6E 65 72-27 73 20 41 6C 6C 2D 70 Beginner's All-p
0B49:0010 75 72 70 6F 73 65 20 53-79 6D 62 6F 6C 69 63 20 urpose Symbolic
0B49:0020 49 6E 73 74 72 75 63 74-69 6F 6E 20 43 6F 64 65 Instruction Code
0B49:0030 2E 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................


[ Last edited by redtek on 2007-1-8 at 02:45 PM ]
第89楼 redtek 发表于 2007-01-07 07:47
金牌会员 发帖 1,147 积分 2,902
【80x86汇编语言学习笔记】
ADC指令

功能: 操作对象1 = 操作对象1 + 操作对象2 + CF


adc是带位加法指令,它利用了CF位上记录的进位值。

指令 adc ax,bx 实现的功能是: (ax)=(ax)+(bx)+CF


C:\Masm50>debug
-a
0AF5:0100 mov ax,2
0AF5:0103 mov bx,1
0AF5:0106 sub bx,ax
0AF5:0108 adc ax,1
0AF5:010B
-r
AX=0000 BX=0000 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=0AF5 ES=0AF5 SS=0AF5 CS=0AF5 IP=0100 NV UP EI PL NZ NA PO NC
0AF5:0100 B80200 MOV AX,0002
-t

AX=0002 BX=0000 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=0AF5 ES=0AF5 SS=0AF5 CS=0AF5 IP=0103 NV UP EI PL NZ NA PO NC
0AF5:0103 BB0100 MOV BX,0001
-t

AX=0002 BX=0001 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=0AF5 ES=0AF5 SS=0AF5 CS=0AF5 IP=0106 NV UP EI PL NZ NA PO NC
0AF5:0106 29C3 SUB BX,AX
-t

AX=0002 BX=FFFF CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=0AF5 ES=0AF5 SS=0AF5 CS=0AF5 IP=0108 NV UP EI NG NZ AC PE CY
0AF5:0108 150100 ADC AX,0001
-t

(上面:标置寄存器 CF 进位标置:CY代表进位或是借位,在这里是借位)
(当产生借位状态时,CF=1,所以最后结果是 4)


(上面:标置寄存器 SF 符号标置位为: NG(蓝色) 代表负数,其值为 1)


AX=0004 BX=FFFF CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=0AF5 ES=0AF5 SS=0AF5 CS=0AF5 IP=010B NV UP EI PL NZ NA PO NC


adc指令和add指令相配合可以对更大的数据进行加法运算。



例:计算 1EF000H + 201000H,结果放在AX(高16位)和bx(低16位)中。


C:\Masm50>debug
-a
0AF5:0100 mov ax,001e
0AF5:0103 mov bx,f000
0AF5:0106 add bx,1000
0AF5:010A adc ax,0020
0AF5:010D
-r
AX=0000 BX=0000 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=0AF5 ES=0AF5 SS=0AF5 CS=0AF5 IP=0100 NV UP EI PL NZ NA PO NC
0AF5:0100 B81E00 MOV AX,001E
-t

AX=001E BX=0000 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=0AF5 ES=0AF5 SS=0AF5 CS=0AF5 IP=0103 NV UP EI PL NZ NA PO NC
0AF5:0103 BB00F0 MOV BX,F000
-t

AX=001E BX=F000 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=0AF5 ES=0AF5 SS=0AF5 CS=0AF5 IP=0106 NV UP EI PL NZ NA PO NC
0AF5:0106 81C30010 ADD BX,1000
-t

AX=001E BX=0000 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=0AF5 ES=0AF5 SS=0AF5 CS=0AF5 IP=010A NV UP EI PL ZR NA PE CY
0AF5:010A 152000 ADC AX,0020
-t

AX=003F BX=0000 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=0AF5 ES=0AF5 SS=0AF5 CS=0AF5 IP=010D NV UP EI PL NZ NA PE NC


先低位与低位相加,结果保存在低位中。
当低位相加产生了进位时,其CF的值为1,Debug中表示:CY(代表进位状态)

然后 adc 指令:高位与高位相加,其相加结果由 adc 指令会自动再与CF相加,因刚刚有了进位,所以高位存放的是进位后的值。
高位与低位的组成就是计算在数相加的总值。


计算 1EF0001000H + 2010001EF0H ,结果放在AX(最高16位),BX(次高16位),CX(低16位)中。


13BF:0100 mov ax,1e
13BF:0103 mov bx,f000
13BF:0106 mov cx,1000
13BF:0109 add cx,1ef0
13BF:010D adc bx,1000
13BF:0111 adc ax,0020
13BF:0114
-r
AX=0000 BX=0000 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=13BF ES=13BF SS=13BF CS=13BF IP=0100 NV UP EI PL NZ NA PO NC
13BF:0100 B81E00 MOV AX,001E
-t

AX=001E BX=0000 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=13BF ES=13BF SS=13BF CS=13BF IP=0103 NV UP EI PL NZ NA PO NC
13BF:0103 BB00F0 MOV BX,F000
-t

AX=001E BX=F000 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=13BF ES=13BF SS=13BF CS=13BF IP=0106 NV UP EI PL NZ NA PO NC
13BF:0106 B90010 MOV CX,1000
-t

AX=001E BX=F000 CX=1000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=13BF ES=13BF SS=13BF CS=13BF IP=0109 NV UP EI PL NZ NA PO NC
13BF:0109 81C1F01E ADD CX,1EF0
-t

AX=001E BX=F000 CX=2EF0 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=13BF ES=13BF SS=13BF CS=13BF IP=010D NV UP EI PL NZ NA PE NC
13BF:010D 81D30010 ADC BX,1000
-t

AX=001E BX=0000 CX=2EF0 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=13BF ES=13BF SS=13BF CS=13BF IP=0111 NV UP EI PL ZR NA PE CY
13BF:0111 152000 ADC AX,0020
-t

AX=003F BX=0000 CX=2EF0 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=13BF ES=13BF SS=13BF CS=13BF IP=0114 NV UP EI PL NZ NA PE NC



两个1024位数字相加




sbb指令

sbb是带借位减法指令,它利用了CF位上记录的借位值。

功能:操作对象1 = 操作对象1 -操作对象2 -CF

例: sbb ax,bx 实现的功能是: (ax) = (ax) - (bx) - CF

sbb指令执行后,将对CF进行设置。利用sbb指令可以对任意大的数据进行减法运算。


计算 003E1000H -00202000H,结果放在AX,BX中。


C:\Masm50>debug
-a
0AF5:0100 mov bx,1000
0AF5:0103 mov ax,003e
0AF5:0106 sub bx,2000
0AF5:010A sbb ax,0020
0AF5:010D
-r
AX=0000 BX=0000 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=0AF5 ES=0AF5 SS=0AF5 CS=0AF5 IP=0100 NV UP EI PL NZ NA PO NC
0AF5:0100 BB0010 MOV BX,1000
-t

AX=0000 BX=1000 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=0AF5 ES=0AF5 SS=0AF5 CS=0AF5 IP=0103 NV UP EI PL NZ NA PO NC
0AF5:0103 B83E00 MOV AX,003E
-t

AX=003E BX=1000 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=0AF5 ES=0AF5 SS=0AF5 CS=0AF5 IP=0106 NV UP EI PL NZ NA PO NC
0AF5:0106 81EB0020 SUB BX,2000
-t

AX=003E BX=F000 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=0AF5 ES=0AF5 SS=0AF5 CS=0AF5 IP=010A NV UP EI NG NZ NA PE CY
0AF5:010A 1D2000 SBB AX,0020
-t

AX=001D BX=F000 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=0AF5 ES=0AF5 SS=0AF5 CS=0AF5 IP=010D NV UP EI PL NZ NA PE NC



当执行 SUB BX,2000 时,就是(1000 -2000 )计算,低位中这个计算必然是负数,而且它需要借位。

所以,紫色 NG 符号标置表示这个计算结果是负数。
   红色 CY 进位/借位标置表示。
  
计算时因为是借位,所以 CF=1,在执行比当前低位再高一点的位的时候,则sbb指令会两个数字相减后再减去CF的值,因为借位了,所以要减去。


pushf 和 popf 指令

pushf的功能是将标志寄存器的值压栈。
popf是从栈中弹出数据,送入标志寄存器中。

pushf和popf,为直接访问标志寄存器提供了一种方法。




经过分析,pushf压入栈内的标置寄存器的16位值并非与Debug上显示的位置相对应。
因为标置寄存器的第 1、3、5、12、13、14、15位在8086CPU都没有使用(不具有任何含义),所以压栈后的二进制位表示也是这样安排的。

在80586 CPU中标置寄存器的长度可达32位,有如天书一般。

[ Last edited by redtek on 2007-1-8 at 11:22 AM ]
第90楼 redtek 发表于 2007-01-07 23:01
金牌会员 发帖 1,147 积分 2,902
【80x86汇编语言学习笔记】
cmp比较指令

cmp的功能相当于减法指令,只是不保存结果。
cmp指令执行后,将对标志寄存器产生影响。
其他相关指令通过识别这些被影响的标志寄存器位来得知比较结果。


例: cmp ax,ax
   相当于: (ax) -(ax) ,结果为0,但并不在ax中保存。
   仅影响 flag 的相关各位。

   指令执行后: ZF=1  PF=1 SF=0 CF=0 OF=O



C:\Masm50>debug
-a
0AF5:0100 mov ax,8
0AF5:0103 mov bx,3
0AF5:0106 cmp ax,bx
0AF5:0108
-r
AX=0000 BX=0000 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=0AF5 ES=0AF5 SS=0AF5 CS=0AF5 IP=0100 NV UP EI PL NZ NA PO NC
0AF5:0100 B80800 MOV AX,0008
-t

AX=0008 BX=0000 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=0AF5 ES=0AF5 SS=0AF5 CS=0AF5 IP=0103 NV UP EI PL NZ NA PO NC
0AF5:0103 BB0300 MOV BX,0003
-t

AX=0008 BX=0003 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=0AF5 ES=0AF5 SS=0AF5 CS=0AF5 IP=0106 NV UP EI PL NZ NA PO NC
0AF5:0106 39D8 CMP AX,BX
-t

AX=0008 BX=0003 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=0AF5 ES=0AF5 SS=0AF5 CS=0AF5 IP=0108 NV UP EI PL NZ NA PE NC
 

PE是奇偶标置位,cmp ax,bx 即(8 - 3)=5。
5的二进制表示:00000101 其中有2个1,也就是说1的出现次数是偶数,所以PF=1。

通过cmp(几乎每次都打成cmd)指令执行后,相关标志位的值可以看出比较结果。


CPU在执行cmp指令的时候,包含两种含义:进行无符号数运算和进行有符号数运算。


关于比较计算与补码的关系:



分析如下:

AH=0A0H = -96(十进制)
BH=0CBH = -53(十进制)

CMP AH,BH 相当于减法计算:

  (AH)=(-96)-(-53)
      =(-96)+53
      =-43 (十进制)

得到了-43,现在求有符号数表示(-43的补码):

  |-43| 绝对值为: 43
  43=0010 1011     (二进制表示)
    =1101 0100     (将它求反)
    =1101 0100 + 1 (加1,防止重码)
    =1101 0101     (结果)
    =D5H           (16进制结果:就是-43的补码)

所以,结果是负数,SF符号位标置为1(负数)。


过程



C:\Masm50>debug
-a
0AF5:0100 mov ah,a0
0AF5:0102 mov bh,cb
0AF5:0104 cmp ah,bh
0AF5:0106
-r
AX=0000 BX=0000 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=0AF5 ES=0AF5 SS=0AF5 CS=0AF5 IP=0100 NV UP EI PL NZ NA PO NC
0AF5:0100 B4A0 MOV AH,A0
-t

AX=A000 BX=0000 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=0AF5 ES=0AF5 SS=0AF5 CS=0AF5 IP=0102 NV UP EI PL NZ NA PO NC
0AF5:0102 B7CB MOV BH,CB
-t

AX=A000 BX=CB00 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=0AF5 ES=0AF5 SS=0AF5 CS=0AF5 IP=0104 NV UP EI PL NZ NA PO NC
0AF5:0104 38FC CMP AH,BH
-t

AX=A000 BX=CB00 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=0AF5 ES=0AF5 SS=0AF5 CS=0AF5 IP=0106 NV UP EI NG NZ AC PO CY



检测比较结果的条件转移指令

大多数条件转移指令都检测标志寄存器的相关标志位,根据检测的结果来决定是否修改IP。
被cmp指令影响的那些标置位,这些转移指令通常都和cmp相配合使用。


条件转移指令:

je、jne、jb、jnb、ja、jna


上面条件转移指令所检测的标志位,都是cmp指令进行无符号数比较时候,记录比较结果的标志位。

例: je (等于),检测 ZF 位,当ZF=1的时候进行转移。如果在je前面使用了cmp指令,则je对ZF的检测实际上就是间接地检测cmp的比较结果是否为两数相等。

因为 cmp 比较实际上就是两个操作数相减,如果两个操作数值一样,自然相减为0,ZF(是否为0标置位)=1,代表为0。而 je 判断 ZF 标置位,所以就检测到了是相等。

je 检测的是ZF标置,不管 je 前面是什么指令,只要CPU执行JE指令时,ZF=1,那么就发生转移。



如果 ah与bh相等,则 cmp 会将 ZF的值置1,所以后面的 JE 指令判断 ZF=1,则跳转到指定位置。



虽然没有了 cmp 与 je 的配合,但是 add ax,0 就相当(AX)=(AX)+0 。ax=0,那么ax=ax+0还是0 ,结果为0所以 add 指令将ZF=1。当 je 指令执行时,它判断ZF=1的标置,如果发现ZF=1,则认为相等,进行跳转。

(即,不管是什么指令让CF=1的,只要je判断CF=1,就一定发生跳转)


很多种方法都可以让ZF=1,所以标置寄存器与cmp的原理必须掌握。对这些原理的理解有多少,就意味着对指令深度的理解和灵活开发。


CPU提供了cmp指令,也提供了je等条件转移指令。将它们配合使用,可以实现根据比较结果进行转移的功能。

对于je、jne、jb、jnb、ja、jna等指令和cmp指令配合的使用思和je相同。


例: 在 data 段中统计 8 出现的次数



出现了3次8。


-g 0017

AX=0003 BX=0008 CX=0000 DX=0000 SP=0000 BP=0000 SI=0000 DI=0000
DS=0B49 ES=0B39 SS=0B49 CS=0B4A IP=0017 NV UP EI PL NZ NA PO NC



例:统计 data 段中数值大于8的字节的个数,用 AX 保存统计结果。




例:统计 data 段中数值小于8的字节个数,用 ax 保存统计结果。



[ Last edited by redtek on 2007-1-7 at 08:19 PM ]
上一页  13 4 5 6 7 8 9  下一页
[ 联系联盟系统管理团队 - 中国DOS联盟 - 标准版 ]
Sponsored by ifanr Inc | © 2001–2023