记VC6中STL的map的一处BUG

今天和同事一起调了一个vc6.0中stl的map的一个bug。

BUG的起因是,我们的项目中使用了stl的map类。而这个map类的对象被用在了不同的DLL模块中,在这样的条件写,BUG就产生了,一个模块内部map对象指针能正常工作,另一个模块内部就出了问题。起初我们就觉得很奇怪,很简单一份代码,怎么会出现访问无效内存的情况,我们还是通常的思路,先在自己身上找问题。调了一会发现,自己的代码确实没有错误。于是我们把目光转向了vc6的stl本身。

跟踪了一下stl的代码,发现错误发生在下面这段代码内部。

20130419140128

在正常的模块中while (_x != _Nil)这个循环只经历了一次,就跳出循环了。而发生错误模块中第二次进入这个循环,也就在这次的循环中,出现了内存访问异常的情况。很明显就要看两次_x != _Nil比较的详细结果。刚开始我被误导了,以为_Nil就是一个为0的常量,把注意力留在_X上后来发现,正确和错误的模块中,_X值一直都是相同的。这才缓过神来,_Nil这个值有问题。

确实,这个不是一个0,更不是一个常量,他是一个静态指针变量。在map对象被创建的时候,生成了一个填充为0的结构体,并且把结构体指针存到了这个变量中。

真相大白了,由于在不同的模块中都是用了stl的map代码,这样map的代码就被编译了两份,同样每个模块中map的_Nil也存放在各自的模块地址范围内。这样就使得_Nil值是不相同的,如果在非创建这个map对象的模块中引用对象指针,并且调用map的函数。如果遇到了_Nil,就会引用此模块自己的_Nil,而不是创建对象模块的_Nil,如果这个模块没有初始化过map对象,那么这个模块的_Nil就是0,即使初始化过,两个模块的_Nil也没可能是同一个值。

新版stl中这个bug必然已经解决,简单来看看vs2010的stl

20130419142503

循环中检查是否为空,用到了函数_Isnil这个,而这个函数查看了_Nodeptr结构中的_Isnil成员变量。判断空放在成员对象内部,这样在多模块之间调用该对象就不会有任何问题了。

话说,vc6的stl确实是bug一堆,很早之前,人们就喜欢用sgi-stl来代替vc6自带的stl了。

DebuggingTips

文件搜索工具everythings工作原理简介

everythings是一个非常强大而且好用的文件搜索工具。他搜索文件的速度非常之快,基本上刚刚输入要查找的文件名,文件已经搜索了出来。这里就简单介绍一下他的工作原理。

有一点已经非常明显,everythings在查找工作开始之前会建立全盘文件索引的数据库,那么这个数据库必然就是搜索文件快速的最重要的原因。而这个数据库的建立的用时似乎非常之短,是普通方法下遍历整个卷所不能及的。

实际上,建立数据库的方法确实比较特殊。简单来说就是遍历目标卷NTFS文件系统的Master File Table(简称MFT)的记录,这也解释了为什么everythings只能工作在是NTFS的文件系统的卷上。MFT可以看成ntfs中文件的索引,MFT中的每条记录都是指向卷中的一个文件。遍历这个索引的速度可要比按照目录递归整个卷的速度要快得多了。不过可惜的是,Windows并没有提供能够直接访问MFT的API,除非你直接解析NTFS磁盘格式(参考这篇文章)。而且就算让你直接遍历的MFT,要监控文件的变化并且写入数据库也是一个不好办的工作。不过可喜的是,微软提供了一种间接遍历MFT记录的方式,并且通过这样的方式可以监控卷上文件的变化,他就是Change Journal(官方文档.aspx))。

Change Journal实际上是Windows 2000的NTFS文件系统就提供了的功能。其目的就是如同名字一样,记录文件改变的日志,方便NTFS文件系统对文件的恢复,这确实是个不错的特性。如何使用这个功能去遍历和监控MFT的记录,我这里就不做详细介绍了。因为有一片更加好的文章已经写的非常的清楚,我的demo也参考了他的很多代码。这篇文章叫做《Keeping an Eye on Your NTFS Drives: the Windows 2000 Change Journal Explained》是1999年9月份的msdn杂志发表的。

我这里假设你已经阅读了这篇文章。我们已经知道了遍历和监控MFT记录的方法,并且通过这个方法获得了每个文件的文件名,file reference和parent reference(可以理解为文件id和其父目录id)。那么这里就可以把这三个元素作为一条记录存储在我们制定的数据库中。

下面简单说下如何利用这个数据库,当需要查找文件的时候:
1.获得用户输入的文件名。
2.通过文件名A可以从数据库中筛选出一些记录,而这些记录中就包括了文件的parent reference。
3.通过parent reference再次查找数据库的file reference字段,获得对应的文件名B,这个文件名B就是文件A的父目录了。重复这一步直到根目录为止。
这样就能找到文件的详细路径了。

我写demo的时候并没有考虑怎么构建这个数据库,直接用了sqlite。不过如果能自己设计一个专门为存储这些数据的小数据库,也应该有更高的效率吧。everythings的数据库就是经过bzip压缩的自定义的文件。

DEMO——构建文件索引数据库:
20130409003609

DEMO——查找文件
20130409003752

P.S 如果采用sqlite作为文件数据库的话,建立索引的插入数据操作一定要利用sqlite的事务机制,否则会在插入数据上花费很多时间。

P.S.2 everythings之所以需要管理员权限才能工作,是因为他需要打开本地卷的句柄,这个就需要管理员权限了。

最后说一点这种方法的不足吧。那就是这种方法对于有多个hardlinks的文件,只能枚举出一个路径。关于hardlink的介绍可以参考这篇文章。由于这个不足,你在查找system32下的文件的时候,往往搜索不到,搜索到的又往往在其他目录,出现尤其多的应该是Winsxs目录了(而关于Winsxs可以看看这篇翻译,翻译质量很不错)。例如我们搜索C盘下的notepad.exe,everythings搜索的结果是这样的:
20130409021029

可以注意到并没有system32下notepad.exe的身影。我们的demo同样也会遇到这样的情况:
20130409021252

下图显示的是同一个notepad.exe的4个存储位置。
20130409021829

虽然everythings有这样的一个不足,但是瑕不掩瑜,他的的确确是一个非常非常优秀的工具!我这里探讨的也仅仅是他的大概原理。实际上这个工具如此优秀,必然是在很多细节上做的非常好才行的。强烈推荐大家使用!

NTInternals

proc_dump_study —— 进程dump工具

proc_dump_study是逆向sysinternals的Procdump的一个工具。在功能上几乎和procdump一模一样。有一点差距就是目前没有支持clr的异常,也就是procdump的-g参数。其他的usage基本上相同,这里也不细说了。想说的一点是,proc_dump_study和procdump一样,功能比较强大,参数也比较多。所以为了方便使用,我把用的比较多的功能总结了一下,写了一个带UI shell程序。这样就方便测试人员或者不想深入理解命令行程序的人员使用。

20130404013936

简单介绍一下使用方法
1.选择要监控或者dump的进程,确定生成dump的文件位置。
2.选择dump类型,包括mini dump,full dump以及effective dump,dump的大小分别为小,大,中等。
3.选择是否监控进程的cpu使用率
4.选择是否监控进程的内存提交数量
5.选择是否监控进程窗口是否挂起
6.选择是否监控进程发生异常
7.选择是否监控进程推出
8.最后点击dump按钮

这样,一旦监控的时候任何监控点达到要求,就会产生dump文件了。如果不需要监控任何进程窗口,程序会立刻dump进程。实际上直接使用proc_dump_study会有更多的功能可以使用,不过这个UI版本应该可以应付大多数的情况了吧。

下载proc_dump_ui

最后放一张测试图
20130402230714

DebuggingNTInternals

find_links_study —— 仿FindLinks的文件硬链接查找工具

在上一篇讨论hardlink的文章中,我在最后提到要写一个查找hardlinks的工具。正如上一篇文章中介绍这种工具的工作原理一样,他的代码比较简单。所以前几天find_links_study就已经写完了,只是一直没空发出来。这个工具的使用和FindLinks一模一样,这里也不多做介绍了。下图是工具的工作效果:

20130316214417

从图中我们可以发现,原来我们在系统中看到的多个notepad文件实际上就是一个文件而已,只不过他利用了hardlink的技术,产生了多个路径罢了。我感觉hardlink还是一个非常实用的技术,具体用在哪大家可以发挥想象力。就我个人看来,在某些情况下,用hardlink来备份文件,倒是一种不错的选择。

下载find_links_study

NTInternals

使用ntfs_study探寻hardlink的本质

在推出ntfs_study的博文中,我谈到过要用一些例子来简单介绍这个工具的用法,本文就算是这个工具的使用介绍以及hardlink在ntfs文件系统底层的简单探讨。

MSDN中写到,hardlink是在文件系统中,用多个在同一个卷的路径表示同一个文件的方法。那么在ntfs格式中这些被link的文件是怎么存在的呢?下面进行一些简单的探讨。

首先,我们需要去创建hardlink的文件。
20130312202150

图中,第一个命令,在target_file.txt的同目录下,创建了hardlink文件link_file.txt。第二个命令,在不同目录(otherdir)下创建了第二个hardlink文件,link_file_in_otherdir.txt。第三个命令返回了错误,原因是我试图在不同卷里面来创建hardlink文件。失败的原因文章后面会介绍。

