diff --git a/srcs/dependtool/binary_file.go b/srcs/dependtool/binary_file.go index ba8a88e..24a9aa7 100644 --- a/srcs/dependtool/binary_file.go +++ b/srcs/dependtool/binary_file.go @@ -48,7 +48,6 @@ func getElf(filename string) (*elf.File, error) { if err != nil { return nil, err } - defer f.Close() _elf, err := elf.NewFile(f) if err != nil { diff --git a/srcs/dependtool/parser.go b/srcs/dependtool/parser.go index bc90402..c1aecb6 100644 --- a/srcs/dependtool/parser.go +++ b/srcs/dependtool/parser.go @@ -25,68 +25,6 @@ type recursiveData struct { // --------------------------------Static Output-------------------------------- -// parseReadELF parses the output of the 'readelf' command. -// -func parseReadELF(output string, data *u.StaticData) { - types := map[string]bool{"FUNC": true, "FILE": true, "OBJECT": true} - - // Check the output of 'readelf' command - for _, line := range strings.Split(output, "\n") { - words := strings.Fields(line) - - if len(words) > 8 && types[words[3]] { - symbol := strings.Split(words[7], "@") - if len(symbol) > 2 { - data.Symbols[symbol[0]] = symbol[1] - } else { - data.Symbols[words[7]] = "" - } - } - } -} - -// parseNMMacos parses the output of the 'nm' command (Mac os). -// -func parseNMMac(output string, data *u.StaticData) { - // Get the list of system calls - systemCalls := initSystemCalls() - - // Check the output of 'nm' command - var re = regexp.MustCompile(`(?m)([U|T|B|D]\s)(.*)\s*`) - for _, match := range re.FindAllStringSubmatch(output, -1) { - if len(match) > 2 { - if match[2][0] == '_' { - match[2] = match[2][1:] - } - } - - // Add to system calls map if symbol is a system call - if _, isSyscall := systemCalls[match[2]]; isSyscall { - data.SystemCalls[match[2]] = systemCalls[match[2]] - } else { - data.Symbols[match[2]] = "" - } - } -} - -// parseNMLinux parses the output of the 'nm' command (Linux). -// -func parseNMLinux(output string, data *u.StaticData) { - // Get the list of system calls - systemCalls := initSystemCalls() - - // Check the output of 'nm' command - var re = regexp.MustCompile(`(?m)([U|u|T|t|w|W]\s)(.*)\s*`) - for _, match := range re.FindAllStringSubmatch(output, -1) { - // Add to system calls map if symbol is a system call - if _, isSyscall := systemCalls[match[2]]; isSyscall { - data.SystemCalls[match[2]] = systemCalls[match[2]] - } else { - data.Symbols[match[2]] = "" - } - } -} - // parsePackagesName parses the output of the 'apt-cache pkgnames' command. // // It returns a string which represents the name of application used by the diff --git a/srcs/dependtool/run_deptool.go b/srcs/dependtool/run_deptool.go index fbf0d80..14e71e2 100644 --- a/srcs/dependtool/run_deptool.go +++ b/srcs/dependtool/run_deptool.go @@ -1,6 +1,7 @@ package dependtool import ( + "debug/elf" "errors" "fmt" "github.com/fatih/color" @@ -52,8 +53,11 @@ func RunAnalyserTool(homeDir string, data *u.Data) { displayProgramDetails(programName, programPath, args) // Check if the program is a binary + + var elfFile *elf.File + dynamicCompiled := false if strings.ToLower(runtime.GOOS) == "linux" { - checkElf(&programPath) + elfFile, dynamicCompiled = checkElf(&programPath) } else if strings.ToLower(runtime.GOOS) == "darwin" { checkMachOS(&programPath) } @@ -61,7 +65,7 @@ func RunAnalyserTool(homeDir string, data *u.Data) { if typeAnalysis == 0 || typeAnalysis == 1 { // Run static analyser u.PrintHeader1("(1.1) RUN STATIC ANALYSIS") - runStaticAnalyser(args, programName, programPath, outFolder, data) + runStaticAnalyser(elfFile, dynamicCompiled, args, programName, programPath, outFolder, data) } // Run dynamic analyser @@ -117,7 +121,8 @@ func checkMachOS(programPath *string) { } // checkElf checks if the program (from its path) is an ELF file -func checkElf(programPath *string) { +func checkElf(programPath *string) (*elf.File, bool) { + dynamicCompiled := false elfFile, err := getElf(*programPath) if err != nil { u.PrintErr(err) @@ -126,20 +131,32 @@ func checkElf(programPath *string) { u.PrintWarning("Only ELF binaries are supported! Some analysis" + " procedures will be skipped") } else { + // Get ELF architecture architecture, machine := GetElfArchitecture(elfFile) fmt.Println("ELF Class: ", architecture) fmt.Println("Machine: ", machine) fmt.Println("Entry Point: ", elfFile.Entry) + for _, s := range elfFile.Sections { + if strings.Contains(s.Name, ".dynamic") { + fmt.Println("Type: Dynamically compiled") + dynamicCompiled = true + break + } + } + if !dynamicCompiled { + fmt.Println("Type: Statically compiled") + } fmt.Println("----------------------------------------------") } + return elfFile, dynamicCompiled } // runStaticAnalyser runs the static analyser -func runStaticAnalyser(args *u.Arguments, programName, programPath, +func runStaticAnalyser(elfFile *elf.File, dynamicCompiled bool, args *u.Arguments, programName, programPath, outFolder string, data *u.Data) { - staticAnalyser(*args, data, programPath) + staticAnalyser(elfFile, dynamicCompiled, *args, data, programPath) // Save static Data into text file if display mode is set if *args.BoolArg[saveOutputArg] { diff --git a/srcs/dependtool/static_analyser.go b/srcs/dependtool/static_analyser.go index 2a40575..d79d0aa 100644 --- a/srcs/dependtool/static_analyser.go +++ b/srcs/dependtool/static_analyser.go @@ -8,6 +8,7 @@ package dependtool import ( "bufio" + "debug/elf" "fmt" "os" "runtime" @@ -17,41 +18,55 @@ import ( // ---------------------------------Gather Data--------------------------------- +func addSymbols(symbols []elf.Symbol, data *u.StaticData, systemCalls map[string]int) { + for _, s := range symbols { + if _, isSyscall := systemCalls[s.Name]; isSyscall { + data.SystemCalls[s.Name] = systemCalls[s.Name] + } else { + data.Symbols[s.Name] = s.Library + } + } +} + // gatherStaticSymbols gathers symbols of a given application. // // It returns an error if any, otherwise it returns nil. -func gatherStaticSymbols(programPath string, data *u.StaticData) error { +func gatherStaticSymbols(elfFile *elf.File, dynamicCompiled bool, data *u.StaticData) error { - // Use 'readelf' to get symbols - if output, err := u.ExecuteCommand("readelf", []string{"-s", - programPath}); err != nil { - return err + // Get the list of system calls + systemCalls := initSystemCalls() + + symbols, err := elfFile.Symbols() + if err != nil { + if !dynamicCompiled { + // Skip message for dynamically compiled binary + u.PrintWarning(err) + } } else { - parseReadELF(output, data) - } - return nil -} - -// gatherStaticSymbols gathers system calls of a given application. -// -// It returns an error if any, otherwise it returns nil. -func gatherStaticSystemCalls(programPath, argument string, data *u.StaticData) error { - - var args []string - if len(argument) > 0 { - args = []string{argument, programPath} - } else { - args = []string{programPath} + addSymbols(symbols, data, systemCalls) } - // Use 'nm' to get symbols and system calls - if output, err := u.ExecuteCommand("nm", args); err != nil { - return err - } else { - if strings.ToLower(runtime.GOOS) == "linux" { - parseNMLinux(output, data) + // Additional symbols when dynamically compiled + if dynamicCompiled { + + symbols, err = elfFile.DynamicSymbols() + if err != nil { + u.PrintWarning(err) } else { - parseNMMac(output, data) + addSymbols(symbols, data, systemCalls) + } + + importedSymbols, err := elfFile.ImportedSymbols() + if err != nil { + u.PrintWarning(err) + } else { + for _, s := range importedSymbols { + if _, isSyscall := systemCalls[s.Name]; isSyscall { + data.SystemCalls[s.Name] = systemCalls[s.Name] + } else { + data.Symbols[s.Name] = s.Library + } + } } } return nil @@ -177,7 +192,7 @@ func executeDependAptCache(programName string, data *u.StaticData, // staticAnalyser runs the static analysis to get shared libraries, // system calls and library calls of a given application. // -func staticAnalyser(args u.Arguments, data *u.Data, programPath string) { +func staticAnalyser(elfFile *elf.File, dynamicCompiled bool, args u.Arguments, data *u.Data, programPath string) { programName := *args.StringArg[programArg] fullDeps := *args.BoolArg[fullDepsArg] @@ -196,21 +211,14 @@ func staticAnalyser(args u.Arguments, data *u.Data, programPath string) { if strings.ToLower(runtime.GOOS) == "linux" { u.PrintHeader2("(*) Gathering symbols from binary file") - if err := gatherStaticSymbols(programPath, staticData); err != nil { - u.PrintWarning(err) - } - } - - u.PrintHeader2("(*) Gathering symbols & system calls from binary file") - if err := gatherStaticSystemCalls(programPath, "-D", staticData); err != nil { - // Check without the dynamic argument - if err := gatherStaticSystemCalls(programPath, "", staticData); err != nil { + if err := gatherStaticSymbols(elfFile, dynamicCompiled, staticData); err != nil { u.PrintWarning(err) } } u.PrintHeader2("(*) Gathering shared libraries from binary file") if strings.ToLower(runtime.GOOS) == "linux" { + // Cannot use "elfFile.ImportedLibraries()" since we need the .so path if err := gatherStaticSharedLibsLinux(programPath, staticData, fullDeps); err != nil { u.PrintWarning(err) @@ -221,6 +229,10 @@ func staticAnalyser(args u.Arguments, data *u.Data, programPath string) { u.PrintWarning(err) } } + + if err := elfFile.Close(); err != nil { + u.PrintWarning(err) + } } // Detect symbols from shared libraries @@ -229,14 +241,15 @@ func staticAnalyser(args u.Arguments, data *u.Data, programPath string) { for key, path := range staticData.SharedLibs { if len(path) > 0 { fmt.Printf("\t-> Analysing %s - %s\n", key, path[0]) - if err := gatherStaticSymbols(path[0], staticData); err != nil { + libElf, err := getElf(path[0]) + if err != nil { u.PrintWarning(err) } - if err := gatherStaticSystemCalls(path[0], "-D", staticData); err != nil { - // Check without the dynamic argument - if err := gatherStaticSystemCalls(path[0], "", staticData); err != nil { - u.PrintWarning(err) - } + if err := gatherStaticSymbols(libElf, true, staticData); err != nil { + u.PrintWarning(err) + } + if err := libElf.Close(); err != nil { + u.PrintWarning(err) } } }