汇编入门基础 (配套c++挖掘程序本质)未完待续!~

目录

前言

由于在CSDN上无法看到markdown的目录,有需要额小伙伴可以联系我,附送本文的 .md文件,可以再本地typora上更加方便的学习
这篇文章和 C++基础(点此链接) 为想结合内容,请大家在学习时可以同时参考

1.程序的执行过程

程序的执行过程

2.寄存器与内存

	int a = 3;
	// 运算前会将a装入内存中
	int c = a + 1;
2.1 通常CPU会先将内存中的数据存储到寄存器中,然后再次对寄存器中的数据进行运算
2.2 假设内存中有块红色内存空间的值是3,现在想把它的值加1,并将结果存储到蓝色内存空间
  1. CPU首先会将红色内存空间的值放到EAX寄存器中:mov eax,dword ptr [a] (红色空间)

  2. 然后让EAX寄存器与1相加:add eax,1

  3. 最后将值赋值给内存空间:mov dword ptr [c](蓝色空间),eax

	int a = 3;
012C3D48 C7 45 F8 03 00 00 00 mov         dword ptr [a],3  
	// 运算前会将a装入内存中
	int c = a + 1;
012C3D4F 8B 45 F8             mov         eax,dword ptr [a]  
012C3D52 83 C0 01             add         eax,1  
012C3D55 89 45 EC             mov         dword ptr [c],eax  

在这里插入图片描述

3.编程语言的发展

3.1 机器语言:

由0和1组成

3.2 汇编语言:

用符号代替了0和1,比机器语言便于阅读和记忆

3.3 高级语言:

C\C++\Java\JavaScripte\Python等,更接近人类自然的语言

3.4 三种语言的方式对比

操作:将寄存器的BX的内容送入寄存器AX中

  1. 机器语言: 1000100111011000

  2. 汇编语言: mov ax,bx

  3. 高级语言: ax = bx;

在这里插入图片描述

3.5 总结:

汇编语言机器语言–对应,每一条机器指令都有与之对应的汇编指令

汇编语言可以通过编译得到机器语言机器语言可以通过反汇编得到汇编语言

高级语言可以通过编译得到汇编语言\机器语言,但汇编语言\机器语言几乎不可能还原成高级语言

4.一些编程语言的本质区别

4.1 结构体 和 数组 汇编代码对比

基本相同,所以得出结论将汇编语言or机器语言很难还原成高级语言

	struct Test
	{
		int year;
		int month;
		int day;
	};
	Test t = { 1,2,3 };
C7 45 F0 01 00 00 00 mov         dword ptr [ebp-10h],1  
C7 45 F4 02 00 00 00 mov         dword ptr [ebp-0Ch],2  
C7 45 F8 03 00 00 00 mov         dword ptr [ebp-8],3 

================================ 结构体 和 数组 汇编代码对比 =================================

	int t[] = { 1,2,3 };
C7 45 F0 01 00 00 00 mov         dword ptr [ebp-10h],1  
C7 45 F4 02 00 00 00 mov         dword ptr [ebp-0Ch],2  
C7 45 F8 03 00 00 00 mov         dword ptr [ebp-8],3  

4.2 编译型语言和脚本语言

1)编译型语言(不依赖虚拟机)

C\C++\OC\Swift …

C++:轻易反汇编

2)脚本语言

python\Js\php …

JS:浏览器解析

PHP:C++:右zend Engine(ZE) 进行解析

3)编译型语言(依赖虚拟机)

Java\Ruby …

Java:由JVM装在字节码(xxx.class文件)


图片:对比三类语言

总结:不管是什么语言最终都会成为机器码

5.代码的执行效率分析

5.1 事后统计法 ☆☆☆

1)严重依赖机器的性能
2)需要编写额外的测试代码

5.2 窥探底层的汇编代码

C++\Java\OC -->机器码\汇编代码

5.3 时间与空间 ☆☆☆☆☆

1) 空间换时间:消耗更多的内存去节省时间

消耗了1M内存,执行时间是2s

2)时间换空间:消耗更多的时间去节省内存

消耗了4M内存,执行时间是0.5s

5.4 思考:if-else 和 switch,谁的效率更高?

结论:在可以等价交换时,switch效率更高

1) switch
void test0() {
	int no = 4;
	switch (no) {
	case 1:
		printf("no is 1");
		break;
	case 2:
		printf("no is 2");
		break;
	case 3:
		printf("no is 3");
		break;
	case 4:
		printf("no is 4");
		break;
	case 5:
		printf("no is 5");
		break;
	default:
		printf("other no");
		break;
	}
	int age = 4;
}
011A4F40 55                   push        ebp  
011A4F41 8B EC                mov         ebp,esp  
011A4F43 81 EC DC 00 00 00    sub         esp,0DCh  
011A4F49 53                   push        ebx  
011A4F4A 56                   push        esi  
011A4F4B 57                   push        edi  
011A4F4C 8D BD 24 FF FF FF    lea         edi,[ebp-0DCh]  
011A4F52 B9 37 00 00 00       mov         ecx,37h  
011A4F57 B8 CC CC CC CC       mov         eax,0CCCCCCCCh  
011A4F5C F3 AB                rep stos    dword ptr es:[edi]  
011A4F5E B9 26 C0 1A 01       mov         ecx,offset _F4F4212C_main@cpp (011AC026h)  
================================== 以上部分为计算 case 值 ==================================
011A4F63 E8 AA C2 FF FF       call        @__CheckForDebuggerJustMyCode@4 (011A1212h)  
011A4F68 C7 45 F8 04 00 00 00 mov         dword ptr [no],4  
011A4F6F 8B 45 F8             mov         eax,dword ptr [no]  
011A4F72 89 85 24 FF FF FF    mov         dword ptr [ebp-0DCh],eax  
011A4F78 8B 8D 24 FF FF FF    mov         ecx,dword ptr [ebp-0DCh]  
011A4F7E 83 E9 01             sub         ecx,1  
011A4F81 89 8D 24 FF FF FF    mov         dword ptr [ebp-0DCh],ecx  
011A4F87 83 BD 24 FF FF FF 04 cmp         dword ptr [ebp-0DCh],4  
011A4F8E 77 58                ja          $LN8+0Fh (011A4FE8h)  
011A4F90 8B 95 24 FF FF FF    mov         edx,dword ptr [ebp-0DCh]  
011A4F96 FF 24 95 10 50 1A 01 jmp         dword ptr [edx*4+11A5010h]  
$LN4:
011A4F9D 68 CC 7B 1A 01       push        offset string "no is 1" (011A7BCCh)  
011A4FA2 E8 DD C3 FF FF       call        _printf (011A1384h)  
011A4FA7 83 C4 04             add         esp,4  
011A4FAA EB 49                jmp         $LN8+1Ch (011A4FF5h)  
$LN5:
011A4FAC 68 D4 7B 1A 01       push        offset string "no is 2" (011A7BD4h)  
011A4FB1 E8 CE C3 FF FF       call        _printf (011A1384h)  
011A4FB6 83 C4 04             add         esp,4  
011A4FB9 EB 3A                jmp         $LN8+1Ch (011A4FF5h)  
$LN6:
011A4FBB 68 DC 7B 1A 01       push        offset string "no is 3" (011A7BDCh)  
011A4FC0 E8 BF C3 FF FF       call        _printf (011A1384h)  
011A4FC5 83 C4 04             add         esp,4  
011A4FC8 EB 2B                jmp         $LN8+1Ch (011A4FF5h)  
$LN7:
011A4FCA 68 E4 7B 1A 01       push        offset string "no is 4" (011A7BE4h)  
011A4FCF E8 B0 C3 FF FF       call        _printf (011A1384h)  
011A4FD4 83 C4 04             add         esp,4  
011A4FD7 EB 1C                jmp         $LN8+1Ch (011A4FF5h)  
$LN8:
011A4FD9 68 D0 7C 1A 01       push        offset string "no is 5" (011A7CD0h)  
011A4FDE E8 A1 C3 FF FF       call        _printf (011A1384h)  
011A4FE3 83 C4 04             add         esp,4  
011A4FE6 EB 0D                jmp         $LN8+1Ch (011A4FF5h)  
011A4FE8 68 D8 7C 1A 01       push        offset string "other no" (011A7CD8h)  
011A4FED E8 92 C3 FF FF       call        _printf (011A1384h)  
011A4FF2 83 C4 04             add         esp,4  
011A4FF5 C7 45 EC 04 00 00 00 mov         dword ptr [age],4  
2) if-else
void test2() {
	int no = 8; // 13条汇编指令
	if (no == 1) {
		printf("no is 2");
	} else if (no == 2) {
		printf("no is 2");
	} else if (no == 3) {
		printf("no is 3");
	} else if (no == 4) {
		printf("no is 4");
	} else if (no == 5) {
		printf("no is 5");
	} else {
		printf("other no");
	}
	int age = 4;
}
000C4F70 7D F8                jge         test2+2Ah (0C4F6Ah)  
000C4F72 01 75 0F             add         dword ptr [ebp+0Fh],esi  
000C4F75 68 CC 7B 0C 00       push        offset string "no is 2" (0C7BCCh)  
000C4F7A E8 05 C4 FF FF       call        _printf (0C1384h)  
000C4F7F 83 C4 04             add         esp,4  
000C4F82 EB 61                jmp         test2+0A5h (0C4FE5h)  
// cmp == compare
000C4F84 83 7D F8 02          cmp         dword ptr [no],2  
// jne == jump not equal,不相等的时候会跳转
000C4F88 75 0F                jne         test2+59h (0C4F99h)  
// printf("no is 2")
000C4F8A 68 CC 7B 0C 00       push        offset string "no is 2" (0C7BCCh)  
000C4F8F E8 F0 C3 FF FF       call        _printf (0C1384h)  
000C4F94 83 C4 04             add         esp,4  
// jmp == jump,无条件跳转
000C4F97 EB 4C                jmp         test2+0A5h (0C4FE5h)  

