基于arm的C++反汇编 基本数据类型

12 篇文章 1 订阅
11 篇文章 0 订阅

文章目录


#基本数据类型
  反汇编一个基本的知识点就是掌握数据类型,包括整形和浮点类型在内存中是如何存放的,这里要知道原码,反码,补码,以及IEEE浮点标准,这部分与处理器架构无关,《C++反汇编与逆向分析技术揭秘》 第二章已经有详细讲解,这里略过。
在此推荐下我曾经写过的一个 进制转换的工具 来学习数据类型 。
这里写图片描述

Cpp中的引用

  C++为了简化指针操作,对其进行了封装,产生了引用类型。实际上引用类型就是指针类型,只不过用于存放地址的内存空间对使用者而言是隐藏的。下面看一段代码:

#include <iostream>  

using namespace std;  

void Add(int &nVar)
{
	nVar ++;
}

int main()
{
	int nVar 	= 0x12345678;
	int &nVarType	= nVar;

	Add(nVar);

	cout << &nVarType << endl;
	cout << &nVar << endl;

	cout << hex << nVar << endl;
	nVarType ++;
	cout << hex << nVar << endl;
}

编译$ arm-linux-c++ -static 1.c之后放到上一节的qemu-arm虚拟机中跑下运行结果如下:
这里写图片描述

arm-linux-objdump -D -m arm 看下反汇编结果:

000091fc <_Z3AddRi>:
    91fc:       e52db004        push    {fp}            ; (str fp, [sp, #-4]!)
                            ;这里来到子函数add,首先push fp保存栈帧,相当于x86下的EBP寄存器
    9200:       e28db000        add     fp, sp, #0
    9204:       e24dd00c        sub     sp, sp, #12     ;开辟一个栈帧
    9208:       e50b0008        str     r0, [fp, #-8]
    920c:       e51b3008        ldr     r3, [fp, #-8]   ;生成了好多冗余指令
    9210:       e5933000        ldr     r3, [r3]        ;这里是得到 nVar 数值
    9214:       e2832001        add     r2, r3, #1      ;这里不用说就是 ++
    9218:       e51b3008        ldr     r3, [fp, #-8]   ;得到nVar 地址
    921c:       e5832000        str     r2, [r3]        ;把自加之后的值给 nVar 
    9220:       e24bd000        sub     sp, fp, #0      ;后面两条指令恢复栈帧
    9224:       e49db004        pop     {fp}            ; (ldr fp, [sp], #4)
    9228:       e12fff1e        bx      lr

0000922c <main>:
    922c:       e92d4800        push    {fp, lr}
    9230:       e28db004        add     fp, sp, #4 ;栈帧上边界fp
    9234:       e24dd008        sub     sp, sp, #8 ;栈帧下边界sp
    9238:       e59f30d4        ldr     r3, [pc, #212]  ; 9314 <main+0xe8>
                         ;取出立即数 0x12345678 放到r3中
    923c:       e50b300c        str     r3, [fp, #-12]
                         ;把r3内容也就是0x12345678 存放到nVar(fp-12)地址处
    9240:       e24b300c        sub     r3, fp, #12  ;r3此时存放变量 nVar 的地址
    9244:       e50b3008        str     r3, [fp, #-8] ; 把nVar 地址存放到 nVarType
                          ;这里需要注意了,fp-8属于nVarType变量的地址,在这个地址处存放着
                          ;变量 nVar 的地址,所以说引用也是占用内存的,只是这块内存存放的不是
                          ;被引用对象的数值,而是被引用对象的地址
    9248:       e24b300c        sub     r3, fp, #12
    924c:       e1a00003        mov     r0, r3
    9250:       ebffffe9        bl      91fc <_Z3AddRi> ;传递nVar 地址给add函数
    9254:       e59f00bc        ldr     r0, [pc, #188]  ; 9318 <main+0xec>
    9258:       e51b1008        ldr     r1, [fp, #-8]   ;这里注意了,这条指令相当于把fp-8  
                          ;处的内容传递给r1,也就是 nVar 的地址给r1,这样导致 
                          ;cout << &nVarType 输出结果是 nVar 的地址。
    925c:       eb000dbd        bl      c958 <_ZNSolsEPKv>
    9260:       e1a03000        mov     r3, r0
    9264:       e1a00003        mov     r0, r3
    9268:       e59f10ac        ldr     r1, [pc, #172]  ; 931c <main+0xf0>
    926c:       eb0004fe        bl      a66c <_ZNSolsEPFRSoS_E>
    9270:       e24b300c        sub     r3, fp, #12
    9274:       e59f009c        ldr     r0, [pc, #156]  ; 9318 <main+0xec>
    9278:       e1a01003        mov     r1, r3   ;传递nVar地址给 r1 寄存器
    927c:       eb000db5        bl      c958 <_ZNSolsEPKv>

  通过上面分析发现,引用和指针存储方式是一样的,都是使用内存空间存放地址值。定义一个引用,在本质上编译器生成的指令和定义一个指针是一样的,在反汇编下没有引用这种类型。指针虽然灵活,但是使用失误后果严重,引用就可以避免这种问题。
  另外上面还分析了为什么 cout << &nVarType输出的是 nVar 的地址,其实从编译器理解的角度还可以分析出来,这篇文章给出了一个很好的解释。

#常量
  前面介绍的数据类型都是以变量形式进行演示的,在程序运行中可以修改其保存的数值。从字面理解常量是一个恒定不变的量。常量数据在程序运行前已经存在于可执行文件一个特殊的节中,加载进内存之后,常量就到了一个只读段中,此时对常量试图修改会导致错误。
比如下面一段代码:

#include <iostream>  

using namespace std;

int main()
{
	char *str = "hello world\n";

	cout << str << endl;

	str[1] ++;
}

运行结果就是段错误:
这里写图片描述

##常量的定义
  在C++中可以使用宏机制#define来定义常量,也可以使用const定义一个编译器角度不可写的常量。#define定义的常量名称,编译器编译时会将代码中的宏名称替换成对应信息。宏的使用增加代码可读性,const是为了增加程序健壮性而存在的。宏与const使用如下代码清单所示:

#include <iostream>  

using namespace std;

int main()
{
	///定义 NUMBER_ONE 为常量1
	#define NUMBER_ONE	1
	///将常量 NUMBER_ONE	赋值给常量nVar 
	const int nVar = NUMBER_ONE;
	///显示两者结果
	cout << nVar << NUMBER_ONE << endl;
}

使用编译命令 $ arm-linux-c++ -static -E 2.c > out.txt 生成预处理文件

int main() 
{
 const int nVar = 1;

 cout << nVar << 1 << endl;
}

##const和define的区别
  define是一个真常量,而const却是由编译器判断实现的常量,是一个假常量。在实际中,使用const定义的变量,最终还是一个变量,只是在编译器内进行了检查,发现有修改则报错。

  由于编译器在编译期间对const变量进行检查,因此被const修饰过的变量是可以修改的。利用指针获取到const修饰过的变量地址,强制将指针的const修饰去掉,就可以修改对应的数据内容。

#include <iostream>  

using namespace std;

int main()
{
	// 将变量 nConst 修饰为const
	const int nConst = 5;
	
	//  定义int 类型的指针,保存nConst 地址
	int *pConst = (int*)&nConst;

	//  修改指针pConst 并指向地址中的数据
	*pConst = 6;

	//  将修饰为const 的变量nConst 赋值给nVar
	int nVar = nConst;

	cout << nVar << endl;
}

使用 $ arm-linux-objdump -D -m arm看一下:

000091fc <main>:
    91fc:       e92d4800        push    {fp, lr}
    9200:       e28db004        add     fp, sp, #4
    9204:       e24dd010        sub     sp, sp, #16
    9208:       e3a03005        mov     r3, #5
    920c:       e50b3010        str     r3, [fp, #-16]  ;nConst = 5;
    9210:       e24b3010        sub     r3, fp, #16 
    9214:       e50b3008        str     r3, [fp, #-8]   ;pConst = &nConst 
    9218:       e51b3008        ldr     r3, [fp, #-8]   ;r3 = pConst 
    921c:       e3a02006        mov     r2, #6          ;r2 = 6
    9220:       e5832000        str     r2, [r3]        ;*pConst = r2 = 6;
    9224:       e3a03005        mov     r3, #5
    9228:       e50b300c        str     r3, [fp, #-12]  ;nVar = r3 = 5
    922c:       e59f0024        ldr     r0, [pc, #36]   ; 9258 <main+0x5c>
    9230:       e51b100c        ldr     r1, [fp, #-12]  ;r1 = nVar 
    9234:       eb000971        bl      b800 <_ZNSolsEi>
    9238:       e1a03000        mov     r3, r0
    923c:       e1a00003        mov     r0, r3
    9240:       e59f1014        ldr     r1, [pc, #20]   ; 925c <main+0x60>
    9244:       eb000469        bl      a3f0 <_ZNSolsEPFRSoS_E>
    9248:       e3a03000        mov     r3, #0
    924c:       e1a00003        mov     r0, r3
    9250:       e24bd004        sub     sp, fp, #4
    9254:       e8bd8800        pop     {fp, pc}

  在上述代码中,由于const修饰的变量nConst被赋值一个数字常量5,编译器在编译过程中发现nConst的初值是可知的,并且被修饰为const。之后所有使用nConst的地方都以这个可预知值替换,故intnVar=nConst;对应的汇编代码没有将nConst赋值给nVar,而是用常量值5代替
  通过上面反汇编分析可见nConst的数值已经被修改了,因此 被const修饰后,变量本质上并没有改变,还是可以修改的。#define与const两者之间还是不同的。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
反汇编带符号表的32位/64位ELF目标文件,CPU类型:ARM PowerPC MIPS 操作菜单选择:文件解析 Alx+P ELF文件解析 Alt+E 另有文本比较等杂项功能。V1.25.05相对上一版本,增加内存数据按数据类型解析功能;V1.25.04相对上一版本,完善X86反汇编,修复小BUG;V1.25.02相对上一版本,COREDUMP统计、匹配目标文件等相关功能优化,修复小BUG;V1.25.00相对上一版本,相关功能支持动态库文件,查询代码支持无符号目标文件+有符号目标文件,COREDUMP统计、与问题单关联、目标文件/CORE文件/问题单同步;V1.24.02相对上一版本,针对进程主动捕捉异常的信息定制处理进一步完善COREDUMP文件解析与应用,增强软件管理;V1.24.01相对上一版本,进一步完善COREDUMP文件解析与应用,提供部分ARM Thumb指令反汇编;V1.24.00相对上一版本,进一步完善COREDUMP文件解析与应用,提供堆栈调用关系分析;V1.23.03相对上一版本,提供32位X86反汇编;V1.23.02相对上一版本,提供源代码行查询指令地址,OBJ/COREDUMP文件解析,sprintf函数参数特定检查,完善文件拖放操作,修复小BUG;V1.23.01相对上一版本,提供ELF文件指令修改,修复ARM MLS指令反汇编错误等BUG;V1.23.00相对上一版本,提供程序地址对应源代码行查询,修复MIPS调试信息错误;V1.22相对上一版本,修复MIPS小端字节序反汇编错误,网上最新版本提示;另部分增强功能的菜单操作设有密码,如有兴趣欢迎咨询。欢迎大家反馈相关软件使用过程中的问题!
反汇编带符号表的32位/64位ELF目标文件,CPU类型:ARM PowerPC MIPS 操作菜单选择:文件解析 Alx+P ELF文件解析 Alt+E 另有文本比较等杂项功能。V1.25.00相对上一版本,相关功能支持动态库文件,查询代码支持无符号目标文件+有符号目标文件,COREDUMP统计、与问题单关联、目标文件/CORE文件/问题单同步;V1.24.02相对上一版本,针对进程主动捕捉异常的信息定制处理进一步完善COREDUMP文件解析与应用,增强软件管理;V1.24.01相对上一版本,进一步完善COREDUMP文件解析与应用,提供部分ARM Thumb指令反汇编;V1.24.00相对上一版本,进一步完善COREDUMP文件解析与应用,提供堆栈调用关系分析;V1.23.03相对上一版本,提供32位X86反汇编;V1.23.02相对上一版本,提供源代码行查询指令地址,OBJ/COREDUMP文件解析,sprintf函数参数特定检查,完善文件拖放操作,修复小BUG;V1.23.01相对上一版本,提供ELF文件指令修改,修复ARM MLS指令反汇编错误等BUG;V1.23.00相对上一版本,提供程序地址对应源代码行查询,修复MIPS调试信息错误;V1.22相对上一版本,修复MIPS小端字节序反汇编错误,网上最新版本提示;V1.21相对上一版本,菜单调整,完善64位ELF文件解析,解析调试信息;另部分增强功能的菜单操作设有密码,如有兴趣欢迎咨询。欢迎大家反馈相关软件使用过程中的问题!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值