逆向分析基础总结

article/2025/7/26 15:52:14

一、了解计算机部件

CPU:
中央处理器。有三个重要的部件:
逻辑部件:负责算数运算,包括定点运算、浮点运算等。
寄存器部件:负责临时数据存储,一个CPU包含多个寄存器。
控制部件:负责发出指令所要执行操作的控制信号。

内存:
用于存储系统运行的临时数据,是CPU与外部存储器进行沟通的桥梁。
CPU如果想准确地对内存进行读写,必须进行以下三类信息的交互。
地址信息:存储单元的地址。
控制信息:读或写的命令。
数据信息:读或写的数据。

存储单元一般以字节为单位,每个字节通过他们各自的唯一编号(也叫地址)进行访问,例如,一个存储器包含128个存储单元,这个存储器能够访问的地址范围为0~127。CPU读取数据的大致过程可参考下图:
在这里插入图片描述
对于大容量存储器(如硬盘和内存),可以使用以下不同单位来表示:
1Byte =8 Bit
1KB = 1024Byte
1MB = 1024KB
1GB = 1024MB
1TB = 1024GB
CPU的寻址能力是由CPU的地址总线宽度决定的。如果一个CPU的地址总线宽度为10,那么它的寻址能力就是1024字节(2的10次方)。此时,内存的大小如果大于1024字节,那么地址编号超过1023的内存单元都是没有意义的,因此CPU无法通过地址总线访问到它们。

内存分配机制:
内存的空间分为两类,分别是物理地址和虚拟地址空间。
物理地址就是物理内存的实际大小,
虚拟地址是由操作系统为每个进程分配的虚拟内存,虚拟内存由虚拟内存管理器进行管理。

但也出现了几个弊端:
(1)进程间不隔离。进程之间的数据可以相互修改。
(2)效率低。有些数据用不到,存满的情况下,想启动一个新的进程,需要把另一个进程的数据暂时复制到硬盘等外部存储中。
(3)地址不固定。程序在加载到物理内存时无法每次都分配在一块固定的地址。

后来,为了解决这个问题。提出了“分段”和“分页”两种措施。
分段:操作系统将内存中不同属性的内存分为一个个段,用户在访问不同的段内存时,拥有不同的访问权限,以此达到内存权限控制的目的。
分页:将物理内存按照一定量的大小进行分割,每个分割出来的内存块称为一个“页”。当程序向操作系统申请内存时,如果申请的内存大小小于一个“页”的大小,则默认分配一个页给程序使用,直到程序将这个页面用满为止,才会为程序分配下一块内存,以此减少内存分配的时间成本。

内存映射:
当一个进程启动时,程序的数据和依赖库的数据会被加载到系统分配给它的虚拟内存中,而系统不会马上将这些数据同步映射到物理内存中,否则仍然无法解决进程启动数量有限的问题,而只有当虚拟内存中的数据在程序执行过程中被真正访问(读、写、执行)时,系统才会将该数据与物理内存的某个内存页进行绑定,以此来节省物理内存的使用。
在这里插入图片描述

二、可执行文件

即,能够被操作系统加载并运行的文件。

PE:
是Windows平台下可执行文件遵守的数据格式代码,常见的PE格式文件扩展名有“.exe”“.dll”“.sys”。

PE文件加载:
当使用十六进制编辑器打开一个PE文件时,以字节为单位,每个字节相对于文件头的距离称作文件偏移。
在这里插入图片描述
双击运行程序时,程序的数据会被加载到进程的虚拟内存中,一般情况下,不是从0地址开始加载的,系统根据程序提供的相对虚拟地址将每个数据从文件中复制至虚拟内存中,分为两个部分。
(1)PE文件头:大小一般是固定的,复制到基址开始的位置。
(2)每个区段的数据。复制到节表指定的位置。
数据在虚拟内存中的地址叫作虚拟地址,即基址加数据的相对虚拟地址。

ELF:
ELF是Linux平台使用的数据格式。ELF头的位置是固定的,其余各部分的位置、大小等信息则是由ELF头中的各项值来决定的。ELF格式文件在运行时,只有ELF头和程序头表会被系统引用(程序头表的功能相当于PE格式文件中的节表),节和节头表主要是包含一些符号信息和调试信息,在静态分析时能够发挥作用,对程序运行帮助不大。

三、寄存器

是CPU的重要组成部分之一。不同架构、不同数位的CPU拥有的寄存器类型和数量也各有不同。在调试软件时,观察寄存器的变化也是重要的分析步骤。

寄存器的分类:
Intel x86 架构包含以下几种类型的寄存器,代号分别如下:
通用寄存器:EAX、EBX、ECX、EDX、ESP、EBP、ESI、EDI
段寄存器:CS、SS、DS、ES、FS、GS
标志寄存器:EFLAGS
指令指针寄存器:EIP
系统表寄存器:GDTR、IDTR、LDTR、TR
调试寄存器:DR0、DR1、DR2、DR3、DR4、DR5、DR6、DR7
控制寄存器:CR0、CR1、CR2、CR3、CR4
测试寄存器:TR6、TR7
具体模型寄存器:MSR

通用寄存器:
EAX(累加寄存器)、EBX(基址寄存器)、ECX(计数寄存器)、EDX(数据寄存器)、ESP(堆栈指针寄存器)、EBP(基址指针寄存器)、ESI(源变址寄存器)、EDI(目的变址寄存器)。
为了兼容早期CPU架构的程序,一些通用寄存器又可以拆分为几个低位寄存器。以EAX为例,它的低16位可以通过代号AX进行访问,AX的高8位又叫做AH,AX的低8位又叫AL,在编写程序时,可以根据具体需要选择相应宽度的寄存器进行使用。
在这里插入图片描述
在一般情况下,会将EAX、EBX、ECX、EDX、ESI、EDI这六个寄存器用于数据运算或寻址,只有在特定情况下,才会发挥他们原本的作用(如部分指令默认使用ECX作为循环变量)。

ESI与EDI:
ESI中的S是英文单词Source的缩写,EDI中的D则指的是Destination。一般情况下这两个寄存器也能用于暂存数据,而对于部分指令来说,ESI负责存放源数据的地址,而EDI则负责指向数据处理后需要存储的目的地址。
例:REP MOVS DWORD PTR DS:[EDI],DWORD PTR DS:[ESI]
在这条指令中,REP MOVS 指令的意思是循环复制内存数据,复制的数据默认从ESI指向的内存中读取,保存到EDI指向的内存中,用户可以指定每次复制的数据宽度(DWORD,表示四个字节),每执行一次后,令ESI和EDI指向下一块需复制的内存,并默认使用ECX表示剩余次数。
除了CPU内部设定好的一些默认规则,其他像使用那条指令、哪些寄存器去完成某一个动作,通常是由编译器决定的。

ESP与EBP:
ESP与EBP是与栈相关的两个寄存器,其中ESP通常表示栈顶,EBP表示栈底。
栈是位于内存中的一块线性的数据结构,比寄存器的内存大,从程序开始到结束的所有临时数据都能放得下。
栈的基地址是在程序启动时,由系统指定的。遵守几条基本规则:
1.栈从高地址向低地址分配内存。
2.当发生入栈操作时,入栈的元素被置入栈顶。
3.当发生出栈操作时,从当前栈顶中取出元素。
4.栈底是栈的边界,当栈顶和栈底指向同一块内存时表示栈为空。

段寄存器:
段寄存器比较特殊,常见有6个:
在这里插入图片描述
早期CPU中的每个寄存器宽度只有16位,寻址范围为0~0xFFFF,无法满足当时128KB内存的寻址要求,因此CPU厂商针对这种情况采用了“段寄存器*16+偏移量”的方式进行寻址。
如今单个寄存器的寻址能力已经能够满足寻址要求,因此现在已经不使用段寄存器寻址和类似的方式,段寄存器也被分配到了其他岗位。

标志寄存器:
在进行编程开发时,经常会使用类似“if……else……”这样的条件分支语句,根据条件是否成立来决定最终执行哪个分支的代码。对于CPU来说,标志寄存器(EFLAGS)起到了不可或缺的作用。
标志寄存器具有以下三个作用:
1、存储部分指令的执行状态。
2、为部分指令的执行提供行为依据
3、控制CPU的相关工作方式

需要重点关注的标志位见下表:
在这里插入图片描述
指令指针寄存器:
EIP寄存器叫做指令指针寄存器,它用于告诉CPU下一条指令的位置。如果没有EIP,CPU就不知道该去哪里读取指令,也就无法产生控制信息。
大部分汇编指令并不会自己修改EIP的值,这项工作由CPU自己完成,只有少部分汇编指令能为用户提供修改EIP的权限,控制程序的运行逻辑。
高级语言的判断语句、循环语句及函数调用本质上都是通过修改EIP的值来实现的。

四、汇编语言

汇编指令:汇编语言的主体,也叫机器码的助记符。
伪指令:提供编译过程中的相关信息,由编译器识别。
其他符号:如‘+’,‘-’,‘*’,‘/’等,由编译器识别。
汇编语言示例代码如下:

STACK SEGMENT PARA STACK
DW 20H DUP(0)
STACK ENDSDATA SEGMENT
STRING DB’Hello World’,’$’;
DATA ENDSCODES SEGMENT
ASSUME CS:CODES;DS:DATAS
START:
MOV AX,DATA
MOV DS,AX
LEA  DX,STRING
MOV AH,09H
INT 21H
MOV AH,4CH
INT 21H
CODES ENDS
END START

以上是一段简单的汇编源代码,功能是在控制台输出字符串“Hello World”。在这段汇编代码中,START部分包含具体的汇编指令。
汇编指令是用来代替机器指令的一种便于记忆的符号,因此其准确性和可靠性能够得到保证,并且在分析一个软件时,通过将程序中的机器指令反汇编成汇编指令进行阅读,是除了直接分析机器指令外最准确的一种方法。

汇编指令格式:
以MOV指令为例,InTER X86 汇编指令的格式为:MOV DEST,SRC。在这句汇编指令当中,MOV叫作操作码,表示指令的具体功能。
不同的汇编指令能够携带的参数个数不同,每个参数使用逗号进行分割。
在下文中,对于带两个参数的汇编指令,以DEST表示目的操作数,它既参与指令的执行也负责保存结果,以SRC表示源操作数,它通常只参与运算。
实际上,一条机器指令的构成是非常复杂的,Inter 将其大致分为6个部分,分别为地址前缀、操作码、操作数、辅助操作码、辅助操作数、内存修饰符和标号,如下图所示。且最大长度能达到14字节之多。
在这里插入图片描述
字段说明:
ModR/M:指定操作数的寻址方式(如寄存器、内存)。
SIB:当寻址需要基址 + 索引 + 比例因子时使用(如 [EBX*2+ESI])。
位移量:内存地址的偏移量(如 [EBP+0x8] 中的 0x8)。
立即数:指令中直接携带的数据(如 MOV EAX, 0xFFFFFFFF 中的 0xFFFFFFFF)。

指令执行流程与字段关联图如下图所示:
在这里插入图片描述
数据传送指令:
MOV指令叫作数据传送指令。它的作用是将数据从一个地方(数据源)传送到另一个地方(目的地)。指令执行后,数据源的值不会发生改变。
指令格式:MOV DEST,SRC
MOV 指令支持以下几种具体的用法:
(1)MOV寄存器,立即数,如mov eax,0x10
(2)MOV寄存器,寄存器,如mov ebx,ecx
(3)MOV寄存器,内存,如mov eax,dword ptr ds:[0x402100]
(4)MOV 内存,立即数,如mov byte ptr ds : [0x403400],0x2200
只有两种情况不被允许,具体如下。
(1)目的地是立即数,如mov 5,ebx
(2)两个内存之间进行数据传送,如mov dword ptr ds:[0x401200],dword ptr ds:[0x403400]
32位处理器支持一次性处理的常见数据宽度如下表:
在这里插入图片描述
对于一条正确的MOV指令,需要保证给出的两个参数的数据宽度相同。

算数运算指令:
在这里插入图片描述
在使用DIV指令时有两个需要注意的地方:
(1)除数不能为0,否则会触发除0异常。
(2)执行前需要确保 AH/DX/EDX的值为0。

逻辑运算指令:
在这里插入图片描述
移位指令:
在这里插入图片描述
条件转移指令:

JCC指令也叫条件转移指令,是功能相近的一系列汇编指令的统称,J为英文单词jump的缩写,CC指的是条件码。作用是根据条件码决定是否修改EIP,如果EIP被修改,可以认为程序的执行逻辑发生了“跳转”,因此条件转移指令口语化也称“条件跳转指令”。
指令格式:JCC寄存器/立即数/内存
条件检查码检查一个或多个标志寄存器中标志位的组合,由于标志位有许多个,因此标志位的组合也有许多种。
汇编语言中JCC指令包含的具体指令:
在这里插入图片描述
此外,汇编语言还提供了一个无条件转移指令:JMP指令。例如,JMP 0x401000
这条指令在执行后,EIP寄存器的值将被无条件地修改为0x401000,即CPU下次执行时将从地址0x401000读取指令。

栈操作指令:
PUSH指令和POP指令对应栈的两个基本操作:入栈和出栈。
PUSH指令表示入栈,意为将一个元素送入栈顶,如PUSH EAX
POP表示出栈,意为从栈顶取出一个元素,如POP EAX
其中,PUSH指令可以拆解为两个步骤来理解。

(1)ESP = ESP -4
(2)MOV[ESP],SRC

上面的代码,大家可以问问Deepseek,让它生成HTML格式的动画,方便大家理解。以下是我问豆包得到的图示:
PUSH指令执行前的栈状态见下图:
在这里插入图片描述
PUSH指令执行时的栈变化见下图:
在这里插入图片描述

函数调用:
函数是用于完成一段特定功能的代码片段,通常使用函数名进行调用,使用return退出函数,且能够根据需要附带返回值。
对于CPU来说,调用函数的本质是通过修改EIP实现的,进入函数即修改EIP到目标地址执行代码,退出函数即修改EIP到下一条指令的位置。
在汇编语言中,CALL指令和RET指令是与函数调用相关的两条基本指令。CALL指令表示调用函数,它与JMP指令很像,都能够无条件地修改EIP到目标地址,但是相比JMP指令而言,它在修改EIP之前还会进一步额外的操作,即在栈中存入下一条指令的地址,这个地址被称为“返回地址”。
RET指令表示退出函数,具体实现是从栈顶取出一个元素给EIP,相当于执行了 POP EIP指令,但汇编语言没有后者写法。
在了解CALL指令和RET指令的基本功能后,可以想象一下再汇编语言层面函数调用的大致过程。
(1)CALL指令向栈中存入一个“返回地址”,然后修改EIP到目的地址。
(2)CPU执行函数的主体代码,并在结尾处遇到RET指令。
(3)RET指令从栈中取出返回地址给EIP,程序回到父函数继续运行。
这三个步骤所描绘的函数调用过程可以参考:

除此之外,一个正常且完整的函数通常需要解决以下三个问题。
(1)函数的参数和局部变量在哪里?
(2)寄存器的值被修改了怎么办?
(3)如何保证退出函数时,能取出正确的返回地址?

在编译可执行时,全局变量和静态变量的具体值会被编译到文件中,(如果在代码中有初步定义),且在程序运行后拥有固定的地址;而局部变量只有当函数被调用时才会在栈中为它们分配内存,在函数退出时释放内存。
x86程序在调用函数时默认通过栈传递参数。也就是说,在调用函数前,若该函数需要参数,会将参数依次放入栈中。
在函数执行过程中,难免会用到一部分寄存器参与数据的运算和存储,这些寄存器的值在进入该函数前对于父函数是比较重要的,如果这些值在函数执行过程中被修改,在退出函数后,可能会影响父函数的正常执行,最终产生逻辑错误或崩溃。大多编译器为了避免这种情况的出现,在函数的主体代码执行前,会先将一些重要寄存器的值保存在栈里,然后便可放心地执行函数。只要在退出函数前,将这些关键数据再取出还给对应的寄存器即可。
另外,在执行汇编指令时,难免会遇到与栈相关的操作,这些操作通常会令ESP发生变化,而函数在退出时需要使用RET指令从栈中取出一个“返回地址”给EIP,此时如果栈顶指针并没有指向正确的返回地址,取出的数据将是不确定的,这将破坏整个程序的正常执行,甚至很可能导致程序崩溃。因此,在使用汇编指令对栈进行操作时需要十分小心。
(1)每个函数都拥有一块独立的栈空间,在退出前进行释放。
(2)函数的参数和局部变量位于栈或寄存器中。
(3)函数会通过“保存现场”保护一些关键的数据,在退出函数前“还原现场”,以确保回到父函数后,程序依然能够正常运作。
(4)在函数中对栈进行操作时,要十分小心,确保在函数退出时栈顶能够指向正确的返回地址。
了内存使用的痕迹,对于main函数而言,栈的状态完全没有发生变化。

中断指令:
操作系统在内部设置了一些子程序,用于完成某些用户难以完成的特定功能,如异常处理、I/O通信、屏幕输出等,用户可使用INT指令进行调用。
格式:INT N
INT指令也叫作软件中断指令,N表示中断号。当CPU在程序内部执行时,一旦遇到INT指令,就会暂停执行当前程序,到系统内核中执行相应的子程序,也可以将INT指令当作一种特殊的CALL指令来理解。
中断号占一个字节,其中,中断号3的作用是发出调试信号,是调试器设置断点最常用的方法之一。
当执行INT 3时,若当前进程中存在调试信息,则会将软件控制权转交给调试器,由调试器来决定下一步操作。INT 3对应的字节码为0xCC,这也是为什么编译器喜欢将栈中的局部变量初始化为0xCC的原因(若栈中未初始化的数据被意外执行,则会产生INT 3中断,便于调试分析)


http://www.hkcw.cn/article/faXgADTYIM.shtml

相关文章

企业信息化集成方案:聚水潭·奇门数据对接金蝶云星空

聚水潭奇门数据集成到金蝶云星空:销售退货单更新方案 在企业信息化管理中,数据的高效流动和准确对接是实现业务流程自动化的关键。本文将分享一个实际案例,展示如何通过轻易云数据集成平台,将聚水潭奇门系统中的销售退货数据无缝…

多功能文档处理工具推荐

软件介绍 今天为大家介绍一款功能强大的文档编辑工具坤Tools,这是一款在吾爱论坛广受好评的办公软件。 软件背景 坤Tools是由吾爱论坛用户分享的软件,在论坛软件榜单上长期位居前列,获得了用户的一致好评。 软件性质 这是一款完全离线、…

软考-系统架构设计师-第十八章 面向服务架构设计理论与实践

面向服务架构设计理论与实践 18.1 SOA 的相关概念18.2 SOA 的发展历史18.3 SOA 的参考架构18.4 SOA 主要协议和规范18.5 SOA 设计的标准要求18.6 SOA 的作用与设计原则18.7 SOA 的设计模式18.8 构建 SOA 架构时应该注意的问题18.9 SOA 实施的过程 18.1 SOA 的相关概念 &#x…

AI书签管理工具开发全记录(五):后端服务搭建与API实现

文章目录 AI书签管理工具开发全记录(四):后端服务搭建与API实现前言 📝1. 后端框架选型 🛠️2. 项目结构优化 📁3. API路由设计 🧭分类管理书签管理 4. 数据模型定义 💾分类模型&…

CentOS-stream-9 Zabbix的安装与配置

一、Web环境搭建部署Zabbix时,选择合适的MariaDB、PHP和Nginx版本非常重要,以确保兼容性和最佳性能。以下是建议版本:Zabbix 6.4 MariaDB:官方文档推荐使用MariaDB 10.3或更高版本。对于CentOS Stream 9,建议使用Maria…

Scratch节日 | 龙舟比赛 | 端午节

端午节快乐! 这款专为孩子们打造的Scratch游戏——《龙舟比赛》,让你在掌控龙舟的竞速中,沉浸式体验中华传统节日的魅力! 🎮 游戏亮点 节日氛围浓厚:化身龙舟选手,在波涛汹涌的河流中展开刺激竞…

抢占先机!品牌如何利用软文营销领跑内容营销赛道?

在当今这个信息爆炸的时代,内容营销已成为品牌与消费者沟通的重要桥梁。而软文营销,作为内容营销的一种重要形式,更是以其独特的魅力和高效的传播力,成为众多品牌抢占市场先机的利器。本文将深入探讨品牌如何利用软文营销领跑内容…

用谷歌云服务器可以做些什么?

解锁云端无限可能 在当今数字化飞速发展的时代,云计算技术犹如一股强大的动力,推动着各行各业的变革与创新。而谷歌云服务器,作为云计算领域的佼佼者,正以其卓越的性能和丰富的功能,为全球用户开启一扇通往全新数字世界…

Jetson Orin Nano - SONY imx415 camera驱动开发

