某FM密码加密分析IOS

某FM密码加密分析

个人感觉这个分析用的手段比较典型,所以记录下。个人感觉ios有些封装特点在逆向过程中还是要有一定的认识的,不然就真是分析半天头疼的不行了。

抓包分析可疑数据

这一部分大致说一下,大致看了以了一下,没有看到跟登录的手机号和密码相关的关键字。应该是加密了,倒是path路径有个passport-sign-mobile/v2的网址路径,不过这是后话,开始的时候没看到这里。不过个人感觉从这里入手,应该也不会太容易,不过应该也可行。

界面分析

既然抓不出来啥,我就分析下登录界面

<XMRegisterAndLoginView: 0x1308a2410; frame = (0 0; 320 460); gestureRecognizers = <NSArray: 0x1307a3dc0>; layer = <CALayer: 0x13084e910>>
   |    |    |    |    |    |    |    | <UITextField: 0x13087aa00; frame = (40 30; 240 44); text = '18104849732'; clipsToBounds = YES; opaque = NO; gestureRecognizers = <NSArray: 0x130873d00>; layer = <CALayer: 0x130894920>>
   |    |    |    |    |    |    |    |    | <UIView: 0x13099c850; frame = (0 2; 105 40); layer = <CALayer: 0x1305373d0>>
   |    |    |    |    |    |    |    |    |    | <UIButton: 0x130742b60; frame = (0 0; 80 40); opaque = NO; tag = 100; layer = <CALayer: 0x130585e80>>
   |    |    |    |    |    |    |    |    |    |    | <UIImageView: 0x130868ce0; frame = (60 13; 14 14); clipsToBounds = YES; opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x1308ae3a0>>
   |    |    |    |    |    |    |    |    |    |    | <UIButtonLabel: 0x130797e60; frame = (17 10.5; 30 19.5); text = '+86'; opaque = NO; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x13058bc90>>
   |    |    |    |    |    |    |    |    |    | <UIView: 0x13092f0e0; frame = (90 10; 1 20); layer = <CALayer: 0x130958750>>
   |    |    |    |    |    |    |    |    | <UITextFieldLabel: 0x1306a4380; frame = (105 0; 135 42.5); text = '18104849732'; opaque = NO; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x130833700>>
   |    |    |    |    |    |    |    | <UITextField: 0x1309865c0; frame = (40 89; 240 44); text = 'fyg'; clipsToBounds = YES; opaque = NO; gestureRecognizers = <NSArray: 0x13088b9f0>; layer = <CALayer: 0x130556600>>
   |    |    |    |    |    |    |    |    | <UIImageView: 0x1302f5290; frame = (0 7; 30 30); opaque = NO; userInteractionEnabled = NO; layer = <CALayer: 0x1306b43d0>>
   |    |    |    |    |    |    |    |    | <UITextFieldLabel: 0x130936840; frame = (30 0; 210 42.5); text = '●●●'; opaque = NO; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x13075aef0>>
   |    |    |    |    |    |    |    |    |    | <_UILabelContentLayer: 0x13092a690> (layer)
   |    |    |    |    |    |    |    | <UIButton: 0x130889bd0; frame = (40 148; 240 40); opaque = NO; layer = <CALayer: 0x13084ffb0>>
   |    |    |    |    |    |    |    |    | <UIButtonLabel: 0x1302f7920; frame = (104 10.5; 32 19.5); text = '登录'; opaque = NO; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x13063f460>>
   |    |    |    |    |    |    |    |    |    | <_UILabelContentLayer: 0x13058d3d0> (layer)
   |    |    |    |    |    |    |    | <UIButton: 0x1307fb070; frame = (220 194; 60 27); opaque = NO; layer = <CALayer: 0x13095da50>>
   |    |    |    |    |    |    |    |    | <UIButtonLabel: 0x1307dbbb0; frame = (0 6.5; 60 14.5); text = '忘记密码?'; opaque = NO; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x13098ae30>>
   |    |    |    |    |    |    |    |    |    | <_UILabelContentLayer: 0x13075bed0> (layer)
   |    |    |    |    |    |    |    | <UIView: 0x1309ae010; frame = (30 261; 260 20); layer = <CALayer: 0x13075c690>>
   |    |    |    |    |    |    |    |    | <UILabel: 0x1305a3600; frame = (88 0; 84 20); text = '其他方式登录'; userInteractionEnabled = NO; layer = <_UILabelLayer: 0x13091a880>>
   |    |    |    |

