转自深度:个人防火墙的真相

有多种方式来保护你的计算机免受恶意软件的侵害,比如软件防火墙,病毒和Rootkit检测软件等。所有这些防护软件都基于一些众所周知而几乎没有改进的技 术,而且这些技术你也许已经知道,继承这些技术的安全产品也是不完善的。这样的后果通常是可怕的。然而这些安全公司却天真的认为他们的产品是最先进的,包 含了最新的特性,是用户必备的。但是只要你透过他们的华丽的外衣就会看到,他们的Bug和错误甚至比你在学校的设计还要多。虽说生产一款产品首要的是好而 强有力的广告。但沉重的广告和糟糕的测试结合在一起能向用户展示一些产品所谓的强大吗,能使其产生一种强烈的购买欲望吗?当然不行。

保护你的个人电脑免受外部的和内部的第三者的攻击是一个好主意。一些保护你的PC远离木马和间谍软件“启发式”的方法好像真在工作。如果他们工作的话,付30到50美元就能获得实时的保护和更新的确是个非常好的想法。可惜,他们不能。
首先我想为我糟糕的英语道歉。我们的讨论将会涉及以下产品:
ZoneAlarm Firewall 6.x
Outpost Firewall 3.x and 4.x
Look'n'Stop Firewall 2.0
Kerio Firewall 4.3
Sygate Firewall 5.6
Jetico Firewall 1.0
PortsLock Firewall 1.9
Tiny Firewall 6.5
Norton Internet Security 8.0
Comodo Firewall 2.4
OnlineArmor Firewall 2.1
个人软件防火墙的保护有2种基本级别:NDIS(网络驱动接口规范)和TDI(传输数据接口)。NDIS级是建立在TCP/IP栈的基础上的,在这个级别 上的保护能阻止基于TCP/IP栈BUG的攻击,甚至你不用更新你的栈;他也能防御许多电子欺骗,洪水攻击和分布式拒绝服务攻击。没有使用NDIS 技术的防火墙自然变成了局外人。失败者有PortsLock防火墙, Norton Internet Security, Comodo防火墙等。你真的愿意为PortsLock防火墙付35欧元吗,他甚至不能抵御低级别的TCP/IP攻击。或者为Comodo付79美元?为 你的系统感到悲哀。真正的防火墙是基于NDIS级的。基于NDIS有两种不同的保护方法。第一种是NDIS-hooking保护。主要方法是Hooks NdisRegisterProtocol()/NdisOpenAdapter()。当NDIS协议试图被注册时或是正要绑定某个适配器时,防火墙将接 到通知。在防火墙正在HOOK NDIS_PROTOCOL_BLOCK and NDIS_OPEN_BLOCK的句柄的这个关键时刻,并且当系统想要发送或接收数据包时,防火墙将会被通知当这些发生的时候。防火墙正试图安装他们的钩 子时是有趣的时候,几乎每个防火墙都是hook NDIS_PROTOCOL_BLOCK的句柄,但是Sygate, Kerio 和Jetico等除外,他们是HOOK NDIS_PROTOCOL_CHARACTERISTICS的句柄,而不是调用真正的NdisRegisterProtocol(),在那之后他们又移 动到原来的句柄。更安全吗?对吗?正是,防火墙应该独立工作,与多少协议被注册和多少被绑定无关。是的,每个防火墙都支持这个。例如,当 ZoneAlarm正在hook一个句柄时,他是分配一个无分页的内存并且放入以下几条指令。
pop eax ; 58
push HookData ; 68 XX XX XX XX
push eax ; 50
jmp HookedHandler ; E9 XX XX XX XX
然后,他写一些重要的信息在其他内存片,调用HookData和所有在NDIS_PROTOCOL_BLOCK中被hook的句柄:
OpenAdapterCompleteHandler
CloseAdapterCompleteHandler
BindAdapterHandler
UnbindAdapterHandler
在NDIS_OPEN_BLOCK中的:
SendHandler
ReceiveHandler
ReceivePacketHandler
SendPacketsHandler

对NDIS_PROTOCOL_BLOCK象下面这样:
VOID HookedOpenAdapterComplete(
PVOID HookData,
IN NDIS_HANDLE ProtocolBindingContext,
IN NDIS_STATUS Status,
IN NDIS_STATUS OpenErrorStatus
);

真正的OpenAdapterComplete() 句柄被放在这:[HookData + 0x770]
VOID HookedCloseAdapterComplete(
PVOID HookData,
IN NDIS_HANDLE ProtocolBindingContext,
IN NDIS_STATUS Status
);

真正的CloseAdapterComplete() 句柄被放在这:[HookData + 0x774]
VOID HookedBindAdapter(
PVOID HookData,
OUT PNDIS_STATUS Status,
IN NDIS_HANDLE BindContext,
IN PNDIS_STRING DeviceName,
IN PVOID SystemSpecific1,
IN PVOID SystemSpecific2
);
真正的BindAdapter() 句柄被放在这:[HookData + 0x764]
VOID HookedUnbindAdapter(
PVOID HookData,
OUT PNDIS_STATUS Status,
IN NDIS_HANDLE ProtocolBindingContext,
IN NDIS_HANDLE UnbindContext
);
真正的UnbindAdapter() 句柄被放在这: [HookData + 0x768]
For NDIS_OPEN_BLOCK:
VOIDHookedSend(
PVOID HookData,
IN NDIS_HANDLE NdisBindingHandle,
IN PNDIS_PACKET Packet
);
真正的Send() 句柄被放在这: [HookData + 0x1A4]

VOID HookedReceive(
PVOID HookData,
IN NDIS_HANDLE ProtocolBindingContext,
IN NDIS_HANDLE MacReceiveContext,
IN PVOID HeaderBuffer,
IN UINT HeaderBufferSize,
IN PVOID LookAheadBuffer,
IN UINT LookaheadBufferSize,
IN UINT PacketSize
);

真正的Receive() 句柄被放在这: [HookData + 0x4D0]

VOID HookedReceivePacket(
PVOID HookData,
IN NDIS_HANDLE ProtocolBindingContext,
IN PNDIS_PACKET Packet
);

真正的ReceivePacket() 句柄被放在这: [HookData + 0x570]

VOID HookedSendPackets(
PVOID HookData,
IN NDIS_HANDLE MiniportAdapterContext,
IN PPNDIS_PACKET PacketArray,
IN UINT NumberOfPackets
);
真正的SendPackets() 句柄被放在这: [HookData+2E4h]

可怜的ZoneAlarm,这是如此的简单对于获取真实的句柄并恢复他。
我说过,每个防火墙都支持多数的注册协议。事实上,并不是每个。Sygate认为,保持关于协议和公开包的所有信息在他的.data段中已经足够了。不幸 是个坏主意。他的句柄HOOK方式比ZoneAlarm简单,但是说明哪些内存将被分配的指令被放在Sygate的驱动Teefer.sys中:

pop eax ; 58
push HookData ; 68 XX XX XX XX
push eax ; 50
jmp FakeHandler ; E9 XX XX XX XX

HookData也是放在.data中的。Sygate总共能HOOK576个句柄(包括协议和开放块句柄)。于是,大约有40-50个 NDIS_PROTOCOL_BLOCK和NDIS_OPEN_BLOCK能被Hook(不要忘记几个开放块能被附加到一个协议块)。大概40-50块已 经足够了,但那样的代码实在是个糟糕的设计,给缓冲区溢出打了个招呼。

我有另外一个好例子,是关于关于多么有必要知道怎样Hook和hook多少。那些来自Kerio防火墙小组的家伙不知道这些。作为一个好的防火墙,设置 HOOK在 NdisRegisterProtocol(), NdisDeregisterProtocol(), NdisOpenAdapter(), NdisCloseAdapter() 等函数并且HOOK句柄。就像我说的,Kerio只是hook NDIS_PROTOCOL_CHARACTERISTICS的句柄并且仅仅调用NdisRegisterProtocol()函数,但他不将句柄移回 NDIS_PROTOCOL_CHARACTERISTICS。这会怎样?未公开的特性?我不这样认为,恰恰是粗心的编码和对内核标准和架构的误解。另一 个好的例子是,Kerio团队不知道任何关于NDIS的开发,事实就是这样,他们甚至不知道怎样去HOOK。Kerio防火墙HOOK在 NDIS_PROTOCOL_CHARACTERISTICS里:
OpenAdapterCompleteHandler
CloseAdapterCompleteHandler

并这样hook NDIS_OPEN_BLOCK:
SendHandler
SendPacketsHandler

不是太多?刚好。他能被用来绕过他的NDIS保护,并发送一个包给TCP/IP栈。你虽然已经按了“阻止所有”按钮,但仍然能在本地嗅探器下看到进来的数 据包。我可不喜欢像Kerio防火墙假装的那样假装自己受保护的时候,再去嗅探器下看数据包。从另一个方面来说,Outpost防火墙,喜欢在 NDIS_PROTOCOL_BLOCK中hook更多的句柄:

OpenAdapterCompleteHandler
SendCompleteHandler
TransferDataCompleteHandler
RequestCompleteHandler
ReceiveHandler
StatusHandler
ReceivePacketHandler
BindAdapterHandler
UnbindAdapterHandler

在 NDIS_OPEN_BLOCK中:
Outpost 4.0:
SendCompleteHandler
TransferDataCompleteHandler
ReceiveHandler
ReceivePacketHandler
StatusHandler

Outpost 3.x:
SendHandler
TransferDataHandler
SendCompleteHandler
TransferDataCompleteHandler
ReceiveHandler
RequestCompleteHandler
ReceivePacketHandler
SendPacketsHandler
StatusHandler

他的句柄调用和防火墙之间的代码是非常有趣的:
call ImCode ; E8 XX XX XX XX


For Outpost 3.x:

pop eax ; 58
push [eax] ; FF 30 Pushing the real handler
pushad ; 60
push [eax+4] ; FF 70 04
push [esp+28h] ; FF 74 24 28 Pushing return address
jmp [eax+8] ; FF 60 08

For Outpost 4.0:

pop eax ; 58
add eax, 3 ; 83 C0 03 Missing three zero bytes after call
push [eax] ; FF 30 Pushing the real handler
pushad ; 60
push [eax+4] ; FF 70 04
push [esp+28h] ; FF 74 24 28 Pushing return address
jmp [eax+8] ; FF 60 08
像我们看到的那样,那不是一个问题对于卸载所有的HOOK并立即获取系统控制权。如果你厌烦了这中间的汇编代码,他们通常是能够转换成普通C代码的,我能向你展示一些来自Tiny防火墙团队有趣的东西,他假装是真正的安全,并且向我们展示了很多的hook:

NdisOpenAdapter
NdisCloseAdapter
NdisInitializeWrapper
NdisTerminateWrapper
NdisMRegisterMiniport
NdisIMRegisterLayeredMiniport
NdisRegisterProtocol
NdisMSetAttributesEx
NdisRegisterMac
NdisIMAssociateMiniport
NdisClOpenAddressFamily
NdisCmRegisterAddressFamily
NdisMCmRegisterAddressFamily
NdisMCoSendComplete

哦,看起来很优秀,也真是很优秀的样子,他们试图对这些函数做一个结合HOOK通过设置跳转到他们的句柄。我不喜欢“结合”,知道为什么吗?那不是因为他 难以实现或是有些指令很难“结合”(理论上可行,实践上未必,我不认为在真正函数的开端那样做可行)。主要的原因是那样不安全。Rootkit开发者使用 他是因为那是一种获取控制权的简单的方式,但不要试图把他用作安全软件中,就像用恐怖份子的方法对付恐怖分子一样。还有要说的是,我没有发现任何线程安全 和多处理器安全的代码。再一次失败。当你在“结合”某些代码的时候,你需要确信没有一个线程、没有一个CPU在运行这些代码。
Tiny防火墙为每个HOOK句柄申请内存,并拷贝这些C函数在开端和结尾:
push ebp ; 55
mov ebp, esp ; 8B EC
sub esp, XXh ; 83 EC XX
mov byte ptr [ebp-1], XXh ; C6 45 FF XX
mov dword ptr [ebp-8], HookedHandler ; C7 45 F8 XX XX XX XX
<...>
push RealHandler
call [ebp-8]
mov esp, ebp
pop ebp
ret XXh

于是这就变的相当简单,对于从新句柄偏移0xD去获得真的句柄。对所有的句柄,Tiny都是拷贝上面的代码。
hook在NDIS_PROTOCOL_BLOCK中:

SendCompleteHandler
StatusHandler

Hooked 在 NDIS_OPEN_BLOCK中:

TransferDataHandler
SendCompleteHandler
TransferDataCompleteHandler
ReceiveHandler
ReceivePacketHandler
StatusHandler

需要说的是,有些防火墙变聪明了,如果他们被别人hook的时候,他们会试图恢复他们的hook。例如,Outpost防火墙就检测这个并监视他们的 hook,但仅仅像指针的安全那样。也许他们不认为会有剪接戏法?Outpost小组不知道,白帽子Sygate防火墙也能恢复他们的hook,但他恢复 的仅仅是NDIS_OPEN_BLOCK.SendHandler和 NDIS_OPEN_BLOCK.SendPacketsHandler。那是奇怪的,因为因为他们hook了很多 NDIS_PROTOCOL_BLOCK中的句柄:

OpenAdapterCompleteHandler
CloseAdapterCompleteHandler
TransferDataCompleteHandler
ReceiveHandler
StatusHandler

和 NDIS_OPEN_BLOCK 的hook:

SendHandler
TransferDataHandler
TransferDataCompleteHandler
ReceiveHandler
ReceivePacketHandler
SendPacketsHandler
StatusHandler
只有几种防火墙用中间层NDIS驱动来做NDIS级的保护。这是一个聪明的解决方案。例如,Look’n’Stop 和 Outpost 2008 防火墙用IM NDIS驱动。Outpost好的改进是在去年。但是现在绕过防火墙变的容易使因为没有HOOK,没有伎俩和没有任何未公布的特性被使用。现在你能粉碎所 有的IM NDIS防火墙只是绕过他们。不需要扰乱防火墙接受和发送包的句柄,你也不用向他们发送包。总之,使用IM NDIS驱动是好的,众所周知的,已公布的,一个正确的保护系统不受外部攻击的方法。

像你可能看到的那样,技术各个防火墙的技术是相似的,思路甚至能被盗用并且没有人会说你盗用了一个想法。好的方式是透过表面创新你的产品。不需去思考,不 用产生想法,去反汇编、调试并且对它感兴趣。第二种主级别的防火墙基于TDI,传输数据接口能为内核传输协议栈提供更高级别的访问。 Afd.sys,能解析winsock,经由tdi.sys(TDI层,上面层说过)与Windows TCP/IP栈(tcpip.sys)通信。于是,又几种方式和地方去安装hook并监视你的程序和网络通信。设置你的保护最好的地方正是在 tcpip.sys设备,它能在协议栈的最上层解释执行:
DeviceIp
DeviceTcp
DeviceUdp
DeviceIPMULTICAST
DeviceRawIp

好的,你甚至能通过\Device\RawIp来戏弄一些人。对于阻止程序的一个访问网络的请求最好的方式(公开
的方式,也是重要的),是附加到这些设备上并监视他们。几乎所有的防火墙都这样做了,因为这是通知用户陌生程序要访问网络的好方法。例如,Look’n’Stop附加他的设备到tcpip.sys设备:
DevicePcaTcpFilter -> DeviceTcp
DevicePcaUdpFilter -> DeviceUdp
DevicePcaRawIpFilter -> DeviceRawIp

\Device\Ip在那里?不是足够的,我们需要确信,所有种类的连接需要被监视,或是他们恰恰通过了这个假冒的保护。
你的看见这些设备在你的WinObj工具里:
DeviceComodoRawIpFilter
DeviceComodoUdpFilter
DeviceComodoTcpFilter
DeviceComodoIpFilter
我希望你能猜到,Comodo防火墙是附加设备到相同名字的tcpip.sys设备。

Outpost、Kerio、Jetico 防火墙, Norton Internet Security仅仅附加一个无名的设备到Tcp,Udp,Ip,RawIp,没有感兴趣的事。了解设备栈结构的人应该知道抛开这些附加设备使防火墙不再 提示是相当容易的。你只需要找到tcpip.sys的DRIVER_OBJECT的指针,遍历DeviceObject列表,清空所有 DEVICE_OBJECT的附加项。相当容易吧?但对对抗防火墙很有效,他们的防护程序就是在那附近。另一个脆弱防火墙的错误和夸大的广告。除了附加技 术外,还有另一种有趣而复杂的的技术,但对恶意软件更有效,这就是MajorTable hook。大家都知道,当用户态程序想与驱动交互的时候,他通知要交互驱动的I/O管理器。I/O管理器产生一个IRP,并调用 DRIVER_OBJECT.MajorTable[IRP_MJ_XXX]的句柄。就在tcpip.sys这,一些防火墙仅仅hook一个 IRP_MJ_XXX句柄并监视所有调用,通过允许请求和拒绝禁止。找到一个真的句柄不是很容易,哈?几乎每个人都认为那不可能。寻找一些被hook的句 柄的主要方法是,他必须被调用然后会被发现。通过hook Int 0x01,设置TF,防火墙句柄的所有调用,我们能发现一个防火墙要调用的真的句柄。简单,MajorTable-hooking技术被像 ZoneAlarm这样的防火墙青睐,他是这样HOOK的:
IRP_MJ_CREATE
IRP_MJ_CLOSE
IRP_MJ_DEVICE_CONTROL
IRP_MJ_INTERNAL_DEVICE_CONTROL
IRP_MJ_CLEANUP

另一个局部hook的失败是,tcpip.sys仅有TCPDispatch() 和TCPDispatchInternalDeviceControl()2个IRP句柄,第一个被所有 MajorTable填充,把它从另一个句柄上移出时容易的。TCPDispatchInternalDeviceControl()你能通过使用另一个 方法 (例如前面说的追踪方法和反汇编方法)来发现。Sygate防火墙喜欢HOOK这些句柄:
IRP_MJ_CREATE
IRP_MJ_CLOSE
IRP_MJ_DEVICE_CONTROL
IRP_MJ_INTERNAL_DEVICE_CONTROL

PortsLock防火墙,hook所有MajorTable句柄。好主要,既不将TCPDispatch()也不将TCPDispatchInternalDeviceControl() 给敌人。Tiny防火墙是hook下面的句柄。
IRP_MJ_CREATE
IRP_MJ_CLOSE
IRP_MJ_DEVICE_CONTROL
IRP_MJ_INTERNAL_DEVICE_CONTROL
IRP_MJ_CLEANUP

对于在一个防火墙中仅有这种类型的保护是不严重的,你不这样认为吗?我认为像PortsLock防火墙,Norton Internet Security,Comodo防火墙和其他有“防火墙”这个错误称号的,就像一些有着很漂亮界面的保护系统。恶意软件正在变的越来越猖獗,每天都有大量 的ROOTKIT杯注册,更多老练的,先进的,比防火墙更底层的正在不断涌现。他们试图影响其他进程并隐匿其中。就像一个披着羊皮的狼。我谈到, injection, OpenProcess()/WriteProcessMemory()/CreateRemoteThread()和一个好的shellcode或一个 self-mapped模块等都能透过防护与外部通信,而且工作的很好,也很容易编写,所有需要好的保护而不受其侵害。系统服务标提供内核函数的访问通 道,他是通过调_KiSystemService,那是用用户模式和驱动来实现的。防火墙为了阻止这些动作使系统保持健壮,成功地hook了打开进程、写 进程内存、创建远线程、进程、文件、文件编辑等函数。防火墙喜欢做这些,例如,ZoneAlarm防火墙hook以下系统服务:
ZwConnectPort
ZwCreateFile
ZwCreateKey
ZwCreatePort
ZwCreateProcess
ZwCreateProcessEx
ZwCreateSection
ZwCreateWaitablePort
ZwDeleteFile
ZwDeleteKey
ZwDeleteValueKey
ZwDuplicateObject
ZwLoadKey
ZwMapViewOfSection
ZwOpenFile
ZwOpenProcess
ZwOpenThread
ZwReplaceKey
ZwRequestWaitReplyPort
ZwRestoreKey
ZwSecureConnectPort
ZwSetInformationFile
ZwSetSystemInformation
ZwSetValueKey
ZwTerminateProcess

他监视对注册表,进程,驱动,文件的访问,并阻止自己被关闭。Outpost Firewall 4 hook更多的服务:
ZwAssignProcessToJobObject
ZwClose
ZwCreateFile
ZwCreateKey
ZwCreateProcess
ZwCreateProcessEx
ZwCreateSymbolicLinkObject
ZwCreateThread
ZwDeleteFile
ZwDeleteKey
ZwDeleteValueKey
ZwLoadDriver
ZwMakeTemporaryObject
ZwOpenFile
ZwOpenKey
ZwOpenProcess
ZwProtectVirtualMemory
ZwQueryDirectoryFile
ZwQueryKey
ZwQueryValueKey
ZwReplaceKey
ZwRestoreKey
ZwSaveKey
ZwSaveKeyEx
ZwSetInformationFile
ZwSetValueKey
ZwTerminateProcess
ZwTerminateThread
ZwUnloadDriver
ZwWriteVirtualMemory

