Skip to content

Latest commit

 

History

History
126 lines (100 loc) · 4.79 KB

Carpenter.md

File metadata and controls

126 lines (100 loc) · 4.79 KB

Carpenter

DrunkenIronman allows customizing the message displayed on the Blue Screen instead of the usual error identifier, such as DRIVER_IRQL_NOT_LESS_OR_EQUAL.

This is done by patching the message table resource of the kernel binary (ntoskrnl.exe) with the user-supplied string.

Message Tables

The message table is a PE resource format that stores a mapping between integer identifiers and strings.

Usually, message tables are generated by the Message Compiler (mc.exe).

Message tables are often used to store human-readable descriptions of error codes. For instance, the MUI for kernel32.dll contains a message table with the Win32 error codes and other error values.

The Format

The format of the message table resource is documented in the MSDN. However, it will also be provided here for ease of reference.

Generally speaking, a message table resource consists of one or more blocks of messages, each block holding messages with consecutive identifiers. The message strings can be either ANSI or Unicode.

A message table resource begins with this structure, which describes the blocks of messages in the table.

typedef struct
{
    DWORD                   NumberOfBlocks;
    MESSAGE_RESOURCE_BLOCK  Blocks[1];
} MESSAGE_RESOURCE_DATA, *PMESSAGE_RESOURCE_DATA;

Each block of messages is described by the following header:

typedef struct
{
    DWORD   LowId;
    DWORD   HighId;
    DWORD   OffsetToEntries;
} MESSAGE_RESOURCE_BLOCK;

The header specifies the range of IDs (inclusive) covered by the block, and an offset relative to the beginning of the message table resource to the actual string data. The strings are stored in MESSAGE_RESOURCE_ENTRY structures (described below) and are ordered by ID in ascending order.

Each message string is stored in the following structure:

typedef struct
{
    WORD    Length;
    WORD    Flags;
    BYTE    Text[1];
} MESSAGE_RESOURCE_ENTRY, *PMESSAGE_RESOURCE_ENTRY;
  • The Length field specifies the size, in bytes, of the whole structure.
  • The Flags field specifies whether the string is ANSI or Unicode. If equal to 1, the string is Unicode. If equal to 0, the string is ANSI.
  • The Text field contains the actual string data. It is unspecified whether the string is null-terminated.

Patching the Tables

The DrunkenIronman driver implements a message table parser, which provdes the following functionality:

  • Deserializing a message table resource into an in-memory representation.
  • Adding or replacing individual messages.
  • Serializing the in-memory representation to a new message table resource.

At first glance it may seem like all that is required to patch a message table resource in memory is to simply overwrite it with a new resource, making sure the new resource does not exceed the old one in size. In most cases this would probably be true, but when it comes to ntoskrnl.exe there is a small subtlety.

Apparently, the Windows kernel obtains pointers to specific strings in its own message table early in the boot process. Overwriting the whole message table will most likely result in some strings chaning their location, and so the aforementioned pointers will become invalid.

Because our only interest is in replacing messages, not adding new ones, this problem can be solved by only writing strings that are no longer than the originals.

While implementing this behaviour I stumbled upon the fact that, for some obscure reason, Microsoft's Message Compiler adds excessive padding after some strings in the message table. I say 'excessive' because only at most 1 byte of padding is required for each MESSAGE_RESOURCE_ENTRY, seeing as it must be aligned on a WORD boundary.

The current implementation, in order to maintain generality, assumes that ntoskrnl's message table resource is correctly padded, and reads the string data as-is. When patching entries, the implementation makes sure that the new entry is exactly the same size as the previous one, padding as necessary.

When serializing, however, the implementation stores only the actual string data (up to the null-terminator, if there is one), and fills the rest with zeroes. This assumes that the padding in the original message table was zeroes, as well.

Because there is no way to preserve the exact same layout of the message table resource without resorting to dirty hacks, the implementation also assumes that the message entries are stored consecutively, in block order. For ntoskrnl.exe this happens to be correct.