01-初识汇编

前言

博主本人从事python开发工作差不多五年了,随着时间的推移,我总是在工作中遇到各种问题让我百思不得其解,再加上博主本身就没有系统学习过任何关于计算机结构的知识,
只是零散看了一些资料。这些知识反而让博主更是疑惑,毕竟知识面太窄了,认知也不够全面。所以从这一篇开始,博主决定老老实实的学习底层,然后慢慢向着逆向方向推进吧。

汇编

关于硬编码的资料
https://blog.51cto.com/u_15127590/4549268

最核心的一句话

相同CPU架构下使用不同OS的情况下,生成的汇编是不一样的,但是机器码是一样的

精简总结:相同CPU、不同OS,汇编不一样,机器码一样

CPU架构,指令集,高级语言,汇编,机器码之间的关系

从抽象到具体的顺序是(设计规范层):
CPU指令架构(ISA)→指令集→汇编→机器码→硬件

ISA(指令集架构)是硬件设计的顶层规范,定义“硬件应该支持哪些指令、如何解码、如何执行”。指令集是ISA的具体实现(如x86指令集是ISA的实例化)。
二者是硬件设计的基础与约束,不直接参与程序执行,而是指导“如何将机器码映射到硬件操作”。
正确位置:位于硬件之前,是硬件设计的“蓝图”

执行流程层:
高级语言 → 汇编语言 → 机器码 → 硬件(CPU)

是程序运行时的动态路径,描述“代码如何被编译/解释并最终由硬件执行”。
正确位置:位于设计规范层之上,是软件到硬件的“执行链路”。

如果合在一起看的话,简单结构应该是这样的:
image
或者下面这个图:
image

说实话,上面这两个图我其实很纠结指令集和指令集架构应该放在哪里,因为是决定采用什么架构,然后才能设计对应的的指令集,再然后才是通过指令集映射出机器码,最终弄出对应的硬件。而汇编则是这些资源的使用者
这里我们也可以把这些东西的关系看成施工队(汇编),架构(住宅还是写字楼),指令集(工程图纸),机器码(工程材料)

CPU结构分类

CPU分为两种类型:

  1. CISC(复杂指令集)
  • intel芯片
  1. RISC(精简指令集)
  • ARM芯片
  • Mac M1、M2芯片

CISC

所谓复杂指令集,其实可以理解为,一句指令会执行多个动作,比如add eax 1这一句的动作是eax寄存器中的值加1,然后加1后的值重新赋值给eax寄存器。
这个架构的好处就是可以有效减少过多的指令,只不过会导致一条指令的执行时间更长

RISC

而精简指令集则可以理解为,一条指令尽可能的只做一件事情,add RO, RO, #1这里的意思是先将RO寄存器中的值与1相加后,再存入RO寄存器中。这里面共有两条指令。

寄存器

关于寄存器,执行引擎,内存,CPU缓存之间的关系

image

上图可以知道CPU中包含执行引擎,寄存器和CPU缓存

而数据来源是下面的流程(如果数据在内存中):
执行引擎申请数据资源 → 寄存器收到申请先查找自身是否存在这个资源(如果有则之间给执行引擎) → 自身资源不存在向CPU缓存查找 → CPU缓存也不存在这个资源向内存查找

分类

  1. 通用寄存器
  2. 段寄存器
  3. 指令指针寄存器(x86 EIP、x64 RIP)
  4. 标准寄存器(x86 eflags、x64 rflags) 状态寄存器
  5. 控制寄存器(CR0 - CR4,CR3 页表)
  6. 调试寄存器(DR0 - DR7)
  7. 描述符寄存器(GDTR、LDTR、IDTR)
  8. 任务寄存器(TR)
    ……(x64 CPU有新增寄存器,如型号专属寄存器)

通用寄存器

  1. eax:函数返回值
rax:六十四位
  eax:低三十二位
    ax:低十六位
      ah:高八位
      al:低八位

举例:
ax中存储0x1234,二进制为:0001 0010 0011 0100
如果用al去取ax中的值,则拿到的是0011 0100
而ah拿到的是0001 0010
  1. ebx:随便用,无限制
  2. ecx:循环次数;this指针
  3. edx:随便用,无限制
  4. ebp、esp:栈底指针与栈顶指针
  5. esi、edi:拷贝数据用,源地址、目标地址
  6. EIP:程序计数器,无法直接通过指令操作,比如mov eip eax,一般用call之类的指令操作

EIP标志位和JCC指令

EIP标志位

image

图中的标志位从右往左看,0~31位数值,之所以总共是32位,是因为EIP寄存器是32位的

其中我们需要关注的是下面几个:
1. 中断使能标志:中断调试状态下不为0
2. 单步标志
3. 零标志:判断是否为0,用于程序跳转,不同的指令会根据这个标志位不同值进行判断跳转,具体参考JCC指令表格

在我们实际的操作中,因为展示的值是十六进制而不是二进制的值,所以我们在终端调试并且全部清零的状态下看见的值是00000202,
因为中断使能标志在第9位,所以00000202从右往左数,是第二个2代表中断使能标志,二进制表示为:0000 0000 0000 0000 0000 0010 0000 0010

JCC指令

image

练习

int a = 10;
if (a >= 10){
  a = 100;
}

xor eax, eax
mov eax, 10
cmp eax, 10
jge 12345

mov eax, 100
posted @ 2025-12-10 15:06  影梦无痕  阅读(30)  评论(0)    收藏  举报