前言

本次更新的重點又是個目前只有 Android 系統提供的實用功能:提示距鬧鈴之剩餘時間。

加上近期逆向經驗的提升,再度審視並重寫了直接點選編輯功能,以增加運行效能。


實作過程

新增提示距響鈴之剩餘時間

  1. 使用 Reveal 檢視鬧鐘開關,是個 UISwitch。

  2. 使用 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>>"]]]
    
  3. 讀取 UISwitch 的 property:allControlEvents,來取得其 UIControlEvents 編號:

    cy# #0x10507e5d0.allControlEvents
    4096
    
  4. 使用actionsForTarget:forControlEvent:方法,來取得其 action:

    cy# [#0x10507e5d0 actionsForTarget:#0x106105e00 forControlEvent:4096]
    @["_alarmActiveChanged:"]
    

    透過查閱.h檔,證實於MTAAlarmTableViewCell類別中實現了_alarmActiveChanged:

  5. 使用 IDA 檢視[MTAAlarmTableViewCell _alarmActiveChanged:]的偽代碼:

    void __cdecl -[MTAAlarmTableViewCell _alarmActiveChanged:](MTAAlarmTableViewCell *self, SEL a2, id a3)
    {
      ...
      objc_msgSend(v8, "setAlarmEnabled:forCell:", v4, v3);
      objc_release(v8);
    }
    
  6. 使用 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:]來開啟鬧鐘。

  7. 使用 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:]來更新鬧鐘狀態。

  8. 搜尋_updateAlarm,於MTAAlarmTableViewController.h中一口氣發現了三個可疑的函式名稱:

    -(void)_removeAlarm:(id)arg1 withCompletionBlock:(CDUnknownBlockType)arg2;
    -(void)_updateAlarm:(id)arg1 withCompletionBlock:(CDUnknownBlockType)arg2;
    -(void)_addAlarm:(id)arg1 withCompletionBlock:(CDUnknownBlockType)arg2;
    
  9. 直接 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實體,裡面似乎有鬧鐘的資料。

  10. 上網查一下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
    

    中獎,幾乎需要的鬧鐘資料都有,接下來就是解析資料並發佈通知,不再詳細贅述。

  11. 實測發現_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
    

點選項目直接編輯功能重寫

  1. 使用 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

  2. 所以不用拐彎,直接呼叫[MTAAlarmTableViewController showEditViewForRow:]即可:

    %hook MTAAlarmTableViewController
    // row 被點擊被呼叫
    -(void)tableView:(id)arg1 didSelectRowAtIndexPath:(NSIndexPath *)arg2
    {
      [self showEditViewForRow:arg2.row]; // 直接呼叫顯示編輯介面
    }
    %end