/// <summary> /// Analyzes code starting from the given location. That location /// should be the entry point of a procedure, or otherwise the /// analysis may not work correctly. /// </summary> /// <param name="entryPoint">Specifies the location to start analysis. /// This location is relative to the beginning of the image.</param> /// <param name="entryType">Type of entry, should usually be JMP or /// CALL.</param> public virtual void Analyze(Address entryPoint, XRefType entryType) { GenerateBasicBlocks(entryPoint, entryType); GenerateControlFlowGraph(); GenerateProcedures(); AddBasicBlocksToProcedures(); }
public XRef(XRefType type, Address source, Address target, Address dataLocation) { this.Source = source; this.Target = target; this.Type = type; this.DataLocation = dataLocation; }
protected virtual BasicBlockType GetBasicBlockType(XRefType flowType) { switch (flowType) { case XRefType.ConditionalJump: return(BasicBlockType.Branch); case XRefType.NearJump: case XRefType.FarJump: case XRefType.NearIndexedJump: return(BasicBlockType.Jump); case XRefType.Interrupt: case XRefType.NearCall: case XRefType.FarCall: return(BasicBlockType.Call); case XRefType.NearReturn: case XRefType.FarReturn: case XRefType.InterruptReturn: return(BasicBlockType.Return); case XRefType.Halt: return(BasicBlockType.Halt); default: return(BasicBlockType.Unknown); } }
public ReferenceType(XRefType type, string id) : base() { if (type == XRefType.Unknown) { throw new ArgumentNullException(nameof(type)); } this.XRefType = type; if (string.IsNullOrWhiteSpace(id)) { throw new ArgumentNullException(nameof(id)); } this.XRefId = id; }
protected virtual XRef CreateBranchJumpCallXRef( XRefType type, Address source, Instruction instruction) { Address target = ResolveFlowInstructionTarget(instruction.Operands[0]); if (target == Address.Invalid) { AddError(source, ErrorCode.DynamicTarget, "Cannot determine the target of {0} instruction.", instruction.Operation); } return(new XRef( type: type, source: source, target: target )); }
/// <summary> /// Determines whether the given cross reference may possibly fall /// through to a "default" flow path such as "branch not taken" or /// "return from call". /// </summary> /// <param name="xrefType">Type of cross reference.</param> /// <returns></returns> /// <remarks> /// This method assumes that the program is well-formed, such that /// a "fall-through" path is always possible to be taken. /// /// In particular, if the instruction is a conditional jump, we /// assume that the condition may be true or false, so that both /// "jump" and "no jump" is a reachable branch. If the code is /// malformed such that one of the branches will never execute, the /// analysis may not work correctly. /// /// Likewise, if the instruction is a procedure call, we assume that /// the procedure being called will eventually return. If the /// procedure never returns, the analysis may not work correctly. /// </remarks> protected virtual bool CanFallThrough(XRefType xrefType) { switch (xrefType) { case XRefType.FarJump: case XRefType.NearJump: case XRefType.NearIndexedJump: case XRefType.FarReturn: case XRefType.NearReturn: case XRefType.InterruptReturn: case XRefType.Halt: return(false); default: return(true); } }
protected virtual bool NeedsActiveXRef(XRefType flowType) { switch (flowType) { case XRefType.ConditionalJump: case XRefType.FarCall: case XRefType.NearCall: //case XRefType.Interrupt: case XRefType.FarJump: case XRefType.NearJump: case XRefType.NearIndexedJump: return(true); default: return(false); } }
protected virtual XRef CreateFlowXRef( XRefType xrefType, Address source, Instruction instruction) { switch (xrefType) { case XRefType.ConditionalJump: case XRefType.FarJump: case XRefType.NearJump: case XRefType.FarCall: case XRefType.NearCall: return(CreateBranchJumpCallXRef(xrefType, source, instruction)); case XRefType.NearIndexedJump: throw new NotImplementedException(); default: return(null); } }
public static string AsString(this XRefType enumValue) { switch (enumValue) { case XRefType.Adresse: return(XRefTypeString.Adresse); case XRefType.Auftraggeber: return(XRefTypeString.Auftraggeber); case XRefType.Kontakt: return(XRefTypeString.Kontakt); case XRefType.Mitarbeiter: return(XRefTypeString.Mitarbeiter); case XRefType.LV: return(XRefTypeString.LV); case XRefType.LVVariante: return(XRefTypeString.LVVariante); case XRefType.LVNachtrag: return(XRefTypeString.LVNachtrag); case XRefType.LVBieter: return(XRefTypeString.LVBieter); case XRefType.Projekt: return(XRefTypeString.Projekt); case XRefType.Objekt: return(XRefTypeString.Objekt); case XRefType.Position: return(XRefTypeString.Position); case XRefType.Hierarchiestufe: return(XRefTypeString.Hierarchiestufe); case XRefType.Geraet: return(XRefTypeString.Geraet); case XRefType.Leistung: return(XRefTypeString.Leistung); case XRefType.Standardleistungskatalog: return(XRefTypeString.Standardleistungskatalog); case XRefType.Kreditorenrechnung: return(XRefTypeString.Kreditorenrechnung); } return(XRefTypeString.Unknown); }
/// <summary> /// Analyzes code starting from the given location, and create basic /// blocks iteratively. /// </summary> public void GenerateBasicBlocks(Address entryPoint, XRefType entryType) { Address address = entryPoint; // Maintain a queue of basic block entry points to analyze. At // the beginning, only the user-specified entry point is in the // queue. As we encounter b/c/j instructions during the course // of analysis, we push the target addresses to the queue of // entry points to be analyzed later. PriorityQueue <XRef> xrefQueue = new PriorityQueue <XRef>(XRef.CompareByPriority); // Maintain a list of all procedure calls (with known target) // encountered during the analysis. After we finish analyzing // all the basic blocks, we update the list of procedures. // List<XRef> xrefCalls = new List<XRef>(); // Create a a dummy xref entry using the user-supplied starting // address. xrefQueue.Enqueue(new XRef( type: entryType, source: Address.Invalid, target: entryPoint )); // Analyze each cross reference in order of their priority. // In particular, if the xref is an indexed jump, we delay its // processing until we have processed all other types of xrefs. // This reduces the chance that we process past the end of a // jump table. while (!xrefQueue.IsEmpty) { XRef entry = xrefQueue.Dequeue(); // Handle jump table entry, whose Target == Invalid. if (entry.Type == XRefType.NearIndexedJump) { System.Diagnostics.Debug.Assert(entry.Target == Address.Invalid); // Fill the Target field to make it a static xref. entry = ProcessJumpTableEntry(entry, xrefQueue); if (entry == null) // end of jump table { continue; } } // Skip other dynamic xrefs. if (entry.Target == Address.Invalid) { CrossReferences.Add(entry); continue; } // Process the basic block starting at the target address. BasicBlock block = AnalyzeBasicBlock(entry, xrefQueue); if (block != null) { //int count = block.Length; //int baseOffset = PointerToOffset(entry.Target); //proc.CodeRange.AddInterval(baseOffset, baseOffset + count); //proc.ByteRange.AddInterval(baseOffset, baseOffset + count); //for (int j = 0; j < count; j++) //{ // image[baseOffset + j].Procedure = proc; //} #if false proc.AddBasicBlock(block); #endif } CrossReferences.Add(entry); } }
public XRef(XRefType type, Address source, Address target) : this(type, source, target, Address.Invalid) { }
/// <summary> /// Gets the image associated with the segment specified by its id. /// </summary> /// <param name="segmentId">Id of the segment to resolve.</param> /// <returns>The image associated with the given segment, or null if /// the segment id is invalid.</returns> //protected abstract ImageChunk ResolveSegment(int segmentId); #region Flow Analysis Methods /// <summary> /// Analyzes a contiguous sequence of instructions that form a basic /// block. A basic block terminates as soon as any of the following /// conditions is true: /// - An analysis error occurs /// - An block terminating instructions: RET, RETF, IRET, HLT. /// - A b/c/j instruction: Jcc, JMP, JMPF, LOOPcc. /// </summary> /// <param name="start">Address to begin analysis.</param> /// <param name="xrefs">Collection to add xrefs to.</param> /// <returns> /// A new BasicBlock if one was created during the analysis. /// If no new BasicBlocks are created, or if an existing block was /// split into two, returns null. /// </returns> // TODO: should be roll-back the entire basic block if we // encounters an error on our way? maybe not. protected virtual BasicBlock AnalyzeBasicBlock(XRef start, ICollection <XRef> xrefs) { Address ip = start.Target; // instruction pointer if (!image.IsAddressValid(ip)) { AddError(ip, ErrorCode.OutOfImage, "XRef target is outside of the image (referred from {0})", start.Source); return(null); } // Check if the entry address is already analyzed. ByteAttribute b = image[ip]; if (b.Type != ByteType.Unknown) { // Fail if we ran into data or padding while expecting code. if (b.Type != ByteType.Code) { AddError(ip, ErrorCode.RanIntoData, "XRef target is in the middle of data (referred from {0})", start.Source); return(null); } // Now the byte was previously analyzed as code. We must have // already created a basic block that contains this byte. BasicBlock block = BasicBlocks.Find(ip); System.Diagnostics.Debug.Assert(block != null); // If the existing block starts at this address, we're done. if (block.Location == ip) { return(null); } // TBD: recover the following in some way... #if false if (image[b.BasicBlock.StartAddress].Address.Segment != pos.Segment) { AddError(pos, ErrorCategory.Error, "Ran into the middle of a block [{0},{1}) from another segment " + "when processing block {2} referred from {3}", b.BasicBlock.StartAddress, b.BasicBlock.EndAddress, start.Target, start.Source); return(null); } #endif // Now split the existing basic block into two. This requires // that the cut-off point is at instruction boundary. if (!b.IsLeadByte) { AddError(ip, ErrorCode.RanIntoCode, "XRef target is in the middle of an instruction (referred from {0})", start.Source); return(null); } BasicBlock[] subBlocks = BasicBlocks.SplitBasicBlock(block, ip, image); // Create a xref from the previous block to this block. XRef xref = CreateFallThroughXRef(GetLastInstructionInBasicBlock(subBlocks[0]), ip); xrefs.Add(xref); return(null); } // TODO: Move the above into a separate procedure. // Analyze each instruction in sequence until we encounter // analyzed code, flow instruction, or an error condition. BasicBlockType blockType = BasicBlockType.Unknown; while (true) { // Decode an instruction at this location. Address instructionStart = ip; Instruction insn = CreateInstruction(ip, start); if (insn == null) { AddError(ip, ErrorCode.BrokenBasicBlock, "Basic block ended prematurally because of invalid instruction."); blockType = BasicBlockType.Broken; break; } Address instructionEnd = ip + insn.EncodedLength; // Advance the instruction pointer. ip = instructionEnd; // Determine whether this instruction affects control flow. XRefType flowType = GetFlowInstructionType(insn.Operation); if (flowType != XRefType.None) { // Creates an active cross reference if necessary. if (NeedsActiveXRef(flowType)) { XRef xref = CreateFlowXRef(flowType, instructionStart, insn); if (xref != null) { xrefs.Add(xref); } } // Creates a fall-through cross reference if necessary. if (CanFallThrough(flowType)) { XRef xref = CreateFallThroughXRef(instructionStart, instructionEnd); xrefs.Add(xref); } // Terminate the block. blockType = GetBasicBlockType(flowType); break; } // If the new location is already analyzed as code, create a // control-flow edge from the previous block to the existing // block, and we are done. if (!image.IsAddressValid(ip)) { blockType = BasicBlockType.Broken; break; } if (image[ip].Type == ByteType.Code) { System.Diagnostics.Debug.Assert(image[ip].IsLeadByte); XRef xref = CreateFallThroughXRef(instructionStart, instructionEnd); xrefs.Add(xref); blockType = BasicBlockType.FallThrough; break; } } // Create a basic block unless we failed on the first instruction. if (ip.Offset > start.Target.Offset) { BasicBlock block = new BasicBlock(start.Target, ip, blockType, image); BasicBlocks.Add(block); } return(null); }
/// <summary> /// Determines whether the given cross reference may possibly fall /// through to a "default" flow path such as "branch not taken" or /// "return from call". /// </summary> /// <param name="xrefType">Type of cross reference.</param> /// <returns></returns> /// <remarks> /// This method assumes that the program is well-formed, such that /// a "fall-through" path is always possible to be taken. /// /// In particular, if the instruction is a conditional jump, we /// assume that the condition may be true or false, so that both /// "jump" and "no jump" is a reachable branch. If the code is /// malformed such that one of the branches will never execute, the /// analysis may not work correctly. /// /// Likewise, if the instruction is a procedure call, we assume that /// the procedure being called will eventually return. If the /// procedure never returns, the analysis may not work correctly. /// </remarks> protected virtual bool CanFallThrough(XRefType xrefType) { switch (xrefType) { case XRefType.FarJump: case XRefType.NearJump: case XRefType.NearIndexedJump: case XRefType.FarReturn: case XRefType.NearReturn: case XRefType.InterruptReturn: case XRefType.Halt: return false; default: return true; } }
protected virtual XRef CreateBranchJumpCallXRef( XRefType type, Address source, Instruction instruction) { Address target = ResolveFlowInstructionTarget(instruction.Operands[0]); if (target == Address.Invalid) { AddError(source, ErrorCode.DynamicTarget, "Cannot determine the target of {0} instruction.", instruction.Operation); } return new XRef( type: type, source: source, target: target ); }
/** * Signals that a new XRef object (table or stream) starts. * @param startBytePos the offset to start at * @param type the type of the Xref object */ public void nextXrefObj(long startBytePos, XRefType type) { bytePosToXrefMap[startBytePos] = curXrefTrailerObj = new XrefTrailerObj(); curXrefTrailerObj.xrefType = type; }
protected virtual bool NeedsActiveXRef(XRefType flowType) { switch (flowType) { case XRefType.ConditionalJump: case XRefType.FarCall: case XRefType.NearCall: //case XRefType.Interrupt: case XRefType.FarJump: case XRefType.NearJump: case XRefType.NearIndexedJump: return true; default: return false; } }
protected virtual BasicBlockType GetBasicBlockType(XRefType flowType) { switch (flowType) { case XRefType.ConditionalJump: return BasicBlockType.Branch; case XRefType.NearJump: case XRefType.FarJump: case XRefType.NearIndexedJump: return BasicBlockType.Jump; case XRefType.Interrupt: case XRefType.NearCall: case XRefType.FarCall: return BasicBlockType.Call; case XRefType.NearReturn: case XRefType.FarReturn: case XRefType.InterruptReturn: return BasicBlockType.Return; case XRefType.Halt: return BasicBlockType.Halt; default: return BasicBlockType.Unknown; } }
protected virtual XRef CreateFlowXRef( XRefType xrefType, Address source, Instruction instruction) { switch (xrefType) { case XRefType.ConditionalJump: case XRefType.FarJump: case XRefType.NearJump: case XRefType.FarCall: case XRefType.NearCall: return CreateBranchJumpCallXRef(xrefType, source, instruction); case XRefType.NearIndexedJump: throw new NotImplementedException(); default: return null; } }
/** * Default constructor. */ public XrefTrailerObj() { xrefType = XRefType.TABLE; }
/// <summary> /// Analyzes code starting from the given location, and create basic /// blocks iteratively. /// </summary> public void GenerateBasicBlocks(Address entryPoint, XRefType entryType) { Address address = entryPoint; // Maintain a queue of basic block entry points to analyze. At // the beginning, only the user-specified entry point is in the // queue. As we encounter b/c/j instructions during the course // of analysis, we push the target addresses to the queue of // entry points to be analyzed later. PriorityQueue<XRef> xrefQueue = new PriorityQueue<XRef>(XRef.CompareByPriority); // Maintain a list of all procedure calls (with known target) // encountered during the analysis. After we finish analyzing // all the basic blocks, we update the list of procedures. // List<XRef> xrefCalls = new List<XRef>(); // Create a a dummy xref entry using the user-supplied starting // address. xrefQueue.Enqueue(new XRef( type: entryType, source: Address.Invalid, target: entryPoint )); // Analyze each cross reference in order of their priority. // In particular, if the xref is an indexed jump, we delay its // processing until we have processed all other types of xrefs. // This reduces the chance that we process past the end of a // jump table. while (!xrefQueue.IsEmpty) { XRef entry = xrefQueue.Dequeue(); // Handle jump table entry, whose Target == Invalid. if (entry.Type == XRefType.NearIndexedJump) { System.Diagnostics.Debug.Assert(entry.Target == Address.Invalid); // Fill the Target field to make it a static xref. entry = ProcessJumpTableEntry(entry, xrefQueue); if (entry == null) // end of jump table continue; } // Skip other dynamic xrefs. if (entry.Target == Address.Invalid) { CrossReferences.Add(entry); continue; } // Process the basic block starting at the target address. BasicBlock block = AnalyzeBasicBlock(entry, xrefQueue); if (block != null) { //int count = block.Length; //int baseOffset = PointerToOffset(entry.Target); //proc.CodeRange.AddInterval(baseOffset, baseOffset + count); //proc.ByteRange.AddInterval(baseOffset, baseOffset + count); //for (int j = 0; j < count; j++) //{ // image[baseOffset + j].Procedure = proc; //} #if false proc.AddBasicBlock(block); #endif } CrossReferences.Add(entry); } }