汇编
1、课程概要
1、什么是机器语言
加:0100 0000 减:0100 1000 乘:1111 0111 1110 0000 除:1111 0111 1111 0000
缺点:不方便记忆
2、汇编语言:
由机器语言发展而来,可用符号表示数值间的运算
加:INC EAX → 0100 0000 减:DEC EAX 0100 1000 乘:MUL EAX 1111 0111 1110 0000 除:DIV EAX 1111 0111 1111 0000
优势:相对于机器语言,更简单 劣势:不符合人类的思维范式
编译器: 可将汇编指令转换为机器语言的工具
3、C语言:
加:A+B 0100 0000 减:A-B 0100 1000 乘:A*B 1111 0111 1110 0000 除:A/B 1111 0111 1111 0000
C语言属于高级语言,更加贴近人类的思维方式
4、C与C++的关系
C与C++其实没有本质区别,只是编译器做的事情越来越多,越来越强大
C语言是C++的基础
5、学习汇编的原因
一个不懂汇编的人,都是不懂原理的
如果从事安全行业,汇编是必须要掌握的基础(外挂开发等)
对于正向开发,懂汇编可以做的更好,潜力更大
6、学习环境
VC6
VS2010,2013,2016
选择VC6的原因:越是新版本的编译器,在编译的时候添加的细节代码会更多,不利于学习
2、进制
1、学习进制的原因
计算机只认识二进制,为了更好理解计算机,就需要深刻理解进制
2、学习进制的障碍
总以十进制为依托,需要运算的时候也是先转换为10进制,这种学习方法是错误的
想要学好进制,就要先忘掉十进制,忘掉进制间的转换
3、进制的定义
进制 | 组成 | 规则 |
八进制 | 有八个符号组成,0 1 2 3 4 5 6 7 | 逢八进一 |
十进制 | 有十个符号组成,0 1 2 3 4 5 6 7 8 9 | 逢十进一 |
N进制 | 有N个符号组成 | 逢N进一 |
4、进制的书写
在进制的运算中,没有必要对进制进行转换,可以直接进行计算
进制 | 写法 |
1 | 0,1,11,111,1111,11111 |
3 | 0,1,2,10,11,12,21,22 |
7 | 0,1,2,3,4,5,6,10,11,12 |
5、你能理解1+1=3吗?
学习进制,就要理解进制的深度,而不是表面现象
如果将进制用在加密解密方向,就会给解密的人带来很大的困扰
3、进制的运算
每一种进制的体系都是完美的,不需要转换成其他进制进行运算
1、八进制运算
2+3=?(5) 2*3=?(6) 2+3=?(5) 4+5=?(11) 4*5=?(24) 277+333=?(277+333=7+3(12)+(130)+2+4(6)=632) 276*54=?() 237-54=?() 234/4=?()
0,1,2,3,4,5,6,7,10,11,12,13,14,15,16,17,20,21,22,23,24
2+3为从2开始,往后查3个数,其他相同
制作八进制的加法表与乘法表:
0,1,2,3,4,5,6,7,10,11,12,13,14,15,16,17,20,21,22,23,24,25,26,27
乘法表的本质,也是查询数字
进制总结: 每种进制都是完美的,他自身就是一个完整的体系,可以直接进行各种运算
4、二进制简写形式
1、计算机为什么使用二进制
计算机是需要用电的,电路只有两种状态:1 真(通电),0 假(未通电) 。 计算机中存储的任何文件,接收的任何指令都是由0和1组成的(查看一个.exe程序)
查看EXE文件使用工具为:UltraEdit
其中内容为:
文件的地址位:文件中的内容:UE的注释
2、二进制的简写形式
二进制:从0写到1111 0000 0001 0010 0011 0100 0101 0110 0111 1000 1001 1010 1011 1100 1101 1110 1111
这种二进制使用起来太麻烦,改写成更简单一点的符号:
0 1 2 3 4 5 6 7 8 9 A B C D E F 这就是十六进制了,中国古代时候已经开始使用十六进制了
建议:写代码的时候要习惯十六进制,最好不要用十进制
十六进制:可读性更强
3、熟练掌握进制之间的转换
5、数据宽度
1、数据宽度
数学上的数字,是没有大小限制的,可以无限大,但在计算机中,由于的硬件制约,数据都是有长度限制的(数据宽度),超出宽度的数据会被丢弃
2、计算机中常见的数据宽度
单位 | 宽度 | 存储范围 |
位(BIT) | 1 | 0-1 |
字节(BYTE) | 8 | 0-FF |
字(WORD) | 16 | 0-FFFF |
双字(DOUBLEWORD) | 32 | 0-FFFFFFFF |
位是计算机中存储数据的最小单位 一个字节能存储8个位 字有16位 双字能存储32个位
讲解中的示例代码:
6、有符号数和无符号数
在不同文件中,0和1的含义都是不同的
1、无符号数的编码规则
十六进制显示的话,则是:0x9A,十进制是:154 文件不同,解析方法不同
2、有符号数,正数编码规则:1A
如果最高位是1,则为负数;如果最高位是0,则为正数
如果一个数是有符号数,且最高位是0,那么这个数一定是正数
当数字为正数时,有符号与无符号的显示结果是一样的
7、数字的编码
1、编码规则
不同的文件有不同的编码规则 有符号数与无符号数解析成果是一样的,若首位是0 负数的编码规则与正数不同
2、有符号数的编码规则
原码:最高位为符号位,其余各位是数值本身的绝对值 反码: 正数:反码与原码相同 负数:符号位为1,其余位对原码取反 补码: 正数:补码与原码相同 负数:符号位为1,其余位对原码取反加1
3、举例说明
例子 | 原码 | 反码 | 补码 |
1 | 0000-0001 | 0000-0001 | 0000-0001 |
6 | 0000-0110 | 0000-0110 | 0000-0110 |
-1 | 1000-0001 | 1111-1110 | 1111-1111 |
-7 | 1000-0111 | 1111-1000 | 1111-1001 |
总结:
正数原码存储(编码规则)
负数补码存储(编码规则)
4、假设数据宽度为1BYTE(8 BIT)
无符号数:0 1 2 3 4 5 ........ FF (10进制255) 有符号数: 正数:0......7F 负数:FF......80
5、假设数据宽度为DOUBLEWORD(双字)
无符号数:0 1 2 3 4 5 ........ FFFFFFFF 有符号数: 正数:0......7FFFFFFF 负数:FFFFFFFF......80000000
8、计算机的运算
计算机是通过位运算来做的运算
为什么要学习位运算?
有些功能必须通过位运算才能实现:比如写调试器,判断CPU的各种状态位
大公司面试题:比如2*8的效率最高的实现方式
1、与运算 and(&)
两个数都为1时,结果才是1
比如:1011-0001&1011-0000=10010000
2、或运算 or (|)
只要有一个为1就是1
比如:1011-0001|1101-1000=1111-1001
3、异或运算 xor(^)
不一样的时候是1
比如:1011-0001^11011000=01101001
4、非运算 not(~)
0就是1,1就是0 注意:非运算只需要一个数字,单目运算
比如:1101-1000 = 0010-0111
5、移位运算
5-1、左移: 各二进位全部左移若干位,高位丢弃,低位补0 比如: shl(<<) 1101-1000左移2位为: 0110-0000
5-2、右移: 各二进位全部右移若干位,低位丢弃,高位补0或者补符号位
比如:
在C语言中,如果数字是无符号的,那么在移动后会补0;若是有符号的,那就是补符号位
6、总结:计算机只会做位运算
9、位运算之加减乘除
4+5的运算过程:
4:0000-0100,5:0000-0101
4+5的异或运算:0000-0001
如果不考虑进位的情况下,异或的结果与按位加的结果是一样的
异或类似于按位加,此时必须判断有无进位,如果有进位,就要拿出来进位的值再加
判断是都有进位(只有两个数相加的时候才会有进位),进行与运算。若结果不为0,则说明有进位
判断标准:哪个数字是1,那就说明哪里有进位
异或与按位加是一样的结果,只是拿掉了进位的值
继续异或,将(上次异或出来的结果)与(进位得到的数字左移一位后)继续异或预算
使用与运算,判断是否有进位。若得到结果为0,则说明没有进位
得出最终结果
过程简化后:
异或运算:两数字进行异或运算(结果为C)
与运算:判断两数字进行与运算时是否有进位(结果为D)
异或运算,用C与D进行异或运算,得出最终结果(结果为E)
与运算,用C与D进行与运算,若结果为0,则E为最终结果
4-5的运算过程:
4-5=4+(-5)
异或运算:0000-0100 异或 1111-1011=1111-1111
与运算:判断是否有进位:0000-0100与1111-1011=0000-0000
说明第二步的运算结果就是最终结果,1111-1111:FF,-1
乘法的运算过程: X*Y的本质就是加法:Y个X相加
除法的运算过程: X/Y的本质就是减法:X能减去多少个Y
10、汇编学习环境搭建
1、学汇编不是为了写代码
学汇编的目的:了解程序的运行机制,汇编是高手的必经之路
正常应用:需要明白C语言等于汇编的对应关系
安全:做外挂以及反外挂等,需要了解汇编与二进制的关系。PE与硬编码也需要了解
课堂讲的是32位的汇编,不是win32汇编。 笔记程度:可看懂普通的应用程序
若想研究擦做系统之类的,需要了解保护模式等相关内容
2、配置汇编的学习环境
DTDEBUG下载
配置路径
当想使用OD编写汇编代码时,可随便拖拽一个EXE文件进去,可看到如下页面
当左上角显示黄色时,说明程序已暂停,此时可双击右面的汇编代码并打开窗口,并在新开的窗口中编写汇编代码
窗口名称:从左往右依次为:反汇编窗口,寄存器,内存窗口与堆栈
11、通用寄存器
1、寄存器
存储数据: CPU>内存>硬盘 32位CPU:8 16 32 64位CPU:8 16 32 64
寄存器:CPU用来存储数据的地方 若是32位CPU,有提供容器有3种类型,为8位16位,32位 若是64位CPU,有提供容器有4种类型,为8位16位,32位,64位
学习32位汇编的理由:
64位CPU与32位CPU并没有实际的变化,只是增加了新的寄存器,汇编指令都是一样的。若掌握了32位汇编,可以直接看懂64位汇编
绝大多数程序都是32位的
2、通用寄存器
32位通用寄存器: 在32位寄存器中,大多数寄存器有特殊用途 在32位的寄存器中,存储的数据不能超过32位,也就是不能超过32个0和1,否则多余的部分会被丢弃
名称 | 编号 | 数据存储范围 |
EAX | 0 | 0 - 0xFFFFFFFF |
ECX | 1 | 0 - 0xFFFFFFFF |
EDX | 2 | 0 - 0xFFFFFFFF |
EBX | 3 | 0 - 0xFFFFFFFF |
ESP | 4 | 0 - 0xFFFFFFFF |
EBP | 5 | 0 - 0xFFFFFFFF |
ESI | 6 | 0 - 0xFFFFFFFF |
EDI | 7 | 0 - 0xFFFFFFFF |
不同位数的通用寄存器
32位 | 16位 | 8位 |
EAX | AX | AL |
ECX | CX | CL |
EDX | DX | DL |
EBX | BX | BL |
ESP | SP | AH |
EBP | BP | CH |
ESI | SI | DH |
EDI | DI | BH |
其中,L代表低八位,H代表高八位
2、MOV指令
作用:移动数据
汇编可理解为:寄存器与寄存器之间,寄存器与内存之间数据的来回流动 想学好汇编需要记住的关键词:寄存器,内存,指令
立即数到寄存器
寄存器到寄存器
若想执行语句,可点击F8,执行后,寄存器的语句会变成红色
也可以将寄存器中的值传到另一个寄存器中,例:
其中,因为eax中的值为1,所以edx的值也是1
八位寄存器:
注意:寄存器之间的数值移动,需要位数相同,若位数不同,会报错,不符合语法
12、内存
当存储数据足够多的时候,可将数据存储到内存中
内存:并不是内存条
1、每个应用程序都会有自己独立的4GB内存空间
其中,每个进程的4GB内存空间并不是真正的物理内存,是假的 当进行读写操作时,会将进程中额内存映射到物理内存中 物理内存与内存条之间,还有一层映射
在编程过程中,提到的内存,往往指的是4GB的虚拟内存
1Byte=8BIT 1KB=1024Byte 1MB=1024KB 1GB=1024MB 1TB=1024GB 1PB=1024TB
2、内存地址
内存太大没办法起名字,所以只能用编号当我们想想内存中存储数据时,或者从内存中读取数据时,必须用到这个编号,就像写信必须要写收信人一样
这个编号又叫内存地址(32位,前面的0可以省略)
对于编程人员来讲,内存与寄存器都是用来存储数据的容器,故内存太大了,就用编号代替
其中:
[0x00000000]称为内存地址,为内存块,是8位一字节
在读和写的时候,必须使用内存地址
内存地址有32位,如果转换成16进制的话,就是8位
因为地址宽度是32位,宽度决定了能查找的最大的范围就是4GB,值最大为100000000
计算过程:
16进制最大寻址范围是FFFF-FFFF,加一后为10000-0000
因为每个字节是8位,乘以8就是1-0000-0000
转换成10进制,得到数字:34359738368
除以8以后得出结果为:4294967296
除以1024后,得出结果为:4194304(能找到的KB的数量)
除以1024后,得出结果为:4096(能找到的MB的数量)
除以1024后,得出结果为:4(能找到的GB的数量)
所以,每个进程都有自己的4GB的内存空间
注意:
内存必须有地址,这个地址的宽度是32位的
每个地址对应的内存有一个字节8位,8个0和1
3、MOV指令
立即数到内存
寄存器到内存
内存到寄存器
语句含义:将1写到内存中,写到1个字节中 ptr ds:[] 为地址编号,方括号中的内容不可以随便写,必须是已经申请了的地址
填入地址后,点击确定即可
其中,宽度为8,点击F8后:
注意:向内存中写数据的时候,一定要告诉他宽度是多少
其中: DWORD代表要写的内存有多大,数据宽度是多少 ptr ds:[]是固定写法 []中的内容,就是地址,也可称为编号 每一个编号对应的是一个字节
编号是连续使用的
可以将立即数写到内存中,也可以将寄存器写到内存中,但前提是:宽度必须是一样的
注意: 在使用MOV指令的时候,要保证两边的宽度是一样的
将内存中的值写入寄存器中:
在汇编中,就大多数指令是不允许从内存到内存的,其中包括MOV指令;必须经过寄存器;也不能从内存到立即数。立即数不是容器,只能往别的东西里放
涉及代码:
内存的五种表示形式:
立即数:
读取内存的值:MOV EAX,DWORD PTR DS:[13FFC4]
向内存中写入数据:MOV DWORD PTR DS:[13FFC4],EAX
[reg] reg代表寄存器,可以是8个寄存器中的任意一个
读取内存的值:MOV EAX,13FFD0 ; MOV EAX,DWORD PTR DS:[ECX]
向内存中写入数据:MOV EDX,13FFD8 ; MOV DWORD PTR DS:[DEX],87654321
[reg+立即数]
读取内存的值:mov ecx,13ffd0 ; mov eax,dword ptr ds:[ecx+4]
向内存中写入数据:mov edx,13FFD8 ; MOV DWORD PTR DS:[EDX+0xC],87654321
[reg+reg*{1,2,4,8}] #在高级语言中对数组赋值,最终生成的汇编语言就是这样的 八个32位的通用寄存器加上八个32位的通用寄存器乘以1或2或4或8
读取内存的值:MOV EAX,13FFC4 ; MOV ECX,2 ; MOV EDX,DWORD PTR DS:[EAX+ECX*4]
向内存中写入数据:MOV EAX,13FFC4 ; MOV ECX,2 ; MOV DWORD PTR DS:[EAX+ECX*4],87654321
[reg+reg*{1,2,4,8}+立即数] #
读取内存的值:MOV EAX,12FFC4 ; MOV ECX,2 ; MOV EDX,DWORD PTR DS:[EAX+ECX*4+4]
向内存中写入数据:MOV EAX,13FFC4 ; MOV ECX,2 ; MOV DWORD PTR DS:[EAX+ECX*4+4],87654321
学习五种形式的作用:看得懂各式编译器反编译出来的汇编代码
13、数据的存储模式
1、存储模式
大端模式:数据高位在低位,数据低位在高位 小端模式:数据低位在低位,数据高位在高位
2、3、4、
14、
15、
16、
17、
18、
19、
20、
21、
22、
23、
24、
25、
26、
最后更新于