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.



39
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
# File 'lib/android/apk.rb', line 39

def initialize(filepath)
  @path = filepath
  raise NotFoundError, "'#{filepath}'" unless File.exist? @path
  begin
    @zip = Zip::File.open(@path)
  rescue Zip::Error => e
    raise NotApkFileError, e.message 
  end

  @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
    $stderr.puts "failed to parse resource:#{e}"
    #$stderr.puts e.backtrace
  end
  begin
    @manifest = Android::Manifest.new(self.file(MANIFEST), @resource)
  rescue => e
    $stderr.puts "failed to parse manifest:#{e}"
    #$stderr.puts e.backtrace
  end
  begin
    @dex = Android::Dex.new(self.file(DEX))
  rescue => e
    $stderr.puts "failed to parse dex:#{e}"
    #$stderr.puts e.backtrace
  end
end

Instance Attribute Details

#bindataString (readonly)

Returns binary data of apk.

Returns:

  • binary data of apk



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

def bindata
  @bindata
end

#dexAndroid::Dex? (readonly)

Returns:

  • dex instance

  • when parsing dex is failed.



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

def dex
  @dex
end

#manifestAndroid::Manifest? (readonly)

Returns:

  • manifest instance

  • when parsing manifest is failed.



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

def manifest
  @manifest
end

#pathString (readonly)

Returns apk file path.

Returns:

  • apk file path



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

def path
  @path
end

#resourceResource? (readonly)

Returns:

  • resouce data

  • when parsing resource is failed.



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

def resource
  @resource
end

Instance Method Details

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

certificate info which is used for signing

Returns:

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

Since:

  • 0.7.0



202
203
204
# File 'lib/android/apk.rb', line 202

def certificates
  return 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



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

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



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

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



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

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

#entry(name) ⇒ Zip::ZipEntry

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



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

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



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

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



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

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 icon data from AndroidManifest and resource.

Returns:

  • hash key is icon filename. value is image data

Raises:

Since:

  • 0.6.0



159
160
161
162
163
164
165
166
167
# File 'lib/android/apk.rb', line 159

def icon
  icon_id = @manifest.doc.elements['/manifest/application'].attributes['icon']
  if /^@(\w+\/\w+)|(0x[0-9a-fA-F]{8})$/ =~ icon_id
    drawables = @resource.find(icon_id)
    Hash[drawables.map {|name| [name, file(name)] }]
  else 
    { icon_id => file(icon_id) } # ugh!: not tested!!
  end
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



175
176
177
# File 'lib/android/apk.rb', line 175

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



182
183
184
# File 'lib/android/apk.rb', line 182

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

#signsHash{ String => OpenSSL::PKCS7 }

apk’s signature information

Returns:

  • key: sign file path, value: signature

Since:

  • 0.7.0



189
190
191
192
193
194
195
196
197
# File 'lib/android/apk.rb', line 189

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

#sizeInteger

return apk file size

Returns:

  • bytes



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

def size
  @bindata.size
end

#timeTime

returns date of AndroidManifest.xml as Apk date

Returns:



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

def time
  entry(MANIFEST).time
end