关于InMemoryDexClassLoader方法,首先要知道可以一次性加载多个dex文件

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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
import android.content.Context;
import dalvik.system.InMemoryDexClassLoader;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;

public class MultiDexLoader {
private Context context;
private InMemoryDexClassLoader dexClassLoader;

public MultiDexLoader(Context context) {
this.context = context;
}

public void loadDex() {
try {
// 读取多个 DEX 文件
String[] dexFiles = {"dex1.dex", "dex2.dex"};
List<ByteBuffer> dexBuffersList = new ArrayList<>();

for (String dexFile : dexFiles) {
InputStream inputStream = context.getAssets().open(dexFile);
byte[] dexBytes = new byte[inputStream.available()];
inputStream.read(dexBytes);
inputStream.close();
dexBuffersList.add(ByteBuffer.wrap(dexBytes));
}

// 转换为数组
ByteBuffer[] dexBuffers = dexBuffersList.toArray(new ByteBuffer[0]);

// 创建 InMemoryDexClassLoader,加载多个 DEX
dexClassLoader = new InMemoryDexClassLoader(dexBuffers, null, context.getClassLoader());

} catch (Exception e) {
e.printStackTrace();
}
}

public void dynamicInvoke() {
try {
if (dexClassLoader == null) {
loadDex();
}

// 加载第一个 DEX 里的类
Class<?> dynamicClass1 = dexClassLoader.loadClass("com.example.dex1.DynamicClass1");
Object obj1 = dynamicClass1.newInstance();
dynamicClass1.getDeclaredMethod("sayHello").invoke(obj1);

// 加载第二个 DEX 里的类
Class<?> dynamicClass2 = dexClassLoader.loadClass("com.example.dex2.DynamicClass2");
Object obj2 = dynamicClass2.newInstance();
dynamicClass2.getDeclaredMethod("sayHello").invoke(obj2);

} catch (Exception e) {
e.printStackTrace();
}
}
}

针对Android 10.0.0_r47版本的源码

libcore/dalvik/src/main/java/dalvik/system/InMemoryDexClassLoader.java类如下

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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package dalvik.system;

import libcore.util.NonNull;
import libcore.util.Nullable;
import java.nio.ByteBuffer;

/**
* A {@link ClassLoader} implementation that loads classes from a
* buffer containing a DEX file. This can be used to execute code that
* has not been written to the local file system.
*/
public final class InMemoryDexClassLoader extends BaseDexClassLoader {
/**
* Create an in-memory DEX class loader with the given dex buffers.
*
* @param dexBuffers array of buffers containing DEX files between
* <tt>buffer.position()</tt> and <tt>buffer.limit()</tt>.
* @param librarySearchPath the list of directories containing native
* libraries, delimited by {@code File.pathSeparator}; may be {@code null}
* @param parent the parent class loader for delegation.
*/
public InMemoryDexClassLoader(@NonNull ByteBuffer @NonNull [] dexBuffers,
@Nullable String librarySearchPath, @Nullable ClassLoader parent) {
// 都是调用的这个构造函数
// dexBuffers:一个 ByteBuffer 数组,每个元素都是一个 DEX 文件的数据。
// librarySearchPath:可选的 .so 库搜索路径,通常传 null
// parent是父类加载器
super(dexBuffers, librarySearchPath, parent);
}

/**
* Create an in-memory DEX class loader with the given dex buffers.
*
* @param dexBuffers array of buffers containing DEX files between
* <tt>buffer.position()</tt> and <tt>buffer.limit()</tt>.
* @param parent the parent class loader for delegation.
*/
public InMemoryDexClassLoader(@NonNull ByteBuffer @NonNull [] dexBuffers,
@Nullable ClassLoader parent) {
// 再次调用自身的构造函数,最终调用父类加载器
this(dexBuffers, null, parent);
}

/**
* Creates a new in-memory DEX class loader.
*
* @param dexBuffer buffer containing DEX file contents between
* <tt>buffer.position()</tt> and <tt>buffer.limit()</tt>.
* @param parent the parent class loader for delegation.
*/
public InMemoryDexClassLoader(@NonNull ByteBuffer dexBuffer, @Nullable ClassLoader parent) {
// dexBuffer其实就是dex文件,这里转成了字节
// 调用自身的构造函数,就是上面的那个
this(new ByteBuffer[] { dexBuffer }, parent);
}
}

有三个构造函数,分析一下就可以看出来,最终调用的都是public InMemoryDexClassLoader(@NonNull ByteBuffer @NonNull [] dexBuffers,@Nullable String librarySearchPath, @Nullable ClassLoader parent) 这个构造函数,在这个函数中调用父类BaseDexClassLoader的构造函数

现在再看父类BaseDexClassLoader的该构造函数super(dexBuffers, librarySearchPath, parent);

1
2
3
4
5
6
public BaseDexClassLoader(ByteBuffer[] dexFiles, String librarySearchPath, ClassLoader parent) {
super(parent);
this.sharedLibraryLoaders = null;
this.pathList = new DexPathList(this, librarySearchPath);
this.pathList.initByteBufferDexPath(dexFiles);
}

可以看到super(parent);这里又调用了父类的函数,BaseDexClassLoader 的父类是 ClassLoader

继续跟进到ClassLoader源码libcore/ojluni/src/main/java/java/lang/ClassLoader.java,找到里面该方法在第270行

1
2
3
protected ClassLoader(ClassLoader parent) {
this(checkCreateClassLoader(), parent);
}

调用了自身的两个参数的构造函数,该函数在第247行,如下

1
2
3
private ClassLoader(Void unused, ClassLoader parent) {
this.parent = parent;
}

这里就是设置了一下parent属性,其实就是在设置双亲委派

继续看this.pathList.initByteBufferDexPath(dexFiles);这一句,进入libcore/dalvik/src/main/java/dalvik/system/DexPathList.java,代码在第254行

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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
/**
* For InMemoryDexClassLoader. Initializes {@code dexElements} with dex files
* loaded from {@code dexFiles} buffers.
*
* @param dexFiles ByteBuffers containing raw dex data. Apks are not supported.
*/
/* package */ void initByteBufferDexPath(ByteBuffer[] dexFiles) {
// 这三个逻辑不用管,一般进不到这里面去
if (dexFiles == null) {
throw new NullPointerException("dexFiles == null");
}
if (Arrays.stream(dexFiles).anyMatch(v -> v == null)) {
throw new NullPointerException("dexFiles contains a null Buffer!");
}
if (dexElements != null || dexElementsSuppressedExceptions != null) {
throw new IllegalStateException("Should only be called once");
}

final List<IOException> suppressedExceptions = new ArrayList<IOException>();

try {
Element[] null_elements = null;
// 得到DexFile对象
DexFile dex = new DexFile(dexFiles, definingContext, null_elements);
// Capture class loader context from *before* `dexElements` is set (see comment below).
String classLoaderContext = dex.isBackedByOatFile()
? null : DexFile.getClassLoaderContext(definingContext, null_elements);
// 把得到的DexFile对象转成dexElements
dexElements = new Element[] { new Element(dex) };
// Spawn background thread to verify all classes and cache verification results.
// Must be called *after* `dexElements` has been initialized for ART to find
// its classes (the field is hardcoded in ART and dex files iterated over in
// the order of the array), but with class loader context from *before*
// `dexElements` was set because that is what it will be compared against next
// time the same bytecode is loaded.
// We only spawn the background thread if the bytecode is not backed by an oat
// file, i.e. this is the first time this bytecode is being loaded and/or
// verification results have not been cached yet. Skip spawning the thread on
// all subsequent loads of the same bytecode in the same class loader context.
if (classLoaderContext != null) {
dex.verifyInBackground(definingContext, classLoaderContext);
}
} catch (IOException suppressed) {
System.logE("Unable to load dex files", suppressed);
suppressedExceptions.add(suppressed);
dexElements = new Element[0];
}

if (suppressedExceptions.size() > 0) {
dexElementsSuppressedExceptions = suppressedExceptions.toArray(
new IOException[suppressedExceptions.size()]);
}
}

