Beispiel #1
0
func NewFile(r io.Reader) (*File, error) {
	rdr := tar.NewReader(r)
	f := new(File)

	gotQVM, gotComments, gotSyscalls := false, false, false

L:
	for {
		hdr, err := rdr.Next()
		switch {
		case err == io.EOF:
			break L
		case err != nil:
			return nil, err
		case strings.HasSuffix(hdr.Name, ".qvm"):
			data, err := ioutil.ReadAll(rdr)
			if err != nil {
				return nil, err
			}
			f.QvmFile, err = qvm.NewFile(Rab(data))
			if err != nil {
				return nil, err
			}
			gotQVM = true
		case strings.HasSuffix(hdr.Name, "csv"):
			f.CommentsFile, err = NewCommentsFile(rdr)
			if err != nil {
				return nil, err
			}
			_, _, err = f.CommentsFile.Parse()
			if err != nil {
				return nil, fmt.Errorf("Malformed comments file: %s", err)
			}
			gotComments = true
		case strings.HasSuffix(hdr.Name, "asm"):
			f.SyscallsFile, err = NewSyscallsFile(rdr)
			if err != nil {
				return nil, err
			}
			_, err = f.SyscallsFile.Parse()
			if err != nil {
				return nil, fmt.Errorf("Malformed syscalls file: %s", err)
			}
			gotSyscalls = true
		default:
			return nil, fmt.Errorf("Malformed dar: Extra file %s in archive.", hdr.Name)
		}
	}
	if !gotQVM || !gotComments || !gotSyscalls {
		return nil, fmt.Errorf("Malformed dar: Missing QVM, comments, or syscalls file.")
	}
	return f, nil
}
Beispiel #2
0
func main() {
	cfFile, scFile := "", ""
	flag.StringVar(&cfFile, "comments", "", "Specify a file containing comments and data references")
	flag.StringVar(&scFile, "syscalls", "", "Specify a file defining the syscalls")
	flag.Parse()

	if flag.NArg() < 1 {
		fmt.Println("Must specify at least one QVM or disassembly archive!")
		os.Exit(-1)
	}

	f, err := os.OpenFile(flag.Arg(0), os.O_RDWR, 0600)
	exitErrNotNil(err)

	ctx := new(Context)
	ctx.dar = new(dar.File)
	ctx.dar.CommentsFile = new(dar.CommentsFile)
	ctx.dar.SyscallsFile = new(dar.SyscallsFile)

	switch {
	case strings.HasSuffix(flag.Arg(0), ".qvm"):
		ctx.dar.QvmFile, err = qvm.NewFile(f)
		exitErrNotNil(err)
	case strings.HasSuffix(flag.Arg(0), ".dar"):
		ctx.dar, err = dar.NewFile(f)
		exitErrNotNil(err)
	default:
		fmt.Println("File needs to have a .qvm or .dar extension.")
		os.Exit(-1)
	}

	err = f.Close()
	exitErrNotNil(err)
	ctx.disCtx, err = qvmd.NewContext(ctx.dar.QvmFile, true)
	exitErrNotNil(err)
	ctx.comments, ctx.renames, err = ctx.dar.CommentsFile.Parse()
	exitErrNotNil(err)
	ctx.disCtx.Syscalls, err = ctx.dar.SyscallsFile.Parse()
	exitErrNotNil(err)

	if cfFile != "" {
		commentsFile, err := os.OpenFile(cfFile, os.O_RDWR, 0600)
		exitErrNotNil(err)
		ctx.dar.CommentsFile, err = dar.NewCommentsFile(commentsFile)
		exitErrNotNil(err)
		ctx.comments, ctx.renames, err = ctx.dar.CommentsFile.Parse()
		exitErrNotNil(err)
		err = commentsFile.Close()
		exitErrNotNil(err)
	}

	if scFile != "" {
		syscallsFile, err := os.OpenFile(scFile, os.O_RDWR, 0600)
		exitErrNotNil(err)
		ctx.dar.SyscallsFile, err = dar.NewSyscallsFile(syscallsFile)
		exitErrNotNil(err)
		ctx.disCtx.Syscalls, err = ctx.dar.SyscallsFile.Parse()
		exitErrNotNil(err)
		err = syscallsFile.Close()
		exitErrNotNil(err)
	}

	for num, rename := range ctx.renames {
		if _, exists := ctx.disCtx.Procs[num]; exists {
			ctx.disCtx.Procs[num].Name = rename
		}
	}

	stdin := bufio.NewReader(os.Stdin)

	for {
		fmt.Print("qvmd> ")
		input, err := stdin.ReadString(byte('\n'))
		exitErrNotNil(err)
		cmd := strings.SplitN(strings.TrimSpace(input), " ", -1)
		if strings.ToLower(cmd[0]) == "quit" || err == io.EOF {
			fmt.Print("\n")
			os.Exit(0)
		}

		switch cmd[0] {
		case "help":
			fmt.Println("                   comments - Print all comments")
			fmt.Println("comment <insnNum> <comment> - Assign a comment to instruction number <insnNum>")
			fmt.Println(" dis[as[semble]] <funcName> - Disassemble function <funcName>")
			fmt.Println("             disi <insnNum> - Disassemble function containing instruction <insnNum>")
			fmt.Println("                     header - Print the header for the QVM file")
			fmt.Println("            info <funcName> - Print information about function <funcName>")
			fmt.Println("            infoi <insnNum> - Print information about function containing instruction <insnNum>")
			fmt.Println("      ren[ame] <orig> <new> - Rename function <orig> to <new>")
			fmt.Println("              save [tgtDar] - Save your disassembly. If opened as a QVM [tgtDar] is required")
			fmt.Println("      savecomments [tgtCsv] - Save all comments and renamed functions")
			fmt.Println("      savesyscalls [tgtAsm] - Save all syscalls")
			fmt.Println("              sref <string> - Search for functions referencing strings containing <string>")
			fmt.Println("                   syscalls - Print all known syscalls")

		case "comments":
			for num, comment := range ctx.comments {
				fmt.Printf("0x%08x: %s\n", num, comment)
			}
		case "comment":
			if len(cmd) < 3 {
				fmt.Println("Usage: comment <insnNum> <comment>")
				break
			}
			insn, err := strconv.ParseUint(cmd[1], 0, 64)
			if err != nil {
				fmt.Println("Usage: comment <insnNum> <comment>")
				break
			}
			goAhead := true
			if _, exists := ctx.comments[int(insn)]; exists {
				fmt.Print("Overwrite existing comment? [Y/n]: ")
			Ans1:
				for {
					answer, err := stdin.ReadString(byte('\n'))
					if err != nil {
						fmt.Println(err)
						break
					}
					answer = strings.ToLower(strings.TrimSpace(answer))
					switch answer {
					case "n", "no":
						goAhead = false
						break Ans1
					case "y", "yes", "":
						break Ans1
					default:
						fmt.Print("Please answer \"yes\" or \"no\": ")
					}
				}
			}
			if goAhead {
				ctx.comments[int(insn)] = strings.Join(cmd[2:], " ")
			} else {
				fmt.Println("Comment not replaced.")
			}
		case "dis", "disas", "disassemble":
			if len(cmd) < 2 {
				fmt.Printf("Usage: %s <funcName>\n", cmd[0])
				break
			}
			found := false
			for _, proc := range ctx.disCtx.Procs {
				if proc.Name == cmd[1] {
					disassemble(ctx, proc)
					found = true
					break
				}
			}
			if !found {
				fmt.Printf("No function named \"%s\" found.\n", cmd[1])
			}
		case "disi":
			if len(cmd) < 2 {
				fmt.Println("Usage: disi <insnNum>")
				break
			}
			tgt, err := strconv.ParseUint(cmd[1], 0, 64)
			if err != nil {
				fmt.Println(err)
				break
			}
			found := false
			for _, proc := range ctx.disCtx.Procs {
				if int(tgt) >= proc.StartInstruction && int(tgt) < proc.StartInstruction+proc.InstructionCount {
					disassemble(ctx, proc)
					found = true
					break
				}
			}
			if !found {
				fmt.Printf("No function containing instruction %d\n", tgt)
			}
		case "header":
			printHeader(ctx.dar.QvmFile)
		case "info":
			if len(cmd) < 2 {
				fmt.Println("Usage: info <funcName>")
				break
			}
			found := false
			for _, proc := range ctx.disCtx.Procs {
				if proc.Name == cmd[1] {
					printInfo(ctx, proc)
					found = true
					break
				}
			}
			if !found {
				fmt.Printf("No function named \"%s\" found.\n", cmd[1])
			}
		case "infoi":
			if len(cmd) < 2 {
				fmt.Println("Usage: infoi <insnNum>")
				break
			}
			tgt, err := strconv.ParseUint(cmd[1], 0, 64)
			if err != nil {
				fmt.Println(err)
				break
			}
			found := false
			for _, proc := range ctx.disCtx.Procs {
				if int(tgt) >= proc.StartInstruction && int(tgt) < proc.StartInstruction+proc.InstructionCount {
					printInfo(ctx, proc)
					found = true
					break
				}
			}
			if !found {
				fmt.Printf("No function containing instruction %d\n", tgt)
			}
		case "ren", "rename":
			if len(cmd) < 3 {
				fmt.Printf("Usage: %s <orig> <new>\n", cmd[0])
				break
			}
			found := false
			for _, proc := range ctx.disCtx.Procs {
				if proc.Name == cmd[1] {
					proc.Name = cmd[2]
					found = true
					ctx.renames[proc.StartInstruction] = cmd[2]
				}
			}
			if !found {
				fmt.Printf("No function named \"%s\" found.\n", cmd[1])
			}
		case "save":
			tgtFile := flag.Arg(0)
			if len(cmd) >= 2 {
				tgtFile = strings.Join(cmd[1:], " ")
			}
			if strings.HasSuffix(tgtFile, ".qvm") {
				fmt.Println("Opened as a single QVM. Please save to a new dar")
				break
			}
			if err := ctx.dar.CommentsFile.Write(ctx.comments, ctx.renames); err != nil {
				fmt.Println(err)
				break
			}

			f, err = os.OpenFile(tgtFile, os.O_RDWR|os.O_CREATE, 0600)
			if err != nil {
				fmt.Println(err)
			}
			err = f.Truncate(0)
			if err != nil {
				fmt.Println(err)
				f.Close()
				break
			}
			_, err = f.Seek(0, 0)
			if err != nil {
				fmt.Println(err)
				f.Close()
				break
			}
			err = ctx.dar.WriteTo(f)
			if err != nil {
				fmt.Println(err)
				f.Close()
				break
			}
			err = f.Sync()
			if err != nil {
				fmt.Println(err)
				f.Close()
				break
			}
			err = f.Close()
			if err != nil {
				fmt.Println(err)
			}

		case "savecomments":
			tgtFile := cfFile
			if len(cmd) >= 2 {
				tgtFile = strings.Join(cmd[1:], " ")
			}
			if tgtFile == "" {
				fmt.Println("Usage: savecomments <file>")
				break
			}

			if err := ctx.dar.CommentsFile.Write(ctx.comments, ctx.renames); err != nil {
				fmt.Println(err)
				break
			}

			f, err := os.OpenFile(tgtFile, os.O_RDWR|os.O_CREATE, 0600)
			if err != nil {
				fmt.Println(err)
				break
			}

			err = f.Truncate(0)
			if err != nil {
				fmt.Println(err)
				f.Close()
				break
			}
			_, err = f.Seek(0, 0)
			if err != nil {
				fmt.Println(err)
				f.Close()
				break
			}
			if n, err := f.Write(ctx.dar.CommentsFile.Data); err != nil || n != len(ctx.dar.CommentsFile.Data) {
				fmt.Printf("Error writing comments: %s\n", err)
				f.Close()
				break
			}
			err = f.Close()
			if err != nil {
				fmt.Println(err)
			}

		case "savesyscalls":
			tgtFile := scFile
			if len(cmd) >= 2 {
				tgtFile = strings.Join(cmd[1:], " ")
			}

			if tgtFile == "" {
				fmt.Println("savesyscalls <file>")
				break
			}

			f, err := os.OpenFile(tgtFile, os.O_RDWR|os.O_CREATE, 0600)
			if err != nil {
				fmt.Println(err)
				break
			}

			err = f.Truncate(0)
			if err != nil {
				fmt.Println(err)
				f.Close()
				break
			}
			_, err = f.Seek(0, 0)
			if err != nil {
				fmt.Println(err)
				f.Close()
				break
			}
			if n, err := f.Write(ctx.dar.SyscallsFile.Data); err != nil || n != len(ctx.dar.SyscallsFile.Data) {
				fmt.Printf("Error writing syscalls: %s\n", err)
				f.Close()
				break
			}
			err = f.Close()
			if err != nil {
				fmt.Println(err)
			}
		case "sref":
			if len(cmd) < 2 {
				fmt.Println("Usage: sref <string>")
			}
			found := false
			for _, proc := range ctx.disCtx.Procs {
				for i := proc.StartInstruction; i < proc.StartInstruction+proc.InstructionCount; i++ {
					if ctx.disCtx.Insns[i].Op == qvmd.OP_CONST && ctx.disCtx.Insns[i+1].Op != qvmd.OP_CALL {
						tgtBuf := bytes.NewBuffer(ctx.disCtx.Insns[i].Arg)
						var tgt uint32
						if err := binary.Read(tgtBuf, binary.LittleEndian, &tgt); err != nil {
							fmt.Println(err)
						}
						if tgt >= ctx.dar.QvmFile.Header.DataLength && tgt < ctx.dar.QvmFile.Header.DataLength+ctx.dar.QvmFile.Header.LitLength {
							if str, exists := ctx.disCtx.Strings[int(tgt)]; exists {
								if strings.Contains(str, strings.Join(cmd[1:], " ")) {
									fmt.Println(proc.Name)
									found = true
								}
							}
						}
					}
				}
			}
			if !found {
				fmt.Printf("No functions containing \"%s\"\n", strings.Join(cmd[1:], " "))
			}
		case "syscalls":
			keys := make([]int, len(ctx.disCtx.Syscalls))
			for key, _ := range ctx.disCtx.Syscalls {
				keys = append(keys, key)
			}
			sort.Sort(sort.IntSlice(keys))
			for _, key := range keys {
				fmt.Printf("%d: %s\n", key, ctx.disCtx.Syscalls[key].Name)
			}
		}
	}
}