sign.py 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242
  1. import random
  2. import hashlib
  3. import urllib.parse
  4. import binascii
  5. import json
  6. import re
  7. import requests
  8. from Crypto.Cipher import AES
  9. from Crypto.Util.Padding import pad
  10. from Crypto.PublicKey.RSA import construct
  11. from Crypto.Cipher import PKCS1_v1_5
  12. from geeked.slide import SlideSolver
  13. from geeked.gobang import GobangSolver
  14. from geeked.icon import IconSolver
  15. class LotParser:
  16. def __init__(self):
  17. self.mapping = {"(n[19:21]+n[19:21])+.+(n[23:26])+.+(n[18:23])":'n[6:9]'}
  18. self.lot = []
  19. self.lot_res = []
  20. for k, v in self.mapping.items():
  21. self.lot = self._parse(k)
  22. self.lot_res = self._parse(v)
  23. @staticmethod
  24. def _parse_slice(s):
  25. return [int(x) for x in s.split(':')]
  26. @staticmethod
  27. def _extract(part):
  28. return re.search(r'\[(.*?)\]', part).group(1)
  29. def _parse(self, s):
  30. parts = s.split('+.+')
  31. parsed = []
  32. for part in parts:
  33. if '+' in part:
  34. subs = part.split('+')
  35. parsed_subs = [self._parse_slice(self._extract(sub)) for sub in subs]
  36. parsed.append(parsed_subs)
  37. else:
  38. parsed.append([self._parse_slice(self._extract(part))])
  39. return parsed
  40. @staticmethod
  41. def _build_str(parsed, num):
  42. result = []
  43. for p in parsed:
  44. current = []
  45. for s in p:
  46. start = s[0]
  47. end = s[1] + 1 if len(s) > 1 else start + 1
  48. current.append(num[start:end])
  49. result.append(''.join(current))
  50. return '.'.join(result)
  51. def get_dict(self, lot_number):
  52. i = self._build_str(self.lot, lot_number)
  53. r = self._build_str(self.lot_res, lot_number)
  54. parts = i.split('.')
  55. a = {}
  56. current = a
  57. for idx, part in enumerate(parts):
  58. if idx == len(parts) - 1:
  59. current[part] = r
  60. else:
  61. current[part] = current.get(part, {})
  62. current = current[part]
  63. return a
  64. lotParser = LotParser() # doesn't need to calculate the lot and lot_res every time, so were gonna cache it
  65. class Signer:
  66. encryptor_pubkey = construct((
  67. int("00C1E3934D1614465B33053E7F48EE4EC87B14B95EF88947713D25EECBFF7E74C7977D02DC1D9451F79DD5D1C10C29ACB6A9B4D6FB7D0A0279B6719E1772565F09AF627715919221AEF91899CAE08C0D686D748B20A3603BE2318CA6BC2B59706592A9219D0BF05C9F65023A21D2330807252AE0066D59CEEFA5F2748EA80BAB81".lower(),
  68. 16),
  69. int("10001", 16))
  70. )
  71. @staticmethod
  72. def rand_uid():
  73. result = ''
  74. for _ in range(4):
  75. result += hex(int(65536 * (1 + random.random())))[2:].zfill(4)[-4:]
  76. return result
  77. @staticmethod
  78. def encrypt_symmetrical_1(o_text, random_str):
  79. key = random_str.encode('utf-8')
  80. iv = b'0000000000000000'
  81. cipher = AES.new(key, AES.MODE_CBC, iv)
  82. encrypted_bytes = cipher.encrypt(pad(o_text.encode('utf-8'), AES.block_size))
  83. return encrypted_bytes
  84. @staticmethod
  85. def encrypt_asymmetric_1(message: str) -> str:
  86. message_bytes = message.encode('utf-8')
  87. cipher = PKCS1_v1_5.new(Signer.encryptor_pubkey)
  88. encrypted_bytes = cipher.encrypt(message_bytes)
  89. encrypted_hex = binascii.hexlify(encrypted_bytes).decode('utf-8')
  90. return encrypted_hex
  91. """ ill implement it when needed
  92. function encrypt_asymmetric_2(input, key) {
  93. void 0 === key && (key = '9a4ea935b2576f37516d9b29cd8d8cc9bffe548ba6853253ba20f4ba44fba8c9e97a398882769aa0dd1e3e1b5601429287303880ca17bd244ed73bf702a68fc7');
  94. var moreargs = 2 < arguments['length'] && arguments[2] !== undefined ? arguments[2] : 1;
  95. var encryptor = new _ᖉᖉᕾᖉ;
  96. input = sortaGlobals['hexToArray'](sortaGlobals['parseUtf8StringToHex'](input)), 128 < key['length'] && (key = key['substr'](key['length'] - 128));
  97. var keyLeft = key['substr'](0, 64), keyRest = key['substr'](64);
  98. key = encryptor['createPoint'](keyLeft, keyRest);
  99. var initCypher = encryptor['initEncipher'](key);
  100. encryptor['encryptBlock'](input);
  101. var end = sortaGlobals['arrayToHex'](input);
  102. emptyArray = new Array(32);
  103. return encryptor['doFinal'](emptyArray), emptyArray = sortaGlobals['arrayToHex'](emptyArray), 0 === moreargs ? initCypher + end + emptyArray : initCypher + emptyArray + end
  104. }
  105. """
  106. @staticmethod
  107. def encrypt_w(raw_input, pt) -> str:
  108. if not pt or '0' == pt:
  109. return urllib.parse.quote_plus(raw_input)
  110. random_uid = Signer.rand_uid()
  111. enc_key: str
  112. enc_input: bytes
  113. if pt == "1":
  114. enc_key = Signer.encrypt_asymmetric_1(random_uid)
  115. enc_input = Signer.encrypt_symmetrical_1(raw_input, random_uid)
  116. else: # elif pt == "2" # there's either "1" or "2"
  117. raise NotImplementedError("This type of encryption is not implemented yet. Create an issue")
  118. return binascii.hexlify(enc_input).decode() + enc_key
  119. @staticmethod
  120. def generate_pow(lot_number_pow, captcha_id_pow, hash_func, hash_version, bits, date, empty) -> dict:
  121. """Generate the pow_msg & pow_sign | translated directly from the .js"""
  122. pow_string = f"{hash_version}|{bits}|{hash_func}|{date}|{captcha_id_pow}|{lot_number_pow}|{empty}|"
  123. if hash_func == 'md5':
  124. hash_algo = hashlib.md5
  125. elif hash_func == 'sha1':
  126. hash_algo = hashlib.sha1
  127. elif hash_func == 'sha256':
  128. hash_algo = hashlib.sha256
  129. else:
  130. raise ValueError(f"Unsupported hash function: {hash_func}. Create an issue")
  131. hex_zeros = bits // 4
  132. extra_zero_bits = bits % 4
  133. prefix = '0' * hex_zeros
  134. max_hex_char = ''
  135. if extra_zero_bits == 1:
  136. max_hex_char = '7'
  137. elif extra_zero_bits == 2:
  138. max_hex_char = '3'
  139. elif extra_zero_bits == 3:
  140. max_hex_char = '1'
  141. while True:
  142. h = Signer.rand_uid()
  143. combined = pow_string + h
  144. hashed_value = hash_algo(combined.encode('utf-8')).hexdigest()
  145. if hashed_value.startswith(prefix):
  146. if extra_zero_bits == 0 or hashed_value[hex_zeros] <= max_hex_char:
  147. return {
  148. 'pow_msg': combined,
  149. 'pow_sign': hashed_value
  150. }
  151. @staticmethod
  152. def generate_w(data: dict, captcha_id: str, risk_type: str):
  153. lot_number = data['lot_number']
  154. pow_detail = data['pow_detail']
  155. abo = {"uONi":"4vVu"}
  156. base = abo | {
  157. **Signer.generate_pow(lot_number, captcha_id, pow_detail['hashfunc'], pow_detail['version'],
  158. pow_detail['bits'], pow_detail['datetime'], ""),
  159. **lotParser.get_dict(lot_number),
  160. "biht": "1426265548", # static
  161. "device_id": "", # why is this empty!!
  162. "em": { # save to have this static (see em.js)
  163. "cp": 0, # checkCallPhantom
  164. "ek": "11", # checkErrorKeys "11" as value is also fine
  165. "nt": 0, # checkNightmare
  166. "ph": 0, # checkPhantom
  167. "sc": 0, # checkSeleniumMarker
  168. "si": 0, # checkScriptFn
  169. "wd": 1, # checkWebDriver
  170. },
  171. "gee_guard": {
  172. "roe": { # "3" = no | "1" = yes
  173. "auh": "3", # HEADCHR_UA | regex(/HeadlessChrome/) in UserAgent
  174. "aup": "3", # PHANTOM_UA | regex(/PhantomJS/) in UserAgent
  175. "cdc": "3", # CDC | cdc check
  176. "egp": "3", # PHANTOM_LANGUAGE | language header !== undefined
  177. "res": "3", # SELENIUM_DRIVER | 35 selenium checks 💀
  178. "rew": "3", # WEBDRIVER | webDriver check
  179. "sep": "3", # PHANTOM_PROPERTIES | phantomJS check
  180. "snh": "3", # HEADCHR_PERMISSIONS | checks browser version etc.
  181. }
  182. },
  183. "ep": "123", # static
  184. "geetest": "captcha", # static
  185. "lang": "zh", # static
  186. "lot_number": lot_number,
  187. }
  188. if risk_type in ("ai", "invisible"):
  189. pass
  190. elif risk_type == "slide":
  191. left = SlideSolver(
  192. requests.get(f"https://static.geetest.com/{data['slice']}", timeout=10, verify=False).content,
  193. requests.get(f"https://static.geetest.com/{data['bg']}", timeout=10, verify=False).content
  194. ).find_puzzle_piece_position() + random.uniform(0, .5)
  195. base |= {
  196. "passtime": random.randint(600, 1200), # time in ms it took to solve
  197. "setLeft": left,
  198. "userresponse": left / 1.0059466666666665 + 2 # 1.0059466666666665 = .8876 * 340 / 300
  199. }
  200. elif risk_type in ("winlinze", "gobang"):
  201. base |= {
  202. "userresponse": GobangSolver(data["ques"]).find_four_in_line()
  203. }
  204. elif risk_type in 'icon':
  205. base |= {
  206. "passtime": random.randint(600, 1200), # time in ms it took to solve
  207. "userresponse": IconSolver(data["imgs"], data["ques"]).find_icon_position()
  208. }
  209. else:
  210. raise NotImplementedError(f"This type ({risk_type}) of captcha is not implemented yet.")
  211. return Signer.encrypt_w(json.dumps(base), data["pt"])