import { unsafeWindow } from "ViteGM"; import { BaiduData } from "@/main/BaiduData"; import { DOMUtils, OriginPrototype, log, utils } from "@/env"; import { Panel } from "@components/setting/panel"; type BaiduHookFunctionApplyMode = "copy" | "scheme"; type BaiduHookFunctionCallMode = "baijiahao_invoke" | "map"; interface BaiduHookDefine { path: string; requirePathList: string[]; callback: (...args: any[]) => boolean | void; } /** * 百度劫持 */ export const BaiduHook = { $isHook: { windowBoxJSBefore: false, objectDefineProperty_search: false, windowJQueryAppend: false, windowOpenBox: false, windowWebpackJsonp_tieba: false, windowWebpackJsonp_haokan: false, window_openContentBox: false, functionCall_baijiahao_map: false, }, $data: { functionApply: [], functionCall: [], elementAppendChild: <((element: HTMLElement) => boolean | void)[]>[], setTimeout: <(string | RegExp)[]>[], windowDefine: [], }, /** * 统一管理apply的劫持,防止套娃 * @param mode 劫持的类型 */ functionApply(mode: BaiduHookFunctionApplyMode) { this.$data.functionApply.push(mode); if (this.$data.functionApply.length > 1) { log.info("Function.property.apply hook新增劫持参数:" + mode); return; } let that = this; // 初始化调用 log.info("初始化Function.property.apply hook"); unsafeWindow.Function.prototype.apply = function (...args) { /** * 劫持剪贴板写入 * + 百度搜索 */ if (that.$data.functionApply.includes("copy")) { try { let firstParam = args[1]; if ( args.length === 2 && typeof firstParam === "object" && "" + firstParam === "[object Arguments]" && firstParam.length === 1 && typeof firstParam[0] === "object" && firstParam[0] != null && "appName" in firstParam[0] && "checkTokenCopied" in firstParam[0] && "deeplink" in firstParam[0] && "scheme" in firstParam[0] && "token" in firstParam[0] && "useDeeplink" in firstParam[0] ) { log.success("劫持复制到剪贴板函数", ...firstParam); return new Promise(function (resolve) { log.success("修改参数并劫持复制到剪贴板返回true"); resolve({ status: true, }); }); } } catch (error) { // log.error(error); } } if (that.$data.functionApply.includes("scheme")) { /** * 劫持apply的Scheme调用 * + 百度搜索 */ try { let firstParam = args[1]; if ( args.length === 2 && typeof firstParam === "object" && "" + firstParam === "[object Arguments]" && firstParam.length === 2 && firstParam[1] === "scheme" ) { log.success("劫持Scheme", ...firstParam); return; } } catch (error) { // log.error(error); } } return OriginPrototype.Function.apply.call(this, ...args); }; }, /** * 统一管理call的劫持,防止套娃 * + 百家号(baijiahao.baidu.com) * + 百度地图(map.baidu.com) * @param mode 劫持的类型 */ functionCall(mode: BaiduHookFunctionCallMode) { this.$data.functionCall.push(mode); if (this.$data.functionCall.length > 1) { log.info("Function.property.call hook新增劫持参数:" + mode); return; } let that = this; // 初始化调用 log.info("初始化Function.property.call hook"); let originCall = unsafeWindow.Function.prototype.call; unsafeWindow.Function.prototype.call = function (...args) { let result = originCall.apply(this, args); if (mode === "baijiahao_invoke") { // 百家号 if ( args.length === 4 && typeof args[1]?.["exports"] === "object" && "execCopy" in args[1]?.["exports"] && "invokeApp" in args[1]?.["exports"] && "invokeMarket" in args[1]?.["exports"] && "invokeTpApp" in args[1]?.["exports"] ) { args[1]["exports"]["execCopy"] = function (...args: any[]) { return new Promise((resolve) => { log.success("阻止调用execCopy", args); resolve(null); }); }; args[1]["exports"]["invokeApp"] = function (...args: any[]) { return new Promise((resolve) => { log.success("阻止调用invokeApp", args); resolve(null); }); }; args[1]["exports"]["invokeMarket"] = function (...args: any[]) { return new Promise((resolve) => { log.success("阻止调用invokeMarket", args); resolve(null); }); }; args[1]["exports"]["invokeTpApp"] = function (...args: any[]) { return new Promise((resolve) => { log.success("阻止调用invokeTpApp", args); resolve(null); }); }; } else if ( args.length === 2 && args[0] === void 0 && args[1] != null && "arg" in args[1] && "delegate" in args[1] && "done" in args[1] && "method" in args[1] && "next" in args[1] && "prev" in args[1] ) { log.success("修改参数", args[1]); args[1]["method"] = "return"; args[1]["next"] = "end"; args[1]["prev"] = 24; } } else if (mode === "map") { // 百度地图 if ( args.length === 2 && args[0] === void 0 && args[1] != null && "arg" in args[1] && "delegate" in args[1] && "done" in args[1] && "method" in args[1] && "next" in args[1] && "prev" in args[1] ) { log.success("修改参数", args[1]); args[1]["method"] = "return"; args[1]["next"] = "end"; args[1]["prev"] = 24; } } return result; }; }, /** * 劫持全局define */ windowDefine(path: string, requirePathList: string[], callback: (...args: any[]) => boolean | void) { this.$data.windowDefine.push({ path: path, requirePathList: requirePathList, callback: callback, }); if (this.$data.windowDefine.length > 1) { log.info("define hook新增劫持参数:" + path); return; } let that = this; let safeDefine = void 0; let unsafeDefine = function (...args: any) { let define_path = args[0]; let define_requrePathList = args[1]; let define_callback = args[2]; for (let index = 0; index < that.$data.windowDefine.length; index++) { let hookConfig = that.$data.windowDefine[index]; if ( hookConfig.path === define_path && JSON.stringify(hookConfig.requirePathList) === JSON.stringify(define_requrePathList) ) { args[2] = hookConfig.callback; break; } } (safeDefine as any)(...args); }; unsafeDefine.prototype.amd = {}; OriginPrototype.Object.defineProperty(unsafeWindow, "define", { get() { if (safeDefine == null) { return; } return unsafeDefine; }, set(v) { log.success("define ==> ", v); safeDefine = v; }, }); }, /** * 劫持百度搜索某些项的点击事件 * + 百度搜索 * * Object.defineProperty * @param menuKeyName */ objectDefineProperty_search(menuKeyName: string) { if (this.$isHook.objectDefineProperty_search) { return; } this.$isHook.objectDefineProperty_search = true; unsafeWindow.Object.defineProperty = function ( this: any, target: T, propertyKey: PropertyKey, _attributes: PropertyDescriptor & ThisType ): T { if (propertyKey === "_onClick") { BaiduData.search.isHijack_onClick = true; log.info("成功劫持_onClick", arguments); let oldFn = _attributes["value"]; _attributes["value"] = function (this: any, event: Event) { let eventNode = this._getNode(event.target); let eventNodeName = this._getType(eventNode); if (eventNodeName === "link") { let linkProps = this._getLinkProps(eventNode); log.success("点击事件-linkProps信息", linkProps); if (!linkProps.href) { DOMUtils.emit(document, "click", event, false); return; } DOMUtils.preventEvent(event); if (Panel.getValue("baidu_search_hijack__onClick_to_blank")) { log.success("新标签页打开: " + linkProps.href); window.open(linkProps.href, "_blank"); } else { window.location.href = linkProps.href; } } else { log.success("点击事件-this._getType(eventNode)不为link", eventNodeName, event); oldFn.call(this, ...arguments); } }; } // @ts-ignore return OriginPrototype.Object.defineProperty.call(this, ...arguments); }; }, /** * 劫持添加元素,包括script标签、iframe标签,默认劫持iframe的非http链接 * + 百度贴吧(tieba.baidu.com) * + 百度地图(map.baidu.com) * Element.prototype.appendChild * @param handleCallBack 处理的回调函数,如果劫持请返回true */ elementAppendChild( handleCallBack: (element: HTMLElement) => boolean | void = function (element) { if (element instanceof HTMLIFrameElement) { if (typeof element?.src === "string" && !element.src.startsWith("http")) { log.success("劫持iframe唤醒:" + element.src, element); // @ts-ignore return true; } } } ) { this.$data.elementAppendChild.push(handleCallBack); if (this.$data.elementAppendChild.length > 1) { log.info("Element.prototype.appendChild hook新增劫持判断回调"); return; } unsafeWindow.Element.prototype.appendChild = function (element: Node): T { if (typeof handleCallBack === "function") { let handleResult = handleCallBack(element as any); if (handleResult) { // @ts-ignore return; } } // @ts-ignore return OriginPrototype.Element.appendChild.call(this, ...arguments); }; }, /** * 劫持jQuery的append的iframe * + 百度地图(map.baidu.com) * * $().append(); */ windowJQueryAppend() { if (this.$isHook.windowJQueryAppend) { return; } this.$isHook.windowJQueryAppend = true; let originAppend = (unsafeWindow as any).$.fn.append; (unsafeWindow as any).$.fn.append = function (params: any) { if (typeof params === "string") { params = params.trim(); if (params.startsWith('