@@ -5,33 +5,49 @@ public class ReflectionCriticalAnalyzer : ICriticalAnalyzer<MethodDefinition>
55{
66 private readonly ObfuscationSettings _obfuscationSettings ;
77 private readonly List < MethodDefinition > _cachedMethods ;
8+ private readonly List < FieldDefinition > _cachedFields ;
9+ private readonly List < PropertyDefinition > _cachedProperties ;
10+ private readonly List < EventDefinition > _cachedEvents ;
11+ private readonly List < TypeDefinition > _cachedTypes ;
812 private static readonly string [ ] ReflectionMethods =
913 [
1014 nameof ( Type . GetMethod ) ,
1115 nameof ( Type . GetField ) ,
1216 nameof ( Type . GetProperty ) ,
1317 nameof ( Type . GetEvent ) ,
14- nameof ( Type . GetMember )
18+ nameof ( Type . GetMember ) ,
19+ nameof ( Type . GetTypeFromHandle )
1520 ] ;
1621
1722 public ReflectionCriticalAnalyzer ( IOptions < ObfuscationSettings > obfuscation )
1823 {
1924 _obfuscationSettings = obfuscation . Value ;
2025 _cachedMethods = [ ] ;
26+ _cachedFields = [ ] ;
27+ _cachedProperties = [ ] ;
28+ _cachedEvents = [ ] ;
29+ _cachedTypes = [ ] ;
2130 }
2231
2332 public IReadOnlyList < MethodDefinition > CachedMethods => _cachedMethods . AsReadOnly ( ) ;
33+ public IReadOnlyList < FieldDefinition > CachedFields => _cachedFields . AsReadOnly ( ) ;
34+ public IReadOnlyList < PropertyDefinition > CachedProperties => _cachedProperties . AsReadOnly ( ) ;
35+ public IReadOnlyList < EventDefinition > CachedEvents => _cachedEvents . AsReadOnly ( ) ;
36+ public IReadOnlyList < TypeDefinition > CachedTypes => _cachedTypes . AsReadOnly ( ) ;
2437
2538 public bool NotCriticalToMakeChanges ( MethodDefinition method )
2639 {
27- if ( _obfuscationSettings . ReflectionMembersObfuscationExclude == false )
40+ if ( ! _obfuscationSettings . ReflectionMembersObfuscationExclude )
2841 {
2942 return true ;
3043 }
3144 if ( _cachedMethods . FirstOrDefault ( x => x . Name . Equals ( method . Name ) ) != null )
3245 {
3346 return false ;
3447 }
48+
49+ bool foundReflection = false ;
50+
3551 if ( method . CilMethodBody is { } body )
3652 {
3753 body . ConstructSymbolicFlowGraph ( out var dataFlowGraph ) ;
@@ -46,40 +62,257 @@ public bool NotCriticalToMakeChanges(MethodDefinition method)
4662 {
4763 if ( IsReflection ( calledMethod ) )
4864 {
49- var traceArgument = TraceLdstrArgument ( body , instruction ) ;
50- if ( traceArgument ? . Operand is string traceMethodName )
65+ var traceArgument = TraceStringArgument ( body , instruction ) ;
66+ if ( traceArgument ? . Operand is string memberName )
5167 {
52- foreach ( var possibleMethod in method . DeclaringModule
53- . FindMembers ( )
54- . OfType < MethodDefinition > ( )
55- . Where ( x => x . Name . Equals ( traceMethodName ) ) )
68+ var module = method . DeclaringModule ;
69+ var allMembers = module . FindMembers ( ) ;
70+
71+ switch ( calledMethod . Name . Value )
5672 {
57- _cachedMethods . Add ( possibleMethod ) ;
58- return false ;
73+ case nameof ( Type . GetMethod ) :
74+ foreach ( var possibleMethod in allMembers . OfType < MethodDefinition > ( )
75+ . Where ( x => x . Name . Equals ( memberName ) ) )
76+ {
77+ if ( possibleMethod == method && ! _cachedMethods . Contains ( possibleMethod ) )
78+ {
79+ _cachedMethods . Add ( possibleMethod ) ;
80+ foundReflection = true ;
81+ }
82+ }
83+ break ;
84+
85+ case nameof ( Type . GetField ) :
86+ foreach ( var possibleField in allMembers . OfType < FieldDefinition > ( )
87+ . Where ( x => x . Name . Equals ( memberName ) ) )
88+ {
89+ _cachedFields . Add ( possibleField ) ;
90+ foundReflection = true ;
91+ }
92+ break ;
93+
94+ case nameof ( Type . GetProperty ) :
95+ foreach ( var possibleProperty in allMembers . OfType < PropertyDefinition > ( )
96+ . Where ( x => x . Name . Equals ( memberName ) ) )
97+ {
98+ _cachedProperties . Add ( possibleProperty ) ;
99+ foundReflection = true ;
100+ }
101+ break ;
102+
103+ case nameof ( Type . GetEvent ) :
104+ foreach ( var possibleEvent in allMembers . OfType < EventDefinition > ( )
105+ . Where ( x => x . Name . Equals ( memberName ) ) )
106+ {
107+ _cachedEvents . Add ( possibleEvent ) ;
108+ foundReflection = true ;
109+ }
110+ break ;
111+
112+ case nameof ( Type . GetMember ) :
113+ foreach ( var possibleMember in allMembers )
114+ {
115+ string ? memberNameToCheck = possibleMember switch
116+ {
117+ MethodDefinition m => m . Name ,
118+ FieldDefinition f => f . Name ,
119+ PropertyDefinition p => p . Name ,
120+ EventDefinition e => e . Name ,
121+ TypeDefinition t => t . Name ,
122+ _ => null
123+ } ;
124+
125+ if ( memberNameToCheck != null && memberNameToCheck . Equals ( memberName ) )
126+ {
127+ switch ( possibleMember )
128+ {
129+ case MethodDefinition m :
130+ _cachedMethods . Add ( m ) ;
131+ break ;
132+ case FieldDefinition f :
133+ _cachedFields . Add ( f ) ;
134+ break ;
135+ case PropertyDefinition p :
136+ _cachedProperties . Add ( p ) ;
137+ break ;
138+ case EventDefinition e :
139+ _cachedEvents . Add ( e ) ;
140+ break ;
141+ case TypeDefinition t :
142+ _cachedTypes . Add ( t ) ;
143+ break ;
144+ }
145+ foundReflection = true ;
146+ }
147+ }
148+ break ;
59149 }
60150 }
61151 }
152+ else if ( IsTypeGetTypeFromHandle ( calledMethod ) )
153+ {
154+ var typeFromHandle = TraceTypeFromHandle ( body , instruction ) ;
155+ if ( typeFromHandle != null )
156+ {
157+ _cachedTypes . Add ( typeFromHandle ) ;
158+ foundReflection = true ;
159+ }
160+ }
161+ }
162+ else if ( instruction ? . OpCode == CilOpCodes . Ldtoken && instruction . Operand is ITypeDefOrRef typeRef )
163+ {
164+ if ( typeRef . Resolve ( ) is TypeDefinition typeDef )
165+ {
166+ _cachedTypes . Add ( typeDef ) ;
167+ foundReflection = true ;
168+ }
62169 }
63170 }
64171 }
65172 }
66- return true ;
173+
174+ return ! foundReflection ;
175+ }
176+
177+ public bool NotCriticalToMakeChanges ( FieldDefinition field )
178+ {
179+ if ( ! _obfuscationSettings . ReflectionMembersObfuscationExclude )
180+ {
181+ return true ;
182+ }
183+ return _cachedFields . FirstOrDefault ( x => x . Name . Equals ( field . Name ) ) != null ;
184+ }
185+
186+ public bool NotCriticalToMakeChanges ( PropertyDefinition property )
187+ {
188+ if ( ! _obfuscationSettings . ReflectionMembersObfuscationExclude )
189+ {
190+ return true ;
191+ }
192+ return _cachedProperties . FirstOrDefault ( x => x . Name . Equals ( property . Name ) ) != null ;
193+ }
194+
195+ public bool NotCriticalToMakeChanges ( EventDefinition eventDef )
196+ {
197+ if ( ! _obfuscationSettings . ReflectionMembersObfuscationExclude )
198+ {
199+ return true ;
200+ }
201+ return _cachedEvents . FirstOrDefault ( x => x . Name . Equals ( eventDef . Name ) ) != null ;
202+ }
203+
204+ public bool NotCriticalToMakeChanges ( TypeDefinition type )
205+ {
206+ if ( ! _obfuscationSettings . ReflectionMembersObfuscationExclude )
207+ {
208+ return true ;
209+ }
210+ return _cachedTypes . FirstOrDefault ( x => x . Name . Equals ( type . Name ) ) != null ;
67211 }
68212
69213 private static bool IsReflection ( IMethodDefOrRef calledMethod )
70214 {
71215 return calledMethod . DeclaringType . IsSystemType ( ) &&
72216 ReflectionMethods . Contains ( calledMethod . Name . Value ) ;
73217 }
74- private static CilInstruction ? TraceLdstrArgument ( CilMethodBody body , CilInstruction instruction )
218+
219+ private static bool IsTypeGetTypeFromHandle ( IMethodDefOrRef calledMethod )
75220 {
76- for ( var i = body . Instructions . IndexOf ( instruction ) ; i > 0 && body . Instructions . Count . IsLess ( i ) == false ; i -- )
221+ return calledMethod . DeclaringType . IsSystemType ( ) &&
222+ calledMethod . Name . Value == nameof ( Type . GetTypeFromHandle ) ;
223+ }
224+
225+ private static TypeDefinition ? TraceTypeFromHandle ( CilMethodBody body , CilInstruction instruction )
226+ {
227+ var callIndex = body . Instructions . IndexOf ( instruction ) ;
228+ if ( callIndex <= 0 ) return null ;
229+
230+ for ( var i = callIndex - 1 ; i >= 0 ; i -- )
231+ {
232+ var prevInstruction = body . Instructions [ i ] ;
233+ if ( prevInstruction . OpCode == CilOpCodes . Ldtoken && prevInstruction . Operand is ITypeDefOrRef typeRef )
234+ {
235+ return typeRef . Resolve ( ) ;
236+ }
237+ }
238+ return null ;
239+ }
240+ private static CilInstruction ? TraceStringArgument ( CilMethodBody body , CilInstruction instruction )
241+ {
242+ return TraceStringArgumentSimple ( body , instruction ) ;
243+ }
244+
245+ private static CilInstruction ? TraceStringArgumentSimple ( CilMethodBody body , CilInstruction instruction )
246+ {
247+ var callIndex = body . Instructions . IndexOf ( instruction ) ;
248+ if ( callIndex <= 0 ) return null ;
249+
250+ for ( var i = callIndex - 1 ; i >= 0 ; i -- )
77251 {
78252 var previousInstruction = body . Instructions [ i ] ;
253+
79254 if ( previousInstruction . OpCode == CilOpCodes . Ldstr )
80255 {
81256 return previousInstruction ;
82257 }
258+
259+ if ( previousInstruction . OpCode == CilOpCodes . Ldloc_0 ||
260+ previousInstruction . OpCode == CilOpCodes . Ldloc_1 ||
261+ previousInstruction . OpCode == CilOpCodes . Ldloc_2 ||
262+ previousInstruction . OpCode == CilOpCodes . Ldloc_3 ||
263+ previousInstruction . OpCode == CilOpCodes . Ldloc_S ||
264+ previousInstruction . OpCode == CilOpCodes . Ldloc )
265+ {
266+ var variableIndex = GetVariableIndex ( previousInstruction ) ;
267+ if ( variableIndex >= 0 )
268+ {
269+ var stringInstruction = FindStringAssignmentToVariable ( body , variableIndex , i ) ;
270+ if ( stringInstruction != null )
271+ {
272+ return stringInstruction ;
273+ }
274+ }
275+ }
276+ }
277+ return null ;
278+ }
279+
280+ private static int GetVariableIndex ( CilInstruction instruction )
281+ {
282+ return instruction . OpCode . Code switch
283+ {
284+ CilCode . Ldloc_0 => 0 ,
285+ CilCode . Ldloc_1 => 1 ,
286+ CilCode . Ldloc_2 => 2 ,
287+ CilCode . Ldloc_3 => 3 ,
288+ CilCode . Ldloc_S => ( ( CilLocalVariable ) instruction . Operand ! ) . Index ,
289+ CilCode . Ldloc => ( ( CilLocalVariable ) instruction . Operand ! ) . Index ,
290+ _ => - 1
291+ } ;
292+ }
293+
294+ private static CilInstruction ? FindStringAssignmentToVariable ( CilMethodBody body , int variableIndex , int maxIndex )
295+ {
296+ for ( var i = 0 ; i < maxIndex ; i ++ )
297+ {
298+ var instruction = body . Instructions [ i ] ;
299+
300+ if ( ( instruction . OpCode == CilOpCodes . Stloc_0 && variableIndex == 0 ) ||
301+ ( instruction . OpCode == CilOpCodes . Stloc_1 && variableIndex == 1 ) ||
302+ ( instruction . OpCode == CilOpCodes . Stloc_2 && variableIndex == 2 ) ||
303+ ( instruction . OpCode == CilOpCodes . Stloc_3 && variableIndex == 3 ) ||
304+ ( instruction . OpCode == CilOpCodes . Stloc_S && ( ( CilLocalVariable ) instruction . Operand ! ) . Index == variableIndex ) ||
305+ ( instruction . OpCode == CilOpCodes . Stloc && ( ( CilLocalVariable ) instruction . Operand ! ) . Index == variableIndex ) )
306+ {
307+ for ( var j = i - 1 ; j >= 0 ; j -- )
308+ {
309+ var prevInstruction = body . Instructions [ j ] ;
310+ if ( prevInstruction . OpCode == CilOpCodes . Ldstr )
311+ {
312+ return prevInstruction ;
313+ }
314+ }
315+ }
83316 }
84317 return null ;
85318 }
0 commit comments