Initial commit

This commit is contained in:
lilhoser 2022-10-07 17:40:31 -04:00 committed by Aaron LeMasters
commit 34fe2749a6
88 changed files with 31988 additions and 0 deletions

16
.github/scripts/environment.ps1 vendored Normal file
View File

@ -0,0 +1,16 @@
if (-NOT ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator"))
{
$arguments = "& '" +$myinvocation.mycommand.definition + "'"
Start-Process powershell -Verb runAs -ArgumentList $arguments
Break
}
$url = "https://aaron.nyc3.digitaloceanspaces.com/X64%20Debuggers%20And%20Tools-x64_en-us.msi"
$file = "$env:temp\debuggingtools.msi"
Invoke-WebRequest $url -OutFile $file
$log = "install.log"
$procMain = Start-Process "msiexec" "/i `"$file`" /qn /l*! `"$log`"" -NoNewWindow -PassThru
$procLog = Start-Process "powershell" "Get-Content -Path `"$log`" -Wait" -NoNewWindow -PassThru
$procMain.WaitForExit()
$procLog.Kill()

43
.github/workflows/ci.yml vendored Normal file
View File

@ -0,0 +1,43 @@
name: CI
env:
DOTNET_VERSION: '4.8.1'
on: [push, pull_request, workflow_dispatch]
jobs:
RPCInvestigator_Unit_Tests:
runs-on: windows-latest
steps:
- name: checkout code
uses: actions/checkout@v2
# Install MS debugging tools using hard-coded MSI (to-do: replace this)
#- name: Install MS Debugging tools
# run: |
# powershell "Set-ExecutionPolicy -ExecutionPolicy Unrestricted"
# powershell .github\scripts\environment.ps1
# Build project files (nuget restore is automatic here.)
- name: Setup MSBuild
uses: microsoft/setup-msbuild@v1
- name: Save VS install path
run: |
$vswhere = $env:Programfiles + " (x86)\Microsoft Visual Studio\Installer\vswhere"
$vs_install_path = & $vswhere -property installationPath
$vstest_location = $vs_install_path + "\Common7\IDE\CommonExtensions\Microsoft\TestWindow\vstest.console.exe"
echo "VSTEST_LOCATION=$vstest_location" | Out-File -FilePath $Env:GITHUB_ENV -Encoding utf8 -Append
- name: Setup NuGet
uses: nuget/setup-nuget@v1
- uses: actions/cache@v3
with:
path: ~/.nuget/packages
key: ${{ runner.os }}-nuget-${{ hashFiles('**/packages.lock.json') }}
restore-keys: |
${{ runner.os }}-nuget-
- name: Restore nuget packages
run: nuget restore RpcInvestigator.sln
- name: Build Solution
run: |
msbuild.exe RpcInvestigator.sln /nologo /nr:false /p:platform="Any CPU" /p:configuration="Release"
- name: Test
# Note: "dotnet test" only works for .NET core, not .NET framework. Apparently MSTest is deprecated,
# and we're supposed to use VsTest.console.exe, whatever that is. But it's buried in the VS install
# folder which can be discovered via %ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere
run: |
& $env:VSTEST_LOCATION UnitTests\bin\Release\UnitTests.dll

398
.gitignore vendored Normal file
View File

@ -0,0 +1,398 @@
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
##
## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore
# User-specific files
*.rsuser
*.suo
*.user
*.userosscache
*.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Mono auto generated files
mono_crash.*
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
[Ww][Ii][Nn]32/
[Aa][Rr][Mm]/
[Aa][Rr][Mm]64/
bld/
[Bb]in/
[Oo]bj/
[Ll]og/
[Ll]ogs/
# Visual Studio 2015/2017 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
# Visual Studio 2017 auto generated files
Generated\ Files/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUnit
*.VisualState.xml
TestResult.xml
nunit-*.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
# Benchmark Results
BenchmarkDotNet.Artifacts/
# .NET Core
project.lock.json
project.fragment.lock.json
artifacts/
# ASP.NET Scaffolding
ScaffoldingReadMe.txt
# StyleCop
StyleCopReport.xml
# Files built by Visual Studio
*_i.c
*_p.c
*_h.h
*.ilk
*.meta
*.obj
*.iobj
*.pch
*.pdb
*.ipdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*_wpftmp.csproj
*.log
*.tlog
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
*.VC.VC.opendb
# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap
# Visual Studio Trace Files
*.e2e
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# AxoCover is a Code Coverage Tool
.axoCover/*
!.axoCover/settings.json
# Coverlet is a free, cross platform Code Coverage Tool
coverage*.json
coverage*.xml
coverage*.info
# Visual Studio code coverage results
*.coverage
*.coveragexml
# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# Note: Comment the next line if you want to checkin your web deploy settings,
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj
# Microsoft Azure Web App publish settings. Comment the next line if you want to
# checkin your Azure Web App publish settings, but sensitive information contained
# in these scripts will be unencrypted
PublishScripts/
# NuGet Packages
*.nupkg
# NuGet Symbol Packages
*.snupkg
# The packages folder can be ignored because of Package Restore
**/[Pp]ackages/*
# except build/, which is used as an MSBuild target.
!**/[Pp]ackages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/[Pp]ackages/repositories.config
# NuGet v3's project.json files produces more ignorable files
*.nuget.props
*.nuget.targets
# Microsoft Azure Build Output
csx/
*.build.csdef
# Microsoft Azure Emulator
ecf/
rcf/
# Windows Store app package directories and files
AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt
*.appx
*.appxbundle
*.appxupload
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!?*.[Cc]ache/
# Others
ClientBin/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.jfm
*.pfx
*.publishsettings
orleans.codegen.cs
# Including strong name files can present a security risk
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
#*.snk
# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
ServiceFabricBackup/
*.rptproj.bak
# SQL Server files
*.mdf
*.ldf
*.ndf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
*.rptproj.rsuser
*- [Bb]ackup.rdl
*- [Bb]ackup ([0-9]).rdl
*- [Bb]ackup ([0-9][0-9]).rdl
# Microsoft Fakes
FakesAssemblies/
# GhostDoc plugin setting file
*.GhostDoc.xml
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
node_modules/
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
*.vbw
# Visual Studio 6 auto-generated project file (contains which files were open etc.)
*.vbp
# Visual Studio 6 workspace and project file (working project files containing files to include in project)
*.dsw
*.dsp
# Visual Studio 6 technical files
*.ncb
*.aps
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions
# Paket dependency manager
.paket/paket.exe
paket-files/
# FAKE - F# Make
.fake/
# CodeRush personal settings
.cr/personal
# Python Tools for Visual Studio (PTVS)
__pycache__/
*.pyc
# Cake - Uncomment if you are using it
# tools/**
# !tools/packages.config
# Tabs Studio
*.tss
# Telerik's JustMock configuration file
*.jmconfig
# BizTalk build output
*.btp.cs
*.btm.cs
*.odx.cs
*.xsd.cs
# OpenCover UI analysis results
OpenCover/
# Azure Stream Analytics local run output
ASALocalRun/
# MSBuild Binary and Structured Log
*.binlog
# NVidia Nsight GPU debugger configuration file
*.nvuser
# MFractors (Xamarin productivity tool) working folder
.mfractor/
# Local History for Visual Studio
.localhistory/
# Visual Studio History (VSHistory) files
.vshistory/
# BeatPulse healthcheck temp database
healthchecksdb
# Backup folder for Package Reference Convert tool in Visual Studio 2017
MigrationBackup/
# Ionide (cross platform F# VS Code tools) working folder
.ionide/
# Fody - auto-generated XML schema
FodyWeavers.xsd
# VS Code files for those working on multiple tools
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
*.code-workspace
# Local History for Visual Studio Code
.history/
# Windows Installer files from build outputs
*.cab
*.msi
*.msix
*.msm
*.msp
# JetBrains Rider
*.sln.iml

14
App.config Normal file
View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.8.1" />
</startup>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Runtime.CompilerServices.Unsafe" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-5.0.0.0" newVersion="5.0.0.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>

202
LICENSE Normal file
View File

@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

26
Program.cs Normal file
View File

@ -0,0 +1,26 @@
//
// Copyright (c) 2022-present, Trail of Bits, Inc.
// All rights reserved.
//
// This source code is licensed in accordance with the terms specified in
// the LICENSE file found in the root directory of this source tree.
//
using System;
using System.Windows.Forms;
namespace RpcInvestigator
{
internal static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new MainWindow());
}
}
}

View File

@ -0,0 +1,43 @@
//
// Copyright (c) 2022-present, Trail of Bits, Inc.
// All rights reserved.
//
// This source code is licensed in accordance with the terms specified in
// the LICENSE file found in the root directory of this source tree.
//
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("RpcInvestigator")]
[assembly: AssemblyDescription("Windows RPC investigation tool")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("Trail of Bits")]
[assembly: AssemblyProduct("RpcInvestigator")]
[assembly: AssemblyCopyright("Copyright © 2022")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("de141fc8-f9a3-41d2-a0a9-fd6a6ab310a3")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

133
Properties/Resources.Designer.cs generated Normal file
View File

@ -0,0 +1,133 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace RpcInvestigator.Properties {
using System;
/// <summary>
/// A strongly-typed resource class, for looking up localized strings, etc.
/// </summary>
// This class was auto-generated by the StronglyTypedResourceBuilder
// class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class Resources {
private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal Resources() {
}
/// <summary>
/// Returns the cached ResourceManager instance used by this class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Resources.ResourceManager ResourceManager {
get {
if (object.ReferenceEquals(resourceMan, null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("RpcInvestigator.Properties.Resources", typeof(Resources).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
/// <summary>
/// Overrides the current thread's CurrentUICulture property for all
/// resource lookups using this strongly typed resource class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
set {
resourceCulture = value;
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Bitmap.
/// </summary>
internal static System.Drawing.Bitmap columns {
get {
object obj = ResourceManager.GetObject("columns", resourceCulture);
return ((System.Drawing.Bitmap)(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Bitmap.
/// </summary>
internal static System.Drawing.Bitmap gears {
get {
object obj = ResourceManager.GetObject("gears", resourceCulture);
return ((System.Drawing.Bitmap)(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Bitmap.
/// </summary>
internal static System.Drawing.Bitmap group {
get {
object obj = ResourceManager.GetObject("group", resourceCulture);
return ((System.Drawing.Bitmap)(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Bitmap.
/// </summary>
internal static System.Drawing.Bitmap nodes {
get {
object obj = ResourceManager.GetObject("nodes", resourceCulture);
return ((System.Drawing.Bitmap)(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Bitmap.
/// </summary>
internal static System.Drawing.Bitmap play {
get {
object obj = ResourceManager.GetObject("play", resourceCulture);
return ((System.Drawing.Bitmap)(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Bitmap.
/// </summary>
internal static System.Drawing.Bitmap save {
get {
object obj = ResourceManager.GetObject("save", resourceCulture);
return ((System.Drawing.Bitmap)(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Drawing.Bitmap.
/// </summary>
internal static System.Drawing.Bitmap stop {
get {
object obj = ResourceManager.GetObject("stop", resourceCulture);
return ((System.Drawing.Bitmap)(obj));
}
}
}
}

142
Properties/Resources.resx Normal file
View File

@ -0,0 +1,142 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
<data name="columns" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\images\columns.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="gears" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\images\gears.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="group" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\images\group.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="nodes" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\images\nodes.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="play" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\images\play.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="save" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\images\save.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="stop" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\images\stop.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
</root>

119
README.md Normal file
View File

@ -0,0 +1,119 @@
# RPC Investigator
RPC Investigator (RPCI) is a .NET/C# Windows Forms UI application that provides an advanced discovery and analysis interface to Windows RPC endpoints. The tool provides a visual interface around the existing core RPC capabilities of the [NtApiDotNet](https://github.com/googleprojectzero/sandbox-attacksurface-analysis-tools/tree/main/NtApiDotNet) platform, including:
* Enumerating all active ALPC RPC servers
* Parsing RPC servers from any PE file
* Parsing RPC servers from processes and their loaded modules, including services
* Pulling symbol information from a Symbol Server
* Exporting RPC server definitions as serialized .NET objects for your own scripting
Beyond these core features, RPCI provides additional capabilities:
* The Client Workbench allows you to create and execute an RPC client binary on-the-fly by right-clicking on an RPC server of interest. The workbench has a C# code editor pane that allows you to edit the client in real time and observe results from RPC procedures executed in your code.
* Discovered RPC servers are organized into a searchable library, allowing you to pivot RPC server data in useful ways, such as searching all RPC procedures for all servers for interesting routines through a customizable search interface.
* The RPC Sniffer tool adds visibility into RPC-related ETW data to provide a near real-time view of active RPC calls. By combining ETW data with RPC server data from NtApiDotNet, we can build a more complete picture of ongoing RPC activity.
## Common Workflows
There are several workflows that the RPC Investigator supports:
- **Auditing**
- Enumerating all active ALPC RPC servers across all processes that are communicating with an ALPC endpoint
- Enumerating all RPC servers running in a Windows service
- Loading offline RPC servers defined in a PE file (such as an EXE or DLL)
- **Interactive**
- Client Workbench: Automatically generate RPC client code that can be customized and used to call into any RPC service.
- RPC Sniffer: Realtime monitor of RPC-related Event Tracing for Windows (ETW) data.
## Example Workflow: Analyzing the Task Scheduler RPC
In this example, we'll be inspecting the Windows Task Scheduler RPC service, which is used to manage and execute scheduled tasks. We'll find the service, generate client code, and then customize the client to interact with one of the exposed procedures.
First, load the Windows services list by clicking **File -> Load From Service**. This opens a new service list window:
![](docs/img/ServiceListWindow.png)
Find the **Schedule** service, which is the Windows Task Scheduler, select the service and click **Go**.
![](docs/img/ScheduleService.png)
You will be prompted prior to RPCI loading all associated RPC DLLs. Click **Yes** to continue. Once loaded, you will see a list of all RPC servers discovered across all modules loaded in the service process. The Windows Task Scheduler RPC server has an Interface ID of [`86D35949-83C9-4044-B424-DB363231FD0C`](https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-tsch/fbab083e-f79f-4216-af4c-d5104a913d40). Find the row within the list that has this Interface ID, which should have a running service named **Task Scheduler**, right-click on the row and select **New Client**.
![](docs/img/TaskSchedulerClient.png)
The left portion of the client window shows RPC server metadata and command line output from the client code. The right side shows two tabs:
- **Client Code** - Auto generated C# client code that can be customized to interact with one or more procedures.
- **Procedures** - List of exposed RPC procedures.
In this example we'll be calling the [`SchRpcHighestVersion`](https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-tsch/b266c231-52db-4244-88da-725cf2a9557a) procedure. This method accepts a single argument, `out int version`, which, after calling the procedure, will contain the highest Task Scheduler protocol version supported by the RPC interface. The high 16-bits are the major version and the low 16-bits are the minor version.
To call this procedure:
1. In the **Client Code** window, find the **`Run`** method, which is the main entry point for the RPC client.
2. Edit the **`Run`** method body to call the procedure:
```cs
public async Task<bool> Run()
{
int version;
int status = SchRpcHighestVersion(out version);
if (status == 0) {
long major = (version & 0xffff0000) >> 16;
long minor = version & 0x0000ffff;
Console.WriteLine("highest supported RPC version: {0}.{1}", major, minor);
} else {
Console.WriteLine("call to SchRpcHighestVersion failed with error: {0:X}", status);
}
return true;
}
```
3. After adding this code, run the client by clicking the **Run** button. This will compile the C# code and then execute the **`Run`** method.
- You will see a popup box with any compilation errors if the client code could not be compiled.
If compilation is successful, you will see something similar to the following in the **Output** box:
```
> Run() output:
highest supported RPC version: 1.6
```
![](docs/img/TaskSchedulerClient-Version.png)
### Configuration
The Rpc Investigator has several configuration settings.
| Setting | Description | Default |
|---------|-------------|---------|
| dbghelp.dll | File location of the `dbghelp.dll` module | Find latest version within installed Windows Kits. |
| Symbol Path | Path to Windows symbols, which can be a symbol server or local directory | Default public Windows Server: `srv*c:\symbols*https://msdl.microsoft.com/download/symbols` |
| Trace Level | The logging trace level | `info` |
The configuration settings can be modified within the application through the **Edit -> Settings** menu.
## Development Environment
1. Install [Visual Studio Community 2022](https://visualstudio.microsoft.com/vs/community/), make sure to select the **.NET Desktop Development** workflow.
2. Download and install the latest [Windows 10 SDK](https://developer.microsoft.com/en-us/windows/downloads/windows-sdk/). Perform a full installation so that .NET 4.8.1 and Debugging Tools are installed.
3. Open the Solution and verify that the projects loaded correctly. If there is an error about missing .NET 4.8.1 Targeting Pack, download and install [.NET Framework 4.8.1 Developer Pack](https://dotnet.microsoft.com/en-us/download/dotnet-framework/net481) and then restart Visual Studio.
## Further Reading
Because Windows RPC has been a popular research topic for well over a decade, there are too many related resources and research efforts to name here. We've listed a few below that we encountered while building this tool:
* https://clearbluejar.github.io/posts/surveying-windows-rpc-discovery-tools/
* https://www.powerofcommunity.net/poc2019/James.pdf
* https://www.tiraniddo.dev/2022/06/finding-running-rpc-server-information.html
* https://clearbluejar.github.io/posts/from-ntobjectmanager-to-petitpotam/
* https://itm4n.github.io/from-rpcview-to-petitpotam/
* https://learn.microsoft.com/en-us/windows/win32/rpc/rpc-security-essentials
* https://www.cyberark.com/resources/threat-research-blog/understanding-windows-containers-communication
* https://github.com/silverf0x/RpcView
* https://github.com/xpn/RpcEnum
* https://github.com/cyberark/RPCMon
* https://github.com/tyranid/WindowsRpcClients
If you're unfamiliar with RPC internals or need a technical refresher, we would recommend one of the authoritative sources on the topic - Alex Ionescu's 2014 SyScan talk in Singapore, [All about the RPC, LRPC, ALPC, and LPC in your PC](https://www.youtube.com/watch?v=UNpL5csYC1E).

295
RpcInvestigator.csproj Normal file
View File

@ -0,0 +1,295 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{DE141FC8-F9A3-41D2-A0A9-FD6A6AB310A3}</ProjectGuid>
<OutputType>WinExe</OutputType>
<RootNamespace>RpcInvestigator</RootNamespace>
<AssemblyName>RpcInvestigator</AssemblyName>
<TargetFrameworkVersion>v4.8.1</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<Deterministic>true</Deterministic>
<PublishUrl>publish\</PublishUrl>
<Install>true</Install>
<InstallFrom>Disk</InstallFrom>
<UpdateEnabled>false</UpdateEnabled>
<UpdateMode>Foreground</UpdateMode>
<UpdateInterval>7</UpdateInterval>
<UpdateIntervalUnits>Days</UpdateIntervalUnits>
<UpdatePeriodically>false</UpdatePeriodically>
<UpdateRequired>false</UpdateRequired>
<MapFileExtensions>true</MapFileExtensions>
<ApplicationRevision>0</ApplicationRevision>
<ApplicationVersion>1.0.0.%2a</ApplicationVersion>
<IsWebBootstrapper>false</IsWebBootstrapper>
<UseApplicationTrust>false</UseApplicationTrust>
<BootstrapperEnabled>true</BootstrapperEnabled>
<TargetFrameworkProfile />
<NuGetPackageImportStamp>
</NuGetPackageImportStamp>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>TRACE;DEBUG</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<Prefer32Bit>false</Prefer32Bit>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<PropertyGroup>
<ApplicationManifest>app.manifest</ApplicationManifest>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x64\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>x64</PlatformTarget>
<LangVersion>7.3</LangVersion>
<ErrorReport>prompt</ErrorReport>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
<OutputPath>bin\x64\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<Optimize>true</Optimize>
<DebugType>pdbonly</DebugType>
<PlatformTarget>x64</PlatformTarget>
<LangVersion>7.3</LangVersion>
<ErrorReport>prompt</ErrorReport>
<Prefer32Bit>true</Prefer32Bit>
</PropertyGroup>
<PropertyGroup>
<ApplicationIcon>tob.ico</ApplicationIcon>
</PropertyGroup>
<ItemGroup>
<Reference Include="FastColoredTextBox, Version=2.16.24.0, Culture=neutral, PublicKeyToken=fb8aa12b994ef61b, processorArchitecture=MSIL">
<HintPath>packages\FCTB.2.16.24\lib\FastColoredTextBox.dll</HintPath>
</Reference>
<Reference Include="GraphX.Standard.Common, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>packages\GraphX.3.0.0\lib\net461\GraphX.Standard.Common.dll</HintPath>
</Reference>
<Reference Include="GraphX.Standard.Logic, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>packages\GraphX.3.0.0\lib\net461\GraphX.Standard.Logic.dll</HintPath>
</Reference>
<Reference Include="GraphX.WPF.Controls, Version=3.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>packages\GraphX.3.0.0\lib\net461\GraphX.WPF.Controls.dll</HintPath>
</Reference>
<Reference Include="Newtonsoft.Json, Version=13.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>packages\Newtonsoft.Json.13.0.2\lib\net45\Newtonsoft.Json.dll</HintPath>
</Reference>
<Reference Include="NtApiDotNet, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>packages\NtApiDotNet.1.1.33\lib\net461\NtApiDotNet.dll</HintPath>
</Reference>
<Reference Include="NtApiDotNet.Forms, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>packages\NtApiDotNet.Forms.1.1.33\lib\net461\NtApiDotNet.Forms.dll</HintPath>
</Reference>
<Reference Include="ObjectListView, Version=2.9.1.25410, Culture=neutral, PublicKeyToken=b1c5bf581481bcd4, processorArchitecture=MSIL">
<HintPath>packages\ObjectListView.Official.2.9.2-alpha2\lib\net20\ObjectListView.dll</HintPath>
</Reference>
<Reference Include="PresentationCore" />
<Reference Include="PresentationFramework" />
<Reference Include="QuickGraph, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>packages\QuickGraphCore.1.0.0\lib\net40\QuickGraph.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Buffers, Version=4.0.2.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>packages\System.Buffers.4.4.0\lib\netstandard2.0\System.Buffers.dll</HintPath>
</Reference>
<Reference Include="System.Core" />
<Reference Include="System.DirectoryServices" />
<Reference Include="System.Management" />
<Reference Include="System.Management.Instrumentation" />
<Reference Include="System.Memory, Version=4.0.1.1, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>packages\System.Memory.4.5.3\lib\netstandard2.0\System.Memory.dll</HintPath>
</Reference>
<Reference Include="System.Numerics" />
<Reference Include="System.Numerics.Vectors, Version=4.1.3.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>packages\System.Numerics.Vectors.4.4.0\lib\net46\System.Numerics.Vectors.dll</HintPath>
</Reference>
<Reference Include="System.Resources.Extensions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>packages\System.Resources.Extensions.4.6.0\lib\netstandard2.0\System.Resources.Extensions.dll</HintPath>
</Reference>
<Reference Include="System.Runtime.CompilerServices.Unsafe, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>packages\System.Runtime.CompilerServices.Unsafe.5.0.0\lib\net45\System.Runtime.CompilerServices.Unsafe.dll</HintPath>
</Reference>
<Reference Include="System.Runtime.Serialization" />
<Reference Include="System.Security" />
<Reference Include="System.ServiceModel" />
<Reference Include="System.Xaml" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Deployment" />
<Reference Include="System.Drawing" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xml" />
<Reference Include="UIAutomationProvider" />
<Reference Include="UIAutomationTypes" />
<Reference Include="WindowsBase" />
<Reference Include="WindowsFormsIntegration" />
</ItemGroup>
<ItemGroup>
<Compile Include="TabPages\ContextMenu.cs">
<SubType>Component</SubType>
</Compile>
<Compile Include="TabPages\RpcLibraryProcedureList.cs">
<SubType>Component</SubType>
</Compile>
<Compile Include="TabPages\TabManager.cs" />
<Compile Include="TraceLogger.cs" />
<Compile Include="Util\EtwEventParser.cs" />
<Compile Include="Util\EtwFileTrace.cs" />
<Compile Include="Util\EtwNativeDefinitions.cs" />
<Compile Include="Util\EtwProviderParser.cs" />
<Compile Include="Util\Formatting.cs" />
<Compile Include="Util\EtwRealTimeTrace.cs" />
<Compile Include="Util\MarshalHelper.cs" />
<Compile Include="Util\ReflectionHelper.cs" />
<Compile Include="Util\SddlParser.cs" />
<Compile Include="Util\TextRenderer.cs">
<SubType>Component</SubType>
</Compile>
<Compile Include="Windows\Client.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="Windows\Client.Designer.cs">
<DependentUpon>Client.cs</DependentUpon>
</Compile>
<Compile Include="Windows\Controls\SnifferGraph.cs" />
<Compile Include="Windows\Controls\SnifferListview.cs">
<SubType>Component</SubType>
</Compile>
<Compile Include="Windows\EtwColumnPicker.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="Windows\EtwColumnPicker.Designer.cs">
<DependentUpon>EtwColumnPicker.cs</DependentUpon>
</Compile>
<Compile Include="Windows\MainWindow.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="Windows\MainWindow.Designer.cs">
<DependentUpon>MainWindow.cs</DependentUpon>
</Compile>
<Compile Include="TabPages\RpcLibraryServerList.cs">
<SubType>Component</SubType>
</Compile>
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="RpcLibrary.cs" />
<Compile Include="TabPages\RpcProcedureList.cs">
<SubType>Component</SubType>
</Compile>
<Compile Include="TabPages\RpcEndpointList.cs">
<SubType>Component</SubType>
</Compile>
<Compile Include="TabPages\RpcAlpcServerList.cs">
<SubType>Component</SubType>
</Compile>
<Compile Include="TabPages\RpcServerList.cs">
<SubType>Component</SubType>
</Compile>
<Compile Include="Windows\Services.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="Windows\Services.Designer.cs">
<DependentUpon>Services.cs</DependentUpon>
</Compile>
<Compile Include="Settings.cs" />
<Compile Include="Windows\SettingsWindow.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="Windows\SettingsWindow.Designer.cs">
<DependentUpon>SettingsWindow.cs</DependentUpon>
</Compile>
<Compile Include="Util\TaskWorker.cs" />
<Compile Include="Windows\Sniffer.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="Windows\Sniffer.Designer.cs">
<DependentUpon>Sniffer.cs</DependentUpon>
</Compile>
<EmbeddedResource Include="Windows\Client.resx">
<DependentUpon>Client.cs</DependentUpon>
<SubType>Designer</SubType>
</EmbeddedResource>
<EmbeddedResource Include="Windows\EtwColumnPicker.resx">
<DependentUpon>EtwColumnPicker.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Windows\MainWindow.resx">
<DependentUpon>MainWindow.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Properties\Resources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
<SubType>Designer</SubType>
</EmbeddedResource>
<Compile Include="Properties\Resources.Designer.cs">
<AutoGen>True</AutoGen>
<DependentUpon>Resources.resx</DependentUpon>
<DesignTime>True</DesignTime>
</Compile>
<EmbeddedResource Include="Windows\Services.resx">
<DependentUpon>Services.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Windows\SettingsWindow.resx">
<DependentUpon>SettingsWindow.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Windows\Sniffer.resx">
<DependentUpon>Sniffer.cs</DependentUpon>
</EmbeddedResource>
<None Include="app.manifest" />
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<None Include="App.config" />
</ItemGroup>
<ItemGroup>
<BootstrapperPackage Include=".NETFramework,Version=v4.7.2">
<Visible>False</Visible>
<ProductName>Microsoft .NET Framework 4.7.2 %28x86 and x64%29</ProductName>
<Install>true</Install>
</BootstrapperPackage>
<BootstrapperPackage Include="Microsoft.Net.Framework.3.5.SP1">
<Visible>False</Visible>
<ProductName>.NET Framework 3.5 SP1</ProductName>
<Install>false</Install>
</BootstrapperPackage>
</ItemGroup>
<ItemGroup>
<None Include="images\play.png" />
<None Include="images\stop.png" />
<None Include="images\gears.png" />
<None Include="images\columns.png" />
<None Include="images\save.png" />
<None Include="images\nodes.png" />
<None Include="images\group.png" />
<Content Include="tob.ico" />
</ItemGroup>
<ItemGroup>
<Page Include="Windows\Controls\GraphTemplate.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

41
RpcInvestigator.sln Normal file
View File

@ -0,0 +1,41 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.3.32825.248
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RpcInvestigator", "RpcInvestigator.csproj", "{DE141FC8-F9A3-41D2-A0A9-FD6A6AB310A3}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnitTests", "UnitTests\UnitTests.csproj", "{43CD7AEF-CD26-4E7C-9428-1F8BFE40B10D}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Debug|x64 = Debug|x64
Release|Any CPU = Release|Any CPU
Release|x64 = Release|x64
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{DE141FC8-F9A3-41D2-A0A9-FD6A6AB310A3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{DE141FC8-F9A3-41D2-A0A9-FD6A6AB310A3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{DE141FC8-F9A3-41D2-A0A9-FD6A6AB310A3}.Debug|x64.ActiveCfg = Debug|x64
{DE141FC8-F9A3-41D2-A0A9-FD6A6AB310A3}.Debug|x64.Build.0 = Debug|x64
{DE141FC8-F9A3-41D2-A0A9-FD6A6AB310A3}.Release|Any CPU.ActiveCfg = Release|Any CPU
{DE141FC8-F9A3-41D2-A0A9-FD6A6AB310A3}.Release|Any CPU.Build.0 = Release|Any CPU
{DE141FC8-F9A3-41D2-A0A9-FD6A6AB310A3}.Release|x64.ActiveCfg = Release|x64
{DE141FC8-F9A3-41D2-A0A9-FD6A6AB310A3}.Release|x64.Build.0 = Release|x64
{43CD7AEF-CD26-4E7C-9428-1F8BFE40B10D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{43CD7AEF-CD26-4E7C-9428-1F8BFE40B10D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{43CD7AEF-CD26-4E7C-9428-1F8BFE40B10D}.Debug|x64.ActiveCfg = Debug|Any CPU
{43CD7AEF-CD26-4E7C-9428-1F8BFE40B10D}.Debug|x64.Build.0 = Debug|Any CPU
{43CD7AEF-CD26-4E7C-9428-1F8BFE40B10D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{43CD7AEF-CD26-4E7C-9428-1F8BFE40B10D}.Release|Any CPU.Build.0 = Release|Any CPU
{43CD7AEF-CD26-4E7C-9428-1F8BFE40B10D}.Release|x64.ActiveCfg = Release|Any CPU
{43CD7AEF-CD26-4E7C-9428-1F8BFE40B10D}.Release|x64.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {643C9C52-2CBB-4088-A145-BD5AC0905502}
EndGlobalSection
EndGlobal

743
RpcLibrary.cs Normal file
View File

@ -0,0 +1,743 @@
//
// Copyright (c) 2022-present, Trail of Bits, Inc.
// All rights reserved.
//
// This source code is licensed in accordance with the terms specified in
// the LICENSE file found in the root directory of this source tree.
//
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.Serialization.Formatters.Binary;
using System.Text;
using System.Threading.Tasks;
using NtApiDotNet.Win32;
using NtApiDotNet;
using System.Net;
using GraphX.Common;
using System.Windows;
namespace RpcInvestigator
{
using static TraceLogger;
public enum RpcLibraryFilterType
{
NoFilter,
FilterByKeyword
}
public class RpcLibraryFilter
{
public RpcLibraryFilterType FilterType;
public string Keyword;
public RpcLibraryFilter()
{
FilterType = RpcLibraryFilterType.NoFilter;
}
}
[Serializable]
public class RpcServerEntry
{
public RpcServerEntry()
{
}
public Version InterfaceVersion;
public int PointerSize;
public byte[] SerializedServer;
}
[Serializable]
public class RpcServerData
{
//
// Note: this class is NOT thread-safe.
//
public int m_Version;
private Dictionary<Guid, List<RpcServerEntry>> m_Entries;
public RpcServerData()
{
m_Version = 0;
m_Entries = new Dictionary<Guid, List<RpcServerEntry>>();
}
public bool Add(RpcServer Server)
{
if (Exists(Server.InterfaceId, Server.InterfaceVersion))
{
return false;
}
if (!m_Entries.ContainsKey(Server.InterfaceId))
{
m_Entries.Add(Server.InterfaceId, new List<RpcServerEntry>());
}
m_Entries[Server.InterfaceId].Add(new RpcServerEntry()
{
InterfaceVersion = Server.InterfaceVersion,
SerializedServer = Server.Serialize(),
PointerSize = IntPtr.Size
});
return true;
}
public bool Remove(Guid InterfaceId)
{
if (!Exists(InterfaceId))
{
Trace(TraceLoggerType.RpcLibrary,
TraceEventType.Warning,
"Server " + InterfaceId.ToString() +
" does not exist in the data set.");
return false;
}
var num = m_Entries[InterfaceId].Count(
e => e.PointerSize == IntPtr.Size);
var removed = m_Entries[InterfaceId].RemoveAll(
e => e.PointerSize == IntPtr.Size);
if (num != removed)
{
Trace(TraceLoggerType.RpcLibrary,
TraceEventType.Error,
"Library: Only removed " + removed + " out of " + num +
" for interface " + InterfaceId.ToString());
return false;
}
Trace(TraceLoggerType.RpcLibrary,
TraceEventType.Verbose,
"Library: Successfully removed " + removed + " out of " + num +
" for interface " + InterfaceId.ToString());
return true;
}
public bool Remove(Guid InterfaceId, Version InterfaceVersion)
{
if (!Exists(InterfaceId, InterfaceVersion))
{
Trace(TraceLoggerType.RpcLibrary,
TraceEventType.Warning,
"Server " + InterfaceId.ToString() +
" does not exist in the data set.");
return false;
}
var num = m_Entries[InterfaceId].Count(
e => e.PointerSize == IntPtr.Size &&
e.InterfaceVersion.Equals(InterfaceVersion));
var removed = m_Entries[InterfaceId].RemoveAll(
e => e.PointerSize == IntPtr.Size &&
e.InterfaceVersion.Equals(InterfaceVersion));
if (num != removed)
{
Trace(TraceLoggerType.RpcLibrary,
TraceEventType.Error,
"Library: Only removed " + removed + " out of " + num +
" for interface " + InterfaceId.ToString() + ", version " +
InterfaceVersion.ToString());
return false;
}
Trace(TraceLoggerType.RpcLibrary,
TraceEventType.Verbose,
"Library: Successfully removed " + removed + " out of " + num +
" for interface " + InterfaceId.ToString() + ", version " +
InterfaceVersion.ToString());
return true;
}
public RpcServerEntry Get(Guid InterfaceId, Version InterfaceVersion)
{
if (!Exists(InterfaceId, InterfaceVersion))
{
return null;
}
return m_Entries[InterfaceId].FirstOrDefault(
entry => entry.InterfaceVersion.Equals(InterfaceVersion) &&
entry.PointerSize == IntPtr.Size);
}
public List<RpcServerEntry> Get(Guid InterfaceId)
{
if (!Exists(InterfaceId))
{
return null;
}
return m_Entries[InterfaceId].Where(
entry => entry.PointerSize == IntPtr.Size).ToList();
}
public Dictionary<Guid, List<RpcServer>> Get()
{
var all = new Dictionary<Guid, List<RpcServer>>();
foreach (var kvp in m_Entries)
{
foreach (var serverEntry in kvp.Value)
{
if (serverEntry.PointerSize != IntPtr.Size)
{
continue;
}
if (!all.ContainsKey(kvp.Key))
{
all.Add(kvp.Key, new List<RpcServer>());
}
all[kvp.Key].Add(
RpcServer.Deserialize(serverEntry.SerializedServer));
}
}
return all;
}
public bool Exists(Guid InterfaceId, Version InterfaceVersion)
{
if (!Exists(InterfaceId))
{
return false;
}
return m_Entries[InterfaceId].Any(
entry => entry.InterfaceVersion.Equals(InterfaceVersion) &&
entry.PointerSize == IntPtr.Size);
}
public bool Exists(Guid InterfaceId)
{
return m_Entries.ContainsKey(InterfaceId);
}
public void Clear()
{
m_Entries.Clear();
}
public int GetCount()
{
return m_Entries.Values.Sum(v => v.Count(s => s.PointerSize == IntPtr.Size));
}
public override string ToString()
{
StringBuilder sb = new StringBuilder();
var all = Get();
sb.Append("Database for " + (IntPtr.Size == 4 ? "32-bit" : "64-bit") +
" RPC servers.");
foreach (var kvp in all)
{
sb.AppendLine("Server " + kvp.Key + " has " + kvp.Value.Count + " versions:");
kvp.Value.ForEach(entry =>
{
sb.AppendLine(" Server " + entry.InterfaceId.ToString() +
" / " + entry.InterfaceVersion.ToString());
sb.AppendLine(" FilePath : " + entry.FilePath);
sb.AppendLine(" Procedure Count : " + entry.ProcedureCount);
sb.AppendLine(" Service Name: " + entry.ServiceName);
sb.AppendLine(" Endpoints:");
foreach (var endpoint in entry.Endpoints.ToList())
{
sb.AppendLine(" " + endpoint.InterfaceId.ToString() +
" / " + endpoint.InterfaceVersion.ToString() +
" - " + endpoint.BindingString);
}
});
}
return sb.ToString();
}
}
public class RpcLibrary
{
private static string m_DefaultPath = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
"RpcInvestigator");
private static string m_DefaultRpcServerDb = "rpcserver.db";
private readonly string m_Path;
private RpcServerData m_Data;
public RpcLibrary(string TargetPath)
{
m_Path = TargetPath;
if (string.IsNullOrEmpty(m_Path))
{
m_Path = Path.Combine(m_DefaultPath, m_DefaultRpcServerDb);
}
m_Data = new RpcServerData();
}
public override string ToString()
{
StringBuilder sb = new StringBuilder();
sb.AppendLine("Database '" + m_Path + "' with " + m_Data.GetCount() + " entries.");
sb.AppendLine("Generated on " + DateTime.Now);
sb.AppendLine(m_Data.ToString());
return sb.ToString();
}
public void Load()
{
if (!File.Exists(m_Path))
{
try
{
File.Create(m_Path).Close();
Trace(TraceLoggerType.RpcLibrary,
TraceEventType.Verbose,
"Library: successfully created '" + m_Path + "'");
}
catch (Exception ex) // swallow
{
Trace(TraceLoggerType.RpcLibrary,
TraceEventType.Error,
"Failed to create database '" +
m_Path + "': " + ex.Message);
}
}
else
{
try
{
using (var stream = File.OpenRead(m_Path))
{
BinaryFormatter formatter = new BinaryFormatter();
m_Data = (RpcServerData)formatter.Deserialize(stream);
Trace(TraceLoggerType.RpcLibrary,
TraceEventType.Verbose,
"Library: successfully loaded '" + m_Path + "'");
Trace(TraceLoggerType.RpcLibrary,
TraceEventType.Verbose,
"Library: " + GetServerCount() + " entries.");
}
}
catch (Exception ex)
{
throw new Exception("Failed to load database '" + m_Path +
"': " + ex.Message);
}
}
}
public void Save()
{
try
{
using (var stream = File.Open(
m_Path,
FileMode.Create,
FileAccess.ReadWrite,
FileShare.ReadWrite))
{
BinaryFormatter formatter = new BinaryFormatter();
formatter.Serialize(stream, m_Data);
}
}
catch (Exception ex)
{
throw new Exception("Failed to save '" + m_Path + "': " + ex.Message);
}
Trace(TraceLoggerType.RpcLibrary,
TraceEventType.Verbose,
"Library: successfully saved '" + m_Path + "'");
}
public bool Add(RpcServer Server)
{
return m_Data.Add(Server);
}
public bool Remove(Guid InterfaceId)
{
return m_Data.Remove(InterfaceId);
}
public bool Remove(Guid InterfaceId, Version Version)
{
return m_Data.Remove(InterfaceId, Version);
}
public RpcServer Get(Guid InterfaceId, Version InterfaceVersion)
{
var item = m_Data.Get(InterfaceId, InterfaceVersion);
if (item == null)
{
return null;
}
return RpcServer.Deserialize(item.SerializedServer);
}
public List<RpcServer> Get(Guid InterfaceId)
{
var items = m_Data.Get(InterfaceId);
if (items == null || items.Count() == 0)
{
return null;
}
return items.Select(
entry => RpcServer.Deserialize(entry.SerializedServer)).ToList();
}
public List<RpcServer> GetAllServers()
{
//
// Note: Expensive!
//
return m_Data.Get().Values.SelectMany(v => v).ToList();
}
public Dictionary<Guid, List<RpcServer>> GetServersWithMultipleVersions()
{
//
// Note: expensive!
//
return m_Data.Get().Where(
s => s.Value.Count > 0).ToDictionary(i => i.Key, i => i.Value);
}
public void Clear()
{
m_Data.Clear();
}
public bool Exists(Guid InterfaceId)
{
return m_Data.Exists(InterfaceId);
}
public bool Exists(Guid InterfaceId, Version InterfaceVersion)
{
return m_Data.Exists(InterfaceId, InterfaceVersion);
}
public List<RpcServer> Find(RpcLibraryFilter Filter)
{
var matches = new List<RpcServer>();
var keyword = Filter.Keyword;
switch (Filter.FilterType)
{
case RpcLibraryFilterType.FilterByKeyword:
{
var all = GetAllServers(); // expensive: deserializes all servers!
foreach (var server in all)
{
if ((!string.IsNullOrEmpty(server.ServiceDisplayName) &&
server.ServiceDisplayName.IndexOf(keyword, StringComparison.OrdinalIgnoreCase) >= 0) ||
(!string.IsNullOrEmpty(server.ServiceName) &&
server.ServiceName.IndexOf(keyword, StringComparison.OrdinalIgnoreCase) >= 0) ||
(!string.IsNullOrEmpty(server.FilePath) &&
server.FilePath.IndexOf(keyword, StringComparison.OrdinalIgnoreCase) >= 0) ||
(server.InterfaceId.ToString().IndexOf(keyword, StringComparison.OrdinalIgnoreCase) >= 0))
{
matches.Add(server);
continue;
}
foreach (var endpoint in server.Endpoints)
{
if ((!string.IsNullOrEmpty(endpoint.EndpointPath) &&
endpoint.EndpointPath.IndexOf(keyword, StringComparison.OrdinalIgnoreCase) >= 0) ||
(!string.IsNullOrEmpty(endpoint.BindingString) &&
endpoint.BindingString.IndexOf(keyword, StringComparison.OrdinalIgnoreCase) >= 0) ||
(endpoint.InterfaceId.ToString().IndexOf(keyword, StringComparison.OrdinalIgnoreCase) >= 0))
{
matches.Add(server);
break;
}
}
}
break;
}
default:
{
var all = GetAllServers(); // expensive: deserializes all servers!
matches.AddRange(all);
break;
}
}
return matches;
}
public int GetServerCount()
{
return m_Data.GetCount();
}
public bool Merge(
List<RpcServer> Servers
)
{
if (Servers.Count() == 0)
{
Trace(TraceLoggerType.RpcLibrary,
TraceEventType.Information,
"Merge: No RPC servers were provided.");
return true;
}
//
// Add to database and save.
// Note: a failure to add means that RPC server is already in the
// database, which is entirely possible.
//
int numAdded = 0;
Servers.ForEach(server =>
{
if (Add(server))
{
numAdded++;
}
});
try
{
Save();
Trace(TraceLoggerType.RpcLibrary,
TraceEventType.Information,
"Merge: Added " + numAdded + " RPC servers.");
}
catch (Exception ex)
{
Trace(TraceLoggerType.RpcLibrary,
TraceEventType.Error,
"An error occurred when saving the database: " +
ex.Message);
return false;
}
return true;
}
public async Task<bool> Refresh(
Settings Settings,
Action<ArrayList> ProgressInitializeCallback,
Action<string> ProgressReportCallback,
Action<string> ProgressSetStatusLabelCallback,
int MaxServers = 0
)
{
bool success = false;
var alpcServers = RpcAlpcServer.GetAlpcServers().ToList();
if (alpcServers.Count() == 0)
{
Trace(TraceLoggerType.RpcLibrary,
TraceEventType.Warning,
"Refresh: No RPC ALPC servers available.");
return true;
}
//
// Build a list of process IDs that contain at least one RPC server
// that's not currently in our library. Because the same DLL/RPC server
// can be loaded in multiple processes, and we only need to parse it
// once, we'll pick a representative process.
//
// In practice, this optimization doesn't help much, as there always
// seems to be some prolific DLLs that can't be parsed for whatever
// reason, meaning we'll always have to re-parse them.
//
var distinctInterfaceIds = new Dictionary<string, int>();
await Task.Run(() =>
{
foreach (var s in alpcServers)
{
if (s.EndpointCount == 0 || s.Endpoints == null)
{
continue;
}
s.Endpoints.Where(e =>
!Exists(e.InterfaceId, e.InterfaceVersion)).ToList().ForEach(e =>
{
var id = e.InterfaceId.ToString();
if (!distinctInterfaceIds.ContainsKey(id))
{
distinctInterfaceIds.Add(id, s.ProcessId);
}
});
}
});
var pids = distinctInterfaceIds.Values.ToList();
if (pids.Count() == 0)
{
Trace(TraceLoggerType.RpcLibrary,
TraceEventType.Information,
"Refresh: No new ALPC servers.");
return true;
}
pids = pids.Distinct().ToList();
//
// Get all loaded modules for all processes, as reported by WMI.
//
var modules = new List<string>();
ProgressSetStatusLabelCallback?.Invoke("Scanning for loaded modules...");
await Task.Run(() =>
{
modules = GetLoadedModules(pids);
});
Trace(TraceLoggerType.RpcLibrary,
TraceEventType.Information,
"Refresh: Discovered " + modules.Count() +
" unique modules.");
//
// Get RPC servers from unique modules.
//
// IMPORTANT: ParsePeFile (NtApiDotNet) uses DbgHelp.dll's SymFromAddr
// function which states on MSDN:
// "All DbgHelp functions, such as this one, are single threaded.
// Therefore, calls from more than one thread to this function will
// likely result in unexpected behavior or memory corruption.
// To avoid this, you must synchronize all concurrent calls from
// more than one thread to this function.
// Do NOT use TPL or threading here!
//
int numAdded = 0;
int max = modules.Count();
ProgressInitializeCallback?.Invoke(new ArrayList() {
1, max, "Processing " + max + " unique modules, please wait..." });
var rpcServers = new List<RpcServer>();
await Task.Run(() =>
{
foreach (var module in modules)
{
ProgressReportCallback?.Invoke("Processing module " +
"<current> of <total>");
var servers = new List<RpcServer>();
try
{
servers = RpcServer.ParsePeFile(
module, Settings.m_DbghelpPath, Settings.m_SymbolPath, false).ToList();
if (servers.Count() > 0)
{
rpcServers.AddRange(servers);
}
}
catch (Exception ex) // swallow
{
Trace(TraceLoggerType.RpcLibrary,
TraceEventType.Error,
"Refresh: Unable to parse PE file '" +
module + "': " + ex.Message);
}
//
// Add to the library
//
foreach (var s in servers)
{
//
// Because we had to parse all modules in a process that might
// only contain one unknown RPC server, we'll end up parsing
// RPC servers we already know about. Don't try to re-add them.
//
if (!distinctInterfaceIds.ContainsKey(s.InterfaceId.ToString()))
{
continue;
}
if (Add(s))
{
numAdded++;
}
else
{
//
// This shouldn't happen, since we already culled the list
// to only servers that don't exist in the library, and we
// intentionally skipped servers that were unnecessarily
// parsed above.
//
Trace(TraceLoggerType.RpcLibrary,
TraceEventType.Error,
"Refresh: failed to add server " +
s.InterfaceId.ToString() + " to the library.");
}
}
if (MaxServers > 0 && numAdded >= MaxServers)
{
//
// This path is useful for UnitTests that don't need every
// single RPC server on the system.
//
return;
}
//
// Sometimes symbol resolution fails. And when it does, it
// fails silently (see NtApiDotNet's NdrParser.cs). I don't
// know why. Maybe it's some sort of rate limiter logic on
// the public MS symbol server side.
//
Task.Delay(50);
}
});
try
{
Save();
Trace(TraceLoggerType.RpcLibrary,
TraceEventType.Information,
"Refresh: Added " + numAdded + " RPC servers.");
ProgressSetStatusLabelCallback?.Invoke("Added " + numAdded + " RPC servers.");
success = true;
}
catch (Exception ex)
{
Trace(TraceLoggerType.RpcLibrary,
TraceEventType.Error,
"Refresh: An error occurred when saving the database: " +
ex.Message);
ProgressSetStatusLabelCallback?.Invoke("An exception occurred, please see the logs.");
}
return success;
}
private List<string> GetLoadedModules(List<int> ProcessIds)
{
var modules = new List<string>();
foreach (var pid in ProcessIds)
{
try
{
using (var proc = NtProcess.Open(pid,
ProcessAccessRights.QueryLimitedInformation
| ProcessAccessRights.DupHandle,
false))
{
if (!proc.IsSuccess)
continue;
if (proc.Result.Frozen)
continue;
var p = Process.GetProcessById(pid);
var moduleList = p.Modules.Cast<
System.Diagnostics.ProcessModule>().ToList().Select(
m => m.FileName).ToList();
modules = modules.Union(moduleList).ToList();
}
}
catch (Exception ex)
{
Trace(TraceLoggerType.RpcLibrary,
TraceEventType.Error,
"GetLoadedModules exception: " + ex.Message);
}
}
return modules;
}
}
}

197
Settings.cs Normal file
View File

@ -0,0 +1,197 @@
//
// Copyright (c) 2022-present, Trail of Bits, Inc.
// All rights reserved.
//
// This source code is licensed in accordance with the terms specified in
// the LICENSE file found in the root directory of this source tree.
//
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
namespace RpcInvestigator
{
using static TraceLogger;
public class Settings
{
public static readonly string m_WorkingDir = Environment.GetFolderPath(
Environment.SpecialFolder.ApplicationData) + "\\RpcInvestigator";
public static readonly string m_LogDir = Path.Combine(m_WorkingDir, "Logs");
private static string m_DefaultPath = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
"RpcInvestigator");
private static string m_DefaultSettingsFileName = "settings.json";
public string m_DbghelpPath;
public string m_SymbolPath;
public SourceLevels m_TraceLevel;
public List<string> m_SnifferColumns;
public Settings()
{
m_TraceLevel = SourceLevels.Information;
m_SnifferColumns = new List<string>();
if (!Directory.Exists(m_DefaultPath))
{
try
{
Directory.CreateDirectory(m_DefaultPath);
}
catch (Exception ex) // swallow
{
Trace(TraceLoggerType.Settings,
TraceEventType.Warning,
"Unable to create settings directory '" +
m_DefaultPath + "': " + ex.Message);
}
}
if (!Directory.Exists(m_LogDir))
{
try
{
Directory.CreateDirectory(m_LogDir);
}
catch (Exception ex) // swallow
{
Trace(TraceLoggerType.Settings,
TraceEventType.Warning,
"Unable to create log directory '" +
m_LogDir + "': " + ex.Message);
}
}
m_SymbolPath = @"srv*c:\symbols*https://msdl.microsoft.com/download/symbols";
m_DbghelpPath = FindDbghelpDll();
}
public static void Validate (Settings Object)
{
return;
}
static public void Save(Settings Object, string Target)
{
string target = Target;
if (string.IsNullOrEmpty(target))
{
target = Path.Combine(m_DefaultPath, m_DefaultSettingsFileName);
}
string json;
try
{
json = JsonConvert.SerializeObject(Object, Formatting.Indented);
using (MemoryStream ms = new MemoryStream())
{
using (var sw = new StreamWriter(ms))
{
sw.Write(json);
sw.Flush();
}
}
File.WriteAllText(target, json);
}
catch (Exception ex)
{
throw new Exception("Could not serialize the Settings object " +
"to JSON: " +
ex.Message);
}
}
static public Settings Load(string Location)
{
if (!File.Exists(Location))
{
throw new Exception("File does not exist");
}
Settings settings;
try
{
var json = File.ReadAllText(Location);
settings = (Settings)JsonConvert.DeserializeObject(json, typeof(Settings));
Validate(settings);
}
catch (Exception ex)
{
throw new Exception("Could not deserialize settings: " + ex.Message);
}
return settings;
}
static public Settings LoadDefault()
{
var target = Path.Combine(m_DefaultPath, m_DefaultSettingsFileName);
if (!File.Exists(target))
{
return new Settings();
}
return Load(target);
}
private string FindDbghelpDll()
{
//
// First try to get the dbghelp.dll from the Windows debugging tools.
//
Trace(TraceLoggerType.Settings,
TraceEventType.Information,
"Searching for dbghelp.dll");
var debuggingToolsPath =
@"C:\Program Files (x86)\Windows Kits\10\Debuggers\x64\dbghelp.dll";
if (File.Exists(debuggingToolsPath))
{
Trace(TraceLoggerType.Settings,
TraceEventType.Information,
"Found dbghelp.dll in Debugging Tools for Windows: " +
debuggingToolsPath);
return debuggingToolsPath;
}
//
// If that fails, try to get dbghelp.dll from any of the installed Windows
// SDKs and prefer the most recent version of the SDK.
//
var baseDir = @"C:\Program Files (x86)\Windows Kits\10\bin";
var potentialKits = new List<string>();
foreach (string dirname in Directory.GetDirectories(baseDir))
{
if (Path.GetFileName(dirname).StartsWith("10."))
{
potentialKits.Add(dirname);
}
}
//
// Sort and then reverse potential kits so that we look at the most recent
// kit first
//
potentialKits.Sort();
potentialKits.Reverse();
foreach (string dirname in potentialKits)
{
var dll = Path.Combine(dirname, "x64", "dbghelp.dll");
if (File.Exists(dll))
{
Trace(TraceLoggerType.Settings,
TraceEventType.Information,
$"Found dbghelp.dll in SDK: {dll}");
return dll;
}
}
Trace(TraceLoggerType.Settings,
TraceEventType.Warning,
"unable to find dbghelp.dll, symbols may not be available");
return null;
}
}
}

171
TabPages/ContextMenu.cs Normal file
View File

@ -0,0 +1,171 @@
//
// Copyright (c) 2022-present, Trail of Bits, Inc.
// All rights reserved.
//
// This source code is licensed in accordance with the terms specified in
// the LICENSE file found in the root directory of this source tree.
//
using BrightIdeasSoftware;
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
namespace RpcInvestigator.TabPages
{
public class ContextMenu : ContextMenuStrip
{
public ContextMenu()
{
Items.AddRange(new ToolStripMenuItem[]
{
new ToolStripMenuItem("Copy Row(s)", null, ContextMenuCopyRow),
new ToolStripMenuItem("Copy Column", null, ContextMenuCopyColumn),
});
}
public
void
ContextMenuCopyRow(
object Sender,
EventArgs Args
)
{
object tag = ((ToolStripMenuItem)Sender).Tag;
if (tag == null)
{
return;
}
var args = (CellRightClickEventArgs)tag;
var listview = args.ListView;
StringBuilder text = new StringBuilder();
int count = 0;
foreach (int index in listview.SelectedIndices)
{
foreach (ListViewItem.ListViewSubItem cell in listview.Items[index].SubItems)
{
text.Append(cell.Text);
text.Append(',');
}
text.Length--; // remove trailing ','
text.AppendLine();
count++;
}
text.Length--; // remove trailing new line
Clipboard.SetText(text.ToString());
}
public
void
ContextMenuCopyColumn(
object Sender,
EventArgs Args
)
{
object tag = ((ToolStripMenuItem)Sender).Tag;
if (tag == null)
{
return;
}
var args = (CellRightClickEventArgs)tag;
var targetCell = args.ColumnIndex;
var listview = args.ListView as FastObjectListView;
StringBuilder text = new StringBuilder();
int count = 0;
foreach (int index in listview.SelectedIndices)
{
var cell = listview.Items[index].SubItems[targetCell];
text.AppendLine(cell.Text);
count++;
}
text.Length--; // remove trailing new line
Clipboard.SetText(text.ToString());
}
public
static
void
BuildRightClickMenu (
CellRightClickEventArgs Args,
List<ToolStripMenuItem> AdditionalMenuItems
)
{
var row = Args.Model;
var column = Args.Column;
if (row == null || column == null)
{
return;
}
Args.MenuStrip = new ContextMenu();
foreach (var item in AdditionalMenuItems)
{
Args.MenuStrip.Items.Add(item);
}
foreach (var item in Args.MenuStrip.Items)
{
((ToolStripMenuItem)item).Tag = Args;
}
}
public
static
void
AddSearchElements (
CellRightClickEventArgs Args
)
{
if (Args.MenuStrip == null || Args.ListView == null)
{
return;
}
var listview = Args.ListView;
var textbox = new ToolStripTextBox("keyword");
textbox.Text = "<keyword search>";
textbox.Click += new EventHandler((object o, EventArgs a) =>
{
if (textbox.Text == "<keyword search>")
{
textbox.Text = "";
}
});
textbox.LostFocus += new EventHandler((object o, EventArgs a) =>
{
if (textbox.Text == "")
{
textbox.Text = "<keyword search>";
}
});
textbox.KeyUp += new KeyEventHandler((object o, KeyEventArgs a) =>
{
if (a.KeyCode != Keys.Enter)
{
return;
}
var text = textbox.Text;
if (string.IsNullOrEmpty(text))
{
listview.ModelFilter = null;
Args.MenuStrip.Close();
return;
}
TextMatchFilter searchFilter = new TextMatchFilter(listview, text.ToLower());
listview.DefaultRenderer = new HighlightTextRenderer(searchFilter);
listview.ModelFilter = searchFilter;
Args.MenuStrip.Close();
});
textbox.KeyDown += new KeyEventHandler((object o, KeyEventArgs a) =>
{
if (a.KeyCode != Keys.Enter)
{
return;
}
a.SuppressKeyPress = true;
a.Handled = true;
});
Args.MenuStrip.Items.Add(textbox);
}
}
}

View File

@ -0,0 +1,239 @@
//
// Copyright (c) 2022-present, Trail of Bits, Inc.
// All rights reserved.
//
// This source code is licensed in accordance with the terms specified in
// the LICENSE file found in the root directory of this source tree.
//
using System;
using System.Collections.Generic;
using System.Linq;
using NtApiDotNet.Win32;
using NtApiDotNet;
using System.Windows.Forms;
using BrightIdeasSoftware;
using System.Drawing;
using System.Diagnostics;
using RpcInvestigator.TabPages;
using System.Runtime.InteropServices;
using System.Security.AccessControl;
using Newtonsoft.Json.Linq;
using RpcInvestigator.Util;
using System.ServiceModel.Channels;
namespace RpcInvestigator
{
using static TraceLogger;
public class RpcAlpcServerList : TabPage
{
public FastObjectListView m_Listview;
private TabManager m_TabManager;
public RpcAlpcServerList(TabManager Manager)
{
m_TabManager = Manager;
m_Listview = new FastObjectListView();
Random random = new Random();
string rand = random.Next().ToString();
m_Listview.OwnerDraw = true;
m_Listview.BorderStyle = BorderStyle.FixedSingle;
m_Listview.CellEditUseWholeCell = false;
m_Listview.Cursor = Cursors.Default;
m_Listview.Dock = DockStyle.Fill;
m_Listview.FullRowSelect = true;
m_Listview.GridLines = true;
m_Listview.HideSelection = false;
m_Listview.Location = new Point(0, 0);
m_Listview.Margin = new Padding(5);
m_Listview.Name = "RpcAlpcServerListview" + rand;
m_Listview.UseAlternatingBackColors = true;
m_Listview.AlternateRowBackColor = Color.LightBlue;
m_Listview.UseCompatibleStateImageBehavior = false;
m_Listview.View = View.Details;
m_Listview.VirtualMode = true;
m_Listview.ShowGroups = false;
m_Listview.Alignment = ListViewAlignment.Left;
Generator.GenerateColumns(m_Listview, typeof(RpcAlpcServer), true);
foreach (var column in m_Listview.AllColumns)
{
if (column.Name.ToLower() == "endpoints")
{
column.IsVisible = false;
}
column.MaximumWidth = -1;
}
m_Listview.AllColumns.ForEach(col =>
{
if (col.Name == "SecurityDescriptor")
{
col.AspectToStringConverter = delegate (object Item)
{
if (Item == null)
{
return "";
}
return SddlParser.Parse(Item.ToString());
};
}
});
//
// When a listview row is double-clicked, a new tab will open with endpoints
// for the selected RPC server. Right-click shows context menu.
//
m_Listview.DoubleClick += new EventHandler((object obj, EventArgs e2) =>
{
if (m_Listview.SelectedObjects == null ||
m_Listview.SelectedObjects.Count == 0)
{
return;
}
var selectedRow = m_Listview.SelectedObjects.Cast<RpcAlpcServer>().ToList()[0];
m_TabManager.LoadRpcEndpointsTab(
selectedRow.Name, selectedRow.Endpoints.ToList(), selectedRow.Name);
});
m_Listview.CellRightClick += RightClickHandler;
Controls.Add(m_Listview);
}
public int GetCount()
{
if (m_Listview.Objects == null)
{
return 0;
}
return m_Listview.Objects.Cast<RpcAlpcServer>().Count();
}
public List<RpcAlpcServer> GetAll()
{
if (m_Listview.Objects == null)
{
return new List<RpcAlpcServer>();
}
return m_Listview.Objects.Cast<RpcAlpcServer>().ToList();
}
public void Build()
{
IEnumerable<RpcAlpcServer> servers;
Text = "RPC ALPC Servers by Process";
Name = "RPC ALPC Servers by Process";
ImageIndex = 0;
try
{
using (NtToken token = NtProcess.Current.OpenToken())
{
token.SetPrivilege(TokenPrivilegeValue.SeDebugPrivilege, PrivilegeAttributes.Enabled, true);
servers = RpcAlpcServer.GetAlpcServers();
if (servers.Count() == 0)
{
Trace(TraceLoggerType.RpcAlpcServerList,
TraceEventType.Error,
"No RPC servers available.");
}
else
{
Trace(TraceLoggerType.RpcAlpcServerList,
TraceEventType.Information,
"Retrieved " + servers.Count() + " RPC ALPC servers.");
m_Listview.ClearObjects();
m_Listview.SetObjects(servers);
m_Listview.AutoResizeColumns();
m_Listview.RebuildColumns();
m_Listview.Refresh();
}
}
}
catch (Exception ex)
{
Trace(TraceLoggerType.RpcAlpcServerList,
TraceEventType.Error,
"Unable to retrieve RPC server list: " + ex.Message);
}
}
public
static
RpcEndpoint
FindEndpoint(
Guid InterfaceId,
Version InterfaceVersion
)
{
var alpcServers = RpcAlpcServer.GetAlpcServers().ToList();
RpcEndpoint match = null;
if (alpcServers.Count() == 0)
{
Trace(TraceLoggerType.RpcAlpcServerList,
TraceEventType.Warning,
"FindEndpoint: No RPC ALPC servers available.");
return null;
}
foreach (var server in alpcServers)
{
match = server.Endpoints.FirstOrDefault(endpoint =>
endpoint.InterfaceId.Equals(InterfaceId) &&
endpoint.InterfaceVersion.Equals(InterfaceVersion));
if (match != null)
{
break;
}
}
if (match == null)
{
Trace(TraceLoggerType.RpcAlpcServerList,
TraceEventType.Warning,
"FindEndpoint: No ALPC server match for UUID " +
InterfaceId + " and version " + InterfaceVersion);
}
return match;
}
private
void
RightClickHandler(
object Obj,
CellRightClickEventArgs Args
)
{
TabPages.ContextMenu.BuildRightClickMenu(Args, new List<ToolStripMenuItem>{
new ToolStripMenuItem("Open in Library", null, ContextMenuOpenAlpcServerInLibrary),
});
}
private
async
void
ContextMenuOpenAlpcServerInLibrary(
object Sender,
EventArgs Args
)
{
if (m_TabManager.IsLibraryEmpty())
{
MessageBox.Show("The library is empty. Please click the Refresh menu " +
"item under the Library menu to regenerate it.");
return;
}
object tag = ((ToolStripMenuItem)Sender).Tag;
if (tag == null)
{
return;
}
var args = (CellRightClickEventArgs)tag;
var alpcServer = args.Model as RpcAlpcServer;
var filter = new RpcLibraryFilter
{
FilterType = RpcLibraryFilterType.FilterByKeyword,
Keyword = alpcServer.Name
};
_ = await m_TabManager.LoadRpcLibraryServersTab(filter);
}
}
}

174
TabPages/RpcEndpointList.cs Normal file
View File

@ -0,0 +1,174 @@
//
// Copyright (c) 2022-present, Trail of Bits, Inc.
// All rights reserved.
//
// This source code is licensed in accordance with the terms specified in
// the LICENSE file found in the root directory of this source tree.
//
using System;
using System.Collections.Generic;
using NtApiDotNet.Win32;
using System.Windows.Forms;
using BrightIdeasSoftware;
using System.Drawing;
using System.Diagnostics;
using System.Linq;
using NtApiDotNet;
using RpcInvestigator.TabPages;
namespace RpcInvestigator
{
using static TraceLogger;
public class RpcEndpointList : TabPage
{
public FastObjectListView m_Listview;
private RpcLibrary m_Library;
private TabManager m_Manager;
public RpcEndpointList (RpcLibrary Library, TabManager Manager)
{
m_Manager = Manager;
m_Library = Library;
m_Listview = new FastObjectListView();
Random random = new Random();
string rand = random.Next().ToString();
m_Listview.OwnerDraw = true;
m_Listview.BorderStyle = BorderStyle.FixedSingle;
m_Listview.CellEditUseWholeCell = false;
m_Listview.Cursor = Cursors.Default;
m_Listview.Dock = DockStyle.Fill;
m_Listview.FullRowSelect = true;
m_Listview.GridLines = true;
m_Listview.HideSelection = false;
m_Listview.Location = new Point(0, 0);
m_Listview.Margin = new Padding(5);
m_Listview.Name = "RpcEndpointListview" + rand;
m_Listview.UseAlternatingBackColors = true;
m_Listview.AlternateRowBackColor = Color.LightBlue;
m_Listview.UseCompatibleStateImageBehavior = false;
m_Listview.View = View.Details;
m_Listview.VirtualMode = true;
m_Listview.ShowGroups = false;
m_Listview.Alignment = ListViewAlignment.Left;
Generator.GenerateColumns(m_Listview, typeof(RpcEndpoint), true);
foreach (var column in m_Listview.AllColumns)
{
column.MaximumWidth = -1;
}
//
// When a listview row is double-clicked, a new tab will open with procedures
// for the selected interface on the given RPC server. When a row is right-clicked,
// a context menu shows.
//
m_Listview.DoubleClick += new EventHandler((object obj, EventArgs e2) =>
{
if (m_Listview.SelectedObjects == null ||
m_Listview.SelectedObjects.Count == 0)
{
return;
}
var selectedRow = m_Listview.SelectedObjects.Cast<RpcEndpoint>().ToList()[0];
var id = selectedRow.InterfaceId;
var version = selectedRow.InterfaceVersion;
var server = m_Library.Get(id, version);
if (server == null)
{
MessageBox.Show("Unable to locate RPC server " +
id.ToString() + ", version " + version.ToString());
return;
}
m_Manager.LoadRpcProceduresTab(server.Name, server.Procedures.ToList());
});
m_Listview.CellRightClick += RightClickHandler;
Controls.Add(m_Listview);
}
public void Build(string Name, List<RpcEndpoint> Endpoints)
{
this.Name = Name;
Text = BuildTabTitle(Name);
ImageIndex = 1;
try
{
using (NtToken token = NtProcess.Current.OpenToken())
{
if (Endpoints.Count > 0)
{
m_Listview.ClearObjects();
m_Listview.SetObjects(Endpoints);
m_Listview.RebuildColumns();
m_Listview.AutoResizeColumns();
}
}
}
catch (Exception ex)
{
Trace(TraceLoggerType.RpcEndpointList,
TraceEventType.Error,
"Unable to retrieve RPC endpoint list: " + ex.Message);
}
}
public int GetCount()
{
if (m_Listview.Objects == null)
{
return 0;
}
return m_Listview.Objects.Cast<RpcEndpoint>().Count();
}
private
void
RightClickHandler(
object Obj,
CellRightClickEventArgs Args
)
{
TabPages.ContextMenu.BuildRightClickMenu(Args, new List<ToolStripMenuItem>{
new ToolStripMenuItem("New Client", null, ContextMenuNewClient),
});
}
private void ContextMenuNewClient(object Sender, EventArgs Args)
{
object tag = ((ToolStripMenuItem)Sender).Tag;
if (tag == null)
{
return;
}
var args = (CellRightClickEventArgs)tag;
//
// We have the endpoint, we just need to find the corresponding
// RpcServer object in the library.
//
var endpoint = args.Model as RpcEndpoint;
var id = endpoint.InterfaceId;
var version = endpoint.InterfaceVersion;
var server = m_Library.Get(id, version);
if (server == null)
{
MessageBox.Show("Unable to locate an RPC server definition for id " +
id.ToString() + ", version " + version.ToString());
return;
}
var clientWindow = new Client(server, endpoint);
clientWindow.ShowDialog();
}
private static string BuildTabTitle(string Name)
{
var name = Name;
if (name.Length > 30)
{
name = name.Substring(0, 15) + "..." + name.Substring(
name.Length - 15, 15);
}
return "Endpoints for " + name;
}
}
}

View File

@ -0,0 +1,189 @@
//
// Copyright (c) 2022-present, Trail of Bits, Inc.
// All rights reserved.
//
// This source code is licensed in accordance with the terms specified in
// the LICENSE file found in the root directory of this source tree.
//
using BrightIdeasSoftware;
using NtApiDotNet.Ndr;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Threading.Tasks;
using System.Windows.Forms;
using NtApiDotNet;
using System.Diagnostics;
using System.Linq;
namespace RpcInvestigator
{
using static TraceLogger;
public class RpcLibraryProcedureList : TabPage
{
public FastObjectListView m_Listview;
private RpcLibrary m_Library;
public RpcLibraryProcedureList(RpcLibrary Library)
{
m_Library = Library;
m_Listview = new FastObjectListView();
Random random = new Random();
string rand = random.Next().ToString();
m_Listview.OwnerDraw = true;
m_Listview.BorderStyle = BorderStyle.FixedSingle;
m_Listview.CellEditUseWholeCell = false;
m_Listview.Cursor = Cursors.Default;
m_Listview.Dock = DockStyle.Fill;
m_Listview.FullRowSelect = true;
m_Listview.GridLines = true;
m_Listview.HideSelection = false;
m_Listview.Location = new Point(0, 0);
m_Listview.Margin = new Padding(5);
m_Listview.Name = "RpcLibraryProceduresListview" + rand;
m_Listview.UseAlternatingBackColors = true;
m_Listview.AlternateRowBackColor = Color.LightBlue;
m_Listview.UseCompatibleStateImageBehavior = false;
m_Listview.View = View.Details;
m_Listview.VirtualMode = true;
m_Listview.ShowGroups = false;
m_Listview.UseFiltering = true;
m_Listview.Alignment = ListViewAlignment.Left;
Generator.GenerateColumns(m_Listview, typeof(RpcLibraryProcedure), true);
foreach (var column in m_Listview.AllColumns)
{
column.MaximumWidth = -1;
}
m_Listview.CellRightClick += RightClickHandler;
Controls.Add(m_Listview);
}
public async Task<bool> Build(
RpcLibraryFilter Filter,
ToolStripProgressBar ProgressBar,
ToolStripStatusLabel ProgressLabel
)
{
Text = "Procedures";
Name = "Procedures";
ImageIndex = 3;
try
{
using (NtToken token = NtProcess.Current.OpenToken())
{
var searchResults = new List<RpcLibraryProcedure>();
ProgressBar.Step = 1;
ProgressBar.Value = 0;
ProgressBar.Visible = true;
await Task.Run(() =>
{
var results = m_Library.Find(Filter);
foreach (var server in results)
{
foreach (var procedure in server.Procedures)
{
var formatter = DefaultNdrFormatter.Create(DefaultNdrFormatterFlags.RemoveComments);
string friendly = formatter.FormatProcedure(procedure);
searchResults.Add(new RpcLibraryProcedure()
{
FilePath = server.FilePath,
InterfaceId = server.InterfaceId,
InterfaceVersion = server.InterfaceVersion,
Name = friendly,
});
};
}
});
if (searchResults.Count > 0)
{
m_Listview.SetObjects(searchResults);
m_Listview.AutoResizeColumns();
m_Listview.RebuildColumns();
}
ProgressBar.Value = 0;
ProgressBar.Visible = false;
}
}
catch (Exception ex)
{
Trace(TraceLoggerType.RpcLibraryProcedureList,
TraceEventType.Error,
"Unable to retrieve RPC library procedure list: " + ex.Message);
}
return true;
}
public int GetCount()
{
if (m_Listview.Objects == null)
{
return 0;
}
return m_Listview.Objects.Cast<RpcLibraryProcedure>().Count();
}
private
void
RightClickHandler(
object Obj,
CellRightClickEventArgs Args
)
{
TabPages.ContextMenu.BuildRightClickMenu(Args, new List<ToolStripMenuItem>{
new ToolStripMenuItem("New Client", null, ContextMenuNewClient),
new ToolStripMenuItem("Reset", null, ContextMenuResetSearch)
});
TabPages.ContextMenu.AddSearchElements(Args);
}
private void ContextMenuResetSearch(object Sender, EventArgs Args)
{
m_Listview.ModelFilter = null;
}
private void ContextMenuNewClient(object Sender, EventArgs Args)
{
object tag = ((ToolStripMenuItem)Sender).Tag;
if (tag == null)
{
return;
}
var args = (CellRightClickEventArgs)tag;
//
// We need to find the corresponding RpcServer object in the library.
//
var procedure = args.Model as RpcLibraryProcedure;
var id = procedure.InterfaceId;
var version = procedure.InterfaceVersion;
var server = m_Library.Get(id, version);
if (server == null)
{
MessageBox.Show("Unable to locate RPC server in library with UUID " + id +
" and version " + version);
return;
}
var endpoint = RpcAlpcServerList.FindEndpoint(id, version);
if (endpoint == null)
{
MessageBox.Show("Unable to locate an endpoint for RPC server UUID " +
id + " and version " + version);
return;
}
var clientWindow = new Client(server, endpoint);
clientWindow.ShowDialog();
}
}
public class RpcLibraryProcedure
{
public string FilePath { get; set; }
public Guid InterfaceId { get; set; }
public Version InterfaceVersion { get; set; }
public string Name { get; set; }
}
}

View File

@ -0,0 +1,215 @@
//
// Copyright (c) 2022-present, Trail of Bits, Inc.
// All rights reserved.
//
// This source code is licensed in accordance with the terms specified in
// the LICENSE file found in the root directory of this source tree.
//
using BrightIdeasSoftware;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Threading.Tasks;
using System.Windows.Forms;
using NtApiDotNet.Win32;
using NtApiDotNet;
using System.Diagnostics;
using System.Linq;
using RpcInvestigator.TabPages;
namespace RpcInvestigator
{
using static TraceLogger;
public class RpcLibraryServerList : TabPage
{
public FastObjectListView m_Listview;
private RpcLibrary m_Library;
private TabManager m_Manager;
public RpcLibraryServerList(RpcLibrary Library, TabManager Manager)
{
m_Manager = Manager;
m_Library = Library;
m_Listview = new FastObjectListView();
Random random = new Random();
string rand = random.Next().ToString();
m_Listview.OwnerDraw = true;
m_Listview.BorderStyle = BorderStyle.FixedSingle;
m_Listview.CellEditUseWholeCell = false;
m_Listview.Cursor = Cursors.Default;
m_Listview.Dock = DockStyle.Fill;
m_Listview.FullRowSelect = true;
m_Listview.GridLines = true;
m_Listview.Location = new Point(0, 0);
m_Listview.Margin = new Padding(5);
m_Listview.Name = "RpcLibraryServersListview" + rand;
m_Listview.UseAlternatingBackColors = true;
m_Listview.AlternateRowBackColor = Color.LightBlue;
m_Listview.UseCompatibleStateImageBehavior = false;
m_Listview.View = View.Details;
m_Listview.VirtualMode = true;
m_Listview.ShowGroups = false;
m_Listview.UseFiltering = true;
m_Listview.Alignment = ListViewAlignment.Left;
m_Listview.HideSelection = false;
//
// When a listview row is double-clicked, a new tab will open with endpoints
// for the selected RPC server. When a row is right-clicked,
// a context menu shows.
//
m_Listview.DoubleClick += new EventHandler((object obj, EventArgs e2) =>
{
if (m_Listview.SelectedObjects == null ||
m_Listview.SelectedObjects.Count == 0)
{
return;
}
var selectedRow = m_Listview.SelectedObjects.Cast<RpcServer>().ToList()[0];
m_Manager.LoadRpcEndpointsTab(
selectedRow.Name, selectedRow.Endpoints.ToList(), selectedRow.Name);
});
m_Listview.CellRightClick += RightClickHandler;
Generator.GenerateColumns(m_Listview, typeof(RpcServer), true);
foreach (var column in m_Listview.AllColumns)
{
if (column.Name.ToLower() == "transfersyntaxid" ||
column.Name.ToLower() == "transfersyntaxversion" ||
column.Name.ToLower() == "procedures" ||
column.Name.ToLower() == "server" ||
column.Name.ToLower() == "complextypes" ||
column.Name.ToLower() == "offset" ||
column.Name.ToLower() == "endpoints" ||
column.Name.ToLower() == "client")
{
column.IsVisible = false;
}
column.MaximumWidth = -1;
}
Controls.Add(m_Listview);
}
public void ScrollToServer(Guid InterfaceId)
{
if (m_Listview.Objects == null)
{
return;
}
var servers = m_Listview.Objects.Cast<RpcServer>().ToList();
for (int i = 0; i < servers.Count; i++)
{
if (servers[i].InterfaceId.Equals(InterfaceId))
{
m_Listview.EnsureModelVisible(servers[i]);
m_Listview.SelectObject(servers[i]);
return;
}
}
MessageBox.Show("RPC server " + InterfaceId.ToString() + " doesn't exist " +
"in the library.");
}
public async Task<bool> Build(
RpcLibraryFilter Filter,
ToolStripProgressBar ProgressBar,
ToolStripStatusLabel ProgressLabel
)
{
Text = "Servers";
Name = "Servers";
ImageIndex = 3;
try
{
using (NtToken token = NtProcess.Current.OpenToken())
{
ProgressBar.Step = 1;
ProgressBar.Value = 0;
ProgressBar.Visible = true;
var results = new List<RpcServer>();
await Task.Run(() =>
{
results = m_Library.Find(Filter);
});
if (results.Count > 0)
{
m_Listview.SetObjects(results);
m_Listview.AutoResizeColumns();
m_Listview.RebuildColumns();
}
ProgressBar.Value = 0;
ProgressBar.Visible = false;
}
}
catch (Exception ex)
{
Trace(TraceLoggerType.RpcLibraryServerList,
TraceEventType.Error,
"Unable to retrieve RPC library server list: " + ex.Message);
}
return true;
}
public int GetCount()
{
if (m_Listview.Objects == null)
{
return 0;
}
return m_Listview.Objects.Cast<RpcServer>().Count();
}
private
void
RightClickHandler(
object Obj,
CellRightClickEventArgs Args
)
{
TabPages.ContextMenu.BuildRightClickMenu(Args, new List<ToolStripMenuItem>{
new ToolStripMenuItem("New Client", null, ContextMenuNewClient),
new ToolStripMenuItem("Reset", null, ContextMenuResetSearch)
});
TabPages.ContextMenu.AddSearchElements(Args);
}
private void ContextMenuResetSearch(object Sender, EventArgs Args)
{
m_Listview.ModelFilter = null;
}
private void ContextMenuNewClient(object Sender, EventArgs Args)
{
object tag = ((ToolStripMenuItem)Sender).Tag;
if (tag == null)
{
return;
}
var args = (CellRightClickEventArgs)tag;
//
// We need to find the corresponding RpcServer object in the library.
//
var server = args.Model as RpcServer;
var id = server.InterfaceId;
var version = server.InterfaceVersion;
var match = m_Library.Get(id, version);
if (match == null)
{
MessageBox.Show("Unable to locate RPC server in library with id " +
id.ToString() + ", version " + version.ToString());
return;
}
var endpoint = RpcAlpcServerList.FindEndpoint(id, version);
if (endpoint == null)
{
MessageBox.Show("Unable to locate an endpoint for RPC server id " +
id.ToString() + ", version " + version.ToString());
return;
}
var clientWindow = new Client(server, endpoint);
clientWindow.ShowDialog();
}
}
}

View File

@ -0,0 +1,128 @@
//
// Copyright (c) 2022-present, Trail of Bits, Inc.
// All rights reserved.
//
// This source code is licensed in accordance with the terms specified in
// the LICENSE file found in the root directory of this source tree.
//
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using BrightIdeasSoftware;
using System.Drawing;
using System.Linq;
using NtApiDotNet;
using NtApiDotNet.Ndr;
using System.Diagnostics;
using RpcInvestigator.Util;
namespace RpcInvestigator
{
using static TraceLogger;
public class RpcProcedureList : TabPage
{
public FastObjectListView m_Listview;
public RpcProcedureList()
{
m_Listview = new FastObjectListView();
Random random = new Random();
string rand = random.Next().ToString();
m_Listview.OwnerDraw = true;
m_Listview.BorderStyle = BorderStyle.FixedSingle;
m_Listview.CellEditUseWholeCell = false;
m_Listview.Cursor = Cursors.Default;
m_Listview.Dock = DockStyle.Fill;
m_Listview.FullRowSelect = true;
m_Listview.GridLines = true;
m_Listview.HideSelection = false;
m_Listview.Location = new Point(0, 0);
m_Listview.Margin = new Padding(5);
m_Listview.Name = "RpcProcedureListview" + rand;
m_Listview.UseAlternatingBackColors = true;
m_Listview.AlternateRowBackColor = Color.LightBlue;
m_Listview.UseCompatibleStateImageBehavior = false;
m_Listview.View = View.Details;
m_Listview.VirtualMode = true;
m_Listview.ShowGroups = false;
m_Listview.Alignment = ListViewAlignment.Left;
Generator.GenerateColumns(m_Listview, typeof(NdrProcedureDefinition), true);
foreach (var column in m_Listview.AllColumns)
{
column.Renderer = new NoEllipsisRenderer();
column.MaximumWidth = -1;
}
m_Listview.RowHeight = 125;
m_Listview.AllColumns[1].AspectToStringConverter = delegate (object Params)
{
if (Params == null)
{
return "<null>";
}
StringBuilder sb = new StringBuilder();
foreach (var param in (IList<NdrProcedureParameter>)Params)
{
sb.AppendLine(param.ToString());
}
return sb.ToString();
};
m_Listview.AllColumns[8].AspectToStringConverter = delegate (object DispatchFunction)
{
if (DispatchFunction == null)
{
return "<null>";
}
return "0x" + ((IntPtr)DispatchFunction).ToString("X");
};
Controls.Add(m_Listview);
}
public void Build(string Name, List<NdrProcedureDefinition> Procedures)
{
this.Name = Name + " Procedures";
Text = BuildTabTitle(Name);
ImageIndex = 2;
try
{
using (NtToken token = NtProcess.Current.OpenToken())
{
if (Procedures.Count > 0)
{
m_Listview.ClearObjects();
m_Listview.SetObjects(Procedures);
m_Listview.RebuildColumns();
m_Listview.AutoResizeColumns();
}
}
}
catch (Exception ex)
{
Trace(TraceLoggerType.RpcProcedureList,
TraceEventType.Error,
"Unable to retrieve RPC procedure list: " + ex.Message);
}
}
public int GetCount()
{
if (m_Listview.Objects == null)
{
return 0;
}
return m_Listview.Objects.Cast<NdrProcedureDefinition>().Count();
}
private static string BuildTabTitle(string Name)
{
var name = Name;
if (name.Length > 30)
{
name = name.Substring(0, 15) + "..." + name.Substring(
name.Length - 15, 15);
}
return "Procedures for " + name;
}
}
}

226
TabPages/RpcServerList.cs Normal file
View File

@ -0,0 +1,226 @@
//
// Copyright (c) 2022-present, Trail of Bits, Inc.
// All rights reserved.
//
// This source code is licensed in accordance with the terms specified in
// the LICENSE file found in the root directory of this source tree.
//
using BrightIdeasSoftware;
using NtApiDotNet;
using NtApiDotNet.Win32;
using RpcInvestigator.TabPages;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Windows.Forms;
namespace RpcInvestigator
{
using static TraceLogger;
public class RpcServerList : TabPage
{
public FastObjectListView m_Listview;
private TabManager m_Manager;
public RpcServerList(TabManager Manager)
{
m_Manager = Manager;
m_Listview = new FastObjectListView();
Random random = new Random();
string rand = random.Next().ToString();
m_Listview.OwnerDraw = true;
m_Listview.BorderStyle = BorderStyle.FixedSingle;
m_Listview.CellEditUseWholeCell = false;
m_Listview.Cursor = Cursors.Default;
m_Listview.Dock = DockStyle.Fill;
m_Listview.FullRowSelect = true;
m_Listview.GridLines = true;
m_Listview.HideSelection = false;
m_Listview.Location = new Point(0, 0);
m_Listview.Margin = new Padding(5);
m_Listview.Name = "RpcServerListview" + rand;
m_Listview.UseAlternatingBackColors = true;
m_Listview.AlternateRowBackColor = Color.LightBlue;
m_Listview.UseCompatibleStateImageBehavior = false;
m_Listview.View = View.Details;
m_Listview.VirtualMode = true;
m_Listview.ShowGroups = false;
m_Listview.Alignment = ListViewAlignment.Left;
Generator.GenerateColumns(m_Listview, typeof(RpcServer), true);
foreach (var column in m_Listview.AllColumns)
{
if (column.Name.ToLower() == "procedures" ||
column.Name.ToLower() == "complextypes" ||
column.Name.ToLower() == "name" ||
column.Name.ToLower() == "offset" ||
column.Name.ToLower() == "endpoints")
{
column.IsVisible = false;
}
column.MaximumWidth = -1;
}
//
// When a listview row is double-clicked, a new tab will open with procedures
// for the selected interface on the given RPC server. When a row is right-clicked,
// a context menu shows.
//
m_Listview.DoubleClick += new EventHandler((object obj, EventArgs e2) =>
{
if (m_Listview.SelectedObjects == null ||
m_Listview.SelectedObjects.Count == 0)
{
return;
}
var selectedRow = m_Listview.SelectedObjects.Cast<RpcServer>().ToList()[0];
m_Manager.LoadRpcProceduresTab(selectedRow.Name, selectedRow.Procedures.ToList());
});
m_Listview.CellRightClick += RightClickHandler;
Controls.Add(m_Listview);
}
public void Build (
List<string> FileNames,
Settings Settings,
RpcLibrary Library
)
{
Text = BuildTabTitle(FileNames);
ImageIndex = 0;
try
{
using (NtToken token = NtProcess.Current.OpenToken())
{
var allservers = new List<RpcServer>();
token.SetPrivilege(TokenPrivilegeValue.SeDebugPrivilege, PrivilegeAttributes.Enabled, true);
foreach (var filename in FileNames)
{
try
{
var servers = RpcServer.ParsePeFile(filename,
Settings.m_DbghelpPath, Settings.m_SymbolPath);
if (servers.Count() == 0)
{
Trace(TraceLoggerType.RpcServerList,
TraceEventType.Warning,
"No RPC servers in file '" + filename + "'");
continue;
}
allservers.AddRange(servers);
}
catch (Exception ex)
{
Trace(TraceLoggerType.RpcServerList,
TraceEventType.Error,
"Unable to retrieve RPC server list for file '" +
filename + "': " + ex.Message);
}
}
if (allservers.Count > 0)
{
m_Listview.ClearObjects();
m_Listview.SetObjects(allservers);
m_Listview.RebuildColumns();
m_Listview.AutoResizeColumns();
//
// Cache any new RPC servers from this binary.
//
if (!Library.Merge(allservers))
{
Trace(TraceLoggerType.RpcServerList,
TraceEventType.Error,
"Unable to merge new RPC servers to database.");
}
}
}
}
catch (Exception ex)
{
Trace(TraceLoggerType.RpcServerList,
TraceEventType.Error,
"Unable to retrieve RPC server list: " + ex.Message);
}
}
public int GetCount()
{
if (m_Listview.Objects == null)
{
return 0;
}
return m_Listview.Objects.Cast<RpcServer>().Count();
}
public List<RpcServer> GetAll()
{
if (m_Listview.Objects == null)
{
return new List<RpcServer>();
}
return m_Listview.Objects.Cast<RpcServer>().ToList();
}
private
void
RightClickHandler(
object Obj,
CellRightClickEventArgs Args
)
{
TabPages.ContextMenu.BuildRightClickMenu(Args, new List<ToolStripMenuItem>{
new ToolStripMenuItem("New Client", null, ContextMenuNewClient),
});
}
private void ContextMenuNewClient(object Sender, EventArgs Args)
{
object tag = ((ToolStripMenuItem)Sender).Tag;
if (tag == null)
{
return;
}
var args = (CellRightClickEventArgs)tag;
//
// RpcServer type comes from parsing RPC server information out of a
// binary (such as a DLL), so we have to scan for an ALPC port hosting
// the interface using the RPC endpoint mapper.
//
var server = args.Model as RpcServer;
var id = server.InterfaceId;
var version = server.InterfaceVersion;
var endpoint = RpcAlpcServerList.FindEndpoint(id, version);
if (endpoint == null)
{
MessageBox.Show("Unable to locate an endpoint for RPC server UUID " +
id.ToString() + ", version " + version.ToString());
return;
}
var clientWindow = new Client(server, endpoint);
clientWindow.ShowDialog();
}
private static string BuildTabTitle(List<string> FileNames)
{
if (FileNames.Count == 1)
{
var path = Path.GetDirectoryName(FileNames[0]);
string title = FileNames[0];
if (path.Length > 25)
{
title = path.Substring(0, 20) +
"...\\" + Path.GetFileName(FileNames[0]);
}
return title;
}
return "Multiple files";
}
}
}

157
TabPages/TabManager.cs Normal file
View File

@ -0,0 +1,157 @@
//
// Copyright (c) 2022-present, Trail of Bits, Inc.
// All rights reserved.
//
// This source code is licensed in accordance with the terms specified in
// the LICENSE file found in the root directory of this source tree.
//
using NtApiDotNet.Ndr;
using NtApiDotNet.Win32;
using System.Collections.Generic;
using System.Windows.Forms;
using System.Threading.Tasks;
namespace RpcInvestigator.TabPages
{
public class TabManager
{
private TabControl m_TabControl;
private Settings m_Settings;
private RpcLibrary m_Library;
private ToolStripProgressBar m_ProgressBar;
private ToolStripStatusLabel m_StatusLabel;
public TabManager (
TabControl Control,
Settings Settings,
RpcLibrary Library,
ToolStripProgressBar Bar,
ToolStripStatusLabel Label
)
{
m_TabControl = Control;
m_Settings = Settings;
m_Library = Library;
m_ProgressBar = Bar;
m_StatusLabel = Label;
}
public RpcAlpcServerList LoadRpcAlpcServersTab()
{
TabPage tab;
RpcAlpcServerList rpcAlpcTab;
if (!TabExists("RPC ALPC Servers by Process", out tab))
{
tab = new RpcAlpcServerList(this);
m_TabControl.TabPages.Add(tab);
}
rpcAlpcTab = tab as RpcAlpcServerList;
rpcAlpcTab.Build();
m_TabControl.SelectedTab = rpcAlpcTab;
m_StatusLabel.Text = "Discovered " + rpcAlpcTab.GetCount() + " RPC ALPC servers.";
return rpcAlpcTab;
}
public RpcServerList LoadRpcServersTab(List<string> FileNames)
{
RpcServerList rpcTab = new RpcServerList(this);
m_TabControl.TabPages.Add(rpcTab);
rpcTab.Build(FileNames, m_Settings, m_Library);
m_TabControl.SelectedTab = rpcTab;
m_StatusLabel.Text = "Discovered " + rpcTab.GetCount() + " RPC servers.";
return rpcTab;
}
public RpcEndpointList LoadRpcEndpointsTab(string ServerName, List<RpcEndpoint> Endpoints, string AlpcPortName)
{
TabPage tab;
RpcEndpointList endpointsTab;
if (!TabExists(ServerName, out tab))
{
tab = new RpcEndpointList(m_Library, this);
m_TabControl.TabPages.Add(tab);
}
endpointsTab = tab as RpcEndpointList;
endpointsTab.Build(ServerName, Endpoints);
m_TabControl.SelectedTab = endpointsTab;
m_StatusLabel.Text = "Discovered " + endpointsTab.GetCount() + " endpoints.";
return endpointsTab;
}
public RpcProcedureList LoadRpcProceduresTab(string Name, List<NdrProcedureDefinition> Procedures)
{
TabPage tab;
RpcProcedureList proceduresTab;
string name = Name + " Procedures";
if (!TabExists(name, out tab))
{
tab = new RpcProcedureList();
m_TabControl.TabPages.Add(tab);
}
proceduresTab = tab as RpcProcedureList;
proceduresTab.Build(Name, Procedures);
m_TabControl.SelectedTab = proceduresTab;
m_StatusLabel.Text = "Discovered " + proceduresTab.GetCount() +
" procedures.";
return proceduresTab;
}
public async Task<RpcLibraryServerList> LoadRpcLibraryServersTab(RpcLibraryFilter Filter)
{
TabPage tab;
RpcLibraryServerList libraryServersTab;
if (!TabExists("Servers", out tab))
{
tab = new RpcLibraryServerList(m_Library, this);
m_TabControl.TabPages.Add(tab);
}
libraryServersTab = tab as RpcLibraryServerList;
_ = await libraryServersTab.Build(Filter, m_ProgressBar, m_StatusLabel);
m_TabControl.SelectedTab = libraryServersTab;
m_StatusLabel.Text = "Loaded " + libraryServersTab.GetCount() +
" servers from the library";
return libraryServersTab;
}
public async Task<RpcLibraryProcedureList> LoadRpcLibraryProceduresTab(RpcLibraryFilter Filter)
{
TabPage tab;
RpcLibraryProcedureList libraryProceduresTab;
if (!TabExists("Procedures", out tab))
{
tab = new RpcLibraryProcedureList(m_Library);
m_TabControl.TabPages.Add(tab);
}
libraryProceduresTab = tab as RpcLibraryProcedureList;
_ = await libraryProceduresTab.Build(Filter, m_ProgressBar, m_StatusLabel);
m_TabControl.SelectedTab = libraryProceduresTab;
m_StatusLabel.Text = "Loaded " + libraryProceduresTab.GetCount() +
" procedures from the library";
return libraryProceduresTab;
}
public bool TabExists(string Name, out TabPage Match)
{
Match = null;
foreach (var tab in m_TabControl.TabPages)
{
var tabPage = tab as TabPage;
if (tabPage.Name == Name)
{
Match = tabPage;
return true;
}
}
return false;
}
public bool IsLibraryEmpty()
{
return m_Library.GetServerCount() == 0;
}
}
}

92
TraceLogger.cs Normal file
View File

@ -0,0 +1,92 @@
//
// Copyright (c) 2022-present, Trail of Bits, Inc.
// All rights reserved.
//
// This source code is licensed in accordance with the terms specified in
// the LICENSE file found in the root directory of this source tree.
//
using System.Diagnostics;
using System.IO;
namespace RpcInvestigator
{
public static class TraceLogger
{
private static readonly string m_TraceFileDir = Path.Combine(
new string[] { Settings.m_WorkingDir, "Logs\\" });
private static string m_Location = Path.Combine(new string[] { m_TraceFileDir,
System.DateTime.Now.ToString("yyyy-MM-dd-HHmmss") +
".txt" });
private static TextWriterTraceListener m_TraceListener =
new TextWriterTraceListener(m_Location, "RpcInvestigatorListener");
private static SourceSwitch m_Switch =
new SourceSwitch("RpcInvestigatorSwitch", "Verbose");
private static TraceSource[] Sources = {
new TraceSource("Settings", SourceLevels.Verbose),
new TraceSource("RpcLibrary", SourceLevels.Verbose),
new TraceSource("TaskWorker", SourceLevels.Verbose),
new TraceSource("ServicesWindow", SourceLevels.Verbose),
new TraceSource("RpcLibraryServerList", SourceLevels.Verbose),
new TraceSource("RpcLibraryProcedureList", SourceLevels.Verbose),
new TraceSource("RpcProcedureList", SourceLevels.Verbose),
new TraceSource("RpcEndpointList", SourceLevels.Verbose),
new TraceSource("RpcServerList", SourceLevels.Verbose),
new TraceSource("RpcAlpcServerList", SourceLevels.Verbose),
new TraceSource("RpcSniffer", SourceLevels.Verbose),
new TraceSource("RealTimeTrace", SourceLevels.Verbose),
new TraceSource("FileTrace", SourceLevels.Verbose),
new TraceSource("EtwEventParser", SourceLevels.Verbose),
new TraceSource("EtwProviderParser", SourceLevels.Verbose),
new TraceSource("SddlParser", SourceLevels.Verbose),
};
public enum TraceLoggerType
{
Settings,
RpcLibrary,
TaskWorker,
ServicesWindow,
RpcLibraryServerList,
RpcLibraryProcedureList,
RpcProcedureList,
RpcEndpointList,
RpcServerList,
RpcAlpcServerList,
RpcSniffer,
RealTimeTrace,
FileTrace,
EtwEventParser,
EtwProviderParser,
SddlParser,
Max
}
public static void Initialize()
{
System.Diagnostics.Trace.AutoFlush = true;
foreach (var source in Sources)
{
source.Listeners.Add(m_TraceListener);
source.Switch = m_Switch;
}
}
public static void SetLevel(SourceLevels Level)
{
m_Switch.Level = Level;
}
public static void Trace(TraceLoggerType Type, TraceEventType EventType, string Message)
{
if (Type >= TraceLoggerType.Max)
{
throw new System.Exception("Invalid logger type");
}
//
// Event ID is not used.
//
Sources[(int)Type].TraceEvent(EventType, 1, Message);
}
}
}

68
UnitTests/Environment.cs Normal file
View File

@ -0,0 +1,68 @@
//
// Copyright (c) 2022-present, Trail of Bits, Inc.
// All rights reserved.
//
// This source code is licensed in accordance with the terms specified in
// the LICENSE file found in the root directory of this source tree.
//
using Microsoft.VisualStudio.TestTools.UnitTesting;
using RpcInvestigator;
using System;
using System.IO;
namespace UnitTests
{
internal class Environment
{
public Settings m_Settings;
public RpcLibrary m_Library;
public Environment()
{
}
public void Initialize()
{
TraceLogger.Initialize();
try
{
m_Settings = Settings.LoadDefault();
//
// TODO: This is hard-coded to match our github workflow action that installs
// the debugging tools from an MSI stored in Digital Ocean. This sucks.
//
m_Settings.m_DbghelpPath = "C:\\Program Files (x86)\\Windows Kits\\10\\bin\\10.0.22621.0\\x64\\dbghelp.dll";
m_Settings.m_SymbolPath = @"srv*c:\\symbols*https://msdl.microsoft.com/download/symbols";
}
catch (Exception ex)
{
Assert.Fail("Exception loading default settings: " + ex.Message);
}
Assert.IsTrue(File.Exists(m_Settings.m_DbghelpPath));
try
{
//
// Important: each instance of Environment must setup a database unique
// to the caller, as the database is not meant to be thread-safe and
// tests can run in parallel.
//
m_Library = new RpcLibrary(Path.GetRandomFileName());
m_Library.Load();
//
// Wipe any existing entries from a prior test.
//
m_Library.Clear();
m_Library.Save();
}
catch (Exception ex)
{
Assert.Fail("Exception loading default library: " + ex.Message);
}
Assert.AreEqual(0, m_Library.GetServerCount());
}
}
}

181
UnitTests/EtwTests.cs Normal file
View File

@ -0,0 +1,181 @@
//
// Copyright (c) 2022-present, Trail of Bits, Inc.
// All rights reserved.
//
// This source code is licensed in accordance with the terms specified in
// the LICENSE file found in the root directory of this source tree.
//
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Threading.Tasks;
using System.Runtime.InteropServices;
using RpcInvestigator.Util;
using System.IO;
namespace UnitTests
{
using static NativeTraceConsumer;
using static NativeTraceControl;
[TestClass]
public class EtwTests
{
private readonly Guid s_RpcEtwGuid =
new Guid("6ad52b32-d609-4be9-ae07-ce8dae937e39");
private readonly int s_NumEvents = 1000;
[DataTestMethod]
[DataRow(true)]
[DataRow(false)]
public async Task RealTimeEtwTraceTest(bool EnableDebugEvents)
{
var env = new Environment();
env.Initialize();
int eventsConsumed = 0;
await Task.Run(() =>
{
//
// This trace will automatically terminate after a set number
// of ETW events have been successfully consumed/parsed.
//
using (var trace = new EtwRealTimeTrace(
"RPC Investigator Unit Test Real-Time Tracing",
s_RpcEtwGuid,
EnableDebugEvents))
using (var parserBuffers = new EtwEventParserBuffers())
{
try
{
trace.Start();
//
// Begin consuming events. This is a blocking call.
//
trace.Consume(new EventRecordCallback((Event) =>
{
var evt = (EVENT_RECORD)Marshal.PtrToStructure(
Event, typeof(EVENT_RECORD));
var parser = new EtwEventParser(
evt,
parserBuffers,
trace.GetPerfFreq());
try
{
var result = parser.Parse();
Assert.IsNotNull(result);
}
catch (Exception ex)
{
Assert.Fail("Unable to parse event: " + ex.Message);
}
eventsConsumed++;
}),
new BufferCallback((LogFile) =>
{
var logfile = new EVENT_TRACE_LOGFILE();
try
{
logfile = (EVENT_TRACE_LOGFILE)
Marshal.PtrToStructure(LogFile, typeof(EVENT_TRACE_LOGFILE));
}
catch (Exception ex)
{
Assert.Fail("Unable to cast EVENT_TRACE_LOGFILE: " + ex.Message);
}
if (eventsConsumed >= s_NumEvents)
{
return 0;
}
return 1;
}));
}
catch (Exception ex)
{
Assert.Fail("An exception occurred when consuming events: " + ex.Message);
}
}
});
}
[TestMethod]
[DeploymentItem(@"..\..\data\trace_files\ms-rpc-capture-arrays.etl")]
public void FileEtwTraceTest()
{
var env = new Environment();
env.Initialize();
int eventsConsumed = 0;
//
// This trace will automatically terminate after a set number
// of ETW events have been successfully consumed/parsed.
//
var current = Directory.GetCurrentDirectory();
var target = Path.Combine(current, "ms-rpc-capture-arrays.etl");
using (var trace = new EtwFileTrace(target))
using (var parserBuffers = new EtwEventParserBuffers())
{
try
{
//
// Begin consuming events. This is a blocking call.
//
trace.Consume(new EventRecordCallback((Event) =>
{
var evt = (EVENT_RECORD)Marshal.PtrToStructure(
Event, typeof(EVENT_RECORD));
if (!evt.EventHeader.ProviderId.Equals(s_RpcEtwGuid))
{
//
// Skip events from other providers, because it might not
// be a builtin provider, in which case we'd need to go
// find the right manifest and that is overly complex for
// this unit test.
//
return;
}
var parser = new EtwEventParser(
evt,
parserBuffers,
trace.GetPerfFreq());
try
{
var result = parser.Parse();
Assert.IsNotNull(result, "Failed to parse the event");
}
catch (Exception ex)
{
Assert.Fail("Unable to parse event: " + ex.Message);
}
eventsConsumed++;
}),
new BufferCallback((LogFile) =>
{
var logfile = new EVENT_TRACE_LOGFILE();
try
{
logfile = (EVENT_TRACE_LOGFILE)
Marshal.PtrToStructure(LogFile, typeof(EVENT_TRACE_LOGFILE));
}
catch (Exception ex)
{
Assert.Fail("Unable to cast EVENT_TRACE_LOGFILE: " + ex.Message);
}
if (eventsConsumed >= s_NumEvents)
{
return 0;
}
return 1;
}));
}
catch (Exception ex)
{
Assert.Fail("An exception occurred when consuming events: " + ex.Message);
}
}
}
}
}

View File

@ -0,0 +1,26 @@
//
// Copyright (c) 2022-present, Trail of Bits, Inc.
// All rights reserved.
//
// This source code is licensed in accordance with the terms specified in
// the LICENSE file found in the root directory of this source tree.
//
using System.Reflection;
using System.Runtime.InteropServices;
[assembly: AssemblyTitle("RPC Investigator UnitTests")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("Trail of Bits")]
[assembly: AssemblyProduct("RPC Investigator")]
[assembly: AssemblyCopyright("Copyright © 2022")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
[assembly: ComVisible(false)]
[assembly: Guid("43cd7aef-cd26-4e7c-9428-1f8bfe40b10d")]
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

View File

@ -0,0 +1,26 @@
//
// Copyright (c) 2022-present, Trail of Bits, Inc.
// All rights reserved.
//
// This source code is licensed in accordance with the terms specified in
// the LICENSE file found in the root directory of this source tree.
//
using Microsoft.VisualStudio.TestTools.UnitTesting;
using RpcInvestigator;
namespace UnitTests
{
[TestClass]
public class RpcAlpcServerListTests
{
[TestMethod]
public void AtLeastOneALPC()
{
var env = new Environment();
env.Initialize();
var list = new RpcAlpcServerList(null);
list.Build();
Assert.IsTrue(list.GetCount() > 0);
}
}
}

View File

@ -0,0 +1,245 @@
//
// Copyright (c) 2022-present, Trail of Bits, Inc.
// All rights reserved.
//
// This source code is licensed in accordance with the terms specified in
// the LICENSE file found in the root directory of this source tree.
//
using Microsoft.VisualStudio.TestTools.UnitTesting;
using RpcInvestigator;
using System;
using System.Linq;
using System.Threading.Tasks;
namespace UnitTests
{
[TestClass]
public class RpcLibraryTests
{
public TestContext m_TestContext { get; set; }
private static int s_MaxServers = 25;
[TestMethod]
public void LoadLibrary()
{
var env = new Environment();
env.Initialize();
Assert.AreEqual(0, env.m_Library.GetServerCount());
}
[TestMethod]
public async Task SaveLibrary()
{
var env = new Environment();
env.Initialize();
Assert.AreEqual(0, env.m_Library.GetServerCount());
_ = await env.m_Library.Refresh(
env.m_Settings, null, null, null, s_MaxServers);
Assert.IsTrue(env.m_Library.GetServerCount() > 0);
try
{
env.m_Library.Save();
}
catch (Exception ex)
{
Assert.Fail("Save threw an exception: " + ex.Message);
}
}
[TestMethod]
public async Task BuildLibrary()
{
//
// This unit test covers building the library as well as adding
// individual RPC servers, since that class cannot be instantiated.
//
var env = new Environment();
env.Initialize();
Assert.AreEqual(0, env.m_Library.GetServerCount());
_ = await env.m_Library.Refresh(
env.m_Settings, null, null, null, s_MaxServers);
Assert.IsTrue(env.m_Library.GetServerCount() > 0);
}
[TestMethod]
public async Task ClearLibrary()
{
var env = new Environment();
env.Initialize();
Assert.AreEqual(0, env.m_Library.GetServerCount());
_ = await env.m_Library.Refresh(
env.m_Settings, null, null, null, s_MaxServers);
Assert.IsTrue(env.m_Library.GetServerCount() > 0);
env.m_Library.Clear();
Assert.AreEqual(0, env.m_Library.GetServerCount());
}
[DataTestMethod]
public async Task GetSingleServerFromLibrary()
{
var env = new Environment();
env.Initialize();
Assert.AreEqual(0, env.m_Library.GetServerCount());
_ = await env.m_Library.Refresh(
env.m_Settings, null, null, null, s_MaxServers);
//
// Pick a random server and get it from the library.
//
var servers = env.m_Library.GetAllServers();
Assert.IsTrue(servers.Count > 0);
Random r = new Random();
var server = servers[r.Next(servers.Count)];
Assert.IsNotNull(env.m_Library.Get(
server.InterfaceId, server.InterfaceVersion));
}
[DataTestMethod]
public async Task GetMultipleServersFromLibrary()
{
var env = new Environment();
env.Initialize();
Assert.AreEqual(0, env.m_Library.GetServerCount());
_ = await env.m_Library.Refresh(
env.m_Settings, null, null, null, s_MaxServers);
//
// Pick a random server with multiple versions and get them from the library.
//
var servers = env.m_Library.GetServersWithMultipleVersions();
Assert.IsTrue(servers.Count > 0);
Random r = new Random();
var server = servers.ElementAt(r.Next(servers.Count));
Assert.IsNotNull(env.m_Library.Get(server.Key));
}
[DataTestMethod]
public async Task RemoveSingleServerFromLibrary()
{
var env = new Environment();
env.Initialize();
Assert.AreEqual(0, env.m_Library.GetServerCount());
_ = await env.m_Library.Refresh(
env.m_Settings, null, null, null, s_MaxServers);
//
// Pick a random server and remove it from the library.
//
var servers = env.m_Library.GetAllServers();
Assert.IsTrue(servers.Count > 0);
Random r = new Random();
var server = servers[r.Next(servers.Count)];
Assert.IsTrue(env.m_Library.Remove(
server.InterfaceId, server.InterfaceVersion));
Assert.IsNull(env.m_Library.Get(
server.InterfaceId, server.InterfaceVersion));
}
[DataTestMethod]
public async Task RemoveMultipleServersFromLibrary()
{
var env = new Environment();
env.Initialize();
Assert.AreEqual(0, env.m_Library.GetServerCount());
_ = await env.m_Library.Refresh(
env.m_Settings, null, null, null, s_MaxServers);
//
// Pick a random server with multiple versions and remove them from the library.
//
var servers = env.m_Library.GetServersWithMultipleVersions();
Assert.IsTrue(servers.Count > 0);
Random r = new Random();
var server = servers.ElementAt(r.Next(servers.Count));
Assert.IsTrue(env.m_Library.Remove(server.Key));
Assert.IsNull(env.m_Library.Get(server.Key));
}
[DataTestMethod]
public async Task RemoveServerFromLibraryExpectFailure()
{
var env = new Environment();
env.Initialize();
Assert.AreEqual(0, env.m_Library.GetServerCount());
_ = await env.m_Library.Refresh(
env.m_Settings, null, null, null, s_MaxServers);
Assert.IsFalse(env.m_Library.Remove(Guid.Empty));
}
[DataTestMethod]
public async Task AddServerToLibraryExpectFailure()
{
var env = new Environment();
env.Initialize();
Assert.AreEqual(0, env.m_Library.GetServerCount());
_ = await env.m_Library.Refresh(
env.m_Settings, null, null, null, s_MaxServers);
//
// Pick a random server and try to add it to the library.
//
var servers = env.m_Library.GetAllServers();
Assert.IsTrue(servers.Count > 0);
Random r = new Random();
var server = servers[r.Next(servers.Count)];
Assert.IsFalse(env.m_Library.Add(server));
}
[DataTestMethod]
public async Task MergeServersIntoLibraryExpectMultiple()
{
var env = new Environment();
env.Initialize();
Assert.AreEqual(0, env.m_Library.GetServerCount());
_ = await env.m_Library.Refresh(
env.m_Settings, null, null, null, s_MaxServers);
//
// Pick a random range of servers, remove them from the library, then merge back.
//
var servers = env.m_Library.GetAllServers();
Assert.IsTrue(servers.Count > 0);
Random r = new Random();
int start = r.Next(servers.Count - 1);
int count = r.Next(servers.Count - start);
var subset = servers.Skip(start).Take(count).ToList();
subset.ForEach(server =>
{
Assert.IsTrue(env.m_Library.Remove(
server.InterfaceId, server.InterfaceVersion));
});
Assert.IsTrue(env.m_Library.Merge(subset));
subset.ForEach(server =>
{
Assert.IsNotNull(env.m_Library.Get(
server.InterfaceId, server.InterfaceVersion));
});
}
[DataTestMethod]
public async Task MergeServersIntoLibraryExpectNone()
{
var env = new Environment();
env.Initialize();
Assert.AreEqual(0, env.m_Library.GetServerCount());
_ = await env.m_Library.Refresh(
env.m_Settings, null, null, null, s_MaxServers);
//
// Merging the current list should result in no new servers.
//
var servers = env.m_Library.GetAllServers();
Assert.IsTrue(servers.Count > 0);
int before = env.m_Library.GetServerCount();
Assert.IsTrue(env.m_Library.Merge(servers));
int after = env.m_Library.GetServerCount();
Assert.AreEqual(before, after);
}
[DataTestMethod]
public async Task FindServerByKeywordInLibrary()
{
var env = new Environment();
env.Initialize();
Assert.AreEqual(0, env.m_Library.GetServerCount());
_ = await env.m_Library.Refresh(
env.m_Settings, null, null, null, s_MaxServers);
var servers = env.m_Library.Find(new RpcLibraryFilter() { Keyword = "lsass" });
Assert.IsTrue(servers.Count > 0);
}
}
}

View File

@ -0,0 +1,49 @@
//
// Copyright (c) 2022-present, Trail of Bits, Inc.
// All rights reserved.
//
// This source code is licensed in accordance with the terms specified in
// the LICENSE file found in the root directory of this source tree.
//
using Microsoft.VisualStudio.TestTools.UnitTesting;
using RpcInvestigator;
using System;
using System.IO;
using System.Collections.Generic;
namespace UnitTests
{
[TestClass]
public class RpcServerListTests
{
[TestMethod]
public void AtLeastOneRpcServer()
{
var env = new Environment();
env.Initialize();
var source = Path.Combine(System.Environment.SystemDirectory, "samsrv.dll");
var dest = Path.GetTempFileName();
try
{
File.Copy(source, dest, true);
}
catch (Exception ex)
{
Assert.Fail("Unable to copy '" + source + "' to '" + dest + "': " + ex.Message);
}
var list = new RpcServerList(null);
list.Build(new List<string>() { dest }, env.m_Settings, env.m_Library);
Assert.IsTrue(list.GetCount() > 0);
//
// Check that it was cached in the library
//
var servers = list.GetAll();
Assert.IsTrue(servers.Count > 0);
servers.ForEach(server =>
{
Assert.IsNotNull(env.m_Library.Get(server.InterfaceId));
});
}
}
}

View File

@ -0,0 +1,85 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\packages\MSTest.TestAdapter.2.2.7\build\net45\MSTest.TestAdapter.props" Condition="Exists('..\packages\MSTest.TestAdapter.2.2.7\build\net45\MSTest.TestAdapter.props')" />
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{43CD7AEF-CD26-4E7C-9428-1F8BFE40B10D}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>UnitTests</RootNamespace>
<AssemblyName>UnitTests</AssemblyName>
<TargetFrameworkVersion>v4.8.1</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<ProjectTypeGuids>{3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">15.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
<ReferencePath>$(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages</ReferencePath>
<IsCodedUITest>False</IsCodedUITest>
<TestProjectType>UnitTest</TestProjectType>
<NuGetPackageImportStamp>
</NuGetPackageImportStamp>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="Microsoft.VisualStudio.TestPlatform.TestFramework, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\MSTest.TestFramework.2.2.7\lib\net45\Microsoft.VisualStudio.TestPlatform.TestFramework.dll</HintPath>
</Reference>
<Reference Include="Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\MSTest.TestFramework.2.2.7\lib\net45\Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.dll</HintPath>
</Reference>
<Reference Include="NtApiDotNet, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\NtApiDotNet.1.1.33\lib\net461\NtApiDotNet.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.DirectoryServices" />
<Reference Include="System.Security" />
<Reference Include="System.Windows.Forms" />
</ItemGroup>
<ItemGroup>
<Compile Include="Environment.cs" />
<Compile Include="EtwTests.cs" />
<Compile Include="RpcServerListTests.cs" />
<Compile Include="RpcLibraryTests.cs" />
<Compile Include="RpcAlpcServerListTests.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<None Include="app.config" />
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\RpcInvestigator.csproj">
<Project>{de141fc8-f9a3-41d2-a0a9-fd6a6ab310a3}</Project>
<Name>RpcInvestigator</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets" Condition="Exists('$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets')" />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\packages\MSTest.TestAdapter.2.2.7\build\net45\MSTest.TestAdapter.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\MSTest.TestAdapter.2.2.7\build\net45\MSTest.TestAdapter.props'))" />
<Error Condition="!Exists('..\packages\MSTest.TestAdapter.2.2.7\build\net45\MSTest.TestAdapter.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\MSTest.TestAdapter.2.2.7\build\net45\MSTest.TestAdapter.targets'))" />
</Target>
<Import Project="..\packages\MSTest.TestAdapter.2.2.7\build\net45\MSTest.TestAdapter.targets" Condition="Exists('..\packages\MSTest.TestAdapter.2.2.7\build\net45\MSTest.TestAdapter.targets')" />
</Project>

11
UnitTests/app.config Normal file
View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Runtime.CompilerServices.Unsafe" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-5.0.0.0" newVersion="5.0.0.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>

Binary file not shown.

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="MSTest.TestAdapter" version="2.2.7" targetFramework="net481" />
<package id="MSTest.TestFramework" version="2.2.7" targetFramework="net481" />
<package id="NtApiDotNet" version="1.1.33" targetFramework="net481" />
</packages>

1068
Util/EtwEventParser.cs Normal file

File diff suppressed because it is too large Load Diff

134
Util/EtwFileTrace.cs Normal file
View File

@ -0,0 +1,134 @@
//
// Copyright (c) 2022-present, Trail of Bits, Inc.
// All rights reserved.
//
// This source code is licensed in accordance with the terms specified in
// the LICENSE file found in the root directory of this source tree.
//
using NtApiDotNet;
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
namespace RpcInvestigator.Util
{
using static NativeTraceConsumer;
using static NativeTraceControl;
using static TraceLogger;
public class EtwFileTrace : IDisposable
{
private bool m_Disposed;
private readonly string m_EtlFileName;
private long m_PerfFreq;
public EtwFileTrace(string EtlFileName)
{
m_EtlFileName = EtlFileName;
m_Disposed = false;
}
~EtwFileTrace()
{
Dispose(false);
}
private void Dispose(bool disposing)
{
if (m_Disposed)
{
return;
}
Trace(TraceLoggerType.FileTrace,
TraceEventType.Information,
"Disposing FileTrace");
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
public void Consume(
EventRecordCallback EventCallback,
BufferCallback BufferCallback
)
{
var logfile = new EVENT_TRACE_LOGFILE()
{
LogFileName = m_EtlFileName,
EventCallback = EventCallback,
BufferCallback = BufferCallback,
ProcessTraceMode = ProcessTraceMode.EventRecord
};
var logFilePointer = Marshal.AllocHGlobal(Marshal.SizeOf(logfile));
Trace(TraceLoggerType.FileTrace,
TraceEventType.Information,
"Consuming events from ETL file " + m_EtlFileName);
Marshal.StructureToPtr(logfile, logFilePointer, false);
var handle = OpenTrace(logFilePointer);
//
// Marshal the structure back so we can get the PerfFreq
//
logfile = (EVENT_TRACE_LOGFILE)Marshal.PtrToStructure(
logFilePointer, typeof(EVENT_TRACE_LOGFILE));
Marshal.FreeHGlobal(logFilePointer);
if (handle == -1 || handle == 0)
{
var error = "OpenTrace() returned an invalid handle: 0x" +
Marshal.GetLastWin32Error().ToString("X");
Trace(TraceLoggerType.FileTrace,
TraceEventType.Error,
error);
throw new Exception(error);
}
Trace(TraceLoggerType.FileTrace,
TraceEventType.Information,
"Trace session successfully opened, processing trace..");
try
{
//
// Update PerfFreq so event's timestamps can be parsed.
//
m_PerfFreq = logfile.LogfileHeader.PerfFreq.QuadPart;
//
// Blocking call. The caller's BufferCallback must return false to
// unblock this routine.
//
var status = ProcessTrace(
new long[1] { handle },
1,
IntPtr.Zero,
IntPtr.Zero).MapDosErrorToStatus();
if (status != NtStatus.STATUS_SUCCESS)
{
var error = "ProcessTrace() failed: 0x" + status.ToString("X") +
", GetLastError: " + Marshal.GetLastWin32Error().ToString("X");
Trace(TraceLoggerType.FileTrace,
TraceEventType.Error,
error);
throw new Exception(error);
}
Trace(TraceLoggerType.FileTrace,
TraceEventType.Information,
"Trace processing successfully completed.");
}
finally
{
CloseTrace(handle);
}
}
public
long
GetPerfFreq()
{
return m_PerfFreq;
}
}
}

View File

@ -0,0 +1,694 @@
//
// Copyright (c) 2022-present, Trail of Bits, Inc.
// All rights reserved.
//
// This source code is licensed in accordance with the terms specified in
// the LICENSE file found in the root directory of this source tree.
//
using NtApiDotNet.Win32;
using NtApiDotNet;
using System;
using System.Runtime.InteropServices;
using static RpcInvestigator.Util.NativeTraceConsumer;
namespace RpcInvestigator.Util
{
public static class NativeTraceControl
{
#region Enums
public enum EventTraceLevel : byte
{
Critical = 1,
Error = 2,
Warning = 3,
Information = 4,
Verbose = 5,
}
public enum ControlCode : uint
{
Query = 0,
Stop = 1,
Update = 2,
Flush = 3
}
public enum EventControlCode : uint
{
DisableProvider = 0,
EnableProvider = 1,
CaptureState = 2,
}
public enum EnableTraceProperties : uint
{
Sid = 0x1,
TerminalServicesId = 0x2,
StackTrace = 0x4,
PsmKey = 0x8,
IgnoreKeyword0 = 0x10,
ProviderGroup = 0x20,
EnableKeyword0 = 0x40,
ProcessStartKey = 0x80,
EventKey = 0x100,
ExcludeInPrivate = 0x200,
}
[Flags]
public enum LogFileModeFlags : uint
{
None = 0,
Sequential = 0x00000001,
Circular = 0x00000002,
Append = 0x00000004,
NewFile = 0x00000008,
Preallocate = 0x00000020,
NonStoppable = 0x00000040,
Secure = 0x00000080,
RealTime = 0x00000100,
DelayOpen = 0x00000200,
Buffering = 0x00000400,
PrivateLogger = 0x00000800,
AddHeader = 0x00001000,
UseKBytesForSize = 0x00002000,
UseGlobalSequence = 0x00004000,
UseLocalSequence = 0x00008000,
Relog = 0x00010000,
PrivateInProc = 0x00020000,
Reserved = 0x00100000,
UsePagedMember = 0x01000000,
NoPerProcessorBuffering = 0x10000000,
SystemLogger = 0x02000000,
AddToTriageDump = 0x80000000,
StopOnHybridShutdown = 0x00400000,
PersistOnHybridShutdown = 0x00800000,
IndependentSession = 0x08000000,
Compressed = 0x04000000,
}
[Flags]
public enum WNodeFlags : uint
{
None = 0,
AllData = 0x00000001,
SingleInstance = 0x00000002,
SingleItem = 0x00000004,
EventItem = 0x00000008,
FixedInstanceSize = 0x00000010,
TooSmall = 0x00000020,
InstancesSame = 0x00000040,
StaticInstanceNames = 0x00000080,
Internal = 0x00000100,
UseTimestamp = 0x00000200,
PersistEvent = 0x00000400,
Reference = 0x00002000,
AnsiInstanceNames = 0x00004000,
MethodItem = 0x00008000,
PDOInstanceNames = 0x00010000,
TracedGuid = 0x00020000,
LogWNode = 0x00040000,
UseGuidPtr = 0x00080000,
UseMofPtr = 0x00100000,
NoHeader = 0x00200000,
SendDataBlock = 0x00400000,
VersionedProperties = 0x00800000,
}
[Flags]
public enum ProcessTraceMode : uint
{
RealTime = 0x00000100,
RawTimestamp = 0x00001000,
EventRecord = 0x10000000
}
public enum WNodeClientContext : uint
{
Default = 0,
QPC = 1,
SystemTime = 2,
CpuCycleCounter = 3
}
#endregion
#region Structs
[StructLayout(LayoutKind.Sequential)]
public class ENABLE_TRACE_PARAMETERS
{
public uint Version;
public EnableTraceProperties EnableProperty;
public uint ControlFlags;
public Guid SourceId;
public IntPtr EnableFilterDesc;
public uint FilterDescCount;
}
[StructLayout(LayoutKind.Sequential)]
public struct WNODE_HEADER
{
public uint BufferSize;
public uint ProviderId;
public ulong HistoricalContext;
public LargeIntegerStruct TimeStamp;
public Guid Guid;
public WNodeClientContext ClientContext;
public WNodeFlags Flags;
}
[StructLayout(LayoutKind.Sequential)]
public struct EVENT_TRACE_PROPERTIES
{
public WNODE_HEADER Wnode;
public uint BufferSize;
public uint MinimumBuffers;
public uint MaximumBuffers;
public uint MaximumFileSize;
public LogFileModeFlags LogFileMode;
public uint FlushTimer;
public uint EnableFlags;
public uint AgeLimit;
public uint NumberOfBuffers;
public uint FreeBuffers;
public uint EventsLost;
public uint BuffersWritten;
public uint LogBuffersLost;
public uint RealTimeBuffersLost;
public IntPtr LoggerThreadId;
public uint LogFileNameOffset;
public uint LoggerNameOffset;
public uint VersionNumber;
public uint FilterDescCount;
public IntPtr FilterDesc;
public ulong V2Options;
}
[StructLayout(LayoutKind.Sequential, Size = 0xac, CharSet = CharSet.Unicode)]
public struct TIME_ZONE_INFORMATION
{
public int bias;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
public string standardName;
[MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.U2, SizeConst = 8)]
public ushort[] standardDate;
public int standardBias;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
public string daylightName;
[MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.U2, SizeConst = 8)]
public ushort[] daylightDate;
public int daylightBias;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct TRACE_LOGFILE_HEADER
{
public uint BufferSize;
public uint Version;
public uint ProviderVersion;
public uint NumberOfProcessors;
public LargeIntegerStruct EndTime;
public uint TimerResolution;
public uint MaximumFileSize;
public uint LogFileMode;
public uint BuffersWritten;
public uint StartBuffers;
public uint PointerSize;
public uint EventsLost;
public uint CpuSpeedInMhz;
public string LoggerName;
public string LogFileName;
public TIME_ZONE_INFORMATION TimeZone;
public LargeIntegerStruct BootTime;
public LargeIntegerStruct PerfFreq;
public LargeIntegerStruct StartTime;
public uint Reserved;
public uint BuffersLost;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct EVENT_TRACE_LOGFILE
{
public string LogFileName;
public string LoggerName;
public long CurrentTime;
public uint BuffersRead;
public ProcessTraceMode ProcessTraceMode;
public EVENT_TRACE CurrentEvent;
public TRACE_LOGFILE_HEADER LogfileHeader;
public BufferCallback BufferCallback;
public uint BufferSize;
public uint Filled;
public uint EventsLost;
public EventRecordCallback EventCallback;
public uint IsKernelTrace;
public IntPtr Context;
}
#endregion
#region APIs
[DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern Win32Error StartTrace(
[In, Out] ref long SessionHandle,
[In] string SessionName,
[In, Out] IntPtr Properties // EVENT_TRACE_PROPERTIES
);
[DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern Win32Error ControlTrace(
[In] long SessionHandle,
[In] string SessionName,
[In, Out] IntPtr Properties, // EVENT_TRACE_PROPERTIES
[In] ControlCode ControlCode
);
[DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern long OpenTrace(
[In, Out] IntPtr LogFile // EVENT_TRACE_LOGFILE*
);
[DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
internal static extern Win32Error CloseTrace(
[In] long SessionHandle
);
[DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern Win32Error ProcessTrace(
[In] long[] handleArray,
[In] uint handleCount,
[In] IntPtr StartTime,
[In] IntPtr EndTime);
[DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern Win32Error EnableTraceEx2(
[In] long SessionHandle,
[In] ref Guid ProviderId,
[In] EventControlCode ControlCode,
[In] EventTraceLevel Level,
[In] ulong MatchAnyKeyword,
[In] ulong MatchAllKeyword,
[In] uint Timeout,
[In, Optional] IntPtr EnableParameters // ENABLE_TRACE_PARAMETERS
);
#endregion
}
public static class NativeTraceConsumer
{
[UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Unicode)]
public delegate void EventRecordCallback(
[In] IntPtr EventRecord
);
[UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Unicode)]
public delegate uint BufferCallback(
[In] IntPtr Logfile // EVENT_TRACE_LOGFILE
);
#region Enums
[Flags]
public enum EventHeaderFlags : ushort
{
ExtendedInfo = 0x01,
PrivateSession = 0x02,
StringOnly = 0x04,
TraceMessage = 0x08,
NoCpuTime = 0x10,
Is32BitHeader = 0x20,
Is64BitHeader = 0x40,
ClassicHeader = 0x100,
ProcessorIndex = 0x200
}
public enum EventHeaderExtendedDataType : ushort
{
RelatedActivityId = 0x0001,
Sid = 0x0002,
TerminalServicesId = 0x0003,
InstanceInfo = 0x0004,
StackTrace32 = 0x0005,
StackTrace64 = 0x0006,
PebsIndex = 0x0007,
PmcCounters = 0x0008,
PsmKey = 0x0009,
EventKey = 0x000A,
SchemaTl = 0x000B,
ProvTraits = 0x000C,
ProcessStartKey = 0x000D,
Max = 0x000E,
}
[Flags]
public enum EventHeaderPropertyFlags : ushort
{
Xml = 1,
ForwardedXml = 2,
LegacyEventLog = 3
}
[Flags]
public enum MAP_FLAGS
{
ValueMap = 1,
Bitmap = 2,
ManifestPatternMap = 4,
WbemValueMap = 8,
WbemBitmap = 16,
WbemFlag = 32,
WbemNoMap = 64
};
[Flags]
public enum PROPERTY_FLAGS
{
None = 0,
Struct = 0x1,
ParamLength = 0x2,
ParamCount = 0x4,
WbemXmlFragment = 0x8,
ParamFixedLength = 0x10,
ParamFixedCount = 0x20
}
public enum TdhInputType : ushort
{
Null,
UnicodeString,
AnsiString,
Int8,
UInt8,
Int16,
UInt16,
Int32,
UInt32,
Int64,
UInt64,
Float,
Double,
Boolean,
Binary,
GUID,
Pointer,
FILETIME,
SYSTEMTIME,
SID,
HexInt32,
HexInt64,
CountedUtf16String = 22,
CountedMbcsString = 23,
Struct = 24,
CountedString = 300,
CountedAnsiString,
ReversedCountedString,
ReversedCountedAnsiString,
NonNullTerminatedString,
NonNullTerminatedAnsiString,
UnicodeChar,
AnsiChar,
SizeT,
HexDump,
WbemSID
};
public enum TdhOutputType : ushort
{
Null = 0, // use TdhInputType
String = 1,
DateTime = 2,
Byte = 3,
UnsignedByte = 4,
Short = 5,
UnsignedShort = 6,
Integer = 7,
UnsignedInteger = 8,
Long = 9,
UnsignedLong = 10,
Float = 11,
Double = 12,
Boolean = 13,
Guid = 14,
HexBinary = 15,
HexInteger8 = 16,
HexInteger16 = 17,
HexInteger32 = 18,
HexInteger64 = 19,
Pid = 20,
Tid = 21,
Port = 22,
Ipv4 = 23,
Ipv6 = 24,
SocketAddress = 25,
CimDateTime = 26,
EtwTime = 27,
Xml = 28,
ErrorCode = 29,
Win32Error = 30,
Ntstatus = 31,
Hresult = 32,
CultureInsensitiveDatetime = 33,
Json = 34,
ReducedString = 300,
NoPrin = 301,
}
public enum EVENT_FIELD_TYPE {
KeywordInformation,
LevelInformation,
ChannelInformation,
TaskInformation,
OpcodeInformation,
Max
}
#endregion
#region Structs
[StructLayout(LayoutKind.Sequential)]
public struct EVENT_DESCRIPTOR
{
public ushort Id;
public byte Version;
public byte Channel;
public EventTraceLevel Level;
public byte Opcode;
public ushort Task;
public ulong Keyword;
}
[StructLayout(LayoutKind.Sequential)]
public struct EVENT_DATA_DESCRIPTOR
{
public long Ptr;
public int Size;
public byte Type;
public byte Reserved1;
public ushort Reserved2;
}
[StructLayout(LayoutKind.Sequential)]
public struct EVENT_TRACE_HEADER
{
public ushort Size;
public ushort FieldTypeFlags;
public byte Type;
public EventTraceLevel Level;
public ushort Version;
public uint ThreadId;
public uint ProcessId;
public LargeIntegerStruct Timestamp;
public Guid Guid;
public ulong ProcessorTime;
}
[StructLayout(LayoutKind.Sequential)]
public struct EVENT_TRACE
{
public EVENT_TRACE_HEADER Header;
public uint InstanceId;
public uint ParentInstanceId;
public Guid ParentGuid;
public IntPtr MofData;
public uint MofLength;
public uint BufferContext;
}
[StructLayout(LayoutKind.Sequential)]
public struct EVENT_HEADER
{
public ushort Size;
public ushort HeaderType;
public EventHeaderFlags Flags;
public EventHeaderPropertyFlags EventProperty;
public uint ThreadId;
public uint ProcessId;
public long TimeStamp;
public Guid ProviderId;
public ushort Id;
public byte Version;
public byte Channel;
public EventTraceLevel Level;
public byte Opcode;
public ushort Task;
public ulong Keyword;
public uint KernelTime;
public uint UserTime;
public Guid ActivityId;
}
[StructLayout(LayoutKind.Sequential)]
public struct ETW_BUFFER_CONTEXT
{
public byte ProcessorNumber;
public byte Alignment;
public ushort LoggerId;
}
[StructLayout(LayoutKind.Sequential)]
public struct EVENT_HEADER_EXTENDED_DATA_ITEM
{
public ushort Reserved1;
public EventHeaderExtendedDataType ExtType;
public ushort Reserved2;
public ushort DataSize;
public ulong DataPtr;
};
[StructLayout(LayoutKind.Sequential)]
public struct EVENT_RECORD
{
public EVENT_HEADER EventHeader;
public ETW_BUFFER_CONTEXT BufferContext;
public ushort ExtendedDataCount;
public ushort UserDataLength;
public IntPtr ExtendedData; // array of EVENT_HEADER_EXTENDED_DATA_ITEM
public IntPtr UserData;
public IntPtr UserContext;
}
[StructLayout(LayoutKind.Sequential)]
public struct EVENT_MAP_ENTRY
{
public int NameOffset;
public int Value;
}
[StructLayout(LayoutKind.Sequential)]
public struct EVENT_MAP_INFO
{
public int NameOffset;
public MAP_FLAGS Flag;
public int EntryCount;
public int ValueType;
public EVENT_MAP_ENTRY MapEntryArray;
}
[StructLayout(LayoutKind.Sequential)]
public struct TRACE_EVENT_INFO
{
public Guid ProviderGuid;
public Guid EventGuid;
public EVENT_DESCRIPTOR EventDescriptor;
public int DecodingSource;
public int ProviderNameOffset;
public int LevelNameOffset;
public int ChannelNameOffset;
public int KeywordsNameOffset;
public int TaskNameOffset;
public int OpcodeNameOffset;
public int EventMessageOffset;
public int ProviderMessageOffset;
public int BinaryXmlOffset;
public int BinaryXmlSize;
public int EventNameOffset;
public int RelatedActivityIDNameOffset;
public int PropertyCount;
public int TopLevelPropertyCount;
public int Flags;
public IntPtr EventPropertyInfoArray; // EVENT_PROPERTY_INFO[1]
}
public struct EVENT_PROPERTY_INFO
{
public PROPERTY_FLAGS Flags;
public int NameOffset;
public TdhInputType InType;
public TdhOutputType OutType;
public int MapNameOffset;
public ushort StructStartIndex
{
get
{
return (ushort)InType;
}
}
public ushort NumOfStructMembers
{
get
{
return (ushort)OutType;
}
}
public ushort CountOrCountIndex;
public ushort LengthOrLengthIndex;
public int Reserved;
}
public struct PROVIDER_EVENT_INFO
{
public uint NumberOfEvents;
public uint Reserved;
public EVENT_DESCRIPTOR EventDescriptorsArray; // EVENT_DESCRIPTOR[ANYSIZE_ARRAY]
}
#endregion
#region APIs
[DllImport("tdh.dll", CharSet = CharSet.Unicode, SetLastError = true)]
internal static extern Win32Error TdhGetEventInformation(
[In] IntPtr Event, // EVENT_RECORD*
[In] uint TdhContextCount,
[In] IntPtr TdhContext,
[Out] IntPtr Buffer, // TRACE_EVENT_INFO*
[In, Out] ref uint BufferSize
);
[DllImport("tdh.dll", CharSet = CharSet.Unicode, SetLastError = true)]
internal static extern Win32Error TdhGetEventMapInformation(
[In] IntPtr Event, // EVENT_RECORD*
[In] string MapName,
[Out] IntPtr Buffer, // EVENT_MAP_INFO*
[In, Out] ref uint BufferSize
);
[DllImport("tdh.dll", CharSet = CharSet.Unicode, SetLastError = true)]
internal static extern Win32Error TdhFormatProperty(
[In] IntPtr TraceEventInfo, // TRACE_EVENT_INFO*
[In, Optional] IntPtr MapInfo, // EVENT_MAP_INFO*
[In] uint PointerSize,
[In] TdhInputType PropertyInType,
[In] TdhOutputType PropertyOutType,
[In] ushort PropertyLength,
[In] ushort UserDataLength,
[In] IntPtr UserData, // BYTE*
[In, Out] ref uint BufferSize,
[Out, Optional] IntPtr Buffer, // WCHAR*
[In, Out] ref ushort UserDataConsumed
);
[DllImport("tdh.dll", CharSet = CharSet.Unicode, SetLastError = true)]
internal static extern Win32Error TdhEnumerateManifestProviderEvents(
[In] ref Guid ProviderGuid,
[Out] IntPtr Buffer, // PROVIDER_EVENT_INFO*
[In, Out] ref uint BufferSize
);
[DllImport("tdh.dll", CharSet = CharSet.Unicode, SetLastError = true)]
internal static extern Win32Error TdhGetManifestEventInformation(
[In] ref Guid ProviderGuid,
[In] IntPtr EventDescriptor, // EVENT_DESCRIPTOR*
[Out] IntPtr Buffer, // TRACE_EVENT_INFO*
[In, Out] ref uint BufferSize
);
#endregion
}
}

331
Util/EtwProviderParser.cs Normal file
View File

@ -0,0 +1,331 @@
//
// Copyright (c) 2022-present, Trail of Bits, Inc.
// All rights reserved.
//
// This source code is licensed in accordance with the terms specified in
// the LICENSE file found in the root directory of this source tree.
//
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Linq;
using System.Text;
using NtApiDotNet.Win32;
namespace RpcInvestigator.Util
{
using static NativeTraceConsumer;
using static TraceLogger;
public static class EtwProviderParser
{
public
static
Dictionary<string, List<string>>
GetDistinctProviderEventInfo(Guid ProviderGuid)
{
uint bufferSize = 1024;
var buffer = Marshal.AllocHGlobal((int)bufferSize);
try
{
var status = TdhEnumerateManifestProviderEvents(
ref ProviderGuid,
buffer,
ref bufferSize);
if (status != Win32Error.SUCCESS)
{
var error = "TdhEnumerateManifestProviderEvents failed: " +
status.ToString("X");
Trace(TraceLoggerType.EtwProviderParser,
TraceEventType.Error,
error);
throw new Exception(error);
}
return ParseProviderEventArray(ProviderGuid, buffer);
}
catch (Exception ex)
{
Trace(TraceLoggerType.EtwProviderParser,
TraceEventType.Error,
"Exception in ParseProviderEventArray(): " +
ex.Message);
throw;
}
finally
{
Marshal.FreeHGlobal(buffer);
}
}
private
static
Dictionary<string, List<string>>
ParseProviderEventArray(Guid ProviderGuid, IntPtr Buffer)
{
var array = (PROVIDER_EVENT_INFO)Marshal.PtrToStructure(
Buffer, typeof(PROVIDER_EVENT_INFO));
int offset = Marshal.OffsetOf(typeof(PROVIDER_EVENT_INFO),
"EventDescriptorsArray").ToInt32();
IntPtr arrayStart = IntPtr.Add(Buffer, offset);
var events = MarshalHelper.MarshalArray<EVENT_DESCRIPTOR>(arrayStart,
array.NumberOfEvents);
var debugStr = new StringBuilder();
var results = new Dictionary<string, List<string>>()
{
//
// Default system-provided fields. Custom/userData fields
// added dynamically during parsing.
//
{"Task", new List<string>() },
{"Opcode", new List<string>() },
{"Level", new List<string>() },
{"Channel", new List<string>() },
{"Keywords", new List<string>() },
{"UserDataProperties", new List<string>()},
};
foreach (var evt in events)
{
//
// Important: we exclude events at the Debug level, as these
// add potentially hundreds of columns to the listview and
// are not useful anyway.
//
if (evt.Level > EventTraceLevel.Information)
{
continue;
}
uint descriptorBufferSize = 512;
uint traceEventInfoBufferSize = 1024 * 4000;
var eventDescriptorBuffer =
Marshal.AllocHGlobal((int)descriptorBufferSize);
var traceEventInfoBuffer =
Marshal.AllocHGlobal((int)traceEventInfoBufferSize);
try
{
Marshal.StructureToPtr(evt, eventDescriptorBuffer, false);
var status = TdhGetManifestEventInformation(
ref ProviderGuid,
eventDescriptorBuffer,
traceEventInfoBuffer,
ref traceEventInfoBufferSize);
if (status != Win32Error.SUCCESS)
{
Trace(TraceLoggerType.EtwProviderParser,
TraceEventType.Error,
"TdhGetManifestEventInformation failed: " +
status.ToString("X"));
return null;
}
ParseProviderManifestEvent(
traceEventInfoBuffer,
evt,
ref results,
ref debugStr);
}
catch (Exception ex)
{
Trace(TraceLoggerType.EtwProviderParser,
TraceEventType.Error,
"Exception in ParseProviderEventArray(): " +
ex.Message);
throw;
}
finally
{
Marshal.FreeHGlobal(eventDescriptorBuffer);
Marshal.FreeHGlobal(traceEventInfoBuffer);
//System.IO.File.WriteAllText(
// "EtwProviderDump.txt", debugStr.ToString());
}
}
return results;
}
private
static
void
ParseProviderManifestEvent(
IntPtr TraceEventInfoBuffer,
EVENT_DESCRIPTOR Descriptor,
ref Dictionary<string, List<string>> Results,
ref StringBuilder DebugStr
)
{
var traceEventInfo = (TRACE_EVENT_INFO)Marshal.PtrToStructure(
TraceEventInfoBuffer, typeof(TRACE_EVENT_INFO));
string str;
DebugStr.AppendLine("Event ID "+traceEventInfo.EventDescriptor.Id);
try
{
if (traceEventInfo.LevelNameOffset > 0)
{
str = Marshal.PtrToStringUni(IntPtr.Add(TraceEventInfoBuffer,
traceEventInfo.LevelNameOffset));
if (!Results["Level"].Contains(str,
StringComparer.CurrentCultureIgnoreCase))
{
Results["Level"].Add(str);
}
DebugStr.AppendLine(" Level: " + str);
}
else
{
str = Descriptor.Level.ToString();
if (!Results["Level"].Contains(str,
StringComparer.CurrentCultureIgnoreCase))
{
Results["Level"].Add(str);
}
DebugStr.AppendLine(" Level: " + str);
}
if (traceEventInfo.ChannelNameOffset > 0)
{
str = Marshal.PtrToStringUni(IntPtr.Add(TraceEventInfoBuffer,
traceEventInfo.ChannelNameOffset));
if (!Results["Channel"].Contains(str,
StringComparer.CurrentCultureIgnoreCase))
{
Results["Channel"].Add(str);
}
DebugStr.AppendLine(" Channel: " + str);
}
else
{
str = Descriptor.Channel.ToString();
if (!Results["Channel"].Contains(str,
StringComparer.CurrentCultureIgnoreCase))
{
Results["Channel"].Add(str);
}
DebugStr.AppendLine(" Channel: " + str);
}
if (traceEventInfo.KeywordsNameOffset > 0)
{
for (int offset = traceEventInfo.KeywordsNameOffset; ;)
{
str = Marshal.PtrToStringUni(
IntPtr.Add(TraceEventInfoBuffer, offset));
if (string.IsNullOrEmpty(str))
{
break;
}
if (!Results["Keywords"].Contains(str,
StringComparer.CurrentCultureIgnoreCase))
{
Results["Keywords"].Add(str);
}
DebugStr.AppendLine(" Keyword: " + str);
offset += Encoding.Unicode.GetByteCount(str) + 2;
}
}
if (traceEventInfo.TaskNameOffset > 0)
{
str = Marshal.PtrToStringUni(IntPtr.Add(TraceEventInfoBuffer,
traceEventInfo.TaskNameOffset));
if (!Results["Task"].Contains(str,
StringComparer.CurrentCultureIgnoreCase))
{
Results["Task"].Add(str);
}
DebugStr.AppendLine(" Task: " + str);
}
else
{
str = Descriptor.Task.ToString();
if (!Results["Task"].Contains(str,
StringComparer.CurrentCultureIgnoreCase))
{
Results["Task"].Add(str);
}
DebugStr.AppendLine(" Task: " + str);
}
if (traceEventInfo.OpcodeNameOffset > 0)
{
str = Marshal.PtrToStringUni(IntPtr.Add(TraceEventInfoBuffer,
traceEventInfo.OpcodeNameOffset));
if (!Results["Opcode"].Contains(str,
StringComparer.CurrentCultureIgnoreCase))
{
Results["Opcode"].Add(str);
}
DebugStr.AppendLine(" Opcode: " + str);
}
else
{
str = Descriptor.Opcode.ToString();
if (!Results["Opcode"].Contains(str,
StringComparer.CurrentCultureIgnoreCase))
{
Results["Opcode"].Add(str);
}
DebugStr.AppendLine(" Opcode: " + str);
}
ParseEventPropertyInfoArray(
TraceEventInfoBuffer,
(uint)traceEventInfo.PropertyCount,
ref Results,
ref DebugStr);
}
catch (Exception ex)
{
Trace(TraceLoggerType.EtwProviderParser,
TraceEventType.Error,
"An exception occurred inside " +
"ParseProviderManifestEvent: " + ex.Message);
}
}
private
static
void
ParseEventPropertyInfoArray(
IntPtr TraceEventInfoBuffer,
uint NumberOfProperties,
ref Dictionary<string, List<string>> Results,
ref StringBuilder DebugStr
)
{
int offset = Marshal.OffsetOf(typeof(TRACE_EVENT_INFO),
"EventPropertyInfoArray").ToInt32();
IntPtr arrayStart = IntPtr.Add(TraceEventInfoBuffer, offset);
var properties = MarshalHelper.MarshalArray<EVENT_PROPERTY_INFO>(arrayStart,
NumberOfProperties);
int currentPropertyIndex = 0;
DebugStr.AppendLine(" Properties:");
foreach (var property in properties)
{
if (property.NameOffset != 0)
{
var str = Marshal.PtrToStringUni(
IntPtr.Add(TraceEventInfoBuffer, property.NameOffset));
if (!Results["UserDataProperties"].Contains(str,
StringComparer.CurrentCultureIgnoreCase))
{
Results["UserDataProperties"].Add(str);
}
DebugStr.AppendLine(" " + str + " (" + property.InType + ")");
}
else
{
var str = "Unnamed_" + property.InType.ToString() +
"_" + currentPropertyIndex;
Results["UserDataProperties"].Add(str);
DebugStr.AppendLine(" " + str + " (" + property.InType + ")");
}
currentPropertyIndex++;
}
}
}
}

315
Util/EtwRealTimeTrace.cs Normal file
View File

@ -0,0 +1,315 @@
//
// Copyright (c) 2022-present, Trail of Bits, Inc.
// All rights reserved.
//
// This source code is licensed in accordance with the terms specified in
// the LICENSE file found in the root directory of this source tree.
//
using NtApiDotNet;
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Text;
namespace RpcInvestigator.Util
{
using static NativeTraceConsumer;
using static NativeTraceControl;
using static TraceLogger;
public class EtwRealTimeTrace : IDisposable
{
private readonly string m_SessionName;
private readonly Guid m_SessionGuid;
private bool m_Disposed;
private IntPtr m_PropertiesBuffer;
private Guid m_ProviderGuid;
private long m_SessionHandle;
private EventTraceLevel m_TraceLevel;
private long m_PerfFreq;
public EtwRealTimeTrace(
string SessionName,
Guid Provider,
bool EnableDebugEvents)
{
m_SessionName = SessionName;
m_SessionGuid = Guid.NewGuid();
m_Disposed = false;
m_PropertiesBuffer = IntPtr.Zero;
m_ProviderGuid = Provider;
m_SessionHandle = 0;
m_TraceLevel = EventTraceLevel.Information;
if (EnableDebugEvents)
{
//
// We don't need this to be more customizable for
// how simple RPC provider events are.
//
m_TraceLevel = EventTraceLevel.Verbose;
}
}
~EtwRealTimeTrace()
{
Dispose(false);
}
private void Dispose(bool disposing)
{
if (m_Disposed)
{
return;
}
Trace(TraceLoggerType.RealTimeTrace,
TraceEventType.Information,
"Disposing RealTimeTrace");
m_Disposed = true;
if (m_SessionHandle != 0 && m_SessionHandle != -1)
{
var result = EnableTraceEx2(m_SessionHandle,
ref m_ProviderGuid,
EventControlCode.DisableProvider,
m_TraceLevel,
0, 0, 0,
IntPtr.Zero).MapDosErrorToStatus();
if (result != NtStatus.STATUS_SUCCESS)
{
Trace(TraceLoggerType.RealTimeTrace,
TraceEventType.Error,
"RealTimeTrace dispose could not disable provider: " +
result.ToString("X"));
}
result = ControlTrace(
m_SessionHandle,
m_SessionName,
m_PropertiesBuffer,
ControlCode.Stop).MapDosErrorToStatus();
if (result != NtStatus.STATUS_SUCCESS)
{
Trace(TraceLoggerType.RealTimeTrace,
TraceEventType.Error,
"RealTimeTrace dispose could not stop trace: " +
result.ToString("X"));
}
}
if (m_PropertiesBuffer != IntPtr.Zero)
{
Marshal.FreeHGlobal(m_PropertiesBuffer);
}
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
public void Start()
{
NtStatus ntStatus;
Trace(TraceLoggerType.RealTimeTrace,
TraceEventType.Information,
"Starting RealTimeTrace " + m_SessionName + "...");
m_PropertiesBuffer = GenerateSessionProperties(); // freed in dtor
for (; ; )
{
var status = StartTrace(
ref m_SessionHandle, m_SessionName, m_PropertiesBuffer);
if (status == NtApiDotNet.Win32.Win32Error.ERROR_ALREADY_EXISTS)
{
Trace(TraceLoggerType.RealTimeTrace,
TraceEventType.Warning,
"A trace is already opened with instance " +
"name " + m_SessionName + ", attempting to stop it.");
//
// Orphaned session, possibly from a crash. Try to stop it.
//
ntStatus = ControlTrace(
0,
m_SessionName,
m_PropertiesBuffer,
ControlCode.Stop).MapDosErrorToStatus();
if (ntStatus != NtStatus.STATUS_SUCCESS)
{
var error = "Unable to stop orphaned trace session: " +
ntStatus.ToString("X");
Trace(TraceLoggerType.RealTimeTrace,
TraceEventType.Error,
error);
throw new Exception(error);
}
Trace(TraceLoggerType.RealTimeTrace,
TraceEventType.Information,
"Prior trace session stopped.");
continue;
}
else if (status != NtApiDotNet.Win32.Win32Error.SUCCESS ||
m_SessionHandle == 0 || m_SessionHandle == -1)
{
m_SessionHandle = 0;
var error = "StartTrace() failed: 0x" + status.ToString("X");
Trace(TraceLoggerType.RealTimeTrace,
TraceEventType.Error,
error);
throw new Exception(error);
}
break;
}
Trace(TraceLoggerType.RealTimeTrace,
TraceEventType.Information,
"Trace started. Enabling provider...");
var parameters = new ENABLE_TRACE_PARAMETERS
{
Version = 2,
EnableProperty = EnableTraceProperties.ProcessStartKey |
EnableTraceProperties.Sid,
};
var parametersPtr = Marshal.AllocHGlobal(Marshal.SizeOf(parameters));
Marshal.StructureToPtr(parameters, parametersPtr, false);
ntStatus = EnableTraceEx2(
m_SessionHandle,
ref m_ProviderGuid,
EventControlCode.EnableProvider,
m_TraceLevel,
0,
0,
0xffffffff,
parametersPtr).MapDosErrorToStatus();
Marshal.FreeHGlobal(parametersPtr);
if (ntStatus != NtStatus.STATUS_SUCCESS)
{
var error = "EnableTraceEx2() failed: 0x" + ntStatus.ToString("X");
Trace(TraceLoggerType.RealTimeTrace,
TraceEventType.Error,
error);
throw new Exception(error);
}
Trace(TraceLoggerType.RealTimeTrace,
TraceEventType.Information,
"Provider enabled successfully.");
}
public void Consume(
EventRecordCallback EventCallback,
BufferCallback BufferCallback
)
{
var logfile = new EVENT_TRACE_LOGFILE()
{
EventCallback = EventCallback,
BufferCallback = BufferCallback,
LoggerName = m_SessionName,
ProcessTraceMode = ProcessTraceMode.EventRecord |
ProcessTraceMode.RealTime
};
var logFilePointer = Marshal.AllocHGlobal(Marshal.SizeOf(logfile));
Trace(TraceLoggerType.RealTimeTrace,
TraceEventType.Information,
"Consuming events...");
Marshal.StructureToPtr(logfile, logFilePointer, false);
var handle = OpenTrace(logFilePointer);
//
// Marshal the structure back so we can get the PerfFreq
//
logfile = (EVENT_TRACE_LOGFILE)Marshal.PtrToStructure(
logFilePointer, typeof(EVENT_TRACE_LOGFILE));
Marshal.FreeHGlobal(logFilePointer);
if (handle == -1 || handle == 0)
{
var error = "OpenTrace() returned an invalid handle: 0x" +
Marshal.GetLastWin32Error().ToString("X");
Trace(TraceLoggerType.RealTimeTrace,
TraceEventType.Error,
error);
throw new Exception(error);
}
Trace(TraceLoggerType.RealTimeTrace,
TraceEventType.Information,
"Trace session successfully opened, processing trace..");
try
{
//
// Update PerfFreq so event's timestamps can be parsed.
//
m_PerfFreq = logfile.LogfileHeader.PerfFreq.QuadPart;
//
// Blocking call. The caller's BufferCallback must return false to
// unblock this routine.
//
var status = ProcessTrace(
new long[1] { handle },
1,
IntPtr.Zero,
IntPtr.Zero).MapDosErrorToStatus();
if (status != NtStatus.STATUS_SUCCESS)
{
var error = "ProcessTrace() failed: 0x" + status.ToString("X") +
", GetLastError: " + Marshal.GetLastWin32Error().ToString("X");
Trace(TraceLoggerType.RealTimeTrace,
TraceEventType.Error,
error);
throw new Exception(error);
}
Trace(TraceLoggerType.RealTimeTrace,
TraceEventType.Information,
"Trace processing successfully completed.");
}
finally
{
CloseTrace(handle);
}
}
public
long
GetPerfFreq()
{
return m_PerfFreq;
}
private
IntPtr
GenerateSessionProperties()
{
var loggerName = Encoding.Unicode.GetBytes(m_SessionName + "\0");
var loggerNameLocation = Marshal.SizeOf(typeof(EVENT_TRACE_PROPERTIES));
int total = loggerNameLocation + loggerName.Length;
var buffer = Marshal.AllocHGlobal(total);
var properties = new EVENT_TRACE_PROPERTIES();
properties.Wnode.BufferSize = (uint)total;
properties.Wnode.Flags =
WNodeFlags.TracedGuid | WNodeFlags.VersionedProperties;
properties.Wnode.ClientContext = WNodeClientContext.QPC;
properties.Wnode.Guid = m_SessionGuid;
properties.VersionNumber = 2;
properties.BufferSize = 64; // high freq should use 64kb - 128kb (this field in KB!)
properties.LogFileMode =
LogFileModeFlags.RealTime | LogFileModeFlags.Sequential;
properties.MinimumBuffers = 4;
properties.MaximumBuffers = 4;
properties.FilterDescCount = 0;
properties.FilterDesc = IntPtr.Zero;
properties.LogFileNameOffset = 0;
properties.LoggerNameOffset = (uint)loggerNameLocation;
Marshal.StructureToPtr(properties, buffer, false);
IntPtr dest = IntPtr.Add(buffer, loggerNameLocation);
Marshal.Copy(loggerName, 0, dest, loggerName.Length);
return buffer;
}
}
}

43
Util/Formatting.cs Normal file
View File

@ -0,0 +1,43 @@
//
// Copyright (c) 2022-present, Trail of Bits, Inc.
// All rights reserved.
//
// This source code is licensed in accordance with the terms specified in
// the LICENSE file found in the root directory of this source tree.
//
using System;
namespace RpcInvestigator.Util
{
internal static class Formatting
{
static readonly string[] SizeSuffixes = { "B", "KB", "MB", "GB" };
public static string InfoUnit(dynamic Value, int DecimalPlaces = 2)
{
if (!decimal.TryParse(Value.ToString(), out decimal value))
{
return "NaN!";
}
if (value > int.MaxValue)
{
return "NaN!";
}
else if (value < 0)
{
return "-" + InfoUnit(-value, DecimalPlaces);
}
int i = 0;
while (Math.Round(value, DecimalPlaces) >= 1000)
{
value /= 1024;
i++;
}
return string.Format("{0:n" + DecimalPlaces + "} {1}", value, SizeSuffixes[i]);
}
}
}

60
Util/MarshalHelper.cs Normal file
View File

@ -0,0 +1,60 @@
//
// Copyright (c) 2022-present, Trail of Bits, Inc.
// All rights reserved.
//
// This source code is licensed in accordance with the terms specified in
// the LICENSE file found in the root directory of this source tree.
//
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.InteropServices;
namespace RpcInvestigator.Util
{
public static class MarshalHelper
{
public
static
object
MarshalArbitraryType<T>(IntPtr Pointer)
{
try
{
return Convert.ChangeType(Marshal.PtrToStructure(Pointer, typeof(T)), typeof(T));
}
catch (Exception ex)
{
throw new Exception("Could not marshal pointer 0x" +
Pointer.ToInt64().ToString("X") +
" to destination type " + typeof(T).ToString() + ": " +
ex.Message);
}
}
public
static
List<T>
MarshalArray<T>(IntPtr ArrayAddress, uint ElementCount)
{
IntPtr entry = ArrayAddress;
var result = new List<T>();
for (int i = 0; i < ElementCount; i++)
{
Debug.Assert(entry != IntPtr.Zero);
result.Add((T)MarshalArbitraryType<T>(entry));
//
// Advance to the next structure.
//
unsafe
{
entry = (IntPtr)((byte*)entry.ToPointer() + Marshal.SizeOf(typeof(T)));
}
}
return result;
}
}
}

48
Util/ReflectionHelper.cs Normal file
View File

@ -0,0 +1,48 @@
//
// Copyright (c) 2022-present, Trail of Bits, Inc.
// All rights reserved.
//
// This source code is licensed in accordance with the terms specified in
// the LICENSE file found in the root directory of this source tree.
//
using BrightIdeasSoftware;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
namespace RpcInvestigator.Util
{
public static class ReflectionHelper
{
public
static
List<string>
GetOlvAttributes(Type T)
{
try
{
var properties = T.GetProperties(BindingFlags.Public | BindingFlags.Instance);
if (properties.Count() > 0)
{
var props = new List<string>();
foreach (var prop in properties)
{
if (prop.CustomAttributes.Count() != 1)
{
continue;
}
if (prop.CustomAttributes.ElementAt(0).AttributeType ==
typeof(OLVColumnAttribute))
{
props.Add(prop.Name);
}
}
return props;
}
}
catch (Exception) { }
return null;
}
}
}

129
Util/SddlParser.cs Normal file
View File

@ -0,0 +1,129 @@
//
// Copyright (c) 2022-present, Trail of Bits, Inc.
// All rights reserved.
//
// This source code is licensed in accordance with the terms specified in
// the LICENSE file found in the root directory of this source tree.
//
using NtApiDotNet;
using System;
using System.Text;
using System.Security.AccessControl;
using System.Security.Principal;
using static NtApiDotNet.NtSecurity;
using AceType = NtApiDotNet.AceType;
using AceFlags = NtApiDotNet.AceFlags;
using System.Runtime.InteropServices;
using System.Diagnostics;
namespace RpcInvestigator.Util
{
using static TraceLogger;
public static class SddlParser
{
private static string SidToString(SecurityIdentifier SidValue)
{
try
{
return SidValue.Translate(typeof(NTAccount)).Value;
}
catch
{
return null;
}
}
private static string AclToString(RawAcl Acl)
{
StringBuilder result = new StringBuilder();
if (Acl == null)
{
return " {none}";
}
foreach (var ace in Acl)
{
var aceData = new byte[ace.BinaryLength];
IntPtr acePointer = Marshal.AllocHGlobal(ace.BinaryLength);
IntPtr currentPointer = acePointer;
try
{
ace.GetBinaryForm(aceData, 0);
Marshal.Copy(aceData, 0, currentPointer, ace.BinaryLength);
var header = (ACE_HEADER)Marshal.PtrToStructure(
currentPointer, typeof(ACE_HEADER));
//
// What follows the header depends on the ACE type, but the
// access mask, which is the last part we need, is always
// directly after the header.
//
currentPointer = IntPtr.Add(
currentPointer, Marshal.SizeOf(typeof(ACE_HEADER)));
var accessMask = Marshal.ReadInt32(currentPointer);
currentPointer = IntPtr.Add(currentPointer, 4);
var type = (AceType)header.AceType;
if (IsObjectAceType(type))
{
//
// Skip 32 bytes (object type and inherited object type)
//
currentPointer = IntPtr.Add(currentPointer, 32);
}
var sid = new Sid(currentPointer);
var ntAce = new Ace((AceType)header.AceType,
(AceFlags)header.AceFlags,
accessMask,
sid);
result.Append(ntAce.ToString() + ", ");
}
catch (Exception ex)
{
Trace(TraceLoggerType.SddlParser,
TraceEventType.Error,
"Exception parsing SDDL string: " + ex.Message);
break;
}
finally
{
Marshal.FreeHGlobal(acePointer);
}
}
return result.ToString();
}
public static string Parse(string SddlString)
{
StringBuilder result = new StringBuilder();
RawSecurityDescriptor descriptor;
try
{
descriptor = new RawSecurityDescriptor(SddlString);
}
catch (Exception ex)
{
throw new Exception("Unable to create RawSecurityDescriptor from " +
"the provided SDDL string '" + SddlString + "': " + ex.Message);
}
result.AppendLine("Owner: " + SidToString(descriptor.Owner));
result.AppendLine("Group: " + SidToString(descriptor.Group));
result.Append("Discretionary ACL: ");
result.Append(AclToString(descriptor.DiscretionaryAcl));
result.AppendLine();
result.Append("System ACL: ");
result.Append(AclToString(descriptor.SystemAcl));
result.AppendLine();
return result.ToString();
}
}
[StructLayout(LayoutKind.Sequential)]
public struct ACE_HEADER
{
public byte AceType;
public byte AceFlags;
public ushort AceSize;
}
}

156
Util/TaskWorker.cs Normal file
View File

@ -0,0 +1,156 @@
//
// Copyright (c) 2022-present, Trail of Bits, Inc.
// All rights reserved.
//
// This source code is licensed in accordance with the terms specified in
// the LICENSE file found in the root directory of this source tree.
//
using System;
using System.Collections.Generic;
using System.Linq;
using System.Diagnostics;
using System.Threading.Tasks;
using System.Text;
namespace RpcInvestigator
{
using static TraceLogger;
public static class TaskWorker
{
public class TaskWorkerResult<T>
{
public StringBuilder Messages;
public T TaskResult;
}
public
static
async
Task<List<TaskWorkerResult<T>>>
Run<T, T2>(
List<T2> Input,
int WorkSize,
Func<List<T2>, Task<TaskWorkerResult<T>>> WorkRoutine
)
{
//
// This routine takes an input array of work items and splices it into
// multiple smaller lists that are passed to an asynchronous work routine.
// The results of all those workers is returned.
//
var workers = new List<Task<TaskWorkerResult<T>>>();
for (int i = 0; i < Input.Count; i += WorkSize)
{
var input = Input.Skip(i).Take(WorkSize).ToList();
workers.Add(Task.Run(() => WorkRoutine(input)));
}
//
// If a WorkRoutine task threw an unhandled exception, it will be treated
// as a TPL AggregateException which itself must be handled, or the whole
// application will go down.
//
var results = await Task.WhenAll(workers.ToArray()).ContinueWith(final =>
{
if (final.Exception != null)
{
final.Exception.Flatten().Handle(ex =>
{
Trace(TraceLoggerType.TaskWorker,
TraceEventType.Error,
"Exception in Run: " + ex.Message);
return true;
});
}
return final.Result;
});
return results.ToList();
}
public
static
async
Task<bool>
Run<T>(
List<T> Input,
int WorkSize,
Func<List<T>, Task<bool>> WorkRoutine
)
{
//
// This routine takes an input array of work items and splices it into
// multiple smaller lists that are passed to an asynchronous work routine.
// The results of all those workers is returned.
//
var workers = new List<Task<bool>>();
for (int i = 0; i < Input.Count; i += WorkSize)
{
var input = Input.Skip(i).Take(WorkSize).ToList();
workers.Add(Task.Run(() => WorkRoutine(input)));
}
//
// If a WorkRoutine task threw an unhandled exception, it will be treated
// as a TPL AggregateException which itself must be handled, or the whole
// application will go down.
//
_ = await Task.WhenAll(workers.ToArray()).ContinueWith(final =>
{
if (final.Exception != null)
{
final.Exception.Flatten().Handle(ex =>
{
Trace(TraceLoggerType.TaskWorker,
TraceEventType.Error,
"Exception in Run: " + ex.Message);
return true;
});
}
return final.Result;
});
return true;
}
public
static
async
Task<List<TaskWorkerResult<T>>>
RunSync<T, T2>(
List<T2> Input,
int WorkSize,
Func<List<T2>, TaskWorkerResult<T>> WorkRoutine
)
{
//
// This routine takes an input array of work items and splices it into
// multiple smaller lists that are passed to a synchronous work routine.
// The results of all those workers is returned.
//
var workers = new List<Task<TaskWorkerResult<T>>>();
for (int i = 0; i < Input.Count; i += WorkSize)
{
var input = Input.Skip(i).Take(WorkSize).ToList();
workers.Add(Task.Run(() => WorkRoutine(input)));
}
//
// If a WorkRoutine task threw an unhandled exception, it will be treated
// as a TPL AggregateException which itself must be handled, or the whole
// application will go down.
//
var results = await Task.WhenAll(workers.ToArray()).ContinueWith(final =>
{
if (final.Exception != null)
{
final.Exception.Flatten().Handle(ex =>
{
Trace(TraceLoggerType.TaskWorker,
TraceEventType.Error,
"Exception in RunSync: " + ex.Message);
return true;
});
}
return final.Result;
});
return results.ToList();
}
}
}

39
Util/TextRenderer.cs Normal file
View File

@ -0,0 +1,39 @@
//
// Copyright (c) 2022-present, Trail of Bits, Inc.
// All rights reserved.
//
// This source code is licensed in accordance with the terms specified in
// the LICENSE file found in the root directory of this source tree.
//
using BrightIdeasSoftware;
using System;
using System.Drawing;
using System.Windows.Forms;
namespace RpcInvestigator.Util
{
public class NoEllipsisRenderer : BaseRenderer
{
//
// This renderer overrides cell content truncation/ellipsis.
//
public override void Render(Graphics g, Rectangle r)
{
DrawBackground(g, r);
StringFormat fmt = new StringFormat(StringFormatFlags.NoWrap);
fmt.LineAlignment = StringAlignment.Center;
switch (this.Column.TextAlign)
{
case HorizontalAlignment.Center: fmt.Alignment = StringAlignment.Center; break;
case HorizontalAlignment.Left: fmt.Alignment = StringAlignment.Near; break;
case HorizontalAlignment.Right: fmt.Alignment = StringAlignment.Far; break;
}
Column.CellVerticalAlignment = StringAlignment.Near;
//
// Use GDI+ to preserve multi-line.
//
this.UseGdiTextRendering = false;
this.DrawAlignedImageAndText(g, r);
}
}
}

389
Windows/Client.Designer.cs generated Normal file
View File

@ -0,0 +1,389 @@
namespace RpcInvestigator
{
partial class Client
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.components = new System.ComponentModel.Container();
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(Client));
this.mainSplitContainer = new System.Windows.Forms.SplitContainer();
this.leftTableLayoutPanel = new System.Windows.Forms.TableLayoutPanel();
this.outputGroupbox = new System.Windows.Forms.GroupBox();
this.outputRichTextBox = new System.Windows.Forms.RichTextBox();
this.label2 = new System.Windows.Forms.Label();
this.rpcServerGroupbox = new System.Windows.Forms.GroupBox();
this.versionValue = new System.Windows.Forms.TextBox();
this.label5 = new System.Windows.Forms.Label();
this.connectButton = new System.Windows.Forms.Button();
this.protocolValue = new System.Windows.Forms.TextBox();
this.label4 = new System.Windows.Forms.Label();
this.endpointValue = new System.Windows.Forms.TextBox();
this.label3 = new System.Windows.Forms.Label();
this.uuidValue = new System.Windows.Forms.TextBox();
this.label1 = new System.Windows.Forms.Label();
this.rpcServerStartLabel = new System.Windows.Forms.Label();
this.workbenchTabControl = new System.Windows.Forms.TabControl();
this.codeTabPage = new System.Windows.Forms.TabPage();
this.rightTableLayoutPanel = new System.Windows.Forms.TableLayoutPanel();
this.resetButton = new System.Windows.Forms.Button();
this.rpcClientSourceCode = new FastColoredTextBoxNS.FastColoredTextBox();
this.runButton = new System.Windows.Forms.Button();
((System.ComponentModel.ISupportInitialize)(this.mainSplitContainer)).BeginInit();
this.mainSplitContainer.Panel1.SuspendLayout();
this.mainSplitContainer.Panel2.SuspendLayout();
this.mainSplitContainer.SuspendLayout();
this.leftTableLayoutPanel.SuspendLayout();
this.outputGroupbox.SuspendLayout();
this.rpcServerGroupbox.SuspendLayout();
this.workbenchTabControl.SuspendLayout();
this.codeTabPage.SuspendLayout();
this.rightTableLayoutPanel.SuspendLayout();
((System.ComponentModel.ISupportInitialize)(this.rpcClientSourceCode)).BeginInit();
this.SuspendLayout();
//
// mainSplitContainer
//
this.mainSplitContainer.Dock = System.Windows.Forms.DockStyle.Fill;
this.mainSplitContainer.Location = new System.Drawing.Point(0, 0);
this.mainSplitContainer.Name = "mainSplitContainer";
//
// mainSplitContainer.Panel1
//
this.mainSplitContainer.Panel1.Controls.Add(this.leftTableLayoutPanel);
//
// mainSplitContainer.Panel2
//
this.mainSplitContainer.Panel2.Controls.Add(this.workbenchTabControl);
this.mainSplitContainer.Size = new System.Drawing.Size(1792, 761);
this.mainSplitContainer.SplitterDistance = 597;
this.mainSplitContainer.TabIndex = 0;
//
// leftTableLayoutPanel
//
this.leftTableLayoutPanel.ColumnCount = 1;
this.leftTableLayoutPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50F));
this.leftTableLayoutPanel.Controls.Add(this.outputGroupbox, 0, 1);
this.leftTableLayoutPanel.Controls.Add(this.rpcServerGroupbox, 0, 0);
this.leftTableLayoutPanel.Dock = System.Windows.Forms.DockStyle.Fill;
this.leftTableLayoutPanel.Location = new System.Drawing.Point(0, 0);
this.leftTableLayoutPanel.Margin = new System.Windows.Forms.Padding(2);
this.leftTableLayoutPanel.Name = "leftTableLayoutPanel";
this.leftTableLayoutPanel.RowCount = 2;
this.leftTableLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 36.18657F));
this.leftTableLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 63.81343F));
this.leftTableLayoutPanel.Size = new System.Drawing.Size(597, 761);
this.leftTableLayoutPanel.TabIndex = 2;
//
// outputGroupbox
//
this.outputGroupbox.Controls.Add(this.outputRichTextBox);
this.outputGroupbox.Controls.Add(this.label2);
this.outputGroupbox.Dock = System.Windows.Forms.DockStyle.Fill;
this.outputGroupbox.Location = new System.Drawing.Point(3, 278);
this.outputGroupbox.Name = "outputGroupbox";
this.outputGroupbox.Size = new System.Drawing.Size(591, 480);
this.outputGroupbox.TabIndex = 3;
this.outputGroupbox.TabStop = false;
this.outputGroupbox.Text = "Output";
//
// outputRichTextBox
//
this.outputRichTextBox.Dock = System.Windows.Forms.DockStyle.Fill;
this.outputRichTextBox.Location = new System.Drawing.Point(3, 18);
this.outputRichTextBox.Margin = new System.Windows.Forms.Padding(2);
this.outputRichTextBox.Name = "outputRichTextBox";
this.outputRichTextBox.Size = new System.Drawing.Size(585, 459);
this.outputRichTextBox.TabIndex = 8;
this.outputRichTextBox.Text = "";
//
// label2
//
this.label2.AutoSize = true;
this.label2.Location = new System.Drawing.Point(7, 22);
this.label2.Name = "label2";
this.label2.Size = new System.Drawing.Size(0, 16);
this.label2.TabIndex = 7;
//
// rpcServerGroupbox
//
this.rpcServerGroupbox.Controls.Add(this.versionValue);
this.rpcServerGroupbox.Controls.Add(this.label5);
this.rpcServerGroupbox.Controls.Add(this.connectButton);
this.rpcServerGroupbox.Controls.Add(this.protocolValue);
this.rpcServerGroupbox.Controls.Add(this.label4);
this.rpcServerGroupbox.Controls.Add(this.endpointValue);
this.rpcServerGroupbox.Controls.Add(this.label3);
this.rpcServerGroupbox.Controls.Add(this.uuidValue);
this.rpcServerGroupbox.Controls.Add(this.label1);
this.rpcServerGroupbox.Controls.Add(this.rpcServerStartLabel);
this.rpcServerGroupbox.Dock = System.Windows.Forms.DockStyle.Fill;
this.rpcServerGroupbox.Location = new System.Drawing.Point(3, 3);
this.rpcServerGroupbox.Name = "rpcServerGroupbox";
this.rpcServerGroupbox.Size = new System.Drawing.Size(591, 269);
this.rpcServerGroupbox.TabIndex = 2;
this.rpcServerGroupbox.TabStop = false;
this.rpcServerGroupbox.Text = "RPC Server";
//
// versionValue
//
this.versionValue.Location = new System.Drawing.Point(413, 25);
this.versionValue.Name = "versionValue";
this.versionValue.Size = new System.Drawing.Size(46, 22);
this.versionValue.TabIndex = 16;
//
// label5
//
this.label5.AutoSize = true;
this.label5.Location = new System.Drawing.Point(348, 28);
this.label5.Name = "label5";
this.label5.Size = new System.Drawing.Size(56, 16);
this.label5.TabIndex = 15;
this.label5.Text = "Version:";
//
// connectButton
//
this.connectButton.Location = new System.Drawing.Point(384, 161);
this.connectButton.Name = "connectButton";
this.connectButton.Size = new System.Drawing.Size(75, 30);
this.connectButton.TabIndex = 14;
this.connectButton.Text = "Connect";
this.connectButton.UseVisualStyleBackColor = true;
this.connectButton.Click += new System.EventHandler(this.connectButton_Click);
//
// protocolValue
//
this.protocolValue.Location = new System.Drawing.Point(85, 113);
this.protocolValue.Name = "protocolValue";
this.protocolValue.Size = new System.Drawing.Size(374, 22);
this.protocolValue.TabIndex = 13;
//
// label4
//
this.label4.AutoSize = true;
this.label4.Location = new System.Drawing.Point(13, 116);
this.label4.Name = "label4";
this.label4.Size = new System.Drawing.Size(60, 16);
this.label4.TabIndex = 12;
this.label4.Text = "Protocol:";
//
// endpointValue
//
this.endpointValue.Location = new System.Drawing.Point(85, 66);
this.endpointValue.Name = "endpointValue";
this.endpointValue.Size = new System.Drawing.Size(374, 22);
this.endpointValue.TabIndex = 11;
//
// label3
//
this.label3.AutoSize = true;
this.label3.Location = new System.Drawing.Point(13, 69);
this.label3.Name = "label3";
this.label3.Size = new System.Drawing.Size(63, 16);
this.label3.TabIndex = 10;
this.label3.Text = "Endpoint:";
//
// uuidValue
//
this.uuidValue.Location = new System.Drawing.Point(85, 25);
this.uuidValue.Name = "uuidValue";
this.uuidValue.Size = new System.Drawing.Size(243, 22);
this.uuidValue.TabIndex = 9;
//
// label1
//
this.label1.AutoSize = true;
this.label1.Location = new System.Drawing.Point(13, 28);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(43, 16);
this.label1.TabIndex = 8;
this.label1.Text = "UUID:";
//
// rpcServerStartLabel
//
this.rpcServerStartLabel.AutoSize = true;
this.rpcServerStartLabel.Location = new System.Drawing.Point(7, 22);
this.rpcServerStartLabel.Name = "rpcServerStartLabel";
this.rpcServerStartLabel.Size = new System.Drawing.Size(0, 16);
this.rpcServerStartLabel.TabIndex = 7;
//
// workbenchTabControl
//
this.workbenchTabControl.Controls.Add(this.codeTabPage);
this.workbenchTabControl.Dock = System.Windows.Forms.DockStyle.Fill;
this.workbenchTabControl.Location = new System.Drawing.Point(0, 0);
this.workbenchTabControl.Name = "workbenchTabControl";
this.workbenchTabControl.SelectedIndex = 0;
this.workbenchTabControl.Size = new System.Drawing.Size(1191, 761);
this.workbenchTabControl.TabIndex = 0;
//
// codeTabPage
//
this.codeTabPage.Controls.Add(this.rightTableLayoutPanel);
this.codeTabPage.Location = new System.Drawing.Point(4, 25);
this.codeTabPage.Name = "codeTabPage";
this.codeTabPage.Padding = new System.Windows.Forms.Padding(3);
this.codeTabPage.Size = new System.Drawing.Size(1183, 732);
this.codeTabPage.TabIndex = 0;
this.codeTabPage.Text = "Client Code";
this.codeTabPage.UseVisualStyleBackColor = true;
//
// rightTableLayoutPanel
//
this.rightTableLayoutPanel.ColumnCount = 3;
this.rightTableLayoutPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle());
this.rightTableLayoutPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle());
this.rightTableLayoutPanel.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle());
this.rightTableLayoutPanel.Controls.Add(this.resetButton, 0, 1);
this.rightTableLayoutPanel.Controls.Add(this.rpcClientSourceCode, 0, 0);
this.rightTableLayoutPanel.Controls.Add(this.runButton, 0, 1);
this.rightTableLayoutPanel.Dock = System.Windows.Forms.DockStyle.Fill;
this.rightTableLayoutPanel.Location = new System.Drawing.Point(3, 3);
this.rightTableLayoutPanel.Margin = new System.Windows.Forms.Padding(2);
this.rightTableLayoutPanel.Name = "rightTableLayoutPanel";
this.rightTableLayoutPanel.RowCount = 2;
this.rightTableLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 92.9267F));
this.rightTableLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 7.073298F));
this.rightTableLayoutPanel.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 20F));
this.rightTableLayoutPanel.Size = new System.Drawing.Size(1177, 726);
this.rightTableLayoutPanel.TabIndex = 1;
//
// resetButton
//
this.resetButton.Location = new System.Drawing.Point(123, 677);
this.resetButton.Name = "resetButton";
this.resetButton.Size = new System.Drawing.Size(114, 33);
this.resetButton.TabIndex = 8;
this.resetButton.Text = "Reset";
this.resetButton.UseVisualStyleBackColor = true;
this.resetButton.Click += new System.EventHandler(this.resetButton_Click);
//
// rpcClientSourceCode
//
this.rpcClientSourceCode.AutoCompleteBracketsList = new char[] {
'(',
')',
'{',
'}',
'[',
']',
'\"',
'\"',
'\'',
'\''};
this.rpcClientSourceCode.AutoIndentCharsPatterns = "\r\n^\\s*[\\w\\.]+(\\s\\w+)?\\s*(?<range>=)\\s*(?<range>[^;]+);\r\n^\\s*(case|default)\\s*[^:]" +
"*(?<range>:)\\s*(?<range>[^;]+);\r\n";
this.rpcClientSourceCode.AutoScrollMinSize = new System.Drawing.Size(31, 18);
this.rpcClientSourceCode.AutoSize = true;
this.rpcClientSourceCode.BackBrush = null;
this.rpcClientSourceCode.BracketsHighlightStrategy = FastColoredTextBoxNS.BracketsHighlightStrategy.Strategy2;
this.rpcClientSourceCode.CharHeight = 18;
this.rpcClientSourceCode.CharWidth = 10;
this.rightTableLayoutPanel.SetColumnSpan(this.rpcClientSourceCode, 3);
this.rpcClientSourceCode.Cursor = System.Windows.Forms.Cursors.IBeam;
this.rpcClientSourceCode.DisabledColor = System.Drawing.Color.FromArgb(((int)(((byte)(100)))), ((int)(((byte)(180)))), ((int)(((byte)(180)))), ((int)(((byte)(180)))));
this.rpcClientSourceCode.Dock = System.Windows.Forms.DockStyle.Fill;
this.rpcClientSourceCode.Font = new System.Drawing.Font("Courier New", 9.75F);
this.rpcClientSourceCode.IsReplaceMode = false;
this.rpcClientSourceCode.Language = FastColoredTextBoxNS.Language.CSharp;
this.rpcClientSourceCode.LeftBracket = '(';
this.rpcClientSourceCode.LeftBracket2 = '{';
this.rpcClientSourceCode.Location = new System.Drawing.Point(3, 3);
this.rpcClientSourceCode.Name = "rpcClientSourceCode";
this.rpcClientSourceCode.Paddings = new System.Windows.Forms.Padding(0);
this.rpcClientSourceCode.RightBracket = ')';
this.rpcClientSourceCode.RightBracket2 = '}';
this.rpcClientSourceCode.SelectionColor = System.Drawing.Color.FromArgb(((int)(((byte)(60)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))), ((int)(((byte)(255)))));
this.rpcClientSourceCode.ServiceColors = ((FastColoredTextBoxNS.ServiceColors)(resources.GetObject("rpcClientSourceCode.ServiceColors")));
this.rpcClientSourceCode.Size = new System.Drawing.Size(1171, 668);
this.rpcClientSourceCode.TabIndex = 1;
this.rpcClientSourceCode.Zoom = 100;
//
// runButton
//
this.runButton.Location = new System.Drawing.Point(3, 677);
this.runButton.Name = "runButton";
this.runButton.Size = new System.Drawing.Size(114, 33);
this.runButton.TabIndex = 7;
this.runButton.Text = "Run";
this.runButton.UseVisualStyleBackColor = true;
this.runButton.Click += new System.EventHandler(this.runButton_Click);
//
// Client
//
this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 16F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(1792, 761);
this.Controls.Add(this.mainSplitContainer);
this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
this.Name = "Client";
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
this.Text = "RPC Client Workbench";
this.Shown += new System.EventHandler(this.Client_Shown);
this.mainSplitContainer.Panel1.ResumeLayout(false);
this.mainSplitContainer.Panel2.ResumeLayout(false);
((System.ComponentModel.ISupportInitialize)(this.mainSplitContainer)).EndInit();
this.mainSplitContainer.ResumeLayout(false);
this.leftTableLayoutPanel.ResumeLayout(false);
this.outputGroupbox.ResumeLayout(false);
this.outputGroupbox.PerformLayout();
this.rpcServerGroupbox.ResumeLayout(false);
this.rpcServerGroupbox.PerformLayout();
this.workbenchTabControl.ResumeLayout(false);
this.codeTabPage.ResumeLayout(false);
this.rightTableLayoutPanel.ResumeLayout(false);
this.rightTableLayoutPanel.PerformLayout();
((System.ComponentModel.ISupportInitialize)(this.rpcClientSourceCode)).EndInit();
this.ResumeLayout(false);
}
#endregion
private System.Windows.Forms.SplitContainer mainSplitContainer;
private System.Windows.Forms.TableLayoutPanel leftTableLayoutPanel;
private System.Windows.Forms.GroupBox rpcServerGroupbox;
private System.Windows.Forms.Label rpcServerStartLabel;
private System.Windows.Forms.GroupBox outputGroupbox;
private System.Windows.Forms.Label label2;
private System.Windows.Forms.RichTextBox outputRichTextBox;
private System.Windows.Forms.TabControl workbenchTabControl;
private System.Windows.Forms.TabPage codeTabPage;
private System.Windows.Forms.TableLayoutPanel rightTableLayoutPanel;
private System.Windows.Forms.Button resetButton;
private FastColoredTextBoxNS.FastColoredTextBox rpcClientSourceCode;
private System.Windows.Forms.Button runButton;
private System.Windows.Forms.Button connectButton;
private System.Windows.Forms.TextBox protocolValue;
private System.Windows.Forms.Label label4;
private System.Windows.Forms.TextBox endpointValue;
private System.Windows.Forms.Label label3;
private System.Windows.Forms.TextBox uuidValue;
private System.Windows.Forms.Label label1;
private System.Windows.Forms.TextBox versionValue;
private System.Windows.Forms.Label label5;
}
}

360
Windows/Client.cs Normal file
View File

@ -0,0 +1,360 @@
//
// Copyright (c) 2022-present, Trail of Bits, Inc.
// All rights reserved.
//
// This source code is licensed in accordance with the terms specified in
// the LICENSE file found in the root directory of this source tree.
//
using NtApiDotNet.Win32;
using NtApiDotNet.Win32.Rpc;
using NtApiDotNet.Win32.Rpc.Transport;
using System;
using System.Collections.Generic;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.CodeDom.Compiler;
using System.IO;
using System.Globalization;
using FastColoredTextBoxNS;
namespace RpcInvestigator
{
public partial class Client : Form
{
private readonly RpcServer m_RpcServer;
private readonly RpcEndpoint m_Endpoint;
public Client(RpcServer Server, RpcEndpoint Endpoint)
{
InitializeComponent();
HideConnectControls();
m_RpcServer = Server;
m_Endpoint = Endpoint;
}
private void Client_Shown(object sender, EventArgs e)
{
var last = AddLabels(
new Dictionary<string, string>(){
{ "UUID", m_RpcServer.InterfaceId.ToString() },
{ "Version", m_RpcServer.InterfaceVersion.ToString() },
{ "Name", m_RpcServer.Name.ToString() },
{ "Transfer Syntax ID", m_RpcServer.TransferSyntaxId.ToString() },
{ "Transfer Syntax Version", m_RpcServer.TransferSyntaxVersion.ToString() },
{ "FilePath", m_RpcServer.FilePath.ToString() },
{ "Endpoint Count", m_RpcServer.EndpointCount.ToString() },
{ "Procedure Count", m_RpcServer.ProcedureCount.ToString() },
},
rpcServerStartLabel.Location.X,
rpcServerStartLabel.Location.Y);
if (m_Endpoint != null)
{
AddLabels(
new Dictionary<string, string>()
{
{ "Endpoint information", "" },
{ "Endpoint Name", m_Endpoint.Endpoint.ToString() },
{ "Protocol Sequence", m_Endpoint.ProtocolSequence.ToString() },
{ "Binding String", m_Endpoint.BindingString.ToString() },
{ "Endpoint Path", m_Endpoint.EndpointPath.ToString() },
},
rpcServerStartLabel.Location.X,
last.Item2);
}
try
{
SetSourceCode();
}
catch (Exception ex)
{
MessageBox.Show("There was a problem compiling the RPC client " +
"code: " + ex.Message);
}
RpcProcedureList proceduresTab = new RpcProcedureList();
workbenchTabControl.TabPages.Add(proceduresTab);
proceduresTab.Build(m_RpcServer.Name, m_RpcServer.Procedures.ToList());
}
private
void
HideConnectControls()
{
foreach (var control in rpcServerGroupbox.Controls.Cast<Control>().ToList())
{
control.Visible = false;
}
}
private
(int,int)
AddLabels(
Dictionary<string, string> Values,
int StartX,
int StartY
)
{
int longestNameX = 0;
int currY = StartY;
int fixedX = StartX;
Values.Keys.ToList().ForEach(name =>
{
var label = new Label();
label.AutoSize = true;
label.Name = name.Replace(" ", "_") + "_Label";
label.Text = name + ":";
label.Location = new Point(fixedX, currY);
label.PerformLayout();
rpcServerGroupbox.Controls.Add(label);
currY += label.Height + 3;
longestNameX = Math.Max(longestNameX, label.Width);
});
currY = StartY;
fixedX = StartX + longestNameX + 5;
Values.Values.ToList().ForEach(value =>
{
Random rand = new Random();
var label = new Label();
label.AutoSize = true;
label.Name = "LabelValue" + rand.Next();
label.Text = value;
label.Location = new Point(fixedX, currY);
rpcServerGroupbox.Controls.Add(label);
currY += label.Height + 3;
});
return (fixedX, currY);
}
private async void runButton_Click(object sender, EventArgs e)
{
Assembly assembly;
//
// Compile the source
//
try
{
assembly = ClientCompiler.Compile(rpcClientSourceCode.Text);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
return;
}
var clientType = assembly.GetType("RpcInvestigator.Client1", true);
var client = Activator.CreateInstance(clientType);
try
{
//
// Instantiate the client and connect to the server. If an endpoint was
// provided to us, use it.
//
if (m_Endpoint != null)
{
((RpcClientBase)client).Connect(m_Endpoint, new RpcTransportSecurity());
}
else
{
/*
var endpoints = RpcEndpointMapper.FindAlpcEndpointForInterface(
m_RpcServer.InterfaceId, m_RpcServer.InterfaceVersion);
if (endpoints == null || endpoints.Count() == 0)
{
MessageBox.Show("Unable to locate an endpoint for server " +
m_RpcServer.InterfaceId.ToString() + ", version " +
m_RpcServer.InterfaceVersion.ToString() + " - unable to " +
"retrieve server definition.");
}
*/
((RpcClientBase)client).Connect();
}
//
// Get the 'Run' method and execute it
//
var run = clientType.GetMethod("Run");
if (run == null)
{
throw new Exception("Method 'Run' must exist in client class.");
}
if (run.ReturnType != typeof(Task<bool>))
{
throw new Exception("Method 'Run' must return Task<bool> type.");
}
//
// Capture our own stdout, as the assembly will run in a sub-process of us.
//
var sb = new StringBuilder();
var writer = new StringWriter(sb);
Console.SetOut(writer);
_ = await (Task<bool>)run.Invoke(
client,
BindingFlags.InvokeMethod | BindingFlags.Instance | BindingFlags.Public,
null,
null,
CultureInfo.CurrentCulture);
outputRichTextBox.Text += "> Run() output:" + Environment.NewLine +
sb.ToString() +
Environment.NewLine;
}
catch (Exception ex)
{
MessageBox.Show("Run() failed: " + ex.Message);
return;
}
finally
{
((RpcClientBase)client).Disconnect();
}
}
private void resetButton_Click(object sender, EventArgs e)
{
try
{
SetSourceCode();
}
catch (Exception ex)
{
MessageBox.Show("There was a problem compiling the RPC client " +
"code: " + ex.Message);
}
}
private void SetSourceCode()
{
var code = ClientCompiler.GetBaseClientCode(m_RpcServer);
rpcClientSourceCode.Text = code;
var lines = rpcClientSourceCode.FindLines(@"public async Task\<bool\> Run",
System.Text.RegularExpressions.RegexOptions.None);
if (lines.Count != 1)
{
throw new Exception("Unexpected source code layout.");
}
var range = new Range(rpcClientSourceCode, lines[0]);
rpcClientSourceCode.DoCaretVisible();
var regions = rpcClientSourceCode.FindLines("#region ", System.Text.RegularExpressions.RegexOptions.None);
foreach (var line in regions)
{
if (rpcClientSourceCode.Lines[line].Contains("#region Client Implementation"))
{
continue;
}
rpcClientSourceCode.CollapseFoldingBlock(line);
}
rpcClientSourceCode.Selection = range;
}
private void connectButton_Click(object sender, EventArgs e)
{
var uuid = uuidValue.Text;
var version = versionValue.Text;
var endpoint = endpointValue.Text;
var protocol = protocolValue.Text;
if (string.IsNullOrEmpty(uuid) || string.IsNullOrEmpty(version) ||
string.IsNullOrEmpty(endpoint) || string.IsNullOrEmpty(protocol))
{
MessageBox.Show("Please specify UUID, version, endpoint and protocol.");
return;
}
try
{
var client = new RpcClient(Guid.Parse(uuid), new Version(version));
var security = new RpcTransportSecurity();
security.AuthenticationLevel = RpcAuthenticationLevel.None;
security.AuthenticationType = RpcAuthenticationType.None;
security.AuthenticationCapabilities = RpcAuthenticationCapabilities.None;
client.Connect(protocol, endpoint, security);
}
catch (Exception ex)
{
MessageBox.Show("Unable to connect: " + ex.Message);
return;
}
MessageBox.Show("Connected OK");
}
}
public static class ClientCompiler
{
public static Assembly Compile(string Source)
{
CompilerParameters compile_params = new CompilerParameters();
using (TempFileCollection temp_files = new TempFileCollection(Path.GetTempPath()))
{
compile_params.GenerateExecutable = false;
compile_params.GenerateInMemory = true;
compile_params.IncludeDebugInformation = true;
compile_params.TempFiles = temp_files;
compile_params.ReferencedAssemblies.Add(typeof(RpcClientBuilder).Assembly.Location);
compile_params.ReferencedAssemblies.Add("System.dll");
compile_params.ReferencedAssemblies.Add("System.Core.dll");
temp_files.KeepFiles = true;
CompilerResults results = CodeDomProvider.CreateProvider("CSharp").
CompileAssemblyFromSource(compile_params, Source);
if (results.Errors.HasErrors)
{
StringBuilder errors = new StringBuilder();
foreach (CompilerError e in results.Errors)
{
errors.AppendLine(e.ToString());
}
throw new Exception("Compiler errors:" + errors.ToString());
}
return results.CompiledAssembly;
}
}
public static string GetBaseClientCode(RpcServer Server)
{
//
// TODO: It would be nice if RpcClientBuilder exposed its internal
// routines for different parts of the source code creation process,
// such that we could add method definitions to the resulting Client
// class prior to source code generation.
// As is stands, all those things are marked private and we can only
// either get back an immutable assembly object or the finalized source
// code as text, neither of which are ideal for modifying the code.
// So this becomes a text search problem.
//
RpcClientBuilderArguments args = new RpcClientBuilderArguments();
args.Flags = RpcClientBuilderFlags.GenerateConstructorProperties |
RpcClientBuilderFlags.StructureReturn |
RpcClientBuilderFlags.HideWrappedMethods |
RpcClientBuilderFlags.UnsignedChar |
RpcClientBuilderFlags.NoNamespace |
RpcClientBuilderFlags.MarshalPipesAsArrays |
RpcClientBuilderFlags.ExcludeVariableSourceText;
args.NamespaceName = "RpcInvestigator";
args.ClientName = "Client1";
var src = RpcClientBuilder.BuildSource(Server, args);
src = "using System;" + Environment.NewLine +
"using System.Threading.Tasks;" + Environment.NewLine +
"using NtApiDotNet.Win32;" + Environment.NewLine +
"using NtApiDotNet;" + Environment.NewLine +
"using NtApiDotNet.Ndr.Marshal;" + Environment.NewLine +
src;
int loc = src.IndexOf("public Client1()");
src = src.Insert(loc,
"public async Task<bool> Run()" + Environment.NewLine +
" {" + Environment.NewLine +
" return true;" + Environment.NewLine +
" }" + Environment.NewLine+
" ");
return src;
}
}
}

3409
Windows/Client.resx Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,86 @@
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="http://schemas.panthernet.ru/graphx/">
<!-- VERTEX CONTROL -->
<Style TargetType="{x:Type controls:VertexControl}">
<Setter Property="Background" Value="#000000"/>
<Setter Property="BorderThickness" Value="5,3,5,3"/>
<Setter Property="Padding" Value="10,5,10,5"/>
<Setter Property="BorderBrush" Value="#FF393939"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type controls:VertexControl}">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
CornerRadius="10,10,10,10"
Padding="{TemplateBinding Padding}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<ContentPresenter Content="{TemplateBinding Vertex}" />
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<!-- VERTEX DRAGGING CONTROL -->
<Setter Property="controls:DragBehaviour.IsDragEnabled"
Value="True" />
<Setter Property="controls:HighlightBehaviour.IsHighlightEnabled" Value="False"/>
<Style.Triggers>
<Trigger Property="controls:HighlightBehaviour.Highlighted" Value="True">
<Setter Property="Background" Value="Gold"/>
<Setter Property="BorderThickness" Value="7"/>
</Trigger>
</Style.Triggers>
</Style>
<!-- EDGE CONTROL -->
<Style TargetType="{x:Type controls:EdgeControl}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type controls:EdgeControl}">
<Grid>
<Path Stroke="{TemplateBinding Foreground}"
StrokeThickness="2" MinWidth="1" MinHeight="1"
ToolTip="{TemplateBinding ToolTip}"
x:Name="PART_edgePath"/>
<controls:DefaultEdgePointer NeedRotation="true" x:Name="PART_EdgePointerForTarget" >
<Path Data="M0,0.5 L1,1 1,0" Fill="Black" Stretch="Uniform" Width="10" Height="10"/>
</controls:DefaultEdgePointer>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="MinWidth"
Value="1" />
<Setter Property="MinHeight"
Value="1" />
<Setter Property="Background"
Value="Red" />
<Setter Property="Foreground"
Value="Black" />
<Setter Property="Opacity"
Value="1" />
<Setter Property="controls:HighlightBehaviour.IsHighlightEnabled" Value="False"/>
<Style.Triggers>
<Trigger Property="controls:HighlightBehaviour.Highlighted" Value="True">
<Setter Property="Foreground" Value="Gold"/>
<Setter Property="StrokeThickness" Value="5"/>
</Trigger>
</Style.Triggers>
</Style>
</ResourceDictionary>

View File

@ -0,0 +1,453 @@
//
// Copyright (c) 2022-present, Trail of Bits, Inc.
// All rights reserved.
//
// This source code is licensed in accordance with the terms specified in
// the LICENSE file found in the root directory of this source tree.
//
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using RpcInvestigator.Util;
using System.Windows.Forms.Integration;
using GraphX.Common.Enums;
using GraphX.Logic.Algorithms.OverlapRemoval;
using GraphX.Logic.Models;
using GraphX.Controls;
using GraphX.Controls.Models;
using QuickGraph;
using GraphX.Common.Models;
using System.Windows;
using MessageBox = System.Windows.Forms.MessageBox;
using GraphX.Logic.Algorithms.LayoutAlgorithms;
using Brushes = System.Windows.Media.Brushes;
using Style = System.Windows.Style;
using Binding = System.Windows.Data.Binding;
using Control = System.Windows.Controls.Control;
using GraphX.Common.Interfaces;
namespace RpcInvestigator.Windows.Controls
{
public class SnifferGraph
{
private readonly RpcLibrary m_Library;
private readonly SnifferCallbackTable m_Callbacks;
private ZoomControl m_Zoom;
private SnifferGraphArea m_GraphArea;
private Dictionary<long, string> m_PidLookupCache;
public SnifferGraph(
ElementHost Host,
RpcLibrary Library,
SnifferCallbackTable Callbacks
)
{
m_PidLookupCache = new Dictionary<long, string>();
m_Callbacks = Callbacks;
m_Library = Library;
m_Zoom = new ZoomControl();
Host.Child = m_Zoom;
m_Zoom.ZoomToFill();
ZoomControl.SetViewFinderVisibility(m_Zoom, System.Windows.Visibility.Visible);
var logic = new GXLogicCore<SnifferNode, SnifferEdge, BidirectionalSnifferGraph>();
m_GraphArea = new SnifferGraphArea
{
LogicCore = logic,
EdgeLabelFactory = new DefaultEdgelabelFactory()
};
m_GraphArea.LogicCore.Graph = new BidirectionalSnifferGraph();
logic.DefaultLayoutAlgorithm = LayoutAlgorithmTypeEnum.LinLog;
var layoutParams = (LinLogLayoutParameters)
logic.AlgorithmFactory.CreateLayoutParameters(LayoutAlgorithmTypeEnum.LinLog);
logic.DefaultLayoutAlgorithmParams = layoutParams;
logic.DefaultOverlapRemovalAlgorithm = OverlapRemovalAlgorithmTypeEnum.FSA;
logic.DefaultOverlapRemovalAlgorithmParams = logic.AlgorithmFactory.CreateOverlapRemovalParameters(OverlapRemovalAlgorithmTypeEnum.FSA);
((OverlapRemovalParameters)logic.DefaultOverlapRemovalAlgorithmParams).HorizontalGap = 20;
((OverlapRemovalParameters)logic.DefaultOverlapRemovalAlgorithmParams).VerticalGap = 20;
logic.DefaultEdgeRoutingAlgorithm = EdgeRoutingAlgorithmTypeEnum.None;
logic.AsyncAlgorithmCompute = false;
m_Zoom.Content = m_GraphArea;
OverrideTemplateStyles();
m_GraphArea.RelayoutFinished += SnifferGraphArea_RelayoutFinished;
m_GraphArea.VertexClicked += NodeClicked;
ToggleVisibility(false);
}
void SnifferGraphArea_RelayoutFinished(object sender, EventArgs e)
{
m_Zoom.ZoomToFill();
}
private void NodeClicked(object sender, VertexClickedEventArgs e)
{
var node = e.Control.Vertex as SnifferNode;
if (node.UserData == null || node.UserData.GetType() != typeof(SnifferNodeUserData))
{
return;
}
if (!Guid.TryParse(node.UserData.InterfaceId, out Guid parsed))
{
MessageBox.Show("The InterfaceUuid has an invalid format.");
return;
}
m_Callbacks.ShowRpcServerDetailsCallback(parsed.ToString());
}
private
void
OverrideTemplateStyles()
{
//
// Ellipsis node type
//
var ellipsisNodeTrigger = new DataTrigger()
{
Binding = new Binding("NodeType"),
Value = SnifferNodeType.Ellipsis
};
ellipsisNodeTrigger.Setters.Add(new Setter()
{
Property = Control.BackgroundProperty,
Value = Brushes.LightBlue
});
//
// RPC server node type
//
var rpcServerNodeTrigger = new DataTrigger()
{
Binding = new Binding("NodeType"),
Value = SnifferNodeType.RpcServer
};
rpcServerNodeTrigger.Setters.Add(new Setter()
{
Property = Control.BackgroundProperty,
Value = Brushes.Red
});
rpcServerNodeTrigger.Setters.Add(new Setter()
{
Property = VertexControl.VertexShapeProperty,
Value = VertexShape.Rectangle
});
//
// Process node type
//
var processNodeTrigger = new DataTrigger()
{
Binding = new Binding("NodeType"),
Value = SnifferNodeType.Process
};
processNodeTrigger.Setters.Add(new Setter()
{
Property = Control.BackgroundProperty,
Value = Brushes.Blue
});
processNodeTrigger.Setters.Add(new Setter()
{
Property = Control.ForegroundProperty,
Value = Brushes.White
});
//
// There's probably a better way, but this works.
//
m_Zoom.Resources.MergedDictionaries.Add(new ResourceDictionary
{
Source = new Uri("Windows/Controls/GraphTemplate.xaml", UriKind.Relative)
});
var style = (Style)m_Zoom.Resources.MergedDictionaries[0][typeof(VertexControl)];
style.Setters.Clear();
style.Triggers.Clear();
style.Triggers.Add(ellipsisNodeTrigger);
style.Triggers.Add(rpcServerNodeTrigger);
style.Triggers.Add(processNodeTrigger);
}
public
void
ToggleVisibility(bool Visible)
{
m_GraphArea.Visibility = Visible ? Visibility.Visible : Visibility.Hidden;
}
public void Update(List<ParsedEtwEvent> Events)
{
foreach (var evt in Events)
{
//
// Ignore events that don't contain an interface ID for the RPC server
// involved or activity ID. This greatly reduces noise and the
// back-and-forth parts of a typical RPC call which show up here as
// multiple ETW events but represent one logical call.
// Also, ignore this tool as well.
//
if (!evt.UserDataProperties.ContainsKey("InterfaceUuid") ||
evt.ActivityId == null || evt.ActivityId == Guid.Empty ||
evt.ProcessId == Process.GetCurrentProcess().Id)
{
continue;
}
//
// Only draw nodes/edge for an activity event group one time.
//
var activityId = evt.ActivityId.ToString();
var edgeId = activityId;
var existingActivityEdge = m_GraphArea.EdgesList.Where(
e => e.Key.SnifferId == activityId);
if (existingActivityEdge.Count() != 0)
{
continue;
}
var rpcServerUuid = evt.UserDataProperties["InterfaceUuid"];
var existing = m_GraphArea.VertexList.Where(
v => v.Key.SnifferId == rpcServerUuid);
SnifferNode rpcServerNode;
if (existing.Count() == 0)
{
var node = new SnifferNode()
{
NodeType = SnifferNodeType.RpcServer,
SnifferId = rpcServerUuid,
UserData = new SnifferNodeUserData()
{
InterfaceId = rpcServerUuid,
EdgeCount = 0,
},
Text = rpcServerUuid,
};
var servers = m_Library.Get(new Guid(rpcServerUuid));
if (servers != null)
{
//
// TODO: Icon to show this info came from our RPC library.
// Also, we should add ALPC port information if it's available.
//
var friendly = "";
if (!string.IsNullOrEmpty(servers[0].ServiceName))
{
friendly = servers[0].ServiceName + Environment.NewLine;
}
else if (!string.IsNullOrEmpty(servers[0].FilePath))
{
friendly = servers[0].FilePath + Environment.NewLine;
}
node.Text = friendly + rpcServerUuid;
}
var control = new VertexControl(node);
m_GraphArea.AddVertexAndData(node, control);
rpcServerNode = node;
}
else
{
rpcServerNode = existing.ToList()[0].Key;
}
//
// Avoid edge explosion for chatty RPC servers.
// TODO: Make this customizable.
//
if (rpcServerNode.UserData.EdgeCount > 10)
{
AddOrUpdateEllipsisNode(rpcServerNode);
continue;
}
ConnectRpcNode(activityId, rpcServerNode, evt);
}
m_GraphArea.SetEdgesDrag(false);
m_GraphArea.SetVerticesDrag(false);
m_GraphArea.GenerateGraph(true);
m_GraphArea.ShowAllEdgesLabels(false);
m_Zoom.ZoomToFill();
}
private void ConnectRpcNode(
string ActivityId,
SnifferNode RpcServerNode,
ParsedEtwEvent Event
)
{
//
// Increment the node's edge count to prevent edge explosion.
//
RpcServerNode.UserData.EdgeCount++;
//
// TODO: The PID could have been reused. We should probably
// build and maintain a best-effort mapping.
//
string processName = null;
if (!m_PidLookupCache.ContainsKey(Event.ProcessStartKey))
{
try
{
Process p = Process.GetProcessById((int)Event.ProcessId);
processName = p.ProcessName + "(" + Event.ProcessId + ")";
}
catch (Exception)
{
processName = "[" + Event.ProcessId.ToString() + "]";
}
m_PidLookupCache.Add(Event.ProcessStartKey, processName);
}
else
{
processName = m_PidLookupCache[Event.ProcessStartKey];
}
var existingProcessNode = m_GraphArea.VertexList.Where(
v => v.Key.SnifferId == processName);
SnifferNode processNode;
if (existingProcessNode.Count() == 0)
{
var node = new SnifferNode()
{
NodeType = SnifferNodeType.Process,
SnifferId = processName,
Text = processName,
};
var control = new VertexControl(node);
m_GraphArea.AddVertexAndData(node, control);
processNode = node;
}
else
{
processNode = existingProcessNode.ToList()[0].Key;
}
var processNodeControl = m_GraphArea.VertexList[processNode];
//
// RPC etw events have tasks (RpcClientCall, RpcServerCall)
// and opcodes (Start, Stop) which would result in multiple
// redundant edges between a process and an RPC server. We
// have already filtered by ActivityId, so we should never
// have to check that an edge exists here.
//
var rpcServerNodeControl = m_GraphArea.VertexList[RpcServerNode];
var edgeId = ActivityId;
var edgeFromRpcServerNodeToProcessNode =
new SnifferEdge(processNode, RpcServerNode)
{
SnifferId = edgeId,
RpcNodeSnifferId = RpcServerNode.SnifferId,
};
var edgeControl = new EdgeControl(processNodeControl,
rpcServerNodeControl,
edgeFromRpcServerNodeToProcessNode);
m_GraphArea.AddEdgeAndData(edgeFromRpcServerNodeToProcessNode, edgeControl);
}
private void AddOrUpdateEllipsisNode(SnifferNode RpcServerNode)
{
if (RpcServerNode.UserData.EllipsisNode != null)
{
var count = RpcServerNode.UserData.EdgeCount++;
RpcServerNode.UserData.EllipsisNode.Text = "+" + count + " more...";
return;
}
//
// No ellipsis node exists for this RPC server. Create it now.
//
var nodeId = RpcServerNode.UserData.InterfaceId + "-ellipsis";
var node = new SnifferNode()
{
NodeType = SnifferNodeType.Ellipsis,
SnifferId = nodeId,
UserData = new SnifferNodeUserData(),
Text = "+1 more..."
};
var control = new VertexControl(node);
m_GraphArea.AddVertexAndData(node, control);
//
// Connect it to the RPC server node.
//
var rpcServerNodeControl = m_GraphArea.VertexList[RpcServerNode];
var edgeFromRpcServerNodeToEllipsisNode =
new SnifferEdge(node, RpcServerNode);
m_GraphArea.AddEdgeAndData(
edgeFromRpcServerNodeToEllipsisNode,
new EdgeControl(
control,
rpcServerNodeControl,
edgeFromRpcServerNodeToEllipsisNode));
RpcServerNode.UserData.EllipsisNode = node;
}
public void Reset()
{
m_GraphArea.RemoveAllVertices();
m_GraphArea.RemoveAllEdges();
}
public void SaveAsImage()
{
if (m_GraphArea.LogicCore.Graph.IsVerticesEmpty)
{
return;
}
m_GraphArea.ExportAsImageDialog(ImageType.PNG);
}
}
public class SnifferGraphArea : GraphArea<SnifferNode, SnifferEdge, BidirectionalSnifferGraph> { }
public class BidirectionalSnifferGraph : BidirectionalGraph<SnifferNode, SnifferEdge> { }
public enum SnifferNodeType
{
Ellipsis,
Process,
RpcServer
}
public class SnifferNode : VertexBase
{
public string SnifferId { get; set; }
public SnifferNodeUserData UserData { get; set; }
public string Text { get; set; }
public SnifferNodeType NodeType { get; set; }
public SnifferNode() { }
public override string ToString()
{
return Text;
}
}
public class SnifferNodeUserData
{
public string InterfaceId;
public int EdgeCount;
public SnifferNode EllipsisNode;
}
public class SnifferEdge : EdgeBase<SnifferNode>
{
public string SnifferId { get; set; }
public string RpcNodeSnifferId { get; set; }
public object UserData { get; set; }
public string Text { get; set; }
public SnifferEdge(SnifferNode source, SnifferNode target, double weight = 1)
: base(source, target, weight)
{
}
public SnifferEdge()
: base(null, null, 1)
{
}
public override string ToString()
{
return Text;
}
}
}

View File

@ -0,0 +1,360 @@
//
// Copyright (c) 2022-present, Trail of Bits, Inc.
// All rights reserved.
//
// This source code is licensed in accordance with the terms specified in
// the LICENSE file found in the root directory of this source tree.
//
using BrightIdeasSoftware;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using RpcInvestigator.Util;
using System.Windows.Forms;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using RpcInvestigator.TabPages;
namespace RpcInvestigator.Windows.Controls
{
internal class SnifferListview : FastObjectListView
{
private List<string> m_ChosenColumns;
private readonly SnifferCallbackTable m_Callbacks;
private Settings m_Settings;
public SnifferListview(
SnifferCallbackTable Callbacks,
Settings Settings
)
{
m_Settings = Settings;
m_Callbacks = Callbacks;
DoubleBuffered = true;
Visible = false;
BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
CellEditUseWholeCell = false;
Dock = System.Windows.Forms.DockStyle.Fill;
FullRowSelect = true;
GridLines = true;
HideSelection = false;
Location = new System.Drawing.Point(6, 130);
Margin = new System.Windows.Forms.Padding(6, 6, 6, 6);
Name = "snifferListview";
ShowGroups = false;
Size = new System.Drawing.Size(2940, 1336);
TabIndex = 0;
UseAlternatingBackColors = true;
View = View.Details;
AlternateRowBackColor = Color.LightBlue;
UseCompatibleStateImageBehavior = false;
VirtualMode = true;
if (m_Settings.m_SnifferColumns.Count() == 0)
{
m_ChosenColumns = new List<string>() {
"Timestamp",
"ProcessId",
"ThreadId",
"UserSid",
"Task",
"Opcode",
"InterfaceUuid",
"ProcNum",
"Protocol",
"NetworkAddress",
"Endpoint",
"Params",
"AuthenticationLevel",
"ImpersonationLevel"
};
}
else
{
m_ChosenColumns = m_Settings.m_SnifferColumns;
}
DoubleClick += new EventHandler((object obj, EventArgs e2) =>
{
if (SelectedObjects == null || SelectedObjects.Count == 0)
{
return;
}
var selectedRow = SelectedObjects.Cast<ParsedEtwEvent>().ToList()[0];
});
CellRightClick += RightClickHandler;
}
public
void
ChooseColumns(Guid EtwProviderGuid)
{
var allColumns = new List<string>();
try
{
//
// Add any member of the ParsedEtwEvent class that is marked OLVColumn
//
var props = ReflectionHelper.GetOlvAttributes(typeof(ParsedEtwEvent));
allColumns.AddRange(props);
}
catch (Exception ex)
{
MessageBox.Show("Unable to reflect ParsedEtwEvent: " + ex.Message);
return;
}
try
{
//
// Add columns for custom fields inside UserData.
//
var info = EtwProviderParser.GetDistinctProviderEventInfo(
EtwProviderGuid);
if (info.ContainsKey("UserDataProperties"))
{
allColumns.AddRange(info["UserDataProperties"]);
}
}
catch (Exception ex)
{
MessageBox.Show("Unable to load columns: " +
ex.Message);
return;
}
using (var chooser = new EtwColumnPicker(
allColumns, m_ChosenColumns, m_Settings))
{
chooser.ShowDialog();
if (chooser.DialogResult == DialogResult.OK)
{
m_ChosenColumns = chooser.m_SelectedColumns;
BuildColumns();
}
}
}
public
void
SetGroupingStrategy(bool EnableGroupingByActivity)
{
if (!EnableGroupingByActivity)
{
ShowGroups = false;
return;
}
ShowGroups = true;
AlwaysGroupByColumn = AllColumns[4]; // ActivityID
BuildGroups();
}
public
void
BuildColumns()
{
//
// Add UserData columns that were chosen by the user (or the default ones)
//
var cols = new List<OLVColumn>();
foreach (var columnName in m_ChosenColumns)
{
OLVColumn col;
if (columnName == "Params")
{
//
// This is a special-case hack for Params, which is an
// array of structs. This particular property is useful
// for RPC, so we'll manually handle it. Other arrays
// are not generally handled, because we have no idea
// how many elements are there. Columns cannot be created
// randomly on the fly as data comes in, so we have to
// choose a random number of array elements to support now.
// The column names produced below are aligned with how
// EtwEventParser handles storing array elements in the
// UserDataProperties array.
//
for (int i = 0; i < 4; i++)
{
col = new OLVColumn("Params-" + i, null);
col.Renderer = new NoEllipsisRenderer();
col.Name = "Params-" + i;
col.AspectGetter = delegate (object Row)
{
if (Row == null)
{
return "";
}
var row = (ParsedEtwEvent)Row;
if (!row.UserDataProperties.ContainsKey(col.Name))
{
return "";
}
return row.UserDataProperties[col.Name];
};
col.IsVisible = true;
cols.Add(col);
}
continue;
}
col = new OLVColumn(columnName, null);
col.Renderer = new NoEllipsisRenderer();
col.Name = columnName;
col.AspectGetter = delegate (object Row)
{
if (Row == null)
{
return "";
}
var row = (ParsedEtwEvent)Row;
//
// Use reflection to try to pull the value directly from a field
// in the structure that matches this column name.
//
object value = null;
try
{
var properties = Row.GetType().GetProperties(
BindingFlags.Public | BindingFlags.Instance);
properties.ToList().ForEach(property =>
{
if (property.Name.ToLower() == col.Name.ToLower())
{
value = property.GetValue(Row, null);
}
});
}
catch (Exception) { }
//
// Next, try the UserDataProperties array.
//
if (value == null)
{
if (!row.UserDataProperties.ContainsKey(col.Name))
{
return "";
}
return row.UserDataProperties[col.Name];
}
return value;
};
col.AspectToStringConverter = delegate (object Item)
{
if (Item == null)
{
return "";
}
if (col.Name == "ProcessId" || col.Name == "ProcessID"
|| col.Name == "ThreadId")
{
return Item.ToString();
}
else if (col.Name == "UserSid")
{
}
else if (long.TryParse(Item.ToString(), out long value))
{
return "0x" + value.ToString("X");
}
return Item.ToString();
};
col.IsVisible = true;
cols.Add(col);
}
AllColumns.AddRange(cols.ToArray());
foreach (var column in AllColumns)
{
column.MaximumWidth = -1;
}
AllColumns = cols;
RebuildColumns();
}
public
void
Update(List<ParsedEtwEvent> Events)
{
SuspendLayout();
AddObjects(Events);
ResumeLayout();
}
public
new
void
Reset()
{
ClearObjects();
Groups.Clear();
}
public
void
SaveAsText()
{
if (Objects == null)
{
return;
}
var data = Objects.Cast<ParsedEtwEvent>().ToList();
if (data.Count == 0)
{
return;
}
StringBuilder sb = new StringBuilder();
data.ForEach(ev =>
{
sb.AppendLine(ev.ToString());
});
var location = Path.GetRandomFileName() + ".txt";
File.WriteAllText(location, sb.ToString());
Process.Start(location);
}
private
void
RightClickHandler(
object Obj,
CellRightClickEventArgs Args
)
{
TabPages.ContextMenu.BuildRightClickMenu(Args, new List<ToolStripMenuItem>{
new ToolStripMenuItem("Open in Library", null, ContextMenuOpenInLibrary),
});
}
private
void
ContextMenuOpenInLibrary(
object Sender,
EventArgs Args
)
{
object tag = ((ToolStripMenuItem)Sender).Tag;
if (tag == null)
{
return;
}
var args = (CellRightClickEventArgs)tag;
var evt = args.Model as ParsedEtwEvent;
if (!evt.UserDataProperties.ContainsKey("InterfaceUuid"))
{
MessageBox.Show("Unable to show RPC server details because " +
"there is no interface UUID present in the ETW event.");
return;
}
var interfaceId = evt.UserDataProperties["InterfaceUuid"].Replace(
"{", "").Replace("}", "");
m_Callbacks.ShowRpcServerDetailsCallback(interfaceId);
}
}
}

92
Windows/EtwColumnPicker.Designer.cs generated Normal file
View File

@ -0,0 +1,92 @@
namespace RpcInvestigator.Windows
{
partial class EtwColumnPicker
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(EtwColumnPicker));
this.columnsList = new System.Windows.Forms.ListBox();
this.applyButton = new System.Windows.Forms.Button();
this.saveButton = new System.Windows.Forms.Button();
this.SuspendLayout();
//
// columnsList
//
this.columnsList.FormattingEnabled = true;
this.columnsList.ItemHeight = 16;
this.columnsList.Location = new System.Drawing.Point(12, 38);
this.columnsList.MultiColumn = true;
this.columnsList.Name = "columnsList";
this.columnsList.ScrollAlwaysVisible = true;
this.columnsList.SelectionMode = System.Windows.Forms.SelectionMode.MultiSimple;
this.columnsList.Size = new System.Drawing.Size(405, 532);
this.columnsList.Sorted = true;
this.columnsList.TabIndex = 2;
//
// applyButton
//
this.applyButton.Location = new System.Drawing.Point(99, 591);
this.applyButton.Name = "applyButton";
this.applyButton.Size = new System.Drawing.Size(80, 45);
this.applyButton.TabIndex = 3;
this.applyButton.Text = "Apply";
this.applyButton.UseVisualStyleBackColor = true;
this.applyButton.Click += new System.EventHandler(this.applyButton_Click);
//
// saveButton
//
this.saveButton.Location = new System.Drawing.Point(222, 591);
this.saveButton.Name = "saveButton";
this.saveButton.Size = new System.Drawing.Size(80, 45);
this.saveButton.TabIndex = 4;
this.saveButton.Text = "Save";
this.saveButton.UseVisualStyleBackColor = true;
this.saveButton.Click += new System.EventHandler(this.saveButton_Click);
//
// EtwColumnPicker
//
this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 16F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(429, 648);
this.Controls.Add(this.saveButton);
this.Controls.Add(this.applyButton);
this.Controls.Add(this.columnsList);
this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
this.Name = "EtwColumnPicker";
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
this.Text = "Choose Columns";
this.ResumeLayout(false);
}
#endregion
private System.Windows.Forms.ListBox columnsList;
private System.Windows.Forms.Button applyButton;
private System.Windows.Forms.Button saveButton;
}
}

