如果不会写代码,那就出书、写博客、做视频、录播客。
📚 S35赛季末王者
昭君罗
关键代码定位
- 使用方法【逆向-快速定位关键代码】通过hook常用函数HashMap方法
动态分析
- 下面是我们通过访问目标页面时 Frida hook 捕获
HashMap
的调用堆栈:
a: username b: AI爱答题
java.lang.Throwableat java.util.HashMap.put(Native Method)at org.json.JSONObject.put(JSONObject.java:267)at org.json.JSONTokener.readObject(JSONTokener.java:384)at org.json.JSONTokener.nextValue(JSONTokener.java:100)at com.qp333.car.api.DecodeInterceptor.intercept(DecodeInterceptor.java:86) ## 解码拦截器at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109)at com.qp333.car.api.ParamsInterceptor.intercept(ParamsInterceptor.java:60)at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109)...
分析该堆栈后,我们定位到了com.qp333.car.api.DecodeInterceptor.intercept
目标代码如下:
其中通过hookaesDecryptString
方法得知,通过此方法可以将http响应中的密文转化成明文
关键代码分析
这段代码展示了如何在 Java 中使用一些工具和方法来解密 JSON 数据,并将解密后的数据放回到 JSON 对象中。让我们逐步解析这段代码:
jSONObject.put("data", new JSONTokener(aesDecryptString(jSONObject.getString("data"),StringUtils.MD5(String.format(Locale.getDefault(), "%s%s", UserManager.get().sessionid, str)),String.format(Locale.getDefault(), "%s%s%s%s", str + "000", Character.valueOf(str.charAt(1)), Character.valueOf(str.charAt(3)), Character.valueOf(str.charAt(7))))
).nextValue());
获取原始加密数据:
jSONObject.getString("data")
这是从 jSONObject 中获取名为 data 的字段的值,该值是一个加密的字符串。
生成解密密钥:
StringUtils.MD5(String.format(Locale.getDefault(), "%s%s", UserManager.get().sessionid, str)
)
这里生成了一个解密密钥。具体步骤是:
使用String.format
方法将 UserManager.get().sessionid
和 str
拼接成一个字符串,Locale.getDefault()
确保格式化时使用默认的区域设置。
将拼接后的字符串传递给 StringUtils.MD5
方法,计算其 MD5 哈希值。这个哈希值将作为解密的密钥。
生成初始向量(IV):
String.format(Locale.getDefault(), "%s%s%s%s", str + "000", Character.valueOf(str.charAt(1)), Character.valueOf(str.charAt(3)), Character.valueOf(str.charAt(7))
)
这里生成了解密过程中的初始向量(IV),具体步骤是:
使用 String.format
方法将 str
的第一个字符加上 “000”、str 的第 1、3、7 个字符拼接成一个字符串。
Locale.getDefault()
确保格式化时使用默认的区域设置。
解密数据:
aesDecryptString(jSONObject.getString("data"), StringUtils.MD5(String.format(Locale.getDefault(), "%s%s", UserManager.get().sessionid, str)), String.format(Locale.getDefault(), "%s%s%s%s", str + "000", Character.valueOf(str.charAt(1)), Character.valueOf(str.charAt(3)), Character.valueOf(str.charAt(7)))
)
调用aesDecryptString
方法,使用从 jSONObject
获取的加密数据、生成的密钥和 IV
进行解密。aesDecryptString
方法返回解密后的字符串。
解析解密后的 JSON 数据:
new JSONTokener(aesDecryptString(...)).nextValue()
将解密后的字符串传递给 JSONTokener
,并调用nextValue
方法解析解密后的 JSON 数据。
将解密后的数据放回 JSON 对象中:
jSONObject.put("data", new JSONTokener(...).nextValue())
将解析后的 JSON 数据放回 jSONObject 中 data 字段。
整体流程
这段代码的目的是:
- 从 JSON 对象中获取加密的 data 字段。
- 生成解密密钥和初始向量(IV)。
- 使用这些密钥和 IV 通过 aesDecryptString 方法解密数据。
- 将解密后的 JSON 数据解析并放回到原来的 jSONObject 中。
代码重构
import base64
import hashlibfrom Crypto.Cipher import AESdef md5_string(s):return hashlib.md5(s.encode('utf-8')).hexdigest()def pad(data):block_size = AES.block_sizepad_len = block_size - (len(data) % block_size)return data + chr(pad_len) * pad_lendef unpad(data):pad_len = ord(data[-1])return data[:-pad_len]def aes_decrypt(data, key, iv):cipher = AES.new(key.encode('utf-8'), AES.MODE_CBC, iv.encode('utf-8'))decrypted = cipher.decrypt(base64.b64decode(data))return decrypted.decode('utf-8', 'ignore')def parse_result(data, session_id, time_str):# 生成动态MD5密钥md5_key = md5_string(f"{session_id}{time_str}")# 生成动态IViv_dynamic = f"{time_str}000{time_str[1]}{time_str[3]}{time_str[7]}"print(f"MD5 Key: {md5_key}")print(f"Dynamic IV: {iv_dynamic}")try:decrypted_data = aes_decrypt(data, md5_key, iv_dynamic)print(f"Decrypted data (raw): {decrypted_data}")return decrypted_dataexcept Exception as e:print(f"Error during decryption: {e}")return Nonedef main():session_id = '请求参数中获取'encrypted_message = "加密数据"timestamp = "1718554525" # URL请求发起时间戳# 解密decrypted_data = parse_result(encrypted_message, session_id, timestamp)print("Decrypted Data:", decrypted_data)if __name__ == "__main__":main()