Saturday, July 28, 2012

Deep Reflection - Defining Structs and Enums in PowerShell

One of the reasons PowerShell is so powerful is in its ability to access to .NET framework. In theory, this should make the transition for C# developers seamless. Unfortunately, PowerShell is not quite a one-to-one translation of C#. I demonstrated where this is not the case in a previous post by illustrating the hoops you need to jump through to declare a delegate type in PowerShell using reflection. Declaring an enum or struct is no different.

Before I jump into the technical details, it's worth addressing the question you may have already formed. Why don't you just compile C# code using the Add-Type cmdlet? Well, that is a perfectly valid method of declaring a struct, enum, delegate, etc. However, compiling C# code leaves artifacts on the disk. To prove my point, just run Procmon while compiling C# in PowerShell. As someone with an attacker's mindset, I prefer that all operations take place in memory unless absolutely necessary. Fortunately, true memory residence can be achieved using reflection.

As an example, here is a portion of the code from my last PowerSploit release - Get-PEHeader:

$code = @"
    using System;
    using System.Runtime.InteropServices;
 
    public class PE
    {
        public enum IMAGE_DOS_SIGNATURE : ushort
        {
            DOS_SIGNATURE =                 0x5A4D,      // MZ
            OS2_SIGNATURE =                 0x454E,      // NE
            OS2_SIGNATURE_LE =              0x454C,      // LE
        }


        [StructLayout(LayoutKind.Sequential, Pack=1)]
        public struct _IMAGE_DOS_HEADER
        {
            public IMAGE_DOS_SIGNATURE   e_magic;        // Magic number
            public ushort   e_cblp;                      // public bytes on last page of file
            public ushort   e_cp;                        // Pages in file
            public ushort   e_crlc;                      // Relocations
            public ushort   e_cparhdr;                   // Size of header in paragraphs
            public ushort   e_minalloc;                  // Minimum extra paragraphs needed
            public ushort   e_maxalloc;                  // Maximum extra paragraphs needed
            public ushort   e_ss;                        // Initial (relative) SS value
            public ushort   e_sp;                        // Initial SP value
            public ushort   e_csum;                      // Checksum
            public ushort   e_ip;                        // Initial IP value
            public ushort   e_cs;                        // Initial (relative) CS value
            public ushort   e_lfarlc;                    // File address of relocation table
            public ushort   e_ovno;                      // Overlay number
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 8)]
            public string   e_res;                       // May contain 'Detours!'
            public ushort   e_oemid;                     // OEM identifier (for e_oeminfo)
            public ushort   e_oeminfo;                   // OEM information; e_oemid specific
            [MarshalAsAttribute(UnmanagedType.ByValArray, SizeConst=10)]
            public ushort[] e_res2;                      // Reserved public ushorts
            public int      e_lfanew;                    // File address of new exe header
        }
    }
"@


Add-Type -TypeDefinition $code -WarningAction SilentlyContinue | Out-Null

As you can see, the C# 'code' above defines an enum and a struct that are compiled when Add-Type is called. The need to compile the code above can be obviated with reflection. This however requires some understanding of the attributes that define the fields in the enum and struct above. Interrogating these attributes can be accomplished with ildasm or PowerShell. I'll show you how to do this in PowerShell.

First, let's view the attributes of the IMAGE_DOS_SIGNATURE enum:

PS > [PE+IMAGE_DOS_SIGNATURE] | Format-List BaseType, Attributes
BaseType   : System.Enum
Attributes : AutoLayout, AnsiClass, Class, NestedPublic, Sealed

Now, let's view all the relevant individual attributes of the _IMAGE_DOS_HEADER struct:

PS > [PE+_IMAGE_DOS_HEADER] | Format-List BaseType, Attributes
BaseType   : System.ValueType
Attributes : AutoLayout, AnsiClass, Class, NestedPublic, SequentialLayout, Sealed, BeforeFieldInit
PS > [PE+_IMAGE_DOS_HEADER].GetField('e_res').GetCustomAttributes($True) | Format-List Value, TypeId, SizeConst
Value     : ByValTStr
TypeId    : System.Runtime.InteropServices.MarshalAsAttribute
SizeConst : 8
PS > [PE+_IMAGE_DOS_HEADER].GetField('e_res2').GetCustomAttributes($True) | Format-List Value, TypeId, SizeConst
Value     : ByValArray
TypeId    : System.Runtime.InteropServices.MarshalAsAttribute
SizeConst : 10

Now we have everything we need to dynamically generate the enum and struct. The following code demonstrates how to accomplish this and returns the DOS header for calc.exe:


By now, you can see the additional overhead required to dynamically generate code. However, if your goal is to avoid compiling code and remain truly memory resident, this is the way to go. Expect to see an update to Get-PEHeader that will implement these changes in the near future.

1 comment: