pedump 0.3.2 → 0.3.3

Sign up to get free protection for your applications and to get access to all the features.
data/lib/pedump/cli.rb CHANGED
@@ -14,10 +14,10 @@ class PEdump::CLI
14
14
 
15
15
  KNOWN_ACTIONS = (
16
16
  %w'mz dos_stub rich pe data_directory sections' +
17
- %w'strings resources resource_directory imports exports web'
17
+ %w'strings resources resource_directory imports exports packer web packer_only'
18
18
  ).map(&:to_sym)
19
19
 
20
- DEFAULT_ALL_ACTIONS = KNOWN_ACTIONS - %w'resource_directory web'.map(&:to_sym)
20
+ DEFAULT_ALL_ACTIONS = KNOWN_ACTIONS - %w'resource_directory web packer_only'.map(&:to_sym)
21
21
 
22
22
  URL_BASE = "http://pedump.me"
23
23
 
@@ -31,28 +31,35 @@ class PEdump::CLI
31
31
  optparser = OptionParser.new do |opts|
32
32
  opts.banner = "Usage: pedump [options]"
33
33
 
34
+ opts.on "-V", "--version", "Print version information and exit" do
35
+ puts PEdump::VERSION
36
+ exit
37
+ end
34
38
  opts.on "-v", "--[no-]verbose", "Run verbosely" do |v|
35
39
  @options[:verbose] ||= 0
36
40
  @options[:verbose] += 1
37
41
  end
38
- opts.on "-F", "--force", "Try to dump by all means (can cause exceptions & heavy wounds)" do |v|
42
+ opts.on "-F", "--force", "Try to dump by all means","(can cause exceptions & heavy wounds)" do |v|
39
43
  @options[:force] ||= 0
40
44
  @options[:force] += 1
41
45
  end
42
46
  opts.on "-f", "--format FORMAT", [:binary, :c, :dump, :hex, :inspect, :table],
43
- "Output format: bin,c,dump,hex,inspect,table (default)" do |v|
47
+ "Output format: bin,c,dump,hex,inspect,table","(default: table)" do |v|
44
48
  @options[:format] = v
45
49
  end
46
50
  KNOWN_ACTIONS.each do |t|
47
51
  opts.on "--#{t.to_s.tr('_','-')}", eval("lambda{ |_| @actions << :#{t.to_s.tr('-','_')} }")
48
52
  end
49
- opts.on "--all", "Dump all but #{(KNOWN_ACTIONS-DEFAULT_ALL_ACTIONS).join(',')} (default)" do
53
+ opts.on '-P', "--packer-only", "packer/compiler detect only,","mimics 'file' command output" do
54
+ @actions << :packer_only
55
+ end
56
+ opts.on "--all", "Dump all but resource-directory (default)" do
50
57
  @actions = DEFAULT_ALL_ACTIONS
51
58
  end
52
59
  opts.on "--va2file VA", "Convert RVA to file offset" do |va|
53
60
  @actions << [:va2file,va]
54
61
  end
55
- opts.on "-W", "--web", "Upload file to a #{URL_BASE} for a nice HTML tables with image previews, candies & stuff" do
62
+ opts.on "-W", "--web", "Uploads files to a #{URL_BASE}","for a nice HTML tables with image previews,","candies & stuff" do
56
63
  @actions << :web
57
64
  end
58
65
  end
@@ -68,13 +75,16 @@ class PEdump::CLI
68
75
  end
69
76
  @actions = DEFAULT_ALL_ACTIONS if @actions.empty?
70
77
 
78
+ if @actions.include?(:packer_only)
79
+ raise "[!] can't mix --packer-only with other actions" if @actions.size > 1
80
+ dump_packer_only(argv)
81
+ return
82
+ end
83
+
71
84
  argv.each_with_index do |fname,idx|
72
- if argv.size > 1
73
- puts if idx > 0
74
- puts "# -----------------------------------------------"
75
- puts "# #{fname}"
76
- puts "# -----------------------------------------------"
77
- end
85
+ @need_fname_header = (argv.size > 1)
86
+ @file_idx = idx
87
+ @file_name = fname
78
88
 
79
89
  File.open(fname,'rb') do |f|
80
90
  @pedump = PEdump.new(fname, :force => @options[:force]).tap do |x|
@@ -99,6 +109,23 @@ class PEdump::CLI
99
109
  # prevents a 'Broken pipe - <STDOUT> (Errno::EPIPE)' message
100
110
  end
101
111
 
112
+ def dump_packer_only fnames
113
+ max_fname_len = fnames.map(&:size).max
114
+ fnames.each do |fname|
115
+ File.open(fname,'rb') do |f|
116
+ @pedump = PEdump.new(fname, :force => @options[:force]).tap do |x|
117
+ if @options[:verbose]
118
+ x.logger.level = @options[:verbose] > 1 ? Logger::INFO : Logger::DEBUG
119
+ end
120
+ end
121
+ packers = @pedump.packers(f)
122
+ pname = Array(packers).first.try(:packer).try(:name)
123
+ pname ||= "unknown" if @options[:verbose]
124
+ printf("%-*s %s\n", max_fname_len+1, "#{fname}:", pname) if pname
125
+ end
126
+ end
127
+ end
128
+
102
129
  class ProgressProxy
103
130
  attr_reader :pbar
104
131
 
@@ -173,8 +200,17 @@ class PEdump::CLI
173
200
  end
174
201
 
175
202
  def action_title action
203
+ if @need_fname_header
204
+ @need_fname_header = false
205
+ puts if @file_idx > 0
206
+ puts "# -----------------------------------------------"
207
+ puts "# #@file_name"
208
+ puts "# -----------------------------------------------"
209
+ end
210
+
176
211
  s = action.to_s.upcase.tr('_',' ')
177
212
  s += " Header" if [:mz, :pe, :rich].include?(action)
213
+ s = "Packer / Compiler" if action == :packer
178
214
  "\n=== %s ===\n\n" % s
179
215
  end
180
216
 
@@ -310,6 +346,8 @@ class PEdump::CLI
310
346
  dump_strings data
311
347
  when PEdump::IMAGE_IMPORT_DESCRIPTOR
312
348
  dump_imports data
349
+ when PEdump::Packer::Match
350
+ dump_packers data
313
351
  else
314
352
  puts "[?] don't know how to dump: #{data.inspect[0,50]}" unless data.empty?
315
353
  end
@@ -322,6 +360,17 @@ class PEdump::CLI
322
360
  end
323
361
  end
324
362
 
363
+ def dump_packers data
364
+ if @options[:verbose]
365
+ data.each do |p|
366
+ printf "%8x %4d %s\n", p.offset, p.packer.size, p.packer.name
367
+ end
368
+ else
369
+ # show only largest detected unless verbose output requested
370
+ puts " #{data.first.packer.name}"
371
+ end
372
+ end
373
+
325
374
  def dump_exports data
326
375
  printf "# module %s\n# flags=0x%x ts=%s version=%d.%d ord_base=%d\n",
327
376
  data.name.inspect,
@@ -0,0 +1,124 @@
1
+ class PEdump
2
+ class Packer < Struct.new(:name, :re, :ep_only, :size)
3
+
4
+ DATA_ROOT = File.dirname(File.dirname(File.dirname(__FILE__)))
5
+ BIN_SIGS_FILE = File.join(DATA_ROOT, "data", "sig.bin")
6
+ TEXT_SIGS_FILE = File.join(DATA_ROOT, "data", "sig.txt")
7
+
8
+ Match = Struct.new :offset, :packer
9
+
10
+ class << self
11
+
12
+ def all
13
+ @@all ||=
14
+ begin
15
+ r = unmarshal
16
+ unless r
17
+ if PEdump.respond_to?(:logger) && PEdump.logger
18
+ PEdump.logger.warn "[?] #{self}: unmarshal failed, using slow text parsing instead"
19
+ else
20
+ STDERR.puts "[?] #{self}: unmarshal failed, using slow text parsing instead"
21
+ end
22
+ r = parse
23
+ end
24
+ r
25
+ end
26
+ end
27
+ alias :load :all
28
+
29
+ def max_size
30
+ @@max_size ||= all.map(&:size).max
31
+ end
32
+
33
+ def of data, ep_offset = nil
34
+ if data.respond_to?(:read) && data.respond_to?(:seek) && ep_offset
35
+ of_file data, ep_offset
36
+ else
37
+ of_data data
38
+ end
39
+ end
40
+
41
+ # try to determine packer of FILE f, ep_offset - offset to entrypoint from start of file
42
+ def of_file f, ep_offset
43
+ f.seek(ep_offset)
44
+ of_data f.read(max_size)
45
+ end
46
+
47
+ def of_data data
48
+ r = []
49
+ each do |packer|
50
+ if (idx=data.index(packer.re)) == 0
51
+ r << Match.new(idx, packer)
52
+ end
53
+ end
54
+ r.any? ? r.sort_by{ |x| -x.packer.size } : nil
55
+ end
56
+
57
+ def method_missing *args, &block
58
+ all.respond_to?(args.first) ? all.send(*args,&block) : super
59
+ end
60
+
61
+ def unmarshal
62
+ File.open(BIN_SIGS_FILE,"rb") do |f|
63
+ Marshal.load(f)
64
+ end
65
+ rescue
66
+ nil
67
+ end
68
+
69
+ # parse text signatures
70
+ def parse fname = TEXT_SIGS_FILE
71
+ sigs = {}; sig = nil
72
+
73
+ File.open(fname,'r:utf-8') do |f|
74
+ while line = f.gets
75
+ line.strip!
76
+
77
+ # XXX
78
+ # "B\xE9rczi G\xE1bor".force_encoding('binary').to_yaml:
79
+ # RuntimeError: expected SCALAR, SEQUENCE-START, MAPPING-START, or ALIAS
80
+
81
+ case line
82
+ when /^;/,/^$/
83
+ next
84
+ when /^\[(.+)\]$/
85
+ sig = Packer.new($1.sub(/^\*\s+/,'').sub(/\s+\(h\)$/,''))
86
+ when /^signature = (.+)$/
87
+ sig.re = $1
88
+ if sigs[sig.re]
89
+ next if sigs[sig.re].name == sig.name
90
+ printf "[?] dup %-40s, %s\n", sigs[sig.re].name.inspect, sig.name.inspect
91
+ end
92
+ sigs[sig.re] = sig
93
+ when /^ep_only = (.+)$/
94
+ sig.ep_only = ($1.strip.downcase == 'true')
95
+ else raise line
96
+ end
97
+ end
98
+ end
99
+
100
+ sigs = sigs.values
101
+ sigs.each do |sig|
102
+ sig.re = Regexp.new(
103
+ sig.re.split(' ').tap do |a|
104
+ sig.size = a.size
105
+ end.map do |x|
106
+ case x
107
+ when '??'
108
+ '.'
109
+ when /[a-f0-9]{2}/i
110
+ Regexp::escape x.to_i(16).chr
111
+ else raise x
112
+ end
113
+ end.join
114
+ )
115
+ if sig.name[/-+>/]
116
+ a = sig.name.split(/-+>/,2).map(&:strip)
117
+ sig.name = "#{a[0]} (#{a[1]})"
118
+ end
119
+ end
120
+ sigs
121
+ end
122
+ end
123
+ end
124
+ end
@@ -0,0 +1,10 @@
1
+ class PEdump
2
+ module Version
3
+ MAJOR = 0
4
+ MINOR = 3
5
+ PATCH = 3
6
+ BUILD = nil
7
+
8
+ STRING = [MAJOR, MINOR, PATCH, BUILD].compact.join('.')
9
+ end
10
+ end
data/lib/pedump.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  #!/usr/bin/env ruby
2
2
  require 'logger'
3
+ require 'pedump/version'
3
4
 
4
5
  # pedump.rb by zed_0xff
