首页
社区
课程
招聘
5
[原创]逆向iOS SDK -- “添加本地通知”的流程分析
发表于: 2013-6-30 00:54 11328

[原创]逆向iOS SDK -- “添加本地通知”的流程分析

2013-6-30 00:54
11328

观点:

    代码面前没有秘密


添加通知的 Demo 代码

- (void)scheduleOneLocalNotification {
    [[UIApplication sharedApplication] cancelAllLocalNotifications];
    UILocalNotification *localNotification = [[UILocalNotification alloc] init];
    localNotification.alertBody = @"Proteas";
    localNotification.fireDate = [[NSDate date] dateByAddingTimeInterval:300000];
    [[UIApplication sharedApplication] scheduleLocalNotification:localNotification];
    [localNotification release]; localNotification = nil;
}

问题

参考如上的 Demo,相应的系统API为:-[UIApplication scheduleLocalNotification:]
可以看到添加本地通知相对简单,但是我们要多问几个为什么:
1、接口背后发生了什么?
2、本地通知有64个限制,如何控制的?
3、... ...

Reverse UIKit

UIApplication 是UIKit中的类,所以我们首先逆向 UIKit。
UIKit 的路径为:
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS6.1.sdk/System/Library/Frameworks/UIKit.framework
用 Hopper Disassembler(or IDA) 打开这个库,Hopper 会开始反汇编、分析这个 MachO,待分析完毕后,可以看到如下界面:



scheduleLocalNotification的实现
这时我们查找 scheduleLocalNotification 的实现,在 Labels 下面的搜索框中输入 scheduleLocalNotification,Hopper 会查找出相关的实现,如下图:

 
在搜索出来的结果中点击第一个条目,右边窗口中会定位到实现的起始部分,如下图:

可以看到 scheduleLocalNotification: 的实现比较简单,只有 9 行汇编代码。
 
 
我们来分析下这几行代码:

1 movw  r1, #0xf8c4
2 movt  r1, #0x40   ;r1 = 0x40F8C4
3 movw  r0, #0xc7be
4 movt  r0, #0x41   ;r0 = 0x41C7BE
5 add   r1, pc       ;r1 = 0x5e5ec8
6 add   r0, pc       ;r0 = 0x5f2dc4
7 ldr   r1, [r1]     ;@selector(scheduleLocalNotification:),a = *a
8 ldr   r0, [r0]     ;@bind__OBJC_CLASS_$_SBSLocalNotificationClient
9 b.w   _objc_msgSend$shim
 
1、Hopper 使用的 Intel 系列的汇编语法:“目的”在左,“源”在右。
2、ARM没有直接加载32位立即数的指令,而是使用movw与movt。
3、Call Frame
上面几行代码的功能是:call + [SBSLocalNotificationClient scheduleLocalNotification:]。
当读到这段代码的时候,有个疑问:0x40F8C4 是如何得到的?
在说明这个问题之前需要先说下 MachO 文件的格式,如下图:

 
Objective-C 被编译、链接后,代码、类、方法、类与方法间关系被放到不同 Section 中,也就是说:
上面的代码与scheduleLocalNotification: (selector)在不同的 Section 中,这样就可以计算地址之间的差值了:
0x40F8C4 = (CodeSectionStartAddress + Offset)
- (SelfRefSectionStartAddress + Offset)
因为 ASLR(Address Space Layout Randomization) 的存在,Section 的开始地址并不是固定的,也就是说差值并不总是0x40F8C4。

Reverse SpringBoardServices

 scheduleLocalNotification的实现
