ios逆向分析进阶之关键字加密还原

来源:互联网 发布:淘宝网李宁专卖店 编辑:程序博客网 时间:2024/06/10 17:05

ios逆向分析进阶之关键字加密还原

衔接上一篇博客,这一片自主逆向了一个较坑的算法,感觉基本入门了:

  • 文中使用工具:
  • IDA,Xcode(主要用来静态分析代码)
  • theos lldb debugserver(动态跟踪函数参数返回值)
  • mac自带的日志查看工具(这里找了很多日志工具,感觉还是mac自带的控制台比较好用)
  • ssh连接手机
  • frida分析应用结构
  • charles(http截取)

回顾流程

  • 本次逆向重在总结思路,所以就不给出app名称。静态分析花费时间较长,相信一般都是这样。当然如果经验多了,关键字猜的准一点,分析时间会大大缩短。本次分析从一个基本对app默认规则了解较少的视角来分析,确保逆向思路准确或接近准确。过程尽量还原分析过程和流程。
  • 砸壳过程其实显示是没有加密,但dump头文件却很奇怪,一度以为dump不出头文件,也是后来发现,app目录下就有。不过个人觉得,只要app没加密,头文件其实不重要了。我们直接反编译搜索就行了,当然前提是思路要清晰。

charles抓包

POST /rest/user/thirdPlatformLogin?appver=5.4.4.297&did=BDF77B98-E3CD-4C99-BA77-EA75EE21FC77&c=PP&ver=5.4&sys=ios9.3.2&mod=iPhone6%2C1&net=%E4%B8%AD%E5%9B%BD%E8%81%94%E9%80%9A_5 HTTP/1.1Host: api.gifshow.comContent-Type: application/x-www-form-urlencodedConnection: keep-aliveX-REQUESTID: 7603933Accept: application/jsonUser-Agent: kwai-iosAccept-Language: zh-Hans-CN;q=1Content-Length: 580Accept-Encoding: gzip, deflateaccess_token=2.001azZhGohp7gC7c212d77790PBR_H&client_key=56c3713c&country_code=cn&language=zh-Hans-CN%3Bq%3D1&open_id=6140757656&platform=sina2.0&raw=1511322475686&secret=dyUsRMKNXHmsDwmC3cIq%2FdhlAlreWTMlgW9iGIt63lGd06FI5uJNP3Mt0N6X19n8I90zJIYC6MW54GRWq2qr7Zz6ztpybrQg8J05hNDWL2ADNGPTnTPP0Z%2FDdxJ2Yjlhmse4lFAHNj7mlsNY1J6m52tE9E9c%2BBkR1K6OaCr69iiBKPFjciq%2BWSOTU17SXnWl6pJR80FAJ%2BgnBXkCiIvAaEs5Y%2BNRgvAbxdxFd6LOnrfLguX6%2F75nMIdIIR5P%2BfjTqjM3Ux%2FAGlTxHG4OhKnTSNV%2F%2BRUwkMi0naooCdnZ6uMrSRA7Kw5EedS1z4n0efCQ6xtYSYHglxJOastX%2BmKevQ%3D%3D&sig=e162bc6fe6b588027938ab497fd33dd9access_token    2.001azZhGohp7gC7c212d77790PBR_Hclient_key  56c3713ccountry_code    cnlanguage    zh-Hans-CN;q=1open_id 6140757656platform    sina2.0raw 1511322475686secret  dyUsRMKNXHmsDwmC3cIq/dhlAlreWTMlgW9iGIt63lGd06FI5uJNP3Mt0N6X19n8I90zJIYC6MW54GRWq2qr7Zz6ztpybrQg8J05hNDWL2ADNGPTnTPP0Z/DdxJ2Yjlhmse4lFAHNj7mlsNY1J6m52tE9E9c+BkR1K6OaCr69iiBKPFjciq+WSOTU17SXnWl6pJR80FAJ+gnBXkCiIvAaEs5Y+NRgvAbxdxFd6LOnrfLguX6/75nMIdIIR5P+fjTqjM3Ux/AGlTxHG4OhKnTSNV/+RUwkMi0naooCdnZ6uMrSRA7Kw5EedS1z4n0efCQ6xtYSYHglxJOastX+mKevQ==sig e162bc6fe6b588027938ab497fd33dd9
  • 我们这里同样分析secret关键字,这里分析过程其实不重要了,其实也不能说不重要。我们在程序中搜索secret,发现和secret相关的字段比较多,这里其实也用到了猜测的方法。我们根据直觉和认识寻找,但是都比较坑,就不说了。其实到后来才知道,我们应该搜索KS开头的类,这个主程序代码类库命名的主要方式。因为这两个字母是app中文拼音的两个开头字母,刚开始有点找不着北。总之,最后没有找到相关的。
    这时候峰回路转,我们需要改变搜索规则。考虑是否可以搜索api.gifshow.com网址,但发现往这个地址发的数据很多,不现实。但发现这里这个路径/rest/user/thirdPlatformLogin?只有这个包里利用这条路径。经过搜索。我们发现apipath这个字符串里包含了/rest/user/thirdPlatformLogin?。搜索引用。其实在这里又花了点时间,发现IDA不是特别会用。因为它这个引用会跳到一个object的结构里,然后又包含了这个apipath,而且直接找引用根本找不到。
    apipath位置
  • 原因是object c调用规则,它采用了obj_msgsend()消息机制传输字符串来寻找对应的结构和方法,是所以我们直接找引用分本没有。好吧。

  • 这里就掉坑里,我放弃了正确的方法,转到分析界面寻找按钮响应事件去了。

