refs #39 Added analyzer for reflection
The renaming protection now has an additional service for the usage of reflection.
This commit is contained in:
Родитель
af847d7dda
Коммит
efb98dbc2a
|
@ -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" />
|
||||||
|
|
Загрузка…
Ссылка в новой задаче