一、拋出問題
在上一篇 dalvik.system.CloseGuard 介紹了CloseGuard的原理和作用,並在文中提到
APP端可以利用Hook REPORTER 在來實現客製化的上報提示信息
本章通過代碼來學一下怎樣Hook這個REPORTER來定製上報信息功能。
二、案例分析
直接上代碼:
package com.peterzhang.androidhookdemo;
import android.database.CursorWindow;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Toast;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
private volatile static Object sOriginalReporter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
setTitle("HookCloseGuard");
}
private void testAllocate(){
CursorWindow cursorWindow = new CursorWindow("test");
}
public void allocate(View view){
testAllocate();
}
public void hook(View view){
tryHook();
}
private boolean tryHook() {
try {
Class<?> closeGuardCls = Class.forName("dalvik.system.CloseGuard");
Class<?> closeGuardReporterCls = Class.forName("dalvik.system.CloseGuard$Reporter");
Field fieldREPORTER = closeGuardCls.getDeclaredField("REPORTER");
Field fieldENABLED = closeGuardCls.getDeclaredField("ENABLED");
fieldREPORTER.setAccessible(true);
fieldENABLED.setAccessible(true);
sOriginalReporter = fieldREPORTER.get(null);
fieldENABLED.set(null, true);
ClassLoader classLoader = closeGuardReporterCls.getClassLoader();
if (classLoader == null) {
return false;
}
fieldREPORTER.set(null, Proxy.newProxyInstance(classLoader,
new Class<?>[]{closeGuardReporterCls},
new IOCloseLeakDetector(sOriginalReporter)));
fieldREPORTER.setAccessible(false);
return true;
} catch (Throwable e) {
Log.e(TAG, "tryHook exp=%s", e);
}
return false;
}
class IOCloseLeakDetector implements InvocationHandler {
private final Object originalReporter;
public IOCloseLeakDetector(Object originalReporter) {
this.originalReporter = originalReporter;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (method.getName().equals("report")) {
Log.d(TAG,"invoke hook method");
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(MainActivity.this,"invoke hook method",Toast.LENGTH_LONG).show();
}
});
return null;
}
return method.invoke(originalReporter, args);
}
}
@Override
protected void onDestroy() {
Log.d(TAG,"onDestroy");
super.onDestroy();
}
}
說明:
- 界面上兩個按鈕,分別是hook和allocate。hook的作用是通過反射修改App運行環境中CloseGuard實例中的REPORTER對象,allocate是在方法體中創建一個局部變量,類型是CursorWindow,爲什麼用CursorWindow,因爲它的源碼接入了CloseGuard機制。
- 分別點擊hook和allocate,然後點返回按鍵
- 等一會會發生gc,觸發CloseCuard中的warnIfOpen方法,warnIfOpen方法中又會調用REPORTER的report方法,由於我們預先hook了REPORTER,因此這裏會走到IOCloseLeakDetector的invoke方法,這裏是通過動態代理實現了一個REPORTER的代理對象。
- 彈出Toast提示
四、總結
通過案例學習到了:
- 如何通過Hook來定製REPORTER實現自己的上報邏輯
- 進一步瞭解CloseGuard的使用機制