从“運”字漏洞解析宽字节注入:字符集编码与SQL注入防御
1. 项目概述一个汉字引发的安全思考最近在复盘一些老漏洞和CTF题目时我又想起了那个经典的“運”字绕addslashes的案例。这听起来像是一个冷门的技巧但它背后串联起的字符集编码、PHP魔术引号、SQL注入以及Web应用安全的底层逻辑却是每一位后端开发者和安全研究员都绕不开的必修课。很多朋友可能知道addslashes()这个函数是用来给单引号、双引号、反斜杠和NULL字符添加转义反斜杠的是早期防御SQL注入的一种常见手段。但为什么一个看似普通的汉字“運”在某些特定条件下就能让这道防线形同虚设呢这绝不是魔法而是字符集转换过程中产生的“编码漏洞”。这个问题不仅出现在CTF的赛题里在真实的、尤其是那些遗留的或对国际化支持考虑不周的Web系统中依然是一个潜在的威胁点。理解它能帮助我们以点带面深刻认识到安全不是一个孤立的函数调用而是从数据库连接、到后端处理、再到前端展示的完整链条。今天我们就从这个“運”字出发拆解字符集编码如何与Web安全产生化学反应聊聊那些年我们踩过的编码坑以及如何系统性地构建更稳固的防御。2. 核心原理字符集、转义与“宽字节”的诞生要理解“運”字如何绕过转义我们必须先回到计算机如何表示字符这个根本问题上。我们常说的“字符集”和“字符编码”是两个紧密相关但不同的概念。字符集Character Set是一个规则集合定义了哪些字符比如“A”、“汉”、“運”被收录并给每个字符分配一个唯一的编号这个编号叫码位Code Point。而字符编码Character Encoding则定义了如何将这个编号转换成计算机存储的二进制序列。2.1 从ASCII到多字节编码的演进早期的ASCII编码用1个字节8位表示一个字符足以覆盖英文世界。但当需要表示中文、日文等成千上万的字符时1个字节256种可能显然不够。于是多字节编码方案应运而生其中GBK、GB2312、BIG5等就是中文环境下常见的“双字节”字符集。在这些编码中一个汉字由2个字节组成。addslashes()函数的工作机制非常直接它遍历输入字符串当遇到单引号ASCII值为0x27、双引号0x22、\反斜杠0x5C和NULL0x00时就在其前面插入一个反斜杠\0x5C。所以its会变成it\s。在SQL解析时这个反斜杠会告诉解析器“后面的单引号是数据的一部分不是语句的边界”。理想情况下攻击者输入 OR 11经过addslashes后会变成\ OR \1\\1从而被安全地当作普通字符串处理。2.2 “宽字节注入”的核心漏洞点漏洞的种子就埋藏在“字符集不一致”和“多次转码”的土壤里。我们来看一个典型的漏洞场景环境设定Web应用程序使用PHP开启了magic_quotes_gpc或手动调用了addslashes()进行转义。数据库连接使用的字符集是GBK或BIG5等双字节编码。攻击输入攻击者提交的参数中包含一个精心构造的字符其第一个字节的值是0xBF第二个字节是0x27即单引号的ASCII码。在GBK编码中0xBF27对应一个特定的汉字事实上0xBF5C是“縗”字但原理相通0xBF27是一个未定义的编码序列但会被组合解析。转义发生addslashes()看到第二个字节0x27于是尽职地在它前面插入一个反斜杠\ASCII0x5C。此时数据在内存中变成了0xBF, 0x5C, 0x27。字符集转换与“吞并”当这个字符串被送入数据库时如果数据库连接认为这是GBK编码的字符串它会尝试每两个字节解析成一个汉字。它会将0xBF5C这两个字节组合起来去查GBK码表。在GBK码表中0xBF5C恰好对应一个合法的汉字——“運”。于是神奇的“化学反应”发生了反斜杠0x5C被前一个字节0xBF“吞并”共同组成了“運”字。而原本的单引号0x27现在被“孤立”了出来重新暴露在SQL解析器面前这个过程可以简化为0xBF27-addslashes-0xBF5C27- GBK解码 -運。那个用于保护的单引号的反斜杠“护盾”在编码转换的熔炉里被蒸发掉了。这就是“宽字节注入”的经典原理。注意并非所有0xBF开头的组合都有效也并非只有GBK存在此问题。任何使用双字节或更多字节表示一个字符且其编码范围覆盖了0x5C反斜杠的字符集在配合不正确的转义和字符集设置时都可能出现类似漏洞。BIG5中的“誠”字0xA55C也是一个著名的例子。3. 漏洞场景深度复现与实操理解了原理我们通过一个高度简化的模拟场景来亲手复现一下这比纯理论要直观得多。我们会搭建一个存在漏洞的PHP环境并一步步演示漏洞的利用。3.1 模拟漏洞环境搭建假设我们有一个简单的用户登录查询后端代码如下请勿在生产环境使用?php // 模拟magic_quotes_gpc开启或手动addslashes的效果 $username isset($_GET[user]) ? $_GET[user] : ; $password isset($_GET[pass]) ? $_GET[pass] : ; // 漏洞点1使用了addslashes进行转义 $username addslashes($username); $password addslashes($password); // 漏洞点2数据库连接字符集设置为GBK模拟旧系统常见配置 // 在真实环境中这可能是通过SET NAMES GBK或连接参数设置的。 // 此处我们通过手动进行字符串转换来模拟这一过程。 // 假设从客户端经过addslashes后到数据库的传输过程中字符串被当作GBK解码。 function simulate_gbk_decoding($str) { // 这是一个非常简化的模拟重点演示宽字节吞并效果。 // 在实际的PHPMySQL GBK环境里这个过程由mysql/mysqli扩展和MySQL服务器自动完成。 return mb_convert_encoding($str, UTF-8, GBK); } // 构造SQL语句这是最危险的拼接方式仅用于演示 $sql SELECT * FROM users WHERE username . $username . AND password . $password . ; echo 转义后拼接的SQL: . $sql . br; // 模拟GBK解码后的SQL漏洞触发点 $sql_after_gbk simulate_gbk_decoding($sql); echo 模拟GBK解码后的SQL: . $sql_after_gbk . br; // 这里本应执行数据库查询... echo 如果执行上述SQL将被解析。br; ?这个代码有两个关键问题一是使用addslashes进行转义二是隐含了环境字符集为GBK的假设。在实际的古老PHPMySQL环境中可能是通过mysql_set_charset(gbk)或连接参数设置的。3.2 构造攻击Payload并分析现在我们如何利用呢攻击者的目标是让username参数后的单引号闭合然后开始注入新的SQL命令。一个经典的Payload是username運pass123。但这里有个细节我们不能直接输入汉字“運”因为浏览器通常以UTF-8编码发送運的UTF-8编码是E9 81 8B这不会被GBK解码成两个字节。攻击者需要直接输入GBK编码下的字节序列。在URL中我们可以通过百分号编码Percent-Encoding来输入特定字节。運字的GBK编码是BF 5C。单引号的ASCII码是0x27。所以构造的Payload如下我们想让username的值为0xBF 0x27。这样经过addslashes会变成0xBF 0x5C 0x27再经GBK解码0xBF5C变成“運”剩下一个孤立的0x27单引号。访问URL需要对字节进行URL编码http://vulnerable-site.com/login.php?user%BF%27pass123后端处理流程$_GET[user]接收到字节%BF%27-0xBF 0x27。addslashes($username)检测到0x27单引号在前面插入0x5C反斜杠。现在字符串是0xBF 0x5C 0x27。拼接SQL... username 0xBF 0x5C 0x27 ...。关键步骤当这个SQL语句字符串被发送到配置了GBK字符集的MySQL连接时MySQL客户端库或服务器会将这些字节作为GBK编码进行解析。它看到0xBF5C查GBK码表得到汉字“運”。于是最终进入SQL解析器的字符串变成了... username 運 ...。看单引号成功逃逸了攻击者现在可以继续构造%BF%27 OR 11 --。--是SQL注释符用于注释掉后面的AND password...部分。最终执行的SQL可能是SELECT * FROM users WHERE username 運 OR 11 -- AND password 123从而绕过登录验证。实操心得在真实测试中你可能会使用Burp Suite等工具直接修改原始HTTP请求的十六进制体而不是依赖浏览器的URL编码因为浏览器对非标准UTF-8字节的处理可能不一致。直接发送0xBF 0x27的原始字节更为可靠。3.3 漏洞的变种与延伸宽字节注入不局限于addslashes和GBK。任何“转义后发生字符集转换”的场景都可能存在类似问题iconv()函数滥用使用iconv(UTF-8, GBK, $input)进行转换时如果遇到不合法的UTF-8序列iconv默认会静默丢弃或替换这可能意外地“创造”出能吞并反斜杠的字节组合。攻击者可以精心构造一个无效的UTF-8序列使得iconv转换后恰好产生0xBF5C这样的组合。其他双字节字符集如前面提到的BIG5繁体中文0xA55C对应“誠”字同样可以吞并反斜杠。二次解码有时应用层会进行多次解码如先URL解码再处理。如果转义发生在第一次解码之后、第二次解码之前而第二次解码涉及字符集转换也可能引入漏洞。4. 从防御到根治构建安全的字符处理体系知道了漏洞原理修复和防御的思路就清晰了。核心原则是统一字符集并使用参数化查询预编译语句代替字符串拼接和转义。4.1 立即修复方案设置正确的字符集对于仍在使用mysql_*扩展或旧版mysqli且无法立即重构代码的系统最首要的修复是确保字符集设置一致且安全。绝对不要这样做$conn mysql_connect(...); mysql_query(SET NAMES GBK); // 或 SET NAMES ‘latin1’应该这样做// 使用 mysqli并在建立连接后立即设置字符集为 utf8mb4 $conn new mysqli($host, $user, $pass, $db); if ($conn-connect_error) die(...); // 关键步骤使用 mysqli_set_charset它会确保客户端和服务器使用正确的编码方式 if (!$conn-set_charset(utf8mb4)) { die(字符集设置失败: . $conn-error); }mysqli_set_charset()或PDO::setAttribute(PDO::MYSQL_ATTR_INIT_COMMAND, “SET NAMES utf8mb4”)函数对于MySQL不仅发送一个SET NAMES语句还会调整连接层内部的编码处理逻辑从根本上避免宽字节问题。现代Web应用应统一使用UTF-8更推荐utf8mb4以支持完整的Unicode如表情符号。4.2 治本之策使用参数化查询预编译语句转义函数addslashes、mysql_real_escape_string是“修补”思维而参数化查询是“设计安全”思维。它的原理是将SQL语句的结构模板与数据分开发送给数据库。// 使用 mysqli 预处理 $stmt $conn-prepare(SELECT * FROM users WHERE username ? AND password ?); $stmt-bind_param(ss, $username, $password); // ‘ss’ 表示两个字符串参数 $username $_GET[user]; $password $_GET[pass]; $stmt-execute(); $result $stmt-get_result(); // 使用 PDO 预处理更推荐 $pdo new PDO($dsn, $user, $pass, [PDO::ATTR_ERRMODE PDO::ERRMODE_EXCEPTION]); $stmt $pdo-prepare(SELECT * FROM users WHERE username :user AND password :pass); $stmt-execute([:user $_GET[user], :pass $_GET[pass]]); $result $stmt-fetchAll();在这种方式下数据库引擎明确知道?或:user是数据占位符无论用户输入什么内容即使是運 OR 11 --它都会被整体当作一个纯粹的字符串值来处理而不会被解析为SQL语法的一部分。这就彻底杜绝了所有基于拼接的注入攻击包括宽字节注入、数字型注入等。4.3 辅助防御与最佳实践禁用magic_quotes_gpc这个特性在PHP 5.4.0中被移除。如果你的环境还有应在php.ini中将其关闭。它盲目地对所有GPCGet/Post/Cookie数据加反斜杠破坏原始数据且无法解决根本问题。输入验证与白名单对于已知格式的数据如邮箱、电话号码、数字ID进行严格的格式验证。例如用户ID应为整数使用intval()或filter_var($input, FILTER_VALIDATE_INT)进行转换和验证。最小化数据库权限连接数据库的账户应仅具有应用所需的最小权限通常是SELECT,INSERT,UPDATE,DELETE避免使用GRANT ALL或具有FILE,PROCESS等高级权限的账户这样即使发生注入攻击者能造成的破坏也有限。框架与ORM使用成熟的PHP框架如Laravel, Symfony, ThinkPHP或其内置的ORM/查询构造器。它们通常默认使用参数化查询并提供了更安全、便捷的数据库操作接口。5. 实战排查与深度问题解析在实际开发和安全审计中遇到疑似编码相关的问题可以按照以下思路进行排查。5.1 问题排查清单当你发现一些特殊字符特别是中文、反斜杠、引号导致数据库查询出错、数据乱码或存在安全疑虑时请按顺序检查检查点正常情况异常可能排查命令/方法1. 连接字符集连接后立即设置为utf8mb4。未设置或设置为gbk,latin1等。SHOW VARIABLES LIKE ‘character_set_connection’;(MySQL)2. 数据库/表/列字符集统一为utf8mb4。不一致如库是utf8mb4表是gbk。SHOW CREATE DATABASE dbname;SHOW CREATE TABLE tablename;3. 网页/HTTP字符集HTML Meta标签或HTTP头声明meta charset”UTF-8″。未声明或声明错误浏览器错误解码。查看网页源代码或浏览器开发者工具Network标签。4. 数据传输编码表单提交、Ajax请求使用UTF-8编码。表单accept-charset缺失或为其他编码。检查表单标签或Ajax请求头Content-Type。5. PHP内部处理使用mb_internal_encoding(‘UTF-8’);。默认内部编码可能不是UTF-8影响字符串函数。echo mb_internal_encoding();6. 转义函数使用不应依赖addslashes应使用参数化查询。仍在使用addslashes或mysql_real_escape_string且字符集不安全。搜索代码中的相关函数调用。5.2 常见疑难场景解析场景一数据存入数据库是乱码但网页显示正常。这通常是“连接字符集”与“数据库存储字符集”不一致导致的“双重编码”或“错误解码”。例如连接字符集是latin1但PHP以UTF-8发送了中文字符。MySQL用latin1错误地解释了UTF-8字节流并存储。当读取时连接字符集若改为UTF-8就会错误地解读存储的字节可能显示为乱码或“黑方块”。根治方法是确保从连接到存储全程统一为utf8mb4并修正已错误存储的数据。场景二使用了PDO但似乎仍有注入风险。检查PDO的连接参数和模拟预处理Emulated Prepared Statements。在早期PDO或某些配置下为了兼容不支持原生预处理的数据库PDO可能会在客户端“模拟”预处理即内部进行转义拼接。这仍然可能受到字符集影响。确保连接时禁用模拟预处理new PDO($dsn, $user, $pass, [PDO::ATTR_EMULATE_PREPARES false, PDO::ATTR_ERRMODE PDO::ERRMODE_EXCEPTION]);设置了正确的字符集可以在DSN中指定如mysql:host…;dbname…;charsetutf8mb4或连接后执行SET NAMES。场景三从文件或第三方API读取的数据出现编码问题。使用mb_detect_encoding()探测编码然后用mb_convert_encoding()转换到内部统一使用的UTF-8。不要猜测编码特别是处理用户上传的文件或爬取外部数据时。5.3 安全开发习惯养成将“统一使用UTF-8utf8mb4”作为项目启动的强制规范。在数据库设计、连接配置、HTML模板、PHP脚本中保持一致。在代码审查中将任何直接的SQL字符串拼接标记为高危无论是否使用了转义函数。推动团队使用查询构造器或ORM。对新人进行安全编码培训讲清楚“宽字节注入”这类漏洞的原理理解为什么“转义”不是银弹。在测试阶段将特殊字符集尤其是GBK, BIG5下的输入测试纳入用例尝试输入包含单引号、反斜杠的中文字符串观察系统行为。字符集与安全这个坑表面上看是“運”字或者一个反斜杠的问题深层次反映的是我们对数据流动全过程缺乏一致性的控制。在全球化、多语言的互联网环境下UTF-8已经成为事实上的标准。尽早拥抱它并配合使用参数化查询这类从根本上隔离代码与数据的技术才能让我们的应用在安全性和稳定性上走得更远。毕竟安全不是靠一两个函数“防住”的而是靠一套严谨、一致的处理体系“构建”出来的。

相关新闻

分布式任务调度平台XXL-JOB核心原理与实践指南

分布式任务调度平台XXL-JOB核心原理与实践指南

1. 为什么需要分布式任务调度平台在现代互联网应用中,定时任务几乎无处不在:报表生成、数据同步、日志清理、消息推送...传统单机定时任务方案(如Spring的Scheduled)在单体架构时代尚可应付,但在微服务、分布式环境下会…

2026/10/13 14:39:36 阅读更多 →
brag故障排除:常见问题与解决方案大全

brag故障排除:常见问题与解决方案大全

brag故障排除:常见问题与解决方案大全 【免费下载链接】brag You built it. Now brag. Turn the project you just created into a short, shareable launch video with one command. 项目地址: https://gitcode.com/gh_mirrors/brag1/brag 你是否在使用brag…

2026/10/14 6:27:01 阅读更多 →
终极教程:使用Gemma-SEA-LION-v4.5-E2B-IT-8bits构建企业级AI应用

终极教程:使用Gemma-SEA-LION-v4.5-E2B-IT-8bits构建企业级AI应用

终极教程:使用Gemma-SEA-LION-v4.5-E2B-IT-8bits构建企业级AI应用 【免费下载链接】Gemma-SEA-LION-v4.5-E2B-IT-8bits 项目地址: https://ai.gitcode.com/hf_mirrors/mlx-community/Gemma-SEA-LION-v4.5-E2B-IT-8bits 想要快速构建企业级AI应用&#xff1f…

2026/10/3 5:05:17 阅读更多 →

最新新闻

Claude Code 安装配置完全指南:从Node环境到终端AI编程助手

Claude Code 安装配置完全指南:从Node环境到终端AI编程助手

简介:面向开发者的Claude Code安装配置代码包,聚焦智能终端助手在Windows、macOS、Linux三平台下的部署与集成,帮助读者完成Node.js环境准备、全局安装、API密钥配置,并快速接入Visual Studio Code与JetBrains IDE。包体共3个文件…

2026/10/14 23:23:41 阅读更多 →
在线火山图绘制全攻略:从双色到四色的参数与实操

在线火山图绘制全攻略:从双色到四色的参数与实操

1. 先看懂火山图:坐标轴、阈值线、颜色的底层逻辑很多人上来就找按钮,先问“火山图在哪里”。我的建议是,先用两分钟理解这张图的坐标和阈值在说什么。你知道了每个参数背后的含义,在线工具里的选项就不会让你犯迷糊,出…

2026/10/14 23:23:41 阅读更多 →
大模型训练数据管线全拆解:从清洗去重到Tokenize分片

大模型训练数据管线全拆解:从清洗去重到Tokenize分片

1. 从数据视角切入大模型训练:为什么值得深挖做模型训练的人都有一个共识:架构决定上限,数据决定下限。很多人拿到一个开源大模型,第一反应是去看它的网络结构——注意力怎么设计的、位置编码用的什么方案、归一化放在哪里。但真正…

2026/10/14 23:23:41 阅读更多 →
手写Tool Use的三种实战方案:从字符串解析到AST沙箱

手写Tool Use的三种实战方案:从字符串解析到AST沙箱

1. “手写 Tool Use”到底在考什么:面试官没说破的三层潜台词“面试官让我手写一个 Tool Use,我用了 3 种方案”——这个标题乍看像段程序员日常吐槽,但背后藏着当前大模型应用开发岗位最核心的能力筛选逻辑。我带过十几轮校招和社招技术面&a…

2026/10/14 23:23:41 阅读更多 →
智能体五类伤害与三方责任:风险控制与实操指南

智能体五类伤害与三方责任:风险控制与实操指南

1. 从“智能体闯祸”说起:为什么五类伤害与三方责任值得每个从业者吃透智能体这两年被聊得很多,从自动订票、自动写代码,到在业务系统里替人做决策、调资源、发指令,能力边界一路外扩。但真正在一线做过落地的人都知道&#xff0c…

2026/10/14 23:23:41 阅读更多 →
Delphi中用TPaintBox生成显示二维码,并保存为PNG回读Image的完整实践

Delphi中用TPaintBox生成显示二维码,并保存为PNG回读Image的完整实践

1. 从画布到文件:为什么用PaintBox而不是直接放Image最近在做桌面端工具时,遇到一个挺典型的交互需求:界面某处要根据数据动态生成二维码,当场显示给用户看,同时提供一个“保存图片”的按钮,把二维码落盘。…

2026/10/14 23:22:37 阅读更多 →

日新闻

AnyPS5通用适配层设计:兼容性与性能的工程取舍

AnyPS5通用适配层设计:兼容性与性能的工程取舍

1. 从"AnyPS5"这个名字说起:它到底想解决什么问题第一次看到"AnyPS5"这个标题,我脑子里冒出来的第一个念头是:这大概率是一个围绕"跨平台运行"或者"通用适配"做文章的项目。名字里的"Any"…

2026/10/14 0:00:41 阅读更多 →
Flutter跨平台开发实战:二手交易应用与HarmonyOS适配

Flutter跨平台开发实战:二手交易应用与HarmonyOS适配

1. 项目概述:Flutter跨平台二手交易应用开发作为一名长期从事移动应用开发的工程师,我最近完成了一个基于Flutter框架的二手物品交易平台项目。这个项目最吸引我的地方在于它完美结合了Flutter的跨平台优势与HarmonyOS的生态特性,实现了"…

2026/10/14 0:00:41 阅读更多 →
DFS算法解决飞机降落调度问题实战

DFS算法解决飞机降落调度问题实战

1. 项目背景与核心思路飞机降落问题是一个经典的调度优化场景,也是算法竞赛和面试中的高频题目。这个题目要求我们合理安排多架飞机的降落顺序和时间,确保所有飞机都能安全着陆而不发生冲突。在实际航空管制中,这类问题直接关系到飞行安全和机…

2026/10/14 0:00:41 阅读更多 →

周新闻

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

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

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

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

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

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

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

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

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

2026/10/14 3:29:58 阅读更多 →

月新闻

我发现了一个新思路:用 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/13 22:01:27 阅读更多 →
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/14 22:45:47 阅读更多 →
黑夜航拍船只数据集训练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/14 7:53:34 阅读更多 →