000C4F99 83 7D F8 03          cmp         dword ptr [no],3  
000C4F9D 75 0F                jne         test2+6Eh (0C4FAEh)  
000C4F9F 68 D4 7B 0C 00       push        offset string "no is 2" (0C7BD4h)  
000C4FA4 E8 DB C3 FF FF       call        _printf (0C1384h)  
000C4FA9 83 C4 04             add         esp,4  
000C4FAC EB 37                jmp         test2+0A5h (0C4FE5h)  
000C4FAE 83 7D F8 04          cmp         dword ptr [no],4  
000C4FB2 75 0F                jne         test2+83h (0C4FC3h)  
000C4FB4 68 DC 7B 0C 00       push        offset string "no is 3" (0C7BDCh)  
000C4FB9 E8 C6 C3 FF FF       call        _printf (0C1384h)  
000C4FBE 83 C4 04             add         esp,4  
000C4FC1 EB 22                jmp         test2+0A5h (0C4FE5h)  
000C4FC3 83 7D F8 05          cmp         dword ptr [no],5  
000C4FC7 75 0F                jne         test2+98h (0C4FD8h)  
000C4FC9 68 E4 7B 0C 00       push        offset string "no is 4" (0C7BE4h)  
000C4FCE E8 B1 C3 FF FF       call        _printf (0C1384h)  
000C4FD3 83 C4 04             add         esp,4  
000C4FD6 EB 0D                jmp         test2+0A5h (0C4FE5h)  
000C4FD8 68 E4 7C 0C 00       push        offset string "other no" (0C7CE4h)  
000C4FDD E8 A2 C3 FF FF       call        _printf (0C1384h)  
000C4FE2 83 C4 04             add         esp,4  
000C4FE5 C7 45 EC 04 00 00 00 mov         dword ptr [age],4  

6. x86 与 x64-常用数据类型对应字节数

6.1 x86 与 x64是什么?

x86 就是 32位操作系统,即2^32 ≈ 4GB 所以支持最大内存是4G

x64 就是 64位操作系统,即2^64 ≈ 128GB 所以支持最大内存是128G

x86 与 x64 的bit与b

01001011 = 8 bit(位)= 1Byte(字节)

6.2 常用数据类型对应字节数

可用函数如:sizeof(char) sizeof(char*) 得出

32位编译器:

char :1个字节
char*(即指针变量): 4个字节(32位的寻址空间是2^32, 即32个bit,也就是4个字节。同理64位编译器)
short int : 2个字节
int: 4个字节
unsigned int : 4个字节
float: 4个字节
double: 8个字节
long: 4个字节
long long: 8个字节
unsigned long: 4个字节

64位编译器:

