env->ReleaseStringUTFChars(methodName_, methodName);
return ret;
}
6. binggo,使用smali api,直接解碼
/**
* 將指定loader的smali全部dump到硬盤,請異步執行該函數
*
* @param loader loader
*/
public static void dissembleAllDex(Object loader) {
Class loaderClass = resolveLoaderClass(loader);
DexBackedDexFile memoryMethodDexFile = createMemoryDexFile(loaderClass);
File dumpDir = resolveDumpDir(memoryMethodDexFile);
DexFile reWritedDexFile = rewrite(memoryMethodDexFile, loaderClass.getClassLoader());
XposedBridge.log("脫殼目錄:" + dumpDir.getAbsolutePath());
int jobs = Runtime.getRuntime().availableProcessors();
if (jobs > 6) {
jobs = 6;
}
if (memoryMethodDexFile instanceof DexBackedOdexFile) {
baksmaliOptions.inlineResolver = InlineMethodResolver
.createInlineMethodResolver(((DexBackedOdexFile) memoryMethodDexFile).getOdexVersion());
}
Log.i("weijia", "開始進行脫殼");
if (Baksmali.disassembleDexFile(reWritedDexFile, dumpDir, jobs, baksmaliOptions)) {
Log.i("weijia", "脫殼完成,但是存在錯誤");
} else {
Log.i("weijia", "脫殼成功,請在" + dumpDir + "中查看smali文件");
}
Toast.makeText(SharedObject.context, "脫殼完成,請在" + dumpDir + "中查看smali文件", Toast.LENGTH_LONG).show();
}
7.binggo,使用smali API,輸出dex文件
/**
* 將對應class對應的dex文件的二進制dump出來
*
* @param loader 該dex文件定義的任何一個class,或者class定義的object
* @return 一個byteBuffer,包含了二進制數據
*/
public static ByteBuffer dumpDex(Object loader) {
Class loaderClass = resolveLoaderClass(loader);
DexBackedDexFile memoryDexFile = createMemoryDexFile(loaderClass);
byte[] buf = (byte[]) XposedHelpers.getObjectField(memoryDexFile, "buf");
DexFile dexFile = rewrite(memoryDexFile, loaderClass.getClassLoader());
final DexBuilder builder = new DexBuilder(Opcodes.forApi(apiLevel()));
MemoryDataStore memoryDataStore = new MemoryDataStore(buf.length);
for (ClassDef classDef : dexFile.getClasses()) {
try {
buildClassDef(classDef, builder);
} catch (Exception e) {
Log.i("weijia", "error when define class:" + classDef.getType() + " skipped for rebuild it");
}
}
try {
builder.writeTo(memoryDataStore);
} catch (IOException ioe) {
//the memory writer,no ioe happend
throw new RuntimeException(ioe);
}
return ByteBuffer.wrap(memoryDataStore.getData());
}
7. 如何調用
XposedHelpers.findAndHookConstructor(Activity.class, new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
Object activity = param.thisObject;
if (activity == null) {
return;
}
XposedBridge.log("hook class " + activity.getClass());
if (StringUtils.equalsIgnoreCase(activity.getClass().getName(), "com.xxx.xxx.MainActivity")) {
Dumper.dumpDex(activity);
}
}
});
8. 對了你需要移植dalvik的dexlib模塊代碼,還有/vm/oo包下面的代碼,到你的jni環境下。要不然沒有dex的相關數據結構,也沒有ClassObject的數據結構。
然後,不願意放整個工程,不要求代碼。論文放出來,還是自己實現一遍才能有收穫。
其他:
相比原始DexHunter的優點:
1. 定位dex更加精準,DexHunter用過filename來確定當前dex是不是需要處理,很容易被加殼平臺識別到這個特徵。而且每次都要在放Android系統push一些文件,比較麻煩。我這個,直接通過class對象尋找,想找按個找那個。
2. 併發,可能沒有寫過Linux c語言程序,看那個pthread哪裡看不懂。DexHunter為了防止多次重複處理同一個dex文件,寫了比較複雜的加鎖邏輯。這個放到java層,很簡單實現吧。
3. 多dex,如果一個apk多個dex都需要處理。DexHunter不好處理,因為他輸出就是whole.dex
4. dvmDefineClass失敗,就算正常情況,一個classLoader下面的class,也不是全部可以正常load成功。DexHunter的流程是刪除這些bad class,我還可以儘可能的使用原始dex數據進行解碼。
5. Dalvik_dalvik_system_DexFile_defineClassNative被替換,這可能導致defineClassNative函數不被調用,這樣DexHunter無法攔截到。我用classLoader.loadeClass,java標準接口,這個他永遠沒法替換。
6.其他數據結構加密調整,如果未來不光光是method的數據變化了。由於使用baksmali建立的模型,任何數據結構都很容易重構替換,畢竟是java,抽象封裝很好用。
7.脫殼時機,脫殼輸出控制更加方便。提供的是api,你調用就脫殼不調用就不脫殼。脫殼結果是java的二進制流,你想怎麼編碼、加密、轉儲都很方便。java層的api太多了。
8.可移植性,是一個普通的xposed項目,任何一個有xposed環境的Android機器都可以(當然現在還沒有實現art,不過理論上沒問題)。也不需要編譯系統鏡像。等幾天再把xposed包裝一下,免root脫殼。
當然,我對dex文件格式,並不是非常熟,反正沒有DexHunter玩兒的那麼溜,所以只有多用別人的api了。
關注看雪學院公眾號:ikanxue,更多幹貨等你來拿~
閱讀更多 看雪學院 的文章