• Android中Java编写的APK使用apktool反编译并修改smali

    今天装了某个游戏,游戏中有“商店”,可以用金币购买相关物品。而获得金币的途径,是用人民币购买(1元100只)或者安装该公司的其他应用(安装3款得一点点)。

    这显然不科学啊!

    当然,程序在我机子上,我们当然能够用科学的手段来对付不科学的游戏货币商城制度。

    1、使用apktool进行反编译,得到smali的字节码

    假设游戏安装文件为my_game.apk,我们将把它反编译到mygame目录下。

    这里下载apktool和对应的dependencies,把apktool和对应的dependencies加压缩到一起,并将该文件夹添加到path环境变量中。我在使用Windows系统,这时候apktool的解压文件夹有以下内容:

    aapt.exe

    apktool.bat

    apktool.jar

    然后对my_game.apk进行反编译,运行命令:

    apktool d my_game.apk mygame

    可以看到apktool将游戏反编译到mygame文件夹中。其中的smali就是对应的字节码。

    2、修改smali字节码

    关于smali字节码的寄存器可以参考这里,类型、函数和成员可以参考这里,操作符号可以参考这里

    当然,由于程序已经混淆,所以不能从文件名和包名中猜测相关信息。这时候有好几个办法定位到要修改的地方。如调试设断点,在这里有教程。还可以通过资源和字符串定位,这是我现在使用的方法,因为我的调试环境还没搭好……

    我注意到,某一个与购买相关的按钮图片的id编号为“0x7f020093”,于是在smali中搜索,最后在某个文件夹,也就对应Java的某个包中找到g.smali,这个文件包含了字符串“0x7f020093”。然后,点击购买时,会提示“金币不足,无法购买”,这是文本信息。也就是说,可以在g.smali中搜索该字符串,或者字符串子串来进行程序上下文的定位。当然,直接搜索中文字符串是一无所获的,因为反编译器将中文以utf8的裸编码格式表现。这时候在g.smali中搜索“\u91d1\u5e01\u4e0d\u8db3,\u65e0\u6cd5\u8d2d\u4e70”,找到详细位置!具体代码片段如下:

    sget-object v0, La/f/c;->s:La/a/b/g;

    iget-object v9, v0, La/a/b/g;->j:Ld/a/b;

    iget v10, p2, La/d/n;->f:I

    iget v0, v9, Ld/a/b;->v:I

    if-ge v0, v10, :cond_0

    invoke-static {}, Lcom/gale/manager/f;->a()Lcom/gale/manager/f;

    move-result-object v0

    const-string v1, "\u91d1\u5e01\u4e0d\u8db3,\u65e0\u6cd5\u8d2d\u4e70"

    invoke-virtual {v0, v1}, Lcom/gale/manager/f;->a(Ljava/lang/String;)V

    :goto_0
    return-void

    :cond_0
    neg-int v0, v10

    iput v0, v9, Ld/a/b;->x:I

    需要关注的是“if-ge”这句,意思是如果v0大于等于v10,则跳到cond_0执行,否则显示“金币不足,无法购买”并返回。

    我们可以直接将“if-ge”改成“if-le”解决问题,不过这时金币会在购买后变负数,说不定会把游戏crash。所以,更好的修改方法如下:

        sget-object v0, La/f/c;->s:La/a/b/g;

        iget-object v9, v0, La/a/b/g;->j:Ld/a/b;

        iget v10, p2, La/d/n;->f:I

        iget v0, v9, Ld/a/b;->v:I

        if-ge v0, v10, :cond_0
        goto/16 :cond_0

        :goto_0
        return-void

        :cond_0
        move v0, v10

        iput v0, v9, Ld/a/b;->x:I

    意思是,即使当前金币小于购买所需金币,也跳到条件成立的地方执行。另外,执行时会将玩家的金币数加上所需金币的相反数,即“neg-int v0, v10”,将其改为“move v0, v10”就可以每次都把玩家的金币数加上所需金币数。最终效果则是,即使金币不足也可以购买,并且越买钱越多。

    3、打包、签名和安装修改后的apk

    修改完了,就可以打包回apk了。执行以下命令:

    apktool b mygame

    在mygame目录下的dist在会看到打包好的apk。

    当然,现在一般是无法安装的,因为apk还没有签名。下面就来签名。签名需要keystore文件,我已经有专用的keystore了,如果还没有,请参阅这里进行生成。

    执行以下命令为重新编译的my_game.apk签名:

    jarsigner -verbose -keystore creke.keystore my_game.apk Alias_name

    最后,在安装到手机前,需要把手机中的已有版本先卸载,因为如果签名不同,是不能覆盖安装的,会提示“应用程序未安装”错误。

    4、其他

    Windows下的aapt似乎不能对非本地磁盘进行读写,比如我原来在内存盘中进行操作,apktool总是提示aapt出错,不能打包apk。后来放到本地磁盘中进行才一切正常。

    最后说一句,好的游戏从来不会用奇怪的货币和商城来吸引玩家或者玩家的钱财。