目录
  1. 1. 10.1
  2. 2. 10.2
  3. 3. 10.3
  4. 4. 10.4
  5. 5. 10.5
检测点10.1-10.5

10.1

补全程序,实现从内存1000:0000处开始执行指令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
assume 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

程序分析:

  1. 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
18
assume 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.

文章作者: nocbtm
文章链接: https://nocbtm.github.io/2018/09/12/检测点10-1-10-5/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 nocbtm's Blog
打赏
  • 微信
  • 支付宝