mirror of
https://github.com/thunderbrewhq/binana.git
synced 2025-12-12 01:42:29 +00:00
feat(profile): binana x64dbg-gen now generates a types file from C source headers
This commit is contained in:
parent
e3ec21ecec
commit
5d5630a6cb
19 changed files with 2961 additions and 354 deletions
|
|
@ -1 +0,0 @@
|
||||||
CDataRecyler__Clear 0095CEC0 f end=0095CF39
|
|
||||||
1
3.3.5a/symbol/datarecycler/func.sym
Normal file
1
3.3.5a/symbol/datarecycler/func.sym
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
CDataRecycler__Clear 0095CEC0 f end=0095CF39
|
||||||
2206
3.3.5a/x32dbg/types.json
Normal file
2206
3.3.5a/x32dbg/types.json
Normal file
File diff suppressed because it is too large
Load diff
121
README.md
121
README.md
|
|
@ -1,56 +1,65 @@
|
||||||
# binana
|
# binana
|
||||||
|
|
||||||
This repository hosts some work related to studying the original game binaries.
|
This repository hosts some work related to studying the original game binaries.
|
||||||
|
|
||||||
You can use the information here to get a headstart when working on the [Whoa project](https://github.com/whoahq/whoa).
|
You can use the information here to get a headstart when working on the [Whoa project](https://github.com/whoahq/whoa).
|
||||||
|
|
||||||
# Header files
|
# Header files
|
||||||
|
|
||||||
To make reverse engineering easier, C header files matching the original memory layout are provided.
|
To make reverse engineering easier, C header files to match the original executable's memory layout are provided.
|
||||||
|
|
||||||
## Importing headers
|
# Symbol files
|
||||||
|
|
||||||
To use these headers in Ghidra,
|
Symbol files are text files that map addresses to functions and variables/data labels.
|
||||||
|
|
||||||
1. go to `File` > `Parse C Source...`.
|
In this repo, `script/compile-symbols` is used to concatenate our organized symbol files into one big file (`<game version>/symbol/main.sym`).
|
||||||
2. Select `clib.prf` as your parse configuration, and clear all source files and input paths.
|
|
||||||
3. Add the header `<game version>/include/main.h` to the `Source files to parse` combo box.
|
To refresh the gigantic `main.sym` file after changing one of the source symbol files:
|
||||||
4. Add the path to `<game version>/include` to the `Include paths` combo box.
|
|
||||||
5. press `Parse to Program`.
|
```bash
|
||||||
|
script/compile-symbols <game version>
|
||||||
TODO: equivalent guide for IDA Pro.
|
```
|
||||||
|
|
||||||
## Maintaining headers
|
# Loading information into Ghidra
|
||||||
|
|
||||||
Headers should be restricted at all times to the C programming language only. C++ syntax will break type analysis.
|
## Importing headers
|
||||||
|
|
||||||
To convert C++ namespaces, use double-underscores. For instance:
|
To import the main header file into your Ghidra project,
|
||||||
|
|
||||||
```cpp
|
1. go to `File` > `Parse C Source...`.
|
||||||
void Name::Space::Thing();
|
2. Select `clib.prf` as your parse configuration, and clear all source files and input paths.
|
||||||
```
|
3. Add the header `<game version>/include/main.h` to the `Source files to parse` combo box.
|
||||||
|
4. Add the path to `<game version>/include` to the `Include paths` combo box.
|
||||||
should become:
|
5. press `Parse to Program`.
|
||||||
|
|
||||||
```c
|
If all goes well, Data Type Manager will now contain the data structures from the headers.
|
||||||
void Name__Space__Thing();
|
|
||||||
```
|
## Importing symbols
|
||||||
|
|
||||||
# Symbol files
|
To import the symbol file into your Ghidra project,
|
||||||
|
|
||||||
Symbol files are text files that map function names and data labels to addresses.
|
1. go to `Window` > `Script Manager`
|
||||||
|
2. In the table view, lookup `ImportSymbolsScript.py`
|
||||||
In this repo, `script/compile-symbols` is used to concatenate our organized symbol files into one big file (`<game version>/symbol/main.sym`).
|
3. Run the script
|
||||||
|
4. Enter the path to `<game version>/symbol/main.sym`
|
||||||
To import this file to your Ghidra project,
|
|
||||||
|
# Loading information into x64dbg (x32dbg)
|
||||||
1. go to `Window` > `Script Manager`
|
|
||||||
2. In the table view, lookup `ImportSymbolsScript.py`
|
For ease of debugging, we provide x64dbg databases (generated by the Go tool from symbol maps), as well as x64dbg type information (generated by the same Go tool from the C headers).
|
||||||
3. Run the script
|
|
||||||
4. Enter the path to `<game version>/symbol/main.sym`
|
## Importing database
|
||||||
|
|
||||||
To refresh the `main.sym` file after changing one of the constituent symbol files:
|
To load the database information into x64dbg:
|
||||||
|
|
||||||
```bash
|
1. Open x96dbg.exe or x32dbg.exe directly
|
||||||
script/compile-symbols <game version>
|
2. Load your game binary
|
||||||
```
|
3. Go to `File` > `Database` > `Import database`
|
||||||
|
4. Navigate to <game version>/x32dbg/game.dd32`.
|
||||||
|
|
||||||
|
## Importing types
|
||||||
|
|
||||||
|
To load the type information JSON file:
|
||||||
|
|
||||||
|
1. Open the binary in x32dbg.exe
|
||||||
|
2. in the console, type: `LoadTypes <full path to local binana repository>\x32dbg\types.json`
|
||||||
|
|
||||||
|
|
|
||||||
11
go.mod
11
go.mod
|
|
@ -2,10 +2,17 @@ module github.com/thunderbrewhq/binana
|
||||||
|
|
||||||
go 1.22.0
|
go 1.22.0
|
||||||
|
|
||||||
require github.com/spf13/cobra v1.8.1
|
require (
|
||||||
|
github.com/spf13/cobra v1.8.1
|
||||||
|
modernc.org/cc/v3 v3.41.0
|
||||||
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||||
github.com/pierrec/lz4/v4 v4.1.21 // indirect
|
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||||
github.com/spf13/pflag v1.0.5 // indirect
|
github.com/spf13/pflag v1.0.5 // indirect
|
||||||
|
lukechampine.com/uint128 v1.3.0 // indirect
|
||||||
|
modernc.org/mathutil v1.6.0 // indirect
|
||||||
|
modernc.org/strutil v1.2.0 // indirect
|
||||||
|
modernc.org/token v1.1.0 // indirect
|
||||||
)
|
)
|
||||||
|
|
|
||||||
18
go.sum
18
go.sum
|
|
@ -1,8 +1,12 @@
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||||
|
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||||
|
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||||
|
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||||
|
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||||
github.com/pierrec/lz4/v4 v4.1.21 h1:yOVMLb6qSIDP67pl/5F7RepeKYu/VmTyEXvuMI5d9mQ=
|
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
|
||||||
github.com/pierrec/lz4/v4 v4.1.21/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
|
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||||
github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM=
|
github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM=
|
||||||
github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y=
|
github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y=
|
||||||
|
|
@ -10,3 +14,13 @@ github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
lukechampine.com/uint128 v1.3.0 h1:cDdUVfRwDUDovz610ABgFD17nXD4/uDgVHl2sC3+sbo=
|
||||||
|
lukechampine.com/uint128 v1.3.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk=
|
||||||
|
modernc.org/cc/v3 v3.41.0 h1:QoR1Sn3YWlmA1T4vLaKZfawdVtSiGx8H+cEojbC7v1Q=
|
||||||
|
modernc.org/cc/v3 v3.41.0/go.mod h1:Ni4zjJYJ04CDOhG7dn640WGfwBzfE0ecX8TyMB0Fv0Y=
|
||||||
|
modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4=
|
||||||
|
modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo=
|
||||||
|
modernc.org/strutil v1.2.0 h1:agBi9dp1I+eOnxXeiZawM8F4LawKv4NzGWSaLfyeNZA=
|
||||||
|
modernc.org/strutil v1.2.0/go.mod h1:/mdcBmfOibveCTBxUl5B5l6W+TTH1FXPLHZE6bTosX0=
|
||||||
|
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
|
||||||
|
modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,6 @@ func init() {
|
||||||
x64dbg_gen.Flags().StringP("game", "g", "3.3.5a", "the game profile")
|
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("module-name", "m", "wow.exe", "the name of the module")
|
||||||
x64dbg_gen.Flags().StringP("base-address", "b", "00400000", "the base address of the module")
|
x64dbg_gen.Flags().StringP("base-address", "b", "00400000", "the base address of the module")
|
||||||
x64dbg_gen.Flags().BoolP("compress", "c", false, "enable/disable lz4 compression of the x64dbg database")
|
|
||||||
|
|
||||||
rootCmd.AddCommand(x64dbg_gen)
|
rootCmd.AddCommand(x64dbg_gen)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,117 +1,50 @@
|
||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"strconv"
|
||||||
"strconv"
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
"github.com/spf13/cobra"
|
"github.com/thunderbrewhq/binana/go/profile"
|
||||||
"github.com/thunderbrewhq/binana/go/symfile"
|
)
|
||||||
"github.com/thunderbrewhq/binana/go/x64dbg"
|
|
||||||
)
|
var x64dbg_gen = &cobra.Command{
|
||||||
|
Use: "x64dbg-gen",
|
||||||
func hex_address(u uint64) string {
|
Short: "Generate x64dbg database using a symfile",
|
||||||
return fmt.Sprintf("0x%x", u)
|
Run: x64dbg_gen_func,
|
||||||
}
|
}
|
||||||
|
|
||||||
// rootCmd represents the base command when called without any subcommands
|
func x64dbg_gen_func(cmd *cobra.Command, args []string) {
|
||||||
var x64dbg_gen = &cobra.Command{
|
// get command line arguments
|
||||||
Use: "x64dbg-gen",
|
module_name, err := cmd.Flags().GetString("module-name")
|
||||||
Short: "Generate x64dbg database using a symfile",
|
if err != nil {
|
||||||
Run: x64dbg_gen_func,
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func x64dbg_gen_func(cmd *cobra.Command, args []string) {
|
base_address_s, err := cmd.Flags().GetString("base-address")
|
||||||
compress, err := cmd.Flags().GetBool("compress")
|
if err != nil {
|
||||||
if err != nil {
|
panic(err)
|
||||||
panic(err)
|
}
|
||||||
}
|
|
||||||
|
// parse the base address of game EXE
|
||||||
module_name, err := cmd.Flags().GetString("module-name")
|
base_address, err := strconv.ParseUint(base_address_s, 16, 64)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
base_address_s, err := cmd.Flags().GetString("base-address")
|
game_profile_directory, err := cmd.Flags().GetString("game")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
base_address, err := strconv.ParseUint(base_address_s, 16, 64)
|
game_profile, err := profile.Open(game_profile_directory)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
game_profile, err := cmd.Flags().GetString("game")
|
if err = game_profile.CreateX64dbgFiles(module_name, base_address); err != nil {
|
||||||
if err != nil {
|
fmt.Println(err)
|
||||||
panic(err)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
symfile_path := filepath.Join(game_profile, "symbol", "main.sym")
|
|
||||||
|
|
||||||
file, err := os.Open(symfile_path)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
os.Exit(1)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
table := new(symfile.InMemoryTable)
|
|
||||||
|
|
||||||
if err := symfile.Load(table, file); err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
os.Exit(1)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
file.Close()
|
|
||||||
|
|
||||||
fmt.Printf("loaded %d symbols\n", table.Len())
|
|
||||||
|
|
||||||
var dd x64dbg.Database
|
|
||||||
|
|
||||||
for _, entry := range table.Entries {
|
|
||||||
relative_start_address := entry.StartAddress - base_address
|
|
||||||
relative_end_address := relative_start_address
|
|
||||||
|
|
||||||
if entry.EndAddress != 0 {
|
|
||||||
relative_end_address = entry.EndAddress - base_address
|
|
||||||
// for x64dbg, the end address is the last instruction.
|
|
||||||
// for us, the end address is the address immediately after the last instruction.
|
|
||||||
relative_end_address -= 1
|
|
||||||
}
|
|
||||||
|
|
||||||
if relative_end_address < relative_start_address || relative_end_address-relative_start_address >= 10000 {
|
|
||||||
fmt.Printf("Wtf symbol %s %08x %08x (offset %d)\n", entry.Name, relative_start_address, relative_end_address, relative_end_address-relative_start_address)
|
|
||||||
}
|
|
||||||
|
|
||||||
// create label
|
|
||||||
var label x64dbg.Label
|
|
||||||
label.Manual = true
|
|
||||||
label.Address = hex_address(relative_start_address)
|
|
||||||
label.Text = entry.Name
|
|
||||||
label.Module = module_name
|
|
||||||
dd.Labels = append(dd.Labels, label)
|
|
||||||
|
|
||||||
if entry.Kind == symfile.Function {
|
|
||||||
var fn x64dbg.Function
|
|
||||||
fn.Manual = true
|
|
||||||
fn.Start = hex_address(relative_start_address)
|
|
||||||
fn.End = hex_address(relative_end_address)
|
|
||||||
|
|
||||||
fn.Module = module_name
|
|
||||||
|
|
||||||
fn.InstructionCount = hex_address(0)
|
|
||||||
fn.Parent = hex_address(relative_start_address)
|
|
||||||
|
|
||||||
dd.Functions = append(dd.Functions, fn)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
dd_path := filepath.Join(game_profile, "x32dbg", "game.dd32")
|
|
||||||
if err = x64dbg.SaveDatabase(dd_path, &dd, compress); err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
6
go/profile/include/stdbool.h
Normal file
6
go/profile/include/stdbool.h
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
#ifndef SYSTEM_STD_BOOL_H
|
||||||
|
#define SYSTEM_STD_BOOL_H
|
||||||
|
|
||||||
|
typedef char bool;
|
||||||
|
|
||||||
|
#endif
|
||||||
17
go/profile/include/stdint.h
Normal file
17
go/profile/include/stdint.h
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
#ifndef SYSTEM_STD_INT_H
|
||||||
|
#define SYSTEM_STD_INT_H
|
||||||
|
|
||||||
|
typedef signed char int8_t;
|
||||||
|
typedef short int16_t;
|
||||||
|
typedef int int32_t;
|
||||||
|
typedef long long int64_t;
|
||||||
|
typedef unsigned char uint8_t;
|
||||||
|
typedef unsigned short uint16_t;
|
||||||
|
typedef unsigned int uint32_t;
|
||||||
|
typedef unsigned long long uint64_t;
|
||||||
|
|
||||||
|
typedef int32_t ptrdiff_t;
|
||||||
|
typedef uint32_t uintptr_t;
|
||||||
|
typedef int32_t intptr_t;
|
||||||
|
|
||||||
|
#endif
|
||||||
51
go/profile/open.go
Normal file
51
go/profile/open.go
Normal file
|
|
@ -0,0 +1,51 @@
|
||||||
|
package profile
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io/fs"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/thunderbrewhq/binana/go/symfile"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Profile struct {
|
||||||
|
Directory string
|
||||||
|
SymbolTable *symfile.InMemoryTable
|
||||||
|
}
|
||||||
|
|
||||||
|
func Open(profile_directory string) (profile *Profile, err error) {
|
||||||
|
var dir fs.FileInfo
|
||||||
|
dir, err = os.Stat(profile_directory)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !dir.IsDir() {
|
||||||
|
err = fmt.Errorf("profile: game profile is not a directory")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println("Opening profile", profile_directory)
|
||||||
|
|
||||||
|
profile = new(Profile)
|
||||||
|
profile.Directory = profile_directory
|
||||||
|
|
||||||
|
path_to_symbols_file := filepath.Join(profile_directory, "symbol", "main.sym")
|
||||||
|
var symbols_file *os.File
|
||||||
|
symbols_file, err = os.Open(path_to_symbols_file)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
profile.SymbolTable = new(symfile.InMemoryTable)
|
||||||
|
|
||||||
|
if err = symfile.Load(profile.SymbolTable, symbols_file); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
symbols_file.Close()
|
||||||
|
|
||||||
|
//
|
||||||
|
return
|
||||||
|
}
|
||||||
13
go/profile/x64dbg.go
Normal file
13
go/profile/x64dbg.go
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
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 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = profile.generate_x64dbg_types(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
66
go/profile/x64dbg_generate_database.go
Normal file
66
go/profile/x64dbg_generate_database.go
Normal file
|
|
@ -0,0 +1,66 @@
|
||||||
|
package profile
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/thunderbrewhq/binana/go/symfile"
|
||||||
|
"github.com/thunderbrewhq/binana/go/x64dbg"
|
||||||
|
)
|
||||||
|
|
||||||
|
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) {
|
||||||
|
// Convert symbol table into x64dbg database
|
||||||
|
var dd x64dbg.Database
|
||||||
|
|
||||||
|
for _, entry := range profile.SymbolTable.Entries {
|
||||||
|
relative_start_address := entry.StartAddress - base_address
|
||||||
|
relative_end_address := relative_start_address
|
||||||
|
|
||||||
|
if entry.EndAddress != 0 {
|
||||||
|
relative_end_address = entry.EndAddress - base_address
|
||||||
|
// for x64dbg, the end address is the last instruction.
|
||||||
|
// for us, the end address is the address immediately after the last instruction.
|
||||||
|
relative_end_address -= 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if relative_end_address < relative_start_address || relative_end_address-relative_start_address >= 10000 {
|
||||||
|
fmt.Printf("Strange symbol %s %08x %08x (offset %d)\n", entry.Name, relative_start_address, relative_end_address, relative_end_address-relative_start_address)
|
||||||
|
}
|
||||||
|
|
||||||
|
// create label
|
||||||
|
var label x64dbg.Label
|
||||||
|
label.Manual = true
|
||||||
|
label.Address = hex_address(relative_start_address)
|
||||||
|
label.Text = entry.Name
|
||||||
|
label.Module = module_name
|
||||||
|
dd.Labels = append(dd.Labels, label)
|
||||||
|
|
||||||
|
if entry.Kind == symfile.Function {
|
||||||
|
var fn x64dbg.Function
|
||||||
|
fn.Manual = true
|
||||||
|
fn.Start = hex_address(relative_start_address)
|
||||||
|
fn.End = hex_address(relative_end_address)
|
||||||
|
|
||||||
|
fn.Module = module_name
|
||||||
|
|
||||||
|
fn.InstructionCount = hex_address(0)
|
||||||
|
fn.Parent = hex_address(relative_start_address)
|
||||||
|
|
||||||
|
dd.Functions = append(dd.Functions, fn)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// save database
|
||||||
|
dd_path := filepath.Join(profile.Directory, "x32dbg", "game.dd32")
|
||||||
|
if err = x64dbg.SaveDatabase(dd_path, &dd); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println("database generated!", dd_path)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
236
go/profile/x64dbg_generate_types.go
Normal file
236
go/profile/x64dbg_generate_types.go
Normal file
|
|
@ -0,0 +1,236 @@
|
||||||
|
package profile
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"path/filepath"
|
||||||
|
"slices"
|
||||||
|
"sort"
|
||||||
|
|
||||||
|
"github.com/thunderbrewhq/binana/go/x64dbg"
|
||||||
|
"modernc.org/cc/v3"
|
||||||
|
)
|
||||||
|
|
||||||
|
func sort_string_ids(ids []cc.StringID) {
|
||||||
|
sort.Slice(ids, func(i, j int) bool {
|
||||||
|
return ids[i] < ids[j]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func cc_type_to_struct_member_type(t cc.Type) (m x64dbg.StructMemberType) {
|
||||||
|
var s string
|
||||||
|
var arrsize int32 = 1
|
||||||
|
var array bool
|
||||||
|
|
||||||
|
loop:
|
||||||
|
for t != nil {
|
||||||
|
if t.Name().String() != "" {
|
||||||
|
s = t.Name().String() + s
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
switch t.Kind() {
|
||||||
|
case cc.Ptr:
|
||||||
|
s = "*" + s
|
||||||
|
case cc.Array:
|
||||||
|
array = true
|
||||||
|
arrsize *= int32(t.Len())
|
||||||
|
case cc.Function:
|
||||||
|
s = "void*" + s
|
||||||
|
break loop
|
||||||
|
default:
|
||||||
|
s = t.Kind().String() + s
|
||||||
|
break loop
|
||||||
|
}
|
||||||
|
|
||||||
|
t = t.Elem()
|
||||||
|
}
|
||||||
|
|
||||||
|
if array {
|
||||||
|
m.ArraySize = arrsize
|
||||||
|
}
|
||||||
|
|
||||||
|
m.Offset = -1
|
||||||
|
m.Type = s
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func cc_type_to_typedef(t cc.Type) (m x64dbg.Type) {
|
||||||
|
var s string
|
||||||
|
var arrsize int32 = 1
|
||||||
|
var array bool
|
||||||
|
|
||||||
|
loop:
|
||||||
|
for t != nil {
|
||||||
|
|
||||||
|
if t.Name().String() != "" && t.Kind() != cc.Enum {
|
||||||
|
s = t.Name().String() + s
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
switch t.Kind() {
|
||||||
|
case cc.Enum:
|
||||||
|
s = t.EnumType().String() + s
|
||||||
|
break loop
|
||||||
|
case cc.Ptr:
|
||||||
|
s = "*" + s
|
||||||
|
case cc.Array:
|
||||||
|
array = true
|
||||||
|
arrsize *= int32(t.Len())
|
||||||
|
case cc.Function:
|
||||||
|
s = "void*" + s
|
||||||
|
default:
|
||||||
|
s = t.Kind().String() + s
|
||||||
|
break loop
|
||||||
|
}
|
||||||
|
|
||||||
|
t = t.Elem()
|
||||||
|
}
|
||||||
|
|
||||||
|
if array {
|
||||||
|
m.ArraySize = arrsize
|
||||||
|
}
|
||||||
|
|
||||||
|
m.Type = s
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// parses the C headers and generates a matching x64dbg types.json file
|
||||||
|
func (profile *Profile) generate_x64dbg_types() (err error) {
|
||||||
|
// parse C headers
|
||||||
|
var cc_config cc.Config
|
||||||
|
cc_config.ABI, err = cc.NewABI("windows", "386")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
cc_include_paths := []string{
|
||||||
|
filepath.Join(profile.Directory, "include"),
|
||||||
|
}
|
||||||
|
|
||||||
|
cc_system_include_paths := []string{
|
||||||
|
filepath.Join("go", "profile", "include"),
|
||||||
|
}
|
||||||
|
|
||||||
|
cc_sources := []cc.Source{
|
||||||
|
cc.Source{
|
||||||
|
Name: filepath.Join(profile.Directory, "include", "main.h"),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var ast *cc.AST
|
||||||
|
ast, err = cc.Translate(&cc_config, cc_include_paths, cc_system_include_paths, cc_sources)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var x64_types x64dbg.Types
|
||||||
|
|
||||||
|
// Parse integer types
|
||||||
|
var scope_ids []cc.StringID
|
||||||
|
for name := range ast.Scope {
|
||||||
|
scope_ids = append(scope_ids, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
sort_string_ids(scope_ids)
|
||||||
|
|
||||||
|
// these types needed by the C parser can't be included in x64dbg json
|
||||||
|
ignore_types := []string{
|
||||||
|
"int8_t",
|
||||||
|
"int16_t",
|
||||||
|
"int32_t",
|
||||||
|
"int64_t",
|
||||||
|
|
||||||
|
"uint8_t",
|
||||||
|
"uint16_t",
|
||||||
|
"uint32_t",
|
||||||
|
"uint64_t",
|
||||||
|
|
||||||
|
"bool",
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, scope_id := range scope_ids {
|
||||||
|
scope := ast.Scope[scope_id]
|
||||||
|
for _, node := range scope {
|
||||||
|
if declarator, ok := node.(*cc.Declarator); ok {
|
||||||
|
if declarator.IsTypedefName {
|
||||||
|
if declarator.Type().Kind() != cc.Struct {
|
||||||
|
var x64_type x64dbg.Type = cc_type_to_typedef(declarator.Type())
|
||||||
|
x64_type.Name = scope_id.String()
|
||||||
|
if !slices.Contains(ignore_types, x64_type.Name) {
|
||||||
|
x64_types.Types = append(x64_types.Types, x64_type)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var struct_names []cc.StringID
|
||||||
|
for struct_name := range ast.StructTypes {
|
||||||
|
struct_names = append(struct_names, struct_name)
|
||||||
|
}
|
||||||
|
sort.Slice(struct_names, func(i, j int) bool {
|
||||||
|
return struct_names[i] < struct_names[j]
|
||||||
|
})
|
||||||
|
|
||||||
|
for _, struct_name := range struct_names {
|
||||||
|
struct_type := ast.StructTypes[struct_name]
|
||||||
|
|
||||||
|
var x64_struct x64dbg.StructType
|
||||||
|
x64_struct.Name = struct_name.String()
|
||||||
|
|
||||||
|
for i := range struct_type.NumField() {
|
||||||
|
struct_member := struct_type.FieldByIndex([]int{i})
|
||||||
|
|
||||||
|
// If struct member is a union
|
||||||
|
if struct_member.Type().Kind() == cc.Union {
|
||||||
|
union_member_name := struct_member.Name().String()
|
||||||
|
if union_member_name == "" {
|
||||||
|
// union is anonymous
|
||||||
|
union_member_name = fmt.Sprintf("%04d", struct_member.Offset())
|
||||||
|
}
|
||||||
|
|
||||||
|
union_type_name := fmt.Sprintf("%s__%s", struct_name.String(), union_member_name)
|
||||||
|
|
||||||
|
var x64_union x64dbg.UnionType
|
||||||
|
x64_union.Name = union_type_name
|
||||||
|
|
||||||
|
for i := range struct_member.Type().NumField() {
|
||||||
|
union_field := struct_member.Type().FieldByIndex([]int{i})
|
||||||
|
var x64_union_member x64dbg.Type = cc_type_to_typedef(union_field.Type())
|
||||||
|
x64_union_member.Name = union_field.Name().String()
|
||||||
|
x64_union.Members = append(x64_union.Members, x64_union_member)
|
||||||
|
}
|
||||||
|
|
||||||
|
x64_types.Unions = append(x64_types.Unions, x64_union)
|
||||||
|
|
||||||
|
x64_struct_member := x64dbg.StructMemberType{}
|
||||||
|
x64_struct_member.Type = union_type_name
|
||||||
|
x64_struct_member.Name = struct_member.Name().String()
|
||||||
|
x64_struct_member.Offset = int32(struct_member.Offset())
|
||||||
|
x64_struct.Members = append(x64_struct.Members, x64_struct_member)
|
||||||
|
} else {
|
||||||
|
|
||||||
|
x64_struct_member := cc_type_to_struct_member_type(struct_member.Type())
|
||||||
|
x64_struct_member.Name = struct_member.Name().String()
|
||||||
|
x64_struct_member.Offset = int32(struct_member.Offset())
|
||||||
|
x64_struct.Members = append(x64_struct.Members, x64_struct_member)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
x64_types.Structs = append(x64_types.Structs, x64_struct)
|
||||||
|
}
|
||||||
|
|
||||||
|
types_file_path := filepath.Join(profile.Directory, "x32dbg", "types.json")
|
||||||
|
|
||||||
|
err = x64dbg.SaveTypes(types_file_path, &x64_types)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println("types generated!", types_file_path)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
@ -1,29 +1,33 @@
|
||||||
package symfile
|
package symfile
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"slices"
|
"slices"
|
||||||
"sort"
|
"sort"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Most tables are reasonably-sized and can be kept in memory
|
// Most tables are reasonably-sized and can be kept in memory
|
||||||
type InMemoryTable struct {
|
type InMemoryTable struct {
|
||||||
Entries []Entry
|
Entries []Entry
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *InMemoryTable) Insert(entry *Entry) (err error) {
|
func (t *InMemoryTable) Insert(entry *Entry) (err error) {
|
||||||
i := sort.Search(len(t.Entries), func(i int) bool {
|
i := sort.Search(len(t.Entries), func(i int) bool {
|
||||||
return t.Entries[i].StartAddress >= entry.StartAddress
|
return t.Entries[i].StartAddress >= entry.StartAddress
|
||||||
})
|
})
|
||||||
|
|
||||||
if i < len(t.Entries) {
|
if i < len(t.Entries) {
|
||||||
t.Entries = slices.Insert(t.Entries, i, *entry)
|
t.Entries = slices.Insert(t.Entries, i, *entry)
|
||||||
} else {
|
} else {
|
||||||
t.Entries = append(t.Entries, *entry)
|
t.Entries = append(t.Entries, *entry)
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *InMemoryTable) Len() int {
|
func (t *InMemoryTable) Len() int {
|
||||||
return len(t.Entries)
|
return len(t.Entries)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func NewInMemoryTable() *InMemoryTable {
|
||||||
|
return new(InMemoryTable)
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,44 +1,43 @@
|
||||||
package symfile
|
package symfile
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"io"
|
"io"
|
||||||
)
|
)
|
||||||
|
|
||||||
// What kind of Entry is this?
|
// What kind of Entry is this?
|
||||||
type EntryKind uint8
|
type EntryKind uint8
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// Something that can be executed
|
// Something that can be executed
|
||||||
Function EntryKind = 'f'
|
Function EntryKind = 'f'
|
||||||
// Something that is read or written to
|
// Something that is read or written to
|
||||||
DataLabel EntryKind = 'l'
|
DataLabel EntryKind = 'l'
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
valid_kinds = []EntryKind{Function, DataLabel}
|
valid_kinds = []EntryKind{Function, DataLabel}
|
||||||
)
|
)
|
||||||
|
|
||||||
// An entry in the table
|
// An entry in the table
|
||||||
type Entry struct {
|
type Entry struct {
|
||||||
// Undecorated, raw name
|
// Undecorated, raw name
|
||||||
Name string
|
Name string
|
||||||
// Offset to the start of the function or data
|
// Offset to the start of the function or data
|
||||||
StartAddress uint64
|
StartAddress uint64
|
||||||
// What kind of Entry is this?
|
// What kind of Entry is this?
|
||||||
Kind EntryKind
|
Kind EntryKind
|
||||||
// Any table entry can have a comment after a ';' column
|
// Any table entry can have a comment after a ';' column
|
||||||
Comment string
|
Comment string
|
||||||
// Attributes
|
// Attributes
|
||||||
// end=AABBCCEEDD
|
// end=AABBCCEEDD
|
||||||
EndAddress uint64
|
EndAddress uint64
|
||||||
}
|
}
|
||||||
|
|
||||||
type Table interface {
|
type Table interface {
|
||||||
Insert(entry *Entry) (err error)
|
Insert(entry *Entry) (err error)
|
||||||
// Find() (iter func() (entry *Entry, err error))
|
}
|
||||||
}
|
|
||||||
|
func Load(table Table, text io.Reader) (err error) {
|
||||||
func Load(table Table, text io.Reader) (err error) {
|
err = load(text, table)
|
||||||
err = load(text, table)
|
return
|
||||||
return
|
}
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -1,91 +1,77 @@
|
||||||
package x64dbg
|
package x64dbg
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"io"
|
"os"
|
||||||
"os"
|
)
|
||||||
|
|
||||||
"github.com/pierrec/lz4/v4"
|
type Function struct {
|
||||||
)
|
Manual bool `json:"manual,omitempty"`
|
||||||
|
Start string `json:"start,omitempty"`
|
||||||
type Function struct {
|
End string `json:"end,omitempty"`
|
||||||
Manual bool `json:"manual,omitempty"`
|
Module string `json:"module,omitempty"`
|
||||||
Start string `json:"start,omitempty"`
|
InstructionCount string `json:"icount,omitempty"`
|
||||||
End string `json:"end,omitempty"`
|
Parent string `json:"parent,omitempty"`
|
||||||
Module string `json:"module,omitempty"`
|
}
|
||||||
InstructionCount string `json:"icount,omitempty"`
|
|
||||||
Parent string `json:"parent,omitempty"`
|
type Comment struct {
|
||||||
}
|
Manual bool `json:"manual,omitempty"`
|
||||||
|
Module string `json:"module,omitempty"`
|
||||||
type Comment struct {
|
Text string `json:"text,omitempty"`
|
||||||
Manual bool `json:"manual,omitempty"`
|
Address string `json:"address,omitempty"`
|
||||||
Module string `json:"module,omitempty"`
|
}
|
||||||
Text string `json:"text,omitempty"`
|
|
||||||
Address string `json:"address,omitempty"`
|
type Label struct {
|
||||||
}
|
Manual bool `json:"manual,omitempty"`
|
||||||
|
Module string `json:"module,omitempty"`
|
||||||
type Label struct {
|
Text string `json:"text,omitempty"`
|
||||||
Manual bool `json:"manual,omitempty"`
|
Address string `json:"address,omitempty"`
|
||||||
Module string `json:"module,omitempty"`
|
}
|
||||||
Text string `json:"text,omitempty"`
|
|
||||||
Address string `json:"address,omitempty"`
|
type Bookmark struct {
|
||||||
}
|
Manual bool `json:"manual,omitempty"`
|
||||||
|
Module string `json:"module,omitempty"`
|
||||||
type Bookmark struct {
|
Address string `json:"address,omitempty"`
|
||||||
Manual bool `json:"manual,omitempty"`
|
}
|
||||||
Module string `json:"module,omitempty"`
|
|
||||||
Address string `json:"address,omitempty"`
|
type Breakpoint struct {
|
||||||
}
|
Address string `json:"address,omitempty"`
|
||||||
|
CommandText string `json:"commandText,omitempty"`
|
||||||
type Breakpoint struct {
|
Enabled bool `json:"enabled,omitempty"`
|
||||||
Address string `json:"address,omitempty"`
|
FastResume string `json:"fastResume,omitempty"`
|
||||||
CommandText string `json:"commandText,omitempty"`
|
OldBytes string `json:"oldbytes,omitempty"`
|
||||||
Enabled bool `json:"enabled,omitempty"`
|
Type string `json:"type,omitempty"`
|
||||||
FastResume string `json:"fastResume,omitempty"`
|
Module string `json:"module,omitempty"`
|
||||||
OldBytes string `json:"oldbytes,omitempty"`
|
TitanType string `json:"titantype,omitempty"`
|
||||||
Type string `json:"type,omitempty"`
|
Name string `json:"name,omitempty"`
|
||||||
Module string `json:"module,omitempty"`
|
BreakCondition string `json:"breakCondition,omitempty"`
|
||||||
TitanType string `json:"titantype,omitempty"`
|
LogText string `json:"logText,omitempty"`
|
||||||
Name string `json:"name,omitempty"`
|
LogCondition string `json:"logCondition,omitempty"`
|
||||||
BreakCondition string `json:"breakCondition,omitempty"`
|
Silent string `json:"silent,omitempty"`
|
||||||
LogText string `json:"logText,omitempty"`
|
CommandCondition string `json:"commandCondition,omitempty"`
|
||||||
LogCondition string `json:"logCondition,omitempty"`
|
}
|
||||||
Silent string `json:"silent,omitempty"`
|
|
||||||
CommandCondition string `json:"commandCondition,omitempty"`
|
type Database struct {
|
||||||
}
|
Functions []Function `json:"functions,omitempty"`
|
||||||
|
Comments []Comment `json:"comments,omitempty"`
|
||||||
type Database struct {
|
Labels []Label `json:"labels,omitempty"`
|
||||||
Functions []Function `json:"functions,omitempty"`
|
Bookmarks []Bookmark `json:"bookmarks,omitempty"`
|
||||||
Comments []Comment `json:"comments,omitempty"`
|
Breakpoints []Breakpoint `json:"breakpoints,omitempty"`
|
||||||
Labels []Label `json:"labels,omitempty"`
|
}
|
||||||
Bookmarks []Bookmark `json:"bookmarks,omitempty"`
|
|
||||||
Breakpoints []Breakpoint `json:"breakpoints,omitempty"`
|
func SaveDatabase(name string, database *Database) (err error) {
|
||||||
}
|
var file *os.File
|
||||||
|
file, err = os.Create(name)
|
||||||
func SaveDatabase(name string, database *Database, compress bool) (err error) {
|
if err != nil {
|
||||||
var file *os.File
|
return
|
||||||
file, err = os.Create(name)
|
}
|
||||||
if err != nil {
|
|
||||||
return
|
e := json.NewEncoder(file)
|
||||||
}
|
e.SetIndent("", " ")
|
||||||
|
if err = e.Encode(database); err != nil {
|
||||||
var writecloser io.WriteCloser = file
|
return
|
||||||
|
}
|
||||||
if compress {
|
|
||||||
lz4_writer := lz4.NewWriter(file)
|
err = file.Close()
|
||||||
writecloser = lz4_writer
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
e := json.NewEncoder(writecloser)
|
|
||||||
e.SetIndent("", " ")
|
|
||||||
if err = e.Encode(database); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if compress {
|
|
||||||
writecloser.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
err = file.Close()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
|
||||||
61
go/x64dbg/types.go
Normal file
61
go/x64dbg/types.go
Normal file
|
|
@ -0,0 +1,61 @@
|
||||||
|
package x64dbg
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Type struct {
|
||||||
|
Type string `json:"type"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
ArraySize int32 `json:"arrsize,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type StructMemberType struct {
|
||||||
|
Type string `json:"type"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
ArraySize int32 `json:"arrsize,omitempty"`
|
||||||
|
Offset int32 `json:"offset,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type StructType struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Members []StructMemberType `json:"members,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type UnionType struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Members []Type `json:"members,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type FunctionType struct {
|
||||||
|
ReturnType string `json:"rettype"`
|
||||||
|
CallConvention string `json:"callconv"`
|
||||||
|
NoReturn bool `json:"noreturn"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Arguments []Type `json:"arguments,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Types struct {
|
||||||
|
Types []Type `json:"types,omitempty"`
|
||||||
|
Structs []StructType `json:"structs,omitempty"`
|
||||||
|
Unions []UnionType `json:"unions,omitempty"`
|
||||||
|
Functions []FunctionType `json:"functions,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func SaveTypes(name string, types *Types) (err error) {
|
||||||
|
var file *os.File
|
||||||
|
file, err = os.Create(name)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
e := json.NewEncoder(file)
|
||||||
|
e.SetIndent("", " ")
|
||||||
|
if err = e.Encode(types); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = file.Close()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
# this script just collects symbols into one file for convenience
|
# this script just collects symbols into one file for convenience
|
||||||
|
|
||||||
if [ $# -eq 0 ]
|
if [ $# -eq 0 ]
|
||||||
then
|
then
|
||||||
echo "Select a version to use"
|
echo "Select a version to use"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
awk 1 $1/symbol/*/*.sym | sort -k2 -t' ' > $1/symbol/main.sym
|
awk 1 $1/symbol/*/*.sym | sort -k2 -t' ' > $1/symbol/main.sym
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue