落华无痕 发表于 2014-5-2 11:33

Smali注入之打造属于自己的安卓crack利器(6月14日更新,待续。。。)

本帖最后由 落华无痕 于 2014-12-4 18:52 编辑

关于Smali注入大家应该了解过,网上有不少教程。那些注入代码看上去简单,实际用起来得花不少功夫。

普通的注入就是在软件源代码中添加几行代码,用于改变软件的功能,或查看某个寄存器在运行中具体的值。

需要注意的地方是:添加的注入代码所使用的寄存器不影响其他代码的执行。当注入代码较多时,这个要求就变得很困难了。

在这里我的解决办法是:把注入代码写进自己专属的crack.smali,然后在要注入的地方调用crack.smali里的注入方法即可,只用一行注入代码,而且不影响其他寄存器。

举个例子说明下两种方法的区别,假设原软件中有以下代码:

.method public methodName()Ljava/lang/String;
.locals 4
.prologue
const-string v0, "test1"

const-string v3, "test2"

invoke-static {v0}, Lpackage/name/ObjectName;——>methodName1(Ljava/lang/String;)Ljava/lang/String;

move-result-object v1

invoke-static {v1}, Lpackage/name/ObjectName;——>methodName2(Ljava/lang/String;)Ljava/lang/String;

move-result-object v2

invoke-static {v3}, Lpackage/name/ObjectName;——>methodName3(Ljava/lang/String;)V

new-instance v3, Ljava/lang/StringBuilder;

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

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

move-result-object v0

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

move-result-object v0

return-object v0
.end method
假如我要查看move-result-object v1和move-result-object v2,两处中v1、v2的值该如何注入?

普通的log.d注入方法如下(可以用Logcat查看日志):

.method public methodName()Ljava/lang/String;
.locals 5
.prologue
const-string v0, "test1"

const-string v3, "test2"

invoke-static {v0}, Lpackage/name/ObjectName;——>methodName1(Ljava/lang/String;)Ljava/lang/String;

move-result-object v1

const-string v4, "info"

invoke-static {v4, v1}, Landroid/util/Log;->d(Ljava/lang/String;Ljava/lang/String;)I

invoke-static {v1}, Lpackage/name/ObjectName;——>methodName2(Ljava/lang/String;)Ljava/lang/String;

move-result-object v2

const-string v4, "info"

invoke-static {v4, v2}, Landroid/util/Log;->d(Ljava/lang/String;Ljava/lang/String;)I

invoke-static {v3}, Lpackage/name/ObjectName;——>methodName3(Ljava/lang/String;)V

new-instance v3, Ljava/lang/StringBuilder;

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

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

move-result-object v0

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

move-result-object v0

return-object v0
.end method
在上面的代码中,我先是修改了开头的“.locals 5”,表示使用的寄存器为v0-v4。然后我用v4作为log.d的第一个参数,寄存器v1、v2为第二个参数。

由于原代码中,v0、v1、v2、v3从头到尾都有使用,所以注入时使用这几个寄存器会影响软件的正常执行,所以我才修改“.locals”开辟新的寄存器v4。

明显这样注入很麻烦。而且刚好v1、v2都是字符串,符合log.d的要求。如果v1、v2为整数值,注入就更加复杂了。

下面看看创建了crack.smali的注入会如何。

假设我已经有了个crack.smali,代码如下:


.class public Lcrack;
.super Ljava/lang/Object;
.source "crack.java"

.method public static log(Ljava/lang/String;)V
    .locals 1
    .prologue

    const-string v0, "info"
    invoke-static {v0, p0}, Landroid/util/Log;->d(Ljava/lang/String;Ljava/lang/String;)I
    return-void
.end method


把crack.smali放到反编译后的smali根目录,在源代码中注入:

.method public methodName()Ljava/lang/String;
.locals 4
.prologue
const-string v0, "test1"

const-string v3, "test2"

invoke-static {v0}, Lpackage/name/ObjectName;——>methodName1(Ljava/lang/String;)Ljava/lang/String;

move-result-object v1

invoke-static {v1}, Lcrack;->log(Ljava/lang/String;)V

invoke-static {v1}, Lpackage/name/ObjectName;——>methodName2(Ljava/lang/String;)Ljava/lang/String;

move-result-object v2

invoke-static {v2}, Lcrack;->log(Ljava/lang/String;)V

invoke-static {v3}, Lpackage/name/ObjectName;——>methodName3(Ljava/lang/String;)V

new-instance v3, Ljava/lang/StringBuilder;

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

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

move-result-object v0

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

move-result-object v0

return-object v0
.end method
明显比前面的注入方式简单多了。crack.smali的方法可以不断丰富,需要用时信手拈来。

既然如此,就让我们打造属于自己的安卓crack利器吧!

最基本的crack.smali推荐加入log日志输出,代码如下:


.class public Lcrack;
.super Ljava/lang/Object;
.source "crack.java"

.method public static log(Ljava/lang/String;)V
    .locals 1
    .prologue

    const-string v0, "info"
    invoke-static {v0, p0}, Landroid/util/Log;->d(Ljava/lang/String;Ljava/lang/String;)I
    return-void
.end method


把crack.smali放进smali目录,在要查看的,保存了字符串的寄存器vx的下面,添加代码:

invoke-static {vx}, Lcrack;->log(Ljava/lang/String;)V

保存并重新编译,在手机或模拟器上安装软件,连上电脑,打开cmd命令行:




cd到桌面,然后输入命令adb logcat>test.txt,然后在手机上运行软件,当软件执行了注入的代码(自行判断),按ctrl+c结束,然后回到桌面。

如无法识别adb命令,请先添加adb.exe所在位置的环境变量,如我安装了靠谱助手,我找到了它提供的adb.exe所在的路径,然后我照下面图设置:




打开test.txt,查找d/info,在找到的那一行的右边就是要查看的寄存器的值。




“info”是由“ const-string v0, "info" ”给出。可以自行修改。


上面的说的命令还可以添加过滤代码,有经验的人自行修改,使得test.txt的内容更简洁。


从上面test.txt的结果可以看出,如果需要注入的位置有多处,那么就很难分辨具体哪个结果对应哪个了。

解决办法是,给crack.smali的log方法加序号,并复制多份log方法,参照下面修改方式,如:


.class public Lcrack;
.super Ljava/lang/Object;
.source "crack.java"

.method public static log1(Ljava/lang/String;)V
    .locals 1
    .prologue

    const-string v0, "info1"
    invoke-static {v0, p0}, Landroid/util/Log;->d(Ljava/lang/String;Ljava/lang/String;)I
    return-void
.end method

.method public static log2(Ljava/lang/String;)V
    .locals 1
    .prologue

    const-string v0, "info2"
    invoke-static {v0, p0}, Landroid/util/Log;->d(Ljava/lang/String;Ljava/lang/String;)I
    return-void
.end method



第一处注入调用log1,第二处注入调用log2,依次类推,然后根据序号对应结果。

上面说的注入,前提都是vx是String,如果vx是int型怎么办?

可以给crack.smali添加下面方法:


.method public static I(I)V
    .locals 2

    .prologue

    const-string v0, "info_int"

    invoke-static {p0}, Ljava/lang/String;->valueOf(I)Ljava/lang/String;

    move-result-object v1

    invoke-static {v0, v1}, Landroid/util/Log;->d(Ljava/lang/String;Ljava/lang/String;)I

    return-void
.end method


上面方法调用代码为:

invoke-static {vx}, Lcrack;->I(I)V

vx为要查看的寄存器。

当vx保存的是long型的话,就比较麻烦了,稍微用错就会导致程序停止运行,所以尽量避免查看long型vx。

给crack.smali添加以下代码:


.method public static J(J)V
.locals 2

.prologue

const-string v0, "info_long"

invoke-static {p0, p1}, Ljava/lang/String;->valueOf(J)Ljava/lang/String;

move-result-object v1

invoke-static {v0, v1}, Landroid/util/Log;->d(Ljava/lang/String;Ljava/lang/String;)I

return-void
.end method


上面方法调用代码为:

invoke-static {vx, vx+1}, Lcrack;->J(J)V


vx为要查看的寄存器,同时确保vx+1在上面的代码中没有被使用过,且在.locals声明的可使用的寄存器范围内。

至于[C、[B、[Ljava/lang/String的查看,有机会再补上。

软件大部分时间都在跟字符串、数据打交道。很多时候就因为某个寄存器的值不知,导致软件代码很难分析下去。

因此在没有调试器的前提下,通过注入查看寄存器的值就变得非常重要了。

总是通过logcat查看寄存器值,多少有点不方便,所以下面讲下如何将字符串输出到文本。

直接用smali写有点麻烦,所以先用java写,编译后再反编译为smali,参考java代码如下:

import java.io.*;
import android.util.Log;
public class crack
{
/*将字符串s输出到/sdcard/debug.txt*/
public static void puts(String s)
{
try
{
          String path= "/sdcard/debug.txt";
         
                  FileOutputStream outStream = new FileOutputStream(path,true);

                  OutputStreamWriter writer = new OutputStreamWriter(outStream,"gb2312");

                  writer.write(s);
                  write.write("\r\n");

                  writer.flush();

                  writer.close();

                  outStream.close();
}
                  catch (Exception e)

                  {

                        Log.e("debug", "file write error");

                  }
}


}

反编译后smali代码如下:

.method public static puts(Ljava/lang/String;)V
    .locals 7

    .prologue
    :try_start_0

    const-string v3, "/sdcard/debug.txt"


    new-instance v2, Ljava/io/FileOutputStream;

    const/4 v5, 0x1

    invoke-direct {v2, v3, v5}, Ljava/io/FileOutputStream;-><init>(Ljava/lang/String;Z)V

    .line 19
    new-instance v4, Ljava/io/OutputStreamWriter;

    const-string v5, "gb2312"

    invoke-direct {v4, v2, v5}, Ljava/io/OutputStreamWriter;-><init>(Ljava/io/OutputStream;Ljava/lang/String;)V

    .line 21
    invoke-virtual {v4, p0}, Ljava/io/OutputStreamWriter;->write(Ljava/lang/String;)V

    const-string v5, "\r\n"

    invoke-virtual {v4, v5}, Ljava/io/OutputStreamWriter;->write(Ljava/lang/String;)V
    .line 23
    invoke-virtual {v4}, Ljava/io/OutputStreamWriter;->flush()V

    .line 25
    invoke-virtual {v4}, Ljava/io/OutputStreamWriter;->close()V

    .line 27
    invoke-virtual {v2}, Ljava/io/FileOutputStream;->close()V
    :try_end_0
    .catch Ljava/lang/Exception; {:try_start_0 .. :try_end_0} :catch_0

    .line 37

    :cond_0
    :goto_0
    return-void

    .line 30
    :catch_0
    move-exception v0

    .line 34
    const-string v5, "debug"

    const-string v6, "file write error"

    invoke-static {v5, v6}, Landroid/util/Log;->e(Ljava/lang/String;Ljava/lang/String;)I

    goto :goto_0
.end method

使用方法为,在要查看的寄存器vx的下方换行添加代码:
invoke-static {vx}, Lcrack;->puts(Ljava/lang/String;)V

至于输出整型变量到文本,请参考之前的例子,先将整型变量转为字符串,再输出,具体代码这里略过。。。

下面介绍通过注入,改变软件获得的imei号,让软件改为读取保存在 “/sdcard/deviceid/imei.txt” 里的串号。


参考java代码如下:


import java.io.*;
public class crack {

      public static String getDeviceId()
      {
      String path = "/sdcard/deviceid/imei.txt";
      String str = null;
      File f = new File(path);
      if (f != null && f.exists())
      {
            FileInputStream fis = null;
            try {
                  fis = new FileInputStream(f);
            } catch (FileNotFoundException e1) {
            e1.printStackTrace();
      }
            InputStreamReader inputStreamReader = null;
            try {
                  inputStreamReader = new InputStreamReader(fis, "utf-8");
                  try {
                            BufferedReader reader = new BufferedReader(inputStreamReader);
                            StringBuffer sb = new StringBuffer("");
                            String line;
                            line = reader.readLine();
                            sb.append(line);
                            reader.close();      
                            fis.close();
                            str = sb.toString();
                  }catch (IOException e) {   
                e.printStackTrace();   
            }
            } catch (UnsupportedEncodingException e) {
                     e.printStackTrace();         
               }   
      }
            return str;
      }
}



反编译后得到的smali代码如下:


.method public static getDeviceId()Ljava/lang/String;
    .locals 13

    .prologue
    const-string v8, "/sdcard/deviceid/imei.txt"

    const/4 v11, 0x0

    new-instance v2, Ljava/io/File;

    invoke-direct {v2, v8}, Ljava/io/File;-><init>(Ljava/lang/String;)V

    if-eqz v2, :cond_0

    invoke-virtual {v2}, Ljava/io/File;->exists()Z

    move-result v12

    if-eqz v12, :cond_0

    const/4 v3, 0x0

    :try_start_0
    new-instance v4, Ljava/io/FileInputStream;

    invoke-direct {v4, v2}, Ljava/io/FileInputStream;-><init>(Ljava/io/File;)V
    :try_end_0
    .catch Ljava/io/FileNotFoundException; {:try_start_0 .. :try_end_0} :catch_0

    move-object v3, v4

    :goto_0
    const/4 v5, 0x0

    :try_start_1
    new-instance v6, Ljava/io/InputStreamReader;

    const-string v12, "utf-8"

    invoke-direct {v6, v3, v12}, Ljava/io/InputStreamReader;-><init>(Ljava/io/InputStream;Ljava/lang/String;)V
    :try_end_1
    .catch Ljava/io/UnsupportedEncodingException; {:try_start_1 .. :try_end_1} :catch_3

    :try_start_2
    new-instance v9, Ljava/io/BufferedReader;

    invoke-direct {v9, v6}, Ljava/io/BufferedReader;-><init>(Ljava/io/Reader;)V

    new-instance v10, Ljava/lang/StringBuffer;

    const-string v12, ""

    invoke-direct {v10, v12}, Ljava/lang/StringBuffer;-><init>(Ljava/lang/String;)V

    invoke-virtual {v9}, Ljava/io/BufferedReader;->readLine()Ljava/lang/String;

    move-result-object v7

    invoke-virtual {v10, v7}, Ljava/lang/StringBuffer;->append(Ljava/lang/String;)Ljava/lang/StringBuffer;

    invoke-virtual {v9}, Ljava/io/BufferedReader;->close()V

    invoke-virtual {v3}, Ljava/io/FileInputStream;->close()V

    invoke-virtual {v10}, Ljava/lang/StringBuffer;->toString()Ljava/lang/String;
    :try_end_2
    .catch Ljava/io/IOException; {:try_start_2 .. :try_end_2} :catch_1
    .catch Ljava/io/UnsupportedEncodingException; {:try_start_2 .. :try_end_2} :catch_2

    move-result-object v11

    :cond_0
    :goto_1
    return-object v11

    :catch_0
    move-exception v1

    invoke-virtual {v1}, Ljava/io/FileNotFoundException;->printStackTrace()V

    goto :goto_0

    :catch_1
    move-exception v0

    :try_start_3
    invoke-virtual {v0}, Ljava/io/IOException;->printStackTrace()V
    :try_end_3
    .catch Ljava/io/UnsupportedEncodingException; {:try_start_3 .. :try_end_3} :catch_2

    goto :goto_1

    :catch_2
    move-exception v0

    move-object v5, v6

    :goto_2
    invoke-virtual {v0}, Ljava/io/UnsupportedEncodingException;->printStackTrace()V

    goto :goto_1

    :catch_3
    move-exception v0

    goto :goto_2
.end method


使用方法为:

查找
Landroid/telephony/TelephonyManager;->getDeviceId()Ljava/lang/String;

在下面换行添加
invoke-static {}, Lcrack;->getDeviceId()Ljava/lang/String;


心情原因,拖了这么久都没更新,有机会再续。。。









淡然出尘 发表于 2014-5-2 11:40

犀利 这样可以全称DIY了{:1_918:}

落华无痕 发表于 2014-12-3 08:22

h3616 发表于 2014-12-2 15:10
你好 ,将字符串输出到文本。用的你的代码,为什么每次文本都会覆盖写入呢,有没有可以追加写入的
已经修改帖子。
把相关smali代码的第12行,由const/4 v5, 0x0 改成 const/4 v5, 0x1
相关java代码的FileOutputStream outStream = new FileOutputStream(path,false)改成FileOutputStream outStream = new FileOutputStream(path,true)
红色为修改部分。感觉还是默认追加写入的好,所以原帖改成默认追加写入,感谢提醒。

pxhb 发表于 2014-5-2 11:51

牛B,学习了

冰楓丶殘瀷 发表于 2014-5-2 12:16

小菜路过!!!!

manbajie 发表于 2014-5-2 12:48

不错学习了 多谢楼主分享

逆向学习菜鸟 发表于 2014-5-2 13:44

不错,学习了,长知识了

z6810753 发表于 2014-5-2 14:58

不会~定起来

MMAKI 发表于 2014-5-2 17:45

不懂也帮顶

mfkwgij 发表于 2014-5-2 17:46

不错,学习了,长知识了

Thend 发表于 2014-5-3 21:12

nice啊,省去了很大的麻烦
页: [1] 2 3 4 5 6 7 8 9 10
查看完整版本: Smali注入之打造属于自己的安卓crack利器(6月14日更新,待续。。。)