DelegateType Reflection
Now that we can resolve addresses of the Win32 APIs,
we must define the argument types.
The information about the number of arguments and their associated data types
must be paired with the resolved function memory address.
In C# this is done using the GetDelegateForFunctionPointer method.
This method takes two arguments, first the memory address of the function,
and second the function prototype represented as a type.
In C#, a function prototype is known as a Delegate or delegate type.
Unfortunately, there is no equivalent to the delegate keyword in PowerShell
so we must obtain this in a different manner.
Luckily, Microsoft described how a delegate type may be
created using reflection in an old blog post from 2004.
As we know from our usage of Add-Type,
the delegate type is created when the assembly is compiled, but instead
we’ll manually create an assembly in memory and populate it with content.
The first step is to create a new assembly object through the
AssemblyName class and assign it a name like ReflectedDelegate.
We do this by creating a new variable called $MyAssembly and setting it
to the instantiated assembly object with the name “ReflectedDelegate”.
Before we populate the assembly, we must configure its access mode.
This is an important permission,
because we want it to be executable and not saved to disk.
We can achieved through the DefineDynamicAssembly method,
first by supplying the custom assembly name.
Then we set it as executable by supplying the Run access mode value
defined in the System.Reflection.Emit. AssemblyBuilderAccess namespace
as the second argument.
With permissions set on the assembly, we can start creating content.
Inside an assembly, the main building block is a Module.
We can create this Module through the DefineDynamicModule method.
We'll supply a custom name for the module
and tell it not to include symbol information.
Now we can create a custom type that will become our delegate type.
We can do this within the module, using the DefineType method.
To do this, we need to set three arguments.
The first is the custom name, in our case MyDelegateType.
The second is the combined list of attributes for the type.
In our case, we must specify the type to be a class,
public, non-extendable, and use ASCII instead of Unicode.
Finally, it is set to be interpreted automatically,
since our testing found that this undocumented setting was required.
The attributes then become Class, Public, Sealed, AnsiClass, and AutoClass.
As a third argument, we must specify the type it builds on top of.
We'll choose the MulticastDelegate class to create a delegate type with multiple
entries, which will allow us to call the target API with multiple arguments.
Finally we are ready to put the function prototype inside the type
and let it become our custom delegate type.
First, we define the constructor through the DefineConstructor method,
which takes three arguments.
The first argument contains the attributes of the constructor itself,
defined through MethodAttributes Enum.
Here we must make it public
and require it to be referenced by both name and signature.
To do this, we choose RTSpecialName, HideBySig, and Public.
The second argument is the calling convention for the constructor,
which defines how arguments and return values are handled by the .NET framework.
In our case, we choose the default calling convention by specifying the enum value.
In the last argument, we come to the crux of our work.
We finally get to define the parameter types of the constructor
that will become the function prototype.
The complete call to DefineConstructor combines the constructor attributes,
the calling convention for the constructor, and the function arguments for MessageBoxA
that we have seen earlier in the module given as an array.
With the constructor created, we must call it.
But before we can do that, we must set a couple of implementation flags
with the SetImplementationFlags method
using values outlined in MethodImplAttributes Enum.
We choose Runtime and Managed
since it is used at runtime and the code is managed code.
The constructor is now ready to be called.
But to actually tell the .NET framework
the delegate type to be used in calling a function,
we have to define the Invoke method.
We’ll use DefineMethod to create and specify the settings for the Invoke method
DefineMethod takes four arguments.
The first is the name of the method to define, which in our case is “Invoke”.
The second argument includes method attributes
taken from the MethodAttributes Enum.
In our case, we choose Public to make it accessible,
HideBySig to allow it to be called by both name and signature,
NewSlot, to indicate that the method always gets a new slot in the vtable.
and finally Virtual.
As for the third argument, we specify the return type of the function,
which for MessageBoxA is [int].
The fourth argument is an array of argument types
that we already identified when we first introduced MessageBox.
The setup of the Invoke method puts together the four arguments
and supplies them to DefineMethod.
Just as with the constructor, we must set the implementation flags
to allow the Invoke method to be called.
This is done after it is defined through the SetImplementationFlags method.
To instantiate the delegate type,
we call our custom constructor through the CreateType method.
After all this effort, we finally have a delegate type to use in our call
to GetDelegateForFunctionPointer.
Combining all the pieces along with the resolved memory address of MessageBoxA,
we can call a Win32 native API without using Add-Type.
In review, we repeat the LookupFunc method that resolves the Win32 API address
and use that to locate the address of MessageBoxA.
Then we create the DelegateType.
Finally, we call GetDelegateForFunctionPointer
to link the function address and the DelegateType and invoke MessageBox.
Execution of this code yields a simple “Hello World” prompt showing our success.
Nice!
The final piece remaining now is to use our newly developed technique
to create a shellcode runner
and eventually execute it through our Word macro.