fuzz AndroidManifest.xml 实现反编译对抗
字数 1474 2025-08-20 18:17:47
AndroidManifest.xml 二进制文件修改对抗反编译技术研究
1. 技术背景
恶意APK开发者为了防止APK被反编译工具(如apktool)分析,会在AndroidManifest.xml文件中进行特殊处理,使得APK能够正常安装运行,但会导致反编译工具出现异常并退出。这种技术通过修改AndroidManifest.xml二进制文件中的特定位置来实现。
2. AndroidManifest.xml 文件结构
2.1 XML文件结构
AndroidManifest.xml是Android应用程序的清单文件,包含以下主要元素:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.test"
android:versionCode="1"
android:versionName="1.0">
<uses-sdk android:minSdkVersion="14" android:targetSdkVersion="19" />
<uses-permission android:name="android.permission.INTERNET" />
<application
android:allowBackup="false"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme">
<activity android:name="com.android.test.MainActivity" android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
2.2 二进制文件结构
AndroidManifest.xml二进制文件可分为四个主要部分:
- Header:包含文件魔数和文件大小
- String Chunk:字符串资源池
- ResourceId Chunk:系统资源id信息
- XmlContent Chunk:清单文件中的具体信息,包含五个子部分
Header部分
- 魔数(Magic Number):0x00080003(小端表示)
- 文件大小:示例中为0x00000904(2308字节)
3. 对抗技术原理
3.1 已知对抗方法
-
修改魔数:早期版本apktool会检查魔数是否为0x00080003,修改后可导致反编译失败
- 例如修改为00 00 08 00
- 新版本apktool已修复此问题
-
修改字符串池计数(stringCount):
- 修改字符串个数stringCount字段,使其与实际不符
- 导致AndroidManifest.xml解析出现问题
- apktool 2.9.0已修复此问题
3.2 有效对抗位置
通过fuzz测试发现,修改String Offsets数组中的特定位置可以有效对抗反编译:
- 示例位置:0x00000198
- 修改为:0x00005098(使数组越界的下标值)
4. 实现步骤
4.1 环境准备
- 工具:
- 010Editor:用于查看和编辑二进制文件
- apktool:用于测试反编译效果
- jadx:Java反编译工具
- Android SDK:用于安装测试
4.2 操作流程
- 获取原始APK:使用正常APK作为基础
- 解压APK:获取二进制AndroidManifest.xml文件
- 使用010Editor修改:
- 定位到String Offsets数组
- 修改特定偏移量为越界值
- 重新打包:
zip -r modified.apk . - 签名APK:
jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 \ -keystore ./spring.keystore -storepass 123456 \ -keypass 123456 -signedjar sign.apk \ ./app-debug.apk spring - 测试安装:
adb install sign.apk - 测试反编译:
apktool d sign.apk
4.3 自动化fuzz脚本
def apktoolDecode() -> bool:
"""获取apktool的扫描结果"""
apktool_cmd = f"apktool d -f {sign_name}"
process = subprocess.Popen(
apktool_cmd,
shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True
)
# 多线程处理输出
command_completed = threading.Event()
def handle_output(stream, prefix):
for line in stream:
print(f"{prefix}: {line.strip()}")
command_completed.set()
stderr_thread = threading.Thread(target=handle_output, args=(process.stderr, "STDERR"))
stderr_thread.start()
timeout_seconds = 5
command_completed.wait(timeout_seconds)
if not command_completed.is_set():
process.terminate()
return False
else:
process.terminate()
return True
def signApk():
subprocess.run([
'jarsigner', '-verbose',
'-sigalg', 'SHA1withRSA',
'-digestalg', 'SHA1',
'-keystore', "./spring.keystore",
'-storepass', "123456",
'-keypass', "123456",
'-signedjar', "sign.apk",
"./app-debug.apk", "spring"
])
def installApp():
adb_install_cmd = f'adb install {sign_name}'
result = os.system(adb_install_cmd)
return result == 0
5. 技术原理分析
5.1 反编译工具崩溃原因
反编译工具(apktool和jadx)都会抛出java.lang.ArrayIndexOutOfBoundsException异常。
apktool源代码中的关键位置:
private static int[] getUtf16(byte[] array, int offset) {
int val = ((array[offset + 1] & 0xFF) << 8 | array[offset] & 0xFF);
if ((val & 0x8000) != 0) {
int high = (array[offset + 3] & 0xFF) << 8;
int low = (array[offset + 2] & 0xFF);
int len_value = ((val & 0x7FFF) << 16) + (high + low);
return new int[]{4, len_value * 2};
}
return new int[]{2, val * 2};
}
异常发生在:
int val = ((array[offset + 1] & 0xFF) << 8 | array[offset] & 0xFF);
5.2 有效对抗条件
成功的对抗需要满足两个条件:
- 使反编译工具解析时产生异常
- 不影响APK的正常安装和运行
6. 防御措施
对于安全研究人员:
- 使用最新版本的反编译工具(apktool 2.9.0+)
- 对异常位置进行手动修复后再反编译
- 开发自定义解析工具,增加容错处理
对于Android系统:
- 严格验证Manifest文件完整性
- 对异常文件进行更详细的错误报告
7. 扩展思路
除了单字节修改外,还可以尝试:
- 多字节组合修改
- 增加或删除部分内容
- 修改文件结构中的其他关键字段
- 结合多种对抗技术