Map and projects (the most frequently updated page of this blog)

2010/02/13

葉っぱ一枚あればいい

Drivers in user-mode

Ever wanted to trace a driver directly from OllyDbg, without the usual
Unable to start file 'driver.sys'

Why

I already introduced the basics of a driver, at PE level.
It might be interesting to run a driver in user-mode, for example, to unpack it:
On one hand, if a driver is packed, you just won't be able to quickly run and dump it the usual way, so you'd have to use a kernel debugger.
On the other hand, typically, packed drivers unpack themselves with no or few API calls, no or few privileged instruction, which makes you think:
'this is standard user-mode code that just runs inside a driver to unpack itself, if only I could just run it the usual way'.


Loading the driver

But there are 2 things that prevent Driver.sys from loading under a user-mode debugger:
The Subsystem itself: the file will just refuse to load if it's Native, so just change that to Gui or Console, either manually or with a line of PEFile:
p.OPTIONAL_HEADER.Subsystem = 2
# = pefile.SUBSYSTEM_TYPE['IMAGE_SUBSYSTEM_WINDOWS_GUI']

Then, a driver will import ntoskrnl.exe, which will eventually fail in user-mode.
If you don't need APIs - or can emulate them manually - the easiest solution is just to get rid of the imports altogether. Just delete the RVA of the Imports data directory - with PEFile, that translates as:
p.OPTIONAL_HEADER.DATA_DIRECTORY[1].VirtualAddress = 0

So, now you have a driver, without imports - which means it won't be able to call any API - that will load in OllyDbg. Maybe that's enough to reach code after the unpacking stub - most kernel packers are not complex - and then get the original file.


Further

Even though ntoskrnl.exe is Native itself, it's loaded like any other file (from user-mode), so if you want to be able to run a few API calls in your driver, a solution is to create a fake ntoskrnl.exe, and emulate the required APIs.
A normal file with exports and relocations will do.
In my example, I redirected ntoskrnl.exe!DbgPrint to user32.dll!MessageBoxA, which makes the HelloWorld driver work directly from your explorer window.

This turns the original kernel-level call
push helloworld
call DbgPrint
into a standard MessageBoxA call
00400216 CALL to MessageBoxA from ntoskrnl.00400211
00000000 hOwner = NULL
00010215 Text = "Hello World!"
00400217 Title = "User mode Ntoskrnl"
00000040 Style = MB_OK|MB_ICONASTERISK|MB_APPLMODAL



However, after the Subsystem and the APIs, the extra problem is that the driver might use privileged instructions, such as
FB sti
FA cli
0F20C0 mov eax, cr0
0F22C0 mov cr0, eax
Besides nopping each of them, there is no generic solution, as they might be obfuscated. A possible solution would be to load the executable with your own SEH, to skip such instructions (Exception: PRIVILEGED INSTRUCTION, 0C0000096h).
I am not sure it's worth the trouble, if it gets too complicated, you're just better with a kernel debugger.

Detection

Also, it's possible to detect if you're executed in user-mode (which is not the same as detecting kernel or user debuggers), for example by checking the value of CS:

default CS under XP
user mode 1B
kernel mode 8


HelloWorld driver, Sys2Exe python script, fake Ntoskrnl
Sources and binaries
(compile with MakePE)


Drivers en mode utilisateur

Avez-vous déjà voulu analyser un driver directement depuis OllyDbg, sans l'habituel
Unable to start file 'driver.sys'

Pourquoi

J'ai déjà parlé des bases des drivers, au niveau PE.
Ça peut être interessant d'exécuter un driver en mode utilisateur, par exemple pour le décompresser :
D'un coté, si le driver est packé, on ne peut pas le lancer et le dumper rapidement comme d'habitude, donc on devrait utiliser un débogueur noyau.
D'un autre coté, la plupart du temps, les drivers packés se décompressent eux-même en n'utilisant aucune ou très peu d'APIs, aucune ou très peu d'instructions privilégiées, ce qui nous amène à penser:
'C'est du code utilisateur normal, dans un driver. Si seulement je pouvais l'exécuter comme d'habitude'

Charger un driver

Mais il y a 2 choses qui empêchent Driver.sys de s'exécuter dans un débogueur utilisateur:
Le Subsystem lui-même: le fichier refusera tout simplement de se charger s'il est Native, donc changez le en Gui ou Console, manuellement ou avec une ligne de PEFile:
p.OPTIONAL_HEADER.Subsystem = 2
# = pefile.SUBSYSTEM_TYPE['IMAGE_SUBSYSTEM_WINDOWS_GUI']

Ensuite, le driver importe ntoskrnl.exe, ce qui finira par échouer en mode utilisateur.
Si on n'a pas besoin des APIs - ou qu'on peut les simuler à la main - la solution la plus simple est de se débarasser directement des imports. Il suffit de supprimer la RVA du data directory Imports - avec PEFile, cela donne:
p.OPTIONAL_HEADER.DATA_DIRECTORY[1].VirtualAddress = 0

Donc, on a maintenant un driver sans imports - donc qui ne pourra pas appeler d'API - qui marchera dans OllyDbg. Peut-être ça suffira pour atteindre le code après la décompression - la plupart des packeurs noyaux ne sont pas complexes - et d'obtenir le fichier original.

Un peu plus loin

Même si ntoskrnl.exe est lui-même Native, il est chargé comme n'importe quel fichier (depuis le mode utilisateur), donc si on veut exécuter quelques appels API depuis notre driver, une solution est de créer un ntoskrnl.exe factice, qui émulera les APIs requises.
Un fichier normal avec exports et relocations suffira.
Dans mon exemple, j'ai redirigé ntoskrnl.exe!DbgPrint vers user32.dll!MessageBoxA, ce qui permet au driver HelloWorld de marcher directement depuis votre fenêtre d'explorateur.

Cela change l'appel original au niveau noyau
push helloworld
call DbgPrint
en un appel MessageBoxA standard
00400216 CALL to MessageBoxA from ntoskrnl.00400211
00000000 hOwner = NULL
00010215 Text = "Hello World!"
00400217 Title = "User mode Ntoskrnl"
00000040 Style = MB_OK|MB_ICONASTERISK|MB_APPLMODAL



Cependant, après le Subsystem et les APIs, un problème possible est que le driver utilise des instructions privilégiées, telles que
FB sti
FA cli
0F20C0 mov eax, cr0
0F22C0 mov cr0, eax
A part nopper chacune d'entre elles, il n'y a pas de solution générique, puisqu'elles pourraient être dissimulées. Une solution possible serait de charger l'exécutable avec votre propre SEH, pour sauter de telles instructions (Exception: PRIVILEGED INSTRUCTION, 0C0000096h).
Je ne suis pas sûr que ça en vaille la peine, si ça devient trop compliqué, autant utiliser un débogueur noyau.

Détection

D'autre part, il est possible de détecter si on est exécuté en mode utilisateur - ce qui n'est pas la même chose de détecter un débogueur noyau ou utilisateur - par exemple en vérifiant la valeur de CS:
valeur par défaut de CS, sous XP
niveau utilisateur 1B
niveau noyau 8


driver HelloWorld, script python Sys2Exe, Ntoskrnl factice
Sources et binaires
(compiler avec MakePE)

No comments:

Post a Comment