CTF-Wiki DEX 文件格式深度解析:从 Dalvik 可执行文件到逆向实战
CTF-Wiki DEX 文件格式深度解析从 Dalvik 可执行文件到逆向实战【免费下载链接】ctf-wikiCome and join us, we need you!项目地址: https://gitcode.com/gh_mirrors/ct/ctf-wiki导读DEXDalvik eXecutable File是 Android 平台专门为 Java/Kotlin 代码设计的可执行文件格式面向内存受限、处理器性能有限的移动设备做了大量空间优化。本文以 CTF-Wiki 仓库中的 DEX 文件章节 为核心系统讲解 DEX 的底层数据类型含 LEB128 变长编码、文件头、索引区、数据区与 map section 的完整结构并结合仓库内相关文档与源码级示例进行纵深拓展。读完本文你将掌握 DEX 文件的二进制布局、索引指向关系具备使用 010 Editor 等工具手工解析 DEX 文件的能力为后续 Android 逆向smali 分析、APK 加固对抗打下坚实基础。一、为什么 Android 需要 DEX 文件1.1 移动平台的资源约束Google 在设计 Android 时没有直接使用 JVM 的.class文件格式而是专门设计了 DEXDalvik eXecutable File格式。原因在于手机这类移动平台的两个显著特征内存低每个应用可用的堆内存有限不能容忍冗余的字节码布局处理器性能较差CPU 指令集相对精简解析和执行开销需要尽量压低。.class文件面向桌面 JVM 设计每个类一个文件存在大量重复的字符串常量、类型描述符与方法签名信息而 DEX 将整个 APK 中所有类合并进一个文件通过**统一的索引表index**共享常量数据从而大幅压缩可执行文件体积。从仓库目录结构看CTF-Wiki 将 Android 逆向知识组织为 Android 中 Java 层的运行机制 这一大章节其中明确把研究内容划分为三条主线Java 虚拟机宏观执行流程、Smali 代码指令级视角、Java 层可执行文件即本文的 DEX 文件格式。1.2 DEX 在 Android 运行机制中的位置Android 中 Java 层代码的完整链路大致是Java/Kotlin 源码 ──► .class 文件 ──► dx/d8 ──► classes.dex ──► Dalvik/ART 虚拟机编译产物classes.dex位于 APK 包内运行时由 DalvikAndroid 4.x 及以前或 ARTAndroid 5.0 以后加载执行。DEX 中的字节码可读性较差逆向领域使用 smali 语法 作为其汇编级表示——这从侧面印证了 DEX 是理解整个 Android Java 层逆向的地基。二、DEX 基础数据类型与 LEB128 变长编码2.1 数据类型定义总表在介绍 DEX 具体结构之前必须先掌握文件中使用的基础数据类型。下表完整列出 DEX 格式规范中定义的原子类型名称说明byte8 位有符号整数ubyte8 位无符号整数short16 位有符号整数采用小端字节序ushort16 位无符号整数采用小端字节序int32 位有符号整数采用小端字节序uint32 位无符号整数采用小端字节序long64 位有符号整数采用小端字节序ulong64 位无符号整数采用小端字节序sleb128有符号 LEB128可变长度见下文uleb128无符号 LEB128可变长度见下文uleb128p1无符号 LEB128 加1可变长度见下文注意除byte/ubyte外的多字节整数均显式规定为小端字节序little-endian这与 x86/ARM 等主流移动处理器一致在手工解析二进制时无需字节交换。2.2 为什么需要变长整数DEX 之所以引入变长类型核心目标是尽可能减小可执行文件的体积。举例来说如果某个字符串的长度为 5那么其实只需要 1 个字节即可表示但如果为此专门定义 1 字节的u1类型就会把所有字符串的长度都限制在 255 以内无法表达更长的字符串。LEB128 编码根据数字大小动态选择字节数完美兼顾了空间与表达能力。2.3 LEB128 编码原理LEB128Little-Endian Base 128是一种基于小端的变长整数编码可表示 32 位大小的整数。其编码规则是每个字节的最高位第 7 位作为延续标志continuation bit1表示后面还有下一个字节0表示当前字节是最后一个因此每个字节实际上只有7 个有效 bit用来表示数值有效 bit 按小端顺序排列即第一个字节存放最低的 7 位后续字节依次存放更高位。一个 LEB128 类型的变量最多需要 5 个字节如果第 5 个字节的最高位仍然为1说明数据非法溢出了 32 位范围。2.4 无符号 LEB128uleb128的读取实现Dalvik 虚拟机源码libdex中读取无符号 LEB128 的函数如下它逐字节取出、掩掉最高位并移位拼接DEX_INLINE int readUnsignedLeb128(const u1** pStream) { const u1* ptr *pStream; int result *(ptr); //取第一个字节 if (result 0x7f) { //如果第1个字节大于0x7f,表示第一个字节最高位为1 int cur *(ptr); //第2个字节 result (result 0x7f) | ((cur 0x7f) 7); //前两个字节 if (cur 0x7f) { cur *(ptr); result | (cur 0x7f) 14; if (cur 0x7f) { cur *(ptr); result | (cur 0x7f) 21; if (cur 0x7f) { /* * Note: We dont 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; }从实现可以看出两点关键细节每个字节仅取低 7 位参与运算 0x7f依次左移 7、14、21、28 位最高 4 位第 5 字节的第 4~7 bit不做越界检查容忍垃圾数据——这是 Dalvik 出于性能考虑的务实取舍也是逆向分析时需要注意的宽松解析行为。手算示例计算字节流c0 83 92 25的 uleb128 值第一个字节最高位为 1说明还有后续字节result1 0xc0 0x7f 0x40第二个字节result2 (0x83 0x7f) 7 0x180第三个字节result3 (0x92 0x7f) 14 0x48000第四个字节最高位 0结束result4 0x25 21 0x4a00000最终值result1 result2 result3 result4 0x4a481c02.5 有符号 LEB128sleb128的读取实现有符号版本在拼接完所有有效位后还需根据最高有效位做符号扩展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 dont 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; }手算示例计算字节流d1 c2 b3 40的 sleb128 值result1 0xd1 0x7f 0x51result2 (0xc2 0x7f) 7 0x21000result3 (0xb3 0x7f) 14 0xcc000result4 0x40 21 0x8000000符号扩展(result1 result2 result3 result4) 4 4 0xf80ce151注意此处示例的第二、三步结果与文档原文一致0x21000、0xcc000最终通过算术右移完成符号扩展得到负数补码0xf80ce151。2.6 uleb128p1加一编码uleb128p1表示数值加 1 后再进行 uleb128 编码适用于以下场景要求数字的表示必须非负如用于表示前驱索引0xffffffff即无前驱的哨兵值当数字为0xffffffff时加 1 后为 0此时只需 1 个字节即可表示极大节约空间其余细节有待进一步结合 Dalvik 源码深入思考。三、DEX 文件整体结构概览DEX 文件的整体布局如下整个文件主要分为三大部分文件头header给出 DEX 文件的基本属性magic、校验和、各索引区大小与偏移等索引区index给出各类数据的索引真正的数据存放在数据区数据区data存放真实的字符串、代码、类型列表等原始数据。这种索引区 数据区分离的设计是 DEX 空间优化的关键索引区中的string_ids、type_ids、proto_ids、field_ids、method_ids、class_defs全部是定长结构便于二分查找而数据区中的字符串、代码等变长内容按需存放。四、DEX 文件头Header详解4.1 DexHeader 结构文件头固定占用0x70112个字节包含 magic 字段、adler32 校验值、SHA-1 哈希值以及各索引区的个数与偏移地址。其 C 结构体定义如下struct DexHeader { u1 magic[8]; /* includes version number */ u4 checksum; /* adler32 checksum */ u1 signature[kSHA1DigestLen]; /* SHA-1 hash */ u4 fileSize; /* length of entire file */ u4 headerSize; /* offset to start of next section */ u4 endianTag; u4 linkSize; u4 linkOff; u4 mapOff; u4 stringIdsSize; u4 stringIdsOff; u4 typeIdsSize; u4 typeIdsOff; u4 protoIdsSize; u4 protoIdsOff; u4 fieldIdsSize; u4 fieldIdsOff; u4 methodIdsSize; u4 methodIdsOff; u4 classDefsSize; u4 classDefsOff; u4 dataSize; u4 dataOff; };4.2 字段逐一说明名称格式说明magicubyte[8] DEX_FILE_MAGIC标识 DEX 文件其中DEX_FILE_MAGIC dex\n035\0checksumuint除magic和此字段之外的文件剩余内容的adler32 校验和用于检测文件损坏情况signatureubyte[20]除magic、checksum和此字段之外的文件内容的SHA-1 签名哈希用于对文件进行唯一标识file_sizeuint整个文件包括文件头的大小以字节为单位header_sizeuint 0x70文件头的大小以字节为单位endian_taguint ENDIAN_CONSTANT字节序标记大端序或小端序link_sizeuint如果此文件未进行静态链接则该值为0反之为链接区段的大小link_offuint如果link_size 0则该值为0反之该偏移量是文件开头到link_data区段的偏移量map_offuint该偏移量必须非零标识从文件开头到data区段的偏移量string_ids_sizeuint字符串标识符列表中的字符串数量string_ids_offuint如果string_ids_size 0一种奇怪的极端情况则该值为0反之表示从文件开头到string_ids的偏移量type_ids_sizeuint类型标识符列表中的元素数量最大为 65535type_ids_offuint如果type_ids_size 0则该值为0反之表示从文件开头到type_ids区段开头的偏移量proto_ids_sizeuint原型方法标识符列表中的元素数量最多为 65535proto_ids_offuint如果proto_ids_size 0则该值为0反之表示文件开头到proto_ids区段开头的偏移量field_ids_sizeuint字段标识符列表中的元素数量field_ids_offuint如果field_ids_size 0则该值为0反之表示文件开头到field_ids区段的偏移量method_ids_sizeuint方法标识符列表中的元素数量method_ids_offuint如果method_ids_size 0则该值为0反之表示从文件开头到method_ids区段的偏移量class_defs_sizeuint类定义列表中的元素数量class_defs_offuint如果class_defs_size 0则该值为0反之表示文件开头到class_defs区段的偏移量data_sizeuintdata区段以字节为单位的大小必须是sizeof(uint)的偶数倍说明按 8 字节对齐data_offuint从文件开头到data区段开头的偏移量4.3 逆向视角的关键字段从逆向和文件校验的角度文件头中三个字段特别值得关注magicdex\n035\0前 8 字节其中035是 DEX 格式版本号。不同 Android 版本可能使用035、037、038、039等识别版本有助于判断对应 ART 的字节码特性checksumadler32覆盖magic之后到文件末尾的内容用于快速检测文件是否损坏或被修改signatureSHA-120 字节覆盖signature字段之后的内容用于唯一标识文件。在对抗 APK 加固与 dex 修改时这两个校验值常被用作完整性校验点篡改后需同步重算。另外endian_tag固定为ENDIAN_CONSTANT小端值为0x12345678若在解析时发现该值与预期不符说明文件使用了反向字节序需要整体翻转解析。五、DEX 索引区六大表索引区由六张定长表组成所有表项都只是指针/索引真正的数据在数据区。这种设计保证索引区可以连续、定长地存放便于快速定位与二分查找。5.1 string_id字符串索引string_ids区段包含stringIdsSize个DexStringId结构struct DexStringId { u4 stringDataOff; /* 字符串数据偏移也就是数据区中各 StringData 的文件偏移 */ };关键点DexStringId中只存储每个字符串的文件偏移相对文件头每个偏移占 4 字节因此字符串索引区总共占用4 * stringIdsSize字节在对应偏移处字符串以MUTF-8Modified UTF-8格式存储开头是一个 uleb128 变量表示字符串长度不含末尾\x00之后紧跟字符串内容最后以\x00结尾。补充MUTF-8 与标准 UTF-8 的差异主要在于对 U0000NUL使用双字节0xC0 0x80编码、对补充平面字符使用代理对surrogate pair编码。由于 NUL 在 DEX 中用作字符串终止符MUTF-8 避免了嵌入 NUL 带来的歧义。5.2 type_id类型索引type_ids区索引了 Java 代码中使用的所有类型类、数组或基本类型。此列表必须按string_id索引排序且不能重复struct DexTypeId { u4 descriptorIdx; /* 指向 DexStringId 列表的索引 */ };descriptorIdx指向字符串索引表例如Ljava/lang/String;的 descriptor 字符串就存放在 string 区type_id 只是其下标。5.3 proto_id方法原型索引proto_ids针对 Java 方法的**原型签名**设计包含返回类型与参数列表不涉及方法名struct DexProtoId { u4 shortyIdx; /* 返回类型参数类型简写指向 DexStringId 列表的索引 */ u4 returnTypeIdx; /* 返回类型指向 DexTypeId 列表的索引 */ u4 parametersOff; /* 参数类型指向 DexTypeList 的偏移 */ } struct DexTypeList { u4 size; /* DexTypeItem 的个数即参数个数 */ DexTypeItem list[1]; /* 指向 DexTypeItem 开始处 */ }; struct DexTypeItem { u2 typeIdx; /* 参数类型指向 DexTypeId 列表的索引最终指向字符串索引 */ };三个字段的语义shortyIdx指向一个简短的字符串如VI、L等描述返回类型参数类型的压缩表示便于快速比较returnTypeIdx返回类型的 type_id 索引parametersOff指向DexTypeList的文件偏移DexTypeList中size给出参数个数list[]中每个DexTypeItem的typeIdx指向 type_ids 表。5.4 field_id字段索引field_ids针对 Java 类中的字段设计struct DexFieldId { u2 classIdx; /* 类的类型指向 DexTypeId 列表的索引 */ u2 typeIdx; /* 字段类型指向 DexTypeId 列表的索引 */ u4 nameIdx; /* 字段名指向 DexStringId 列表的索引 */ };一个字段通过所属类 字段类型 字段名三元组唯一定义三者分别指向 type_ids 与 string_ids 索引表。5.5 method_id方法索引method_ids直接为 Java 方法设计包含方法所在的类、方法原型和方法名struct DexMethodId { u2 classIdx; /* 类的类型指向 DexTypeId 列表的索引 */ u2 protoIdx; /* 声明类型指向 DexProtoId 列表的索引 */ u4 nameIdx; /* 方法名指向 DexStringId 列表的索引 */ };可以看到 method 索引通过类 原型 方法名三元组描述完整的方法签名其中原型又进一步引用 type_ids 与 string_ids。5.6 class_def类定义索引classDefsSize表明 class_def 区域的大小classDefsOff表明其偏移。该区为 Java 中的类设计核心结构如下// 类的基本信息 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; /* 静态字段DexField 结构 */ DexField* instanceFields; /* 实例字段DexField 结构 */ DexMethod* directMethods; /* 直接方法DexMethod 结构 */ DexMethod* virtualMethods; /* 虚方法DexMethod 结构 */ }; // 详细描述类的字段个数与方法个数 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[1]; /* 指令集 */ };从源码结构可以推断出的层次关系DexClassDef是类的门面通过classDataOff指向DexClassDataDexClassData由 header四个计数 静态/实例字段 直接/虚方法四部分组成DexMethod的codeOff指向DexCodeDexCode才是真正存放字节码指令insns[]与寄存器元信息的地方registersSize、insSize、outsSize三个寄存器计数与 smali 中的.registers/.locals/.params声明一一对应。可结合仓库 smali 章节 理解registers_size是 16 位无符号数最大 65535Dalvik 调用方法时据此为该方法的寄存器分配栈空间。outsSize的语义在文档中标注为会在自己的调用栈申请并压栈推测即方法调用其他方法时被调用方参数在调用方栈帧上预留的空间。逆向分析指令流时outsSize往往与invoke-*指令的参数传递方式相关。5.7 小结索引区的指向关系索引区看起来复杂但本质上是一条多级索引链string_ids字符串 ← type_ids类型 ← proto_ids/field_ids/method_ids ← class_defs可以用 Dalvik 设计者在 Google Developer Day 2008 China 演讲中给出的经典图示直观理解这种巧妙设计这张图清楚地展示了各索引表之间的引用关系字符串是最底层的数据类型描述符、字段名、方法名都引用字符串方法原型引用类型方法/字段/类定义再引用前两者。理解这条链是手工解析 DEX 与编写解析工具的基础。六、DEX 数据区数据区存放的是索引区所指的各种真实数据主要包括StringDataMUTF-8 编码的字符串前面由 uleb128 长度前缀TypeList参数/接口类型列表ClassData类的字段与方法概要DexClassDataCodeItem方法字节码DexCode以及注解annotation、调试信息debug info等辅助数据。从文件头来看data_size必须为sizeof(uint)的偶数倍8 字节对齐data_off给出数据区起始偏移。数据区内部各项通过各索引项的*Off字段定位这也是 map section见下节存在的意义——为 Dalvik 虚拟机提供一份内容清单便于一次性映射全部区段。七、DEX map sectionDexHeader中的mapOff字段给出了DexMapList结构在 DEX 文件中的偏移。当 Dalvik 虚拟机解析 DEX 文件后会将内容映射到DexMapList数据结构可以说该结构描述了对应 DEX 文件的整体概况。struct DexMapList { u4 size; /* DexMapItem 的个数方便解析 */ DexMapItem list[1]; /* 指向 DexMapItem */ }; struct DexMapItem { u2 type; /* kDexType 开头的类型 */ u2 unused; /* 未使用用于字节对齐 */ u4 size; /* 指定相应类型的个数 */ u4 offset; /* 指定相应类型的数据的文件偏移 */ }; /* type 字段为一个枚举常量通过类型名称很容易判断它的具体类型。 */ /* map item type codes */ 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, };map section 的用途可以归纳为三点全局索引DexMapList中的每一项都记录某类数据在文件中的偏移 个数相当于整个文件的目录合法性校验枚举值0x0000 ~ 0x0006对应 header 与五大索引区0x1000 ~ 0x1003与0x2000 ~ 0x2006对应数据区中的各种 item。逆向工具如 010 Editor 模板可借助 map 快速列出文件内所有区段并校验偏移是否合法加载优化Dalvik 解析时可根据 map 一次性完成内存映射无需逐个扫描。注意枚举值区间暗示的类别归属0x0000~0x0006是头部与索引区 item0x1000~0x1003是类型列表与注解集合相关0x2000~0x2006是类数据、代码、字符串数据、调试信息、注解与编码数组——这与前文索引区/数据区的三段式划分完全对应。八、实战手工解析一个真实 DEX 文件原文档给出的实战建议是自己找一个 APK然后拿 010 Editor 的模板解析。这里给出完整的实操路径8.1 获取样本任意 APK可从自己手机上提取或使用 SDK 构建一个 hello-world 应用APK 本质是 zip 包其中的classes.dex即目标文件。解压后即可获得 DEX 样本。8.2 使用 010 Editor 解析打开 010 Editor加载 DEX 文件安装/加载 DEX 模板如Dex.bt解析后即可看到与本文结构体一一对应的树形结果先看DexHeader确认 magic 为dex\n035\0、headerSize 为 0x70、endianTag 为 0x12345678依次展开string_ids、type_ids、proto_ids、field_ids、method_ids、class_defs核对各表的大小字段与偏移字段通过map_off找到DexMapList对照 map item 枚举检查各数据区段手动验证一个字符串取string_ids中第一个stringDataOff跳转到对应偏移按uleb128 长度 MUTF-8 字节 \x00的布局读取验证前文所述编码规则。8.3 命令行辅助工具除 010 Editor 外也可以使用现成的开源工具链交叉验证dexdumpAndroid SDK 自带dexdump classes.dex可输出文件头摘要、各索引表与 class 定义baksmali/apktool将 dex 反汇编为 smali 代码把二进制结构还原为可读的.registers、.field、.method声明与DexCode中的寄存器计数相互印证。8.4 与 ODEX 的关系在实际逆向中还会遇到 ODEX 文件——它是 DEX 的优化产物首次加载 dex 时进行优化生成 ODEX 并存放在/data/dalvik-cache目录ROM 自带 app 则直接以 odex 形式存放在 apk 同目录以加快开机启动速度。其具体结构与生成过程可参考仓库中 ODEX 文件 一文的介绍。理解 DEX 是理解 ODEX、以及后续脱壳与修复工作的前提。九、延伸从 DEX 到 Smali 与逆向实战DEX 是二进制格式可读性差逆向工程领域使用 smali 语法作为其汇编级表示。仓库 Smali 章节 指出Android Java/Kotlin 代码最终编译为 dex 字节码由 Dalvik 或 ART 运行时加载执行ART 视情况采用解释执行、JIT 或 AOT 编译借助 apktool、baksmali 可将 dex 反汇编为多个 smali 文件一般一个 smali 文件对应一个 Java 类。从本文的DexCode结构可以看到这种对应的底层依据registersSize寄存器总数、insSize参数个数直接对应 smali 方法声明中的.registers与.locals/.paramsclass_defs中的 accessFlags、staticFields/instanceFields、directMethods/virtualMethods 划分对应 smali 文件中的类信息、字段描述与方法描述triesSize与debugInfoOff对应 smali 中的异常处理.catch与调试信息。因此在 CTF 逆向题中常见思路是先用工具如unzip取出 classes.dex、再用baksmali/jadx将其反汇编/反编译再结合本文的二进制结构知识定位关键类与方法最后在寄存器级smali或源码级jadx 反编译分析算法。参考阅读Android 软件安全与逆向分析仓库 DEX 文件本文核心来源仓库 Smali 语法DEX 字节码的汇编表示仓库 ODEX 文件DEX 的优化形态仓库 Android 中 Java 层的运行机制本主题在仓库中的章节定位【免费下载链接】ctf-wikiCome and join us, we need you!项目地址: https://gitcode.com/gh_mirrors/ct/ctf-wiki创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

相关新闻

Python基础零基础入门:从环境搭建到实战的完整学习路线

Python基础零基础入门:从环境搭建到实战的完整学习路线

如果你现在拿着“Python基础”这四个字在搜索引擎里翻来翻去,大概率已经被“七天速成”“零基础逆袭”这类标题搞得越来越焦虑了。作为一个用Python写了好几年代码、也带过不少新人入门的从业者,我先给你一颗定心丸:Python基础真的不难&#…

2026/9/24 20:27:45 阅读更多 →
Python类机制进阶:属性访问、描述符与元类深入解析

Python类机制进阶:属性访问、描述符与元类深入解析

看到这个标题可能有人会问:面向对象编程写到第四篇,还能讲什么?基础语法、类定义、继承、多态前面都过了一遍,再往下挖,就要碰到 Python 类机制的内裤了。这一篇我打算聊的东西,既基础又经常被忽略——属性…

2026/9/24 20:27:45 阅读更多 →
深入理解Python面向对象编程:从类到魔术方法的实践指南

深入理解Python面向对象编程:从类到魔术方法的实践指南

先说个真实感受:Python我用了好几年,写业务代码、写脚本、做数据清洗都没问题,但真正对面向对象编程产生“原来如此”的顿悟,还是在系统翻完《Python3 面向对象编程(第三版)》之后。网上聊Python OOP的文章…

2026/9/24 20:27:45 阅读更多 →

最新新闻

决策树算法详解:从信息熵到调参实战,理解机器学习基石

决策树算法详解:从信息熵到调参实战,理解机器学习基石

1. 为什么我把决策树当成机器学习的“第一课”在很多机器学习入门资料里,第一个接触的算法往往是线性回归,然后是逻辑回归,一路学到神经网络。但说实话,从我自己的学习经历和后来带新人的经验来看,决策树才是最适合建立…

2026/9/24 21:12:17 阅读更多 →
AI编程实战:构建人机协同的项目纪律系统

AI编程实战:构建人机协同的项目纪律系统

1. 从“写不出第一行代码”到跑通4个AI编程项目的实战路径我第一次打开Cursor时,光是配置Python环境就卡了两小时——不是因为不会装conda,而是根本不确定该用系统Python、pyenv还是直接上Docker。那会儿连requirements.txt里-e .代表什么都要查三遍文档…

2026/9/24 21:12:16 阅读更多 →
Python爬虫必学:接口、JSON与分页实战全解析

Python爬虫必学:接口、JSON与分页实战全解析

很多零基础学Python爬虫的人,真正卡住的地方往往不是requests用不熟,而是这样一个瞬间:网页上明明能看到自己想要的数据,可把抓下来的HTML源码翻个底朝天,就是搜不到目标文本。我第一次遇到这个情况,硬是折…

2026/9/24 21:12:16 阅读更多 →
CNN/VGG/ResNet人脸表情识别实战:从数据到部署全流程

CNN/VGG/ResNet人脸表情识别实战:从数据到部署全流程

简介:面向计算机专业毕业设计与深度学习初学者的完整人脸表情识别项目,以卷积神经网络为核心,覆盖数据预处理、模型搭建、训练评估与实时识别演示的完整流程,可直接用于课程作业、论文写作或实战练手。压缩包共36个文件&#xff0…

2026/9/24 21:12:16 阅读更多 →
工厂焊装车间照明节能改造:KNX照明系统方案分区灯控人体感应

工厂焊装车间照明节能改造:KNX照明系统方案分区灯控人体感应

焊装车间是汽车工厂中照明设计最复杂的场景之一。焊接作业时弧光强烈,而检验工位又要求极高照度——两者对灯光的需求完全不同,用同一套照明方案无法兼顾。据《乘用车工厂焊装车间照明节能设计的探讨》一文披露,一汽大众华北生产基地焊装车间…

2026/9/24 21:12:16 阅读更多 →
结构可靠性分析:从安全系数到失效概率的定量评估

结构可靠性分析:从安全系数到失效概率的定量评估

在结构设计里,最怕的不是算不准,而是你以为自己算得很准。刚工作那会儿,我按规范给一根简支梁取了安全系数2.5,所有验算都满足,结果现场反馈说梁在使用荷载下挠度偏大,局部焊缝还有开裂迹象。复核时我反复检…

2026/9/24 21:11:15 阅读更多 →

日新闻

基于YOLOv8的渔船作业监控系统:从环境搭建到边缘部署全流程

基于YOLOv8的渔船作业监控系统:从环境搭建到边缘部署全流程

简介:这是一套面向计算机、人工智能、自动化等专业学生与教师的毕业设计级项目资源,围绕YOLOv8实现渔船作业监控系统,可用于毕设、课程设计、大作业或项目立项演示。压缩包共97个文件,约24.21MB,以70个Python源码文件为…

2026/9/24 0:00:19 阅读更多 →
单细胞注释实战:基于Scanpy的标记基因与参考映射流程解析

单细胞注释实战:基于Scanpy的标记基因与参考映射流程解析

简介:一份基于单细胞RNA测序数据的细胞类型注释算法研究Python毕业设计源码,针对计算机相关专业正在做毕设或需要项目实战的学习者,可用于课程设计与期末大作业。项目代码完整、经导师指导评审通过,可直接运行,覆盖数据…

2026/9/24 0:00:19 阅读更多 →
C#源生成器实战:用增量生成器替代反射,告别AOT崩溃

C#源生成器实战:用增量生成器替代反射,告别AOT崩溃

第一次在项目里被反射卡住,是在一个老旧的WinForms模块里:几十个类依赖PropertyChanged通知,运行时反射读属性、发通知,每次启动慢半拍不说,一上.NET Native/AOT裁剪模式几乎全面崩盘。后来我把这段逻辑全部改成C#源生…

