個人檔案haoxg's blog部落格清單 工具 說明

haoxg's blog

Coding, Reading, Thinking, and Daydreaming...

hao xg

興趣
2009/3/5

解决 VMware 中 Ubuntu 8.04 的鼠标滚轮失效的问题

- haoxg -

编辑 /etc/X11/xorg.conf 文件:

Section "InputDevice"
    Identifier "Configured Mouse"
    Driver "vmmouse"
    Option "Protocol" "ImPS/2"
    Option "CorePointer"
    Option "Device" "/dev/input/mice"
    Option "ZAxisMapping" "4 5"
    Option "Emulate3Buttons" "yes"
EndSection

Ctrl+Alt+Backspace 重启X,即可。

(EOF)

Debian下若干问题的解决办法

- haoxg -

  • Debian 下解决 mysql 无法远程连接的问题

    Debian缺省禁止远程连接mysql。
  • 解决办法
    vi /etc/mysql/my.cnf
    注释掉 bind-address = 127.0.0.1

  • Debian 下解决 ssh, mysql 等连接很慢的问题

    解决办法
    vi /etc/nsswitch.conf
    将下面这行:
    hosts:          files mdns4_minimal [NOTFOUND=return] dns mdns4
    改为:
    hosts:          files dns
  • 重启机器,或重启服务:
    sudo /etc/init.d/mysql restart
    sudo /etc/init.d/ssh restart

  • Debian 下解决 apt-get update 出现 "There is no public key..." 错误的问题

    出错信息:
    There is no public key available for the following key IDs: 4D270D06F42584E6
    You may want to run apt-get update to correct these problems.
  • 解决办法
    apt-get install debian-keyring debian-archive-keyring
    apt-key update

(EOF)

2009/2/12

我的装机必备软件

- haoxg -

“用工具武装电脑”,其乐无穷。

  • Total Commander
    版本:7.04a
    毫无疑问,这个软件是贵宾。她是我最常用的,也是对我改变最大的。她对我的改变不仅是文件操作和管理的习惯 —— 她常常让我反思自己对软件开发的理解。是的,Total Commander 处处体现了“软件以用户为本”。正象xbeta兄所说的,使用Total Commander,能提升你的软件品味。Total Commander就是这样一个伟大的软件。

  • 极点五笔
    版本:6.5
    为了追求无重码的自由,也为了改变自己,从去年开始学习并使用五笔。从依赖了十多年的拼音切换到陌生的五笔,其中自有辛苦,但更多的是乐趣。近半年来,对五笔渐有依赖,对拼音反而陌生。这篇文章就是用五笔输入的。
    五笔输入法中,个人认为最好的是极点五笔,谢谢作者杜志民老师。

  • 世界之窗
    版本:2.4
    记得有人说过这样一句话:如果Maxthon是漫漫冬日的加厚羽绒服,那么TheWord便是春天里轻舞飞扬的休闲风衣。
    我自从遇见了这件休闲风衣,便连冬天也忘了羽绒服了。

  • EDiary
    版本:2.53
    感谢EDiary,给了我这么多惊喜和幸福。很惭愧,我给予EDiary的远不如EDiary回馈给我的多。因为有了EDiary,我的邮箱不再空虚;自从有了EDiary,现实再支离破碎,也有梦想在跟随。
    2009,EDiary十周年了。
    给EDiary做了很久的新衣服,就快要穿上了。

  • FlashPad
    版本:1.5
    自从有了这个小工具,我键盘上的“Alt”和“Z”恐怕会有明显磨损的趋势了。
    来了灵感怕忘了?Alt+Z吧。要临时记个电话号码?Alt+Z吧。会议记录?Alt+Z吧。……

  • Foxmail
    版本:6.5 beta3
    用了近十年的邮件客户端软件。虽然高版本并不尽如人意,但还是很有感情的。
    强烈建议打算升级的用户暂时放弃升级,继续使用6.0以下的版本。

  • EditPlus
    版本:3.10
    不要和我提UltraEdit。因为EditPlus已经完全满足了我的需要。

  • MiniHex
    版本:1.61
    十六进制工具有很多,但能自己定制和修改的十六进制工具并不多。想加个功能?打开Delphi,修改编译,完成。
    在我的电脑里,MiniHex已和Total Commander绑定到一起,一键打开,一键关闭,非常方便。

  • 迷你密码箱
    版本:1.6
    我认为,整天和电脑厮混的人,应懂得“生命在于整理”的道理。不仅文件需要整理,成堆的密码更需要整理。此外,密码绝不能以明文形式暴露在电脑里,所以,交给密码箱吧。

  • Araxis Merge
    版本:2001
    比较了N多文件比较工具,只有这个合我的意。注意,千万要用2001版本,不要升级。

  • CyberArticle
    版本: 5.2 专业版
    网上的一切都不可靠,包括你喜欢的网页,今天能打开,明天说不定就没了。所以,随手保存有价值的网页,便成了我的上网习惯。

(EOF)

2008/12/11

安妮宝贝博客上的问答(转载)


安妮

第一次给你写信,不知会不会被淹没。写下这一句的时候,又不知下一句该说什么。人是孤独的存在,生活是一部静默的断代史,无论多少言语,多少书本,却也写不出那些最简单的问题。譬如,人生的意义是什么,譬如,死亡的那一端是什么,譬如,爱在何种意义上成为可能。

我走过墨脱,安宁的旅行,不似莲花。多年过去,依旧朋友很多,依旧是与人争论时的雄辩滔滔,一人独处时的沉默坚定。可是,也依旧无法摆脱建造许久的心境崩塌于一个夜晚,一个眼神,一段文字。

哲学,宗教,数学和物理,这一切在本质上最为接近我们的灵魂,却也是它们让一个人远离了简单的生活。即使我们此刻再平静,也无法否定心中的累累伤痕。佛家说,烦恼即菩提,是否这一切本就是一个人为的悖论。

于茫茫人海中寻找灵魂唯一之伴侣,是否会是一场虚幻,你可以告诉我吗?



为何要在茫茫人海寻找灵魂唯一之伴侣,自己是唯一伴侣,他人不过是路边风景,就如你坐在火车上,看得到风景在出现,消失,又出现,一直此起彼伏,那是因为你在前进。你只能带着自己去旅行。对他人,可以善待,珍重,但无需寄以厚望。没有人可以解决我们的内心。

哲学,宗教,数学和物理……诸如此类,一切方式,我认为并非让人远离简单的生活,而是为了让我们的生活更简单,因为它们的系统在建立中有强大的超脱感。理性思考分析和辨证,让我们的心灵在劳作中单纯。烦恼即菩提,只是说明,黑暗与光明,是与非,此与彼,罪与荣耀……都是彼此依存相衬的关系,密不可分,而不是泾渭分明。物质世界的种种元素环环相扣,精神的层面组合也不会是单一。

多少言语,多少书本,不是为了解答众多答案,它们没有这种力量。是那些在寻找解答的人,在寻找中得到了力量。认真走路的时候,会忘记真实的目标在哪里,持续而明确的发力本身,就带来抵达。如果你有过长途跋涉,会对这种感觉记忆深刻。

看似建造已必的心境崩塌于一个夜晚,一个眼神,一段文字,又有什么不好呢,说明那颗心依旧十分柔软。这和坚定无冲突。良好的心境,是一片大海,要承容下微澜或巨浪,而非停滞静止。心可以无限扩大,敏感善良却难得。

仅是一些个人性的观点,供参考。


2008/11/25

Linux下链接静态库的顺序问题


在链接静态库时,如果多个静态库之间存在依赖关系,则有依赖关系的静态库之间存在链接顺序问题。这在使用静态库时需要注意,否则会报符号找不到的链接错误。

例如:
lib2.a 依赖于 lib1.a,而最终可执行文件 test 依赖于 lib2.a,则链接选项应为:
-llib2.a -llib1.a,而不能反过来,否则会报 lib1.a 中的某些符号未定义。

(EOF)

2008/10/17

感谢生活


生活很苦,可生活也很甜,不是么:

  • 工作步入正轨,一步一步,认真对待。扑朔迷离的一切,渐渐变得心中有数。
  • 玲玲没有被抽中论文盲审。电话那头的快乐,让这个周五充满轻松。
  • 姐姐和姐夫的小风波终于过去,姐夫发来短信:我们很好,放心!

快乐其实随处可见:

  • 终于用五笔替代了拼音。意义不在于五笔更快,而在于我终于作出了改变。
  • 每次打开我的T40,心里便掠过那一丝永不褪色的温暖,5年过去了。我是如此喜爱它。
  • Total Commander,让我彻底改变了文件管理的方式。只能说相见恨晚。
  • Delphi2009。Delphi每次推出新版本,无论它好不好,我心里都和过年一样。
  • 用砒霜杀死了一颗大牙的神经,现在终于不疼了。

感谢生活!

(EOF)

2008/9/26

三言两语


翻看曾收集的电子书,发现了一些会让我反省的句子,摘录于此:



  • 你要努力成为一个值得被爱的人;而非着迷于成为一个被爱的人。
  • 你如果付出爱,它自然会回来。
  • 努力工作只是成功的前提,聪明地工作才是成功的关键。
  • 这个世界唯一不变的就是“变”。
  • 你今天的一切是由你的昨天决定的,你的明天将取决于你今天的选择。
  • 没有目标的人是在为有目标的人完成目标。
  • 不经过你的同意,任何人无权让你觉得差人一筹。
  • 希望别人怎样对你,就应怎样对待别人!
  • 为什么做比怎么做更重要!
  • 如果你不在乎荣誉归谁,你将大有作为。
  • 最不像你的人、你最不喜欢的人、或许正是你的团队最需要的人。

(EOF)

2008/8/26

Delphi 2009、C++Builder 2009 正式发布


CodeGear已经在官网发布Delphi2009的消息。Delphi2009、C++Builder2009正式发布,并且开始接受预订。

以下是Delphi2009的价格:
Delphi2009 Professional editions(专业版): $399
Delphi2009 Enterprise editions(企业版): $1,299
Delphi2009 Architect editions(架构师版): $2,299

详情请见:
http://www.codegear.com/products/delphi/win32
http://www.codegear.com/article/38512

(EOF)

2008/8/22

Delphi 2009 将于2008-08-25 发布!


传言 Delphi2009 将于2008年08月25日发布。希望这是一个真实的消息。

Delphi2009是自Delphi7以来功能提升最显著的一个版本,同时也是最值得期待的一个版本,这不仅仅因为产品质量的改进,还因为这是多灾多难的Delphi在完全脱离Borland之后首次以 Embarcadero 的身份向世人展示!

永远支持、永远追随、永远关注 Delphi!

领域

新功能

Delphi编译器 Delphi Win32 和 Delphi .NET 同时支持泛型编程。同时增加匿名方法的支持。
Delphi核心 从语言核心级别提供对Unicode的完整的支持。泛型容器的支持。
VCL 支持Ribbon风格。更多的VCL组件。更多的功能增强。
数据库 Unicode驱动程序。新的DataSnap技术,支持JSON的分布式架构。
COM/COM+ RIDL。支持最新的COM+标准。

 (EOF)

2008/7/17

解决Windows下修改环境变量后需重启才能生效的问题

- haoxg -

在“我的电脑”->“属性”->“高级”->“环境变量”中增加或修改环境变量后,需重启系统才能使之生效。有没有什么方法可让它即时生效呢?下面介绍一种方法:

以修改环境变量“PATH”为例,修改完成后,进入DOS命令提示符,输入:set PATH=C: ,关闭DOS窗口。再次打开DOS窗口,输入:echo %PATH% ,可以发现“我的电脑”->“属性”->“高级”->“环境变量”中设置的 PATH 值已经生效。

不用担心DOS窗口中的修改会影响环境变量的值,DOS窗口中的环境变量只是Windows环境变量的一个副本而已。但是对副本的修改却会引发Windows环境变量的刷新,这正是我们想要的!

(EOF)

2008/7/8

网络防火墙开发二三事

- haoxg -

花了近一个月的时间研究 Windows 平台下的网络防火墙相关技术,并实现了一个简单的防火墙。
在独自摸索的过程中,由于以往的开发经历从未涉及此领域,所以碰到了不少困难,也走了些弯路。
现此项目暂告一段,遂将相关心得整理成文。文章以归纳总结为主,没有创新性技术,高手免看。

防火墙的数据包拦截方式小结

网络防火墙都是基于数据包的拦截技术之上的。在 Windows 下,数据包的拦截方式有很多种,
其原理和实现方式也千差万别。总的来说,可分为“用户级”和“内核级”数据包拦截两大类。

用户级下的数据包拦截方式有:

* Winsock Layered Service Provider (LSP)
*
Win2K 包过滤接口 (Win2K Packet Filtering Interface)
* 替换 Winsock 动态链接库 (Winsock Replacement DLL)。

内核级下的数据包拦截方式有:

* TDI过滤驱动程序 (TDI-Filter Driver)。
* NDIS中间层驱动程序 (NDIS Intermediate Driver)。
* Win2K Filter-Hook Driver
* Win2K Firewall-Hook Driver
* NDIS-Hook Driver

在这么多种方式面前,我们该如何决定采用哪一种作为自己项目的实现技术?这需要对每一种
方式都有一个大致的了解,并清楚它们各自的优缺点。技术方案的盲目选用往往会带来一些技术
风险。以自己为例,我需要在截包的同时得到当前进程文件名,也就是说,需向用户报告当前是
哪个应用程序要访问网络。在选用 Win2K Filter-Hook Driver 这一方案之后(很多小型开源项
目都采用这一方案),便开始编码。但之后发现 Win2K Filter-Hook Driver 的截包上下文处于内
核进程中,即 IRQL >= DISPATCH_LEVEL,根本无法知道当前应用程序的名字。相比之下,
TDI-Filter DriverNDIS-Hook Driver 则可以得知这些信息。其中 TDI-Filter Driver
NDIS-Hook Driver 更能准确地获知当前应用程序文件名,后者的接收数据包和少数发送数据
包的场景仍然处于内核进程中。

下面列出了各种截包方式的特点:

* Winsock Layered Service Provider (LSP)
  该方式也称为 SPI (Service Provider Interface) 截包技术。SPI是由 Winsock2 提供的一个
  接口,它需要用户机上安装有 Winsock 2.0Winsock2 SPI 工作在 API 之下的 Driver 之上,
  可以截获所有基于 Socket 的网络数据包。
  优点:
  * 以DLL形式存在,编程方便,调试简单。
  * 数据封包比较完整,未做切片,便于做内容过滤。
  缺点:
  * 拦截不够严密,对于不用 Socket 的网络通讯则无法拦截 (如 ICMP),木马病毒很容易绕过。

* Win2K Packet Filtering Interface
  这是 Win2K 中一组 API 提供的功能 (PfCreateInterface, PfAddFiltersToInterface, ...)。
  优点:
  * 接口简单,实现起来没什么难度。
  缺点:
  * 功能过于简单,只能提供IP和端口的过滤,可能无法满足防火墙的复杂需求。
  * 处于 API 层,木马病毒容易绕过。
  * 只能在 Win2K 以上(含)系统中使用。

* Winsock Replacement DLL
  这种方法通过替换系统 Winsock 库的部分导出函数,实现数据报的监听和拦截。
  缺点:
  * 由于工作在 Winsock 层,所以木马病毒容易绕过。

* TDI-Filter Driver
  TDI 的全称是 Transport Driver Interface。传输层过滤驱动程序通过创建一个或多个设备对象
  直接挂接到一个现有的驱动程序之上。当有应用程序或其它驱动程序调用这个设备对象时,会首
  先映射到过滤驱动程序上,然后由过滤驱动程序再传递给原来的设备对象。
  优点:
  * 能获取到当前进程的详细信息,这对开发防火墙尤其有用。
  缺点:
  * 该驱动位于 tcpip.sys 之上,所以没有机会得到那些由 tcpip.sys 直接处理的包,比如ICMP
  * TDI驱动需要重启系统方能生效。

* NDIS Intermediate Driver
  也称之为 IM Driver。它位于协议层驱动和小端口驱动之间,它主要是在网络层和链路层之间对
  所有的数据包进行检查,因而具有强大的过滤功能。它能截获所有的数据包。
  可参考DDK中附带的例子 Passthru
  优点:
  * 功能非常强大,应用面广泛,不仅仅是防火墙,还可以用来实现VPNNATVLan 等。
  缺点:
  * 编程复杂,难度较大。
  * 中间层驱动的概念是在 WinNT SP4 之后才有的,因此 Win9X 无法使用。
  * 不容易安装,自动化安装太困难。

* Win2K Filter-Hook Driver
  这是从 Win2K 开始提供的一种机制,该机制主要利用 ipfiltdrv.sys 所提供的功能来拦截网络
  数据包。Filter-Hook Driver 的结构非常简单,易于实现。但是正因为其结构过于简单,并且
  依赖于 ipfiltdrv.sys,微软并不推荐使用。
  可参考 CodeProject 上的例子:
http://www.codeproject.com/KB/IP/drvfltip.aspx
  优点:
  * 结构简单,易于实现。
  * 能截获所有的IP包(包括ICMP包)。
  缺点:
  * 工作于内核进程中,无法取得当前应用程序进程的信息。
  * 虽能截获所有IP包,但无法取得数据包的以太帧(Ethernet Frame)。
  * 只能在 Win2K 以上(含)系统中使用。

* Win2K Firewall-Hook Driver
  这是一种和 Win2k Filter-Hook Driver 差不多的机制,所不同的是,
Firewall-Hook Driver
  能在 IP Driver 上挂接多个回调函数,所以和前者相比,它引起冲突的可能性更小一些。
  可参考 CodeProject 上的例子:
http://www.codeproject.com/KB/IP/FwHookDrv.aspx
  这种方式的优缺点和 Win2K Filter-Hook Driver 基本相同。

* NDIS-Hook Driver
  这是一种要重点讲述的截包方式。它是目前大多数网络防火墙所使用的方法。这种方式的做法
  是安装钩子到 ndis.sys 中,替换其中的某些关键函数,从而达到截包的目的。在下一节中我
  们将详细地介绍它的实现方法。
  优点:
  * 安装简单,可即时安装和卸载驱动,无需重启系统。
  * 能截获所有的IP包,同时能取得数据包的以太帧(Ethernet Frame)。
  * 安全性高,木马病毒不容易穿透。
  * 在大多数情况下,能获取到当前应用程序的进程信息。
  * 能在 Win98 以上(含)系统中使用。
  缺点:
  * 接收数据包、或偶尔发送数据包时,驱动工作在内核进程中,无法获得应用程序进程信息。

NDIS-Hook 技术

微软和 3COM 公司在1989年制定了一套开发 Windows 下网络驱动程序的标准,称为 NDIS
NDIS 的全称是 Network Driver Interface SpecificationNDIS为网络驱动的开发提供了一套
标准的接口,使得网络驱动程序的跨平台性更好。

NDIS提供以下几个层次的接口:
1. NDIS 小端口驱动 (NDIS Miniport Driver)。
   这也就是我们常说的网卡驱动。
2. NDIS 协议驱动 (NDIS Protocol Driver)。
   用来实现某个具体的协议栈,如 TCP/IP 协议栈,并向上层导出 TDI 接口。
3. NDIS 中间层驱动 (NDIS Intermediate Driver)。
   这是位于小端口驱动和协议驱动之间的驱动。

NDIS为了给出上述三种接口,提供了一个系统的、完整的 Wrapper。这个 Wrapperndis.sys
上面提到的 Miniport DriverProtocol DriverIntermediate Driver 均属于插入到这个
Wrapper 中的“模块”,它们调用 Wrapper 提供的函数,同时也向 Wrapper 注册回调函数。

在简单了解了NDIS的机制之后,不难得知,网络防火墙只需要将自己的函数挂钩(Hook)到 ndis.sys
中即可截获网络数据包。NDIS-Hook 技术有两种实现方案:

1. 修改 ndis.sysExport Table

   在 Win32 下,可执行文件(EXE/DLL/SYS)都遵从PE格式。所有提供接口的驱动都有 Export Table
   因此只要修改 ndis.sysExport Table,就可实现对关键函数的挂接。在实现步骤中,首先
   需要得到 ndis.sys 的内存基址,再根据PE格式得到DOS头部结构(IMAGE_DOS_HEADER),进一步得
   到NT头部结构(IMAGE_NT_HEADER),最后从头部结构中查得 Export Table 的地址。

   由于协议驱动程序(NDIS Protocol Driver)在系统启动时会调用 NdisRegisterProtocol() 来向
   系统注册协议,因此这种方法关键在于修改 ndis.sys 所提供的 NdisRegisterProtocol
   NdisDeRegisterProtocolNdisOpenAdapterNdisCloseAdapterNdisSend 这几个函数的地址。
   对于处于系统核心的 ndis.sys 而言,要修改它的内存区域,只有驱动程序才能做到,所以我们
   必须编写驱动程序来达到这个目的。

   该方案的缺点是加载或卸载驱动后无法立即生效,必须重启系统。且挂钩方法较为复杂。早期凡
   使用 NDIS-Hook 的防火墙都采用这一方法,包括著名的费尔防火墙的早期版本(v2.1)。
   直到 2004 年,www.rootkit.com 上一名黑客公布了一种全新的 NDIS-Hook 技术(即下文即将提
   到的第2种方法),诸多防火墙产品才都悄悄对自己的核心技术进行了升级。由于新的挂钩技术更
   好,故本文不打算详述修改 Export Table 这一方法的具体细节。

2. 向系统注册假协议(Bogus Protocol)。

   NDIS提供了一个API: NdisRegisterProtocol(),这个API的职责是向系统注册一个协议(如TCPIP),
   并将该协议作为一个链表节点插入到“协议链表”的头部,最后返回该链表头节点(即新节点)的
   地址。正常情况下,只有NDIS协议驱动程序(NDIS Protocol Driver)才会调用这个API

   既然如此,如果我们也调用 NdisRegisterProtocol() 向系统注册一个新的协议,我们也就能轻
   易地得到“协议链表”的首地址,通过走访这个链表,就能修改其中的某些关键信息,比如关键
   函数的地址。修改完毕后,再调用 NdisDeRegisterProtocol() 注销掉新协议。这看似一切都没
   发生,但事实上目的已经达到了。这个新协议我们称之为假协议(Bogus Protocol)。

   通过这种方法,我们可以不用重启系统就能轻松挂接截包函数。当今大多数网络防火墙都采用了
   这一方法。近来网上又有人提出了获取协议链表首地址的新的怪异途径,比如获取 tcpip.sys
   中全局变量 _ARPHandle 值的方法。不管怎样,相比之下,注册假协议仍不失为一种经典且简单
   的方法。

本文将详细叙述第2种方案的内部原理和实现细节,即通过注册假协议获取协议链表首地址,遍历
链表并修改其中的函数地址,挂钩自己的函数,从而实现网络截包。在这么做之前,需要先对NDIS
内部维护的几个结构有清楚的认识。另外,由于历史原因,NDIS存在诸多并不完全向下兼容的版本,
不同的版本中关键数据域的偏移地址也不尽相同。微软并没有以文档形式提供这些变化的列表。本
文稍后给出这些变化。

* NDIS_PROTOCOL_BLOCKNDIS_OPEN_BLOCK

NDIS中,所有已注册的协议是通过一个单向的协议链表来维护的。这个单向链表保存了所有已注册
的协议,每个协议对应一个节点。链表节点由 NDIS_PROTOCOL_BLOCK 结构来描述,在这个结构中保存
了注册协议驱动时所指定的各种信息,如支持协议即插即用的回调函数地址等。同时,每个协议驱动
还对应一个 NDIS_OPEN_BLOCK 节点结构的单向链表来维护其所绑定的网卡信息,协议驱动发送和接收
数据包的回调函数地址就保存在这个结构中,是我们要重点修改的对象。

协议与网卡绑定的示意图如下:

              ┌───┐
              │ Head │
              └─┬─┘
                  ↓
    ┌──────────────┐      ┌───────────┐
    │ TCPIP Protocol Block       ├──→│ RTL8168 Open Block   │
    └──────┬───────┘      └─────┬─────┘
                  ↓                                  ↓
    ┌──────────────┐      ┌───────────┐
    │ TCPIP_WANARP Protocol Block│      │ Wireless Open Block  │
    └──────┬───────┘      └─────┬─────┘
                  ↓                                  ↓         
              ┌───┐                          ┌───┐
              │ NULL │                          │ NULL │
              └───┘                          └───┘

* 得到 NDIS_PROTOCOL_BLOCK 链表的首地址

上文已提到,通过向系统注册假协议,我们即可得到协议链表的首地址。
DDK中可查到 NdisRegisterProtocol() 的原型:

    EXPORT
    VOID
    NdisRegisterProtocol(
        OUT PNDIS_STATUS   Status,
        OUT PNDIS_HANDLE   NdisProtocolHandle,
        IN  PNDIS_PROTOCOL_CHARACTERISTICS ProtocolCharacteristics,
        IN  UINT           CharacteristicsLength
        );

可以看出,我们在调用它时需要传入一个结构 NDIS_PROTOCOL_CHARACTERISTICS,这个结构是我们
在注册协议时必须填写的一张表格,这个表格描述了协议的相关信息。不过既然我们注册的是一个
假协议,所以可以尽量简单地填写它。

    NDIS_STATUS
    DummyNdisProtocolReceive(
        IN NDIS_HANDLE ProtocolBindingContext,
        IN NDIS_HANDLE MacReceiveContext,
        IN PVOID HeaderBuffer,
        IN UINT HeaderBufferSize,
        IN PVOID LookAheadBuffer,
        IN UINT LookAheadBufferSize,
        IN UINT PacketSize
        )
    {
        return NDIS_STATUS_NOT_ACCEPTED;
    }

    NDIS_HANDLE
    RegisterBogusNdisProtocol(void)
    {
        NTSTATUS Status = STATUS_SUCCESS;
        NDIS_HANDLE hBogusProtocol = NULL;
        NDIS_PROTOCOL_CHARACTERISTICS BogusProtocol;
        NDIS_STRING ProtocolName;

        NdisZeroMemory(&BogusProtocol, sizeof(NDIS_PROTOCOL_CHARACTERISTICS));
        BogusProtocol.MajorNdisVersion = 0x04;
        BogusProtocol.MinorNdisVersion = 0x0;

        NdisInitUnicodeString(&ProtocolName, L"BogusProtocol");
        BogusProtocol.Name = ProtocolName;
        BogusProtocol.ReceiveHandler = DummyNdisProtocolReceive;

        NdisRegisterProtocol(&Status, &hBogusProtocol, &BogusProtocol,
            sizeof(NDIS_PROTOCOL_CHARACTERISTICS));

        if (Status == STATUS_SUCCESS) return hBogusProtocol;
        else return NULL;
    }

