前言
对每个程序员来说调试(Debug)是一个不可或缺的技能
偵錯(英语:Debug),又稱除錯,是发现和减少计算机程序或电子仪器设备中程序错误的一个过程(wiki-调试)
背景
做SDK开发,难免会有些app接入SDK后并没有达到所需要的效果,这时候就需要你具备一定的debug
的能力找到问题的所在,并提示/建议app开发人员进行修改.
比如: app开发人员并没有按照SDK接入文档进行Coding
,又或是app开发人员按照SDK接入文档进行Coding(但由于app的业务逻辑比较复杂某些细节忽略了)导致没有达到所需的效果.
使用我们sdk进行流量引导的app必须在app启动时优先调用
sdk
的+[WSPX start]
案例
- 某民直播APP,在接入我方SDK后发现:在进入首页的直播平台后,会有几条请求无法被SDK引导(以下简称
流量泄露
) - 某民直播APP开发人员,一再确认已按照SDK接入文档进行Coding
调试前已有的资料
- 流量泄露时对应的抓包数据
- 某民直播提供的IPA包
问题重现的操作步骤:
follow tcp flow of FvlrC6LHMC9LspI_jVj-qzDhktiq
:
根据抓包数据能得到的有用数据
- 从图中红色圈起来的部分我们可以知道该http请求发往的
Destination
不是36.250.87.88
- 绿色圈起来的部分是我们SDK有意让其不走我们的的节点
36.250.87.88
- 除了有意使其不走节点
36.250.87.88
,剩下的就是泄露的流量 - 从
UA
看FvlrC6LHMC9LspI_jVj-qzDhktiq
这条请求确实是某民app发出的 - 从
Content-Type
可以看出这是个zip包之类的资源(判定是一个需要下载的资源)
判定泄露类型:
SDK现有的技术壁垒
- 不支持wkwebview的流量引导
- 不支持cfsocket和cfstream写的
- 不支持对Apple的一些服务的流量引导
- 如果有session的创建比sdk的session创建的早,则会导致泄漏
- 如果有第三方也对我们hook的API也做了hook,有可能会导致泄漏
UA
判定不是wkwebview
发出的流量- 应该不是cfsocket和cfstream的流量,下载数据,是我的话应该会选择
downloadSession
来做的 - 不是Apple的一些后台服务发出的流量
- 可能
session
创建时没有挂上sdk的nsurlprotocol
- 第三方也对我们hook的API做了hook
借助OC语言的特性使用methodSwizzling对可疑session
相关的API进行hook
并打印session
的实例地址
#import "NSURLSession+Hooked.h"
#import "MethodSwizzling.h"
@implementation NSURLSession (Hooked)
+ (void)load {
if ([WSPXConfig enableHook]) {
exchangeInstanceMethod([NSURLSession class], @selector(downloadTaskWithRequest:), @selector(debugHookedDownloadTaskWithRequest:));
exchangeInstanceMethod([NSURLSession class], @selector(dataTaskWithRequest:), @selector(debugHookedDataTaskWithRequest:));
exchangeInstanceMethod([NSURLSession class], @selector(dataTaskWithURL:), @selector(debugHookedDataTaskWithURL:));
exchangeInstanceMethod([NSURLSession class], @selector(uploadTaskWithRequest:fromFile:), @selector(debugHookedUploadTaskWithRequest:fromFile:));
exchangeInstanceMethod([NSURLSession class], @selector(uploadTaskWithRequest:fromData:), @selector(debugHookedUploadTaskWithRequest:fromData:));
exchangeInstanceMethod([NSURLSession class], @selector(uploadTaskWithStreamedRequest:), @selector(debugHookedUploadTaskWithStreamedRequest:));
exchangeInstanceMethod([NSURLSession class], @selector(downloadTaskWithURL:), @selector(debugHookedDownloadTaskWithURL:));
exchangeInstanceMethod([NSURLSession class], @selector(downloadTaskWithResumeData:), @selector(debugHookedDownloadTaskWithResumeData:));
exchangeMethodClass([NSURLSession class],
@selector(sessionWithConfiguration:delegate:delegateQueue:),
@selector(debugHookedSessionWithConfiguration:delegate:delegateQueue:));
}
}
+ (NSURLSession *)debugHookedSessionWithConfiguration:(NSURLSessionConfiguration *)configuration delegate:(id<NSURLSessionDelegate>)delegate delegateQueue:(NSOperationQueue *)queue {
NSURLSession* finalSession = [self debugHookedSessionWithConfiguration:configuration delegate:delegate delegateQueue:queue];
NSLog(@"Hallo-->session debugHookedSessionWithConfiguration -> session(%@), delegate(%@)\n",finalSession, delegate);
return finalSession;
}
/* Creates a data task with the given request. The request may have a body stream. */
- (NSURLSessionDataTask *)debugHookedDataTaskWithRequest:(NSURLRequest *)request; {
NSLog(@"Hallo-->session(%@), -->request(%@) \n%s", self, request, __PRETTY_FUNCTION__);
return [self debugHookedDataTaskWithRequest:request];
}
/* Creates a data task to retrieve the contents of the given URL. */
- (NSURLSessionDataTask *)debugHookedDataTaskWithURL:(NSURL *)url; {
NSLog(@"Hallo-->session(%@), -->url(%@) \n%s", self, url, __PRETTY_FUNCTION__);
return [self debugHookedDataTaskWithURL:url];
}
/* Creates an upload task with the given request. The body of the request will be created from the file referenced by fileURL */
- (NSURLSessionUploadTask *)debugHookedUploadTaskWithRequest:(NSURLRequest *)request fromFile:(NSURL *)fileURL; {
NSLog(@"Hallo-->session(%@), -->request(%@) \n%s", self, request, __PRETTY_FUNCTION__);
return [self debugHookedUploadTaskWithRequest:request fromFile:fileURL];
}
/* Creates an upload task with the given request. The body of the request is provided from the bodyData. */
- (NSURLSessionUploadTask *)debugHookedUploadTaskWithRequest:(NSURLRequest *)request fromData:(NSData *)bodyData; {
NSLog(@"Hallo-->session(%@), -->request(%@) \n%s", self, request, __PRETTY_FUNCTION__);
return [self debugHookedUploadTaskWithRequest:request fromData:bodyData];
}
/* Creates an upload task with the given request. The previously set body stream of the request (if any) is ignored and the URLSession:task:needNewBodyStream: delegate will be called when the body payload is required. */
- (NSURLSessionUploadTask *)debugHookedUploadTaskWithStreamedRequest:(NSURLRequest *)request; {
NSLog(@"Hallo-->session(%@), -->request(%@) \n%s", self, request, __PRETTY_FUNCTION__);
return [self debugHookedUploadTaskWithStreamedRequest:request];
}
/* Creates a download task with the given request. */
- (NSURLSessionDownloadTask *)debugHookedDownloadTaskWithRequest:(NSURLRequest *)request; {
NSLog(@"Hallo-->session(%@), -->request(%@) \n%s", self, request, __PRETTY_FUNCTION__);
return [self debugHookedDownloadTaskWithRequest:request];
}
/* Creates a download task to download the contents of the given URL. */
- (NSURLSessionDownloadTask *)debugHookedDownloadTaskWithURL:(NSURL *)url; {
NSLog(@"Hallo-->session(%@), -->url(%@) \n%s", self, url, __PRETTY_FUNCTION__);
return [self debugHookedDownloadTaskWithURL:url];
}
/* Creates a download task with the resume data. If the download cannot be successfully resumed, URLSession:task:didCompleteWithError: will be called. */
- (NSURLSessionDownloadTask *)debugHookedDownloadTaskWithResumeData:(NSData *)resumeData;{
NSLog(@"Hallo-->session(%@), \n%s", self, __PRETTY_FUNCTION__);
return [self debugHookedDownloadTaskWithResumeData:resumeData];
}
@end
借助MonkeyDev动态库注入工具将上面的代码注入某民的可执行文件
- 具体如何使用该工具我就不再赘述,不懂的话可以去看Wiki
复现问题
- 使用
Wireshark
对手机进行抓包 - 点开首页随便逛逛,点开一个直播间
- 如愿
Wireshark
中出现了那个泄露的关键字符串FvlrC6LHMC9LspI_jVj-qzDhktiq
- 也如愿收获了一大堆log
在log寻找线索
- 在log中搜索关键字
FvlrC6LHMC9LspI_jVj-qzDhktiq
找到如下这条2018-08-02 15:27:46.070729+0800 QuanMinTV-Mini[1529:185056] Hallo-->session(<__NSURLSessionLocal: 0x1741d8150>), -->request(<NSURLRequest: 0x174214f90> { URL: http://attachment.shouyintv.cn/FvlrC6LHMC9LspI_jVj-qzDhktiq })
- 根据上面得到的那条log,拿到
0x1741d8150
,并对log文件进行过滤得到如下结果:2018-08-02 15:09:49.403756+0800 QuanMinTV-Mini[1529:182446] Hallo-->session debugHookedSessionWithConfiguration -> session(<__NSURLSessionLocal: 0x1741d8150>), delegate(<AFHTTPSessionManager: 0x1701d9140, baseURL: (null), session: (null), operationQueue: <NSOperationQueue: 0x17422cb80>{name = 'NSOperationQueue 0x17422cb80'}>) 2018-08-02 15:09:49.418955+0800 QuanMinTV-Mini[1529:182446] Hallo-->session(<__NSURLSessionLocal: 0x1741d8150>), -->request(<NSMutableURLRequest: 0x17001aa40> { URL: https://api-shouyin.quanmin.tv/public/version/check/2/4346?cid=6&conn=3G&cv=iphone2_4.3.40&debug=0&dev=1F57FD00-74E2-4007-942B-8F9744151B9B&device=&nonce=QBMHVSNPSZJQUOOFNEVPZXFPAHNNXIWE&os=30&osversion=ios_10.3.1&sign=B2DEA231ED08CD9BDDABD0C75B7714FB&toid=-1&ua=iPhone7%2C2 }) 2018-08-02 15:09:49.424917+0800 QuanMinTV-Mini[1529:182446] Hallo-->session(<__NSURLSessionLocal: 0x1741d8150>), -->request(<NSMutableURLRequest: 0x1740194c0> { URL: https://api-shouyin.quanmin.tv/public/app/msg?cid=6&conn=3G&cv=iphone2_4.3.40&debug=0&dev=1F57FD00-74E2-4007-942B-8F9744151B9B&device=&nonce=NHWKXUEVMDFVAJINOJRHPOWUNYSWZSBP&os=30&osversion=ios_10.3.1&sign=4787B043ECCD08701DAAA511A1A57E11&toid=-1&ua=iPhone7%2C2 }) 2018-08-02 15:09:49.426879+0800 QuanMinTV-Mini[1529:182446] Hallo-->session(<__NSURLSessionLocal: 0x1741d8150>), -->request(<NSMutableURLRequest: 0x17001ab50> { URL: https://api-shouyin.quanmin.tv/public/config/gift/version?cid=6&conn=3G&cv=iphone2_4.3.40&debug=0&dev=1F57FD00-74E2-4007-942B-8F9744151B9B&device=&nonce=EPTGXBVCVGWNNMOAEJPDFCMKGIMRTKTY&os=30&osversion=ios_10.3.1&sign=96C4834DAB7554ECCC1A4CB1100D7A8B&toid=-1&ua=iPhone7%2C2 }) 2018-08-02 15:09:49.428882+0800 QuanMinTV-Mini[1529:182446] Hallo-->session(<__NSURLSessionLocal: 0x1741d8150>), -->request(<NSMutableURLRequest: 0x17001ac10> { URL: https://api-shouyin.quanmin.tv/bag/propConfigVersion?cid=6&conn=3G&cv=iphone2_4.3.40&debug=0&dev=1F57FD00-74E2-4007-942B-8F9744151B9B&device=&nonce=DIYIROKGPMHUYPIEMJUMWDPDWJPBQYIP&os=30&osversion=ios_10.3.1&sign=417C6DC17E0B63EFF3D4A22FBEE0C60F&toid=-1&ua=iPhone7%2C2 }) 2018-08-02 15:09:49.431908+0800 QuanMinTV-Mini[1529:182446] Hallo-->session(<__NSURLSessionLocal: 0x1741d8150>), -->request(<NSMutableURLRequest: 0x174019700> { URL: https://api-shouyin.quanmin.tv/public/app/act?cid=6&conn=3G&cv=iphone2_4.3.40&debug=0&dev=1F57FD00-74E2-4007-942B-8F9744151B9B&device=&nonce=QCATFARGNZSQTESFECNREAJFSYGCYBVN&os=30&osversion=ios_10.3.1&sign=8C631191D38D45F6BFD5BAE1504DB99D&toid=-1&ua=iPhone7%2C2 }) 2018-08-02 15:09:49.932752+0800 QuanMinTV-Mini[1529:182446] Hallo-->session(<__NSURLSessionLocal: 0x1741d8150>), -->request(<NSMutableURLRequest: 0x17401a440> { URL: https://api-shouyin.quanmin.tv/auth/check?cid=6&conn=3G&cv=iphone2_4.3.40&debug=0&dev=1F57FD00-74E2-4007-942B-8F9744151B9B&device=&nonce=KQVXMQLGZUVPBYNKMYRRQKFSYAKAWJBQ&os=30&osversion=ios_10.3.1&sign=98A1A861B795A6FE5E5FA9D8F81CCD08&toid=-1&ua=iPhone7%2C2 }) 2018-08-02 15:09:52.778470+0800 QuanMinTV-Mini[1529:182446] Hallo-->session(<__NSURLSessionLocal: 0x1741d8150>), -->request(<NSMutableURLRequest: 0x174203ca0> { URL: https://api-shouyin.quanmin.tv/bag/propConfig?cid=6&client=app&conn=3G&cv=iphone2_4.3.40&debug=0&dev=1F57FD00-74E2-4007-942B-8F9744151B9B&device=&nonce=KLPMQQQLQPRGWLBERPQBDEHOHTCVZRNV&os=30&osversion=ios_10.3.1&sign=C8B31DD95B00DCB64ADA533C6D275CF8&toid=-1&ua=iPhone7%2C2 }) 2018-08-02 15:09:55.811342+0800 QuanMinTV-Mini[1529:182446] Hallo-->session(<__NSURLSessionLocal: 0x1741d8150>), -->request(<NSMutableURLRequest: 0x170216310> { URL: https://api-shouyin.quanmin.tv/auth/check?cid=6&conn=3G&cv=iphone2_4.3.40&debug=0&dev=1F57FD00-74E2-4007-942B-8F9744151B9B&device=&nonce=KQXUGSPOATHXRNJGGVWSIRVVXGLEFHXM&os=30&osversion=ios_10.3.1&sign=0D0762A1D2C39E2904035AE8C248F826&toid=-1&ua=iPhone7%2C2 }) 2018-08-02 15:09:58.884550+0800 QuanMinTV-Mini[1529:182446] Hallo-->session(<__NSURLSessionLocal: 0x1741d8150>), -->request(<NSMutableURLRequest: 0x1702132b0> { URL: https://api-shouyin.quanmin.tv/auth/check?cid=6&conn=3G&cv=iphone2_4.3.40&debug=0&dev=1F57FD00-74E2-4007-942B-8F9744151B9B&device=&nonce=BYRNKTDIGMVYPVYYGXSOXENFPQIUBGHN&os=30&osversion=ios_10.3.1&sign=D00DF763A33CC62288FA219D98DBBBA4&toid=-1&ua=iPhone7%2C2 }) 2018-08-02 15:10:02.214492+0800 QuanMinTV-Mini[1529:182446] Hallo-->session(<__NSURLSessionLocal: 0x1741d8150>), -->request(<NSMutableURLRequest: 0x17401bb90> { URL: https://api-shouyin.quanmin.tv/auth/check?cid=6&conn=3G&cv=iphone2_4.3.40&debug=0&dev=1F57FD00-74E2-4007-942B-8F9744151B9B&device=&nonce=FKQONYAGQJHWBPJEQVXHTXOXYEDMMNBV&os=30&osversion=ios_10.3.1&sign=B784F426048B7DC5EF1B86988725D822&toid=-1&ua=iPhone7%2C2 }) onLocal: 0x1741d8150>), -->request(<NSMutableURLRequest: 0x170218e00> { URL: https://api-shouyin.quanmin.tv/user/honor/single?cid=6&conn=3G&cv=iphone2_4.3.40&debug=0&dev=1F57FD00-74E2-4007-942B-8F9744151B9B&device=&nonce=USEAFOCLRVOYRAHHGOYDIHUKOFITNSHQ&os=30&osversion=ios_10.3.1&owid=1639748150&sign=A22A2E14B358545B355D8C454DAC828F&toid=-1&ua=iPhone7%2C2 }) 2018-08-02 15:27:45.693859+0800 QuanMinTV-Mini[1529:182446] Hallo-->session(<__NSURLSessionLocal: 0x1741d8150>), -->request(<NSMutableURLRequest: 0x17021aaa0> { URL: https://api-shouyin.quanmin.tv/public/config/gift/version?cid=6&conn=WIFI&cv=iphone2_4.3.40&debug=0&dev=1F57FD00-74E2-4007-942B-8F9744151B9B&device=&nonce=MSARXVQPBUSSTRXVLBLM2018-08-02 15:27:45.695986+0800 QuanMinTV-Mini[1529:182446] Hallo-->session(<__NSURLSessionLocal: 0x1741d8150>), -->request(<NSMutableURLRequest: 0x1742141d0> { URL: https://api-shouyin.quanmin.tv/bag/propConfigVersion?cid=6&conn=WIFI&cv=iphone2_4.3.40&debug=0&dev=1F57FD00-74E2-4007-942B-8F9744151B9B&device=&nonce=WGKLJVVVABHMMKTOHJJXVRBVNCIVDMDF&os=30&osversion=ios_10.3.1&sign=F20484F9E26A949403144286937FFD2F&toid=-1&ua=iPhone7%2C2 }) 2018-08-02 15:27:45.980003+0800 QuanMinTV-Mini[1529:182446] Hallo-->session(<__NSURLSessionLocal: 0x1741d8150>), -->request(<NSMutableURLRequest: 0x174213ec0> { URL: https://api-shouyin.quanmin.tv/bag/propConfig?cid=6&client=app&conn=WIFI&cv=iphone2_4.3.40&debug=0&dev=1F57FD00-74E2-4007-942B-8F9744151B9B&device=&nonce=BZMZIXOWSUALQDCKSCZUBAQYMLTRSNYX&os=30&osversion=ios_10.3.1&sign=E4D305CACD884D959D8A242C8C28DC06&toid=-1&ua=iPhone7%2C2 }) 2018-08-02 15:27:46.070729+0800 QuanMinTV-Mini[1529:185056] Hallo-->session(<__NSURLSessionLocal: 0x1741d8150>), -->request(<NSURLRequest: 0x174214f90> { URL: http://attachment.shouyintv.cn/FvlrC6LHMC9LspI_jVj-qzDhktiq })
从上面的log中第一条可以判定该条
session
是由AFHTTPSessionManager
创建的并不是我们sdk创建的,或者说该session
在我们sdk在hooksessionWithConfiguration:delegate:delegateQueue:
之前就已经创建了因为以前有看过
AFHTTPSessionManager
的session初始化方法所以我直接在Xcode中使用符号断点在-[AFHTTPSessionManager initWithBaseURL:sessionConfiguration:]
下了个断点- 并且也在我们sdk启动的方法下了个符号断点
+[WSPX start]
- 如果Xcode先停在了
-[AFHTTPSessionManager initWithBaseURL:sessionConfiguration:]
那么可以确定就是因为session
没有挂上sdk的nsurlprotocol
,导致该session
无法被我们sdk进行流量引导 如下是断点
-[AFHTTPSessionManager initWithBaseURL:sessionConfiguration:]
触发时使用bt查看堆栈(lldb) bt * thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 9.1 * frame #0: 0x00000001007ad6c4 QuanMinTV-Mini`-[DVLHttpManager initWithBaseUrl:] frame #1: 0x00000001001eaac0 QuanMinTV-Mini`-[ZXHttpTask init] + 132 frame #2: 0x00000001001eaa1c QuanMinTV-Mini`___lldb_unnamed_symbol2824$$QuanMinTV-Mini + 36 frame #3: 0x0000000105f8da10 libdispatch.dylib`_dispatch_client_callout + 16 frame #4: 0x0000000105f8e778 libdispatch.dylib`dispatch_once_f + 116 frame #5: 0x00000001001ea9f4 QuanMinTV-Mini`+[ZXHttpTask sharedManager] + 112 frame #6: 0x00000001001eb268 QuanMinTV-Mini`+[ZXHttpTask GET:parameters:taskResponse:] + 164 frame #7: 0x00000001007a48d4 QuanMinTV-Mini`-[ZXUserModel checkUpate:] + 344 frame #8: 0x00000001007a2604 QuanMinTV-Mini`-[ZXUserModel delayFetchThings] + 56 frame #9: 0x000000010079e9f8 QuanMinTV-Mini`-[ZXUserModel fetchData] + 56 frame #10: 0x00000001007a0080 QuanMinTV-Mini`-[ZXUserModel userInfo] + 148 frame #11: 0x0000000100623fb8 QuanMinTV-Mini`-[AppDelegate setupBugly] + 408 frame #12: 0x0000000100623798 QuanMinTV-Mini`-[AppDelegate application:didFinishLaunchingWithOptions:] + 184 frame #13: 0x00000001940c72dc UIKit`-[UIApplication _handleDelegateCallbacksWithOptions:isSuspended:restoreState:] + 380 frame #14: 0x00000001942d3800 UIKit`-[UIApplication _callInitializationDelegatesForMainScene:transitionContext:] + 3452 frame #15: 0x00000001942d92a8 UIKit`-[UIApplication _runWithMainScene:transitionContext:completion:] + 1684 frame #16: 0x00000001942edde0 UIKit`__84-[UIApplication _handleApplicationActivationWithScene:transitionContext:completion:]_block_invoke.3151 + 48 frame #17: 0x00000001942d653c UIKit`-[UIApplication workspaceDidEndTransaction:] + 168 frame #18: 0x000000018facf884 FrontBoardServices`__FBSSERIALQUEUE_IS_CALLING_OUT_TO_A_BLOCK__ + 36 frame #19: 0x000000018facf6f0 FrontBoardServices`-[FBSSerialQueue _performNext] + 176 frame #20: 0x000000018facfaa0 FrontBoardServices`-[FBSSerialQueue _performNextFromRunLoopSource] + 56 frame #21: 0x000000018ded5424 CoreFoundation`__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 24 frame #22: 0x000000018ded4d94 CoreFoundation`__CFRunLoopDoSources0 + 540 frame #23: 0x000000018ded29a0 CoreFoundation`__CFRunLoopRun + 744 frame #24: 0x000000018de02d94 CoreFoundation`CFRunLoopRunSpecific + 424 frame #25: 0x00000001940c045c UIKit`-[UIApplication _run] + 652 frame #26: 0x00000001940bb130 UIKit`UIApplicationMain + 208 frame #27: 0x0000000100a0f8d4 QuanMinTV-Mini`___lldb_unnamed_symbol16136$$QuanMinTV-Mini + 88 frame #28: 0x000000018ce1159c libdyld.dylib`start + 4
从上面的调用栈可以整理出如下的调用关系1:
[AppDelegate application:didFinishLaunchingWithOptions:] [AppDelegate setupBugly] [ZXUserModel userInfo] [ZXUserModel fetchData] [ZXUserModel delayFetchThings] [ZXUserModel checkUpate:] [ZXHttpTask GET:parameters:taskResponse:] [ZXHttpTask sharedManager] . . . [ZXHttpTask init] [DVLHttpManager initWithBaseUrl:] [AFHTTPSessionManager initWithBaseURL:sessionConfiguration:]
- 如下是符号断点
+[WSPX start]
触发时使用bt查看的堆栈(lldb) bt * thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 8.1 * frame #0: 0x0000000100d46a34 QuanMinTV-Mini`+[WSPX start] frame #1: 0x00000001008c027c QuanMinTV-Mini`+[QMFreeFlowManager start] + 40 frame #2: 0x0000000100623830 QuanMinTV-Mini`-[AppDelegate application:didFinishLaunchingWithOptions:] + 336 frame #3: 0x00000001940c72dc UIKit`-[UIApplication _handleDelegateCallbacksWithOptions:isSuspended:restoreState:] + 380 frame #4: 0x00000001942d3800 UIKit`-[UIApplication _callInitializationDelegatesForMainScene:transitionContext:] + 3452 frame #5: 0x00000001942d92a8 UIKit`-[UIApplication _runWithMainScene:transitionContext:completion:] + 1684 frame #6: 0x00000001942edde0 UIKit`__84-[UIApplication _handleApplicationActivationWithScene:transitionContext:completion:]_block_invoke.3151 + 48 frame #7: 0x00000001942d653c UIKit`-[UIApplication workspaceDidEndTransaction:] + 168 frame #8: 0x000000018facf884 FrontBoardServices`__FBSSERIALQUEUE_IS_CALLING_OUT_TO_A_BLOCK__ + 36 frame #9: 0x000000018facf6f0 FrontBoardServices`-[FBSSerialQueue _performNext] + 176 frame #10: 0x000000018facfaa0 FrontBoardServices`-[FBSSerialQueue _performNextFromRunLoopSource] + 56 frame #11: 0x000000018ded5424 CoreFoundation`__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 24 frame #12: 0x000000018ded4d94 CoreFoundation`__CFRunLoopDoSources0 + 540 frame #13: 0x000000018ded29a0 CoreFoundation`__CFRunLoopRun + 744 frame #14: 0x000000018de02d94 CoreFoundation`CFRunLoopRunSpecific + 424 frame #15: 0x00000001940c045c UIKit`-[UIApplication _run] + 652 frame #16: 0x00000001940bb130 UIKit`UIApplicationMain + 208 frame #17: 0x0000000100a0f8d4 QuanMinTV-Mini`___lldb_unnamed_symbol16136$$QuanMinTV-Mini + 88 frame #18: 0x000000018ce1159c libdyld.dylib`start + 4
- 从上面的调用栈可以整理出如下的调用关系2:
[WSPX start] [QMFreeFlowManager start] [AppDelegate application:didFinishLaunchingWithOptions:]
- 说明一下:断点是先触发了
-[AFHTTPSessionManager initWithBaseURL:sessionConfiguration:]
然后放行再触发了+[WSPX start]
排查结论:
- 根据断点触发的先后顺序,可以确定是因为session没有挂上sdk的
nsurlprotocol
,导致该session无法被我们sdk进行流量引导 - 然后将调用关系1和调用关系2结合得出:
[QMFreeFlowManager start]
比[AppDelegate setupBugly]
调用的晚导致导致泄露了。 - 将上述结论发给某民直播的开发者,让其将
[QMFreeFlowManager start]
放在[AppDelegate setupBugly]
之前调用
Note:
- 这里使用bt就是为了找到某民直播app因为业务逻辑比较复杂导致
[QMFreeFlowManager start]
并没有按照接入文档进行coding的证据,这样子和甲方沟通起来就比较硬气一些了。