CTF实战:从simple_php看PHP弱类型比较与MD5绕过
simple_php 是我最近在一个 CTF 平台的 Web 入门区刷到的一道题名字起得很直白考察的就是 PHP 代码审计更具体一点是 PHP 弱类型比较。这道题刚刷的时候没少折腾后来想明白了发现它其实把 PHP 里最容易让人翻车的几个点全凑到了一起的类型转换、is_numeric的判定口径、MD5 结果的魔幻字符串。刷透这一题再去碰别的 PHP 代码审计题会顺很多。这篇文章我按自己当时的完整做题思路来写。不只会告诉你最终 payload 是什么还会把每一道判断条件为什么成立、为什么会死、换了 PHP 版本会怎样都拆开讲。适合刚开始接触 CTF Web 方向、看到源码不知道从哪下手的同学也适合写 PHP 但没认真研究过类型比较的开发者。1. 先看题面simple_php 在源码里埋了哪几道坎1.1 一份常见的源码长这样平台上的题目版本可能略有差异但核心考点非常统一。我当时拿到的源码逻辑大致如下我把它整理成了更容易讲解的版本?php show_source(__FILE__); include(flag.php); $a $_GET[a]; $b $_GET[b]; if ($a 0 $b) { echo $flag1; } if (is_numeric($a)) { die(a should not be numeric); } if ($a 0 $b) { echo $flag2; } if (md5($a) md5($b) $a ! $b) { echo $flag3; } ?你拿到的源码不一定和我这个逐字符一致但考点基本跑不出这几句先判断$a 0再用is_numeric排除纯数字最后要求两个参数的 MD5 松散比较相等同时两个参数本身还不能相同。show_source(__FILE__)这句很关键它直接把源码输出到页面里所以这类题的“信息收集”阶段基本不存在剩下的事情就是对着源码做逻辑推演。1.2 三个判断条件拆开看第一个条件是$a 0 $b。这里用的是松散比较不是全等。PHP 在做比较时如果一边是数字 0另一边是字符串会发生类型转换。所以这行代码不只是在问“a 是不是数字 0”而是在问“a 这个东西有没有可能等于数字 0”。第二个条件是if (is_numeric($a)) { die(...); }。这个函数用来判断一个变量是不是数字或数字字符串。纯粹的0、0e0、123都会返回 true因此会被直接拦下来。出题人很聪明他用这个函数把最容易想到的a0直接堵死了。第三个条件是md5($a) md5($b) $a ! $b。它要求 a 和 b 的 MD5 值在松散比较下相等但 a 和 b 自己不能相等。这条看着难实际上是整道题最好突破的地方因为 PHP 的对 MD5 字符串依旧会类型转换后面我会详细说。理解这几条之后问题就变成一道约束求解找一个参数 a不是纯数字但在下等于 0再找一个参数 b和 a 不同但 MD5 值在松散比较下和 a 的 MD5 相等。2. 弱类型比较的底牌为什么字符串能等于整数 02.1的转换规则与is_numeric的严格认定PHP 早期是一门非常“宽容”的语言。HTTP 请求参数通过$_GET、$_POST拿到的时候全是字符串如果不允许字符串和数字互相比较开发者写逻辑会非常难受。所以 PHP 设计了松散比较当的一边是数字另一边是字符串时会把字符串尝试转成数字再比较。关键在于“尝试转成数字”的规则。字符串开头是合法数字就取那个数字字符串开头不是数字老版本 PHP 会直接把它当成 0。这就是为什么0 abc这种写法在 PHP 7 及更早版本里会返回 true看起来非常反直觉。is_numeric则是另一套判断标准。它不看你字符串能不能“被转成数字”而是看你本身是否符合 PHP 的数字格式。0e123这种科学计数法字符串is_numeric会返回 true哪怕它其实是一个字母e加数字。但如果字符串以字母开头比如QNKCDZOis_numeric就直接返回 false。这两件事经常被搞混。240610708是纯数字它既不能通过is_numeric的拦截也不能让240610708 0成立因为转成数字之后它是一个很大的整数并不是 0。我们需要的是那种“转成数字时被视为 0但is_numeric又不认识”的字符串。2.2 几个亲自跑过的比较结果我当时为了确认这些细节直接在本地写了一段测试代码把常见比较结果打了出来表达式PHP 5/7 结果PHP 8 结果解释0 0truetrue纯数字字符串会按数值 0 比较0 0e123truetrue科学计数法字符串按数值 0 比较0 abctruefalsePHP8 不再把非数字字符串强行转 0is_numeric(0e123)truetrue科学计数法被 PHP 视为合法数字格式is_numeric(abc)falsefalse字母开头直接 falsemd5(QNKCDZO) md5(s878926199a)truetrue两个哈希值都按科学计数法变成 0这张表基本就是解开 simple_php 的钥匙。你不需要去背每个表达式的结果但一定要理解PHP 在面前会做类型转换而is_numeric是个非常严格的“格式校验器”。这两者之间天然存在一个夹缝题目就是让你钻这个夹缝。2.3 这种宽松设计的来由你可能会问为什么 PHP 要把比较做得这么随意这跟 PHP 的历史定位有关。早期 PHP 大量用于处理表单数据表单里所有值都是字符串如果要求开发者每次都手动转型代码会非常啰嗦。松散比较降低了写代码的门槛也让“字符串能直接和数字比大小”成了很多 PHP 初学者的第一印象。代价就是安全问题。0 abc这种比较在业务逻辑中一旦出现往往就是漏洞的入口。所以现在的开发规范都会强调比较用户输入时尽量用做哈希校验时尽量用hash_equals。CTF 题目只是把这个宽松行为放大给你看。3. 魔幻哈希两个不同字符串的 MD5 也能“相等”3.1 0e 开头的科学计数法骗局MD5 的运算结果是一串 32 位的十六进制字符通常看起来像d41d8cd98f00b204e9800998ecf8427e这样。十六进制字符只能包含 0-9 和 a-f所以它天然有可能出现类似0e830400451993494058024219903391的结果。重点来了这个字符串以0e开头后面跟着的全是数字。PHP 在做比较时会把这个字符串当成科学计数法数字来看待。科学计数法的格式是“数字 e 指数”0e830...的底数是 0指数是 830...0 的任何次方还是 0。于是这个 MD5 字符串在 PHP 眼里就等于数字 0。如果两个不同的字符串它们的 MD5 结果都以0e开头那么它们彼此用比较的时候都会被当成数字 0于是0 0成立。这就是 MD5 松散比较绕过的基本原理。这类字符串在圈子里被叫做“魔幻哈希”。注意它们不是真正的 MD5 碰撞只是恰好撞上了同一个数值解释。真正的 MD5 碰撞需要精心构造两个不同文件成本和工作量都大得多CTF 里这种简单题根本不需要。3.2 常用 payload 对照表我自己本地验证过的几个魔幻哈希如下原始字符串MD5 值is_numeric 判定PHP7 下 0结果QNKCDZO0e830400451993494058024219903391falsetrues878926199a0e545993274517709034328855841020falsetrues155964671a0e342768416822977524108657743045falsetrues214587387a0e848240448830537924465865611904falsetrue你可以看到这些字符串全都是字母开头所以is_numeric返回 false不会触发die。同时它们在老版本 PHP 里与数字 0 做比较时都是 true因为非数字开头字符串会被转成 0。而它们两两之间的 MD5 值又都满足0e数字的格式所以松散比较结果也是 true。这就是 simple_php 最经典的参数组合?aQNKCDZObs878926199a。3.3 为什么数组绕过在这里用不上很多人一看到“MD5 比较绕过”第一反应是用数组?a[]1b[]2。这个思路在专门考察 MD5 的题目里很常见因为 PHP 7 下对数组调用md5()会返回 nullnull null正好成立而且数组和数组之间也能绕过$a ! $b。但在这个题目里数组走不通。原因在于第一关的$a 0不是靠着“数组等于 0”来通过的。PHP 在比较数组和数字时数组会被视为一个特殊类型并不会被转成 0。我在本地环境里实测[] 0是 false所以用a[]1连第一关都过不去更不用提后面的is_numeric和 MD5 了。这也提醒我刷题时不要看到一个考点就往上套模板必须回到源码确认每个条件是否真的兼容。数组绕过适合纯 MD5 比较的题目不适合带$a 0这种前置条件的题目。4. 组装完整 Payload从零到拿 flag 的整个验证过程4.1 逐条卡条件的选参逻辑现在我把整道题的参数选择过程重新走一遍。第一步看第一关$a 0 $b。b 只要求“非空”随便给个非零字符串就行。a 必须能松散等于 0。最容易想到的a0不行因为后面is_numeric(0)会拦截。我们需要找一个is_numeric返回 false、但 0返回 true 的值。第二步从魔幻哈希表里选。QNKCDZO满足全部前置条件。注意不能选240610708虽然它的 MD5 也是0e开头但它本身是纯数字字符串is_numeric会拦截而且它和数字 0 松散比较也不相等。第三步处理$a ! $b。a 已经选了QNKCDZOb 必须与它不同。我选s878926199a因为它的 MD5 同样是0e开头松散比较时两者相等。第四步把参数放到 URL 里?aQNKCDZObs878926199aQNKCDZO和s878926199a都是普通字母数字不需要 URL 编码直接拼接即可。如果你拿到的源码版本没有$a ! $b这个判断那更简单?aQNKCDZObQNKCDZO也能过但既然源码里有去重判断还是老老实实传两个不同的值。4.2 本地模拟验证我把核心判断写成一段独立脚本在本地跑了一遍?php $a $_GET[a] ?? QNKCDZO; $b $_GET[b] ?? s878926199a; var_dump($a 0); var_dump(is_numeric($a)); var_dump(md5($a)); var_dump(md5($b)); var_dump(md5($a) md5($b)); var_dump($a ! $b); ?在老版本 PHP 环境下访问http://127.0.0.1:8000/test.php?aQNKCDZObs878926199a结果会全部是true。这个验证方式非常推荐新手做因为你可以清楚地看到每一行判断到底是怎样通过的而不是稀里糊涂把 URL 复制到浏览器里。如果你本地是新版 PHP第一条$a 0可能会变成 false这是版本差异造成的暂时不慌。平台上的靶机如果声明了 PHP 5 或 PHP 7按经典思路做没有问题。4.3 新手最容易死在哪一步我见过最多的翻车点有两个。第一个是拿240610708当 a。很多人的思路是“MD5 是 0e 开头就行”忽略了这道题前面还压着一个is_numeric。240610708是纯数字is_numeric返回 true直接触发die。同时它和数字 0 的松散比较结果也不是 true所以即使不看is_numeric它也过不了第一关。第二个是给 b 传0。$b只要求“非空”但在 PHP 里0是一个 falsy 值0 0 0的结果是 false。很多人以为传 0 也能让$b成立实际上不行。b 最好是字符串比如s878926199a或者直接和 a 同系列的魔幻哈希。还有一个非常容易被忽略的细节空字符串也不能用。 0在 PHP7 里确实是 trueis_numeric()也是 false但你很难让 MD5 比较同时通过因为空字符串的 MD5 需要另一个空字符串来匹配而那个空字符串又会让$b判断失败。与其折腾不如直接用标准魔幻哈希。5. 同一类坑换了个马甲弱类型漏洞变体和开发侧教训5.1 常见的 PHP 弱类型比较漏洞变体simple_php 只是把弱类型比较这个问题浓缩到了一道题里。实际项目中类似的危险写法到处都是而且经常以完全不同的形式出现。我整理了几个常见的变体场景危险写法常见绕过思路数值范围判断in_array($id, $allowed)未开启严格模式时1可以和1abc相等密码校验strcmp($input, $password) 0传数组让strcmp返回 nullnull 0成立哈希校验md5($a) md5($b)0e 魔幻哈希或传数组让两边都变 null登录判断$user[id] $admin_id数据库中字符串与数字的隐式转换这些变体的共同点是只要代码里用了而不是或者写了in_array、strcmp却没做类型约束就可能被非预期输入绕过去。5.2 换到 PHP8 后为什么老经验失灵PHP 8 对字符串与数字的比较行为做了比较大的调整。最直观的变化是0 abc在 PHP 7 里是 true在 PHP 8 里是 false。也就是说非数字字符串不会再看都不看就转成 0 了QNKCDZO 0这种老经验直接失效。我在本地装了 PHP 8 之后复现这道题第一个判断就翻车了。折腾了半天才发现不是 payload 写错而是环境版本问题。所以刷题前一定要确认平台的环境版本尤其这种老题目很多思路都是基于 PHP 5 或 PHP 7 的宽松行为。不过有个细节需要注意0e123 0e456这种“两个数字字符串之间的比较”在 PHP 8 里仍然会按数值比较结果还是 true因为两边都是合法数字字符串。PHP 8 改的是字符串与数字之间的比较而不是两个数字字符串之间的比较。这也是为什么 MD5 魔幻哈希绕过在 PHP 8 里依然有效真正失效的是前面a 0这一步。5.3 作为开发者应该怎么写更稳的代码刷完这道题我最直接的感受是写 PHP 时能用就不要用特别是所有涉及用户输入的地方。要求类型和值都相等字符串QNKCDZO不会等于数字 01也不会等于整数 1这能省掉一大半弱类型漏洞。哈希校验场景优先使用hash_equals。它是专门为哈希值比较设计的会做长度校验和逐字节比对而且比较过程中尽量保持恒定时间能防时序侧信道攻击。判断数字输入时别只依赖is_numeric。它接受科学计数法和各种数字字符串业务上未必是你想要的。如果确实需要一个整数用filter_var($input, FILTER_VALIDATE_INT)或者先判ctype_digit再用intval做一次显式转换。数组操作要小心in_array和array_search默认都是松散比较。如果数组里存的是数字 ID而用户输入是个字符串最好给这两个函数传入第三个参数 true 开启严格模式。6. 做题记录里的几个翻车点和排查思路6.1 本地复现和平台环境不一致我第一次做这道题时没有先确认平台 PHP 版本直接在本地跑了一段模拟脚本结果发现$a 0怎么都是 false。当时第一反应是魔幻哈希表记错了换了好几个字符串都没用。后来才意识到本地默认装的是 PHP 8而这道题的经典环境是 PHP 7。把判断脚本里的var_dump(PHP_VERSION);加上马上就定位到了问题。这个教训让我之后刷题多了一个习惯涉及语言特性、版本差异的题目先确认环境版本而不是一上来就猜参数。6.2 只看 payload 不看源码的后果很多人刷题追求“秒出答案”看到熟悉的关键字就复制网上现成的 payload。simple_php被公开过太多次网上答案很多但如果你只是背下QNKCDZO和s878926199a换一个带$a 0的版本就完全不会了。正确的做题姿势一定是先把源码抄下来逐行翻译成自然语言。比如$a 0 $b可以翻译成“a 松散等于 0同时 b 为真”is_numeric($a)可以翻译成“a 必须不是数字格式”。把每条约束列清楚再开始选参数。这个过程比背一百个 payload 都有用。6.3 解决这类题的一个稳定套路我现在遇到 PHP 弱类型相关题目基本会按下面四步走先把源码里的比较运算符全部圈出来区分和。把is_numeric、in_array、strcmp、md5这类函数单独列出来想清楚它们的输入要求和返回结果。根据约束条件列出候选值优先考虑0e开头的魔幻哈希、数组、空数组、特殊布尔值。在本地写一个最小复现脚本验证确认每个判断的输出再拿最终 payload 去提交。这套流程不需要多高深的技术但能避免绝大多数低级错误。遇到变体题只要源码没变思路就基本通用源码变了也能通过这套流程重新推导。我在实际做这道题时最大的收获不是背会了两个哈希串而是真正理解了 PHP 松散比较的边界在哪里。以后再看到我第一反应不是“这题能用 0e 绕过”而是“这段代码在比较时到底把两边转化成了什么”。带着这个意识去读源码很多漏洞入口一眼就能扫出来。

相关新闻

OpenAI API Key 获取两种方式全解析:Computer Use Preview 驱动 Playwright 浏览器自动化实战

OpenAI API Key 获取两种方式全解析:Computer Use Preview 驱动 Playwright 浏览器自动化实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/11 15:42:13 阅读更多 →
TensorFlow 2教学级工程课件:可运行、可复现、可部署的实战教案

TensorFlow 2教学级工程课件:可运行、可复现、可部署的实战教案

简介:本资源是《TensorFlow2深度学习实战全书》配套的电子教案完整版课件,面向人工智能初学者、高校教学人员及深度学习入门开发者,系统梳理深度学习核心概念、主流应用场景与TensorFlow 2.x实践路径。课件共1个PPTX文件,大小27.1…

2026/10/11 15:42:13 阅读更多 →
NyaTerm 安全功能完全指南:加密凭据存储、主密码与锁屏防护详解

NyaTerm 安全功能完全指南:加密凭据存储、主密码与锁屏防护详解

【免费下载链接】nyaterm A modern remote terminal workspace 项目地址: https://gitcode.com/gh_mirrors/ny/nyaterm 点击查看 免费下载 🔐 还在担心 SSH 密码明文散落在配置文件里吗?NyaTerm 是一款现代远程终端工作区工具,内…

2026/10/11 15:42:13 阅读更多 →

最新新闻

Oracle EBS R12月结关账顺序与子账对账SQL实战指南

Oracle EBS R12月结关账顺序与子账对账SQL实战指南

简介:本资源是一份面向Oracle EBS R12财务实施顾问、系统运维人员及财务信息化从业者的专业培训课件,聚焦财务月结核心流程与实操要点,系统解决多模块协同关账、数据一致性校验及常见异常排查等关键问题。课件为单个PPTX文件(2.9M…

2026/10/11 16:20:35 阅读更多 →
Android聊天机器人课设全攻略:架构设计、网络层与本地规则引擎实战

Android聊天机器人课设全攻略:架构设计、网络层与本地规则引擎实战

简介:一份面向移动应用开发学习者的Android聊天机器人课程设计报告,围绕智能机器人App的完整实现展开,适合作为安卓课程设计或移动开发实训的参照范本。报告从实验目的与内容入手,详细介绍在图灵机器人平台申请API Key、创建聊天界…

2026/10/11 16:20:35 阅读更多 →
Blume 部署完全指南:从静态 HTML 托管到 Vercel、Netlify、Cloudflare 服务端适配全覆盖

Blume 部署完全指南:从静态 HTML 托管到 Vercel、Netlify、Cloudflare 服务端适配全覆盖

【免费下载链接】blume The open-source docs framework for humans and agents. 项目地址: https://gitcode.com/gh_mirrors/blum/blume 点击查看 免费下载 Blume 是一个面向人类与 AI 代理的开源文档框架,本文带你完成 Blume 文档站点部署&#xff1a…

2026/10/11 16:20:35 阅读更多 →
python的先进制造技术工业场景模拟第一百二十二篇:读取机器人工作站来料数据,建立模型,预测来料偏差造成的抓取失败概率。

python的先进制造技术工业场景模拟第一百二十二篇:读取机器人工作站来料数据,建立模型,预测来料偏差造成的抓取失败概率。

机器人抓取失败概率预测——从来料偏差到抓取风险评估一、实际应用场景(真实痛点)时间:周三凌晨 2:47。地点:某家电企业注塑车间机器人上下料工作站。夜班班长小赵被电话吵醒赶到现场时,机器人已经第 7 次停在了安全位…

2026/10/11 16:20:35 阅读更多 →
2026乐山景区古建牌坊检测排名 TOP5 CMA 资质机构提供牌坊裂缝检测、牌坊倾斜检测、老化检测 联系方式推荐

2026乐山景区古建牌坊检测排名 TOP5 CMA 资质机构提供牌坊裂缝检测、牌坊倾斜检测、老化检测 联系方式推荐

在乐山这座历史文化名城,古建牌坊星罗棋布,从峨眉山麓的巍峨石坊到乡野村落的古朴木楼,每一处都承载着岁月印记。然而,本地古建牌坊检测机构虽鳞次栉比,却也鱼龙混杂,大量无资质机构出具的检测报告在住建、…

2026/10/11 16:20:35 阅读更多 →
Intouch 加数据库做 Excel 报表:从 SQLConnect 到 VBA 的完整数据链路

Intouch 加数据库做 Excel 报表:从 SQLConnect 到 VBA 的完整数据链路

简介:这份文档面向SCADA系统工程师与Intouch组态开发人员,聚焦WonderWare Intouch 2014R2平台与SQL Server 2012数据库的集成及Excel报表实现,适合需要搭建实时数据存储与报表展示方案的中级技术人员参考。资源包内共1个doc文件,约…

2026/10/11 16:19:34 阅读更多 →

日新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/11 0:00:27 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/11 0:00:27 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/11 0:00:27 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/11 0:00:27 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/11 0:00:27 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/11 0:00:27 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/11 10:45:37 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/11 14:36:53 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/11 14:36:54 阅读更多 →