我这里用的工具是Frida写的一个界面的工具,为什么?因为这个东西,支持中文格式,很好用。当然如果用不惯可以用 cycript 或reveal等。
至于frida工具,可以参看我的博客ios逆向神器frida。
这里我们找到“登录”关键字,看到它所属的类XMRegisterAndLoginView。可以看到0x1308a2410是他的地址。
验证下

#cycript -p 程序名称
#[#0x1302f7920 setHidden:YES]

登录标签消失了。说明我们分析正确。

ida静态分析和动态验证

这里重点分析XMRegisterAndLoginView

-[XMRegisterAndLoginView dealloc]   __text  000000010023E5B4    000000A4            R   .   .   .   B   T   .
-[XMRegisterAndLoginView initWithFrame:]    __text  000000010023E658    000000A8            R   .   .   .   B   T   .
-[XMRegisterAndLoginView layoutSubviews]    __text  000000010023E700    000008B4            R   .   .   .   B   T   .
-[XMRegisterAndLoginView customInit]    __text  000000010023EFB4    00001C5C            R   .   .   .   B   T   .
-[XMRegisterAndLoginView resignTextfieldAllResponder]   __text  0000000100240C10    0000007C            R   .   .   .   B   T   .
-[XMRegisterAndLoginView becomeSelected]    __text  0000000100240C8C    00000080            R   .   .   .   B   T   .
-[XMRegisterAndLoginView tapPressed]    __text  0000000100240D0C    0000000C            R   .   .   .   .   T   .
-[XMRegisterAndLoginView registerButtonPressed] __text  0000000100240D18    000000B8            R   .   .   .   B   T   .
-[XMRegisterAndLoginView loginButtonPressed]    __text  0000000100240DD0    000000B8            R   .   .   .   B   T   .
-[XMRegisterAndLoginView forgetPasswordButtonPressed]   __text  0000000100240E88    000000B8            R   .   .   .   B   T   .
-[XMRegisterAndLoginView thirdPartyLoginPressed:]   __text  0000000100240F40    00000124            R   .   .   .   B   T   .
-[XMRegisterAndLoginView showLastLoginTip:] __text  0000000100241064    000001E4            R   .   .   .   B   T   .
-[XMRegisterAndLoginView updateCountryCode:]    __text  0000000100241760    000000B8            R   .   .   .   B   T   .
-[XMRegisterAndLoginView countryCode:]  __text  0000000100241818    000000B0            R   .   .   .   B   T   .
-[XMRegisterAndLoginView usernameTextField] __text  00000001002418C8    00000010            R   .   .   .   .   T   .
-[XMRegisterAndLoginView setUsernameTextField:] __text  00000001002418D8    00000014            R   .   .   .   .   T   .
-[XMRegisterAndLoginView passwordTextField] __text  00000001002418EC    00000010            R   .   .   .   .   T   .
-[XMRegisterAndLoginView setPasswordTextField:] __text  00000001002418FC    00000014            R   .   .   .   .   T   .
-[XMRegisterAndLoginView loginButton]   __text  0000000100241910    00000010            R   .   .   .   .   T   .
-[XMRegisterAndLoginView setLoginButton:]   __text  0000000100241920    00000014            R   .   .   .   .   T   .
-[XMRegisterAndLoginView forgetPasswordButton]  __text  0000000100241934    00000010            R   .   .   .   .   T   .
-[XMRegisterAndLoginView setForgetPasswordButton:]  __text  0000000100241944    00000014            R   .   .   .   .   T   .
-[XMRegisterAndLoginView delegate]  __text  0000000100241958    00000020            R   .   .   .   B   T   .
-[XMRegisterAndLoginView setDelegate:]  __text  0000000100241978    00000014            R   .   .   .   .   T   .
-[XMRegisterAndLoginView countryCodeButton] __text  000000010024198C    00000010            R   .   .   .   .   T   .
-[XMRegisterAndLoginView setCountryCodeButton:] __text  000000010024199C    00000014            R   .   .   .   .   T   .
-[XMRegisterAndLoginView .cxx_destruct] __text  00000001002419B0    0000012C            R   .   .   .   B   T   .

