日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

转载:使用 frida hook 插件化 apk ( classloader )

發(fā)布時間:2024/7/23 编程问答 38 豆豆
生活随笔 收集整理的這篇文章主要介紹了 转载:使用 frida hook 插件化 apk ( classloader ) 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

?

使用 frida hook 插件化 apk:https://bbs.pediy.com/thread-258772.htm

?

最近拿到一個XX視頻apk樣本,里面有視頻、直播和小說,沒有VIP只能試看30秒,剛好最近學(xué)習(xí)frida,用來練習(xí)下,分析過程中發(fā)現(xiàn)是一個插件化的apk,本文記錄下分析的過程。

?

初步分析

首先從AndroidManifest.xml中獲取到apk的包名,并且查看下activity情況:

發(fā)現(xiàn)只有4個Activity,正常情況下一個apk肯定不止這些,所以初步懷疑這只是外殼,真正邏輯是在其他地方,會動態(tài)加載進(jìn)來。

?

ps 查看

打開 apk

可以看到有兩個進(jìn)程,從上圖也可以看到,2、3和4處的Activity是運行在plugin進(jìn)程中,為了確認(rèn)下視頻播放所在的進(jìn)程,使用dumpsys meminfo查看

?

dumpsys meminfo查看

打開任意播放界面

確認(rèn)視頻播放是在plugin進(jìn)程中,此時真正的邏輯已經(jīng)加載到進(jìn)程中,查看下plugin進(jìn)程的maps

?

cat /proc/7906/maps

從上圖可以看到,真正邏輯所在的apk是plugin-shadow-apk-debug.apk,是在該apk的私有文件目錄中。

從代碼中分析也可知道,此apk是插件化apk,使用的是騰訊開源的插件化框架Shadow,感興趣的可以去了解下。

?

定位關(guān)鍵代碼

既然已經(jīng)找到真正的apk,那我們就需要定位到關(guān)鍵代碼地方。

?

字符串定位

從字符串中定位到有多個類滿足,此時一個一個去分析排查太耗時,接下來通過frida來枚舉出所有加載的類。

?

frida 枚舉所有加載的類

Java.enumerateLoadedClasses(callbacks) 是用來枚舉當(dāng)前所有加載的類,通過和上述幾個關(guān)鍵類對比來找到實際調(diào)用的類,callbacks需要提供回調(diào)函數(shù),對應(yīng)onMatch和onComplete。具體如下面:

Java.perform(function () {// 上述搜索到的多個類var key_class = ["com.facebook.plugin.widget.dkplayer.controller.PlayerVideoController","com.iqiyi.plugin.widget.dkplayer.controller.PlayerVideoController","com.facebook.plugin.widget.dkplayer.controller.VideoController","com.iqiyi.plugin.widget.dkplayer.controller.VideoController"]Java.enumerateLoadedClasses({"onMatch": function(name, handle) {for (var i = 0; i < key_class.length; i++) {if (key_class[i] == name) {console.log(name);}}},"onComplete": function() {console.log("success");}}); });

運行結(jié)果:

com.iqiyi.plugin.widget.dkplayer.controller.VideoController

success

第一行為輸出結(jié)果,即表示當(dāng)前使用的類為 com.iqiyi.plugin.widget.dkplayer.controller.VideoController;

第二行為執(zhí)行完成的日志。

?

VideoController 類分析

找到字符串位置

public int setProgress() {... ...if (this.tryWatchTv != null && position > 0) { // 如果是試看pos = (int) (((long) this.stopPlayTime) - position);TextView textView = this.tryWatchTv;StringBuilder stringBuilder = new StringBuilder();stringBuilder.append("剩余試看時間: "); // 此處是我們看到的字符串if (pos > 0) {j = (long) pos;}stringBuilder.append(stringForTime(j));textView.setText(stringBuilder.toString());}if (!this.isVip) { // 此處是通過isVip變量執(zhí)行不同邏輯StringBuilder stringBuilder2 = new StringBuilder();stringBuilder2.append("position = ");stringBuilder2.append(position);stringBuilder2.append(" showVipHintTime = ");stringBuilder2.append(this.showVipHintTime);LogHelper.i(stringBuilder2.toString());if (position < ((long) this.showVipHintTime) || this.showVipHintTime <= 0) {this.vipHintView.setVisibility(8);} else {this.vipHintView.setVisibility(0);}if (position >= ((long) this.stopPlayTime)) {this.mMediaPlayer.pause();}}... ...}

可以看到類中通過isVip變量來執(zhí)行不同邏輯,繼續(xù)看下isVip是如何設(shè)置的

public void setVip(boolean isVip) {this.isVip = isVip;this.tryWatchTv.setVisibility(this.isVip ? 8 : 0);if (this.isVip) {this.vipHintView.setVisibility(8);} }

可以看到當(dāng)前類有setVip方法,用于設(shè)置該變量,此時可以不用在繼續(xù)分析調(diào)用者,最終都會調(diào)用此處,所以我們可以使用frida hook該方法。

?

frida hook setVip

var videoController = Java.use("com.iqiyi.plugin.widget.dkplayer.controller.VideoController"); videoController.setVip.implementation = function() {console.log("hook setVip");this.setVip(true); };

運行結(jié)果:

從運行結(jié)果來看,出現(xiàn)ClassNotFoundException錯誤,說明沒有找到我們要hook的類。

?

frida枚舉classloader

由于是插件化apk,類加載是在插件化框架自定義的,所以classloader不能使用默認(rèn)的。我們可以使用Java.enumerateClassLoaders(callbacks)來打印出所有的加載器。

Java.perform(function () {Java.enumerateClassLoaders({"onMatch": function(loader) {console.log(loader);},"onComplete": function() {console.log("success");}}); });

運行結(jié)果:

由上面分析可知,真正邏輯代碼是在plugin-shadow-apk-debug.apk中,那該apk對應(yīng)的classloader是com.tencent.shadow.core.loader.classloaders.PluginClassLoader。

?

frida指定classloader

來看下Java.ClassFactory中l(wèi)oader的介紹:"read-only property providing a wrapper for the class loader currently being used.",loader是當(dāng)前classloader的wrapper,我們修改classloader可以通過修改該字段。Java.classFactory是默認(rèn)的class factory,所以我們需要修改的是Java.classFactory.loader。

Java.perform(function () {Java.enumerateClassLoaders({"onMatch": function(loader) {if (loader.toString().startsWith("com.tencent.shadow.core.loader.classloaders.PluginClassLoader")) {Java.classFactory.loader = loader; // 將當(dāng)前class factory中的loader指定為我們需要的}},"onComplete": function() {console.log("success");}}); });

?

最終腳本

Java.perform(function () {Java.enumerateClassLoaders({"onMatch": function(loader) {if (loader.toString().startsWith("com.tencent.shadow.core.loader.classloaders.PluginClassLoader")) {Java.classFactory.loader = loader; // 將當(dāng)前class factory中的loader指定為我們需要的}},"onComplete": function() {console.log("success");}});// 此處需要使用Java.classFactory.usevar videoController = Java.classFactory.use("com.iqiyi.plugin.widget.dkplayer.controller.VideoController");videoController.setVip.implementation = function() {console.log("hook setVip");this.setVip(true);}; });

運行結(jié)果:

可以看到,我們已經(jīng)成功hook,并且視頻上已經(jīng)沒有顯示剩余時間。

?

frida hook enum

直播和小說的vip判斷和視頻是不一致的,是通過enum中VIP字段值和1進(jìn)行對比來判斷,具體定位過程和上面類似。

判斷代碼為:

if (TextUtils.equals("1", PluginEnum.VIP.getValue())) {...}

?

enum 測試

我們的目的是為了hook VIP,但是對enum的這種用法不是很熟,于是寫了個測試程序,來進(jìn)一步了解

public enum TestEnum {A("a"),B("b"),C("c");private String value;private TestEnum(String value) {this.value = value;}public String getValue() {return this.value;}}

使用 javap 打開對應(yīng)的 class 文件:

Compiled from "TestEnum.java" public final class TestEnum extends java.lang.Enum<TestEnum> {public static final TestEnum A;public static final TestEnum B;public static final TestEnum C;public static TestEnum[] values();public static TestEnum valueOf(java.lang.String);public java.lang.String getValue();static {}; }

從這里可以很明顯看到, A、B和C都屬于TestEnum中的靜態(tài)成員變量。來看下調(diào)用的smali代碼:

sget-object v3, Lcom/iqiyi/plugin/base/PluginEnum;->VIP:Lcom/iqiyi/plugin/base/PluginEnum; invoke-virtual {v3}, Lcom/iqiyi/plugin/base/PluginEnum;->getValue()Ljava/lang/String;

從smali上也能看出來類似的邏輯,VIP是com/iqiyi/plugin/base/PluginEnum的靜態(tài)成員,然后在調(diào)用getValue()方法。所以我們hook com/iqiyi/plugin/base/PluginEnum類的getValue方法,然后判斷調(diào)用者是否為VIP。

?

最終腳本

Java.perform(function () {var pluginEnum = Java.classFactory.use("com.iqiyi.plugin.base.PluginEnum");var String = Java.use("java.lang.String");pluginEnum.getValue.implementation = function() {var value = this.getValue();if (this == "VIP") { // 此時this 或者 this.getString() 返回的是靜態(tài)成員名var vip = String.$new("1");this.setValue(vip); // 調(diào)用 setValue 修改VIP值return vip;} else {return value;}} });

?

整體腳本

Java.perform(function () {Java.enumerateClassLoaders({"onMatch": function(loader) {if (loader.toString().startsWith("com.tencent.shadow.core.loader.classloaders.PluginClassLoader")) {Java.classFactory.loader = loader;}},"onComplete": function() {console.log("success");}});var videoController = Java.classFactory.use("com.iqiyi.plugin.widget.dkplayer.controller.VideoController");videoController.setVip.implementation = function() {console.log("hook setVip");this.setVip(true);};var pluginEnum = Java.classFactory.use("com.iqiyi.plugin.base.PluginEnum");var String = Java.use("java.lang.String");pluginEnum.getValue.implementation = function() {var value = this.getValue();if (this == "VIP") {var vip = String.$new("1");this.setValue(vip);return vip;} else {return value;}}});

?

總結(jié)

通過對該樣本的分析,逆向找尋關(guān)鍵代碼相對簡單,但是在使用frida hook時相對難點,特別是對于frida和插件化不熟的情況下。本文涉及到的有:

  • frida枚舉所有加載的類;
  • frida枚舉classloader;
  • frida對enum類型的hook。
  • ?

    ?

    ?

    總結(jié)

    以上是生活随笔為你收集整理的转载:使用 frida hook 插件化 apk ( classloader )的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。