如何在Linux上实现文件系统的自动检查和修复

平常Linux管理方面最重要的任务之一就是,检查文件系统的完整性。Linux文件系统有可能在各种各样的情况下受到损坏,比如系统崩溃、突然断电、磁盘断开,或者文件节点(i-node)不小心被覆盖等等。因此,定期检查文件系统的完整性是个好主意,以便尽量减小文件系统受到损坏的风险。而说到检查和修复Linux文件系统,fsck是一款实用的工具。

我在本教程中将介绍如何借助fsck工具,实现文件系统的自动检查。

系统一启动,就触发文件系统自动检查机制

如果你希望系统一启动,就自动触发fsck,那么有一些方法可以设置在启动过程中实现无人值守的fschk,这些方法具体取决于特定的Linux发行版。

在Debian、Ubuntu或Linux Mint上,编辑/etc/default/rcS,如下所示。

# 启动过程中,自动修复出现不一致性的文件系统

在CentOS上,使用下列内容,编辑/etc/sysconfig/autofsck(要是它没有,就创建)。

强制定期检查文件系统

如果文件系统很庞大,你可以强制定期检查文件系统,而不是每次系统启动时检查文件系统。为了实现这个操作,先要借助tune2fs命令,找到文件系统配置。下面这个命令行显示了文件系统相关参数的当前值。请注意:/dev/sda1是文件系统超级块所在的分区。

wKioL1NEqAWAGs_iAAEZzcUI6Zg257

从tune2fs的输出结果中,我们不仅可以看到文件系统的状态(干净与否),还可以看到与文件系统检查有关的一些参数。"Maximum mount count"(最大挂载次数)这个参数是指文件系统检查被触发后的挂载次数。"Check interval"(检查时间间隔)这个参数显示了两次文件系统检查之间的最长时间。在大多数Linux发行版上,这些参数并不是默认情况下被设置的,这意味着并不进行任何定期的文件系统检查。

如果想强制每隔30次挂载就检查文件系统,请运行下面这个命令。

如果想强制每隔3个月就检查文件系统,请使用下面这个命令。

现在确认刚添加上去的文件系统检查条件已正确设置。

强制下一次系统重启时,进行一次性的文件系统检查

如果你想在下一次系统重启时触发一次性的文件系统检查,可以使用这个命令。

一旦你在类似上面的顶层目录(/)中创建了一个名为forcefsck的空文件,它就会在你下一次重启时,强制进行文件系统检查。系统成功启动后,/forcefsck则会自动被清除。

wKiom1NEqKqQMvNoAACpmSYvFUE296

Windows机器上程序崩溃dump(UMDF Crash Dump)

最近接到用户的投诉,在Windows 7 32位的机器上,UMDF驱动频繁崩溃,关键是SetUnhandledExceptionFilter 设置的异常过滤竟然一丁点用都没有,查询了半天,才注意到UMDF框架把UnhandledException接管了,你完全是无力反抗。

然后去MSDN上查询,根据Determining Why the Reflector Terminated the Host Process微软文档,WER会在“%windir%\system32\LogFiles\WUDF”目录下面生成DUMP文件,测试之后发现,是否生成完全依赖WER的心情,捣鼓到后来,干脆完全不生成DUMP文件了,另外他偶尔生成的也都是MiniDump ,作用有限。“Users\All Users\Microsoft\Windows\WER\ReportQueue”目录下面的报告数据,也是时有时无,阴晴不定。

继续Google,微软文档 Collecting User-Mode Dumps 设置如下的注册表

这样设置之后,正常的应用都可以在D:\Temp 下面生成崩溃记录,但是WUDFHost.exe 就是不能生成崩溃DUMP,貌似是WUDFHost.exe 用其他低权限用户账户运行,导致没办法生成转储文件。

继续Google,ProcDump进入视野,着实是个好的工具软件,非常好用。于是写了个批处理文件来跟踪WUDFHost.exe的异常。

应用崩溃的时候,会在当前目录下面生产对应的.dmp文件。

本地下载

下面为简单的用法例子

使用源代码将 Glibc 升级到 2.6

简介

有些软件可能要求系统的 Glibc 高于某个版本才可以正常运行。如果您的 Glibc 低于要求的版本,为了运行这些软件,您就不得不升级您的 Glibc 了 。您可以寻找已经编译好的 rpm 包或者使用源代码的方式升级 Glibc。

使用源代码方式升级 Glibc 是需要小心考虑的事情,因为整个系统几乎所有应用程序都依赖于原有的动态库,升级的时候,执行"make install"命令会打断旧的动态库链接,改为指向新的库文件。在这个过程中,不同的链接指向新旧不同版本的库文件,很容易导致系统崩溃,崩溃后,一般是无法重新启动的。

笔者使用的Linux发行版本是Mandriva Linux release 2006.0,其Glibc版本是2.3.5,内核版本是2.6.12-12mdk。由于某些需要,笔者必须升级原来的Glibc到更高的版本。经过实 践,成功使用源代码的方式安全地把Glibc从2.3.5升级到2.6。使用相同的方法,也能成功升级到Glibc2.4或Glibc2.5。成功升级 后,还可以在新旧不同版本的Glibc之间自由切换。

如果您需要阅读有关升级Glibc的文档,可以阅读Glibc 2 HOWTO文档,该文论述了如何从libc5升级到libc6,但是现在的Linux发行版的Glibc都已经使用libc6,更多的需要是在libc6 的范围内,从低版本升级到更高的版本。另外您可以阅读Linux From Scratch项目有关如何安装Glibc的文档。笔者认为最有必要阅读的是Glibc源程序目录树下的INSTALL和FAQ文档,INSTALL详细 描述了有关各编译选项的具体作用,FAQ回答了很多具体的问题。

升级Glibc的流程

为了安全升级Glibc,在升级前必须做好详细的部署和备份,即使是升级失败,系统也要能够还原为原来的状态。升级Glibc失败后,一般是无法重新启动系统的,必须使用另外一个可以启动计算机的Linux系统启动,挂载升级失败的根文件系统,恢复系统的Glibc为原来的状态。因此,准备另外一个可以启动 的Linux系统,是必需的。

准备好另外一个可以启动的Linux系统后,接下来的工作是编译Glibc。编译前要执行"configure"命令,用户可以根据自己的需要,加入不同的编译选项,编译选项在INSTLL文件里有详细的说明。如果是升级当前的Glibc, 必须使用--prefix=/usr选项,该选项指定当前即将编译的Glibc作为系统的标准动态库,安装的时候,Glibc会修改/lib,/usr等 目录下的文件和链接。如果--prefix不是指向/usr,譬如--prefix=/glibc, 安装的时候,Glibc只会在/glibc目录下生成目录,文件以及链接,安装后的Glibc不会成为系统的标准动态库。如果不指定 --prefix,Glibc缺省认为--prefix=/usr/local,安装后的Glibc也不会成为系统的标准动态库。编译Glibc的时候需 要使用Linux内核的头文件,如果内核的头文件太旧,执行"make"的时候,可能会失败,这时可以在configure的时候,使用--with- headers选项指定新内核的头文件的所在目录。笔者的Mandriva Linux release 2006.0的内核头文件就不能令Glibc2.6成功编译,但是却能令Glibc2.4和Glibc2.5成功编译,使用--with-headers 指向新内核的头文件后,Glibc2.6也能成功编译,下文将会有详细叙述。

Glibc安装的时候,会直接修改/lib,/usr/lib等目录下的文件和链接,为了在升级失败的情况下能够恢复系统的Glibc为原来状态,在安装新版本的Glibc之前必须对一些重要目录进行备份,至于哪些目录需要做备份,下文将会有叙述。

备份好重要目录后就可以安装Glibc了,必须由root用户执行make install命令,不同的Linux发行版,可能会有不同的结果。笔者使用的Mandriva Linux release 2006.0在这个过程中会出现错误,并且输入任何命令都无效,重新启动也无法再次进入Linux系统。出现的这样的错误是由于Coreutils的命令 都是依赖于/usr/tls/目录下的链接文件,这些链接文件都指向原来的Glibc动态库文件,而/lib/ld-linux.so.2已经被改为指向 新版本的Glibc动态库文件了,这些重要链接分别指向新旧不同版本的库文件,导致了执行Coreutils的命令无效。这个时候,就要用另外一个 Linux系统启动,把/usr/tls/目录下的链接修改为指向新版本的Glibc的库文件。

经过上述修改,重新启动计算机,如果能重新 进入原Linux系统,表明到目前为止升级过程都是正确的。这个时候,要再执行一次"make install"重新安装Glibc,正常情况下,这次不会出现任何错误,安装结束的时候,Glibc会给出安装成功的消息。如果修改/lib/tls /下的链接后重新启动计算机仍然不能进入原Linux系统,表明这次升级失败,只能再次使用另外一个Linux系统启动,挂载升级失败的根文件系统,把先前备份好的重要目录恢复为原来的名字,经过这样的恢复,一般是可以重新启动升级失败的Linux系统的。

升级的最后一步是执行"make localedata/install-locales"命令安装时区和地区数据库。

图一:升级Glibc的流程图

image002

注意

本文介绍的方法在升级过程中会出现一次链接错误,系统无法输入命令,重新启动也无法进入Linux,必须使用另外一个Linux系统启动,并挂载原根文件系 统,修正在升级过程中产生的错误链接,然后才能继续升级。因此在升级之前,一定要先确保拥有另外一个可启动的Linux系统。此外,还要备份下文将会论述 的一些重要目录,如果最终升级失败,需要用另外一个Linux系统启动,恢复原Linux系统的Glibc为原来的版本。

建议在升级之前,详细阅读Glibc源代码包内的INSTALL和FAQ文件。

升级过程

本文假设进行编译的用户名字是xyz,用户目录是/home/xyz/,所有源代码都在/home/xyz/build/目录下编译。所有源代码压缩包都已 经拷贝到/home/xyz/build/目录下。升级Glibc的Linux系统的根文件系统使用hda5分区,其文件系统格式为xfs。

1 准备另外一个可启动的Linux系统

准备另外一个可启动的Linux系统的方法有很多,可以在硬盘上安装另外一个Linux发行版,可以在DOS环境下使用loadlin.exe启动一个 Linux系统,最简单的方式是使用Live CD,从光驱启动。无论使用什么形式,它都必须能挂载需要升级Glibc的根文件系统。

如果选择Live CD启动,Knoppix是一个很好的选择。您可以从Knoppix的网站下载iso文件,烧制成启动CD,也可以把Knoppix的系统文件拷贝到硬盘 上,在DOS环境下,执行loadlin.exe启动。笔者使用了Knoppix 5.0.1,制作成Live CD启动。参考资料有介绍如何使用 Knoppix 进行系统恢复。

2 编译

Glibc的编译安装与一般的软件很类似,都是要经过configure,make和make install三个阶段。Glibc不能在源代码目录下编译,必须在一个空目录下编译。

编译Glibc是需要内核头文件的,笔者的Mandriva Linux release 2006.0的内核版本是2.6.12-12mdk,可以直接使用该版本内核的头文件升级到Glibc2.4 和Glibc2.5。如果升级到Glibc2.6,在make过程中,会出现以下错误:

这是由于2.6.12-12mdk的内核头文件太旧,为了顺利编译Glibc2.6,必须使用新的内核头文件。

2.1 编译Glibc2.4或Glibc2.5

如果要升级Glibc到2.4或2.5,configure的时候只需要--prefix=/usr一个选项,意思是该Glibc将会作为系统的标准动态库。

以升级到Glibc2.5为例,执行以下命令进行编译:

glibc-2.5.tar.bz2是Glibc2.5的源代码压缩包,执行"tar xjf glibc-2.5.tar.bz2"命令后,在~/build/目录下产生了glibc-2.5目录树,Glibc2.5的源代码就在这个目录里面。 Glibc不能在源代码目录下编译,因此执行了"mkdir glibc-2.5-build"命令,生成glibc-2.5-build目录,编译Glibc2.5时产生的文件,保存在这个目录下。如果顺利,将会成功编译,不会出现错误信息。

2.2 编译Glibc2.6

如果您打算升级到 Glibc2.6,使用版本为2.6.12-12mdk的内核头文件是不能成功编译的,必须使用新的内核头文件。configure的时候使用 --with-headers选项指向新的内核头文件的目录。Glibc的FAQ文档明确指出最好使用最新版本的内核头文件,而且编译时需要的内核头文件 的版本不需要与当前正在运行的内核版本一致。笔者试验了版本为linux-2.6.20.7和linux-2.6.18两个版本的内核头文件,编译时系统 运行的内核版本是2.6.12-12mdk,都可以成功编译Glibc2.6。

为了使用新版本的内核头文件,解开新版本的内核压缩包后,在源代码目录下执行"make menuconfig",这将会进入内核的设置页面,不需要作任何处理,直接退出并保存。然后执行"make",不需要等待make完毕,一开始看到

就可以按下ctrl+c停止编译内核。执行"make"的目的是在内核源代码目录树内生成一些重要的文件和软链接,其中在include/目录下产生一个名为asm的软链接指向asm-i386目录,没有这个链接,Glibc2.6是无法编译的。

假设使用linux-2.6.20.7的内核头文件,执行以下命令:

linux-2.6.20.7.tar.bz2是2.6.20.7版本的Linux内核源代码压缩包,执 行"tar xjf linux-2.6.20.7.tar.bz2"命令后,将会产生linux-2.6.20.7目录,Linux内核的源代码都在这个目录下,其中内核头 文件在linux-2.6.20.7/include/下。确保linux-2.6.20.7/include/asm已经产生后,就可以停止内核的编 译,然后进行Glibc2.6的编译:

glibc-2.6.tar.bz2是Glibc2.6的源代码压缩包,执行"tar xjf glibc-2.6.tar.bz2"命令后,在~/build/目录下产生了glibc-2.6目录树。

如果您的系统当前运行的内核已经升级到比较新的版本,例如2.6.20.7,configure的时候,就不需要--with-headers选项了,与 Glibc2.4或Glibc2.5的configure命令一样,只需要--prefix=/usr选项就可以了,但是前提条件是/lib /modules/uname -r/source指向内核的源程序目录,您可以用以下命令查看它指向哪里:

3.make install前的准备工作

3.1 哪些目录需要备份?

安装Glibc前必须备份好原来的系统动态库,一旦安装失败,使用另外一个Linux系统启动,挂载升级失败的根文件系统,还原Glibc为原来的状态。可是哪些目录需要备份?Glibc在安装的时候,将会修改哪个目录下的文件?

Glibc 在安装时可以指定install_root选项,命令Glibc把该选项指向的目录作为根,文件都安装到这个目录下,通过观察Glibc在这个目录里生成了什么目录和文件,我们就可以知道Glibc在安装的时候到底会修改哪些目录了。假设安装到~/build/system_fake_root上:

安装后:

可以看出,Glibc的make install命令会修改/etc,/lib,/sbin和/usr目录下的文件。这里最重要的是/lib和/usr。一般来说/usr目录下有很多文件,占用硬盘空间很大,实际也不需要对整个/usr目录备份,使用以下命令查看Glibc会修改/usr/下的哪些目录:

经分析,笔者选择了备份/lib,/usr/lib,/usr/include,/usr/sbin和/usr/bin。您当然可以备份更多的目录,不过笔者发现备份上述5个目录已经可以恢复系统的Glibc为原来的版本了。

3.2 备份目录

使用su命令切换为root。执行:

经过以上步骤,就备份了以后还原用的目录。备份的目录名字使用原目录名后加上.2.3.5后缀,目的是区分不同的Glibc版本目录。

3.3 如何还原系统的Glibc

如果升级最终失败,导致系统无法启动,就必须使用另外一个Linux系统启动,还原上述备份的目录为原来的名字。以笔者的系统为例,升级失败的根文件系统使用hda5分区,其文件系统格式为xfs,将被挂载到/mnt/m1目录。使用Knoppix 5.0.1 Live CD启动后,打开一个控制台,执行"su"命令切换为root用户,执行以下命令

执行上述命令后,Glibc已经被恢复为原来的版本,系统应该可以正常启动。

3.4 如何知道当前的Glibc版本

Mandriva Linux release 2006.0的动态库是Glibc2.3.5。执行"/lib/libc.so.6"可以知道当前的Glibc是什么版本:

从以上输出可以看到,当前系统的Glibc版本是2.3.5,使用Gcc4.0.1编译,编译时运行的Linux内核版本是2.6.12。

Glibc的FAQ文档给出以下程序用于查看当前的Glibc的版本:

把上述程序写到chk_lib_v.c文件,执行以下命令编译:

执行chk_lib_v后有以下输出:

3.5 注意Coreutils依赖的动态库情况

Mandriva Linux release 2006.0系统的/lib/目录下有个tls目录。该目录下的文件是能否成功升级Glibc到2.6的关键。使用ldd命令查看Coreutils的应用程序依赖的动态库情况可以发现:

请注意上述的librt.so.1,libc.so.6和libpthread.so.0,它们都是位于/lib/tls/,而不是/lib/。换言之,ls的执行,依赖于/lib/tls目录下的某些动态链接库文件。tls是thread-local storage,当不同的线程使用同一个全局变量的时候,各线程将会在本地保留该变量的备份,查阅参考资料可以了解更多有关tls的信息。 Glibc2.4,Glibc2.5和Glibc2.6都支持tls。

/lib/tls/下有以下文件:

/lib/tls目录下的文件是一些很重要的动态库及软连接,其中包括libc.so.6,它们都是指向2.3.5版本的动态库文件。

4. 安装Glibc2.6

4.1 第一次make install

切换到root用户,执行make install就开始安装Glibc了:

在安装过程中,将会出现如下错误:

安装失败后,输入任何命令都是无效的,系统只会重 复"relocation error: /lib/tls/libc.so.6: symbol _dl_out_of_memory, version GLIBC_PRIVATE not defined in file ld-linux.so.2 with link time reference"的错误信息,重新启动计算机在启动中就会失败,根本无法进入原Linux系统。

出现这样的错误的原因是 Coreutils的应用程序都依赖于/lib/tls/下的动态库,在make install的时候,/lib/ld-linux.so.2从原来指向ld-2.3.5.so被改为指向ld-2.6.so,但这个时候/lib /tls/libc.so.6指向的仍然是/lib/tls/libc-2.3.5.so。/lib/ld-linux.so.2和/lib/tls /libc.so.6各自指向不同版本的库文件导致了Coreutils的命令执行失败,从而make install也失败。

这个时候, 就要用另外一个Linux系统启动,挂载升级失败的根文件系统,把原根文件系统的/lib/tls/下的链接全部改为指向2.6版本的库文件,具体就是 /lib/tls/libc.so.6,/lib/tls/libm.so.6, /lib/tls/libpthread.so.0和/lib/tls/librt.so.1这4个软链接分别指向libc-2.6.so, libm-2.6.so,libpthread-2.6.so和librt-2.6.so。libthread_db.so.1仍然是指向 libthread_db-1.0.so,但这个时候/lib/libthread_db-1.0.so已经是Glibc2.6版本的了,原/lib /tls/libthread_db-1.0.so必须被替换为Glibc2.6版本的libthread_db-1.0.so。

笔者使用Knoppix 5.0.1 Live CD启动计算机,启动后,打开一个控制台,执行"su"命令切换为root,执行以下命令:

上述命令就是把原根文件系统/lib/下的libc- 2.6.so, libm-2.6.so,libpthread-2.6.so和librt-2.6.so拷贝到/lib/tls/下,并把/lib/tls /libc.so.6,/lib/tls/libm.so.6,/lib/tls/libpthread.so.0和/lib/tls /librt.so.1这4个软链接从原来指向2.3.5版本的库文件改为指向最新的2.6版本的库文件。原/lib/tls /libthread_db-1.0.so下的文件被/lib/libthread_db-1.0.so替换。

4.2 第二次make install

修改了/lib/tls/下的链接和文件后,就可以重新启动计算机,进入原Linux系统,这次将会正常启动,登陆后,执行Coreutils的命令,例如 ls,已经不会出现"relocation error: /lib/tls/libc.so.6: symbol _dl_out_of_memory, version GLIBC_PRIVATE not defined in file ld-linux.so.2 with link time reference"的错误信息。由于Glibc2.6没有彻底安装完毕,我们还要重新执行一次make install:

这次make install将不会出现错误,如果安装成功,到最后会有以下信息提示:

执行/lib/libc.so.6查看Glibc的版本:

这都表明当前系统的动态库已经使用了Glibc2.6。

如果把/lib/tls/下的链接改为指向2.6版本的库文件后,重新启动计算机仍然无法进入原Linux系统,就说明升级Glibc失败,只能再次使用另外一个Linux系统启动,恢复原系统的Glibc为原来的版本,请参考3.3节。

4.3 安装时区和地区数据库

虽然现在系统已经使用Glibc2.6,但是在X Window下,使用fcitx中文输入法的用户会发现按下ctrl+space不会弹出中文输入框。这是因为新的Glibc2.6还没安装时区和地区数据库。执行以下命令:

上述命令将会在/usr/lib/locale/下生成一个名为"locale-archive"的文件。重新启动X Window, 按下ctrl+space就可以调出中文输入框并输入中文了。

至此,升级Glibc完毕。笔者在升级到了Glibc2.6的Linux系统上使用了 VMware,Openoffice,Mplayer,Skype,Firefox,Thunderbird,Vim,Fcitx等一系列软件一段时间, 未发现由于升级了Glibc而导致的错误出现。

回到Glibc2.3.5 成功升级后,想在Glibc2.6和原来的Glibc2.3.5之间切换是很容易的。因为安装前对Glibc2.3.5的重要目录进行了备份,只需使用另 外一个Linux启动,挂载原Linux的根文件系统,把升级前备份好的/lib.2.3.5等目录的名字改为/lib等原来的名字即可。

使用Knoppix 5.0.1 Live CD启动,打开一个控制台,执行"su"命令切换为root,执行以下命令:

重新启动,系统将会使用原来的Glibc2.3.5,Glibc2.6的目录备份为原目录名后加.2.6后缀。

总结

使用源代码升级Glibc,做好升级前的准备是最重要的。首先要准备好另外一个可以启动的,能挂载原根文件系统的Linux系统。其次是对/lib, /usr/lib,/usr/include,/usr/sbin,/usr/bin等目录进行备份。成功升级后,可以在高低两个版本的Glibc之间自 由切换。

升级前只要做好上述两个准备,升级系统的Glibc是安全的。

由于不同的Linux发行版的系统环境不一样,因此使用源代码升级Glibc的过程可能会有差异,为了成功升级,读者必须在升级过程中根据自己的系统的实际情况作相应的调整。

链接地址

http://www.ibm.com/developerworks/cn/linux/l-cn-glibc-upd/

VisualSVNServer Service failed to start

这是VisualSVN Server Manager启动时报出的一个启动错误,可能的问题有以下3种。

1:端口被占用:使用CMD命令

查看端口是否已使用,如果已使用则更换一个端口。

2:Repositories 的物理目录不存在,重新设置Repositories  物理路径。

3:服务VisualSVN Server Manager 服务启动用户设置不正确,在系统服务中,找到 VisualSVN Server 点击右键——属性——登录选项卡,如果是网络用户,就尝试切换到本地用户试试,如果是本地用户,就尝试指定一下其他用户试试。

VistualSVN

解决Win7“无法访问Windows Installer服务。Windows Installer没有正确安装时可能发生这种情况...”

今天在卸载一个软件时,发生错误,提示信息如下:

无法访问Windows Installer服务。Windows Installer没有正确安装时可能发生这种情况...

截图如下:
1356154505_5881

在网上找了下原因,都知道是Windows Installer服务出现了问题,如没有启动它呀,

启动不了Windows Installer呀之类的...却没有给出一个有用的解决方法。

