Browse Source

#1498: Remove Fusion API usage: Use standard file enumeration instead. This should enable us to use the GAC even with restricted access, as only the LIST permission is required.

pull/1994/head
Siegfried Pammer 5 years ago
parent
commit
40687ea8ad
  1. 38
      ICSharpCode.Decompiler/Metadata/UniversalAssemblyResolver.cs
  2. 3
      ILSpy.AddIn/Commands/ProjectReferenceForILSpy.cs
  3. 135
      ILSpy/Fusion.cs
  4. 137
      ILSpy/GacInterop.cs
  5. 2
      ILSpy/ILSpy.csproj
  6. 9
      ILSpy/Properties/Resources.Designer.cs
  7. 3
      ILSpy/Properties/Resources.resx
  8. 2
      ILSpy/ViewModels/ManageAssemblyListsViewModel.cs
  9. 2
      ILSpy/Views/OpenFromGacDialog.xaml
  10. 16
      ILSpy/Views/OpenFromGacDialog.xaml.cs

38
ICSharpCode.Decompiler/Metadata/UniversalAssemblyResolver.cs

@ -23,6 +23,7 @@ using System.Linq; @@ -23,6 +23,7 @@ using System.Linq;
using System.Reflection.Metadata;
using System.Reflection.PortableExecutable;
using System.Text;
using System.Text.RegularExpressions;
namespace ICSharpCode.Decompiler.Metadata
{
@ -63,7 +64,7 @@ namespace ICSharpCode.Decompiler.Metadata @@ -63,7 +64,7 @@ namespace ICSharpCode.Decompiler.Metadata
readonly string mainAssemblyFileName;
readonly string baseDirectory;
readonly List<string> directories = new List<string>();
readonly List<string> gac_paths = GetGacPaths();
static readonly List<string> gac_paths = GetGacPaths();
HashSet<string> targetFrameworkSearchPaths;
static readonly DecompilerRuntime decompilerRuntime;
@ -465,7 +466,7 @@ namespace ICSharpCode.Decompiler.Metadata @@ -465,7 +466,7 @@ namespace ICSharpCode.Decompiler.Metadata
return path;
}
static List<string> GetGacPaths()
public static List<string> GetGacPaths()
{
if (decompilerRuntime == DecompilerRuntime.Mono)
return GetDefaultMonoGacPaths();
@ -512,7 +513,7 @@ namespace ICSharpCode.Decompiler.Metadata @@ -512,7 +513,7 @@ namespace ICSharpCode.Decompiler.Metadata
"gac");
}
string GetAssemblyInGac(IAssemblyReference reference)
public static string GetAssemblyInGac(IAssemblyReference reference)
{
if (reference.PublicKeyToken == null || reference.PublicKeyToken.Length == 0)
return null;
@ -523,7 +524,7 @@ namespace ICSharpCode.Decompiler.Metadata @@ -523,7 +524,7 @@ namespace ICSharpCode.Decompiler.Metadata
return GetAssemblyInNetGac(reference);
}
string GetAssemblyInMonoGac(IAssemblyReference reference)
static string GetAssemblyInMonoGac(IAssemblyReference reference)
{
for (int i = 0; i < gac_paths.Count; i++) {
var gac_path = gac_paths[i];
@ -535,7 +536,7 @@ namespace ICSharpCode.Decompiler.Metadata @@ -535,7 +536,7 @@ namespace ICSharpCode.Decompiler.Metadata
return null;
}
string GetAssemblyInNetGac(IAssemblyReference reference)
static string GetAssemblyInNetGac(IAssemblyReference reference)
{
var gacs = new[] { "GAC_MSIL", "GAC_32", "GAC_64", "GAC" };
var prefixes = new[] { string.Empty, "v4.0_" };
@ -568,6 +569,33 @@ namespace ICSharpCode.Decompiler.Metadata @@ -568,6 +569,33 @@ namespace ICSharpCode.Decompiler.Metadata
reference.Name + ".dll");
}
/// <summary>
/// Gets the names of all assemblies in the GAC.
/// </summary>
public static IEnumerable<AssemblyNameReference> EnumerateGac()
{
var gacs = new[] { "GAC_MSIL", "GAC_32", "GAC_64", "GAC" };
foreach (var path in GetGacPaths()) {
foreach (var gac in gacs) {
string rootPath = Path.Combine(path, gac);
if (!Directory.Exists(rootPath))
continue;
foreach (var item in new DirectoryInfo(rootPath).EnumerateFiles("*.dll", SearchOption.AllDirectories)) {
string[] name = Path.GetDirectoryName(item.FullName).Substring(rootPath.Length + 1).Split(new[] { "\\" }, StringSplitOptions.RemoveEmptyEntries);
if (name.Length != 2)
continue;
var match = Regex.Match(name[1], $"(v4.0_)?(?<version>[^_]+)_(?<culture>[^_]+)?_(?<publicKey>[^_]+)");
if (!match.Success)
continue;
string culture = match.Groups["culture"].Value;
if (string.IsNullOrEmpty(culture))
culture = "neutral";
yield return AssemblyNameReference.Parse(name[0] + ", Version=" + match.Groups["version"].Value + ", Culture=" + culture + ", PublicKeyToken=" + match.Groups["publicKey"].Value);
}
}
}
}
#endregion
}
}

3
ILSpy.AddIn/Commands/ProjectReferenceForILSpy.cs

@ -4,6 +4,7 @@ using System.Linq; @@ -4,6 +4,7 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
using EnvDTE;
using ICSharpCode.Decompiler.Metadata;
using Microsoft.VisualStudio.Shell;
using Mono.Cecil;
@ -74,7 +75,7 @@ namespace ICSharpCode.ILSpy.AddIn.Commands @@ -74,7 +75,7 @@ namespace ICSharpCode.ILSpy.AddIn.Commands
if (resolvedPath != null) {
return new ILSpyParameters(new[] { $"{resolvedPath}" });
} else if (!string.IsNullOrWhiteSpace(fusionName)) {
return new ILSpyParameters(new string[] { GacInterop.FindAssemblyInNetGac(Decompiler.Metadata.AssemblyNameReference.Parse(fusionName)) });
return new ILSpyParameters(new string[] { UniversalAssemblyResolver.GetAssemblyInGac(Decompiler.Metadata.AssemblyNameReference.Parse(fusionName)) });
}
return null;

135
ILSpy/Fusion.cs

@ -1,135 +0,0 @@ @@ -1,135 +0,0 @@
// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.Runtime.InteropServices;
using System.Text;
namespace ICSharpCode.ILSpy
{
// .NET Fusion COM interfaces
[ComImport(), Guid("CD193BC0-B4BC-11D2-9833-00C04FC31D2E"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal interface IAssemblyName
{
[PreserveSig()]
int SetProperty(uint PropertyId, IntPtr pvProperty, uint cbProperty);
[PreserveSig()]
int GetProperty(uint PropertyId, IntPtr pvProperty, ref uint pcbProperty);
[PreserveSig()]
int Finalize();
[PreserveSig()]
int GetDisplayName([Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder szDisplayName,
ref uint pccDisplayName,
uint dwDisplayFlags);
[PreserveSig()]
int BindToObject(object refIID,
object pAsmBindSink,
IApplicationContext pApplicationContext,
[MarshalAs(UnmanagedType.LPWStr)] string szCodeBase,
long llFlags,
int pvReserved,
uint cbReserved,
out int ppv);
[PreserveSig()]
int GetName(ref uint lpcwBuffer,
[Out(), MarshalAs(UnmanagedType.LPWStr)] StringBuilder pwzName);
[PreserveSig()]
int GetVersion(out uint pdwVersionHi, out uint pdwVersionLow);
[PreserveSig()]
int IsEqual(IAssemblyName pName,
uint dwCmpFlags);
[PreserveSig()]
int Clone(out IAssemblyName pName);
}
[ComImport(), Guid("7C23FF90-33AF-11D3-95DA-00A024A85B51"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal interface IApplicationContext
{
void SetContextNameObject(IAssemblyName pName);
void GetContextNameObject(out IAssemblyName ppName);
void Set([MarshalAs(UnmanagedType.LPWStr)] string szName,
int pvValue,
uint cbValue,
uint dwFlags);
void Get([MarshalAs(UnmanagedType.LPWStr)] string szName,
out int pvValue,
ref uint pcbValue,
uint dwFlags);
void GetDynamicDirectory(out int wzDynamicDir,
ref uint pdwSize);
}
[ComImport(), Guid("21B8916C-F28E-11D2-A473-00C04F8EF448"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal interface IAssemblyEnum
{
[PreserveSig()]
int GetNextAssembly(out IApplicationContext ppAppCtx,
out IAssemblyName ppName,
uint dwFlags);
[PreserveSig()]
int Reset();
[PreserveSig()]
int Clone(out IAssemblyEnum ppEnum);
}
internal static class Fusion
{
// dwFlags: 1 = Enumerate native image (NGEN) assemblies
// 2 = Enumerate GAC assemblies
// 4 = Enumerate Downloaded assemblies
//
[DllImport("fusion.dll", CharSet=CharSet.Auto)]
internal static extern int CreateAssemblyEnum(out IAssemblyEnum ppEnum,
IApplicationContext pAppCtx,
IAssemblyName pName,
uint dwFlags,
int pvReserved);
[DllImport("fusion.dll")]
internal static extern int GetCachePath(uint flags,
[MarshalAs(UnmanagedType.LPWStr)] StringBuilder wzDir,
ref uint pdwSize);
public static string GetGacPath(bool isCLRv4 = false)
{
const uint ASM_CACHE_ROOT = 0x08; // CLR V2.0
const uint ASM_CACHE_ROOT_EX = 0x80; // CLR V4.0
uint flags = isCLRv4 ? ASM_CACHE_ROOT_EX : ASM_CACHE_ROOT;
const int size = 260; // MAX_PATH
StringBuilder b = new StringBuilder(size);
uint tmp = size;
GetCachePath(flags, b, ref tmp);
return b.ToString();
}
}
}

137
ILSpy/GacInterop.cs

@ -1,137 +0,0 @@ @@ -1,137 +0,0 @@
// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Windows;
using ICSharpCode.Decompiler.Metadata;
namespace ICSharpCode.ILSpy
{
/// <summary>
/// Interop with the .NET GAC.
/// </summary>
static class GacInterop
{
/// <summary>
/// Gets the names of all assemblies in the GAC.
/// </summary>
public static IEnumerable<AssemblyNameReference> GetGacAssemblyFullNames()
{
IApplicationContext applicationContext = null;
IAssemblyEnum assemblyEnum = null;
IAssemblyName assemblyName = null;
uint result = unchecked((uint)Fusion.CreateAssemblyEnum(out assemblyEnum, null, null, 2, 0));
if (result == 0x80070005) {
MessageBox.Show($"Cannot access GAC, please restart with elevated privileges! (HRESULT 0x{result:X})", "ILSpy", MessageBoxButton.OK, MessageBoxImage.Error);
yield break;
}
while (assemblyEnum.GetNextAssembly(out applicationContext, out assemblyName, 0) == 0) {
if (assemblyName == null) continue;
uint nChars = 0;
assemblyName.GetDisplayName(null, ref nChars, 0);
StringBuilder name = new StringBuilder((int)nChars);
assemblyName.GetDisplayName(name, ref nChars, 0);
AssemblyNameReference r = null;
try {
r = AssemblyNameReference.Parse(name.ToString());
} catch (ArgumentException) {
} catch (FormatException) {
} catch (OverflowException) {
}
if (r != null)
yield return r;
}
}
#region FindAssemblyInGac
// This region is based on code from Mono.Cecil:
// Author:
// Jb Evain (jbevain@gmail.com)
//
// Copyright (c) 2008 - 2010 Jb Evain
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
static readonly string[] gac_paths = { Fusion.GetGacPath(false), Fusion.GetGacPath(true) };
static readonly string[] gacs = { "GAC_MSIL", "GAC_32", "GAC" };
static readonly string[] prefixes = { string.Empty, "v4.0_" };
/// <summary>
/// Gets the file name for an assembly stored in the GAC.
/// </summary>
public static string FindAssemblyInNetGac (AssemblyNameReference reference)
{
// without public key, it can't be in the GAC
if (reference.PublicKeyToken == null)
return null;
for (int i = 0; i < 2; i++) {
for (int j = 0; j < gacs.Length; j++) {
var gac = Path.Combine (gac_paths [i], gacs [j]);
var file = GetAssemblyFile (reference, prefixes [i], gac);
if (File.Exists (file))
return file;
}
}
return null;
}
static string GetAssemblyFile (AssemblyNameReference reference, string prefix, string gac)
{
var gac_folder = new StringBuilder ()
.Append (prefix)
.Append (reference.Version)
.Append ("__");
for (int i = 0; i < reference.PublicKeyToken.Length; i++)
gac_folder.Append (reference.PublicKeyToken [i].ToString ("x2"));
return Path.Combine (
Path.Combine (
Path.Combine (gac, reference.Name), gac_folder.ToString ()),
reference.Name + ".dll");
}
#endregion
}
}

2
ILSpy/ILSpy.csproj

@ -237,8 +237,6 @@ @@ -237,8 +237,6 @@
<Compile Include="DecompilationOptions.cs" />
<Compile Include="ExtensionMethods.cs" />
<Compile Include="FilterSettings.cs" />
<Compile Include="Fusion.cs" />
<Compile Include="GacInterop.cs" />
<Compile Include="GuessFileType.cs" />
<Compile Include="ContextMenuEntry.cs" />
<Compile Include="Languages\CSharpBracketSearcher.cs" />

9
ILSpy/Properties/Resources.Designer.cs generated

@ -513,6 +513,15 @@ namespace ICSharpCode.ILSpy.Properties { @@ -513,6 +513,15 @@ namespace ICSharpCode.ILSpy.Properties {
}
}
/// <summary>
/// Looks up a localized string similar to Culture.
/// </summary>
public static string CultureLabel {
get {
return ResourceManager.GetString("CultureLabel", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to DEBUG -- Decompile All.
/// </summary>

3
ILSpy/Properties/Resources.resx

@ -861,4 +861,7 @@ Do you want to continue?</value> @@ -861,4 +861,7 @@ Do you want to continue?</value>
<data name="RenameList" xml:space="preserve">
<value>Rename list</value>
</data>
<data name="CultureLabel" xml:space="preserve">
<value>Culture</value>
</data>
</root>

2
ILSpy/ViewModels/ManageAssemblyListsViewModel.cs

@ -275,7 +275,7 @@ namespace ICSharpCode.ILSpy.ViewModels @@ -275,7 +275,7 @@ namespace ICSharpCode.ILSpy.ViewModels
void AddToListFromGAC(string fullName)
{
AssemblyNameReference reference = AssemblyNameReference.Parse(fullName);
string file = GacInterop.FindAssemblyInNetGac(reference);
string file = UniversalAssemblyResolver.GetAssemblyInGac(reference);
if (file != null)
list.OpenAssembly(file);
}

2
ILSpy/Views/OpenFromGacDialog.xaml

@ -33,7 +33,7 @@ @@ -33,7 +33,7 @@
<GridView>
<controls:SortableGridViewColumn x:Name="nameColumn" Width="300" Header="{x:Static properties:Resources.ReferenceName}" DisplayMemberBinding="{Binding ShortName}" />
<controls:SortableGridViewColumn Width="75" Header="{x:Static properties:Resources.Version}" DisplayMemberBinding="{Binding Version}" />
<controls:SortableGridViewColumn Width="65" Header="{x:Static properties:Resources.Culture}" DisplayMemberBinding="{Binding Culture}" />
<controls:SortableGridViewColumn Width="65" Header="{x:Static properties:Resources.CultureLabel}" DisplayMemberBinding="{Binding Culture}" />
<controls:SortableGridViewColumn Width="115" Header="{x:Static properties:Resources.PublicToken}" DisplayMemberBinding="{Binding PublicKeyToken}" />
<controls:SortableGridViewColumn Width="1000" Header="{x:Static properties:Resources.Location}" DisplayMemberBinding="{Binding FileName}" />
</GridView>

16
ILSpy/Views/OpenFromGacDialog.xaml.cs

@ -95,7 +95,11 @@ namespace ICSharpCode.ILSpy @@ -95,7 +95,11 @@ namespace ICSharpCode.ILSpy
}
public string Culture {
get { return r.Culture; }
get {
if (string.IsNullOrEmpty(r.Culture))
return "neutral";
return r.Culture;
}
}
public string PublicKeyToken {
@ -116,21 +120,21 @@ namespace ICSharpCode.ILSpy @@ -116,21 +120,21 @@ namespace ICSharpCode.ILSpy
void FetchGacContents()
{
HashSet<string> fullNames = new HashSet<string>();
UpdateProgressBar(pg => { pg.Visibility = System.Windows.Visibility.Visible; pg.IsIndeterminate = true; });
var list = GacInterop.GetGacAssemblyFullNames().TakeWhile(_ => !cancelFetchThread).ToList();
UpdateProgressBar(pg => { pg.Visibility = Visibility.Visible; pg.IsIndeterminate = true; });
var list = UniversalAssemblyResolver.EnumerateGac().TakeWhile(_ => !cancelFetchThread).ToList();
UpdateProgressBar(pg => { pg.IsIndeterminate = false; pg.Maximum = list.Count; });
foreach (var r in list) {
if (cancelFetchThread)
break;
if (fullNames.Add(r.FullName)) { // filter duplicates
var file = GacInterop.FindAssemblyInNetGac(r);
var file = UniversalAssemblyResolver.GetAssemblyInGac(r);
if (file != null) {
var entry = new GacEntry(r, file);
UpdateProgressBar(pg => { pg.Value = pg.Value + 1; AddNewEntry(entry); });
UpdateProgressBar(pg => { pg.Value++; AddNewEntry(entry); });
}
}
}
UpdateProgressBar(pg => { pg.Visibility = System.Windows.Visibility.Hidden; });
UpdateProgressBar(pg => { pg.Visibility = Visibility.Hidden; });
}
void UpdateProgressBar(Action<ProgressBar> updateAction)

Loading…
Cancel
Save