内核对象类型不恰当验证
Windows一系列的“内核对象”暴露了内核的很多特性。这些对象有可能是由用户层通过句柄用户来执行的。句柄是整数值,内核会判断作为调用者进行交互的是哪个(通常是系统服务),然后将句柄解析成某个特定对象的指针。所有的对象都共享相同的句柄命名空间。
由于这些不同类型的对象共享着相同的命名空间,系统服务检查句柄的一项任务就是验证这些对象指向的是期望类型。这项工作是由对象管理例行程序bReferenceObjectByHandle完成的,这个例行程序会将句柄解析成对象指针并进行可选的嵌入类型检查,它是通过把标准对象页头中的类型域与需要验证的进行对照来完成检查的。
由于KAV关联着系统服务,它不可避免地需要处理内核句柄。不幸的是,它并没有正确地进行此类操作。在一些情况下,在使用对象指针前,KAV并不能确定指向某类型对象的句柄。如果错误类型的句柄被传递给了系统服务,那么系统就有可能崩溃。
典型的例子就是KAV的NtResumeThread关联,该关联试图跟踪系统中正在运行的线程的状态。在这个特例中,看起来用户层好像无法通过把错误类型的对象作为返回对象指针而导致系统崩溃。这主要是因为它仅仅是被用作查找表的钥匙,该列表由线程对象指针预先占用了。KAV跟NtSuspendThread相关联也是基于同样的目的,并且这项关联也在验证对象的句柄类型上存在问题。
.text:F82245E0 ; NTSTATUS __stdcall KavNtResumeThread(
HANDLE ThreadHandle,
PULONG PreviousSuspendCount)
.text:F82245E0 KavNtResumeThread proc near ; DATA XREF: sub_F82249D0+FBo
.text:F82245E0
.text:F82245E0 ThreadHandle = dword ptr 8
.text:F82245E0 PreviousSuspendCount= dword ptr 0Ch
.text:F82245E0
.text:F82245E0 push esi
.text:F82245E1 mov esi, [esp+ThreadHandle]
.text:F82245E5 test esi, esi
.text:F82245E7 jz short loc_F8224620
.text:F82245E9 lea eax, [esp+ThreadHandle] ;
.text:F82245E9 ; This should pass an object type here!在这里应当传递对象类型
.text:F82245ED push 0 ; HandleInformation
.text:F82245EF push eax ; Object
.text:F82245F0 push 0 ; AccessMode
.text:F82245F2 push 0 ; ObjectType
.text:F82245F4 push 0F0000h ; DesiredAccess
.text:F82245F9 push esi ; Handle
.text:F82245FA mov [esp+18h+ThreadHandle], 0
.text:F8224602 call ds:ObReferenceObjectByHandle
.text:F8224608 test eax, eax
.text:F822460A jl short loc_F8224620
.text:F822460C mov ecx, [esp+ThreadHandle]
.text:F8224610 push ecx
.text:F8224611 call KavUpdateThreadRunningState
.text:F8224616 mov ecx, [esp+ThreadHandle] ; Object
.text:F822461A call ds:ObfDereferenceObject
.text:F8224620
.text:F8224620 loc_F8224620: ; CODE XREF: KavNtResumeThread+7j
.text:F8224620 ; KavNtResumeThread+2Aj
.text:F8224620 mov edx, [esp+PreviousSuspendCount]
.text:F8224624 push edx
.text:F8224625 push esi
.text:F8224626 call OrigNtResumeThread
.text:F822462C pop esi
.text:F822462D retn 8
.text:F822462D KavNtResumeThread endp
.text:F822462D
.text:F8224590 ; NTSTATUS __stdcall KavNtSuspendThread(
HANDLE ThreadHandle,
PULONG PreviousSuspendCount)
.text:F8224590 sub_F8224590 proc near ; DATA XREF: sub_F82249D0+113o
.text:F8224590
.text:F8224590 ThreadHandle = dword ptr 8
.text:F8224590 PreviousSuspendCount= dword ptr 0Ch
.text:F8224590
.text:F8224590 push esi
.text:F8224591 mov esi, [esp+ThreadHandle]
.text:F8224595 test esi, esi
.text:F8224597 jz short loc_F82245D0
.text:F8224599 lea eax, [esp+ThreadHandle] ;
.text:F8224599 ; This should pass an object type here!
.text:F822459D push 0 ; HandleInformation
.text:F822459F push eax ; Object
.text:F82245A0 push 0 ; AccessMode
.text:F82245A2 push 0 ; ObjectType
.text:F82245A4 push 0F0000h ; DesiredAccess
.text:F82245A9 push esi ; Handle
.text:F82245AA mov [esp+18h+ThreadHandle], 0
.text:F82245B2 call ds:ObReferenceObjectByHandle
.text:F82245B8 test eax, eax
.text:F82245BA jl short loc_F82245D0
.text:F82245BC mov ecx, [esp+ThreadHandle]
.text:F82245C0 push ecx
.text:F82245C1 call KavUpdateThreadSuspendedState
.text:F82245C6 mov ecx, [esp+ThreadHandle] ; Object
.text:F82245CA call ds:ObfDereferenceObject
.text:F82245D0
.text:F82245D0 loc_F82245D0: ; CODE XREF: sub_F8224590+7j
.text:F82245D0 ; sub_F8224590+2Aj
.text:F82245D0 mov edx, [esp+PreviousSuspendCount]
.text:F82245D4 push edx
.text:F82245D5 push esi
.text:F82245D6 call OrigNtSuspendThread
.text:F82245DC pop esi
.text:F82245DD retn 8
.text:F82245DD sub_F8224590 endp
.text:F82245DD
但是,并不是所有的KAV关联都这么幸运。KAV安装的NtTerminateProcess钩子会查看函数的进程句柄参数所指向的对象实体,这样就能确定要终止的进程的名称。但是KAV没有验证用户层提供的对象句柄是否是真的指向一个进程对象。
由于种种原因,这样做很不安全,如果读者曾经进行过内核编程你就会深刻体会为什么这样不安全。
内核进程结构定义(EPROCESS)随着OS更换甚至是Servicepack的更换而频繁地改变。这样造成的结果就是,直接访问这样的结构通常都很不安全。
由于KAV不适当地执行类型核实操作,因此很有可能将一个对象句柄传递给不同的内核对象——比如,mutex——于是因为mutex的内部结构(或者任何其它的内核对象)与进程对象的内部结构不兼容,KAV将导致系统崩溃。
对于上一个问题,KAV想用如下方法应对:它试图实时地找出包含进程名称的EPROCESS结构成员的偏移地址。它所使用的算法是,每次从进程对象指针的开始提前扫描一字节,直到它发现用来识别初始系统进程名字的那个字节串。(这个例行程序在初始系统进程环境中被调用)。与杀毒软件、还有其他利用与进程关联的像文件名称的那些低端产品比起来,这种例行程序似乎很普通。
.text:F82209E0 KavFindEprocessNameOffset proc near ; CODE XREF: sub_F8217A60+FCp
.text:F82209E0 push ebx
.text:F82209E1 push esi
.text:F82209E2 push edi
.text:F82209E3 call ds:IoGetCurrentProcess
.text:F82209E9 mov edi, ds:strncmp
.text:F82209EF mov ebx, eax
.text:F82209F1 xor esi, esi
.text:F82209F3
.text:F82209F3 loc_F82209F3: ; CODE XREF: KavFindEprocessNameOffset+2Ej
.text:F82209F3 lea eax, [esi+ebx]
.text:F82209F6 push 6 ; size_t
.text:F82209F8 push eax ; char *
.text:F82209F9 push offset aSystem ; "System"
.text:F82209FE call edi ; strncmp
.text:F8220A00 add esp, 0Ch
.text:F8220A03 test eax, eax
.text:F8220A05 jz short loc_F8220A16
.text:F8220A07 inc esi
.text:F8220A08 cmp esi, 3000h
.text:F8220A0E jl short loc_F82209F3
.text:F8220A10 pop edi
.text:F8220A11 pop esi
.text:F8220A12 xor eax, eax
.text:F8220A14 pop ebx
.text:F8220A15 retn
.text:F8220A16 ; ---------------------------------------------------------------------------
.text:F8220A16
.text:F8220A16 loc_F8220A16: ; CODE XREF: KavFindEprocessNameOffset+25j
.text:F8220A16 mov eax, esi
.text:F8220A18 pop edi
.text:F8220A19 pop esi
.text:F8220A1A pop ebx
.text:F8220A1B retn
.text:F8220A1B KavFindEprocessNameOffset endp
.text:F8217B5C call KavFindEprocessNameOffset
.text:F8217B61 mov g_EprocessNameOffset, eax
有了类型错误的那些对象的句柄后,KAV就可以通过读取返回的对象body指针,得到被破坏的进程名字。如果一个对象不是进程对象的话,这种方法是无法应付到对象结构末尾的(与一些对象比起来,进程对象非常巨大,比如Mutex对象,并且结构中的对象名称的偏移地址通常是几百字节或者更多)。可以预见,如果错误的句柄被传递给NtTerminateProcess的话,它将会造成系统的崩溃。
.text:F82241C0 ; NTSTATUS __stdcall KavNtTerminateProcess(HANDLE ThreadHandle,NTSTATUS ExitStatus)
.text:F82241C0 KavNtTerminateProcess proc near ; DATA XREF: sub_F82249D0+ABo
.text:F82241C0
.text:F82241C0 var_58 = dword ptr -58h
.text:F82241C0 ProcessObject = dword ptr -54h
.text:F82241C0 ProcessData = KAV_TERMINATE_PROCESS_DATA ptr -50h
.text:F82241C0 var_4 = dword ptr -4
.text:F82241C0 ProcessHandle = dword ptr 4
.text:F82241C0 ExitStatus = dword ptr 8
.text:F82241C0
.text:F82241C0 sub esp, 54h
.text:F82241C3 push ebx
.text:F82241C4 xor ebx, ebx
.text:F82241C6 push esi
.text:F82241C7 mov [esp+5Ch+ProcessObject], ebx
.text:F82241CB call KeGetCurrentIrql
.text:F82241D0 mov esi, [esp+5Ch+ProcessHandle]
.text:F82241D4 cmp al, 2 ;
.text:F82241D4 ; IRQL >= DISPATCH_LEVEL? Abort
.text:F82241D4 ; ( This is impossible for a system service )
.text:F82241D6 jnb Ret_KavNtTerminateProcess
.text:F82241DC cmp esi, ebx ;
.text:F82241DC ; Null process handle? Abort
.text:F82241DE jz Ret_KavNtTerminateProcess
.text:F82241E4 call PsGetCurrentProcessId
.text:F82241E9 mov [esp+5Ch+ProcessData.CurrentProcessId], eax
.text:F82241ED xor eax, eax
.text:F82241EF cmp esi, 0FFFFFFFFh
.text:F82241F2 push esi ; ProcessHandle
.text:F82241F3 setnz al
.text:F82241F6 dec eax
.text:F82241F7 mov [esp+60h+ProcessData.TargetIsCurrentProcess], eax
.text:F82241FB call KavGetProcessIdFromProcessHandle
.text:F8224200 lea ecx, [esp+5Ch+ProcessObject] ; Object
.text:F8224204 push ebx ; HandleInformation
.text:F8224205 push ecx ; Object
.text:F8224206 push ebx ; AccessMode
.text:F8224207 push ebx ; ObjectType
.text:F8224208 push 0F0000h ; DesiredAccess
.text:F822420D push esi ; Handle
.text:F822420E mov [esp+74h+ProcessData.TargetProcessId], eax
.text:F8224212 mov [esp+74h+var_4], ebx
.text:F8224216 call ds:ObReferenceObjectByHandle
.text:F822421C test eax, eax
.text:F822421E jl short loc_F8224246
.text:F8224220 mov edx, [esp+5Ch+ProcessObject]
.text:F8224224 mov eax, g_EprocessNameOffset
.text:F8224229 add eax, edx
.text:F822422B push 40h ; size_t
.text:F822422D lea ecx, [esp+60h+ProcessData.ProcessName]
.text:F8224231 push eax ; char *
.text:F8224232 push ecx ; char *
.text:F8224233 call ds:strncpy
.text:F8224239 mov ecx, [esp+68h+ProcessObject]
.text:F822423D add esp, 0Ch
.text:F8224240 call ds:ObfDereferenceObject
.text:F8224246
.text:F8224246 loc_F8224246: ; CODE XREF: KavNtTerminateProcess+5Ej
.text:F8224246 cmp esi, 0FFFFFFFFh
.text:F8224249 jnz short loc_F8224255
.text:F822424B mov edx, [esp+5Ch+ProcessData.TargetProcessId]
.text:F822424F push edx
.text:F8224250 call sub_F8226710
.text:F8224255
.text:F8224255 loc_F8224255: ; CODE XREF: KavNtTerminateProcess+89j
.text:F8224255 lea eax, [esp+5Ch+ProcessData]
.text:F8224259 push ebx ; int
.text:F822425A push eax ; ProcessData
.text:F822425B call KavCheckTerminateProcess
.text:F8224260 cmp eax, 7
.text:F8224263 jz short loc_F822427D
.text:F8224265 cmp eax, 1
.text:F8224268 jz short loc_F822427D
.text:F822426A cmp eax, ebx
.text:F822426C jz short loc_F822427D
.text:F822426E mov esi, STATUS_ACCESS_DENIED
.text:F8224273 mov eax, esi
.text:F8224275 pop esi
.text:F8224276 pop ebx
.text:F8224277 add esp, 54h
.text:F822427A retn 8
.text:F822427D ; ---------------------------------------------------------------------------
.text:F822427D
.text:F822427D loc_F822427D: ; CODE XREF: KavNtTerminateProcess+A3j
.text:F822427D ; KavNtTerminateProcess+A8j ...
.text:F822427D mov eax, [esp+5Ch+ProcessData.TargetProcessId]
.text:F8224281 cmp eax, 1000h
.text:F8224286 jnb short loc_F8224296
.text:F8224288 mov dword_F8228460[eax*8], ebx
.text:F822428F mov byte_F8228464[eax*8], bl
.text:F8224296
.text:F8224296 loc_F8224296: ; CODE XREF: KavNtTerminateProcess+C6j
.text:F8224296 push eax
.text:F8224297 call sub_F82134D0
.text:F822429C mov ecx, [esp+5Ch+ProcessData.TargetProcessId]
.text:F82242A0 push ecx
.text:F82242A1 call sub_F8221F70
.text:F82242A6 mov edx, [esp+5Ch+ExitStatus]
.text:F82242AA push edx
.text:F82242AB push esi
.text:F82242AC call OrigNtTerminateProcess
.text:F82242B2 mov esi, eax
.text:F82242B4 lea eax, [esp+5Ch+ProcessData]
.text:F82242B8 push 1 ; int
.text:F82242BA push eax ; ProcessData
.text:F82242BB mov [esp+64h+var_4], esi
.text:F82242BF call KavCheckTerminateProcess
.text:F82242C4 mov eax, esi
.text:F82242C6 pop esi
.text:F82242C7 pop ebx
.text:F82242C8 add esp, 54h
.text:F82242CB retn 8
.text:F82242CE ; ---------------------------------------------------------------------------
.text:F82242CE
.text:F82242CE Ret_KavNtTerminateProcess: ; CODE XREF: KavNtTerminateProcess+16j
.text:F82242CE ; KavNtTerminateProcess+1Ej
.text:F82242CE mov ecx, [esp+5Ch+ExitStatus]
.text:F82242D2 push ecx
.text:F82242D3 push esi
.text:F82242D4 call OrigNtTerminateProcess
.text:F82242DA pop esi
.text:F82242DB pop ebx
.text:F82242DC add esp, 54h
.text:F82242DF retn 8
.text:F82242DF KavNtTerminateProcess endp
该系统服务钩子的目的也是“不可靠的”。该钩子函数的功能是保证某些KAV进程不会被终止,即使是合法的系统管理员也无法终止KAV进程——这种功能更像是恶意软件,比如Rootkits,而不是商用软件应该有的功能。对此的合理解释可能是,它想要阻止病毒删除扫描病毒的进程,尽管如此,我们还是想知道如果KAV实时扫描机制真的如广告所说的那么有用话,那么这种情况发生的可能性究竟有多高?
另外,KAV似乎只在进程与系统服务的关联终止前才跟踪其状态。而恰当的方法应当是通过PsSetCreateProcessNotifyRoutine这个有记录的内核功能来进行,该功能允许驱动程序注册那些进程创建和进程退出时被调用的callback函数。
欢迎访问最专业的网吧论坛,无盘论坛,网吧经营,网咖管理,网吧专业论坛https://bbs.txwb.com |
关注天下网吧微信,了解网吧网咖经营管理,安装维护:
本文来源:赛迪网 作者:佚名