首页 > 其他分享 >安卓逆向系列教程(二)APK 和 DEX

安卓逆向系列教程(二)APK 和 DEX

时间:2023-05-02 15:02:37浏览次数:51  
标签:DEX struct 指向 存放 u4 安卓 APK 偏移 区段


安卓逆向系列教程(二)APK 和 DEX

作者:飞龙

APK

APK 是 Android 软件包的分发格式,它本身是个 Zip 压缩包。APK 根目录下可能出现的目录和文件有:

名称

用途

META-INF

存放元数据

AndroidManifest.xml

编译后的全局配置文件

assets

存放资源文件,不会编译

classes.dex

编译并打包后的源代码

lib

存放二进制共享库,含有armeabi-*mipsx86等文件夹,对应具体的平台

res

存放资源文件

resources.arsc

编译并打包后的res/values中的文件

res

res 中可能出现的目录如下:

名称

用途

anim

存放编译后的动画 XML 文件(<XXXAnimation>

color

存放编译后的选择器 XML 文件(<selector>

drawable-*

存放图片,*为不同分辨率,图片按照不同分辨率归类。其中带.9的图片为可拉伸的图片。

layout

存放编译后的布局 XML 文件(<XXXLayout>

menu

存放编译后的菜单 XML 文件(<menu>

mipmap-*

存放使用 mipmap 技术加速的图片,一般用来存放应用图标,其它同drawable-*

raw

存放资源文件,不会编译,比如音乐、视频、纯文本等

xml

存放编译后的自定义 XML 文件

resources.arsc

在 APK 中是找不到res/values这个目录的,因为它里面的文件编译后打包成了resources.arsc。为了理解它,我们先看一看原始的res/values

res/values中保存资源 XML 文件,根节点为<resources>。一般可能会出现以下几种文件:

名称

用途

arrays.xml

存放整数数组和字符串数组,使用<integer-array><string-array>定义,元素使用<item>定义

bools.xml

存放布尔值,使用<bool>定义

colors.xml

存放颜色,使用<color>定义

dimens.xml

存放尺寸,使用<dimen>定义

drawables.xml

存放颜色,使用<drawable>定义

ids.xml

存放 ID,使用<item type="id">定义

integers.xml

存放整数,使用<integers>定义

strings.xml

存放字符串,使用<strings>定义

styles.xml

存放颜色,使用<style>定义,元素使用<item>定义

res/values中的文件名称是无所谓的,这些名称只是约定。也就是说,任何res/values中的文件中的字符串都会出现在R.strings里面。

虽然我们在 APK 中无法直接看到这些文件,但是反编译之后就可以了。反编译之后,我们也会找到一个public.xml文件,是res里所有东西的索引:

<resources>
    <public type="drawable" name="ic_launcher" id="0x7f020000" />
    <public type="layout" name="activity_main" id="0x7f030000" />
    <public type="layout" name="activity_sub" id="0x7f030001" />
    <public type="dimen" name="activity_horizontal_margin" id="0x7f040000" />
    <public type="dimen" name="activity_vertical_margin" id="0x7f040001" />
    <public type="string" name="action_settings" id="0x7f050000" />
    <public type="string" name="app_name" id="0x7f050001" />
    <public type="string" name="hello_world" id="0x7f050002" />
    <public type="string" name="title_activity_sub" id="0x7f050003" />
    <public type="style" name="AppTheme" id="0x7f060000" />
    <public type="menu" name="main" id="0x7f070000" />
    <public type="menu" name="sub" id="0x7f070001" />
    <public type="id" name="button1" id="0x7f080000" />
    <public type="id" name="action_settings" id="0x7f080001" />
</resources>

DEX

DEX 即 Dalvik Executable,Dalvik 可执行文件。它的结构如下:

struct DexFile{
    DexHeader    Header;
    DexStringId  StringIds[stringIdsSize];
    DexTypeId    TypeIds[typeIdsSize];
    DexFieldId   FieldIds[fieldIdsSize];
    DexMethodId  MethodIds[methodIdsSize];
    DexProtoId   ProtoIds[protoIdsSize];
    DexClassDef  ClassDefs[classDefsSize];
    DexData      Data;
    DexLink      LinkData;
};

我们可以看到,它可以分为九个区段,如下:

Header

StringIds

TypeIds

FieldIds

MethodIds

ProtoIds

ClassDefs

Data

LinkData

大体结构如这张图所示:

另外,在讲解各个区段之前,需要首先了解一些数据类型的定义:

类型

定义

u1

等同于uint8_t,表示 1 字节的无符号数

u2

等同于uint16_t,表示 2 字节的无符号数

u4

等同于uint32_t,表示 4 字节的无符号数

u8

等同于uint64_t,表示 8 字节的无符号数

Header 区段

Header 区段用于储存版本标识、校验和、文件大小、各部分的大小及偏移。结构以及描述如下:

struct DexHeader {
    u1  magic[8];           /* 版本标识 */  
    u4  checksum;           /* adler32 检验和 */  
    u1  signature[kSHA1DigestLen]; /* SHA-1 哈希值 */
    u4  fileSize;           /* 整个文件大小 */
    u4  headerSize;         /* Header 区段大小 */
    u4  endianTag;          /* 字节序标记 */
    u4  linkSize;           /* 链接区段大小 */
    u4  linkOff;            /* 链接区段偏移 */
    u4  mapOff;             /* MapList 的偏移 */
    u4  stringIdsSize;      /* StringId 的个数 */
    u4  stringIdsOff;       /* StringIds 区段偏移 */
    u4  typeIdsSize;        /* TypeId 的个数 */
    u4  typeIdsOff;         /* TypeIds 区段偏移 */
    u4  protoIdsSize;       /* ProtoId 的个数 */
    u4  protoIdsOff;        /* ProtoIds 区段偏移 */
    u4  fieldIdsSize;       /* FieldId 的个数 */
    u4  fieldIdsOff;        /* FieldIds 区段偏移 */
    u4  methodIdsSize;      /* MethodId 的个数 */
    u4  methodIdsOff;       /* MethodIds 区段偏移 */
    u4  classDefsSize;      /* ClassDef 的个数 */
    u4  classDefsOff;       /* ClassDefs 区段偏移 */
    u4  dataSize;           /* 数据区段的大小 */
    u4  dataOff;            /* 数据区段的文件偏移 */
};

有几个条目需要特别提醒。

  • magic:必须为DEX_FILE_MAGIC
ubyte[8] DEX_FILE_MAGIC = { 0x64 0x65 0x78 0x0a 0x30 0x33 0x35 0x00 }
                        = "dex\n035\0";
  • checksum:是整个文件除去它本身以及魔数的校验和。
  • signature:是整个文件除去它本身、校验和以及魔数的哈希值。
  • headerSize:一般为 70。
  • endianTag:有两种顺序,小端和大端,定义如下:
uint ENDIAN_CONSTANT = 0x12345678; /* 小端序 */
uint REVERSE_ENDIAN_CONSTANT = 0x78563412; /* 大端序 */

一般为小端序,反正我还没见过大端的。

  • stringIdsOff:由于前一个区段的偏移加上它的长度一般为后一个区段的偏移,所以这个条目一般也为 70。
  • xxxSize:要注意有几个是个数,后缀也是Size
  • xxxOff:如果对应的xxxSize为 0,那么它也为 0(很奇怪)。

StringIds 区段

StringIds 区段包含stringIdsSizeDexStringId结构,如下:

struct DexStringId {
    u4 stringDataOff;   /* 字符串内容,字符串数据偏移 */
};

其中数据偏移指向 Data 区段的字符串数据。

TypeIds 区段

TypeIds 包含typeIdsSizeDexTypeId结构,如下:

struct DexTypeId {
    u4 descriptorIdx;    /* 类型的完全限定符,指向 DexStringId 列表的索引 */
};

索引是一个从 0 开始的数字,表示对应第几个DexStringId。这些DexStringId指向的字符串都是类型名称,比如ILjava/lang/String;之类的。DexTypeId的索引也会用于后面的结构。

ProtoIds 区段

ProtoIds 包含ProtoIdsSizeDexProtoId结构。这里的 Proto 指方法原型,包含返回类型和参数类型。

struct DexProtoId {
    u4 shortyIdx;       /* 原型缩写,指向 DexStringId 列表的索引 */
    u4 returnTypeIdx;   /* 返回类型,指向 DexTypeId 列表的索引 */
    u4 parametersOff;   /* 参数类型列表,指向 DexTypeList 的偏移 */
};

struct DexTypeList {
    u4 size;             /* 接下来 DexTypeItem 的个数 */
    DexTypeItem list[size]; /* DexTypeItem 结构 */
};

struct DexTypeItem {
    u2 typeIdx;    /* 参数类型,指向 DexTypeId 列表的索引 */
};

原型缩写是把所有返回类型和参数类型的名称拼在一起,对象的话只写L。比如int(int,int)写为IIIvoid()写为Vvoid(String)写为VL

参数类型列表一般保存在Data区段中,如果没有,parametersOff为 0。

FieldIds 区段

TypeIds 包含fieldIdsSizeDexFieldId结构,如下:

struct DexFieldId {
    u2 classIdx;   /* 类的类型,指向 DexTypeId 列表的索引 */
    u2 typeIdx;    /* 字段类型,指向 DexTypeId 列表的索引 */
    u4 nameIdx;    /* 字段名称,指向 DexStringId 列表的索引 */
};

MethodIds 区段

MethodIds 包含methodIdsSizeDexMethodId结构,如下:

struct DexMethodId {
    u2 classIdx;  /* 类的类型,指向 DexTypeId 列表的索引 */
    u2 protoIdx;  /* 方法原型,指向 DexProtoId 列表的索引 */
    u4 nameIdx;   /* 方法名称,指向 DexStringId 列表的索引 */
};

ClassDefs 区段

ClassDefs 包含classDefsSizeDexClassDef结构,如下:

struct DexClassDef {
    u4 classIdx;         /* 类的类型,指向 DexTypeId 列表的索引 */
    u4 accessFlags;      /* 访问标志 */
    u4 superclassIdx;    /* 父类类型,指向 DexTypeId列表的索引 */
    u4 interfacesOff;    /* 接口,指向 DexTypeList 的偏移 */
    u4 sourceFileIdx;    /* 源文件名,指向 DexStringId 列表的索引 */
    u4 annotationsOff;   /* 注解,指向 DexAnnotationsDirectoryItem 结构 */
    u4 classDataOff;     /* 指向 DexClassData 结构的偏移 */
    u4 staticValuesOff;  /* 指向 DexEncodedArray 结构的偏移 */
};

struct DexClassData {
    DexClassDataHeader header;                    /* 各个字段与方法的个数 */
    DexField staticFields[staticFieldsSize];      /* 静态字段 */
    DexField instanceFields[instanceFieldsSize];  /* 实例字段 */
    DexMethod directMethods[directMethodsSize];   /* 直接方法 */
    DexMethod virtualMethods[virtualMethodsSize]; /* 虚方法 */
};

struct DexClassDataHeader {
    u4 staticFieldsSize;   /* 静态字段个数 */
    u4 instanceFieldsSize; /* 实例字段个数 */
    u4 directMethodsSize;  /* 直接方法个数 */
    u4 virtualMethodsSize; /* 虚方法个数 */
};

struct DexField {
    u4 fieldIdx;    /* 指向 DexFieldId 的索引 */
    u4 accessFlags; /* 访问标志 */
};

struct DexMethod {
    u4 methodIdx;   /* 指向 DexMethodId 的索引 */
    u4 accessFlags; /* 访问标志 */
    u4 codeOff;     /* 方法指令,指向DexCode结构的偏移 */
};

struct DexCode {
    u2 registersSize;    /* 使用的寄存器个数 */
    u2 insSize;          /* 参数个数 */
    u2 outsSize;         /* 调用其他方法时使用的寄存器个数 */
    u2 triesSize;        /* Try/Catch个数 */
    u4 debugInfoOff;     /* 指向调试信息的偏移 */
    u4 insnsSize;        /* 指令集个数,以2字节为单位 */
    u2 insns[insnsSize]; /* 指令集 */
};

DexClassDataDexCode保存在 Data 区段中。

Data 区段

这个区段中除了存放二级结构和字符串,还有个重要的结构叫做DexMapList,它实际上 DEX 中所有东西的索引,包括各种二级结构、字符串和它本身。DEX 中同类结构都会保存在一起,所以一类结构只占用一个条目。

struct DexMapList {
    u4 size;                  /* 条目个数 */
    DexMapItem list[size];    /* 条目列表 */
};

struct DexMapItem {   
    u2 type;      /* 结构类型,kDexType 开头 */
    u2 unused;    /* 未使用,用于字节对齐 */
    u4 size;      /* 连续存放的结构个数 */
    u4 offset;    /* 结构的偏移 */
};

/* 结构类型代码 */
enum {
    kDexTypeHeaderItem               = 0x0000,
    kDexTypeStringIdItem             = 0x0001,
    kDexTypeTypeIdItem               = 0x0002,
    kDexTypeProtoIdItem              = 0x0003,
    kDexTypeFieldIdItem              = 0x0004,
    kDexTypeMethodIdItem             = 0x0005,
    kDexTypeClassDefItem             = 0x0006,
    kDexTypeMapList                  = 0x1000,
    kDexTypeTypeList                 = 0x1001,
    kDexTypeAnnotationSetRefList     = 0x1002,
    kDexTypeAnnotationSetItem        = 0x1003,
    kDexTypeClassDataItem            = 0x2000,
    kDexTypeCodeItem                 = 0x2001,
    kDexTypeStringDataItem           = 0x2002,
    kDexTypeDebugInfoItem            = 0x2003,
    kDexTypeAnnotationItem           = 0x2004,
    kDexTypeEncodedArrayItem         = 0x2005,
    kDexTypeAnnotationsDirectoryItem = 0x2006,
};

参考


标签:DEX,struct,指向,存放,u4,安卓,APK,偏移,区段
From: https://blog.51cto.com/wizardforcel/6239968

相关文章

  • 安卓docker使用Alpine Term
    虽然Termux很强大,可以安装各种linux,但基于termux版linux还是不能运行Docker,需要Termux上装Qemu虚拟机,Qemu装alpinelinux,这个时候才是原汁原味的x86linux。幸运的是有人把Termux+Qemu+Alpine整合好了,名为alpine-term,开箱即用,安装一个apk打开就是配置完全的alpinelinux。下载......
  • 用alter table添加索引与create index区别
    1、altertable一次可以添加多个索引,createindex一次只能创建一个。创建多个索引时,altertable只对表扫描一次,效率较高。2、altertable可以不指定索引名,此时将使用索引列的第一列的列名;createindex必须指定索引名。因此,altertable添加索引更灵活,所以在创建索引的时候提倡使用a......
  • Looking in indexes: https://pypi.tuna.tsinghua.edu.cn/simple ERROR: Could not fi
    命令行输入:pipinstallmediapipe报错:Lookinginindexes:https://pypi.tuna.tsinghua.edu.cn/simpleERROR:Couldnotfindaversionthatsatisfiestherequirementmediapipe(fromversions:none)ERROR:Nomatchingdistributionfoundformediapipe查看了网......
  • 安卓手机一键硬改手机技术
    在现代社会中,手机已成为人们生活中必不可少的设备之一。安卓手机是智能手机市场中的一大主流,但有时我们可能会觉得手机的性能不足,或者我们想要实现一些安卓手机本身不支持的功能。在这种情况下,一键硬改手机技术就成了一个非常有用的工具。一键硬改手机技术是指通过软件和一......
  • 安卓一键改串号免root技术原理
    在智能手机用户中,有一部分人因为种种原因需要改变手机串号,比如解锁手机、绕过应用程序检测等。然而,传统的修改串号方法需要root权限,对普通用户来说比较困难。近年来,出现了一些安卓一键改串号免root技术,让用户可以更方便地修改手机串号。那么,这种技术的原理是什么呢?首先,我们......
  • 安卓手机虚拟定位技术原理
    随着互联网技术的发展,人们对于移动设备的需求越来越高,同时也希望通过移动设备实现更多的功能。手机定位功能就是其中一个重要的功能之一,它可以实现位置服务,帮助我们方便地找到目的地、搜索周边、定位人员等。但是在某些情况下,我们需要修改手机的位置信息,来达到某种特定的目的,这就......
  • Deep Dynamics Models for Learning Dexterous Manipulation
    发表时间:2019(CoRL2019)文章要点:文章提出了一个onlineplanningwithdeepdynamicsmodels(PDDM)的算法来学习Dexterousmulti-fingeredhands,大概意思就是学习拟人的灵活的手指操控技巧。大概思路就是结合uncertainty-awareneuralnetworkmodels和gradient-freetrajecto......
  • 使用findIndex查找并做一些操作
    1.查找指定数据并删除letfindIndex=arrItemsApprover.findIndex(item=>item.zusrid===oObject.zusrid);if(findIndex!==-1){ arrItemsApprover.splice(findIndex,1);}2.查找指定数据并添加属性arrData.forEach(item=>{if(selectApproveUserData.findIndex(i=>item......
  • 安卓QQ浏览器打开新浪博客
    Update以上问题已经不是问题了,因为况哥不用新浪博客了。况哥登录新浪博客时,提示账户异常,以前的博客无法查看,更别说修改了。为了避免即使可能恢复正常后再次出现这种问题,况哥决定直接弃用。 ......
  • [Termux]更换Termux源 安装Debian容器并 设置Debian镜像源且 安装code-server(附安卓/
    前言Termux开发者称已经不会在GooglePlay上更新该应用了,要么在Github下载要么去F-Driod下载,为了方便下载,本文已经给出下载链接...GitHub下载链接:https://github.com/termux/termux-app/releases/download/v0.118.0/termux-app_v0.118.0+github-debug_universal.apk(GitHub......