但事实上他仅用了他们的一半。他不关心驱动,你能加载任何你想要的,但他hook了ZwLoadDriver。更多的hook就更好吗?啊哈,对漏洞挖掘 者来说更好。不是每个人都知道代码的质量怎样,它将要运行当我们调用ZwOpenKey时。我的系统将要蓝屏吗?另一个关于hook的例子是Kerio 防火墙:
ZwClose
ZwCreateFile
ZwCreateKey
ZwCreateProcess
ZwCreateProcessEx
ZwCreateThread
ZwDeleteFile
ZwDeleteKey
ZwDeleteValueKey
ZwLoadDriver
ZwMapViewOfSection
ZwOpenFile
ZwOpenKey
ZwResumeThread
ZwSetInformationFile
ZwSetValueKey
ZwTerminateProcess
ZwWriteFile
没看见ZwOpenProcess/ZwWriteVirtualMemory的hook。使用Kerio你能插入你想要的每个进程,包括允许的进程。Sygate所用的hook比其他的少:
ZwTerminateProcess
ZwMapViewOfSection
ZwAllocateVirtualMemory
ZwCreateThread
ZwProtectVirtualMemory
ZwWriteVirtualMemory
他们认为能通过hook ZwWriteVirtualMemory来保护系统。那是有BUG的:你能打开进程,并改变一些线程的context,几乎与 Jetico做的相同:
ZwConnectPort
ZwCreatePort
ZwCreateThread
ZwTerminateProcess
ZwWriteVirtualMemory
不是很有趣。Tiny防火墙做的最有趣:
ZwCreateKey
ZwCreateSection
ZwOpenKey
ZwSetInformationProcess
ZwTerminateProcess
你能这样说吗?:这是容易的对于通过ZwWriteVirtualMemory来插入一些东西。当然,但当此恶意行为发生时,用户将被通知。这是个愚蠢的 小玩笑,对于在用户模式下来保护,Tiny防火墙载入他的UmxSbxExw.dll 到每个进程并hook许多进程通过安装每个函数开始的跳转:
kernel32.dll:
CreateProcessA
CreateProcessW
CreateRemoteThread
DebugActiveProcess
FreeLibrary
GetProcAddress
LoadLibraryExW
OpenThread
TerminateProcess
TerminateThread
WriteProcessMemory

user32.dll:
BroadcastSystemMessage
BroadcastSystemMessageA
BroadcastSystemMessageExA
BroadcastSystemMessageExW
BroadcastSystemMessageW
EndTask
ExitWindowsEx
OpenClipboard
PostMessageA
PostMessageW
PostThreadMessageA
PostThreadMessageW
SendDlgItemMessageA
SendDlgItemMessageW
SendMessageA
SendMessageCallbackA
SendMessageCallbackW
SendMessageTimeoutA
SendMessageTimeoutW
SendMessageW
SendNotifyMessageA
SendNotifyMessageW
SetUserObjectSecurity
SetWindowsHookA
SetWindowsHookExA
SetWindowsHookExW
SetWindowsHookW

advapi32.dll:
AbortSystemShutdownW
AdjustTokenPrivileges
ChangeServiceConfig2A
ChangeServiceConfig2W
ChangeServiceConfigA
ChangeServiceConfigW
ControlService
CreateProcessAsUserA
CreateProcessAsUserW
CreateProcessWithLogonW
CreateServiceA
CreateServiceW
DeleteService
EnumDependentServicesA
EnumDependentServicesW
EnumServicesStatusA
EnumServicesStatusExA
EnumServicesStatusExW
EnumServicesStatusW
InitiateSystemShutdownExW
InitiateSystemShutdownW
OpenSCManagerA
OpenSCManagerW
OpenServiceA
OpenServiceW
QueryServiceConfig2A
QueryServiceConfig2W
QueryServiceConfigA
QueryServiceConfigW
QueryServiceStatus
QueryServiceStatusEx
SetFileSecurityW
SetKernelObjectSecurity
SetNamedSecurityInfoW
SetSecurityInfo
SetServiceObjectSecurity
StartServiceA
StartServiceW

哦,Tiny是个hook怪物。你能看见在 kernel32.dll上的hook!WriteProcessMemory在他们的保护之下以防止被插入。 Tiny防火墙团队认为那是一个很酷的保护方式,在一些不稳定的程序中他可能会挂掉。从那些进程中卸载掉他的Dll也不是件难事,恢复所有的函数入口就可 以享受生活了。Outpost也喜欢这样做,他加载他的wl_hook.dll到应该被保护以防止恶意软件的每个进程。那是庞大的hook队列:

kernel32.dll:
CreateProcessA
CreateProcessW
CreateRemoteThread
DebugActiveProcess
WinExec

ntdll.dll:
LdrLoadDll
LdrUnloadDll
NtCreateThread
NtResumeProcess
NtResumeThread
NtSetContextThread
NtSetValueKey
NtSuspendProcess
NtSuspendThread
NtTerminateProcess
NtWriteVirtualMemory
ZwCreateThread
ZwResumeProcess
ZwResumeThread
ZwSetContextThread
ZwSetValueKey
ZwSuspendProcess
ZwSuspendThread
ZwTerminateProcess
ZwWriteVirtualMemory