好不容易知道微软提供了一个 Windows Fix it 服务,网址如下

http://support2.microsoft.com/default.aspx?scid=kb;%5BLN%5D;290301
或者
http://support2.microsoft.com/fixit/zh-cn
或者
Fix problems that block programs from being installed or removed
经过一番折腾,搞定!

目前(2018.03.14)貌似微软关闭了这个网站,可以在本站下载Microsoft Fix it 51015

这个工具微软的下载地址为http://download.microsoft.com/download/D/9/7/D97359C4-B5EA-40C7-9991-3EEF14D4848F/MicrosoftFixit51015.msi

如何在VS2010 IE11 下调试BHO控件

VS2010 IE11 下调试BHO控件,在新版本的调试器中已经简化调试流程,简单的设置一下,就可以直接调试BHO的DLL文件了,方便很多。

如下图设置,点击工程属性,然后选择 “调试”在“要启动的调试器”中选择 “Web 浏览器调试器”,在“HTTP URL”中输入具体的网站地址,然后直接启动调试即可。

BHODebug

用Visual Studio 2008开发IE BHO (浏览器帮助对象)之一

BHO 通常并不提供其自身的任何用户界面 (UI)。它们而是通过在后台响应浏览器事件和用户输入数据来发挥作用。例如,BHO 可以拦截弹出窗口、自动填充窗体或为鼠标手势添加支持。

有一种常见误解认为工具栏扩展项需要 BHO.但如果将 BHO 与工具栏配合使用,则可以实现更丰富的用户体验。(关于IE工具栏的编程,在另一篇文章中说明).

BHO 的生命周期与它所交互的浏览器实例的生命周期相等。在 IE 6 和早期版本中,这意味着要为每个新的顶层窗口创建(和销毁)一个新 BHO。在IE 7中则是为每个选项卡都创建和销毁一个新 BHO。

BHO 必须实现 IObjectWithSite 接口, 该接口提供了两个方法GetSite和SetSite。根据MSDN的说明:

我们主要是对后者进行调用,此方法方便了与 Internet Explorer 的初始通信,并会在其将要释放时通知 BHO。我们实现此接口,然后将 BHO 的 CLSID 添加到注册表中,就可以创建一个简单的浏览器扩展。过程如下:

1. 在Visual Studio中,选择VC++中的ATL项目, 创建一个新的项目MySolutionPlugin, 在随后的向导中,确认Server Type是Dll, Visual Studio会为我们创建程序的模板.

2. 为该项目添加我们的程序主体, (不熟悉visual studio的同学在资源浏览器里的右键菜单里选 add-->class, 可别选到New Item), 类型选ATL Simple Object ,  short name命名为RayBHO,各项属性如下:
a) “线程模型” ---“Apartment”
b) “聚合”---“否”
c) “接口”---“双重”
d) “支持”---勾上“IobjectWithSite”。

具体的含义请参考MSDN.

一般来说,Internet Explorer 至少调用SetSite方法两次: 一次用于建立连接,另一次则是在浏览器退出时。我们 BHO 中的 SetSite 实现将执行以下操作:

•存储对站点的引用。在初始化期间,浏览器将 IUnknown 指针传递给顶层 WebBrowser 控件,然后 BHO 将对它的引用存储在一个专用成员变量中。

•释放目前被占用的站点指针。Internet Explorer 传递 NULL 时,BHO 必须释放所有接口引用并且断开与浏览器的连接。

要实现SetSite,我们需手工在添加一个public的方法:

STDMETHOD 宏是将方法标记为虚方法并且确保其具有适用于公共 COM 接口的调用约定的一个ATL 约定, 它有助于区分 COM 接口和该类中可能存在的其他公共方法。其实现成员方法时应相应使用 STDMETHODIMP 宏。同时我们需要声明一个私有变量来保存Browser的指针"

私有变量
然后是SetSite的实现

从上面的介绍我们知道, 初始化期间,浏览器将传递一个对其顶层 IWebBrowser2 接口(我们对其进行缓存处理)的引用。浏览器关闭时将传递 NULL,为避免内存泄漏和循环引用计数,此时释放所有指针和资源非常重要。最后,我们调用基类实现以便继续执行接口合约的其余部分。

加载DLL 后,系统将通过 DLL_PROCESS_ATTACH 通知调用 DllMain 函数。由于 Internet Explorer 大量使用多线程,因此,对 DllMain 的频繁的 DLL_THREAD_ATTACH 和 DLL_THREAD_DETACH 通知会降低扩展和浏览器进程的整体性能。

如果BHO 不需要线程级的跟踪,我们可以在 DLL_PROCESS_ATTACH 通知期间调用 DisableThreadLibraryCalls 以避免新线程通知的额外开销。修改DllMain.cpp 中的DllMain函数:

要使BHO工作,我们还需要把BHO 的 CLSID 添加到注册表中。此条目会将 此DLL 标记为浏览器帮助程序对象,并使 Internet Explorer 在启动时加载 BHO。我们可以在MySolutionPlugin.idl中找到该BHO的CLSID.幸运的,Visual Studio会帮助我们实现这些, 你看到:

您的机器的CLSID可能有所不同, 接着打开RayBHO.rgs文件,添加入:

这一段是为了在注册表里添加一个双字节的NoExplorer=1的键,不让Windows Explorer加载该BHO,因此该BHO只能在ie中运行.

注意,这里的UUID字段跟MySolutionPlugin.idl 中的是一致的。

你可以编译这个BHO. 如果一切正常, 你可以在IE的管理加载项里看到这个BHO.

如果不幸报错: 该BHO无法被注册,根据我的经验,原因大概有2类,可以依次检查

1. 你是否有管理员权限以修改注册表,如不是管理员身份,可以在菜单上右击Microsoft Visual studio 2008,从右键菜单中选择"运行方式"...
2. 你的注册表条目语法是否正确,或者含有非法字符.

引用 http://blog.csdn.net/dupei/article/details/6092692

802.11 Four-way handshake Messages

1.  4-way handshake sequence

0_13104396958zm8

2. Key Heirarchy

0_1310440023gpQ0

The EAPOL encryption key is the middle 128 bits of the PTK value.

And the first 128 bits of the PTK (KCK), is used in the computation(and validation) of the EAPOL frame MIC field value (4way handshake Message 1/2).

3. EAPOL Frame format

0_1310440523Xe68

4 Key Data Format

0_13104406628SwT

Key data may be zero or more InformationElement(s) (such as the RSN information element), and zero or more key dataencapsulation(s) (KDEs) (such as GTK(s)).

4.1 RSN Information

0_1310440804770w

4.2 GTK

If theEncrypted Key Data subfield (of the Key Information field) is set, the entireKey Data field shall be encrypted. If the Key Data field uses the NIST AES keywrap, then the Key Data field shall be padded before encrypting if the key datalength is less than 16 octets or if it is not a multiple of 8. The paddingconsists of appending a single octet 0xdd followed by zero or more 0x00 octets.When processing a received EAPOL-Key message, the receiver shall ignore thistrailing padding.  Key Data fields that are encrypted but do not containthe GroupKey or STAKey KDE, shall be accepted.

If the GroupKey or STAKey KDE is included in the Key Data field but theKey data field is not encrypted the EAPOL-Key frames shall be ignored.

0_1310440955sZ01

5. Sample 4-way Handshake

5.1 Message 1

The Authenticator sends an EAPOL-Key frame containing an ANonce.

0_13104413205d5G

Key data is zero.

5.2 Message 2

The Supplicant derives a PTK from ANonce and SNonce.

The Supplicant sends an EAPOL-Key frame containing SNonce, the RSN information element

from the Association Request frame and a MIC.

0_1310441522WyDx

MIC is the KCK (The first 128 bits in the PTK).

Key data is RSN Information.

802.1X authentication, CCMP pairwise dna group cipher suites.

5.3 Message 3

The Authenticator derives PTK from ANonce and SNonce and validates the MIC in the EAPOL Key frame

The Authenticator sends an EAPOL-Key frame containing ANonce, the RSN from its Beacon or Probe

Response messages, MIC whether to install the temporal keys, and the encapsulated GTK

0_1310442380H8du

Should the MIC data  be same with the one in Message 2   ?!

Key data is RSN Information + GTK,  and the data is encapsulated by using of the AES algrithm with the KEK(The middle 128 bits in PTK).

5.4  Message 4

To be added.

原始链接 http://blog.csdn.net/stevenliyong/article/details/6599528

在VC中编译和使用OpenSSL

在编译OpenSSL前,需要正确安装Perl,因为在编译OpenSSL时需要使用到该程序。

下载最新版本的Perl:http://downloads.activestate.com/ActivePerl/releases/5.20.1.2000/ActivePerl-5.20.1.2000-MSWin32-x64-298557.msi。然后安装之。

Windows 一定要使用 ActivePerl 主要是由于换行跟Linux ,Unix 的不同,如果使用其他的Perl生成的MakeFile 文件,会导致NMake无法正常的编译。

另外,一定要确保ActivePerl在系统的PATH 环境变量中在最前面,并且在命令行中执行 PATH命令的输出是跟环境变量中是一致的,Windows中有时候会发生刚刚添加的环境变量不能及时更新到命令行界面的问题,即使是你已经重新打开新的命令行窗口了。

下载最新版本的OpenSSL:http://www.openssl.org/source/openssl-1.0.1j.tar.gz

一定要使用最新的OpenSSL,早期的版本有Heartbleed的BUG,另外一定要是新下载并且刚刚解压缩出来的,没有修改过的文件,有时候,某些不经意的修改会导致编译不成功。

然后将源码释放的c:\openssl-1.0.1j目录中。

进入openssl源码目录。

以下为参照该目录下的文件INSTALL.W32的执行过程:

运行configure:

创建Makefile文件:

此处建议执行 do_ms ,有时候执行do_nasm会不正常,尽管文档中说执行 do_nasm

编译动态库:

编译静态库:

测试动态库:

测试静态库:

安装动态库:

安装静态库:

清除上次动态库的编译,以便重新编译:

清除上次静态库的编译,以便重新编译:

安全协议系列(三)----CCMP与WPA-PSK

本节讨论 CCM 在 WiFi 中的实际应用 -- CCMP 协议

根据 RFC 3610,完成 CCMP 报文的加解密,需要提供:分组密钥(K)、随机数(Nonce)、附加认证数据(AAD),这三个参数从哪里来?

另外, 作为处理对象的 CCMP 报文又来自哪里? 正常是通过抓包获取,但无线报文比普通的有线(以太)报文抓取相对麻烦点
幸运的是,万能的 Internet 已经给我们准备好了,在 Wireshark 网站 -- wiki.wireshark.org -- 中有个网页链接
主流协议的报文都被世界各地的网友抓取并上传到链接指向的页面上
进入页面,下载其中一个叫 wpa-Induction.cap  (Wireshark 原始地址点击这里)的抓包文件,该文件将作为后续的解密报文

至于分组密钥 K,它与 STA 如何接入 WiFi 网络有关,具体而言
如果 STA 是通过 WPA-PSK/WPA2-PSK 模式(这是家用无线路由器中使用最多的模式)接入,则 K 来源于配置此模式时输入的密码(后面记为 PSK)
如果 STA 是通过 WPA-Enterprise/WPA2-Enterprise 模式接入,则 K 来自 802.1X 认证过程中协商出来的密钥(后续会单独讨论 802.1X 认证)
这两种模式下,STA/AP 双方最终都会得到名为 PMK 的密钥
PMK 的作用类似一个密钥种子,它衍生出(更为准确说是协商出)密钥 PTK(其中就包括分组密钥 K)

就文件 wpa-Induction.cap 而言,它抓取的是 WPA-PSK 模式下 STA 与 AP 的交互报文,AP 的密码(PSK)为 Induction

本节讨论 WPA-PSK 模式下:无线网络密码(PSK) --> PMK --> PTK --> K 的具体变化情况

