Skip to content

Instantly share code, notes, and snippets.

@jborean93
Last active October 16, 2025 21:21
Show Gist options
  • Save jborean93/3d7448b5e4b6f8912168ffb624bc1b11 to your computer and use it in GitHub Desktop.
Save jborean93/3d7448b5e4b6f8912168ffb624bc1b11 to your computer and use it in GitHub Desktop.
Gets the LSA logon session data
# Copyright: (c) 2025, Jordan Borean (@jborean93) <[email protected]>
# MIT License (see LICENSE or https://opensource.org/licenses/MIT)
# Added GetLastErrorRecord
#Requires -Module @{ ModuleName = 'Ctypes'; ModuleVersion = '0.3.0' }
using namespace System.ComponentModel
using namespace System.Management.Automation
using namespace System.Runtime.InteropServices
using namespace System.Security.Principal
Function Get-LogonSessionData {
<#
.SYNOPSIS
Gets the LSA logon session data.
.DESCRIPTION
Gets the LSA logon session data for the process' specified. This session
data contains information about the logon session stored in LSA.
.PARAMETER ProcessId
The process id's to get the logon session data for.
.EXAMPLE
Get-LogonSessionData
Gets the logon session data for the current process.
.EXAMPLE
Get-LogonSessionData -ProcessId 1234
Gets the logon session data for the process 1234.
#>
[CmdletBinding()]
param (
[Parameter(ValueFromPipeline, ValueFromPipelineByPropertyName)]
[Alias('Id')]
[int[]]
$ProcessId = $pid
)
begin {
$PROCESS_QUERY_INFORMATION = 0x00000400
$TokenStatistics = 10
[Flags()] enum LogonUserFlags {
None = 0x00000000
Guest = 0x0000000
NoEncryption = 0x00000002
CachedAccount = 0x00000004
UsedLmPassword = 0x00000008
ExtraSids = 0x00000020
SubauthSessionKey = 0x00000040
ServerTrustAccount = 0x00000080
Ntlmv2Enabled = 0x00000100
ResourceGroups = 0x00000200
ProfilePathReturned = 0x00000400
Ntv2 = 0x00000800
Lmv2 = 0x00001000
Ntlmv2 = 0x00002000
Optimized = 0x00004000
Winlogon = 0x00008000
Pkinit = 0x00010000
NoOptimized = 0x00020000
NoElevation = 0x00040000
ManagedService = 0x00080000
}
enum SecurityLogonType {
UndefinedLogonType = 0
Interactive = 2
Network
Batch
Service
Proxy
Unlock
NetworkCleartext
NewCredentials
RemoteInteractive
CachedInteractive
CachedRemoteInteractive
CachedUnlock
}
ctypes_struct LUID {
[int]$LowPart
[int]$HighPart
}
ctypes_struct LSA_LAST_INTER_LOGON_INFO {
[int64]$LastSuccessfulLogon
[int64]$LastFailedLogon
[int]$FailedAttemptCountSinceLastSuccessfulLogon
}
ctypes_struct LSA_UNICODE_STRING {
[int16]$Length
[int16]$MaximumLength
[IntPtr]$Buffer
}
ctypes_struct SECURITY_LOGON_SESSION_DATA {
[int]$Size
[Luid]$logonId
[LSA_UNICODE_STRING]$UserName
[LSA_UNICODE_STRING]$LogonDomain
[LSA_UNICODE_STRING]$AuthenticationPackage
[int]$LogonType
[int]$Session
[IntPtr]$Sid
[int64]$LogonTime
[LSA_UNICODE_STRING]$LogonServer
[LSA_UNICODE_STRING]$DnsDomainName
[LSA_UNICODE_STRING]$Upn
[int]$UserFlags
[LSA_LAST_INTER_LOGON_INFO]$LastLogonInfo
[LSA_UNICODE_STRING]$LogonScript
[LSA_UNICODE_STRING]$ProfilePath
[LSA_UNICODE_STRING]$HomeDirectory
[LSA_UNICODE_STRING]$HomeDirectoryDrive
[int64]$LogoffTime
[int64]$KickOffTime
[int64]$PasswordLastSet
[int64]$PasswordCanChange
[int64]$PasswordMustChange
}
ctypes_struct TOKEN_STATISTICS {
[Luid]$TokenId
[Luid]$AuthenticationId
[Int64]$ExpirationTime
[int]$TokenType
[int]$ImpersonationLevel
[int]$DynamicCharged
[int]$DynamicAvailable
[int]$GroupCount
[int]$PrivilegeCount
[int]$ModifiedId
}
$advapi32 = New-CtypesLib Advapi32.dll
$advapi32.Returns([bool]).SetLastError($true).OpenProcessToken = [Ordered]@{
ProcessHandle = [IntPtr]
DesiredAccess = [TokenAccessLevels]
TokenHandle = [ref][IntPtr]
}
$advapi32.Returns([bool]).SetLastError($true).Entrypoint('GetTokenInformation').GetTokenInformationTokenStatistics = [Ordered]@{
TokenHandle = [IntPtr]
TokenInformationClass = [int]
TokenInformation = [ref][TOKEN_STATISTICS]
TokenInformationLength = [int]
ReturnLength = [ref][int]
}
$advapi32.Returns([int]).LsaNtStatusToWinError = [Ordered]@{
Status = [int]
}
$kernel32 = New-CtypesLib Kernel32.dll
$kernel32.Returns([IntPtr]).GetCurrentProcess = [Ordered]@{}
$kernel32.Returns([IntPtr]).SetLastError($true).OpenProcess = [Ordered]@{
dwDesiredAccess = [int]
bInheritHandle = [bool]
dwProcessId = [int]
}
$kernel32.Returns([bool]).SetLastError($true).CloseHandle = [Ordered]@{
hObject = [IntPtr]
}
$secur32 = New-CtypesLib Secur32.dll
$secur32.Returns([int]).LsaFreeReturnBuffer = [Ordered]@{
Buffer = [IntPtr]
}
$secur32.Returns([int]).LsaGetLogonSessionData = [Ordered]@{
LogonId = [ref][Luid]
ppLogonSessionData = [ref][IntPtr]
}
Function ConvertFrom-FileTime {
[OutputType([DateTime])]
param (
[Parameter(Mandatory, ValueFromPipeline)]
[int64]
$InputObject
)
process {
if ($InputObject -eq [Int64]::MaxValue) {
return
}
[DateTime]::FromFileTimeUtc($InputObject)
}
}
Function ConvertFrom-LsaString {
[OutputType([string])]
[CmdletBinding()]
param (
[Parameter(Mandatory, ValueFromPipelineByPropertyName)]
[int]
$Length,
[Parameter(Mandatory, ValueFromPipelineByPropertyName)]
[IntPtr]
$Buffer
)
process {
if ($Length -eq 0 -or $Buffer -eq [IntPtr]::Zero) {
return
}
[Marshal]::PtrToStringUni($Buffer, $Length / 2)
}
}
}
process {
foreach ($procId in $ProcessId) {
$sessionDataPtr = $procHandle = $token = [IntPtr]::Zero
try {
if ($procId -eq $pid) {
Write-Verbose "Opening current process handle"
$procHandle = $kernel32.GetCurrentProcess()
}
else {
Write-Verbose "Opening process handle for $procId"
$procHandle = $kernel32.OpenProcess(
$PROCESS_QUERY_INFORMATION,
$false,
$procId)
if ($procHandle -eq [IntPtr]::Zero) {
$err = $kernel32.GetLastErrorRecord('OpenProcessFailed')
$err.ErrorDetails = "Failed to open process ${procId}: $err"
$PSCmdlet.WriteError($err)
continue
}
}
Write-Verbose "Opening token handle for process $procId"
$res = $advapi32.OpenProcessToken(
$procHandle,
[TokenAccessLevels]::Query,
[ref]$token)
if (-not $res) {
$err = $advapi32.GetLastErrorRecord('OpenProcessTokenFailed')
$err.ErrorDetails = "Failed to open process token for ${procId}: $err"
$PSCmdlet.WriteError($err)
continue
}
$tokenStats = [TOKEN_STATISTICS]::new()
$tokenStatsLength = [Marshal]::SizeOf([type]$tokenStats.GetType())
$res = $advapi32.GetTokenInformationTokenStatistics(
$token,
$TokenStatistics,
[ref]$tokenStats,
$tokenStatsLength,
[ref]$tokenStatsLength)
if (-not $res) {
$err = $advapi32.GetLastErrorRecord('GetTokenInformationTokenStatisticsFailed')
$err.ErrorDetails = "Failed to get TokenStatistics for ${procId}: $err"
$PSCmdlet.WriteError($err)
continue
}
$logonId = $tokenStats.AuthenticationId
$ntStatus = $secur32.LsaGetLogonSessionData(
[ref]$logonId,
[ref]$sessionDataPtr)
if ($ntStatus) {
$winError = $advapi32.LsaNtStatusToWinError($ntStatus)
$exp = [Win32Exception]::new($winError)
$err = [ErrorRecord]::new(
$exp,
"LsaGetLogonSessionDataFailed",
[ErrorCategory]::InvalidResult,
$winError)
$err.ErrorDetails = 'Failed to get LSA logon session data for {0}: {1} (0x{2:X8})' -f @(
$procId, $exp.Message, $winError)
$PSCmdlet.WriteError($err)
continue
}
$sessionData = [Marshal]::PtrToStructure($sessionDataPtr, [type][SECURITY_LOGON_SESSION_DATA])
[PSCustomObject]@{
PSTypeName = 'LsaLogonSessionData'
ProcessId = $procId
LogonId = (([int64]$sessionData.LogonId.HighPart) -shl 32) -bor $sessiondata.LogonId.LowPart
UserName = $sessionData.UserName | ConvertFrom-LsaString
LogonDomain = $sessionData.LogonDomain | ConvertFrom-LsaString
AuthenticationPackage = $sessionData.AuthenticationPackage | ConvertFrom-LsaString
LogonType = [Enum]::ToObject([SecurityLogonType], $sessionData.LogonType)
Session = $sessionData.Session
Sid = [SecurityIdentifier]::new($sessionData.Sid)
LogonTime = $sessionData.LogonTime | ConvertFrom-FileTime
LogonServer = $sessionData.LogonServer | ConvertFrom-LsaString
DnsDomainName = $sessionData.DnsDomainName | ConvertFrom-LsaString
Upn = $sessionData.Upn | ConvertFrom-LsaString
UserFlags = [Enum]::ToObject([LogonUserFlags], $sessionData.UserFlags)
LastSuccessfulLogon = $sessionData.LastLogonInfo.LastSuccessfulLogon
LastFailedLogon = $sessionData.LastLogonInfo.LastFailedLogon
FailedAttemptCountSinceLastSuccessfulLogon = $sessionData.LastLogonInfo.FailedAttemptCountSinceLastSuccessfulLogon
LogonScript = $sessionData.LogonScript | ConvertFrom-LsaString
ProfilePath = $sessionData.ProfilePath | ConvertFrom-LsaString
HomeDirectory = $sessionData.HomeDirectory | ConvertFrom-LsaString
HomeDirectoryDrive = $sessionData.HomeDirectoryDrive | ConvertFrom-LsaString
LogoffTime = $sessionData.LogoffTime | ConvertFrom-FileTime
KickOffTime = $sessionData.KickOffTime | ConvertFrom-FileTime
PasswordLastSet = $sessionData.PasswordLastSet | ConvertFrom-FileTime
PasswordCanChange = $sessionData.PasswordCanChange | ConvertFrom-FileTime
PasswordMustChange = $sessionData.PasswordMustChange | ConvertFrom-FileTime
}
}
finally {
if ($sessionDataPtr -ne [IntPtr]::Zero) {
$null = $secur32.LsaFreeReturnBuffer($sessionDataPtr)
}
if ($token -ne [IntPtr]::Zero) {
$null = $kernel32.CloseHandle($token)
}
if ($procHandle -ne [IntPtr]-1 -and $procHandle -ne [IntPtr]::Zero) {
$null = $kernel32.CloseHandle($procHandle)
}
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment