private StridedInterval MakeInterval_IAdd(Expression left, Constant right) { if (right == null) { return(StridedInterval.Empty); } var cc = this.ccNext; if (this.invertCondition) { cc = cc.Invert(); } switch (cc) { default: return(StridedInterval.Empty); case ConditionCode.UGE: long max = ~right.ToInt64(); if (max < 0) { return(StridedInterval.Empty); } return(StridedInterval.Create(1, 0, max)); } }
private StridedInterval MakeInterval_ISub(Expression left, Constant right) { if (right == null) { return(StridedInterval.Empty); } var cc = this.ccNext; if (this.invertCondition) { cc = cc.Invert(); } switch (cc) { case ConditionCode.ULE: return(StridedInterval.Create(1, 0, right.ToInt64())); case ConditionCode.ULT: return(StridedInterval.Create(1, 0, right.ToInt64() - 1)); case ConditionCode.UGE: return(StridedInterval.Create(1, right.ToInt64(), long.MaxValue)); case ConditionCode.UGT: return(StridedInterval.Create(1, right.ToInt64() + 1, long.MaxValue)); case ConditionCode.EQ: case ConditionCode.NE: case ConditionCode.None: return(StridedInterval.Empty); default: throw new NotImplementedException($"Unimplemented condition code {cc}."); } }
public override ValueSet And(Constant right) { long v = right.ToInt64(); return(new IntervalValueSet( this.DataType, StridedInterval.Create(1, 0, v))); }
public override ValueSet Shl(Constant cRight) { int v = (int)cRight.ToInt64(); return(new IntervalValueSet( this.DataType, StridedInterval.Create( SI.Stride << v, SI.Low << v, SI.High << v))); }
public override ValueSet IMul(Constant cRight) { long v = cRight.ToInt64(); return(new IntervalValueSet( this.DataType, StridedInterval.Create( SI.Stride * (int)v, SI.Low * v, SI.High * v))); }
public override ValueSet Sub(Constant right) { if (SI.Stride < 0) { return(new IntervalValueSet( this.DataType, StridedInterval.Empty)); } long v = right.ToInt64(); return(new IntervalValueSet( this.DataType, StridedInterval.Create( SI.Stride, SI.Low - v, SI.High - v))); }
private SlicerResult FoundBoundaryCheck(BinaryExpression binExp, Expression liveKey) { var interval = binExp.Operator == Operator.ISub ? MakeInterval_ISub(liveKey, binExp.Right as Constant) : MakeInterval_IAdd(liveKey, binExp.Right as Constant); //$TODO: if jmptableindex and jmptableindextouse not same, inject a statement. this.JumpTableIndex = liveKey; this.JumpTableIndexToUse = binExp.Left; this.JumpTableIndexInterval = interval; DebugEx.Verbose(BackwardSlicer.trace, " Found range of {0}: {1}", liveKey, JumpTableIndexInterval); return(new SlicerResult { SrcExpr = binExp, Stop = true }); }
private StridedInterval MakeInterval_ISub(Expression left, Constant right) { if (right == null) { return(StridedInterval.Empty); } var cc = this.ccNext; if (this.invertCondition) { cc = cc.Invert(); } switch (cc) { // NOTE: GE and GT should really be modeled with the semi-open range // [right,inf) and the open range (right,inf), respectively. See comment // for LE/LT. case ConditionCode.GE: return(StridedInterval.Create(1, right.ToInt64(), long.MaxValue)); case ConditionCode.GT: return(StridedInterval.Create(1, right.ToInt64() + 1, long.MaxValue)); // NOTE: LE and LT should really be modeled with the semi-open range // (inf,right] and the open range (inf,right). However, typically compilers // make the mistake and use LE/LT for boundary checking in indirect transfers. case ConditionCode.LE: return(StridedInterval.Create(1, 0, right.ToInt64())); case ConditionCode.LT: return(StridedInterval.Create(1, 0, right.ToInt64() - 1)); case ConditionCode.ULE: return(StridedInterval.Create(1, 0, right.ToInt64())); case ConditionCode.ULT: return(StridedInterval.Create(1, 0, right.ToInt64() - 1)); case ConditionCode.UGE: return(StridedInterval.Create(1, right.ToInt64(), long.MaxValue)); case ConditionCode.UGT: return(StridedInterval.Create(1, right.ToInt64() + 1, long.MaxValue)); case ConditionCode.EQ: case ConditionCode.NE: case ConditionCode.None: return(StridedInterval.Empty); default: throw new NotImplementedException($"Unimplemented condition code {cc}."); } }
private StridedInterval MakeInterval_And(Expression left, Constant right) { if (right == null) { return(StridedInterval.Empty); } long n = right.ToInt64(); if (Bits.IsEvenPowerOfTwo(n + 1)) { // n is a mask (0000...00111..111) return(StridedInterval.Create(1, 0, n)); } else { return(StridedInterval.Empty); } }
/// <summary> /// Returns a valueset whose values are the truncation of this valueset's /// values. /// </summary> /// <param name="dt"></param> /// <returns></returns> public override ValueSet Truncate(DataType dt) { if (SI.Stride < 0) { return(this); } var mask = (1 << dt.BitSize) - 1; StridedInterval siNew; if (SI.Low == SI.High) { siNew = StridedInterval.Constant( Constant.Create(dt, SI.Low & mask)); } else { siNew = StridedInterval.Create( SI.Stride, 0, Math.Min(mask, SI.High)); } return(new IntervalValueSet(dt, siNew)); }
public IntervalValueSet(DataType dt, StridedInterval si) : base(dt) { this.SI = si; }
public ValueSet VisitBinaryExpression(BinaryExpression binExp) { var cLeft = binExp.Left as Constant; var cRight = binExp.Right as Constant; //$TODO: it would be great if Address were simply a Constant. // but we have segmented addresses which need special treatment // everywhere. if (binExp.Left is Address aLeft) { cLeft = aLeft.ToConstant(); } if (binExp.Right is Address aRight) { cRight = aRight.ToConstant(); } if (cLeft != null && cRight != null) { return(new IntervalValueSet( cLeft.DataType, StridedInterval.Constant( binExp.Operator.ApplyConstants(cLeft, cRight)))); } if (cLeft == null && cRight != null) { var left = binExp.Left.Accept(this); if (binExp.Operator == Operator.IAdd) { return(left.Add(cRight)); } else if (binExp.Operator == Operator.And) { return(left.And(cRight)); } else if (binExp.Operator == Operator.Shl) { return(left.Shl(cRight)); } else if (binExp.Operator == Operator.IMul) { return(left.IMul(cRight)); } else if (binExp.Operator == Operator.ISub) { return(left.Sub(cRight)); } } if (cRight == null && cLeft != null) { var right = binExp.Right.Accept(this); if (binExp.Operator == Operator.IAdd) { return(right.Add(cLeft)); } else if (binExp.Operator == Operator.And) { return(right.And(cLeft)); } } if (binExp.Operator == Operator.IAdd) { if (cmp.Equals(binExp.Left, binExp.Right)) { var left = binExp.Left.Accept(this); return(left.Shl(Constant.Int32(1))); } } return(IntervalValueSet.Any); }
public SlicerResult VisitBinaryExpression(BinaryExpression binExp, BackwardSlicerContext ctx) { if (binExp.Operator == Operator.Eq || binExp.Operator == Operator.Ne) { // Equality comparisons cannot contribute to determining the size // of the jump table; stop processing this instruction. return(null); } if ((binExp.Operator == Operator.Xor || binExp.Operator == Operator.ISub) && this.slicer.AreEqual(binExp.Left, binExp.Right)) { // XOR r,r (or SUB r,r) clears a register. Is it part of a live register? var regDst = this.assignLhs as Identifier; var regHi = binExp.Left as Identifier; if (regHi != null && regDst != null && DomainOf(regDst) == regHi.Storage.Domain && regDst.Storage.OffsetOf(regHi.Storage) == 8) { // The 8086 didn't have a MOVZX instruction, so clearing the high byte of a // register BX was done by issuing XOR BH,BH var seXor = new SlicerResult { SrcExpr = new Cast(regDst.DataType, new Cast(PrimitiveType.Byte, this.assignLhs)), LiveExprs = new Dictionary <Expression, BackwardSlicerContext> { { this.assignLhs, BackwardSlicerContext.Jump(new BitRange(0, 8)) } } }; return(seXor); } } var seLeft = binExp.Left.Accept(this, ctx); var seRight = binExp.Right.Accept(this, ctx); if (seLeft == null && seRight == null) { return(null); } if (binExp.Operator == Operator.ISub && this.Live != null && (ctx.Type & ContextType.Condition) != 0) { var domLeft = DomainOf(seLeft.SrcExpr); if (Live.Count > 0) { foreach (var live in Live) { if (live.Value.Type != ContextType.Jumptable) { continue; } if ((domLeft != StorageDomain.Memory && DomainOf(live.Key) == domLeft) || (this.slicer.AreEqual(live.Key, binExp.Left))) { //$TODO: if jmptableindex and jmptableindextouse not same, inject a statement. this.JumpTableIndex = live.Key; this.JumpTableIndexToUse = binExp.Left; this.JumpTableIndexInterval = MakeInterval_ISub(live.Key, binExp.Right as Constant); DebugEx.Verbose(BackwardSlicer.trace, " Found range of {0}: {1}", live, JumpTableIndexInterval); return(new SlicerResult { SrcExpr = binExp, Stop = true }); } } } else { // We have no live variables, which means this subtraction instruction // is both computing the jumptable index and also performing the // comparison. this.JumpTableIndex = assignLhs; this.JumpTableIndexToUse = assignLhs; this.JumpTableIndexInterval = MakeInterval_ISub(assignLhs, binExp.Right as Constant); DebugEx.Verbose(BackwardSlicer.trace, " Found range of {0}: {1}", assignLhs, JumpTableIndexInterval); return(new SlicerResult { SrcExpr = null, // the jump table expression already has the correct shape. Stop = true }); } } else if (binExp.Operator == Operator.And) { this.JumpTableIndex = binExp.Left; this.JumpTableIndexToUse = binExp.Left; this.JumpTableIndexInterval = MakeInterval_And(binExp.Left, binExp.Right as Constant); return(new SlicerResult { SrcExpr = binExp, Stop = true, }); } IEnumerable <KeyValuePair <Expression, BackwardSlicerContext> > liveExpr = seLeft.LiveExprs; if (seRight != null) { liveExpr = liveExpr.Concat(seRight.LiveExprs); } var se = new SlicerResult { LiveExprs = liveExpr .GroupBy(e => e.Key) .ToDictionary(k => k.Key, v => v.Max(vv => vv.Value)), SrcExpr = binExp, }; return(se); }
public TableExtent DiscoverTableExtent(Address addrSwitch, RtlTransfer xfer, DecompilerEventListener listener) { if (!Start(rtlBlock, host.BlockInstructionCount(rtlBlock) - 1, xfer.Target)) { // No registers were found, so we can't trace back. return(null); } while (Step()) { ; } var jumpExpr = this.JumpTableFormat; var interval = this.JumpTableIndexInterval; var index = this.JumpTableIndexToUse; var ctx = new Dictionary <Expression, ValueSet>(new ExpressionValueComparer()); if (index == null) { // Weren't able to find the index register, // try finding it by blind pattern matching. index = this.FindIndexWithPatternMatch(this.JumpTableFormat); if (index == null) { // This is likely an indirect call like a C++ // vtable dispatch. Since these are common, we don't // spam the user with warnings. return(null); } // We have a jump table, and we've guessed the index expression. // At this point we've given up on knowing the exact size // of the table, but we do know that it must be at least // more than one entry. The safest assumption is that it // has two entries. listener.Warn( listener.CreateAddressNavigator(host.Program, addrSwitch), "Unable to determine size of call or jump table; there may be more than 2 entries."); ctx.Add(index, new IntervalValueSet(index.DataType, StridedInterval.Create(1, 0, 1))); } else if (interval.IsEmpty) { return(null); } else if (interval.High == Int64.MaxValue) { // We have no reasonable upper bound. We make the arbitrary // assumption that the jump table has 2 items; it wouldn't // make sense to be indexing otherwise. listener.Warn( listener.CreateAddressNavigator(host.Program, addrSwitch), "Unable to determine the upper bound of an indirect call or jump; there may be more than 2 entries."); var vs = new IntervalValueSet( this.JumpTableIndex.DataType, StridedInterval.Create(1, interval.Low, interval.Low + 1)); ctx.Add(this.JumpTableIndexToUse, vs); } else { ctx.Add(this.JumpTableIndex, new IntervalValueSet(this.JumpTableIndex.DataType, interval)); } var vse = new ValueSetEvaluator(host.Architecture, host.SegmentMap, ctx, this.processorState); var(values, accesses) = vse.Evaluate(jumpExpr); var vector = values.Values .TakeWhile(c => c != Constant.Invalid) .Take(2000) // Arbitrary limit .Select(ForceToAddress) .TakeWhile(a => a != null) .ToList(); if (vector.Count == 0) { return(null); } return(new TableExtent { Targets = vector, Accesses = accesses, Index = index, }); }
private ValueSet IVS(int stride, long low, long high) { return(new IntervalValueSet(PrimitiveType.Word32, StridedInterval.Create(stride, low, high))); }
public SlicerResult VisitBinaryExpression(BinaryExpression binExp, BackwardSlicerContext ctx) { if (binExp.Operator == Operator.Eq || binExp.Operator == Operator.Ne) { // Equality comparisons cannot contribute to determining the size // of the jump table; stop processing this instruction. return(null); } if ((binExp.Operator == Operator.Xor || binExp.Operator == Operator.ISub) && this.slicer.AreEqual(binExp.Left, binExp.Right)) { // XOR r,r (or SUB r,r) clears a register. Is it part of a live register? var regDst = this.assignLhs as Identifier; var regHi = binExp.Left as Identifier; if (regHi != null && regDst != null && DomainOf(regDst) == regHi.Storage.Domain && regDst.Storage.OffsetOf(regHi.Storage) == 8) { // The 8086 didn't have a MOVZX instruction, so clearing the high byte of a // register BX was done by issuing XOR BH,BH var seXor = new SlicerResult { SrcExpr = new Cast(regDst.DataType, new Cast(PrimitiveType.Byte, this.assignLhs)), LiveExprs = new Dictionary <Expression, BackwardSlicerContext> { { this.assignLhs, BackwardSlicerContext.Jump(new BitRange(0, 8)) } } }; return(seXor); } } var seLeft = binExp.Left.Accept(this, ctx); var seRight = binExp.Right.Accept(this, ctx); if (seLeft == null && seRight == null) { return(null); } if (binExp.Operator == Operator.ISub && Live != null) { var domLeft = DomainOf(seLeft.SrcExpr); foreach (var live in Live.Keys) { if (DomainOf(live) == domLeft) { if (slicer.AreEqual(this.assignLhs, this.JumpTableIndex)) { //$TODO: if jmptableindex and jmptableindextouse not same, inject a statement. this.JumpTableIndex = live; this.JumpTableIndexToUse = binExp.Left; this.JumpTableIndexInterval = MakeInterval_ISub(live, binExp.Right as Constant); DebugEx.PrintIf(BackwardSlicer.trace.TraceVerbose, " Found range of {0}: {1}", live, JumpTableIndexInterval); return(new SlicerResult { SrcExpr = binExp, Stop = true }); } } if (this.slicer.AreEqual(live, binExp.Left)) { this.JumpTableIndex = live; this.JumpTableIndexToUse = binExp.Left; this.JumpTableIndexInterval = MakeInterval_ISub(live, binExp.Right as Constant); return(new SlicerResult { SrcExpr = binExp, Stop = true, }); } } } else if (binExp.Operator == Operator.And) { this.JumpTableIndex = binExp.Left; this.JumpTableIndexToUse = binExp.Left; this.JumpTableIndexInterval = MakeInterval_And(binExp.Left, binExp.Right as Constant); return(new SlicerResult { SrcExpr = binExp, Stop = true, }); } var se = new SlicerResult { LiveExprs = seLeft.LiveExprs.Concat(seRight.LiveExprs) .GroupBy(e => e.Key) .ToDictionary(k => k.Key, v => v.Max(vv => vv.Value)), SrcExpr = binExp, }; return(se); }