如何调试安卓应用程序的smali代码?

19

我有一款工作中的安卓应用程序,但是没有它的源代码。

我想要调试该应用程序的功能,并通过 apktool 成功地对该应用进行了反编译:

https://code.google.com/p/android-apktool/

该工具生成了 smali 格式的类文件。

我的要求是:

  1. 通过添加调试日志来调试方法。
  2. 通过打印堆栈跟踪来调试方法调用。

为实现这些目标,我需要注入 / 插入等效于调试日志或堆栈跟踪的 smali 代码。

我尝试在其中一个方法开头添加一些 smali 指令,但它会崩溃并显示 ClassVerifyError。

示例 smali 代码:

.method public declared-synchronized b()V
    .locals 2

    .prologue

    .line 87
    monitor-enter p0

    :try_start_0
    iget-object v0, p0, Lcom/example/viewerlib/t/d;->a:Ljava/lang/Thread;

    invoke-virtual {v0}, Ljava/lang/Thread;->isAlive()Z

               :
               :

有人可以帮我添加smali调试日志吗?提前感谢。


所以最初这个应用是使用Smali编写的? - Skynet
这不是我的应用程序。这是普通的安卓应用程序。我们可以使用apktool反编译apk,从apk内部的classes.dex文件生成smali代码。 - Dheeraj Kumar Chaudhary
不只是如此!它也可以生成Java代码,使用Dex转Jar。完成后使用JD-GUI即可。 - Skynet
但如果他将其转换为Java,他可以在Eclipse IDE中完成所有操作。另外,再花费一点努力,他还可以获取XML文件和资源! - Skynet
1
@AstralProjection 从smali构建Java代码是可行的,但如果一个方法/类很大,那么这将更加繁琐。相反,正如他所指出的,为了理解应用程序本身的功能,打印变量值可能在运行时更有用。构建Java代码仅适用于一个解耦的功能。对于完整的应用程序,他必须在运行时进行调试以检查是否存在相同的问题。 - rajan goswami
显示剩余5条评论
6个回答

34

1. 在smali中输出调试日志

在smali中输出调试日志。比如说,你想在方法test()中打印"Inside Test()"的调试日志。在smali代码中的方法开头添加以下指令:

sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;

const-string v1, "Inside Test()"

invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V

注意 - 在使用寄存器v0,v1时需要小心。在代码执行流程中,您必须检查您没有使用流程后使用的寄存器。否则您可能会收到异常。

2. 堆栈跟踪

以下是打印方法堆栈跟踪的smali代码

Java代码

public static void printStackTraces() {
    StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace(); 
    for (StackTraceElement element : stackTraceElements) {
        System.out.println("Class name :: " + element.getClassName() + "  || method name :: " + element.getMethodName());
    }
}

等效的 Smali 代码是

.method public static printStackTraces()V
    .locals 7

    .prologue
    .line 74
    invoke-static {}, Ljava/lang/Thread;->currentThread()Ljava/lang/Thread;

    move-result-object v2

    invoke-virtual {v2}, Ljava/lang/Thread;->getStackTrace()[Ljava/lang/StackTraceElement;

    move-result-object v1

    .line 75
    .local v1, stackTraceElements:[Ljava/lang/StackTraceElement;
    array-length v3, v1

    const/4 v2, 0x0

    :goto_0
    if-lt v2, v3, :cond_0

    .line 78
    return-void

    .line 75
    :cond_0
    aget-object v0, v1, v2

    .line 76
    .local v0, element:Ljava/lang/StackTraceElement;
    sget-object v4, Ljava/lang/System;->out:Ljava/io/PrintStream;

    new-instance v5, Ljava/lang/StringBuilder;

    const-string v6, "Class name :: " 

    invoke-direct {v5, v6}, Ljava/lang/StringBuilder;-><init>(Ljava/lang/String;)V

    invoke-virtual {v0}, Ljava/lang/StackTraceElement;->getClassName()Ljava/lang/String;

    move-result-object v6

    invoke-virtual {v5, v6}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;

    move-result-object v5

    const-string v6, "  || method name :: " 

    invoke-virtual {v5, v6}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;

    move-result-object v5

    invoke-virtual {v0}, Ljava/lang/StackTraceElement;->getMethodName()Ljava/lang/String;

    move-result-object v6

    invoke-virtual {v5, v6}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;

    move-result-object v5

    invoke-virtual {v5}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;

    move-result-object v5

    invoke-virtual {v4, v5}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V

    .line 75
    add-int/lit8 v2, v2, 0x1

    goto :goto_0
.end method
将以下方法添加到任何smali文件中。并调用如下:

(假设您已将上述smali代码添加到com.example.packagename.ClassName中)


(假设您已将上述smali代码添加到com.example.packagename.ClassName中)

invoke-static {}, Lcom/example/packagename/ClassName;->printStackTraces()V

希望这能帮到你......


好东西,你的“注释”部分说出了程序员在修改smali代码时最常见的错误。+1 - Pankaj Kumar
谢谢,我会尝试这个。 - Dheeraj Kumar Chaudhary
10
我遇到了以下异常: no viable alternative at input 'stackTraceElements'no viable alternative at input 'stackTraceElements' ... 只需要将.local v1, stackTraceElements:[Ljava/lang/StackTraceElement;更新为.local v1, "stackTraceElements":[Ljava/lang/StackTraceElement;,将.local v0, element:Ljava/lang/StackTraceElement;更新为.local v0, "element":Ljava/lang/StackTraceElement;。看起来.local变量的语法已经更新。 - sdc

6
如果您喜欢IntellijIdea,不妨尝试一下 smalidea

2
我建议使用Sony的apkanalyzer。

https://github.com/sonyxperiadev/ApkAnalyser

它允许您反编译APK并阅读Smali代码,将打印语句插入到各个位置。有一个菜单选项:“打印自定义日志”,您还可以打印堆栈跟踪,并进行许多其他操作。完成所有更改后,只需按下“设备->安装”并启动APK即可。一切都在GUI中完成。

2

1

很抱歉指出,apkanalyzer是一个静态分析工具。 如果想要理解和编辑smali代码,请尝试以下方法。

http://themasterofmagik.wordpress.com/2014/03/08/decompilerecompile-an-apk-basic-editing/

它解释了在不同情境下的编辑方式,以及何时使用哪种工具。 对apk进行反编译/重新编译的不同方法。
a. apk
    1. apktool + Notepad++
    2. Virtuous Ten Studio
    3. AndroChef Java Decompiler
b. classes.dex
    1. smali/baksmali
    2. dex2jar + JD-GUI

0

我尝试使用smalidea插件,但无法使其正常工作,因此尝试了下面提到的不同方法。

在Android Studio 3.1.2中有一个选项,转到文件->选择配置文件或分析Apk选项。然后它将反编译Apk文件并生成classes.dex文件和包含smali文件的out文件。您可以然后运行As-> Debug选项,并在任何smali文件中设置断点,它将停止在指定的断点处。这仅适用于应用程序中只有单个dex文件的情况。但是,在多dex应用程序中有一种解决方法,您需要按照以下步骤操作-

1)从here下载Apktool工具

2)使用 -> apktool d 反编译Apk

3)将上面生成的所有smali文件复制到多个文件夹(smali、smali_classess2、smali_classes3等)中,然后将它们放入从Android Studio创建的out文件夹中-> Analyze Apk方法,以便所有smali文件都在单个文件夹中

4) 这一步我不确定是否必要,但我已经按照这个步骤进行了操作,可以自由验证。在这里,您需要使用apktool使用第2步创建的文件夹构建Apk,使用命令 -> apktool b . 这将生成所有.dex文件并将所有文件复制到与之前使用Analyze Apk方法生成单个classes.dex文件相同的位置。

5) 现在使用使用Analyze Apk方法创建的相同项目,您就可以进行调试了。

P.S. - Android Studio会抱怨使用Java代码进行调试,但是这种方法是有效的。如果您正在编辑任何smali文件并希望进行调试,则此方法非常有用。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接