en-US/about_PSDetourMarshalling.help.txt

TOPIC
    about_psdetourmarshalling
 
SHORT DESCRIPTION
    For dotnet to call C functions it needs to know how to convert types that
    exist in dotnet to types that exist in the C layer. This document aims to
    help document the way this is done in PSDetour and some things to lookout
    for.
 
LONG DESCRIPTION
    Due to the native of C functions working outside of the dotnet garbage
    collector (GC) it is important to distinguish these two boundaries. This
    guide will use the terms managed code to reference the dotnet side and
    unmanaged code for the C side. As unmanaged code is run outside of dotnet it
    has no ideal how to deal with classes and complex types that you can
    typically use in PowerShell. The behaviour around type marshalling in
    PSDetour is the same behaviour that is used when using PInvoke to call C
    functions directly in dotnet. Typically types are split into two difference
    categories: value types and reference types.
 
VALUE TYPES
    Value types are more primitive types that typically map to a common byte
    representation and are passed by value to functions. Examples of value types are:
    * `int` and the various sizes they emcompass like `Int32`, `Byte`, `SByte`,
    `UInt32`, etc
    * `bool`
    * `IntPtr`
    * `structs`
    You can check if a type is a value type by running `$obj.GetType().IsValue`
    in PowerShell.
    Passing an object by value typically means the actual bytes of that object
    are copied to where it needs to go rather than a reference/pointer being
    passed. These value types have a counterpark in the unmanaged code but the
    names of these types vary from standard to standard. Due to the simplistic
    nature of value types, these can be used directly by dotnet when crossing
    the managed/unmanaged boundary and the marshaler will handle all the work to
    do so.
    One other value type that can be used are structs. Like integers, bools,
    structs are also passed by value across the boundary and can contain
    multiple fields denoted by name. Dotnet will handle the marshalling of a
    struct based on the code that was used to define it. Keep in mind some C
    APIs use struct references, or pointers to structs. This is not the same as
    using the struct type directly on the parameter types but must be passed in
    as a reference. See the VALUE PASS BY REFERENCE section for more
    information. There are a few builtin structs in dotnet which may map to an
    actual struct define in the C API. It is also possible to define custom
    struct types using `Add-Type` with C# code for use in PSDetours. Take care
    that the struct is defined in the process that the hook is running on if
    dealing with remoted sessions.
    When in doubt, search online for what a C type is represented in dotnet
    PInvoke. Some common mappings between Win32/C types and dotnet are
    |C Type|Dotnet Type|Notes| |-|-|-| |VOID|`[void]`|| |HANDLE|`[IntPtr]`||
    |BYTE|`[Byte]`|| |SHORT|`[Int16]`|| |WORD|`[UInt16]`|Can typically be used
    with `[Int16]`| |INT|`[Int32]`|| |UINT|`[UInt32]`|| |LONG|`[Int32]`||
    |DWORD|`[UInt32]`|Can typically be used with `[Int32]`| |ULONG|`[UInt32]`||
    |BOOL|`[bool]`|| |BOOLEAN|`[bool]`|Use with `MarshalAs(UnmanagedType.U1)`|
    |CHAR|`[char]`|Use with `MarshalAs(UnamangedType.LPStr)`|
    |WCHAR|`[char]`|Use with `MarshalAs(UnamangedType.LPWStr)`|
    |LPSTR|`[System.Text.StringBuilder]`|Use with
    `MarshalAs(UnamangedType.LPStr)`| |LPCSTR|`[string]`|Use with
    `MarshalAs(UnamangedType.LPStr)`| |LPWSTR|`[System.Text.StringBuilder]`|Use
    with `MarshalAs(UnmanagedType.LPWStr)`| |LPCWSTR|`[string]`|Use with `MarshalAs(UnmanagedType.LPWStr)`|
    The `LPSTR` and `LPCSTR` string are similar with the difference being
    `LPCSTR` being a constant value. This means the underlying buffer won't be
    changed by the C function. Because an `LPSTR` can be modified by the C
    function, the `StringBuilder` type is usually recommended for these types.
    The same also applies to `LPWSTR` and `LPCWSTR`. A string type can also use
    `IntPtr` but will have to be manually converted in the hook if the string
    value is needed.
    Standard convention for Win32 APIs is to prefix types with `P` to denote a
    pointer to that type. For example `PHANDLE` is `HANDLE*` or a pointer to
    `HANDLE`. These types should be passed in as a by reference value, see VALUE
    PASS BY REFERENCE for more information.
 
REFERENCE TYPES
    Reference types are the rest of the types in dotnet. When calling a function
    with a reference type as a parameter the value in the background is not
    passed directly to that function, rather a pointer/reference is used
    instead. Like with PInvoke using reference types in dotnet to cross the
    managed and unmanaged boundary is certainly possible. A common reference
    type that is used with PInvoke code is the `string` or `StringBuilder`
    types. The dotnet marshaler knows how to pass these types by
    reference/pointer to the unmanaged code but it is important in PSDetour to
    denote the `MarshalAs` attribute to denote what type of string it is. For
    example the wide variant Win32 APIs, ones ending with W, use wide strings or
    `LPWSTR` and dotnet needs to know to use the Unicode/UTF-16-LE encoding when
    marshalling those types. This is done using the
    `[System.Runtime.InteropServices.MarshalAs([System.Runtime.InteropServices.UnmanagedType]::LPWStr)]`
    attribute alongside the type declaration. For example a Win32 C API that has
    a `LPWSTR` typed argument would look like this as a scriptblook hook.
 
    {
        param (
            [System.Runtime.InteropServices.MarshalAs([System.Runtime.InteropServices.UnmanagedType]::LPWStr)]
            [string]$Parameter
        )
     
        ...
    }
 
    See the MARSHAL AS ATTRIBUTE section for more details.
    Other classes typicall act like struct value types but instead they are
    always passed by reference. Typically it's better to use structs when
    possible as it can be passed by value, and optionally by reference.
 
TYPE DEFINITIONS
    To declare a return type for a C function hook, the `[OutputType]` attribute
    is used in the hook scriptblook. If hooking a C API that returns a `DWORD`
    then the `[OutputType([Int32])]` or `[OutputType([UInt32])]` type will be used.
    For example the `GetCurrentProcessId` function is a simple function without
    any parameter and a `DWORD` return value. The scriptblock to hook this in
    PowerShell would look like
 
    {
        [OutputType([int])]
        param ()
     
        ...
    }
 
    The return type is simply denoted by the `[OutputType(...)]` attribute.
    Another common return type in Windows is the `HANDLE` return type which is
    essentially a pointer and represented by `IntPtr` in C#. The
    `GetCurrentProcess` function is an example of a C API that returns a
    `HANDLE` and would look like
 
    {
        [OutputType([IntPtr])]
        param ()
     
        ...
    }
 
    While in PInvoke code, `HANDLE` return types can be defined with a
    `SafeHandle` return type, it is not recommended to use these for PSDetour
    hooking. This is because having a `SafeHandle` defined in the hook might be
    freed by the GC before it reaches back to the caller invaliding the pointer.
    It is possible to define a custom `MarshalAs` behaviour on the return type,
    see MARSHAL AS ATTRIBUTE for more information. If no `[OutputType]` is
    declared in the hook scriptblock, it is assumed the C function as no return
    type and is `[OutputType([void])]`.
    Parameter definitions are simply PowerShell parameters in a `param ()`
    block. It is important to specify a specific type for each parameter that
    fits with the C API being hooked. For example the `OpenProcess` Win32
    function definition looks like the following:
 
    HANDLE OpenProcess(
      [in] DWORD dwDesiredAccess,
      [in] BOOL bInheritHandle,
      [in] DWORD dwProcessId
    );
 
    The return type is an `IntPtr` to represent the `HANDLE` and the parameters
    simply match the value types the C types represents. The scriptblock hook
    for this function looks like the following:
 
    {
        [OutputType([IntPtr])]
        param (
            [int]$Access,
            [bool]$InheritHandle,
            [int]$ProcessId
        )
     
        ...
    }
 
    When in doubt as to what types to use, it is best to search online for the
    PInvoke definitions for these functions and replicate how they are defined
    in C#.
 