在上述代码中,dex文件加载之后其实就存放在DexPathListdexElements属性中

再分析这行代码DexFile dex = new DexFile(dexFiles, definingContext, null_elements);,DexFile位于libcore/dalvik/src/main/java/dalvik/system/DexFile.java ,调用的函数在第113行

1
2
3
4
5
6
7
DexFile(ByteBuffer[] bufs, ClassLoader loader, DexPathList.Element[] elements)
throws IOException {
// mCookie是DexFile类的属性
mCookie = openInMemoryDexFiles(bufs, loader, elements);
mInternalCookie = mCookie;
mFileName = null;
}

mCookie其实是dex文件对象的一个指针,通过mCookie可以找到dex在内存的位置

跟进openInMemoryDexFiles方法,位于DexFile.java类的第374行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
private static Object openInMemoryDexFiles(ByteBuffer[] bufs, ClassLoader loader,
DexPathList.Element[] elements) throws IOException {
// Preprocess the ByteBuffers for openInMemoryDexFilesNative. We extract
// the backing array (non-direct buffers only) and start/end positions
// so that the native method does not have to call Java methods anymore.
byte[][] arrays = new byte[bufs.length][];
int[] starts = new int[bufs.length];
int[] ends = new int[bufs.length];
for (int i = 0; i < bufs.length; ++i) {
// 把bufs给转换到arrays里
arrays[i] = bufs[i].isDirect() ? null : bufs[i].array();
starts[i] = bufs[i].position();
ends[i] = bufs[i].limit();
}
// bufs是dex文件的内容,byteBuffer[]形式
// arrays也是dex文件的内容,其实是jByteArray类型,这个在后面c代码里可以看到
return openInMemoryDexFilesNative(bufs, arrays, starts, ends, loader, elements);
}

跟进openInMemoryDexFilesNative,该方法定义在DexFile.java的第390行

1
2
private static native Object openInMemoryDexFilesNative(ByteBuffer[] bufs, byte[][] arrays,
int[] starts, int[] ends, ClassLoader loader, DexPathList.Element[] elements);

这个方法是native函数,实现在C中,c代码位于art/runtime/native/dalvik_system_DexFile.cc,函数在第240行

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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
static jobject DexFile_openInMemoryDexFilesNative(JNIEnv* env,
jclass,
jobjectArray buffers,
jobjectArray arrays,
jintArray jstarts,
jintArray jends,
jobject class_loader,
jobjectArray dex_elements) {
jsize buffers_length = env->GetArrayLength(buffers);
CHECK_EQ(buffers_length, env->GetArrayLength(arrays));
CHECK_EQ(buffers_length, env->GetArrayLength(jstarts));
CHECK_EQ(buffers_length, env->GetArrayLength(jends));

ScopedIntArrayAccessor starts(env, jstarts);
ScopedIntArrayAccessor ends(env, jends);

// Allocate memory for dex files and copy data from ByteBuffers.
std::vector<MemMap> dex_mem_maps;
dex_mem_maps.reserve(buffers_length);
// 循环处理buffer中的成员
for (jsize i = 0; i < buffers_length; ++i) {
// 可以理解成buffer和array存的都是dex文件的内容
jobject buffer = env->GetObjectArrayElement(buffers, i);
jbyteArray array = reinterpret_cast<jbyteArray>(env->GetObjectArrayElement(arrays, i));
jint start = starts.Get(i);
jint end = ends.Get(i);
// 申请内存空间,用于存放加载的dex文件
MemMap dex_data = AllocateDexMemoryMap(env, start, end);
if (!dex_data.IsValid()) {
DCHECK(Thread::Current()->IsExceptionPending());
return nullptr;
}
// 如果array没有加载成功,那么就从buffer中取数据
if (array == nullptr) {
// Direct ByteBuffer
uint8_t* base_address = reinterpret_cast<uint8_t*>(env->GetDirectBufferAddress(buffer));
if (base_address == nullptr) {
ScopedObjectAccess soa(env);
ThrowWrappedIOException("dexFileBuffer not direct");
return nullptr;
}
size_t length = static_cast<size_t>(end - start);
// 把dex_data填充成dex的数据
memcpy(dex_data.Begin(), base_address + start, length);
} else {
// ByteBuffer backed by a byte array
// 如果array加载成功,就直接把array的地址赋值给dex_data
jbyte* destination = reinterpret_cast<jbyte*>(dex_data.Begin());
env->GetByteArrayRegion(array, start, end - start, destination);
}
//dex_mem_maps存储的就是dex数据
dex_mem_maps.push_back(std::move(dex_data));
}

// Hand MemMaps over to OatFileManager to open the dex files and potentially
// create a backing OatFile instance from an anonymous vdex.
std::vector<std::string> error_msgs;
const OatFile* oat_file = nullptr;
std::vector<std::unique_ptr<const DexFile>> dex_files =
Runtime::Current()->GetOatFileManager().OpenDexFilesFromOat(std::move(dex_mem_maps),
class_loader,
dex_elements,
/*out*/ &oat_file,
/*out*/ &error_msgs);
// 当OpenDexFilesFromOat调用完成之后,实际上dex文件已经加载完毕了
// CreateCookieFromOatFileManagerResult会返回mCookie
return CreateCookieFromOatFileManagerResult(env, dex_files, oat_file, error_msgs);
}

