3

I'm intrigued by the DISCARDABLE flag in the section flags in PE files, specifically in the context of Windows drivers (in this case NDIS). I noticed that the INIT section was marked as RWX in a driver I'm reviewing, which seems odd - good security practice says you should adopt a W^X policy.

The dump of the section is as follows:

Name      Virtual Size  Virtual Addr  Raw Size      Raw Addr      Reloc Addr    LineNums      RelocCount    LineNumCount  Characteristics
INIT      00000B7E      0000E000      00000C00      0000B200      00000000      00000000      0000          0000          E2000020

The characteristics map to:

  • IMAGE_SCN_MEM_EXECUTE
  • IMAGE_SCN_MEM_READ
  • IMAGE_SCN_MEM_WRITE
  • IMAGE_SCN_MEM_DISCARDABLE
  • IMAGE_SCN_CNT_CODE

The INIT section seems to contain the driver entry, which implies that it might be used to ensure that the driver entry function resides in nonpaged memory, whereas the rest of the code is allowed to be paged. I'm not entirely sure, though. I can see no evidence in the driver code to say that the developers explicitly set the page flags, or forced the driver entry into a separate section, so it looks like the compiler did it automatically. I also manually flipped the writeable flag in the driver binary to test it out, and it works fine without writing enabled, so that implies that having it RWX is unnecessary.

So, my questions are:

  • What is the INIT section used for in the context of a Windows driver and why is it marked discardable?
  • How are discardable sections treated in the Windows kernel? I have some idea of how ReactOS handles them but that's still fuzzy and not massively helpful.
  • Why would the compiler move the driver entry to an INIT section?
  • Why would the compiler mark the section as RWX, when RX is sufficient and RWX may constitute a security issue?

References I've looked at so far:


EDIT, 2022: I forgot to update this, but a while after I posted this question I passed it on to Microsoft and it did turn out to be a bug in the MSVC linker. They were mistakenly marking the discard section that contained DriverEntry as RWX. The issue was fixed in VS2015.

Polynomial
  • 27,674
  • 12
  • 80
  • 107

2 Answers2

1

What is the INIT section used for in the context of a Windows...

It is normally used for the DriverEntry() function.

How are discardable sections treated in the Windows kernel?

It allows the page(s) that contain the DriverEntry() function code to be discarded. They are no longer needed after the driver is initialized.

Why would the compiler move the driver entry to an INIT section?

An NDIS driver normally contains

   #pragma NDIS_INIT_FUNCTION(DriverEntry)

Which is a macro in the WDK's inc/ddk/ndis.h header file:

   #define NDIS_INIT_FUNCTION(_F)      alloc_text(INIT,_F)

#pragma alloc_text is one of the ways to move a function into a particular section. Another common way it is done is by bracketing the DriverEntry function with #pragma code_seg(INIT) and #pragma code_seg().

Why would the compiler mark the section as RWX

That requires an archeological dig. Many drivers were started a long time ago and are likely to still use ~VS6, back when life was still uncomplicated and programmers wore white hats. Or perhaps the programmer used #pragma section, yet another way to name sections, it permits setting the attributes directly. A modern toolchain certainly won't do this, you get RX from #pragma alloc_text. There very little point in fretting about it, given that DriverEntry() lives for a very short time and any malware code that runs with ring0 privileges can do a lot more practical damage.

Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
  • The driver was compiled from source with VS2013, targeting XP, 7, and 8 using the latest 8.1 DDK. My primary concern is that, given a write-what-where bug in another part of the kernel which would be otherwise difficult to exploit due to KASLR / DEP / KPP, and given a kernel pointer leak (via debug output), and given a situation where the attacker can cause the driver to be unloaded and loaded again from unprivileged usermode, there's a code execution path via race condition (overwrite the end of DriverEntry), leading to privesc. – Polynomial Jul 01 '15 at 09:06
  • I can find no reference to `NDIS_INIT_FUNCTION`, `INIT`, `alloc_text` or `code_seg` in the source code of the driver. Any thought on where I should look to track down this odd behaviour? Perhaps it's a genuine compiler bug? – Polynomial Jul 01 '15 at 09:10
0

I passed this information on to Microsoft and it did turn out to be a bug in the MSVC linker. They were mistakenly marking the discard section that contained DriverEntry as RWX. This issue was fixed in Visual Studio 2015.

I wrote about the issue in more detail here.

Polynomial
  • 27,674
  • 12
  • 80
  • 107