速通汇编(五)认识段地址与偏移地址,CS、IP寄存器和jmp指令,DS寄存器
一,地址的概念
通常所说的地址指的是某内存单元在整个机器内存中的物理地址,把整个机器内存比作一个酒店,内存单元就是这个酒店的各个房间,给这些房间编的门牌号,类比回来就是内存单元的物理地址
在第一篇介绍debug的命令时使用过很多命令,不知你还记得与否,可点击快速复习->速通汇编(一)汇编环境配置(附资源),debug六种命令使用,四个通用寄存器_d指令怎么看寄存器中内容-CSDN博客
接下来我用e命令和d命令进行两步操作:在[0000:0000]地址处写入一串数据"123456789abc",然后再用d命令读取以验证是否成功写入
可以看到确实成功写入
(一)物理地址的表示方式
(1)段地址:偏移地址
看上图,我刚刚在[0000:0000]地址处写入了一串数据,这个[0000:0000]就是一个确切的物理地址,它由两部分组成,冒号左边是段地址,右边是偏移地址
需要提前说明,地址一定是用16进制数来表示的!
形象来说,酒店分5层,那么其中有几个房间的门牌号是207、310、404等等,我们正常的认知都知道这里的2、3、4是根据楼层来编的,后面的07、10、04则是基于它们各自的楼层的额外编号,所以段地址和偏移地址就是一个这样类似的概念,段地址相当于楼层号,再拼上一个偏移地址就能具体到一个房间门牌号(确定了内存单元的物理地址)
(2)单个十六进制数表示地址
物理地址 = 段地址*16+偏移地址
使用这个公式,可以将一个地址确切地表示成一个数而不是用段地址:偏移地址的形式来表示(这样才方便使用地址)
举几个例子(地址是十六进制数,乘16相当于整体左移一位,再加上偏移地址即是物理地址):
①0000:0000 == 0000*16+0000 == 00000
②1100:00bc == 1100*16+00bc == 110bc
③2000:1F60 == 2000*16+1F60 == 21F60
④2100:0F60 == 2100*16+0F60 == 21F60
⑤21F6:0000 == 21F6*16+0000 == 21F60
通过上述例子,相信你很清楚了如何表示地址,并且通过后3个例子,不难领悟到,同一个物理地址,可以用多个段地址:偏移地址的形式来表示
可以用下面这个例子来证明这点,可以发现,查看[2000:1F60]处的数据和[21F6:0000]处的数据是一模一样的,它们表示的确实是同一块内存的物理地址
二 ,CS、IP寄存器和jmp指令的作用
在第一篇时我们粗略介绍过所有寄存器,现在领略了地址的概念,再趁势学习一下CS和IP寄存器的具体作用
在debug模式下,使用a命令可以在[CS:IP]地址下写入汇编指令,任意时刻,CPU都将[CS:IP]指向的内容(“指向”就是地址的形象说法)当作指令执行(当然不是说你写进去就直接执行了,你需要用命令去让它执行,比如t命令)
因此,我们可以合理规划一台机器的内存,将需要的汇编代码写在内存的某些位置,然后通过改变[CS:IP]的内容,达到运行代码的目的
那要如何修改CS和IP的内容呢?mov cs,xxxx这样吗?不可以这样,CS和IP这两个寄存器的值不能用mov指令来改变,而要用jmp指令来赋值
使用形如【jmp 段地址:偏移地址】的指令,才可以修改[CS:IP]的内容
(补充:还可以使用形如【jmp 偏移地址】的指令,这样的指令被执行之后将只改变IP寄存器的值,CS寄存器的值不变,即只改偏移地址不改变段地址)
实例:
然后在[CS:IP]正指向的地址处写下这条jmp指令,让CS和IP能够跳转到刚刚写好3条汇编的地址处
t执行[CS:IP]处的汇编【jmp 2000:1F60】,观察到CS和IP的内容确实变成了[2000:1F60]
继续使用t命令,因为此时[CS:IP]已经指向[2000:1F60]了,那么刚刚提前写好的3条汇编理所应当地被正确执行
三,DS寄存器的作用
回到开头,我们在[0000:0000]地址处写下了"123456789abc"这串数据
现在的问题是,就好比你学习高级编程语言时,定义变量并赋值存好数据之后,要如何去使用它呢?
使用[偏移地址]的形式,就可以取用了
这时你应该心生疑问,前面不是说要段地址:偏移地址的形式才能确定一块物理地址吗,怎么这里中括号里面只用写偏移地址了呢?这时你看这块的小标题,应该恍然大悟了吧——是的,此时段地址就由DS来决定
可以如此说:汇编中出现的形如[偏移地址]形式的地址,指的都是[DS:偏移地址],即DS*16+偏移地址处的内存
那要如何修改DS的值呢?
不能直接【mov ds,段地址】,你可以先把段地址存入ax/bx/cx/dx这四个寄存器,然后再【mov ds,寄存器】
或者更加省事一点的方法,用r命令直接修改ds的值(当然在实际编程中没办法用r命令,只有debug模式下可以)
也就是说,如果我想要读取[0000:0000]处的数据,那么就先让DS寄存器来存放这里的段地址0000,然后直接写汇编【mov ax,[0]】,cpu就知道你要做的事情是将[0000:0000]处的内容赋值给ax,下面进行实例验证
①先在[cs:ip]处写好3条简单的mov指令汇编
② 修改ds寄存器的值为0000,待会段地址就是0000了
③t命令执行完提前写好的3条mov汇编,观察到ax、bx、cx寄存器的值被分别赋值成3412、5634、7856
我们来分析一下原因:
首先ds寄存器的值被提前修改成了0000,意味着段地址全程是0000,下图是提前在[0000:0000]处写好的数据"123456789abc"
①第一条汇编是【mov ax,[0]】 ,将[0000:0000]处的内容赋值给ax,因为ax是十六位寄存器,因此它会从[0000:0000]处取4位十六进制数,12显然是不够的(才两位),因此再取后两位34凑成3412赋值给ax。
为什么是3412而不是1234呢?因为12是低地址处(偏左边)的数据,应该放在ax寄存器的低位al处;34是高地址处(偏右边)的数据,应该放在ax寄存器的高位ah处,而ax是ah+al,ah在前,也就成了3412这样看起来倒过来的数据了(自行了解一下小端存储)
②第二条汇编是【mov bx,[1]】 ,将[0000:0001]处的内容赋值给bx,因为bx是十六位寄存器,因此它会从[0000:0001]处取4位十六进制数,34显然是不够的(才两位),因此再取后两位56凑成5634赋值给ax。
③第三条汇编同理,不再赘述。
④此时我们举一反三,同样的内存数据,如果我们执行汇编【mov dh,[0]】,结果会怎样?
因为dh是八位寄存器(dx的高位),因此它只取[0000:0000]处的两位数据12就够了,然后赋值给dh
实例验证:
⑤再举一反三, 同样的内存数据,如果执行汇编【add ax,[0]】,是不是可以把[0000:0000]处的数据和ax中的数据相加?
确实如此,如果执行【add al,[0]】也是同理的,相信你对地址已经很透彻了吧o(* ̄▽ ̄*)ブ