From Wireshark to Linux Capabilities

Tcpdump 和 Wireshark 是抓包必备的程序,但是由于需要截取网络数据包,所以在 Linux 下必须以 root 的身份来运行。每次都 sudo 执行不方便也并不安全(对 Wireshark 来说捉包只是一小部分功能),解决方案当然有,在寻找的过程中我了解到了 Capabilities 的冰山一角。 TL;DR 可以通过设置 Setuid 以 root 身份执行,但如此以来赋予了过高的权限(也没有必要)。 Linux 下用 Capabilities 把系统权限划分成多个条目,以此实现细粒度地提升程序的执行能力。 Setuid 先盗一张图复习下 Linux File Permission 的基础知识: 除了 rwx 之外还存在三种特殊类型,即是为了在更高权限下运行程序: Setuid: 程序的运行者不再是执行者,而是变成文件的所有者 Setgid: 程序的运行群组变成了文件的所在群组,如果给目录设置,那么其中新建的文件所有群组会变成目录的群组而不是执行者的群组 Sticky bit: 针对目录设置,目录下的文件只有所有者和 root 能够重命名、移动和删除 在 Linux 中 sudo 是 Setuid 最好的例子,而 crontab 和 tmp/ 分别是 Setgid 和 Sticky bit 的典型应用。 在命令行中测试: # Setuid ➜ ~ umask -S u=rwx,g=rx,o=rx ➜ ~ umask # 掩码是 022,所以默认文件的权限是 666-022=644,而目录则是 777-022=755 022 ➜ ~ touch foo....

11-21 · 3 min

Overflow in Python

犹记得在 Java 中,int 占用 4 bytes 所以大小限制在 -2^32 和 2^32 -1 之间,如果考虑到溢出的情况,就要用 long 类型来转换。 但是在 Python 中似乎从来没有考虑过类似的问题,也不记得 int 是占几个字节,那么是说 Python 的数字永远不会溢出吗?不可能吧。 答案是对于 int 类型来说,可以认为不会;而 float 类型则需要注意溢出的情况。 首先说 int 类型,打印 sys.maxsize 可以看到 Python 会根据系统是 32 位还是 64 位来确定一个上限值,这点与 Java 等语言一致。不同的是我们仍然可以使用比这个上限大(得多)的整数,因为 Python 支持的是 Arbitrary-precision arithmetic。也就是说为了突破 CPU 字长带来的运算限制,通过在软件层面模拟计算过程,是可以完成更高位数和精度的运算的。比如在公钥加密的场景下,经常需要对上百位的整数进行运算,这时候就需要在软件层面支持。 这确实说明我们可以使用任意长度的 int 数字,只要不超出内存限制的话。因为如果给 Python 解释器分配了 2GB 内存,但是 2 * 1024 * 1024 * 1024 * 8 这么多位也不够表达的话,还是会出错的,只是并非 Overflow,而是 MemoryError. 再说 float. 打印 sys.float_info.max 可以看到 float 的上限值,如果超出之后是会报错的,即 OverflowError....

11-12 · 1 min

Understand Recursion Better

在 Simple Recursion 之后,我一度把递归当作一种算法。但通过比较 Divide and Conquer 和 Dynamic Programming,我才发现之前的理解有点问题。 一切还是要从 Algorithmic Paradigm 说起: An algorithmic paradigm or algorithm design paradigm is a generic model or framework which underlies the design of a class of algorithms. An algorithmic paradigm is an abstraction higher than the notion of an algorithm, just as an algorithm is an abstraction higher than a computer program. 算法范式,是在算法的层面上抽象出来的一种更泛化的思想,常用的有: Brute-force search 暴力解法 Backtracking 回溯算法 Greedy algorithm 贪心算法 Divide and conquer 分治法 Dynamic programming 动态规划 Divide and Conquer 的基本思路是把复杂的问题分解成多个类似的简单问题,解决之后再组合起来得到最终结果。这种算法有很多应用,比如排序中的 Merge Sort,先将数列分解成单个元素,然后再归并,这时子数组都已经排好顺序了,所以过程很快。...

11-08 · 2 min

Record Terminal as GIF

这几天在做一个 CLI 项目,因为涉及到命令行操作,所以想录制一段 GIF 放在 README 中展示。 印象里这种工具都是 JS 写的,但搜了搜居然发现有个 Python 的实现:asciinema 用法很简单,就是安装好之后在命令行执行 asciinema rec 便会启动一个新的 Shell 并开始录制,录制的时候就像正常使用 Terminal 一样即可,完成之后按 Ctrl-D 或者 Exit 退出,asciinema 会把录制好的 cast 文件保存到本地,也可以选择上传到他们的网站:asciinema.org. 那么 cast 文件是什么,又怎样得到 GIF 呢?其实这是 asciinema 自己定义的一种文件格式: A CAST file is a record of a terminal session recorded using asciinema, an open source terminal recording program. It contains a JSON-formatted header and a timestamped record of the text typed and printed during a terminal session....

11-07 · 2 min

Look and Say

无意中看到一种叫 look-and-say 的数列,很有意思,有点儿 Fibonacci 的感觉。数列如下: 1, 11, 21, 1211, 111221, 312211, 13112221, 1113213211, … 从第二位开始,每个数字都是对前一个数的计数结果的描述。比如 11 表示前面的 1 有一个 1,而 21 表示前面的 11 有两个 1,1121 表示 21 有一个 2 和一个 1,依次类推,可以无穷循环出新的结果。(除了 22 这个数字,因为会一直重复 22 本身) 后来查了查才发现原来 Leetcode 也有这道题,不过名字叫做 count-and-say。基本就是给 n 然后求此数列的第 n 项结果。 想了想思路并不难,无非是对一个数字的所有位数循环再计数就好了,不过写的时候很犯难,竟然还写出了一个无比冗长的 for 循环加 if 面条代码。这让我意识到了自己对于循环的认识有多么不够深刻。 之所以犯难,其实是不知道怎样把几种逻辑合并在一起,如果粗暴地列举所有分支大概是这样: def find_next(num: str): result = '' cur, count = num[0], 1 size = len(num) for i in range(1, size): if num[i] == cur and i == size - 1: # count 加一 # 更新 result elif num[i] == cur: # count 加一 elif i == size - 1: # 更新 result # 更新 cur&count # 更新 result else: # 更新 result # 更新 cur&count # 还有一种情况就是并没有进入 for 循环 # 那么也需要更新 result 直接按照这些分支把代码填满肯定很难看,所以要挑选合并。看起来只有第二个分支不需要更新 result,于是我决定把它单拎出来:如果 num[i]==cur 那么 count 加一;否则就更新 result 以及 cur&count....

11-07 · 2 min