PowerShell
Step 11. Exploring the Registry — Read Keys and Values, Record Startup Entries
Level 0 — Understanding Computer Operation and Structure | Difficulty ★★★☆☆ | Estimated time 2–3 hours
This opens the Inside Windows unit. Prerequisite: Step 10. Use Windows PowerShell 5.1 or PowerShell 7 on Windows. Internet access is needed when finding official product information.
- Tools: a Windows PC, PowerShell, and Registry Editor (regedit).
- Scope: read registry data and save new report files. Do not execute, delete, or modify startup entries or change permissions.
- Assessment: an unfamiliar name needs investigation. A name, path, or signature alone does not establish maliciousness or benignness.
Today you record what is registered to run at logon. Whether it actually ran, was authorized, or behaves safely requires separate evidence. Keeping raw observations separate from assessment makes later comparisons more useful.
1. Learning Objectives
- Distinguish keys, subkeys, and a value’s name, type, and data.
- Explain HKCU/HKLM and the conditions for comparing 32-bit and 64-bit views.
- Read structure and values with Get-ChildItem and Get-ItemProperty.
- Find the same key and values in Registry Editor.
- Produce raw reports for both Run keys and review notes for each entry.
2. Background — Configuration Trees and Startup Entries
2-1. Keys and Values
The registry is a hierarchical configuration store used by Windows and many applications. It does not hold every setting or file; applications also use configuration files and separate databases. A key resembles a folder-like branch. A value has a name, type, and data attached to a key, rather than being a file-like child path.
For example, REG_SZ stores a string, REG_EXPAND_SZ can contain environment-variable references, and REG_DWORD stores a 32-bit integer. A default value has an empty name; Registry Editor displays it as “(Default)”. The editor can show a “(Default) — (value not set)” row even when no default value is stored. That display row is not counted by a report enumerating actual stored value names. An unfamiliar or GUID-like name is not proof that a key belongs to a removed program.
2-2. HKCU, HKLM, and View Conditions
HKCU: is the PowerShell drive for the account under which the process runs; HKLM: addresses machine-wide settings. Access depends on key permissions. Even HKCU does not guarantee unrestricted access to every key.
The root names shown by Registry Editor are not exactly the same concept as file-backed groups of keys and values called hives. On 64-bit Windows, some paths provide different views for 32-bit and 64-bit processes. Compare the account, full key path, process bitness, and collection time.
2-3. Run Keys and Persistence
A Run key is one location for registering programs to run at logon. Registration does not guarantee execution at boot or immediate execution. Disabled startup settings, policies, or missing files can prevent it. HKCU has per-user scope; HKLM provides machine-wide scope for users of that computer.
Persistence means retaining an opportunity to execute across events such as logons or reboots. Both legitimate and malicious programs can use Run keys. Other locations include RunOnce, services, scheduled tasks, and Startup folders. Empty Run keys do not establish that all startup mechanisms or malware are absent.
3. Follow Along
Before you begin. Registry evidence depends on account, key path, permissions, and process bitness. A startup registration alone does not establish execution or malicious intent.
3-1. Registry Drives and Structure
Get-PSDrive -PSProvider Registry [Environment]::Is64BitProcess Get-ChildItem -LiteralPath HKCU:\ | Select-Object Name
Check the HKCU/HKLM drives, whether this process is 64-bit, and the immediate key names under the current-user root. Lists vary by account. Next, read at most twelve immediate key names under Software.
Get-ChildItem -LiteralPath 'HKCU:\Software' | Select-Object -First 12 Name
3-2. Read HKCU Run Values
$userRun = 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Run'
try {
Get-ItemProperty -LiteralPath $userRun -ErrorAction Stop | Format-List
} catch {
"Query failed: $($_.Exception.Message)"
}
The left side shows value names and the right side their data. Run data can contain arguments as well as executable paths. PSPath, PSParentPath, PSChildName, PSDrive, and PSProvider are provider metadata. Do not generalize this to ignoring every name beginning with PS; real values can also have such names. The report below enumerates actual registry value names separately.
This is a fictional example of the data format. Do not execute its command line or add it to the registry.
ExampleSync : "C:\Program Files\ExampleSync\sync.exe" --background
3-3. Subkeys Are Different from Values
try {
$subkeys = @(Get-ChildItem -LiteralPath $userRun -ErrorAction Stop)
"Subkey count: $($subkeys.Count)"
$subkeys | Select-Object Name
} catch {
"Query failed: $($_.Exception.Message)"
}
If the query succeeds and Subkey count is 0, there are no subkeys at that time. Values may still exist. Run keys can contain subkeys, so zero is not universal. A missing key, denied access, and an existing key with no values are different states.
3-4. View the Same Location in Registry Editor
Open Registry Editor from Start or run the following command. If UAC appears, verify the application and publisher and approve it yourself. Do not bypass organizational restrictions or switch to another account’s credentials.
regedit
Enter this full path in the address bar and press Enter, or expand the tree in order.
HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run
- Compare Name, Type, and Data with PowerShell. Default-value labels and line wrapping can differ.
- First verify the same account, view, and key. Do not create a missing key merely to perform the comparison.
- Double-clicking a value may open an edit dialog; cancel if it opens. Registry Editor is not a document editor with one final Save operation. Closing it does not undo changes already applied.
3-5. Read HKLM Run Too
$machineRun = 'HKLM:\Software\Microsoft\Windows\CurrentVersion\Run'
try {
Get-ItemProperty -LiteralPath $machineRun -ErrorAction Stop | Format-List
} catch {
"Query failed: $($_.Exception.Message)"
}
Registration here does not prove an entry is a required system component. Application notification or update helpers can also register. Read access and write access are different permissions. Record access errors; this exercise does not require accessing sensitive areas such as SAM or expanding privileges.
3-6. Review Each Entry
- Record the original name, complete command line, and registry location. Do not paste and execute the command line.
- Separate the path from arguments. If a host such as powershell.exe or rundll32.exe is named, inspect its script or DLL argument too.
- Check file existence, product metadata, signer, and signature status against official product information. A filename or product string alone does not establish provenance.
- Valid reports the signature-check result at that time, not harmless behavior or installation consent. NotSigned does not automatically mean malicious.
- Temp paths, hidden-window flags, and policy options are clues. Familiar names or Program Files/Windows locations are not safety guarantees. Separate observations, evidence, assessment, and next checks.
The following inspects one file’s metadata without executing a recorded command line. It targets the Windows PowerShell executable itself. A valid signature on that host does not validate a separate script it might run.
$exampleFile = Join-Path $env:WINDIR 'System32\WindowsPowerShell\v1.0\powershell.exe' Get-Item -LiteralPath $exampleFile | Select-Object FullName, Length, VersionInfo Get-AuthenticodeSignature -LiteralPath $exampleFile | Select-Object Status, StatusMessage, SignerCertificate
4. Mission — Startup Entry Audit Report
Save both Run keys and annotate every actual value. Distinguish why a result is absent, and do not label an unknown item benign just to finish. Verified artifact facts and unverified installation origin or necessity are separate findings.
- Raw data: startup-audit.json and startup-audit.txt.
- Review: review-notes.txt — identity, evidence, disposition, and next check for every entry.
- Record a plan to collect again in about one month using the same account and process view. Keep the original snapshot.
5. Model Answer and Completion Criteria
Save the Two Keys Separately in One Report
Run the complete block in the terminal. It creates a new folder and three report files without modifying registry data. Actual value-name enumeration avoids mixing in provider metadata. JSON preserves strings, arrays, numbers, and empty strings; REG_EXPAND_SZ is read with its original environment-variable notation.
$auditlab = Join-Path $HOME ('infosec-step11-' + [guid]::NewGuid().ToString('N'))
New-Item -ItemType Directory -Path $auditlab -ErrorAction Stop | Out-Null
$audit = @()
foreach ($path in @(
'HKCU:\Software\Microsoft\Windows\CurrentVersion\Run',
'HKLM:\Software\Microsoft\Windows\CurrentVersion\Run'
)) {
$key = $null
try {
$key = Get-Item -LiteralPath $path -ErrorAction Stop
$names = @($key.GetValueNames() | Sort-Object)
if ($names.Count -eq 0) {
$audit += [pscustomobject]@{Key=$path;Status='Empty';Name=$null;Type=$null;Data=$null;Error=$null}
}
foreach ($name in $names) {
$audit += [pscustomobject]@{
Key=$path; Status='Read'; Name=$name
Type=$key.GetValueKind($name).ToString()
Data=$key.GetValue($name, $null, [Microsoft.Win32.RegistryValueOptions]::DoNotExpandEnvironmentNames)
Error=$null
}
}
} catch {
$status = 'Error'
if ($_.CategoryInfo.Category -eq 'ObjectNotFound') { $status = 'Missing' }
elseif ($_.CategoryInfo.Category -eq 'PermissionDenied') { $status = 'AccessDenied' }
$audit += [pscustomobject]@{Key=$path;Status=$status;Name=$null;Type=$null;Data=$null;Error=$_.Exception.Message}
} finally {
if ($null -ne $key) { $key.Close() }
}
}
$record = [ordered]@{
CollectedAt=(Get-Date).ToString('o')
User=[System.Security.Principal.WindowsIdentity]::GetCurrent().Name
PowerShell=$PSVersionTable.PSVersion.ToString()
Is64BitProcess=[Environment]::Is64BitProcess
Scope='Only the two Run keys in this process registry view; not all startup mechanisms.'
Entries=$audit
}
$jsonPath = Join-Path $auditlab 'startup-audit.json'
$textPath = Join-Path $auditlab 'startup-audit.txt'
$record | ConvertTo-Json -Depth 8 | Out-File -LiteralPath $jsonPath -Encoding UTF8 -Width 4096
$lines = @("CollectedAt: $($record.CollectedAt)", "User: $($record.User)",
"PowerShell: $($record.PowerShell); 64-bit process: $($record.Is64BitProcess)", $record.Scope)
foreach ($row in $audit) {
$lines += ''
$lines += "Key: $($row.Key)"
$lines += "Status: $($row.Status)"
$lines += 'Name (JSON): ' + (ConvertTo-Json -InputObject $row.Name -Compress)
$lines += "Type: $($row.Type)"
$lines += 'Data (JSON): ' + (ConvertTo-Json -InputObject $row.Data -Compress)
if ($row.Error) { $lines += "Error: $($row.Error)" }
}
$lines | Out-File -LiteralPath $textPath -Encoding UTF8 -Width 4096
$notesPath = Join-Path $auditlab 'review-notes.txt'
$notes = @('For each Read entry, record identity, evidence, disposition, and next check.',
'Keep raw JSON/TXT unchanged; do not execute the command lines in them.',
'Repeat collection in the same account and process view in about one month; retain both snapshots.')
foreach ($row in $audit | Where-Object {$_.Status -eq 'Read'}) {
$notes += @('', "Entry: $($row.Key) :: $($row.Name)",
'Identity: ', 'Evidence: ', 'Disposition: NeedsReview', 'Next check: ')
}
$notes | Out-File -LiteralPath $notesPath -Encoding UTF8 -Width 4096
"Read entries: $(@($audit | Where-Object {$_.Status -eq 'Read'}).Count)"
"Non-Read rows: $(@($audit | Where-Object {$_.Status -ne 'Read'}).Count)"
$auditlab
Read means a value was collected; Empty means an existing key has no values. Missing means no key, AccessDenied means denied access, and Error records another failure. A nonzero Non-Read rows count requires inspecting the status; do not claim every key was successfully collected.
.NET type names String, ExpandString, and DWord correspond to REG_SZ, REG_EXPAND_SZ, and REG_DWORD in Registry Editor. Data (JSON) can display escaped quotes and backslashes to preserve the original characters. It is not a new command to execute.
Inspect the Saved Report and Notes
Get-ChildItem -LiteralPath $auditlab -File | Select-Object Name, Length
Get-Content -LiteralPath $textPath -Encoding UTF8
$savedAudit = Get-Content -LiteralPath $jsonPath -Raw -Encoding UTF8 | ConvertFrom-Json
@($savedAudit.Entries | Where-Object {$_.Status -eq 'Read'}).Count
Keep raw JSON/TXT unchanged. Open review-notes.txt in a text editor and document every Read entry. Use dispositions such as “identity supported by evidence”, “needs further review”, or “unable to inspect”. Search public product names, publishers, and filenames; do not upload private paths, full command lines, or potentially token-bearing data to search engines or external scanning sites.
Fictional notes: ExampleSync — official product description and file signer agree; purpose supported. Another unsigned script — code inspection explains its function, but installation origin remains unverified; check administration records. You can complete the audit record without inventing a benign verdict for the second entry.
Exercises and Explanations
- Get-ChildItem versus Get-ItemProperty? → Subkeys versus values and provider properties on that key.
- HKCU versus HKLM? → The process user’s area versus machine-wide scope. Do not generalize that malware must prefer one.
- Subkey count is 0? → After a successful query, no subkeys exist at that time. This differs from the value count, a missing key, or denied access.
- An updt.exe under Temp? → Name and location are clues, not a verdict. Check provenance, signatures, code, and execution evidence.
- Does closing regedit undo changes? → Already-applied changes remain. Do not modify entries today; cancel an accidentally opened edit dialog.
Completion Checklist
- [ ] I distinguish keys from values, including name, type, and data.
- [ ] I queried HKCU/HKLM consistently and recorded the account and bitness.
- [ ] I compared the same key’s names, types, and data in Registry Editor.
- [ ] I retained raw reports separately from entry-by-entry review notes.
- [ ] Every Read entry has evidence, a disposition, and any necessary next check.
- [ ] I preserved errors and uncertainty and recorded the next comparison plan.
6. Troubleshooting
- Missing path: check CurrentVersion spelling, separators, account, and view. A key may genuinely be absent; do not assume nearly every failure is a typo.
- Access denied: record the error. Do not change key ownership/ACLs or open credential-storage areas to finish this lesson.
- GUI mismatch: compare account, full path, 32/64-bit view, refresh time, and default-value display. Entries can also change between observations.
- Truncated commands: use the raw JSON/TXT rather than copying a formatted screen table. Display formatting can differ from the full string.
- .reg backup: importing an export can merge content, so it is not a complete rollback that automatically removes later additions or restores all state such as permissions. Export/import is not performed in this lesson.
7. Summary
This exercise produces a point-in-time record of two Run keys and supporting review evidence. It is neither a malware-free verdict nor an inventory of every startup mechanism. Sysinternals Autoruns is an official tool for inspecting many more autostart locations; distinguish that broader scope from this section’s two-key audit.
Practice Verification Record
Historical record. The dated record below describes the earlier lesson version. The current instructions include subsequent editorial corrections; that record does not certify every revised example.
On 2026-09-10, actual Run-key queries and report saving were verified in Windows PowerShell 5.1.26100.9444 and PowerShell 7.6.5. Checks inspected names, types, original data, and actual file metadata/signatures without executing the registered command lines. Published examples use fictional data instead of exposing a personal startup inventory.
On 2026-09-11, the actual Registry Editor view of the same HKCU Run key was compared with the collected entries, REG_SZ types, and visible data. The check also confirmed column clipping of long names/data and the editor’s unset default-value display row. Full-string verification used the separately saved raw JSON/TXT reports.
Microsoft: Registry provider · Microsoft: Run and RunOnce · Microsoft: Registry views · Microsoft: Registry hives · Microsoft: Authenticode signature · Microsoft: Autoruns · Microsoft: reg import
ONE STEP FURTHER
Finished this lesson?
Check the completion criteria, then mark your progress.