pedump 0.0.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.
@@ -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: []