func resolveNumber(emu *workspace.Emulator, s string) (uint64, error) { // using '.' refers to the current PC value if s == "." { return uint64(emu.GetInstructionPointer()), nil } reg, e := getRegFromString(s) if e == nil { r, e := emu.RegRead(reg) return uint64(r), e } // otherwise, parse int addrInt, e := parseNumber(s) check(e) return addrInt, nil }
func doloop(emu *workspace.Emulator) error { edb, e := New(emu) check(e) done := false app := cli.NewApp() app.Name = "cli" app.Usage = "" app.Commands = []cli.Command{ { Name: "quit", Aliases: []string{"exit", "q"}, Usage: "quit the emulator shell", // Subcommands: []cli.Command{}, Action: func(c *cli.Context) { fmt.Printf("quit.\n") done = true }, }, { Name: "help", Aliases: []string{"h", "?"}, Usage: "show help", // Subcommands: []cli.Command{}, Action: func(c *cli.Context) { if !c.Args().Present() { cli.ShowAppHelp(c) } else { cli.ShowCommandHelp(c, c.Args().First()) } }, }, { Name: "stepi", Aliases: []string{"t"}, Usage: "single step an instruction, emulating into function calls", Action: func(c *cli.Context) { e := emu.StepInto() check(e) }, }, { Name: "stepo", Aliases: []string{"p"}, Usage: "single step an instruction, emulating over function calls", Action: func(c *cli.Context) { e := emu.StepOver() check(e) }, }, { Name: "go", Aliases: []string{"g"}, Usage: "emulate multiple instructions", Action: func(c *cli.Context) { if c.Args().Present() { addrInt, e := resolveNumber(emu, c.Args().First()) check(e) addr := workspace.VA(addrInt) e = emu.RunTo(addr) check(e) } else { fmt.Printf("error: `go` requires one argument\n") } }, }, { Name: "registers", Aliases: []string{"reg", "r"}, Usage: "show, edit registers", Action: func(c *cli.Context) { if c.Args().Present() { regStr := c.Args().First() if strings.Contains(regStr, "=") { parts := strings.Split(regStr, "=") if len(parts) != 2 { fmt.Printf("error: bad register assignment\n") return } regStr := parts[0] reg, e := getRegFromString(regStr) if e != nil { fmt.Printf("error: invalid register name: %s\n", regStr) return } valStr := parts[1] val, e := resolveNumber(emu, valStr) if e != nil { fmt.Printf("error: invalid value: %s\n", valStr) return } e = emu.RegWrite(reg, val) check(e) } else { reg, e := getRegFromString(regStr) if e != nil { fmt.Printf("error: invalid register name: %s\n", regStr) return } r, e := emu.RegRead(reg) check(e) fmt.Printf("%s: 0x%08x\n", strings.ToLower(regStr), r) } } else { eax, _ := emu.RegRead(uc.X86_REG_EAX) ebx, _ := emu.RegRead(uc.X86_REG_EBX) ecx, _ := emu.RegRead(uc.X86_REG_ECX) edx, _ := emu.RegRead(uc.X86_REG_EDX) esi, _ := emu.RegRead(uc.X86_REG_ESI) edi, _ := emu.RegRead(uc.X86_REG_EDI) ebp, _ := emu.RegRead(uc.X86_REG_EBP) esp, _ := emu.RegRead(uc.X86_REG_ESP) eip, _ := emu.RegRead(uc.X86_REG_EIP) cf := emu.RegReadEflag(workspace.EFLAG_CF) pf := emu.RegReadEflag(workspace.EFLAG_PF) af := emu.RegReadEflag(workspace.EFLAG_AF) zf := emu.RegReadEflag(workspace.EFLAG_ZF) sf := emu.RegReadEflag(workspace.EFLAG_SF) tf := emu.RegReadEflag(workspace.EFLAG_TF) if_ := emu.RegReadEflag(workspace.EFLAG_IF) df := emu.RegReadEflag(workspace.EFLAG_DF) of := emu.RegReadEflag(workspace.EFLAG_OF) fmt.Printf("eax: 0x%08x CF: %v\n", eax, cf) fmt.Printf("ebx: 0x%08x PF: %v\n", ebx, pf) fmt.Printf("ecx: 0x%08x AF: %v\n", ecx, af) fmt.Printf("edx: 0x%08x ZF: %v\n", edx, zf) fmt.Printf("esi: 0x%08x SF: %v\n", esi, sf) fmt.Printf("edi: 0x%08x TF: %v\n", edi, tf) fmt.Printf("ebp: 0x%08x IF: %v\n", ebp, if_) fmt.Printf("esp: 0x%08x DF: %v\n", esp, df) fmt.Printf("eip: 0x%08x OF: %v\n", eip, of) } }, }, { Name: "unassemble", Aliases: []string{"u", "dis", "disassemble"}, Usage: "disassemble instructions", Action: func(c *cli.Context) { // usage: u [addr|. [num instructions]] var e error addr := emu.GetInstructionPointer() length := uint64(3) if c.Args().Get(0) != "" { addrInt, e := resolveNumber(emu, c.Args().Get(0)) check(e) addr = workspace.VA(addrInt) } if c.Args().Get(1) != "" { length, e = resolveNumber(emu, c.Args().Get(1)) check(e) } for i := uint64(0); i < length; i++ { s, read, e := emu.FormatAddress(addr) check(e) fmt.Printf(s + "\n") addr = workspace.VA(uint64(addr) + read) } }, }, { Name: "hexdump", Aliases: []string{"dc"}, Usage: "disassemble instructions", Action: func(c *cli.Context) { // usage: dps [addr|. [num bytes]] var e error addr := emu.GetInstructionPointer() length := uint64(0x40) if c.Args().Get(1) != "" { addrInt, e := resolveNumber(emu, c.Args().Get(1)) check(e) addr = workspace.VA(addrInt) } if c.Args().Get(2) != "" { length, e = resolveNumber(emu, c.Args().Get(2)) check(e) } b, e := emu.MemRead(addr, length) check(e) e = hexdump.DumpFromOffset(b, uint64(addr), os.Stdout) check(e) }, }, { Name: "dps", Aliases: []string{}, Usage: "dump pointers to symbols", Action: func(c *cli.Context) { // usage: dps [addr|. [num pointers]] var e error addr := emu.GetInstructionPointer() length := uint64(0x8) if c.Args().Get(0) != "" { addrInt, e := resolveNumber(emu, c.Args().Get(0)) check(e) addr = workspace.VA(addrInt) } if c.Args().Get(1) != "" { length, e = resolveNumber(emu, c.Args().Get(1)) check(e) } for i := uint64(0); i < length; i++ { va, e := emu.MemReadPtr(addr) check(e) fmt.Printf("%08x: %08x\n", uint64(addr), uint64(va)) if emu.GetMode() == workspace.MODE_32 { addr += 0x4 } else if emu.GetMode() == workspace.MODE_64 { addr += 0x8 } } }, }, { Name: "snapshot", Aliases: []string{"s"}, Usage: "manipulate snapshots", Subcommands: []cli.Command{ { Name: "create", Aliases: []string{"c"}, Usage: "create new snapshot", Action: func(c *cli.Context) { if len(edb.snaps) > 0 { // pause the previous current snapshot's memory tracking e := emu.UnhookSnapshot(edb.snaps[len(edb.snaps)-1]) check(e) } // push our new snapshot onto the stack of snapshots snap, e := emu.Snapshot() check(e) fmt.Printf("Snapshot taken.\n") edb.snaps = append(edb.snaps, snap) }, }, { Name: "revert", Aliases: []string{"r"}, Usage: "revert to current snapshot", Action: func(c *cli.Context) { if len(edb.snaps) == 0 { fmt.Printf("Error: no snapshot active.\n") return } snap := edb.snaps[len(edb.snaps)-1] e := emu.RestoreSnapshot(snap) check(e) fmt.Printf("Snapshot restored.\n") }, }, { Name: "list", Aliases: []string{"l"}, Usage: "list snapshots", Action: func(c *cli.Context) { fmt.Printf("snapshots:\n") for i := len(edb.snaps) - 1; i >= 0; i-- { snap := edb.snaps[i] if i == len(edb.snaps)-1 { fmt.Printf(" > %s\n", snap.String()) } else { fmt.Printf(" - %s\n", snap.String()) } } }, }, { Name: "diff", Aliases: []string{"s"}, Usage: "diff reg, mem from current snapshot", Action: func(c *cli.Context) { fmt.Printf("unsupported.\n") }, }, { Name: "destroy", Aliases: []string{"d"}, Usage: "destroy current snapshot", Action: func(c *cli.Context) { if len(edb.snaps) == 0 { fmt.Printf("Error: no snapshot active.\n") } else { // TODO: ensure emu.pc == snap.pc snap := edb.snaps[len(edb.snaps)-1] e := emu.UnhookSnapshot(snap) check(e) fmt.Printf("Snapshot destroyed: %s\n", snap.String()) edb.snaps = edb.snaps[:len(edb.snaps)-1] if len(edb.snaps) > 0 { oldsnap := edb.snaps[len(edb.snaps)-1] e := emu.HookSnapshot(oldsnap) // TODO: need to merge changes from `snap` into `oldsnap` check(e) fmt.Printf("Continuing with snapshot: %s\n", oldsnap.String()) } } }, }, }, }, } for !done { insn, e := emu.GetCurrentInstruction() check(e) s, _, e := emu.FormatAddress(emu.GetInstructionPointer()) check(e) color.Set(color.FgHiBlack) fmt.Printf("next:\n" + s) // hack if workspace.DoesInstructionHaveGroup(insn, gapstone.X86_GRP_JUMP) && string(insn.Mnemonic[0]) == "j" { nextPc, e := GetNextInstruction(edb) check(e) if nextPc == workspace.VA(uint64(emu.GetInstructionPointer())+uint64(insn.Size)) { fmt.Printf(" (jump not taken)") } else { fmt.Printf(" (jump taken, to: 0x%08x)", uint64(nextPc)) } } fmt.Printf("\n") color.Unset() color.Set(color.FgBlue) fmt.Printf("0x%08x > ", emu.GetInstructionPointer()) color.Unset() // TODO: use a readline like lib here line, e := getLine() if e != nil { line = "" done = true } words, e := shlex.Split("cli "+line, true) check(e) if e != nil { return e } if len(words) == 1 { continue } app.Run(words) } return nil }