教你如何高效的檢查APK中使用敏感權(quán)限的地方以及檢查某系統(tǒng)方法被調(diào)用的地方

前言

相信最近在App上架應(yīng)用商店的同學(xué)都感受到了,國內(nèi)對用戶的隱私越來越重視,如MAC地址,設(shè)備ID,IMEI信息等,要么就干脆不用,要么就必須很明顯的告訴用戶想要獲取這些信息,相關(guān)法律及規(guī)定,參考《網(wǎng)絡(luò)安全法》及《關(guān)于開展APP侵害用戶權(quán)益專項整治工作的通知》

開門見山

廢話不多說,找了幾個反編譯工具,并簡單看了下使用方法,最終鎖定androguard,官方解釋:對Android應(yīng)用程序的逆向工程、惡意軟件和惡意軟件分析,它提供了一系列的Apk以及dex、odex、arsc等文件的分析處理功能,可以輕松的幫助我們找到調(diào)用系統(tǒng)權(quán)限的地方。且python腳本執(zhí)行,簡直不能再好用了

環(huán)境

  • python
https://www.python.org
  • pycharm
https://www.jetbrains.com/pycharm/download/
  • androguard
https://androguard.readthedocs.io/en/latest/

安裝

pip install -U androguard

如果想在命令行直接操作,請在安裝完后執(zhí)行如下:

androguard analyze

執(zhí)行后如圖:


CleanShot 2021-07-15 at 17.20.24@2x.png

然后再加載apk,在上面執(zhí)行后,輸入如下:

a, d, dx = AnalyzeAPK("examples/android/abcore/app-prod-debug.apk")

apk加載完成后就可以調(diào)用相關(guān)api來獲取信息

獲取權(quán)限

In [2]: a.get_permissions()
Out[2]:
['android.permission.INTERNET',
 'android.permission.WRITE_EXTERNAL_STORAGE',
 'android.permission.ACCESS_WIFI_STATE',
 'android.permission.ACCESS_NETWORK_STATE']

獲取Activity

In [3]: a.get_activities()
Out[3]:
['com.greenaddress.abcore.MainActivity',
 'com.greenaddress.abcore.BitcoinConfEditActivity',
 'com.greenaddress.abcore.AboutActivity',
 'com.greenaddress.abcore.SettingsActivity',
 'com.greenaddress.abcore.DownloadSettingsActivity',
 'com.greenaddress.abcore.PeerActivity',
 'com.greenaddress.abcore.ProgressActivity',
 'com.greenaddress.abcore.LogActivity',
 'com.greenaddress.abcore.ConsoleActivity',
 'com.greenaddress.abcore.DownloadActivity']

其他

# 包名
In [4]: a.get_package()
Out[4]: 'com.greenaddress.abcore'
# app名字
In [5]: a.get_app_name()
Out[5]: u'ABCore'
# logo
In [6]: a.get_app_icon()
Out[6]: u'res/mipmap-xxxhdpi-v4/ic_launcher.png'
# 版本號
In [7]: a.get_androidversion_code()
Out[7]: '2162'
# 版本名
In [8]: a.get_androidversion_name()
Out[8]: '0.62'
# 最低sdk支持
In [9]: a.get_min_sdk_version()
Out[9]: '21'
# 最高
In [10]: a.get_max_sdk_version()
# 目標(biāo)版本
In [11]: a.get_target_sdk_version()
Out[11]: '27'
# 獲取有效目標(biāo)版本
In [12]: a.get_effective_target_sdk_version()
Out[12]: 27
# manifest文件
In [13]: a.get_android_manifest_xml()
Out[13]: <Element manifest at 0x7f9d01587b00>

等等吧,Api實(shí)在是太多了,還是關(guān)注官方文檔吧,只有你想不到,沒有它沒有的,如下鏈接:

https://androguard.readthedocs.io/en/latest/intro/gettingstarted.html#using-the-analysis-object

更多demo

https://github.com/androguard/androguard/tree/master/examples

下面直接開始實(shí)踐。

檢索使用敏感權(quán)限的地方并輸出文件

下面就是檢查APK中使用敏感權(quán)限的實(shí)現(xiàn),請看:

import os
import sys

# 引入androguard的路徑,根據(jù)個人存放的位置而定
androguard_module_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'androguard')
if not androguard_module_path in sys.path:
    sys.path.append(androguard_module_path)

from androguard.misc import AnalyzeAPK
from androguard.core.androconf import load_api_specific_resource_module

path = r"/apk"
out_path = r"/out"
files = []
path_list = os.listdir(path)
path_list.sort()
for name in path_list:
    if os.path.isfile(os.path.join(path, name)):
        files.append(name)


def main():
    for apkFile in files:
        file_name = os.path.splitext(apkFile)[0]
        print(apkFile)
        out = AnalyzeAPK(path + '/' + apkFile)
        # apk object 抽象apk對象,可以獲取apk的一些信息,如版本號、包名、Activity等
        a = out[0]
        # DalvikVMFormat 數(shù)組,一個元素其實(shí)對應(yīng)的是class.dex,可以從DEX文件中獲取類、方法或字符串。
        d = out[1]
        # Analysis 分析對象,因?yàn)樗厥獾念悾@些類鏈接有關(guān)classes.dex的信息,甚至可以一次處理許多dex文件,所以下面我們從這里面來分析整個apk
        dx = out[2]

        # api和權(quán)限映射
        # 輸出文件路徑
        api_perm_filename = os.path.join(out_path, file_name + "_api-perm.txt")
        api_perm_file = open(api_perm_filename, 'w', encoding='utf-8')
        # 權(quán)限映射map
        permissionMap = load_api_specific_resource_module('api_permission_mappings')
        # 遍歷apk所有方法
        for meth_analysis in dx.get_methods():
            meth = meth_analysis.get_method()
            # 獲取類名、方法名
            name = meth.get_class_name() + "-" + meth.get_name() + "-" + str(
                meth.get_descriptor())
             
            for k, v in permissionMap.items():
                # 匹配系統(tǒng)權(quán)限方法,匹配上就輸出到文件中
                if name == k:
                    result = str(meth) + ' : ' + str(v)
                    api_perm_file.write(result + '\n')
        api_perm_file.close()


if __name__ == '__main__':
    main()

輸出結(jié)果

Landroid/app/Activity;->navigateUpTo(Landroid/content/Intent;)Z : ['android.permission.BROADCAST_STICKY']
Landroid/app/Activity;->onMenuItemSelected(I Landroid/view/MenuItem;)Z : ['android.permission.BROADCAST_STICKY']
Landroid/app/Activity;->setRequestedOrientation(I)V : ['android.permission.BROADCAST_STICKY']
Landroid/app/Activity;->unregisterReceiver(Landroid/content/BroadcastReceiver;)V : ['android.permission.BROADCAST_STICKY']
Landroid/os/PowerManager$WakeLock;->acquire(J)V : ['android.permission.WAKE_LOCK']
Landroid/os/PowerManager$WakeLock;->release()V : ['android.permission.WAKE_LOCK']
Landroid/location/LocationManager;->isProviderEnabled(Ljava/lang/String;)Z : ['android.permission.ACCESS_COARSE_LOCATION', 'android.permission.ACCESS_FINE_LOCATION']
Landroid/location/LocationManager;->getLastKnownLocation(Ljava/lang/String;)Landroid/location/Location; : ['android.permission.ACCESS_COARSE_LOCATION', 'android.permission.ACCESS_FINE_LOCATION']
Landroid/app/ActivityManager;->getRunningTasks(I)Ljava/util/List; : ['android.permission.GET_TASKS']
Landroid/accounts/AccountManager;->invalidateAuthToken(Ljava/lang/String; Ljava/lang/String;)V : ['android.permission.MANAGE_ACCOUNTS', 'android.permission.USE_CREDENTIALS']
Landroid/net/ConnectivityManager;->getNetworkInfo(I)Landroid/net/NetworkInfo; : ['android.permission.ACCESS_NETWORK_STATE']
Landroid/net/ConnectivityManager;->isActiveNetworkMetered()Z : ['android.permission.ACCESS_NETWORK_STATE']
Landroid/net/ConnectivityManager;->getActiveNetworkInfo()Landroid/net/NetworkInfo; : ['android.permission.ACCESS_NETWORK_STATE']
Landroid/telephony/TelephonyManager;->getDeviceId()Ljava/lang/String; : ['android.permission.READ_PHONE_STATE']
Landroid/telephony/TelephonyManager;->getSubscriberId()Ljava/lang/String; : ['android.permission.READ_PHONE_STATE']
Landroid/telephony/TelephonyManager;->getSimSerialNumber()Ljava/lang/String; : ['android.permission.READ_PHONE_STATE']

輸出的系統(tǒng)類、調(diào)用方法、需要的權(quán)限。

檢索某系統(tǒng)方法被調(diào)用的地方并打印

import os
import sys

# 引入androguard的路徑,根據(jù)個人存放的位置而定
androguard_module_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'androguard')
if not androguard_module_path in sys.path:
    sys.path.append(androguard_module_path)

from androguard.misc import AnalyzeAPK
from androguard.core.androconf import load_api_specific_resource_module

path = r"/apk"
out_path = r"/out"
files = []
path_list = os.listdir(path)
path_list.sort()
for name in path_list:
    if os.path.isfile(os.path.join(path, name)):
        files.append(name)


def main():
    for apkFile in files:
        file_name = os.path.splitext(apkFile)[0]
        print(apkFile)
        out = AnalyzeAPK(path + '/' + apkFile)
        a = out[0]
        d = out[1]
        dx = out[2]

        for meth in dx.classes['Ljava/io/File;'].get_methods():
            print("usage of method {}".format(meth.name))
            # 拿到改函數(shù)的引用函數(shù)
            for _, call, _ in meth.get_xref_from():
            print("  called by -> {} -- {}".format(call.class_name, call.name))

if __name__ == '__main__':
    main()

輸出結(jié)果

usage of method getPath
  called by -> Landroid/support/v4/util/AtomicFile; -- <init>
usage of method <init>
  called by -> Landroid/support/v4/util/AtomicFile; -- <init>
usage of method delete
  called by -> Landroid/support/v4/util/AtomicFile; -- failWrite
  called by -> Landroid/support/v4/util/AtomicFile; -- delete
  called by -> Landroid/support/v4/util/AtomicFile; -- delete
  called by -> Landroid/support/v4/util/AtomicFile; -- startWrite
  called by -> Landroid/support/v4/util/AtomicFile; -- openRead
  called by -> Landroid/support/v4/util/AtomicFile; -- finishWrite
usage of method renameTo
  called by -> Landroid/support/v4/util/AtomicFile; -- openRead
  called by -> Landroid/support/v4/util/AtomicFile; -- failWrite
  called by -> Landroid/support/v4/util/AtomicFile; -- startWrite
usage of method exists
  called by -> Landroid/support/v4/util/AtomicFile; -- startWrite
  called by -> Landroid/support/v4/util/AtomicFile; -- openRead
  called by -> Landroid/support/v4/util/AtomicFile; -- startWrite
usage of method getParentFile
  called by -> Landroid/support/v4/util/AtomicFile; -- startWrite
usage of method mkdir
  called by -> Landroid/support/v4/util/AtomicFile; -- startWrite
  • ‘Ljava/io/File;’ 需要檢測的類
  • meth.get_xref_from() 那該類中函數(shù)被引用的地方
  • 你也可以自己搞個數(shù)組,配置好要檢查的相關(guān)函數(shù),然后在上面代碼中加入if過濾即可
    如果你想找Android系統(tǒng)定位,被應(yīng)用哪些方法調(diào)用,你就可以這樣做:
dx.classes['Landroid/location/LocationManager;']

再運(yùn)行一遍腳本就可以看到結(jié)果了。

結(jié)束

寫這篇博客,主要目的是為了讓更多人知道這個東西吧,我自己去搜索文章的時候發(fā)現(xiàn)并沒有多少可以參考的,導(dǎo)致很多人無從下手,但其實(shí)官方文檔也很詳細(xì),但是英文的,看起來也不方便,也希望這篇簡短的文章給你提供幫助,如果有問題請再聯(lián)系我或留言評論

歡迎關(guān)注新網(wǎng)站

http://jetpack.net.cn

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容