rex-bin_tools 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. checksums.yaml +7 -0
  2. checksums.yaml.gz.sig +1 -0
  3. data.tar.gz.sig +0 -0
  4. data/.gitignore +9 -0
  5. data/.rspec +2 -0
  6. data/.travis.yml +5 -0
  7. data/CODE_OF_CONDUCT.md +52 -0
  8. data/Gemfile +4 -0
  9. data/LICENSE +27 -0
  10. data/README.md +22 -0
  11. data/Rakefile +6 -0
  12. data/bin/console +14 -0
  13. data/bin/msfbinscan +284 -0
  14. data/bin/msfelfscan +120 -0
  15. data/bin/msfmachscan +100 -0
  16. data/bin/msfpescan +184 -0
  17. data/bin/setup +8 -0
  18. data/data/identify.txt +3043 -0
  19. data/lib/rex/assembly/nasm.rb +104 -0
  20. data/lib/rex/bin_tools.rb +13 -0
  21. data/lib/rex/bin_tools/version.rb +5 -0
  22. data/lib/rex/elfparsey.rb +9 -0
  23. data/lib/rex/elfparsey/elf.rb +121 -0
  24. data/lib/rex/elfparsey/elfbase.rb +265 -0
  25. data/lib/rex/elfparsey/exceptions.rb +25 -0
  26. data/lib/rex/elfscan.rb +10 -0
  27. data/lib/rex/elfscan/scanner.rb +226 -0
  28. data/lib/rex/elfscan/search.rb +44 -0
  29. data/lib/rex/image_source.rb +10 -0
  30. data/lib/rex/image_source/disk.rb +58 -0
  31. data/lib/rex/image_source/image_source.rb +48 -0
  32. data/lib/rex/image_source/memory.rb +35 -0
  33. data/lib/rex/machparsey.rb +9 -0
  34. data/lib/rex/machparsey/exceptions.rb +31 -0
  35. data/lib/rex/machparsey/mach.rb +209 -0
  36. data/lib/rex/machparsey/machbase.rb +408 -0
  37. data/lib/rex/machscan.rb +9 -0
  38. data/lib/rex/machscan/scanner.rb +217 -0
  39. data/lib/rex/peparsey.rb +10 -0
  40. data/lib/rex/peparsey/exceptions.rb +30 -0
  41. data/lib/rex/peparsey/pe.rb +210 -0
  42. data/lib/rex/peparsey/pe_memdump.rb +61 -0
  43. data/lib/rex/peparsey/pebase.rb +1662 -0
  44. data/lib/rex/peparsey/section.rb +128 -0
  45. data/lib/rex/pescan.rb +11 -0
  46. data/lib/rex/pescan/analyze.rb +366 -0
  47. data/lib/rex/pescan/scanner.rb +230 -0
  48. data/lib/rex/pescan/search.rb +68 -0
  49. data/rex-bin_tools.gemspec +32 -0
  50. metadata +284 -0
  51. metadata.gz.sig +0 -0
