pedump 0.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,359 @@
1
+ require 'pedump'
2
+ require 'optparse'
3
+
4
+ unless Object.instance_methods.include?(:try)
5
+ class Object
6
+ def try(method)
7
+ send method if respond_to? method
8
+ end
9
+ end
10
+ end
11
+
12
+ class PEdump::CLI
13
+ attr_accessor :data, :argv
14
+
15
+ KNOWN_ACTIONS = %w'mz dos_stub rich pe data_directory sections strings resources resource_directory'.map(&:to_sym)
16
+ DEFAULT_ALL_ACTIONS = KNOWN_ACTIONS - %w'resource_directory'.map(&:to_sym)
17
+
18
+ def initialize argv = ARGV
19
+ @argv = argv
20
+ end
21
+
22
+ def run
23
+ @actions = []
24
+ @options = { :format => :table }
25
+ optparser = OptionParser.new do |opts|
26
+ opts.banner = "Usage: pedump [options]"
27
+
28
+ opts.on "-v", "--[no-]verbose", "Run verbosely" do |v|
29
+ @options[:verbose] ||= 0
30
+ @options[:verbose] += 1
31
+ end
32
+ opts.on "-f", "--format FORMAT", [:binary, :c, :dump, :hex, :inspect, :table],
33
+ "Output format: bin,c,dump,hex,inspect,table (default)" do |v|
34
+ @options[:format] = v
35
+ end
36
+ # TODO: imports, exports
37
+ KNOWN_ACTIONS.each do |t|
38
+ opts.on "--#{t.to_s.tr('_','-')}", eval("lambda{ |_| @actions << :#{t.to_s.tr('-','_')} }")
39
+ end
40
+ opts.on "--all", "Dump all but #{(KNOWN_ACTIONS-DEFAULT_ALL_ACTIONS).join(',')} (default)" do
41
+ @actions = DEFAULT_ALL_ACTIONS
42
+ end
43
+ end
44
+
45
+ if (@argv = optparser.parse(@argv)).empty?
46
+ puts optparser.help
47
+ return
48
+ end
49
+
50
+ if (@actions-KNOWN_ACTIONS).any?
51
+ puts "[?] unknown actions: #{@actions-KNOWN_ACTIONS}"
52
+ @actions.delete_if{ |x| !KNOWN_ACTIONS.include?(x) }
53
+ end
54
+ @actions = DEFAULT_ALL_ACTIONS if @actions.empty?
55
+
56
+ argv.each_with_index do |fname,idx|
57
+ if argv.size > 1
58
+ puts if idx > 0
59
+ puts "# -----------------------------------------------"
60
+ puts "# #{fname}"
61
+ puts "# -----------------------------------------------"
62
+ puts
63
+ end
64
+ @pedump = PEdump.new fname
65
+ if @options[:verbose]
66
+ @pedump.logger.level = @options[:verbose] > 1 ? Logger::INFO : Logger::DEBUG
67
+ end
68
+ @pedump = @pedump.dump
69
+
70
+ @actions.each do |action|
71
+ dump_action action
72
+ end
73
+ end
74
+ rescue Errno::EPIPE
75
+ # output interrupt, f.ex. when piping output to a 'head' command
76
+ # prevents a 'Broken pipe - <STDOUT> (Errno::EPIPE)' message
77
+ end
78
+
79
+ def action_title action
80
+ s = action.to_s.upcase.tr('_',' ')
81
+ s += " Header" if [:mz, :pe, :rich].include?(action)
82
+ "=== %s ===\n\n" % s
83
+ end
84
+
85
+ def dump_action action
86
+ data = @pedump.send(action)
87
+ return if !data || (data.respond_to?(:empty?) && data.empty?)
88
+
89
+ puts action_title(action)
90
+
91
+ return dump(data) if [:inspect, :table].include?(@options[:format])
92
+
93
+ dump_opts = {:name => action}
94
+ case action
95
+ when :pe
96
+ @pedump.pe.ifh.TimeDateStamp = @pedump.pe.ifh.TimeDateStamp.to_i
97
+ data = @pedump.pe.signature + (@pedump.pe.ifh.try(:pack)||'') + (@pedump.pe.ioh.try(:pack)||'')
98
+ @pedump.pe.ifh.TimeDateStamp = Time.at(@pedump.pe.ifh.TimeDateStamp)
99
+ when :resources
100
+ return dump_resources(data)
101
+ when :strings
102
+ return dump_strings(data)
103
+ else
104
+ if data.is_a?(Struct) && data.respond_to?(:pack)
105
+ data = data.pack
106
+ elsif data.is_a?(Array) && data.all?{ |x| x.is_a?(Struct) && x.respond_to?(:pack)}
107
+ data = data.map(&:pack).join
108
+ end
109
+ end
110
+ dump data, dump_opts
111
+ ensure
112
+ puts
113
+ end
114
+
115
+ def dump data, opts = {}
116
+ case opts[:format] || @options[:format] || :dump
117
+ when :dump, :hexdump
118
+ puts hexdump(data)
119
+ when :hex
120
+ puts data.each_byte.map{ |x| "%02x" % x }.join(' ')
121
+ when :binary
122
+ print data
123
+ when :c
124
+ name = opts[:name] || "foo"
125
+ puts "// #{data.size} bytes total"
126
+ puts "unsigned char #{name}[] = {"
127
+ data.unpack('C*').each_slice(12) do |row|
128
+ puts " " + row.map{ |c| "0x%02x," % c}.join(" ")
129
+ end
130
+ puts "};"
131
+ when :inspect
132
+ require 'pp'
133
+ pp data
134
+ when :table
135
+ dump_table data
136
+ end
137
+ end
138
+
139
+ COMMENTS = {
140
+ :Machine => {
141
+ 0x014c => 'x86',
142
+ 0x0200 => 'Intel Itanium',
143
+ 0x8664 => 'x64',
144
+ 'default' => '???'
145
+ },
146
+ :Magic => {
147
+ 0x010b => '32-bit executable',
148
+ 0x020b => '64-bit executable',
149
+ 0x0107 => 'ROM image',
150
+ 'default' => '???'
151
+ },
152
+ :Subsystem => PEdump::IMAGE_SUBSYSTEMS
153
+ }
154
+
155
+ def dump_table data
156
+ if data.is_a?(Struct)
157
+ return dump_res_dir(data) if data.is_a?(PEdump::IMAGE_RESOURCE_DIRECTORY)
158
+ data.each_pair do |k,v|
159
+ case v
160
+ when Numeric
161
+ case k
162
+ when /\AMajor.*Version\Z/
163
+ printf "%30s: %24s\n", k.to_s.sub('Major',''), "#{v}.#{data[k.to_s.sub('Major','Minor')]}"
164
+ when /\AMinor.*Version\Z/
165
+ else
166
+ if COMMENTS[k]
167
+ printf "%30s: %10d %12s %s\n", k, v, v<10 ? v : ("0x"+v.to_s(16)),
168
+ COMMENTS[k][v] || COMMENTS[k]['default'] || ''
169
+ else
170
+ printf "%30s: %10d %12s\n", k, v, v<10 ? v : ("0x"+v.to_s(16))
171
+ end
172
+ end
173
+ when Struct
174
+ printf "\n# %s:\n", v.class.to_s.split('::').last
175
+ dump_table v
176
+ when Time
177
+ printf "%30s: %24s\n", k, v.strftime('"%Y-%m-%d %H:%M:%S"')
178
+ when Array
179
+ next if %w'DataDirectory section_table'.include?(k)
180
+ else
181
+ printf "%30s: %24s\n", k, v.to_s.inspect
182
+ end
183
+ end
184
+ elsif data.is_a?(Enumerable) && data.map(&:class).uniq.size == 1
185
+ case data.first
186
+ when PEdump::IMAGE_DATA_DIRECTORY
187
+ dump_data_dir data
188
+ when PEdump::IMAGE_SECTION_HEADER
189
+ dump_sections data
190
+ when PEdump::Resource
191
+ dump_resources data
192
+ when PEdump::STRING
193
+ dump_strings data
194
+ else
195
+ puts "[?] don't know how to dump: #{data.inspect[0,50]}" unless data.empty?
196
+ end
197
+ elsif data.is_a?(PEdump::DOSStub)
198
+ puts hexdump(data)
199
+ elsif data.is_a?(PEdump::RichHdr)
200
+ dump_rich_hdr data
201
+ else
202
+ puts "[?] Don't know how to display #{data.inspect[0,50]}... as a table"
203
+ end
204
+ end
205
+
206
+ def dump_strings data
207
+ printf "%5s %5s %4s %s\n", "ID", "ID", "LANG", "STRING"
208
+ prev_lang = nil
209
+ data.sort_by{|s| [s.lang, s.id] }.each do |s|
210
+ #puts if prev_lang && prev_lang != s.lang
211
+ printf "%5d %5x %4x %s\n", s.id, s.id, s.lang, s.value.inspect
212
+ prev_lang = s.lang
213
+ end
214
+ end
215
+
216
+ def dump_res_dir entry, level = 0
217
+ if entry.is_a?(PEdump::IMAGE_RESOURCE_DIRECTORY)
218
+ # root entry
219
+ printf "dir? %8s %8s %5s %5s", "FLAGS", "TIMESTMP", "VERS", 'nEnt'
220
+ printf " | %-15s %8s | ", "NAME", "OFFSET"
221
+ printf "data? %8s %8s %5s %8s\n", 'DATA_OFS', 'DATA_SZ', 'CP', 'RESERVED'
222
+ end
223
+
224
+ dir =
225
+ case entry
226
+ when PEdump::IMAGE_RESOURCE_DIRECTORY
227
+ entry
228
+ when PEdump::IMAGE_RESOURCE_DIRECTORY_ENTRY
229
+ entry.data
230
+ end
231
+
232
+ fmt1 = "DIR: %8x %8x %5s %5d"
233
+ fmt1s = fmt1.tr("xd\nDIR:","ss ") % ['','','','']
234
+
235
+ if dir.is_a?(PEdump::IMAGE_RESOURCE_DIRECTORY)
236
+ printf fmt1,
237
+ dir.Characteristics, dir.TimeDateStamp,
238
+ [dir.MajorVersion,dir.MinorVersion].join('.'),
239
+ dir.NumberOfNamedEntries + dir.NumberOfIdEntries
240
+ else
241
+ print fmt1s
242
+ end
243
+
244
+ name =
245
+ case level
246
+ when 0 then "ROOT"
247
+ when 1 then PEdump::ROOT_RES_NAMES[entry.Name] || entry.name
248
+ else entry.name
249
+ end
250
+
251
+ printf " | %-15s", name
252
+ printf("\n%s %15s",fmt1s,'') if name.size > 15
253
+ printf " %8x | ", entry.respond_to?(:OffsetToData) ? entry.OffsetToData : 0
254
+
255
+ if dir.is_a?(PEdump::IMAGE_RESOURCE_DIRECTORY)
256
+ puts
257
+ dir.entries.each do |child|
258
+ dump_res_dir child, level+1
259
+ end
260
+ else
261
+ printf "DATA: %8x %8x %5s %8x\n", dir.OffsetToData, dir.Size, dir.CodePage, dir.Reserved
262
+ end
263
+ end
264
+
265
+ # def dump_res_dir0 dir, level=0, dir_entry = nil
266
+ # dir_entry ||= PEdump::IMAGE_RESOURCE_DIRECTORY_ENTRY.new
267
+ # printf "%-10s %8x %8x %8x %5s %5d\n", dir_entry.name || "ROOT", dir_entry.OffsetToData.to_i,
268
+ # dir.Characteristics, dir.TimeDateStamp,
269
+ # [dir.MajorVersion,dir.MinorVersion].join('.'),
270
+ # dir.NumberOfNamedEntries + dir.NumberOfIdEntries
271
+ # dir.entries.each do |child|
272
+ # if child.data.is_a?(PEdump::IMAGE_RESOURCE_DIRECTORY)
273
+ # dump_res_dir child.data, level+1, child
274
+ # else
275
+ # print " "*(level+1) + "CHILD"
276
+ # child.data.each_pair do |k,v|
277
+ # print " #{k[0,2]}=#{v}"
278
+ # end
279
+ # puts
280
+ # #p child
281
+ # end
282
+ # end
283
+ # end
284
+
285
+ def dump_resources data
286
+ keys = []; fmt = []
287
+ fmt << "%11x " ; keys << :file_offset
288
+ fmt << "%5d " ; keys << :cp
289
+ fmt << "%5x " ; keys << :lang
290
+ fmt << "%8d " ; keys << :size
291
+ fmt << "%-13s "; keys << :type
292
+ fmt << "%s\n" ; keys << :name
293
+ printf fmt.join.tr('dx','s'), *keys.map(&:to_s).map(&:upcase)
294
+ data.each do |res|
295
+ fmt.each_with_index do |f,i|
296
+ v = res.send(keys[i])
297
+ if f['x']
298
+ printf f.tr('x','s'), v < 10 ? v.to_s : "0x#{v.to_s(16)}"
299
+ else
300
+ printf f, v
301
+ end
302
+ end
303
+ end
304
+ end
305
+
306
+ def dump_sections data
307
+ printf " %-8s %8s %8s %8s %8s %5s %8s %5s %8s %8s\n",
308
+ 'NAME', 'RVA', 'VSZ','RAW_SZ','RAW_PTR','nREL','REL_PTR','nLINE','LINE_PTR','FLAGS'
309
+ data.each do |s|
310
+ name = s.Name[/[^a-z0-9_.]/i] ? s.Name.inspect : s.Name
311
+ name = "#{name}\n " if name.size > 8
312
+ printf " %-8s %8x %8x %8x %8x %5x %8x %5x %8x %8x %s\n", name,
313
+ s.VirtualAddress, s.VirtualSize,
314
+ s.SizeOfRawData, s.PointerToRawData,
315
+ s.NumberOfRelocations, s.PointerToRelocations,
316
+ s.NumberOfLinenumbers, s.PointerToLinenumbers,
317
+ s.flags, s.flags_desc
318
+ end
319
+ end
320
+
321
+ def dump_data_dir data
322
+ data.each do |row|
323
+ printf " %-12s rva:0x%8x size:0x %8x\n", row.type, row.va, row.size
324
+ end
325
+ end
326
+
327
+ def dump_rich_hdr data
328
+ puts " LIB_ID VERSION TIMES_USED "
329
+ data.decode.each do |row|
330
+ printf " %5d %2x %7d %4x %7d %3x\n",
331
+ row.id, row.id, row.version, row.version, row.times, row.times
332
+ end
333
+ end
334
+
335
+ def hexdump data, h = {}
336
+ offset = h[:offset] || 0
337
+ add = h[:add] || 0
338
+ size = h[:size] || (data.size-offset)
339
+ tail = h[:tail] || "\n"
340
+ width = h[:width] || 0x10 # row width, in bytes
341
+
342
+ size = data.size-offset if size+offset > data.size
343
+
344
+ r = ''; s = ''
345
+ r << "%08x: " % (offset + add)
346
+ ascii = ''
347
+ size.times do |i|
348
+ if i%width==0 && i>0
349
+ r << "%s |%s|\n%08x: " % [s, ascii, offset + add + i]
350
+ ascii = ''; s = ''
351
+ end
352
+ s << " " if i%width%8==0
353
+ c = data[offset+i].ord
354
+ s << "%02x " % c
355
+ ascii << ((32..126).include?(c) ? c.chr : '.')
356
+ end
357
+ r << "%-*s |%-*s|%s" % [width*3+width/8+(width%8==0?0:1), s, width, ascii, tail]
358
+ end
359
+ end
@@ -0,0 +1,63 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = "pedump"
8
+ s.version = "0.0.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Andrey \"Zed\" Zaikin"]
12
+ s.date = "2011-12-07"
13
+ s.description = "dump headers, sections, extract resources"
14
+ s.email = "zed.0xff@gmail.com"
15
+ s.executables = ["pedump"]
16
+ s.extra_rdoc_files = [
17
+ "LICENSE.txt",
18
+ "README.rdoc"
19
+ ]
20
+ s.files = [
21
+ ".document",
22
+ ".rspec",
23
+ "Gemfile",
24
+ "Gemfile.lock",
25
+ "LICENSE.txt",
26
+ "README.rdoc",
27
+ "Rakefile",
28
+ "VERSION",
29
+ "bin/pedump",
30
+ "lib/pedump.rb",
31
+ "lib/pedump/cli.rb",
32
+ "pedump.gemspec",
33
+ "spec/pedump_spec.rb",
34
+ "spec/spec_helper.rb"
35
+ ]
36
+ s.homepage = "http://github.com/zed-0xff/pedump"
37
+ s.licenses = ["MIT"]
38
+ s.require_paths = ["lib"]
39
+ s.rubygems_version = "1.8.10"
40
+ s.summary = "dump win32 PE executable files with a pure ruby"
41
+
42
+ if s.respond_to? :specification_version then
43
+ s.specification_version = 3
44
+
45
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
46
+ s.add_development_dependency(%q<rspec>, ["~> 2.3.0"])
47
+ s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
48
+ s.add_development_dependency(%q<jeweler>, ["~> 1.6.4"])
49
+ s.add_development_dependency(%q<rcov>, [">= 0"])
50
+ else
51
+ s.add_dependency(%q<rspec>, ["~> 2.3.0"])
52
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
53
+ s.add_dependency(%q<jeweler>, ["~> 1.6.4"])
54
+ s.add_dependency(%q<rcov>, [">= 0"])
55
+ end
56
+ else
57
+ s.add_dependency(%q<rspec>, ["~> 2.3.0"])
58
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
59
+ s.add_dependency(%q<jeweler>, ["~> 1.6.4"])
60
+ s.add_dependency(%q<rcov>, [">= 0"])
61
+ end
62
+ end
63
+
@@ -0,0 +1,7 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe "Pedump" do
4
+ it "fails" do
5
+ fail "hey buddy, you should probably rename this file and start specing for real"
6
+ end
7
+ end
@@ -0,0 +1,12 @@
1
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
2
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
3
+ require 'rspec'
4
+ require 'pedump'
5
+
6
+ # Requires supporting files with custom matchers and macros, etc,
7
+ # in ./support/ and its subdirectories.
8
+ Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
9
+
10
+ RSpec.configure do |config|
11
+
12
+ end
metadata ADDED
@@ -0,0 +1,109 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: pedump
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Andrey "Zed" Zaikin
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2011-12-07 00:00:00.000000000Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rspec
16
+ requirement: &70235807112320 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: 2.3.0
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: *70235807112320
25
+ - !ruby/object:Gem::Dependency
26
+ name: bundler
27
+ requirement: &70235807111480 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ~>
31
+ - !ruby/object:Gem::Version
32
+ version: 1.0.0
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: *70235807111480
36
+ - !ruby/object:Gem::Dependency
37
+ name: jeweler
38
+ requirement: &70235807110340 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ~>
42
+ - !ruby/object:Gem::Version
43
+ version: 1.6.4
44
+ type: :development
45
+ prerelease: false
46
+ version_requirements: *70235807110340
47
+ - !ruby/object:Gem::Dependency
48
+ name: rcov
49
+ requirement: &70235807082820 !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ type: :development
56
+ prerelease: false
57
+ version_requirements: *70235807082820
58
+ description: dump headers, sections, extract resources
59
+ email: zed.0xff@gmail.com
60
+ executables:
61
+ - pedump
62
+ extensions: []
63
+ extra_rdoc_files:
64
+ - LICENSE.txt
65
+ - README.rdoc
66
+ files:
67
+ - .document
68
+ - .rspec
69
+ - Gemfile
70
+ - Gemfile.lock
71
+ - LICENSE.txt
72
+ - README.rdoc
73
+ - Rakefile
74
+ - VERSION
75
+ - bin/pedump
76
+ - lib/pedump.rb
77
+ - lib/pedump/cli.rb
78
+ - pedump.gemspec
79
+ - spec/pedump_spec.rb
80
+ - spec/spec_helper.rb
81
+ homepage: http://github.com/zed-0xff/pedump
82
+ licenses:
83
+ - MIT
84
+ post_install_message:
85
+ rdoc_options: []
86
+ require_paths:
87
+ - lib
88
+ required_ruby_version: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ! '>='
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ segments:
95
+ - 0
96
+ hash: -4543562618514295469
97
+ required_rubygems_version: !ruby/object:Gem::Requirement
98
+ none: false
99
+ requirements:
100
+ - - ! '>='
101
+ - !ruby/object:Gem::Version
102
+ version: '0'
103
+ requirements: []
104
+ rubyforge_project:
105
+ rubygems_version: 1.8.10
106
+ signing_key:
107
+ specification_version: 3
108
+ summary: dump win32 PE executable files with a pure ruby
109
+ test_files: []