Skip to content

Commit b176034

Browse files
authored
Merge d0611a3 into 8566265
2 parents 8566265 + d0611a3 commit b176034

File tree

14 files changed

+390
-1
lines changed

14 files changed

+390
-1
lines changed
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
using System.Linq;
2+
using Confuser.Core;
3+
using Confuser.Core.Services;
4+
using Confuser.Renamer.References;
5+
using dnlib.DotNet;
6+
using dnlib.DotNet.Emit;
7+
8+
namespace Confuser.Renamer.Analyzers {
9+
internal sealed class CallSiteAnalyzer : IRenamer {
10+
public void Analyze(ConfuserContext context, INameService service, ProtectionParameters parameters, IDnlibDef def) {
11+
if (!(def is MethodDef method) || !method.HasBody)
12+
return;
13+
14+
var logger = context.Logger;
15+
16+
var traceService = context.Registry.GetService<ITraceService>();
17+
MethodTrace methodTrace = null;
18+
19+
var instructions = method.Body.Instructions;
20+
foreach (var instruction in instructions) {
21+
if (!IsCreateCallSiteInstruction(instruction)) continue;
22+
23+
if (methodTrace is null)
24+
methodTrace = traceService.Trace(method);
25+
26+
// CallSite`1.Create(CallSiteBinder)
27+
int[] createArguments = methodTrace.TraceArguments(instruction);
28+
if (createArguments.Length != 1) continue;
29+
30+
// Binder.InvokeMember(CSharpBinderFlags, string, IEnumerable<Type>, Type, IEnumerable<CSharpArgumentInfo>)
31+
var binderInstruction = instructions[createArguments[0]];
32+
if (IsBinderInvokeMember(binderInstruction)) {
33+
HandleBinderInvokeMember(context, method, methodTrace, binderInstruction);
34+
}
35+
}
36+
}
37+
38+
private static void HandleBinderInvokeMember(ConfuserContext context, MethodDef method, MethodTrace methodTrace, Instruction instruction) {
39+
var instructions = method.Body.Instructions;
40+
41+
int[] binderArguments = methodTrace.TraceArguments(instruction);
42+
if (binderArguments.Length != 5) return;
43+
44+
var nameInstruction = instructions[binderArguments[1]];
45+
var contextInstruction = instructions[binderArguments[3]];
46+
47+
// Name instruction is expected to contain a string constant - This is the name of the invoked member
48+
if (nameInstruction.OpCode.Code != Code.Ldstr) return;
49+
string boundMemberName = nameInstruction.Operand as string;
50+
51+
var ldContextTokenInstruction = contextInstruction;
52+
if (IsGetTypeFromHandle(contextInstruction)) {
53+
int[] getTypeFromHandleArguments = methodTrace.TraceArguments(contextInstruction);
54+
if (getTypeFromHandleArguments.Length == 1)
55+
ldContextTokenInstruction = instructions[getTypeFromHandleArguments[0]];
56+
}
57+
58+
if (ldContextTokenInstruction.OpCode.Code == Code.Ldtoken &&
59+
ldContextTokenInstruction.Operand is ITypeDefOrRef typeDefOrRef) {
60+
// We found the load token of the context parameter. This means we know the type the member is called for.
61+
BuildMemberReferences(context, typeDefOrRef, boundMemberName, nameInstruction);
62+
}
63+
else {
64+
context.Logger.WarnFormat(
65+
"Failed to resolve type for dynamic invoke member in {0} - blocking all members with name {1} from renaming.",
66+
method, boundMemberName);
67+
68+
// The type referenced is unknown. To be safe, all methods matching the name need to be blocked from renaming.
69+
DisableRenamingForMethods(context, boundMemberName);
70+
}
71+
}
72+
73+
static void DisableRenamingForMethods(ConfuserContext context, string methodName) {
74+
var service = context.Registry.GetService<INameService>();
75+
76+
var candidateMethods = context.Modules
77+
.SelectMany(m => m.FindDefinitions())
78+
.OfType<MethodDef>()
79+
.Where(m => m.Name.Equals(methodName));
80+
foreach (var candidateMethod in candidateMethods)
81+
service.SetCanRename(candidateMethod, false);
82+
}
83+
84+
static void BuildMemberReferences(ConfuserContext context, ITypeDefOrRef typeDefOrRef, string boundMemberName,
85+
Instruction nameInstruction) {
86+
var service = context.Registry.GetService<INameService>();
87+
88+
var boundMemberTypeDef = typeDefOrRef.ResolveTypeDef();
89+
if (boundMemberTypeDef is null) return;
90+
91+
var currentType = boundMemberTypeDef;
92+
while (currentType != null) {
93+
foreach (var refMethod in currentType.FindMethods(boundMemberName)) {
94+
service.AddReference(refMethod,
95+
new StringMemberNameReference(nameInstruction, refMethod));
96+
service.ReduceRenameMode(refMethod, RenameMode.Reflection);
97+
}
98+
99+
currentType = currentType.BaseType.ResolveTypeDef();
100+
}
101+
}
102+
103+
private static bool IsCreateCallSiteInstruction(Instruction instruction) {
104+
if (instruction.OpCode.Code != Code.Call) return false;
105+
if (!(instruction.Operand is IMethodDefOrRef method)) return false;
106+
107+
return method.DeclaringType.Namespace.Equals("System.Runtime.CompilerServices") &&
108+
method.DeclaringType.Name.Equals("CallSite`1") &&
109+
method.Name.Equals("Create");
110+
}
111+
112+
private static bool IsBinderInvokeMember(Instruction instruction) {
113+
if (instruction.OpCode.Code != Code.Call) return false;
114+
if (!(instruction.Operand is IMethodDefOrRef method)) return false;
115+
116+
return method.DeclaringType.Namespace.Equals("Microsoft.CSharp.RuntimeBinder") &&
117+
method.DeclaringType.Name.Equals("Binder") &&
118+
method.Name.Equals("InvokeMember");
119+
}
120+
121+
private static bool IsGetTypeFromHandle(Instruction instruction) {
122+
if (instruction.OpCode.Code != Code.Call) return false;
123+
if (!(instruction.Operand is IMethodDefOrRef method)) return false;
124+
125+
return method.DeclaringType.Namespace.Equals("System") &&
126+
method.DeclaringType.Name.Equals("Type") &&
127+
method.Name.Equals("GetTypeFromHandle");
128+
}
129+
130+
public void PreRename(ConfuserContext context, INameService service, ProtectionParameters parameters, IDnlibDef def) { }
131+
132+
public void PostRename(ConfuserContext context, INameService service, ProtectionParameters parameters, IDnlibDef def) { }
133+
}
134+
}

