从0开始进入汇编的世界之每天理解一篇汇编帖-第一节

urfyyyy   ·   发表于 2020-06-13 19:34:30   ·   技术文章

前言

大家好,我是桐镜。从今天开始我将和各位哥哥们一起学习汇编语言。本帖会持续更新,大概想法是做一个从0开始,一直到理解汇编的过程,我尽量将内容写的浅显易懂,参考教程是王爽的《汇编语言》。参考视频为fishc的汇编语言。可能我也会有理解不到位的地方,还请各位哥哥指正。咱们一天只需要理解一篇汇编帖子就行。知识在于积累,成为强者的路上充满了寂寞,现在我来陪着大家一起深入。

有很多小伙伴并不是计算机科学专业的,对安全感兴趣来学习安全,那么对于计算机专业的同学来说,下面的内容会非常简单,对于非计算机科学专业的同学来说,就一起跟着我补充上这部分基础知识。
不学C,没学过java,甚至Python,能否学会汇编呢?相信自己!你一定能!

第一节、了解有关汇编的基础知识

时至今日,有很多高级语言,如c,c#,java,python等等。被广泛运用在各个领域。但汇编目前是我们能操作到的除了机器语言之外的最底层的语言了,很多的嵌入式,驱动等,都是用汇编语言来编写的,学会了汇编之后,你会对计算机底层有一个高度的理解,如,计算机内各个组件是如何配合的。用计算机的思维来操作计算机。在你涉及到破解,逆向,溢出等领域的时候,汇编也显得至关重要。
比如最简单的破解思维就是,某收费软件,看其汇编代码,判断出程序的真正入口,然后让其跳过验证是否充钱的那段代码,直接跳转到程序的真正入口就行了,找到关键跳~

汇编语言是直接在硬件之上工作的编程语言。那也就是说,我们首先要了解计算机的硬件系统的结构,才能运用汇编语言对其进行有效的编程。其中,我们需要理解最多的就是CPU内存

1.1、机器语言

我们首先来简单了解一下比汇编语言更接近底层的机器语言。机器语言是机器指令的集合,展开来说,就是一台计算机,它可以正确执行的所有命令。机器语言是我们的CPU唯一认可,唯一理解的语言。因为计算机也是人制造的,所以计算机的相关知识也不会超过人的理解范围。打个比方就是说,与人交流需要使用人类的语言,如和美国人交流你需要使用英语,和日本人交流需要使用日语…机器语言是由0和1组成的
例如机器语言的指令:01010000 (PUSH AX) //前面的0和1组成的是机器语言,括号中的是将这句机器指令翻译成了汇编语言。这句指令的意思是,将AX推进堆栈
至于堆栈是什么,我们暂且不用去了解,放在后面的内容中。早期的程序员很伟大,他们将0、1这种数字编程的代码打在纸带或者卡片上,用打孔来表示1,用不打孔来表示0。再将这些写好的代码通过卡片机或者纸带机,输入到计算机中,计算机理解了这些0和1的意思,去执行对应的指令,进行计算

总结:我们计算机的底层使用的是二进制,也就是1和0

如果我们要计算768+12288-1280,可能你现在使用python之类的语言,只需要原封不动的把数字输入进去就会得到计算后的结果,但使用机器语言的情况下,就需要写成如下这样(不必背也不用会写,只需要知道很复杂就行了):
101100000000000000000011000001010000000000110000001011010000000000000101
可以看出,如果你在中间写错一个数字,那程序就会得到不同的结果,而且在检查起来无疑是要你老命的。这只是一个很简单的计算而已,更不用说写出一个在屏幕上打印hello world,甚至做一个贪吃蛇小游戏了。

1.2、汇编语言的产生

我眼里的计算机,是一个又蠢又聪明的家伙,它聪明就在于一秒钟可以进行计算上万亿次,这是你永远也无法达到的速度;但它笨就笨在,如果我让你帮我买一个小包纸,那你肯定会给我买一个小包纸,而它除了0和1,其他的都不能认识也不理解,在它眼里除了0和1,其他的就是个屁。
越高级的语言也就越方便程序员,而相对的,理论上来说越高级的语言它的处理效率就越低
汇编语言的产生就是为了方便我们人类,让人类更容易与计算机沟通。汇编语言的主体是汇编指令。它是机器语言的助记符。
其实汇编指令是便于记忆机器指令的书写格式,汇编语言的指令和机器指令是一一对应的。通过编译器,汇编语言的指令会被翻译成0和1,去让计算机读取理解并执行。
例如:

  1. 机器指令:1000100111011000 //以前的程序员有一个算一个,通通没有头发
  2. 这段机器指令的用途是将寄存器BX的内容移动到寄存器AX中。关于什么是寄存器,后面会有讲解,这里各位先有一个印象就好
  3. 将其对应为汇编指令就是:MOV AX,BX //固定写法,移动时,是将后面的BX的内容移动到前面的AX中,MOV是英文move的缩写

