赵云与阿斗》XAPK 逆向魔改完整实战文档

https://img.cdn1.vip/i/6a41dfdea3716_1782702046.webp

一、项目目标

对单机文字塔防游戏《赵云与阿斗》逆向修改,实现无限体力、无限初始馒头、零对局体力消耗、体力无冷却恢复,剔除广告逻辑,最终输出修改版 mod.xapk 安装包。

二、第一步:layadcc2 资源格式逆向解密

1. 资源路径

加密资源目录:assets/cache/dcc2.0/objects/

目录包含 394 个无后缀文件,文件名全部为 SHA1 哈希字符串。

2. layadcc2 文件头部结构(总共 24 字节头部)

表格

偏移位置 占用长度 字段说明 样例数据
0x00 8 Byte 文件魔数 layadcc2
0x08 4 Byte 版本号(小端序 LE) 02 00 00 00
0x0C 8 Byte XOR 加密密钥 e9 b6 86 5f 86 8d 03 2a
0x14 4 Byte 加密数据长度(小端序 LE) -
0x18 往后 剩余全部 加密数据流 逐字节异或加密内容

3. 加解密规则

  • 固定 8 字节密钥:2a038d865f86b6e9
  • 解密公式:明文字节 = 密文字节 ^ 密钥[字节下标 % 8]
  • 批量解密全部文件后,得到核心业务逻辑文件 js/bundle.js(大小 2.8MB,游戏主逻辑载体)

三、第二步:bundle.js 三层混淆逐层脱壳

第一层:\xNN 十六进制转义字符解码

混淆特征:所有字符串被转义为 \xXX 十六进制格式

javascript

运行

1
2
// 解码代码
let decoded = src.replace(/\\x([0-9a-f]{2})/gi, (m, c) => String.fromCharCode(parseInt(c,16)));

示例:this['\x63\x69'] 解码后等价 this['ci']

第二层:字符串数组下标混淆

混淆特征:全部字符串预先存入数组,通过数组下标取值调用

javascript

运行

1
2
var _0x181c = ["\u0072\u0061..."];
_0x87abaa(_0x181c[7]);

处理方案:提取完整字符串映射表,全局批量替换下标调用为原始明文字符串。

第三层:_0x727 自定义栈虚拟机数值混淆

  1. 混淆运算规则

    函数调用格式:

    1
    _0x727([32, A, 32, B, 38, 37])

    等价原生运算:

    1
    A ^ B

    (两数异或运算)

  2. 脱壳方案

    全局匹配该函数调用,直接替换为原生

    1
    A ^ B

    表达式,裸露真实运算数值,方便定点修改。

四、第三步:定位目标变量代码位置

1. 体力类 T(搜索关键词 _stamina

javascript

运行

1
2
3
4
5
6
7
8
T = class {
constructor() {
this['en'] = 30; // 最大体力
this['cn'] = 5; // 每局消耗体力
this['an'] = 300000;// 体力恢复间隔
this['nn'] = 5; // 单次恢复体力值
}
}

2. 馒头货币类 d(Ki 变量,搜索 get Ki()

javascript

运行

1
2
3
4
5
6
7
8
9
d = class {
constructor() {
this['ci'] = 20; // 馒头初始值
this['pi'] = 20;
this['Mi'] = 0;
}
get Ki() { return this['Mi']; }
set Ki(t) { this['Mi'] = t; }
}
  • 变量访问路径:F['instance']()['Gn']['Ki']
  • 关键特性:Ki 属于运行时临时变量,不存入本地存档 JSON,对局结束自动归零,仅修改构造函数初始值可以永久生效

五、第四步:XOR 数值最小侵入式修改方案

1. 异或修改推导公式

原始运算:result = A ^ B

修改目标:将运算结果改为 NewVal

推导新右值:B_new = A ^ NewVal

修改原则:左操作数 A 保持不变,仅替换 B 值,不改动原有代码结构,重加密后兼容性最强,不易程序崩溃。

示例:

原式 327014 ^ 327026 = 20,需要结果改为 99999

plaintext

1
2
newB = 327014 ^ 99999 = 359417
校验:327014 ^ 359417 = 99999

_0x727 封装异或函数同理,仅修改参与运算的对应参数即可。

2. 特殊变量处理(体力恢复间隔 an)

an 未做异或混淆,定义形式为匿名返回函数:

javascript

运行

1
function(){return 300000;}

该字面量全局出现两处,必须携带上下文 \x61\x6e']=function(){return 300000;} 精准唯一匹配替换,避免误修改其他冷却计时变量。

3. 修改收尾

JS 修改完成后,反向套用 XOR 加密算法,对文件重新加密,覆盖原哈希命名的资源文件。

六、第五步:XAPK 重打包踩坑与解决方案

问题 1:apktool 编译重复类报错

报错信息:Class Lcom/facebook/ads/...ColorInfo; has already been interned

成因:Facebook 广告 SDK 以 assets dex 形式重复存放,smali_assets/com/smali_assets/audience_network/classes2/ 存在大量重复类

解决方案:手动删除 com/ 重复目录,运行时定向加载 audience_network/classes2.dex

问题 2:Split APK 分包签名不一致

XAPK 由 base、V1、V2、V3 多个分包组成,安装校验要求全部分包使用同一个签名证书

解决方案:自建 keystore 签名密钥,对所有分包统一重签名。

问题 3:XAPK 打包后无法被安装器识别

APKPure 等主流 XAPK 安装器要求包内文件禁止压缩

解决方案:使用 Python zipfile 开启 ZIP_STORED 无压缩模式打包输出成品。

七、修改前后参数对照表

表格

修改项目 修改前 修改后
初始馒头 (Ki) 20 99999
最大体力上限 30 99999
每局体力消耗 5 0
体力恢复间隔 300 秒 0ms

输出成品:赵云与阿斗_1.0.4_mod.xapk(105MB)

八、实战总结 & 通用逆向经验

  1. layadcc2 防护强度极低,仅固定文件头 + 简单字节异或加密,逆向解密门槛很低,仅做基础防小白篡改。
  2. JS 三层混淆属于 “纸老虎防护”:转义字符、字符串数组、自定义异或虚拟机仅增加阅读难度,理清运算逻辑后可精准还原、定点改值,不会破坏原有程序逻辑。
  3. XOR 原位改值是最优修改方案:仅替换数字、不改动代码结构,程序崩溃概率最低,重加密兼容性最好。
  4. Split XAPK 打包最大难点是签名一致性,多分包必须统一签名,是打包阶段最高频踩坑点。
  5. 单机游戏本地加密、代码混淆都无法有效防篡改;真正防数值修改必须依赖后端数据校验,单机魔改效果仅本地生效。
  6. https://img.cdn1.vip/i/6a41e007372ae_1782702087.webp