首页
社区
课程
招聘
[原创]Android4.4和8.0 DexClassLoader加载流程分析之寻找脱壳点
2022-8-21 16:42 21853

[原创]Android4.4和8.0 DexClassLoader加载流程分析之寻找脱壳点

2022-8-21 16:42
21853

一、启动app的启动流程分析

【前言】
Android手机开机后-->启动Zygoye进程-->创建SystemServer进程
1.Zygoye进程孵化器:Android中的所有进程,如 系统进程、应用进程、SystemServer进程,都是由Zygoye调用fork方法创建的
2.SysremServer进程:就是核心服务所在进程,核心服务如 WindowsManagerServer、PowerManagerService、ActivityManagerService等系统服务
3.ActivityManagerService服务:简称AMS,该服务由SystemServer启动,主要功能是控制四大组件启动和调度工作,控制应用程序的管理和调度工作
【应用程序启动】
Launcher应用(系统主界面)-->最终获得ActivityManagerService-->ActivityManagerService.start()方法-->判断要启动的应用是否存在-->存在则直接切换到前台-->不存在则调用Process类,通过Process类调用Zygoye的fork方法创建进程-->调用ActivityThread的main函数

【ActivityThread.main函数引发的调用流程源码分析】

【1】创建ActivityThread对象、调用Looper.loop无限循环处理消息

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
ActivityThread类                           
 
 public static void main(String[] args) {                           
        SamplingProfilerIntegration.start();                           
 
        CloseGuard.setEnabled(false);                           
 
        Environment.initForCurrentUser();                           
 
        EventLogger.setReporter(new EventLoggingReporter());                           
 
       Security.addProvider(new AndroidKeyStoreProvider());                           
 
        Process.setArgV0("<pre-initialized>");                           
 
        Looper.prepareMainLooper();                           
 
        ActivityThread thread = new ActivityThread(); //创建ActivityThread对象                           
       thread.attach(false);                           
 
        if (sMainThreadHandler == null) {                           
            sMainThreadHandler = thread.getHandler();                           
        }                           
 
        AsyncTask.init();                           
 
       if (false) {                           
            Looper.myLooper().setMessageLogging(new                           
                    LogPrinter(Log.DEBUG, "ActivityThread"));                           
        }                           
 
        Looper.loop();     //启动Looper.loop无限循环处理消息                       
 
        throw new RuntimeException("Main thread loop unexpectedly exited");                           
    }                           
}

【2】msg.target.dispatchMessage(msg)进行分发消息

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
Looper类                   
 
 public static void loop() {                   
        final Looper me = myLooper();                   
        if (me == null) {                   
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");                   
        }                   
        final MessageQueue queue = me.mQueue;                   
        Binder.clearCallingIdentity();                   
        final long ident = Binder.clearCallingIdentity();                   
 
       for (;;) {                   
            Message msg = queue.next(); // might block                   
            if (msg == null) {                   
                return;                   
            }                   
 
           Printer logging = me.mLogging;                   
           if (logging != null) {                   
               logging.println(">>>>> Dispatching to " + msg.target + " " +                   
                        msg.callback + ": " + msg.what);                   
           }                   
 
            msg.target.dispatchMessage(msg);                   
 
           if (logging != null) {                   
                logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);                   
           }                   
 
          // Make sure that during the course of dispatching the                   
           // identity of the thread wasn't corrupted.                   
           final long newIdent = Binder.clearCallingIdentity();                   
           if (ident != newIdent) {                   
                Log.wtf(TAG, "Thread identity changed from 0x"                   
                        + Long.toHexString(ident) + " to 0x"                   
                       + Long.toHexString(newIdent) + " while dispatching to "                   
                        + msg.target.getClass().getName() + " "                   
                        + msg.callback + " what=" + msg.what);                   
            }                   
 
            msg.recycle();                   
        }                   
    }

【3】handleMessage(msg)进行消息处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Handler类               
 
    public void dispatchMessage(Message msg) {               
        if (msg.callback != null) {               
            handleCallback(msg);               
        } else {               
            if (mCallback != null) {               
                if (mCallback.handleMessage(msg)) {               
                    return;               
                }               
            }               
           handleMessage(msg);               
       }               
    }

【4】通过消息类型分别调用handleLaunchActivity(r, null)[Activity流程代码]和handleBindApplication(data)[Application流程代码]

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
ActivityThread$H类                               
 
 public void handleMessage(Message msg) {                               
            if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));                               
            switch (msg.what) {                               
                case LAUNCH_ACTIVITY: {                               
                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart");                               
                    ActivityClientRecord r = (ActivityClientRecord)msg.obj;                               
 
                    r.packageInfo = getPackageInfoNoCheck(                               
                            r.activityInfo.applicationInfo, r.compatInfo);                               
                    handleLaunchActivity(r, null);                               
                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);                               
                } break;                               
                case RELAUNCH_ACTIVITY: {                               
                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityRestart");                               
                    ActivityClientRecord r = (ActivityClientRecord)msg.obj;                               
                    handleRelaunchActivity(r);                               
                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);                               
                } break;                               
    ...                           
                case BIND_APPLICATION:                               
                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "bindApplication");                               
                    AppBindData data = (AppBindData)msg.obj;                               
                    handleBindApplication(data);                               
                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);                               
                    break;

【5】Activity调用流程

handleLaunchActivity(r, null)-->performLaunchActivity(r, customIntent)--> mInstrumentation.callActivityOnCreate(activity, r.state)-->activity.performCreate(icicle)-->onCreate(icicle)
注意,此时调用到的onCreate方法就是我们平常写Android代码时候的Activity的onCreate方法
图片描述

【6】Application的调用流程

handleBindApplication(data)-->data.info.makeApplication(data.restrictedBackupMode, null)-->mActivityThread.mInstrumentation.newApplication(cl, appClass, appContext)--> app.attach(context)-->attachBaseContext(context)
handleBindApplication(data)-->mInstrumentation.callApplicationOnCreate(app)--> app.onCreate()
可以分析到Application最终是分别调用了两个方法attachBaseContext和onCreate,此处的onCreate和Activity的onCreate是两码事

【7】小结

综上分析可以得出,在真正调用到Activity的onCreate方法之前,还会经过两个关键的函数,就是Application的attachBaseContext和onCreate方法
【可以做什么?】
可以在Application调用的函数中进行真正的dex代码的加载替换。达到加壳的目的,因为在调用Activity之前回调用Application的两个函数,所以在此期间将真正程序的代码给替换进来即可。

二、ClassLoader的加载流程-找脱壳点

【前言】
了解Android的ClassLoader的继承关系
图片描述

1.Android4.4 Dalvik模式下加载Dex流程

【1】从DexClassLoader开始入手分析,因为加载Dex文件一般调用DexClassLoader创建对象,发现该类的构造方法调用了父类类构造

【文件目录】libcore
【类名】DexClassLoader
【构造方法】public DexClassLoader(String dexPath, String optimizedDirectory, String libraryPath, ClassLoader parent)
【父类构造】super(dexPath, new File(optimizedDirectory), libraryPath, parent);
继承自父类BaseDexClassLoader(pathClassLoader也是继承自此父类)
父类构造方法
图片描述

【2】BaseDexClassLoader是一个关键的类,很多的实现方法都封装于此类,调用构造向父类传递了类加载器的父类

【文件目录】libcore
【类名】BaseDexClassLoader
【构造方法】public BaseDexClassLoader(String dexPath, File optimizedDirectory, String libraryPath, ClassLoader parent)
【父类构造】super(parent);
图片描述

【3】ClassLoader唯一的作用就是指定了父类

【文件目录】libcore
【类名】ClassLoader
【构造方法】protected ClassLoader(ClassLoader parentLoader)
【初始化】ClassLoader(ClassLoader parentLoader, boolean nullAllowed){ parent = parentLoader;}
图片描述

【4】BaseDexClassLoader创建DexPathList对象

【构造方法初始化】this.pathList = new DexPathList(this, dexPath, libraryPath, optimizedDirectory);
【类成员】private final DexPathList pathList;
图片描述
图片描述

【5】DexPathList构造函数

【文件目录】libcore
【类名】DexPathList
【构造方法】public DexPathList(ClassLoader definingContext, String dexPath, String libraryPath, File optimizedDirectory)
【成员变量】private final Element[] dexElements;
5.1 对类加载器、dex文件路径、文件优化路径等参数基本判断
5.2 调用方法makeDexElements 对成员变量赋值dexElements
图片描述
图片描述

【6】分析makeDexElements方法

【方法原型】private static Element[] makeDexElements(ArrayList<File> files, File optimizedDirectory
6.1 判断文件名后缀
6.2 loadDexFile加载dex后缀的文件
图片描述
6.3 根据加载进来的dex文件new Element对象,并且赋值到成员变量中
6.4 返回局部变量Element对象数组
图片描述

【7】分析loadDexFile方法

【方法原型】private static DexFile loadDexFile(File file, File optimizedDirectory)
7.1 如果不存在优化后的dex文件,则直接创建DexFile类对象
7.2 如果存在优化后的dex文件,则调用DexFile类的loadDex方法加载dex文件
图片描述
7.3 这里对于优化后的文件,以及dex如何被优化的,在稍后有专门描述该内容

【8】分析DexFile类构造方法

【文件目录】libcore
【类名】DexFile
【构造方法】public DexFile(String fileName) throws IOException
【成员变量】private int mCookie;
8.1 调用方法openDexFile获取Cookie值
图片描述
图片描述

【9】分析openDexFile方法

【参数说明】:参数1:dex路径名;参数2:优化文件路径名
【函数说明】:调用openDexFileNative方法进入c++代码,并且返回cookie值
图片描述

【10】进入native层的Dalvik_system_DexFile.cpp文件分析

【文件目录】dalvik
【文件名】dalvik_system_DexFile
【方法】static void Dalvik_dalvik_system_DexFile_openDexFileNative(const u4 args, JValue pResult)
10.1 java层进入native层,获得参数,并且转换为c的类型格式
图片描述
10.2 dvmClassPathContains判断dex文件路径是否当前类加载器加载或者存在的
图片描述
10.3 hasDexExtension如果以dex结尾返回true
10.4 dvmRawDexFileOpen打开没有被优化的dex文件
10.5 pDexorJar 管理dex文件内部结构
图片描述
图片描述
【如果类路径包含特定路径返回true】
图片描述
【如果name以dex结尾,返回true】
图片描述

【11】分析RawDexFile.cpp文件

【文件目录】vm/dalvik
【文件名】RawDexFile.cpp
【方法】int dvmRawDexFileOpen(const char fileName, const char odexOutputName, RawDexFile** ppRawDexFile, bool isBootstrap)
11.1 dvmOptimizeDexFile生成优化版的dex文件
图片描述

【12】分析DexPrepare.cpp文件

【文件目录】vm/dalvik
【文件名】DexPrepare.cpp
【方法】bool dvmOptimizeDexFile(int fd, off_t dexOffset, long dexLength, const char* fileName, u4 modWhen, u4 crc, bool isBootstrap)
图片描述

【13】通过创建的新线程实现的优化代码中,可以找到脱壳点

13.1 包括Dex文件缓冲区和长度
图片描述
图片描述
图片描述
图片描述
图片描述
图片描述
图片描述

【14】小结

14.1 加载Dex的过程中,加载一个dex文件并赋值DexPathList的字段Element[] dexElements
14.2 创建一次Dex对象,打开dex文件时回赋值mCookie值
14.3 进入native层会对dex文件进行优化

2.Android8.0 Art模式下加载Dex流程-Native层开始分析

【1】/art/runtime/native/dalvik_system_DexFile.cc/ DexFile_openDexFileNative

图片描述

【2】OpenDexFilesFromOat

图片描述

【3】MakeUpToDate

图片描述
3.1 GenerateOatFileNoChecks
图片描述
3.2 Dex2Oat
图片描述
图片描述
3.3 Exec
图片描述
3.4 ExecAndReturnCode
图片描述

【4】/art/dex2oat/dex2oat.cc

图片描述
4.1 Dex2oat()
图片描述
4.2 Setup()
图片描述

【5】如果阻断了Oat的生成

图片描述
【OpenDexFilesFromOat函数中】
图片描述
图片描述

【6】 DexFile::Open

【脱壳点】OpenAndReadMagic、DexFile::OpenFile
图片描述
6.1 DexFile::OpenFile
图片描述
6.2 OpenCommon
图片描述

3.Android8.0 Art模式下ImMemoryDexClassLoader加载Dex流程-Native层开始分析

【特点】:仅加载DEX文件,并不会对文件进行优化生成oat文件

【1】InMemoryDexClassLoader

【文件目录】libore
【父类】BaseDexClassLoader
图片描述

【2】BaseDexClassLoader

【父类】ClassLoader
2.1父类构造函数,设置父类变量parent
2.2创建DexPathList对象
图片描述

【3】DexPathList

【构造函数】public DexPathList(ClassLoader definingContext, ByteBuffer[] dexFiles)
图片描述
【3.1】makInMemoryDexElements
返回值:Element数组(dex文件信息数组)
dexFiles:dex缓冲区数组
创建DexFile文件对象并根据DexFile对象创建Element对象
图片描述

【4】DexFile

【构造方法】:DexFile(ByteBuffer buf) throws IOException
图片描述
【4.1】openInMemoryDexFile
调用native层方法进入c++
图片描述

【5】dalvik_system_DexFile.cc文件

【文件目录】/art/runtime/native/dalvik_system_DexFile.cc
【5.1】DexFile_createCookieWithDirectBuffer
图片描述
【5.2】DexFile_createCookieWithArray
图片描述
【5.3】 CreateSingleDexFileCookie
创建DexFile实例对象和ConvertDexFilesToJavaArray
图片描述
【5.4】CreateDexFile
DexFile::Open和dex_file.release()
图片描述
【5.5】/art/runtime/dex_file.cc/DexFile::Open
图片描述
【5.6】DexFile::OpenCommon
图片描述
【5.7】DexFile构造函数
图片描述
【5.8】InitializeSectionsFromMapList
判断比较Dex文件格式是否有误
图片描述

【6】/art/runtime/dex_file_verifier.cc/DexFileVerifier::Verify

图片描述
图片描述

【7】ConvertDexFilesToJavaArray

图片描述


[CTF入门培训]顶尖高校博士及硕士团队亲授《30小时教你玩转CTF》,视频+靶场+题目!助力进入CTF世界

收藏
点赞7
打赏
分享
最新回复 (8)
雪    币: 0
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
mb_zxwszblr 2022-8-24 08:35
2
0
能问大佬一个问题吗?如果手机被黑了,而黑客获取信息的方式主要是通过发送短信,请问拔掉手机卡可以避免信息泄露吗
雪    币: 3748
活跃值: (5465)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
huangjw 2022-8-24 14:34
3
0
好文章,谢谢分享,最近一直在巩固dalvik和art虚拟机以及oat文件的相关问题
雪    币: 5348
活跃值: (5349)
能力值: ( LV9,RANK:170 )
在线值:
发帖
回帖
粉丝
GitRoy 3 2022-8-24 19:36
4
0
感谢楼主分享!
雪    币: 2066
活跃值: (3109)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
shmilyaxy 2022-8-25 08:25
5
0
mb_zxwszblr 能问大佬一个问题吗?如果手机被黑了,而黑客获取信息的方式主要是通过发送短信,请问拔掉手机卡可以避免信息泄露吗
共同讨论,我得观点理论上可行,但是拔掉手机卡的速度得多快;而且能够通过网络发送出去
雪    币: 2066
活跃值: (3109)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
shmilyaxy 2022-8-25 08:26
6
0
huangjw 好文章,谢谢分享,最近一直在巩固dalvik和art虚拟机以及oat文件的相关问题
共同学习了。文章有很多分析得还不是很透彻,希望多多指点
雪    币: 2066
活跃值: (3109)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
shmilyaxy 2022-8-25 08:26
7
0
GitRoy 感谢楼主分享!
客气客气
雪    币: 232
活跃值: (173)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
wx_逝去流星 2022-9-3 14:59
8
0
楼主大才,,可惜基础太差看的云山雾绕的。。。 楼主可否推荐个学习路线
雪    币: 2066
活跃值: (3109)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
shmilyaxy 2022-9-3 20:05
9
0
wx_逝去流星 楼主大才,,可惜基础太差看的云山雾绕的。。。 楼主可否推荐个学习路线
其实我也是在学习中。其实看你想往哪个方向吧,安卓逆向的话我感觉基础知识java、安卓开发、NDK开发、这些都挺有用的,像这篇文章是分析源码的一个过程,知道过程才在逆向的时候利用吧。还有逆向的一些知识,加固脱壳、arm汇编、smali指令、hook框架、抓包、协议分析...这些都会有用我感觉,可以学习学习,后面还可以学关于安全的一些知识
游客
登录 | 注册 方可回帖
返回