1. 资源逆向工程基础
1.1 APK资源架构解析
资源文件组成:
resources.arsc:编译后的资源索引表
res/
├── layout/ # XML布局(编译为二进制)
├── drawable/ # 图片与矢量图
├── values/ # 字符串/颜色/尺寸等
└── raw/ # 原生资源文件
资源ID映射规则:
public final class R {
public static final class layout {
public static final int main_activity = 0x7f0a001c;
}
public static final class string {
public static final int app_name = 0x7f0b0001;
}
}
2. 资源反编译与重构
2.1 资源逆向工具链
工具名称 | 功能 | 使用场景 |
---|---|---|
Apktool | 反编译资源为可读格式 | 整体资源逆向 |
AndroResGuard | 资源混淆与反混淆 | 对抗资源保护方案 |
Android Studio | 布局动态预览 | 实时界面分析 |
ResEdit | ARSC文件编辑 | 直接修改资源索引表 |
Apktool反编译实战:
apktool d target.apk -o output_dir --no-src
# 关键参数:
# -s 保留dex不反编译
# -r 跳过资源解码(快速提取)
3. 布局文件逆向与篡改
3.1 二进制XML解析
AXML解析脚本:
from xml.etree.ElementTree import parse
from axmlparser import AXMLParser
with open('layout/main_activity.xml', 'rb') as f:
parser = AXMLParser(f.read())
root = parser.get_xml_obj()
for elem in root.iter('TextView'):
if elem.attrib.get('android:id') == '@id/title':
elem.attrib['android:text'] = "Hacked Title"
3.2 动态布局注入
Xposed动态修改方案:
XposedHelpers.findAndHookMethod(
"android.view.LayoutInflater",
lpparam.classLoader,
"inflate",
int.class,
ViewGroup.class,
boolean.class,
new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) {
View root = (View) param.getResult();
TextView tv = root.findViewById(R.id.title);
tv.setText("动态注入文本");
tv.setOnClickListener(v -> {
Toast.makeText(v.getContext(), "界面已被篡改", Toast.LENGTH_SHORT).show();
});
}
}
);
4. 图片资源提取与替换
4.1 图片资源定位技巧
自动化扫描脚本:
import zipfile
from PIL import Image
with zipfile.ZipFile('target.apk') as z:
for name in z.namelist():
if name.startswith('res/drawable') and name.endswith('.png'):
with z.open(name) as f:
img = Image.open(f)
print(f"尺寸: {img.size} | 路径: {name}")
img.save(f"extracted/{name.split('/')[-1]}")
4.2 实时资源替换
Frida内存替换技术:
const res = Java.use('android.content.res.Resources');
res.getDrawable.implementation = function(id) {
if (id === 0x7f020001) { // 目标资源ID
return Java.use('android.graphics.drawable.BitmapDrawable').$new(
this,
load_custom_image()
);
}
return this.getDrawable(id);
};
5. 多语言资源篡改
5.1 字符串资源映射表
多语言文件结构:
res/
├── values/strings.xml # 默认语言
├── values-zh/strings.xml # 中文
└── values-en/strings.xml # 英文
批量修改脚本:
import xml.etree.ElementTree as ET
for lang in ['values', 'values-zh']:
tree = ET.parse(f'{lang}/strings.xml')
root = tree.getroot()
for elem in root.findall('string'):
if elem.get('name') == 'app_name':
elem.text = "修改后名称"
tree.write(f'{lang}/strings.xml', encoding='utf-8')
5.2 运行时语言切换
Hook资源管理器:
XposedHelpers.findAndHookMethod(
"android.content.res.Resources",
lpparam.classLoader,
"getString",
int.class,
new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) {
int id = (int) param.args[0];
if (id == R.string.welcome_message) {
param.setResult("欢迎使用破解版");
}
}
}
);
6. 资源混淆对抗
6.1 资源保护方案解析
AndroResGuard配置:
<!-- androguard.yml -->
resource:
keep:
- R.drawable.ic_launcher
mappingFile: resource_mapping.txt
compress:
enable: true
filePattern: "*.png"
6.2 反混淆实战
资源ID映射恢复:
def restore_res_mapping(arsc_path, mapping):
with open(arsc_path, 'r+b') as f:
data = f.read()
for obf_name, real_name in mapping.items():
data = data.replace(obf_name.encode(), real_name.encode())
f.seek(0)
f.write(data)
7. 企业级实战案例
7.1 游戏界面篡改
目标:修改《某游戏》血条显示逻辑
步骤:
-
定位血条控件ID:
0x7f0a00d3
-
Hook布局加载过程:
XposedHelpers.findAndHookMethod(
"com.game.ui.HUDView",
lpparam.classLoader,
"onDraw",
Canvas.class,
new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) {
ProgressBar hpBar = (ProgressBar) ((View) param.thisObject)
.findViewById(0x7f0a00d3);
hpBar.setProgress(100); // 锁定满血
}
}
);
7.2 视频APP去广告
关键Hook点:
// 隐藏广告View
XposedHelpers.findAndHookMethod(
"com.video.ad.AdManager",
lpparam.classLoader,
"showBannerAd",
Activity.class,
new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) {
param.setResult(null); // 拦截广告显示
}
}
);
8. 防护与检测技术
8.1 资源完整性校验
CRC校验实现:
public class ResourceCheck {
public static boolean verifyResources() {
long crc = calculateCRC(R.raw.critical_resource);
return crc == 0x12345678L;
}
}
绕过方案:
Java.perform(() => {
const R = Java.use('com.target.app.R$raw');
const CheckClass = Java.use('com.target.app.ResourceCheck');
CheckClass.verifyResources.implementation = function() {
return true; // 强制返回验证通过
};
});
8.2 动态资源加载
加密资源解密方案:
InputStream encrypted = getAssets().open("encrypted_layout.bin");
byte[] data = decrypt(encrypted, getKey());
View view = getLayoutInflater().inflate(new XmlPullParser(data));
技术验证清单:
-
成功提取并修改APK图片资源
-
实现动态布局元素篡改
-
完成多语言资源批量替换
-
绕过资源混淆保护机制
-
复现企业级界面劫持案例
本章所有实验需在授权测试应用(如开源APP)上完成,严禁对未授权商业软件实施逆向操作。资源修改仅限于界面展示层,禁止篡改核心功能逻辑。
关于作者:
15年互联网开发、带过10-20人的团队,多次帮助公司从0到1完成项目开发,在TX等大厂都工作过。当下为退役状态,写此篇文章属个人爱好。本人开发期间收集了很多开发课程等资料,需要可联系我