Beispiel #1
0
func IsPE(fPath string) error {
	if _, err := pe.Open(fPath); err != nil {
		return err
	}

	return nil
}
Beispiel #2
0
func pclnPE() (textStart uint64, symtab, pclntab []byte, err error) {
	f, err := pe.Open(*flagCompiler)
	if err != nil {
		return
	}
	defer f.Close()

	var imageBase uint64
	switch oh := f.OptionalHeader.(type) {
	case *pe.OptionalHeader32:
		imageBase = uint64(oh.ImageBase)
	case *pe.OptionalHeader64:
		imageBase = oh.ImageBase
	default:
		return 0, nil, nil, fmt.Errorf("pe file format not recognized")
	}
	if sect := f.Section(".text"); sect != nil {
		textStart = imageBase + uint64(sect.VirtualAddress)
	}
	if pclntab, err = loadPETable(f, "runtime.pclntab", "runtime.epclntab"); err != nil {
		return
	}
	if symtab, err = loadPETable(f, "runtime.symtab", "runtime.esymtab"); err != nil {
		return
	}

	return
}
Beispiel #3
0
func getDwarf(execname string) *dwarf.Data {
	e, err := elf.Open(execname)
	if err == nil {
		defer e.Close()
		d, err := e.DWARF()
		if err == nil {
			return d
		}
	}
	m, err := macho.Open(execname)
	if err == nil {
		defer m.Close()
		d, err := m.DWARF()
		if err == nil {
			return d
		}
	}
	p, err := pe.Open(execname)
	if err == nil {
		defer p.Close()
		d, err := p.DWARF()
		if err == nil {
			return d
		}
	}
	log.Fatal("can't get dwarf info from executable", err)
	return nil
}
Beispiel #4
0
func openBinary(name string) (Binary, error) {
	f, err := os.Open(name)
	if err != nil {
		return nil, err
	}
	magic := make([]byte, 4)
	if _, err := f.ReadAt(magic[:], 0); err != nil {
		return nil, err
	}

	if bytes.HasPrefix(magic, []byte{0x7f, 'E', 'L', 'F'}) {
		e, err := elf.NewFile(f)
		if err != nil {
			return nil, err
		}
		return &elfBinary{File: e}, nil
	} else if bytes.HasPrefix(magic, []byte{'M', 'Z'}) {
		p, err := pe.Open(name)
		if err != nil {
			return nil, err
		}
		return &peBinary{File: p}, nil
	} else if bytes.HasPrefix(magic, []byte{0xcf, 0xfa, 0xed, 0xfe}) {
		m, err := macho.Open(name)
		if err != nil {
			return nil, err
		}
		return &machoBinary{File: m}, nil
	}
	return nil, fmt.Errorf("unsupported binary format")
}
Beispiel #5
0
func doit(path string) error {
	f, e := pe.Open(path)
	check(e)

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

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

	m, e := loader.Load(ws)
	check(e)

	e = ws.Disassemble(m.EntryPoint, 0x30, os.Stdout)
	check(e)

	emu, e := ws.GetEmulator()
	check(e)

	emu.SetInstructionPointer(m.EntryPoint)

	log.Printf("emudbg: start: 0x%x", emu.GetInstructionPointer())

	e = doloop(emu)
	check(e)

	return nil
}
Beispiel #6
0
func main() {
	file, err := pe.Open("Hello.exe")
	if err != nil {
		log.Fatalf("failed opening file: %s", err)
	}
	defer file.Close()
	printFileInformation(file)
}
Beispiel #7
0
func dynimport(obj string) {
	if f, err := elf.Open(obj); err == nil {
		sym, err := f.ImportedSymbols()
		if err != nil {
			fatalf("cannot load imported symbols from ELF file %s: %v", obj, err)
		}
		for _, s := range sym {
			targ := s.Name
			if s.Version != "" {
				targ += "@" + s.Version
			}
			fmt.Printf("#pragma dynimport %s %s %q\n", s.Name, targ, s.Library)
		}
		lib, err := f.ImportedLibraries()
		if err != nil {
			fatalf("cannot load imported libraries from ELF file %s: %v", obj, err)
		}
		for _, l := range lib {
			fmt.Printf("#pragma dynimport _ _ %q\n", l)
		}
		return
	}

	if f, err := macho.Open(obj); err == nil {
		sym, err := f.ImportedSymbols()
		if err != nil {
			fatalf("cannot load imported symbols from Mach-O file %s: %v", obj, err)
		}
		for _, s := range sym {
			if len(s) > 0 && s[0] == '_' {
				s = s[1:]
			}
			fmt.Printf("#pragma dynimport %s %s %q\n", s, s, "")
		}
		lib, err := f.ImportedLibraries()
		if err != nil {
			fatalf("cannot load imported libraries from Mach-O file %s: %v", obj, err)
		}
		for _, l := range lib {
			fmt.Printf("#pragma dynimport _ _ %q\n", l)
		}
		return
	}

	if f, err := pe.Open(obj); err == nil {
		sym, err := f.ImportedSymbols()
		if err != nil {
			fatalf("cannot load imported symbols from PE file %s: %v", obj, err)
		}
		for _, s := range sym {
			ss := strings.Split(s, ":", -1)
			fmt.Printf("#pragma dynimport %s %s %q\n", ss[0], ss[0], strings.ToLower(ss[1]))
		}
		return
	}

	fatalf("cannot parse %s as ELF, Mach-O or PE", obj)
}
Beispiel #8
0
func PEAnal(input string, symbolsDump bool) ([][]int, error) {
	// An array of arrays for storing the section offsets
	var sectionData [][]int

	fmt.Printf("[+] Analyzing binary: %s\n", input)

	// Check for executable type
	peFmt, err := pe.Open(input)
	if err != nil {
		fmt.Println("[!] This is not a valid PE file")
		return sectionData, nil
	}
	defer peFmt.Close()

	fmt.Println("[+] This is a valid PE file")
	fmt.Printf("[+] Number of sections: %d\n", peFmt.NumberOfSections)
	sections := peFmt.Sections

	for k := range sections {
		sec := sections[k]
		secName := sec.Name
		secSize := sec.Size
		secOffset := sec.Offset + 1
		secEnd := secOffset + secSize - 1
		secVSize := sec.VirtualSize
		secVAddr := sec.VirtualAddress

		fmt.Printf("\t Name: %s\n", secName)
		fmt.Printf("\t Size: %d\n", secSize)
		fmt.Printf("\t Offset: %d\n", secOffset)
		fmt.Printf("\t Section end: %d\n", secEnd)
		fmt.Printf("\t Virtual size: %d\n", secVSize)
		fmt.Printf("\t Virtual address: %d\n", secVAddr)
		fmt.Println("")

		sectionData = append(sectionData, []int{int(secOffset), int(secEnd)})
	}

	numberOfSymbols := peFmt.NumberOfSymbols
	fmt.Printf("[+] Found %d symbols\n", numberOfSymbols)
	if numberOfSymbols > 0 && symbolsDump {
		symbols := peFmt.Symbols

		for k := range symbols {
			sym := symbols[k]
			symName := sym.Name
			// symType := sym.Type
			// symValue := sym.Value

			fmt.Printf("\t Name: %s\n", symName)
			// fmt.Printf("\t\t Type: %d", symType)
			// fmt.Printf("\t\t Value: %d", symValue)
		}
		fmt.Println("")
	}

	return sectionData, nil
}
Beispiel #9
0
// gccDebug runs gcc -gdwarf-2 over the C program stdin and
// returns the corresponding DWARF data and, if present, debug data block.
func (p *Package) gccDebug(stdin []byte) (*dwarf.Data, binary.ByteOrder, []byte) {
	runGcc(stdin, p.gccCmd())

	if f, err := macho.Open(gccTmp); err == nil {
		d, err := f.DWARF()
		if err != nil {
			fatalf("cannot load DWARF output from %s: %v", gccTmp, err)
		}
		var data []byte
		if f.Symtab != nil {
			for i := range f.Symtab.Syms {
				s := &f.Symtab.Syms[i]
				// Mach-O still uses a leading _ to denote non-assembly symbols.
				if s.Name == "_"+"__cgodebug_data" {
					// Found it.  Now find data section.
					if i := int(s.Sect) - 1; 0 <= i && i < len(f.Sections) {
						sect := f.Sections[i]
						if sect.Addr <= s.Value && s.Value < sect.Addr+sect.Size {
							if sdat, err := sect.Data(); err == nil {
								data = sdat[s.Value-sect.Addr:]
							}
						}
					}
				}
			}
		}
		return d, f.ByteOrder, data
	}

	// Can skip debug data block in ELF and PE for now.
	// The DWARF information is complete.

	if f, err := elf.Open(gccTmp); err == nil {
		d, err := f.DWARF()
		if err != nil {
			fatalf("cannot load DWARF output from %s: %v", gccTmp, err)
		}
		return d, f.ByteOrder, nil
	}

	if f, err := pe.Open(gccTmp); err == nil {
		d, err := f.DWARF()
		if err != nil {
			fatalf("cannot load DWARF output from %s: %v", gccTmp, err)
		}
		return d, binary.LittleEndian, nil
	}

	fatalf("cannot parse gcc output %s as ELF, Mach-O, PE object", gccTmp)
	panic("not reached")
}
Beispiel #10
0
func Open(name string) (binaryx.File, error) {
	elfBinary, err := elf.Open(name)
	if err == nil {
		return newFile(&elfx.File{elfBinary})
	}
	machoBinary, err := macho.Open(name)
	if err == nil {
		return newFile(&machox.File{machoBinary})
	}
	peBinary, err := pe.Open(name)
	if err == nil {
		return newFile(&pex.File{peBinary})
	}
	return nil, err
}
Beispiel #11
0
func listSections(path string) error {
	f, err := pe.Open(path)
	if err != nil {
		return err
	}
	defer f.Close()

	// print sections
	fmt.Println()
	fmt.Println("Sections:")
	fmt.Println()
	fmt.Printf("idx virtual virtual    disk    disk   reloc  reloc     mask\n")
	fmt.Printf("    address    size  offset    size  offset    qty         \n")
	fmt.Printf("--- ------- ------- ------- ------- ------- ------ --------\n")
	for i, s := range f.Sections {
		fmt.Printf("%3d %7x %7x %7x %7x %7x %6d %x %s\n",
			i+1, s.VirtualAddress, s.VirtualSize, s.Offset, s.Size,
			s.PointerToRelocations, s.NumberOfRelocations,
			s.Characteristics, s.Name)
	}

	// print alignments
	fmt.Println()
	switch oh := f.OptionalHeader.(type) {
	case nil:
		fmt.Printf("no section or file alignment (no optional header present)\n")
	case *pe.OptionalHeader32:
		fmt.Printf("section alignment is 0x%x\n", oh.SectionAlignment)
		fmt.Printf("file alignment is 0x%x\n", oh.FileAlignment)
	case *pe.OptionalHeader64:
		fmt.Printf("section alignment is 0x%x\n", oh.SectionAlignment)
		fmt.Printf("file alignment is 0x%x\n", oh.FileAlignment)
	default:
		panic("unknown OptionalHeader type")
	}

	// print symbols
	return printSymbols(f)

	// TODO: I am not sure I want to print coff strings here
	fmt.Println()
	fmt.Println("Strings:")
	fmt.Println()
	fmt.Print(hex.Dump(f.StringTable))
	return nil
}
Beispiel #12
0
// Exe returns architecture of execute file.
func Exe(name string) (CPU, error) {
	f, err := pe.Open(name)
	if err != nil {
		if os.IsNotExist(err) {
			return OS()
		}
		return 0, err
	}
	defer f.Close()

	switch f.FileHeader.Machine {
	case 0x014c:
		return X86, nil
	case 0x8664:
		return AMD64, nil
	}
	return 0, ErrorUnknownArch
}
Beispiel #13
0
func loadArch(name string) (arch string, err error) {
	f, err := pe.Open(name)
	if err != nil {
		if os.IsNotExist(err) {
			err = nil
		}
		return
	}
	defer f.Close()

	switch f.FileHeader.Machine {
	case 0x014c:
		arch = "x86"
	case 0x8664:
		arch = "amd64"
	}
	return
}
Beispiel #14
0
func PEAnal(input string) ([]SectionData, []string, []string, error) {
	// An array of arrays for storing the section offsets
	var sectionData []SectionData

	// Check for executable type
	peFmt, err := pe.Open(input)
	if err != nil {
		return sectionData, nil, nil, NotValidPEFileError
	}
	defer peFmt.Close()

	// Extract sections
	sections := peFmt.Sections
	for k := range sections {
		sec := sections[k]
		secName := sec.Name
		secSize := sec.Size
		secOffset := sec.Offset + 1
		secEnd := secOffset + secSize - 1

		sd := SectionData{
			Name:   secName,
			Size:   int(secSize),
			Offset: int(secOffset),
			End:    int(secEnd),
		}

		sectionData = append(sectionData, sd)
	}

	// Get imported symbols
	symbolsArr, err := peFmt.ImportedSymbols()
	if err != nil {
		return sectionData, nil, nil, err
	}

	// Get imported libraries
	libraries, err := peFmt.ImportedLibraries()
	if err != nil {
		return sectionData, nil, nil, err
	}

	return sectionData, libraries, symbolsArr, nil
}
Beispiel #15
0
func dumpSection(path string, name string) error {
	f, err := pe.Open(path)
	if err != nil {
		return err
	}
	defer f.Close()

	sect := f.Section(name)
	if sect == nil {
		return fmt.Errorf("could not find section %q", name)
	}
	data, err := sect.Data()
	if err != nil {
		return err
	}
	fmt.Print(hex.Dump(data))

	return printRelocations(f, sect)
}
Beispiel #16
0
func newpe(path string) (tabler, error) {
	f, err := pe.Open(path)
	if err != nil {
		return nil, err
	}
	tbl := petbl{f, nil, 0}
	switch oh := tbl.OptionalHeader.(type) {
	case *pe.OptionalHeader32:
		tbl.base = uint64(oh.ImageBase)
		tbl.typ = PlatformWindows386
	case *pe.OptionalHeader64:
		tbl.base = oh.ImageBase
		tbl.typ = PlatformWindowsAMD64
	default:
		tbl.Close()
		return nil, ErrNotGoExec
	}
	return tbl, nil
}
Beispiel #17
0
func PEAutopsy(fileDir string) error {
	// Open PE file
	peFile, err := pe.Open(name)
	if err != nil {
		return NotValidPEFileError
	}
	defer peFile.Close()

	// Extract all the valuable info
	architecture := peFile.Machine
	librariesArr, err := peFile.ImportedLibraries()
	if err != nil {
		return err
	}
	importedSymbols, err := peFile.ImportedSymbols()
	if err != nil {
		return err
	}
    peFile.
}
Beispiel #18
0
func containsDLLs(dir string) bool {
	compatible := func(name string) bool {
		file, err := pe.Open(filepath.Join(dir, name))
		if err != nil {
			return false
		}
		defer file.Close()

		switch file.Machine {
		case pe.IMAGE_FILE_MACHINE_AMD64:
			return "amd64" == runtime.GOARCH
		case pe.IMAGE_FILE_MACHINE_ARM:
			return "arm" == runtime.GOARCH
		case pe.IMAGE_FILE_MACHINE_I386:
			return "386" == runtime.GOARCH
		}
		return false
	}

	return compatible("libglesv2.dll") && compatible("libegl.dll") && compatible("d3dcompiler_47.dll")
}
Beispiel #19
0
func TestPE(filename, expectedArch, expectedOs string) error {
	file, err := pe.Open(filename)
	if err != nil {
		return errors.New("NOT a PE file")
	}
	defer file.Close()
	log.Printf("File '%s' is a PE file, arch: %d (%d='X86' and %d='AMD64')\n", filename, file.FileHeader.Machine, pe.IMAGE_FILE_MACHINE_I386, pe.IMAGE_FILE_MACHINE_AMD64)
	if expectedArch == platforms.X86 {
		if file.FileHeader.Machine != pe.IMAGE_FILE_MACHINE_I386 {
			return errors.New("Not a 386 executable")
		}

	}
	if expectedArch == platforms.AMD64 {
		if file.FileHeader.Machine != pe.IMAGE_FILE_MACHINE_AMD64 {
			return errors.New("Not an AMD64 executable")
		}

	}
	return nil

}
Beispiel #20
0
// gccDebug runs gcc -gdwarf-2 over the C program stdin and
// returns the corresponding DWARF data and any messages
// printed to standard error.
func (p *Package) gccDebug(stdin []byte) *dwarf.Data {
	runGcc(stdin, p.gccCmd())

	// Try to parse f as ELF and Mach-O and hope one works.
	var f interface {
		DWARF() (*dwarf.Data, os.Error)
	}
	var err os.Error
	if f, err = elf.Open(gccTmp); err != nil {
		if f, err = macho.Open(gccTmp); err != nil {
			if f, err = pe.Open(gccTmp); err != nil {
				fatalf("cannot parse gcc output %s as ELF or Mach-O or PE object", gccTmp)
			}
		}
	}

	d, err := f.DWARF()
	if err != nil {
		fatalf("cannot load DWARF debug information from %s: %s", gccTmp, err)
	}
	return d
}
Beispiel #21
0
func doit(path string) error {
	logrus.SetLevel(logrus.DebugLevel)

	f, 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)

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

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

	check(config.RegisterDefaultAnalyzers(ws))

	ws.AnalyzeAll()

	return nil
}
Beispiel #22
0
func dynimport(obj string) {
	stdout := os.Stdout
	if *dynout != "" {
		f, err := os.Create(*dynout)
		if err != nil {
			fatalf("%s", err)
		}
		stdout = f
	}

	fmt.Fprintf(stdout, "package %s\n", *dynpackage)

	if f, err := elf.Open(obj); err == nil {
		if *dynlinker {
			// Emit the cgo_dynamic_linker line.
			if sec := f.Section(".interp"); sec != nil {
				if data, err := sec.Data(); err == nil && len(data) > 1 {
					// skip trailing \0 in data
					fmt.Fprintf(stdout, "//go:cgo_dynamic_linker %q\n", string(data[:len(data)-1]))
				}
			}
		}
		sym, err := f.ImportedSymbols()
		if err != nil {
			fatalf("cannot load imported symbols from ELF file %s: %v", obj, err)
		}
		for _, s := range sym {
			targ := s.Name
			if s.Version != "" {
				targ += "#" + s.Version
			}
			fmt.Fprintf(stdout, "//go:cgo_import_dynamic %s %s %q\n", s.Name, targ, s.Library)
		}
		lib, err := f.ImportedLibraries()
		if err != nil {
			fatalf("cannot load imported libraries from ELF file %s: %v", obj, err)
		}
		for _, l := range lib {
			fmt.Fprintf(stdout, "//go:cgo_import_dynamic _ _ %q\n", l)
		}
		return
	}

	if f, err := macho.Open(obj); err == nil {
		sym, err := f.ImportedSymbols()
		if err != nil {
			fatalf("cannot load imported symbols from Mach-O file %s: %v", obj, err)
		}
		for _, s := range sym {
			if len(s) > 0 && s[0] == '_' {
				s = s[1:]
			}
			fmt.Fprintf(stdout, "//go:cgo_import_dynamic %s %s %q\n", s, s, "")
		}
		lib, err := f.ImportedLibraries()
		if err != nil {
			fatalf("cannot load imported libraries from Mach-O file %s: %v", obj, err)
		}
		for _, l := range lib {
			fmt.Fprintf(stdout, "//go:cgo_import_dynamic _ _ %q\n", l)
		}
		return
	}

	if f, err := pe.Open(obj); err == nil {
		sym, err := f.ImportedSymbols()
		if err != nil {
			fatalf("cannot load imported symbols from PE file %s: %v", obj, err)
		}
		for _, s := range sym {
			ss := strings.Split(s, ":")
			name := strings.Split(ss[0], "@")[0]
			fmt.Fprintf(stdout, "//go:cgo_import_dynamic %s %s %q\n", name, ss[0], strings.ToLower(ss[1]))
		}
		return
	}

	fatalf("cannot parse %s as ELF, Mach-O or PE", obj)
}
Beispiel #23
0
// gccDebug runs gcc -gdwarf-2 over the C program stdin and
// returns the corresponding DWARF data and, if present, debug data block.
func (p *Package) gccDebug(stdin []byte) (*dwarf.Data, binary.ByteOrder, []byte) {
	runGcc(stdin, p.gccCmd())

	isDebugData := func(s string) bool {
		// Some systems use leading _ to denote non-assembly symbols.
		return s == "__cgodebug_data" || s == "___cgodebug_data"
	}

	if f, err := macho.Open(gccTmp()); err == nil {
		defer f.Close()
		d, err := f.DWARF()
		if err != nil {
			fatalf("cannot load DWARF output from %s: %v", gccTmp(), err)
		}
		var data []byte
		if f.Symtab != nil {
			for i := range f.Symtab.Syms {
				s := &f.Symtab.Syms[i]
				if isDebugData(s.Name) {
					// Found it.  Now find data section.
					if i := int(s.Sect) - 1; 0 <= i && i < len(f.Sections) {
						sect := f.Sections[i]
						if sect.Addr <= s.Value && s.Value < sect.Addr+sect.Size {
							if sdat, err := sect.Data(); err == nil {
								data = sdat[s.Value-sect.Addr:]
							}
						}
					}
				}
			}
		}
		return d, f.ByteOrder, data
	}

	if f, err := elf.Open(gccTmp()); err == nil {
		defer f.Close()
		d, err := f.DWARF()
		if err != nil {
			fatalf("cannot load DWARF output from %s: %v", gccTmp(), err)
		}
		var data []byte
		symtab, err := f.Symbols()
		if err == nil {
			for i := range symtab {
				s := &symtab[i]
				if isDebugData(s.Name) {
					// Found it.  Now find data section.
					if i := int(s.Section); 0 <= i && i < len(f.Sections) {
						sect := f.Sections[i]
						if sect.Addr <= s.Value && s.Value < sect.Addr+sect.Size {
							if sdat, err := sect.Data(); err == nil {
								data = sdat[s.Value-sect.Addr:]
							}
						}
					}
				}
			}
		}
		return d, f.ByteOrder, data
	}

	if f, err := pe.Open(gccTmp()); err == nil {
		defer f.Close()
		d, err := f.DWARF()
		if err != nil {
			fatalf("cannot load DWARF output from %s: %v", gccTmp(), err)
		}
		var data []byte
		for _, s := range f.Symbols {
			if isDebugData(s.Name) {
				if i := int(s.SectionNumber) - 1; 0 <= i && i < len(f.Sections) {
					sect := f.Sections[i]
					if s.Value < sect.Size {
						if sdat, err := sect.Data(); err == nil {
							data = sdat[s.Value:]
						}
					}
				}
			}
		}
		return d, binary.LittleEndian, data
	}

	fatalf("cannot parse gcc output %s as ELF, Mach-O, PE object", gccTmp())
	panic("not reached")
}
Beispiel #24
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
}