前言

对于应用层开发人员而言,仅仅掌握Objective-C和系统框架即可较好的完成开发,但在涉及到应用加固、逆向分析等内容时仅有应用层开发技能就会显得非常的无力,因此掌握汇编对于突破iOS开发水平的瓶颈十分有效。

一个例子

以反调试为例,我们知道,通过调用ptrace函数可以阻止调试器依附。

  1. ptrace(31, 0, 0, 0)

这种方式能够被函数hook轻易破解,例如使用facebook的fishhook。为了防止函数被hook,我们可以将函数调用转为通过汇编发起系统调用,即使用下面的代码。

  1. mov x0, #31

  2. mov x1, #0

  3. mov x2, #0

  4. mov x3, #0

  5. mov x16, #26

  6. svc #0x80

其中x0-x3存储的为函数入参,x16存储的为函数编号,通过Apple提供的System Call Table 可以查出ptrace的编号为26,最后一句指令发起了系统调用。通过使用_asm_指令能够将汇编代码嵌入我们的函数中,构成反调试方法。

  1. // 使用inline方式将函数在调用处强制展开,防止被hook和追踪符号

  2. static __attribute__((always_inline)) void anti_debug() {

  3. // 判断是否是ARM64处理器指令集

  4. #ifdef __arm64__

  5. // volatile修饰符能够防止汇编指令被编译器忽略

  6. __asm__ __volatile__(

  7. "mov x0, #31\n"

  8. "mov x1, #0\n"

  9. "mov x2, #0\n"

  10. "mov x3, #0\n"

  11. "mov x16, #26\n"

  12. "svc #0x80\n"

  13. );

  14. #endif

  15. }

虽然上面的反调试机制并不完善,但是比直接调用ptrace要好上很多倍,从这一点来看,掌握汇编技能对于iOS应用安全和底层研究非常有利。

入门攻略

iOS设备主要使用的为ARM64汇编,因此本文主要介绍ARM64汇编的入门技巧。汇编入门最难的地方在于对栈的理解,汇编的所有指令操作都是围绕栈实现的,在汇编中,没有变量的概念,只有寄存器和内存。

汇编中的栈是由高地址向低地址生长的数据结构,sp指针永远指向栈顶,需要记住的是,在某位置进行存储时,是向高地址进行的,下面以一个简单的例子讲解汇编的栈操作。我们以一段简单的C代码为例。

  1. // hello.c

  2. #include <stdio.h>


  3. int test(int a, int b) {

  4. int res = a + b;

  5. return res;

  6. }


  7. int main() {

  8. int res = test(1, 2);

  9. return 0;

  10. }

使用clang可以将其编译为特定指令集的汇编代码,这里我们将其编译为ARM64指令集的汇编代码。

  1. clang -S -arch arm64 -isysroot `xcrun --sdk iphoneos --show-sdk-path` hello.c