char :1个字节
char*(即指针变量): 8个字节
short int : 2个字节
int: 4个字节
unsigned int : 4个字节
float: 4个字节
double: 8个字节
long: 8个字节
long long: 8个字节
unsigned long: 8个字节

汇编语言

特点:汇编语言不区分大小写

1.汇编语言的种类

1.1 汇编的发展

8086汇编(16bit)

x86汇编(32bit)

x64汇编(64bit)

ARM汇编(嵌入式、移动设备)

1.2 x64汇编的2种书写格式

x64汇编根据编译器的不同,有2种书写格式

1)Inter(windows平台-vs)

2)AT&T(mac平台)

1.3 AT&T汇编 vs Intel汇编

在这里插入图片描述

1.4 学习汇编语言的2大知识点

1) 汇编指令

2) 寄存器

2. x64汇编 - 寄存器 Register

寄存器就是用来存储的,一个寄存器能存多少由计算机架构决定如:x64 寄存器最大存储8Btye(字节)

2.1 常用通用寄存器

16bit-通用寄存器(general-Purpose Registers):AX\BX\CX\DX (不常用未列举)

32bit-通用寄存器(general-Purpose Registers):EAX\EBX\ECX\EDX (不常用未列举)
函数的返回值一般返回EAX

64bit-通用寄存器(general-Purpose Registers):RAX\RBX\RCX\RDX (不常用未列举) 向下兼容

在这里插入图片描述

2.2 一般规律

AH H high

AL L low

R开头的寄存器是64bit的,占8字节

E开头的寄存器是32bit的,占4字节

在这里插入图片描述
在这里插入图片描述
总结:

寄存器 范围 字节数Byte
RAX 63~0 占8字节
EAX 31~0 占4字节
AX 15~0 占2字节
AL 7~0 占1字节-低(low)
AH 15~7 占1字节-高(high)

2.3 内联汇编

1) 内联汇编的写法

__asm {
mov eax,10
}

2) 内联汇编实例一

#include <iostream>
using namespace std;

int main() {
	//assembly
	__asm {
		mov eax,10
	}
	return 0;
}
	__asm {
		mov eax,10
012417CE B8 0A 00 00 00       mov         eax,0Ah  
	}

在VS-反汇编中,可以看到将10赋值给eax寄存器中 (下图和下下图)
在这里插入图片描述

在这里插入图片描述

3) 内联汇编实例二

#include <iostream>
using namespace std;

int main() {
	//assembly
	__asm {
		mov ax,10
		mov eax, 11223344H
	}
	return 0;
}
	__asm {
		mov ax,10
010A17CE 66 B8 0A 00          mov         ax,0Ah  
		mov eax, 11223344H
010A17D2 B8 44 33 22 11       mov         eax,11223344h  
	}
	return 0;
010A17D7 33 C0                xor         eax,eax  
}

在这里插入图片描述

在这里插入图片描述
在VS下 debug-x64 架构下不能编译内联汇编 asm

在VS下 debug-x32 架构下不能编译 rax(rax为64位寄存器)

3. x64汇编 -操作总结(含说明)

3.1 mov dest, src

将src 内容赋值给dest ,类似dest=src

函数返回的结果一般都存在 EAX

3.2 [地址值]

中括号 [ ] 里面放的都是内存地址,而且是变量的首地址小端模式

例如: mov dword ptr [ebp-8] , 3

注意:局部变量 age 和 全局变量 height 的地址区别

#include <iostream>
using namespace std;
// 全局变量
int height = 10;
int main() {
	height = 4;
	int age = 10;	// 局部变量
	cout << "hello world!" << endl;
	return 0;
}

全局变量:height 和 局部变量:age
一个是固定地址(全局变量),一个不是固定地址(局部变量)
在这里插入图片描述

3.3 word是2字节,dword是4字节(double word),qword是8字节(quad word)
3.4 call 函数地址

调用函数 E8开头

3.5 lea(load effect address) dest,[地址值]

mov 操作不支持计算 如:mov eax,ebp-0CH
这里右边部分ebp-0CH 减法操作,在intel内核中mov不支持,但是[]是支持运算的
所以使用lea 如:lea eax,[ebp-0CH] (指针的标志)