现在,让我们用ntfs_study来查看ntfs对这三个文件的处理到底是怎么样的。
20130312202324

在这张图中,我们可以清楚的看到,这三个文件的file reference,也就是在主文件表(MFT)的id都是一样的!有一点我们必须明白,一个文件的存在不是因为在目录里面显示了文件名,而是他在MFT中有自己的位置,另外文件名只是文件的一个属性而已,没什么特别的。这也就解释了,看似三个文件为什么会指向同一个文件,因为在目录的记录中,他们指向了同一个id。

为了更加深入的探讨这个问题,我们来看一看id为0xB4A6这个具体情况。首先看看他的file record的数据。

20130312202509

这里可以看到hardlinks的值是4!看到这里,应该就感到奇怪了,我们明明只创建了两个hardlink的文件,为什么这里写的是4呢?实际上,对于ntfs的文件而已,文件名以及他们在那个目录,这些都是属性而已,没有本体和hardlink之分,也就是说,我们原始创建的target_file.txt对于文件本身,也是一个hardlink。那么这个问题还是没解决啊,就算加上本身,最多也就是3个hardlinks,但是这里明明写的是4个!

让我们更加具体的看一看到底是什么回事吧。

20130312202536

首先,我们看到了这个文件的属性中,居然有4个文件名,其中有三个实际上我们已经能够猜到,他们应该分别是target_file,link_file和link_file_in_otherdir这三个名称,那么第四个又是什么呢?只能再进一步看了。

20130312202616

看了这幅图,估计大家就明白了,这个是为了兼容8.3文件名而产生的一个hardlink,只不过在我们现在的系统上隐藏了这个文件hardlink而已。其他三个文件名,如我们刚刚所料,就是那三个文件的名字。

现在解释下为什么hardlink只能在同一个卷里了。原因很显而易见,hardlink实际上是依赖于ntfs的MFT的,而不同的卷,会有不同的MFT,所以不能在不同卷之间创建hardlink也是理所当然的。

SysinternalsSuite中有一个工具叫做findlinks,用来找到一个文件所有的hardlink。其中实现的方法在不同的系统中有所不同,在vista以下的系统中程序调用GetFileInformationByHandle获得文件的MFT id,然后查找整个卷的文件,打开他们获得句柄,再调用GetFileInformationByHandle得到这些文件的id,与之前的id进行比对。可以说,这是非常费时的。而在vista中,这个耗时的问题得到了解决,调用FindFirstFileNameW和FindNextFileNameW就能够文件所有的hardlinks了。

我也计划过两天写一个find_links_study。

NTInternals

access_enum_study —— 仿AccessEnum的工具

access_enum_study 是我逆向AccessEnum所写的程序。写这个逆向加上写这个程序大概用了1个多星期的时间吧。不过说实话,逆向算法还是比较麻烦的事情,所以这个程序里面有一些算法是我自己想的。不过果然不出我所料,效率比起Mark的正牌工具差了不少。这下,真的只能当作玩具玩玩了。

20130309230748

下载access_enum_study

NTInternals

ntfs_info_study —— 仿NtfsInfo工具

ntfs_info_study 这个工具可以显示ntfs卷的一些信息。主要也是学习NtfsInfo的功能,而仿造的一个小工具。ntfs_info_study能显示的信息包括卷大小,扇区数量,簇数量,扇区字节数,簇字节数,主文件表每条记录字节数以及主文件表的一些信息。当然它还可以显示部分NTFS系统文件的信息,例如:$Volume。

实际上ntfs_info_study稍微修复了NtfsInfo的一个问题。原来的NtfsInfo已经无法显示NTFS系统文件的信息了。原因是这个工具调用FindFirstFile这样的函数来查找NTFS系统文件。我不知道什么版本的Windows可以这么做,至少现在Windows 7上,这个方法是行不通的。所以在我从写的工具里,是先打开系统文件,然后查询文件信息,但是普通的CreateFile是打不开这些文件的,这里我的方法是调用OpenFileById。不过实际上,我还没找到正规而且完美显示所有NTFS系统文件的方法,因为部分系统文件在打开的时候会提示访问拒绝。

当然不正规的但是却比较完美的查看NTFS系统文件的方法也有,就是直接打开卷,解析NTFS文件系统数据结构。这个功能已经在ntfs_study中实现了,具体可以移步这个链接

20130227235402

以上是一副对比图,其他功能是一样的,唯一的区别就是最后一项中,ntfs_info_study能够显示部分NTFS系统文件信息。

Usage: ntfs_info_study.exe

使用方法自然也不必说明了,有兴趣的各位可以下载玩玩。

下载ntfs_info_study

NTInternals

pipe_list_study —— 仿PipeList的小工具

昨天在家看完笑傲江湖,没事可做,看了sysinternals的一个很简单的小工具PipeList,然后逆了下,山寨了一个,并且加按照管道名筛选的功能。工具很简单,一共也就200行代码。使用方法如下:

1
2
3
4
Usage: pipe_list_study.exe [search_pipe_name]
search_pipe_name  ---  List the pipes that has search_pipe_name in the pipe name string.
                       Without this argument pipe_list_study will list all pipes.

20130221114347

下载pipe_list_study

NTInternals

又是总结和展望:没有世界末日,生活还要继续

今天总算是有时间,有心情写一篇总结去年生活,展望今年的文章了。

过去的2012年对我来说并不是末日,恰恰相反,他更像是一个新的开始。年初加入新公司,感觉选择还是比较正确的。在新的公司工作,工作不算忙,考勤也比较人性化,环境也还不错。主要是新同事们都还不错,别的不说,侃大山的能力还是很让人钦佩的。所以工作之余也非常娱乐。他们负责娱,我负责乐就行了,哈哈哈哈。刚刚说到工作不算忙,说起来本人也确实在这个环境中挺酱油的,不过俺也不是偷懒,主要是工作上合适我的活不算多。我去小组之后,发现本来打打杀杀的软件,现在要做良民了,不过其实这个做产品的思路是正确的,我也落得点清闲,实在不错。

当然了,本人实际上不甘于清闲。没事的时候还是跟哥们研究了许多编译器和系统底层的机制。除此之外,今年最大的一个收获是把COM这套东西了解一下,这样不仅对今后的工作有所帮助,而且自己本身对代码应该如何写方面也有一些收获。了解了COM当然要了解ATL,因为ATL过于强大,大部分代码我也没有去读,现在而言主要停留在能合理的使用,对于这点我也比较满意,实际上没打算把ATL搞得太清楚。至于编译器,学的也不多,刚刚把语法分析那块弄得一知半解吧。系统底层倒是一直在研究,尤其是文件系统,注册表等。关于系统底层这些,可以看我去年发的几篇blog,都是一些研究后自己写的工具。包括ntfs文件系统学习工具,注册表格式学习工具,pdb的解析工具等等。

回头再看看去年定的目标。。。我只能说“呵呵”。。。没有一个完成的啊有木有!!!果然奇迹没有发生啊有木有!!!那么,我又要给今年定目标了。。。

1.写一个study系列的工具,可以从山寨sysinternals的工具开始做起。

没了,对!就这一条!!!我就不信这一条我也搞不定!!!!!

去年写了总结后,同学们说我生活上的写的太少了,好吧那我简单总结下生活上的开心事!最开心的是,哥开始健身了!!!每周固定和一哥们去三次健身房,每次25分钟跑4k多米,并且锻炼腹肌和肱二头肌。现在人感觉就像回到高中状态。更让人欣慰的事情是能看到一点点腹肌了!另外精神方面,更多的相信“念念不忘,必有回响”,也有称这个为宇宙神秘力量的,不是迷信,而是一种信念,是一种相信只要你不断努力,不断追求就一定能达到目标的执念。不过可惜,我一直念念不忘年会大奖,可惜大宇宙送了我两个字——“呵呵”。。。关于练字,总是断断续续,貌似没啥进步。关于英语,嗯,没了。关于更八卦的生活详情~呵呵~你们以为我会写么!

那么今年生活上的目标呢?!还是健身,练字,英语和嘿嘿~~~

最后还是送祝福时间:祝福我的家人,朋友和我自己,新春快乐,健康平安,家庭和睦温馨!!!

proc_study —— 仿PsList的进程查看工具

proc_study 是我通过逆向PsList而写出来的小工具,如果在本地查看进程,这个工具和pslist没有任何区别。因为实现查看进程的方式也是和pslist一模一样的。另一方面,他缺乏pslist的查看远程计算机的进程的功能。没有实现这个并不是不知道怎么实现,是我半天也没搭建出这样的一个远程环境,真够郁闷的。这个应该是年前的最后一个study系列的工具了。期待蛇年有时间山寨更多工具,嘿嘿~~~

20130204204101

这个工具的使用方法和命令行参数可以直接参看PsList的。因为整个Usage我都是直接山寨过来的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
Usage: proc_study [-d][-m][-x][-t][name|pid]
-d      Show thread detail.
-m     Show memory detail.
-x      Show processes, memory information and threads.
-t       Show process tree.
name Show information about processes that begin with the name
specified.
-e      Exact match the process name.
pid Show information about specified process.
All memory values are displayed in KB.
Abbreviation key:
Pri Priority
Thd Number of Threads
Hnd Number of Handles
VM Virtual Memory
WS Working Set
Priv Private Virtual Memory
Priv Pk Private Virtual Memory Peak
Faults Page Faults
NonP Non-Paged Pool
Page Paged Pool
Cswtch Context Switches

下载proc_study

NTInternals