Skip to content
This repository was archived by the owner on Feb 18, 2021. It is now read-only.

Commit 0da5929

Browse files
committed
Initial commit
0 parents  commit 0da5929

File tree

271 files changed

+153361
-0
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

271 files changed

+153361
-0
lines changed

README.md

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
# elf-strings
2+
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.
3+
4+
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.
5+
6+
This can prove extremely useful for quickly grabbing strings when analysing a binary.
7+
8+
# Output
9+
![alt text](https://i.imgur.com/plIdQCF.png "example of demangled strings")
10+
11+
# Building
12+
```
13+
git clone https://github.com/LloydLabs/elf-strings
14+
cd elf-strings
15+
go build
16+
```
17+
18+
# Arguments
19+
```
20+
-binary string
21+
the path to the ELF you wish to parse
22+
-demangle
23+
demangle C++ symbols into their original source identifiers, prettify found C++ symbols (optional)
24+
-hex
25+
output the strings as a hexadecimal literal (optional)
26+
-libs
27+
show the linked libraries in the binary (optional)
28+
-max uint
29+
the maximum amount of strings that you wish to be output (optional)
30+
-offset
31+
show the offset of the string in the section (default, recommended) (default true)
32+
-output-file string
33+
the path of the output file that you want to output to (optional)
34+
-output-format string
35+
the format you want to output as (optional, plain/json/xml) (default "plain")
36+
```
37+
38+
# Example
39+
40+
An example grabbing the strings from the `echo` utility.
41+
42+
```
43+
./elf-strings --binary=/bin/echo --min=4 --max-count=10
44+
45+
[+] Size: 31 kB
46+
[+] Arch: x86_64
47+
[+] Entry point: 0x401800
48+
[+] Class: ELFCLASS64
49+
[+] Byte order: LittleEndian
50+
51+
[.dynstr+0x0]: libc.so.6
52+
[.dynstr+0xa]: fflush
53+
[.dynstr+0x11]: __printf_chk
54+
[.dynstr+0x1e]: setlocale
55+
[.dynstr+0x28]: mbrtowc
56+
[.dynstr+0x30]: strncmp
57+
[.dynstr+0x38]: strrchr
58+
[.dynstr+0x40]: dcgettext
59+
[.dynstr+0x4a]: error
60+
[.dynstr+0x50]: __stack_chk_fail
61+
```

elfread.go

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
package main
2+
3+
import (
4+
"bytes"
5+
"debug/elf"
6+
"errors"
7+
"os"
8+
)
9+
10+
// ElfReader instance containing information
11+
// about said ELF binary
12+
type ElfReader struct {
13+
ExecReader *elf.File
14+
File *os.File
15+
}
16+
17+
// NewELFReader will create a new instance of ElfReader
18+
func NewELFReader(path string) (*ElfReader, error) {
19+
var r ElfReader
20+
var err error
21+
22+
r.File, err = os.OpenFile(path, os.O_RDONLY, os.ModePerm)
23+
if err != nil {
24+
return nil, errors.New("failed to open the file")
25+
}
26+
27+
r.ExecReader, err = elf.NewFile(r.File)
28+
if err != nil {
29+
return nil, errors.New("failed to parse the ELF file succesfully")
30+
}
31+
32+
return &r, nil
33+
}
34+
35+
// ReaderParseSection will parse the ELF section and
36+
// return an array of bytes containing the content
37+
// of the section, using the file instance..
38+
func (r *ElfReader) ReaderParseSection(name string) []byte {
39+
var s *elf.Section
40+
if s = r.ExecReader.Section(name); s == nil {
41+
return nil
42+
}
43+
44+
sectionSize := int64(s.Offset)
45+
46+
_, err := r.File.Seek(0, 0)
47+
if err != nil {
48+
return nil
49+
}
50+
51+
ret, err := r.File.Seek(sectionSize, 0)
52+
if ret != sectionSize || err != nil {
53+
return nil
54+
}
55+
56+
buf := make([]byte, s.Size)
57+
if buf == nil {
58+
return nil
59+
}
60+
61+
_, err = r.File.Read(buf)
62+
if err != nil {
63+
return nil
64+
}
65+
66+
return buf
67+
}
68+
69+
// ReaderParseStrings will parse the strings by a null terminator
70+
// and then place them into an [offset => string] type map
71+
// alignment does not matter here, as when \x00 exists more than once
72+
// it will simply be skipped.
73+
func (r *ElfReader) ReaderParseStrings(buf []byte) map[uint64][]byte {
74+
var slice [][]byte
75+
if slice = bytes.Split(buf, []byte("\x00")); slice == nil {
76+
return nil
77+
}
78+
79+
strings := make(map[uint64][]byte, len(slice))
80+
length := uint64(len(slice))
81+
82+
var offset uint64
83+
84+
for i := uint64(0); i < length; i++ {
85+
if len(slice[i]) == 0 {
86+
continue
87+
}
88+
89+
strings[offset] = slice[i]
90+
91+
offset += (uint64(len(slice[i])) + 1)
92+
}
93+
94+
return strings
95+
}
96+
97+
// Close softly close all of the instances associated
98+
// with the ElfReader
99+
func (r *ElfReader) Close() {
100+
r.ExecReader.Close()
101+
r.File.Close()
102+
}

main.go

Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
package main
2+
3+
import (
4+
"flag"
5+
"fmt"
6+
"log"
7+
"os"
8+
"strings"
9+
10+
humanize "github.com/dustin/go-humanize"
11+
"github.com/fatih/color"
12+
"github.com/shawnsmithdev/zermelo"
13+
)
14+
15+
var (
16+
demangleOpt = flag.Bool("demangle", false, "demangle C++ symbols into their original source identifiers, prettify found C++ symbols (optional)")
17+
hexOpt = flag.Bool("hex", false, "output the strings as a hexadecimal literal (optional)")
18+
offsetOpt = flag.Bool("offset", true, "show the offset of the string in the section (default, recommended)")
19+
binaryOpt = flag.String("binary", "", "the path to the ELF you wish to parse")
20+
formatOpt = flag.String("output-format", "plain", "the format you want to output as (optional, plain/json/xml)")
21+
outputOpt = flag.String("output-file", "", "the path of the output file that you want to output to (optional)")
22+
maxOpt = flag.Uint64("max-count", 0, "the maximum amount of strings that you wish to be output (optional)")
23+
libOpt = flag.Bool("libs", false, "show the linked libraries in the binary (optional)")
24+
infoOpt = flag.Bool("no-info", false, "don't show any information about the binary")
25+
minOpt = flag.Uint64("min", 0, "the minimum length of the string")
26+
colorOpt = flag.Bool("no-color", false, "disable color output in the results")
27+
trimOpt = flag.Bool("no-trim", false, "disable triming whitespace and trailing newlines")
28+
humanOpt = flag.Bool("no-human", false, "don't validate that its a human readable string, this could increase the amount of junk.")
29+
)
30+
31+
// ReadSection is the main logic here
32+
// it combines all of the modules, etc.
33+
func ReadSection(reader *ElfReader, section string) {
34+
var err error
35+
var writer *OutWriter
36+
var count uint64
37+
38+
sect := reader.ReaderParseSection(section)
39+
40+
if *outputOpt != "" {
41+
writer, err = NewOutWriter(*outputOpt, OutParseTypeStr(*formatOpt))
42+
if err != nil {
43+
log.Fatal(err.Error())
44+
}
45+
}
46+
47+
if sect != nil {
48+
nodes := reader.ReaderParseStrings(sect)
49+
50+
// Since maps in Go are unsorted, we're going to have to make
51+
// a slice of keys, then iterate over this and just use the index
52+
// from the map.
53+
keys := make([]uint64, len(nodes))
54+
for k, _ := range nodes {
55+
keys = append(keys, k)
56+
}
57+
58+
err = zermelo.Sort(keys)
59+
if err != nil {
60+
return
61+
}
62+
63+
keys = UtilUniqueSlice(keys)
64+
65+
for _, off := range keys {
66+
if *maxOpt != 0 {
67+
if count == *maxOpt {
68+
break
69+
}
70+
}
71+
72+
str := string(nodes[off])
73+
if uint64(len(str)) < *minOpt {
74+
continue
75+
}
76+
77+
if !*humanOpt {
78+
if !UtilIsNice(str) {
79+
continue
80+
}
81+
}
82+
83+
str = strings.TrimSpace(str)
84+
85+
if !*trimOpt {
86+
bad := []string{"\n", "\r"}
87+
for _, char := range bad {
88+
str = strings.Replace(str, char, "", -1)
89+
}
90+
}
91+
92+
if *demangleOpt {
93+
demangled, err := UtilDemangle(&str)
94+
if err == nil {
95+
str = demangled
96+
}
97+
}
98+
99+
if *hexOpt {
100+
str = UtilConvHex(str)
101+
}
102+
103+
if *offsetOpt {
104+
if os.Getenv("NO_COLOR") != "" || *colorOpt {
105+
fmt.Printf("[%s+%#x]: %s\n",
106+
section,
107+
off,
108+
str)
109+
} else {
110+
fmt.Printf("[%s%s]: %s\n",
111+
color.BlueString(section),
112+
color.GreenString("+%#x", off),
113+
str)
114+
}
115+
} else {
116+
fmt.Println(str)
117+
}
118+
119+
if writer != nil {
120+
writer.WriteResult(str, off)
121+
}
122+
123+
count++
124+
}
125+
}
126+
}
127+
128+
// ReadBasic will read the basic information
129+
// about the ELF
130+
func ReadBasic(reader *ElfReader) {
131+
stat, err := reader.File.Stat()
132+
if err != nil {
133+
return
134+
}
135+
136+
size := humanize.Bytes(uint64(stat.Size()))
137+
138+
fmt.Printf(
139+
"[+] Size: %s\n"+
140+
"[+] Arch: %s\n"+
141+
"[+] Entry point: %#x\n"+
142+
"[+] Class: %s\n"+
143+
"[+] Byte order: %s\n",
144+
size,
145+
UtilConvertMachine(reader.ExecReader.Machine),
146+
reader.ExecReader.Entry,
147+
reader.ExecReader.Class.String(),
148+
reader.ExecReader.ByteOrder.String(),
149+
)
150+
151+
if *libOpt {
152+
fmt.Println("[+] Libraries:")
153+
libs, err := reader.ExecReader.ImportedLibraries()
154+
if err == nil {
155+
for _, lib := range libs {
156+
fmt.Printf("\t [!] %s\n", lib)
157+
}
158+
}
159+
}
160+
161+
fmt.Println(strings.Repeat("-", 16))
162+
}
163+
164+
// main is the entrypoint for this program
165+
func main() {
166+
flag.Parse()
167+
168+
if *binaryOpt == "" {
169+
flag.PrintDefaults()
170+
return
171+
}
172+
173+
r, err := NewELFReader(*binaryOpt)
174+
if err != nil {
175+
log.Fatal(err.Error())
176+
}
177+
178+
defer r.Close()
179+
180+
ReadBasic(r)
181+
182+
sections := []string{".dynstr", ".rodata", ".rdata",
183+
".strtab", ".comment", ".note",
184+
".stab", ".stabstr", ".note.ABI-tag", ".note.gnu.build-id"}
185+
186+
for _, section := range sections {
187+
ReadSection(r, section)
188+
}
189+
}

0 commit comments

Comments
 (0)