View File

@ -0,0 +1,72 @@
//
// Copyright (c) 2022-present, Trail of Bits, Inc.
// All rights reserved.
//
// This source code is licensed in accordance with the terms specified in
// the LICENSE file found in the root directory of this source tree.
//
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Runtime;
using System.Windows.Forms;
using static System.Windows.Forms.VisualStyles.VisualStyleElement;
namespace RpcInvestigator.Windows
{
public partial class EtwColumnPicker : Form
{
public List<string> m_SelectedColumns;
private Settings m_Settings;
public EtwColumnPicker(
List<string> AllColumns,
List<string> DefaultSelectedColumns,
Settings Settings
)
{
InitializeComponent();
m_Settings = Settings;
columnsList.DataSource = AllColumns;
for (int i = 0; i < columnsList.Items.Count; i++)
{
var item = (string)columnsList.Items[i];
if (DefaultSelectedColumns.Contains(item))
{
columnsList.SetSelected(i, true);
}
else
{
columnsList.SetSelected(i, false);
}
}
}
private void applyButton_Click(object sender, EventArgs e)
{
if (columnsList.SelectedItems == null ||
columnsList.SelectedItems.Count == 0)
{
MessageBox.Show("Please select at least one column.");
return;
}
m_SelectedColumns = columnsList.SelectedItems.Cast<string>().ToList();
DialogResult = DialogResult.OK;
Close();
}
private void saveButton_Click(object sender, EventArgs e)
{
if (columnsList.SelectedItems == null ||
columnsList.SelectedItems.Count == 0)
{
MessageBox.Show("Please select at least one column.");
return;
}
m_SelectedColumns = columnsList.SelectedItems.Cast<string>().ToList();
m_Settings.m_SnifferColumns = m_SelectedColumns;
Settings.Save(m_Settings, null);
}
}
}

3391
Windows/EtwColumnPicker.resx Normal file

File diff suppressed because it is too large Load Diff

350
Windows/MainWindow.Designer.cs generated Normal file
View File

@ -0,0 +1,350 @@
namespace RpcInvestigator
{
partial class MainWindow
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.components = new System.ComponentModel.Container();
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(MainWindow));
this.menuStrip1 = new System.Windows.Forms.MenuStrip();
this.fileToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.allRpcAlpcServersToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.loadFromBinaryToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.loadFromServiceToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.toolStripSeparator2 = new System.Windows.Forms.ToolStripSeparator();
this.exitToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.editToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.settingsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.viewToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.logsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.libraryToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.searchToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.proceduresToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.toolStripSeparator1 = new System.Windows.Forms.ToolStripSeparator();
this.refreshToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.exportAsTextToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.toolsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.rPCSnifferToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.mainTabControl = new System.Windows.Forms.TabControl();
this.imageList1 = new System.Windows.Forms.ImageList(this.components);
this.statusStrip1 = new System.Windows.Forms.StatusStrip();
this.progressBar = new System.Windows.Forms.ToolStripProgressBar();
this.statusLabel = new System.Windows.Forms.ToolStripStatusLabel();
this.imageList2 = new System.Windows.Forms.ImageList(this.components);
this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel();
this.eraseToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.menuStrip1.SuspendLayout();
this.statusStrip1.SuspendLayout();
this.tableLayoutPanel1.SuspendLayout();
this.SuspendLayout();
//
// menuStrip1
//
this.menuStrip1.ImageScalingSize = new System.Drawing.Size(20, 20);
this.menuStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.fileToolStripMenuItem,
this.editToolStripMenuItem,
this.viewToolStripMenuItem,
this.libraryToolStripMenuItem,
this.toolsToolStripMenuItem});
this.menuStrip1.Location = new System.Drawing.Point(0, 0);
this.menuStrip1.Name = "menuStrip1";
this.menuStrip1.Size = new System.Drawing.Size(1084, 28);
this.menuStrip1.TabIndex = 0;
this.menuStrip1.Text = "menuStrip1";
//
// fileToolStripMenuItem
//
this.fileToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.allRpcAlpcServersToolStripMenuItem,
this.loadFromBinaryToolStripMenuItem,
this.loadFromServiceToolStripMenuItem,
this.toolStripSeparator2,
this.exitToolStripMenuItem});
this.fileToolStripMenuItem.Name = "fileToolStripMenuItem";
this.fileToolStripMenuItem.Size = new System.Drawing.Size(46, 24);
this.fileToolStripMenuItem.Text = "File";
//
// allRpcAlpcServersToolStripMenuItem
//
this.allRpcAlpcServersToolStripMenuItem.Name = "allRpcAlpcServersToolStripMenuItem";
this.allRpcAlpcServersToolStripMenuItem.Size = new System.Drawing.Size(236, 26);
this.allRpcAlpcServersToolStripMenuItem.Text = "All RPC ALPC servers...";
this.allRpcAlpcServersToolStripMenuItem.Click += new System.EventHandler(this.loadAllRPCALPCServersToolStripMenuItem_Click);
//
// loadFromBinaryToolStripMenuItem
//
this.loadFromBinaryToolStripMenuItem.Name = "loadFromBinaryToolStripMenuItem";
this.loadFromBinaryToolStripMenuItem.Size = new System.Drawing.Size(236, 26);
this.loadFromBinaryToolStripMenuItem.Text = "Load from binary...";
this.loadFromBinaryToolStripMenuItem.Click += new System.EventHandler(this.loadFromBinaryToolStripMenuItem_Click);
//
// loadFromServiceToolStripMenuItem
//
this.loadFromServiceToolStripMenuItem.Name = "loadFromServiceToolStripMenuItem";
this.loadFromServiceToolStripMenuItem.Size = new System.Drawing.Size(236, 26);
this.loadFromServiceToolStripMenuItem.Text = "Load from service...";
this.loadFromServiceToolStripMenuItem.Click += new System.EventHandler(this.loadFromServiceToolStripMenuItem_Click);
//
// toolStripSeparator2
//
this.toolStripSeparator2.Name = "toolStripSeparator2";
this.toolStripSeparator2.Size = new System.Drawing.Size(233, 6);
//
// exitToolStripMenuItem
//
this.exitToolStripMenuItem.Name = "exitToolStripMenuItem";
this.exitToolStripMenuItem.Size = new System.Drawing.Size(236, 26);
this.exitToolStripMenuItem.Text = "Exit";
this.exitToolStripMenuItem.Click += new System.EventHandler(this.exitToolStripMenuItem_Click);
//
// editToolStripMenuItem
//
this.editToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.settingsToolStripMenuItem});
this.editToolStripMenuItem.Name = "editToolStripMenuItem";
this.editToolStripMenuItem.Size = new System.Drawing.Size(49, 24);
this.editToolStripMenuItem.Text = "Edit";
//
// settingsToolStripMenuItem
//
this.settingsToolStripMenuItem.Name = "settingsToolStripMenuItem";
this.settingsToolStripMenuItem.Size = new System.Drawing.Size(154, 26);
this.settingsToolStripMenuItem.Text = "Settings...";
this.settingsToolStripMenuItem.Click += new System.EventHandler(this.settingsToolStripMenuItem_Click);
//
// viewToolStripMenuItem
//
this.viewToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.logsToolStripMenuItem});
this.viewToolStripMenuItem.Name = "viewToolStripMenuItem";
this.viewToolStripMenuItem.Size = new System.Drawing.Size(55, 24);
this.viewToolStripMenuItem.Text = "View";
//
// logsToolStripMenuItem
//
this.logsToolStripMenuItem.Name = "logsToolStripMenuItem";
this.logsToolStripMenuItem.Size = new System.Drawing.Size(132, 26);
this.logsToolStripMenuItem.Text = "Logs...";
this.logsToolStripMenuItem.Click += new System.EventHandler(this.logsToolStripMenuItem_Click);
//
// libraryToolStripMenuItem
//
this.libraryToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.searchToolStripMenuItem,
this.proceduresToolStripMenuItem,
this.toolStripSeparator1,
this.refreshToolStripMenuItem,
this.exportAsTextToolStripMenuItem,
this.eraseToolStripMenuItem});
this.libraryToolStripMenuItem.Name = "libraryToolStripMenuItem";
this.libraryToolStripMenuItem.Size = new System.Drawing.Size(68, 24);
this.libraryToolStripMenuItem.Text = "Library";
//
// searchToolStripMenuItem
//
this.searchToolStripMenuItem.Name = "searchToolStripMenuItem";
this.searchToolStripMenuItem.Size = new System.Drawing.Size(224, 26);
this.searchToolStripMenuItem.Text = "Servers";
this.searchToolStripMenuItem.Click += new System.EventHandler(this.libraryServersToolStripMenuItem_Click);
//
// proceduresToolStripMenuItem
//
this.proceduresToolStripMenuItem.Name = "proceduresToolStripMenuItem";
this.proceduresToolStripMenuItem.Size = new System.Drawing.Size(224, 26);
this.proceduresToolStripMenuItem.Text = "Procedures";
this.proceduresToolStripMenuItem.Click += new System.EventHandler(this.libraryProceduresToolStripMenuItem_Click);
//
// toolStripSeparator1
//
this.toolStripSeparator1.Name = "toolStripSeparator1";
this.toolStripSeparator1.Size = new System.Drawing.Size(221, 6);
//
// refreshToolStripMenuItem
//
this.refreshToolStripMenuItem.Name = "refreshToolStripMenuItem";
this.refreshToolStripMenuItem.Size = new System.Drawing.Size(224, 26);
this.refreshToolStripMenuItem.Text = "Refresh";
this.refreshToolStripMenuItem.Click += new System.EventHandler(this.refreshLibraryToolStripMenuItem_Click);
//
// exportAsTextToolStripMenuItem
//
this.exportAsTextToolStripMenuItem.Name = "exportAsTextToolStripMenuItem";
this.exportAsTextToolStripMenuItem.Size = new System.Drawing.Size(224, 26);
this.exportAsTextToolStripMenuItem.Text = "Export as Text";
this.exportAsTextToolStripMenuItem.Click += new System.EventHandler(this.exportLibraryAsTextToolStripMenuItem_Click);
//
// toolsToolStripMenuItem
//
this.toolsToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.rPCSnifferToolStripMenuItem});
this.toolsToolStripMenuItem.Name = "toolsToolStripMenuItem";
this.toolsToolStripMenuItem.Size = new System.Drawing.Size(58, 24);
this.toolsToolStripMenuItem.Text = "Tools";
//
// rPCSnifferToolStripMenuItem
//
this.rPCSnifferToolStripMenuItem.Name = "rPCSnifferToolStripMenuItem";
this.rPCSnifferToolStripMenuItem.Size = new System.Drawing.Size(172, 26);
this.rPCSnifferToolStripMenuItem.Text = "RPC sniffer...";
this.rPCSnifferToolStripMenuItem.Click += new System.EventHandler(this.rPCSnifferToolStripMenuItem_Click);
//
// mainTabControl
//
this.mainTabControl.Dock = System.Windows.Forms.DockStyle.Fill;
this.mainTabControl.DrawMode = System.Windows.Forms.TabDrawMode.OwnerDrawFixed;
this.mainTabControl.ImageList = this.imageList1;
this.mainTabControl.Location = new System.Drawing.Point(3, 48);
this.mainTabControl.Name = "mainTabControl";
this.mainTabControl.Padding = new System.Drawing.Point(9, 3);
this.mainTabControl.SelectedIndex = 0;
this.mainTabControl.Size = new System.Drawing.Size(1078, 671);
this.mainTabControl.TabIndex = 0;
this.mainTabControl.DrawItem += new System.Windows.Forms.DrawItemEventHandler(this.mainTabControl_DrawItem);
this.mainTabControl.MouseDown += new System.Windows.Forms.MouseEventHandler(this.mainTabControl_MouseDown);
//
// imageList1
//
this.imageList1.ImageStream = ((System.Windows.Forms.ImageListStreamer)(resources.GetObject("imageList1.ImageStream")));
this.imageList1.TransparentColor = System.Drawing.Color.Transparent;
this.imageList1.Images.SetKeyName(0, "service.png");
this.imageList1.Images.SetKeyName(1, "endpoint.jpg");
this.imageList1.Images.SetKeyName(2, "binary.png");
this.imageList1.Images.SetKeyName(3, "search.jpg");
this.imageList1.Images.SetKeyName(4, "greendot.png");
this.imageList1.Images.SetKeyName(5, "reddot.png");
//
// statusStrip1
//
this.statusStrip1.Dock = System.Windows.Forms.DockStyle.None;
this.statusStrip1.ImageScalingSize = new System.Drawing.Size(20, 20);
this.statusStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.progressBar,
this.statusLabel});
this.statusStrip1.Location = new System.Drawing.Point(0, 722);
this.statusStrip1.MaximumSize = new System.Drawing.Size(0, 35);
this.statusStrip1.Name = "statusStrip1";
this.statusStrip1.Size = new System.Drawing.Size(22, 22);
this.statusStrip1.SizingGrip = false;
this.statusStrip1.TabIndex = 1;
this.statusStrip1.Text = "statusStrip1";
//
// progressBar
//
this.progressBar.Name = "progressBar";
this.progressBar.Size = new System.Drawing.Size(100, 27);
this.progressBar.Visible = false;
//
// statusLabel
//
this.statusLabel.Name = "statusLabel";
this.statusLabel.Padding = new System.Windows.Forms.Padding(5, 0, 0, 0);
this.statusLabel.Size = new System.Drawing.Size(5, 16);
//
// imageList2
//
this.imageList2.ImageStream = ((System.Windows.Forms.ImageListStreamer)(resources.GetObject("imageList2.ImageStream")));
this.imageList2.TransparentColor = System.Drawing.Color.Transparent;
this.imageList2.Images.SetKeyName(0, "x-icon.png");
//
// tableLayoutPanel1
//
this.tableLayoutPanel1.ColumnCount = 1;
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F));
this.tableLayoutPanel1.Controls.Add(this.menuStrip1, 0, 0);
this.tableLayoutPanel1.Controls.Add(this.mainTabControl, 0, 1);
this.tableLayoutPanel1.Controls.Add(this.statusStrip1, 0, 2);
this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill;
this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 0);
this.tableLayoutPanel1.Name = "tableLayoutPanel1";
this.tableLayoutPanel1.RowCount = 3;
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 6F));
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 89F));
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 5F));
this.tableLayoutPanel1.Size = new System.Drawing.Size(1084, 761);
this.tableLayoutPanel1.TabIndex = 2;
//
// eraseToolStripMenuItem
//
this.eraseToolStripMenuItem.Name = "eraseToolStripMenuItem";
this.eraseToolStripMenuItem.Size = new System.Drawing.Size(224, 26);
this.eraseToolStripMenuItem.Text = "Erase";
this.eraseToolStripMenuItem.Click += new System.EventHandler(this.eraseToolStripMenuItem_Click);
//
// MainWindow
//
this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 16F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(1084, 761);
this.Controls.Add(this.tableLayoutPanel1);
this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
this.MainMenuStrip = this.menuStrip1;
this.Name = "MainWindow";
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
this.Text = "RPC Investigator";
this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.MainWindow_FormClosing);
this.menuStrip1.ResumeLayout(false);
this.menuStrip1.PerformLayout();
this.statusStrip1.ResumeLayout(false);
this.statusStrip1.PerformLayout();
this.tableLayoutPanel1.ResumeLayout(false);
this.tableLayoutPanel1.PerformLayout();
this.ResumeLayout(false);
}
#endregion
private System.Windows.Forms.MenuStrip menuStrip1;
private System.Windows.Forms.ToolStripMenuItem fileToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem exitToolStripMenuItem;
private System.Windows.Forms.TabControl mainTabControl;
private System.Windows.Forms.ToolStripMenuItem loadFromBinaryToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem loadFromServiceToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem editToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem settingsToolStripMenuItem;
private System.Windows.Forms.StatusStrip statusStrip1;
private System.Windows.Forms.ToolStripStatusLabel statusLabel;
private System.Windows.Forms.ImageList imageList1;
private System.Windows.Forms.ToolStripMenuItem allRpcAlpcServersToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem viewToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem logsToolStripMenuItem;
private System.Windows.Forms.ImageList imageList2;
private System.Windows.Forms.ToolStripProgressBar progressBar;
private System.Windows.Forms.ToolStripSeparator toolStripSeparator2;
private System.Windows.Forms.ToolStripMenuItem libraryToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem searchToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem refreshToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem exportAsTextToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem proceduresToolStripMenuItem;
private System.Windows.Forms.ToolStripSeparator toolStripSeparator1;
private System.Windows.Forms.ToolStripMenuItem toolsToolStripMenuItem;
private System.Windows.Forms.ToolStripMenuItem rPCSnifferToolStripMenuItem;
private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1;
private System.Windows.Forms.ToolStripMenuItem eraseToolStripMenuItem;
}
}

351
Windows/MainWindow.cs Normal file
View File

@ -0,0 +1,351 @@
//
// Copyright (c) 2022-present, Trail of Bits, Inc.
// All rights reserved.
//
// This source code is licensed in accordance with the terms specified in
// the LICENSE file found in the root directory of this source tree.
//
using RpcInvestigator.TabPages;
using System;
using System.Collections.Generic;
using System.Collections;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Windows.Forms;
using RpcInvestigator.Windows;
using static RpcInvestigator.TraceLogger;
namespace RpcInvestigator
{
public partial class MainWindow : Form
{
private Settings m_Settings;
private RpcLibrary m_Library;
private TabManager m_TabManager;
private Sniffer m_RpcSnifferWindow;
public MainWindow()
{
InitializeComponent();
DoubleBuffered = true;
TraceLogger.Initialize();
//
// Load settings and database
//
try
{
m_Settings = Settings.LoadDefault();
TraceLogger.SetLevel(m_Settings.m_TraceLevel);
}
catch (Exception) { } // swallow
try
{
m_Library = new RpcLibrary(null);
m_Library.Load();
}
catch (Exception) { } // swallow
m_TabManager = new TabManager(
mainTabControl, m_Settings, m_Library, progressBar, statusLabel);
}
private void MainWindow_FormClosing(object sender, FormClosingEventArgs e)
{
if (m_RpcSnifferWindow != null)
{
MessageBox.Show("Please close the RPC sniffer window.");
e.Cancel = true;
return;
}
}
private void mainTabControl_DrawItem(object sender, DrawItemEventArgs e)
{
TabPage thisTab = mainTabControl.TabPages[e.Index];
string tabTitle = thisTab.Text;
Rectangle tabRect = mainTabControl.GetTabRect(e.Index);
tabRect.Inflate(-2, -2);
//
// Draw the icon for the tab's selected icon image.
//
var icon = imageList1.Images[thisTab.ImageIndex];
e.Graphics.DrawImage(icon,
(tabRect.Left + 5),
tabRect.Top + (tabRect.Height - icon.Height) / 2);
//
// Draw the close tab button
//
Image closeIcon = imageList2.Images[0];
e.Graphics.DrawImage(closeIcon,
(tabRect.Right - icon.Width) + 5,
tabRect.Top + (tabRect.Height - closeIcon.Height) / 2);
//
// Draw the tab title
//
var textRect = new Rectangle(tabRect.X + icon.Width + 5, tabRect.Y,
tabRect.Width - icon.Width - closeIcon.Width, tabRect.Height);
TextRenderer.DrawText(e.Graphics, tabTitle, thisTab.Font,
textRect, thisTab.ForeColor, TextFormatFlags.Left);
}
private void mainTabControl_MouseDown(object sender, MouseEventArgs e)
{
for (int i = 0; i < mainTabControl.TabCount; i++)
{
Rectangle tabRect = mainTabControl.GetTabRect(i);
tabRect.Inflate(-2, -2);
Image closeIcon = imageList2.Images[0];
var imageRect = new Rectangle(
(tabRect.Right - closeIcon.Width - 5),
tabRect.Top + (tabRect.Height - closeIcon.Height - 5) / 2,
closeIcon.Width + 5,
closeIcon.Height + 5);
if (imageRect.Contains(e.Location))
{
if (i > 0)
{
mainTabControl.SelectedIndex = i - 1;
}
mainTabControl.TabPages.RemoveAt(i);
break;
}
}
}
private void ToggleMenu(bool Enable)
{
menuStrip1.Enabled = Enable;
mainTabControl.Enabled = Enable;
}
#region Menu actions
private void exitToolStripMenuItem_Click(object sender, EventArgs e)
{
Close();
}
private void settingsToolStripMenuItem_Click(object sender, EventArgs e)
{
var window = new SettingsWindow(m_Settings);
if (window.ShowDialog() == DialogResult.OK)
{
m_Settings = window.m_Settings;
TraceLogger.SetLevel(m_Settings.m_TraceLevel);
}
}
private void loadFromBinaryToolStripMenuItem_Click(object sender, EventArgs e)
{
OpenFileDialog dialog = new OpenFileDialog();
dialog.CheckFileExists = true;
dialog.CheckPathExists = true;
dialog.Multiselect = false;
if (dialog.ShowDialog() != DialogResult.OK)
{
return;
}
ToggleMenu(false);
_ = m_TabManager.LoadRpcServersTab(new List<string>() { dialog.FileName });
ToggleMenu(true);
}
private void loadFromServiceToolStripMenuItem_Click(object sender, EventArgs e)
{
Services servicesForm = new Services(m_Settings);
if (servicesForm.ShowDialog() != DialogResult.OK)
{
return;
}
if (servicesForm.m_SelectedDlls.Count == 0)
{
return;
}
ToggleMenu(false);
_ = m_TabManager.LoadRpcServersTab(servicesForm.m_SelectedDlls);
ToggleMenu(true);
}
private void loadAllRPCALPCServersToolStripMenuItem_Click(object sender, EventArgs e)
{
ToggleMenu(false);
_ = m_TabManager.LoadRpcAlpcServersTab();
ToggleMenu(true);
}
private void logsToolStripMenuItem_Click(object sender, EventArgs e)
{
Process.Start(Settings.m_LogDir);
}
private async void libraryServersToolStripMenuItem_Click(object sender, EventArgs e)
{
if (DetectEmptyLibrary())
{
return;
}
ToggleMenu(false);
_ = await m_TabManager.LoadRpcLibraryServersTab(new RpcLibraryFilter());
ToggleMenu(true);
}
private async void libraryProceduresToolStripMenuItem_Click(object sender, EventArgs e)
{
if (DetectEmptyLibrary())
{
return;
}
ToggleMenu(false);
_ = await m_TabManager.LoadRpcLibraryProceduresTab(new RpcLibraryFilter());
ToggleMenu(true);
}
private void eraseToolStripMenuItem_Click(object sender, EventArgs e)
{
if (DetectEmptyLibrary())
{
return;
}
DialogResult result = MessageBox.Show(
"Are you sure you want to erase the library?",
"Confirmation",
MessageBoxButtons.YesNoCancel);
if (result != DialogResult.Yes)
{
return;
}
ToggleMenu(false);
try
{
m_Library.Clear();
m_Library.Save();
Trace(TraceLoggerType.RpcLibrary,
TraceEventType.Information,
"Database erased.");
}
catch (Exception ex)
{
MessageBox.Show("An error occurred when erasing the database: " +
ex.Message);
}
ToggleMenu(true);
}
private async void refreshLibraryToolStripMenuItem_Click(object sender, EventArgs e)
{
ToggleMenu(false);
int workSize = 0;
int max = 0;
_ = await m_Library.Refresh(
m_Settings,
//
// Initialize progress bar.
//
(ArrayList args) =>
{
statusStrip1.Invoke((MethodInvoker)(() =>
{
workSize = (int)args[0];
max = (int)args[1];
var message = (string)args[2];
progressBar.Visible = true;
progressBar.Step = 1;
progressBar.Value = 0;
progressBar.Visible = true;
progressBar.Maximum = max;
statusLabel.Text = message;
}));
},
//
// Update progress bar
//
(string message) =>
{
statusStrip1.Invoke((MethodInvoker)(() =>
{
progressBar.PerformStep();
statusLabel.Text = message.Replace("<current>",
(progressBar.Value * workSize).ToString()).Replace(
"<total>", (max * workSize).ToString());
}));
},
//
// Complete
//
(string message) =>
{
statusLabel.Text = message;
progressBar.Value = 0;
progressBar.Visible = false;
});
ToggleMenu(true);
}
private void exportLibraryAsTextToolStripMenuItem_Click(object sender, EventArgs e)
{
if (DetectEmptyLibrary())
{
return;
}
ToggleMenu(false);
var location = Path.Combine(new string[] { Settings.m_LogDir,
"library-text-"+Path.GetRandomFileName() + ".txt" });
File.WriteAllText(location, m_Library.ToString());
Process.Start(location);
ToggleMenu(true);
}
private void rPCSnifferToolStripMenuItem_Click(object sender, EventArgs e)
{
if (m_RpcSnifferWindow != null)
{
MessageBox.Show("Only one RPC sniffer window is allowed.");
return;
}
m_RpcSnifferWindow = new Sniffer(
m_Library,
new SnifferCallbackTable()
{
ShowRpcServerDetailsCallback = SnifferCallbackShowRpcServerDetails,
},
ref m_Settings);
m_RpcSnifferWindow.Show();
m_RpcSnifferWindow.FormClosed += delegate (object w, FormClosedEventArgs args) {
m_RpcSnifferWindow = null;
};
}
private async void SnifferCallbackShowRpcServerDetails(string InterfaceId)
{
if (DetectEmptyLibrary())
{
return;
}
if (!Guid.TryParse(InterfaceId, out Guid id))
{
MessageBox.Show(InterfaceId + " is not a valid GUID.");
return;
}
ToggleMenu(false);
var tab = await m_TabManager.LoadRpcLibraryServersTab(new RpcLibraryFilter());
tab.ScrollToServer(id);
ToggleMenu(true);
}
private bool DetectEmptyLibrary()
{
if (m_Library.GetServerCount() == 0)
{
MessageBox.Show("The library is empty. Please click the Refresh menu " +
"item under the Library menu to regenerate it.");
return true;
}
return false;
}
#endregion
}
}

3551
Windows/MainWindow.resx Normal file

File diff suppressed because it is too large Load Diff

123
Windows/Services.Designer.cs generated Normal file
View File

@ -0,0 +1,123 @@
namespace RpcInvestigator
{
partial class Services
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(Services));
this.splitContainer1 = new System.Windows.Forms.SplitContainer();
this.servicesListview = new BrightIdeasSoftware.FastObjectListView();
this.selectAllButton = new System.Windows.Forms.Button();
this.goButton = new System.Windows.Forms.Button();
((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).BeginInit();
this.splitContainer1.Panel1.SuspendLayout();
this.splitContainer1.Panel2.SuspendLayout();
this.splitContainer1.SuspendLayout();
((System.ComponentModel.ISupportInitialize)(this.servicesListview)).BeginInit();
this.SuspendLayout();
//
// splitContainer1
//
this.splitContainer1.Dock = System.Windows.Forms.DockStyle.Fill;
this.splitContainer1.Location = new System.Drawing.Point(0, 0);
this.splitContainer1.Name = "splitContainer1";
this.splitContainer1.Orientation = System.Windows.Forms.Orientation.Horizontal;
//
// splitContainer1.Panel1
//
this.splitContainer1.Panel1.Controls.Add(this.servicesListview);
//
// splitContainer1.Panel2
//
this.splitContainer1.Panel2.Controls.Add(this.selectAllButton);
this.splitContainer1.Panel2.Controls.Add(this.goButton);
this.splitContainer1.Size = new System.Drawing.Size(1371, 815);
this.splitContainer1.SplitterDistance = 749;
this.splitContainer1.TabIndex = 0;
//
// servicesListview
//
this.servicesListview.Dock = System.Windows.Forms.DockStyle.Fill;
this.servicesListview.GridLines = true;
this.servicesListview.HideSelection = false;
this.servicesListview.Location = new System.Drawing.Point(0, 0);
this.servicesListview.Name = "servicesListview";
this.servicesListview.ShowGroups = false;
this.servicesListview.Size = new System.Drawing.Size(1371, 749);
this.servicesListview.TabIndex = 0;
this.servicesListview.UseCompatibleStateImageBehavior = false;
this.servicesListview.View = System.Windows.Forms.View.Details;
this.servicesListview.VirtualMode = true;
//
// selectAllButton
//
this.selectAllButton.Location = new System.Drawing.Point(496, 13);
this.selectAllButton.Name = "selectAllButton";
this.selectAllButton.Size = new System.Drawing.Size(154, 37);
this.selectAllButton.TabIndex = 1;
this.selectAllButton.Text = "Select All";
this.selectAllButton.UseVisualStyleBackColor = true;
this.selectAllButton.Click += new System.EventHandler(this.selectAllButton_Click);
//
// goButton
//
this.goButton.Location = new System.Drawing.Point(675, 13);
this.goButton.Name = "goButton";
this.goButton.Size = new System.Drawing.Size(154, 37);
this.goButton.TabIndex = 0;
this.goButton.Text = "Go";
this.goButton.UseVisualStyleBackColor = true;
this.goButton.Click += new System.EventHandler(this.goButton_Click);
//
// Services
//
this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 16F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(1371, 815);
this.Controls.Add(this.splitContainer1);
this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
this.Name = "Services";
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
this.Text = "Services";
this.Shown += new System.EventHandler(this.Services_Shown);
this.splitContainer1.Panel1.ResumeLayout(false);
this.splitContainer1.Panel2.ResumeLayout(false);
((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).EndInit();
this.splitContainer1.ResumeLayout(false);
((System.ComponentModel.ISupportInitialize)(this.servicesListview)).EndInit();
this.ResumeLayout(false);
}
#endregion
private System.Windows.Forms.SplitContainer splitContainer1;
private BrightIdeasSoftware.FastObjectListView servicesListview;
private System.Windows.Forms.Button goButton;
private System.Windows.Forms.Button selectAllButton;
}
}

