Как получить доступ к 64-битному реестру из 32-битного экземпляра Powershell?

Если вы запускаете 32-битный экземпляр Powershell (% SystemRoot% \ syswow64 \ WindowsPowerShell \ v1.0 \ powershell.exe), то провайдер реестра видит только ограниченные 32-битные части реестра.

**32-bit console**
PS> (dir HKLM:\SOFTWARE | measure).count - (dir HKLM:\SOFTWARE\wow6432node | measure).count

0

**64-bit console**
PS> (dir HKLM:\SOFTWARE | measure).count - (dir HKLM:\SOFTWARE\wow6432node | measure).count

-5

Есть ли способ заставить провайдера перейти в 64-битный режим? Я мог бы перейти к API [Microsoft.Win32] .Net или, может быть, к WMI, но я бы предпочел этого не делать. Я использую Powershell v2 CTP3, если это вообще расширяет возможности.


person Richard Berg    schedule 10.03.2009    source источник


Ответы (8)


Когда Powershell работает как 32-битный процесс, я не знаю механизма, позволяющего «переключить его» в 64-битный режим. Весь смысл поддержки виртуализации в 64-битных системах состоит в том, чтобы заставить 32-битные процессы поверить, что они живут в 32-битной ОС ...

Тем не менее, это говорит о том, что я использовал следующую технику в прошлом, и она мне очень понравилась (следующий код был протестирован на Vista SP1 x64 с Powershell v1). Этот метод основан на том факте, что исполняемые файлы .NET "Any CPU" будут работать как 64-битный процесс даже при вызове из 32-битного процесса. Шаги, которые мы будем выполнять:

  1. Скомпилируйте короткую программу на C #, которая запустит powershell (то есть очень простую реализацию "вилки" :-))
  2. Запустите скомпилированную программу C #
  3. Скомпилированная программа на C # запустит Powershell, но поскольку это «Любой процессор», он будет работать как 64-битный процесс, поэтому он запустит 64-битный Powershell (обратите внимание, что, поскольку это всего лишь доказательство концепции, я ожидаю, что PowerShell будет в твой путь')
  4. Новый 64-битный Powershell будет запускать командлет по нашему выбору.

Это снимок экрана вышеупомянутого в действии (обратите внимание на разрядность процессов): http://img3.imageshack.us/img3/3248/powershellfork.png

Следующая программа ожидает, что все перечисленные файлы будут находиться в одном каталоге. Я рекомендую создать тестовый каталог, например C: \ Temp \ PowershellTest, и хранить там все файлы).

Точкой входа в программу будет простая команда:

# file "test.ps1"
$basePath = Split-Path -resolve $myInvocation.MyCommand.Path
$exe = Join-Path $basePath test.exe
&"$env:SystemRoot\Microsoft.NET\Framework\v3.5\csc.exe" /nologo /target:exe /out:$exe (Join-Path $basePath test.cs)
&$exe (Join-Path $basePath visibility.ps1)

Он запускает csc (32-битный, но это не имеет значения :-)), а затем запускает результат компилятора csc, передавая один аргумент (полный путь к) visibility.ps1 (это командлет, который мы хотим запустить в 64-битной Powershell).

Код C # тоже очень прост:

// file "test.cs"
using System.Diagnostics;
static class Program {
    static int Main(string[] args) {
        ProcessStartInfo i = new ProcessStartInfo("powershell", args[0]);
        i.UseShellExecute = false;
        using(Process p = Process.Start(i)) {
            p.WaitForExit();
            return p.ExitCode;
        }
    }
}

И, наконец, ваш скрипт "видимости":

# file "visibility.ps1"
(dir HKLM:\SOFTWARE).count - (dir HKLM:\SOFTWARE\wow6432node).count

Запуск сценария входа из 32-битной Powershell теперь дает желаемый результат (чтобы показать, что я не обманываю, я сначала запускаю сценарий видимости напрямую, а затем используя нашу технику вилки):

http://img3.imageshack.us/img3/2766/powershellrunc.png

