locked
Add-Type in a scriptblock fails. RRS feed

  • Question

  • I suspect I might be pushing the boundaries of my own knowledge a little too hard here. But hopefully not yours...

    I am running into a namespace error when I run Add-Type in a scriptblock on a new powershell instance. I should say there is no issue whatsoever running this from a file or locally. Its specifically when invoked remotely that the compiler seems to have trouble adding the namespace or working out what namespace its in - I am not sure. Here is the code.

        $imp = @"
        [DllImport("cryptnet.dll", CharSet = CharSet.Auto, SetLastError = true)]
        public static extern bool CryptGetObjectUrl(int pszUrlOid,IntPtr pvPara,int dwFlags,byte[] pUrlArray,ref int pcbUrlArray,IntPtr pUrlInfo,ref int pcbUrlInfo,int pvReserved);
    "@
        try
        {
            Add-Type -MemberDefinition $imp -Namespace PKI -Name Cryptnet
        }
        catch {}


    Here's how I am implementing it on a new instance of PowerShell.

    $sb = 
    {
        $imp=@'
        [DllImport("cryptnet.dll", CharSet = CharSet.Auto, SetLastError = true)]public static extern bool CryptGetObjectUrl(int pszUrlOid,IntPtr pvPara,int dwFlags,byte[] pUrlArray,ref int pcbUrlArray,IntPtr pUrlInfo,ref int pcbUrlInfo,int pvReserved);
    '@
        try
        {
            Add-Type -MemberDefinition $imp -Namespace MySpace -Name SomeeSpace
        }
        catch {}
    }
    
    [string[]]$argumentList = "-WindowStyle"
    $argumentList += 'normal'
    $argumentList += '-nologo'
    $argumentList += '-noexit'
    $argumentList += "-Command & {$sb}"
    
    $newProcess = New-Object System.Diagnostics.Process
    $NewProcess.startinfo.FileName = "$pshome\PowerShell.exe"
    $NewProcess.startinfo.Arguments = $argumentList
    $newProcess.Start() | Out-Null


    Fails with:

    The name 'MySapce' does not exist in the current context.

    Any thoughts ? (Solutions I mean)

    Monday, April 27, 2020 6:41 PM

Answers

  • You cannot pass a scriptblock on a command line.  This can only be done in PowerShell.

    Try this to see what I mean.

    $sb = {
    $imp = @'
        [DllImport("cryptnet.dll", CharSet = CharSet.Auto, SetLastError = true)]
        public static extern bool CryptGetObjectUrl(int pszUrlOid,IntPtr pvPara,int dwFlags,byte[] pUrlArray,ref int pcbUrlArray,IntPtr pUrlInfo,ref int pcbUrlInfo,int pvReserved);
    '@
    
    try{
            Add-Type -MemberDefinition $imp -Namespace PKI -Name Cryptnet
    }
    catch{
        throw $_
    }
    Write-Host 'Hello From SB +++++++++++++++++++' -Fore green
    pause
    }
    
    powershell -WindowStyle normal -nologo -noexit -Command $sb
    
    Only running this in PowerShell will work correctly.


    \_(ツ)_/

    • Marked as answer by Anthony Guyon Tuesday, April 28, 2020 11:15 AM
    Tuesday, April 28, 2020 1:07 AM

