rex-bin_tools 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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,9 @@
1
+ # -*- coding: binary -*-
2
+
3
+ module Rex
4
+ module MachScan
5
+
6
+ end
7
+ end
8
+
9
+ require 'rex/machscan/scanner'
@@ -0,0 +1,217 @@
1
+ # -*- coding: binary -*-
2
+
3
+ module Rex
4
+ module MachScan
5
+ module Scanner
6
+ class Generic
7
+
8
+ attr_accessor :mach, :fat, :regex
9
+
10
+ def initialize(binary)
11
+ if binary.class == Rex::MachParsey::Mach
12
+ self.mach = binary
13
+ else
14
+ self.fat = binary
15
+ end
16
+ end
17
+
18
+ def config(param)
19
+ end
20
+
21
+ def scan(param)
22
+ config(param)
23
+
24
+ $stdout.puts "[#{param['file']}]"
25
+
26
+ if !self.mach
27
+ for mach in fat.machos
28
+ if mach.mach_header.cputype == 0x7 #since we only support intel for the time being its all we process
29
+ self.mach = mach
30
+ end
31
+ end
32
+ end
33
+
34
+ self.mach.segments.each do |segment|
35
+ if segment.segname.include? "__TEXT"
36
+ scan_segment(segment, param).each do |hit|
37
+ vaddr = hit[0]
38
+ message = hit[1].is_a?(Array) ? hit[1].join(" ") : hit[1]
39
+ $stdout.puts self.mach.ptr_s(vaddr - self.mach.fat_offset) + " " + message
40
+ end
41
+ end
42
+ end
43
+
44
+ end
45
+
46
+ def scan_segment(segment, param={})
47
+ []
48
+ end
49
+ end
50
+
51
+ class JmpRegScanner < Generic
52
+
53
+ def config(param)
54
+ regnums = param['args']
55
+
56
+ # build a list of the call bytes
57
+ calls = _build_byte_list(0xd0, regnums - [4]) # note call esp's don't work..
58
+ jmps = _build_byte_list(0xe0, regnums)
59
+ pushs1 = _build_byte_list(0x50, regnums)
60
+ pushs2 = _build_byte_list(0xf0, regnums)
61
+
62
+ regexstr = '('
63
+ if !calls.empty?
64
+ regexstr += "\xff[#{calls}]|"
65
+ end
66
+
67
+ regexstr += "\xff[#{jmps}]|([#{pushs1}]|\xff[#{pushs2}])(\xc3|\xc2..))"
68
+
69
+ self.regex = Regexp.new(regexstr, nil, 'n')
70
+ end
71
+
72
+ # build a list for regex of the possible bytes, based on a base
73
+ # byte and a list of register numbers..
74
+ def _build_byte_list(base, regnums)
75
+ regnums.collect { |regnum| Regexp.escape((base | regnum).chr) }.join('')
76
+ end
77
+
78
+ def _ret_size(offset)
79
+ case mach.read(offset, 1)
80
+ when "\xc3"
81
+ return 1
82
+ when "\xc2"
83
+ return 3
84
+ end
85
+ $stderr.puts("Invalid return instruction")
86
+ end
87
+
88
+ def _parse_ret(data)
89
+ if data.length == 1
90
+ return "ret"
91
+ else
92
+ return "retn 0x%04x" % data[1, 2].unpack('v')[0]
93
+ end
94
+ end
95
+
96
+ def scan_segment(segment, param={})
97
+ base_addr = segment.vmaddr
98
+ segment_offset = segment.fileoff
99
+ offset = segment_offset
100
+
101
+ hits = []
102
+
103
+ while (offset = mach.index(regex, offset)) != nil
104
+
105
+ vaddr = base_addr + (offset - segment_offset)
106
+ message = ''
107
+
108
+ parse_ret = false
109
+
110
+ byte1 = mach.read(offset, 1).unpack("C*")[0]
111
+
112
+ if byte1 == 0xff
113
+ byte2 = mach.read(offset+1, 1).unpack("C*")[0]
114
+ regname = Rex::Arch::X86.reg_name32(byte2 & 0x7)
115
+
116
+ case byte2 & 0xf8
117
+ when 0xd0
118
+ message = "call #{regname}"
119
+ offset += 2
120
+ when 0xe0
121
+ message = "jmp #{regname}"
122
+ offset += 2
123
+ when 0xf0
124
+ retsize = _ret_size(offset+2)
125
+ message = "push #{regname}; " + _parse_ret(mach.read(offset+2, retsize))
126
+ offset += 2 + retsize
127
+ else
128
+ raise "Unexpected value at offset: #{offset}"
129
+ end
130
+ else
131
+ regname = Rex::Arch::X86.reg_name32(byte1 & 0x7)
132
+ retsize = _ret_size(offset+1)
133
+ message = "push #{regname}; " + _parse_ret(mach.read(offset+1, retsize))
134
+ offset += 1 + retsize
135
+ end
136
+
137
+ hits << [ vaddr, message ]
138
+ end
139
+
140
+ return hits
141
+ end
142
+ end
143
+
144
+ class PopPopRetScanner < JmpRegScanner
145
+
146
+ def config(param)
147
+ pops = _build_byte_list(0x58, (0 .. 7).to_a - [4]) # we don't want pop esp's...
148
+ self.regex = Regexp.new("[#{pops}][#{pops}](\xc3|\xc2..)", nil, 'n')
149
+ end
150
+
151
+ def scan_segment(segment, param={})
152
+ base_addr = segment.vmaddr
153
+ segment_offset = segment.fileoff
154
+ offset = segment_offset
155
+
156
+ hits = []
157
+
158
+ while offset < segment.fileoff + segment.filesize && (offset = mach.index(regex, offset)) != nil
159
+
160
+ vaddr = base_addr + (offset - segment_offset)
161
+ message = ''
162
+
163
+ pops = mach.read(offset, 2)
164
+ reg1 = Rex::Arch::X86.reg_name32(pops[0,1].unpack("C*")[0] & 0x7)
165
+ reg2 = Rex::Arch::X86.reg_name32(pops[1,1].unpack("C*")[0] & 0x7)
166
+
167
+ message = "pop #{reg1}; pop #{reg2}; "
168
+
169
+ retsize = _ret_size(offset+2)
170
+ message += _parse_ret(mach.read(offset+2, retsize))
171
+
172
+ offset += 2 + retsize
173
+
174
+ hits << [ vaddr, message ]
175
+ end
176
+
177
+ return hits
178
+ end
179
+ end
180
+
181
+ class RegexScanner < JmpRegScanner
182
+
183
+ def config(param)
184
+ self.regex = Regexp.new(param['args'], nil, 'n')
185
+ end
186
+
187
+ def scan_segment(segment, param={})
188
+ base_addr = segment.vmaddr
189
+ segment_offset = segment.fileoff
190
+ offset = segment_offset
191
+
192
+ hits = []
193
+
194
+ while offset < segment.fileoff + segment.filesize && (offset = mach.index(regex, offset)) != nil
195
+
196
+ idx = offset
197
+ buf = ''
198
+ mat = nil
199
+
200
+ while (! (mat = buf.match(regex)))
201
+ buf << mach.read(idx, 1)
202
+ idx += 1
203
+ end
204
+
205
+ vaddr = base_addr + (offset - segment_offset)
206
+
207
+ hits << [ vaddr, buf.unpack("H*") ]
208
+ offset += buf.length
209
+ end
210
+ return hits
211
+ end
212
+ end
213
+
214
+ end
215
+ end
216
+ end
217
+
@@ -0,0 +1,10 @@
1
+ # -*- coding: binary -*-
2
+
3
+ module Rex
4
+ module PeParsey
5
+
6
+ end
7
+ end
8
+
9
+ require 'rex/peparsey/pe'
10
+ require 'rex/peparsey/pe_memdump'
@@ -0,0 +1,30 @@
1
+ # -*- coding: binary -*-
2
+
3
+ module Rex
4
+ module PeParsey
5
+
6
+ class PeError < ::RuntimeError
7
+ end
8
+
9
+ class ParseError < PeError
10
+ end
11
+
12
+ class DosHeaderError < ParseError
13
+ end
14
+
15
+ class FileHeaderError < ParseError
16
+ end
17
+
18
+ class OptionalHeaderError < ParseError
19
+ end
20
+
21
+ class BoundsError < PeError
22
+ end
23
+
24
+ class PeParseyError < PeError
25
+ end
26
+
27
+ class SkipError < PeError
28
+ end
29
+
30
+ end end
@@ -0,0 +1,210 @@
1
+ # -*- coding: binary -*-
2
+
3
+ require 'rex/image_source'
4
+ require 'rex/peparsey/exceptions'
5
+ require 'rex/peparsey/pebase'
6
+ require 'rex/peparsey/section'
7
+ require 'rex/struct2'
8
+
9
+ module Rex
10
+ module PeParsey
11
+ class Pe < PeBase
12
+
13
+ def initialize(isource)
14
+
15
+ #
16
+ # DOS Header
17
+ #
18
+ # Parse the initial dos header, starting at the file beginning
19
+ #
20
+ offset = 0
21
+ dos_header = self.class._parse_dos_header(isource.read(offset, IMAGE_DOS_HEADER_SIZE))
22
+
23
+ #
24
+ # File Header
25
+ #
26
+ # If there is going to be a PE, the dos header tells us where to find it
27
+ # So now we try to parse the file (pe) header
28
+ #
29
+ offset += dos_header.e_lfanew
30
+
31
+ # most likely an invalid e_lfanew...
32
+ if offset > isource.size
33
+ raise FileHeaderError, "e_lfanew looks invalid", caller
34
+ end
35
+
36
+ file_header = self.class._parse_file_header(isource.read(offset, IMAGE_FILE_HEADER_SIZE))
37
+
38
+ #
39
+ # Optional Header
40
+ #
41
+ # After the file header, we find the optional header. Right now
42
+ # we require a optional header. Despite it's name, all binaries
43
+ # that we are interested in should have one. We need this
44
+ # header for a lot of stuff, so we die without it...
45
+ #
46
+ offset += IMAGE_FILE_HEADER_SIZE
47
+ optional_header = self.class._parse_optional_header(
48
+ isource.read(offset, file_header.SizeOfOptionalHeader)
49
+ )
50
+
51
+ if !optional_header
52
+ raise OptionalHeaderError, "No optional header!", caller
53
+ end
54
+
55
+ base = optional_header.ImageBase
56
+
57
+ #
58
+ # Section Headers
59
+ #
60
+ # After the optional header should be the section headers.
61
+ # We know how many there should be from the file header...
62
+ #
63
+ offset += file_header.SizeOfOptionalHeader
64
+
65
+ num_sections = file_header.NumberOfSections
66
+ section_headers = self.class._parse_section_headers(
67
+ isource.read(offset, IMAGE_SIZEOF_SECTION_HEADER * num_sections)
68
+ )
69
+
70
+ #
71
+ # End of Headers
72
+ #
73
+ # After the section headers (which are padded to FileAlignment)
74
+ # we should find the section data, described by the section
75
+ # headers...
76
+ #
77
+ # So this is the end of our header data, lets store this
78
+ # in an image source for possible access later...
79
+ #
80
+ offset += IMAGE_SIZEOF_SECTION_HEADER * num_sections
81
+ offset = self.class._align_offset(offset, optional_header.FileAlignment)
82
+
83
+ header_section = Section.new(isource.subsource(0, offset), 0, nil)
84
+
85
+ #
86
+ # Sections
87
+ #
88
+ # So from here on out should be section data, and then any
89
+ # trailing data (like authenticode and stuff I think)
90
+ #
91
+
92
+ sections = [ ]
93
+
94
+ section_headers.each do |section_header|
95
+
96
+ rva = section_header.VirtualAddress
97
+ size = section_header.SizeOfRawData
98
+ file_offset = section_header.PointerToRawData
99
+
100
+ sections << Section.new(
101
+ isource.subsource(file_offset, size),
102
+ rva,
103
+ section_header
104
+ )
105
+ end
106
+
107
+
108
+
109
+ #
110
+ # Save the stuffs!
111
+ #
112
+ # We have parsed enough to load the file up here, now we just
113
+ # save off all of the structures and data... We will
114
+ # save our fake header section, the real sections, etc.
115
+ #
116
+
117
+ #
118
+ # These should not be accessed directly
119
+ #
120
+
121
+ self._isource = isource
122
+
123
+ self._dos_header = dos_header
124
+ self._file_header = file_header
125
+ self._optional_header = optional_header
126
+ self._section_headers = section_headers
127
+
128
+ self.image_base = base
129
+ self.sections = sections
130
+ self.header_section = header_section
131
+
132
+ self._config_header = _parse_config_header()
133
+ self._tls_header = _parse_tls_header()
134
+
135
+ # These can be accessed directly
136
+ self.hdr = HeaderAccessor.new
137
+ self.hdr.dos = self._dos_header
138
+ self.hdr.file = self._file_header
139
+ self.hdr.opt = self._optional_header
140
+ self.hdr.sections = self._section_headers
141
+ self.hdr.config = self._config_header
142
+ self.hdr.tls = self._tls_header
143
+ self.hdr.exceptions = self._exception_header
144
+
145
+ # We load the exception directory last as it relies on hdr.file to be created above.
146
+ self._exception_header = _load_exception_directory()
147
+ end
148
+
149
+ #
150
+ # Return everything that's going to be mapped in the process
151
+ # and accessable. This should include all of the sections
152
+ # and our "fake" section for the header data...
153
+ #
154
+ def all_sections
155
+ [ header_section ] + sections
156
+ end
157
+
158
+ #
159
+ # Returns true if this binary is for a 64-bit architecture.
160
+ #
161
+ def ptr_64?
162
+ [
163
+ IMAGE_FILE_MACHINE_IA64,
164
+ IMAGE_FILE_MACHINE_ALPHA64,
165
+ IMAGE_FILE_MACHINE_AMD64
166
+ ].include?(self._file_header.Machine)
167
+ end
168
+
169
+ #
170
+ # Returns true if this binary is for a 32-bit architecture.
171
+ # This check does not take into account 16-bit binaries at the moment.
172
+ #
173
+ def ptr_32?
174
+ ptr_64? == false
175
+ end
176
+
177
+ #
178
+ # Converts a virtual address to a string representation based on the
179
+ # underlying architecture.
180
+ #
181
+ def ptr_s(va)
182
+ (ptr_32?) ? ("0x%.8x" % va) : ("0x%.16x" % va)
183
+ end
184
+
185
+ #
186
+ # Converts a file offset into a virtual address
187
+ #
188
+ def file_offset_to_va(offset)
189
+ image_base + file_offset_to_rva(offset)
190
+ end
191
+
192
+ #
193
+ # Read raw bytes from the specified offset in the underlying file
194
+ #
195
+ # NOTE: You should pass raw file offsets into this, not offsets from
196
+ # the beginning of the section. If you need to read from within a
197
+ # section, add section.file_offset prior to passing the offset in.
198
+ #
199
+ def read(offset, len)
200
+ _isource.read(offset, len)
201
+ end
202
+
203
+ def size
204
+ _isource.size
205
+ end
206
+ def length
207
+ _isource.size
208
+ end
209
+
210
+ end end end