要手动调用fupkInvokeMethod
bool DexDumper::fixMethodByDvm(DexSharedData &shared, DexMethod *dexMethod,
ClassDefBuilder* builder, u4 &lastIndex) {
lastIndex = lastIndex + dexMethod->methodIdx;
auto m = builder->getMethodMap(lastIndex);
assert(m != nullptr && "Unable to fix MethodBy Dvm, this should happened");
shared.mCurMethod = dexMethod;
FupkImpl::fupkInvokeMethod(m); /////////////////////////////////////////////////
shared.mCurMethod = nullptr;
return true;
}
/* @F8LEFT
* This method is used to export some data for fupk3 to dump dex file.
* Fupk3 will hook this method and get data from it.
*/
void fupkInvokeMethod(Method* meth) {
// it is no need to init or link class, the code of the method will
// not exec actually, so just ignore it
// anyway, I should make sure this method has code to execute
if (dvmIsMirandaMethod(meth) || dvmIsAbstractMethod(meth)) {
return;
}
dvmInvokeMethod((Object*)0xF88FF88F, meth, NULL, NULL, NULL, true);
}
dvmInvokeMethod又会调用gFupk.ExportMethod
gFupk.ExportMethod在xposed的框架中加载so的过程中被设置为fupk_ExportMethod
也就是说,调用fixMethodByDvm 就会跳转到fupk_ExportMethod(void * thread,Method *method)
接口设计
c语言好像没有接口这种概念,作者整了个接口。
bool fupkExportMethod(Thread* self, const Method* method) {
return false;
}
FupkInterface gFupk = {
NULL, NULL, NULL, NULL,
fupkExportMethod
};
然后在xposed 的so中替换
// 在so中直接替换ExportMethod
auto interface = FupkImpl::gUpkInterface;
if (interface == nullptr) {
FLOGE("Unable to found fupk interface");
return;
}
// Hook all
interface->ExportMethod = fupk_ExportMethod;
dex重组
// 传递参数,然后直接调用
gUpkInterface->reserved0 = &shared;
shared.mCurMethod = dexMethod;
FupkImpl::fupkInvokeMethod(m);
shared.mCurMethod = nullptr;
// 在ExportMethod中直接提取CodeItem数据
bool fupk_ExportMethod(void *thread, Method *method) {
DexSharedData* shared = (DexSharedData*)gUpkInterface->reserved0;
DexMethod* dexMethod = shared->mCurMethod;
u4 ac = (method->accessFlags) & mask;
if (method->insns == nullptr || ac & ACC_NATIVE) {
if (ac & ACC_ABSTRACT) {
ac = ac & ~ACC_NATIVE;
}
dexMethod->accessFlags = ac;
dexMethod->codeOff = 0;
return false;
}
if (ac != dexMethod->accessFlags) {
dexMethod->accessFlags = ac;
}
dexMethod->codeOff = shared->total_point;
DexCode *code = (DexCode*)((const u1*) method->insns - 16);
u1 *item = (u1*) code;
int code_item_len = 0;
if (code->triesSize) {
const u1*handler_data = dexGetCatchHandlerData(code);
const u1 **phandler = (const u1**) &handler_data;
u1 *tail = codeitem_end(phandler);
code_item_len = (int)(tail - item);
} else {
code_item_len = 16 + code->insnsSize * 2;
}
shared->extra.append((char*)item, code_item_len);
shared->total_point += code_item_len;
while(shared->total_point & 3) {
shared->extra.push_back(shared->padding);
shared->total_point++;
}
return true;
}
这样,所有加密的数据都提取出来了,直接进行组合,以加密的方式dump出来。
size_t myfwrite(const void* buffer, size_t size, size_t count, FILE* stream) {
char *tmp = new char[size * count];
mymemcpy(tmp, buffer, size * count);
for (size_t i = 0; i < size * count; ++i) {
tmp[i] ^= encryptKey;
}
size_t rel = fwrite(tmp, size, count, stream);
delete []tmp;
return rel;
}
- dex修复
上面dump下来的dex文件是非标准的,可能存在部分的class数据不合法,并且一些软件不认。所以需要一个写一个修复的server来跳过非法的数据。server主要修改了baksmali与smali的代码,自动跳过无法反编译的类。
- 其他
Android加固发展到现在,经历了好几个大版本改动。最初的是dex整体加固,现在的是VMP加固,中间出了不少非常不错的脱壳机,其中最为经典的有2个:ZjDroid与dexHunter,这两个都是开源的,并且写得非常好,即使是放到今天来看,也具有相当的参考价值,想要学习脱壳的同学们可以拜读一下。另外,FUpk3是运行在dalvik上的,那么要在art下脱壳怎么办呢?道理还是一样的,只是art下会复杂很多, 跑解析的跑编译的都有,修复起来需要记录很多数据,这里由于某些原因就不公开art下的脱壳机了。
- 写在最后
新手想要入门,需要什么?1. 一台谷歌亲儿子(nexus) 2. 一个Ubuntu系统 3. 一套完整的Android源码。平时有事没事可以多看看源码,刷刷机之类的。Android平台与Windows上的不同,到目前为止,Android上系统的优秀的教材实在是少,如果没人手把手地带入门的话,基本上就是在白做功,幸好大部分难点的解决方案答案都可以在源码里面找到,多熟悉一下总会有好处的。