goblib

package module
v0.1.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 19, 2025 License: GPL-3.0 Imports: 7 Imported by: 0

README

goblib (beta)

goblib_mascot
goblib is a safe binary dependency analysis toolkit in Go.

It can be used in other Go programs to take in a binary and output the dependencies that the binary relies on, including the dependencies of its dependencies. In this way, a user can see full visibility of the binaries supply chain.

goblib uses Go's built in debug/elf package to parse ELF headers, so there's no chance of accidental binary execution like ldd (which was the initial starting point on how this project started....).

Why is this needed?

There was once a NVDIA CUDA container that my buddy, notthatguy, and I were deploying to run for hashcracking. We were using the GPU's for a single binary (hashcat) and the container was overkill at 8GB. To narrow it down, notthatguy used ldd to trace the system calls and get everything we needed to run on a minimal CUDA image. This was the result.

8GB -> 300MB ain't too bad.

This library is meant to help with tasks like that, and as a helper to a distroless tool we're currently building in stealth mode (coming soon!).

Installation

TODO

Usage

goblib builds a tree from a given binary path. In the initial beta it has only been tested with /bin/ls, unzip, hashcat, and vim-tiny.

Example usage

The main() example below takes in a binary path as the argument.

package main

import (
	"flag"
	"fmt"
	"os"
)

func main() {
	flag.Parse()
	if len(flag.Args()) < 1 {
		fmt.Fprintf(os.Stderr, "Usage: %s <binary>\n", os.Args[0])
		os.Exit(2)
	}

	rootPath := flag.Args()[0]

	tree := BuildTree(rootPath, EmptyTree())

	fmt.Println("\n**********PRINT TREE************\n")
	PrintFullTree(tree, "", map[string]bool{})

	fmt.Println("\n**********PRINT UNIQUE LIB DEPENDENCIES **************\n")
	lib_dependencies, err := tree.GetUniqueDependencies()
	if err != nil {
		// do nothing
	}
	fmt.Println(lib_dependencies)

	fmt.Println("\n**********GET NODES************\n")
	nodes := tree.GetNodes()
	fmt.Println(nodes)

	fmt.Println("\n**********GET INDIVIDUAL NODES************\n")
	for _, node := range nodes {
		if node.Metadata != nil {
			fmt.Printf("Path: %s\n", node.Path)
			fmt.Printf("Architecture: %s\n", node.Metadata.Architecture)
			fmt.Printf("DynamicLoader: %s\n", node.Metadata.DynamicLoader)
			fmt.Printf("SharedLibraries: %v\n", node.Metadata.SharedLibraries)
			fmt.Printf("RuntimeBinaries: %v\n", node.Metadata.RuntimeBinaries)
			fmt.Printf("Package: %v\n", node.Metadata.Package)
			fmt.Println()
		}
	}

	fmt.Println("\n**********GET DYNAMIC LOADER LIBS************\n")
	dls := tree.GetDynamicLoaders()
	fmt.Println(dls)

	fmt.Println("\n**********STATIC BINARY CHECK************\n")
	bin_found, _ := StaticBinaryCheck(rootPath)
	if bin_found != nil {
		print(bin_found)
	}
	fmt.Println("\n*********************\n")
	
	df := DebianFinder{}
	tree.UpdateMetaWithPackage(df)
	fmt.Println("\n**********GET INDIVIDUAL NODES AFTER FINDING PACKAGES************\n")
	for _, node := range nodes {
		if node.Metadata != nil {
			fmt.Printf("Path: %s\n", node.Path)
			fmt.Printf("Architecture: %s\n", node.Metadata.Architecture)
			fmt.Printf("DynamicLoader: %s\n", node.Metadata.DynamicLoader)
			fmt.Printf("SharedLibraries: %v\n", node.Metadata.SharedLibraries)
			fmt.Printf("RuntimeBinaries: %v\n", node.Metadata.RuntimeBinaries)
			fmt.Printf("Package: %v\n", node.Metadata.Package)
			fmt.Println()
		}
	}
}

What that looks like:

image

Contributions

Always welcome

TODO

  • Test with other binaries, i.e hashcat []
  • Link trees so that a list of binaries produces a forest of all dependencies needed by that list []
  • Error testing and edge cases []

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func EmptyTree

func EmptyTree() map[string]*Node

func PrintFullTree

func PrintFullTree(node *Node, indent string, seen map[string]bool)

func ResolveLib

func ResolveLib(name string) (string, error)

resolve library names to real file paths using ldconfig -p

func StaticBinaryCheck

func StaticBinaryCheck(path string) ([]string, error)

look for hardcoded executable paths inside the binary Need to update so that if it sees an exec() , etc

Types

type ApkFinder

type ApkFinder struct{}

func (ApkFinder) FindPackage

func (a ApkFinder) FindPackage(lib string) (string, error)

type BinaryMetadata

type BinaryMetadata struct {
	Architecture    string   // e.g., "x86-64", "ARM64"
	DynamicLoader   string   // e.g., "/lib64/ld-linux-x86-64.so.2"
	SharedLibraries []string // e.g., ["libpthread.so.0", "libdl.so.2", "libm.so.6", "libc.so.6"]
	RuntimeBinaries []string // e.g., ["tar", "unzip", "sh"] - TODO maybe put in path instead
	AbsPath         string   // e.g the binary location /usr/bin/ls
	Package         string   // e.g the package where the library can be installed
}

type DebianFinder

type DebianFinder struct{}

func (DebianFinder) FindPackage

func (d DebianFinder) FindPackage(lib string) (string, error)

type FedoraFinder

type FedoraFinder struct{}

func (FedoraFinder) FindPackage

func (r FedoraFinder) FindPackage(lib string) (string, error)

type Forest

type Forest []*Node // multiple bin support TODO

type Metadata

type Metadata interface {
}

Model for metadata information from a binary

type Node

type Node struct {
	Path     string  `json:"path"`
	Deps     []*Node `json:"deps,omitempty"`
	Metadata *BinaryMetadata
}

Model for Tree Node objects, this is the graph

func BuildTree

func BuildTree(bin_path string, visited map[string]*Node) *Node

func (*Node) GetDynamicLoaders

func (node *Node) GetDynamicLoaders() []string

Returns all the DynamicLoaders found in the tree

func (*Node) GetNodes

func (node *Node) GetNodes() []*Node

func (*Node) GetUniqueDependencies

func (node *Node) GetUniqueDependencies() ([]string, error)

Returns all the shared libraries for a binary (that was passed in as the root)

func (*Node) UpdateMetaWithPackage

func (node *Node) UpdateMetaWithPackage(finder PackageFinder)

Provide the option to update all metadata with the package that the lib is in

type PackageFinder

type PackageFinder interface {
	FindPackage(lib string) (string, error)
}

func DetectFinder

func DetectFinder() PackageFinder

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL