汇编入门学习

学自狂神:最通俗易懂的计算机底层教学,二进制到汇编学习!

1、概述

学习路线:

  • 语言

  • 进制

  • 进制如何运算

  • 二进制

  • 数据宽度

  • 有符号数和无符号数

  • 原码反码补码

  • 位运算

  • 位运算计算

  • 汇编

  • 寄存器

  • 内存

  • 汇编指令

  • 内存复制

  • 堆栈的指令

  • 汇编如何写函数

  • 堆栈传参

  • 堆栈平衡

2、机器语言

什么是机器语言?

# 我们目前主流的电子计算机!
状态:  0和1
# 最早的程序员:穿孔卡带!
# 需要解决的最基本的问题:
# 假如:
加 0100 0000
减 0100 1000
乘 0100 1000 0100 1000
除 0100 1000 1100 1000

这些加减乘除的机器语言能简化么?----用助记符!即汇编语言,把人能理解的语言转换成机器能够理解的语言。

加 INC  -编译器-->0100 0000
减 DEC  -编译器-->0100 1000
乘 MUL  -编译器-->0100 1000 0100 1000
除 DIV  -编译器-->0100 1000 1100 1000

因此,在人和程序的本质之间有隔阂,这就需要汇编的帮助。汇编一般用于底层的编写,如单片机…

再进一步:

C语言

加 A+B  -编译器-->0100 0000
减 A-B  -编译器-->0100 1000
乘 A*B  -编译器-->0100 1000 0100 1000
除 A/B  -编译器-->0100 1000 1100 1000

在这里插入图片描述

底层学习软件:VC6 OD

3、进制

3.1、定义

学习进制的障碍**--------10进制

人类天然的选择就是10进制,因为有10个指头。因此需要跳出固有的思维。

二进制

思想:每一种进制都是完美的,都有自己的计算方式!

进制?

  • 1进制: 一进一,结绳记事。1 1

  • 2进制: 二进一,计算机

  • 八进制: 八进一。8个符号组成:0 1 2 3 4 5 6 7

  • 10进制: 10进一。10个符号组成:0 1 2 3 4 5 6 7 8 9

  • 16进制: 16进一。16个符号组成:0 1 2 3 4 5 6 7 8 9 a b c d e f

测试

# 1进制: 1~20
1
1 1
1 1 1
...

# 3进制 1~20
0  1  2
10 11 12
20 21 22
100 101 102
110 111 112

# 7进制 1~20
0  1  2  3  4  5  6 
10 11 12 13 14 15 16
20 21 22 23 24 25 26
30 ...

# 2进制 1~20
0 1
10 11
100 101
110 111 
1000

问题:你真的理解进制了吗? 1+1=3对吗?! 如果你可以使用进制来解答这个问题,那么你就学会了!

十进制:0 1 2 3 4 5 6 7 8 9
狂神的十进制: 0 2 4 7 8 a b r d f,可以自己随便定义的,学习,创造者!

加密解密:程序员,破解程序的人! 进制的加密
数字量一大,总是有规律的!

3.2、进制的运算

运算的本质就是查数。

# 八进制的运算
0 1 2 3 4 5 6 7 10 11 12 13 14 15 16 17 20 21 22 23 24 25 26 27 

# 则八进制简单计算
2+3=5   2往后数3个数是5
2*3=6
4+5=11  4往后数5个数是11
4*5=24  1 2 3 4是1个4,5 6 7 10是2个4,21 22 23 24是5个4
# 八进制复杂计算
277+333=
276*54=
237-54=
234/4=

八进制加法表:

1+1=2
1+2=32+2=4
1+3=42+3=53+3=6
1+4=52+4=63+4=74+4=10
1+5=62+5=73+5=104+5=115+5=12
1+6=72+6=103+6=114+6=125+6=136+6=14
1+7=102+7=113+7=124+7=135+7=146+7=157+7=16

八进制乘法表:

1*1=11*2=21*3=31*4=41*5=51*6=61*7=7
2*2=42*3=62*4=102*5=122*6=142*7=16
3*3=113*4=143*5=173*6=223*7=25
4*4=204*5=244*6=304*7=34
5*5=315*6=365*7=43
6*6=446*7=52
7*7=61
# 加法 277+333=
277
333 +
------
632 
===================================================
# 乘法 276*54=
    276
     54 *
    ------
   1370
  1666  +
  --------
  20250  
====================================
# 运算的本质就是查数
# 减法 本质其实是加法 237-54=237+(-54)
237
-54
-----
163
# 除法 除数乘以那个数最接近结果即可!
234
  4  /
-------
23/4=4···3
34/4=7
即234/4=47

结论:无论是什么进制,本身都是有一套完美的运算体系的,我们都可以通过列表的方式将它计算出来!

4、二进制

计算机使用二进制 0 1—>表示状态!目前的计算机都是电子计算机!

物理极限:摩尔定律–>当价格不变时,集成电路上可容纳的晶体管数目,约每隔18个月便会增加一倍,性能也将提升一倍。换言之,每一美元所能买到的电脑性能,将每隔18个月翻两倍以上。这一定律揭示了信息技术进步的速度。 硬操作!

当物理极限达到以后,就会追求语言的极限—>并发语言! 软操作!

量子计算机:(传道)

  • 可以实现量子计算的机器。

传统的计算机:集成电路!!0 1 。硅晶片!
量子计算机的单位:昆比特。(量子比特!)量子的两态来表示。

光子:正交偏振方向。
磁场:电子的自旋方向。

回到电子计算机: 0 1

# 二进制
0  1  10  11  100  101  110  111  1000  1001  1010  1011  1100  1101  1110  1111
# 二进制的标志位
2  10
4  100
8  1000
16 10000

二进制这么写很麻烦,能否简写?

0  1  2   3   4    5    6    7    8     9      a     b     c     d     e     f

即1111用f表示,1110用e表示

这就是十六进制。

为什么要学习理解二进制?

寄存器、内存、位! 底层的每一个位都是有含义的。汇编入门理解的基础!

汇编高级:了解程序的深层!操作系统的内核?

5、数据宽度

C、C++、Java都需要定义数据的类型,因为计算机需要我们给这些数据定义宽度。

0 1
字节  80~0xFF   0x表示十六进制  F表示1111
字   160~0xFFFF
双字  320~0xFFFFFFFF

在计算机中,每一个数据都需要定义类型,目的是定义它的宽度,在内存中的宽度。

6、有符号数和无符号数

数据都是有宽度的。每个数据代表什么意思呢?

0 1 0 1 0 1   这表示的是什么意思?

因此,需要定制规则来表示数据代表的含义。

无符号数规则

数字是什么,就表示什么

二进制:1001 1010   十六进制:0x9A    十进制:154

有符号数规则

最高位是符号位:1(负数) 0(正数)

二进制:1001 1010  该如何转换?

7、原码反码补码

编码规则

有符号数的编码规则

原码:最高位为符号位,对其它的位进行本身绝对值即可。

反码:

  • 正数:反码和原码相同
  • 负数:符号位一定是1。其余位对原码取反。

补码:

  • 正数:反码和原码相同
  • 负数:符号位一定是1。反码+1
# 测试
# 以下都是8位的二进制数
# 如果是正数,那都是一样的
1
# 原码 0000 0001
# 反码 0000 0001
# 补码 0000 0001

-1
# 原码 1000 0001
# 反码 1111 1110   除符号位外其余位取反
# 补码 1111 1111   反码+1

-7
# 原码 1000 0111
# 反码 1111 1000   除符号位外其余位取反
# 补码 1111 1001   反码+1

如果看到一个数字,二进制的,需要了解它是有符号数还是无符号数。

8、位运算

计算机现在可以存储所有的数字(整数、浮点数、字符),进行运算。

存储数字的时候,会有补码的形式存储。

如果可以把这些数字加以运算,我们就可以做到世界上的一切。无论多复杂的运算,底层都是加减乘除。我们只要把位运算的位如何操作运算记住、突破就可以了。

首先有一个面试高频题:2*8最高效的计算方式?

这道题不论怎样都非常慢,只有通过位运算才是最快的,比如左移、右移。而且要记住一句话:很多底层的调试器(例如C语言调试器),当我们手写调试器的时候就需要通过位来判断CPU的状态。

位运算就是我们常说的与或非 异或运算等…我们一个一个来看:

与运算:

在JAVA语言中用 & 符号去表示,但是在汇编中用 and 代表与。下面图片方便我们的理解:

在这里插入图片描述

1011 0001
1101 1000
-------------- 与运算的结果
1001 0000

或运算

在JAVA语言中用(|)表示,在汇编语言中用or表示,同样根据或运算也有一张电路图可以帮助理解:

在这里插入图片描述

1011 0001
1101 1000
--------------- 或运算
1111 1001

异或运算:

在我们JAVA语言中用(^)表示,在汇编语言中xor表示。说白了记住一句话:不一样就是1。再来一张电路图理解:

在这里插入图片描述

1011 0001
1101 1000
--------------------异或运算
0110 1001

非运算(单目运算符):

我们所谓的取反(非),在JAVA语言中是(!),在C语言中是(~),在汇编语言中是not。
说白了一句话:0就是1,1就是0。

1101 1000
----------------- 非运算 
0010 0111

通过这些可以完成我们的加减乘除。怎么通过位运算实现加减乘除呢?

位运算:

它是一个移动位,分为左移,右移。(在10进制中,左移相当于 *2,右移相当于 /2)。

左移(shl <<):

0000 0001  所有的二进制位全部左移若干位,高位就丢弃,低位补0
0000 0010

右移(shr >>):

0000 0001  所有二进制全部右移若干位,低位就丢弃,高位补0或1(根据符号位决定,负数补1,正数补0)
0000 0000

9、位运算的加减乘除

接下来我们讲,如何通过位运算实现加减乘除。我们的计算机只认识0和1,但是基本的数学是建立在加减乘除上。

案例:

4+5=?

十进制是9,那么计算机是怎么做的?

# 计算机操作步骤
0000 0100
0000 0101
-----------  人为可以直接加法操作,但计算机不会,只能进行与、或、异或运算
0000 1001

================================
# 那么计算机的实现原理是什么呢?
# 怎么将两个数加起来?核心是:异或。

# 第一步,异或(不一样为1):如果不考虑进位,异或就可以直接出结果
0000 0100
0000 0101
--------------
0000 0001

# 第二步,计算机在这个异或的结果上再做与运算操作:
# 与运算(判断进位),如果与运算结果为0,那么就没有进位。
0000 0100
0000 0101
--------------
0000 0100     结果中有1,说明有进位

# 第三步,将第二步与运算的结果左移一位:
0000 0100--->0000 1000
# 第四步,异或  将第一步异或的结果与第三步与运算的结果进行异或运算
0000 0001
0000 1000
------------
0000 1001
#第五步,继续与运算判断有没有进位
0000 0001
0000 1000          上一步异或的数
------------
0000 0000  结果都为0,没有进位了
# 则最终计算结果为,与运算结果都为0的上一步异或运算结果:
0000 1001   此结果即为十进制的9

4-5=?

# 计算机操作步骤
# -5  原码:1000 0101  反码:1111 1010 补码:1111 1011
0000 0100
1111 1011
---------- 直接加
1111 1111   ff

# 第一步:异或
0000 0100
1111 1011
---------- 
1111 1111
# 第二步:与运算
0000 0100
1111 1011
---------- 
0000 0000  没有进位
# 结果为:1111 1111  16进制表示:ff  10进制表示:-1
# 因为计算机是以补码的形式存储数字的,因此转回原码:1111 1111(补码)-->1111 1110(反码)-->1000 0001(原码)

9、汇编语言环境说明

目前为止,我们可以从零设计一套自己的进制规则。自己设计电路来实现加减乘除。但是最终乘除的结果是一个二进制,例如:我们有32个灯泡,就可以显示32个数值,最终亮起灯泡的数就是最终的结果。手动转换这个结果和值!(十进制和二进制的转换)

机器语言并不会做很多事情,它很“笨”。机器语言说白了就是位运算,(加减乘除)
都是电路来实现的。这就是计算机最底层的本质。

