Como classificar as entradas do registro por hora da última gravação / hora da última modificação no PowerShell
Estou aprendendo PowerShell, uso PSCore7.1 no Windows 10 20H2, atualmente posso fazer isso:
Get-ChildItem -Path "C:\" -Directory -Recurse -Depth 5 | Where-Object{$_.LastWriteTime.ToString("yyyy-MM-dd") -eq "2020-12-14"} | Sort-Object LastWriteTime
Para obter recursivamente subpastas dentro de uma determinada profundidade (neste caso 5) de um diretório especificado (neste caso C :), encontre objetos modificados em um determinado intervalo de tempo (neste caso, hoje ou 14 de dezembro de 2020) e, finalmente, classifique o resultados por carimbo de data / hora.
Mas quando executo get-childitem para visualizar o registro, desta forma:
Get-ChildItem -Path "HKLM:\SOFTWARE"
Existem apenas dois itens: Nome e Propriedade, sem carimbo de data / hora e em regedit.exe também não há carimbo de data / hora, então como posso classificar as chaves do Registro pela hora da última modificação como o comando que postei acima usando o PowerShell?
Edit: Li aqui no Super User que posso exportar chaves de registro para arquivos txt no Editor do Registro para ver seus carimbos de data / hora, embora seja simples, é irrelevante para esta questão, pois desejo classificar as chaves de registro por carimbo de data / hora no console.
Reeditar:
Eu executei este comando:
get-childitem -path "HKLM:\" | Get-Member
E aqui estão os resultados:
TypeName: Microsoft.Win32.RegistryKey
Name MemberType Definition
---- ---------- ----------
Close Method void Close()
CreateSubKey Method Microsoft.Win32.RegistryKey CreateSubKey(string subkey), Microsoft.Win32.RegistryKey CreateSubKey(string subkey, bool writable…
DeleteSubKey Method void DeleteSubKey(string subkey), void DeleteSubKey(string subkey, bool throwOnMissingSubKey)
DeleteSubKeyTree Method void DeleteSubKeyTree(string subkey), void DeleteSubKeyTree(string subkey, bool throwOnMissingSubKey)
DeleteValue Method void DeleteValue(string name), void DeleteValue(string name, bool throwOnMissingValue)
Dispose Method void Dispose(), void IDisposable.Dispose()
Equals Method bool Equals(System.Object obj)
Flush Method void Flush()
GetAccessControl Method System.Security.AccessControl.RegistrySecurity GetAccessControl(), System.Security.AccessControl.RegistrySecurity GetAccessCon…
GetHashCode Method int GetHashCode()
GetLifetimeService Method System.Object GetLifetimeService()
GetSubKeyNames Method string[] GetSubKeyNames()
GetType Method type GetType()
GetValue Method System.Object GetValue(string name), System.Object GetValue(string name, System.Object defaultValue), System.Object GetValue(s…
GetValueKind Method Microsoft.Win32.RegistryValueKind GetValueKind(string name)
GetValueNames Method string[] GetValueNames()
InitializeLifetimeService Method System.Object InitializeLifetimeService()
OpenSubKey Method Microsoft.Win32.RegistryKey OpenSubKey(string name), Microsoft.Win32.RegistryKey OpenSubKey(string name, bool writable), Micro…
SetAccessControl Method void SetAccessControl(System.Security.AccessControl.RegistrySecurity registrySecurity)
SetValue Method void SetValue(string name, System.Object value), void SetValue(string name, System.Object value, Microsoft.Win32.RegistryValue…
ToString Method string ToString()
Property NoteProperty string[] Property=System.String[]
PSChildName NoteProperty string PSChildName=BCD00000000
PSDrive NoteProperty PSDriveInfo PSDrive=HKLM
PSIsContainer NoteProperty bool PSIsContainer=True
PSParentPath NoteProperty string PSParentPath=Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE
PSPath NoteProperty string PSPath=Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\BCD00000000
PSProvider NoteProperty ProviderInfo PSProvider=Microsoft.PowerShell.Core\Registry
Handle Property Microsoft.Win32.SafeHandles.SafeRegistryHandle Handle {get;}
Name Property string Name {get;}
SubKeyCount Property int SubKeyCount {get;}
ValueCount Property int ValueCount {get;}
View Property Microsoft.Win32.RegistryView View {get;}
Como você pode ver, não há carimbo de data / hora ...
Cite: Existe alguma maneira de visualizar as datas das alterações nas chaves / valores do registro do Windows?
Prova:
Key Name: HKEY_CLASSES_ROOT\._bsln140
Class Name: <NO CLASS>
Last Write Time: 2020-12-11 - 14:54
Value 0
Name: <NO NAME>
Type: REG_SZ
Data: VisualStudio.Launcher._bsln140
Respostas
Infelizmente, isso pode ficar complicado devido à forma como o Windows acessa as informações da chave do registro, mas é possível. Abaixo está uma série de tutoriais do Dr. Scripto no devblog da Microsoft:
- usar o PowerShell para acessar o registro de data e hora da última modificação cobre como, no PowerShell, usar a função Win32
RegQueryInfoKey
para obter carimbos de data / hora. - reutilizar o código de registro de data e hora do PowerShell abrange a criação de uma ferramenta reutilizável envolvendo esse código em uma função do Windows PowerShell.
- alavancar os carimbos de hora da chave de registro via PowerShell cobre alguns usos no mundo real.
O produto final terá a seguinte aparência, salvo como Add-RegKeyLastWriteTime.ps1:
#requires -version 3.0
function Add-RegKeyLastWriteTime {
[CmdletBinding()]
param(
[Parameter(Mandatory, ParameterSetName="ByKey", Position=0, ValueFromPipeline)]
# Registry key object returned from Get-ChildItem or Get-Item
[Microsoft.Win32.RegistryKey] $RegistryKey, [Parameter(Mandatory, ParameterSetName="ByPath", Position=0)] # Path to a registry key [string] $Path
)
begin {
# Define the namespace (string array creates nested namespace):
$Namespace = "HeyScriptingGuy" # Make sure type is loaded (this will only get loaded on first run): Add-Type @" using System; using System.Text; using System.Runtime.InteropServices; $($Namespace | ForEach-Object { "namespace $_ {"
})
public class advapi32 {
[DllImport("advapi32.dll", CharSet = CharSet.Auto)]
public static extern Int32 RegQueryInfoKey(
Microsoft.Win32.SafeHandles.SafeRegistryHandle hKey,
StringBuilder lpClass,
[In, Out] ref UInt32 lpcbClass,
UInt32 lpReserved,
out UInt32 lpcSubKeys,
out UInt32 lpcbMaxSubKeyLen,
out UInt32 lpcbMaxClassLen,
out UInt32 lpcValues,
out UInt32 lpcbMaxValueNameLen,
out UInt32 lpcbMaxValueLen,
out UInt32 lpcbSecurityDescriptor,
out System.Runtime.InteropServices.ComTypes.FILETIME lpftLastWriteTime
);
}
$($Namespace | ForEach-Object { "}" })
"@
# Get a shortcut to the type:
$RegTools = ("{0}.advapi32" -f ($Namespace -join ".")) -as [type]
}
process {
switch ($PSCmdlet.ParameterSetName) { "ByKey" { # Already have the key, no more work to be done 🙂 } "ByPath" { # We need a RegistryKey object (Get-Item should return that) $Item = Get-Item -Path $Path -ErrorAction Stop # Make sure this is of type [Microsoft.Win32.RegistryKey] if ($Item -isnot [Microsoft.Win32.RegistryKey]) {
throw "'$Path' is not a path to a registry key!" } $RegistryKey = $Item } } # Initialize variables that will be populated: $ClassLength = 255 # Buffer size (class name is rarely used, and when it is, I've never seen
# it more than 8 characters. Buffer can be increased here, though.
$ClassName = New-Object System.Text.StringBuilder $ClassLength # Will hold the class name
$LastWriteTime = New-Object System.Runtime.InteropServices.ComTypes.FILETIME switch ($RegTools::RegQueryInfoKey($RegistryKey.Handle, $ClassName,
[ref] $ClassLength, $null, # Reserved
[ref] $null, # SubKeyCount [ref] $null, # MaxSubKeyNameLength
[ref] $null, # MaxClassLength [ref] $null, # ValueCount
[ref] $null, # MaxValueNameLength [ref] $null, # MaxValueValueLength
[ref] $null, # SecurityDescriptorSize [ref] $LastWriteTime
)) {
0 { # Success
# Convert to DateTime object:
$UnsignedLow = [System.BitConverter]::ToUInt32([System.BitConverter]::GetBytes($LastWriteTime.dwLowDateTime), 0)
$UnsignedHigh = [System.BitConverter]::ToUInt32([System.BitConverter]::GetBytes($LastWriteTime.dwHighDateTime), 0)
# Shift high part so it is most significant 32 bits, then copy low part into 64-bit int:
$FileTimeInt64 = ([Int64] $UnsignedHigh -shl 32) -bor $UnsignedLow # Create datetime object $LastWriteTime = [datetime]::FromFileTime($FileTimeInt64) # Add properties to object and output them to pipeline $RegistryKey | Add-Member -NotePropertyMembers @{
LastWriteTime = $LastWriteTime ClassName = $ClassName.ToString()
} -PassThru -Force
}
122 { # ERROR_INSUFFICIENT_BUFFER (0x7a)
throw "Class name buffer too small"
# function could be recalled with a larger buffer, but for
# now, just exit
}
default {
throw "Unknown error encountered (error code $_)"
}
}
}
}
Finalmente, um exemplo de uso:
Get-ChildItem HKCU:\ | Add-RegKeyLastWriteTime | Select Name,LastWriteTime
'
Name LastWriteTime
---- -------------
HKEY_CURRENT_USER\AppEvents 7/6/2020 8:56:11 AM
HKEY_CURRENT_USER\Console 7/6/2020 8:56:11 AM
HKEY_CURRENT_USER\Control Panel 7/6/2020 1:04:53 PM
'