汇编语言(王爽第三版)检测点10

检测点10.1

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

汇编源代码check10-1.asm

assume cs:code

stack segment

    db 16 dup (0)

stack ends

code segment

start:  ;建栈

        mov ax,stack

        mov ss,ax

        mov sp,16

        ;压栈

        mov ax,1000H

        push ax

        mov ax,0000H

        push ax

        retf

       

        mov ax,4c00H

        int 21H

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中的数值为多少?

内存地址:         机器码                 汇编指令          

1000: 0           b8 00 00              mov ax,0

1000:3           e8 01 00              call s    

1000: 6           40                    inc ax

1000:7           58                  s:pop ax

程序分析:

1.熟悉call指令的操作过程。call指令在执行时,首先push ip(此时ip应为6 ,CPU将要执行的地址),然后jmp 标号,执行标号处的代码,pop ax (弹栈写入ax,(ax)= 6),我们发现inc ax没有执行。

       此时使用的栈空间是系统自动创建的。

2.结论:(ax)= 6

 

检测点10.3

内存地址          机器码                 汇编指令                 执行后情况

1000:0             b8 00 00             mov ax,0                ax=0,ip指向1000:3

1000:3             9a 09 00 00 10       call far ptr s          push cs,push ip,ip指向1000:9

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

程序分析:

1.关键还要明白是什么段地址和偏移地址压栈?遇到CALL指令,老样子,首先将当前的CS和IP值压栈,(cs)=1000H(由CALL的机器码得知),此时IP指向了0008H。它们统统压栈(cs先入栈,ip在栈顶)。(ax)=0

2.转移到标号S处继续执行代码,pop ax出栈写入到ax,(IP)先出栈(0008H),(ax)=0008H

3.add ax,ax   0008H+0008H=0010H

4.pop bx 将(cs)=1000H出栈,并写入bx中,(bx)=1000H

5.add ax,bx   0010H+1000H =1010H

6.(ax)=1010H;代码:inc ax没有执行

 

检测点 10.4

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

内存地址      机器码           汇编指令            执行后情况

1000:0           b8 06 00        mov ax,6               (ax)=6,ip指向1000:3

1000:3                  ff d0            call ax                  push ip(此时IP值为5),ip转移指向1000:6

1000:5           40                      inc ax

1000:6           58                 mov bp,sp                    (bp)=(sp)=fffeh

                                         add ax,[bp]                  (ax)=6+(ss:bp)=6+5=0bh

程序分析:

1. 遇到CALL指令,老样子,(ip)(此时是5),压栈;这里我们可以不管sp是多少(我的debug是0000H),目前我们确定的就是IP的值是5(压栈的数据)。此时栈中有一个字就是0005H。(sp)=(sp)-2 =>>0000H-2=fffeH

2.由于是CALL ax,(ax)=0006H,直接转移到1000:0006处执行,将sp的值赋值给bp。

3.由于bp默认隶属于ss段寄存器,故[bp]指向ss段的物理内存,也就是栈结构的空间,此时栈中就一个字0005H,那么(ss:[bp])=0005H(读取ss栈中的内容)。

4.add ax,[bp]    ,0006H+0005H=000BH

5.结果:ax的值是000BH。inc ax指令依然没有执行。

 

检测点10.5

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

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

 

         mov ax,4c00h

        int 21h

code ends

end start

程序分析:

1. 在栈段初始化并定义了8个字,16个字节的空间。是dw指令。

2. 初始化栈,将栈段地址也赋值给了ds;(ss)=(ds),数据段和栈段是同一个内存空间段。

3. 遇到了call,老样子,将(cs)压栈(这个值肯定是call指令后面的那个指令的cs和ip我们可以不关心它,依据你的debug程序)、将(ip)压栈,此时的ip值应该是第一个inc ax的偏移地址。然后jmp到ds:[0eh]内存单元内容作为偏移地址的点执行代码。由于ds和ss都是同一个段,call的是一个字单元,故ds:[0eh]内存单元是指向的一个字(ds:[0eh]~ds:[0fh]内存单元),此单元正好是栈空间栈顶的存储单元,存储着最后压入的ip的值,这个值就是代码inc ax的偏移地址,那么开始执行inc ax

4.执行3次inc ax后, (ax)=3。

5.总结:这个考察了我们对于CALL 内存空间这个指令的熟悉程度,也考察了栈段和数据段在同一段地址下,怎样读出栈空间单元内容。

       为什么不让使用debug来逐步调试?因为在执行到CALL指令时,显示ds:[0eh]=065D(在我的环境中debug后),ds:【oeh】中的内容是中断的信息,并不是我们程序所希望的0011H,它一下就jmp到065D的地址去了,而不是cs:11地址执行。

       stack段初始的时候定义的全是0,怎么初始化栈段后有数据了?因为中断信息。

(2)下面的程序执行后,ax和bx中的数值为多少?

assume cs:codesg

stack segment

    dw 8 dup(0)                       ;初始化8个双字,16个字的内存作为栈空间

stack ends

codesg segment

start:

    mov ax,stack

    mov ss,ax

    mov sp,10h                        ;初始化栈顶

    mov word ptr ss:[0],offset s      ;(ss:[0])=001ah

    mov ss:[2],cs                     ;(ss:[2])=cs

    call dword ptr ss:[0]             ;cs入栈,ip=19h入栈,转到cs:1ah处执行指令

                                        ;(ss:[4])=cs,(ss:[6])=ip

    nop

s:  mov ax,offset s                   ;ax=1ah

    sub ax,ss:[0ch]                     ;ax=1ah-(ss:[0ch])=1ah-19h=1

    mov bx,cs                         ;bx=cs=0c5bh

    sub bx,ss:[0eh]                   ;bx=cs-cs=0

 

    mov ax,4c00h

    int 21h

codesg ends

end start

 

posted @ 2017-05-21 09:08  筑基2017  阅读(2500)  评论(1编辑  收藏  举报