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

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 2153|回复: 38
上一主题 下一主题
收起左侧

[原创] 一个有意思的python软件逆向

  [复制链接]
跳转到指定楼层
楼主
Benx1 发表于 2024-7-3 12:05 回帖奖励

声明

本文章中所有内容仅供研究、学习交流使用,不能用作其他任何目的,严禁用于商业用途和非法用途,否则一切后果自负,与作者无关。您必须在下载后的24个小时之内,从您的电脑中彻底删除上述内容。如果您喜欢该程序,请支持正版软件,购买注册,得到更好的正版服务。如有侵权请发送邮件Service@52pojie.cn联系论坛管理员删除文章

这个软件时间比较久了,我就不放链接了,主要是学习思路,XX中控v1.3

1、检查软件

拖到PEinfo里面查壳

PyInstaller打包,py软件无疑,接下来就解包就完事了,下载 pyinstxtractor.py 用python解包,注意python版本要一致才能解出来PYZ里面的文件

运行软件查看特征

软件跑起来没啥特别的,点击注册,然后输入个假码,可以看到提示了一个弹窗,输入正确的激活码

没啥有用的消息,开搞

2、破解思路

刚开始打算动态调试找关键点的,但是之前的软件也是动态调试踩了坑,在虚拟机的海洋里面遨游,找不到关键点,但是发现了个神奇的东西,我x64dbg打开就被关掉了,第二次才能打开,看来这个软件是有反调试的(后面检查发现不止x64dbg,x32dbg,CheatEngine,IDA都被检测了)。但是这里就稍微差点了,检测应该开个单独的线程持续检测,但是作者在检测了一次之后就停止了检测,所以第二次才能打开。而且文件有完整性校验,改了之后文件sha256对不上他会云端下载个新的替换了。

3、开始破解

开始开包,包打开长这个样子

主函数一点有用的都没,大致可以猜出来这个inlet应该就是程序入口(至于原因自己多拆几个包就知道了),可以看到程序运行了start,从runner包里面导入的,去pyz里面找了找runner包,发现没有,那更寄了,runner可能是一个pyd,返回这个exe的目录,可以在目录里面发现resources,在里面找到了我们想要的runner.pyd。已知pyd不可逆(为python代码),从源代码找蛛丝马迹的方法失败,看下一步了

动态调试

已知动态调试被检测了,而且找不到关键点

那我们跳过动态调试,直接开始静态

打开大姐姐(IDA),拖入runner.pyd,啥也看不出来,关闭大姐姐,开VAN笑,我能这么简单就被劝退吗。继续研究,先找几个函数看看特征,发现里面的函数基本一致,但是里面的字符串是明文的,函数功能基本看不出来,可以发现程序调用的py的函数名字,只能慢慢跟流程,看看程序都干了什么

在这里我们发现了几个比较关键的点,一个加密库,(至于为什么不是前面的sha_256和RSA(rsa加密是在明文里面找到的resources\certifi\cacert.pem)因为sha不可逆,不会用来当请求传输,RSA我已经试过了,不是这个)。两个http请求,一个base64字符串(实际是秘钥,这里先假装不知道干嘛的)。

现在看着一下就找到关键点了,实际这个东西我前后研究了一周多才找到这些关键点,所以做逆向最重要的就是耐心和细节。善于发现代码里的蛛丝马迹

柳暗花明又一村

发现了http请求那就转换思路,我们抓包改响应,伪造正确响应返回。现在转向第二战场HttpDbgPro(为啥用HttpDbgPro不用之前的小黄鸟了,因为小黄鸟有缺陷,漏包,很多HttpDbgPro能抓到的包小黄鸟抓不到,虽然小黄鸟的重写脚本之类的很好用很方便,但是一个抓包工具漏包那我是不能接受的,如果是使用姿势有问题,希望能提供正确方法让他不漏包)

打开HTTPDbgPro,设置过滤,重新运行软件,可以看到软件在启动的时候有两个请求,一个GET一个POST,这里先不管,看看登录发送了什么。点击登录,发现两个POST请求,根据请求链接猜测一下一个是登录一个是心跳(实测过了verify就是心跳,几分钟发一次的)

但是这里的请求内容和响应内容是加密的,先分析下加密后的内容,看起来像是BASE64,解码一下试试发现不对,但是每个请求不管是请求体还是响应体都是‘gAAAAABm’开头的,看起来是自定义加密,我们喂给GPT看看

哦豁了,GPT直接识别出来加密方式了

那我们继续问问秘钥有什么特征

好了,已知秘钥带等号,那就简单许多了,直接去IDA里面搜索=

成功找到了一条,去python里面写代码解密一下

An error occurred during decryption:解密失败了,先不担心,找他对应的请求,发现请求是/multiple_files_value_verify那解密这个里面的请求体和响应体呢

请求体

{"file_paths": {"resources/cc.pyd": "7d3e2366464a411a1d470ac59813c61573d5e387cc7b5916d28d7e6f935a3847", "resources/dev_opn.pyd": "b012c3e7c1acb26e41d1ea0d02bc49dd1180a56a8257dc38ce813949c3463482", "resources/setting.pyd": "4387baea0cd32fd1c77db8bb123f0480094769533df13204daa4e9220dd265f7", "resources/img_rc.py": "2d96fde0fdbcc6d557f5d1c780cd0ffc721f8822c7fff63efd57d93015429c1f", "resources/lib/centercontrol.pyd": "83a2f532074c05184ac91faf24bdfd7bee129269f103db21b9b27b670c838123", "resources/lib/DialogBox.pyd": "1d071f441d159e3fa0110baad8b906826ab059bf6b8ea3c72b13218b5bfd4c57", "resources/lib/disclaimers.pyd": "8475c9f12549ca501a15464d20dfd22fa080bc871d05f27dba8a8c40a7e6c3e1", "resources/lib/helper.pyd": "d41b3b080156988fb84ac4aa0c990f468c716ce4284578d9bcad0d240e6a037c", "resources/lib/InputDialog.pyd": "d489a67649ca2d8055d51554fe0af2f27e618a0bba2d59a8d3f0a67e92e3570d", "resources/lib/localnetwork.pyd": "d8df10b1ca2fdc419e5e7d16e9a88a6ea90e8f6485a6431efb23d24ff7ae0258", "resources/lib/MessageBox.pyd": "9e2a3dc52a7689afd481d717606632d74252f134a3e93d44bd8efdbd653768c0", "resources/lib/phone.pyd": "9f9660875348df20c44adaad74b38c353ec3308db3b750b4074f5699e790a633", "resources/lib/settingui.pyd": "09fd8a6c1be334480362273528f4feec93a54632b6cbb4bd91ddc7815b404838", "resources/lib/SignatureTool.pyd": "7b6765ba7e3c5e64c0873c6bdc45619500803549f9d583bf95736a7deae9f2b4", "resources/lib/sreg.pyd": "cfed634a3ee25eb37613f9a475d9bd7a6467a571975581bce60790ad9cb4a8a1", "resources/utils/comm_tools.pyd": "f1ca3be73cedba5b651db036a1e92998368ee78a6c29c4707b73ca9c4c213abc", "resources/utils/ios_api.pyd": "0716ddd14e70edd2252dc12c5ca09426b9b4360d279bb34c01336973da4d1e0e", "resources/utils/tid.pyd": "de35c28d4c17eec071b8ee4f964ceeec2d9cdd7fc492b305f7af163f484b7e88"}, "num": "971", "sign": "c56da924d5b04f1e7ec1f56c5a1c61c9a73633eb4b487a82cce9e3f840f3093e", "folder": "cluster_control_files", "version": "1.3.0"}

响应体

{"resources/cc.pyd": true, "resources/dev_opn.pyd": true, "resources/setting.pyd": true, "resources/img_rc.py": true, "resources/lib/centercontrol.pyd": true, "resources/lib/DialogBox.pyd": true, "resources/lib/disclaimers.pyd": true, "resources/lib/helper.pyd": true, "resources/lib/InputDialog.pyd": true, "resources/lib/localnetwork.pyd": true, "resources/lib/MessageBox.pyd": true, "resources/lib/phone.pyd": true, "resources/lib/settingui.pyd": true, "resources/lib/SignatureTool.pyd": true, "resources/lib/sreg.pyd": true, "resources/utils/comm_tools.pyd": true, "resources/utils/ios_api.pyd": true, "resources/utils/tid.pyd": true}

这下就能确定了,加密方式就是Fernet ,只是每条请求的秘钥可能不一致,去其他pyd里面寻找总共是找到了6条key

# http://47.119.173.20:18003/multiple_files_value_verify   校验文件完整性的,作者简直天才,有问题直接重新下载
key1 = 'ayWCv9tLU1lmxDCNjIghboVL6SLSnetRiXRjZcu9Ziw='
#   http://47.119.173.20:18003/request_cluster_control_code  带了些信息上传了,不知道干嘛的 后面发现是拉云端代码的
key2 = '-q-CsAkWo1iPo2U35MGS5CYiJELMg3_VAf8PmMbROec='
#   http://47.119.173.20:18003/request_software_information   获取信息的
key3 = 'Ff8vE_M8E7u9FG2EFxOqICc0rMmNzN8ywChbTXWGovM='
# http://47.119.173.20:18001/software_register || software_verify 他两用的是同一个秘钥,一个注册一个心跳
key4 = 'iHy7diw8_43oseqGJjKWupDGlEIzRh7ToHVpDD2YKUE='
# http://47.119.173.20:18011/software_register 不是,先暂定 这是单个的注册加密算法XX助手的
key5 = 'arUYowV22DeqFmhOjja5OxcjyywFjBdUs5pSsaeGJyM='
# http://47.119.173.20:18003/request_single_control_code  # XX助手云端代码
key6 = '1GhQMT8aMYcAlEn-ugrKMAuzY3cnyyJ1Q6xskL-cMXs='

我上面已经把所有的请求和密钥全部对应起来了。XX助手是作者的另一个软件免费的,帮了很大的忙,接下来先用HTTPDbgPro伪造响应,我们先把XX中控的登录响应解密,是一个txt文本

假码登录请求体

{"code": "123", "uuid": "03000200040005000006000700080009", "ip": "", "requests_times": "1", "num": "2569", "sign": "9f468242fce1c30d780913a1cf0afaa6fdb80fce24c741c8d5d2f847ee628cdd"}

假码登录响应体

2|无效注册码

这样伪造有点难,我们去XX助手抓下包(因为xx助手也是作者的免费软件,但是作者送了一个激活码用来登录,我们直接解下xx助手的响应),注意这里的xx助手的登录加密秘钥和xx中控不一致,解码之后响应如下

XX助手响应体(登陆成功)

1|2029-5-21 00:00:00|1|9

我们吧这个响应用XX中控的秘钥重新加密,然后在HttpDbgPro伪造响应(Fernet 加密每次加密后的文本不一定一样,不用在意)

这会再去登录,可以发现我们现在的状态已经变成已激活了

但是过段时间就掉了,变成无效注册码,软件肯定有心跳,之前也说了两条验证,心跳和登陆用的是同一个秘钥,直接用登陆的内容返回就行

我们把心跳也伪造了,这样就不会掉线了,然后开始测试软件功能

可以看到手机已经能正常连接了,但是开启投屏之后什么都没有投出来,操控是可以使用的,刚开始以为是软件问题,去XX助手(免费的)上面试了试发现可以投屏,那我们再观察一下登录之后还干了什么

可以看到有个GET请求,但是无响应,刚开始我以为这是上传用户信息的,我们去XX助手(免费版)看看有没有类似的,在免费版上我们看到了类似的请求,但是是有响应的,猜测可能是远程代码或者远程变量,如下

远程代码响应

gAAAAABmRv8MolSu5VZnogyidKQPoiXqYQpXJKQuj5v8-9oM3u0sFv5XTGs7GbxfEm-X8c4d826NVc7KWn5bjiCzNWweSyIcK4mmt_VPY_oBINg8BNCDWQn30drd6Na0iPYGXlx09XmT3KWbACcgZoHHKTIH7J0eWqo2jy7spCjks2Ol7Up6XkU7I0AtJCs75vZlBpfQowLrS4Mfc4EDiU6vjUkOclDSkwFnbD_Rsw3BVn09gx0-XZDBJgh7ftZ9TEAYKV5mt-SyUuwqdmbfvCwuWgrIC15AWpeI8cxpqQhaufXKKVbPkE9fSOcfkcSOpZ6HLAtLTE3thG9bIRU0j0ixmv0CirPh0pAtQ4BNASWVqZN3PkQQBJHyKIlVfFjBnwNXeMbsKJGJeIlrXnpOiUf89a6QSoNJVhjo5Zrk-J6Nan5tQYoWgIdF0mMZrlK-Atxx-bZH8ZELb_NR9jv08kFcW5TLAT-k3SnSmxZcot5s1JAInU5cRaizHM2va0ZfovUmMZyCbNCEgbyIj89eC_mTLyV9GjlmRkIMyTTVIkkzuaVLgK0xpTkyNzmDgRd4UcEyHfnMXIe7xUA0GOkK8KmyHKB3fsCxuhrtYDCReGG3prYvNjC2_jk5oWVMK036sDuG0OjyILWms7IY-Ihk9yoUZ9_PbOgrkQUR3pEgZNde7y2H9FRU82pBQzYufWKQPCWXhVOE_zj0DKgNCXAVMHKXVoPDHIzGkbf9wVY1qsxsSWDQ7qkwKZB2h1LIGYK_4hdGk1FuNlhhx10IsFMUHl4qZ8ju7saxDZ-YC33EEbCer_CRPtJk9_XAAZww1ppe0EQg0k4chYFd21wDoGgDTveq34Lvur0IHPBaiuIsQ2b16RUOxM64_Hso0c8waTnjUDs1jGMhdnSBwnmIXWfupM42luxIhBlOMmGbnpZYdobhT4ph9X6NFiB_GV6n18iDkCIOT2lATSjdpUykTVYrv1_IwCaLWibaiAtLk0CJwRWG7YRKV46WFAxVKOSWxrqapW0NHhGUMAPspN1OVj59WJ36apmSm6D_CLfvj1GIbZiK5GCKf7h5RzyduzSQZiWqe0rueMOtPA2zg3BdNojkPlAr6C4H8dmbn_cH5U0kzEIrhQ9YQm8op5RriEduaUY5MjkmbGITKZKo_j7TivLNmzexcxu74uK-WVLgHdhDrpVjP4ByttavH9mMiVdC52JwJgmKCyLZy7UzPum50fqd5iFqjOknqaE6V-1zFFZmNYP5QhaReTdcR9ThqzaTCx0liVNKVFsT_jOQHY6Qwycu5lEMUIpMaYG_HIlaIf6kuNSO0wI6Qavz_D5KFXCQ1Qax_ueXVkwdSJKmP-ibzyCpwP--Bei-Z_kTxBnzV4IxjIwuEiRHuKK8Ej4d09i9E0iRfR9jkwoB4PhrvpeEkKkDId2ZQlvk9Bv6E8uCXj6bW9uSHQ_gA4hVGBQUBKmFH9MeYiKDihaloRYTq9G3Lw9xxy_EPLKmQN5cyqBT-iCaS0Yti9VgL1XTcEqiO_gwjDn77XU7TLWym3ZAFNePpBTjren5XGDzNd4o_sk8o1oZWKjTqr0KSD0unsTKA-90UwMi_6UPGtjcwy3Qwej3YAKm8Hovi8gXFfhPe6CmP1j7zkqPkkLMqP5Ayi8VWvVALNTXPIVafiU54tb5JgU7-bYeXyFBYOPoLdYIjYGh1yAMK_sjO510hxcjMCUMWfqjR5wzSJyxXbQSqC4L_gxw7Q8hHjjjZxlxhz1cpE4M3X7VvkJ97vHAePB49jdP7LXw3Ob4k4lD05FZeXaPzS0iou04isJun4Atp4ZKH44XGeDNnTBG_3Y8B64a1GTMSUuBo2icJ5Adu9tBMSa-0Twyfd32w9_uX3Ui2siUCOAwtdySvQa8lnApRvsR2Y4w68PgltxLD6LQKHiBoxIYOVRog2ZKqwWkzduTfBy1XXCkNdNXalZcUaEkwHB_ehm05NAa-tn8NKxSl6kxgUuNwvFzz2WxEiT0bOYzJQb6i7y6udGMBjlwvTfB9kpcTzclQxxlgOhPXYfuPfgAjtsSTZklmF7KPzu7vUEw6UjT49P-fexBevoEN86AZWI6DHmbDxGKzWCyFB8FCPxPpqVz1Fy1ZOFlFl6ClfFTG-keQ7U13khDXNiFlfVv5bO3pP3VVrT0Do3v-ltNuqA0WEkYMjTdF_oNY9D_yq5TpIGUW3kYZvLmKW08C51g_ttf2Z8MnCpinBuqS7-Qp25E0X0jOrhC8KjUFzGbARCCmnkfTCxkk9xmaHVFKk0jQcz_IUWWfHe95oQ8lE3_SBliMxzC5mBDfg_4x9DtxN9dCgt767yWfmiaket3tCNoD0Yxi7y-OnKWS3kxWKOZWjsBTfP07w20GXdOYSpWIfJZlUqUuaMFQXjmeSqaadrcWiuYok8Olww7imwGr1LUhy0hapsQG-0JIxvpNozMz3GJSWvXCuMe_ErDm5gKkIKRy-lPyKBQaPR0oCZP4Ey70lCkWQKO5amrTEEd-CH-gvchT0vq5Yh1WvQ81A3rQW2vm_XdsGpW53y0xgMFJd9zJ6eoCvHJtfCaYfI4Xf_hh0wucsmUnJ2GqRToN3q5TNOuLoB80jVhNVc2fjUw_li9LdDTXMKx5EmSA1iNWtAMQm5m2tvAxge-bb4W17aSyE4GZ0eRrXXr-dkrxOAxPof24vygtzXj7-iY6zF7Zn4ZfYVxxzo5qo-M2zXKF-ta3h6wm5E6RXnLdH0YJ4gr7udyvM8Ty7c7Swhyo5BGH5q1ThEje1oUhps8u3CALKSJ_eQ__CMqakebdKxXiZJDUZBkgFsBr5kUP0SHqaD7IcHM42M3RGTKZZ2nznes930XBQMZ1t75zfJPiOKieGUpqXyjiVCIUu4eFcmEF3e5NXatoQCxohLXPZ3k-ZopKaDuY6xbM3eqcQJqq3NAGNjve0px-Y_MwilGSuhhUUV1RRXRBt-r7-SrBpYzJWU4t6QmZ-g-d-FQp-5EMM8QE_lSW2o_kRyBrEAGyBcUUwMqWUKkqZGvSZ8bd9FMZez20p4XCZA1qQ4J6fUha505oTgx2WnwLQ--quM0c1PCSXP6KxI5uEatv2pfxNrDRCfVF-vLJPSt3mPvD5gkYUVgUQnZZx7X75NnxDM-7mq0qzzJphCBnZ7uOa6cVirzORrBIfRMImaGKOMFA7OONeon_zQBbCXuLkeGUX__IA3jjPclyf_CsnZ1U2OPQn75SXDARlVmqpAvAMOpg0vqAVds2pBnpGIwPS4nMcQpnMiS0Mf51tNJARyKSm7sxu33Dus8ne39Spzu7iBfQqKJkxx0CADshuOq6l8RQEgRboOK3MS-ganTVfmenKBPL-g0y5Bk8bo28-Xp-1-yMnGGBH1hYqlONn57xEL_I-TvA6dJTqCQHQEBUZdzOFF-VUn9eUqwLdKpEa0PmlweFtt7xDWYfIDc6_hTH_nv30b4hf89bW0_wmdWv9hC1H3ENHa-VuCrQF87RI7QKU0pS1tqbUoPZoXEipV_epLTz2lFk_TNfkxE8EiCe0KbM79YqHDg0EpRbWtOvvoBg7IpVvkc3XfCn9lYOlx5HeCDj8MgUBI6jKfrWv0LDUnEpksHsBNH-JqeDmCvI4XX8xapgn4s9tw3dAfF6wonJvhZWVfAhDqCZSvM8_ebjXv5fBvW8TG05uggKvOjBj7f_l98DALny0JedIK1kTRSodqwNq1BsJyppInZUkkBc0U5Jsg9tXvuC4PCKS8GUoXO_PMqVKZCSr7eHs6VpgI7n4bNuPsb9Jvfxe1d2gHUe5pulTvoGGLU99Sf9kUIY_h6VHZ_xP7MB20ZMJOUUoOFCSrIHSYqcEIEKl6Z-9pJUxR7f2RfZxN0Jvi662XtafqkrDuxaqRINFvXYSfe4SoNqNuVsNCgVMIZmzgHnH_xmEgdKZxSJ73FdgiMr8EcjbnunBHP3h36jI1nrEvY71ZroVHXMw1W9xcW8GUuYA4TIILfmpByCJpfBkWmlxBK8bR5ewYwgMKgfPcz-9SlPIJPIVhfI67Q_gQWJjQe3dHWhXjk0ghzCw6hUUBB87uxNo4vSZPTrkfezc_XFsTgihN0AcfgG-ce-TFqqsimF3L1uXwPF1Ra2qRrP_QfzRwT6s4Ie4_wBRrdWUvX4Vo1ho-LkCnO9g40jbGqBm5nOcT09N6lUkSgGTkU55F6aJIM7sqss1gbkL0kQZqdzpc6nUnisbNXC0Rw5B8PDrXNxGAGYsCFn2LwHbRdyIM1QoqHoUjjDUBE5z0XburgpxXO1JS0ANWBI0CDCe4xCQIC-UVeoqiW6kRI_dTKL8nPpxYD7JUUvn-eja3GK6ZN4YK_c4Fs4pv_u68yJRKmz5XDlvxhJXR08nKoHsc_ofKBBxO5Z-ywLMQD3ysf95cRyBlQpnXrbT4pUHBiadkKeAAJDJIxAA_AlswyeU0LIetwsz39zYjc9cGXQZ6aw-PHuqPNEIuLRK6cljnzD8vk2VW-1nLH7TiCtMWdcF6duJmXhymuatnUIbZbo1hv83n06ouOVn9NrHfBiMif6L1x4N4VgAw6MmnnA6E7OlM8BpWwvxN4GffcKY5dtuWVn8wuZkmzWM7fs3s_xxCUYNjqY12gXlwvIAX104fMySg52Ocu4fJX1VPN9e1hpS9tpe_IQt61H2sWcMXf-C9HBfvzMySJBwMVwHxztIMBMsnVLI_fJxJ4DLhHVFZMfbwsJ0yvAqbZxrQewsE7KGgyy7l8TKqegK5YRKROMmjBvVluD-8W1FGeVE0GOiQK_W1-bHFL5P_qdRaw2wrahZbIEa0KHk2WrUDSyHqA7QZmGSAtmWbFPcKL0bMl_IF2dV8j_rJdbzyKTEAKywWoa4VxryC8CJhCgFrASezgniLjkuaXnHfsHnOPj4Qdw7Y062jw7zm4tMY-vkvihrmgVVE2kmkr7Aq5uRTsSvjzPM36wfrU7sU8IzWtqCSRrTJ_EWY-n_Hb8pwOtJRpeNtJ_Fd46IvCAQKUpUbJmAUlhmLgcidKQtii-SkKRFsTctRMJIBLXkHHffKyPLD14v0rBDfBhxf5w0R21IqH08MNQaUL1VJVM-Qzbm4itHeoOeryRAkFBH0oMtezqiHB4du0NkysLjuqpR3AU9eXSfNTvZBgbuxuh_c6edUTU665phvNBziCm2K5O8BvHkZ1P4nA44npx2O8QIYcXJ0gI0xeeIpCVjkna5FtwS5OEkgO2g6bLHDWivWf9vRLVZXkK2cOL6fHKIu7C0oYQ0nAFoLeIHOC4Jg3MGVbKnkG5cdSw0OxpE4-zkmzpV5g49NxvGy1tOYbCk_1aHJonP4GfyMrxlavTaTeq--oojnspNePjexPcTOKcS4d6kK8ZWVb3A0gPnm1-xPr9jiTkz-m2a-vgRne4myjNDcza8CRBKunWzfT-TO-L9-ibELCmAElp6po4htgxiD-t9qvrxkJaBDxqM2Z8Yau3n9Wa1e8qYcKtAoZo5J4F6gZpr9AGsqnDuzCgdxTzU8ulQhpqd6yfJNhxM_jTPlzRsinTqzm2kNtJhG0cgt_LEKqSiRHTmE8t33IWEQ0vNJZSXTBuJxYZZcWZaeglK6Iuen6Q3cNd5RF4INxDPy2BUSOxmN2ILPFJ1mof553z8-R0jLMgLD-yIQ6bSDM1PtpSoBG9ZXQP-VD9HNzjy__6WZWNzW_da-zN_uhvjFR_thRaTvoZOiYXjhVU5mYQ6Qhuk45Z_ICBYmHeCcnFiq9sC26Pqyw0jjgQ-DHeRXHyYYqvVRklaw0zr09ipl0nqmbsbPR6I8JV3MGpk28uaaO4fVdpGlj123DMFgsFOnKJqbeu9RnbQuFQG99O2-sRRDpwvsUD-UwdQiLbQiyoqQCwoH7McW3nf7_NJnbfTpVQdy3l9qgG-a-UPDnU2qW_hQhq5mV-Q5KRwOKFFxNt1UbCOzcRn12dapOAiH5hj2xYXHrp_fqSbwoljMQJ_d_broXl-47oXYP40vc5UNltHNY4t2c-XqI5UJbD7yoRnky1xs3hh8ghga-3PSEbmZgmls6QQqWT4ogl6Vyhr_CLix3lU7i3JTYFjgIhyLksNHpdkj-wKnhgsDYYyDw23siCLu3RHK2y3lS3GsSlbMqU2KbBHo_dwQbVa97bN2BTXlJJSO-8OPTFOVcNHSot5UCCP_KfjD2GuvCD7rey54-MgScgon55Qj0IKXx9-gILb5nzJWaezVAyBq2RSYHHHAHTgZuwDd9HXyI1uGzchpab1Hhu9hzg-O9Vr0ILl2iENuPZ1SmpARQnL-rPr5L0VmCHt0jooLzgc6qQLRdQuBW5_vXTXLLk1ej85cDykg7d2vVMSVcB4xlhsxVo4okH6iXwefEmsTQ-FGNuhxSr_qplzObTT5qOXYrOgcv-ehy46IoFIYCeOUPp_YVQcA3v6VpOX6BSPhfWodbxUJ7P22YUxYCO-QoZrj1Nt8e-W35FfWGERuv2lDwiOmcSu3Ka7HsunboAL-461QFGmVFyVXNiY0FsSi83OsJwzat9ViQmVESIINQClts_lXz8sUg1X2m7gG7ss3_5sifA2bzjSDQDkqzhX9LDnnsXJIO28NpFAWcB7z7Kjllb5d4n0cG5ovc4fNfIUNRXO4pf5Q9nhnqJ3Oj0OS3jJvR7mvQLaQGjtoR3nM2eMulhLPFAtVXK-nV0TYA6buxYAWKKrtjCv-ZKPxQFnfcgEkMyLqyT274MCDj21c3EZWwHMHlpvicpUVykp_qqM_zzHyFmrjqYzLIkkYZKCDr5Jbh6U8OmCyIEVWYKySRZzCN7A-VeoEsIqcsDlivstKq49u6tP97L1nsIhD9CVRzJkc0QxLO4RBgND1bkhw-whnM3w_y-UhtgyyMEBx04BYpkw57VP2dbA9pvW9J8SmnmYZnUYYhwiJ6pT9Oj-uzCzgFAx3ytAddQHOjDfzXEo4D0dztefKAGjOBfWn3m468Xszr3SgnhnAI0m5kyKaNDduayL9j2KNm4yvvleBRlgm_aaUvGIa9jeUpwl9bZb6Alo84qGiYTPcy5xuXGnDHj5WxyAujcHNSybF00K3FQQpcqE4SBK0GvKmwraCY4tZYhs4vm_cyTC-YZ4E1Mq353jrtF4TGbBLwqDsSTKXYMsl8Hb5FchLpQ3lez99zInhYjExugDfaKay-SkDBjlWD2uI1TGE_V-mNNCQfz5MsWu6lA6rov5KEH4_9lxAT0OU21ShzsvNeAbYOeFWGGXewXUEwCqMN4-yY-3DJmM2ulqMyD7JoeFSNdENbpB5dDDqHJRdU7j-cAMnHXOKOA_ypRAhyeKuBpF58tL_wAR_yEZCejDgHFLdr0flvvV2csN3S70IZGdN7VfhZe1lZQDyDC0__IiFRgqufDTMr0kIN1vhGyfuc-t-m66FdwZCOD6xrqGKCWcHv6YyNuOcOPZrp-v9KdyCrfrCsCOFuFPmtLQaqeJaE6A-wOQeie3Hz6bP8Jv4V7E7bI3j0A4j9znOhCpFFL91rh07IW6hCuriiP6-xf5kCrLkcvFiW9cJGN4eh4f4y-nGV_1gRxCGz0MKSBBaDhKWgBCFlqVqMnxOyQVc3RFa5wryPkysdJAY2jRz-FBlFqjJYUegeH_JaoM70ArdwgTCw0gEDi0Mune7DnRzzBECNzbfJcfL8cMcu-tSFn4bjhu098YbnSM3rNvIteGVof4hiGwedaJ2RTE1lPZz6QoIiEU2Iwty3auH1zzFpHMgbdMPeBSF2HxDSNrY61spsJ2HQ_Y39VYs0okiyc0geaPsi_QW7UG2misjWMombokYSVUALKSarauSgJwQO-uaHedImL8hvOwoGQv5jaKED3SI_xVTNVMQwUVLWWkCECkWfpvb3SWYjd5Gv3U3Ylb64j4AB41wGVTNCMv6f9CeACVErcYfpfALva3sfPu7hjezfND-oKzseJZfI7tDawl1ZJV6haBfr_YHhhrvs8U9xB2gsIfLI5aIx-VsvdbuP50ApJpgoJUlcLKcVekQLrmixGvGy-hdpAq2DPHvRQlh7QSL0QhI7a2vBEpU-UXec6GwZ1BkY_8QOSAHR0fy4THqQCMrj3fFziKO8aN7AVKOx2Vjzi93WY5z-eKI5gvpsyaXfYwaaRf1jowPeA-bK1Ue-YJaryH1PolkZ8YUhZvqU2UMpCCDG9DA8d0_CEML_D3NHeH-1Tc1MhFug5VhhWQpFmGn6fypGY3zEfRlQnBUEYMC5_krAgp6IYPWIqRSD0pGe3E7p8NkEVUmentp928fOUipSqMNFf66IkBlaqcEp-oSPOb6QLsCllZS_hhhp1k4uHBc-kSl3gpmJsiVTIdTvLidTjJkplExhKjR1L-q6JZQI7S43EOl9nZ5ZmlvXIPUC9qyLEOdeGfEPRLXGwwLBgWLTEwsUGchLtjAVJet9INM4rLPiMYxYp6_NpXG3Ov17HYeiby7_8IW2SMtOfTr3IedsLMi1u2WugGhL1MK7O3NKvKi2jFp2WPC9AMAI1ZmQcDfWOMAlrkb9Wkv4g5fj_daqZF7qkckTfPgMSpJLfDSj7AYe1JWbZ8bOhSGdgV8kZm8w0cKITdzlNc2pakbSQMzvO9MHMOdUr2ceOOfMbgRWGuBefIEihur8NSsI99FABtBirU2c7x0H70eB00WE99B6VMRT11kRl0Xlf7w_rAYbCwHemVzHX9lqvg7R9sahVDlCl329PhIkj_pMmDj5rBmLXn8oK3fYseUxT_ZorNYDphpFw31PWWvR9x2CGbEv-nd0ALNia7ahqhNzWK8dmdI0pxJUPLhW4pyfk7aCSmz0loRuTxdDj39EwUgQpnQMJSnWP8RX3v2_7CuAWSXxUgVZddWaSuBFcMHzvKmTMcOCSuSK28zDUIb04CP45Ta0WXHYe8zxpoXMkA4rdCzD_8lFW8TmDe8SclfjUrQtTEQx6c19s5Xqpm-wFcfaKtYTc_EMzhzFsOzCt5X7kA9Ir_heF14xeEmSUFIIzqFlUX6rmOTxtBrp46TKdJYOQmSmioT3LR4Jv1K5ogx0xJTAzjGoJJC6Wdi0oP-uBbFd3_alH_RIMYWhCGIzDTS0OHy07DF4TAaC-dtwQ626mJnNWMR8lgqbc64qdupxlaTNxwjKlzLhwTgyaEkam2cy4WkMRtlvIA70ftPlc4Qo3Jo7IGaujDi-iC6kKYgAbzHu1alvwqP6H5n1mhQv9P4rXRy3g7FuqwofcFvI8f38JYXfQ5awAC7lN35Cpd7oGmxspRz4_OuOIEQZw7TE8u0SZcH3I8-qMA47yx3RKUcJNDepnZeQbDbw0ppbJ2VxPj-ix0mfaacX24KLD0Gt_byZaxnR_Q-w34O57fzNFd_P2AuFWegNrhoYwfMD_XQt59GyXWUFkOtZSsb4SiIGnnqOy67XDz5IOYokNF22IcguQET2sRdgo3-NALBRYAPj8V0MZ1V9jnHuodrowjOtDAMflegq2aRxjrbFTgeGLe0Fdl0auSyLO0t-2T5PQgFVgloWCF85Flq_pq5LwRQTqtVs9gzbxYYMUYsro1jqam81ZJ6kO79TPWERhj1i3k7QQOIb_ImUO_VV6Ti-yRyb83mJfnlalkKFes0rnFulXTxXmnBeXoPnvU-Lbi98offFLUkB6eLH9bZqh3IeRMHUBladZOjwdI7ZFRmakMKkui-ljll_mOhm7WTMg_TtUx-N7YONjuIq2EljeHjmR4UvRt6XnVyemYz-JpRq3uqcZ3UoxLFVDtZU3wLVHy0ArI5p7e-zTYuceewMT2L7gzqTnczSWMR7Z6bu7bfVkVo9dVahAzRZyLormOSb-7R1Dq_MD0BtUgg43h26-hyCHK6vx8pFAtWs4kWowJqznE2OcJBVEJNN1UhcU02V37igoPpIvu8gn6eFZwSjdLBSCTmlKP5RpO-oGGzNrNrwHzwg6N3IRbKAn7d-iK9ddiyPhZd66db336fDHTt_wlHXF84K9xcY2yn_vyEWzibv1J9ucr_zwVfBS07Ecg445Vn8VbxRjVHztN2tXThNAggDWep3aR5r9652z5W67yogl4n6oCV4WghWUq1kNYtxYBK6UZhJQO5CoLAB2WEJqge9_AbhF6_xIMAKIJI4XnW4n0h-ZakOaEDENG-OX8w_Bh935vfDd5Fvw0Dwih-Oe3ydL138kl-7db_AwihMMjU1wYc8TvDdzSq1IqgXyg_Vdv3GwOUQeKYD6HNDA3tGXuE-bAKA9yXYsKI9KTVQGrSjpWugyEXCGtVQfce4mRlKbxPoCbYDQKY2tLawRlIUVU8x938f98JE_FsOQzUFdQ91JvLTBWdfKa2ZgFPzV-n60pxy4YyHv0SpczJ-AeSUzhjp0tkVbWr7LbOHJPfTME_raFG23yfZvVm1jUVV1w8jjYE3hOxUUEo4xm3MVbww9G1Xwdi4pjWK4KJjLFtPjs0kZhy0KRAd_SvcMfowld2i4A03cZiPaR4E7u2p7VaIwAYuuRK6r7pwYIgtR__INzaxxJRZdnoIHXzqBrlGEgFyB8kIiNxnw_Rl2oHF01ahrtHgpoUt-p7lkiRQ2jckeicVveTtCa-F9E2vLHAYkEAWrwJON2gX6hnkXkBkKlNKOjGwG25gx8p-UNfyzbuMT7adCAAavesNuKmTKuecSs4f963SD3T5Yn0oc_LEsyUlX_tqs3h8ISiMDTcXhZwgK7XO7tTiog5MigjQi6lhoH6UitN4ntwXegbJsWoOqkBBb2J2u5HhNBCfMNcgcaCcgzXV3qWuUKMs_FBzWoygtuEK-j5BOFl2kxHLojYk5KPZ1pyUwGQwRwvk4xLxMFd-NnE67g1iTAcsPkmGJ3sWl_zTYDfqDX_v-DWJNE-ajqgXw-JwF5zZFhXCgb4SWLt-9M635hJ02_E64vcRU4uCZus9dqJEeantEvHjiWUzWcgZu_rH_T997XzoinLlEXCb1TnIiFm9BJn-JVjlDW5Jd4qyAihFOlPqJXy3_PX2pjoGSfX9SrKCz6khKBC8uWbzYEit8fLgAZqGT7ZC7wy71JfPgIkBN5v7BBMtyplmoFW-40ArV6f1qH_gEdFgm4tHIlXjXUc3VDBLCLQ5e6j3m8X5keFAaokC9L1ypmPFNnzO1pOIIS9lpdKH2f9QIcUE2cR-w9doCAxPYEOKPYwnLS5s6SYDVtzAuUvW-xKjRnbka09_w6uzxNEsrnbyYeoYko-6b4XElOr28-X8ydOcLR0c7SUVJhx6Y76jG7OO10Ee904uggHUdV5unmRoCrGHEmvEe07g5unC6XvH40J7Aev1VHOiZJq-JLXUQwZoxreLMXPm_O8A5_dNA3X_rtKJoKYu3FgIRR-F0YzgOQ9Pi9imp27m-vTraGc-8JXOUgMFMocMnydIehEGftyrSpFB4jYOafJT6HjKasj3OSRwXy5pDEtXxjqS-0mIiRU8yOW06N-uuO3hF1GPoEazom12VH_o4l_MQwi4Wo3oUQtE_ql1ZWObVtuR8Qpc9EZSpnX_C5wzNCj3iofqMxEf9b2odlBc5sG1DLFEdkmX4DZYBqspSsYqYHqQfpsksXybdIqm7U0TeYh59MAj_y7fhPHulQ9PWH7GGX_KXTIghlOpqy8YWY55aLBtg_EO7t-SRgY6jEfs-0Xop2nB0GLvRcZqgIuA2pddshj9LkgroPkripJuDqeIf4uGjw9qmlSG7vMukEvVEuJ-3uQcXUzwnTockh3akUC2J1DdnjmqyXZ3pvVl0pXhGoz_dcrKYW-2vKWc091mOtok88sqqrXlSp0eoh0xBnyuHXLq_fXsf94Vk3VDaVksAhM8rHi95Gxz-o5QHRf25UgRjuJlEMPhnJzNyvu3i7SNaHsNRcjebUCQIVobTd_GkvUGp8cvAukhm_ny66LXV7R5TvSk3TXQGccnomhhEakcA1C3MoB7JQKwA8wraXvLKuy_j7kl4YXH3071SaoLySxwSf3rq0wiVHt8RPkqeVchS1QswS7B-MVtopVjfEIJbmBSs5bnJqQgLFv_J3KoPLpWaYiiKO29eYOpSQvU9J2aJ-zY1OfPILGZ076zgVdDbmodPe5Dvk65BubxeKT_nLJ7oO08LnxhSK_UEGyau-9otcS1IoeX0wieQJ5JC1AqS-zk2wPMI5jo2arRgRU3mO1R_KPJRoJsgKpte0GqWfOBHf9H-EZgH6TvwA7kvjXRP5unu8sAu0XcUVhGuNhpHcnuxrSn_rnCZebBiQZyD6Ve4pVS4_rs5osFPOu5VQqDtH2-ZEuQ56cS-O0Xg4I9Kxo_mg19M1Vh8KL4mM1V9TwxWiJXvfGyhDEgHMxiCFo4yrxjYpuah0Wp-ge9qwSo3YZnqHAVApYh6O2plq3QXth7Ap7eQnsmMIxuwNzkk61lUKC1FDb7Std8wlcXzwXy_BEkxfN83XReTY1cfvh8GlpI4AN3OSrz4BzS4V-GD_vzRCWK-HeBKVV59avbj-HQzNchLALDHfGn5nCJtTYk1Ng-OmSzaJGp2qGPOFHPM4uF16b09dGl_VIVsn5xhZ4s0NVBvZbpcdZgBUyKTBZJeefRTbxi6QTH18Vw2bteDCoTxVkz-8BcEhFYOLoZ0OQZ1kOA7rD-79UsFzqKaOPACRrlASqGh08NBpzq9JEsV2z9Sc-J4ABWHHUJRGC1Io62iYkP_nn4TSU0Cvl8v5jkd9V279JHkX9cvulv3MPgsAeflMC7DR7889gUTCLCaUQ2IaoO9vmjSynIoDLQJiCNd0rQxIXtLKLUMSYR3dlZ9wFE1fEj8xkzhh-fdhFI1-Cfxk4DN2pmS2en4nFtB_7ZlG_YXzOEtrbWAY9swXJ3ss6qbVEuNvFbmVtNgF0Ze8PpL8AUkGqEDgDSy6kZ41a-nOtAHigCYSrar3WeXSfb4KKqAqEhdHAtvDSQ1ZFQUTAgxabJFnutX6N4Hpouqcf-URUCMA65d0B9kf8X7z2rtf0q0Xwcrkn5Iaqg3Ae3T192DF4ooiiOkktmAuM83McNApPAtzlSIMeS8yCnc7xQhu5le8PATwS7Sa5M-HLdjm5K6SnZHpPpI5d4jsMcM5z1bTMrekkbzoUUmEzdvngHhMXvHijSYhrFxI7SUfg1rGatjLoph834aGHbbYH9YhlL3YKhW6FlyZKuzFVAMTceITEXNSduwTbejGD20MHV11bmNlnij9t1fQC15UbTtwGtLIVCPcDPutvY9tipbf0NMacMIt4k44cgMFpz0MqfXXIvLEF480XOMJXTNutU5xsjmDF5OKVRRPAIsGnQHsUwjTW9JCy8AyRHQkQUkXNbQgMNnJK9ZjjEcGqttES7RZtneebDlid-CDsEEvHqhI7mDXnMgT5JVkq9FzvJqFUVy6m3k6bv9kGiy--FemOsxjAdgF3Go_-jnPORvTYZmbXxEKGH_-esLUlQTegt9ZJ7haN-iD6BIiM4lNcV7g2Qbj37ZSz88USu69cvz0MHztML_dl6XP8hyOpNhzLUOo5MfnOmE4YelGG7RRNZ8pNXVXuKqNAfwZR9Hb2h5GfnOoVAR6_HpSqD4pS-xAhKVMtmJRjpoeAgZFVyNDVDjXbrp6ZPXTCto0CVrhP5jaTvDkYLlU2z85p6hjI2FrludTrm7uk2TQQNP8DdVGz-LcQdlxY4paPANokMF6uWkqzRXit_JYbd-nM_lD_PQm5ur7JNFWq5R-5BfF6C2QA9byNm_iKarwkUZct_2dMfkivYfeKCfX_53FN9ktXeRTCXW7KwzLA76g63nyv5HmWUl7u6w_3kEYyyoKI2C4FfXfQ7t7_cpUPead18ElB86tdM4R9DDK8vxzmXsP18p88tZ7gPzWM97VeahBIOsI4JCFjN_IeHSKBy-maMu4JQoTiPNdGzPZgc0nztfzDS4WOKzsAEMBdZNH1-ISumDUSxp3gmAz_6cqQRJ_SSEZ9Yvc9WvDR7cfTUrxW5eIvfMrw2rORSq32XkoiE8CWOd4FUdwFbU0rWDLupHnkl8JqXsOvtfMhLYGe0m-5iIwYCTa54J8r9yW_72inI

