| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596 |
- using System.Reflection;
- using System.Reflection.Emit;
- using System.Runtime.Loader;
- if (args.Length < 2)
- {
- Console.Error.WriteLine("Usage: LegacyAssemblyInspector <assembly.dll> <type-or-method-filter>");
- return 2;
- }
- var assemblyPath = Path.GetFullPath(args[0]);
- AssemblyLoadContext.Default.Resolving += (_, name) =>
- {
- var candidate = Path.Combine(Path.GetDirectoryName(assemblyPath)!, $"{name.Name}.dll");
- return File.Exists(candidate) ? AssemblyLoadContext.Default.LoadFromAssemblyPath(candidate) : null;
- };
- var assembly = AssemblyLoadContext.Default.LoadFromAssemblyPath(assemblyPath);
- var filter = args[1];
- var oneByte = new OpCode[0x100];
- var twoByte = new OpCode[0x100];
- foreach (var field in typeof(OpCodes).GetFields(BindingFlags.Public | BindingFlags.Static))
- {
- if (field.GetValue(null) is not OpCode code) continue;
- var value = unchecked((ushort)code.Value);
- if (value < 0x100) oneByte[value] = code;
- else if ((value & 0xff00) == 0xfe00) twoByte[value & 0xff] = code;
- }
- Type[] assemblyTypes;
- try
- {
- assemblyTypes = assembly.GetTypes();
- }
- catch (ReflectionTypeLoadException error)
- {
- assemblyTypes = error.Types.Where(type => type is not null).Cast<Type>().ToArray();
- Console.Error.WriteLine($"Loaded {assemblyTypes.Length} partial types; {error.LoaderExceptions.Length} types could not be loaded.");
- foreach (var loaderError in error.LoaderExceptions.Where(item => item is not null).Select(item => item!.Message).Distinct().Take(20))
- Console.Error.WriteLine($" {loaderError}");
- }
- foreach (var type in assemblyTypes.Where(type => type.FullName?.Contains(filter, StringComparison.OrdinalIgnoreCase) == true
- || type.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly)
- .Any(method => method.Name.Contains(filter, StringComparison.OrdinalIgnoreCase))))
- {
- Console.WriteLine($"TYPE {type.FullName}");
- foreach (var method in type.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly))
- {
- Console.WriteLine($" METHOD {method}");
- foreach (var attribute in method.CustomAttributes)
- Console.WriteLine($" ATTR {attribute.AttributeType.FullName} {string.Join(",", attribute.ConstructorArguments.Select(value => value.Value))}");
- var body = method.GetMethodBody();
- if (body is null) continue;
- var bytes = body.GetILAsByteArray() ?? [];
- for (var offset = 0; offset < bytes.Length;)
- {
- var instructionOffset = offset;
- var code = bytes[offset++] == 0xfe ? twoByte[bytes[offset++]] : oneByte[bytes[offset - 1]];
- try
- {
- object? operand = null;
- switch (code.OperandType)
- {
- case OperandType.InlineString:
- operand = method.Module.ResolveString(BitConverter.ToInt32(bytes, offset)); offset += 4; break;
- case OperandType.InlineMethod:
- case OperandType.InlineField:
- case OperandType.InlineType:
- case OperandType.InlineTok:
- operand = method.Module.ResolveMember(BitConverter.ToInt32(bytes, offset), type.GetGenericArguments(), method.GetGenericArguments()); offset += 4; break;
- case OperandType.ShortInlineI: operand = bytes[offset++]; break;
- case OperandType.InlineI: operand = BitConverter.ToInt32(bytes, offset); offset += 4; break;
- case OperandType.InlineI8: operand = BitConverter.ToInt64(bytes, offset); offset += 8; break;
- case OperandType.ShortInlineR: operand = BitConverter.ToSingle(bytes, offset); offset += 4; break;
- case OperandType.InlineR: operand = BitConverter.ToDouble(bytes, offset); offset += 8; break;
- case OperandType.ShortInlineBrTarget: operand = offset + 1 + (sbyte)bytes[offset]; offset += 1; break;
- case OperandType.InlineBrTarget: operand = offset + 4 + BitConverter.ToInt32(bytes, offset); offset += 4; break;
- case OperandType.InlineSwitch:
- var count = BitConverter.ToInt32(bytes, offset); offset += 4 + count * 4; operand = $"{count} targets"; break;
- case OperandType.InlineVar: operand = BitConverter.ToUInt16(bytes, offset); offset += 2; break;
- case OperandType.ShortInlineVar: operand = bytes[offset++]; break;
- }
- if (operand is string || operand is MemberInfo)
- Console.WriteLine($" IL_{instructionOffset:x4} {code.Name} {operand}");
- }
- catch (Exception error)
- {
- Console.WriteLine($" IL_{instructionOffset:x4} {code.Name} <{error.GetType().Name}>");
- break;
- }
- }
- }
- }
- return 0;
|