钉钉逆向打卡定位功能思路解析

钉钉逆向打卡定位功能思路解析

目标:定位钉钉定位代码,实现修改定位坐标,实现任意地点打卡

  • 界面定位

首先通过Reveal或者Cycript定位打卡页面,发现定位页面是WebView,页面元素全是HTML来编写的。要通过界面来定位对应的控制器或者方法来分析这条思路是行不通的。

但是通过正向开发的经验得出,定位功能必然是调用原生的定位接口来获得定位数据的,必然会调用delegate包含didUpdateLocations的方法。所以通过class-dump导出所有头文件,查找包含didUpdateLocations方法的类。然后建立Tweak工程进行Hook分析。

@interface AMapLocationCLMDelegate
@end

%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 DTCLocationManager
- (void)locationManager:(id)arg1 didUpdateLocations:(id)arg2
{
    %log;
    %orig;
}
%end

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

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

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

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

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

  • 定位到原生定位代码

通过上面的Tweak工程测试查看定位过程主要涉及的类如下:

AMapLocationCLMDelegate
AMapLocationManager
LAPLocationInfo

此时就需要我们分析下面的三个类了,根据经验猜想首先分析AMapLocationManager类,如果没有找到合适的入口,再分析后面的类。

通过logify.pl生成对应的Tweak.xm文件

logify.pl  ./AMapLocationManager.h > Tweak.xm
- (void)startCLMLocating { %log; %orig; }
- (void)startMonitoringForRegion:(id)arg1 { %log; %orig; }
- (void)startRegionCLManager { %log; %orig; }
- (void)startUpdatingHeading { %log; %orig; }
- (void)startUpdatingLocation { %log; %orig; }
- (void)stopAndExitAllCLM { %log; %orig; }
- (void)stopCLMLocating { %log; %orig; }
- (void)stopMonitoringForRegion:(id)arg1 { %log; %orig; }
- (void)stopRegionCLManager { %log; %orig; }
- (void)stopUpdatingHeading { %log; %orig; }
- (void)stopUpdatingLocation { %log; %orig; }
... 省略其他方法

通过Tweak工程产看log分析调用该类中的方法。这时候查看调用的方法中,猜想哪些方法会有用处。此时看到startUpdatingLocation很有可能是我们需要的方法。

  • 动态调试获取猜想方法的函数调用栈

目标:然后通过lldb动态调试给startUpdatingLocation下断点,查看该函数的调用栈。
1、首先通过IDA获取[AMapLocationManager startUpdatingLocation]方法的地址为0x00000001001a0870
2、通过lldb获取ASLR偏移地址

image list -o -f

0x0000000100040000
...

3、对AMapLocationManager startUpdatingLocation下断点

br s -a "0x00000001001a0870 + 0x40000"

4、获取函数调用栈

bt

* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
  * frame #0: 0x00000001001e0870 DingTalk` ___lldb_unnamed_symbol7414$$DingTalk 
    frame #1: 0x000000010830daa0 DingDing.dylib` _logos_method$_ungrouped$AMapLocationManager$startUpdatingLocation(self=0x0000000146124e00, _cmd="startUpdatingLocation")  + 168 at Tweak.xm:119
    frame #2: 0x0000000102468cbc DingTalk` ___lldb_unnamed_symbol173872$$DingTalk  + 248
    frame #3: 0x0000000103916e6c DingTalk` ___lldb_unnamed_symbol274461$$DingTalk  + 1980
    frame #4: 0x000000010395b338 DingTalk` ___lldb_unnamed_symbol275794$$DingTalk  + 52
    frame #5: 0x000000010395b240 DingTalk` ___lldb_unnamed_symbol275793$$DingTalk  + 2388
    frame #6: 0x00000001038f2158 DingTalk` ___lldb_unnamed_symbol273743$$DingTalk  + 348
    frame #7: 0x00000001038e3078 DingTalk` ___lldb_unnamed_symbol273399$$DingTalk  + 1328
    frame #8: 0x00000001038e2100 DingTalk` ___lldb_unnamed_symbol273390$$DingTalk  + 232
    frame #9: 0x00000001038e38c0 DingTalk` ___lldb_unnamed_symbol273408$$DingTalk  + 304
    frame #10: 0x0000000103933e08 DingTalk` ___lldb_unnamed_symbol275019$$DingTalk  + 172
    frame #11: 0x0000000103932254 DingTalk` ___lldb_unnamed_symbol274996$$DingTalk  + 408
    frame #12: 0x0000000103976800 DingTalk` ___lldb_unnamed_symbol276394$$DingTalk  + 288
    frame #13: 0x0000000103c4a8d0 DingTalk` ___lldb_unnamed_symbol292198$$DingTalk  + 360
    frame #14: 0x00000001868c5808 UIKit` -[UIWebView webView:decidePolicyForNavigationAction:request:frame:decisionListener:]  + 296
    frame #15: 0x000000018199b430 CoreFoundation` __invoking___  + 144
    frame #16: 0x0000000181898eb4 CoreFoundation` -[NSInvocation invoke]  + 284
    frame #17: 0x000000018189d7e0 CoreFoundation` -[NSInvocation invokeWithTarget:]  + 60
    frame #18: 0x000000018638cd48 WebKitLegacy` -[_WebSafeForwarder forwardInvocation:]  + 156
    frame #19: 0x00000001819993e8 CoreFoundation` ___forwarding___  + 408
    frame #20: 0x000000018189d68c CoreFoundation` _CF_forwarding_prep_0  + 92
    frame #21: 0x000000018199b430 CoreFoundation` __invoking___  + 144
    frame #22: 0x0000000181898eb4 CoreFoundation` -[NSInvocation invoke]  + 284
    frame #23: 0x0000000185459548 WebCore` HandleDelegateSource(void*)  + 108
    frame #24: 0x000000018194cefc CoreFoundation` __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__  + 24
    frame #25: 0x000000018194c990 CoreFoundation` __CFRunLoopDoSources0  + 540
    frame #26: 0x000000018194a690 CoreFoundation` __CFRunLoopRun  + 724
    frame #27: 0x0000000181879680 CoreFoundation` CFRunLoopRunSpecific  + 384
    frame #28: 0x0000000182d88088 GraphicsServices` GSEventRunModal  + 180
    frame #29: 0x00000001866f0d90 UIKit` UIApplicationMain  + 204
    frame #30: 0x0000000100044bf8 DingTalk` ___lldb_unnamed_symbol17$$DingTalk  + 336
    frame #31: 0x000000018141a8b8 libdyld.dylib` start  + 4

5、 分析函数调用栈,获取调用函数名

    frame #2: 0x0000000102468cbc DingTalk` 
    0x0000000102468cbc - 0x40000 = 0x102428CBC   startUpdatingLocation
    
    frame #3: 0x0000000103916e6c DingTalk` 
    0x0000000103916e6c - 0x40000 = 0x1038D6E6C   LAPLocationInfo start:to:
    
    frame #4: 0x000000010395b338 DingTalk` 
    0x000000010395b338 - 0x40000 = 0x10391B338   [LAPluginInstanceCollector handleJavaScriptRequest:callback:]
    

6、通过还原函数调用栈发现handleJavaScriptRequest很有可能就是我们需要查找的定位交互方法。

  • 编写Tweak工程分析方法参数
- (void)handleJavaScriptRequest:(id)arg1 callback:(id)arg2 {
    NSLog(@"arg1 = %@, arg1 class = %@, arg2 = %@, arg2 class = %@", arg1, [arg1 class], arg2, [arg2 class]);
    %orig;
}

查看打印信息:

arg1 class = __NSCFDictionary, arg2 = <__NSStackBlock__: 0x16fdc2550>
说明参数1是NSDictionary 参数2是Block

动态分析参数2,Block的详细信息,包括有无返回值,有无参数,参数类型等。
通过lldb对该方法下断点,查看对用参数

(lldb) po $x0


(lldb) po $x1
4412466501

(lldb) memory read 4412466501
0x10700e545: 68 61 6e 64 6c 65 4a 61 76 61 53 63 72 69 70 74  handleJavaScript
0x10700e555: 52 65 71 75 65 73 74 3a 63 61 6c 6c 62 61 63 6b  Request:callback

(lldb) po $x2
{
    action = openLink;
    args =     {
        credible = 1;
        ddAgentId = 349574454;
        ddAppId = 158;
        enableShare = 1;
        showMenuBar = 1;
        url = "https://attend.dingtalk.com/attend/index.html?lwfrom=2019040215441244000&corpId=dingd6eefcc883a0b55c4ac5d6980864d335&showmenu=false&dd_share=false&dd_progress=false&spm=a2o5v.12729200";
    };
    plugin = "biz.util";
    url = "https://h5.dingtalk.com/industry_versatility/index.html?dd_progress=false&corpId=dingd6eefcc883a0b55c4ac5d6980864d335&fromOld=true&mini_app_scheme=dingtalk%3a%2f%2fdingtalkclient%2faction%2fopen_mini_app%3fpVersion%3d1%26miniAppId%3d2018121962624311%26page%3dsrc%2fpages%2fhome%2findex%26packageType%3d1&dd_version=4.6.16";
}

(lldb) po $x3
<__NSStackBlock__: 0x16fd5e550>

补充:Block的底层结构
通过苹果官方开源网站下载Block底层源码查看
#define BLOCK_DESCRIPTOR_1 1
struct Block_descriptor_1 {
    uintptr_t reserved; // uintptr_t = unsigned long
    uintptr_t size;
};

#define BLOCK_DESCRIPTOR_2 1
struct Block_descriptor_2 {
    // requires BLOCK_HAS_COPY_DISPOSE
    BlockCopyFunction copy;
    BlockDisposeFunction dispose;
};

#define BLOCK_DESCRIPTOR_3 1
struct Block_descriptor_3 {
    // requires BLOCK_HAS_SIGNATURE
    const char *signature;
    const char *layout;     // contents depend on BLOCK_HAS_EXTENDED_LAYOUT
};

struct Block_layout {
    void *isa;
    volatile int32_t flags; // contains ref count
    int32_t reserved;
    BlockInvokeFunction invoke;
    struct Block_descriptor_1 *descriptor;
    // imported variables
};

// Values for Block_layout->flags to describe block objects
enum {
    BLOCK_DEALLOCATING =      (0x0001),  // runtime
    BLOCK_REFCOUNT_MASK =     (0xfffe),  // runtime
    BLOCK_NEEDS_FREE =        (1 << 24), // runtime
    BLOCK_HAS_COPY_DISPOSE =  (1 << 25), // compiler
    BLOCK_HAS_CTOR =          (1 << 26), // compiler: helpers have C++ code
    BLOCK_IS_GC =             (1 << 27), // runtime
    BLOCK_IS_GLOBAL =         (1 << 28), // compiler
    BLOCK_USE_STRET =         (1 << 29), // compiler: undefined if !BLOCK_HAS_SIGNATURE
    BLOCK_HAS_SIGNATURE  =    (1 << 30), // compiler
    BLOCK_HAS_EXTENDED_LAYOUT=(1 << 31)  // compiler
};

BLOCK_HAS_SIGNATURE  =   (1 << 30)
当Block_layout中flags的值第30位值等于1时才有签名信息。

根据Block_layout结构体看出,依次是isa指针(8个字节)、flags标识(4个字节)、reserved(4个字节)、invoke实现(8个字节)、descriptor(8个字节)
而descriptor中包括了BLOCK_DESCRIPTOR_1(16个字节)、BLOCK_DESCRIPTOR_2(16个字节)、BLOCK_DESCRIPTOR_3(16个字节)结构,只有BLOCK_DESCRIPTOR_3中才有block签名信息。

读取Block 0x16fd5e550 往下的内存数据获取Block的签名信息
(lldb) memory read 0x16fd5e550
0x16fd5e550: 18 b2 a9 a0 01 00 00 00 00 00 00 c2 00 00 00 00  .???.......?....
0x16fd5e560: 9c 21 95 03 01 00 00 00 78 39 b7 04 01 00 00 00  .!......x9?.....

根据上述分析地址 0x0104b73978(小端模式) 才是descriptor中的数据

读取0x0104b73978往下的内存数据
(lldb) x/48 0x0104b73978
0x104b73978: 0x00000000 0x00000000 0x00000028 0x00000000
0x104b73988: 0x039521b0 0x00000001 0x039521c0 0x00000001
0x104b73998: 0x068fa364 0x00000001 0x00000100 0x00000000
0x104b739a8: 0x00000000 0x00000000 0x00000028 0x00000000
0x104b739b8: 0x039521c8 0x00000001 0x039521d4 0x00000001
0x104b739c8: 0x06c78065 0x00000001 0x00000001 0x00000000
0x104b739d8: 0x00000000 0x00000000 0x00000028 0x00000000
0x104b739e8: 0x039522bc 0x00000001 0x039522cc 0x00000001
0x104b739f8: 0x068fa364 0x00000001 0x00000100 0x00000000
0x104b73a08: 0x00000000 0x00000000 0x00000028 0x00000000
0x104b73a18: 0x039522d4 0x00000001 0x039522e0 0x00000001
0x104b73a28: 0x06c6baa3 0x00000001 0x00000001 0x00000000

根据上面分析,从0x0104b73978往下32个字节才是签名信息数据的开始地址即:0x01068fa364
读取第32个字节出的数据
(lldb) x 0x01068fa364
0x1068fa364: 76 31 36 40 3f 30 40 38 00 61 63 63 65 6c 65 72  v16@[email protected]
0x1068fa374: 6f 6d 65 74 65 72 43 68 61 6e 67 65 00 6b 45 78  ometerChange.kEx

如lldb打印结果所示:v16@?0@8就是该block的签名信息
获取详细签名信息:
(lldb) po [NSMethodSignature signatureWithObjCTypes:"v16@?0@8"]

    number of arguments = 2
    frame size = 224
    is special struct return? NO
    return value: -------- -------- -------- --------
        type encoding (v) 'v'
        flags {}
        modifiers {}
        frame {offset = 0, offset adjust = 0, size = 0, size adjust = 0}
        memory {offset = 0, size = 0}
    argument 0: -------- -------- -------- --------
        type encoding (@) '@?'
        flags {isObject, isBlock}
        modifiers {}
        frame {offset = 0, offset adjust = 0, size = 8, size adjust = 0}
        memory {offset = 0, size = 8}
    argument 1: -------- -------- -------- --------
        type encoding (@) '@'
        flags {isObject}
        modifiers {}
        frame {offset = 8, offset adjust = 0, size = 8, size adjust = 0}
        memory {offset = 0, size = 8}
        
由结果可知:return value:v 说明没有返回值,argument 0:是block对象本身,argument 1: 是一个对象

综上所述。callback的参数时callback:(void(^)(id))arg2

修改Tweak工程继续分析参数

- (void)handleJavaScriptRequest:(id)arg1 callback:(void(^)(id))arg2 {
    id test_block = ^(id block_arg) {
         NSLog(@"block_arg = %@, block_arg class = %@", block_arg, [block_arg class]);
         arg2(block_arg);
    };
    %orig(arg1, test_block);
}
查看答应结果:
 block_arg = {
	    errorCode = 0;
	    errorMessage = "";
	    keep = 0;
	    result = "";
	}, block_arg class = __NSDictionaryI
	
说明block的参数是一个字典,该方法可以修改成:
- (void)handleJavaScriptRequest:(NSDictionary *)arg1 callback:(void(^)(NSDictionary *))arg2 {
    id test_block = ^(NSDictionary * block_arg) {
         arg2(block_arg);
    };
    %orig(arg1, test_block);
}
  • 利用Tweak跟踪定位信息

利用上述还原的方法跟踪定位过程中的参数数据。

arg1 = {
        action = start;
        args =     {
            callBackInterval = 5;
            checkLBSRisk = 1;
            forbidMock = 0;
            locationMode = 2;
            targetAccuracy = 50;
            useCache = 1;
            withReGeocode = 0;
        };
        plugin = "device.geolocation";
    }, block_arg = {
        errorCode = 0;
        errorMessage = "";
        keep = 1;
        result =     {
            aMapCode = 0;
            accuracy = 65;
            latitude = "32.54996012369792";
            longitude = "105.0593180338542";
            netType = wifi;
            operatorType = CUCC;
            resultCode = 0;
            resultMessage = "";
            time = "1583738200832.482";
        };
    }
    
通过log打印数据分析参数内容,这个时候可以看到经纬度信息,所以如何修改定位信息就看自己需要了!

你可能感兴趣的:(iOS)