国外安全技术高手谈卡巴斯基存在隐患

互联网 | 编辑: 杨剑锋 2007-06-18 09:30:00转载 一键看全文

内核对象类型不恰当验证

内核对象类型不恰当验证

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函数。

提示:试试键盘 “← →” 可以实现快速翻页 

一键看全文

本文导航

相关阅读

每日精选

点击查看更多

首页 手机 数码相机 笔记本 游戏 DIY硬件 硬件外设 办公中心 数字家电 平板电脑