C/C++逆向:数据类型识别
在逆向工程中,数据类型识别是理解程序逻辑的重要步骤,因为它直接影响对程序逻辑和功能的理解,识别出数据类型有助于确定变量的含义和函数的行为。在分析恶意软件或者寻找安全漏洞时,识别数据类型能够帮助发现代码中的潜在问题。例如,缓冲区溢出问题通常与错误的数据类型或者数组边界检查不当有关,了解变量的类型和大小有助于发现这类安全问题。那接下去我们就通过一个简单的例子来针对不同类型的数据进行特征分析。
逆向分析实例
下面是一个含有多种类型的数据变量的代码示例:
#include <stdio.h>
#include <stdlib.h>
int main() {short numA = 20;int numB = 10;long numC = 30;long long numD = 40;char letter = 'A';char text[] = "Hello, world!";float factor = 3.14;int * nPtr = &numB;long & nQuC = numC;
printf("numA=%d\r\n", numA);printf("numB=%d\r\n", numB);printf("numC=%d\r\n", numC);printf("numD=%lld\r\n", numD);printf("Letter=%c\r\n", letter);printf("text=%s\r\n", text);printf("text=%f\r\n", factor);printf("text=%d\r\n", *nPtr);printf("text=%d\r\n", nQuC);
system("pause");return 0;
}
此时使用Visual Studio
对该代码进行编译,生成exe
文件,对应的编译配置为Debug-x86
;
生成完成后放入x96dbg
进行动态调试:
接着定位到main
函数进行反汇编代码分析(定位main
函数的方法请看笔者前面C/C++逆向:定位main函数
文章);重点需要看的反汇编代码为红线以下部分,我们重点关注变量初始化部分,也就是方框中的代码。
①Short类型
mov eax,14
mov word ptr ss:[ebp-C],ax
mov eax, 14
:将数值 20
加载到 eax
寄存器中。
mov word ptr ss:[ebp-C],ax
:将 eax
的低 16 位(即 ax
)的内容存储到内存地址 ss:[ebp-C]
,其中 [ebp-C]
通常表示函数栈帧中的一个局部变量。
word ptr
是什么含义?
word ptr 是用于指示操作数的数据类型大小的指令修饰符word:表示 16 位的数据,即两个字节(2 bytes)。ptr(pointer):指针的缩写,意味着操作的对象是一个内存地址。
ss:
前缀
ss 表示 Stack Segment(堆栈段);在 x86 架构中,内存地址可以由段寄存器和偏移量共同构成,ss: 表明目标内存地址是相对于堆栈段的。
因为short
类型占2个字节,即16位,所以在第一条指令将数值加载到 eax
寄存器以后,在将数值放入内存ss:[ebp-C]
时的源操作数为eax
寄存器的低16位ax
。后续在逆向过程中遇到这种特征的代码就可以初步进行判断。
②int类型
相关代码:
mov dword ptr ss:[ebp-18],A
可以看到int
类型的数据初始化就没有像short
类型那样子弯绕,直接就将数值A(10)
存入内存地址 ss:[ebp-18]
。此时用于指示操作数的数据类型大小的指令修饰符为dword ptr
也就是表示32位。
③long类型
相关代码:
mov dword ptr ss:[ebp-24],1E
long
类型数据与int
类型一样也是直接将将值1E(30)
直接放入内存堆栈中,原因是因为在Windows环境中long
的长度与int
类型是一致的,长度都是4字节。所以此时用于指示操作数的数据类型大小的指令修饰符为dword ptr
也就是表示32位。此外还有一个需要注意的点即长整型long在不用的操作系统中所占用的字节数不同,具体的占用字节如下图:
所以在写跨平台的应用时,如果使用到long型,需要考虑到精度丢失的问题,在写跨平台应用时也尽量避免使用long型。
④long long 类型
相关代码:
mov dword ptr ss:[ebp-34],28
mov dword ptr ss:[ebp-30],0
在 x86 体系结构中,long long
类型是一个 64 位(8 字节)
的数据类型,而在 32 位程序中,通常使用两个 32 位(4 字节)
的存储单元来存储 long long
变量的高 32 位和低 32 位。因此,初始化 long long
类型数据会涉及到两个内存位置的赋值操作。
mov dword ptr ss:[ebp-34], 28
:这条指令意味着 long long
变量的低 32 位被初始化为 28(40)
。
mov dword ptr ss:[ebp-30], 0
:[ebp-30]
是紧接着 [ebp-34]
的下一个 4 字节内存位置,用来存储 long long
变量的高 32 位;这里将高 32 位初始化为 0
,此时我们可以查看内存窗口中的值:
可以发现确实是低 32 位被初始化为 28(40)
,高 32 位初始化为 0
。
如果此时代码的编译配置设置位x64
,那么long long
数据的初始化的反汇编代码就会是这样的:
mov qword ptr [numD],28h
⑤char类型
相关代码:
mov byte ptr ss:[ebp-3D],41
byte ptr
表示操作的数据大小是 8 位(1 字节),char
类型的数据在内存中占用 1 字节,因此汇编代码使用了 byte ptr
来进行操作。值 41
(十六进制)对应于 ASCII 字符 'A'
,此时我们也可以通过查看内存来进行确定,在反汇编窗口中的对应反汇编代码中右击,选择在内存中转到:
接着选择地址(A)选项,然后此时内存窗口中就会将对该地址的值进行定位。
⑥字符串类型
相关代码:
mov eax,dword ptr ds:[FD7B30]
mov dword ptr ss:[ebp-58],eax
mov ecx,dword ptr ds:[FD7B34]
mov dword ptr ss:[ebp-54],ecx
mov edx,dword ptr ds:[FD7B38]
mov dword ptr ss:[ebp-50],edx
mov ax,word ptr ds:[FD7B3C]
mov word ptr ss:[ebp-4C],ax
这段汇编代码对应的是对一个字符串的逐步初始化过程。在 32 位系统中,由于寄存器和内存操作的限制,字符串数据是分块拷贝的。前面几条指令每次拷贝 4 字节,最后拷贝 2 字节。
char text[] = "Hello, world!";
这个字符串的初始化过程:
先将dword ptr ds:[FD7B30]
值也就是6C6C6548(Hell)
放入eax寄存器中;
然后将eax
中的值转入dword ptr ss:[ebp-58]
内存堆栈中,这就是第一个分块拷贝的过程,后续则是分别将内存中存储的字符串的值分别进行分块拷贝,因为过程都一样这边就不再一一列举了。这边主要说一下最后一个步骤:对于字符串Hello, world!
来说应该需要做3次4字节+1次1字节的内存分块拷贝才对,为什么最后一个步骤使操作的指示符为word ptr
两个字节呢?
mov ax,word ptr ds:[FD7B3C]
mov word ptr ss:[ebp-4C],ax
原因就是因为每个字符串都需要一个结束符号,所以最后两个字节应该是21(!) 00(结束符)
。
⑦float类型
相关代码
movss xmm0,dword ptr ds:[FD7BB0]
movss dword ptr ss:[ebp-64],xmm0
movss xmm0, dword ptr ds:[FD7BB0]
:从数据段(ds
)地址 [FD7BB0]
处读取一个 32 位(4 字节) 的单精度浮点数(float
),并将其存储到 xmm0
寄存器中。
movss 指令用于移动单精度浮点数(即 32 位的 float),它与整数的 mov 不同,专门用于处理浮点数据;xmm0 是一个用于浮点运算的寄存器。
此时寄存器中的如下:
movss dword ptr ss:[ebp-64], xmm0
:将 xmm0
寄存器中的单精度浮点数存储到栈帧中相对于基址指针 ebp
的偏移量 -64
处。
寄存器中XMM0
中的十六进制值就是浮点型数据在内存中的存储形式,下面我们就来说一下十六进制的值与浮点数之间的转化是如何进行的,将十六进制数转换为浮点数涉及将其按照 IEEE 754 标准进行解码。IEEE 754 是计算机中用来表示浮点数的标准格式,其中单精度浮点数(float
)占用 32 位(4 字节)。其转换的具体步骤如下:
IEEE 754 单精度(float)浮点数表示法
单精度浮点数的 32 位结构如下:
符号位(S):1 位,表示正负号(0 为正,1 为负)。 指数位(E):8 位,用于表示指数。 尾数位(M):23 位,用于表示有效数字(尾数)。
首先将十六进制数转化为二进制:0x4048F5C3
转换为二进制是:0100 0000 0100 1000 1111 0101 1100 0011
对应到 IEEE 754 的结构中:
符号位(S):第 1 位是 0,表示正数。 指数位(E):接下来的 8 位是 10000000,对应十进制为 128。 尾数位(M):剩下的 23 位是 0100 1000 1111 0101 1100 0011。
接着需要解码浮点数:
符号位:S = 0,表示正数。 指数位:E = 128,所以实际的指数为:E - 127 = 1。 尾数位:M = 1.01001000111101011100011(添加隐含的 1)。相当于1+0.25+0.00390625+0.0009765625+⋯≈1.57
接着将数值代入公式中进行计算,具体表示公式为:
Value = 1^0 * 2^1 * 1.57 = 3.14
通过使用 IEEE 754 格式将 0x4048F5C3
转换为浮点数,得到的值是大约 3.14。
IEEE 754 双精度(double)浮点数表示法
在 IEEE 754 双精度浮点数(double 类型)中,浮点数的表示和单精度有所不同。双精度使用 64 位来表示,其中包括符号位、指数位和尾数位,具体结构如下:
双精度浮点数的 64 位结构
符号位(S):1 位,用于表示正负号(0 表示正数,1 表示负数)。 指数位(E):11 位,用于表示指数,使用偏移量 1023,需要减去偏移量 1023 以得到实际的指数值。 尾数位(M):52 位,用于表示有效数字(尾数)。
双精度浮点数表示公式为:
计算方式都是一样的,这边就不做过多赘述。
⑧指针
相关代码
lea eax,dword ptr ss:[ebp-18]
mov dword ptr ss:[ebp-70],eax
lea
是 Load Effective Address
指令,用于将操作数的有效地址加载到指定的寄存器中。
所以lea eax, dword ptr ss:[ebp-18]
的作用是将该偏移量的地址加载到 eax
寄存器中,而不是读取该地址的内容。这种用法通常用来获取某个变量的地址,因此 eax
寄存器中存储的是变量的地址,即指针值。
mov dword ptr ss:[ebp-70], eax
:这条指令将 eax
寄存器的值(即之前计算出的地址)存储到栈中相对于基址指针 ebp
的偏移量 -70
处。
⑨引用类型
相关代码
lea eax,dword ptr ss:[ebp-24]
mov dword ptr ss:[ebp-7C],eax
在这边我们可以看到引用类型的初始化反汇编代码与指针类型的反汇编代码一致。lea eax, [ebp-24]
的作用是将该局部变量的地址加载到寄存器 eax
中。mov dword ptr ss:[ebp-7C], eax
这条指令将寄存器 eax
中的值(也就是局部变量的地址)存储到栈中相对于基址指针 ebp
的偏移量 -7C
处。
指针和引用在 C++ 中虽然有不同的特性,但在底层的实现中,它们的初始化通常会生成类似的汇编代码。之所以出现这种情况,是因为引用和指针的本质上都是地址的操作,而汇编层面只关心数据的地址和访问方式。引用和指针的差异是由编译器层面决定和实现的,而不是在底层硬件或者汇编指令层面体现的。
相关文章:

C/C++逆向:数据类型识别
在逆向工程中,数据类型识别是理解程序逻辑的重要步骤,因为它直接影响对程序逻辑和功能的理解,识别出数据类型有助于确定变量的含义和函数的行为。在分析恶意软件或者寻找安全漏洞时,识别数据类型能够帮助发现代码中的潜在问题。例…...

PASCAL VOC 2012数据集 20类物体,这些物体包括人、动物(如猫、狗、鸟等)、交通工具(如车、船、飞机等)以及家具(如椅子、桌子、沙发等)。
VOC2012数据集是PASCAL VOC挑战赛官方使用的数据集之一,主要包含20类物体,这些物体包括人、动物(如猫、狗、鸟等)、交通工具(如车、船、飞机等)以及家具(如椅子、桌子、沙发等)。每个…...
题目:最左边的数字
问题 - 1060 (hdu.edu.cn) 解题思路: 数字很大,使用科学计数法。则,我们需要的是a的整数位,最终求出a即可。 取对数:nlgnmlga,移项:lganlgn-m,接下来我们需要求m。 …...

第 4 章 Spring IoC容器之BeanFactory
Spring 的 IoC 容器是一个提供 IoC 支持的轻量级容器,除了基本的 IoC 支持,它作为轻量级容器还提供了 IoC 之外的支持。 Spring 提供了两种容器类型:BeanFactory 和 ApplicationContext: BeanFactory,基础类型 IoC 容…...

滚雪球学Oracle[2.3讲]:Oracle Listener配置与管理
全文目录: 前言一、Oracle Listener的基础概念1.1 什么是Oracle Listener?Listener的作用: 1.2 Oracle Listener的配置文件示例listener.ora配置文件: 1.3 启动与管理Listener 二、多Listener配置与负载分担2.1 多Listener的应用场…...

免费送源码:Javaspringboot++MySQL springboot 社区互助服务管理系统小程序 计算机毕业设计原创定制
摘 要 随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受居民的喜爱,社区互助服务管理系统小程序被居民普遍使用,为…...

成都睿明智科技有限公司抖音电商新蓝海的领航者
在当今这个数字化浪潮汹涌的时代,电商行业正以惊人的速度迭代升级,而抖音电商作为新兴势力,更是凭借其庞大的用户基数、精准的算法推荐和高度互动的社区氛围,成为了众多商家竞相追逐的蓝海市场。在这片充满机遇与挑战的海洋中&…...

不可错过!CMU最新《生成式人工智能大模型》课程:从文本、图像到多模态大模型
1. 课程简介 从生成图像和文本到生成音乐和艺术,生成模型一直是人工智能的关键挑战之一。本课程将探讨推动生成模型和基础模型(Foundation Models)最近进展的机器学习和人工智能技术。学生将学习、开发并应用最先进的算法,使机器…...

重庆数字孪生工业互联网可视化技术,赋能新型工业化智能制造工厂
重庆作为西南地区的重要工业基地,正积极探索和实践数字孪生、工业互联网及可视化技术在智能制造领域的深度融合,致力于打造新型工业化智能制造工厂,为制造业的高质量发展注入强劲动力。 在重庆的智能制造工厂中,数字孪生技术被广…...

Qt QPushButton clicked信号浅析
前言 Qt 的 QPushButton clicked 信号原型: void clicked(bool checked false);通常,使用 Qt 的 QPushButton clicked 点击信号时,会以如下方式使用: connect(ui->pushButton, &QPushButton::clicked, this, [](){qDeb…...

Python时间戳转日期
在Python中,你可以使用datetime模块将时间戳转换为日期。时间戳通常是一个表示自1970年1月1日(UTC)以来的秒数的浮点数或整数。 以下是一个简单的示例,展示了如何将时间戳转换为日期和时间: import datetime# 示例时…...

对 LLM 工具使用进行统一
我们为 LLM 确立了一个跨模型的 统一工具调用 API。有了它,你就可以在不同的模型上使用相同的代码,在 [Mistral] 或 [Llama]) 等模型间自由切换,而无需或很少需要根据模型更改工具调用相关的代码。此外,我们还在 transformers 中新…...

webpack/vite的区别
Webpack和Vite都是前端开发中常用的构建工具,它们在多个方面存在显著的区别。以下是对这两个构建工具的详细比较: 一、基础概念与定位 Webpack Webpack是一个现代JavaScript应用程序的静态模块打包器(module bundler)。它允许开发…...

【笔记】信度检验
一、信度 信度是指测量结果的一致性和稳定性。 1.一致性(Consistency) 一致性指的是测量工具内部各个部分或项目之间的协调一致程度。高一致性意味着测量工具的不同部分都在测量同一个概念或特质。 例子:智力测试 假设我们有一个包含100…...

使用Python实现无人机路径规划的灰狼优化算法
目录 使用 Python 实现无人机路径规划的灰狼优化算法引言1. 灰狼优化算法概述1.1 定义1.2 算法原理1.3 灰狼的狩猎策略1.4 算法步骤 2. Python 中的灰狼优化算法实现2.1 安装必要的库2.2 定义类2.2.1 灰狼类2.2.2 群体类2.2.3 路径规划类 2.3 示例程序 3. 灰狼优化算法的优缺点…...

理解递归和回溯
文章目录 什么是递归回溯 什么是递归 回溯 //使用递归回溯来给小球找路//说明//1. map 表示地图//2. i,j 表示从地图的哪个位置开始出发 (1,1)//3. 如果小球能到 map[6][5] 位置,则说明通路找到.//4. 约定: 当map[i][j] 为 0 表示该点没有走过 当为 1 表…...

知识图谱入门——3:工具分类与对比(知识建模工具:Protégé、 知识抽取工具:DeepDive、知识存储工具:Neo4j)
在知识图谱构建的过程中,针对不同的任务和需求,我们可以使用多种工具。为了帮助你快速选择合适的工具,本文将常用工具按类别进行分类介绍,并对比其优缺点,方便你在不同场景中做出最佳选择。 文章目录 1. 知识建模工具…...

使用指标进行量化交易时,有哪些需要注意的风险点呢
炒股自动化:申请官方API接口,散户也可以 python炒股自动化(0),申请券商API接口 python炒股自动化(1),量化交易接口区别 Python炒股自动化(2):获取…...

数据结构阶段测试2的一点小补充
数据结构阶段测试2的一点小补充 1.已知⼩根堆为8,15,10,21,34,16,12,删除关键字8之后需重建堆,最后的叶⼦ 节点为() A. 34 B. 21 C. 16 D. 12 解题思路 向下调整算法删除堆顶元素 💡 答案:C 删除堆顶元素的思路: …...

量化交易里面的挂单成交率大概是多少呢
炒股自动化:申请官方API接口,散户也可以 python炒股自动化(0),申请券商API接口 python炒股自动化(1),量化交易接口区别 Python炒股自动化(2):获取…...

【Android 14源码分析】Activity启动流程-3
忽然有一天,我想要做一件事:去代码中去验证那些曾经被“灌输”的理论。 – 服装…...

Javascript客户端时间与服务器时间
在Java代码中使用new Date(),获取的是本机时间; 但是在Javascript 中使用new Date(),获取的却是访问该页面的客户端时间。 这样,就可能会出现一个问题:我的电脑时间比正常时间要快,我访问一个页面&#x…...

系统架构设计师教程 第11章 11.4 边缘计算概述 笔记
11.4 边缘计算概述 ★★☆☆☆ 11.4.1 边缘计算概念 边缘计算将数据的处理、应用程序的运行甚至一些功能服务的实现,由 网络中心下放到网络边缘的节点上。在网络边缘侧的智能网关上就近采集并且处理数据,不需要上传原生数据。 11.4.2 边缘计算的定义 1…...

CSS全解析
文章目录 CSS全解析一、CSS是什么二、基本语法规范三、引入方式(一)内部样式表(二)行内样式表(三)外部样式 四、代码风格(一)样式格式(二)样式大小写…...

一款基于 Java 的可视化 HTTP API 接口快速开发框架,干掉 CRUD,效率爆炸(带私活源码)
平常我们经常需要编写 API,但其实常常只是一些简单的增删改查,写这些代码非常枯燥无趣。 今天给大家带来的是一款基于 Java 的可视化 HTTP API 接口快速开发框架,通过 UI 界面编写接口,无需定义 Controller、Service、Dao 等 Jav…...

CSS3渐变
一、线性渐变 通过background-image: linear-gradient(...)设置线性渐变 语法: linear-gradient(direction,color1,color2, . . ) direction:渐变方向,默认从上到下,可选值: 简单选取: ① to right&…...

Emissive CEO Fabien Barati谈《消失的法老》背后的故事:XR大空间体验的创新与未来
在最近的一次播客访谈中,虚拟现实之声(Voices of VR)的主持人Kent Bye与Emissive公司的联合创始人兼CEO Fabien Barati进行了深入交流。Emissive是全球顶级的VR大空间体验制作商之一,以其沉浸式探险项目如《永恒的巴黎圣母院》和《胡夫地平线》而闻名。以下是这次访谈的核心…...

mysql设置表的某一个字段每天定时清零
推荐学习文档 golang应用级os框架,欢迎stargolang应用级os框架使用案例,欢迎star案例:基于golang开发的一款超有个性的旅游计划app经历golang实战大纲golang优秀开发常用开源库汇总想学习更多golang知识,这里有免费的golang学习笔…...

实例分割、语义分割和 SAM(Segment Anything Model)
实例分割、语义分割和 SAM(Segment Anything Model) 都是图像处理中的重要技术,它们的目标是通过分割图像中的不同对象或区域来帮助识别和分析图像,但它们的工作方式和适用场景各有不同。 1. 语义分割(Semantic Segme…...

深度学习项目----用LSTM模型预测股价(包含LSTM网络简介,代码数据均可下载)
前言 前几天在看论文,打算复现,论文用到了LSTM,故这一篇文章是小编学LSTM模型的学习笔记;LSTM感觉很复杂,但是结合代码构建神经网络,又感觉还行;本次学习的案例数据来源于GitHub,在…...