iOS逆向安防從入門到禿頭--釘釘居家打卡插件&開關

小谷禿頭合集ios

1. 引言

  • 小谷的公司每月能夠遲到3次,小谷5月的時候考勤居然遲到了4次。很難受。爲了杜絕這種狀況。毅然決然的研究一波偷偷打卡😃git

  • 小谷用的設備是iPhone XS MaxiOS 14.3(越獄),目前最新的釘釘:6.0.16markdown

  • Windows逆向的兄弟們,常常使用IDA和Reveal還有終端debugserver附加進程,今天小谷要用iOS開發的兄弟們都比較喜歡的Xcode和hopper。😆(其實都差很少。)函數

  • 先看效果圖post

0.png

2. 釘釘打卡插件

2.1. 調試應用

蹂躪第一步,首先要裝酷學習

  • 先把釘釘砸殼,取下來。我使用的是frida-ios-dump,小谷寫過一篇砸殼和Frida的博客.

1.1.png

1.2.png

這樣砸過殼 釘釘.ipa就出來了,先放這,咱們先無論他ui

  • 開始了! iOS兄弟們超級喜歡的,Xcode調試(曾經有兄弟問過我怎麼附加的。我這裏畫下流程圖)

2.png

這樣就清晰不少了spa

  • 既然咱們能夠調試的話,那就直接定位打卡的代碼了啊 (找到打卡界面,viewdebug調試)

3.png

我滴天啊。是個WKWebview,定位不到!!插件

這樣就結束了嗎?怎麼可能呢,那之後小谷要是遲到,沒有辦法彌補了~·debug

2.1.1. 思路

整理下思路和線索

  • 線索:
    • 打卡界面是個WKWebview界面
    • 咱們打卡成功的時候H5會有變化,說明是有地方告訴H5定位成功
    • 若是是原生定位告訴H5的,那麼他們就必定有交互代碼
  • 思路:
    • 若是他們有交互,咱們知道hook住這個交互就能夠了(看裏面的回傳,經過改變值可能作到)
    • 若是是iOS的定位,必定會走 locationManager: didUpdateLocations:
    • 頗有可能就是,H5交互的時候開始定位,而後回調定位信息

那麼咱們接下來的任務就是定位交互的代碼打印回傳的信息了!! 搞起,搞起~

2.2. 定位代碼

  • 咱們剛開始導出了釘釘.ipa,解壓 ,拿MachO文件

  • 而後經過class-dump取出header

class-dump -H DingTalk -o dingHeader

  • 把二進制文件拖進Hopper (搜索locationManager: didUpdateLocations:)

4.png

一共就5個,咱們就把這幾個hook一下,看下log了,我猜想應該就能夠定位到了(若是他調用的原生的)

  • 此次咱們就用MonkeyTweak插件了(固然也能夠用THEOS,主要咱們此次可能要好幾回調試才能定位,我就用Monkey方便點~)

5.png

  • 獲取釘釘的APPID,並配置

6.png

7.png

  • 咱們把上面找到的那5個,hook一下
#import <UIKit/UIKit.h>

%hook AMapLocationCLMDelegate
- (void)locationManager:(id)arg1 didUpdateLocations:(id)arg2{
    %log;
    %orig;
}
%end

%hook AMapLocationManager
- (void)locationManager:(id)arg1 didUpdateLocations:(id)arg2{
    %log;
    %orig;
}
%end

%hook LALocationManager
- (void)locationManager:(id)arg1 didUpdateLocations:(id)arg2{
    %log;
    %orig;
}
%end

%hook DTCLocationManager
- (void)locationManager:(id)arg1 didUpdateLocations:(id)arg2{
    %log;
    %orig;
}
%end

%hook MAMapView
- (void)locationManager:(id)arg1 didUpdateLocations:(id)arg2{
    %log;
    %orig;
}
%end
複製代碼

