1. 程式人生 > >Android中使用ClassLoader修改自定義異常類繼承來使異常捕獲失效來坑害你的同事

Android中使用ClassLoader修改自定義異常類繼承來使異常捕獲失效來坑害你的同事

原理:使用熱修復的原理,用ClassLoader載入同名替換類。根據類的載入機制,一個類只會被載入一次,所以可以使用ClassLoader載入一個同名的、Throwable子類中的非異常類的類,來使異常捕獲失效

首先,定義一個自定義異常

public class FooledYouException extends Exception {
    public FooledYouException() {
        super("Surprise!");
    }
}

然後,在方法中宣告丟擲和捕獲
public class FooledYouTool {


    public static void fooledYou(int i){
        try{
            System.out.println("Do something for i "+i);
            throwExceptionMethod();
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    private static void throwExceptionMethod() throws FooledYouException{
        throw new FooledYouException();
    }
}
可以看到方法中宣告的位所有異常類的父類,不應該有異常被丟擲

最後,呼叫方法

public class MainActivity2 extends Activity {
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        FooledYouTool.fooledYou(1);
    }
}

可以看到控制檯打印出了Do something for i 1和之後的異常棧,程式正常執行。

下面開始搗亂

1.把FooledYouException複製出來,將繼承Exception改為繼承Error,並編譯成類檔案,目錄結構和包保持一致

public class FooledYouException extends Error {
    public FooledYouException() {
        super("Surprise!");
    }
}


2.用jar工具打成jar包

jar cvf FooledYou.jar alpacaplayground/FooledYouException.class

3.用dx工具轉成dex

dx工具在sdk的built-tools目錄中,以mac為例,目錄為

~/Library/Android/sdk/build-tools

會有多個buildtools版本,我選擇了app使用的26版本。

執行命令

~/Library/Android/sdk/build-tools/26.0.2/dx --dex --output=FoolYouDex.jar FooledYou.jar

再將轉好的dex檔案複製到手機裡可被App訪問的地方,比如SD卡中。我為了省事放到了Assets目錄中,在App啟動後複製到App快取目錄下

由於要在其他人第一次使用這個類之前完成替換,將替換程式碼寫在Application類中

public class MyApplication extends Application {

    @Override
    public void onCreate() {
        super.onCreate();
        try {
            /*
            這裡是將dexjar檔案複製到快取目錄中來給ClassLoader載入
            所有的耗時操作都應該放在工作執行緒中進行,這裡是為了演示省事,
            直接在主執行緒中進行IO操作,請勿效仿
            不要阻塞主執行緒,不要阻塞主執行緒,不要阻塞主執行緒,重要的事情說三遍
             */
            InputStream im = getAssets().open("FoolYouDex.jar");
            File foolYou = new File(getCacheDir(), "foolYou.jar");
            if (foolYou.exists()) {
                foolYou.delete();
            }
            byte[] buf = new byte[4096];
            FileOutputStream fos = new FileOutputStream(foolYou);
            int length;
            while ((length = im.read(buf)) > 0) {
                fos.write(buf, 0, length);
            }
           //獲得預設的ClassLoader
            ClassLoader pathClassLoader = getClassLoader();
            try {
                Field field = ClassLoader.class.getDeclaredField("parent");
                field.setAccessible(true);
                //建立自定義ClassLoader,並以預設ClassLoader的parent作為自己的parent
                DexClassLoader dexClassLoader = new DexClassLoader(foolYou.getAbsolutePath(), getDir("dex", 0).getAbsolutePath(),
                        null, (ClassLoader) field.get(pathClassLoader));
                //將預設ClassLoader的parent設定為自定義ClassLoader
                field.set(pathClassLoader, dexClassLoader);
            } catch (Throwable e) {
                e.printStackTrace();
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}
修改manifest將Application的name指定為自定義的,再執行


可以看到一呼叫方法立刻就崩潰了。這時你的同事會上FooledYouTool中找問題,然而他會看到一個宣告捕獲Exception公共父類的try-catch語句。

這裡就是利用熱修復的原理,將載入的類替換成了外部dex中的,導致應用行為改變。

為了驗證一下,將異常捕獲地方的宣告改為捕獲對應類,並驗證繼承關係

public static void fooledYou(int i){
        try{
            System.out.println("Do something for i "+i);
            throwExceptionMethod();
        }catch (FooledYouException e){
            e.printStackTrace();
            Log.i("FooledYouException","is Throwable "+(Throwable.class.isInstance(e)));
            Log.i("FooledYouException","is Exception "+(Exception.class.isInstance(e)));
            Log.i("FooledYouException","is Error "+(Error.class.isInstance(e)));
        }
}
執行App,結果如下



可以看到方法正常執行,進入catch塊列印異常棧,根據Log輸出結果可以看出新建的FooledYouException實際為Error的子類,以Exception宣告捕獲自然是會漏掉。

這裡參考的熱修復/類載入方法來自文章

http://blog.csdn.net/sahadev_/article/details/53363052

原文中通過修改預設ClassLoader中的dexElements陣列實現,我在使用時遇到了問題,修改後無法找到入口Activity,一啟動就會丟擲ClassNotFound異常。

根據類載入器的雙親委派機制,子ClassLoader在嘗試載入一個類時,首先會讓父ClassLoader載入,所以嘗試修改預設載入器的parent來實現替換。

原關係為

預設載入器->預設載入器父載入器

修改後變為

預設載入器->我的載入器->預設載入器父載入器

執行結果正常。


相關推薦

Android使用ClassLoader修改定義異常繼承使異常捕獲失效坑害同事

原理:使用熱修復的原理,用ClassLoader載入同名替換類。根據類的載入機制,一個類只會被載入一次,所以可以使用ClassLoader載入一個同名的、Throwable子類中的非異常類的類,來使異常捕獲失效 首先,定義一個自定義異常 public class Fool

Android快速實現定義字體!

sdk true fcm version ttf spa pre ets 怎麽 前言:我們都知道,Android中默認的字體是黑體,而大多數app也都是使用的這種字體,但我們發現,大多數app中,個別地方字體非常好看,例如app的標題欄,菜單欄等地方,那他們是怎麽做到的呢?

AndroidIntent傳遞定義物件型別的資料

Android中Intent是用來實現元件之間的通訊的,可以在元件之間傳遞資料。可以傳遞一些基本型別的資料,也可以傳遞自定義物件型別的資料,但是如果要傳遞自定義型別的物件資料,這個自定義型別的物件必須是實現了Serializable介面或者是實現Parcelab

Android的Button定義點選效果

原來聽很多人說Button不能使用自定義的點選或選中效果,所以做下測試 結果顯示Button很強大 完全可以使用這種靈活的方法  方法一 1.放在drawable下的selector.xml檔案 <?xml version="1.0" encoding="utf-8"?

Android的Button定義點選效果之改變點選時按鈕的顏色

在Android中定義按鈕的點選效果可以通過自定義selector,通過設定兩張不同的背景圖片來改變點選時和未點選時的狀態,但有時候僅僅只想改變點選時按鈕的顏色,但是在selector中並不能直接定義顏色。下面程式碼實現一個點選按鈕時僅僅改變按鈕顏色。 1.

Android 定義Dialog,並在Activity實現按鈕監聽。

實際開發中,經常會用到Dialog,比如退出時候會彈出是否退出,或者還有一些編輯框也會用Dialog實現,效果圖如下: 開發中遇到的問題無非在於如果在Activity中監聽這個Dialog中實現的按鈕,Dialog類如下,在MyDialog這個類中實現了一個LeaveMyDialogLi

c#(winform)定義ListItem方便ComboBox添加Item項

urn left over string his 定義 return box item 1.定義ListItem類 public class ListItem { private string _key = string.Empty;

Lambda語句創建定義型時,也可指定某種特定型,方法是在new與{}之間寫上型名稱

特定 pan sel lambda語句 lam {} where distinct select 如: var fc =...ChildFath = fc.Select(c => new Child_Father { child = c.child, father =

java web項目後臺控制層對參數進行定義驗證 Pattern

span 快捷方式 pattern 例如 att ice 模式 匹配 ret Pattern pattern = Pattern.compile("/^([1-9]\\d+元*|[0]{0,1})$/");//將給定的正則表達式編譯到模式中 if(!"".equals

Java載入器( CLassLoader ) 死磕 6: 定義網路載入器

【正文】Java類載入器(  CLassLoader ) 死磕 6:  自定義網路類載入器 本小節目錄 6.1. 自定義網路類載入器的類設計 6.2. 檔案傳輸Server端的原始碼 6.3. 檔案傳輸Client端的原始碼 6. 4 自定義載入器SocketClassLoader的原始

ByteBuf 一個用於在通訊的資料解析傳輸組裝的定義容器

在做和硬體通訊的專案的時候,通訊的內容一般都是最基本的byte陣列,比如BLE,UART等等方式,傳遞的都是byte陣列。 移動端在接收的時候,就需要去解析byte陣列,然後從中通過拼接和或(|)以及位移等運算來得到想要的資料型別,比如說,unsignedByte,short,int,float

Mybatis需要返回的資料引數在資料表沒有對應的欄位,定義實體和resultmap作為返回值型別

自定義實體類:因為需要做相關記錄的統計,而表中沒有統計欄位 public class TrafficJeevesDistrictCount { //施工top5+1 按區域 private String districtInfo; private

Android系統實現AIDL 定義物件傳遞

  今天要在《在Android系統中實現AIDL介面回撥》這篇文章的基礎上實現AIDL自定義物件的傳遞功能。還是上一篇說到的三個專案: ├── SimpleJar ├── SimpleJarClient └── SimpleJarService 一、在SimpleJar專

spring 定義工具 @Autowired引入service或者mapper為null

在SMM專案中,經常使用@Autowired引入service或者mapper,但是在自定的utils包下引入service或者mapper的時候,會出現null的情況,下面就來解決這個問題。 首先,我們在spring的配置檔案中加上 掃描utils工具類的配置 <context:

android studio引入一個定義的佈局,定義控制元件,避免每一個活動都編寫一樣佈局程式碼的問題

本次演示的是標題欄上建立按鈕,即 引入自定義佈局和自定義控制元件的應用十分的廣泛,它的形成的效果很多的應用程式都有,我們可以自定義標題欄,因為普通的標題欄就是一行文字,但是,我們可以發現,很多手機軟體的標題欄上都有返回,或者 進入的按鈕,尤其是全面屏的手機。而且它還能解

實驗報告:(1)合理定義一個三角形Triangle,成員屬性包括3條邊,能否構成三角形的標誌;成員方法包括構造方法、修改3條邊、計算面積。 (2)寫一測試,測試自定義三角形Triangle是否正

(1)合理定義一個三角形類Triangle,成員屬性包括3條邊,能否構成三角形的標誌;成員方法包括構造方法、修改3條邊、計算面積。(2)寫一測試類,測試自定義三角形類Triangle是否正確。class Triangle_D{ private double f_edge;

c++STL的hash_map定義

是的,hash_map是一個很方便的容器,有了STL確確實實給了C++developer很大方便,hash_map就是其中一種。他在資料少的時候,作用和基於RB-tree的map差不多,甚至不如,畢竟有hasher。但是在大量資料的時候,就很快捷了。我平時用hash_map

定義Threadrun()方法,super().run 的理解

當我們重寫Thread類中的run方法時,經常會看到這樣的程式碼: public void run() { super.run(); System.out.println(Thread.currentThread().getName(

Android】 XML佈局檔案,使用定義屬性不提示和不生效

在XML檔案中使用首先要宣告 xmlns:toolbar=http://schemas.android.com/apk/res/cn.zzm.toolbar 注意,“toolbar”可以換成其他的任何名字,後面的url地址必須最後一部分必須用上自定義元件的包名。自定義屬性了

Android 獲取不到定義屬性(Android studio 2.2版本定義屬性的宣告)

跟著慕課網中鴻洋大神照著寫QQ5.0的側滑選單中遇到的問題。 如果按照鴻洋大神所說書寫格式是 xmlns:arrtss=”http://schemas.android.com/apk/+當前應用包名但