spguangz 发表于 2015-8-13 19:47

一种安卓逆向方法--"存档注入"

本帖最后由 spguangz 于 2015-8-13 19:50 编辑

"存档注入"就是把修改好的存档放到原存档位置,当应用读取新存档时,即可达到跳过内购支付过程、免激活、无限金币等目的。"存档注入"按照注入的方式可分为手动式和自动式。
手动就是指拿到存档后,自己动手把其放到存档位置,取代原存档。
自动即应用运行过程中,应用自行把存档放到存档位置,全由代码实现。

存档注入
手动注入代码注入
优点不需要修改应用快捷,简便,不需要root
缺点繁琐,需要root要修改应用,可能还会涉及应用自校验等
所以后者是一种相对"一劳永逸"的方法!下文主要阐述用代码进行存档注入的方法。

"存档注入"按照存档文件的数量可分为复制式和解压式。
复制式用于存档文件只有1个。假如不止1个的话,可以进行多次复制,或用解压式。

1 复制式存档注入

游戏:涂鸦双轮车(1个存档文件)
来源:联通沃游戏搜索下载
目标:去除游戏激活过程

1-1 存档的准备

a.内购破解
游戏玩到第4关,需要进行解锁。
   

点击退出,提示未支付,游戏并未解锁。


看到联通支付框里的文字,可以搜索"游戏激活",定位到Lcom/cloud/dw/DoodleWheels;
查看源码,发现某方法

protected void onActivityResult(int paramInt1, int paramInt2, Intent paramIntent)
{
    int i = 1;
    switch (paramInt1)
    {
    default:
      super.onActivityResult(paramInt1, paramInt2, paramIntent);
      return;
    }
    if (paramIntent.getIntExtra("result", i) == 0) {}
    while (i != 0)
    {
      setActivated();
      Toast.makeText(this, 2130903080, 0).show();
      return;
      i = 0;
    }
    Toast.makeText(this, paramIntent.getStringExtra("errorstr"), 0).show();
}
其中setActivated();为完成激活游戏,所以把代码跳转到此处即可,
或者直接setActivated();将挪到switch分支前,那么退出支付时,实际上也是完成了游戏的激活。

b.存档导出
把手机上的存档导出到电脑上备用。我的方法是使用Android killer的文件管理功能。(。・・)ノ

游戏激活后的存档大致内容:

<int name="lv0state" value="1" />
<int name="lv1state" value="1" />
<int name="lv2state" value="1" />
<int name="lv3state" value="1" />
<boolean name="activated" value="true" />
<float name="lv1time" value="8.622523" />
<int name="topScore" value="1126" />
<boolean name="init" value="true" />
<boolean name="sound" value="true" />

为了装作我没玩过的样子,我把打开了的关卡关上。

<int name="lv0state" value="1" />
<int name="lv1state" value="0" />
<int name="lv2state" value="0" />
<int name="lv3state" value="0" />
<boolean name="activated" value="true" />
<float name="lv1time" value="0.0" />
<int name="topScore" value="0" />
<boolean name="init" value="true" />
<boolean name="sound" value="true" />

因为"activated"为true,所以游戏一打开即为已激活版。
存档准备部分完成。

1-2 "复制式"代码的添加

在主Activity里添加以下方法:

.method public static doCopyAssets(Landroid/content/Context;)V
    .locals 9
    .param p0, "context"    # Landroid/content/Context;

    .prologue
    .line 15
   
    new-instance v2, Ljava/io/File;

    const-string v7, "/data/data/包名/shared_prefs/存档文件名"

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

    .line 18
    .local v2, "file":Ljava/io/File;
    invoke-virtual {v2}, Ljava/io/File;->exists()Z

    move-result v7

    if-nez v7, :cond_1

    .line 26
    :cond_0
    :try_start_0
    new-instance v6, Ljava/io/FileOutputStream;

    const-string v7, "/data/data/包名/shared_prefs/存档文件名"
                                                      
    invoke-direct {v6, v7}, Ljava/io/FileOutputStream;-><init>(Ljava/lang/String;)V

    .line 27
    .local v6, "myOutput":Ljava/io/OutputStream;
    invoke-virtual {p0}, Landroid/content/Context;->getAssets()Landroid/content/res/AssetManager;

    move-result-object v7

    const-string v8, "存档文件名"

    invoke-virtual {v7, v8}, Landroid/content/res/AssetManager;->open(Ljava/lang/String;)Ljava/io/InputStream;

    move-result-object v5

    .line 28
    .local v5, "myInput":Ljava/io/InputStream;
    const/16 v7, 0x400

    new-array v0, v7, [B

    .line 29
    .local v0, "buffer":[B
    invoke-virtual {v5, v0}, Ljava/io/InputStream;->read([B)I

    move-result v4

    .line 30
    .local v4, "length":I
    :goto_0
    if-gtz v4, :cond_2

    .line 34
    invoke-virtual {v6}, Ljava/io/OutputStream;->flush()V

    .line 35
    invoke-virtual {v5}, Ljava/io/InputStream;->close()V

    .line 36
    invoke-virtual {v6}, Ljava/io/OutputStream;->close()V

    .line 45
    .end local v0    # "buffer":[B
    .end local v2    # "file":Ljava/io/File;
    .end local v3    # "file1":Ljava/io/File;
    .end local v4    # "length":I
    .end local v5    # "myInput":Ljava/io/InputStream;
    .end local v6    # "myOutput":Ljava/io/OutputStream;
    :cond_1
    :goto_1
    return-void

    .line 31
    .restart local v0    # "buffer":[B
    .restart local v2    # "file":Ljava/io/File;
    .restart local v3    # "file1":Ljava/io/File;
    .restart local v4    # "length":I
    .restart local v5    # "myInput":Ljava/io/InputStream;
    .restart local v6    # "myOutput":Ljava/io/OutputStream;
    :cond_2
    const/4 v7, 0x0

    invoke-virtual {v6, v0, v7, v4}, Ljava/io/OutputStream;->write([BII)V

    .line 32
    invoke-virtual {v5, v0}, Ljava/io/InputStream;->read([B)I
    :try_end_0
    .catch Ljava/io/FileNotFoundException; {:try_start_0 .. :try_end_0} :catch_0
    .catch Ljava/io/IOException; {:try_start_0 .. :try_end_0} :catch_1

    move-result v4

    goto :goto_0

    .line 37
    .end local v0    # "buffer":[B
    .end local v4    # "length":I
    .end local v5    # "myInput":Ljava/io/InputStream;
    .end local v6    # "myOutput":Ljava/io/OutputStream;
    :catch_0
    move-exception v1

    .line 38
    .local v1, "e":Ljava/io/FileNotFoundException;
    invoke-virtual {v1}, Ljava/io/FileNotFoundException;->printStackTrace()V

    goto :goto_1

    .line 39
    .end local v1    # "e":Ljava/io/FileNotFoundException;
    :catch_1
    move-exception v1

    .line 41
    .local v1, "e":Ljava/io/IOException;
    invoke-virtual {v1}, Ljava/io/IOException;->printStackTrace()V

    goto :goto_1
.end method
其大概作用为将assets里的指定文件复制到指定的存档位置。
当然需有个存档文件是否存在的判断,不然每次打开游戏都要重玩了。

把准备好的存档文件置于assets文件夹下,并于主Activity中(一般在onCreate)添加调用以上方法的代码。
回编译。试玩,目标达成。


2 解压式存档注入

应用:计算管家 v3.4(多个存档文件)
来源:繁华最近的一篇“求破”贴
目标:软件一打开即为已付费版

2-1 存档的准备

按照繁华入门教程(五)的方法,
吾爱破解安卓逆向入门教程(五)---Smali实战分析进行授权破解和去掉软件重启验证后,进行授权,成功后获得存档,并导出到电脑备用。

2-2 "解压式"代码的添加

在主Activity里添加以下方法:

.method private doUnZipAssets(Ljava/lang/String;Ljava/lang/String;)V                                                               
    .locals 3                                                               
    .param p1, "path"    # Ljava/lang/String;                                                               
    .param p2, "zipfileInAssets"    # Ljava/lang/String;                                                               
                                                               
    .prologue                                                               
    .line 201                                                               
                                                               
    const/4 v1, 0x0                                                               
                                                               
    .line 203                                                               
    .local v1, "is":Ljava/io/InputStream;                                                               
    :try_start_0                                                               
    invoke-virtual {p0}, Lx/y/z;->getResources()Landroid/content/res/Resources;
                                                               
    move-result-object v2                                                               
                                                               
    invoke-virtual {v2}, Landroid/content/res/Resources;->getAssets()Landroid/content/res/AssetManager;                                                               
                                                               
    move-result-object v2                                                               
                                                               
    invoke-virtual {v2, p2}, Landroid/content/res/AssetManager;->open(Ljava/lang/String;)Ljava/io/InputStream;                                                               
    :try_end_0                                                               
    .catch Ljava/io/IOException; {:try_start_0 .. :try_end_0} :catch_0                                                               
                                                               
    move-result-object v1                                                               
                                                               
    .line 208                                                               
    :goto_0                                                               
    :try_start_1                                                               
    invoke-static {v1, p1}, Lx/y/z;->extnativeZipFileList(Ljava/io/InputStream;Ljava/lang/String;)V
    :try_end_1                                                               
    .catch Ljava/lang/Exception; {:try_start_1 .. :try_end_1} :catch_1                                                               
                                                               
    .line 212                                                               
    :goto_1                                                               
    return-void                                                               
                                                               
    .line 204                                                               
    :catch_0                                                               
    move-exception v0                                                               
                                                               
    .line 205                                                               
    .local v0, "e":Ljava/io/IOException;                                                               
    invoke-virtual {v0}, Ljava/io/IOException;->printStackTrace()V                                                               
                                                               
    goto :goto_0                                                               
                                                               
    .line 209                                                               
    .end local v0    # "e":Ljava/io/IOException;                                                               
    :catch_1                                                               
    move-exception v0                                                               
                                                               
    .line 210                                                               
    .local v0, "e":Ljava/lang/Exception;                                                               
    invoke-virtual {v0}, Ljava/lang/Exception;->printStackTrace()V                                                               
                                                               
    goto :goto_1                                                               
.end method                                                               
再于主Activity的onCreate中添加以下代码:

new-instance v0, Ljava/io/File;

    const-string v1, "/data/data/包名/shared_prefs/存档文件名字"

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

    .line 46
    .local v0, "file":Ljava/io/File;
    invoke-virtual {v0}, Ljava/io/File;->exists()Z

    move-result v1
   
    if-nez v1, :cond_0
   
    const-string v1, "存档位置"

    const-string v2, "xxx.zip"

    invoke-direct {p0, v1, v2}, Lx/y/z;->doUnZipAssets(Ljava/lang/String;Ljava/lang/String;)V

    .line 47
    :cond_0
    return-void
其大概作用为判断存档里是否有某个存档文件,没有的话执行:将assets里的xxx.zip解压到指定的存档位置。

这样软件首次运行会将修改好的存档解压到存档目录,以后就不会了。

将授权成功后的存档压缩成xxx.zip,并置于assets文件夹下。
进行回编译。试玩,目标达成。

3 小结
"存档注入"适用于某些代码难分析修改,但存档易于攻破的应用。
或者某游戏自己打出极品装备,想共享存档给好友,他丫手机却没有root?不妨试试此方法。



晓源 发表于 2015-12-15 13:11

回编译失败了
>I: 编译 smali 到 classes.dex...
>..\..\projects\com\Project\smali\com\nflystudio\InfiniteStaircase\UnityPlayerNativeActivity.smali There is already a label with that name.
>Exception in thread "main" b.a.D: Could not smali file: com/nflystudio/InfiniteStaircase/UnityPlayerNativeActivity.smali
>        at b.a.e.e.a(Unknown Source)
>        at b.a.e.e.a(Unknown Source)
>        at b.a.e.e.a(Unknown Source)
>        at b.a.a.a(Unknown Source)
>        at b.a.a.a(Unknown Source)
>        at b.a.a.e(Unknown Source)
>        at b.a.a.a(Unknown Source)
>        at b.b.a.b(Unknown Source)
>        at b.b.a.a(Unknown Source)
>        at com.rover12421.shaka.cli.Main.main(Unknown Source)

糯米与绿茶 发表于 2015-8-13 19:53

看起来好厉害的样子 支持下~

yeelnn 发表于 2015-8-14 07:53

感觉最近有很多牛出现啊

情侣的书包 发表于 2015-8-14 08:04

存档注入其实很简单!不过我复习一下!

心存侥幸 发表于 2015-8-14 14:23

好厉害的样子 支持下~

叶小凡01 发表于 2015-8-28 12:17

还是要学会查看smali源码。。。。

spguangz 发表于 2015-8-28 12:18

叶小凡01 发表于 2015-8-28 12:17
还是要学会查看smali源码。。。。

源码会看自然好
不会看的话也行

tigor65 发表于 2015-8-28 21:51

我来看评论

xiaomi1991 发表于 2016-4-8 12:31

支持楼主 学习一下
页: [1] 2
查看完整版本: 一种安卓逆向方法--"存档注入"