-
-
[原创]某营业厅算法分析
-
发表于: 2022-9-5 09:10 18760
-
阅读此文档的过程中遇到任何问题,请关注公众号【移动端Android和iOS开发技术分享
】或加QQ群【812546729
】
1.目标
使用frida stalker分析某营业厅的签名算法。
2.操作环境
mac系统
frida-ios-dump:砸壳
Charles:抓包
已越狱iOS设备:脱壳及frida调试
IDA Pro:静态分析
3.流程
寻找切入点
在账号密码登录页,点击登录,通过Charles抓包获取到关键词为loginAuthCipherAsymmertric,这也就是我们的切入点:
分析过程
使用frida-ios-dump的砸壳命令dump.py com.wemomo.momoappdemo1
砸壳获取到ipa文件,再使用IDA Pro编译ipa文件,然后搜索搜索字符串loginAuthCipherAsymmertric失败,继续搜索userLoginNormal,失败。该应用对字符串都进行了混淆。搜索无果后,只能换个思路,尝试hook NSMutableURLRequest类。
使用frida-trace的frida-trace -UF -m "-[NSMutableURLRequest setHTTPBody:]"
命令跟踪该函数,js代码如下:
1 2 3 4 5 6 7 8 | { onEnter(log, args, state) { var arg2 = new ObjC. Object (args[ 2 ]) log(` - [NSMutableURLRequest setHTTPBody:${arg2.bytes().readUtf8String()}]`); }, onLeave(log, retval, state) { } } |
点击登录后,获取到的日志如下:
1 2 3 | - [NSMutableURLRequest setHTTPBody:ndh = 1 &t = 00 % 2F00 % 2F0000 % 2000 % 3A00 % 3A00 % 200 % 20 - 480 &c.&v3 = baoguang_event&deviceId = 7B063D78 - C5D8 - 45A6 - 8E3D - 37B787DEB2CD &hitDate = 2022 - 09 - 02 % 2023 % 3A01 % 3A52 ¤tPage = Denglujh - kuandaidenglushouji&v63 = 101 - 106 &appVersion = 4 &c25 = % E6 % B6 % 88 % E6 % 81 % AF % 2BSyjh - sytop - message - 1_1 % 2B % 28null % 29 % 2B % 28null % 29 &es_timestamp = 1662130874519 &v60 = lo158b3b81&codeVersion = 22033101 &v65 = WIFI&v61 = disable&a.&action = trkAppBgAction&OSVersion = iOS % 2012.5 . 5 &DeviceName = iPhone7 % 2C2 &RunMode = Application&AppID = CTPocket % 209.4 . 0 % 20 % 284 % 29 &CarrierName = % 28null % 29 &Resolution = 750x1334 &TimeSinceLaunch = 2536 &.a&lunchtype = aut_lunch&.c&pev2 = AMACTION % 3AtrkAppBgAction &pageName = CTPocket % 2F4 &ce = UTF - 8 &aid = 540B88F2280D4599 - 146C532D0B076319 &pe = lnk_o&cp = foreground] - [NSMutableURLRequest setHTTPBody:{ "content" :{ "fieldData" :{ "isChinatelecom" : "0" , "phoneNum" : "13245678901" , "authentication" : "222222" , "accountType" :" "," deviceUid ":" ddecc53e1fce414e9e89b6ea47e567e3 "," systemVersion ":" 12.5 . 5 "," loginAuthCipherAsymmertric ":" o4Af5TvC5iV25FhTE9NIZJEiqHLWg + JkcCF4AGp727uhvFydBWvkCz8HauqTDpIoRhpfqLUMLN6Hk1ucBZOPYhCHwm4N\ / 4PuPsMWZTEbips + uL74ufgeLMci0nIZRmFsCsBrgvUVkebKcRo2yO0DZQ2jtnKe + cG78v6aOHl5ssk = "," loginType ":" 4 "}," attach ":" iPhone "}," headerInfos ":{" broadAccount ":" "," source ":" 120002 "," shopId ":" 20004 "," userLoginName ":" 13245678901 "," broadToken ":" "," code ":" userLoginNormal "," clientType ":" #9.4.0#channel50#iPhone 6#","token":"","timestamp":"20220902230115","sourcePassword":"TiqmIZ"}}] - [NSMutableURLRequest setHTTPBody:ndh = 1 &t = 00 % 2F00 % 2F0000 % 2000 % 3A00 % 3A00 % 200 % 20 - 480 &c.&v3 = hit_event&deviceId = 7B063D78 - C5D8 - 45A6 - 8E3D - 37B787DEB2CD &hitDate = 2022 - 09 - 02 % 2023 % 3A01 % 3A05 &c20 = % E5 % 8F % B3 % E6 % BB % 91 % E7 % 99 % BB % E5 % BD % 95 &appVersion = 4 &lastAction = Denglujh - denglu - mima - 2_8 % 5E % E5 % 8F % B3 % E6 % BB % 91 % E7 % 99 % BB % E5 % BD % 95 &method = trkAppButtonClick % 20 - action % 20. ..&v63 = 101 - 106 ¤tPage = Denglujh - kuandaidenglushouji&es_timestamp = 1662130875056 &c21 = Denglujh - denglu - mima - 2_8 &v60 = lo158b3b81&codeVersion = 22033101 &v65 = WIFI&v61 = disable&a.&action = trkAppButtonClick&OSVersion = iOS % 2012.5 . 5 &CarrierName = % 28null % 29 &DeviceName = iPhone7 % 2C2 &AppID = CTPocket % 209.4 . 0 % 20 % 284 % 29 &RunMode = Application&Resolution = 750x1334 &TimeSinceLaunch = 2537 &.a&lunchtype = aut_lunch&prePageAction = hit_event&.c&pev2 = AMACTION % 3AtrkAppButtonClick &pageName = CTPocket % 2F4 &ce = UTF - 8 &aid = 540B88F2280D4599 - 146C532D0B076319 &pe = lnk_o&cp = foreground] |
搜索登录的账号13245678901后,发现日志里有登录的body信息,在setHTTPBody的js代码里打印堆栈:
1 2 3 4 5 6 7 8 9 10 11 | { onEnter(log, args, state) { var arg2 = new ObjC. Object (args[ 2 ]) log(` - [NSMutableURLRequest setHTTPBody:${arg2.bytes().readUtf8String()}]`); log( 'NSMutableURLRequest setHTTPBody called from:\n' + Thread.backtrace(this.context, Backtracer.ACCURATE) . map (DebugSymbol.fromAddress).join( '\n' ) + '\n' ); }, onLeave(log, retval, state) { } } |
获取到的堆栈信息如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | 0x10397f4d8 CTPocket! - [AFJSONRequestSerializer requestBySerializingRequest:withParameters:error:] 0x10397ae50 CTPocket! - [AFHTTPRequestSerializer requestWithMethod:URLString:parameters:error:] 0x10394edac CTPocket! - [AFHTTPSessionManager dataTaskWithHTTPMethod:URLString:parameters:headers:uploadProgress:downloadProgress:success:failure:] 0x10394e378 CTPocket! - [AFHTTPSessionManager POST:parameters:headers:progress:success:failure:] 0x101b8fae8 CTPocket! - [ESHttpSessionManager postWithHost:urlString:parameters:success:failure:] 0x1015f91c8 CTPocket! - [ESNetworkingManager postWithURLString:parameters:modifiParamsBlock:success:failure:] 0x100c14da4 CTPocket! - [ESLoginService loginWithPhoneNbr: type :code:isChinatelecom:slidingTime:percentage:success:failure:] 0x101f85f58 CTPocket! - [ESBindLoginViewController phoneLoginWithPhoneNbr: type :code:slidingTime:percentage:isChinatelecom:isBind:failureBlock:] 0x101f6a060 CTPocket! - [ESBindLoginViewController phoneLoginViewController:inputView:didSliderWithSlidingTime:Percentage:phoneNbr:passwd:loginType:isChinatelecom:isBind:failureBlock:] 0x102ccb964 CTPocket! - [ESLoginViewController phoneLoginView:inputView:didSliderWithSlidingTime:Percentage:phoneNbr:passwd:loginType:isChinatelecom:isBind:] 0x101261728 CTPocket! - [ESPhoneLoginView passwordInputView:sliderWithSlidingTime:Percentage:phoneNbr:passwd:] 0x101e73d10 CTPocket! - [PasswordInputView commitBtnAction:] 0x1e7091300 UIKitCore! - [UIApplication sendAction:to: from :forEvent:] 0x10400a288 CTPocket! - [UIApplication(AutoTrack) sa_sendAction:to: from :forEvent:] 0x1e6b3a424 UIKitCore! - [UIControl sendAction:to:forEvent:] 0x1e6b3a744 UIKitCore! - [UIControl _sendActionsForEvents:withEvent:] |
接下来我们使用frida-trace工具,对以上调用栈进行逐个跟踪并打印入参,最终确定loginAuthCipherAsymmertric参数在[ESLoginService loginWithPhoneNbr:type:code:isChinatelecom:slidingTime:percentage:success:failure:]方法里生成的,打开IDA Pro并找到该方法,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | id __cdecl - [ESLoginService loginWithPhoneNbr: type :code:isChinatelecom:slidingTime:percentage:success:failure:](ESLoginService * self , SEL a2, id a3, signed __int64 a4, id a5, id a6, id a7, id a8, id a9, id a10) { id v10; / / x19 id v11; / / x20 id v12; / / x25 id v13; / / x21 ESLoginService * v14; / / x24 __int64 v15; / / x1 __int64 v16; / / x1 __int64 v17; / / x1 __int64 v18; / / x1 __int64 v19; / / x1 __int64 v20; / / x1 void * v21; / / x0 __int64 v22; / / x25 void * v23; / / x0 unsigned int v24; / / off signed int v25; / / w8 v10 = a8; v11 = a7; v12 = a6; v13 = a5; v14 = self ; objc_retain(a3, a2); objc_retain(a9, v15); objc_retain(a10, v16); objc_retain(v10, v17); objc_retain(v11, v18); objc_retain(v12, v19); objc_retain(v13, v20); v21 = objc_msgSend(&OBJC_CLASS___NSDate, "date" ); v22 = objc_retainAutoreleasedReturnValue(v21); - [ESLoginService setRequestDate:](v14, "setRequestDate:" , v22); objc_release(v22); v23 = (void * )((__int64 (__fastcall * )(void * ))((char * )off_105153068 + 92708870 ))(&OBJC_CLASS___NSDateFormatter); objc_msgSend(v23, "init" ); v24 = __ldar((unsigned int * )&dword_10577A424); if ( (unsigned int )&dword_10577A424 ) v25 = 7 ; else v25 = 34 ; JUMPOUT(__CS__, (char * ) * (&off_105153070 + v25) - dword_1051531F0[v25]); } |
傻眼了了吧。 JUMPOUT,也就是经常逆向会遇到的跳表,需要手动恢复。在这,我们使用frida stalker来跟踪该函数。ts代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | var addr = 0x0000000029FD08 / / loginWithPhoneNbr函数的起始地址 var mainModule = Process.enumerateModules()[ 0 ]; console.log(JSON.stringify(mainModule)); var mainName: string = mainModule.name; var baseAddr = Module.findBaseAddress(mainName)!; Interceptor.attach(baseAddr.add(addr), { onEnter: function(args) { console.log(addr.toString( 16 ), "= loginWithPhoneNbr onEnter =" ); var tid = Process.getCurrentThreadId(); Stalker.follow(tid, { events: { call: true, / / CALL instructions: yes please ret: false, / / RET instructions exec : false, / / all instructions: not recommended as it's block: false, / / block executed: coarse execution trace compile : false / / block compiled: useful for coverage }, transform: (iterator: StalkerArm64Iterator) = > { let instruction = iterator. next (); const startAddress = instruction!.address; var isAppCode = startAddress.compare(baseAddr.add(addr)) > = 0 && startAddress.compare(baseAddr.add(addr).add( 10000 )) = = = - 1 ; do { if (isAppCode) { if (instruction!.mnemonic = = = "bl" ) { iterator.putCallout((ctx) = > { var arm64Context = ctx as Arm64CpuContext; console.log( "bl x0 = " + new ObjC. Object (arm64Context.x0)) console.log( "bl x1 = " + arm64Context.x1.readCString()) }); } } iterator.keep(); } while ((instruction = iterator. next ()) ! = = null); } }) }, onLeave: function(retval) { console.log( "retval:" , new ObjC. Object (retval)) console.log(addr.toString( 16 ), "= loginWithPhoneNbr onLeave =" ); } }); |
获取到的关键日志如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 | 29fd08 = loginWithPhoneNbr onEnter = bl x0 = Utils bl x1 = createRSAStringWithPhone:authentication:timestamp:slidingTime:percentage: bl x0 = s21NZBfNx8ndPADDLDJCxCUOdWo45EL8jNr6TnHcwKwfnahCFpLGjXkZkhbGj2CZODR3LVjabCRw6Li3SLcvLtA6g0meKbUTgQzANtn9y2ttg9Svj9flxj9k18Ju5EBjloSbdd4ee4o0rHgn9W0iSSDs4zwKsqm + rOxYDZFHgWI = bl x1 = autorelease bl x0 = 0.000000 bl x1 = null bl x0 = 0 bl x1 = null bl x0 = NSMutableDictionary bl x1 = dictionary bl x0 = { } bl x1 = autorelease bl x0 = { } bl x1 = setObject:forKey: bl x0 = { accountType = ""; } bl x1 = setObjectOrNil:forKey: bl x0 = 222222 bl x1 = copyWithZone: bl x0 = ESGlobalFactory bl x1 = sharedInstance bl x0 = <ESGlobalFactory: 0x282e568b0 > bl x1 = sharedInstance bl x0 = <ESGlobalFactory: 0x282e568b0 > bl x1 = deviceInfo bl x0 = <ESDeviceInfo: 0x28225e120 > bl x1 = deviceInfo bl x0 = <ESDeviceInfo: 0x28225e120 > bl x1 = uuidForDevice bl x0 = ddecc53e1fce414e9e89b6ea47e567e3 bl x1 = autorelease bl x0 = { accountType = ""; authentication = 222222 ; } bl x1 = setObjectOrNil:forKey: bl x0 = ddecc53e1fce414e9e89b6ea47e567e3 bl x1 = copyWithZone: bl x0 = <ESDeviceInfo: 0x28225e120 > bl x1 = release bl x0 = <ESGlobalFactory: 0x282e568b0 > bl x1 = release bl x0 = { accountType = ""; authentication = 222222 ; deviceUid = ddecc53e1fce414e9e89b6ea47e567e3; } bl x1 = setObjectOrNil:forKey: bl x0 = 0 bl x1 = copyWithZone: bl x0 = NSString bl x1 = stringWithFormat: bl x0 = 4 bl x1 = autorelease bl x0 = { accountType = ""; authentication = 222222 ; deviceUid = ddecc53e1fce414e9e89b6ea47e567e3; isChinatelecom = 0 ; } bl x1 = setObjectOrNil:forKey: bl x0 = 4 bl x1 = copyWithZone: bl x0 = { accountType = ""; authentication = 222222 ; deviceUid = ddecc53e1fce414e9e89b6ea47e567e3; isChinatelecom = 0 ; loginType = 4 ; } bl x1 = setObjectOrNil:forKey: bl x0 = UIDevice bl x1 = currentDevice bl x0 = <UIDevice: 0x2820598e0 > bl x1 = currentDevice bl x0 = <UIDevice: 0x2820598e0 > bl x1 = systemVersion bl x0 = 12.5 . 5 bl x1 = X7Lyw9oGPgMDQ bl x0 = { accountType = ""; authentication = 222222 ; deviceUid = ddecc53e1fce414e9e89b6ea47e567e3; isChinatelecom = 0 ; loginType = 4 ; phoneNum = 13245678901 ; } bl x1 = setObjectOrNil:forKey: bl x0 = 12.5 . 5 bl x1 = 0 ��ו� bl x0 = <UIDevice: 0x2820598e0 > bl x1 = 0 ��ו� bl x0 = { accountType = ""; authentication = 222222 ; deviceUid = ddecc53e1fce414e9e89b6ea47e567e3; isChinatelecom = 0 ; loginType = 4 ; phoneNum = 13245678901 ; systemVersion = "12.5.5" ; } bl x1 = setObjectOrNil:forKey: bl x0 = ESDataAccessFactory bl x1 = sharedInstance bl x0 = <ESDataAccessFactory: 0x280472460 > bl x1 = sharedInstance bl x0 = <ESDataAccessFactory: 0x280472460 > bl x1 = highFrequencyNetworkingManager bl x0 = <ESNetworkingManager: 0x282e9cc90 > bl x1 = highFrequencyNetworkingManager bl x0 = <__NSStackBlock__: 0x16f4f0c28 > bl x1 = highFrequencyNetworkingManager bl x0 = <__NSStackBlock__: 0x16f4f0c88 > bl x1 = retain bl x0 = 20220905003215 bl x1 = retain bl x0 = 13245678901 bl x1 = null bl x0 = <ESNetworkingManager: 0x282e9cc90 > bl x1 = postWithURLString:parameters:modifiParamsBlock:success:failure: bl x0 = { content = { attach = iPhone; fieldData = { accountType = ""; authentication = 222222 ; deviceUid = ddecc53e1fce414e9e89b6ea47e567e3; isChinatelecom = 0 ; loginAuthCipherAsymmertric = "s21NZBfNx8ndPADDLDJCxCUOdWo45EL8jNr6TnHcwKwfnahCFpLGjXkZkhbGj2CZODR3LVjabCRw6Li3SLcvLtA6g0meKbUTgQzANtn9y2ttg9Svj9flxj9k18Ju5EBjloSbdd4ee4o0rHgn9W0iSSDs4zwKsqm+rOxYDZFHgWI=" ; loginType = 4 ; phoneNum = 13245678901 ; systemVersion = "12.5.5" ; }; }; headerInfos = { broadAccount = ""; broadToken = ""; clientType = "#9.4.0#channel50#iPhone 6#" ; code = userLoginNormal; shopId = 20004 ; source = 120002 ; sourcePassword = TiqmIZ; timestamp = 20220905003218 ; token = ""; userLoginName = ""; }; } bl x1 = a��� |
结果
通过日志,我们可以发现loginAuthCipherAsymmertric参数是使用Utils类的createRSAStringWithPhone:authentication:timestamp:slidingTime:percentage:方法生成的,伪代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 | id __cdecl + [Utils createRSAStringWithPhone:authentication:timestamp:slidingTime:percentage:](Utils_meta * self , SEL a2, id a3, id a4, id a5, id a6, id a7) { v7 = a7; v8 = a5; v9 = a4; v10 = a3; v11 = self ; v12 = objc_retain(a6, a2); v14 = objc_retain(v7, v13); v16 = (void * )objc_retain(v8, v15); v18 = objc_retain(v9, v17); v20 = objc_retain(v10, v19); v21 = + [ESGlobalFactory sharedInstance](&OBJC_CLASS___ESGlobalFactory, "sharedInstance" ); v22 = (void * )objc_retainAutoreleasedReturnValue(v21); v23 = v22; v24 = objc_msgSend(v22, "deviceInfo" ); v25 = (void * )objc_retainAutoreleasedReturnValue(v24); v26 = v25; v27 = objc_msgSend(v25, "uuidForDevice" ); v28 = objc_retainAutoreleasedReturnValue(v27); v29 = + [Utils substring:ToIndex:](&OBJC_CLASS___Utils, "substring:ToIndex:" , v28, 12LL ); v30 = objc_retainAutoreleasedReturnValue(v29); v31 = + [Utils deviceName](&OBJC_CLASS___Utils, "deviceName" ); v32 = objc_retainAutoreleasedReturnValue(v31); v33 = v32; v34 = + [Utils substring:ToIndex:](&OBJC_CLASS___Utils, "substring:ToIndex:" , v32, 10LL ); v35 = objc_retainAutoreleasedReturnValue(v34); v36 = objc_msgSend(&OBJC_CLASS___UIDevice, "currentDevice" ); v37 = (void * )objc_retainAutoreleasedReturnValue(v36); v38 = v37; v39 = objc_msgSend(v37, "systemVersion" ); v40 = objc_retainAutoreleasedReturnValue(v39); objc_release(v38); v41 = + [Utils substring:ToIndex:](&OBJC_CLASS___Utils, "substring:ToIndex:" , v40, 5LL ); v42 = objc_retainAutoreleasedReturnValue(v41); objc_release(v40); v43 = (void * )objc_alloc(&OBJC_CLASS___NSDateFormatter); v44 = objc_msgSend(v43, "init" ); v45 = v44; v46 = v44; objc_msgSend(v44, "setDateFormat:" , CFSTR( "yyyyMMddHHmmss" )); v47 = (void * )objc_alloc(&OBJC_CLASS___NSLocale); v48 = objc_msgSend(v47, "initWithLocaleIdentifier:" , CFSTR( "en_US" )); objc_msgSend(v45, "setLocale:" , v48); v49 = objc_msgSend(v16, "stringByReplacingOccurrencesOfString:withString:" , CFSTR( ":" ), &stru_10480C358); v50 = objc_retainAutoreleasedReturnValue(v49); objc_release(v16); v51 = + [Utils substring:ToIndex:](&OBJC_CLASS___Utils, "substring:ToIndex:" , v50, 14LL ); v52 = objc_retainAutoreleasedReturnValue(v51); v53 = v52; v54 = v52; v55 = objc_msgSend(v11, "substring:ToIndex:" , v12, 4LL ); v56 = objc_retainAutoreleasedReturnValue(v55); v57 = objc_msgSend(v11, "substring:ToIndex:" , v14, 2LL ); v58 = objc_retainAutoreleasedReturnValue(v57); v59 = objc_msgSend(v11, "substring:ToIndex:" , v18, 6LL ); v60 = objc_retainAutoreleasedReturnValue(v59); v61 = objc_msgSend(v11, "substring:ToIndex:" , v20, 11LL ); v62 = objc_retainAutoreleasedReturnValue(v61); v63 = objc_msgSend( &OBJC_CLASS___NSString, "stringWithFormat:" , CFSTR( "%@%@%@%@%@%@%@%@" ), v35, v42, v30, v62, v53, v60, v56, v58); v64 = objc_retainAutoreleasedReturnValue(v63); v65 = + [Utils loginRsaKey2](&OBJC_CLASS___Utils, "loginRsaKey2" ); v66 = objc_retainAutoreleasedReturnValue(v65); v67 = v66; v68 = + [RSAEncryptor encryptString:publicKey:](&OBJC_CLASS___RSAEncryptor, "encryptString:publicKey:" , v64, v66); v69 = objc_retainAutoreleasedReturnValue(v68); return ( id )objc_autoreleaseReturnValue(v69); } |
这就是生成loginAuthCipherAsymmertric的最终函数。