天天看點

Android Hook 實戰--替換第三方sdk的類場景:問題描述:解決方案:總結

業精于勤荒于嬉,寫文章練習表達能力,寫代碼練習基本工。

場景:

項目中調用了第三方sdk中的方法,此方法中關鍵日志被屏蔽,需要跟蹤日志方可分析失敗原因。

問題描述:

  1. 檢視jar包的class,對應失敗的分支均有log,但log開關設定為false。
  2. 裝置定制的os關閉了斷點調試的功能。
  3. 和sdk提供商溝通後,暫時沒有答複什麼時候提供新的sdk(希望對方打開日志開關)
/**
    由于内容涉密,我用僞代碼代替
   */
    public class SDKImpl extends SDK {

    
   	public Info getXXX() {
        try {
            /*此處省略*/
            if (條件1) {
                Logger.t( TAG, "ERROR 1 !!");
                return null;
            } else {
                 /*此處省略*/
                if (條件2) {
                
                    Logger.t( TAG, "ERROR 2 !!");
                    return null;
                    
                } else {

                    if (條件3) {
                        Logger.t(TAG, "ERROR 3 !!");
                        return null;
                        
                    } else if (條件4) {
                      
                        if (條件5) {
                            Logger.t(TAG, "ERROR 4 !!");
                            return null;
                            
                        } else {
  
                            if (條件6) {
                                Logger.t(TAG, "ERROR 5 !!");
                                return null;
                            } else {
                             
                             	//成功
                                return new Info();
                            }
                        }
                    } else {
                        Logger.t(TAG, "ERROR 6 !!");
                        return null;
                    }
                }
            }
        } catch (Exception e) {
 			Logger.t(TAG, "ERROR 7 !!");
            return null;
        }
    }

   }
    

           

如果日志能列印,就知道具體在哪裡失敗了,我們看一下Logger.t()方法。

public class Logger {
	//如果調用這個,那麼就直接能看到log
	public static void d(String tag, String log) {
        Log.d(tag, log);
    }
    
    public static void t(String tag, String log) {
        if (loggable()) {
           d(tag, log);
        }
    }

	private static boolean loggable() {
        return false;
    }
 }
           

解決方案:

這種情況下,讓我們提供失敗原因,讓我先冷靜幾分鐘再說。

還好,想到了兩個解決方法,在客戶的sdk暫時無法更新的時間裡,讓我們把思緒放飛(當然是事後這樣調侃,客戶着急上火,可沒心思去想這些,肯定是,你不給打開日志,我分析不了!!!)。

  1. 使用熱修複方案的一種(類加載,替換)。
  2. Hook,實作SDKImplProxy,劫持并替換sdk中的SDKImpl對象。

類替換

在我們的代碼裡面寫一個一模一樣的Logger類(包括包名),僅僅把loggable()的傳回值改為false,讓PathClassLoader優先加載我們的Logger。

步驟1:編寫Logger (注意包名也和SDK中一模一樣)

private static boolean loggable() {
        return true;
}
           

步驟2:build後,找到class,生成dex,導入sd卡根目錄

dx --dex --output=patch.dex com/…/Logger.class

步驟3:在application中,利用雙親委派加載dex中的Logger.class,忽略sdk中的。

public class MyApplication extends Application {

    @Override
    protected void attachBaseContext(Context base) {
        super.attachBaseContext(base);
        //越早執行這個,越好。
        HotfixUtils.installPatch(this,new File("/sdcard/patch.dex"));
    }
}
           

然後重新開機app,成功列印了錯誤日志,沒有給出HotfixUtils的源碼,因為比較繁瑣,針對不通android版本,需要做相容,我是直接拿來其他人的代碼運作成功的,原理大家在網上查,都差不多,利用反射最終修改dexElements數組,把更新檔中的dex放在第一個。

Hook

等啊等,客戶的sdk還是沒有發過來,就一個變量值,讓人望眼欲穿,在用熱修複實作後,成功的列印了,複現問題後,也知道最終是條件3,可以答複了,這個時間,浏覽着客戶的SDK代碼,SDK這個類中的單例讓我眼前一亮。

public abstract class SDK {
    private static SDK instance;
    public SDK() {
    }

    public static SDK getInstance() {
        if (instance == null) {
			//這裡可以作為hook點
            instance = new SDKImpl();
        }

        return instance;
    }
}
           

符合hook的适用場景,我開始嘗試如下:

建立代理類

由于SDK不是接口類型,我們使用靜态代理
public class SDKImplProxy extends SDK {

	
	/*其他方法省略,一律拷貝*/

	//隻修改get方法,把 Logger.t,全部換成沒有開關限制的 Logger.d
	public Info getXXX() {
            /*此處省略*/
            if (條件1) {
                Logger.d( TAG, "ERROR 1 !!");
                return null;
            }       
			/*後面全部替換成Logger.d*/
		}
}
           

進行反射,替換

Class<?> buspasssdkClass = Class.forName("com.xxx.SDK");
        Field mInstanceField = sdkClass.getDeclaredField("instance");
        mInstanceField.setAccessible(true);
        SDKImplProxy proxy = new SDKImplProxy();
        mInstanceField.set(null,proxy);
           
一定要在SDK.getInstance() 之前完成hook

重新開機,運作成功拿到日志。

總結

有時候遇到問題,第一時間可能想不到最适合的方法,在技術上無法立馬得到改進,或者要去查資料研究的時候,首先還得學習如何溝通,給自己創造時間或空間,去思考,驗證解決方案。