一步步教你粗暴破解宫爆老奶奶-APK反编译教程

郑重提示,转载请声明出处,多谢合作!

 

相信很多人对反编译有过兴趣,但是可能有部分人仅仅停留在通过反编译获取到apk里面的一些图片资源如style、layout等xml文件以作参考借鉴之用,可能也会有人用dex2jar+jd-gui的方式来看其java代码(如下图),但是大家都知道,这样的代码是不能回编译回去重新生成apk的,甚至代码也很容易被混淆,让你看得一头雾水



 

今天,我在这里就跟大家谈谈,希望通过这个博客,让大家知道,其实反编译是可以把apk反编译出来,然后修改里面的内容,然后再回编译,然后再签名,最后的结果是一个修改过的apk(当然是手机上能装的!)

 

大家都听过 Dalvik吧?帮大家百度了一下:

Dalvik是google专门为Android操作系统设计的一个虚拟机,经过专门针对移动平台深度优化过。Dalvik 基于寄存器,而 JVM 基于栈。基于寄存器的虚拟机对于更大的程序来说,在它们编译的时候,花费的时间更短。不同于Java虚拟机运行java字节码,Dalvik虚拟机运行的是其专有的文件格式Dex。

当一个apk被反编译后,将会得到一个装满.smali文件的文件夹,而我们这次操作正是这些smail文件。

 

我们以目前 比较火的《宫爆老奶奶》为例子,讲述一下如何简单粗暴反编译一款游戏:

第一步:

首先,下载 《宫爆老奶奶》 的apk(以下简称apk),我这个是从豌豆荚下的,腾讯计费的版本,这里就省略传送了。

 

第二步:

下载反编译用的工具——apktool(传送门:http://download.csdn.net/detail/qq359948834/5116151),下载完后,随便解压放好。

 

第三步:

开始反编译。先将apk放到./ApkTool/文件夹里(./代表当前文件夹,这个路径随意,下同),然后打开./ApkTool/APKTool.cmd,按照窗口提示,要先重命名apk为123.apk,重命名完毕后,按‘1:反编译’然后回车,会看到反编译成功,得到./ApkTool/APK 文件夹。

第四步:

大家可以先随便浏览下文件夹里面的内容,会看到如图所示的文件:


 
 很明显,这是混淆过的代码,但是不要紧,混淆只不过是一种浅层次的防反编译动作,只是对一些类进行了重命名而已,对应用的逻辑函数变量的用法还是一样的。

好,初步了解后,我们先不要改动任何文件,尝试再次打开./ApkTool/ApkTool.cmd,然后选择‘3:回编译并签名’看看能不能打回原版包。

如无意外,都会出现下面这种错误:


熟悉android开发的同学可能会知道是什么原因了,我菜鸟猜了一下,应该是当前apktool.jar所使用的android平台版本比较低,所以识别不了“layout-h500dp-large”和“layout-h500dp-normal”资源文件的命名,所以我们可以修改成“layout-large”和”layout-normal“,这样,我没记错的话,android2.2系统以上的都适用了。修改后,重新选择‘3’试试看,如果步骤没错,会出现以下结果:



*以上关于警告的信息我们可以先放放不予理会。

如果大家都是出现这种情况,说明反编译成功了,接下来,大家可以随意修改图片资源以及布局文件了,只要不影响命名,以及资源数量不变,就不会影响回编译,现在,我们进入重点部分——开始修改smail文件,以实现屏蔽扣费

第五步:

先锁定计费代码。怎么锁定计费代码呢?大家可以想一下计费sdk的架构和流程:

a.用户点击UI上的物品item

b.游戏底层通过jni方式回调java层请求计费

c.弹框提示购买内容及价格

d.监听用户点击动作,发送短信(或者支付宝等支付方式)或用户取消购买,jni回调游戏底层告知购买结果(一般只有两种结果,成功与否)

所以,以上流程大家有几种方法锁定:

1、使用UltrafileSearch(一款很出名的搜索工具)批量搜索支付常用关键字(pay、purchase、success、fail、notify等等)

2、从非内部类(文件名没有$)入手,扫读所有方法名,碰到敏感方法,可以尝试添加smail代码输出log信息已判定该方法在什么时候执行

3、从与支付有关的资源入手,看看程序哪里调用了支付用到的图片和文字资源(老奶奶的资源都是写在asset,然后把所有调用支付界面的代码打成so,所以我们反编译后看不到,这个方法不适用于老奶奶这款游戏)

经过一轮苦苦摸索,终于锁定了ITencent.smali这个文件,里面有MakePay(I)V,PrePay(I)V,SendSMSCB(ILjava/lang/String;)Z 这几个很明显的方法,大家可以在这几个方法里面添加log语句(具体怎么添加,如图所示,如果还有疑问,请先阅读smail的相关语法),然后看看logcat的输出情况:


贴上代码:

.method public static MakePay(I)V
    .locals 3
    .parameter "id"

    .prologue
	#==========
    .line 8888
    const-string v0, "kelly"
    const-string v1, "========MakePay"
    invoke-static {v0,v1},Landroid/util/Log;->i(Ljava/lang/String;Ljava/lang/String;)I
    #==========

 
 *其他方法类似,这里不作赘述(注意!这里的.locals 3指的是当前方法使用了3个寄存器,因为我们的log语句一般都用到了两个寄存器,所以,当使用log时,请先留意下该方法的.locals 值是不是>=2

修改完毕后, 直接打开反编译工具选择‘3’回编译后,再安装到手机跑跑看看logcat显示(当然,也可以直接使用‘9:一键回编译并安装’,前提是你的adb环境要搭好),如图所示:

游戏界面:

 
logcat如图:



*android开发者都知道,log有两个参数,一个tag一个是String型的content,大家可以发挥一下小宇宙,输出所有String型的内容看看
 

看到这log,我们成功一半了,说明计费代码锁定了

第六步:

修改计费逻辑。这一部分涉及不少smail语法,如果大家觉得迷茫,可以先暂停下来,看看有关smail语法的介绍。

大家看看MakePay方法里面,有一行代码:

 invoke-static {p0, v0}, Lcom/tencent/webnet/WebNetInterface;->SMSBillingPoint(ILjava/lang/String;)V

 略懂英语的应该也能猜到什么意思:“短信,购买,点 ”,连起来差不多意思就是生成计费短信,并提示用户付费,我们进入该方法所在的路径看个究竟(com/tencent/webnet/WebNetInterface

如图,大家也可以在这方法加上log:

.method public static SMSBillingPoint(ILjava/lang/String;)V
    .locals 7
    .parameter
    .parameter

    .prologue
	#==========
    .line 8888
    const-string v0, "kelly"
    const-string v1, "========SMSBillingPoint"
    invoke-static {v0,v1},Landroid/util/Log;->i(Ljava/lang/String;Ljava/lang/String;)I
    #==========

 看到这里,大家开始头晕了吧?hold住,距离成功就差一点点了!

大家看看这个方法里面,有一句代码:

 invoke-static {v0, v5, v1}, Lcn/emagsoftware/gamebilling/api/GameInterface;->doBillingBySms(Ljava/lang/String;ZLcn/emagsoftware/gamebilling/api/GameInterface$BillingCallback;)V

 意思也很易读懂:doBillingBySms=“做,购买,通过,短信”,而且,大家再看,这个方法参数之中有一个字符串,一个int整形,以及一个回调方法BillingCallback,所以,我可以肯定,这是一个调用发短信被回调发送结果的方法!但是,大家也看到,这句代码藏在这个SMSBillingPoint方法里面的底部,也就是说,你要调用它,必须经过重重逻辑判断。

那么,现在你有两种选择:1、读懂所有逻辑,修改每个逻辑的条件,以达到doBillingBySms语句所在行; 2、直接把doBillingBySms语句提前到所有逻辑之前

很明显,我会选择第二种

但是,在选择第二种方法,也意味着,你要提供doBillingBySms的三个参数。

细心一看,你会发现,这三个参数中的第二个,也就是说v5,在方法一开始就定义了,所以参数已解决一个;

v0是什么?大家请看:

invoke-static {v1, v2}, Ljava/lang/String;->format(Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/String;

    move-result-object v0

    .line 221
    new-instance v1, Ljava/lang/StringBuilder;

    const-string v2, "IDO send sms code = "

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

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

 里面与v0有关的都是些字符串操作,比如StringBuilder,所以,我猜v0是一个让运营商识别所购买道具的识别码,而v1,是一个回调函数的实例,它的实例化代码为,大家去这个路径写一下log输出,对后面的步骤有用(步骤和注意点跟上面提到的如何写log一致):

new-instance v1, Lcom/tencent/webnet/q;

    invoke-direct {v1}, Lcom/tencent/webnet/q;-><init>()V

 好!现在,三个参数都拿到手了,我整理一下,用以下内容,直接覆盖这个SMSBillingPoint方法:(其实我个人不太推荐删减源文件的所有代码,毕竟以后可能会有用,所以建议只把我们的代码插入到方法里的头部,然后在我们的代码的尾部加一个return-void即可)

.method public static SMSBillingPoint(ILjava/lang/String;)V
    .locals 7
    .parameter
    .parameter

    .prologue
	#==========
    .line 8888
    const-string v0, "kelly"
    const-string v1, "========SMSBillingPoint"
    invoke-static {v0,v1},Landroid/util/Log;->i(Ljava/lang/String;Ljava/lang/String;)I
    #==========
    const/16 v6, 0x3ed

    const/4 v5, 0x1

    const/4 v4, 0x0

    const/4 v0, 0x0

	sget-object v1, Lcom/tencent/webnet/b;->ah:Lcom/tencent/webnet/WebNetInterface;

    iput-object p1, v1, Lcom/tencent/webnet/WebNetInterface;->m_SMSCurMark:Ljava/lang/String;

    .line 220
    const-string v1, "%03d"

    new-array v2, v5, [Ljava/lang/Object;

    add-int/lit8 v3, p0, -0x1

    invoke-static {v3}, Ljava/lang/Integer;->valueOf(I)Ljava/lang/Integer;

    move-result-object v3

    aput-object v3, v2, v0

    invoke-static {v1, v2}, Ljava/lang/String;->format(Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/String;

    move-result-object v0
	 new-instance v1, Lcom/tencent/webnet/q;

    invoke-direct {v1}, Lcom/tencent/webnet/q;-><init>()V

    invoke-static {v0, v5, v1}, Lcn/emagsoftware/gamebilling/api/GameInterface;->doBillingBySms(Ljava/lang/String;ZLcn/emagsoftware/gamebilling/api/GameInterface$BillingCallback;)V
	return-void
.end method

 替换好之后,我们回编译一下,看看有没有报错(报错的同学,麻烦仔细检查一下上面提到的.local有没有注意,而且也要很仔细地检查代码有没有写错),然后打开logcat,在游戏里面点击购买,看看输出情况:



 

很好,这下说明我们的apk即使已经动过计费逻辑,也能跑起来了

下面,是最后一步,也是最简单的一步,就是把回调失败改成回调成功,怎么改呢?大家看看com/tencent/webnet/q的内容:

.method public onBillingFail()V
    .locals 3

    .prologue
    .line 233
    sget-object v0, Lcom/tencent/webnet/b;->am:Lcom/tencent/webnet/o;

    const/16 v1, 0x3e9

    sget-object v2, Lcom/tencent/webnet/b;->ah:Lcom/tencent/webnet/WebNetInterface;

    invoke-static {v2}, Lcom/tencent/webnet/WebNetInterface;->a(Lcom/tencent/webnet/WebNetInterface;)Ljava/lang/String;

    move-result-object v2

    invoke-virtual {v0, v1, v2}, Lcom/tencent/webnet/o;->a(ILjava/lang/String;)V

    .line 234
    return-void
.end method

.method public onBillingSuccess()V
    .locals 3

    .prologue
    .line 227
    sget-object v0, Lcom/tencent/webnet/b;->am:Lcom/tencent/webnet/o;

    const/16 v1, 0x3e8

    sget-object v2, Lcom/tencent/webnet/b;->ah:Lcom/tencent/webnet/WebNetInterface;

    invoke-static {v2}, Lcom/tencent/webnet/WebNetInterface;->a(Lcom/tencent/webnet/WebNetInterface;)Ljava/lang/String;

    move-result-object v2

    invoke-virtual {v0, v1, v2}, Lcom/tencent/webnet/o;->a(ILjava/lang/String;)V

    .line 228
    return-void
.end method

 怎么改?不用教吧?直接把onBillingSuccess的内容复制粘贴,替换onBillingFail方法原有的内容(其实细心的同学可以发现,两个方法的区别就在于那个字符串“ const/16 v1, 0x3e9”和“ const/16 v1, 0x3e8”而已

大功告成,现在打开反编译工具,回编译后安装到手机,试试看吧(注意!为保险起见,建议各位断网+飞行模式,你懂的

写在最后:

作为一个开发者,我们要尊重每一个应用,无论游戏好玩不好玩好不好用,它都代表着一个开发者全部的心血,赞之合理,踩之有度。

至于软件扣费这个情况,无可否认,对于已经习惯免费的国人来说,一开始可能不习惯,但是,试想下,一款优秀软件,只需要20块钱左右,就已经可以买到很不错的道具了,而且有更加好的游戏体验及乐趣,再加上,这些钱能帮助到开发者开发出更多更好玩的游戏更精彩的应用,这是一个良性循环,希望玩家、开发者、用户能一起构建这种氛围。

附件为一个已经破解了的《宫爆老奶奶》,仅供参考,请支持正版游戏!下载后请在24小时内自觉删除,多谢合作!

 使用的是微盘分享:Hi,推荐文件给你 "宫爆老奶奶-破解版.apk" http://vdisk.weibo.com/s/z2r45 

猜你喜欢

转载自chenhaolin.iteye.com/blog/1825174