【Android 逆向】Dalvik 函数抽取加壳 ( 类加载流程分析 | Class.cpp#findClassNoInit 函数 | DexFile.cpp#dexFindClass 函数分析 )

简介: 【Android 逆向】Dalvik 函数抽取加壳 ( 类加载流程分析 | Class.cpp#findClassNoInit 函数 | DexFile.cpp#dexFindClass 函数分析 )

文章目录

前言

一、Class.cpp#dvmDefineClass 函数分析

二、Class.cpp#findClassNoInit 函数分析

三、DexFile.cpp#dexFindClass 函数分析

前言


上一篇博客 【Android 逆向】Dalvik 函数抽取加壳 ( 类加载流程分析 | native 函数查询 | dalvik_system_DexFile.cpp#defineClassNative 函数 ) 中 , dalvik_system_DexFile.cpp#Dalvik_dalvik_system_DexFile_defineClassNative 函数中 , 调用了 Class.cpp#dvmDefineClass 函数 ;






一、Class.cpp#dvmDefineClass 函数分析


在 Class.cpp#dvmDefineClass 函数中 , 主要调用了 Class.cpp#findClassNoInit 函数 ;



Class.cpp#dvmDefineClass 函数源码 :


/*
 * 从指定的DEX文件加载命名类(按描述符)。
 * 由类装入器用于从
 * 虚拟机管理的DEX。
 */
ClassObject* dvmDefineClass(DvmDex* pDvmDex, const char* descriptor,
    Object* classLoader)
{
    assert(pDvmDex != NULL);
    return findClassNoInit(descriptor, classLoader, pDvmDex);
}


源码路径 : /dalvik/vm/oo/Class.cpp#dvmDefineClass






二、Class.cpp#findClassNoInit 函数分析


在 Class.cpp#findClassNoInit 函数中 ,


先调用 dvmLookupClass 函数 , 查询当前已经加载的类 , 一般情况下 , 第一次加载 , 查询到的结果是空的 ;


// 查询当前已经加载的类 , 第一次加载 , 一般查询不到 
    clazz = dvmLookupClass(descriptor, loader, true);


然后进入到 clazz == NULL 分支 , 执行 dexFindClass 函数 , 此处调用了 dexFindClass , 就是在该函数中 , 恢复被抽取的函数 , 《Android中实现「类方法指令抽取方式」加固方案原理解析 | 作者 : 姜维》 博客 中恢复抽取函数的 hook 点 , 就是 dexFindClass 中 ;


pClassDef = dexFindClass(pDvmDex->pDexFile, descriptor);

1

参考 【Android 逆向】Dalvik 函数抽取加壳 ( Dalvik 下的函数指令抽取与恢复 | dex 函数指令恢复时机点 | 类加载流程 : 加载、链接、初始化 ) 博客 ;



Class.cpp#findClassNoInit 函数源码 :


/*

* 查找命名类(按描述符)。如果还没有加载,

* 我们加载并链接它,但不执行<clinit>。(越南船民

* 事件可能导致初始化的特定限制。)

*

* 如果“pDexFile”为空,我们将在bootclasspath中搜索条目。

*

* 失败时,返回NULL并引发异常。

*

* TODO:我们需要返回一个是否加载了类的指示

* 使用现有的定义。如果有人故意加载

* 在同一个类加载器中初始化两次,它们将得到LinkageError,

* 但无意中同时引用类应该“正常工作”。

*/

static ClassObject* findClassNoInit(const char* descriptor, Object* loader,

   DvmDex* pDvmDex)

{


// 查询当前已经加载的类 , 第一次加载 , 一般查询不到

   clazz = dvmLookupClass(descriptor, loader, true);

   if (clazz == NULL) {

 // 第一次查询 , 肯定会进入该分支

       const DexClassDef* pClassDef;


       dvmMethodTraceClassPrepBegin();

       profilerNotified = true;


#if LOG_CLASS_LOADING

       u8 startTime = dvmGetThreadCpuTimeNsec();

#endif


       if (pDvmDex == NULL) {

           assert(loader == NULL);     /* shouldn't be here otherwise */

           pDvmDex = searchBootPathForClass(descriptor, &pClassDef);

       } else {

/*
 * 查找命名类(按描述符)。如果还没有加载,
 * 我们加载并链接它,但不执行<clinit>。(越南船民
 * 事件可能导致初始化的特定限制。)
 * 
 * 如果“pDexFile”为空,我们将在bootclasspath中搜索条目。
 * 
 * 失败时,返回NULL并引发异常。
 * 
 * TODO:我们需要返回一个是否加载了类的指示
 * 使用现有的定义。如果有人故意加载
 * 在同一个类加载器中初始化两次,它们将得到LinkageError,
 * 但无意中同时引用类应该“正常工作”。
 */
static ClassObject* findClassNoInit(const char* descriptor, Object* loader,
    DvmDex* pDvmDex)
{
  // 查询当前已经加载的类 , 第一次加载 , 一般查询不到 
    clazz = dvmLookupClass(descriptor, loader, true);
    if (clazz == NULL) {
  // 第一次查询 , 肯定会进入该分支
        const DexClassDef* pClassDef;
        dvmMethodTraceClassPrepBegin();
        profilerNotified = true;
#if LOG_CLASS_LOADING
        u8 startTime = dvmGetThreadCpuTimeNsec();
#endif
        if (pDvmDex == NULL) {
            assert(loader == NULL);     /* shouldn't be here otherwise */
            pDvmDex = searchBootPathForClass(descriptor, &pClassDef);
        } else {
    // 此处调用了 dexFindClass , 就是在该函数中 , 恢复被抽取的函数 
    //《Android中实现「类方法指令抽取方式」加固方案原理解析 | 作者 : 姜维》 博客
    // 中恢复抽取函数的 hook 点 , 就是 dexFindClass 中
            pClassDef = dexFindClass(pDvmDex->pDexFile, descriptor);
        }
    return clazz;
}


源码路径 : /dalvik/vm/oo/Class.cpp#findClassNoInit






三、DexFile.cpp#dexFindClass 函数分析


在该 DexFile.cpp#dexFindClass 函数 中 , 恢复被抽取的函数 , 《Android中实现「类方法指令抽取方式」加固方案原理解析 | 作者 : 姜维》 博客 中恢复抽取函数的 hook 点 , 就是 dexFindClass 中 ;



DexFile.cpp#dexFindClass 函数源码 :


/*
 * 按描述符查找类定义条目。
 * 
 * “描述符”应该像“Landroid/debug/Stuff;”。
 */
const DexClassDef* dexFindClass(const DexFile* pDexFile,
    const char* descriptor)
{
    const DexClassLookup* pLookup = pDexFile->pClassLookup;
    u4 hash;
    int idx, mask;
    hash = classDescriptorHash(descriptor);
    mask = pLookup->numEntries - 1;
    idx = hash & mask;
    /*
     * 搜索,直到找到匹配的条目或空插槽。
     */
    while (true) {
        int offset;
        offset = pLookup->table[idx].classDescriptorOffset;
        if (offset == 0)
            return NULL;
        if (pLookup->table[idx].classDescriptorHash == hash) {
            const char* str;
            str = (const char*) (pDexFile->baseAddr + offset);
            if (strcmp(str, descriptor) == 0) {
                return (const DexClassDef*)
                    (pDexFile->baseAddr + pLookup->table[idx].classDefOffset);
            }
        }
        idx = (idx + 1) & mask;
    }
}


源码路径 : /dalvik/libdex/DexFile.cpp#dexFindClass



目录
相关文章
|
3月前
|
Android开发
Android面试之Activity启动流程简述
Android面试之Activity启动流程简述
98 6
|
3月前
|
XML 前端开发 Android开发
Android面试高频知识点(3) 详解Android View的绘制流程
Android面试高频知识点(3) 详解Android View的绘制流程
Android面试高频知识点(3) 详解Android View的绘制流程
|
3月前
|
消息中间件 Android开发 索引
Android面试高频知识点(4) 详解Activity的启动流程
Android面试高频知识点(4) 详解Activity的启动流程
34 3
|
3月前
|
XML 前端开发 Android开发
Android面试高频知识点(3) 详解Android View的绘制流程
Android面试高频知识点(3) 详解Android View的绘制流程
29 2
|
3月前
|
XML 前端开发 Android开发
Android View的绘制流程和原理详细解说
Android View的绘制流程和原理详细解说
56 3
|
3月前
|
Android开发
Android面试之Activity启动流程简述
Android面试之Activity启动流程简述
23 0
|
4月前
|
消息中间件 Android开发 索引
Android面试高频知识点(4) 详解Activity的启动流程
讲解Activity的启动流程了,Activity的启动流程相对复杂一下,涉及到了Activity中的生命周期方法,涉及到了Android体系的CS模式,涉及到了Android中进程通讯Binder机制等等, 首先介绍一下Activity,这里引用一下Android guide中对Activity的介绍:
61 4
|
4月前
|
Android开发 开发者
Android面试之Activity启动流程简述
每个Android开发者都熟悉的Activity,但你是否了解它的启动流程呢?本文将带你深入了解。启动流程涉及四个关键角色:Launcher进程、SystemServer的AMS、应用程序的ActivityThread及Zygote进程。核心在于AMS与ActivityThread间的通信。文章详细解析了从Launcher启动Activity的过程,包括通过AIDL获取AMS、Zygote进程启动以及ActivityThread与AMS的通信机制。接着介绍了如何创建Application及Activity的具体步骤。整体流程清晰明了,帮助你更深入理解Activity的工作原理。
64 0
|
5月前
|
Android开发
我的Android进阶修炼:安卓启动流程之init(1)
本文深入分析了Android系统中的init进程,包括其源码结构、主要功能以及启动流程的详细注解,旨在帮助读者理解init作为用户空间的1号进程在Android启动过程中的关键作用。
101 1
|
5月前
|
Java 调度 Android开发
Android经典实战之Kotlin的delay函数和Java中的Thread.sleep有什么不同?
本文介绍了 Kotlin 中的 `delay` 函数与 Java 中 `Thread.sleep` 方法的区别。两者均可暂停代码执行,但 `delay` 适用于协程,非阻塞且高效;`Thread.sleep` 则阻塞当前线程。理解这些差异有助于提高程序效率与可读性。
89 1