汇编学习笔记(二)

来源:互联网 发布:淘宝外卖.分口袋 编辑:程序博客网 时间:2024/05/26 02:21
第三章:寄存器(内存访问)

内存中字的存储:如存放的是20000H,就在高地址存放高端位,低地址存放低端为,记住,在内存的地址是以字节为单位的,就是说没隔8位为一个地址加1,字型数据:就是两个字节,在任何两个地址的连续的内存单元可以看做是两个内存单元,也可以看做是一个字的高低8位(高位字节单元和低位字节单元)

DS和[address]:
就是说在[]直接接内存的偏移地址,注意是偏移地址,不是段地址哈,DS:代码段寄存器,属于专用寄存器,要想往他里面写东西,必须要借助通用寄存器哈,呵呵,如:
mov bx,1000H
mov ds,bx
mov a1,[0]
rem:解释一下这段代码:首先把数据1000H写入bx通用寄存器,然后将bx的内容写入ds的段地址寄存器,最后是将al,(注意是l字母,不是1数字哈,是低8位寄存器)的内容赋值为0值,再苦逼的回来解释一下这个,1000H看吧,最后是放入ds代码段寄存器中的,如下:
mov ax,1000H
mov ds,ax  rem:达到了最终的目的,将CPU的取代码位置定位到1000H
mov ax,[0] rem:不管你写的代码在那块内存,但是你的段地址是1000H的内存区域,这是关键哈
mov bx,[2] rem:重新理解ds 寄存器的作用:就是存放取代码的段地址
mov cx,[1]
add bx,[1]
add cx,[2]

mov:有三个功能哈:1.将一个数值送入特定的寄存器,2.将一个特定的寄存器的内容送入另一个特定的寄存器,3.将一个固定的值写入一个特定的内存地址(这里填偏移地址),几乎就各种传送拉啦,呵呵,这里mov的时候注意[0]是偏移的起始地址,如果移动的目的是al,就是说是半段寄存器的时候,他会自动写入8位的值,如果是ax的整个寄存器,则就会默认写入双字的值,呵呵,总之就是不会浪费寄存器的空间,也不会浪费总线的空间

重新说一下对CS:IP和DS的理解:
CS:IP指向的物理地址是CPU要取指令的位置,就是说CPU将要下次取指令的地方
DS:是代码段地址,就是在代码执行过程中的要去取数据的段地址
sub bx,[2] 表示是将 bx = bx -[2]
数据段:同代码段一样哈,呵呵,N <=64,的连续地址作为数据段

mov ,add,sub 就是具有两个操作数的指令,jmp是具有一个操作数的指令
栈:first in last out(FILO)  ,就是一个具有特殊访问权限的存储空间,也就是常说的先进后出,8086CPU的入栈和出栈操作都是以字为单位进行的(64位的可能是双字),push ax,就是将ax寄存器的数据送入栈中,pop ax,就是从栈顶取出数据送入ax寄存器中
SS:SP:介绍下这两个寄存器吧,SS(stack segment,)他存放栈顶的段地址哈,呵呵,就是给CPU一个专门的寄存器,告诉他这里面的段地址都是指向栈空间的地址,他的指针永远指向系统栈的最上面的栈帧的栈顶地址,
SP:就是存放栈顶的偏移地址,记住是栈顶哈,呵呵,就是一个游标卡尺的游标,另外,任何时候,SS:SP指向的就是栈顶元素,数据

空栈:首先说的是在栈是空的时候,因为 SS:SP 永远指向的是栈顶元素,如果是空栈,栈中根本没有元素,也就不存在栈顶元素的,则其就不能指在栈内,所以SS:SP指向的就是栈空间最高地址的下一个地址,就是说,因为wondows的栈都是向低地址生长的,也就是说栈底实际上是在上方的,就是一个倒着的罐子而已,当空的时候指向罐子底的上方,就是+2的位置,如:10000H-1000FH的空间是栈的空间,那么最初的话,SS:SP就指向10001H,呵呵,但是这里的SP = 0000H,因为啊,当栈为空的时候,栈中唯一的元素出栈,呵呵,然后理论上SP进行的操作是SP=SP+2;但是SP寄存器又是16位的,加2就会溢出啦,溢出之后的SP的值恰好是0000H,所以栈空时,SP=0000H,这样会造成咋样后果哪?栈顶的环绕覆盖,呵呵,就是SP又回到了原来的0位置,进行再次把栈当做空,覆盖原来的数据,的这是为啥那?其实就死为了运用完整的栈空间而已,也就是一个规定,没啥哈,呵呵

push 的全过程,如果CPU在执行push的指令的话,首先是SP = SP+数据所占的字数(记住是字数,不是字节数哈,因为栈里面的一个最基本的元素就占一个16位),然后在把某某寄存器或者是地址单元的数据压入栈中,试想如果是先入栈在使SP的地址加的话,那就会覆盖原来SP指向的数据哈,呵呵,因为他压入栈中把数据摞在哪里,还是的看SP啊,呵呵,这里的SP寄存器的自加器就起作用喽
POP 的全过程:同add 和sub 为逆操作,呵呵,POP首先是栈里有数据哈,然后是首先将数据放入某某内存地址或寄存器里,然后SP的地址加上数据占的字数,其一样,pop和push是互实在每一次POP之后呐,是将栈内的数据复制到某某寄存器或内存空间了,但是原来在栈里的数据还是会在那,因为没有人去刷这块内存,只是SP指针的值指向了下面的数据,就是在索引的时候不会找到他啦,呵呵,其实相同的原理格式化硬盘也是这样的,只是删除了硬盘的索引(主文件表:MFT表,在NTFS系统中,1/8的磁盘存储空间作为MFT的专用空间)PUSH和POP指令可以在栈和内存之间进行传递数据,也可以在内存(可能是栈空间)与段寄存器(以S结尾的)数据交流

PUSH和POP指令访问的内存地址不是指令中给出的,是由SS:SP指向的,这两个指令都是通过两个步骤完成的,但是mov指令是一步完成的,PUSH和POP两个操作指令只是修改了SP的值,也就是说栈底的变化范围是0-FFFFH(16位 = 64kB的寄存器哈,只是8086CPU)

栈的超界问题:
如果使用栈的push的指令一直push的情况,则就会出现在(规定的栈帧空间)最低地址的外面继续加数据,这样做的危险就是覆盖了外部的有用数据,就是栈的溢出问题,还有如果是使用pop指令一直取数据的话,在栈空了之后,还是pop,则会造成在最高地址的上面的数据被pop到寄存器中去,也就是说意外获取了数据,这可以是一种利用栈溢出的缺陷来获取外部数据的技术吧,解释这里的原因是:CPU是一个极傻的东西,他只知道读取CS:IP指向的内存的地址当做代码执行(当做CPU的指令哈),他只会把DS的地址取来当做是数据执行,还有就是现在的把SS:SP指向的地址内容作为栈的内容来读取(只知道SS:SP指向的地址是栈顶,但是不会知道栈的空间有多大),并且在这其中没有做栈边界的检测,呵呵,就像C和C++里没有数组越界的检测一样哈,但是在Java的底层是有数组越界的检测的哈,实际这一切都是CPU的单一功能的化身的实例哈

建立栈的代码:
mov ax,1000
mov ss,ax
mov sp,0010 ;可以用mov直接对sp操作
xor ax,ax ;就是说相同的数就为0

第四章 汇编源程序
语法:segment ends 成对使用的伪指令,这是在写那些可以被编译的汇编程序时,必须用到的一对伪指令,他的意思是定义一个段,segment 是段的开始,ends是段的结束
一个段必须有一个名称来标识,使用格式为: 段名 segment ----- 段名 ends  (伪指令的代表)
end 代表是汇编源程序的结束,遇到这个end后,编译器翻译为汇编就停止 end后可以是空,也可以是end start  其中的start就是代码段的开始,最初的一个地方一定会有start:mov.... 这里的start 上面可以使用dw 定义 字型数据,呵呵
一个.exe的程序可以是有很多段组成,但是以前的.com程序只能有一个段组成,可以做可执行文件
在源程序文件中所有的代码叫做源程序,在编译后能被CPU执行的的程序才叫程序
PE文件是可移植型文件,在.exe中具有PF头 代码  PE尾,在反编译之后就能区分开
一个标点代表一个地址
codesg:放在segment的前面,作为一个段的名称,这个单的名称最终将被编译,链接程序处理为一个段的地址
assume cs:abc   就是让abc 在CPU执行的时候认为是一个默认的寄存器,用assume后面的abc作为一个地址来解释,而这里的地址就是CS的地址,就是说abc被解释为CS代码段
DOS只是一个单任务的系统
简单的程序调用交接过程:1.首先在一个.exe程序运行起来的时候,就会获得他对CPU的控制权,但是如cmd.exe的程序运行起来的时候,在调用如 msdev.exe的时候cmd.exe程序停止运行,然后将CPU的控制权交给msdev.exe,然后执行msdev.exe,当子程序执行完成后,将CPU的控制权交还给cmd.exe,然后cmd.exe继续执行,其实之前的cmd.exe就是处在中断的状态了
中断:mov ax,4c00H  int 21H 就是程序的固定返回,使用的是21中断

exe文件的执行过程(硬盘->加载->内存的程序->运行)
exe文件的加载过程是首先在内存里找一块空闲的内存地址,这段内存的前256Byte规定为PSP区,这是一个DOS和应用程序交换的数据的地方,另外 PSP区虽然和程序物理地址连续,但是却有不同的段地址 空闲内存:SA:0   PSP:SA:0   程序区:SA+10H:0 就是往下加了16进制的256K的空间,呵呵,将该程序的地址存入DS中,初始化其他相关寄存器后,设置CS:IP的值指向该程序的入口

SA:是应用程序在内存的段地址
原创粉丝点击