void WriteStructureBody(ILStructure s, HashSet <int> branchTargets, ref BlobReader body) { bool isFirstInstructionInStructure = true; bool prevInstructionWasBranch = false; int childIndex = 0; while (body.RemainingBytes > 0 && body.Offset < s.EndOffset) { int offset = body.Offset; if (childIndex < s.Children.Count && s.Children[childIndex].StartOffset <= offset && offset < s.Children[childIndex].EndOffset) { ILStructure child = s.Children[childIndex++]; WriteStructureHeader(child); WriteStructureBody(child, branchTargets, ref body); WriteStructureFooter(child); } else { if (!isFirstInstructionInStructure && (prevInstructionWasBranch || branchTargets.Contains(offset))) { output.WriteLine(); // put an empty line after branches, and in front of branch targets } var currentOpCode = ILParser.DecodeOpCode(ref body); body.Offset = offset; // reset IL stream WriteInstruction(output, metadata, s.MethodHandle, ref body); prevInstructionWasBranch = currentOpCode.IsBranch() || currentOpCode.IsReturn() || currentOpCode == ILOpCode.Throw || currentOpCode == ILOpCode.Rethrow || currentOpCode == ILOpCode.Switch; } isFirstInstructionInStructure = false; } }
void WriteRVA(BlobReader blob, int offset, ILOpCode opCode) { if (ShowRawRVAOffsetAndBytes) { output.Write("/* "); var tmp = blob; if (opCode == ILOpCode.Switch) { tmp.ReadInt32(); } else { ILParser.SkipOperand(ref tmp, opCode); } output.Write($"0x{offset:X8} {(ushort)opCode:X2}"); int appendSpaces = (ushort)opCode > 0xFF ? 14 : 16; while (blob.Offset < tmp.Offset) { output.Write($"{blob.ReadByte():X2}"); appendSpaces -= 2; } if (appendSpaces > 0) { output.Write(new string(' ', appendSpaces)); } output.Write(" */ "); } }
HashSet <int> GetBranchTargets(BlobReader blob) { HashSet <int> branchTargets = new HashSet <int>(); while (blob.RemainingBytes > 0) { var opCode = ILParser.DecodeOpCode(ref blob); if (opCode == ILOpCode.Switch) { branchTargets.UnionWith(ILParser.DecodeSwitchTargets(ref blob)); } else if (opCode.IsBranch()) { branchTargets.Add(ILParser.DecodeBranchTarget(ref blob, opCode)); } else { ILParser.SkipOperand(ref blob, opCode); } } return(branchTargets); }
protected virtual void WriteInstruction(ITextOutput output, MetadataReader metadata, MethodDefinitionHandle methodDefinition, ref BlobReader blob) { int offset = blob.Offset; if (ShowSequencePoints && nextSequencePointIndex < sequencePoints?.Count) { var sp = sequencePoints[nextSequencePointIndex]; if (sp.Offset <= offset) { output.Write("// sequence point: "); if (sp.Offset != offset) { output.Write("!! at " + DisassemblerHelpers.OffsetToString(sp.Offset) + " !!"); } if (sp.IsHidden) { output.WriteLine("hidden"); } else { output.WriteLine($"(line {sp.StartLine}, col {sp.StartColumn}) to (line {sp.EndLine}, col {sp.EndColumn}) in {sp.DocumentUrl}"); } nextSequencePointIndex++; } } ILOpCode opCode = ILParser.DecodeOpCode(ref blob); output.WriteLocalReference(DisassemblerHelpers.OffsetToString(offset), offset, isDefinition: true); output.Write(": "); if (opCode.IsDefined()) { output.WriteReference(new OpCodeInfo(opCode, opCode.GetDisplayName())); switch (opCode.GetOperandType()) { case OperandType.BrTarget: case OperandType.ShortBrTarget: output.Write(' '); int targetOffset = ILParser.DecodeBranchTarget(ref blob, opCode); output.WriteLocalReference($"IL_{targetOffset:x4}", targetOffset); break; case OperandType.Field: case OperandType.Method: case OperandType.Sig: case OperandType.Type: output.Write(' '); int metadataToken = blob.ReadInt32(); EntityHandle?handle = MetadataTokenHelpers.TryAsEntityHandle(metadataToken); try { handle?.WriteTo(module, output, genericContext); } catch (BadImageFormatException) { handle = null; } WriteMetadataToken(handle, metadataToken, spaceBefore: true); break; case OperandType.Tok: output.Write(' '); metadataToken = blob.ReadInt32(); handle = MetadataTokenHelpers.TryAsEntityHandle(metadataToken); switch (handle?.Kind) { case HandleKind.MemberReference: switch (metadata.GetMemberReference((MemberReferenceHandle)handle).GetKind()) { case MemberReferenceKind.Method: output.Write("method "); break; case MemberReferenceKind.Field: output.Write("field "); break; } break; case HandleKind.FieldDefinition: output.Write("field "); break; case HandleKind.MethodDefinition: output.Write("method "); break; } try { handle?.WriteTo(module, output, genericContext); } catch (BadImageFormatException) { handle = null; } WriteMetadataToken(handle, metadataToken, spaceBefore: true); break; case OperandType.ShortI: output.Write(' '); DisassemblerHelpers.WriteOperand(output, blob.ReadSByte()); break; case OperandType.I: output.Write(' '); DisassemblerHelpers.WriteOperand(output, blob.ReadInt32()); break; case OperandType.I8: output.Write(' '); DisassemblerHelpers.WriteOperand(output, blob.ReadInt64()); break; case OperandType.ShortR: output.Write(' '); DisassemblerHelpers.WriteOperand(output, blob.ReadSingle()); break; case OperandType.R: output.Write(' '); DisassemblerHelpers.WriteOperand(output, blob.ReadDouble()); break; case OperandType.String: metadataToken = blob.ReadInt32(); output.Write(' '); UserStringHandle?userString; string text; try { userString = MetadataTokens.UserStringHandle(metadataToken); text = metadata.GetUserString(userString.Value); } catch (BadImageFormatException) { userString = null; text = null; } if (userString != null) { DisassemblerHelpers.WriteOperand(output, text); } WriteMetadataToken(userString, metadataToken, spaceBefore: true); break; case OperandType.Switch: int[] targets = ILParser.DecodeSwitchTargets(ref blob); output.Write(" ("); for (int i = 0; i < targets.Length; i++) { if (i > 0) { output.Write(", "); } output.WriteLocalReference($"IL_{targets[i]:x4}", targets[i]); } output.Write(")"); break; case OperandType.Variable: output.Write(' '); int index = blob.ReadUInt16(); if (opCode == ILOpCode.Ldloc || opCode == ILOpCode.Ldloca || opCode == ILOpCode.Stloc) { DisassemblerHelpers.WriteVariableReference(output, metadata, methodDefinition, index); } else { DisassemblerHelpers.WriteParameterReference(output, metadata, methodDefinition, index); } break; case OperandType.ShortVariable: output.Write(' '); index = blob.ReadByte(); if (opCode == ILOpCode.Ldloc_s || opCode == ILOpCode.Ldloca_s || opCode == ILOpCode.Stloc_s) { DisassemblerHelpers.WriteVariableReference(output, metadata, methodDefinition, index); } else { DisassemblerHelpers.WriteParameterReference(output, metadata, methodDefinition, index); } break; } } else { ushort opCodeValue = (ushort)opCode; if (opCodeValue > 0xFF) { // split 16-bit value into two emitbyte directives output.WriteLine($".emitbyte 0x{(byte)(opCodeValue >> 8):x}"); // add label output.WriteLocalReference(DisassemblerHelpers.OffsetToString(offset + 1), offset + 1, isDefinition: true); output.Write(": "); output.Write($".emitbyte 0x{(byte)(opCodeValue & 0xFF):x}"); } else { output.Write($".emitbyte 0x{(byte)opCodeValue:x}"); } } output.WriteLine(); }
public virtual void Disassemble(PEFile module, MethodDefinitionHandle handle) { this.module = module ?? throw new ArgumentNullException(nameof(module)); metadata = module.Metadata; genericContext = new GenericContext(handle, module); signatureDecoder = new DisassemblerSignatureTypeProvider(module, output); var methodDefinition = metadata.GetMethodDefinition(handle); // start writing IL code output.WriteLine("// Method begins at RVA 0x{0:x4}", methodDefinition.RelativeVirtualAddress); if (methodDefinition.RelativeVirtualAddress == 0) { output.WriteLine("// Header size: {0}", 0); output.WriteLine("// Code size: {0} (0x{0:x})", 0); output.WriteLine(".maxstack {0}", 0); output.WriteLine(); return; } MethodBodyBlock body; BlobReader bodyBlockReader; try { body = module.Reader.GetMethodBody(methodDefinition.RelativeVirtualAddress); bodyBlockReader = module.Reader.GetSectionData(methodDefinition.RelativeVirtualAddress).GetReader(); } catch (BadImageFormatException ex) { output.WriteLine("// {0}", ex.Message); return; } var blob = body.GetILReader(); int headerSize = ILParser.GetHeaderSize(bodyBlockReader); output.WriteLine("// Header size: {0}", headerSize); output.WriteLine("// Code size: {0} (0x{0:x})", blob.Length); output.WriteLine(".maxstack {0}", body.MaxStack); var entrypointHandle = MetadataTokens.MethodDefinitionHandle(module.Reader.PEHeaders.CorHeader.EntryPointTokenOrRelativeVirtualAddress); if (handle == entrypointHandle) { output.WriteLine(".entrypoint"); } DisassembleLocalsBlock(handle, body); output.WriteLine(); sequencePoints = DebugInfo?.GetSequencePoints(handle) ?? EmptyList <DebugInfo.SequencePoint> .Instance; nextSequencePointIndex = 0; if (DetectControlStructure && blob.Length > 0) { blob.Reset(); HashSet <int> branchTargets = GetBranchTargets(blob); blob.Reset(); WriteStructureBody(new ILStructure(module, handle, genericContext, body), branchTargets, ref blob, methodDefinition.RelativeVirtualAddress + headerSize); } else { while (blob.RemainingBytes > 0) { cancellationToken.ThrowIfCancellationRequested(); WriteInstruction(output, metadata, handle, ref blob, methodDefinition.RelativeVirtualAddress); } WriteExceptionHandlers(module, handle, body); } sequencePoints = null; }