Android逆向实例笔记—手游中的内购破解(火柴人联盟最新版1.9.2 BB弹 )

本文介绍了Android逆向工程在手游内购破解中的应用,以BB弹和火柴人联盟为例。通过搜索关键代码并进行修改,实现了内购的模拟成功,展示了破解思路和步骤,旨在学习交流。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

最近学到了一些内购的破解方式,就来试试手。然后找个了比较火爆的游戏BB弹,找个个没壳的就来练习。

这些东西都是大神写烂了的东西了,我这里只是写出我自己找不到方法的时候的思路。勿笑。


一、BB弹

BB弹的话比较简单,我们首先弄到模拟器上看看是什么支付。



我们发现支付宝和话费都可以。

那说明我们有很多种方法去破解内购了。我们的目的就是取消就为购买!


我们先用支付宝的




该图为引用的。


我们直接搜索0x1771




把它改为0x1771 -> :sswitch_0  就行了。


然后再来试试话费的,我们直接所搜索paysuccess




分别点进去看看


.class public interface abstract Lcn/egame/terminal/paysdk/EgamePayListener;
.super Ljava/lang/Object;
.source "EgamePayListener.java"


# virtual methods
.method public abstract payCancel(Ljava/util/Map;)V
    .annotation system Ldalvik/annotation/Signature;
        value = {
            "(",
            "Ljava/util/Map",
            "<",
            "Ljava/lang/String;",
            "Ljava/lang/String;",
            ">;)V"
        }
    .end annotation
.end method

.method public abstract payFailed(Ljava/util/Map;I)V
    .annotation system Ldalvik/annotation/Signature;
        value = {
            "(",
            "Ljava/util/Map",
            "<",
            "Ljava/lang/String;",
            "Ljava/lang/String;",
            ">;I)V"
        }
    .end annotation
.end method

.method public abstract paySuccess(Ljava/util/Map;)V
    .annotation system Ldalvik/annotation/Signature;
        value = {
            "(",
            "Ljava/util/Map",
            "<",
            "Ljava/lang/String;",
            "Ljava/lang/String;",
            ">;)V"
        }
    .end annotation
.end method

显然看不出太多的信息

第二处


.class Lcom/zplay/bbtan/plug/EgamePlug$2$1;
.super Ljava/lang/Object;
.source "EgamePlug.java"

# interfaces
.implements Lcn/egame/terminal/paysdk/EgamePayListener;


# annotations
.annotation system Ldalvik/annotation/EnclosingMethod;
    value = Lcom/zplay/bbtan/plug/EgamePlug$2;->run()V
.end annotation

.annotation system Ldalvik/annotation/InnerClass;
    accessFlags = 0x0
    name = null
.end annotation


# instance fields
.field final synthetic this$1:Lcom/zplay/bbtan/plug/EgamePlug$2;


# direct methods
.method constructor <init>(Lcom/zplay/bbtan/plug/EgamePlug$2;)V
    .locals 0

    .prologue
    .line 1
    iput-object p1, p0, Lcom/zplay/bbtan/plug/EgamePlug$2$1;->this$1:Lcom/zplay/bbtan/plug/EgamePlug$2;

    .line 79
    invoke-direct {p0}, Ljava/lang/Object;-><init>()V

    return-void
.end method


# virtual methods
.method public paySuccess(Ljava/util/Map;)V
    .locals 2
    .param p1, "params"    # Ljava/util/Map;

    .prologue
    .line 90
    sget-object v0, Lcom/dubo/android/JniMsgType;->RechargeFail:Lcom/dubo/android/JniMsgType;

    invoke-virtual {v0}, Lcom/dubo/android/JniMsgType;->ordinal()I

    move-result v0

    iget-object v1, p0, Lcom/zplay/bbtan/plug/EgamePlug$2$1;->this$1:Lcom/zplay/bbtan/plug/EgamePlug$2;

    # getter for: Lcom/zplay/bbtan/plug/EgamePlug$2;->this$0:Lcom/zplay/bbtan/plug/EgamePlug;
    invoke-static {v1}, Lcom/zplay/bbtan/plug/EgamePlug$2;->access$0(Lcom/zplay/bbtan/plug/EgamePlug$2;)Lcom/zplay/bbtan/plug/EgamePlug;

    move-result-object v1

    # getter for: Lcom/zplay/bbtan/plug/EgamePlug;->_sku:Ljava/lang/String;
    invoke-static {v1}, Lcom/zplay/bbtan/plug/EgamePlug;->access$1(Lcom/zplay/bbtan/plug/EgamePlug;)Ljava/lang/String;

    move-result-object v1

    invoke-static {v0, v1}, Lcom/dubo/android/PlatformMessage;->SendPlatformMessage(ILjava/lang/String;)V

    .line 91
    return-void
