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.1
Host: api.gifshow.com
Content-Type: application/x-www-form-urlencoded
Connection: keep-alive
X-REQUESTID: 7603933
Accept: application/json
User-Agent: kwai-ios
Accept-Language: zh-Hans-CN;q=1
Content-Length: 580
Accept-Encoding: gzip, deflate
access_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=e162bc6fe6b588027938ab497fd33dd9
access_token 2.001azZhGohp7gC7c212d77790PBR_H
client_key 56c3713c
country_code cn
language zh-Hans-CN;q=1
open_id 6140757656
platform sina2.0
raw 1511322475686
secret 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,而且直接找引用根本找不到。
原因是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++rXoU4dY
R0iPpYbXqMdU2kpRIgrPlCZHgC6I14UxaRbQ1JBJNqUqLGCzYDNWyBywurLFapIjfFy8857iYQ8WNIP9KmZX4W
ysTgfnmJBO2ltVkYZaUAP7ljdlritTSQ5R7qGAmwYlj1Vw1W6NWSlRTJAhIPaIRRw8jsBTdRELpEdwAoTX+EeW
jsEPTNbyaCOkNYcBsZcBiI4sFdVCvSRfigVxaZdYN+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 [1;36m[kuaishouhook] [m[0;36mTweak.xm:169[m [0;30;46mDEBUG:[m +[<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 [1;36m[kuaishouhook] [m[0;36mTweak.xm:121[m [0;30;46mDEBUG:[m +[<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 [1;36m[kuaishouhook] [m[0;36mTweak.xm:169[m [0;30;46mDEBUG:[m +[<KSRSAUtil: 0x102a4c270> getSignatureStr:0x1604bbc70 identifier:0x10257dcc0 error:0x16fd4a0f8]:
hkms KSRSAUtil getSignatureStr::arg1:1511579735417
hkms getSignatureStr::arg2:kKSDefaultSecurityIdentifier
hkm::self-value:KSRSAUtil
hkm::resultvalue:qCdZPHdPQx/KW/jsJv6QVJcR4blEMlKWlOAYFtharNFuWuKjlkbiGoaCKrKUsFrewK3K6CJA0wX0UVBb/61A9lqulSX5IpxoARUCGO4YoLksWVAX2Gv4cDguPlWHIBkbMLKQm21RKKCADXf8NOQRQuLITYsulnOozxQvhHdeI3jbt1LzdKz/GbG7os3jdN9kRCZtxXe3f6gE5ejNq5ILjQPubATLeOoTiVI60HBO6Iq+j7YNHVf80Brixme/Q/NgJWKIvziBOxQ89EPhUEnyMgPVkH/Vqd9bvDKnXT21NZwAvrQwGv8heVp5ttzrqrswx1cfU8O52Lu6BuFxj96N6w==
10:15:35.000000 +0800 com_kwai_gif [1;36m[kuaishouhook] [m[0;36mTweak.xm:121[m [0;30;46mDEBUG:[m +[<UIDevice: 0x1a1e546a8> getSignatureStr:0x1604bbc70]:
hkms getSignatureStr::arg1:1511579735417
hkm::self-value:UIDevice
hkm::result-value:qCdZPHdPQx/KW/jsJv6QVJcR4blEMlKWlOAYFtharNFuWuKjlkbiGoaCKrKUsFrewK3K6CJA0wX0UVBb/61A9lqulSX5IpxoARUCGO4YoLksWVAX2Gv4cDguPlWHIBkbMLKQm21RKKCADXf8NOQRQuLITYsulnOozxQvhHdeI3jbt1LzdKz/GbG7os3jdN9kRCZtxXe3f6gE5ejNq5ILjQPubATLeOoTiVI60HBO6Iq+j7YNHVf80Brixme/Q/NgJWKIvziBOxQ89EPhUEnyMgPVkH/Vqd9bvDKnXT21NZwAvrQwGv8heVp5ttzrqrswx1cfU8O52Lu6BuFxj96N6w==
11:15:35.000000 +0800 com_kwai_gif [1;36m[kuaishouhook] [m[0;36mTweak.xm:97[m [0;30;46mDEBUG:[m -[<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完全照着跟,很容掉坑里。分析要重点关注流程,不能一位分析汇编代码。 - 以上总结纯属个人意见,如果异议,欢迎留言。
本次实验纯属研究为目的,如有不妥请联系本人。