-><_UIWebViewScrollView: 0x12fbeaa00; frame = (0 0; 320 568); clipsToBounds = YES; autoresize = H; gestureRecognizers = <NSArray: 0x130ac0820>; layer = <CALayer: 0x130a41b30>; contentOffset: {0, -64}; contentSize: {320, 504}>--><UIWebView: 0x130ebf310; frame = (0 0; 320 568); autoresize = W+H; layer = <CALayer: 0x130e2d8c0>>---><WBSDKWebView: 0x1312a9510; frame = (0 0; 320 568); opaque = NO; autoresize = W+H; layer = <CALayer: 0x12f7fa190>>----><UIView: 0x12f5f4fb0; frame = (0 0; 320 568); autoresize = W+H; layer = <CALayer: 0x12f799f20>>-----><WBSDKAuthorizeWebViewController: 0x13114df20>------><UIViewControllerWrapperView: 0x131040c50; frame = (0 0; 320 568); autoresize = W+H; layer = <CALayer: 0x130d0cb30>>-------><UINavigationTransitionView: 0x130aade80; frame = (0 0; 320 568); clipsToBounds = YES; autoresize = W+H; layer = <CALayer: 0x130ac5a70>>--------><UILayoutContainerView: 0x130a4cbd0; frame = (0 0; 320 568); autoresize = W+H; gestureRecognizers = <NSArray: 0x131039dd0>; layer = <CALayer: 0x1310c71b0>>---------><UINavigationController: 0x12fbd5000>----------><KSNavigationController: 0x12fbd8200>-----------><KSRevealSideViewController: 0x13085e630>------------><UITransitionView: 0x130a24210; frame = (0 0; 320 568); autoresize = W+H; layer = <CALayer: 0x130d550a0>>-------------><UIWindow: 0x12f5a1850; frame = (0 0; 320 568); autoresize = W+H; gestureRecognizers = <NSArray: 0x12f5e19b0>; layer = <UIWindowLayer: 0x12f5e0fe0>>--------------><UIApplication: 0x12f58cb30>---------------><AppDelegate: 0x12f599c60>
  • 这里显示的微博登录的一个界面,坑的是,管理按钮的根本不是本地的界面,而是weibo的sdk接口。我找了半天没找到响应时间在那。理论上应该能找到,但我在congtroller里面找半天后来果断放弃,回到了原来的路上。

IDA静态分析和各种动态分析

  • 其实这里静态分析,其实这里就走到正途上。但前面确实花了不少时间,很是纠结。IDA上有一个神奇的界面,叫鱼眼视图,开始不知道是用来干啥的,看起来很怪,后来发现很强大。
    鱼眼视图
    走到了正途我们就捡紧要的说了
    sel_apipath    sub_1001AB000    KSLoginService loginWithContext:(id)arg1    id __cdecl -[KSPhoneLoginService processLoginResponse:context:](KSPhoneLoginService *self, SEL a2, id a3, id a4)  //    sel_processLoginResponse    sub_1001AB2B0    KSLoginService loginWithContext                           KSLoginManager login    sub_1003596AC    id __cdecl -[KSPhoneLoginService processLoginResponse:context:](KSPhoneLoginService *self, SEL a2, id a3, id a4)
  • 经过跳转引用和鱼眼视图的不断切换,我们发现了程序的主要逻辑。apipath 通过sub_1001AB000通过KSLoginService loginWithContext引用。然后回溯到了[KSPhoneLoginService processLoginResponse:context:]。之后又回到了KSLoginService loginWithContex,只不过中间变成经过sub_1001AB2B0。
    这个时候要动态分析了,
    分别挂钩KSLoginService loginWithContext
    KSPhoneLoginService processLoginResponse:context:
