加快 Windows 注册表搜索的 PowerShell 脚本速度(当前为 30 分钟)

2023-12-12

我正在编写一个在 Windows 7 和 Windows 10 中使用的脚本,用于在 HKLM:\Software\Classes 中进行 Windows 注册表搜索。到目前为止,我的代码可以工作,但速度非常慢。大约需要 30 分钟才能完成。

我需要使用设置位置还可以避免因 $path 不是有效对象而发生 Get-ItemProperty 错误。

我怎样才能加快这段代码的速度?怎么了?

File regsearch.ps1 (马蒂亚斯·R·杰森的回答应用)

Function Get-RegItems
{
    Param(
        [Parameter(Mandatory=$true)]
        [string]$path,
        [string]$match)

    #Set Local Path and ignore wildcard (literalpath)
    Set-Location -literalpath $path
    $d = Get-Item -literalpath $path

    # If more than one value -> process
    If ($d.Valuecount -gt 0) {
        $d |
        # Get unkown property
        Select-Object -ExpandProperty Property |
            ForEach {
                $val = (Get-ItemProperty -Path . -Name $_).$_
                #if Filter $match found, generate ReturnObject
                if (($_ -match $match) -or ($val -match $match ) -or ($path-match $match)) {
                    New-Object psobject -Property @{ “key”=$path; “property”=$_; “value” = $val ;}
                }
            }
    }
} #end function Get-RegItems

Function RegSearch
{
    Param(
        [Parameter(Mandatory=$true)]
        [string]$path,
        [string]$match)

    # Expand $path if necessary to get a valid object
    if ($path.Indexof("HKEY") -ne "-1" -and $path.Indexof("Registry::") -eq "-1" )  {
        $path = "Microsoft.PowerShell.Core\Registry::" +$path
    }

    # Retrieve items of the main key
    Get-RegItems -path $path -match $match

    # Retrieve items of all child keys
    Get-ChildItem $path -Recurse -ErrorAction SilentlyContinue |
        ForEach {
            Get-RegItems -path $_.PsPath -match $match
        }
} #end function RegSearch


#$search = "HKCU:\SOFTWARE\Microsoft\Office"
$searchkey = ‘HKLM:\SOFTWARE\Microsoft\Office\’
#$searchkey = "HKLM:\Software\Classes\"
$pattern = "EventSystem"

cls
$result = @()

Measure-Command {$result = Regsearch -path $searchkey -match $pattern }

# TESTING
#$t = @( "Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\Software\Classes",
#       "HKLM:\Software\Classes\Wow6432Node\CLSID\",
#       "HKCU:\SOFTWARE\Microsoft\Office\",
#       "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Office")
#cls
#$t |ForEach { Get-RegItems -path $_ } | fl

if ($result.Count) {
    $result
    "Count: {0}" -f ($result.Count-1)
}
else {
    "Path: {0} `nNo Items found" -f $searchkey
}

我接受了挑战并“尽可能快地”完成了它。 现在它甚至比 REGEDIT 或任何其他工具还要快。 下面的示例持续 11 秒来解析完整的 OFFICE 密钥和所有子密钥。

此外,它还搜索 REG-BINARY 等中的字符串匹配项。

Enjoy!

# [email protected]
# reference: https://msdn.microsoft.com/de-de/vstudio/ms724875(v=vs.80)

cls
remove-variable * -ea 0
$ErrorActionPreference = "stop"

$signature = @'
[DllImport("advapi32.dll")]
public static extern Int32 RegOpenKeyEx(
    UInt32 hkey,
    StringBuilder lpSubKey,
    int ulOptions,
    int samDesired,
    out IntPtr phkResult
    );

[DllImport("advapi32.dll")]
public static extern Int32 RegQueryInfoKey(
    IntPtr hKey,
    StringBuilder lpClass, Int32 lpCls, Int32 spare, 
    out int subkeys, out int skLen, int mcLen, out int values,
    out int vNLen, out int mvLen, int secDesc,                
    out System.Runtime.InteropServices.ComTypes.FILETIME lpftLastWriteTime
);

[DllImport("advapi32.dll", CharSet = CharSet.Unicode)]
public static extern Int32 RegEnumValue(
  IntPtr hKey,
  int dwIndex,
  IntPtr lpValueName,
  ref IntPtr lpcchValueName,
  IntPtr lpReserved,
  out IntPtr lpType,
  IntPtr lpData,
  ref int lpcbData
);

[DllImport("advapi32.dll", CharSet = CharSet.Unicode)]
public static extern Int32 RegEnumKeyEx(
  IntPtr hKey,
  int dwIndex,
  IntPtr lpName,
  ref int lpcName,
  IntPtr lpReserved,
  IntPtr lpClass,
  int lpcClass,
  out System.Runtime.InteropServices.ComTypes.FILETIME lpftLastWriteTime
);

[DllImport("advapi32.dll")]
public static extern Int32 RegCloseKey(IntPtr hkey);
'@ 
$reg = add-type $signature -Name reg -Using System.Text -PassThru
$marshal = [System.Runtime.InteropServices.Marshal]

function search-RegistryTree($path) {

    # open the key:
    [IntPtr]$hkey = 0
    $result = $reg::RegOpenKeyEx($global:hive, $path, 0, 25,[ref]$hkey)
    if ($result -eq 0) {

        # get details of the key:
        $subKeyCount  = 0
        $maxSubKeyLen = 0
        $valueCount   = 0
        $maxNameLen   = 0
        $maxValueLen  = 0
        $time = $global:time
        $result = $reg::RegQueryInfoKey($hkey,$null,0,0,[ref]$subKeyCount,[ref]$maxSubKeyLen,0,[ref]$valueCount,[ref]$maxNameLen,[ref]$maxValueLen,0,[ref]$time)
        if ($result -eq 0) {
           $maxSubkeyLen += $maxSubkeyLen+1
           $maxNameLen   += $maxNameLen  +1
           $maxValueLen  += $maxValueLen +1
        }

        # enumerate the values:
        if ($valueCount -gt 0) {
            $type = [IntPtr]0
            $pName  = $marshal::AllocHGlobal($maxNameLen)
            $pValue = $marshal::AllocHGlobal($maxValueLen)
            foreach ($index in 0..($valueCount-1)) {
                $nameLen  = $maxNameLen
                $valueLen = $maxValueLen
                $result = $reg::RegEnumValue($hkey, $index, $pName, [ref]$nameLen, 0, [ref]$type, $pValue, [ref]$valueLen)
                if ($result -eq 0) {
                    $name = $marshal::PtrToStringUni($pName)
                    $value = switch ($type) {
                        1 {$marshal::PtrToStringUni($pValue)}
                        2 {$marshal::PtrToStringUni($pValue)}
                        3 {$b = [byte[]]::new($valueLen)
                           $marshal::Copy($pValue,$b,0,$valueLen)
                           if ($b[1] -eq 0 -and $b[-1] -eq 0 -and $b[0] -ne 0) {
                                [System.Text.Encoding]::Unicode.GetString($b)
                           } else {
                                [System.Text.Encoding]::UTF8.GetString($b)}
                           }
                        4 {$marshal::ReadInt32($pValue)}
                        7 {$b = [byte[]]::new($valueLen)
                           $marshal::Copy($pValue,$b,0,$valueLen)
                           $msz = [System.Text.Encoding]::Unicode.GetString($b)
                           $msz.TrimEnd(0).split(0)}
                       11 {$marshal::ReadInt64($pValue)}
                    }
                    if ($name -match $global:search) {
                        write-host "$path\$name : $value"
                        $global:hits++
                    } elseif ($value -match $global:search) {
                        write-host "$path\$name : $value"
                        $global:hits++
                    }
                }
            }
            $marshal::FreeHGlobal($pName)
            $marshal::FreeHGlobal($pValue)
        }

        # enumerate the subkeys:
        if ($subkeyCount -gt 0) {
            $subKeyList = @()
            $pName = $marshal::AllocHGlobal($maxSubkeyLen)
            $subkeyList = foreach ($index in 0..($subkeyCount-1)) {
                $nameLen = $maxSubkeyLen
                $result = $reg::RegEnumKeyEx($hkey, $index, $pName, [ref]$nameLen,0,0,0, [ref]$time)
                if ($result -eq 0) {
                    $marshal::PtrToStringUni($pName)
                }
            }
            $marshal::FreeHGlobal($pName)
        }

        # close:
        $result = $reg::RegCloseKey($hkey)

        # get Tree-Size from each subkey:
        $subKeyValueCount = 0
        if ($subkeyCount -gt 0) {
            foreach ($subkey in $subkeyList) {
                $subKeyValueCount += search-RegistryTree "$path\$subkey"
            }
        }
        return ($valueCount+$subKeyValueCount)
    }
}

$timer = [System.Diagnostics.Stopwatch]::new()
$timer.Start()

# setting global variables:
$search = "enterprise"
$hive   = [uint32]"0x80000002" #HKLM
$subkey = "SOFTWARE\Microsoft\Office"
$time   = New-Object System.Runtime.InteropServices.ComTypes.FILETIME
$hits   = 0

write-host "We start searching for pattern '$search' in Registry-Path '$subkey' ...`n"
$count = search-RegistryTree $subkey

$timer.stop()
$sec = [int](100 * $timer.Elapsed.TotalSeconds)/100
write-host "`nWe checked $count reg-values in $sec seconds. Number of hits = $hits."
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

加快 Windows 注册表搜索的 PowerShell 脚本速度(当前为 30 分钟) 的相关文章

随机推荐