阅读此文档的过程中遇到任何问题,请关注公众号【移动端Android和iOS开发技术分享】或加QQ群【812546729

使用frida stalker分析某营业厅的签名算法。

  • mac系统

  • frida-ios-dump:砸壳

  • Charles:抓包

  • 已越狱iOS设备:脱壳及frida调试

  • IDA Pro:静态分析

在账号密码登录页,点击登录,通过Charles抓包获取到关键词为loginAuthCipherAsymmertric,这也就是我们的切入点:

image-20220902221846037

使用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. onEnter(log, args, state) {
  3. var arg2 = new ObjC.Object(args[2])
  4. log(`-[NSMutableURLRequest setHTTPBody:${arg2.bytes().readUtf8String()}]`);
  5. },
  6. onLeave(log, retval, state) {
  7. }
  8. }

点击登录后,获取到的日志如下:

  1. -[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&currentPage=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]
  2. -[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"}}]
  3. -[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&currentPage=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. onEnter(log, args, state) {
  3. var arg2 = new ObjC.Object(args[2])
  4. log(`-[NSMutableURLRequest setHTTPBody:${arg2.bytes().readUtf8String()}]`);
  5. log('NSMutableURLRequest setHTTPBody called from:\n' +
  6. Thread.backtrace(this.context, Backtracer.ACCURATE)
  7. .map(DebugSymbol.fromAddress).join('\n') + '\n');
  8. },
  9. onLeave(log, retval, state) {
  10. }
  11. }

获取到的堆栈信息如下:

  1. 0x10397f4d8 CTPocket!-[AFJSONRequestSerializer requestBySerializingRequest:withParameters:error:]
  2. 0x10397ae50 CTPocket!-[AFHTTPRequestSerializer requestWithMethod:URLString:parameters:error:]
  3. 0x10394edac CTPocket!-[AFHTTPSessionManager dataTaskWithHTTPMethod:URLString:parameters:headers:uploadProgress:downloadProgress:success:failure:]
  4. 0x10394e378 CTPocket!-[AFHTTPSessionManager POST:parameters:headers:progress:success:failure:]
  5. 0x101b8fae8 CTPocket!-[ESHttpSessionManager postWithHost:urlString:parameters:success:failure:]
  6. 0x1015f91c8 CTPocket!-[ESNetworkingManager postWithURLString:parameters:modifiParamsBlock:success:failure:]
  7. 0x100c14da4 CTPocket!-[ESLoginService loginWithPhoneNbr:type:code:isChinatelecom:slidingTime:percentage:success:failure:]
  8. 0x101f85f58 CTPocket!-[ESBindLoginViewController phoneLoginWithPhoneNbr:type:code:slidingTime:percentage:isChinatelecom:isBind:failureBlock:]
  9. 0x101f6a060 CTPocket!-[ESBindLoginViewController phoneLoginViewController:inputView:didSliderWithSlidingTime:Percentage:phoneNbr:passwd:loginType:isChinatelecom:isBind:failureBlock:]
  10. 0x102ccb964 CTPocket!-[ESLoginViewController phoneLoginView:inputView:didSliderWithSlidingTime:Percentage:phoneNbr:passwd:loginType:isChinatelecom:isBind:]
  11. 0x101261728 CTPocket!-[ESPhoneLoginView passwordInputView:sliderWithSlidingTime:Percentage:phoneNbr:passwd:]
  12. 0x101e73d10 CTPocket!-[PasswordInputView commitBtnAction:]
  13. 0x1e7091300 UIKitCore!-[UIApplication sendAction:to:from:forEvent:]
  14. 0x10400a288 CTPocket!-[UIApplication(AutoTrack) sa_sendAction:to:from:forEvent:]
  15. 0x1e6b3a424 UIKitCore!-[UIControl sendAction:to:forEvent:]
  16. 0x1e6b3a744 UIKitCore!-[UIControl _sendActionsForEvents:withEvent:]

接下来我们使用frida-trace工具,对以上调用栈进行逐个跟踪并打印入参,最终确定loginAuthCipherAsymmertric参数在[ESLoginService loginWithPhoneNbr:type:code:isChinatelecom:slidingTime:percentage:success:failure:]方法里生成的,打开IDA Pro并找到该方法,代码如下:

  1. 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)
  2. {
  3. id v10; // x19
  4. id v11; // x20
  5. id v12; // x25
  6. id v13; // x21
  7. ESLoginService *v14; // x24
  8. __int64 v15; // x1
  9. __int64 v16; // x1
  10. __int64 v17; // x1
  11. __int64 v18; // x1
  12. __int64 v19; // x1
  13. __int64 v20; // x1
  14. void *v21; // x0
  15. __int64 v22; // x25
  16. void *v23; // x0
  17. unsigned int v24; // off
  18. signed int v25; // w8
  19. v10 = a8;
  20. v11 = a7;
  21. v12 = a6;
  22. v13 = a5;
  23. v14 = self;
  24. objc_retain(a3, a2);
  25. objc_retain(a9, v15);
  26. objc_retain(a10, v16);
  27. objc_retain(v10, v17);
  28. objc_retain(v11, v18);
  29. objc_retain(v12, v19);
  30. objc_retain(v13, v20);
  31. v21 = objc_msgSend(&OBJC_CLASS___NSDate, "date");
  32. v22 = objc_retainAutoreleasedReturnValue(v21);
  33. -[ESLoginService setRequestDate:](v14, "setRequestDate:", v22);
  34. objc_release(v22);
  35. v23 = (void *)((__int64 (__fastcall *)(void *))((char *)off_105153068 + 92708870))(&OBJC_CLASS___NSDateFormatter);
  36. objc_msgSend(v23, "init");
  37. v24 = __ldar((unsigned int *)&dword_10577A424);
  38. if ( (unsigned int)&dword_10577A424 )
  39. v25 = 7;
  40. else
  41. v25 = 34;
  42. JUMPOUT(__CS__, (char *)*(&off_105153070 + v25) - dword_1051531F0[v25]);
  43. }

傻眼了了吧。 JUMPOUT,也就是经常逆向会遇到的跳表,需要手动恢复。在这,我们使用frida stalker来跟踪该函数。ts代码如下:

  1. var addr = 0x0000000029FD08 // loginWithPhoneNbr函数的起始地址
  2. var mainModule = Process.enumerateModules()[0];
  3. console.log(JSON.stringify(mainModule));
  4. var mainName: string = mainModule.name;
  5. var baseAddr = Module.findBaseAddress(mainName)!;
  6. Interceptor.attach(baseAddr.add(addr), {
  7. onEnter: function(args) {
  8. console.log(addr.toString(16), "= loginWithPhoneNbr onEnter =");
  9. var tid = Process.getCurrentThreadId();
  10. Stalker.follow(tid, {
  11. events: {
  12. call: true, // CALL instructions: yes please
  13. ret: false, // RET instructions
  14. exec: false, // all instructions: not recommended as it's
  15. block: false, // block executed: coarse execution trace
  16. compile: false // block compiled: useful for coverage
  17. },
  18. transform: (iterator: StalkerArm64Iterator) => {
  19. let instruction = iterator.next();
  20. const startAddress = instruction!.address;
  21. var isAppCode = startAddress.compare(baseAddr.add(addr)) >= 0 && startAddress.compare(baseAddr.add(addr).add(10000)) === -1;
  22. do {
  23. if (isAppCode) {
  24. if (instruction!.mnemonic === "bl") {
  25. iterator.putCallout((ctx) => {
  26. var arm64Context = ctx as Arm64CpuContext;
  27. console.log("bl x0 = " + new ObjC.Object(arm64Context.x0))
  28. console.log("bl x1 = " + arm64Context.x1.readCString())
  29. });
  30. }
  31. }
  32. iterator.keep();
  33. } while ((instruction = iterator.next()) !== null);
  34. }
  35. })
  36. }, onLeave: function(retval) {
  37. console.log("retval:", new ObjC.Object(retval))
  38. console.log(addr.toString(16), "= loginWithPhoneNbr onLeave =");
  39. }
  40. });