All replies

  • Your script block has double quotes. Since you are passing that on the command line, I think that you need to account for how that's processed. 

     

    [DllImport("""cryptnet.dll"""

    Run this and you'll see the difference.

    $sb = 
    {
        "Hello world"
    }
    
    $sb2 = 
    {
        """Hello world 2"""
    }
    
    
    [string[]]$argumentList = "-WindowStyle"
    $argumentList += 'normal'
    $argumentList += '-nologo'
    $argumentList += '-noexit'
    $argumentList += "-Command & {$sb}"
    
    $newProcess = New-Object System.Diagnostics.Process
    $NewProcess.startinfo.FileName = "$pshome\PowerShell.exe"
    $NewProcess.startinfo.Arguments = $argumentList
    $newProcess.Start() | Out-Null
    
    [string[]]$argumentList = "-WindowStyle"
    $argumentList += 'normal'
    $argumentList += '-nologo'
    $argumentList += '-noexit'
    $argumentList += "-Command & {$sb2}"
    
    $newProcess = New-Object System.Diagnostics.Process
    $NewProcess.startinfo.FileName = "$pshome\PowerShell.exe"
    $NewProcess.startinfo.Arguments = $argumentList
    $newProcess.Start() | Out-Null

    Monday, April 27, 2020 8:08 PM
  • I tried it and it made no difference to the error message at all.
    Monday, April 27, 2020 10:29 PM
  • I am on Win10 Home 1909, Powershell 5.1.18362.752.

    I referenced https://www.sysadmins.lv/blog-en/how-to-programmatically-extract-cdp-aia-and-ocsp-urls-from-a-digital-certificate.aspx 

    Using this script.... I get an error (it crashes), but I expected that because I don't have the proper parameters filled in. I just wanted to see if I could reference the namespace. 

    $sb = 
    {
        $imp=@'
        [DllImport("""cryptnet.dll"""", CharSet = CharSet.Auto, SetLastError = true)]public static extern bool CryptGetObjectUrl(int pszUrlOid,IntPtr pvPara,int dwFlags,byte[] pUrlArray,ref int pcbUrlArray,IntPtr pUrlInfo,ref int pcbUrlInfo,int pvReserved);
    '@
        try
        {
            Add-Type -MemberDefinition $imp -Namespace MySpace -Name SomeeSpace
        }
        catch {}
        $pcbUrlArray = 0
        $pcbUrlInfo = 0
        [intptr]$pvpara = 0       
        [MySpace.SomeeSpace]::CryptGetObjectUrl(1,$pvpara,$null,$null,[ref]$pcbUrlArray,[IntPtr]::Zero,[ref]$pcbUrlInfo,$null)
    
    }
    
    [string[]]$argumentList = "-WindowStyle"
    $argumentList += 'normal'
    $argumentList += '-nologo'
    $argumentList += '-noexit'
    $argumentList += "-Command & {$sb}"
    
    $newProcess = New-Object System.Diagnostics.Process
    $NewProcess.startinfo.FileName = "$pshome\PowerShell.exe"
    $NewProcess.startinfo.Arguments = $argumentList
    $newProcess.Start() | Out-Null


    Unhandled Exception: System.AccessViolationException: Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
       at MySpace.SomeeSpace.CryptGetObjectUrl(Int32 pszUrlOid, IntPtr pvPara, Int32 dwFlags, Byte[] pUrlArray, Int32& pcbUrlArray, IntPtr pUrlInfo, Int32& pcbUrlInfo, Int32 pvReserved)
       at CallSite.Target(Closure , CallSite , Type , Int32 , IntPtr , Object , Object , PSReference , IntPtr , PSReference , Object )
       at System.Dynamic.UpdateDelegates.UpdateAndExecute9[T0,T1,T2,T3,T4,T5,T6,T7,T8,TRet](CallSite site, T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8)
       at System.Management.Automation.Interpreter.DynamicInstruction`10.Run(InterpretedFrame frame)
       at System.Management.Automation.Interpreter.EnterTryCatchFinallyInstruction.Run(InterpretedFrame frame)
       at System.Management.Automation.Interpreter.EnterTryCatchFinallyInstruction.Run(InterpretedFrame frame)
       at System.Management.Automation.Interpreter.Interpreter.Run(InterpretedFrame frame)
       at System.Management.Automation.Interpreter.LightLambda.RunVoid1[T0](T0 arg0)
       at System.Management.Automation.DlrScriptCommandProcessor.RunClause(Action`1 clause, Object dollarUnderbar, Object inputToProcess)

    I removed the extra quotes from around cryptnet.dll and I get this.

    Add-Type : c:\Users\Dave\AppData\Local\Temp\q2ko5zes\q2ko5zes.0.cs(8) : The name 'cryptnet' does not exist in the
    current context
    c:\Users\Dave\AppData\Local\Temp\q2ko5zes\q2ko5zes.0.cs(7) :     {
    c:\Users\Dave\AppData\Local\Temp\q2ko5zes\q2ko5zes.0.cs(8) : >>>      [DllImport(cryptnet.dll, CharSet = CharSet.Auto,
    SetLastError = true)]public static extern bool CryptGetObjectUrl(int pszUrlOid,IntPtr pvPara,int dwFlags,byte[]
    pUrlArray,ref int pcbUrlArray,IntPtr pUrlInfo,ref int pcbUrlInfo,int pvReserved);
    c:\Users\Dave\AppData\Local\Temp\q2ko5zes\q2ko5zes.0.cs(9) :
    At line:7 char:2
    +  Add-Type -MemberDefinition $imp -Namespace MySpace -Name SomeeSpace
    +  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        + CategoryInfo          : InvalidData: (Microsoft.Power...peCompilerError:AddTypeCompilerError) [Add-Type], Except
       ion
        + FullyQualifiedErrorId : SOURCE_CODE_ERROR,Microsoft.PowerShell.Commands.AddTypeCommand
    
    Unable to find type [MySpace.SomeeSpace].

    Tuesday, April 28, 2020 12:07 AM
  • You cannot pass a scriptblock on a command line.  This can only be done in PowerShell.

    Try this to see what I mean.

    $sb = {
    $imp = @'
        [DllImport("cryptnet.dll", CharSet = CharSet.Auto, SetLastError = true)]
        public static extern bool CryptGetObjectUrl(int pszUrlOid,IntPtr pvPara,int dwFlags,byte[] pUrlArray,ref int pcbUrlArray,IntPtr pUrlInfo,ref int pcbUrlInfo,int pvReserved);
    '@
    
    try{
            Add-Type -MemberDefinition $imp -Namespace PKI -Name Cryptnet
    }
    catch{
        throw $_
    }
    Write-Host 'Hello From SB +++++++++++++++++++' -Fore green
    pause
    }
    
    powershell -WindowStyle normal -nologo -noexit -Command $sb
    
    Only running this in PowerShell will work correctly.


    \_(ツ)_/

    • Marked as answer by Anthony Guyon Tuesday, April 28, 2020 11:15 AM
    Tuesday, April 28, 2020 1:07 AM
  • Why do you need to run this as a new process?
    Tuesday, April 28, 2020 1:20 AM
  • I can't tell you that- because they don't know where you are to shoot you :)

    Seriously.

    Thank you for the responses, they are as always, very much appreciated.

    I've reverted to redirecting stio, and grabbing window handles, to do the needful. It gives us what we need to achieve the ask.


    Tuesday, April 28, 2020 11:16 AM
  • $script = @'
    Param(
        $P1,
        $P2
    )
    $imp = '
        [DllImport("cryptnet.dll", CharSet = CharSet.Auto, SetLastError = true)]
        public static extern bool CryptGetObjectUrl(int pszUrlOid,IntPtr pvPara,int dwFlags,byte[] pUrlArray,ref int pcbUrlArray,IntPtr pUrlInfo,ref int pcbUrlInfo,int pvReserved);
    '
    
    try{
            Add-Type -MemberDefinition $imp -Namespace PKI -Name Cryptnet
    }
    catch{
        throw $_
    }
    [PKI.Cryptnet] | gm -static
    Write-Output "$P1 $P2"
    '@
    $ps = [powershell]::Create()
    [void]$ps.AddScript($script).AddArgument('P1Value').AddArgument('P2Value')
    $ps.Invoke()

    • Edited by jrv Tuesday, April 28, 2020 11:36 AM
    Tuesday, April 28, 2020 11:28 AM