@@ -0,0 +1,104 @@
1
+ # -*- coding: binary -*-
2
+
3
+ require 'tempfile'
4
+ require 'rex/file'
5
+ require 'rex/text'
6
+
7
+ module Rex
8
+ module Assembly
9
+
10
+ ###
11
+ #
12
+ # This class uses assembly to assemble and disassemble stuff.
13
+ #
14
+ ###
15
+ class Nasm
16
+
17
+ @@nasm_path = 'assembly'
18
+ @@ndisasm_path = 'ndisasm'
19
+
20
+ #
21
+ # Ensures that the assembly environment is sane.
22
+ #
23
+ def self.check
24
+ @@nasm_path =
25
+ Rex::FileUtils.find_full_path('assembly') ||
26
+ Rex::FileUtils.find_full_path('assembly.exe') ||
27
+ Rex::FileUtils.find_full_path('nasmw.exe') ||
28
+ raise(RuntimeError, "No assembly installation was found.")
29
+
30
+ @@ndisasm_path =
31
+ Rex::FileUtils.find_full_path('ndisasm') ||
32
+ Rex::FileUtils.find_full_path('ndisasm.exe') ||
33
+ Rex::FileUtils.find_full_path('ndisasmw.exe') ||
34
+ raise(RuntimeError, "No ndisasm installation was found.")
35
+ end
36
+
37
+ #
38
+ # Assembles the supplied assembly and returns the raw opcodes.
39
+ #
40
+ def self.assemble(assembly, bits=32)
41
+ check
42
+
43
+ # Open the temporary file
44
+ tmp = Tempfile.new('nasmXXXX')
45
+ tmp.binmode
46
+
47
+ tpath = tmp.path
48
+ opath = tmp.path + '.out'
49
+
50
+ # Write the assembly data to a file
51
+ tmp.write("BITS #{bits}\n" + assembly)
52
+ tmp.flush()
53
+ tmp.seek(0)
54
+
55
+ # Run assembly
56
+ if (system(@@nasm_path, '-f', 'bin', '-o', opath, tpath) == false)
57
+ raise RuntimeError, "Assembler did not complete successfully: #{$?.exitstatus}"
58
+ end
59
+
60
+ # Read the assembled text
61
+ rv = ::IO.read(opath)
62
+
63
+ # Remove temporary files
64
+ File.unlink(opath)
65
+ tmp.close(true)
66
+
67
+ rv
68
+ end
69
+
70
+ #
71
+ # Disassembles the supplied raw opcodes
72
+ #
73
+ def self.disassemble(raw, bits=32)
74
+ check
75
+
76
+ tmp = Tempfile.new('nasmout')
77
+ tmp.binmode
78
+
79
+ tfd = File.open(tmp.path, "wb")
80
+
81
+ tfd.write(raw)
82
+ tfd.flush()
83
+ tfd.close
84
+
85
+ p = ::IO.popen("\"#{@@ndisasm_path}\" -b #{bits} \"#{tmp.path}\"")
86
+ o = ''
87
+
88
+ begin
89
+ until p.eof?
90
+ o += p.read
91
+ end
92
+ ensure
93
+ p.close
94
+ end
95
+
96
+ tmp.close(true)
97
+
98
+ o
99
+ end
100
+
101
+ end
102
+
103
+ end
104
+ end
@@ -0,0 +1,13 @@
1
+ require "rex/bin_tools/version"
2
+ require 'rex/image_source'
3
+ require 'rex/elfparsey'
4
+ require 'rex/elfscan'
5
+ require 'rex/machparsey'
6
+ require 'rex/machscan'
7
+ require 'rex/peparsey'
8
+ require 'rex/pescan'
9
+
10
+ module Rex
11
+ module BinTools
12
+ end
13
+ end
@@ -0,0 +1,5 @@
1
+ module Rex
2
+ module BinTools
3
+ VERSION = "0.1.0"
4
+ end
5
+ end
@@ -0,0 +1,9 @@
1
+ # -*- coding: binary -*-
2
+
3
+ module Rex
4
+ module ElfParsey
5
+
6
+ end
7
+ end
8
+
9
+ require 'rex/elfparsey/elf'
@@ -0,0 +1,121 @@
1
+ # -*- coding: binary -*-
2
+
3
+ require 'rex/elfparsey/elfbase'
4
+ require 'rex/elfparsey/exceptions'
5
+ require 'rex/image_source'
6
+
7
+ module Rex
8
+ module ElfParsey
9
+ class Elf < ElfBase
10
+
11
+ attr_accessor :elf_header, :program_header, :base_addr, :isource
12
+
13
+ def initialize(isource)
14
+ offset = 0
15
+ base_addr = 0
16
+
17
+ # ELF Header
18
+ elf_header = ElfHeader.new(isource.read(offset, ELF_HEADER_SIZE))
19
+
20
+ # Data encoding
21
+ ei_data = elf_header.e_ident[EI_DATA,1].unpack("C")[0]
22
+
23
+ e_phoff = elf_header.e_phoff
24
+ e_phentsize = elf_header.e_phentsize
25
+ e_phnum = elf_header.e_phnum
26
+
27
+ # Program Header Table
28
+ program_header = []
29
+
30
+ e_phnum.times do |i|
31
+ offset = e_phoff + (e_phentsize * i)
32
+
33
+ program_header << ProgramHeader.new(
34
+ isource.read(offset, PROGRAM_HEADER_SIZE), ei_data
35
+ )
36
+
37
+ if program_header[-1].p_type == PT_LOAD && program_header[-1].p_flags & PF_EXEC > 0
38
+ base_addr = program_header[-1].p_vaddr
39
+ end
40
+
41
+ end
42
+
43
+ self.elf_header = elf_header
44
+ self.program_header = program_header
45
+ self.base_addr = base_addr
46
+ self.isource = isource
47
+ end
48
+
49
+ def self.new_from_file(filename, disk_backed = false)
50
+
51
+ file = ::File.new(filename)
52
+ # file.binmode # windows... :\
53
+
54
+ if disk_backed
55
+ return self.new(ImageSource::Disk.new(file))
56
+ else
57
+ obj = new_from_string(file.read)
58
+ file.close
59
+ return obj
60
+ end
61
+ end
62
+
63
+ def self.new_from_string(data)
64
+ return self.new(ImageSource::Memory.new(data))
65
+ end
66
+
67
+ #
68
+ # Returns true if this binary is for a 64-bit architecture.
69
+ #
70
+ def ptr_64?
71
+ unless [ ELFCLASS32, ELFCLASS64 ].include?(
72
+ elf_header.e_ident[EI_CLASS,1].unpack("C*")[0])
73
+ raise ElfHeaderError, 'Invalid class', caller
74
+ end
75
+
76
+ elf_header.e_ident[EI_CLASS,1].unpack("C*")[0] == ELFCLASS64
77
+ end
78
+
79
+ #
80
+ # Returns true if this binary is for a 32-bit architecture.
81
+ # This check does not take into account 16-bit binaries at the moment.
82
+ #
83
+ def ptr_32?
84
+ ptr_64? == false
85
+ end
86
+
87
+ #
88
+ # Converts a virtual address to a string representation based on the
89
+ # underlying architecture.
90
+ #
91
+ def ptr_s(rva)
92
+ (ptr_32?) ? ("0x%.8x" % rva) : ("0x%.16x" % rva)
93
+ end
94
+
95
+ def offset_to_rva(offset)
96
+ base_addr + offset
97
+ end
98
+
99
+ def rva_to_offset(rva)
100
+ rva - base_addr
101
+ end
102
+
103
+ def read(offset, len)
104
+ isource.read(offset, len)
105
+ end
106
+
107
+ def read_rva(rva, len)
108
+ isource.read(rva_to_offset(rva), len)
109
+ end
110
+
111
+ def index(*args)
112
+ isource.index(*args)
113
+ end
114
+
115
+ def close
116
+ isource.close
117
+ end
118
+
119
+ end
120
+ end
121
+ end
@@ -0,0 +1,265 @@
1
+ # -*- coding: binary -*-
2
+
3
+ require 'rex/struct2'
4
+
5
+ module Rex
6
+ module ElfParsey
7
+ class ElfBase
8
+
9
+ # ELF Header
10
+
11
+ ELF_HEADER_SIZE = 52
12
+
13
+ EI_NIDENT = 16
14
+
15
+ ELF32_EHDR_LSB = Rex::Struct2::CStructTemplate.new(
16
+ [ 'string', 'e_ident', EI_NIDENT, '' ],
17
+ [ 'uint16v', 'e_type', 0 ],
18
+ [ 'uint16v', 'e_machine', 0 ],
19
+ [ 'uint32v', 'e_version', 0 ],
20
+ [ 'uint32v', 'e_entry', 0 ],
21
+ [ 'uint32v', 'e_phoff', 0 ],
22
+ [ 'uint32v', 'e_shoff', 0 ],
23
+ [ 'uint32v', 'e_flags', 0 ],
24
+ [ 'uint16v', 'e_ehsize', 0 ],
25
+ [ 'uint16v', 'e_phentsize', 0 ],
26
+ [ 'uint16v', 'e_phnum', 0 ],
27
+ [ 'uint16v', 'e_shentsize', 0 ],
28
+ [ 'uint16v', 'e_shnum', 0 ],
29
+ [ 'uint16v', 'e_shstrndx', 0 ]
30
+ )
31
+
32
+ ELF32_EHDR_MSB = Rex::Struct2::CStructTemplate.new(
33
+ [ 'string', 'e_ident', EI_NIDENT, '' ],
34
+ [ 'uint16n', 'e_type', 0 ],
35
+ [ 'uint16n', 'e_machine', 0 ],
36
+ [ 'uint32n', 'e_version', 0 ],
37
+ [ 'uint32n', 'e_entry', 0 ],
38
+ [ 'uint32n', 'e_phoff', 0 ],
39
+ [ 'uint32n', 'e_shoff', 0 ],
40
+ [ 'uint32n', 'e_flags', 0 ],
41
+ [ 'uint16n', 'e_ehsize', 0 ],
42
+ [ 'uint16n', 'e_phentsize', 0 ],
43
+ [ 'uint16n', 'e_phnum', 0 ],
44
+ [ 'uint16n', 'e_shentsize', 0 ],
45
+ [ 'uint16n', 'e_shnum', 0 ],
46
+ [ 'uint16n', 'e_shstrndx', 0 ]
47
+ )
48
+
49
+ # e_type This member identifies the object file type
50
+
51
+ ET_NONE = 0 # No file type
52
+ ET_REL = 1 # Relocatable file
53
+ ET_EXEC = 2 # Executable file
54
+ ET_DYN = 3 # Shared object file
55
+ ET_CORE = 4 # Core file
56
+ ET_LOPROC = 0xff00 # Processor-specific
57
+ ET_HIPROC = 0xffff # Processor-specific
58
+
59
+ #
60
+ # e_machine This member's value specifies the required architecture for an
61
+ # individual file.
62
+ #
63
+
64
+ # ET_NONE = 0 # No machine
65
+ EM_M32 = 1 # AT&T WE 32100
66
+ EM_SPARC = 2 # SPARC
67
+ EM_386 = 3 # Intel Architecture
68
+ EM_68K = 4 # Motorola 68000
69
+ EM_88K = 5 # Motorola 88000
70
+ EM_860 = 7 # Intel 80860
71
+ EM_MIPS = 8 # MIPS RS3000 Big-Endian
72
+ EM_MIPS_RS4_BE = 10 # MIPS RS4000 Big-Endian
73
+
74
+ # e_version This member identifies the object file version
75
+
76
+ EV_NONE = 0 # Invalid version
77
+ EV_CURRENT = 1 # Current version
78
+
79
+
80
+ # ELF Identification
81
+
82
+ # e_ident[] Identification indexes
83
+
84
+ EI_MAG0 = 0 # File identification
85
+ EI_MAG1 = 1 # File identification
86
+ EI_MAG2 = 2 # File identification
87
+ EI_MAG3 = 3 # File identification
88
+ EI_CLASS = 4 # File class
89
+ EI_DATA = 5 # Data encoding
90
+ EI_VERSION = 6 # File version
91
+ EI_PAD = 7 # Start of padding bytes
92
+ # EI_NIDENT = 16 # Size of e_ident[]
93
+
94
+ #
95
+ # EI_MAG0 to EI_MAG3 A file's first 4 bytes hold a "magic number",
96
+ # identifying the file as an ELF object file.
97
+ #
98
+
99
+ ELFMAG0 = 0x7f # e_ident[EI_MAG0]
100
+ ELFMAG1 = ?E # e_ident[EI_MAG1]
101
+ ELFMAG2 = ?L # e_ident[EI_MAG2]
102
+ ELFMAG3 = ?F # e_ident[EI_MAG3]
103
+
104
+ ELFMAG = ELFMAG0.chr + ELFMAG1.chr + ELFMAG2.chr + ELFMAG3.chr
105
+
106
+ # EI_CLASS Identifies the file's class, or capacity
107
+
108
+ ELFCLASSNONE = 0 # Invalid class
109
+ ELFCLASS32 = 1 # 32-bit objects
110
+ ELFCLASS64 = 2 # 64-bit objects
111
+
112
+ #
113
+ # EI_DATA Specifies the data encoding of the processor-specific data in
114
+ # the object file. The following encodings are currently defined.
115
+ #
116
+
117
+ ELFDATANONE = 0 # Invalid data encoding
118
+ ELFDATA2LSB = 1 # Least significant byte first
119
+ ELFDATA2MSB = 2 # Most significant byte first
120
+
121
+ class GenericStruct
122
+ attr_accessor :struct
123
+ def initialize(_struct)
124
+ self.struct = _struct
125
+ end
126
+
127
+ # The following methods are just pass-throughs for struct
128
+
129
+ # Access a value
130
+ def v
131
+ struct.v
132
+
133
+ end
134
+
135
+ # Access a value by array
136
+ def [](*args)
137
+ struct[*args]
138
+ end
139
+
140
+ # Obtain an array of all fields
141
+ def keys
142
+ struct.keys
143
+ end
144
+
145
+ def method_missing(meth, *args)
146
+ v[meth.to_s] || (raise NoMethodError.new, meth)
147
+ end
148
+ end
149
+
150
+ class GenericHeader < GenericStruct
151
+ end
152
+
153
+ class ElfHeader < GenericHeader
154
+ def initialize(rawdata)
155
+
156
+ # Identify the data encoding and parse ELF Header
157
+ elf_header = ELF32_EHDR_LSB.make_struct
158
+
159
+ if !elf_header.from_s(rawdata)
160
+ raise ElfHeaderError, "Couldn't parse ELF Header", caller
161
+ end
162
+
163
+ if elf_header.v['e_ident'][EI_DATA,1].unpack('C')[0] == ELFDATA2MSB
164
+ elf_header = ELF32_EHDR_MSB.make_struct
165
+
166
+ if !elf_header.from_s(rawdata)
167
+ raise ElfHeaderError, "Couldn't parse ELF Header", caller
168
+ end
169
+ end
170
+
171
+ unless [ ELFDATA2LSB, ELFDATA2MSB ].include?(
172
+ elf_header.v['e_ident'][EI_DATA,1].unpack('C')[0])
173
+ raise ElfHeaderError, "Invalid data encoding", caller
174
+ end
175
+
176
+ # Identify the file as an ELF object file
177
+ unless elf_header.v['e_ident'][EI_MAG0, 4] == ELFMAG
178
+ raise ElfHeaderError, 'Invalid magic number', caller
179
+ end
180
+
181
+ self.struct = elf_header
182
+ end
183
+
184
+ def e_ident
185
+ struct.v['e_ident']
186
+ end
187
+
188
+ end
189
+
190
+
191
+ # Program Header
192
+
193
+ PROGRAM_HEADER_SIZE = 32
194
+
195
+ ELF32_PHDR_LSB = Rex::Struct2::CStructTemplate.new(
196
+ [ 'uint32v', 'p_type', 0 ],
197
+ [ 'uint32v', 'p_offset', 0 ],
198
+ [ 'uint32v', 'p_vaddr', 0 ],
199
+ [ 'uint32v', 'p_paddr', 0 ],
200
+ [ 'uint32v', 'p_filesz', 0 ],
201
+ [ 'uint32v', 'p_memsz', 0 ],
202
+ [ 'uint32v', 'p_flags', 0 ],
203
+ [ 'uint32v', 'p_align', 0 ]
204
+ )
205
+
206
+ ELF32_PHDR_MSB = Rex::Struct2::CStructTemplate.new(
207
+ [ 'uint32n', 'p_type', 0 ],
208
+ [ 'uint32n', 'p_offset', 0 ],
209
+ [ 'uint32n', 'p_vaddr', 0 ],
210
+ [ 'uint32n', 'p_paddr', 0 ],
211
+ [ 'uint32n', 'p_filesz', 0 ],
212
+ [ 'uint32n', 'p_memsz', 0 ],
213
+ [ 'uint32n', 'p_flags', 0 ],
214
+ [ 'uint32n', 'p_align', 0 ]
215
+ )
216
+
217
+ # p_flags This member tells which permissions should have the segment
218
+
219
+ # Flags
220
+
221
+ PF_EXEC = 1
222
+ PF_WRITE = 2
223
+ PF_READ = 4
224
+
225
+
226
+ #
227
+ # p_type This member tells what kind of segment this array element
228
+ # describes or how to interpret the array element's information.
229
+ #
230
+
231
+ # Segment Types
232
+
233
+ PT_NULL = 0
234
+ PT_LOAD = 1
235
+ PT_DYNAMIC = 2
236
+ PT_INTERP = 3
237
+ PT_NOTE = 4
238
+ PT_SHLIB = 5
239
+ PT_PHDR = 6
240
+ PT_LOPROC = 0x70000000
241
+ PT_HIPROC = 0x7fffffff
242
+
243
+ class ProgramHeader < GenericHeader
244
+ def initialize(rawdata, ei_data)
245
+ # Identify the data encoding and parse Program Header
246
+ if ei_data == ELFDATA2LSB
247
+ program_header = ELF32_PHDR_LSB.make_struct
248
+ elsif ei_data == ELFDATA2MSB
249
+ program_header = ELF32_PHDR_MSB.make_struct
250
+ else
251
+ raise ElfHeaderError, "Invalid data encoding", caller
252
+ end
253
+
254
+ if !program_header.from_s(rawdata)
255
+ raise ProgramHeaderError, "Couldn't parse Program Header", caller
256
+ end
257
+
258
+ self.struct = program_header
259
+ end
260
+
261
+ end
262
+
263
+ end
264
+ end
265
+ end