Skip to content

Commit a9f5453

Browse files
committed
Commence setting up parsing for the final PortableExecutable data directory (debug).
1 parent eb1982b commit a9f5453

30 files changed

Lines changed: 1539 additions & 8 deletions

.github/copilot-instructions.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,11 +146,15 @@ When working on the PowerShell module in VS Code:
146146
- **Resource Management**: Use nested try/finally blocks for COM cleanup and resource management rather than flat structures with nullable checks, even if it results in deep indentation.
147147
- **Null Checks**: When checking if a method returns a non-null value and using it, prefer pattern matching with `is` to tightly scope the variable: `if (Method() is Type variable) { ... }` instead of `var variable = Method(); if (variable != null) { ... }`. This scopes the variable to the branch where it's needed and makes the code more robust.
148148
- **Inline Factory Method Usage**: When a factory method already provides context for what a value represents (e.g., `FromOrdinal()`), inline the expression directly rather than storing it in a temporary variable first. Example: prefer `entries.Add(DelayImportEntry.FromOrdinal((ushort)(thunkData & 0xFFFF)));` over creating a temporary `ushort ordinal = ...` variable.
149+
- **Ternary Expression Formatting**: Favor the positive/valid scenario first. Example: `size >= HeaderSize ? new Instance(...) : null` instead of `size < HeaderSize ? null : new Instance(...)`. The only exception is when throwing - check for the invalid condition first, then throw.
149150
- **ThrowIfNullOrWhiteSpace Usage**: When using the `ThrowIfNullOrWhiteSpace` extension method, do not pass explicit `nameof()` arguments — rely on the `[CallerMemberName]` attribute to automatically populate the name parameter.
150151
- **SafeHandle Validation**: When validating SafeHandle parameters, avoid standalone `ThrowIfNullOrInvalid` or `ThrowIfNullOrClosed` calls that discard return values; inline the call into the subsequent usage (e.g., chain into `DangerousAddRef` or PInvoke argument).
151152
- **RemoveFontResource Input**: Do not pre-validate `RemoveFontResource` input with file existence checks; it can operate on file names and not necessarily full paths.
152153
- **Named Pipe Security**: Preserve strict named pipe security and reject solutions that weaken ACLs (e.g., creating pipes without explicit PipeSecurity).
153154

155+
### File Modification Guidelines
156+
- **One Class Per File**: Each class should have its own dedicated file. Do not put multiple classes/records in a single file.
157+
154158
### Testing Strategy
155159
`powershell.exe -ExecutionPolicy Bypass -File build.ps1`
156160

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
namespace PSADT.Interop
2+
{
3+
/// <summary>
4+
/// Defines the various CODEVIEW format signatures used for debugging information.
5+
/// </summary>
6+
/// <remarks>Each value corresponds to a specific version of the CODEVIEW format, which is essential for
7+
/// interpreting debugging data correctly.</remarks>
8+
[System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1008:Enums should have zero value", Justification = "These are signature values, not flags.")]
9+
[System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1028:Enum Storage should be Int32", Justification = "The type is correct for the underlying Win32 API.")]
10+
[System.Diagnostics.CodeAnalysis.SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores", Justification = "These values are precisely as they're defined in the Win32 API.")]
11+
[System.Diagnostics.CodeAnalysis.SuppressMessage("Naming", "CA1712:Do not prefix enum values with type name", Justification = "These are as they're defined within the API.")]
12+
public enum CODEVIEW_SIGNATURE : uint
13+
{
14+
/// <summary>
15+
/// Represents the signature for the CODEVIEW format version NB09.
16+
/// </summary>
17+
CODEVIEW_SIGNATURE_NB09 = 0x3930424E,
18+
19+
/// <summary>
20+
/// Represents the signature for the CODEVIEW format version NB10, which is associated with PDB 2.0 files.
21+
/// </summary>
22+
CODEVIEW_SIGNATURE_NB10 = 0x3031424E,
23+
24+
/// <summary>
25+
/// Represents the signature for the CODEVIEW format version NB11.
26+
/// </summary>
27+
CODEVIEW_SIGNATURE_NB11 = 0x3131424E,
28+
29+
/// <summary>
30+
/// Represents the signature for the RSDS code view format, used in debugging information.
31+
/// </summary>
32+
CODEVIEW_SIGNATURE_RSDS = 0x53445352,
33+
}
34+
}

src/PSADT/PSADT.Interop/FRAME.cs

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
namespace PSADT.Interop
2+
{
3+
/// <summary>
4+
/// Defines the various frame types used in Windows structured exception handling and API interactions.
5+
/// </summary>
6+
/// <remarks>This enumeration includes constants that represent specific frame types, which are essential
7+
/// for low-level programming scenarios involving exception handling, stack unwinding, and thread-specific storage
8+
/// in Windows environments.</remarks>
9+
[System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1028:Enum Storage should be Int32", Justification = "<Pending>")]
10+
[System.Diagnostics.CodeAnalysis.SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores", Justification = "<Pending>")]
11+
[System.Diagnostics.CodeAnalysis.SuppressMessage("Naming", "CA1712:Do not prefix enum values with type name", Justification = "<Pending>")]
12+
public enum FRAME : byte
13+
{
14+
/// <summary>
15+
/// Represents the FRAME_FPO frame type used in Windows structured exception handling.
16+
/// </summary>
17+
/// <remarks>This constant corresponds to the FRAME_FPO value defined in the Windows.Win32.PInvoke
18+
/// namespace. It is typically used to identify a specific frame type when working with low-level exception
19+
/// handling or stack unwinding operations in Windows environments.</remarks>
20+
FRAME_FPO = (byte)Windows.Win32.PInvoke.FRAME_FPO,
21+
22+
/// <summary>
23+
/// Represents the constant value for FRAME_TRAP used in Windows API calls.
24+
/// </summary>
25+
/// <remarks>This constant is typically used in low-level programming scenarios involving Windows
26+
/// API interactions, particularly in the context of frame manipulation.</remarks>
27+
FRAME_TRAP = (byte)Windows.Win32.PInvoke.FRAME_TRAP,
28+
29+
/// <summary>
30+
/// Represents the FRAME_TSS structure used for thread-specific storage in Windows API operations.
31+
/// </summary>
32+
/// <remarks>This field references the FRAME_TSS structure defined in the Windows.Win32.PInvoke
33+
/// namespace, which facilitates management of thread-specific data in Windows environments. Use this member
34+
/// when interacting with APIs that require thread-local storage structures.</remarks>
35+
FRAME_TSS = (byte)Windows.Win32.PInvoke.FRAME_TSS,
36+
37+
/// <summary>
38+
/// Represents a constant value indicating that a frame is non-FPO (Frame Pointer Omission).
39+
/// </summary>
40+
/// <remarks>This constant is used in the context of Windows API calls to specify the frame type
41+
/// for stack unwinding. It is important for developers working with low-level memory management and
42+
/// debugging.</remarks>
43+
FRAME_NONFPO = (byte)Windows.Win32.PInvoke.FRAME_NONFPO,
44+
}
45+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
namespace PSADT.Interop
2+
{
3+
/// <summary>
4+
/// Defines the set of signatures used to identify Profile-Guided Optimization (PGO) debug information within image
5+
/// files. These signatures distinguish between various optimization techniques, such as Link-Time Code Generation
6+
/// (LTCG), standard PGO, and sample-based PGO, enabling tools and compilers to interpret and process optimization
7+
/// data appropriately.
8+
/// </summary>
9+
/// <remarks>This enumeration is intended for internal use when parsing or generating image files that
10+
/// contain PGO-related debug information. Each value corresponds to a specific optimization strategy or data
11+
/// format, which may affect how the debug information is handled during compilation or analysis. Refer to official
12+
/// documentation or relevant image file specifications for details on the meaning and usage of each
13+
/// signature.</remarks>
14+
[System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1008:Enums should have zero value", Justification = "These are signature values, not flags.")]
15+
[System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1028:Enum Storage should be Int32", Justification = "The type is correct for the underlying Win32 API.")]
16+
[System.Diagnostics.CodeAnalysis.SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores", Justification = "These values are precisely as they're defined in the Win32 API.")]
17+
[System.Diagnostics.CodeAnalysis.SuppressMessage("Naming", "CA1712:Do not prefix enum values with type name", Justification = "These are as they're defined within the API.")]
18+
public enum IMAGE_DEBUG_POGO_SIGNATURE : uint
19+
{
20+
/// <summary>
21+
/// Represents the signature for the POGO (Profile-Guided Optimization) debug information in the context of
22+
/// Link-Time Code Generation (LTCG).
23+
/// </summary>
24+
/// <remarks>https://github.com/winsiderss/systeminformer/blob/af8f8d245a5e8c934bbbe5de9de3869974f56a4e/phnt/include/ntimage.h#L34</remarks>
25+
IMAGE_DEBUG_POGO_SIGNATURE_LTCG = 0x4C544347,
26+
27+
/// <summary>
28+
/// Represents the signature for the POGO (Profile-Guided Optimization) debug information in an image file.
29+
/// </summary>
30+
/// <remarks>https://github.com/winsiderss/systeminformer/blob/af8f8d245a5e8c934bbbe5de9de3869974f56a4e/phnt/include/ntimage.h#L35</remarks>
31+
IMAGE_DEBUG_POGO_SIGNATURE_PGI = 0x50474900,
32+
33+
/// <summary>
34+
/// Represents the signature for Profile-Guided Optimization (PGO) data in image debug information.
35+
/// </summary>
36+
/// <remarks>https://github.com/winsiderss/systeminformer/blob/af8f8d245a5e8c934bbbe5de9de3869974f56a4e/phnt/include/ntimage.h#L36</remarks>
37+
IMAGE_DEBUG_POGO_SIGNATURE_PGO = 0x50474F00,
38+
39+
/// <summary>
40+
/// Represents the signature for the POGO (Profile-Guided Optimization) debug information in an image.
41+
/// </summary>
42+
/// <remarks>https://github.com/winsiderss/systeminformer/blob/af8f8d245a5e8c934bbbe5de9de3869974f56a4e/phnt/include/ntimage.h#L37</remarks>
43+
IMAGE_DEBUG_POGO_SIGNATURE_PGU = 0x50475500,
44+
45+
/// <summary>
46+
/// Represents the signature for the SPGO (Sample-Based Profile-Guided Optimization) debug information in an
47+
/// image.
48+
/// </summary>
49+
/// <remarks>https://github.com/winsiderss/systeminformer/blob/af8f8d245a5e8c934bbbe5de9de3869974f56a4e/phnt/include/ntimage.h#L38</remarks>
50+
IMAGE_DEBUG_POGO_SIGNATURE_SPGO = 0x5350474F,
51+
}
52+
}

src/PSADT/PSADT.Interop/IMAGE_DEBUG_TYPE.cs

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,10 +51,86 @@ public enum IMAGE_DEBUG_TYPE : uint
5151
/// </summary>
5252
IMAGE_DEBUG_TYPE_FIXUP = Windows.Win32.System.Diagnostics.Debug.IMAGE_DEBUG_TYPE.IMAGE_DEBUG_TYPE_FIXUP,
5353

54+
/// <summary>
55+
/// Represents OMAP (Optimized MAP) data for mapping from optimized addresses to source addresses.
56+
/// </summary>
57+
/// <remarks>Contains an array of OMAP structures used when code has been optimized and addresses
58+
/// have been rearranged. Each entry maps an RVA to its original RVA.</remarks>
59+
IMAGE_DEBUG_TYPE_OMAP_TO_SRC = 7,
60+
61+
/// <summary>
62+
/// Represents OMAP (Optimized MAP) data for mapping from source addresses to optimized addresses.
63+
/// </summary>
64+
/// <remarks>Contains an array of OMAP structures used when code has been optimized and addresses
65+
/// have been rearranged. Each entry maps an original RVA to its optimized RVA.</remarks>
66+
IMAGE_DEBUG_TYPE_OMAP_FROM_SRC = 8,
67+
5468
/// <summary>
5569
/// Represents the Borland debug type in the Windows debugging system.
5670
/// </summary>
5771
/// <remarks>This value identifies debug information specific to Borland compilers.</remarks>
5872
IMAGE_DEBUG_TYPE_BORLAND = Windows.Win32.System.Diagnostics.Debug.IMAGE_DEBUG_TYPE.IMAGE_DEBUG_TYPE_BORLAND,
73+
74+
/// <summary>
75+
/// Reserved for future use (also known as BBT - Branch Boundary Table).
76+
/// </summary>
77+
IMAGE_DEBUG_TYPE_BBT = 10,
78+
79+
/// <summary>
80+
/// Contains a CLSID (Class ID) GUID.
81+
/// </summary>
82+
IMAGE_DEBUG_TYPE_CLSID = 11,
83+
84+
/// <summary>
85+
/// Contains Visual C++ feature information including compiler flags and counts.
86+
/// </summary>
87+
/// <remarks>Contains five uint values: PreVC11, C/C++, /GS, /sdl, and guardN counts.</remarks>
88+
IMAGE_DEBUG_TYPE_VC_FEATURE = 12,
89+
90+
/// <summary>
91+
/// Contains Profile Guided Optimization (POGO) information.
92+
/// </summary>
93+
/// <remarks>Contains a signature followed by POGO entries with RVA, size, and name for each section.</remarks>
94+
IMAGE_DEBUG_TYPE_POGO = 13,
95+
96+
/// <summary>
97+
/// Indicates Incremental Link Time Code Generation was used.
98+
/// </summary>
99+
/// <remarks>This is a marker type with no associated data.</remarks>
100+
IMAGE_DEBUG_TYPE_ILTCG = 14,
101+
102+
/// <summary>
103+
/// Contains Intel Memory Protection Extensions (MPX) information.
104+
/// </summary>
105+
IMAGE_DEBUG_TYPE_MPX = 15,
106+
107+
/// <summary>
108+
/// Contains reproducible build hash information.
109+
/// </summary>
110+
/// <remarks>Contains a hash that can be used to verify reproducible builds.</remarks>
111+
IMAGE_DEBUG_TYPE_REPRO = 16,
112+
113+
/// <summary>
114+
/// Contains embedded portable PDB debug information.
115+
/// </summary>
116+
/// <remarks>The data contains a compressed portable PDB embedded directly in the PE file.</remarks>
117+
IMAGE_DEBUG_TYPE_EMBEDDED_PORTABLE_PDB = 17,
118+
119+
/// <summary>
120+
/// Contains Static Profile Guided Optimization (SPGO) information.
121+
/// </summary>
122+
IMAGE_DEBUG_TYPE_SPGO = 18,
123+
124+
/// <summary>
125+
/// Contains the hash of the PDB file for verification.
126+
/// </summary>
127+
/// <remarks>Used to verify the PDB file matches the executable.</remarks>
128+
IMAGE_DEBUG_TYPE_PDBCHECKSUM = 19,
129+
130+
/// <summary>
131+
/// Contains extended DLL characteristics flags.
132+
/// </summary>
133+
/// <remarks>Contains additional DLL characteristics that don't fit in the standard header field.</remarks>
134+
IMAGE_DEBUG_TYPE_EX_DLLCHARACTERISTICS = 20,
59135
}
60136
}

src/PSADT/PSADT.Interop/NativeMethods.txt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,11 @@ FindNextFile
3535
FindWindow
3636
FOLDERID_UserProfiles
3737
FormatMessage
38+
FPO_DATA
39+
FRAME_FPO
40+
FRAME_TRAP
41+
FRAME_TSS
42+
FRAME_NONFPO
3843
GENERIC_MAPPING
3944
GetApplicationUserModelId
4045
GetCurrentProcess
@@ -109,7 +114,10 @@ IExecAction
109114
IMAGE_BASE_RELOCATION
110115
IMAGE_BOUND_FORWARDER_REF
111116
IMAGE_BOUND_IMPORT_DESCRIPTOR
117+
IMAGE_COFF_SYMBOLS_HEADER
112118
IMAGE_COR20_HEADER
119+
IMAGE_DEBUG_MISC
120+
IMAGE_DEBUG_MISC_EXENAME
113121
IMAGE_DEBUG_DIRECTORY
114122
IMAGE_DELAYLOAD_DESCRIPTOR
115123
IMAGE_DLL_CHARACTERISTICS
@@ -220,6 +228,7 @@ NtQueryObject
220228
NtQuerySystemInformation
221229
OBJECT_ATTRIBUTE_FLAGS
222230
OBJECT_NAME_INFORMATION
231+
OMAP
223232
OOBEComplete
224233
OpenProcess
225234
OpenProcessToken
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
using System.IO;
2+
using PSADT.Interop;
3+
4+
namespace PSADT.PortableExecutable
5+
{
6+
/// <summary>
7+
/// Represents CodeView debug information parsed from an IMAGE_DEBUG_TYPE_CODEVIEW entry.
8+
/// </summary>
9+
/// <remarks>
10+
/// CodeView data contains a 4-byte signature that identifies the format:
11+
/// - "NB09" (0x3930424E): CodeView 4.10 format (VC++ 2.x), embedded debug info
12+
/// - "NB10" (0x3031424E): PDB 2.0 format (VC++ 4.x-6.x), external PDB
13+
/// - "NB11" (0x3131424E): CodeView 5.0/C7 format, embedded debug info
14+
/// - "RSDS" (0x53445352): PDB 7.0 format (VS 2002+), external PDB with GUID
15+
/// </remarks>
16+
public abstract record CvInfoBase
17+
{
18+
/// <summary>
19+
/// Parses CodeView data from the given binary reader.
20+
/// </summary>
21+
/// <param name="reader">The binary reader positioned at the CodeView data.</param>
22+
/// <param name="size">The size of the CodeView data in bytes.</param>
23+
/// <returns>A CodeViewInfo instance, or null if the format is unrecognized.</returns>
24+
internal static CvInfoBase? Parse(BinaryReader reader, uint size)
25+
{
26+
return size < 4 ? null : (CODEVIEW_SIGNATURE)reader.ReadUInt32() switch
27+
{
28+
CODEVIEW_SIGNATURE.CODEVIEW_SIGNATURE_NB09 => CvInfoCv41.Parse(reader),
29+
CODEVIEW_SIGNATURE.CODEVIEW_SIGNATURE_NB10 => CvInfoPdb20.Parse(reader),
30+
CODEVIEW_SIGNATURE.CODEVIEW_SIGNATURE_NB11 => CvInfoCv50.Parse(reader),
31+
CODEVIEW_SIGNATURE.CODEVIEW_SIGNATURE_RSDS => CvInfoPdb70.Parse(reader),
32+
_ => null
33+
};
34+
}
35+
36+
/// <summary>
37+
/// Initializes a new instance of the CodeViewInfo class.
38+
/// </summary>
39+
/// <param name="signature">The signature identifying the CodeView format.</param>
40+
/// <param name="age">The age value used for PDB matching.</param>
41+
/// <param name="pdbPath">The path to the PDB file.</param>
42+
private protected CvInfoBase(CODEVIEW_SIGNATURE signature, uint age, string? pdbPath = null)
43+
{
44+
Signature = signature;
45+
Age = age;
46+
PdbPath = !string.IsNullOrWhiteSpace(pdbPath) ? pdbPath : null;
47+
}
48+
49+
/// <summary>
50+
/// Gets the signature identifying the CodeView format.
51+
/// </summary>
52+
public CODEVIEW_SIGNATURE Signature { get; }
53+
54+
/// <summary>
55+
/// Gets the age value used for PDB matching.
56+
/// </summary>
57+
public uint Age { get; }
58+
59+
/// <summary>
60+
/// Gets the path to the PDB file.
61+
/// </summary>
62+
public string? PdbPath { get; }
63+
}
64+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
using System.IO;
2+
using PSADT.Interop;
3+
4+
namespace PSADT.PortableExecutable
5+
{
6+
/// <summary>
7+
/// Represents NB09 (CodeView 4.10) debug information.
8+
/// </summary>
9+
/// <remarks>
10+
/// The NB09 format is a legacy format used by VC++ 2.x compilers and contains:
11+
/// - 4-byte signature "NB09" (0x3930424E)
12+
/// - 4-byte file position offset to debug info within the PE file
13+
/// This format predates external PDB files; debug info is embedded in the executable.
14+
/// </remarks>
15+
public sealed record CvInfoCv41 : CvInfoBase
16+
{
17+
/// <summary>
18+
/// Parses NB09 CodeView data from the given reader.
19+
/// </summary>
20+
internal static CvInfoCv41 Parse(BinaryReader reader)
21+
{
22+
// Read the 4-byte file position offset
23+
return new(reader.ReadUInt32());
24+
}
25+
26+
/// <summary>
27+
/// Initializes a new instance of the CvInfoCv41 class.
28+
/// </summary>
29+
private CvInfoCv41(uint filePosition) : base(CODEVIEW_SIGNATURE.CODEVIEW_SIGNATURE_NB09, age: 0)
30+
{
31+
FilePosition = filePosition;
32+
}
33+
34+
/// <summary>
35+
/// Gets the file position offset to the debug information within the PE file.
36+
/// </summary>
37+
/// <remarks>
38+
/// This is an offset from the start of the file to the embedded CodeView debug data.
39+
/// </remarks>
40+
public uint FilePosition { get; }
41+
}
42+
}

0 commit comments

Comments
 (0)