| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159 |
- using System.Reflection.Emit;
- using System.Reflection.Metadata;
- using System.Reflection.Metadata.Ecma335;
- using System.Reflection.PortableExecutable;
- if (args.Length < 2)
- {
- Console.Error.WriteLine("Usage: LegacyMetadataInspector <assembly.dll> <type-or-method-filter>");
- return 2;
- }
- using var stream = File.OpenRead(Path.GetFullPath(args[0]));
- using var pe = new PEReader(stream);
- var metadata = pe.GetMetadataReader();
- var filter = args[1];
- var oneByte = new OpCode[0x100];
- var twoByte = new OpCode[0x100];
- foreach (var field in typeof(OpCodes).GetFields(System.Reflection.BindingFlags.Public | System.Reflection.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;
- }
- var methodsWithMatchingString = new HashSet<MethodDefinitionHandle>();
- foreach (var methodHandle in metadata.MethodDefinitions)
- {
- var method = metadata.GetMethodDefinition(methodHandle);
- if (method.RelativeVirtualAddress != 0 && MethodContainsString(method))
- methodsWithMatchingString.Add(methodHandle);
- }
- foreach (var typeHandle in metadata.TypeDefinitions)
- {
- var type = metadata.GetTypeDefinition(typeHandle);
- var typeName = FullTypeName(type.Namespace, type.Name);
- var methods = type.GetMethods().Select(handle => (Handle: handle, Value: metadata.GetMethodDefinition(handle))).ToArray();
- var typeMatches = typeName.Contains(filter, StringComparison.OrdinalIgnoreCase);
- var selectedMethods = typeMatches
- ? methods
- : methods.Where(item => metadata.GetString(item.Value.Name).Contains(filter, StringComparison.OrdinalIgnoreCase)
- || methodsWithMatchingString.Contains(item.Handle)).ToArray();
- if (selectedMethods.Length == 0)
- continue;
- Console.WriteLine($"TYPE {typeName}");
- foreach (var methodItem in selectedMethods)
- {
- var method = methodItem.Value;
- Console.WriteLine($" METHOD {metadata.GetString(method.Name)}");
- if (method.RelativeVirtualAddress == 0) continue;
- var bytes = pe.GetMethodBody(method.RelativeVirtualAddress).GetILBytes();
- if (bytes is null) continue;
- for (var offset = 0; offset < bytes.Length;)
- {
- var instructionOffset = offset;
- var code = bytes[offset++] == 0xfe ? twoByte[bytes[offset++]] : oneByte[bytes[offset - 1]];
- try
- {
- string? printedOperand = null;
- switch (code.OperandType)
- {
- case OperandType.InlineString:
- var userStringToken = BitConverter.ToInt32(bytes, offset); offset += 4;
- printedOperand = $"\"{metadata.GetUserString(MetadataTokens.UserStringHandle(userStringToken))}\"";
- break;
- case OperandType.InlineMethod:
- case OperandType.InlineField:
- case OperandType.InlineType:
- case OperandType.InlineTok:
- var entityToken = BitConverter.ToInt32(bytes, offset); offset += 4;
- printedOperand = DescribeEntity(MetadataTokens.EntityHandle(entityToken));
- break;
- case OperandType.InlineSig: offset += 4; break;
- case OperandType.ShortInlineI: offset += 1; break;
- case OperandType.InlineI: offset += 4; break;
- case OperandType.InlineI8: offset += 8; break;
- case OperandType.ShortInlineR: offset += 4; break;
- case OperandType.InlineR: offset += 8; break;
- case OperandType.ShortInlineBrTarget: offset += 1; break;
- case OperandType.InlineBrTarget: offset += 4; break;
- case OperandType.InlineSwitch:
- var count = BitConverter.ToInt32(bytes, offset); offset += 4 + count * 4; break;
- case OperandType.InlineVar: offset += 2; break;
- case OperandType.ShortInlineVar: offset += 1; break;
- }
- if (printedOperand is not null)
- Console.WriteLine($" IL_{instructionOffset:x4} {code.Name} {printedOperand}");
- }
- catch (Exception error)
- {
- Console.WriteLine($" IL_{instructionOffset:x4} {code.Name} <{error.GetType().Name}>");
- break;
- }
- }
- }
- }
- return 0;
- string FullTypeName(StringHandle namespaceHandle, StringHandle nameHandle)
- {
- var typeNamespace = metadata.GetString(namespaceHandle);
- var typeName = metadata.GetString(nameHandle);
- return string.IsNullOrEmpty(typeNamespace) ? typeName : $"{typeNamespace}.{typeName}";
- }
- string DescribeEntity(EntityHandle handle) => handle.Kind switch
- {
- HandleKind.MethodDefinition => $"method {metadata.GetString(metadata.GetMethodDefinition((MethodDefinitionHandle)handle).Name)}",
- HandleKind.FieldDefinition => $"field {metadata.GetString(metadata.GetFieldDefinition((FieldDefinitionHandle)handle).Name)}",
- HandleKind.MemberReference => $"member {metadata.GetString(metadata.GetMemberReference((MemberReferenceHandle)handle).Name)}",
- HandleKind.TypeDefinition => $"type {FullTypeName(metadata.GetTypeDefinition((TypeDefinitionHandle)handle).Namespace, metadata.GetTypeDefinition((TypeDefinitionHandle)handle).Name)}",
- HandleKind.TypeReference => $"type {FullTypeName(metadata.GetTypeReference((TypeReferenceHandle)handle).Namespace, metadata.GetTypeReference((TypeReferenceHandle)handle).Name)}",
- _ => handle.Kind.ToString()
- };
- bool MethodContainsString(MethodDefinition method)
- {
- try
- {
- var bytes = pe.GetMethodBody(method.RelativeVirtualAddress).GetILBytes();
- if (bytes is null) return false;
- for (var offset = 0; offset < bytes.Length;)
- {
- var code = bytes[offset++] == 0xfe ? twoByte[bytes[offset++]] : oneByte[bytes[offset - 1]];
- switch (code.OperandType)
- {
- case OperandType.InlineString:
- var token = BitConverter.ToInt32(bytes, offset); offset += 4;
- if (metadata.GetUserString(MetadataTokens.UserStringHandle(token)).Contains(filter, StringComparison.OrdinalIgnoreCase))
- return true;
- break;
- case OperandType.InlineMethod:
- case OperandType.InlineField:
- case OperandType.InlineType:
- case OperandType.InlineTok:
- case OperandType.InlineSig:
- case OperandType.ShortInlineR:
- case OperandType.InlineI:
- case OperandType.InlineBrTarget: offset += 4; break;
- case OperandType.InlineI8:
- case OperandType.InlineR: offset += 8; break;
- case OperandType.ShortInlineI:
- case OperandType.ShortInlineBrTarget:
- case OperandType.ShortInlineVar: offset += 1; break;
- case OperandType.InlineVar: offset += 2; break;
- case OperandType.InlineSwitch:
- var count = BitConverter.ToInt32(bytes, offset); offset += 4 + count * 4; break;
- }
- }
- }
- catch
- {
- return false;
- }
- return false;
- }
|