上一主题 下一主题
ScriptCat,新一代的脚本管理器脚本站,与全世界分享你的用户脚本油猴脚本开发指南教程目录
返回列表 发新帖

记一次逆向拆包的过程(三)

[复制链接]
  • TA的每日心情
    慵懒
    2022-3-8 11:41
  • 签到天数: 2 天

    [LV.1]初来乍到

    22

    主题

    857

    回帖

    1356

    积分

    荣誉开发者

    积分
    1356

    荣誉开发者卓越贡献油中2周年生态建设者油中3周年挑战者 lv2

    发表于 2022-11-11 23:20:51 | 显示全部楼层 | 阅读模式

    前文:https://bbs.tampermonkey.net.cn/thread-3560-1-1.html
    https://bbs.tampermonkey.net.cn/thread-3572-1-1.html

    书接上文,我们前面重写请求已经把UID暴露出来了,直接在Charles中搜索这个UID,发现大都在client.app.coc.10086.cn这个域名下的cookie里,那就像前面一样,搜索Set-Cookie请求,可以确定是这个请求设置了UID:
    https://client.app.coc.10086.cn/biz-orange/LN/uamrandcodelogin/autoLogin
    让我们复现一下,将请求头和data照抄一遍,成功得到了新的UID。这里注意到Set-Cookie是这种格式:
    JSESSIONID=XXX; UID=XXX; Comment=SessionServer-unity; Path=/;HTTPOnly; ticketID=XXX; Secure
    如果你对cookie有一定了解,可以知道这种写法其实是错误的。在Set-Cookie中,由于包含Path、Secure、HttpOnly等字段,这些字段之间使用分号分隔;而响应头中允许发送多条Set-Cookie,也允许将这些Set-Cookie合并为一条,其使用逗号分隔。在我们的情况中,Set-Cookie全部使用分号作为分隔符,这会导致浏览器的解析出现问题,用开发者工具或GM_cookie查看client.app.coc.10086.cn域下的cookie,可以发现只有一条JSESSIONID,其他的都被丢弃了。
    为什么客户端就能正常工作?因为客户端并不是真正的浏览器,他对cookie的处理就是当成一个字符串存储,然后在发送请求时手动携带上去,可以去看一下后续请求的cookie,跟这个Set-Cookie一模一样。当需要解析cookie时,是通过解析字符串手动实现的,可以看看这个函数:
    com.cmccit.basemodule.helper.ParamUtil.getUid
    这种操作属于代码跑起来了就算成功,完全不考虑规范问题,应当认为是一个bug。不过问题出现了我们总得解决,尽管XMLHttpRequest和fetch不允许直接读取Set-Cookie,但GM_xhr是允许的,因此我们可以从响应头中获取Set-Cookie字段,再手动解析出UID。这里带出了另一个问题:脚本猫在解析这个古怪的Set-Cookie时出现bug,导致Set-Cookie丢失了。这个问题在我与一之大佬反馈后,目前于脚本猫v0.10.0-beta.1得到解决,不过由于版本还不太稳定,最好等待0.10.0正式版再做测试。
    继续我们的分析。显然这个autoLogin的data被加密了,请求头中也包含很多x-开头的加密字段,直接从Java源码里找。搜索x-nonce,可以找到处理加密的地方是(这里要用jd-gui 1.4.0查看):
    com.cmccit.netmodule.CommonHttp
    先不管请求头的处理,注意到里面有一个encrypt函数:
    11.png
    EncryptionAesUtils.encrypt这句已经明明白白告诉你了,这是AES加密,第一个参数是明文,后两个参数是什么呢?了解AES的话应该可以直接猜出答案:是key和iv。我们姑且这么猜测,key和iv有2套,分别是EncryptionContent.requestKeyByte_online/9791027341711819和EncryptionContent.requestKeyByte_gray/1234567890123456。点一下requestKeyByte_online跟进:
    12.png
    好家伙,这不就是密钥吗?在脚本里测试一下,需要用到CryptoJS这个库,我这里给出加解密的函数:

    // aes加密
    function aesEncrypt(str, key, iv) {
        const encrypt = CryptoJS.AES.encrypt(str, key, {
            iv: iv,
            mode: CryptoJS.mode.CBC,
            padding: CryptoJS.pad.Pkcs7
        });
        return encrypt.toString();
    }
    // aes解密
    function aesDecrypt(str, key, iv) {
        const decrypt = CryptoJS.AES.decrypt(str, key, {
            iv: iv,
            mode: CryptoJS.mode.CBC,
            padding: CryptoJS.pad.Pkcs7
        });
        return CryptoJS.enc.Utf8.stringify(decrypt);
    }

    key和iv不能直接传,要用CryptoJS指定的格式,key是字节数组,先用这个函数转成十六进制字符串:

    function bytes2hex(arr) {
        return arr.map(key => ('0' + key.toString(16)).match(/..$/)[0]).join('').toUpperCase();
    }

    得到62414967767741754134746244723964,所以

    key = CryptoJS.enc.Hex.parse('62414967767741754134746244723964');

    iv是字符串比较简单:

    iv = CryptoJS.enc.Utf8.parse('9791027341711819');

    由于AES是对称加密,加解密用的相同密钥,我们可以试着用这套密钥来解密一下,把data放进来试试,哈,成功解密出一段json。响应数据看着也是乱码,试一下结果解密失败,换成gray的那套密钥,还是失败。且慢,我们得到的密钥明明白白写着requestKey,这下面不是还有个responseKey吗?故技重施一下,得到responseKeyByt_online的hex是47533756656C6B4A6C35495431757751,再试一次,解密成功了!继续尝试,你会发现所有client.app.coc.10086.cn这个域下的请求,都可以用这套密钥解密成功。这种简单根据函数名大胆假设的做法,可以省去很多分析源码的功夫,在逆向中是一种很有用的技巧。
    对于请求头的处理全都写在com.cmccit.netmodule.CommonHttp.requestData这个函数里了,这种就是纯Java分析,没什么可说的,之后我会发一个成品的脚本,可以参考里面代码。
    现在来研究下data,我们已解密出明文,data是一个json对象,对比其他client.app.coc.10086.cn的请求,可以发现属性基本是一模一样的,只有reqBody不同。这些相同的属性含了你的设备信息、应用签名信息、个人信息等内容,如果只是自用脚本,照抄就可以了,发布的话需要做匿名化处理。测试一下发现有些可以去掉,去不掉可以尝试用0填充,最后发现只有ak和xk这两个比较特殊。ak的设置在requestData函数里,跟进去看到是应用签名信息,照抄即可。xk比较复杂,跟到com.mc10086.lib.base.AppDataManager.getXk可以发现,xk写在数据库里了,经过多次搜索,我在这里找到了线索:
    com.mc10086.cmcc.view.tabs.AppTabFragment.getXkData
    从这里跟进去,最终可以定位到:
    com.mc10086.cmcc.helper.XkHelper.queryData
    该函数发出一个请求,猜测是从响应数据中获得xk,那这个请求在哪里呢?多次启动APP抓包发现,xk是不变的,猜测是第一次启动时的请求。手机设置->应用管理->中国移动->清除数据,这个操作会把应用初始化,重新启动抓包,结果抓到了这个:
    https://client.app.coc.10086.cn/biz-orange/DN/init/startInit
    该请求的xk是null,响应数据中包含了一个ssv参数,而后续请求携带的xk,都与这个ssv相同,由此可以断定,这就是获取xk的请求。
    脚本测试一下,发现reqBody中除了ssk参数,其他都是非必须的,ssk的处理写在queryData函数里,跟一下看看:
    13.png
    很明显是对手机设备信息进行拼接然后做DES加密,跟前面AES的分析是一样的,得到key为40786927616E256C766469616E237869746F6E6762757E26,iv为01234567。DES的加解密与AES基本一样,只需将前面函数中的CryptoJS.AES替换为CryptoJS.DES即可,不过这里要注意,key是24字节,CryptoJS.DES只支持8字节,应替换为CryptoJS.TripleDES。
    解密ssk,看到确实是设备信息的拼接,既然要做匿名化处理,干脆全部替换成等长的0:
    00000000000000000|#$0000000000000|#$0000000000000000|#$00:00:00:00:00:00|#$null
    加密得到新的ssk:
    +SqABQLTnglfIwDzp4GnO6PPsXqrQ3K9ZGDeJum/OObGx2Yyn4dwWwkBWekX8KIGg7nhMj6fGDPWARjKIOJ2sGApIwvaXIcjdLNp8UaNyvM=
    用这个ssk构造startInit请求,就可以得到我们自己的xk了,后面脚本其实不再需要包含DES算法,直接用这个现成的ssk去申请xk即可。测试一下后续请求,发现autoLogin不好使了,响应数据提示:尊敬的用户,您好,该号码已在其他设备登录,请重新登录。显然服务端是根据xk来判断登录设备的,我们APP注销一下重新登录,抓到登录请求是这个:
    https://client.app.coc.10086.cn/biz-orange/LN/uamthreenetworklogin/login
    发送手机验证码的请求是这个:
    https://client.app.coc.10086.cn/biz-orange/LN/uamrandcode/sendMsgLogin
    脚本里测试一下,配合手机验证码成功用这个“虚空设备”完成了登录,之后又可以autoLogin了。既然这么麻烦,为什么不直接用抓包得到的ssk呢?因为ssk包含你的个人设备信息,脚本给别人用的话相当于所有人都在用你的手机登录。另外使用这个假设备登录以后,会发现手机上的登录失效了,两者不能共存,即脚本与手机会互相挤掉线。出于方便使用的角度考虑,可以在脚本中提供ssk的设置项,会抓包分析的用户填入自己的ssk,不会的就用假设备。
    最后一个问题,autoLogin和login的reqBody都有一个cellNum属性,应该是被加密后的手机号,在jd-gui中搜索autoLogin,找到:
    com.cmccit.loginmodule.helper.manager.autoLoginOnekey
    跟进sendOneKeyAutoLoginReq,再跟进EncryptionLogin.getEncryptStr,后面的是不是很眼熟?没错,这就是我们一开始分析UID加密的地方,只是这次换成了Encrypt函数(上次是EncryptTEL),动态链接库还是同一个,分析方式完全一样,最终读出加密公钥rsa_s:
    N: 968E9ACF9E59B4E2E1BB24266EE39043788A45F788ECDC5C971F4964C3267CF20AFD3B7F029B68A9A9B65D77D0306B8319DF0C174F3DD82EF3894A6C38E23634F6095A81901AD7E6650911C0910F12C7DE50A6FCEE3AE3563CC5985C46A965DB2AF49E94F69B62F67FF3D5C0F79782572375E5F8B44AA43C0CA6D48E8A969BEB
    E: 10001
    至此,我们完成了全部破解工作,可以动手写成脚本了,后面我会发布完整脚本以供参考。如此大费周章只为实现一个签到功能,这些看似简单的请求背后需要复杂的分析工作,脚本的编写也绝非易事啊。其实APP里还有很多其他功能可以自动化,这里就不继续深入了,方法已经给出,读者如有兴趣,不妨作为练习。

    已有3人评分好评 油猫币 贡献 理由
    脚本体验师001 + 1 + 5 很给力!
    王一之 + 1 + 4 + 1 赞一个!
    tfsn20 + 1 + 7 好文章,ggnb

    查看全部评分 总评分:好评 +3  油猫币 +16  贡献 +1 

  • TA的每日心情
    无聊
    2023-11-2 17:37
  • 签到天数: 275 天

    [LV.8]以坛为家I

    107

    主题

    438

    回帖

    944

    积分

    荣誉开发者

    积分
    944

    荣誉开发者油中2周年卓越贡献生态建设者

    发表于 2022-11-11 23:34:48 | 显示全部楼层
    gg太强了
    I frequently record, because want to leave something.
    回复

    使用道具 举报

  • TA的每日心情
    开心
    2024-3-3 00:00
  • 签到天数: 117 天

    [LV.6]常住居民II

    27

    主题

    588

    回帖

    521

    积分

    专家

    积分
    521

    油中2周年生态建设者油中3周年挑战者 lv2

    发表于 2022-11-12 14:07:35 | 显示全部楼层
    这比看美女还舒服
    入驻爱发电 让这世界充满爱 https://afdian.net/a/vpannice
    回复

    使用道具 举报

  • TA的每日心情
    开心
    2023-2-28 23:59
  • 签到天数: 191 天

    [LV.7]常住居民III

    620

    主题

    5084

    回帖

    5958

    积分

    管理员

    非物质文化遗产社会摇传承人

    积分
    5958

    荣誉开发者管理员油中2周年生态建设者喜迎中秋

    发表于 2022-11-12 14:59:07 | 显示全部楼层
    这波有意思!
    混的人。
    ------------------------------------------
    進撃!永遠の帝国の破壊虎---李恒道

    入驻了爱发电https://afdian.net/a/lihengdao666
    个人宣言:この世界で私に胜てる人とコードはまだ生まれていません。死ぬのが怖くなければ来てください。
    回复

    使用道具 举报

  • TA的每日心情
    开心
    2023-2-28 23:59
  • 签到天数: 191 天

    [LV.7]常住居民III

    620

    主题

    5084

    回帖

    5958

    积分

    管理员

    非物质文化遗产社会摇传承人

    积分
    5958

    荣誉开发者管理员油中2周年生态建设者喜迎中秋

    发表于 2022-11-12 14:59:09 | 显示全部楼层
    这波有意思!
    混的人。
    ------------------------------------------
    進撃!永遠の帝国の破壊虎---李恒道

    入驻了爱发电https://afdian.net/a/lihengdao666
    个人宣言:この世界で私に胜てる人とコードはまだ生まれていません。死ぬのが怖くなければ来てください。
    回复

    使用道具 举报

  • TA的每日心情

    2023-8-10 19:52
  • 签到天数: 9 天

    [LV.3]偶尔看看II

    3

    主题

    23

    回帖

    25

    积分

    助理工程师

    积分
    25
    发表于 2023-7-17 16:18:35 | 显示全部楼层
    动态链接库 这块还是没弄太懂,可能是因为C语言不熟的原因
    回复

    使用道具 举报

  • TA的每日心情
    开心
    2023-9-13 14:01
  • 签到天数: 1 天

    [LV.1]初来乍到

    0

    主题

    2

    回帖

    4

    积分

    助理工程师

    积分
    4
    发表于 2023-9-10 21:51:09 | 显示全部楼层
    大佬厉害,我循迹而来,经过2天摸索,最终失败了,app又更新了。关键参数找不到解密方式,大佬可否再出个更新版的帖子。
    关键点
    https://client.app.coc.10086.cn/biz-orange/LN/uamonekeylogin/login HTTP/1.1
    post参数多了个hyToken,找不到算法
    提交后提示
    {"retCode":"110001","retDesc":"系统繁忙,请稍后再试"}
    app升级9.0后访问地址也变了。没找到脱壳方法。
    可能frida还有效。脱8.0的倒是不错。
    回复

    使用道具 举报

  • TA的每日心情
    慵懒
    2022-3-8 11:41
  • 签到天数: 2 天

    [LV.1]初来乍到

    22

    主题

    857

    回帖

    1356

    积分

    荣誉开发者

    积分
    1356

    荣誉开发者卓越贡献油中2周年生态建设者油中3周年挑战者 lv2

    发表于 2023-9-10 22:24:18 | 显示全部楼层
    dragon100 发表于 2023-9-10 21:51
    大佬厉害,我循迹而来,经过2天摸索,最终失败了,app又更新了。关键参数找不到解密方式,大佬可否再出个更 ...

    抱歉,暂时没有跟进新版本的想法,一个是太花时间,另一个是法律问题,这种东西说到底是不太见得光的,万一中国移动要搞我就麻烦了
    回复

    使用道具 举报

    发表回复

    本版积分规则

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