Forum Discussion
Issue with date modified for NTUSER.DAT
- Feb 23, 2018
Here is the code from the script:
#Purpose: Used to set the ntuser.dat last modified date to that of the last modified date on the user profile folder.
#This is needed because windows cumulative updates are altering the ntuser.dat last modified date which then defeats
#the ability for GPO to delete profiles based on date and USMT migrations based on date.$ErrorActionPreference = "SilentlyContinue"
$Report = $Null
$Path = "C:\Users"
$UserFolders = $Path | GCI -DirectoryForEach ($UserFolder in $UserFolders)
{
$UserName = $UserFolder.Name
If (Test-Path "$Path\$UserName\NTUSer.dat")
{
$Dat = Get-Item "$Path\$UserName\NTUSer.dat" -force
$DatTime = $Dat.LastWriteTime
If ($UserFolder.Name -ne "default"){
$Dat.LastWriteTime = $UserFolder.LastWriteTime
}
Write-Host $UserName $DatTime
Write-Host (Get-item $Path\$UserName -Force).LastWriteTime
$Report = $Report + "$UserName`t$DatTime`r`n"
$Dat = $Null
}
}
I threw together some code you can use & abuse. (It has not been tested in Production, so use at your own risk. Make improvements and share if you'd like!)
To convert the LocalProfileLoadTimeHigh and LocalProfileLoadTimeLow, you combine them (text strings) and then convert to DateTime. It's convoluted. I'm guessing Microsoft could have used a single QWORD but instead chose 2 DWORDs registry entries.
# Get list of profiles
$profiles = Get-ItemProperty -Path "HKLM:\Software\Microsoft\Windows NT\CurrentVersion\ProfileList\*"
# Loop through each profile
Foreach ($profile in $profiles) {
# Get the SID
$SID = New-Object System.Security.Principal.SecurityIdentifier($profile.PSChildName)
# Convert SID to Friendly name
$FriendlyName = $SID.Translate([System.Security.Principal.NTAccount])
# Trim and store variables
$SidTrimmed = $SID.Value
$FriendlyNameTrimmed = $FriendlyName.Value
# Store the Profile Load time (in Decimal)
# Example: ProfileLoadHighDec = 30997847 / ProfileLoadLowDec = 2259805116
$ProfileLoadHighDec = $profile.LocalProfileLoadTimeHigh
$ProfileLoadLowDec = $profile.LocalProfileLoadTimeLow
# Convert Decimal to Hex string
# Example: ProfileLoadHighHex = 01d8fd57 / ProfileLoadLowHex = 86b1d3bc
$ProfileLoadHighHex = [System.Convert]::ToString($ProfileLoadHighDec,16)
$ProfileLoadLowHex = [System.Convert]::ToString($ProfileLoadLowDec,16)
# Concatenate hex strings
# Example: 01d8fd5786b1d3bc
$ProfileHexJoined = -join ($ProfileLoadHighHex,$ProfileLoadLowHex)
# Convert to DateTime format
# Example: 11/21/2022 03:15:37
$TimestampInt = [Convert]::ToInt64($ProfileHexJoined,16)
$ProfileLoadDate = [DateTime]::FromFileTimeutc($TimestampInt)
# Optional output for example purposes
Write-Output "SID: $SidTrimmed"
Write-Output "Friendly Name: $FriendlyNameTrimmed"
Write-Output "Profile Load Date: $ProfileLoadDate"
Write-Output "`n"
}
Running this script results in the following output:
SID: S-1-5-18
SID: S-1-5-19
SID: S-1-5-20
SID: S-1-5-21-1234567890-1234567890-1234567890-1234 |
Good luck!
-rp
noob alert! Late to the party I know, but I got here searching for a solution and others will too.
I believe there's a logic error in this which results in an incorrect conversion for some values of the input.
For it to work the string representation of the low DWORD value has to represent all 32 bits. In other words, some values have leading zeros. If those zeros are missing this effectively bit shifts right the high DWORD value, resulting in the wrong datetime.
The only non-contemporary date you should get is midnight 1/1/1601, the date represented by int zero. I was seeing year 1627 as an example.
Here's an alternate implementation.
Caveat: I am not a PowerShell expert, had to look up how to make a function . I am, however, confident in the logic having been weaned on K&R.
# https://techcommunity.microsoft.com/t5/windows-deployment/issue-with-date-modified-for-ntuser-dat/m-p/102438/page/3
# made a function. Added UnloadDate
# changed the logic of the function
function Get-ProfileTimestamp {
param (
$ProfileLoadHigh,$ProfileLoadLow
)
$a = ([int64]$ProfileLoadHigh -shl 32) -bor ([int64]$ProfileLoadLow)
return [DateTime]::FromFileTimeutc($a)
}
# Get list of profiles
$profiles = Get-ItemProperty -Path "HKLM:\Software\Microsoft\Windows NT\CurrentVersion\ProfileList\*"
# Loop through each profile
Foreach ($profile in $profiles) {
# Get the SID
$SID = New-Object System.Security.Principal.SecurityIdentifier($profile.PSChildName)
# Convert SID to Friendly name
$FriendlyName = $SID.Translate([System.Security.Principal.NTAccount])
# Trim and store variables
$SidTrimmed = $SID.Value
$FriendlyNameTrimmed = $FriendlyName.Value
$ProfileLoadDate = Get-ProfileTimestamp -ProfileLoadHigh $profile.LocalProfileLoadTimeHigh -ProfileLoadLow $profile.LocalProfileLoadTimeLow
$ProfileUnLoadDate = Get-ProfileTimestamp -ProfileLoadHigh $profile.LocalProfileUnLoadTimeHigh -ProfileLoadLow $profile.LocalProfileUnLoadTimeLow
# Optional output for example purposes
Write-Output "SID: $SidTrimmed"
Write-Output "Friendly Name: $FriendlyNameTrimmed"
Write-Output "Profile Load Date: $ProfileLoadDate"
Write-Output "Profile UnLoad Date: $ProfileUnLoadDate"
Write-Output "`n"
}
For extra credit, how many bits is the data shifted for each missing 0x0?
- MrMattiPantsJan 06, 2025Copper Contributor
I too came to this realization, as I've been seeing odd dates from the 1600's, as well.
After some research & testing, I was able to determine that if the "LocalProfileLoadTimeHigh" and "LocalProfileLoadTimeLow" Registry Keys/Values are missing entirely, the Date & Time should always come out to "Monday, January 1, 1601 12:00:00 AM". However, I came across some other dates, in the 1600s, that confirmed the function is Incorrect.
Fortunately, I was able to capture an example, in case anyone would like to confirm for themselves.
Unfortunately, the Script below spits out the Date/Time as "Saturday, July 3, 1627 7:00:49 AM". However, the Correct Date/Time should be "Monday, January 6, 2025 4:07:15 PM".$ProfileLoadHighDec = 31154261 $ProfileLoadLowDec = 232160855 $ProfileLoadHighHex = [System.Convert]::ToString($ProfileLoadHighDec,16) $ProfileLoadLowHex = [System.Convert]::ToString($ProfileLoadLowDec,16) $ProfileHexJoined = -join ($ProfileLoadHighHex,$ProfileLoadLowHex) $TimestampInt = [Convert]::ToInt64($ProfileHexJoined,16) $ProfileLoadDate = [DateTime]::fromFileTimeUtc($TimestampInt) $ProfileLoadDate
On the other hand, the following example spits out the correct Date/Time, without issue.$ProfileLoadHighDec = 31154261 $ProfileLoadLowDec = 232160855 $ProfileLoadTime = [datetime]::fromFileTimeUtc([uint64]$ProfileLoadHighDec -shl 32 -bor$ProfileLoadLowDec) $ProfileLoadTime
- ianjhart2718Mar 11, 2025Copper Contributor
Joe_Friedel midnight51 Ryan Pertusio
MrMattiPantsThanks for validating the code.
I turned it into a tool. It's somewhat off topic as it's now more like delete profile (theshonkproject) or Delprof2 (Helge Klein). It's also huge, so I've decided to put it on GitHub.
https://github.com/ianjhart2718/profiletimes
I've retained a small amount of the original code for nostalgic reasons. I hope that's okay. It read like it was intended to be public domain.
I'm new to GitHub also but I believe it's functional.