pedump 0.6.0 → 0.6.4

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.
data/lib/pedump/cli.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  require 'pedump'
2
2
  require 'pedump/packer'
3
+ require 'pedump/rich'
3
4
  require 'pedump/version_info'
4
5
  require 'optparse'
5
6
 
@@ -34,12 +35,13 @@ class PEdump::CLI
34
35
 
35
36
  KNOWN_ACTIONS = (
36
37
  %w'mz dos_stub rich pe ne te data_directory sections tls security' +
37
- %w'strings resources resource_directory imports exports version_info packer web console packer_only'
38
+ %w'strings resources resource_directory imports exports version_info packer web console packer_only' +
39
+ %w'extract' # 'disasm'
38
40
  ).map(&:to_sym)
39
41
 
40
- DEFAULT_ALL_ACTIONS = KNOWN_ACTIONS - %w'resource_directory web packer_only console'.map(&:to_sym)
42
+ DEFAULT_ALL_ACTIONS = KNOWN_ACTIONS - %w'resource_directory web packer_only console extract disasm'.map(&:to_sym)
41
43
 
42
- URL_BASE = "http://pedump.me"
44
+ URL_BASE = "https://pedump.me"
43
45
 
44
46
  def initialize argv = ARGV
45
47
  @argv = argv
@@ -96,8 +98,24 @@ class PEdump::CLI
96
98
  opts.on "--all", "Dump all but resource-directory (default)" do
97
99
  @actions = DEFAULT_ALL_ACTIONS
98
100
  end
101
+
102
+ opts.separator ''
103
+
104
+ # opts.on "--disasm [X]", "Disassemble a symbol/VA" do |v|
105
+ # @actions << [:disasm, v]
106
+ # end
107
+ opts.on("--extract ID", "Extract a resource/section/data_dir",
108
+ "ID: datadir:EXPORT - datadir by type",
109
+ "ID: resource:0x98478 - resource by offset",
110
+ "ID: resource:ICON/#1 - resource by type & name",
111
+ "ID: section:.text - section by name",
112
+ "ID: section:rva/0x1000 - section by RVA",
113
+ "ID: section:raw/0x400 - section by RAW_PTR",
114
+ ) do |v|
115
+ @actions << [:extract, v]
116
+ end
99
117
  opts.on "--va2file VA", "Convert RVA to file offset" do |va|
100
- @actions << [:va2file,va]
118
+ @actions << [:va2file, va]
101
119
  end
102
120
 
103
121
  opts.separator ''
@@ -133,7 +151,7 @@ class PEdump::CLI
133
151
  @file_name = fname
134
152
 
135
153
  File.open(fname,'rb') do |f|
136
- @pedump = create_pedump fname
154
+ @pedump = create_pedump f
137
155
 
138
156
  next if !@options[:force] && !@pedump.supported_file?(f)
139
157
 
@@ -152,8 +170,8 @@ class PEdump::CLI
152
170
  # prevents a 'Broken pipe - <STDOUT> (Errno::EPIPE)' message
153
171
  end
154
172
 
155
- def create_pedump fname
156
- PEdump.new(fname, :force => @options[:force]).tap do |x|
173
+ def create_pedump io
174
+ PEdump.new(io, :force => @options[:force]).tap do |x|
157
175
  x.logger.level =
158
176
  case @options[:verbose]
159
177
  when -100..-3
@@ -194,7 +212,7 @@ class PEdump::CLI
194
212
  end
195
213
 
196
214
  class ProgressProxy
197
- def initialize file, prefix = "[.] uploading: ", io = STDOUT
215
+ def initialize file, prefix = "[.] uploading: ", io = $stdout
198
216
  @file = file
199
217
  @io = io
200
218
  @prefix = prefix
@@ -227,15 +245,16 @@ class PEdump::CLI
227
245
  require 'net/http'
228
246
  require 'net/http/post/multipart'
229
247
 
230
- stdout_sync = STDOUT.sync
231
- STDOUT.sync = true
248
+ stdout_sync = $stdout.sync
249
+ $stdout.sync = true
232
250
 
233
251
  md5 = Digest::MD5.file(f.path).hexdigest
234
252
  @pedump.logger.info "[.] md5: #{md5}"
235
253
  file_url = "#{URL_BASE}/#{md5}/"
236
254
 
237
255
  @pedump.logger.warn "[.] checking if file already uploaded.."
238
- Net::HTTP.start('pedump.me') do |http|
256
+ uri = URI.parse URL_BASE
257
+ Net::HTTP.start(uri.host, uri.port, use_ssl: (uri.scheme == 'https')) do |http|
239
258
  http.open_timeout = 10
240
259
  http.read_timeout = 10
241
260
  # doing HTTP HEAD is a lot faster than open-uri
@@ -257,18 +276,20 @@ class PEdump::CLI
257
276
  uio = UploadIO.new(f, "application/octet-stream", File.basename(f.path))
258
277
  ppx = ProgressProxy.new(uio)
259
278
  req = Net::HTTP::Post::Multipart.new post_url.path, "file" => ppx
260
- res = Net::HTTP.start(post_url.host, post_url.port){ |http| http.request(req) }
279
+ res = Net::HTTP.start(post_url.host, post_url.port, use_ssl: (post_url.scheme == 'https')) do |http|
280
+ http.request(req)
281
+ end
261
282
  ppx.finish!
262
283
 
263
- puts "[.] analyzing..."
264
-
265
- if (r=open(File.join(URL_BASE,md5,'analyze')).read) != "OK"
266
- raise "invalid server response: #{r}"
284
+ unless [200, 302, 303].include?(res.code.to_i)
285
+ @pedump.logger.fatal "[!] invalid server response: #{res.code} #{res.msg}"
286
+ @pedump.logger.fatal res.body unless res.body.empty?
287
+ exit(1)
267
288
  end
268
289
 
269
290
  puts "[.] uploaded: #{file_url}"
270
291
  ensure
271
- STDOUT.sync = stdout_sync
292
+ $stdout.sync = stdout_sync
272
293
  end
273
294
 
274
295
  def console f
@@ -312,6 +333,10 @@ class PEdump::CLI
312
333
  def dump_action action, f
313
334
  if action.is_a?(Array)
314
335
  case action[0]
336
+ when :disasm
337
+ return
338
+ when :extract
339
+ return extract action[1]
315
340
  when :va2file
316
341
  @pedump.sections(f)
317
342
  va = action[1] =~ /(^0x)|(h$)/i ? action[1].to_i(16) : action[1].to_i
@@ -798,10 +823,12 @@ class PEdump::CLI
798
823
 
799
824
  def dump_rich_hdr data
800
825
  if decoded = data.decode
801
- puts " LIB_ID VERSION TIMES_USED "
826
+ puts " ID VER COUNT DESCRIPTION"
802
827
  decoded.each do |row|
803
- printf " %5d %2x %7d %4x %7d %3x\n",
804
- row.id, row.id, row.version, row.version, row.times, row.times
828
+ # printf " %5d %4x %7d %4x %12d %8x\n",
829
+ # row.id, row.id, row.version, row.version, row.times, row.times
830
+ printf " %4x %4x %12d %s\n",
831
+ row.id, row.version, row.times, ::PEdump::RICH_IDS[(row.id<<16) + row.version]
805
832
  end
806
833
  else
807
834
  puts "# raw:"
@@ -812,4 +839,79 @@ class PEdump::CLI
812
839
  end
813
840
  end
814
841
 
842
+ def disasm x
843
+ puts "TODO"
844
+ end
845
+
846
+ def extract x
847
+ a = x.split(':',2)
848
+ case a[0]
849
+ when 'datadir'
850
+ extract_datadir a[1]
851
+ when 'resource'
852
+ extract_resource a[1]
853
+ when 'section'
854
+ extract_section a[1]
855
+ else
856
+ raise "invalid #{x.inspect}"
857
+ end
858
+ end
859
+
860
+ def extract_datadir id
861
+ entry = @pedump.data_directory.find{ |x| x.type == id }
862
+ unless entry
863
+ @pedump.logger.fatal "[!] entry #{id.inspect} not found"
864
+ exit(1)
865
+ end
866
+ if entry.size != 0
867
+ IO::copy_stream @pedump.io, $stdout, entry.size, @pedump.va2file(entry.va)
868
+ end
869
+ end
870
+
871
+ def extract_resource id
872
+ res = nil
873
+ if id =~ /\A0x\h+\Z/
874
+ needle = id.to_i(16)
875
+ res = @pedump.resources.find{ |r| r.file_offset == needle }
876
+ elsif id =~ /\A\d+\Z/
877
+ needle = id.to_i(10)
878
+ res = @pedump.resources.find{ |r| r.file_offset == needle }
879
+ elsif id['/']
880
+ type, name = id.split('/', 2)
881
+ res = @pedump.resources.find{ |r| r.type == type && r.name == name }
882
+ else
883
+ @pedump.logger.fatal "[!] invalid resource id #{id.inspect}"
884
+ exit(1)
885
+ end
886
+ unless res
887
+ @pedump.logger.fatal "[!] resource #{id.inspect} not found"
888
+ exit(1)
889
+ end
890
+ IO::copy_stream @pedump.io, $stdout, res.size, res.file_offset
891
+ end
892
+
893
+ def extract_section id
894
+ section = nil
895
+ if id['/']
896
+ a = id.split('/',2)
897
+ if a[0] == 'rva'
898
+ value = a[1].to_i(0) # auto hex/dec, but also can parse oct and bin
899
+ section = @pedump.sections.find{ |s| s.VirtualAddress == value }
900
+ elsif a[0] == 'raw'
901
+ value = a[1].to_i(0) # auto hex/dec, but also can parse oct and bin
902
+ section = @pedump.sections.find{ |s| s.PointerToRawData == value }
903
+ else
904
+ @pedump.logger.fatal "[!] invalid section id #{id.inspect}"
905
+ exit(1)
906
+ end
907
+ else
908
+ section = @pedump.sections.find{ |s| s.Name == id }
909
+ end
910
+ unless section
911
+ @pedump.logger.fatal "[!] section #{id.inspect} not found"
912
+ exit(1)
913
+ end
914
+ IO::copy_stream @pedump.io, $stdout, section.SizeOfRawData, section.PointerToRawData
915
+ end
916
+
815
917
  end # class PEdump::CLI
@@ -10,15 +10,16 @@ class PEdump::Loader
10
10
  @hdr = x.dup
11
11
  end
12
12
  @data = EMPTY_DATA.dup
13
+ @delta = args[:delta] || 0
13
14
  @deferred_load_io = args[:deferred_load_io]
14
- @deferred_load_pos = args[:deferred_load_pos] || (@hdr && @hdr.PointerToRawData)
15
+ @deferred_load_pos = args[:deferred_load_pos] || (@hdr && (@hdr.PointerToRawData - @delta))
15
16
  @deferred_load_size = args[:deferred_load_size] || (@hdr && @hdr.SizeOfRawData)
16
17
  @image_base = args[:image_base] || 0
17
18
  end
18
19
 
19
20
  def name; @hdr.Name; end
20
- def va ; @hdr.VirtualAddress + @image_base; end
21
- def rva ; @hdr.VirtualAddress; end
21
+ def va ; @hdr.VirtualAddress + @image_base - @delta; end
22
+ def rva ; @hdr.VirtualAddress - @delta; end
22
23
  def vsize; @hdr.VirtualSize; end
23
24
  def flags; @hdr.Characteristics; end
24
25
  def flags= f; @hdr.Characteristics= f; end
@@ -51,6 +52,7 @@ class PEdump::Loader
51
52
  @data.size > 0 ? @data.size.to_s(16) : (@deferred_load_io ? "<defer>" : 0)
52
53
  ]
53
54
  r << (" dlpos=%8x" % @deferred_load_pos) if @deferred_load_pos
55
+ r << (" delta=%3x" % @delta) if @delta != 0
54
56
  r << ">"
55
57
  end
56
58
  end
data/lib/pedump/loader.rb CHANGED
@@ -15,7 +15,15 @@ class PEdump::Loader
15
15
 
16
16
  # shortcuts
17
17
  alias :pe :pe_hdr
18
- def ep; @pe_hdr.ioh.AddressOfEntryPoint; end
18
+
19
+ def ep
20
+ if @pe_hdr
21
+ @pe_hdr.try(:ioh).try(:AddressOfEntryPoint)
22
+ elsif @te_hdr
23
+ @te_hdr.AddressOfEntryPoint - @delta
24
+ end
25
+ end
26
+
19
27
  def ep= v; @pe_hdr.ioh.AddressOfEntryPoint=v; end
20
28
 
21
29
  ########################################################################
@@ -23,12 +31,24 @@ class PEdump::Loader
23
31
  ########################################################################
24
32
 
25
33
  def initialize io = nil, params = {}
34
+ # @delta is for EFI TE loading, based on https://github.com/gdbinit/TELoader/blob/master/teloader.cpp
35
+ @delta = 0
26
36
  @pedump = PEdump.new(io, params)
27
37
  if io
28
38
  @mz_hdr = @pedump.mz
29
39
  @dos_stub = @pedump.dos_stub
30
40
  @pe_hdr = @pedump.pe
31
- @image_base = params[:image_base] || @pe_hdr.try(:ioh).try(:ImageBase) || 0
41
+ @te_hdr = @pedump.te
42
+
43
+ @image_base = params[:image_base]
44
+ if @pe_hdr
45
+ @image_base ||= @pe_hdr.try(:ioh).try(:ImageBase)
46
+ elsif @te_hdr
47
+ @image_base ||= @te_hdr.ImageBase
48
+ @delta = @pedump.te_shift
49
+ end
50
+ @image_base ||= 0
51
+
32
52
  load_sections @pedump.sections, io
33
53
  end
34
54
  @find_limit = params[:find_limit] || DEFAULT_FIND_LIMIT
@@ -38,7 +58,7 @@ class PEdump::Loader
38
58
  if section_hdrs.is_a?(Array)
39
59
  @sections = section_hdrs.map do |x|
40
60
  raise "unknown section hdr: #{x.inspect}" unless x.is_a?(PEdump::IMAGE_SECTION_HEADER)
41
- Section.new(x, :deferred_load_io => f, :image_base => @image_base )
61
+ Section.new(x, :deferred_load_io => f, :image_base => @image_base, :delta => @delta )
42
62
  end
43
63
  if f.respond_to?(:seek) && f.respond_to?(:read)
44
64
  #
@@ -249,10 +269,12 @@ class PEdump::Loader
249
269
  def names
250
270
  return @names if @names
251
271
  @names = {}
252
- if oep = @pe_hdr.try(:ioh).try(:AddressOfEntryPoint)
253
- oep += @image_base
254
- @names[oep] = 'start'
272
+
273
+ oep = ep()
274
+ if oep
275
+ @names[oep + @image_base] = 'start'
255
276
  end
277
+
256
278
  _parse_imports
257
279
  _parse_exports
258
280
  #TODO: debug info