完整的汇编代码如下。

  1. .section __TEXT,__text,regular,pure_instructions

  2. .ios_version_min 11, 2

  3. .globl _test

  4. .p2align 2

  5. _test:; @test

  6. ; BB#0:

  7. sub sp, sp, #16 ; =16

  8. str w0, [sp, #12]

  9. str w1, [sp, #8]

  10. ldr w0, [sp, #12]

  11. ldr w1, [sp, #8]

  12. add w0, w0, w1

  13. str w0, [sp, #4]

  14. ldr w0, [sp, #4]

  15. add sp, sp, #16 ; =16

  16. ret


  17. .globl _main

  18. .p2align 2

  19. _main: ; @main

  20. ; BB#0:

  21. sub sp, sp, #32 ; =32

  22. stp x29, x30, [sp, #16] ; 8-byte Folded Spill

  23. add x29, sp, #16 ; =16

  24. orr w0, wzr, #0x1

  25. orr w1, wzr, #0x2

  26. stur wzr, [x29, #-4]

  27. bl _test

  28. mov w1, #0

  29. str w0, [sp, #8]

  30. mov x0, x1

  31. ldp x29, x30, [sp, #16] ; 8-byte Folded Reload

  32. add sp, sp, #32 ; =32

  33. ret



  34. .subsections_via_symbols

本节我们只讨论栈操作,因此忽略main函数和printf调用部分,我们只看对test函数的调用,节选这一段汇编代码如下。

  1. sub sp, sp, #16 ; =16

  2. str w0, [sp, #12]

  3. str w1, [sp, #8]

  4. ldr w0, [sp, #12]

  5. ldr w1, [sp, #8]

  6. add w0, w0, w1

  7. str w0, [sp, #4]

  8. ldr w0, [sp, #4]

  9. add sp, sp, #16 ; =16

  10. ret

首先介绍一下基本指令和指令的学习方式,要查询某个指令如何使用,最好的方式是去查询ARM公司提供的官方文档,在官方文档页面可以直接搜索指令并查看用法和例程,本文会简单讲解上面的汇编代码中出现的指令。

sub用于对寄存器实施减法, suba,b,c等价于 a=b-c,在ARM汇编中,目的操作数一般出现最前方,例如 mov ra,rb 代表将rb寄存器的值复制到ra寄存器。add和sub同理,只是将减法变成了加法。

str和ldr是一对指令,str的全称是store register,即将寄存器的值存储到内存中,ldr的全称是load register,即将内存中的值读到寄存器,因此他们的第一个参数都是寄存器,第二个参数都是内存地址。[sp,#12] 代表 sp+12 这个地址,同理 [sp,#-12] 代表 sp-12 这个地址。注意这里的数字都是以字节为单位的偏移量,以 str w0,[sp,#12] 为例,w是4字节的寄存器,这个指令代表将w0寄存器的值存储在sp+12这个地址上,由于w0有4个字节,所以存储后会占据 sp+12~sp+16这个内存区域。

下面将分段讲解这段汇编代码,在编译器生成汇编时,首先会计算需要的栈空间大小,并利用sp指针向低地址开辟相应的空间,我们再来看一下test函数。

  1. int test(int a, int b) {

  2. int res = a + b;

  3. return res;

  4. }

这里涉及了3个int变量,分别是a、b、res,int变量占据4个字节,因此一共需要12个字节,但ARM64汇编为了提高访问效率要求按照16字节进行对齐,因此需要16byte的空间,也就是需要在栈上开辟16字节的空间,我们来看汇编的第一句,正是将sp指针下移16字节。

  1. sub sp, sp, #16

sp下移16后,留下了4个4字节的内存空格,共计16字节,我们继续看下面的句子。

  1. str w0, [sp, #12]

  2. str w1, [sp, #8]

这两句的含义是将w0存储在sp+12的格子中,w1存储在sp+8的格子中,上面的例子中提到 x0, x1等寄存器将顺序存放函数的入参,x0和w0是同一个寄存器的不同大小体现,x0为8字节,w0为x0的前4个字节,因此w0是函数的第一个入参a,w1是函数的第二个入参b,由于存储是从低地址到高地址的,所以a将占据 sp+12~sp+16,同理b将占据 sp+8~sp+12,则栈的结构变为下图。

按照“上帝视角”,接下来test函数应该将a和b相加,需要注意的是,只有寄存器才能参与运算,因此接下来的汇编代码又将变量的值从内存中读出,进行相加运算。

  1. ldr w0, [sp, #12]

  2. ldr w1, [sp, #8]

  3. add w0, w0, w1

由此可见先存储再读取后运算其实是多余的,这是没有进行编译优化的结果,学习不进行编译优化的汇编更能让我们理解其工作机制。

接下来的代码将w0存入了sp+4,也就是res变量的内存区域。

  1. str w0, [sp, #4]

接下来就要进行返回了,在例子中我们提到,函数的返回值一般存储在x0寄存器中返回,因此我们需要将res的值载入x0寄存器。

  1. ldr w0, [sp, #4]

这里之所以使用w寄存器,是因为int为4字节,这也就是类型转换时带来信息丢失的原因,例如从long到int的转换就类似于将x寄存器的值以w的形式进行存储。最后的代码为将栈还原,并返回到函数调用处继续向下执行。

  1. add sp, sp, #16

  2. ret

显然,经过这样的操作,栈被完全还原到了函数调用以前的样子,需要注意的细节是,栈空间中的内存单元并未被清空,这也就导致下一次使用低地址的栈时,未初始化单元的值是不确定的,这也就是局部变量不初始化值随机的根本原因。

通过上面的例子,我们对栈有了基本的认识,汇编的操作基本都是对栈进行的,只要理解了栈机制,只需要学习各种指令,即可掌握足够使用的汇编技能。

深入

在了解了栈以后,就可以看一些较为复杂的汇编片段来进行学习了,初级阶段可以尝试看着函数写汇编代码,高级阶段要求能够看着汇编还原成函数逻辑,本文仅仅介绍入门基础,下面推荐一些大牛的博客供大家深入学习汇编技能。

1.知兵的知乎专栏

2.刘坤的汇编入门文章

总结

掌握ARM汇编能够帮助开发者更好地了解编译器和CPU的工作原理,除了能够指导编码外,还能够扩宽视野,通过反编译分析一些闭源代码的逻辑或是进行一些安全加固,因此在汇编上付出时间是十分值得的。

参考资料


发布     👍 0 举报 写留言 🖊   
✋热门推荐
  • #许嵩#【36岁的许嵩“铁树开花”,老粉纷纷送祝福,女友竟比他小14岁】 是谁一开口,就能让全体90后瞬间泪奔? 没错,就是这个曾经占据了所有90后整个青春
  • 终于到了暂时安全的地方.叛军追来,在危急关头,是成朗,举起了摄像机,救了大家.“真相不会被掩盖,他不敢对我们所有人开枪”我想要是章宁还在,他夸成朗的时候,成朗一
  • “反正在我心里,李若彤才是最美的小龙女……”吃这种态度的粉丝很多,应该是很多人直接看的电视剧,可能确实没翻过《神雕侠侣》原著。有跟刘亦菲配戏的男演员采访时候说过
  • 都长到这么大了真的得明白说话的分寸了,不是你想说什么不经思考就能随便说出来的,有点眼力价,让人与人之间相处的更舒服点吧。结果我们班真有女生直接这么问我“你怎么一
  • 在旅行的世界里,满足永远只是咫尺之遥,渴望一直沾染着失望;那是一个浓缩的幻影,像一首诗。 20世纪70年代意大利一部电影《巧克力》说着火车如何连接欧洲,区隔
  • 有哪些微甜的情话? 1.不是在撩你,是真的想和你在一起,也不是一点点喜欢你,是好喜欢好喜欢你。 2.我真的是不太会表达的人,如果你感觉到我喜欢你,再乘个二十
  • Ta吃饭 你在玩Ta睡觉 你在玩Ta在看书 你还在玩所以你说 能 比 吗灿烂星空谁是真的英雄哪有人 会 随随便便 成 功 长大后才懂得朋友家人的重要性在这
  • 而DDG(X)方案看起来和中国的055大驱非常相似,一直被外界广泛批评为是055大驱的“私生子”要知道美国海军驱逐舰一直是引领全世界的设计,是世界先进驱逐舰的标
  • ”大棕熊说,“也许你担心我会把它捂坏了,不过我每天都要把它拿出来的,因为我要好好地看着它,一直看着,看着……不然我真不敢相信,我能这样幸运地拥有它。”“我同意,
  • 来源:阿斯报邯郸学步,东施效颦,是自古流传下来喻含深意的成语故事,因为刻意的去模仿别人,想让自己也成为像别人一样的人,而忽视了与人成长环境与经历背景的不同,以及
  • 在一个多小时的授课中,刘健用一个个生动鲜活的事例和自己的亲身体会,从如何成为有理想、敢担当、能吃苦、肯奋斗的新时代好青年四个方面,结合金山实际,为学生们带来了一
  • 在智能网联汽车区域,新区“科创+产业”的典型—北理工重庆创新中心,与上市公司雷科防务联合孵化企业重庆睿行电子,带来毫米波雷达、交通雷达等众多产品,将为智能网联汽
  • #肖战[超话]##肖战# xz#肖战我喜欢你# #肖战盛阳# #肖战时影# #肖战肖春生# ✨✨✨✨✨✨✨✨❣️肖战好帅,我喜欢肖战❣️见山是深情伟岸,❣️见海
  • 高烧不退被救护车拉走的小明,见到医生:#干货分享# “医生,我用了六个退热贴,脑瓜子还是烧的嗡嗡的” 发烧很常见,尤其是免疫力低下的成人和儿童,那退热贴有没
  • 该剧讲述了“源计划”实验室的核心研究员裘佳宁与周小山从相遇相知到彼此相爱以及在爱情和理想之间权衡选择的故事。该剧讲述了“源计划”实验室的核心研究员裘佳宁与周小山
  • “他们的需求是想做一些项目来维持,达到像明星一样光洁漂亮的皮肤。马湉是一位20岁的大学生,她是那种你在生活中遇到肯定觉得漂亮的女生:白皮肤、大眼睛,鼻头精致小巧
  • "还在为找不到医师挂号入口而烦恼吗?上爱优牙(小呈序)快速找对正畸医生免费预箹>>各城市正畸医生全部收录,执业年限也都清晰明了,按区域、医
  • 更多有关新加坡入境要求的信息,请参阅此处【对于所有旅客实行的强制性健康申报措施】所有旅客,无论来自哪个国家或地区,均须在抵达新加坡前的三天内在线提交电子健康申报
  • 基罗斯周二出席世盃记招,竟就被问到伊朗国内的骚乱,有英国记者问他是否还想代表这样的国家参加比赛,令他非常愤怒。面对不可完成的任务,穆里尼奥并没有气馁,希望创造奇
  • 本书以史实事件为干,以人物为线,以幽默为引,20多个主要政权交锋,100多权贵轮番登场,再现分分合合,这段历史挺乱,但是写得却挺精彩。作者不是平白直序,而是以人