zhou12189108 commited on
Commit
d0e5bf9
1 Parent(s): 4bdc62b

Upload 3 files

Browse files
Files changed (3) hide show
  1. Dockerfile +48 -0
  2. api.py +77 -0
  3. hcaptcha_solver.py +149 -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 libglib2.0-dev sudo libpci-dev
21
+ RUN pip install playwright hcaptcha_challenger requests loguru flask Flask-Limiter
22
+ RUN dpkg -i /libgl1-mesa-dri.deb \
23
+ && dpkg -i /adwaita-icon-theme.deb \
24
+ # Install dependencies
25
+ && apt-get update \
26
+ && apt-get install -y --no-install-recommends xvfb dumb-init \
27
+ procps curl vim xauth \
28
+ # Remove temporary files and hardware decoding libraries
29
+ && rm -rf /var/lib/apt/lists/* \
30
+ && rm -f /usr/lib/x86_64-linux-gnu/libmfxhw* \
31
+ && rm -f /usr/lib/x86_64-linux-gnu/mfx/* \
32
+ && useradd --home-dir /app --shell /bin/sh foxer \
33
+ && chown -R foxer:foxer .
34
+
35
+ RUN rm -rf /root/.cache
36
+ RUN chmod 777 -R "/usr/local/lib/python3.11/"
37
+ RUN chmod 777 -R "/app/"
38
+ RUN playwright install firefox --with-deps
39
+
40
+ USER foxer
41
+ RUN playwright install firefox
42
+ COPY hcaptcha_solver.py .
43
+ COPY api.py .
44
+ EXPOSE 7860
45
+
46
+ ENTRYPOINT ["/usr/bin/dumb-init", "--"]
47
+ CMD ["/usr/local/bin/python","-u","/app/api.py"]
48
+
api.py ADDED
@@ -0,0 +1,77 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import asyncio
3
+ from flask import Flask, jsonify, request, logging as flog
4
+ from flask_limiter.util import get_remote_address
5
+ import hcaptcha_solver
6
+
7
+ app = Flask(__name__)
8
+
9
+
10
+ def get_ipaddr():
11
+ if request.access_route:
12
+ print(request.access_route[0])
13
+ return request.access_route[0]
14
+ else:
15
+ return request.remote_addr or '127.0.0.1'
16
+
17
+
18
+ handler = flog.default_handler
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
+ @app.errorhandler(429)
40
+ def rate_limit_exceeded(e):
41
+ print(get_remote_address())
42
+ return jsonify(msg="Too many request"), 429
43
+
44
+
45
+ @app.errorhandler(405)
46
+ def method_not_allowed(e):
47
+ print(get_remote_address())
48
+ return jsonify(msg="Unauthorized Request"), 405
49
+
50
+
51
+ @app.route("/", methods=["GET"])
52
+ def index():
53
+ return jsonify(status_code=200, ip=get_ipaddr())
54
+
55
+
56
+ @app.route("/update/token", methods=["POST"])
57
+ def update_token():
58
+ require_data = ["token", "new_token"]
59
+ data = request.get_json(force=True, silent=True)
60
+ if not check_request(require_data, data):
61
+ return jsonify(msg="Unauthorized Request"), 403
62
+ token = open("token", "w+")
63
+ token.write(data["new_token"])
64
+ token.close()
65
+ return jsonify(msg="Token updated successfully", success=True)
66
+
67
+
68
+ @app.route("/api/solve", methods=["POST"])
69
+ def solver_captcha():
70
+ require_data = ["token", "host", "site_key"]
71
+ data = request.get_json(force=True, silent=True)
72
+ if not check_request(require_data, data):
73
+ return jsonify(msg="Unauthorized Request"), 403
74
+ return asyncio.run(hcaptcha_solver.bytedance(data["host"], data["site_key"]))
75
+
76
+
77
+ app.run(host="0.0.0.0", port=7860)
hcaptcha_solver.py ADDED
@@ -0,0 +1,149 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ from pathlib import Path
3
+ import os
4
+ import requests
5
+ from playwright.async_api import BrowserContext as ASyncContext, async_playwright
6
+ import hcaptcha_challenger as solver
7
+ from hcaptcha_challenger.agents import AgentT, Malenia
8
+
9
+ # Init local-side of the ModelHub
10
+ solver.install(upgrade=True)
11
+
12
+ # Save dataset to current working directory
13
+ user_data_dir = Path(__file__).parent.joinpath("user_data_dir")
14
+ context_dir = user_data_dir.joinpath("context")
15
+ tmp_dir = Path(__file__).parent.joinpath("tmp_dir")
16
+
17
+
18
+ async def hit_challenge(context: ASyncContext, host, sitekey, times: int = 8):
19
+ await context.route('**/*', lambda route, request: route_continuation(route, request, host, sitekey))
20
+ page = context.pages[0]
21
+ agent = AgentT.from_page(page=page, tmp_dir=tmp_dir)
22
+ await page.goto(f"https://{host}")
23
+
24
+ await agent.handle_checkbox()
25
+
26
+ for pth in range(1, times):
27
+ result = await agent()
28
+ print(f">> {pth} - Challenge Result: {result}")
29
+ match result:
30
+ case agent.status.CHALLENGE_BACKCALL:
31
+ await page.wait_for_timeout(500)
32
+ fl = page.frame_locator(agent.HOOK_CHALLENGE)
33
+ await fl.locator("//div[@class='refresh button']").click()
34
+ case agent.status.CHALLENGE_SUCCESS:
35
+ rqdata = agent.cr.__dict__
36
+ print(os.system(f"rm -rf {tmp_dir}"))
37
+ print(os.system(f"rm -rf {user_data_dir}"))
38
+ return rqdata["generated_pass_UUID"]
39
+
40
+
41
+ async def route_continuation(route, request, host, sitekey):
42
+ # 检查请求的URL,只拦截特定网站的请求
43
+ if request.url == f"https://{host}/":
44
+ print("start to solve")
45
+ # 修改DNS解析结果
46
+ await route.fulfill(status=200,
47
+ body="""
48
+
49
+ <!DOCTYPE html>
50
+ <html lang="en">
51
+ <head>
52
+ <title>hCAPTCHA 演示</title>
53
+ <meta http-equiv="X-UA-Compatible" content="IE=edge">
54
+ <meta name="viewport" content="width=device-width, user-scalable=yes">
55
+ <script src="https://js.hcaptcha.com/1/api.js?reportapi=https%3A%2F%2Faccounts.hcaptcha.com&custom=False&pstissuer=https://pst-issuer.hcaptcha.com" type="text/javascript" async defer></script>
56
+ <script src="/static/js/b.js" type="text/javascript" async defer></script>
57
+ <style type="text/css">
58
+ html,body,div,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,big,cite,code,del,dfn,em,font,img,ins,kbd,q,s,samp,small,strike,strong,sub,sup,tt,var,dl,dt,dd,ol,ul,li,fieldset,form,label,legend,table,caption,tbody,tfoot,thead,tr,th,td{margin:0;padding:0;border:0;outline:0;font-family:-apple-system,helvetica,arial,sans-serif;font-size:14px;vertical-align:baseline}fieldset{border:1px solid #ebebeb;padding:16px}form label{display:block;line-height:29px}form ul li{margin-bottom:10px}input{font-family:-apple-system,helvetica,arial,sans-serif;font-size:14px}ul{list-style:none}.sample-form{width:335px;padding:16px}.hcaptcha-error{border:1px solid #dd4b39;padding:5px}.hcaptcha-error-message{color:#dd4b39;font-size:24px;padding:10px 0}.hcaptcha-success{margin:20px; font-size: 24px;}.smsg{color:green;}.wrap{overflow-wrap: anywhere;}.warning{padding: 10px 10px 0px 10px;}
59
+ code { font-family: 'Courier New', Courier, monospace; background-color: lightgrey;}
60
+ }
61
+ }
62
+ </style>
63
+ </head>
64
+ <body>
65
+ <div aria-hidden="true" class="warning"><code>?hl=XXX</code> 使用它来更改 hl JS API 参数。</div>
66
+ <div aria-hidden="true" class="warning"><code>?sitekey=XXX</code> 用它来更改站点密钥。</div>
67
+ <div aria-hidden="true" class="warning"><code>?secret=XXX</code> 用它来更改站点验证秘密。</div>
68
+ <div aria-hidden="true" class="warning"><code>?sitekey=XXX&secret=XXX</code> 用它来改变两者。</div>
69
+ <br><br>
70
+ <div class="sample-form">
71
+ <form id="hcaptcha-demo-form" method="POST">
72
+ <fieldset>
73
+ <legend>带 hCAPTCHA 的示例表单</legend>
74
+ <ul>
75
+ <li role="presentation">
76
+ <label for="ignored1" aria-hidden="true">表单字段(可选)</label>
77
+ <input class="textinput" id="ignored1" name="email" type="text" tabindex="-1" aria-hidden="true" aria-label="表单字段(可选)" aria-required="false">
78
+ </li>
79
+ <li role="presentation">
80
+ <div class>
81
+
82
+ <div id="hcaptcha-demo" class="h-captcha" data-sitekey="%%%%%%%%%%%" data-callback="onSuccess" data-expired-callback="onExpire"></div>
83
+ <script>
84
+ // success callback
85
+ var onSuccess = function(response) {
86
+ var errorDivs = document.getElementsByClassName("hcaptcha-error");
87
+ if (errorDivs.length) {
88
+ errorDivs[0].className = "";
89
+ }
90
+ var errorMsgs = document.getElementsByClassName("hcaptcha-error-message");
91
+ if (errorMsgs.length) {
92
+ errorMsgs[0].parentNode.removeChild(errorMsgs[0]);
93
+ }
94
+
95
+ var logEl = document.querySelector(".hcaptcha-success");
96
+ logEl.innerHTML = "挑战成功!"
97
+ };
98
+
99
+ var onExpire = function(response) {
100
+ var logEl = document.querySelector(".hcaptcha-success");
101
+ logEl.innerHTML = "令牌已过期。"
102
+ };
103
+ </script>
104
+
105
+ </div>
106
+ </li>
107
+ <li>
108
+ <input id="hcaptcha-demo-submit" type="submit" value="Submit">
109
+ </li>
110
+ </ul>
111
+ </fieldset>
112
+ </form>
113
+ <div class="hcaptcha-success smsg" aria-live="polite"></div>
114
+ </div>
115
+ </body>
116
+ <script type="text/javascript">
117
+ // beacon example
118
+ function addEventHandler(object,szEvent,cbCallback){
119
+ if(!!object.addEventListener){ // for modern browsers or IE9+
120
+ return object.addEventListener(szEvent,cbCallback);
121
+ }
122
+ if(!!object.attachEvent){ // for IE <=8
123
+ return object.attachEvent(szEvent,cbCallback);
124
+ }
125
+ };
126
+ // Ex: triggers pageview beacon
127
+ addEventHandler(window,'load',function(){b();});
128
+ // Ex: triggers event beacon without pageview
129
+ addEventHandler(window,'load',function(){b({"vt": "e", "ec": "test_cat", "ea": "test_action"});});
130
+ </script>
131
+ </html>
132
+ """.replace("%%%%%%%%%%%", sitekey))
133
+ else:
134
+ # 对于其他网站的请求,不做修改
135
+ await route.continue_()
136
+
137
+
138
+ async def bytedance(host, sitekey):
139
+ # playwright install firefox --with-deps
140
+ async with async_playwright() as p:
141
+ context = await p.firefox.launch_persistent_context(
142
+ user_data_dir=context_dir,
143
+ headless=True,
144
+ locale="en-US"
145
+ )
146
+ await Malenia.apply_stealth(context)
147
+ token = await hit_challenge(context, host, sitekey)
148
+ return token
149
+