(如果本题得分为72分,请不要投票!)
我跑了这个:
cat /usr/bin/* |
perl -ne 'map {$a{$_}++} split//; END{print map { "$a{$_}\t$_\n" } keys %a}' |
grep --text . | sort -n | plotpipe --log y {1}
并得到这个:
(即使使用对数 y 轴,它看起来仍然是指数的!顶部和底部之间的距离超过 100 倍)
看一下数字:
:
31919597 ^H
32983719 ^B
33943030 ^O
39130281 \213
39893389 $
52237360 \211
53229196 ^A
76884442 \377
100776756 H
746405320 ^@
^@ (NUL) 是可执行文件中最常见的字节,这并不奇怪。 \377 (255) 和 ^A (1) 对我来说也具有直观意义。
但是,是什么导致“H”(72)成为可执行文件中第二常见的字节——比 255 和 1 更常见呢?
背景
对于 Perl 脚本,我需要找到 Perl 脚本中最不常见的字节。意外的是,我没有只 grep 出 Perl 脚本,而是对所有二进制文件运行了该命令。我预计有几个字节会脱颖而出,例如 NUL、1 和 255,但绝不是“H”。
该图的输入是每个字节的计数(已排序)。 y 轴代表计数,x 轴代表行号(1-256,因为一个字节只能采用 256 个不同的值)。 y 轴是对数刻度,因此差异大于指数。
答案1
那将是64 位操作数大小前缀amd64 机器代码指令。
您会注意到它只发生在 amd64 可执行文件上。
/bin/*
如果你比较http://ftp.debian.org/debian/pool/main/c/coreutils/coreutils_9.1-1_arm64.deb,
http://ftp.debian.org/debian/pool/main/c/coreutils/coreutils_9.1-1_amd64.deb和
http://ftp.debian.org/debian/pool/main/c/coreutils/coreutils_9.1-1_i386.deb, 你会看到的:
$ for f (coreutils_9.1-1_*.deb) bsdtar xOf $f da\* | bsdtar xO ./bin/\* | xxd -p -c1 | sort | uniq -c | sort -rn | head -n 5 | grep -H --label="${${f:r}##*_}" .
amd64: 692417 00
amd64: 145689 ff
amd64: 81911 48
amd64: 48006 89
amd64: 45331 0f
arm64:1409826 00
arm64: 70391 ff
arm64: 67915 03
arm64: 49380 20
arm64: 41655 40
i386: 515346 00
i386: 171643 ff
i386: 78361 0e
i386: 69317 24
i386: 50497 83
0x48(72,'H')仅位于 amd64 上的前 3 名中。
在ls
我的 amd64 Debian 系统上:
$ xxd -p -c1 =ls | sort | uniq -c | sort -rn | head -n 5
39187 00
7827 ff
5565 48
4181 20
3393 0f
如果我们反汇编该可执行文件中的代码,我们会在指令中发现大量 0x48 字节:
$ objdump -d =ls | grep -cw 48
5353
其中大多数都处于第一位置:
$ objdump -d =ls | grep -wm10 48
4000: 48 83 ec 08 sub $0x8,%rsp
4004: 48 8b 05 ad ff 01 00 mov 0x1ffad(%rip),%rax # 23fb8 <__gmon_start__@Base>
400b: 48 85 c0 test %rax,%rax
4012: 48 83 c4 08 add $0x8,%rsp
44b6: 68 48 00 00 00 push $0x48
4751: 48 89 f3 mov %rsi,%rbx
4754: 48 83 ec 68 sub $0x68,%rsp
4758: 48 8b 3e mov (%rsi),%rdi
475b: 64 48 8b 04 25 28 00 mov %fs:0x28,%rax
4764: 48 89 44 24 58 mov %rax,0x58(%rsp)
$ objdump -d =ls | grep -Pc '^\s*[\da-f]+:\s+48'
5113
根据http://ref.x86asm.net/geek.html#x48,0x48 是64 位操作数大小 REX.W
操作码前缀,指定对 64 位操作数进行操作,而不是默认操作数。
$ objdump -d =ls | pcregrep -o1 -o2 '^\s*[\da-f]+:\s+(48 .. ).*?\t(\S+)' | sort | uniq -c | sort -rn | head
1512 48 89 mov
1040 48 8b mov
630 48 8d lea
372 48 85 test
326 48 83 add
198 48 39 cmp
158 48 83 sub
79 48 01 add
72 48 83 cmp
69 48 c7 movq
所有指令均在 64 位操作数上完成。