freeBuf
主站

分类

云安全 AI安全 开发安全 终端安全 数据安全 Web安全 基础安全 企业安全 关基安全 移动安全 系统安全 其他安全

特色

热点 工具 漏洞 人物志 活动 安全招聘 攻防演练 政策法规

点我创作

试试在FreeBuf发布您的第一篇文章 让安全圈留下您的足迹
我知道了

官方公众号企业安全新浪微博

FreeBuf.COM网络安全行业门户,每日发布专业的安全资讯、技术剖析。

FreeBuf+小程序

FreeBuf+小程序

Radare2分析ARM64汇编
2020-12-06 13:13:24

使用Radare2静态分析apk(2)末尾通过Radare2分析出一段ARM64汇编代码,这篇文通过分析这段汇编代码来来了解下ARM64汇编。

完整的代码

; UNKNOWN XREF fromunk @ 
156: sym.Java_com_example_myapplication_MainActivity_stringFromJNI (int64_targ1, int64_targ2);
│ ; var int64_tvar_28h @ x29-0x28
│ ; var int64_tvar_bp_8h @ x29-0x8
│ ; var int64_tvar_0h @ sp+0x0
│ ; var int64_tvar_8h @ sp+0x8
│ ; var int64_tvar_10h @ sp+0x10
│ ; varchar*var_18h @ sp+0x18
│ ; var int64_tvar_0h_2 @ sp+0x30
│ ; var int64_tvar_20h @ sp+0x40
│ ; var int64_tvar_60h @ sp+0x60
│ ; var int64_tvar_60h_2 @ sp+0x68
│ ; arg int64_targ1 @ x0
│ ; arg int64_targ2 @ x1
0x0000f08cffc301d1 sub sp, sp, 0x70
│ 0x0000f090fd7b06a9stpx29, x30, [var_60h]
│ 0x0000f094 fd830191 add x29, var_60h
│ 0x0000f098 48d03bd5 mrs x8, tpidr_el0
│ 0x0000f09c081540f9ldrx8, [x8, 0x28]; aav.0x00000028
│ ; [0x28:4]=0x341f8
│ 0x0000f0a0a8831ff8sturx8, [var_bp_8h]
│ 0x0000f0a4a0831df8sturx0, [var_28h]; arg1
│ 0x0000f0a8e11b00f9strx1, [var_0h_2]; arg2
│ 0x0000f0ac c10000b0 adrp x1, 0x28000
│ 0x0000f0b0 21201191 add x1, x1, 0x448 ; 0x28448 ; "Hello from C++"; section..rodata
0x0000f0b4a88300d1 sub x8, var_20h
│ 0x0000f0b8 e00308aa mov x0, x8
│ 0x0000f0bce80f00f9strx8, [var_18h]
│ 0x0000f0c094ffff97blfcn.0000ef10
│ 0x0000f0c4a0835df8ldurx0, [var_28h]
│ 0x0000f0c8e80f40f9ldrx8, [var_18h]; aav.0x00000018
│ ; [0x18:4]=0xf050 pc ; "P\xf0"
│ 0x0000f0cce00b00f9strx0, [var_10h]
0x0000f0d0e00308aa mov x0, x8 ; int64_targ1
│ 0x0000f0d440000094 blfcn.0000f1d4; fcn.0000f1d4(0x0)
│ 0x0000f0d8e80b40f9ldrx8, [var_10h]; aav.0x00000010
│ ; [0x10:4]=0xb70003
│ 0x0000f0dce00700f9strx0, [var_8h]
│ 0x0000f0e0 e00308aa mov x0, x8
│ 0x0000f0e4e10740f9ldrx1, [var_8h]; aav.0x00000008
│ ; [0x8:4]=0
│ 0x0000f0e8e6feff97blfcn.0000ec80
│ 0x0000f0ece00300f9strx0, [sp]
│ ┌─< 0x0000f0f001000014b 0xf0f4
│ │ ; CODE XREF fromsym.Java_com_example_myapplication_MainActivity_stringFromJNI @ 0xf0f0
│ └─> 0x0000f0f4a08300d1 sub x0, var_20h
│ 0x0000f0f82affff97blfcn.0000eda0
│ 0x0000f0fc48d03bd5 mrs x8, tpidr_el0
│ 0x0000f100081540f9ldrx8, [x8, 0x28]; aav.0x00000028
│ ; [0x28:4]=0x341f8
│ 0x0000f104a9835ff8ldurx9, [var_bp_8h]
│ 0x0000f108 080109eb subs x8, x8, x9
│ ┌─< 0x0000f10ca1010054 b.ne0xf140; likely
│ ┌──< 0x0000f11001000014b 0xf114
│ ││ ; CODE XREF fromsym.Java_com_example_myapplication_MainActivity_stringFromJNI @ 0xf110
│ └──> 0x0000f114e00340f9ldrx0, [sp]
│ │ 0x0000f118fd7b46a9ldpx29, x30, [var_60h]
│ │ 0x0000f11c ffc30191 add sp, sp, 0x70 ; 0x178000
│ │ 0x0000f120 c0035fd6 ret
..
││ ; CODE XREF fromunk @
│ │ ; CODE XREF fromsym.Java_com_example_myapplication_MainActivity_stringFromJNI @ 0xf10c
└ └─> 0x0000f14014ffff97 bl sym.imp.__stack_chk_fail ; void__stack_chk_fail(void) ; sym.std::__ndk1::basic_string_char__std::__ndk1::char_traits_char___std::__ndk1::allocator_char___::basic_string_decltype_nullptr___char_const
└ ; void__stack_chk_fail(void)

ARM处理器用到的指令集分为 ARM 和 THUMB 两种。ARM指令长度固定为32bit,THUMB指令长度固定为16bit。上面代码的所有指令都是32位的,所以都是ARM指令。

代码分析

1. 开辟堆栈空间`sub sp, sp, 0x70`
2. 存在一对数据 `stp x29, x30, [var_60h]`
3.x29与[var_60h]相加 `add x29, var_60h`
4.保存状态tpidr_el0到x8寄存器 `mrs x8, tpidr_el0`
5. 将x8+0x28内存中的值加载到x8寄存器 `ldr x8, [x8, 0x28] `
6. 将x8寄存器保存起来,保存到var_bp_8h指向的内存中
7. 将第一个参数x0保存到var_bp_8h指向的内存中
8. 将第二个参数x1保存到var_0h_2指向的内存中
9. 将x1寄存器指向只读字符串"Hello from C++"
10. 将x8寄存器与var_20h相减法,把结果赋值给x8寄存器
11. 将x8赋值给x0
12. 将x8寄存器保存到var_18h指向的内存中
13. 调用函数fcn.0000ef10,没有处理返回值,暂时不用关注
14. 将第一个参数给x0寄存器,上面函数的返回值没有什么实际的用途呀,这里直接把x0寄存器覆盖了
15. 恢复x8寄存器的值
16. 将x0寄存器的值保存起来,放到var_10h指向的内存中
17. 将x8寄存器赋值给x0
18. 调用fcn.0000f1d4
19. 恢复x8寄存器的值
20. 将上述函数的返回值x0保存到var_8h指向的内存中
21. 将x8赋值给x0
22. 将var_8h中的值赋值给x1寄存器
23. 调用函数fcn.0000ec80
24. 将返回值x0保存到sp指向的内存中
25. 跳转到0xf0f4,也就是下一条指令
26. 将x0与var_20h相减,结果赋值给x0
27. 调用fcn.0000eda0
28. 读取tpidr_el0状态到x8寄存器
29. 将x8+0x28内存中的值加载到x8寄存器 `ldr x8, [x8, 0x28] `
30. 加载var_bp_8h中的值到x9寄存器
31. 判断x8的值是否发生变化,如果发生变化,走失败流程,这个应该是使用cookie技术防止堆栈攻击的技术
32. 如果没有发生变化,跳转到0xf114
33. 将sp内存中的值加载到x0寄存器中,用作返回值
34. 恢复x29,x30的值
35. 恢复堆栈
36. ret返回

通过上面的分析,可知最终的返回值是通过调用函数fcn.0000ec80。这个函数其中的一个参数是字符串处理函数fcn.0000f1d4的返回值。另一个参数是x8的值,通过分析可知x8寄存器正是函数Java_com_example_myapplication_MainActivity_stringFromJNI的第一个参数,类型是JNIEnv。由此推断fcn.0000ec80很可能是JNIEnv相关函数。

查看fcn.0000ec80

; CALLXREF fromsym.Java_com_example_myapplication_MainActivity_stringFromJNI @ 0xf0e8
┌ 16: fcn.0000ec80();
│ 0x0000ec80 300100d0 adrp x16, obj.typeinfo_for_decltype_nullptr__const ; 0x34000
│ 0x0000ec84 119a46f9 ldr x17, [x16, 0xd30] ; [0x34d30:4]=0xeb50 fcn.0000eb50 ; "P\xeb"
│ 0x0000ec88 10c23491 add x16, x16, 0xd30 ; 0x34d30 ; "P\xeb"
└ 0x0000ec8c 20021fd6 br x17

跳转到0x34d30,通过pd命令查看相应的内容。很显然这个函数就是NewStringUTF。这涉及到got表和plt表,在深入理解GOT覆写技术 中对GOT表进行了介绍。在后续文章中还会重点讲解android got表。在这里插入图片描述使用pdg命令decompiler一下更加直观。在这里插入图片描述

查看fcn.0000ef10

; CALLXREF fromsym.Java_com_example_myapplication_MainActivity_stringFromJNI @ 0xf0c0
┌ 16: fcn.0000ef10();
│ 0x0000ef10 300100d0 adrp x16, obj.typeinfo_for_decltype_nullptr__const ; 0x34000
│ 0x0000ef14 113e47f9 ldr x17, [x16, 0xe78] ; [0x34e78:4]=0xeb50 fcn.0000eb50 ; "P\xeb"
│ 0x0000ef18 10e23991 add x16, x16, 0xe78 ; 0x34e78 ; "P\xeb"
└ 0x0000ef1c 20021fd6 br x17

最终定位到basic_string<decltype(nullptr)>(char const*)。在这里插入图片描述**查看basic_string函数信息,可知有2个参数,其中x0也就是参数1是x8寄存器的值,x1指向字符串"Hello from C++"**。这个函数的功能相当于string.c_str()。c_str()函数返回一个指向正规C字符串的指针常量, 内容与本string串相同。

[0x0000eb50]&gt; pdf@ 0x0000f144
; UNKNOWN XREF fromaav.0x00001f0c @ +0x664
88: sym.std::__ndk1::basic_string_char__std::__ndk1::char_traits_char___std::__ndk1::allocator_char___::basic_string_decltype_nullptr___char_const (int64_targ1, int64_targ2);
│ ; var int64_tvar_10h @ x29-0x10
│ ; var int64_tvar_8h @ x29-0x8
│ ; var int64_tvar_sp_8h @ sp+0x8
│ ; var int64_tvar_sp_10h @ sp+0x10
│ ; varchar*var_18h @ sp+0x18
│ ; var int64_tvar_30h @ sp+0x30
│ ; var int64_tvar_30h_2 @ sp+0x38
│ ; arg int64_targ1 @ x0
│ ; arg int64_targ2 @ x1
0x0000f144ff0301d1 sub sp, sp, 0x40; std::__ndk1::basic_string&lt;char, std::__ndk1::char_traits&lt;char&gt;, std::__ndk1::allocator&lt;char&gt; &gt;::basic_string&lt;decltype(nullptr)&gt;(charconst*)
│ 0x0000f148fd7b03a9stpx29, x30, [var_30h]
│ 0x0000f14c fdc30091 add x29, var_30h
│ 0x0000f150a0831ff8sturx0, [var_8h]; arg1
│ 0x0000f154a1031ff8sturx1, [var_10h]; arg2
│ 0x0000f158a8835ff8ldurx8, [var_8h]
│ 0x0000f15c e00308aa mov x0, x8
│ 0x0000f160e80f00f9strx8, [var_18h]
│ 0x0000f164cbfeff97blfcn.0000ec90
│ 0x0000f168a1035ff8ldurx1, [var_10h]
│ 0x0000f16ca0035ff8ldurx0, [var_10h]
│ 0x0000f170e10b00f9strx1, [var_sp_10h]
│ 0x0000f1749fffff97blfcn.0000eff0
│ 0x0000f178e80f40f9ldrx8, [var_18h]; aav.0x00000018
│ ; [0x18:4]=0xf050 pc ; "P\xf0"
│ 0x0000f17ce00700f9strx0, [var_sp_8h]
│ 0x0000f180 e00308aa mov x0, x8
│ 0x0000f184e10b40f9ldrx1, [var_sp_10h]; aav.0x00000010
│ ; [0x10:4]=0xb70003
│ 0x0000f188e20740f9ldrx2, [var_sp_8h]; aav.0x00000008
│ ; [0x8:4]=0
│ 0x0000f18ca1ffff97blfcn.0000f010
│ 0x0000f190fd7b43a9ldpx29, x30, [var_30h]
│ 0x0000f194 ff030191 add sp, sp, 0x40 ; 0x178000
└ 0x0000f198 c0035fd6 ret

查看fcn.0000f1d4

; CALLXREF fromsym.Java_com_example_myapplication_MainActivity_stringFromJNI @ 0xf0d4
36: fcn.0000f1d4 (char*arg1);
│ ; varchar*var_8h @ sp+0x8
│ ; var int64_tvar_10h @ sp+0x10
│ ; var int64_tvar_10h_2 @ sp+0x18
│ ; arg char*arg1 @ x0
0x0000f1d4ff8300d1 sub sp, sp, 0x20
│ 0x0000f1d8fd7b01a9stpx29, x30, [var_10h]
│ 0x0000f1dc fd430091 add x29, var_10h
│ 0x0000f1e0e00700f9strx0, [var_8h]; arg1
│ 0x0000f1e4e00740f9ldrx0, [var_8h]; aav.0x00000008
│ ; [0x8:4]=0; int64_targ1
│ 0x0000f1e8d8020094blfcn.0000fd48
│ 0x0000f1ecfd7b41a9ldpx29, x30, [var_10h]
│ 0x0000f1f0 ff830091 add sp, sp, 0x20 ; 0x178000
└ 0x0000f1f4 c0035fd6 ret

通过分析大概推测是混淆函数,没什么实际的作用,从上面的代码中可以能看出x0的值也就是返回值没有发生任何的改变

查看fcn.0000eda0

; CALLXREF fromsym.Java_com_example_myapplication_MainActivity_stringFromJNI @ 0xf0f8
; CALLXREF fromsym.Java_com_example_myapplication_MainActivity_stringFromJNI @ +0xa4
┌ 16: fcn.0000eda0();
│ 0x0000eda0 300100d0 adrp x16, obj.typeinfo_for_decltype_nullptr__const ; 0x34000
│ 0x0000eda4 11e246f9 ldr x17, [x16, 0xdc0] ; [0x34dc0:4]=0xeb50 fcn.0000eb50 ; "P\xeb"
│ 0x0000eda8 10023791 add x16, x16, 0xdc0 ; 0x34dc0 ; "P\xeb"
└ 0x0000edac 20021fd6 br x17

最终定位到~basic_string()方法。在这里插入图片描述

IDA查看

通过下图可知与上面的分析基本一致。在这里插入图片描述

ARM指令和寄存器

ARM指令

MRS指令的格式为: MRS{条件} 通用寄存器,程序状态寄存器(CPSR或SPSR) MRS指令用亍将程序状态寄存器的内容传送到通用寄存器中。

STUR (SIMD&FP): Store SIMD&FP register (unscaled offset).

跳转指令: B ;跳转指令,可带条件跳转与cmp配合使用 BL ;带返回的跳转指令, 返回地址保存到LR(X30) BLR ; 带返回的跳转指令,跳转到指令后边跟随寄存器中保存的地址(例:blr x8 ;跳转到x8保存的地址中去执行),并且把当前PC+4回写到X30。 BR: 与BLR差别在于有无回写。 RET ;子程序返回指令,返回地址默认保存在LR(X30)

ADRP Xd, lable(Address Page) 符号扩展一个21位的offset, 向左移动12位 PC的值的低12位 清零, 然后 把 这两者相加, 结果写入到Xd寄存器 用来得到一块含有 lable的4KB对齐 内存区域的base地址 (也就是说lable所在的地址,一定落在这个4KB的内存区域里, 指令助记符里Page也就是这个意思), 可用来寻址 +/- 4GB的范围。

ARM寄存器

ARM64 有34个寄存器,包括31个通用寄存器、SP、PC、CPSR。 寄存器 | 位数 | 描述 | -------- | ----- | ----- x0-x30 | 64| 通用寄存器,如果有需要可以当做32bit使用:W0-W30 FP(X29) | 64| 保存栈帧地址(栈底指针) LR(x30) | 64|通常称X30为程序链接寄存器,保存子程序结束后需要执行的下一条指令 SP | 64 |保存栈指针,使用 SP/WSP来进行对SP寄存器的访问。 PC |64|程序计数器,俗称PC指针,总是指向即将要执行的下一条指令,在arm64中,软件是不能改写PC寄存器的。 CPSR |64 |状态寄存器

程序状态寄存器

CPSR (Current Program Status Register),各个bit的含义如下图:在这里插入图片描述SPSR (Saved Program Status Register),在异常状态下使用,当发生异常时,会把CPSR的内容写入SPSR, 等异常恢复之后,又会把SPSR写到CPSR中。

公众号

更多内容,欢迎关注我的微信公众号:无情剑客。在这里插入图片描述


# 黑客 # 系统安全 # 漏洞分析 # Android
本文为 独立观点,未经授权禁止转载。
如需授权、对文章有疑问或需删除稿件,请联系 FreeBuf 客服小蜜蜂(微信:freebee1024)
被以下专辑收录,发现更多精彩内容
+ 收入我的专辑
+ 加入我的收藏
相关推荐
  • 0 文章数
  • 0 关注者