5
6
  #
@@ -37,6 +38,8 @@ end
37
38
  class PEdump
38
39
  attr_accessor :fname, :logger, :force
39
40
 
41
+ VERSION = Version::STRING
42
+
40
43
  def initialize fname, params = {}
41
44
  @fname = fname
42
45
  @force = params[:force]
@@ -522,6 +525,7 @@ class PEdump
522
525
  nil
523
526
  end
524
527
  end
528
+ x[tbl] && x[tbl].compact!
525
529
  end
526
530
  if x.original_first_thunk && !x.first_thunk
527
531
  logger.warn "[?] import table: empty FirstThunk of #{x.module_name}"
@@ -967,6 +971,28 @@ class PEdump
967
971
  end
968
972
  end.flatten.compact
969
973
  end
974
+
975
+ def packer f = nil
976
+ @packer ||= pe(f) && @pe.ioh &&
977
+ begin
978
+ if !(va=@pe.ioh.AddressOfEntryPoint)
979
+ logger.error "[?] can't find EntryPoint RVA"
980
+ nil
981
+ elsif !(ofs = va2file(va))
982
+ logger.error "[?] can't find EntryPoint RVA (0x#{va.to_s(16)}) file offset"
983
+ nil
984
+ else
985
+ require 'pedump/packer'
986
+ if PEdump::Packer.all.size == 0
987
+ logger.error "[?] no packer definitions found"
988
+ nil
989
+ else
990
+ Packer.of(f, ofs)
991
+ end
992
+ end
993
+ end
994
+ end
995
+ alias :packers :packer
970
996
  end
971
997
 
972
998
  ####################################################################################
data/pedump.gemspec CHANGED
@@ -5,17 +5,18 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = "pedump"
8
- s.version = "0.3.2"
8
+ s.version = "0.3.3"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Andrey \"Zed\" Zaikin"]
12
- s.date = "2011-12-12"
12
+ s.date = "2011-12-13"
13
13
  s.description = "dump headers, sections, extract resources of win32 PE exe,dll,etc"
14
14
  s.email = "zed.0xff@gmail.com"
15
15
  s.executables = ["pedump"]
16
16
  s.extra_rdoc_files = [
17
17
  "LICENSE.txt",
18
- "README.rdoc"
18
+ "README.md",
19
+ "README.md.tpl"
19
20
  ]
20
21
  s.files = [
21
22
  ".document",
@@ -23,13 +24,19 @@ Gem::Specification.new do |s|
23
24
  "Gemfile",
24
25
  "Gemfile.lock",
25
26
  "LICENSE.txt",
26
- "README.rdoc",
27
+ "README.md",
28
+ "README.md.tpl",
27
29
  "Rakefile",
28
30
  "VERSION",
29
31
  "bin/pedump",
32
+ "data/sig.bin",
33
+ "data/sig.txt",
30
34
  "lib/pedump.rb",
31
35
  "lib/pedump/cli.rb",
36
+ "lib/pedump/packer.rb",
37
+ "lib/pedump/version.rb",
32
38
  "pedump.gemspec",
39
+ "samples/calc.7z",
33
40
  "spec/pedump_spec.rb",
34
41
  "spec/spec_helper.rb"
35
42
  ]
data/samples/calc.7z ADDED
Binary file
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pedump
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.2
4
+ version: 0.3.3
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2011-12-12 00:00:00.000000000Z
12
+ date: 2011-12-13 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: multipart-post
16
- requirement: &70099128727380 !ruby/object:Gem::Requirement
16
+ requirement: &70135365235740 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ~>
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: 1.1.4
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *70099128727380
24
+ version_requirements: *70135365235740
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: progressbar
27
- requirement: &70099128726580 !ruby/object:Gem::Requirement
27
+ requirement: &70135365807020 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ~>
@@ -32,10 +32,10 @@ dependencies:
32
32
  version: 0.9.2
33
33
  type: :runtime
34
34
  prerelease: false
35
- version_requirements: *70099128726580
35
+ version_requirements: *70135365807020
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: rspec
38
- requirement: &70099128724780 !ruby/object:Gem::Requirement
38
+ requirement: &70135365806000 !ruby/object:Gem::Requirement
39
39
  none: false
40
40
  requirements:
41
41
  - - ~>
@@ -43,10 +43,10 @@ dependencies:
43
43
  version: 2.3.0
44
44
  type: :development
45
45
  prerelease: false
46
- version_requirements: *70099128724780
46
+ version_requirements: *70135365806000
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: bundler
49
- requirement: &70099128713520 !ruby/object:Gem::Requirement
49
+ requirement: &70135365804780 !ruby/object:Gem::Requirement
50
50
  none: false
51
51
  requirements:
52
52
  - - ~>
@@ -54,10 +54,10 @@ dependencies:
54
54
  version: 1.0.0
55
55
  type: :development
56
56
  prerelease: false
57
- version_requirements: *70099128713520
57
+ version_requirements: *70135365804780
58
58
  - !ruby/object:Gem::Dependency
59
59
  name: jeweler
60
- requirement: &70099128712420 !ruby/object:Gem::Requirement
60
+ requirement: &70135365803680 !ruby/object:Gem::Requirement
61
61
  none: false
62
62
  requirements:
63
63
  - - ~>
@@ -65,10 +65,10 @@ dependencies:
65
65
  version: 1.6.4
66
66
  type: :development
67
67
  prerelease: false
68
- version_requirements: *70099128712420
68
+ version_requirements: *70135365803680
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: rcov
71
- requirement: &70099128711520 !ruby/object:Gem::Requirement
71
+ requirement: &70135365802300 !ruby/object:Gem::Requirement
72
72
  none: false
73
73
  requirements:
74
74
  - - ! '>='
@@ -76,7 +76,7 @@ dependencies:
76
76
  version: '0'
77
77
  type: :development
78
78
  prerelease: false
79
- version_requirements: *70099128711520
79
+ version_requirements: *70135365802300
80
80
  description: dump headers, sections, extract resources of win32 PE exe,dll,etc
81
81
  email: zed.0xff@gmail.com
82
82
  executables:
@@ -84,20 +84,27 @@ executables:
84
84
  extensions: []
85
85
  extra_rdoc_files:
86
86
  - LICENSE.txt
87
- - README.rdoc
87
+ - README.md
88
+ - README.md.tpl
88
89
  files:
89
90
  - .document
90
91
  - .rspec
91
92
  - Gemfile
92
93
  - Gemfile.lock
93
94
  - LICENSE.txt
94
- - README.rdoc
95
+ - README.md
96
+ - README.md.tpl
95
97
  - Rakefile
96
98
  - VERSION
97
99
  - bin/pedump
100
+ - data/sig.bin
101
+ - data/sig.txt
98
102
  - lib/pedump.rb
99
103
  - lib/pedump/cli.rb
104
+ - lib/pedump/packer.rb
105
+ - lib/pedump/version.rb
100
106
  - pedump.gemspec
107
+ - samples/calc.7z
101
108
  - spec/pedump_spec.rb
102
109
  - spec/spec_helper.rb
103
110
  homepage: http://github.com/zed-0xff/pedump
@@ -115,7 +122,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
115
122
  version: '0'
116
123
  segments:
117
124
  - 0
118
- hash: 3702742729748465882
125
+ hash: -3106600206779889876
119
126
  required_rubygems_version: !ruby/object:Gem::Requirement
120
127
  none: false
121
128
  requirements:
data/README.rdoc DELETED
@@ -1,19 +0,0 @@
1
- = pedump
2
-
3
- Description goes here.
4
-
5
- == Contributing to pedump
6
-
7
- * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
8
- * Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
9
- * Fork the project
10
- * Start a feature/bugfix branch
11
- * Commit and push until you are happy with your contribution
12
- * Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
13
- * Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
14
-
15
- == Copyright
16
-
17
- Copyright (c) 2011 Andrey "Zed" Zaikin. See LICENSE.txt for
18
- further details.
19
-