// ExploreBB linearly disassembles instructions starting at a given address
//  in a given address space, invoking the appropriate callbacks, and terminates
//  at the end of the current basic block.
// A basic block is delimited by a ret or jump instruction.
// Returns the addresses to which this basic block may transfer control via jumps.
func (ld *LinearDisassembler) ExploreBB(as AS.AddressSpace, va AS.VA) ([]AS.VA, error) {
	logrus.Debugf("linear disassembler: explore bb: %s", va)
	bbStart := va
	// the last VA reached while exploring tihs BB
	// only makes sense to fetch this value after iterating instructions
	lastVa := AS.VA(0)
	nextBBs := make([]AS.VA, 0, 2)

	e := ld.disassembler.IterateInstructions(as, va, func(insn gapstone.Instruction) (bool, error) {
		lastVa = AS.VA(insn.Address)
		check(ld.EmitInstruction(insn))

		if disassembly.DoesInstructionHaveGroup(insn, gapstone.X86_GRP_JUMP) {
			// this return a slice with zero length, but that should be ok
			targets, e := disassembly.GetJumpTargets(insn)
			if e != nil {
				return false, e
			}

			for _, target := range targets {
				check(ld.EmitJump(insn, bbStart, target.To, target.Type))
				nextBBs = append(nextBBs, target.To)
			}

			// though we can assume that IterateInstructions will return after this insn (end of bb),
			//  we'd better not make assumptions. here, we explicitly end processing.
			return false, nil // continue processing instructions
		} else if disassembly.DoesInstructionHaveGroup(insn, gapstone.X86_GRP_CALL) {
			if insn.X86.Operands[0].Type == gapstone.X86_OP_IMM {
				// example: call 0x401000
				targetva := AS.VA(insn.X86.Operands[0].Imm)
				check(ld.EmitCall(AS.VA(insn.Address), targetva))
			} else if insn.X86.Operands[0].Type == gapstone.X86_OP_REG {
				// example: call eax
				// indirect call, we're stuck
			} else {
				op := insn.X86.Operands[0]
				if op.Mem.Base != 0 || op.Mem.Index != 0 {
					// example: call [eax] or call [eax + 10]
					// indirect call, we're stuck
				} else {
					// example: call [GetVersionA]
					targetva := AS.VA(op.Mem.Disp)
					check(ld.EmitCall(AS.VA(insn.Address), targetva))
				}
			}
		}

		return true, nil // continue processing instructions
	})
	check(e)

	check(ld.EmitBB(bbStart, lastVa))

	return nextBBs, nil
}
/** StackDeltaAnalysis implements FunctionAnalysis interface **/
func (a *StackDeltaAnalysis) AnalyzeFunction(f *artifacts.Function) error {
	ld, e := LD.New(a.ws)
	check(e)

	didSetStackDelta := false
	c, e := ld.RegisterInstructionTraceHandler(func(insn gapstone.Instruction) error {
		if !didSetStackDelta {
			if !disassembly.DoesInstructionHaveGroup(insn, gapstone.X86_GRP_RET) {
				return nil
			}
			if len(insn.X86.Operands) == 0 {
				f.SetStackDelta(0)
				return nil
			}
			if insn.X86.Operands[0].Type != gapstone.X86_OP_IMM {
				return nil
			}
			stackDelta := insn.X86.Operands[0].Imm
			f.SetStackDelta(stackDelta)
			didSetStackDelta = true
		}
		return nil
	})
	check(e)
	defer ld.UnregisterInstructionTraceHandler(c)

	e = ld.ExploreFunction(a.ws, f.Start)
	check(e)

	return nil
}
Beispiel #3
0
func (emu *Emulator) StepOver() error {
	logrus.Debugf("emulator: step over")
	insn, e := emu.GetCurrentInstruction()
	check(e)
	if e != nil {
		return e
	}

	if dis.DoesInstructionHaveGroup(insn, gapstone.X86_GRP_CALL) {
		return emu.RunTo(AS.VA(uint64(emu.GetInstructionPointer()) + uint64(insn.Size)))
	} else {
		return emu.StepInto()
	}
}
/** IndirectControlFlowAnalysis implements FunctionAnalysis interface **/
func (a *IndirectControlFlowAnalysis) AnalyzeFunction(f *artifacts.Function) error {
	logrus.Debugf("indirect cf analysis: analyze function: %s", f.Start)
	ed, e := ED.New(a.ws)
	check(e)
	defer ed.Close()

	cj, e := ed.RegisterJumpTraceHandler(func(
		insn gapstone.Instruction,
		from_bb AS.VA,
		target AS.VA,
		jtype P.JumpType) error {

		return a.ws.MakeCodeCrossReference(AS.VA(insn.Address), target, jtype)
	})
	check(e)
	defer ed.UnregisterJumpTraceHandler(cj)

	cb, e := ed.RegisterBBTraceHandler(func(start AS.VA, end AS.VA) error {
		return a.ws.MakeBasicBlock(start, end)
	})
	check(e)
	defer ed.UnregisterBBTraceHandler(cb)

	c, e := ed.RegisterInstructionTraceHandler(func(insn gapstone.Instruction) error {
		if disassembly.DoesInstructionHaveGroup(insn, gapstone.X86_GRP_CALL) {
			if insn.X86.Operands[0].Type == gapstone.X86_OP_IMM {
				// assume we have: call 0x401000
				targetva := AS.VA(insn.X86.Operands[0].Imm)
				a.ws.MakeFunction(targetva)
			}
		}
		return nil
	})
	check(e)
	defer ed.UnregisterInstructionTraceHandler(c)

	e = ed.ExploreFunction(a.ws, f.Start)
	check(e)

	return nil
}
Beispiel #5
0
func isBBEnd(insn gapstone.Instruction) bool {
	return dis.DoesInstructionHaveGroup(insn, gapstone.X86_GRP_CALL) ||
		dis.DoesInstructionHaveGroup(insn, gapstone.X86_GRP_JUMP) ||
		dis.DoesInstructionHaveGroup(insn, gapstone.X86_GRP_RET) ||
		dis.DoesInstructionHaveGroup(insn, gapstone.X86_GRP_IRET)
}
// when/where can this function be safely called?
func (ed *EmulatingDisassembler) ExploreBB(as AS.AddressSpace, va AS.VA) ([]AS.VA, error) {
	logrus.Debugf("emu disassembler: explore bb: %s", va)

	// things done here:
	//  - find CALL instructions
	//  - emulate to CALL instructions
	//     - using emulation, figure out what the target of the call is
	//     - using linear disassembly, find target calling convention
	//     - decide how much stack to clean up
	//  - manually move PC to instruction after the CALL
	//  - clean up stack
	//  - continue emulating
	//  - resolve jump targets at end of BB using emulation
	var nextBBs []AS.VA
	bbStart := va
	var callVAs []AS.VA

	// recon
	endVA := va
	e := ed.disassembler.IterateInstructions(as, va, func(insn gapstone.Instruction) (bool, error) {
		logrus.Debugf("recon: insn: %s", AS.VA(insn.Address))
		endVA = AS.VA(insn.Address) // update last reached VA, to compute end of BB
		if !disassembly.DoesInstructionHaveGroup(insn, gapstone.X86_GRP_CALL) {
			return true, nil
		}

		logrus.Debugf("EmulateBB: planning: found call: va: 0x%x", insn.Address)
		callVAs = append(callVAs, AS.VA(insn.Address))
		return true, nil // continue processing instructions
	})
	check(e)

	// prepare emulator
	ed.emulator.SetInstructionPointer(va)

	// emulate!
	for len(callVAs) > 0 {
		callVA := callVAs[0]
		callVAs = callVAs[1:]

		logrus.Debugf("EmulateBB: emulating: from: %s to: %s", ed.emulator.GetInstructionPointer(), callVA)

		e := ed.bulletproofRun(callVA)
		check(e)

		pc := ed.emulator.GetInstructionPointer()

		insn, e := ed.disassembler.ReadInstruction(ed.ws, pc)
		check(e)
		if !disassembly.DoesInstructionHaveGroup(insn, gapstone.X86_GRP_CALL) {
			panic(fmt.Sprintf("expected to be at a call, but we're not: %s", pc))
		}
		logrus.Debugf("EmulateBB: paused at call: %s", pc)
		// this instruction may be emitted twice, since we potentially
		//  use emulation to resolve the call target
		check(ed.EmitInstruction(insn))

		var stackDelta int64
		callTarget, e := ed.discoverCallTarget()
		if e == ErrFailedToResolveTarget {
			// will just have to make a guess as to how to clean up the stack
			// for right now, assume its 0
			// TODO: if its an import, assume STDCALL
			//  and use MSDN documentation to extract number of parameters
		} else if e != nil {
			e := ed.ws.MakeFunction(callTarget)
			check(e)

			f, e := ed.ws.Artifacts.GetFunction(callTarget)
			check(e)

			stackDelta, e = f.GetStackDelta()
			check(e)
		}
		check(ed.EmitCall(pc, callTarget))

		logrus.Debugf("EmulateBB: skipping call: %s", pc)
		check(SkipInstruction(ed.emulator, ed.disassembler))

		// cleanup stack
		ed.emulator.SetStackPointer(AS.VA(int64(ed.emulator.GetStackPointer()) + stackDelta))
	}

	// emulate to end of current basic block
	logrus.Debugf("EmulateBB: emulating to end: from: %s to: %s", ed.emulator.GetInstructionPointer(), endVA)
	e = ed.bulletproofRun(endVA)
	check(e)

	pc := ed.emulator.GetInstructionPointer()
	check(ed.EmitBB(bbStart, pc))

	insn, e := ed.disassembler.ReadInstruction(ed.ws, pc)
	check(e)

	logrus.Debugf("EmulateBB: final instruction: %s", pc)
	check(ed.EmitInstruction(insn))

	if disassembly.DoesInstructionHaveGroup(insn, gapstone.X86_GRP_RET) {
		logrus.Debugf("EmulateBB: ends with a ret")
	} else {
		// must be a jump
		nextBBs, e = ed.discoverJumpTargets()
		check(e)

		logrus.Debugf("EmulateBB: next BBs: %v", nextBBs)

		for _, target := range nextBBs {
			// TODO: use real jump types
			check(ed.EmitJump(insn, bbStart, target, P.JumpTypeUncond))
		}
	}
	return nextBBs, nil
}