ART下脱壳原理

android8.0引入了InMemoryDexClassLoader

Android8.0 Oreo

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
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
98
99
100
101
102
103
104
105
106
107
108
109
libcore/dalvik/src/main/java/dalvik/system/InMemoryDexClassLoader.java
InMemoryDexClassLoader(ByteBuffer dexBuffer, ClassLoader parent)
有两个构造函数,分别用于加载一个或多个dex

libcore/dalvik/src/main/java/dalvik/system/BaseDexClassLoader.java
BaseDexClassLoader(String dexPath, File optimizedDirectory, String librarySearchPath, ClassLoader parent)
InMemoryDexClassLoader的父类

libcore/dalvik/src/main/java/dalvik/system/DexPathList.java
DexPathList(ClassLoader definingContext, ByteBuffer[] dexFiles)

libcore/dalvik/src/main/java/dalvik/system/DexPathList.java
makeInMemoryDexElements(ByteBuffer[] dexFiles,List<IOException>suppressedExceptions)

libcore/dalvik/src/main/java/dalvik/system/DexFile.java
DexFile(ByteBuffer buf)

libcore/dalvik/src/main/java/dalvik/system/DexFile.java
openInMemoryDexFile(ByteBuffer buf)
最后openInMemoryDexFile调用了两个native函数
private static native Object createCookieWithDirectBuffer(ByteBuffer buf, int start, int end);
private static native Object createCookieWithArray(byte[] buf, int start, int end);

art/runtime/native/dalvik_system_DexFile.cc
static jobject DexFile_createCookieWithDirectBuffer(JNIEnv* env,
jclass,
jobject buffer,
jint start,
jint end)
其中包含
memcpy(dex_mem_map->Begin(), base_address, length);
传入的是dex的起始地址和长度,因此这里可以直接进行dump

static jobject DexFile_createCookieWithArray(JNIEnv* env,
jclass,
jbyteArray buffer,
jint start,
jint end)
这两个函数最后都return CreateSingleDexFileCookie(env, std::move(dex_mem_map));

art/runtime/native/dalvik_system_DexFile.cc
static jobject CreateSingleDexFileCookie(JNIEnv* env, std::unique_ptr<MemMap> data)
创建了一个dex_file实例,传入参数CreateDexFile(env, std::move(data)):
std::unique_ptr<const DexFile> dex_file(CreateDexFile(env, std::move(data)));
最后对dex_file实例进行返回,return ConvertDexFilesToJavaArray(env, nullptr, dex_files);

art/runtime/native/dalvik_system_DexFile.cc
static const DexFile* CreateDexFile(JNIEnv* env, std::unique_ptr<MemMap> dex_mem_map)
dex_mem_map,dex在内存文件中的映射地址
调用了DexFile::Open
std::string location = StringPrintf("Anonymous-DexFile@%p-%p",
dex_mem_map->Begin(),
dex_mem_map->End());
std::string error_message;
std::unique_ptr<const DexFile> dex_file(DexFile::Open(location,
0,
std::move(dex_mem_map),
/* verify */ true,
/* verify_location */ true,
&error_message));

art/runtime/dex_file.cc
std::unique_ptr<const DexFile> DexFile::Open(const std::string& location,
uint32_t location_checksum,
std::unique_ptr<MemMap> map,
bool verify,
bool verify_checksum,
std::string* error_msg)
传入了dex加载的路径和映射
调用了
std::unique_ptr<DexFile> dex_file = OpenCommon(map->Begin(),
map->Size(),
location,
location_checksum,
kNoOatDexFile,
verify,
verify_checksum,
error_msg);

art/runtime/dex_file.cc
std::unique_ptr<DexFile> DexFile::OpenCommon(const uint8_t* base,
size_t size,
const std::string& location,
uint32_t location_checksum,
const OatDexFile* oat_dex_file,
bool verify,
bool verify_checksum,
std::string* error_msg,
VerifyResult* verify_result)
传入的参数也包含了加载dex的起始地址
又新建了一个DexFile对象,也包含起始地址和大小,base,size
std::unique_ptr<DexFile> dex_file(new DexFile(base,
size,
location,
location_checksum,
oat_dex_file));

art/runtime/dex_file.cc
DexFile::DexFile(const uint8_t* base,
size_t size,
const std::string& location,
uint32_t location_checksum,
const OatDexFile* oat_dex_file)



也就是说InMemoryDexClassLoader在对内存中的ByteBuffer dexBuffer的dex信息进行加载的流程过程中用到了很多dex信息的起始地址和大小

InMemoryDexClassLoader没有对内存当中的dex信息进行编译生成相应的oat文件

InMemoryDexClassLoader脱壳点

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
static jobject CreateSingleDexFileCookie(JNIEnv* env, std::unique_ptr<MemMap> data)


static const DexFile* CreateDexFile(JNIEnv* env, std::unique_ptr<MemMap> dex_mem_map)


std::unique_ptr<const DexFile> DexFile::Open(const std::string& location,
uint32_t location_checksum,
std::unique_ptr<MemMap> map,
bool verify,
bool verify_checksum,
std::string* error_msg)


std::unique_ptr<DexFile> DexFile::OpenCommon(const uint8_t* base,
size_t size,
const std::string& location,
uint32_t location_checksum,
const OatDexFile* oat_dex_file,
bool verify,
bool verify_checksum,
std::string* error_msg,
VerifyResult* verify_result)


DexFile::DexFile(const uint8_t* base,
size_t size,
const std::string& location,
uint32_t location_checksum,
const OatDexFile* oat_dex_file)

DexClassLoader加载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
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
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
libcore/dalvik/src/main/java/dalvik/system/DexClassLoader.java
public DexClassLoader(String dexPath, String optimizedDirectory, String librarySearchPath, ClassLoader parent)

libcore/dalvik/src/main/java/dalvik/system/BaseDexClassLoader.java
BaseDexClassLoader(String dexPath, File optimizedDirectory, String librarySearchPath, ClassLoader parent)

libcore/dalvik/src/main/java/dalvik/system/DexPathList.java
DexPathList(ClassLoader definingContext, ByteBuffer[] dexFiles)

libcore/dalvik/src/main/java/dalvik/system/DexPathList.java
private static Element[] makeDexElements(List<File> files, File optimizedDirectory,
List<IOException> suppressedExceptions, ClassLoader loader)

private static DexFile loadDexFile(File file, File optimizedDirectory, ClassLoader loader, Element[] elements)
其中return DexFile.loadDex(file.getPath(), optimizedPath, 0, loader, elements);

libcore/dalvik/src/main/java/dalvik/system/DexFile.java
static DexFile loadDex(String sourcePathName, String outputPathName,int flags, ClassLoader loader, DexPathList.Element[] elements)
其中return new DexFile(sourcePathName, outputPathName, flags, loader, elements);

libcore/dalvik/src/main/java/dalvik/system/DexFile.java
DexFile(String fileName, ClassLoader loader, DexPathList.Element[] elements)
其中mCookie = openDexFile(fileName, null, 0, loader, elements);

private static Object openDexFile(String sourceName, String outputName, int flags,
ClassLoader loader, DexPathList.Element[] elements)
其中
return openDexFileNative(new File(sourceName).getAbsolutePath(),
(outputName == null)
? null
: new File(outputName).getAbsolutePath(),
flags,
loader,
elements);

是个native函数
private static native Object openDexFileNative(String sourceName, String outputName, int flags, ClassLoader loader, DexPathList.Element[] elements)

art/runtime/native/dalvik_system_DexFile.cc
static jobject DexFile_openDexFileNative(JNIEnv* env,
jclass,
jstring javaSourceName,
jstring javaOutputName ATTRIBUTE_UNUSED,
jint flags ATTRIBUTE_UNUSED,
jobject class_loader,
jobjectArray dex_elements)
加载路径javaSourceName
DexFile_openDexFileNative其中包含:
const OatFile* oat_file = nullptr;
dex_files = runtime->GetOatFileManager().OpenDexFilesFromOat(sourceName.c_str(),
class_loader,
dex_elements,
/*out*/ &oat_file,
/*out*/ &error_msgs);
首次出现oat
OpenDexFilesFromOat最终会编译生成OAT的关键地方
当我们第一次使用DexClassLoader去动态加载一个解密的dex文件时,还没有编译生成OAT

art/runtime/oat_file_manager.cc
std::vector<std::unique_ptr<const DexFile>> OatFileManager::OpenDexFilesFromOat(
const char* dex_location,
jobject class_loader,
jobjectArray dex_elements,
const OatFile** out_oat_file,
std::vector<std::string>* error_msgs)

其中创建了oat_file_assistant对象
OatFileAssistant oat_file_assistant(dex_location,
kRuntimeISA,
!runtime->IsAotCompiler());
第一次调用时是没有OAT的
if (!oat_file_assistant.IsUpToDate())
这时会执行MakeUpToDate
switch (oat_file_assistant.MakeUpToDate(/*profile_changed*/false, /*out*/ &error_msg))

art/runtime/oat_file_assistant.cc
OatFileAssistant::MakeUpToDate(bool profile_changed, std::string* error_msg)
最后返回return GenerateOatFileNoChecks(info, target, error_msg);

art/runtime/oat_file_assistant.cc
OatFileAssistant::ResultOfAttemptToUpdate OatFileAssistant::GenerateOatFileNoChecks(
OatFileAssistant::OatFileInfo& info, CompilerFilter::Filter filter, std::string* error_msg)
其中包含生成OAT文件的路径相关的函数
首先判断 if (!runtime->IsDex2OatEnabled()),Dex2Oat的状态可不可用,如果是false则直接返回,不会进行编译生成OAT的过程
编译, if (!Dex2Oat(args, error_msg))

art/runtime/oat_file_assistant.cc
bool OatFileAssistant::Dex2Oat(const std::vector<std::string>& args, std::string* error_msg)
其中包括调用编译生成OAT的一些程序的相关参数
最终调用Exec来完成Dex2Oat的编译的过程,return Exec(argv, error_msg);

art/runtime/exec_utils.cc
bool Exec(std::vector<std::string>& arg_vector, std::string* error_msg)
调用了ExecAndReturnCode

art/runtime/exec_utils.cc
int ExecAndReturnCode(std::vector<std::string>& arg_vector, std::string* error_msg)
在这里首次进行了对进程的fork,pid_t pid = fork();
在子进程中使用execve来实现对Dex2Oat的编译过程

在这整个流程过程中如果我们对某个函数,比如execve进行修改或hook,都会导致Dex2OAT的流程的结束

实际上,强制停止Dex2Oat的流程,会让DexClassLoader第一次加载动态的dex时的过程变得非常的快速,减少了Dex2Oat的编译的流程

与此同时,要想实现ART下的函数抽取技术,我们也要强制阻断Dex2Oat的流程

回到art/runtime/oat_file_manager.cc
当我们阻断Dex2Oat的编译的流程,std::unique_ptr<const OatFile> oat_file(oat_file_assistant.GetBestOatFile().release());将会是一个空的操作

最终我们会进入到 if (oat_file_assistant.HasOriginalDexFiles()),会尝试加载我们的dex文件
之后调用DexFile::Open,if (!DexFile::Open(dex_location, dex_location, kVerifyChecksum, /*out*/ &error_msg, &dex_files))

art/runtime/dex_file.cc
bool DexFile::Open(const char* filename,
const std::string& location,
bool verify_checksum,
std::string* error_msg,
std::vector<std::unique_ptr<const DexFile>>* dex_files)

其中调用了File fd = OpenAndReadMagic(filename, &magic, error_msg);是一个脱壳点

又因为我们加载的是一个dex文件,之后我们又会进入到DexFile::OpenFile
if (IsDexMagic(magic)) {
std::unique_ptr<const DexFile> dex_file(DexFile::OpenFile(fd.Release(),
location,
/* verify */ true,
verify_checksum,
error_msg));

art/runtime/dex_file.cc
std::unique_ptr<const DexFile> DexFile::OpenFile(int fd,
const std::string& location,
bool verify,
bool verify_checksum,
std::string* error_msg)
这其中又会调用并进入OpenCommon
std::unique_ptr<DexFile> dex_file = OpenCommon(map->Begin(),
map->Size(),
location,
dex_header->checksum_,
kNoOatDexFile,
verify,
verify_checksum,
error_msg);

art/runtime/dex_file.cc
std::unique_ptr<DexFile> DexFile::OpenCommon(const uint8_t* base,
size_t size,
const std::string& location,
uint32_t location_checksum,
const OatDexFile* oat_dex_file,
bool verify,
bool verify_checksum,
std::string* error_msg,
VerifyResult* verify_result)
其中DexFile的构造函数
std::unique_ptr<DexFile> dex_file(new DexFile(base,
size,
location,
location_checksum,
oat_dex_file));

art/runtime/dex_file.cc
DexFile::DexFile(const uint8_t* base,
size_t size,
const std::string& location,
uint32_t location_checksum,
const OatDexFile* oat_dex_file)

和 InMemoryDexClassLoader一样

DexClassLoader脱壳点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
OpenAndReadMagic(filename, &magic, error_msg)

std::unique_ptr<DexFile> DexFile::OpenCommon(const uint8_t* base,
size_t size,
const std::string& location,
uint32_t location_checksum,
const OatDexFile* oat_dex_file,
bool verify,
bool verify_checksum,
std::string* error_msg,
VerifyResult* verify_result)

DexFile::DexFile(const uint8_t* base,
size_t size,
const std::string& location,
uint32_t location_checksum,
const OatDexFile* oat_dex_file)
  • 使用重合的脱壳点的话,我们就可以不管他是InMemoryDexClassLoader还是DexClassLoader都可以进行脱壳

脱壳点脱壳实现代码

DexFile::OpenCommon

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
std::unique_ptr<DexFile> DexFile::OpenCommon(const uint8_t* base,
size_t size,
const std::string& location,
uint32_t location_checksum,
const OatDexFile* oat_dex_file,
bool verify,
bool verify_checksum,
std::string* error_msg,
VerifyResult* verify_result) {
if (verify_result != nullptr) {
*verify_result = VerifyResult::kVerifyNotAttempted;
}
/*===start===*/
int pid = getpid();
char dexfilepath[100] = {0};
sprintf(dexfilepath, "/sdcard/%d_%d_OpenCommon.dex", (int)size, pid);
int fd = open(dexfilepath, 0 CREAT|0 RDWR, 666);
if (fd > 0) {
int number = write(fd, base, size);
if (number > 0) {

}
close(fd);
}
/*===end===*/
std::unique_ptr<DexFile> dex_file(new DexFile(base,
size,
location,
location_checksum,
oat_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;
}

DexFile::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
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
DexFile::DexFile(const uint8_t* base,
size_t size,
const std::string& location,
uint32_t location_checksum,
const OatDexFile* oat_dex_file)
: begin_(base),
size_(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),
oat_dex_file_(oat_dex_file) {
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));
/*===start===*/
int pid = getpid();
char dexfilepath[100] = {0};
sprintf(dexfilepath, "/sdcard/%d_%d_DexFile.dex", (int)size, pid);
int fd = open(dexfilepath, 0 CREAT|0 RDWR, 666);
if (fd > 0) {
int number = write(fd, base, size);
if (number > 0) {

}
close(fd);
}
/*===end===*/
InitializeSectionsFromMapList();
}

模块编译并刷入手机

对google手机编译android源码刷机_factory images for nexus and pixel-CSDN博客

Android13源码下载及全编译流程_android源码下载-CSDN博客

改好代码后,在android源码的目录下,打开终端输入命令

初始化编译环境

1
source build/envsetup.sh

选择构建目标

1
lunch

选择需要构建的目标并输入相应数字

编译固件

全部编译

1
make -j24

部分编译

编译生成的文件在out/target/你选择构建的目标

手机进入Fastboot 模式

通过ADB命令刷入boot.img和recovery.img的方法_adb刷入boot.img-CSDN博客

方法一,运行命令,设备将重启并进入 Fastboot 模式(bootloader模式)

1
adb reboot bootloader

方法二,手机关机,电源键 + 音量下键,两个键长按

将img刷入手机

单刷

fastboot flash 分区 .img文件

1
fastboot flash boot boot.img

刷入整个刷机包

1