mirror of https://github.com/fdiskyou/Zines.git
2071 lines
103 KiB
Plaintext
2071 lines
103 KiB
Plaintext
What Were They Thinking?
|
||
Anti-Virus Software Gone Wrong
|
||
Skywing
|
||
skywing@valhallalegends.com
|
||
|
||
0. Foreword
|
||
|
||
Abstract: Anti-virus software is becoming more and more prevalent on end-user
|
||
computers today. Many major computer vendors (such as Dell) bundle anti-virus
|
||
software and other personal security suites in the default configuration of
|
||
newly-sold computer systems. As a result, it is becoming increasingly important
|
||
that anti-virus software be well-designed, secure by default, and interoperable
|
||
with third-party applications. Software that is installed and running by
|
||
default constitutes a prime target for attack and, as such, it is especially
|
||
important that said software be designed with security and interoperability in
|
||
mind. In particular, this article provides examples of issues found in
|
||
well-known anti-virus products. These issues range from not properly validating
|
||
input from an untrusted source (especially within the context of a kernel
|
||
driver) to failing to conform to API contracts when hooking or implementing an
|
||
intermediary between applications and the underlying APIs upon which they rely.
|
||
For popular software, or software that is installed by default, errors of this
|
||
sort can become a serious problem to both system stability and security. Beyond
|
||
that, it can impact the ability of independent software vendors to deploy
|
||
functioning software on end-user systems.
|
||
|
||
1. Introduction
|
||
|
||
In today's computing environment, computer security is becoming a more and more
|
||
important role. The Internet poses unique dangers to networked computers, as
|
||
threats such as viruses, worms, and other malicious software become more and
|
||
more common.
|
||
|
||
As a result, there has been a shift towards including personal security
|
||
software on most new computers sold today, such as firewall software and
|
||
anti-virus software. Many new computers are operated and administered by
|
||
individuals who are not experienced or savvy with the administration of a
|
||
secure system, and as such rely solely on the protection provided by a firewall
|
||
or anti-virus security suite.
|
||
|
||
Given this, one would expect that firewall, anti-virus, and other personal
|
||
security software would be high quality - after all, for many individuals,
|
||
firewall and anti-virus software are the first (and all-too-often only) line
|
||
of defense.
|
||
|
||
Unfortunately, though, most common anti-virus and personal firewall software is
|
||
full of defects that can at best make it very difficult to interoperate with
|
||
(which turns out to be a serious problem for most software vendors, given how
|
||
common anti-virus and firewall software is), and at worst compromise the very
|
||
system security they advertise to protect.
|
||
|
||
This article discusses two personal security software packages that suffer from
|
||
problems that make it difficult to interoperate with the software, or even
|
||
compromise system security, all due to shortcuts and unsafe assumptions made by
|
||
the original developers.
|
||
|
||
- Kaspersky Internet Security Suite 5.0
|
||
- McAfee Internet Security Suite 2006
|
||
|
||
Both of these software packages include several personal security programs,
|
||
including firewall and anti-virus software.
|
||
|
||
|
||
2. The problem: Kaspersky Internet Security Suite 5.0
|
||
|
||
Kaspersky ships a personal security software suite known as Kaspersky Internet
|
||
Security Suite 5.0. This package includes various personal security software
|
||
programs, including a firewall and anti-virus software.
|
||
|
||
Kaspersky's anti-virus software is the primarily focus of this article. Like
|
||
many other anti-virus software, Kaspersky Anti-Virus provides both manual and
|
||
real-time scanning capabilities.
|
||
|
||
Kaspersky's anti-virus system (KAV) employs various unsafe techniques in its
|
||
kernel mode components, which may lead to a compromise of system security.
|
||
|
||
2.1. Patching system services at runtime.
|
||
|
||
Although KAV appears to use a filesystem filter, the standard Windows mechanism
|
||
for intercepting accesses to files (specifically designed for applications like
|
||
anti-virus software), the implementors also used a series of API-level function
|
||
hooks to intercept various file accesses. Performing function hooking in
|
||
kernel mode is a dangerous proposition; one must be very careful to fully
|
||
validate all parameters if a function could be called from user mode (otherwise
|
||
system security could be compromised by a malicious unprivileged program).
|
||
Additionally, it is generally not safe to remove code hooks in kernel mode as
|
||
it is difficult to prove that no threads will be running a particular code
|
||
region in order to unhook without risking bringing down the system. KAV also
|
||
hooks several other system services in a misguided attempt to "protect" its
|
||
processes from debuggers and process termination.
|
||
|
||
Unfortunately, the KAV programmers did not properly validate parameters passed
|
||
to hooked system calls, opening holes that, at the very least, allow unprivileged
|
||
user mode programs to bring down the system, and may even allow local privilege
|
||
escalation (though the author has not spent the time necessary to prove whether
|
||
such is possible).
|
||
|
||
KAV hooks the following system services (easily discoverable in WinDbg by
|
||
comparing nt!KeServiceDescriptorTableShadow on a system with KAV loaded with a
|
||
clean system:
|
||
|
||
|
||
kd> dps poi ( nt!KeServiceDescriptorTableShadow ) l dwo ( nt!KeServiceDescriptorTableShadow + 8 )
|
||
8191c9c8 805862de nt!NtAcceptConnectPort
|
||
8191c9cc 8056fded nt!NtAccessCheck
|
||
.
|
||
.
|
||
.
|
||
8191ca2c f823fd00 klif!KavNtClose
|
||
.
|
||
.
|
||
.
|
||
8191ca84 f823fa20 klif!KavNtCreateProcess
|
||
8191ca88 f823fb90 klif!KavNtCreateProcessEx
|
||
8191ca8c 80647b59 nt!NtCreateProfile
|
||
8191ca90 f823fe40 klif!KavNtCreateSection
|
||
8191ca94 805747cf nt!NtCreateSemaphore
|
||
8191ca98 8059d4db nt!NtCreateSymbolicLinkObject
|
||
8191ca9c f8240630 klif!KavNtCreateThread
|
||
8191caa0 8059a849 nt!NtCreateTimer
|
||
.
|
||
.
|
||
.
|
||
8191cbb0 f823f7b0 klif!KavNtOpenProcess
|
||
.
|
||
.
|
||
.
|
||
8191cc24 f82402f0 klif!KavNtQueryInformationFile
|
||
.
|
||
.
|
||
.
|
||
8191cc7c f8240430 klif!KavNtQuerySystemInformation
|
||
.
|
||
.
|
||
.
|
||
8191cd00 f82405e0 klif!KavNtResumeThread
|
||
.
|
||
.
|
||
.
|
||
8191cd58 f82421f0 klif!KavNtSetInformationProcess
|
||
.
|
||
.
|
||
.
|
||
8191cdc0 f8240590 klif!KavNtSuspendThread
|
||
.
|
||
.
|
||
.
|
||
8191cdcc f82401c0 klif!KavNtTerminateProcess
|
||
|
||
|
||
|
||
Additionally, KAV attempts to create several entirely new system services as a
|
||
shortcut for calling kernel mode by patching the service descriptor table.
|
||
This is certainly not the preferred mechanism to allow a user mode program to
|
||
communicate with a driver; the programmers should have used the conventional
|
||
IOCTL interface, which avoids the pitfalls of patching kernel structures at
|
||
runtime and having to deal with other inconveniences such as system service
|
||
ordinals changing from OS release to OS release.
|
||
|
||
2.2. Improper validation of user mode pointers, assuming the size of the kernel
|
||
address space.
|
||
|
||
Many of the hooks that KAV installs (and even the custom system services)
|
||
suffer from flaws that are detrimental to the operation of the system.
|
||
|
||
For instance, KAV's modified NtOpenProcess attempts to determine if a user
|
||
address is valid by comparing it to the hardcoded value 0x7FFF0000. On most
|
||
x86 Windows systems, this address is below the highest user address (typically
|
||
0x7FFEFFFF). However, hardcoding the size of the kernel address space is not a
|
||
very good idea; there is a boot parameter `/3GB' that can be set in boot.ini in
|
||
order to change the default address space split of 2GB kernel and 2GB user to
|
||
1GB kernel and 3GB user. If a system with KAV is configured with /3GB, it is
|
||
expected that anything that calls NtOpenProcess (such as the win32 OpenProcess)
|
||
may randomly fail if parameter addresses are located above the first 2GB of the
|
||
user address space:
|
||
|
||
.text:F82237B0 ; NTSTATUS __stdcall KavNtOpenProcess(PHANDLE ProcessHandle,ACCESS_MASK DesiredAccess,POBJECT_ATTRIBUTES ObjectAttributes,PCLIENT_ID ClientId)
|
||
.text:F82237B0 KavNtOpenProcess proc near ; DATA XREF: sub_F82249D0+BFo
|
||
.
|
||
.
|
||
.
|
||
.text:F8223800 cmp eax, 7FFF0000h ; eax = ClientId
|
||
.text:F8223805 jbe short loc_F822380D
|
||
.text:F8223807
|
||
.text:F8223807 loc_F8223807: ; CODE XREF: KavNtOpenProcess+4Ej
|
||
.text:F8223807 call ds:ExRaiseAccessViolation
|
||
|
||
|
||
The proper way to perform this validation would have been to use the documented
|
||
ProbeForRead function with a SEH frame, which will automatically raise an
|
||
access violation if the address is not a valid user address.
|
||
|
||
Additionally, many of KAV's custom system services do not properly validate
|
||
user mode pointer arguments, which could be used to bring down the system:
|
||
|
||
|
||
.text:F8222BE0 ; int __stdcall KAVService10(int,PVOID OutputBuffer,int)
|
||
.text:F8222BE0 KAVService10 proc near ; DATA XREF: .data:F8227D14o
|
||
.text:F8222BE0
|
||
.text:F8222BE0 arg_0 = dword ptr 4
|
||
.text:F8222BE0 OutputBuffer = dword ptr 8
|
||
.text:F8222BE0 arg_8 = dword ptr 0Ch
|
||
.text:F8222BE0
|
||
.text:F8222BE0 mov edx, [esp+OutputBuffer]
|
||
.text:F8222BE4 push esi
|
||
.text:F8222BE5 mov esi, [esp+4+arg_8]
|
||
.text:F8222BE9 lea ecx, [esp+4+arg_8]
|
||
.text:F8222BED push ecx ; int
|
||
.text:F8222BEE mov eax, [esi] ; Unvalidated user mode pointer access
|
||
.text:F8222BF0 mov [esp+8+arg_8], eax
|
||
.text:F8222BF4 push eax ; OutputBufferLength
|
||
.text:F8222BF5 mov eax, [esp+0Ch+arg_0]
|
||
.text:F8222BF9 push edx ; OutputBuffer
|
||
.text:F8222BFA push eax ; int
|
||
.text:F8222BFB call sub_F821F9A0 ; This routine internally assumes that all pointer parameters given are valid.
|
||
.text:F8222C00 mov edx, [esi]
|
||
.text:F8222C02 mov ecx, [esp+4+arg_8]
|
||
.text:F8222C06 cmp ecx, edx
|
||
.text:F8222C08 jbe short loc_F8222C13
|
||
.text:F8222C0A mov eax, 0C0000173h
|
||
.text:F8222C0F pop esi
|
||
.text:F8222C10 retn 0Ch
|
||
.text:F8222C13 ; ---------------------------------------------------------------------------
|
||
.text:F8222C13
|
||
.text:F8222C13 loc_F8222C13: ; CODE XREF: KAVService10+28j
|
||
.text:F8222C13 mov [esi], ecx
|
||
.text:F8222C15 pop esi
|
||
.text:F8222C16 retn 0Ch
|
||
.text:F8222C16 KAVService10 endp
|
||
|
||
|
||
.text:F8222C20 KAVService11 proc near ; DATA XREF: .data:F8227D18o
|
||
.text:F8222C20
|
||
.text:F8222C20 arg_0 = dword ptr 4
|
||
.text:F8222C20 arg_4 = dword ptr 8
|
||
.text:F8222C20 arg_8 = dword ptr 0Ch
|
||
.text:F8222C20
|
||
.text:F8222C20 mov edx, [esp+arg_4]
|
||
.text:F8222C24 push esi
|
||
.text:F8222C25 mov esi, [esp+4+arg_8]
|
||
.text:F8222C29 lea ecx, [esp+4+arg_8]
|
||
.text:F8222C2D push ecx
|
||
.text:F8222C2E mov eax, [esi] ; Unvalidated user mode pointer access
|
||
.text:F8222C30 mov [esp+8+arg_8], eax
|
||
.text:F8222C34 push eax
|
||
.text:F8222C35 mov eax, [esp+0Ch+arg_0]
|
||
.text:F8222C39 push edx
|
||
.text:F8222C3A push eax
|
||
.text:F8222C3B call sub_F8214CE0 ; This routine internally assumes that all pointer parameters given are valid.
|
||
.text:F8222C40 test eax, eax
|
||
.text:F8222C42 jnz short loc_F8222C59
|
||
.text:F8222C44 mov ecx, [esp+4+arg_8]
|
||
.text:F8222C48 mov edx, [esi]
|
||
.text:F8222C4A cmp ecx, edx
|
||
.text:F8222C4C jbe short loc_F8222C57
|
||
.text:F8222C4E mov eax, STATUS_INVALID_BLOCK_LENGTH
|
||
.text:F8222C53 pop esi
|
||
.text:F8222C54 retn 0Ch
|
||
.text:F8222C57 ; ---------------------------------------------------------------------------
|
||
.text:F8222C57
|
||
.text:F8222C57 loc_F8222C57: ; CODE XREF: KAVService11+2Cj
|
||
.text:F8222C57 mov [esi], ecx
|
||
.text:F8222C59
|
||
.text:F8222C59 loc_F8222C59: ; CODE XREF: KAVService11+22j
|
||
.text:F8222C59 pop esi
|
||
.text:F8222C5A retn 0Ch
|
||
.text:F8222C5A KAVService11 endp
|
||
|
||
|
||
2.3. Improper validation of user mode structures and pointers, hiding threads
|
||
from user mode.
|
||
|
||
KAV's errors with hooking do not end with NtOpenProcess, however. One of the
|
||
system services KAV hooks is NtQuerySystemInformation, which is modified to
|
||
sometimes truncate a thread listing from certain processes when the
|
||
SystemProcessesAndThreads information class is requested. This is the
|
||
underlying mechanism for user mode to receive a process and thread listing of
|
||
all programs running in the system, and in effect provides a means for KAV to
|
||
hide threads from user mode. The very fact that this code exists at all in KAV
|
||
is curious; hiding running code from user mode is typically something that is
|
||
associated with rootkits and not anti-virus software.
|
||
|
||
Besides the potentially abusive behavior of hiding running code, this hook
|
||
contains several security flaws:
|
||
|
||
1. It uses the user mode output buffer from NtQuerySystemInformation after it
|
||
has been filled by the actual kernel implementation, but it does not guard
|
||
against a malicious user mode program modifying this buffer or even freeing
|
||
it. There is no SEH frame wrapping this function, so a user mode program
|
||
could cause KAV to touch freed memory.
|
||
|
||
2. There is no validation of offsets within the returned output buffer to
|
||
ensure that offsets do not refer to memory outside of the output buffer.
|
||
This is problematic, because the returned data structure is actually a list
|
||
of sub-structures that must be walked by adding an offset supplied as part
|
||
of a particular substructure to the address of that substructure in order to
|
||
reach the next substructure. Such an offset could be modified by user mode
|
||
to actually point into kernel memory. Because the hook then sometimes
|
||
writes data into what it believes is the user mode output buffer, this is an
|
||
interesting avenue to explore for gaining kernel privileges from an
|
||
unprivileged user mode function.
|
||
|
||
.text:F8224430 ; NTSTATUS __stdcall KavNtQuerySystemInformation(SYSTEM_INFORMATION_CLASS SystemInformationClass,PVOID SystemInformation,ULONG SystemInformationLength,PULONG ReturnLength)
|
||
.text:F8224430 KavNtQuerySystemInformation proc near ; DATA XREF: sub_F82249D0+17Bo
|
||
.text:F8224430
|
||
.text:F8224430 var_10 = dword ptr -10h
|
||
.text:F8224430 var_C = dword ptr -0Ch
|
||
.text:F8224430 var_8 = dword ptr -8
|
||
.text:F8224430 SystemInformationClass= dword ptr 4
|
||
.text:F8224430 SystemInformation= dword ptr 8
|
||
.text:F8224430 SystemInformationLength= dword ptr 0Ch
|
||
.text:F8224430 ReturnLength = dword ptr 10h
|
||
.text:F8224430 arg_24 = dword ptr 28h
|
||
.text:F8224430
|
||
.text:F8224430 mov eax, [esp+ReturnLength]
|
||
.text:F8224434 mov ecx, [esp+SystemInformationLength]
|
||
.text:F8224438 mov edx, [esp+SystemInformation]
|
||
.text:F822443C push ebx
|
||
.text:F822443D push ebp
|
||
.text:F822443E push esi
|
||
.text:F822443F mov esi, [esp+0Ch+SystemInformationClass]
|
||
.text:F8224443 push edi
|
||
.text:F8224444 push eax
|
||
.text:F8224445 push ecx
|
||
.text:F8224446 push edx
|
||
.text:F8224447 push esi
|
||
.text:F8224448 call OrigNtQuerySystemInformation
|
||
.text:F822444E mov edi, eax
|
||
.text:F8224450 cmp esi, SystemProcessesAndThreadsInformation ;
|
||
.text:F8224450 ; Not the process / thread list API?
|
||
.text:F8224450 ; Return to caller
|
||
.text:F8224453 mov [esp+10h+ReturnLength], edi
|
||
.text:F8224457 jnz ret_KavNtQuerySystemInformation
|
||
.text:F822445D xor ebx, ebx
|
||
.text:F822445F cmp edi, ebx ;
|
||
.text:F822445F ; Nothing returned?
|
||
.text:F822445F ; Return to caller
|
||
.text:F8224461 jl ret_KavNtQuerySystemInformation
|
||
.text:F8224467 push ebx
|
||
.text:F8224468 push 9
|
||
.text:F822446A push 8
|
||
.text:F822446C call sub_F8216730
|
||
.text:F8224471 test al, al
|
||
.text:F8224473 jz ret_KavNtQuerySystemInformation
|
||
.text:F8224479 mov ebp, g_KavDriverData
|
||
.text:F822447F mov ecx, [ebp+0Ch]
|
||
.text:F8224482 lea edx, [ebp+48h]
|
||
.text:F8224485 inc ecx
|
||
.text:F8224486 mov [ebp+0Ch], ecx
|
||
.text:F8224489 mov ecx, ebp
|
||
.text:F822448B call ds:ExInterlockedPopEntrySList
|
||
.text:F8224491 mov esi, eax
|
||
.text:F8224493 cmp esi, ebx
|
||
.text:F8224495 jnz short loc_F82244B7
|
||
.text:F8224497 mov eax, [ebp+10h]
|
||
.text:F822449A mov ecx, [ebp+24h]
|
||
.text:F822449D mov edx, [ebp+1Ch]
|
||
.text:F82244A0 inc eax
|
||
.text:F82244A1 mov [ebp+10h], eax
|
||
.text:F82244A4 mov eax, [ebp+20h]
|
||
.text:F82244A7 push eax
|
||
.text:F82244A8 push ecx
|
||
.text:F82244A9 push edx
|
||
.text:F82244AA call [ebp+arg_24]
|
||
.text:F82244AD mov esi, eax
|
||
.text:F82244AF cmp esi, ebx
|
||
.text:F82244B1 jz ret_KavNtQuerySystemInformation
|
||
.text:F82244B7
|
||
.text:F82244B7 loc_F82244B7: ; CODE XREF: KavNtQuerySystemInformation+65j
|
||
.text:F82244B7 mov edi, [esp+10h+SystemInformation]
|
||
.text:F82244BB mov dword ptr [esi], 8
|
||
.text:F82244C1 mov dword ptr [esi+4], 9
|
||
.text:F82244C8 mov [esi+8], ebx
|
||
.text:F82244CB mov [esi+34h], ebx
|
||
.text:F82244CE mov dword ptr [esi+3Ch], 1
|
||
.text:F82244D5 mov [esi+10h], bl
|
||
.text:F82244D8 mov [esi+30h], ebx
|
||
.text:F82244DB mov [esi+0Ch], ebx
|
||
.text:F82244DE mov [esi+38h], ebx
|
||
.text:F82244E1 mov ebp, 13h
|
||
.text:F82244E6
|
||
.text:F82244E6 LoopThreadProcesses: ; CODE XREF: KavNtQuerySystemInformation+ECj
|
||
.text:F82244E6 mov dword ptr [esi+40h], 4 ;
|
||
.text:F82244E6 ; Loop through the returned list of processes and threads.
|
||
.text:F82244E6 ; For each process, we shall check to see if it is a
|
||
.text:F82244E6 ; special (protected) process. If so, then we might
|
||
.text:F82244E6 ; decide to remove its threads from the listing returned
|
||
.text:F82244E6 ; by setting the thread count to zero.
|
||
.text:F82244ED mov [esi+48h], ebx
|
||
.text:F82244F0 mov [esi+44h], ebp
|
||
.text:F82244F3 mov eax, [edi+SYSTEM_PROCESSES.ProcessId]
|
||
.text:F82244F6 push ebx
|
||
.text:F82244F7 push esi
|
||
.text:F82244F8 mov [esi+4Ch], eax
|
||
.text:F82244FB call KavCheckProcess
|
||
.text:F8224500 cmp eax, 7
|
||
.text:F8224503 jz short CheckNextThreadProcess
|
||
.text:F8224505 cmp eax, 1
|
||
.text:F8224508 jz short CheckNextThreadProcess
|
||
.text:F822450A cmp eax, ebx
|
||
.text:F822450C jz short CheckNextThreadProcess
|
||
.text:F822450E mov [edi+SYSTEM_PROCESSES.ThreadCount], ebx ; Zero thread count out (hide process threads)
|
||
.text:F8224511
|
||
.text:F8224511 CheckNextThreadProcess: ; CODE XREF: KavNtQuerySystemInformation+D3j
|
||
.text:F8224511 ; KavNtQuerySystemInformation+D8j ...
|
||
.text:F8224511 mov eax, [edi+SYSTEM_PROCESSES.NextEntryDelta]
|
||
.text:F8224513 cmp eax, ebx
|
||
.text:F8224515 setz cl
|
||
.text:F8224518 add edi, eax
|
||
.text:F822451A cmp cl, bl
|
||
.text:F822451C jz short LoopThreadProcesses
|
||
|
||
|
||
2.4. Improper validation of kernel object types.
|
||
|
||
Windows exposes many kernel features through a series of "kernel objects",
|
||
which may be acted upon by user mode through the user of handles. Handles are
|
||
integral values that are translated by the kernel into pointers to a particular
|
||
object upon which something (typically a system service) interacts with on
|
||
behalf of a caller. All objects share the same handle namespace.
|
||
|
||
Because of this handle namespace sharing between objects of different types,
|
||
one of the jobs of a system service inspecting a handle is to verify that the
|
||
object that it refers to is of the expected type. This is accomplished by an
|
||
object manager routine ObReferenceObjectByHandle, which performs the
|
||
translation of handles to object pointers and does an optional built-in type
|
||
check by comparing a type field in the standard object header to a passed in
|
||
type.
|
||
|
||
Since KAV hooks system services, in inevitably must deal with kernel handles.
|
||
Unfortunately, it does not do so correctly. In some cases, it does not ensure
|
||
that a handle refers to an object of a particular type before using the object
|
||
pointer, which will result in corruption or a system crash if a handle of the
|
||
wrong type is passed to a system service.
|
||
|
||
One such case is the KAV NtResumeThread hook, which attempts to track the state
|
||
of running threads in the system. In this particular case, it does not seem
|
||
possible for user mode to crash the system by passing an object of the wrong
|
||
type as the returned object pointer, because it is simply used as a key in a
|
||
lookup table that is prepopulated with thread object pointers. KAV also hooks
|
||
NtSuspendThread for similar purposes, and this hook has the same problem with
|
||
the validation of object handle types.
|
||
|
||
|
||
.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
|
||
|
||
|
||
Not all of KAV's hooks are so fortunate, however. The NtTerminateProcess hook
|
||
that KAV installs looks into the body of the object referred to by the process
|
||
handle parameter of the function in order to determine the name of the process
|
||
being terminated. However, KAV fails to validate that the object handle given
|
||
by user mode really refers to a process object.
|
||
|
||
This is unsafe for several reasons, which may be well known to the reader if
|
||
one is experienced with Windows kernel programming.
|
||
|
||
1. The kernel process structure definition (EPROCESS) changes frequently from
|
||
OS release to OS release, and even between service packs. As a result, it
|
||
is not generally safe to access this structure directly.
|
||
|
||
2. Because KAV does not perform proper type checking, it is possible to pass an
|
||
object handle to a different kernel object - say, a mutex - which may cause
|
||
KAV to bring down the system because the internal object structures of a
|
||
mutex (or any other kernel object) are not compatible with that of a process
|
||
object.
|
||
|
||
KAV attempts to work around the first problem by attempting to discover the
|
||
offset of the member in the EPROCESS structure that contains the process name
|
||
at runtime. The algorithm used is to scan forward one byte at a time from the
|
||
start of the process object pointer until a sequence of bytes identifying the
|
||
name of the initial system process is discovered. (This routine is called in
|
||
the context of the initial system process).
|
||
|
||
|
||
.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
|
||
|
||
|
||
Given a handle to an object of the wrong type, KAV will read from the returned
|
||
object body pointer in an attempt to determine the name of the process being
|
||
destroyed. This will typically run off the end of the structure for an object
|
||
that is not a process object (the Process object is very large compared to some
|
||
objects, such as a Mutex object, and the offset of the process name within this
|
||
structure is typically several hundred bytes or more). It is expected that
|
||
this will cause the system to crash if a bad handle is passed to
|
||
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
|
||
|
||
|
||
The whole purpose of this particular system service hook is "shady" as well.
|
||
The hook prevents certain KAV processes from being terminated, even by a
|
||
legitimate computer administrator - something that is once again typically
|
||
associated with malicious software such as rootkits rather than commercial
|
||
software applications. One possible explanation is to attempt to prevent
|
||
viruses from terminating the virus scanner processes itself, although one
|
||
wonders how much of a concern this would be if KAV's real-time scanning
|
||
mechanisms really do work as advertised.
|
||
|
||
Additionally, KAV appears to do some state tracking just before the process is
|
||
terminated with this system service hook. The proper way to do this would have
|
||
been through PsSetCreateProcessNotifyRoutine, a documented kernel function that
|
||
allows drivers to register a callback that is called on process creation and
|
||
process exit.
|
||
|
||
|
||
2.5. Patching non-exported, non-system-service kernel functions.
|
||
|
||
KAV's kernel patching is not limited to just system services, however. One of
|
||
the most dangerous hooks that KAV installs is one in the middle of the
|
||
nt!SwapContext function, which is neither exported nor a system service (and
|
||
thus has reliable mechanism to be detected by driver code, other than code
|
||
fingerprinting). nt!SwapContext is called by the kernel on every context
|
||
switch in order to perform some internal bookkeeping tasks.
|
||
|
||
Patching such a critical, non-exported kernel function with a mechanism as
|
||
unreliable as blind code fingerprinting is, in the author's opinion, not a
|
||
particularly good idea. To make matters worse, KAV actually modifies code in
|
||
the middle of nt!SwapContext instead of patching the start of the function, and
|
||
as such makes assumptions about the internal register and stack usage of this
|
||
kernel function.
|
||
|
||
|
||
kd> u nt!SwapContext
|
||
nt!SwapContext:
|
||
804db924 0ac9 or cl,cl
|
||
804db926 26c6462d02 mov byte ptr es:[esi+0x2d],0x2
|
||
804db92b 9c pushfd
|
||
804db92c 8b0b mov ecx,[ebx]
|
||
804db92e e9dd69d677 jmp klif!KavSwapContext (f8242310)
|
||
|
||
|
||
The unmodified nt!SwapContext has code that runs along the lines of this:
|
||
|
||
|
||
lkd> u nt!SwapContext
|
||
nt!SwapContext:
|
||
80540ab0 0ac9 or cl,cl
|
||
80540ab2 26c6462d02 mov byte ptr es:[esi+0x2d],0x2
|
||
80540ab7 9c pushfd
|
||
80540ab8 8b0b mov ecx,[ebx]
|
||
80540aba 83bb9409000000 cmp dword ptr [ebx+0x994],0x0
|
||
80540ac1 51 push ecx
|
||
80540ac2 0f8535010000 jne nt!SwapContext+0x14d (80540bfd)
|
||
80540ac8 833d0ca0558000 cmp dword ptr [nt!PPerfGlobalGroupMask (8055a00c)],0x0
|
||
|
||
|
||
This is an extremely dangerous patching operation to make, for several reasons:
|
||
|
||
1. nt!SwapContext is a *very* hot code path, as it is called on every single
|
||
context switch. Therefore, patching it at runtime without running a non-trivial
|
||
risk of bringing down the system is very difficult, especially on
|
||
multiprocessor systems. KAV attempts to solve the synchronization problems
|
||
relating to patching this function on uniprocessor systems by disabling
|
||
interrupts entirely, but this approach will not work reliably on
|
||
multiprocessor systems. KAV makes no attempt to address this problem on
|
||
multiprocessor systems and puts them at the risk of randomly failing on boot
|
||
during KAV's patching.
|
||
|
||
2. Reliably locating this function and making assumptions about the register
|
||
and stack usage (and instruction layout) across all released and future
|
||
Windows versions is a practical impossibility, and yet KAV attempts to do
|
||
just this. This puts KAV customers at the mercy of the next Windows update,
|
||
which may cause their systems to crash on boot because KAV's hooking code
|
||
makes an assumption that has been invalidated about the context-switching
|
||
process.
|
||
|
||
|
||
Additionally, in order to perform code patching on the kernel, KAV adjusts the
|
||
page protections of kernel code to be writable by altering PTE attributes
|
||
directly instead of using documented functions (which would have proper locking
|
||
semantics for accessing internal memory management structures).
|
||
|
||
|
||
KAV nt!SwapContext patching:
|
||
|
||
|
||
.text:F82264EA mov eax, 90909090h ; Build the code to be written to nt!SwapContext
|
||
.text:F82264EF mov [ebp+var_38], eax
|
||
.text:F82264F2 mov [ebp+var_34], eax
|
||
.text:F82264F5 mov [ebp+var_30], ax
|
||
.text:F82264F9 mov byte ptr [ebp+var_38], 0E9h
|
||
.text:F82264FD mov ecx, offset KavSwapContext
|
||
.text:F8226502 sub ecx, ebx
|
||
.text:F8226504 sub ecx, 5
|
||
.text:F8226507 mov [ebp+var_38+1], ecx
|
||
.text:F822650A mov ecx, [ebp+var_1C]
|
||
.text:F822650D lea edx, [ecx+ebx]
|
||
.text:F8226510 mov dword_F8228338, edx
|
||
.text:F8226516 mov esi, ebx
|
||
.text:F8226518 mov edi, offset unk_F8227DBC
|
||
.text:F822651D mov eax, ecx
|
||
.text:F822651F shr ecx, 2
|
||
.text:F8226522 rep movsd
|
||
.text:F8226524 mov ecx, eax
|
||
.text:F8226526 and ecx, 3
|
||
.text:F8226529 rep movsb
|
||
.text:F822652B lea ecx, [ebp+var_48] ; Make nt!SwapContext writable by directly accessing
|
||
.text:F822652B ; the PTEs.
|
||
.text:F822652E push ecx
|
||
.text:F822652F push 1
|
||
.text:F8226531 push ebx
|
||
.text:F8226532 call ModifyPteAttributes
|
||
.text:F8226537 test al, al
|
||
.text:F8226539 jz short loc_F8226588
|
||
.text:F822653B mov ecx, offset KavInternalSpinLock
|
||
.text:F8226540 call KavSpinLockAcquire ; Disable interrupts
|
||
.text:F8226545 mov ecx, [ebp+var_1C] ; Write to kernel code
|
||
.text:F8226548 lea esi, [ebp+var_38]
|
||
.text:F822654B mov edi, ebx
|
||
.text:F822654D mov edx, ecx
|
||
.text:F822654F shr ecx, 2
|
||
.text:F8226552 rep movsd
|
||
.text:F8226554 mov ecx, edx
|
||
.text:F8226556 and ecx, 3
|
||
.text:F8226559 rep movsb
|
||
.text:F822655B mov edx, eax
|
||
.text:F822655D mov ecx, offset KavInternalSpinLock
|
||
.text:F8226562 call KavSpinLockRelease ; Reenable interrupts
|
||
.text:F8226567 lea eax, [ebp+var_48] ; Restore the original PTE attributes.
|
||
.text:F822656A push eax
|
||
.text:F822656B mov ecx, [ebp+var_48]
|
||
.text:F822656E push ecx
|
||
.text:F822656F push ebx
|
||
.text:F8226570 call ModifyPteAttributes
|
||
.text:F8226575 mov al, 1
|
||
.text:F8226577 mov ecx, [ebp+var_10]
|
||
.text:F822657A mov large fs:0, ecx
|
||
.text:F8226581 pop edi
|
||
.text:F8226582 pop esi
|
||
.text:F8226583 pop ebx
|
||
.text:F8226584 mov esp, ebp
|
||
.text:F8226586 pop ebp
|
||
.text:F8226587 retn
|
||
|
||
|
||
KavSpinLockAcquire subroutine (disables interrupts):
|
||
|
||
|
||
.text:F8221240 KavSpinLockAcquire proc near ; CODE XREF: sub_F8225690+D7p
|
||
.text:F8221240 ; sub_F8225D50+8Cp ...
|
||
.text:F8221240 pushf
|
||
.text:F8221241 pop eax
|
||
.text:F8221242
|
||
.text:F8221242 loc_F8221242: ; CODE XREF: KavSpinLockAcquire+13j
|
||
.text:F8221242 cli
|
||
.text:F8221243 lock bts dword ptr [ecx], 0
|
||
.text:F8221248 jb short loc_F822124B
|
||
.text:F822124A retn
|
||
.text:F822124B ; ---------------------------------------------------------------------------
|
||
.text:F822124B
|
||
.text:F822124B loc_F822124B: ; CODE XREF: KavSpinLockAcquire+8j
|
||
.text:F822124B push eax
|
||
.text:F822124C popf
|
||
.text:F822124D
|
||
.text:F822124D loc_F822124D: ; CODE XREF: KavSpinLockAcquire+17j
|
||
.text:F822124D test dword ptr [ecx], 1
|
||
.text:F8221253 jz short loc_F8221242
|
||
.text:F8221255 pause
|
||
.text:F8221257 jmp short loc_F822124D
|
||
.text:F8221257 KavSpinLockAcquire endp
|
||
|
||
|
||
KavSpinLockRelease subroutine (reenables interrupts):
|
||
|
||
|
||
.text:F8221260 KavSpinLockRelease proc near ; CODE XREF: sub_F8225690+F2p
|
||
.text:F8221260 ; sub_F8225D50+BAp ...
|
||
.text:F8221260 mov dword ptr [ecx], 0
|
||
.text:F8221266 push edx
|
||
.text:F8221267 popf
|
||
.text:F8221268 retn
|
||
.text:F8221268 KavSpinLockRelease endp
|
||
|
||
|
||
|
||
|
||
ModifyPteAttributes subroutine:
|
||
|
||
|
||
.text:F82203C0 ModifyPteAttributes proc near ; CODE XREF: sub_F821A9D0+91p
|
||
.text:F82203C0 ; sub_F8220950+43p ...
|
||
.text:F82203C0
|
||
.text:F82203C0 var_24 = dword ptr -24h
|
||
.text:F82203C0 var_20 = byte ptr -20h
|
||
.text:F82203C0 var_1C = dword ptr -1Ch
|
||
.text:F82203C0 var_18 = dword ptr -18h
|
||
.text:F82203C0 var_10 = dword ptr -10h
|
||
.text:F82203C0 var_4 = dword ptr -4
|
||
.text:F82203C0 arg_0 = dword ptr 8
|
||
.text:F82203C0 arg_4 = byte ptr 0Ch
|
||
.text:F82203C0 arg_8 = dword ptr 10h
|
||
.text:F82203C0
|
||
.text:F82203C0 push ebp
|
||
.text:F82203C1 mov ebp, esp
|
||
.text:F82203C3 push 0FFFFFFFFh
|
||
.text:F82203C5 push offset dword_F8212180
|
||
.text:F82203CA push offset _except_handler3
|
||
.text:F82203CF mov eax, large fs:0
|
||
.text:F82203D5 push eax
|
||
.text:F82203D6 mov large fs:0, esp
|
||
.text:F82203DD sub esp, 14h
|
||
.text:F82203E0 push ebx
|
||
.text:F82203E1 push esi
|
||
.text:F82203E2 push edi
|
||
.text:F82203E3 mov [ebp+var_18], esp
|
||
.text:F82203E6 xor ebx, ebx
|
||
.text:F82203E8 mov [ebp+var_20], bl
|
||
.text:F82203EB mov esi, [ebp+arg_0]
|
||
.text:F82203EE mov ecx, esi
|
||
.text:F82203F0 call KavGetEflags
|
||
.text:F82203F5 push esi
|
||
.text:F82203F6 call KavGetPte ; This is a function pointer filled in at runtime,
|
||
.text:F82203F6 ; differing based on whether the system has PAE
|
||
.text:F82203F6 ; enabled or not.
|
||
.text:F82203FC mov edi, eax
|
||
.text:F82203FE mov [ebp+var_1C], edi
|
||
.text:F8220401 cmp edi, 0FFFFFFFFh
|
||
.text:F8220404 jz short loc_F8220458
|
||
.text:F8220406 mov [ebp+var_4], ebx
|
||
.text:F8220409 mov ecx, esi
|
||
.text:F822040B call KavGetEflags
|
||
.text:F8220410 mov eax, [edi]
|
||
.text:F8220412 test al, 1
|
||
.text:F8220414 jz short loc_F8220451
|
||
.text:F8220416 mov ecx, eax
|
||
.text:F8220418 mov [ebp+var_24], ecx
|
||
.text:F822041B cmp [ebp+arg_4], bl
|
||
.text:F822041E jz short loc_F8220429
|
||
.text:F8220420 mov eax, [ebp+var_1C]
|
||
.text:F8220423 lock or dword ptr [eax], 2
|
||
.text:F8220427 jmp short loc_F8220430
|
||
.text:F8220429 ; ---------------------------------------------------------------------------
|
||
.text:F8220429
|
||
.text:F8220429 loc_F8220429: ; CODE XREF: ModifyPteAttributes+5Ej
|
||
.text:F8220429 mov eax, [ebp+var_1C]
|
||
.text:F822042C lock and dword ptr [eax], 0FFFFFFFDh
|
||
.text:F8220430
|
||
.text:F8220430 loc_F8220430: ; CODE XREF: ModifyPteAttributes+67j
|
||
.text:F8220430 mov eax, [ebp+arg_8]
|
||
.text:F8220433 cmp eax, ebx
|
||
.text:F8220435 jz short loc_F822043C
|
||
.text:F8220437 and ecx, 2
|
||
.text:F822043A mov [eax], cl
|
||
.text:F822043C
|
||
.text:F822043C loc_F822043C: ; CODE XREF: ModifyPteAttributes+75j
|
||
.text:F822043C mov [ebp+var_20], 1
|
||
.text:F8220440 mov eax, [ebp+arg_0]
|
||
.text:F8220443 invlpg byte ptr [eax]
|
||
.text:F8220446 jmp short loc_F8220451
|
||
.text:F8220448 ; ---------------------------------------------------------------------------
|
||
.text:F8220448
|
||
.text:F8220448 loc_F8220448: ; DATA XREF: .text:F8212184o
|
||
.text:F8220448 mov eax, 1
|
||
.text:F822044D retn
|
||
.text:F822044E ; ---------------------------------------------------------------------------
|
||
.text:F822044E
|
||
.text:F822044E loc_F822044E: ; DATA XREF: .text:F8212188o
|
||
.text:F822044E mov esp, [ebp-18h]
|
||
.text:F8220451
|
||
.text:F8220451 loc_F8220451: ; CODE XREF: ModifyPteAttributes+54j
|
||
.text:F8220451 ; ModifyPteAttributes+86j
|
||
.text:F8220451 mov [ebp+var_4], 0FFFFFFFFh
|
||
.text:F8220458
|
||
.text:F8220458 loc_F8220458: ; CODE XREF: ModifyPteAttributes+44j
|
||
.text:F8220458 mov al, [ebp+var_20]
|
||
.text:F822045B mov ecx, [ebp+var_10]
|
||
.text:F822045E mov large fs:0, ecx
|
||
.text:F8220465 pop edi
|
||
.text:F8220466 pop esi
|
||
.text:F8220467 pop ebx
|
||
.text:F8220468 mov esp, ebp
|
||
.text:F822046A pop ebp
|
||
.text:F822046B retn 0Ch
|
||
.text:F822046B ModifyPteAttributes endp
|
||
|
||
|
||
2.6. Allowing user mode code to access kernel memory directly from user mode,
|
||
improper validation of user mode structures.
|
||
|
||
One of the most important principles of the kernel/user division that modern
|
||
operating systems enforce is that user mode is not allowed to directly access
|
||
kernel mode memory. This is necessary to enforce system stability, otherwise
|
||
a buggy user mode program could corrupt the kernel and bring down the whole
|
||
system.
|
||
|
||
Unfortunately, the KAV programmers appear to think that this distinction is not
|
||
really so important after all.
|
||
|
||
One of the strangest of the unsafe practicies implemented by KAV is to allow
|
||
user mode to directly call some portions of their kernel driver (within kernel
|
||
address space!) instead of just loading a user mode DLL (or otherwise loading
|
||
user mode code in the target process).
|
||
|
||
This mechanism appears to be used to inspect DLLs as they are loaded - a task
|
||
which would be much better accomplished with PsSetLoadImageNotifyRoutine.
|
||
|
||
KAV patches kernel32.dll as a new process is created, such that the export
|
||
table points all of the DLL-loading routines (e.g. LoadLibraryA) to a thunk
|
||
that calls portions of KAV's driver in kernel mode. Additionally, KAV modifes
|
||
protections on parts of its code and data sections to allow user mode read
|
||
access.
|
||
|
||
KAV sets a PsLoadImageNotifyRoutine hook to detect kernel32.dll being loaded in
|
||
order to know when to patch kernel32's export table. The author wonders why
|
||
KAV did not just do their work from within PsSetLoadImageNotifyRoutine directly
|
||
instead of going through all the trouble to allow user mode to call kernel mode
|
||
for a LoadLibrary hook.
|
||
|
||
|
||
The CheckInjectCodeForNewProcess function is called when a new process loads an
|
||
image, and checks for kernel32 being loaded. If this is the case, it will
|
||
queue an APC to the process that will perform patching.
|
||
|
||
|
||
.text:F82218B0 ; int __stdcall CheckInjectCodeForNewProcess(wchar_t *,PUCHAR ImageBase)
|
||
.text:F82218B0 CheckInjectCodeForNewProcess proc near ; CODE XREF: KavLoadImageNotifyRoutine+B5p
|
||
.text:F82218B0 ; KavDoKernel32Check+41p
|
||
.text:F82218B0
|
||
.text:F82218B0 arg_0 = dword ptr 4
|
||
.text:F82218B0 ImageBase = dword ptr 8
|
||
.text:F82218B0
|
||
.text:F82218B0 mov al, byte_F82282F9
|
||
.text:F82218B5 push esi
|
||
.text:F82218B6 test al, al
|
||
.text:F82218B8 push edi
|
||
.text:F82218B9 jz short loc_F8221936
|
||
.text:F82218BB mov eax, [esp+8+arg_0]
|
||
.text:F82218BF push offset aKernel32_dll ; "kernel32.dll"
|
||
.text:F82218C4 push eax ; wchar_t *
|
||
.text:F82218C5 call ds:_wcsicmp
|
||
.text:F82218CB add esp, 8
|
||
.text:F82218CE test eax, eax
|
||
.text:F82218D0 jnz short loc_F8221936
|
||
.text:F82218D2 mov al, g_FoundKernel32Exports
|
||
.text:F82218D7 mov edi, [esp+8+ImageBase]
|
||
.text:F82218DB test al, al
|
||
.text:F82218DD jnz short KavInitializePatchApcLabel
|
||
.text:F82218DF push edi
|
||
.text:F82218E0 call KavCheckFindKernel32Exports
|
||
.text:F82218E5 test al, al
|
||
.text:F82218E7 jz short loc_F8221936
|
||
.text:F82218E9
|
||
.text:F82218E9 KavInitializePatchApcLabel: ; CODE XREF: CheckInjectCodeForNewProcess+2Dj
|
||
.text:F82218E9 push '3SeB' ; Tag
|
||
.text:F82218EE push 30h ; NumberOfBytes
|
||
.text:F82218F0 push 0 ; PoolType
|
||
.text:F82218F2 call ds:ExAllocatePoolWithTag
|
||
.text:F82218F8 mov esi, eax
|
||
.text:F82218FA test esi, esi
|
||
.text:F82218FC jz short loc_F8221936
|
||
.text:F82218FE push edi
|
||
.text:F82218FF push 0
|
||
.text:F8221901 push offset KavPatchNewProcessApcRoutine
|
||
.text:F8221906 push offset loc_F82218A0
|
||
.text:F822190B push offset loc_F8221890
|
||
.text:F8221910 push 0
|
||
.text:F8221912 call KeGetCurrentThread
|
||
.text:F8221917 push eax
|
||
.text:F8221918 push esi
|
||
.text:F8221919 call KeInitializeApc
|
||
.text:F822191E push 0
|
||
.text:F8221920 push 0
|
||
.text:F8221922 push 0
|
||
.text:F8221924 push esi
|
||
.text:F8221925 call KeInsertQueueApc
|
||
.text:F822192B test al, al
|
||
.text:F822192D jnz short loc_F822193D
|
||
.text:F822192F push esi ; P
|
||
.text:F8221930 call ds:ExFreePool
|
||
.text:F8221936
|
||
.text:F8221936 loc_F8221936: ; CODE XREF: CheckInjectCodeForNewProcess+9j
|
||
.text:F8221936 ; CheckInjectCodeForNewProcess+20j ...
|
||
.text:F8221936 pop edi
|
||
.text:F8221937 xor al, al
|
||
.text:F8221939 pop esi
|
||
.text:F822193A retn 8
|
||
.text:F822193D ; ---------------------------------------------------------------------------
|
||
.text:F822193D
|
||
.text:F822193D loc_F822193D: ; CODE XREF: CheckInjectCodeForNewProcess+7Dj
|
||
.text:F822193D pop edi
|
||
.text:F822193E mov al, 1
|
||
.text:F8221940 pop esi
|
||
.text:F8221941 retn 8
|
||
|
||
|
||
The APC routine itself patches kernel32's export table (and generates the
|
||
thunks to call kernel mode) and adjusts PTE attributes on KAV's driver image
|
||
to allow user mode access.
|
||
|
||
|
||
.text:F8221810 KavPatchNewProcessApcRoutine proc near ; DATA XREF: CheckInjectCodeForNewProcess+51o
|
||
.text:F8221810
|
||
.text:F8221810 var_8 = dword ptr -8
|
||
.text:F8221810 var_4 = dword ptr -4
|
||
.text:F8221810 ImageBase = dword ptr 8
|
||
.text:F8221810
|
||
.text:F8221810 push ebp
|
||
.text:F8221811 mov ebp, esp
|
||
.text:F8221813 sub esp, 8
|
||
.text:F8221816 mov eax, [ebp+ImageBase]
|
||
.text:F8221819 push esi
|
||
.text:F822181A push eax ; ImageBase
|
||
.text:F822181B call KavPatchImageForNewProcess
|
||
.text:F8221820 mov esi, dword_F8230518
|
||
.text:F8221826 mov eax, dword_F823051C
|
||
.text:F822182B and esi, 0FFFFF000h
|
||
.text:F8221831 cmp esi, eax
|
||
.text:F8221833 mov [ebp+ImageBase], esi
|
||
.text:F8221836 jnb short loc_F8221883
|
||
.text:F8221838
|
||
.text:F8221838 loc_F8221838: ; CODE XREF: KavPatchNewProcessApcRoutine+71j
|
||
.text:F8221838 push esi
|
||
.text:F8221839 call KavPageTranslation0
|
||
.text:F822183F push esi
|
||
.text:F8221840 mov [ebp+var_8], eax
|
||
.text:F8221843 call KavPageTranslation1
|
||
.text:F8221849 mov [ebp+var_4], eax
|
||
.text:F822184C mov eax, [ebp+var_8]
|
||
.text:F822184F lock or dword ptr [eax], 4
|
||
.text:F8221853 lock and dword ptr [eax], 0FFFFFEFFh
|
||
.text:F822185A mov eax, [ebp+var_4]
|
||
.text:F822185D invlpg byte ptr [eax]
|
||
.text:F8221860 lock or dword ptr [eax], 4
|
||
.text:F8221864 lock and dword ptr [eax], 0FFFFFEFDh
|
||
.text:F822186B mov eax, [ebp+ImageBase]
|
||
.text:F822186E invlpg byte ptr [eax]
|
||
.text:F8221871 mov eax, dword_F823051C
|
||
.text:F8221876 add esi, 1000h
|
||
.text:F822187C cmp esi, eax
|
||
.text:F822187E mov [ebp+ImageBase], esi
|
||
.text:F8221881 jb short loc_F8221838
|
||
.text:F8221883
|
||
.text:F8221883 loc_F8221883: ; CODE XREF: KavPatchNewProcessApcRoutine+26j
|
||
.text:F8221883 pop esi
|
||
.text:F8221884 mov esp, ebp
|
||
.text:F8221886 pop ebp
|
||
.text:F8221887 retn 0Ch
|
||
.text:F8221887 KavPatchNewProcessApcRoutine endp
|
||
|
||
|
||
.text:F8221750 ; int __stdcall KavPatchImageForNewProcess(PUCHAR ImageBase)
|
||
.text:F8221750 KavPatchImageForNewProcess proc near ; CODE XREF: KavPatchNewProcessApcRoutine+Bp
|
||
.text:F8221750
|
||
.text:F8221750 ImageBase = dword ptr 8
|
||
.text:F8221750
|
||
.text:F8221750 push ebx
|
||
.text:F8221751 call ds:KeEnterCriticalRegion
|
||
.text:F8221757 mov eax, dword_F82282F4
|
||
.text:F822175C push 1 ; Wait
|
||
.text:F822175E push eax ; Resource
|
||
.text:F822175F call ds:ExAcquireResourceExclusiveLite
|
||
.text:F8221765 push 1
|
||
.text:F8221767 call KavSetPageAttributes1
|
||
.text:F822176C mov ecx, [esp+ImageBase]
|
||
.text:F8221770 push ecx ; ImageBase
|
||
.text:F8221771 call KavPatchImage
|
||
.text:F8221776 push 0
|
||
.text:F8221778 mov bl, al
|
||
.text:F822177A call KavSetPageAttributes1
|
||
.text:F822177F mov ecx, dword_F82282F4 ; Resource
|
||
.text:F8221785 call ds:ExReleaseResourceLite
|
||
.text:F822178B call ds:KeLeaveCriticalRegion
|
||
.text:F8221791 mov al, bl
|
||
.text:F8221793 pop ebx
|
||
.text:F8221794 retn 4
|
||
.text:F8221794 KavPatchImageForNewProcess endp
|
||
|
||
|
||
The actual image patching reprotects the export table of kernel32, changes the
|
||
export address table entries for the LoadLibrary* family of functions to point
|
||
to a thunk that is written into spare space within the kernel32 image, and
|
||
writes the actual thunk code out:
|
||
|
||
|
||
.text:F8221680 ; int __stdcall KavPatchImage(PUCHAR ImageBase)
|
||
.text:F8221680 KavPatchImage proc near ; CODE XREF: KavPatchImageForNewProcess+21p
|
||
.text:F8221680
|
||
.text:F8221680 var_C = dword ptr -0Ch
|
||
.text:F8221680 FunctionVa = dword ptr -8
|
||
.text:F8221680 var_4 = dword ptr -4
|
||
.text:F8221680 ImageBase = dword ptr 4
|
||
.text:F8221680
|
||
.text:F8221680 mov eax, [esp+ImageBase]
|
||
.text:F8221684 sub esp, 0Ch
|
||
.text:F8221687 push ebp
|
||
.text:F8221688 push 3Ch
|
||
.text:F822168A push eax
|
||
.text:F822168B call KavReprotectExportTable
|
||
.text:F8221690 mov ebp, eax
|
||
.text:F8221692 test ebp, ebp
|
||
.text:F8221694 jnz short loc_F822169F
|
||
.text:F8221696 xor al, al
|
||
.text:F8221698 pop ebp
|
||
.text:F8221699 add esp, 0Ch
|
||
.text:F822169C retn 4
|
||
.text:F822169F ; ---------------------------------------------------------------------------
|
||
.text:F822169F
|
||
.text:F822169F loc_F822169F: ; CODE XREF: KavPatchImage+14j
|
||
.text:F822169F push ebx
|
||
.text:F82216A0 push esi
|
||
.text:F82216A1 push edi
|
||
.text:F82216A2 xor ebx, ebx
|
||
.text:F82216A4 mov edi, ebp
|
||
.text:F82216A6 mov esi, offset ExportedFunctionsToCheckTable
|
||
.text:F82216AB
|
||
.text:F82216AB CheckNextFunctionInTable: ; CODE XREF: KavPatchImage+B4j
|
||
.text:F82216AB mov edx, [esi+0Ch]
|
||
.text:F82216AE mov eax, [esp+1Ch+ImageBase]
|
||
.text:F82216B2 lea ecx, [esp+1Ch+var_C]
|
||
.text:F82216B6 push ecx
|
||
.text:F82216B7 push edx
|
||
.text:F82216B8 push eax
|
||
.text:F82216B9 call LookupExportedFunction
|
||
.text:F82216BE test eax, eax
|
||
.text:F82216C0 mov [esp+1Ch+FunctionVa], eax
|
||
.text:F82216C4 jz short loc_F8221725
|
||
.text:F82216C6 mov edx, [esp+1Ch+var_C]
|
||
.text:F82216CA lea ecx, [esp+1Ch+var_4]
|
||
.text:F82216CE push ecx
|
||
.text:F82216CF push 40h
|
||
.text:F82216D1 push 4
|
||
.text:F82216D3 push edx
|
||
.text:F82216D4 call KavExecuteNtProtectVirtualMemoryInt2E
|
||
.text:F82216D9 test al, al
|
||
.text:F82216DB jz short loc_F8221725
|
||
.text:F82216DD cmp dword ptr [esi], 0
|
||
.text:F82216E0 jnz short loc_F82216EF
|
||
.text:F82216E2 mov eax, [esp+1Ch+FunctionVa]
|
||
.text:F82216E6 mov ecx, [esp+1Ch+var_C]
|
||
.text:F82216EA mov [esi], eax
|
||
.text:F82216EC mov [esi+8], ecx
|
||
.text:F82216EF
|
||
.text:F82216EF loc_F82216EF: ; CODE XREF: KavPatchImage+60j
|
||
.text:F82216EF mov eax, edi
|
||
.text:F82216F1 mov edx, 90909090h
|
||
.text:F82216F6 mov [eax], edx
|
||
.text:F82216F8 mov [eax+4], edx
|
||
.text:F82216FB mov [eax+8], edx
|
||
.text:F82216FE mov [eax+0Ch], dx
|
||
.text:F8221702 mov [eax+0Eh], dl
|
||
.text:F8221705 mov byte ptr [edi], 0E9h
|
||
.text:F8221708 mov ecx, [esi+4]
|
||
.text:F822170B mov edx, ebx
|
||
.text:F822170D sub ecx, ebx
|
||
.text:F822170F sub ecx, ebp
|
||
.text:F8221711 sub ecx, 5
|
||
.text:F8221714 mov [edi+1], ecx
|
||
.text:F8221717 mov ecx, [esp+1Ch+ImageBase]
|
||
.text:F822171B mov eax, [esp+1Ch+var_C]
|
||
.text:F822171F sub edx, ecx
|
||
.text:F8221721 add edx, ebp
|
||
.text:F8221723 mov [eax], edx ;
|
||
.text:F8221723 ; Patching Export Table here
|
||
.text:F8221723 ; e.g. write to 7c802f58
|
||
.text:F8221723 ; (kernel32 EAT entry for LoadLibraryA)
|
||
.text:F8221723 ;
|
||
.text:F8221723 ; 578 241 00001D77 LoadLibraryA = _LoadLibraryA@4
|
||
.text:F8221723 ; 579 242 00001D4F LoadLibraryExA = _LoadLibraryExA@12
|
||
.text:F8221723 ; 580 243 00001AF1 LoadLibraryExW = _LoadLibraryExW@12
|
||
.text:F8221723 ; 581 244 0000ACD3 LoadLibraryW = _LoadLibraryW@4
|
||
.text:F8221723 ;
|
||
.text:F8221723 ; KAV writes a new RVA pointing to its hook code here.
|
||
.text:F8221725
|
||
.text:F8221725 loc_F8221725: ; CODE XREF: KavPatchImage+44j
|
||
.text:F8221725 ; KavPatchImage+5Bj
|
||
.text:F8221725 add esi, 10h
|
||
.text:F8221728 add ebx, 0Fh
|
||
.text:F822172B add edi, 0Fh
|
||
.text:F822172E cmp esi, offset byte_F82357E0
|
||
.text:F8221734 jb CheckNextFunctionInTable
|
||
.text:F822173A pop edi
|
||
.text:F822173B pop esi
|
||
.text:F822173C pop ebx
|
||
.text:F822173D mov al, 1
|
||
.text:F822173F pop ebp
|
||
.text:F8221740 add esp, 0Ch
|
||
.text:F8221743 retn 4
|
||
.text:F8221743 KavPatchImage endp
|
||
|
||
|
||
KAV's export table reprotecting code assumes that the user mode PE header is
|
||
well-formed and does not contain offsets pointing to kernel mode addresses:
|
||
|
||
|
||
.text:F8221360 KavReprotectExportTable proc near ; CODE XREF: KavPatchImage+Bp
|
||
.text:F8221360
|
||
.text:F8221360 var_10 = dword ptr -10h
|
||
.text:F8221360 var_C = dword ptr -0Ch
|
||
.text:F8221360 var_8 = dword ptr -8
|
||
.text:F8221360 var_4 = dword ptr -4
|
||
.text:F8221360 arg_0 = dword ptr 4
|
||
.text:F8221360 arg_4 = dword ptr 8
|
||
.text:F8221360
|
||
.text:F8221360 mov eax, [esp+arg_0]
|
||
.text:F8221364 sub esp, 10h
|
||
.text:F8221367 cmp word ptr [eax], 'ZM'
|
||
.text:F822136C push ebx
|
||
.text:F822136D push ebp
|
||
.text:F822136E push esi
|
||
.text:F822136F push edi
|
||
.text:F8221370 jnz loc_F8221442
|
||
.text:F8221376 mov esi, [eax+3Ch]
|
||
.text:F8221379 add esi, eax
|
||
.text:F822137B mov [esp+20h+var_C], esi
|
||
.text:F822137F cmp dword ptr [esi], 'EP'
|
||
.text:F8221385 jnz loc_F8221442
|
||
.text:F822138B lea eax, [esp+20h+var_8]
|
||
.text:F822138F xor edx, edx
|
||
.text:F8221391 mov dx, [esi+14h]
|
||
.text:F8221395 push eax
|
||
.text:F8221396 xor eax, eax
|
||
.text:F8221398 push 40h
|
||
.text:F822139A mov ax, [esi+6]
|
||
.text:F822139E lea ecx, [eax+eax*4]
|
||
.text:F82213A1 lea eax, [edx+ecx*8+18h]
|
||
.text:F82213A5 push eax
|
||
.text:F82213A6 push esi
|
||
.text:F82213A7 call KavExecuteNtProtectVirtualMemoryInt2E ; NtProtectVirtualMemory
|
||
.text:F82213AC test al, al
|
||
.text:F82213AE jz loc_F8221442
|
||
.text:F82213B4 mov ecx, [esi+8]
|
||
.text:F82213B7 mov [esp+20h+var_10], 0
|
||
.text:F82213BF inc ecx
|
||
.text:F82213C0 mov [esi+8], ecx
|
||
.text:F82213C3 xor ecx, ecx
|
||
.text:F82213C5 mov cx, [esi+14h]
|
||
.text:F82213C9 cmp word ptr [esi+6], 0
|
||
.text:F82213CE lea edi, [ecx+esi+18h]
|
||
.text:F82213D2 jbe short loc_F8221442
|
||
.text:F82213D4 mov ebp, [esp+20h+arg_4]
|
||
.text:F82213D8
|
||
.text:F82213D8 loc_F82213D8: ; CODE XREF: KavReprotectExportTable+E0j
|
||
.text:F82213D8 mov ebx, [edi+10h]
|
||
.text:F82213DB test ebx, 0FFFh
|
||
.text:F82213E1 jz short loc_F82213EA
|
||
.text:F82213E3 or ebx, 0FFFh
|
||
.text:F82213E9 inc ebx
|
||
.text:F82213EA
|
||
.text:F82213EA loc_F82213EA: ; CODE XREF: KavReprotectExportTable+81j
|
||
.text:F82213EA mov ecx, [edi+8]
|
||
.text:F82213ED mov edx, ebx
|
||
.text:F82213EF sub edx, ecx
|
||
.text:F82213F1 cmp edx, ebp
|
||
.text:F82213F3 jle short loc_F822142C
|
||
.text:F82213F5 mov esi, [edi+0Ch]
|
||
.text:F82213F8 mov ecx, [esp+20h+arg_0]
|
||
.text:F82213FC sub esi, ebp
|
||
.text:F82213FE push ebp
|
||
.text:F82213FF add esi, ebx
|
||
.text:F8221401 add esi, ecx
|
||
.text:F8221403 push esi
|
||
.text:F8221404 call KavFindSectionName
|
||
.text:F8221409 test al, al
|
||
.text:F822140B jz short loc_F8221428
|
||
.text:F822140D cmp dword ptr [edi+1], 'TINI'
|
||
.text:F8221414 jz short loc_F8221428
|
||
.text:F8221416 lea eax, [esp+20h+var_4]
|
||
.text:F822141A push eax
|
||
.text:F822141B push 40h
|
||
.text:F822141D push ebp
|
||
.text:F822141E push esi
|
||
.text:F822141F call KavExecuteNtProtectVirtualMemoryInt2E ; NtProtectVirtualMemory
|
||
.text:F8221424 test al, al
|
||
.text:F8221426 jnz short loc_F822144E
|
||
.text:F8221428
|
||
.text:F8221428 loc_F8221428: ; CODE XREF: KavReprotectExportTable+ABj
|
||
.text:F8221428 ; KavReprotectExportTable+B4j
|
||
.text:F8221428 mov esi, [esp+20h+var_C]
|
||
.text:F822142C
|
||
.text:F822142C loc_F822142C: ; CODE XREF: KavReprotectExportTable+93j
|
||
.text:F822142C mov eax, [esp+20h+var_10]
|
||
.text:F8221430 xor ecx, ecx
|
||
.text:F8221432 mov cx, [esi+6]
|
||
.text:F8221436 add edi, 28h
|
||
.text:F8221439 inc eax
|
||
.text:F822143A cmp eax, ecx
|
||
.text:F822143C mov [esp+20h+var_10], eax
|
||
.text:F8221440 jb short loc_F82213D8
|
||
.text:F8221442
|
||
.text:F8221442 loc_F8221442: ; CODE XREF: KavReprotectExportTable+10j
|
||
.text:F8221442 ; KavReprotectExportTable+25j ...
|
||
.text:F8221442 pop edi
|
||
.text:F8221443 pop esi
|
||
.text:F8221444 pop ebp
|
||
.text:F8221445 xor eax, eax
|
||
.text:F8221447 pop ebx
|
||
.text:F8221448 add esp, 10h
|
||
.text:F822144B retn 8
|
||
.text:F822144E ; ---------------------------------------------------------------------------
|
||
.text:F822144E
|
||
.text:F822144E loc_F822144E: ; CODE XREF: KavReprotectExportTable+C6j
|
||
.text:F822144E mov eax, [edi+8]
|
||
.text:F8221451 mov [edi+10h], ebx
|
||
.text:F8221454 add eax, ebp
|
||
.text:F8221456 mov [edi+8], eax
|
||
.text:F8221459 mov eax, esi
|
||
.text:F822145B pop edi
|
||
.text:F822145C pop esi
|
||
.text:F822145D pop ebp
|
||
.text:F822145E pop ebx
|
||
.text:F822145F add esp, 10h
|
||
.text:F8221462 retn 8
|
||
.text:F8221462 KavReprotectExportTable endp
|
||
|
||
|
||
The mechanism by which KAV uses to reprotect user mode code is rather much of
|
||
a hack as well. KAV dynamically determines the system call ordinal of the
|
||
NtProtectVirtualMemory system service and uses its own int 2e thunk to call the
|
||
service.
|
||
|
||
|
||
.text:F8221320 KavExecuteNtProtectVirtualMemoryInt2E proc near
|
||
.text:F8221320 ; CODE XREF: KavReprotectExportTable+47p
|
||
.text:F8221320 ; KavReprotectExportTable+BFp ...
|
||
.text:F8221320
|
||
.text:F8221320 arg_0 = dword ptr 4
|
||
.text:F8221320 arg_4 = dword ptr 8
|
||
.text:F8221320 arg_8 = dword ptr 0Ch
|
||
.text:F8221320 arg_C = dword ptr 10h
|
||
.text:F8221320
|
||
.text:F8221320 mov eax, [esp+arg_0]
|
||
.text:F8221324 mov ecx, [esp+arg_C]
|
||
.text:F8221328 mov edx, [esp+arg_8]
|
||
.text:F822132C push ebx
|
||
.text:F822132D mov [esp+4+arg_0], eax
|
||
.text:F8221331 push ecx
|
||
.text:F8221332 lea eax, [esp+8+arg_4]
|
||
.text:F8221336 push edx
|
||
.text:F8221337 mov edx, NtProtectVirtualMemoryOrdinal
|
||
.text:F822133D lea ecx, [esp+0Ch+arg_0]
|
||
.text:F8221341 push eax
|
||
.text:F8221342 push ecx
|
||
.text:F8221343 push 0FFFFFFFFh
|
||
.text:F8221345 push edx
|
||
.text:F8221346 xor bl, bl
|
||
.text:F8221348 call KavInt2E
|
||
.text:F822134D test eax, eax
|
||
.text:F822134F mov al, 1
|
||
.text:F8221351 jge short loc_F8221355
|
||
.text:F8221353 mov al, bl
|
||
.text:F8221355
|
||
.text:F8221355 loc_F8221355: ; CODE XREF: KavExecuteNtProtectVirtualMemoryInt2E+31j
|
||
.text:F8221355 pop ebx
|
||
.text:F8221356 retn 10h
|
||
.text:F8221356 KavExecuteNtProtectVirtualMemoryInt2E endp
|
||
|
||
|
||
.user:F8231090 KavInt2E proc near ; CODE XREF: KavExecuteNtProtectVirtualMemoryInt2E+28p
|
||
.user:F8231090
|
||
.user:F8231090 arg_0 = dword ptr 8
|
||
.user:F8231090 arg_4 = dword ptr 0Ch
|
||
.user:F8231090
|
||
.user:F8231090 push ebp
|
||
.user:F8231091 mov ebp, esp
|
||
.user:F8231093 mov eax, [ebp+arg_0]
|
||
.user:F8231096 lea edx, [ebp+arg_4]
|
||
.user:F823109C int 2Eh
|
||
.user:F823109C
|
||
.user:F823109E pop ebp
|
||
.user:F823109F retn 18h
|
||
.user:F823109F KavInt2E endp
|
||
.user:F823109F
|
||
|
||
|
||
KAV's export lookup code does not correctly validate offsets garnered from the
|
||
PE header before using them:
|
||
|
||
|
||
.text:F8220CA0 LookupExportedFunction proc near ; CODE XREF: sub_F8217A60+C9p
|
||
.text:F8220CA0 ; sub_F82181D0+Dp ...
|
||
.text:F8220CA0
|
||
.text:F8220CA0 var_20 = dword ptr -20h
|
||
.text:F8220CA0 var_1C = dword ptr -1Ch
|
||
.text:F8220CA0 var_18 = dword ptr -18h
|
||
.text:F8220CA0 var_14 = dword ptr -14h
|
||
.text:F8220CA0 var_10 = dword ptr -10h
|
||
.text:F8220CA0 var_C = dword ptr -0Ch
|
||
.text:F8220CA0 var_8 = dword ptr -8
|
||
.text:F8220CA0 var_4 = dword ptr -4
|
||
.text:F8220CA0 arg_0 = dword ptr 4
|
||
.text:F8220CA0 arg_4 = dword ptr 8
|
||
.text:F8220CA0 arg_8 = dword ptr 0Ch
|
||
.text:F8220CA0
|
||
.text:F8220CA0 mov edx, [esp+arg_0]
|
||
.text:F8220CA4 sub esp, 20h
|
||
.text:F8220CA7 cmp word ptr [edx], 'ZM'
|
||
.text:F8220CAC push ebx
|
||
.text:F8220CAD push ebp
|
||
.text:F8220CAE push esi
|
||
.text:F8220CAF push edi
|
||
.text:F8220CB0 jnz loc_F8220DE1
|
||
.text:F8220CB6 mov eax, [edx+3Ch]
|
||
.text:F8220CB9 add eax, edx
|
||
.text:F8220CBB cmp dword ptr [eax], 'EP'
|
||
.text:F8220CC1 jnz loc_F8220DE1
|
||
.text:F8220CC7 mov eax, [eax+78h]
|
||
.text:F8220CCA mov edi, [esp+30h+arg_4]
|
||
.text:F8220CCE add eax, edx
|
||
.text:F8220CD0 mov [esp+30h+var_14], eax
|
||
.text:F8220CD4 mov esi, [eax+1Ch]
|
||
.text:F8220CD7 mov ebx, [eax+24h]
|
||
.text:F8220CDA mov ecx, [eax+20h]
|
||
.text:F8220CDD add esi, edx
|
||
.text:F8220CDF add ebx, edx
|
||
.text:F8220CE1 add ecx, edx
|
||
.text:F8220CE3 cmp edi, 1000h
|
||
.text:F8220CE9 mov [esp+30h+var_4], esi
|
||
.text:F8220CED mov [esp+30h+var_C], ebx
|
||
.text:F8220CF1 mov [esp+30h+var_18], ecx
|
||
.text:F8220CF5 jnb short loc_F8220D27
|
||
.text:F8220CF7 mov ecx, [eax+10h]
|
||
.text:F8220CFA mov eax, edi
|
||
.text:F8220CFC sub eax, ecx
|
||
.text:F8220CFE mov eax, [esi+eax*4]
|
||
.text:F8220D01 add eax, edx
|
||
.text:F8220D03 mov edx, [esp+30h+arg_8]
|
||
.text:F8220D07 test edx, edx
|
||
.text:F8220D09 jz loc_F8220DE3
|
||
.text:F8220D0F mov ebx, ecx
|
||
.text:F8220D11 shl ebx, 1Eh
|
||
.text:F8220D14 sub ebx, ecx
|
||
.text:F8220D16 add ebx, edi
|
||
.text:F8220D18 pop edi
|
||
.text:F8220D19 lea ecx, [esi+ebx*4]
|
||
.text:F8220D1C pop esi
|
||
.text:F8220D1D pop ebp
|
||
.text:F8220D1E mov [edx], ecx
|
||
.text:F8220D20 pop ebx
|
||
.text:F8220D21 add esp, 20h
|
||
.text:F8220D24 retn 0Ch
|
||
.text:F8220D27 ; ---------------------------------------------------------------------------
|
||
.text:F8220D27
|
||
.text:F8220D27 loc_F8220D27: ; CODE XREF: LookupExportedFunction+55j
|
||
.text:F8220D27 mov edi, [eax+14h]
|
||
.text:F8220D2A mov [esp+30h+arg_0], 0
|
||
.text:F8220D32 test edi, edi
|
||
.text:F8220D34 mov [esp+30h+var_8], edi
|
||
.text:F8220D38 jbe loc_F8220DE1
|
||
.text:F8220D3E mov [esp+30h+var_1C], esi
|
||
.text:F8220D42
|
||
.text:F8220D42 loc_F8220D42: ; CODE XREF: LookupExportedFunction+13Bj
|
||
.text:F8220D42 cmp dword ptr [esi], 0
|
||
.text:F8220D45 jz short loc_F8220DC5
|
||
.text:F8220D47 mov ecx, [eax+18h]
|
||
.text:F8220D4A xor ebp, ebp
|
||
.text:F8220D4C test ecx, ecx
|
||
.text:F8220D4E mov [esp+30h+var_10], ecx
|
||
.text:F8220D52 jbe short loc_F8220DC5
|
||
.text:F8220D54 mov edi, [esp+30h+var_18]
|
||
.text:F8220D58 mov [esp+30h+var_20], ebx
|
||
.text:F8220D5C
|
||
.text:F8220D5C loc_F8220D5C: ; CODE XREF: LookupExportedFunction+11Bj
|
||
.text:F8220D5C mov ebx, [esp+30h+var_20]
|
||
.text:F8220D60 xor esi, esi
|
||
.text:F8220D62 mov si, [ebx]
|
||
.text:F8220D65 mov ebx, [esp+30h+arg_0]
|
||
.text:F8220D69 cmp esi, ebx
|
||
.text:F8220D6B jnz short loc_F8220DAA
|
||
.text:F8220D6D mov eax, [edi]
|
||
.text:F8220D6F mov esi, [esp+30h+arg_4]
|
||
.text:F8220D73 add eax, edx
|
||
.text:F8220D75
|
||
.text:F8220D75 loc_F8220D75: ; CODE XREF: LookupExportedFunction+F3j
|
||
.text:F8220D75 mov bl, [eax]
|
||
.text:F8220D77 mov cl, bl
|
||
.text:F8220D79 cmp bl, [esi]
|
||
.text:F8220D7B jnz short loc_F8220D99
|
||
.text:F8220D7D test cl, cl
|
||
.text:F8220D7F jz short loc_F8220D95
|
||
.text:F8220D81 mov bl, [eax+1]
|
||
.text:F8220D84 mov cl, bl
|
||
.text:F8220D86 cmp bl, [esi+1]
|
||
.text:F8220D89 jnz short loc_F8220D99
|
||
.text:F8220D8B add eax, 2
|
||
.text:F8220D8E add esi, 2
|
||
.text:F8220D91 test cl, cl
|
||
.text:F8220D93 jnz short loc_F8220D75
|
||
.text:F8220D95
|
||
.text:F8220D95 loc_F8220D95: ; CODE XREF: LookupExportedFunction+DFj
|
||
.text:F8220D95 xor eax, eax
|
||
.text:F8220D97 jmp short loc_F8220D9E
|
||
.text:F8220D99 ; ---------------------------------------------------------------------------
|
||
.text:F8220D99
|
||
.text:F8220D99 loc_F8220D99: ; CODE XREF: LookupExportedFunction+DBj
|
||
.text:F8220D99 ; LookupExportedFunction+E9j
|
||
.text:F8220D99 sbb eax, eax
|
||
.text:F8220D9B sbb eax, 0FFFFFFFFh
|
||
.text:F8220D9E
|
||
.text:F8220D9E loc_F8220D9E: ; CODE XREF: LookupExportedFunction+F7j
|
||
.text:F8220D9E test eax, eax
|
||
.text:F8220DA0 jz short loc_F8220DED
|
||
.text:F8220DA2 mov eax, [esp+30h+var_14]
|
||
.text:F8220DA6 mov ecx, [esp+30h+var_10]
|
||
.text:F8220DAA
|
||
.text:F8220DAA loc_F8220DAA: ; CODE XREF: LookupExportedFunction+CBj
|
||
.text:F8220DAA mov esi, [esp+30h+var_20]
|
||
.text:F8220DAE inc ebp
|
||
.text:F8220DAF add esi, 2
|
||
.text:F8220DB2 add edi, 4
|
||
.text:F8220DB5 cmp ebp, ecx
|
||
.text:F8220DB7 mov [esp+30h+var_20], esi
|
||
.text:F8220DBB jb short loc_F8220D5C
|
||
.text:F8220DBD mov ebx, [esp+30h+var_C]
|
||
.text:F8220DC1 mov edi, [esp+30h+var_8]
|
||
.text:F8220DC5
|
||
.text:F8220DC5 loc_F8220DC5: ; CODE XREF: LookupExportedFunction+A5j
|
||
.text:F8220DC5 ; LookupExportedFunction+B2j
|
||
.text:F8220DC5 mov ecx, [esp+30h+arg_0]
|
||
.text:F8220DC9 mov esi, [esp+30h+var_1C]
|
||
.text:F8220DCD inc ecx
|
||
.text:F8220DCE add esi, 4
|
||
.text:F8220DD1 cmp ecx, edi
|
||
.text:F8220DD3 mov [esp+30h+arg_0], ecx
|
||
.text:F8220DD7 mov [esp+30h+var_1C], esi
|
||
.text:F8220DDB jb loc_F8220D42
|
||
.text:F8220DE1
|
||
.text:F8220DE1 loc_F8220DE1: ; CODE XREF: LookupExportedFunction+10j
|
||
.text:F8220DE1 ; LookupExportedFunction+21j ...
|
||
.text:F8220DE1 xor eax, eax
|
||
.text:F8220DE3
|
||
.text:F8220DE3 loc_F8220DE3: ; CODE XREF: LookupExportedFunction+69j
|
||
.text:F8220DE3 ; LookupExportedFunction+162j
|
||
.text:F8220DE3 pop edi
|
||
.text:F8220DE4 pop esi
|
||
.text:F8220DE5 pop ebp
|
||
.text:F8220DE6 pop ebx
|
||
.text:F8220DE7 add esp, 20h
|
||
.text:F8220DEA retn 0Ch
|
||
.text:F8220DED ; ---------------------------------------------------------------------------
|
||
.text:F8220DED
|
||
.text:F8220DED loc_F8220DED: ; CODE XREF: LookupExportedFunction+100j
|
||
.text:F8220DED mov eax, [esp+30h+var_4]
|
||
.text:F8220DF1 mov ecx, [esp+30h+arg_0]
|
||
.text:F8220DF5 lea ecx, [eax+ecx*4]
|
||
.text:F8220DF8 mov eax, [ecx]
|
||
.text:F8220DFA add eax, edx
|
||
.text:F8220DFC mov edx, [esp+30h+arg_8]
|
||
.text:F8220E00 test edx, edx
|
||
.text:F8220E02 jz short loc_F8220DE3
|
||
.text:F8220E04 pop edi
|
||
.text:F8220E05 pop esi
|
||
.text:F8220E06 pop ebp
|
||
.text:F8220E07 mov [edx], ecx
|
||
.text:F8220E09 pop ebx
|
||
.text:F8220E0A add esp, 20h
|
||
.text:F8220E0D retn 0Ch
|
||
.text:F8220E0D LookupExportedFunction endp
|
||
|
||
|
||
User mode calling KAV kernel code directly without a ring 0 transition:
|
||
|
||
|
||
kd> bp f824d820
|
||
kd> g
|
||
Breakpoint 0 hit
|
||
klif!sub_F8231820:
|
||
001b:f824d820 83ec08 sub esp,0x8
|
||
kd> kv
|
||
ChildEBP RetAddr Args to Child
|
||
WARNING: Stack unwind information not available. Following frames may be wrong.
|
||
0006f4ec 7432f69c 74320000 00000001 00000000 klif!sub_F8231820
|
||
0006f50c 7c9011a7 74320000 00000001 00000000 0x7432f69c
|
||
0006f52c 7c91cbab 7432f659 74320000 00000001 ntdll!LdrpCallInitRoutine+0x14
|
||
0006f634 7c916178 00000000 c0150008 00000000 ntdll!LdrpRunInitializeRoutines+0x344 (FPO: [Non-Fpo])
|
||
0006f8e0 7c9162da 00000000 0007ced0 0006fbd4 ntdll!LdrpLoadDll+0x3e5 (FPO: [Non-Fpo])
|
||
0006fb88 7c801bb9 0007ced0 0006fbd4 0006fbb4 ntdll!LdrLoadDll+0x230 (FPO: [Non-Fpo])
|
||
0006fc20 f824d749 0106c0f0 0000000e 0107348c 0x7c801bb9
|
||
0006fd14 7c918dfa 7c90d625 7c90eacf 00000000 klif!loc_F823173D+0xc
|
||
0006fe00 7c910551 000712e8 00000044 0006ff0c ntdll!_LdrpInitialize+0x246 (FPO: [Non-Fpo])
|
||
0006fecc 00000000 00072368 00000000 00078c48 ntdll!RtlFreeHeap+0x1e9 (FPO: [Non-Fpo])
|
||
kd> t
|
||
klif!sub_F8231820+0x3:
|
||
001b:f824d823 53 push ebx
|
||
kd> r
|
||
eax=0006f3cc ebx=00000000 ecx=00005734 edx=0006f3ea esi=7c882fd3 edi=7432f608
|
||
eip=f824d823 esp=0006ef00 ebp=0006f4ec iopl=0 nv up ei pl nz na po nc
|
||
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000206
|
||
klif!sub_F8231820+0x3:
|
||
001b:f824d823 53 push ebx
|
||
kd> dg 1b
|
||
P Si Gr Pr Lo
|
||
Sel Base Limit Type l ze an es ng Flags
|
||
---- -------- -------- ---------- - -- -- -- -- --------
|
||
001B 00000000 ffffffff Code RE 3 Bg Pg P Nl 00000cfa
|
||
kd> !pte eip
|
||
VA f824d823
|
||
PDE at C0300F80 PTE at C03E0934
|
||
contains 01010067 contains 06B78065
|
||
pfn 1010 ---DA--UWEV pfn 6b78 ---DA--UREV
|
||
|
||
|
||
KAV crashing the system when stepping through its kernel mode code when called
|
||
from user mode (apparently not that reliable after all!):
|
||
|
||
|
||
Breakpoint 0 hit
|
||
klif!sub_F8231820:
|
||
001b:f824d820 83ec08 sub esp,0x8
|
||
kd> u eip
|
||
klif!sub_F8231820:
|
||
f824d820 ebfe jmp klif!sub_F8231820 (f824d820)
|
||
f824d822 085355 or [ebx+0x55],dl
|
||
f824d825 56 push esi
|
||
f824d826 57 push edi
|
||
f824d827 33ed xor ebp,ebp
|
||
f824d829 6820d824f8 push 0xf824d820
|
||
f824d82e 896c2418 mov [esp+0x18],ebp
|
||
f824d832 896c2414 mov [esp+0x14],ebp
|
||
kd> g
|
||
Breakpoint 0 hit
|
||
klif!sub_F8231820:
|
||
001b:f824d820 ebfe jmp klif!sub_F8231820 (f824d820)
|
||
kd> g
|
||
Breakpoint 0 hit
|
||
klif!sub_F8231820:
|
||
001b:f824d820 ebfe jmp klif!sub_F8231820 (f824d820)
|
||
kd> bd 0
|
||
kd> g
|
||
Break instruction exception - code 80000003 (first chance)
|
||
*******************************************************************************
|
||
* *
|
||
* You are seeing this message because you pressed either *
|
||
* CTRL+C (if you run kd.exe) or, *
|
||
* CTRL+BREAK (if you run WinDBG), *
|
||
* on your debugger machine's keyboard. *
|
||
* *
|
||
* THIS IS NOT A BUG OR A SYSTEM CRASH *
|
||
* *
|
||
* If you did not intend to break into the debugger, press the "g" key, then *
|
||
* press the "Enter" key now. This message might immediately reappear. If it *
|
||
* does, press "g" and "Enter" again. *
|
||
* *
|
||
*******************************************************************************
|
||
nt!RtlpBreakWithStatusInstruction:
|
||
804e3592 cc int 3
|
||
kd> gu
|
||
|
||
*** Fatal System Error: 0x000000d1
|
||
(0x00003592,0x0000001C,0x00000000,0x00003592)
|
||
|
||
Break instruction exception - code 80000003 (first chance)
|
||
*******************************************************************************
|
||
* *
|
||
* You are seeing this message because you pressed either *
|
||
* CTRL+C (if you run kd.exe) or, *
|
||
* CTRL+BREAK (if you run WinDBG), *
|
||
* on your debugger machine's keyboard. *
|
||
* *
|
||
* THIS IS NOT A BUG OR A SYSTEM CRASH *
|
||
* *
|
||
* If you did not intend to break into the debugger, press the "g" key, then *
|
||
* press the "Enter" key now. This message might immediately reappear. If it *
|
||
* does, press "g" and "Enter" again. *
|
||
* *
|
||
*******************************************************************************
|
||
nt!RtlpBreakWithStatusInstruction:
|
||
804e3592 cc int 3
|
||
kd> g
|
||
Break instruction exception - code 80000003 (first chance)
|
||
|
||
A fatal system error has occurred.
|
||
Debugger entered on first try; Bugcheck callbacks have not been invoked.
|
||
|
||
A fatal system error has occurred.
|
||
|
||
Connected to Windows XP 2600 x86 compatible target, ptr64 FALSE
|
||
Loading Kernel Symbols
|
||
................................................................................................................
|
||
Loading User Symbols
|
||
................................
|
||
Loading unloaded module list
|
||
............
|
||
*******************************************************************************
|
||
* *
|
||
* Bugcheck Analysis *
|
||
* *
|
||
*******************************************************************************
|
||
|
||
Use !analyze -v to get detailed debugging information.
|
||
|
||
BugCheck D1, {3592, 1c, 0, 3592}
|
||
|
||
*** ERROR: Module load completed but symbols could not be loaded for klif.sys
|
||
Probably caused by : hardware
|
||
|
||
Followup: MachineOwner
|
||
---------
|
||
*** Possible invalid call from 804e331f ( nt!KeUpdateSystemTime+0x160 )
|
||
*** Expected target 804e358e ( nt!DbgBreakPointWithStatus+0x0 )
|
||
|
||
nt!RtlpBreakWithStatusInstruction:
|
||
804e3592 cc int 3
|
||
kd> !analyze -v
|
||
*******************************************************************************
|
||
* *
|
||
* Bugcheck Analysis *
|
||
* *
|
||
*******************************************************************************
|
||
|
||
DRIVER_IRQL_NOT_LESS_OR_EQUAL (d1)
|
||
An attempt was made to access a pageable (or completely invalid) address at an
|
||
interrupt request level (IRQL) that is too high. This is usually
|
||
caused by drivers using improper addresses.
|
||
If kernel debugger is available get stack backtrace.
|
||
Arguments:
|
||
Arg1: 00003592, memory referenced
|
||
Arg2: 0000001c, IRQL
|
||
Arg3: 00000000, value 0 = read operation, 1 = write operation
|
||
Arg4: 00003592, address which referenced memory
|
||
|
||
Debugging Details:
|
||
------------------
|
||
|
||
|
||
READ_ADDRESS: 00003592
|
||
|
||
CURRENT_IRQL: 1c
|
||
|
||
FAULTING_IP:
|
||
+3592
|
||
00003592 ?? ???
|
||
|
||
PROCESS_NAME: winlogon.exe
|
||
|
||
DEFAULT_BUCKET_ID: INTEL_CPU_MICROCODE_ZERO
|
||
|
||
BUGCHECK_STR: 0xD1
|
||
|
||
LAST_CONTROL_TRANSFER: from 804e3324 to 00003592
|
||
|
||
FAILED_INSTRUCTION_ADDRESS:
|
||
+3592
|
||
00003592 ?? ???
|
||
|
||
POSSIBLE_INVALID_CONTROL_TRANSFER: from 804e331f to 804e358e
|
||
|
||
TRAP_FRAME: f7872ce0 -- (.trap fffffffff7872ce0)
|
||
ErrCode = 00000000
|
||
eax=00000001 ebx=000275fc ecx=8055122c edx=000003f8 esi=00000005 edi=ddfff298
|
||
eip=00003592 esp=f7872d54 ebp=f7872d64 iopl=0 nv up ei pl nz na pe nc
|
||
cs=0008 ss=0010 ds=0023 es=0023 fs=0030 gs=0000 efl=00010202
|
||
00003592 ?? ???
|
||
Resetting default scope
|
||
|
||
STACK_TEXT:
|
||
WARNING: Frame IP not in any known module. Following frames may be wrong.
|
||
f7872d50 804e3324 00000001 f7872d00 000000d1 0x3592
|
||
f7872d50 f824d820 00000001 f7872d00 000000d1 nt!KeUpdateSystemTime+0x165
|
||
0006f4ec 7432f69c 74320000 00000001 00000000 klif+0x22820
|
||
0006f50c 7c9011a7 74320000 00000001 00000000 ODBC32!_DllMainCRTStartup+0x52
|
||
0006f52c 7c91cbab 7432f659 74320000 00000001 ntdll!LdrpCallInitRoutine+0x14
|
||
0006f634 7c916178 00000000 c0150008 00000000 ntdll!LdrpRunInitializeRoutines+0x344
|
||
0006f8e0 7c9162da 00000000 0007ced0 0006fbd4 ntdll!LdrpLoadDll+0x3e5
|
||
0006fb88 7c801bb9 0007ced0 0006fbd4 0006fbb4 ntdll!LdrLoadDll+0x230
|
||
0006fbf0 7c801d6e 7ffddc00 00000000 00000000 kernel32!LoadLibraryExW+0x18e
|
||
0006fc04 7c801da4 0106c0f0 00000000 00000000 kernel32!LoadLibraryExA+0x1f
|
||
0006fc20 f824d749 0106c0f0 0000000e 0107348c kernel32!LoadLibraryA+0x94
|
||
00000000 00000000 00000000 00000000 00000000 klif+0x22749
|
||
|
||
|
||
STACK_COMMAND: .trap 0xfffffffff7872ce0 ; kb
|
||
|
||
FOLLOWUP_NAME: MachineOwner
|
||
|
||
MODULE_NAME: hardware
|
||
|
||
IMAGE_NAME: hardware
|
||
|
||
DEBUG_FLR_IMAGE_TIMESTAMP: 0
|
||
|
||
BUCKET_ID: CPU_CALL_ERROR
|
||
|
||
Followup: MachineOwner
|
||
---------
|
||
*** Possible invalid call from 804e331f ( nt!KeUpdateSystemTime+0x160 )
|
||
*** Expected target 804e358e ( nt!DbgBreakPointWithStatus+0x0 )
|
||
|
||
kd> u 804e331f
|
||
nt!KeUpdateSystemTime+0x160:
|
||
804e331f e86a020000 call nt!DbgBreakPointWithStatus (804e358e)
|
||
804e3324 ebb4 jmp nt!KeUpdateSystemTime+0x11b (804e32da)
|
||
804e3326 90 nop
|
||
804e3327 fb sti
|
||
804e3328 8d09 lea ecx,[ecx]
|
||
nt!KeUpdateRunTime:
|
||
804e332a a11cf0dfff mov eax,[ffdff01c]
|
||
804e332f 53 push ebx
|
||
804e3330 ff80c4050000 inc dword ptr [eax+0x5c4]
|
||
|
||
|
||
2.7. The solution.
|
||
|
||
KAV's anti-virus software relies upon many unsafe kernel-mode hacks that put
|
||
system stability in jeopardy. Removing unsafe kernel mode hacks like
|
||
patching non-exported kernel functions or hooking various system services
|
||
without parameter validation is the first step towards fixing the problem.
|
||
|
||
Many of the operations KAV uses hooking or other unsafe means for are doable
|
||
using documented and safe APIs and conventions that are well-described in the
|
||
Windows Device Driver Kit (DDK) and Installable File System Kit (IFS Kit). It
|
||
would behoove the KAV programmers to take the time to read and understand the
|
||
documented way to do things in the Windows kernel instead of taking a quite
|
||
literally hack-and-slash approach that leaves the system at risk of crashes
|
||
and potentially even privilege escalation.
|
||
|
||
Many of the unsafe practices relied upon by KAV are blocked by PatchGuard on
|
||
x64 and will make it significantly harder to release a 64-bit version of KAV's
|
||
anti-virus software (which will become increasingly important as computers are
|
||
sold with x64 support and run x64 Windows by default). Because 32-bit kernel
|
||
drivers cannot be loaded on 64-bit Windows, KAV will need to port their driver
|
||
to x64 and deal with PatchGuard. Additionally, assumptions that end user
|
||
computers will be uniprocessor are fast becoming obsolete, as most new systems
|
||
sold today support HyperThreading or multiple cores.
|
||
|
||
|
||
3. The problem: McAfee Internet Security Suite 2006
|
||
|
||
McAfee's Internet Security Suite 2006 package includes a number of programs,
|
||
including anti-virus, firewall, and anti-spam software. In particular,
|
||
however, this article discusses one particular facet of Internet Security Suite
|
||
2006: The McAfee Privacy Service.
|
||
|
||
This component is designed to intercept outbound traffic and sanitize it of any
|
||
predefined sensitive information before it hits the wire.
|
||
|
||
>From the very start, if one is familiar with network programming, such a goal
|
||
would appear to be very difficult to practically achieve. For instance, many
|
||
programs send data in a compressed or encrypted form, and there is no common
|
||
way to process such data without writing specialized software for each target
|
||
application. This immediately limits the effectiveness of the Privacy Service
|
||
software's generalized information sanitization process to programs that have
|
||
a) had specialized handler code written for them, or b) send information to
|
||
the Internet in plaintext. Furthermore, the very act of modifying an outbound
|
||
data stream could potentially cause an application to fail (consider the case
|
||
where an application network protocol includes its own checksums of data sent
|
||
and received, where arbitrary modifications of network traffic might cause it
|
||
to be rejected).
|
||
|
||
The problem with McAfee Internet Security Suite goes deeper, however. The
|
||
mechanism by which Internet Security Suite intercepts (and potentially alters)
|
||
outbound network traffic is through a Windows-specific mechanism known as an
|
||
LSP (or Layered Service Provider).
|
||
|
||
LSPs are user mode DLLs that "plug-in" to Winsock (the Windows sockets API) and
|
||
are called for every sockets API call that a user mode program makes. This
|
||
allows easy access to view (and modify) network traffic without going through
|
||
the complexities of writing a conventional kernel driver. An LSP is loaded and
|
||
called in the context of the program making the original socket API call.
|
||
|
||
This means that for most programs using user mode socket calls, all API calls
|
||
will be redirected through the Internet Security Suite's LSP, for potential
|
||
modification.
|
||
|
||
If one has been paying attention so far, this approach should already be
|
||
setting off alarms. One serious problem with this approach is that since the
|
||
LSP DLL itself resides in the same address space (and thus has the same
|
||
privileges) as the calling program, there is nothing technically stopping a
|
||
malicious program from modifying the LSP DLL's code to exempt itself from
|
||
alteration, or even bypassing the LSP directly.
|
||
|
||
Unfortunately, the flaws in the McAfee Privacy Service do not simply end here,
|
||
although already the technical limitations of an LSP for securely intercepting
|
||
and modifying network traffic make this approach (in the author's opinion)
|
||
wholly unsuitable for a program designed to protect a user from having his or
|
||
her private data stolen by malicious software.
|
||
|
||
Specifically, there are implementation flaws in how the LSP itself handles
|
||
certain socket API calls that may cause otherwise perfectly working software
|
||
to fail when run under McAfee Internet Security Suite 2006. This poses a
|
||
serious problem to software vendors, who are often forced to interoperate with
|
||
pervasive personal security software (such as Internet Security Suite).
|
||
|
||
The Windows Sockets environment is fully multithreaded and thread-safe, and
|
||
allows programs to call into the sockets API from multiple threads concurrently
|
||
without risk of data corruption or other instability. Unfortunately, the LSP
|
||
provided by McAfee for its Privacy Service software breaks this particular
|
||
portion of the Windows Sockets API contract. In particular, McAfee's LSP does
|
||
not correctly synchronize access to internal data structures when sockets are
|
||
created or destroyed, often leading to situations where a newly created socket
|
||
handed back to an application program is already mistakenly closed by the
|
||
flawed LSP before the application even sees it.
|
||
|
||
In addition, the author has also observed a similar synchronization problem
|
||
regarding the implementation of the `select' function in the Privacy Service
|
||
LSP. The select function is used to poll a set of sockets for a series of
|
||
events, such as data being available to read, or buffer space being available
|
||
to send data. The McAfee LSP appears to fail when calls to select are made
|
||
from multiple threads concurrently, however, often appearing to switch a
|
||
ocket handle specified by the original application program with an entirely
|
||
different handle. (In Windows, the same handle space is shared by
|
||
socket handles and all other types of kernel objects, such as files or
|
||
processes and threads). This subsequently results in calls to select failing
|
||
in strange ways, or worse, returning that data is available for a particular
|
||
socket when it was in fact available on a different socket entirely.
|
||
|
||
Both of these flaws result in intermittant failures of correctly written third
|
||
party applications when used in conjunction with McAfee Internet Security Suite
|
||
2006.
|
||
|
||
3.2. Solution for Software Vendors
|
||
|
||
If one is stuck in the unfortunate position of being forced to support software
|
||
running under McAfee Internet Security Suite 2006, one potential solution to
|
||
this problem is to manually serialize all calls to select (and other functions
|
||
that create or destroy sockets, such as socket and the WSASocket family of
|
||
functions). This approach has worked in practice, and is perhaps the least
|
||
invasive solution to the flawed LSP.
|
||
|
||
An alternative solution is to bypass the LSP entirely and instead call directly
|
||
to the kernel sockets driver (AFD.sys). However, this entails determining the
|
||
actual handle associated with a socket (the handle returned by the McAfee LSP
|
||
is in fact not the underlying socket handle), as well as relying on the as of
|
||
yet officially undocumented AFD IOCTL interface.
|
||
|
||
3.3. Solution for McAfee
|
||
|
||
>From McAfee's perspective, the solution is fairly simple: correctly serialize
|
||
access to internal data structures from function calls that are made from
|
||
multiple threads concurrently.
|
||
|
||
|
||
4. Conclusion
|
||
|
||
As the Internet becomes an increasingly hostile place and the need for in-depth
|
||
personal security software (as a supplement or even replacement for proper
|
||
system administrator) grows for end-users, it will become increasingly
|
||
important for the vendors and providers of personal security software to ensure
|
||
that their programs do not impair the normal operation of systems upon which
|
||
their software is installed. The author realizes that such is a very difficult
|
||
task given what is expected of most personal security software suites, and
|
||
hopes that by shedding light on the flaws in existing software, new programs
|
||
can be made to avoid similar mistakes.
|