免责声明(学习使用,不得恶意传播)
本软件仅供学习交流使用,不得用于任何商业用途。
使用者需自行承担所有风险和法律责任。
开发者不对因使用本软件而产生的任何后果负责。
请仔细阅读并同意以上条款后再使用本软件。
1、修改host文件
通常host文件在C:\Windows\System32\drivers\etc目录下
添加字段,保存即可
127.0.0.1 card.handturn.com
2、将软件放到你驯龙安装的目录

3、运行vip脚本后运行驯龙即可
运行vip.exe
允许访问
运行驯龙
账号密码(随便填写就行你输入1/1都可以)
4、使用结束后将脚本退出
桌面右下角
5、每次都要启动两个软件觉得麻烦的可以看这一步,不想弄得到这就可以了
第一种方式自己创建
新建文本
输入启动代码(注意:驯龙安装后程序名称可能不同,你需要将代码中的pong.exe替换为你驯龙的名称)
Option Explicit
Dim fso, scriptFolder, ws
Set fso = CreateObject("Scripting.FileSystemObject")
scriptFolder = fso.GetParentFolderName(WScript.ScriptFullName)
Set ws = CreateObject("WScript.Shell")
' 在脚本所在目录以隐藏窗口方式启动(不等待启动结束)
ws.Run """" & scriptFolder & "\vip.exe" & """", 0, False
ws.Run """" & scriptFolder & "\pong.exe" & """", 0, False
Set ws = Nothing
Set fso = Nothing