person Milan Gardian    schedule 10.03.2009

С .NET API вы можете читать 64-битные значения следующим образом:

$key = [Microsoft.Win32.RegistryKey]::OpenBaseKey([Microsoft.Win32.RegistryHive]::LocalMachine, [Microsoft.Win32.RegistryView]::Registry64)
$subKey =  $key.OpenSubKey("SOFTWARE\Microsoft\.NETFramework")
$root = $subKey.GetValue("InstallRoot")
person SergVro    schedule 15.10.2013
comment
Обратите внимание, что это будет работать только с .NET 4.0+. - person Koraktor; 06.03.2014
comment
Очень простое / чистое решение, которое хорошо работает для доступа как к 32-битным, так и к 64-битным представлениям реестра. Я даже не стал пытаться прочитать / понять принятый ответ. Но только .NET 4.0+, как указывает @Koraktor (я думаю, это означает PowerShell 3.0+). - person James Johnston; 24.06.2015
comment
Основным недостатком является то, что вы больше не можете использовать Get-Item или Get-ChildItem, но, по крайней мере, это просто работает. - person jpmc26; 21.11.2018
comment
Не работает для 64-битных ключей реестра, невидимых для 32-битных процессов (например, SOFTWARE \ Microsoft \ Windows \ CurrentVersion \ AppModelUnlock), GetValue завершается с ошибкой. 32-разрядная команда Reg с флагом / reg: 64 работает, но в ней отсутствует интеграция с PowerShell. - person whenrybruce; 18.12.2018
comment
@whenrybruce Не уверен, какую версию PowerShell вы тестировали. У меня все работает нормально (PS 5.1) - pastebin.com/irLQ4ksm - person Peter Schneider; 21.02.2020

Я считаю, что встроенный командлет Start-Job позволит вам проверить 64-битный реестр из 32-битного экземпляра.

Если нет, используйте Invoke-Command для возврата к локальной машине. 64-битная машина будет иметь две конечные точки (64-битную и 32-битную), и 64-битная конечная точка будет по умолчанию.

Пример создания значения реестра и обеспечения его 64-битного пути, а не 32.

Invoke-Command -scriptblock {
    New-ItemProperty -Path HKLM:\SOFTWARE\Acme 
                     -Name NameofNewReg 
                     -PropertyType String -Value "1"
    } -computername .
person Start-Automating    schedule 10.01.2011
comment
Отличный трюк! 'invoke-command -scriptblock {gci ‹ваш 64-битный ключ реестра›} -computername.' работает. Спасибо! - person Ameer Deen; 17.01.2011

Если переменная среды PROCESSOR_ARCHITEW6432 существует и имеет значение AMD64, то вы используете 32-битную версию на 64-битной машине. Затем вам нужно запустить powershell по виртуальному 64-битному пути% windir% \ sysnative.

if ($env:PROCESSOR_ARCHITEW6432 -eq "AMD64") {
    write-warning "changing from 32bit to 64bit PowerShell..."
    $powershell=$PSHOME.tolower().replace("syswow64","sysnative").replace("system32","sysnative")

    if ($myInvocation.Line) {
        &"$powershell\powershell.exe" -NonInteractive -NoProfile $myInvocation.Line
    } else {
        &"$powershell\powershell.exe" -NonInteractive -NoProfile -file "$($myInvocation.InvocationName)" $args
    }

    exit $lastexitcode
}
person ggz    schedule 05.01.2012

Самый простой способ - использовать этот ярлык: C: \ Windows \ sysnative, который эквивалентен C: \ Windows \ System32, но главное отличие в том, что процесс запускается как 64-битный. Поэтому самый простой способ получить доступ к 64-битному реестру из 32-битной оболочки PowerShell - это вызвать reg.exe через C: \ Windows \ sysnative. Например:

C:\Windows\sysnative\reg.exe QUERY HKLM\SOFTWARE\JavaSoft\JDK

источник: https://stackoverflow.com/a/25103599

