从堆里寻找丢失的数据
这是刚刚发生的一个故事,笔者亲历。
因为年底一个月很忙碌,所以久未写博客了,这几天放假,略有空闲,于是想写点东西出来,一来练练手,免得本来就不熟练的文笔变得更加生疏;二来与大家交流一下,免得朋友也生疏了。于是上网,打开网页,准备写个短文。写什么呢?这几天放假,就写点闲话吧,放假前去了次闵行老街,就写这个吧,怀怀旧。:-)
写文章不是件简单的话,很多时候的确像赵本山说的那样,半天憋不出俩字。:-)于是乎,就一篇简简单单的《重访闵行老街》,前前后后也用了几个小时。当然中间有一些停顿,有时陪女儿玩一会,有时喝杯茶,这样到中午时,基本写好了,检查一遍就可以发出去了;但中饭时间到了,女儿强烈要求煮意大利面给她吃,于是只好把写完大半的文章放下了。
“老爸,意大利面煮的很棒”,女儿怪腔怪调的表扬是最好的奖励:-)
吃过午饭,又想起写文章的事,看了一遍,改掉几个错别字,部分语句润色一下,自我感觉可以了,点击发送。
鼠标点下去后我就后悔了,应该先保存一下,因为是在网页上写的,没有自动保存,如果发送失败,后果可能很严重。但是这时已经没有机会取消了,虽然还可以看到编辑的文字,可以选中,但是菜单中的Copy命令已经不管用了。
接下来看到的是浏览器与服务器艰难对话的过程,速度很慢,过了几秒钟,问题真的出现了,“Cannot find server”,无语。
打开另一个浏览器,试图访问服务器,真的连不上,后来证明,服务器确实不知道啥时候宕了,但肯定是在我写文章的过程中,因为开始写时还是连得上的。
根据经验,有时回退还可以退回到刚才编辑的页面,救回所写的内容。于是点击Back按钮,但是浏览器并没有老老实实的回退到刚才的编辑页面,可能是因为页面上的“智能脚本”又触发了连接服务器的动作,于是浏览器让人失望的试图连接服务器,连接失败,又显示 “Cannot find server”,糟糕的软件!
向前又向后来回了几次,我意识到,刚才几个小时的功夫要白费了。这时老婆从书桌路过,听说刚才写的东西丢了,哈哈大笑,“你也会烦这种错误的啊,我还以为只有我们这些人才烦这种错误哪,呵呵呵”
是啊,这个错误是够低级的,要么应该用更好一点的工具来写,要么该先保存一下......
怎么办,重写一遍?不可接受。放弃拉倒?不可接受。
上调试器!
相信数据还在内存里,找得到就可以读出来,读出来了就可以找办法恢复成可读的形式。
把WinDBG附加到IE进程后,首先看到的是进程中有几十个模块;列一下线程,五十几个线程;列一下堆,有45个。
茫茫2GB用户态空间,在哪里找自己写的文章哪?搜索吧!就从当前进程用户态空间的较低地址开始搜,找文章里的一句有特色的话:
0:045> s -u 10000 L80000 "当年在交大"
什么都没找到,扩大搜索范围:
0:045> s -u 10000 L8000000 "当年在交大"
延迟一刹那后,刷刷出来很多结果,搜到了。这让人很高兴,说明数据还在内存里。
02fbc78a 5f53 5e74 5728 4ea4 5927 4e8c 90e8 8bfb S_t^(W.N'Y.N....
02fd3fb2 5f53 5e74 5728 4ea4 5927 4e8c 90e8 8bfb S_t^(W.N'Y.N....
0379c138 5f53 5e74 5728 4ea4 5927 0022 0020 ff0c S_t^(W.N'Y". ...
03d266c6 5f53 5e74 5728 4ea4 5927 0022 003c 002f S_t^(W.N'Y".<./.
03d44daa 5f53 5e74 5728 4ea4 5927 4e8c 90e8 8bfb S_t^(W.N'Y.N....
03d75662 5f53 5e74 5728 4ea4 5927 4e8c 90e8 8bfb S_t^(W.N'Y.N....
03d7d7ba 5f53 5e74 5728 4ea4 5927 4e8c 90e8 8bfb S_t^(W.N'Y.N....
03dcc012 5f53 5e74 5728 4ea4 5927 4e8c 90e8 8bfb S_t^(W.N'Y.N....
03e247ae 5f53 5e74 5728 4ea4 5927 0022 003c 002f S_t^(W.N'Y".<./.
03e623ca 5f53 5e74 5728 4ea4 5927 4e8c 90e8 8bfb S_t^(W.N'Y.N....
03e67ffa 5f53 5e74 5728 4ea4 5927 4e8c 90e8 8bfb S_t^(W.N'Y.N....
05b1633a 5f53 5e74 5728 4ea4 5927 4e8c 90e8 8bfb S_t^(W.N'Y.N....
05b54d1a 5f53 5e74 5728 4ea4 5927 4e8c 90e8 8bfb S_t^(W.N'Y.N....
05b9076e 5f53 5e74 5728 4ea4 5927 0022 003c 002f S_t^(W.N'Y".<./.
05b907f8 5f53 5e74 5728 4ea4 5927 0022 003c 002f S_t^(W.N'Y".<./.
05b99d02 5f53 5e74 5728 4ea4 5927 4e8c 90e8 8bfb S_t^(W.N'Y.N....
05bc143a 5f53 5e74 5728 4ea4 5927 4e8c 90e8 8bfb S_t^(W.N'Y.N....
05bd77ea 5f53 5e74 5728 4ea4 5927 4e8c 90e8 8bfb S_t^(W.N'Y.N....
05bf3c62 5f53 5e74 5728 4ea4 5927 0022 003c 002f S_t^(W.N'Y".<./.
....
07288632 5f53 5e74 5728 4ea4 5927 4e8c 90e8 8bfb S_t^(W.N'Y.N....
....
因为WinDBG不支持中文显示,所以有点不确认上面搜到的信息,为了确认,在记事本里输入上面的中文字符,保存让后用二进制查看工具查看,证实上面搜索到的结果是对的,5f53就是“当”字的编码, 4ea4 5927 就是“交大”。
搜到很多处,看来这个数据被复制了很多次,使用哪一处的数据呢?
当然希望是完整的,于是又搜索了一下文章末尾的一个特征字:
0:045> s -u 10000 L8000000 "曙光"
......
0723225a 66d9 5149 7167 76f8 9986 5f88 5927 ff0c .fIQgq.v..._'Y..
0723befe 66d9 5149 7167 76f8 9986 7684 4f4d 7f6e .fIQgq.v...vMOn.
0723bf2e 66d9 5149 7167 76f8 9986 5f88 5927 ff0c .fIQgq.v..._'Y..
0728985a 66d9 5149 7167 76f8 9986 7684 4f4d 7f6e .fIQgq.v...vMOn.
0728988a 66d9 5149 7167 76f8 9986 5f88 5927 ff0c .fIQgq.v..._'Y..
.......
也找到很多份,看来只要用对了工具和方式,刚刚苦苦寻找不可见的数据现在可以变得俯拾便是。
找两个结果中都有一个地址,观察一下属性:
0:045> !address 0728988a
06fa0000 : 07288000 - 00005000
Type 00020000 MEM_PRIVATE
Protect 00000004 PAGE_READWRITE
State 00001000 MEM_COMMIT
Usage RegionUsageHeap
Handle 00150000
在堆上,靠谱!
接下来的目标的是将数据从内存堆保存到文件,WinDBG已经帮我准备好了一条命令:
0:045> .writemem c:\dumps\blog.txt 07288600 L2000
Writing 2000 bytes....
写好了,打开看一下:
乱码,应该高兴还是失望?
看到这个样子,老实说,我当时挺高兴的,根据经验这很像是中文,Unicode编码的,只不过没有按中文显示罢了。
怎么让编辑器程序按中文显示呢?方法很简单,对于文本文件,只要在文件开头加入Unicode文件的两个标识字符就行了,也就是0xFF FE。
怎么加入这两个字符呢?使用任何二进制编辑工具都可以,手头最方便的就是VC6。右键选择使用VC6打开,文件开头刚好有几个空闲的字符(<P>之类),将其修改为0xFF FE后,另存为blog_u.txt。再打开观看:
哦,成功了!
整理一下,前面那篇文章便发出了,再把失而复得的经过写一下,便有了这一篇,一篇怀旧,一篇技术,送给喜欢不同口味的朋友们!