Driver packing

I have written an antimalware program that uses an NT driver as part of its solution. Although the driver works correctly as is, I would like to pack/encrypt it in some fashion to avoid simple disassembly and analysis by malware writers seeking to evade my program. A colleague and I have been working on this and have run into a problem.

We have encrypted the .reloc section of the driver image. However, when it is decrypted at runtime, as soon as the first byte of the .reloc section is modified in memory, the system (VirtualPC) reboots. There is no BSOD, just an immediate reboot.

We are fairly confident that there is nothing wrong with the assembly code itself, for two reasons.
(1) If the driver headers are modified so that the same code runs in ring 3, the relocs are decrypted and applied successfully.
(2) If a small test driver is encrypted instead of the actual driver used in my program, the runtime decryption again is successful and the driver loads.

We suspect that the problem may have to do with paging because of reason (2) above. Is this possible? If not, what else might cause this behavior?

The small test driver is less than 2*PAGE_SIZE (8K) large, whereas the real driver is much larger.

I have not specified any #pragma alloc_text directives in any of the code, which I thought meant every driver function was by default non-pageable. Is this true? Is DriverEntry put in the INIT section by default if no pragma is specified? In which section are the other driver functions put?

I do allocate dynamically from paged pool – but I don’t think that will change the pageability of the driver image itself.

Any guidance would be greatly appreciated. Thank you very much!

Le Fri, 16 Nov 2007 06:36:35 -0500 (EST), xxxxx@mvps.org a ecrit:

I have written an antimalware program that uses an NT driver as part of
its
solution. Although the driver works correctly as is, I would like to
pack/encrypt it in some fashion to avoid simple disassembly and analysis
by malware writers seeking to evade my program. A colleague and I have
been working on this and have run into a problem.

It will take you one month to make it work, few hours to break it.

EA

You do realize that any malware writer can hook up a debugger and view the decrypted assembly right? Or write their own driver and things to circumvent you. In the end you will spend a lot of time on this and the malware author will just view you as a minor bump in the road.

d

-----Original Message-----
From: xxxxx@lists.osr.com [mailto:xxxxx@lists.osr.com] On Behalf Of xxxxx@mvps.org
Sent: Friday, November 16, 2007 3:37 AM
To: Windows System Software Devs Interest List
Subject: [ntdev] Driver packing

I have written an antimalware program that uses an NT driver as part of its solution. Although the driver works correctly as is, I would like to pack/encrypt it in some fashion to avoid simple disassembly and analysis by malware writers seeking to evade my program. A colleague and I have been working on this and have run into a problem.

We have encrypted the .reloc section of the driver image. However, when it is decrypted at runtime, as soon as the first byte of the .reloc section is modified in memory, the system (VirtualPC) reboots. There is no BSOD, just an immediate reboot.

We are fairly confident that there is nothing wrong with the assembly code itself, for two reasons.
(1) If the driver headers are modified so that the same code runs in ring 3, the relocs are decrypted and applied successfully.
(2) If a small test driver is encrypted instead of the actual driver used in my program, the runtime decryption again is successful and the driver loads.

We suspect that the problem may have to do with paging because of reason (2) above. Is this possible? If not, what else might cause this behavior?

The small test driver is less than 2*PAGE_SIZE (8K) large, whereas the real driver is much larger.

I have not specified any #pragma alloc_text directives in any of the code, which I thought meant every driver function was by default non-pageable. Is this true? Is DriverEntry put in the INIT section by default if no pragma is specified? In which section are the other driver functions put?

I do allocate dynamically from paged pool – but I don’t think that will change the pageability of the driver image itself.

Any guidance would be greatly appreciated. Thank you very much!


NTDEV is sponsored by OSR

For our schedule of WDF, WDM, debugging and other seminars visit:
http://www.osr.com/seminars

To unsubscribe, visit the List Server section of OSR Online at http://www.osronline.com/page.cfm?name=ListServer

xxxxx@mvps.org wrote:

I have written an antimalware program that uses an NT driver as part of its solution. Although the driver works correctly as is, I would like to pack/encrypt it in some fashion to avoid simple disassembly and analysis by malware writers seeking to evade my program. A colleague and I have been working on this and have run into a problem.

What an enviable task :slight_smile:

We have encrypted the .reloc section of the driver image. However, when it is decrypted at runtime, as soon as the first byte of the .reloc section is modified in memory, the system (VirtualPC) reboots. There is no BSOD, just an immediate reboot.

VirtualPC … hmm… have you checked the behaviour when the driver is
running on a real as opposed to virtual machine?

From my experience, attempts to modify code in the kernel tend to lead
to some sort of a protection fault, and subsequent bugcheck, although I
can’t remember exactly what.

We suspect that the problem may have to do with paging because of reason (2) above. Is this possible? If not, what else might cause this behavior?

I think what causes this behaviour is that you “got lucky”.
Specifically, depending on the layout, your tiny driver may have ended
up somewhere that made it more … ermm … malleable!

The way to find out more precisely is to use WinDbg and the !pte
command, which will tell you the page permissions and paging state for
the appropriate virtual addresses.

If you really *do* want to write self modifying code in the land of the
kernel, then the way to do it is:

  1. Use IoAllocateMdl to create an MDL describing the chunk of executable
    pages you want to modify.
  2. Use MmProbeAndLockPages, and then MmMapLockedPages (noncached,
    please!) to map the pages into a different region of address space.
  3. Make the changes via that alternate mapping.
  4. Unmap and unlock the pages.
  5. I suspect that the Mm operations *might* deal with ICache coherency,
    but I’m not sure.
  6. Prepare for lots of flames from here :slight_smile:
  7. Dream on about ever passing WHQL or getting the driver into a
    production product :slight_smile:
  8. Prepare for everyone else on the planet to be marginally less happy
    as kernel reliability takes another step in the wrong direction :slight_smile:

I have not specified any #pragma alloc_text directives in any of the code, which I thought meant every driver function was by default non-pageable. Is this true?

Mostly, but not always. Generally, the functions are non-pageable, but
if you’re writing a miniport driver, then you need to be aware that some
port drivers call MmPageEntireDriver on their miniports if they can
determine that no code should be executing in the miniport.

Is DriverEntry put in the INIT section by default if no pragma is specified? In which section are the other driver functions put?

I believe so, and I think the rest are in the .text (or whatever it’s
called nowadays - I forget).

I do allocate dynamically from paged pool – but I don’t think that will change the pageability of the driver image itself.

No, it won’t.

Any guidance would be greatly appreciated. Thank you very much!

Hope my 2 cents is of use.

MH.

Thank you all, and I am sorry for the delay in replying. I will work on this, and I may get back to you with follow-up questions if that is all right! Thanks again.