将新建的文件修改后缀为vbs
之后双击运行vbs脚本就可以了
第二种方式使用提供好的
这个简单点将提供的vbs和vip.exe放到驯龙目录,修改驯龙exe为pong.exe(懂得人可以自己改vbs,不懂得就直接用吧)
到这结束了,后面不用看了
最后,相信懂的人都知道原理了吧,他的校验都在本地,代码为
import tkinter as tk
from tkinter import messagebox
import threading
from http.server import HTTPServer, BaseHTTPRequestHandler
import json
import urllib.parse
import sys
import logging
import os
from datetime import datetime, timedelta
import pystray
from PIL import Image, ImageDraw
import psutil
import requests
import dns.resolver # pip install dnspython
# ------------------ 免责声明 ------------------
DISCLAIMER_TEXT = """
免责声明:
本软件仅供学习交流使用,不得用于任何商业用途。
使用者需自行承担所有风险和法律责任。
开发者不对因使用本软件而产生的任何后果负责。
请仔细阅读并同意以上条款后再使用本软件。
"""
# ------------------ 进程保护 ------------------
def terminate_existing_instances():
current_pid = os.getpid()
current_process = psutil.Process(current_pid)
current_exe = current_process.exe()
terminated_count = 0
for proc in psutil.process_iter(['pid', 'name', 'exe']):
try:
if proc.info['pid'] == current_pid:
continue
if proc.info['exe'] == current_exe:
proc.terminate()
proc.wait(timeout=3)
terminated_count += 1
logging.info(f"已终止已运行的实例 PID: {proc.info['pid']}")
except Exception:
pass
if terminated_count > 0:
logging.info(f"共终止了 {terminated_count} 个已运行的实例")
# ------------------ 日志 ------------------
def setup_logging():
if not os.path.exists('logs'):
os.makedirs('logs')
log_filename = f"logs/log_{datetime.now().strftime('%Y%m%d')}.log"
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s',
handlers=[
logging.FileHandler(log_filename, encoding='utf-8'),
logging.StreamHandler(sys.stdout)
]
)
return logging.getLogger(__name__)
def clean_old_logs():
logs_dir = 'logs'
if not os.path.exists(logs_dir):
return
current_date = datetime.now()
for filename in os.listdir(logs_dir):
if filename.startswith('log_') and filename.endswith('.log'):
try:
file_date_str = filename[4:12]
file_date = datetime.strptime(file_date_str, '%Y%m%d')
if current_date - file_date > timedelta(days=7):
os.remove(os.path.join(logs_dir, filename))
logging.info(f"已删除旧日志文件: {filename}")
except ValueError:
continue
# ------------------ 托盘 ------------------
def create_image():
width, height = 64, 64
image = Image.new('RGB', (width, height), (0, 123, 255))
dc = ImageDraw.Draw(image)
dc.rectangle((width//4, height//4, width*3//4, height*3//4), fill=(255, 255, 255))
return image
def on_quit(icon, item):
logging.info("用户通过系统托盘退出程序")
icon.stop()
os._exit(0)
# ------------------ Mock 数据 ------------------
# 定义响应数据
home_ads_data = {
"code": 200,
"data": [
{
"adSeqCode": "13",
"title": "恐龙岛论坛",
"imageUrl": "https://vip.123pan.cn/1816931221/%E9%A6%96%E9%A1%B5%E5%B9%BF%E5%91%8A%E4%BD%8D/konglongdao_link.png",
"linkUrl": "https://www.konglongdao.com",
"describ": "恐龙岛论坛友情链接"
},
{
"adSeqCode": "00",
"title": "安装教程进入",
"imageUrl": "http://public.handturn.com/sitapi/imgShow/imgs_app_home_ad/20250506/3iH775MH78SN1104a33U.png",
"linkUrl": "https://note.youdao.com/s/LmvZs5ie",
"describ": "驯龙安装教程请点击"
}
],
"success": 1
}
login_data = {
"code": 200,
"data": {
"account": "admin",
"nickname": "admin",
"accessToken": "o8Hxl237bw81589H3249Y2u135803v56T9SvN1T7",
"email": "admin@qq.com",
"userLevel": "NORMAL",
"vipExpire": "2099-11-11 00:00:00",
"shareUrl": "https://sitapi.fuckno1.com/dowloadLastApk?share=X26g6567010CqHO0H9R1M237F330mp0VRd2042c8",
"authManageItems": "",
"authItems": "",
"dinoWebMap": {}
},
"success": 1
}
version_data = {
"code": 200,
"data": {
"versionCode": "3.01.43",
"versionName": "正式版",
"versionContent": "1.恐龙岛4.0养龙存龙时自动退出问题修复;\n2.Theisle传送存龙问题修复。",
"downloadUrl": "http://public.handturn.com/sitapi/fileShow/file_app_version/20250925/9B33q0V0W2mDgyFwA494.exe",
"apkSize": 27505,
"isForce": True,
"hasUpdate": True,
"isIgnorable": False,
"remark": "",
"courseUrl": "https://note.youdao.com/s/ON0I4LYg",
"plugInJson": "[{\"name\":\"ocr\",\"url\":\"https://vip.123pan.cn/1816931221/11131128\"}]"
},
"success": 1
}
heartbeat_data = {
"code": 200,
"data": {
"kickOut": False,
"kickOutMes": "",
"donateTitle": "今日头条",
"donateWorld": "我的确好色,金色的周大生,银色的卡地亚,红色的人民币,黑色的银行卡。",
"isFree": False,
"effectiveTime": "2023-11-11 00:00:00",
"effectiveTimeStr": "2099-11-11 00:00:00",
"isExpire": False,
"wxPayUrl": "http://card.handturn.com/sitapi/fileShow/imgs_pay/20240118/x68x75kugjXqK588j48y.jpg",
"aliPayUrl": "http://card.handturn.com/sitapi/fileShow/imgs_pay/20240118/M37lY352Sbib505y728k.jpg"
},
"success": 1
}
# ------------------ DNS解析真实IP ------------------
REAL_DOMAIN = "card.handturn.com"
FALLBACK_IP = "45.248.9.152"
def resolve_real_ip(domain):
try:
resolver = dns.resolver.Resolver()
resolver.nameservers = ["8.8.8.8", "114.114.114.114"]
answers = resolver.resolve(domain, "A")
ip = answers[0].to_text()
logging.info(f"DNS解析成功: {domain} -> {ip}")
return ip
except Exception as e:
logging.error(f"DNS解析失败: {e},使用备用IP {FALLBACK_IP}")
return FALLBACK_IP
REAL_IP = resolve_real_ip(REAL_DOMAIN)
REAL_SERVER = f"http://{REAL_IP}"
logging.info(f"真实服务器解析: {REAL_SERVER}")
# ------------------ 请求处理 ------------------
class RequestHandler(BaseHTTPRequestHandler):
def log_message(self, format, *args):
logging.info("%s - - [%s] %s" %
(self.address_string(),
self.log_date_time_string(),
format % args))
def do_GET(self):
parsed_path = urllib.parse.urlparse(self.path)
path = parsed_path.path
query_params = urllib.parse.parse_qs(parsed_path.query)
logging.info(f"收到请求: {self.path} 来自 {self.client_address[0]}")
# === 本地 mock ===
if path == '/appapi/user/getHomeAds':
return self._send_json(home_ads_data)
elif path == '/appapi/user/login':
return self._send_json(login_data)
elif path == '/appapi/app/getLastVersion':
return self._send_json(version_data)
elif path == '/appapi/user/heartbeat':
return self._send_json(heartbeat_data)
# 其他走代理
self._proxy_request()
def do_POST(self):
if self.path == '/appapi/user/login':
return self._send_json(login_data)
self._proxy_request()
def _send_json(self, data):
self.send_response(200)
self.send_header('Content-type', 'application/json')
self.send_header('Access-Control-Allow-Origin', '*')
self.end_headers()
self.wfile.write(json.dumps(data, ensure_ascii=False).encode('utf-8'))
def _proxy_request(self):
if not REAL_SERVER:
self.send_response(502)
self.end_headers()
self.wfile.write("真实服务器解析失败".encode("utf-8"))
return
method = self.command
url = REAL_SERVER + self.path
headers = {k: v for k, v in self.headers.items()}
headers["Host"] = REAL_DOMAIN # 保持Host为原始域名
try:
if method == "GET":
resp = requests.get(url, headers=headers, timeout=10)
elif method == "POST":
length = int(self.headers.get('Content-Length', 0))
body = self.rfile.read(length) if length > 0 else None
resp = requests.post(url, headers=headers, data=body, timeout=10)
else:
self.send_response(405)
self.end_headers()
return
self.send_response(resp.status_code)
for k, v in resp.headers.items():
if k.lower() not in ['content-encoding', 'transfer-encoding', 'connection']:
self.send_header(k, v)
self.end_headers()
self.wfile.write(resp.content)
logging.info(f"代理 {self.path} 到 {url}")
except Exception as e:
self.send_response(502)
self.end_headers()
self.wfile.write(f"代理失败: {e}".encode("utf-8"))
logging.error(f"代理失败 {self.path}: {e}")
# ------------------ 免责声明 ------------------
def show_disclaimer():
try:
root = tk.Tk()
root.withdraw()
root.lift()
root.attributes('-topmost', True)
result = messagebox.askyesno("免责声明", DISCLAIMER_TEXT + "\n\n您同意以上条款吗?")
root.destroy()
return result
except Exception as e:
# 如果GUI初始化失败,记录错误并默认返回True(同意)
logging.error(f"免责声明弹窗显示失败: {e}")
return True
# ------------------ 服务线程 ------------------
class ServerThread(threading.Thread):
def __init__(self, port=80):
super().__init__()
self.port = port
self.httpd = None
self.daemon = True
def run(self):
server_address = ('', self.port)
self.httpd = HTTPServer(server_address, RequestHandler)
logging.info(f"服务器启动,监听端口 {self.port}")
self.httpd.serve_forever()
def stop(self):
if self.httpd:
self.httpd.shutdown()
def run_server(port=80):
setup_logging()
clean_old_logs()
server_thread = ServerThread(port)
server_thread.start()
image = create_image()
menu = pystray.Menu(pystray.MenuItem('退出', on_quit))
icon = pystray.Icon("Web服务", image, "Web服务", menu)
logging.info("系统托盘图标已创建,右键点击可退出")
icon.run()
def run_tray_icon():
image = create_image()
menu = pystray.Menu(pystray.MenuItem('退出', on_quit))
icon = pystray.Icon("Web服务", image, "Web服务", menu)
icon.run() # 阻塞运行,但在独立线程
# ------------------ 主入口 ------------------
if __name__ == '__main__':
# ========== 初始化日志 ==========
logger = setup_logging()
clean_old_logs()
logging.info("程序启动中...")
# ========== 确保单实例运行 ==========
terminate_existing_instances()
logging.info("已检查并终止其他实例")
# ========== 显示免责声明 ==========
disclaimer_result = show_disclaimer()
if disclaimer_result:
logging.info("用户同意免责声明,正在启动服务...")
# 启动 HTTP 服务线程
server_thread = ServerThread(80)
server_thread.start()
# 启动托盘图标线程
tray_thread = threading.Thread(target=run_tray_icon, daemon=True)
tray_thread.start()
logging.info("HTTP 服务与托盘图标已启动")
logging.info("当前日志文件位置: logs/log_%s.log", datetime.now().strftime('%Y%m%d'))
# 主线程保持运行,防止程序退出
try:
while True:
server_thread.join(timeout=1)
except KeyboardInterrupt:
logging.info("收到退出信号,正在退出...")
on_quit(None, None)
else:
logging.info("用户不同意免责声明,程序退出。")
input("按任意键退出...")
sys.exit(0)