5
31
2017
7

换用 Rust 解析 pcap:快了1000倍!

前天手上有个VPS的提供商发来通知,说VPS在攻击别人的80端口,被第三方投诉了-_-|||

我跑上去看了好久,啥也没发现。进程列表、登录日志、网络使用、各服务我能找到的日志都检查了一遍,也跑了遍 dpkg --verify,啥也没发现。没办法,我就留了句 tcpdump 命令,看看能不能抓到那些恶意流量:

sudo tcpdump -vv -s0 -w port80.log '(dst port 80 and src host 123.456.789.0) or (src port 80 and dst host 123.456.789.0)'

盯了好久,一直只看到有零星的正常访问。

就这么放了一天。第二天想起来的时候过去一看,哇塞,抓到了 2.1G 的包!看来真出问题了。先分析一下得到的 pcap 文件吧。

这么大的 pcap,自然不能拿 Wireshark 打开了。我知道 scapy 能够读取 pcap,于是用它读取 pcap,收集另一方的 IP 地址,看看流量到底都去哪里了。代码很简单,20多行。跑起来~progress 告诉我需要半小时……

于是半小时过去了。我能看到它访问了不少IP,很多是意料之外的。我就想,这些流量是什么时候发生的呢?不过我可不想再花半小时等结果了。然后再想得到点别的数据又得半小时。而且 Python 这种动态类型的语言,稍微复杂一点之后就容易抛异常。说不定快跑出结果的时候,抛个异常出来,前边就全白跑了……

所以就试试 Rust 啦。去 docs.rs 上一搜,还有好几个可以解析 pcap 的库呢。那就第一个吧,叫「pcap」的这个。程序写起来也挺容易的,甚至代码行数都跟 Python 版相仿。时间的处理也是用了一个之前没用过的库「chrono」。并没有花多长时间,直接在文档里找到我需要的功能,然后编译通过就成了 O(∩_∩)O~

extern crate pcap;
extern crate chrono;

use std::collections::BTreeMap;
use chrono::prelude::*;

fn main() {
  let mut capture = pcap::Capture::from_file("../port80.log").unwrap();
  let mut distribution = BTreeMap::new();
  while let Ok(pkt) = capture.next() {
    let t = pkt.header.ts.tv_sec;
    let by_hour = t / 3600 * 3600;
    let dt = Local.timestamp(by_hour, 0);
    *distribution.entry(dt).or_insert(0) += 1;
  }

  for (t, c) in distribution {
    println!("{}: {:8}", t.format("%Y-%m-%d %H:%M"), c);
  }
}

然后花了几秒编译出来优化版本的程序,跑起来~然后我切换到另一个 shell 去跑 progress。啥,没找到进程?再回来一看,哦哦,已经跑完了!只花了一两秒!

这是一千倍的差别啊!虽然两个程序所做的事情并不一样,比如 Python 版用的 scapy,实际上会把整个网络包的各层协议都给解析了,而不像 Rust 版只解析了 pcap 的记录头。但是我又没有别的选择,就像我也不能让 Python 别管引用计数一样。

对于这种小任务,Python 还是强在对少量数据的交互操作,比如 Jupyter Notebook 就很强大。而数据量一旦大起来,就是 Rust 的天下了 :-)

Category: 编程 | Tags: Rust tcpdump
4
23
2017
15

UDP: 谁动了我的源地址?

最近 #archlinux-cn 又流行玩 teeworlds 了,然而我却连不上那个服务器。

情况很奇怪。我能 ping 通服务器 IP,TCP 连接也正常,UDP traceroute 也表现得很正常(对关闭端口能够完成,对开放端口会在最后一跳开始得到一堆星号),并且我连接的时候,服务器能看到我在连接。也就是说,TCP 和 ICMP 都正常,UDP 上行正常,下行出了状况。

难道是有防火墙?首先呢,我能连接其它服务器,说明我这边没有问题;大部分人能连接上服务器,说明服务器那边也没有问题。所以,问题出在路上。也确实有另外的北京联通用户连不上这个服务器。但是很奇怪啊,为什么单单只是这一个 IP 的 UDP 包丢失了呢?

