跳转至

2019

每周分享第 9 期

  1. IDA Loader plugin for some Nintendo rom https://github.com/w4kfu/IDA_loader
  2. websocket daemon http://websocketd.com/
  3. 实时的游戏开发器 https://script-8.github.io/
  4. 硬核逆向 Leica 相机的固件 https://alexhude.github.io/2019/01/24/hacking-leica-m240.html
  5. 做 CTF 时遇到的整数溢出的 CVE https://www.anquanke.com/post/id/104182
  6. 发现一个 speedtest 的轮子 https://github.com/adolfintel/speedtest
  7. 西数的 RISCV 核实现 https://github.com/westerndigitalcorporation/swerv_eh1
  8. iOS 12.2 会有更多的 PWA 兹瓷 https://twitter.com/mhartington/status/1089292031548145666
  9. 代替死去的 git-up: git config --global alias.up 'pull --rebase --autostash'
  10. 利用已知明文破解旧版加密 zip 的工具 https://github.com/kimci86/bkcrack
  11. 在线看 jwt 内容 https://jwt.io/
  12. JS 的 Lua VM https://github.com/fengari-lua/fengari
  13. 鲁棒但不优雅的前端 KV https://github.com/gruns/ImmortalDB
  14. Emacs Modules doc https://phst.eu/emacs-modules
  15. 用 IPv6 的 Flow Label 实现类似 MPLS 的效果 https://github.com/wenxin-wang/flowlabel-switching
  16. 又一个 JS 实现的表格 https://github.com/myliang/x-spreadsheet 类似以前用过的 handsontable
  17. @shankerwangmiao 推荐的光纤教程 http://www.kepu.net.cn/gb/technology/telecom/fiber/fbr215.html
  18. 用 Rust 写 iOS App https://medium.com/visly/rust-on-ios-39f799b3c1dd
  19. Build Once, Run Anywhere 还行 https://wasmer.io/
  20. 挺科学的 DNS proxy https://github.com/AdguardTeam/dnsproxy

使用 Rust 实现 VirtIO 驱动

背景

最近在给 rCore 添加驱动层的支持。一开始是想做网卡驱动,后来发现, qemu-system-riscv32 只支持如下的驱动:

# qemu-system-riscv32 -device help

Storage devices:
name "scsi-cd", bus SCSI, desc "virtual SCSI CD-ROM"
name "scsi-disk", bus SCSI, desc "virtual SCSI disk or CD-ROM (legacy)"
name "scsi-hd", bus SCSI, desc "virtual SCSI disk"
name "virtio-blk-device", bus virtio-bus
name "virtio-scsi-device", bus virtio-bus

Network devices:
name "virtio-net-device", bus virtio-bus

Input devices:
name "virtconsole", bus virtio-serial-bus
name "virtio-keyboard-device", bus virtio-bus
name "virtio-mouse-device", bus virtio-bus
name "virtio-serial-device", bus virtio-bus
name "virtio-tablet-device", bus virtio-bus
name "virtserialport", bus virtio-serial-bus

Display devices:
name "virtio-gpu-device", bus virtio-bus

Misc devices:
name "loader", desc "Generic Loader"
name "virtio-balloon-device", bus virtio-bus
name "virtio-crypto-device", bus virtio-bus
name "virtio-rng-device", bus virtio-bus

所以要实现网卡的话,只能实现这里的 virtio-net-device ,而 VirtIO 驱动之间有很多共通的地方,于是顺带把 gpu mouseblk 实现了。

第一个驱动 virtio-net 的实现

首先想到并且实现了的是网卡驱动, virtio-net 。最开始的时候,为了简单,只开了一块缓冲区,每次同时只收/发一个包。首先拿了 device_tree-rs 读取 bbl 传过来的 dtb 地址,找到各个 virtio_mmio 总线以后按照设备类型找到对应的设备。然后就是对着 virtio 的标准死磕,同时看 Linux 和 QEMU 的源代码辅助理解,最后终于是成功地把收/发的两个 virtqueue 配置好,并且在中断的时候处理收到的包。这个时候,可以成功地输出收到的包的内容,并且发出指定内容的包了。效果就是看到了这样的图片(图中网站是 Hex Packet Decoder):

基于此,写了一个简单的以太网帧的解析,ARP 的回复和 ping 的回复(直接修改 ECHO_REQUESTECHO_REPLY 然后更新 CHECKSUM),实现了最基本的 ping:

