从Fakebook看CTF综合题:SQL注入与PHP反序列化链式利用实战复盘

发布时间:2026/10/20 12:28:34

从Fakebook看CTF综合题:SQL注入与PHP反序列化链式利用实战复盘 今年网鼎杯的 Web 题里Fakebook 这道题给我留下了很深的印象。表面上看是一个模仿主流社交平台的站点注册、登录、发动态、看主页功能一应俱全长得人畜无害。但实际上它把信息收集、源码审计、SQL 注入、反序列化、任意文件读取这几个考点串成了一条完整的攻击链难度不算顶高但每一步都卡得很到位综合性很强。如果你是刚开始打 CTF 的 Web 选手或者想过几道像样的综合题来练手这道题非常适合拿来做复盘素材。网上已经有不少关于这道题的 writeup但大多数只写了“这里注入、那里反序列化”没把为什么要这么做、每一步的依据是什么讲清楚。这篇文章我从头到尾把完整的做题过程梳理一遍把踩坑和排查思路也带上尽量让话捡干货说看完能直接上手复现。1. 环境侦察与谜题拆解1.1 从首页开始摸清功能面拿到题目第一件事永远不是急着点页面而是先想清楚这个站点到底有哪些功能入口可以用。Fakebook 的首页看起来就是一个标准社交网站的样子有登录框、有注册入口、有几个公开的博主信息列表底下还分页。基本功能面很清晰游客能看公开用户列表和公开的个人主页注册登录后能创建账号发布自己的博客内容访问自己的个人主页站点存在分页查询逻辑说明大概率有数据库交互头像和博客内容如果存在上传或展示逻辑就要额外关注文件处理和序列化存储的情况。第二步我会顺手看一顿页面上暴露的静态文件和响应头。大多数时候真正的突破口藏在容易被忽略的文本里比如注释、框架指纹、Cookie 的序列化痕迹。Fakebook 的 Cookie 里有个显眼的 user 字段值是一长串看起来很像 PHP 序列化的字符串具体是a:3:{s:2:id;s:1:1;...}这种形态。看到这个基本可以确定两件事后端是 PHP而且把用户数据序列化后写进了 Cookie。这就为后面的反序列化埋下了重要的伏笔。1.2 顺手扫一波目录与备份文件在确认功能面之后常规思路是先跑一遍目录扫描看看有没有隐藏管理入口、备份文件、源码包之类的东西。工具我用的是常见扫描工具字典选的是带 php 备份文件特征的常用路径。扫到的结果里有一个很有价值的入口/robots.txt。很多比赛进去第一件事就该看它虽然里面不一定会直接给 flag但经常会间接给线索。Fakebook 的 robots 文件内容并不复杂但有两条路径直接暴露了敏感文件的存在/user.php.bak/view.php.bak.bak 后缀说明是编辑器或人为备份的 PHP 源码文件。这类文件如果直接能被访问下载就等于把源码送给了参赛者。访问/robots.txt里暴露的那两个 .bak 文件可以拉到两份关键源码user.php和view.php。从这里开始题目就正式从“黑盒猜谜”转入了“白盒审计”阶段。1.3 源码泄露后的文件结构分析拿到user.php.bak和view.php.bak之后先不着急逐行看先观察整体文件结构和引用关系。两个文件代码量都不大但信息密度很高。user.php里面定义了一个User类私有属性有$id、$name、$blog和$avatar构造方法把接收到的数据赋给对应属性还实现了魔术方法__toString()。这个方法打印用户信息同时里面用了一个file_get_contents去读取图片路径并输出。看到对象里有file_get_contents并且参数是用户可控的就要警觉这就是潜在的任意文件读取点或者更准确说是一个可以被反序列化触发的敏感操作点。view.php主要负责渲染用户主页。它从 Cookie 里取user字段反序列化成一个User对象然后调用__toString()把信息输出到页面。问题很明显了Cookie 是客户端可控的后端直接对 Cookie 内容做unserialize()没有任何过滤或签名校验。这就像把保险柜钥匙放在了密码锁旁边只要你能构造合法结构的序列化字符串让反序列化出来的对象符合预期__toString()里的file_get_contents就会按照你指定的路径去读文件。这里插一句个人经验拿到源码别急着把所有函数都盯一遍。第一轮先看数据流从入口参数一路追到危险函数凡是“用户输入 → 存储 → 取出后再使用”的路径都要理清楚。第二轮再针对危险函数找触发条件。Fakebook 的核心数据流非常短Cookie 里取序列化数据 →unserialize()生成对象 → 对象输出时自动调用__toString()→file_get_contents读取路径。理解这条链路后面的利用就顺理成章了。2. 数据库层的突破口SQL 注入定位与利用2.1 注入点到底藏在哪里反序列化链虽然已经看到了但光靠这一个点还不够因为要构造出有效的序列化对象前提是知道代码里真实存在哪些可用的类、类的属性名是什么以及要读取的目标文件路径。这些信息一部分在源码里能直接看到另一部分需要靠别的漏洞去“撬”出来。Fakebook 首页的用户列表带分页参数URL 大概长这样/index.php?page1。第一个想到的当然是尝试对分页参数做 SQL 注入测试。经验上来说分页、排序、过滤这类参数是注入高发区因为开发者容易直接拿参数拼 SQL。用一个单引号去试页面直接报了数据库错误。能在页面上看到原始报错信息这本身就是个很好的信号意味着写注入脚本时可以直接采用报错注入不用盲注一点点猜效率能翻好几倍。具体报错长这样You have an error in your SQL syntax...。不用关心完整报错内容看关键点就够传入的单引号被拼进了 SQL 语句导致语法错误而且错误信息被直接回显。这基本坐实了GET请求的page参数存在字符型 SQL 注入。2.2 判断注入类型与绕过思路继续往下测我用order by逐列判断字段数量。order by 3页面正常order by 4直接报错说明查询只有 3 列。这里顺便说一句order by报错时不需要纠结具体报什么只要页面行为发生变化就够了这个方法是判断列数最稳的手段。然后测试注入类型。在page参数后面拼接and 11和and 12一个正常一个空内容确认是数字型或字符型。这里有个小细节做题时最好多试几种闭合方式包括不加引号、加单引号、加双引号、加括号等。Fakebook 这题实际过滤不算严格常规 payload 就能打通但不要因为一次探索顺利就掉以轻心很多题会卡在过滤绕过上。利用报错注入的方式我用updatexml(1,concat(0x7e,(select ...),0x7e),1)把查询结果带出来。这个是比赛里最常用的报错注入手法配合concat拼接输出。为了让拿到的信息更完整我把查询目标定在information_schema上逐步获取库名、表名和字段名。第一步先拿库名payload 大概长这样page1 and updatexml(1,concat(0x7e,(select database()),0x7e),1)返回结果里能看到当前使用的库名继续下一步从information_schema.tables里找当前库下有几张表page1 and updatexml(1,concat(0x7e,(select table_name from information_schema.tables where table_schemadatabase() limit 0,1),0x7e),1)这里注意limit的使用一次只能取一行要循环换offset把表名逐个拉出来。拿到的表名里有几个和用户、博客相关的表还有一个表名一看就跟 flag 气质贴合。按照题目的常见套路flag 要么藏在传说中的 flag 表里要么放在某个用户的高权限私有博客里所以先不急着动 flag 表而是先看用户表因为用户表里的数据往往是后续反序列化利用的跳板。2.3 从数据表里挖出序列化内容沿着注入点把用户表的数据拖出来看发现表结构里有一个字段专门存“个人介绍”或“博客信息”这类长文本内容。关键来了这个字段里存的不是普通字符串而是以O:4:User:开头的序列化数据。这里解释一下为什么它是关键在反序列化漏洞里如果数据表里存储的内容本身是序列化对象并且某个功能在取出内容后会重新执行unserialize()那么只要我们能通过注入把恶意构造的序列化对象写进数据库当功能触发时就会自动反序列化执行我们的恶意对象。这就是著名的“二次注入与反序列化组合利用”。Fakebook 实际结构差不多是这样注册用户时后端把一个User对象序列化后存入数据库查看主页时取出字段内容一部分用于渲染一部分交给unserialize()重新还原成对象。这就给了一条路通过 SQL 注入往这个字段里写入我们自己的序列化数据然后触发前端渲染让代码主动调用__toString()。但这里有一个现实问题如果直接通过注入修改数据库里的字段字段长度可能受限而且有些字符会被转义特别是引号和反斜杠。实际操作中比较稳的方式是注册一个新的测试账号把恶意序列化内容拼到可控输入点里比如博客地址、昵称、头像路径再利用注入把其他字段顺带改掉或者干脆借助注册功能本身的数据流让恶意数据进库。具体用哪种方式取决于题目过滤规则和功能设计。Fakebook 这里我用的是注册功能结合字段内容拼接的方式把构造好的序列化字符串当作博客地址内容提交然后触发查看主页。3. 反序列化利用与源码级利用链组装3.1 为什么数据表里躺着一串序列化字符串要理解反序列化利用先得理解序列化存储的初衷。PHP 的serialize()把内存中的对象转成可存储传输的字符串unserialize()再把它还原成对象。在 Web 开发里很多老项目喜欢把对象直接存进数据库字段省事但代价是安全边界很模糊。Fakebook 的User类就是典型例子。它的__toString()魔术方法本来是为了方便直接echo $user但方法内部却调用了file_get_contents并把结果拼进输出。这意味着只要我们能控制对象中的某个属性值file_get_contents的路径就完全被我们拿捏了。再配合服务端直接对 Cookie 或数据表内容进行unserialize()漏洞就串起来了。用生活化的类比讲序列化数据就像一张快递单上面写着收件人、地址、物品名称。正常情况下你按单子发货没问题但如果你能偷偷改单子上的字段比如把“地址”改成“服务器上的 /flag”那快递员就会把不该发的包裹送到不该去的地方。这里的快递员就是unserialize()和__toString()的组合。3.2 反序列化漏洞的核心机制PHP 反序列化漏洞的本质在于两个环节的信任错位一个环节信任数据内容是安全的不去校验就还原对象另一个环节信任对象属性是合法范围内的值拿着属性值就直接去执行敏感操作。常见的危险魔术方法有几个__destruct()对象销毁时触发适合执行清理或恶意命令__toString()对象被当作字符串输出时触发适合输出文件内容__call()/__get()访问不存在的方法或属性时触发可用来劫持调用__wakeup()反序列化时触发常用来绕过限制或初始化恶意属性Fakebook 里核心触发点是__toString()。所以利用条件很明确第一步确认存在可控的file_get_contents调用且路径由对象属性决定第二步找到能把这个对象写入存储或 Cookie 的途径第三步在访问页面时让字符串输出方式触发__toString()。这里有一个细节会被很多新手忽略__toString()只在对象被当成字符串使用的时候才会调用。如果你只是把对象赋给变量没有任何输出或拼接操作它不会触发。所以看源码时要注意上下文看看目标对象到底是被echo、拼接还是传给字符串函数用了。3.3 构造序列化 Payload 的正确姿势构造 Payload 之前要先确认目标类定义里的属性名。从user.php.bak里能看到User类有$name、$blog、$avatar等属性其中$blog很可能就是触发file_get_contents的那个点。序列化 Payload 的基本格式是O:4:User:2:{s:4:name;s:4:test;s:4:blog;s:18:/flag_xxx/flag.txt;}展开解释一下O:4:User表示这是一个类名为 User 的对象类名长度是 42表示有两个属性s:4:name表示属性名为 name长度 4s:4:test表示属性值为字符串 tests:4:blog和后面的路径同理构造时最容易被坑的是长度计数。PHP 反序列化对字符串长度的要求非常严格长度不对会直接报错或者导致后面的解析错位。如果字符串里有中文要注意中文字符的字节数长度必须以字节为单位计算。我之前踩过不少次这种坑所以每次构造完都会用serialize()函数跑一遍确认格式无误再传给靶机。写个本地小脚本用和目标一样的类定义去serialize()生成的数据最可靠不要手拼字符串。不过这里还有一个微妙之处如果目标是让$blog指向某个远程地址或者内网地址那file_get_contents能不能成功还要看 PHP 配置里是否开启了allow_url_fopen。大多数情况下默认是开启的但读本地文件基本不受影响。所以第一个尝试目标应该放在服务器本地的敏感文件上比如 flag 文件、/etc/passwd通过这些内容反推服务器的目录结构。4. 完整攻击链实现从浏览器到 flag4.1 注册测试账号采集关键数据接下来把攻击链完整走一遍。第一步先注册一个普通测试账号填入可控内容的时候就要有意识地往“会影响序列化属性”的方向去构造。注册页面的表单字段不复杂但每填一项都要想清楚它会被拼进哪个属性。比如博客地址或主页 URL 这个字段如果你在源码里看到它最终被赋值给$blog那恭喜这里就是我们要塞恶意内容的位置。实践里我一般会先用一个正常值注册比如bloghello然后在查看主页时观察页面输出了什么确认字段和__toString()的映射关系。确认之后重新注册一个账号把博客字段填成反序列化 Payload。注意这里的输入长度一般没有限制所以序列化对象可以写得很完整。注册完成数据库里对应记录的存储内容实际就是序列化后的对象。4.2 让目标对象在页面上“复活”注册成功之后访问这个账号的主页服务端会把数据库里取到的内容交给unserialize()还原成User对象然后在模板里输出。只要输出操作触发__toString()对象里的$blog值就会被当作路径传给file_get_contents最终读到的内容直接拼在页面上回显。如果第一次读取失败页面可能会留白或者直接报错这时候别慌先确认几件事对象属性名是不是拼错了路径对不对目标文件是否存在。实战里面过程往往不会那么顺利所以要用 SQL 注入继续收集信息。比如先通过注入读服务器配置文件、看 Web 目录路径、确认 flag 文件名。Fakebook 里 flag 的路径不会写死在题目描述里而是放在某个不容易猜到名字的目录中。用注入去读information_schema已经拿不到这个文件系统的信息了所以还得靠反序列化链读文件。再配合一个常见技巧如果file_get_contents(/flag)读不出来可以先读/proc/self/cmdline之类的文件确认目标进程的启动路径也能看到 Web 目录的物理位置。或者读/etc/nginx/nginx.conf找到 Web 根目录和站点配置再从配置文件里找 flag 的存储位置。实际操作中我经常这样层层推进先读/etc/passwd确认文件读取成功再读 Web 目录下的index.php看实际代码确认数据库账号密码然后尝试用数据库客户端连接进去直接从数据库里掏 flag。这种思路比盲目猜文件路径要稳妥得多。4.3 从文件读取到最终 getflag如果题目环境允许反序列化读文件已经足够拿到 flag。但如果遇到读文件路径被限制的情况比如file_get_contents只能读取固定前缀的路径那就需要转换思路利用 PHP 的流协议来绕过比如用php://filter/convert.base64-encode/resourceindex.php读取源码。这是非常实用的一招可以把源码用 base64 方式带出来避免直接读 PHP 文件时被服务器执行而看不到内容。Fakebook 的场景里$blog最终指向的文件路径被拼进file_get_contents所以读 flag 的直接姿势是O:4:User:2:{s:4:name;s:4:test;s:4:blog;s:18:/var/www/html/flag.php;}这里要注意路径长度是 18构造时一定要数清楚。如果 class 的私有属性名带了空字节或其他特殊字符序列化格式会略有不同但一般情况下构造void。实际上用 PHP 脚本去serialize()一个真实对象最不容易出错。真正拿到 flag 的内容之后我一般会顺手验证一下是不是题目要求的格式。很多比赛要求提交的 flag 是特定格式比如flag{...}只要内容完整复制即可。同时建议把完整攻击链的每一步截图保存方便赛后复盘或者写 writeup 时参考。这里不只是为了交差更是为了下次遇到同类题目时能快速定位问题出在哪个环节。4.4 攻击链全景回顾把整条链路串起来看Fakebook 的精髓就很清楚了。它不是考某一个孤立的漏洞而是考你怎么把多个漏洞点串成一跳链路。单独的 SQL 注入只能拿到数据库内容单独的反序列化也不一定能构造出合法的对象但二者结合后注入负责把恶意序列化内容送进数据库反序列化负责把它还原成危险对象__toString()负责真正执行文件读取最终把 flag 带回页面。这就是 CTF 里常说的“链式思维”。做题时不要只盯着某个字段发呆要跳出来看整个流程数据从哪里进来经过了哪些存储在哪里被取出最终在哪里触发了敏感操作。只要这条数据流里有任何一个地方对输入不够警惕整条链就能被打通。5. 踩坑记录与排查思路复盘5.1 实验过程中最容易翻车的三个点这题我做了不止一遍每次都能碰到不一样的小坑。最典型的有三个基本可以覆盖大多数人会遇到的卡点。第一个坑是序列化字符串长度错误。这是新手翻车率最高的地方。PHP 的序列化格式里每个字符串字段都要标注字节长度长度不对unserialize()会直接返回 false后面的逻辑全部失效。很多情况下你构造的 Payload 看起来没毛病但页面就是不回显任何变化极大概率就是长度没对上。解决办法很直接用 PHP 官方函数serialize()生成数据不要手拼。第二个坑是没搞清楚触发时机。__toString()不是反序列化时自动触发的而是对象被当成字符串使用时才触发。如果你写入数据库之后只是简单查询验证没有触发对象输出页面自然不会出现预期效果。一定要确认前端渲染逻辑里确实有echo $user或echo $user-blog之类的地方。如果没有触发点就要另找别的魔术方法或代码逻辑。第三个坑是路径抓瞎。直接猜 flag 文件路径成功率很低。正确做法是先利用任意文件读取读几个基础文件判断服务器操作系统、Web 目录结构和运行权限再顺着配置文件去找 flag。哪怕题目提示 flag 在根目录也要先试/flag不行再试/var/www/html/flag不要一棵树上吊死。如果路径有限制优先尝试php://filter流协议读源码再根据源码内容定位 flag 位置。5.2 FAQ 速查表问题现象可能原因排查方法unserialize()报错或返回 false序列化字符串长度不对属性名/类名拼写错误用本地 PHP 脚本 serialize() 生成合法 Payload页面内容无变化没有触发__toString()或对象没被当作字符串输出检查前端模板是否有 echo 对象变量的代码file_get_contents读不到目标文件路径写错、目标文件权限不足先读/etc/passwd、/proc/self/cmdline判断环境反序列化 Payload 全部失败类属性是 private/protected格式和 public 不同检查源码里属性可见性按可见性调整序列化格式文件名不确定flag 文件没放在常规位置读配置文件找 Web 根目录和站点物理路径再逐层找PHP 文件读出来被解析没法看内容直接读 PHP 文件被服务端执行了改用php://filter/convert.base64-encode/读取源码5.3 综合题方法论先画数据流再找拼接点复盘完整道题我最想分享的一点其实是“怎么做综合题”的方法论。不要一上来就盯着某个参数猛怼也不要看到一个漏洞就急着打先把整个应用的数据流画出来。从用户可控输入开始跟踪到存储层再从存储层跟踪到使用层重点观察三个东西输入有没有校验、存储有没有转义、使用有没有危险函数。Fakebook 的数据流是一条线注册参数 → 序列化写入数据库 → 前端取数据 →unserialize()→__toString()→file_get_contents。任何环境下这种链路都要作为重点排查对象。尤其是 PHP 题目序列化字段出现在数据库或 Cookie 里基本等于把门牌号写在了脸上。再补一个实战技巧构造 Payload 时养成脚本化的习惯。本地放一个和目标类同名的 PHP 测试文件用serialize()直接生成序列化数据。一是省去手工数长度的痛苦二是验证序列化格式的过程本身就是对类结构的二次确认。我之前看别人代码审计时经常卡在属性可见性上public、private、protected 的序列化格式不一样用脚本生成可以彻底避开这些记忆负担。这个思路还能延伸到哪里Fakebook 这种“多漏洞点串链”的思路放到真实项目审计里同样适用。渗透测试时拿到的往往不是单一漏洞直通服务器而是几个中低危问题组合起来突破边界比如一个存储型 XSS 配合 CSRF 改管理员密码再配合后台文件上传拿权限本质上都是链式思维。做题和实战并没有分界线差别只在于题目有标准答案而实际系统千奇百怪。以这道题为引子建议你接下来可以做两件事一是找几道同样涉及序列化 注入组合的题目练手最好是自己搭环境改几行代码换换路径和属性名再打一遍看看能不能适应变化二是把本地 PHP 环境里常见的危险函数触发场景全列一遍做一张自己的速查表打题时翻起来效率极高。我在复现这道题的过程里最大的收获不是最后拿到 flag 那一瞬间而是中途被序列化长度卡了两个小时后突然意识到用脚本生成 Payload 有多省事。后来每道 PHP 反序列化题我都坚持这个习惯再也没在格式上栽过跟头。希望这篇复盘能帮你少踩几个我没必要的坑把时间花在更关键的思路上。
延伸阅读

更多相关文章

2026/10/20 12:28:34

C#与OPCKeepsever通信控制西门子PLC/OPCUA

一、整体链路 典型架构是:[西门子PLC] --(以太网)–> [KEPServerEX] --(OPC UA/DA)–> [上位机软件],所有 PLC 与 KEPServerEX 运行在同一局域网内,通过 TCP/IP 协议通信。你要“控制”PLC,本质是上位机通过 OPC 写入标签 →…

2026/10/20 12:28:34

Kepware OPC Server配置

一、整体思路 KEPServerEX 在 OPC DA 体系里扮演“数据汇聚 协议转换”的角色:下挂各种 PLC/仪表驱动,向上统一以 OPC DA 接口暴露。典型链路是 PLC(以太网)→ KEPServerEX → 上位机软件(OPC UA/DA)。所以…

2026/10/20 12:23:34

YOLOv8与YOLOv11塑料瓶目标智能监测系统全解析

1. 项目概述:这套系统到底在解决什么问题先把这个毕设项目说清楚。它叫“YOLOv11YOLOv8塑料瓶目标智能监测系统”,核心任务是对塑料瓶进行实时目标检测与计数。听起来很简单?实际做起来牵扯的东西远比想象的多:反光、透明瓶身、堆…

2026/10/20 13:18:59

低空经济车辆无人机协同配送路径优化:pymoo集中式建模实践

“低空经济”四个字,这几年几乎出现在每一本物流配送方向论文的引言里,标题里那篇论文做的正是低空经济下的车辆与无人机协同配送路径优化。它和传统车辆路径问题最大的区别是:决策对象从“一辆车跑完整条线”变成了“一辆车带着多架无人机&a…

2026/10/20 13:18:59

深入理解TCP send与recv:内核缓冲区、阻塞行为与流式语义

1. 从两个函数说起:为什么send和recv值得单独拎出来讲做网络编程的人,每天都在跟send和recv打交道。但说实话,我见过太多写了几年服务端代码的人,对这两个函数的理解还停留在“一个发一个收”的层面。真到了线上出现吞吐上不去、延…

2026/10/20 13:18:59

9个HTML5原生功能,重构老项目砍掉4万行JS

删代码比写代码更容易上瘾。去年年底我接手了公司一个年久失修的管理后台,光JS文件加起来就有4万行上下。业务逻辑说实话不算复杂,但里面有大量重复的弹窗、折叠面板、表单校验、滚动懒加载,而且全是老式写法,一个页面动不动就几十…

2026/10/20 13:18:59

AI视频量产实战:无限画布如何将生产效率提升10倍

1. 内容工作室的产能困局与破局思路做内容工作室这行有些年头了,从最早几个人扛着单反拍口播,到后来批量做信息流短视频,再到现在客户张口就要“日更三十条、一周一迭代”,我最大的感受就是:产能焦虑是刻在骨子里的。尤…

2026/10/20 13:13:55

从文档解析到混合检索:RAG知识库落地与调优实战

从有明确背景的团队内部知识库,到个人积累的文档、书摘和实验笔记,很多人在接触大模型落地的时候都会听别人提一句“接入RAG就好了”。可真正动手做第一个知识库Demo的人多半会发现,最大的障碍往往不是模型本身,而是“原来知识并不…

2026/10/19 18:06:35

金融AI落地指南:从调研报告到技术选型与工程实践

简介:这份PDF报告聚焦2025年金融服务业中国AI现状与趋势,面向金融科技从业者、AI战略规划人员及关注行业数字化转型的研究者,帮助读者把握生成式AI在银行、支付、交易等场景中的落地路径与投资方向。资源包内含1个PDF文件,大小约1…

2026/10/20 1:05:37

单片机C运行时从启动到多任务:中断安全与共享资源冲突

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

2026/10/20 1:05:37

GCC编译四步详解:预处理、编译、汇编、链接实战

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

2026/10/20 1:05:37

FIW线是什么?FEP注塑线与普通氟塑料线的区别及应用指南

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

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