于是继续试验。从最简单的开始,用 netcat / socat 尝试通讯。方向反过来,我监听,服务器那边连接。端口是我在路由器上做过端口映射的。结果是正常的。再来,服务器那边监听,我往那边发,果然我就收不到包了。按理说,UDP 双方是对等的,不应该换了个方向就出问题呀。难道是因为端口映射?Wireshark 抓包看到本地使用的端口号之后,在路由器上映射一下,果然就通了!

然后,我注意到了一件十分诡异的事情:虽然我和服务器能够通讯了,但是我的 Wireshark 上只显示了我发出去的包,却看不到回来的包!我抓包时按服务器 IP 做了过滤,所以,回来的包的源 IP 不是服务器的地址!

重新抓包一看,果然。服务器 IP 是 202.118.17.142,但是回来的包的源 IP 变成了 121.22.88.41……看起来这是联通的设备,在下行 traceroute 时能够看到有节点与它 IP 相似(121.22.88.1)。原来又是这著名的「联不通」又干坏事了 -_-|||

虽然 socat 接收 UDP 时不介意源 IP 变化了,但是 teeworlds 介意啊。并且 NAT 那边也会不知所措。所以,首先得告诉路由器把来自这个 IP 的 UDP 包全部扔给我:

ssh 192.168.1.1 iptables -I FORWARD -i ppp0.2 -p udp -s 121.22.88.41 -j ACCEPT

于是数据包有了。接下来是修正源 IP。我试过 SNAT,无效。这东西似乎只对本地发出的包有用?于是我又用 netfilter_queue 了。这东西很强大呢~一个简单的 Python 脚本搞定:

#!/usr/bin/env python3

from netfilterqueue import NetfilterQueue
from scapy.all import *

def main(pkt):
  p = IP(pkt.get_payload())
  # print('recv', p)
  p.src = '202.118.17.142'
  p.chksum = None
  p[UDP].chksum = None
  pkt.set_payload(bytes(p))
  # print('fixed to', p)
  print('.', flush=True, end='')
  pkt.accept()

conf.color_theme = DefaultTheme()
nfqueue = NetfilterQueue()
nfqueue.bind(1, main)
try:
  nfqueue.run()
except KeyboardInterrupt:
  pass

然后是 iptables 命令:

sudo iptables -I INPUT -s 121.22.88.41 -p udp -j NFQUEUE --queue-num 1 --queue-bypass

scapy 这个神奇的网络库在 Arch 官方源里叫「scapy3k」。Python 的 netfilterqueue 模块需要用我自己修改过的这个版本

2017年7月30日更新:Python 的依赖有点麻烦,所以我又写了个 Rust 版本,放在 GitHub 上了

Category: 网络 | Tags: linux python 网络 iptables Rust
4
15
2017
0

卸载被挂载点掩蔽的挂载点

刚刚遇到一件很囧的事情:我在 /run/user/1000/cache 挂载了我的 SSD,用作火狐和 neocomplete 的缓存。/run/user/1000 这个目录是 systemd 为用户创建的,用来放那些只在运行时有用的文件,比如 pid 文件啦、套接字啦之类的。把挂载点放这里,很显然可以避免为其在磁盘上创建目录,又不必和 /tmp 里的一堆临时文件混在一起。

然而这一次,出大事了!我的火狐启动不了了!我的 Vim 也报了一堆错!细看下来,发现 /run/user/1000/cache 没了……

因为需要 root 权限,/run/user/1000/cache 是在 /etc/rc.local 里挂载的。这一次,它抢先挂载了 /run/user/1000/cache,然后我登录,systemd 帮我挂载了 /run/user/1000。就是下边这个样子:

├─/run                           run        tmpfs          rw,nosuid,nodev,relatime,mode=755                                                shared
│ ├─/run/user/1000/cache         test       zfs            rw,xattr,posixacl                                                                shared
│ └─/run/user/1000               tmpfs      tmpfs          rw,nosuid,nodev,relatime,size=804780k,mode=700,uid=1000,gid=1000                 shared
│   └─/run/user/1000/gvfs        gvfsd-fuse fuse.gvfsd-fus rw,nosuid,nodev,relatime,user_id=1000,group_id=1000                              shared

WTF!这样我就访问不到它了呀!我一开始还以为我的 SSD 又出什么状况了呢,结果是这样,挂载上了,但是访问不到……而因为访问不到,所以也没法卸载……