获取到的关键日志如下:

  1. 29fd08 = loginWithPhoneNbr onEnter =
  2. bl x0 = Utils
  3. bl x1 = createRSAStringWithPhone:authentication:timestamp:slidingTime:percentage:
  4. bl x0 = s21NZBfNx8ndPADDLDJCxCUOdWo45EL8jNr6TnHcwKwfnahCFpLGjXkZkhbGj2CZODR3LVjabCRw6Li3SLcvLtA6g0meKbUTgQzANtn9y2ttg9Svj9flxj9k18Ju5EBjloSbdd4ee4o0rHgn9W0iSSDs4zwKsqm+rOxYDZFHgWI=
  5. bl x1 = autorelease
  6. bl x0 = 0.000000
  7. bl x1 = null
  8. bl x0 = 0
  9. bl x1 = null
  10. bl x0 = NSMutableDictionary
  11. bl x1 = dictionary
  12. bl x0 = {
  13. }
  14. bl x1 = autorelease
  15. bl x0 = {
  16. }
  17. bl x1 = setObject:forKey:
  18. bl x0 = {
  19. accountType = "";
  20. }
  21. bl x1 = setObjectOrNil:forKey:
  22. bl x0 = 222222
  23. bl x1 = copyWithZone:
  24. bl x0 = ESGlobalFactory
  25. bl x1 = sharedInstance
  26. bl x0 = <ESGlobalFactory: 0x282e568b0>
  27. bl x1 = sharedInstance
  28. bl x0 = <ESGlobalFactory: 0x282e568b0>
  29. bl x1 = deviceInfo
  30. bl x0 = <ESDeviceInfo: 0x28225e120>
  31. bl x1 = deviceInfo
  32. bl x0 = <ESDeviceInfo: 0x28225e120>
  33. bl x1 = uuidForDevice
  34. bl x0 = ddecc53e1fce414e9e89b6ea47e567e3
  35. bl x1 = autorelease
  36. bl x0 = {
  37. accountType = "";
  38. authentication = 222222;
  39. }
  40. bl x1 = setObjectOrNil:forKey:
  41. bl x0 = ddecc53e1fce414e9e89b6ea47e567e3
  42. bl x1 = copyWithZone:
  43. bl x0 = <ESDeviceInfo: 0x28225e120>
  44. bl x1 = release
  45. bl x0 = <ESGlobalFactory: 0x282e568b0>
  46. bl x1 = release
  47. bl x0 = {
  48. accountType = "";
  49. authentication = 222222;
  50. deviceUid = ddecc53e1fce414e9e89b6ea47e567e3;
  51. }
  52. bl x1 = setObjectOrNil:forKey:
  53. bl x0 = 0
  54. bl x1 = copyWithZone:
  55. bl x0 = NSString
  56. bl x1 = stringWithFormat:
  57. bl x0 = 4
  58. bl x1 = autorelease
  59. bl x0 = {
  60. accountType = "";
  61. authentication = 222222;
  62. deviceUid = ddecc53e1fce414e9e89b6ea47e567e3;
  63. isChinatelecom = 0;
  64. }
  65. bl x1 = setObjectOrNil:forKey:
  66. bl x0 = 4
  67. bl x1 = copyWithZone:
  68. bl x0 = {
  69. accountType = "";
  70. authentication = 222222;
  71. deviceUid = ddecc53e1fce414e9e89b6ea47e567e3;
  72. isChinatelecom = 0;
  73. loginType = 4;
  74. }
  75. bl x1 = setObjectOrNil:forKey:
  76. bl x0 = UIDevice
  77. bl x1 = currentDevice
  78. bl x0 = <UIDevice: 0x2820598e0>
  79. bl x1 = currentDevice
  80. bl x0 = <UIDevice: 0x2820598e0>
  81. bl x1 = systemVersion
  82. bl x0 = 12.5.5
  83. bl x1 = X7Lyw9oGPgMDQ
  84. bl x0 = {
  85. accountType = "";
  86. authentication = 222222;
  87. deviceUid = ddecc53e1fce414e9e89b6ea47e567e3;
  88. isChinatelecom = 0;
  89. loginType = 4;
  90. phoneNum = 13245678901;
  91. }
  92. bl x1 = setObjectOrNil:forKey:
  93. bl x0 = 12.5.5
  94. bl x1 = 0��ו�
  95. bl x0 = <UIDevice: 0x2820598e0>
  96. bl x1 = 0��ו�
  97. bl x0 = {
  98. accountType = "";
  99. authentication = 222222;
  100. deviceUid = ddecc53e1fce414e9e89b6ea47e567e3;
  101. isChinatelecom = 0;
  102. loginType = 4;
  103. phoneNum = 13245678901;
  104. systemVersion = "12.5.5";
  105. }
  106. bl x1 = setObjectOrNil:forKey:
  107. bl x0 = ESDataAccessFactory
  108. bl x1 = sharedInstance
  109. bl x0 = <ESDataAccessFactory: 0x280472460>
  110. bl x1 = sharedInstance
  111. bl x0 = <ESDataAccessFactory: 0x280472460>
  112. bl x1 = highFrequencyNetworkingManager
  113. bl x0 = <ESNetworkingManager: 0x282e9cc90>
  114. bl x1 = highFrequencyNetworkingManager
  115. bl x0 = <__NSStackBlock__: 0x16f4f0c28>
  116. bl x1 = highFrequencyNetworkingManager
  117. bl x0 = <__NSStackBlock__: 0x16f4f0c88>
  118. bl x1 = retain
  119. bl x0 = 20220905003215
  120. bl x1 = retain
  121. bl x0 = 13245678901
  122. bl x1 = null
  123. bl x0 = <ESNetworkingManager: 0x282e9cc90>
  124. bl x1 = postWithURLString:parameters:modifiParamsBlock:success:failure:
  125. bl x0 = {
  126. content = {
  127. attach = iPhone;
  128. fieldData = {
  129. accountType = "";
  130. authentication = 222222;
  131. deviceUid = ddecc53e1fce414e9e89b6ea47e567e3;
  132. isChinatelecom = 0;
  133. loginAuthCipherAsymmertric = "s21NZBfNx8ndPADDLDJCxCUOdWo45EL8jNr6TnHcwKwfnahCFpLGjXkZkhbGj2CZODR3LVjabCRw6Li3SLcvLtA6g0meKbUTgQzANtn9y2ttg9Svj9flxj9k18Ju5EBjloSbdd4ee4o0rHgn9W0iSSDs4zwKsqm+rOxYDZFHgWI=";
  134. loginType = 4;
  135. phoneNum = 13245678901;
  136. systemVersion = "12.5.5";
  137. };
  138. };
  139. headerInfos = {
  140. broadAccount = "";
  141. broadToken = "";
  142. clientType = "#9.4.0#channel50#iPhone 6#";
  143. code = userLoginNormal;
  144. shopId = 20004;
  145. source = 120002;
  146. sourcePassword = TiqmIZ;
  147. timestamp = 20220905003218;
  148. token = "";
  149. userLoginName = "";
  150. };
  151. }
  152. bl x1 = a���

通过日志,我们可以发现loginAuthCipherAsymmertric参数是使用Utils类的createRSAStringWithPhone:authentication:timestamp:slidingTime:percentage:方法生成的,伪代码如下:

  1. id __cdecl +[Utils createRSAStringWithPhone:authentication:timestamp:slidingTime:percentage:](Utils_meta *self, SEL a2, id a3, id a4, id a5, id a6, id a7)
  2. {
  3. v7 = a7;
  4. v8 = a5;
  5. v9 = a4;
  6. v10 = a3;
  7. v11 = self;
  8. v12 = objc_retain(a6, a2);
  9. v14 = objc_retain(v7, v13);
  10. v16 = (void *)objc_retain(v8, v15);
  11. v18 = objc_retain(v9, v17);
  12. v20 = objc_retain(v10, v19);
  13. v21 = +[ESGlobalFactory sharedInstance](&OBJC_CLASS___ESGlobalFactory, "sharedInstance");
  14. v22 = (void *)objc_retainAutoreleasedReturnValue(v21);
  15. v23 = v22;
  16. v24 = objc_msgSend(v22, "deviceInfo");
  17. v25 = (void *)objc_retainAutoreleasedReturnValue(v24);
  18. v26 = v25;
  19. v27 = objc_msgSend(v25, "uuidForDevice");
  20. v28 = objc_retainAutoreleasedReturnValue(v27);
  21. v29 = +[Utils substring:ToIndex:](&OBJC_CLASS___Utils, "substring:ToIndex:", v28, 12LL);
  22. v30 = objc_retainAutoreleasedReturnValue(v29);
  23. v31 = +[Utils deviceName](&OBJC_CLASS___Utils, "deviceName");
  24. v32 = objc_retainAutoreleasedReturnValue(v31);
  25. v33 = v32;
  26. v34 = +[Utils substring:ToIndex:](&OBJC_CLASS___Utils, "substring:ToIndex:", v32, 10LL);
  27. v35 = objc_retainAutoreleasedReturnValue(v34);
  28. v36 = objc_msgSend(&OBJC_CLASS___UIDevice, "currentDevice");
  29. v37 = (void *)objc_retainAutoreleasedReturnValue(v36);
  30. v38 = v37;
  31. v39 = objc_msgSend(v37, "systemVersion");
  32. v40 = objc_retainAutoreleasedReturnValue(v39);
  33. objc_release(v38);
  34. v41 = +[Utils substring:ToIndex:](&OBJC_CLASS___Utils, "substring:ToIndex:", v40, 5LL);
  35. v42 = objc_retainAutoreleasedReturnValue(v41);
  36. objc_release(v40);
  37. v43 = (void *)objc_alloc(&OBJC_CLASS___NSDateFormatter);
  38. v44 = objc_msgSend(v43, "init");
  39. v45 = v44;
  40. v46 = v44;
  41. objc_msgSend(v44, "setDateFormat:", CFSTR("yyyyMMddHHmmss"));
  42. v47 = (void *)objc_alloc(&OBJC_CLASS___NSLocale);
  43. v48 = objc_msgSend(v47, "initWithLocaleIdentifier:", CFSTR("en_US"));
  44. objc_msgSend(v45, "setLocale:", v48);
  45. v49 = objc_msgSend(v16, "stringByReplacingOccurrencesOfString:withString:", CFSTR(":"), &stru_10480C358);
  46. v50 = objc_retainAutoreleasedReturnValue(v49);
  47. objc_release(v16);
  48. v51 = +[Utils substring:ToIndex:](&OBJC_CLASS___Utils, "substring:ToIndex:", v50, 14LL);
  49. v52 = objc_retainAutoreleasedReturnValue(v51);
  50. v53 = v52;
  51. v54 = v52;
  52. v55 = objc_msgSend(v11, "substring:ToIndex:", v12, 4LL);
  53. v56 = objc_retainAutoreleasedReturnValue(v55);
  54. v57 = objc_msgSend(v11, "substring:ToIndex:", v14, 2LL);
  55. v58 = objc_retainAutoreleasedReturnValue(v57);
  56. v59 = objc_msgSend(v11, "substring:ToIndex:", v18, 6LL);
  57. v60 = objc_retainAutoreleasedReturnValue(v59);
  58. v61 = objc_msgSend(v11, "substring:ToIndex:", v20, 11LL);
  59. v62 = objc_retainAutoreleasedReturnValue(v61);
  60. v63 = objc_msgSend(
  61. &OBJC_CLASS___NSString,
  62. "stringWithFormat:",
  63. CFSTR("%@%@%@%@%@%@%@%@"),
  64. v35,
  65. v42,
  66. v30,
  67. v62,
  68. v53,
  69. v60,
  70. v56,
  71. v58);
  72. v64 = objc_retainAutoreleasedReturnValue(v63);
  73. v65 = +[Utils loginRsaKey2](&OBJC_CLASS___Utils, "loginRsaKey2");
  74. v66 = objc_retainAutoreleasedReturnValue(v65);
  75. v67 = v66;
  76. v68 = +[RSAEncryptor encryptString:publicKey:](&OBJC_CLASS___RSAEncryptor, "encryptString:publicKey:", v64, v66);
  77. v69 = objc_retainAutoreleasedReturnValue(v68);
  78. return (id)objc_autoreleaseReturnValue(v69);
  79. }

这就是生成loginAuthCipherAsymmertric的最终函数。

阅读此文档的过程中遇到任何问题,请关注公众号【移动端Android和iOS开发技术分享】或加QQ群【812546729

IMG_4048

版权声明:本文为小陈原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/witchan/p/16656912.html