Skip to content

Instantly share code, notes, and snippets.

@alterakey
Last active August 29, 2015 14:25
Show Gist options
  • Save alterakey/6ae60b0b309eb1250cd6 to your computer and use it in GitHub Desktop.
Save alterakey/6ae60b0b309eb1250cd6 to your computer and use it in GitHub Desktop.
Crude API usage analyzer for Android
# analyze-api: Crude API usage analyzer for Android
# Copyright (C) 2015 Takahiro Yoshimura <[email protected]>. All rights reserved.
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
from __future__ import print_function
import re
import itertools
def decoded_class_name_and_remainder(c):
return re.sub(r'L(.+?);', lambda m: m.group(1).replace('/', '.'), c[:c.index(';')+1]), c[c.index(';')+1:]
def decoded_array_and_remainder(a):
p, q = decoded_type_and_remainder(a[1:])
return p + '[]', q
def decoded_type_and_remainder(p):
try:
return dict(I='int', F='float', J='long', B='byte', C='char', D='double', S='short', Z='boolean', V='void')[p[0]], p[1:]
except KeyError:
if p[0] == 'L':
return decoded_class_name_and_remainder(p)
elif p[0] == '[':
return decoded_array_and_remainder(p)
else:
raise
def decoded_types(p):
o = []
while p:
d, p = decoded_type_and_remainder(p)
if d:
o.append(d)
return o
def decoded_type(p):
return decoded_types(p)[0]
def decoded_invocation(p):
c, m = p.split('->')
if m != '<init>':
return '#'.join([decoded_type(c), m])
else:
return decoded_type(c)
def decoded_prototype_of(p):
return re.sub(r'^(.*?)\((.*?)\)(.+?)$',
lambda m: '%s %s(%s)' % (decoded_type(m.group(3)), decoded_invocation(m.group(1)), ', '.join(decoded_types(m.group(2)))),
p)
def invocation_target_of(l):
try:
return re.search(r'^.+?\}, ([JIL\[].+)$', l.strip()).group(1)
except AttributeError:
return re.search(r'invokes: (.+)$', l.strip()).group(1)
def apis_in_line(l):
if 'invoke' in l:
try:
yield decoded_prototype_of(invocation_target_of(l))
except AttributeError:
pass
def apis_used_in_smali_files_listed_in(smali_list):
api = set()
for fn in (rfn.strip() for rfn in smali_list):
with open(fn, 'r') as f:
api = api | set(itertools.chain(*(apis_in_line(l) for l in f)))
return api
if __name__ == '__main__':
import sys
map(print, sorted(apis_used_in_smali_files_listed_in(sys.stdin)))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment