mirror of
https://github.com/thunderbrewhq/binana.git
synced 2025-12-12 01:42:29 +00:00
feat(go): profiles are now configured by an info.json file
This commit is contained in:
parent
e591b8b17d
commit
9053d61b6b
13 changed files with 222 additions and 111 deletions
37
go/cmd/binana/cmd/generate.go
Normal file
37
go/cmd/binana/cmd/generate.go
Normal file
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -35,13 +35,10 @@ func init() {
|
||||||
// when this action is called directly.
|
// when this action is called directly.
|
||||||
// rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
|
// rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
|
||||||
|
|
||||||
ida_gen.Flags().StringP("game", "g", "3.3.5a", "the game profile")
|
generate.Flags().StringP("profile", "p", "3.3.5a", "the game profile")
|
||||||
rootCmd.AddCommand(ida_gen)
|
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")
|
rootCmd.AddCommand(generate)
|
||||||
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(x64dbg_gen)
|
|
||||||
rootCmd.AddCommand(x64dbg_typesort)
|
rootCmd.AddCommand(x64dbg_typesort)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
15
go/profile/generate.go
Normal file
15
go/profile/generate.go
Normal file
|
|
@ -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
|
||||||
|
}
|
||||||
|
|
@ -85,8 +85,19 @@ func (profile *Profile) generate_symbols_idc() (err error) {
|
||||||
b.P("static import_symbols() {")
|
b.P("static import_symbols() {")
|
||||||
b.T(1)
|
b.T(1)
|
||||||
b.P("// Set/create names")
|
b.P("// Set/create names")
|
||||||
|
|
||||||
|
name_instances := make(map[string]int)
|
||||||
|
|
||||||
for _, symbol := range profile.SymbolTable.Entries {
|
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)
|
address := fmt.Sprintf("0x%08X", symbol.StartAddress)
|
||||||
b.P("set_name(%s, %s);", address, quoted_name)
|
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 {
|
for _, function_symbol := range profile.SymbolTable.Entries {
|
||||||
if function_symbol.Kind == symfile.Function {
|
if function_symbol.Kind == symfile.Function {
|
||||||
address := fmt.Sprintf("0x%08X", function_symbol.StartAddress)
|
address := fmt.Sprintf("0x%08X", function_symbol.StartAddress)
|
||||||
b.P("set_func_start(%s, %s);", address, address)
|
// b.P("set_func_start(%s, %s);", address, address)
|
||||||
if function_symbol.EndAddress != 0 {
|
// if function_symbol.EndAddress != 0 {
|
||||||
end_address := fmt.Sprintf("0x%08X", function_symbol.EndAddress)
|
// end_address := fmt.Sprintf("0x%08X", function_symbol.EndAddress)
|
||||||
b.P("set_func_end(%s, %s);", address, end_address)
|
// b.P("set_func_end(%s, %s);", address, end_address)
|
||||||
}
|
// }
|
||||||
if function_symbol.Comment != "" {
|
if function_symbol.Comment != "" {
|
||||||
b.P("set_func_cmt(%s, %s, 0);", address, strconv.Quote(function_symbol.Comment))
|
b.P("set_func_cmt(%s, %s, 0);", address, strconv.Quote(function_symbol.Comment))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
72
go/profile/info.go
Normal file
72
go/profile/info.go
Normal file
|
|
@ -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
|
||||||
|
}
|
||||||
|
|
@ -10,6 +10,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type Profile struct {
|
type Profile struct {
|
||||||
|
Info Info
|
||||||
Directory string
|
Directory string
|
||||||
SymbolTable *symfile.InMemoryTable
|
SymbolTable *symfile.InMemoryTable
|
||||||
}
|
}
|
||||||
|
|
@ -29,6 +30,11 @@ func Open(profile_directory string) (profile *Profile, err error) {
|
||||||
fmt.Println("Opening profile", profile_directory)
|
fmt.Println("Opening profile", profile_directory)
|
||||||
|
|
||||||
profile = new(Profile)
|
profile = new(Profile)
|
||||||
|
|
||||||
|
if err = read_info(filepath.Join(profile_directory, "info.json"), &profile.Info); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
profile.Directory = profile_directory
|
profile.Directory = profile_directory
|
||||||
|
|
||||||
path_to_symbols_file := filepath.Join(profile_directory, "symbol", "main.sym")
|
path_to_symbols_file := filepath.Join(profile_directory, "symbol", "main.sym")
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
package profile
|
package profile
|
||||||
|
|
||||||
func (profile *Profile) CreateX64dbgFiles(module_name string, base_address uint64) (err error) {
|
func (profile *Profile) CreateX64dbgFiles(compress_db bool) (err error) {
|
||||||
if err = profile.generate_x64dbg_database(module_name, base_address); err != nil {
|
if err = profile.generate_x64dbg_database(compress_db); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -12,8 +12,14 @@ func hex_address(u uint64) string {
|
||||||
return fmt.Sprintf("0x%x", u)
|
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
|
// 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
|
var dd x64dbg.Database
|
||||||
|
|
||||||
for _, entry := range profile.SymbolTable.Entries {
|
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
|
// save database
|
||||||
dd_path := filepath.Join(profile.Directory, "x32dbg", "game.dd32")
|
dd_path := filepath.Join(profile.Directory, "x64dbg", filename)
|
||||||
if err = x64dbg.SaveDatabase(dd_path, &dd); err != nil {
|
if err = x64dbg.SaveDatabase(dd_path, &dd, compress); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -101,7 +101,7 @@ loop:
|
||||||
func (profile *Profile) generate_x64dbg_types() (err error) {
|
func (profile *Profile) generate_x64dbg_types() (err error) {
|
||||||
// parse C headers
|
// parse C headers
|
||||||
var cc_config cc.Config
|
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 {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
@ -231,7 +231,7 @@ func (profile *Profile) generate_x64dbg_types() (err error) {
|
||||||
x64_types.Structs = append(x64_types.Structs, x64_struct)
|
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)
|
err = x64dbg.SortTypes(&x64_types)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
31
go/x64dbg/compress.go
Normal file
31
go/x64dbg/compress.go
Normal file
|
|
@ -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
|
||||||
|
}
|
||||||
|
|
@ -2,6 +2,7 @@ package x64dbg
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"io"
|
||||||
"os"
|
"os"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -59,19 +60,37 @@ type Database struct {
|
||||||
Breakpoints []Breakpoint `json:"breakpoints,omitempty"`
|
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
|
var file *os.File
|
||||||
file, err = os.Create(name)
|
file, err = os.Create(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
e := json.NewEncoder(file)
|
var writecloser io.WriteCloser = file
|
||||||
e.SetIndent("", " ")
|
|
||||||
|
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 {
|
if err = e.Encode(database); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
err = file.Close()
|
if err = writecloser.Close(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if compress {
|
||||||
|
err = file.Close()
|
||||||
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue