mirror of
https://github.com/maride/pancap.git
synced 2024-11-21 16:34:26 +00:00
Generate Graphviz graphs of network communication
This commit is contained in:
parent
1aa3572c30
commit
1989ae996f
@ -39,6 +39,9 @@ func Analyze(source *gopacket.PacketSource) error {
|
||||
}
|
||||
}
|
||||
|
||||
// Register communication for graph
|
||||
output.AddPkgToGraph(packet)
|
||||
|
||||
// Raise statistics
|
||||
totalPackets += 1
|
||||
if processed {
|
||||
|
3
main.go
3
main.go
@ -36,6 +36,9 @@ func main() {
|
||||
// Extract found and requested files
|
||||
output.StoreFiles()
|
||||
|
||||
// Create communication graph
|
||||
output.CreateGraph()
|
||||
|
||||
// Show user analysis
|
||||
analyze.PrintSummary()
|
||||
|
||||
|
@ -8,6 +8,7 @@ var (
|
||||
targetFiles *string
|
||||
targetAllFiles *bool
|
||||
targetOutput *string
|
||||
graphOutput *string
|
||||
)
|
||||
|
||||
func RegisterFlags() {
|
||||
@ -16,6 +17,5 @@ func RegisterFlags() {
|
||||
targetFiles = flag.String("extract-these", "", "Comma-separated list of files to extract.")
|
||||
targetAllFiles = flag.Bool("extract-all", false, "Extract all files found.")
|
||||
targetOutput = flag.String("extract-to", "./extracted", "Directory to store extracted files in.")
|
||||
graphOutput = flag.String("create-graph", "", "Create a Graphviz graph out of collected communication")
|
||||
}
|
||||
|
||||
|
||||
|
104
output/graph.go
Normal file
104
output/graph.go
Normal file
@ -0,0 +1,104 @@
|
||||
package output
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
|
||||
"crypto/sha256"
|
||||
|
||||
"github.com/google/gopacket"
|
||||
)
|
||||
|
||||
var graphPkgs []GraphPkg
|
||||
|
||||
// AddPkgToGraph adds the given packet as communication to the graph
|
||||
func AddPkgToGraph(pkg gopacket.Packet) {
|
||||
// Only proceed if pkg contains a network layer
|
||||
if pkg.NetworkLayer() == nil {
|
||||
return
|
||||
}
|
||||
|
||||
src := pkg.NetworkLayer().NetworkFlow().Src().String()
|
||||
dst := pkg.NetworkLayer().NetworkFlow().Dst().String()
|
||||
|
||||
// Search for the given communication pair
|
||||
for _, p := range graphPkgs {
|
||||
if p.from == src && p.to == dst {
|
||||
// Communication pair found, add protocol and finish
|
||||
p.AddProtocol("nil")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Communcation pair was not in graphPkgs, add to it
|
||||
graphPkgs = append(graphPkgs, GraphPkg{
|
||||
from: src,
|
||||
to: dst,
|
||||
protocol: []string{""},
|
||||
})
|
||||
}
|
||||
|
||||
// CreateGraph writes out a Graphviz digraph
|
||||
func CreateGraph() {
|
||||
if *graphOutput == "" {
|
||||
// No graph requested
|
||||
return
|
||||
}
|
||||
|
||||
// Start with the Graphviz-specific header
|
||||
dot := fmt.Sprintf("# Compile with `neato -Tpng %s > %s.png`\n", *graphOutput, *graphOutput)
|
||||
dot += "digraph pancap {\n\toverlap = false;\n"
|
||||
|
||||
// First, gather all nodes as-is and write them out
|
||||
dot += nodedef(graphPkgs)
|
||||
|
||||
// Iterate over communication
|
||||
for _, p := range graphPkgs {
|
||||
dot += fmt.Sprintf("\tn%s->n%s\n", hash(p.from), hash(p.to))
|
||||
}
|
||||
|
||||
// Close
|
||||
dot += "}\n"
|
||||
|
||||
// Write out
|
||||
ioutil.WriteFile(*graphOutput, []byte(dot), 0644)
|
||||
}
|
||||
|
||||
// Creates a list of distinct nodes, Graphviz-compatible
|
||||
func nodedef(pkgs []GraphPkg) string {
|
||||
output := ""
|
||||
nodes := []string{}
|
||||
for _, p := range graphPkgs {
|
||||
// Check if src and dst are already present in nodes array
|
||||
srcFound := false
|
||||
dstFound := false
|
||||
for _, n := range nodes {
|
||||
if p.from == n {
|
||||
srcFound = true
|
||||
}
|
||||
if p.to == n {
|
||||
dstFound = true
|
||||
}
|
||||
}
|
||||
if !srcFound {
|
||||
// src not yet present, add to node list
|
||||
nodes = append(nodes, p.from)
|
||||
}
|
||||
if !dstFound {
|
||||
// dst not yet present, add to node list
|
||||
nodes = append(nodes, p.to)
|
||||
}
|
||||
}
|
||||
|
||||
// Output Graphviz-compatible node definition
|
||||
for _, n := range nodes {
|
||||
// As the Graphviz charset for nodes is rather small, rely on hashes
|
||||
output += fmt.Sprintf("\tn%s[label=\"%s\"]\n", hash(n), n)
|
||||
}
|
||||
|
||||
return output
|
||||
}
|
||||
|
||||
func hash(s string) string {
|
||||
return fmt.Sprintf("%x", sha256.Sum256([]byte(s)))[:6]
|
||||
}
|
19
output/graphpkg.go
Normal file
19
output/graphpkg.go
Normal file
@ -0,0 +1,19 @@
|
||||
package output
|
||||
|
||||
// GraphPkg resembles a directed communication from one address to another
|
||||
// It wraps up required information to draw a graph of the communication, including spoken protocols.
|
||||
type GraphPkg struct {
|
||||
from string
|
||||
to string
|
||||
protocol []string
|
||||
}
|
||||
|
||||
// AddProtocol adds the given protocol to the list of protocols if not already present
|
||||
func (p *GraphPkg) AddProtocol(protocol string) {
|
||||
for _, p := range p.protocol {
|
||||
if p == protocol {
|
||||
return
|
||||
}
|
||||
}
|
||||
p.protocol = append(p.protocol, protocol)
|
||||
}
|
@ -23,4 +23,10 @@ func Finalize() {
|
||||
// User avoided the files
|
||||
printer.Println("Files found in stream. Add --extract-all or --extract-these <list> to extract them.")
|
||||
}
|
||||
|
||||
// Check if something graph-worthy was collected
|
||||
if *graphOutput == "" && len(graphPkgs) > 0 {
|
||||
// User didn't want a graph
|
||||
printer.Println("To summarize the communcation flow with a Graphviz graph, specify --create-graph <out.dot>.")
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user