├── argus.py ├── gorgon.py ├── ladon.py └── readme.md /argus.py: -------------------------------------------------------------------------------- 1 | import base64 2 | import hashlib 3 | import random 4 | import struct 5 | 6 | from Crypto.Cipher import AES 7 | from pysmx.SM3 import SM3 8 | 9 | from com.xargus1128 import xa_pb2 10 | 11 | unpad = lambda s: s[: -ord(s[len(s) - 1:])] 12 | pad = lambda s: s + (chr((16 - (len(s) % 16))).encode() * (16 - (len(s) % 16))) 13 | 14 | 15 | class Argus: 16 | sign_key = (b'\x8e\xbd\xfa8\x06\xec\xc5\xce\xe7\x94#\xe6\x02\x9e\xd8%@\xbc"\x18\xbb~' 17 | b'\xae\xf7\x1c\xb6\x91\xf7\xaa\x8a\xa2\xf5') 18 | 19 | def __init__(self, ): 20 | self.xa_pb: bytes | None = None 21 | self.apd = [] 22 | 23 | def gen_xa_pb(self, khronos: int, queryHash: bytes, bodyHash: bytes, 24 | device_id: str, license_id: str, app_version: str, 25 | sdk_version_str, sdk_version, sec_device_id, call_type, sign_count, report_count, 26 | setting_count) -> bytes: 27 | def calc_protobuf3(d): 28 | high = (d << 1) & 0xFFFFFFFF 29 | return high ^ (d >> 31) 30 | 31 | xa_pb = xa_pb2.GenerateObj() # type:ignore 32 | xa_pb.magic = 1077940818 33 | xa_pb.version = 2 34 | xa_pb.random = calc_protobuf3(random.randint(0x10000000, 0x8FFFFFFF)) 35 | # xa_pb.random = 2904324654 36 | xa_pb.msAppId = "1128" 37 | xa_pb.deviceId = device_id 38 | xa_pb.licenseId = license_id 39 | xa_pb.appVersion = app_version 40 | xa_pb.sdkVersionStr = sdk_version_str 41 | xa_pb.sdkVersion = sdk_version 42 | 43 | # root检测 44 | xa_pb.envCode = b"\x00\x00\x00\x00\x00\x00\x00\x00" 45 | 46 | xa_pb.platform = 0 # 安卓-0 IOS-1 47 | xa_pb.createTime = khronos << 1 48 | # xa_pb.createTime = 3396616762 49 | 50 | self.apd.append(bodyHash[0]) 51 | self.apd.append(queryHash[0]) 52 | xa_pb.bodyHash = bodyHash 53 | xa_pb.queryHash = queryHash 54 | 55 | action_record = xa_pb2.ActionRecord() # type:ignore 56 | # unknown_fields = xa_pb2.UnknownFields() 57 | 58 | action_record.signCount = sign_count 59 | # action_record.reportCount = random.randint(10000, 100000) 60 | action_record.reportCount = report_count 61 | action_record.settingCount = setting_count 62 | 63 | # action_record.reportFailCount = random.randint(0, 3) 64 | # action_record.memoizedIsInitialized = -1 65 | # action_record.unknownFields.CopyFrom(unknown_fields) 66 | # action_record.memoizedSize = -1 67 | # action_record.memoizedHashCode = 0 68 | 69 | # action_record.signCount = 506 70 | # action_record.reportCount = 2 71 | # action_record.settingCount = 1388734 72 | 73 | xa_pb.actionRecord.CopyFrom(action_record) 74 | 75 | xa_pb.secDeviceToken = sec_device_id 76 | xa_pb.isAppLicense = xa_pb.createTime 77 | xa_pb.pskVersion = 'none' 78 | xa_pb.callType = call_type 79 | 80 | result = xa_pb.SerializeToString() 81 | 82 | # print(result.hex()) 83 | self.xa_pb = result 84 | return result 85 | 86 | @staticmethod 87 | def _encrypt_random(key): 88 | A = 0 89 | T = 0 90 | for i in range(0, len(key), 2): 91 | B = key[i] ^ A 92 | C = (T >> 0x3) & 0xFFFFFFFF 93 | D = C ^ B 94 | E = D ^ T 95 | F = (E >> 0x5) & 0xFFFFFFFF 96 | G = (E << 0xB) & 0xFFFFFFFF 97 | H = key[i + 1] | G 98 | I = F ^ H 99 | J = I ^ E 100 | T = ~J & 0xFFFFFFFF 101 | # A = (T << 7) & 0xFFFFFFFF 102 | return T 103 | 104 | def _eor_data(self, key, data): 105 | rdm_list = self._encrypt_random(key) 106 | rdm_list = struct.pack(">I", rdm_list) 107 | for i in range(len(data)): 108 | data[i] ^= rdm_list[i % 4] 109 | return data 110 | 111 | def _gen_key(self, rdm): 112 | def bfi(rd, rn, lsb, width): 113 | ls = 0xFFFFFFFF >> (32 - width) 114 | rn = (rn & ls) << lsb 115 | ls = ~(ls << lsb) 116 | rd = rd & ls 117 | rd = rd | rn 118 | return rd 119 | 120 | data = ( 121 | list(self.sign_key[:16]) 122 | + list(self.sign_key[16:]) 123 | + list(struct.pack("> t) & 0xFFFFFFFF) | A 145 | off_1 = t - 0x20 146 | if off_1 >= 0: 147 | B = 0x3DC94C3A >> off_1 148 | H = (sm3_list[6] >> 3) & 0xFFFFFFFF 149 | H |= (sm3_list[7] << 29) & 0xFFFFFFFF 150 | # print(hex(H), hex(sm3_list[2])) 151 | C = H ^ sm3_list[2] 152 | # bfi = (B & 1) | 0xFFFFFFFD 153 | bfi_v = bfi(B, 0x7FFFFFFE, 1, 0x1F) 154 | D = bfi_v ^ sm3_list[0] ^ C 155 | H = (sm3_list[7] >> 3) & 0xFFFFFFFF 156 | H |= (sm3_list[6] << 29) & 0xFFFFFFFF 157 | # print("H==========", hex(H)) 158 | E = H ^ sm3_list[3] 159 | # 根据E判断是否进位 160 | if E & 1: 161 | B = (C >> 1) | 0x80000000 162 | else: 163 | B = C >> 1 164 | H = (C << 31) & 0xFFFFFFFF 165 | F = (E >> 1) | H 166 | G = F ^ sm3_list[1] ^ E 167 | A = ~G & 0xFFFFFFFF 168 | F = D ^ B 169 | for j in range(6): 170 | sm3_list[j] = sm3_list[j + 2] 171 | sm3_list[6] = F 172 | sm3_list[7] = A 173 | for j in range(2): 174 | for d in list(struct.pack("> 0x18) | ((block[(2 + t) % 4] << 0x08) & 0xFFFFFFFF) 189 | BB = ((block[(2 + t) % 4] << 0x1) & 0xFFFFFFFF) | (block[3 - t] >> 0x1F) 190 | CC = AA & BB 191 | DD = block[t] ^ CC 192 | EE = (block[3 - t] >> 0x1E) | ((block[(2 + t) % 4] << 0x02) & 0xFFFFFFFF) 193 | block[t] = sm3_list[i] ^ DD ^ EE 194 | res_list = [] 195 | for i in range(4): 196 | res_list += struct.pack(" dict: 258 | def pb2dict(obj): 259 | """ 260 | Takes a ProtoBuf Message obj and convertes it to a dict. 261 | """ 262 | adict = {} 263 | if not obj.IsInitialized(): 264 | return None 265 | 266 | for field in obj.DESCRIPTOR.fields: 267 | if not getattr(obj, field.name): 268 | continue 269 | from google.protobuf.descriptor import FieldDescriptor 270 | if not field.label == FieldDescriptor.LABEL_REPEATED: 271 | if not field.type == FieldDescriptor.TYPE_MESSAGE: 272 | adict[field.name] = getattr(obj, field.name) 273 | else: 274 | value = pb2dict(getattr(obj, field.name)) 275 | if value: 276 | adict[field.name] = value 277 | else: 278 | if field.type == FieldDescriptor.TYPE_MESSAGE: 279 | adict[field.name] = [pb2dict(v) for v in getattr(obj, field.name)] 280 | else: 281 | adict[field.name] = [v for v in getattr(obj, field.name)] 282 | return adict 283 | 284 | unpad = lambda s: s[: -ord(s[len(s) - 1:])] 285 | 286 | xa_pb = xa_pb2.GenerateObj() # type:ignore 287 | xa_pb.ParseFromString(unpad(pb_bytes)) 288 | 289 | xa_pb = pb2dict(xa_pb) 290 | # for key, value in xa_pb.items(): 291 | # print(key, value) 292 | 293 | xa_pb['envCode'] = xa_pb['envCode'].hex() 294 | xa_pb['bodyHash'] = xa_pb['bodyHash'].hex() 295 | xa_pb['queryHash'] = xa_pb['queryHash'].hex() 296 | 297 | if pskHash := xa_pb.get('pskHash'): 298 | xa_pb['pskHash'] = pskHash.hex() 299 | if pskCalHash := xa_pb.get('pskCalHash'): 300 | xa_pb['pskCalHash'] = pskCalHash.hex() 301 | 302 | return xa_pb 303 | 304 | def decrypt(self, x_argus: str): 305 | def restore_eor_data(key, data): 306 | rdm_list = self._encrypt_random(key) 307 | rdm_list = struct.pack(">I", rdm_list) 308 | restored_data = bytearray(len(data)) 309 | 310 | for i in range(len(data)): 311 | restored_data[i] = data[i] ^ rdm_list[i % 4] 312 | 313 | return restored_data 314 | 315 | def decrypt(encrypted_result, key): 316 | sm3_list = [] 317 | for i in range(0, len(key), 4): 318 | c = struct.unpack("> 0x18) | ((proto[(2 + t) % 4] << 0x08) & 0xFFFFFFFF) 331 | BB = ((proto[(2 + t) % 4] << 0x1) & 0xFFFFFFFF) | (proto[3 - t] >> 0x1F) 332 | CC = AA & BB 333 | DD = proto[t] ^ CC 334 | EE = (proto[3 - t] >> 0x1E) | ((proto[(2 + t) % 4] << 0x02) & 0xFFFFFFFF) 335 | proto[t] = sm3_list[i] ^ DD ^ EE 336 | # print(proto) 337 | return proto 338 | 339 | raw_data = base64.b64decode(x_argus) 340 | b64_header = raw_data[:2] 341 | 342 | # 随机的头 343 | print('b64_header ---> ', b64_header) 344 | raw_data = raw_data[2:] 345 | 346 | aes_key = hashlib.md5(self.sign_key[:16]).digest() 347 | aes_iv = hashlib.md5(self.sign_key[16:]).digest() 348 | 349 | cipher = AES.new(aes_key, AES.MODE_CBC, aes_iv) 350 | de_text = cipher.decrypt(raw_data) 351 | de_text = unpad(de_text) 352 | 353 | headers = de_text[:9] 354 | print('headers ---> ', [hex(x) for x in headers]) 355 | 356 | de_text = de_text[9:] 357 | 358 | eor_key = de_text[-2:] 359 | de_text = de_text[:-2] 360 | 361 | de_text = restore_eor_data(eor_key, de_text) 362 | 363 | eor_pad = de_text[-8:] 364 | print('enc_pad ---> ', [hex(x) for x in eor_pad]) 365 | # 转置 366 | de_text = de_text[:-8][::-1] 367 | 368 | rdm = struct.unpack(" ', raw_pb.hex()) 380 | pb = self.parse_xa_pb(raw_pb) 381 | print(pb) 382 | -------------------------------------------------------------------------------- /gorgon.py: -------------------------------------------------------------------------------- 1 | import hashlib 2 | import json 3 | import time 4 | 5 | 6 | class Gorgon: 7 | # 修改data类型为str和bytes 8 | def __init__(self, params: str, unix: int, data: str | bytes = None, cookies: str = None) -> None: 9 | self.unix = unix 10 | self.params = params 11 | self.data = data 12 | self.cookies = cookies 13 | 14 | # 修改data类型为str和bytes 15 | def hash(self, data: str | bytes) -> str: 16 | if type(data) is bytes: 17 | return str(hashlib.md5(data).hexdigest()) 18 | else: 19 | return str(hashlib.md5(data.encode()).hexdigest()) 20 | 21 | def get_base_string(self) -> str: 22 | base_str = self.hash(self.params) 23 | base_str = ( 24 | base_str + self.hash(self.data) if self.data else base_str + str("0" * 32) 25 | ) 26 | base_str = ( 27 | base_str + self.hash(self.cookies) 28 | if self.cookies 29 | else base_str + str("0" * 32) 30 | ) 31 | return base_str 32 | 33 | def get_value(self) -> dict: 34 | return self.encrypt(self.get_base_string()) 35 | 36 | def encrypt(self, data: str) -> json: 37 | len = 0x14 38 | key = [ 39 | 0xDF, 40 | 0x77, 41 | 0xB9, 42 | 0x40, 43 | 0xB9, 44 | 0x9B, 45 | 0x84, 46 | 0x83, 47 | 0xD1, 48 | 0xB9, 49 | 0xCB, 50 | 0xD1, 51 | 0xF7, 52 | 0xC2, 53 | 0xB9, 54 | 0x85, 55 | 0xC3, 56 | 0xD0, 57 | 0xFB, 58 | 0xC3, 59 | ] 60 | param_list = [] 61 | for i in range(0, 12, 4): 62 | temp = data[8 * i: 8 * (i + 1)] 63 | for j in range(4): 64 | H = int(temp[j * 2: (j + 1) * 2], 16) 65 | param_list.append(H) 66 | param_list.extend([0x0, 0x6, 0xB, 0x1C]) 67 | H = int(hex(int(self.unix)), 16) 68 | param_list.append((H & 0xFF000000) >> 24) 69 | param_list.append((H & 0x00FF0000) >> 16) 70 | param_list.append((H & 0x0000FF00) >> 8) 71 | param_list.append((H & 0x000000FF) >> 0) 72 | eor_result_list = [] 73 | for A, B in zip(param_list, key): 74 | eor_result_list.append(A ^ B) 75 | for i in range(len): 76 | C = self.reverse(eor_result_list[i]) 77 | D = eor_result_list[(i + 1) % len] 78 | E = C ^ D 79 | F = self.rbit_algorithm(E) 80 | H = ((F ^ 0xFFFFFFFF) ^ len) & 0xFF 81 | eor_result_list[i] = H 82 | result = "" 83 | 84 | for param in eor_result_list: 85 | result += self.hex_string(param) 86 | 87 | return { 88 | "x-ss-req-ticket": str(int(self.unix * 1000)), 89 | "x-khronos": str(int(self.unix)), 90 | "x-gorgon": f"0404b0d30000{result}" 91 | } 92 | 93 | def rbit_algorithm(self, num): 94 | result = "" 95 | tmp_string = bin(num)[2:] 96 | while len(tmp_string) < 8: 97 | tmp_string = "0" + tmp_string 98 | for i in range(0, 8): 99 | result = result + tmp_string[7 - i] 100 | return int(result, 2) 101 | 102 | def hex_string(self, num): 103 | tmp_string = hex(num)[2:] 104 | if len(tmp_string) < 2: 105 | tmp_string = "0" + tmp_string 106 | return tmp_string 107 | 108 | def reverse(self, num): 109 | tmp_string = self.hex_string(num) 110 | return int(tmp_string[1:] + tmp_string[:1], 16) 111 | 112 | 113 | def main(): 114 | def test_str_data(): 115 | params = 'aweme_id=7277398346103328058&cursor=0&count=20&address_book_access=2&gps_access=2&forward_page_type=1&channel_id=0&city=440100&hotsoon_filtered_count=0&hotsoon_has_more=0&follower_count=0&is_familiar=0&page_source=0&user_avatar_shrink=64_64&aweme_author=MS4wLjABAAAA1edgaCuAnMvrS6zhTpLMnwQIk39kngyeeWaS-XViaIc&item_type=0&manifest_version_code=160409&_rticket=1698305971445&app_type=normal&iid=3828917516401031&is_android_pad=0&channel=googlePlay&device_type=Pixel+4&language=zh&cpu_support64=true&host_abi=arm64-v8a&resolution=1080*2214&openudid=bae463c2f0481673&update_version_code=16409909&cdid=44177971-e3b7-49a0-aa3a-f6f25066462e&appTheme=light&minor_status=0&os_api=33&dpi=440&ac=wifi&package=com.ss.android.ugc.aweme.mobile&device_id=2773384256826104&os=android&os_version=13&version_code=160409&app_name=aweme&version_name=16.4.9&device_brand=google&ssmix=a&device_platform=android&aid=1128&ts=1698305971' 116 | unix = int(time.time()) 117 | payload = 'aaa' 118 | cookie = '' 119 | 120 | xa = Gorgon(params, unix, payload, cookie).get_value() 121 | print(xa) 122 | 123 | def test_bytes_data(): 124 | params = 'aweme_id=7277398346103328058&cursor=0&count=20&address_book_access=2&gps_access=2&forward_page_type=1&channel_id=0&city=440100&hotsoon_filtered_count=0&hotsoon_has_more=0&follower_count=0&is_familiar=0&page_source=0&user_avatar_shrink=64_64&aweme_author=MS4wLjABAAAA1edgaCuAnMvrS6zhTpLMnwQIk39kngyeeWaS-XViaIc&item_type=0&manifest_version_code=160409&_rticket=1698305971445&app_type=normal&iid=3828917516401031&is_android_pad=0&channel=googlePlay&device_type=Pixel+4&language=zh&cpu_support64=true&host_abi=arm64-v8a&resolution=1080*2214&openudid=bae463c2f0481673&update_version_code=16409909&cdid=44177971-e3b7-49a0-aa3a-f6f25066462e&appTheme=light&minor_status=0&os_api=33&dpi=440&ac=wifi&package=com.ss.android.ugc.aweme.mobile&device_id=2773384256826104&os=android&os_version=13&version_code=160409&app_name=aweme&version_name=16.4.9&device_brand=google&ssmix=a&device_platform=android&aid=1128&ts=1698305971' 125 | unix = int(time.time()) 126 | cookie = '' 127 | with open('../bin/device_register', 'rb') as file: 128 | # with open('../bin/package', 'rb') as file: 129 | payload = file.read() 130 | xa = Gorgon(params, unix, payload, cookie).get_value() 131 | print(xa) 132 | 133 | # test_str_data() 134 | test_bytes_data() 135 | 136 | 137 | if __name__ == '__main__': 138 | main() 139 | -------------------------------------------------------------------------------- /ladon.py: -------------------------------------------------------------------------------- 1 | import base64 2 | import ctypes 3 | import hashlib 4 | from os import urandom 5 | 6 | 7 | def pkcs7_padding_data_length(buffer, buffer_size, modulus): 8 | if buffer_size % modulus != 0 or buffer_size < modulus: 9 | return 0 10 | padding_value = buffer[buffer_size - 1] 11 | if padding_value < 1 or padding_value > modulus: 12 | return 0 13 | if buffer_size < padding_value + 1: 14 | return 0 15 | count = 1 16 | buffer_size -= 1 17 | for i in range(count, padding_value): 18 | buffer_size -= 1 19 | if buffer[buffer_size] != padding_value: 20 | return 0 21 | return buffer_size 22 | 23 | 24 | def pkcs7_padding_pad_buffer(buffer: bytearray, data_length: int, buffer_size: int, modulus: int) -> int: 25 | pad_byte = modulus - (data_length % modulus) 26 | if data_length + pad_byte > buffer_size: 27 | return -pad_byte 28 | for i in range(pad_byte): 29 | buffer[data_length + i] = pad_byte 30 | return pad_byte 31 | 32 | 33 | def padding_size(size: int) -> int: 34 | mod = size % 16 35 | if mod > 0: 36 | return size + (16 - mod) 37 | return size 38 | 39 | 40 | def md5bytes(data: bytes) -> str: 41 | m = hashlib.md5() 42 | m.update(data) 43 | return m.hexdigest() 44 | 45 | 46 | def get_type_data(ptr, index, data_type): 47 | if data_type == "uint64_t": 48 | return int.from_bytes(ptr[index * 8: (index + 1) * 8], "little") 49 | else: 50 | raise ValueError("Invalid data type") 51 | 52 | 53 | def set_type_data(ptr, index, data, data_type): 54 | if data_type == "uint64_t": 55 | ptr[index * 8: (index + 1) * 8] = data.to_bytes(8, "little") 56 | else: 57 | raise ValueError("Invalid data type") 58 | 59 | 60 | def validate(num): 61 | return num & 0xFFFFFFFFFFFFFFFF 62 | 63 | 64 | def __ROR__(value: ctypes.c_ulonglong, count: int) -> ctypes.c_ulonglong: 65 | nbits = ctypes.sizeof(value) * 8 66 | count %= nbits 67 | low = ctypes.c_ulonglong(value.value << (nbits - count)).value 68 | value = ctypes.c_ulonglong(value.value >> count).value 69 | value = value | low 70 | return value 71 | 72 | 73 | def encrypt_ladon_input(hash_table, input_data): 74 | data0 = int.from_bytes(input_data[:8], byteorder="little") 75 | data1 = int.from_bytes(input_data[8:], byteorder="little") 76 | 77 | for i in range(0x22): 78 | hash = int.from_bytes(hash_table[i * 8: (i + 1) * 8], byteorder="little") 79 | data1 = validate(hash ^ (data0 + ((data1 >> 8) | (data1 << (64 - 8))))) 80 | data0 = validate(data1 ^ ((data0 >> 0x3D) | (data0 << (64 - 0x3D)))) 81 | 82 | output_data = bytearray(26) 83 | output_data[:8] = data0.to_bytes(8, byteorder="little") 84 | output_data[8:] = data1.to_bytes(8, byteorder="little") 85 | 86 | return bytes(output_data) 87 | 88 | 89 | def encrypt_ladon(md5hex: bytes, data: bytes, size: int): 90 | hash_table = bytearray(272 + 16) 91 | hash_table[:32] = md5hex 92 | 93 | temp = [] 94 | for i in range(4): 95 | temp.append(int.from_bytes(hash_table[i * 8: (i + 1) * 8], byteorder="little")) 96 | 97 | buffer_b0 = temp[0] 98 | buffer_b8 = temp[1] 99 | temp.pop(0) 100 | temp.pop(0) 101 | 102 | for i in range(0, 0x22): 103 | x9 = buffer_b0 104 | x8 = buffer_b8 105 | x8 = validate(__ROR__(ctypes.c_ulonglong(x8), 8)) 106 | x8 = validate(x8 + x9) 107 | x8 = validate(x8 ^ i) 108 | temp.append(x8) 109 | x8 = validate(x8 ^ __ROR__(ctypes.c_ulonglong(x9), 61)) 110 | set_type_data(hash_table, i + 1, x8, "uint64_t") 111 | buffer_b0 = x8 112 | buffer_b8 = temp[0] 113 | temp.pop(0) 114 | 115 | new_size = padding_size(size) 116 | 117 | input = bytearray(new_size) 118 | input[:size] = data 119 | pkcs7_padding_pad_buffer(input, size, new_size, 16) 120 | 121 | output = bytearray(new_size) 122 | for i in range(new_size // 16): 123 | output[i * 16: (i + 1) * 16] = encrypt_ladon_input( 124 | hash_table, input[i * 16: (i + 1) * 16] 125 | ) 126 | 127 | return output 128 | 129 | 130 | def ladon_encrypt( 131 | khronos: int, 132 | license_id: int = 1611921764, 133 | aid: int = 1233, 134 | random_bytes: bytes = urandom(4)) -> str: 135 | data = f"{khronos}-{license_id}-{aid}" 136 | 137 | keygen = random_bytes + str(aid).encode() 138 | md5hex = md5bytes(keygen) 139 | 140 | size = len(data) 141 | new_size = padding_size(size) 142 | 143 | output = bytearray(new_size + 4) 144 | output[:4] = random_bytes 145 | 146 | output[4:] = encrypt_ladon(md5hex.encode(), data.encode(), size) 147 | 148 | return base64.b64encode(bytes(output)).decode() 149 | 150 | 151 | class Ladon: 152 | @staticmethod 153 | def encrypt(x_khronos: int, lc_id: str, aid: int) -> str: 154 | return ladon_encrypt(x_khronos, lc_id, aid) 155 | 156 | 157 | if __name__ == "__main__": 158 | print(ladon_encrypt(1674223203, 1611921764, 1233)) 159 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | 抖音数据接口,四神算法,X-Argus,X-Medusa,抖音个人信息,抖音优惠团购列表,橱窗商品列表,刷播放量(可突破1000限制) 2 | 3 | 联系 TG:@LuxoPus5896 4 | --------------------------------------------------------------------------------