cocos2dx反编译

2015年03月06日 17:04 0 点赞 0 评论 更新于 2020-01-16 15:40

在软件开发与安全研究领域,反编译是一项重要的技术手段。近期我发现,对 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 项目

  1. 选择 File -> New -> Project -> Java Project -> Next
  2. 为项目随意命名,取消勾选 Use default location 选项,将 Location 设置为 out 文件夹,然后点击 Next
  3. smali 文件夹设置为 Source Folder,最后点击 Finish

8. 设置断点

在 Eclipse 中,打开第 2 步找到的主类文件,找到 onCreate 方法,在 waitForDebugger 后面的第一个方法处添加断点。

9. 打开 DDMS

打开 DDMS(路径为 %android-sdks%\\tools\\ddms.bat),如果在第 6 步中成功运行了修改后的程序,在 DDMS 的设备列表中会显示可调试的程序。对应程序的最后一栏会显示类似 8600/8700 的信息,其中 8600 即为调试该程序的端口。

10. 配置远程调试

回到 Eclipse,配置远程调试:

  1. 选择菜单 Run -> Debug -> Debug Configurations
  2. 双击 Remote Java Application,将 Host 设置为默认的 localhostPort 填写第 9 步得到的调试端口(如 8600),然后点击 Apply -> Debug

11. 开始调试

此时,Eclipse 会自动切换至调试视图,程序将运行并中断在下一行可执行的代码处,你可以直接查看相关变量的值。

总结

通过上述步骤,我们可以使用 Eclipse 对 smali 代码进行调试。不过,从程序开头开始调试到我们关心的代码位置可能比较麻烦。建议先使用 jd-gui 等软件直接查看反编译后的 Java 代码,确定要调试的位置后,再在 smali 代码中定位断点并进行实时调试,这样可以提高调试效率。如果不需要在程序开头进行调试,建议将第 3 步中添加的代码注释掉。

作者信息

boke

boke

共发布了 3994 篇文章