Class: PEdump::Resource

Inherits:
Struct
  • Object
show all
Defined in:
lib/pedump/resources.rb

Direct Known Subclasses

NE::Resource

Instance Attribute Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#cpObject

Returns the value of attribute cp

Returns:

  • (Object)

    the current value of cp



28
29
30
# File 'lib/pedump/resources.rb', line 28

def cp
  @cp
end

#dataObject

Returns the value of attribute data

Returns:

  • (Object)

    the current value of data



28
29
30
# File 'lib/pedump/resources.rb', line 28

def data
  @data
end

#file_offsetObject

Returns the value of attribute file_offset

Returns:

  • (Object)

    the current value of file_offset



28
29
30
# File 'lib/pedump/resources.rb', line 28

def file_offset
  @file_offset
end

#idObject

Returns the value of attribute id

Returns:

  • (Object)

    the current value of id



28
29
30
# File 'lib/pedump/resources.rb', line 28

def id
  @id
end

#langObject

Returns the value of attribute lang

Returns:

  • (Object)

    the current value of lang



28
29
30
# File 'lib/pedump/resources.rb', line 28

def lang
  @lang
end

#nameObject

Returns the value of attribute name

Returns:

  • (Object)

    the current value of name



28
29
30
# File 'lib/pedump/resources.rb', line 28

def name
  @name
end

#reservedObject

Returns the value of attribute reserved

Returns:

  • (Object)

    the current value of reserved



28
29
30
# File 'lib/pedump/resources.rb', line 28

def reserved
  @reserved
end

#sizeObject

Returns the value of attribute size

Returns:

  • (Object)

    the current value of size



28
29
30
# File 'lib/pedump/resources.rb', line 28

def size
  @size
end

#typeObject

Returns the value of attribute type

Returns:

  • (Object)

    the current value of type



28
29
30
# File 'lib/pedump/resources.rb', line 28

def type
  @type
end

#validObject

Returns the value of attribute valid

Returns:

  • (Object)

    the current value of valid



28
29
30
# File 'lib/pedump/resources.rb', line 28

def valid
  @valid
end

Instance Method Details

#bitmap_hdrObject



29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
# 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) ⇒ Object



68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
# File 'lib/pedump/resources.rb', line 68

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) ⇒ Object

also sets the file position for restore_bitmap next call



95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
# File 'lib/pedump/resources.rb', line 95

def parse f
  raise "called parse with type not set" unless self.type
  #return if self.data

  self.data = []
  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) ⇒ Object

only valid for types BITMAP, ICON & CURSOR



57
58
59
60
61
62
63
64
65
66
# 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

#validateObject



175
176
177
178
179
180
181
182
183
# File 'lib/pedump/resources.rb', line 175

def validate
  self.valid =
    case type
    when 'BITMAP','ICON','CURSOR'
      data.any?{ |x| x.is_a?(BITMAPINFOHEADER) && x.valid? } || data.first == 'PNG'
    else
      true
    end
end