Class: Android::Apk

Inherits:
Object
  • Object
show all
Defined in:
lib/android/apk.rb

Overview

apk object class

Constant Summary collapse

MANIFEST =

AndroidManifest file name

'AndroidManifest.xml'
DEX =

dex file name

'classes.dex'
RESOURCE =

resource file name

'resources.arsc'

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(filepath) ⇒ Apk

create new apk object

Parameters:

  • apk file path

Raises:

  • path file does’nt exist

  • path file is not Apk file.



40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
# File 'lib/android/apk.rb', line 40

def initialize(filepath)
  Zip.warn_invalid_date = false

  @path = filepath
  raise NotFoundError, "'#{filepath}'" unless File.exist? @path

  @bindata = File.open(@path, 'rb') {|f| f.read }
  @bindata.force_encoding(Encoding::ASCII_8BIT)
  raise NotApkFileError, 'manifest file is not found.' if zip.find_entry(MANIFEST).nil?

  begin
    @resource = Android::Resource.new(self.file(RESOURCE))
  rescue => e
    logger.error "failed to parse resource: #{e} with backtrace"
    logger.error e.backtrace
  end

  begin
    @manifest = Android::Manifest.new(self.file(MANIFEST), @resource)
  rescue => e
    logger.error "failed to parse manifest:#{e} with backtrace"
    logger.error e.backtrace
  end

  begin
    @dex = Android::Dex.new(self.file(DEX))
  rescue => e
    logger.error "failed to parse dex:#{e} with backtrace"
    logger.error e.backtrace
  end
end

Instance Attribute Details

#bindataString (readonly)

Returns binary data of apk.

Returns:

  • binary data of apk



24
25
26
# File 'lib/android/apk.rb', line 24

def bindata
  @bindata
end

#dexAndroid::Dex? (readonly)

Returns:

  • dex instance

  • when parsing dex is failed.



22
23
24
# File 'lib/android/apk.rb', line 22

def dex
  @dex
end

#manifestAndroid::Manifest? (readonly)

Returns:

  • manifest instance

  • when parsing manifest is failed.



19
20
21
# File 'lib/android/apk.rb', line 19

def manifest
  @manifest
end

#pathString (readonly)

Returns apk file path.

Returns:

  • apk file path



16
17
18
# File 'lib/android/apk.rb', line 16

def path
  @path
end

#resourceResource? (readonly)

Returns:

  • resouce data

  • when parsing resource is failed.



27
28
29
# File 'lib/android/apk.rb', line 27

def resource
  @resource
end

Instance Method Details

#archsArray<String> Also known as: architectures

Return all architectures (all most for universal apk)

Returns:

Since:

  • 2.7.0



227
228
229
230
231
232
# File 'lib/android/apk.rb', line 227

def archs
  @archs ||= zip.glob('lib/**/*').each_with_object([]) do |entry, obj|
    arch = entry.name.split('/')[1]
    obj << arch unless obj.include?(arch)
  end
end

#certificatesHash{String => OpenSSL::X509::Certificate }

v1 certificate info which is used for signing

Returns:

  • key: sign file path, value: first certficate in the sign file

Since:

  • 0.7.0



220
221
222
# File 'lib/android/apk.rb', line 220

def certificates
  @certificates ||= Hash[self.signs.map{|path, sign| [path, sign.certificates.first] }]
end

#digest(type = :sha1) ⇒ String

return hex digest string of apk file

Parameters:

  • (defaults to: :sha1)

    hash digest type(:sha1, sha256, :md5)

Returns:

  • hex digest string

Raises:

  • type is knknown type



82
83
84
85
86
87
88
89
90
91
92
93
# File 'lib/android/apk.rb', line 82

def digest(type = :sha1)
  case type
  when :sha1
    Digest::SHA1.hexdigest(@bindata)
  when :sha256
    Digest::SHA256.hexdigest(@bindata)
  when :md5
    Digest::MD5.hexdigest(@bindata)
  else
    raise ArgumentError
  end
end

#each_entry {|entry| ... } ⇒ Object

Yields:

Yield Parameters:

  • entry (Zip::Entry)

    zip entry



121
122
123
124
125
126
# File 'lib/android/apk.rb', line 121

def each_entry
  zip.each do |entry|
    next unless entry.file?
    yield entry
  end
end

#each_file {|name, data| ... } ⇒ Object

Yields:

  • (name, data)

Yield Parameters:

  • name (String)

    file name in apk

  • data (String)

    file data in apk



104
105
106
107
108
109
# File 'lib/android/apk.rb', line 104

def each_file
  zip.each do |entry|
    next unless entry.file?
    yield entry.name, zip.read(entry)
  end
end

#entry(name) ⇒ Zip::Entry

find and return zip entry with name

Parameters:

  • file name in apk(fullpath)

Returns:

  • zip entry object

Raises:

  • when ‘name’ doesn’t exist in the apk



132
133
134
135
136
# File 'lib/android/apk.rb', line 132

def entry(name)
  entry = zip.find_entry(name)
  raise NotFoundError, "'#{name}'" if entry.nil?
  return entry
end

#file(name) ⇒ String

find and return binary data with name

Parameters:

  • file name in apk(fullpath)

Returns:

  • binary data

Raises:

  • when ‘name’ doesn’t exist in the apk



115
116
117
# File 'lib/android/apk.rb', line 115

def file(name) # get data by entry name(path)
  zip.read(entry(name))
end

#find {|name, data| ... } ⇒ Array

find files which is matched with block condition

Examples:

apk = Apk.new(path)
elf_files = apk.find  { |name, data|  data[0..3] == [0x7f, 0x45, 0x4c, 0x46] } # ELF magic number

Yields:

  • (name, data)

    find condition

Yield Parameters:

  • name (String)

    file name in apk

  • data (String)

    file data in apk

Yield Returns:

  • (Array)

    Array of matched entry name

Returns:

  • Array of matched entry name



147
148
149
150
151
152
153
154
# File 'lib/android/apk.rb', line 147

def find(&block)
  found = []
  self.each_file do |name, data|
    ret = block.call(name, data)
    found << name if ret
  end
  found
end

#iconHash{ String => String }

extract application icon data from AndroidManifest and resource.

Returns:

  • hash key is icon filename. value is image data

Raises:

Since:

  • 0.6.0



160
161
162
163
# File 'lib/android/apk.rb', line 160

def icon
  icon_id = @manifest.doc.elements['/manifest/application'].attributes['icon']
  icon_by_id(icon_id)
end

#icon_by_id(icon_id) ⇒ Hash{ String => String }

extract icon data from AndroidManifest and resource by a given icon id.

Parameters:

  • to be searched in the resource.

Returns:

  • hash key is icon filename. value is image data

Raises:

Since:

  • 0.6.0



170
171
172
173
174
175
176
177
178
179
180
181
# File 'lib/android/apk.rb', line 170

def icon_by_id(icon_id)
  if /^@(\w+\/\w+)|(0x[0-9a-fA-F]{8})$/ =~ icon_id
    drawables = @resource.find(icon_id)
    Hash[drawables.map {|name| [name, file(name)] }]
  else
    begin
      { icon_id => file(icon_id) } # ugh!: not tested!!
    rescue NotFoundError
      {}
    end
  end
end

#kotlin?Boolean

detect if use kotlin language (may be third-party sdk or not)

Returns:

Since:

  • 2.6.0



245
246
247
# File 'lib/android/apk.rb', line 245

def kotlin?
  @kotlin ||= kotlin_file? || kotlin_classes?
end

#label(lang = nil) ⇒ String?

Deprecated.

move to Manifest#label

get application label from AndroidManifest and resources.

Parameters:

  • (defaults to: nil)

    language code like ‘ja’, ‘cn’, …

Returns:

  • application label string

  • when label is not found

Since:

  • 0.6.0



189
190
191
# File 'lib/android/apk.rb', line 189

def label(lang = nil)
  @manifest.label
end

#layoutsHash{ String => Android::Layout }

get screen layout xml datas

Returns:

  • key: laytout file path, value: layout object

Since:

  • 0.6.0



196
197
198
# File 'lib/android/apk.rb', line 196

def layouts
  @layouts ||= Layout.collect_layouts(self) # lazy parse
end

#signsHash{ String => OpenSSL::PKCS7 } Also known as: signatures

apk’s v1 signature information

Returns:

  • key: sign file path, value: signature

Since:

  • 0.7.0



203
204
205
206
207
208
209
210
211
212
213
214
# File 'lib/android/apk.rb', line 203

def signs
  @signs ||= lambda {
    signs = {}
    self.each_file do |path, data|
      # find META-INF/xxx.{RSA|DSA|EC}
      next unless path =~ /^META-INF\// && data.unpack("CC") == [0x30, 0x82]

      signs[path] = OpenSSL::PKCS7.new(data)
    end
    signs
  }.call
end

#sizeInteger

return apk file size

Returns:

  • bytes



74
75
76
# File 'lib/android/apk.rb', line 74

def size
  @bindata.size
end

#timeTime

returns date of AndroidManifest.xml as Apk date

Returns:



97
98
99
# File 'lib/android/apk.rb', line 97

def time
  entry(MANIFEST).time
end

#universal?Boolean

detect if contains multi-platforms (native machine code)

Returns:

Since:

  • 2.7.0



238
239
240
# File 'lib/android/apk.rb', line 238

def universal?
  archs.size > 1
end