.end method

.method public payFailed(Ljava/util/Map;I)V
    .locals 2
    .param p1, "params"    # Ljava/util/Map;
    .param p2, "errorInt"    # I

    .prologue
    .line 86
    sget-object v0, Lcom/dubo/android/JniMsgType;->RechargeFail:Lcom/dubo/android/JniMsgType;

    invoke-virtual {v0}, Lcom/dubo/android/JniMsgType;->ordinal()I

    move-result v0

    iget-object v1, p0, Lcom/zplay/bbtan/plug/EgamePlug$2$1;->this$1:Lcom/zplay/bbtan/plug/EgamePlug$2;

    # getter for: Lcom/zplay/bbtan/plug/EgamePlug$2;->this$0:Lcom/zplay/bbtan/plug/EgamePlug;
    invoke-static {v1}, Lcom/zplay/bbtan/plug/EgamePlug$2;->access$0(Lcom/zplay/bbtan/plug/EgamePlug$2;)Lcom/zplay/bbtan/plug/EgamePlug;

    move-result-object v1

    # getter for: Lcom/zplay/bbtan/plug/EgamePlug;->_sku:Ljava/lang/String;
    invoke-static {v1}, Lcom/zplay/bbtan/plug/EgamePlug;->access$1(Lcom/zplay/bbtan/plug/EgamePlug;)Ljava/lang/String;

    move-result-object v1

    invoke-static {v0, v1}, Lcom/dubo/android/PlatformMessage;->SendPlatformMessage(ILjava/lang/String;)V

    .line 87
    return-void
.end method

.method public payCancel(Ljava/util/Map;)V
    .locals 2
    .param p1, "params"    # Ljava/util/Map;

    .prologue
    .line 82
    sget-object v0, Lcom/dubo/android/JniMsgType;->Recharge:Lcom/dubo/android/JniMsgType;

    invoke-virtual {v0}, Lcom/dubo/android/JniMsgType;->ordinal()I

    move-result v0

    iget-object v1, p0, Lcom/zplay/bbtan/plug/EgamePlug$2$1;->this$1:Lcom/zplay/bbtan/plug/EgamePlug$2;

    # getter for: Lcom/zplay/bbtan/plug/EgamePlug$2;->this$0:Lcom/zplay/bbtan/plug/EgamePlug;
    invoke-static {v1}, Lcom/zplay/bbtan/plug/EgamePlug$2;->access$0(Lcom/zplay/bbtan/plug/EgamePlug$2;)Lcom/zplay/bbtan/plug/EgamePlug;

    move-result-object v1

    # getter for: Lcom/zplay/bbtan/plug/EgamePlug;->_sku:Ljava/lang/String;
    invoke-static {v1}, Lcom/zplay/bbtan/plug/EgamePlug;->access$1(Lcom/zplay/bbtan/plug/EgamePlug;)Ljava/lang/String;

    move-result-object v1

    invoke-static {v0, v1}, Lcom/dubo/android/PlatformMessage;->SendPlatformMessage(ILjava/lang/String;)V

    .line 83
    return-void
.end method


这里就很关键了。这个看起来眼花的话,我们看看Java的




我们惊奇的发现,这几个成功,取消和失败的代码只有一处不同。


方法一:

那么我们就可以把取消的RechargeFail换成Recharge就OK了。


方法二:

我们可以把paySuccess和payCancel的函数名调换。也就说说,我们点了取消,却调用的是paySuccess里面的代码。OK。BB弹就简单的破解了。


二、火柴人联盟


