导读

背景

  • 分析友盟sdk,其实目的就是看一下友盟统计的数据真实性如何.

  • 这东西其实说白了就是hook一些方法,可能是sdk方法,或者系统一些参数之类.难点是在分析过程,不在编写生效代码.

  • 以下是分析思路,所说的观点可能不恰当,请见谅…

  • 最新思维导图

image

  • 脚本运行后效果

image

需要技能

  • 1.ios开发基础.

  • 2.部分第三方工具Charles.

目标,获取通讯参数

捉包 Charles(app)

  • https加密,此路不通

image

分析缓存 Flex(sdk)

  • 缓存文件加密,此路不通

image

image

捉网络请求 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"