mirror of
https://github.com/thunderbrewhq/binana.git
synced 2025-12-12 09:52:28 +00:00
feat(go): x64dbg can't parse types correctly without being in the correct order, but we can use Kahn topological sorting to avoid issues
This commit is contained in:
parent
df04015c59
commit
b6fb39c844
12 changed files with 475 additions and 35 deletions
17
go/x64dbg/alias_type.go
Normal file
17
go/x64dbg/alias_type.go
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
package x64dbg
|
||||
|
||||
type AliasType struct {
|
||||
Type string `json:"type"`
|
||||
Name string `json:"name"`
|
||||
ArraySize int32 `json:"arrsize,omitempty"`
|
||||
}
|
||||
|
||||
func (alias *AliasType) GetName() string {
|
||||
return alias.Name
|
||||
}
|
||||
|
||||
func (alias *AliasType) Dependencies() []string {
|
||||
return []string{
|
||||
alias.Type,
|
||||
}
|
||||
}
|
||||
23
go/x64dbg/function_type.go
Normal file
23
go/x64dbg/function_type.go
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
package x64dbg
|
||||
|
||||
type FunctionType struct {
|
||||
ReturnType string `json:"rettype"`
|
||||
CallConvention string `json:"callconv"`
|
||||
NoReturn bool `json:"noreturn"`
|
||||
Name string `json:"name"`
|
||||
Arguments []AliasType `json:"arguments,omitempty"`
|
||||
}
|
||||
|
||||
func (function *FunctionType) GetName() string {
|
||||
return function.Name
|
||||
}
|
||||
|
||||
func (function *FunctionType) Dependencies() (s []string) {
|
||||
if function.ReturnType != "" {
|
||||
s = append(s, function.ReturnType)
|
||||
}
|
||||
for _, t := range function.Arguments {
|
||||
s = append(s, t.Type)
|
||||
}
|
||||
return
|
||||
}
|
||||
17
go/x64dbg/sort_types.go
Normal file
17
go/x64dbg/sort_types.go
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
package x64dbg
|
||||
|
||||
func SortTypes(types *Types) (err error) {
|
||||
graph := new_type_dependency_graph()
|
||||
if err = graph.Load(types); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
var sorted *Types
|
||||
sorted, err = graph.Save()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
*types = *sorted
|
||||
return
|
||||
}
|
||||
26
go/x64dbg/struct_type.go
Normal file
26
go/x64dbg/struct_type.go
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
package x64dbg
|
||||
|
||||
type StructMemberType struct {
|
||||
Type string `json:"type"`
|
||||
Name string `json:"name"`
|
||||
ArraySize int32 `json:"arrsize,omitempty"`
|
||||
Offset int32 `json:"offset"`
|
||||
}
|
||||
|
||||
type StructType struct {
|
||||
Name string `json:"name"`
|
||||
Size int32 `json:"size"`
|
||||
Members []StructMemberType `json:"members,omitempty"`
|
||||
}
|
||||
|
||||
func (struct_ *StructType) GetName() string {
|
||||
return struct_.Name
|
||||
}
|
||||
|
||||
func (struct_ *StructType) Dependencies() (s []string) {
|
||||
for _, member := range struct_.Members {
|
||||
s = append(s, member.Type)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
257
go/x64dbg/type_dependency_graph.go
Normal file
257
go/x64dbg/type_dependency_graph.go
Normal file
|
|
@ -0,0 +1,257 @@
|
|||
package x64dbg
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"slices"
|
||||
"sort"
|
||||
)
|
||||
|
||||
type type_dependency_graph struct {
|
||||
all_types map[string]*type_graph_node
|
||||
}
|
||||
|
||||
func (graph *type_dependency_graph) get(name string) *type_graph_node {
|
||||
return graph.all_types[name]
|
||||
}
|
||||
|
||||
func new_type_dependency_graph() *type_dependency_graph {
|
||||
t := new(type_dependency_graph)
|
||||
t.all_types = make(map[string]*type_graph_node)
|
||||
return t
|
||||
}
|
||||
|
||||
func new_type_graph_node() *type_graph_node {
|
||||
t := new(type_graph_node)
|
||||
return t
|
||||
}
|
||||
|
||||
func (graph *type_dependency_graph) get_node_dependencies(t *type_graph_node) (nodes []*type_graph_node, err error) {
|
||||
nodes = t.depends_on
|
||||
return
|
||||
}
|
||||
|
||||
func (graph *type_dependency_graph) check_sub_dependency_cycle(root_node, sub_node *type_graph_node) (err error) {
|
||||
var sub_node_deps []*type_graph_node
|
||||
sub_node_deps, err = graph.get_node_dependencies(sub_node)
|
||||
for _, dependency_node := range sub_node_deps {
|
||||
if dependency_node == root_node {
|
||||
return fmt.Errorf("cycle detected with %s", root_node.t.GetName())
|
||||
}
|
||||
|
||||
// recursively check for deeper dependency cycles
|
||||
if err = graph.check_sub_dependency_cycle(root_node, dependency_node); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (graph *type_dependency_graph) check_root_dependency_cycle(node *type_graph_node) (err error) {
|
||||
var node_deps []*type_graph_node
|
||||
node_deps, err = graph.get_node_dependencies(node)
|
||||
for _, dependency_node := range node_deps {
|
||||
// check for obvious self-referential
|
||||
if dependency_node == node {
|
||||
return fmt.Errorf("immediate %s->%s self-reference", node.t.GetName(), node.t.GetName())
|
||||
}
|
||||
|
||||
// recursively check for deeper dependency cycles
|
||||
if err = graph.check_sub_dependency_cycle(node, dependency_node); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (graph *type_dependency_graph) remove_edge(dependency, dependent *type_graph_node) (err error) {
|
||||
dependent_index := slices.Index(dependent.depends_on, dependency)
|
||||
if dependent_index == -1 {
|
||||
err = fmt.Errorf("dependency %s not found in dependent %s", dependency.t.GetName(), dependent.t.GetName())
|
||||
return
|
||||
}
|
||||
|
||||
dependency_index := slices.Index(dependency.is_depended_on_by, dependent)
|
||||
|
||||
if dependency_index == -1 {
|
||||
err = fmt.Errorf("dependent %s not found in dependency %s", dependent.t.GetName(), dependency.t.GetName())
|
||||
return
|
||||
}
|
||||
|
||||
dependent.depends_on = slices.Delete(dependent.depends_on, dependent_index, dependent_index+1)
|
||||
dependency.is_depended_on_by = slices.Delete(dependency.is_depended_on_by, dependency_index, dependency_index+1)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (graph *type_dependency_graph) Load(types *Types) (err error) {
|
||||
// combine all types into an array
|
||||
var all_types []*type_graph_node
|
||||
|
||||
type_exists := map[string]bool{}
|
||||
|
||||
for _, alias_type := range types.Types {
|
||||
t := new_type_graph_node()
|
||||
at := alias_type
|
||||
t.t = &at
|
||||
|
||||
if !type_exists[t.String()] {
|
||||
type_exists[t.String()] = true
|
||||
all_types = append(all_types, t)
|
||||
}
|
||||
}
|
||||
|
||||
for _, struct_type := range types.Structs {
|
||||
t := new_type_graph_node()
|
||||
st := struct_type
|
||||
t.t = &st
|
||||
|
||||
if !type_exists[t.String()] {
|
||||
type_exists[t.String()] = true
|
||||
all_types = append(all_types, t)
|
||||
}
|
||||
}
|
||||
|
||||
for _, union_type := range types.Unions {
|
||||
t := new_type_graph_node()
|
||||
ut := union_type
|
||||
t.t = &ut
|
||||
|
||||
if !type_exists[t.String()] {
|
||||
type_exists[t.String()] = true
|
||||
all_types = append(all_types, t)
|
||||
}
|
||||
}
|
||||
|
||||
for _, func_type := range types.Functions {
|
||||
t := new_type_graph_node()
|
||||
ft := func_type
|
||||
t.t = &ft
|
||||
|
||||
if !type_exists[t.String()] {
|
||||
type_exists[t.String()] = true
|
||||
all_types = append(all_types, t)
|
||||
}
|
||||
}
|
||||
|
||||
// load types into map
|
||||
graph.all_types = make(map[string]*type_graph_node)
|
||||
for _, t := range all_types {
|
||||
graph.all_types[t.t.GetName()] = t
|
||||
}
|
||||
|
||||
// build graph
|
||||
for _, t := range all_types {
|
||||
for _, dependency_name := range t.t.Dependencies() {
|
||||
if is_type_name_builtin(dependency_name) {
|
||||
continue
|
||||
}
|
||||
|
||||
dependency_type := graph.get(dependency_name)
|
||||
if dependency_type == nil {
|
||||
err = fmt.Errorf("unknown dependency name %s", dependency_name)
|
||||
return
|
||||
}
|
||||
|
||||
if !slices.Contains(t.depends_on, dependency_type) {
|
||||
t.depends_on = append(t.depends_on, dependency_type)
|
||||
}
|
||||
|
||||
if !slices.Contains(dependency_type.is_depended_on_by, t) {
|
||||
dependency_type.is_depended_on_by = append(dependency_type.is_depended_on_by, t)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// check for cycles
|
||||
for _, t := range all_types {
|
||||
if err = graph.check_root_dependency_cycle(t); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
type type_dependency_sorter struct {
|
||||
graph *type_dependency_graph
|
||||
type_names []string
|
||||
}
|
||||
|
||||
func new_type_dependency_sorter(graph *type_dependency_graph) *type_dependency_sorter {
|
||||
sorter := new(type_dependency_sorter)
|
||||
sorter.graph = graph
|
||||
for k := range sorter.graph.all_types {
|
||||
sorter.type_names = append(sorter.type_names, k)
|
||||
}
|
||||
sort.Strings(sorter.type_names)
|
||||
return sorter
|
||||
}
|
||||
|
||||
func (sorter *type_dependency_sorter) sort() (sorted []*type_graph_node, err error) {
|
||||
// first, peel off types with no dependencies.
|
||||
var s []*type_graph_node
|
||||
var l []*type_graph_node
|
||||
|
||||
for _, type_name := range sorter.type_names {
|
||||
node := sorter.graph.all_types[type_name]
|
||||
//
|
||||
if len(node.depends_on) == 0 {
|
||||
s = append(s, node)
|
||||
}
|
||||
}
|
||||
|
||||
for len(s) != 0 {
|
||||
n := s[0]
|
||||
s = s[1:]
|
||||
|
||||
l = append(l, n)
|
||||
|
||||
n_dependents := make([]*type_graph_node, len(n.is_depended_on_by))
|
||||
copy(n_dependents, n.is_depended_on_by)
|
||||
for i := range n_dependents {
|
||||
m := n_dependents[i]
|
||||
|
||||
if err = sorter.graph.remove_edge(n, m); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if len(m.depends_on) == 0 {
|
||||
s = append(s, m)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sorted = l
|
||||
return
|
||||
}
|
||||
|
||||
func (graph *type_dependency_graph) Save() (types *Types, err error) {
|
||||
sorter := new_type_dependency_sorter(graph)
|
||||
|
||||
var sorted []*type_graph_node
|
||||
sorted, err = sorter.sort()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
types = new(Types)
|
||||
|
||||
for _, t := range sorted {
|
||||
switch xt := t.t.(type) {
|
||||
case *AliasType:
|
||||
types.Types = append(types.Types, *xt)
|
||||
case *StructType:
|
||||
types.Structs = append(types.Structs, *xt)
|
||||
case *UnionType:
|
||||
types.Unions = append(types.Unions, *xt)
|
||||
case *FunctionType:
|
||||
types.Functions = append(types.Functions, *xt)
|
||||
default:
|
||||
panic(t)
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
|
||||
}
|
||||
51
go/x64dbg/type_node.go
Normal file
51
go/x64dbg/type_node.go
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
package x64dbg
|
||||
|
||||
import (
|
||||
"slices"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type type_graph_set []*type_graph_node
|
||||
|
||||
type graph_type interface {
|
||||
GetName() string
|
||||
Dependencies() []string
|
||||
}
|
||||
|
||||
type type_graph_node struct {
|
||||
t graph_type
|
||||
depends_on type_graph_set
|
||||
is_depended_on_by type_graph_set
|
||||
}
|
||||
|
||||
func (t *type_graph_node) String() string {
|
||||
return t.t.GetName()
|
||||
}
|
||||
|
||||
var builtins = []string{
|
||||
"char",
|
||||
"bool",
|
||||
"long",
|
||||
"short",
|
||||
"long long",
|
||||
"int",
|
||||
"float",
|
||||
"double",
|
||||
"char*",
|
||||
"void*",
|
||||
"int8_t",
|
||||
"int16_t",
|
||||
"int32_t",
|
||||
"int64_t",
|
||||
"uint8_t",
|
||||
"uint16_t",
|
||||
"uint32_t",
|
||||
"uint64_t",
|
||||
}
|
||||
|
||||
func is_type_name_builtin(t string) bool {
|
||||
if strings.HasSuffix(t, "*") {
|
||||
return true
|
||||
}
|
||||
return slices.Contains(builtins, t)
|
||||
}
|
||||
|
|
@ -5,39 +5,9 @@ import (
|
|||
"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"`
|
||||
}
|
||||
|
||||
// Describes the format of an x64dbg type information file
|
||||
type Types struct {
|
||||
Types []Type `json:"types,omitempty"`
|
||||
Types []AliasType `json:"types,omitempty"`
|
||||
Structs []StructType `json:"structs,omitempty"`
|
||||
Unions []UnionType `json:"unions,omitempty"`
|
||||
Functions []FunctionType `json:"functions,omitempty"`
|
||||
|
|
@ -59,3 +29,21 @@ func SaveTypes(name string, types *Types) (err error) {
|
|||
err = file.Close()
|
||||
return
|
||||
}
|
||||
|
||||
func LoadTypes(name string) (types *Types, err error) {
|
||||
var file *os.File
|
||||
file, err = os.Open(name)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
types = new(Types)
|
||||
|
||||
e := json.NewDecoder(file)
|
||||
if err = e.Decode(types); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err = file.Close()
|
||||
return
|
||||
}
|
||||
|
|
|
|||
18
go/x64dbg/union_type.go
Normal file
18
go/x64dbg/union_type.go
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
package x64dbg
|
||||
|
||||
type UnionType struct {
|
||||
Name string `json:"name"`
|
||||
Members []AliasType `json:"members,omitempty"`
|
||||
}
|
||||
|
||||
func (union *UnionType) GetName() string {
|
||||
return union.Name
|
||||
}
|
||||
|
||||
func (union *UnionType) Dependencies() (s []string) {
|
||||
for _, member := range union.Members {
|
||||
s = append(s, member.Type)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue