【Android SDK程序逆向分析与破解系列】之二:Android可执行程序DEX分析(一)

作者:郭嘉

邮箱:[email protected]

博客:http://blog.csdn.net/allenwells

github:https://github.com/AllenWells

【Android SDK程序逆向分析与破解系列】章节索引

一 DEX文件数据结构

DEX使用的数据类型如下表所示:

u1~u8:表示1~8字节的无符号数。

sleb128、uled128和uled128pl:DEX文件特有的LEB128数据类型。每个LEB128由1~5个字节组成,所有的字节组合在一起表示一个32位的数据。

LEB128数据类型如下表所示:

每个字节只有7位有效位,如果第一个字节的最高位为1,则表明LEB128需要用到第2个字节,如果第2个字节最高位为1,则表明LEB128需要用到第3个字节,依次类推,知道最后的字节最高位为0.LEB128最多只会使用5个字节,如果读取5个字节后下一个字节的最高位仍为1,则表明该DEX文件无效,Dalvik虚拟机在验证DEX会失败返回。

Android系统源码中读取无符号LEB128的实现如下所示:

源码位置dalvik\libdex\Leb128.h

/*
 * Reads an unsigned LEB128 value, updating the given pointer to point
 * just past the end of the read value. This function tolerates
 * non-zero high-order bits in the fifth encoded byte.
 */
DEX_INLINE int readUnsignedLeb128(const u1** pStream) {
    const u1* ptr = *pStream;
    int result = *(ptr++);

    if (result > 0x7f) {    //大于0x77f表示第1个字节最高位为1
        int cur = *(ptr++); //第2个字节
        result = (result & 0x7f) | ((cur & 0x7f) << 7);//前2个字节组合
        if (cur > 0x7f) {   //大于0x77f表示第2个字节最高位为1
            cur = *(ptr++); //第3个字节
            result |= (cur & 0x7f) << 14;    //前3个字节的组合
            if (cur > 0x7f) {
                cur = *(ptr++);              //第4个字节
                result |= (cur & 0x7f) << 21;//前4个字节的组合
                if (cur > 0x7f) {
                    /*
                     * Note: We don‘t check to see if cur is out of
                     * range here, meaning we tolerate garbage in the
                     * high four-order bits.
                     */
                    cur = *(ptr++);          //第5个字节
                    result |= cur << 28;     //前5个字节的组合
                }
            }
        }
    }

    *pStream = ptr;
    return result;
}

Android系统源码中读取有符号LEB128的实现如下所示(有符号与无符号的计算方法是一样的,只是对有符号的LEB128最后一个字节的最高有效位进行了符号扩展):

源码位置dalvik\libdex\Leb128.h

/*
 * Reads a signed LEB128 value, updating the given pointer to point
 * just past the end of the read value. This function tolerates
 * non-zero high-order bits in the fifth encoded byte.
 */
DEX_INLINE int readSignedLeb128(const u1** pStream) {
    const u1* ptr = *pStream;
    int result = *(ptr++);

    if (result <= 0x7f) {
        result = (result << 25) >> 25;
    } else {
        int cur = *(ptr++);
        result = (result & 0x7f) | ((cur & 0x7f) << 7);
        if (cur <= 0x7f) {
            result = (result << 18) >> 18;
        } else {
            cur = *(ptr++);
            result |= (cur & 0x7f) << 14;
            if (cur <= 0x7f) {
                result = (result << 11) >> 11;
            } else {
                cur = *(ptr++);
                result |= (cur & 0x7f) << 21;
                if (cur <= 0x7f) {
                    result = (result << 4) >> 4;
                } else {
                    /*
                     * Note: We don‘t check to see if cur is out of
                     * range here, meaning we tolerate garbage in the
                     * high four-order bits.
                     */
                    cur = *(ptr++);
                    result |= cur << 28;
                }
            }
        }
    }

    *pStream = ptr;
    return result;
}

二 DEX文件整体结构

DEX文件由多个结构体组合而成,具体结构如下图所示:

- dex header:DEX文件头,指定了dex文件的一些属性,并记录了其他6部分在DEX文件中的物理偏移。

- string_ids

- type_ids

- proto_ids

- field_ids

- method_ids

- class_def

- data:真实的数据存放区。

- link_data:静态链接数据区。

Android源码中DexFile结构体的定义如下所示:

DexFile结构为DEX文件被映射到内存中结构,保存各个结构的指针,还包括了DexOptHeader与DexFile尾部附加的数据。

源码位置dalvik\libdex\DexFile.h

/*
 * Structure representing a DEX file.
 *
 * Code should regard DexFile as opaque, using the API calls provided here
 * to access specific structures.
 */
struct DexFile {
    /* directly-mapped "opt" header */
    const DexOptHeader* pOptHeader;

    /* pointers to directly-mapped structs and arrays in base DEX */
    const DexHeader*    pHeader;
    const DexStringId*  pStringIds;
    const DexTypeId*    pTypeIds;
    const DexFieldId*   pFieldIds;
    const DexMethodId*  pMethodIds;
    const DexProtoId*   pProtoIds;
    const DexClassDef*  pClassDefs;
    const DexLink*      pLinkData;

    /*
     * These are mapped out of the "auxillary" section, and may not be
     * included in the file.
     */
    const DexClassLookup* pClassLookup;
    const void*         pRegisterMapPool;       // RegisterMapClassPool

    /* points to start of DEX file data */
    const u1*           baseAddr;

    /* track memory overhead for auxillary structures */
    int                 overhead;

    /* additional app-specific data structures associated with the DEX */
    //void*               auxData;
};
时间: 2024-11-09 06:20:05

【Android SDK程序逆向分析与破解系列】之二:Android可执行程序DEX分析(一)的相关文章

Skipping &#39;Android SDK Tools, revision 24.0.2&#39;; it depends on &#39;Android SDK Platform-tools, revision 20&#39; which was not installed.

前几天,同事问我eclipse android sdk怎么不能更新. 更新界面是显示(mirrors.neusoft.edu.cn:80),但是不能更新. 问题描述如下: URL not found: F:\Android\android-sdk\temp\samples-19_r05.zip (拒绝访问.) Skipping 'Android SDK Tools, revision 24.0.2'; it depends on 'Android SDK Platform-tools, revi

Android Native 程序逆向入门(一)—— Native 程序的启动流程

八月的太阳晒得黄黄的,谁说这世界不是黄金?小雀儿在树荫里打盹,孩子们在草地里打滚.八月的太阳晒得黄黄的,谁说这世界不是黄金?金黄的树林,金黄的草地,小雀们合奏着欢畅的清音:金黄的茅舍,金黄的麦屯,金黄是老农们的笑声. —— 徐志摩·八月的太阳 ilocker:关注 Android 安全(新入行,0基础) QQ: 2597294287 在生成 native 程序时,在链接阶段会传入一个链接脚本,在该脚本中指定了程序的入口函数. 可以看到,在默认的链接脚本 armelf_linux_eabi.x 中

Android应用程序框架层和系统运行库层日志系统源代码分析

文章转载至CSDN社区罗升阳的安卓之旅,原文地址:http://blog.csdn.net/luoshengyang/article/details/6598703 在开发Android应用程序时,少不了使用Log来监控和调试程序的执行.在上一篇文章Android日志系统驱动程序Logger源代码分析中,我们分析了驱动程序Logger的源代码,在前面的文章浅谈Android系统开发中Log的使用一文,我们也简单介绍在应用程序中使Log的方法,在这篇文章中,我们将详细介绍Android应用程序框架

.Net程序员玩转Android系列之二~Android Framework概要(1)

从windows操作系统说起 人们总是喜欢从将陌生的事物和自己所了解的东西关联起来,以加深对未知事物的了解,这一讲我们从windows操作系统说起,逐步引领带大家走入android的世界.写任何程序都需要知道程序运行的原理和环境,就比如开发winform程序,你至少需要知道操作系统的原理,CLR运行时,了解一些常用的C/C++库.诚然,你不需要全部了解得很清楚也能写出很不错的应用程序,但是你了解的越清楚,钻研得越透彻,那么你越能开发出适合于你所在平台的.更优化的应用程序.譬如在windows操作

Titanium系列--安装Titanium Studio 中的Android SDK,JDK以及环境变量的配置(二)

Ubuntu安装配置JDK 1.先去 Oracle下载Linux下的JDK压缩包,我下载的是jdk-8u25-linux-x64.tar.gz文件,下好后直接解压 Step1:# 将解压好的jdk1.8.0_25文件夹用最高权限复制到/usr/lib/jvm目录里sudo cp -r ~/jdk1.8.0_25/ /usr/lib/jvm/ Step2:# 配置环境变量sudo gedit ~/.profile在末尾加上:export JAVA_HOME=/usr/lib/jvm/jdk1.8.

Android视频录制从不入门到入门系列教程(二)————显示视频图像

1.创建一个空的工程,注意声明下列权限: <uses-permission android:name="android.permission.CAMERA"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> 2.布局文件 <?xml version="1.0" encoding="utf-8"

[基于Android的ARM汇编语言系列]之二:原生程序的生成过程

作者:郭嘉 邮箱:[email protected] 博客:http://blog.csdn.net/allenwells github:https://github.com/AllenWell 所谓的原生程序指的是用C/C++编写的程序,下面来详细演示一下原生程序是怎么一步步生成汇编代码的. 这里编译的是一个简单的hello.c程序,如下所示: #include <stdio.h> int main(int argc, char* argv[]){ printf("Hello AR

Android应用程序启动过程源代码分析

文章转载至CSDN社区罗升阳的安卓之旅,原文地址:http://blog.csdn.net/luoshengyang/article/details/6689748 前文简要介绍了Android应用程序的Activity的启动过程.在Android系统中,应用程序是由Activity组成的,因此,应用程 序的启动过程实际上就是应用程序中的默认Activity的启动过程,本文将详细分析应用程序框架层的源代码,了解Android应用程序的启动过程. 在上一篇文章Android应用程序的Activit

Android应用程序消息处理机制(Looper、Handler)分析

文章转载至CSDN社区罗升阳的安卓之旅,原文地址:http://blog.csdn.net/luoshengyang/article/details/6817933 Android应用程序是通过消息来驱动的,系统为每一个应用程序维护一个消息队例,应用程序的主线程不断地从这个消息 队例中获取消息(Looper),然后对这些消息进行处理(Handler),这样就实现了通过消息来驱动应用程序的执行,本文将详细分析Android 应用程序的消息处理机制. 前面我们学习Android应用程序中的Activ