最近在学习ios逆向,就顺手拿个app练练手,顺便分享一下。
本文使用皮皮搞笑 1.5.5 ios 版本为例,逆向分析关注功能。
工具
打开App后,如下图,可以看到一个关注按钮,通过点击按钮,可以实现关注该用户动态。
通过抓包分析,点击关注按钮时,会发送add?sign=xxxxxx数据包,想要分析数据来源,可以从界面上的关注按钮着手.
为了更直观分析,这里使用Reveal神器来分析UI界面,在使用Reveal前要在手机设置 > Reveal->Enabled Applications 里面打开需要分析的App.
配置好Reveal后,在手机上打开需要分析的app界面,再在笔记本上打开Reveal工具,Reveal支持USB和网络连接两种方式,一般选择USB.
打开Reveal后,如下图,可以看到AppUI 应用结构层级
通过分析AppUI 应用结构层级,可以明显看到红色关注按钮,直接点击按钮,可以看到的确是一个UIButton,内存地址0x143883f80,视图控制器Class 为 LJJUSerProfileVC,内存地址0x140abb400
为了验证数据是否正确,使用Cycript(动态调式工具),来获取运行时信息
使用ssh登录iphone,执行命令 crcript -p pid或进程名
接下来对app进行脱壳.再使用IDA加载脱壳后的Mach-o文件,恢复App符号表可参考(http://blog.imjun.net/posts/restore-symbol-of-iOS-app/)
为了方便查看,使用F5后的伪代码来进行观察
LJJUSerProfileVC followClick 又调用了 LJJUSerProfileVC followClick]_block 函数
因为函数调用较多,就不一一贴图了,经过分析,以下为主要函数调用流程:
ZYURLManager requestHeaderDictionaryWithParameters:主要功能就是h_model,u_id,h_ch,h_app,h_ts,token 等字段进行填充,其中h_ts为时间戳.
以下是ZYURLManager requestHeaderDictionaryWithParameters: 部分伪代码
从调用图可以看出sub_100200C40 又调用了sub_1002003C4 和 sub_100200BB0 函数
sub_100200C40 流程图
上图中,其中蓝小方块为调用sub_1002003C4 和 sub_100200BB0 函数,是整个函数必经之路
sub_1002003C4 流程图
我们知道md5算法中需要 0x67452301 0xefcdab89 0x98badcfe 0x10325476 数据做为初始值,以分组为单位进行处理,分别对每一块信息块进行MD5计算,每次计算4轮,每轮16次计算.
根据以上信息可以推断 sub_1002003C4 md5算法或其变形,不过还需要进一步分析才能确定
sub_100200BB0 伪代码
小结一下
ZYURLManager requestHeaderDictionaryWithParameters: 将需要提交的数据进行字段进行填充,大部分都是都是固定字段的如h_model,u_id,h_ch,h_app,h_ts,token等字段,这里就不做详细分析。
ZYCrypto signWithParameters 调用sub_100200C40 函数将数据进行签名.
iphone ssh 登录,开启 debugserver 。
对关键的函数下断点,查看其参数和返回值
sub_1002003C4 函数头地址为0x100200c40 + 0x24000 = 0x100224c40
查看参数可以看到就是post字符串数据.接下来再分析sub_1002003c4参数和返回值
分别对sub_1002003c4 入口和返回后地址下断
整个分析流程大概就这些了,通过界面分析到功能函数静态和动态分析,这也是在逆向中常用的思路。
IDA 7.0
Reveal 21(11690)
Cycript
lldb
Charles
环境
Iphone6 IOS 9.1
Macos Mojave 10.14.5
三.界面分析
通过分析app UI界面为突破口,来进行逆向分析。 打开App后,如下图,可以看到一个关注按钮,通过点击按钮,可以实现关注该用户动态。
通过抓包分析,点击关注按钮时,会发送add?sign=xxxxxx数据包,想要分析数据来源,可以从界面上的关注按钮着手.
为了更直观分析,这里使用Reveal神器来分析UI界面,在使用Reveal前要在手机设置 > Reveal->Enabled Applications 里面打开需要分析的App.
配置好Reveal后,在手机上打开需要分析的app界面,再在笔记本上打开Reveal工具,Reveal支持USB和网络连接两种方式,一般选择USB.
打开Reveal后,如下图,可以看到AppUI 应用结构层级
通过分析AppUI 应用结构层级,可以明显看到红色关注按钮,直接点击按钮,可以看到的确是一个UIButton,内存地址0x143883f80,视图控制器Class 为 LJJUSerProfileVC,内存地址0x140abb400
为了验证数据是否正确,使用Cycript(动态调式工具),来获取运行时信息
使用ssh登录iphone,执行命令 crcript -p pid或进程名
Davinde-iPhone:/var root# cycript -p PPSocial
cy# #0x143883f80
#"<UIButton: 0x143883f80; frame = (270 29; 50 26); alpha = 0; opaque = NO; layer = <CALayer: 0x1438a4cc0>>"
通过以上信息可以判断,0x143883f80的确是UIButton按钮的地址,如果不确定,可以通过调用hidden方法,并且观察app对应的按钮是否有变化.
cy# #0x143883f80.hidden=YES //隐藏按钮
true
cy# #0x143883f80.hidden=NO //显示按钮
false
光知道UiButton内存地址好像没有用,如何得到UiBtton的点击事件的所调用函数呢? 通过cycript 脚可以来遍历
@import mycript //导入mycript 脚本
cy# MyBtnTouchUpEvent(#0x143883f80)
[@["followClick"]]
下面是MyBtnTouchUpEvent代码,获取该Button对象所有的Target对象,并将事件相关所有action遍历输出,顺提一下,action可能有多个
// 获取按钮绑定的所有TouchUpInside事件的方法名
MyBtnTouchUpEvent = function(btn) {
var events = [];
var allTargets = btn.allTargets().allObjects()
var count = allTargets.count;
for (var i = count - 1; i >= 0; i--) {
if (btn != allTargets[i]) {
var e = [btn actionsForTarget:allTargets[i] forControlEvent:UIControlEventTouchUpInside];
events.push(e);
}
}
return events;
};
通过MyBtnTouchUpEvent得到followClick方法,需要测试下这个方法是否正确,通过调用LJJUSerProfileVC控制器的 followClick方法来测试一下
cy# [#0x140abb400 followClick] //#0x140abb400为LJJUSerProfileVC对象
通过反复[#0x140abb400 followClick] 方法调用,可以实现手动点击关注效果,现在可以确定LJJUSerProfileVC followClick就是关注按钮的action方法.
四.静态分析
接下来对app进行脱壳.再使用IDA加载脱壳后的Mach-o文件,恢复App符号表可参考(http://blog.imjun.net/posts/restore-symbol-of-iOS-app/)
为了方便查看,使用F5后的伪代码来进行观察
LJJUSerProfileVC followClick 又调用了 LJJUSerProfileVC followClick]_block 函数
因为函数调用较多,就不一一贴图了,经过分析,以下为主要函数调用流程:
LJJUSerProfileVC followClick
LJJUSerProfileVC attention:
LJJAttentionManager userAttentionOther:isAttened:from:success:failure:
ZYAPIClient postWithPath:parameters:userInfo:progress:success:retry:failure:
ZYAPIClient postWithURL:parameters:userInfo:progress:success:retry:failure:
ZYURLManager requestHeaderDictionaryWithParameters:
ZYCrypto signWithParameters:
分析过程中发现比较重要的函数是ZYURLManager requestHeaderDictionaryWithParameters:和ZYCrypto signWithParameters:
ZYURLManager requestHeaderDictionaryWithParameters:主要功能就是h_model,u_id,h_ch,h_app,h_ts,token 等字段进行填充,其中h_ts为时间戳.
以下是ZYURLManager requestHeaderDictionaryWithParameters: 部分伪代码
ZYAPIClient signWithParameters: 从名字可以看出是对参数进行签名的,里面调用了-ZYCrypto signWithParameters:
进入-ZYCrypto signWithParameters:函数
从调用图可以看出sub_100200C40 又调用了sub_1002003C4 和 sub_100200BB0 函数
sub_100200C40 流程图
放大后更清楚
上图中,其中蓝小方块为调用sub_1002003C4 和 sub_100200BB0 函数,是整个函数必经之路
sub_1002003C4 流程图
sub_1002003C4 部分伪代码
我们知道md5算法中需要 0x67452301 0xefcdab89 0x98badcfe 0x10325476 数据做为初始值,以分组为单位进行处理,分别对每一块信息块进行MD5计算,每次计算4轮,每轮16次计算.
根据以上信息可以推断 sub_1002003C4 md5算法或其变形,不过还需要进一步分析才能确定
sub_100200BB0 伪代码
LJJUSerProfileVC followClick
LJJUSerProfileVC attention:
LJJAttentionManager userAttentionOther:isAttened:from:success:failure:
ZYAPIClient postWithPath:parameters:userInfo:progress:success:retry:failure:
ZYAPIClient postWithURL:parameters:userInfo:progress:success:retry:failure:
ZYURLManager requestHeaderDictionaryWithParameters:
ZYCrypto signWithParameters:
-ZYCrypto signWithParameters:
sub_100200C40
sub_1002003C4
sub_100200BB0
小结一下
ZYURLManager requestHeaderDictionaryWithParameters: 将需要提交的数据进行字段进行填充,大部分都是都是固定字段的如h_model,u_id,h_ch,h_app,h_ts,token等字段,这里就不做详细分析。
ZYCrypto signWithParameters 调用sub_100200C40 函数将数据进行签名.
五.动态调试
为了验证推测是否正确,需要对app进行动态调试。 iphone ssh 登录,开启 debugserver 。
Davinde-iPhone:~ root# debugserver *:10011 -a PPSocial
debugserver-@(#)PROGRAM:debugserver PROJECT:debugserver-340.3.51.1
for arm64.
Attaching to process PPSocial...
Listening to port 10011 for a connection from *...
Waiting for debugger instructions for process 0.
mac lldb 附加
process connect connect://192.168.31.199:10011
获取ASLR偏移
(lldb) image list -o -f PPSocial
[ 0] 0x0000000000024000 /var/mobile/Containers/Bundle/Application/C3170E24-F5A3-4FD8-AB42-532DA856722E/PPSocial.app/PPSocial(0x0000000100024000)
函数的内存地址(VM Address ) = File Offset + ASLR Offset IDA 中的地址是未使用ASLR的VM Address. 对关键的函数下断点,查看其参数和返回值
sub_1002003C4 函数头地址为0x100200c40 + 0x24000 = 0x100224c40
(lldb) br s -a 0x100224c40
Breakpoint 1: where = PPSocial`_mh_execute_header + 2079508, address = 0x0000000100224c40
成功下断后,运行app,点击关注。
Process 1457 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
frame #0: 0x0000000100224c40 PPSocial`_mh_execute_header + 2100288
PPSocial`_mh_execute_header:
-> 0x100224c40 <+2100288>: sub sp, sp, #0x90 ; =0x90
0x100224c44 <+2100292>: stp x22, x21, [sp, #0x60]
0x100224c48 <+2100296>: stp x20, x19, [sp, #0x70]
0x100224c4c <+2100300>: stp x29, x30, [sp, #0x80]
(lldb) memory read $x0 $x0+400
0x12876f268: 7b 22 68 5f 6d 6f 64 65 6c 22 3a 22 69 50 68 6f {"h_model":"iPho
0x12876f278: 6e 65 20 36 22 2c 22 68 5f 63 68 22 3a 22 61 70 ne 6","h_ch":"ap
0x12876f288: 70 73 74 6f 72 65 22 2c 22 75 69 64 22 3a 39 37 pstore","uid":97
0x12876f298: 35 31 37 32 39 30 2c 22 68 5f 61 70 70 22 3a 22 517290,"h_app":"
0x12876f2a8: 7a 75 69 79 6f 75 5f 6c 69 74 65 22 2c 22 68 5f zuiyou_lite","h_
0x12876f2b8: 74 73 22 3a 31 35 35 38 39 36 36 35 30 31 35 36 ts":155896650156
0x12876f2c8: 34 2c 22 68 5f 61 76 22 3a 22 34 2e 35 2e 35 22 4,"h_av":"4.5.5"
0x12876f2d8: 2c 22 68 5f 6e 74 22 3a 31 2c 22 68 5f 64 69 64 ,"h_nt":1,"h_did
0x12876f2e8: 22 3a 22 64 38 30 36 62 65 35 35 34 61 61 31 34 ":"d806be554aa14
0x12876f2f8: 34 63 34 37 34 64 30 35 61 63 38 61 37 66 35 36 4c474d05ac8a7f56
0x12876f308: 64 63 31 22 2c 22 68 5f 6d 22 3a 31 36 37 31 33 dc1","h_m":16713
0x12876f318: 35 32 34 39 2c 22 68 5f 70 69 70 69 22 3a 22 31 5249,"h_pipi":"1
0x12876f328: 2e 35 2e 35 22 2c 22 64 69 64 22 3a 22 64 38 30 .5.5","did":"d80
0x12876f338: 36 62 65 35 35 34 61 61 31 34 34 63 34 37 34 64 6be554aa144c474d
0x12876f348: 30 35 61 63 38 61 37 66 35 36 64 63 31 22 2c 22 05ac8a7f56dc1","
0x12876f358: 68 5f 6f 73 22 3a 39 31 30 30 30 30 2c 22 74 6f h_os":910000,"to
0x12876f368: 6b 65 6e 22 3a 22 54 30 4b 35 4e 6a 64 75 79 53 ken":"T0K5NjduyS
0x12876f378: 39 39 69 42 61 72 47 71 56 4c 52 62 52 51 74 68 99iBarGqVLRbRQth
0x12876f388: 78 37 63 4b 45 77 46 6e 4b 5f 45 6a 45 54 6a 7a x7cKEwFnK_EjETjz
0x12876f398: 5f 58 62 53 65 54 71 58 73 61 6d 68 45 79 61 46 _XbSeTqXsamhEyaF
0x12876f3a8: 74 48 4e 77 4c 6b 75 36 42 55 39 22 2c 22 68 5f tHNwLku6BU9","h_
0x12876f3b8: 64 74 22 3a 31 7d 00 00 00 00 00 00 00 00 00 e0 dt":1}.........�
0x12876f3c8: 00 00 00 00 00 00 00 e0 08 00 ff ff f5 01 00 00 .......�..���...
0x12876f3d8: f5 01 00 00 f5 01 00 00 f5 01 00 00 61 00 00 00 �...�...�...a...
0x12876f3e8: 00 00 00 00 62 00 00 00 70 b5 0d 28 01 00 00 00 ....b...p�.(....
(lldb)
查看参数可以看到就是post字符串数据.接下来再分析sub_1002003c4参数和返回值
分别对sub_1002003c4 入口和返回后地址下断
(lldb) br s -a 0x100224e30
Breakpoint 2: where = PPSocial`_mh_execute_header + 2080004, address = 0x0000000100224e30
(lldb) br s -a 0x100224e34
Breakpoint 3: where = PPSocial`_mh_execute_header + 2080008, address = 0x0000000100224e34
sub_1002003c4 有三个参数分别为 x0,x1,x2
-> 0x100224e30 <+2100784>: bl 0x1002243c4 ; PPSocial.__TEXT.__text + 2077336
0x100224e34 <+2100788>: sub x0, x29, #0x38 ; =0x38
0x100224e38 <+2100792>: mov x8, sp
0x100224e3c <+2100796>: bl 0x100224bb0 ; PPSocial.__TEXT.__text + 2079364
Target 0: (PPSocial) stopped.
(lldb) register read
General Purpose Registers:
x0 = 0x0000000128773ef0
x1 = 0x0000000000000162
x2 = 0x000000016fdd8ef8
x3 = 0x0000000000000016
x4 = 0x0000000000000158
x5 = 0x0000000000000010
x6 = 0x0000000000000022
x7 = 0x000000016fdd8eb0
x8 = 0x0000000000000080
x0为字符串数据,x1字符串长度,x2传出参数,字符串头尾进行处理.
lldb) memory read $x0 $x0+$x1
0x128773ef0: 59 30 4d 54 42 6c 4f 44 63 78 3a 22 69 50 68 6f Y0MTBlODcx:"iPho
0x128773f00: 6e 65 20 36 22 2c 22 68 5f 63 68 22 3a 22 61 70 ne 6","h_ch":"ap
0x128773f10: 70 73 74 6f 72 65 22 2c 22 75 69 64 22 3a 39 37 pstore","uid":97
0x128773f20: 35 31 37 32 39 30 2c 22 68 5f 61 70 70 22 3a 22 517290,"h_app":"
0x128773f30: 7a 75 69 79 6f 75 5f 6c 69 74 65 22 2c 22 68 5f zuiyou_lite","h_
0x128773f40: 74 73 22 3a 31 35 35 38 39 36 36 35 30 31 35 36 ts":155896650156
0x128773f50: 34 2c 22 68 5f 61 76 22 3a 22 34 2e 35 2e 35 22 4,"h_av":"4.5.5"
0x128773f60: 2c 22 68 5f 6e 74 22 3a 31 2c 22 68 5f 64 69 64 ,"h_nt":1,"h_did
0x128773f70: 22 3a 22 64 38 30 36 62 65 35 35 34 61 61 31 34 ":"d806be554aa14
0x128773f80: 34 63 34 37 34 64 30 35 61 63 38 61 37 66 35 36 4c474d05ac8a7f56
0x128773f90: 64 63 31 22 2c 22 68 5f 6d 22 3a 31 36 37 31 33 dc1","h_m":16713
0x128773fa0: 35 32 34 39 2c 22 68 5f 70 69 70 69 22 3a 22 31 5249,"h_pipi":"1
0x128773fb0: 2e 35 2e 35 22 2c 22 64 69 64 22 3a 22 64 38 30 .5.5","did":"d80
0x128773fc0: 36 62 65 35 35 34 61 61 31 34 34 63 34 37 34 64 6be554aa144c474d
0x128773fd0: 30 35 61 63 38 61 37 66 35 36 64 63 31 22 2c 22 05ac8a7f56dc1","
0x128773fe0: 68 5f 6f 73 22 3a 39 31 30 30 30 30 2c 22 74 6f h_os":910000,"to
0x128773ff0: 6b 65 6e 22 3a 22 54 30 4b 35 4e 6a 64 75 79 53 ken":"T0K5NjduyS
0x128774000: 39 39 69 42 61 72 47 71 56 4c 52 62 52 51 74 68 99iBarGqVLRbRQth
0x128774010: 78 37 63 4b 45 77 46 6e 4b 5f 45 6a 45 54 6a 7a x7cKEwFnK_EjETjz
0x128774020: 5f 58 62 53 65 54 71 58 73 61 6d 68 45 79 61 46 _XbSeTqXsamhEyaF
0x128774030: 74 48 4e 77 4c 6b 75 36 42 55 39 22 2c 22 68 5f tHNwLku6BU9","h_
0x128774040: 64 74 22 3a 31 7d 5a 44 7b 22 68 5f 6d 6f 64 65 dt":1}ZD{"h_mode
0x128774050: 6c 22
按c 继续,查看 sub_1002003c4 返回值
(lldb) c
Process 1457 resuming
Process 1457 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 3.1
frame #0: 0x0000000100224e34 PPSocial`_mh_execute_header + 2100788
PPSocial`_mh_execute_header:
-> 0x100224e34 <+2100788>: sub x0, x29, #0x38 ; =0x38
0x100224e38 <+2100792>: mov x8, sp
0x100224e3c <+2100796>: bl 0x100224bb0 ; PPSocial.__TEXT.__text + 2079364
0x100224e40 <+2100800>: ldrsb w20, [sp, #0x17]
Target 0: (PPSocial) stopped.
(lldb) memory read 0x000000016fdd8ef8
0x16fdd8ef8: 27 dd a1 ce 26 ed 16 f3 00 d5 51 93 5c 4c 10 a4 'ݡ�&�.�.�Q.\L.�
0x16fdd8f08: 76 8e 2b 21 e1 de 00 eb e8 90 dd 6f 01 00 00 00 v.+!��.��.�o....
经过抓包分析对比这个就是返回的签名数据
六.代码还原
直接将伪代码稍微修改就可以使用,以下是关键代码
__int64 sub_1002003C4(__int64 a1, __int64 a2, __int64 a3)
{
__int64 len; // x2
__int64 buf; // x3
signed int v5; // w5
__int64 v6; // x20
signed int h0; // w4
signed int h1; // w22
unsigned int h2; // w23
unsigned int h3; // w21
signed int new_len; // w26
char *new_buf; // x1
signed int a; // w21
__int64 v14; // x25
__int64 v15; // x20
signed int v16; // w24
__int64 i_1; // x8
unsigned __int64 v18; // x12
int *v19; // x13
char *v20; // x14
signed int a_1; // w8
unsigned int b_1; // w9
unsigned int c_1; // w10
signed int d_1; // w11
int v25; // w11
unsigned __int64 v26; // t2
int v27; // w8
int v28; // w16
int v29; // w17
int v30; // w9
int b; // w0
int v32; // w16
int v33; // w9
int v34; // w10
unsigned __int64 v35; // x12
int *v36; // x13
signed __int64 v37; // x14
char *v38; // x15
int v39; // w11
unsigned __int64 v40; // t2
int v41; // w8
int v42; // w9
int v43; // t1
int v44; // w10
unsigned __int64 v45; // x12
int *v46; // x13
signed __int64 v47; // x14
char *v48; // x15
int v49; // w11
unsigned __int64 v50; // t2
int v51; // w8
int v52; // w9
int v53; // w17
int v54; // t1
int v55; // t1
int v56; // w10
unsigned __int64 v57; // x12
int *v58; // x13
char *v59; // x14
signed __int64 v60; // x15
int v61; // w11
unsigned __int64 v62; // t2
int v63; // w8
int v64; // t1
int v65; // w9
__int64 result; // x0
unsigned int v67; // t1
int v68; // w10
__int64 i; // x8
int v70; // w10
__int64 v71; // x11
unsigned __int64 v72; // x10
__int64 v73; // [xsp+8h] [xbp-128h]
unsigned int v74; // [xsp+28h] [xbp-108h]
int v75; // [xsp+30h] [xbp-100h]
unsigned int d; // [xsp+3Ch] [xbp-F4h]
__int64 v77; // [xsp+40h] [xbp-F0h]
char v78[64]; // [xsp+48h] [xbp-E8h]
unsigned __int64 v79[64]; // [xsp+88h] [xbp-A8h]
//unsigned __int64 v80; // [xsp+90h] [xbp-A0h]
char msg[64]; // [xsp+98h] [xbp-98h]
v73 = a3;
len = a2;
buf = a1;
v5 = 0;
v6 = 0LL;
//v79 = 0xEFCDAB8967452301LL;
v79[0] = *((__int64*)(__int64)buf);
v75 = 8 * a2;
v74 = (unsigned int)a2 >> 0x1D;
//v80 = 0x1032547698BADCFELL;
v79[1] = *(((__int64*)(__int64)buf) + 1);
h0 = 0x67452301;
h1 = 0x10325476;
h2 = 0x98BADCFE;
h3 = 0xEFCDAB89;
do
{
if (len - v6 <= 63)
new_len = len - v6;
else
new_len = 64;
new_buf = (char *)(buf + v6);
if (new_len > 63)
{
v5 = 0;
}
else
{
d = h3;
v77 = v6;
a = h0;
v14 = buf;
v15 = len;
v16 = v5;
memcpy(msg, new_buf, new_len);
ZeroMemory(&msg[new_len], 64 - new_len);
if (v16)
{
v5 = 1;
new_buf = msg;
}
else
{
new_buf = msg;
msg[new_len] = 128;
v5 = 1;
}
len = v15;
buf = v14;
h0 = a;
v6 = v77;
h3 = d;
}
i_1 = 0LL;
do
{
*(_DWORD *)&v78[i_1] = *(_DWORD *)&new_buf[i_1];
i_1 += 4LL;
} while (i_1 != 64);
if (new_len <= 0x37)
{
*(_DWORD *)&v78[56] = v75;
*(_DWORD *)&v78[60] = v74;
}
v18 = 0LL;
// v19 = (int *)&unk_10089FE28;
v19 = (int*)((BYTE*)&unk_10089FE20 + 0x8);
v20 = &v78[8];
a_1 = h1;
b_1 = h2;
c_1 = h3;
d_1 = h0;
do
{
v25 = (a_1 & ~c_1 | c_1 & b_1) + d_1 + *((_DWORD *)v20 - 2) + *(v19 - 2);
HIDWORD(v26) = v25;
LODWORD(v26) = v25;
d_1 = (v26 >> 25) + c_1;
v27 = *((_DWORD *)v20 - 1) + a_1 + *(v19 - 1) + (d_1 & c_1 | b_1 & ~d_1);
HIDWORD(v26) = v27;
LODWORD(v26) = v27;
a_1 = (v26 >> 20) + d_1;
v28 = *(_DWORD *)v20;
v29 = *((_DWORD *)v20 + 1);
v20 += 16;
v30 = v28 + b_1;
v32 = *v19;
b = v19[1];
v19 += 4;
v33 = v30 + v32 + (a_1 & d_1 | c_1 & ~a_1);
HIDWORD(v26) = v33;
LODWORD(v26) = v33;
b_1 = (v26 >> 15) + a_1;
v34 = v29 + c_1 + b + (b_1 & a_1 | d_1 & ~b_1);
HIDWORD(v26) = v34;
LODWORD(v26) = v34;
c_1 = (v26 >> 10) + b_1;
v18 += 4LL;
} while (v18 < 16);
v35 = 0LL;
//v36 = &dword_10089FE6C;
v36 = (int*)((BYTE*)&unk_10089FE20 + 0x4C);
v37 = 10LL;
v38 = &v78[4];
do
{
v39 = (c_1 & a_1 | b_1 & ~a_1) + d_1 + *(_DWORD *)v38 + *(v36 - 3);
HIDWORD(v40) = v39;
LODWORD(v40) = v39;
d_1 = (v40 >> 27) + c_1;
v41 = *(_DWORD *)&v78[4 * (((_BYTE)v37 - 4) & 0xE)] + a_1 + *(v36 - 2) + (d_1 & b_1 | c_1 & ~b_1);
HIDWORD(v40) = v41;
LODWORD(v40) = v41;
a_1 = (v40 >> 23) + d_1;
v42 = *(_DWORD *)&v78[8 * (((unsigned __int64)(v37 & 0xE) >> 1) & 7) | 4]
+ b_1
+ *(v36 - 1)
+ (a_1 & c_1 | d_1 & ~c_1);
HIDWORD(v40) = v42;
LODWORD(v40) = v42;
b_1 = (v40 >> 18) + a_1;
v43 = *v36;
v36 += 4;
v44 = *((_DWORD *)v38 - 1) + c_1 + v43 + (b_1 & d_1 | a_1 & ~d_1);
HIDWORD(v40) = v44;
LODWORD(v40) = v44;
c_1 = (v40 >> 12) + b_1;
v35 += 4LL;
v38 += 16;
v37 += 20LL;
} while (v35 < 16);
v45 = 0LL;
//v46 = &dword_10089FEAC;
v46 = (int*)((BYTE*)&unk_10089FE20 + 0x8C);
v47 = 5LL;
v48 = &v78[56];
do
{
v49 = (c_1 ^ b_1 ^ a_1) + d_1 + *(_DWORD *)&v78[4 * (v47 & 0xD)] + *(v46 - 3);
HIDWORD(v50) = v49;
LODWORD(v50) = v49;
d_1 = (v50 >> 28) + c_1;
v51 = *(_DWORD *)&v78[4 * (((_BYTE)v47 + 3) & 0xC)] + a_1 + *(v46 - 2) + (d_1 ^ c_1 ^ b_1);
HIDWORD(v50) = v51;
LODWORD(v50) = v51;
a_1 = (v50 >> 21) + d_1;
v52 = *(_DWORD *)&v78[4 * (((_BYTE)v47 + 6) & 0xF)] + b_1 + *(v46 - 1) + (a_1 ^ d_1 ^ c_1);
HIDWORD(v50) = v52;
LODWORD(v50) = v52;
b_1 = (v50 >> 16) + a_1;
v54 = *(_DWORD *)v48;
v48 -= 16;
v53 = v54;
v55 = *v46;
v46 += 4;
v56 = v53 + c_1 + v55 + (a_1 ^ d_1 ^ b_1);
HIDWORD(v50) = v56;
LODWORD(v50) = v56;
c_1 = (v50 >> 9) + b_1;
v45 += 4LL;
v47 += 12LL;
} while (v45 < 0x10);
v57 = 0LL;
//v58 = &dword_10089FEEC;
v58 = (int*)((BYTE*)&unk_10089FE20 + 0xcc);
v59 = &v78[56];
v60 = 21LL;
do
{
v61 = ((c_1 | ~a_1) ^ b_1) + d_1 + *(_DWORD *)&v78[4 * (((_BYTE)v60 - 21) & 0xC)] + *(v58 - 3);
HIDWORD(v62) = v61;
LODWORD(v62) = v61;
d_1 = (v62 >> 26) + c_1;
v63 = *(_DWORD *)&v78[4 * (((_BYTE)v60 - 14) & 0xF)] + a_1 + *(v58 - 2) + ((d_1 | ~b_1) ^ c_1);
HIDWORD(v62) = v63;
LODWORD(v62) = v63;
a_1 = (v62 >> 22) + d_1;
v64 = *(_DWORD *)v59;
v59 -= 16;
v65 = v64 + b_1 + *(v58 - 1) + ((a_1 | ~c_1) ^ d_1);
HIDWORD(v62) = v65;
LODWORD(v62) = v65;
b_1 = (v62 >> 17) + a_1;
v67 = *v58;
v58 += 4;
result = v67;
v68 = *(_DWORD *)&v78[4 * (v60 & 0xD)] + c_1 + v67 + ((b_1 | ~d_1) ^ a_1);
HIDWORD(v62) = v68;
LODWORD(v62) = v68;
c_1 = (v62 >> 11) + b_1;
v57 += 4LL;
v60 += 28LL;
} while (v57 < 16);
h0 += d_1;
h3 += c_1;
h2 += b_1;
h1 += a_1;
v6 += new_len;
} while (new_len > 55);
i = 0LL;
v79[0] = __PAIR__(h3, h0);
v79[1]= __PAIR__(h1, h2);
*(_WORD *)v73 = h0;
*(_BYTE *)(v73 + 2) = BYTE2(h0);
*(_BYTE *)(v73 + 3) = HIBYTE(h0);
do
{
v70 = *(_DWORD *)((char *)&v79 + i + 4);
v71 = v73 + i;
*(_WORD *)(v71 + 4) = v70;
*(_BYTE *)(v71 + 6) = BYTE2(v70);
*(_BYTE *)(v71 + 7) = HIBYTE(v70);
v72 = i + 8;
i += 4LL;
} while (v72 < 16);
return result;
}
void sub_100200BB0(unsigned char *buf,char* outbuf)
{
const char *set = "0123456789abcdef";
char *tmp;
tmp = outbuf;
for (int i = 0; i < 0x10;i++)
{
*tmp++ = set[(*buf) >> 4];
*tmp++ = set[(*buf) & 0xF];
buf++;
}
*tmp = '\0';
}
static char * sub_100200C40(string data)
{
//string
string strbuf= data;
strbuf += byte_100E379FF;
string strDatahead = strbuf.substr(0, 10);
string strDataend = strbuf.substr(strbuf.length() - 10, strbuf.length());
strbuf.replace(0, 10, strDataend);
strbuf.replace(strbuf.length() - 10, strbuf.length(), strDatahead);
char szsignbuf[64] = { 0 };
//md5
sub_1002003C4((__int64)strbuf.c_str(), strbuf.length(), (__int64)szsignbuf);
static char szstrbuf[64] = { 0 };
//hextostr
sub_100200BB0((unsigned char*)szsignbuf, szstrbuf);
return szstrbuf;
}
void fllowclick()
{
char* pszsign = NULL;
string strpostdata = "{\"h_model\":\"iPhone 6\",\"h_ch\":\"appstore\",\"uid\":86743096,\"h_app\":\"zuiyou_lite\",\"h_ts\":1559223191423,\"h_av\":\"4.5.5\",\"h_nt\":1,\"h_did\":\"d806be554aa144c474d05ac8a7f56dc1\",\"h_m\":167135249,\"h_pipi\":\"1.5.5\",\"did\":\"d806be554aa144c474d05ac8a7f56dc1\",\"h_os\":910000,\"token\":\"T3KbNjduyS99iBarGqVLRbRQth40at0OX5FiZugjHhEbynKsitGYLASgxkXp44kS1TzBh\",\"h_dt\":1}";
string strpost = "POST /attention/add?sign=";
pszsign = sub_100200C40(strpostdata);
strpost += pszsign;
strpost += " HTTP/1.1\r\n";
strpost += "Host: api.ippzone.com\r\n";
strpost += "App-Ver: 4.5.5\r\n";
strpost += "Accept: */*\r\n";
strpost += "ZYP: mid=167135249\r\n";
strpost += "Dev-Type: ios\r\n";
strpost += "Content-Type: application/json\r\n";
strpost += "Accept-Language: zh-Hans-CN;q=1\r\n";
strpost += "Accept-Encoding: gzip, deflate\r\n";
strpost += "ZYP: mid=167135249\r\n";
strpost += "Content-Length: 342\r\n";
strpost += "did: d806be554aa144c474d05ac8a7f56dc1\r\n";
strpost += "User-Agent: PPSocial/1.5.5 (iPhone; iOS 9.1; Scale/2.00)\r\n";
strpost += "Connection: keep-alive\r\n";
strpost += "Cookie: aliyungf_tc=AQAAAAxoX0ooZQQAVgISG16+OqOGG0RR\r\n";
strpost += "\r\n";
strpost += strpostdata;
postdata(strpost);
}
void postdata(string data)
{
printf(data.c_str());
printf("\r\n");
system("pause");
}
int main()
{
fllowclick();
return 0;
}
总结
整个分析流程大概就这些了,通过界面分析到功能函数静态和动态分析,这也是在逆向中常用的思路。
Iphone6 IOS 9.1
Macos Mojave 10.14.5
三.界面分析
通过分析app UI界面为突破口,来进行逆向分析。 打开App后,如下图,可以看到一个关注按钮,通过点击按钮,可以实现关注该用户动态。
通过抓包分析,点击关注按钮时,会发送add?sign=xxxxxx数据包,想要分析数据来源,可以从界面上的关注按钮着手.
为了更直观分析,这里使用Reveal神器来分析UI界面,在使用Reveal前要在手机设置 > Reveal->Enabled Applications 里面打开需要分析的App.
配置好Reveal后,在手机上打开需要分析的app界面,再在笔记本上打开Reveal工具,Reveal支持USB和网络连接两种方式,一般选择USB.
打开Reveal后,如下图,可以看到AppUI 应用结构层级
通过分析AppUI 应用结构层级,可以明显看到红色关注按钮,直接点击按钮,可以看到的确是一个UIButton,内存地址0x143883f80,视图控制器Class 为 LJJUSerProfileVC,内存地址0x140abb400
为了验证数据是否正确,使用Cycript(动态调式工具),来获取运行时信息
使用ssh登录iphone,执行命令 crcript -p pid或进程名
Davinde-iPhone:/var root# cycript -p PPSocial
cy# #0x143883f80
#"<UIButton: 0x143883f80; frame = (270 29; 50 26); alpha = 0; opaque = NO; layer = <CALayer: 0x1438a4cc0>>"
通过以上信息可以判断,0x143883f80的确是UIButton按钮的地址,如果不确定,可以通过调用hidden方法,并且观察app对应的按钮是否有变化.
cy# #0x143883f80.hidden=YES //隐藏按钮
true
cy# #0x143883f80.hidden=NO //显示按钮
false
光知道UiButton内存地址好像没有用,如何得到UiBtton的点击事件的所调用函数呢? 通过cycript 脚可以来遍历
@import mycript //导入mycript 脚本
cy# MyBtnTouchUpEvent(#0x143883f80)
[@["followClick"]]
下面是MyBtnTouchUpEvent代码,获取该Button对象所有的Target对象,并将事件相关所有action遍历输出,顺提一下,action可能有多个
// 获取按钮绑定的所有TouchUpInside事件的方法名
MyBtnTouchUpEvent = function(btn) {
var events = [];
var allTargets = btn.allTargets().allObjects()
var count = allTargets.count;
for (var i = count - 1; i >= 0; i--) {
if (btn != allTargets[i]) {
var e = [btn actionsForTarget:allTargets[i] forControlEvent:UIControlEventTouchUpInside];
events.push(e);
}
}
return events;
};
通过MyBtnTouchUpEvent得到followClick方法,需要测试下这个方法是否正确,通过调用LJJUSerProfileVC控制器的 followClick方法来测试一下
cy# [#0x140abb400 followClick] //#0x140abb400为LJJUSerProfileVC对象
通过反复[#0x140abb400 followClick] 方法调用,可以实现手动点击关注效果,现在可以确定LJJUSerProfileVC followClick就是关注按钮的action方法.
四.静态分析
接下来对app进行脱壳.再使用IDA加载脱壳后的Mach-o文件,恢复App符号表可参考(http://blog.imjun.net/posts/restore-symbol-of-iOS-app/)
为了方便查看,使用F5后的伪代码来进行观察
LJJUSerProfileVC followClick 又调用了 LJJUSerProfileVC followClick]_block 函数
因为函数调用较多,就不一一贴图了,经过分析,以下为主要函数调用流程:
LJJUSerProfileVC followClick
LJJUSerProfileVC attention:
LJJAttentionManager userAttentionOther:isAttened:from:success:failure:
ZYAPIClient postWithPath:parameters:userInfo:progress:success:retry:failure:
ZYAPIClient postWithURL:parameters:userInfo:progress:success:retry:failure:
ZYURLManager requestHeaderDictionaryWithParameters:
ZYCrypto signWithParameters:
分析过程中发现比较重要的函数是ZYURLManager requestHeaderDictionaryWithParameters:和ZYCrypto signWithParameters:
ZYURLManager requestHeaderDictionaryWithParameters:主要功能就是h_model,u_id,h_ch,h_app,h_ts,token 等字段进行填充,其中h_ts为时间戳.
以下是ZYURLManager requestHeaderDictionaryWithParameters: 部分伪代码
ZYAPIClient signWithParameters: 从名字可以看出是对参数进行签名的,里面调用了-ZYCrypto signWithParameters:
进入-ZYCrypto signWithParameters:函数
从调用图可以看出sub_100200C40 又调用了sub_1002003C4 和 sub_100200BB0 函数
sub_100200C40 流程图
放大后更清楚
上图中,其中蓝小方块为调用sub_1002003C4 和 sub_100200BB0 函数,是整个函数必经之路
sub_1002003C4 流程图
sub_1002003C4 部分伪代码
我们知道md5算法中需要 0x67452301 0xefcdab89 0x98badcfe 0x10325476 数据做为初始值,以分组为单位进行处理,分别对每一块信息块进行MD5计算,每次计算4轮,每轮16次计算.
根据以上信息可以推断 sub_1002003C4 md5算法或其变形,不过还需要进一步分析才能确定
sub_100200BB0 伪代码
LJJUSerProfileVC followClick
LJJUSerProfileVC attention:
LJJAttentionManager userAttentionOther:isAttened:from:success:failure:
ZYAPIClient postWithPath:parameters:userInfo:progress:success:retry:failure:
ZYAPIClient postWithURL:parameters:userInfo:progress:success:retry:failure:
ZYURLManager requestHeaderDictionaryWithParameters:
ZYCrypto signWithParameters:
-ZYCrypto signWithParameters:
sub_100200C40
sub_1002003C4
sub_100200BB0
小结一下
ZYURLManager requestHeaderDictionaryWithParameters: 将需要提交的数据进行字段进行填充,大部分都是都是固定字段的如h_model,u_id,h_ch,h_app,h_ts,token等字段,这里就不做详细分析。
ZYCrypto signWithParameters 调用sub_100200C40 函数将数据进行签名.
五.动态调试
为了验证推测是否正确,需要对app进行动态调试。 iphone ssh 登录,开启 debugserver 。
Davinde-iPhone:~ root# debugserver *:10011 -a PPSocial
debugserver-@(#)PROGRAM:debugserver PROJECT:debugserver-340.3.51.1
for arm64.
Attaching to process PPSocial...
Listening to port 10011 for a connection from *...
Waiting for debugger instructions for process 0.
mac lldb 附加
process connect connect://192.168.31.199:10011
获取ASLR偏移
(lldb) image list -o -f PPSocial
[ 0] 0x0000000000024000 /var/mobile/Containers/Bundle/Application/C3170E24-F5A3-4FD8-AB42-532DA856722E/PPSocial.app/PPSocial(0x0000000100024000)
函数的内存地址(VM Address ) = File Offset + ASLR Offset IDA 中的地址是未使用ASLR的VM Address. 对关键的函数下断点,查看其参数和返回值
sub_1002003C4 函数头地址为0x100200c40 + 0x24000 = 0x100224c40
(lldb) br s -a 0x100224c40
Breakpoint 1: where = PPSocial`_mh_execute_header + 2079508, address = 0x0000000100224c40
成功下断后,运行app,点击关注。
Process 1457 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
frame #0: 0x0000000100224c40 PPSocial`_mh_execute_header + 2100288
PPSocial`_mh_execute_header:
-> 0x100224c40 <+2100288>: sub sp, sp, #0x90 ; =0x90
0x100224c44 <+2100292>: stp x22, x21, [sp, #0x60]
0x100224c48 <+2100296>: stp x20, x19, [sp, #0x70]
0x100224c4c <+2100300>: stp x29, x30, [sp, #0x80]
(lldb) memory read $x0 $x0+400
0x12876f268: 7b 22 68 5f 6d 6f 64 65 6c 22 3a 22 69 50 68 6f {"h_model":"iPho
0x12876f278: 6e 65 20 36 22 2c 22 68 5f 63 68 22 3a 22 61 70 ne 6","h_ch":"ap
0x12876f288: 70 73 74 6f 72 65 22 2c 22 75 69 64 22 3a 39 37 pstore","uid":97
0x12876f298: 35 31 37 32 39 30 2c 22 68 5f 61 70 70 22 3a 22 517290,"h_app":"
0x12876f2a8: 7a 75 69 79 6f 75 5f 6c 69 74 65 22 2c 22 68 5f zuiyou_lite","h_
0x12876f2b8: 74 73 22 3a 31 35 35 38 39 36 36 35 30 31 35 36 ts":155896650156
0x12876f2c8: 34 2c 22 68 5f 61 76 22 3a 22 34 2e 35 2e 35 22 4,"h_av":"4.5.5"
0x12876f2d8: 2c 22 68 5f 6e 74 22 3a 31 2c 22 68 5f 64 69 64 ,"h_nt":1,"h_did
0x12876f2e8: 22 3a 22 64 38 30 36 62 65 35 35 34 61 61 31 34 ":"d806be554aa14
0x12876f2f8: 34 63 34 37 34 64 30 35 61 63 38 61 37 66 35 36 4c474d05ac8a7f56
0x12876f308: 64 63 31 22 2c 22 68 5f 6d 22 3a 31 36 37 31 33 dc1","h_m":16713
0x12876f318: 35 32 34 39 2c 22 68 5f 70 69 70 69 22 3a 22 31 5249,"h_pipi":"1
0x12876f328: 2e 35 2e 35 22 2c 22 64 69 64 22 3a 22 64 38 30 .5.5","did":"d80
0x12876f338: 36 62 65 35 35 34 61 61 31 34 34 63 34 37 34 64 6be554aa144c474d
0x12876f348: 30 35 61 63 38 61 37 66 35 36 64 63 31 22 2c 22 05ac8a7f56dc1","
0x12876f358: 68 5f 6f 73 22 3a 39 31 30 30 30 30 2c 22 74 6f h_os":910000,"to
0x12876f368: 6b 65 6e 22 3a 22 54 30 4b 35 4e 6a 64 75 79 53 ken":"T0K5NjduyS
0x12876f378: 39 39 69 42 61 72 47 71 56 4c 52 62 52 51 74 68 99iBarGqVLRbRQth
0x12876f388: 78 37 63 4b 45 77 46 6e 4b 5f 45 6a 45 54 6a 7a x7cKEwFnK_EjETjz
0x12876f398: 5f 58 62 53 65 54 71 58 73 61 6d 68 45 79 61 46 _XbSeTqXsamhEyaF
0x12876f3a8: 74 48 4e 77 4c 6b 75 36 42 55 39 22 2c 22 68 5f tHNwLku6BU9","h_
0x12876f3b8: 64 74 22 3a 31 7d 00 00 00 00 00 00 00 00 00 e0 dt":1}.........�
0x12876f3c8: 00 00 00 00 00 00 00 e0 08 00 ff ff f5 01 00 00 .......�..���...
0x12876f3d8: f5 01 00 00 f5 01 00 00 f5 01 00 00 61 00 00 00 �...�...�...a...
0x12876f3e8: 00 00 00 00 62 00 00 00 70 b5 0d 28 01 00 00 00 ....b...p�.(....
(lldb)
查看参数可以看到就是post字符串数据.接下来再分析sub_1002003c4参数和返回值
分别对sub_1002003c4 入口和返回后地址下断
(lldb) br s -a 0x100224e30
Breakpoint 2: where = PPSocial`_mh_execute_header + 2080004, address = 0x0000000100224e30
(lldb) br s -a 0x100224e34
Breakpoint 3: where = PPSocial`_mh_execute_header + 2080008, address = 0x0000000100224e34
sub_1002003c4 有三个参数分别为 x0,x1,x2
-> 0x100224e30 <+2100784>: bl 0x1002243c4 ; PPSocial.__TEXT.__text + 2077336
0x100224e34 <+2100788>: sub x0, x29, #0x38 ; =0x38
0x100224e38 <+2100792>: mov x8, sp
0x100224e3c <+2100796>: bl 0x100224bb0 ; PPSocial.__TEXT.__text + 2079364
Target 0: (PPSocial) stopped.
(lldb) register read
General Purpose Registers:
x0 = 0x0000000128773ef0
x1 = 0x0000000000000162
x2 = 0x000000016fdd8ef8
x3 = 0x0000000000000016
x4 = 0x0000000000000158
x5 = 0x0000000000000010
x6 = 0x0000000000000022
x7 = 0x000000016fdd8eb0
x8 = 0x0000000000000080
x0为字符串数据,x1字符串长度,x2传出参数,字符串头尾进行处理.
lldb) memory read $x0 $x0+$x1
0x128773ef0: 59 30 4d 54 42 6c 4f 44 63 78 3a 22 69 50 68 6f Y0MTBlODcx:"iPho
0x128773f00: 6e 65 20 36 22 2c 22 68 5f 63 68 22 3a 22 61 70 ne 6","h_ch":"ap
0x128773f10: 70 73 74 6f 72 65 22 2c 22 75 69 64 22 3a 39 37 pstore","uid":97
0x128773f20: 35 31 37 32 39 30 2c 22 68 5f 61 70 70 22 3a 22 517290,"h_app":"
0x128773f30: 7a 75 69 79 6f 75 5f 6c 69 74 65 22 2c 22 68 5f zuiyou_lite","h_
0x128773f40: 74 73 22 3a 31 35 35 38 39 36 36 35 30 31 35 36 ts":155896650156
0x128773f50: 34 2c 22 68 5f 61 76 22 3a 22 34 2e 35 2e 35 22 4,"h_av":"4.5.5"
0x128773f60: 2c 22 68 5f 6e 74 22 3a 31 2c 22 68 5f 64 69 64 ,"h_nt":1,"h_did
0x128773f70: 22 3a 22 64 38 30 36 62 65 35 35 34 61 61 31 34 ":"d806be554aa14
0x128773f80: 34 63 34 37 34 64 30 35 61 63 38 61 37 66 35 36 4c474d05ac8a7f56
0x128773f90: 64 63 31 22 2c 22 68 5f 6d 22 3a 31 36 37 31 33 dc1","h_m":16713
0x128773fa0: 35 32 34 39 2c 22 68 5f 70 69 70 69 22 3a 22 31 5249,"h_pipi":"1
0x128773fb0: 2e 35 2e 35 22 2c 22 64 69 64 22 3a 22 64 38 30 .5.5","did":"d80
0x128773fc0: 36 62 65 35 35 34 61 61 31 34 34 63 34 37 34 64 6be554aa144c474d
0x128773fd0: 30 35 61 63 38 61 37 66 35 36 64 63 31 22 2c 22 05ac8a7f56dc1","
0x128773fe0: 68 5f 6f 73 22 3a 39 31 30 30 30 30 2c 22 74 6f h_os":910000,"to
0x128773ff0: 6b 65 6e 22 3a 22 54 30 4b 35 4e 6a 64 75 79 53 ken":"T0K5NjduyS
0x128774000: 39 39 69 42 61 72 47 71 56 4c 52 62 52 51 74 68 99iBarGqVLRbRQth
0x128774010: 78 37 63 4b 45 77 46 6e 4b 5f 45 6a 45 54 6a 7a x7cKEwFnK_EjETjz
0x128774020: 5f 58 62 53 65 54 71 58 73 61 6d 68 45 79 61 46 _XbSeTqXsamhEyaF
0x128774030: 74 48 4e 77 4c 6b 75 36 42 55 39 22 2c 22 68 5f tHNwLku6BU9","h_
0x128774040: 64 74 22 3a 31 7d 5a 44 7b 22 68 5f 6d 6f 64 65 dt":1}ZD{"h_mode
0x128774050: 6c 22
按c 继续,查看 sub_1002003c4 返回值
(lldb) c
Process 1457 resuming
Process 1457 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 3.1
frame #0: 0x0000000100224e34 PPSocial`_mh_execute_header + 2100788
PPSocial`_mh_execute_header:
-> 0x100224e34 <+2100788>: sub x0, x29, #0x38 ; =0x38
0x100224e38 <+2100792>: mov x8, sp
0x100224e3c <+2100796>: bl 0x100224bb0 ; PPSocial.__TEXT.__text + 2079364
0x100224e40 <+2100800>: ldrsb w20, [sp, #0x17]
Target 0: (PPSocial) stopped.
(lldb) memory read 0x000000016fdd8ef8
0x16fdd8ef8: 27 dd a1 ce 26 ed 16 f3 00 d5 51 93 5c 4c 10 a4 'ݡ�&�.�.�Q.\L.�
0x16fdd8f08: 76 8e 2b 21 e1 de 00 eb e8 90 dd 6f 01 00 00 00 v.+!��.��.�o....
经过抓包分析对比这个就是返回的签名数据
六.代码还原
直接将伪代码稍微修改就可以使用,以下是关键代码
__int64 sub_1002003C4(__int64 a1, __int64 a2, __int64 a3)
{
__int64 len; // x2
__int64 buf; // x3
signed int v5; // w5
__int64 v6; // x20
signed int h0; // w4
signed int h1; // w22
unsigned int h2; // w23
unsigned int h3; // w21
signed int new_len; // w26
char *new_buf; // x1
signed int a; // w21
__int64 v14; // x25
__int64 v15; // x20
signed int v16; // w24
__int64 i_1; // x8
unsigned __int64 v18; // x12
int *v19; // x13
char *v20; // x14
signed int a_1; // w8
unsigned int b_1; // w9
unsigned int c_1; // w10
signed int d_1; // w11
int v25; // w11
unsigned __int64 v26; // t2
int v27; // w8
int v28; // w16
int v29; // w17
int v30; // w9
int b; // w0
int v32; // w16
int v33; // w9
int v34; // w10
unsigned __int64 v35; // x12
int *v36; // x13
signed __int64 v37; // x14
char *v38; // x15
int v39; // w11
unsigned __int64 v40; // t2
int v41; // w8
int v42; // w9
int v43; // t1
int v44; // w10
unsigned __int64 v45; // x12
int *v46; // x13
signed __int64 v47; // x14
char *v48; // x15
int v49; // w11
unsigned __int64 v50; // t2
int v51; // w8
int v52; // w9
int v53; // w17
int v54; // t1
int v55; // t1
int v56; // w10
unsigned __int64 v57; // x12
int *v58; // x13
char *v59; // x14
signed __int64 v60; // x15
int v61; // w11
unsigned __int64 v62; // t2
int v63; // w8
int v64; // t1
int v65; // w9
__int64 result; // x0
unsigned int v67; // t1
int v68; // w10
__int64 i; // x8
int v70; // w10
__int64 v71; // x11
unsigned __int64 v72; // x10
__int64 v73; // [xsp+8h] [xbp-128h]
unsigned int v74; // [xsp+28h] [xbp-108h]
int v75; // [xsp+30h] [xbp-100h]
unsigned int d; // [xsp+3Ch] [xbp-F4h]
__int64 v77; // [xsp+40h] [xbp-F0h]
char v78[64]; // [xsp+48h] [xbp-E8h]
unsigned __int64 v79[64]; // [xsp+88h] [xbp-A8h]
//unsigned __int64 v80; // [xsp+90h] [xbp-A0h]
char msg[64]; // [xsp+98h] [xbp-98h]
v73 = a3;
len = a2;
buf = a1;
v5 = 0;
v6 = 0LL;
//v79 = 0xEFCDAB8967452301LL;
v79[0] = *((__int64*)(__int64)buf);
v75 = 8 * a2;
v74 = (unsigned int)a2 >> 0x1D;
//v80 = 0x1032547698BADCFELL;
v79[1] = *(((__int64*)(__int64)buf) + 1);
h0 = 0x67452301;
h1 = 0x10325476;
h2 = 0x98BADCFE;
h3 = 0xEFCDAB89;
do
{
if (len - v6 <= 63)
new_len = len - v6;
else
new_len = 64;
new_buf = (char *)(buf + v6);
if (new_len > 63)
{
v5 = 0;
}
else
{
d = h3;
v77 = v6;
a = h0;
v14 = buf;
v15 = len;
v16 = v5;
memcpy(msg, new_buf, new_len);
ZeroMemory(&msg[new_len], 64 - new_len);
if (v16)
{
v5 = 1;
new_buf = msg;
}
else
{
new_buf = msg;
msg[new_len] = 128;
v5 = 1;
}
len = v15;
buf = v14;
h0 = a;
v6 = v77;
h3 = d;
}
i_1 = 0LL;
do
{
*(_DWORD *)&v78[i_1] = *(_DWORD *)&new_buf[i_1];
i_1 += 4LL;
} while (i_1 != 64);
if (new_len <= 0x37)
{
*(_DWORD *)&v78[56] = v75;
*(_DWORD *)&v78[60] = v74;
}
v18 = 0LL;
// v19 = (int *)&unk_10089FE28;
v19 = (int*)((BYTE*)&unk_10089FE20 + 0x8);
v20 = &v78[8];
a_1 = h1;
b_1 = h2;
c_1 = h3;
d_1 = h0;
do
{
v25 = (a_1 & ~c_1 | c_1 & b_1) + d_1 + *((_DWORD *)v20 - 2) + *(v19 - 2);
HIDWORD(v26) = v25;
LODWORD(v26) = v25;
d_1 = (v26 >> 25) + c_1;
v27 = *((_DWORD *)v20 - 1) + a_1 + *(v19 - 1) + (d_1 & c_1 | b_1 & ~d_1);
HIDWORD(v26) = v27;
LODWORD(v26) = v27;
a_1 = (v26 >> 20) + d_1;
v28 = *(_DWORD *)v20;
v29 = *((_DWORD *)v20 + 1);
v20 += 16;
v30 = v28 + b_1;
v32 = *v19;
b = v19[1];
v19 += 4;
v33 = v30 + v32 + (a_1 & d_1 | c_1 & ~a_1);
HIDWORD(v26) = v33;
LODWORD(v26) = v33;
b_1 = (v26 >> 15) + a_1;
v34 = v29 + c_1 + b + (b_1 & a_1 | d_1 & ~b_1);
HIDWORD(v26) = v34;
LODWORD(v26) = v34;
c_1 = (v26 >> 10) + b_1;
v18 += 4LL;
} while (v18 < 16);
v35 = 0LL;
//v36 = &dword_10089FE6C;
v36 = (int*)((BYTE*)&unk_10089FE20 + 0x4C);
v37 = 10LL;
v38 = &v78[4];
do
{
v39 = (c_1 & a_1 | b_1 & ~a_1) + d_1 + *(_DWORD *)v38 + *(v36 - 3);
HIDWORD(v40) = v39;
LODWORD(v40) = v39;
d_1 = (v40 >> 27) + c_1;
v41 = *(_DWORD *)&v78[4 * (((_BYTE)v37 - 4) & 0xE)] + a_1 + *(v36 - 2) + (d_1 & b_1 | c_1 & ~b_1);
HIDWORD(v40) = v41;
LODWORD(v40) = v41;
a_1 = (v40 >> 23) + d_1;
v42 = *(_DWORD *)&v78[8 * (((unsigned __int64)(v37 & 0xE) >> 1) & 7) | 4]
+ b_1
+ *(v36 - 1)
+ (a_1 & c_1 | d_1 & ~c_1);
HIDWORD(v40) = v42;
LODWORD(v40) = v42;
b_1 = (v40 >> 18) + a_1;
v43 = *v36;
v36 += 4;
v44 = *((_DWORD *)v38 - 1) + c_1 + v43 + (b_1 & d_1 | a_1 & ~d_1);
HIDWORD(v40) = v44;
LODWORD(v40) = v44;
c_1 = (v40 >> 12) + b_1;
v35 += 4LL;
v38 += 16;
v37 += 20LL;
} while (v35 < 16);
v45 = 0LL;
//v46 = &dword_10089FEAC;
v46 = (int*)((BYTE*)&unk_10089FE20 + 0x8C);
v47 = 5LL;
v48 = &v78[56];
do
{
v49 = (c_1 ^ b_1 ^ a_1) + d_1 + *(_DWORD *)&v78[4 * (v47 & 0xD)] + *(v46 - 3);
HIDWORD(v50) = v49;
LODWORD(v50) = v49;
d_1 = (v50 >> 28) + c_1;
v51 = *(_DWORD *)&v78[4 * (((_BYTE)v47 + 3) & 0xC)] + a_1 + *(v46 - 2) + (d_1 ^ c_1 ^ b_1);
HIDWORD(v50) = v51;
LODWORD(v50) = v51;
a_1 = (v50 >> 21) + d_1;
v52 = *(_DWORD *)&v78[4 * (((_BYTE)v47 + 6) & 0xF)] + b_1 + *(v46 - 1) + (a_1 ^ d_1 ^ c_1);
HIDWORD(v50) = v52;
LODWORD(v50) = v52;
b_1 = (v50 >> 16) + a_1;
v54 = *(_DWORD *)v48;
v48 -= 16;
v53 = v54;
v55 = *v46;
v46 += 4;
v56 = v53 + c_1 + v55 + (a_1 ^ d_1 ^ b_1);
HIDWORD(v50) = v56;
LODWORD(v50) = v56;
c_1 = (v50 >> 9) + b_1;
v45 += 4LL;
v47 += 12LL;
} while (v45 < 0x10);
v57 = 0LL;
//v58 = &dword_10089FEEC;
v58 = (int*)((BYTE*)&unk_10089FE20 + 0xcc);
v59 = &v78[56];
v60 = 21LL;
do
{
v61 = ((c_1 | ~a_1) ^ b_1) + d_1 + *(_DWORD *)&v78[4 * (((_BYTE)v60 - 21) & 0xC)] + *(v58 - 3);
HIDWORD(v62) = v61;
LODWORD(v62) = v61;
d_1 = (v62 >> 26) + c_1;
v63 = *(_DWORD *)&v78[4 * (((_BYTE)v60 - 14) & 0xF)] + a_1 + *(v58 - 2) + ((d_1 | ~b_1) ^ c_1);
HIDWORD(v62) = v63;
LODWORD(v62) = v63;
a_1 = (v62 >> 22) + d_1;
v64 = *(_DWORD *)v59;
v59 -= 16;
v65 = v64 + b_1 + *(v58 - 1) + ((a_1 | ~c_1) ^ d_1);
HIDWORD(v62) = v65;
LODWORD(v62) = v65;
b_1 = (v62 >> 17) + a_1;
v67 = *v58;
v58 += 4;
result = v67;
v68 = *(_DWORD *)&v78[4 * (v60 & 0xD)] + c_1 + v67 + ((b_1 | ~d_1) ^ a_1);
HIDWORD(v62) = v68;
LODWORD(v62) = v68;
c_1 = (v62 >> 11) + b_1;
v57 += 4LL;
v60 += 28LL;
} while (v57 < 16);
h0 += d_1;
h3 += c_1;
h2 += b_1;
h1 += a_1;
v6 += new_len;
} while (new_len > 55);
i = 0LL;
v79[0] = __PAIR__(h3, h0);
v79[1]= __PAIR__(h1, h2);
*(_WORD *)v73 = h0;
*(_BYTE *)(v73 + 2) = BYTE2(h0);
*(_BYTE *)(v73 + 3) = HIBYTE(h0);
do
{
v70 = *(_DWORD *)((char *)&v79 + i + 4);
v71 = v73 + i;
*(_WORD *)(v71 + 4) = v70;
*(_BYTE *)(v71 + 6) = BYTE2(v70);
*(_BYTE *)(v71 + 7) = HIBYTE(v70);
v72 = i + 8;
i += 4LL;
} while (v72 < 16);
return result;
}
void sub_100200BB0(unsigned char *buf,char* outbuf)
{
const char *set = "0123456789abcdef";
char *tmp;
tmp = outbuf;
for (int i = 0; i < 0x10;i++)
{
*tmp++ = set[(*buf) >> 4];
*tmp++ = set[(*buf) & 0xF];
buf++;
}
*tmp = '\0';
}
static char * sub_100200C40(string data)
{
//string
string strbuf= data;
strbuf += byte_100E379FF;
string strDatahead = strbuf.substr(0, 10);
string strDataend = strbuf.substr(strbuf.length() - 10, strbuf.length());
strbuf.replace(0, 10, strDataend);
strbuf.replace(strbuf.length() - 10, strbuf.length(), strDatahead);
char szsignbuf[64] = { 0 };
//md5
sub_1002003C4((__int64)strbuf.c_str(), strbuf.length(), (__int64)szsignbuf);
static char szstrbuf[64] = { 0 };
//hextostr
sub_100200BB0((unsigned char*)szsignbuf, szstrbuf);
return szstrbuf;
}
void fllowclick()
{
char* pszsign = NULL;
string strpostdata = "{\"h_model\":\"iPhone 6\",\"h_ch\":\"appstore\",\"uid\":86743096,\"h_app\":\"zuiyou_lite\",\"h_ts\":1559223191423,\"h_av\":\"4.5.5\",\"h_nt\":1,\"h_did\":\"d806be554aa144c474d05ac8a7f56dc1\",\"h_m\":167135249,\"h_pipi\":\"1.5.5\",\"did\":\"d806be554aa144c474d05ac8a7f56dc1\",\"h_os\":910000,\"token\":\"T3KbNjduyS99iBarGqVLRbRQth40at0OX5FiZugjHhEbynKsitGYLASgxkXp44kS1TzBh\",\"h_dt\":1}";
string strpost = "POST /attention/add?sign=";
pszsign = sub_100200C40(strpostdata);
strpost += pszsign;
strpost += " HTTP/1.1\r\n";
strpost += "Host: api.ippzone.com\r\n";
strpost += "App-Ver: 4.5.5\r\n";
strpost += "Accept: */*\r\n";
strpost += "ZYP: mid=167135249\r\n";
strpost += "Dev-Type: ios\r\n";
strpost += "Content-Type: application/json\r\n";
strpost += "Accept-Language: zh-Hans-CN;q=1\r\n";
strpost += "Accept-Encoding: gzip, deflate\r\n";
strpost += "ZYP: mid=167135249\r\n";
strpost += "Content-Length: 342\r\n";
strpost += "did: d806be554aa144c474d05ac8a7f56dc1\r\n";
strpost += "User-Agent: PPSocial/1.5.5 (iPhone; iOS 9.1; Scale/2.00)\r\n";
strpost += "Connection: keep-alive\r\n";
strpost += "Cookie: aliyungf_tc=AQAAAAxoX0ooZQQAVgISG16+OqOGG0RR\r\n";
strpost += "\r\n";
strpost += strpostdata;
postdata(strpost);
}
void postdata(string data)
{
printf(data.c_str());
printf("\r\n");
system("pause");
}
int main()
{
fllowclick();
return 0;
}
总结
整个分析流程大概就这些了,通过界面分析到功能函数静态和动态分析,这也是在逆向中常用的思路。
Davinde-iPhone:/var root# cycript -p PPSocial
cy# #0x143883f80
#"<UIButton: 0x143883f80; frame = (270 29; 50 26); alpha = 0; opaque = NO; layer = <CALayer: 0x1438a4cc0>>"
通过以上信息可以判断,0x143883f80的确是UIButton按钮的地址,如果不确定,可以通过调用hidden方法,并且观察app对应的按钮是否有变化.
cy# #0x143883f80.hidden=YES //隐藏按钮
true
cy# #0x143883f80.hidden=NO //显示按钮
false
光知道UiButton内存地址好像没有用,如何得到UiBtton的点击事件的所调用函数呢? 通过cycript 脚可以来遍历
cy# #0x143883f80.hidden=YES //隐藏按钮
true
cy# #0x143883f80.hidden=NO //显示按钮
false
光知道UiButton内存地址好像没有用,如何得到UiBtton的点击事件的所调用函数呢? 通过cycript 脚可以来遍历
@import mycript //导入mycript 脚本
cy# MyBtnTouchUpEvent(#0x143883f80)
[@["followClick"]]
下面是MyBtnTouchUpEvent代码,获取该Button对象所有的Target对象,并将事件相关所有action遍历输出,顺提一下,action可能有多个
@import mycript //导入mycript 脚本
cy# MyBtnTouchUpEvent(#0x143883f80)
[@["followClick"]]
下面是MyBtnTouchUpEvent代码,获取该Button对象所有的Target对象,并将事件相关所有action遍历输出,顺提一下,action可能有多个
// 获取按钮绑定的所有TouchUpInside事件的方法名
MyBtnTouchUpEvent = function(btn) {
var events = [];
var allTargets = btn.allTargets().allObjects()
var count = allTargets.count;
for (var i = count - 1; i >= 0; i--) {
if (btn != allTargets[i]) {
var e = [btn actionsForTarget:allTargets[i] forControlEvent:UIControlEventTouchUpInside];
events.push(e);
}
}
return events;
};
通过MyBtnTouchUpEvent得到followClick方法,需要测试下这个方法是否正确,通过调用LJJUSerProfileVC控制器的 followClick方法来测试一下
// 获取按钮绑定的所有TouchUpInside事件的方法名
MyBtnTouchUpEvent = function(btn) {
var events = [];
var allTargets = btn.allTargets().allObjects()
var count = allTargets.count;
for (var i = count - 1; i >= 0; i--) {
if (btn != allTargets[i]) {
var e = [btn actionsForTarget:allTargets[i] forControlEvent:UIControlEventTouchUpInside];
events.push(e);
}
}
return events;
};
通过MyBtnTouchUpEvent得到followClick方法,需要测试下这个方法是否正确,通过调用LJJUSerProfileVC控制器的 followClick方法来测试一下
cy# [#0x140abb400 followClick] //#0x140abb400为LJJUSerProfileVC对象
通过反复[#0x140abb400 followClick] 方法调用,可以实现手动点击关注效果,现在可以确定LJJUSerProfileVC followClick就是关注按钮的action方法.
cy# [#0x140abb400 followClick] //#0x140abb400为LJJUSerProfileVC对象
通过反复[#0x140abb400 followClick] 方法调用,可以实现手动点击关注效果,现在可以确定LJJUSerProfileVC followClick就是关注按钮的action方法.
四.静态分析
LJJUSerProfileVC followClick
LJJUSerProfileVC attention:
LJJAttentionManager userAttentionOther:isAttened:from:success:failure:
ZYAPIClient postWithPath:parameters:userInfo:progress:success:retry:failure:
ZYAPIClient postWithURL:parameters:userInfo:progress:success:retry:failure:
ZYURLManager requestHeaderDictionaryWithParameters:
ZYCrypto signWithParameters:
分析过程中发现比较重要的函数是ZYURLManager requestHeaderDictionaryWithParameters:和ZYCrypto signWithParameters:
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!