而後咱們安裝插件,看下log

  • 咱們發現這個AMapLocationCLMDelegateAMapLocationManager都是成對出現的,其餘的都沒有走

8.png

  • 而後咱們觀察下class-dump出來的header文件。發現AMapLocationCLMDelegate裏面都是一些代理回調

這個時候,小谷又能夠推測一波了: 在AMapLocationManager獲取的位置,而後參數在把參數傳給H5交互

  • 咱們把AMapLocationManager裏面的方法都打一遍log,使用logify.pl

9.png

  • 而後咱們在次裝上插件看下log

11.png

看到了不錯的信息。咱們要繼續開始Xcode調試附加

  • Hopper找到偏移位置

12.png

  • Xcode附加下,能夠找到ASLR

13.png

  • 定位偏移,設置斷點

14.png

0x104e70000 + 0x36b4a8 = 0x1051DB4A8

  • 當我點擊考勤打卡的時候,斷住了~

15.png

  • 根據咱們的思路,咱們要看函數調用棧了!!

16.png

有沒有那麼一丟丟爽歪歪~ (若是沒有符號也沒有關係,咱們就經過Hopper地址定位!)

  • 咱們hook下這個函數
%hook LAPluginInstanceCollector
- (void)handleJavaScriptRequest:(id)arg1 callback:(id)arg2{
    %log;
    %orig;
}
%end
複製代碼
  • 兄弟們~ 咱們能夠獲得線索,這個callback是個block,說明arg1是參數,arg2是回調回去的參數

17.png

  • 想知道這個block的參數類型。就要看看他的簽名了~

18.png

經過內存平移來拿他的簽名~

  • 老辦法,Hopper拿地址。而後Xcode附加下斷點

19.png 20.png

0x100f50000 + 0x488f110 = 0x1057DF110

  • b 0x1057DF110 斷住它,而後分析他

21.png 22.png

  • 這個時候們就能夠繼續hook了,看看他裏面的參數是啥
%hook LAPluginInstanceCollector

- (void)handleJavaScriptRequest:(id)arg1 callback:(void (^) (id))arg2{
    
    //咱們能夠自定義個block看下。
    id xg_callback = ^(id argCb){
        //能夠先把傳入的參數打印下,看看有沒有啥觸發器
        NSLog(@"xg_callback arg1:%@",arg1);
        //而後把傳入的參數打印下,也看下他是什麼類型的
        NSLog(@"xg_callback argCbClass:%@, argCb:%@",[argCb class],argCb);
        arg2(argCb);
    };
    //xg_callback替換arg2。(只作了一個轉接的過程)
    %orig(arg1,xg_callback);
}
%end
複製代碼
  • 而後看效果

23.png

我好像找到了,可是這裏面還有好多其餘跟他相關連的信息。(好比地址,城市,經緯度啥的)

2.3. 打卡插件代碼

咱們知道他類型是個字典。action=start的時候觸發

  • 直接上代碼了(最新版)
%hook LAPluginInstanceCollector
- (void)handleJavaScriptRequest:(id)arg1 callback:(void (^) (id))arg2{
    id xg_callback = ^(id argCb){
        //小谷作的時候其實打了好多log。。
        NSDictionary *arg1Dic = (NSDictionary *)arg1;
        if([arg1Dic[@"action"] isEqualToString:@"start"]){
            NSLog(@"xg_callback text:start");
            NSMutableDictionary * CbDic = [NSMutableDictionary dictionaryWithDictionary:argCb];
            if (CbDic[@"result"][@"latitude"] && CbDic[@"result"][@"longitude"]){
                NSLog(@"xg_callback text:result");
                NSMutableDictionary * resultDic = [NSMutableDictionary dictionaryWithDictionary:CbDic[@"result"]];
                resultDic[@"latitude"] = @"40.0361208767361";
                resultDic[@"longitude"] = @"116.4161067708333";
                [CbDic setValue:resultDic forKey:@"result"];
            }
            arg2(CbDic);
        }else{
            arg2(argCb);
        }
    };
    //xg_callback替換arg2。(只作了一個轉接的過程)
    %orig(arg1,xg_callback);
}
%end
複製代碼
  • 看下效果圖

24.png

3. 設置打卡開關

主要功能寫的差很少了。咱們再來設置一個開關來控制一下:若是開關開啓,就走打卡插件邏輯,若是開關關閉,走原來的邏輯

  • 咱們在設置裏面偷偷的加個按鈕~

25.png

Xcode好強大~

  • 而後找下dataSource

26.png

  • 那這就好辦了啊~ (兄得們,這是個tableview啊,把他的代碼一hook 加行cell,不是很easy嗎)
@interface DTTableViewHandler : NSObject
- (long long)numberOfSectionsInTableView:(UITableView *)tableView;
@end

%hook DTTableViewHandler

%new
-(void)xg_switchChang:(UISwitch *)switchView{
    [XGDefaults setBool:switchView.isOn forKey:XGSWITCHKEY];
    [XGDefaults synchronize];
}

- (long long)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
// 若是是最後一組,增長一行
    if([tableView.nextResponder .nextResponder isKindOfClass:%c(DTSettingListViewController)] && (section == [self numberOfSectionsInTableView:tableView]-1)){
        return 1;
    }
    return %orig;
}

- (id)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
    //最後一組,最後一行,設置一個cell
    if([tableView.nextResponder .nextResponder isKindOfClass:%c(DTSettingListViewController)] && ([indexPath section] == [self numberOfSectionsInTableView:tableView]-1)){
        UITableViewCell * cell = nil;
        if([indexPath row] == 0){
            static NSString * swCell = @"SWCELL";
            cell = [tableView dequeueReusableCellWithIdentifier:swCell];
            if(!cell){
                cell = [[UITableViewCell alloc] initWithStyle:(UITableViewCellStyleDefault) reuseIdentifier:nil];
            }
            cell.textLabel.text = @"偷偷打卡開啓!!";
            UISwitch * switchView = [[UISwitch alloc] init];
            switchView.on = [XGDefaults boolForKey:XGSWITCHKEY];
            [switchView addTarget:self action:@selector(xg_switchChang:) forControlEvents:(UIControlEventValueChanged)];
            cell.accessoryView = switchView;
            cell.backgroundColor = [UIColor whiteColor];
            return cell;
        }
        return nil;
    }else{
        return %orig;
    }
}

- (double)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
    //設置最後一組最後一行的行高
    if([tableView.nextResponder .nextResponder isKindOfClass:%c(DTSettingListViewController)] && ([indexPath section] == [self numberOfSectionsInTableView:tableView]-1)){
        return 45;
    }
    return %orig;
}

- (long long)numberOfSectionsInTableView:(UITableView *)tableView {
    //增長一組
    if([tableView.nextResponder .nextResponder isKindOfClass:%c(DTSettingListViewController)]){
        return %orig + 1;
    }
    return %orig;
}
%end
複製代碼
  • 而後在原來打卡的地方加個判斷,就行了

if([arg1Dic[@"action"] isEqualToString:@"start"] && [XGDefaults boolForKey:XGSWITCHKEY]){

  • 看效果

0.png

4. 總結

  • 這篇博客禁止作商業用途。純屬爲了學習,若是有法律責任與本人無關!!

  • 這篇博客是小谷自創的,若是轉載請標明出處!

  • 小谷原本想直接HOOK-iOS回調的那個函數改值,不過感受會有誤傷

  • 好了兄弟們。逆向插件開發了一波。小谷準備下一篇寫個基礎安防

  • 不過不能寫這麼長了。博客寫太長,兄弟們估計不想看。我儘可能精簡

  • 最後祝兄弟們,愈來愈帥!!!! 還有你們永不遲到~

相關文章
相關標籤/搜索