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,25 @@
1
+ # -*- coding: binary -*-
2
+
3
+ module Rex
4
+ module ElfParsey
5
+
6
+ class ElfError < ::RuntimeError
7
+ end
8
+
9
+ class ParseError < ElfError
10
+ end
11
+
12
+ class ElfHeaderError < ParseError
13
+ end
14
+
15
+ class ProgramHeaderError < ParseError
16
+ end
17
+
18
+ class BoundsError < ElfError
19
+ end
20
+
21
+ class ElfParseyError < ElfError
22
+ end
23
+
24
+ end
25
+ end
@@ -0,0 +1,10 @@
1
+ # -*- coding: binary -*-
2
+
3
+ module Rex
4
+ module ElfScan
5
+
6
+ end
7
+ end
8
+
9
+ require 'rex/elfscan/scanner'
10
+ require 'rex/elfscan/search'
@@ -0,0 +1,226 @@
1
+ # -*- coding: binary -*-
2
+ require 'metasm'
3
+
4
+ module Rex
5
+ module ElfScan
6
+ module Scanner
7
+ class Generic
8
+
9
+ attr_accessor :elf, :regex
10
+
11
+ def initialize(elf)
12
+ self.elf = elf
13
+ end
14
+
15
+ def config(param)
16
+ end
17
+
18
+ def scan(param)
19
+ config(param)
20
+
21
+ $stdout.puts "[#{param['file']}]"
22
+ elf.program_header.each do |program_header|
23
+
24
+ # Scan only loadable segment entries in the program header table
25
+ if program_header.p_type == Rex::ElfParsey::ElfBase::PT_LOAD
26
+ hits = scan_segment(program_header, param)
27
+ hits.each do |hit|
28
+ rva = hit[0]
29
+ message = hit[1].is_a?(Array) ? hit[1].join(" ") : hit[1]
30
+ $stdout.puts elf.ptr_s(rva) + " " + message
31
+ if(param['disasm'])
32
+ message.gsub!("; ", "\n")
33
+ if message.include?("retn")
34
+ message.gsub!("retn", "ret")
35
+ end
36
+
37
+ begin
38
+ d2 = Metasm::Shellcode.assemble(Metasm::Ia32.new, message).disassemble
39
+ rescue Metasm::ParseError
40
+ d2 = Metasm::Shellcode.disassemble(Metasm::Ia32.new, [message].pack('H*'))
41
+ end
42
+
43
+ addr = 0
44
+ while ((di = d2.disassemble_instruction(addr)))
45
+ disasm = "0x%08x\t" % (rva + addr)
46
+ disasm << di.instruction.to_s
47
+ $stdout.puts disasm
48
+ addr = di.next_addr
49
+ end
50
+ end
51
+ end
52
+ end
53
+
54
+ end
55
+ end
56
+
57
+ def scan_segment(program_header, param={})
58
+ []
59
+ end
60
+ end
61
+
62
+ class JmpRegScanner < Generic
63
+
64
+ def config(param)
65
+ regnums = param['args']
66
+
67
+ # build a list of the call bytes
68
+ calls = _build_byte_list(0xd0, regnums - [4]) # note call esp's don't work..
69
+ jmps = _build_byte_list(0xe0, regnums)
70
+ pushs1 = _build_byte_list(0x50, regnums)
71
+ pushs2 = _build_byte_list(0xf0, regnums)
72
+
73
+ regexstr = '('
74
+ if !calls.empty?
75
+ regexstr += "\xff[#{calls}]|"
76
+ end
77
+
78
+ regexstr += "\xff[#{jmps}]|([#{pushs1}]|\xff[#{pushs2}])(\xc3|\xc2..))"
79
+
80
+ self.regex = Regexp.new(regexstr, nil, 'n')
81
+ end
82
+
83
+ # build a list for regex of the possible bytes, based on a base
84
+ # byte and a list of register numbers..
85
+ def _build_byte_list(base, regnums)
86
+ regnums.collect { |regnum| Regexp.escape((base | regnum).chr) }.join('')
87
+ end
88
+
89
+ def _ret_size(offset)
90
+ case elf.read(offset, 1)
91
+ when "\xc3"
92
+ return 1
93
+ when "\xc2"
94
+ return 3
95
+ end
96
+
97
+ raise "Cannot read at offset: #{offset}"
98
+ end
99
+
100
+ def _parse_ret(data)
101
+ if data.length == 1
102
+ return "ret"
103
+ else
104
+ return "retn 0x%04x" % data[1, 2].unpack('v')[0]
105
+ end
106
+ end
107
+
108
+
109
+ def scan_segment(program_header, param={})
110
+ offset = program_header.p_offset
111
+
112
+ hits = []
113
+
114
+ while (offset = elf.index(regex, offset)) != nil
115
+
116
+ rva = elf.offset_to_rva(offset)
117
+ message = ''
118
+
119
+ parse_ret = false
120
+
121
+ byte1 = elf.read(offset, 1).unpack('C')[0]
122
+
123
+ if byte1 == 0xff
124
+ byte2 = elf.read(offset+1, 1).unpack('C')[0]
125
+ regname = Rex::Arch::X86.reg_name32(byte2 & 0x7)
126
+
127
+ case byte2 & 0xf8
128
+ when 0xd0
129
+ message = "call #{regname}"
130
+ offset += 2
131
+ when 0xe0
132
+ message = "jmp #{regname}"
133
+ offset += 2
134
+ when 0xf0
135
+ retsize = _ret_size(offset+2)
136
+ message = "push #{regname}; " + _parse_ret(elf.read(offset+2, retsize))
137
+ offset += 2 + retsize
138
+ else
139
+ raise "Unexpected value at #{offset}"
140
+ end
141
+ else
142
+ regname = Rex::Arch::X86.reg_name32(byte1 & 0x7)
143
+ retsize = _ret_size(offset+1)
144
+ message = "push #{regname}; " + _parse_ret(elf.read(offset+1, retsize))
145
+ offset += 1 + retsize
146
+ end
147
+
148
+ hits << [ rva, message ]
149
+ end
150
+
151
+ return hits
152
+ end
153
+ end
154
+
155
+ class PopPopRetScanner < JmpRegScanner
156
+
157
+ def config(param)
158
+ pops = _build_byte_list(0x58, (0 .. 7).to_a - [4]) # we don't want pop esp's...
159
+ self.regex = Regexp.new("[#{pops}][#{pops}](\xc3|\xc2..)", nil, 'n')
160
+ end
161
+
162
+ def scan_segment(program_header, param={})
163
+ offset = program_header.p_offset
164
+
165
+ hits = []
166
+
167
+ while offset < program_header.p_offset + program_header.p_filesz &&
168
+ (offset = elf.index(regex, offset)) != nil
169
+
170
+ rva = elf.offset_to_rva(offset)
171
+ message = ''
172
+
173
+ pops = elf.read(offset, 2)
174
+ reg1 = Rex::Arch::X86.reg_name32(pops[0,1].unpack('C*')[0] & 0x7)
175
+ reg2 = Rex::Arch::X86.reg_name32(pops[1,1].unpack('C*')[0] & 0x7)
176
+
177
+ message = "pop #{reg1}; pop #{reg2}; "
178
+
179
+ retsize = _ret_size(offset+2)
180
+ message += _parse_ret(elf.read(offset+2, retsize))
181
+
182
+ offset += 2 + retsize
183
+
184
+ hits << [ rva, message ]
185
+ end
186
+
187
+ return hits
188
+ end
189
+ end
190
+
191
+ class RegexScanner < JmpRegScanner
192
+
193
+ def config(param)
194
+ self.regex = Regexp.new(param['args'], nil, 'n')
195
+ end
196
+
197
+ def scan_segment(program_header, param={})
198
+ offset = program_header.p_offset
199
+
200
+ hits = []
201
+
202
+ while offset < program_header.p_offset + program_header.p_filesz &&
203
+ (offset = elf.index(regex, offset)) != nil
204
+
205
+ idx = offset
206
+ buf = ''
207
+ mat = nil
208
+
209
+ while (! (mat = buf.match(regex)))
210
+ buf << elf.read(idx, 1)
211
+ idx += 1
212
+ end
213
+
214
+ rva = elf.offset_to_rva(offset)
215
+
216
+ hits << [ rva, buf.unpack("H*") ]
217
+ offset += buf.length
218
+ end
219
+
220
+ return hits
221
+ end
222
+ end
223
+
224
+ end
225
+ end
226
+ end
@@ -0,0 +1,44 @@
1
+ # -*- coding: binary -*-
2
+
3
+ module Rex
4
+ module ElfScan
5
+ module Search
6
+
7
+ class DumpRVA
8
+ attr_accessor :elf
9
+
10
+ def initialize(elf)
11
+ self.elf = elf
12
+ end
13
+
14
+ def config(param)
15
+ @address = param['args']
16
+ end
17
+
18
+ def scan(param)
19
+ config(param)
20
+
21
+ $stdout.puts "[#{param['file']}]"
22
+
23
+ # Adjust based on -A and -B flags
24
+ pre = param['before'] || 0
25
+ suf = param['after'] || 16
26
+
27
+ @address -= pre
28
+ @address = 0 if (@address < 0 || ! @address)
29
+ buf = elf.read_rva(@address, suf)
30
+ $stdout.puts elf.ptr_s(@address) + " " + buf.unpack("H*")[0]
31
+ end
32
+ end
33
+
34
+ class DumpOffset < DumpRVA
35
+ def config(param)
36
+ begin
37
+ @address = elf.offset_to_rva(param['args'])
38
+ rescue Rex::ElfParsey::BoundsError
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,10 @@
1
+ # -*- coding: binary -*-
2
+
3
+ module Rex
4
+ module ImageSource
5
+
6
+ end
7
+ end
8
+
9
+ require 'rex/image_source/disk'
10
+ require 'rex/image_source/memory'
@@ -0,0 +1,58 @@
1
+ # -*- coding: binary -*-
2
+
3
+ require 'rex/image_source/image_source'
4
+ require 'rex/struct2'
5
+
6
+ module Rex
7
+ module ImageSource
8
+ class Disk < ImageSource
9
+
10
+ attr_accessor :file, :file_offset, :size
11
+
12
+ WINDOW_SIZE = 4096
13
+ WINDOW_OVERLAP = 64
14
+
15
+ def initialize(_file, _offset = 0, _len = nil)
16
+ _len = _file.stat.size if !_len
17
+
18
+ self.file = _file
19
+ self.file_offset = _offset
20
+ self.size = _len
21
+ end
22
+
23
+ def read(offset, len)
24
+ if offset < 0 || offset+len > size
25
+ raise RangeError, "Offset #{offset} outside of image source", caller
26
+ end
27
+
28
+ file.seek(file_offset + offset)
29
+ file.read(len)
30
+ end
31
+
32
+ def index(search, offset = 0)
33
+ # do a sliding window search across the disk
34
+ while offset < size
35
+
36
+ # get a full window size if we can, we
37
+ # don't want to read past our boundaries
38
+ wsize = size - offset
39
+ wsize = WINDOW_SIZE if wsize > WINDOW_SIZE
40
+
41
+ window = self.read(offset, wsize)
42
+ res = window.index(search)
43
+ return res + offset if res
44
+ offset += WINDOW_SIZE - WINDOW_OVERLAP
45
+ end
46
+ end
47
+
48
+ def subsource(offset, len)
49
+ self.class.new(file, file_offset+offset, len)
50
+ end
51
+
52
+ def close
53
+ file.close
54
+ end
55
+ end
56
+
57
+ end
58
+ end
@@ -0,0 +1,48 @@
1
+ # -*- coding: binary -*-
2
+
3
+ module Rex
4
+ module ImageSource
5
+ class ImageSource
6
+
7
+ #
8
+ # Um, just some abstract class stuff I guess, this is the interface
9
+ # that any image sources should subscribe to...
10
+ #
11
+
12
+ def subsource(offset, len)
13
+ raise "do something"
14
+ end
15
+
16
+ def size
17
+ raise "do something"
18
+ end
19
+
20
+ def file_offset
21
+ raise "do something"
22
+ end
23
+
24
+ def close
25
+ raise "do something"
26
+ end
27
+
28
+ def read_asciiz(offset)
29
+ # FIXME, make me better
30
+ string = ''
31
+ loop do
32
+ begin
33
+ char = read(offset, 1)
34
+ rescue RangeError
35
+ break
36
+ end
37
+ break if char.nil? || char == "\x00"
38
+ offset += 1
39
+ string << char
40
+ end
41
+ return string
42
+ end
43
+
44
+
45
+ end
46
+
47
+ end
48
+ end
@@ -0,0 +1,35 @@
1
+ # -*- coding: binary -*-
2
+
3
+ require 'rex/image_source/image_source'
4
+ require 'rex/struct2'
5
+
6
+ module Rex
7
+ module ImageSource
8
+ class Memory < ImageSource
9
+
10
+ attr_accessor :rawdata, :size, :file_offset
11
+
12
+ def initialize(_rawdata, _file_offset = 0)
13
+ self.rawdata = _rawdata
14
+ self.size = _rawdata.length
15
+ self.file_offset = _file_offset
16
+ end
17
+
18
+ def read(offset, len)
19
+ rawdata[offset, len]
20
+ end
21
+
22
+ def subsource(offset, len)
23
+ self.class.new(rawdata[offset, len], offset + file_offset)
24
+ end
25
+
26
+ def close
27
+ end
28
+
29
+ def index(*args)
30
+ rawdata.index(*args)
31
+ end
32
+ end
33
+
34
+ end
35
+ end