函数 RegisterBogusNDISProtocol() 的返回值即是我们想要的协议链表首地址。不过须注意的是,
在函数挂钩完成后,应调用 NdisDeregisterProtocol() 将假协议注销。另外,在遍历协议链表
进行函数挂钩时,应从首节点的下一个节点开始,因为首节点是我们的假协议节点。

* 修改原有函数地址值实现函数挂钩

上文已提到了和NDIS相关的三个结构:
NDIS_PROTOCOL_BLOCK,
NDIS_OPEN_BLOCK,
NDIS_PROTOCOL_CHARACTERISTICS

那么我们要替换的函数在哪儿呢?答案是在 NDIS_OPEN_BLOCKNDIS_PROTOCOL_CHARACTERISTICS
这两个结构中,而且重点是前者,因为前者是协议驱动和网卡绑定的纽带。现在的主流网卡都只
调用 NDIS_OPEN_BLOCK 中的收发函数进行发送和接收数据包。但据试验,虚拟机 VMware 有时会
调用 NDIS_PROTOCOL_CHARACTERISTICS 中的函数进行数据包收发。所以为了严谨,我们应该对两
个结构中的函数进行替换。关于这两个结构的定义,读者可以自行查阅DDK文档和头文件。

下面给出示意性代码。简单起见,下列代码均假设当前NDIS的版本为5.0

    BOOLEAN
    InstallHook(void)
    {
        NDIS_STATUS nStatus;
        NDIS_HANDLE hBogusProtocol = NULL;
        BYTE *pProtocolChain;

        // Get the address of the first NDIS_PROTOCOL_BLOCK node.
        hBogusProtocol = RegisterBogusNDISProtocol();
        if (hBogusProtocol == NULL) return FALSE;

        pProtocolChain = (BYTE*)hBogusProtocol;
        while (TRUE)
        {
            // Get the address of the next node.
            DWORD dwOffset = 0x10;  // for NDIS 5.0
            pProtocolChain = ((BYTE **)(pProtocolChain + dwOffset))[0];
            if (!pProtocolChain) break;

            HookNdisProtocolBlock(pProtocolChain);
        }

        NdisDeregisterProtocol(&nStatus, hBogusProtocol);
        return TRUE;
    }

    void
    HookNdisProtocolBlock(
        IN  BYTE *pProtocolBlock
        )
    {
        PNDIS_PROTOCOL_CHARACTERISTICS pProtoChar;
        PNDIS_OPEN_BLOCK pOpenBlock;

        pProtoChar = (PNDIS_PROTOCOL_CHARACTERISTICS)(pProtocolBlock + 0x14);

        HookNdisProc(MyReceive, (PVOID *)&pProtoChar->ReceiveHandler);
        HookNdisProc(MyReceivePacket, (PVOID *)&pProtoChar->ReceivePacketHandler);
        HookNdisProc(MyBindAdapter, (PVOID *)&pProtoChar->BindAdapterHandler);

        pOpenBlock = ((PNDIS_OPEN_BLOCK *)pProtocolBlock)[0];
        while (pOpenBlock)
        {
            HookNdisProc(MySend, (PVOID *)&pOpenBlock->SendHandler);
            HookNdisProc(MyReceive, (PVOID *)&pOpenBlock->ReceiveHandler);
            HookNdisProc(MyReceivePacket, (PVOID *)&pOpenBlock->ReceivePacketHandler);
            HookNdisProc(MySendPackets, (PVOID *)&pOpenBlock->SendPacketsHandler);

            pOpenBlock = pOpenBlock->ProtocolNextOpen;
        }
    }

    void
    HookNdisProc(
        IN  PVOID pMyProc,
        IN  PVOID *ppOrgProc
        )
    {
        // TODO: Save the address of the original proc.

        *ppOrgProc = pMyProc;
    }

InstallHook() 首先得到协议链表的首地址,接着遍历链表,针对系统中的每个(第一个除外)
NDIS_PROTOCOL_BLOCK 调用 HookNdisProtocolBlock() 函数。
HookNdisProtocolBlock() 对 NDIS_PROTOCOL_BLOCKNDIS_PROTOCOL_CHARACTERISTICS
NDIS_OPEN_BLOCK 链表的每个节点进行函数挂接。
HookNdisProc() 用于替换函数地址。给出的代码中它只是简单地替换函数地址,在实际应用中,
它还应当保存原始函数的地址值,以供新的函数调用。

* 关键数据域在不同NDIS版本中的差异

由于 NDIS-Hook 并非受微软官方支持的技术,所以相关文档非常缺乏。不仅如此,操作系统的
每次升级,都会同时升级NDIS,而NDIS中的某些数据结构并没有保持向下兼容。最需要注意的
NDIS_PROTOCOL_BLOCK

Win9x/Me/NT DDK中,NDIS_PROTOCOL_BLOCK 都有明确的定义,但在 Win2K/XPDDK中,
并没有该结构的详细定义,也就是说该结构在 Win2K 以后(含)的系统中是非公开的。因此开发
人员只能利用各种调试工具来发掘该结构的详细定义。也正是因为如此,NDIS-Hook 方法对平台
的依赖性比较大,需要在程序中判断不同的操作系统版本而使用不同的结构定义。

NDIS_PROTOCOL_BLOCK 的定义可大致认为是这个样子:

    typedef struct _NDIS_PROTOCOL_BLOCK
    {
        PNDIS_OPEN_BLOCK              OpenQueue;
        REFERENCE                     Ref;
        UINT                          Length;
        NDIS_PROTOCOL_CHARACTERISTICS ProtocolChars;

        struct _NDIS_PROTOCOL_BLOCK*  NextProtocol;
        ULONG                         MaxPatternSize;

        // ...
    } NDIS_PROTOCOL_BLOCK, *PNDIS_PROTOCOL_BLOCK;

其中 OpenQueuePNDIS_OPEN_BLOCK 链表的首节点地址,NextProtocol 指向下一个
NDIS_PROTOCOL_BLOCK 节点。

在不同的NDIS版本中,该结构中的某些域的偏移地址是不同的,现列于下:

  ┌───────┬───────────┬───────────┐
  │ NDIS Version │ ProtocolChars offset │ NextProtocol offset  │
  ├───────┼───────────┼───────────┤
  │   3.XX       │        0x14          │        0x04          │
  │   4.XX       │        0x14          │        0x60          │
  │   4.01       │        0x14          │        0x8C          │
  │   5.XX       │        0x14          │        0x10          │
  └───────┴───────────┴───────────┘

* 如何在驱动中得到当前NDIS版本?

有两种方法可得到当前NDIS版本。一种是先取得当前操作系统的版本信息,在根据操作系统
的版本得到NDIS的版本。操作系统版本和NDIS版本有一个映射关系,读者可在DDK帮助中查到。

  ┌───────┬───────┐
  │ OS Version   │ NDIS Version │
  ├───────┼───────┤
  │ Win95        │     3.1      │
  │ Win95 OSR2   │     4.0      │
  │ Win98        │     4.1      │
  │ Win98 SE     │     5.0      │
  │ WinMe        │     5.0      │
  │ WinNT 3.5    │     3.0      │
  │ WinNT 4.0    │     4.0      │
  │ WinNT 4.0 SP3│     4.1      │
  │ Win2K        │     5.0      │
  │ WinXP        │     5.1      │
  │ WinVista     │     6.0      │
  └───────┴───────┘

还有一种方法,通过调用 NdisReadConfiguration() 直接获取NDIS版本。代码如下:

    BOOLEAN
    GetNdisVersion(
        OUT DWORD *pMajorVersion,
        OUT DWORD *pMinorVersion
        )
    {
        NDIS_STATUS nStatus;
        NDIS_STRING VersionStr = NDIS_STRING_CONST("NdisVersion");
        PNDIS_CONFIGURATION_PARAMETER ReturnedValue;
        BOOLEAN bResult;

        NdisReadConfiguration(
            &nStatus,
            &ReturnedValue,
            NULL,
            &VersionStr,
            NdisParameterInteger);

        bResult = ((nStatus == NDIS_STATUS_SUCCESS)? TRUE : FALSE);
        if (bResult)
        {
            //
            // The returned value has the NDIS version of the form
            // 0xMMMMmmmm, where MMMM is major version and mmmm is minor
            // version so 0x00050000 is 5.0
            //
            DWORD dwVersion = ReturnedValue->ParameterData.IntegerData;
            if (pMajorVersion)
                *pMajorVersion = dwVersion >> 16;
            if (pMinorVersion)
                *pMinorVersion = dwVersion & 0xFFFF;
        }

        return bResult;
    }

须注意的是,GetNdisVersion() 必须在 PASSIVE_LEVEL 下运行。所以此函数适合于在
驱动的 DriverEntry() 中调用,因为 DriverEntry() 一定是处于 PASSIVE_LEVEL 的。

 

(未完待续)

2008/7/7

SourceForge 被封

- haoxg -

记得上个月还能打开 SourceForge,现在居然无法打开了。还以为是公司网络的问题,结果上 google 一查,原来是被封了。
SourceForge 被封,已经不是头回了。

(我的博客被封,倒是头回。为了博客能顺利打开,此处已删去若干敏感字句。)

(EOF)

2008/6/30

世界卫生组织公布的“全球十大垃圾食品”!(转载)


转贴于此,提醒自己注意。

一、油炸类食品
  1、导致心血管疾病元凶(油炸淀粉)
  2、含致癌物质
  3、破坏维生素,使蛋白质变性

二、腌制类食品
  1、导致高血压,肾负担过重,导致鼻咽癌
  2、影响粘膜系统(对肠胃有害)
  3、易得溃疡和发炎
  4、导致高血压,肾负担过重,导致鼻咽癌
  5、影响粘膜系统(对肠胃有害)
  6、易得溃疡和发炎

三、加工类肉食品(肉干、肉松、香肠等)
  1、含三大致癌物质之一:亚硝酸盐(防腐和显色作用)
  2、含大量防腐剂(加重肝脏负担)

四、饼干类食品(不含低温烘烤和全麦饼干)
  1、食用香精和色素过多(对肝脏功能造成负担)
  2、严重破坏维生素
  3、热量过多、营养成分低

五、汽水可乐类食品
  1,含磷酸、碳酸,会带走体内大量的钙
  2、含糖量过高,喝后有饱胀感,影响正餐

六、方便类食品(主要指方便面和膨化食品)
  1、盐分过高,含防腐剂、香精(损肝)
  2、只有热量,没有营养

七、罐头类食品(包括鱼肉类和水果类)
  1、破坏维生素,使蛋白质变性
  2、热量过多,营养成分低

八、话梅蜜饯类食品(果脯)
  1、含三大致癌物质之一:亚硝酸盐(防腐和显色作用)
  2、盐分过高,含防腐剂、香精(损肝)

九、冷冻甜品类食品(冰淇淋、冰棒和各种雪糕)
  1、含奶油极易引起肥胖
  2、含糖量过高影响正餐

十、烧烤类食品
  1、含大量“三苯四丙吡”(三大致癌物质之首)
  2、1只烤鸡腿=60支烟毒性
  3、导致蛋白质炭化变性(加重肾脏、肝脏负担)

(EOF)

2008/6/5

如何编译 OCCI (Oracle C++ Call Interface) 程序

- haoxg -

有朋友问到如何在C++中调用Oracle,针对此问题我在这篇文章中简要介绍相关的环境配置和编译方法。由于配置和编译OCCI程序相对比较复杂,本人也曾摸索了好一阵。希望这些总结能给OCCI开发人员提供一些简单的帮助。

开发环境:
  Red Hat Enterprise Linux AS4U2
  gcc 3.4.3
  Oracle 10.2.0 Express Edition

在以上环境下编译 occi 程序,可按如下步骤进行:

1. 若 RHEL4 中自带的 gcc 不是 3.4.3 版本,则需安装 gcc 3.4.3。
2. 安装 Oracle,设置好环境变量。
3. 更新 libocci.so.10.1 和 libocci10.a 两个文件。
4. 利用 Oracle 自带的 occi 例子,测试是否能成功编译。

安装 gcc 3.4.3

安装 gcc 3.4.3 的原因是为了兼容 Oracle 提供的 occi 库。Oracle 安装包中的 occi 库只支持 gcc 2.96,官方网站提供的 occi 更新库支持 gcc 3.4.3。但经测试,gcc 3.4.4 也是能兼容的。

gcc 3.4.3 可从下面的地址下载得到:
ftp://linuxforum.net/ftp.gcc.gnu.org/releases/gcc-3.4.3/gcc-3.4.3.tar.gz

由于是编译安装,所以在安装之前,必须保证原系统中已存在某个版本的gcc。
输入如下命令:

# cp gcc-3.4.3.tar.gz /usr/src
# cd /usr/src
# tar zxvf gcc-3.4.3.tar.gz
# cd gcc-3.4.3
# ./configure --prefix=/usr/local/src/gcc-3.4.3 --enable-threads=posix --disable-checking --enable--long-long --host=i386-redhat-linux --with-system-zlib --enable-languages=c,c++
# make
# make install

这样就将 gcc 3.4.3 安装到 /usr/local/src/gcc-3.4.3 目录下了。
为了和原系统中的gcc共存,可作如下符号链接:

# cd /usr/bin
# ln -s /usr/local/src/gcc-3.4.3/bin/gcc gcc343
# ln -s /usr/local/src/gcc-3.4.3/bin/g++ g++343

最后需设置库的路径到环境变量中。修改 /etc/profile 文件,在尾部增加:

export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/src/gcc-3.4.3/lib

安装 Oracle

安装过程很简单:
# rpm -ivh oracle-xe-univ-10.2.0.1-1.0.i386.rpm
成功安装之后,可在 /usr/lib/oracle 下找到 Oracle 的全部文件。

重要的是必须手工设置环境变量。修改 /etc/profile 文件,在尾部增加:

export ORACLE_BASE=/usr/lib/oracle/xe/app/oracle
export ORACLE_HOME=$ORACLE_BASE/product/10.2.0/server
export ORACLE_SID=XE
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$ORACLE_HOME/lib
export PATH=$PATH:$ORACLE_HOME/bin
export NLS_LANG="SIMPLIFIED CHINESE_CHINA.ZHS16GBK"
export NLS_DATE_FORMAT="yyyy-mm-dd hh24:mi:ss"

注意其中的 ORACLE_SID,其值为 Oracle 的实例名。实例名可通过如下方法查到:
select * from v$instance
字段 INSTANCE_NAME 即为 Oracle 实例名。

更新库文件

由于 Oracle 自带的库文件不支持 gcc 3.4.3,所以需要更新某些库文件。
Oracle 官方网站上提供更新文件的下载:occi_gcc343.tar。
(注:如果不想到官方网站去找,也可以点此下载,但此下载链接可能会失效。)

将包中的两个库文件(libocci.so.10.1, libocci10.a)复制到下面的目录下:
/usr/lib/oracle/xe/app/oracle/product/10.2.0/server/lib

测试编译 occi

Oracle 自带了 occi 的例子,在如下路径下:
/usr/lib/oracle/xe/app/oracle/product/10.2.0/server/rdbms/demo

demo_xe.mk 的头部注释很清楚地说明了如何编译例子程序。
如果要编译 occidml.cpp,可输入命令行:
make -f demo_xe.mk buildocci EXE=occidml OBJS=occidml.o


(EOF)

2008/5/31

解决 Photoshop CS3 和 WinXP SP3 的兼容性问题

- haoxg -

突然发现一直工作正常的 Photoshop CS3 不正常工作了:

1. 点击图层左侧的小眼睛图标使图层显示或隐藏,图像窗口不能自动刷新。
2. 使用文字工具时,CPU占用100%。

经排查,确定是 PS CS3XP SP3 存在兼容性问题,前不久刚打了 XP SP3

解决办法:

点击菜单“编辑 -> 首选项 -> 文字”,去掉“字体预览大小”前面的勾。
重启 Photoshop

(EOF)