%hook KSLoginService- (void *)loginWithContext:(void *)arg1{    NSString *string=@"hkms loginWithContext::arg1:";    NSString *stringcombine=[[NSString alloc] initWithFormat:@"%@,%@",string,arg1];    %log(stringcombine);    string=[[NSString alloc] initWithFormat:@"%@,%@",@"hkms ::self-value",self];    %log(string);    void* result=%orig;//(arg1 ,arg2,arg3);    string=[[NSString alloc] initWithFormat:@"%@,%@",@"hkms ::result-value",result];    %log(string);    return result;}%end
  • 只剩成功的这一段了,反正经过日志分析,我们发现 KSPhoneLoginService processLoginResponse:context:没有进来。我们loginWithContext似乎和这个关键字相关。
    为了验证我们的猜想,我们lldb跟进。
    image list -o -f
    找到模块地址基地址
    br s -a 基地址+IDA分析偏移
    下断

我们发现sub_1001AB000函数dataTaskWithRequest:cancellationToken执行后,我们的关键请求立马会被发送。

        id __fastcall sub_1001AB000(__int64 a1, __int64 a2, __int64 a3, __int64 a4, __int64 a5, __int64 a6, __int64 a7, __int64 a8, struct objc_object *a9)           v29 = +[KSUHTTPTaskManager defaultTaskManager](&OBJC_CLASS___KSUHTTPTaskManager, "defaultTaskManager");    v30 = (void *)objc_retainAutoreleasedReturnValue(v29);    v31 = objc_msgSend(v28, "token");    v32 = objc_retainAutoreleasedReturnValue(v31);    v33 = v32;    v34 = objc_msgSend(v30, "dataTaskWithRequest:cancellationToken:", v22, v32);    v15 = (KSApiDotGifShowHTTPClient *)objc_retainAutoreleasedReturnValue(v34);

到这里已经比较近了。但还需要很多分析。应为这里传经来的都是类.

    _text:00000001001AB1E0                 BL              _objc_msgSend__text:00000001001AB1E4                 MOV             X29, X29__text:00000001001AB1E8                 BL              _objc_retainAutoreleasedReturnValue__text:00000001001AB1EC                 MOV             X25, X0__text:00000001001AB1F0                 ADRP            X8, #selRef_dataTaskWithRequest_cancellationToken_@PAGE__text:00000001001AB1F4                 LDR             X1, [X8,#selRef_dataTaskWithRequest_cancellationToken_@PAGEOFF] ; char *__text:00000001001AB1F8                 MOV             X0, X24 ; void *__text:00000001001AB1FC                 MOV             X2, X21__text:00000001001AB200                 MOV             X3, X25__text:00000001001AB204                 BL              _objc_msgSend__text:00000001001AB208                 MOV             X29, X29__text:00000001001AB20C                 BL              _objc_retainAutoreleasedReturnValue

挂钩得到了传经来的类,细节不说了,跟上面一样

%hook KSUHTTPTaskManager//这个管理器参数一就是KSUPOSTHTTPRequest 参数二BFCancellationToken- (void*)dataTaskWithRequest:(void*)arg1 cancellationToken:(void*)arg2{
  • 经过了一大段分析,我们跟进了一步。但这里我用lldb动态把这个函数给跟了一遍,最后自己都醉了。
    应为这一段不和静态分析的代码的执行过程完全一样,虽然找到了地方,参数也相似,但有很多跳转就像山上的猴子,简直是乱跳,没有逻辑。

最后放弃了全盘动态跟踪的想法,这也就是arm汇编和x86汇编的区别吧。

  • 其实这里有点思路不清晰了,KSUPOSTHTTPRequest 知识发送请求的操作,我们的数据很有可能在这之前就完成,最后回到了sub_1001AB000
id __fastcall sub_1001AB000(__int64 a1, __int64 a2, __int64 a3, __int64 a4, __int64 a5, __int64 a6, __int64 a7, __int64 a8, struct objc_object *a9){  __int64 v9; // x22  void *v10; // x19  void *v11; // x0  struct objc_object *v12; // x0  struct objc_object *v13; // x21  id v14; // x0  KSApiDotGifShowHTTPClient *v15; // x22  __int64 v16; // x0  void *v17; // x0  __int64 v18; // x20  void *v19; // x0  __int64 v20; // x21  void *v21; // x0  void *v22; // x21  void *v23; // x0  __int64 v24; // x23  void *v25; // x0  __int64 v26; // x22  id v27; // x0  void *v28; // x23  id v29; // x0  void *v30; // x24  void *v31; // x0  __int64 v32; // x0  __int64 v33; // x25  void *v34; // x0  SEL v35; // x1  id v36; // x2  unsigned __int64 v37; // x3  id v38; // x4  id v39; // x5  id v40; // x6  id v41; // x7  v9 = a1;  v10 = (void *)objc_retain(a2, a2);  if ( (unsigned int)objc_msgSend(v10, "isFaulted") )  {    v11 = objc_msgSend(v10, "error");    v12 = (struct objc_object *)objc_retainAutoreleasedReturnValue(v11);    v13 = v12;    v14 = ((id (__cdecl *)(BFTask_meta *, SEL, id))objc_msgSend)(            (BFTask_meta *)&OBJC_CLASS___BFTask,            "taskWithError:",            v12);    v15 = (KSApiDotGifShowHTTPClient *)objc_retainAutoreleasedReturnValue(v14);    v16 = (__int64)v13;  }  else  {    v17 = objc_msgSend(*(void **)(v9 + 32), "paramsFromContext:", *(_QWORD *)(v9 + 40));    v18 = objc_retainAutoreleasedReturnValue(v17);    if ( (unsigned int)objc_msgSend(*(void **)(v9 + 32), "needAppendTimestampSecret") )    {      v19 = objc_msgSend(*(void **)(v9 + 32), "_paramsWithTimestampSecret:", v18);      v20 = objc_retainAutoreleasedReturnValue(v19);      objc_release(v18);      v18 = v20;    }    v21 = objc_msgSend(*(void **)(v9 + 32), "request");    v22 = (void *)objc_retainAutoreleasedReturnValue(v21);    v23 = objc_msgSend(*(void **)(v9 + 32), "apiPath");    v24 = objc_retainAutoreleasedReturnValue(v23);    objc_msgSend(v22, "setPath:", v24);    objc_release(v24);    objc_msgSend(v22, "setBodyParameter:", v18);    objc_msgSend(*(void **)(v9 + 32), "additionSetupRequest:context:", v22, *(_QWORD *)(v9 + 40));    v25 = objc_msgSend(*(void **)(v9 + 32), "_transformResponseObjectBlock");    v26 = objc_retainAutoreleasedReturnValue(v25);    objc_msgSend(v22, "setTransformResponseObject:", v26);    objc_release(v26);    v27 = ((id (__cdecl *)(BFCancellationTokenSource_meta *, SEL))objc_msgSend)(            (BFCancellationTokenSource_meta *)&OBJC_CLASS___BFCancellationTokenSource,            "cancellationTokenSource");    v28 = (void *)objc_retainAutoreleasedReturnValue(v27);    v29 = ((id (__cdecl *)(KSUHTTPTaskManager_meta *, SEL))objc_msgSend)(            (KSUHTTPTaskManager_meta *)&OBJC_CLASS___KSUHTTPTaskManager,            "defaultTaskManager");    v30 = (void *)objc_retainAutoreleasedReturnValue(v29);    v31 = objc_msgSend(v28, "token");    v32 = objc_retainAutoreleasedReturnValue(v31);    v33 = v32;    v34 = objc_msgSend(v30, "dataTaskWithRequest:cancellationToken:", v22, v32);    v15 = (KSApiDotGifShowHTTPClient *)objc_retainAutoreleasedReturnValue(v34);    objc_release(v33);    objc_release(v30);    objc_release(v28);    objc_release(v22);    v16 = v18;  }  objc_release(v16);  objc_release(v10);  return objc_autoreleaseReturnValue(v15, v35, v36, v37, v38, v39, v40, v41, a9);}

这段代码比较长,但是也比较重要,就全贴吧。
明显这里是加密关键:KSLoginService _paramsWithTimestampSecret。进去

id __cdecl -[KSLoginService _paramsWithTimestampSecret:](KSLoginService *self, SEL selm, id instr){  void *v3; // x19  void *datastr; // x0  void *datastr0; // x21  double ident0; // d0   我猜测这里是采用datastr溢出的方式的到ident0  void *raw0; // x0  __int64 raw; // x20  void *secret0; // x0  __int64 secret; // x21  KSApiDotGifShowHTTPClient *instr_encode; // x22  SEL instr_encode0; // x1  id instr_encode1; // x2  unsigned __int64 diguisign; // x3  id diguisign0; // x4  id v16; // x5  id base64result; // x6  id base64result0; // x7  struct objc_object *v20; // [xsp+40h] [xbp+10h]  v3 = objc_msgSend(instr, "mutableCopy");   //id __cdecl -[VKAccessToken mutableCopy](VKAccessToken *self, SEL selm)  datastr = objc_msgSend(&OBJC_CLASS___NSDate, "date");  datastr0 = (void *)objc_retainAutoreleasedReturnValue(datastr);  objc_msgSend(datastr0, "timeIntervalSince1970");  //ident0不知是什么,突然出现这么个值,raw还不定时的变化  raw0 = objc_msgSend(&OBJC_CLASS___NSString, "stringWithFormat:", CFSTR("%ld"), (signed __int64)(ident0 * 1000.0));//拼接字符串,这里就一个,就是个类型转换  raw = objc_retainAutoreleasedReturnValue(raw0);  objc_release(datastr0);  secret0 = objc_msgSend(&OBJC_CLASS___UIDevice, "getSignatureStr:", raw);  //经过测试这里已经完成了secret字符串的加密}

secret0 = objc_msgSend(&OBJC_CLASS___UIDevice, “getSignatureStr:”, raw);
//经过测试这里已经完成了secret字符串的加密

    id __cdecl +[UIDevice getSignatureStr:](UIDevice_meta *self, SEL selm, id instr){  UIDevice_meta *v3; // x20  struct objc_object *weiRSA; // x0  __int64 weiRSA0; // x19  __int64 ident0; // x1  __int64 num0; // x21  KSUTaskMetaData *ksutaskmetadata; // x0  KSUTaskMetaData *ksutaskmetadata0; // x22  signed __int64 ident1; // x2  const __CFString *weiRSA1; // x20  struct objc_object *weiRSA2; // x20  __int64 num; // [xsp+8h] [xbp-38h]  v3 = self;  num = 0LL;  weiRSA = +[KSRSAUtil getSignatureStr:identifier:error:](         &OBJC_CLASS___KSRSAUtil,         "getSignatureStr:identifier:error:",         instr,         CFSTR("kKSDefaultSecurityIdentifier"),         &num);  weiRSA0 = objc_retainAutoreleasedReturnValue(weiRSA);  num0 = objc_retain(num, ident0);  ksutaskmetadata = objc_msgSend(&OBJC_CLASS___KSUTaskMetaData, "alloc");  ksutaskmetadata0 = objc_msgSend(ksutaskmetadata, "init");  -[KSUTaskMetaData setAction:](ksutaskmetadata0, "setAction:", 41LL);  -[KSUTaskMetaData setStatus:](ksutaskmetadata0, "setStatus:", 1LL);  objc_msgSend((void *)v3, "taskMetaData:addErrorInfo:", ksutaskmetadata0, num0);  if ( weiRSA0 )    ident1 = 7LL;  else    ident1 = 8LL;  if ( weiRSA0 )    weiRSA1 = (const __CFString *)weiRSA0;  else    weiRSA1 = &stru_1024ident740;  -[KSUTaskMetaData setStatus:](ksutaskmetadata0, "setStatus:", ident1);  objc_release(num0);  weiRSA2 = (struct objc_object *)objc_retainAutoreleaseReturnValue(weiRSA1);  objc_release(ksutaskmetadata0);  objc_release(weiRSA0);  return weiRSA2;}

weiRSA = +[KSRSAUtil getSignatureStr:identifier:error:](
&OBJC_CLASS___KSRSAUtil,
“getSignatureStr:identifier:error:”,
instr,
CFSTR(“kKSDefaultSecurityIdentifier”),
&num);
这里的结果就是secret
我们把这几个函数可以都挂一下,发现,返回值都是secret.
但最核心的是这个。

id __cdecl +[KSRSAUtil getSignatureStr:identifier:error:](KSRSAUtil_meta *self, SEL selm, id instr, id ident, id *a5){  id *errostr; // x21  id ident0; // x20  KSRSAUtil_meta *selfobj; // x22  void *instr_selm; // x19  __int64 v9; // x1  void *ident1; // x20  void *instr_encode; // x0  __int64 instr_encode0; // x0  __int64 instr_encode1; // x23  void *diguisign; // x0  void *diguisign0; // x21  void *NSSv; // x22  void *base64result; // x0  __int64 base64result0; // x23  KSApiDotGifShowHTTPClient *base64result1; // x22  void *v20; // x0  __int64 v21; // x0  SEL v22; // x1  id v23; // x2  unsigned __int64 v24; // x3  id v25; // x4  id v26; // x5  id v27; // x6  id v28; // x7  struct objc_object *v30; // [xsp+40h] [xbp+10h]  errostr = a5;  ident0 = ident;   //这是一个字符串"KKSDefaultSecurityIdentifier"        selfobj = self;  instr_selm = (void *)objc_retain(instr, selm);//这个选择子还是getSignatureStr,要么输入还是这么个类,要么其他的类也是这个方法,看了下其他类没有这个方法  //这个instr为raw,为一串变化的字符串,目测跟时间有关,产生于上一层  ident1 = (void *)objc_retain(ident0, v9);  if ( objc_msgSend(ident1, "length") )//计算字节长度  {    instr_encode = objc_msgSend(instr_selm, "dataUsingEncoding:", 4LL);//静态调用本方法    instr_encode0 = objc_retainAutoreleasedReturnValue(instr_encode);    instr_encode1 = instr_encode0;    diguisign = objc_msgSend(selfobj, "getSignature:identifier:error:", instr_encode0, ident1, errostr);    diguisign0 = (void *)objc_retainAutoreleasedReturnValue(diguisign);    objc_release(instr_encode1);    if ( diguisign0 )    {        //这几个函数都是NSString里的库函数,也就是说,本地函数到此调用结束      NSSv = objc_msgSend(&OBJC_CLASS___NSString, "alloc");      base64result = objc_msgSend(diguisign0, "base64EncodedDataWithOptions:", 0LL);//NSData      base64result0 = objc_retainAutoreleasedReturnValue(base64result);      base64result1 = (KSApiDotGifShowHTTPClient *)objc_msgSend(NSSv, "initWithData:encoding:", base64result0, 4LL);//NNString      objc_release(base64result0);    }    else    {      base64result1 = 0LL;    }    objc_release(diguisign0);  }  else if ( errostr )  {    v20 = objc_msgSend(selfobj, "errorWithErrorType:", 3001LL);//错误类型,不用管    v21 = objc_retainAutoreleasedReturnValue(v20);    base64result1 = 0LL;    *errostr = (id)objc_autorelease(v21);  }  else  {    base64result1 = 0LL;  }  objc_release(ident1);  objc_release(instr_selm);  return objc_autoreleaseReturnValue(base64result1, v22, v23, v24, v25, v26, v27, v28, v30);}

函数名中有RSA,我还以为跟RSA相关。但根本没啥关系。

(1)加密要用公钥 (n,e)
m必须是整数(字符串可以取ascii值或unicode值),且m必须小于n。
所谓”加密”,就是算出下式的c:
  m的e次方 ≡ c (mod n)
例如:公钥是 (3233, 17),鲍勃的m假设是65,那么可以算出下面的等式:
  6517 ≡ 2790 (mod 3233)
于是,c等于2790。
(2)解密要用私钥(n,d)
拿到2790以后,就用私钥(3233, 2753) 进行解密。
  cd ≡ m (mod n)
也就是说,c的d次方除以n的余数为m。现在,c等于2790,私钥是(3233, 2753)
  2790的2753次方 ≡ 65 (mod 3233)

因为里面就是base64加密,可见代码注释。
我们把这几个函数验证下。

%hook KSLoginService- (void*)_paramsWithTimestampSecret:(void*)arg1{    NSString *string=@"hkms _paramsWithTimestampSecret::arg1:";    NSString *stringcombine=[[NSString alloc] initWithFormat:@"%@,%@",string,arg1];    //NSString * match=@"^((?!rest/user).)*$"    //[NSPredicate predcateWithFormate:arg1,match]    //%log(stringcombine);    string=[[NSString alloc] initWithFormat:@"%@,%@",@"hkm::self-value",self];    stringcombine=[[NSString alloc] initWithFormat:@"%@,%@",stringcombine,string];    //%log(stringcombine);    void* result=%orig;//(arg1 ,arg2,arg3);    string=[[NSString alloc] initWithFormat:@"%@,%@",@"hkm::result-value",result];    stringcombine=[[NSString alloc] initWithFormat:@"%@,%@",stringcombine,string];    %log(stringcombine);    return result;}%end%hook UIDevice//这里的返回值已经是sercret关键字加密后的形式了/*dp7RIvaOm/20vyL0eYCUqZP5los9YTlAPT/a0Bv6jlQfq2m3KfX9KuI/nEu771FIRLaxCvkaGA7++rXoU4dYR0iPpYbXqMdU2kpRIgrPlCZHgC6I14UxaRbQ1JBJNqUqLGCzYDNWyBywurLFapIjfFy8857iYQ8WNIP9KmZX4WysTgfnmJBO2ltVkYZaUAP7ljdlritTSQ5R7qGAmwYlj1Vw1W6NWSlRTJAhIPaIRRw8jsBTdRELpEdwAoTX+EeWjsEPTNbyaCOkNYcBsZcBiI4sFdVCvSRfigVxaZdYN+1oJdiksp8OOxyyDe6VAir15wf+1gcpVIiP7/fUxk0r5Q==*/+ (void*)getSignatureStr:(void*)arg1{    NSString *string=@"hkms getSignatureStr::arg1:";    NSString *stringcombine=[[NSString alloc] initWithFormat:@"%@,%@",string,arg1];    string=[[NSString alloc] initWithFormat:@"%@,%@",@"hkm::self-value:",self];    stringcombine=[[NSString alloc] initWithFormat:@"%@,%@",stringcombine,string];    //%log(stringcombine);    void* result=%orig;//(arg1 ,arg2,arg3);    string=[[NSString alloc] initWithFormat:@"%@,%@",@"hkm::result-value:",result];    stringcombine=[[NSString alloc] initWithFormat:@"%@,%@",stringcombine,string];    %log(stringcombine);    return result;}%end

日志跟踪结果

10:13:59.000000 +0800   com_kwai_gif     [kuaishouhook] Tweak.xm:169 DEBUG: +[<KSRSAUtil: 0x102a4c270> getSignatureStr:0x160722db0 identifier:0x10257dcc0 error:0x16fd4a618]: hkms KSRSAUtil getSignatureStr::arg1:,1511579639518,hkms getSignatureStr::arg2:,kKSDefaultSecurityIdentifier,hkm::self-value:,KSRSAUtil,hkm::result-value:,RXyOXL3s1QvkStBtDFO65BRIkuak5pz6t/EQMRhEmy/htpBnHNdCykZYmy7xWXecJ+znh8/4CEsc/48yJepfDMJyvfwsZLb/lXLZBdQmo/irCCrCbHfxsosnD7WUg7kapj1sRcUdx7R6SCi9pPgopebo21Ag2MtRDqnVjt3HzQvaKZIukGD5mb1DGi0ZpDFRbRX7pLKKZbihupvEYkxsTqL36pKWgs50b8y56zyQlNI43Ts3LD27RYTnJLGjTCRrSMX9kakVtAIeHI+EQoIPiajtJa+MA74MvynCMh1FMXkv0xhj0+rY1T5Iu4TZIFYSHLjwzjEC40TPDVP4vB3frA==10:13:59.000000 +0800   com_kwai_gif     [kuaishouhook] Tweak.xm:121 DEBUG: +[<UIDevice: 0x1a1e546a8> getSignatureStr:0x160722db0]: hkms getSignatureStr::arg1:,1511579639518,hkm::self-value:,UIDevice,hkm::result-value:,RXyOXL3s1QvkStBtDFO65BRIkuak5pz6t/EQMRhEmy/htpBnHNdCykZYmy7xWXecJ+znh8/4CEsc/48yJepfDMJyvfwsZLb/lXLZBdQmo/irCCrCbHfxsosnD7WUg7kapj1sRcUdx7R6SCi9pPgopebo21Ag2MtRDqnVjt3HzQvaKZIukGD5mb1DGi0ZpDFRbRX7pLKKZbihupvEYkxsTqL36pKWgs50b8y56zyQlNI43Ts3LD27RYTnJLGjTCRrSMX9kakVtAIeHI+EQoIPiajtJa+MA74MvynCMh1FMXkv0xhj0+rY1T5Iu4TZIFYSHLjwzjEC40TPDVP4vB3frA==10:15:35.000000 +0800   com_kwai_gif     [kuaishouhook] Tweak.xm:169 DEBUG: +[<KSRSAUtil: 0x102a4c270> getSignatureStr:0x1604bbc70 identifier:0x10257dcc0 error:0x16fd4a0f8]: hkms KSRSAUtil getSignatureStr::arg1:1511579735417hkms getSignatureStr::arg2:kKSDefaultSecurityIdentifierhkm::self-value:KSRSAUtilhkm::resultvalue:qCdZPHdPQx/KW/jsJv6QVJcR4blEMlKWlOAYFtharNFuWuKjlkbiGoaCKrKUsFrewK3K6CJA0wX0UVBb/61A9lqulSX5IpxoARUCGO4YoLksWVAX2Gv4cDguPlWHIBkbMLKQm21RKKCADXf8NOQRQuLITYsulnOozxQvhHdeI3jbt1LzdKz/GbG7os3jdN9kRCZtxXe3f6gE5ejNq5ILjQPubATLeOoTiVI60HBO6Iq+j7YNHVf80Brixme/Q/NgJWKIvziBOxQ89EPhUEnyMgPVkH/Vqd9bvDKnXT21NZwAvrQwGv8heVp5ttzrqrswx1cfU8O52Lu6BuFxj96N6w==10:15:35.000000 +0800   com_kwai_gif     [kuaishouhook] Tweak.xm:121 DEBUG: +[<UIDevice: 0x1a1e546a8> getSignatureStr:0x1604bbc70]: hkms getSignatureStr::arg1:1511579735417hkm::self-value:UIDevicehkm::result-value:qCdZPHdPQx/KW/jsJv6QVJcR4blEMlKWlOAYFtharNFuWuKjlkbiGoaCKrKUsFrewK3K6CJA0wX0UVBb/61A9lqulSX5IpxoARUCGO4YoLksWVAX2Gv4cDguPlWHIBkbMLKQm21RKKCADXf8NOQRQuLITYsulnOozxQvhHdeI3jbt1LzdKz/GbG7os3jdN9kRCZtxXe3f6gE5ejNq5ILjQPubATLeOoTiVI60HBO6Iq+j7YNHVf80Brixme/Q/NgJWKIvziBOxQ89EPhUEnyMgPVkH/Vqd9bvDKnXT21NZwAvrQwGv8heVp5ttzrqrswx1cfU8O52Lu6BuFxj96N6w==11:15:35.000000 +0800   com_kwai_gif     [kuaishouhook] Tweak.xm:97 DEBUG: -[<KSLoginService: 0x1607618c0> _paramsWithTimestampSecret:0x1606d1da0]: hkms _paramsWithTimestampSecret::arg1:,{        "access_token" = "2.001azZhGohp7gC7c212d77790PBR_H";        "open_id" = 6140757656;        platform = "sina2.0";    },hkm::self-value,<KSThirdPartyLoginService: 0x1607618c0>,hkm::result-value,{        "access_token" = "2.001azZhGohp7gC7c212d77790PBR_H";        "open_id" = 6140757656;        platform = "sina2.0";        raw = 1511579735417;        secret = "qCdZPHdPQx/KW/jsJv6QVJcR4blEMlKWlOAYFtharNFuWuKjlkbiGoaCKrKUsFrewK3K6CJA0wX0UVBb/61A9lqulSX5IpxoARUCGO4YoLksWVAX2Gv4cDguPlWHIBkbMLKQm21RKKCADXf8NOQRQuLITYsulnOozxQvhHdeI3jbt1LzdKz/GbG7os3jdN9kRCZtxXe3f6gE5ejNq5ILjQPubATLeOoTiVI60HBO6Iq+j7YNHVf80Brixme/Q/NgJWKIvziBOxQ89EPhUEnyMgPVkH/Vqd9bvDKnXT21NZwAvrQwGv8heVp5ttzrqrswx1cfU8O52Lu6BuFxj96N6w==";    }

突然发现把关键信息泄露了,算了,给细心的同学点福利吧。

总结

  • 日志这东西以前不用,想在用了发现。挂钩的结果一定要标准清楚,用来在日志中方便查找。否则一片混乱,就真找不到北了。
  • 分析过程中关键字可能不只一个,一般类似secret、password、username.道理相同。实在找不到可以看下路径里有没有特异性的字符。总之,就是要能找到这个地方,其他人应用较少。
  • 一般找到引用后有两个问题:
    1.找到大致位置后不能定位。
    2.引用位置太多,基本看不完。
  • 这时候除了利用鱼眼视图要注意两点:
    1.动态调试前一定要理清思路,把参数传递和数据发送的动作分开来思考。因为这符合模块设计的思路。
    2.挂钩和动态调试结合,静态分析的结果很多有片面性,如果lldb完全照着跟,很容掉坑里。分析要重点关注流程,不能一位分析汇编代码。
  • 以上总结纯属个人意见,如果异议,欢迎留言。

本次实验纯属研究为目的,如有不妥请联系本人。

原创粉丝点击