Class: ApkResources

Inherits:
Object
  • Object
show all
Defined in:
lib/apktools/apkresources.rb

Overview

Class to parse an APK’s resources.arsc data and retrieve resource data associated with a given R.id value

Defined Under Namespace

Classes: ChunkHeader, Package, PackageHeader, ResType, ResTypeConfig, ResTypeEntry, ResTypeSpec, StringPool

Constant Summary collapse

DEBUG =

:nodoc:

false

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(apk_file) ⇒ ApkResources

Create a new ApkResources instance from the specified apk_file

This opens and parses the contents of the APK’s resources.arsc file.



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
174
# File 'lib/apktools/apkresources.rb', line 134

def initialize(apk_file)
  data = nil
  Zip.warn_invalid_date = false

  # Get resources.arsc from the APK file
  Zip::File.foreach(apk_file) do |f|
    if f.name.match(/resources.arsc/)
      data = f.get_input_stream.read.force_encoding('BINARY')
    end
  end

  # Parse the Table Chunk
  ## Header
  header_type = read_short(data, HEADER_START)
  header_size = read_short(data, HEADER_START+2)
  header_chunk_size = read_word(data, HEADER_START+4)
  header_package_count = read_word(data, HEADER_START+8)
  puts "Resource Package Count = #{header_package_count}" if DEBUG

  # Parse the StringPool Chunk
  ## Header
  startoffset_pool = HEADER_START + header_size
  puts "Parse Main StringPool Chunk" if DEBUG
  @stringpool_main = parse_stringpool(data, startoffset_pool)
  puts "#{@stringpool_main.values.length} strings found" if DEBUG

  # Parse the Package Chunk
  ## Header
  startoffset_package = startoffset_pool + @stringpool_main.header.chunk_size
  @packages = Hash.new()
  i = 0
  while i < header_package_count
    package_element = parse_package(data, startoffset_package)
    puts "Package #{package_element.package_header.id}" if DEBUG
    startoffset_package = startoffset_package + package_element.package_header.header.chunk_size
    @packages[package_element.package_header.id] = package_element

    i += 1
  end

end

Instance Attribute Details

#package_headerObject (readonly)

PackageHeader containing information about all the type and key strings in the package



124
125
126
# File 'lib/apktools/apkresources.rb', line 124

def package_header
  @package_header
end

#packagesObject (readonly)

Hash of Package chunks, keyed by package id



128
129
130
# File 'lib/apktools/apkresources.rb', line 128

def packages
  @packages
end

#stringpool_mainObject (readonly)

StringPool containing all value strings in the package



126
127
128
# File 'lib/apktools/apkresources.rb', line 126

def stringpool_main
  @stringpool_main
end

Instance Method Details

#get_all_keysObject

Return hash of all the key values in the file keyed by package id



200
201
202
203
204
205
206
207
# File 'lib/apktools/apkresources.rb', line 200

def get_all_keys
  keys = Hash.new()
  @packages.each do |key, value|
    keys[key] = value.stringpool_keystrings.values
  end

  return keys
end

#get_all_stringsObject

Return array of all string values in the file



179
180
181
# File 'lib/apktools/apkresources.rb', line 179

def get_all_strings
  return @stringpool_main.values
end

#get_all_typesObject

Return hash of all the type values in the file keyed by package id



187
188
189
190
191
192
193
194
# File 'lib/apktools/apkresources.rb', line 187

def get_all_types
  types = Hash.new()
  @packages.each do |key, value|
    types[key] = value.stringpool_typestrings.values
  end

  return types
end

#get_default_resource_value(res_id) ⇒ Object

Obtain the default value for a given resource id

res_id: ID values of a resources as a FixNum or String representation (i.e. 0x7F060001)

Returns: The default ResTypeEntry to the given id, or nil if no default exists



262
263
264
265
266
267
268
269
270
271
272
273
274
275
# File 'lib/apktools/apkresources.rb', line 262

def get_default_resource_value(res_id)
  if res_id.is_a? String
    res_id = res_id.hex
  end

  entries = get_resource_value(res_id)
  if entries != nil
    default = ResTypeConfig.new(0, 0, 0, 0, 0, 0, 0, 0)
    default_entry = entries[default]
    return default_entry
  else
    return nil
  end
end

#get_resource_key(res_id, xml_format = false) ⇒ Object

Obtain the key value for a given resource id

res_id: ID value of a resource as a FixNum or String representation (i.e. 0x7F060001) xml_format: Optionally format return string for XML files.

If xml_format is true, return value will be @<type>/<key> If xml_format is false or missing, return value will be R.<type>.<key> If the resource id does not exist, return value will be nil



219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
# File 'lib/apktools/apkresources.rb', line 219

def get_resource_key(res_id, xml_format=false)
  if res_id.is_a? String
    res_id = res_id.hex
  end

  # R.id integers are a concatenation of package_id, type_id, and entry index
  res_package = (res_id >> 24) & 0xFF
  res_type = (res_id >> 16) & 0xFF
  res_index = res_id & 0xFFFF

  package_element = @packages[res_package]
  if package_element == nil
    # This is not a resource we can parse
    return nil
  end

  res_spec = package_element.type_data[res_type-1]
  if res_spec == nil
    puts "Could not find ResTypeSpec for #{res_package} #{res_type}" if DEBUG
    return nil
  end

  entry = res_spec.types.entries[res_index]
  if entry == nil
    # There is no entry in our table for this resource
    puts "Could not find #{res_spec.types.id} ResType chunk" if DEBUG
    return nil
  end

  if xml_format
    return "@#{res_spec.id}/#{entry.values[0].key}"
  else
    return "R.#{res_spec.id}.#{entry.values[0].key}"
  end
end

#get_resource_value(res_id) ⇒ Object

Obtain the value(s) for a given resource id. A default resource is one defined in an unqualified directory.

res_id: ID value of a resource as a FixNum or String representation (i.e. 0x7F060001)

Returns: Hash of all entries matching this id, keyed by their matching ResTypeConfig or nil if the resource id cannot be found.



286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
# File 'lib/apktools/apkresources.rb', line 286

def get_resource_value(res_id)
  if res_id.is_a? String
    res_id = res_id.hex
  end

  # R.id integers are a concatenation of package_id, type_id, and entry index
  res_package = (res_id >> 24) & 0xFF
  res_type = (res_id >> 16) & 0xFF
  res_index = res_id & 0xFFFF

  package_element = @packages[res_package]
  if package_element == nil
    # This is not a resource we can parse
    return nil
  end

  res_spec = package_element.type_data[res_type-1]
  if res_spec == nil
    puts "Could not find ResTypeSpec for #{res_package} #{res_type}" if DEBUG
    return nil
  end

  entries = res_spec.types.entries[res_index]
  if entries == nil
    puts "Could not find #{res_spec.types.id} ResType chunk" if DEBUG
    return nil
  end

  return entries
end