但是,我们发现,学完了这些东西依旧是不懂,只是对现在的程序做了一些提高的理解。但是想通过理解写程序写不出来,难道我们真的写不出来吗?

通过机器语言来实现加法计算器,这就是设计电路。

我们通过指令来代替我们的一些二进制编码!比如说:刚才的加法运算是通过各种操作能否通过一个符号计算呢?比如说我就想叫它(ADD指令),假设我给计算机发一个ADD指令,它通过识别我的指令转换成机器语言(也就是编译)ADD指令转换为二进制操作。

在这里插入图片描述

汇编语言说白了,底层还是二进制,但是二进制写起来太麻烦了。这个时候我们通过汇编指令给计算机发一些操作,然后让计算机执行。这个地方就要涉及到编译器!因为我们说的编译命令不是机器直接能识别的,需要把命令转码,转成计算机认识的信息,不然没法识别。这个时候就涉及到编译器的发展。

如果学底层,一定不要用特别智能的编译器(IDEA,VSCODE等),就是用越远古的越好(记事本,vim等)。很多人学习C语言使用,用的是vim编辑器去写C语言,用gcc来执行。这是学习C的正确方式。底层的大佬几乎都是最原始的idea。

在学习汇编之前,先要掌握环境的配置:

  • Vc6(程序到汇编的理解,通过C语言实现)

  • OD

  • 抓包工具

  • 加密解密工具

尽量不要使用java去学汇编,学完了汇编去学jvm就会觉得很简单。但是如果我学java再学汇编就有点痛苦,建议使用C++学汇编。因为C++可以直接看到内存的地址,可以打印通过指针直接找到内存的地址,通过地址判断信息。

学汇编不是为了写代码,就是为了理解程序的本质。
如果懂汇编,就能够理解所有复杂的概念。

如果我们是一个做应用的程序员,别人调试不了的程序,如果学过汇编,都可以调试。因为知道底层堆栈到底做了什么。如果是做安全的(反外挂,反病毒),就要理解汇编到二进制的全部知识。

现在的计算机至少是32位,还有的是64位。我们要知道,它是由32位演化过来的。底层架构没有发生变化,只是多了寄存器,主要是寻址能力增加。

汇编入门: 了解汇编和程序的对应关系,程序的本质即可。

在这里插入图片描述

学会了这些,不理解的java原码就理解了。汇编非常重要!这对我们向上学习有很大的帮助。有些编程技术学进不去,很大原因就是因为底层不够。底层精通了在学习编程语言就发现太轻松了!

10、寄存器的理解

学习汇编,要学习三个重要的概念:

  • 寄存器

  • 内存

  • 汇编指令

通用寄存器:可以存储任何值

存储数据:CPU–>内存–>硬盘

CPU分为32位和64位。

  • 32位:8 16 32

  • 64位:8 16 32 64

32位的通用寄存器只有8个:EAX、ECX、EDX、EBX、ESP、EBP、ESI、EDI、EIP

寄存器中的存值范围:0 ~ FFFFFFFF

计算机如何向寄存器中存值呢?
对于二进制来说,就是直接修改值。但是修改值需要找到对应的位置,所以才有内存地址。

mov指令

mov  存的地址,存的数      作用:可以将数字写入到寄存器
mov  存的地址1,存的地址1   作用:可以将寄存器中的值,写到寄存器。

计算机的本质:计算力! 就是为了计算!(鼠标光标在移动都是在计算)

32位代表八个F(FFFF FFFF),一个F代表4位(1111)
		FFFF	FF    
32168位
EAX     AX		AL
ECX		CX		CL
EDX		DX		DL
EBX		BX		BL
ESP		SP		AH
EBP		BP		CH
ESI		SI		DH
EDI		DI		BH

对于8位:L代表低8位,H代表高8位
16位是FFFF 高八位占前两个FF,低八位占后两个FF

除了这些通用寄存器之外,其他的寄存器每一位都有自己特定的功能(比如:开机关机)。
我们一般写值都会写到通用寄存器中。

11、内存

寄存器很小,而且不够用。所以我们会把数据放到内存中。

有句话:每个应用程序进程都有4GB的内存空间。 程序用的内存就是空头支票,虽然每个应用程序的进程都有4GB内存空间,但是真正到机器上使用时候并没有那么大。

在这里插入图片描述

程序真正运行的时候,才会用到物理内存。

1B = 8bit
1KB = 1024B
1MB = 1024KB
1GB = 1024MB

假设是4GB内存电脑,就等于4096m => 最终计算为位,就是可以存储的最大容量。
计算机中的内存地址很多,空间很大。

内存地址:

存一个数:占用大小,数据宽度。存到哪里呢?

计算机中的内存地址很多,空间很大。我们要给空间取名字,每个空间分配一个地址,名字。

在这里插入图片描述

这些给内存起的编号就是我们的内存地址。32位(8个十六进制的值)

32位:决定寻址能力!

FFFFFFFF + 1 = 100000000,用十六进制表示的能够存储的最大的值。

位是怎么限制内存大小呢?

100000000 内存地址 * 8位 :800000000。     1个字节占8个位,所以乘8
转换为十进制并/8(除8是为了把位转换成字节):4,294,967,296字节。
4,294,967,296/1024=4,194,304KB/1024=4096MB/1024=4GB
按照规则/1024最终发现就是 4GB,因此32位

64位

FFFFFFFFFFFFFFFF + 1 = 
理论上能装128GB内存,但受物理因素影响,最多只能装到64GB

所以每个内存地址都有一个编号:可以通过编号向里面存值

在这里插入图片描述

很多人学C语言搞不懂指针,原因就是 不懂内存。

内存如何存值?(mov指令)

存值需要知道数据宽度:byte word dword

地址位置:0xFFFFFFFF
不是任意的地址都可以写东西,只有程序申请过的内存地址我们才可以使用。

# 汇编如何向内存中写值:
mov 数据宽度 内存地址,要存储的值
mov byte ptr ds:[0x19ff70],1 # ptr ds:[]是固定写法

# 传递的值的大小一定要和数据宽度要相等,如果大放不进去。

内存地址有多种写法:

ds:[0x19FF70+4](内存地址偏移):加 偏移(4),地址就变成了:0x19FF74
ds:[eax](寄存器):把寄存器中的值放到内存中。
ds:[eax + 4](寄存器偏移)

以数组为例:
ds:[reg + reg * {1,2,4,8}]
ds:[reg + reg * {1,2,4,8} + 4] 偏移
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
嗨!对于逆向学习汇编语言的学习笔记,我可以给你一些基本的指导。首先,汇编语言是一种低级语言,它与计算机的底层硬件密切相关。逆向工程则是通过分析和理解已编译的程序来获取程序的内部信息。 以下是一些学习汇编语言和逆向工程的建议: 1. 学习基础知识:了解计算机体系结构、寄存器、内存和指令集等基础概念是必要的。可以先阅读相关的书籍或在线教程,掌握这些基本概念。 2. 掌握汇编语言的语法和指令集:每种计算机体系结构都有自己的汇编语言语法和指令集。选择一种你感兴趣的体系结构(如x86、ARM等),并学习它的汇编语言。 3. 练习编写和调试汇编代码:通过编写简单的汇编代码来熟悉语法和指令集。使用调试器来单步执行代码并观察寄存器和内存的变化。 4. 分析已编译程序:选择一个目标程序进行逆向分析。使用反汇编器将程序转换为汇编代码,并分析代码的逻辑和功能。这有助于理解程序的结构和运行过程。 5. 使用调试器进行动态分析:通过调试器来动态地执行程序,并观察程序在运行时的行为。使用断点、内存查看器和寄存器查看器等工具来分析程序的状态和数据。 6. 学习逆向工程工具和技术:了解常用的逆向工程工具和技术,如IDA Pro、OllyDbg、Ghidra等。掌握这些工具的使用可以提高你的逆向分析能力。 7. 参考优秀资源:阅读与逆向工程和汇编语言相关的书籍、论文和博客,关注相关的社区和论坛。与其他逆向工程师交流经验也是很有帮助的。 记住,逆向工程是一个需要耐心和实践的过程。持续学习和实践将帮助你提高逆向分析的技能。祝你在学习汇编语言和逆向工程的过程中取得好成果!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值