观点:

代码面前没有秘密

添加通知的 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

表:ARM ABI Register Usage

Register

Brief

Preserved

Rules

r0

Argument and result

No

r0 and r1 are used for passing the first two arguments to functions, and returning the results of functions. If a function does not use them for a return value, they can take any value after a function.

r1

Argument and result

No

r2

Argument

No

r2 and r3 are used for passing the second two arguments to functions. There values after a function is called can be anything.

r3

Argument

No

lr

Return address

No

lr is the address to branch back to when a function is finished, but this does have to contain the same address after the function has finished.

sp

Stack pointer

Yes

sp is the stack pointer, described below. Its value must be the same after the function has finished.

 

上面几行代码的功能是: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 <dyld_stub_objc_msgSend>

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。

转载于:https://www.cnblogs.com/Proteas/p/3163240.html

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

  1. [原]逆向iOS SDK -- +[UIImage imageNamed:] 的实现

    汇编代码: ; Dump of assembler code for function +[UIImage imageNamed:] ; R0 = UIImage, R1 = "imageN ...

  2. [原]逆向iOS SDK -- _UIImageAtPath 的实现(SDK 5.1)

    注释过的反汇编代码:http://pan.baidu.com/share/link?shareid=3491166579&uk=537224442 伪代码(不精确,仅供参考): NSStrin ...

  3. iOS开发之本地通知UILocalNotification

    本地通知是UILocalNotification的实例,主要有三类属性: scheduled time:时间周期,用来指定iOS系统发送通知的日期和时间: notification type:通知类型 ...

  4. 【Android 逆向】整体加固脱壳 ( DEX 优化流程分析 | DexPrepare.cpp 中 dvmContinueOptimizati() 函数分析 )

    文章目录 前言 一.DexPrepare.cpp 中 dvmContinueOptimizati() 方法分析 前言 上一篇博客 [Android 逆向]整体加固脱壳 ( DEX 优化流程分析 | D ...

  5. 【Android 逆向】整体加固脱壳 ( DEX 优化流程分析 | dvmDexFileOpenPartial | dexFileParse | 脱壳点 | 获取 dex 文件在内存中的首地址 )

    文章目录 前言 一.DexPrepare.cpp 中 rewriteDex() 方法分析 二.DvmDex.cpp 中 dvmDexFileOpenPartial() 方法分析 ( 脱壳点 ) 三.D ...

  6. 【Android 逆向】整体加固脱壳 ( DEX 优化流程分析 | DexPrepare.cpp 中 dvmOptimizeDexFile() 方法分析 | /bin/dexopt 源码分析 )

    文章目录 前言 一.DexPrepare.cpp 中 dvmOptimizeDexFile() 方法分析 二./bin/dexopt 源码分析 前言 上一篇博客 [Android 逆向]整体加固脱壳 ...

  7. ios vue 添加本地音乐_Vue 项目一些常见问题的解决方案

       戳蓝字「前端技术优选」关注我们哦! 有一些问题不限于 Vue,还适应于其他类型的 SPA 项目. 1. 页面权限控制和登陆验证 页面权限控制 页面权限控制是什么意思呢? 就是一个网站有不同的角色 ...

  8. ios vue 添加本地音乐_vue 项目,ios上audio音频 无法自动播放

    // 音乐播放 function autoPlayMusic() { // 自动播放音乐效果,解决浏览器或者APP自动播放问题 function musicInBrowserHandler() { m ...

  9. Android12 (S) 去掉悬浮通知消息及通知创建流程分析

    目录 概述 问题分析 1.通知创建的流程 2.通知view的不同类型 3.HeadsUpView创建入口 4.shouldHeadsUp方法的实现 解决方案 概述 根据产品需求,产品定位不需要通知栏, ...

最新文章

  1. 200字带你看完一本书,GPT-3已经会给长篇小说写摘要了
  2. OSChina 周六乱弹 ——土肥圆装高富帅相亲节目现场拆穿
  3. 报班学python到底怎么样-你们都是怎么学 Python 的?
  4. linux停止nodejs,node.js – 在linux重启后,nodejs消失了
  5. 莫博士:Facebook别再推诿,请承担打击假新闻责任
  6. 深度学习目标检测系列:faster RCNN实现|附python源码
  7. appcan slider轮播图和页面弹动冲突解决
  8. bzoj4695 最假女选手(势能线段树/吉司机线段树)题解
  9. 修改IP4属性时,针对闪退问题的解决方法
  10. 传感器自学笔记第四章——土壤湿度+雨滴模块
  11. talib安装error: Microsoft Visual C++ 14.0 or greater is required. Get it with Microsoft C++ Build的解决方案
  12. Java SE《基础篇》——(二)程序基础01
  13. [Python图像处理] 四十三.Python图像形态学处理万字详解(腐蚀膨胀、开闭运算、梯度顶帽黑帽运算)
  14. 听某个老师的ElasticSearch记的笔记了
  15. 用生成对抗网络给雪人上色,探索人工智能时代的美学
  16. Tinker 合并及加载补丁过程源码分析 (三)
  17. 机器学习(2)——周志华
  18. K_A16_001 基于STM32等单片机驱动HX711称重模块 串口与OLED0.96双显示
  19. 阿里数据中台:组合式or颠覆式创新,企业要不要跟风
  20. 湖北武汉资料员证书资料员工程建设的资料管理建筑七大员证书

热门文章

  1. android开启前台服务_Android 知识点必知之ANR与OOM
  2. c语言运行程序没有,这个程序怎么运行?为什么显示没有exe??
  3. mysql acid介绍_InnoDB ACID模型介绍
  4. JS事件流(事件冒泡 事件委托)
  5. “21天好习惯”第一期-12
  6. Kafka日志清除策略
  7. was supplied but isn‘t a known config
  8. spark RDD概念及组成详解
  9. spark Drive 与Executor
  10. CSS绝对底部布局 Sticky footer