ntdll/NtCreateKey.ps1

function NtCreateKey
{
    <#
    .SYNOPSIS
 
    Creates a new registry key or opens an existing one. Once the driver has finished its manipulations, it must call NtClose to close the handle.
 
    .PARAMETER KeyName
 
    Specifies the full path of the registry key to be created, beginning with \Registry. Passed as the object name to an OBJECT_ATTRIBUTES structure.
 
    .PARAMETER DesiredAccess
 
    Specifies an ACCESS_MASK bitmask for the registry key. Use the constants KeyRead, KeyWrite, KeyExecute, or KeyAllAccess (default).
     
    .NOTES
 
    Author: Jared Atkinson (@jaredcatkinson), Brian Reitz (@brian_psu)
    License: BSD 3-Clause
    Required Dependencies: PSReflect, KEY_ACCESS (Enumeration), OBJECT_ATTRIBUTES (Enumeration), UNICODE_STRING (Enumeration)
    Optional Dependencies: None
 
    (func ntdll NtCreateKey ([UInt32]) @(
        [IntPtr].MakeByRefType(), #_Out_ PHANDLE KeyHandle,
        [Int32], #_In_ ACCESS_MASK DesiredAccess,
        $OBJECT_ATTRIBUTES.MakeByRefType(), #_In_ POBJECT_ATTRIBUTES ObjectAttributes,
        [Int32], #_Reserved_ ULONG TitleIndex,
        $UNICODE_STRING.MakeByRefType(), #_In_opt_ PUNICODE_STRING Class,
        [Int32], #_In_ ULONG CreateOptions,
        [IntPtr] #_Out_opt_ PULONG Disposition
    ) -EntryPoint NtCreateKey),
    .LINK
 
    https://msdn.microsoft.com/en-us/library/windows/hardware/ff566425(v=vs.85).aspx
 
    .EXAMPLE
     
    $MyKeyHandle = NtCreateKey -KeyName "\Registry\Machine\SOFTWARE\Microsoft\Windows\CurrentVersion\Run"
    NtClose -KeyHandle $MyKeyHandle
 
    #>


    param
    (
        [Parameter(Mandatory = $true)]
        [string]
        $KeyName,

        [Parameter()]
        [ValidateSet('KeyRead','KeyWrite','KeyExecute','KeyAllAccess')]
        [string]
        $DesiredAccess = 'KeyAllAccess'
    )
#>
    $KeyHandle = [IntPtr]::Zero
    #$KeyName = "\Registry\Machine\SOFTWARE\Microsoft\Windows\CurrentVersion\Run"

    # Create a UNICODE_STRING for the key name, should be a fully qualified object name
    $kName = RtlInitUnicodeString -SourceString $KeyName

    switch($DesiredAccess) {
        KeyRead      { $DesiredAccessMask = $KEY_ACCESS::KEY_READ }
        KeyWrite     { $DesiredAccessMask = $KEY_ACCESS::KEY_WRITE }
        KeyExecute   { $DesiredAccessMask = $KEY_ACCESS::KEY_EXECUTE }
        KeyAllAccess { $DesiredAccessMask = $KEY_ACCESS::KEY_ALL_ACCESS }
    }
    # InitializeObjectAttributes clone
    $objectAttribute                = [Activator]::CreateInstance($OBJECT_ATTRIBUTES)
    $objectAttribute.Length         = $OBJECT_ATTRIBUTES::GetSize()
    $objectAttribute.RootDirectory  = [IntPtr]::Zero
    $objectAttribute.Attributes     = $OBJ_ATTRIBUTE::OBJ_CASE_INSENSITIVE
    $objectAttribute.ObjectName     = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($UNICODE_STRING::GetSize())
    [System.Runtime.InteropServices.Marshal]::StructureToPtr($kName, $objectAttribute.ObjectName, $true)

    # These are set to NULL for default Security Settings (mirrors the InitializeObjectAttributes macro).
    $objectAttribute.SecurityDescriptor = [IntPtr]::Zero
    $objectAttribute.SecurityQualityOfService = [IntPtr]::Zero

    $TitleIndex = 0 # this is always set to 0 according to MSDN
    # "This parameter is reserved. Device and intermediate drivers should set this parameter to zero."

    $Class = [Activator]::CreateInstance($UNICODE_STRING)
    $CreateOptions = $REG_OPTION::REG_OPTION_NON_VOLATILE
    $Disposition = [IntPtr]::Zero

    $Success = $ntdll::NtCreateKey([ref]$KeyHandle, $DesiredAccessMask, [ref]$objectAttribute, $TitleIndex, [ref]$Class, $CreateOptions, $Disposition)

    if(-not $Success) 
    {
        Write-Debug "NtCreateKey Error: $(([ComponentModel.Win32Exception] $LastError).Message)"
    }
    Write-Output $KeyHandle

    # free our memory after allocation
    [System.Runtime.InteropServices.Marshal]::FreeHGlobal($objectAttribute.ObjectName)
}