汇编学习笔记

来源:互联网 发布:2017年适合做淘宝客吗 编辑:程序博客网 时间:2024/06/05 07:34

前言

为了更深刻的理解c/c++,学习一下汇编(注:本文汇编讲解只是为了更好的使用c/c++,并不是严格的学习汇编)

我们所有的高级语言除了解释型的,比如javascript,都是被编译器编译成二进制代码。这些二进制代码术语叫COFF文件。

COFF文件在windows平台上被叫做PE文件,linux平台上叫elf文件。

windows上的PE文件常见的有exe dll ocx sys,编译器将我们的程序编译成PE文件,这些PE文件 本质上都是二进制,就是很多01组成的,稍微合并一下就能看到汇编指令,

比如 010110011 可能对应 mov eax, 1  

因为指令数量是一定的,所以这些都是固定的

寄存器

根据冯·诺伊曼原理 计算机的的组成是这样的:cpu + 存储设备

而cpu是由运算器,控制器和寄存器组成 ,所有的指令都是在cpu里面运算的,寄存器英文单词叫register,先简单理解为就是一个存储设备。

常见寄存器有: AX BX CX DX ES DS GS SP BP IP

不常见寄存器:GT GS

寄存器简介:http://blog.csdn.net/rankun1/article/details/70880239

80386以前的cpu,也就是16位操作系统下,所有的寄存器都是16位的,等到了32位时代大多数的寄存器被intel扩展成32位的了,也就是4个字节 32位

注意是大多数,不是所有。比如AX变成EAX,BX变EBX, CX变ECX,DX变EDX,  E是extend,扩展的意思。

所以,一个EAX的低16位是AX,AX低8位是AL,高八位是AH。EAX高16位没有名字,因为一般要么用低16位,要么用32位 ,没有单独用高16位。就像数字12(十二)一样,没有低位2,高位1没有意思。

到64位系统再次做了一个扩展,变成RAX RBX RCX RDX,这里不在多说。

EAX

EAX一般有一个很重要的用途,存储函数返回值!(这是编译器的建议规则,大多数编译器会遵循,但也有的不遵循,详情学习编译原理)

比如你调用一个函数,返回一个值,等函数调用完了 栈内存被销毁了,你去哪里找这个返回值呢?

比如这条语句:

int i = func();

假如func()返回一个值,实际上在函数销毁前 

mov eax, 函数返回值

这样函数调用完了再从eax中将返回值取出来给i,也就是

 mov ptr dword [i], eax

EAX一般用来放置函数的返回值,这在逆向很有用的。如果EAX放不下 就用EDX,如果还放不下就存储指针这样就够了。

ECX 

这个C是count,计数用的,像for while等循环里面的计数,就是放在这个寄存器里面(这也是编译器的一条规则,当然也是建议规则)。

举例说明:

char sz[128] = {0};

它对应的机器指令如何呢?,首先就是给这段内存清0,假如现在eax里面就是sz的地址,那实际上就是一个循环 (下面代码只是大概,目的是为了说明ecx作为计数器的用法)

mov ecx 32zzzz:mov eax, 0dec ecxtest ecxjnz zzzz
先看下编译器的思路,首先eax里面存的是数组的首地址,eax是四个字节,那么ecx表示计数的话,要清零这段内存ecx应该是多少?

每次搬动四个字节,总共128个字节,所以应该是128/4 = 32,(这个过程是编译器计算出32)

所以上面指令依次为:

ecx设置为32

zzzz:将eax清0

ecx减一

如果ecx不为0

跳转到zzzz

这样32次循环之后,整个数组就清零了。(疑问:eax增加没看到

byte word dword

下面几行代码,有什么区别?

char c1 = 1;shor c2 = 1;int c3 = 1;long c4 =1;
不同类型占用的内存长度不同,分别为1字节,2字节,4字节,4字节,那执行的时候cpu如何知道不同的长度?如何知道第一句就是1个字节 第二个是两个字节 第三个和第四个是四个字节?(byte,word,dword)

编译器会把他们编译成不同的机器指令,

mov byte ptr [c1的地址], 1mov word ptr [c2的地址], 1mov dword ptr [c3的地址], 1mov dword ptr [c4的地址], 1
byte是一个字节,word是两个字节,dword是四个字节,这样来区分的。更多的字节,都是通过这些基础的组合。

但是实际的指令不是这样的,因为cpu不能直接将立即数直接放到内存中,必须通过寄存器。所以,先把1放到寄存器里面,再从寄存器转到对应的内存,

比如第一句

mov AL, 1mov byte ptr [c1的地址], AL

先将1移入寄存器AL,然后将AL移入到c1的地址,大小为1字节
比如一个内存地址:0xFF00EEFF   ,单从这个地址来看你知道它指向的内存多大?根本没法知道,那么我们就利用这些限定符

ptr byte [0xFF00EEFF]

意思是0xFF00EEFF地址1字节大小

空指针异常汇编解释

char* p = NULL;*p = 9;
首先是把 内存地址p出的值赋值为0,
mov dword ptr [p], 0
然后是*p = 9
...待续

补充

第一,数据只能从寄存器到寄存器,或者从寄存器到内存,或者从内存到寄存器,不能内存到内存,

比如 你就不能写这样的指令:
mov ptr dword [a1], ptr dword [a2]

这还是现在的冯·诺伊曼原理决定的,因为内存和cpu是不同的组件,cpu通过地址总线连接内存,所以数据必须先从内存中运到cpu中(存在寄存器),

然后再从寄存器搬回内存。

第二,看两种写法,有什么区别

mov eax, ebxmov eax, [ebx]
第一个是把ebx寄存器里面存的值放到eax中 ,加了中括号表示里面值对应的内存地址中的值

比如ebx 现在等于8,那么mov eax, ebx这样eax就等于8了,如果是mov eax, [ebx]那么相当于把内存地址是8处的内存中的值放到eax中

实例讲解

待续。。。




0 0
原创粉丝点击