在无线路由器上配置过 WPA-PSK 模式的网友知道,配置时除了要输入 PSK,还要指定无线网络名(标准名为 SSID)
SSID 有两个作用:标识 AP 自己的名称(与其他 AP 区分出来),另一个就是参与 PMK 的构造
构造过程遵循 PKCS #5 v2.0 中的 PBKDF2 标准,简言之,我们可以认为 PMK = PBKDF2(PSK, SSID)

wpa-Induction.cap 中的 SSID 又是多少?这需要查看类型为 Beacon 的报文
其中有个字段解析为 SSID parameter set: Coherer,即 SSID 名称为 Coherer

这里我们看到,一旦输入的 PSK 和 SSID 固定,PMK 就不再变化,这带来了一定的安全性问题
因为知道 PSK 的 STA 可以通过抓取四次握手报文,嗅探别的 STA 与 AP 之间的流量(见后面详细说明)
在 WPA-Enterprise/WPA2-Enterprise 模式中,PMK 是动态生成的,避免了上述担心

脚本 PBKDF2.pl 是生成 PMK 过程的代码实现,下面是其内容及执行结果

C:\>perl PBKDF2.pl Induction Coherer
输出如下:
Salt = SSID = Coherer
Password    = Induction
WPA-PSK:PMK = A288FCF0CAAACDA9A9F58633FF35E8992A01D9C10BA5E02EFDF8CB5D730CE7BC

前已述及,PMK 会协商出 PTK,这个协商过程就是著名的 EAPOL-Key 四次握手
具体而言,四次握手的前两个报文(分别由 STA/AP 发出)各自包括了一串名为 WPA Key Nonce 的随机数(分别记为 ANonce/SNonce)

在 wpa-Induction.cap 中,EAPOL-Key 四次握手报文分别为 #87 #89 #92 #94,其中
#87 报文包含 WPA Key Nonce: 3e8e967dacd960324cac5b6aa721235bf57b949771c867989f49d04ed47c6933 -- ANonce
#89 报文包含 WPA Key Nonce: cdf405ceb9d889ef3dec42609828fae546b7add7baecbb1a394eac5214b1d386 -- SNonce

光有这两个随机数还不够,IEEE 802.11i 中还规定,STA 和 AP 的无线 MAC 地址(分别记为 AA/SPA)也要参与 PTK 的生成
最终生成 PTK 需要五个参数:PMK、AA、SPA、ANonce、SNonce

脚本 PMK2PTK.pl 对上述过程进行了实现,下面是其内容及执行结果

C:\>perl PMK2PTK.pl a288fcf0caaacda9a9f58633ff35e8992a01d9c10ba5e02efdf8cb5d730ce7bc 000c4182b255 000d9382363a
3e8e967dacd960324cac5b6aa721235bf57b949771c867989f49d04ed47c6933
cdf405ceb9d889ef3dec42609828fae546b7add7baecbb1a394eac5214b1d386
上面命令行实际在一行内输入,只处为折行显示

输出如下:
EAPOL-Key Confirm Key               b1cd792716762903f723424cd7d16511
EAPOL-Key Encrypt Key               82a644133bfa4e0b75d96d2308358433
(TKIP|CCMP) Temporal Key            15798d511beae0028313c8ab32f12c7e
TKIP MIC Key(for Authenticator Tx)  cb71c893482669da
TKIP MIC Key(for Supplicant Tx)     af0e9223fe1c0aed

从输出结果中看到,PTK 并不是一个单独的密钥,而是分成若干部分
其中 EAPOL-Key Confirm Key 和 EAPOL-Key Encrypt Key 仅用于 EAPOL-Key 的四次握手(对握手报文起加密和认证作用)
第三行 (TKIP|CCMP) Temporal Key 参与报文的加解密,在我们讨论的上下文中,就是 CCM 中的分组密钥 K
最后二行 TKIP MIC Key(...) 仅适用于 TKIP 协议,与 CCMP 协议无关

下面以报文 #99 为例,剖析 CCMP 的具体操作,报文内容如下
  08 41 2c 00 00 0c 41 82 b2 55 00 0d 93 82 36 3a -- IEEE 802.11 Data 报文头
  ff ff ff ff ff ff b0 01 01 00 00 20 00 00 00 00
  7e cc f6 0a c1 dd ff b0 47 96 c3 0b a1 9c 92 c6 -- 此行开始共 336 字节(共 21 行)为密文部分
6f 4d 1c e7 27 08 c2 95 cf 58 19 45 8c 18 d5 1f    提取为文件 wpa-Induction-cipher.txt
64 56 7a 7c c5 ff 85 e7 a6 8b 23 8a 33 5e 44 44
f7 de 0c 5e ef 72 1d 9f db 0d 51 44 03 d1 c9 06
46 15 23 3e fc e2 4b 41 6d 53 8c 88 84 5e 46 0d
29 63 0e da 72 97 fd db b5 66 ac 0a 05 f9 21 1f
bf 24 39 9a 15 a9 15 11 04 39 bd 0c 0c 51 0a 08
4a 88 90 50 01 fc 64 cc 9a 4f ca d2 51 d6 e0 f1
55 00 b7 13 fb 42 c2 44 60 58 2a 68 d0 a5 b9 9c
80 8e 01 2c 20 0a c5 27 b0 eb 32 0f 75 7d 60 ea
01 fa 79 f6 5c 2f c3 55 66 90 62 d9 25 e3 e4 4c
02 91 c1 a7 36 d5 0f 0b 8c 6c 68 de 9e 53 6e d9
7f eb 43 93 82 80 4b 73 92 3a 61 7f cc ef 37 60
cf 65 98 f7 7e 39 b9 90 a6 d1 67 ab 5c a6 a9 57
76 38 fe a8 34 2c 97 ab d5 54 f5 6f ea 48 eb 48
be 52 df c8 27 66 7b 1c 09 08 78 58 b9 96 9a 74
10 2d 53 e3 7d 35 2e e4 62 44 84 3d 02 f5 1b 04
43 64 cb 26 33 fd 2e 8c 16 0a 21 31 24 56 e5 74
74 89 33 e0 d8 49 5b e8 23 97 d8 9c b7 39 f7 ab
a0 e8 44 c2 b8 dc 3a 3d 57 d1 a7 b0 7e a9 ff 97
a3 d7 17 ff 02 83 0b 58 2c a8 94 27             -- 本行中前 8 字节为认证字段部分,后 4 字节为 FCS
                                                     CCMP 协议规定:认证字段长度 M = 8

现在已经知道分组密钥 K = 15798d511beae0028313c8ab32f12c7e
另两个参数 AAD/Nonce,它们在 IEEE 802.11i 有详细定义,这里直接给出结果

AAD = l(a) || a,构造比较复杂,我们估且认为它是下列字段的组合(实际上做了一些修正,感兴趣可参考标准)
l(a) || Frame Control || Address 1 || Address 2 || Address 3 || Sequence Control || Address 4 || Qos Control
长度    2           2             6            6            6               2                6             2
其中最后两个字段 Address 4、Qos Control 为可选字段, 所以 AAD 长度范围是 24-32 字节
将 AAD 以 16 字节为单位分组,得到 B_1、B_2 两个附加认证分组,其中 B_2 可能要用 0 填充

在本例中,实际的 AAD 内容为(最后两个字段 Address 4、Qos Control 不存在,末尾填充 0)
00 16 08 41 00 0c 41 82 b2 55 00 0d 93 82 36 3a ff ff ff ff ff ff 00 00 00 00 00 00 00 00 00 00
l(a)  Fc    Address 1         Address 2         Address 3         此处开始用 0 填充

生成 AAD 分组文件
C:\>perl -e "binmode STDOUT; print pack('H*','00160841000c4182b255000d9382363affffffffffff00000000000000000000')" > aad.txt

Nonce 相对简单,其长度 = 15 - L = 15 - 2 = 13,格式为
Nonce = Priority Octet || A2                || PN
= 00             || 00 0d 93 82 36 3a || 00 00 00 00 00 01
= 00 00 0d 93 82 36 3a 00 00 00 00 00 01

B_0 = Flags || Nonce                                  || l(m)
= 59    || 00 00 0d 93 82 36 3a 00 00 00 00 00 01 || 01 50 -- 0x0150 = 336
= 59 00 00 0d 93 82 36 3a 00 00 00 00 00 01 01 50
生成 B_0 分组文件
C:\>perl -e "binmode STDOUT; print pack('H*','5900000d9382363a0000000000010150')" > b_0.txt

计算 A_i ...
A_0 = 01 00 00 0d 93 82 36 3a 00 00 00 00 00 01 00 00
A_1 = 01 00 00 0d 93 82 36 3a 00 00 00 00 00 01 00 01
生成 A_1 || A_2 || ... 分组文件
C:\>perl -e "binmode STDOUT; for(i=1;i<22;$i++){print pack('H28n','0100000d9382363a000000000001',$i)}" > a_i.txt
# 循环 21 次,因为 21 * 16 = 336,索引为何从 1 开始,参见上节

计算密钥流 E( K, A_1 || A_2 || ... )
C:\>openssl enc -aes-128-ecb -K 15798d511beae0028313c8ab32f12c7e -iv 0 -nopad -in a_i.txt > enc_key.txt

密文 与 加密密钥 异或,得到明文

C:\>perl xor_file.pl wpa-Induction-cipher.txt enc_key.txt > wpa-Induction-plain.txt

明文内容是什么呢?我们对下答案,看看 Wireshark 自身的 CCMP 解密结果,如下图
08233422-706caedf229f4c11bc525f490f45bfc1
原来是 DHCP Request 报文,这也说明 WiFi 安全属于二层安全范畴
仔细对比明文 wpa-Induction-plain.txt 的内容,完全符合

仅仅满足数据的机密性还是不够的,如何判断报文是否被篡改过?
CCMP 是通过附在密文后的 8 字节认证字段(红色显示部分)来解决的,下面按照 RFC 3610 中的规定进行验证

已知 A_0 = 01 00 00 0d 93 82 36 3a 00 00 00 00 00 01 00 00
生成 A_0 分组文件
C:\>perl -e "binmode STDOUT; print pack('H*','0100000d9382363a0000000000010000')" > a_0.txt

生成数据校验密钥 E(K, A_0)
C:\>openssl enc -aes-128-ecb -K 15798d511beae0028313c8ab32f12c7e -iv 0 -nopad -in a_0.txt > mac_key.txt
C:\>od -An -tx1 mac_key.txt
37 ef 73 a5 87 be 43 61 92 b3 6e ed c2 01 a3 49 -- 取前 8 字节 37 ef 73 a5 87 be 43 61

数据校验密钥 与 认证字段 异或,得到 X_n+1(即 CBC-MAC)的期望值
C:\>perl xor.pl 37ef73a587be4361 a3d717ff02830b58
结果为 XOR = 9438645A853D4839

而真实的 CBC-MAC 由密钥 K 对 -- B_0 || AAD分组 || WiFi明文分组(wpa-Induction-plain.txt) -- 进行 CBC 加密得到

合并 B_0 || AAD分组 || WiFi明文分组
C:\>perl -pe "BEGIN{binmode STDOUT;}" b_0.txt aad.txt wpa-Induction-plain.txt > b.txt

AES 加密计算 CBC-MAC

C:\>perl AES-CBC.pl encrypt 15798d511beae0028313c8ab32f12c7e 00000000000000000000000000000000 b.txt > cbc-mac.txt
文件 cbc-mac.txt 的最后一个分组为
94 38 64 5A 85 3D 48 39 5B 35 A9 89 E6 47 A3 3B
其前 8 字节 9438645A853D4839期望值 相同,校验成功,说明报文未被篡改

再看报文最后一个字段 FCS(蓝色显示部分),在 CCMP 中也是起校验作用
不过校验范围为除 FCS 外的整个报文,即:报文头 || 密文 || 认证字段,不像 WEP 报文中是针对明文校验
同样也可以利用 perl 自带的 crc32.bat 脚本进行验证

来源 http://www.cnblogs.com/efzju/p/3247155.html