累计签到:10 天 连续签到:3 天
|
分享一个自制的摸鱼小工具-伪装弹窗看剧、挂机-Mord V1.0.2
平时想挂个页游、看个视频又怕被抓包?传统的“老板键”切来切去太显着。
这个工具能把目标窗口完美隐蔽到“弹窗广告”。最硬核的是:你可以直接在这个小挂件里操作原游戏!
核心亮点:
- 动态伪装:外观是个带 GIF 底图的圆角小窗口(自带 5 款,支持自定义当地gif),看起来就像个垃圾广告弹窗。无缝交互:你在伪装小窗口里的点击、拖拽、滚轮操作,都会 100% 实时同步给后台隐蔽的游戏/网页/视频。支持范围广:支持恣意已打开的窗口、小窗。
3 步极简用法:
- 打开你想摸鱼的网页/游戏(最小化也可以)。运行软件,按中键框选你要显示的位置在弹出的下拉列表选中它,点击完成。
注意:右下角有个隐蔽的设置键,光标移动即显示,点击可进入设置界面、重选区域。
rgbrgb
rgb
rgb
rgb
rgb
[Python] 纯文本查看 复制代码import sysimport osimport jsonimport ctypesfrom ctypes import wintypesfrom PyQt5.QtWidgets import (QApplication, QMainWindow, QLabel, QRubberBand, QDialog, QVBoxLayout, QComboBox, QPushButton, QHBoxLayout, QFileDialog)from PyQt5.QtCore import Qt, QRect, QPoint, QTimerfrom PyQt5.QtGui import QMovie, QPainterPath, QRegion, QFont, QCursor, QIcon# ================= 资源路径自动解析 (兼容打包) =================def get_resource_path(relative_path): if getattr(sys, 'frozen', False): base_path = sys._MEIPASS else: base_path = os.path.dirname(os.path.abspath(__file__)) return os.path.join(base_path, relative_path)GIF_DIR = get_resource_path("")CONFIG_FILE = "moyu_config.json"# ================= Windows API 结构体与常量 =================DWM_TNP_VISIBLE = 0x8DWM_TNP_RECTDESTINATION = 0x1DWM_TNP_OPACITY = 0x4SWP_NOZORDER = 0x0004SWP_NOSIZE = 0x0001SWP_NOACTIVATE = 0x0010WM_SIZING = 0x0214WMSZ_LEFT = 1WMSZ_RIGHT = 2WMSZ_TOP = 3WMSZ_TOPLEFT = 4WMSZ_TOPRIGHT = 5WMSZ_BOTTOM = 6WMSZ_BOTTOMLEFT = 7WMSZ_BOTTOMRIGHT = 8WM_LBUTTONDOWN = 0x0201WM_LBUTTONUP = 0x0202WM_MOUSEMOVE = 0x0200WM_MOUSEWHEEL = 0x020AMK_LBUTTON = 0x0001GWL_EXSTYLE = -20WS_EX_APPWINDOW = 0x00040000WS_EX_TOOLWINDOW = 0x00000080WS_EX_LAYERED = 0x00080000LWA_ALPHA = 0x00000002GW_OWNER = 4DWMWA_CLOAKED = 14class DWM_THUMBNAIL_PROPERTIES(ctypes.Structure): _fields_ = [ ("dwFlags", wintypes.DWORD), ("rcDestination", wintypes.RECT), ("rcSource", wintypes.RECT), ("opacity", wintypes.BYTE), ("fVisible", wintypes.BOOL), ("fSourceClientAreaOnly", wintypes.BOOL), ]class POINT(ctypes.Structure): _fields_ = [("x", wintypes.LONG), ("y", wintypes.LONG)]class WINDOWPLACEMENT(ctypes.Structure): _fields_ = [ ("length", wintypes.UINT), ("flags", wintypes.UINT), ("showCmd", wintypes.UINT), ("ptMinPosition", POINT), ("ptMaxPosition", POINT), ("rcNormalPosition", wintypes.RECT) ]dwmapi = ctypes.windll.dwmapiuser32 = ctypes.windll.user32# ================= 辅助函数 =================def load_config(): default_config = { "gif_name": "1.gif", "target_window_title": "" } if os.path.exists(CONFIG_FILE): try: with open(CONFIG_FILE, 'r', encoding='utf-8') as f: data = json.load(f) default_config.update(data) except: pass return default_configdef save_config(config): try: with open(CONFIG_FILE, 'w', encoding='utf-8') as f: json.dump(config, f, ensure_ascii=False, indent=4) except Exception as e: print(f"保存设置失败: {e}")def get_visible_windows(): taskbar_windows = [] tray_windows = [] blacklist = [ "mord", "Program Manager", "", "Windows 输入体验", "Realtek Audio Console", "设置", "Settings", "Microsoft Text Input Application", "Microsoft Store" ] def enum_windows_proc(hwnd, lParam): if user32.IsWindowVisible(hwnd) and user32.GetWindowTextLengthW(hwnd) > 0: length = user32.GetWindowTextLengthW(hwnd) buf = ctypes.create_unicode_buffer(length + 1) user32.GetWindowTextW(hwnd, buf, length + 1) title = buf.value if title in blacklist: return True is_cloaked = ctypes.c_int(0) dwmapi.DwmGetWindowAttribute(hwnd, DWMWA_CLOAKED, ctypes.byref(is_cloaked), ctypes.sizeof(is_cloaked)) if is_cloaked.value != 0: return True ex_style = user32.GetWindowLongW(hwnd, GWL_EXSTYLE) owner = user32.GetWindow(hwnd, GW_OWNER) is_taskbar = False if not (ex_style & WS_EX_TOOLWINDOW): if owner == 0 or (ex_style & WS_EX_APPWINDOW): is_taskbar = True if is_taskbar: taskbar_windows.append((hwnd, title)) else: tray_windows.append((hwnd, title)) return True user32.EnumWindows(ctypes.WINFUNCTYPE(ctypes.c_bool, wintypes.HWND, wintypes.LPARAM)(enum_windows_proc), 0) return taskbar_windows, tray_windows# ================= iOS 极简风设置面板 (精简版) =================class SettingsDialog(QDialog): def __init__(self, parent=None, config=None): super().__init__(parent) self.setWindowTitle("设置") self.setWindowIcon(QIcon(get_resource_path("mord.ico"))) self.resize(300, 200) self.action_type = "confirm" self.selected_hwnd = None self.config = config self.setup_ui() self.apply_ios_theme() def setup_ui(self): layout = QVBoxLayout(self) layout.setSpacing(12) layout.setContentsMargins(15, 20, 15, 15) layout.addWidget(QLabel("底图 GIF")) self.gif_combo = QComboBox() current_gif = self.config.get("gif_name", "1.gif") # 这里的范围改为了 1 到 7,也就是会添加 1.gif 到 6.gif for i in range(1, 7): self.gif_combo.addItem(f"{i}.gif") existing_items = [self.gif_combo.itemText(i) for i in range(self.gif_combo.count())] if current_gif not in existing_items: self.gif_combo.insertItem(0, current_gif) self.gif_combo.addItem("自定义当地文件...") self.gif_combo.setCurrentText(current_gif) self.gif_combo.activated.connect(self.on_gif_activated) layout.addWidget(self.gif_combo) layout.addWidget(QLabel("隐蔽并克隆的目标窗口")) self.hwnd_combo = QComboBox() self.taskbar_windows, self.tray_windows = get_visible_windows() self.tray_expanded = False self.populate_hwnd_combo() self.hwnd_combo.activated.connect(self.on_hwnd_activated) layout.addWidget(self.hwnd_combo) layout.addSpacing(15) btn_layout = QHBoxLayout() rb = QPushButton("重绘") rb.setObjectName("btn_redraw") rb.clicked.connect(self.on_redraw) ok = QPushButton("完成") ok.setObjectName("btn_confirm") ok.clicked.connect(self.on_confirm) ex = QPushButton("退出") ex.setObjectName("btn_exit") ex.clicked.connect(self.on_exit) btn_layout.addWidget(rb) btn_layout.addWidget(ok) btn_layout.addWidget(ex) layout.addLayout(btn_layout) layout.addStretch() info_label = QLabel("mord v1.0.3 | 作者: ALiang") info_label.setAlignment(Qt.AlignCenter) info_label.setStyleSheet("color: #A1A1A6; font-size: 10px; margin-top: 5px;") layout.addWidget(info_label) def populate_hwnd_combo(self): self.hwnd_combo.clear() saved_title = self.config.get("target_window_title", "") if not self.tray_expanded: for hwnd, title in self.tray_windows: if title == saved_title: self.tray_expanded = True break index_to_select = -1 for hwnd, title in self.taskbar_windows: self.hwnd_combo.addItem(title, hwnd) if title == saved_title: index_to_select = self.hwnd_combo.count() - 1 if self.tray_expanded: for hwnd, title in self.tray_windows: self.hwnd_combo.addItem(f"[后台/托盘] {title}", hwnd) if title == saved_title: index_to_select = self.hwnd_combo.count() - 1 else: self.hwnd_combo.addItem("--- 点击展开后台与托盘工具 ---", None) if index_to_select >= 0: self.hwnd_combo.setCurrentIndex(index_to_select) elif self.hwnd_combo.count() > 0: self.hwnd_combo.setCurrentIndex(0) def on_hwnd_activated(self, index): data = self.hwnd_combo.itemData(index) if data is None and not self.tray_expanded: self.tray_expanded = True self.populate_hwnd_combo() self.hwnd_combo.showPopup() def apply_ios_theme(self): self.setStyleSheet(""" QDialog { background-color: #F2F2F7; font-family: -apple-system, "Segoe UI", "Microsoft YaHei"; } QLabel { color: #6E6E73; font-size: 11px; font-weight: 500; margin-top: 2px; } QComboBox { background-color: #FFFFFF; border: none; border-radius: 8px; padding: 6px 10px; color: #1D1D1F; font-size: 13px; } QComboBox::drop-down { border: none; } QPushButton { border-radius: 8px; padding: 7px; font-size: 13px; font-weight: bold; } QPushButton#btn_confirm { background-color: #007AFF; color: white; } QPushButton#btn_confirm:hover { background-color: #0066CC; } QPushButton#btn_redraw { background-color: #E5E5EA; color: #007AFF; } QPushButton#btn_redraw:hover { background-color: #D1D1D6; } QPushButton#btn_exit { background-color: #E5E5EA; color: #FF3B30; } QPushButton#btn_exit:hover { background-color: #D1D1D6; } """) def on_gif_activated(self, index): if self.gif_combo.itemText(index) == "自定义当地文件...": path, _ = QFileDialog.getOpenFileName(self, "选择当地 GIF", "", "GIF 动画 (*.gif)") if path: self.gif_combo.insertItem(0, path) self.gif_combo.setCurrentIndex(0) else: self.gif_combo.setCurrentText(self.config.get("gif_name", "1.gif")) def on_confirm(self): self.selected_hwnd = self.hwnd_combo.currentData() if self.selected_hwnd: raw_title = self.hwnd_combo.currentText().replace("[后台/托盘] ", "") self.config["target_window_title"] = raw_title self.action_type = "confirm" self.accept() def on_redraw(self): self.action_type = "redraw" self.accept() def on_exit(self): self.action_type = "exit" self.accept()# ================= 主窗口 =================class mord(QMainWindow): def __init__(self): super().__init__() self.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint) self.setAttribute(Qt.WA_TranslucentBackground) self.setWindowTitle("mord") self.setWindowIcon(QIcon(get_resource_path("mord.ico"))) self.setMouseTracking(True) self.config = load_config() self.state = "INIT" self.video_rect = QRect() self.thumb_handle = ctypes.c_void_p(0) self.target_hwnd = None self.drag_pos = None self.is_interacting = False self.aspect_ratio = 1.0 self.current_movie = None self.saved_placement = None self.original_ex_style = None self.init_ui() QTimer.singleShot(50, lambda: self.load_specified_gif(self.config["gif_name"])) def init_ui(self): self.bg_label = QLabel(self) self.bg_label.setScaledContents(True) self.bg_label.setStyleSheet("background-color: transparent; border-radius: 12px;") self.bg_label.setMouseTracking(True) self.hint_label = QLabel(self) self.hint_label.setAlignment(Qt.AlignCenter) self.hint_label.setStyleSheet( "background-color: rgba(0, 0, 0, 160); color: white; padding: 10px 16px; border-radius: 16px;") self.hint_label.setFont(QFont("-apple-system", 9, QFont.Medium)) self.hint_label.hide() self.settings_btn = QPushButton("", self) self.settings_btn.resize(30, 30) self.settings_btn.setToolTip("设置 (点击修改窗口或重绘)") self.settings_btn.clicked.connect(self.open_settings) self.settings_btn.setStyleSheet(""" QPushButton { background: transparent; border: none; } QPushButton:hover { background: rgba(255, 255, 255, 40); border-radius: 10px; } """) self.rubber_band = QRubberBand(QRubberBand.Rectangle, self) def move_to_bottom_right(self): screen = QApplication.screenAt(self.geometry().center()) if not screen: screen = QApplication.primaryScreen() screen_geo = screen.availableGeometry() margin = 20 target_x = screen_geo.x() + screen_geo.width() - self.width() - margin target_y = screen_geo.y() + screen_geo.height() - self.height() - margin self.move(target_x, target_y) def show_hint(self, text): self.hint_label.setText(text) self.hint_label.adjustSize() self.hint_label.move((self.width() - self.hint_label.width()) // 2, (self.height() - self.hint_label.height()) // 2) self.hint_label.show() def hide_original_window(self, hwnd): if not hwnd: return self.saved_placement = WINDOWPLACEMENT() self.saved_placement.length = ctypes.sizeof(WINDOWPLACEMENT) user32.GetWindowPlacement(hwnd, ctypes.byref(self.saved_placement)) self.original_ex_style = user32.GetWindowLongW(hwnd, GWL_EXSTYLE) user32.SetWindowLongW(hwnd, GWL_EXSTYLE, self.original_ex_style | WS_EX_LAYERED) user32.SetLayeredWindowAttributes(hwnd, 0, 0, LWA_ALPHA) if self.saved_placement.showCmd != 2: user32.ShowWindow(hwnd, 7) user32.ShowWindow(hwnd, 9) user32.SetWindowPos(hwnd, 0, -30000, -30000, 0, 0, SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE) user32.SetLayeredWindowAttributes(hwnd, 0, 255, LWA_ALPHA) def restore_original_window(self): if self.target_hwnd: if self.original_ex_style is not None: user32.SetWindowLongW(self.target_hwnd, GWL_EXSTYLE, self.original_ex_style) if self.saved_placement: user32.SetWindowPlacement(self.target_hwnd, ctypes.byref(self.saved_placement)) else: user32.SetWindowPos(self.target_hwnd, 0, 100, 100, 0, 0, SWP_NOZORDER | SWP_NOSIZE) def register_thumbnail(self, hwnd): if self.thumb_handle: dwmapi.DwmUnregisterThumbnail(self.thumb_handle) self.thumb_handle = ctypes.c_void_p(0) self.target_hwnd = hwnd self.hide_original_window(hwnd) bg_rect = wintypes.RECT() user32.GetWindowRect(hwnd, ctypes.byref(bg_rect)) orig_w = bg_rect.right - bg_rect.left orig_h = bg_rect.bottom - bg_rect.top if orig_w > 0 and orig_h > 0 and not self.video_rect.isEmpty(): target_ratio = orig_w / orig_h box_w = self.video_rect.width() box_h = self.video_rect.height() box_ratio = box_w / box_h if box_ratio > target_ratio: final_h = box_h final_w = int(final_h * target_ratio) final_x = self.video_rect.left() + (box_w - final_w) // 2 final_y = self.video_rect.top() else: final_w = box_w final_h = int(final_w / target_ratio) final_x = self.video_rect.left() final_y = self.video_rect.top() + (box_h - final_h) // 2 self.video_rect = QRect(final_x, final_y, final_w, final_h) self.original_video_rect = QRect(self.video_rect) res = dwmapi.DwmRegisterThumbnail(int(self.winId()), hwnd, ctypes.byref(self.thumb_handle)) if res == 0: self.update_thumbnail_rect(visible=False) self.state = "PLAYING" self.hint_label.hide() def update_thumbnail_rect(self, visible=True): if not self.thumb_handle or self.video_rect.isEmpty(): return ratio = self.devicePixelRatioF() props = DWM_THUMBNAIL_PROPERTIES() props.dwFlags = DWM_TNP_VISIBLE | DWM_TNP_RECTDESTINATION | DWM_TNP_OPACITY props.fVisible = visible props.opacity = 255 left = int(self.video_rect.x() * ratio) top = int(self.video_rect.y() * ratio) right = int((self.video_rect.x() + self.video_rect.width()) * ratio) bottom = int((self.video_rect.y() + self.video_rect.height()) * ratio) props.rcDestination = wintypes.RECT(left, top, right, bottom) dwmapi.DwmUpdateThumbnailProperties(self.thumb_handle, ctypes.byref(props)) def load_specified_gif(self, gif_name_or_path): if os.path.isabs(gif_name_or_path): path = gif_name_or_path else: path = os.path.join(GIF_DIR, gif_name_or_path) if not os.path.exists(path): return self.current_movie = QMovie(path) self.bg_label.setMovie(self.current_movie) self.current_movie.start() sz = self.current_movie.currentImage().size() if sz.width() > 0 and sz.height() > 0: screen = QApplication.screenAt(self.geometry().center()) if not screen: screen = QApplication.primaryScreen() screen_h = screen.availableGeometry().height() target_h = screen_h // 3 self.aspect_ratio = sz.width() / sz.height() target_w = int(target_h * self.aspect_ratio) self.resize(target_w, target_h) self.move_to_bottom_right() self.show_hint("按住鼠标中键 拖拽框选游戏区") def open_settings(self): dialog = SettingsDialog(self, self.config) if dialog.exec_() == QDialog.Accepted: if dialog.action_type == "exit": self.close() elif dialog.action_type == "redraw": self.trigger_redraw() elif dialog.action_type == "confirm": new_gif = dialog.gif_combo.currentText() if new_gif != self.config.get("gif_name") and new_gif != "自定义当地文件...": self.config["gif_name"] = new_gif self.trigger_redraw() self.load_specified_gif(new_gif) dialog.selected_hwnd = None save_config(self.config) if dialog.selected_hwnd: self.register_thumbnail(dialog.selected_hwnd) def trigger_redraw(self): if self.thumb_handle: dwmapi.DwmUnregisterThumbnail(self.thumb_handle) self.thumb_handle = ctypes.c_void_p(0) self.restore_original_window() self.state = "INIT" self.video_rect = QRect() self.show_hint("按住鼠标中键 拖拽框选游戏区") def enterEvent(self, e): if self.state == "PLAYING": self.update_thumbnail_rect(True) def leaveEvent(self, e): if self.state == "PLAYING": self.update_thumbnail_rect(False) def map_and_send_mouse_event(self, pos, msg_type, wparam=0): if not self.target_hwnd: return bg_rect = wintypes.RECT() user32.GetWindowRect(self.target_hwnd, ctypes.byref(bg_rect)) orig_w = bg_rect.right - bg_rect.left orig_h = bg_rect.bottom - bg_rect.top if orig_w w - m: return True, 11 except: pass return super().nativeEvent(eventType, message) def resizeEvent(self, event): self.bg_label.resize(self.size()) self.settings_btn.move(self.width() - 36, self.height() - 36) path = QPainterPath() path.addRoundedRect(0, 0, self.width(), self.height(), 12, 12) self.setMask(QRegion(path.toFillPolygon().toPolygon())) if self.state in ["PLAYING", "READY"] and hasattr(self, 'original_video_rect'): wr, hr = self.width() / self.original_size.width(), self.height() / self.original_size.height() self.video_rect = QRect( int(self.original_video_rect.left() * wr), int(self.original_video_rect.top() * hr), int(self.original_video_rect.width() * wr), int(self.original_video_rect.height() * hr) ) self.update_thumbnail_rect(True) super().resizeEvent(event) def closeEvent(self, e): self.restore_original_window() super().closeEvent(e)if __name__ == "__main__": if hasattr(Qt, 'AA_EnableHighDpiScaling'): QApplication.setAttribute(Qt.AA_EnableHighDpiScaling, True) if hasattr(Qt, 'AA_UseHighDpiPixmaps'): QApplication.setAttribute(Qt.AA_UseHighDpiPixmaps, True) app = QApplication(sys.argv) window = mord() window.show() sys.exit(app.exec_())<font face="Arial, 微软雅黑, 宋体, 新宋体, sans-serif">rgb |
精密测量技术论坛免责声明
重要声明:以上内容仅代表该作者观点,不代表本站精密测量技术论坛立场。
如有涉及侵权请尽快告知,我们将会在第一时间处理。作者原创内容未经允许不得转载!
站长联系邮箱:1339305021@qq.com
站长联系微信:dddnnbbb
|