当然啦,我可以先把 /run/user/1000 和下边的那个 gvfs 给卸载掉。但那样做,我不确定 systemd、PulseAudio、D-Bus 它们会有多生气。bind mount 也尝试了,然而并没有什么用。它只能用来访问被挂载点掩蔽的文件,访问不到被挂载点掩蔽的挂载点。

然后我想到了之前玩过的网络命名空间。当然这次需要的是之前没仔细探索的挂载命名空间了。

直接 sudo unshare -m 进去,findmnt -o+PROPAGATION 发现全部都是 private 的,也就是 umount 了不影响外边。于是我就可以把 /run/user/1000 这个树卸载掉啦。然后 mount --make-shared /run/user/1000/cache 把它变成 shared 状态,再卸载,应该就可以把外边那个也卸载掉了吧?

No。失败了。研究了半天 unshare、mount_namespace 的文档之后确认,把 private 的挂载点变成 shared 之后,会创建一个新的「共享组」,而只有在同一个「共享组」里的挂载点才会相互传播。所以,unshare 你别把我的挂载点都变成 private 了好么?

文档下翻,它还真有这么个选项:

sudo unshare -m --propagation shared

然后里外执行这条命令,确认一下「shared:」后边那个数字是一致的:

# cat /proc/self/mountinfo | grep zfs
282 281 0:47 / /run/user/1000/cache rw shared:137 - zfs test rw,xattr,posixacl

没问题了。先 mount --make-private /run/user/1000 等把它们变成私有的,卸载掉,再把 /run/user/1000/cache 给卸载掉。来外边一看,果然被卸载掉啦~

(然后我还是为其在磁盘上专门建立个目录防止出问题好了。zfs,不知道怎么写 systemd 的 .mount 文件。)

Category: Linux | Tags: linux
3
14
2017
21

我的 zsh 提示符

这是我用了多年的 zsh 提示符。

My zsh prompt

右提示符比较简单,先说。

首先,这个右提示符是 zsh 才支持的,不是 hack 左提示符来的哦。

我的右提示符显示的是(提示符打印出来时的)时间。在有后台任务时,会在左边以黄色显示出后台任务的数量,增加些许后台默默工作的进程的存在感啦。

截图中可以看到,只有最后一行才显示了右提示符(以至于我截图都得 hack 一下)。我使用了setopt transient_rprompt,这样 zsh 会清掉旧的右提示符,就不会影响复制了。以前每次复制时都带上一堆空格然后几个时间,折行之后根本没法看,后来才发现体贴的 zsh 已经有这么个选择了。

另外,在输入命令到右提示符时,右提示符会自动消失,以免和命令混淆。都说了很体贴的哦~

左边,是一个两行的提示符。之所以做成两行,是为了保持命令的起始位置不会因为提示符的长度变化而变化,每次输入新命令的时候,光标都在同一列,易读好找。我就不明白,那些坚持 bash 默认提示符的人是怎么坚持下来的,用着用着不知道自己光标去哪里了……对了,zsh 在输出提示符时,会保证它从终端最左边那一列开始输出。如果上一行不完整,zsh 会打印一个反色的「%」来表示(截图里 ^C 那里就有一个)。

蓝色「>>> 」是学 Python 的,但是使用了蓝色以免和 Python 混淆。如果是 root 用户,则显示红色的「### 」以警示。这个比较刺眼,所以就尽量不用 root 跑 shell 啦。

第一行开头是命令序号,就是历史记录里有多少条命令。每执行一条命令它就会加一,空行或者 Ctrl-C 放弃的不算。其实没什么用的样子。

然后是一个用于标识不同机器的名字。比如这里 lilywork 表示我正在我的工作机上。我家里那个系统里不会显示这个。这个信息可以通过ZSH_PS_HOST变量来设置,比如一般可以设置成$(hostname)。GitLab 之前的提示符里大概没有这个吧。

再就是最后一条命令的状态码($?)。如果命令成功就不显示,否则显示一个红色的数字,以提示上条命令出错了。所以说了嘛,我没法理解坚持使用 bash 及其默认提示符的人……

然后是缩短过的当前目录。~tmp是我的临时目录,有名字(hash -d tmp=....)的。但是它不会缩短中间路径的名字,反正我在它下边写命令,不用担心路径太长。不过我不建议深入探索 nodejs 的模块树,显示好几行的路径并不好看的。

最后一项又是可选的,git 当前分支。这个功能是我自己写的,不是 zsh 自带的那个,是异步显示的哦~忙着干活呢,不能在这种小事上浪费时间、中断思绪嘛。并且还可以通过设置来排除一些目录,比如访问特别慢的远程目录,比如已经死掉很久的 Wuala。

显示的信息不多,也一点都不华丽,但十分有用呢。

介绍完毕,提示符的定义我这里就不写啦。代码都在这里:https://github.com/lilydjwg/dotzsh

Category: shell | Tags: zsh linux
3
10
2017
6

在 Vim 8 里用 git subrepo 安装 vim-go

我一直没有使用 Vim 插件管理插件。我没数过,但是三四个实现肯定是有的。我不喜欢它们的原因,一是迁移成本,二是依赖太巨大:我在一个新环境上配置 Vim 时,我不希望必须访问 GitHub,而且是几十个 clone,这得多慢啊……而且这还意味着它需要有 git,能够联网,DNS 解析正常,等等。这些在各种奇怪的环境里都不容易实现啊。

后来,Vim 8 出来了,内建 packages 管理功能。不愧是官方出品,没有奇怪的依赖。插件来源用户随意,把文件放对位置就可以了。

然后,我又遇见了 git-subrepo,给了 submodule 以外的选择。简单来说它有点 vendoring 的感觉,把第三方仓库放自己里边了。如果目标没有安装 git-subrepo,也没有关系,所有文件都在这儿了,只是没有与第三方仓库同步的能力了而已。

而且,git-subrepo 和 git-notes 类似,它使用额外的 ref 来存储第三方仓库的数据。但与 notes 不同的是,subrepo 已经存在于第三方仓库了,所以我不必把 subrepo 的 ref 推得到处都是,还在每个地方 fetch 下来浪费空间。万一第三方仓库没了或者搬家了,关系也不大,反正我用的代码还在我的仓库里呀。

git-subrepo 是很棒的「pay as you go」设计呢。

有了这两个东西,我就可以尝试一下新的 Vim 插件管理方案了。刚好我需要安装 vim-go,所以就开始尝试啦~

于是开始安装:

cd ~/.vim
git subrepo clone git@github.com:fatih/vim-go.git pack/go/start/vim-go

Vim 8 会自动加载pack/*/start/*下的东西(它下边的目录跟原来的 ~/.vim 布局和用法一样),:packadd命令会加载pack/*/opt/*下的东西。

然后记得设置环境变量。首先是 GOPATH,这是放所有 Go 的源码和编译出来的库和二进制文件的地方。Go 1.8 开始默认 ~/go,但是也需要设置 GOPATH,不然 vim-go 不认。然后得把 $GOPATH/bin 加到 PATH 里去。

确认环境变量设置好之后,打开一个新的 Vim,执行:GoInstallBinaries命令安装依赖。如果中途有神秘失败的,可以把地址拷出来,在命令行里手动调用 go install xxx 安装。部分软件需要访问 Google 的服务器,国内用户注意自行配置一下国际互联网的访问。

安装好之后就有补全、自动格式化、自动加 imports、重命名什么的啦。具体看 vim-go 的文档吧。不过有几点我稍微调整了一下。

我使用 syntastic 来进行代码检查。它默认不启用针对 Go 的检查,需要配置一下:

let g:syntastic_go_checkers = ['go', 'golint']

syntastic 的显示效果比 :GoMetaLinter 命令使用 quickfix 窗口的要好很多哦。

另外不要使用 :GoLint 这个命令,它连非常简单的未定义变量都检查不出来。

而且 vim-go 好 chatty,连跳转到定义都要用显眼的颜色来报告一下成功了……安静真的很珍贵的说。

:GoDoc 我还不会用,因为它报错了:

vim-go: gogetdoc: couldn't get package for .../t.go: can't find package containing .../t.go^@

写 Go 的时候,特别是调试的时候,加句fmt.Printf得记得修改 import,不需要了还得再去删掉才能通过编译……Go 编译是快了,但是处理未使用变量比等待编译结束更令人心烦啊。

有了 goimports 这个命令之后,不需要在开发过程中反复手动修改导入的包列表了。它可以自动添加和删除 import。不过 vim-go 并没有提供在保存时自动调用的支持,所以我就自己动手了。

首先禁用掉它自己的自动格式化:

let g:go_fmt_autosave = 0

然后,是我自己设置的自动命令:

if !exists('#GoImports') && executable("goimports")
  augroup GoImports
    au!
    autocmd BufWrite *.go silent call s:GoImports()
  augroup END
endif

function! s:GoImports()
  let pos = getpos('.')
  let na = line('$')
  %!goimports
  if v:shell_error
    undo
  endif
  let nb = line('$')
  let pos[1] = pos[1] + nb - na
  call setpos('.', pos)
endfunction

还有待提高(比如 undo 的处理),但至少能用。

Category: Vim | Tags: vim go Git
2
28
2017
11

如果重回到学生时代,我想这样做

试题太多了做不完就挑喜欢的做。自习没意思就去图书馆里借书看。累了就休息,困了就睡觉。

老师要是有意见,就告诉TA,你别管我这么多,反正考试的时候给你拿年级前五。要是还不同意,每次考试时就这么干:

所有单选题,一律选正确答案后边那项。多选择题该全选的就选A,否则就只选错误的部分。数值填空题就把正确值加一再填上,解答题就只写上答案,然后解释说太困了就不写过程了,或者手写作业写酸了过程就省掉了。英文填空就填反义词。没反义词的就随便找个押韵的词填上好了。作文当然向现在网传的各种高考零分作文看齐。要不就写社论。社论太敏感就写相对论,或者总结一下微积分啊抽象代数啥的,也可以教教阅卷老师编程。

当然抗议完了,有意思的东西还是照学不误。除了高考这样的考试,分数都是老师家长的,但知识和思考能力是自己的。反正推掉了那么一大堆重复的试题,自己有的是时间把那么点儿知识学得融会贯通嘛。当然在此之前,要对自己好点,该吃就吃,慢慢吃,要吃得健康。到点就睡觉,休息好,精神爽,身体棒,才能够好好地生活。


我的学生时代,父母听信了 CCTV 和学校老师的谎言,为虎作伥,编织了一个美丽的谎言。在那时候,没有人关心我的感受,关注我的将来、我的命运。那是一个充满敌意的世界。那是一段灰暗无光的童年与青春。直到结束了他们制定的行程,走入社会多年,我才发现,那个美丽的未来不过是个谎言,是牢笼的围栏,也是那个充满敌意的世界里虚构的天堂。


To be who you are and become what you are capable of is the only goal worth living. —Alvin Ailey
Category: 未分类 | Tags: 生活 教育
2
25
2017
17

中键的功能

鼠标中键,就是左键和右键之间的那个键啦。常见的鼠标上它在滚轮上。所以你知道了,滚轮是可以往下按的哦。如果是触摸板并且没有中键的话,可以配置双指点击来作为中键使用的(synclient ClickFinger2=3)。

中键具有以下好用的功能哦~(括号里是适用的场景)

  • 粘贴选择区,不用按复制和粘贴的快捷键了~不过选择区的寿命通常比较短,只适合快速的粘贴操作。另见 X Window 中的剪贴板一文。(Linux 桌面、macOS 终端、gpm)
  • 在后台新标签页打开链接(火狐、Google Chrome 等浏览器都支持)
  • 关闭标签页(基本上也是用于网页浏览器。我自己的 GVim 也支持)
  • 定位滚动条,可以快速地定位到开头、结尾,或者之前的位置。不需要拖来拖去的麻烦。可惜 GTK 3 里这个功能不好用的了。(GTK 2、Qt)
  • 移动画布(GIMP、Inkscape 等作图软件、GNOME 的文档查看器 Evince)

这只是比较通用的功能。我的 Awesome 还配置了使用中键关闭窗口呢(「关闭标签页」语义的扩展)。火狐的一些菜单项也支持中键点击,比如书签菜单,右键的「查看图像」菜单,比如前进/后退按钮,以及在它上边点击右键出来的历史记录项目。

总结一下中键的语义:

  • 在可以粘贴的地方,粘贴
  • 在打开对象时,打开新对象而不取代已有者
  • 在打开的对象本身上时,关闭之
  • 在可定位对象上,移动之
Category: Linux | Tags: linux X Window X window
2
4
2017
17

使用 Reabble 在 Kindle 上阅读 InoReader 里的文章

2017年02月08日更新:已经无法通过 InoReader 的 OAuth 功能登录。现只能通过提交 InoReader 账号和密码到 Reabble 的方式登录。我正考虑放弃此服务。


InoReader 的核心在于阅读。Kindle 的核心也在于阅读。

InoReader 的重点在于,它是在线 RSS 阅读器,手机 app 还支持离线,不仅可以在电脑前研读,还可以随时随地使用手机来闲读。可有个问题:长时间盯着 LCD 屏幕看,对眼睛不好!

而这正是使用电子墨水的阅读器——如 Kindle——所解决的问题。所以,怎么把两者结合起来呢?

土木坛子介绍了一个名为 kindle4rss 的服务,通过电子邮件推送 RSS 源。其实我之前也听朋友说过,RSS 源是可以推送到 Kindle 的。但是这样是针对单个的 RSS 设置,推送到 Kindle 的部分不能和其它地方的阅读同步。然后,我顺着 kindle4rss 看了一下,忽然眼前一亮:他们基于 InoReader 的 API 做了一个网页版的、适合 Kindle 使用的阅读器!

就是 Reabble 啦。其实它支持各种浏览器的,只是配色、操作方式是为 Kindle 优化的。(说起来,我挺讨厌像美团招聘或者 AirDroid 局域网 HTTPS 版那样故意不支持特定客户端的行为,明明能用的,却偏偏不让用户用。)

直接访问就可以试用啦。黑白的界面,在电脑上看着挺丑的,不过在 Kindle 上看就大不一样啦。左边是文章列表界面(调整过选项,隐藏了侧栏和已读文章),右边是文章阅读界面(打开了菜单):

Reabble 文章列表 Reabble 文章阅读

在 Kindle 上的体验非常好。而且 Kindle 上的登录做得非常棒:只需要在电脑上通过 InoReader 的 OAuth 授权登录,之后就可以获得一串代码,在 Kindle 那边填写就可以了。我的 InoReader 是用 Google 账号登录的,要是让我在 Kindle 上登录我大概会直接放弃的。

它要求有 Wi-Fi 网络(废话,Kindle 那个破浏览器又不支持离线模式)。不过这对我来说不是问题。真要没 Wi-Fi 的话,用手机开个热点用也成。

不过有些小问题:

  • 不支持广播和赞,只支持加星标。
  • 侧栏里没有未读文章的项、以及星标数不能隐藏。
  • 免费用户只能每天阅读15篇文章,但是并没有阅读到那么多就会出现提示。好像重新打开又可以继续读了。

最后,这服务收费也非常便宜,一年只要18RMB,订更久还会更便宜~最重要的是,它的支付方式对于我很方便!(谁去帮 InoReader 接入支付宝或者微信支付吧,这样我就不用看广告了~)另外,推荐它的话,可以获赠两年的使用权。

Category: 网络 | Tags: 阅读 rss kindle InoReader
1
6
2017
3

如何快速高效地修 bug?

看到知乎上的一个问题,心血来潮,随意写写,请读者不要太较真。

看回答,有一些可操作性很强的答案。但是呢,你知道的,考试好不代表能力强,如果你只是学习别人的方法而并不理解,那么学来之后只会是东施效颦而不能融会贯通。所以呢,我也来发表一下自己的见解。

首先,你要定位 bug。这时,你需要:

  1. 注重逻辑性。不要做没有证据的结论。如果你有猜测,就去证实或者否定它。比如某次,同事代码返回的数据有问题,认为是缓存用的 Redis 有问题,返回了错误的数据。然而没人去对此猜测进行求证……我去确认了一下,Redis 收到了请求,并且响应正常。接下来,排除所有其它可能的原因之后,最后剩下的那个就是真相。真相就是,代码里有个 } 的位置放错了,因为它刚好在一屏之后的位置,所以没有人发现……(是 Vim 帮我找到的)
  2. 基本的方法论。比如二分法。比如最小化测试用例。如果你要提问,要懂得提问的智慧,不管是向搜索引擎还是向人,你都需要提出正确的问题
  3. 知识面。你写 Web 后端的话,普通的 HTTP 得懂,浏览器的开发者工具得会用。简单的 JavaScript 也有会点儿。简单地说就是,你要精于你自己主攻的部分,然后要熟悉你的上下游。再比如如果你使用 CPython 的话,你要准备一份 CPython 的源码,并且要能够流畅地阅读 C 代码。
  4. 工具。工欲善其事,必先利其器。一大堆调试用的工具,你至少得知道它们能干什么,需要的时候能用。比如 strace、lsof、gdb、git bisect,还有高级点的 sysdig、systemtap、perf 等等。当然还有一堆不是专门为调试而设计的通用工具,比如 the silver searcher 或者 ripgrep。一个快速的全文搜索工具能帮你在最短时间内找到相关的代码或者日志。你不必成为正则表达式大师,但是简单的一定要会,不然面对上千个匹配结果你要怎么办呢?Vim 有一个插件 Mark,能够同时高亮多个模式,非常利于调试期间阅读代码和日志。投入时间学习使用高效的工具,不要把时间浪费在等待和人工搜索上,也不要让自己忙于琐事而断了灵感和线索。

最后,不要不断地、毫无目的地换个环境啦,换个版本啦,换个用户啦,这样子找问题。如果这样做很有效的话,大家都去买彩票去了。

找到 bug 之后,理解它是如何产生的。当你理解之后才能真正修好它。就像你感冒了吃抗生素,根本没有用。

Category: 编程 | Tags: 编程 软件开发
1
5
2017
17

我使用的 Xposed 模块

开始使用 Xposed 之后,我对我的手机又多了一份拥有感,然后呢,装的模块越来越多了。以下是我正在使用的模块的列表,以及我为什么使用它们。链接我就懒得放了,想要安装的读者可以自己去 Xposed Installer 里搜。

绿色守护。装上这个我才敢装各种国产应用。它的 Xposed 模块用于功能增强,最重要的一点是,它可以切断唤醒途径。这样就不会总也杀不死那些耗电又耗流量的应用们了。

去你大爷的内置浏览器。我发现 Android 6 里,好多 Google 家的应用都开始默认使用内置浏览器了呢……不过国外的应用一般都是可以选择在内置浏览器里打开,还是外部浏览器里打开的。我更喜欢在外置浏览器里打开,一来减少相同的缓存和数据文件,节约存储空间,因为缓存共享而加快加载速度,二来能够使用自己的配置(比如广告拦截),并且能够使用最新的浏览器特性。上次听说了一个很棒的浏览器特性来着,然后就有人告诉我微信里不支持……当然了,微信这种应用,为它的内置浏览器增加了接口,有些网页必须在它里边打开。所以这个模块是有白名单的。

微信防撤回模块。顾名思义啦。

Android通话振动。Android 4.0.4 有一个很贴心的功能:在拨出电话接通时,它可以振动一下,告诉用户已经接通了。所以就不用一直把电话放在耳边等着啦,有时候等着等着,因为某些原因呼叫终止了还傻傻地等着……不知道为什么,后来的版本就没有这个功能了。还好我们有 Xposed。这个模块不仅把接通时振动给加回来了,还可以挂断时振动,以及如果对话费敏感的话,可以定时振动。

App Settings。目前我用来强制QQ轻聊版出现在最近使用的应用列表中。以后还可能需要强制某些中文翻译拙劣的应用使用英文语言。

Battery Stats Plus。这是一个同名应用带的模块。用于电池使用统计。

Xposed Torch。不需要解锁屏幕然后点来点去的手电筒。在锁屏状态下长按音量上键就可以启动了,再按一下音量下就关闭了。方便好用~

XPrivacy。权限管理。也是使用国产应用时必备的功能。虽然 Android 6 里,很多权限都像 iOS 那样在运行的时候询问了。但是呢,流氓总有流氓的手段,你要拒绝授权?那好,一切功能免谈,你卸载我吧。

XuiMod。我用来让右上角的时间显示秒数的。

哇已经装了九个模块了呢。其实我是不希望用这么多 Xposed 模块的,毕竟是打补丁嘛。可是呢,毕竟不是自由软件,只能这样了。也幸好我们还能打补丁。

Category: Android | Tags: Android Xposed

部分静态文件存储由又拍云存储提供。 | Theme: Aeros 2.0 by TheBuckmaker.com