跟进CreateCookieFromOatFileManagerResult方法,位于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* 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;
}
// 其实关键就在这一句
// 最终调用ConvertDexFilesToJavaArray方法把dex_files处理成了jlongArray
// 返回jlongArray类型
jlongArray array = ConvertDexFilesToJavaArray(env, oat_file, dex_files);
if (array == nullptr) {
ScopedObjectAccess soa(env);
for (auto& dex_file : dex_files) {
if (linker->IsDexFileRegistered(soa.Self(), *dex_file)) {
dex_file.release(); // NOLINT
}
}
}
return array;
}

跟进ConvertDexFilesToJavaArray方法,,位于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
33
static jlongArray ConvertDexFilesToJavaArray(JNIEnv* env,
const OatFile* oat_file,
std::vector<std::unique_ptr<const DexFile>>& vec) {
// Add one for the oat file.
// 参数vec就是dex文件的内容这里赋值给了long_array
jlongArray long_array = env->NewLongArray(static_cast<jsize>(kDexFileIndexStart + vec.size()));
if (env->ExceptionCheck() == JNI_TRUE) {
return nullptr;
}
// 把long_array转换成long_data
jboolean is_long_data_copied;
jlong* long_data = env->GetLongArrayElements(long_array, &is_long_data_copied);
if (env->ExceptionCheck() == JNI_TRUE) {
return nullptr;
}
// 给long_data赋值,让long_data存储dex文件
long_data[kOatFileIndex] = reinterpret_cast64<jlong>(oat_file);
for (size_t i = 0; i < vec.size(); ++i) {
long_data[kDexFileIndexStart + i] = reinterpret_cast64<jlong>(vec[i].get());
}

env->ReleaseLongArrayElements(long_array, long_data, 0);
if (env->ExceptionCheck() == JNI_TRUE) {
return nullptr;
}

// Now release all the unique_ptrs.
for (auto& dex_file : vec) {
dex_file.release(); // NOLINT
}
// 最终返回jlongArray类型
return long_array;
}

mCookie其实就是java的long类型的array数组,数组的每个成员是dex文件的指针