显卡驱动

网卡可以用了,很自然地会想到做一些其他的 virtio 驱动,第一个下手的是显卡。显卡和网卡的主要区别是,网卡是两个 queue 异步作,而在显卡驱动上则是在一个 queue 上每次放一输入一输出的缓冲区来进行交互,具体步骤在 virtio 标准中也写得很清楚。在这个过程中,QEMU 的 Tracing 功能帮了很大的忙,在调试 desc 的结构上提供了很多帮助。

然后就在 framebuffer 上画了一个 mandelbrot:

在 @shankerwangmiao 的建议下,调了一下颜色:

这样就好看多了。

HTTP 服务器

在 @wangrunji0408 的提醒和建议下,我开始把一个 Rust 实现的网络栈 smoltcp 集成到代码中来。这个库中,对底层 Interface 的要求如下:

  1. 当可以发包并且可以收包的时候,返回一收一发两个 Token,并在使用的时候调用指定的函数。
  2. 当可以发包的时候,返回一个发的 Token,含义同上。

这是我第一次看到这种抽象,而且也没有特别明确的文档表示,这个 Token 代表什么,我应该提供什么。我直接按照一些已有的例子,照着实现了一把。过程中遇到了 ownership 的问题,通过 Arc 和 Mutex 解决了,然后又出现了死锁的问题,调了半天才调出来。

接着按照 somltcp 的样例写一个简单的 udp echo server 和(假的)tcp 服务器:

// simple http server
let mut socket = sockets.get::<TcpSocket>(tcp_handle);
if !socket.is_open() {
    socket.listen(80).unwrap();
}

if socket.can_send() {
    write!(socket, "HTTP/1.1 200 OK\r\nServer: rCore\r\nContent-Length: 13\r\nContent-Type: text/html\r\nConnection: Closed\r\n\r\nHello, world!\r\n").unwrap();
    socket.close();
}

虽然很粗暴,但是 work 了:

鼠标驱动和块设备驱动

接着自然是往 QEMU 支持的剩下的 virtio 设备里下手。首先下手的是鼠标驱动。这次遇到了新的问题:

  1. 由于缓冲的存在,每次只有在 EV_SYN 的时候才会一次性把若干个事件放入队列中。
  2. 一个事件就要一个 desc chain,意味着直接串足够大小的 buffer 到同一个 desc chain 中并不能工作。

于是只好痛定思痛照着 Linux 内核的实现把完整的 Virtqueue 的操作实现了,并且顺带把前面的网卡和显卡的驱动也更新了。果然,每次都是三个左右的事件(X,Y,SYN)插入,然后根据这些事件就可以计算出当前的鼠标位置了。

至于块设备,遇到的则是别的坑。看标准的时候,本以为就一个结构体 virtio_blk_req 就搞完了,但仔细读了读,标准似乎没讲清楚,读的时候是怎么传,写的时候又是怎么传。于是在这里卡了很久,从 Tracing 信息可以看出,QEMU 一直认为我提供的 buffer 大小不正确,多次实验之后发现,给 device 写入的 buffer 大小为 block size 的整数倍加一,这个一存放的是状态,其他则是数据,真的太坑了。

有了块设备以后,就可以替换掉原来的内嵌 SFS 的方案,转为直接从块设备读 SFS 文件。这里我没想明白 lazy_static 和 ownership 的一些问题,最后也则是@wangrunji0408 的帮助我解决了。

总结

用 Rust 写出一个可以工作的驱动并不难,只要知道 unsafe 怎么用,但是一旦需要深入思考这里应该用什么安全的方法封装的时候,才发现是个很困难的事情。现在虽然工作了,但是很多地方线程并不安全,代码也不够简洁高效,以后还有很多需要改进的地方。

See also

  1. Virtio Spec

THUWC 2019 小记

前段时间,以工作人员的身份参加了在广州二中举办的 THUWC 2019。作为一只菜鸡 OI 选手,我没想到过我会以另一种身份参与一个我本来没能参与的活动,就好像以暑校辅导员的身份参与清华暑校一样。

提早来到了赛场,布置场地,然后把机考的各个流程都过一遍,记住各个细节,各方面都有条不紊地进行,看着第一场前同学们特别激动地冲入考场,到最后一场同学们考完后的释放,在同学们身上看到了很多不成熟的样子,看到了兴奋想要和同伴分享的喜悦,也看到了不甘的眼泪。

希望各位强大的选手们可以来到九字班、零字班乃至一字班,享受课改的乐趣吧嘿嘿嘿

每周分享第 8 期

这周更加忙了,所以内容不多。

  1. Rust 1.32.0 is out 其中 dbg macro 挺有意思 https://blog.rust-lang.org/2019/01/17/Rust-1.32.0.html
  2. 在线的 hex packet decoder https://hpd.gasmi.net/
  3. Rust Cheatsheet https://www.breakdown-notes.com/make/load/rust_cs_canvas/true
  4. Rust tcp/ip stack https://github.com/m-labs/smoltcp
  5. 逆向 CAJ 的转换器 https://github.com/JeziL/caj2pdf

每周分享第 7 期

这周比较忙,所以内容不多。

  1. 在嵌入式系统里跑 Lisp 可以在串口开个 repl 在线调试 http://www.ulisp.com/show?3J
  2. Pattern matching for C++ https://github.com/solodon4/Mach7
  3. 一个商业版的类似 ASan 的产品 不知道效果如何 https://stensal.com/
  4. 用 Python 写 Cocoa 界面还行 https://dawes.wordpress.com/2017/08/17/python-with-a-cocoa-gui-on-macos/
  5. Rust 实现的权威搜索引擎 https://github.com/toshi-search/Toshi
  6. Rust Embedonomicon 讲述 Rust 在 bare metal 下的一些神奇操作 https://docs.rust-embedded.org/embedonomicon/preface.html
  7. 发现另一个基于 Github 的 Disqus 替代品 https://utteranc.es/
  8. 挺好的一部讲 Rust 生命周期的小书 http://cglab.ca/~abeinges/blah/too-many-lists/book/README.html
  9. 跨平台的 Ctrl-C 处理 for Rust https://github.com/Detegr/rust-ctrlc
  10. 用 Rust 写 stm32 上程序所需要的库 https://github.com/stm32-rs/stm32-rs
  11. go 语言编写的基于 fuse 的加密文件系统 https://github.com/rfjakob/gocryptfs
  12. Rust 的图片解析库 https://github.com/PistonDevelopers/image
  13. 用 BPF 做 API 解析和过滤 https://github.com/cilium/cilium
  14. 学到了 x86 又一个指令集 bmi2 https://github.com/jordanbray/chess
  15. FoundationDB 推出 Record Layer 名字十分贴切 https://github.com/foundationdb/fdb-record-layer/
  16. 一个开源的教室电脑监控系统 https://github.com/veyon/veyon
  17. DNS flag day https://dnsflagday.net/
  18. Rust stable 1.32.0 发布 https://blog.rust-lang.org/2019/01/17/Rust-1.32.0.html dbg 宏好评
  19. 各 MacBook 的 Linux 支持情况一览 https://github.com/Dunedan/mbp-2016-linux

实现 VSCodeVim 中支持中文分词的单词移动

最近用 VS Code 写中文 LaTeX 比较多,但是编辑起来总是比较麻烦,不能用各种带 w 的 motion,不然整行都没了。于是 @xalanq 提出能不能拿一个 JS 的分词库,魔改一下 VSCode Vim 来得到同样效果?答案是可以的。

最后代码在 jiegec/VSCodeVimChinese 里,还没有合并到上游的打算。不定期根据上游发版本同步更新,在 Github Release 里发布 vsix 文件,目前版本为 v1.0.1。在 VS Code 里 Extensions: Install from VSIX... 即可安装。

经过对代码的研究,发现对 motion w 的处理都是通过 getWordLeft getWordRightgetCurrentWordEnd 完成的。于是我修改了这三个函数,根据原来的返回值把字符串喂给分词器,再返回的新的位置。一开始用的是 nodejieba ,但是因为需要用到 node-gyp 遇到了 Node 版本不兼容的问题,于是换了一个纯 Node 的实现 node-segment ,就完成了这个功能。

Grafana 中可视化 Ping 时把 Timeout 显示为指定值

刚遇到一个需求,就是用 Telegraf 收集 ping 信息,然后在 Grafana 里可视化当前的延迟,如果超时了,就显示一个指定值,如 999,这样就可以放到一个 Gauge 里面可视化了。但是,问题在于,Telegraf 的 ping input 在超时的时候只会在 result_code 里写一个 2 ,其他项都是空的,因而如果直接用 GROUP BY time(interval) fill(999) 会导致最新的一个数据经常得到 999。这意味着需要根据 "result_code" 来进行区分 Timeout 的情况。最后捣腾了很久,得到了这个方案:

 select "average_response_ms" * (2 - "result_code") / 2 + "result_code" / 2 * 999 from (select "average_response_ms", "result_code" from ping where $timeFilter fill(0))

最后的方法很粗糙:当 "result_code" 是 0 也就是成功的时候,得到延迟,而当 "result_code" 是 2 也就是超时的时候,直接得到 999。这样就解决了这个问题。

域名已经迁移到 jiege.ch

从买新域名到迁移大概用了一个多小时,现在已经恢复访问。原有的地址也会直接 301 到新的域名上来。

仍然 Host 在 Github Pages 上。还会继续更新,不会跑路的(逃

每周分享第 6 期

今天刚迁移了域名到 jiege.ch,原来的 jiegec.me 会自动跳转过来,链接什么的都不用变。

  1. Rust 的 cfg! 宏只是返回 bool 而不会影响内部是否被编译 如果需要 if constexpr 的效果需要用 cfg-if
  2. 基于 HTML5 canvas 的图表库 Chart.js
  3. Endianness MATTERS! -- Harry Chen
  4. 各数据库的带补全的 CLI https://www.dbcli.com/
  5. 边开发边测试 k8s 部署 tilt
  6. Github unlimited free private repos 于是称为了 PRO 用户 https://blog.github.com/2019-01-07-new-year-new-github/
  7. Github 美食博主还行 https://github.com/hendricius/pizza-dough
  8. 一个用于本地化的库 https://github.com/dustin/go-humanize
  9. Vim Verilog 补全 https://github.com/vhda/verilog_systemverilog.vim
  10. homebridge 网页前端 https://github.com/oznu/homebridge-config-ui-x
  11. Zigbee 2 MQTT Bridge 需要额外的设备 https://github.com/Koenkk/zigbee2mqtt
  12. gdb 的网页 gui https://github.com/cs01/gdbgui
  13. Rust 一键 par iter https://github.com/rayon-rs/rayon
  14. 挺好看的 hex viewer https://github.com/sharkdp/hexyl 可以配合 bat 滚屏
  15. gitalk 基于 Github issues 的评论系统 类似 Disqus https://github.com/gitalk/gitalk
  16. 根据蓝牙连接键盘与否改变键盘布局 https://github.com/jwilm/alacritty/wiki/Automatic-MacOS-Keyboard-layout-change-for-Bluetooth
  17. 8.8.8.8 加入 DoT 家庭 https://security.googleblog.com/2019/01/google-public-dns-now-supports-dns-over.html
  18. 恢复损坏的 QRCode 工具箱 https://merricx.github.io/qrazybox/
  19. 通过同时修改两个芯片的 PLL 达成了非标准的 Wi-Fi 通信还行 https://hackaday.com/2019/01/04/underclocking-the-esp8266-leads-to-wifi-weirdness/
  20. inline x86 asm in go https://github.com/mmcloughlin/avo#readme

调整 Alacritty 的 Powerline 字体显示偏移

今天体验了一下 Alacritty,以前一直在用 iTerm2,但是它的高级功能我都没用到。于是现在用了下 Alacritty,把 Solarized Dark 配置了,发现 Inconsolata for Powerline 字体显示有点偏差,于是调整了一下:

# Font configuration (changes require restart)
font:
  # Normal (roman) font face
  normal:
    family: Inconsolata for Powerline
    # The `style` can be specified to pick a specific face.
    #style: Regular

  # Bold font face
  bold:
    family: Inconsolata for Powerline
    # The `style` can be specified to pick a specific face.
    #style: Bold

  # Italic font face
  italic:
    family: Inconsolata for Powerline
    # The `style` can be specified to pick a specific face.
    #style: Italic

  # Point size
  size: 18.0

  # Offset is the extra space around each character. `offset.y` can be thought of
  # as modifying the line spacing, and `offset.x` as modifying the letter spacing.
  offset:
    x: 0
    y: 0

  # Glyph offset determines the locations of the glyphs within their cells with
  # the default being at the bottom. Increasing `x` moves the glyph to the right,
  # increasing `y` moves the glyph upwards.
  glyph_offset:
    x: 0
    y: 3

主要是这里的 glyph_offset 设置为 3(2 也可以,我更喜欢 3) ,这样箭头就基本对齐了不会突出来。

然后按照官方 Wiki,配置了 Solarized Dark 配色:

## Colors (Solarized Dark)
colors:
  # Default colors
  primary:
    background: '0x002b36' # base03
    foreground: '0x839496' # base0

  # Normal colors
  normal:
    black:   '0x073642' # base02
    red:     '0xdc322f' # red
    green:   '0x859900' # green
    yellow:  '0xb58900' # yellow
    blue:    '0x268bd2' # blue
    magenta: '0xd33682' # magenta
    cyan:    '0x2aa198' # cyan
    white:   '0xeee8d5' # base2

  # Bright colors
  bright:
    black:   '0x002b36' # base03
    red:     '0xcb4b16' # orange
    green:   '0x586e75' # base01
    yellow:  '0x657b83' # base00
    blue:    '0x839496' # base0
    magenta: '0x6c71c4' # violet
    cyan:    '0x93a1a1' # base1
    white:   '0xfdf6e3' # base3

真香

Grafana Variable 的 regex 过滤方式

用 InfluxDB 收集到 Mountpoint 数据的时候,经常会掺杂一些不关心的,如 TimeMachine,KSInstallAction 和 AppTranslocation 等等。所以,为了在 Variables 里过滤掉他们,需要用 Regex 进行处理。网上有人提供了方案,就是通过 Negative Lookahead 实现:

/^(?!.*TimeMachine)(?!.*KSInstallAction)(?!.*\/private)/

这样就可以把不想看到的这些 mountpoint 隐藏,节省页面空间了。当然了,这里其实也可以用白名单的方法进行处理,直接写 regex 就可以了。

Rust 获取 Linker Script 中的地址

在 Linker Script 中可以记录下一个地址到一个变量中,大概这样:

.text: {
    PROVIDE(__text_start = .);
    *(.text .text.* .gnu.linkonce.t*)
    PROVIDE(__text_end = .);
}

这里的 PROVIDE() 是可选的。这样,代码里就可以获取到 .text 段的地址了。在 C 中,直接 extern 一个同名的变量就可以了,但在 Rust 中,需要这样获取:

extern "C" {
    fn __text_start();
    fn __text_end();
}

// __text_start as usize
// __text_end as usize

这样就可以拿到地址了。

每周分享第 5 期

2019 年第一篇博文,祝大家新年快乐。最近忙于期末,没怎么搞事情,所以暂时没有关于别的内容的博文。

  1. 发现一个很好看的 http web server index 就是不再更新了 h5ai
  2. 录制 DOM 变化并且重放 rrweb
  3. C++ 中的 Lazy Sequence 实现 lazyCode
  4. 来自 Berrysoft 的 Windows UWP 校园网认证解决方案 TsinghuaNetUWP
  5. 中科大老运维的笔记 ITTS
  6. Go 源码研究电子书 目测还在编写,不过读来挺有收获的 go-under-the-hood
  7. 第一次了解到 ELF Aux Vectors auxv
  8. 发现了 C99 的参数列表里 static 数组大小语法 static array indicies
  9. 发现一个有趣的包装了 socket 的消息库 支持一些消息分发方法 nanomsg
  10. 找到一个可视化 YUV 和视频解码的一些内部信息的工具 YUVView
  11. 通过动态 QRCode 传输数据 txqr qr-transfer
  12. 发现一些很好看的 CSS 动画 10-stunning-css-3d-effect-must-see 其中最神奇也最复杂的是 这个
  13. Android runtime 中动态获取权限的库 Dexter
  14. 来自 Berrysoft 的 Stream operators in C++ CppLinq
  15. Squirrel (Rime for macOS) 在两年以后终于出了 0.10.0 新版本 有了好看的新皮肤
  16. 神奇的 v8 漏洞利用 实在是太复杂了 exploting math expm1 in v8
  17. JWT How-to 发现 Koa 和 Flask 的 session 默认实现也是同样的原理 learn-json-web-tokens
  18. 配置 DNS CAA 的在线工具 限制 CA 签证书的方案 sslmate caa
  19. 又一个 Python 的 Parser 库 配合有趣的 decorator 语法 sly
  20. 自动配置 webpack 的库 Jetpack jetpack
  21. eBPF tutorial learn ebpf tracing
  22. 图片 xkcd-ify 太有趣了 xkcd-style plots
  23. 又一个 Vue 的 rich text 控件 tiptap