MARSHAL AS ATTRIBUTE
    The `MarshalAs` attribute is a special attribute used by the dotnet
    marshaler that provides extra metadata on how it should transfer the
    arguments across the boundaries. As mentioned above, the most common use
    case for this attribute is when marshalling string types and the type of
    string needs to be known by the marshaler. To specify these attributes for
    either the output type or parameter itself, it should simply by provided
    alongside the definition.
    A more complex Win32 function that requires complex marshalling is the
    `CreateSymbolicLinkW` function. The C function is defined as
 
    BOOLEAN CreateSymbolicLinkW(
        [in] LPCWSTR lpSymlinkFileName,
        [in] LPCWSTR lpTargetFileName,
        [in] DWORD dwFlags
    );
 
    Not only does it use string for arguments that need to be documented as
    `LPWSTR` but the return value is a C `BOOLEAN` type. Unlike a C `BOOL`, a
    `BOOLEAN` is a single byte value so dotnet needs to know how to marshal this
    properly. The scriptblook hook for this function looks like the following:
 
    {
        [OutputType([bool])]
        [System.Runtime.InteropServices.MarshalAs([System.Runtime.InteropServices.UnmanagedType]::U1)]
        param (
            [System.Runtime.InteropServices.MarshalAs([System.Runtime.InteropServices.UnmanagedType]::LPWStr)]
            [string]$SymlinkFileName,
     
            [System.Runtime.InteropServices.MarshalAs([System.Runtime.InteropServices.UnmanagedType]::LPWStr)]
            [string]$TargetFileName,
     
            [int]$Flags
        )
    }
 
    Currently only a simple `MarshalAs` attribute is supported where the
    `UnamangedType` can be specified. More complex scenarios where things like
    `SizeConst` is used are currently not implemented.
 
VALUE PASS BY REFERENCE
    Passing a value by reference is a way to pass a value type through a
    pointer/reference. This typically means the C function will set the value in
    the unmanaged side and updates the managed value at the same time. It is
    also commonly used when passing in structs by pointers rather than the full
    value. Any of these definitions can simply be an `IntPtr` type but it is
    important to ensure these pointers are valid for the unmanaged code to use.
    Using an invalid pointer can either lead to invalid data being used or more
    commonly crashing the entire process.
    Another option to pass a value type by pointer is to use the
    `PSDetour.Ref<T>` type on the parameter. This is a special type implemented
    by PSDetour that documents a parameter as a reference type and what the
    underlying reference is for. The Win32 API `OpenProcessToken` is an example
    where a value is passed by reference. The C definition for this function
    looks like:
 
    BOOL OpenProcessToken(
      [in] HANDLE ProcessHandle,
      [in] DWORD DesiredAccess,
      [out] PHANDLE TokenHandle
    );
 
    The last parameter is shown as an `[out]` type for a `PHANDLE` (`HANDLE*`)
    which means the `PSDetour.Ref<IntPtr>` type can be used here. The
    scriptblock hook for this function looks like the following:
 
    {
        [OutputType([bool])]
        param (
            [IntPtr]$Process,
            [int]$Access,
            [PSDetour.Ref[IntPtr]]$Token
        )
     
        # The $Var.Value property can be used to get/set the value for the caller
        $Token.Value
     
        # A PSDetour.Ref can be passed directly into the native function as a
        # reference value.
        $this.Invoke($Process, $Access, $Token)
     
        # Otherwise a temp value of the actual type can still be passed in with
        # [ref]. The value still needs to manually be set back to the PSDetour.Ref
        # variable.
        $tempToken = $Token.Value
        $this.Invoke($Process, $Access, [ref]$tempToken)
        $Token.Value = $tempToken.Value
    }
 
    The hook can get the input value using `$Var.Value` and can set a value
    using `$Var.Value = ...`. The `PSDetour.Ref` type is also special here it
    can be passed in directly to `$this.Invoke` without having to use `[ref]`.
    Only value types can be used with `PSDetour.Ref`.