SecureBrain / ruby_apk

analyzing android apk library for ruby
MIT License
83 stars 52 forks source link

Querying label #1

Closed cielavenir closed 11 years ago

cielavenir commented 11 years ago

I'd like to query application-label. It isn't yet implemented? (although I can pipe aapt, it is quite heavy).

axml=Android::AXMLParser.new(zip.read('AndroidManifest.xml')) xml=axml.parse label_id=xml.get_elements('/manifest/application')[0].attributes['label'] arsc=Android::Resource.new(zip.read('resources.arsc'))

p arsc.query(label_id,'ja') #not implemented?

There are no ways to know label_id from arsc.strings...

masatanish commented 11 years ago

Thank you for your comment. Resource#query method is not implemented yet. Your suggestion sounds good.

I'll consider to implementing it. But I'm very busy now. If you have a pull requrest, I can merge it. Or please wait a few weeks.

cielavenir commented 11 years ago

1 month passed... Uh... Although I have little knowledge about resources.arsc, there was a little progress. After reading: http://justanapplication.wordpress.com/tag/androidresources/ http://justanapplication.wordpress.com/2011/09/17/android-internals-resources-part-six-the-typespec-chunk/ I was able to relate keyStrings to ID.

https://gist.github.com/4364699

Using my sample application http://dl.dropbox.com/u/46196585/mondayloader/MondayLoader.apk , apk.resource.keyStrings returns [[], ["ic_launcher"], ["main"], ["app_name", "text1", "text2", "textkey"], ["textView1", "textView2", "progressBar1", "textViewKey"]] So this code successfully points to "app_name".

Zip::ZipFile.open(path){|zip|
    xml=Android::AXMLParser.new(zip.read('AndroidManifest.xml')).parse
    label_id=xml.get_elements('/manifest/application')[0].attributes['label']
    query=label_id[3..-1].to_i(16) #0x7f040000
    arsc=Android::Resource.new(zip.read('resources.arsc'))
    p arsc.keyStrings
    p arsc.keyStrings[((query&0xff0000)>>16)-1][query&0xffff] 
}

But I still don't know how to relate this stuff with string pool...

Actually I'd like to add StreamParser to Android::AXMLParser, but that's after...

cielavenir commented 11 years ago

Another progress... https://gist.github.com/4365521

#!/usr/bin/ruby
require 'ruby_apk'
path='org.connectbot[ConnectBot]-323_1.7.1.apk'
Zip::ZipFile.open(path){|zip|
    xml=Android::AXMLParser.new(zip.read('AndroidManifest.xml')).parse
    label_id=xml.get_elements('/manifest/application')[0].attributes['label']
    query=label_id[3..-1].to_i(16)
    arsc=Android::Resource.new(zip.read('resources.arsc'))
    p arsc.keyStrings[((query&0xff0000)>>16)-1][query&0xffff]
    label_index=arsc.stringpool_relation[((query&0xff0000)>>16)-1][query&0xffff]
    p arsc.strings[label_index] #"ConnectBot" success!
}

But unfortunately this code is very dirty and doesn't work for some complex apks. further debug is required. But maybe I'm too tired...

masatanish commented 11 years ago

Thank you for your information. I'll try implementing it during this new year holidays.

masatanish commented 11 years ago

Hi cielavenir.

I've released v0.5.0. And this version includes Android::Resource#find method. You can get string by using this method with resource id like '@0x7f040000' or '@string/app_name'. This method supports only string resources for now.

I've tested only few cases. I think this version has some bugs. If you find bugs, let me know.

Thanks.

cielavenir commented 11 years ago

This code isn't working for some apks: http://pastebin.com/GYi86iva

#!/usr/bin/ruby
require 'find'
require 'ruby_apk'

LANG='ja'
DIR='/work/apk/data'
files=Dir.glob(DIR+'/**/*').grep(/\.apk$/)
packages=Hash.new([0,''])
files.each{|path|
    Zip::ZipFile.open(path){|zip|
        xml=Android::AXMLParser.new(zip.read('AndroidManifest.xml')).parse
        package=xml.get_elements('/manifest')[0].attributes['package']
        versionCode=xml.get_elements('/manifest')[0].attributes['versionCode'].to_i
        versionName=xml.get_elements('/manifest')[0].attributes['versionName']
        label=xml.get_elements('/manifest/application')[0].attributes['label']
        arsc=Android::Resource.new(zip.read('resources.arsc'))
        begin
            label=arsc.find(label,:lang=>LANG) if label[0,1]=='@'
            versionName=arsc.find(versionName,:lang=>LANG) if versionName[0,1]=='@'
            label=arsc.find(label) if !label
            versionName=arsc.find(versionName) if !versionName
        rescue
            puts package+' failed'
        end
        apkname="#{package}[#{label}]-#{versionCode}_#{versionName}.apk"
        apkname.tr!("/?*:\\\"'",'_')
        if versionCode>packages[package][0]
            #auto rename
            packages[package]=[versionCode,apkname]
            p packages[package]
        end
    }
}
cielavenir commented 11 years ago

I should have used this code, but this still fails on com.viber.voip

        begin
            label=arsc.find(label,:lang=>LANG)||arsc.find(label) if label[0,1]=='@'
            versionName=arsc.find(versionName,:lang=>LANG)||arsc.find(versionName) if versionName[0,1]=='@'
        rescue
            puts package+' failed'
        end
masatanish commented 11 years ago

Sorry for replying too late.

I've tried with com.viber.voip(v.2.2.0.1334 and v2.3.2.61). But I can't reproduce it. Which version did you use? or Can you send reproduced resource file?

Maybe the issue reported by you is a bug. I registered new issue #5, because I want to separate a bug from this item.

gilesian commented 11 years ago

hey masatanish, can you guide me in how to run it and test/use it... i am quite new to ruby

masatanish commented 11 years ago

Hi gllesian, you can run test with below.

$ bundle exec rake spec

The usage of ruby_apk is in readme.

BTW, if you don't need modify this library, you don't need clone the source. install ruby_apk gem with below, and put 'require "ruby_apk"' in top of your script.

$ gem install ruby_apk

then you can use ruby_apk in your script.