汇编

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++的关系

  1. C与C++其实没有本质区别,只是编译器做的事情越来越多,越来越强大

  2. C语言是C++的基础

5、学习汇编的原因

  1. 一个不懂汇编的人,都是不懂原理的

  2. 如果从事安全行业,汇编是必须要掌握的基础(外挂开发等)

  3. 对于正向开发,懂汇编可以做的更好,潜力更大

6、学习环境

  1. VC6

  2. VS2010,2013,2016

  3. 选择VC6的原因:越是新版本的编译器,在编译的时候添加的细节代码会更多,不利于学习

win7下安装VC6:教程链接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个位

讲解中的示例代码:

// firstCreckme.cpp : defines the entry point for the console application 
//

# include "stdafx.h"
# include <stdio.h>

int main(int argc,char*argv[])
{
    char x = 0x1FF;
    // char 要使用的容器是8位的
    // int  要是用的容器是32位的
    printf("%x\n ",&x);
    
    return 0;
}

6、有符号数和无符号数

在不同文件中,0和1的含义都是不同的

1、无符号数的编码规则

十六进制显示的话,则是:0x9A,十进制是:154 文件不同,解析方法不同

2、有符号数,正数编码规则:1A

如果最高位是1,则为负数;如果最高位是0,则为正数

如果一个数是有符号数,且最高位是0,那么这个数一定是正数

