12189108 commited on
Commit
d5e73d8
·
1 Parent(s): 2317a66
Files changed (4) hide show
  1. Dockerfile +48 -0
  2. main/main.py +164 -0
  3. main/recaptcha_bypass.py +179 -0
  4. requirements.txt +9 -0
Dockerfile ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM python:3.11-slim-bullseye as builder
2
+
3
+ # Build dummy packages to skip installing them and their dependencies
4
+ RUN apt-get update \
5
+ && apt-get install -y --no-install-recommends equivs \
6
+ && equivs-control libgl1-mesa-dri \
7
+ && printf 'Section: misc\nPriority: optional\nStandards-Version: 3.9.2\nPackage: libgl1-mesa-dri\nVersion: 99.0.0\nDescription: Dummy package for libgl1-mesa-dri\n' >> libgl1-mesa-dri \
8
+ && equivs-build libgl1-mesa-dri \
9
+ && mv libgl1-mesa-dri_*.deb /libgl1-mesa-dri.deb \
10
+ && equivs-control adwaita-icon-theme \
11
+ && printf 'Section: misc\nPriority: optional\nStandards-Version: 3.9.2\nPackage: adwaita-icon-theme\nVersion: 99.0.0\nDescription: Dummy package for adwaita-icon-theme\n' >> adwaita-icon-theme \
12
+ && equivs-build adwaita-icon-theme \
13
+ && mv adwaita-icon-theme_*.deb /adwaita-icon-theme.deb
14
+
15
+ FROM python:3.11-slim-bullseye
16
+ COPY --from=builder /*.deb /
17
+ WORKDIR /app
18
+ RUN echo "deb http://deb.debian.org/debian/ unstable main contrib non-free" >> /etc/apt/sources.list
19
+ RUN apt update
20
+ RUN apt install -y python3 python3-pip libgl1-mesa-glx wget
21
+
22
+
23
+ RUN dpkg -i /libgl1-mesa-dri.deb \
24
+ && dpkg -i /adwaita-icon-theme.deb \
25
+ # Install dependencies
26
+ && apt-get update \
27
+ && apt-get install -y --no-install-recommends xvfb dumb-init \
28
+ procps curl vim xauth \
29
+ # Remove temporary files and hardware decoding libraries
30
+ && rm -rf /var/lib/apt/lists/* \
31
+ && rm -f /usr/lib/x86_64-linux-gnu/libmfxhw* \
32
+ && rm -f /usr/lib/x86_64-linux-gnu/mfx/* \
33
+ && useradd --home-dir /app --shell /bin/sh foxer \
34
+ && chown -R foxer:foxer .
35
+
36
+ COPY requirements.txt .
37
+ RUN pip install -r requirements.txt \
38
+ # Remove temporary files
39
+ && rm -rf /root/.cache
40
+ USER foxer
41
+
42
+ RUN mkdir -p "/app/.config/Ultralytics"
43
+
44
+ COPY main .
45
+ EXPOSE 7860
46
+
47
+ ENTRYPOINT ["/usr/bin/dumb-init", "--"]
48
+ CMD ["/usr/local/bin/python","-u", "/app/main.py"]
main/main.py ADDED
@@ -0,0 +1,164 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import base64
2
+ import hashlib
3
+ import os
4
+ import uuid
5
+ import shutil
6
+ from flask import Flask, jsonify, request
7
+ from flask_limiter import Limiter
8
+ from flask_limiter.util import get_remote_address
9
+ import recaptcha_bypass
10
+
11
+ app = Flask(__name__)
12
+
13
+ limiter = Limiter(
14
+ app=app,
15
+ key_func=get_remote_address, # 使用 IP 地址作为键
16
+ default_limits=["10 per minute", "50 per hour"], # 设置默认的请求限制
17
+ )
18
+ shared_limiter = limiter.shared_limit("20/minute", scope="memory")
19
+
20
+
21
+ def get_token():
22
+ default_token = "init_token"
23
+ if os.path.exists("token"):
24
+ return open("token", "r").read().strip()
25
+ return default_token
26
+
27
+
28
+ def check_request(required_data, data):
29
+ token = get_token()
30
+ if not data or any(key not in data for key in required_data):
31
+ print("Error:Invalid Request Data\n" + str(data))
32
+ return False
33
+ if data["token"] != token:
34
+ print("Error:Invalid Token\n" + str(data))
35
+ return False
36
+ return True
37
+
38
+
39
+ def check_task_token(required_data, data):
40
+ if not check_request(required_data, data):
41
+ return False
42
+ if not os.path.isdir(data["task_token"]):
43
+ print("Task token err\n" + data)
44
+ return False
45
+ return True
46
+
47
+
48
+ def generate_uuid():
49
+ unique_identifier = str(uuid.uuid4())
50
+ hashed_string = hashlib.sha256(unique_identifier.encode()).hexdigest()
51
+ return hashed_string
52
+
53
+
54
+ def base64_to_file(task_token, name, base64_data):
55
+ file = open(f"{task_token}/{name}.png", "wb+")
56
+ file.write(base64.b64decode(base64_data))
57
+ file.close()
58
+
59
+
60
+ @app.errorhandler(429)
61
+ def rate_limit_exceeded(e):
62
+ print(get_remote_address())
63
+ return jsonify(msg="Too many request"), 429
64
+
65
+
66
+ @app.errorhandler(405)
67
+ def method_not_allowed(e):
68
+ print(get_remote_address())
69
+ return jsonify(msg="Unauthorized Request"), 405
70
+
71
+
72
+ @app.route("/", methods=["GET"])
73
+ @shared_limiter
74
+ def index():
75
+ return jsonify(status_code=200)
76
+
77
+
78
+ @app.route("/update/token", methods=["POST"])
79
+ @shared_limiter
80
+ def update_token():
81
+ require_data = ["token", "new_token"]
82
+ data = request.get_json(force=True, silent=True)
83
+ if not check_request(require_data, data):
84
+ return jsonify(msg="Unauthorized Request"), 403
85
+ token = open("token", "w+")
86
+ token.write(data["new_token"])
87
+ token.close()
88
+ return jsonify(msg="Token updated successfully", success=True)
89
+
90
+
91
+ @app.route("/task-token", methods=["POST"])
92
+ @shared_limiter
93
+ def get_task_token():
94
+ data = request.get_json(force=True, silent=True)
95
+ required_data = ["token"]
96
+ if not check_request(required_data, data):
97
+ return jsonify(msg="Unauthorized Request"), 403
98
+ # 任务唯一凭证
99
+ task_token = generate_uuid()
100
+ os.makedirs(task_token)
101
+ return jsonify(msg="ok", task_token=task_token, success=True)
102
+
103
+
104
+ @app.route("/revoke-token", methods=["POST"])
105
+ @shared_limiter
106
+ def revoke_token():
107
+ data = request.get_json(force=True, silent=True)
108
+ required_data = ["token", "task_token"]
109
+ if not check_task_token(required_data, data):
110
+ return jsonify(msg="Unauthorized Request"), 403
111
+ if len(data["task_token"]) != 64 or not data["task_token"].isalnum():
112
+ return jsonify(msg="Unauthorized Request"), 403
113
+ shutil.rmtree(data["task_token"].lower())
114
+ return jsonify(msg="success", success=True)
115
+
116
+
117
+ @app.route("/upload", methods=["POST"])
118
+ @shared_limiter
119
+ def init_solver():
120
+ data = request.get_json(force=True, silent=True)
121
+ required_data = ["token", "img_name", "img_base", "task_token"]
122
+ if not check_task_token(required_data, data):
123
+ return jsonify(msg="Unauthorized Request"), 403
124
+ task_token = data["task_token"]
125
+ img = data["img_base"]
126
+ img_name = data["img_name"]
127
+ base64_to_file(task_token, img_name, img)
128
+ return jsonify(msg="file upload success", task_token=task_token, success=True)
129
+
130
+
131
+ @app.route("/get_answers", methods=["POST"])
132
+ @shared_limiter
133
+ def get_answer():
134
+ data = request.get_json(force=True, silent=True)
135
+ required_data = ["token", "target_num", "task_token"]
136
+ if not check_task_token(required_data, data):
137
+ return jsonify(msg="Unauthorized Request"), 403
138
+ answers = recaptcha_bypass.get_answers(data["target_num"], data["task_token"])
139
+ return jsonify(msg="success", success=True, answers=answers)
140
+
141
+
142
+ @app.route("/paste", methods=["POST"])
143
+ @shared_limiter
144
+ def paste():
145
+ data = request.get_json(force=True, silent=True)
146
+ required_data = ["token", "task_token", "new", "loc"]
147
+ if not check_task_token(required_data, data):
148
+ return jsonify(msg="Unauthorized Request"), 403
149
+ recaptcha_bypass.paste_new_img_on_main_img(data["new"], data["loc"], data["task_token"])
150
+ return jsonify(msg="success", success=True)
151
+
152
+
153
+ @app.route("/get_answers4", methods=["POST"])
154
+ @shared_limiter
155
+ def get_answers4():
156
+ data = request.get_json(force=True, silent=True)
157
+ required_data = ["token", "task_token", "target_num"]
158
+ if not check_task_token(required_data, data):
159
+ return jsonify(msg="Unauthorized Request"), 403
160
+ answers = recaptcha_bypass.get_answers_4(data["target_num"], data["task_token"])
161
+ return jsonify(msg="success", success=True, answers=answers)
162
+
163
+
164
+ app.run(port=7860)
main/recaptcha_bypass.py ADDED
@@ -0,0 +1,179 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import shutil
2
+ import requests
3
+ import re
4
+ from ultralytics import YOLO
5
+ import numpy as np
6
+ from PIL import Image
7
+ import cv2
8
+
9
+ model = YOLO("yolov8x.pt")
10
+
11
+
12
+ def get_answers(target_num, task_token):
13
+ image = Image.open(f"{task_token}/0.png")
14
+ image = np.asarray(image)
15
+ result = model.predict(image)
16
+
17
+ target_index = []
18
+ count = 0
19
+ for num in result[0].boxes.cls:
20
+ if num == target_num:
21
+ target_index.append(count)
22
+ count += 1
23
+
24
+ answers = []
25
+ boxes = result[0].boxes.data
26
+ count = 0
27
+ for i in target_index:
28
+ target_box = boxes[i]
29
+ p1, p2 = (int(target_box[0]), int(target_box[1])
30
+ ), (int(target_box[2]), int(target_box[3]))
31
+ x1, y1 = p1
32
+ x2, y2 = p2
33
+
34
+ xc = (x1 + x2) / 2
35
+ yc = (y1 + y2) / 2
36
+
37
+ if xc < 100 and yc < 100:
38
+ answers.append(1)
39
+ if 200 > xc > 100 > yc:
40
+ answers.append(2)
41
+ if 200 < xc < 300 and yc < 100:
42
+ answers.append(3)
43
+
44
+ if xc < 100 < yc < 200:
45
+ answers.append(4)
46
+ if 100 < xc < 200 and 100 < yc < 200:
47
+ answers.append(5)
48
+ if 300 > xc > 200 > yc > 100:
49
+ answers.append(6)
50
+
51
+ if xc < 100 and 200 < yc < 300:
52
+ answers.append(7)
53
+ if 100 < xc < 200 < yc < 300:
54
+ answers.append(8)
55
+ if 200 < xc < 300 and 200 < yc < 300:
56
+ answers.append(9)
57
+
58
+ count += 1
59
+
60
+ return list(set(answers))
61
+
62
+
63
+ def paste_new_img_on_main_img(new, loc, task_token):
64
+ main_img = Image.open(f"{task_token}/0.png")
65
+ new_img = Image.open(f"{task_token}/{new}.png")
66
+ paste = np.copy(main_img)
67
+ if loc == 1:
68
+ paste[0:100, 0:100] = new_img
69
+ elif loc == 2:
70
+ paste[0:100, 100:200] = new_img
71
+ elif loc == 3:
72
+ paste[0:100, 200:300] = new_img
73
+ elif loc == 4:
74
+ paste[100:200, 0:100] = new_img
75
+ elif loc == 5:
76
+ paste[100:200, 100:200] = new_img
77
+ elif loc == 6:
78
+ paste[100:200, 200:300] = new_img
79
+ elif loc == 7:
80
+ paste[200:300, 0:100] = new_img
81
+ elif loc == 8:
82
+ paste[200:300, 100:200] = new_img
83
+ elif loc == 9:
84
+ paste[200:300, 200:300] = new_img
85
+
86
+ paste = cv2.cvtColor(paste, cv2.COLOR_RGB2BGR)
87
+ cv2.imwrite(f'{task_token}/0.png', paste)
88
+
89
+
90
+ def get_occupied_cells(vertices):
91
+ occupied_cells = set()
92
+ rows, cols = zip(*[((v - 1) // 4, (v - 1) % 4) for v in vertices])
93
+
94
+ for i in range(min(rows), max(rows) + 1):
95
+ for j in range(min(cols), max(cols) + 1):
96
+ occupied_cells.add(4 * i + j + 1)
97
+
98
+ return sorted(list(occupied_cells))
99
+
100
+
101
+ def get_answers_4(target_num, task_token):
102
+ image = Image.open(f"{task_token}/0.png")
103
+ image = np.asarray(image)
104
+ result = model.predict(image)
105
+ boxes = result[0].boxes.data
106
+ target_index = []
107
+ count = 0
108
+ for num in result[0].boxes.cls:
109
+ if num == target_num:
110
+ target_index.append(count)
111
+ count += 1
112
+
113
+ for i in target_index:
114
+ target_box = boxes[i]
115
+ p1, p2 = (int(target_box[0]), int(target_box[1])
116
+ ), (int(target_box[2]), int(target_box[3]))
117
+ x1, y1 = p1
118
+ x2, y2 = p2
119
+
120
+ answers = []
121
+ count = 0
122
+ for i in target_index:
123
+ target_box = boxes[i]
124
+ p1, p2 = (int(target_box[0]), int(target_box[1])
125
+ ), (int(target_box[2]), int(target_box[3]))
126
+ x1, y1 = p1
127
+ x4, y4 = p2
128
+ x2 = x4
129
+ y2 = y1
130
+ x3 = x1
131
+ y3 = y4
132
+ xys = [x1, y1, x2, y2, x3, y3, x4, y4]
133
+
134
+ four_cells = []
135
+ for i in range(4):
136
+ x = xys[i * 2]
137
+ y = xys[(i * 2) + 1]
138
+
139
+ if x < 112.5 and y < 112.5:
140
+ four_cells.append(1)
141
+ if 225 > x > 112.5 > y:
142
+ four_cells.append(2)
143
+ if 225 < x < 337.5 and y < 112.5:
144
+ four_cells.append(3)
145
+ if 337.5 < x <= 450 and y < 112.5:
146
+ four_cells.append(4)
147
+
148
+ if x < 112.5 < y < 225:
149
+ four_cells.append(5)
150
+ if 112.5 < x < 225 and 112.5 < y < 225:
151
+ four_cells.append(6)
152
+ if 337.5 > x > 225 > y > 112.5:
153
+ four_cells.append(7)
154
+ if 337.5 < x <= 450 and 112.5 < y < 225:
155
+ four_cells.append(8)
156
+
157
+ if x < 112.5 and 225 < y < 337.5:
158
+ four_cells.append(9)
159
+ if 112.5 < x < 225 < y < 337.5:
160
+ four_cells.append(10)
161
+ if 225 < x < 337.5 and 225 < y < 337.5:
162
+ four_cells.append(11)
163
+ if 450 >= x > 337.5 > y > 225:
164
+ four_cells.append(12)
165
+
166
+ if x < 112.5 and 337.5 < y <= 450:
167
+ four_cells.append(13)
168
+ if 112.5 < x < 225 and 337.5 < y <= 450:
169
+ four_cells.append(14)
170
+ if 225 < x < 337.5 < y <= 450:
171
+ four_cells.append(15)
172
+ if 337.5 < x <= 450 and 337.5 < y <= 450:
173
+ four_cells.append(16)
174
+ answer = get_occupied_cells(four_cells)
175
+ count += 1
176
+ for ans in answer:
177
+ answers.append(ans)
178
+ answers = sorted(list(answers))
179
+ return list(set(answers))
requirements.txt ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ numpy==1.25.2
2
+ opencv_python==4.8.0.76
3
+ Pillow==10.0.0
4
+ Requests==2.31.0
5
+ selenium==4.11.2
6
+ ultralytics==8.0.153
7
+ webdriver_manager==4.0.0
8
+ Flask
9
+ Flask-Limiter