// note: rva is relative to the module
func (m LoadedModule) MemReadPtr(ws *Workspace, rva AS.RVA) (AS.VA, error) {
	if ws.Mode == MODE_32 {
		var data uint32
		d, e := m.MemRead(ws, rva, 0x4)
		if e != nil {
			return 0, e
		}

		p := bytes.NewBuffer(d)
		binary.Read(p, binary.LittleEndian, &data)
		return AS.VA(uint64(data)), nil
	} else if ws.Mode == MODE_64 {
		var data uint64
		d, e := m.MemRead(ws, rva, 0x8)
		if e != nil {
			return 0, e
		}

		p := bytes.NewBuffer(d)
		binary.Read(p, binary.LittleEndian, &data)
		return AS.VA(uint64(data)), nil
	} else {
		return 0, InvalidModeError
	}
}
Example #2
0
func MemReadPointer(as AS.AddressSpace, va AS.VA, mode Mode) (AS.VA, error) {
	if mode == MODE_32 {
		var data uint32
		d, e := as.MemRead(va, 0x4)
		if e != nil {
			return AS.VA(0), e
		}

		p := bytes.NewBuffer(d)
		binary.Read(p, binary.LittleEndian, &data)
		return AS.VA(uint64(data)), nil
	} else if mode == MODE_64 {
		var data uint64
		d, e := as.MemRead(va, 0x8)
		if e != nil {
			return AS.VA(0), e
		}

		p := bytes.NewBuffer(d)
		binary.Read(p, binary.LittleEndian, &data)
		return AS.VA(uint64(data)), nil
	} else {
		return 0, InvalidModeError
	}
}
// discoverJumpTargets finds the targets of the current instruction that
//  should be a jump/branch instruction.
// returns ErrFailedToResolveTarget if the target is not resolvable.
// this should be expected in some cases, like jumping into uninitialized memory.
func (ed *EmulatingDisassembler) discoverJumpTargets() ([]AS.VA, error) {
	jumpVA := ed.emulator.GetInstructionPointer()

	insn, e := ed.disassembler.ReadInstruction(ed.ws, jumpVA)
	if e != nil {
		return nil, e
	}

	var jumpTargets []AS.VA
	if insn.X86.Operands[0].Type == gapstone.X86_OP_IMM {
		// simple case:
		// this looks like: jnz 0x401000
		jumpTargets = []AS.VA{AS.VA(insn.X86.Operands[0].Imm)}
	} else if insn.X86.Operands[0].Type == gapstone.X86_OP_MEM {
		// complex cases:
		op := insn.X86.Operands[0]
		if op.Mem.Base == 0 {
			// this can look like: jmp dword ptr [edx*4 + 0x401000]
			// segment: 0
			// base: 0
			// index: 0x18 (X86_REG_EDX)
			// scale: 4
			// disp: 0x401000
			jumpTargets, e = ed.findJumpTableTargets(AS.VA(op.Mem.Disp), op.Mem.Scale)
			check(e)
		} else {
			// or this can look like: jmp dword ptr [0x401000]
			//
			// or this:  jmp dword ptr [ebx + 0x18]"
			// segment: 0
			// base: 0x15 (X86_REG_EBX)
			// index: 0
			// scale: 1
			// disp: 0x18
			jumpTargets, e = ed.emulateToJumpTargetsAndBack()
			check(e)
		}
	} else if insn.X86.Operands[0].Type == gapstone.X86_OP_REG {
		// complex case:
		// this can look like: jmp [edx]
		jumpTargets, e = ed.emulateToJumpTargetsAndBack()
		check(e)
	} else {
		// this shouldnt really happen. the remaining types are: INVALID, and FP
		logrus.Debugf("jump target type: %x", insn.X86.Operands[0].Type)
		jumpTargets, e = ed.emulateToJumpTargetsAndBack()
		check(e)
	}

	if disassembly.IsConditionalJump(insn) {
		logrus.Debugf("conditional jump")
		jumpTargets = append(jumpTargets, AS.VA(insn.Address+insn.Size))
	}
	return jumpTargets, nil
}
Example #4
0
// GetJumpTargets gets the possible addresses to which a known jump instruction
//  transfers control.
// For a conditional jump, get both the true and false targets.
// This function uses just the instruction instance, so for an indirect jump, we can't tell much.
func GetJumpTargets(insn gapstone.Instruction) ([]*Jump, error) {
	ret := make([]*Jump, 0, 2)

	if DoesInstructionHaveGroup(insn, gapstone.X86_GRP_JUMP) && insn.Mnemonic == "jmp" {
		// unconditional jump, have the following possibilities:
		//   - direct jump: jmp 0x1000
		//   - indirect jump: jmp eax
		//   - indirect jump: jmp [0x1000]???

		next, e := GetJumpTarget(insn)
		if e != nil {
			// do the best we can
			return ret, nil
		}

		ret = append(
			ret,
			&Jump{
				From: AS.VA(insn.Address),
				To:   next,
				Type: P.JumpTypeUncond,
			})
	} else {
		// assume a two case situation:
		//   here:
		//     jnz yes
		//     xor eax, eax
		//     ret
		//   yes:
		//     mov eax, 1
		//     ret
		falsePc := AS.VA(uint64(insn.Address) + uint64(insn.Size))
		ret = append(
			ret,
			&Jump{
				From: AS.VA(insn.Address),
				To:   falsePc,
				Type: P.JumpTypeCondFalse,
			})

		truePc, e := GetJumpTarget(insn)
		if e == nil {
			ret = append(
				ret,
				&Jump{
					From: AS.VA(insn.Address),
					To:   truePc,
					Type: P.JumpTypeCondTrue,
				})
		}
	}
	return ret, nil
}
Example #5
0
/** ControlFlowAnalysis implements FunctionAnalysis interface **/
func (a *ControlFlowAnalysis) AnalyzeFunction(f *artifacts.Function) error {
	ld, e := LD.New(a.ws)
	check(e)

	cj, e := ld.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 ld.UnregisterJumpTraceHandler(cj)

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

	c, e := ld.RegisterCallTraceHandler(func(from AS.VA, to AS.VA) error {
		return a.ws.MakeCallCrossReference(from, to)
	})
	check(e)
	defer ld.UnregisterCallTraceHandler(c)

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

	return nil
}
Example #6
0
// IterateInstructions invokes the provided callback with each instruction starting
//  from the given address until the end of the current basic block.
func (dis *GapstoneDisassembler) IterateInstructions(as AS.AddressSpace, va AS.VA, f func(insn gapstone.Instruction) (bool, error)) error {
	for insn, e := dis.ReadInstruction(as, va); e == nil; insn, e = dis.ReadInstruction(as, va) {

		cont, e := f(insn)
		if e != nil {
			return e
		}

		if !cont {
			break
		}

		// stop processing a basic block if we're at: RET, IRET, JUMP
		if DoesInstructionHaveGroup(insn, gapstone.X86_GRP_RET) {
			break // out of instruction processing loop
		} else if DoesInstructionHaveGroup(insn, gapstone.X86_GRP_IRET) {
			break // out of instruction processing loop
		} else if DoesInstructionHaveGroup(insn, gapstone.X86_GRP_JUMP) {
			break // out of instruction processing loop
		}

		va = AS.VA(uint64(va) + uint64(insn.Size))
	}
	return nil
}
Example #7
0
func (a *Artifacts) GetBasicBlock(va AS.VA) (*BasicBlock, error) {
	v, e := a.persistence.GetAddressValueNumber(P.LocationData, va, P.TypeOfLocation)
	if e != nil {
		return nil, ErrBasicBlockNotFound
	}
	switch v {
	case int64(P.LocationFunction):
		// ok
	case int64(P.LocationBasicBlock):
		// ok
	default:
		return nil, ErrFunctionNotFound
	}

	length, e := a.persistence.GetAddressValueNumber(P.BasicBlockData, va, P.BasicBlockLength)
	if e != nil {
		return nil, ErrBasicBlockNotFound
	}

	return &BasicBlock{
		artifacts: a,
		Start:     va,
		End:       AS.VA(uint64(va) + uint64(length)),
	}, nil
}
Example #8
0
func New(ws *W.Workspace) (*Emulator, error) {
	logrus.Debug("emulator: new")
	if ws.Arch != W.ARCH_X86 {
		return nil, W.InvalidArchError
	}
	if !(ws.Mode == W.MODE_32 || ws.Mode == W.MODE_64) {
		return nil, W.InvalidModeError
	}

	var u uc.Unicorn
	var e error
	if ws.Mode == W.MODE_32 {
		u, e = uc.NewUnicorn(uc.ARCH_X86, uc.MODE_32)
	} else if ws.Mode == W.MODE_64 {
		u, e = uc.NewUnicorn(uc.ARCH_X86, uc.MODE_64)
	}
	if e != nil {
		return nil, e
	}

	disassembler, e := ws.GetDisassembler()

	emu := &Emulator{
		ws:           ws,
		u:            u,
		disassembler: disassembler,
		maps:         make([]AS.MemoryRegion, 0),
	}

	e = AS.CopyAddressSpace(emu, ws)
	check(e)
	if e != nil {
		return nil, e
	}

	stackAddress := AS.VA(0x69690000)
	stackSize := uint64(0x40000)
	e = emu.MemMap(AS.VA(uint64(stackAddress)-(stackSize/2)), stackSize, "stack")
	check(e)

	emu.SetStackPointer(stackAddress)

	return emu, nil
}
Example #9
0
func (emu *Emulator) StepInto() error {
	logrus.Debugf("emulator: step into")
	var memErr error = nil
	var codeErr error = nil

	memHook, e := emu.traceMemUnmapped(&memErr)
	check(e)
	defer memHook.Close()

	// always stop after one instruction
	hitCount := 0
	h, e := emu.HookCode(func(addr AS.VA, size uint32) {
		if hitCount == 0 {
			// pass
		} else if hitCount == 1 {
			emu.u.Stop()
		} else {
			codeErr = EmulatorEscapedError
		}
		hitCount++
	})
	check(e)
	defer h.Close()

	insn, e := emu.GetCurrentInstruction()
	ip := emu.GetInstructionPointer()
	end := AS.VA(uint64(ip) + uint64(insn.Size))
	e = emu.start(ip, end)
	if e != nil {
		logrus.Warnf("Single step failed: %s", e.Error())
		switch e := e.(type) {
		case uc.UcError:
			// TODO: nested switch here
			// TODO: split out into utility function??
			if e == uc.ERR_FETCH_UNMAPPED {
				return AS.ErrInvalidMemoryExec
			} else if e == uc.ERR_READ_UNMAPPED {
				return AS.ErrInvalidMemoryRead
			} else if e == uc.ERR_WRITE_UNMAPPED {
				return AS.ErrInvalidMemoryWrite
			}
			break
		default:
			break
		}
		return e
	}
	if memErr != nil {
		return memErr
	}
	if codeErr != nil {
		return codeErr
	}

	return nil
}
func SkipInstruction(emu *emulator.Emulator, dis disassembly.Disassembler) error {

	pc := emu.GetInstructionPointer()
	insn, e := dis.ReadInstruction(emu, pc)
	check(e)

	nextPc := AS.VA(insn.Address + insn.Size)
	logrus.Debugf("Skipping from %s to %s", pc, nextPc)
	emu.SetInstructionPointer(nextPc)
	return nil
}
Example #11
0
/** 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
}
Example #12
0
// MemReadPeOffset reads a 32bit (even on x64) AS.VA from the given address
//  of the module.
// note: rva is relative to the module
func (m LoadedModule) MemReadPeOffset(ws *Workspace, rva AS.RVA) (AS.VA, error) {
	// PE header offsets are 32bits even on x64
	var data uint32
	d, e := m.MemRead(ws, rva, 0x4)
	if e != nil {
		return 0, e
	}

	p := bytes.NewBuffer(d)
	binary.Read(p, binary.LittleEndian, &data)
	return AS.VA(uint64(data)), nil
}
Example #13
0
// GetJumpTarget gets the address to which a known jump instruction
//  transfers control.
// If the instruction is a conditional jump, then this function returns
//  the "jump is taken" target.
func GetJumpTarget(insn gapstone.Instruction) (AS.VA, error) {
	// have the following possibilities:
	//   - direct jump: jmp 0x1000
	//   - indirect jump: jmp eax
	//   - indirect jump: jmp [0x1000]???

	if insn.X86.Operands[0].Type == gapstone.X86_OP_IMM {
		return AS.VA(insn.X86.Operands[0].Imm), nil
	} else if insn.X86.Operands[0].Type == gapstone.X86_OP_REG {
		// jump eax
		// this is indirect, which is unresolvable.
		// leave analysis to the emulator.
		return AS.VA(0), ErrFailedToResolveJumpTarget
	} else if insn.X86.Operands[0].Type == gapstone.X86_OP_MEM {
		// jump [0x1000]
		// calling this indirect for now, which is unresolvable.
		// we could attempt to manually read out the pointer contents
		//  but that should really be left to the emulator.
		return AS.VA(0), ErrFailedToResolveJumpTarget
	}
	return AS.VA(0), nil
}
// discoverCallTarget finds the target of the current instruction that
//  should be a CALL instruction.
// returns ErrFailedToResolveTarget if the target is not resolvable.
// this should be expected in some cases, like calling into uninitialized memory.
//
// find call target
//   - is direct call, like: call 0x401000
//     -> directly read target
//   - is direct call, like: call [0x401000] ; via IAT
//     -> read IAT, use MSDN doc to determine number of args?
//   - is indirect call, like: call EAX
//     -> just save PC, step into, read PC, restore PC, pop SP
//     but be sure to handle invalid fetch errors
func (ed *EmulatingDisassembler) discoverCallTarget() (AS.VA, error) {
	var callTarget AS.VA
	callVA := ed.emulator.GetInstructionPointer()

	insn, e := ed.disassembler.ReadInstruction(ed.ws, callVA)
	if e != nil {
		return 0, e
	}

	if insn.X86.Operands[0].Type == gapstone.X86_OP_MEM {
		// assume we have: call [0x4010000]  ; IAT
		iva := AS.VA(insn.X86.Operands[0].Mem.Disp)
		if e == nil {
			// we successfully resolved an imported function.
			// TODO: how are we marking xrefs to imports? i guess with xrefs to the IAT
			callTarget = iva
		} else {
			// this is not an imported function, so we'll just have to try and see.
			// either there's a valid function pointer at the address, or we'll get an invalid fetch.
			callTarget, e = ed.emulateToCallTargetAndBack()
			if e != nil {
				logrus.Debugf("EmulateBB: emulating: failed to resolve call: %s", callVA)
				return 0, ErrFailedToResolveTarget
			}
		}
	} else if insn.X86.Operands[0].Type == gapstone.X86_OP_IMM {
		// assume we have: call 0x401000
		callTarget = AS.VA(insn.X86.Operands[0].Imm)
	} else if insn.X86.Operands[0].Type == gapstone.X86_OP_REG {
		// assume we have: call eax
		callTarget, e = ed.emulateToCallTargetAndBack()
		if e != nil {
			logrus.Debugf("EmulateBB: emulating: failed to resolve call: %s", callVA)
			return 0, ErrFailedToResolveTarget
		}
	}
	return callTarget, nil
}
Example #15
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()
	}
}
// 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
}
func (ed *EmulatingDisassembler) findJumpTableTargets(disp AS.VA, scale int) ([]AS.VA, error) {
	var targets []AS.VA
	va := disp
	for {
		logrus.Debugf("pointer read: %s", va)
		target, e := W.MemReadPointer(ed.emulator, va, ed.emulator.GetMode())
		check(e)

		if ProbeMemory(ed.emulator, target, uint64(scale)) {
			targets = append(targets, target)
			// not sure this conversion from negative number to uint64 is correct
			va = AS.VA(uint64(va) + uint64(scale))
		} else {
			break
		}
	}
	return targets, nil
}
Example #18
0
func HookSnapshot(emu *Emulator, snap *Snapshot) error {
	logrus.Debugf("snapshot: hook")
	if snap.hook != nil {
		return ErrSnapshotHookAlreadyActive
	}

	h, e := emu.HookMemWrite(func(access int, addr AS.VA, size int, value int64) {
		for i := uint64(addr); i < uint64(addr)+uint64(size); i += AS.PAGE_SIZE {
			snap.memory.MarkDirty(AS.VA(i))
		}
	})
	check(e)
	if e != nil {
		return e
	}

	snap.hook = h
	return nil
}
Example #19
0
func (emu *Emulator) GetInstructionPointer() AS.VA {
	logrus.Debug("emulator: get insn pointer")
	var r uint64
	var e error
	if emu.ws.Arch == W.ARCH_X86 {
		if emu.ws.Mode == W.MODE_32 {
			r, e = emu.RegRead(uc.X86_REG_EIP)
		} else if emu.ws.Mode == W.MODE_64 {
			r, e = emu.RegRead(uc.X86_REG_RIP)
		} else {
			panic(W.InvalidModeError)
		}
	} else {
		panic(W.InvalidArchError)
	}
	if e != nil {
		panic(e)
	}
	return AS.VA(r)
}
Example #20
0
func (loader *PELoader) Load(ws *workspace.Workspace) (*workspace.LoadedModule, error) {
	var imageBase AS.VA
	var addressOfEntryPoint AS.RVA
	var dataDirectory [16]pe.DataDirectory

	if optionalHeader, ok := loader.file.OptionalHeader.(*pe.OptionalHeader32); ok {
		imageBase = AS.VA(optionalHeader.ImageBase)
		addressOfEntryPoint = AS.RVA(optionalHeader.AddressOfEntryPoint)
		dataDirectory = optionalHeader.DataDirectory
	} else {
		return nil, workspace.InvalidModeError
	}

	mod := &workspace.LoadedModule{
		Name:             loader.name,
		BaseAddress:      imageBase,
		EntryPoint:       addressOfEntryPoint.VA(imageBase),
		Imports:          map[AS.RVA]workspace.LinkedSymbol{},
		ExportsByName:    map[string]workspace.ExportedSymbol{},
		ExportsByOrdinal: map[uint16]workspace.ExportedSymbol{},
	}

	for _, section := range loader.file.Sections {
		e := loader.loadPESection(ws, mod, section)
		check(e)
	}

	e := loader.resolveImports(ws, mod, dataDirectory)
	check(e)

	e = loader.resolveExports(ws, mod, dataDirectory)
	check(e)

	e = ws.AddLoadedModule(mod)
	check(e)

	return mod, nil
}
Example #21
0
func main() {
	app := cli.NewApp()
	app.Version = "0.1"
	app.Name = "run_linear_disassembler"
	app.Usage = "Invoke linear disassembler."
	app.Flags = []cli.Flag{inputFlag, fvaFlag}
	app.Action = func(c *cli.Context) {
		if utils.CheckRequiredArgs(c, []cli.StringFlag{inputFlag, fvaFlag}) != nil {
			return
		}

		inputFile := c.String("input_file")
		if !utils.DoesPathExist(inputFile) {
			log.Printf("Error: file %s must exist", inputFile)
			return
		}

		fva, e := strconv.ParseUint(c.String("fva"), 0x10, 64)
		check(e)

		check(doit(inputFile, AS.VA(fva)))
	}
	app.Run(os.Args)
}
Example #22
0
func doit(path string, fva AS.VA) error {
	runtime.LockOSThread()
	logrus.SetLevel(logrus.DebugLevel)

	exe, e := pe.Open(path)
	check(e)

	persis, e := config.MakeDefaultPersistence()
	check(e)

	ws, e := W.New(W.ARCH_X86, W.MODE_32, persis)
	check(e)

	dis, e := ws.GetDisassembler()
	check(e)

	loader, e := peloader.New(path, exe)
	check(e)

	_, e = loader.Load(ws)
	check(e)

	check(config.RegisterDefaultAnalyzers(ws))

	check(ws.MakeFunction(fva))

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

	fmt.Printf("digraph asm {\n")
	fmt.Printf(" node [shape=plain, style=\"rounded\", fontname=\"courier\"]\n")

	var exploreBBs func(bb *artifacts.BasicBlock) error
	exploreBBs = func(bb *artifacts.BasicBlock) error {
		fmt.Printf("bb_%s [label=<\n", bb.Start)
		fmt.Printf("<TABLE BORDER='1' CELLBORDER='0'>\n")

		insns, e := bb.GetInstructions(dis, ws)
		check(e)
		for _, insn := range insns {

			d, e := ws.MemRead(AS.VA(insn.Address), uint64(insn.Size))
			check(e)

			// format each of those as hex
			var bytesPrefix []string
			for _, b := range d {
				bytesPrefix = append(bytesPrefix, fmt.Sprintf("%02X", b))
			}
			prefix := strings.Join(bytesPrefix, " ")

			fmt.Printf("  <TR>\n")
			fmt.Printf("    <TD ALIGN=\"LEFT\">\n")
			fmt.Printf("      %s\n", AS.VA(insn.Address))
			fmt.Printf("    </TD>\n")
			fmt.Printf("    <TD ALIGN=\"LEFT\">\n")
			fmt.Printf("      %s\n", prefix)
			fmt.Printf("    </TD>\n")
			fmt.Printf("    <TD ALIGN=\"LEFT\">\n")
			fmt.Printf("      %s\n", insn.Mnemonic)
			fmt.Printf("    </TD>\n")
			fmt.Printf("    <TD ALIGN=\"LEFT\">\n")
			fmt.Printf("      %s\n", insn.OpStr)
			fmt.Printf("    </TD>\n")
			fmt.Printf("  </TR>\n")
		}
		fmt.Printf("</TABLE>\n")
		fmt.Printf(">];\n")

		nextBBs, e := bb.GetNextBasicBlocks()
		check(e)

		for _, nextBB := range nextBBs {
			exploreBBs(nextBB)
		}

		for _, nextBB := range nextBBs {
			fmt.Printf("bb_%s -> bb_%s;\n", bb.Start, nextBB.Start)
		}

		return nil
	}

	firstBB, e := f.GetFirstBasicBlock()
	check(e)

	exploreBBs(firstBB)
	defer fmt.Printf("}")

	runtime.UnlockOSThread()
	return nil
}
Example #23
0
func (m *hookMultiplexer) Install(emu *Emulator, hookType int) error {
	// TODO: ensure multiplexer not already installed
	if m.h != nil {
		return ErrAlreadyHooked
	}

	switch hookType {
	case uc.HOOK_MEM_READ:
		h, e := emu.u.HookAdd(
			uc.HOOK_MEM_READ,
			func(mu uc.Unicorn, access int, addr uint64, size int, value int64) {
				for _, f := range m.entries {
					if f, ok := f.(MemReadHandler); ok {
						f(access, AS.VA(addr), size, value)
					} else {
						log.Printf("error: failed to convert handler to mem read handler")
					}
				}
			})

		check(e)
		if e != nil {
			return e
		}
		logrus.Debugf("hooks: added hook: %v", h)

		m.h = &UnicornCloseableHook{emu: emu, h: h}
		return nil
	case uc.HOOK_MEM_WRITE:
		h, e := emu.u.HookAdd(
			uc.HOOK_MEM_WRITE,
			func(mu uc.Unicorn, access int, addr uint64, size int, value int64) {
				for _, f := range m.entries {
					if f, ok := f.(MemWriteHandler); ok {
						f(access, AS.VA(addr), size, value)
					} else {
						log.Printf("error: failed to convert handler to mem write handler")
					}
				}
			})

		check(e)
		if e != nil {
			return e
		}
		logrus.Debugf("hooks: added hook: %v", h)

		m.h = &UnicornCloseableHook{emu: emu, h: h}
		return nil
	case uc.HOOK_MEM_UNMAPPED:
		h, e := emu.u.HookAdd(
			uc.HOOK_MEM_UNMAPPED,
			func(mu uc.Unicorn, access int, addr uint64, size int, value int64) bool {
				for _, f := range m.entries {
					if f, ok := f.(MemUnmappedHandler); ok {
						if !f(access, AS.VA(addr), size, value) {
							return false
						}
					} else {
						log.Printf("error: failed to convert handler to mem unmapped handler")
					}
				}
				return true
			})

		check(e)
		if e != nil {
			return e
		}
		logrus.Debugf("hooks: added hook: %v", h)

		m.h = &UnicornCloseableHook{emu: emu, h: h}
		return nil
	case uc.HOOK_CODE:
		h, e := emu.u.HookAdd(
			uc.HOOK_CODE,
			func(mu uc.Unicorn, addr uint64, size uint32) {
				for _, f := range m.entries {
					if f, ok := f.(CodeHandler); ok {
						f(AS.VA(addr), uint32(size))
					} else {
						log.Printf("error: failed to convert handler to mem unmapped handler")
					}
				}
			})

		check(e)
		if e != nil {
			return e
		}
		logrus.Debugf("hooks: added hook: %v", h)

		m.h = &UnicornCloseableHook{emu: emu, h: h}
		return nil

	default:
		return ErrInvalidArgument
	}
}
// 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
}