如果不会写代码,那就出书、写博客、做视频、录播客。
📚 S35赛季末王者
昭君罗
关键代码定位
动态分析
- 下面是我们通过访问目标页面时 Frida hook 捕获
HashMap
的调用堆栈:
a: username b: AI爱答题
java.lang.Throwable
at 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 hashlib
from Crypto.Cipher import AES
def md5_string(s):
return hashlib.md5(s.encode('utf-8')).hexdigest()
def pad(data):
block_size = AES.block_size
pad_len = block_size - (len(data) % block_size)
return data + chr(pad_len) * pad_len
def 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}")
# 生成动态IV
iv_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_data
except Exception as e:
print(f"Error during decryption: {e}")
return None
def 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()