159
Windows/Services.cs Normal file
View File

@ -0,0 +1,159 @@
//
// Copyright (c) 2022-present, Trail of Bits, Inc.
// All rights reserved.
//
// This source code is licensed in accordance with the terms specified in
// the LICENSE file found in the root directory of this source tree.
//
using System;
using System.Collections.Generic;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
using NtApiDotNet.Win32.Service;
using BrightIdeasSoftware;
using NtApiDotNet.Win32;
using System.Diagnostics;
namespace RpcInvestigator
{
using static TraceLogger;
public partial class Services : Form
{
public List<string> m_SelectedDlls = new List<string>();
private Settings m_Settings;
public Services(Settings Settings)
{
InitializeComponent();
m_Settings = Settings;
}
private void Services_Shown(object sender, EventArgs e)
{
servicesListview.BorderStyle = BorderStyle.FixedSingle;
servicesListview.Cursor = Cursors.Default;
servicesListview.Dock = DockStyle.Fill;
servicesListview.FullRowSelect = true;
servicesListview.GridLines = true;
servicesListview.MultiSelect = true;
servicesListview.HideSelection = false;
servicesListview.Location = new Point(0, 0);
servicesListview.Margin = new Padding(5);
servicesListview.Name = "selectedServicesListview";
servicesListview.UseAlternatingBackColors = true;
servicesListview.AlternateRowBackColor = Color.LightBlue;
servicesListview.View = View.Details;
servicesListview.VirtualMode = true;
servicesListview.Alignment = ListViewAlignment.Left;
servicesListview.ShowGroups = false;
IEnumerable<Win32Service> services;
try
{
var scm = ServiceControlManager.Open(
"localhost", NtApiDotNet.Win32.ServiceControlManagerAccessRights.All);
services = scm.GetServices(
NtApiDotNet.Win32.ServiceState.Active, (NtApiDotNet.Win32.ServiceType)0x3ff);
}
catch (Exception ex)
{
MessageBox.Show("Unable to get service list: " + ex.Message);
return;
}
if (services.Count() == 0)
{
MessageBox.Show("No services found.");
return;
}
Generator.GenerateColumns(servicesListview, typeof(Win32Service), true);
foreach (var column in servicesListview.AllColumns)
{
if (column.Name.ToLower() == "triggers" ||
column.Name.ToLower() == "requiredprivileges" ||
column.Name.ToLower() == "dependencies")
{
column.IsVisible = false;
}
column.MaximumWidth = -1;
}
servicesListview.SetObjects(services);
servicesListview.RebuildColumns();
servicesListview.AutoResizeColumns();
}
private void goButton_Click(object sender, EventArgs e)
{
if (servicesListview.SelectedObjects == null ||
servicesListview.SelectedObjects.Count == 0)
{
return;
}
var selectedRows = servicesListview.SelectedObjects.Cast<
Win32Service>().ToList();
foreach (var row in selectedRows)
{
try
{
if (row.ProcessId == 0 || row.ProcessId == 4)
{
continue;
}
var process = Process.GetProcessById(row.ProcessId);
if (process.Modules == null || process.Modules.Count == 0)
{
Trace(TraceLoggerType.ServicesWindow,
TraceEventType.Warning,
"Process " + process.ProcessName + " has " +
"no loaded modules.");
continue;
}
process.Modules.Cast<ProcessModule>().ToList().ForEach(
module => m_SelectedDlls.Add(module.FileName));
m_SelectedDlls = m_SelectedDlls.Distinct().ToList();
}
catch (Exception ex)
{
Trace(TraceLoggerType.ServicesWindow,
TraceEventType.Error,
"Unable to open process with PID=" +
row.ProcessId + ": " + ex.Message);
}
}
var count = m_SelectedDlls.Count;
if (count > 0)
{
DialogResult result = MessageBox.Show(
"RPC Investigator found "+count+" DLLs loaded in the selected service processes. Continue?",
"Confirm Action",
MessageBoxButtons.YesNoCancel);
if (result == DialogResult.Yes)
{
DialogResult = DialogResult.OK;
Close();
return;
}
m_SelectedDlls.Clear();
}
else
{
MessageBox.Show("No DLLs were discovered for the selected services.");
}
}
private void selectAllButton_Click(object sender, EventArgs e)
{
servicesListview.SelectAll();
}
}
}

3391
Windows/Services.resx Normal file

File diff suppressed because it is too large Load Diff

151
Windows/SettingsWindow.Designer.cs generated Normal file
View File

@ -0,0 +1,151 @@
namespace RpcInvestigator
{
partial class SettingsWindow
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(SettingsWindow));
this.label1 = new System.Windows.Forms.Label();
this.dbghelpPath = new System.Windows.Forms.TextBox();
this.browseButton1 = new System.Windows.Forms.Button();
this.symbolPath = new System.Windows.Forms.TextBox();
this.label2 = new System.Windows.Forms.Label();
this.saveButton = new System.Windows.Forms.Button();
this.traceLevelComboBox = new System.Windows.Forms.ComboBox();
this.label3 = new System.Windows.Forms.Label();
this.SuspendLayout();
//
// label1
//
this.label1.AutoSize = true;
this.label1.Location = new System.Drawing.Point(12, 25);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(79, 16);
this.label1.TabIndex = 0;
this.label1.Text = "Dbghelp.dll:";
//
// dbghelpPath
//
this.dbghelpPath.Location = new System.Drawing.Point(107, 22);
this.dbghelpPath.Name = "dbghelpPath";
this.dbghelpPath.Size = new System.Drawing.Size(386, 22);
this.dbghelpPath.TabIndex = 1;
//
// browseButton1
//
this.browseButton1.Location = new System.Drawing.Point(499, 21);
this.browseButton1.Name = "browseButton1";
this.browseButton1.Size = new System.Drawing.Size(75, 23);
this.browseButton1.TabIndex = 2;
this.browseButton1.Text = "Browse...";
this.browseButton1.UseVisualStyleBackColor = true;
this.browseButton1.Click += new System.EventHandler(this.browseButton1_Click);
//
// symbolPath
//
this.symbolPath.Location = new System.Drawing.Point(107, 70);
this.symbolPath.Name = "symbolPath";
this.symbolPath.Size = new System.Drawing.Size(386, 22);
this.symbolPath.TabIndex = 4;
//
// label2
//
this.label2.AutoSize = true;
this.label2.Location = new System.Drawing.Point(12, 73);
this.label2.Name = "label2";
this.label2.Size = new System.Drawing.Size(86, 16);
this.label2.TabIndex = 3;
this.label2.Text = "Symbol Path:";
//
// saveButton
//
this.saveButton.Location = new System.Drawing.Point(260, 518);
this.saveButton.Name = "saveButton";
this.saveButton.Size = new System.Drawing.Size(93, 54);
this.saveButton.TabIndex = 5;
this.saveButton.Text = "Save";
this.saveButton.UseVisualStyleBackColor = true;
this.saveButton.Click += new System.EventHandler(this.saveButton_Click);
//
// traceLevelComboBox
//
this.traceLevelComboBox.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
this.traceLevelComboBox.FormattingEnabled = true;
this.traceLevelComboBox.Items.AddRange(new object[] {
"Off",
"Error",
"Warning",
"Information",
"Verbose"});
this.traceLevelComboBox.Location = new System.Drawing.Point(107, 115);
this.traceLevelComboBox.Name = "traceLevelComboBox";
this.traceLevelComboBox.Size = new System.Drawing.Size(180, 24);
this.traceLevelComboBox.TabIndex = 6;
//
// label3
//
this.label3.AutoSize = true;
this.label3.Location = new System.Drawing.Point(12, 118);
this.label3.Name = "label3";
this.label3.Size = new System.Drawing.Size(82, 16);
this.label3.TabIndex = 7;
this.label3.Text = "Trace Level:";
//
// SettingsWindow
//
this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 16F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(613, 584);
this.Controls.Add(this.label3);
this.Controls.Add(this.traceLevelComboBox);
this.Controls.Add(this.saveButton);
this.Controls.Add(this.symbolPath);
this.Controls.Add(this.label2);
this.Controls.Add(this.browseButton1);
this.Controls.Add(this.dbghelpPath);
this.Controls.Add(this.label1);
this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
this.Name = "SettingsWindow";
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
this.Text = "Settings";
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.Label label1;
private System.Windows.Forms.TextBox dbghelpPath;
private System.Windows.Forms.Button browseButton1;
private System.Windows.Forms.TextBox symbolPath;
private System.Windows.Forms.Label label2;
private System.Windows.Forms.Button saveButton;
private System.Windows.Forms.ComboBox traceLevelComboBox;
private System.Windows.Forms.Label label3;
}
}

69
Windows/SettingsWindow.cs Normal file
View File

@ -0,0 +1,69 @@
//
// Copyright (c) 2022-present, Trail of Bits, Inc.
// All rights reserved.
//
// This source code is licensed in accordance with the terms specified in
// the LICENSE file found in the root directory of this source tree.
//
using System;
using System.Diagnostics;
using System.Windows.Forms;
namespace RpcInvestigator
{
public partial class SettingsWindow : Form
{
public Settings m_Settings;
public SettingsWindow(Settings CurrentSettings)
{
InitializeComponent();
m_Settings = CurrentSettings;
if (!string.IsNullOrEmpty(m_Settings.m_DbghelpPath))
{
dbghelpPath.Text = m_Settings.m_DbghelpPath;
}
if (!string.IsNullOrEmpty(m_Settings.m_SymbolPath))
{
symbolPath.Text = m_Settings.m_SymbolPath;
}
foreach (var item in traceLevelComboBox.Items)
{
if (!Enum.TryParse((string)item, out SourceLevels value))
{
continue;
}
if (value == m_Settings.m_TraceLevel)
{
traceLevelComboBox.SelectedItem = item;
break;
}
}
}
private void browseButton1_Click(object sender, EventArgs e)
{
OpenFileDialog dialog = new OpenFileDialog();
dialog.CheckFileExists = true;
dialog.CheckPathExists = true;
dialog.Multiselect = false;
if (dialog.ShowDialog() != System.Windows.Forms.DialogResult.OK)
{
return;
}
m_Settings.m_DbghelpPath = dialog.FileName;
}
private void saveButton_Click(object sender, EventArgs e)
{
DialogResult = DialogResult.OK;
m_Settings.m_SymbolPath = symbolPath.Text;
m_Settings.m_DbghelpPath = dbghelpPath.Text;
Enum.TryParse((string)traceLevelComboBox.SelectedItem, out m_Settings.m_TraceLevel);
Settings.Save(m_Settings, null);
Close();
}
}
}

3391
Windows/SettingsWindow.resx Normal file

File diff suppressed because it is too large Load Diff

246
Windows/Sniffer.Designer.cs generated Normal file
View File

@ -0,0 +1,246 @@
namespace RpcInvestigator.Windows
{
partial class Sniffer
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(Sniffer));
this.toolStrip1 = new System.Windows.Forms.ToolStrip();
this.startButton = new System.Windows.Forms.ToolStripButton();
this.enableDebugEventsButton = new System.Windows.Forms.ToolStripButton();
this.chooseColumnsButton = new System.Windows.Forms.ToolStripButton();
this.toolStripSeparator1 = new System.Windows.Forms.ToolStripSeparator();
this.groupByActivityButton = new System.Windows.Forms.ToolStripButton();
this.nodeViewButton = new System.Windows.Forms.ToolStripButton();
this.toolStripSeparator2 = new System.Windows.Forms.ToolStripSeparator();
this.saveButton = new System.Windows.Forms.ToolStripButton();
this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel();
this.statusStrip1 = new System.Windows.Forms.StatusStrip();
this.toolStripProgressBar1 = new System.Windows.Forms.ToolStripProgressBar();
this.toolStripStatusLabel1 = new System.Windows.Forms.ToolStripStatusLabel();
this.elementHost1 = new System.Windows.Forms.Integration.ElementHost();
this.toolStrip1.SuspendLayout();
this.tableLayoutPanel1.SuspendLayout();
this.statusStrip1.SuspendLayout();
this.SuspendLayout();
//
// toolStrip1
//
this.toolStrip1.Dock = System.Windows.Forms.DockStyle.Fill;
this.toolStrip1.ImageScalingSize = new System.Drawing.Size(20, 20);
this.toolStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.startButton,
this.enableDebugEventsButton,
this.chooseColumnsButton,
this.toolStripSeparator1,
this.groupByActivityButton,
this.nodeViewButton,
this.toolStripSeparator2,
this.saveButton});
this.toolStrip1.Location = new System.Drawing.Point(0, 0);
this.toolStrip1.Name = "toolStrip1";
this.toolStrip1.Padding = new System.Windows.Forms.Padding(0, 0, 2, 0);
this.toolStrip1.Size = new System.Drawing.Size(884, 76);
this.toolStrip1.TabIndex = 0;
this.toolStrip1.Text = "toolStrip1";
//
// startButton
//
this.startButton.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image;
this.startButton.Image = global::RpcInvestigator.Properties.Resources.play;
this.startButton.ImageScaling = System.Windows.Forms.ToolStripItemImageScaling.None;
this.startButton.ImageTransparentColor = System.Drawing.Color.Magenta;
this.startButton.Name = "startButton";
this.startButton.Size = new System.Drawing.Size(36, 73);
this.startButton.ToolTipText = "Start or stop the trace";
this.startButton.Click += new System.EventHandler(this.startButton_Click);
//
// enableDebugEventsButton
//
this.enableDebugEventsButton.CheckOnClick = true;
this.enableDebugEventsButton.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image;
this.enableDebugEventsButton.Image = global::RpcInvestigator.Properties.Resources.gears;
this.enableDebugEventsButton.ImageScaling = System.Windows.Forms.ToolStripItemImageScaling.None;
this.enableDebugEventsButton.ImageTransparentColor = System.Drawing.Color.Magenta;
this.enableDebugEventsButton.Name = "enableDebugEventsButton";
this.enableDebugEventsButton.Padding = new System.Windows.Forms.Padding(15, 0, 0, 0);
this.enableDebugEventsButton.Size = new System.Drawing.Size(51, 73);
this.enableDebugEventsButton.ToolTipText = "Enable debug events (requires restart)";
this.enableDebugEventsButton.Click += new System.EventHandler(this.enableDebugEventsButton_Click);
//
// chooseColumnsButton
//
this.chooseColumnsButton.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image;
this.chooseColumnsButton.Image = global::RpcInvestigator.Properties.Resources.columns;
this.chooseColumnsButton.ImageScaling = System.Windows.Forms.ToolStripItemImageScaling.None;
this.chooseColumnsButton.ImageTransparentColor = System.Drawing.Color.Magenta;
this.chooseColumnsButton.Name = "chooseColumnsButton";
this.chooseColumnsButton.Padding = new System.Windows.Forms.Padding(15, 0, 0, 0);
this.chooseColumnsButton.Size = new System.Drawing.Size(51, 73);
this.chooseColumnsButton.ToolTipText = "Choose columns...";
this.chooseColumnsButton.Click += new System.EventHandler(this.chooseColumnsButton_Click);
//
// toolStripSeparator1
//
this.toolStripSeparator1.Name = "toolStripSeparator1";
this.toolStripSeparator1.Size = new System.Drawing.Size(6, 76);
//
// groupByActivityButton
//
this.groupByActivityButton.CheckOnClick = true;
this.groupByActivityButton.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image;
this.groupByActivityButton.Image = ((System.Drawing.Image)(resources.GetObject("groupByActivityButton.Image")));
this.groupByActivityButton.ImageScaling = System.Windows.Forms.ToolStripItemImageScaling.None;
this.groupByActivityButton.ImageTransparentColor = System.Drawing.Color.Magenta;
this.groupByActivityButton.Name = "groupByActivityButton";
this.groupByActivityButton.Padding = new System.Windows.Forms.Padding(15, 0, 0, 0);
this.groupByActivityButton.Size = new System.Drawing.Size(51, 73);
this.groupByActivityButton.ToolTipText = "Group by Activity ID";
this.groupByActivityButton.Click += new System.EventHandler(this.groupByActivityButton_Click);
//
// nodeViewButton
//
this.nodeViewButton.CheckOnClick = true;
this.nodeViewButton.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image;
this.nodeViewButton.Image = global::RpcInvestigator.Properties.Resources.nodes;
this.nodeViewButton.ImageScaling = System.Windows.Forms.ToolStripItemImageScaling.None;
this.nodeViewButton.ImageTransparentColor = System.Drawing.Color.Magenta;
this.nodeViewButton.Name = "nodeViewButton";
this.nodeViewButton.Padding = new System.Windows.Forms.Padding(15, 0, 0, 0);
this.nodeViewButton.Size = new System.Drawing.Size(51, 73);
this.nodeViewButton.ToolTipText = "Switch to node view";
this.nodeViewButton.Click += new System.EventHandler(this.nodeViewButton_Click);
//
// toolStripSeparator2
//
this.toolStripSeparator2.Name = "toolStripSeparator2";
this.toolStripSeparator2.Size = new System.Drawing.Size(6, 76);
//
// saveButton
//
this.saveButton.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image;
this.saveButton.Image = global::RpcInvestigator.Properties.Resources.save;
this.saveButton.ImageScaling = System.Windows.Forms.ToolStripItemImageScaling.None;
this.saveButton.ImageTransparentColor = System.Drawing.Color.Magenta;
this.saveButton.Name = "saveButton";
this.saveButton.Padding = new System.Windows.Forms.Padding(15, 0, 0, 0);
this.saveButton.Size = new System.Drawing.Size(51, 73);
this.saveButton.ToolTipText = "Save the current view";
this.saveButton.Click += new System.EventHandler(this.saveButton_Click);
//
// tableLayoutPanel1
//
this.tableLayoutPanel1.ColumnCount = 1;
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F));
this.tableLayoutPanel1.Controls.Add(this.toolStrip1, 0, 0);
this.tableLayoutPanel1.Controls.Add(this.statusStrip1, 0, 2);
this.tableLayoutPanel1.Controls.Add(this.elementHost1, 0, 1);
this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill;
this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 0);
this.tableLayoutPanel1.Name = "tableLayoutPanel1";
this.tableLayoutPanel1.RowCount = 3;
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 10F));
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 85F));
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 5F));
this.tableLayoutPanel1.Size = new System.Drawing.Size(884, 761);
this.tableLayoutPanel1.TabIndex = 1;
//
// statusStrip1
//
this.statusStrip1.Dock = System.Windows.Forms.DockStyle.Fill;
this.statusStrip1.ImageScalingSize = new System.Drawing.Size(20, 20);
this.statusStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.toolStripProgressBar1,
this.toolStripStatusLabel1});
this.statusStrip1.Location = new System.Drawing.Point(0, 722);
this.statusStrip1.MaximumSize = new System.Drawing.Size(0, 35);
this.statusStrip1.Name = "statusStrip1";
this.statusStrip1.Size = new System.Drawing.Size(884, 35);
this.statusStrip1.TabIndex = 1;
this.statusStrip1.Text = "statusStrip1";
//
// toolStripProgressBar1
//
this.toolStripProgressBar1.Name = "toolStripProgressBar1";
this.toolStripProgressBar1.Size = new System.Drawing.Size(100, 27);
this.toolStripProgressBar1.Visible = false;
//
// toolStripStatusLabel1
//
this.toolStripStatusLabel1.Name = "toolStripStatusLabel1";
this.toolStripStatusLabel1.Padding = new System.Windows.Forms.Padding(5, 0, 0, 0);
this.toolStripStatusLabel1.Size = new System.Drawing.Size(5, 29);
//
// elementHost1
//
this.elementHost1.Dock = System.Windows.Forms.DockStyle.Fill;
this.elementHost1.Location = new System.Drawing.Point(3, 79);
this.elementHost1.Name = "elementHost1";
this.elementHost1.Size = new System.Drawing.Size(878, 640);
this.elementHost1.TabIndex = 2;
this.elementHost1.Visible = false;
this.elementHost1.Child = null;
//
// Sniffer
//
this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 16F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(884, 761);
this.Controls.Add(this.tableLayoutPanel1);
this.DoubleBuffered = true;
this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
this.Name = "Sniffer";
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
this.Text = "RPC Sniffer";
this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.Sniffer_FormClosing);
this.toolStrip1.ResumeLayout(false);
this.toolStrip1.PerformLayout();
this.tableLayoutPanel1.ResumeLayout(false);
this.tableLayoutPanel1.PerformLayout();
this.statusStrip1.ResumeLayout(false);
this.statusStrip1.PerformLayout();
this.ResumeLayout(false);
}
#endregion
private System.Windows.Forms.ToolStrip toolStrip1;
private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1;
private System.Windows.Forms.StatusStrip statusStrip1;
private System.Windows.Forms.ToolStripButton startButton;
private System.Windows.Forms.ToolStripProgressBar toolStripProgressBar1;
private System.Windows.Forms.ToolStripStatusLabel toolStripStatusLabel1;
private System.Windows.Forms.ToolStripButton enableDebugEventsButton;
private System.Windows.Forms.ToolStripButton chooseColumnsButton;
private System.Windows.Forms.ToolStripButton saveButton;
private System.Windows.Forms.ToolStripButton nodeViewButton;
private System.Windows.Forms.ToolStripSeparator toolStripSeparator1;
private System.Windows.Forms.ToolStripSeparator toolStripSeparator2;
private System.Windows.Forms.ToolStripButton groupByActivityButton;
private System.Windows.Forms.Integration.ElementHost elementHost1;
}
}

360
Windows/Sniffer.cs Normal file
View File

@ -0,0 +1,360 @@
//
// Copyright (c) 2022-present, Trail of Bits, Inc.
// All rights reserved.
//
// This source code is licensed in accordance with the terms specified in
// the LICENSE file found in the root directory of this source tree.
//
using System;
using System.Collections.Generic;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Diagnostics;
using System.Threading.Tasks;
using RpcInvestigator.Util;
using System.Threading;
using RpcInvestigator.Properties;
using System.Collections.Concurrent;
using RpcInvestigator.Windows.Controls;
namespace RpcInvestigator.Windows
{
using static NativeTraceConsumer;
using static NativeTraceControl;
using static TraceLogger;
public partial class Sniffer : Form
{
private readonly Guid s_RpcEtwGuid =
new Guid("6ad52b32-d609-4be9-ae07-ce8dae937e39");
private CancellationTokenSource s_CancelSource;
private ConcurrentQueue<ParsedEtwEvent> m_Queue;
private System.Windows.Forms.Timer m_RefreshTimer;
private bool m_EnableDebugEvents;
private bool m_EnableGroupByActivity;
private SnifferListview m_Listview;
private SnifferGraph m_Graph;
private AutoResetEvent m_TaskCompletedEvent;
private enum StartButtonState
{
None,
Running,
Stopped
}
public Sniffer(
RpcLibrary Library,
SnifferCallbackTable Callbacks,
ref Settings Settings
)
{
DoubleBuffered = true;
InitializeComponent();
m_TaskCompletedEvent = new AutoResetEvent(false);
m_Listview = new SnifferListview(Callbacks, Settings);
m_Listview.BuildColumns();
tableLayoutPanel1.Controls.Add(m_Listview, 0, 1);
m_Graph = new SnifferGraph(elementHost1, Library, Callbacks);
startButton.Tag = StartButtonState.None;
m_RefreshTimer = new System.Windows.Forms.Timer();
m_RefreshTimer.Tick += RefreshTimer_Tick;
m_RefreshTimer.Interval = 5000;
}
private async void Sniffer_FormClosing(object sender, FormClosingEventArgs e)
{
if (m_RefreshTimer.Enabled)
{
m_RefreshTimer.Stop();
}
if (s_CancelSource != null)
{
ToggleButtonsNotAllowedDuringTrace(false);
e.Cancel = true;
await Task.Run(() =>
{
s_CancelSource.Cancel();
m_TaskCompletedEvent.WaitOne();
Task.Delay(1000); // extra delay for continuation task to complete
});
s_CancelSource = null;
Close();
}
}
private void enableDebugEventsButton_Click(object sender, EventArgs e)
{
m_EnableDebugEvents = enableDebugEventsButton.Checked;
}
private void groupByActivityButton_Click(object sender, EventArgs e)
{
m_EnableGroupByActivity = groupByActivityButton.Checked;
m_Listview.SetGroupingStrategy(m_EnableGroupByActivity);
}
private void chooseColumnsButton_Click(object sender, EventArgs e)
{
m_Listview.ChooseColumns(s_RpcEtwGuid);
}
private void saveButton_Click(object sender, EventArgs e)
{
if (nodeViewButton.Checked)
{
m_Graph.SaveAsImage();
}
else
{
m_Listview.SaveAsText();
}
}
private void nodeViewButton_Click(object sender, EventArgs e)
{
ToggleView();
}
private async void startButton_Click(object sender, EventArgs e)
{
ToggleButtonsNotAllowedDuringTrace(false);
var currentState = (StartButtonState)startButton.Tag;
m_TaskCompletedEvent.Reset();
if (currentState == StartButtonState.Running)
{
//
// A trace is in progress.
//
// Request cancellation for the next time a buffer fills.
// When the continuation task runs, the UI will be
// reset to allow another start operation.
//
s_CancelSource.Cancel();
m_RefreshTimer.Stop();
toolStripStatusLabel1.Text = "Cancellation requested...";
}
else
{
s_CancelSource = new CancellationTokenSource();
m_Queue = new ConcurrentQueue<ParsedEtwEvent>();
toolStripProgressBar1.Visible = true;
toolStripProgressBar1.Step = 1;
toolStripProgressBar1.Maximum = 3; // start, consume, done
toolStripProgressBar1.Value = 0;
toolStripStatusLabel1.Text = "Starting trace session...";
startButton.Image = Resources.stop;
startButton.Enabled = true;
startButton.Tag = StartButtonState.Running;
int sizeBuffersConsumed = 0;
int eventsConsumed = 0;
m_Listview.Reset();
m_Graph.Reset();
m_RefreshTimer.Enabled = true;
m_RefreshTimer.Start();
ToggleView();
//
// Start the trace in a separate task
//
await Task.Run(() =>
{
using (var trace = new EtwRealTimeTrace(
"RPC Investigator Tracing",
s_RpcEtwGuid,
m_EnableDebugEvents))
using (var parserBuffers = new EtwEventParserBuffers())
{
try
{
trace.Start();
statusStrip1.Invoke((MethodInvoker)(() =>
{
toolStripProgressBar1.PerformStep();
toolStripStatusLabel1.Text = "Consuming events...";
}));
//
// Begin consuming events. This is a blocking call.
//
trace.Consume(
//
// Callback when a new event arrives
//
new EventRecordCallback((Event) =>
{
//
// Check for cancellation request. Event processing
// continues until the buffer is completely drained.
//
if (s_CancelSource.IsCancellationRequested)
{
statusStrip1.Invoke((MethodInvoker)(() =>
{
toolStripStatusLabel1.Text = "Cancellation requested...";
}));
return;
}
var evt = (EVENT_RECORD)Marshal.PtrToStructure(
Event, typeof(EVENT_RECORD));
var parser = new EtwEventParser(
evt,
parserBuffers,
trace.GetPerfFreq());
try
{
var result = parser.Parse();
if (result != null)
{
m_Queue.Enqueue(result);
}
else
{
//
// Uncomment to dump binary event and the parsed
// event up until failure. These files can be
// associated with trace logs via the unique ID
// in the file names.
//
//parser.DumpDiagnosticInfo(Settings.m_LogDir);
}
}
catch (Exception ex)
{
Trace(TraceLoggerType.RpcSniffer,
TraceEventType.Error,
"Unable to parse event: " + ex.Message);
}
eventsConsumed++;
}),
//
// Callback when a trace buffer has been processed
//
new BufferCallback((LogFile) =>
{
//
// Check for cancellation request. Returning false unblocks
// ProcessTrace() and causes trace.Consume to return, which
// will invoke our trace object dispose routine to disable
// the provider and stop our active trace.
//
if (s_CancelSource.IsCancellationRequested)
{
statusStrip1.Invoke((MethodInvoker)(() =>
{
toolStripStatusLabel1.Text = "Cancellation requested...";
}));
return 0;
}
var logfile = new EVENT_TRACE_LOGFILE();
try
{
logfile = (EVENT_TRACE_LOGFILE)
Marshal.PtrToStructure(LogFile, typeof(EVENT_TRACE_LOGFILE));
}
catch (Exception ex)
{
//
// Stop processing
//
Trace(TraceLoggerType.RpcSniffer,
TraceEventType.Error,
"Exception in BufferCallback when casting " +
"pointer to EVENT_TRACE_LOGFILE: " + ex.Message);
return 0;
}
sizeBuffersConsumed += (int)logfile.Filled;
statusStrip1.Invoke((MethodInvoker)(() =>
{
toolStripStatusLabel1.Text = "Events: " + eventsConsumed + ", Buffers: " +
logfile.BuffersRead + " (" + Formatting.InfoUnit(sizeBuffersConsumed) +
")";
}));
return 1;
}));
}
catch (Exception ex)
{
MessageBox.Show("An exception occurred when consuming events: " + ex.Message);
return;
}
}
}, s_CancelSource.Token).ContinueWith(result =>
{
statusStrip1.Invoke((MethodInvoker)(() =>
{
toolStripProgressBar1.Visible = false;
toolStripStatusLabel1.Text = "Trace session terminated.";
startButton.Image = Resources.play;
startButton.Tag = StartButtonState.Stopped;
ToggleButtonsNotAllowedDuringTrace(true);
m_TaskCompletedEvent.Set();
//
// TODO: show stats for buffers consumed, etc
//
}));
});
}
}
private void RefreshTimer_Tick(object sender, EventArgs e)
{
m_RefreshTimer.Stop();
int num = 0;
var events = new List<ParsedEtwEvent>();
while (m_Queue.TryDequeue(out ParsedEtwEvent evt))
{
if (++num > 1000)
{
break;
}
events.Add(evt);
}
m_Listview.Update(events);
m_Graph.Update(events);
m_RefreshTimer.Start();
}
private
void
ToggleButtonsNotAllowedDuringTrace(bool Enable)
{
startButton.Enabled = Enable;
enableDebugEventsButton.Enabled = Enable;
chooseColumnsButton.Enabled = Enable;
saveButton.Enabled = Enable;
}
private
void
ToggleView()
{
if (nodeViewButton.Checked)
{
m_Listview.Visible = false;
elementHost1.Visible = true;
m_Graph.ToggleVisibility(true);
}
else
{
elementHost1.Visible = false;
m_Listview.Visible = true;
m_Graph.ToggleVisibility(false);
}
}
}
public delegate void ShowRpcServerDetails(string InterfaceId);
public class SnifferCallbackTable
{
public ShowRpcServerDetails ShowRpcServerDetailsCallback;
}
}

3423
Windows/Sniffer.resx Normal file

File diff suppressed because it is too large Load Diff

66
app.manifest Normal file
View File

@ -0,0 +1,66 @@
<?xml version="1.0" encoding="utf-8"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
<assemblyIdentity version="1.0.0.0" name="MyApplication.app"/>
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
<security>
<requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
<requestedExecutionLevel level="requireAdministrator"/>
</requestedPrivileges>
</security>
</trustInfo>
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<!-- A list of the Windows versions that this application has been tested on
and is designed to work with. Uncomment the appropriate elements
and Windows will automatically select the most compatible environment. -->
<!-- Windows Vista -->
<!--<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}" />-->
<!-- Windows 7 -->
<!--<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}" />-->
<!-- Windows 8 -->
<!--<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}" />-->
<!-- Windows 8.1 -->
<!--<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}" />-->
<!-- Windows 10 -->
<!--<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />-->
</application>
</compatibility>
<!-- Indicates that the application is DPI-aware and will not be automatically scaled by Windows at higher
DPIs. Windows Presentation Foundation (WPF) applications are automatically DPI-aware and do not need
to opt in. Windows Forms applications targeting .NET Framework 4.6 that opt into this setting, should
also set the 'EnableWindowsFormsHighDpiAutoResizing' setting to 'true' in their app.config.
Makes the application long-path aware. See https://docs.microsoft.com/windows/win32/fileio/maximum-file-path-limitation -->
-->
<application xmlns="urn:schemas-microsoft-com:asm.v3">
<windowsSettings>
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">false</dpiAware>
<longPathAware xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">true</longPathAware>
</windowsSettings>
</application>
<!-- Enable themes for Windows common controls and dialogs (Windows XP and later) -->
<!--
<dependency>
<dependentAssembly>
<assemblyIdentity
type="win32"
name="Microsoft.Windows.Common-Controls"
version="6.0.0.0"
processorArchitecture="*"
publicKeyToken="6595b64144ccf1df"
language="*"
/>
</dependentAssembly>
</dependency>
-->
</assembly>

1
docs/img/.gitattributes vendored Normal file
View File

@ -0,0 +1 @@
*.png filter=lfs diff=lfs merge=lfs -text

View File

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:eee169ca8aac39ea381fc2318f417a63ed94c53015ccf3faf5c190e5eb79c744
size 98411

View File

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:41e7796b693f81cbe07224a935f38003aecada1500252fe4d7074d17a949cb18
size 1216446

View File

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:ff6a340bdd006ba976462207cefad8e6526ea383874226f0be43707c5d09d969
size 1141153

View File

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:5eec292d4e2d10d27b3177ca41dfb7bf335b8d5736cc4bba68c5ab148e01799a
size 1068931

BIN
images/binary.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

BIN
images/columns.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

BIN
images/endpoint.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

BIN
images/gears.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

BIN
images/greendot.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

BIN
images/group.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

BIN
images/nodes.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

BIN
images/play.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

BIN
images/reddot.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 305 KiB

BIN
images/save.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

BIN
images/search.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

BIN
images/service.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 158 KiB

BIN
images/stop.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

BIN
images/tob.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 191 KiB

BIN
images/x-icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

15
packages.config Normal file
View File

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="FCTB" version="2.16.24" targetFramework="net481" />
<package id="GraphX" version="3.0.0" targetFramework="net481" />
<package id="Newtonsoft.Json" version="13.0.2" targetFramework="net481" />
<package id="NtApiDotNet" version="1.1.33" targetFramework="net472" />
<package id="NtApiDotNet.Forms" version="1.1.33" targetFramework="net472" />
<package id="ObjectListView.Official" version="2.9.2-alpha2" targetFramework="net472" />
<package id="QuickGraphCore" version="1.0.0" targetFramework="net481" />
<package id="System.Buffers" version="4.4.0" targetFramework="net472" />
<package id="System.Memory" version="4.5.3" targetFramework="net472" />
<package id="System.Numerics.Vectors" version="4.4.0" targetFramework="net472" />
<package id="System.Resources.Extensions" version="4.6.0" targetFramework="net472" />
<package id="System.Runtime.CompilerServices.Unsafe" version="5.0.0" targetFramework="net481" />
</packages>

BIN
tob.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 191 KiB