github.com/decomp/exp@v0.0.0-20210624183419-6d058f5e1da6/cmd/bin2c/main.go (about) 1 // bin2c is a tool which converts binary executables to equivalent C source 2 // code. 3 package main 4 5 import ( 6 "debug/pe" 7 "flag" 8 "fmt" 9 "go/printer" 10 "go/token" 11 "log" 12 "os" 13 "strconv" 14 "strings" 15 16 "github.com/mewkiz/pkg/errutil" 17 ) 18 19 func usage() { 20 const use = ` 21 Usage: bin2c -addr ADDRESS [OPTION]... FILE 22 Convert binary executables to equivalent C source code. 23 24 Flags: 25 ` 26 fmt.Fprint(os.Stderr, use[1:]) 27 flag.PrintDefaults() 28 } 29 30 // Command line flags. 31 var ( 32 // flagVerbose specifies whether verbose output is enabled. 33 flagVerbose bool 34 ) 35 36 // Base address of the ".text" section. 37 const baseAddr = 0x00401000 38 39 func main() { 40 // Parse command line arguments. 41 var ( 42 // addr specifies the address to decompile. 43 addr address 44 ) 45 flag.BoolVar(&flagVerbose, "v", false, "Enable verbose output.") 46 flag.Var(&addr, "addr", "Address of function to decompile.") 47 flag.Usage = usage 48 flag.Parse() 49 if flag.NArg() != 1 { 50 flag.Usage() 51 os.Exit(1) 52 } 53 path := flag.Arg(0) 54 if addr == 0 { 55 flag.Usage() 56 os.Exit(1) 57 } 58 59 // Parse ".text" section. 60 text, err := parseText(path) 61 if err != nil { 62 log.Fatal(err) 63 } 64 65 // Sanity check. 66 offset := int(addr - baseAddr) 67 if offset < 0 || offset >= len(text) { 68 log.Fatalf("invalid address; expected >= 0x%X and < 0x%X, got 0x%X", baseAddr, baseAddr+len(text), addr) 69 } 70 71 // Parse basic blocks. 72 //blocks, err := parseBlocks(text, offset) 73 //if err != nil { 74 // log.Fatal(err) 75 //} 76 77 // Convert the given function to C source code. 78 fn, err := parseFunc(text, offset) 79 if err != nil { 80 log.Fatal(err) 81 } 82 printer.Fprint(os.Stdout, token.NewFileSet(), fn) 83 } 84 85 // parseText parses and returns the ".text" section of the given binary 86 // executable. 87 func parseText(path string) (text []byte, err error) { 88 f, err := pe.Open(path) 89 if err != nil { 90 return nil, errutil.Err(err) 91 } 92 defer f.Close() 93 94 sect := f.Section(".text") 95 if sect == nil { 96 return nil, errutil.Newf(`unable to locate ".text" section in %q`, path) 97 } 98 return sect.Data() 99 } 100 101 // address implements the flag.Value interface and allows addresses to be 102 // specified in hexadecimal format. 103 type address uint64 104 105 // String returns the hexadecimal string representation of v. 106 func (v *address) String() string { 107 return fmt.Sprintf("0x%X", uint64(*v)) 108 } 109 110 // Set sets v to the numberic value represented by s. 111 func (v *address) Set(s string) error { 112 base := 10 113 if strings.HasPrefix(s, "0x") || strings.HasPrefix(s, "0X") { 114 s = s[2:] 115 base = 16 116 } 117 x, err := strconv.ParseUint(s, base, 64) 118 if err != nil { 119 return errutil.Err(err) 120 } 121 *v = address(x) 122 return nil 123 }