好友
阅读权限10
听众
最后登录1970-1-1
|
首先拿到应用的格式为xapk,这个格式其实就是把包的其他资源分开打包了。
后缀改为zip解压后得到几个文件如下图:
大致看出来有com的这个就是核心代码apk文件了,咱们主要分析这个包体,看看怎么个事
首先通过一个root过的真机进行调试,首先把xapk丢入手机中进行安装,点击运行后出现的界面顺序如下两张图
先是出现了登录界面,然后便跳出另外一个界面了。
我先打开Manifest.xml文件分析一下主Activity的调用为
然后adb进入手机的启动frda的服务短程序
使用objection调用其内存中存在的Activity,这里我直接调用SplashScreenActivity,成功跳到手机号输入界面。
启动Reqable,进行抓包,输入手机号码,点击GET OTP按钮后出现登录请求的包体数据,全部进行加密了。
NP8AqqGZMadYeIN5AcedoWFyiYW9Rln4dHDFFaNLy8umH6qSwAOppb7O/SmycgtWOL+A3ndWEYtOGbnlYpnx+9/Kr8sWtrS+wk/PFgBXmfRb6Es6LpCYdycxhoX2+tRvloAhJ8FXCo+FDC6ZO+QJwM1Hcbcc6eLzfbZ6Pp+0dID7p90nJLFClcfCUk5T8q8jCtBR3l4BxCfUAZcgvrAlp487ITaGyxhMp6aogUrm4AcwFzdPhgG2XpMrn60xWKx/vBuq7wYmJH7h6VKcrjjS4zNQaBUbaODFBJMyXeWfSw3Ctf8D9IfXOtzPr+NJmGGfD+grlwFVfm2Cbd3OqkYD9xu0udjf3gjw9Nw/S85aEcj0lN5VaepJ2hRoSCPK5589yxB+K8CEwoiNDWZJMg4xwca7We9mffeeHWC39IXyvtTqEX7WHOY+aN9zQU6cu2SjUuk8fLuYWCyrY0IdIxc/2pKFyh2pxr5gQSW2teobrgmLUNBBLgRXwN2a6Oz+SYAF+a4n/+5Rr/A=
然后输入验证码后出现真实的请求包体数据,同样也进行加密了。
SbCgfyIaaDwVpdFfDj0noFoSauFfqOkBfA6X1nb+zwj5EPwWmfvVApVZkXTjurcMHShSo+gKTwOuAq34AFzHn2SR2IZU3kJ2qbX8NVWNFeykBNDwD0lu0R/uiv4n8rR7DLg0gGIfMgtMxhZbnk9tTrc1VJqAL83kzJHRK63OadSNKM8IWE2QrOyhmyUt2F0onKwJpcY1/C5CLatJnggBZADmTRUmsKSZo/4WHlwUQaxMNF4XVb4lfKYtCjn3XWQEEFO3WCQeANg=
我收到了验证码登录后的响应并没有进行加密:
arg[1]: [response={"errorCode":-304,"success":null,"errorMsg":"OTP is incorrect or expired; try again.","httpStatusCode":200}]
OTP is incorrect or expired; try again. 这个提示就是验证码错误的响应。
继续打开开源脚本r0trace.py进行hook此应用 输入完整的验证码后,然后查看堆栈信息,发现如下重点信息:
这个方法应该就是登录后发送的请求加密函数了
打开jadx找到此函数
[Java] 纯文本查看 复制代码 public static String encryptToString(String str) {
try {
return getCompletePayload(str, readPublicKeyFromFile(KEY_FILE));
} catch (IOException unused) {
return null;
}
}
[Java] 纯文本查看 复制代码 private static String getCompletePayload(String str, PublicKey publicKey) {
try {
String substring = UUID.randomUUID().toString().substring(0, 24);
String encrypt = encrypt(substring, str);
String encrypt2 = encrypt(substring, String.valueOf(System.currentTimeMillis()));
Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(1, publicKey);
byte[] doFinal = cipher.doFinal(substring.getBytes());
String encodeToString = android.util.Base64.encodeToString(doFinal, 0, doFinal.length, 2);
return "data=" + URLEncoder.encode(encrypt, CHARSET) + "&key=" + URLEncoder.encode(encodeToString, CHARSET) + "×tamp=" + URLEncoder.encode(encrypt2, CHARSET) + "&dataContentType=" + URLEncoder.encode(ContentType.JSON_PROXY_MONEY, CHARSET);
} catch (UnsupportedEncodingException | InvalidKeyException | NoSuchAlgorithmException | BadPaddingException | IllegalBlockSizeException | NoSuchPaddingException unused) {
return null;
}
}
[Java] 纯文本查看 复制代码 String substring = UUID.randomUUID().toString().substring(0, 24);
这个是得到一个UUID的密钥
[Java] 纯文本查看 复制代码 String encrypt = encrypt(substring, str);
这个就是加密数据的方法
[Java] 纯文本查看 复制代码 Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(1, publicKey);
byte[] doFinal = cipher.doFinal(substring.getBytes());
这里是拿到公钥给加密的密码进行再次加密,用的是RSA非对称加密。
继续分析encrypt函数代码
[Java] 纯文本查看 复制代码 private static String encrypt(String str, String str2) {
byte[] bArr = new byte[24];
if (str == null) {
new SecureRandom().nextBytes(bArr);
} else {
System.arraycopy(str.getBytes(), 0, bArr, 0, 24);
}
try {
SecretKey generateSecret = SecretKeyFactory.getInstance("DESede").generateSecret(new DESedeKeySpec(bArr));
Cipher cipher = Cipher.getInstance(generateSecret.getAlgorithm());
cipher.init(1, generateSecret);
byte[] doFinal = cipher.doFinal(str2.getBytes(CHARSET));
return android.util.Base64.encodeToString(doFinal, 0, doFinal.length, 2);
} catch (UnsupportedEncodingException | InvalidKeyException | NoSuchAlgorithmException | InvalidKeySpecException | BadPaddingException | IllegalBlockSizeException | NoSuchPaddingException unused) {
return "";
}
}
str密钥 str2是密码
可以看输出数据加密使用的是3DES进行加密,也就是128位168位的二进制数据进行加密,这里使用的是24位3个字节数据来存储密码。
写一个脚本进行解密代码如下:
[Python] 纯文本查看 复制代码 from Crypto.Cipher import DES3
from Crypto.Util.Padding import unpad
import base64
def decrypt_3des_ecb(encrypted_base64: str, key_str: str) -> str:
"""
解密使用Java函数加密的3DES-ECB数据
参数:
encrypted_base64: Base64编码的加密字符串
key_str: 密钥字符串(长度不足24字节时用0填充)
返回:
解密后的原始字符串
"""
# 处理密钥(与Java逻辑一致)
key_bytes = key_str.encode('utf-8')
if len(key_bytes) < 24:
# 不足24字节时用0填充
key_bytes = key_bytes.ljust(24, b'\x00')
else:
# 超过24字节时截取前24字节
key_bytes = key_bytes[:24]
# 解码Base64密文
encrypted_bytes = base64.b64decode(encrypted_base64)
# 创建3DES-ECB解密器
cipher = DES3.new(key_bytes, DES3.MODE_ECB)
# 解密并去除PKCS5填充
decrypted_bytes = cipher.decrypt(encrypted_bytes)
unpadded_bytes = unpad(decrypted_bytes, DES3.block_size)
# 假设原始编码是UTF-8(根据Java代码中的CHARSET)
return unpadded_bytes.decode('utf-8')
if __name__ == "__main__":
encrypted_data = "xxxxxxxxxxxx"
# 替换为实际加密数据
# 密钥(与加密时使用的相同)
key = "bc52a640-7bda-49d8-9a25-" # 长度不足24字节
decrypted_text = decrypt_3des_ecb(encrypted_data, key)
print(f"Decrypted Text: {decrypted_text}")
最终得到解密的原始协议数据
|
免费评分
-
查看全部评分
|
发帖前要善用【论坛搜索】功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。 |
|
|
|
|
|