Ring0 under WinNT/2k/XP.

来源:互联网 发布:java字符串拼接方法 编辑:程序博客网 时间:2024/06/05 06:07
Ring0 under WinNT/2k/XP.
.by Ratter/29A.

.Intro.
Ring0 under NT? Does it also sound to you almost religious as to me?
NT based operating systems weren't written as a toy so gaining ring0
under these is much harder (if not impossible) than under Win9x/Me.
Yes there were exploits (like getadmin) that used bugs in the code
but that was the badly written code. The design does not have any
hole (i believe) that would let on well written system without bugs
run nonprivileged code under ring0. But that's it. Microsoft
unfortunately doesn't write code without bugs (what degrades the
architecture; i wish, Microsoft would succeed in this particular
task - to write at least one version of NT system without high
priority flaws - to shut the fuckin linux radicals mouth. yes, open
source is nice, M$ monopol is bad, but well i don't care - if there
would be comparable nice OS i would love it (ReactOS is coming :-))
but i don't like *nix, anyway i do like FreeBSD, but less than NT :-))

Do you hear too so much _often_ how Microsoft uses FUDs? Read
something from the other side about NT - so much hate and lack of
knowledge about the thing they're talking about is fascinating. I can
understand that by Microsoft as they want to earn money. But that
almost nazi radicalism on the linux side irritates me. Yet you can't
find almost anything like that on the *BSD side. This (apart of that
mess in linux sources) made me decide not to use Linux (for what i
don't have any reason neither) at least until this radicalism
dissapears.

What you should get from this almost political intro? If you don't
like Microsoft, use ReactOS (it will be comparable soon!!). If you
don't like NT architecture use *BSD, cause Linux sucks :-)


.Ring0 WinNT virus (Ideas).
So as i stated above, on well configured, well patched NT system, you
won't have a chance to even infect the system in ring3 much less to
get into ring0 (if you wont discover some new hole :-)). But we're
virus writers right, so we almost everytime count with user's
ignorance, laziness or whatever makes the box open so we will get in
anyway ;-)

Do you remember that WinNT.Adonai virus that appeared in 29A#5
claiming
; HA HA HA HA.... THE WORLD FIRST VIRUS ABLE TO JUMP INTO RING 0
; ON NT MACHINES (NT3.1, NT4 AND W2000)
? It imho speaks itself about it's nature just let me say, that
jumping into ring0 _ain't_ what we should want to achieve. Because as
i said some time before - it's not about getting into ring0, one must
know _what_ to do there. and, forgive me Henky, (if i omit that using
the callgate tech in a virus is _so_ convulsive and that you used the
routine that shipped with callgate.sys for beeping)beeping using direct
port I/O is very unhappily. If you wanted to beep a driver modifying
TSS I/O map would be a better solution - too using undocumented but
exported functions from ntoskrnl. So this is probably not the way to
go, we simply just don't want to beep on the files, we want to infect
them :-)

When i first started to think about native ring0 WinNT virus i made
a few points that could characterise it:

1) it should fully exploit the nature of the I/O subsystem - ie if
file infector, possibly a filesystem filter driver
2) it should fully exploit the nature of other subsystems - ie install
for example some NDIS backdoor, use TDI routines for using the
internet etc
3) if it needs something in ring3 it should be something that can't
be done in ring0 (i doubt anything like this exists, but for sure)
4) try to cover all versions of NT avalaible and handle all
variations (iapx86) - ie PAE, /3GB option, 4 meg pages
5) because of the complexity of the above points, it should be written
in C - anyway this is not mandatory, but i wont write it in asm
:-)

as you'll see nearly none of the points i resolved to implement i
haven't fulfilled in my current try of a ring0 NT virus :-)


.How to get into ring0.
This is a fundamental question for a virus writer that wants to do
something there :-) There are ways to tamper with ring0 mem using
/device/physicalmemory which has been described in Phrack55 i think.
Better implementation of this technique was written by Matt Wu who's
code is used as a base for FakeExec (in z0mbie's hkit). This is nice,
but not so much for a file infector.

Ntoskrnl.exe can be modified to run our code, or some other system
component. But we want to write a native ring0 virus with no ring3
part. And i doubt someone will send ntorkrnl.exe over email :-) so
this would need some ring3 dropper.

Thatswhy i realised that infecting *.sys (WinNT drivers) files is the
solution we're looking for. Moreover all infection techniques we know
from ring3 can be used, because *.sys file is a normal PE file (just
with a nonmandatory checksum - but BumbleBee (where are you friend??)
presented a way how to code a checksum routine on our own)! And if
you think about it a little - what is the most exchanged commodity
apart of movies and music on the internet? Drivers i believe, because
everyone wants to get the best from his machine :-)

(imagine this: native ring0 iworm :-) uses a social engineering to
send a driver sys file that will speed up recipients machine by 100%
for sure with instructions how to install it using TDI and normal SMTP
client and native file routines to get email addresses and read it's
body from the file)

Microsoft signs drivers that pass their WHQL test. These drivers
won't show ugly message that the drivers aren't signed - this is when
installing them via legal *.inf way. Any other way wont show anything
:-) anyway users don't care about this - i don't care neither because
most of the non hardware drivers wouldnt pass it as they use
undocumented techs exactly as we'll use, so don't try to send your
driver virus to Microsoft, they wont sign it for you :-)