这样的写法是不是就像人类的语言在说话一样,更容易阅读和理解了呢

我们来理解一下什么是寄存器。众所众知,打开机箱后,你能看到cpu和内存,那么这个寄存器在哪里呢,它实际上位于cpu之中,它是cpu用于存储数据的器件。一个cpu有多个寄存器,上面汇编代码中的AX和BX,就是其中两个寄存器的代号(可以理解为名字),就像你去一个办公室找人,你也得说你找谁(他的名字)对吧?寄存器可以理解为嵌入在CPU中的内存,但它并不是cpu高级缓存,二级缓存,它要比高级缓存和二级缓存更接近cpu

学过编程的同学应该都知道,cpu如何理解我们的代码呢?下面为大家简单描写这个过程,用C语言举例
程序员—->C语言—->C语言编译器—->汇编指令—->编译器—->机器指令—->计算机
程序员写了C语言程序后,经过C语言的编译器(如VC++),将C语言编译成汇编语言,汇编语言再交给汇编语言的编译器,翻译成了机器指令,最后交给cpu
那聪明的同学一下就反应过来了,汇编语言的整个过程要比C语言少:
程序员—->汇编指令—->编译器—->机器指令—->计算机

现在我来带大家看一下什么是汇编语言,首先我先写了一段很简单的c语言
vim test1.c

  1. #include <stdio.h>
  2. int main()
  3. {
  4. printf("hello everyone!\n");
  5. system("whoami");
  6. printf("\n");
  7. return 0;
  8. }

这段代码的意思是先打印hello everyone并\n换行,然后再调用system函数执行whoami。

接下来编译这个C文件并生成可执行文件
gcc test1.c -o test1

编译成功并生成了一个可执行的文件,叫做test1

先运行一下看看结果
./test1

接下来我们反汇编这个文件,gdb test1

查看main函数,disas main

可以看到中间push %rbq、move %rsp,%rbp之类的,就是汇编语言啦

windows下下载ollydbg来进行反编译
下载链接
使用ollydbg随便打开一个exe(打开OLLYDBG.EXE然后选择file-open)
这里我选择打开自己的一个木马

打开后就有很多汇编指令可以看到啦,我们暂且不去研究这个木马.exe的汇编指令都在做什么事情

1.3、汇编语言的组成

通过上面的两个例子可以看出。汇编语言由3个部分组成
1、汇编指令 (也就是机器指令的助记符,如MOV,JMP,ADD,PUSH等等,他们每一条都对应了一个机器指令,可以被直接翻译成机器码由计算机执行)

2、伪指令 (就是没有对应的机器指令,需要由编译器去翻译执行的指令,计算机并不执行,计算机是不认识的,只有编译器认识)
该类指令并不是可执行指令,没有机器代码,只用于汇编过程中为汇编程序提供汇编信息。例如,提供如下信息:哪些是指令、哪些是数据及数据的字长、程序的起始地址和结束地址等。伪指令有2个特点:
(1)由于是伪“指令”,因而它只存在于汇编语言中。高级语言中不叫指令,叫语句;
(2)由于是“伪”指令,也即“假”指令,因而不是可执行指令,不会产生机器代码,不会占用ROM空间,只用于汇编过程中为汇编程序提供汇编信息。

3、其他符号 (如+,- 也是由编译器去识别执行的)

汇编语言的核心就是汇编指令

1.4、了解存储器

CPU控制整个计算机的运作,那么如果想要让CPU正常工作,就需要给它提供指令和数据,指令用于告诉CPU怎么做,数据用来告诉CPU做什么。这些指令和数据存放在哪里呢,得找个地方放着吧,总不可能指令和数据凭空出现,又凭空消失…那就是在存储器中啦,也就是咱们平时一直说的内存。
内存是存储器中的其中一个,也是主要的一个存储器。其实拆开计算机的很多部件都能看到存在存储器,比如显卡的显存,网卡的存储器

一台计算机中,内存的作用仅次于CPU,离开了内存,再厉害的CPU也无法工作。打个比方就是,CPU相当于一辆车的发动机,而内存就相当于油箱或者轮子,缺少哪一个都会导致计算机根本不具备运作能力。
而硬盘不同于内存,硬盘中的数据如果不读取到内存中,那么就无法被CPU使用。比如各位玩的游戏,在你点击游戏运行的时候,硬盘中的游戏就会被加载到内存,然后CPU和内存之间互相交流数据,游戏得以被运行。

1.5、了解指令和数据

前面的内容已经说过,CPU能处理的就是一堆二进制的0、1代码,而在内存或者硬盘上,指令和数据没有任何区别,他们都是一堆二进制的信息
比如:
1000100111011000 //可以被当做指令也可以被当做是数据
如果将其作为数据,那么表示的数据是89D8H(H表示hex,16进制)
1000是8,1001是9,1101是D,1000是8
如果将其作为指令,那么表示的指令是MOV AX,BX
那么它到底是指令还是数据呢,就看我们怎么去用,我们告诉CPU这是指令,那这些就是指令,告诉CPU这是数据,那么这些就是数据。

1.6、存储单元

存储器(如内存,显存等)被划分为若干个存储单元,每个存储单元从0开始编号(从0开始是计算机科学中的常识了)
如果一个存储器有128个存储单元,那么其存储单元的编号为0到127。如下图所示

对于存储器的计量单位为:
1B = 8b(bit)也就是位
1KB = 1024B
1MB = 1024KB
1GB = 1024MB
1TB = 1024GB
磁盘的计量单位和内存是一样的。都是这样的转换

1.7、CPU对存储器的读写

CPU若想进行数据的读写,必须和外部器件进行三类信息的交互
1.存储单元的地址 (地址信息,和谁交互?它在哪?)
2.器件的读或写命令 (控制信息,让他做什么?读数据or写入数据?)
3.读或者写的数据 (数据信息,让他具体做什么,写什么数据?读什么数据?)

比如现在想在桌面上显示一张图片,简单来说,CPU就会通过地址信息找到显存的地址,然后让其进行显示操作(读读写写的一大堆操作),将图片的数据交给GPU去处理,然后映射到屏幕中

众所众知,计算机是用电的。它能处理的、传输的信息,都是电信号,既然是电信号,当然需要用到导线了。是CPU与各类器件沟通的通道(将计算机拆开,你不但能在主板上看到cpu,内存,显卡等期间,还会看到一堆堆的线路,那些就是导线了)
计算机中专门有连接cpu和其它芯片的导线,被称为总线(重点来了)

在物理上来看,总线就是一根根导线的集合
在逻辑上看,总线可以分为三种
1.地址总线
2.控制总线
3.数据总线
分别对应了上文的3个信息:地址信息,控制信息,数据信息。

那么CPU的读操作,可以通过下图理解

首先从地址总线发出3,那么就会去找编号为3的存储单元,然后从控制总线发出读的指令,最后将3号存储单元中的08这条数据从数据总线读取过来,替换掉原来的0

对于CPU的写操作,也是相同的

CPU从地址总线发出3,确定地址为3号存储单元,接着从控制总线发出写的指令,将数据26通过数据总线写入到3号存储单元中,替换掉原有的08

上文已经提过

那输入的这个二进制信息到底是数据还是指令,如何让CPU理解呢?通过刚才的知识点,我们不难想象,如果这段二进制数从地址总线走的话,就规定了CPU要将其理解为一个地址;同样的,从数据总线走的话,CPU将其理解为数据;通过控制总线走的话,CPU将其理解为控制指令。

1.8、地址总线

CPU要对一个存储器单元进行操作,无论你要怎么操作,操作什么,你都需要知道它的地址,它在哪里。CPU是通过地址总线来指定存储单元的
因为通过地址总线传送的数据无论具体是啥,都代表是一个地址,那么地址总线上能传送多少个不通的信息,CPU就可以对多少个存储单元进行寻址,也就是CPU的寻址能力
我们现在所说的64位CPU,32位CPU,就是指CPU的寻址能力,也就是地址总线的宽度(线的数目),64位就是64条线
大家现在应该都见过,64位的游戏,64位的某软件,实际上在这个情况下,只有64位CPU+64位操作系统+64位的游戏,才能达到理论上64位的速度,缺一不可

地址总线是如何传输地址信息呢?

如上图,CPU通过地址总线传输了一条“0000001011”的数据给内存,由于是从地址总线传输的,那么内存自动会定位到1011这个地址去

拿现在的64位CPU举例,其地址总线宽度为64,那么它的寻址能力就是2^64(2的64次方),可以最多寻找到2的64次方个内存单元。
一个地址总线可以索引一个内存单元,1个内存单元也就是一个字节(Byte,简写为B),1B = 8bit(位),1bit对应一个数据,1或者0。那么1个字节(B)可以存放多少个数据呢,可以存8位2进制数,8位二进制数从0开始有多少个呢,就是2的8次方
00000001、00000010、00000011……….11111111
那64位CPU总共就能找到2的64次方个内存单元

1.9、数据总线

CPU与存储器或其他器件传送数据是通过数据总线来进行的。
那也就是说数据总线的宽度决定了CPU与外部器件的数据传送速度
我们可以联想一下现实生活中的马路,一条路如果是一个单行道,那么一次只能通过一辆车,如果是一个有8个车道的高速路,那么一次可以通行8辆车,这样的话同一时间内运送的货物也就越多(我们假设一条道只有一辆车抵达终点,才可以通行第二辆,且车移动的速度是一样的),那么传送速度就越快。
如传送一个89D8这条数据,如果数据总线宽度为8,那么传送则需要两次

第一次传送D8,第二次传送89(从低位开始传)

而如果同样的89D8这条数据,在数据总线宽度为16的时候只需要传送一次,速度快了一倍

1.10、控制总线

CPU对外部器件的控制,是通过控制总线来进行的,控制总线是对 一些不同控制线的集合 的总称
控制总线的宽度决定了CPU对外部器件的控制能力

如上图,CPU将1011发送给内存,1和0代表具体的操作,读命令,写命令。

1.11、内存地址空间

如果一个cpu的地址总线为10,那么可以寻址1024个内存单元(2的10次方),那么这1024个可被寻到内存单元就构成了这个CPU的内存地址空间
在了解深入了解内存地址空间之前,我们需要了解部分基础知识:主板和接口卡

1.主板
在每一台PC中,都有一个主板,主板上有核心器件(CPU、内存)和一些主要器件
这些器件通过总线(地址总线,控制总线,数据总线)相连。
同学们可以自己拆开看啊

2.接口卡
计算机系统中,所有可用程序控制其工作的设备,必须受到CPU的控制。
CPU不能直接控制外部设备,如音响,打印机,显示器等。直接控制这些设备进行工作的是插在扩展插槽上的接口卡。比如显卡。CPU控制显卡,显卡再控制显示器。再比如网卡,CPU控制网卡,网卡负责和网线交流数据。耳机,打印机,通过USB。

3.各类存储器芯片
从读写属性上来划分的话,分为两类
随机存储器(RAM),如内存,显存。断了电数据就会消失
只读存储器(ROM),数据永远都能保存在里面,比如装有BIOS的ROM,在开机时能看到黑屏幕打印出一堆看不大懂的白色字体,那会就是BIOS在发生作用,它在检测你的计算机状态,如内存,cpu,硬盘等等。个人理解如果你可以把病毒刷到BIOS里面去,那这个病毒就成为了无敌状态,杀毒软件无论如何也是杀不了的,可惜刷不得。因为ROM只可以被读取
BIOS是由主板和各类接口卡(如显卡,网卡等)厂商提供的软件系统(程序),可以通过它,利用各类硬件设备进行最基础的输入输出。在主板和某些接口卡上插有存储着相应BIOS的ROM

上图为逻辑上的连接,并不是物理连接。不要错误理解

现在我们说回来内存地址空间,上述的那些存储器在物理上都是独立的器件。
但是他们也存在共同点:
1.都和CPU的总线相连
2.CPU对它们进行读或写的时候,都通过控制线发出内存读写命令

从CPU的视角上来看
如下图所示,在CPU的视角上,它看到的是中间那么一堆地址空间。看成是一个整体,CPU本身看不到后面的具体器件。

那么如果想在屏幕上显示一张图片,就需要往显存中写入数据,CPU怎么去确定显存的地址空间呢?其实不同的计算机系统有不同的规定,我们为了便于理解,做一个假设,上图的内存空间的地址段分配如下:
地址 0 ~ 777FH的32KB空间为主存储器的地址空间 。那么CPU往这个地址写数据就是操作内存了
地址 8000H ~ 9FFFH的8kb空间为显存的地址空间,如果操作这个地址段,那么则是去操作了显存
地址 A000H ~ FFFFH的24KB空间为各个ROM的地址空间

上面的分配都是我们虚拟认为的,实际上不同的系统规定也是不同的。
如intel的8086CPU,它的内存地址空间分配如下图

现在大家用的都是I7,I9了,比起8086CPU的内存地址空间,要大非常多了。
这就是为什么32位CPU,你上16G内存没有用的原因,因为它本身地址总线的宽度为32,则内存地址空间最大为4GB
2的32次方 = 4294967296Byte = 4G左右

小结
最终执行程序的是CPU,所以我们需要在CPU的角度上思考问题,对CPU来说,系统中的所有存储器中的存储单元都处于一个统一的逻辑存储器中,也就是以CPU的视角能看到的东西,它的容量受到CPU的寻址能力的限制,这个逻辑存储器就是我们所说的内存地址空间。



总结

1.汇编指令是机器指令的助记符,同机器指令一 一对应。
2.每一种CPU都有自己的汇编指令集
3.CPU可以直接使用的信息在存储器中存放。
4.在存储器中,指令和数据没有任何区别,都是二进制信息。
5.存储单元从0开始顺序编号。
6.一个存储单元可以存储8个bit,即8位二进制数
7.每一个cpu芯片都有许多管脚,这些管脚和总线相连,一个CPU可以由管脚引出三种总线,三种总线的宽度标志了这个CPU在不同方面的性能:
地址总线的宽度决定了CPU的寻址能力
数据总线的宽度决定了CPU与其他器件进行数据传送时,一次数据的传送量
控制总线的宽度决定了CPU对系统中其他器件的控制能力。



原书中的检测点

王爽老师说没有通过检测点不可以继续学习
自主完成
1、1个CPU的寻址能力为8KB,那么它的地址总线的宽度为?
2、1KB的存储器有多少个存储单元?存储单元编号从x到y?(x和y是多少)
3、1KB的存储器可以存储多少个bit?多少个Byte?
4、1GB、1MB、1KB分别是多少个Byte?
5、8080、8088、80286、80386 CPU的地址总线宽度分别为16根、20根、24根、32根,则他们的寻址能力分别为_KB、_MB、_MB、_GB
6、8080、8088、8086、80286、80386 CPU的数据总线宽度分别为8根、8根、16根、16根、32根,则它们一次可以传送的数据为:_B、_B、_B、_B、_B。
7、从内存中读取1024字节的数据,8086至少要读次、80386至少要读_次。
8、在存储器中,数据和程序以__形式存放。







检测点答案

1、寻址能力为8KB,即8192字节,总共可以找到8192个内存单元。为2的13次方,所以地址总线宽度为13
2、1KB = 1024B,则有1024个存储单元,编号从0到1023,存储地址通常以16进制表示,即编号000H到3FFH
3、1KB = 1024B = 8192bit
4、1GB = 1024 x 1024 x 1024Byte,1MB = 1024 x 1024Byte ,1KB = 1024Byte
5、16根为2^16Byte,再除以1024等于64KB,同理,20根为1MB,24根为16MB,32根为4GB
6、8根数据总线一次可以传输8位二进制数,也就是1个字节,所以答案为,8根1B,16根2B,32根4B。
7、8086的数据总线为16根,则一次传输2B的数据,1024字节需要,512次。同理80386位32根,一次传输4B,则需要256次
8、在存储器中,数据和程序以二进制形式存放

用户名金币积分时间理由
天狗 5.00 0 2021-05-15 15:03:04 一个受益终生的帖子~~
奖励系统 100.00 0 2021-04-21 08:08:27 投稿满 10 赞奖励
奖励系统 50.00 0 2021-03-16 21:09:59 投稿满 5 赞奖励
admin 100.00 0 2020-06-15 15:03:16 核心审核通过

打赏我,让我更有动力~

7 条回复   |  直到 2021-6-17 | 2088 次浏览

l836918621
发表于 2020-6-17

指不定哪天就要用了。支持镜姐

评论列表

  • 加载数据中...

编写评论内容

superkoier
发表于 2021-3-15

现在已经这么屌了么!!!

评论列表

  • 加载数据中...

编写评论内容

顾双
发表于 2021-3-18

支持!今天我看这个,我也进步了一些!

评论列表

  • 加载数据中...

编写评论内容

mamor丶
发表于 2021-3-29

感谢

评论列表

  • 加载数据中...

编写评论内容

xiao_yi
发表于 2021-4-21

感谢

评论列表

  • 加载数据中...

编写评论内容

苟子狗子钩子
发表于 2021-5-7

强啊

评论列表

  • 加载数据中...

编写评论内容

小花生
发表于 2021-6-17

学习汇编对渗透有什么用呢?

评论列表

  • 加载数据中...

编写评论内容
登录后才可发表内容
返回顶部 投诉反馈

© 2016 - 2024 掌控者 All Rights Reserved.