// 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 }
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 }
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 }