/// <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 SourceAwareRelativeOperand(RelativeOperand opr, Address source) : base(opr.Offset) { this.source = source; }
/// <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); } }
/// <summary> /// Decodes an instruction at the given offset, applying associated /// fix-up information if present. /// </summary> /// <returns>The decoded instruction.</returns> /// <remarks> /// When overridden, the method must guarantee that the instruction /// is contained in the image, and not into another segment. /// </remarks> /// <exception cref="ArgumentOutOfRangeException">If offset refers to /// a location outside of the image.</exception> protected virtual Instruction DecodeInstruction(Address address) { if (!image.IsAddressValid(address)) throw new ArgumentOutOfRangeException("address"); Instruction instruction ; try { instruction = X86Codec.Decoder.Decode( image.GetBytes(address), CpuMode.RealAddressMode); } catch (Exception ex) { AddError(address, ErrorCode.InvalidInstruction, "Bad instruction: {0}", ex.Message); return null; } MakeRelativeOperandSourceAware(instruction, address); return instruction; }
/// <summary> /// Creates an instruction starting at the given address. If the /// decoded instruction covers bytes that are already analyzed, /// returns null. /// </summary> /// <param name="image"></param> /// <param name="address"></param> /// <returns></returns> protected virtual Instruction CreateInstruction(Address address, XRef entry) { Instruction instruction = DecodeInstruction(address); if (instruction == null) return null; // Check that the bytes covered by the decoded instruction are // unanalyzed. if (!image.CheckByteType(address, address + instruction.EncodedLength, ByteType.Unknown)) { AddError(address, ErrorCode.OverlappingInstruction, "Ran into the middle of code when processing block {0} referred from {1}", entry.Target, entry.Source); return null; } // Create a code piece for this instruction. image.UpdateByteType(address, address + instruction.EncodedLength, ByteType.Code); image.Instructions.Add(address, instruction); // Return the decoded instruction. return instruction; }
/// <summary> /// Creates a procedure with the given entry point. /// </summary> /// <returns></returns> protected virtual Procedure CreateProcedure(Address entryPoint) { // If there is already a procedure defined at the given entry // point, return that procedure. Procedure proc = Procedures.Find(entryPoint); if (proc != null) return proc; // Create a procedure at the entry point. The entry point must be // be the first byte of a basic block, or otherwise some flow // analysis error must have occurred. On the other hand, note // that multiple procedures may share one or more basic blocks // as part of their implementation. proc = new Procedure(entryPoint); AddBasicBlocksToProcedure(proc); //proc.Name = "TBD"; // To determine the call type of the procedure, examine the // features of the basic blocks. CodeFeatures features = proc.Features; #if false foreach (BasicBlock block in proc.BasicBlocks) { features |= block.Features; } #endif CodeFeatures callFeatures = features & ( CodeFeatures.HasRETN | CodeFeatures.HasRETF | CodeFeatures.HasIRET); switch (callFeatures) { case CodeFeatures.HasRETN: proc.ReturnType |= ReturnType.Near; break; case CodeFeatures.HasRETF: proc.ReturnType |= ReturnType.Far; break; case CodeFeatures.HasIRET: proc.ReturnType |= ReturnType.Interrupt; break; case CodeFeatures.None: AddError(entryPoint, ErrorCode.InconsistentCall, "Procedure at entry point {0} does not contain a RET/RETF/IRET instruction.", entryPoint); break; default: AddError(entryPoint, ErrorCode.InconsistentCall, "Procedure at entry point {0} contains inconsistent return instructions: {1}.", entryPoint, callFeatures); break; } return proc; }
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; } }
protected virtual XRef CreateFallThroughXRef(Address source, Address target) { XRef xref = new XRef( type: XRefType.FallThrough, source: source, target: target ); return xref; }
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 ); }
protected void AddError( Address location, ErrorCode errorCode, string format, params object[] args) { Errors.Add(new Error(location, errorCode, string.Format(format, args))); }
/// <summary> /// Replaces any RelativeOperand with SourceAwareRelativeOperand. /// </summary> /// <param name="instruction"></param> // TODO: make SourceAwareRelativeOperand.Target a dummy // SymbolicTarget, so that we can handle them consistently. protected static void MakeRelativeOperandSourceAware( Instruction instruction, Address instructionStart) { for (int i = 0; i < instruction.Operands.Length; i++) { if (instruction.Operands[i] is RelativeOperand && instruction.Operands[i].Tag == null) { instruction.Operands[i] = new SourceAwareRelativeOperand( (RelativeOperand)instruction.Operands[i], instructionStart + instruction.EncodedLength); } } }