破解BB弹之后,我本以为内购破解起来很简单。也很好玩,然后逛吾爱的时候,看到一篇破解火柴人的帖子。我也就去下了个官方版本去试试破解。(版本比帖子的高,帖子地址:http://www.52pojie.cn/thread-522841-1-1.html)


这不是重点,重点是。这个游戏在模拟器上打不开。我也不知道为什么,直接反编译。


发现了这个游戏的购买方式很多,移动,电信,联通,支付宝都有。然后还是按照之前破解BB弹的方式去破解。


却发现根本不行。把取消的RechargeFail换成Recharge,不行


把支付宝的代码换掉也不行。


于是认真参考了刚刚那个帖子,发现很多代码已经被原作者改了。那就只有自己研究了


这里就是重点了,我来说说我自己的思路。后来发现这个游戏坑爹的只能移动购买。


首先是住入口去看看

然后在这个函数去看看移动购买的函数。



然后点过去看看,发现信息不是太多。


.method private payInYidong()V
    .locals 6

    .prologue
    const/4 v1, 0x1

    .line 735
    sget v0, Lcom/DBGame/DiabloLOL/DiabloLOL;->sCMCC_OPEN:I

    if-nez v0, :cond_0

    .line 736
    const-string v0, "\u6b63\u5728\u5904\u7406,\u8bf7\u7a0d\u540e....."

    invoke-static {v0}, Lcom/DBGame/Common/BLHelper;->showShieldLayer(Ljava/lang/String;)V

    .line 738
    :cond_0
    iget-object v0, p0, Lcom/DBGame/DiabloLOL/DiabloLOL;->PAY_CODE_MM:[Ljava/lang/String;

    iget v2, p0, Lcom/DBGame/DiabloLOL/DiabloLOL;->mPayIndex:I

    aget-object v3, v0, v2

    const/4 v4, 0x0

    iget-object v5, p0, Lcom/DBGame/DiabloLOL/DiabloLOL;->payCallback:Lcn/cmgame/billing/api/GameInterface$IPayCallback;

    move-object v0, p0

    move v2, v1

    invoke-static/range {v0 .. v5}, Lcn/cmgame/billing/api/GameInterface;->doBilling(Landroid/content/Context;ZZLjava/lang/String;Ljava/lang/String;Lcn/cmgame/billing/api/GameInterface$IPayCallback;)V

    .line 739
    return-void
.end method

但是我们好像发现了payCallback这个东西。感觉又价值。我们搜索看看。




发现了两处,有用的是第一处的。

于是我们过去看看


这个时候我们就看出来了


package com.DBGame.DiabloLOL;

import cn.cmgame.billing.api.GameInterface.IPayCallback;
import com.DBGame.Common.BLHelper;

class DiabloLOL$4
  implements GameInterface.IPayCallback
{
  DiabloLOL$4(DiabloLOL paramDiabloLOL) {}
  
  public void onResult(int paramInt, String paramString, Object paramObject)
  {
    switch (paramInt)
    {
    default: 
      new StringBuilder().append("购买道具:[").append(this.this$0.PAY_NAME[DiabloLOL.access$800(this.this$0)]).append("] 取消!").toString();
    }
    for (;;)
    {
      BLHelper.closeShieldLayer();
      return;
      if (!"10".equals(paramObject.toString()))
      {
        new StringBuilder().append("购买道具:[").append(this.this$0.PAY_NAME[DiabloLOL.access$800(this.this$0)]).append("] 成功!").toString();
        BLHelper.purchaseComplete(this.this$0.PRO_ID_Str[DiabloLOL.access$800(this.this$0)], 1);
        continue;
        new StringBuilder().append("购买道具:[").append(this.this$0.PAY_NAME[DiabloLOL.access$800(this.this$0)]).append("] 失败!").toString();
      }
    }
  }
}


也就说packed-switch p1, :pswitch_data_0,然后pswitch_data_0就购买成功。




直接来个goto大发。OK了

教程就到这里了,本破解只为学习交流。


提供样本

BB弹:https://yunpan.cn/cMN8KD5h2IUg2  访问密码 9456

火柴人去官网下就是了

成本:

BB弹:https://yunpan.cn/cM4jkAt4u7m5n  访问密码 5c1b

火柴人:https://yunpan.cn/cMN89LIBLqjPq  访问密码 67a6

评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值