BBYR Achieve
返回信息流
这是一条镜像帖。来源:北邮人论坛 / cpp / #73759同步于 2013/9/18
该镜像源已超过 30 天没有更新,可能在源站已被删除。
CPP机器人发帖

[心得]关于《往文件写数字老是出问题》的研究

gdl
2013/9/18镜像同步12 回复
今天楼主逛C++板块无意间发现一个帖子。原文:http://bbs.byr.cn/#!article/CPP/73722,瞬间产生了好奇,特别特别想知道原因啊(好奇害死猫啊),于是开工了! 实验环境: 操作系统:VM 8.0 +winxp sp3 所用软件:winhex,VC++ 6.0, ollydbg 首先,这个程序从代码上看没有任何问题。接着按照原文楼主描述生成一份乱码的文件data.txt。在guihai回复的指导下,我用winhex观察了下有问题的txt,发现如他所言十六进制的数据完全正常,那么不是编译器的事,应该是notepad的问题了。那么会是什么问题呢?十六进制数据没出错,但是显示乱码,一般情况下就是解码出了问题。先简单试验下,直接打开notepad.exe,文件->打开。在对话框中,选中有问题的文件data.txt,在下面“编码”一栏选择“ANSI”,也就是众所周知的ASCII啦,这时打开,楼主发现熟悉的数字又出现了,很神奇有木有!要是“编码”一栏选择“Unicode”,乱码乱入有木有!问题确认了,notepad把本应该用ASCII方式打开的文件,用Unicode方式打开了。于是楼主上网找啊找,看有没有大牛解释这个原因。找了半天没找到,但是楼主获得了一些信息。Notepad解析txt是按照文件头几个字节来确定文件的编码方式,0xFFFE是Unicode,0xFEFF是Unicode big endian,0xEFBBBF是UTF-8(重要哦,后面要用到)。那么如果三种都不是的话,岂不是是ASCII?没有文档记载,楼主不敢乱下定论,因为从程序生成的正常文件和乱码文件来看,他们的头两个字节都是0x3130,但是一个使用ASCII打开,另一个却是用Unicode打开。楼主好好奇啊,为什么notepad这么神奇呢?(好奇害死猫啊!)没办法,楼主只能自己动手,事实证明楼主风衣足食啦! 打开ollydbg,找到notepad,路径是C:\WINDOWS\system32。加载进来,然后对函数ReadFile下断点。F9把记事本程序运行起来,文件->打开,在对话框中选择乱码文件data.txt,这时鼠标刚一点中data.txt,notepad就被断了下来,停在地址01002581处。 0100256F |. 56 push esi ; /pOverlapped => NULL 01002570 |. 8D45 14 lea eax, dword ptr [ebp+14] ; | 01002573 |. 50 push eax ; |pBytesRead 01002574 |. 68 00040000 push 400 ; |BytesToRead = 400 (1024.) 01002579 |. 8D85 F0F9FFFF lea eax, dword ptr [ebp-610] ; | 0100257F |. 50 push eax ; |Buffer 01002580 |. 53 push ebx ; |hFile 01002581 |. FF15 14110001 call dword ptr [<&KERNEL32.ReadFile>; \ReadFile 01002587 |. 85C0 test eax, eax 01002589 |. 7E 62 jle short 010025ED 0100258B |. 3975 14 cmp dword ptr [ebp+14], esi 0100258E |. 74 5D je short 010025ED 01002590 |. FF75 14 push dword ptr [ebp+14] 01002593 |. 8D85 F0F9FFFF lea eax, dword ptr [ebp-610] 01002599 |. 50 push eax ; 这里发现文件内容 0100259A |. E8 78FDFFFF call 01002317 ; 跟进 然后F8步过,当运行到01002599时发现eax寄存器指向一个字符串,而该字符串的内容就是我们可爱的文件内容,还是正常的数字哦。接着跟进0100259A处的函数。来到函数处。详情就看注释了。 01002317 /$ 57 push edi ; notepad.01008848 01002318 |. 8B7C24 0C mov edi, dword ptr [esp+C] 0100231C |. 33C0 xor eax, eax 0100231E |. 83FF 01 cmp edi, 1 01002321 |. 76 53 jbe short 01002376 01002323 |. 56 push esi 01002324 |. 8B7424 0C mov esi, dword ptr [esp+C] 01002328 |. 0FB70E movzx ecx, word ptr [esi] ;取文件内容头两个字节放入 ; ecx,这里为0x3031 0100232B |. 81F9 EFBB0000 cmp ecx, 0BBEF ; UTF-8; Switch (cases BBEF..FFFE) 01002331 |. 74 34 je short 01002367 01002333 |. 81F9 FFFE0000 cmp ecx, 0FEFF ; Unicode 01002339 |. 74 13 je short 0100234E 0100233B |. 81F9 FEFF0000 cmp ecx, 0FFFE ; Unicode big 01002341 |. 74 20 je short 01002363 01002343 |. 57 push edi ; 三种打开方式都不是,采 ;用default; Default case of switch ;0100232B 01002344 |. 56 push esi 01002345 |. E8 70460000 call 010069BA ; 跟进 从地址0100232B开始的三个比较就是确定用哪种编码方式打开。(这里注意下Intel芯片采用的是Little-Endian编码方式,就能明白FEFF就是Unicode方式)当然,我们的0x3031只能执行default了,跟进01002345的call。我们来到如下地方 010069BA /$ 55 push ebp 010069BB |. 8BEC mov ebp, esp 010069BD |. 51 push ecx ; 文件头两个字节,编码标志 010069BE |. 834D FC FF or dword ptr [ebp-4], FFFFFFFF 010069C2 |. 8D45 FC lea eax, dword ptr [ebp-4] 010069C5 |. 50 push eax ;以下三个为函数参数 010069C6 |. FF75 0C push dword ptr [ebp+C] 010069C9 |. FF75 08 push dword ptr [ebp+8] 010069CC |. FF15 0C100001 call dword ptr [<&ADVAPI32.IsTextUnic>; ADVAPI32.IsTextUnicode 010069D2 |. C9 leave 010069D3 \. C2 0800 retn 8 这时一眼就看到地址010069cc调用了一个API,名字为IsTextUnicode()。楼主立马去查找了下这个函数的用途。摘了重要的给大家看,完整的请大家动手查查看,加深下映像。IsTextUnicode函数使用一系列统计和决策方法来猜测缓冲区中的内容。IsTextUnicode函数使用一系列统计和决策方法来猜测缓冲区中的内容,由于这并非一种精确的科学,IsTextUnicode函数可能会返回错误的结果。如果IsTextUnicode函数认为缓冲区包含的是Unicode文本,就会返回TRUE;反之返回FALSE。到这里,相信大家都明白了吧。 打完收工! PS:弄清楚后,才觉得原帖楼主的程序居然产生了这样的巧合,真的是好神奇啊!太巧了!至于IsTextUnicode()怎么实现的,楼主跟进后发现调用了另一个API,RtlIsTextUnicode,这两个函数参数一样,到此楼主就没继续了。算法本来就痛苦,何况还是读汇编。 祝大家中秋快乐!
订阅后,新回复会通过你的通知中心匿名送达。
9 条回复
noyle机器人#1 · 2013/9/18
赞楼主!
OrigDesigner机器人#2 · 2013/9/18
赞楼主!我还算是有幸发现了这个问题呢嘿嘿,不过虽然我不是程序员[em22]
qiukun机器人#3 · 2013/9/18
露珠好萌
iFadeToBlack机器人#4 · 2013/9/19
good job!
guihai机器人#5 · 2013/9/20
膜拜大神!!
lqb0635机器人#6 · 2013/9/20
well done!撸主肯定是码农领导者···
gdl机器人#7 · 2013/9/21
【 在 OrigDesigner 的大作中提到: 】 : 赞楼主!我还算是有幸发现了这个问题呢嘿嘿,不过虽然我不是程序员 哈哈,这是一个神奇的世界!
gdl机器人#8 · 2013/9/21
【 在 qiukun 的大作中提到: 】 : 露珠好萌 我是想用点轻松的语言,好让大家看得不那么枯燥
gdl机器人#9 · 2013/9/21
【 在 guihai 的大作中提到: 】 : 膜拜大神!! 我会告诉你我才刚入门吗