例如: lea eax , [1122H]
eax == 1122H 就是将地址的值(不是地址指向的值)传给eax
等价于 mov eax,1122H
例如:mov eax,dword ptr [1122H]
eax == 4 就是将[1122H] 地址指向的值(不是地址的值)传给eax

3.6 ret

函数返回 return

3.7 xor op1,op2

将op1和op2异或的结果赋值给op1,类似op1 = op1^op2

3.8 add op1,op2

类似于 op1 = op1+op2

3.9 sub op1,op2

类似于 op1 = op1-op2

3.10 inc op

自增,类似于 op = op + 1

3.11 dec op

自增,类似于 op = op - 1

3.11 jmp 内存地址

跳转到某个内存地址去执行代码
j开头的一般都是跳转,大多数是带条件的跳转,一般跟test、cmp等指令配合使用
可以参考JCC(jump condition code) 跳转指令集
jne jump not equal 比较结果不相等才跳转

	if (a == b) {
00DB5D27 8B 45 EC             mov         eax,dword ptr [a]  
00DB5D2A 3B 45 E0             cmp         eax,dword ptr [b]  
00DB5D2D 75 2B                jne         main+7Ah (0DB5D5Ah)  
	int a = 10;
	// ebp-0CH是age的地址值
003241A2 C7 45 F4 0A 00 00 00 mov         dword ptr [ebp-0Ch],0Ah  
	int* p = &a;
	//将	ebp-0Ch 赋值给 eax,存放着a的地址
003241A9 8D 45 F4             lea         eax,[ebp-0Ch]  
	// 将eax的值(a的地址)存放到指针p的存储空间中
003241AC 89 45 E8             mov         dword ptr [ebp-18h],eax  
	*p = 5;
	// 将age的地址(P的存储空间)存放到eax中
003241AF 8B 45 E8             mov         eax,dword ptr [ebp-18h]  
003241B2 C7 00 05 00 00 00    mov         dword ptr [eax],5  

权威参考:Intel 白皮书

3.12 ebp、esp

(1)ESP:栈指针寄存器(extended stack pointer),其内存放着一个指针,该指针永远指向系统栈最上面一个栈帧的栈顶。
(2)EBP:基址指针寄存器(extended base pointer),其内存放着一个指针,该指针永远指向系统栈最上面一个栈帧的底部

4. CPU大小端模式

在这里插入图片描述

5. 变量的地址 [ ]

一个变量的地址值,是它所有字节地址中的最小值
在这里插入图片描述

6.不能再内存上进行运算操作

由CPU架构决定的,只能对寄存器进行计算操作

6.1 简单的运算与EAX (寄存器)
#include <iostream>
using namespace std;

int main() {
	int a = 1;
	int b = 2;
	int c = a + b;
	return 0;
}

C++对应的汇编代码

     9: 	int a = 1;
01155D19  mov         dword ptr [a],1  
    10: 	int b = 2;
01155D20  mov         dword ptr [b],2  
    11: 	int c = a + b;
01155D27  mov         eax,dword ptr [a]  
01155D2A  add         eax,dword ptr [b]  
01155D2D  mov         dword ptr [c],eax
6.2 简单的赋值与EAX(寄存器)
#include <iostream>
using namespace std;

int main() {
	int a = 1;
	int d = a;
	return 0;
}

C++对应的汇编代码

     9: 	int a = 1;
01125D19  mov         dword ptr [ebp-14h],1  
    10: 	int d = a;
01125D20  mov         eax,dword ptr [ebp-14h]  
01125D23  mov         dword ptr [ebp-20h],eax  
6.3 为什么不能 mov 内存,内存

mov dword ptr [ebp-20h],dword ptr [ebp-14h]
由于cpu架构不允许这么做的

01125D20  mov         dword ptr [ebp-20h],1
01125D23  mov         dword ptr [ebp-20h],dword ptr [ebp-14h]  

主要以b站免费学习资源
打造同进度学习的同学
在没有up主回答的情况下
通过一起学习组队可以互相解决观看视频中自己出现的问题,通过教学相长的方式,将知识可以牢固掌握。
欢迎加入《同学组队帮》让同进度同资料的同学可以互帮互助
我们的目标是:学习是我们终身的任务

发布了10 篇原创文章 · 获赞 5 · 访问量 369

猜你喜欢

转载自blog.csdn.net/wanglei19891210/article/details/104981895