可以看到很长一串,那我们看看请求是什么样子的,能不能去发送假请求去获取远程代码

远程代码请求

{"uuid": "03000200040005000006000700080009", "ip": "", "code": "UE4GET4CRP9BWDQY20Z1P73GCBYHKB0PQCTH0QCEXUKSSJCTVVUJQZWTM3RRXW0P506D1MD4XYIGSX86", "requests_times": 1, "num": "6156", "sign": "7e2ec0e60d857648a30ccedfec6d294aa1258f88d73b7ea5e0ae7615988354b3", "version": "1.1.0", "filepath": "./single_control_files/1.1.0/resources/utils/mjpeg_tool.py"}

在请求里面很明显的看到了code注册卡,那这就麻烦了,后台肯定校验了卡对不对,不对就不下发代码。但是我们有XX助手的云代码,先给他填上看看对不对,XX助手云代码解密如下

#! /usr/bin/env python
# -*- coding: utf-8 -*-
"""
Updated on 2023-10-22
"""
import os
import socket
import struct

from lk_logger import lk

class SafeSocket(object):
    """safe and exact recv & send"""
    def __init__(self, sock=None):
        if sock is None:
            self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        else:
            self.sock = sock
        self.buf = b""

    def __enter__(self):
        try:
            return self.sock.__enter__()
        except AttributeError:
            return self.sock

    def __exit__(self, exc_type, exc_val, exc_tb):
        try:
            return self.sock.__exit__(exc_type, exc_val, exc_tb)
        except AttributeError:
            self.sock.close()

    # PEP 3113 -- Removal of Tuple Parameter Unpacking
    # https://www.python.org/dev/peps/pep-3113/
    def connect(self, tuple_hp):
        host, port = tuple_hp
        self.sock.connect((host, port))

    def send(self, msg):
        totalsent = 0
        while totalsent < len(msg):
            sent = self.sock.send(msg[totalsent:])
            if sent == 0:
                raise socket.error("socket connection broken")
            totalsent += sent

    def recv(self, size):
        while len(self.buf) < size:
            trunk = self.sock.recv(min(size-len(self.buf), 4096))
            if trunk == b"":
                raise socket.error("socket connection broken")
            self.buf += trunk
        ret, self.buf = self.buf[:size], self.buf[size:]
        return ret

    def recv_with_timeout(self, size, timeout=2):
        self.sock.settimeout(timeout)
        try:
            ret = self.recv(size)
        except socket.timeout:
            ret = None
        finally:
            self.sock.settimeout(None)
        return ret

    def recv_nonblocking(self, size):
        self.sock.settimeout(0)
        try:
            ret = self.recv(size)
        except(socket.error) as e:
            #10035 no data when nonblocking
            if e.args[0] == 10035: #errno.EWOULDBLOCK: 尼玛errno似乎不一致
                ret = None
            #10053 connection abort by wda
            #10054 connection reset by peer
            elif e.args[0] in [10053, 10054]: #errno.ECONNABORTED:
                raise
            else:
                raise
        return ret

    def close(self):
        if hasattr(self.sock, "_closed") and not self.sock._closed:
            self.sock.shutdown(socket.SHUT_RDWR)
            self.sock.close()
        else:
            self.sock.close()

class SocketBuffer(SafeSocket):
    def __init__(self, sock: socket.socket):
        super(SocketBuffer, self).__init__(sock)

    def _drain(self):
        _data = self.sock.recv(1024)
        if _data is None or _data == b"":
            raise IOError("socket closed")
        self.buf += _data
        return len(_data)

    def read_until(self, delimeter: bytes) -> bytes:
        """ return without delimeter """
        while True:
            index = self.buf.find(delimeter)
            if index != -1:
                _return = self.buf[:index]
                self.buf = self.buf[index + len(delimeter):]
                return _return
            self._drain()

    def read_bytes(self, length: int) -> bytes:
        while length > len(self.buf):
            self._drain()

        _return, self.buf = self.buf[:length], self.buf[length:]
        return _return

    def write(self, data: bytes):
        return self.sock.sendall(data)

class MJpegcap(object):
    """重写airtest的mjpegcap,用已知的端口获取数据"""
    def __init__(self, ip='localhost', port=None):
        self.port = int(port)
        self.ip = ip
        self.sock = None
        self.buf = None
        self._is_running = False
        # self.ori_function = ori_function

    # @ready_method
    # def setup_stream_server(self):
    #     if self.port_forwarding:
    #         self.port, _ = self.instruct_helper.setup_proxy(9100)
    #         lk.log(self.port, _)  # 2023-04-21修改
    #     self.init_sock()
    #     reg_cleanup(self.teardown_stream)

    def init_sock(self):
        try:
            self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            # lk.log(self.port+40000)
            # if self.check_port_in_use(self.port+40000):
            #     lk.log('port_in_use', self.port + 40000)
            #     self.relase_port(self.port+40000)
            # self.sock.bind(('127.0.0.1', self.port+40000))
            # lk.log('self.sock.bind', self.port+40000)
            self.sock.connect((self.ip, self.port))
            # local_ip, self.local_port = self.sock.getsockname()
            # lk.log(f"Local IP: {local_ip}, Local Port: {self.local_port}")
            self.buf = SocketBuffer(self.sock)
            self.buf.write(b"GET / HTTP/1.0\r\nHost: localhost\r\n\r\n")
            self.buf.read_until(b'\r\n\r\n')
            self._is_running = True
            # lk.log("mjpegsock is ready")
        except ConnectionResetError:
            # 断开tidevice或是拔线,会导致这个异常,直接退出即可
            lk.log("mjpegsock connection error")
            raise

    def close_sock(self):
        try:
            # 设置SO_REUSEADDR选项
            self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
            # 设置 SO_LINGER 选项,超时时间为 0 秒
            self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_LINGER, struct.pack('ii', 1, 0))
            self.sock.close()
            # self.relase_port(self.port)
        except ConnectionResetError:
            lk.log("sock close error")
            raise

    @staticmethod  # 未使用类中变量的函数为静态函数,删除静态方法中的self,然后在这个方法的用@staticmethod声明一下。
    def check_port_in_use(port):
        """检测端口是否占用"""
        port = int(port)
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sock.settimeout(0.05)
        return sock.connect_ex(('127.0.0.1', port)) == 0

    def get_frame_from_stream(self):
        if self._is_running is False:
            self.init_sock()
        try:
            while True:
                line = self.buf.read_until(b'\r\n')
                if line.startswith(b"Content-Length"):
                    length = int(line.decode('utf-8').split(": ")[1])
                    break
            while True:
                if self.buf.read_until(b'\r\n') == b'':
                    break
            imdata = self.buf.read_bytes(length)

            return imdata
        except IOError:
            # 如果暂停获取mjpegsock的数据一段时间,可能会导致它断开,这里将self.buf关闭并临时返回黑屏图像
            # 等待下一次需要获取屏幕时,再进行重连
            # LOGGING.debug("mjpegsock is closed")
            lk.log("mjpegsock is closed")
            self._is_running = False
            self.buf.close()
            # return self.get_blank_screen()
            return None

    def relase_port(self, _port):
        """释放指定端口"""
        # 查找指定端口对应的pid
        cmd_find = 'netstat -aon | findstr %s' % _port
        # lk.log(cmd_find)
        # 返回命令执行后的结果
        result = os.popen(cmd_find).read()
        # lk.log(result)
        if str(_port) and 'LISTENING' in result:
            # 获取端口对应的pid进程
            i = result.index('LISTENING')
            # LISTENING 与端口号之间相隔7个空格
            start = i + len('LISTENING') + 7
            end = result.index('\n')
            # 提取端口号
            pid = result[start:end]
            # 关闭被占用端口的pid
            cmd_kill = 'taskkill -f -pid %s' % pid
            lk.log(cmd_kill)
            os.popen(cmd_kill)
        else:
            # lk.log('port %s is available' % _port)
            pass

    # def get_frame(self):
    #     # 获得单张屏幕截图
    #     return self.get_frame_from_stream()

    # def snapshot(self, ensure_orientation=True, *args, **kwargs):
    #     """
    #     Take a screenshot and convert it into a cv2 image object
    #
    #     获取一张屏幕截图,并转化成cv2的图像对象
    #     !!! 注意,该方法拿到的截图可能不是队列中最新的,除非一直在消费队列中的图像,否则可能会是过往图像内容,请谨慎使用
    #
    #     Args:
    #         ensure_orientation: True or False whether to keep the orientation same as display
    #
    #     Returns: numpy.ndarray
    #
    #     """
    #     screen = self.get_frame_from_stream()
    #     try:
    #         screen = aircv.utils.string_2_img(screen)
    #     except Exception:
    #         # may be black/locked screen or other reason, print exc for debugging
    #         traceback.print_exc()
    #         return None
    #
    #     if ensure_orientation:
    #         if self.ori_function:
    #             display_info = self.ori_function()
    #             orientation = next(key for key, value in ROTATION_MODE.items() if value == display_info["orientation"])
    #             screen = aircv.rotate(screen, -orientation, clockwise=False)
    #
    #     return screen

    # def get_blank_screen(self):
    #     """
    #     生成一个黑屏图像,在连接失效时代替屏幕画面返回
    #     Returns:
    #
    #     """
    #     if self.ori_function:
    #         display_info = self.ori_function()
    #         width, height = display_info['width'], display_info['height']
    #         if display_info["orientation"] in [90, 270]:
    #             width, height = height, width
    #     else:
    #         width, height = 1080, 1920
    #     img = numpy.zeros((width, height, 3)).astype('uint8')
    #     img_string = aircv.utils.img_2_string(img)
    #     return img_string

    # def teardown_stream(self):
    #     if self.port_forwarding:
    #         self.instruct_helper.remove_proxy(self.port)
    #     if self.buf:
    #         self.buf.close()
    #     self.port = None

if __name__ == "__main__":
    pass
    # import wda
    # from airtest.core.ios.instruct_cmd import InstructHelper
    # addr = "http://localhost:8100"
    # driver = wda.Client(addr)
    # info = driver.info
    # instruct_helper = InstructHelper(info['uuid'])
    # mjpeg_server = MJpegcap(instruct_helper)
    # print(len(mjpeg_server.get_frame()))

咱们吧代码重新加密回去,在HttpDbgPro上伪造响应

可以看到投屏出来了,控制也没有问题。难道这个作者两个软件是同一个云端代码吗,我是报怀疑态度的。但是我没法去拉XX中控的云端代码,这需要一个真卡去抓请求

注:因为我没多的手机,无法测试多个手机的情况。经供参考

4、补充

你以为到这里就结束了?,实则不然。这个免费版的代码和收费版的云端代码通用在我这里一直是一个心事,难道除了买真卡就没办法去获取这个收费版的代码了吗。

回收伏笔

记得我们上面说的完整性校验吗,校验不通过会通过http://47.119.173.20:18003/download_file下载,既然他要下载文件,那我们伪造一个去下载云端代码的,触发下载很简单,去resources目录下删掉setting.pyd就可以触发下载了。http://47.119.173.20:18003/download_file请求是明文的,不需要解析。解析下XX中控的远程代码请求,如下

远程代码请求

{"code": "123", "uuid": "03000200040005000006000700080009", "ip": "", "requests_times": 1, "num": "5193", "sign": "bb82a5f577dbb55241699b6756436a5683fd4776930a10a642590c3b6192880a", "version": "1.3.0", "filepath": "./cluster_control_files/1.3.0/resources/utils/mjpeg_tool.py"}

下载文件请求

{"file_paths": {"resources/cc.pyd": "7d3e2366464a411a1d470ac59813c61573d5e387cc7b5916d28d7e6f935a3847", "resources/dev_opn.pyd": "b012c3e7c1acb26e41d1ea0d02bc49dd1180a56a8257dc38ce813949c3463482", "resources/setting.pyd": "4387baea0cd32fd1c77db8bb123f0480094769533df13204daa4e9220dd265f7", "resources/img_rc.py": "2d96fde0fdbcc6d557f5d1c780cd0ffc721f8822c7fff63efd57d93015429c1f", "resources/lib/centercontrol.pyd": "83a2f532074c05184ac91faf24bdfd7bee129269f103db21b9b27b670c838123", "resources/lib/DialogBox.pyd": "", "resources/lib/disclaimers.pyd": "8475c9f12549ca501a15464d20dfd22fa080bc871d05f27dba8a8c40a7e6c3e1", "resources/lib/helper.pyd": "d41b3b080156988fb84ac4aa0c990f468c716ce4284578d9bcad0d240e6a037c", "resources/lib/InputDialog.pyd": "d489a67649ca2d8055d51554fe0af2f27e618a0bba2d59a8d3f0a67e92e3570d", "resources/lib/localnetwork.pyd": "d8df10b1ca2fdc419e5e7d16e9a88a6ea90e8f6485a6431efb23d24ff7ae0258", "resources/lib/MessageBox.pyd": "9e2a3dc52a7689afd481d717606632d74252f134a3e93d44bd8efdbd653768c0", "resources/lib/phone.pyd": "9f9660875348df20c44adaad74b38c353ec3308db3b750b4074f5699e790a633", "resources/lib/settingui.pyd": "09fd8a6c1be334480362273528f4feec93a54632b6cbb4bd91ddc7815b404838", "resources/lib/SignatureTool.pyd": "7b6765ba7e3c5e64c0873c6bdc45619500803549f9d583bf95736a7deae9f2b4", "resources/lib/sreg.pyd": "cfed634a3ee25eb37613f9a475d9bd7a6467a571975581bce60790ad9cb4a8a1", "resources/utils/comm_tools.pyd": "f1ca3be73cedba5b651db036a1e92998368ee78a6c29c4707b73ca9c4c213abc", "resources/utils/ios_api.pyd": "0716ddd14e70edd2252dc12c5ca09426b9b4360d279bb34c01336973da4d1e0e", "resources/utils/tid.pyd": "de35c28d4c17eec071b8ee4f964ceeec2d9cdd7fc492b305f7af163f484b7e88"}, "num": "4431", "sign": "5ba24cff51e82560d5d08a8cf8f7d471d40abfcb0eb272430b3d56fd29918be5", "folder": "cluster_control_files", "version": "1.3.0", "file_name": "DialogBox.pyd", "file_path": "./cluster_control_files/1.3.0/resources/lib/DialogBox.pyd", "save_path": "resources/lib/DialogBox.pyd"}

我们稍加修改,把目录和文件名字改成我们要的远程代码mjpeg_tool.py然后随便用个工具进行GET请求。诶,作者最谨慎的点害了他呀

云端代码被我们下载下来了,至此,分析全部结束。

但是实际上这个玩意的代码和免费版的是一样的,除了日期

5、分析代码

解密相关代码

from pprint import pprint

from cryptography.fernet import Fernet
# http://47.119.173.20:18003/multiple_files_value_verify   校验文件完整性的,作者简直天才,有问题直接重新下载
key1 = 'ayWCv9tLU1lmxDCNjIghboVL6SLSnetRiXRjZcu9Ziw='
#   http://47.119.173.20:18003/request_cluster_control_code  带了些信息上传了,不知道干嘛的 后面发现是拉云端代码的
key2 = '-q-CsAkWo1iPo2U35MGS5CYiJELMg3_VAf8PmMbROec='
#   http://47.119.173.20:18003/request_software_information   获取信息的
key3 = 'Ff8vE_M8E7u9FG2EFxOqICc0rMmNzN8ywChbTXWGovM='
# http://47.119.173.20:18001/software_register || software_verify 他两用的是同一个秘钥,一个注册一个心跳
key4 = 'iHy7diw8_43oseqGJjKWupDGlEIzRh7ToHVpDD2YKUE='
# http://47.119.173.20:18011/software_register 不是,先暂定 这是单个的注册加密算法MC助手的
key5 = 'arUYowV22DeqFmhOjja5OxcjyywFjBdUs5pSsaeGJyM='
# http://47.119.173.20:18003/request_single_control_code  # 云端代码
key6 = '1GhQMT8aMYcAlEn-ugrKMAuzY3cnyyJ1Q6xskL-cMXs='

cipher_suite = Fernet(key2)

# 需要解密的数据
token = b''
token2 = b'gAAAAABmRv4Zc6BkqzkSHYYCOeOq0BA1O7Nm0txwPv1-ZZ2JhjPeVMgAm_0n-mhQso0xCOQMlZ0GnncTENo3fOwzj9yqfgONmjdthfkzYqh5WaCUbnsQfyr_oH-xxtA1F7MmIcXdK-5SjcBewB1OU9_wY3ABBjOAO1rmZ_FXdFrwljVu1OgJi2w40JG9o2rOF1HFxTPJ-40xlU_s2C1s0C9bE18CxOD4Qb0LFJMtdbppqKFKm5yGeOlVKtj8yyDLcB1k5tXKc7BwAU3Fs7U1gqYWWj_2-coL5RDzlZWb1cCkXVKJNfM1q05YV0twoR9acOWIMofFWUo1uMJjVwpJ7nmorVrKcLN7_qZQAi9v5PgQAmdae_BzU3eGU5pPFOsr3M-6umioM5jm3WA0kWLY2eyvEY8RrNmeTwQELizVgApRQagBx4N3qc97rcriHm2j3jGIgfjepMZ8'

try:
    # 解密过程
    # decrypted_text = cipher_suite.decrypt(token)
    # print(decrypted_text.decode())
    decrypted_text2 = cipher_suite.decrypt(token2)
    print(decrypted_text2.decode())
except Exception as e:
    print("An error occurred during decryption:", str(e))

伪造响应相关代码

import os
import subprocess
import requests
import scapy.all as scapy
import sys
import traceback
# http://47.119.173.20:18003/request_cluster_control_code
TARGET_IP = "47.119.173.20"
REGISTER_PORT = 18001
VERIFY_PORT = 18001
CONTROL_PORT = 18003
MODIFIED_RESPONSE_CONTENT = b"gAAAAABmRa5phtw-CaWQuOI6dYxQaL4eXE76iPTs42eMMFYQdAkKAto6ST5elyih8j7uNtosHCOPc3z2yO-JFhK6TljGWxMu6D6A0pjnvGGHOQ7j3fesgDs="
WANZHENG_CONTENT = b"gAAAAABmQsG_M6toB78h-3QtI-hrNdgMSrysnKGr4y4hvPOSS31BpRetMQospPONk8YJpG2wrKAczoOB0pZPIvVgqE9hdRmhJkyvbGTWwxoPYgcrvduu3EgiK-6lBZ22nV4N9JQEWLn7np7XJ1RP8Epb0Ibx9S8azS5gkmRznn0J_OGI2tI2OKbmEFgHT0BCduAd1i_vDtbkf2xxVHBQYmDvY9bEoI30GlAzTVkzbMTgOp3qpBPP5RrWeEklY_QNZabfc1pAI7gvdjE7KyJu9NR-bvF3IGQ5DIvb3PvRPtjpodjFc0xMxmOZJ1gdjSmzed7EOh7IUYxKt5uv0xFt9apFR3ERWfc4Y31boeMgjWzcjRQxbKBLLo4oi7nhZPEdtVFbbCkAumo94nRnUmsiGW0bmSzEthFUT29tgPLqVc88KsLPEhfKxLwQSdGhuVlteiXLUFJx4sJ_6muJRlEyzRVztf9vhXO7GS_Ptqb4f8vUyg63wBlgsvxU-PZ7ebNJb_IUaH_a65cLqF94Z1hHM5Y0dSn6uEIUMgmdjNhZHlr-inqsJq8eKAc7hapenBmSrermBMcBEVR89MgI6SlFkgFHF_Glv7IIIvbEFRl0fYxD-q1q1ZiUfJe_ruxa2bmOqLKS3NhPuLPi4yNuHUl4MUgIScwkInTqVtNC_eI1U3PV2S6xupifhrz1tkfz3FRfHj46eaGf-iPqoEBD6nk3YRl6nl6sFisslCWzNVsmRMTocLFqlIFH2ChPIj1K4tbq5e0WZpIHgZ3EHD6spCsWFueFVmDG1qKzL3Yv-GoP-53K4d9xIiVRhmdk_K2lDSnhR6zvWAKBUratbFEfZyZpj_MDlarxX5HudZs1Kg7tZd5cEVrK_IMKfnvBFe7H_Q_3KzbkgJhQnEmnYgeRgZggh-Yp1cCcseEIIw=="
XINXI_CONTENT = b'gAAAAABmQepEbWe8a-WrXuftttxLhXB6_XJCMoz80UzJfNU5WvGKbHhyUJJ_dr54tAmOjm8YWcJuQNg9XSQDLvZBcBQFcu6t_dtE1LuZLOMoUw5I61TbVwJANHZ14lL8pA822TPX60k-Yc6ZGUExKP49AERMFAP7iuvYhLspAX8oN6Wc_y4hRv4ii4aLcS3yI7SeTonWsG4kFvRsQm--VCoM-AFVgTpIC3ygyen7bGM-oHKMf-3U2gLzgJIgxV-NbJupzJXhNBJQcxHorJg4bLCFMdaMNsTovL4R0PgImIItwHXxJbfKaow='

NPCAP_URL = "https://nmap.org/npcap/dist/npcap-1.55.exe"  # Npcap 的下载链接
NPCAP_INSTALLER = "npcap-1.55.exe"

def is_npcap_installed():
    try:
        scapy.conf.L3socket
        return True
    except AttributeError:
        return False

def download_npcap(url, filename):
    response = requests.get(url, stream=True)
    total_size = int(response.headers.get('content-length', 0))
    block_size = 1024
    with open(filename, 'wb') as file:
        for data in response.iter_content(block_size):
            file.write(data)
    print(f"{filename} downloaded successfully.")

def install_npcap(installer_path):
    try:
        subprocess.run([installer_path, '/S', '/winpcap_mode'], check=True)
        print("Npcap installed successfully.")
    except subprocess.CalledProcessError as e:
        print(f"Failed to install Npcap: {e}")
        sys.exit(1)

def create_modified_response(content):
    response_payload = (
                           f"HTTP/1.1 200 OK\r\n"
                           f"Content-Length: {len(content)}\r\n"
                           f"Content-Type: text/plain\r\n"
                           f"Connection: close\r\n\r\n"
                       ).encode() + content
    return response_payload

def create_header_only_response():
    content = b'这里是云端代码加密后的字符串,太长了我懒得写,排版也不好看,自己填一下'
    response_payload = (
        f"HTTP/1.1 200 OK\r\n"
        f"Content-Length: {len(content)}\r\n"
        f"Content-Type: text/plain\r\n"
        f"Connection: close\r\n\r\n"
        ).encode() + content
    return response_payload

def packet_callback(packet):
    if packet.haslayer(scapy.IP) and packet.haslayer(scapy.TCP) and packet.haslayer(scapy.Raw):
        payload = packet[scapy.Raw].load
        if packet[scapy.IP].dst == TARGET_IP and packet[scapy.TCP].dport == REGISTER_PORT:
            if b'POST /software_register' in payload:
                print("[Scapy] 注册请求")
                response_payload = create_modified_response(MODIFIED_RESPONSE_CONTENT)
            elif b'POST /software_verify' in payload:
                print("[Scapy] 心跳请求")
                response_payload = create_modified_response(MODIFIED_RESPONSE_CONTENT)
            else:
                return

        elif packet[scapy.IP].dst == TARGET_IP and packet[scapy.TCP].dport == CONTROL_PORT:
            if b'GET /request_cluster_control_code' in payload:
                print("[Scapy] 远程代码请求")
                response_payload = create_header_only_response()
            elif b'GET /multiple_files_value_verify' in payload:
                return
                print("[Scapy] 完整性校验请求")
                response_payload = create_modified_response(WANZHENG_CONTENT)
            elif b'POST /request_software_information' in payload:  # http://47.119.173.20:18003/request_software_information
                return
                print("[Scapy] 软件信息请求")
                response_payload = create_modified_response(XINXI_CONTENT)
            else:
                # print("[Scapy] payload",payload)
                return

        else:
            return

        # 创建新的数据包,包含伪造的响应内容
        modified_packet = (
                scapy.IP(dst=packet[scapy.IP].src, src=packet[scapy.IP].dst) /
                scapy.TCP(dport=packet[scapy.TCP].sport, sport=packet[scapy.TCP].dport, seq=packet[scapy.TCP].ack,
                          ack=packet[scapy.TCP].seq + len(packet[scapy.Raw]), flags='PA') /
                scapy.Raw(load=response_payload)
        )

        # 发送伪造的响应
        scapy.send(modified_packet)
        print("[Scapy] Sent modified response")

免费评分

参与人数 16吾爱币 +14 热心值 +16 收起 理由
iteamo + 2 + 1 热心回复!
romobin + 1 + 1 我很赞同!
heartblade + 1 + 1 用心讨论,共获提升!
Chenanin + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
ly871108 + 1 + 1 我很赞同!
lulu52pj + 1 我很赞同!
gmc333 + 1 + 1 谢谢@Thanks!
不能吃的李子 + 1 用心讨论,共获提升!
unxeer + 1 + 1 我很赞同!
yangand + 2 + 1 谢谢@Thanks!
五行阿尔法 + 1 + 1 刚破解软件没思路 看到楼主文章 一下子找到灵感了
Issacclark1 + 1 谢谢@Thanks!
janken + 1 + 1 热心回复!
小k666 + 1 + 1 用心讨论,共获提升!
15126819695 + 1 + 1 完美啊 这个分析方法 nb
cooltnt + 1 谢谢@Thanks!

查看全部评分

本帖被以下淘专辑推荐:

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

推荐
yanggod 发表于 2024-7-3 20:16
gpt 还能识别什么加密?以后直接扔一串乱码问问他 是什么加密
推荐
spd97 发表于 2024-7-3 14:55
沙发
蜜雪精灵 发表于 2024-7-3 14:30
4#
cooltnt 发表于 2024-7-3 15:10
太猛了,这都能解
5#
wu1280 发表于 2024-7-3 15:22
真厉害又学到了一招哈哈哈哈
6#
景轩 发表于 2024-7-3 16:08
学习到了,好巧妙!
7#
Sandwiches 发表于 2024-7-3 16:42
虽然看的不太懂,但是支持原创!!!!
8#
小k666 发表于 2024-7-3 17:22
怎么反编译python打包的程序没说
9#
xnkj119 发表于 2024-7-3 17:32
这是什么软件
您需要登录后才可以回帖 登录 | 注册[Register]

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

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

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

GMT+8, 2024-7-8 12:10

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

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