refs #39 Added analyzer for reflection

The renaming protection now has an additional service for the usage of reflection.
This commit is contained in:
Martin Karing 2019-04-28 11:16:51 +02:00
Родитель af847d7dda
Коммит efb98dbc2a
4 изменённых файлов: 204 добавлений и 1 удалений

Просмотреть файл

@ -0,0 +1,97 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Confuser.Core;
using Confuser.Core.Services;
using dnlib.DotNet;
using dnlib.DotNet.Emit;
namespace Confuser.Renamer.Analyzers {
/// <summary>
/// This analyzer is looking for calls to the reflection API and blocks methods from being renamed if required.
/// </summary>
public sealed class ReflectionAnalyzer : IRenamer {
void IRenamer.Analyze(ConfuserContext context, INameService service, ProtectionParameters parameters, IDnlibDef def) {
if (!(def is MethodDef method) || !method.HasBody) return;
Analyze(service, context.Registry.GetService<ITraceService>(), context.Modules.Cast<ModuleDef>().ToArray(), method);
}
public void Analyze(INameService nameService, ITraceService traceService, IReadOnlyList<ModuleDef> moduleDefs, MethodDef method) {
MethodTrace methodTrace = null;
MethodTrace GetMethodTrace() {
if (methodTrace == null)
methodTrace = traceService.Trace(method);
return methodTrace;
}
foreach (var instr in method.Body.Instructions) {
if (instr.OpCode.Code == Code.Call && instr.Operand is IMethodDefOrRef calledMethod) {
if (calledMethod.DeclaringType.FullName == "System.Type") {
Func<TypeDef, IEnumerable<IMemberDef>> getMember = null;
if (calledMethod.Name == nameof(Type.GetMethod))
getMember = t => t.Methods;
else if (calledMethod.Name == nameof(Type.GetField))
getMember = t => t.Fields;
else if (calledMethod.Name == nameof(Type.GetProperty))
getMember = t => t.Properties;
else if (calledMethod.Name == nameof(Type.GetEvent))
getMember = t => t.Events;
else if (calledMethod.Name == nameof(Type.GetMember))
getMember = t => Enumerable.Empty<IMemberDef>().Concat(t.Methods).Concat(t.Fields).Concat(t.Properties).Concat(t.Events);
if (getMember != null) {
var trace = GetMethodTrace();
var arguments = trace.TraceArguments(instr);
if (arguments.Length >= 2) {
var types = GetReferencedTypes(method.Body.Instructions[arguments[0]], method, trace);
var names = GetReferencedNames(method.Body.Instructions[arguments[1]]);
if (!types.Any())
types = moduleDefs.SelectMany(m => m.GetTypes()).ToArray();
foreach (var possibleMethod in types.SelectMany(getMember).Where(m => names.Contains(m.Name))) {
nameService.SetCanRename(possibleMethod, false);
}
}
}
}
}
}
}
/// <summary>
/// This method is used to determine the types that are load onto the stack at the referenced instruction.
/// In case the method is unable to determine all the types reliable, it will return a empty list.
/// </summary>
private static IReadOnlyList<TypeDef> GetReferencedTypes(Instruction instruction, MethodDef method, MethodTrace trace) {
if (instruction.OpCode.Code == Code.Call && instruction.Operand is IMethodDefOrRef calledMethod) {
if (calledMethod.DeclaringType.FullName == "System.Type" && calledMethod.Name == "GetTypeFromHandle") {
var arguments = trace.TraceArguments(instruction);
if (arguments.Length == 1) {
var ldTokenInstr = method.Body.Instructions[arguments[0]];
if (ldTokenInstr.OpCode.Code == Code.Ldtoken && ldTokenInstr.Operand is TypeDef refTypeDef) {
return new List<TypeDef>() { refTypeDef };
}
}
}
}
return new List<TypeDef>();
}
private static IReadOnlyList<UTF8String> GetReferencedNames(Instruction instruction) {
if (instruction.OpCode.Code == Code.Ldstr && instruction.Operand is string str) {
return new List<UTF8String>() { str };
}
return new List<UTF8String>();
}
void IRenamer.PostRename(ConfuserContext context, INameService service, ProtectionParameters parameters, IDnlibDef def) { }
void IRenamer.PreRename(ConfuserContext context, INameService service, ProtectionParameters parameters, IDnlibDef def) { }
}
}

Просмотреть файл

@ -67,7 +67,8 @@ namespace Confuser.Renamer {
new VTableAnalyzer(), new VTableAnalyzer(),
new TypeBlobAnalyzer(), new TypeBlobAnalyzer(),
new ResourceAnalyzer(), new ResourceAnalyzer(),
new LdtokenEnumAnalyzer() new LdtokenEnumAnalyzer(),
new ReflectionAnalyzer()
}; };
} }

Просмотреть файл

@ -0,0 +1,104 @@
using System.Collections.Generic;
using System.Reflection;
using Confuser.Core.Services;
using Confuser.Renamer.Analyzers;
using dnlib.DotNet;
using Moq;
using Xunit;
namespace Confuser.Renamer.Test.Analyzers {
public sealed class ReflectionAnalyzerTest {
private string _referenceField;
private string ReferenceProperty { get; }
private void TestReferenceMethod1() {
var method1 = typeof(ReflectionAnalyzerTest).GetMethod(nameof(TestReferenceMethod1));
Assert.Null(method1);
var method2 = typeof(ReflectionAnalyzerTest).GetMethod(nameof(TestReferenceMethod1), BindingFlags.NonPublic | BindingFlags.Instance);
Assert.NotNull(method2);
}
public void TestReferenceField1() {
var field1 = typeof(ReflectionAnalyzerTest).GetField(nameof(_referenceField));
Assert.Null(field1);
var field2 = typeof(ReflectionAnalyzerTest).GetField(nameof(_referenceField), BindingFlags.NonPublic | BindingFlags.Instance);
Assert.NotNull(field2);
}
public void TestReferenceProperty1() {
var prop1 = typeof(ReflectionAnalyzerTest).GetProperty(nameof(ReferenceProperty));
Assert.Null(prop1);
var prop2 = typeof(ReflectionAnalyzerTest).GetProperty(nameof(ReferenceProperty), BindingFlags.NonPublic | BindingFlags.Instance);
Assert.NotNull(prop2);
}
[Fact]
public void TestReferenceMethod1Test() {
TestReferenceMethod1();
var moduleDef = LoadTestModuleDef();
var thisTypeDef = moduleDef.Find("Confuser.Renamer.Test.Analyzers.ReflectionAnalyzerTest", false);
var refMethod = thisTypeDef.FindMethod(nameof(TestReferenceMethod1));
var nameService = Mock.Of<INameService>();
Mock.Get(nameService).Setup(s => s.SetCanRename(refMethod, false));
Mock.Get(nameService).Setup(s => s.SetCanRename(refMethod, false));
var traceService = new TraceService();
var analyzer = new ReflectionAnalyzer();
analyzer.Analyze(nameService, traceService, new List<ModuleDef>() { moduleDef }, refMethod);
Mock.Get(nameService).VerifyAll();
}
[Fact]
public void TestReferenceField1Test() {
TestReferenceField1();
var moduleDef = LoadTestModuleDef();
var thisTypeDef = moduleDef.Find("Confuser.Renamer.Test.Analyzers.ReflectionAnalyzerTest", false);
var refMethod = thisTypeDef.FindMethod(nameof(TestReferenceField1));
var refField = thisTypeDef.FindField(nameof(_referenceField));
var nameService = Mock.Of<INameService>();
Mock.Get(nameService).Setup(s => s.SetCanRename(refField, false));
Mock.Get(nameService).Setup(s => s.SetCanRename(refField, false));
var traceService = new TraceService();
var analyzer = new ReflectionAnalyzer();
analyzer.Analyze(nameService, traceService, new List<ModuleDef>() { moduleDef }, refMethod);
Mock.Get(nameService).VerifyAll();
}
[Fact]
public void TestReferenceProperty1Test() {
TestReferenceProperty1();
var moduleDef = LoadTestModuleDef();
var thisTypeDef = moduleDef.Find("Confuser.Renamer.Test.Analyzers.ReflectionAnalyzerTest", false);
var refMethod = thisTypeDef.FindMethod(nameof(TestReferenceProperty1));
var refProp = thisTypeDef.FindProperty(nameof(ReferenceProperty));
var nameService = Mock.Of<INameService>();
Mock.Get(nameService).Setup(s => s.SetCanRename(refProp, false));
Mock.Get(nameService).Setup(s => s.SetCanRename(refProp, false));
var traceService = new TraceService();
var analyzer = new ReflectionAnalyzer();
analyzer.Analyze(nameService, traceService, new List<ModuleDef>() { moduleDef }, refMethod);
Mock.Get(nameService).VerifyAll();
}
private static ModuleDef LoadTestModuleDef() {
var asmResolver = new AssemblyResolver { EnableTypeDefCache = true };
asmResolver.DefaultModuleContext = new ModuleContext(asmResolver);
var options = new ModuleCreationOptions(asmResolver.DefaultModuleContext) {
TryToLoadPdbFromDisk = false
};
return ModuleDefMD.Load(typeof(VTableTest).Module, options);
}
}
}

Просмотреть файл

@ -7,6 +7,7 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.8.0" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.8.0" />
<PackageReference Include="Moq" Version="4.10.1" />
<PackageReference Include="xunit" Version="2.3.1" /> <PackageReference Include="xunit" Version="2.3.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.3.1" /> <PackageReference Include="xunit.runner.visualstudio" Version="2.3.1" />
<DotNetCliToolReference Include="dotnet-xunit" Version="2.3.1" /> <DotNetCliToolReference Include="dotnet-xunit" Version="2.3.1" />