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

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

2022-8-21 16:42
22952

【前言】
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函数

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

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是两码事

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

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

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

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

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

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

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

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

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

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

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

【文件目录】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】
图片描述

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

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

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

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

图片描述

图片描述

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

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

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

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

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

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

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

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

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

【文件目录】/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文件格式是否有误
图片描述

图片描述
图片描述

图片描述

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");                           
    }                           
}
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");                           
    }                           
}
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();                   
        }                   
    }
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();                   

[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

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