PowerRunAsSystem.psm1

<#-------------------------------------------------------------------------------
 
    .Developer
        Jean-Pierre LESUEUR (@DarkCoderSc)
        https://www.twitter.com/darkcodersc
        https://github.com/DarkCoderSc
        www.phrozen.io
        jplesueur@phrozen.io
        PHROZEN
    .License
        Apache License
        Version 2.0, January 2004
        http://www.apache.org/licenses/
    .Disclaimer
        We are doing our best to prepare the content of this app. However, PHROZEN SASU and / or
        Jean-Pierre LESUEUR cannot warranty the expressions and suggestions of the contents,
        as well as its accuracy. In addition, to the extent permitted by the law,
        PHROZEN SASU and / or Jean-Pierre LESUEUR shall not be responsible for any losses
        and/or damages due to the usage of the information on our app.
        By using our app, you hereby consent to our disclaimer and agree to its terms.
        Any links contained in our app may lead to external sites are provided for
        convenience only. Any information or statements that appeared in these sites
        or app are not sponsored, endorsed, or otherwise approved by PHROZEN SASU and / or
        Jean-Pierre LESUEUR. For these external sites, PHROZEN SASU and / or Jean-Pierre LESUEUR
        cannot be held liable for the availability of, or the content located on or through it.
        Plus, any losses or damages occurred from using these contents or the internet
        generally.
         
    .Ideas
        - Capture SYSTEM Process Stdin and Stdout/err in current session.
 
-------------------------------------------------------------------------------#>


<#
    --------------------------
    - STRUCTURES MEMORY MAPS -
    --------------------------
 
    typedef struct _WTS_SESSION_INFOA {
        // x86-32: 0x4 Bytes | Padding = 0x0 | Offset: 0x0
        // x86-64: 0x4 Bytes | Padding = 0x4 | Offset: 0x0
        DWORD SessionId;
         
        // x86-32: 0x4 Bytes | Padding = 0x0 | Offset: 0x4
        // x86-64: 0x8 Bytes | Padding = 0x0 | Offset: 0x8
        LPSTR pWinStationName;
 
        // x86-32: 0x1 Bytes | Padding = 0x3 | Offset: 0x8
        // x86-64: 0x1 Bytes | Padding = 0x7 | Offset: 0x10
        WTS_CONNECTSTATE_CLASS State;
    } WTS_SESSION_INFOA, *PWTS_SESSION_INFOA;
 
    // x86-32 Struct Size: 0x4(+0x0) + 0x4(+0x0) + 0x1(+0x3) = 0xc (12 Bytes)
    // x86-64 Struct Size: 0x4(+0x4) + 0x8(+0x0) + 0x1(+0x7) = 0x18 (24 Bytes)
 
    ---
 
    typedef struct _STARTUPINFOW {
        // x86-32: 0x4 Bytes | Padding = 0x0 | Offset: 0x0
        // x86-64: 0x4 Bytes | Padding = 0x4 | Offset: 0x0
        DWORD cb;
 
        // x86-32: 0x4 Bytes | Padding = 0x0 | Offset: 0x4
        // x86-64: 0x8 Bytes | Padding = 0x0 | Offset: 0x8
        LPWSTR lpReserved;
 
        // x86-32: 0x4 Bytes | Padding = 0x0 | Offset: 0x8
        // x86-64: 0x8 Bytes | Padding = 0x0 | Offset: 0x10
        LPWSTR lpDesktop;
 
        // x86-32: 0x4 Bytes | Padding = 0x0 | Offset: 0xC
        // x86-64: 0x8 Bytes | Padding = 0x0 | Offset: 0x18
        LPWSTR lpTitle;
 
        // x86-32: 0x4 Bytes | Padding = 0x0 | Offset: 0x10
        // x86-64: 0x4 Bytes | Padding = 0x0 | Offset: 0x20
        DWORD dwX;
 
        // x86-32: 0x4 Bytes | Padding = 0x0 | Offset: 0x14
        // x86-64: 0x4 Bytes | Padding = 0x0 | Offset: 0x24
        DWORD dwY;
 
        // x86-32: 0x4 Bytes | Padding = 0x0 | Offset: 0x18
        // x86-64: 0x4 Bytes | Padding = 0x0 | Offset: 0x28
        DWORD dwXSize;
 
        // x86-32: 0x4 Bytes | Padding = 0x0 | Offset: 0x1C
        // x86-64: 0x4 Bytes | Padding = 0x0 | Offset: 0x2C
        DWORD dwYSize;
 
        // x86-32: 0x4 Bytes | Padding = 0x0 | Offset: 0x20
        // x86-64: 0x4 Bytes | Padding = 0x0 | Offset: 0x30
        DWORD dwXCountChars;
 
        // x86-32: 0x4 Bytes | Padding = 0x0 | Offset: 0x24
        // x86-64: 0x4 Bytes | Padding = 0x0 | Offset: 0x34
        DWORD dwYCountChars;
 
        // x86-32: 0x4 Bytes | Padding = 0x0 | Offset: 0x28
        // x86-64: 0x4 Bytes | Padding = 0x0 | Offset: 0x38
        DWORD dwFillAttribute;
 
        // x86-32: 0x4 Bytes | Padding = 0x0 | Offset: 0x2C
        // x86-64: 0x4 Bytes | Padding = 0x0 | Offset: 0x3C
        DWORD dwFlags;
 
        // x86-32: 0x2 Bytes | Padding = 0x0 | Offset: 0x30
        // x86-64: 0x2 Bytes | Padding = 0x0 | Offset: 0x40
        WORD wShowWindow;
 
        // x86-32: 0x2 Bytes | Padding = 0x0 | Offset: 0x32
        // x86-64: 0x2 Bytes | Padding = 0x4 | Offset: 0x42
        WORD cbReserved2;
 
        // x86-32: 0x4 Bytes | Padding = 0x0 | Offset: 0x34
        // x86-64: 0x8 Bytes | Padding = 0x0 | Offset: 0x48
        LPBYTE lpReserved2;
 
        // x86-32: 0x4 Bytes | Padding = 0x0 | Offset: 0x38
        // x86-64: 0x8 Bytes | Padding = 0x0 | Offset: 0x50
        HANDLE hStdInput;
 
        // x86-32: 0x4 Bytes | Padding = 0x0 | Offset: 0x3C
        // x86-64: 0x8 Bytes | Padding = 0x0 | Offset: 0x58
        HANDLE hStdOutput;
 
        // x86-32: 0x4 Bytes | Padding = 0x0 | Offset: 0x40
        // x86-64: 0x8 Bytes | Padding = 0x0 | Offset: 0x60
        HANDLE hStdError;
    } STARTUPINFOW, *LPSTARTUPINFOW;
 
    // x86-32 Struct Size: 0x44 (68 Bytes)
    // x86-64 Struct Size: 0x68 (104 Bytes)
#>


Add-Type @"
    using System;
    using System.Security;
    using System.Runtime.InteropServices;
 
    [StructLayout(LayoutKind.Sequential, Pack = 1)]
    public struct TokenPrivilege
    {
        public UInt32 PrivilegeCount;
        public long Luid;
        public UInt32 Attributes;
    }
 
    public static class ADVAPI32
    {
        [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
        public static extern bool LookupPrivilegeValue(
            IntPtr lpSystemName,
            string lpName,
            ref long lpLuid
        );
 
        [DllImport("advapi32.dll", SetLastError=true)]
        public static extern bool AdjustTokenPrivileges(
            IntPtr TokenHandle,
            bool DisableAllPrivileges,
            ref TokenPrivilege NewState,
            UInt32 BufferLengthInBytes,
            IntPtr PreviousState,
            IntPtr ReturnLengthInBytes
        );
 
        [DllImport("advapi32.dll", SetLastError = true)]
        public static extern bool ImpersonateNamedPipeClient(
            IntPtr hNamedPipe
        );
 
        [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
        public static extern bool CreateProcessWithToken(
            IntPtr hToken,
            UInt32 dwLogonFlags,
            IntPtr lpApplicationName,
            string lpCommandLine,
            uint dwCreationFlags,
            IntPtr lpEnvironment,
            IntPtr lpCurrentDirectory,
            IntPtr lpStartupInfo,
            ref IntPtr lpProcessInformation
        );
 
        [DllImport("advapi32.dll", SetLastError = true)]
        public static extern bool RevertToSelf();
    }
"@
 

class WinAPIException: System.Exception {
    WinAPIException([string] $ApiName) : base (
        [string]::Format(
            "WinApi Exception -> {0}, LastError: {1}",
            $ApiName,
            [System.Runtime.InteropServices.Marshal]::GetLastWin32Error().ToString()
        )
    ) 
    {}
}

function Test-Impersonating
{
    <#
        .SYNOPSIS
            Check whether or not current thread is impersonating another user.
    #>

    return ([System.Security.Principal.WindowsIdentity]::GetCurrent().ImpersonationLevel -eq [System.Management.ImpersonationLevel]::Impersonate)
}

$global:InvokeInteractiveProcessScriptBlock = {
    Add-Type @"
        using System;
        using System.Security;
        using System.Runtime.InteropServices;
 
        public static class WTSAPI32
        {
            [DllImport("wtsapi32.dll", SetLastError = true)]
            public static extern bool WTSEnumerateSessions(
                IntPtr hServer,
                UInt32 Reserved,
                UInt32 Version,
                ref IntPtr ppSessionInfo,
                ref UInt32 pCount
            );
 
            [DllImport("wtsapi32.dll")]
            public static extern void WTSFreeMemory(IntPtr pMemory);
        }
 
        public static class ADVAPI32
        {
            [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
            public static extern bool CreateProcessAsUser(
                IntPtr hToken,
                string lpApplicationName,
                string lpCommandLine,
                IntPtr lpProcessAttributes,
                IntPtr lpThreadAttributes,
                bool bInheritHandles,
                uint dwCreationFlags,
                IntPtr lpEnvironment,
                IntPtr lpCurrentDirectory,
                IntPtr lpStartupInfo,
                ref IntPtr lpProcessInformation
            );
 
            [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
            public static extern bool DuplicateTokenEx(
                IntPtr hExistingToken,
                uint dwDesiredAccess,
                IntPtr lpTokenAttributes,
                byte ImpersonationLevel,
                byte TokenType,
                ref IntPtr phNewToken
            );
 
            [DllImport("advapi32.dll", SetLastError = true)]
            public static extern bool SetTokenInformation(
                IntPtr TokenHandle,
                byte TokenInformationClass,
                ref UInt32 TokenInformation,
                UInt32 TokenInformationLength
            );
        }
 
        public static class Kernel32
        {
            [DllImport("kernel32.dll", SetLastError = true)]
            public static extern bool CloseHandle(IntPtr handle);
        }
"@
  

    if (-not [Security.Principal.WindowsIdentity]::GetCurrent().IsSystem)
    {
        throw "You must be system user to run an interactive system process."
    }

    # Get Active Session Id

    [IntPtr] $pSessionArray = [IntPtr]::Zero
    [UInt32] $sessionCount = 0

    try
    {
        if (-not [WTSAPI32]::WTSEnumerateSessions([IntPtr]::Zero, 0, 1, [ref]$pSessionArray, [ref]$sessionCount))
        {
            throw "Could not enumerate sessions."
        }
        try
        {                    
            #$structSize = [Runtime.InteropServices.Marshal]::SizeOf([System.Type][WTS_SESSION_INFO])
            if ([Environment]::Is64BitProcess)
            {
                $structSize = 0x18  
                $structOffset_State = 0x10  
            }
            else
            {
                $structSize = 0xc
                $structOffset_State = 0x8
            }
                        
            $activeSession = -1            

            for ($i; $i -lt $sessionCount; $i++)
            {
                [IntPtr] $pOffset = [IntPtr]([Int64]$pSessionArray + ($i * $structSize))

                #$sessionInfo = [WTS_SESSION_INFO][Runtime.InteropServices.Marshal]::PtrToStructure($pOffset, [System.Type][WTS_SESSION_INFO])
                $curSessionId = [System.Runtime.InteropServices.Marshal]::ReadInt32($pOffset, 0x0)
                $curSessionState = [System.Runtime.InteropServices.Marshal]::ReadInt32($pOffset, $structOffset_State)

                $WTSActive = 0
                if ($curSessionState -eq $WTSActive)
                {                      
                    $activeSession = $curSessionId

                    break
                }                
            }
        }
        finally
        {
            if ($pSessionArray -ne [IntPtr]::Zero)
            {
                [WTSAPI32]::WTSFreeMemory($pSessionArray)
            }
        }

        if ($activeSession -eq -1)
        {
            throw "Could not found active session."
        }

        # Create new system process in Active Session

        <#
            $token = [IntPtr]::Zero
            $ALL_ACCESS = 0xF01FF
 
            if (-not [ADVAPI32]::OpenProcessToken([Kernel32]::GetCurrentProcess(), $ALL_ACCESS, [ref]$token))
            {
                throw "OpenProcessToken"
            }
        #>


        $token = [Security.Principal.WindowsIdentity]::GetCurrent().Token
        $newToken = [IntPtr]::Zero

        $MAXIMUM_ALLOWED = 0x02000000
        $SecurityIdentification = 0x2
        $TokenPrimary = 0x1

        if (-not [ADVAPI32]::DuplicateTokenEx($token, $MAXIMUM_ALLOWED, [IntPtr]::Zero, $SecurityIdentification, $TokenPrimary, [ref]$newToken))
        {
            throw "Could not duplicate token."
        }    

        $TokenSessionId = 0xc

        if (-not [ADVAPI32]::SetTokenInformation($newToken, $TokenSessionId, [ref]$activeSession, [UInt32][Runtime.InteropServices.Marshal]::SizeOf($activeSession)))
        {
            throw "Could not set token information."
        }

        $STARTF_USESHOWWINDOW = 0x1
        $SW_SHOW = 0x5
    
        if ([Environment]::Is64BitProcess)
        {
            $structSize = 0x68
            $structOffset_dwFlags = 0x3c
            $structOffset_wShowWindow = 0x40
        }
        else
        {
            $structSize = 0x44
            $structOffset_dwFlags = 0x2c
            $structOffset_wShowWindow = 0x30
        }

        $pSTARTUPINFO = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($structSize)
        try
        {
            # ZeroMemory
            for ($i = 0; $i -lt $structSize; $i++)
            {
                [System.Runtime.InteropServices.Marshal]::WriteByte($pSTARTUPINFO, $i, 0x0)    
            }                   

            [System.Runtime.InteropServices.Marshal]::WriteInt32($pSTARTUPINFO, 0x0, $structSize) # cb
            [System.Runtime.InteropServices.Marshal]::WriteInt32($pSTARTUPINFO, $structOffset_dwFlags, $STARTF_USESHOWWINDOW) # dwFlags
            [System.Runtime.InteropServices.Marshal]::WriteInt16($pSTARTUPINFO, $structOffset_wShowWindow, $SW_SHOW) # wShowWindow
        
            $processInfo = [IntPtr]::Zero

            $CREATE_NEW_CONSOLE = 0x10

            if (-not [ADVAPI32]::CreateProcessAsUser(
                $newToken,
                "cmd.exe",
                "/c ""start powershell.exe""",
                [IntPtr]::Zero,
                [IntPtr]::Zero,
                $false,
                $CREATE_NEW_CONSOLE,
                [IntPtr]::Zero,
                [IntPtr]::Zero,
                $pSTARTUPINFO,
                [ref]$processInfo
            ))
            {
                throw "Could not create process as user."
            }  
        }
        finally
        {
            [System.Runtime.InteropServices.Marshal]::FreeHGlobal($pSTARTUPINFO)
        }
    }
    catch
    {
        # Uncomment for debug
        # ([string]::Format("$_ LastError:{0}", [Runtime.InteropServices.Marshal]::GetLastWin32Error().ToString())) | Out-File "c:\temp\error.log"
    } 
    finally
    {
        <#
            if ($token -ne [IntPtr]::Zero)
            {
                [Kernel32]::CloseHandle($token)
            }
        #>


        if ($newToken -ne [IntPtr]::Zero)
        {
            $null = [Kernel32]::CloseHandle($newToken)
        }
    }       
}

function Test-Administrator
{
    <#
        .SYNOPSIS
            Check if current user is administrator.
    #>

    $windowsPrincipal = New-Object Security.Principal.WindowsPrincipal(
        [Security.Principal.WindowsIdentity]::GetCurrent()
    )
    
    return $windowsPrincipal.IsInRole(
        [Security.Principal.WindowsBuiltInRole]::Administrator
    )    
}

function Test-AdministratorOrRaise
{
    <#
        .SYNOPSIS
            Check if current user is administrator, if not it will raise an exception.
    #>

    if (-not (Test-Administrator))
    {
        throw "Insufficient Privilege: You must have administrator privilege to perform this action."
    }
}

function Get-RandomString
{
    <#
        .SYNOPSIS
            Return a random string composed of a-Z and 0-9
    #>

    $charList = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"

    return -join ((1..15) | ForEach-Object { Get-Random -Input $charList.ToCharArray() })
}

function Invoke-SystemCommand
{
    <#
        .SYNOPSIS
            Execute program as NT AUTHORITY/SYSTEM with optional arguments.
 
        .PARAMETER Execute
            Program to execute as System.
 
        .PARAMETER Argument
            Optional argument(s) to pass to program to execute.
    #>

    param(
        [string] $Execute = "powershell.exe",
        [string] $Argument = "-Command ""whoami | Out-File C:\result.txt"""
    )

    Test-AdministratorOrRaise

    if (Test-Impersonating)
    {
        throw "Could use this function while impersonating user."
    }

    $taskName = Get-RandomString
    
    if ($Argument)
    {
        $action = New-ScheduledTaskAction -Execute $Execute -Argument $Argument
    }
    else
    {
        $action = New-ScheduledTaskAction -Execute $Execute
    }

    $null = Register-ScheduledTask -Force -Action $action -TaskName $taskName -User "NT AUTHORITY\SYSTEM"
    try
    {
        Start-ScheduledTask $taskName
    }
    finally
    {
        Unregister-ScheduledTask -TaskName $taskName -Confirm:$false
    }
}

function Invoke-InteractiveSystemPowerShell
{
    <#
        .SYNOPSIS
            Spawn a SYSTEM process in Active Microsoft Windows Session.
    #>


    if (Test-Impersonating)
    {
        throw "Could use this function while impersonating user."
    }

    $secondStageBlock = { 
        try
        {        
            $pipeClient = New-Object System.IO.Pipes.NamedPipeClientStream(".", "PIPENAME", [System.IO.Pipes.PipeDirection]::In)

            $pipeClient.Connect(5 * 1000)

            $reader = New-Object System.IO.StreamReader($pipeClient)

            $nextStage = $reader.ReadLine()

            Invoke-Expression([System.Text.Encoding]::ASCII.GetString([System.Convert]::FromBase64String($nextStage)))            
        }
        finally
        {
            if ($reader)
            {
                $reader.Close()
            }

            if ($pipeClient)
            {
                $pipeClient.Dispose()
            }            
        }        
    }

    $pipeName = Get-RandomString

    $encodedBlock =  [Convert]::ToBase64String(
        [System.Text.Encoding]::ASCII.GetBytes(
            ([string]$secondStageBlock).replace('PIPENAME', $pipeName)
        )
    )    

    # If using bellow technique, replace ::ASCII by ::Unicode above.
    #$command = [string]::Format(
    # "-NoProfile -EncodedCommand {0}""",
    # $encodedBlock
    #)

    $command = [string]::Format(
        "Invoke-Expression([System.Text.Encoding]::ASCII.GetString([System.Convert]::FromBase64String('{0}')))",
        $encodedBlock
    )

    Invoke-SystemCommand -Argument $command

    try
    {
        $pipeServer = New-Object System.IO.Pipes.NamedPipeServerStream($pipeName, [System.IO.Pipes.PipeDirection]::Out)

        $pipeServer.WaitForConnection()

        $writer = New-Object System.IO.StreamWriter($pipeServer)
        $writer.AutoFlush = $true

        $writer.WriteLine([Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes(([string]$global:InvokeInteractiveProcessScriptBlock))))
    }
    finally
    {
        if ($writer)
        {
            $writer.Close()
        }

        if ($pipeServer)
        {
            $pipeServer.Dispose()
        }
    }
}

function Set-CurrentProcessPrivilege
{
    <#
        .SYNOPSIS
            Ajust current process privilege.
 
        .PARAMETER PrivilegeName
            Type: String
            Default: None
            Description: The name of the privilege to adjust.
 
        .PARAMETER Enable
            Type: Boolean
            Default: True
            Description:
                True: Enable privilege
                False: Disable privilege
    #>

    param(
        [Parameter(Mandatory=$True)]
        $PrivilegeName,

        [bool] $Enable = $true
    )
    
    try
    {        
        # Grab current process token using WinAPI (Commented)
        <#
            $curToken = [IntPtr]::Zero
            $TOKEN_ADJUST_PRIVILEGES = 0x20
            $TOKEN_QUERY = 0x8
 
            $result = [ADVAPI32]::OpenProcessToken(
                [System.Diagnostics.Process]::GetCurrentProcess().Handle,
                $TOKEN_ADJUST_PRIVILEGES -bor $TOKEN_QUERY,
                [ref] $curToken
            )
            if (-not $result)
            {
                throw "OpenProcessToken"
            }
        #>
       

        [long] $luid = 0

        $result = [ADVAPI32]::LookupPrivilegeValue(
            [IntPtr]::Zero,
            $PrivilegeName,
            [ref] $luid
        )
        if (-not $result)
        {
            throw [WinAPIException]::New("LookupPrivilegeValue")
        }     

        $SE_PRIVILEGE_ENABLED = 0x2

        if ($Enable)
        {
            $attr = $SE_PRIVILEGE_ENABLED
        }
        else
        {
            $attr = 0x0
        }

        $tokenPrivilege = [TokenPrivilege]::New()
        $tokenPrivilege.PrivilegeCount = 1        
        $tokenPrivilege.Luid = $luid   
        $tokenPrivilege.Attributes = $attr        

        $result = [ADVAPI32]::AdjustTokenPrivileges(
            [Security.Principal.WindowsIdentity]::GetCurrent().Token,
            $false,
            [ref] $tokenPrivilege,
            0,
            [IntPtr]::Zero,
            [IntPtr]::Zero
        )
        if (-not $result)
        {            
            throw [WinAPIException]::New("AdjustTokenPrivileges")
        }

        return ([System.Runtime.InteropServices.Marshal]::GetLastWin32Error() -eq 0)
    }    
    finally
    {
        # Release Token Handle if grabbind current process token using WinAPI
        <#
            if ($curToken -ne [IntPtr]::Zero)
            {
                $null = [Kernel32]::CloseHandle($curToken)
            }
        #>

    }
}

function Write-CurrentUser
{
    <#
        .SYNOPSIS
            Output current username to terminal.
    #>

    Write-Host "Current User: " -NoNewLine
    Write-Host ([System.Security.Principal.WindowsIdentity]::GetCurrent().Name) -ForegroundColor Green -NoNewLine
    Write-Host " (" -NoNewLine
    Write-Host ([Security.Principal.WindowsIdentity]::GetCurrent().Token) -NoNewLine -ForegroundColor Cyan
    

    if (Test-Impersonating)
    {
        Write-Host " - Impersonated" -NoNewLine
    }

    Write-Host ")"
}

function Invoke-ImpersonateSystem
{
    <#
        .SYNOPSIS
            Impersonate SYSTEM User using NamedPipes. After calling this command,
            current thread will impersonate the SYSTEM User. You will be able to spawn a new
            process as SYSTEM using the impersonated token.
 
        .DESCRIPTION
            Use the Invoke-RevertToSelf to stop impersonation.
    #>

    Test-AdministratorOrRaise

    if (Test-Impersonating)
    {
        throw "You are already impersonating a user."
    }

    $script = {
        try
        {
            $pipeClient = New-Object System.IO.Pipes.NamedPipeClientStream(".", "PIPENAME", [System.IO.Pipes.PipeDirection]::Out)

            $pipeClient.Connect(10 * 1000)

            $writer = New-Object System.IO.StreamWriter($pipeClient)            
            $writer.AutoFlush = $true

            $writer.Write("A")
        }
        finally
        {
            if ($writer)
            {
                $writer.Close()
            }

            if ($pipeClient)
            {
                $pipeClient.Dispose()
            }
        }        
    }

    # Just in case it is not already.
    try
    {
        $null = Set-CurrentProcessPrivilege -PrivilegeName "SeImpersonatePrivilege"
    }
    catch
    {}
    
    try
    {
        Write-CurrentUser

        $pipeName = Get-RandomString

        $pipeServer = New-Object System.IO.Pipes.NamedPipeServerStream($pipeName, [System.IO.Pipes.PipeDirection]::In)        

        $encodedBlock =  [Convert]::ToBase64String(
            [System.Text.Encoding]::ASCII.GetBytes(
                ([string]$script).replace('PIPENAME', $pipeName)
            )
        )    

        $command = [string]::Format(
            "Invoke-Expression([System.Text.Encoding]::ASCII.GetString([System.Convert]::FromBase64String('{0}')))",
            $encodedBlock
        )

        Invoke-SystemCommand -Argument $command

        $pipeServer.WaitForConnection()

        $reader = New-Object System.IO.StreamReader($pipeServer)

        $null = $reader.Read()

        $pipeHandle = $pipeServer.SafePipeHandle.DangerousGetHandle()        

        if (-not [ADVAPI32]::ImpersonateNamedPipeClient($pipeHandle))
        {
            throw [WinAPIException]::New("ImpersonateNamedPipeClient")
        }    

        Write-CurrentUser
    }
    finally
    {
        if ($reader)
        {
            $reader.Close()
        }

        if ($pipeServer)
        {
            $pipeServer.Dispose()
        }    
    }
}

function Invoke-ImpersonatedProcess
{
    <#
        .SYNOPSIS
            Invoke a new process as Impersonated User.
 
        .DESCRIPTION
            Requires current thread to impersonate a user.
 
        .PARAMETER CommandLine
            Type: String
            Default: PowerShell.exe
            Description: The command line to run as impersonated user.
    #>

    param(
        [string] $CommandLine = "powershell.exe"
    )

    if (-not (Test-Impersonating))
    {
        throw "You must first impersonate a user."
    }

    $STARTF_USESHOWWINDOW = 0x1
    $STARTF_USESTDHANDLES = 0x100
    $SW_SHOW = 0x5

    if ([Environment]::Is64BitProcess)
    {
        $structSize = 0x68
        $structOffset_dwFlags = 0x3c
        $structOffset_wShowWindow = 0x40            
    }
    else
    {
        $structSize = 0x44
        $structOffset_dwFlags = 0x2c
        $structOffset_wShowWindow = 0x30            
    }

    $pSTARTUPINFO = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($structSize)
    try
    {
        # ZeroMemory
        for ($i = 0; $i -lt $structSize; $i++)
        {
            [System.Runtime.InteropServices.Marshal]::WriteByte($pSTARTUPINFO, $i, 0x0)    
        }                   

        [System.Runtime.InteropServices.Marshal]::WriteInt32($pSTARTUPINFO, 0x0, $structSize) # cb
        [System.Runtime.InteropServices.Marshal]::WriteInt32($pSTARTUPINFO, $structOffset_dwFlags, $STARTF_USESHOWWINDOW -bor $STARTF_USESTDHANDLES) # dwFlags
        [System.Runtime.InteropServices.Marshal]::WriteInt16($pSTARTUPINFO, $structOffset_wShowWindow, $SW_SHOW) # wShowWindow
    
        $processInfo = [IntPtr]::Zero                        

        if (-not [ADVAPI32]::CreateProcessWithToken(
            [IntPtr]([Security.Principal.WindowsIdentity]::GetCurrent().Token),
            0x0,
            [IntPtr]::Zero,
            $CommandLine,                
            0x0,
            [IntPtr]::Zero,
            [IntPtr]::Zero,
            $pSTARTUPINFO,
            [ref]$processInfo
        ))
        {
            throw [WinAPIException]::New("CreateProcessWithToken")
        }  
    }
    finally
    {
        [System.Runtime.InteropServices.Marshal]::FreeHGlobal($pSTARTUPINFO)
    }
}

function Invoke-RevertToSelf
{
    <#
        .SYNOPSIS
            Stop impersonating user.
    #>


    if (-not (Test-Impersonating))
    {
        throw "You are currently not impersonating a user."
    }

    Write-CurrentUser

    Write-Host ([string]::Format("Result: {0}", [ADVAPI32]::RevertToSelf()))

    Write-CurrentUser
}

try {  
    Export-ModuleMember -Function Invoke-SystemCommand
    Export-ModuleMember -Function Invoke-InteractiveSystemPowerShell
    Export-ModuleMember -Function Set-CurrentProcessPrivilege
    Export-ModuleMember -Function Invoke-ImpersonateSystem
    Export-ModuleMember -Function Invoke-ImpersonatedProcess
    Export-ModuleMember -Function Invoke-RevertToSelf
} catch {}