goot

module
v0.0.17 Latest Latest
Warning

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

Go to latest
Published: Sep 12, 2022 License: MIT

README

goot

What is goot?

goot is a static analysis framework for Go. goot is easy-to-learn, easy-to-use and highly extensible, allowing you to easily develop new analyses on top of it.

Currently, goot provides the following major analysis components (and more analyses are on the way):

  • Control/Data-flow analysis framework
    • Control-flow graph construction
    • Classic data-flow analyses, e.g., constant propagtion analysis, taint passthrough analysis
    • Your dataflow analyses

Get started

First intall goot by

go get -u github.com/cokeBeer/goot

Then you can copy examples from package cmd to your project
For example, copy cmd/constantpropagationanalysis

package main

import (
	"github.com/cokeBeer/goot/pkg/example/dataflow/constantpropagation"
)

const src = `package main

func Hello(a int, b int) bool {
	a = 1
	x := a + 3
	y := b + 2
	if x > y {
		x = x + 1
	} else {
		x = y + 1
	}
	w := x > 0
	return w
}`

func main() {
	runner := &constantpropagation.Runner{Src: src, Function: "Hello"}
	runner.Run()
}

Run the code, and you will get a constant propagtion analysis result output to console

Use taint analysis

Write code below in your project

package main

import "github.com/cokeBeer/goot/pkg/example/dataflow/taint"

func main() {
	runner := taint.NewRunner("path-to-your-project")
	runner.ModuleName = "your-module-name"
	runner.PassThroughSrcPath = ""
	runner.PassThroughDstPath = "passthrough.json"
	runner.CallGraphDstPath = "callgraph.json"
	runner.PassThroughOnly = false
	runner.InitOnly = false
	runner.Debug = true
	runner.Run()
}

Here is an example of what the args should be. It shows how to analyse the pkg package in github.com/cokeBeer/goot
Run the code, and you will get a passthrough.json in the same directory, which contains taint passthrough information of all functions in your project
You can see key fmt.Sprintf holds the value [[0,1]]

{
    "fmt.Sprintf": [
        [0, 1]
    ]
}

This means the first parameter's taint and the second parameter's taint are passed to the first return value
Also, you will get a callgraph.json in the same directory
You can see the json file contains taint edges from one call parameter to another call parameter

{
    "(*github.com/cokeBeer/goot/pkg/bench.cleaner).startProcessing#0#(*os/exec.Cmd).StdoutPipe#0": {
        "From": "(*github.com/cokeBeer/goot/pkg/bench.cleaner).startProcessing",
        "FromIndex": 0,
        "To": "(*os/exec.Cmd).StdoutPipe",
        "ToIndex": 0,
        "ToIsMethod": false,
        "ToIsSink": true,
        "ToIsSignature": false,
        "ToIsStatic": true
    }
}

This means there is a taint edge from position 0 of startProcessing (in this case, the parameter is the receiver bench.cleaner itself ) to position 0 of StdoutPipe (in this case, the parameter is ther recevier exec.Cmd iteself, too)

Use as a framework

To use goot as a framework, first create two structs implementing pkg/toolkits/scalar.FlowAnalysis interface

// FlowAnalysis represents a flow analysis
type FlowAnalysis interface {
	GetGraph() *graph.UnitGraph
	IsForward() bool
	Computations() int
	FlowThrougth(inMap *map[any]any, unit ssa.Instruction, outMap *map[any]any)
	NewInitalFlow() *map[any]any
	EntryInitalFlow() *map[any]any
	Copy(srcMap *map[any]any, dstMap *map[any]any)
	MergeInto(Unit ssa.Instruction, inout *map[any]any, in *map[any]any)
	End(universe []*entry.Entry)
}

and pkg/golang/switcher.Switcher interface seperately

// Switcher represents a ssa instruction switcher
type Switcher interface {
	CaseAlloc(inst *ssa.Alloc)
	CasePhi(inst *ssa.Phi)
	CaseCall(inst *ssa.Call)
	CaseBinOp(inst *ssa.BinOp)
	CaseUnOp(inst *ssa.UnOp)
	...
	CaseGo(inst *ssa.Go)
	CaseDefer(inst *ssa.Defer)
	CaseSend(inst *ssa.Send)
	CaseStore(inst *ssa.Store)
	CaseMapUpdate(inst *ssa.MapUpdate)
	CaseDebugRef(inst *ssa.DebugRef)
}

Don't worry for these apis. An easy way to implement them is using compose like pkg/toolkits/scalar.BaseFlowAnalysis

// ConstantPropagationAnalysis represents a constant propagtion analysis
type ConstantPropagationAnalysis struct {
	scalar.BaseFlowAnalysis
	constantPropagationSwitcher *ConstantPropagationSwitcher
}

and pkg/golang/switcher.BaseSwitcher

// ConstantPropagationSwitcher represents a constant propagtion switcher
type ConstantPropagationSwitcher struct {
	switcher.BaseSwitcher
	constanctPropagationAnalysis *ConstantPropagationAnalysis
	inMap                        *map[any]any
	outMap                       *map[any]any
}

These can make you focus on the core methods you really need to design carefully in specific analyses

Some examples can be found in pkg/example/dataflow package

And you can learn how to run an analysis from cmd package

Presentation

This is the output of cmd/constantpropagationanalysis
The first part is the SSA format of the source code and the second part is the constant propagation on SSA

# Name: constantpropagtionanalysis.Hello
# Package: constantpropagtionanalysis
# Location: 3:6
func Hello(a int, b int) bool:
0:                                                                entry P:0 S:2
        t0 = 1:int + 3:int                                                  int
        t1 = b + 2:int                                                      int
        t2 = t0 > t1                                                       bool
        if t2 goto 1 else 3
1:                                                              if.then P:1 S:1
        t3 = t0 + 1:int                                                     int
        jump 2
2:                                                              if.done P:2 S:0
        t4 = phi [1: t3, 3: t6] #x                                          int
        t5 = t4 > 0:int                                                    bool
        return t5
3:                                                              if.else P:1 S:1
        t6 = t1 + 1:int                                                     int
        jump 2

constant fact for instruction: 1:int + 3:int
a=UNDEF b=UNDEF t0=4 

constant fact for instruction: b + 2:int
a=UNDEF b=UNDEF t0=4 t1=UNDEF 

constant fact for instruction: t0 > t1
a=UNDEF b=UNDEF t0=4 t1=UNDEF t2=UNDEF 

constant fact for instruction: if t2 goto 1 else 3
a=UNDEF b=UNDEF t0=4 t1=UNDEF t2=UNDEF 

constant fact for instruction: t1 + 1:int
a=UNDEF b=UNDEF t0=4 t1=UNDEF t2=UNDEF t6=UNDEF 

constant fact for instruction: jump 2
a=UNDEF b=UNDEF t0=4 t1=UNDEF t2=UNDEF t6=UNDEF 

constant fact for instruction: t0 + 1:int
a=UNDEF b=UNDEF t0=4 t1=UNDEF t2=UNDEF t3=5 

constant fact for instruction: jump 2
a=UNDEF b=UNDEF t0=4 t1=UNDEF t2=UNDEF t3=5 

constant fact for instruction: phi [1: t3, 3: t6] #x
a=UNDEF b=UNDEF t0=4 t1=UNDEF t2=UNDEF t3=5 t4=5 t6=UNDEF 

constant fact for instruction: t4 > 0:int
a=UNDEF b=UNDEF t0=4 t1=UNDEF t2=UNDEF t3=5 t4=5 t5=NAC t6=UNDEF 

constant fact for instruction: return t5
a=UNDEF b=UNDEF t0=4 t1=UNDEF t2=UNDEF t3=5 t4=5 t5=NAC t6=UNDEF 

Tips

  • goot's api is similar to soot, so if you wonder how goot's api work, you can learn soot first
  • goot uses *map[any]any as flow and ssa.Instruction as unit, so please be careful of type assertion

Thanks

Jump to

Keyboard shortcuts

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