要理解ret,retfcall指令,必须要先理清以下汇编基础知识:

一.             [bx]和内存单元

  [bx]是什么呢?

       [0]有些类似,[0]表示内存单元,它的偏移地址是0

n       我们要完整地描述一个内存单元,需要两种信息:

n       1)内存单元的地址;

n       2)内存单元的长度(类型)。

n       我们用[0]表示一个内存单元时,0 表示单元的偏移地址,段地址默认在ds中,单元的长度(类型)可以由具体指令中的其他操作对象(比如说寄存器)指出

   所以,按照这样的原理,我们对如下指令可以这样理解:

   Mov ax,0]:表示将一个内存单元的内容送入ax寄存器。其中内存单元的长度为2字节,偏移地址为0,段地址在ds中。

   Mov al,0]:表示将一个内存单元的内容送入al寄存器。其中内存单元的长度为1字节,偏移地址为0,段地址在ds中。

   Mov ax,[bx]:表示将一个内存单元的内容送入ax寄存器。其中内存单元的长度为2字节,偏移地址在bx,段地址在ds中。

例子:比如AX=1234BX=1000 DS1000内容为1111

      mov [bx],axAX的值赋予BX所指向的内存单元 
 
那么执行后AX=1234BX=1000DS1000的内容为1234

      这种做法就叫做:寄存器间接寻址

 

二. 描述性符号()

     为了描述上的简洁,我们将使用一个描述性的符号 “() ”来表示一个寄存器或一个内存单元中的内容。

     例如:

     (ax):表示寄存器ax中的内容。

     20000h)表示内存单元20000h中的内容,(?)括号中的表示物理地址。

     ((ds)*16+(bx)),如果ds中的内容为1230,bx中的内容为4567,(1230*16+4567)可以理解为:

                    Ds中的内容作为段地址,bx中的内容作为偏移地址,这样形成的物理地址的内容。

     为了加强理解,我们看看下面的如何描述:

n        我们看一下(X)的应用,比如:

n        1ax中的内容为0010H,我们可以这样来描述:(ax)=0010H

n        22000:1000 处的内容为0010H,我们可以这样来描述:(21000H)=0010H

n        3)对于mov ax,[2]的功能,我们可以这样来描述:(ax)=((ds)*16+2)

n        4)对于mov [2],ax 的功能,我们可以这样来描述:((ds)*16+2)=(ax)

n        5)对于 add ax,2 的功能,我们可以这样来描述:(ax)=(ax)+2

n        6)对于add ax,bx的功能,我们可以这样来描述:(ax)=(ax)+(bx)

n        7)对于push ax的功能,我们可以这样来描述:
      (sp) = (sp)-2

          ((ss)*16(sp))=(ax)

 

三. mov ax,2 那其机器代码是 B8 02 00,这个设计计算机指令,汇编语言教材第一章一般就讲这个,B8表示把后面的一个WORD存放到AX里面,比如MOV AL,1的机器指令是B0 01B0就表示把后面的字节保存到AL里面,而MOV AH,1的机器指令是B4 01

 

四.总结:现在我们通过一个实例,将上述的指令综合一下:

题目:从offffh:0006内存单元中取一个值,并相加三次

分析:(1)运算后的结果是否会超出dx所能存储的范围?

       Ffff:0006 单元中的数是一个字节型的数据,范围在0~255之间,则用它和3相乘结果不会大于65535,可以在dx 中存放下

2)我们用循环累加来实现乘法,用哪个寄存器进行累加?
我们将ffff:0006单元中的数赋值给ax,用dx进行累加。先设(dx)=0,然后做3(dx)=(dx)+(ax)

3 ffff:0006单元是一个字节单元,ax是一个 16 位寄存器,数据长度不一样,如何赋值?

我们说的是赋值,就是说,让 ax 中的数据的值(数据的大小)和ffff:0006 单元中的数据的值(数据的大小)相等。

8 位数据01H16位数据0001H的数据长度不一样,但它们的值是相等的。

n        那么我们如何赋值?

n        ffff:0006单元中的数据是XXH,若要ax中的值和ffff:0006单元中的相等,ax中的数据应为00XXH

   所以,若实现ffff:0006单元向ax 赋值,我们应该令(ah)=0(al)=(ffff6H)

n        实现计算ffff:0006单元中的数乘以3,结果存储在dx中的程序代码。

 

 

代码:

assume cs:codesg

codesg segment

     mov ax,0ffffh

     mov ds,ax

       mov bx,6

       mov al,[bx]

       mov ah,0

       mov dx,0

       mov cx,3

      s:add dx,ax

       loop s

 

       mov ax,4c00h

       int 21h

codesg ends

end

 

现在我们用debug来跟踪这个程序:

 

91.jpg

 

我们来分析一下:

1.       图中ds=165h,所以程序cs=166f,ss的值也为166f,为什么?如果还不清楚的可以看看我们前面的学习汇编系列(大略提一下:不明白cs的值的,请仔细回头去看.exe文件装载的那张图,ss的值不明白的请回头看栈的那张图),实际上程序运行是,先要找到一块起始地址的内存,此处起始地址为:ds:0,但是在.exe加载时,因为dos需要与.exe文件通讯,所以会有一块psp区用来存放通讯的内容。所以此处cs的值实际上是cs=ds+10h=166F,我们说cpu取得内存单元,就是通过cs:ip,所以cs:0实际上就是指程序的第一条指令。

2.       同样的道理,此时指针也是指向程序的第一条指令,所以指针的段地址也为166f,不明白的回头去看栈的原理,这里就不多说了。

3.       破解软件时,我们要先学会用g命令,为了使大家看得更清楚一点,我们将循环次数改为10

我们可以使用g 0012,则会自动执行到偏移地址为0012处。

当我们遇到loop时,因为后面的循环次数太多,我们可以用p命令。也可以用g 0016

4.       同一种写法,不同的处理方式:

对于这种写mov al,[0]debug表示:把ds:0内存单元处的内容放入al.

                      源程序中表示:就是把数据0放入al中。

解决方法:1.用过渡寄存器,比如:bx,如下:

            Mov bx,0

            Mov ax,[bx]

          2Mov ax,ds:[0]

 

五.活用:loop[bx]的联合应用

例子:计算ffff:0~ffff:b单元中的数据的和,结果存储在dx中。

n        我们还是先分析一下

1.运算后的结果是否会超出 dx 所能存储的范围?

 ffff:0ffff:b内存单元中的数据是字节型数据,范围在0255之间,12个这样的数据相加,结果不会大于 65535 ,可以在dx中存放下。

2. 我们是否将 ffff:0ffff:b中的数据直接累加到dx中?

 当然不行,因为ffff:0ffff:b中的数据是8位的,不能直接加到16位寄存器dx中。

3.我们能否将ffff:0ffff:b中的数据累加到dl中,并设置(dh=0,从而实现累加到dx中的目标?


 这也不行,因为
dl8位寄存器,能容纳的数据的范围在小 255 之间,ffff : 0ffff:b中的数据也都是 8 位,如果仅向dl中累加12 8 位数据,很有可能造成进位丢失。

4. 我们到底怎样将用ffff:0ffff:b中的8位数据,累加到16位寄存器dx中? 


我们将内存单元中的
8 位数据赋值到一个16位寄存器ax中,再将ax中的数据加到dx上,从而使两个运算对象的类型匹配并且结果不会超界。

 

分析后,我们现在来写出代码:

Mov ax,0ffffh

Mov ds,ax

Mov bx,0

Mov dx,0

Mov cx,0

 

S:Mov al,[bx]

Mov ah,0

Add dx,ax

Inc bx

Loop s

 

请大家自行加上伟指令运行。

通过前面的介绍,我们知道了[寄存器]的含义以及各个寄存器之间如何灵活计算的方式,这是学习破解的基础,那么破解时入口地址的确认以及如何查看反汇编的数据段,代码段,栈段呢?看下面的几个基础点。

六.在一个段中存放数据、代码、栈,我们先来体会一下不使用多个段时的情况;

1. 考虑这样一个问题,编程计算以下8个数据的和,结果存在ax 寄存器中:

   0123H0456H0789H0abcH0defH0fedH0cbaH0987H

看下面的程序:

assume cs:codesg

codesg segment

       dw 0123h,0456h,0789h,0abch,0defh,0fedh,0cbah,0987h

      mov bx,0

       mov ax,0

       mov cx,8

 s: add ax,cs:[bx]

       add bx,2

       loop s

       mov ax,4c00h

       int 21h

codesg ends

end

n        解释一下,程序第一行中的 “dw”的含义是定义字型数据。dwdefine word

n        在这里,我们使用dw定义了8个字型数据(数据之间以逗号分隔),它们所占的内存空间的大小为16个字节

n        8个数据的偏移地址是多少呢?

   因为用dw定义的数据处于代码段的最开始,所以偏移地址为0,这8 个数据就在代码段的偏移02468ACE处。

   程序运行时,它们的地址就是CS:0CS:2CS:4CS:6CS:8CS:ACS:CCS:E

2.如何让这个程序在编译后可以存系统中直接运行呢?我们可以在源程序中指明界序的入口所在.

n           Debug 载后,我们可以将 IP 设置为10h,从而使CS:IP指向程序中的第一条指令。然后再用T命令、P命令、或者是G 命令执行。

n        可是这样一来,我们就必须用Debug 来执行程序。

   程序 6.1 编译成可执行文件后,在系统中直接运行可能会出现问题,因为程序的入口处不是我们所希望执行的指令

n        我们在程序的第一条指令的前面加上了一个标号start,而这个标号在伪指令end的后面出现。end 除了通知编译器程序结束外,还可以通知编译器程序的入口在什么地方。

 

2.通过上面的分析,我们明白了在代码段中使用数据的情况,现在在来看看在代码段中使用栈的情况。

n        完成下面的程序,利用栈,将程序中定义的数据逆序存放。

   assume cs:codesg

   codesg segment

      dw 0123h,0456h,0789h,0abch,0defh,0fedh,0cbah,0987h

 

      ?

   code ends

   end

分析:

(1).程序运行时,定义的数据存放在cs:0~cs:15单元中,共8个字单元。依次将这8个字单元中的数据入栈,然后再依次出栈到这 8 个字单元中,从而实现数据的逆序存放。

(2).问题是,我们首先要有一段可当作栈的内存空间。如前所述,这段空间应该由系统来分配。我们可以在程序中通过定义数据来取得一段空间,然后将这段空间当作栈空间来用。

程序如下所示:

assume cs:codesg

codesg segment

     dw 0123h,0456h,0789h,0abch,0defh,0fedh,0cbah,0987h

     dw 0,0,0,0,0,0,0,0

  start:mov ax,cs

  mov ss,ax

  mov sp,32

  mov bx,0

  mov cx,8

 

  s: push cs:[bx]

     add bx,2

     loop s

 

     mov bx,0

     mov cx,8

   s0:pop cs:[bx]

      add bx,2

      loop s0

 

      mov ax,4c00h

      int 21h

codesg ends

end start

 

解释:(1.其中mov ax,cs

           mov ss,ax

           mov sp,32

   我们要讲 cs:16 ~ cs:31 的内存空间当作栈来用,初始状态下栈为空,所以 ss:sp要指向栈底,则设置ss:sp指向cs:32


七。看下面的代码,实现了两个字符串变大写和变小写的方法。

Code


 

 

1.  数据段是放在程序开头的,程序指令执行段是在数据段之后,所以数据段的首地址为:ds+10h.

2.  注意data,and,or ,[bx]的用法。

3.  [bx][bx+idata]的用法。后者只是取出bx中的数值(即偏移地址)后+一个数=新的偏移地址而已。例如:

2000:1000 00 be 00 06 00 05

Mov ax,2000h

Mov ds,ax

Mov bx,1000h

Mov cx,[bx] 那么此时cx=00

Mov cx,[bx+1],那么此时cx=be

 4.sidi,与bx相似,只是不能分成两个8位寄存器。所以在传输时,一般按字传。(比如将一个字符串复制到另外的空间地址)。

5.mov ax,[bx+si],bxsi]此种方式与数组类似。一般si计数,然后通过si找新偏移地址的数据。例如:

  Mov ax,2000h

  Mov ds,ax

  Mov bx,1000h

  Mov si,0

 

  Mov ax,[bx+si]

  Inc si

 

6.[bx+si+idata]与[bx+si]类似。

 

最后总结一下:

n          如果我们比较一下前而用到的几种定位内存地址的方法(可称为寻址方式),就可以发现有以下几种方式:

n              1[iata] 用一个常量来表示地址,可用于直接定位一个内存单元;

n              2[bx]用一个变量来表示内存地址,可用于间接定位一个内存单元;

n              3[bx+idata] 用一个变量和常量表示地址,可在一个起始地址的基础上用变量间接定位一个内存单元;

n              4[bx+si]用两个变量表示地址;

n              5[bx+si+idata] 用两个变量和一个常量表示地址。

 

 

 

 

 

 

 



 

 

 

 

 

 

 

 

 

 

 

 

 

 

posted on 2009-03-27 12:55  jasonM  阅读(1671)  评论(2编辑  收藏  举报