返回 douyin-downloader
abogus.py
根目录 / utils / abogus.py
1 # path: f2/utils/abogus.py
2
3 #!/usr/bin/env python
4 # -*- encoding: utf-8 -*-
5 """
6 @Description:abogus.py
7 @Date :2024/06/16 11:21:14
8 @Author :JohnserfSeed
9 @version :0.0.3
10 @License :Apache License 2.0
11 @Github :https://github.com/johnserf-seed
12 @Mail :support@f2.wiki
13 -------------------------------------------------
14 Change Log :
15 2024/06/16 17:27:47 - Create ABogus algorithm & black style
16 2024/06/16 17:27:47 - Limit custom ua late open source full version
17 2024/07/08 21:50:12 - Open the full version of the custom UA
18 2025/03/05 155:55:42 - Perf Post Method Generate
19 -------------------------------------------------
20 """
21
22 import random
23 import time
24 from typing import Callable, Dict, List, Union
25
26 from gmssl import func, sm3
27
28
29 class StringProcessor:
30 """
31 StringProcessor 类用于计算ABogus算法中所需的字符串处理方法。
32 包括字符串和 ASCII 码之间的转换、无符号右移运算等。
33
34 类方法:
35
36 to_ord_str(s: str) -> str:
37 将字符串转换为 ASCII 码字符串。
38
39 to_ord_array(s: str) -> List[int]:
40 将字符串转换为 ASCII 码列表。
41
42 to_char_str(s: str) -> str:
43 将 ASCII 码列表转换为字符串。
44
45 to_char_array(s: str) -> List[int]:
46 将字符串转换为 ASCII 码列表。
47
48 js_shift_right(val: int, n: int) -> int:
49 实现 JavaScript 中的无符号右移运算。
50
51 generate_random_bytes(length: int = 3) -> str:
52 生成一组伪随机字节字符串,用于混淆数据。
53
54 使用示例:
55 ```python
56 # 将字符串转换为 ASCII 码字符串
57 ord_str = StringProcessor.to_ord_str("Hello, World!")
58 print(ord_str)
59
60 # 将字符串转换为 ASCII 码列表
61 ord_array = StringProcessor.to_ord_array("Hello, World!")
62 print(ord_array)
63
64 # 将 ASCII 码列表转换为字符串
65 char_str = StringProcessor.to_char_str(ord_array)
66 print(char_str)
67
68 # 将字符串转换为 ASCII 码列表
69 char_array = StringProcessor.to_char_array("Hello, World!")
70 print(char_array)
71
72 # 实现 JavaScript 中的无符号右移运算
73 shifted_val = StringProcessor.js_shift_right(10, 2)
74 print(shifted_val)
75
76 # 生成一组伪随机字节字符串
77 random_bytes = StringProcessor.generate_random_bytes(3)
78 print(random_bytes)
79 ```
80 """
81
82 @staticmethod
83 def to_ord_str(s: str) -> str:
84 """
85 将字符串转换为 ASCII 码字符串 (Convert a string to an ASCII code string).
86
87 Args:
88 s (str): 输入字符串 (Input string).
89
90 Returns:
91 str: 转换后的 ASCII 码字符串 (Converted ASCII code string).
92 """
93 return "".join([chr(i) for i in s])
94
95 @staticmethod
96 def to_ord_array(s: str) -> List[int]:
97 """
98 将字符串转换为 ASCII 码列表 (Convert a string to a list of ASCII codes).
99
100 Args:
101 s (str): 输入字符串 (Input string).
102
103 Returns:
104 List[int]: 转换后的 ASCII 码列表 (Converted list of ASCII codes).
105 """
106 return [ord(char) for char in s]
107
108 @staticmethod
109 def to_char_str(s: str) -> str:
110 """
111 将 ASCII 码列表转换为字符串 (Convert a list of ASCII codes to a string).
112
113 Args:
114 s (str): ASCII 码列表 (List of ASCII codes).
115
116 Returns:
117 str: 转换后的字符串 (Converted string).
118 """
119 return "".join([chr(i) for i in s])
120
121 @staticmethod
122 def to_char_array(s: str) -> List[int]:
123 """
124 将字符串转换为 ASCII 码列表 (Convert a string to a list of ASCII codes).
125
126 Args:
127 s (str): 输入字符串 (Input string).
128
129 Returns:
130 List[int]: 转换后的 ASCII 码列表 (Converted list of ASCII codes).
131 """
132 return [ord(char) for char in s]
133
134 @staticmethod
135 def js_shift_right(val: int, n: int) -> int:
136 """
137 实现 JavaScript 中的无符号右移运算 (Implement the unsigned right shift operation in JavaScript).
138
139 Args:
140 val (int): 输入值 (Input value).
141 n (int): 右移位数 (Number of bits to shift right).
142
143 Returns:
144 int: 右移后的值 (Value after right shift).
145 """
146 return (val % 0x100000000) >> n
147
148 @staticmethod
149 def generate_random_bytes(length: int = 3) -> str:
150 """
151 生成一组伪随机字节字符串,用于混淆数据 (Generate a pseudo-random byte string to obfuscate the data).
152
153 Args:
154 length (int): 生成的字节序列长度 (Length of the byte sequence to generate).
155
156 Returns:
157 str: 生成的伪随机字节字符串 (Generated pseudo-random byte string).
158 """
159
160 def generate_byte_sequence() -> List[str]:
161 _rd = int(random.random() * 10000)
162 return [
163 chr(((_rd & 255) & 170) | 1),
164 chr(((_rd & 255) & 85) | 2),
165 chr((StringProcessor.js_shift_right(_rd, 8) & 170) | 5),
166 chr((StringProcessor.js_shift_right(_rd, 8) & 85) | 40),
167 ]
168
169 result = []
170 for _ in range(length):
171 result.extend(generate_byte_sequence())
172
173 return "".join(result)
174
175
176 class CryptoUtility:
177 """
178 CryptoUtility 类用于提供加密和编码的工具方法,包括 SM3 哈希、添加盐值、Base64 编码和 RC4 加密等。
179
180 类属性:
181 salt (str): 加密盐值 (Encryption salt).
182 base64_alphabet (List[str]): 自定义 Base64 字符表 (Custom Base64 alphabet).
183
184 类方法:
185 sm3_to_array(input_data: Union[str, List[int]]) -> List[int]:
186 计算请求体的 SM3 哈希值,并将结果转换为整数数组。
187
188 add_salt(param: str) -> str:
189 为字符串参数添加盐值。
190
191 process_param(param: Union[str, List[int]], add_salt: bool) -> Union[str, List[int]]:
192 处理输入参数,根据需要添加盐值。
193
194 params_to_array(param: Union[str, List[int]], add_salt: bool = True) -> List[int]:
195 获取输入参数的哈希数组。
196
197 transform_bytes(bytes_list: List[int]) -> str:
198 对输入的字节列表进行加密/解密操作,返回处理后的字符串。
199
200 base64_encode(input_string: str, selected_alphabet: int = 0) -> str:
201 使用自定义字符表对输入字符串进行 Base64 编码。
202
203 abogus_encode(abogus_bytes_str: str, selected_alphabet: int) -> str:
204 对输入的字节字符串进行自定义 Base64 编码,并添加位移和填充。
205
206 rc4_encrypt(key: bytes, plaintext: str) -> bytes:
207 使用 RC4 算法加密数据。
208
209 使用示例:
210 ```python
211 # 计算请求体的 SM3 哈希值
212 sm3_hash = CryptoUtility.sm3_to_array("Hello, World!")
213 print(sm3_hash)
214
215 # 为字符串参数添加盐值
216 salted_param = CryptoUtility.add_salt("Hello, World!")
217 print(salted_param)
218
219 # 获取输入参数的哈希数组
220 hash_array = CryptoUtility.params_to_array("Hello, World!")
221 print(hash_array)
222
223 # 对输入的字节列表进行加密/解密操作
224 encrypted_str = CryptoUtility.transform_bytes([72, 101, 108, 108, 111, 44, 32, 87, 111, 114, 108, 100, 33])
225 print(encrypted_str)
226
227 # 使用自定义字符表对输入字符串进行 Base64 编码
228 base64_str = CryptoUtility.base64_encode("Hello, World!")
229 print(base64_str)
230
231 # 对输入的字节字符串进行自定义 Base64 编码,并添加位移和填充
232 abogus_str = CryptoUtility.abogus_encode("Hello, World!", 0)
233 print(abogus_str)
234
235 # 使用 RC4 算法加密数据
236 key = b"key"
237 plaintext = "Hello, World!"
238 ciphertext = CryptoUtility.rc4_encrypt(key, plaintext)
239 print(ciphertext)
240 ```
241 """
242
243 def __init__(self, salt: str, custom_base64_alphabet: List[str]):
244 """
245 初始化 CryptoUtility 类
246 Initialize the CryptoUtility class.
247
248 Args:
249 salt (str): 加密盐值 (Encryption salt).
250 custom_base64_alphabet (List[str]): 自定义 Base64 字符表 (Custom Base64 alphabet).
251 """
252 self.salt = salt
253 self.base64_alphabet = custom_base64_alphabet
254
255 # fmt: off
256 self.big_array = [
257 121, 243, 55, 234, 103, 36, 47, 228, 30, 231, 106, 6, 115, 95, 78, 101, 250, 207, 198, 50,
258 139, 227, 220, 105, 97, 143, 34, 28, 194, 215, 18, 100, 159, 160, 43, 8, 169, 217, 180, 120,
259 247, 45, 90, 11, 27, 197, 46, 3, 84, 72, 5, 68, 62, 56, 221, 75, 144, 79, 73, 161,
260 178, 81, 64, 187, 134, 117, 186, 118, 16, 241, 130, 71, 89, 147, 122, 129, 65, 40, 88, 150,
261 110, 219, 199, 255, 181, 254, 48, 4, 195, 248, 208, 32, 116, 167, 69, 201, 17, 124, 125, 104,
262 96, 83, 80, 127, 236, 108, 154, 126, 204, 15, 20, 135, 112, 158, 13, 1, 188, 164, 210, 237,
263 222, 98, 212, 77, 253, 42, 170, 202, 26, 22, 29, 182, 251, 10, 173, 152, 58, 138, 54, 141,
264 185, 33, 157, 31, 252, 132, 233, 235, 102, 196, 191, 223, 240, 148, 39, 123, 92, 82, 128, 109,
265 57, 24, 38, 113, 209, 245, 2, 119, 153, 229, 189, 214, 230, 174, 232, 63, 52, 205, 86, 140,
266 66, 175, 111, 171, 246, 133, 238, 193, 99, 60, 74, 91, 225, 51, 76, 37, 145, 211, 166, 151,
267 213, 206, 0, 200, 244, 176, 218, 44, 184, 172, 49, 216, 93, 168, 53, 21, 183, 41, 67, 85,
268 224, 155, 226, 242, 87, 177, 146, 70, 190, 12, 162, 19, 137, 114, 25, 165, 163, 192, 23, 59,
269 9, 94, 179, 107, 35, 7, 142, 131, 239, 203, 149, 136, 61, 249, 14, 156
270 ]
271 # fmt: on
272
273 @staticmethod
274 def sm3_to_array(input_data: Union[str, List[int]]) -> List[int]:
275 """
276 计算请求体的 SM3 哈希值,并将结果转换为整数数组 (Calculate the SM3 hash value of the request body and convert the result to an array of integers).
277
278 Args:
279 input_data (Union[str, List[int]]): 输入数据 (Input data).
280
281 Returns:
282 List[int]: 哈希值的整数数组 (Array of integers representing the hash value).
283 """
284 # 如果输入是字符串,则将其编码为字节数组
285 if isinstance(input_data, str):
286 input_data_bytes = input_data.encode("utf-8")
287 else:
288 input_data_bytes = bytes(input_data) # 将 List[int] 转换为字节数组
289
290 # 将字节数组转换为适合 sm3.sm3_hash 函数处理的列表格式
291 hex_result = sm3.sm3_hash(func.bytes_to_list(input_data_bytes))
292
293 # 将十六进制字符串结果转换为十进制整数列表
294 return [int(hex_result[i : i + 2], 16) for i in range(0, len(hex_result), 2)]
295
296 def add_salt(self, param: str) -> str:
297 """
298 为字符串参数添加盐值 (Add salt to the string parameter).
299
300 Args:
301 param (str): 输入字符串 (Input string).
302
303 Returns:
304 str: 添加盐值后的字符串 (String with added salt).
305 """
306 return param + self.salt
307
308 def process_param(self, param: Union[str, List[int]], add_salt: bool) -> Union[str, List[int]]:
309 """
310 处理输入参数,根据需要添加盐值 (Process input parameter and add salt if needed).
311
312 Args:
313 param (Union[str, List[int]]): 输入参数 (Input parameter).
314 add_salt (bool): 是否添加盐值 (Whether to add salt).
315
316 Returns:
317 Union[str, List[int]]: 处理后的参数 (Processed parameter).
318 """
319 if isinstance(param, str) and add_salt:
320 param = self.add_salt(param)
321 return param
322
323 def params_to_array(self, param: Union[str, List[int]], add_salt: bool = True) -> List[int]:
324 """
325 获取输入参数的哈希数组 (Get the hash array of the input parameter).
326
327 Args:
328 param (Union[str, List[int]]): 输入参数 (Input parameter).
329 add_salt (bool): 是否添加盐值 (Whether to add salt).
330
331 Returns:
332 List[int]: 哈希数组 (Hash array).
333 """
334 processed_param = self.process_param(param, add_salt)
335 return self.sm3_to_array(processed_param)
336
337 def transform_bytes(self, bytes_list: List[int]) -> str:
338 """
339 对输入的字节列表进行加密/解密操作,返回处理后的字符串 (Encrypt/decrypt the input byte list and return the processed string).
340
341 Args:
342 bytes_list (List[int]): 输入的字节列表 (Input byte list).
343
344 Returns:
345 str: 处理后的字符串 (Processed string).
346 """
347 # 将字节列表转换为字符字符串
348 bytes_str = StringProcessor.to_char_str(bytes_list)
349 result_str = []
350 index_b = self.big_array[1]
351 initial_value = 0
352 # `value_e` is populated at the end of each loop iteration (see
353 # below) and consumed at the top of the NEXT iteration's `else`
354 # branch (index > 0). The index==0 branch doesn't read it, so
355 # static analysis can't tell that it's always defined before
356 # first use. Pre-declare 0 here to silence F821 without changing
357 # runtime behaviour — the 0 is overwritten on iteration 0 before
358 # any `else` branch runs.
359 value_e = 0
360
361 for index, char in enumerate(bytes_str):
362 if index == 0:
363 initial_value = self.big_array[index_b]
364 sum_initial = index_b + initial_value
365
366 self.big_array[1] = initial_value
367 self.big_array[index_b] = index_b
368 else:
369 sum_initial = initial_value + value_e
370
371 char_value = ord(char)
372 sum_initial %= len(self.big_array)
373 value_f = self.big_array[sum_initial]
374 encrypted_char = char_value ^ value_f
375 result_str.append(chr(encrypted_char))
376
377 # 交换数组元素
378 value_e = self.big_array[(index + 2) % len(self.big_array)]
379 sum_initial = (index_b + value_e) % len(self.big_array)
380 initial_value = self.big_array[sum_initial]
381 self.big_array[sum_initial] = self.big_array[(index + 2) % len(self.big_array)]
382 self.big_array[(index + 2) % len(self.big_array)] = initial_value
383 index_b = sum_initial
384
385 return "".join(result_str)
386
387 def base64_encode(self, input_string: str, selected_alphabet: int = 0) -> str:
388 """
389 使用自定义字符表对输入字符串进行 Base64 编码 (Encode the input string using a custom Base64 alphabet).
390
391 Args:
392 input_string (str): 输入字符串 (Input string).
393 selected_alphabet (int): 选择的自定义 Base64 字符表索引 (Selected custom Base64 alphabet index).
394
395 Returns:
396 str: 编码后的字符串 (Encoded string).
397 """
398
399 # 将输入字符串转换为ASCII码的二进制形式
400 binary_string = "".join(["{:08b}".format(ord(char)) for char in input_string])
401
402 # 补全二进制字符串使其长度为6的倍数
403 padding_length = (6 - len(binary_string) % 6) % 6
404 binary_string += "0" * padding_length
405
406 # 将二进制字符串分割为6位一组
407 base64_indices = [int(binary_string[i : i + 6], 2) for i in range(0, len(binary_string), 6)]
408
409 # 根据自定义字符表生成输出字符串
410 output_string = "".join(
411 [self.base64_alphabet[selected_alphabet][index] for index in base64_indices]
412 )
413
414 # 添加等号填充,使符合 Base64 编码规范
415 output_string += "=" * (padding_length // 2)
416
417 return output_string
418
419 def abogus_encode(self, abogus_bytes_str: str, selected_alphabet: int) -> str:
420 """
421 对输入的字节字符串进行自定义 Base64 编码,并添加位移和填充 (Encode the input byte string using a custom Base64 alphabet, and add shifts and padding).
422
423 Args:
424 abogus_bytes_str (str): 输入的字节字符串 (Input byte string).
425 selected_alphabet (int): 选择的自定义 Base64 字符表索引 (Selected custom Base64 alphabet index).
426
427 Returns:
428 str: 编码后的字符串 (Encoded string).
429 """
430 abogus = []
431
432 for i in range(0, len(abogus_bytes_str), 3):
433 if i + 2 < len(abogus_bytes_str):
434 n = (
435 (ord(abogus_bytes_str[i]) << 16)
436 | (ord(abogus_bytes_str[i + 1]) << 8)
437 | ord(abogus_bytes_str[i + 2])
438 )
439 elif i + 1 < len(abogus_bytes_str):
440 n = (ord(abogus_bytes_str[i]) << 16) | (ord(abogus_bytes_str[i + 1]) << 8)
441 else:
442 n = ord(abogus_bytes_str[i]) << 16
443
444 for j, k in zip(range(18, -1, -6), (0xFC0000, 0x03F000, 0x0FC0, 0x3F)):
445 if j == 6 and i + 1 >= len(abogus_bytes_str):
446 break
447 if j == 0 and i + 2 >= len(abogus_bytes_str):
448 break
449 abogus.append(self.base64_alphabet[selected_alphabet][(n & k) >> j])
450
451 abogus.append("=" * ((4 - len(abogus) % 4) % 4))
452 return "".join(abogus)
453
454 @staticmethod
455 def rc4_encrypt(key: bytes, plaintext: str) -> bytes:
456 """
457 使用 RC4 算法加密数据 (Encrypt data using the RC4 algorithm).
458
459 Args:
460 key (bytes): 加密密钥 (Encryption key).
461 plaintext (str): 明文数据 (Plaintext data).
462
463 Returns:
464 bytes: 加密后的数据 (Encrypted data).
465 """
466 S = list(range(256))
467 j = 0
468 for i in range(256):
469 j = (j + S[i] + key[i % len(key)]) % 256
470 S[i], S[j] = S[j], S[i]
471
472 i = j = 0
473 ciphertext = []
474 for char in plaintext:
475 i = (i + 1) % 256
476 j = (j + S[i]) % 256
477 S[i], S[j] = S[j], S[i]
478 K = S[(S[i] + S[j]) % 256]
479 ciphertext.append(ord(char) ^ K)
480
481 return bytes(ciphertext)
482
483
484 class BrowserFingerprintGenerator:
485 """
486 BrowserFingerprintGenerator 用于生成模拟的浏览器指纹信息,用于在不同浏览器环境中进行测试和数据采集。
487
488 类属性:
489 browsers (Dict[str, Callable[[], str]]): 浏览器类型和生成浏览器指纹的映射关系。
490
491 方法:
492 generate_fingerprint(browser_type="Edge"):
493 根据指定的浏览器类型生成浏览器指纹。
494
495 generate_chrome_fingerprint():
496 生成 Chrome 浏览器指纹。
497
498 generate_firefox_fingerprint():
499 生成 Firefox 浏览器指纹。
500
501 generate_safari_fingerprint():
502 生成 Safari 浏览器指纹。
503
504 generate_edge_fingerprint():
505 生成 Edge 浏览器指纹。
506
507 _generate_fingerprint(platform="Win32"):
508 根据给定的参数生成浏览器指纹字符串。
509
510 使用示例:
511 ```python
512 chrome_fp = BrowserFingerprintGenerator.generate_fingerprint("Chrome")
513 print(chrome_fp)
514 ```
515 """
516
517 @classmethod
518 def generate_fingerprint(cls, browser_type: str = "Edge") -> str:
519 """
520 根据指定的浏览器类型生成浏览器指纹。 (Generate a browser fingerprint based on the specified browser type.)
521
522 Args:
523 browser_type (str): 浏览器类型 (Browser type).
524
525 Returns:
526 str: 生成的浏览器指纹字符串 (Generated browser fingerprint string).
527 """
528 cls.browsers: Dict[str, Callable[[], str]] = {
529 "Chrome": cls.generate_chrome_fingerprint,
530 "Firefox": cls.generate_firefox_fingerprint,
531 "Safari": cls.generate_safari_fingerprint,
532 "Edge": cls.generate_edge_fingerprint,
533 }
534 return cls.browsers.get(browser_type, cls.generate_chrome_fingerprint)()
535
536 @classmethod
537 def generate_chrome_fingerprint(cls) -> str:
538 return cls._generate_fingerprint(platform="Win32")
539
540 @classmethod
541 def generate_firefox_fingerprint(cls) -> str:
542 return cls._generate_fingerprint(platform="Win32")
543
544 @classmethod
545 def generate_safari_fingerprint(cls) -> str:
546 return cls._generate_fingerprint(platform="MacIntel")
547
548 @classmethod
549 def generate_edge_fingerprint(cls) -> str:
550 return cls._generate_fingerprint(platform="Win32")
551
552 @staticmethod
553 def _generate_fingerprint(platform: str) -> str:
554 """
555 根据给定的参数生成浏览器指纹字符串。 (Generate a browser fingerprint string based on the given parameters.)
556
557 Args:
558 platform (str): 操作系统平台 (Operating system platform).
559
560 Returns:
561 str: 生成的浏览器指纹字符串 (Generated browser fingerprint string).
562 """
563 inner_width = random.randint(1024, 1920)
564 inner_height = random.randint(768, 1080)
565 outer_width = inner_width + random.randint(24, 32)
566 outer_height = inner_height + random.randint(75, 90)
567 screen_x = 0
568 screen_y = random.choice([0, 30])
569 size_width = random.randint(1024, 1920)
570 size_height = random.randint(768, 1080)
571 avail_width = random.randint(1280, 1920)
572 avail_height = random.randint(800, 1080)
573
574 fingerprint = (
575 f"{inner_width}|{inner_height}|{outer_width}|{outer_height}|"
576 f"{screen_x}|{screen_y}|0|0|{size_width}|{size_height}|"
577 f"{avail_width}|{avail_height}|{inner_width}|{inner_height}|24|24|{platform}"
578 )
579 return fingerprint
580
581
582 class ABogus:
583 """
584 ABogus 类用于生成 ABogus 参数。
585
586 类属性:
587 array1 (List[int]): 加密请求体 (Encrypted request body).
588 array2 (List[int]): 加密请求头 (Encrypted request header).
589 array3 (List[int]): 加密 UA (Encrypted User-Agent).
590 aid (int): AID 值 (AID value).
591 pageId (int): 页面 ID (Page ID).
592 salt (str): 加密盐值 (Encryption salt).
593 options (List[int]): 请求选项 (Request options).
594 ua_key (bytes): UA 加密密钥 (UA encryption key).
595 character (str): 自定义 Base64 字符表 (Custom Base64 alphabet).
596 character2 (str): 自定义 Base64 字符表 (Custom Base64 alphabet).
597 character_list (List[str]): 自定义 Base64 字符表列表 (List of custom Base64 alphabets).
598 crypto_utility (CryptoUtility): 加密工具类 (Encryption utility).
599 user_agent (str): 自定义 UA (Custom User-Agent).
600 browser_fp (str): 浏览器指纹 (Browser fingerprint).
601 sort_index (List[int]): 排序索引 (Sort index).
602 sort_index_2 (List[int]): 排序索引 (Sort index).
603
604 说明:
605 options 参数用于指定请求的类型,GET 请求使用 [0, 1, 8],POST 请求使用 [0, 1, 14]。14兼容8,POST同样可以编码params,故写死。
606 (The options parameter is used to specify the type of request. GET requests use [0, 1, 8], and POST requests use [0, 1, 14]. 14 is compatible with 8, and POST can also encode params, so it is hardcoded.)
607
608 方法:
609 encode_data(data: str, alphabet_index: int = 0) -> str:
610 使用指定的字符表对数据进行 Base64 编码 (Encode the data using the specified Base64 alphabet).
611
612 generate_abogus(params: str, request: str = "") -> str:
613 生成 ABogus 参数 (Generate the ABogus parameter).
614
615 使用示例:
616 ```python
617 # 生成 ABogus 参数,置空使用默认 UA 和浏览器指纹
618 abogus = ABogus(user_agent="xxx", fp="xxx")
619 abogus_param = abogus.generate_abogus("device_platform=webapp&aid=6383&channel=channel_pc_web&aweme_id=7380308675841297704……省略……")
620 print(abogus_param[1])
621 ```
622 """
623
624 def __init__(
625 self,
626 fp: str = "",
627 user_agent: str = "",
628 options: List[int] = [0, 1, 14],
629 ):
630 self.aid = 6383
631 self.pageId = 0 # 1.0.1.19 -> 6241
632 self.salt = "cus" # 1.0.1.19 -> 加密盐 # dhzx
633 self.boe = False
634 self.ddrt = 8.5
635 self.ic = 8.5
636 self.paths = [
637 "^/webcast/",
638 "^/aweme/v1/",
639 "^/aweme/v2/",
640 "/v1/message/send",
641 "^/live/",
642 "^/captcha/",
643 "^/ecom/",
644 ]
645 self.array1 = [] # 加密请求体
646 self.array2 = [] # 加密请求头 为空
647 self.array3 = [] # 加密UA
648 self.options = options # GET [0, 1, 8] POST [0, 1, 14]
649 self.ua_key = b"\x00\x01\x0e" # ua加密key
650
651 self.character = "Dkdpgh2ZmsQB80/MfvV36XI1R45-WUAlEixNLwoqYTOPuzKFjJnry79HbGcaStCe"
652 self.character2 = "ckdp1h4ZKsUB80/Mfvw36XIgR25+WQAlEi7NLboqYTOPuzmFjJnryx9HVGDaStCe"
653 self.character_list = [self.character, self.character2] # 自定义base64字符表
654
655 self.crypto_utility = CryptoUtility(self.salt, self.character_list) # 加密工具类
656
657 self.user_agent = (
658 user_agent
659 if user_agent is not None and user_agent != ""
660 else "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36 Edg/130.0.0.0"
661 ) # 自定义ua,为空则设置一个默认ua
662
663 self.browser_fp = (
664 fp
665 if fp is not None and fp != ""
666 else BrowserFingerprintGenerator.generate_fingerprint("Edge")
667 ) # 自定义浏览器指纹,为空则生成Edge指纹
668
669 # fmt: off
670 self.sort_index = [
671 18, 20, 52, 26, 30, 34, 58, 38, 40, 53, 42, 21, 27, 54, 55, 31, 35, 57, 39, 41, 43, 22, 28,
672 32, 60, 36, 23, 29, 33, 37, 44, 45, 59, 46, 47, 48, 49, 50, 24, 25, 65, 66, 70, 71
673 ]
674 self.sort_index_2 = [
675 18, 20, 26, 30, 34, 38, 40, 42, 21, 27, 31, 35, 39, 41, 43, 22, 28, 32, 36, 23, 29, 33, 37,
676 44, 45, 46, 47, 48, 49, 50, 24, 25, 52, 53, 54, 55, 57, 58, 59, 60, 65, 66, 70, 71
677 ]
678 # fmt: on
679
680 def encode_data(self, data: str, alphabet_index: int = 0) -> str:
681 """
682 使用指定的字符表对数据进行 Base64 编码 (Encode the data using the specified Base64 alphabet).
683
684 Args:
685 data (str): 输入数据 (Input data).
686 alphabet_index (int): 自定义字符表索引 (Custom alphabet index).
687
688 Returns:
689 str: 编码后的数据 (Encoded data).
690 """
691 return self.crypto_utility.abogus_encode(data, alphabet_index)
692
693 def generate_abogus(self, params: str, body: str = "") -> tuple:
694 """
695 生成 abogus 参数 (Generate the ABogus parameter).
696
697 Args:
698 params (str): 请求参数 (Request parameters).
699 body (str): 请求体,GET接口则为空 (Request body, empty for GET interfaces).
700
701 Returns:
702 tuple: params 生成的 abogus 参数 和 ua (ABogus parameter generated by params and ua).
703 """
704 ab_dir = {
705 8: 3, # 固定
706 15: {
707 "aid": self.aid,
708 "pageId": self.pageId,
709 "boe": self.boe,
710 "ddrt": self.ddrt,
711 "paths": self.paths,
712 "track": {"mode": 0, "delay": 300, "paths": []},
713 "dump": True,
714 "rpU": "",
715 },
716 18: 44,
717 19: [1, 0, 1, 0, 1],
718 66: 0, # 固定
719 69: 0, # 固定
720 70: 0, # 固定
721 71: 0, # 固定
722 }
723
724 # 开始加密时间
725 start_encryption = int(time.time() * 1000)
726
727 # params参数加盐加密
728 array1 = self.crypto_utility.params_to_array(self.crypto_utility.params_to_array(params))
729 array2 = self.crypto_utility.params_to_array(self.crypto_utility.params_to_array(body))
730 array3 = self.crypto_utility.params_to_array(
731 self.crypto_utility.base64_encode(
732 StringProcessor.to_ord_str(
733 self.crypto_utility.rc4_encrypt(self.ua_key, self.user_agent)
734 ),
735 1,
736 ),
737 add_salt=False,
738 )
739
740 # 结束加密时间
741 end_encryption = int(time.time() * 1000)
742
743 # 插入加密开始时间
744 ab_dir[20] = (start_encryption >> 24) & 255
745 ab_dir[21] = (start_encryption >> 16) & 255
746 ab_dir[22] = (start_encryption >> 8) & 255
747 ab_dir[23] = start_encryption & 255
748 ab_dir[24] = int(start_encryption / 256 / 256 / 256 / 256) >> 0
749 ab_dir[25] = int(start_encryption / 256 / 256 / 256 / 256 / 256) >> 0
750
751 # 插入请求头配置
752 ab_dir[26] = (self.options[0] >> 24) & 255
753 ab_dir[27] = (self.options[0] >> 16) & 255
754 ab_dir[28] = (self.options[0] >> 8) & 255
755 ab_dir[29] = self.options[0] & 255
756
757 # 插入请求方法
758 ab_dir[30] = int(self.options[1] / 256) & 255
759 ab_dir[31] = (self.options[1] % 256) & 255
760 ab_dir[32] = (self.options[1] >> 24) & 255
761 ab_dir[33] = (self.options[1] >> 16) & 255
762
763 # 插入请求头加密
764 ab_dir[34] = (self.options[2] >> 24) & 255
765 ab_dir[35] = (self.options[2] >> 16) & 255
766 ab_dir[36] = (self.options[2] >> 8) & 255
767 ab_dir[37] = self.options[2] & 255
768
769 # 插入请求体加密
770 ab_dir[38] = array1[21]
771 ab_dir[39] = array1[22]
772 # 插入body加密
773 ab_dir[40] = array2[21]
774 ab_dir[41] = array2[22]
775 # 插入ua加密
776 ab_dir[42] = array3[23]
777 ab_dir[43] = array3[24]
778
779 # 插入加密结束时间
780 ab_dir[44] = (end_encryption >> 24) & 255
781 ab_dir[45] = (end_encryption >> 16) & 255
782 ab_dir[46] = (end_encryption >> 8) & 255
783 ab_dir[47] = end_encryption & 255
784 ab_dir[48] = ab_dir[8]
785 ab_dir[49] = int(end_encryption / 256 / 256 / 256 / 256) >> 0
786 ab_dir[50] = int(end_encryption / 256 / 256 / 256 / 256 / 256) >> 0
787
788 # 插入固定值
789 ab_dir[51] = (self.pageId >> 24) & 255
790 ab_dir[52] = (self.pageId >> 16) & 255
791 ab_dir[53] = (self.pageId >> 8) & 255
792 ab_dir[54] = self.pageId & 255
793 ab_dir[55] = self.pageId
794 ab_dir[56] = self.aid
795 ab_dir[57] = self.aid & 255
796 ab_dir[58] = (self.aid >> 8) & 255
797 ab_dir[59] = (self.aid >> 16) & 255
798 ab_dir[60] = (self.aid >> 24) & 255
799
800 # 插入浏览器指纹
801 ab_dir[64] = len(self.browser_fp)
802 ab_dir[65] = len(self.browser_fp)
803
804 # 获取 ab_dir 中 sort_index 的值
805 sorted_values = [ab_dir.get(i, 0) for i in self.sort_index]
806
807 # 将浏览器指纹转换为 ASCII 码列表
808 edge_fp_array = StringProcessor.to_char_array(self.browser_fp)
809
810 # 将浏览器指纹长度的低 8 位作为异或值
811 ab_xor = (len(self.browser_fp) & 255) >> 8 & 255
812
813 # 进行异或计算
814 for index in range(len(self.sort_index_2) - 1):
815 if index == 0:
816 ab_xor = ab_dir.get(self.sort_index_2[index], 0)
817 ab_xor ^= ab_dir.get(self.sort_index_2[index + 1], 0)
818
819 sorted_values.extend(edge_fp_array)
820 sorted_values.append(ab_xor)
821
822 abogus_bytes_str = (
823 StringProcessor.generate_random_bytes()
824 + self.crypto_utility.transform_bytes(sorted_values)
825 )
826
827 abogus = self.crypto_utility.abogus_encode(abogus_bytes_str, 0)
828 params = "%s&a_bogus=%s" % (params, abogus)
829 return (params, abogus, self.user_agent, body)
830
831
832 if __name__ == "__main__":
833 # 24/06/16 晚点开源自定义ua
834 # 24/07/08 支持自定义ua和浏览器指纹
835 # 24/11/15 完成1.0.1.19版本abogus算法,择日开源
836 # 25/03/05 修复POST请求参数加密错误,修补环境
837
838 user_agent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36 Edg/131.0.0.0"
839 chrome_fp = BrowserFingerprintGenerator.generate_fingerprint("Edge")
840 abogus = ABogus(user_agent=user_agent, fp=chrome_fp)
841
842 # GET
843 url = "https://www.douyin.com/aweme/v1/web/aweme/detail/?"
844 params = "device_platform=webapp&aid=6383&channel=channel_pc_web&sec_user_id=MS4wLjABAAAArDVBosPJF3eIWVEFp0szuJ-e1V_-rK0ieJeWwpE77E8&max_cursor=0&locate_query=false&show_live_replay_strategy=1&need_time_list=1&time_list_query=0&whale_cut_token=&cut_version=1&count=18&publish_video_strategy_type=2&from_user_page=1&update_version_code=170400&pc_client_type=1&pc_libra_divert=Windows&support_h265=1&support_dash=0&version_code=290100&version_name=29.1.0&cookie_enabled=true&screen_width=1920&screen_height=1080&browser_language=zh-CN&browser_platform=Win32&browser_name=Edge&browser_version=131.0.0.0&browser_online=true&engine_name=Blink&engine_version=131.0.0.0&os_name=Windows&os_version=10&cpu_core_num=12&device_memory=8&platform=PC&downlink=10&effective_type=4g&round_trip_time=50"
845 body = ""
846 print(url + abogus.generate_abogus(params=params, body=body)[0])
847
848 # POST
849 url = "https://www.douyin.com/aweme/v2/web/aweme/stats/?"
850 params = "device_platform=webapp&aid=6383&channel=channel_pc_web&pc_client_type=1&pc_libra_divert=Windows&update_version_code=170400&support_h265=1&support_dash=0&version_code=170400&version_name=17.4.0&cookie_enabled=true&screen_width=1920&screen_height=1080&browser_language=zh-CN&browser_platform=Win32&browser_name=Edge&browser_version=131.0.0.0&browser_online=true&engine_name=Blink&engine_version=131.0.0.0&os_name=Windows&os_version=10&cpu_core_num=12&device_memory=8&platform=PC&downlink=10&effective_type=4g&round_trip_time=50"
851 body = "aweme_type=0&item_id=7467485482314763572&play_delta=1&source=0"
852 print(url + abogus.generate_abogus(params=params, body=body)[0])
853
854 # # 测试生成100个abogus参数 和 100个指纹所需时间
855 # start = time.time()
856 # for _ in range(100):
857 # abogus.generate_abogus(params=params, body=body)
858 # end = time.time()
859 # print("生成100个abogus参数和指纹所需时间:", end - start) # 生成100个abogus参数和指纹所需时间: 2.203000783920288
860
861 # start = time.time()
862 # for _ in range(100):
863 # BrowserFingerprintGenerator.generate_fingerprint("Chrome")
864 # end = time.time()
865 # print("生成100个指纹所需时间:", end - start) # 生成100个指纹所需时间: 0.00400090217590332
866
866 lines PYTHON