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,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