public class MainActivity extends AppCompatActivity implements View.OnClickListener {private static final String TAG = "MainActivity";private Button btn;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);btn = (Button) findViewById(R.id.btn);btn.setOnClickListener(this);}@Overridepublic void onClick(View v) {Log.d(TAG,"Button is clicked");}} 将这个工程编译生成的apk解压,取出其中的classes.dex放在dex2jar工具的目录下,然后执行命令

然后打开jd-gui,将class-dex2jar.jar文件拖进去,就可以看到反编译出来的源代码。
可以看到反编译的代码和原本的代码差别不大,主要差别是原来的资源引用全都变成了数字。
下面我们来修改这个apk的内容。
首先我们将apk拷贝到apktool工具目录下,执行命令apktool d app-release.apk。
生成的目录中包含smali文件夹

然后找到我们的主要的类MainActivity.smali,文件内容如下:
.class public Lcom/viclee/decompiledemo/MainActivity;.super Landroid/support/v7/app/AppCompatActivity;.source "MainActivity.java"# interfaces.implements Landroid/view/View$OnClickListener;# static fields.field private static final TAG:Ljava/lang/String; = "MainActivity"# instance fields.field private btn:Landroid/widget/Button;# direct methods.method public constructor <init>()V.locals 0.prologue.line 9invoke-direct {p0}, Landroid/support/v7/app/AppCompatActivity;-><init>()Vreturn-void.end method# virtual methods.method public onClick(Landroid/view/View;)V.locals 2.param p1, "v"# Landroid/view/View;.prologue.line 23const-string v0, "MainActivity"const-string v1, "Button is clicked"invoke-static {v0, v1}, Landroid/util/Log;->d(Ljava/lang/String;Ljava/lang/String;)I.line 24return-void.end method.method protected onCreate(Landroid/os/Bundle;)V.locals 1.param p1, "savedInstanceState"# Landroid/os/Bundle;.prologue.line 14invoke-super {p0, p1}, Landroid/support/v7/app/AppCompatActivity;->onCreate(Landroid/os/Bundle;)V.line 15const v0, 0x7f040019invoke-virtual {p0, v0}, Lcom/viclee/decompiledemo/MainActivity;->setContentView(I)V.line 17const v0, 0x7f0c0050invoke-virtual {p0, v0}, Lcom/viclee/decompiledemo/MainActivity;->findViewById(I)Landroid/view/View;move-result-object v0check-cast v0, Landroid/widget/Button;iput-object v0, p0, Lcom/viclee/decompiledemo/MainActivity;->btn:Landroid/widget/Button;.line 18iget-object v0, p0, Lcom/viclee/decompiledemo/MainActivity;->btn:Landroid/widget/Button;invoke-virtual {v0, p0}, Landroid/widget/Button;->setOnClickListener(Landroid/view/View$OnClickListener;)V.line 19return-void.end method 其中36-40行是打印日志的位置,文件内容很清晰,每个区域的意义如下:public class MainActivity extends AppCompatActivity{@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);showToast();}public void showToast() {Toast.makeText(this,"我是反编译后进行的修改。",Toast.LENGTH_LONG).show();}} 然后像前面一样执行apktool命令,生成的smali文件内容如下:.class public Lcom/viclee/decompiledemo/MainActivity;.super Landroid/support/v7/app/AppCompatActivity;.source "MainActivity.java"# direct methods.method public constructor <init>()V.locals 0.prologue.line 7invoke-direct {p0}, Landroid/support/v7/app/AppCompatActivity;-><init>()Vreturn-void.end method# virtual methods.method protected onCreate(Landroid/os/Bundle;)V.locals 1.param p1, "savedInstanceState"# Landroid/os/Bundle;.prologue.line 10invoke-super {p0, p1}, Landroid/support/v7/app/AppCompatActivity;->onCreate(Landroid/os/Bundle;)V.line 11const v0, 0x7f040019invoke-virtual {p0, v0}, Lcom/viclee/decompiledemo/MainActivity;->setContentView(I)V.line 13invoke-virtual {p0}, Lcom/viclee/decompiledemo/MainActivity;->showToast()V.line 14return-void.end method.method public showToast()V.locals 2.prologue.line 17const-string v0, "u6211u662fu53cdu7f16u8bd1u540eu8fdbu884cu7684u4feeu6539u3002"const/4 v1, 0x1invoke-static {p0, v0, v1}, Landroid/widget/Toast;->makeText(Landroid/content/Context;Ljava/lang/CharSequence;I)Landroid/widget/Toast;move-result-object v0invoke-virtual {v0}, Landroid/widget/Toast;->show()V.line 18return-void.end method 上面代码中,33、39-56行就是弹出Toast的代码部分。将上面整个showToast方法拷贝到原始工程的smali文件中,这里要特别注意修改行号,这个行号表示的是代码在原始Java文件中的行号,需要参考两个smali文件的行号来修改。我认为只要保证方法内的行号不乱序,并且方法之间的行号不冲突就可以。然后,需要将原始工程中打印日志的代码替换为显示Toast的代码,也就是将原始smali文件中36-40行修改为新建工程中33、39-56行的内容。修改后的内容如下,主要关注下面内容中36行、75-91行与原始smali文件的差异。.class public Lcom/viclee/decompiledemo/MainActivity;.super Landroid/support/v7/app/AppCompatActivity;.source "MainActivity.java"# interfaces.implements Landroid/view/View$OnClickListener;# static fields.field private static final TAG:Ljava/lang/String; = "MainActivity"# instance fields.field private btn:Landroid/widget/Button;# direct methods.method public constructor <init>()V.locals 0.prologue.line 9invoke-direct {p0}, Landroid/support/v7/app/AppCompatActivity;-><init>()Vreturn-void.end method# virtual methods.method public onClick(Landroid/view/View;)V.locals 2.param p1, "v"# Landroid/view/View;.prologue.line 23invoke-virtual {p0}, Lcom/viclee/decompiledemo/MainActivity;->showToast()V.line 24return-void.end method.method protected onCreate(Landroid/os/Bundle;)V.locals 1.param p1, "savedInstanceState"# Landroid/os/Bundle;.prologue.line 14invoke-super {p0, p1}, Landroid/support/v7/app/AppCompatActivity;->onCreate(Landroid/os/Bundle;)V.line 15const v0, 0x7f040019invoke-virtual {p0, v0}, Lcom/viclee/decompiledemo/MainActivity;->setContentView(I)V.line 17const v0, 0x7f0c0050invoke-virtual {p0, v0}, Lcom/viclee/decompiledemo/MainActivity;->findViewById(I)Landroid/view/View;move-result-object v0check-cast v0, Landroid/widget/Button;iput-object v0, p0, Lcom/viclee/decompiledemo/MainActivity;->btn:Landroid/widget/Button;.line 18iget-object v0, p0, Lcom/viclee/decompiledemo/MainActivity;->btn:Landroid/widget/Button;invoke-virtual {v0, p0}, Landroid/widget/Button;->setOnClickListener(Landroid/view/View$OnClickListener;)V.line 19return-void.end method.method public showToast()V.locals 2.prologue.line 27const-string v0, "u6211u662fu53cdu7f16u8bd1u540eu8fdbu884cu7684u4feeu6539u3002"const/4 v1, 0x1invoke-static {p0, v0, v1}, Landroid/widget/Toast;->makeText(Landroid/content/Context;Ljava/lang/CharSequence;I)Landroid/widget/Toast;move-result-object v0invoke-virtual {v0}, Landroid/widget/Toast;->show()V.line 28return-void 然后我们需要将修改后的文件目录重新打包,执行命令 apktool b app-release,就会在app-releae目录下生成两个文件夹:build 文件夹里面是一些中间文件(classes.dex等内容),dist 文件夹里面存放着重新打包出来的apk文件。
另外,apk反编译后也可以修改资源,将反编译出来的资源文件修改一通,然后按照之前的方法,重新打包、签名、安装。下面两个页面是修改之前和修改之后的对比图。


到这里,本文的全部内容就讲解完了,欢迎大家评论交流~