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

leaLoad 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++ 中虽然有不同的特性,但在底层的实现中,它们的初始化通常会生成类似的汇编代码。之所以出现这种情况,是因为引用和指针的本质上都是地址的操作,而汇编层面只关心数据的地址和访问方式。引用和指针的差异是由编译器层面决定和实现的,而不是在底层硬件或者汇编指令层面体现的。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/887215.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【越学学糊涂的Linux系统】(5)shell命令以及运行原理|权限问题

Ⅰ.shell命名以及运行原理&#xff1a; 0x00引用&#xff1a; 什么是shell命令&#xff1f;&#xff1f; ✔️ Shell 是一种命令行解释器&#xff08;Command - Line Interpreter&#xff09;&#xff0c;它为用户提供了与操作系统内核进行交互的接口。用户通过在 She…

【Qt】控件概述(3)—— 显示类控件

显示类控件 1. QLabel——标签1.1 setPixmap设置图片1.2 setAlignment设置文本对齐方式1.3 setWordWrap设置自动换行1.4 setIndent设置缩进1.5 setMargin设置边距1.6 body 2. QLCDNumber2.1 使用QTimer实现一个倒计时效果2.2 使用循环的方式实现倒计时 3. QProgressBar——进度…

【工程测试技术】第6章 信号处理初步,频谱分析,相关系数

目录 6.1 数字信号处理的基本步骤 6.2 离散信号及其频谱分析 6.2.1 概述 6.2.2 时域采样、混叠和采样定理 6.2.3 量化和量化误差 6.2.4 截断、泄漏和窗函数 6.2.5 频域采样、时域周期延拓和栅栏效应 6.2.6 频率分辨率、整周期截断 6.3 相关分析及其应用 6.3.1 两…

【C++】--类与对象(1)

&#x1f9c7;个人主页: 起名字真南 &#x1f32d;个人专栏:【数据结构初阶】 【C语言】 【C】 目录 1 类的定义1.1 类定义格式1.1.1 Stack类1.1.2 Date类1.1.3 Struct格式 1.2 访问限定符1.3 类域 2 实例化2.2 对象大小 3 this指针 1 类的定义 1.1 类定义格式 1 class为定义…

解决磁盘负载不均——ElasticSearch 分片分配和路由设置

ES 分片分配&#xff08;Shard Allocation&#xff09;时间点&#xff1a; 初始恢复&#xff08;Initial Recovery&#xff09;副本分配&#xff08;Replica Allocation&#xff09;重平衡&#xff08;Rebalance&#xff09;节点添加或移除 小结&#xff1a; 准备移除节点时&a…

【Golang】关于Go语言字符串转换strconv

✨✨ 欢迎大家来到景天科技苑✨✨ &#x1f388;&#x1f388; 养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; &#x1f3c6; 作者简介&#xff1a;景天科技苑 &#x1f3c6;《头衔》&#xff1a;大厂架构师&#xff0c;华为云开发者社区专家博主&#xff0c;…

k8s-集群部署1

k8s-集群部署1 一、基础环境准备二、docker环境准备三、k8s集群部署1.kubeadm创建集群2.使用kubeadm引导集群 总结 一、基础环境准备 首先&#xff0c;需要准备三个服务器实例&#xff0c;这里我使用了阿里云创建了三个实例&#xff0c;如果不想花钱&#xff0c;也可以在VM上创…

1panel申请https/ssl证书自动续期

参考教程 https://hin.cool/posts/sslfor1panel.html #Acme 账户 #1panel.腾讯云dns账号 这里有一步不需要参考,腾讯云dns账号,就是子帐号授权 直接控制台搜索 访问管理 创建用户 授权搜索dns,选择第一个 点击用户名,去掉AdministratorAccess权限 5.点击api密钥生成即可…

CSS3练习--电商web

免责声明&#xff1a;本文仅做分享&#xff01; 目录 小练--小兔鲜儿 目录构建 SEO 三大标签 Favicon 图标 布局网页 版心 快捷导航&#xff08;shortcut&#xff09; 头部&#xff08;header&#xff09; logo 导航 搜索 购物车 底部&#xff08;footer&#xff0…

初学51单片机之I2C总线与E2PROM二

总结下上篇博文的结论&#xff1a; 1&#xff1a;ACK信号在SCL为高电平期间会一直保持。 2&#xff1a;在字节数据传输过程中如果发送电平跳变&#xff0c;那么电平信号就会变成重复起始或者结束的信号。&#xff08;上篇博文的测试方法还是不能够明确证明这个结论&#xff0…

字符串和字符数组(2)

6.求字符串长度 C语言中有一个库函数叫strlen&#xff0c;这个函数是专门用来求字符串长度的。strlen的使用需要包含一个头文件string.h。 strlen函数统计的是字符串中\0之前的字符个数&#xff0c;所以传递给strlen函数的字符串中必须得包含\0. 请看代码&#xff1a; #inc…

数据结构 ——— 单链表oj题:链表分割(带哨兵位单向不循环链表实现)

目录 题目要求 手搓简易单链表 代码实现 题目要求 现有一链表的头指针 ListNode* head &#xff0c;给一定值 x &#xff0c;编写一段代码将所有小于 x 的节点排在其余节点之前&#xff0c;且不能改变原来的数据顺序&#xff0c;返回重新排列后的链表的头节点 举例说明&a…

免费送源码:Javaspringboot++MySQL springboot 社区互助服务管理系统小程序 计算机毕业设计原创定制

摘 要 随着我国经济迅速发展&#xff0c;人们对手机的需求越来越大&#xff0c;各种手机软件也都在被广泛应用&#xff0c;但是对于手机进行数据信息管理&#xff0c;对于手机的各种软件也是备受居民的喜爱&#xff0c;社区互助服务管理系统小程序被居民普遍使用&#xff0c;为…

macOS编译和运行prometheus2.54

欢迎访问我的GitHub 这里分类和汇总了欣宸的全部原创(含配套源码)&#xff1a;https://github.com/zq2599/blog_demos 本篇概览 本文详述了在macOS(M2芯片)上编译和运行prometheus2.54版本的过程&#xff0c;以及安装node_exporter和grafana并使用prometheus指标进行展示 本地…

Redis:zset类型

Redis&#xff1a;zset类型 zset命令ZADDZCARDZCOUNTZRANGEZREVRANGEZRANGEBYSCOREZREVRANGEBYSCOREZPOPMAXBZPOPMAXZPOPMINBZPOPMINZRANKZREVRANKZSCOREZREMZREMRANGEBYRANKZREMRANGEBYSCOREZINCRBY 集合间操作ZINRERSTOREZUNIONSTORE 内部编码ziplistskiplist 在Redis中&…

单片机的两种看门狗原理解析——IWDG和WWDG

一、IWDG独立开门狗的主要性能 计时机制&#xff1a; 递减计数器 独立开门狗的初始频率&#xff1a; LSI低速内部时钟&#xff1a;RC震荡器&#xff0c;40kHz 独立开门狗是以LSI为初始频率的&#xff0c;所以独立开门狗的初始时钟频率取决与单片机本身&#xff0c;因此在使…

[每周一更]-(第117期):硬盘分区表类型:MBR和GPT区别

文章目录 1. **支持的磁盘容量**2. **分区数量**3. **引导方式**4. **冗余和数据恢复**5. **兼容性**6. **安全性**7. **操作系统支持**8. 对比 国庆假期前补一篇 在一次扫描机械硬盘故障的问题&#xff0c;发现我本机SSD和机械硬盘的分类型不一样&#xff0c;分别是GPT和MBR&a…

Matlab编程示例24:freexyn在b站的读取手写体mnist数据集的matlab代码

1.mnist手写体数据集介绍 手写数字MNIST数据库由60000个示例的训练集和10000个示例的测试集组成。这些数字已进行归一化&#xff0c;每个示例是28*28像素的图片&#xff0c;图片是黑底白字&#xff0c;每个图片的标签就是图片上的数字&#xff0c;数字范围是0~9&#xff0c;总…

记录一次病毒启动脚本

在第一次下载软件时&#xff0c;目录中配了一个使用说明&#xff0c;说是需要通过start.bat 这个文件来启动程序&#xff0c;而这个 start.bat 就是始作俑者&#xff1a; 病毒作者比较狡猾&#xff0c;其中start.bat 用记事本打开是乱码&#xff0c;但是可以通过将这个批处理…

小程序 uniapp+Android+hbuilderx体育场地预约管理系统的设计与实现

目录 项目介绍支持以下技术栈&#xff1a;具体实现截图HBuilderXuniappmysql数据库与主流编程语言java类核心代码部分展示登录的业务流程的顺序是&#xff1a;数据库设计性能分析操作可行性技术可行性系统安全性数据完整性软件测试详细视频演示源码获取方式 项目介绍 用户 注册…