1- namespace BitMono . Protections ;
1+ namespace BitMono . Protections ;
22
33[ DoNotResolve ( MemberInclusionFlags . SpecialRuntime | MemberInclusionFlags . Model | MemberInclusionFlags . Reflection ) ]
44public class FullRenamer : Protection
@@ -12,21 +12,116 @@ public FullRenamer(Renamer renamer, IBitMonoServiceProvider serviceProvider) : b
1212
1313 public override Task ExecuteAsync ( )
1414 {
15- foreach ( var method in Context . Parameters . Members . OfType < MethodDefinition > ( ) )
15+ // -----------------------------------------------------------------
16+ // Behaviour:
17+ //
18+ // 1. Interface methods within the obfuscation scope and their
19+ // implementations are renamed TOGETHER with the same random
20+ // name (rather than being skipped wholesale because of
21+ // IsVirtual, which would break the API surface).
22+ // 2. Compiler-generated async state-machine types
23+ // ("<MethodName>d__N") are renamed as well so the original
24+ // method name does not leak through stack traces.
25+ // 3. True "override" methods (IsVirtual && !IsNewSlot) are left
26+ // alone because they must keep the same name as the base
27+ // class method they override.
28+ // 4. Implicit implementations of interfaces that live OUTSIDE
29+ // the current module (IsVirtual && IsNewSlot && IsFinal but
30+ // not picked up by phase 1) are skipped so we do not break
31+ // the contract and trigger TypeLoadException at assembly
32+ // load time.
33+ // -----------------------------------------------------------------
34+
35+ var members = Context . Parameters . Members ;
36+ var membersList = members . ToList ( ) ;
37+ var allTypes = membersList . OfType < TypeDefinition > ( ) . ToList ( ) ;
38+ var allMethods = membersList . OfType < MethodDefinition > ( ) . ToList ( ) ;
39+ var allFields = membersList . OfType < FieldDefinition > ( ) . ToList ( ) ;
40+
41+ // Track which methods have already received a new name through
42+ // phase 1 so the standard loop below can skip them.
43+ var alreadyRenamed = new HashSet < MethodDefinition > ( ) ;
44+
45+ // Phase 1: collect interface methods in scope, reserve a random
46+ // name for each, and apply the same name to every
47+ // matching implementation that is also in scope.
48+ var concreteTypes = allTypes
49+ . Where ( t => ! t . IsInterface && ! t . IsModuleType && ! t . IsCompilerGenerated ( ) )
50+ . ToList ( ) ;
51+
52+ foreach ( var iface in allTypes . Where ( t => t . IsInterface && ! t . IsCompilerGenerated ( ) ) )
1653 {
17- if ( method . DeclaringType ? . IsModuleType == true )
54+ foreach ( var ifaceMethod in iface . Methods . ToList ( ) )
1855 {
19- continue ;
20- }
21- if ( method . IsConstructor || method . IsVirtual )
22- {
23- continue ;
24- }
25- if ( method . IsCompilerGenerated ( ) )
26- {
27- continue ;
56+ if ( ifaceMethod . IsConstructor ) continue ;
57+ if ( ifaceMethod . IsCompilerGenerated ( ) ) continue ;
58+ if ( ! ShouldRenameMethodName ( ifaceMethod ) ) continue ;
59+
60+ var groupName = _renamer . RenameUnsafely ( ) ;
61+
62+ // Capture the original name so we can match implementations
63+ // by name + signature BEFORE we overwrite the interface name.
64+ var originalIfaceName = ifaceMethod . Name ? . Value ;
65+
66+ ifaceMethod . Name = groupName ;
67+ alreadyRenamed . Add ( ifaceMethod ) ;
68+ RenameParametersOf ( ifaceMethod ) ;
69+ RenameAsyncStateMachineFor ( ifaceMethod , originalIfaceName ) ;
70+
71+ // Find matching implementations.
72+ foreach ( var concrete in concreteTypes )
73+ {
74+ if ( ! ImplementsInterface ( concrete , iface ) ) continue ;
75+
76+ foreach ( var implMethod in concrete . Methods . ToList ( ) )
77+ {
78+ if ( implMethod . IsConstructor ) continue ;
79+ if ( implMethod . IsCompilerGenerated ( ) ) continue ;
80+ if ( alreadyRenamed . Contains ( implMethod ) ) continue ;
81+ if ( originalIfaceName == null ) continue ;
82+ if ( implMethod . Name ? . Value != originalIfaceName ) continue ;
83+ if ( ! SignatureCompatible ( implMethod , ifaceMethod ) ) continue ;
84+
85+ var originalImplName = implMethod . Name ? . Value ;
86+ implMethod . Name = groupName ;
87+ alreadyRenamed . Add ( implMethod ) ;
88+ RenameParametersOf ( implMethod ) ;
89+ RenameAsyncStateMachineFor ( implMethod , originalImplName ) ;
90+ }
91+ }
2892 }
93+ }
94+
95+ // Phase 2: standard rename loop for everything that was not
96+ // handled by phase 1.
97+ foreach ( var method in allMethods )
98+ {
99+ if ( alreadyRenamed . Contains ( method ) ) continue ;
100+ if ( method . DeclaringType ? . IsModuleType == true ) continue ;
101+ if ( method . IsConstructor ) continue ;
102+ if ( method . IsCompilerGenerated ( ) ) continue ;
103+
104+ // Skip true overrides: IsVirtual && !IsNewSlot means the
105+ // method reuses an inherited vtable slot (override keyword
106+ // in C#). Renaming it would break the link to the base
107+ // class method.
108+ if ( method . IsVirtual && ! method . IsNewSlot ) continue ;
109+
110+ // Skip implementations of external interfaces. The C#
111+ // compiler marks implicit interface impls with
112+ // IsVirtual+IsNewSlot+IsFinal+IsHideBySig. If phase 1 did
113+ // not pick them up (alreadyRenamed check above), the
114+ // corresponding interface lives in another assembly. If we
115+ // rename the impl, the contract breaks and the CLR throws
116+ // at assembly load time:
117+ // System.TypeLoadException: Method '...' in type '...'
118+ // does not have an implementation.
119+ if ( method . IsVirtual && method . IsNewSlot && method . IsFinal ) continue ;
120+
121+ var originalName = method . Name ? . Value ;
29122 _renamer . Rename ( method ) ;
123+ RenameAsyncStateMachineFor ( method , originalName ) ;
124+
30125 if ( ! method . HasParameters ( ) )
31126 {
32127 continue ;
@@ -40,30 +135,95 @@ public override Task ExecuteAsync()
40135 _renamer . Rename ( parameter . Definition ) ;
41136 }
42137 }
43- foreach ( var type in Context . Parameters . Members . OfType < TypeDefinition > ( ) )
138+
139+ // Type renames - unchanged from the original logic.
140+ foreach ( var type in allTypes )
44141 {
45- if ( type . IsModuleType )
46- {
47- continue ;
48- }
49- if ( type . IsCompilerGenerated ( ) )
50- {
51- continue ;
52- }
142+ if ( type . IsModuleType ) continue ;
143+ if ( type . IsCompilerGenerated ( ) ) continue ;
53144 _renamer . Rename ( type ) ;
54145 }
55- foreach ( var field in Context . Parameters . Members . OfType < FieldDefinition > ( ) )
146+
147+ // Field renames - unchanged from the original logic.
148+ foreach ( var field in allFields )
56149 {
57- if ( field . DeclaringType ? . IsModuleType == true )
58- {
59- continue ;
60- }
61- if ( field . IsCompilerGenerated ( ) )
62- {
63- continue ;
64- }
150+ if ( field . DeclaringType ? . IsModuleType == true ) continue ;
151+ if ( field . IsCompilerGenerated ( ) ) continue ;
65152 _renamer . Rename ( field ) ;
66153 }
154+
67155 return Task . CompletedTask ;
68156 }
69- }
157+
158+ private static bool ShouldRenameMethodName ( MethodDefinition method )
159+ {
160+ if ( method . IsCompilerGenerated ( ) ) return false ;
161+ if ( method . IsConstructor ) return false ;
162+ return true ;
163+ }
164+
165+ private void RenameParametersOf ( MethodDefinition method )
166+ {
167+ if ( ! method . HasParameters ( ) ) return ;
168+ foreach ( var parameter in method . Parameters )
169+ {
170+ if ( parameter . Definition == null ) continue ;
171+ _renamer . Rename ( parameter . Definition ) ;
172+ }
173+ }
174+
175+ private void RenameAsyncStateMachineFor ( MethodDefinition method , string ? originalName )
176+ {
177+ if ( string . IsNullOrEmpty ( originalName ) ) return ;
178+ var declaring = method . DeclaringType ;
179+ if ( declaring == null ) return ;
180+ if ( declaring . NestedTypes == null ) return ;
181+
182+ // Compiler-generated state-machine types are named
183+ // "<MethodName>d__N" (async/await) or "<MethodName>b__N_M"
184+ // (lambdas). Once we know the original method name, we can find
185+ // the related nested types and rename them along with it.
186+ var prefix = "<" + originalName + ">" ;
187+ foreach ( var nested in declaring . NestedTypes . ToList ( ) )
188+ {
189+ var nestedName = nested . Name ? . Value ;
190+ if ( nestedName == null ) continue ;
191+ if ( ! nestedName . StartsWith ( prefix , StringComparison . Ordinal ) ) continue ;
192+ nested . Name = _renamer . RenameUnsafely ( ) ;
193+ }
194+ }
195+
196+ private static bool ImplementsInterface ( TypeDefinition type , TypeDefinition iface )
197+ {
198+ if ( type . Interfaces == null ) return false ;
199+ foreach ( var implemented in type . Interfaces )
200+ {
201+ var resolved = implemented . Interface ? . Resolve ( ) ;
202+ if ( resolved == iface ) return true ;
203+ }
204+ // Also walk the base-type chain.
205+ var baseTypeDef = type . BaseType ? . Resolve ( ) ;
206+ if ( baseTypeDef != null && baseTypeDef != type )
207+ {
208+ return ImplementsInterface ( baseTypeDef , iface ) ;
209+ }
210+ return false ;
211+ }
212+
213+ private static bool SignatureCompatible ( MethodDefinition a , MethodDefinition b )
214+ {
215+ if ( a . Signature == null || b . Signature == null ) return false ;
216+ if ( a . Signature . ParameterTypes . Count != b . Signature . ParameterTypes . Count ) return false ;
217+ if ( a . Signature . GenericParameterCount != b . Signature . GenericParameterCount ) return false ;
218+ var aRet = a . Signature . ReturnType ? . FullName ;
219+ var bRet = b . Signature . ReturnType ? . FullName ;
220+ if ( ! string . Equals ( aRet , bRet , StringComparison . Ordinal ) ) return false ;
221+ for ( int i = 0 ; i < a . Signature . ParameterTypes . Count ; i ++ )
222+ {
223+ var aParam = a . Signature . ParameterTypes [ i ] ? . FullName ;
224+ var bParam = b . Signature . ParameterTypes [ i ] ? . FullName ;
225+ if ( ! string . Equals ( aParam , bParam , StringComparison . Ordinal ) ) return false ;
226+ }
227+ return true ;
228+ }
229+ }
0 commit comments