Skip to content

Commit df6fb0e

Browse files
committed
Added support for user atoms.
1 parent a6f0eaa commit df6fb0e

3 files changed

Lines changed: 140 additions & 52 deletions

File tree

NtApiDotNet/NtAtom.cs

Lines changed: 128 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -26,17 +26,86 @@ public sealed class NtAtom
2626
{
2727
#region Constructors
2828

29-
internal NtAtom(ushort atom)
29+
internal NtAtom(ushort atom, bool global)
3030
{
3131
Atom = atom;
32+
Global = global;
33+
}
34+
35+
#endregion
36+
37+
#region Private Members
38+
private static IEnumerable<NtAtom> GetGlobalAtoms()
39+
{
40+
int size = 1024;
41+
while (size < 5 * 1024 * 1024)
42+
{
43+
using (var buffer = new SafeStructureInOutBuffer<AtomTableInformation>(size, true))
44+
{
45+
NtStatus status = NtSystemCalls.NtQueryInformationAtom(0,
46+
AtomInformationClass.AtomTableInformation, buffer, buffer.Length, out int return_length);
47+
if (status.IsSuccess())
48+
{
49+
AtomTableInformation table = buffer.Result;
50+
IntPtr data = buffer.Data.DangerousGetHandle();
51+
ushort[] atoms = new ushort[table.NumberOfAtoms];
52+
buffer.Data.ReadArray(0, atoms, 0, atoms.Length);
53+
return atoms.Select(a => new NtAtom(a, true)).ToArray();
54+
}
55+
else if (status != NtStatus.STATUS_INFO_LENGTH_MISMATCH)
56+
{
57+
throw new NtException(status);
58+
}
59+
size *= 2;
60+
}
61+
}
62+
return new NtAtom[0];
63+
}
64+
65+
private static IEnumerable<NtAtom> GetUserAtoms()
66+
{
67+
List<NtAtom> atoms = new List<NtAtom>();
68+
for (int atom = 0xC000; atom < 0x10000; ++atom)
69+
{
70+
var next_atom = new NtAtom((ushort)atom, false);
71+
if (next_atom.GetName(false).IsSuccess)
72+
{
73+
atoms.Add(next_atom);
74+
}
75+
}
76+
return atoms.AsReadOnly();
77+
}
78+
79+
private NtResult<string> GetGlobalName(bool throw_on_error)
80+
{
81+
using (var buffer = new SafeStructureInOutBuffer<AtomBasicInformation>(2048, false))
82+
{
83+
return NtSystemCalls.NtQueryInformationAtom(Atom, AtomInformationClass.AtomBasicInformation,
84+
buffer, buffer.Length, out int return_length)
85+
.CreateResult(throw_on_error, () => buffer.Data.ReadUnicodeString(buffer.Result.NameLength / 2));
86+
}
87+
}
88+
89+
private NtResult<string> GetUserName(bool throw_on_error)
90+
{
91+
using (UnicodeStringAllocated str = new UnicodeStringAllocated(2048))
92+
{
93+
int length = NtSystemCalls.NtUserGetAtomName(Atom, str);
94+
if (length == 0)
95+
{
96+
return NtStatus.STATUS_OBJECT_NAME_NOT_FOUND.CreateResultFromError<string>(throw_on_error);
97+
}
98+
str.String.Length = (ushort)(length * 2);
99+
return str.ToString().CreateResult();
100+
}
32101
}
33102

34103
#endregion
35104

36105
#region Static Methods
37106

38107
/// <summary>
39-
/// Add an atom name
108+
/// Add a global atom name
40109
/// </summary>
41110
/// <param name="name">The name to add</param>
42111
/// <param name="flags">Flags for the add.</param>
@@ -47,17 +116,17 @@ public static NtResult<NtAtom> Add(string name, AddAtomFlags flags, bool throw_o
47116
if (flags == AddAtomFlags.None)
48117
{
49118
return NtSystemCalls.NtAddAtom(name + "\0", (name.Length + 1) * 2,
50-
out ushort atom).CreateResult(throw_on_error, () => new NtAtom(atom));
119+
out ushort atom).CreateResult(throw_on_error, () => new NtAtom(atom, true));
51120
}
52121
else
53122
{
54123
return NtSystemCalls.NtAddAtomEx(name + "\0", (name.Length + 1) * 2,
55-
out ushort atom, flags).CreateResult(throw_on_error, () => new NtAtom(atom));
124+
out ushort atom, flags).CreateResult(throw_on_error, () => new NtAtom(atom, true));
56125
}
57126
}
58127

59128
/// <summary>
60-
/// Add an atom name
129+
/// Add a global atom name
61130
/// </summary>
62131
/// <param name="name">The name to add</param>
63132
/// <param name="flags">Flags for the add.</param>
@@ -68,7 +137,7 @@ public static NtAtom Add(string name, AddAtomFlags flags)
68137
}
69138

70139
/// <summary>
71-
/// Add an atom name
140+
/// Add a global atom name
72141
/// </summary>
73142
/// <param name="name">The name to add</param>
74143
/// <param name="throw_on_error">True to throw on error.</param>
@@ -79,7 +148,7 @@ public static NtResult<NtAtom> Add(string name, bool throw_on_error)
79148
}
80149

81150
/// <summary>
82-
/// Add an atom name
151+
/// Add a global atom name
83152
/// </summary>
84153
/// <param name="name">The name to add</param>
85154
/// <returns>A reference to the atom</returns>
@@ -89,19 +158,19 @@ public static NtAtom Add(string name)
89158
}
90159

91160
/// <summary>
92-
/// Find an atom by name.
161+
/// Find a global atom by name.
93162
/// </summary>
94163
/// <param name="name">The name of the atom.</param>
95164
/// <param name="throw_on_error">True to throw on error.</param>
96165
/// <returns>The found atom.</returns>
97166
public static NtResult<NtAtom> Find(string name, bool throw_on_error)
98167
{
99168
return NtSystemCalls.NtFindAtom(name + "\0", (name.Length + 1) * 2,
100-
out ushort atom).CreateResult(throw_on_error, () => new NtAtom(atom));
169+
out ushort atom).CreateResult(throw_on_error, () => new NtAtom(atom, true));
101170
}
102171

103172
/// <summary>
104-
/// Find an atom by name.
173+
/// Find a global atom by name.
105174
/// </summary>
106175
/// <param name="name">The name of the atom.</param>
107176
/// <returns>The found atom.</returns>
@@ -111,25 +180,37 @@ public static NtAtom Find(string name)
111180
}
112181

113182
/// <summary>
114-
/// Query if the atom exists.
183+
/// Query if a global atom exists.
115184
/// </summary>
116185
/// <param name="atom">The atom to check.</param>
117186
/// <returns>True if the atom exists.</returns>
118187
public static bool Exists(ushort atom)
119188
{
120-
return new NtAtom(atom).GetName(false).IsSuccess;
189+
return Exists(atom, true);
121190
}
122191

123192
/// <summary>
124-
/// Open an atom by number.
193+
/// Query if the atom exists.
194+
/// </summary>
195+
/// <param name="atom">The atom to check.</param>
196+
/// <param name="global">Specify true to check for a global atom, otherwise gets a user atom.</param>
197+
/// <returns>True if the atom exists.</returns>
198+
public static bool Exists(ushort atom, bool global)
199+
{
200+
return new NtAtom(atom, global).GetName(false).IsSuccess;
201+
}
202+
203+
/// <summary>
204+
/// Open a global atom by number.
125205
/// </summary>
126206
/// <param name="atom">The atom to open.</param>
127207
/// <param name="check_exists">True to check atom exists.</param>
208+
/// <param name="global">True to open a global atom, otherwise a user atom.</param>
128209
/// <param name="throw_on_error">True to throw on error.</param>
129210
/// <returns>The atom object.</returns>
130-
public static NtResult<NtAtom> Open(ushort atom, bool check_exists, bool throw_on_error)
211+
public static NtResult<NtAtom> Open(ushort atom, bool check_exists, bool global, bool throw_on_error)
131212
{
132-
NtAtom ret = new NtAtom(atom);
213+
NtAtom ret = new NtAtom(atom, global);
133214
if (check_exists)
134215
{
135216
return ret.GetName(false).Status.CreateResult(throw_on_error, () => ret);
@@ -138,7 +219,19 @@ public static NtResult<NtAtom> Open(ushort atom, bool check_exists, bool throw_o
138219
}
139220

140221
/// <summary>
141-
/// Open an atom by number.
222+
/// Open a global atom by number.
223+
/// </summary>
224+
/// <param name="atom">The atom to open.</param>
225+
/// <param name="check_exists">True to check atom exists.</param>
226+
/// <param name="throw_on_error">True to throw on error.</param>
227+
/// <returns>The atom object.</returns>
228+
public static NtResult<NtAtom> Open(ushort atom, bool check_exists, bool throw_on_error)
229+
{
230+
return Open(atom, check_exists, true, throw_on_error);
231+
}
232+
233+
/// <summary>
234+
/// Open a global atom by number.
142235
/// </summary>
143236
/// <param name="atom">The atom to open.</param>
144237
/// <param name="check_exists">True to check atom exists.</param>
@@ -149,7 +242,7 @@ public static NtAtom Open(ushort atom, bool check_exists)
149242
}
150243

151244
/// <summary>
152-
/// Open an atom by number.
245+
/// Open a global atom by number.
153246
/// </summary>
154247
/// <param name="atom">The atom to open.</param>
155248
/// <returns>The atom object.</returns>
@@ -162,37 +255,24 @@ public static NtAtom Open(ushort atom)
162255
/// Enumerate all atoms.
163256
/// </summary>
164257
/// <returns>An enumeration of all atoms on the system.</returns>
258+
public static IEnumerable<NtAtom> GetAtoms(bool global)
259+
{
260+
return global ? GetGlobalAtoms() : GetUserAtoms();
261+
}
262+
263+
/// <summary>
264+
/// Enumerate all global atoms.
265+
/// </summary>
266+
/// <returns>An enumeration of all atoms on the system.</returns>
165267
public static IEnumerable<NtAtom> GetAtoms()
166268
{
167-
int size = 1024;
168-
while (size < 5 * 1024 * 1024)
169-
{
170-
using (var buffer = new SafeStructureInOutBuffer<AtomTableInformation>(size, true))
171-
{
172-
NtStatus status = NtSystemCalls.NtQueryInformationAtom(0,
173-
AtomInformationClass.AtomTableInformation, buffer, buffer.Length, out int return_length);
174-
if (status.IsSuccess())
175-
{
176-
AtomTableInformation table = buffer.Result;
177-
IntPtr data = buffer.Data.DangerousGetHandle();
178-
ushort[] atoms = new ushort[table.NumberOfAtoms];
179-
buffer.Data.ReadArray(0, atoms, 0, atoms.Length);
180-
return atoms.Select(a => new NtAtom(a));
181-
}
182-
else if (status != NtStatus.STATUS_INFO_LENGTH_MISMATCH)
183-
{
184-
throw new NtException(status);
185-
}
186-
size *= 2;
187-
}
188-
}
189-
return new NtAtom[0];
269+
return GetAtoms(true);
190270
}
191271
#endregion
192272

193273
#region Public Methods
194274
/// <summary>
195-
/// Delete an atom.
275+
/// Delete a global atom.
196276
/// </summary>
197277
/// <param name="throw_on_error">True to throw on error.</param>
198278
/// <returns>The NT status code.</returns>
@@ -202,7 +282,7 @@ public NtStatus Delete(bool throw_on_error)
202282
}
203283

204284
/// <summary>
205-
/// Delete an atom.
285+
/// Delete a global atom.
206286
/// </summary>
207287
public void Delete()
208288
{
@@ -216,12 +296,7 @@ public void Delete()
216296
/// <returns>The name of the atom.</returns>
217297
public NtResult<string> GetName(bool throw_on_error)
218298
{
219-
using (var buffer = new SafeStructureInOutBuffer<AtomBasicInformation>(2048, false))
220-
{
221-
return NtSystemCalls.NtQueryInformationAtom(Atom, AtomInformationClass.AtomBasicInformation,
222-
buffer, buffer.Length, out int return_length)
223-
.CreateResult(throw_on_error, () => buffer.Data.ReadUnicodeString(buffer.Result.NameLength / 2));
224-
}
299+
return Global ? GetGlobalName(throw_on_error) : GetUserName(throw_on_error);
225300
}
226301

227302
#endregion
@@ -239,6 +314,11 @@ public NtResult<string> GetName(bool throw_on_error)
239314
/// <returns>The name of the atom</returns>
240315
public string Name => GetName(true).Result;
241316

317+
/// <summary>
318+
/// If true indicates this is a global atom, otherwise it's a user atom.
319+
/// </summary>
320+
public bool Global { get; }
321+
242322
#endregion
243323
}
244324
}

NtApiDotNet/NtAtomNative.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,9 @@ public static extern NtStatus NtQueryInformationAtom(
6969
int AtomInformationLength,
7070
out int ReturnLength
7171
);
72+
73+
[DllImport("win32u.dll", SetLastError = true)]
74+
public static extern int NtUserGetAtomName(ushort Atom, UnicodeStringAllocated Name);
7275
}
7376
#pragma warning restore 1591
7477
}

NtObjectManager/NtObjectManager.psm1

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7185,13 +7185,15 @@ function Get-NtTypeAccess {
71857185

71867186
<#
71877187
.SYNOPSIS
7188-
Get an ATOM objects.
7188+
Get an ATOM object.
71897189
.DESCRIPTION
71907190
This cmdlet gets all ATOM objects or by name or atom.
71917191
.PARAMETER Atom
71927192
Specify the ATOM to get.
71937193
.PARAMETER Name
71947194
Specify the name of the ATOM to get.
7195+
.PARAMETER User
7196+
Specify to get a user atom rather than a global.
71957197
.INPUTS
71967198
None
71977199
.OUTPUTS
@@ -7203,12 +7205,15 @@ function Get-NtAtom {
72037205
[Parameter(Mandatory, ParameterSetName = "FromAtom")]
72047206
[uint16]$Atom,
72057207
[Parameter(Mandatory, Position = 0, ParameterSetName = "FromName")]
7206-
[string]$Name
7208+
[string]$Name,
7209+
[Parameter(ParameterSetName = "All")]
7210+
[Parameter(ParameterSetName = "FromAtom")]
7211+
[switch]$User
72077212
)
72087213

72097214
switch ($PSCmdlet.ParameterSetName) {
7210-
"All" { [NtApiDotNet.NtAtom]::GetAtoms() | Write-Output }
7211-
"FromAtom" { [NtApiDotNet.NtAtom]::Open($Atom) | Write-Output }
7215+
"All" { [NtApiDotNet.NtAtom]::GetAtoms(!$User) | Write-Output }
7216+
"FromAtom" { [NtApiDotNet.NtAtom]::Open($Atom, $true, !$User, $true).Result | Write-Output }
72127217
"FromName" { [NtApiDotNet.NtAtom]::Find($Name) | Write-Output }
72137218
}
72147219
}

0 commit comments

Comments
 (0)