TimerFix 更新 ver.2
前言
本次更新的重點又是個目前只有 Android 系統提供的實用功能:提示距鬧鈴之剩餘時間。
加上近期逆向經驗的提升,再度審視並重寫了直接點選編輯功能,以增加運行效能。
實作過程
新增提示距響鈴之剩餘時間
-
使用 Reveal 檢視鬧鐘開關,是個 UISwitch。
-
使用 Cycript 讀取 UISwitch 的 property:
allTargets
,來取得其 target:cy# #0x10507e5d0.allTargets [NSSet setWithArray:@[#"<MTAAlarmTableViewCell: 0x106105e00; baseClass = UITableViewCell; frame = (0 0; 414 96.3333); autoresize = W; layer = <CALayer: 0x281598520>>"]]]
-
讀取 UISwitch 的 property:
allControlEvents
,來取得其 UIControlEvents 編號:cy# #0x10507e5d0.allControlEvents 4096
-
使用
actionsForTarget:forControlEvent:
方法,來取得其 action:cy# [#0x10507e5d0 actionsForTarget:#0x106105e00 forControlEvent:4096] @["_alarmActiveChanged:"]
透過查閱
.h
檔,證實於MTAAlarmTableViewCell
類別中實現了_alarmActiveChanged:
。 -
使用 IDA 檢視
[MTAAlarmTableViewCell _alarmActiveChanged:]
的偽代碼:void __cdecl -[MTAAlarmTableViewCell _alarmActiveChanged:](MTAAlarmTableViewCell *self, SEL a2, id a3) { ... objc_msgSend(v8, "setAlarmEnabled:forCell:", v4, v3); objc_release(v8); }
-
使用 LLDB 於
setAlarmEnabled:forCell:
的記憶體位址處0x100027994
下斷點,來查看誰是呼叫者(v8
):(lldb) im li -o -f "MobileTimer" [ 0] 0x000000000291c000 /Applications/MobileTimer.app/MobileTimer(0x000000010291c000) [ 1] 0x0000000064130000 /Users/yuripeyamashita/Library/Developer/Xcode/iOS DeviceSupport/12.4 (16G77)/Symbols/System/Library/PrivateFrameworks/MobileTimer.framework/MobileTimer (lldb) br s -a 0x000000000291c000+0x100027994 Breakpoint 1: where = MobileTimer`_mh_execute_header + 145716, address = 0x0000000102943994 (lldb) c Process 7968 resuming Process 7968 stopped *thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1 frame #0: 0x0000000102943994 MobileTimer`_mh_execute_header + 162196 MobileTimer`_mh_execute_header: -> 0x102943994 <+162196>: ldr x1, #0x7cccc ; "setAlarmEnabled:forCell:" 0x102943998 <+162200>: mov x2, x20 0x10294399c <+162204>: mov x3, x19 0x1029439a0 <+162208>: bl 0x1029891e8 ; symbol stub for: objc_msgSend Target 0: (MobileTimer) stopped. (lldb) po $x0 <MTAAlarmTableViewController: 0x147d390d0>
可見是呼叫
[MTAAlarmTableViewController setAlarmEnabled:forCell:]
來開啟鬧鐘。 -
使用 IDA 檢視
[MTAAlarmTableViewController setAlarmEnabled:forCell:]
的偽代碼:void __cdecl -[MTAAlarmTableViewController setAlarmEnabled:forCell:](MTAAlarmTableViewController *self, SEL a2, bool a3, id a4) { ... -[MTAAlarmTableViewController _updateAlarm:withCompletionBlock:](v5, "_updateAlarm:withCompletionBlock:", v23, &v26); objc_release(v31); objc_release(v30); objc_release(v25); objc_release(v23); objc_release(v11); }
發現最後是呼叫
[MTAAlarmTableViewController _updateAlarm:withCompletionBlock:]
來更新鬧鐘狀態。 -
搜尋
_updateAlarm
,於MTAAlarmTableViewController.h
中一口氣發現了三個可疑的函式名稱:-(void)_removeAlarm:(id)arg1 withCompletionBlock:(CDUnknownBlockType)arg2; -(void)_updateAlarm:(id)arg1 withCompletionBlock:(CDUnknownBlockType)arg2; -(void)_addAlarm:(id)arg1 withCompletionBlock:(CDUnknownBlockType)arg2;
-
直接 hook
_updateAlarm:withCompletionBlock:
看看參數是什麼:%hook MTAAlarmTableViewController -(void)_updateAlarm:(id)arg1 withCompletionBlock:(id)arg2; { HBLogDebug(@"_updateAlarm:%@,%@", arg1, arg2); %orig; } %end
[TimerFix: Tweak.xm:49] DEBUG: _updateAlarm: <MTMutableAlarm:0x2814ce800 AlarmID: 3994270B-E71B-4CDA-90D2-848E2CC3A3CB, Title: (null), Hour: 11, Minute: 0, enabled: 0, active: Phone>,<__NSStackBlock__: 0x16dbf4aa8>
第一個參數是個
MTMutableAlarm
實體,裡面似乎有鬧鐘的資料。 -
上網查一下
MTMutableAlarm
有什麼 property 可用:@interface MTMutableAlarm : NSObject @property (nonatomic) bool allowsSnooze; @property (getter=isEnabled, nonatomic) bool enabled; @property (nonatomic) unsigned long long hour; @property (nonatomic) unsigned long long minute; @property (nonatomic) unsigned long long repeatSchedule; @property (nonatomic, copy) NSString *title; @end
中獎,幾乎需要的鬧鐘資料都有,接下來就是解析資料並發佈通知,不再詳細贅述。
-
實測發現
_addAlarm:
、_updateAlarm:
兩個方法都需要 hook:%hook MTAAlarmTableViewController // 新增鬧鐘時被呼叫 -(void)_addAlarm:(MTMutableAlarm *)arg1 withCompletionBlock:(id)arg2 { [self left_time_notify:arg1]; %orig; } // 更新鬧鐘狀態時被呼叫 -(void)_updateAlarm:(MTMutableAlarm *)arg1 withCompletionBlock:(id)arg2 { [self left_time_notify:arg1]; %orig; } %end
點選項目直接編輯功能重寫
-
使用 IDA 深入分析
[MTAAlarmTableViewController tableView:didSelectRowAtIndexPath:]
:void __cdecl -[MTAAlarmTableViewController tableView:didSelectRowAtIndexPath:](MTAAlarmTableViewController *self, SEL a2, id a3, id a4) { ... v5 = self; ... v10 = objc_msgSend(v8, "row"); -[MTAAlarmTableViewController showEditViewForRow:](v5, "showEditViewForRow:", v10); ... }
透過 LLDB 下斷點,發現
v8
是來自第二個參數,是個NSIndexPath
實體,row
是其 property,會回傳一個NSInteger
。 -
所以不用拐彎,直接呼叫
[MTAAlarmTableViewController showEditViewForRow:]
即可:%hook MTAAlarmTableViewController // row 被點擊被呼叫 -(void)tableView:(id)arg1 didSelectRowAtIndexPath:(NSIndexPath *)arg2 { [self showEditViewForRow:arg2.row]; // 直接呼叫顯示編輯介面 } %end