supersonicads广告sdk逆向分析以及无源码debug java

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.SupersonicWebViewshowRewardedVideo,该函数定义如下:

其中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.applovinAppLovinAdapter类中,有showRewardedVideo函数如下:

可以看到该函数会调用mAppLovinRV.show(),跟踪此show函数来到com.applovin.adviewAppLovinIncentivizedInterstitial类中,发现下面的代码:

至此一经开始受到proguard的混淆的影响了,不过我们仍然可以用jd-gui跟踪下去,来到com.applovin.impl.sdk <code>的</code>z<code>类的</code>a函数:

注意看该函数调用了一个b函数:

b函数又调用了localAppLovinInterstitialAdDialog.showAndRender(localAppLovinAdImpl),此showAndRender函数为虚函数,我们要找到它实现的地方,发现它的实现在com.applovin.impl.adviewaf类中:

注意到它启动了一个AppLovinInterstitialActivity Activity,这个Activity在com.applovin.adview包中,发现它里面有一些MediaPlayer的启动与关闭代码,bingo,这个Activity就是用来播放广告的了。
注意到它的c函数:

里面调用了setOnCompletionListener(new k(this)),我们追踪这个k类,它在com.applovin.adview包中:

我们发现它又调用回了AppLovinInterstitialActivitya函数,该a函数如下:

注意到它调用了videoPlaybackEnded函数,这个函数也是个虚函数,我们来找一下它被实现的地方,在com.supersonic.adapters.applovinAppLovinAdapter类中:

bingo,正是它触发了onRewardedVideoAdRewarded函数!

到此为止,“AppLovin”的一个广告周期我们也分析完了。下面说一下我是如何发现supersonic会调用不同adapter的。

0x03 java无源码调试

比较简单的方法为

  1. adb forward tcp:9999 jdwp:[pid]
  2. 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函数,且它调用的是SuperSonicXBridgeshowRewardVideo函数,该SuperSonicXBridge类调用了SuperSonicXBridge$1这个内部类,由该内部类调用了真正的supsersonic的showRewardVideo接口:

接下来分析广告播放完后的情况。在SupersonicXBridge中有如下函数:

这个函数毫无疑问会在广告结束后调用,该函数是一个JNI函数,我们来看它在so文件中的定义:

我们看到这个函数会调用一个v5v5v4这个对象的vptr+16处的函数。v4这个对象应该是SupersonicXDelegate的子类对象。我们先看一下SupersonicXDelegate这个类的虚表:

我们可以看到,0x526d78+0xa=0x526d88处正好是函数supersonicRVAdRewarded。接下来我们看一下GameModeScene这个类的虚表:

可以看到同样有函数supersonicRVAdRewarded。看一下这个函数的定义:

这个函数又调用了supersonicClosesupersonicClose的定义如下:

它调用了setUserRewarded这个函数,给用户进行奖励。

至此,整个广告播放过程也就结束了。

0x06 总结

本文分析了一下supersonic这个广告sdk的实现,大家有空可以分析一下今天没有提到的几个adapter,它们有的可能是用native java实现的,有的可能又用了webview实现,很有意思。