int main(int argc,char* argv[])
{
    int x = 0x1A000000;
    printf("以无符号形式显示:%v\n",x);
    printf("以有符号形式显示:%d\n",x);
    
    return 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

总结:

  1. 正数原码存储(编码规则)

  2. 负数补码存储(编码规则)

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、计算机的运算

计算机是通过位运算来做的运算

为什么要学习位运算?

  1. 有些功能必须通过位运算才能实现:比如写调试器,判断CPU的各种状态位

  2. 大公司面试题:比如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或者补符号位

比如:

shr 1101-0101 0011-0101    #shr补0
对应C语言(>>
unsigned int a = 10;
printf("%d\n",a>>2);

sar 1101-0101 1111-0101    #sar补符号位
对应C语言(>>
int a = 10;
printf("%d\n",a>>2);

在C语言中,如果数字是无符号的,那么在移动后会补0;若是有符号的,那就是补符号位

6、总结:计算机只会做位运算

9、位运算之加减乘除

4+5的运算过程:

  1. 4:0000-0100,5:0000-0101

  2. 4+5的异或运算:0000-0001

如果不考虑进位的情况下,异或的结果与按位加的结果是一样的

  1. 异或类似于按位加,此时必须判断有无进位,如果有进位,就要拿出来进位的值再加

  2. 判断是都有进位(只有两个数相加的时候才会有进位),进行与运算。若结果不为0,则说明有进位

  3. 判断标准:哪个数字是1,那就说明哪里有进位

  4. 异或与按位加是一样的结果,只是拿掉了进位的值

  5. 继续异或,将(上次异或出来的结果)与(进位得到的数字左移一位后)继续异或预算

  6. 使用与运算,判断是否有进位。若得到结果为0,则说明没有进位

  7. 得出最终结果

过程简化后:

  1. 异或运算:两数字进行异或运算(结果为C)

  2. 与运算:判断两数字进行与运算时是否有进位(结果为D)

  3. 异或运算,用C与D进行异或运算,得出最终结果(结果为E)

  4. 与运算,用C与D进行与运算,若结果为0,则E为最终结果

4-5的运算过程:

  1. 4-5=4+(-5)

  2. 异或运算:0000-0100 异或 1111-1011=1111-1111

  3. 与运算:判断是否有进位:0000-0100与1111-1011=0000-0000

  4. 说明第二步的运算结果就是最终结果,1111-1111:FF,-1

乘法的运算过程: X*Y的本质就是加法:Y个X相加

除法的运算过程: X/Y的本质就是减法:X能减去多少个Y

10、汇编学习环境搭建

1、学汇编不是为了写代码

学汇编的目的:了解程序的运行机制,汇编是高手的必经之路

  • 正常应用:需要明白C语言等于汇编的对应关系

  • 安全:做外挂以及反外挂等,需要了解汇编与二进制的关系。PE与硬编码也需要了解

课堂讲的是32位的汇编,不是win32汇编。 笔记程度:可看懂普通的应用程序

若想研究擦做系统之类的,需要了解保护模式等相关内容

2、配置汇编的学习环境

  1. DTDEBUG下载

  2. 配置路径

当想使用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位汇编的理由:

  1. 64位CPU与32位CPU并没有实际的变化,只是增加了新的寄存器,汇编指令都是一样的。若掌握了32位汇编,可以直接看懂64位汇编

  2. 绝大多数程序都是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指令

作用:移动数据

汇编可理解为:寄存器与寄存器之间,寄存器与内存之间数据的来回流动 想学好汇编需要记住的关键词:寄存器,内存,指令

  1. 立即数到寄存器

  2. 寄存器到寄存器

mov eax,1    #向eax存入值1
mov ecx,2    #向ecx存入值2

若想执行语句,可点击F8,执行后,寄存器的语句会变成红色

也可以将寄存器中的值传到另一个寄存器中,例:

mov edx,eax    #将eax的值传到edx中

其中,因为eax中的值为1,所以edx的值也是1

八位寄存器:

注意:寄存器之间的数值移动,需要位数相同,若位数不同,会报错,不符合语法

12、内存

当存储数据足够多的时候,可将数据存储到内存中

内存:并不是内存条

1、每个应用程序都会有自己独立的4GB内存空间

其中,每个进程的4GB内存空间并不是真正的物理内存,是假的 当进行读写操作时,会将进程中额内存映射到物理内存中 物理内存与内存条之间,还有一层映射

在编程过程中,提到的内存,往往指的是4GB的虚拟内存

1Byte=8BIT 1KB=1024Byte 1MB=1024KB 1GB=1024MB 1TB=1024GB 1PB=1024TB

2、内存地址

  1. 内存太大没办法起名字,所以只能用编号当我们想想内存中存储数据时,或者从内存中读取数据时,必须用到这个编号,就像写信必须要写收信人一样

  2. 这个编号又叫内存地址(32位,前面的0可以省略)

对于编程人员来讲,内存与寄存器都是用来存储数据的容器,故内存太大了,就用编号代替

其中:

  1. [0x00000000]称为内存地址,为内存块,是8位一字节

  2. 在读和写的时候,必须使用内存地址

  3. 内存地址有32位,如果转换成16进制的话,就是8位

  4. 因为地址宽度是32位,宽度决定了能查找的最大的范围就是4GB,值最大为100000000

计算过程:

  1. 16进制最大寻址范围是FFFF-FFFF,加一后为10000-0000

  2. 因为每个字节是8位,乘以8就是1-0000-0000

  3. 转换成10进制,得到数字:34359738368

  4. 除以8以后得出结果为:4294967296

  5. 除以1024后,得出结果为:4194304(能找到的KB的数量)

  6. 除以1024后,得出结果为:4096(能找到的MB的数量)

  7. 除以1024后,得出结果为:4(能找到的GB的数量)

所以,每个进程都有自己的4GB的内存空间

注意:

  1. 内存必须有地址,这个地址的宽度是32位的

  2. 每个地址对应的内存有一个字节8位,8个0和1

3、MOV指令

  1. 立即数到内存

  2. 寄存器到内存

  3. 内存到寄存器

mov byte ptr ds:[],1

语句含义:将1写到内存中,写到1个字节中 ptr ds:[] 为地址编号,方括号中的内容不可以随便写,必须是已经申请了的地址

填入地址后,点击确定即可

mov byte ptr ds:[18FFC4],1

其中,宽度为8,点击F8后:

注意:向内存中写数据的时候,一定要告诉他宽度是多少

mov dword ptr ds:[12FFC4],12345678
mov dword ptr ds:[12FFC4],ecx

其中: DWORD代表要写的内存有多大,数据宽度是多少 ptr ds:[]是固定写法 []中的内容,就是地址,也可称为编号 每一个编号对应的是一个字节

编号是连续使用的

可以将立即数写到内存中,也可以将寄存器写到内存中,但前提是:宽度必须是一样的

注意: 在使用MOV指令的时候,要保证两边的宽度是一样的

将内存中的值写入寄存器中:

mov ecx,dword ptr ds:[12FFC4]

在汇编中,就大多数指令是不允许从内存到内存的,其中包括MOV指令;必须经过寄存器;也不能从内存到立即数。立即数不是容器,只能往别的东西里放

涉及代码:

#include "stdafx.h"
#include <stdlib.h>

int x = 1;//全局变量
int main(int argc,char* argv[])
{
    x = 2;
    
    int y = 3;//局部变量
    
    int z[] = {0};
    for(int i=0;i<10;i++)
    {
        z[i] = 1;
    }

}

内存的五种表示形式:

  1. 立即数:

    1. 读取内存的值:MOV EAX,DWORD PTR DS:[13FFC4]

    2. 向内存中写入数据:MOV DWORD PTR DS:[13FFC4],EAX

  2. [reg] reg代表寄存器,可以是8个寄存器中的任意一个

    1. 读取内存的值:MOV EAX,13FFD0 ; MOV EAX,DWORD PTR DS:[ECX]

    2. 向内存中写入数据:MOV EDX,13FFD8 ; MOV DWORD PTR DS:[DEX],87654321

  3. [reg+立即数]

    1. 读取内存的值:mov ecx,13ffd0 ; mov eax,dword ptr ds:[ecx+4]

    2. 向内存中写入数据:mov edx,13FFD8 ; MOV DWORD PTR DS:[EDX+0xC],87654321

  4. [reg+reg*{1,2,4,8}] #在高级语言中对数组赋值,最终生成的汇编语言就是这样的 八个32位的通用寄存器加上八个32位的通用寄存器乘以1或2或4或8

    1. 读取内存的值:MOV EAX,13FFC4 ; MOV ECX,2 ; MOV EDX,DWORD PTR DS:[EAX+ECX*4]

    2. 向内存中写入数据:MOV EAX,13FFC4 ; MOV ECX,2 ; MOV DWORD PTR DS:[EAX+ECX*4],87654321

  5. [reg+reg*{1,2,4,8}+立即数] #

    1. 读取内存的值:MOV EAX,12FFC4 ; MOV ECX,2 ; MOV EDX,DWORD PTR DS:[EAX+ECX*4+4]

    2. 向内存中写入数据:MOV EAX,13FFC4 ; MOV ECX,2 ; MOV DWORD PTR DS:[EAX+ECX*4+4],87654321

学习五种形式的作用:看得懂各式编译器反编译出来的汇编代码

13、数据的存储模式

1、存储模式

mov byte ptr ds:[0x00000000],0x1A
mov byte ptr ds:[0x00000000],0x1A2C
mov byte ptr ds:[0x00000000],0x1A2C3E4F

大端模式:数据高位在低位,数据低位在高位 小端模式:数据低位在低位,数据高位在高位

2、3、4、

14、

15、

16、

17、

18、

19、

20、

21、

22、

23、

24、

25、

26、

最后更新于