mirror of
https://github.com/thunderbrewhq/binana.git
synced 2026-03-22 22:00:13 +00:00
175 lines
3.9 KiB
Go
175 lines
3.9 KiB
Go
|
|
package stringrecovery
|
||
|
|
|
||
|
|
import (
|
||
|
|
"debug/macho"
|
||
|
|
"debug/pe"
|
||
|
|
"encoding/binary"
|
||
|
|
"errors"
|
||
|
|
"fmt"
|
||
|
|
"io"
|
||
|
|
"os"
|
||
|
|
"strings"
|
||
|
|
)
|
||
|
|
|
||
|
|
var (
|
||
|
|
charset_english = ` !"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_abcdefghijklmnopqrstuvwxyz{|}~` + "`"
|
||
|
|
lookup_table_english [256]bool
|
||
|
|
)
|
||
|
|
|
||
|
|
type (
|
||
|
|
Callback func(segment_name string, address uint64, token string)
|
||
|
|
)
|
||
|
|
|
||
|
|
func init() {
|
||
|
|
for _, c := range charset_english {
|
||
|
|
lookup_table_english[c] = true
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func recover_section(try_align bool, word_size uint64, minimum_length int, section_name string, virtual_address uint64, section_reader io.ReaderAt, callback Callback) (err error) {
|
||
|
|
var (
|
||
|
|
offset int64
|
||
|
|
)
|
||
|
|
|
||
|
|
var (
|
||
|
|
current_token strings.Builder
|
||
|
|
current_token_offset int64
|
||
|
|
)
|
||
|
|
|
||
|
|
for {
|
||
|
|
var b [1]byte
|
||
|
|
if _, err = section_reader.ReadAt(b[:], offset); err != nil {
|
||
|
|
if err == io.EOF {
|
||
|
|
err = nil
|
||
|
|
break
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if b[0] == 0 {
|
||
|
|
// if current_token != "", this is a 0-terminator
|
||
|
|
// emit the token
|
||
|
|
if current_token.Len() > 0 {
|
||
|
|
if current_token.Len() < minimum_length {
|
||
|
|
current_token.Reset()
|
||
|
|
offset++
|
||
|
|
continue
|
||
|
|
}
|
||
|
|
|
||
|
|
align_offset := 0
|
||
|
|
|
||
|
|
if try_align {
|
||
|
|
for i := uint64(current_token_offset); (i % word_size) != 0; i++ {
|
||
|
|
current_token_offset++
|
||
|
|
align_offset++
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
current_token_string := current_token.String()
|
||
|
|
current_token_string = current_token_string[align_offset:]
|
||
|
|
|
||
|
|
callback(section_name, virtual_address+uint64(current_token_offset), current_token_string)
|
||
|
|
current_token.Reset()
|
||
|
|
}
|
||
|
|
offset++
|
||
|
|
continue
|
||
|
|
}
|
||
|
|
if lookup_table_english[b[0]] {
|
||
|
|
if current_token.Len() == 0 {
|
||
|
|
current_token_offset = offset
|
||
|
|
}
|
||
|
|
current_token.WriteByte(b[0])
|
||
|
|
} else {
|
||
|
|
// discard everything leaing up to this
|
||
|
|
current_token.Reset()
|
||
|
|
}
|
||
|
|
offset++
|
||
|
|
}
|
||
|
|
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
func recover_file_macho(word_size uint64, file *os.File, callback Callback) (err error) {
|
||
|
|
var (
|
||
|
|
macho_file *macho.File
|
||
|
|
)
|
||
|
|
macho_file, err = macho.NewFile(file)
|
||
|
|
if err != nil {
|
||
|
|
return
|
||
|
|
}
|
||
|
|
for _, section := range macho_file.Sections {
|
||
|
|
fmt.Fprintln(os.Stderr, "recovering", section.Name)
|
||
|
|
switch section.Name {
|
||
|
|
case "__cstring":
|
||
|
|
if err = recover_section(false, word_size, 1, section.Name, section.Addr, section, callback); err != nil {
|
||
|
|
return
|
||
|
|
}
|
||
|
|
case "__const":
|
||
|
|
if err = recover_section(false, word_size, 4, section.Name, section.Addr, section, callback); err != nil {
|
||
|
|
return
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
func recover_file_pe(file *os.File, callback Callback) (err error) {
|
||
|
|
var (
|
||
|
|
pe_file *pe.File
|
||
|
|
)
|
||
|
|
pe_file, err = pe.NewFile(file)
|
||
|
|
if err != nil {
|
||
|
|
return
|
||
|
|
}
|
||
|
|
image_base := uint64(0x400000)
|
||
|
|
var word_size uint64
|
||
|
|
|
||
|
|
switch h := pe_file.OptionalHeader.(type) {
|
||
|
|
case *pe.OptionalHeader32:
|
||
|
|
word_size = 4
|
||
|
|
image_base = uint64(h.ImageBase)
|
||
|
|
case *pe.OptionalHeader64:
|
||
|
|
word_size = 8
|
||
|
|
image_base = h.ImageBase
|
||
|
|
}
|
||
|
|
|
||
|
|
for _, section := range pe_file.Sections {
|
||
|
|
fmt.Fprintln(os.Stderr, "recovering", section.Name)
|
||
|
|
switch section.Name {
|
||
|
|
case ".data":
|
||
|
|
if err = recover_section(true, word_size, 4, section.Name, image_base+uint64(section.VirtualAddress), section, callback); err != nil {
|
||
|
|
return
|
||
|
|
}
|
||
|
|
case ".rdata":
|
||
|
|
if err = recover_section(true, word_size, 4, section.Name, image_base+uint64(section.VirtualAddress), section, callback); err != nil {
|
||
|
|
return
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
func RecoverFile(filename string, callback Callback) (err error) {
|
||
|
|
var file *os.File
|
||
|
|
file, err = os.Open(filename)
|
||
|
|
if err != nil {
|
||
|
|
return
|
||
|
|
}
|
||
|
|
var magic [4]byte
|
||
|
|
if _, err = file.ReadAt(magic[:], 0); err != nil {
|
||
|
|
return
|
||
|
|
}
|
||
|
|
magic_number := binary.LittleEndian.Uint32(magic[:])
|
||
|
|
if magic[0] == 'M' && magic[1] == 'Z' {
|
||
|
|
err = recover_file_pe(file, callback)
|
||
|
|
} else if magic_number == 0xfeedface {
|
||
|
|
err = recover_file_macho(4, file, callback)
|
||
|
|
} else if magic_number == 0xfeedfacf {
|
||
|
|
err = recover_file_macho(8, file, callback)
|
||
|
|
} else if magic_number == 0xcefaedfe {
|
||
|
|
err = recover_file_macho(4, file, callback)
|
||
|
|
} else {
|
||
|
|
err = errors.New("unknown file magic: " + filename)
|
||
|
|
}
|
||
|
|
return
|
||
|
|
}
|