资源文件逆向与界面篡改技术

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 游戏界面篡改

目标:修改《某游戏》血条显示逻辑
步骤

  1. 定位血条控件ID:0x7f0a00d3

  2. 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等大厂都工作过。当下为退役状态,写此篇文章属个人爱好。本人开发期间收集了很多开发课程等资料,需要可联系我