友盟sdk逆向分析
导读
背景
-
分析友盟sdk,其实目的就是看一下友盟统计的数据真实性如何.
-
这东西其实说白了就是hook一些方法,可能是sdk方法,或者系统一些参数之类.难点是在分析过程,不在编写生效代码.
-
以下是分析思路,所说的观点可能不恰当,请见谅…
- 脚本运行后效果
需要技能
-
1.ios开发基础.
-
2.部分第三方工具Charles.
目标,获取通讯参数
捉包 Charles(app)
- https加密,此路不通
分析缓存 Flex(sdk)
- 缓存文件加密,此路不通
捉网络请求 NSURLConnection.Class
-
从捉包中可知,友盟域名为umeng.com
-
runtimehook系统方法并分析,友盟底层是使用系统NSURLConnection中的同步请求,通讯的域名https://ulogs.umeng.com/unify_logs
-
到这里看到有系统版本和设备版本,另外,猜测63BCACE101FD49EF6944406A663B0C3A这串数据,是设备唯一id,随后重启几次发现确实不会变,手工收改后发现,对新增用户无效.
//hook
[[self class] exchangeClass1:NSURLConnection.class Method1:@selector(sendAsynchronousRequest:queue:completionHandler:)
class2:TTVUmcCheatManage.class method2:@selector(ttv_sendAsynchronousRequest:queue:completionHandler:)];
[[self class] exchangeClass1:NSURLConnection.class Method1:@selector(sendSynchronousRequest:returningResponse:error:)
class2:TTVUmcCheatManage.class method2:@selector(ttv_sendSynchronousRequest:returningResponse:error:)];
目标,获取参数
捉取请求header,body
- 到这里看到有系统版本和设备版本,另外,猜测63BCACE101FD49EF6944406A663B0C3A这串数据,是设备唯一id,随后重启几次发现确实不会变,手工收改后发现,对新增用户无效.
//log
[TTVUmcCheatManage ttv_sendSynchronousRequest:returningResponse:error:] [Line 119] {
X-Umeng-Sdk : s/6.9.5 1.0.0/iOS/%E7%B1%B3%E6%96%97/iPhone11,4/12.0 63BCACE101FD49EF6944406A663B0C3A,
X-Umeng-UTC : 1551257504555,
Content-Type : ut/s,
Msg-Type : envelope/json
}
[TTVUmcCheatManage ttv_sendSynchronousRequest:returningResponse:error:] [Line 119] {
X-Umeng-Sdk : s/6.9.5 1.0.0/iOS/%E7%B1%B3%E6%96%97/iPhone11,4/12.0 63BCACE101FD49EF6944406A663B0C3A,
X-Umeng-UTC : 1551257505855,
Content-Type : ut/s,
Msg-Type : envelope/json
}
UMUMIDManager.h
- 然后从class-dump中查找疑似友盟umid管理类,UMUMIDManager.h,下hook,
UMUMIDManager.h
#import <objc/NSObject.h>
@class NSString;
@interface UMUMIDManager : NSObject
{
NSString *_privateUMIDToken;
id _secSDK;
id _secListener;
}
+ (id)umidToken;
+ (void)requestUMID;
+ (id)shareInstance;
@property(retain, nonatomic) id secListener; // @synthesize secListener=_secListener;
@property(retain, nonatomic) id secSDK; // @synthesize secSDK=_secSDK;
@property(copy) NSString *privateUMIDToken; // @synthesize privateUMIDToken=_privateUMIDToken;
- (void).cxx_destruct;
- (Class)createChildClassWithSecListenerClassName:(id)arg1;
- (id)createSecSDKInstanceWithClassName:(id)arg1;
- (void)requestUMID;
- (id)init;
@end
UMUtils.h
- 然后从class-dump中继续查找疑似友盟umid管理类,UMUtils.h,下hook,
#import <objc/NSObject.h>
@interface UMUtils : NSObject
{
}
+ (void)invoke:(id)arg1 selString:(id)arg2 enabled:(_Bool)arg3 md5:(id)arg4 returnValue:(void *)arg5;
+ (void)invoke:(id)arg1 selString:(id)arg2 enabled:(_Bool)arg3 returnValue:(void *)arg4;
+ (void)invoke:(id)arg1 selString:(id)arg2 object1:(id)arg3 selector:(SEL)arg4 returnValue:(void *)arg5;
+ (void)invoke:(id)arg1 selString:(id)arg2 object1:(id)arg3 object2:(id)arg4 returnValue:(void *)arg5;
+ (id)componentFlags;
+ (void)appendComponentFlag:(id)arg1;
+ (void)invoke:(id)arg1 selString:(id)arg2 argument:(id)arg3 returnValue:(void *)arg4;
+ (id)reflectObject:(id)arg1 selector:(id)arg2 object:(id)arg3 object:(id)arg4;
+ (id)reflectObject:(id)arg1 selector:(id)arg2 object:(id)arg3;
+ (id)reflectObject:(id)arg1 selector:(id)arg2;
+ (_Bool)loadSysFramework:(id)arg1;
+ (void)reflectClass:(id)arg1 selector:(id)arg2 params:(void **)arg3 paramsCount:(int)arg4;
+ (void)performClass:(id)arg1 method:(id)arg2 object:(id)arg3;
+ (id)deflatedDataPrefixedWith:(id)arg1 level:(int)arg2 source:(id)arg3;
+ (id)deflatedData:(id)arg1;
+ (_Bool)isEmptyString:(id)arg1;
+ (_Bool)notEmptyString:(id)arg1;
+ (id)getString:(id)arg1 bytesLength:(int)arg2;
+ (id)bytesToHexString:(char *)arg1 length:(int)arg2;
+ (id)AES128DecryptData:(id)arg1 withKey:(const void *)arg2;
+ (id)AES128EncryptData:(id)arg1 withKey:(const void *)arg2;
+ (id)decodeBase64:(id)arg1;
+ (id)encodeBase64:(id)arg1;
+ (id)JSONValue:(id)arg1;
+ (id)JSONFragment:(id)arg1;
+ (id)urlDecode:(id)arg1;
+ (id)urlEncode:(id)arg1;
+ (id)md5DataWithData:(id)arg1;
+ (char *)md5BytesWithString:(id)arg1;
+ (id)md5:(id)arg1;
+ (_Bool)isAppPirate;
+ (id)appPirateString;
+ (id)appShortVersionString;
+ (id)appBundleVersionString;
+ (id)realAppDisplayNameString;
+ (id)appDisplayNameString;
+ (id)appPackageNameString;
+ (id)mccString;
+ (id)mncString;
+ (id)carrierString;
+ (id)accessSubType;
+ (id)CTTelephonyNetworkInfoObject;
+ (int)accessStatus;
+ (id)accessString;
+ (id)batteryInfo;
+ (_Bool)isPad;
+ (id)deviceJailBreakString;
+ (_Bool)isDeviceJailBreak;
+ (id)resolutionString;
+ (id)deviceModelString;
+ (id)deviceMacAddressString;
+ (id)idfv;
+ (id)idfa;
+ (id)openUDIDString;
+ (id)utdid;
+ (id)deviceToken;
+ (id)dateString;
+ (id)timezoneString;
+ (int)timezone;
+ (id)languageString;
+ (id)countryString;
+ (id)osVersionString;
+ (id)osString;
+ (_Bool)isDebugging;
+ (unsigned long long)UTCIntervalMS;
+ (double)durationNowWith1970TimeInterval:(double)arg1;
+ (id)appkey;
@end
- JSONFragment 下hook
//log
[TTVUMUtilsCheat JSONFragment:] [Line 146] {
header : {
wrapper_version : ,
language : en,
country : US,
app_version : 1.0.0,
com_ver : 1.5.2,
sdk_type : IOS,
device_id : f4fa4e18673fc0f7419a349d53dcabc6bd10d968,
channel : App Store,
id_tracking : DQABCwwAAAAGAAAABGlkZnYLAAEAAAAkRkIwRURBQTUtQTYwRC0zRjFDLUJDRUItREFFQ0VFQzJCQ0ZDCgACAAABaS5bCz8IAAMAAAABAAAAAARpZGZhCwABAAAAJEJEQjlDRjZFLUVERUUtRDdFNS1FQUZGLUZBRUFGNEVEMjNDRgoAAgAAAWkuWws/CAADAAAAAQAAAAAIaG9zdG5hbWULAAEAAAAgNzRDNDRGNzlEMkVBNTEzOTA1MDhCOTBFM0JFNjRCODkKAAIAAAFpLlsLPwgAAwAAAAEAAAAAA29pZAsAAQAAAChmNGZhNGUxODY3M2ZjMGY3NDE5YTM0OWQ1M2RjYWJjNmJkMTBkOTY4CgACAAABaS5bCz8IAAMAAAABAAAAAAJtYwsAAQAAABEwMjowMDowMDowMDowMDowMAoAAgAAAWkuWws+CAADAAAAAQAAAAAFdXRkaWQLAAEAAAAYVytFVjNpZ3BCSzhEQUZOVEMrWkpCNFlKCgACAAABaS5bCz8IAAMAAAABAA8AAgwAAAAGCwABAAAAAm1jCwADAAAAETAyOjAwOjAwOjAwOjAwOjAwCgAEAAABaS5bCz4ACwABAAAABGlkZmELAAMAAAAkQkRCOUNGNkUtRURFRS1EN0U1LUVBRkYtRkFFQUY0RUQyM0NGCgAEAAABaS5bCz8ACwABAAAABGlkZnYLAAMAAAAkRkIwRURBQTUtQTYwRC0zRjFDLUJDRUItREFFQ0VFQzJCQ0ZDCgAEAAABaS5bCz8ACwABAAAAA29pZAsAAwAAAChmNGZhNGUxODY3M2ZjMGY3NDE5YTM0OWQ1M2RjYWJjNmJkMTBkOTY4CgAEAAABaS5bCz8ACwABAAAABXV0ZGlkCwADAAAAGFcrRVYzaWdwQks4REFGTlRDK1pKQjRZSgoABAAAAWkuWws/AAsAAQAAAAhob3N0bmFtZQsAAwAAACA3NEM0NEY3OUQyRUE1MTM5MDUwOEI5MEUzQkU2NEI4OQoABAAAAWkuWws/AAsAAwAAAAhjaGVja3N1bQA=,
resolution : 2436*1125,
os : iOS,
package_name : com.xxxx.xxxx,
app_b_v : 201902150932,
access : wifi,
access_subtype : unknown,
carrier : ,
is_jailbroken : NO,
os_version : 12.1,
device_model : iPhone11,2,
timezone : 8,
mc : 02:00:00:00:00:00,
display_name : 米x,
wrapper_type : native,
module : asoi,
com_type : 0,
i_sdk_v : 1.2.0,
appkey : 5bfbb63ab465f5ced50000be,
is_pirated : NO
},
inner : {
winfo : {
wl : [
{
sta : 0,
bssid : ,
ssid : ,
rssi : 0,
ts : 1551260912466,
i_winfo : [
{
i_nm : 255.255.0.0,
i_if : en0,
i_ip : 169.254.59.173,
i_bc : 169.254.255.255
}
]
}
]
},
by : [
{
st : -1,
ts : 1551260912465,
le : -100
}
],
sd : {
t : 121018208256,
f : 3989200896,
ts : 1551260912465
},
sinfo : {
i_homd : /Users/zhouqirui/Library/Developer/CoreSimulator/Devices/6BD669DF-21B1-49E0-B85A-74ACE0C39C56/data/Containers/Data/Application/873703F0-EADA-4793-AA32-D149F2A25DFA,
i_boud : /Users/zhouqirui/Library/Developer/CoreSimulator/Devices/6BD669DF-21B1-49E0-B85A-74ACE0C39C56/data/Containers/Bundle/Application/57286A3A-19A2-4435-A4B9-162B50E7FF39/米x.app,
i_simu : 1,
hn : 74C44F79D2EA51390508B90E3BE64B89,
tf : ,
st_usec : 396968,
ts : 1551260912465,
s_fs : 17,
i_debu : 1,
i_disp : 米xxx,
st : 1550888214
},
mem : {
t : 8589934592,
f : 2114293760,
ts : 1551260912466
}
}
}
- id_tracking base64解密 这里涉及编码问题手工处理了一下
idfv FB0EDAA5-A60D-3F1C-BCEB-DAECEEC2BCFC
idfa BDB9CF6E-EDEE-D7E5-EAFF-FAEAF4ED23CF
hostname 74C44F79D2EA51390508B90E3BE64B89
oid f4fa4e18673fc0f7419a349d53dcabc6bd10d968
mc 02:00:00:00:00:00
utdid W+EV3igpBK8DAFNTC+ZJB4YJ
mc 02:00:00:00:00:00
idfa BDB9CF6E-EDEE-D7E5-EAFF-FAEAF4ED23CF
idfv FB0EDAA5-A60D-3F1C-BCEB-DAECEEC2BCFC
oid f4fa4e18673fc0f7419a349d53dcabc6bd10d968
utdid W+EV3igpBK8DAFNTC+ZJB4YJ
hostname 74C44F79D2EA51390508B90E3BE64B89
checksum
- 总结,初步猜测需要字段
//openUDIDString
device_id : f4fa4e18673fc0f7419a349d53dcabc6bd10d968,
idfv $FB0EDAA5-A60D-3F1C-BCEB-DAECEEC2BCFC
idfa $BDB9CF6E-EDEE-D7E5-EAFF-FAEAF4ED23CF
//分辨率
resolution : 2436*1125,
//手机型号
deviceModelString:
目标,编写有效hook代码
- 测试运行有效
+ (id)resolutionString{
id appkey = [TTVUMUtilsCheat resolutionString];
if ([TTVUmcCheatManage sharedTTVUmcCheatManage].resolutionString.length) {
appkey = [TTVUmcCheatManage sharedTTVUmcCheatManage].resolutionString;
}
return appkey;
}
+ (id)deviceModelString{
id appkey = [TTVUMUtilsCheat deviceModelString];
if ([TTVUmcCheatManage sharedTTVUmcCheatManage].deviceModelString.length) {
appkey = [TTVUmcCheatManage sharedTTVUmcCheatManage].deviceModelString;
}
return appkey;
}
+ (id)openUDIDString{
NSString* appkey = [TTVUMUtilsCheat openUDIDString];
if ([TTVUmcCheatManage sharedTTVUmcCheatManage].openUDIDString.length) {
appkey = [TTVUmcCheatManage sharedTTVUmcCheatManage].openUDIDString;
}
return appkey;
}
+ (id)idfv{
NSString* appkey = [TTVUMUtilsCheat idfv];
if ([TTVUmcCheatManage sharedTTVUmcCheatManage].idfv.length) {
appkey = [TTVUmcCheatManage sharedTTVUmcCheatManage].idfv;
}
return appkey;
}
+ (id)idfa{
NSString* appkey = [TTVUMUtilsCheat idfa];
if ([TTVUmcCheatManage sharedTTVUmcCheatManage].idfa.length) {
appkey = [TTVUmcCheatManage sharedTTVUmcCheatManage].idfa;
}
return appkey;
}
编写运行脚本
打好.app直接用自动化跑,测试同事使用
mac调用自动运行模拟器
- 脚本参数可调,主要友盟数据更新
#!/bin/bash
# xcrun instruments -s
#./cheat.sh "com.touchtv.midou" "CEE17154-FFE4-4173-BCC0-CB8C4D3F3469" "/Users/zhouqirui/Desktop/Ipa/Midou.app"
#./cheat.sh "com.touchtv.midou" "82DC60B6-14D7-462F-BC59-BDD2D89D896D" "/Users/zhouqirui/Desktop/Ipa/Midou.app"
deviceId="CEE17154-FFE4-4173-BCC0-CB8C4D3F3469"
appPath="/Users/zhouqirui/Desktop/Ipa/Midou.app"
appId="com.touchtv.midou"
appId=$1
deviceId=$2
appPath=$3
SECONDS=0
build_cheat(){
device_id=$1
app_path=$2
app_id=$3
echo "*** 开始运行 ***"
xcrun instruments -w "${device_id}"
sleep 5
xcrun simctl install "${device_id}" "${app_path}"
sleep 5
echo "no.0 ${app_id} ${device_id}"
xcrun simctl launch "${device_id}" "${app_id}"
sleep 5
xcrun simctl terminate "${device_id}" "${app_id}"
for i in {1..100}
do
echo "no.${i} ${app_id} ${device_id}"
xcrun simctl launch "${device_id}" "${app_id}"
sleep 10
xcrun simctl terminate "${device_id}" "${app_id}"
done
xcrun simctl shutdown "${device_id}"
}
build_cheat "${deviceId}" "${appPath}" "${appId}"
echo "*** 运行结束 *** Total time: ${SECONDS}s"