cocos2dx反编译
在软件开发与安全研究领域,反编译是一项重要的技术手段。近期我发现,对 APK 进行反编译不仅操作便捷,还能在修改后重新编译运行,这相较于在 Windows 系统下修改 PE 文件容易许多。在此,要感谢 apktool 和 smali 工具的开发者,为我们提供了如此出色的工具。
跟踪 APK 的常规方法及实时调试需求
通常,跟踪 APK 的做法是在反编译后的 smali 代码中插入日志输出,然后重新编译运行并查看输出日志。然而,这种方法既耗时又费力。若能实现实时调试,将极大提高效率。经搜索,目前较为可行的方法是使用 NetBeans + DDMS。我曾尝试使用该方法进行调试,但对 NetBeans 的操作不太熟悉。不过,Eclipse 是许多开发者常用的工具,其调试设置与 NetBeans 大同小异。
调试步骤
1. 反编译 APK 生成可调试的 smali 代码
使用 apktool 对 APK 进行反编译,将可调试的 smali 代码输出到 out 文件夹。目前,apktool 的最新版本是 2.0.0b7,执行以下命令:
java -jar apktool_2.0.0b7.jar d -d test.apk -o out
需要注意的是,必须使用 -d 参数,这样反编译出来的代码后缀均为 .java,因为只有 Java 文件才能被 Eclipse 或 NetBeans 识别并用于调试。
2. 设置调试标记并确定主类
在输出的 out 文件夹中,使用文本编辑工具打开 AndroidManifest.xml 文件,在 <application> 节点中添加属性 android:debuggable="true",示例如下:
<application android:debuggable="true" ...>
接着,在 AndroidManifest.xml 中搜索以下关键字:
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
找到包含上述信息的 <activity> 节点,记录其 android:name 属性的值,该值即为应用的主类。例如:
<?xml version="1.0" encoding="utf-8" standalone="no"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.acids.helloworld">
<application android:debuggable="true" android:allowBackup="true" android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme">
<activity android:label="@string/app_name" android:name="com.acids.helloworld.MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
</application>
</manifest>
在上述示例中,主类为 com.acids.helloworld.MainActivity。
3. 在主类的 onCreate 事件中添加调试等待
使用文本编辑工具打开主类文件,找到 onCreate 方法,在第一句前插入以下代码:
invoke-static {}, Landroid/os/Debug;->waitForDebugger()V
为保持代码格式一致,需添加 a=0;// 前缀,最终结果如下:
a=0;// # virtual methods
a=0;// .method protected onCreate(Landroid/os/Bundle;)V
a=0;// invoke-static {}, Landroid/os/Debug;->waitForDebugger()V
a=0;//
a=0;// .locals 1
a=0;// .param p1, "savedInstanceState" # Landroid/os/Bundle;
a=0;//
a=0;// .prologue
a=0;// .line 11
a=0;// invoke-super {p0, p1}, Landroid/app/Activity;->onCreate(Landroid/os/Bundle;)V
4. 重新编译打包 APK
保存文件后,使用 apktool 重新编译打包为 debug.apk,执行以下命令:
java -jar apktool_2.0.0b7.jar b -d out -o debug.apk
5. 对 APK 进行签名
对 debug.apk 进行签名(需要下载签名工具),我将签名工具放在了 signapk 文件夹下,生成 debug.sign.apk,执行以下命令:
java -jar .\\signapk\\signapk.jar .\\signapk\\testkey.x509.pem .\\signapk\\testkey.pk8 .\\debug.apk .\\debug.sign.apk
6. 上传并运行 APK
将 debug.sign.apk 上传至手机或模拟器,然后安装并运行。此时,程序运行后会停留在白屏界面,请勿操作设备或退出程序,因为程序此时运行到了之前添加的 waitForDebugger 代码处,该代码的作用是使程序挂起,等待调试器连接。
7. 启动 Eclipse 并构建 Java 项目
- 选择
File->New->Project->Java Project->Next。 - 为项目随意命名,取消勾选
Use default location选项,将Location设置为out文件夹,然后点击Next。 - 将
smali文件夹设置为Source Folder,最后点击Finish。
8. 设置断点
在 Eclipse 中,打开第 2 步找到的主类文件,找到 onCreate 方法,在 waitForDebugger 后面的第一个方法处添加断点。
9. 打开 DDMS
打开 DDMS(路径为 %android-sdks%\\tools\\ddms.bat),如果在第 6 步中成功运行了修改后的程序,在 DDMS 的设备列表中会显示可调试的程序。对应程序的最后一栏会显示类似 8600/8700 的信息,其中 8600 即为调试该程序的端口。
10. 配置远程调试
回到 Eclipse,配置远程调试:
- 选择菜单
Run->Debug->Debug Configurations。 - 双击
Remote Java Application,将Host设置为默认的localhost,Port填写第 9 步得到的调试端口(如8600),然后点击Apply->Debug。
11. 开始调试
此时,Eclipse 会自动切换至调试视图,程序将运行并中断在下一行可执行的代码处,你可以直接查看相关变量的值。
总结
通过上述步骤,我们可以使用 Eclipse 对 smali 代码进行调试。不过,从程序开头开始调试到我们关心的代码位置可能比较麻烦。建议先使用 jd-gui 等软件直接查看反编译后的 Java 代码,确定要调试的位置后,再在 smali 代码中定位断点并进行实时调试,这样可以提高调试效率。如果不需要在程序开头进行调试,建议将第 3 步中添加的代码注释掉。