样本:很老的x度加固
分析工具:ida、jeb、010、apktool
NativeHook:基于Substrate的Nativehook
Android系统: 4.4
上一篇我们已经初步修复了dex,然后这一篇也是最后一篇,把onCreate001修复。
先看Java层源代码
A.d和A.e 都是native函数,看到这里相信大家都已经有思路了,A.d肯定一定有对DexFile进行修复,把onCreate001的code_item变成正常的。A.e呢?经过一番测试(我分别dump调用A.e和不调用A.e进行对比),发现A.e是对修复的DexFile还原回错误的,为了防止我们直接dump修复后的dex。
那么这个时候,我个人思路,先防止它修复后还原,然后再指定时机进行Dump。注意这里指定时机,就是说一定要等到原程序调用A.d这个native函数之后,再进行dexFile的dump。
先通过nativehook,拦截A,e函数,下面直接上代码
jint new_JNI_OnLoad(JavaVM *vm, void *reserved) { vm->GetEnv((void **) &jniEnv, JNI_VERSION_1_4); //0x35c为RegisterNatives偏移地址 elf_hook_Direct((void *) *(unsigned int *) ((*(unsigned int *) jniEnv) + 0x35C), (void *) &myRegisterNatives, (void **) &oldRegisterNatives); return old_JNI_OnLoad(vm, reserved); }
//修复函数 void new_d(JNIEnv *env, jclass c, jstring js) { log("%s",env->GetStringUTFChars(js, false)); return old_d(env,c,js); } void (*old_e)(JNIEnv *env, jclass c,jstring js); //还原函数 void new_e(JNIEnv *env, jclass c, jstring js) { //这里直接拦截它恢复 // log("%s", "run new e"); // log("%s",env->GetStringUTFChars(js, false)); // return old_e(env,c,js); } void on_native_reg(JNIEnv *env, jclass c, const JNINativeMethod *m, jint n) { for (int i = 0; i < n; i++) { if (strcmp(m[i].name, "d") == 0) { elf_hook_Direct(m[i].fnPtr, (void *) &new_d, (void **) &old_d); } if (strcmp(m[i].name, "e") == 0) { elf_hook_Direct(m[i].fnPtr, (void *) &new_e, (void **) &old_e); } } }
好的上面代码已经拦截了还原函数,那么下面我们就确认时机点了,我选择了Lcom/qsq/qianshengqian/LoginActivity这个时机点,确保SplashActivity已经onCreate了,然后再次dump DexFile,上代码
void *new_dexFindClass(const void *pDexFile, const char *descriptor) { DexFile *dexFile = (DexFile *) pDexFile; char *aim = "Lcom/qsq/qianshengqian/LoginActivity;"; int cmp = strcmp(descriptor, aim); if (dexFile->pHeader->fileSize == 5573376 && a == 0 && cmp) { log("%s","start dump"); a = 1; size_t size = dexFile->pHeader->fileSize; DexClassDef *pDef = dexFile->pClassDefs; // log("class def size is %d", dexFile->pHeader->classDefsSize); int classDefSize = dexFile->pHeader->classDefsSize; log("%d",classDefSize); //2.dex保留classdef。 FILE *pFile = fopen("/data/data/com.qsq.qianshengqian/2.dex", "wb+"); u4 currentLength =dexFile->pHeader->fileSize; for (int i = 0; i < classDefSize; ++i) { if (pDef->classDataOff != 0) { const u1 *start = dexGetClassData(dexFile, pDef); DexClassData *pData = ReadClassData(&start); int len = 0; u4 f_off = currentLength + len; uint8_t *string = EncodeClassData(pData, len); fwrite(string, 1, len, pFile); currentLength = currentLength + len; //重置偏移, if (i<4495) { u4 *def_off = &(pDef->classDataOff); *def_off = f_off; } } //重置annotation u4 *off = &(pDef->annotationsOff); *off = 0; pDef++; } log("%s","write compete"); fclose(pFile); char *file_name = "/data/data/com.qsq.qianshengqian/1.dex"; FILE *dex_file = fopen(file_name, "wb+"); fwrite(dexFile->pHeader, dexFile->pHeader->fileSize, 1, dex_file); fclose(dex_file); } return old_findDex(pDexFile, descriptor); }
同样我们这次dump好两个文件, 一个是修后后的dex,我们后面叫Fix_1.dex,另一个是修复后的class_data段,我们叫Fix_2.dex。然后再次拼接,上010看图。
Dump出来的文件有如下问题
1、header被处理了,膜(摩)数都没了。上面那个我自己加上去的
2、是一个相当不规则的文件,以至于baksmali都没办法转换。
不过我们可以先看看SplashActivity的CodeItem是不是修复了
OK,可以看到确实不一样了,那么下面我们要做的,就是把原dex进行修复(因为新dump出的dex太不规则了)。
这里我们先列一下,以免合并的时候懵逼了。
原dex:1.dex 这个是经过第一次修复后,onCreate001出问题的
原class_data段: 2.dex 这个是第一次dump出的class_data段
修复后的dex: Fix_1.dex
修复后的class_data段:Fix_2.dex
然后我们就用1.dex与Fix_2.dex组合成一个新的dex,再将Fix_1.dex中的code_item覆盖到1.dex的code_item
可以看到已经修复成功了,到此为止 这个样本完成了。然后把样本以及代码资源奉上,谢谢大家的观看。
2020安全开发者峰会(2020 SDC)议题征集 中国.北京 7月!
最后于 15小时前 被GitRoy编辑 ,原因: