样本:很老的x度加固
分析工具:ida、jeb、010、apktool
NativeHook:基于Substrate的Nativehook
Android系统: 4.4
上一篇我们已经初步修复了dex,然后这一篇也是最后一篇,把onCreate001修复。
修复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
可以看到已经修复成功了,到此为止 这个样本完成了。然后把样本以及代码资源奉上,谢谢大家的观看。
[课程]Android-CTF解题方法汇总!
最后于 2020-2-4 22:05
被GitRoy编辑
,原因: