SSTI 这几个字母我第一次看到的时候还以为是什么新的传输协议缩写翻了几篇入门题解才反应过来它说的是服务端模板注入Server-Side Template Injection。这套 ctfshow web 入门题单里的 SSTI 系列基本就是带你从零把这条链路走一遍先认出页面上那个不起眼的输入框会被拼进模板再一步步判断后端用的是哪家的模板引擎最后理解为什么一个{{7*7}}能变成 49而另一个表达式能一路摸到系统命令。如果你刚做完 SQL 注入和文件包含那几关正卡在 SSTI 这里不得其门而入或者你是写后端代码的想知道自己项目里那句render_template_string到底有多危险那这篇东西应该能帮上忙。我按我自己做题和后来做代码审计的顺序来写从原理、判断、实测到防御尽量把每一步为什么这么做讲透而不是甩一串 payload 让你背。1. 先把 SSTI 这件事的本质讲明白1.1 模板引擎到底在替我们做什么要理解注入得先理解模板引擎存在的意义。后端渲染页面的痛点是HTML 骨架是死的数据是活的。一个商品列表页标题、价格、库存数量每次都不一样但你不可能为每个商品写一个 HTML 文件。于是就有了模板——把固定的 HTML 骨架写成一个带占位符的文件运行时把真实数据塞进占位符引擎负责替换并输出最终 HTML。这个过程叫渲染。用生活化的类比模板就是一张已经印好横线的填空题试卷模板引擎是批卷老师手里的红笔它认得这里该填名字这里该填分数然后把你的答案抄上去。关键在于老师抄的是答案不是题目。你在姓名栏写张三试卷上就出现张三你写请把所有考生的分数改成 100老师也不会照做因为那是答案不是指令。SSTI 出问题的地方就在这个边界上。如果开发者偷懒把用户输入直接拼进了试卷本身——也就是模板源码——而不是作为答案传进去那么用户输入的内容就会被引擎当成模板语法来解析。这时候你写的不再是答案而是新的题目。引擎会老老实实执行你写的表达式这就叫模板注入。1.2 用户输入被当成了代码而不是数据我用一段最小化的 Flask 代码来对比这样最直观。假设要做一个打招呼的页面# 有问题的写法用户输入被拼进了模板源码 from flask import Flask, request, render_template_string app Flask(__name__) app.route(/) def index(): name request.args.get(name, world) return render_template_string(h1Hello name /h1)注意h1Hello name /h1这一行。字符串拼接发生在渲染之前拼接完成后的结果才是真正的模板。所以当你传?name{{7*7}}进去的时候最终交给引擎的模板变成了h1Hello {{7*7}}/h1引擎看到{{ }}就当成表达式求值输出h1Hello 49/h1。正确的写法只有一处差别但性质完全不同# 正确写法用户输入作为变量传入永远不参与模板源码构造 app.route(/) def index(): name request.args.get(name, world) return render_template_string(h1Hello {{ name }}/h1, namename)这时候模板源码是固定的name只是被填进{{ name }}这个占位符的值。哪怕你传?name{{7*7}}页面显示的也是原样的{{7*7}}字符串因为引擎在替换变量的时候不会对变量值做二次解析——它已经把值当成文本处理了。这个差别就是 SSTI 的全部成因。记住一句话模板是代码变量是数据代码和数据绝不能混在一条字符串里。这跟 SQL 注入里SQL 语句是代码参数是数据的道理一模一样只是一个发生在数据库协议层一个发生在视图渲染层。我在审代码的时候只要看到render_template_string(后面跟着一个字符串拼接或者.format()基本就可以直接标红了。1.3 它跟 SQL 注入、XSS 到底差在哪新手最容易混的就是这三个。它们的共同点是把不可信数据当成了可执行内容但执行的位置和危害边界完全不同维度SQL 注入SSTIXSS执行位置数据库服务进程应用服务端进程受害者浏览器执行主体数据库引擎模板引擎JavaScript 引擎危害边界数据库及文件系统权限应用进程权限常常等于服务器权限当前用户会话典型探测 OR 11--{{7*7}}scriptalert(1)/script影响面数据泄露、篡改数据泄露、命令执行、内网横向会话劫持、钓鱼这张表里最值得盯的是执行位置那一行。XSS 再严重执行的也是浏览器里的 JS攻击者拿到的是用户会话SSTI 是在服务端进程里执行跑在什么用户下就是什么权限很多时候 Web 服务以非 root 用户运行但哪怕这样读配置文件、连内网数据库、横向探测都已经够用了。我在做代码审计时有个习惯先把所有用户可控输入 → 字符串拼接 → 进入解释器的路径梳理一遍。数据库驱动、模板引擎、表达式引擎比如某些规则引擎、SpEL、Shell 调用全都算。SSTI 之所以在真实业务里比 SQL 注入少见但危害更大就是因为一旦模板里能打通到 Python 的内置对象体系等于直接把解释器的一部分能力暴露给了外部后面的路太宽了。2. 入门题的题型结构拆解2.1 这类题目通常长什么样Web 入门题单里的 SSTI 系列页面形态一般都很朴素一个输入框或者 URL 上一个参数提交之后页面回显Hello xxx之类的问候语。有少数会包装成一个名片生成器公告预览之类的场景本质一样——你输入的内容被渲染到了页面上。有意思的是题目难度是递进的。前几关通常不做任何过滤就是让你确认{{7*7}}会变成 49中间几关开始过滤关键字比如把{{、}}、_、os、class这些词拉黑逼你去想怎么用字符串拼接、编码、过滤器来绕后面几关可能干脆不回显了只能靠布尔条件盲注或者把回显藏进了某个看不到的地方。我个人的破题习惯是先不打 payload先看页面。原因很简单题目如果给了源码提示有些会在注释里写用的是哪个框架能省掉一半的判断时间如果页面有报错回显报错信息里往往直接带着引擎名称和版本号这是最快的指纹。新手常犯的错是上来就贴一段从别处抄来的长 payload页面没反应就开始怀疑人生其实连引擎是哪家都还没确认。2.2 破题顺序先判断有没有再判断是哪一种我给自己定了一个固定的三步走做题和真实评估都能用。第一步确认注入点存不存在。用最无害的算术表达式探因为算术表达式在任何模板引擎里的解析逻辑都一致不会有副作用也不会被业务逻辑干扰。常用的几个探针是{{7*7}}、${7*7}、% 7*7 %、#{7*7}、{7*7}、${{7*7}}。如果页面输出49基本上就实锤了如果原样输出{{7*7}}说明输入被当成了纯文本这条路径大概率是安全的如果页面报 500那可能是语法错误反而说明内容被解析了值得继续挖。第二步识别是哪家引擎。不同引擎的语法、可用的对象链完全不同判断错了后面全是白费功夫。这里有个很省事的小技巧用{{7*7}}。在 Jinja2Python里7乘以字符串7会得到重复七次的字符串7777777而在 TwigPHP里7*7会被当成数值运算输出49。一个表达式就能区分这两家最容易搞混的引擎比翻报错快多了。第三步摸清过滤规则和回显方式。这一步决定你后面能走多远。过滤可能是黑名单禁止某些子串、白名单只允许字母数字、长度限制也可能是在 WAF 层做的。回显可能直接渲染在页面上也可能被转义了还可能根本不回显只能盲猜。我在纸上会把每一步的试了什么、返回什么、推断什么记下来做到第十关的时候回头看规律就出来了。2.3 常见引擎的指纹对照表这张表我贴在笔记里很久了入门阶段够用模板引擎主要语言探测表达式预期回显备注Jinja2Python{{7*7}}49配合{{7*7}}输出7777777TwigPHP{{7*7}}49配合{{7*7}}输出49SmartyPHP{$smarty.version}版本号字符串支持{7*7}输出 49VelocityJava#set($x7*7)$x49语法带#指令FreeMarkerJava${7*7}49部分配置下直接报错ThymeleafJava[[${7*7}]]49需要内联表达式开启EJSNode.js% 7*7 %49语法与 ERB 一致HandlebarsNode.js{{7*7}}原样输出或报错默认不支持任意表达式ERBRuby% 7*7 %49Ruby 生态常见注意这张表只用于本地靶场和无授权的自查环境。任何真实系统上的探测都必须拿到书面授权且探测行为本身要控制在不影响业务的最小范围内我不建议在任何非授权环境里试第一个表达式。表里没有覆盖到的引擎还有不少但做题的时候遇到陌生语法最快的办法是搜一下报错原文里的模板文件路径或者类名比猜快得多。3. 从探测到出结果的完整实操链路3.1 第一轮探针只用算术表达式我拿一道典型的无过滤入门题演示。页面是一个输入框提交后回显你的名字是xxx。我第一次输入的是abc页面回显你的名字是abc说明输入确实被渲染了但还不能判断是否被模板解析。第二轮输入{{7*7}}。页面回显你的名字是49。到这里可以确定两点一是字符串被拼进了模板源码二是后端用的是支持{{ }}语法的引擎。第三轮输入{{7*7}}。页面回显你的名字是7777777确认是 Jinja2排除了 Twig。这三轮下来一共花了不到一分钟全程没有触发任何危险操作。我在给别人讲的时候会反复强调这个顺序的意义——先确定有没有再确定是哪家最后才谈利用。很多人跳步结果在 Twig 环境里用 Jinja2 的对象链怎么调都是空白白浪费两小时。顺手补一句如果是 Java 环境探针要换成${7*7}和#{7*7}因为这俩在 Spring EL 和部分表达式引擎里表现不同遇到 Thymeleaf 得用[[${7*7}]]因为它有自己的内联规则。这类差异只能靠平时积累没什么捷径。3.2 第二轮确认上下文和可用对象确认了 Jinja2 之后下一步是摸清我能碰到什么。Jinja2 在渲染时会往模板上下文里塞一些全局对象比如configFlask 应用的配置、request当前请求对象、url_for、get_flashed_messages、lipsum、cycler、joiner等等。这些对象本身是给模板开发用的便利工具但在注入场景下它们就是通往底层 Python 运行时的跳板。最温和的一步是尝试{{config}}。在 Flask 环境里这会直接打印出应用的配置字典。很多入门题的第二问就是藏在config里的——比如SECRET_KEY泄露之后可以伪造 Flask 的 session 拿到管理员权限这是 CTF 里非常经典的一条组合拳。我在真实项目里见过更离谱的配置里直接放着数据库连接串和第三方 API 密钥一个 SSTI 直接把整条供应链都交出去了。如果没有config比如不是 Flask 环境或者被过滤了就试{{request}}看能不能拿到请求对象或者试{{lipsum}}看看有没有被沙箱限制。这一步的产出是知道自己能不能访问__globals__、能不能调用函数、有没有沙箱拦截。有了这些信息后面的路才好规划。这里要特别提醒一句{{config}}之所以危险是因为它绕过了一切业务逻辑直接读取运行时配置。你自己写代码时如果用到render_template_string哪怕是内部可信输入也得掂量一下这个口子值不值得开。3.3 第三轮属性访问链的构造逻辑Jinja2 里从一个表达式走到能执行命令中间的桥梁是 Python 的对象模型。我把这条链的原理拆开讲理解了原理payload 就不用背了。Jinja2 表达式支持属性访问和方法调用。Python 里所有东西都是对象字符串是str类的实例而每个类都有__class__属性指向它的类类又有__mro__方法解析顺序指向它的继承链object是所有类的根object.__subclasses__()能列出当前进程里所有已加载的object子类。这是一张巨大的网只要能在里面找到某个能执行命令的类就能打通。比如subprocess.Popen是object的子类在__subclasses__()返回的列表里通常能按索引找到。找到之后通过.__init__.__globals__可以访问到该函数所在模块的全局命名空间里面往往就有os、sys、__builtins__这些模块而这些模块里的popen、system、__import__就是执行入口。用生活化的话说__class__是我从哪个类来__mro__是我的祖谱__subclasses__()是全城所有人的花名册__init__.__globals__是这个人的办公室钥匙串。你从自己身上出发拿到祖谱翻到全城花名册找到一个办公室有钥匙的人然后拿到钥匙串。这条路径之所以存在是因为 Python 的对象模型本身在设计上就是开放内省的这不是模板引擎的锅是语言特性带来的双刃剑。在入门题里最常见的几条捷径是{{lipsum.__globals__[os].popen(ls).read()}} {{cycler.__init__.__globals__.os.popen(id).read()}} {{request.application.__globals__.__builtins__.__import__(os).popen(whoami).read()}}三条链的原理一样都是从上下文里一个现成对象出发通过__globals__拿到模块字典再找到os。为什么选这三个对象因为它们默认就在 Jinja2 的全局上下文里不需要沙箱突破也不需要猜索引最省事。做题时优先试这三条成功率最高。如果这些被过滤了再退回到__subclasses__()那条经典路径用索引去找。找索引的办法是先输出整个列表然后肉眼或者写脚本找subprocess.Popen、os._wrap_close这些名字。我一般会在本地起个一样的 Python 版本做对照因为不同版本__subclasses__()的顺序不一样直接抄别人的索引号经常不灵——这是新手最常见的坑之一。3.4 过滤绕过的几种常见思路中间几关开始上过滤我总结了几类基本覆盖入门题的难度。引号被过滤。如果单双引号都被干掉{{.__class__}}就写不出来了。这时候可以用request.args把字符串从 URL 参数里带进来比如{{request.args.a}}然后 URL 上传?a__class__。也可以用 Jinja2 的chr拼接或者干脆用dict()、()|list之类的构造出字符串。下划线被过滤。__class__里的下划线没了写法要变。最常用的是attr过滤器{{|attr(__class__)}}因为过滤器参数里的字符串可以绕开对下划线的直接匹配。或者用十六进制、八进制转义比如{{[\x5f\x5fclass\x5f\x5f]}}。还有一种是用request.args传参把危险字符藏在参数里。点号被过滤。属性访问除了.还能用中括号{{[__class__][__mro__]}}中括号里可以放字符串灵活性更高。Jinja2 里|attr也是替代方案。关键字被过滤。os、system、popen被拉黑时可以用字符串拼接os或者用|join、|reverse这些过滤器把字符拼出来。比如{{|attr(__class__)}}。再狠一点可以用 Jinja2 的{% set %}先赋值再拼接。整套花括号被过滤。这个比较少见但如果{{被过滤了可以试试{%、{#或者其他引擎特有的语法有时候能用{% print %}绕。也有题是通过头部、请求方法或者其他参数位置把 payload 送进去。我个人的经验是绕过这关的关键不在技巧多而在信息足。知道过滤规则是什么绕过就是一道填空题。所以卡住的时候别急着换 payload先去测一下到底哪些字符被拦了通常试两三轮就能把黑名单摸出来。3.5 本地复现环境自己搭一个比刷题更值我强烈建议每个学 SSTI 的人都自己搭一遍环境。原因很实在题目里的报错你看不懂但本地环境里你可以直接看源码、打断点、加日志理解速度快十倍。最小可复现环境就一个文件from flask import Flask, request, render_template_string app Flask(__name__) app.config[SECRET_KEY] local-test-key app.route(/) def index(): name request.args.get(name, world) # 故意写成拼接只用于本地复现 return render_template_string(h1Hello name /h1) if __name__ __main__: app.run(host127.0.0.1, port5000, debugTrue)装好 Flask 之后python app.py浏览器访问http://127.0.0.1:5000/?name{{7*7}}看到 49 就对了。然后你可以把render_template_string换成render_template或者把拼接改成变量传参对比一下行为差异这个对比比看十篇文章都管用。再进一步你可以自己加一个黑名单模拟题目里的过滤然后练习绕过。这种出题人视角的练习特别有价值因为你出题的时候会想我怎么防而防守思路和攻击思路是互通的。我第一次自己写过滤规则的时候才发现很多我以为能防住的写法其实一绕就过这个认知直接改变了我后来写代码的习惯。4. 把做题经验搬到真实代码审计里4.1 真实业务里的 SSTI 长什么样CTF 题目是浓缩的、刻意的真实业务里的 SSTI 往往藏得很深。我复盘过几类典型场景。第一类是模板片段拼接。比如做一个邮件模板系统允许运营在后台配置邮件正文正文里支持变量占位符后端为了灵活直接render_template_string(user_config data)。看起来是内部人员操作风险可控但后台账号一旦被盗或者某个接口存在越权这条链就直接通了。第二类是报表和规则引擎。有些业务需要让用户配置简单的计算规则比如满 100 减 20后端用表达式引擎求值。表达式引擎和模板引擎在原理上是近亲用户可控的表达式字符串一旦直接求值效果和 SSTI 一样。第三类是文件名、错误信息渲染。这个最隐蔽有时候模板里拼的只是错误提示看起来无害但错误提示里包含了用户输入的用户名或者路径拼进模板后照样能注入。我在一次自查里就发现过一个 500 页面把请求路径直接渲染进了错误模板虽然没被利用但性质是明确的。第四类是第三方组件默认行为。某些脚手架或者低代码平台为了让用户自定义页面把模板编辑能力暴露给了前端这是把整台服务器的钥匙交出去。这类问题在做技术选型的时候就要警惕——任何允许用户提交模板源码的功能都必须假定用户是恶意的。4.2 从代码到配置的加固清单防御 SSTI 这件事分层做比单点做靠谱。我按优先级列一下我自己项目里会做的层级措施说明编码规范禁止字符串拼接构造模板模板源码必须是常量用户输入只走变量传参编码规范禁用render_template_string处理外部输入该函数应仅用于完全可控的静态片段运行时使用沙箱环境Jinja2 提供SandboxedEnvironment会拦截危险属性访问运行时限制模板可用全局对象清理__globals__、__builtins__等上下文对象配置关闭 debug 和生产环境报错回显报错信息是最快的指纹来源配置敏感配置不放在可被模板读取的对象里减少config泄露的损失运维Web 进程最小权限运行即使被突破也限制影响范围边界WAF 规则补充只能作为兜底不能作为主要防线表格里我特意把编码规范放在最前面因为后面所有措施都是补救。模板拼接这个问题只要写代码的时候守住模板源码是常量这条线压根不会有 SSTI。沙箱这层要单独说一下Jinja2 的SandboxedEnvironment不是万能的历史上出过多次绕过所以它是纵深防御的一层不是免死金牌。我在需要让用户自定义模板的场景里做法是沙箱 白名单变量 独立低权限进程三道一起上。还有一个容易被忽略的点日志和报错。生产环境把堆栈信息暴露给前端等于把引擎版本、模板路径、甚至部分源码直接送给对方。这个成本极低收益极高但很多项目就是懒得关。5. 卡住时的排查思路和速查表5.1 报错信息怎么读做题时页面报 500很多人第一反应是payload 错了其实报错信息里藏着大量信息值得逐字看。如果报错里出现jinja2.exceptions.TemplateSyntaxError说明语法层面就错了可能是括号不匹配也可能是花括号被转义了。如果出现UndefinedError说明表达式语法没问题但引用的变量在上下文里不存在这时候要换个对象试。如果出现SecurityError或者类似的沙箱拦截提示说明沙箱生效了得换绕过思路。如果报错里直接带出了模板文件的绝对路径那说明回显控制没做好这本身就是个信息泄露。我的习惯是把报错原文复制下来去掉自己 payload 的部分剩下的就是引擎的自白。入门阶段这么做能快速建立对引擎行为的直觉比看文档快。5.2 常见问题速查现象可能原因排查方向输入{{7*7}}原样输出输入被当文本处理未拼进模板换参数位置检查是否有二次编码输入后页面空白或 500语法错误或表达式被拦截看报错原文简化表达式重试{{7*7}}出 49 但{{7*7}}也出 49后端是 Twig 而非 Jinja2换 PHP 侧的思路{{config}}报未定义非 Flask 环境或上下文被清理换lipsum、request等对象试探属性链能读不能执行沙箱拦截了函数调用找沙箱允许的方法或绕过滤器抄来的索引号无效Python 版本不同子类顺序不同本地起同版本环境重新查索引过滤了引号导致无法构造字符串黑名单命中用request.args传参或字符拼接页面完全无回显盲注场景用条件表达式配合布尔差异判断这张表我自己用了很久卡住的时候对着过一遍八成能定位到问题在哪一层。真正难的从来不是 payload 本身而是判断现在卡在没注入、注入了但被过滤还是执行了但没回显这三个状态中的哪一个。5.3 几个我踩过的坑第一个坑是在错误的引擎上套模板。有一关我怎么试都不对最后发现后端是 Twig我一直在用 Jinja2 的__class__链PHP 里根本没有这套东西。从那以后我养成了先跑指纹再动手的习惯多花十秒省下两小时。第二个坑是忽略了 URL 编码。花括号、下划线在某些位置会被中间层处理掉尤其是经过多次跳转或者被前端 JS 处理过的参数。我现在的做法是抓包看原始请求确认发出去的内容和我想的一致再判断是不是过滤。第三个坑是字符串拼接里少了个空格或者引号。这类低级错误在调试阶段特别浪费时间因为报错信息往往指向模板内部不指向你的输入。我的解决办法是把 payload 拆短一段一段加每加一段验证一次出问题立刻知道是哪一段。第四个坑是以为过滤了关键字就没戏了。实际上黑名单几乎永远可以被绕因为模板语法的表达能力太强了。真正有效的防御只有白名单和沙箱黑名单只是提高门槛。这个认知对做题和写代码都适用。6. 一点个人体会这套入门题我从头到尾刷了两遍第一遍是照着题解抄 payload刷完其实什么都没记住第二遍是自己搭环境、自己出题、自己绕才算真正入门。SSTI 这个知识点最值得学的地方不是那几个固定的对象链而是它背后那个通用的思维方式——凡是数据被当成代码执行的地方就有注入。SQL 是这样模板是这样表达式引擎、命令拼接、反序列化全都是同一个道理。我现在审代码的时候看到任何eval、exec、render_template_string、format拼接、动态import脑子里会自动亮红灯。这个直觉不是背书背出来的是把这类题目一道道啃下来之后长出来的。如果你正在学这块我给的建议就一条别急着找现成的长 payload先把为什么这个表达式能返回 49和为什么那个属性能摸到系统命令这两件事想透剩下的都是排列组合。环境一定自己搭报错一定自己读绕不过去的时候先测过滤规则而不是换 payload。这三件事做到了SSTI 这关基本就过了。
企业数字化 ERP 产品动态
相关推荐
Python Cookbook 300+配方精讲:抽象类、排序与工程实践 简介:《Python Cookbook: Everyone can cook delicious recipes 300》是Hernando Abella撰写、Aluna Publishing House于2024年出版的Python进阶参考读物,面向已掌握基础语法、希望系统提升编程能力的中级开发者与自学者。全书以300余个实用示例为线索&a… · 2026/11/5 9:32:18
滑动窗口与前缀和解决众数问题 1. 问题背景与核心思路 这道题目来自XTUOJ在线判题系统,要求我们解决一个关于"众数"的有趣问题。题目大意是:给定一个数组和最多允许的操作次数k,每次操作可以将任意一个元素加1,问经过最多k次操作后,数组中… · 2026/11/5 9:32:18
X For You Feed:深度学习驱动的智能推荐系统架构解析 1. 项目概述在信息爆炸的时代,个性化推荐系统已经成为各大内容平台的核心竞争力。X For You Feed作为新一代推荐系统架构,通过深度学习技术的深度应用,实现了从传统协同过滤到端到端智能推荐的跨越式升级。这套系统架构最显著的特点是实现了从… · 2026/11/5 9:32:18
西门子S7-200 PLC与组态王在传送带控制系统中的应用 1. 项目背景与核心价值传送带控制系统在工业生产中扮演着至关重要的角色,特别是在物料输送、分拣和装配线上。传统的手动控制方式效率低下且容易出错,而基于PLC(可编程逻辑控制器)和SCADA(监控与数据采集系统ÿ… · 2026/11/5 9:34:20
BookStack集成阿里云OSS实现文件存储分离 1. 项目背景与需求分析BookStack作为一款轻量级的开源Wiki系统,凭借简洁的界面和易用的功能深受开发者喜爱。但在实际使用中,我发现了一个普遍存在的痛点:所有上传的图片和附件默认存储在应用服务器本地,随着内容不断积累… · 2026/11/5 9:34:20
数据库分库分表实战:解决亿级数据性能瓶颈 1. 数据库分库分表:从理论到实战的全方位解析 作为经历过多个亿级用户系统的架构师,我深刻理解当数据量突破单机数据库承载能力时的痛苦。记得有一次凌晨三点,我们的订单数据库因为单表数据量超过2亿条,导致核心交易接口响应时间从… · 2026/11/5 9:34:20
本地化部署WEB意见投诉管理系统设计与实践 1. 项目概述:本地化部署的WEB意见投诉管理系统 在景区、物业和生产企业这类需要高频收集用户反馈的场景中,传统纸质登记或单一电话渠道存在明显短板。这套WEB意见投诉管理系统通过本地化部署二维码交互的创新组合,解决了三个行业共通的痛点&a… · 2026/11/5 9:34:20
LLama2推理性能三要素:TTFT、ITL、TGS精准测量与优化 1. 项目概述:一次面向实际部署的模型推理链路全解析“05-28 周二 TTFT, ITL, TGS 计算过程以及LLama2推理代码调试过程”——这个标题看起来像是一条工作日志的快照,但背后藏着当前大模型落地中最关键、最常被忽略的三座技术标尺&… · 2026/11/5 9:34:20
live555 MP4点播实战:自定义FramedSource实现RTSP流媒体 简介:面向具备C基础、希望掌握流媒体点播实现的中高级开发者,这份资源围绕live555 Media Server与MP4点播展开,可用于搭建RTSP点播服务、理解RTP封装与音视频同步机制,也可作为ffmpeg解析MP4元数据、处理客户端Range请求与断点续传… · 2026/11/5 9:33:19
双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/4 3:16:49
STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/4 6:59:29
DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/4 7:02:38
多智能体协作系统架构设计与实践优化 1. 多智能体协作系统的时代背景2017年Transformer架构的诞生彻底改变了人工智能的发展轨迹。当行业还沉浸在单一大模型带来的性能突破时,Anthropic的研究团队已经将目光投向了更前沿的领域——如何让多个智能体通过协作产生超越单体能力的系统智能。这就像交响乐团中… · 2026/11/5 0:00:39
大模型系统性入门实战指南:从原理到部署微调的全链路路线 提到大模型的系统性入门,我先说个结论:这事真不难,难的是资料太散、路线太多,今天这个框架明天那个工具,新人很容易在“收藏夹吃灰”和“教程看到一半放弃”之间反复横跳。市面上关于大模型的内容少说有几十万篇&#… · 2026/11/5 0:00:39
鸿蒙文件系统沙箱机制解析与开发实践 1. 鸿蒙文件系统沙箱机制深度解析作为一名从Android转向鸿蒙开发的工程师,我深刻体会到文件系统访问是迁移过程中最先遇到的"拦路虎"。在Android上运行良好的文件读写代码,在鸿蒙真机环境却频频出现文件写入后神秘消失或直接抛出权限拒绝错误的… · 2026/11/5 0:00:39
双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/4 3:16:49
STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/4 6:59:29
DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/4 7:02:38