目录 前言: 调试准备工作: 修改内核默认打印等级 一、imx415驱动开发 1、硬件接线 2、设备树修改 2.1 创建 tegra234-p3767-camera-p3768-imx415-C-4lane.dtsi 文件 2.2 tegra234-p3767-camera-p3768-imx415-C-4lane.dtsi 添加到设备树 2.3 编译设备树 3、imx415驱动…

Linux ClearOS yum无法使用解决备忘

前述 一个长期未使用的系统ClearOS Linux系统,属于CentOS家族,该系统用于网络设备。现在继续使用时,发现通过yum命令无法更新或下载软件。在这里记录一下解决该问题的过程。 问题 SSL证书问题 问题描述 问题现象如下所示,执行…

Bonjour

Bonjour 是苹果的一套零配置网络协议,用于发现局域网内的其他设备并进行通信,比如发现打印机、手机、电视等。 一句话:发现局域网其他设备和让其他设备发现。 Bonjour 可以完成的工作 IP 获取名称解析搜索服务 实际应用场景示例&#xff0…

day16 数组的常见操作和形状

目录 Numpy数组基础知识 数组的维度 数组的秩 数组的简单创建 zeros创建数组 ones创建数组 arange创建数组 数组的随机化创建 数组的遍历 数组的运算 数组的索引 一维数组索引 二维数组索引 三维数组索引 SHAP值的深入理解 知识点: numpy数组的创建:简单…

利用 Synonyms 中文近义词库调优 RAG 服务,基于 Ollama, DeepSeek R1, Langchain

目录 比对代码对比结果Synonyms 中文近义词 EmbeddingsHuggingFaceEmbeddings GitHub https://github.com/hailiang-wang/llm-get-started/tree/master/003_rag_langchain 本文介绍,在基于 RAG 服务实现问答的过程中,使用两种 Embeddings 模式下&#…

HTML 文件反编译指南:优化与学习网页代码

原文:HTML 文件反编译指南:优化与学习网页代码 | w3cschool笔记 (请勿将文章标记为付费!!!) 一、何为 HTML 文件反编译? 反编译 HTML 文件即将其从可读代码转换为更精简的形式。实际上&#…

在 ODROID-H3+ 上安装 Win11 系统

在 ODROID-H3 上安装 Windows 11 系统。 以下是完整的步骤,包括 BIOS 设置、U 盘制作、安装和驱动处理,全程不保留之前的系统数据。 ✅ 准备工作 1. 准备一个 ≥8GB 的 USB 启动盘 用另一台电脑制作 Windows 11 安装盘。 👉 推荐工具&…

大话软工笔记—分离之业务与管理

1. 业务与管理的定义 业务,指企业为达成某个目标而进行的一系列活动(业务指的是“做事”)。 管理,为实现业务目标而进行的决策、计划、组织、指导、实施、控制的过程(管理是“管事”,“事”指的是业务&am…

DeepSeek R1 模型小版本升级,DeepSeek-R1-0528都更新了哪些新特性?

DeepSeek-R1‑0528 技术剖析:思维链再进化,推理性能飙升 目录 版本概览深度思考能力再升级基准测试成绩功能与体验更新API 变动与示例模型开源与下载结语 版本概览 DeepSeek 团队今日发布 DeepSeek‑R1‑0528 —— 基于 DeepSeek V3 Base(2…

请求分页中的内存分配

最小物理块数的确定 最小物理块数是确保进程能够正常运行所需的最少物理块数量。它是一个基础保障值,若分配的物理块数少于这个值,进程可能因无法完整加载必要页面而无法正常执行。例如,一个简单程序可能至少需要 3 个物理块来存放关键代码和…

痉挛性斜颈相关内容说明

一、颈部姿态的异常偏移​ 痉挛性斜颈会打破颈部原本自然笔直的状态,让颈部像被无形的力量牵引,出现不自主的歪斜、扭转。它就像打乱了颈部原本和谐的 “平衡游戏”,使得颈部姿态偏离正常,影响日常的体态与活动。​ 二、容易察觉…

【C++】位图

位图(Bitmap)是一种用于高效表示集合的数据结构,其核心思想是使用二进制位来指示某个元素是否存在。在位图中,每个元素对应一个二进制位,若该元素存在,则对应的位为1;若不存在,则为0…