Browse Source

Detect local functions renamed by obfuscators

Obfuscators strip the CompilerGeneratedAttribute and rewrite the
"<caller>g__name|x_y" name, which is all IsLocalFunctionMethod had to go on.
The method then stays an ordinary static method, its display struct escapes by
ref into a plain call, and TransformDisplayClassUsage correctly refuses to
scalar-replace it -- so the closure fields leak into the output as
"<>c__DisplayClass29_0_.iid" (issue #3202).

The one marker an obfuscator cannot remove is the signature: Roslyn emits
struct closures exclusively for local functions, and no hand-written C# can
name a "<>c__DisplayClass" type, so a by-ref parameter of one identifies the
method regardless of what it is called.

#3202

Assisted-by: Claude:claude-opus-5[1m]:Claude Code
pull/4062/head
Siegfried Pammer 3 weeks ago
parent
commit
e85d2ba00e
  1. 34
      ICSharpCode.Decompiler/IL/Transforms/LocalFunctionDecompiler.cs

34
ICSharpCode.Decompiler/IL/Transforms/LocalFunctionDecompiler.cs

@ -781,36 +781,46 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -781,36 +781,46 @@ namespace ICSharpCode.Decompiler.IL.Transforms
var method = metadata.GetMethodDefinition(methodHandle);
var declaringType = method.GetDeclaringType();
if ((method.Attributes & MethodAttributes.Assembly) == 0 || !(method.IsCompilerGenerated(metadata) || declaringType.IsCompilerGenerated(metadata)))
if ((method.Attributes & MethodAttributes.Assembly) == 0)
return false;
if (!ParseLocalFunctionName(metadata.GetString(method.Name), out _, out _))
return false;
if ((method.IsCompilerGenerated(metadata) || declaringType.IsCompilerGenerated(metadata))
&& ParseLocalFunctionName(metadata.GetString(method.Name), out _, out _))
{
return true;
}
return true;
// Obfuscators strip the CompilerGeneratedAttribute and rewrite the
// "<caller>g__name|x_y" name, but they cannot remove the by-ref display-struct
// parameter: a compiler-generated struct closure is only ever passed by reference
// to the local functions that capture it.
return HasDisplayStructParameter(module, methodHandle);
}
public static bool LocalFunctionNeedsAccessibilityChange(MetadataFile module, MethodDefinitionHandle methodHandle)
/// <summary>
/// True if any parameter is a by-ref compiler-generated closure struct of this module.
/// </summary>
static bool HasDisplayStructParameter(MetadataFile module, MethodDefinitionHandle methodHandle)
{
if (!IsLocalFunctionMethod(module, methodHandle))
return false;
var metadata = module.Metadata;
var method = metadata.GetMethodDefinition(methodHandle);
FindRefStructParameters visitor = new FindRefStructParameters();
method.DecodeSignature(visitor, default);
foreach (var h in visitor.RefStructTypes)
{
var td = metadata.GetTypeDefinition(h);
if (td.IsCompilerGenerated(metadata) && td.IsValueType(metadata))
if (td.IsCompilerGenerated(metadata) && td.IsValueType(metadata) && td.HasGeneratedName(metadata))
return true;
}
return false;
}
public static bool LocalFunctionNeedsAccessibilityChange(MetadataFile module, MethodDefinitionHandle methodHandle)
{
return IsLocalFunctionMethod(module, methodHandle)
&& HasDisplayStructParameter(module, methodHandle);
}
public static bool IsLocalFunctionDisplayClass(MetadataFile module, TypeDefinitionHandle typeHandle, ILTransformContext context = null)
{
if (context != null && context.PEFile != module)

Loading…
Cancel
Save