< Summary - Results for net9.0, Release

Information
Class: LockCheck.Windows.Peb
Assembly: LockCheck
File(s): D:\a\LockCheck\LockCheck\src\LockCheck\Windows\Peb.cs
Tag: 117_11660770947
Line coverage
71%
Covered lines: 125
Uncovered lines: 49
Coverable lines: 174
Total lines: 431
Line coverage: 71.8%
Branch coverage
60%
Covered branches: 64
Total branches: 106
Branch coverage: 60.3%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
get_ProcessId()100%11100%
set_ProcessId(...)100%11100%
get_StartTime()100%11100%
set_StartTime(...)100%11100%
get_SessionId()100%11100%
set_SessionId(...)100%11100%
get_ParentProcessId()100%11100%
set_ParentProcessId(...)100%11100%
get_ProcessName()100%11100%
set_ProcessName(...)100%11100%
get_CommandLine()100%210%
set_CommandLine(...)100%11100%
get_CurrentDirectory()100%11100%
set_CurrentDirectory(...)100%11100%
get_WindowTitle()100%210%
set_WindowTitle(...)100%11100%
get_ExecutableFullPath()100%11100%
set_ExecutableFullPath(...)100%11100%
get_DesktopInfo()100%210%
set_DesktopInfo(...)100%11100%
get_Owner()100%11100%
set_Owner(...)100%11100%
get_HasError()100%11100%
set_HasError(...)100%11100%
get_IsCritical()100%11100%
set_IsCritical(...)100%11100%
get_IsPseudoProcess()100%210%
set_IsPseudoProcess(...)100%11100%
get_ProcessSequenceNumber()100%11100%
set_ProcessSequenceNumber(...)100%11100%
get_ProcessStartKey()100%11100%
set_ProcessStartKey(...)100%11100%
SetError(...)100%22100%
.ctor(...)100%22100%
.ctor(...)100%11100%
Initialize()82.14%29.172888.57%
InitTargetAnySelfAny(...)100%1212100%
InitTarget64Self32(...)0%156120%
InitTarget32SelfAny(...)71.43%15.831478.95%
GetInt32Target32(...)50%2.06275%
GetStringTarget32(...)50%8.7877.78%
GetInt32Target64(...)0%620%
GetStringTarget64(...)0%7280%
GetInt32(...)50%2.06275%
GetString(...)62.5%8.09888.89%
SUCCEEDED(...)50%2.5250%
SUCCEEDED(...)50%2.5250%
SUCCEEDED(...)100%22100%

File(s)

D:\a\LockCheck\LockCheck\src\LockCheck\Windows\Peb.cs

#LineLine coverage
 1using Microsoft.Win32.SafeHandles;
 2using System;
 3using System.Diagnostics;
 4using System.IO;
 5using System.Linq;
 6using System.Runtime.CompilerServices;
 7using System.Runtime.InteropServices;
 8using static LockCheck.Windows.NativeMethods;
 9
 10namespace LockCheck.Windows;
 11
 12[DebuggerDisplay("{HasError} {ProcessId} {ExecutableFullPath}")]
 13internal class Peb : IWin32ProcessDetails, IHasErrorState
 14{
 15#if DEBUG
 16#pragma warning disable IDE0052
 17    private string? _errorStack;
 18    private Exception? _errorCause;
 19    private int _errorCode;
 20#pragma warning restore IDE0052
 21#endif
 22
 223    public int ProcessId { get; private set; }
 224    public DateTime StartTime { get; private set; }
 225    public int SessionId { get; private set; }
 226    public int? ParentProcessId { get; private set; }
 227    public string? ProcessName { get; private set; }
 228    public string? CommandLine { get; private set; }
 229    public string? CurrentDirectory { get; private set; }
 230    public string? WindowTitle { get; private set; }
 231    public string? ExecutableFullPath { get; private set; }
 232    public string? DesktopInfo { get; private set; }
 233    public string? Owner { get; private set; }
 234    public bool HasError { get; private set; }
 235    public bool? IsCritical { get; private set; }
 236    public bool IsPseudoProcess { get; private set; }
 237    public ulong? ProcessSequenceNumber { get; private set; }
 238    public ulong? ProcessStartKey { get; private set; }
 39
 40    public void SetError(Exception? ex = null, int errorCode = 0)
 41    {
 242        if (!HasError)
 43        {
 244            HasError = true;
 45#if DEBUG
 46            if (Debugger.IsAttached)
 47            {
 48                // Support manual inspection at a later point
 49                _errorStack = Environment.StackTrace;
 50                _errorCause = ex;
 51                _errorCode = errorCode;
 52            }
 53#endif
 54        }
 255    }
 56
 257    internal Peb(SYSTEM_PROCESS_INFORMATION pi, ulong? processSequenceNumber)
 58    {
 59        // Convert as many members as possible, without actually needing to open the process handle.
 60        // Also, ProcessId/StartTime serve as identity. So it is "useful" to have them.
 261        ProcessId = pi.UniqueProcessId.ToInt32();
 262        StartTime = DateTime.FromFileTime(pi.CreateTime);
 263        ParentProcessId = pi.InheritedFromUniqueProcessId.ToInt32();
 264        ProcessSequenceNumber = processSequenceNumber;
 65
 266        if (pi.NamePtr != IntPtr.Zero)
 67        {
 68            // NamePtr will contain information not otherwise easily obtainable.
 69            // For example it will contain "System", "Secure System", "Registry", etc.
 70            // for those Windows pseudo processes. For regular processes it contains
 71            // the of the executable.
 272            ProcessName = Marshal.PtrToStringUni(pi.NamePtr);
 73        }
 74
 275        Initialize();
 276    }
 77
 278    internal Peb(int processId, DateTime startTime)
 79    {
 280        ProcessId = processId;
 281        StartTime = startTime;
 82
 283        Initialize();
 284    }
 85
 86    private void Initialize()
 87    {
 288        if (NtDll.TryGetSystemPseudoProcess(ProcessId, out var pseudoPeb))
 89        {
 90            // Common values for pseudo processes. All other fields don't really make sense for
 91            // these and also would cause access denied errors when attempting to read memory
 92            // or even "open" the "processes".
 93
 294            ProcessName ??= pseudoPeb.ProcessName;
 295            IsPseudoProcess = pseudoPeb.IsPseudoProcess;
 296            IsCritical = pseudoPeb.IsCritical;
 297            ExecutableFullPath = pseudoPeb.ExecutableFullPath;
 298            Owner = pseudoPeb.Owner;
 299            SessionId = pseudoPeb.SessionId;
 2100            ParentProcessId = pseudoPeb.ParentProcessId;
 2101            ProcessSequenceNumber ??= pseudoPeb.ProcessSequenceNumber;
 102        }
 103        else
 104        {
 2105            using var process = OpenProcessRead(ProcessId);
 106
 2107            if (!SUCCEEDED(!process.IsInvalid, this))
 108            {
 109                // If if not a system pseudo process, this can still fail with access denied errors.
 2110                return;
 111            }
 112
 113            // Need to check if either the current process, or the target process is 32bit or 64bit.
 114            // Additionally, if it is a 32bit process on a 64bit OS (WOW64).
 115
 2116            bool os64 = Environment.Is64BitOperatingSystem;
 2117            bool self64 = Environment.Is64BitProcess;
 2118            bool target64 = false;
 119
 2120            if (os64)
 121            {
 2122                if (!SUCCEEDED(IsWow64Process(process, out bool isWow64Target), this))
 123                {
 0124                    return;
 125                }
 126
 2127                target64 = !isWow64Target;
 128            }
 129
 2130            var offsets = PebOffsets.Get(target64);
 131
 2132            if (os64)
 133            {
 2134                if (!target64)
 135                {
 136                    // os: 64bit, self: any, target: 32bit
 2137                    InitTarget32SelfAny(process, offsets, this);
 138                }
 2139                else if (!self64)
 140                {
 141                    // os: 64bit, self: 32bit, target: 64bit
 0142                    InitTarget64Self32(process, offsets, this);
 143                }
 144                else
 145                {
 146                    // os: 64bit, self: 64bit, target: 64bit
 2147                    InitTargetAnySelfAny(process, offsets, this);
 148                }
 149            }
 150            else
 151            {
 152                // os: 32bit, self: 32bit, target: 32bit
 0153                InitTargetAnySelfAny(process, offsets, this);
 154            }
 155
 156            // Make sure that the current directory always ends with a backslash. AFAICT that is always the case,
 157            // so this should really be a noop, but we need to make sure to ensure hassle free comparison later.
 2158            if (!string.IsNullOrEmpty(CurrentDirectory) && CurrentDirectory[CurrentDirectory.Length - 1] != '\\')
 159            {
 0160                CurrentDirectory += "\\";
 161            }
 162
 163            // Owner is not really part of the native PEB, but since we have the process handle
 164            // here anyway, and going to need this value later on, we get it here as well.
 2165            Owner = GetProcessOwner(process);
 2166            IsCritical = IsProcessCritical(process, this);
 2167            ProcessName ??= Path.GetFileName(ExecutableFullPath);
 168        }
 169
 2170        if (ProcessStartKey == null && ProcessSequenceNumber != null)
 171        {
 2172            ProcessStartKey = GetProcessStartKey(ProcessSequenceNumber.Value);
 173        }
 2174    }
 175
 176    private static void InitTargetAnySelfAny(SafeProcessHandle handle, PebOffsets offsets, Peb peb)
 177    {
 2178        var pbi = new PROCESS_BASIC_INFORMATION();
 2179        if (SUCCEEDED(NtQueryInformationProcess(handle, PROCESS_INFORMATION_CLASS.ProcessBasicInformation, ref pbi, Mars
 180        {
 2181            peb.ParentProcessId ??= pbi.InheritedFromUniqueProcessId.ToInt32();
 182
 2183            var pp = new IntPtr();
 2184            if (SUCCEEDED(ReadProcessMemory(handle, new IntPtr(pbi.PebBaseAddress.ToInt64() + offsets.ProcessParametersO
 185            {
 2186                peb.CommandLine = GetString(handle, pp, offsets.CommandLineOffset, peb);
 2187                peb.CurrentDirectory = GetString(handle, pp, offsets.CurrentDirectoryOffset, peb);
 2188                peb.ExecutableFullPath = GetString(handle, pp, offsets.ImagePathNameOffset, peb);
 2189                peb.WindowTitle = GetString(handle, pp, offsets.WindowTitleOffset, peb);
 2190                peb.DesktopInfo = GetString(handle, pp, offsets.DesktopInfoOffset, peb);
 191            }
 192
 2193            peb.SessionId = GetInt32(handle, pbi.PebBaseAddress, offsets.SessionIdOffset, peb);
 194        }
 195
 2196        if (SupportsProcessSequenceNumber && peb.ProcessSequenceNumber == null)
 197        {
 198#if NET
 2199            using var psn = new ScopedNativeMemory(stackalloc byte[sizeof(ulong)]);
 200#else
 201            using var psn = new ScopedNativeMemory(Marshal.SizeOf<ulong>());
 202#endif
 2203            var buffer = (IntPtr)psn;
 2204            if (SUCCEEDED(NtQueryInformationProcess(handle, PROCESS_INFORMATION_CLASS.ProcessSequenceNumber, ref buffer,
 205            {
 2206                peb.ProcessSequenceNumber = (ulong)buffer.ToInt64();
 207            }
 208        }
 2209    }
 210
 211    private static void InitTarget64Self32(SafeProcessHandle handle, PebOffsets offsets, Peb peb)
 212    {
 0213        var pbi = new PROCESS_BASIC_INFORMATION_WOW64();
 0214        if (SUCCEEDED(NtWow64QueryInformationProcess64(handle, PROCESS_INFORMATION_CLASS.ProcessBasicInformation, ref pb
 215        {
 0216            peb.ParentProcessId ??= (int)pbi.InheritedFromUniqueProcessId;
 217
 0218            long pp = 0;
 0219            if (SUCCEEDED(NtWow64ReadVirtualMemory64(handle, pbi.PebBaseAddress + offsets.ProcessParametersOffset, ref p
 220            {
 0221                peb.CommandLine = GetStringTarget64(handle, pp, offsets.CommandLineOffset, peb);
 0222                peb.CurrentDirectory = GetStringTarget64(handle, pp, offsets.CurrentDirectoryOffset, peb);
 0223                peb.ExecutableFullPath = GetStringTarget64(handle, pp, offsets.ImagePathNameOffset, peb);
 0224                peb.WindowTitle = GetStringTarget64(handle, pp, offsets.WindowTitleOffset, peb);
 0225                peb.DesktopInfo = GetStringTarget64(handle, pp, offsets.DesktopInfoOffset, peb);
 226            }
 227
 0228            peb.SessionId = GetInt32Target64(handle, pbi.PebBaseAddress, offsets.SessionIdOffset, peb);
 229        }
 230
 0231        if (SupportsProcessSequenceNumber && peb.ProcessSequenceNumber == null)
 232        {
 233#if NET
 0234            using var psn = new ScopedNativeMemory(stackalloc byte[sizeof(ulong)]);
 235#else
 236            using var psn = new ScopedNativeMemory(Marshal.SizeOf<ulong>());
 237#endif
 0238            var buffer = (IntPtr)psn;
 0239            if (SUCCEEDED(NtWow64QueryInformationProcess64(handle, PROCESS_INFORMATION_CLASS.ProcessSequenceNumber, ref 
 240            {
 0241                peb.ProcessSequenceNumber = (ulong)buffer.ToInt64();
 242            }
 243        }
 0244    }
 245
 246    private static void InitTarget32SelfAny(SafeProcessHandle handle, PebOffsets offsets, Peb peb)
 247    {
 2248        var pbi = new PROCESS_BASIC_INFORMATION();
 2249        if (SUCCEEDED(NtQueryInformationProcess(handle, PROCESS_INFORMATION_CLASS.ProcessBasicInformation, ref pbi, Mars
 250        {
 2251            peb.ParentProcessId ??= pbi.InheritedFromUniqueProcessId.ToInt32();
 252
 253            // A 32bit process on a 64bit OS has a separate PEB structure.
 2254            var peb32 = new IntPtr();
 2255            if (SUCCEEDED(NtQueryInformationProcessWow64(handle, PROCESS_INFORMATION_CLASS.ProcessWow64Information, ref 
 256            {
 2257                var pp = new IntPtr();
 2258                if (SUCCEEDED(ReadProcessMemory(handle, new IntPtr(peb32.ToInt64() + offsets.ProcessParametersOffset), r
 259                {
 2260                    peb.CommandLine = GetStringTarget32(handle, pp, offsets.CommandLineOffset, peb);
 2261                    peb.CurrentDirectory = GetStringTarget32(handle, pp, offsets.CurrentDirectoryOffset, peb);
 2262                    peb.ExecutableFullPath = GetStringTarget32(handle, pp, offsets.ImagePathNameOffset, peb);
 2263                    peb.WindowTitle = GetStringTarget32(handle, pp, offsets.WindowTitleOffset, peb);
 2264                    peb.DesktopInfo = GetStringTarget32(handle, pp, offsets.DesktopInfoOffset, peb);
 265                }
 266
 2267                peb.SessionId = GetInt32Target32(handle, new IntPtr(peb32.ToInt64()), offsets.SessionIdOffset, peb);
 268            }
 269
 2270            if (SupportsProcessSequenceNumber && peb.ProcessSequenceNumber == null)
 271            {
 272#if NET
 0273                using var psn = new ScopedNativeMemory(stackalloc byte[sizeof(ulong)]);
 274#else
 275                using var psn = new ScopedNativeMemory(Marshal.SizeOf<ulong>());
 276#endif
 0277                var buffer = (IntPtr)psn;
 0278                if (SUCCEEDED(NtQueryInformationProcessWow64(handle, PROCESS_INFORMATION_CLASS.ProcessSequenceNumber, re
 279                {
 0280                    peb.ProcessSequenceNumber = (ulong)buffer.ToInt64();
 281                }
 282            }
 283        }
 2284    }
 285
 286    private static int GetInt32Target32(SafeProcessHandle handle, IntPtr pp, int offset, Peb he)
 287    {
 2288        var ptr = IntPtr.Zero;
 2289        if (SUCCEEDED(ReadProcessMemory(handle, pp + offset, ref ptr, new IntPtr(sizeof(int)), IntPtr.Zero), he))
 290        {
 2291            return ptr.ToInt32();
 292        }
 293
 0294        return default;
 295    }
 296
 297    private static string? GetStringTarget32(SafeProcessHandle handle, IntPtr pp, int offset, Peb he)
 298    {
 2299        var us = new UNICODE_STRING_32();
 2300        if (SUCCEEDED(ReadProcessMemory(handle, pp + offset, ref us, new IntPtr(Marshal.SizeOf(us)), IntPtr.Zero), he))
 301        {
 2302            if (us.Buffer != 0)
 303            {
 2304                if (us.Length == 0)
 305                {
 0306                    return string.Empty;
 307                }
 308
 2309                string lpBuffer = us.GetEmptyBuffer();
 2310                if (SUCCEEDED(ReadProcessMemory(handle, new IntPtr(us.Buffer), lpBuffer, new IntPtr(us.Length), IntPtr.Z
 311                {
 2312                    return lpBuffer;
 313                }
 314            }
 315        }
 316
 0317        return null;
 318    }
 319
 320    private static int GetInt32Target64(SafeProcessHandle handle, long pp, int offset, Peb he)
 321    {
 0322        var ptr = IntPtr.Zero;
 0323        uint buf = 0;
 0324        if (SUCCEEDED(NtWow64ReadVirtualMemory64(handle, pp + offset, ref buf, sizeof(uint), IntPtr.Zero), he))
 325        {
 0326            ptr = new IntPtr(buf);
 0327            return ptr.ToInt32();
 328        }
 329
 0330        return default;
 331    }
 332
 333    private static string? GetStringTarget64(SafeProcessHandle handle, long pp, int offset, Peb he)
 334    {
 0335        var us = new UNICODE_STRING_WOW64();
 0336        if (SUCCEEDED(NtWow64ReadVirtualMemory64(handle, pp + offset, ref us, Marshal.SizeOf(us), IntPtr.Zero), he))
 337        {
 0338            if (us.Buffer != 0)
 339            {
 0340                if (us.Length == 0)
 341                {
 0342                    return string.Empty;
 343                }
 344
 0345                string lpBuffer = us.GetEmptyBuffer();
 0346                if (SUCCEEDED(NtWow64ReadVirtualMemory64(handle, us.Buffer, lpBuffer, us.Length, IntPtr.Zero), he))
 347                {
 0348                    return lpBuffer;
 349                }
 350            }
 351        }
 352
 0353        return null;
 354    }
 355
 356    private static int GetInt32(SafeProcessHandle handle, IntPtr pp, int offset, Peb he)
 357    {
 2358        var ptr = IntPtr.Zero;
 2359        if (SUCCEEDED(ReadProcessMemory(handle, pp + offset, ref ptr, new IntPtr(IntPtr.Size), IntPtr.Zero), he))
 360        {
 2361            return ptr.ToInt32();
 362        }
 363
 0364        return 0;
 365    }
 366
 367    private static string? GetString(SafeProcessHandle handle, IntPtr pp, int offset, Peb he)
 368    {
 2369        var us = new UNICODE_STRING();
 2370        if (SUCCEEDED(ReadProcessMemory(handle, pp + offset, ref us, new IntPtr(Marshal.SizeOf(us)), IntPtr.Zero), he))
 371        {
 2372            if (us.Buffer != IntPtr.Zero)
 373            {
 2374                if (us.Length == 0)
 375                {
 2376                    return string.Empty;
 377                }
 378
 2379                string lpBuffer = us.GetEmptyBuffer();
 2380                if (SUCCEEDED(ReadProcessMemory(handle, us.Buffer, lpBuffer, new IntPtr(us.Length), IntPtr.Zero), he))
 381                {
 2382                    return lpBuffer;
 383                }
 384            }
 385        }
 386
 0387        return null;
 388    }
 389
 390    /// <summary>
 391    /// Checks if <paramref name="status"/> is success, otherwise sets <c><paramref name="he"/>.SetError(errorCode: <par
 392    /// </summary>
 393    private static bool SUCCEEDED(uint status, Peb he, [CallerMemberName] string? callerName = null, [CallerFilePath] st
 394    {
 2395        if (status != STATUS_SUCCESS)
 396        {
 0397            he.SetError(errorCode: (int)status);
 0398            return false;
 399        }
 400
 2401        return true;
 402    }
 403
 404    /// <summary>
 405    /// Checks if <paramref name="status"/> is success, otherwise sets <c><paramref name="he"/>.SetError(errorCode: <par
 406    /// </summary>
 407    private static bool SUCCEEDED(int status, Peb he, [CallerMemberName] string? callerName = null, [CallerFilePath] str
 408    {
 2409        if (status != STATUS_SUCCESS)
 410        {
 0411            he.SetError(errorCode: status);
 0412            return false;
 413        }
 414
 2415        return true;
 416    }
 417
 418    /// <summary>
 419    /// Checks if <paramref name="status"/> is success, otherwise sets <c><paramref name="he"/>.SetError(errorCode: <see
 420    /// </summary>
 421    private static bool SUCCEEDED(bool result, Peb he, [CallerMemberName] string? callerName = null, [CallerFilePath] st
 422    {
 2423        if (!result)
 424        {
 2425            he.SetError(errorCode: Marshal.GetLastWin32Error());
 2426            return false;
 427        }
 428
 2429        return true;
 430    }
 431}

Methods/Properties

get_ProcessId()
set_ProcessId(int)
get_StartTime()
set_StartTime(System.DateTime)
get_SessionId()
set_SessionId(int)
get_ParentProcessId()
set_ParentProcessId(System.Nullable<int>)
get_ProcessName()
set_ProcessName(string)
get_CommandLine()
set_CommandLine(string)
get_CurrentDirectory()
set_CurrentDirectory(string)
get_WindowTitle()
set_WindowTitle(string)
get_ExecutableFullPath()
set_ExecutableFullPath(string)
get_DesktopInfo()
set_DesktopInfo(string)
get_Owner()
set_Owner(string)
get_HasError()
set_HasError(bool)
get_IsCritical()
set_IsCritical(System.Nullable<bool>)
get_IsPseudoProcess()
set_IsPseudoProcess(bool)
get_ProcessSequenceNumber()
set_ProcessSequenceNumber(System.Nullable<ulong>)
get_ProcessStartKey()
set_ProcessStartKey(System.Nullable<ulong>)
SetError(System.Exception, int)
.ctor(LockCheck.Windows.NativeMethods.SYSTEM_PROCESS_INFORMATION, System.Nullable<ulong>)
.ctor(int, System.DateTime)
Initialize()
InitTargetAnySelfAny(Microsoft.Win32.SafeHandles.SafeProcessHandle, LockCheck.Windows.NativeMethods.PebOffsets, LockCheck.Windows.Peb)
InitTarget64Self32(Microsoft.Win32.SafeHandles.SafeProcessHandle, LockCheck.Windows.NativeMethods.PebOffsets, LockCheck.Windows.Peb)
InitTarget32SelfAny(Microsoft.Win32.SafeHandles.SafeProcessHandle, LockCheck.Windows.NativeMethods.PebOffsets, LockCheck.Windows.Peb)
GetInt32Target32(Microsoft.Win32.SafeHandles.SafeProcessHandle, native int, int, LockCheck.Windows.Peb)
GetStringTarget32(Microsoft.Win32.SafeHandles.SafeProcessHandle, native int, int, LockCheck.Windows.Peb)
GetInt32Target64(Microsoft.Win32.SafeHandles.SafeProcessHandle, long, int, LockCheck.Windows.Peb)
GetStringTarget64(Microsoft.Win32.SafeHandles.SafeProcessHandle, long, int, LockCheck.Windows.Peb)
GetInt32(Microsoft.Win32.SafeHandles.SafeProcessHandle, native int, int, LockCheck.Windows.Peb)
GetString(Microsoft.Win32.SafeHandles.SafeProcessHandle, native int, int, LockCheck.Windows.Peb)
SUCCEEDED(uint, LockCheck.Windows.Peb, string, string, int)
SUCCEEDED(int, LockCheck.Windows.Peb, string, string, int)
SUCCEEDED(bool, LockCheck.Windows.Peb, string, string, int)