10.1
补全程序,实现从内存1000:0000处开始执行指令1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20assume cs:code
stack segment
db 16 dup (0)
stack ends
code segment
mov ax,4c00h
int 21h
start: mov ax,stack
mov ss,ax
mov sp,16
mov ax,0
push ax
mov ax,1000h
retf
code ends
end start
程序分析:
retf指令作用(CPU角度):从栈中弹出2个字单元,并修改CS(第二个字)和IP(第一个字);首先它弹出的是IP,其次是CS,故在压栈时,CS的值首先入栈,IP再入栈。
在汇编编程角度,retf实现了远转移。
讲解:在汇编代码这个层次,retf指令作用是修改CS和IP的值,进而使指令从修改后的地址处开始执行。由于它所依赖的是栈中存储的内容,故在压栈过程中要搞清楚入栈的顺序、入栈的值。
2.熟悉ret指令和RETF指令执行的操作。
我们编译链接后,debug跟踪check10-1.exe
-d ss:0
0B66:0000 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 …………….
在stack数据区中初始化了16个0,此时它未成为栈结构。直到初始化栈的结构。
执行代码t t(二次)
AX=0B66 BX=0000 CX=0026 DX=0000 SP=0010 BP=0000 SI=0000 DI=0000
DS=0B56 ES=0B56 SS=0B66 CS=0B67 IP=0008 NV UP EI PL NZ NA PO NC
0B67:0008 B80010 MOV AX,1000
-d ss:00
0B66:0000 00 00 00 00 00 00 66 0B-00 00 08 00 67 0B 68 05 ……f…..g.h.
初始化一个栈sp=0010H(16),栈地址:ss=0b66,这里我们发现一些不应该有的数据(不理会它,它是中断的信息)
继续执行代码:
-d ss:0
0B66:0000 00 00 00 00 00 00 10 00-67 0B 68 05 00 00 00 10 ……..g.h…..
发现从高位开始存储10 00 00 00四个字节的数据,(体会栈是从高地址向低地址发展的,sp指针从10H减少到了0cH)
此时的CS=0B67 IP=0010
执行retf代码:
我们发现:CS=1000 IP=0000,CS和IP的值改变了。
总结:ret和RETF依赖于栈的结构存储一个程序执行点(IP或CS和IP),当执行这个代码时,可以恢复到这个程序的执行点(将栈中的数据修改IP或CS和IP,使CPU指向新的CS:IP)
10.2
下面的程序执行后,ax中的数值为多少?1
2
3
4
5
6
7
8
9
10
内存地址 机器码 汇编指令 执行后情况
1000:0 b8 00 00 mov ax,0 ax=0 ip指向1000:3
1000:3 e8 01 00 call s 读取指令后IP指向下一条指令IP为6;push 6
1000:6 40 inc ax
1000:7 58 s:pop ax ax=6
“call 标号”是将该指令后的第一个字节偏移地址入栈,再转到标号处执行指令。
10.3
下面的程序执行后,AX中的数值为多少?1
2
3
4
5
6
7
8
9
10
11内存地址 机器码 汇编指令
1000:0 b8 00 00 mov ax,0
1000:3 9A 09 00 00 10 call far ptr s ;cs为1000h,ip为8,
push 1000h,push 8
1000:8 40 inc ax
1000:9 58 s: pop ax ;ax=8h
add ax,ax ;ax=10h
pop bx ;bx=1000h
add ax,bx ;ax=1010h
执行call far ptr s时,取IP为8,add ax,ax ax=16
BX=CS=1000H 相加转16进制ax=1010H
10.4
下面的程序执行后,AX中的数值为多少?1
2
3
4
5
6内存地址 机器码 汇编指令
1000:0 b8 06 00 mov ax,6
1000:3 ff d0 call ax
1000:5 40 inc ax
1000:6 mov bp,sp
add ax,[bp]
在执行了
call ax
的时候
指令寄存器会指向下一条指令的起始地址,也就是
1000:5 inc ax
然后指令寄存器ip=5要压入堆栈
然后跳转到cs:ax指定的地址执行
又因为此时ax中的值为6 ,cs=1000
所以跳转到1000:6执行
于是修改 ip为6
到此时call ax指令才执行完毕
然后就执行
mov bp,sp
因为sp里面放的是堆顶的地址,所以bp也是栈顶的地址了
之后
add ax,[bp]
因为,bp里面放的是栈顶的地址,所以 [bp] 寄存器寻址后 [bp]实际是代表该地址单元的内容,也就是栈顶的内容,因为先前我把ip压入了堆栈,所以[bp]得到的内容就是5(先前在call ax时候压入的ip的值)
所以 ax=ax+[bp]
也就是 ax=6+5=0BH
10.5
题目:下面的程序执行后,ax中的数据是多少?1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18assume cs:code
stack segment
dw 8 dup (0)
stack ends
code segment
start :mov ax,stack
mov ss,ax
mov sp,16
mov ds,ax
mov ax,0
call word ptr ds:[0eH]
inc ax
inc ax
inc ax
code ends
end start
解答:刚开始时mov ax,stack到mov sp,10h是分别设置ss:sp指向程序中定义的栈段stack。mov ds,ax也把数据段的段地址也设置成了stack段的段地址。call word prt ds:[0Eh]相当于是sp=sp-2,push ip,jmp word ptr ds:[0Eh]这三条指令。而sp-2就是0Eh了,push ip就是把第一条inc ax对应的ip值给压入栈。存放在ss:[0Eh]和ss:[0Fh]里面。接下来是jmp word ptr ds:[0Eh]了,它的功能就是把stack段的[0eh]子单元的值付给ip,很巧的是stack中[0eh]字单元的值就是刚刚压入的ip值。所以ip值没有改变。程序继续执行inc ax后面的程序。所以ax的值是3.