-=What to do in ring0=-
So we're in ring0 via infection of a driver file. What to do now? We
redirected the entrypoint to our code and we're called by the kernel
as a legal DriverEntry. Now the question comes how we we're
integrated with the victim driver. Personally i prefer a little asm
stub - a pe file loader - that will load our real driver to some non
paged allocated memory and execute from there. Why? Because merging
with the victim doesn't guarantee the distance from the victim -
imagine you have a 700kb driver and you merged with it. Now the
initialiation of the driver fails, but it has to remain in the
memory, because we want to infect :-) If we'll use an asm pe loader,
it will separate us forever from the victim and we can take care
about ourselves. What about the DriverObject param to the
DriverEntry? IoCreateDriver function exported by ntoskrnl will fix
that ...

.The Loader.
It should be an asm stub, that will load the pe file that it carries
(it can be compressed, but for easier handling the virtual aligning
should be the same as physical aligning in the pe file), relocates it
and imports all needed functions - for pe file loader source you can
have a look at the In-memory PE File execution by z0mbie in 29A#6
magazine. Now i will only concentrate on questions regarding the
ring0:

1) this time we wont be looking for the base of kernel32.dll but
ntoskrnl.exe :-) how to get it? that's a question. in Che i use
hardcoded addresses and that is bad - as you know from the history
of the ring3 infection. Roy had an idea to get some interrupt
handler from IDT. Good idea, but it has it's limitations -
interrupts can be redirected. But what about the address of the
caller on the DriverEntry? We _should_ be called by the kernel
code that _should_ reside in ntoskrnl.exe. Well we should, unless
we are infected by some other virus :-) Now normal looking for
start of ntoskrnl comes, but, what a pitty, without SEH. this can
be replaced by checking the page tables (don't forget to handle
PAE and 4 meg pages) whether the page is present or redirecting
the page fault/access fault handler.

2) we have a ntoskrnl base. did i mention it is a pe file? we can use
exactly the same routines as in ring3 (protected by the above methods)
:-) drivers in most times import from ntoskrnl/hal but sometimes
also from other modules (like ndis or scsiport) so how to handle
this? there exists a routine for getting fc address from ntoskrnl
and hal. but we need something more general - SYSTEM_MODULE_INFORMATION
class of ZwQuerySystemInformation will do it - it will give you
all module names and bases loaded in the kernel address space.

3) we have the driver decompressed and loaded in allocated memory so
nothing prevents us from calling the DriverEntry :-)

4) after our babe does what it needs (and it finishes with no error)
simply jump to the victim's DriverEntry and all will go on ...

.The Driver.
Now the time to write a driver comes, but that's out of the scope of
this article - DDK, IFS kit, some good (filter) driver writing book
will help you. Go into it, it's beatiful and there are a lot of internet
resources covering this - i'll name sysinternals.com at least.

.The ideal virus.
Imagine a ring0 WinNT *.sys infector with stealth techs, using epo,
some metamorphic engine (go to mental driller, vecna and z0mbie for
this :-)) and this little feature:
as you probably know, the I/O system is layered - the IRP goes
through each layer on which it is maybe modified or whatever. Now
imagine we'll attach to the stack below the antivirus driver and
above it. when someone opens infected file, we'll disinfect it, pass
it down to the av driver which will pass it again to us and we'll
reinfect it. Or maybe we could simply create some kind of bridge
above this av driver - create irps pass down without av driver
intervention, other irps (mainly ioctl ones to let not ring3 portion
of the av product see the av driver is bridged) pass to the av
driver. These aren't possibly the only possibilities - try to find
out some as this will totally fuck up the whole av protection :-)

-=Win2k/Xp.Che=-
That was the theory :-) Now let's have a look at the piece of code
i've written for this issue. I'll cover only interesting points.

Che tries to be a resident on access *.sys file infector. This is a
first of its kind afaik so it has certain limitations and doesn't
fulfill none of the above mentioned points so please be patient, the
real native ring0 WinNT virus waits maybe on you, dear reader :-)

Che redirects the NtCreateFile native api via modifying the
KeServiceDescriptorTable. Under XP this table is read only so Che has
to make it read/write - for this exists at least 4 possible ways
afaik, in Che i use the Mdl way. Cr0 way can be used for example too
- you can have a look at it in TaiChi's bootvid.dll from previous
issue. Now when NtCreateFile is redirected, we can start to intercept
the open calls and infect the *.sys files.

btw - i check for one processor present. if i used Interlocked*
functions or lock prefix, i could omit this check. but since i don't
have access to MP machine, i can't test it so thatswhy i use it
although it _should_ work.

btw2 - the Che's hardcoded ntoskrnl addresses would not work with the
/3gb option obviously.

.Infection.
The problem is, that if your virus will infect some very low level
driver such as ntfs.sys it gets loaded before volumes are mounted
so you woldn't be able to infect anything. To wait for the system
being in normal condition i added a check for user mode thread on the
beginning of the hook. So Che infects files touched from ring3 only.

I also decided to move infection routine to ring3. This is mainly due
my laziness but also because i use SFP exception routine (described
in my other article) and checksum routine.

So the ring0 NtCreateFile hook only checks for ring3 thread opening
*.sys file, gets full name of the file, for the real infection
routine it finds imports from kernel32.dll using peb, allocates
memory in process address space, copies it there and calls it. This
routine will finally infect the file possibly exceptioning it from
SFP for a while and counting the checksum for it. This is also good
because SEH can be used.

But surely all code could be moved to ring0 - the real infection
routine could be rewritten in native api, the SFP could be disabled
using directory notification hooking and checksum is the easiest of
it. I'm sorry, probably next time :-)

.Ring3 synchronous procedure calling from ring0.
The interesting point i think is using of KeUserModeCallback to
synchronously call ring3 code. There is a way to call it
asynchronously using user mode APCs but that was not what i needed.
KeUserModeCallback is used by Win32k to call user mode gui routines.
There exists an index which is not checked in ntdll and can be easily
modified to point to our routine - look at the Che source.