Если по какой-то причине вам потребовался доступ к 32-битному реестру из 64-битной командной строки, используйте C: \ Windows \ syswow64

C:\Windows\syswow64\reg.exe QUERY HKLM\SOFTWARE\JavaSoft
person JohnP2    schedule 28.03.2018

Небольшой вариант ответа из Милана - разместить powershell с помощью программы C # согласно blog. Хотя эта запись в блоге посвящена компиляции для .NET 4.0, то же самое можно скомпилировать и для .NET 3.5. Результатом является двоичный файл, который является хостом PowerShell, который может получить доступ к 64-битному реестру и войти в него при вызове из 32-битного процесса:

using System;
using System.Management.Automation.Runspaces;
using Microsoft.PowerShell;

namespace PSHost
{
    class Program
    {
        static void Main(string[] args)
        {

            var config = RunspaceConfiguration.Create();
            ConsoleShell.Start(
                config,
                "Windows PowerShell - Compiled for ANY CPU",
                "",
                args
            );

        }
    }
}
person Ameer Deen    schedule 09.01.2011

Чтобы расширить ответ Милана Гардиана, используйте эту функцию для небольших блоков кода:

function RunAs-64Bit ([ScriptBlock]$scriptblock)
{
    [string]$code = 'using System.Diagnostics; static class Program { static int Main(string[] args) { ProcessStartInfo i = new ProcessStartInfo("powershell", args[0]); i.UseShellExecute = false; using(Process p = Process.Start(i)) { p.WaitForExit(); return p.ExitCode; } } }'
    [string]$exeName = $env:temp + '\' + [System.IO.Path]::GetRandomFileName() + '.exe';
    $params = New-Object 'System.CodeDom.Compiler.CompilerParameters'; 
    @('mscorlib.dll',  'System.dll', ([System.Reflection.Assembly]::GetAssembly([PSObject]).Location)) | %{ $params.ReferencedAssemblies.Add( $_ ) } | Out-Null
    $params.GenerateExecutable      = $true
    $params.GenerateInMemory        = $false;
    $params.CompilerOptions         = '/optimize';
    $params.OutputAssembly          = $exeName;
    $params.TreatWarningsAsErrors   = $false;
    $params.WarningLevel            = 4;

    $csprovider = New-Object 'Microsoft.CSharp.CSharpCodeProvider'; #disposable
    try {
        $compileResults = $csprovider.CompileAssemblyFromSource($params, $code)
        $errors = $compileResults.Errors | ?{ -not $_.IsWarning }
        if ($errors.Count -gt 0) 
        {
            Write-Host -f red 'Compiler errors are found.'
            foreach ($output in $compileResults.Output) { Write-Host -$output }
            foreach ($err in $errors) { Write-Host -f red $('Compile Error: ' + $err); }            
        }
        else 
        {
            $compileResults.Errors | %{ Abr-Write-UtilLog 'Util Get assembly from code' $('Compile Warning: ' + $_); }            
            $assembly = $compileResults.CompiledAssembly
            $commandParam = '-encodedCommand  ' + [System.Convert]::ToBase64String([System.Text.Encoding]::UNICODE.GetBytes($scriptblock));
            &$exeName $commandParam
        }
        Remove-Item -force $exeName -ErrorAction 'SilentlyContinue';
    } finally{
        $csprovider.Dispose();
        Remove-Variable 'csprovider';
    }
}

Теперь используйте эту функцию для запуска любого блока сценария (если он не слишком большой) в 64-битном режиме, когда доступен 64-битный режим.

person Air2    schedule 13.08.2013

Команда REG.EXE может записывать в 64-битные реестры, поэтому приведенное ниже должно быть безопасным для 32/64-битных из PowerShell.

&REG.EXE @('ADD','HKLM\YOURPATH\...','/v','KEY','/t','REG_DWORD','/d','12c','/f','/reg:64')

Кажется, что это немного проще и менее подвержено ошибкам, чем другие решения. Это могло появиться много лет спустя.

person John A. Booth    schedule 02.07.2020