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,128 @@
1
+ # -*- coding: binary -*-
2
+
3
+ require 'rex/peparsey/exceptions'
4
+ require 'rex/peparsey/pebase'
5
+ require 'rex/struct2'
6
+
7
+ module Rex
8
+ module PeParsey
9
+ class Section
10
+ attr_accessor :_section_header, :_isource
11
+ attr_accessor :base_rva
12
+
13
+ #
14
+ # Initialize a section.
15
+ #
16
+ # isource - The ImageSource class backing the image
17
+ # base_vma - The address of this section base
18
+ # section_header - The section header (struct2) although this is not
19
+ # required, which is why there is a base_vma. This can be nil.
20
+ #
21
+ def initialize(isource, base_rva, section_header = nil)
22
+ self._isource = isource
23
+ self.base_rva = base_rva
24
+ self._section_header = section_header
25
+ end
26
+
27
+ def file_offset
28
+ _isource.file_offset
29
+ end
30
+
31
+ def size
32
+ _isource.size
33
+ end
34
+
35
+ def name
36
+ # a section header is not required
37
+ return nil if !_section_header
38
+
39
+ # FIXME make this better...
40
+ _section_header.v['Name'].gsub(/\x00+$/n, '')
41
+ end
42
+
43
+ def flags
44
+ # a section header is not required
45
+ return nil if !_section_header
46
+ _section_header.v['Characteristics']
47
+ end
48
+
49
+ def vma
50
+ # a section header is not required
51
+ return nil if !_section_header
52
+ _section_header.v['VirtualAddress']
53
+ end
54
+
55
+ def raw_size
56
+ # a section header is not required
57
+ return nil if !_section_header
58
+ _section_header.v['SizeOfRawData']
59
+ end
60
+
61
+ def _check_offset(offset, len = 1)
62
+ if offset < 0 || offset+len > size
63
+ raise BoundsError, "Offset #{offset} outside of section", caller
64
+ end
65
+ end
66
+
67
+ def read(offset, len)
68
+ _check_offset(offset, len)
69
+ return _isource.read(offset, len)
70
+ end
71
+
72
+ def read_rva(rva, len)
73
+ return read(rva_to_offset(rva), len)
74
+ end
75
+
76
+ def read_asciiz(offset)
77
+ _check_offset(offset)
78
+ return _isource.read_asciiz(offset)
79
+ end
80
+
81
+ def read_asciiz_rva(rva)
82
+ return read_asciiz(rva_to_offset(rva))
83
+ end
84
+
85
+ def index(*args)
86
+ _isource.index(*args)
87
+ end
88
+
89
+ def offset_to_rva(offset)
90
+ if !contains_offset?(offset)
91
+ raise BoundsError, "Offset #{offset} outside of section", caller
92
+ end
93
+
94
+ return offset + base_rva
95
+ end
96
+
97
+ def file_offset_to_rva(foffset)
98
+ return offset_to_rva(foffset - file_offset)
99
+ end
100
+
101
+ def rva_to_offset(rva)
102
+ offset = rva - base_rva
103
+ if !contains_offset?(offset)
104
+ raise BoundsError, "RVA #{rva} outside of section", caller
105
+ end
106
+
107
+ return offset
108
+ end
109
+
110
+ def rva_to_file_offset(rva)
111
+ return rva_to_offset(rva) + file_offset
112
+ end
113
+
114
+ def contains_offset?(offset)
115
+ offset >= 0 && offset < size
116
+ end
117
+
118
+ def contains_file_offset?(foffset)
119
+ contains_offset?(foffset - file_offset)
120
+ end
121
+
122
+ def contains_rva?(rva)
123
+ contains_offset?(rva - base_rva)
124
+ end
125
+
126
+ end
127
+
128
+ end end
@@ -0,0 +1,11 @@
1
+ # -*- coding: binary -*-
2
+
3
+ module Rex
4
+ module PeScan
5
+
6
+ end
7
+ end
8
+
9
+ require 'rex/pescan/analyze'
10
+ require 'rex/pescan/scanner'
11
+ require 'rex/pescan/search'
@@ -0,0 +1,366 @@
1
+ # -*- coding: binary -*-
2
+ module Rex
3
+ module PeScan
4
+ module Analyze
5
+
6
+ require "rex/text/table"
7
+
8
+ class Fingerprint
9
+ attr_accessor :pe
10
+
11
+ def initialize(pe)
12
+ self.pe = pe
13
+ end
14
+
15
+ def config(param)
16
+ @sigs = {}
17
+
18
+ name = nil
19
+ regx = ''
20
+ epon = 0
21
+ sidx = 0
22
+
23
+ fd = File.open(param['database'], 'rb')
24
+ fd.each_line do |line|
25
+ case line
26
+ when /^\s*#/
27
+ next
28
+ when /\[\s*(.*)\s*\]/
29
+ if (name)
30
+ @sigs[ name ] = [regx, epon]
31
+ end
32
+ name = $1 + " [#{ sidx+=1 }]"
33
+ epon = 0
34
+ next
35
+ when /signature\s*=\s*(.*)/
36
+ pat = $1.strip
37
+ regx = ''
38
+ pat.split(/\s+/).each do |c|
39
+ next if c.length != 2
40
+ regx << (c.index('?') ? '.' : "\\x#{c}")
41
+ end
42
+ when /ep_only\s*=\s*(.*)/
43
+ epon = ($1 =~ /^T/i) ? 1 : 0
44
+ end
45
+ end
46
+
47
+ if (name and ! @sigs[name])
48
+ @sigs[ name ] = [regx, epon]
49
+ end
50
+
51
+ fd.close
52
+ end
53
+
54
+ def scan(param)
55
+ config(param)
56
+
57
+ epa = pe.hdr.opt.AddressOfEntryPoint
58
+ buf = pe.read_rva(epa, 256) || ""
59
+
60
+ @sigs.each_pair do |name, data|
61
+ begin
62
+ if (buf.match(Regexp.new('^' + data[0], nil, 'n')))
63
+ $stdout.puts param['file'] + ": " + name
64
+ end
65
+ rescue RegexpError
66
+ $stderr.puts "Invalid signature: #{name} #{data[0]}"
67
+ end
68
+ end
69
+ end
70
+ end
71
+
72
+ class Information
73
+ attr_accessor :pe
74
+
75
+ def initialize(pe)
76
+ self.pe = pe
77
+ end
78
+
79
+ def add_fields(tbl, obj, fields)
80
+ fields.each do |name|
81
+ begin
82
+ tbl << [name, "0x%.8x" % obj.send(name)]
83
+ rescue ::NoMethodError => e
84
+ $stderr.puts "Invalid field #{name}"
85
+ end
86
+ end
87
+ end
88
+
89
+ def scan(param)
90
+
91
+ $stdout.puts "\n\n"
92
+
93
+ tbl = table("Image Headers", ['Name', 'Value'])
94
+ add_fields(tbl, pe.hdr.file, %W{
95
+ Characteristics
96
+ SizeOfOptionalHeader
97
+ PointerToSymbolTable
98
+ TimeDateStamp
99
+ NumberOfSections
100
+ Machine
101
+ })
102
+ $stdout.puts tbl.to_s
103
+ $stdout.puts "\n\n"
104
+
105
+ tbl = table("Optional Image Headers", ['Name', 'Value'])
106
+ add_fields(tbl, pe.hdr.opt, %W{
107
+ ImageBase
108
+ Magic
109
+ MajorLinkerVersion
110
+ MinorLinkerVersion
111
+ SizeOfCode
112
+ SizeOfInitializeData
113
+ SizeOfUninitializeData
114
+ AddressOfEntryPoint
115
+ BaseOfCode
116
+ BaseOfData
117
+ SectionAlignment
118
+ FileAlignment
119
+ MajorOperatingSystemVersion
120
+ MinorOperatingSystemVersion
121
+ MajorImageVersion
122
+ MinorImageVersion
123
+ MajorSubsystemVersion
124
+ MinorSubsystemVersion
125
+ Win32VersionValue
126
+ SizeOfImage
127
+ SizeOfHeaders
128
+ CheckSum
129
+ Subsystem
130
+ DllCharacteristics
131
+ SizeOfStackReserve
132
+ SizeOfStackCommit
133
+ SizeOfHeapReserve
134
+ SizeOfHeapCommit
135
+ LoaderFlags
136
+ NumberOfRvaAndSizes
137
+ })
138
+
139
+ $stdout.puts tbl.to_s
140
+ $stdout.puts "\n\n"
141
+
142
+ # Get DllCharacteristics (in Integer)
143
+ dllcharacteristics = pe.hdr.opt.struct[23].value
144
+
145
+ if (dllcharacteristics > 0)
146
+ tbl = table("DllCharacteristics", ['Flag', 'Value'])
147
+
148
+ # http://msdn.microsoft.com/en-us/library/ms680339(v=vs.85).aspx
149
+ traits = {
150
+ :ASLR => 'False', #IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE
151
+ :Integrity => 'False', #IMAGE_DLLCHARACTERISTICS_FORCE_INTEGRITY
152
+ :NX => 'False', #IMAGE_DLLCHARACTERISTICS_NX_COMPAT
153
+ :Isolation => 'False', #IMAGE_DLLCHARACTERISTICS_NO_ISOLATION
154
+ :SEH => 'False', #IMAGE_DLLCHARACTERISTICS_NO_SEH
155
+ :Bind => 'False', #IMAGE_DLLCHARACTERISTICS_NO_BIND
156
+ :WDM => 'False', #IMAGE_DLLCHARACTERISTICS_WDM_DRIVER
157
+ :Terminal => 'False' #IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE
158
+ }
159
+
160
+ # Convert integer to an bit array
161
+ c_bits = ("%32d" %dllcharacteristics.to_s(2)).split('').map { |e| e.to_i }.reverse
162
+
163
+ # Check characteristics
164
+ traits[:ASLR] = 'True' if c_bits[6] == 1 #0x0040
165
+ traits[:Integrity] = 'True' if c_bits[7] == 1 #0x0080
166
+ traits[:NX] = 'True' if c_bits[8] == 1 #0x0100
167
+ traits[:Isolation] = 'True' if c_bits[9] == 1 #0x0200
168
+ traits[:SEH] = 'True' if c_bits[10] == 1 #0x0400
169
+ traits[:Bind] = 'True' if c_bits[11] == 1 #0x0800
170
+ traits[:WDM] = 'True' if c_bits[13] == 1 #2000
171
+ traits[:Terminal] = 'True' if c_bits[15] == 1 #0x8000
172
+
173
+ # Putting results to table
174
+ traits.each do |trait_name, trait_value|
175
+ tbl << [trait_name, trait_value]
176
+ end
177
+
178
+ $stdout.puts tbl.to_s
179
+ $stdout.puts "\n\n"
180
+ end
181
+
182
+ if (pe.exports)
183
+ tbl = table("Exported Functions", ['Ordinal', 'Name', 'Address'])
184
+ pe.exports.entries.each do |ent|
185
+ tbl << [ent.ordinal, ent.name, "0x%.8x" % pe.rva_to_vma(ent.rva)]
186
+ end
187
+ $stdout.puts tbl.to_s
188
+ $stdout.puts "\n\n"
189
+ end
190
+
191
+ # Rex::PeParsey::Pe doesn't seem to give us any offset information for each function,
192
+ # which makes it difficult to calculate the actual addresses for them. So instead we
193
+ # are using Metasm::COFF::ImportDirectory to do this task. The ability to see
194
+ # addresses is mainly for ROP.
195
+ if (pe.imports)
196
+ tbl = table("Imported Functions", ['Library', 'Address', 'Ordinal', 'Name'])
197
+ exefmt = Metasm::AutoExe.orshellcode{ Metasm.const_get('x86_64').new }
198
+ exe = exefmt.decode_file(pe._isource.file.path)
199
+ ibase = pe.image_base
200
+ exe_imports = exe.imports
201
+ exe_imports.each do |lib|
202
+ lib_name = lib.libname
203
+ ini_offset = lib.iat_p
204
+ func_table = lib.imports
205
+ offset = 0
206
+ func_table.each do |func|
207
+ func_addr = "0x%08x" %(ibase + ini_offset + offset)
208
+ tbl << [lib_name, func_addr, func.hint, func.name]
209
+ offset += 4
210
+ end
211
+ end
212
+
213
+ $stdout.puts tbl.to_s
214
+ $stdout.puts "\n\n"
215
+ end
216
+
217
+ if(pe.config)
218
+ tbl = table("Configuration Header", ['Name', 'Value'])
219
+ add_fields(tbl, pe.config, %W{
220
+ Size
221
+ TimeDateStamp
222
+ MajorVersion
223
+ MinorVersion
224
+ GlobalFlagsClear
225
+ GlobalFlagsSet
226
+ CriticalSectionDefaultTimeout
227
+ DeCommitFreeBlockThreshold
228
+ DeCommitTotalFreeThreshold
229
+ LockPrefixTable
230
+ MaximumAllocationSize
231
+ VirtualMemoryThreshold
232
+ ProcessAffinityMask
233
+ ProcessHeapFlags
234
+ CSDVersion
235
+ Reserved1
236
+ EditList
237
+ SecurityCookie
238
+ SEHandlerTable
239
+ SEHandlerCount
240
+ })
241
+ $stdout.puts tbl.to_s
242
+ $stdout.puts "\n\n"
243
+ end
244
+
245
+
246
+ if(pe.resources)
247
+ tbl = table("Resources", ['ID', 'Language', 'Code Page', 'Size', 'Name'])
248
+ pe.resources.keys.sort.each do |rkey|
249
+ res = pe.resources[rkey]
250
+ tbl << [rkey, res.lang, res.code, res.size, res.file]
251
+ end
252
+ $stdout.puts tbl.to_s
253
+ $stdout.puts "\n\n"
254
+ end
255
+
256
+ tbl = table("Section Header", ["Name", "VirtualAddress", "SizeOfRawData", "Characteristics"])
257
+ pe.sections.each do |sec|
258
+ tbl << [ sec.name, *[sec.vma, sec.raw_size, sec.flags].map{|x| "0x%.8x" % x} ]
259
+ end
260
+ $stdout.puts tbl.to_s
261
+ $stdout.puts "\n\n"
262
+
263
+ end
264
+
265
+ def table(name, cols)
266
+ Rex::Text::Table.new(
267
+ 'Header' => name,
268
+ 'Columns' => cols
269
+ )
270
+ end
271
+ end
272
+
273
+
274
+ class Ripper
275
+
276
+ require "fileutils"
277
+
278
+ attr_accessor :pe
279
+
280
+ def initialize(pe)
281
+ self.pe = pe
282
+ end
283
+
284
+ def scan(param)
285
+ dest = param['dir']
286
+
287
+ if (param['file'])
288
+ dest = File.join(dest, File.basename(param['file']))
289
+ end
290
+
291
+ ::FileUtils.mkdir_p(dest)
292
+
293
+ pe.resources.keys.sort.each do |rkey|
294
+ res = pe.resources[rkey]
295
+ path = File.join(dest, rkey.split('/')[1] + '_' + res.file)
296
+
297
+ fd = File.new(path, 'wb')
298
+ fd.write(res.data)
299
+ fd.close
300
+ end
301
+ end
302
+ end
303
+
304
+ class ContextMapDumper
305
+
306
+ attr_accessor :pe
307
+
308
+ def initialize(pe)
309
+ self.pe = pe
310
+ end
311
+
312
+ def scan(param)
313
+ dest = param['dir']
314
+ path = ''
315
+
316
+ ::FileUtils.mkdir_p(dest)
317
+
318
+ if(not (param['dir'] and param['file']))
319
+ $stderr.puts "No directory or file specified"
320
+ return
321
+ end
322
+
323
+ if (param['file'])
324
+ path = File.join(dest, File.basename(param['file']) + ".map")
325
+ end
326
+
327
+ fd = File.new(path, "wb")
328
+ pe.all_sections.each do |section|
329
+
330
+ # Skip over known bad sections
331
+ next if section.name == ".data"
332
+ next if section.name == ".reloc"
333
+
334
+ offset = 0
335
+ while offset < section.size
336
+ byte = section.read(offset, 1)[0]
337
+ if byte != 0
338
+ chunkbase = pe.rva_to_vma(section.base_rva) + offset
339
+ data = ''
340
+ while byte != 0
341
+ data << byte
342
+ offset += 1
343
+ byte = 0
344
+ byte = section.read(offset, 1)[0] if offset < section.size
345
+ end
346
+ buff = nil
347
+ buff = [ 0x01, chunkbase, data.length, data].pack("CNNA*") if data.length > 0
348
+
349
+ fd.write(buff) if buff
350
+ end
351
+ offset += 1
352
+ end
353
+
354
+ end
355
+
356
+
357
+ fd.close
358
+ end
359
+ end
360
+
361
+ # EOC
362
+
363
+ end
364
+ end
365
+ end
366
+