2026/9/24 0:00:19 阅读更多 →

周新闻

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

直接铺开项目本身吧。这几个月我一直在折腾一件事:用Flutter给OpenHarmony做一款游戏集合类的App,说白了就是把若干小游戏塞进一个壳里,用统一入口分发。这个方向本身不算新鲜,真正让我花了不少心思的,是首页那堆游戏卡…

2026/9/24 14:34:13 阅读更多 →
Word表格编号全攻略:从列表编号到题注交叉引用

Word表格编号全攻略:从列表编号到题注交叉引用

写Word文档,最让人头疼的往往是那些“看起来不起眼”的小问题。比如表格编号这事:今天在表后面多加了两个空白行,明天给客户交稿前发现整个章节的编号全部错位,光是挨个改序号就能耗掉大半个下午。我前阵子帮人整理一份上百页的技…

2026/9/24 9:10:42 阅读更多 →
从第一个站到第二个站:独立开发者的静态网站选型与落地实践

从第一个站到第二个站:独立开发者的静态网站选型与落地实践

1. 项目概述1.1 核心需求解析做独立开发者这几年,说实话,第一个网站上线的那天晚上我兴奋得没睡着。但等它跑了半年,流量惨淡、功能臃肿、代码自己都懒得看第二遍之后,我才慢慢琢磨明白一个道理:第一个网站是练手&…

2026/9/24 14:33:56 阅读更多 →

月新闻

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能分类:[AI/大模型]细分主题:AI 增强型 CI/CD 流水线自动化与 GitOps 实践:Agent 工作流、工具调用与任务拆解:从原型到生产的验收清单很多团队在尝试用大…

2026/9/24 12:50:34 阅读更多 →
容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场分类:[工程技术]细分主题:Kubernetes 生产环境运维与排障实战:可复制的项目复盘模板与决策记录大部分团队的事故复盘报告,最后都变成了躺在 Confluence 或钉…

2026/9/24 14:33:48 阅读更多 →
容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步分类:[工程技术]细分主题:Docker 容器化技术与镜像安全管理:核心链路的逐步实现与关键代码取舍面对一个积累了五六年历史包袱的单体架构应用(包含 Web 接口、后台…

2026/9/24 12:49:17 阅读更多 →