问题
11100110 10110001 10011111
表示什么?
ASCII
American Standard Code for Information Interchange,美国信息交换标准代码
ASCII
最初由美国提出,解决了英语世界在计算机里的编码问题。ASCII
从00
开始,一直到7f
,包含了128
个字符,其中33
个控制字符,95
个可见字符。
- 控制字符比较常见的如换行键、空字符、退出键等。
- 可见字符如空格、数字、大小写字母、符号等。
可以通过echo
配合xxd
命令很简单的获取字符的ASCII
值,如下我们就拿到了a
的ASCII
值为0x61
1
2
3
4 echo a | xxd
00000000: 610a a.
echo a | xxd -b
00000000: 01100001 00001010 a.
随着互联网在全世界的普及,ASCII
的局限性慢慢凸显,其无法表示其他语种如汉语、日语的字符。于是发展出了Unicode
。
Unicode
Unicode,又称为万国码、国际码、统一码、单一码
Unicode
是一项业界标准,为每一个字符都制定了唯一的编码,统一了各语种的编码。
比如U+0061
表示a
,U+660e
代表汉字的明
字,U+1f600
表示露齿而笑的脸😀
,U+1F1F3
表示中国国旗🇨🇳
Unicode
里一个字型可以代表多种字符编码,甚至是某些编码的组合。
如é
既可以用U+00e9
表示,也可以用U+0065U+0301
的组合表示。所以我们使用的时候,还是得根据实际使用的意义来输入,否则在计算长度以及字符串比较等方面就会不一致,造成不必要的麻烦,而且这些问题排查起来也很困难。
UTF-8
8-bit Unicode Transformation Format
UTF-8
是一种针对Unicode
的可变长度字符编码,是一种前缀码。
128
个US-ASCII
字符只需一个字节编码(Unicode
范围由U+0000
至U+007F
)- 带有附加符号的拉丁文、希腊文、西里尔字母、亚美尼亚语、希伯来文、阿拉伯文、叙利亚文及它拿字母则需要两个字节编码(
Unicode
范围由U+0080
至U+07FF
) - 其他基本多文种平面(
BMP
)中的字符(这包含了大部分常用字,如大部分的汉字)使用三个字节编码(Unicode
范围由U+0800
至U+FFFF
) - 其他极少使用的
Unicode
辅助平面的字符使用四至六字节编码(Unicode
范围由U+10000
至U+1FFFFF
使用四字节,Unicode
范围由U+200000
至U+3FFFFFF
使用五字节,Unicode
范围由U+4000000
至U+7FFFFFFF
使用六字节)。
代码范围 | 标量值 | UTF-8 | 注释 | 个数 |
---|---|---|---|---|
000000 - 00007f | 00000000 00000000 0zzzzzzz | 00-f7 | ASCII,字节由0开始 | 128 |
000080 - 0007ff | 00000000 00000yyy yyzzzzzz | 110yyyyy 10zzzzzz | 第一个字节由110开始,接着的字节由10开始 | 1920 |
000800 - 00d7ff , 00e000 - 00ffff | 00000000 xxxxyyyy yyzzzzzz | 1110xxxx 10yyyyyy 10zzzzzz | 第一个字节由1110开始,接着的字节由10开始 | 61440 |
010000 - 10ffff | 000wwwxx xxxxyyyy yyzzzzzz | 11110www 10xxxxxx 10yyyyyy 10zzzzzz | 第一个字节由11110开始,接着的字节由10开始 | 1048576 |
Unicode
在范围D800-DFFF
中不存在任何字符,基本多文种平面中约定了这个范围用于UTF-16
扩展标识辅助平面(两个UTF-16
表示一个辅助平面字符)。当然,任何编码都是可以被转换到这个范围,但在Unicode
中他们并不代表任何合法的值。
根据上面的表可见,UTF-8
完美兼容ASCII
编码,Unicode
范围很大,而且是可以继续扩展的
同时我们可以根据Unicode
值推导出UTF-8
的编码1
2
3
4graph TD
A[Unicode:黄,U+9ec4] -->|二进制| B(1001 1110 1100 0100)
B --> |UTF-8| C(11101001 10111011 10000100)
C --> |十六进制| D(UTF-8:e9bb84)
现在我们回到文章开头的问题
11100110 10110001 10011111
表示什么?
可以看出这段二进制编码实际上是UTF-8
编码,按照上表的规则,可以推导出其Unicode
字符
1 | graph TD |
应用:修改机器码字符段
学以致用,这样效果才会好
这是一段简单的输出字符串的程序1
2
3
4
5
6
7
int main(int argc, char const *argv[])
{
printf("hello,world!黄");
return 0;
}
编译$ gcc main.c | ./a.out
输出:hello,world!黄
查看目标文件的段信息$ objdump -s a.out
输出里的字符段如下:1
2Contents of section __cstring:
100000fa2 68656c6c 6f2c776f 726c6521 e9bb8400 hello,world!....
我们可以知道,这一串十六进制编码代表了UTF-8
编码的hello,world!黄
。可以尝试对其进行修改vim -b a.out
打开十六进制模式%!xxd
查找到hello部分进行修改,将 e9bb84
(黄) 改成 e6b19f
(江)。/hello
恢复成二进制模式%!xxd -r
保存退出wq!
查看字符段$ objdump -s a.out
输出:1
2Contents of section __cstring:
100000fa2 68656c6c 6f2c776f 726c6521 e6b19f00 hello,world!....
$ ./a.out
输出:hello,world!江
这样,我们就修改了机器码。
到这里,笔者也想通了一些问题,比如hardCode
进代码里的密钥肯定是不安全的,存在被查看以及被篡改的风险;二进制文件也是不安全的,所以Apple
使用了代码签名的机制,来保证设备上运行的代码是未被篡改过的。