而由应用程序开发者自己所编写的代码由dalvik.system.PathClassLoader
加载器加载,打印出来的信息如下:
dalvik.system.PathClassLoader[DexPathList[[directory “.”],nativeLibraryDirectories=[/system/lib64, /vendor/lib64, /system/lib64, /vendor/lib64]]]
查看源码可以看到PathClassLoader
是继承自BaseDexClassLoader
的,而PathClassLoader
还有另外两个兄弟: InMemoryDexClassLoader
以及DexClassLoader
,而壳程序很多都使用了这两个类加载器来加载解密后的dex文件。其中InMemoryDexClassLoader
是Android8.0以后新增的类,可以实现所谓的"不落地加载"。
总结一下,主要有如下几个ClassLoader:
前面说过壳加载完原始的dex以后还需要对ClassLoader
进行修正,否则加载组件类运行的时候会报ClassNotFoundException
,为什么会报这种错误呢? 这就涉及到了组件类的创建过程,比如对于Activity来说,应用程序的Activity对象是在ActivityThread类的performLaunchActivity()
方法中通过调用mInstrumentation.newActivity()
创建出来的,这个函数的实现逻辑为:
DexClassLoader
,同时设置DexClassLoader
的parent为系统组件类加载器BootClassLoader
的中间插入我们的DexClassLoader
,即加载原始dex的DexClassLoader
作为PathClassLoader
的parent第一种解决方案是将系统组件类替换,这样通过mInstrumentation.newActivity()
试图加载类的时候,就能找到相应的类。
第二种解决方案将ClassLoader
的继承关系修改为: BootClassLoader --> DexClassLoader --> PathClassLoader
,由于双亲委派机制的存在,当PathClassLoader
找不到动态加载的原始dex时,它会请求parent即DexClassLoader
加载,因此可以加载成功。
还有一些壳是通过对PathClassLoader
中的Elements数组进行合并来达到目的,这样的好处是当你试图用Frida枚举ClassLoader
对象的时候,看不到额外的ClassLoader
对象,也就无法轻易的得到Dex文件加载的路径。
art::DexFile
是加载dex绕不开的类,不论是使用BaseClassLoader
,还是自定义类加载器,最终都需要art::DexFile
,这一结论可以通过查阅class_linker.cc文件中的函数得到,像ClassLinker::DefineClass
,ClassLinker::LoadMethod
,ClassLinker::LoadClassMembers
这些重量级函数都需要以DexFile对象做为参数:
通过BaseDexClassLoader
来加载类会执行art/runtime/native/dalvik_system_DexFile.cc
的DexFile_defineClassNative
函数,这个函数的实现就是遍历Java层的DexFile
的mCookie
对象所表示的native层的art::DexFile
,因为这个art::DexFile
对象代表了dex文件在内存中的结构,所以可以通过类名在art::DexFile
列表中查找到DexFile::ClassDef
结构,才能继续调用ClassLinker::DefineClass()
函数,最终才能得到一个虚拟机实现中的Class对象表示mirror::Class*
,因此这个流程仍然离不开虚拟机中的art::DexFile
类。
这还会引发出一个问题:
art虚拟机执行smail指令可以解释执行,这种模式叫Interpreter模式(很显然解释模式下需要dex文件存在于内存当中才能解释执行)
也可以执行oat以后elf文件中的本地机器指令,这种模式叫quick code模式。
在quick code模式中,dex编译出来的机器指令包含在dex2oat生成出来的oat文件中(对于app来说,oat文件以.odex结尾),那么对于quick code模式是否还需要dex文件呢?如果不需要dex文件,是不是就无法在内存中dump出dex文件了?
既然知道虚拟机中的art::DexFile
类是dex在内存中的表示,那么得到这个对象就可以dump出dex文件。
接下来就是寻找一个合适的点可以得到art::DexFile
对象,得到对象以后通过hook的方式或者修改源码的方式都可以dump下来了。
下面是脱函数抽取型壳fart的作者寒冰大佬所提出的办法:
找到libart.so文件中所有导出函数中带有art::DexFile
参数或者返回值的函数,那么这就是一个可以脱壳的点
我写了一个命令可以查找满足这种条件的函数:
arm64-readelf -s libart.so -W | tr -s ’ ’ | cut -f9 -d ’ '| c++filt | grep “art::DexFile”
比如在art/runtime/dex_file.cc
的DexFile::OpenCommon
或者DexFile::DexFile
中添加如下代码即可脱壳:
pid_t pid = getpid();
char dexfilepath[100] = {0};
sprintf(dexfilepath,“/sdcard/drdump_%d_%d_DexFile.dex”,(int)size,(int) pid);
int fd = open(dexfilepath, O_CREAT | O_RDWR , 666);
if (fd > 0){
int number = write(fd,base,size);
if(number > 0){
}
close(fd);
}
这种是修改源码的方式来脱壳。
还有一种方式是通过frida hook来脱壳,它的优点是简单有效,不需要重新编译rom。
它的原理如下:
对于通过使用BaseDexClassLoader
来加载的程序来说,DexFile.java
类的mCookie
变量在native层的表现其实是一个jlong类型的指针的数组,数组的个数为此ClassLoader
加载的dex文件个数 + 1,第一个元素类型为OatFile*
,剩余的元素为对应的art::DexFile*
,因此可以通过获取mCookie
变量来得到art::DexFile*
列表,并且通过art::DexFile
的begin_和size_来dump。
代码如下:
function hasOwnProperty(obj, name) {
try {
return obj.hasOwnProperty(name) || name in obj;
} catch (e) {
return obj.hasOwnProperty(name)
}
}
function getHandle(object) {
var result = null;
if (hasOwnProperty(object, "KaTeX parse error: Expected '}', got 'EOF' at end of input: …esult = object.handle;
if (result) {
return result;
}
}
if (hasOwnProperty(object, "KaTeX parse error: Expected '}', got 'EOF' at end of input: … return object.h;
}
return null;
}
function dump_dex(packagename, dexfilebegin, dexfilesize) {
var dexfile_path = “/sdcard/my_frida_dump_” + packagename + “_” + dexfilesize + “.dex”;
var dexfile_handle = new File(dexfile_path, “w”);
if (dexfile_handle && dexfile_handle != null) {
var dex_buffer = ptr(dexfilebegin).readByteArray(dexfilesize);
dexfile_handle.write(dex_buffer);
dexfile_handle.flush();
dexfile_handle.close();
}
}
function dealwithClassLoader(classloaderobj, packagename) {
if (Java.available) {
Java.perform(function () {
try {
var dexfileclass = Java.use(“dalvik.system.DexFile”);
var BaseDexClassLoaderclass = Java.use(“dalvik.system.BaseDexClassLoader”);
var DexPathListclass = Java.use(“dalvik.system.DexPathList”);
var Elementclass = Java.use(“dalvik.system.DexPathList$Element”);
var basedexclassloaderobj = Java.cast(classloaderobj, BaseDexClassLoaderclass);
var tmpobj = basedexclassloaderobj.pathList.value;
var pathlistobj = Java.cast(tmpobj, DexPathListclass);
console.log(“pathlistobj->” + pathlistobj);
var dexElementsobj = pathlistobj.dexElements.value;
console.log(“dexElementsobj->” + dexElementsobj);
for (var i in dexElementsobj) {
var obj = dexElementsobj[i];
var elementobj = Java.cast(obj, Elementclass);
console.log(“elementobj->” + elementobj);
tmpobj = elementobj.dexFile.value;
var dexfileobj = Java.cast(tmpobj, dexfileclass);
var mCookie = dexfileobj.mInternalCookie.value;
var mInternalCookie = dexfileobj.mInternalCookie.value;
if (mCookie != null) {
var jnienv = Java.vm.tryGetEnv();
var cookiePtr = getHandle(mCookie);
var arrayLength = jnienv.getArrayLength(cookiePtr);
var long_data = jnienv.getLongArrayElements(cookiePtr, 0);
console.log(“arrayLength:” + arrayLength + “,long_data:” + long_data);
for (var i = 1; i < arrayLength; i++) {
var dexfileptr = Memory.readPointer(ptr(long_data).add(8 * i));
var dexfilebegin = Memory.readPointer(ptr(dexfileptr).add(Process.pointerSize * 1));
var dexfilesize = Memory.readU32(ptr(dexfileptr).add(Process.pointerSize * 2));
console.log(“pointer:” + dexfileptr + “,dexfilebegin:” + dexfilebegin + “,dexfilesize:” + dexfilesize);
dump_dex(packagename, dexfilebegin, dexfilesize);
}
我最近从朋友那里收集到了2020-2021BAT 面试真题解析,内容很多也很系统,包含了很多内容:Android 基础、Java 基础、Android 源码相关分析、常见的一些原理性问题
等等,可以很好地帮助大家深刻理解Android相关知识点的原理以及面试相关知识。
这份资料把大厂面试中常被问到的技术点整理成了PDF,包知识脉络 + 诸多细节;还有 高级架构技术进阶脑图 帮助大家学习提升进阶,也节省大家在网上搜索资料的时间来学习,也可以分享给身边好友一起学习。
这里也分享给广大面试同胞们,希望每位程序猿们都能面试成功~
Android 基础知识点
Java 基础知识点
Android 源码相关分析
常见的一些原理性问题
腾讯、字节跳动、阿里、百度等BAT大厂 2019-2020面试真题解析
加入社区》https://bbs.csdn.net/forums/4304bb5a486d4c3ab8389e65ecb71ac0
[外链图片转存中…(img-Tw0yoLsS-1725748274526)]
腾讯、字节跳动、阿里、百度等BAT大厂 2019-2020面试真题解析
[外链图片转存中…(img-ZBhBC54H-1725748274526)]
加入社区》https://bbs.csdn.net/forums/4304bb5a486d4c3ab8389e65ecb71ac0