移動安全--44--MobSF-v3.0beta源代碼分析【長文鉅獻】

一、項目說明

分析日期:2020-01-17
源碼地址:https://github.com/MobSF/Mobile-Security-Framework-MobSF

關於如何搭建源碼分析環境,請閱讀我的另一篇博客:MobSF移動安全框架實踐–基於3.0beta版

移動安全框架(MobSF)是一種自動化的移動應用程序(Android/iOS/Windows)測試框架,能夠執行靜態、動態和惡意軟件分析。 它可用於Android、iOS和Windows移動應用程序的有效和快速安全分析,並支持二進制文件(APK,IPA和APPX)和壓縮源代碼。 MobSF可以在運行時爲Android應用程序進行動態應用程序測試,並具有由CapFuzz(一種特定於Web API的安全掃描程序)提供支持的Web API模糊測試。MobSF旨在使您的CI/CD或DevSecOps管道集成無縫。

二、項目入口

項目結構如下:

在這裏插入圖片描述

我們首先瀏覽項目源碼,找到項目入口,然後從入口開始分析,這在分析任何源碼都是一樣的。

由於MobSF使用Django框架開發的,我們瀏覽源碼後可以看到,入口在/MobSF/urls.py中,通過瀏覽器訪問相應的URL地址,功能映射到對應的代碼邏輯。

代碼如下所示:

爲了便於閱讀,我將備註重寫爲中文備註

urlpatterns = [
    # 一般功能URL
    url(r'^$', home.index, name='home'),
    url(r'^upload/$', home.Upload.as_view),
    url(r'^download/', home.download),
    url(r'^about$', home.about, name='about'),
    url(r'^api_docs$', home.api_docs, name='api_docs'),
    url(r'^recent_scans/$', home.recent_scans, name='recent'),
    url(r'^delete_scan/$', home.delete_scan),
    url(r'^search$', home.search),
    url(r'^error/$', home.error, name='error'),
    url(r'^not_found/$', home.not_found),
    url(r'^zip_format/$', home.zip_format),
    url(r'^mac_only/$', home.mac_only),

    # 靜態分析URL
    # Android應用靜態分析URL
    url(r'^StaticAnalyzer/$', android_sa.static_analyzer),
    url(r'^ViewSource/$', view_source.run),
    url(r'^Smali/$', smali.run),
    url(r'^Java/$', java.run),
    url(r'^Find/$', find.run),
    url(r'^generate_downloads/$', generate_downloads.run),
    url(r'^ManifestView/$', manifest_view.run),
    # IOS應用靜態分析URL
    url(r'^StaticAnalyzer_iOS/$', ios_sa.static_analyzer_ios),
    url(r'^ViewFile/$', io_view_source.run),
    # Windows應用靜態分析URL
    url(r'^StaticAnalyzer_Windows/$', windows.staticanalyzer_windows),
    # PDF報告
    url(r'^PDF/$', shared_func.pdf),
    # 應用比較
    url(r'^compare/(?P<hash1>[0-9a-f]{32})/(?P<hash2>[0-9a-f]{32})/$',
        shared_func.compare_apps),

    # 動態分析URL
    url(r'^dynamic_analysis/$',
        dz.dynamic_analysis,
        name='dynamic'),
    url(r'^android_dynamic/$',
        dz.dynamic_analyzer,
        name='dynamic_analyzer'),
    url(r'^httptools$',
        dz.httptools_start,
        name='httptools'),
    url(r'^logcat/$', dz.logcat),
    # Android設備操作
    url(r'^mobsfy/$', operations.mobsfy),
    url(r'^screenshot/$', operations.take_screenshot),
    url(r'^execute_adb/$', operations.execute_adb),
    url(r'^screen_cast/$', operations.screen_cast),
    url(r'^touch_events/$', operations.touch),
    url(r'^get_component/$', operations.get_component),
    url(r'^mobsf_ca/$', operations.mobsf_ca),
    # 動態測試
    url(r'^activity_tester/$', tests_common.activity_tester),
    url(r'^download_data/$', tests_common.download_data),
    url(r'^collect_logs/$', tests_common.collect_logs),
    # Frida框架
    url(r'^frida_instrument/$', tests_frida.instrument),
    url(r'^live_api/$', tests_frida.live_api),
    url(r'^frida_logs/$', tests_frida.frida_logs),
    url(r'^list_frida_scripts/$', tests_frida.list_frida_scripts),
    url(r'^get_script/$', tests_frida.get_script),
    # 動態掃描報告
    url(r'^dynamic_report/$', report.view_report),
    url(r'^dynamic_view_file/$', report.view_file),

    # REST API
    url(r'^api/v1/upload$', rest_api.api_upload),
    url(r'^api/v1/scan$', rest_api.api_scan),
    url(r'^api/v1/delete_scan$', rest_api.api_delete_scan),
    url(r'^api/v1/download_pdf$', rest_api.api_pdf_report),
    url(r'^api/v1/report_json$', rest_api.api_json_report),
    url(r'^api/v1/view_source$', rest_api.api_view_source),
    url(r'^api/v1/scans$', rest_api.api_recent_scans),

    # Test
    url(r'^tests/$', tests.start_test),

]

可以很明顯的看到,MobSF的功能分爲四大塊,分別是:

一般功能:包括上傳APP、下載報告、關於說明、搜索、刪除掃描等常規功能;
靜態掃描:Android應用、iOS應用、windows應用的靜態掃描,APP比較等;
動態分析:只支持Android應用的動態分析,包括Android設備操作、Frida框架等、報告生成等;
REST API:封裝好的可以調用的API接口,使得MobSF的功能可以接入到其他任何系統中。

三、一般功能分析

由於一般功能並非核心功能(核心功能是靜態掃描和動態分析),那我們只分析上傳功能即可,用來熱熱身。

對應代碼如下:

# General
url(r'^$', home.index, name='home'),
url(r'^upload/$', home.Upload.as_view),   # 這個是我們要分析的
url(r'^download/', home.download),
url(r'^about$', home.about, name='about'),
url(r'^api_docs$', home.api_docs, name='api_docs'),
url(r'^recent_scans/$', home.recent_scans, name='recent'),
url(r'^delete_scan/$', home.delete_scan),
url(r'^search$', home.search),
url(r'^error/$', home.error, name='error'),
url(r'^not_found/$', home.not_found),
url(r'^zip_format/$', home.zip_format),
url(r'^mac_only/$', home.mac_only),

編譯器中雙擊as_view選中,按下Command+B跟蹤(蘋果系統快捷鍵),我們來到/MobSF/views/home.py中。

隨後跟進到upload_html函數,首先看到的是,上傳只支持POST方法,其他方法不支持。

代碼如下:

if request.method != 'POST':
    logger.error('Method not Supported!')
    response_data['description'] = 'Method not Supported!'
    response_data['status'] = HTTP_BAD_REQUEST
    return self.resp_json(response_data)

之後,是對上傳的無效文件和不支持文件的錯誤處理,會給出錯誤提示。

對於使用windows平臺下上傳的ipa包,也會給出錯誤提示,要求操作系統必須是MAC或者Linux。

接下來是upload_api函數,這個是REST API的上傳功能,我們這裏不做分析。

隨後對上傳的文件做分類,對不同類型的應用包做對應的掃描。代碼如下:

def upload(self):
    request = self.request
    scanning = Scanning(request)     # 就是這裏,對上傳的文件做掃描,稍後跟進
    file_type = self.file_content_type
    file_name_lower = self.file_name_lower
    
    # 判斷上傳的應用包的類型,對不同類型的包做對應的掃描
    logger.info('MIME Type: %s FILE: %s', file_type, file_name_lower)
    if self.file_type.is_apk():
        return scanning.scan_apk()    # 掃描APK包
    elif self.file_type.is_zip():
        return scanning.scan_zip()    # 掃描ZIP包
    elif self.file_type.is_ipa():
        return scanning.scan_ipa()    # 掃描IPA包
    elif self.file_type.is_appx():
        return scanning.scan_appx()   # 掃描APPX包

至此/MobSF/views/home.py文件中的上傳代碼就分析完了,其他代碼是其他功能,包括api_docs、關於說明、錯誤、最近掃描等一大堆功能,從其他URL可以進入分析,我們此處不做分析。

我們來看看上傳之後是如何進行掃描的,跟進Scanning(request),來到/MobSF/views/scanning.py的Scanning類中。可以看到這裏的掃描分了4類,分別是對APK包的掃描、對ZIP包的掃描、對IPA包的掃描、對APPX包的掃描。

我們來看看對APK包的掃描,先是通過文件名和文件類型(.apk)算出一個MD5值,這也是我們使用的時候看到的那個MD5值,以及首頁查詢框中要輸入的MD5值。

代碼如下:

md5 = handle_uploaded_file(self.file, '.apk')

隨後,將掃描任務添加到最近的掃描列表,這也是我們在使用時可以從最近的掃描列表找到之前掃描過的任務,不用再重新做掃描。

我們看掃描APK的代碼:

def scan_apk(self):
    """Android APK."""
    md5 = handle_uploaded_file(self.file, '.apk')
    url = 'StaticAnalyzer/?name={}&type=apk&checksum={}'.format(    # 注意,此處使到了URL
        self.file_name, md5)
    data = {
        'url': url,
        'status': 'success',
        'hash': md5,
        'scan_type': 'apk',
        'file_name': self.file_name,
    }

    add_to_recent_scan(self.file_name, md5, data['url'])

    logger.info('Performing Static Analysis of Android APK')
    return data

可以看到,URL參數中使用了StaticAnalyzer的URL,此時回到最開始的/MobSF/urls.py中,找到StaticAnalyzer跟入,至此,上傳的文件正式進入到靜態掃描。

URL:http://127.0.0.1:8000/StaticAnalyzer/?name=homesecurity.apk&type=apk&checksum=ef13eb870fa8538cd1bb450f7179dec5

請看截圖中的URL哦!

在這裏插入圖片描述

四、靜態掃描分析

先捋一下代碼

在做完靜態掃描的源碼分析後,我覺得這一小節的內容應當加在最前面,於是我就將它寫在了最前面。

靜態分析的核心部分在/StaticAnalyzer/views/目錄下,另外我們這次只分析Android的APK,因此所分析的代碼集中在/StaticAnalyzer/views/android/目錄下。

android/android_apis.py:常見的API規則庫文件
android/android_manifest_desc.py:AndroidManifest規則庫文件
android/android_rules.py:要檢測的API列表文件
android/binary_analysis.py:二進制分析文件
android/cert_analysis.py:證書分析文件
android/code_analysis.py:代碼分析文件
android/converter.py:反編譯Java/smali代碼文件
android/db_interaction.py:數據庫交互文件
android/dvm_permissions.py:權限規則庫文件
android/find.py:查找源代碼文件
android/generate_downloads.py:生成下載文件
android/icon_analysis.py:圖標分析文件
android/java.py:Java代碼展示文件
android/manifest_analysis.py:AndroidManifest分析文件
android/manifest_view.py:AndroidManifest視圖文件
android/playstore.py:應用商店分析文件
android/smali.py:Smali代碼展示文件
android/static_analyzer.py:靜態分析流程文件(主文件)
android/strings.py:常量字符串獲取文件
android/view_source.py:文件源查看
android/win_fixes.py:windows環境下會使用
comparer.py:靜態分析結果比較文件
shared_func.py:靜態分析文件

我們接下來的分析中,我們會按照流程一步一步走完靜態分析,出了非必要的,如規則庫代碼、windows環境使用代碼外,其他代碼都會涉及到。

捋完代碼我們再繼續

通過剛纔的分析我們得知,我們先通過uploadURL上傳了我們的APK文件,經過系統的一梭羅處理後,系統自動使用StaticAnalyzerURL開始對我們的APK文件進行靜態分析。

我們回到最開始的定義URL的地方,/MobSF/urls.py文件中,找到靜態分析的地方。

代碼如下:

# Android
url(r'^StaticAnalyzer/$', android_sa.static_analyzer),
url(r'^ViewSource/$', view_source.run),
url(r'^Smali/$', smali.run),
url(r'^Java/$', java.run),
url(r'^Find/$', find.run),
url(r'^generate_downloads/$', generate_downloads.run),
url(r'^ManifestView/$', manifest_view.run),

跟入static_analyzer函數,即來到靜態分析主流程文件:/StaticAnalyzer/views/android/static_analyzer.py文件中。

分析這裏的代碼非常傷,因爲代碼很長,又是縮進語法的Python,因此你要盯好縮進,不然就蒙圈了。

靜態分析一上來,提取參數,包括包類型、hash值、文件名、rescan等。之後判斷上傳的文件是APK包還是ZIP包還是其他包,對不同的包做對應的靜態分析。此處我們分析對APK包的靜態分析。

如果這個APP是之前掃描過的,則直接從數據庫拉取數據,如果是第一次掃描,則從零開始做掃描。

代碼如下:

if db_entry.exists() and rescan == '0':
    context = get_context_from_db_entry(db_entry)
else:
    ......

這樣的話,if下的代碼我們就不跟進去看了,因爲沒啥可看的。只看else下的代碼。

開始靜態分析後,首先提取APK文件名和APK路徑,之後解壓APK包,如果APK包解壓失敗則報錯。

代碼如下:

app_dic['files'] = unzip(
    app_dic['app_path'], app_dic['app_dir'])
if not app_dic['files']:
    # Can't Analyze APK, bail out.
    msg = 'APK file is invalid or corrupt'
    if api:
        return print_n_send_error_response(
            request,
            msg,
            True)
    else:
        return print_n_send_error_response(
            request,
            msg,
            False)
app_dic['certz'] = get_hardcoded_cert_keystore(app_dic['files'])

在成功解壓APK包之後,正式進入靜態分析階段。

4.1、AndroidManifest.xml安全分析

首先分析AndroidManifest.xml文件,代碼如下:

app_dic['parsed_xml'] = get_manifest(
    app_dic['app_path'],
    app_dic['app_dir'],
    app_dic['tools_dir'],
    '',
    True,
)

我們跟進去,來到了/StaticAnalyzer/views/android/manifest_analysis.py文件中。

這個文件近900行代碼,看得我快睡着了。其實並沒啥高深的東西,首先解壓APK.

代碼如下:

manifest = None
if (len(settings.APKTOOL_BINARY) > 0 and is_file_exists(settings.APKTOOL_BINARY)):
    apktool_path = settings.APKTOOL_BINARY
else:
    apktool_path = os.path.join(tools_dir, 'apktool_2.4.1.jar')
output_dir = os.path.join(app_dir, 'apktool_out')
args = [settings.JAVA_BINARY,
        '-jar',
        apktool_path,
        '--match-original',
        '--frame-path',
        tempfile.gettempdir(),
        '-f', '-s', 'd',
        app_path,
        '-o',
        output_dir]
manifest = os.path.join(output_dir, 'AndroidManifest.xml')
if is_file_exists(manifest):
    # APKTool already created readable XML
    return manifest
logger.info('Converting AXML to XML')
subprocess.check_output(args)
return manifest

不用多說,一目瞭然,使用apktool2.4.1對APK進行解壓,其使用的參數也是很明顯的。

之後讀取AndroidManifest.xml文件,這裏分爲從解壓的後目錄中讀取,和從源碼目錄中讀取(如果上傳的是ZIP包的話)。

讀取到AndroidManifest.xml文件後,開始解析該xml文件,提取該xml文件中的數據,包括application、uses-permission、manifest、activity、service、provider……等所有參數。

這裏還穿插着對可瀏覽的Activity做了一個單獨的讀取分析,因爲可瀏覽的Activity參數是比較特殊的。

代碼如下:

if cat.getAttribute('android:name') == 'android.intent.category.BROWSABLE':
    datas = node.getElementsByTagName('data')
    for data in datas:
        ......

之後,根據參數的特性,對權限進行了分析判斷,將權限的安全分級爲:normaldangeroussignaturesignatureOrSystem

對其他配置也做了安全分析,如:android:allowBackupandroid:debuggable……等參數.

對四大組件的配置也做了安全分析,將配置的安全分級爲:normaldangeroussignaturesignatureOrSystem

整個分析是基於android:exported = "true"android:exported != "false"的,注意這裏是不等於flase,也就是說要麼明確寫明導出爲true,要麼沒有聲明。因爲這兩種方式對應的分析方法不同,所以這裏是分開處理的。如果android:exported = "false"的話,那自然是安全的,就沒啥可說的了。

在分析的過程中,還分了小於Android 4.2和大於等於Android 4.2版本的情況。


綜述:整個/StaticAnalyzer/views/android/manifest_analysis.py代碼是對AndroidManifest.xml做了一個全面的安全分析


4.2、繼續前進

上小節我們從get_manifest跟入後,看到了系統對AndroidManifest.xml做了一個全面的安全分析,現在我們回來繼續向後前進。

代碼如下:

# 上小節我們是從這裏跟入的
app_dic['parsed_xml'] = get_manifest(
    app_dic['app_path'],
    app_dic['app_dir'],
    app_dic['tools_dir'],
    '',
    True,
)

# 現在我們退回來,繼續向後,跟入這裏
app_dic['real_name'] = get_app_name(
    app_dic['app_path'],
    app_dic['app_dir'],
    app_dic['tools_dir'],
    True,
)

跟入get_app_name,我們發現這是一個獲取APP名字的。

其分爲2種,要麼讀取AndroidManifest.xml文件的<application>標籤下的android:label屬性值。要麼讀取res/values/strings.xml文件中的appname屬性值。代碼如下:

# 讀取AndroidManifest.xml文件的`<application>`標籤下的`android:label`屬性值
if is_apk:
    a = apk.APK(app_path)
    real_name = a.get_app_name()
    return real_name

# 讀取res/values/strings.xml文件中的`appname`屬性值
else:
    strings_path = os.path.join(app_dir, 'app/src/main/res/values/strings.xml')
    eclipse_path = os.path.join(app_dir, 'res/values/strings.xml')
    if os.path.exists(strings_path):
        strings_file = strings_path
    elif os.path.exists(eclipse_path):
        strings_file = eclipse_path
if not os.path.exists(strings_file):
    logger.warning('Cannot find app name')
    return ''

with open(strings_file, 'r', encoding='utf-8') as f:
    data = f.read()

app_name_match = re.search(r'<string name=\"app_name\">(.*)</string>', data)

# 爲空則返回空
if len(app_name_match.groups()) <= 0:
    return ''
return app_name_match.group(app_name_match.lastindex)

之後幹了啥?沒了,我們只能返回繼續向後。

接下來,開始獲取APP的圖標(icon)。跟入到/StaticAnalyzer/views/android/icon_analysis.py中,這裏面其實沒啥可看的。

4.3、設置manifest連接

我們回到起點繼續向下走,接下來是設置AndroidManifest.xml的連接。這裏的量就比較大了,我在代碼中寫了備註,請閱讀。

代碼如下:

# 設置manifest連接
app_dic['mani'] = ('../ManifestView/?md5='
                   + app_dic['md5']
                   + '&type=apk&bin=1')
# manifest_data是對AndroidManifest.xml文件的處理,4.1節已介紹
man_data_dic = manifest_data(app_dic['parsed_xml'])

# get_app_details獲取APP詳細數據,稍後介紹
app_dic['playstore'] = get_app_details(
    man_data_dic['packagename'])

# manifest_analysis是對AndroidManifest.xml文件的處理,4.1節已介紹
man_an_dic = manifest_analysis(
    app_dic['parsed_xml'],
    man_data_dic)
bin_an_buff = []

# elf_analysis是二進制分析,稍後介紹
bin_an_buff += elf_analysis(app_dic['app_dir'])

# res_analysis是二進制分析,稍後介紹
bin_an_buff += res_analysis(app_dic['app_dir'])

# cert_info是對證書的分析,稍後介紹
cert_dic = cert_info(
    app_dic['app_dir'],
    app_dic['app_file'])

# apkid_analysis是對apkid的分析,稍後介紹
apkid_results = apkid_analysis(app_dic[
    'app_dir'], app_dic['app_path'], app_dic['app_name'])
    
# Trackers追蹤檢測,稍後介紹
tracker = Trackers.Trackers(
    app_dic['app_dir'], app_dic['tools_dir'])
tracker_res = tracker.get_trackers()

# apk_2_java反編譯爲Java代碼,稍後介紹
apk_2_java(app_dic['app_path'], app_dic['app_dir'],
           app_dic['tools_dir'])

# dex_2_smali反編譯爲smali代碼,稍後介紹
dex_2_smali(app_dic['app_dir'], app_dic['tools_dir'])

# code_analysis代碼分析,稍後介紹
code_an_dic = code_analysis(
    app_dic['app_dir'],
    man_an_dic['permissons'],
    'apk')

好啦,看完我寫的備註,應該已經一目瞭然了。接下來我們逐個跟入,看看到底是咋實現的!

1-跟入manifest_data/manifest_analysis

跟入後包括對AndroidManifest.xml的解析,這就又回到/StaticAnalyzer/views/android/manifest_analysis.py文件中了,該文件的功能在4.1、AndroidManifest.xml安全分析小節中介紹過,功能其實也沒啥,就是對AndroidManifest.xml的處理,就不再介紹了。

2-跟入get_app_details

接下來是通過應用商店對APP的細節數據做一個讀取,包括APP名字、評分、價格、下載URL……等待數據。跟入到/StaticAnalyzer/views/android/playstore.py文件中。

3-跟入elf_analysis/res_analysis

之後,進入二進制分析階段,跟入到/StaticAnalyzer/views/android/binary_analysis.py文件中。

話說,博主本來想着好好寫的,看了半天發現這個代碼文件中竟然沒啥可講的。整個文件中的功能是對二進制文件做了分析處理。包括res、assets目錄下的資源文件,lib下的.so文件等。

4-跟入cert_info

接下來是對證書做分析處理,跟入到/StaticAnalyzer/views/android/cert_analysis.py文件中。

這個文件代碼一共有2個函數,因此,也只有2個功能。

get_hardcoded_cert_keystore該函數並不是我們跟進來的函數,不過既然在一個文件中,那就一併講解下。該函數的功能是查找證書文件或密鑰文件並返回。包括cer、pem、cert、crt、pub、key、pfx、p12等證書文件,以及jks、bks等密鑰庫文件。

cert_info該函數是我們跟進來的函數。該函數的功能是獲取證書文件信息並對其進行分析。包括debug簽名、SHA1哈希不安全的簽名、正常簽名等。其實也沒啥好說的。

5-跟入apkid_analysis

接下來是apkid分析梳理,跟入到/MalwareAnalyzer/views/apkid.py文件中。

對APKID進行的分析處理,其中背後的核心庫在/venv/lib/python3.7/site-packages/apkid目錄下,由於這個是安裝時會自動下載的,因此這種庫的東西我們不做分析。

對apkid的分析處理並沒有很複雜,在做了簡單的判斷之後,就開始分析出了。

代碼如下:

# 從導入庫可以看到端倪
from apkid.apkid import Scanner, Options
from apkid.output import OutputFormatter
from apkid.rules import RulesManager

logger.info('Running APKiD %s', apkid_ver)

# 跟進Options到site-packages/apkid/apkid.py中
options = Options(
    timeout=30,
    verbose=False,
    entry_max_scan_size=100 * 1024 * 1024,
    recursive=True,
)

# 跟進OutputFormatter到site-packages/apkid/output.py中
output = OutputFormatter到(
    json_output=True,
    output_dir=None,
    rules_manager=RulesManager(),
)

# 以下的函數跟進後也是在上兩個代碼文件中
rules = options.rules_manager.load()
scanner = Scanner(rules, options)
res = scanner.scan_file(apk_file)
try:
    findings = output._build_json_output(res)['files']
except AttributeError:
    # apkid >= 2.0.3
    findings = output.build_json_output(res)['files']
sanitized = {}
6-跟入Trackers

接下來是追蹤檢測。跟入到/MalwareAnalyzer/views/Trackers.py文件中。

那麼將該文件中的所有函數功能一併講解下:

_update_tracker_db函數的主要功能是更新跟蹤檢測數據庫。

_compile_signatures函數的主要功能是編譯與每個簽名相關的正則表達式,以此加快跟蹤器的檢測速度。

load_trackers_signatures函數的主要功能是從官方數據庫加載跟蹤器簽名。

get_embedded_classes函數的主要功能是從所有DEX文件中獲取Java類的列表,這裏使用的工具是baksmali。

detect_trackers_in_list函數的功能是根據上個函數提供的Java類列表,檢測嵌入在其中的跟蹤器,並返回嵌入的跟蹤器列表。

detect_trackers函數的主要功能是檢測嵌入的跟蹤器,並返回嵌入的跟蹤器列表。

get_trackers函數的主要功能是獲取跟蹤器。

7-跟入apk_2_java/dex_2_smali

看完跟蹤器,我們回過頭繼續。

現在要跟入的是將APK反編譯爲Java代碼的功能和將dex反編譯爲smali代碼的功能。跟入到/StaticAnalyzer/views/android/converter.py文件中。

dex_2_smali函數是通過baksmali工具將dex反編譯爲smali代碼。

其使用的參數如下:

for dex_path in dexes:
    logger.info('Converting %s to Smali Code',
                filename_from_path(dex_path))
    if (len(settings.BACKSMALI_BINARY) > 0
            and is_file_exists(settings.BACKSMALI_BINARY)):
        bs_path = settings.BACKSMALI_BINARY
    else:
        bs_path = os.path.join(tools_dir, 'baksmali-2.3.4.jar')
    output = os.path.join(app_dir, 'smali_source/')
    smali = [
        settings.JAVA_BINARY,
        '-jar',
        bs_path,
        'd',
        dex_path,
        '-o',
        output,
    ]

apk_2_java函數是通過jadx工具將APK反編譯爲Java代碼。

相關代碼比較長,因爲需要將參數的源頭也寫入。

代碼如下:

def apk_2_java(app_path, app_dir, tools_dir):
    """Run jadx."""
    try:
        logger.info('APK -> JAVA')
        args = []
        output = os.path.join(app_dir, 'java_source/')
        logger.info('Decompiling to Java with jadx')

        if os.path.exists(output):
            shutil.rmtree(output)

        if (len(settings.JADX_BINARY) > 0
                and is_file_exists(settings.JADX_BINARY)):
            jadx = settings.JADX_BINARY
        else:
            if platform.system() == 'Windows':
                jadx = os.path.join(tools_dir, 'jadx/bin/jadx.bat')
            else:
                jadx = os.path.join(tools_dir, 'jadx/bin/jadx')
                # Set write permission, if JADX is not executable
                if not os.access(jadx, os.X_OK):
                    os.chmod(jadx, stat.S_IEXEC)
            args = [
                jadx,
                '-ds',
                output,
                '-q',
                '-r',
                '--show-bad-code',
                app_path,
            ]
            fnull = open(os.devnull, 'w')
            subprocess.call(args,
                            stdout=fnull,
                            stderr=subprocess.STDOUT)
    except Exception:
        logger.exception('Decompiling to JAVA')
8-跟入code_analysis

回過頭繼續,接下來是代碼分析,跟入到/StaticAnalyzer/views/android/code_analysis.py文件中。

核心代碼如下:

# 源碼情況下的代碼分析
relative_java_path = jfile_path.replace(java_src, '')
code_rule_matcher(
    code_findings,
    list(perms.keys()),
    dat,
    relative_java_path,
    code_rules)
# 使用API情況下的代碼分析
api_rule_matcher(api_findings, list(perms.keys()),
                 dat, relative_java_path, api_rules)
# 通過URL或郵件提取結果
urls, urls_nf, emails_nf = url_n_email_extract(
    dat, relative_java_path)
9-再次跟入到shared_func

以上代碼跟入後,發現均來到了/StaticAnalyzer/views/shared_func.py文件中。那我們繼續來看這個文件中的代碼邏輯。

這個文件的代碼主要是對APP做靜態分析的,包括APK、IPA、APPX等,因爲將三者的靜態分析共同的部分放在一起,因此文件名叫共享功能(shared_func.py)。

其中,生成哈希(hash_gen函數)、解壓(unzip函數)、報告處理(pdf函數)、API相關的(add_apis函數和api_rule_matcher函數)、URL和郵件地址提取(url_n_email_extract函數)我們就不看了,不是主要功能。

靜態分析規則匹配(code_rule_matcher函數)主要是通過遍歷規則來分析得到相應的結果,其分爲兩部分,規則類型爲正則表達式的和規則類型爲字符串的。

規則類型爲正則表達式的又分爲單個正則表達式、多個與關係的正則表達式、多個或關係的正則表達式、多個與關係的固定的正則表達式等,其通過匹配列表(get_list_match_items函數)和代碼分析結果(add_findings函數)做規則匹配,最終產生結果。

規則類型爲字符串的就比較複雜一點了,其分爲單個字符串、多個與關係的字符串、多個或關係的字符串、多個與或關係的字符串、多個或與關係的字符串、多個與關係的固定的字符串、多個或與關係的固定的字符串等,通過匹配列表(get_list_match_items函數)和代碼分析結果(add_findings函數)做規則匹配,最終產生結果。

代碼如下:

# 規則類型爲正則表達式的
if rule['type'] == 'regex':
    # 單個正則表達式的
    if rule['match'] == 'single_regex':
        if re.findall(rule['regex1'], tmp_data):
            add_findings(findings, rule[
                         'desc'], file_path, rule)
    
    # 多個與關係的正則表達式的
    elif rule['match'] == 'regex_and':
        and_match_rgx = True
        match_list = get_list_match_items(rule)
        for match in match_list:
            if bool(re.findall(match, tmp_data)) is False:
                and_match_rgx = False
                break
        if and_match_rgx:
            add_findings(findings, rule[
                         'desc'], file_path, rule)
    
    # 多個或關係的正則表達式的
    elif rule['match'] == 'regex_or':
        match_list = get_list_match_items(rule)
        for match in match_list:
            if re.findall(match, tmp_data):
                add_findings(findings, rule[
                             'desc'], file_path, rule)
                break
    
    # 多個與關係的固定的正則表達式的
    elif rule['match'] == 'regex_and_perm':
        if (rule['perm'] in perms
                and re.findall(rule['regex1'], tmp_data)):
            add_findings(findings, rule[
                         'desc'], file_path, rule)
    
    # 其他情況的,報錯
    else:
        logger.error('Code Regex Rule Match Error\n %s', rule)

# 規則類型爲字符串的
elif rule['type'] == 'string':
    # 單個字符串的
    if rule['match'] == 'single_string':
        if rule['string1'] in tmp_data:
            add_findings(findings, rule[
                         'desc'], file_path, rule)
    
    # 多個與關係的字符串的
    elif rule['match'] == 'string_and':
        and_match_str = True
        match_list = get_list_match_items(rule)
        for match in match_list:
            if (match in tmp_data) is False:
                and_match_str = False
                break
        if and_match_str:
            add_findings(findings, rule[
                         'desc'], file_path, rule)
    
    # 多個或關係的字符串的
    elif rule['match'] == 'string_or':
        match_list = get_list_match_items(rule)
        for match in match_list:
            if match in tmp_data:
                add_findings(findings, rule[
                             'desc'], file_path, rule)
                break
    
    # 多個與或關係的字符串的
    elif rule['match'] == 'string_and_or':
        match_list = get_list_match_items(rule)
        string_or_stat = False
        for match in match_list:
            if match in tmp_data:
                string_or_stat = True
                break
        if string_or_stat and (rule['string1'] in tmp_data):
            add_findings(findings, rule[
                         'desc'], file_path, rule)
    
    # 多個或與關係的字符串的
    elif rule['match'] == 'string_or_and':
        match_list = get_list_match_items(rule)
        string_and_stat = True
        for match in match_list:
            if match in tmp_data is False:
                string_and_stat = False
                break
        if string_and_stat or (rule['string1'] in tmp_data):
            add_findings(findings, rule[
                         'desc'], file_path, rule)
    
    # 多個與關係的固定的字符串的
    elif rule['match'] == 'string_and_perm':
        if (rule['perm'] in perms
                and rule['string1'] in tmp_data):
            add_findings(findings, rule[
                         'desc'], file_path, rule)
    
    # 多個或與關係的固定的字符串的
    elif rule['match'] == 'string_or_and_perm':
        match_list = get_list_match_items(rule)
        string_or_ps = False
        for match in match_list:
            if match in tmp_data:
                string_or_ps = True
                break
        if (rule['perm'] in perms) and string_or_ps:
            add_findings(findings, rule[
                         'desc'], file_path, rule)
    
    # 其他情況的,報錯
    else:
        logger.error('Code String Rule Match Error\n%s', rule)
        
# 規則類型爲其他的直接報錯
else:
    logger.error('Code Rule Error\n%s', rule)

在這之後,做了從源碼中提取URL地址和郵件地址的操作,沒啥可講的,通過正則遍歷源碼實現的。

接下來呢,是個兩個APP比較的功能,注意哈希值一樣的兩個APP不能比較哦。

再之後,是一個記分功能,就是通過AVG CVSS記分的,高危(high)減去15分,警告(warning)減去10分,好(good)增加5分。很簡單的功能實現。

再之後更新了最後掃描時間(update_scan_timestamp函數),檢測打開的Firebase數據庫(open_firebase函數),檢測Firebase的URL(firebase_analysis函數)。

之後,沒有之後了,這個就完結了!

4.4、獲取字符串

不忘初心,我們回到/StaticAnalyzer/views/android/static_analyzer.py文件中。

接下來是獲取APP的常量字符串。

代碼如下:

string_res = strings_jar(
    app_dic['app_file'],
    app_dic['app_dir'])
if string_res:
    app_dic['strings'] = string_res['strings']
    code_an_dic['urls_list'].extend(
        string_res['urls_list'])
    code_an_dic['urls'].extend(string_res['url_nf'])
    code_an_dic['emails'].extend(string_res['emails_nf'])
else:
    app_dic['strings'] = []

我們跟入strings_jar,來到/StaticAnalyzer/views/android/strings.py文件中。它只有一個功能:從APP中提取常量字符串。

4.5、數據準備及入庫

我們回到源頭繼續向後,接下來是數據入庫前的檢查以及數據存入數據庫。

代碼如下:

# Firebase數據庫檢查
code_an_dic['firebase'] = firebase_analysis(
    list(set(code_an_dic['urls_list'])))

# 域名提取和惡意軟件檢查
logger.info(
    'Performing Malware Check on extracted Domains')
code_an_dic['domains'] = malware_check(
    list(set(code_an_dic['urls_list'])))

# 複製APP圖標
copy_icon(app_dic['md5'], app_dic['icon_path'])
app_dic['zipped'] = 'apk'

其中firebase_analysis函數(/StaticAnalyzer/views/shared_func.py)我們剛纔看過了,copy_icon函數(/StaticAnalyzer/views/android/static_analyzer.py)沒啥可看的。那我們就來看看malware_check函數吧。

跟入malware_check函數,我們來到/MalwareAnalyzer/views/domian_check.py文件中。

這個文件夾主要是分析處理惡意軟件,對應靜態分析報告中的Malware Analysis欄目,其包括Domain Malware Check子項目。
update_malware_db函數的功能是更新惡意軟件數據庫;
malware_check函數的功能是校驗惡意軟件;
verify_domain函數的功能是驗證URL;
get_netloc函數的功能是獲取單個URL,注意代碼中是用的是domain;
get_domains函數的功能是獲取多個URL,注意代碼中使用的是domains。

好了,看完domian_check.py文件,我們回過頭繼續看static_analyzer.py文件。

接下來就是把數據往數據庫裏面放了,這裏跟入了get_context_from_analysis函數,來到了/StaticAnalyzer/views/android/db_interaction.py文件中。不過這個文件中的代碼沒啥可分析的。

之後又跟入了VirusTotal函數,來到了/MalwareAnalyzer/views/VirusTotal.py文件中。這是一個統計安全問題總數的地方,也沒啥可說的。

代碼如下:

if settings.VT_ENABLED:
    vt = VirusTotal.VirusTotal()     # 從此處跟入
    context['virus_total'] = vt.get_result(
        os.path.join(app_dic['app_dir'],
                     app_dic['md5']) + '.apk',
        app_dic['md5'])

至此,上傳APK包的靜態分析就結束了,接下來是上傳ZIP源碼包的靜態分析,我們就不看了。

靜態分析總結

如果你認真閱讀完本篇分析文章,再對應看靜態分析報告,你會發現,所有的功能我們都分析到了,現在我們也知道這些強大的功能背後是如何實現的了。

靜態掃描的源碼分析就結束了!

五、動態掃描分析

先捋一下代碼

和靜態分析篇一樣,我將它寫在了最前面。

動態分析的核心部分在/DynamicAnalyzer/views/目錄下,另外我們這次只分析Android的APK,因此所分析的代碼集中在/DynamicAnalyzer/views/android/目錄下。

tools/webproxy.py:設置代理,httptools相關
views/android/analysis.py:對動態分析得到的數據進行分析處理
views/android/dynamic_analyzer.py:動態分析流程文件(主文件)
views/android/environment.py:動態分析環境配置相關
views/android/frida_core.py:Frida框架核心操作部分
views/android/frida_scripts.py:Frida框架腳本
views/android/operations.py:動態分析操作
views/android/report.py:動態分析報告輸出
views/android/tests_common.py:命令測試
views/android/tests_frida.py:Frida框架測試
views/android/tests_xposed.py:Xposed框架測試

我們接下來的分析中,我們會按照流程一步一步走完動態分析,出了非必要的,其他代碼都會涉及到。

捋完代碼我們再繼續

再經過漫長的靜態源碼分析後,我們現在開始進行動態掃描源碼分析。通過瀏覽/DynamicAnalyzer文件下的代碼,我們發現動態掃描其實只有Android纔有。

截止博主分析日期,最新的3.0beta已經不再支持物理設備,僅支持虛擬設備(主要是genymotion)。對於小於Android 5.0的系統版本,會使用Xposed框架,對於大於等於Android 5.0的系統版本,會使用Frida框架。

Android 5.0以下的系統屬於骨灰級,都2020年了,這些老系統連學習的價值都沒有。因此,下文分析中所有遇到Xposed的都將跳過不做分析。

我們回到最開始的定義URL的地方,/MobSF/urls.py文件中,找到動態分析的地方。

代碼如下:

url(r'^dynamic_analysis/$',
    dz.dynamic_analysis,
    name='dynamic'),
url(r'^android_dynamic/$',
    dz.dynamic_analyzer,
    name='dynamic_analyzer'),
url(r'^httptools$',
    dz.httptools_start,
    name='httptools'),
url(r'^logcat/$', dz.logcat),

以上任意函數跟入,我們來到/DynamicAnalyzer/views/android/dynamic_analyzer.py文件中。

我們先不管它跟進來是到哪個函數,我們從上到下逐次分析。

5.1、dynamic_analysis函數

dynamic_analysis函數是動態分析的入口點

這裏首先會檢測模擬器,如果模擬器正常運行起來並被檢測到,則獲取設備數據,跟進到/MobSF/utils.py文件的get_device函數,之後設置代理IP,跟進到/MobSF/utils.py文件的get_proxy_ip函數,其功能是獲取網絡IP並根據它設置代理IP。

如果模擬器未啓動或沒有被檢測到,則報錯,跟進到/MobSF/utils.py文件的print_n_send_error_response函數。

5.2、dynamic_analyzer函數

dynamic_analyzer函數主要功能是配置/創建動態分析環境

在獲取到設備信息後:

......
identifier = get_device()
......
env = Environment(identifier)
......

我們跟入Environment函數,來到環境配置,在/DynamicAnalyzer/views/android/environment.py文件中。

Environment.py文件分析

我們還是先不管跟入的是哪個函數,從上到下講解下各個函數功能。

connect_n_mount函數的功能是重啓adb服務,之後嘗試adb連接設備。

adb_command函數的功能是adb命令包裝,所有將要執行的命令都會經過包裝後成爲可以執行的命令,然後執行。

dz_cleanup函數的功能是清除之前的動態分析記錄和數據,以便於新的動態分析不受影響。

configure_proxy函數的主要功能是設置代理。具體步驟是先調用Httptools殺死請求,再在代理模式下開啓Httptools。

代碼如下:

def configure_proxy(self, project):
    proxy_port = settings.PROXY_PORT
    logger.info('Starting HTTPs Proxy on %s', proxy_port)
    stop_httptools(proxy_port)    # 調用Httptools殺死請求
    start_proxy(proxy_port, project)    # 在代理模式下開啓Httptools

這兩個函數均跟入到/DynamicAnalyzer/tools/webproxy.py文件中。這個文件中的代碼很簡單,就不再分析了。

install_mobsf_ca函數的主要功能是安裝或刪除MobSF的跟證書(ROOT CA)。

set_global_proxy函數的主要功能是給設備設置全局代理,這個功能僅支持Android 4.4及以上系統,設置代理IP的功能會跟入到/MobSF/utils.py的get_proxy_ip函數中。對於小於Android 4.4的系統版本,會將代理設置爲:127.0.0.1:1337

unset_global_proxy函數的主要功能是取消設置的全局代理。

enable_adb_reverse_tcp函數的主要功能是開啓adb反向TCP代理,該功能僅支持Android 5.0以上的系統。

start_clipmon函數的主要功能是開始剪切板監控。

get_screen_res函數的主要功能是獲取當前設備的屏幕分辨率。

screen_shot函數的主要功能是截屏,並保存爲/data/local/screen.png。

screen_stream函數的主要功能是分析屏幕流。

android_component函數的主要功能是獲取APK的組件,包括Activity、Receiver、Provider、Service、Library等。

get_android_version函數的主要功能是獲取Android版本。

get_android_arch函數的主要功能是獲取Android體系結構。

launch_n_capture函數的主要功能是啓動和捕獲Activity,是通過截屏實現的。

is_mobsfyied函數的主要功能是獲取Android的MobSfyed實例,讀取Xposed或Frida文件並輸出。

代碼如下:

if android_version < 5:
    agent_file = '.mobsf-x'
    agent_str = b'MobSF-Xposed'
else:
    agent_file = '.mobsf-f'
    agent_str = b'MobSF-Frida'
try:
    out = subprocess.check_output(
        [get_adb(),
         '-s', self.identifier,
         'shell',
         'cat',
         '/system/' + agent_file])
    if agent_str not in out:
        return False
except Exception:
    return False
return True

mobsfy_init函數的主要功能是設置MobSF代理,安裝Xposed或Frida框架。

代碼如下:

# 系統版本小於5.0,安裝Xposed框架
if version < 5:
    self.xposed_setup(version)
    self.mobsf_agents_setup('xposed')

# 系統版本大於等於5.0,安裝Frida框架
else:
    self.frida_setup()
    self.mobsf_agents_setup('frida')
logger.info('MobSFying Completed!')
return version

mobsf_agents_setup函數的主要功能是安裝MobSF根證書,設置MobSF代理。

xposed_setup函數的主要功能是安裝Xposed框架。

frida_setup函數的主要功能是安裝Frida框架。

run_frida_server函數的主要功能是運行Frida框架。

至此,/DynamicAnalyzer/views/android/environment.py文件的代碼我們就過了一遍了,整個文件主要是做動態分析的環境準備工作,代碼邏輯非常簡單,特別容易理解。

回過頭繼續

剛纔分析了environment.py文件,我們是按照代碼從上到下的順序分析的,並不是按運行邏輯順序分析的。現在我們按邏輯運行順序繼續向下看一下。

看我寫的代碼備註即可。

# 動態分析環境準備
env = Environment(identifier)

# 如果測試ADB連接失敗
if not env.connect_n_mount():
    msg = 'Cannot Connect to ' + identifier
    return print_n_send_error_response(request, msg)

# 獲取Android版本
version = env.get_android_version()
logger.info('Android Version identified as %s', version)
xposed_first_run = False

# 根據系統版本獲取Android的MobSfyed實例,如果失敗
if not env.is_mobsfyied(version):
    msg = ('This Android instance is not MobSfyed.\n'
           'MobSFying the android runtime environment')
    logger.warning(msg)
    # 設置MobSF代理,如果失敗
    if not env.mobsfy_init():
        return print_n_send_error_response(
            request,
            'Failed to MobSFy the instance')
    if version < 5:
        xposed_first_run = True

