Android逆向实战 - 腾讯新闻去开屏广告

    上次反编译一个工具类app失败,原因是使用了360加固,回编译后无法启动。一般来讲,大厂的app考虑到性能、兼容性、包体积等,通常不用加固。因此,本次我们选一个大一些的app-腾讯新闻。写在前面:本篇博客仅用来研究和学习,如有侵权,请联系我删除,谢谢。

目录

一、前言

二、验证加固

1、反编译验证

2、ApkScan验证

三、反编译

1、使用apktool反编译

2、修改smali代码

3、回编译和签名

4、再次修改smali代码

5、再次花式修改源码


一、前言

    现在的app,不管是大厂的,还是一些名不见经传的小众app,都植入了非常多的广告。其中,最让人讨厌的应该就是开屏广告了,特别是那种一晃动就跳走的摇一摇广告。

    本篇,我们就通过腾讯新闻app手把手的教学,反编译去掉开屏广告。顺便一提:腾讯新闻的开屏广告真的是每次启动都必出:

2568775723c54364a2fab181158e603c.gif

二、验证加固

    首先,我们验证下腾讯新闻app是否加固,防止因为加固导致失败。验证加固有两种方式。

一种是反编译后不做任何修改直接回编译签名看是否可以安装和启动

一种是使用ApkScan验证

1、反编译验证

(1)反编译

apktool d /Users/xxx/Desktop/TencentNews_10565.apk  -o /Users/xxx/Desktop/TencentNews

(2)不做任何修改,直接回编译

apktool b /Users/xxx/Desktop/TencentNews

 (3)签名

java -jar apksigner.jar sign --ks /Users/xxx/Desktop/test.keystore  /Users/xxx/Desktop/TencentNews/dist/TencentNews_10565.apk

(4)安装

adb install -r /Users/xxx/Desktop/TencentNews/dist/TencentNews_10565.apk

安装后,启动腾讯新闻app,确认可以打开使用,验证结论:腾讯新闻app没有加固。 

2、ApkScan验证

执行以下命令:

java -jar /Users/xxx/Desktop/反编/ApkScan-PKID.jar

打开窗口后把apk拖进去:

b81570b5c6504a15a85f2489ccbc70b6.png

验证结论:腾讯新闻app没有加固。

三、反编译

1、使用apktool反编译

    如果验证加固使用的方法一,那么直接把反编译文件夹中的dist文件夹删掉,使用反编译的产物就可以了。如果验证加固使用的方法二,可以重新执行下反编译。

apktool d /Users/xxx/Desktop/TencentNews_10565.apk -o /Users/xxx/Desktop/TencentNews

    我们打开res/layout文件夹,这里面有很多的xml文件,这里面包含的就是应用用到的系统控件和自身页面的xml布局文件,可以看到很多包含ad的xml文件,腾讯新闻里面确实很多广告,我们还是挑选开屏广告来看吧。一般来讲,开屏广告是作为启动页,打开AndroidManifest.xml文件,搜索android.intent.category.LAUNCHER,找到了SplashActivity,毫无疑问这就是启动页,大概率承载了开屏广告:

4d7b044614504a1e85e2dafe042e71c1.png

 使用adb命令启动SplashActivity

adb shell am start -n com.tencent.news/.activity.SplashActivity

    确实是启动页,反复启动多次,均没有出现开屏广告,怀疑是判断了点击图标启动app才会出开屏广告。那么,可以顺着这个猜想去试试。直接查看smali代码难度有点大,我们去看下源码。

2、修改smali代码

    直接使用jadx反编译,反编后搜索SplashActivity,打开SplashActivity这个类,搜索onCreate:

79fe55548324452c9953b4182c89c1e9.png

    可以看到有多个if/else的判断逻辑,并且有finish(),猜测是不是结束掉SplashActivity就进入首页了。我们尝试修改smali代码,注意在Jadx不能直接修改,去我们使用aptool反编译后的文件夹中去搜索SplashActivity,然后在SplashActivity.smali修改。在另一个else分支(cond_1)插入finish()的代码:

invoke-virtual {p0}, Lcom/tencent/news/ui/BaseActivity;->finish()V

return-void

b010a9f43f574a68b482070a1525a07d.png

3、回编译和签名

修改smali代码并且保存后,回编:

apktool b /Users/xxx/Desktop/TencentNews

签名生成新的apk: 

java -jar apksigner.jar sign --ks /Users/xxx/Desktop/test.keystore  /Users/xxx/Desktop/TencentNews/dist/TencentNews_10565.apk

ef238032cfdc483694f688071a7eca70.png

    安装一下试试,这样是不行的,在首页直接退出app了,哈哈。之所以这样去尝试,其实是在我们的正常认知中,打开一个app首先是SplashActivity,然后是MainActivity,但其实在Manifest文件中也没搜到MainActivity,说不定SplashActivity也承担了主页的功能呢。 

4、再次修改smali代码

这次不fnish()了,直接插入return:

return-void

    再次执行回编译和签名后安装,果然也不行,卡在启动页不动了。这样尝试,是猜想app为了防止卡在启动页时间过长影响用户体验,可能会做一个定时的task,比如说3秒仍然未进入首页,则打开首页,但初步看上去TX新闻没有做这种兜底逻辑。

    这似乎更加验证了我们一开始的猜想:SplashActivity也承担了首页的功能,首页的几个tab应该是fragment,在首页的空白页面或开屏广告展现后,露出首页的新闻。其实可以在腾讯新闻的首页去dump一下activity:

adb shell dumpsys activity activities

可以看到,只有SplashActivity:

ACTIVITY MANAGER ACTIVITIES (dumpsys activity activities)
Display #0 (activities from top to bottom):

  Stack #1050: type=standard mode=fullscreen
  isSleeping=false
  mBounds=Rect(0, 0 - 0, 0)
    Task id #1051
    mBounds=Rect(0, 0 - 0, 0)
    mMinWidth=-1
    mMinHeight=-1
    mLastNonFullscreenBounds=null
    * TaskRecord{5a6a1a0 #1051 A=com.tencent.news U=0 StackId=1050 sz=1}
      userId=0 effectiveUid=u0a1943 mCallingUid=u0a74 mUserSetupComplete=true mCallingPackage=com.huawei.android.launcher
      affinity=com.tencent.news
      intent={act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10200000 cmp=com.tencent.news/.activity.SplashActivity}
      mActivityComponent=com.tencent.news/.activity.SplashActivity
      autoRemoveRecents=false isPersistable=true numFullscreen=1 activityType=1
      rootWasReset=true mNeverRelinquishIdentity=true mReuseTask=false mLockTaskAuth=LOCK_TASK_AUTH_PINNABLE
      Activities=[ActivityRecord{5d0d791 u0 com.tencent.news/.activity.SplashActivity t1051}]
      askedCompatMode=false inRecents=true isAvailable=true
      stackId=1050
      hasBeenVisible=true mResizeMode=RESIZE_MODE_RESIZEABLE mSupportsPictureInPicture=false isResizeable=true lastActiveTime=5012950897 (inactive for 0s)
      * Hist #0: ActivityRecord{5d0d791 u0 com.tencent.news/.activity.SplashActivity t1051}
          packageName=com.tencent.news processName=com.tencent.news
          launchedFromUid=11943 launchedFromPackage=com.tencent.news userId=0
          app=ProcessRecord{5e2ec99 21259:com.tencent.news/u0a1943}
          Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] cmp=com.tencent.news/.activity.SplashActivity (has extras) }
          frontOfTask=true task=TaskRecord{5a6a1a0 #1051 A=com.tencent.news U=0 StackId=1050 sz=1}
          taskAffinity=com.tencent.news
          mActivityComponent=com.tencent.news/.activity.SplashActivity
          baseDir=/data/app/com.tencent.news-wd3GPOer6dBCnZkO6QJNRg==/base.apk
          dataDir=/data/user/0/com.tencent.news
          stateNotNeeded=false componentSpecified=true mActivityType=standard
          compat={480dpi} labelRes=0x7f100073 icon=0x7f0e0000 theme=0x7f110127
          mLastReportedConfigurations:
           mGlobalConfig={1.0 ?mcc?mnc [zh_CN_#Hans] ldltr sw392dp w392dp h776dp 480dpi nrml long hdr port finger -keyb/v/h -nav/h winConfig={ mBounds=Rect(0, 0 - 1176, 2400) mAppBounds=Rect(0, 72 - 1176, 2400) mWindowingMode=fullscreen mDisplayWindowingMode=fullscreen mActivityType=undefined mAlwaysOnTop=undefined mRotation=ROTATION_0} suim:1 s.228}
           mOverrideConfig={1.0 ?mcc?mnc [zh_CN_#Hans] ldltr sw392dp w392dp h776dp 480dpi nrml long hdr port finger -keyb/v/h -nav/h winConfig={ mBounds=Rect(0, 0 - 1176, 2400) mAppBounds=Rect(0, 72 - 1176, 2400) mWindowingMode=fullscreen mDisplayWindowingMode=fullscreen mActivityType=standard mAlwaysOnTop=undefined mRotation=ROTATION_0} suim:1 s.1}
          CurrentConfiguration={1.0 ?mcc?mnc [zh_CN_#Hans] ldltr sw392dp w392dp h776dp 480dpi nrml long hdr port finger -keyb/v/h -nav/h winConfig={ mBounds=Rect(0, 0 - 1176, 2400) mAppBounds=Rect(0, 72 - 1176, 2400) mWindowingMode=fullscreen mDisplayWindowingMode=fullscreen mActivityType=standard mAlwaysOnTop=undefined mRotation=ROTATION_0} suim:1 s.1}
          taskDescription: label="null" icon=null iconResource=0 iconFilename=null primaryColor=ffe6e6e6
           backgroundColor=fff3f3f3
           statusBarColor=ff000000
           navigationBarColor=fefcfcfb
          launchFailed=false launchCount=0 lastLaunchTime=-20m25s855ms
          haveState=false icicle=null
          state=RESUMED stopped=false delayedResume=false finishing=false
          keysPaused=false inHistory=true visible=true sleeping=false idle=true mStartingWindowState=STARTING_WINDOW_SHOWN
          fullscreen=true noDisplay=false immersive=false launchMode=1
          frozenBeforeDestroy=false forceNewConfig=false
          mActivityType=standard
           nowVisible=true lastVisibleTime=-14s822ms
          resizeMode=RESIZE_MODE_RESIZEABLE
          mLastReportedMultiWindowMode=false mLastReportedPictureInPictureMode=false

    Running activities (most recent first):
      TaskRecord{5a6a1a0 #1051 A=com.tencent.news U=0 StackId=1050 sz=1}
        Run #0: ActivityRecord{5d0d791 u0 com.tencent.news/.activity.SplashActivity t1051}

    mResumedActivity: ActivityRecord{5d0d791 u0 com.tencent.news/.activity.SplashActivity t1051}

  Stack #0: type=home mode=fullscreen
  isSleeping=false
  mBounds=Rect(0, 0 - 0, 0)

    Task id #1
    mBounds=Rect(0, 0 - 0, 0)
    mMinWidth=-1
    mMinHeight=-1
    mLastNonFullscreenBounds=null
    * TaskRecord{5bdd731 #1 A=com.huawei.android.launcher U=0 StackId=0 sz=1}
      userId=0 effectiveUid=u0a74 mCallingUid=0 mUserSetupComplete=true mCallingPackage=null
      affinity=com.huawei.android.launcher
      intent={act=android.intent.action.MAIN cat=[android.intent.category.HOME] flg=0x10000300 cmp=com.huawei.android.launcher/.unihome.UniHomeLauncher}
      mActivityComponent=com.huawei.android.launcher/.unihome.UniHomeLauncher
      autoRemoveRecents=false isPersistable=true numFullscreen=1 activityType=2
      rootWasReset=false mNeverRelinquishIdentity=true mReuseTask=false mLockTaskAuth=LOCK_TASK_AUTH_PINNABLE
      Activities=[ActivityRecord{5c57159 u0 com.huawei.android.launcher/.unihome.UniHomeLauncher t1}]
      askedCompatMode=false inRecents=true isAvailable=true
      mRootProcess=ProcessRecord{5706809 2869:com.huawei.android.launcher/u0a74}
      stackId=0
      hasBeenVisible=true mResizeMode=RESIZE_MODE_RESIZEABLE mSupportsPictureInPicture=false isResizeable=true lastActiveTime=5012936238 (inactive for 15s)
      * Hist #0: ActivityRecord{5c57159 u0 com.huawei.android.launcher/.unihome.UniHomeLauncher t1}
          packageName=com.huawei.android.launcher processName=com.huawei.android.launcher
          launchedFromUid=0 launchedFromPackage=null userId=0
          app=ProcessRecord{5706809 2869:com.huawei.android.launcher/u0a74}
          Intent { act=android.intent.action.MAIN cat=[android.intent.category.HOME] flg=0x10000300 cmp=com.huawei.android.launcher/.unihome.UniHomeLauncher }
          frontOfTask=true task=TaskRecord{5bdd731 #1 A=com.huawei.android.launcher U=0 StackId=0 sz=1}
          taskAffinity=com.huawei.android.launcher
          mActivityComponent=com.huawei.android.launcher/.unihome.UniHomeLauncher
          baseDir=/system/app/HwLauncher6/HwLauncher6.apk
          dataDir=/data/user_de/0/com.huawei.android.launcher
          stateNotNeeded=true componentSpecified=false mActivityType=home
          compat={480dpi} labelRes=0x7f080084 icon=0x7f02012b theme=0x2060013
          mLastReportedConfigurations:
           mGlobalConfig={1.0 ?mcc?mnc [zh_CN_#Hans] ldltr sw392dp w392dp h776dp 480dpi nrml long hdr port finger -keyb/v/h -nav/h winConfig={ mBounds=Rect(0, 0 - 1176, 2400) mAppBounds=Rect(0, 72 - 1176, 2400) mWindowingMode=fullscreen mDisplayWindowingMode=fullscreen mActivityType=undefined mAlwaysOnTop=undefined mRotation=ROTATION_0} suim:1 s.279}
           mOverrideConfig={1.0 ?mcc?mnc [zh_CN_#Hans] ldltr sw392dp w392dp h776dp 480dpi nrml long hdr port finger -keyb/v/h -nav/h winConfig={ mBounds=Rect(0, 0 - 1176, 2400) mAppBounds=Rect(0, 72 - 1176, 2400) mWindowingMode=fullscreen mDisplayWindowingMode=fullscreen mActivityType=home mAlwaysOnTop=undefined mRotation=ROTATION_0} suim:1 s.12}
          CurrentConfiguration={1.0 ?mcc?mnc [zh_CN_#Hans] ldltr sw392dp w392dp h776dp 480dpi nrml long hdr port finger -keyb/v/h -nav/h winConfig={ mBounds=Rect(0, 0 - 1176, 2400) mAppBounds=Rect(0, 72 - 1176, 2400) mWindowingMode=fullscreen mDisplayWindowingMode=fullscreen mActivityType=home mAlwaysOnTop=undefined mRotation=ROTATION_0} suim:1 s.12}
          RequestedOverrideConfiguration={0.0 ?mcc?mnc ?localeList ?layoutDir ?swdp ?wdp ?hdp ?density ?lsize ?long ?ldr ?wideColorGamut ?orien ?uimode ?night ?touch ?keyb/?/? ?nav/? winConfig={ mBounds=Rect(0, 0 - 0, 0) mAppBounds=null mWindowingMode=undefined mDisplayWindowingMode=undefined mActivityType=home mAlwaysOnTop=undefined mRotation=undefined}}
          launchFailed=false launchCount=0 lastLaunchTime=-32d11h41m24s609ms
          haveState=true icicle=Bundle[mParcelledData.dataSize=8832]
          state=STOPPED stopped=true delayedResume=false finishing=false
          keysPaused=false inHistory=true visible=false sleeping=false idle=true mStartingWindowState=STARTING_WINDOW_NOT_SHOWN
          fullscreen=true noDisplay=false immersive=false launchMode=2
          frozenBeforeDestroy=false forceNewConfig=false
          mActivityType=home
           nowVisible=false lastVisibleTime=-16s580ms
          connections=com.android.server.wm.ActivityServiceConnectionsHolder@5ce1c6d
          resizeMode=RESIZE_MODE_RESIZEABLE
          mLastReportedMultiWindowMode=false mLastReportedPictureInPictureMode=false

    Running activities (most recent first):
      TaskRecord{5bdd731 #1 A=com.huawei.android.launcher U=0 StackId=0 sz=1}
        Run #0: ActivityRecord{5c57159 u0 com.huawei.android.launcher/.unihome.UniHomeLauncher t1}

    mLastPausedActivity: ActivityRecord{5c57159 u0 com.huawei.android.launcher/.unihome.UniHomeLauncher t1}

 ResumedActivity:ActivityRecord{5d0d791 u0 com.tencent.news/.activity.SplashActivity t1051}

  ResumedActivity: ActivityRecord{5d0d791 u0 com.tencent.news/.activity.SplashActivity t1051}

ActivityStackSupervisor state:
  topDisplayFocusedStack=ActivityStack{5e38a54 stackId=1050 type=standard mode=fullscreen visible=true translucent=false, 1 tasks}
mLastResumedActivity=ActivityRecord{5d0d791 u0 com.tencent.news/.activity.SplashActivity t1051}
  displayId=0 stacks=2
   mHomeStack=ActivityStack{5b6acd1 stackId=0 type=home mode=fullscreen visible=false translucent=true, 1 tasks}
   mPreferredTopFocusableStack=ActivityStack{5e38a54 stackId=1050 type=standard mode=fullscreen visible=true translucent=false, 1 tasks}
   mLastFocusedStack=ActivityStack{5e38a54 stackId=1050 type=standard mode=fullscreen visible=true translucent=false, 1 tasks}
  mCurTaskIdForUser={0=1051}
  mUserStackInFront={}
  isHomeRecentsComponent=true  KeyguardController:
    mKeyguardShowing=false
    mAodShowing=false
    mKeyguardGoingAway=false
    Occluded=false DismissingKeyguardActivity=null at display=0
    mDismissalRequested=false
    mVisibilityTransactionDepth=0
  LockTaskController
    mLockTaskModeState=NONE
    mLockTaskModeTasks=
    mLockTaskPackages (userId:packages)=
      u0:[]

5、一种存在badcase的去广告方案

    接下来,只能再去看看SplashActivity的代码了。其实,腾讯新闻开屏广告的展示时间是有上限的,差不多五秒,经过了几次搜索,发现了以下两个类,存在疑似控制开屏广告跳过时间的代码。

(1)SplashView

看类名应该是开屏的承载组件

baef7bfb00f249ce8968b293497dfb0c.png

 (2)SplashAdLoader

看类名应该是开屏广告的加载器

21042b890ca24f129d10cb6112363fd6.png

    接下来,尝试转到smali源码,把5000修改为0。这里需要注意的是:smali源码中使用的是16进制,因此是看不到5000的,打开SplashView.smali,可以看到,是0x1388L,而定义完这个常量后,smali代码中不会使用这个常量的名字,而是直接使用0x1388,因此直接搜索0x1388,全部替换为0x0:

6799db90db13496b86c96ad444ad214a.png

SplashAdLoader.smali文件也一样,搜索1388后全部修改为0x0:

419cbf9d84854732ab420a46cba4dd5f.png

改完后保存并且回编译和签名,安装新的app,多次启动确实没展示开屏广告了,大功告成。

3623b00dad1d4ee2a9d11b07acf57ca6.gif

    这样修改并不是完美的,启动了几十次偶现露出开屏后瞬间自动消失,毕竟这么少量的代码修改app,多少会存在badcase。接下来,我们再看看有没有更好的方案?

6、完美的去开屏广告方案

    上面也提到过,SplashView看类名应该是开屏的承载组件。接下来,需要我们有耐心的详细看一下SplashView的源码。在入口方法init中发现了if/else的逻辑:

非常简单且清晰的代码,翻译一下:

if (展示开屏广告) {
    // 挑选出一个开屏
    pickOutSplash();
} else {
    // 不展示广告
    showNoAd();
}

 接下来怎么修改呢?很简单了吧?没错,就是这样:

if (展示开屏广告) {
    // 不展示广告
    showNoAd();
} else {
    // 不展示广告
    showNoAd();
}

尝试修改SplashView.smali代码,找到pickOutSplash()

尝试做如下修改:

接下来回编译,签名验证。哈哈,这次完美无漏出了,试了几十次,没问题,符合预期。当然受限于上传图片的大小为5M,只能演示几次:

再次总结一下反编app的几个步骤:

1、验证是否加固

2、反编译

3、分析和修改smali源码

4、回编译

5、签名

    最后,反编译app其实是不太好的行为。本文仅限于自娱自乐,研究和学习,坚决不会用于传播或商业行为。也希望看到本篇博客的朋友,不要用来做一些不好的事情,多多学些,传播正能量。

猜你喜欢

转载自blog.csdn.net/qq_21154101/article/details/130409016