作者:FloatingGuy 转载请注明出处:https://floatingguy.github.io/


Title:【技术分享】Cocoa应用逆向工程简介
Url: http://bobao.360.cn/learning/detail/3601.html

Core Skill:
逆向分析 Object-C APP基础。

  1. 如何使用 IDA 逆向Object-C编译得到的 C 代码
  2. Cocoa App 的代码入口点
  3. 分析界面

FLARE团队将会介绍两款小工具

我们会解释Objective-C的Runtime特性如何使得代码分析在诸如IDA Pro工具中变得复杂

如何在一份Cocoa应用程序的代码中找到开始分析的入口点。

Objective-C

在编译时,Objective-C编写的程序会被转化为C,
由于Objective-C的动态特性,method一般不是被直接调用的。而是通过一个发给对象的消息。一个method的名字称为一个selector,真正执行的函数被称为一个实现(implementation)。
在Objective-C应用中最常用的发送消息方式就是objc_msgSend函数。

1
2
3
4
5
struct __objc2_meth {
SEL NAME; // selector
const char* types; //type encoding
IMP imp; //implementation
}

该结构的第一个值是指向method的selector。该值的cross-references中的一项将我们带到了可执行文件__objc_selrefs 节,这个节中你可以找到selector reference。跟随该selector reference的cross-references我们可以在代码中找到任何selector被使用的位置。该结构的第三个值指向selector的实现,就是我们想要分析的函数。剩下要做的就是利用这个数据生成cross-references。

静态分析 OC 对应的汇编

1
2
3
4
5
6
7
8
9
__text:0000287E ; 32: v9 = objc_msgSend(&OBJC_CLASS___AppDelegate, "class");
__text:0000287E MOV R0, #(selRef_class - 0x2892)
__text:00002886 MOV R2, #(classRef_AppDelegate - 0x2894)
__text:0000288E ADD R0, PC ; selRef_class
__text:00002890 ADD R2, PC ; classRef_AppDelegate
__text:00002892 LDR R1, [R0] ; "class" //select
__text:00002894 LDR R0, [R2] ;
__text:00002896 BLX _objc_msgSend
_OBJC_CLASS_$_AppDelegate

1
2
3
4
5
6
7
8
9
10
__objc_selrefs:000057B8 AREA __objc_selrefs, DATA
__objc_selrefs:000057B8 ; ORG 0x57B8
__objc_selrefs:000057B8 selRef_class DCD sel_class ; DATA XREF: start+26o
__objc_selrefs:000057B8 ; start+36o ...
__objc_selrefs:000057B8 ; "class"
__objc_selrefs:000057BC selRef_alloc DCD sel_alloc ; DATA XREF: -[AppDelegate application:didFinishLaunchingWithOptions:]+1Eo
__objc_selrefs:000057BC ; -[AppDelegate application:didFinishLaunchingWithOptions:]+36o ...
__objc_selrefs:000057BC ; "alloc"
__objc_selrefs:000057C0 selRef_mainScreen DCD sel_mainScreen ; DATA XREF: -[AppDelegate application:didFinishLaunchingWithOptions:]+70o
__objc_selrefs:000057C0 ; -[AppDel

text 段跟踪到了objc_selrefs段。发现 在objc_selrefs 段中,key-value 名称的对应关系是 selRef_xxx - sel_xxx。

我们在 __objc_const 段中查看 select列表, 只有部分 select 名称中包含 sel 前缀。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
__objc_const:00005098 _OBJC_INSTANCE_METHODS_NSObject __objc2_meth_list <0xC, 0x13>
__objc_const:00005098 ; DATA XREF: __data:_OBJC_PROTOCOL_$_NSObjecto
__objc_const:000050A0 __objc2_meth <aIsequal, aC12048, 0> ; "isEqual:"
__objc_const:000050AC __objc2_meth <aHash, aI804, 0> ; "hash"
__objc_const:000050B8 __objc2_meth <aSuperclass, a804_0, 0> ; "superclass"
__objc_const:000050C4 __objc2_meth <sel_class, a804_0, 0> ; "class"
__objc_const:000050D0 __objc2_meth <aSelf, a804, 0> ; "self"
__objc_const:000050DC __objc2_meth <aZone, a_nszone804, 0> ; "zone"
__objc_const:000050E8 __objc2_meth <aPerformselecto, a12048, 0> ; "performSelector:"
__objc_const:000050F4 __objc2_meth <aPerformselec_0, a1604812, 0> ; "performSelector:withObject:"
__objc_const:00005100 __objc2_meth <aPerformselec_1, a200481216_0, 0> ; "performSelector:withObject:withObject:"
__objc_const:0000510C __objc2_meth <aIsproxy, aC804, 0> ; "isProxy"
__objc_const:00005118 __objc2_meth <aIskindofclass, aC12048_0, 0> ; "isKindOfClass:"
__objc_const:00005124 __objc2_meth <aIsmemberofclas, aC12048_0, 0> ; "isMemberOfClass:"
__objc_const:00005130 __objc2_meth <aConformstoprot, aC12048, 0> ; "conformsToProtocol:"
__objc_const:0000513C __objc2_meth <sel_respondsToSelector_, aC12048_1, 0> ; "respondsToSelector:"
__objc_const:00005148 __objc2_meth <aRetain, a804, 0> ; "retain"
__objc_const:00005154 __objc2_meth <aRelease, aVv804, 0> ; "release"
__objc_const:00005160 __objc2_meth <aAutorelease, a804, 0> ; "autorelease"
__objc_const:0000516C __objc2_meth <aRetaincount, aI804, 0> ; "retainCount"
__objc_const:00005178 __objc2_meth <aDescription, a804, 0> ; "description"
__objc_const:00005184 _OBJC_INSTANCE_METHODS_NSObject_1 __objc2_meth_list <0xC, 1>

额,看不懂 Mach-O的各种段的含义,需要 正向开发 和分析 Mach-O文件格式了。。。
rev-ios

继续。。。

objc2_xrefs_helper.py 脚本针对可执行文件中的每一个定义的Objective-C method都会进行处理
这个工具有一个显著的缺点,如果几个类使用同一个名字来定义method,那么只会有一个selector在可执行文件中显示。现在,该工具会忽略这些不明确的selector。

Cocoa应用-从哪里开始着手?

整体来说,NSApplicationMain执行三个重要步骤:构建NSApplication对象,加载主要的storyboard或nib文件,开始事件循环。NSApplication对象在程序运行时扮演重要的事件和通知协调者的角色。

NSApplication对象可以定位的一个重要的通知就是NSApplicationDidFinishLaunchingNotification。这个通知用来让程序员可以在合适的时候运行应用特殊的初始化代码。为了处理这个通知,应用程序需要指定根据NSApplicationDelegate协议来指定一个委托类。在这个协议中包含初始化代码的对应method就是applicationDidFinishLaunching.Xcode默认为你创建这个委托类,名叫AppDelegate。

那么最好的寻找大多数Cocoa应用的初始化代码的地方就是applicationDidFinishLaunching

接口构建器(Interface Builder)和Nib文件

界面生成器
nib_parse.py使用ccl_bplist来解码和反序列化一个nib文件,打印出里面定义的连接关系列表。对于每个连接,它将会打印出连接的标签(一般是一个方法或者变量名字),源对象的类,目标对象的类。每一个对象都被NSKeyedArchiver编码,并分配一个唯一的数字标识符,该标识符输出在圆括号里面。对于某些GUI组件还会一起打印出文本信息,比如按键标签,文本信息输出在方括号中。