所谓整体脱壳,本质都是获取DexFile对象,包括mCookie脱壳点,mCookie的每一个元素都是DexFile对象的指针

只要能够获取DexFile的地方,都可以尝试脱壳

对于youpk脱壳机,本质也是通过ClassLinker获取到内存中DexFile的对象

https://github.com/Youlor/unpacker/blob/master/android-7.1.2_r33/art/runtime/unpacker/unpacker.cc

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
std::list<const DexFile*> Unpacker::getDexFiles() {
std::list<const DexFile*> dex_files;
Thread* const self = Thread::Current();
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
ReaderMutexLock mu(self, *class_linker->DexLock());
const std::list<ClassLinker::DexCacheData>& dex_caches = class_linker->GetDexCachesData();
for (auto it = dex_caches.begin(); it != dex_caches.end(); ++it) {
ClassLinker::DexCacheData data = *it;
const DexFile* dex_file = data.dex_file;
const std::string& dex_location = dex_file->GetLocation();
if (dex_location.rfind("/system/", 0) == 0) {
continue;
}
dex_files.push_back(dex_file);
}
return dex_files;
}

ClassLinker是什么,参考Android源码里art/runtime/native/dalvik_system_DexFile.cc

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
static jobject CreateCookieFromOatFileManagerResult(
JNIEnv* env,
std::vector<std::unique_ptr<const DexFile>>& dex_files,
const OatFile* oat_file,
const std::vector<std::string>& error_msgs) {
// 获取classLinker
ClassLinker* linker = Runtime::Current()->GetClassLinker();
if (dex_files.empty()) {
ScopedObjectAccess soa(env);
CHECK(!error_msgs.empty());
// The most important message is at the end. So set up nesting by going forward, which will
// wrap the existing exception as a cause for the following one.
auto it = error_msgs.begin();
auto itEnd = error_msgs.end();
for ( ; it != itEnd; ++it) {
ThrowWrappedIOException("%s", it->c_str());
}
return nullptr;
}
// jlongArray就是mCookie,存储DexFile对象指针的数组
jlongArray array = ConvertDexFilesToJavaArray(env, oat_file, dex_files);
if (array == nullptr) {
ScopedObjectAccess soa(env);
// 遍历dex_files数组
for (auto& dex_file : dex_files) {
if (linker->IsDexFileRegistered(soa.Self(), *dex_file)) {
dex_file.release(); // NOLINT
}
}
}
return array;
}

ClassLinker在哪里使用的?可以看到这里有个判断,如果jlongarray为空,会调用linkerIsDexFileRegistered方法

跟进IsDexFileRegistered方法,位于art/runtime/class_linker.cc

1
2
3
4
bool ClassLinker::IsDexFileRegistered(Thread* self, const DexFile& dex_file) {
ReaderMutexLock mu(self, *Locks::dex_lock_);
return DecodeDexCache(self, FindDexCacheDataLocked(dex_file)) != nullptr;
}

跟进FindDexCacheDataLocked方法

1
2
3
4
5
6
7
8
9
10
ClassLinker::DexCacheData ClassLinker::FindDexCacheDataLocked(const DexFile& dex_file) {
// Search assuming unique-ness of dex file.
for (const DexCacheData& data : dex_caches_) {
// Avoid decoding (and read barriers) other unrelated dex caches.
if (data.dex_file == &dex_file) {
return data;
}
}
return DexCacheData();
}

可以看到FindDexCacheDataLocked方法会比较dex_fileDexCacheData,如果相等就返回DexCacheData

DexCacheDataclassLinker类的结构体,结构体中有属性就是DexFile*

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
struct DexCacheData {
// Construct an invalid data object.
DexCacheData()
: weak_root(nullptr),
dex_file(nullptr),
class_table(nullptr) { }

// Check if the data is valid.
bool IsValid() const {
return dex_file != nullptr;
}

// Weak root to the DexCache. Note: Do not decode this unnecessarily or else class unloading may
// not work properly.
jweak weak_root;
// The following field caches the DexCache's field here to avoid unnecessary jweak decode that
// triggers read barriers (and marks them alive unnecessarily and messes with class unloading.)
const DexFile* dex_file;
// Identify the associated class loader's class table. This is used to make sure that
// the Java call to native DexCache.setResolvedType() inserts the resolved type in that
// class table. It is also used to make sure we don't register the same dex cache with
// multiple class loaders.
ClassTable* class_table;
};

所以可以通过获取DexCacheData的方法来获取到DexFile*

class_linker->GetDexCachesData()就是通过调用ClassLinkerGetDexCachesData()方法来获取DexCacheData

1
2
3
4
const std::list<DexCacheData>& GetDexCachesData()
REQUIRES_SHARED(Locks::mutator_lock_, Locks::dex_lock_) {
return dex_caches_;
}