前言
上一篇文章寫了如何使用Python寫一個(gè)簡單的爬蟲缨恒,批量抓取APK的下載鏈接臣嚣。這篇文章記錄下如何批量拆包APK文件并提取想要的信息颅停。
準(zhǔn)備工作
- Androguard環(huán)境部署:Androguard下載
- 在上一篇文章提到的準(zhǔn)備工作的基礎(chǔ)上,這篇文章需要加入Androguard作為環(huán)境具温。Androguard是一款開源的Android應(yīng)用程序分析工具蚕涤,使用Python編寫。眾多功能模塊以Python包的形式存在铣猩。
- 使用方法:首先將GitHub上的Androguard項(xiàng)目下載下來揖铜。
下載Androguard.png
下載完成后得到一個(gè)壓縮包,解壓后進(jìn)入目錄达皿,把Androguard目錄下所有的文件拷貝至Python的根目錄下天吓,合并同名文件夾即可。
- 檢查環(huán)境是否部署成功:
打開cmd命令行峦椰,輸入python進(jìn)入交互狀態(tài)龄寞。輸入from androguard.core.bytecodes import apk, dvm
如下圖所示沒有提示任何錯(cuò)誤信息即可。
檢查環(huán)境.png
注意:在cmd下直接調(diào)python命令行需要將Python加入到環(huán)境變量中汤功。
基礎(chǔ)知識
首先APK文件可以用普通解壓縮的方式拆包物邑,如下圖。
解壓完畢.png
熟悉安卓開發(fā)的人肯定對解壓完畢后的文件很熟悉滔金。這里主要介紹部分文件的作用色解。
- 1.assets文件:這里存放一些資源文件入圖片等,一半情況下解壓縮完畢后是可以直接看到這些圖片的餐茵。
assets.png
- 2.lib文件:這里存放安卓開發(fā)中使用到的庫文件
庫文件.png
- 3.AndroidManifest文件:這是我們這次需要關(guān)注的重點(diǎn)科阎。一個(gè)安卓應(yīng)用需要申請的權(quán)限信息,以及Activity等組件的注冊信息都需要在AndroidMainfest.xml文件中聲明忿族。但是直接解壓后的AndroidManifest文件查看確是如下這種情況:
亂碼.png
沒錯(cuò)萧恕,是亂碼。這就說明僅僅使用將APK解壓縮的形式去獲得我們關(guān)心的信息是不可行的肠阱。
使用Python獲取APK信息
到此為止,相信大家對APK的結(jié)構(gòu)有了一定了解朴读。下面以獲取APP申請權(quán)限為例子使用Python完成APK的拆包提取信息屹徘。
- 首先附上Androguard開發(fā)文檔:Androguard開發(fā)API
- Androguard是開源的,有興趣的朋友可以去閱讀源碼衅金。這里不過多贅述Androguard實(shí)現(xiàn)原理噪伊,我們從目的入手簿煌,直接使用Androguard。
- 首先在新建的Python工程中引包
from androguard.core.bytecodes import apk, dvm
- 接著調(diào)用API
app.get_permissions()
- 沒錯(cuò)鉴吹,Androguard就是這么強(qiáng)大姨伟,一個(gè)API就完成了拆包提權(quán)操作。那么為了實(shí)現(xiàn)批量拆包提權(quán)(以及提取其他信息)豆励,我們必須自己編寫一個(gè)Python腳本夺荒。
- 思路很明了,利用Python寫一個(gè)遍歷文件夾中所有文件的腳本良蒸,對每個(gè)apk文件執(zhí)行相應(yīng)的操作即可技扼。為了讓腳本清晰,這里把調(diào)取API和遍歷文件夾放在兩個(gè)腳本中嫩痰。
首先附上調(diào)用Androguard中API獲取各種信息的代碼:
__author__ = 'Administrator'
#coding=utf-8
from androguard.core.bytecodes import apk, dvm
from androguard.core.analysis import analysis
import re
global count
count = 1
def get_permissions(path, filename):
str = "Permission:"
app = apk.APK(path)
permission = app.get_permissions()
file = permission
print permission
writeToTxt(str, file, filename)
return permission
def get_apis(path, filename):
app = apk.APK(path)
app_dex = dvm.DalvikVMFormat(app.get_dex())
app_x = analysis.newVMAnalysis(app_dex)
methods = set()
cs = [cc.get_name() for cc in app_dex.get_classes()]
for method in app_dex.get_methods():
g = app_x.get_method(method)
if method.get_code() == None:
continue
for i in g.get_basic_blocks().get():
for ins in i.get_instructions():
output = ins.get_output()
match = re.search(r'(L[^;]*;)->[^\(]*\([^\)]*\).*', output)
if match and match.group(1) not in cs:
methods.add(match.group())
methods = list(methods)
methods.sort()
print "methods:"+"\n"
print methods
str = "Methods:"
file = methods
writeToTxt(str, file, filename)
return methods
def get_providers(path, filename):
app = apk.APK(path)
providers = app.get_providers()
print "providers:"+"\n"
print providers
str = "Providers:"
file = providers
writeToTxt(str, file, filename)
return providers
def get_package(path, filename):
app = apk.APK(path)
packname = app.get_package()
print "packageName:"+"\n"
print packname
str = "PackageName:"
file = packname
writeToTxt(str, file, filename)
return packname
def get_activities(path, filename):
app = apk.APK(path)
activitys = app.get_activities()
print "ActivityName:"+"\n"
print activitys
str = "Activitys:"
file = activitys
writeToTxt(str, file, filename)
return activitys
def get_receivers(path, filename):
app = apk.APK(path)
receivers = app.get_receivers()
print "Receivers:"+"\n"
print receivers
str = "Receivers:"
file = receivers
writeToTxt(str, file, filename)
return receivers
def get_services(path, filename):
app = apk.APK(path)
services = app.get_services()
print "Services:"+"\n"
print services
str = "Services:"
file = services
writeToTxt(str, file, filename)
return services
def writeToTxt(str, file, filename):
global count
fm = open('%d'%count+'.txt', 'w')
#fm.write(str)
#fm.write("\n")
for i in file:
tmp = i.split('.')
final = tmp[-1]
fm.write(final)
fm.write("\t")
fm.close()
count += 1
def main(path, apkname):
get_permissions(path, apkname)
#get_apis(path, apkname)
#get_providers(path, apkname)
#get_package(path, apkname)
#get_activities(path, apkname)
#get_receivers(path, apkname)
#get_services(path, apkname)
if __name__ == '__main__':
path = "D:/sample/Good"
filename = "sampleInfo.txt"
main(path, filename)
- 上面代碼中剿吻,witeToTex函數(shù)將調(diào)用API所得到的結(jié)果保存在txt文件中,以便查看和日后使用串纺。get_XXXX函數(shù)就是獲取apk文件中對應(yīng)的信息丽旅,其中g(shù)et_permissions獲取apk中申請的權(quán)限,get_apis獲取一些api調(diào)用信息纺棺,顧名思義榄笙,get_XXX就獲取XXX。
- 再看如何遍歷五辽,這里使用Python 的os.walk遍歷目錄:
os.walk(top, topdown=True, onerror=None, followlinks=False) 可以得到一個(gè)三元tupple(dirpath, dirnames, filenames), 第一個(gè)為起始路徑办斑,第二個(gè)為起始路徑下的文件夾,第三個(gè)是起始路徑下的文件杆逗。dirpath代表目錄的路徑乡翅,dirnames包含了dirpath下所有子目錄的名字。filenames 包含了非目錄文件的名字罪郊。 - 遍歷存放眾多APK的文件夾蠕蚜,對每個(gè)APK文件進(jìn)行相應(yīng)操作,代碼如下:
__author__ = 'Administrator'
#-*- coding:GBK -*-
import os
import os.path
import sys
import subprocess
import getFeatures
rootdir = "D:/Sample/Good//"
destdir = "D:/Sample/workSample/badDone//"
command = "java -jar D://apktool.jar"
class Packages:
def __init__(self, srcdir, desdir):
self.sdir = srcdir
self.ddir = desdir
def check(self):
print("--------------------starting unpackage!---------------------")
for dirpath, dirnames, filenames in os.walk(rootdir):
for filename in filenames:
thefile = os.path.join(dirpath, filename)
apkfile = os.path.split(thefile)[1]
apkname = os.path.splitext(apkfile)[0]
print apkfile
try:
if os.path.splitext(thefile)[1] == ".apk":
# name = os.path.splitext(thefile)[0]
str1= '"'+thefile+'"'
str2= '"'+destdir + os.path.splitext(filename)[0]+'"'
# cmdExtract = r'%s d -f %s %s'% (command, str2, str1)
getFeatures.main(thefile, apkname)
print "******************well done******************"
except IOError, err:
print err
sys.exit()
if __name__ == "__main__":
dir=Packages(rootdir, 'e:/')
dir.check()
- 上面這個(gè)腳本引入文章中第一個(gè)腳本作為模塊悔橄,共同實(shí)現(xiàn)批量拆包提取特征并輸出到txt文件中的功能靶累。這里用正則表達(dá)式對提取出的權(quán)限特征進(jìn)行處理,去掉冗余部分癣疟,僅保留關(guān)鍵字段挣柬,并把每個(gè)apk文件對應(yīng)的權(quán)限特征輸出到各自的txt文本中。
程序運(yùn)行.png
- 這里APK用以編號睛挚,以方便后面查找對比邪蛔。輸出的最終結(jié)果如下圖:
處理前.png
輸出的txt.png
- 將權(quán)限信息取冗余后,寫入到txt文件中扎狱,以tab鍵進(jìn)行分離侧到,方便后面使用勃教。
txt中權(quán)限.png
總結(jié)
到此為止,我們就把安卓中的權(quán)限信息提取出來了匠抗。這為后面使用機(jī)器學(xué)習(xí)方式對安卓應(yīng)用進(jìn)行檢測提供了基本的數(shù)據(jù)故源。在接下來的文章中將會進(jìn)一步介紹如何使用Python實(shí)現(xiàn)機(jī)器學(xué)習(xí)的方式檢測安卓惡意應(yīng)用。