安卓解密的具体方法我就不再赘述了,大体方法都是正确的,这里只讲我遇到的问题。
其实这个问题应该是在 Java 外的语言中才会出现,安卓 QQ 的加密方法是写在 so 层的,传入的是 Java 层的 String.toCharArray()
,然后将每个 char 异或一次。这里就会涉及到编码的问题。
众所周知 UTF-8 是变长编码的,可能的字节数为 1-4。但是 Java 中toCharArray
使用的编码并不是 UTF-8,而是 UTF-16BE。在 UTF-8 长度为四个字节的情况下(例如某些 emoji),UTF-16BE 会将其转换为两个 char,如果我们再按照一个 char 去解密,就会出现乱码。
此处给出正确的解密 python 代码:
import struct
def utf8_to_unicode_arr(utf8_bytes):
string = utf8_bytes.decode('utf-8').encode('utf-16be')
string = struct.unpack(f'>{len(string)//2}H', string)
return list(string)
def convert_to_utf8(char_array):
binary_data = struct.pack(
f'>{len(char_array)}H', *char_array)
utf8_bytes = binary_data.decode('utf-16be').encode('utf-8')
return utf8_bytes.decode('utf-8')
def decrypt(data, key):
if not data:
return data
msg = b''
if type(data) == bytes:
msg = b''
for i in range(0, len(data)):
msg += bytes([data[i] ^ ord(key[i % len(key)])])
return msg
elif type(data) == str:
code_points = utf8_to_unicode_arr(data.encode("utf-8"))
for i in range(0, len(code_points)):
code_points[i] ^= ord(key[i % len(key)])
return convert_to_utf8(code_points)
else:
return data
print(decrypt(b'xWVYQTDXOC\x10[^^OEC\xc2\x90~\x14\xf0\x93\x8c\x8b'.decode(), "02:00:00:00:00:00"))
# 输出:Helianthus annuus L.🌻
当然,在字节长度 1-3 的情况下,直接按照 UTF-8 解密也是可以的,但是在字节长度为 4 的情况下,就会出现乱码,需要特别注意。