开启辅助访问 切换到宽版

精易论坛

 找回密码
 注册

QQ登录

只需一步,快速开始

用微信号发送消息登录论坛

新人指南 邀请好友注册 - 我关注人的新帖 教你赚取精币 - 每日签到


求职/招聘- 论坛接单- 开发者大厅

论坛版规 总版规 - 建议/投诉 - 应聘版主 - 精华帖总集 积分说明 - 禁言标准 - 有奖举报

查看: 25551|回复: 8
收起左侧

[Android逆向] 《摩尔庄园手游》登录协yi分析

[复制链接]
发表于 2021-9-19 16:09:18 | 显示全部楼层 |阅读模式   广东省中山市
本帖最后由 陈新仔 于 2021-9-19 16:25 编辑

0x00 前言

最近学习安卓逆向,刚好发现有个小时候玩过的页游,现在最近一段时间出了手游,拿来练练手也挺好。写的不是很好,请各位看官看个热闹就好。

0x01 使用工具

  • Fiddler 任意版本 、jadx、IDA

  • 雷电模拟器

  • 代理工具 Postern

  • 任意一款编程工具

0x02 教程内容

0x20 首先配置好 Fiddler 这里就不赘述了,论坛里有很多相关教程,模拟器里设置好VPN

0x21 打开游戏,随便登录个账号密码抓个登录包。

可以发现返回内容是没经过加密,然后需要的参数 这里就列举几个比较重要的参数

表单名称 说明
channelNo 110001 登录渠道,如IOS渠道为 210009
key 1881c326106056afee3da488560d75b9 经过 username与pwd计算得出的md5
username 123123123 游戏账号
userpwd 13038c34b00c2645f1cf4e5f087c1681 密码 DES_ECB
versionName 0.13.21082101S 安装包的版本号

返回关键信息

表单名称 说明
sid eaps7chp 游戏ID
token 5779c00f1e0a8cd6393b66f6f8930b0c 登录令牌

登录成功后 二次令牌登录游戏

提交格式为 JSON 需要的关键数据为 sid 和 token

提交地址已匿。可以通过自行复现找到,接下来可以分析dex了。

0x22  寻找加密过程

将APK丢入 Apktool Box 先查个壳

然后丢入 jadx 直接载入apk 这里采用最简单的方式,全局搜索提交地址的部分信息 < mobile!sdkLogin.action > 试着找出关键登录函数

继续跟进搜索 xxx_LOGIN 被谁调用了

可以看到有个调用函数  getLoginUrl 提交的时候肯定也有地方引用了这个函数

可以看到 他有个 XXXSDKUser 直接在这里面找就大概率能找到登录封装过程了。

这里有SDK登录函数,然后我们仔细分析

public void doSdkLogin(String str, String str2) {
        UserBean userBean = new UserBean();// new一个 用户数据
        userBean.setUsername(str.trim());//可以看出 str 为用户名  去掉字符串前面和后面的空格.
        str = DesUtil.encrypt(str2.trim());// 将密码进行加密 可以深入查一下
        if (TextUtils.isEmpty(str) != null) {
            loginFailNotify(String.valueOf(-3), ResUtil.getString(this.mActivity, "lt_encrypt_fail_msg"), false);
            return;
        }
        userBean.setUserpwd(str);//设置密码 也可以看出 str2 为明文密码
        doSdkLogin(userBean, false);
    }

    public void doSdkLogin(final UserBean userBean, final boolean z) {
        if (SdkConfigManager.getInstanse().getLoginUrl() == null) {
            this.mHandler.postDelayed(new Runnable() {
                public void run() {
                    LeitingSdkUser.this.doSdkLogin(userBean, z);
                }
            }, 1000);
        } else {
            handleLogin(setUserBean(userBean, z), z, 0);//先设置其他用户信息后再进行发送登录
        }
    }

public UserBean setUserBean(UserBean userBean, boolean z) {
        userBean.setGame(SdkConfigManager.getInstanse().getGame());//设置游戏名
        userBean.setChannelNo(SdkConfigManager.getInstanse().getChannelNo());//登录渠道
        userBean.setVersionCode(ApkUtil.getVersionCode(this.mActivity));//设置版本编号
        userBean.setVersionName(ApkUtil.getVersionName(this.mActivity));//设置版本名
        String str = "1";
        userBean.setCheckAuth(str);//检查验证
        userBean.setOs(str);//
        userBean.setMmid(LeitingUserManager.getInstance().getMimiId(this.mActivity, userBean.getUsername()));//设置米米号
        if (z) {//是否开启验证
            userBean.setKey(CookieUtil.encryptMobileCookie(userBean.getSid(), BaseConstantUtil.G_1));//设置Key 
            z = new StringBuilder();
            z.append(SdkConfigManager.getInstanse().getLoginUrl());//添加登录地址
            z.append(SdkConfigManager.getInstanse().getUrlApi(LeitingConstant.CHECK_LOGIN_API));//添加检验API
            userBean.setUrl(z.toString());//设置提交网址
        } else {
            z = new StringBuilder();
            z.append(SdkConfigManager.getInstanse().getLoginUrl());
            z.append(SdkConfigManager.getInstanse().getUrlApi(LeitingConstant.LOGIN_API));//添加登录API
            userBean.setUrl(z.toString());//设置提交网址
            userBean.setSerial(PhoneUtil.getUniqueSerial(this.mActivity));//设置 设备唯一序列号
            z = new StringBuilder();
            z.append(userBean.getUsername());//添加账号
            str = "|";
            z.append(str);//添加分隔符
            z.append(userBean.getUserpwd());//添加密码 此时的密码是密文
            z.append(str);//添加分隔符
            z.append(userBean.getGame());//添加游戏名
            userBean.setKey(CookieUtil.encryptMsgMd5(z.toString(), BaseConstantUtil.G_1));//设置Key 计算MD5
        }
        return userBean;
    }

搜索 encryptMsgMd5 找到对应加密函数 可读性还是挺高的

public static java.lang.String encryptMsgMd5(java.lang.String r4, java.lang.String r5) {
        /* JADX: method processing error */
/*
Error: java.lang.NullPointerException
        at jadx.core.dex.visitors.regions.ProcessTryCatchRegions.searchTryCatchDominators(ProcessTryCatchRegions.java:75)
        at jadx.core.dex.visitors.regions.ProcessTryCatchRegions.process(ProcessTryCatchRegions.java:45)
        at jadx.core.dex.visitors.regions.RegionMakerVisitor.postProcessRegions(RegionMakerVisitor.java:63)
        at jadx.core.dex.visitors.regions.RegionMakerVisitor.visit(RegionMakerVisitor.java:58)
        at jadx.core.dex.visitors.DepthTraversal.visit(DepthTraversal.java:31)
        at jadx.core.dex.visitors.DepthTraversal.visit(DepthTraversal.java:17)
        at jadx.core.ProcessClass.process(ProcessClass.java:34)
        at jadx.core.ProcessClass.processDependencies(ProcessClass.java:56)
        at jadx.core.ProcessClass.process(ProcessClass.java:39)
        at jadx.api.JadxDecompiler.processClass(JadxDecompiler.java:323)
        at jadx.api.JavaClass.decompile(JavaClass.java:62)
*/
        r0 = "%";
        r1 = "";
        r2 = new java.lang.StringBuffer;         Catch:{ Exception -> 0x0033 }
        r2.<init>(r1);         Catch:{ Exception -> 0x0033 }
        r3 = "(";         Catch:{ Exception -> 0x0033 } // 先加入一个括号
        r2.append(r3);         Catch:{ Exception -> 0x0033 } // 先加入一个括号
        r2.append(r4);         Catch:{ Exception -> 0x0033 }// 加入 r4 
        r2.append(r0);         Catch:{ Exception -> 0x0033 } // 加入 %
        r2.append(r5);         Catch:{ Exception -> 0x0033 } // 加入 r5
        r2.append(r0);         Catch:{ Exception -> 0x0033 } // 加入%
        r4 = new java.util.Date;         Catch:{ Exception -> 0x0033 }
        r4.<init>();         Catch:{ Exception -> 0x0033 }
        r4 = com.leiting.sdk.util.DateUtil.getDateStrCompact(r4);         Catch:{ Exception -> 0x0033 }// 取时间 SimpleDateFormat("yyyyMMdd", Locale.CHINA).format(date); 

        r2.append(r4);         Catch:{ Exception -> 0x0033 }//加入时间 格式为 yyyyMMdd
        r4 = ")";         Catch:{ Exception -> 0x0033 }
        r2.append(r4);         Catch:{ Exception -> 0x0033 }//加入 )
        r4 = r2.toString();         Catch:{ Exception -> 0x0033 }
        r1 = com.leiting.sdk.util.MD5Util.getMd5(r4);         Catch:{ Exception -> 0x0033 } // 取一下MD5
    L_0x0033:
        return r1;
        throw new UnsupportedOperationException("Method not decompiled: com.leiting.sdk.util.CookieUtil.encryptMsgMd5(java.lang.String, java.lang.String):java.lang.String");
    }

搜索 DesUtil.encrypt  

public static String encrypt(String str) {
        try {
            return SocketHelper.m5514E(str);
        } catch (String str2) {
            str2.printStackTrace();
            return null;
        }
    }

继续跟进 SocketHelper.m5514E

发现新版的apk已经将加密函数放到另外加载SocketHelper库文件里 那我们在lib/armeabi-v7a里找到libSocketHelper.so 拖入 IDA里

这里说明了 Key 为 leiting 然后跟进去就会发现

int __fastcall DesByJ::encodeAndHexToByte(int *a1, int a2, const char *a3)
{
  int v3; // r5
  const char *v4; // r10
  int *v5; // r4
  const char *v6; // r0
  const char *v7; // r5
  size_t v8; // r0
  int v9; // r8
  size_t v10; // r0
  int v11; // r11
  size_t v12; // r0
  int v13; // r5
  size_t v14; // r0
  int v15; // r0
  int v16; // r9
  int v17; // r6
  int v18; // r5
  int v19; // r0
  int v20; // r10
  int v21; // r0
  int v22; // r0
  int v23; // r1
  int v24; // r5
  int v25; // r0
  const char *v26; // r2
  int v27; // r5
  int v28; // r0
  int v29; // r5
  int v30; // r9
  int v32; // r0
  int v33; // r8
  int v34; // [sp+4h] [bp-24h]
  int v35; // [sp+8h] [bp-20h]

  v3 = a2;
  v4 = a3;
  v5 = a1;
  if ( a2 && (*(int (**)(void))(*a1 + 656))() >= 1 )
  {
    v6 = (const char *)(*(int (__fastcall **)(int *, int, _DWORD))(*v5 + 676))(v5, v3, 0);
    v7 = v6;
    v8 = strlen(v6);
    v9 = (*(int (__fastcall **)(int *, size_t))(*v5 + 704))(v5, v8);
    v10 = strlen(v7);
    (*(void (__fastcall **)(int *, int, _DWORD, size_t, const char *))(*v5 + 832))(v5, v9, 0, v10, v7);
    v11 = (*(int (__fastcall **)(int *, const char *))(*v5 + 24))(v5, "com/leiting/sdk/SocketHelper");
    v12 = strlen(v4);
    v13 = (*(int (__fastcall **)(int *, size_t))(*v5 + 704))(v5, v12);
    v14 = strlen(v4);
    (*(void (__fastcall **)(int *, int, _DWORD, size_t, const char *))(*v5 + 832))(v5, v13, 0, v14, v4);
    v15 = (*(int (__fastcall **)(int *, int, const char *, const char *))(*v5 + 452))(
            v5,
            v11,
            "getKey",
            "([B)Ljava/security/Key;");//生成加密Key
    v34 = v13;
    v16 = _JNIEnv::CallStaticObjectMethod(v5, v11, v15, v13);
    v17 = (*(int (__fastcall **)(int *, const char *))(*v5 + 24))(v5, "javax/crypto/Cipher");
    v18 = (*(int (__fastcall **)(int *, int, const char *, const char *))(*v5 + 452))(
            v5,
            v17,
            "getInstance",
            "(Ljava/lang/String;)Ljavax/crypto/Cipher;");//初始化 Cipher
    v19 = (*(int (__fastcall **)(int *, const char *))(*v5 + 668))(v5, "DES/ECB/PKCS5Padding");//使用DES_ECB算法
    v20 = _JNIEnv::CallStaticObjectMethod(v5, v17, v18, v19);
    v21 = (*(int (__fastcall **)(int *, int, const char *, const char *))(*v5 + 132))(
            v5,
            v17,
            "init",
            "(ILjava/security/Key;)V");
    v35 = v16;
    _JNIEnv::CallVoidMethod(v5, v20, v21, 1, v16);
    v22 = (*(int (__fastcall **)(int *))(*v5 + 60))(v5);
    v23 = *v5;
    if ( v22 )
    {
      (*(void (__fastcall **)(int *))(v23 + 64))(v5);
      (*(void (__fastcall **)(int *))(*v5 + 68))(v5);
      v24 = (*(int (__fastcall **)(int *, const char *))(*v5 + 24))(v5, "java/lang/Exception");
      v25 = *v5;
      v26 = "Des init fail!!";
    }
    else
    {
      v28 = (*(int (__fastcall **)(int *, int, const char *, const char *))(v23 + 132))(v5, v17, "doFinal", "([B)[B");
      v29 = _JNIEnv::CallObjectMethod(v5, v20, v28, v9);
      if ( !(*(int (__fastcall **)(int *))(*v5 + 60))(v5) )
      {
        v30 = v34;
        if ( v29 )
        {
          v32 = (*(int (__fastcall **)(int *, int, const char *, const char *))(*v5 + 452))(
                  v5,
                  v11,
                  "byteArr2HexStr",
                  "([B)Ljava/lang/String;");//封装成十六进制字符串
          v27 = _JNIEnv::CallStaticObjectMethod(v5, v11, v32, v29);
          if ( (*(int (__fastcall **)(int *))(*v5 + 60))(v5) )
          {
            (*(void (__fastcall **)(int *))(*v5 + 64))(v5);
            (*(void (__fastcall **)(int *))(*v5 + 68))(v5);
            v33 = (*(int (__fastcall **)(int *, const char *))(*v5 + 24))(v5, "java/lang/Exception");
            (*(void (__fastcall **)(int *, int, const char *))(*v5 + 56))(v5, v33, "Encode byteArr2HexStr fail !!");
            (*(void (__fastcall **)(int *, int))(*v5 + 92))(v5, v33);
          }
        }
        else
        {
          v27 = 0;
        }
        goto LABEL_9;
      }
      (*(void (__fastcall **)(int *))(*v5 + 64))(v5);
      (*(void (__fastcall **)(int *))(*v5 + 68))(v5);
      v24 = (*(int (__fastcall **)(int *, const char *))(*v5 + 24))(v5, "java/lang/Exception");
      v25 = *v5;
      v26 = "please check input argument, last block incomplete in decryption";
    }
    (*(void (__fastcall **)(int *, int, const char *))(v25 + 56))(v5, v24, v26);
    (*(void (__fastcall **)(int *, int))(*v5 + 92))(v5, v24);
    v27 = 0;
    v30 = v34;
LABEL_9:
    (*(void (__fastcall **)(int *, int))(*v5 + 92))(v5, v17);
    (*(void (__fastcall **)(int *, int))(*v5 + 92))(v5, v11);
    (*(void (__fastcall **)(int *, int))(*v5 + 92))(v5, v30);
    (*(void (__fastcall **)(int *, int))(*v5 + 92))(v5, v35);
    (*(void (__fastcall **)(int *, int))(*v5 + 92))(v5, v20);
    return v27;
  }
  return 0;
}

以上的伪代码 还是比较好分析的,既然知道了加密方法 就可以写程序跑一下了。

0x03 封装程序

使用易语言 简单又方便

0x04 写在最后

本篇文章仅供学习交流,提供一个学习思路。致谢所有为逆向工作做出贡献的所有大佬。

转载请注明来处。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

评分

参与人数 2好评 +2 精币 +4 收起 理由
mgfz + 1 + 2 新技能已get√
2017浮影 + 1 + 2 新技能已get√

查看全部评分

发表于 2022-7-24 02:08:22 | 显示全部楼层   广东省东莞市

你是曾昊?
回复 支持 反对

使用道具 举报

结帖率:0% (0/1)

签到天数: 4 天

发表于 2021-11-29 04:04:07 | 显示全部楼层   安徽省淮南市
学到了新的东西,感谢楼主的分享。
不知道大佬有没有兴趣研究游戏的加密解密
回复 支持 反对

使用道具 举报

签到天数: 11 天

发表于 2021-11-10 18:42:42 | 显示全部楼层   湖北省武汉市
支持楼主了
回复 支持 反对

使用道具 举报

发表于 2021-11-7 02:58:45 | 显示全部楼层   江苏省徐州市
支持楼主了
回复 支持 反对

使用道具 举报

结帖率:88% (7/8)
发表于 2021-9-21 23:16:48 | 显示全部楼层   重庆市重庆市
牛x真厉害
回复 支持 反对

使用道具 举报

结帖率:100% (11/11)

签到天数: 1 天

发表于 2021-9-19 17:51:49 | 显示全部楼层   浙江省温州市
不得不说 大佬牛逼
回复 支持 反对

使用道具 举报

结帖率:100% (4/4)

签到天数: 1 天

发表于 2021-9-19 17:30:15 | 显示全部楼层   浙江省衢州市
太强了
回复 支持 反对

使用道具 举报

结帖率:100% (3/3)

签到天数: 22 天

发表于 2021-9-19 16:43:29 | 显示全部楼层   广东省汕头市
可以的
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则 致发广告者

发布主题 收藏帖子 返回列表

sitemap| 易语言源码| 易语言教程| 易语言论坛| 易语言模块| 手机版| 广告投放| 精易论坛
拒绝任何人以任何形式在本论坛发表与中华人民共和国法律相抵触的言论,本站内容均为会员发表,并不代表精易立场!
论坛帖子内容仅用于技术交流学习和研究的目的,严禁用于非法目的,否则造成一切后果自负!如帖子内容侵害到你的权益,请联系我们!
防范网络诈骗,远离网络犯罪 违法和不良信息举报电话0663-3422125,QQ: 793400750,邮箱:wp@125.la
Powered by Discuz! X3.4 揭阳市揭东区精易科技有限公司 ( 粤ICP备12094385号-1) 粤公网安备 44522102000125 增值电信业务经营许可证 粤B2-20192173

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