# 第一次運行Xposed框架,會重啓設備以啓用所有模塊
if xposed_first_run:
    msg = ('Have you MobSFyed the instance before'
           ' attempting Dynamic Analysis?'
           ' Install Framework for Xposed.'
           ' Restart the device and enable'
           ' all Xposed modules. And finally'
           ' restart the device once again.')
    return print_n_send_error_response(request, msg)

# 清除之前的動態分析記錄和數據
env.dz_cleanup(bin_hash)

# 設置web代理
env.configure_proxy(package)

# 開啓adb反向TCP代理,僅支持5.0以上系統
env.enable_adb_reverse_tcp(version)

# 給設備設置全局代理,這個功能僅支持Android 4.4及以上系統
env.set_global_proxy(version)

# 開始剪切板監控
env.start_clipmon()

# 獲取當前設備的屏幕分辨率
screen_width, screen_height = env.get_screen_res()
logger.info('Installing APK')

# APP目錄
app_dir = os.path.join(settings.UPLD_DIR, bin_hash + '/')

# APP路徑
apk_path = app_dir + bin_hash + '.apk'

# adb命令包裝並執行
env.adb_command(['install', '-r', apk_path], False, True)

logger.info('Testing Environment is Ready!')
context = {'screen_witdth': screen_width,
           'screen_height': screen_height,
           'package': package,
           'md5': bin_hash,
           'android_version': version,
           'version': settings.MOBSF_VER,
           'title': 'Dynamic Analyzer'}
template = 'dynamic_analysis/android/dynamic_analyzer.html'

# 通過HttpResponse返回數據
return render(request, template, context)

5.3、httptools_start函數

httptools_start函數的主要功能是在代理模式下開啓Httptools。

這裏是先調用Httptools殺死請求,再在代理模式下開啓Httptools。

代碼如下:

stop_httptools(settings.PROXY_PORT)
start_httptools_ui(settings.PROXY_PORT)
time.sleep(3)
logger.info('httptools UI started')

webproxy.py文件分析

我們跟入到/DynamicAnalyzer/tools/webproxy.py文件中,這個文件中的代碼很簡單。

stop_httptools函數的主要功能是殺死httptools,分爲兩步,第一步是通過調用httptools UI殺死請求,第二步是通過調用httptools代理殺死請求。

start_proxy函數的主要功能是在代理模式下開啓Httptools。

start_httptools_ui函數的功能是啓動httptools的UI。

create_ca函數的功能是第一次運行時創建CA

get_ca_dir函數的功能時獲取CA目錄

5.4、logcat函數

logcat函數主要是啓動logcat流,獲取日誌的。

這個函數沒啥可分析的,就不做分析了。

來看看operations.py文件

這個文件的主要功能是動態分析操作,我們從上到下看一下。

json_response函數的主要功能是返回JSON響應

is_attack_pattern函數的主要功能是通過正則表達式驗證攻擊

strict_package_check函數的主要功能是通過正則表達式校驗包名稱

is_path_traversal函數的主要功能是檢查路徑遍歷

is_md5函數的主要功能是通過正則表達式檢查是否是有效的MD5

invalid_params函數的主要功能是檢查無效參數響應

mobsfy函數的主要功能是通過POST方法配置實例以進行動態分析

execute_adb函數的主要功能是通過POST方法執行ADB命令

get_component函數的主要功能是通過POST方法獲取Android組件

take_screenshot函數的主要功能是通過POST方法截屏

screen_cast函數的主要功能是通過POST方法投屏

touch函數的主要功能是通過POST方法發送觸摸事件

mobsf_ca函數的主要功能是通過POST方法安裝或刪除MobSF代理的ROOT CA

再看看analysis.py文件

該文件的主要功能是對動態分析獲取的數據進行分析,我們也是從上到下看一下。

run_analysis函數的主要功能是運行動態文件分析。

首先收集了日誌數據並對日誌進行遍歷篩選處理,代碼如下:

# 收集日誌
datas = get_log_data(apk_dir, package)
clip_tag = 'I/CLIPDUMP-INFO-LOG'
clip_tag2 = 'I CLIPDUMP-INFO-LOG'
# 遍歷日誌數據,對日誌數據進行處理
for log_line in datas['logcat']:
    if clip_tag in log_line:
        clipboard.append(log_line.replace(clip_tag, 'Process ID '))
    if clip_tag2 in log_line:
        log_line = log_line.split(clip_tag2)[1]
        clipboard.append(log_line)

通過正則表達式收集的URL數據,代碼如下:

url_pattern = re.compile(
    r'((?:https?://|s?ftps?://|file://|'
    r'javascript:|data:|www\d{0,3}'
    r'[.])[\w().=/;,#:@?&~*+!$%\'{}-]+)', re.UNICODE)
urls = re.findall(url_pattern, datas['traffic'].lower())
if urls:
    urls = list(set(urls))
else:
    urls = []

然後對惡意URL進行檢查,通過匹配這些URL是否出現在惡意軟件列表裏實現,代碼如下:

domains = malware_check(urls)

跟入到/MalwareAnalyzer/views/domian_check.py文件的malware_check函數。domian_check.py文件在本篇的4.5、數據準備及入庫章節有講解,此處就不再講解了。

之後通過正則提取了所有的電子郵件地址,代碼如下:

emails = []
regex = re.compile(r'[\w.-]+@[\w-]+\.[\w]{2,}')
for email in regex.findall(datas['traffic'].lower()):
    if (email not in emails) and (not email.startswith('//')):
        emails.append(email)

然後做了結果彙總,代碼如下:

all_files = get_app_files(apk_dir, md5_hash, package)
analysis_result['urls'] = urls
analysis_result['domains'] = domains
analysis_result['emails'] = emails
analysis_result['clipboard'] = clipboard
analysis_result['xml'] = all_files['xml']
analysis_result['sqlite'] = all_files['sqlite']
analysis_result['other_files'] = all_files['others']

最後返回分析結果。

get_screenshots函數的主要功能是獲截圖。

get_log_data函數的主要功能是對日誌數據進行分析。

通過執行adb或其他可執行文件進行處理,得到web數據、日誌數據、域名數據、API數據、Frida數據,並返回。

get_app_files函數的主要功能是從設備獲取APP文件。

包括提取設備數據,對設備中的數據做靜態分析等。

generate_download函數的主要功能是生成文件下載。

生成文件下載後,會刪除現有數據,然後複製新數據。

至此,Android動態分析源代碼分析就結束了

動態分析總結

沒啥可總結的,該分析的都分析了!

六、總結

本文沒有任何參考文獻,因爲網上所有的源碼分析文章都已經過時很久很久了……

歷時一星期,博主要吐血了!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章