安卓修改大师可以在没有源代码的情况下,直接反编译已经打包的APK安装包,通过修改SMALI代码实现添加和去除部分功能,并在应用的任何地方添加任意代码,增加任意任何您想实现的功能。通过这种方式,把该应用变为您自己的应用(通过安卓修改大师反编译生成的新应用仅供个人学习反编译知识,严禁用于商业用途)。
安卓修改大师提供海量的应用、游戏和电子书作为修改模板,您只需要在安卓修改大师中找到想要修改的应用,点击该应用的“安装”或者“一键安装”按钮,即可自动打开相关的反编译选项。
本教程通过对最近流行的“最美手电筒”应用进行反编译并重新打包,实现在用户按下关闭手电筒按钮的时候,弹出一个提示弹窗,提示用户“哈哈~你必须付钱才能关掉我,您确定现在就要付钱吗?”,如果用户选择确定,将打开设置的网页,如果用户选择取消,将关闭应用。
好了,闲话少说,我来开始演示如何进行反编译和添加自定义代码吧~~
第一步,在安卓修改大师顶部的搜索框输入“手电筒”并开始搜索(见下图),搜索列表中出现很多匹配到“手电筒”关键字的应用,点击“最美手电筒”后面的一键安装按钮, 系统会自动弹出相关的反编译选项菜单,可以通过本菜单将应用安装到手机,或者查看源代码,也可以直接进行反编译。点击菜单中的“反编译”按钮,进入到该应用的反编译界面。
由于我们要实现的是点击关闭按钮后弹出一个提示窗口,因此我们需要先找到这个关闭按钮的图片资源,以便通过资源名称找到相应的界面和调用方法(如果是文字按钮,就更简单了,直接在安卓修改大师的搜索功能里面直接搜索按钮文字即可)。最美手电筒的原始界面如下。
我们在安卓修改大师的左侧点击高级模式,然后一层层展开目录列表,在drawable相关的目录里面浏览图片,找到和应用运行界面图对应的图片资源,如下图所示,分别是关闭状态和打开状态的图片。我们是要针对关闭按钮进行操作,所以关注一下关闭按钮的图片资源名称是 off 。
通过搜索功能或者直接在高级模式中浏览layout目录,打开相应的界面布局,我们仔细观察就可以找到Home.xml是这个最美手电筒的主界面,这个xml里面包含了上述的关闭按钮的图片资源名称。
双击打开Home.xml,我们可以清晰的看到这个按钮的图片资源名称和点击后调用的方法名称“switchTorch”,见下图。这个调用方法名称很重要,我们通过这个名称作为入口查询该方法所在的代码页面。
点击安卓修改大师的左侧搜索/替换功能,打开的界面中输入“switchTorch”进行搜索,将搜索到包含该关键字的所有的代码,通过双击打开每个代码页面分析,很快就能找到该方法所在的页面,如下图选中的一行。
双击打开该代码页面,可以看到,已经自动定位到了该调用方法,后续将通过在该方法里面添加代码,来实现弹出提示窗口的功能。
将该代码页面拉到顶部,第一行定义了该类名的包含包名的全路径,下图红框内的路径极为重要,后面很多地方会用到,需要记录下来。
“.class public Lcom/nanshan/simpletorch/home/HomeActivity;”
其中,红颜色部分是包路径,蓝色部分是调用的本类的类名。
下面,就开始最核心的代码移植的工作了。点击高级模式,目录浏览中,按照上面获得的包名,依次从smali展开,逐级点击打开目录,一直到能看到上面获得那个类名的文件。然后点击右下角的”打开所在目录“,将打开包名对应的磁盘目录。
现在来讲解一下要实现的功能对应的代码。安卓应用的反编译代码移植采用的是smali语言,并非Java语言,需要您有一定的Smali的基础知识。当然,如果您现在还完全不懂smali语言,也没有关系,按照下面的教程拷贝粘贴就可以了。
由于要实现的是提示弹窗功能,该功能有两个按钮,每个按钮对应独立的功能。在Smali里面,一般一个独立的功能类对应一个独立的文件。本功能对应的代码片段有三个,一个在关闭按钮点下的时候调用的代码,另外两个分别对应的是确定和取消的代码。
上述功能对应的java代码为:
AlertDialog dialog = new AlertDialog.Builder(this) .setTitle("提示")// 设置对话框的标题 .setMessage("哈哈~你必须付钱才能关掉我,您确定现在就要付钱吗?")// 设置对话框的内容 // 设置对话框的按钮 .setNegativeButton("取消", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { dialog.dismiss(); } }).setPositiveButton("确定", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { Uri uri = Uri.parse("http://www.apkeditor.cn/pay/default.aspx"); Intent intent = new Intent(Intent.ACTION_VIEW, uri); startActivity(intent); dialog.dismiss(); } }).create(); dialog.show(); |
由于安卓反编译后修改逻辑必须在Smali文件里面修改,因此需要将上述代码转换成Smali代码。下面已经转换为Smali代码,只需要复制粘贴即可。后续会出详细教程讲解如何将Java代码生成Smali代码。
下面是取消按钮对应的代码,请在上述包名对应的目录里面创建一个文本文件,命名为” ApkEditorLoader$1.smali “,将下面的代码拷贝到里面,并将代码中所有的” Lcom/txeasy/shoudiantong/ “替换为之前获得的包路径,将代码中所有的” MainActivity “替换为之前获得的类名。后面两份代码也相同操作。
.class Lcom/txeasy/shoudiantong/ApkEditorLoader$1; .super Ljava/lang/Object; .source "MainActivity.java" # interfaces .implements Landroid/content/DialogInterface$OnClickListener; # annotations .annotation system Ldalvik/annotation/EnclosingMethod; value = Lcom/txeasy/shoudiantong/MainActivity;->onCreate(Landroid/os/Bundle;)V .end annotation .annotation system Ldalvik/annotation/InnerClass; accessFlags = 0x0 name = null .end annotation # instance fields .field final synthetic this$0:Lcom/txeasy/shoudiantong/MainActivity; # direct methods .method constructor .locals 0 .prologue iput-object p1, p0, Lcom/txeasy/shoudiantong/ApkEditorLoader$1;->this$0:Lcom/txeasy/shoudiantong/MainActivity; invoke-direct {p0}, Ljava/lang/Object;-> return-void .end method # virtual methods .method public onClick(Landroid/content/DialogInterface;I)V .locals 0 .param p1, "dialog" # Landroid/content/DialogInterface; .param p2, "which" # I .prologue invoke-interface {p1}, Landroid/content/DialogInterface;->dismiss()V return-void .end method |
确定按钮对应的代码,请在上述包名对应的目录里面创建一个文本文件,命名为” ApkEditorLoader$2.smali “,代码中的包名和类名按照前述方法进行替换。
.class Lcom/txeasy/shoudiantong/ApkEditorLoader$2; .super Ljava/lang/Object; .source "MainActivity.java" # interfaces .implements Landroid/content/DialogInterface$OnClickListener; # annotations .annotation system Ldalvik/annotation/EnclosingMethod; value = Lcom/txeasy/shoudiantong/MainActivity;->onCreate(Landroid/os/Bundle;)V .end annotation .annotation system Ldalvik/annotation/InnerClass; accessFlags = 0x0 name = null .end annotation # instance fields .field final synthetic this$0:Lcom/txeasy/shoudiantong/MainActivity; # direct methods .method constructor .locals 0 .prologue iput-object p1, p0, Lcom/txeasy/shoudiantong/ApkEditorLoader$2;->this$0:Lcom/txeasy/shoudiantong/MainActivity; invoke-direct {p0}, Ljava/lang/Object;-> return-void .end method # virtual methods .method public onClick(Landroid/content/DialogInterface;I)V .locals 3 .param p1, "dialog" # Landroid/content/DialogInterface; .param p2, "which" # I .prologue const-string v2, "http://www.apkeditor.cn/pay/default.aspx?username=xiaoping" invoke-static {v2}, Landroid/net/Uri;->parse(Ljava/lang/String;)Landroid/net/Uri; move-result-object v1 .local v1, "uri":Landroid/net/Uri; new-instance v0, Landroid/content/Intent; const-string v2, "android.intent.action.VIEW" invoke-direct {v0, v2, v1}, Landroid/content/Intent;-> .local v0, "intent":Landroid/content/Intent; iget-object v2, p0, Lcom/txeasy/shoudiantong/ApkEditorLoader$2;->this$0:Lcom/txeasy/shoudiantong/MainActivity; invoke-virtual {v2, v0}, Lcom/txeasy/shoudiantong/MainActivity;->startActivity(Landroid/content/Intent;)V invoke-interface {p1}, Landroid/content/DialogInterface;->dismiss()V return-void .end method |
需要植入到取消按钮对应的方法里面的代码,代码中的包路径和类名按照前述方法进行替换后植入。
move-object/from16 v0, p0 new-instance v1, Landroid/app/AlertDialog$Builder; invoke-direct {v1, v0}, Landroid/app/AlertDialog$Builder;-> const-string v2, "\u63d0\u793a" invoke-virtual {v1, v2}, Landroid/app/AlertDialog$Builder;->setTitle(Ljava/lang/CharSequence;)Landroid/app/AlertDialog$Builder; move-result-object v1 const-string v2, "\u60a8\u6b63\u5728\u8fd0\u884c\u7684\u7a0b\u5e8f\u662f\u7531\u201c\u5b89\u5353\u4fee\u6539\u5927\u5e08\u201d\u53cd\u7f16\u8bd1\u751f\u6210\uff0c\u672a\u4ed8\u8d39\u7528\u6237\u751f\u6210\u7684\u5e94\u7528\u4f1a\u5f39\u51fa\u672c\u63d0\u793a\u7a97\u53e3\uff0c\u60a8\u9700\u8981\u73b0\u5728\u5c31\u5347\u7ea7\u201c\u5b89\u5353\u4fee\u6539\u5927\u5e08\u201d\u7684VIP\u4f1a\u5458\u5417\uff1f" invoke-virtual {v1, v2}, Landroid/app/AlertDialog$Builder;->setMessage(Ljava/lang/CharSequence;)Landroid/app/AlertDialog$Builder; move-result-object v1 const-string v2, "\u53d6\u6d88" new-instance v3, Lcom/txeasy/shoudiantong/ApkEditorLoader$1; invoke-direct {v3, v0}, Lcom/txeasy/shoudiantong/ApkEditorLoader$1;-> invoke-virtual {v1, v2, v3}, Landroid/app/AlertDialog$Builder;->setNegativeButton(Ljava/lang/CharSequence;Landroid/content/DialogInterface$OnClickListener;)Landroid/app/AlertDialog$Builder; move-result-object v1 const-string v2, "\u786e\u5b9a" new-instance v3, Lcom/txeasy/shoudiantong/ApkEditorLoader$2; invoke-direct {v3, v0}, Lcom/txeasy/shoudiantong/ApkEditorLoader$2;-> invoke-virtual {v1, v2, v3}, Landroid/app/AlertDialog$Builder;->setPositiveButton(Ljava/lang/CharSequence;Landroid/content/DialogInterface$OnClickListener;)Landroid/app/AlertDialog$Builder; move-result-object v1 invoke-virtual {v1}, Landroid/app/AlertDialog$Builder;->create()Landroid/app/AlertDialog; move-result-object v0 .local v0, "dialog":Landroid/app/AlertDialog; invoke-virtual {v0}, Landroid/app/AlertDialog;->show()V |
上述代码的蓝色部分是提示信息,该信息必须为Unicode格式,例如下面的一句话:
哈哈~你必须付钱才能关掉我,您确定现在就要付钱吗?
对应的Unicode是(请自行百度“Unicode编码转换”,会找到一个转换的网站,输入中文会自动转换为unicode,拷贝过来替换即可):
\u54c8\u54c8\uff5e\u4f60\u5fc5\u987b\u4ed8\u94b1\u624d\u80fd\u5173\u6389\u6211\uff0c\u60a8\u786e\u5b9a\u73b0\u5728\u5c31\u8981\u4ed8\u94b1\u5417\uff1f
替换包路径和类名的方法,如下图:
创建好的取消和确认按钮对应的代码文件如下所示:
需要插入代码的方法(该方法如何定位,前面已经讲过),由于是插入到取消按钮点击后,因此将我们的植入代码拷贝到off方法的调用后面。
插入后的样子:
好了,到此为止已经基本大功告成了,我们点击安卓修改大师左侧的打包/签名功能,开始进行打包环节,打包后,系统会自动提示你是否需要安装到手机上。
下面就是我们的最终成果,安装到手机上并点击关闭手电筒按钮出现的效果。
您可以用这种方法反编译任何安卓应用哦。只要您愿意,您可以在任何应用上面添加任何功能。后面我们会出教程教大家如何添加任意功能,包括如何进行VIP会员的破解等等。
安卓优化大师非常强大,有更多更好玩的方法来折腾任何应用。都来玩玩看吧~我相信你一定能够玩出你的新花样。后面我们会推出更多的实例教程,教您如何破解游戏关卡,甚至在应用中添加自己的功能。