Confuser.Renamer/NameService.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,8 @@ public NameService(ConfuserContext context) {
7878
new ResourceAnalyzer(),
7979
new LdtokenEnumAnalyzer(),
8080
new ManifestResourceAnalyzer(),
81-
new ReflectionAnalyzer()
81+
new ReflectionAnalyzer(),
82+
new CallSiteAnalyzer()
8283
};
8384
}
8485

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
using System;
2+
using System.Text;
3+
using Confuser.Core;
4+
using dnlib.DotNet;
5+
using dnlib.DotNet.Emit;
6+
7+
namespace Confuser.Renamer.References {
8+
public sealed class StringMemberNameReference : INameReference<IMemberDef> {
9+
private readonly Instruction _reference;
10+
private readonly IMemberDef _memberDef;
11+
public bool ShouldCancelRename => false;
12+
13+
public StringMemberNameReference(Instruction reference, IMemberDef memberDef) {
14+
_reference = reference;
15+
_memberDef = memberDef;
16+
}
17+
18+
/// <inheritdoc />
19+
public bool DelayRenaming(INameService service) => false;
20+
21+
public bool UpdateNameReference(ConfuserContext context, INameService service) {
22+
switch (_reference.Operand) {
23+
case string strOp when string.Equals(strOp, _memberDef.Name, StringComparison.Ordinal):
24+
case UTF8String utf8StrOp when UTF8String.Equals(utf8StrOp, _memberDef.Name):
25+
return false;
26+
default:
27+
_reference.Operand = (string)_memberDef.Name;
28+
return true;
29+
}
30+
}
31+
32+
public override string ToString() => ToString(null);
33+
34+
public string ToString(INameService nameService) {
35+
var builder = new StringBuilder();
36+
builder.Append("String Member Name Reference").Append("(");
37+
38+
builder.Append("Instruction").Append("(").AppendHashedIdentifier("Operand", _reference.Operand).Append(")");
39+
builder.Append("; ");
40+
builder.AppendReferencedDef(_memberDef, nameService);
41+
42+
builder.Append(")");
43+
44+
return builder.ToString();
45+
}
46+
}
47+
}

Confuser2.sln

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "342_InterfaceRenamingLoop",
121121
EndProject
122122
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "342_InterfaceRenamingLoop.Test", "Tests\342_InterfaceRenamingLoop.Test\342_InterfaceRenamingLoop.Test.csproj", "{EC62CE1D-ADD7-419A-84A9-D6A04E866197}"
123123
EndProject
124+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "345_RenameDynamicParameter", "Tests\345_RenameDynamicParameter\345_RenameDynamicParameter.csproj", "{DAE3997B-D51B-4D9F-9F11-2EBC6FDDF57C}"
125+
EndProject
126+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "345_RenameDynamicParameter.Test", "Tests\345_RenameDynamicParameter.Test\345_RenameDynamicParameter.Test.csproj", "{0C8F49D8-BD68-420A-907D-031B83737C50}"
127+
EndProject
124128
Global
125129
GlobalSection(SolutionConfigurationPlatforms) = preSolution
126130
Debug|Any CPU = Debug|Any CPU
@@ -755,6 +759,30 @@ Global
755759
{EC62CE1D-ADD7-419A-84A9-D6A04E866197}.Release|x64.Build.0 = Release|Any CPU
756760
{EC62CE1D-ADD7-419A-84A9-D6A04E866197}.Release|x86.ActiveCfg = Release|Any CPU
757761
{EC62CE1D-ADD7-419A-84A9-D6A04E866197}.Release|x86.Build.0 = Release|Any CPU
762+
{DAE3997B-D51B-4D9F-9F11-2EBC6FDDF57C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
763+
{DAE3997B-D51B-4D9F-9F11-2EBC6FDDF57C}.Debug|Any CPU.Build.0 = Debug|Any CPU
764+
{DAE3997B-D51B-4D9F-9F11-2EBC6FDDF57C}.Debug|x64.ActiveCfg = Debug|Any CPU
765+
{DAE3997B-D51B-4D9F-9F11-2EBC6FDDF57C}.Debug|x64.Build.0 = Debug|Any CPU
766+
{DAE3997B-D51B-4D9F-9F11-2EBC6FDDF57C}.Debug|x86.ActiveCfg = Debug|Any CPU
767+
{DAE3997B-D51B-4D9F-9F11-2EBC6FDDF57C}.Debug|x86.Build.0 = Debug|Any CPU
768+
{DAE3997B-D51B-4D9F-9F11-2EBC6FDDF57C}.Release|Any CPU.ActiveCfg = Release|Any CPU
769+
{DAE3997B-D51B-4D9F-9F11-2EBC6FDDF57C}.Release|Any CPU.Build.0 = Release|Any CPU
770+
{DAE3997B-D51B-4D9F-9F11-2EBC6FDDF57C}.Release|x64.ActiveCfg = Release|Any CPU
771+
{DAE3997B-D51B-4D9F-9F11-2EBC6FDDF57C}.Release|x64.Build.0 = Release|Any CPU
772+
{DAE3997B-D51B-4D9F-9F11-2EBC6FDDF57C}.Release|x86.ActiveCfg = Release|Any CPU
773+
{DAE3997B-D51B-4D9F-9F11-2EBC6FDDF57C}.Release|x86.Build.0 = Release|Any CPU
774+
{0C8F49D8-BD68-420A-907D-031B83737C50}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
775+
{0C8F49D8-BD68-420A-907D-031B83737C50}.Debug|Any CPU.Build.0 = Debug|Any CPU
776+
{0C8F49D8-BD68-420A-907D-031B83737C50}.Debug|x64.ActiveCfg = Debug|Any CPU
777+
{0C8F49D8-BD68-420A-907D-031B83737C50}.Debug|x64.Build.0 = Debug|Any CPU
778+
{0C8F49D8-BD68-420A-907D-031B83737C50}.Debug|x86.ActiveCfg = Debug|Any CPU
779+
{0C8F49D8-BD68-420A-907D-031B83737C50}.Debug|x86.Build.0 = Debug|Any CPU
780+
{0C8F49D8-BD68-420A-907D-031B83737C50}.Release|Any CPU.ActiveCfg = Release|Any CPU
781+
{0C8F49D8-BD68-420A-907D-031B83737C50}.Release|Any CPU.Build.0 = Release|Any CPU
782+
{0C8F49D8-BD68-420A-907D-031B83737C50}.Release|x64.ActiveCfg = Release|Any CPU
783+
{0C8F49D8-BD68-420A-907D-031B83737C50}.Release|x64.Build.0 = Release|Any CPU
784+
{0C8F49D8-BD68-420A-907D-031B83737C50}.Release|x86.ActiveCfg = Release|Any CPU
785+
{0C8F49D8-BD68-420A-907D-031B83737C50}.Release|x86.Build.0 = Release|Any CPU
758786
EndGlobalSection
759787
GlobalSection(SolutionProperties) = preSolution
760788
HideSolutionNode = FALSE
@@ -804,6 +832,8 @@ Global
804832
{13431429-2DB6-480F-B73F-CA019FE759E3} = {356BDB31-853E-43BB-8F9A-D8AC08F69EBB}
805833
{382B6332-4A57-458D-96EB-B312688A7604} = {356BDB31-853E-43BB-8F9A-D8AC08F69EBB}
806834
{EC62CE1D-ADD7-419A-84A9-D6A04E866197} = {356BDB31-853E-43BB-8F9A-D8AC08F69EBB}
835+
{DAE3997B-D51B-4D9F-9F11-2EBC6FDDF57C} = {356BDB31-853E-43BB-8F9A-D8AC08F69EBB}
836+
{0C8F49D8-BD68-420A-907D-031B83737C50} = {356BDB31-853E-43BB-8F9A-D8AC08F69EBB}
807837
EndGlobalSection
808838
GlobalSection(ExtensibilityGlobals) = postSolution
809839
SolutionGuid = {0D937D9E-E04B-4A68-B639-D4260473A388}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>net461</TargetFramework>
5+
<RootNamespace>RenameDynamicParameter.Test</RootNamespace>
6+
<IsPackable>false</IsPackable>
7+
</PropertyGroup>
8+
9+
<ItemGroup>
10+
<ProjectReference Include="..\Confuser.UnitTest\Confuser.UnitTest.csproj" />
11+
<ProjectReference Include="..\345_RenameDynamicParameter\345_RenameDynamicParameter.csproj" />
12+
</ItemGroup>
13+
14+
</Project>
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
using System.Threading.Tasks;
2+
using Confuser.Core;
3+
using Confuser.Core.Project;
4+
using Confuser.UnitTest;
5+
using Xunit;
6+
using Xunit.Abstractions;
7+
8+
namespace SignatureMismatch.Test {
9+
public class RenameDynamicParameterTest : TestBase {
10+
public RenameDynamicParameterTest(ITestOutputHelper outputHelper) : base(outputHelper) { }
11+
12+
[Fact]
13+
[Trait("Category", "Protection")]
14+
[Trait("Protection", "rename")]
15+
[Trait("Issue", "https://github.com/mkaring/ConfuserEx/issues/345")]
16+
[Trait("Issue", "https://github.com/mkaring/ConfuserEx/issues/349")]
17+
public async Task RenameDynamicParameter() =>
18+
await Run(
19+
"345_RenameDynamicParameter.exe",
20+
new[] {
21+
"static message",
22+
"dynamic message",
23+
"Overload String: Test",
24+
"Overload Integer: 1",
25+
"Override String: Test",
26+
"Override Integer: 1",
27+
"Field Value: 1",
28+
"Ctor String Value",
29+
"Ctor Integer Value: 1"
30+
},
31+
new SettingItem<Protection>("rename")
32+
);
33+
}
34+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<OutputType>Exe</OutputType>
5+
<TargetFramework>net461</TargetFramework>
6+
<RootNamespace>RenameDynamicParameter</RootNamespace>
7+
<StartupObject>RenameDynamicParameter.Program</StartupObject>
8+
</PropertyGroup>
9+
10+
<ItemGroup>
11+
<PackageReference Include="Microsoft.CSharp" Version="4.7.0" />
12+
</ItemGroup>
13+
14+
</Project>
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
using System;
2+
3+
namespace RenameDynamicParameter {
4+
public class ConstructorTestClass {
5+
private string _message;
6+
7+
private ConstructorTestClass(int value) : this("Ctor Integer Value: " + value) { }
8+
private ConstructorTestClass(string message) => _message = message;
9+
10+
private void WriteMessage() => Console.WriteLine(_message);
11+
12+
public static void TestInteger() {
13+
var instance = new ConstructorTestClass((dynamic)GetInteger());
14+
instance.WriteMessage();
15+
}
16+
17+
public static void TestString() {
18+
var instance = new ConstructorTestClass((dynamic)GetString());
19+
instance.WriteMessage();
20+
}
21+
22+
private static object GetInteger() => 1;
23+
private static object GetString() => "Ctor String Value";
24+
}
25+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
using System;
2+
3+
namespace RenameDynamicParameter {
4+
public class FieldTestClass {
5+
private int _storage;
6+
7+
private void FieldTestMethod() => Console.WriteLine("Field Value: " + _storage);
8+
9+
public void TestDynamic() {
10+
_storage = (dynamic)GetInteger();
11+
FieldTestMethod();
12+
}
13+
14+
private static object GetInteger() => 1;
15+
}
16+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
using System;
2+
3+
namespace RenameDynamicParameter {
4+
public static class OverloadTestClass {
5+
private static void OverloadTestMethod(int strobj) => Console.WriteLine("Overload Integer: " + strobj);
6+
private static void OverloadTestMethod(string strobj) => Console.WriteLine("Overload String: " + strobj);
7+
8+
public static void TestInteger() => OverloadTestMethod((dynamic)GetInteger());
9+
public static void TestString() => OverloadTestMethod((dynamic)GetString());
10+
11+
private static object GetInteger() => 1;
12+
private static object GetString() => "Test";
13+
}
14+
}

0 commit comments

Comments
 (0)