标题: 也谈Windows记事本的BUG
作者: Demon
链接: https://demon.tw/reverse/windows-notepad-bug.html
版权: 本博客的所有文章,都遵守“署名-非商业性使用-相同方式共享 2.5 中国大陆”协议条款。
在Windows操作系统中使用记事本新建一个文本文件,在文件里面写入“联通”两个字并保存。当再次打开这个文本文件时候,在记事本中看到得却不是刚刚输入的“联通”,而是乱码。这就是移动比联通强的原因!
这是网上广为流传的笑话,也有不少人分析过其中的原因:“联通”这两个字符的GBK编码具有UTF-8编码的特征,记事本犯下的错误正是将GBK编码存放的记录有“联通”两个字符的文件误认为UTF-8编码的文件。
这虽然没有错,但是无代码无真相,记事本是如何判断一个文件的编码是不是UTF-8的呢?用OllyDbg调试了一下,判断UTF-8的过程如下:
010098C7 >/$ 8BFF mov edi, edi 010098C9 |. 55 push ebp 010098CA |. 8BEC mov ebp, esp 010098CC |. 56 push esi 010098CD |. 33F6 xor esi, esi 010098CF |. 33C9 xor ecx, ecx 010098D1 |. 46 inc esi 010098D2 |. 33D2 xor edx, edx 010098D4 |. 394D 0C cmp dword ptr [ebp+C], ecx 010098D7 |. 7E 1E jle short 010098F7 010098D9 |> 8B45 08 mov eax, dword ptr [ebp+8] 010098DC |. 8A0401 mov al, byte ptr [ecx+eax] 010098DF |. 84C0 test al, al 010098E1 |. 79 02 jns short 010098E5 010098E3 |. 33F6 xor esi, esi 010098E5 |> 85D2 test edx, edx 010098E7 |. 75 12 jnz short 010098FB 010098E9 |. 3C 80 cmp al, 80 010098EB |. 72 15 jb short 01009902 010098ED |> 02C0 /add al, al 010098EF |. 42 |inc edx 010098F0 |. 84C0 |test al, al 010098F2 |.^ 78 F9 \js short 010098ED 010098F4 |. 4A dec edx 010098F5 |. 75 0B jnz short 01009902 010098F7 |> 33C0 xor eax, eax 010098F9 |. EB 18 jmp short 01009913 010098FB |> 24 C0 and al, 0C0 010098FD |. 3C 80 cmp al, 80 010098FF |.^ 75 F6 jnz short 010098F7 01009901 |. 4A dec edx 01009902 |> 41 inc ecx 01009903 |. 3B4D 0C cmp ecx, dword ptr [ebp+C] 01009906 |.^ 7C D1 jl short 010098D9 01009908 |. 85D2 test edx, edx 0100990A |.^ 77 EB ja short 010098F7 0100990C |. 85F6 test esi, esi 0100990E |.^ 75 E7 jnz short 010098F7 01009910 |. 33C0 xor eax, eax 01009912 |. 40 inc eax 01009913 |> 5E pop esi 01009914 |. 5D pop ebp 01009915 \. C2 0800 retn 8
看不懂汇编?没关系,其实我也看不懂,翻译成C代码大概是这样:
/*************************************** By Demon https://demon.tw ***************************************/ int is_utf8(unsigned char buf, int len) { int i, ascii_only, continuation_bytes; unsigned char c; i = 0; ascii_only = 1; continuation_bytes = 0; if ( len <= 0 ) return 0; do { c = *(buf + i); if ( (signed char)c < 0 ) ascii_only = 0; if ( continuation_bytes ) { if ( (c & 0xC0) != 0x80u ) return 0; --continuation_bytes; } else { if ( c >= 0x80u ) { do { c <<= 1; ++continuation_bytes; } while ( (signed char)c < 0 ); --continuation_bytes; if ( !continuation_bytes ) return 0; } } ++i; } while ( i < len ); if ( continuation_bytes || ascii_only ) return 0; return 1; }
可见,只要是符合UTF-8编码特征的字节流都会被记事本认为是UTF-8编码,而不论其被正确的编码了。例如“联通”的“联”的GBK编码为C1AA,转成二进制是:
C1 11000001 AA 10101010
红色部分为UTF-8的特征,把剩余部分00001和101010连起来是00001101010,即十六进制6A,但是6A在UTF-8里只需要一个字节,而不是两个。所以C1AA其实并不是有效的UTF-8字节流,虽然它表面上符合UTF-8的特征,而记事本却没有做进一步的判断。
参考链接:http://en.wikipedia.org/wiki/Utf8
赞赏微信赞赏支付宝赞赏
随机文章:
[…] « 也谈Windows记事本的BUG […]
这bug恐怕在Win7上无法重现了吧……还有Windows下的文本编辑器用Notepad++吧……
这就是典型的脑袋长在屁股上的说的话,不实践就瞎BB,不好意思,WIN7的同样有这个BUG
server 2012 r2 目前依然存在这个问题
notepad++ 貌似有同样问题哦,在默认设置下
windows 10 最新版 10.0.14xxxx 现在还有这个bug
涨见识啦
这个bug永远将在,不会为了你一个中文编码,而打补丁。