stringrecovery.go
1 package stringrecovery 2 3 import ( 4 "debug/macho" 5 "debug/pe" 6 "encoding/binary" 7 "errors" 8 "fmt" 9 "io" 10 "os" 11 "strings" 12 ) 13 14 var ( 15 charset_english = ` !"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_abcdefghijklmnopqrstuvwxyz{|}~` + "`" 16 lookup_table_english [256]bool 17 ) 18 19 type ( 20 Callback func(segment_name string, address uint64, token string) 21 ) 22 23 func init() { 24 for _, c := range charset_english { 25 lookup_table_english[c] = true 26 } 27 } 28 29 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) { 30 var ( 31 offset int64 32 ) 33 34 var ( 35 current_token strings.Builder 36 current_token_offset int64 37 ) 38 39 for { 40 var b [1]byte 41 if _, err = section_reader.ReadAt(b[:], offset); err != nil { 42 if err == io.EOF { 43 err = nil 44 break 45 } 46 } 47 if b[0] == 0 { 48 // if current_token != "", this is a 0-terminator 49 // emit the token 50 if current_token.Len() > 0 { 51 if current_token.Len() < minimum_length { 52 current_token.Reset() 53 offset++ 54 continue 55 } 56 57 align_offset := 0 58 59 if try_align { 60 for i := uint64(current_token_offset); (i % word_size) != 0; i++ { 61 current_token_offset++ 62 align_offset++ 63 } 64 } 65 66 current_token_string := current_token.String() 67 current_token_string = current_token_string[align_offset:] 68 69 callback(section_name, virtual_address+uint64(current_token_offset), current_token_string) 70 current_token.Reset() 71 } 72 offset++ 73 continue 74 } 75 if lookup_table_english[b[0]] { 76 if current_token.Len() == 0 { 77 current_token_offset = offset 78 } 79 current_token.WriteByte(b[0]) 80 } else { 81 // discard everything leaing up to this 82 current_token.Reset() 83 } 84 offset++ 85 } 86 87 return 88 } 89 90 func recover_file_macho(word_size uint64, file *os.File, callback Callback) (err error) { 91 var ( 92 macho_file *macho.File 93 ) 94 macho_file, err = macho.NewFile(file) 95 if err != nil { 96 return 97 } 98 for _, section := range macho_file.Sections { 99 fmt.Fprintln(os.Stderr, "recovering", section.Name) 100 switch section.Name { 101 case "__cstring": 102 if err = recover_section(false, word_size, 1, section.Name, section.Addr, section, callback); err != nil { 103 return 104 } 105 case "__const": 106 if err = recover_section(false, word_size, 4, section.Name, section.Addr, section, callback); err != nil { 107 return 108 } 109 } 110 } 111 return 112 } 113 114 func recover_file_pe(file *os.File, callback Callback) (err error) { 115 var ( 116 pe_file *pe.File 117 ) 118 pe_file, err = pe.NewFile(file) 119 if err != nil { 120 return 121 } 122 image_base := uint64(0x400000) 123 var word_size uint64 124 125 switch h := pe_file.OptionalHeader.(type) { 126 case *pe.OptionalHeader32: 127 word_size = 4 128 image_base = uint64(h.ImageBase) 129 case *pe.OptionalHeader64: 130 word_size = 8 131 image_base = h.ImageBase 132 } 133 134 for _, section := range pe_file.Sections { 135 fmt.Fprintln(os.Stderr, "recovering", section.Name) 136 switch section.Name { 137 case ".data": 138 if err = recover_section(true, word_size, 4, section.Name, image_base+uint64(section.VirtualAddress), section, callback); err != nil { 139 return 140 } 141 case ".rdata": 142 if err = recover_section(true, word_size, 4, section.Name, image_base+uint64(section.VirtualAddress), section, callback); err != nil { 143 return 144 } 145 } 146 } 147 148 return 149 } 150 151 func RecoverFile(filename string, callback Callback) (err error) { 152 var file *os.File 153 file, err = os.Open(filename) 154 if err != nil { 155 return 156 } 157 var magic [4]byte 158 if _, err = file.ReadAt(magic[:], 0); err != nil { 159 return 160 } 161 magic_number := binary.LittleEndian.Uint32(magic[:]) 162 if magic[0] == 'M' && magic[1] == 'Z' { 163 err = recover_file_pe(file, callback) 164 } else if magic_number == 0xfeedface { 165 err = recover_file_macho(4, file, callback) 166 } else if magic_number == 0xfeedfacf { 167 err = recover_file_macho(8, file, callback) 168 } else if magic_number == 0xcefaedfe { 169 err = recover_file_macho(4, file, callback) 170 } else { 171 err = errors.New("unknown file magic: " + filename) 172 } 173 return 174 }