软件下载官网:aHR0cHM6Ly9tc21oLnNpdGUv |
一. 起因
很久之前为了看擅长捉弄的高木同学,问别人要到了这个漫画软件,但是有广告...
那么就想办法下载下来吧(以后还能看)
二. 抓包
先抓个搜索功能开开胃。
靠,有加密数据。好好好,你这么玩是吧。那我就要掏出我的算法助手
了。
启动算法助手并尝试获取加密方式和秘钥
没获取到什么有用的东西
换SimpleHook
再试试
出乎意料的是SimpleHook结果中也没有找到有用的数据,但是比算法助手多了几个DES的结果
好好好,这样玩的话,我可就只能尝试逆向代码了。
三. 脱壳
- 打开MT管理器提取安装包,发现被360加固了捏。还好我有珍惜大佬的FunDex。
- LSPosed启用模块,选择作用域;FunDex选择软件,一套组合拳打出来,然后打开软件,把每个Tab都点一下确保dex脱的干净一点。
- 再打开MT管理器,找到该软件的data目录,就能看到脱出来的dex啦。然后全选、移动、压缩、发送到电脑一气呵成。
-
四. 分析
-
直接搜索关键字
- 用jadx直接打开压缩包,搜索
safetyData
- 这类名,这函数名,太符合我的想象了。
我们回味一下抓到的数据包的json格式。
然后我们立马就看到了重点的几行 -
ResponseSafetyInfo cast = (cls2 != null ? cls2 : ResponseSafetyInfo.class).cast(b8); String type2 = cast.getType(); String[] token = cast.getToken(); INetSafety encryptor = NetSafetyUtils.getEncryptor(type2); if (encryptor != null && token != null && token.length != 0) { Long valueOf = Long.valueOf(System.currentTimeMillis()); c8 = encryptor.decrypt(c8, token); System.currentTimeMillis(); valueOf.longValue(); }
-
首先是转为ResponseSafetyInfo类,这不是对应safety字段嘛。
所以变量type2就是14339
,token就是YD6YTGLPA
。
再看NetSafetyUtils.getEncryptor(type2),追进去 -
这个encryptoMap肯定有用,看一下啥时定义的
-
可以看到是在static里面定义的,在根据变量
f7372e2 = '14339'
和刚才的type2 = 14339
,找到对应的NetSafetyUtils.a()
,再追 -
再追
-
终于看到希望了,再追进去
-
豁然开朗,加密方法非常明了,非常简单。
此时我们可以看到左边还有很多DES的类,肯定是有用的,我们稍后再看。
先看一下 -
new DESHelper(new StringBuilder(strArr[0].substring(1)).reverse().toString()).decrypt(str);
- 很显然,秘钥是处理之后的字符串,翻译成python就是[-1:0:-1]。
-
五. 实现
- 从网上找一个DES加解密的类
-
from Crypto.Cipher import DES from binascii import b2a_base64, a2b_base64 class PrpCrypt(object): def __init__(self, key): self.key = key.encode('utf-8') self.mode = DES.MODE_ECB def changeKey(self, key): self.key = key.encode('utf-8') def encrypt(self, text): text = text.encode('utf-8') cryptor = DES.new(self.key, self.mode) length = 16 count = len(text) if count < length: add = (length - count) text = text + ('\07' * add).encode('utf-8') print(text) elif count > length: add = (length - (count % length)) text = text + ('\07' * add).encode('utf-8') print(text) self.ciphertext = cryptor.encrypt(text) return b2a_base64(self.ciphertext) def decrypt(self, text): cryptor = DES.new(self.key, self.mode) plain_text = cryptor.decrypt(a2b_base64(text)) return plain_text.decode("utf-8").rstrip("\01").rstrip("\03")
-
1.搜索功能
- 发个请求测试一下
-
import requests headers = { 'Cache-Control': 'public,max-age=60', 'Content-Type': 'application/json; charset=UTF-8', 'Host': '43.248.116.78:20256', 'Connection': 'Keep-Alive', 'User-Agent': 'okhttp/4.10.0', } data = { 'key': '擅长捉弄的高木同学', 'appChannel': 'normal', 'appKey': 'com.aster.zhbj', 'appVersion': 'v1.10.5', 'clientTime': 1696328622216, 'deviceBrand': 'Xiaomi', 'deviceType': '22041211AC', 'ipAddr': '192.168.0.221', 'netType': 'WIFI', 'platform': 0, 'sign': '', 'systemVersion': '12', 'userId': '1526xxxxxxxc8f70abce2', 'uuid': 'df82xxxxxxx65d407e64ddf', 'versionCode': 48 } response = requests.post( 'http://43.248.116.78:20256/api/novel/search/associate', headers=headers, json=data).json() # 秘钥处理方法14239,14339 token = response['safety']["token"][0][-1:0:-1] cry = PrpCrypt(token) # 初始化密钥 decode = cry.decrypt(response["safetyData"]) decode = json.loads(decode) print(decode)
看看输出结果(部分)
-
[ { "key": "<word>擅长</word><word>捉弄</word><word>的</word><word>高木</word><word>同学</word>", "relationType": 1, "relationId": "af0580069e7031e3f03f27fb4ff26cdb", "extra": "{"authorName":"山本崇一朗"}" }, { "key": "<word>擅长</word><word>捉弄</word><word>的</word>(<word>原</word>)<word>高木</word><word>同学</word>", "relationType": 1, "relationId": "9d8b4dad7954734a471cb21452e917e0", "extra": "{"authorName":"山本崇一朗"}" } ]
很好,解密成功了,这是一个好的开始