This repository has been archived by the owner on Feb 18, 2021. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 16
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 0da5929
Showing
271 changed files
with
153,361 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
# elf-strings | ||
elf-strings will programmatically read an ELF binary's string sections within a given binary. This is meant to be much like the `strings` UNIX utility, however is purpose built for ELF binaries. | ||
|
||
This means that you can get suitable information about the strings within the binary, such as the section they reside in, the offset in the section, etc.. This utility also has the functionality to 'demangle' C++ symbols, iterate linked libraries and print basic information about the ELF. | ||
|
||
This can prove extremely useful for quickly grabbing strings when analysing a binary. | ||
|
||
# Output | ||
![alt text](https://i.imgur.com/plIdQCF.png "example of demangled strings") | ||
|
||
# Building | ||
``` | ||
git clone https://github.com/LloydLabs/elf-strings | ||
cd elf-strings | ||
go build | ||
``` | ||
|
||
# Arguments | ||
``` | ||
-binary string | ||
the path to the ELF you wish to parse | ||
-demangle | ||
demangle C++ symbols into their original source identifiers, prettify found C++ symbols (optional) | ||
-hex | ||
output the strings as a hexadecimal literal (optional) | ||
-libs | ||
show the linked libraries in the binary (optional) | ||
-max uint | ||
the maximum amount of strings that you wish to be output (optional) | ||
-offset | ||
show the offset of the string in the section (default, recommended) (default true) | ||
-output-file string | ||
the path of the output file that you want to output to (optional) | ||
-output-format string | ||
the format you want to output as (optional, plain/json/xml) (default "plain") | ||
``` | ||
|
||
# Example | ||
|
||
An example grabbing the strings from the `echo` utility. | ||
|
||
``` | ||
./elf-strings --binary=/bin/echo --min=4 --max-count=10 | ||
[+] Size: 31 kB | ||
[+] Arch: x86_64 | ||
[+] Entry point: 0x401800 | ||
[+] Class: ELFCLASS64 | ||
[+] Byte order: LittleEndian | ||
[.dynstr+0x0]: libc.so.6 | ||
[.dynstr+0xa]: fflush | ||
[.dynstr+0x11]: __printf_chk | ||
[.dynstr+0x1e]: setlocale | ||
[.dynstr+0x28]: mbrtowc | ||
[.dynstr+0x30]: strncmp | ||
[.dynstr+0x38]: strrchr | ||
[.dynstr+0x40]: dcgettext | ||
[.dynstr+0x4a]: error | ||
[.dynstr+0x50]: __stack_chk_fail | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
package main | ||
|
||
import ( | ||
"bytes" | ||
"debug/elf" | ||
"errors" | ||
"os" | ||
) | ||
|
||
// ElfReader instance containing information | ||
// about said ELF binary | ||
type ElfReader struct { | ||
ExecReader *elf.File | ||
File *os.File | ||
} | ||
|
||
// NewELFReader will create a new instance of ElfReader | ||
func NewELFReader(path string) (*ElfReader, error) { | ||
var r ElfReader | ||
var err error | ||
|
||
r.File, err = os.OpenFile(path, os.O_RDONLY, os.ModePerm) | ||
if err != nil { | ||
return nil, errors.New("failed to open the file") | ||
} | ||
|
||
r.ExecReader, err = elf.NewFile(r.File) | ||
if err != nil { | ||
return nil, errors.New("failed to parse the ELF file succesfully") | ||
} | ||
|
||
return &r, nil | ||
} | ||
|
||
// ReaderParseSection will parse the ELF section and | ||
// return an array of bytes containing the content | ||
// of the section, using the file instance.. | ||
func (r *ElfReader) ReaderParseSection(name string) []byte { | ||
var s *elf.Section | ||
if s = r.ExecReader.Section(name); s == nil { | ||
return nil | ||
} | ||
|
||
sectionSize := int64(s.Offset) | ||
|
||
_, err := r.File.Seek(0, 0) | ||
if err != nil { | ||
return nil | ||
} | ||
|
||
ret, err := r.File.Seek(sectionSize, 0) | ||
if ret != sectionSize || err != nil { | ||
return nil | ||
} | ||
|
||
buf := make([]byte, s.Size) | ||
if buf == nil { | ||
return nil | ||
} | ||
|
||
_, err = r.File.Read(buf) | ||
if err != nil { | ||
return nil | ||
} | ||
|
||
return buf | ||
} | ||
|
||
// ReaderParseStrings will parse the strings by a null terminator | ||
// and then place them into an [offset => string] type map | ||
// alignment does not matter here, as when \x00 exists more than once | ||
// it will simply be skipped. | ||
func (r *ElfReader) ReaderParseStrings(buf []byte) map[uint64][]byte { | ||
var slice [][]byte | ||
if slice = bytes.Split(buf, []byte("\x00")); slice == nil { | ||
return nil | ||
} | ||
|
||
strings := make(map[uint64][]byte, len(slice)) | ||
length := uint64(len(slice)) | ||
|
||
var offset uint64 | ||
|
||
for i := uint64(0); i < length; i++ { | ||
if len(slice[i]) == 0 { | ||
continue | ||
} | ||
|
||
strings[offset] = slice[i] | ||
|
||
offset += (uint64(len(slice[i])) + 1) | ||
} | ||
|
||
return strings | ||
} | ||
|
||
// Close softly close all of the instances associated | ||
// with the ElfReader | ||
func (r *ElfReader) Close() { | ||
r.ExecReader.Close() | ||
r.File.Close() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,189 @@ | ||
package main | ||
|
||
import ( | ||
"flag" | ||
"fmt" | ||
"log" | ||
"os" | ||
"strings" | ||
|
||
humanize "github.com/dustin/go-humanize" | ||
"github.com/fatih/color" | ||
"github.com/shawnsmithdev/zermelo" | ||
) | ||
|
||
var ( | ||
demangleOpt = flag.Bool("demangle", false, "demangle C++ symbols into their original source identifiers, prettify found C++ symbols (optional)") | ||
hexOpt = flag.Bool("hex", false, "output the strings as a hexadecimal literal (optional)") | ||
offsetOpt = flag.Bool("offset", true, "show the offset of the string in the section (default, recommended)") | ||
binaryOpt = flag.String("binary", "", "the path to the ELF you wish to parse") | ||
formatOpt = flag.String("output-format", "plain", "the format you want to output as (optional, plain/json/xml)") | ||
outputOpt = flag.String("output-file", "", "the path of the output file that you want to output to (optional)") | ||
maxOpt = flag.Uint64("max-count", 0, "the maximum amount of strings that you wish to be output (optional)") | ||
libOpt = flag.Bool("libs", false, "show the linked libraries in the binary (optional)") | ||
infoOpt = flag.Bool("no-info", false, "don't show any information about the binary") | ||
minOpt = flag.Uint64("min", 0, "the minimum length of the string") | ||
colorOpt = flag.Bool("no-color", false, "disable color output in the results") | ||
trimOpt = flag.Bool("no-trim", false, "disable triming whitespace and trailing newlines") | ||
humanOpt = flag.Bool("no-human", false, "don't validate that its a human readable string, this could increase the amount of junk.") | ||
) | ||
|
||
// ReadSection is the main logic here | ||
// it combines all of the modules, etc. | ||
func ReadSection(reader *ElfReader, section string) { | ||
var err error | ||
var writer *OutWriter | ||
var count uint64 | ||
|
||
sect := reader.ReaderParseSection(section) | ||
|
||
if *outputOpt != "" { | ||
writer, err = NewOutWriter(*outputOpt, OutParseTypeStr(*formatOpt)) | ||
if err != nil { | ||
log.Fatal(err.Error()) | ||
} | ||
} | ||
|
||
if sect != nil { | ||
nodes := reader.ReaderParseStrings(sect) | ||
|
||
// Since maps in Go are unsorted, we're going to have to make | ||
// a slice of keys, then iterate over this and just use the index | ||
// from the map. | ||
keys := make([]uint64, len(nodes)) | ||
for k, _ := range nodes { | ||
keys = append(keys, k) | ||
} | ||
|
||
err = zermelo.Sort(keys) | ||
if err != nil { | ||
return | ||
} | ||
|
||
keys = UtilUniqueSlice(keys) | ||
|
||
for _, off := range keys { | ||
if *maxOpt != 0 { | ||
if count == *maxOpt { | ||
break | ||
} | ||
} | ||
|
||
str := string(nodes[off]) | ||
if uint64(len(str)) < *minOpt { | ||
continue | ||
} | ||
|
||
if !*humanOpt { | ||
if !UtilIsNice(str) { | ||
continue | ||
} | ||
} | ||
|
||
str = strings.TrimSpace(str) | ||
|
||
if !*trimOpt { | ||
bad := []string{"\n", "\r"} | ||
for _, char := range bad { | ||
str = strings.Replace(str, char, "", -1) | ||
} | ||
} | ||
|
||
if *demangleOpt { | ||
demangled, err := UtilDemangle(&str) | ||
if err == nil { | ||
str = demangled | ||
} | ||
} | ||
|
||
if *hexOpt { | ||
str = UtilConvHex(str) | ||
} | ||
|
||
if *offsetOpt { | ||
if os.Getenv("NO_COLOR") != "" || *colorOpt { | ||
fmt.Printf("[%s+%#x]: %s\n", | ||
section, | ||
off, | ||
str) | ||
} else { | ||
fmt.Printf("[%s%s]: %s\n", | ||
color.BlueString(section), | ||
color.GreenString("+%#x", off), | ||
str) | ||
} | ||
} else { | ||
fmt.Println(str) | ||
} | ||
|
||
if writer != nil { | ||
writer.WriteResult(str, off) | ||
} | ||
|
||
count++ | ||
} | ||
} | ||
} | ||
|
||
// ReadBasic will read the basic information | ||
// about the ELF | ||
func ReadBasic(reader *ElfReader) { | ||
stat, err := reader.File.Stat() | ||
if err != nil { | ||
return | ||
} | ||
|
||
size := humanize.Bytes(uint64(stat.Size())) | ||
|
||
fmt.Printf( | ||
"[+] Size: %s\n"+ | ||
"[+] Arch: %s\n"+ | ||
"[+] Entry point: %#x\n"+ | ||
"[+] Class: %s\n"+ | ||
"[+] Byte order: %s\n", | ||
size, | ||
UtilConvertMachine(reader.ExecReader.Machine), | ||
reader.ExecReader.Entry, | ||
reader.ExecReader.Class.String(), | ||
reader.ExecReader.ByteOrder.String(), | ||
) | ||
|
||
if *libOpt { | ||
fmt.Println("[+] Libraries:") | ||
libs, err := reader.ExecReader.ImportedLibraries() | ||
if err == nil { | ||
for _, lib := range libs { | ||
fmt.Printf("\t [!] %s\n", lib) | ||
} | ||
} | ||
} | ||
|
||
fmt.Println(strings.Repeat("-", 16)) | ||
} | ||
|
||
// main is the entrypoint for this program | ||
func main() { | ||
flag.Parse() | ||
|
||
if *binaryOpt == "" { | ||
flag.PrintDefaults() | ||
return | ||
} | ||
|
||
r, err := NewELFReader(*binaryOpt) | ||
if err != nil { | ||
log.Fatal(err.Error()) | ||
} | ||
|
||
defer r.Close() | ||
|
||
ReadBasic(r) | ||
|
||
sections := []string{".dynstr", ".rodata", ".rdata", | ||
".strtab", ".comment", ".note", | ||
".stab", ".stabstr", ".note.ABI-tag", ".note.gnu.build-id"} | ||
|
||
for _, section := range sections { | ||
ReadSection(r, section) | ||
} | ||
} |
Oops, something went wrong.