├── 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 |
5 |
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 |
--------------------------------------------------------------------------------