VX Heaven

Library Collection Sources Engines Constructors Simulators Utilities Links Forum

Ring0 under WinNT/2k/XP

29a [7]
February 2004

[Back to index] [Comments]


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


? 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.

Thats why 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 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 :-)


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.


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.


That is all from this issue's ring0 from me :-) It's the middle of the summer holidays and the weed parties are fully running so if something is too much chaotic i'm sorry, but i tried to make it as much consistent as i'm currently able to do. at last let me thank to Wumpscut, Front242, Project PitchFork and Suicide Commando, because i wouldn't be able to write this text without their support. If you have any suggestions/ideas about this beatiful topic, don't hesitate to let me know.

And now. It's summer so let's have some fun :-)

Ratter/29A - I'm a stranger in the world I haven't made.
[Back to index] [Comments]
By accessing, viewing, downloading or otherwise using this content you agree to be bound by the Terms of Use! aka