0x00 背景
最近看到一个小游戏,但是它使用了supersonicads的sdk,广告极其多,非常烦,所以我就逆向了一下它,顺便分析了一下supersonicads的工作原理。
0x01 supersonic广告sdk分析
首先贴一下该sdk的用法:Rewarded Video Integration for Android。简单地总结一下,就是在需要显示广告的时候调用mMediationAgent.showRewardedVideo(placementName);
,然后在回调函数onRewardedVideoAdRewarded
实现自己想要给予用户的奖励。在我这次逆向的游戏中,如果你没有star无法进行游戏了,该游戏就会调用showRewardedVideo
给你播放一部视频广告,然后播完广告后给你回复一颗star,你才能继续游戏。
很自然地,我想到先分析showRewardedVideo这个函数,于是我下载了supersonic的sdk,然后在
这个网站http://www.javadecompilers.com/result上反编译了一下。我分析了很久源码,发现showRewardedVideo
这个函数最终会调用到com.supersonicads.sdk.controller.SupersonicWebView
的showRewardedVideo
,该函数定义如下:
其中injectJavascript
会生成一段javascript,并用webview来load它。这段javascript会调用游戏加载时已经下载好的一段javascript中的showRewardedVideo
函数。这段js函数由mobileController.html
这个html加载,名字为http://supersonicads-a.akamaihd.net/mobileSDKController/versions/2.0.10/sdk_controller.min.gz.js
。该js使用了一个叫做PubSubJS的库,使用该库注册一个showRewardedVideo
接口,然后由刚刚的webview来调用这个接口。当webview播放完动画后,会调用t.callSDKFunction("adCredited",e,"adCreditedSuccess","adCreditedFailed")
这段js来触发java的native方法adCredited
。该函数是被导出成javascript的接口的:
接下来该函数调用mOnRewardedVideoListener.onRVAdCredited
,这里com.supersonic.adapters.supersonicads.SupersonicAdsAdapter
实现了OnRewardedVideoListener
这个接口,并实现了onRVAdCredited
方法,如下:
可以看到,这个方法又会调用this.mRewardedVideoManager.onRewardedVideoAdRewarded(placement, this);
,触发onRewardedVideoAdRewarded
事件。
以上就是supersonic广告sdk播放一个rewardedVideo的周期。
然而,理想是丰满的,现实是骨感的,当我发现了它的原理后,我尝试使用burp进行截取sdk_controller.min.gz.js
并进行修改,却发现只有前两次好像修改有了效果,之后无论怎么修改该js都没有效果,甚至把该js文件删了广告都照样播放...这个让我的分析一度停止了一个多星期。
好在今天,我有终于了新的进展。我发现supersonic它不光只使用了它自己的sdk,还整合了很多别家的广告sdk、接口,比如“applovin”,“chartboost”,“flurry”,“vungle”,“adcolony”等等。而这些广告sdk只要实现一个com.supersonic.mediationsdk.AbstractAdapter,就能在showRewardedVideo
触发时被调用到。
由于时间问题,我无法分析所有这些sdk,只分析了“applovin”的sdk。
0x02 applovin广告sdk分析
在com.supersonic.adapters.applovin
的AppLovinAdapter
类中,有showRewardedVideo
函数如下:
可以看到该函数会调用mAppLovinRV.show()
,跟踪此show
函数来到com.applovin.adview
的AppLovinIncentivizedInterstitial
类中,发现下面的代码:
至此一经开始受到proguard的混淆的影响了,不过我们仍然可以用jd-gui跟踪下去,来到com.applovin.impl.sdk <code>的</code>z<code>类的</code>a
函数:
注意看该函数调用了一个b
函数:
该b
函数又调用了localAppLovinInterstitialAdDialog.showAndRender(localAppLovinAdImpl)
,此showAndRender
函数为虚函数,我们要找到它实现的地方,发现它的实现在com.applovin.impl.adview
的af
类中:
注意到它启动了一个AppLovinInterstitialActivity
Activity,这个Activity在com.applovin.adview
包中,发现它里面有一些MediaPlayer
的启动与关闭代码,bingo,这个Activity就是用来播放广告的了。
注意到它的c
函数:
里面调用了setOnCompletionListener(new k(this))
,我们追踪这个k
类,它在com.applovin.adview
包中:
我们发现它又调用回了AppLovinInterstitialActivity
的a
函数,该a
函数如下:
注意到它调用了videoPlaybackEnded
函数,这个函数也是个虚函数,我们来找一下它被实现的地方,在com.supersonic.adapters.applovin
的AppLovinAdapter
类中:
bingo,正是它触发了onRewardedVideoAdRewarded
函数!
到此为止,“AppLovin”的一个广告周期我们也分析完了。下面说一下我是如何发现supersonic会调用不同adapter的。
0x03 java无源码调试
比较简单的方法为
- adb forward tcp:9999 jdwp:[pid]
- jdb -attach localhost:9999
即可。然后可以使用stop in
来设置断点在函数上。
另一种方法是将dex反编译成.java文件后,启动android studio,新建一个空项目,然后把生成的.java文件全部拷贝到项目src里。之后启动你的app,然后点击“attach debugger”即可。
这个好处是你可以直接在反编译的代码上加上断点。不过虽然断点是准确的,但是当你真正debug的时候其实代码是跑偏的,毕竟只是反编译出来的,行数什么的都是不对的。
虽然这两种方法都不是很好用,但是可以通过下断点显示函数的调用栈来获得不少信息,对于这次的分析来说是勉强够用了。我就是通过函数的调用栈发现了每次会调用不同的广告sdk接口。如果有什么可以debug smali或者bytecode的方法还请联系我! 我发现用jeb可以很方便地debug smali,有可能需要设置setprop ro.debuggable 1
,不然jeb无法attach进程。
0x04 去广告
这里就进入正题了,但是不好意思,大家看了那么多分析,其实对于去广告并没有什么用处,哈哈。我去广告的方法还是通过分析游戏的so文件来进行的。游戏的so文件里有一个proceedSelectedGameMode
函数,该函数中判断了star的数量够不够,如果不够,则调用startVideo
函数显示广告。
我们只要将此处star的数量的判断改成永真即可不显示广告。
0x05 更多的一点分析
其实我还分析了一下so文件是如何和java的函数进行互相调用的。
比如刚刚的startVideo
函数,进一步跟踪,发现它调用了showRewardVideoJNI
函数,该函数如下:
我们看到这个函数使用了cocos2d的JniHelper来调用java函数,且它调用的是SuperSonicXBridge
的showRewardVideo
函数,该SuperSonicXBridge
类调用了SuperSonicXBridge$1
这个内部类,由该内部类调用了真正的supsersonic的showRewardVideo
接口:
接下来分析广告播放完后的情况。在SupersonicXBridge
中有如下函数:
这个函数毫无疑问会在广告结束后调用,该函数是一个JNI函数,我们来看它在so文件中的定义:
我们看到这个函数会调用一个v5
,v5
是v4
这个对象的vptr+16
处的函数。v4
这个对象应该是SupersonicXDelegate
的子类对象。我们先看一下SupersonicXDelegate
这个类的虚表:
我们可以看到,0x526d78+0xa=0x526d88
处正好是函数supersonicRVAdRewarded
。接下来我们看一下GameModeScene
这个类的虚表:
可以看到同样有函数supersonicRVAdRewarded
。看一下这个函数的定义:
这个函数又调用了supersonicClose
,supersonicClose
的定义如下:
它调用了setUserRewarded
这个函数,给用户进行奖励。
至此,整个广告播放过程也就结束了。
0x06 总结
本文分析了一下supersonic这个广告sdk的实现,大家有空可以分析一下今天没有提到的几个adapter,它们有的可能是用native java实现的,有的可能又用了webview实现,很有意思。