把java的long类型的array数组转换回dex文件的函数如下

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
static bool ConvertJavaArrayToDexFiles(
JNIEnv* env,
jobject arrayObject,
/*out*/ std::vector<const DexFile*>& dex_files, // 输出dex_files,也就是dex文件
/*out*/ const OatFile*& oat_file) {
// arrayObject可以理解为mCookie
jarray array = reinterpret_cast<jarray>(arrayObject);

jsize array_size = env->GetArrayLength(array);
if (env->ExceptionCheck() == JNI_TRUE) {
return false;
}

// TODO: Optimize. On 32bit we can use an int array.
jboolean is_long_data_copied;
jlong* long_data = env->GetLongArrayElements(reinterpret_cast<jlongArray>(array),
&is_long_data_copied);
if (env->ExceptionCheck() == JNI_TRUE) {
return false;
}

oat_file = reinterpret_cast64<const OatFile*>(long_data[kOatFileIndex]);
dex_files.reserve(array_size - 1);
for (jsize i = kDexFileIndexStart; i < array_size; ++i) {
// 把每个array数组中的成员都给转成DexFile类型的指针
dex_files.push_back(reinterpret_cast64<const DexFile*>(long_data[i]));
}

env->ReleaseLongArrayElements(reinterpret_cast<jlongArray>(array), long_data, JNI_ABORT);
return env->ExceptionCheck() != JNI_TRUE;
}

最后再看一下DexFile_openInMemoryDexFilesNativeOpenDexFilesFromOat方法

1
2
3
4
5
6
std::vector<std::unique_ptr<const DexFile>> dex_files =
Runtime::Current()->GetOatFileManager().OpenDexFilesFromOat(std::move(dex_mem_maps),
class_loader,
dex_elements,
/*out*/ &oat_file,
/*out*/ &error_msgs);

该方法位于art/runtime/oat_file_manager.cc的第678行

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
std::vector<std::unique_ptr<const DexFile>> OatFileManager::OpenDexFilesFromOat(
std::vector<MemMap>&& dex_mem_maps, // dex_mem_maps是dex文件
jobject class_loader,
jobjectArray dex_elements,
const OatFile** out_oat_file,
std::vector<std::string>* error_msgs) {
std::vector<std::unique_ptr<const DexFile>> dex_files = OpenDexFilesFromOat_Impl(
std::move(dex_mem_maps),
class_loader,
dex_elements,
out_oat_file,
error_msgs);

if (error_msgs->empty()) {
// Remove write permission from DexFile pages. We do this at the end because
// OatFile assigns OatDexFile pointer in the DexFile objects.
for (std::unique_ptr<const DexFile>& dex_file : dex_files) {
if (!dex_file->DisableWrite()) {
error_msgs->push_back("Failed to make dex file " + dex_file->GetLocation() + " read-only");
}
}
}

if (!error_msgs->empty()) {
return std::vector<std::unique_ptr<const DexFile>>();
}

return dex_files;
}

进入OpenDexFilesFromOat_Impl方法,该方法位于art/runtime/oat_file_manager.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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
std::vector<std::unique_ptr<const DexFile>> OatFileManager::OpenDexFilesFromOat_Impl(
std::vector<MemMap>&& dex_mem_maps,
jobject class_loader,
jobjectArray dex_elements,
const OatFile** out_oat_file,
std::vector<std::string>* error_msgs) {
ScopedTrace trace(__FUNCTION__);
std::string error_msg;
DCHECK(error_msgs != nullptr);

// Extract dex file headers from `dex_mem_maps`.
const std::vector<const DexFile::Header*> dex_headers = GetDexFileHeaders(dex_mem_maps);

// Determine dex/vdex locations and the combined location checksum.
uint32_t location_checksum;
std::string dex_location;
std::string vdex_path;
bool has_vdex = OatFileAssistant::AnonymousDexVdexLocation(dex_headers,
kRuntimeISA,
&location_checksum,
&dex_location,
&vdex_path);

// Attempt to open an existing vdex and check dex file checksums match.
std::unique_ptr<VdexFile> vdex_file = nullptr;
if (has_vdex && OS::FileExists(vdex_path.c_str())) {
vdex_file = VdexFile::Open(vdex_path,
/* writable= */ false,
/* low_4gb= */ false,
/* unquicken= */ false,
&error_msg);
if (vdex_file == nullptr) {
LOG(WARNING) << "Failed to open vdex " << vdex_path << ": " << error_msg;
} else if (!vdex_file->MatchesDexFileChecksums(dex_headers)) {
LOG(WARNING) << "Failed to open vdex " << vdex_path << ": dex file checksum mismatch";
vdex_file.reset(nullptr);
}
}
// Load dex files. Skip structural dex file verification if vdex was found
// and dex checksums matched.
std::vector<std::unique_ptr<const DexFile>> dex_files;
// 调用dex_file函数加载dex文件
for (size_t i = 0; i < dex_mem_maps.size(); ++i) {
static constexpr bool kVerifyChecksum = true;
const ArtDexFileLoader dex_file_loader;
std::unique_ptr<const DexFile> dex_file(dex_file_loader.Open(
DexFileLoader::GetMultiDexLocation(i, dex_location.c_str()),
location_checksum,
std::move(dex_mem_maps[i]),
/* verify= */ (vdex_file == nullptr) && Runtime::Current()->IsVerificationEnabled(),
kVerifyChecksum,
&error_msg));
if (dex_file != nullptr) {
dex::tracking::RegisterDexFile(dex_file.get()); // Register for tracking.
dex_files.push_back(std::move(dex_file));
} else {
error_msgs->push_back("Failed to open dex files from memory: " + error_msg);
}
}

// Check if we should proceed to creating an OatFile instance backed by the vdex.
// We need: (a) an existing vdex, (b) class loader (can be null if invoked via reflection),
// and (c) no errors during dex file loading.
if (vdex_file == nullptr || class_loader == nullptr || !error_msgs->empty()) {
return dex_files;
}

// Attempt to create a class loader context, check OpenDexFiles succeeds (prerequisite
// for using the context later).
std::unique_ptr<ClassLoaderContext> context = ClassLoaderContext::CreateContextForClassLoader(
class_loader,
dex_elements);
if (context == nullptr) {
LOG(ERROR) << "Could not create class loader context for " << vdex_path;
return dex_files;
}
DCHECK(context->OpenDexFiles(kRuntimeISA, ""))
<< "Context created from already opened dex files should not attempt to open again";

// Check that we can use the vdex against this boot class path and in this class loader context.
// Note 1: We do not need a class loader collision check because there is no compiled code.
// Note 2: If these checks fail, we cannot fast-verify because the vdex does not contain
// full VerifierDeps.
if (!vdex_file->MatchesBootClassPathChecksums() ||
!vdex_file->MatchesClassLoaderContext(*context.get())) {
return dex_files;
}

// Initialize an OatFile instance backed by the loaded vdex.
std::unique_ptr<OatFile> oat_file(OatFile::OpenFromVdex(MakeNonOwningPointerVector(dex_files),
std::move(vdex_file),
dex_location));
DCHECK(oat_file != nullptr);
VLOG(class_linker) << "Registering " << oat_file->GetLocation();
*out_oat_file = RegisterOatFile(std::move(oat_file));
return dex_files;
}

重点看加载dex文件的位置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
for (size_t i = 0; i < dex_mem_maps.size(); ++i) {
static constexpr bool kVerifyChecksum = true;
const ArtDexFileLoader dex_file_loader;
std::unique_ptr<const DexFile> dex_file(dex_file_loader.Open(
DexFileLoader::GetMultiDexLocation(i, dex_location.c_str()),
location_checksum,
std::move(dex_mem_maps[i]),
/* verify= */ (vdex_file == nullptr) && Runtime::Current()->IsVerificationEnabled(),
kVerifyChecksum,
&error_msg));
if (dex_file != nullptr) {
dex::tracking::RegisterDexFile(dex_file.get()); // Register for tracking.
// 最终函数返回的就是dex_files
dex_files.push_back(std::move(dex_file));
} else {
error_msgs->push_back("Failed to open dex files from memory: " + error_msg);
}
}

std::unique_ptr<const DexFile> dex_file 语法就是创建了一个DexFile对象,调用的是DexFile的构造函数,赋值给了dex_file

看一下dex_file_loader.Open函数,位于art/libdexfile/dex/art_dex_file_loader.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
33
34
35
36
37
38
std::unique_ptr<const DexFile> ArtDexFileLoader::Open(const std::string& location,
uint32_t location_checksum,
MemMap&& map,
bool verify,
bool verify_checksum,
std::string* error_msg) const {
ScopedTrace trace(std::string("Open dex file from mapped-memory ") + location);
CHECK(map.IsValid());

size_t size = map.Size();
if (size < sizeof(DexFile::Header)) {
*error_msg = StringPrintf(
"DexFile: failed to open dex file '%s' that is too short to have a header",
location.c_str());
return nullptr;
}

uint8_t* begin = map.Begin();
std::unique_ptr<DexFile> dex_file = OpenCommon(begin,
size,
/*data_base=*/ nullptr,
/*data_size=*/ 0u,
location,
location_checksum,
kNoOatDexFile,
verify,
verify_checksum,
error_msg,
std::make_unique<MemMapContainer>(std::move(map)),
/*verify_result=*/ nullptr);
// Opening CompactDex is only supported from vdex files.
if (dex_file != nullptr && dex_file->IsCompactDexFile()) {
*error_msg = StringPrintf("Opening CompactDex file '%s' is only supported from vdex files",
location.c_str());
return nullptr;
}
return dex_file;
}

这里的Open函数其实也是一个脱壳点(对于某些加固来讲,有的加固可能不走这个函数),为什么这么讲?传入的参数是MemMap&& mapmap就是dex文件,这个参数在函数中经过.Size().Begin()等一系列处理会还原出dex文件,如果hook Open函数,就可以还原出dex文件,同样的,Open函数中的OpenCommon函数也是一个脱壳点,因为参数中也有dex文件的起始和大小

进入OpenCommon函数,位于art/libdexfile/dex/dex_file_loader.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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
std::unique_ptr<DexFile> DexFileLoader::OpenCommon(const uint8_t* base,
size_t size,
const uint8_t* data_base,
size_t data_size,
const std::string& location,
uint32_t location_checksum,
const OatDexFile* oat_dex_file,
bool verify,
bool verify_checksum,
std::string* error_msg,
std::unique_ptr<DexFileContainer> container,
VerifyResult* verify_result) {
if (verify_result != nullptr) {
*verify_result = VerifyResult::kVerifyNotAttempted;
}
std::unique_ptr<DexFile> dex_file;
if (size >= sizeof(StandardDexFile::Header) && StandardDexFile::IsMagicValid(base)) {
if (data_size != 0) {
CHECK_EQ(base, data_base) << "Unsupported for standard dex";
}
// 调用StandardDexFile的构造函数,传入了base和size
dex_file.reset(new StandardDexFile(base,
size,
location,
location_checksum,
oat_dex_file,
std::move(container)));
} else if (size >= sizeof(CompactDexFile::Header) && CompactDexFile::IsMagicValid(base)) {
if (data_base == nullptr) {
// TODO: Is there a clean way to support both an explicit data section and reading the one
// from the header.
CHECK_EQ(data_size, 0u);
const CompactDexFile::Header* const header = CompactDexFile::Header::At(base);
data_base = base + header->data_off_;
data_size = header->data_size_;
}
// 调用CompactDexFile的构造函数,传入了base和size
dex_file.reset(new CompactDexFile(base,
size,
data_base,
data_size,
location,
location_checksum,
oat_dex_file,
std::move(container)));
// Disable verification for CompactDex input.
verify = false;
} else {
*error_msg = "Invalid or truncated dex file";
}
if (dex_file == nullptr) {
*error_msg = StringPrintf("Failed to open dex file '%s' from memory: %s", location.c_str(),
error_msg->c_str());
return nullptr;
}
if (!dex_file->Init(error_msg)) {
dex_file.reset();
return nullptr;
}
if (verify && !DexFileVerifier::Verify(dex_file.get(),
dex_file->Begin(),
dex_file->Size(),
location.c_str(),
verify_checksum,
error_msg)) {
if (verify_result != nullptr) {
*verify_result = VerifyResult::kVerifyFailed;
}
return nullptr;
}
if (verify_result != nullptr) {
*verify_result = VerifyResult::kVerifySucceeded;
}
return dex_file;
}

重点在CompactDexFileStandardDexFile两个类的构造函数,先看StandardDexFile类,位于art/libdexfile/dex/standard_dex_file.h,构造函数如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
private:
StandardDexFile(const uint8_t* base,
size_t size,
const std::string& location,
uint32_t location_checksum,
const OatDexFile* oat_dex_file,
std::unique_ptr<DexFileContainer> container)
: DexFile(base,
size,
/*data_begin*/ base,
/*data_size*/ size,
location,
location_checksum,
oat_dex_file,
std::move(container),
/*is_compact_dex*/ false) {}

上面的构造函数实际上就是调用了DexFile类的构造函数去实例化对象,同样的CompactDexFile类(art/libdexfile/dex/compact_dex_file.cc)的构造函数也是调用了DexFile类的构造函数去实例化对象,这里不再贴代码了

然后跟进DexFile类的构造函数,该类位于art/libdexfile/dex/dex_file.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
33
34
35
36
37
38
39
40
DexFile::DexFile(const uint8_t* base,
size_t size,
const uint8_t* data_begin,
size_t data_size,
const std::string& location,
uint32_t location_checksum,
const OatDexFile* oat_dex_file,
std::unique_ptr<DexFileContainer> container,
bool is_compact_dex)
: begin_(base),
size_(size),
data_begin_(data_begin),
data_size_(data_size),
location_(location),
location_checksum_(location_checksum),
header_(reinterpret_cast<const Header*>(base)),
string_ids_(reinterpret_cast<const StringId*>(base + header_->string_ids_off_)),
type_ids_(reinterpret_cast<const TypeId*>(base + header_->type_ids_off_)),
field_ids_(reinterpret_cast<const FieldId*>(base + header_->field_ids_off_)),
method_ids_(reinterpret_cast<const MethodId*>(base + header_->method_ids_off_)),
proto_ids_(reinterpret_cast<const ProtoId*>(base + header_->proto_ids_off_)),
class_defs_(reinterpret_cast<const ClassDef*>(base + header_->class_defs_off_)),
method_handles_(nullptr),
num_method_handles_(0),
call_site_ids_(nullptr),
num_call_site_ids_(0),
hiddenapi_class_data_(nullptr),
oat_dex_file_(oat_dex_file),
container_(std::move(container)),
is_compact_dex_(is_compact_dex),
hiddenapi_domain_(hiddenapi::Domain::kApplication) {
CHECK(begin_ != nullptr) << GetLocation();
CHECK_GT(size_, 0U) << GetLocation();
// Check base (=header) alignment.
// Must be 4-byte aligned to avoid undefined behavior when accessing
// any of the sections via a pointer.
CHECK_ALIGNED(begin_, alignof(Header));

InitializeSectionsFromMapList();
}

上面的构造函数把传入的base和size通过begin_(base)size_(size)处理,这里的begin_size_都是DexFile类的属性,定义在art/libdexfile/dex/dex_file.h

1
2
3
4
5
6
7
8
// The base address of the memory mapping.
const uint8_t* const begin_;

// The size of the underlying memory allocation in bytes.
const size_t size_;

// The base address of the data section (same as Begin() for standard dex).
const uint8_t* const data_begin_;

所以可以根据DexFile对象来得到dex文件的base和size等文件信息来进行脱壳

DexFile中有方法可以获取begin_size_属性的值

1
2
3
4
5
6
7
const uint8_t* Begin() const {
return begin_;
}
...
size_t Size() const {
return size_;
}

得到了basesize就能把dex文件保存下来

整体脱壳的本质就是找到DexFile对象,在一些脱壳机中就是把脱壳点选择在OpenCommonDexFile