某FM密码加密分析IOS
2018-01-09 11:42
1051 查看
某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; fram 13131 e = (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.相关文章推荐
- 关于某ios app登录关键字加密分析
- 【代码笔记】iOS-给密码进行加密
- 【代码笔记】iOS-给密码进行加密
- iOS明文加密方式——MD5和动态密码加密
- 常见密码算法总结--(3)加密模式的openssl代码分析之cbc模式
- ios逆向分析进阶之关键字加密还原
- 协同拨号器2.7账号密码加密分析
- DEDECMS织梦5.5版密码加密分析!
- iOS 注册密码加密 添加了时间戳 遇到的问题...
- ios客户端学习-密码加密存储keychain,KeychainItemWrapper和SFHFKeychainUtils
- 浙飞网站管理系统密码加密方式分析
- ios 学习登陆密码AES 加密
- (IOS)BaiduFM 程序分析
- Yii密码加密与验证(源码分析)
- IOS密码加密
- 新浪微博密码加密分析
- wordpress用户密码加密原理及其算法分析
- ios 学习登陆密码AES 加密
- linux 用户密码加密分析
- ios-密码加密