查看原文
其他

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

shmilyaxy 看雪学苑 2022-09-06


本文为看雪论坛优秀文章

看雪论坛作者ID:shmilyaxy





启动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无限循环处理消息

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)进行分发消息

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)进行消息处理

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流程代码]

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





看雪ID:shmilyaxy

https://bbs.pediy.com/user-home-941979.htm

*本文由看雪论坛 shmilyaxy 原创,转载请注明来自看雪社区



# 往期推荐

1.实战DLL注入

2.逆向篇:解决tiktok切换前后置虚拟摄像头卡住问题

3.House of cat新型glibc中IO利用手法解析 & 第六届强网杯House of cat详解

4.对一个随身WIFI设备的漏洞挖掘尝试

5.[安全运维向]模拟搭建小型企业内网

6.某设备CoAP协议漏洞挖掘实战






球分享

球点赞

球在看



点击“阅读原文”,了解更多!

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存