class PEdump::Resource
Public Instance Methods
bitmap_hdr()
click to toggle source
# File lib/pedump/resources.rb, line 29 def bitmap_hdr bmp_info_hdr = data.find{ |x| x.is_a?(BITMAPINFOHEADER) } raise "no BITMAPINFOHEADER for #{self.type} #{self.name}" unless bmp_info_hdr bmp_info_hdr.biHeight/=2 if %w'ICON CURSOR'.include?(type) colors_used = bmp_info_hdr.biClrUsed colors_used = 2**bmp_info_hdr.biBitCount if colors_used == 0 && bmp_info_hdr.biBitCount < 16 # XXX: one byte in each color is unused! @palette_size = colors_used * 4 # each color takes 4 bytes # scanlines are DWORD-aligned and padded to DWORD-align with zeroes # XXX: some data may be hidden in padding bytes! scanline_size = bmp_info_hdr.biWidth * bmp_info_hdr.biBitCount / 8 scanline_size += (4-scanline_size%4) if scanline_size % 4 > 0 @imgdata_size = scanline_size * bmp_info_hdr.biHeight "BM" + [ BITMAPINFOHEADER::SIZE + 14 + @palette_size + @imgdata_size, 0, BITMAPINFOHEADER::SIZE + 14 + @palette_size ].pack("V3") + bmp_info_hdr.pack ensure bmp_info_hdr.biHeight*=2 if %w'ICON CURSOR'.include?(type) end
bitmap_mask(src_fname)
click to toggle source
only valid for types BITMAP, ICON & CURSOR
# File lib/pedump/resources.rb, line 98 def bitmap_mask src_fname File.open(src_fname, "rb") do |f| parse f bmp_info_hdr = bitmap_hdr bitmap_size = BITMAPINFOHEADER::SIZE + @palette_size + @imgdata_size return nil if bitmap_size >= self.size mask_size = self.size - bitmap_size f.seek file_offset + bitmap_size bmp_info_hdr = BITMAPINFOHEADER.new(*bmp_info_hdr[14..-1].unpack(BITMAPINFOHEADER::FORMAT)) bmp_info_hdr.biBitCount = 1 bmp_info_hdr.biCompression = bmp_info_hdr.biSizeImage = 0 bmp_info_hdr.biClrUsed = bmp_info_hdr.biClrImportant = 2 palette = [0,0xffffff].pack('V2') @palette_size = palette.size "BM" + [ BITMAPINFOHEADER::SIZE + 14 + @palette_size + mask_size, 0, BITMAPINFOHEADER::SIZE + 14 + @palette_size ].pack("V3") + bmp_info_hdr.pack + palette + f.read(mask_size) end end
parse(f)
click to toggle source
also sets the file position for restore_bitmap
next call
# File lib/pedump/resources.rb, line 125 def parse f raise "called parse with type not set" unless self.type #return if self.data self.data = [] return nil unless file_offset case type when 'BITMAP','ICON' f.seek file_offset if f.read(4) == "\x89PNG" data << 'PNG' else f.seek file_offset data << BITMAPINFOHEADER.read(f) end when 'CURSOR' f.seek file_offset data << CURSOR_HOTSPOT.read(f) data << BITMAPINFOHEADER.read(f) when 'GROUP_CURSOR' f.seek file_offset data << CUR_ICO_HEADER.read(f) nRead = CUR_ICO_HEADER::SIZE data.last.wNumImages.to_i.times do if nRead >= self.size PEdump.logger.error "[!] refusing to read CURDIRENTRY beyond resource size" break end data << CURDIRENTRY.read(f) nRead += CURDIRENTRY::SIZE end when 'GROUP_ICON' f.seek file_offset data << CUR_ICO_HEADER.read(f) nRead = CUR_ICO_HEADER::SIZE data.last.wNumImages.to_i.times do if nRead >= self.size PEdump.logger.error "[!] refusing to read ICODIRENTRY beyond resource size" break end data << ICODIRENTRY.read(f) nRead += ICODIRENTRY::SIZE end when 'STRING' f.seek file_offset 16.times do break if f.tell >= file_offset+self.size nChars = f.read(2).to_s.unpack('v').first.to_i t = if nChars*2 + 1 > self.size # TODO: if it's not 1st string in table then truncated size must be less PEdump.logger.error "[!] string size(#{nChars*2}) > stringtable size(#{self.size}). truncated to #{self.size-2}" f.read(self.size-2) else f.read(nChars*2) end data << begin t.force_encoding('UTF-16LE').encode!('UTF-8') rescue t.force_encoding('ASCII') tt = t.size > 0x10 ? t[0,0x10].inspect+'...' : t.inspect PEdump.logger.error "[!] cannot convert #{tt} to UTF-16" [nChars,t].pack('va*') end end # XXX: check if readed strings summary length is less than resource data length when 'VERSION' f.seek file_offset data << PEdump::VS_VERSIONINFO.read(f) end data.delete_if do |x| valid = !x.respond_to?(:valid?) || x.valid? PEdump.logger.warn "[?] ignoring invalid #{x.class}" unless valid !valid end ensure validate end
restore_bitmap(src_fname)
click to toggle source
only valid for types BITMAP, ICON & CURSOR
# File lib/pedump/resources.rb, line 57 def restore_bitmap src_fname File.open(src_fname, "rb") do |f| parse f if data.first == "PNG" "\x89PNG" +f.read(self.size-4) else bitmap_hdr + f.read(@palette_size + @imgdata_size) end end end
restore_icon(src_fname)
click to toggle source
only valid for types BITMAP, ICON & CURSOR
# File lib/pedump/resources.rb, line 69 def restore_icon src_fname File.open(src_fname, "rb") do |f| parse f if data.first == "PNG" "\x89PNG" +f.read(self.size-4) else icondir = [ 0, # Reserved. Must always be 0. 1, # image type: 1 for icon (.ICO), 2 for cursor (.CUR). Other values are invalid 1, # number of images in the file ].pack("v3") bitmap_hdr = data.first # BITMAPINFOHEADER icondirentry = ICODIRENTRY.new( bitmap_hdr.biWidth, bitmap_hdr.biHeight / (%w'ICON CURSOR'.include?(type) ? 2 : 1), 0, # XXX: bColors: may be wrong here 0, 1, bitmap_hdr.biBitCount, bitmap_hdr.biSizeImage, icondir.size + 2 + ICODIRENTRY::SIZE # offset of BMP data from the beginning of ICO file ) # ICONDIRENTRY is 2 bytes larger than ICODIRENTRY icondir + icondirentry.pack + "\x00\x00" + bitmap_hdr.pack + f.read(self.size) end end end
valid?()
click to toggle source
# File lib/pedump/resources.rb, line 220 def valid? valid end
validate()
click to toggle source
# File lib/pedump/resources.rb, line 207 def validate self.valid = self.file_offset && case type when 'BITMAP','ICON','CURSOR' data.any?{ |x| x.is_a?(BITMAPINFOHEADER) && x.valid? } || data.first == 'PNG' when 'GROUP_ICON' # rough validation data.first.is_a?(CUR_ICO_HEADER) && data.size == data.first.wNumImages.to_i+1 else true end end