搜索 SBSLocalNotificationClient,可以发现它是 SpringBoardServices中的类,
现在我们用同样的方法逆向 SpringBoardServices 库,如下图:


 
代码如下:
push {r4, r7, lr}
add   r7, sp, #0x4
sub   sp, #0x8
movw  r1, #0x875c
mov   r4, r0
movt  r1, #0x0
movw  r0, #0x8906
movt  r0, #0x0
add   r1, pc ; 0x13194
add   r0, pc ; 0x13340
ldr   r1, [r1] ; @selector(arrayWithObject:)
ldr   r0, [r0] ; @bind__OBJC_CLASS_$_NSArray
blx   imp___picsymbolstub4__objc_msgSend
mov   r2, r0
movw  r0, #0x8748
movt  r0, #0x0
movs  r3, #0x0
add   r0, pc ; 0x13198
ldr   r1, [r0];@selector(_scheduleLocalNotifications:cancel:replace:optionalBundleIdentifier:)
movs  r0, #0x0
str   r0, [sp]
str   r0, [sp, #0x4]
mov   r0, r4
blx   imp___picsymbolstub4__objc_msgSend
add   sp, #0x8
pop   {r4, r7, pc}
 
上面是使用 Hopper 得到的汇编,
但是使用 GDB 调试程序,并进行反汇编后,发现两者不一致,
 GDB 给出相对简单,此处以 GDB 为准。



状态说明:
    r0 = SBSLocalNotificationClient
    r1 = “scheduleLocalNotification:”
    r2 = UILocalNotification Instance



 

代码:
mov    r4, r0        ; r4 = SBSLocalNotificationClient
movw   r0, #34450     ;
movt   r0, #3577     ; movw, movt 加载32位立即数
movw   r1, #34344    ;
movt   r1, #3577     ; 同上
movw   r3, #34612    ;
movt   r3, #3577     ; 同上
add    r1, pc        ; 惯用法
add    r0, pc        ; 同上
add    r3, pc        ; 同上
ldr    r1, [r1, #0] ; r1 = "arrayWithObject:"
ldr    r5, [r0, #0] ; r5 ="_scheduleLocalNotifications:cancel:replace:optionalBundleIdentifier:"
ldr    r0, [r3, #0] ; r0 = NSArray
blx    0x3058faa4    ; [NSArray arrayWithObject:UILocalNotification]
movs   r3, #0        ; r3 = 0, movs 影响标志位的 zero 位, 0--->1
mov    r1, r5        ; r1 ="_scheduleLocalNotifications:cancel:replace:optionalBundleIdentifier:"
mov    r2, r0        ; r2 = UILocalNotifications
mov    r0, r4        ; r4 = SBSLocalNotificationClient
str    r3, [sp, #0] ; 将 sp 指向的内存空间清零
str    r3, [sp, #4] ; 将 sp + 4将 sp 指向的内存空间清零
blx     0x3058faa4    ; 调用
;_scheduleLocalNotifications:UILocalNotifications
;                     cancel:NO
;                    replace:NO
;    optionalBundleIdentifier:nil
 
; 平衡运行栈的代码参考 Hopper 给出的反汇编
 
结论:
       +[SBSLocalNotificationClient scheduleLocalNotification:]调用:
   +[SBSLocalNotificationClient
_scheduleLocalNotifications:本地通知实例数组
  cancel:NO
 replace:NO
optionalBundleIdentifier:nil]
 

_scheduleLocalNotifications......的实现
Hopper 反汇编:

GDB反汇编代码:
 
push       {r4, r7, lr}         ;
add        r7, sp, #4           ;
sub        sp, #12              ;
movw       r1, #34510 ; 0x86ce   ;
ldr.w      r12, [r7, #8]            ;
movt       r1, #3577             ; 0xdf9
ldr.w      lr, [r7, #12]            ;
add        r1, pc               ;
movs       r4, #0               ;
ldr        r1, [r1, #0]         ;
stmia.w    sp, {r12, lr}        ;
str        r4, [sp, #8]         ;
blx        0x3058faa4 
add        sp, #12              ;
pop        {r4, r7, pc}         ;
nop
 
这里主要是个参数值问题,不进行逐行分析了,
可以用调试得到结论,
在进行实际的调用前(红色代码),设置断点,打印出参数值:


 
得到相关参数:
       r0 = SBSLocalNotificationClient
       r1 = "_scheduleLocalNotifications:cancel:replace:optionalBundleIdentifier:waitUntilDone:"
       r2 = 通知数组
       r3 = 0, cancel
       *(sp) = 0, waitUntilDone
       *(sp + 4) = 0, optionalBundleIdentifier
       *(sp +8) = 0, replace
结论:
       +[SBSLocalNotificationClient
_scheduleLocalNotifications:本地通知实例数组
  cancel:NO
 replace:NO
optionalBundleIdentifier:nil]
调用:
+[SBSLocalNotificationClient
_scheduleLocalNotifications:本地通知实例数组
  cancel:NO
 replace:NO
optionalBundleIdentifier:nil
  waitUntilDone:NO]
 

_scheduleLocalNotifications......waitUntilDone的实现
汇编代码,如下:

; Begin
PUSH   {R4-R7,LR}
ADD     R7, SP, #0xC ; R7此时指向栈参数的开始地址,参数入栈顺序:从右到左
PUSH.W {R8,R10,R11} ; 保存寄存器值,后续会进行修改
SUB     SP, SP, #8 ; 开辟 sizeof(int) * 2的栈空间
MOVW    R1, #(:lower16:(selRef_archivedDataWithRootObject_ - 0xA930))
MOV     R8, R3  ; R8 = shouldCancle
MOVT.W  R1, #(:upper16:(selRef_archivedDataWithRootObject_ - 0xA930))
MOV      R0, #(classRef_NSKeyedArchiver - 0xA932) ;classRef_NSKeyedArchiver
ADD      R1, PC ; selRef_archivedDataWithRootObject_
ADD      R0, PC ; classRef_NSKeyedArchiver
LDR      R1, [R1] ; "archivedDataWithRootObject:"
LDR      R0, [R0] ; _OBJC_CLASS_$_NSKeyedArchiver
BLX      _objc_msgSend ; R2 = Notifications
MOV      R4, R0  ; R0 = NSData*
LDR.W    R11, [R7,#shouldReplace] ; R11 = shouldReplace
LDR.W    R10, [R7,#bundleIdentifier] ; R10 = bundleIdentifier
CMP      R4, #0  ; check if NSData* is nil
BNE      loc_A94C ; if NSData* != nil
MOVS     R5, #0  ; if (NSData* == nil) R5 = 0
MOV      R6, R5  ; R6 = 0
B        loc_A974
; -----------------------------------------
loc_A94C:
MOV      R0, #(selRef_bytes - 0xA958) ; selRef_bytes ; R0 = NSData*
ADD      R0, PC ; selRef_bytes
LDR      R1, [R0] ; "bytes"
MOV      R0, R4  ; R0 = NSData*
BLX      _objc_msgSend ; [NSData* bytes]
MOV      R5, R0  ; R5 = void* of NSData
MOV      R0, #(selRef_length - 0xA96C) ; selRef_length
ADD      R0, PC ; selRef_length
LDR      R1, [R0] ; "length"
MOV      R0, R4
BLX      _objc_msgSend ; [NSData* length]
MOV      R6, R0  ; R6 = length of NSData
; -----------------------------------------
loc_A974:
BL       _SBSSpringBoardServerPort
MOV      R4, R0  ; R4 = R0 = port number of server
MOV      R0, #(unk_F2DF - 0xA98A)
TST.W    R11, #0xFF
ADD      R0, PC
BEQ      loc_A9C0 ; check if bundleIdentifier is nil
CMP.W    R10, #0
BEQ      loc_A9A2 ; R1 = should wait until done
MOV      R0, #(selRef_UTF8String - 0xA99C) ; selRef_UTF8String
ADD      R0, PC ; selRef_UTF8String
LDR      R1, [R0] ; "UTF8String"
MOV      R0, R10
BLX      _objc_msgSend
; -----------------------------------------
loc_A9A2:
LDR      R1, [R7,#shouldWait] ; R1 = should wait until done
MOV      R2, R6
UXTB     R3, R1
MOV      R1, R5  ; void * of NSData
STR      R3, [SP,#0x20+var_20]
UXTB.W  R3, R8
STR      R0, [SP,#0x20+var_1C]
MOV      R0, R4  ; port number of the server
BL       _SBScheduleLocalNotificationsBlocking
; -----------------------------------------
loc_A9B8:
ADD      SP, SP, #8
POP.W    {R8,R10,R11}
POP       {R4-R7,PC}
; -----------------------------------------
loc_A9C0:
CMP.W     R10, #0 ; check if bundleIdentifier is nil
BEQ       loc_A9D8 ; R0 points to the UTF8String of appbundleIdentifier
MOV       R0, #(selRef_UTF8String - 0xA9D2) ; selRef_UTF8String
ADD       R0, PC ; selRef_UTF8String
LDR       R1, [R0] ; "UTF8String"
MOV       R0, R10
BLX       _objc_msgSend
; -----------------------------------------
loc_A9D8:
LDR       R1, [R7,#shouldWait] ; R0 points to the UTF8String of appbundleIdentifier
MOV       R2, R6
UXTB      R3, R1
MOV       R1, R5  ; void * of Notification NSData
STR       R3, [SP,#0x20+var_20]
UXTB.W   R3, R8
STR       R0, [SP,#0x20+var_1C]
MOV       R0, R4  ; Port Number Of the XPC Server
BL        _SBScheduleLocalNotifications
B         loc_A9B8
; End
 
 
伪代码的实现,如下:

-(void)_scheduleLocalNotifications:(id)notifications
                             cancel:(BOOL)cancel
                            replace:(BOOL)replace
           optionalBundleIdentifier:(id)bundleID
                      waitUntilDone:(BOOL)wait
{
id data = [NSKeyedArchiver archivedDataWithRootObject:notifications];
    if (data != nil) {
        bytes = [data bytes];
        length = [data length];
    } else {
        bytes = 0;
        length = 0;
    }
    port = _SBSSpringBoardServerPort();
    if (wait == NO) {
        if (bundleID) {
            var_1C = [bundleID UTF8String];
        }
        var_20 = replace;
        r2 = length;
        r3 = cancel;
        SBScheduleLocalNotifications(/*int*/ port, /*src*/ bytes);
        return;
    }
    if (bundleID != nil) {
        var_1C = [bundleID UTF8String];
    }
    var_20 = replace;
    r2 = length;
    r3 = cancel;
    SBScheduleLocalNotificationsBlocking(/*int*/ port, /*src*/ bytes);
    return;
}
 
上述代码的主要功能为:将通知进行序列化,然后调用本期通知的服务。
 
后续有时间再分析 SpringBoard 的通知服务部分,
需要说明的是:在对 SpringBoard 进行逆向分析前,需要去除其 ASLR。


[招生]科锐逆向工程师培训(2025年3月11日实地,远程教学同时开班, 第52期)!

收藏
免费 5
支持
分享
赞赏记录
参与人
雪币
留言
时间
心游尘世外
为你点赞~
2024-5-31 05:28
QinBeast
为你点赞~
2024-5-31 05:20
飘零丶
为你点赞~
2024-4-3 00:36
shinratensei
为你点赞~
2024-2-14 00:18
PLEBFE
为你点赞~
2023-3-7 00:31
最新回复 (7)
雪    币: 154
活跃值: (91)
能力值: ( LV6,RANK:80 )
在线值:
发帖
回帖
粉丝
2
厉害!mark
2013-6-30 03:40
0
雪    币: 33
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
szr
3
怎么没人顶?支持LZ
2013-7-1 22:28
0
雪    币: 0
活跃值: (41)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
支持一下。。。。。、
2013-7-6 21:05
0
雪    币: 86
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
厉害啊,真心佩服,自己还有一大段路要走的
2013-7-9 14:20
0
雪    币: 86
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
5 add   r1, pc       ;r1 = 0x5e5ec8
其中r1的地址是怎么计算的呢?
2013-7-15 13:05
0
雪    币: 2558
活跃值: (4273)
能力值: ( LV13,RANK:540 )
在线值:
发帖
回帖
粉丝
7
学习,学习~~
2013-7-15 13:12
0
雪    币: 353
活跃值: (549)
能力值: ( LV7,RANK:100 )
在线值:
发帖
回帖
粉丝
8
R1 = R1 + PC
可以看下:http://www.cnblogs.com/Proteas/p/3161855.html
2013-7-16 00:49
0
游客
登录 | 注册 方可回帖
返回

账号登录
验证码登录

忘记密码?
没有账号?立即免费注册