├── README.md ├── solver.py ├── updated.py └── utils └── solver.py /README.md: -------------------------------------------------------------------------------- 1 | captcha solver for app api, solving slide captcha ~ [`solver.py`](./solver.py) 2 | selling tiktok 3d captcha solver 100% accuracy, dm https://t.me/xtekky 3 | 4 | image 5 | image 6 | 7 | https://user-images.githubusercontent.com/98614666/182261794-853e2d7e-8b9c-4609-97aa-6e7e189d413b.mp4 8 | -------------------------------------------------------------------------------- /solver.py: -------------------------------------------------------------------------------- 1 | import requests 2 | import base64 3 | import time 4 | import random 5 | 6 | from urllib.parse import urlencode 7 | from utils.solver import PuzzleSolver 8 | 9 | class Solver: 10 | def __init__(self, did, iid): 11 | self.__host = "verification-va.tiktokv.com" 12 | self.__device_id = did 13 | self.__install_id = iid 14 | self.__cookies = "" 15 | self.__client = requests.Session() 16 | 17 | def __params(self): 18 | params = { 19 | "lang": "en", 20 | "app_name": "musical_ly", 21 | "h5_sdk_version": "2.26.17", 22 | "sdk_version": "1.3.3-rc.7.3-bugfix", 23 | "iid": self.__install_id, 24 | "did": self.__device_id, 25 | "device_id": self.__device_id, 26 | "ch": "beta", 27 | "aid": "1233", 28 | "os_type": "0", 29 | "mode": "", 30 | "tmp": f"{int(time.time())}{random.randint(111, 999)}", 31 | "platform": "app", 32 | "webdriver": "false", 33 | "verify_host": f"https://{self.__host}/", 34 | "locale": "en", 35 | "channel": "beta", 36 | "app_key": "", 37 | "vc": "18.2.15", 38 | "app_verison": "18.2.15", 39 | "session_id": "", 40 | "region": ["va", "US"], 41 | "use_native_report": "0", 42 | "use_jsb_request": "1", 43 | "orientation": "1", 44 | "resolution": ["900*1552", "900*1600"], 45 | "os_version": ["25", "7.1.2"], 46 | "device_brand": "samsung", 47 | "device_model": "SM-G973N", 48 | "os_name": "Android", 49 | "challenge_code": "1105", 50 | "app_version": "18.2.15", 51 | "subtype": "", 52 | } 53 | 54 | return urlencode(params) 55 | 56 | def __headers(self) -> dict: 57 | 58 | headers = { 59 | "passport-sdk-version": "19", 60 | "sdk-version": "2", 61 | "x-ss-req-ticket": f"{int(time.time())}{random.randint(111, 999)}", 62 | "cookie": self.__cookies, 63 | "content-type": "application/json; charset=utf-8", 64 | "host": self.__host, 65 | "connection": "Keep-Alive", 66 | "user-agent": "okhttp/3.10.0.1", 67 | } 68 | 69 | return headers 70 | 71 | def __get_challenge(self) -> dict: 72 | 73 | params = self.__params() 74 | 75 | req = self.__client.get( 76 | url = ( 77 | "https://" 78 | + self.__host 79 | + "/captcha/get?" 80 | + params 81 | ), 82 | headers = self.__headers() 83 | ) 84 | 85 | return req.json() 86 | 87 | def __solve_captcha(self, url_1: str, url_2: str) -> dict: 88 | puzzle = base64.b64encode( 89 | self.__client.get( 90 | url_1, 91 | ).content 92 | ) 93 | piece = base64.b64encode( 94 | self.__client.get( 95 | url_2, 96 | ).content 97 | ) 98 | 99 | solver = PuzzleSolver(puzzle, piece) 100 | maxloc = solver.get_position() 101 | randlength = round( 102 | random.random() * (100 - 50) + 50 103 | ) 104 | time.sleep(1) # don't remove delay or it will fail 105 | return { 106 | "maxloc": maxloc, 107 | "randlenght": randlength 108 | } 109 | 110 | def __post_captcha(self, solve: dict) -> dict: 111 | params = self.__params() 112 | 113 | body = { 114 | "modified_img_width": 552, 115 | "id": solve["id"], 116 | "mode": "slide", 117 | "reply": list( 118 | { 119 | "relative_time": i * solve["randlenght"], 120 | "x": round( 121 | solve["maxloc"] / (solve["randlenght"] / (i + 1)) 122 | ), 123 | "y": solve["tip"], 124 | } 125 | for i in range( 126 | solve["randlenght"] 127 | ) 128 | ), 129 | } 130 | 131 | headers = self.__headers() 132 | 133 | req = self.__client.post( 134 | url = ( 135 | "https://" 136 | + self.__host 137 | + "/captcha/verify?" 138 | + params 139 | ), 140 | headers = headers.update( 141 | { 142 | "content-type": "application/json" 143 | } 144 | ), 145 | json = body 146 | ) 147 | 148 | return req.json() 149 | 150 | def solve_captcha(self): 151 | __captcha_challenge = self.__get_challenge() 152 | 153 | __captcha_id = __captcha_challenge["data"]["id"] 154 | __tip_y = __captcha_challenge["data"]["question"]["tip_y"] 155 | 156 | solve = self.__solve_captcha( 157 | __captcha_challenge["data"]["question"]["url1"], 158 | __captcha_challenge["data"]["question"]["url2"], 159 | ) 160 | 161 | solve.update( 162 | { 163 | "id": __captcha_id, 164 | "tip": __tip_y 165 | } 166 | ) 167 | 168 | return self.__post_captcha(solve) 169 | 170 | 171 | 172 | if __name__ == "__main__": 173 | __device_id = "" 174 | __install_id = "" 175 | 176 | print( 177 | Solver( 178 | did = __device_id, 179 | iid = __install_id 180 | ).solve_captcha() 181 | ) 182 | -------------------------------------------------------------------------------- /updated.py: -------------------------------------------------------------------------------- 1 | import requests, time, requests, json, base64, random 2 | from urllib.parse import urlencode 3 | 4 | import cv2 5 | import base64 6 | import numpy as np 7 | 8 | class PuzzleSolver: 9 | def __init__(self, base64puzzle, base64piece): 10 | self.puzzle = base64puzzle 11 | self.piece = base64piece 12 | 13 | def get_position(self): 14 | puzzle = self.__background_preprocessing() 15 | piece = self.__piece_preprocessing() 16 | matched = cv2.matchTemplate( 17 | puzzle, 18 | piece, 19 | cv2.TM_CCOEFF_NORMED 20 | ) 21 | min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(matched) 22 | return max_loc[0] 23 | 24 | def __background_preprocessing(self): 25 | img = self.__img_to_grayscale(self.piece) 26 | background = self.__sobel_operator(img) 27 | return background 28 | 29 | def __piece_preprocessing(self): 30 | img = self.__img_to_grayscale(self.puzzle) 31 | template = self.__sobel_operator(img) 32 | return template 33 | 34 | def __sobel_operator(self, img): 35 | scale = 1 36 | delta = 0 37 | ddepth = cv2.CV_16S 38 | 39 | img = cv2.GaussianBlur(img, (3, 3), 0) 40 | gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) 41 | grad_x = cv2.Sobel( 42 | gray, 43 | ddepth, 44 | 1, 45 | 0, 46 | ksize=3, 47 | scale=scale, 48 | delta=delta, 49 | borderType=cv2.BORDER_DEFAULT, 50 | ) 51 | grad_y = cv2.Sobel( 52 | gray, 53 | ddepth, 54 | 0, 55 | 1, 56 | ksize=3, 57 | scale=scale, 58 | delta=delta, 59 | borderType=cv2.BORDER_DEFAULT, 60 | ) 61 | abs_grad_x = cv2.convertScaleAbs(grad_x) 62 | abs_grad_y = cv2.convertScaleAbs(grad_y) 63 | grad = cv2.addWeighted(abs_grad_x, 0.5, abs_grad_y, 0.5, 0) 64 | 65 | return grad 66 | 67 | def __img_to_grayscale(self, img): 68 | return cv2.imdecode( 69 | self.__string_to_image(img), 70 | cv2.IMREAD_COLOR 71 | ) 72 | 73 | def __string_to_image(self, base64_string): 74 | 75 | return np.frombuffer( 76 | base64.b64decode(base64_string), 77 | dtype='uint8' 78 | ) 79 | 80 | class Captcha: 81 | def __init__(self, params_dict: dict, detail: str): 82 | self.domain = 'rc-verification-i18n' 83 | self.params = params_dict 84 | self.detail = detail 85 | 86 | self.__client = requests.Session() 87 | 88 | def __params(self): 89 | params = { 90 | 'lang': 'fr', 91 | 'app_name': 'musical_ly', 92 | 'h5_sdk_version': '2.31.2', 93 | 'h5_sdk_use_type': 'cdn', 94 | 'sdk_version': '2.3.3.i18n', 95 | 'iid': self.params['iid'], 96 | 'did': self.params['device_id'], 97 | 'device_id': self.params['device_id'], 98 | 'ch': 'googleplay', 99 | 'aid': '1233', 100 | 'os_type': '0', 101 | 'mode': '', 102 | 'tmp': str(int(time.time())), 103 | 'platform': 'app', 104 | 'webdriver': 'false', 105 | 'verify_host': 'https://rc-verification-i18n.tiktokv.com/', 106 | 'locale': 'fr', 107 | 'channel': 'googleplay', 108 | 'app_key': '', 109 | 'vc': '31.5.3', 110 | 'app_version': '31.5.3', 111 | 'session_id': '', 112 | 'region': 'in', 113 | 'use_native_report': '1', 114 | 'use_jsb_request': '1', 115 | 'orientation': '2', 116 | 'resolution': self.params['resolution'], 117 | 'os_version': self.params['os_version'], 118 | 'device_brand': self.params['device_brand'], 119 | 'device_model': self.params['device_type'], 120 | 'os_name': 'Android', 121 | 'version_code': '3153', 122 | 'device_type': self.params['device_type'], 123 | 'device_platform': 'Android', 124 | 'type': 'verify', 125 | 'detail': self.detail, 126 | 'server_sdk_env': json.dumps({'idc':'useast2a','region':'I18N','server_type':'passport'}, separators=(',', ':')), 127 | 'subtype': 'slide', 128 | 'challenge_code': '3058', 129 | 'triggered_region': 'in', 130 | 'device_redirect_info': '' 131 | } 132 | 133 | return urlencode(params) 134 | 135 | def __headers(self): 136 | return { 137 | 'accept-encoding': 'gzip', 138 | 'x-tt-request-tag': 'n=1;t=0', 139 | 'x-vc-bdturing-sdk-version': '2.3.3.i18n', 140 | 'x-ss-req-ticket': str(int(time.time() * 1000)), 141 | 'x-tt-bypass-dp': '1', 142 | 'content-type': 'application/json; charset=utf-8', 143 | 'x-tt-dm-status': 'login=0;ct=0;rt=7', 144 | 'x-tt-store-region': 'dz', 145 | 'x-tt-store-region-src': 'did', 146 | 'user-agent': 'com.zhiliaoapp.musically/2023105030 (Linux; U; Android 12; fr_FR; SM-G988N; Build/NRD90M;tt-ok/3.12.13.4-tiktok)', 147 | 'host': 'rc-verification-i18n.tiktokv.com', 148 | 'connection': 'Keep-Alive' 149 | } 150 | 151 | def __get_challenge(self): 152 | params = self.__params() 153 | 154 | return self.__client.get('https://%s.tiktokv.com/captcha/get?%s' % (self.domain, params), 155 | headers = self.__headers()).json() 156 | 157 | def __solve_captcha(self, url_1: str, url_2: str): 158 | puzzle = base64.b64encode(self.__client.get(url_1).content) 159 | piece = base64.b64encode(self.__client.get(url_2).content) 160 | solver = PuzzleSolver(puzzle, piece) 161 | 162 | time.sleep(1) 163 | return { 164 | 'maxloc' : solver.get_position(), 165 | 'randlenght': round(random.random() * (100 - 50) + 5) 166 | } 167 | 168 | def __post_captcha(self, solve: dict) -> dict: 169 | body = { 170 | 'modified_img_width': 552, 171 | 'id' : solve['id'], 172 | 'mode' : 'slide', 173 | 'reply' : list({ 174 | 'relative_time': i * solve['randlenght'], 175 | 'x': round(solve['maxloc'] / (solve['randlenght'] / (i + 1))), 176 | 'y': solve['tip']} for i in range(solve['randlenght'])) 177 | } 178 | 179 | return self.__client.post('https://%s.tiktokv.com/captcha/verify?%s' % (self.domain, self.__params()), 180 | headers = self.__headers(), json = body).json() 181 | 182 | def solve_captcha(self): 183 | __challenge = self.__get_challenge() 184 | __captcha_id = __challenge['data']['id'] 185 | __tip_y = __challenge['data']['question']['tip_y'] 186 | 187 | solve = self.__solve_captcha( 188 | __challenge['data']['question']['url1'], 189 | __challenge['data']['question']['url2']) 190 | 191 | solve.update({ 192 | 'id' : __captcha_id, 193 | 'tip': __tip_y}) 194 | 195 | return self.__post_captcha(solve) 196 | 197 | if __name__ == '__main__': 198 | device = {'Cookies': {'install_id': '7284359982429800197', 'store-country-code': 'pa', 'store-country-code-src': 'did', 'store-idc': 'maliva', 'ttreq': '1$cb3fec43d1e03b8a752f6f12ec90eafeb5c38e9e'}, 'Device_Info': {'ab_version': '31.5.1', 'ac': 'wifi', 'ac2': 'wifi', 'aid': '1233', 'app_language': 'en', 'app_name': 'musical_ly', 'app_type': 'normal', 'build_number': '31.5.1', 'carrier_region': 'PA', 'cdid': '710569ab-29a3-4e3b-9d4c-0725d1dffa24', 'channel': 'googleplay', 'device_brand': 'PANASONIC_WA0EC', 'device_id': '7284359569500014085', 'device_platform': 'android', 'device_type': 'panasonic', 'dpi': '240', 'host_abi': 'armeabi-v7a', 'iid': '7284359982429800197', 'language': 'en', 'locale': 'en', 'manifest_version_code': '2023105010', 'mcc_mnc': '7142', 'okhttp_version': '4.2.152.12-tiktok', 'op_region': 'PA', 'openudid': '25b89230286e26cc', 'os': 'android', 'os_api': '31', 'os_version': '12', 'passport-sdk-version': '19', 'region': 'PA', 'resolution': '720*1280', 'ssmix': 'a', 'support_webview': '1', 'sys_region': 'PA', 'timezone_name': 'America/Panama', 'timezone_offset': -18000, 'uoo': '0', 'update_version_code': '2023105010', 'use_store_region_cookie': '1', 'version_code': '310501', 'version_name': '31.5.1'}, 'Ri_Report': True, 'Seed_Algorithm': 6, 'Seed_Token': 'MDGnGJ7SrXMAIj91yWozx4nsnuZyTnF8AzR4ovcLT0iHKkvRF2Y8HFV5FRqxNQKm2egOIADEwBCTvw7OUKfxVVORwzDaVuMAf9hnmhRGuDRr+cgcFrqz9SpEMHmL7tbMA/E=', 'is_activated': 'success', 'secDeviceIdToken': 'ApwszCTXxL-5QyiIiw3qIMW_7'} 199 | captcha = Captcha(device['Device_Info'], 'dzLcZ0u2MHJowbiidnJ5cHQRP3YkMbobGtH1kA2*MBK1Fm*bIsNzbrwXvYN6hklSYvSA2-QAgk6JxxFbUa1G2bPWS-dQ9QOZIG-rkhlrE06Ox0cJ*6cUrZV8wnQfh5TYRMMN1SAMOPKdXPsSmN6GSZWBsLnq68*7Yo9jWx4-NEu6HlL2o-w8spMl*1oeQsnX7qcAPtYqK8FZFLThN0JIJ4uv5DxnBbm9vvQiL36dT1db-Wqw9-89BIopYUqhk29rWnYA5gqkFihs3hg1xctCchUIoc8x0VoZVFVpQeEJMzivuymsVlAYA4MXSIaRlU*0txaaG7fqVEOtpUnKK8byysJrExOuVe4hgok7vMLVSTNiQuKu4WOCLopO9HeYXQCr3-7vroicxSnFxDfRQF1LxNX7MfPCXFSaiomy87zKn24PveRgTYOVSZ7LDcFf') 200 | 201 | print(captcha.solve_captcha()) 202 | -------------------------------------------------------------------------------- /utils/solver.py: -------------------------------------------------------------------------------- 1 | # This is how easy it is to solve TikTok captchas 2 | 3 | import cv2 4 | import base64 5 | import numpy as np 6 | 7 | 8 | class PuzzleSolver: 9 | def __init__(self, base64puzzle, base64piece): 10 | self.puzzle = base64puzzle 11 | self.piece = base64piece 12 | 13 | def get_position(self): 14 | puzzle = self.__background_preprocessing() 15 | piece = self.__piece_preprocessing() 16 | matched = cv2.matchTemplate( 17 | puzzle, 18 | piece, 19 | cv2.TM_CCOEFF_NORMED 20 | ) 21 | min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(matched) 22 | return max_loc[0] 23 | 24 | def __background_preprocessing(self): 25 | img = self.__img_to_grayscale(self.piece) 26 | background = self.__sobel_operator(img) 27 | return background 28 | 29 | def __piece_preprocessing(self): 30 | img = self.__img_to_grayscale(self.puzzle) 31 | template = self.__sobel_operator(img) 32 | return template 33 | 34 | def __sobel_operator(self, img): 35 | scale = 1 36 | delta = 0 37 | ddepth = cv2.CV_16S 38 | 39 | img = cv2.GaussianBlur(img, (3, 3), 0) 40 | gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) 41 | grad_x = cv2.Sobel( 42 | gray, 43 | ddepth, 44 | 1, 45 | 0, 46 | ksize=3, 47 | scale=scale, 48 | delta=delta, 49 | borderType=cv2.BORDER_DEFAULT, 50 | ) 51 | grad_y = cv2.Sobel( 52 | gray, 53 | ddepth, 54 | 0, 55 | 1, 56 | ksize=3, 57 | scale=scale, 58 | delta=delta, 59 | borderType=cv2.BORDER_DEFAULT, 60 | ) 61 | abs_grad_x = cv2.convertScaleAbs(grad_x) 62 | abs_grad_y = cv2.convertScaleAbs(grad_y) 63 | grad = cv2.addWeighted(abs_grad_x, 0.5, abs_grad_y, 0.5, 0) 64 | 65 | return grad 66 | 67 | def __img_to_grayscale(self, img): 68 | return cv2.imdecode( 69 | self.__string_to_image(img), 70 | cv2.IMREAD_COLOR 71 | ) 72 | 73 | def __string_to_image(self, base64_string): 74 | 75 | return np.frombuffer( 76 | base64.b64decode(base64_string), 77 | dtype="uint8" 78 | ) 79 | --------------------------------------------------------------------------------