通过cycript lldb等各种工具,验证以上可以函数的功能。显而易见,loginButtonPressed登录按钮激发函数,passwordTextField获取密码框字符串。其他其实大概也从名字也能开出来。当然这里其实我在这走了一点弯路,因为thoes挂钩不上,我还以为分析错误了。搞了半天,但用cycript和lldb又验证了下上面一些函数才发现是正确的,这里离应该有反挂钩。反反挂钩也懒得做了,不用挂钩了。

void __cdecl -[XMRegisterAndLoginView loginButtonPressed](#928 *self, SEL a2)
{
  #928 *v2; // x19
  #927 *v3; // x0
  void *v4; // x22
  int v5; // w23
  #927 *v6; // x0
  void *v7; // x19

  v2 = self;
  objc_msgSend((void *)self, "resignTextfieldAllResponder");
  v3 = ((#927 *(__cdecl *)(#928 *, SEL))objc_msgSend)(v2, "delegate");
  v4 = (void *)objc_retainAutoreleasedReturnValue(v3);
  v5 = (unsigned __int64)objc_msgSend(v4, "respondsToSelector:", "registerAndLoginViewDidClickedLogin");
  objc_release(v4);
  if ( v5 )
  {
    v6 = ((#927 *(__cdecl *)(#928 *, SEL))objc_msgSend)(v2, "delegate");
    v7 = (void *)objc_retainAutoreleasedReturnValue(v6);
    objc_msgSend(v7, "registerAndLoginViewDidClickedLogin");
    JUMPOUT(objc_release);
  }
}

这里registerAndLoginViewDidClickedLogin属于
XMRegisterAndLoginViewController registerAndLoginViewDidClickedLogin

void __cdecl -[XMRegisterAndLoginViewController registerAndLoginViewDidClickedLogin](#4797 *self, SEL a2)
{
  #4797 *v2; // x19
  #2972 *v3; // x0
  void *v4; // x21
  void *v5; // x0
  void *v6; // x0
  void *v7; // x22
  void *v8; // x0
  __int64 v9; // x24
  #2972 *v10; // x0
  void *v11; // x20
  void *v12; // x0
  void *v13; // x0
  void *v14; // x21
  void *v15; // x0
  __int64 v16; // x22

  v2 = self;
  if ( (unsigned int)objc_msgSend((void *)self, "baseSanityCheck") )
  {
    v3 = ((#2972 *(__cdecl *)(#4797 *, SEL))objc_msgSend)(v2, "registerAndLoginInfo");
    v4 = (void *)objc_retainAutoreleasedReturnValue(v3);
    v5 = objc_msgSend(*((void **)v2 + 24), "usernameTextField");
    v6 = (void *)objc_retainAutoreleasedReturnValue(v5);
    v7 = v6;
    v8 = objc_msgSend(v6, "text");
    v9 = objc_retainAutoreleasedReturnValue(v8);
    objc_msgSend(v4, "setUsername:", v9);
    objc_release(v9);
    objc_release(v7);
    objc_release(v4);
    v10 = ((#2972 *(__cdecl *)(#4797 *, SEL))objc_msgSend)(v2, "registerAndLoginInfo");
    v11 = (void *)objc_retainAutoreleasedReturnValue(v10);
    v12 = objc_msgSend(*((void **)v2 + 24), "passwordTextField");
    v13 = (void *)objc_retainAutoreleasedReturnValue(v12);
    v14 = v13;
    v15 = objc_msgSend(v13, "text");
    v16 = objc_retainAutoreleasedReturnValue(v15);
    objc_msgSend(v11, "setLoginPassword:", v16);
    objc_release(v16);
    objc_release(v14);
    objc_release(v11);
    JUMPOUT(objc_msgSend);
  }
}

发现这里只是给一个类XMRegisterAndLoginItem赋值
根据XMRegisterAndLoginItem loginpassword调用函数找到了XMRegisterAndLoginViewController requestSecurityLogin
当然这里是通过动态lldb验证的,因为这里调用的函数并不多。

LABEL_8:
    v33 = objc_msgSend(v19, "registerAndLoginInfo");
    v34 = (void *)objc_retainAutoreleasedReturnValue(v33);
    v35 = (__int64)v34;
    v36 = objc_msgSend(v34, "username");
    v37 = objc_retainAutoreleasedReturnValue(v36);
    objc_msgSend(v26, "setObject:forKey:", v37, CFSTR("account"));
    objc_release(v37);
    v38 = v35;
  }
  else
  {
    v39 = objc_msgSend(v24, "substringFromIndex:", 1LL);
    v40 = objc_retainAutoreleasedReturnValue(v39);
    v41 = objc_msgSend(v19, "registerAndLoginInfo");
    v42 = (void *)objc_retainAutoreleasedReturnValue(v41);
    v43 = v42;
    v44 = objc_msgSend(v42, "username");
    v45 = objc_retainAutoreleasedReturnValue(v44);
    v46 = v45;
    v47 = objc_msgSend(&OBJC_CLASS___NSString, "stringWithFormat:", CFSTR("%@-%@"), v40, v45);
    v48 = objc_retainAutoreleasedReturnValue(v47);
    objc_msgSend(v26, "setObject:forKey:", v48, CFSTR("account"));
    objc_release(v48);
    objc_release(v46);
    objc_release(v43);
    v38 = v40;
  }
  objc_release(v38);
  v49 = objc_msgSend(v19, "registerAndLoginInfo");
  v50 = (void *)objc_retainAutoreleasedReturnValue(v49);
  v51 = v50;
  v52 = objc_msgSend(v50, "loginPassword");
  v53 = (void *)objc_retainAutoreleasedReturnValue(v52);
  v54 = v53;
  v55 = objc_msgSend(v53, "md5String");
  v56 = objc_retainAutoreleasedReturnValue(v55);
  objc_msgSend(v26, "setObject:forKey:", v56, CFSTR("password"));
  objc_release(v56);
  objc_release(v54);
  objc_release(v51);
  v57 = objc_msgSend(v19, "loginToken");
  v58 = objc_retainAutoreleasedReturnValue(v57);
  objc_msgSend(v26, "setObject:forKey:", v58, CFSTR("nonce"));
  objc_release(v58);
  v59 = objc_msgSend(&OBJC_CLASS___NSString, "securetyStringFromDictionary:", v26);
  v60 = (void *)objc_retainAutoreleasedReturnValue(v59);
  v61 = v60;
  v62 = objc_msgSend(v60, "lowercaseString");
  v63 = (void *)objc_retainAutoreleasedReturnValue(v62);
  v64 = v63;
  v65 = objc_msgSend(v63, "md5String");
  v66 = objc_retainAutoreleasedReturnValue(v65);
  objc_msgSend(v26, "setObject:forKey:", v66, CFSTR("signature"));
  objc_release(v66);
  objc_release(v64);
  v67 = objc_msgSend(&OBJC_CLASS___NSJSONSerialization, "dataWithJSONObject:options:error:", v26, 1LL, 0LL);
  v68 = objc_retainAutoreleasedReturnValue(v67);
  v69 = objc_msgSend(&OBJC_CLASS___NSString, "alloc");
  v70 = objc_msgSend(v69, "initWithData:encoding:", v68, 4LL);
  v71 = v70;
  v72 = objc_msgSend(&OBJC_CLASS___NSString, "RSAStringFromString:", v70);
  v73 = (void *)objc_retainAutoreleasedReturnValue(v72);
  v74 = v73;
  v75 = objc_msgSend(v73, "dataUsingEncoding:", 4LL);
  v76 = objc_retainAutoreleasedReturnValue(v75);
  objc_msgSend(v17, "setPostData:", v76);

动态 在v67 = objc_msgSend(&OBJC_CLASS___NSJSONSerialization, “dataWithJSONObject:options:error:”, v26, 1LL, 0LL);上下断点

对应的汇编

00000101042C2C                 MOV             X0, X19
__text:0000000101042C30                 BL              _objc_release
__text:0000000101042C34                 ADRP            X8, #classRef_NSJSONSerialization@PAGE
__text:0000000101042C38                 LDR             X0, [X8,#classRef_NSJSONSerialization@PAGEOFF] ; void *
__text:0000000101042C3C                 ADRP            X8, #selRef_dataWithJSONObject_options_error_@PAGE
__text:0000000101042C40                 LDR             X1, [X8,#selRef_dataWithJSONObject_options_error_@PAGEOFF] ; char *
__text:0000000101042C44                 MOV             W3, #1
__text:0000000101042C48                 MOV             X2, X24
__text:0000000101042C4C                 MOV             X4, #0
__text:0000000101042C50                 BL              _objc_msgSend
__text:0000000101042C54                 MOV             X29, X29
#subr 0x0000000101042C50

这里我用了个超级断点的工具,可以自动根据偏移计算真实地址。感兴趣的可以在我的博客里面找到。

(lldb) po $x24
{
    account = 1810484xxxx;
    nonce = "6907381121332755797@bjc|bgi|d|fj";
    password = 1aabac6d068eef6a7bad3fdf50a05cc8;
    signature = 117af45c60a1b178934d9a6631567468;
}

通过分析password是密码md5后的值,acccount是登录手机号。

到这里其实比较清晰了。
这里写图片描述

这里还有个RSAStringFromString:
跟以下这个类相关

-[XMRSAEncryptor dealloc]   __text  0000000101293154    0000007C            R   .   .   .   B   T   .
-[XMRSAEncryptor initWithCertificationName:OfType:] __text  00000001012931D0    000000AC            R   .   .   .   B   T   .
-[XMRSAEncryptor encryptWithRawString:] __text  000000010129327C    000000F0            R   .   .   .   B   T   .
-[XMRSAEncryptor getPublicKey]  __text  000000010129336C    000001CC            R   .   .   .   B   T   .
-[XMRSAEncryptor RSAEncrypotoTheData:]  __text  0000000101293538    000002D0            R   .   .   .   B   T   .
-[XMRSAEncryptor certificationName] __text  0000000101293808    00000010            R   .   .   .   .   T   .
-[XMRSAEncryptor setCertificationName:] __text  0000000101293818    00000014            R   .   .   .   .   T   .
-[XMRSAEncryptor certificationType] __text  000000010129382C    00000010            R   .   .   .   .   T   .
-[XMRSAEncryptor setCertificationType:] __text  000000010129383C    00000014            R   .   .   .   .   T   .
-[XMRSAEncryptor toBeEncryptString] __text  0000000101293850    00000010            R   .   .   .   .   T   .
-[XMRSAEncryptor setToBeEncryptString:] __text  0000000101293860    00000014            R   .   .   .   .   T   .
-[XMRSAEncryptor .cxx_destruct] __text  0000000101293874    00000054            R   .   .   .   B   T   .

这里很容易可以得到密钥等关键信息。

id __cdecl -[XMRSAEncryptor RSAEncrypotoTheData:](#5934 *self, SEL a2, id a3)
{
  id v3; // x19
  #5934 *v4; // x20
  __int64 v5; // x22
  void *v6; // x19
  void *v7; // x19
  void *v8; // x0
  void *v9; // x25
  signed int v10; // w22
  unsigned int v11; // w20
  void *v12; // x0
  __int64 v13; // x23
  __int64 v14; // x26
  unsigned __int64 v15; // x24
  __int64 v16; // x21
  char *v17; // x0
  char *v18; // x2
  void *v19; // x0
  void *v20; // x22
  int v21; // w20
  void *v22; // x0
  __int64 v23; // x0
  void *v24; // x22
  void *v25; // x20
  void *v26; // x0
  id v27; // x0
  __int64 v28; // x0
  __int64 v29; // x21
  void *v30; // x0
  __int64 v31; // x20
  struct objc_object *v33; // [xsp-90h] [xbp-90h]
  void *v34; // [xsp-78h] [xbp-78h]
  void *v35; // [xsp-70h] [xbp-70h]
  size_t v36; // [xsp-68h] [xbp-68h]

  v3 = a3;
  v4 = self;
  v5 = objc_retain(a3, a2);
  v35 = objc_msgSend((void *)v4, "getPublicKey");
  v36 = SecKeyGetBlockSize();
  v34 = malloc(v36);
  bzero(v34, v36);
  v6 = objc_msgSend(v3, "dataUsingEncoding:", 4LL);
  objc_release(v5);
  v7 = (void *)objc_retainAutoreleasedReturnValue(v6);
  v8 = objc_msgSend(&OBJC_CLASS___NSNumber, "numberWithLong:", v36 - 11);
  v9 = (void *)objc_retainAutoreleasedReturnValue(v8);
  v10 = (unsigned __int64)objc_msgSend(v9, "intValue");
  objc_release(v9);
  v11 = vcvtpd_s64_f64((double)(unsigned __int64)objc_msgSend(v7, "length") / (double)v10);
  v12 = objc_msgSend(&OBJC_CLASS___NSMutableData, "alloc");
  v33 = (struct objc_object *)objc_msgSend(v12, "init");
  if ( (signed int)v11 >= 1 )
  {
    v13 = 0LL;
    v14 = 0LL;
    v15 = v10;
    v16 = v11;
    do
    {
      v17 = (char *)objc_msgSend(v7, "length");
      if ( v15 >= (unsigned __int64)&v17[v13] )
        v18 = &v17[v13];
      else
        v18 = (char *)v15;
      v19 = objc_msgSend(&OBJC_CLASS___NSNumber, "numberWithLong:", v18);
      v20 = (void *)objc_retainAutoreleasedReturnValue(v19);
      v21 = (unsigned __int64)objc_msgSend(v20, "intValue");
      objc_release(v20);
      v22 = objc_msgSend(v7, "subdataWithRange:", v14, v21);
      v23 = objc_retainAutoreleasedReturnValue(v22);
      v24 = (void *)objc_retainAutorelease(v23);
      v25 = objc_msgSend(v24, "bytes");
      v26 = objc_msgSend(v24, "length");
      if ( !(unsigned int)SecKeyEncrypt(v35, 1LL, v25, v26, v34, &v36) )
        objc_msgSend(v33, "appendBytes:length:", v34, v36);
      objc_release(v24);
      v14 += v15;
      v13 -= v15;
      --v16;
    }
    while ( v16 );
  }
  if ( v34 )
    free(v34);
  if ( v35 )
    CFRelease(v35);
  v27 = ((id (__cdecl *)(#5929 *, SEL, id))objc_msgSend)((#5929 *)&OBJC_CLASS___GTMBase64, "stringByEncodingData:", v33);
  v28 = objc_retainAutoreleasedReturnValue(v27);
  v29 = v28;
  v30 = objc_msgSend(&OBJC_CLASS___NSString, "stringWithFormat:", CFSTR("%@"), v28);
  v31 = objc_retainAutoreleasedReturnValue(v30);
  objc_release(v29);
  objc_release(v33);
  objc_release(v7);
  return (id)objc_autoreleaseReturnValue(v31);
}

总结

工具太多,如果不能灵活应用,往往容易走到死路。我个人感觉,千万不能根据关键字大量的验证猜测。因为用很多相似的类和方法,但我们的目标却不在这些中。为了避免走弯路,我们的分析思路一定要清晰。发包封装的特点往往是,seturl setdata setpost requset等步骤,这些步骤可能不再同一个方法中。setpost是这些当中的关键,虽然不能直接找setpost的方法,但一定要在确定的request请求附近关注setpost.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值