一、什麼是沙箱機制?
沙箱是一個虛拟系統程式,沙箱提供的環境相對于每一個運作的程式的程序空間都是獨立的,程式的運作互不幹擾,而且不會對現有的系統産生影響。
二、什麼是hook?
hook的意思是勾住。比如A發送消息給B,在消息過去之前,可以先把消息勾住,不讓其傳遞,你可以先執行你的操作(或者說先執行你的鈎子函數)。
專業的說法就是:hook技術,能夠改變API執行的結果,将系統的API函數執行重定向。
三、為什麼需要hook?
android系統的沙箱機制使我們不能通過一個程式改變其他程式的某些行為,但是hook技術正好可以解決此類問題。
四、代理是什麼?
舉例生活中,比如我經常需要買一些香港的東西,又不太現實每次自己親自去,隻能找一些代購幫我們去買。那代購就比較坑了,不僅坑你的錢,還可能坑你的貨品,給你帶回了假貨回來。
五、代理分類?
1.靜态代理
2.動态代理
六、反射機制?
Java反射機制主要提供了以下功能: 在運作時判斷任意一個對象所屬的類;在運作時構造任意一個類的對象;在運作時判斷任意一個類所具有的成員變量和方法;在運作時調用任意一個對象的方法;生成動态代理。
在運作狀态中,對于任意一個類,都能夠擷取到這個類的所有屬性和方法,對于任意一個對象,都能夠調用它的任意一個方法和屬性(包括私有的方法和屬性),這種動态擷取的資訊以及動态調用對象的方法的功能就稱為java語言的反射機制。通俗點講,通過反射,該類對我們來說是完全透明的,想要擷取任何東西都可以。
七、hook技術分類?
分類1 —— API hook:
分類2 —— 系統消息hook:
八、小實踐
以下我們對API hook舉一個詳細的例子:
步驟
1.尋找hook點。一般是靜态變量或單例對象,盡量hook public的對象和方法,減輕因非public而導緻的各版本不一緻帶來的工作。
2.選擇合适的代理方式。動态代理适用于接口。
3.偷梁換柱。使用反射機制用代理對象替換掉被hook的對象
舉例 hook startActivity:
1.尋找hook點。
》》對于Context.startActivity,由于Context的實作類為ContextImpl,是以直接分析ContextImpl類的startActivity()。
@Override
public void startActivity(Intent intent, Bundle options) {
warnIfCallingFromSystemProcess();
if ((intent.getFlags()&Intent.FLAG_ACTIVITY_NEW_TASK) == 0
&& options != null && ActivityOptions.fromBundle(options).getLaunchTaskId() == -1) {
throw new AndroidRuntimeException(
"Calling startActivity() from outside of an Activity "
+ " context requires the FLAG_ACTIVITY_NEW_TASK flag."
+ " Is this really what you want?");
}
mMainThread.getInstrumentation().execStartActivity(
getOuterContext(), mMainThread.getApplicationThread(), null,
(Activity) null, intent, -1, options);
}
》》這裡,實際上使用了ActivityThread類的mInstrumentation成員的execStartActivity方法;而ActivityThread 實際上是主線程,因為主線程一個程序隻有一個,是以這裡是一個良好的Hook點。
》》Hook主線程對象:要将這個主線程對象裡面的mInstrumentation替換成修改過的代理對象;要替換主線程對象裡面的字段,得先拿到主線程對象的引用,如何擷取呢? 》》ActivityThread類裡面有一個靜态方法currentActivityThread,通過它可以拿到這個對象類;但ActivityThread是一個隐藏類,需用反射去擷取。
2.選擇合适代理方式
》》拿到currentActivityThread後,要修改它的mInstrumentation字段為修改後的代理對象。是以需要先實作這個代理對象,由于JDK動态代理隻支援接口,而這個Instrumentation是一個類,是以隻能手寫一個靜态代理類,用來覆寫掉原始的方法。
3.偷梁換柱
》》采用反射直接修改即可。
完整代碼如下:
HookUtil
public class HookUtil {
public static void attachContext() throws Exception {
//先擷取到目前的ActivityThread對象
Class<?> activityThreadClass = Class.forName("android.app.ActivityThread");
Method currentActivityThreadMethod = activityThreadClass.getDeclaredMethod("currentActivityThread");
currentActivityThreadMethod.setAccessible(true);
Object currentActivityThread = currentActivityThreadMethod.invoke(null);
// 拿到原始的 mInstrumentation字段
Field mInstrumentationField = activityThreadClass.getDeclaredField("mInstrumentation");
mInstrumentationField.setAccessible(true);
Instrumentation mInstrumentation = (Instrumentation) mInstrumentationField.get(currentActivityThread);
// 建立代理對象
Instrumentation evilInstrumentation = new EvilInstrumentation(mInstrumentation);
//偷梁換柱
mInstrumentationField.set(currentActivityThread, evilInstrumentation);
}
}
EvilInstrumentation
public class EvilInstrumentation extends Instrumentation {
private static final String TAG = "EvilInstrumentation";
Instrumentation mBase;
public EvilInstrumentation(Instrumentation mBase) {
this.mBase = mBase;
}
public ActivityResult execStartActivity(
Context who, IBinder contextThread, IBinder token, Activity target,
Intent intent, int requestCode, Bundle options) {
Log.d(TAG, " Hook之前 列印 execStartActivity");
// 開始調用原始的方法, 調不調用随你,但是不調用的話, 所有的startActivity都失效了.
// 由于這個方法是隐藏的,是以需要使用反射調用;首先找到這個方法
try {
Method execStartActivity = Instrumentation.class.getDeclaredMethod(
"execStartActivity",
Context.class, IBinder.class, IBinder.class, Activity.class,
Intent.class, int.class, Bundle.class);
execStartActivity.setAccessible(true);
return (ActivityResult) execStartActivity.invoke(mBase, who,
contextThread, token, target, intent, requestCode, options);
} catch (Exception e) {
throw new RuntimeException("do not support!");
}
}
}
AActivity
public class AActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Button tv = new Button(this);
tv.setText("Test");
setContentView(tv);
tv.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent i = new Intent(AActivity.this, BActivity.class);
i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK );//必須添加
getApplication().startActivity(i);
}
});
}
@Override
protected void attachBaseContext(Context newBase) {
super.attachBaseContext(newBase);
try {
HookUtil.attachContext();
} catch (Exception e) {
e.printStackTrace();
}
}
}
BActivity
public class BActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
}
運作程式可以看到會列印資訊:Hook之前 列印 execStartActivity
注意:
不添加setFlags(Intent.FLAG_ACTIVITY_NEW_TASK )會報錯噢。
九、總結
hook技術涉及到的知識點主要有反射、代理及android的一些底層知識,需要花更多的時間去學習和總結才能較好地掌握好hook相關的内容。