吾爱破解 - LCG - LSG |安卓破解|病毒分析|www.52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 12324|回复: 24
收起左侧

[Android 原创] Xposed绕过元气骑士内购验证

  [复制链接]
RedFree 发表于 2020-2-14 15:15
本帖最后由 RedFree 于 2020-2-14 23:10 编辑

申明:本文仅用于技术研究所用,请支持正版游戏!

一、APK反编译

从官网下载最新版本(2.5.1)的元气骑士apk安装包:
http://www.chillyroom.com/yqqs-2501.apk

解压并保存apk中的文件:classes.dex
使用dex2jar获取jar文件,到这里便可以使用jd-gui打开这个jar包并查看反编译的java代码。为了方便搜索,可以使用jd-gui的导出源代码功能导出所有反编译的文件。

1 " alt="" />

二、内购功能点分析

走正常的购买流程购买任意物品,在付款前返回,得到如下的消息:

2 " alt="" />

3 " alt="" />

可以看到返回了两个消息:

订单取消user_calcelled.
null 校验订单失败:unpaid

以'订单取消'作为关键字搜索,定位到了以下点:

4 " alt="" />

以'校验订单失败'作为关键字搜索,定位到以下两个点:

5 " alt="" />

三、代码分析 + Xposed Hook

1、第一个点(订单取消)绕过

  protected void onActivityResult(int paramInt1, int paramInt2, Intent paramIntent) {
    try {
      if (paramInt1 == Pingpp.REQUEST_CODE_PAYMENT && paramIntent != null && paramIntent.getExtras() != null) {
        String str3 = paramIntent.getExtras().getString("pay_result");
        String str2 = paramIntent.getExtras().getString("error_msg");
        String str1 = paramIntent.getExtras().getString("extra_msg");
        if (this.payingOrderId != null && !"".equals(this.payingOrderId)) {
          if ("success".equals(str3)) {
            onPaySuccess(this.payingOrderId, "", "");
            Toast.makeText((Context)this, "支付成功", 0).show();
          } else {
            StringBuilder stringBuilder;
            if ("fail".equals(str3)) {
              onPayFail(this.payingOrderId, "");
              stringBuilder = new StringBuilder();
              stringBuilder.append("支付失败:");
              stringBuilder.append(str2);
              stringBuilder.append(",");
              stringBuilder.append(str1);
              Toast.makeText((Context)this, stringBuilder.toString(), 1).show();
            } else if ("cancel".equals(stringBuilder)) {
              onPayCancel(this.payingOrderId, "");
              stringBuilder = new StringBuilder();
              stringBuilder.append("订单取消");
              stringBuilder.append(str2);
              stringBuilder.append(",");
              stringBuilder.append(str1);
              Toast.makeText((Context)this, stringBuilder.toString(), 0).show();
            } else if ("invalid".equals(stringBuilder)) {
              onPayFail(this.payingOrderId, "");
              stringBuilder = new StringBuilder();
              stringBuilder.append("支付失败:");
              stringBuilder.append(str2);
              stringBuilder.append(",");
              stringBuilder.append(str1);
              Toast.makeText((Context)this, stringBuilder.toString(), 1).show();
            } else if ("unknown".equals(stringBuilder)) {
              onPayUnknown(this.payingOrderId, "");
              stringBuilder = new StringBuilder();
              stringBuilder.append("未知错误:");
              stringBuilder.append(str2);
              stringBuilder.append(",");
              stringBuilder.append(str1);
              Toast.makeText((Context)this, stringBuilder.toString(), 1).show();
            } 
          } 
          this.payingOrderId = null;
          return;
        } 
      } 
    } catch (Exception exception) {
      Log.i("Unity", exception.toString());
    } 
  }

可以看到只要让paramIntent.getExtras().getString("pay_result");的值为success便通过了此处验证。

Xposed Hook代码

6 " alt="" />

2、第二个点(校验订单)绕过

com.chillyroomsdk.sdkbridge.order.https_task.OrderCheckTask

  protected void onPostExecute(String paramString) {
    String str = TAG;
    StringBuilder stringBuilder = new StringBuilder();
    stringBuilder.append("onPostExecute: ");
    stringBuilder.append(paramString);
    Log.i(str, stringBuilder.toString());
    if (paramString != null && !paramString.equals("")) {
      OrderInfo orderInfo = new OrderInfo();
      orderInfo.orderId = this.m_orderId;
      try {
        String str1;
        JSONObject jSONObject = new JSONObject(paramString);
        if (jSONObject.getInt("retcode") == 0) {
          jSONObject = jSONObject.getJSONObject("order");
          if (jSONObject.getInt("can_send") == 1) {
            orderInfo.productName = jSONObject.getString("item_name");
            orderInfo.productId = jSONObject.getString("item_id");
            float f = jSONObject.getInt("total_price") / 100.0F;
            ((IPayAgent)UnityPlayer.currentActivity).onPayCheckSuccess(this.m_orderId, orderInfo.productId, orderInfo.productName, f, "");
            if (this.m_dialog != null) {
              this.m_dialog.dismiss();
              return;
            } 
          } else {
            if (this.m_count > 0) {
              StartTaskOnMainThread(this.m_dialog, this.m_orderId, this.m_count, this.m_time_count);
            } else {
              Toast.makeText(this.m_context, "订单支付失败。若发生丢单请勿重复支付,并联系客服", 1).show();
              if (this.m_dialog != null)
                this.m_dialog.dismiss(); 
            } 
            str1 = TAG;
            stringBuilder = new StringBuilder();
            stringBuilder.append("此订单支付失败:");
            stringBuilder.append(orderInfo.orderId);
            Log.i(str1, stringBuilder.toString());
            return;
          } 
        } else {
          str1 = str1.getString("msg");
          Context context = this.m_context;
          StringBuilder stringBuilder1 = new StringBuilder();
          stringBuilder1.append(orderInfo.productName);
          stringBuilder1.append(" 校验订单失败:");
          stringBuilder1.append(str1);
          Toast.makeText(context, stringBuilder1.toString(), 1).show();
          if (this.m_count > 0) {
            StringBuilder stringBuilder2 = new StringBuilder();
            stringBuilder2.append("count : ");
            stringBuilder2.append(this.m_count);
            Log.i("Unity", stringBuilder2.toString());
            StartTaskOnMainThread(this.m_dialog, this.m_orderId, this.m_count, this.m_time_count);
            return;
          } 
          if (this.m_dialog != null) {
            this.m_dialog.dismiss();
            return;
          } 
        } 
      } catch (JSONException jSONException) {
        jSONException.printStackTrace();
        return;
      } 
    } else {
      if (this.m_count > 0) {
        StartTaskOnMainThread(this.m_dialog, this.m_orderId, this.m_count, this.m_time_count);
        return;
      } 
      Toast.makeText(this.m_context, "订单支付失败。若发生丢单请勿重复支付,并联系客服", 1).show();
      if (this.m_dialog != null)
        this.m_dialog.dismiss(); 
    } 
  }

通过代码逻辑可以分析得出:参数paramString是一个JSON字符串,格式如下时便可以通过校验:

{"retcode":0,"order":{"can_send":1,"item_name":"商品名称","item_id":商品编号,"total_price":0},"msg":"购买成功"}

基中的商品名称商品编号暂时未知。

Xposed Hook代码

7 " alt="" />

3、第三个点(校验订单)绕过

com.chillyroomsdk.sdkbridge.order.https_task.OrderRestoreTask

这个点是用来恢复有问题订单的,原理同第二个点,不再重复说明。

Xposed Hook代码

8 " alt="" />

4、商品信息获取

上面第二和第三个点都需要商品名称和商品编号这个关键信息,通过DDMS的跟踪分析,发现了这个点:

com.chillyroomsdk.sdkbridge.order.BaseOrderAgent.requestOrder

9 " alt="" />

10 " alt="" />

可以看到商品名称、编号、价格这里都有了。因此Hook这个点,设置变量内容便可以在购买成功前设置购买商品的信息。

Xposed Hook代码

11 " alt="" />

四、实际效果演示

12 " alt="" />

yq " alt="" />

免费评分

参与人数 12吾爱币 +10 热心值 +12 收起 理由
哇卡s + 1 + 1 谢谢@Thanks!
av321 + 1 + 1 谢谢@Thanks!
maoyj + 1 + 1 我很赞同!
hinyb + 1 + 1 谢谢@Thanks!
风起在下片 + 1 + 1 我很赞同!
邬yu + 1 谢谢@Thanks!
sxcbdd + 1 + 1 我很赞同!
萧岚枫 + 1 谢谢@Thanks!
夜步城 + 1 + 1 已经处理,感谢您对吾爱破解论坛的支持!
浅夏末年、 + 1 + 1 热心回复!
CrazyNut + 1 + 1 图片都没啦 大佬补一下
我想要说 + 1 + 1 热心回复!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

夜步城 发表于 2020-2-16 11:20
DDMS的跟踪分析有没有使用过程啊
ccraker 发表于 2020-6-3 10:54
2.6.7版本的,有研究过吗,我用的是frida来做hook的,然后你说的这几个方法都hook了,付款的时候,会显示付款成功,但是页面会卡死……然后只能强制关闭,重新打开之后,充值失败。
h132011424 发表于 2020-2-15 21:24
lies2014 发表于 2020-2-14 19:50
图片都看不到
工程欧巴 发表于 2020-2-14 22:48
属实厉害
夜步城 发表于 2020-2-16 11:14
赞,用到恰处了!!
Yubai 发表于 2020-2-17 12:54
谢谢 我来支持一下看看
18372261830 发表于 2020-2-17 17:15
这个厉害,但是要自己做模块
Aim灬血饮 发表于 2020-2-18 13:40
是个狼灭
Shepherdmyq 发表于 2020-2-19 11:46
厉害厉害了
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则 警告:本版块禁止灌水或回复与主题无关内容,违者重罚!

快速回复 收藏帖子 返回列表 搜索

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-4-25 06:30

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表