diff --git a/go/cmd/binana/cmd/generate.go b/go/cmd/binana/cmd/generate.go new file mode 100644 index 0000000..e24dbc5 --- /dev/null +++ b/go/cmd/binana/cmd/generate.go @@ -0,0 +1,37 @@ +package cmd + +import ( + "fmt" + "os" + + "github.com/spf13/cobra" + "github.com/thunderbrewhq/binana/go/profile" +) + +var generate = &cobra.Command{ + Use: "generate", + Short: "Convert source files into various tool formats", + Run: generate_func, +} + +func generate_func(cmd *cobra.Command, args []string) { + compress, err := cmd.Flags().GetBool("compress") + if err != nil { + panic(err) + } + + game_profile_directory, err := cmd.Flags().GetString("profile") + if err != nil { + panic(err) + } + + game_profile, err := profile.Open(game_profile_directory) + if err != nil { + panic(err) + } + + if err = game_profile.Generate(compress); err != nil { + fmt.Println(err) + os.Exit(1) + } +} diff --git a/go/cmd/binana/cmd/ida_gen.go b/go/cmd/binana/cmd/ida_gen.go deleted file mode 100644 index 2d80028..0000000 --- a/go/cmd/binana/cmd/ida_gen.go +++ /dev/null @@ -1,38 +0,0 @@ -package cmd - -import ( - "fmt" - "os" - - "github.com/spf13/cobra" - "github.com/thunderbrewhq/binana/go/profile" -) - -var ida_gen = &cobra.Command{ - Use: "ida-gen", - Short: "Generate IDC file using symbol file", - Run: ida_gen_func, -} - -func ida_gen_func(cmd *cobra.Command, args []string) { - // get command line arguments - // module_name, err := cmd.Flags().GetString("module-name") - // if err != nil { - // panic(err) - // } - - game_profile_directory, err := cmd.Flags().GetString("game") - if err != nil { - panic(err) - } - - game_profile, err := profile.Open(game_profile_directory) - if err != nil { - panic(err) - } - - if err = game_profile.CreateIDAFiles(); err != nil { - fmt.Println(err) - os.Exit(1) - } -} diff --git a/go/cmd/binana/cmd/root.go b/go/cmd/binana/cmd/root.go index 37f2f59..bc1ed4d 100644 --- a/go/cmd/binana/cmd/root.go +++ b/go/cmd/binana/cmd/root.go @@ -35,13 +35,10 @@ func init() { // when this action is called directly. // rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") - ida_gen.Flags().StringP("game", "g", "3.3.5a", "the game profile") - rootCmd.AddCommand(ida_gen) + generate.Flags().StringP("profile", "p", "3.3.5a", "the game profile") + generate.Flags().BoolP("compress", "c", true, "enable/disable compression of the x64dbg database file") - x64dbg_gen.Flags().StringP("game", "g", "3.3.5a", "the game profile") - x64dbg_gen.Flags().StringP("module-name", "m", "wow.exe", "the name of the module") - x64dbg_gen.Flags().StringP("base-address", "b", "00400000", "the base address of the module") + rootCmd.AddCommand(generate) - rootCmd.AddCommand(x64dbg_gen) rootCmd.AddCommand(x64dbg_typesort) } diff --git a/go/cmd/binana/cmd/x64dbg_gen.go b/go/cmd/binana/cmd/x64dbg_gen.go deleted file mode 100644 index 130e232..0000000 --- a/go/cmd/binana/cmd/x64dbg_gen.go +++ /dev/null @@ -1,50 +0,0 @@ -package cmd - -import ( - "fmt" - "os" - "strconv" - - "github.com/spf13/cobra" - "github.com/thunderbrewhq/binana/go/profile" -) - -var x64dbg_gen = &cobra.Command{ - Use: "x64dbg-gen", - Short: "Generate x64dbg database using a symfile", - Run: x64dbg_gen_func, -} - -func x64dbg_gen_func(cmd *cobra.Command, args []string) { - // get command line arguments - module_name, err := cmd.Flags().GetString("module-name") - if err != nil { - panic(err) - } - - base_address_s, err := cmd.Flags().GetString("base-address") - if err != nil { - panic(err) - } - - // parse the base address of game EXE - base_address, err := strconv.ParseUint(base_address_s, 16, 64) - if err != nil { - panic(err) - } - - game_profile_directory, err := cmd.Flags().GetString("game") - if err != nil { - panic(err) - } - - game_profile, err := profile.Open(game_profile_directory) - if err != nil { - panic(err) - } - - if err = game_profile.CreateX64dbgFiles(module_name, base_address); err != nil { - fmt.Println(err) - os.Exit(1) - } -} diff --git a/go/profile/generate.go b/go/profile/generate.go new file mode 100644 index 0000000..3409c17 --- /dev/null +++ b/go/profile/generate.go @@ -0,0 +1,15 @@ +package profile + +func (profile *Profile) Generate(compress_db bool) (err error) { + if err = profile.CreateIDAFiles(); err != nil { + return + } + + if profile.Info.OS == "windows" { + if err = profile.CreateX64dbgFiles(compress_db); err != nil { + return + } + } + + return +} diff --git a/go/profile/ida_generate_files.go b/go/profile/ida_generate_files.go index 51e927f..92b8842 100644 --- a/go/profile/ida_generate_files.go +++ b/go/profile/ida_generate_files.go @@ -85,8 +85,19 @@ func (profile *Profile) generate_symbols_idc() (err error) { b.P("static import_symbols() {") b.T(1) b.P("// Set/create names") + + name_instances := make(map[string]int) + for _, symbol := range profile.SymbolTable.Entries { - quoted_name := strconv.Quote(symbol.Name) + name := symbol.Name + instances := name_instances[name] + name_instances[name] = instances + 1 + + if instances != 0 { + name = fmt.Sprintf("%s@%d", name, instances+1) + } + + quoted_name := strconv.Quote(name) address := fmt.Sprintf("0x%08X", symbol.StartAddress) b.P("set_name(%s, %s);", address, quoted_name) } @@ -133,11 +144,11 @@ func (profile *Profile) generate_symbols_idc() (err error) { for _, function_symbol := range profile.SymbolTable.Entries { if function_symbol.Kind == symfile.Function { address := fmt.Sprintf("0x%08X", function_symbol.StartAddress) - b.P("set_func_start(%s, %s);", address, address) - if function_symbol.EndAddress != 0 { - end_address := fmt.Sprintf("0x%08X", function_symbol.EndAddress) - b.P("set_func_end(%s, %s);", address, end_address) - } + // b.P("set_func_start(%s, %s);", address, address) + // if function_symbol.EndAddress != 0 { + // end_address := fmt.Sprintf("0x%08X", function_symbol.EndAddress) + // b.P("set_func_end(%s, %s);", address, end_address) + // } if function_symbol.Comment != "" { b.P("set_func_cmt(%s, %s, 0);", address, strconv.Quote(function_symbol.Comment)) } diff --git a/go/profile/info.go b/go/profile/info.go new file mode 100644 index 0000000..1d086a4 --- /dev/null +++ b/go/profile/info.go @@ -0,0 +1,72 @@ +package profile + +import ( + "encoding/json" + "fmt" + "os" + "slices" + "strconv" +) + +var ( + valid_os = []string{ + "windows", + "darwin", + "linux", + } + + valid_arch = []string{ + "386", + "amd64", + "ppc", + } +) + +type info_schema struct { + OS string `json:"os"` + Arch string `json:"arch"` + ModuleName string `json:"module_name"` + ModuleBase string `json:"module_base"` +} + +type Info struct { + OS string + Arch string + ModuleName string + ModuleBase uint64 +} + +func read_info(filename string, info *Info) (err error) { + var b []byte + b, err = os.ReadFile(filename) + if err != nil { + return + } + var is info_schema + err = json.Unmarshal(b, &is) + if err != nil { + return + } + + if !slices.Contains(valid_os, is.OS) { + err = fmt.Errorf("profile: invalid os '%s'", is.OS) + return + } + + if !slices.Contains(valid_arch, is.Arch) { + err = fmt.Errorf("profile: invalid arch '%s'", is.Arch) + return + } + + info.ModuleName = is.ModuleName + + info.OS = is.OS + info.Arch = is.Arch + + info.ModuleBase, err = strconv.ParseUint(is.ModuleBase, 16, 64) + if err != nil { + return + } + + return +} diff --git a/go/profile/open.go b/go/profile/open.go index adb754f..3bb95b6 100644 --- a/go/profile/open.go +++ b/go/profile/open.go @@ -10,6 +10,7 @@ import ( ) type Profile struct { + Info Info Directory string SymbolTable *symfile.InMemoryTable } @@ -29,6 +30,11 @@ func Open(profile_directory string) (profile *Profile, err error) { fmt.Println("Opening profile", profile_directory) profile = new(Profile) + + if err = read_info(filepath.Join(profile_directory, "info.json"), &profile.Info); err != nil { + return + } + profile.Directory = profile_directory path_to_symbols_file := filepath.Join(profile_directory, "symbol", "main.sym") diff --git a/go/profile/x64dbg.go b/go/profile/x64dbg.go index 8f8b2de..77faac8 100644 --- a/go/profile/x64dbg.go +++ b/go/profile/x64dbg.go @@ -1,7 +1,7 @@ package profile -func (profile *Profile) CreateX64dbgFiles(module_name string, base_address uint64) (err error) { - if err = profile.generate_x64dbg_database(module_name, base_address); err != nil { +func (profile *Profile) CreateX64dbgFiles(compress_db bool) (err error) { + if err = profile.generate_x64dbg_database(compress_db); err != nil { return } diff --git a/go/profile/x64dbg_generate_database.go b/go/profile/x64dbg_generate_database.go index 6850488..bc54b29 100644 --- a/go/profile/x64dbg_generate_database.go +++ b/go/profile/x64dbg_generate_database.go @@ -12,8 +12,14 @@ func hex_address(u uint64) string { return fmt.Sprintf("0x%x", u) } -func (profile *Profile) generate_x64dbg_database(module_name string, base_address uint64) (err error) { +func (profile *Profile) generate_x64dbg_database(compress bool) (err error) { // Convert symbol table into x64dbg database + + is_64bit := profile.Info.Arch == "amd64" + + module_name := profile.Info.ModuleName + base_address := profile.Info.ModuleBase + var dd x64dbg.Database for _, entry := range profile.SymbolTable.Entries { @@ -54,9 +60,14 @@ func (profile *Profile) generate_x64dbg_database(module_name string, base_addres } } + filename := "game.dd32" + if is_64bit { + filename = "game.dd64" + } + // save database - dd_path := filepath.Join(profile.Directory, "x32dbg", "game.dd32") - if err = x64dbg.SaveDatabase(dd_path, &dd); err != nil { + dd_path := filepath.Join(profile.Directory, "x64dbg", filename) + if err = x64dbg.SaveDatabase(dd_path, &dd, compress); err != nil { return } diff --git a/go/profile/x64dbg_generate_types.go b/go/profile/x64dbg_generate_types.go index e38ab74..f21232c 100644 --- a/go/profile/x64dbg_generate_types.go +++ b/go/profile/x64dbg_generate_types.go @@ -101,7 +101,7 @@ loop: func (profile *Profile) generate_x64dbg_types() (err error) { // parse C headers var cc_config cc.Config - cc_config.ABI, err = cc.NewABI("windows", "386") + cc_config.ABI, err = cc.NewABI("windows", profile.Info.Arch) if err != nil { panic(err) } @@ -231,7 +231,7 @@ func (profile *Profile) generate_x64dbg_types() (err error) { x64_types.Structs = append(x64_types.Structs, x64_struct) } - types_file_path := filepath.Join(profile.Directory, "x32dbg", "types.json") + types_file_path := filepath.Join(profile.Directory, "x64dbg", "types.json") err = x64dbg.SortTypes(&x64_types) if err != nil { diff --git a/go/x64dbg/compress.go b/go/x64dbg/compress.go new file mode 100644 index 0000000..f084f94 --- /dev/null +++ b/go/x64dbg/compress.go @@ -0,0 +1,31 @@ +package x64dbg + +import ( + "io" + + "github.com/pierrec/lz4/v4" +) + +type lz4_writecloser struct { + w *lz4.Writer +} + +func new_lz4_writecloser(w io.Writer) (l *lz4_writecloser, err error) { + l = new(lz4_writecloser) + l.w = lz4.NewWriter(w) + if err = l.w.Apply(lz4.LegacyOption(true)); err != nil { + return + } + + return +} + +func (l *lz4_writecloser) Write(b []byte) (n int, err error) { + n, err = l.w.Write(b) + return +} + +func (l *lz4_writecloser) Close() (err error) { + err = l.w.Close() + return +} diff --git a/go/x64dbg/database.go b/go/x64dbg/database.go index 617fa6b..072d42d 100644 --- a/go/x64dbg/database.go +++ b/go/x64dbg/database.go @@ -2,6 +2,7 @@ package x64dbg import ( "encoding/json" + "io" "os" ) @@ -59,19 +60,37 @@ type Database struct { Breakpoints []Breakpoint `json:"breakpoints,omitempty"` } -func SaveDatabase(name string, database *Database) (err error) { +func SaveDatabase(name string, database *Database, compress bool) (err error) { var file *os.File file, err = os.Create(name) if err != nil { return } - e := json.NewEncoder(file) - e.SetIndent("", " ") + var writecloser io.WriteCloser = file + + if compress { + writecloser, err = new_lz4_writecloser(file) + if err != nil { + return + } + } + + e := json.NewEncoder(writecloser) + if !compress { + e.SetIndent("", " ") + } if err = e.Encode(database); err != nil { return } - err = file.Close() + if err = writecloser.Close(); err != nil { + return + } + + if compress { + err = file.Close() + } + return }