算法暴露接口(xhs、dy、ks、wx、hnw)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 

200 lines
7.8 KiB

from functools import reduce
import base64
from xhs.shield_const import *
"""
小红书 hmac_main 的aes处理
自定义aes
逆向版本 6.97.0.1
"""
class AES:
def __init__(self, key: bytes):
self.aes_type = len(key) * 8
self._key_r = self._generate_key(key)
def _sort_key(self, w_list):
"""
重新编排key
"""
def change_char(w):
w_4 = self._split_int(w) # 拆分成 4x8bit
s_w_4 = [SBox[w_4[0]], SBox[w_4[1]], SBox[w_4[2]], SBox[w_4[3]]]
s_s_w_4 = [dword_7F550[s_w_4[0]], dword_7F950[s_w_4[1]], dword_7FD50[s_w_4[2]], dword_80150[s_w_4[3]]]
key4 = reduce(lambda x, y: x ^ y, s_s_w_4)
return key4
# 交换位置
for i in range(len(w_list) // 2):
tmp = w_list[i]
if i > 3: # 需要交换位置
tmp = change_char(tmp)
w_list[len(w_list) - i - 1] = change_char(w_list[len(w_list) - i -1])
w_list[i] = w_list[len(w_list) - i -1]
w_list[len(w_list) - i -1] = tmp
# print(i, len(w_list) - i -1)
return w_list
@staticmethod
def hex_string(num, step=2):
"""
step 控制二进制的长度 避免有些少零了
"""
tmp_string = hex(num)[2:]
if len(tmp_string) < step:
tmp_string = '0' * (step - len(tmp_string)) + tmp_string
return tmp_string
def encrypt(self, plaintext:bytes):
"""
加密 解密
:param plaintext:
:return:
"""
state = [[plaintext[i + j] for j in range(16)] for i in range(0, len(plaintext), 16)] # 先分组
result = []
# 进行加密流程
for p in range(len(state)):
_plaintext_block = [self._joint_int(state[p][i:i+4]) for i in range(0, 16, 4)]
state[p] = _plaintext_block.copy() # 合并为每四个字节的数组
_encrypt_block = self.encrypt_block(_plaintext_block) # 处理第一块
if p > 0:
extra_block = [self.hex_string(_encrypt_block[i] ^ state[p - 1][i], 8) for i in range(4)] # 异或处理
result.append("".join(extra_block))
return "".join(result[:4])
def encrypt_block(self, plaintext_block):
"""
按照块加密
"""
keys = self._key_r
# 第一轮由于明文和那个key是反的 单独计算
plaintext_block[0] = plaintext_block[0] ^ keys[3]
plaintext_block[1] = plaintext_block[1] ^ keys[2]
plaintext_block[2] = plaintext_block[2] ^ keys[1]
plaintext_block[3] = plaintext_block[3] ^ keys[0]
for i in range(0, 9):
num = (i + 1) * 4
a1 = self._split_int(plaintext_block[0]) # 这块是按顺序的
a2 = self._split_int(plaintext_block[1])
a3 = self._split_int(plaintext_block[2])
a4 = self._split_int(plaintext_block[3])
# 查表法进行替换
a1_1_box = [dword_7F550[a1[0]], dword_7F950[a1[1]], dword_7FD50[a1[2]], dword_80150[a1[3]]]
a2_1_box = [dword_7F550[a2[0]], dword_7F950[a2[1]], dword_7FD50[a2[2]], dword_80150[a2[3]]]
a3_1_box = [dword_7F550[a3[0]], dword_7F950[a3[1]], dword_7FD50[a3[2]], dword_80150[a3[3]]]
a4_1_box = [dword_7F550[a4[0]], dword_7F950[a4[1]], dword_7FD50[a4[2]], dword_80150[a4[3]]]
if i == 0:
# 矩阵运算 这块不太一样
d1 = [a4_1_box[0], a3_1_box[1], a2_1_box[2], a1_1_box[3]]
d2 = [a3_1_box[0], a2_1_box[1], a1_1_box[2], a4_1_box[3]]
d3 = [a2_1_box[0], a1_1_box[1], a4_1_box[2], a3_1_box[3]]
d4 = [a1_1_box[0], a4_1_box[1], a3_1_box[2], a2_1_box[3]]
else:
# 矩阵运算
d1 = [a1_1_box[0], a2_1_box[1], a3_1_box[2], a4_1_box[3]]
d2 = [a2_1_box[0], a3_1_box[1], a4_1_box[2], a1_1_box[3]]
d3 = [a3_1_box[0], a4_1_box[1], a1_1_box[2], a2_1_box[3]]
d4 = [a4_1_box[0], a1_1_box[1], a2_1_box[2], a3_1_box[3]]
# 拼接
plaintext_block[0] = reduce(lambda x, y: x ^ y, d1) ^ keys[num]
plaintext_block[1] = reduce(lambda x, y: x ^ y, d2) ^ keys[num + 1]
plaintext_block[2] = reduce(lambda x, y: x ^ y, d3) ^ keys[num + 2]
plaintext_block[3] = reduce(lambda x, y: x ^ y, d4) ^ keys[num + 3]
# 后四位
a1 = self._split_int(plaintext_block[0]) # 这块是按顺序的
a2 = self._split_int(plaintext_block[1])
a3 = self._split_int(plaintext_block[2])
a4 = self._split_int(plaintext_block[3])
a1_sum_box = [SBoxIV[a4[0]], SBoxIV[a1[1]], SBoxIV[a2[2]], SBoxIV[a3[3]]] # 合并后
a2_sum_box = [SBoxIV[a3[0]], SBoxIV[a4[1]], SBoxIV[a1[2]], SBoxIV[a2[3]]] # 合并后
a3_sum_box = [SBoxIV[a2[0]], SBoxIV[a3[1]], SBoxIV[a4[2]], SBoxIV[a1[3]]] # 合并后
a4_sum_box = [SBoxIV[a1[0]], SBoxIV[a2[1]], SBoxIV[a3[2]], SBoxIV[a4[3]]] # 合并后
plaintext_block[0] = self._joint_int(a1_sum_box) ^ keys[-1]
plaintext_block[1] = self._joint_int(a2_sum_box) ^ keys[-2]
plaintext_block[2] = self._joint_int(a3_sum_box) ^ keys[-3]
plaintext_block[3] = self._joint_int(a4_sum_box) ^ keys[-4]
return plaintext_block
def _generate_key(self, key: bytes) -> list:
"""密钥扩展"""
Rcon = [0x12310000, 0x2000100, 0x4020000, 0x8020200, 0x10102000, 0x30020400, 0x40002000, 0x80002000, 0x1B002000, 0x36200200] # 轮常数
Nr, Nk = 10 + (self.aes_type - 128) // 32, self.aes_type // 32 # Nr:轮数,Nk:密钥长度
w = [0 for _ in range(4 * (Nr + 1))] # 轮密钥
p = [4052295985, 4278194467, 4043314006, 4045621392] # 额外处理
for i in range(Nk): # 初始化是对的
w[i] = int.from_bytes(key[4 * i:4 * i + 4], 'big') ^ p[i]
# print(f"w{i} => {hex(w[i])}")
for i in range(Nk, 4 * (Nr + 1)):
temp = w[i - 1]
# print("******************************")
# print(f"开始", hex(temp), i % Nk)
if i % Nk == 0:
temp = self._split_int(temp) # 拆分成 4x8bit
temp = [SBox[temp[1]], SBox[temp[2]], SBox[temp[3]], SBox[temp[0]]] # sbox 的轮数发生变化了
temp = self._joint_int(temp) ^ Rcon[i // Nk - 1] # 合并回 32bit
elif Nk > 6 and i % Nk == 4:
temp = self._split_int(temp) # 拆分成 4x8bit
temp = [SBox[temp[0]], SBox[temp[1]],
SBox[temp[2]], SBox[temp[3]]]
temp = self._joint_int(temp) # 合并回 32bit
w[i] = w[i - Nk] ^ temp
w = self._sort_key(w) # 替换处理key
return w
@staticmethod
def _split_int(n: int) -> list:
"""拆分 32bit 成 4x8bit"""
return [(n >> 24) & 0xFF, (n >> 16) & 0xFF, (n >> 8) & 0xFF, n & 0xFF]
@staticmethod
def _joint_int(b: list) -> int:
"""合并 4x8bit 成 32bit"""
return (b[0] << 24) | (b[1] << 16) | (b[2] << 8) | b[3]
def get_key(deviceId, hmac_main):
"""
外部获取 接口
:param deviceId: 设备id
:param hmac_main: 存在于机器的 s.xml
:return:
"""
key = deviceId[:16]
plaintext = base64.b64decode(hmac_main.encode("utf-8"))
A = AES(key.encode("utf-8"))
result = A.encrypt(plaintext)
return result
if __name__ == '__main__':
key = "2fe75062-a528-33"
m = AES(key.encode("utf-8"))
hmac_main = "cDdMxUWZy3e2szBCUB04rZMxTdf6tVKcpCIRrDQGa/NS8Agki6U5MGN6c6QCT3t6amTAYBbcDwFlPndCV3AfaerPd36GS9sdmTeKzBU45YsIBsGAdBXyy2GnkRlDaVCO"
plaintext = base64.b64decode(hmac_main.encode("utf-8"))
pp = m.encrypt(plaintext)
print(get_key(key, hmac_main))