user32.dll:
CallNextHookEx
ChangeDisplaySettingsExA
ChangeDisplaySettingsExW
DdeConnect
DdeConnectList
DdeInitializeA
DdeInitializeW
EndTask
ExitWindowsEx
FindWindowExA
FindWindowExW
PostMessageA
PostMessageW
SendInput
SendMessageA
SendMessageCallbackA
SendMessageCallbackW
SendMessageTimeoutA
SendMessageTimeoutW
SendMessageW
SendNotifyMessageA
SendNotifyMessageW
SetForegroundWindow
SetWindowPos
SetWindowsHookExA
SetWindowsHookExW
那不是一个保护,是没有必要的代码。是的,他能抵制一些稀有恶意代码,但没有更多。一些防火墙不仅想被作为一个防火墙来使用,他们包含了一些有趣的特性像 间谍软件或rootkit的检测程序。是的,他们工作了,这就产生了一个印象。在遇到调试之前的几分钟。一些防火墙试图监视模块的加载通过设置经由 PsSetLoadImageNotiryRoutine() 的例行检查。Jetico Firewall, Look’n’Stop Firewall, Tiny Firewall, ZoneAlarm Firewall, Outpost Firewall是这样做的。例如,当Outpost加载时,监视dnsapi.dll的加载,他通知用户一些程序试图使用DNS服务。这将会发生,当你 将要调用gethostbyname()时,dnsapi.dll 会自动加载。不是很酷。重命名dnsapi.dll到xxx.dll,加载它,找到DnsQuery_A的指针,并使用它来解析,OutPost将会关 闭。这是个糟糕的方式对于在这儿的保护。几乎在每个防火墙中,你都能发现很多无用的,无效的东西。有时,它帮助理解部分代码或给出一个例子函数命名,甚至 你能发现一些调式信息。Look’n’Stop防火墙团队没有区分发布版和调试版,可能那就是他们为什么留下这些东西在这:
push offset asc_187DC ; "f:\\dev\\lns\\2.05.cur\\tdisys\\w32api.c"
push offset aKegetcurren_23 ; "KeGetCurrentIrql() == PASSIVE_LEVEL"
call ds:RtlAssert

断言在调试构建时是有用的,但不是在发布版中。Tiny防火墙有很多奇怪的格式串日志:
.text:00011608 aNbh0x08xMac0x0 db 9,'nbh:0x%08X mac:0x%08X mah:0x%08X pbc:0x%08X',0Dh,0Ah
.text:00011608 ; DATA XREF: HookedNdisOpenAdapter+273 o
.text:00011608 db 9,'med:"%s"',0Dh,0Ah
.text:00011608 db 9,'drv:"%s"',0Dh,0Ah
.text:00011608 db 9,'dev:"%s"',0Dh,0Ah
.text:00011608 db 9,'ada:"%s"',0
.text:00011661 align 4

我认为没人想因为这些奇怪的记录而看见蓝屏。ZoneAlarm也喜欢记录:
.text:0001A610 aUPacketSProtoS db '%u Packet %s: Prot %s Flags: 0x%08lx Src: %2u.%2u.%2u.%2u '
.text:0001A610 ; DATA XREF: sub_1A693+119 o
.text:0001A610 db 'Dest: %2u.%2u.%2u.%2u ',0

他尤其喜欢记录在发布版本中,当然。
.text:00025A84 aSFragTLxHLxPfl db '%s frag: t=%lx h=%lx pflg=%lx subp=%lx sip=%d.%d.%d.%d dip=%'
.text:00025A84 ; DATA XREF: sub_25BAA+2D1 o
.text:00025A84 db 'd.%d.%d.%d id=%x f=%s%s%s off=%hu act=%lx',0Ah,0

我喜欢 ZoneAlarm,他给我一些函数的名字:
.text:00050250 aVsdatantVstdic db 'VSDATANT:vsTdiClientRequestReceivePreProc(): FO=%p SE=%p RE#'
.text:00050250 ; DATA XREF: sub_502A6+D3 o
.text:00050250 db '=%d IRP=%p : TIMEOUT(%u)',0Ah,0

Kerio防火墙团队不包含路径就不能生成他们的驱动:
.rdata:00433704 aCProjectsNetsecuritytoolsFire db 'C:\Projects\netsecuritytools\Firewall SDK\Build.Release\4.3.'
.rdata:00433704 db 'x\bin\Release\fwdrv.pdb',0

他们也这样记录:
.data:00434020 aFwdrvApicopyas db 'FWDRV: ApiCopyAssociatedEndpoint: Local: %u.%u.%u.%u:%u Remo'
.data:00434020 ; DATA XREF: sub_401150+3B4 o
.data:00434020 db 'te:%u.%u.%u.%u:%u, SpeedIn: %u, SpeedOut: %u, PID: 0x%04X, A'
.data:00434020 db 'pp: [%s], Service: [%s]',0

并且真是有大量这样的记录:
.data:0043B1D4 aSPagedCodeCa_7 db '%s(): Paged code called on IRQL %d',0

我想说的是:不要紧,你喜欢记录多少,你应该移走这样的垃圾记录和垃圾代码在发布版中。越少的代码,越少的bug。也学下次蓝屏会刚好在那些糟糕测试并绑 定调试信息的代码中。今年的首次揭幕是OnlineArmor防火墙。“Online Armor有一个非常好的机会,调者华尔兹就窃取了苏格兰时事通讯2008最佳软件防火墙的荣誉称号” 是的,在DriverVerifier下的首次测试就给我了一个蓝屏。真是很有趣,他在标准检查工具下挂掉了。在里面你能发现 IRP_MJ_DEVICE_CONTROL 句柄,那是处理所有与METHOD_NEITHER有关的IOCTLs。
你知道ProbeForRead()/ProbeForWrite()?他们甚至不检查指针是否为空,这真是可笑,所有Windows用户的最好保护软件有如此多的问题。猜猜它能怎么样?一部分16进制的代码如下所示:

int __stdcall DispatchDeviceControl(int DeviceObject, PIRP Irp)
{
struct _IRP::$::$::$::$A02EC6A2CE86544F716F4825015773AC::_IO_STACK_LOCATION *Stack; // eax@1
unsigned int Status; // edi@1
PIRP _Irp; // esi@1
void *InBuffer; // ecx@2
int InBufferSize; // edi@2
PVOID OutBuffer; // edx@2
int OutBufferSize; // ebx@2
int Length; // eax@3
int ReturnLength; // [sp+8h] [bp-4h]@1

ReturnLength = 0;
_Irp = Irp;
Stack = Irp->Tail.Overlay.CurrentStackLocation;
Status = 0xC00000BBu;
if ( *(_BYTE *)Stack == 14 )
{
InBuffer = (void *)*((_DWORD *)Stack + 4);
InBufferSize = *((_DWORD *)Stack + 2);
OutBuffer = Irp->UserBuffer;
OutBufferSize = *((_DWORD *)Stack + 1);
Irp = (PIRP)*((_DWORD *)Stack + 3);
Status = HandleRequest((int)Irp, InBuffer, InBufferSize, OutBuffer, OutBufferSize, &ReturnLength);
}
Length = ReturnLength;
_Irp->IoStatus.Status = Status;
_Irp->IoStatus.Information = Length;
IofCompleteRequest(_Irp, 0);
return Status;
}

int __stdcall HandleRequest(int Ioctl, void *InBuffer, int InBufferSize, void *OutBuffer, int OutBufferSize, int *ReturnLength)
{

if ( Ioctl == 0x830020EB )
{
sub_1484A((int)OutBuffer);
*v6 = 28;
return 0;
}

在sub_1484A()里什么是如此的有趣?看看这里:
int __stdcall sub_1484A(int a1)
{
int result; // eax@3
CPPEH_RECORD ms_exc; // [sp+Ch] [bp-18h]@1

ms_exc.disabled = 0;
if ( dword_16168 )
{
memcpy((void *)a1, (const void *)dword_161A8, 0x1Cu);
}
else
{
result = 0;
memset((void *)a1, 0, 0x1Cu);
}
return result;
}
酷,在内核空间写些东西已经没有任何困难了,我们能制造一个DOS,甚至可以执行代码。我只是不想进一步发掘了,希望剩下的将被同道之一完成。如果我说的 是真的话,似乎每个防火墙都能被绕过,没有东西能做到100%的保护。Rootkit,木马,蠕虫和其他恶意软件变的更加隐蔽、复杂,而且很难被发现。我 不想给出任何关于选择哪款防火墙的建议,只是要确信,你用了一个功能强大的防火墙。

0 comments:

Post a Comment