Files
nexus/internal/leviathan/disasm/image.go
T
2026-06-16 11:51:07 +01:00

75 lines
2.2 KiB
Go

package disasm
import (
"github.com/andydixon/nexus/internal/leviathan/document"
"github.com/andydixon/nexus/internal/leviathan/parsers"
)
// ImageAnalysis is a metadata-seeded analysis across file-backed executable
// regions in an ELF or PE image.
type ImageAnalysis struct {
Info *parsers.ExecutableInfo `json:"info"`
Programs []*Program `json:"programs"`
Truncated bool `json:"truncated"`
}
// DiscoverExecutable analyzes executable regions using header entries,
// symbols and exports as roots. Budgets apply across the whole image.
func DiscoverExecutable(d *document.Document, maxBytes int64, maxFunctions, maxInstr int) (*ImageAnalysis, error) {
info, err := parsers.InspectExecutable(d)
if err != nil {
return nil, err
}
if maxBytes <= 0 || maxBytes > 32<<20 {
maxBytes = 8 << 20
}
if maxFunctions <= 0 || maxFunctions > 4096 {
maxFunctions = 512
}
if maxInstr <= 0 || maxInstr > 1_000_000 {
maxInstr = 131_072
}
analysis := &ImageAnalysis{Info: info}
remainingBytes := maxBytes
remainingFunctions := maxFunctions
remainingInstr := maxInstr
for _, region := range info.Regions {
if remainingBytes <= 0 || remainingFunctions <= 0 || remainingInstr <= 0 {
analysis.Truncated = true
break
}
length := region.Size
if length > 4<<20 {
length = 4 << 20
analysis.Truncated = true
}
if length > remainingBytes {
length = remainingBytes
analysis.Truncated = true
}
if length <= 0 {
continue
}
buf, err := d.Range(region.Offset, length)
if err != nil {
return nil, err
}
seeds := make([]Seed, 0)
for _, seed := range info.Seeds {
if seed.Offset >= region.Offset && seed.Offset < region.Offset+length {
seeds = append(seeds, Seed{Name: seed.Name, Entry: seed.Offset})
}
}
if len(seeds) == 0 {
seeds = append(seeds, Seed{Name: region.Name, Entry: region.Offset})
}
program := DiscoverFunctionsFromSeeds(buf, region.Offset, info.Arch, seeds, remainingFunctions, remainingInstr)
analysis.Programs = append(analysis.Programs, program)
remainingBytes -= length
remainingFunctions -= len(program.Functions)
remainingInstr -= program.Instructions
analysis.Truncated = analysis.Truncated || program.Truncated
}
return analysis, nil
}