Skip to content

Commit 4174256

Browse files
committed
Added support to fixup structure and parameter names if you have private symbols.
1 parent f2e8895 commit 4174256

8 files changed

Lines changed: 238 additions & 18 deletions

File tree

NtApiDotNet/Ndr/NdrParser.cs

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
using NtApiDotNet.Utilities.Memory;
2121
using NtApiDotNet.Win32;
2222
using NtApiDotNet.Win32.Debugger;
23+
using NtApiDotNet.Win32.Rpc;
2324
using System;
2425
using System.Collections.Generic;
2526
using System.ComponentModel;
@@ -130,6 +131,10 @@ public enum NdrParserFlags
130131
/// Ignore processing any complex user marshal types.
131132
/// </summary>
132133
IgnoreUserMarshal = 1,
134+
/// <summary>
135+
/// Resolve structure names, required private symbols.
136+
/// </summary>
137+
ResolveStructureNames = 2,
133138
}
134139

135140
/// <summary>
@@ -153,6 +158,136 @@ private static NdrRpcServerInterface ReadRpcServerInterface(IMemoryReader reader
153158
server_interface.GetProtSeq(reader).Select(s => new NdrProtocolSequenceEndpoint(s, reader)));
154159
}
155160

161+
private static UserDefinedTypeInformation GetUDTType(TypeInformation type_info)
162+
{
163+
if (type_info is PointerTypeInformation pointer_type)
164+
{
165+
return GetUDTType(pointer_type.PointerType);
166+
}
167+
168+
if (type_info is ArrayTypeInformation array_type)
169+
{
170+
return GetUDTType(array_type.ArrayType);
171+
}
172+
173+
return type_info as UserDefinedTypeInformation;
174+
}
175+
176+
private static NdrComplexTypeReference GetComplexType(NdrBaseTypeReference type_reference)
177+
{
178+
if(type_reference is NdrPointerTypeReference pointer_type)
179+
{
180+
return GetComplexType(pointer_type.Type);
181+
}
182+
183+
if (type_reference is NdrBaseArrayTypeReference array_type)
184+
{
185+
return GetComplexType(array_type.ElementType);
186+
}
187+
188+
return type_reference as NdrComplexTypeReference;
189+
}
190+
191+
private static void UpdateComplexTypes(Dictionary<NdrComplexTypeReference, UserDefinedTypeInformation> complex_types,
192+
TypeInformation type_info, NdrBaseTypeReference type_reference)
193+
{
194+
var udt = GetUDTType(type_info);
195+
var complex = GetComplexType(type_reference);
196+
if (udt != null && complex != null && !complex_types.ContainsKey(complex))
197+
{
198+
complex_types[complex] = udt;
199+
}
200+
}
201+
202+
private static void FixupStructureType(HashSet<NdrComplexTypeReference> fixup_set, NdrBaseStructureTypeReference complex_type, UserDefinedTypeInformation udt)
203+
{
204+
var members = complex_type.Members.ToList();
205+
if (members.Count != udt.Members.Count)
206+
return;
207+
for (int i = 0; i < members.Count; ++i)
208+
{
209+
members[i].Name = udt.Members[i].Name;
210+
var member_udt = GetUDTType(udt.Members[i].Type);
211+
var member_complex = GetComplexType(members[i].MemberType);
212+
if (member_udt != null && member_complex != null)
213+
{
214+
FixupComplexType(fixup_set, member_complex, member_udt);
215+
}
216+
}
217+
}
218+
219+
private static void FixupUnionType(HashSet<NdrComplexTypeReference> fixup_set, NdrUnionTypeReference union_type, UserDefinedTypeInformation udt)
220+
{
221+
var members = union_type.Arms.Arms.ToList();
222+
if (members.Count != udt.Members.Count)
223+
return;
224+
for (int i = 0; i < members.Count; ++i)
225+
{
226+
members[i].Name = udt.Members[i].Name;
227+
var member_udt = GetUDTType(udt.Members[i].Type);
228+
var member_complex = GetComplexType(members[i].ArmType);
229+
if (member_udt != null && member_complex != null)
230+
{
231+
FixupComplexType(fixup_set, member_complex, member_udt);
232+
}
233+
}
234+
}
235+
236+
private static void FixupComplexType(HashSet<NdrComplexTypeReference> fixup_set, NdrComplexTypeReference complex_type, UserDefinedTypeInformation udt)
237+
{
238+
if (!fixup_set.Add(complex_type))
239+
return;
240+
241+
// Fixup the name to remove compiler generated characters.
242+
complex_type.Name = CodeGenUtils.MakeIdentifier(udt.Name);
243+
if (udt.Union)
244+
{
245+
if (complex_type is NdrUnionTypeReference union)
246+
{
247+
FixupUnionType(fixup_set, union, udt);
248+
}
249+
}
250+
else
251+
{
252+
if (complex_type is NdrBaseStructureTypeReference str)
253+
{
254+
FixupStructureType(fixup_set, str, udt);
255+
}
256+
}
257+
}
258+
259+
private static void FixupStructureNames(List<NdrProcedureDefinition> procs,
260+
ISymbolResolver symbol_resolver, NdrParserFlags parser_flags)
261+
{
262+
if (!parser_flags.HasFlagSet(NdrParserFlags.ResolveStructureNames) || !(symbol_resolver is ISymbolTypeResolver type_resolver))
263+
return;
264+
265+
var complex_types = new Dictionary<NdrComplexTypeReference, UserDefinedTypeInformation>();
266+
267+
foreach (var proc in procs)
268+
{
269+
if (!(type_resolver.GetTypeForSymbolByAddress(proc.DispatchFunction) is FunctionTypeInformation func_type))
270+
continue;
271+
272+
if (func_type.Parameters.Count != proc.Params.Count)
273+
continue;
274+
275+
for (int i = 0; i < func_type.Parameters.Count; ++i)
276+
{
277+
proc.Params[i].Name = func_type.Parameters[i].Name;
278+
UpdateComplexTypes(complex_types, func_type.Parameters[i].ParameterType, proc.Params[i].Type);
279+
}
280+
281+
UpdateComplexTypes(complex_types, func_type.ReturnType, proc.ReturnValue.Type);
282+
}
283+
284+
HashSet<NdrComplexTypeReference> fixup_set = new HashSet<NdrComplexTypeReference>();
285+
foreach (var pair in complex_types)
286+
{
287+
FixupComplexType(fixup_set, pair.Key, pair.Value);
288+
}
289+
}
290+
156291
private static IEnumerable<NdrProcedureDefinition> ReadProcs(IMemoryReader reader, MIDL_SERVER_INFO server_info, int start_offset,
157292
int dispatch_count, NdrTypeCache type_cache, ISymbolResolver symbol_resolver, IList<string> names, NdrParserFlags parser_flags)
158293
{
@@ -200,6 +335,9 @@ private static IEnumerable<NdrProcedureDefinition> ReadProcs(IMemoryReader reade
200335
}
201336
}
202337
}
338+
339+
FixupStructureNames(procs, symbol_resolver, parser_flags);
340+
203341
return procs.AsReadOnly();
204342
}
205343

NtApiDotNet/Ndr/NdrUnionTypes.cs

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,23 @@ public sealed class NdrUnionArm
3030
{
3131
public NdrBaseTypeReference ArmType { get; }
3232
public int CaseValue { get; }
33+
public string Name { get; set; }
34+
35+
private static string FormatCaseLabel(int case_value)
36+
{
37+
if (case_value < 0)
38+
{
39+
return $"minus_{case_value}";
40+
}
41+
return case_value.ToString();
42+
}
43+
3344

3445
internal NdrUnionArm(NdrParseContext context, BinaryReader reader)
3546
{
3647
CaseValue = reader.ReadInt32();
3748
ArmType = ReadArmType(context, reader);
49+
Name = $"Arm_{FormatCaseLabel(CaseValue)}";
3850
}
3951

4052
internal static NdrBaseTypeReference ReadArmType(NdrParseContext context, BinaryReader reader)
@@ -142,11 +154,10 @@ internal override string FormatComplexType(INdrFormatterInternal context)
142154
builder.Append(context.FormatComment(Correlation.ToString())).AppendLine();
143155
}
144156

145-
int index = 0;
146157
foreach (NdrUnionArm arm in Arms.Arms)
147158
{
148159
builder.Append(' ', indent).AppendFormat("/* case: {0} */", arm.CaseValue).AppendLine();
149-
builder.Append(' ', indent).AppendFormat("{0} Member_{1};", arm.ArmType.FormatType(context), index++).AppendLine();
160+
builder.Append(' ', indent).AppendFormat("{0} {1};", arm.ArmType.FormatType(context), arm.Name).AppendLine();
150161
}
151162

152163
if (Arms.DefaultArm != null)

NtApiDotNet/NtApiDotNet.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -321,6 +321,7 @@
321321
<Compile Include="Win32\ExecutableManifest.cs" />
322322
<Compile Include="Win32\Debugger\ISymbolTypeResolver.cs" />
323323
<Compile Include="Win32\ISymbolResolver.cs" />
324+
<Compile Include="Win32\RpcServerParserFlags.cs" />
324325
<Compile Include="Win32\SafeHandles\SafeLsaLogonHandle.cs" />
325326
<Compile Include="Utilities\ASN1\DERParser.cs" />
326327
<Compile Include="Win32\Security\Authentication\ASN1AuthenticationToken.cs" />

NtApiDotNet/Win32/Debugger/UserDefinedTypeInformation.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,15 +76,15 @@ public class UserDefinedTypeInformation : TypeInformation
7676
/// <summary>
7777
/// The members of the UDT.
7878
/// </summary>
79-
public ICollection<UserDefinedTypeMember> Members { get; }
79+
public IReadOnlyList<UserDefinedTypeMember> Members { get; }
8080

8181
/// <summary>
8282
/// Indicates the UDT is a union.
8383
/// </summary>
8484
public bool Union { get; }
8585

8686
internal UserDefinedTypeInformation(long size, int type_index, SymbolLoadedModule module,
87-
string name, bool union, ICollection<UserDefinedTypeMember> members)
87+
string name, bool union, IReadOnlyList<UserDefinedTypeMember> members)
8888
: base(SymTagEnum.SymTagUDT, size, type_index, module, name)
8989
{
9090
Members = members;

NtApiDotNet/Win32/Rpc/CodeGenUtils.cs

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -86,15 +86,6 @@ private static void AddAssignmentStatements(this CodeMemberMethod method, CodeEx
8686
}
8787
}
8888

89-
private static string FormatCaseLabel(NdrUnionArm arm)
90-
{
91-
if (arm.CaseValue < 0)
92-
{
93-
return $"minus_{-arm.CaseValue}";
94-
}
95-
return arm.CaseValue.ToString();
96-
}
97-
9889
private static CodeExpression GetArmCase(this NdrUnionArm arm, NdrSimpleTypeReference ndr_type)
9990
{
10091
long ret = arm.CaseValue;
@@ -1007,7 +998,7 @@ public static List<ComplexTypeMember> GetMembers(this NdrComplexTypeReference co
1007998
base_offset = union_type.SwitchIncrement;
1008999
}
10091000

1010-
members.AddRange(union_type.Arms.Arms.Select(a => new ComplexTypeMember(a.ArmType, base_offset, $"Arm_{FormatCaseLabel(a)}", a.GetArmCase(selector_type), false, false)));
1001+
members.AddRange(union_type.Arms.Arms.Select(a => new ComplexTypeMember(a.ArmType, base_offset, a.Name, a.GetArmCase(selector_type), false, false)));
10111002
if (union_type.Arms.DefaultArm != null)
10121003
{
10131004
members.Add(new ComplexTypeMember(union_type.Arms.DefaultArm, base_offset, "Arm_Default", null, true, false));

NtApiDotNet/Win32/RpcServer.cs

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,26 @@ public static IEnumerable<RpcServer> ParsePeFile(string file, string dbghelp_pat
254254
/// <remarks>This only works for PE files with the same bitness as the current process.</remarks>
255255
/// <returns>A list of parsed RPC server.</returns>
256256
public static IEnumerable<RpcServer> ParsePeFile(string file, string dbghelp_path, string symbol_path, bool parse_clients, bool ignore_symbols)
257+
{
258+
RpcServerParserFlags flags = RpcServerParserFlags.None;
259+
if (parse_clients)
260+
flags |= RpcServerParserFlags.ParseClients;
261+
if (ignore_symbols)
262+
flags |= RpcServerParserFlags.IgnoreSymbols;
263+
264+
return ParsePeFile(file, dbghelp_path, symbol_path, flags);
265+
}
266+
267+
/// <summary>
268+
/// Parse all RPC servers from a PE file.
269+
/// </summary>
270+
/// <param name="file">The PE file to parse.</param>
271+
/// <param name="dbghelp_path">Path to a DBGHELP DLL to resolve symbols.</param>
272+
/// <param name="symbol_path">Symbol path for DBGHELP</param>
273+
/// <param name="flags">Flags for the RPC parser.</param>
274+
/// <remarks>This only works for PE files with the same bitness as the current process.</remarks>
275+
/// <returns>A list of parsed RPC server.</returns>
276+
public static IEnumerable<RpcServer> ParsePeFile(string file, string dbghelp_path, string symbol_path, RpcServerParserFlags flags)
257277
{
258278
List<RpcServer> servers = new List<RpcServer>();
259279
using (var result = SafeLoadLibraryHandle.LoadLibrary(file, LoadLibraryFlags.DontResolveDllReferences, false))
@@ -265,17 +285,21 @@ public static IEnumerable<RpcServer> ParsePeFile(string file, string dbghelp_pat
265285

266286
var lib = result.Result;
267287
var sections = lib.GetImageSections();
268-
var offsets = sections.SelectMany(s => FindRpcServerInterfaces(s, parse_clients));
288+
var offsets = sections.SelectMany(s => FindRpcServerInterfaces(s, flags.HasFlagSet(RpcServerParserFlags.ParseClients)));
269289
if (offsets.Any())
270290
{
271-
using (var sym_resolver = !ignore_symbols ? SymbolResolver.Create(NtProcess.Current,
291+
using (var sym_resolver = !flags.HasFlagSet(RpcServerParserFlags.IgnoreSymbols) ? SymbolResolver.Create(NtProcess.Current,
272292
dbghelp_path, symbol_path) : null)
273293
{
294+
NdrParserFlags parser_flags = NdrParserFlags.IgnoreUserMarshal;
295+
if (flags.HasFlagSet(RpcServerParserFlags.ResolveStructureNames))
296+
parser_flags |= NdrParserFlags.ResolveStructureNames;
297+
274298
foreach (var offset in offsets)
275299
{
276300
IMemoryReader reader = new CurrentProcessMemoryReader(sections.Select(s => Tuple.Create(s.Data.DangerousGetHandle().ToInt64(), (int)s.Data.ByteLength)));
277301
NdrParser parser = new NdrParser(reader, NtProcess.Current,
278-
sym_resolver, NdrParserFlags.IgnoreUserMarshal);
302+
sym_resolver, parser_flags);
279303
IntPtr ifspec = lib.DangerousGetHandle() + (int)offset.Offset;
280304
var rpc = parser.ReadFromRpcServerInterface(ifspec);
281305
servers.Add(new RpcServer(rpc, parser.ComplexTypes, file, offset.Offset, offset.Client));
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
// Copyright 2016, 2017, 2018 Google Inc. All Rights Reserved.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
using System;
16+
17+
namespace NtApiDotNet.Win32
18+
{
19+
/// <summary>
20+
/// Flags for the RPC server parser.
21+
/// </summary>
22+
[Flags]
23+
public enum RpcServerParserFlags
24+
{
25+
/// <summary>
26+
/// None.
27+
/// </summary>
28+
None = 0,
29+
/// <summary>
30+
/// Parse client entries.
31+
/// </summary>
32+
ParseClients = 1,
33+
/// <summary>
34+
/// Ignore symbols when parsing.
35+
/// </summary>
36+
IgnoreSymbols = 2,
37+
/// <summary>
38+
/// Try and resolve structure names. Needs private symbols.
39+
/// </summary>
40+
ResolveStructureNames = 4,
41+
}
42+
}

NtObjectManager/NtObjectManager.psm1

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4371,6 +4371,8 @@ function Get-RpcServer {
43714371
[switch]$ParseClients,
43724372
[parameter(ParameterSetName = "FromDll")]
43734373
[switch]$IgnoreSymbols,
4374+
[parameter(ParameterSetName = "FromDll")]
4375+
[switch]$ResolveStructureNames,
43744376
[parameter(Mandatory = $true, ParameterSetName = "FromSerialized")]
43754377
[string]$SerializedPath
43764378
)
@@ -4385,14 +4387,25 @@ function Get-RpcServer {
43854387
$SymbolPath = $Script:GlobalSymbolPath
43864388
}
43874389
}
4390+
4391+
$ParserFlags = [NtApiDotNet.Win32.RpcServerParserFlags]::None
4392+
if ($ParseClients) {
4393+
$ParserFlags = $ParserFlags -bor [NtApiDotNet.Win32.RpcServerParserFlags]::ParseClients
4394+
}
4395+
if ($IgnoreSymbols) {
4396+
$ParserFlags = $ParserFlags -bor [NtApiDotNet.Win32.RpcServerParserFlags]::IgnoreSymbols
4397+
}
4398+
if ($ResolveStructureNames) {
4399+
$ParserFlags = $ParserFlags -bor [NtApiDotNet.Win32.RpcServerParserFlags]::ResolveStructureNames
4400+
}
43884401
}
43894402

43904403
PROCESS {
43914404
try {
43924405
if ($PSCmdlet.ParameterSetName -eq "FromDll") {
43934406
$FullName = Resolve-Path -LiteralPath $FullName -ErrorAction Stop
43944407
Write-Progress -Activity "Parsing RPC Servers" -CurrentOperation "$FullName"
4395-
$servers = [NtApiDotNet.Win32.RpcServer]::ParsePeFile($FullName, $DbgHelpPath, $SymbolPath, $ParseClients, $IgnoreSymbols)
4408+
$servers = [NtApiDotNet.Win32.RpcServer]::ParsePeFile($FullName, $DbgHelpPath, $SymbolPath, $ParserFlags)
43964409
if ($AsText) {
43974410
foreach ($server in $servers) {
43984411
$text = $server.FormatAsText($RemoveComments)

0 commit comments

Comments
 (0)