├── LICENSE ├── README.md └── src ├── DDNA-MalwareActivityDetectionHelper.cpp ├── DDNA-MalwareActivityDetectionHelper.hpp ├── DDNA-MalwareActivityDetectionOperation.cpp ├── DDNA-MalwareActivityDetectionOperation.hpp ├── DDNA-MalwareCheck.cpp ├── DDNA-MalwareCheck.hpp ├── DDNA-MalwareCheckResult.cpp ├── DDNA-MalwareCheckResult.hpp ├── DDNA-STIXIndicators.cpp ├── DDNA-STIXIndicators.hpp └── MalwareChecks ├── DDNA-MalwareCheck-Applications.cpp ├── DDNA-MalwareCheck-Applications.hpp ├── DDNA-MalwareCheck-Calendar.cpp ├── DDNA-MalwareCheck-Calendar.hpp ├── DDNA-MalwareCheck-CallHistory.cpp ├── DDNA-MalwareCheck-CallHistory.hpp ├── DDNA-MalwareCheck-ChromeFavicon.cpp ├── DDNA-MalwareCheck-ChromeFavicon.hpp ├── DDNA-MalwareCheck-ChromeHistory.cpp ├── DDNA-MalwareCheck-ChromeHistory.hpp ├── DDNA-MalwareCheck-ConfigurationProfiles.cpp ├── DDNA-MalwareCheck-ConfigurationProfiles.hpp ├── DDNA-MalwareCheck-Contacts.cpp ├── DDNA-MalwareCheck-Contacts.hpp ├── DDNA-MalwareCheck-DataUsage.cpp ├── DDNA-MalwareCheck-DataUsage.hpp ├── DDNA-MalwareCheck-EdgeFavicon.cpp ├── DDNA-MalwareCheck-EdgeFavicon.hpp ├── DDNA-MalwareCheck-EdgeHistory.cpp ├── DDNA-MalwareCheck-EdgeHistory.hpp ├── DDNA-MalwareCheck-FirefoxFavicon.cpp ├── DDNA-MalwareCheck-FirefoxFavicon.hpp ├── DDNA-MalwareCheck-FirefoxHistory.cpp ├── DDNA-MalwareCheck-FirefoxHistory.hpp ├── DDNA-MalwareCheck-IDStatusCache.cpp ├── DDNA-MalwareCheck-IDStatusCache.hpp ├── DDNA-MalwareCheck-InteractionC.cpp ├── DDNA-MalwareCheck-InteractionC.hpp ├── DDNA-MalwareCheck-InteractionCAttachment.cpp ├── DDNA-MalwareCheck-InteractionCAttachment.hpp ├── DDNA-MalwareCheck-LocationD.cpp ├── DDNA-MalwareCheck-LocationD.hpp ├── DDNA-MalwareCheck-Manifest.cpp ├── DDNA-MalwareCheck-Manifest.hpp ├── DDNA-MalwareCheck-OSAnalyticsAdDaily.cpp ├── DDNA-MalwareCheck-OSAnalyticsAdDaily.hpp ├── DDNA-MalwareCheck-OperaHistory.cpp ├── DDNA-MalwareCheck-OperaHistory.hpp ├── DDNA-MalwareCheck-ProfileEvents.cpp ├── DDNA-MalwareCheck-ProfileEvents.hpp ├── DDNA-MalwareCheck-SMS.cpp ├── DDNA-MalwareCheck-SMS.hpp ├── DDNA-MalwareCheck-SMSAttachment.cpp ├── DDNA-MalwareCheck-SMSAttachment.hpp ├── DDNA-MalwareCheck-SafariBrowserState.cpp ├── DDNA-MalwareCheck-SafariBrowserState.hpp ├── DDNA-MalwareCheck-SafariHistory.cpp ├── DDNA-MalwareCheck-SafariHistory.hpp ├── DDNA-MalwareCheck-Shortcuts.cpp ├── DDNA-MalwareCheck-Shortcuts.hpp ├── DDNA-MalwareCheck-TCC.cpp ├── DDNA-MalwareCheck-TCC.hpp ├── DDNA-MalwareCheck-Viber.cpp ├── DDNA-MalwareCheck-Viber.hpp ├── DDNA-MalwareCheck-WebKitObservations.cpp ├── DDNA-MalwareCheck-WebKitObservations.hpp ├── DDNA-MalwareCheck-WebKitSessionResourceLog.cpp ├── DDNA-MalwareCheck-WebKitSessionResourceLog.hpp ├── DDNA-MalwareCheck-WhatsApp.cpp ├── DDNA-MalwareCheck-WhatsApp.hpp ├── DDNA-MalwareCheck-WhatsAppBusiness.cpp └── DDNA-MalwareCheck-WhatsAppBusiness.hpp /README.md: -------------------------------------------------------------------------------- 1 | iMazing-Malware-Analyzer 2 | ======================== 3 | 4 | [![Issues](http://img.shields.io/github/issues/DigiDNA/iMazing-Malware-Analyzer.svg?style=flat)](https://github.com/DigiDNA/iMazing-Malware-Analyzer/issues) 5 | ![License](https://img.shields.io/badge/license-mvt-brightgreen.svg?style=flat) 6 | [![Contact](https://img.shields.io/badge/contact-@DigiDNA-blue.svg?style=flat)](https://twitter.com/DigiDNA) 7 | 8 | **iMazing-Malware-Analyzer** is a module in iMazing's C++ toolkit which implements spyware detection methodologies mirroring that of [Amnesty International's MVT project](https://github.com/mvt-project/mvt). 9 | 10 | This project cannot be built, it is published for transparency purposes. If you wish to contribute, please do so on the MVT repo and not here – we watch MVT closely and will update this repository when significant methodology changes occur there. 11 | 12 | This project is not affiliated to or endorsed by Amnesty International. Please read [this blog post](https://imazing.com/blog/detecting-pegasus-spyware-with-imazing) for more context on our implementation of MVT. 13 | 14 | Documentation 15 | ------------- 16 | 17 | Use of iMazing's spyware detection tool is anonymous and free. It is documented here: 18 | https://imazing.com/guides/detect-pegasus-and-other-spyware-on-iphone 19 | 20 | License 21 | ------- 22 | 23 | **iMazing-Malware-Analyzer** is released under the terms of the MVT License, v. 1.1. 24 | 25 | The MVT License is an adaptation of [Mozilla Public License v2.0](https://www.mozilla.org/en-US/MPL/). This modified license includes a new clause 3.0, "Consensual Use Restriction" which permits the use of the licensed software (and any "Larger Work" derived from it) exclusively with the explicit consent of the person/s whose data is being extracted and/or analysed ("Data Owner"). In accordance with the terms and spirit of the license, iMazing's UI enforces acceptance of these terms. 26 | 27 | Repository Infos 28 | ---------------- 29 | 30 | Owner: DigiDNA 31 | Web: imazing.com 32 | Blog: imazing.com/blog 33 | GitHub: github.com/DigiDNA 34 | -------------------------------------------------------------------------------- /src/DDNA-MalwareActivityDetectionHelper.hpp: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2021, DigiDNA 3 | * All rights reserved 4 | * 5 | * This Source Code Form is subject to the terms of the MVT License, v. 1.1. 6 | * If a copy of the MVT License was not distributed with this file, you can 7 | * obtain one at https://license.mvt.re/1.1/. 8 | ******************************************************************************/ 9 | 10 | #ifndef DDNA_MALWARE_ACTIVITY_DETECTION_H 11 | #define DDNA_MALWARE_ACTIVITY_DETECTION_H 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | namespace DDNA 21 | { 22 | class STIXIndicators; 23 | class MalwareCheckResult; 24 | 25 | namespace MalwareActivityDetectionHelper 26 | { 27 | static const char * const ErrorDomain = "com.DigiDNA.MobileDevice.MalwareActivityDetectionHelper";; 28 | 29 | Core::RecursiveLock & GetLock( void ); 30 | STIXIndicators LoadSTIXFiles( const std::string stixFolderPath, Core::Progress & progress, std::vector< MalwareCheckResult > & results, Core::Error & e ); 31 | std::shared_ptr< sqlite3 > ExtractAndOpenSQLiteDB( Core::SafePointer< Backup > backup, const std::string & backupDomain, const std::string & backupPath, const std::string & malwareCheckName, std::vector< MalwareCheckResult > & results ); 32 | CF::Dictionary ExtractPropertyList( Core::SafePointer< Backup > backup, const std::string & backupDomain, const std::string & backupPath, bool checkIfDictionaryIsEmpty, const std::string & malwareCheckName, std::vector< MalwareCheckResult > & results, Core::Error & e ); 33 | std::string MalwareFolderLocalPath( void ); 34 | std::string MalwareFolderLocalPath( Core::SafePointer< Backup > backup ); 35 | bool WriteMalwareCheckResultsToLogFile( std::vector< MalwareCheckResult > & results, const std::string & logPath, bool logAsExcel, Core::Error & e ); 36 | std::vector< std::string > FindLinks( std::string text ); 37 | std::string AppName( const std::string & bundleID ); 38 | std::string AppIconPath( const std::string & bundleID ); 39 | double AppleTimestampToUnixTimestamp( double appleTimestamp ); 40 | } 41 | } 42 | 43 | #endif /* DDNA_MALWARE_ACTIVITY_DETECTION_H */ 44 | -------------------------------------------------------------------------------- /src/DDNA-MalwareActivityDetectionOperation.hpp: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2021, DigiDNA 3 | * All rights reserved 4 | * 5 | * This Source Code Form is subject to the terms of the MVT License, v. 1.1. 6 | * If a copy of the MVT License was not distributed with this file, you can 7 | * obtain one at https://license.mvt.re/1.1/. 8 | ******************************************************************************/ 9 | 10 | #ifndef DDNA_MALWARE_ACTIVITY_DETECTION_OPERATION_H 11 | #define DDNA_MALWARE_ACTIVITY_DETECTION_OPERATION_H 12 | 13 | #include 14 | #include 15 | 16 | namespace DDNA 17 | { 18 | class iOSDevice; 19 | 20 | /*! 21 | * @class DDNA::MalwareActivityDetectionOperation 22 | * @abstract ... 23 | * @description ... 24 | */ 25 | class MalwareActivityDetectionOperation: public Core::Operation 26 | { 27 | public: 28 | static const char * const ErrorDomain; 29 | 30 | DDNA_PROPERTY_READONLY( Core::SafePointer< iOSDevice >, device ) 31 | DDNA_PROPERTY_READONLY( std::string, stixFolderPath ) 32 | DDNA_PROPERTY_READONLY( std::string, logPath ) 33 | DDNA_PROPERTY_READONLY( bool, logAsExcel ) 34 | 35 | MalwareActivityDetectionOperation( Core::SafePointer< iOSDevice > device, bool backupDevice, const std::string & stixFolderPath, const std::string & logPath, bool logAsExcel ); 36 | 37 | protected: 38 | bool _execute( void ) override; 39 | 40 | private: 41 | Core::SafePointer< Operation > _subOperation; 42 | std::map< std::string, std::string > _analyticsProperties; 43 | }; 44 | } 45 | #endif /* DDNA_MALWARE_ACTIVITY_DETECTION_OPERATION_H */ 46 | -------------------------------------------------------------------------------- /src/DDNA-MalwareCheck.cpp: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2021, DigiDNA 3 | * All rights reserved 4 | * 5 | * This Source Code Form is subject to the terms of the MVT License, v. 1.1. 6 | * If a copy of the MVT License was not distributed with this file, you can 7 | * obtain one at https://license.mvt.re/1.1/. 8 | ******************************************************************************/ 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | 43 | namespace DDNA 44 | { 45 | Core::ObjectVector< MalwareCheck * > MalwareCheck::allChecks() 46 | { 47 | Core::ObjectVector< MalwareCheck * > checks; 48 | 49 | checks.addObject( Core::makeSafe< MalwareChecks::Contacts >() ); 50 | checks.addObject( Core::makeSafe< MalwareChecks::Calendar >() ); 51 | checks.addObject( Core::makeSafe< MalwareChecks::SMS >() ); 52 | checks.addObject( Core::makeSafe< MalwareChecks::SMSAttachment >() ); 53 | checks.addObject( Core::makeSafe< MalwareChecks::WhatsApp >() ); 54 | checks.addObject( Core::makeSafe< MalwareChecks::WhatsAppBusiness >() ); 55 | checks.addObject( Core::makeSafe< MalwareChecks::Viber >() ); 56 | checks.addObject( Core::makeSafe< MalwareChecks::CallHistory >() ); 57 | checks.addObject( Core::makeSafe< MalwareChecks::SafariHistory >() ); 58 | checks.addObject( Core::makeSafe< MalwareChecks::SafariBrowserState >() ); 59 | checks.addObject( Core::makeSafe< MalwareChecks::ChromeFavicon >() ); 60 | checks.addObject( Core::makeSafe< MalwareChecks::ChromeHistory >() ); 61 | checks.addObject( Core::makeSafe< MalwareChecks::FirefoxFavicon >() ); 62 | checks.addObject( Core::makeSafe< MalwareChecks::FirefoxHistory >() ); 63 | checks.addObject( Core::makeSafe< MalwareChecks::EdgeFavicon >() ); 64 | checks.addObject( Core::makeSafe< MalwareChecks::EdgeHistory >() ); 65 | checks.addObject( Core::makeSafe< MalwareChecks::OperaHistory >() ); 66 | checks.addObject( Core::makeSafe< MalwareChecks::WebKitObservations >() ); 67 | checks.addObject( Core::makeSafe< MalwareChecks::WebKitSessionResourceLog >() ); 68 | checks.addObject( Core::makeSafe< MalwareChecks::Applications >() ); 69 | checks.addObject( Core::makeSafe< MalwareChecks::ConfigurationProfiles >() ); 70 | checks.addObject( Core::makeSafe< MalwareChecks::LocationD >() ); 71 | checks.addObject( Core::makeSafe< MalwareChecks::Shortcuts >() ); 72 | checks.addObject( Core::makeSafe< MalwareChecks::TCC >() ); 73 | checks.addObject( Core::makeSafe< MalwareChecks::ProfileEvents >() ); 74 | checks.addObject( Core::makeSafe< MalwareChecks::IDStatusCache >() ); 75 | checks.addObject( Core::makeSafe< MalwareChecks::InteractionC >() ); 76 | checks.addObject( Core::makeSafe< MalwareChecks::InteractionCAttachment >() ); 77 | checks.addObject( Core::makeSafe< MalwareChecks::OSAnalyticsAdDaily >() ); 78 | checks.addObject( Core::makeSafe< MalwareChecks::DataUsage >() ); 79 | checks.addObject( Core::makeSafe< MalwareChecks::Manifest >() ); 80 | 81 | return checks; 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/DDNA-MalwareCheck.hpp: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2021, DigiDNA 3 | * All rights reserved 4 | * 5 | * This Source Code Form is subject to the terms of the MVT License, v. 1.1. 6 | * If a copy of the MVT License was not distributed with this file, you can 7 | * obtain one at https://license.mvt.re/1.1/. 8 | ******************************************************************************/ 9 | 10 | #ifndef DDNA_MALWARE_CHECK_HPP 11 | #define DDNA_MALWARE_CHECK_HPP 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | #define FOLDER_WILDCARD std::string( "*/" ) 20 | 21 | namespace DDNA 22 | { 23 | class MalwareCheck: public Core::Object 24 | { 25 | public: 26 | 27 | static Core::ObjectVector< MalwareCheck * > allChecks(); 28 | 29 | virtual ~MalwareCheck() = default; 30 | 31 | virtual std::string name( void ) const = 0; 32 | virtual std::string image( void ) const = 0; 33 | virtual std::vector< iOSDataset > iOSDatasets( void ) const = 0; 34 | 35 | virtual void run( Core::SafePointer< Backup > backup, const STIXIndicators & indicators, std::vector< MalwareCheckResult > & results, Core::Progress & progress ) = 0; 36 | }; 37 | } 38 | 39 | #endif /* DDNA_MALWARE_CHECK_HPP */ 40 | -------------------------------------------------------------------------------- /src/DDNA-MalwareCheckResult.cpp: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2021, DigiDNA 3 | * All rights reserved 4 | * 5 | * This Source Code Form is subject to the terms of the MVT License, v. 1.1. 6 | * If a copy of the MVT License was not distributed with this file, you can 7 | * obtain one at https://license.mvt.re/1.1/. 8 | ******************************************************************************/ 9 | 10 | #include 11 | 12 | namespace DDNA 13 | { 14 | MalwareCheckResult::MalwareCheckResult( void ): 15 | _isSuspicious( false ), 16 | _type( Type::Analyzer ), 17 | _severity( Severity::Info ), 18 | _timestamp( 0 ), 19 | _creationTimestamp( Core::DateTime::UTCNow() ), 20 | _sortingIndex( 0 ) 21 | {} 22 | 23 | MalwareCheckResult::MalwareCheckResult( const std::string & malwareName, const std::string & malwareCategory, const std::string & event ): 24 | _isSuspicious( true ), 25 | _type( Type::Device ), 26 | _severity( Severity::Critical ), 27 | _malwareName( malwareName ), 28 | _malwareCategory( malwareCategory ), 29 | _timestamp( 0 ), 30 | _creationTimestamp( Core::DateTime::UTCNow() ), 31 | _event( event ) 32 | {} 33 | 34 | 35 | MalwareCheckResult::MalwareCheckResult( const Core::Error & error, const std::string & malwareCheckName, std::vector< MalwareCheckResult > & results ): 36 | _isSuspicious( false ), 37 | _type( Type::Error ), 38 | _severity( Severity::Info ), 39 | _malwareCheckName( malwareCheckName ), 40 | _timestamp( 0 ), 41 | _creationTimestamp( Core::DateTime::UTCNow() ), 42 | _sortingIndex( results.size() + 1 ), 43 | _event( EVENT_ANALYSING_ITEMS_INFO ), 44 | _data( Core::String::replace( error.message(), "\n", " - " ) ), 45 | _error( error ) 46 | { 47 | results.push_back( *( this ) ); 48 | } 49 | 50 | MalwareCheckResult::MalwareCheckResult( Type type, Severity severity, double timestamp, const std::string & malwareCheckName, const std::string & event, const std::string & data, std::vector< MalwareCheckResult > & results ): 51 | _isSuspicious( severity == Severity::Critical ), 52 | _type( type ), 53 | _severity( severity ), 54 | _malwareCheckName( malwareCheckName ), 55 | _timestamp( timestamp ), 56 | _creationTimestamp( Core::DateTime::UTCNow() ), 57 | _sortingIndex( results.size() + 1 ), 58 | _event( event ), 59 | _data( Core::String::replace( data, "\n", " - " ) ) 60 | { 61 | results.push_back( *( this ) ); 62 | } 63 | 64 | MalwareCheckResult::MalwareCheckResult( const MalwareCheckResult & o ): 65 | _isSuspicious( o._isSuspicious ), 66 | _type( o._type ), 67 | _severity( o._severity ), 68 | _malwareName( o._malwareName ), 69 | _malwareCheckName( o._malwareCheckName ), 70 | _timestamp( o._timestamp ), 71 | _creationTimestamp( o._creationTimestamp ), 72 | _sortingIndex( o._sortingIndex ), 73 | _event( o._event ), 74 | _data( o._data ) 75 | {} 76 | 77 | MalwareCheckResult::MalwareCheckResult( MalwareCheckResult && o ) noexcept: 78 | _isSuspicious( std::move( o._isSuspicious ) ), 79 | _type( std::move( o._type ) ), 80 | _severity( std::move( o._severity ) ), 81 | _malwareName( std::move( o._malwareName ) ), 82 | _malwareCheckName( std::move( o._malwareCheckName ) ), 83 | _timestamp( std::move( o._timestamp ) ), 84 | _creationTimestamp( std::move( o._creationTimestamp ) ), 85 | _sortingIndex( std::move( o._sortingIndex ) ), 86 | _event( std::move( o._event ) ), 87 | _data( std::move( o._data ) ) 88 | {} 89 | 90 | MalwareCheckResult::~MalwareCheckResult() 91 | {} 92 | 93 | MalwareCheckResult & MalwareCheckResult::operator =( MalwareCheckResult o ) 94 | { 95 | swap( *( this ), o ); 96 | 97 | return *( this ); 98 | } 99 | 100 | void swap( MalwareCheckResult & o1, MalwareCheckResult & o2 ) 101 | { 102 | using std::swap; 103 | 104 | swap( o1._isSuspicious, o2._isSuspicious ); 105 | swap( o1._type, o2._type ); 106 | swap( o1._severity, o2._severity ); 107 | swap( o1._malwareName, o2._malwareName ); 108 | swap( o1._malwareCheckName, o2._malwareCheckName ); 109 | swap( o1._timestamp, o2._timestamp ); 110 | swap( o1._creationTimestamp, o2._creationTimestamp ); 111 | swap( o1._sortingIndex, o2._sortingIndex ); 112 | swap( o1._event, o2._event ); 113 | swap( o1._data, o2._data ); 114 | } 115 | 116 | bool MalwareCheckResult::success( void ) const 117 | { 118 | return this->_type != MalwareCheckResult::Type::Error; 119 | } 120 | 121 | bool MalwareCheckResult::failure( void ) const 122 | { 123 | return this->_type == MalwareCheckResult::Type::Error; 124 | } 125 | 126 | void MalwareCheckResult::addToResults( const std::string & malwareCheckName, double timestamp, const std::string & data, std::vector< MalwareCheckResult > & results ) 127 | { 128 | this->malwareCheckName( malwareCheckName ); 129 | this->timestamp( timestamp ); 130 | 131 | // Replace line breaks 132 | std::string dataWithoutLineBreaks = Core::String::replace( data, "\n", " - " ); 133 | dataWithoutLineBreaks = Core::String::replace( dataWithoutLineBreaks, "\r", "" ); 134 | 135 | this->data( dataWithoutLineBreaks ); 136 | this->sortingIndex( results.size() + 1 ); 137 | 138 | results.push_back( *( this ) ); 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /src/DDNA-MalwareCheckResult.hpp: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2021, DigiDNA 3 | * All rights reserved 4 | * 5 | * This Source Code Form is subject to the terms of the MVT License, v. 1.1. 6 | * If a copy of the MVT License was not distributed with this file, you can 7 | * obtain one at https://license.mvt.re/1.1/. 8 | ******************************************************************************/ 9 | 10 | #ifndef DDNA_MALWARE_CHECK_RESULT_HPP 11 | #define DDNA_MALWARE_CHECK_RESULT_HPP 12 | 13 | #include 14 | #include 15 | 16 | #define EVENT_DEVICE_BACKUP_INFO "Device Backup" 17 | #define EVENT_BACKUP_PARSING_INFO "Backup Parsing" 18 | #define EVENT_STIX_DOWNLOAD_INFO "STIX Download" 19 | #define EVENT_EXTRACTED_FROM_BACKUP_INFO "Backup Extraction" 20 | #define EVENT_HTTP_REDIRECT_INFO "HTTP Redirect" 21 | #define EVENT_ANALYSING_ITEMS_INFO "Analysis" 22 | #define EVENT_BACKUP_FILE_NOT_FOUND "File Not Found In Backup" 23 | #define EVENT_BACKUP_EXTRACTION_ERROR "Backup Extraction Error" 24 | #define EVENT_DB_QUERY_ERROR "Database Query Error" 25 | #define EVENT_PLIST_CORRUPTED_ERROR "Property List Corrupted Error" 26 | #define EVENT_PLIST_DICT_IS_EMPTY_ERROR "Property List Dictionary Is Empty" 27 | #define EVENT_INDICATOR_MATCH_EMAIL "Indicator Match - Email" 28 | #define EVENT_INDICATOR_MATCH_DOMAIN "Indicator Match - Domain" 29 | #define EVENT_INDICATOR_MATCH_FILE "Indicator Match - File" 30 | #define EVENT_INDICATOR_MATCH_FILE_PATH "Indicator Match - File Path" 31 | #define EVENT_INDICATOR_MATCH_PROCESS "Indicator Match - Process" 32 | #define EVENT_INDICATOR_MATCH_PROFILE "Indicator Match - Profile" 33 | #define EVENT_INDICATOR_MATCH_APPID "Indicator Match - App ID" 34 | #define EVENT_INDICATOR_MATCH_URL "Indicator Match - URL" 35 | #define EVENT_SUSCPICIOUS_VALUE "Suspicious Value" 36 | #define EVENT_SUSCPICIOUS_FILENAME "Suspicious Filename" 37 | #define EVENT_SUSCPICIOUS_PROCESS "Suspicious Process" 38 | #define EVENT_SUSCPICIOUS_PROFILE "Suspicious Profile" 39 | 40 | namespace DDNA 41 | { 42 | class MalwareCheckResult 43 | { 44 | public: 45 | 46 | enum class Type: int 47 | { 48 | Analyzer, 49 | Device, 50 | Error 51 | }; 52 | 53 | enum class Severity: int 54 | { 55 | Info, 56 | Warning, 57 | Critical 58 | }; 59 | 60 | DDNA_PROPERTY_READWRITE( bool, isSuspicious ) 61 | DDNA_PROPERTY_READWRITE( Type, type ) 62 | DDNA_PROPERTY_READWRITE( Severity, severity ) 63 | DDNA_PROPERTY_READWRITE( std::string, malwareName ) 64 | DDNA_PROPERTY_READWRITE( std::string, malwareCategory ) 65 | DDNA_PROPERTY_READWRITE( std::string, malwareCheckName ) 66 | DDNA_PROPERTY_READWRITE( double, timestamp ) 67 | DDNA_PROPERTY_READWRITE( double, creationTimestamp ) 68 | DDNA_PROPERTY_READWRITE( uint64_t, sortingIndex ) 69 | DDNA_PROPERTY_READWRITE( std::string, event ) 70 | DDNA_PROPERTY_READWRITE( std::string, data ) 71 | DDNA_PROPERTY_READWRITE( Core::Error, error ) 72 | 73 | MalwareCheckResult( void ); 74 | MalwareCheckResult( const std::string & malwareName, const std::string & malwareCategory, const std::string & event ); 75 | MalwareCheckResult( const Core::Error & error, const std::string & malwareCheckName, std::vector< MalwareCheckResult > & results ); 76 | MalwareCheckResult( Type type, Severity kind, double timestamp, const std::string & malwareCheckName, const std::string & event, const std::string & data, std::vector< MalwareCheckResult > & results ); 77 | MalwareCheckResult( const MalwareCheckResult & o ); 78 | MalwareCheckResult( MalwareCheckResult && o ) noexcept; 79 | ~MalwareCheckResult(); 80 | 81 | MalwareCheckResult & operator =( MalwareCheckResult o ); 82 | 83 | friend void swap( MalwareCheckResult & o1, MalwareCheckResult & o2 ); 84 | 85 | bool success( void ) const; 86 | bool failure( void ) const; 87 | void addToResults( const std::string & malwareCheckName, double timestamp, const std::string & data, std::vector< MalwareCheckResult > & results ); 88 | }; 89 | } 90 | 91 | #endif /* DDNA_MALWARE_CHECK_RESULT_HPP */ 92 | -------------------------------------------------------------------------------- /src/DDNA-STIXIndicators.hpp: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2021, DigiDNA 3 | * All rights reserved 4 | * 5 | * This Source Code Form is subject to the terms of the MVT License, v. 1.1. 6 | * If a copy of the MVT License was not distributed with this file, you can 7 | * obtain one at https://license.mvt.re/1.1/. 8 | ******************************************************************************/ 9 | 10 | #ifndef DDNA_INDICATORS_HPP 11 | #define DDNA_INDICATORS_HPP 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | namespace DDNA 22 | { 23 | class STIXIndicators 24 | { 25 | public: 26 | static const char * const ErrorDomain; 27 | 28 | STIXIndicators(); 29 | STIXIndicators( const std::vector< std::string > & stixLocalPaths, Core::Error & error ); 30 | STIXIndicators( const STIXIndicators & o ); 31 | STIXIndicators( STIXIndicators && o ) noexcept; 32 | ~STIXIndicators(); 33 | 34 | STIXIndicators & operator =( STIXIndicators o ); 35 | 36 | friend void swap( STIXIndicators & o1, STIXIndicators & o2 ); 37 | 38 | /* { result, final (expanded) URL } */ 39 | std::tuple< MalwareCheckResult, std::string > checkURL( std::string value, const std::string & malwareCheckName, std::vector< MalwareCheckResult > & results ) const; 40 | 41 | MalwareCheckResult checkProcess( std::string value ) const; 42 | MalwareCheckResult checkEmail( std::string value ) const; 43 | MalwareCheckResult checkFileName( std::string value ) const; 44 | MalwareCheckResult checkFilePath( std::string value ) const; 45 | MalwareCheckResult checkProfile( std::string value ) const; 46 | MalwareCheckResult checkAppID( std::string value ) const; 47 | 48 | /* { value, { indicator, malware } } */ 49 | using IndicatorEntry = std::map< std::string, std::pair< CF::Dictionary, CF::Dictionary > >; 50 | 51 | DDNA_PROPERTY_READONLY( IndicatorEntry, domains ); 52 | DDNA_PROPERTY_READONLY( IndicatorEntry, processes ); 53 | DDNA_PROPERTY_READONLY( IndicatorEntry, emails ); 54 | DDNA_PROPERTY_READONLY( IndicatorEntry, files ); 55 | DDNA_PROPERTY_READONLY( IndicatorEntry, paths ); 56 | DDNA_PROPERTY_READONLY( IndicatorEntry, profiles ); 57 | DDNA_PROPERTY_READONLY( IndicatorEntry, apps ); 58 | DDNA_PROPERTY_READONLY( IndicatorEntry, filesMD5 ); 59 | DDNA_PROPERTY_READONLY( IndicatorEntry, filesSHA1 ); 60 | DDNA_PROPERTY_READONLY( IndicatorEntry, filesSHA256 ); 61 | DDNA_PROPERTY_READONLY( IndicatorEntry, appCertHashes ); 62 | DDNA_PROPERTY_READONLY( IndicatorEntry, androidPropertyNames ); 63 | DDNA_PROPERTY_READONLY( IndicatorEntry, urls ); 64 | 65 | private: 66 | 67 | bool urlIsShortened( const std::string & url ) const; 68 | std::string unshortenURL( const std::string & url, Core::Error & error ) const; 69 | void log( const std::string & message ) const; 70 | 71 | #ifdef DEBUG 72 | void test() const; 73 | #endif 74 | 75 | bool _inited; 76 | }; 77 | } 78 | 79 | #endif /* DDNA_INDICATORS_HPP */ 80 | -------------------------------------------------------------------------------- /src/MalwareChecks/DDNA-MalwareCheck-Applications.cpp: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2024, DigiDNA 3 | * All rights reserved 4 | * 5 | * This Source Code Form is subject to the terms of the MVT License, v. 1.1. 6 | * If a copy of the MVT License was not distributed with this file, you can 7 | * obtain one at https://license.mvt.re/1.1/. 8 | ******************************************************************************/ 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | #define BACKUP_DOMAIN "RootDomain" 15 | #define BACKUP_PATH "Library/Caches/Applications/clients.plist" 16 | 17 | namespace DDNA 18 | { 19 | namespace MalwareChecks 20 | { 21 | std::string Applications::name( void ) const 22 | { 23 | return "Applications"; 24 | } 25 | 26 | std::string Applications::image( void ) const 27 | { 28 | return "com.apple.AppStore"; 29 | } 30 | 31 | std::vector< iOSDataset > Applications::iOSDatasets( void ) const 32 | { 33 | return 34 | { 35 | iOSDataset::customDatasetForTemporaryExtraction 36 | ( 37 | "com.apple.Applications", 38 | { FOLDER_WILDCARD + BACKUP_PATH }, 39 | { 40 | { 41 | BACKUP_DOMAIN, 42 | { 43 | BACKUP_PATH 44 | } 45 | } 46 | } 47 | ) 48 | }; 49 | } 50 | 51 | void Applications::run( Core::SafePointer< Backup > backup, const STIXIndicators & indicators, std::vector< MalwareCheckResult > & results, Core::Progress & progress ) 52 | { 53 | progress.message( Core::L10N( "MalwareActivityDetectionChecklApplications_Info" ) ); 54 | 55 | CF::Dictionary infoPlist = backup->getInfoPlist(); 56 | CF::Dictionary applications = infoPlist.GetValue( "Applications" ); 57 | if( applications.GetCount() == 0 ) 58 | { 59 | MalwareCheckResult( MalwareCheckResult::Type::Analyzer, MalwareCheckResult::Severity::Info, 0, this->name(), EVENT_ANALYSING_ITEMS_INFO, "No applications to analyze.", results ); 60 | 61 | return; 62 | } 63 | 64 | MalwareCheckResult( MalwareCheckResult::Type::Analyzer, MalwareCheckResult::Severity::Info, 0, this->name(), EVENT_ANALYSING_ITEMS_INFO, "Analyzing " + std::to_string( applications.GetCount() ) + " applications.", results ); 65 | 66 | for( auto app: applications ) 67 | { 68 | CF::Dictionary appInfo = app.GetValue(); 69 | 70 | // Check that app has iTunesMetadata 71 | CF::Dictionary metadata = CF::Dictionary::FromPropertyListData( CF::Data( appInfo[ "iTunesMetadata" ] ) ); 72 | if( metadata.IsValid() == false ) 73 | { 74 | MalwareCheckResult 75 | ( 76 | MalwareCheckResult::Type::Analyzer, 77 | MalwareCheckResult::Severity::Warning, 78 | 0, 79 | this->name(), 80 | EVENT_SUSCPICIOUS_VALUE, 81 | "Found an application without iTunesMetadata", 82 | results 83 | ); 84 | 85 | continue; 86 | } 87 | 88 | // Check that iTunesMetadata has a softwareVersionBundleId 89 | std::string softwareVersionBundleId = CF::String( metadata[ "softwareVersionBundleId" ] ); 90 | if( softwareVersionBundleId.empty() ) 91 | { 92 | MalwareCheckResult 93 | ( 94 | MalwareCheckResult::Type::Analyzer, 95 | MalwareCheckResult::Severity::Critical, 96 | 0, 97 | this->name(), 98 | EVENT_SUSCPICIOUS_VALUE, 99 | "Suspicious application identified without softwareVersionBundleId in iTunesMetadata", 100 | results 101 | ); 102 | 103 | continue; 104 | } 105 | 106 | // Check softwareVersionBundleId against process indicators 107 | MalwareCheckResult res = indicators.checkProcess( softwareVersionBundleId ); 108 | if( res.isSuspicious() ) 109 | { 110 | res.addToResults 111 | ( 112 | this->name(), 113 | 0, 114 | "Malicious application " + softwareVersionBundleId + " identified", 115 | results 116 | ); 117 | } 118 | 119 | // Check source app 120 | std::string sourceApp = CF::String( metadata[ "sourceApp" ] ); 121 | if( sourceApp.empty() && metadata.ContainsKey( "distributorInfo" ) ) 122 | { 123 | CF::Dictionary distributorInfo = CF::Dictionary( metadata[ "distributorInfo" ] ); 124 | sourceApp = CF::String( distributorInfo[ "distributorID" ] ); 125 | } 126 | 127 | if( sourceApp.empty() == false ) 128 | { 129 | std::vector< std::string > legits = 130 | { 131 | "com.apple.AppStore", 132 | "com.apple.AppStore.ProductPageExtension", 133 | "com.apple.dmd", 134 | "dmd" 135 | }; 136 | 137 | bool match = false; 138 | for( std::string legit: legits ) 139 | { 140 | if( legit == sourceApp ) 141 | { 142 | match = true; 143 | 144 | break; 145 | } 146 | } 147 | 148 | if( match == false ) 149 | { 150 | MalwareCheckResult 151 | ( 152 | MalwareCheckResult::Type::Analyzer, 153 | MalwareCheckResult::Severity::Warning, 154 | 0, 155 | this->name(), 156 | EVENT_SUSCPICIOUS_VALUE, 157 | "App not installed from the App Store or a MDM " + MalwareActivityDetectionHelper::AppName( softwareVersionBundleId ) + " (" + softwareVersionBundleId + ")", 158 | results 159 | ); 160 | } 161 | } 162 | else 163 | { 164 | MalwareCheckResult 165 | ( 166 | MalwareCheckResult::Type::Analyzer, 167 | MalwareCheckResult::Severity::Warning, 168 | 0, 169 | this->name(), 170 | EVENT_SUSCPICIOUS_VALUE, 171 | "App not installed from the App Store or a MDM " + MalwareActivityDetectionHelper::AppName( softwareVersionBundleId ) + " (" + softwareVersionBundleId + ")", 172 | results 173 | ); 174 | } 175 | } 176 | } 177 | } 178 | } 179 | -------------------------------------------------------------------------------- /src/MalwareChecks/DDNA-MalwareCheck-Applications.hpp: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2024, DigiDNA 3 | * All rights reserved 4 | * 5 | * This Source Code Form is subject to the terms of the MVT License, v. 1.1. 6 | * If a copy of the MVT License was not distributed with this file, you can 7 | * obtain one at https://license.mvt.re/1.1/. 8 | ******************************************************************************/ 9 | 10 | #ifndef DDNA_MALWARECHECK_APPLICATIONS_HPP 11 | #define DDNA_MALWARECHECK_APPLICATIONS_HPP 12 | 13 | #include 14 | 15 | namespace DDNA 16 | { 17 | namespace MalwareChecks 18 | { 19 | class Applications: public MalwareCheck 20 | { 21 | public: 22 | 23 | std::string name( void ) const override; 24 | std::string image( void ) const override; 25 | std::vector< iOSDataset > iOSDatasets( void ) const override; 26 | void run( Core::SafePointer< Backup > backup, const STIXIndicators & indicators, std::vector< MalwareCheckResult > & results, Core::Progress & progress ) override; 27 | }; 28 | } 29 | } 30 | #endif /* DDNA_MALWARECHECK_APPLICATIONS_HPP */ 31 | -------------------------------------------------------------------------------- /src/MalwareChecks/DDNA-MalwareCheck-Calendar.cpp: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2021, DigiDNA 3 | * All rights reserved 4 | * 5 | * This Source Code Form is subject to the terms of the MVT License, v. 1.1. 6 | * If a copy of the MVT License was not distributed with this file, you can 7 | * obtain one at https://license.mvt.re/1.1/. 8 | ******************************************************************************/ 9 | 10 | #include 11 | #include 12 | 13 | #define BACKUP_DOMAIN "HomeDomain" 14 | #define BACKUP_PATH "Library/Calendar/Calendar.sqlitedb" 15 | 16 | namespace DDNA 17 | { 18 | namespace MalwareChecks 19 | { 20 | std::string Calendar::name( void ) const 21 | { 22 | return "Calendar"; 23 | } 24 | 25 | std::string Calendar::image( void ) const 26 | { 27 | return "com.apple.mobilecal"; 28 | } 29 | 30 | std::vector< iOSDataset > Calendar::iOSDatasets( void ) const 31 | { 32 | return 33 | { 34 | iOSDataset::customDatasetForTemporaryExtraction 35 | ( 36 | "com.apple.mobilecal.calendars", 37 | { FOLDER_WILDCARD + BACKUP_PATH }, 38 | { 39 | { 40 | BACKUP_DOMAIN, 41 | { 42 | BACKUP_PATH 43 | } 44 | } 45 | } 46 | ) 47 | }; 48 | } 49 | 50 | void Calendar::run( Core::SafePointer< Backup > backup, const STIXIndicators & indicators, std::vector< MalwareCheckResult > & results, Core::Progress & progress ) 51 | { 52 | progress.message( Core::L10N( "MalwareActivityDetectionCheckCalendar_Info" ) ); 53 | 54 | std::string domain = BACKUP_DOMAIN; 55 | std::string path = BACKUP_PATH; 56 | std::shared_ptr< sqlite3 > db = MalwareActivityDetectionHelper::ExtractAndOpenSQLiteDB( backup, domain, path, this->name(), results ); 57 | if( db == nullptr ) 58 | { 59 | return; 60 | } 61 | 62 | SQLite::QueryDictionary queryDict = db.get(); 63 | std::string query = 64 | R"query( 65 | 66 | SELECT 67 | CalendarItem.ROWID as id, 68 | CalendarItem.summary as summary, 69 | CalendarItem.description as description, 70 | CalendarItem.start_date as start_date, 71 | CalendarItem.end_date as end_date, 72 | CalendarItem.all_day as all_day, 73 | CalendarItem.calendar_id as calendar_id, 74 | CalendarItem.organizer_id as organizer_id, 75 | CalendarItem.url as url, 76 | CalendarItem.last_modified as last_modified, 77 | CalendarItem.external_id as external_id, 78 | CalendarItem.external_mod_tag as external_mod_tag, 79 | CalendarItem.unique_identifier as unique_identifier, 80 | CalendarItem.hidden as hidden, 81 | CalendarItem.UUID as uuid, 82 | CalendarItem.creation_date as creation_date, 83 | CalendarItem.action as action, 84 | CalendarItem.created_by_id as created_by_id, 85 | Participant.UUID as participant_uuid, 86 | Participant.email as participant_email, 87 | Participant.phone_number as participant_phone, 88 | Participant.comment as participant_comment, 89 | Participant.last_modified as participant_last_modified 90 | FROM CalendarItem 91 | LEFT JOIN Participant ON Participant.ROWID = CalendarItem.organizer_id; 92 | 93 | )query"; 94 | 95 | std::vector< CF::Dictionary > records = queryDict.executeQuery( query ); 96 | if( records.size() == 0 ) 97 | { 98 | if( queryDict.errorMessage().length() > 0 ) 99 | { 100 | MalwareCheckResult( MalwareCheckResult::Type::Analyzer, MalwareCheckResult::Severity::Warning, 0, this->name(), EVENT_DB_QUERY_ERROR, "'" + domain + "/" + path + "' cannot be parsed (" + queryDict.errorMessage() + ").", results ); 101 | } 102 | else 103 | { 104 | MalwareCheckResult( MalwareCheckResult::Type::Analyzer, MalwareCheckResult::Severity::Info, 0, this->name(), EVENT_ANALYSING_ITEMS_INFO, "No calendar item to analyze.", results ); 105 | } 106 | 107 | return; 108 | } 109 | 110 | MalwareCheckResult( MalwareCheckResult::Type::Analyzer, MalwareCheckResult::Severity::Info, 0, this->name(), EVENT_ANALYSING_ITEMS_INFO, "Analyzing " + std::to_string( records.size() ) + " calendar items.", results ); 111 | 112 | for( const auto & record: records ) 113 | { 114 | std::string uuid = CF::String( record[ "uuid" ] ); 115 | std::string email = CF::String( record[ "participant_email" ] ); 116 | std::string summary = CF::String( record[ "summary" ] ); 117 | std::string description = CF::String( record[ "description" ] ); 118 | CF::Number creationDate = record[ "creation_date" ]; 119 | double timestamp = MalwareActivityDetectionHelper::AppleTimestampToUnixTimestamp( creationDate.GetDoubleValue() ); 120 | 121 | MalwareCheckResult res = indicators.checkEmail( email ); 122 | if( res.isSuspicious() ) 123 | { 124 | res.addToResults 125 | ( 126 | this->name(), 127 | timestamp, 128 | "Calendar item '" + summary + "' with a suspicious email address: '" + email + "'.", 129 | results 130 | ); 131 | } 132 | 133 | // Custom check for Quadream exploit 134 | if( summary == "Meeting" && description == "Notes" ) 135 | { 136 | MalwareCheckResult 137 | { 138 | MalwareCheckResult::Type::Device, 139 | MalwareCheckResult::Severity::Critical, 140 | timestamp, 141 | this->name(), 142 | EVENT_SUSCPICIOUS_VALUE, 143 | "Potential Quadream exploit event identified: " + uuid, 144 | results 145 | }; 146 | } 147 | } 148 | } 149 | } 150 | } 151 | -------------------------------------------------------------------------------- /src/MalwareChecks/DDNA-MalwareCheck-Calendar.hpp: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2021, DigiDNA 3 | * All rights reserved 4 | * 5 | * This Source Code Form is subject to the terms of the MVT License, v. 1.1. 6 | * If a copy of the MVT License was not distributed with this file, you can 7 | * obtain one at https://license.mvt.re/1.1/. 8 | ******************************************************************************/ 9 | 10 | #ifndef DDNA_MALWARE_CHECK_CALENDAR_HPP 11 | #define DDNA_MALWARE_CHECK_CALENDAR_HPP 12 | 13 | #include 14 | 15 | namespace DDNA 16 | { 17 | namespace MalwareChecks 18 | { 19 | class Calendar: public MalwareCheck 20 | { 21 | public: 22 | 23 | std::string name( void ) const override; 24 | std::string image( void ) const override; 25 | std::vector< iOSDataset > iOSDatasets( void ) const override; 26 | void run( Core::SafePointer< Backup > backup, const STIXIndicators & indicators, std::vector< MalwareCheckResult > & results, Core::Progress & progress ) override; 27 | }; 28 | } 29 | } 30 | 31 | #endif /* DDNA_MALWARE_CHECK_CALENDAR_HPP */ 32 | -------------------------------------------------------------------------------- /src/MalwareChecks/DDNA-MalwareCheck-CallHistory.cpp: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2021, DigiDNA 3 | * All rights reserved 4 | * 5 | * This Source Code Form is subject to the terms of the MVT License, v. 1.1. 6 | * If a copy of the MVT License was not distributed with this file, you can 7 | * obtain one at https://license.mvt.re/1.1/. 8 | ******************************************************************************/ 9 | 10 | #include 11 | #include 12 | 13 | #define BACKUP_DOMAIN "HomeDomain" 14 | #define BACKUP_PATH "Library/CallHistoryDB/CallHistory.storedata" 15 | #define BACKUP_DOMAIN_OLD "WirelessDomain" 16 | #define BACKUP_PATH_OLD "Library/CallHistory/call_history.db" 17 | 18 | namespace DDNA 19 | { 20 | namespace MalwareChecks 21 | { 22 | std::string CallHistory::name( void ) const 23 | { 24 | return "Call History"; 25 | } 26 | 27 | std::string CallHistory::image( void ) const 28 | { 29 | return "com.apple.mobilephone"; 30 | } 31 | 32 | std::vector< iOSDataset > CallHistory::iOSDatasets( void ) const 33 | { 34 | return 35 | { 36 | iOSDataset::customDatasetForTemporaryExtraction 37 | ( 38 | "com.apple.mobilephone.history", 39 | { 40 | FOLDER_WILDCARD + BACKUP_PATH, 41 | FOLDER_WILDCARD + BACKUP_PATH_OLD 42 | }, 43 | { 44 | { 45 | BACKUP_DOMAIN, 46 | { 47 | BACKUP_PATH 48 | } 49 | }, 50 | { 51 | BACKUP_DOMAIN_OLD, 52 | { 53 | BACKUP_PATH_OLD 54 | } 55 | } 56 | } 57 | ) 58 | }; 59 | } 60 | 61 | void CallHistory::run( Core::SafePointer< Backup > backup, const STIXIndicators & indicators, std::vector< MalwareCheckResult > & results, Core::Progress & progress ) 62 | { 63 | progress.message( Core::L10N( "MalwareActivityDetectionCheckCallHistory_Info" ) ); 64 | 65 | // Since iOS 13, Call History is not accessible without encryption 66 | if( backup->iOSMajorVersion() > 12 && !backup->encrypted() ) 67 | { 68 | MalwareCheckResult( MalwareCheckResult::Type::Analyzer, MalwareCheckResult::Severity::Warning, 0, this->name(), EVENT_BACKUP_FILE_NOT_FOUND, std::string( BACKUP_PATH ) + " is only available when backup encryption is enabled.", results ); 69 | 70 | return; 71 | } 72 | 73 | std::string domain = ( backup->iOSMajorVersion() > 7 ) ? BACKUP_DOMAIN : BACKUP_DOMAIN_OLD; 74 | std::string path = ( backup->iOSMajorVersion() > 7 ) ? BACKUP_PATH : BACKUP_PATH_OLD; 75 | std::shared_ptr< sqlite3 > db = MalwareActivityDetectionHelper::ExtractAndOpenSQLiteDB( backup, domain, path, this->name(), results ); 76 | if( db == nullptr ) 77 | { 78 | return; 79 | } 80 | 81 | SQLite::QueryDictionary queryDict = db.get(); 82 | 83 | std::string query; 84 | if( backup->iOSMajorVersion() > 7 ) 85 | { 86 | query = "SELECT ZDATE as date, ZADDRESS as address FROM ZCALLRECORD"; 87 | } 88 | else 89 | { 90 | query = "SELECT date, address FROM call"; 91 | } 92 | 93 | queryDict.addTransformerForColumn( "date", SQLite::TransformTimestampAppleSecondsToUnix ); 94 | 95 | std::vector< CF::Dictionary > records = queryDict.executeQuery( query ); 96 | if( records.size() == 0 ) 97 | { 98 | if( queryDict.errorMessage().length() > 0 ) 99 | { 100 | MalwareCheckResult( MalwareCheckResult::Type::Analyzer, MalwareCheckResult::Severity::Warning, 0, this->name(), EVENT_DB_QUERY_ERROR, "'" + domain + "/" + path + "' cannot be parsed (" + queryDict.errorMessage() + ").", results ); 101 | } 102 | else 103 | { 104 | MalwareCheckResult( MalwareCheckResult::Type::Analyzer, MalwareCheckResult::Severity::Info, 0, this->name(), EVENT_ANALYSING_ITEMS_INFO, "No calls to analyze.", results ); 105 | } 106 | 107 | return; 108 | } 109 | 110 | MalwareCheckResult( MalwareCheckResult::Type::Analyzer, MalwareCheckResult::Severity::Info, 0, this->name(), EVENT_ANALYSING_ITEMS_INFO, "Analyzing " + std::to_string( records.size() ) + " calls.", results ); 111 | 112 | for( const auto & record: records ) 113 | { 114 | std::string contact = CF::String( record[ "address" ] ); 115 | 116 | if( contact.empty() ) 117 | { 118 | CF::Data address = record[ "address" ]; 119 | contact = CF::AutoPointer( CFStringCreateWithBytes( nullptr, address.GetBytePtr(), address.GetLength(), kCFStringEncodingUTF8, false ) ).As< CF::String >(); 120 | } 121 | 122 | if( contact.find( "@" ) == std::string::npos ) 123 | { 124 | continue; 125 | } 126 | 127 | MalwareCheckResult res = indicators.checkEmail( contact ); 128 | if( res.isSuspicious() ) 129 | { 130 | double timestamp = CF::Number( record[ "date" ] ); 131 | 132 | res.addToResults 133 | ( 134 | this->name(), 135 | timestamp, 136 | "Call record with a suspicious email address: '" + contact + "'.", 137 | results 138 | ); 139 | } 140 | } 141 | } 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /src/MalwareChecks/DDNA-MalwareCheck-CallHistory.hpp: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2021, DigiDNA 3 | * All rights reserved 4 | * 5 | * This Source Code Form is subject to the terms of the MVT License, v. 1.1. 6 | * If a copy of the MVT License was not distributed with this file, you can 7 | * obtain one at https://license.mvt.re/1.1/. 8 | ******************************************************************************/ 9 | 10 | #ifndef DDNA_MALWARE_CHECK_CALL_HISTORY_HPP 11 | #define DDNA_MALWARE_CHECK_CALL_HISTORY_HPP 12 | 13 | #include 14 | 15 | namespace DDNA 16 | { 17 | namespace MalwareChecks 18 | { 19 | class CallHistory: public MalwareCheck 20 | { 21 | public: 22 | 23 | std::string name( void ) const override; 24 | std::string image( void ) const override; 25 | std::vector< iOSDataset > iOSDatasets( void ) const override; 26 | void run( Core::SafePointer< Backup > backup, const STIXIndicators & indicators, std::vector< MalwareCheckResult > & results, Core::Progress & progress ) override; 27 | }; 28 | } 29 | } 30 | 31 | #endif /* DDNA_MALWARE_CHECK_CALL_HISTORY_HPP */ 32 | -------------------------------------------------------------------------------- /src/MalwareChecks/DDNA-MalwareCheck-ChromeFavicon.cpp: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2021, DigiDNA 3 | * All rights reserved 4 | * 5 | * This Source Code Form is subject to the terms of the MVT License, v. 1.1. 6 | * If a copy of the MVT License was not distributed with this file, you can 7 | * obtain one at https://license.mvt.re/1.1/. 8 | ******************************************************************************/ 9 | 10 | #include 11 | #include 12 | 13 | #define BACKUP_DOMAIN "AppDomain-com.google.chrome.ios" 14 | #define BACKUP_PATH "Library/Application Support/Google/Chrome/Default/Favicons" 15 | 16 | namespace DDNA 17 | { 18 | namespace MalwareChecks 19 | { 20 | std::string ChromeFavicon::name( void ) const 21 | { 22 | return "Chrome Favicon"; 23 | } 24 | 25 | std::string ChromeFavicon::image( void ) const 26 | { 27 | return MalwareActivityDetectionHelper::AppIconPath( "com.google.chrome.ios" ); 28 | } 29 | 30 | std::vector< iOSDataset > ChromeFavicon::iOSDatasets( void ) const 31 | { 32 | return 33 | { 34 | iOSDataset::customDatasetForTemporaryExtraction 35 | ( 36 | "com.google.chrome.ios.favicon", 37 | { FOLDER_WILDCARD + BACKUP_PATH }, 38 | { 39 | { 40 | BACKUP_DOMAIN, 41 | { 42 | BACKUP_PATH 43 | } 44 | } 45 | } 46 | ) 47 | }; 48 | } 49 | 50 | void ChromeFavicon::run( Core::SafePointer< Backup > backup, const STIXIndicators & indicators, std::vector< MalwareCheckResult > & results, Core::Progress & progress ) 51 | { 52 | progress.message( Core::L10N( "MalwareActivityDetectionCheckChromeFavicon_Info" ) ); 53 | 54 | if( !backup->mbdbRecordExists( BACKUP_DOMAIN, BACKUP_PATH ) ) 55 | { 56 | return; 57 | } 58 | 59 | std::string domain = BACKUP_DOMAIN; 60 | std::string path = BACKUP_PATH; 61 | std::shared_ptr< sqlite3 > db = MalwareActivityDetectionHelper::ExtractAndOpenSQLiteDB( backup, domain, path, this->name(), results ); 62 | if( db == nullptr ) 63 | { 64 | return; 65 | } 66 | 67 | SQLite::QueryDictionary queryDict = db.get(); 68 | std::string query = 69 | R"query( 70 | 71 | SELECT 72 | icon_mapping.page_url, 73 | favicons.url, 74 | favicon_bitmaps.last_updated, 75 | favicon_bitmaps.last_requested 76 | FROM icon_mapping 77 | JOIN favicon_bitmaps ON icon_mapping.icon_id = favicon_bitmaps.icon_id 78 | JOIN favicons ON icon_mapping.icon_id = favicons.id 79 | ORDER BY icon_mapping.id; 80 | 81 | )query"; 82 | 83 | std::vector< CF::Dictionary > records = queryDict.executeQuery( query ); 84 | if( records.size() == 0 ) 85 | { 86 | if( queryDict.errorMessage().length() > 0 ) 87 | { 88 | MalwareCheckResult( MalwareCheckResult::Type::Analyzer, MalwareCheckResult::Severity::Warning, 0, this->name(), EVENT_DB_QUERY_ERROR, "'" + domain + "/" + path + "' cannot be parsed (" + queryDict.errorMessage() + ").", results ); 89 | } 90 | else 91 | { 92 | MalwareCheckResult( MalwareCheckResult::Type::Analyzer, MalwareCheckResult::Severity::Info, 0, this->name(), EVENT_ANALYSING_ITEMS_INFO, "No Chrome favicons to analyze.", results ); 93 | } 94 | 95 | return; 96 | } 97 | 98 | MalwareCheckResult( MalwareCheckResult::Type::Analyzer, MalwareCheckResult::Severity::Info, 0, this->name(), EVENT_ANALYSING_ITEMS_INFO, "Analyzing " + std::to_string( records.size() ) + " Chrome favicons.", results ); 99 | 100 | for( const auto & record: records ) 101 | { 102 | for( const auto & url: std::vector< std::string > { CF::String( record[ "page_url" ] ), CF::String( record[ "url" ] ) } ) 103 | { 104 | auto check = indicators.checkURL( url, this->name(), results ); 105 | 106 | auto res = std::get< 0 >( check ); 107 | if( res.isSuspicious() ) 108 | { 109 | auto finalURL = std::get< 1 >( check ); 110 | 111 | int64_t requested = CF::Number( record[ "last_requested" ] ); 112 | int64_t updated = CF::Number( record[ "last_updated" ] ); 113 | int64_t ts = ( updated != 0 ) ? updated : requested; 114 | Core::DateTime dt = Core::DateTime( ts / 1000000, Core::DateTime::Epoch::Windows ); 115 | 116 | res.addToResults 117 | ( 118 | this->name(), 119 | dt.as( Core::DateTime::Epoch::Unix ), 120 | "Chrome visit to '" + finalURL + "'.", 121 | results 122 | ); 123 | } 124 | } 125 | } 126 | 127 | } 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /src/MalwareChecks/DDNA-MalwareCheck-ChromeFavicon.hpp: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2021, DigiDNA 3 | * All rights reserved 4 | * 5 | * This Source Code Form is subject to the terms of the MVT License, v. 1.1. 6 | * If a copy of the MVT License was not distributed with this file, you can 7 | * obtain one at https://license.mvt.re/1.1/. 8 | ******************************************************************************/ 9 | 10 | #ifndef DDNA_MALWARE_CHECK_CHROME_FAVICON_HPP 11 | #define DDNA_MALWARE_CHECK_CHROME_FAVICON_HPP 12 | 13 | #include 14 | 15 | namespace DDNA 16 | { 17 | namespace MalwareChecks 18 | { 19 | class ChromeFavicon: public MalwareCheck 20 | { 21 | public: 22 | 23 | std::string name( void ) const override; 24 | std::string image( void ) const override; 25 | std::vector< iOSDataset > iOSDatasets( void ) const override; 26 | void run( Core::SafePointer< Backup > backup, const STIXIndicators & indicators, std::vector< MalwareCheckResult > & results, Core::Progress & progress ) override; 27 | }; 28 | } 29 | } 30 | 31 | #endif /* DDNA_MALWARE_CHECK_CHROME_FAVICON_HPP */ 32 | -------------------------------------------------------------------------------- /src/MalwareChecks/DDNA-MalwareCheck-ChromeHistory.cpp: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2021, DigiDNA 3 | * All rights reserved 4 | * 5 | * This Source Code Form is subject to the terms of the MVT License, v. 1.1. 6 | * If a copy of the MVT License was not distributed with this file, you can 7 | * obtain one at https://license.mvt.re/1.1/. 8 | ******************************************************************************/ 9 | 10 | #include 11 | #include 12 | 13 | #define BACKUP_DOMAIN "AppDomain-com.google.chrome.ios" 14 | #define BACKUP_PATH "Library/Application Support/Google/Chrome/Default/History" 15 | 16 | namespace DDNA 17 | { 18 | namespace MalwareChecks 19 | { 20 | std::string ChromeHistory::name( void ) const 21 | { 22 | return "Chrome History"; 23 | } 24 | 25 | std::string ChromeHistory::image( void ) const 26 | { 27 | return MalwareActivityDetectionHelper::AppIconPath( "com.google.chrome.ios" ); 28 | } 29 | 30 | std::vector< iOSDataset > ChromeHistory::iOSDatasets( void ) const 31 | { 32 | return 33 | { 34 | iOSDataset::customDatasetForTemporaryExtraction 35 | ( 36 | "com.google.chrome.ios.history", 37 | { FOLDER_WILDCARD + BACKUP_PATH }, 38 | { 39 | { 40 | BACKUP_DOMAIN, 41 | { 42 | BACKUP_PATH 43 | } 44 | } 45 | } 46 | ) 47 | }; 48 | } 49 | 50 | void ChromeHistory::run( Core::SafePointer< Backup > backup, const STIXIndicators & indicators, std::vector< MalwareCheckResult > & results, Core::Progress & progress ) 51 | { 52 | progress.message( Core::L10N( "MalwareActivityDetectionCheckChromeHistory_Info" ) ); 53 | 54 | if( !backup->mbdbRecordExists( BACKUP_DOMAIN, BACKUP_PATH ) ) 55 | { 56 | return; 57 | } 58 | 59 | std::string domain = BACKUP_DOMAIN; 60 | std::string path = BACKUP_PATH; 61 | std::shared_ptr< sqlite3 > db = MalwareActivityDetectionHelper::ExtractAndOpenSQLiteDB( backup, domain, path, this->name(), results ); 62 | if( db == nullptr ) 63 | { 64 | return; 65 | } 66 | 67 | SQLite::QueryDictionary queryDict = db.get(); 68 | std::string query = 69 | R"query( 70 | 71 | SELECT 72 | urls.id, 73 | urls.url, 74 | visits.id, 75 | visits.visit_time, 76 | visits.from_visit 77 | FROM urls 78 | JOIN visits ON visits.url = urls.id 79 | ORDER BY visits.visit_time 80 | 81 | )query"; 82 | 83 | std::vector< CF::Dictionary > records = queryDict.executeQuery( query ); 84 | if( records.size() == 0 ) 85 | { 86 | if( queryDict.errorMessage().length() > 0 ) 87 | { 88 | MalwareCheckResult( MalwareCheckResult::Type::Analyzer, MalwareCheckResult::Severity::Warning, 0, this->name(), EVENT_DB_QUERY_ERROR, "'" + domain + "/" + path + "' cannot be parsed (" + queryDict.errorMessage() + ").", results ); 89 | } 90 | else 91 | { 92 | MalwareCheckResult( MalwareCheckResult::Type::Analyzer, MalwareCheckResult::Severity::Info, 0, this->name(), EVENT_ANALYSING_ITEMS_INFO, "No Chrome history entries to analyze.", results ); 93 | } 94 | 95 | return; 96 | } 97 | 98 | MalwareCheckResult( MalwareCheckResult::Type::Analyzer, MalwareCheckResult::Severity::Info, 0, this->name(), EVENT_ANALYSING_ITEMS_INFO, "Analyzing " + std::to_string( records.size() ) + " Chrome history entries.", results ); 99 | 100 | for( const auto & record: records ) 101 | { 102 | std::string url = CF::String( record[ "url" ] ); 103 | auto check = indicators.checkURL( url, this->name(), results ); 104 | 105 | auto res = std::get< 0 >( check ); 106 | if( res.isSuspicious() ) 107 | { 108 | auto finalURL = std::get< 1 >( check ); 109 | int64_t timestamp = CF::Number( record[ "visit_time" ] ); 110 | Core::DateTime dateTime = Core::DateTime( timestamp / 1000000, Core::DateTime::Epoch::Windows ); 111 | 112 | res.addToResults 113 | ( 114 | this->name(), 115 | dateTime.as( Core::DateTime::Epoch::Unix ), 116 | "Chrome visit to '" + finalURL + "'.", 117 | results 118 | ); 119 | } 120 | } 121 | } 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /src/MalwareChecks/DDNA-MalwareCheck-ChromeHistory.hpp: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2021, DigiDNA 3 | * All rights reserved 4 | * 5 | * This Source Code Form is subject to the terms of the MVT License, v. 1.1. 6 | * If a copy of the MVT License was not distributed with this file, you can 7 | * obtain one at https://license.mvt.re/1.1/. 8 | ******************************************************************************/ 9 | 10 | #ifndef DDNA_MALWARE_CHECK_CHROME_HISTORY_HPP 11 | #define DDNA_MALWARE_CHECK_CHROME_HISTORY_HPP 12 | 13 | #include 14 | 15 | namespace DDNA 16 | { 17 | namespace MalwareChecks 18 | { 19 | class ChromeHistory: public MalwareCheck 20 | { 21 | public: 22 | 23 | std::string name( void ) const override; 24 | std::string image( void ) const override; 25 | std::vector< iOSDataset > iOSDatasets( void ) const override; 26 | void run( Core::SafePointer< Backup > backup, const STIXIndicators & indicators, std::vector< MalwareCheckResult > & results, Core::Progress & progress ) override; 27 | }; 28 | } 29 | } 30 | 31 | #endif /* DDNA_MALWARE_CHECK_CHROME_HISTORY_HPP */ 32 | -------------------------------------------------------------------------------- /src/MalwareChecks/DDNA-MalwareCheck-ConfigurationProfiles.cpp: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2021, DigiDNA 3 | * All rights reserved 4 | * 5 | * This Source Code Form is subject to the terms of the MVT License, v. 1.1. 6 | * If a copy of the MVT License was not distributed with this file, you can 7 | * obtain one at https://license.mvt.re/1.1/. 8 | ******************************************************************************/ 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | #define BACKUP_DOMAIN "SysSharedContainerDomain-systemgroup.com.apple.configurationprofiles" 15 | 16 | namespace DDNA 17 | { 18 | namespace MalwareChecks 19 | { 20 | std::string ConfigurationProfiles::name() const 21 | { 22 | return "Configuration Profiles"; 23 | } 24 | 25 | std::string ConfigurationProfiles::image() const 26 | { 27 | return "ConfigurationProfile"; 28 | } 29 | 30 | std::vector< iOSDataset > ConfigurationProfiles::iOSDatasets( void ) const 31 | { 32 | return 33 | { 34 | iOSDataset::customDatasetForTemporaryExtraction 35 | ( 36 | "com.apple.configurationprofiles.profiles", 37 | { 38 | FOLDER_WILDCARD + "profile-*" 39 | }, 40 | { 41 | { 42 | BACKUP_DOMAIN, 43 | { 44 | "Library/ConfigurationProfiles/profile-*" 45 | } 46 | } 47 | } 48 | ) 49 | }; 50 | } 51 | 52 | void ConfigurationProfiles::run( Core::SafePointer< Backup > backup, const STIXIndicators & indicators, std::vector< MalwareCheckResult > & results, Core::Progress & progress ) 53 | { 54 | progress.message( Core::L10N( "MalwareActivityDetectionCheckConfigurationProfiles_Info" ) ); 55 | 56 | Core::SafePointer< MBDBFile > mbdb = backup->mbdbFile(); 57 | if( mbdb.isNull() ) 58 | { 59 | return; 60 | } 61 | 62 | std::vector< std::pair< CF::Dictionary, int64_t > > profiles; 63 | 64 | for( const auto & record: mbdb->recordsWithDomain( BACKUP_DOMAIN ) ) 65 | { 66 | if( record->fileName().find( "profile-" ) == 0 ) 67 | { 68 | Core::Error error; 69 | CF::Dictionary profile = MalwareActivityDetectionHelper::ExtractPropertyList( backup, BACKUP_DOMAIN, record->path(), false, this->name(), results, error ); 70 | if( error.success() ) 71 | { 72 | profiles.push_back( { profile, record->lastModified() } ); 73 | } 74 | } 75 | } 76 | 77 | if( profiles.size() == 0 ) 78 | { 79 | MalwareCheckResult( MalwareCheckResult::Type::Analyzer, MalwareCheckResult::Severity::Info, 0, this->name(), EVENT_ANALYSING_ITEMS_INFO, "No configuration profile to analyze.", results ); 80 | 81 | return; 82 | } 83 | 84 | MalwareCheckResult( MalwareCheckResult::Type::Analyzer, MalwareCheckResult::Severity::Info, 0, this->name(), EVENT_ANALYSING_ITEMS_INFO, "Analyzing " + std::to_string( profiles.size() ) + " configuration profiles.", results ); 85 | 86 | for( const auto & p: profiles ) 87 | { 88 | CF::Dictionary profile = p.first; 89 | std::string uuid = CF::String( profile.GetValue( "PayloadUUID" ) ); 90 | std::string name = CF::String( profile.GetValue( "PayloadDisplayName" ) ); 91 | 92 | MalwareCheckResult res = indicators.checkProfile( uuid ); 93 | if( res.isSuspicious() ) 94 | { 95 | res.addToResults 96 | ( 97 | this->name(), 98 | p.second, 99 | "Found a known malicious configuration profile '" + name + "' with UUID " + uuid, 100 | results 101 | ); 102 | } 103 | 104 | CF::Array payloads = profile.GetValue( "PayloadContent" ); 105 | for( const CF::Dictionary & payload: payloads ) 106 | { 107 | std::string payloadType = CF::String( payload[ "PayloadType" ] ); 108 | if( payloadType.find( "com.apple.notificationsettings" ) != std::string::npos ) 109 | { 110 | MalwareCheckResult 111 | ( 112 | MalwareCheckResult::Type::Device, 113 | MalwareCheckResult::Severity::Warning, 114 | p.second, 115 | this->name(), 116 | EVENT_SUSCPICIOUS_PROFILE, 117 | "Found a potentially suspicious configuration profile '" + name + "' with payload type " + payloadType, 118 | results 119 | ); 120 | } 121 | } 122 | } 123 | } 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /src/MalwareChecks/DDNA-MalwareCheck-ConfigurationProfiles.hpp: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2021, DigiDNA 3 | * All rights reserved 4 | * 5 | * This Source Code Form is subject to the terms of the MVT License, v. 1.1. 6 | * If a copy of the MVT License was not distributed with this file, you can 7 | * obtain one at https://license.mvt.re/1.1/. 8 | ******************************************************************************/ 9 | 10 | #ifndef DDNA_MALWARE_CHECK_CONFIGURATION_PROFILES_HPP 11 | #define DDNA_MALWARE_CHECK_CONFIGURATION_PROFILES_HPP 12 | 13 | #include 14 | 15 | namespace DDNA 16 | { 17 | namespace MalwareChecks 18 | { 19 | class ConfigurationProfiles: public MalwareCheck 20 | { 21 | public: 22 | 23 | std::string name( void ) const override; 24 | std::string image( void ) const override; 25 | std::vector< iOSDataset > iOSDatasets( void ) const override; 26 | void run( Core::SafePointer< Backup > backup, const STIXIndicators & indicators, std::vector< MalwareCheckResult > & results, Core::Progress & progress ) override; 27 | }; 28 | } 29 | } 30 | 31 | #endif /* DDNA_MALWARE_CHECK_CONFIGURATION_PROFILES_HPP */ 32 | -------------------------------------------------------------------------------- /src/MalwareChecks/DDNA-MalwareCheck-Contacts.cpp: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2024, DigiDNA 3 | * All rights reserved 4 | * 5 | * This Source Code Form is subject to the terms of the MVT License, v. 1.1. 6 | * If a copy of the MVT License was not distributed with this file, you can 7 | * obtain one at https://license.mvt.re/1.1/. 8 | ******************************************************************************/ 9 | 10 | #include 11 | #include 12 | 13 | #define BACKUP_DOMAIN "HomeDomain" 14 | #define BACKUP_PATH "Library/AddressBook/AddressBook.sqlitedb" 15 | 16 | namespace DDNA 17 | { 18 | namespace MalwareChecks 19 | { 20 | std::string Contacts::name( void ) const 21 | { 22 | return "Contacts"; 23 | } 24 | 25 | std::string Contacts::image( void ) const 26 | { 27 | return "com.apple.MobileAddressBook"; 28 | } 29 | 30 | std::vector< iOSDataset > Contacts::iOSDatasets( void ) const 31 | { 32 | return 33 | { 34 | iOSDataset::customDatasetForTemporaryExtraction 35 | ( 36 | "com.apple.contacts", 37 | { FOLDER_WILDCARD + BACKUP_PATH }, 38 | { 39 | { 40 | BACKUP_DOMAIN, 41 | { 42 | BACKUP_PATH 43 | } 44 | } 45 | } 46 | ) 47 | }; 48 | } 49 | 50 | void Contacts::run( Core::SafePointer< Backup > backup, const STIXIndicators & indicators, std::vector< MalwareCheckResult > & results, Core::Progress & progress ) 51 | { 52 | progress.message( Core::L10N( "MalwareActivityDetectionCheckContacts_Info" ) ); 53 | 54 | std::string domain = BACKUP_DOMAIN; 55 | std::string path = BACKUP_PATH; 56 | std::shared_ptr< sqlite3 > db = MalwareActivityDetectionHelper::ExtractAndOpenSQLiteDB( backup, domain, path, this->name(), results ); 57 | if( db == nullptr ) 58 | { 59 | return; 60 | } 61 | 62 | SQLite::QueryDictionary queryDict = db.get(); 63 | 64 | // query only contacts where value is a pseudo email address (containing @) 65 | std::string query = 66 | R"query( 67 | 68 | SELECT 69 | multi.value, 70 | person.first, 71 | person.middle, 72 | person.last, 73 | person.organization, 74 | person.modificationdate 75 | FROM ABPerson person, ABMultiValue multi 76 | WHERE multi.value LIKE '%@%' AND person.rowid = multi.record_id AND multi.value not null 77 | ORDER by person.rowid ASC; 78 | 79 | )query"; 80 | 81 | std::vector< CF::Dictionary > records = queryDict.executeQuery( query ); 82 | if( records.size() == 0 ) 83 | { 84 | if( queryDict.errorMessage().length() > 0 ) 85 | { 86 | MalwareCheckResult( MalwareCheckResult::Type::Analyzer, MalwareCheckResult::Severity::Warning, 0, this->name(), EVENT_DB_QUERY_ERROR, "'" + domain + "/" + path + "' cannot be parsed (" + queryDict.errorMessage() + ").", results ); 87 | } 88 | else 89 | { 90 | MalwareCheckResult( MalwareCheckResult::Type::Analyzer, MalwareCheckResult::Severity::Info, 0, this->name(), EVENT_ANALYSING_ITEMS_INFO, "No contacts to analyze.", results ); 91 | } 92 | 93 | return; 94 | } 95 | 96 | MalwareCheckResult( MalwareCheckResult::Type::Analyzer, MalwareCheckResult::Severity::Info, 0, this->name(), EVENT_ANALYSING_ITEMS_INFO, "Analyzing " + std::to_string( records.size() ) + " contacts.", results ); 97 | 98 | for( const auto & record: records ) 99 | { 100 | std::string email = CF::String( record[ "value" ] ); 101 | 102 | MalwareCheckResult res = indicators.checkEmail( email ); 103 | if( res.isSuspicious() ) 104 | { 105 | std::string firstname = CF::String( record[ "First" ] ); 106 | std::string lastname = CF::String( record[ "Last" ] ); 107 | std::string organisation = CF::String( record[ "Organisation" ] ); 108 | double modifDate = CF::Number( record[ "ModificationDate" ] ); 109 | double timestamp = MalwareActivityDetectionHelper::AppleTimestampToUnixTimestamp( modifDate ); 110 | 111 | res.addToResults 112 | ( 113 | this->name(), 114 | timestamp, 115 | "Contact " + firstname + " " + lastname + " " + organisation + " with a suspicious email address: '" + email + "'.", 116 | results 117 | ); 118 | } 119 | } 120 | } 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /src/MalwareChecks/DDNA-MalwareCheck-Contacts.hpp: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2024, DigiDNA 3 | * All rights reserved 4 | * 5 | * This Source Code Form is subject to the terms of the MVT License, v. 1.1. 6 | * If a copy of the MVT License was not distributed with this file, you can 7 | * obtain one at https://license.mvt.re/1.1/. 8 | ******************************************************************************/ 9 | 10 | #ifndef DDNA_MALWARECHECK_CONTACTS_HPP 11 | #define DDNA_MALWARECHECK_CONTACTS_HPP 12 | 13 | #include 14 | 15 | namespace DDNA 16 | { 17 | namespace MalwareChecks 18 | { 19 | class Contacts: public MalwareCheck 20 | { 21 | public: 22 | 23 | std::string name( void ) const override; 24 | std::string image( void ) const override; 25 | std::vector< iOSDataset > iOSDatasets( void ) const override; 26 | void run( Core::SafePointer< Backup > backup, const STIXIndicators & indicators, std::vector< MalwareCheckResult > & results, Core::Progress & progress ) override; 27 | }; 28 | } 29 | } 30 | #endif /* DDNA_MALWARECHECK_CONTACTS_HPP */ 31 | -------------------------------------------------------------------------------- /src/MalwareChecks/DDNA-MalwareCheck-DataUsage.hpp: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2021, DigiDNA 3 | * All rights reserved 4 | * 5 | * This Source Code Form is subject to the terms of the MVT License, v. 1.1. 6 | * If a copy of the MVT License was not distributed with this file, you can 7 | * obtain one at https://license.mvt.re/1.1/. 8 | ******************************************************************************/ 9 | 10 | #ifndef DDNA_MALWARE_CHECK_DATA_USAGE_HPP 11 | #define DDNA_MALWARE_CHECK_DATA_USAGE_HPP 12 | 13 | #include 14 | 15 | namespace DDNA 16 | { 17 | namespace MalwareChecks 18 | { 19 | class DataUsage: public MalwareCheck 20 | { 21 | public: 22 | 23 | std::string name( void ) const override; 24 | std::string image( void ) const override; 25 | std::vector< iOSDataset > iOSDatasets( void ) const override; 26 | void run( Core::SafePointer< Backup > backup, const STIXIndicators & indicators, std::vector< MalwareCheckResult > & results, Core::Progress & progress ) override; 27 | 28 | void checkManipulated( const std::vector< CF::Dictionary > & records, std::vector< MalwareCheckResult > & results ); 29 | std::vector< CF::Dictionary > checkDeleted( std::vector< CF::Dictionary > records, std::vector< MalwareCheckResult > & results ); 30 | }; 31 | } 32 | } 33 | 34 | #endif /* DDNA_MALWARE_CHECK_DATA_USAGE_HPP */ 35 | -------------------------------------------------------------------------------- /src/MalwareChecks/DDNA-MalwareCheck-EdgeFavicon.cpp: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2021, DigiDNA 3 | * All rights reserved 4 | * 5 | * This Source Code Form is subject to the terms of the MVT License, v. 1.1. 6 | * If a copy of the MVT License was not distributed with this file, you can 7 | * obtain one at https://license.mvt.re/1.1/. 8 | ******************************************************************************/ 9 | 10 | #include 11 | #include 12 | 13 | #define BACKUP_DOMAIN "AppDomain-com.microsoft.msedge" 14 | #define BACKUP_PATH "Library/Application Support/ChromeSync/Favicons" 15 | 16 | namespace DDNA 17 | { 18 | namespace MalwareChecks 19 | { 20 | std::string EdgeFavicon::name( void ) const 21 | { 22 | return "Edge Favicon"; 23 | } 24 | 25 | std::string EdgeFavicon::image( void ) const 26 | { 27 | return MalwareActivityDetectionHelper::AppIconPath( "com.microsoft.msedge" ); 28 | } 29 | 30 | std::vector< iOSDataset > EdgeFavicon::iOSDatasets( void ) const 31 | { 32 | return 33 | { 34 | iOSDataset::customDatasetForTemporaryExtraction 35 | ( 36 | "com.microsoft.msedge.favicon", 37 | { FOLDER_WILDCARD + BACKUP_PATH }, 38 | { 39 | { 40 | BACKUP_DOMAIN, 41 | { 42 | BACKUP_PATH 43 | } 44 | } 45 | } 46 | ) 47 | }; 48 | } 49 | 50 | void EdgeFavicon::run( Core::SafePointer< Backup > backup, const STIXIndicators & indicators, std::vector< MalwareCheckResult > & results, Core::Progress & progress ) 51 | { 52 | progress.message( Core::L10N( "MalwareActivityDetectionCheckEdgeFavicon_Info" ) ); 53 | 54 | if( !backup->mbdbRecordExists( BACKUP_DOMAIN, BACKUP_PATH ) ) 55 | { 56 | return; 57 | } 58 | 59 | std::string domain = BACKUP_DOMAIN; 60 | std::string path = BACKUP_PATH; 61 | std::shared_ptr< sqlite3 > db = MalwareActivityDetectionHelper::ExtractAndOpenSQLiteDB( backup, domain, path, this->name(), results ); 62 | if( db == nullptr ) 63 | { 64 | return; 65 | } 66 | 67 | SQLite::QueryDictionary queryDict = db.get(); 68 | std::string query = 69 | R"query( 70 | 71 | SELECT 72 | icon_mapping.page_url, 73 | favicons.url, 74 | favicon_bitmaps.last_updated, 75 | favicon_bitmaps.last_requested 76 | FROM icon_mapping 77 | JOIN favicon_bitmaps ON icon_mapping.icon_id = favicon_bitmaps.icon_id 78 | JOIN favicons ON icon_mapping.icon_id = favicons.id 79 | ORDER BY icon_mapping.id; 80 | 81 | )query"; 82 | 83 | std::vector< CF::Dictionary > records = queryDict.executeQuery( query ); 84 | if( records.size() == 0 ) 85 | { 86 | if( queryDict.errorMessage().length() > 0 ) 87 | { 88 | MalwareCheckResult( MalwareCheckResult::Type::Analyzer, MalwareCheckResult::Severity::Warning, 0, this->name(), EVENT_DB_QUERY_ERROR, "'" + domain + "/" + path + "' cannot be parsed (" + queryDict.errorMessage() + ").", results ); 89 | } 90 | else 91 | { 92 | MalwareCheckResult( MalwareCheckResult::Type::Analyzer, MalwareCheckResult::Severity::Info, 0, this->name(), EVENT_ANALYSING_ITEMS_INFO, "No Edge favicons to analyze.", results ); 93 | } 94 | 95 | return; 96 | } 97 | 98 | MalwareCheckResult( MalwareCheckResult::Type::Analyzer, MalwareCheckResult::Severity::Info, 0, this->name(), EVENT_ANALYSING_ITEMS_INFO, "Analyzing " + std::to_string( records.size() ) + " Edge favicons.", results ); 99 | 100 | for( const auto & record: records ) 101 | { 102 | for( const auto & url: std::vector< std::string > { CF::String( record[ "page_url" ] ), CF::String( record[ "url" ] ) } ) 103 | { 104 | auto check = indicators.checkURL( url, this->name(), results ); 105 | 106 | auto res = std::get< 0 >( check ); 107 | if( res.isSuspicious() ) 108 | { 109 | auto finalURL = std::get< 1 >( check ); 110 | int64_t requested = CF::Number( record[ "last_requested" ] ); 111 | int64_t updated = CF::Number( record[ "last_updated" ] ); 112 | int64_t ts = ( updated != 0 ) ? updated : requested; 113 | Core::DateTime dt = Core::DateTime( ts / 1000000, Core::DateTime::Epoch::Windows ); 114 | 115 | res.addToResults 116 | ( 117 | this->name(), 118 | dt.as( Core::DateTime::Epoch::Unix ), 119 | "Edge visit to '" + finalURL + "'", 120 | results 121 | ); 122 | } 123 | } 124 | } 125 | 126 | } 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /src/MalwareChecks/DDNA-MalwareCheck-EdgeFavicon.hpp: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2021, DigiDNA 3 | * All rights reserved 4 | * 5 | * This Source Code Form is subject to the terms of the MVT License, v. 1.1. 6 | * If a copy of the MVT License was not distributed with this file, you can 7 | * obtain one at https://license.mvt.re/1.1/. 8 | ******************************************************************************/ 9 | 10 | #ifndef DDNA_MALWARE_CHECK_EDGE_FAVICON_HPP 11 | #define DDNA_MALWARE_CHECK_EDGE_FAVICON_HPP 12 | 13 | #include 14 | 15 | namespace DDNA 16 | { 17 | namespace MalwareChecks 18 | { 19 | class EdgeFavicon: public MalwareCheck 20 | { 21 | public: 22 | 23 | std::string name( void ) const override; 24 | std::string image( void ) const override; 25 | std::vector< iOSDataset > iOSDatasets( void ) const override; 26 | void run( Core::SafePointer< Backup > backup, const STIXIndicators & indicators, std::vector< MalwareCheckResult > & results, Core::Progress & progress ) override; 27 | }; 28 | } 29 | } 30 | 31 | #endif /* DDNA_MALWARE_CHECK_EDGE_FAVICON_HPP */ 32 | -------------------------------------------------------------------------------- /src/MalwareChecks/DDNA-MalwareCheck-EdgeHistory.cpp: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2021, DigiDNA 3 | * All rights reserved 4 | * 5 | * This Source Code Form is subject to the terms of the MVT License, v. 1.1. 6 | * If a copy of the MVT License was not distributed with this file, you can 7 | * obtain one at https://license.mvt.re/1.1/. 8 | ******************************************************************************/ 9 | 10 | #include 11 | #include 12 | 13 | #define BACKUP_DOMAIN "AppDomain-com.microsoft.msedge" 14 | #define BACKUP_PATH "Library/Application Support/ChromeSync/History" 15 | 16 | namespace DDNA 17 | { 18 | namespace MalwareChecks 19 | { 20 | std::string EdgeHistory::name( void ) const 21 | { 22 | return "Edge History"; 23 | } 24 | 25 | std::string EdgeHistory::image( void ) const 26 | { 27 | return MalwareActivityDetectionHelper::AppIconPath( "com.microsoft.msedge" ); 28 | } 29 | 30 | std::vector< iOSDataset > EdgeHistory::iOSDatasets( void ) const 31 | { 32 | return 33 | { 34 | iOSDataset::customDatasetForTemporaryExtraction 35 | ( 36 | "com.microsoft.msedge.history", 37 | { FOLDER_WILDCARD + BACKUP_PATH }, 38 | { 39 | { 40 | BACKUP_DOMAIN, 41 | { 42 | BACKUP_PATH 43 | } 44 | } 45 | } 46 | ) 47 | }; 48 | } 49 | 50 | void EdgeHistory::run( Core::SafePointer< Backup > backup, const STIXIndicators & indicators, std::vector< MalwareCheckResult > & results, Core::Progress & progress ) 51 | { 52 | progress.message( Core::L10N( "MalwareActivityDetectionCheckEdgeHistory_Info" ) ); 53 | 54 | if( !backup->mbdbRecordExists( BACKUP_DOMAIN, BACKUP_PATH ) ) 55 | { 56 | return; 57 | } 58 | 59 | std::string domain = BACKUP_DOMAIN; 60 | std::string path = BACKUP_PATH; 61 | std::shared_ptr< sqlite3 > db = MalwareActivityDetectionHelper::ExtractAndOpenSQLiteDB( backup, domain, path, this->name(), results ); 62 | if( db == nullptr ) 63 | { 64 | return; 65 | } 66 | 67 | SQLite::QueryDictionary queryDict = db.get(); 68 | std::string query = 69 | R"query( 70 | 71 | SELECT 72 | urls.id, 73 | urls.url, 74 | visits.id, 75 | visits.visit_time, 76 | visits.from_visit 77 | FROM urls 78 | JOIN visits ON visits.url = urls.id 79 | ORDER BY visits.visit_time 80 | 81 | )query"; 82 | 83 | std::vector< CF::Dictionary > records = queryDict.executeQuery( query ); 84 | if( records.size() == 0 ) 85 | { 86 | if( queryDict.errorMessage().length() > 0 ) 87 | { 88 | MalwareCheckResult( MalwareCheckResult::Type::Analyzer, MalwareCheckResult::Severity::Warning, 0, this->name(), EVENT_DB_QUERY_ERROR, "'" + domain + "/" + path + "' cannot be parsed (" + queryDict.errorMessage() + ").", results ); 89 | } 90 | else 91 | { 92 | MalwareCheckResult( MalwareCheckResult::Type::Analyzer, MalwareCheckResult::Severity::Info, 0, this->name(), EVENT_ANALYSING_ITEMS_INFO, "No Edge history entries to analyze.", results ); 93 | } 94 | 95 | return; 96 | } 97 | 98 | MalwareCheckResult( MalwareCheckResult::Type::Analyzer, MalwareCheckResult::Severity::Info, 0, this->name(), EVENT_ANALYSING_ITEMS_INFO, "Analyzing " + std::to_string( records.size() ) + " Edge history entries.", results ); 99 | 100 | for( const auto & record: records ) 101 | { 102 | std::string url = CF::String( record[ "url" ] ); 103 | auto check = indicators.checkURL( url, this->name(), results ); 104 | 105 | auto res = std::get< 0 >( check ); 106 | if( res.isSuspicious() ) 107 | { 108 | auto finalURL = std::get< 1 >( check ); 109 | int64_t ts = CF::Number( record[ "visit_time" ] ); 110 | Core::DateTime dt = Core::DateTime( ts / 1000000, Core::DateTime::Epoch::Windows ); 111 | 112 | res.addToResults 113 | ( 114 | this->name(), 115 | dt.as( Core::DateTime::Epoch::Unix ), 116 | "Edge visit to '" + finalURL + "'.", 117 | results 118 | ); 119 | } 120 | } 121 | } 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /src/MalwareChecks/DDNA-MalwareCheck-EdgeHistory.hpp: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2021, DigiDNA 3 | * All rights reserved 4 | * 5 | * This Source Code Form is subject to the terms of the MVT License, v. 1.1. 6 | * If a copy of the MVT License was not distributed with this file, you can 7 | * obtain one at https://license.mvt.re/1.1/. 8 | ******************************************************************************/ 9 | 10 | #ifndef DDNA_MALWARE_CHECK_EDGE_HISTORY_HPP 11 | #define DDNA_MALWARE_CHECK_EDGE_HISTORY_HPP 12 | 13 | #include 14 | 15 | namespace DDNA 16 | { 17 | namespace MalwareChecks 18 | { 19 | class EdgeHistory: public MalwareCheck 20 | { 21 | public: 22 | 23 | std::string name( void ) const override; 24 | std::string image( void ) const override; 25 | std::vector< iOSDataset > iOSDatasets( void ) const override; 26 | void run( Core::SafePointer< Backup > backup, const STIXIndicators & indicators, std::vector< MalwareCheckResult > & results, Core::Progress & progress ) override; 27 | }; 28 | } 29 | } 30 | 31 | #endif /* DDNA_MALWARE_CHECK_EDGE_HISTORY_HPP */ 32 | -------------------------------------------------------------------------------- /src/MalwareChecks/DDNA-MalwareCheck-FirefoxFavicon.cpp: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2021, DigiDNA 3 | * All rights reserved 4 | * 5 | * This Source Code Form is subject to the terms of the MVT License, v. 1.1. 6 | * If a copy of the MVT License was not distributed with this file, you can 7 | * obtain one at https://license.mvt.re/1.1/. 8 | ******************************************************************************/ 9 | 10 | #include 11 | #include 12 | 13 | #define BACKUP_DOMAIN "AppDomainGroup-group.org.mozilla.ios.Firefox" 14 | #define BACKUP_PATH "profile.profile/browser.db" 15 | 16 | namespace DDNA 17 | { 18 | namespace MalwareChecks 19 | { 20 | std::string FirefoxFavicon::name( void ) const 21 | { 22 | return "Firefox Favicon"; 23 | } 24 | 25 | std::string FirefoxFavicon::image( void ) const 26 | { 27 | return MalwareActivityDetectionHelper::AppIconPath( "org.mozilla.ios.Firefox" ); 28 | } 29 | 30 | std::vector< iOSDataset > FirefoxFavicon::iOSDatasets( void ) const 31 | { 32 | return 33 | { 34 | iOSDataset::customDatasetForTemporaryExtraction 35 | ( 36 | "org.mozilla.ios.Firefox.favicon", 37 | { FOLDER_WILDCARD + BACKUP_PATH }, 38 | { 39 | { 40 | BACKUP_DOMAIN, 41 | { 42 | BACKUP_PATH 43 | } 44 | } 45 | } 46 | ) 47 | }; 48 | } 49 | 50 | void FirefoxFavicon::run( Core::SafePointer< Backup > backup, const STIXIndicators & indicators, std::vector< MalwareCheckResult > & results, Core::Progress & progress ) 51 | { 52 | progress.message( Core::L10N( "MalwareActivityDetectionCheckFirefoxFavicon_Info" ) ); 53 | 54 | if( !backup->mbdbRecordExists( BACKUP_DOMAIN, BACKUP_PATH ) ) 55 | { 56 | return; 57 | } 58 | 59 | std::string domain = BACKUP_DOMAIN; 60 | std::string path = BACKUP_PATH; 61 | std::shared_ptr< sqlite3 > db = MalwareActivityDetectionHelper::ExtractAndOpenSQLiteDB( backup, domain, path, this->name(), results ); 62 | if( db == nullptr ) 63 | { 64 | return; 65 | } 66 | 67 | SQLite::QueryDictionary queryDict = db.get(); 68 | std::string query = 69 | R"query( 70 | 71 | SELECT 72 | favicons.id, 73 | favicons.url as faviconURL, 74 | favicons.width, 75 | favicons.height, 76 | favicons.type, 77 | favicons.date, 78 | history.id, 79 | history.url as historyURL 80 | FROM favicons 81 | INNER JOIN favicon_sites ON favicon_sites.faviconID = favicons.id 82 | INNER JOIN history ON favicon_sites.siteID = history.id; 83 | 84 | )query"; 85 | 86 | std::vector< CF::Dictionary > records = queryDict.executeQuery( query ); 87 | if( records.size() == 0 ) 88 | { 89 | if( queryDict.errorMessage().length() > 0 ) 90 | { 91 | MalwareCheckResult( MalwareCheckResult::Type::Analyzer, MalwareCheckResult::Severity::Warning, 0, this->name(), EVENT_DB_QUERY_ERROR, "'" + domain + "/" + path + "' cannot be parsed (" + queryDict.errorMessage() + ").", results ); 92 | } 93 | else 94 | { 95 | MalwareCheckResult( MalwareCheckResult::Type::Analyzer, MalwareCheckResult::Severity::Info, 0, this->name(), EVENT_ANALYSING_ITEMS_INFO, "No Firefox favicons to analyze.", results ); 96 | } 97 | 98 | return; 99 | } 100 | 101 | MalwareCheckResult( MalwareCheckResult::Type::Analyzer, MalwareCheckResult::Severity::Info, 0, this->name(), EVENT_ANALYSING_ITEMS_INFO, "Analyzing " + std::to_string( records.size() ) + " Firefox favicons.", results ); 102 | 103 | for( const auto & record: records ) 104 | { 105 | double timestamp = CF::Number( record[ "date" ] ); 106 | 107 | for( const auto & url: std::vector< std::string > { CF::String( record[ "faviconURL" ] ), CF::String( record[ "historyURL" ] ) } ) 108 | { 109 | auto check = indicators.checkURL( url, this->name(), results ); 110 | 111 | auto res = std::get< 0 >( check ); 112 | if( res.isSuspicious() ) 113 | { 114 | auto finalURL = std::get< 1 >( check ); 115 | 116 | res.addToResults 117 | ( 118 | this->name(), 119 | ( timestamp / 1000000 ), 120 | "Firefox visit to '" + finalURL + "'.", 121 | results 122 | ); 123 | } 124 | } 125 | } 126 | } 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /src/MalwareChecks/DDNA-MalwareCheck-FirefoxFavicon.hpp: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2021, DigiDNA 3 | * All rights reserved 4 | * 5 | * This Source Code Form is subject to the terms of the MVT License, v. 1.1. 6 | * If a copy of the MVT License was not distributed with this file, you can 7 | * obtain one at https://license.mvt.re/1.1/. 8 | ******************************************************************************/ 9 | 10 | #ifndef DDNA_MALWARE_CHECK_FIREFOX_FAVICON_HPP 11 | #define DDNA_MALWARE_CHECK_FIREFOX_FAVICON_HPP 12 | 13 | #include 14 | 15 | namespace DDNA 16 | { 17 | namespace MalwareChecks 18 | { 19 | class FirefoxFavicon: public MalwareCheck 20 | { 21 | public: 22 | 23 | std::string name( void ) const override; 24 | std::string image( void ) const override; 25 | std::vector< iOSDataset > iOSDatasets( void ) const override; 26 | void run( Core::SafePointer< Backup > backup, const STIXIndicators & indicators, std::vector< MalwareCheckResult > & results, Core::Progress & progress ) override; 27 | }; 28 | } 29 | } 30 | 31 | #endif /* DDNA_MALWARE_CHECK_FIREFOX_FAVICON_HPP */ 32 | -------------------------------------------------------------------------------- /src/MalwareChecks/DDNA-MalwareCheck-FirefoxHistory.cpp: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2021, DigiDNA 3 | * All rights reserved 4 | * 5 | * This Source Code Form is subject to the terms of the MVT License, v. 1.1. 6 | * If a copy of the MVT License was not distributed with this file, you can 7 | * obtain one at https://license.mvt.re/1.1/. 8 | ******************************************************************************/ 9 | 10 | #include 11 | #include 12 | 13 | #define BACKUP_DOMAIN "AppDomainGroup-group.org.mozilla.ios.Firefox" 14 | #define BACKUP_PATH "profile.profile/browser.db" 15 | 16 | namespace DDNA 17 | { 18 | namespace MalwareChecks 19 | { 20 | std::string FirefoxHistory::name( void ) const 21 | { 22 | return "Firefox History"; 23 | } 24 | 25 | std::string FirefoxHistory::image( void ) const 26 | { 27 | return MalwareActivityDetectionHelper::AppIconPath( "org.mozilla.ios.Firefox" ); 28 | } 29 | 30 | std::vector< iOSDataset > FirefoxHistory::iOSDatasets( void ) const 31 | { 32 | return 33 | { 34 | iOSDataset::customDatasetForTemporaryExtraction 35 | ( 36 | "org.mozilla.ios.Firefox.history", 37 | { FOLDER_WILDCARD + BACKUP_PATH }, 38 | { 39 | { 40 | BACKUP_DOMAIN, 41 | { 42 | BACKUP_PATH 43 | } 44 | } 45 | } 46 | ) 47 | }; 48 | } 49 | 50 | void FirefoxHistory::run( Core::SafePointer< Backup > backup, const STIXIndicators & indicators, std::vector< MalwareCheckResult > & results, Core::Progress & progress ) 51 | { 52 | progress.message( Core::L10N( "MalwareActivityDetectionCheckFirefoxHistory_Info" ) ); 53 | 54 | if( !backup->mbdbRecordExists( BACKUP_DOMAIN, BACKUP_PATH ) ) 55 | { 56 | return; 57 | } 58 | 59 | std::string domain = BACKUP_DOMAIN; 60 | std::string path = BACKUP_PATH; 61 | std::shared_ptr< sqlite3 > db = MalwareActivityDetectionHelper::ExtractAndOpenSQLiteDB( backup, domain, path, this->name(), results ); 62 | if( db == nullptr ) 63 | { 64 | return; 65 | } 66 | 67 | SQLite::QueryDictionary queryDict = db.get(); 68 | std::string query = 69 | R"query( 70 | 71 | SELECT 72 | visits.id, 73 | visits.date, 74 | history.url, 75 | history.title, 76 | visits.is_local, 77 | visits.type 78 | FROM visits, history 79 | WHERE visits.siteID = history.id; 80 | 81 | )query"; 82 | 83 | std::vector< CF::Dictionary > records = queryDict.executeQuery( query ); 84 | if( records.size() == 0 ) 85 | { 86 | if( queryDict.errorMessage().length() > 0 ) 87 | { 88 | MalwareCheckResult( MalwareCheckResult::Type::Analyzer, MalwareCheckResult::Severity::Warning, 0, this->name(), EVENT_DB_QUERY_ERROR, "'" + domain + "/" + path + "' cannot be parsed (" + queryDict.errorMessage() + ").", results ); 89 | } 90 | else 91 | { 92 | MalwareCheckResult( MalwareCheckResult::Type::Analyzer, MalwareCheckResult::Severity::Info, 0, this->name(), EVENT_ANALYSING_ITEMS_INFO, "No Firefox history entries to analyze.", results ); 93 | } 94 | 95 | return; 96 | } 97 | 98 | MalwareCheckResult( MalwareCheckResult::Type::Analyzer, MalwareCheckResult::Severity::Info, 0, this->name(), EVENT_ANALYSING_ITEMS_INFO, "Analyzing " + std::to_string( records.size() ) + " Firefox history entries.", results ); 99 | 100 | for( const auto & record: records ) 101 | { 102 | std::string url = CF::String( record[ "url" ] ); 103 | auto check = indicators.checkURL( url, this->name(), results ); 104 | 105 | auto res = std::get< 0 >( check ); 106 | if( res.isSuspicious() ) 107 | { 108 | auto finalURL = std::get< 1 >( check ); 109 | double timestamp = CF::Number( record[ "date" ] ); 110 | 111 | res.addToResults 112 | ( 113 | this->name(), 114 | ( timestamp / 1000000 ), 115 | "Firefox visit to '" + finalURL + "'.", 116 | results 117 | ); 118 | } 119 | } 120 | } 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /src/MalwareChecks/DDNA-MalwareCheck-FirefoxHistory.hpp: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2021, DigiDNA 3 | * All rights reserved 4 | * 5 | * This Source Code Form is subject to the terms of the MVT License, v. 1.1. 6 | * If a copy of the MVT License was not distributed with this file, you can 7 | * obtain one at https://license.mvt.re/1.1/. 8 | ******************************************************************************/ 9 | 10 | #ifndef DDNA_MALWARE_CHECK_FIREFOX_HISTORY_HPP 11 | #define DDNA_MALWARE_CHECK_FIREFOX_HISTORY_HPP 12 | 13 | #include 14 | 15 | namespace DDNA 16 | { 17 | namespace MalwareChecks 18 | { 19 | class FirefoxHistory: public MalwareCheck 20 | { 21 | public: 22 | 23 | std::string name( void ) const override; 24 | std::string image( void ) const override; 25 | std::vector< iOSDataset > iOSDatasets( void ) const override; 26 | void run( Core::SafePointer< Backup > backup, const STIXIndicators & indicators, std::vector< MalwareCheckResult > & results, Core::Progress & progress ) override; 27 | }; 28 | } 29 | } 30 | 31 | #endif /* DDNA_MALWARE_CHECK_CHROME_HISTORY_HPP */ 32 | -------------------------------------------------------------------------------- /src/MalwareChecks/DDNA-MalwareCheck-IDStatusCache.cpp: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2021, DigiDNA 3 | * All rights reserved 4 | * 5 | * This Source Code Form is subject to the terms of the MVT License, v. 1.1. 6 | * If a copy of the MVT License was not distributed with this file, you can 7 | * obtain one at https://license.mvt.re/1.1/. 8 | ******************************************************************************/ 9 | 10 | #include 11 | #include 12 | 13 | #define BACKUP_DOMAIN "HomeDomain" 14 | #define BACKUP_PATH "Library/Preferences/com.apple.identityservices.idstatuscache.plist" 15 | 16 | namespace DDNA 17 | { 18 | namespace MalwareChecks 19 | { 20 | std::string IDStatusCache::name( void ) const 21 | { 22 | return "ID Status Cache"; 23 | } 24 | 25 | std::string IDStatusCache::image( void ) const 26 | { 27 | return "Accounts"; 28 | } 29 | 30 | std::vector< iOSDataset > IDStatusCache::iOSDatasets( void ) const 31 | { 32 | return 33 | { 34 | iOSDataset::customDatasetForTemporaryExtraction 35 | ( 36 | "com.apple.identityservices.idstatuscache", 37 | { FOLDER_WILDCARD + BACKUP_PATH }, 38 | { 39 | { 40 | BACKUP_DOMAIN, 41 | { 42 | BACKUP_PATH 43 | } 44 | } 45 | } 46 | ) 47 | }; 48 | } 49 | 50 | void IDStatusCache::run( Core::SafePointer< Backup > backup, const STIXIndicators & indicators, std::vector< MalwareCheckResult > & results, Core::Progress & progress ) 51 | { 52 | progress.message( Core::L10N( "MalwareActivityDetectionCheckIDStatusCache_Info" ) ); 53 | 54 | Core::Error error; 55 | 56 | // Since iOS 14.7, the idstatus cache is empty 57 | if( backup->deviceOSVersion() >= Core::Version( "14.7" ) ) 58 | { 59 | MalwareCheckResult( MalwareCheckResult::Type::Analyzer, MalwareCheckResult::Severity::Info, 0, this->name(), EVENT_PLIST_DICT_IS_EMPTY_ERROR, "'" + std::string( BACKUP_DOMAIN ) + "/" + BACKUP_PATH + "' is empty since iOS 14.7.", results ); 60 | 61 | return; 62 | } 63 | 64 | CF::Dictionary plist = MalwareActivityDetectionHelper::ExtractPropertyList( backup, BACKUP_DOMAIN, BACKUP_PATH, true, this->name(), results, error ); 65 | if( error.failure() ) 66 | { 67 | return; 68 | } 69 | 70 | /* 71 | UInt8 bytes[] = { 65, 66, 67, 0, 68, 69, 70 }; 72 | CF::AutoPointer badUser = CFStringCreateWithBytes( nullptr, bytes, sizeof( bytes ), kCFStringEncodingUTF8, false ); 73 | CF::Dictionary badEntry = { CF::Pair( CF::String( "IDStatus" ), CF::Number( 42 ) ), CF::Pair( CF::String( "LookupDate" ), CF::Number( 42 ) ) }; 74 | CF::Dictionary badApp = { CF::Pair( CF::String( badUser ), badEntry ) }; 75 | 76 | plist.SetValue( CF::String( "com.apple.foo" ), badApp ); 77 | */ 78 | 79 | for( CF::Pair p1: plist ) 80 | { 81 | CF::String app = p1.GetKey(); 82 | CF::Dictionary appEntry = p1.GetValue(); 83 | 84 | if( app.IsValid() == false || appEntry.IsValid() == false || app.GetLength() == 0 ) 85 | { 86 | continue; 87 | } 88 | 89 | for( CF::Pair p2: appEntry ) 90 | { 91 | CF::String user = p2.GetKey(); 92 | CF::Dictionary userEntry = p2.GetValue(); 93 | 94 | if( user.IsValid() == false || user.GetLength() <= 0 ) 95 | { 96 | continue; 97 | } 98 | 99 | size_t length = static_cast< size_t >( user.GetLength() ); 100 | UniChar * cptr = static_cast< UniChar * >( calloc( length + 1, sizeof( UniChar ) ) ); 101 | CF::Number date = userEntry[ "LookupDate" ]; 102 | CF::Number status = userEntry[ "IDStatus" ]; 103 | 104 | CFStringGetCharacters( user, CFRangeMake( 0, user.GetLength() ), cptr ); 105 | 106 | for( size_t i = 0; i < length; i++ ) 107 | { 108 | if( cptr[ i ] == 0 ) 109 | { 110 | MalwareCheckResult res = 111 | { 112 | MalwareCheckResult::Type::Device, 113 | MalwareCheckResult::Severity::Critical, 114 | MalwareActivityDetectionHelper::AppleTimestampToUnixTimestamp( date.GetDoubleValue() ), 115 | this->name(), 116 | EVENT_SUSCPICIOUS_VALUE, 117 | "Suspicious ID Status Cache entry with \\x00: '" + user.GetValue() + "' (status: " + std::to_string( status.GetSignedLongValue() ) + ", app: " + app.GetValue() + ").", 118 | results 119 | }; 120 | } 121 | } 122 | 123 | free( cptr ); 124 | 125 | if( user.GetValue().find( "mailto:" ) == 0 ) 126 | { 127 | std::string email = Core::String::trim( user.GetValue().substr( 7 ), "'" ); 128 | MalwareCheckResult res = indicators.checkEmail( email ); 129 | 130 | if( res.isSuspicious() ) 131 | { 132 | res.addToResults 133 | ( 134 | this->name(), 135 | Core::DateTime( date.GetSignedLongValue(), Core::DateTime::Epoch::Apple ), 136 | "Suspicious ID Status Cache email: '" + email + "' (status: " + std::to_string( status.GetSignedLongValue() ) + ", app: " + app.GetValue() + ").", 137 | results 138 | ); 139 | } 140 | } 141 | } 142 | } 143 | } 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /src/MalwareChecks/DDNA-MalwareCheck-IDStatusCache.hpp: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2021, DigiDNA 3 | * All rights reserved 4 | * 5 | * This Source Code Form is subject to the terms of the MVT License, v. 1.1. 6 | * If a copy of the MVT License was not distributed with this file, you can 7 | * obtain one at https://license.mvt.re/1.1/. 8 | ******************************************************************************/ 9 | 10 | #ifndef DDNA_MALWARE_CHECK_ID_STATUS_CACHE_HPP 11 | #define DDNA_MALWARE_CHECK_ID_STATUS_CACHE_HPP 12 | 13 | #include 14 | 15 | namespace DDNA 16 | { 17 | namespace MalwareChecks 18 | { 19 | class IDStatusCache: public MalwareCheck 20 | { 21 | public: 22 | 23 | std::string name( void ) const override; 24 | std::string image( void ) const override; 25 | std::vector< iOSDataset > iOSDatasets( void ) const override; 26 | void run( Core::SafePointer< Backup > backup, const STIXIndicators & indicators, std::vector< MalwareCheckResult > & results, Core::Progress & progress ) override; 27 | }; 28 | } 29 | } 30 | 31 | #endif /* DDNA_MALWARE_CHECK_ID_STATUS_CACHE_HPP */ 32 | -------------------------------------------------------------------------------- /src/MalwareChecks/DDNA-MalwareCheck-InteractionC.hpp: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2021, DigiDNA 3 | * All rights reserved 4 | * 5 | * This Source Code Form is subject to the terms of the MVT License, v. 1.1. 6 | * If a copy of the MVT License was not distributed with this file, you can 7 | * obtain one at https://license.mvt.re/1.1/. 8 | ******************************************************************************/ 9 | 10 | #ifndef DDNA_MALWARE_CHECK_INTERACTION_C_HPP 11 | #define DDNA_MALWARE_CHECK_INTERACTION_C_HPP 12 | 13 | #include 14 | 15 | namespace DDNA 16 | { 17 | namespace MalwareChecks 18 | { 19 | class InteractionC: public MalwareCheck 20 | { 21 | public: 22 | 23 | std::string name( void ) const override; 24 | std::string image( void ) const override; 25 | std::vector< iOSDataset > iOSDatasets( void ) const override; 26 | void run( Core::SafePointer< Backup > backup, const STIXIndicators & indicators, std::vector< MalwareCheckResult > & results, Core::Progress & progress ) override; 27 | }; 28 | } 29 | } 30 | 31 | #endif /* DDNA_MALWARE_CHECK_INTERACTION_C_HPP */ 32 | -------------------------------------------------------------------------------- /src/MalwareChecks/DDNA-MalwareCheck-InteractionCAttachment.cpp: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2021, DigiDNA 3 | * All rights reserved 4 | * 5 | * This Source Code Form is subject to the terms of the MVT License, v. 1.1. 6 | * If a copy of the MVT License was not distributed with this file, you can 7 | * obtain one at https://license.mvt.re/1.1/. 8 | ******************************************************************************/ 9 | 10 | #include 11 | #include 12 | 13 | #define BACKUP_DOMAIN "HomeDomain" 14 | #define BACKUP_PATH "Library/CoreDuet/People/interactionC.db" 15 | 16 | namespace DDNA 17 | { 18 | namespace MalwareChecks 19 | { 20 | std::string InteractionCAttachment::name( void ) const 21 | { 22 | return "InteractionC Attachment"; 23 | } 24 | 25 | std::string InteractionCAttachment::image( void ) const 26 | { 27 | return "defaultAttachmentIcon"; 28 | } 29 | 30 | std::vector< iOSDataset > InteractionCAttachment::iOSDatasets( void ) const 31 | { 32 | return 33 | { 34 | iOSDataset::customDatasetForTemporaryExtraction 35 | ( 36 | "com.apple.interactionC.attachment", 37 | { FOLDER_WILDCARD + BACKUP_PATH }, 38 | { 39 | { 40 | BACKUP_DOMAIN, 41 | { 42 | BACKUP_PATH 43 | } 44 | } 45 | } 46 | ) 47 | }; 48 | } 49 | 50 | void InteractionCAttachment::run( Core::SafePointer< Backup > backup, const STIXIndicators & indicators, std::vector< MalwareCheckResult > & results, Core::Progress & progress ) 51 | { 52 | progress.message( Core::L10N( "MalwareActivityDetectionCheckInteractionCAttachment_Info" ) ); 53 | 54 | std::string domain = BACKUP_DOMAIN; 55 | std::string path = BACKUP_PATH; 56 | std::shared_ptr< sqlite3 > db = MalwareActivityDetectionHelper::ExtractAndOpenSQLiteDB( backup, domain, path, this->name(), results ); 57 | if( db == nullptr ) 58 | { 59 | return; 60 | } 61 | 62 | // TODO: Find relationship to ZINTERACTIONS/ZCONTACTS so we can get more info about the attachment sender and date... 63 | SQLite::QueryDictionary queryDict = db.get(); 64 | std::string query = "SELECT * FROM ZATTACHMENT WHERE ZCONTENTURL NOTNULL"; 65 | std::vector< CF::Dictionary > records = queryDict.executeQuery( query ); 66 | if( records.size() == 0 ) 67 | { 68 | if( queryDict.errorMessage().length() > 0 ) 69 | { 70 | MalwareCheckResult( MalwareCheckResult::Type::Analyzer, MalwareCheckResult::Severity::Warning, 0, this->name(), EVENT_DB_QUERY_ERROR, "'" + domain + "/" + path + "' cannot be parsed (" + queryDict.errorMessage() + ").", results ); 71 | } 72 | else 73 | { 74 | MalwareCheckResult( MalwareCheckResult::Type::Analyzer, MalwareCheckResult::Severity::Info, 0, this->name(), EVENT_ANALYSING_ITEMS_INFO, "No interaction attachments to analyze.", results ); 75 | } 76 | 77 | return; 78 | } 79 | 80 | MalwareCheckResult( MalwareCheckResult::Type::Analyzer, MalwareCheckResult::Severity::Info, 0, this->name(), EVENT_ANALYSING_ITEMS_INFO, "Analyzing " + std::to_string( records.size() ) + " interaction attachments.", results ); 81 | 82 | for( const auto & record: records ) 83 | { 84 | std::string url = CF::String( record[ "ZCONTENTURL" ] ); 85 | auto check = indicators.checkURL( url, this->name(), results ); 86 | 87 | auto res = std::get< 0 >( check ); 88 | if( res.isSuspicious() ) 89 | { 90 | auto finalURL = std::get< 1 >( check ); 91 | 92 | res.addToResults 93 | ( 94 | this->name(), 95 | 0, 96 | "Interaction with suspicious attachment URL: '" + finalURL + "'.", 97 | results 98 | ); 99 | } 100 | } 101 | } 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/MalwareChecks/DDNA-MalwareCheck-InteractionCAttachment.hpp: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2021, DigiDNA 3 | * All rights reserved 4 | * 5 | * This Source Code Form is subject to the terms of the MVT License, v. 1.1. 6 | * If a copy of the MVT License was not distributed with this file, you can 7 | * obtain one at https://license.mvt.re/1.1/. 8 | ******************************************************************************/ 9 | 10 | #ifndef DDNA_MALWARE_CHECK_INTERACTION_C_ATTACHMENT_HPP 11 | #define DDNA_MALWARE_CHECK_INTERACTION_C_ATTACHMENT_HPP 12 | 13 | #include 14 | 15 | namespace DDNA 16 | { 17 | namespace MalwareChecks 18 | { 19 | class InteractionCAttachment: public MalwareCheck 20 | { 21 | public: 22 | 23 | std::string name( void ) const override; 24 | std::string image( void ) const override; 25 | std::vector< iOSDataset > iOSDatasets( void ) const override; 26 | void run( Core::SafePointer< Backup > backup, const STIXIndicators & indicators, std::vector< MalwareCheckResult > & results, Core::Progress & progress ) override; 27 | }; 28 | } 29 | } 30 | 31 | #endif /* DDNA_MALWARE_CHECK_INTERACTION_C_ATTACHMENT_HPP */ 32 | -------------------------------------------------------------------------------- /src/MalwareChecks/DDNA-MalwareCheck-LocationD.hpp: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2024, DigiDNA 3 | * All rights reserved 4 | * 5 | * This Source Code Form is subject to the terms of the MVT License, v. 1.1. 6 | * If a copy of the MVT License was not distributed with this file, you can 7 | * obtain one at https://license.mvt.re/1.1/. 8 | ******************************************************************************/ 9 | 10 | #ifndef DDNA_MALWARECHECK_LOCATIOND_HPP 11 | #define DDNA_MALWARECHECK_LOCATIOND_HPP 12 | 13 | #include 14 | 15 | namespace DDNA 16 | { 17 | namespace MalwareChecks 18 | { 19 | class LocationD: public MalwareCheck 20 | { 21 | public: 22 | 23 | std::string name( void ) const override; 24 | std::string image( void ) const override; 25 | std::vector< iOSDataset > iOSDatasets( void ) const override; 26 | void run( Core::SafePointer< Backup > backup, const STIXIndicators & indicators, std::vector< MalwareCheckResult > & results, Core::Progress & progress ) override; 27 | }; 28 | } 29 | } 30 | #endif /* DDNA_MALWARECHECK_LOCATIOND_HPP */ 31 | -------------------------------------------------------------------------------- /src/MalwareChecks/DDNA-MalwareCheck-Manifest.cpp: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2021, DigiDNA 3 | * All rights reserved 4 | * 5 | * This Source Code Form is subject to the terms of the MVT License, v. 1.1. 6 | * If a copy of the MVT License was not distributed with this file, you can 7 | * obtain one at https://license.mvt.re/1.1/. 8 | ******************************************************************************/ 9 | 10 | #include 11 | #include 12 | 13 | namespace DDNA 14 | { 15 | namespace MalwareChecks 16 | { 17 | std::string Manifest::name( void ) const 18 | { 19 | return "Backup Filenames"; 20 | } 21 | 22 | std::string Manifest::image( void ) const 23 | { 24 | return "BackInTime"; 25 | } 26 | 27 | std::vector< iOSDataset > Manifest::iOSDatasets( void ) const 28 | { 29 | return {}; 30 | } 31 | 32 | void Manifest::run( Core::SafePointer< Backup > backup, const STIXIndicators & indicators, std::vector< MalwareCheckResult > & results, Core::Progress & progress ) 33 | { 34 | progress.message( Core::L10N( "MalwareActivityDetectionCheckManifest_Info" ) ); 35 | progress.indeterminate( true ); 36 | 37 | /* JB - 13.04.2023: I decided to remove this check since the file is always present and doesn't seem to be an indicator of compromise. I'm not sure why MVT wants to check it but it is scary for users. 38 | if( backup->mbdbRecordExists( "RootDomain", "Library/Preferences/com.apple.CrashReporter.plist" ) ) 39 | { 40 | MalwareCheckResult( MalwareCheckResult::Type::Device, MalwareCheckResult::Severity::Warning, 0, this->name(), EVENT_SUSCPICIOUS_FILENAME, "Found a potentially suspicious 'com.apple.CrashReporter.plist' file in root domain.", results ); 41 | } 42 | */ 43 | 44 | Core::SafePointer< MBDBFile > mbdb = backup->mbdbFile(); 45 | if( mbdb.isNull() ) 46 | { 47 | return; 48 | } 49 | 50 | STIXIndicators::IndicatorEntry domains = indicators.domains(); 51 | 52 | MalwareCheckResult( MalwareCheckResult::Type::Analyzer, MalwareCheckResult::Severity::Info, 0, this->name(), EVENT_ANALYSING_ITEMS_INFO, "Analyzing " + std::to_string( domains.size() ) + " domains in " + std::to_string( mbdb->records().count() ) + " backup filenames.", results ); 53 | 54 | for( const Core::SafePointer< MBDBRecord > record: mbdb->records().allValues() ) 55 | { 56 | if( ( record->domain() == "HomeDomain" || record->domain() == "RootDomain" ) && record->path() == "Library/Preferences/com.apple.CrashReporter.plist" ) 57 | { 58 | /* 59 | * The presence of com.apple.CrashReporter.plist in the root 60 | * domain is checked in the code above. 61 | * This file may be present in other domains, so we don't 62 | * want false positives here. 63 | */ 64 | continue; 65 | } 66 | 67 | // check suspicious files 68 | MalwareCheckResult res = indicators.checkFilePath( std::string( "/" ) + record->path().toString() ); 69 | if( res.isSuspicious() ) 70 | { 71 | res.addToResults 72 | ( 73 | this->name(), 74 | static_cast< double >( record->lastModified() ), 75 | "Found a known malicious file at path: '" + record->domain().toString() + "/" + record->path().toString() + "'.", 76 | results 77 | ); 78 | } 79 | 80 | // check suspicious URL within paths 81 | res = std::get< 0 > ( indicators.checkURL( record->path(), this->name(), results ) ); 82 | if( res.isSuspicious() ) 83 | { 84 | res.addToResults 85 | ( 86 | this->name(), 87 | static_cast< double >( record->lastModified() ), 88 | "Found a known malicious file at path: '" + record->domain().toString() + "/" + record->path().toString() + "'.", 89 | results 90 | ); 91 | } 92 | } 93 | } 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/MalwareChecks/DDNA-MalwareCheck-Manifest.hpp: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2021, DigiDNA 3 | * All rights reserved 4 | * 5 | * This Source Code Form is subject to the terms of the MVT License, v. 1.1. 6 | * If a copy of the MVT License was not distributed with this file, you can 7 | * obtain one at https://license.mvt.re/1.1/. 8 | ******************************************************************************/ 9 | 10 | #ifndef DDNA_MALWARE_CHECK_MANIFEST_HPP 11 | #define DDNA_MALWARE_CHECK_MANIFEST_HPP 12 | 13 | #include 14 | 15 | namespace DDNA 16 | { 17 | namespace MalwareChecks 18 | { 19 | class Manifest: public MalwareCheck 20 | { 21 | public: 22 | 23 | std::string name( void ) const override; 24 | std::string image( void ) const override; 25 | std::vector< iOSDataset > iOSDatasets( void ) const override; 26 | void run( Core::SafePointer< Backup > backup, const STIXIndicators & indicators, std::vector< MalwareCheckResult > & results, Core::Progress & progress ) override; 27 | }; 28 | } 29 | } 30 | 31 | #endif /* DDNA_MALWARE_CHECK_MANIFEST_HPP */ 32 | -------------------------------------------------------------------------------- /src/MalwareChecks/DDNA-MalwareCheck-OSAnalyticsAdDaily.cpp: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2021, DigiDNA 3 | * All rights reserved 4 | * 5 | * This Source Code Form is subject to the terms of the MVT License, v. 1.1. 6 | * If a copy of the MVT License was not distributed with this file, you can 7 | * obtain one at https://license.mvt.re/1.1/. 8 | ******************************************************************************/ 9 | 10 | #include 11 | #include 12 | 13 | #define BACKUP_DOMAIN "HomeDomain" 14 | #define BACKUP_PATH "Library/Preferences/com.apple.osanalytics.addaily.plist" 15 | 16 | namespace DDNA 17 | { 18 | namespace MalwareChecks 19 | { 20 | std::string OSAnalyticsAdDaily::name( void ) const 21 | { 22 | return "OS Analytics"; 23 | } 24 | 25 | std::string OSAnalyticsAdDaily::image( void ) const 26 | { 27 | return "com.apple.Preferences"; 28 | } 29 | 30 | std::vector< iOSDataset > OSAnalyticsAdDaily::iOSDatasets( void ) const 31 | { 32 | return 33 | { 34 | iOSDataset::customDatasetForTemporaryExtraction 35 | ( 36 | "com.apple.osanalytics.addaily", 37 | { FOLDER_WILDCARD + BACKUP_PATH }, 38 | { 39 | { 40 | BACKUP_DOMAIN, 41 | { 42 | BACKUP_PATH 43 | } 44 | } 45 | } 46 | ) 47 | }; 48 | } 49 | 50 | void OSAnalyticsAdDaily::run( Core::SafePointer< Backup > backup, const STIXIndicators & indicators, std::vector< MalwareCheckResult > & results, Core::Progress & progress ) 51 | { 52 | progress.message( Core::L10N( "MalwareActivityDetectionCheckOSAnalyticsAdDaily_Info" ) ); 53 | 54 | Core::Error error; 55 | CF::Dictionary plist = MalwareActivityDetectionHelper::ExtractPropertyList( backup, BACKUP_DOMAIN, BACKUP_PATH, true, this->name(), results, error ); 56 | if( error.failure() ) 57 | { 58 | return; 59 | } 60 | 61 | CF::Dictionary usage = plist[ "netUsageBaseline" ]; 62 | if( usage.GetCount() == 0 ) 63 | { 64 | MalwareCheckResult( MalwareCheckResult::Type::Analyzer, MalwareCheckResult::Severity::Info, 0, this->name(), EVENT_ANALYSING_ITEMS_INFO, "No OS Analytics records to analyze.", results ); 65 | 66 | return; 67 | } 68 | 69 | MalwareCheckResult( MalwareCheckResult::Type::Analyzer, MalwareCheckResult::Severity::Info, 0, this->name(), EVENT_ANALYSING_ITEMS_INFO, "Analyzing " + std::to_string( usage.GetCount() ) + " OS Analytics records.", results ); 70 | 71 | for( CF::Pair pair: usage ) 72 | { 73 | std::string app = CF::String( pair.GetKey() ); 74 | if( app.empty() == false ) 75 | { 76 | continue; 77 | } 78 | 79 | MalwareCheckResult res = indicators.checkProcess( app ); 80 | if( res.isSuspicious() ) 81 | { 82 | CF::Array values = pair.GetValue(); 83 | CF::Date date = values[ 0 ]; 84 | int64_t timestamp = 0; 85 | 86 | if( date.IsValid() ) 87 | { 88 | timestamp = Core::DateTime( static_cast< int64_t >( date.GetValue() ), Core::DateTime::Epoch::Apple ); 89 | } 90 | 91 | res.addToResults 92 | ( 93 | this->name(), 94 | timestamp, 95 | "Found a suspicious process name: '" + app + "'.", 96 | results 97 | ); 98 | } 99 | } 100 | } 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /src/MalwareChecks/DDNA-MalwareCheck-OSAnalyticsAdDaily.hpp: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2021, DigiDNA 3 | * All rights reserved 4 | * 5 | * This Source Code Form is subject to the terms of the MVT License, v. 1.1. 6 | * If a copy of the MVT License was not distributed with this file, you can 7 | * obtain one at https://license.mvt.re/1.1/. 8 | ******************************************************************************/ 9 | 10 | #ifndef DDNA_MALWARE_CHECK_OS_ANALYTICS_AD_DAILY_HPP 11 | #define DDNA_MALWARE_CHECK_OS_ANALYTICS_AD_DAILY_HPP 12 | 13 | #include 14 | 15 | namespace DDNA 16 | { 17 | namespace MalwareChecks 18 | { 19 | class OSAnalyticsAdDaily: public MalwareCheck 20 | { 21 | public: 22 | 23 | std::string name( void ) const override; 24 | std::string image( void ) const override; 25 | std::vector< iOSDataset > iOSDatasets( void ) const override; 26 | void run( Core::SafePointer< Backup > backup, const STIXIndicators & indicators, std::vector< MalwareCheckResult > & results, Core::Progress & progress ) override; 27 | }; 28 | } 29 | } 30 | 31 | #endif /* DDNA_MALWARE_CHECK_OS_ANALYTICS_AD_DAILY_HPP */ 32 | -------------------------------------------------------------------------------- /src/MalwareChecks/DDNA-MalwareCheck-OperaHistory.hpp: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2021, DigiDNA 3 | * All rights reserved 4 | * 5 | * This Source Code Form is subject to the terms of the MVT License, v. 1.1. 6 | * If a copy of the MVT License was not distributed with this file, you can 7 | * obtain one at https://license.mvt.re/1.1/. 8 | ******************************************************************************/ 9 | 10 | #ifndef DDNA_MALWARE_CHECK_OPERA_HISTORY_HPP 11 | #define DDNA_MALWARE_CHECK_OPERA_HISTORY_HPP 12 | 13 | #include 14 | 15 | namespace DDNA 16 | { 17 | namespace MalwareChecks 18 | { 19 | class OperaHistory: public MalwareCheck 20 | { 21 | public: 22 | 23 | std::string name( void ) const override; 24 | std::string image( void ) const override; 25 | std::vector< iOSDataset > iOSDatasets( void ) const override; 26 | void run( Core::SafePointer< Backup > backup, const STIXIndicators & indicators, std::vector< MalwareCheckResult > & results, Core::Progress & progress ) override; 27 | void analyzeIcons( sqlite3 * db, const STIXIndicators & indicators, std::vector< MalwareCheckResult > & results ); 28 | void analyzeSites( sqlite3 * db, const STIXIndicators & indicators, std::vector< MalwareCheckResult > & results ); 29 | void analyzeTabs( sqlite3 * db, const STIXIndicators & indicators, std::vector< MalwareCheckResult > & results ); 30 | void analyzeURLs( sqlite3 * db, const STIXIndicators & indicators, std::vector< MalwareCheckResult > & results ); 31 | }; 32 | } 33 | } 34 | 35 | #endif /* DDNA_MALWARE_CHECK_OPERA_HISTORY_HPP */ 36 | -------------------------------------------------------------------------------- /src/MalwareChecks/DDNA-MalwareCheck-ProfileEvents.cpp: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2024, DigiDNA 3 | * All rights reserved 4 | * 5 | * This Source Code Form is subject to the terms of the MVT License, v. 1.1. 6 | * If a copy of the MVT License was not distributed with this file, you can 7 | * obtain one at https://license.mvt.re/1.1/. 8 | ******************************************************************************/ 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | #define BACKUP_DOMAIN "SysSharedContainerDomain-systemgroup.com.apple.configurationprofiles" 15 | #define BACKUP_PATH "Library/ConfigurationProfiles/MCProfileEvents.plist" 16 | 17 | namespace DDNA 18 | { 19 | namespace MalwareChecks 20 | { 21 | std::string ProfileEvents::name( void ) const 22 | { 23 | return "Profile Events"; 24 | } 25 | 26 | std::string ProfileEvents::image( void ) const 27 | { 28 | return "ConfigurationProfile"; 29 | } 30 | 31 | std::vector< iOSDataset > ProfileEvents::iOSDatasets( void ) const 32 | { 33 | return 34 | { 35 | iOSDataset::customDatasetForTemporaryExtraction 36 | ( 37 | "SysSharedContainerDomain-systemgroup.com.apple.configurationprofiles", 38 | { FOLDER_WILDCARD + BACKUP_PATH }, 39 | { 40 | { 41 | BACKUP_DOMAIN, 42 | { 43 | BACKUP_PATH 44 | } 45 | } 46 | } 47 | ) 48 | }; 49 | } 50 | 51 | void ProfileEvents::run( Core::SafePointer< Backup > backup, const STIXIndicators & indicators, std::vector< MalwareCheckResult > & results, Core::Progress & progress ) 52 | { 53 | progress.message( Core::L10N( "MalwareActivityDetectionCheckProfileEvents_Info" ) ); 54 | 55 | if( !backup->mbdbRecordExists( BACKUP_DOMAIN, BACKUP_PATH ) ) 56 | { 57 | return; 58 | } 59 | 60 | std::string domain = BACKUP_DOMAIN; 61 | std::string path = BACKUP_PATH; 62 | 63 | Core::Error error; 64 | CF::Dictionary plist = MalwareActivityDetectionHelper::ExtractPropertyList( backup, domain, path, false, this->name(), results, error ); 65 | if( error.failure() ) 66 | { 67 | return; 68 | } 69 | 70 | CF::Array profileEvents = plist[ "ProfileEvents" ]; 71 | if( profileEvents.GetCount() == 0 ) 72 | { 73 | MalwareCheckResult( MalwareCheckResult::Type::Analyzer, MalwareCheckResult::Severity::Info, 0, this->name(), EVENT_ANALYSING_ITEMS_INFO, "No profile events to analyze.", results ); 74 | 75 | return; 76 | } 77 | 78 | MalwareCheckResult( MalwareCheckResult::Type::Analyzer, MalwareCheckResult::Severity::Info, 0, this->name(), EVENT_ANALYSING_ITEMS_INFO, "Analyzing " + std::to_string( profileEvents.GetCount() ) + " profile events.", results ); 79 | 80 | for( const CF::Dictionary & element: profileEvents ) 81 | { 82 | for( const auto & eventPair: element ) 83 | { 84 | CF::Dictionary event = eventPair.GetValue(); 85 | 86 | std::string profileId = CF::String( eventPair.GetKey() ); 87 | std::string process = CF::String( event[ "Process" ] ); 88 | std::string operation = CF::String( event[ "Operation" ] ); 89 | double timestamp = Core::DateTime( static_cast< int64_t >( CF::Date( event[ "Timestamp" ] ).GetValue() ), Core::DateTime::Epoch::Apple ); 90 | 91 | MalwareCheckResult res = indicators.checkProfile( profileId ); 92 | if( res.isSuspicious() ) 93 | { 94 | res.addToResults 95 | ( 96 | this->name(), 97 | timestamp, 98 | "Process: " + process + ", Operation: " + operation + ", Profile:" + profileId, 99 | results 100 | ); 101 | } 102 | 103 | res = indicators.checkProcess( process ); 104 | if( res.isSuspicious() ) 105 | { 106 | res.addToResults 107 | ( 108 | this->name(), 109 | timestamp, 110 | "Process: " + process + ", Operation: " + operation + ", Profile:" + profileId, 111 | results 112 | ); 113 | } 114 | } 115 | } 116 | } 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /src/MalwareChecks/DDNA-MalwareCheck-ProfileEvents.hpp: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2024, DigiDNA 3 | * All rights reserved 4 | * 5 | * This Source Code Form is subject to the terms of the MVT License, v. 1.1. 6 | * If a copy of the MVT License was not distributed with this file, you can 7 | * obtain one at https://license.mvt.re/1.1/. 8 | ******************************************************************************/ 9 | 10 | #ifndef DDNA_MALWARECHECK_PROFILEEVENTS_HPP 11 | #define DDNA_MALWARECHECK_PROFILEEVENTS_HPP 12 | 13 | #include 14 | 15 | namespace DDNA 16 | { 17 | namespace MalwareChecks 18 | { 19 | class ProfileEvents: public MalwareCheck 20 | { 21 | public: 22 | 23 | std::string name( void ) const override; 24 | std::string image( void ) const override; 25 | std::vector< iOSDataset > iOSDatasets( void ) const override; 26 | void run( Core::SafePointer< Backup > backup, const STIXIndicators & indicators, std::vector< MalwareCheckResult > & results, Core::Progress & progress ) override; 27 | }; 28 | } 29 | } 30 | #endif /* DDNA_MALWARECHECK_PROFILEEVENTS_HPP */ 31 | -------------------------------------------------------------------------------- /src/MalwareChecks/DDNA-MalwareCheck-SMS.cpp: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2021, DigiDNA 3 | * All rights reserved 4 | * 5 | * This Source Code Form is subject to the terms of the MVT License, v. 1.1. 6 | * If a copy of the MVT License was not distributed with this file, you can 7 | * obtain one at https://license.mvt.re/1.1/. 8 | ******************************************************************************/ 9 | 10 | #include 11 | #include 12 | 13 | #define BACKUP_DOMAIN "HomeDomain" 14 | #define BACKUP_PATH "Library/SMS/sms.db" 15 | 16 | namespace DDNA 17 | { 18 | namespace MalwareChecks 19 | { 20 | std::string SMS::name( void ) const 21 | { 22 | return "Messages"; 23 | } 24 | 25 | std::string SMS::image( void ) const 26 | { 27 | return "com.apple.MobileSMS"; 28 | } 29 | 30 | std::vector< iOSDataset > SMS::iOSDatasets( void ) const 31 | { 32 | return 33 | { 34 | iOSDataset::customDatasetForTemporaryExtraction 35 | ( 36 | "com.apple.MobileSMS.messages", 37 | { FOLDER_WILDCARD + BACKUP_PATH }, 38 | { 39 | { 40 | BACKUP_DOMAIN, 41 | { 42 | BACKUP_PATH 43 | } 44 | } 45 | } 46 | ) 47 | }; 48 | } 49 | 50 | void SMS::run( Core::SafePointer< Backup > backup, const STIXIndicators & indicators, std::vector< MalwareCheckResult > & results, Core::Progress & progress ) 51 | { 52 | progress.message( Core::L10N( "MalwareActivityDetectionCheckSMS_Info" ) ); 53 | 54 | std::string domain = BACKUP_DOMAIN; 55 | std::string path = BACKUP_PATH; 56 | std::shared_ptr< sqlite3 > db = MalwareActivityDetectionHelper::ExtractAndOpenSQLiteDB( backup, domain, path, this->name(), results ); 57 | if( db == nullptr ) 58 | { 59 | return; 60 | } 61 | 62 | SQLite::QueryDictionary queryDict = db.get(); 63 | std::string query = 64 | R"query( 65 | 66 | SELECT 67 | message.*, 68 | handle.id as "phone_number" 69 | FROM message, handle 70 | WHERE handle.rowid = message.handle_id; 71 | 72 | )query"; 73 | 74 | std::vector< CF::Dictionary > records = queryDict.executeQuery( query ); 75 | if( records.size() == 0 ) 76 | { 77 | if( queryDict.errorMessage().length() > 0 ) 78 | { 79 | MalwareCheckResult( MalwareCheckResult::Type::Analyzer, MalwareCheckResult::Severity::Warning, 0, this->name(), EVENT_DB_QUERY_ERROR, "'" + domain + "/" + path + "' cannot be parsed (" + queryDict.errorMessage() + ").", results ); 80 | } 81 | else 82 | { 83 | MalwareCheckResult( MalwareCheckResult::Type::Analyzer, MalwareCheckResult::Severity::Info, 0, this->name(), EVENT_ANALYSING_ITEMS_INFO, "No messages to analyze.", results ); 84 | } 85 | 86 | return; 87 | } 88 | 89 | MalwareCheckResult( MalwareCheckResult::Type::Analyzer, MalwareCheckResult::Severity::Info, 0, this->name(), EVENT_ANALYSING_ITEMS_INFO, "Analyzing " + std::to_string( records.size() ) + " messages.", results ); 90 | 91 | for( const auto & record: records ) 92 | { 93 | std::string alertOld = "ALERT: State-sponsored attackers may be targeting your iPhone"; 94 | std::string alertNew = "ALERT: Apple detected a targeted mercenary spyware attack against your iPhone"; 95 | std::string text = CF::String( record[ "text" ] ); 96 | int64_t nanosecs = CF::Number( record[ "date" ] ); 97 | double timestamp = CF::Number( Core::DateTime( nanosecs / 1000000000, Core::DateTime::Epoch::Apple ) ); 98 | 99 | if( text.find( alertOld ) == 0 || text.find( alertNew ) == 0 ) 100 | { 101 | MalwareCheckResult 102 | ( 103 | MalwareCheckResult::Type::Device, 104 | MalwareCheckResult::Severity::Critical, 105 | timestamp, 106 | this->name(), 107 | EVENT_SUSCPICIOUS_FILENAME, 108 | "Apple warning about state-sponsored attack", 109 | results 110 | ); 111 | } 112 | 113 | for( const auto & url: MalwareActivityDetectionHelper::FindLinks( text ) ) 114 | { 115 | bool received = CF::Number( record[ "is_from_me" ] ) == 0; 116 | auto check = indicators.checkURL( url, this->name(), results ); 117 | 118 | auto res = std::get< 0 >( check ); 119 | if( res.isSuspicious() ) 120 | { 121 | auto finalURL = std::get< 1 >( check ); 122 | std::string phone = CF::String( record[ "phone_number" ] ); 123 | 124 | std::string data; 125 | if( received ) 126 | { 127 | data = "Message from '" + phone + "' with url: '" + finalURL + "'."; 128 | } 129 | else 130 | { 131 | data = "Message to '" + phone + "' with url: '" + finalURL + "'."; 132 | } 133 | 134 | res.addToResults 135 | ( 136 | this->name(), 137 | timestamp, 138 | data, 139 | results 140 | ); 141 | } 142 | } 143 | } 144 | } 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /src/MalwareChecks/DDNA-MalwareCheck-SMS.hpp: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2021, DigiDNA 3 | * All rights reserved 4 | * 5 | * This Source Code Form is subject to the terms of the MVT License, v. 1.1. 6 | * If a copy of the MVT License was not distributed with this file, you can 7 | * obtain one at https://license.mvt.re/1.1/. 8 | ******************************************************************************/ 9 | 10 | #ifndef DDNA_MALWARE_CHECK_SMS_HPP 11 | #define DDNA_MALWARE_CHECK_SMS_HPP 12 | 13 | #include 14 | 15 | namespace DDNA 16 | { 17 | namespace MalwareChecks 18 | { 19 | class SMS: public MalwareCheck 20 | { 21 | public: 22 | 23 | std::string name( void ) const override; 24 | std::string image( void ) const override; 25 | std::vector< iOSDataset > iOSDatasets( void ) const override; 26 | void run( Core::SafePointer< Backup > backup, const STIXIndicators & indicators, std::vector< MalwareCheckResult > & results, Core::Progress & progress ) override; 27 | }; 28 | } 29 | } 30 | 31 | #endif /* DDNA_MALWARE_CHECK_SMS_HPP */ 32 | -------------------------------------------------------------------------------- /src/MalwareChecks/DDNA-MalwareCheck-SMSAttachment.cpp: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2021, DigiDNA 3 | * All rights reserved 4 | * 5 | * This Source Code Form is subject to the terms of the MVT License, v. 1.1. 6 | * If a copy of the MVT License was not distributed with this file, you can 7 | * obtain one at https://license.mvt.re/1.1/. 8 | ******************************************************************************/ 9 | 10 | #include 11 | #include 12 | 13 | #define BACKUP_DOMAIN "HomeDomain" 14 | #define BACKUP_PATH "Library/SMS/sms.db" 15 | 16 | namespace DDNA 17 | { 18 | namespace MalwareChecks 19 | { 20 | std::string SMSAttachment::name( void ) const 21 | { 22 | return "Messages Attachments"; 23 | } 24 | 25 | std::string SMSAttachment::image( void ) const 26 | { 27 | return "com.apple.MobileSMS"; 28 | } 29 | 30 | std::vector< iOSDataset > SMSAttachment::iOSDatasets( void ) const 31 | { 32 | return 33 | { 34 | iOSDataset::customDatasetForTemporaryExtraction 35 | ( 36 | "com.apple.MobileSMS.attachment", 37 | { FOLDER_WILDCARD + BACKUP_PATH }, 38 | { 39 | { 40 | BACKUP_DOMAIN, 41 | { 42 | BACKUP_PATH 43 | } 44 | } 45 | } 46 | ) 47 | }; 48 | } 49 | 50 | void SMSAttachment::run( Core::SafePointer< Backup > backup, const STIXIndicators & indicators, std::vector< MalwareCheckResult > & results, Core::Progress & progress ) 51 | { 52 | progress.message( Core::L10N( "MalwareActivityDetectionCheckSMSAttachment_Info" ) ); 53 | 54 | std::string domain = BACKUP_DOMAIN; 55 | std::string path = BACKUP_PATH; 56 | std::shared_ptr< sqlite3 > db = MalwareActivityDetectionHelper::ExtractAndOpenSQLiteDB( backup, domain, path, this->name(), results ); 57 | if( db == nullptr ) 58 | { 59 | return; 60 | } 61 | 62 | SQLite::QueryDictionary queryDict = db.get(); 63 | std::string query = 64 | R"query( 65 | 66 | SELECT 67 | attachment.ROWID as "attachment_id", 68 | attachment.*, 69 | message.service as "service", 70 | handle.id as "phone_number" 71 | FROM attachment 72 | LEFT JOIN message_attachment_join ON message_attachment_join.attachment_id = attachment.ROWID 73 | LEFT JOIN message ON message.ROWID = message_attachment_join.message_id 74 | LEFT JOIN handle ON handle.ROWID = message.handle_id 75 | 76 | )query"; 77 | 78 | queryDict.addTransformerForColumn( "created_date", SQLite::TransformTimestampAppleSecondsToUnix ); 79 | 80 | std::vector< CF::Dictionary > records = queryDict.executeQuery( query ); 81 | if( records.size() == 0 ) 82 | { 83 | if( queryDict.errorMessage().length() > 0 ) 84 | { 85 | MalwareCheckResult( MalwareCheckResult::Type::Analyzer, MalwareCheckResult::Severity::Warning, 0, this->name(), EVENT_DB_QUERY_ERROR, "'" + domain + "/" + path + "' cannot be parsed (" + queryDict.errorMessage() + ").", results ); 86 | } 87 | else 88 | { 89 | MalwareCheckResult( MalwareCheckResult::Type::Analyzer, MalwareCheckResult::Severity::Info, 0, this->name(), EVENT_ANALYSING_ITEMS_INFO, "No message attachments to analyze.", results ); 90 | } 91 | 92 | return; 93 | } 94 | 95 | MalwareCheckResult( MalwareCheckResult::Type::Analyzer, MalwareCheckResult::Severity::Info, 0, this->name(), EVENT_ANALYSING_ITEMS_INFO, "Analyzing " + std::to_string( records.size() ) + " message attachments.", results ); 96 | 97 | for( const auto & record: records ) 98 | { 99 | uint64_t received = CF::Number( record[ "is_outgoing" ] ); 100 | std::string filename = CF::String( record[ "filename" ] ); 101 | double timestamp = CF::Number( record[ "created_date" ] ); 102 | 103 | MalwareCheckResult res = indicators.checkFileName( filename ); 104 | if( res.isSuspicious() ) 105 | { 106 | 107 | res.addToResults 108 | ( 109 | this->name(), 110 | timestamp, 111 | "Found a suspicious message attachment: '" + filename + "'.", 112 | results 113 | ); 114 | } 115 | 116 | if( filename.length() == 0 || received != 1 || filename.find( "/var/tmp/" ) != 0 || filename.substr( filename.length() -2, 2 ) != "-1" ) 117 | { 118 | continue; 119 | } 120 | 121 | MalwareCheckResult 122 | ( 123 | MalwareCheckResult::Type::Device, 124 | MalwareCheckResult::Severity::Critical, 125 | timestamp, 126 | this->name(), 127 | EVENT_SUSCPICIOUS_FILENAME, 128 | "Found a suspicious message attachment: '" + filename + "'.", 129 | results 130 | ); 131 | } 132 | } 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /src/MalwareChecks/DDNA-MalwareCheck-SMSAttachment.hpp: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2021, DigiDNA 3 | * All rights reserved 4 | * 5 | * This Source Code Form is subject to the terms of the MVT License, v. 1.1. 6 | * If a copy of the MVT License was not distributed with this file, you can 7 | * obtain one at https://license.mvt.re/1.1/. 8 | ******************************************************************************/ 9 | 10 | #ifndef DDNA_MALWARE_CHECK_SMS_ATTACHMENT_HPP 11 | #define DDNA_MALWARE_CHECK_SMS_ATTACHMENT_HPP 12 | 13 | #include 14 | 15 | namespace DDNA 16 | { 17 | namespace MalwareChecks 18 | { 19 | class SMSAttachment: public MalwareCheck 20 | { 21 | public: 22 | 23 | std::string name( void ) const override; 24 | std::string image( void ) const override; 25 | std::vector< iOSDataset > iOSDatasets( void ) const override; 26 | void run( Core::SafePointer< Backup > backup, const STIXIndicators & indicators, std::vector< MalwareCheckResult > & results, Core::Progress & progress ) override; 27 | }; 28 | } 29 | } 30 | 31 | #endif /* DDNA_MALWARE_CHECK_SMS_ATTACHMENT_HPP */ 32 | -------------------------------------------------------------------------------- /src/MalwareChecks/DDNA-MalwareCheck-SafariBrowserState.cpp: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2021, DigiDNA 3 | * All rights reserved 4 | * 5 | * This Source Code Form is subject to the terms of the MVT License, v. 1.1. 6 | * If a copy of the MVT License was not distributed with this file, you can 7 | * obtain one at https://license.mvt.re/1.1/. 8 | ******************************************************************************/ 9 | 10 | #include 11 | #include 12 | 13 | /* 14 | * BACKUP_DOMAIN_1 is for iOS < 13 15 | * Database path is the same for both domains 16 | */ 17 | #define BACKUP_DOMAIN_1 "HomeDomain" 18 | #define BACKUP_DOMAIN_2 "AppDomain-com.apple.mobilesafari" 19 | #define BACKUP_PATH "Library/Safari/BrowserState.db" 20 | 21 | namespace DDNA 22 | { 23 | namespace MalwareChecks 24 | { 25 | std::string SafariBrowserState::name( void ) const 26 | { 27 | return "Safari Browser State"; 28 | } 29 | 30 | std::string SafariBrowserState::image( void ) const 31 | { 32 | return "com.apple.mobilesafari"; 33 | } 34 | 35 | std::vector< iOSDataset > SafariBrowserState::iOSDatasets( void ) const 36 | { 37 | return 38 | { 39 | iOSDataset::customDatasetForTemporaryExtraction 40 | ( 41 | "com.apple.mobilesafari.browserstate", 42 | { FOLDER_WILDCARD + BACKUP_PATH }, 43 | { 44 | { 45 | BACKUP_DOMAIN_1, 46 | { 47 | BACKUP_PATH 48 | } 49 | }, 50 | { 51 | BACKUP_DOMAIN_2, 52 | { 53 | BACKUP_PATH 54 | } 55 | } 56 | } 57 | ) 58 | }; 59 | } 60 | 61 | void SafariBrowserState::run( Core::SafePointer< Backup > backup, const STIXIndicators & indicators, std::vector< MalwareCheckResult > & results, Core::Progress & progress ) 62 | { 63 | progress.message( Core::L10N( "MalwareActivityDetectionCheckSafariBrowserState_Info" ) ); 64 | 65 | // Since iOS 13, Safari Browser State is not accessible without encryption 66 | if( backup->iOSMajorVersion() > 12 && !backup->encrypted() ) 67 | { 68 | MalwareCheckResult( MalwareCheckResult::Type::Analyzer, MalwareCheckResult::Severity::Warning, 0, this->name(), EVENT_BACKUP_FILE_NOT_FOUND, std::string( BACKUP_PATH ) + " is only available when backup encryption is enabled.", results ); 69 | 70 | return; 71 | } 72 | 73 | std::string domain = ( backup->iOSMajorVersion() < 13 ) ? BACKUP_DOMAIN_2 : BACKUP_DOMAIN_1; 74 | std::string path = BACKUP_PATH; 75 | std::shared_ptr< sqlite3 > db = MalwareActivityDetectionHelper::ExtractAndOpenSQLiteDB( backup, domain, path, this->name(), results ); 76 | if( db == nullptr ) 77 | { 78 | return; 79 | } 80 | 81 | SQLite::QueryDictionary queryDict = db.get(); 82 | std::string query = 83 | R"query( 84 | 85 | SELECT 86 | tabs.title, 87 | tabs.url, 88 | tabs.user_visible_url, 89 | tabs.last_viewed_time, 90 | tab_sessions.session_data 91 | FROM tabs 92 | JOIN tab_sessions ON tabs.uuid = tab_sessions.tab_uuid 93 | ORDER BY tabs.last_viewed_time; 94 | 95 | )query"; 96 | 97 | queryDict.addTransformerForColumn( "last_viewed_time", SQLite::TransformTimestampAppleSecondsToUnix ); 98 | 99 | std::vector< CF::Dictionary > records = queryDict.executeQuery( query ); 100 | if( records.size() == 0 ) 101 | { 102 | if( queryDict.errorMessage().length() > 0 ) 103 | { 104 | MalwareCheckResult( MalwareCheckResult::Type::Analyzer, MalwareCheckResult::Severity::Warning, 0, this->name(), EVENT_DB_QUERY_ERROR, "'" + domain + "/" + path + "' cannot be parsed (" + queryDict.errorMessage() + ").", results ); 105 | } 106 | else 107 | { 108 | MalwareCheckResult( MalwareCheckResult::Type::Analyzer, MalwareCheckResult::Severity::Info, 0, this->name(), EVENT_ANALYSING_ITEMS_INFO, "No Safari sessions to analyze.", results ); 109 | } 110 | 111 | return; 112 | } 113 | 114 | MalwareCheckResult( MalwareCheckResult::Type::Analyzer, MalwareCheckResult::Severity::Info, 0, this->name(), EVENT_ANALYSING_ITEMS_INFO, "Analyzing " + std::to_string( records.size() ) + " Safari sessions.", results ); 115 | 116 | for( const auto & record: records ) 117 | { 118 | CF::Data data = record[ "session_data" ]; 119 | if( data.GetLength() <= 4 ) 120 | { 121 | continue;; 122 | } 123 | 124 | data.DeleteBytes( CFRangeMake( 0, 4 ) ); 125 | 126 | CF::Dictionary plist = CF::Dictionary::FromPropertyListData( data ); 127 | CF::Dictionary history = plist[ "SessionHistory" ]; 128 | CF::Array entries = history[ "SessionHistoryEntries" ]; 129 | std::string title = CF::String( record[ "title" ] ); 130 | double timestamp = CF::Number( record[ "last_viewed_time" ] ); 131 | 132 | for( CF::Dictionary entry: entries ) 133 | { 134 | if( entry.IsValid() == false ) 135 | { 136 | continue; 137 | } 138 | 139 | std::string url = CF::String( entry[ "SessionHistoryEntryURL" ] ); 140 | auto check = indicators.checkURL( url, this->name(), results ); 141 | 142 | auto res = std::get< 0 >( check ); 143 | if( res.isSuspicious() ) 144 | { 145 | auto finalURL = std::get< 1 >( check ); 146 | 147 | res.addToResults 148 | ( 149 | this->name(), 150 | timestamp, 151 | "Safari visit to '" + finalURL + "' (tab title: " + title + ").", 152 | results 153 | ); 154 | } 155 | } 156 | 157 | std::string url = CF::String( record[ "url" ] ); 158 | auto check = indicators.checkURL( url, this->name(), results ); 159 | 160 | auto res = std::get< 0 >( check ); 161 | if( res.isSuspicious() ) 162 | { 163 | auto finalURL = std::get< 1 >( check ); 164 | 165 | res.addToResults 166 | ( 167 | this->name(), 168 | timestamp, 169 | "Safari visit to '" + finalURL + "' (tab title: " + title + ").", 170 | results 171 | ); 172 | } 173 | } 174 | } 175 | } 176 | } 177 | -------------------------------------------------------------------------------- /src/MalwareChecks/DDNA-MalwareCheck-SafariBrowserState.hpp: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2021, DigiDNA 3 | * All rights reserved 4 | * 5 | * This Source Code Form is subject to the terms of the MVT License, v. 1.1. 6 | * If a copy of the MVT License was not distributed with this file, you can 7 | * obtain one at https://license.mvt.re/1.1/. 8 | ******************************************************************************/ 9 | 10 | #ifndef DDNA_MALWARE_CHECK_SAFARI_BROWSER_STATE_HPP 11 | #define DDNA_MALWARE_CHECK_SAFARI_BROWSER_STATE_HPP 12 | 13 | #include 14 | 15 | namespace DDNA 16 | { 17 | namespace MalwareChecks 18 | { 19 | class SafariBrowserState: public MalwareCheck 20 | { 21 | public: 22 | 23 | std::string name( void ) const override; 24 | std::string image( void ) const override; 25 | std::vector< iOSDataset > iOSDatasets( void ) const override; 26 | void run( Core::SafePointer< Backup > backup, const STIXIndicators & indicators, std::vector< MalwareCheckResult > & results, Core::Progress & progress ) override; 27 | }; 28 | } 29 | } 30 | 31 | #endif /* DDNA_MALWARE_CHECK_SAFARI_BROWSER_STATE_HPP */ 32 | -------------------------------------------------------------------------------- /src/MalwareChecks/DDNA-MalwareCheck-SafariHistory.hpp: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2021, DigiDNA 3 | * All rights reserved 4 | * 5 | * This Source Code Form is subject to the terms of the MVT License, v. 1.1. 6 | * If a copy of the MVT License was not distributed with this file, you can 7 | * obtain one at https://license.mvt.re/1.1/. 8 | ******************************************************************************/ 9 | 10 | #ifndef DDNA_MALWARE_CHECK_SAFARI_HISTORY_HPP 11 | #define DDNA_MALWARE_CHECK_SAFARI_HISTORY_HPP 12 | 13 | #include 14 | 15 | namespace DDNA 16 | { 17 | namespace MalwareChecks 18 | { 19 | class SafariHistory: public MalwareCheck 20 | { 21 | public: 22 | 23 | std::string name( void ) const override; 24 | std::string image( void ) const override; 25 | std::vector< iOSDataset > iOSDatasets( void ) const override; 26 | void run( Core::SafePointer< Backup > backup, const STIXIndicators & indicators, std::vector< MalwareCheckResult > & results, Core::Progress & progress ) override; 27 | }; 28 | } 29 | } 30 | 31 | #endif /* DDNA_MALWARE_CHECK_SAFARI_HISTORY_HPP */ 32 | -------------------------------------------------------------------------------- /src/MalwareChecks/DDNA-MalwareCheck-Shortcuts.cpp: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2024, DigiDNA 3 | * All rights reserved 4 | * 5 | * This Source Code Form is subject to the terms of the MVT License, v. 1.1. 6 | * If a copy of the MVT License was not distributed with this file, you can 7 | * obtain one at https://license.mvt.re/1.1/. 8 | ******************************************************************************/ 9 | 10 | #include 11 | #include 12 | 13 | #define BACKUP_DOMAIN "HomeDomain" 14 | #define BACKUP_PATH "Library/Shortcuts/Shortcuts.sqlite" 15 | 16 | namespace DDNA 17 | { 18 | namespace MalwareChecks 19 | { 20 | std::string Shortcuts::name( void ) const 21 | { 22 | return "Shortcuts"; 23 | } 24 | 25 | std::string Shortcuts::image( void ) const 26 | { 27 | return "com.apple.shortcuts"; 28 | } 29 | 30 | std::vector< iOSDataset > Shortcuts::iOSDatasets( void ) const 31 | { 32 | return 33 | { 34 | iOSDataset::customDatasetForTemporaryExtraction 35 | ( 36 | "com.apple.shortcuts", 37 | { FOLDER_WILDCARD + BACKUP_PATH }, 38 | { 39 | { 40 | BACKUP_DOMAIN, 41 | { 42 | BACKUP_PATH 43 | } 44 | } 45 | } 46 | ) 47 | }; 48 | } 49 | 50 | void Shortcuts::run( Core::SafePointer< Backup > backup, const STIXIndicators & indicators, std::vector< MalwareCheckResult > & results, Core::Progress & progress ) 51 | { 52 | progress.message( Core::L10N( "MalwareActivityDetectionCheckShortcuts_Info" ) ); 53 | 54 | std::string domain = BACKUP_DOMAIN; 55 | std::string path = BACKUP_PATH; 56 | std::shared_ptr< sqlite3 > db = MalwareActivityDetectionHelper::ExtractAndOpenSQLiteDB( backup, domain, path, this->name(), results ); 57 | if( db == nullptr ) 58 | { 59 | return; 60 | } 61 | 62 | SQLite::QueryDictionary queryDict = db.get(); 63 | // brute force try 3 different db schemes from v3 to v1 64 | std::string query = 65 | R"query( 66 | 67 | SELECT 68 | ZSHORTCUT.Z_PK as shortcut_id, 69 | ZSHORTCUT.ZNAME as shortcut_name, 70 | ZSHORTCUT.ZCREATIONDATE as created_date, 71 | ZSHORTCUT.ZMODIFICATIONDATE as modified_date, 72 | ZSHORTCUT.ZACTIONSDESCRIPTION as description, 73 | ZSHORTCUTACTIONS.ZDATA as action_data 74 | FROM ZSHORTCUT 75 | LEFT JOIN ZSHORTCUTACTIONS ON ZSHORTCUTACTIONS.ZSHORTCUT == ZSHORTCUT.Z_PK; 76 | 77 | )query"; 78 | 79 | queryDict.addTransformerForColumn( "created_date", SQLite::TransformTimestampAppleSecondsToUnix ); 80 | queryDict.addTransformerForColumn( "modified_date", SQLite::TransformTimestampAppleSecondsToUnix ); 81 | 82 | std::vector< CF::Dictionary > records = queryDict.executeQuery( query ); 83 | if( records.size() == 0 ) 84 | { 85 | if( queryDict.errorMessage().length() > 0 ) 86 | { 87 | MalwareCheckResult( MalwareCheckResult::Type::Analyzer, MalwareCheckResult::Severity::Warning, 0, this->name(), EVENT_DB_QUERY_ERROR, "'" + domain + "/" + path + "' cannot be parsed (" + queryDict.errorMessage() + ").", results ); 88 | } 89 | else 90 | { 91 | MalwareCheckResult( MalwareCheckResult::Type::Analyzer, MalwareCheckResult::Severity::Info, 0, this->name(), EVENT_ANALYSING_ITEMS_INFO, "No ahortcuts to analyze.", results ); 92 | } 93 | 94 | return; 95 | } 96 | 97 | MalwareCheckResult( MalwareCheckResult::Type::Analyzer, MalwareCheckResult::Severity::Info, 0, this->name(), EVENT_ANALYSING_ITEMS_INFO, "Analyzing " + std::to_string( records.size() ) + " shortcuts.", results ); 98 | 99 | for( const CF::Dictionary & record: records ) 100 | { 101 | CF::Array actionData = CF::Array::FromPropertyListData( record[ "action_data" ] ); 102 | 103 | std::vector< std::string > extractedUrls; 104 | for( const CF::Dictionary & actionEntry: actionData ) 105 | { 106 | CF::Dictionary action; 107 | 108 | action << CF::Pair( "identifier", actionEntry[ "WFWorkflowActionIdentifier" ] ) 109 | << CF::Pair( "parameters", actionEntry[ "WFWorkflowActionParameters" ] ); 110 | 111 | // parse the values of the parameters dictionary 112 | for( const CF::Pair & parameter: CF::Dictionary( action[ "parameters" ] ) ) 113 | { 114 | std::vector< std::string > urls = MalwareActivityDetectionHelper::FindLinks( CF::String( parameter.GetValue() ) ); 115 | 116 | extractedUrls.insert( extractedUrls.end(), urls.begin(), urls.end() ); 117 | } 118 | } 119 | 120 | for( const auto & actionURL: extractedUrls ) 121 | { 122 | auto check = indicators.checkURL( actionURL, this->name(), results ); 123 | 124 | auto res = std::get< 0 >( check ); 125 | if( res.isSuspicious() ) 126 | { 127 | std::string finalURL = std::get< 1 >( check ); 128 | double creationDate = CF::Number( record[ "created_date" ] ); 129 | double timestamp = MalwareActivityDetectionHelper::AppleTimestampToUnixTimestamp( creationDate ); 130 | 131 | res.addToResults 132 | ( 133 | this->name(), 134 | timestamp, 135 | "Found a suspicious url " + finalURL + " in Shortcut " + CF::String( record[ "shortcut_name "] ).GetValue(), 136 | results 137 | ); 138 | } 139 | } 140 | } 141 | } 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /src/MalwareChecks/DDNA-MalwareCheck-Shortcuts.hpp: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2024, DigiDNA 3 | * All rights reserved 4 | * 5 | * This Source Code Form is subject to the terms of the MVT License, v. 1.1. 6 | * If a copy of the MVT License was not distributed with this file, you can 7 | * obtain one at https://license.mvt.re/1.1/. 8 | ******************************************************************************/ 9 | 10 | #ifndef DDNA_MALWARECHECK_SHORTCUTS_HPP 11 | #define DDNA_MALWARECHECK_SHORTCUTS_HPP 12 | 13 | #include 14 | 15 | namespace DDNA 16 | { 17 | namespace MalwareChecks 18 | { 19 | class Shortcuts: public MalwareCheck 20 | { 21 | public: 22 | 23 | std::string name( void ) const override; 24 | std::string image( void ) const override; 25 | std::vector< iOSDataset > iOSDatasets( void ) const override; 26 | void run( Core::SafePointer< Backup > backup, const STIXIndicators & indicators, std::vector< MalwareCheckResult > & results, Core::Progress & progress ) override; 27 | }; 28 | } 29 | } 30 | #endif /* DDNA_MALWARECHECK_SHORTCUTS_HPP */ 31 | -------------------------------------------------------------------------------- /src/MalwareChecks/DDNA-MalwareCheck-TCC.hpp: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2024, DigiDNA 3 | * All rights reserved 4 | * 5 | * This Source Code Form is subject to the terms of the MVT License, v. 1.1. 6 | * If a copy of the MVT License was not distributed with this file, you can 7 | * obtain one at https://license.mvt.re/1.1/. 8 | ******************************************************************************/ 9 | 10 | #ifndef DDNA_MALWARECHECK_TCC_HPP 11 | #define DDNA_MALWARECHECK_TCC_HPP 12 | 13 | #include 14 | 15 | namespace DDNA 16 | { 17 | namespace MalwareChecks 18 | { 19 | class TCC: public MalwareCheck 20 | { 21 | public: 22 | 23 | std::string name( void ) const override; 24 | std::string image( void ) const override; 25 | std::vector< iOSDataset > iOSDatasets( void ) const override; 26 | void run( Core::SafePointer< Backup > backup, const STIXIndicators & indicators, std::vector< MalwareCheckResult > & results, Core::Progress & progress ) override; 27 | 28 | private: 29 | static std::string _requestDescription( const std::string & service, const std::string & client ); 30 | static std::string _authValueDescriptionOld( int64_t value ); 31 | static std::string _authValueDescription( int64_t value ); 32 | static std::string _authReasonDescription( int64_t value ); 33 | }; 34 | } 35 | } 36 | #endif /* DDNA_MALWARECHECK_TCC_HPP */ 37 | -------------------------------------------------------------------------------- /src/MalwareChecks/DDNA-MalwareCheck-Viber.cpp: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2021, DigiDNA 3 | * All rights reserved 4 | * 5 | * This Source Code Form is subject to the terms of the MVT License, v. 1.1. 6 | * If a copy of the MVT License was not distributed with this file, you can 7 | * obtain one at https://license.mvt.re/1.1/. 8 | ******************************************************************************/ 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | #define BACKUP_DOMAIN "AppDomainGroup-group.viber.share.container" 15 | #define BACKUP_PATH "com.viber/database/Contacts.data" 16 | 17 | namespace DDNA 18 | { 19 | namespace MalwareChecks 20 | { 21 | std::string Viber::name( void ) const 22 | { 23 | return "Viber"; 24 | } 25 | 26 | std::string Viber::image( void ) const 27 | { 28 | return MalwareActivityDetectionHelper::AppIconPath( "com.viber" ); 29 | } 30 | 31 | std::vector< iOSDataset > Viber::iOSDatasets( void ) const 32 | { 33 | return 34 | { 35 | iOSDataset::customDatasetForTemporaryExtraction 36 | ( 37 | "net.Viber.Viber", 38 | { FOLDER_WILDCARD + BACKUP_PATH }, 39 | { 40 | { 41 | BACKUP_DOMAIN, 42 | { 43 | BACKUP_PATH 44 | } 45 | } 46 | } 47 | ) 48 | }; 49 | } 50 | 51 | void Viber::run( Core::SafePointer< Backup > backup, const STIXIndicators & indicators, std::vector< MalwareCheckResult > & results, Core::Progress & progress ) 52 | { 53 | progress.message( Core::L10N( "MalwareActivityDetectionCheckViber_Info" ) ); 54 | 55 | if( !backup->mbdbRecordExists( BACKUP_DOMAIN, BACKUP_PATH ) ) 56 | { 57 | return; 58 | } 59 | 60 | std::shared_ptr< sqlite3 > db = MalwareActivityDetectionHelper::ExtractAndOpenSQLiteDB( backup, BACKUP_DOMAIN, BACKUP_PATH, this->name(), results ); 61 | if( db == nullptr ) 62 | { 63 | return; 64 | } 65 | 66 | this->analyzeMessages( db.get(), indicators, results, BACKUP_DOMAIN, BACKUP_PATH ); 67 | } 68 | 69 | void Viber::analyzeMessages( sqlite3 * db, const STIXIndicators & indicators, std::vector< MalwareCheckResult > & results, const std::string & domain, const std::string & path ) const 70 | { 71 | std::string query = 72 | R"query( 73 | 74 | SELECT 75 | ZVIBERMESSAGE.*, 76 | ZPHONENUMBER.ZPHONE 77 | FROM ZVIBERMESSAGE 78 | LEFT JOIN ZPHONENUMBER ON ZPHONENUMBER.Z_PK = ZVIBERMESSAGE.Z_PK; 79 | 80 | )query"; 81 | 82 | SQLite::QueryDictionary queryDict = db; 83 | queryDict.addTransformerForColumn( "ZDATE", SQLite::TransformTimestampAppleSecondsToUnix ); 84 | 85 | std::vector< CF::Dictionary > records = queryDict.executeQuery( query ); 86 | if( records.size() == 0 ) 87 | { 88 | if( queryDict.errorMessage().length() > 0 ) 89 | { 90 | MalwareCheckResult( MalwareCheckResult::Type::Analyzer, MalwareCheckResult::Severity::Warning, 0, this->name(), EVENT_DB_QUERY_ERROR, "'" + domain + "/" + path + "' cannot be parsed (" + queryDict.errorMessage() + ").", results ); 91 | } 92 | else 93 | { 94 | MalwareCheckResult( MalwareCheckResult::Type::Analyzer, MalwareCheckResult::Severity::Info, 0, this->name(), EVENT_ANALYSING_ITEMS_INFO, "No Viber messages to analyze.", results ); 95 | } 96 | 97 | return; 98 | } 99 | 100 | MalwareCheckResult( MalwareCheckResult::Type::Analyzer, MalwareCheckResult::Severity::Info, 0, this->name(), EVENT_ANALYSING_ITEMS_INFO, "Analyzing " + std::to_string( records.size() ) + " Viber messages.", results ); 101 | 102 | for( const auto & record: records ) 103 | { 104 | std::vector< std::string > fieldsWithLinks = { "ZTEXT", "ZCLIENTMETADATA" }; 105 | for( const auto & fieldName: fieldsWithLinks ) 106 | { 107 | std::string text; 108 | 109 | if( fieldName == "ZTEXT" ) 110 | { 111 | text = this->getText( CF::String( record[ fieldName ] ) ); 112 | } 113 | else 114 | { 115 | text = CF::String( record[ fieldName ] ); 116 | } 117 | 118 | for( const auto & url: MalwareActivityDetectionHelper::FindLinks( text ) ) 119 | { 120 | auto check = indicators.checkURL( url, this->name(), results ); 121 | 122 | auto res = std::get< 0 >( check ); 123 | if( res.isSuspicious() ) 124 | { 125 | auto finalURL = std::get< 1 >( check ); 126 | double timestamp = CF::Number( record[ "ZDATE" ] ); 127 | 128 | res.addToResults 129 | ( 130 | this->name(), 131 | timestamp, 132 | "Found Viber message with url: '" + finalURL + "'.", 133 | results 134 | ); 135 | } 136 | } 137 | } 138 | } 139 | 140 | return; 141 | } 142 | 143 | std::string Viber::getText( const std::string & data ) const 144 | { 145 | CkJsonObject json; 146 | 147 | json.put_Utf8( true ); 148 | 149 | if( json.Load( data.c_str() ) == false ) 150 | { 151 | return data; 152 | } 153 | 154 | CF::Dictionary dict = JSON::jsonToDictionary( json ); 155 | 156 | if( dict.IsValid() == false ) 157 | { 158 | return data; 159 | } 160 | 161 | if( CF::String( dict[ "Type" ] ) != "txt" ) 162 | { 163 | return data; 164 | } 165 | 166 | CF::String text = dict[ "Text" ]; 167 | 168 | if( text.IsValid() == false || text.GetLength() == 0 ) 169 | { 170 | return data; 171 | } 172 | 173 | return text; 174 | } 175 | } 176 | } 177 | -------------------------------------------------------------------------------- /src/MalwareChecks/DDNA-MalwareCheck-Viber.hpp: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2021, DigiDNA 3 | * All rights reserved 4 | * 5 | * This Source Code Form is subject to the terms of the MVT License, v. 1.1. 6 | * If a copy of the MVT License was not distributed with this file, you can 7 | * obtain one at https://license.mvt.re/1.1/. 8 | ******************************************************************************/ 9 | 10 | #ifndef DDNA_MALWARE_CHECK_VIBER_HPP 11 | #define DDNA_MALWARE_CHECK_VIBER_HPP 12 | 13 | #include 14 | #include 15 | 16 | namespace DDNA 17 | { 18 | namespace MalwareChecks 19 | { 20 | class Viber: public MalwareCheck 21 | { 22 | public: 23 | 24 | std::string name( void ) const override; 25 | std::string image( void ) const override; 26 | std::vector< iOSDataset > iOSDatasets( void ) const override; 27 | void run( Core::SafePointer< Backup > backup, const STIXIndicators & indicators, std::vector< MalwareCheckResult > & results, Core::Progress & progress ) override; 28 | void analyzeMessages( sqlite3 * db, const STIXIndicators & indicators, std::vector< MalwareCheckResult > & results, const std::string & domain, const std::string & path ) const; 29 | std::string getText( const std::string & data ) const; 30 | }; 31 | } 32 | } 33 | 34 | #endif /* DDNA_MALWARE_CHECK_Viber_HPP */ 35 | -------------------------------------------------------------------------------- /src/MalwareChecks/DDNA-MalwareCheck-WebKitObservations.cpp: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2021, DigiDNA 3 | * All rights reserved 4 | * 5 | * This Source Code Form is subject to the terms of the MVT License, v. 1.1. 6 | * If a copy of the MVT License was not distributed with this file, you can 7 | * obtain one at https://license.mvt.re/1.1/. 8 | ******************************************************************************/ 9 | 10 | #include 11 | #include 12 | 13 | #define BACKUP_PATH "Library/WebKit/WebsiteData/ResourceLoadStatistics/observations.db" 14 | 15 | namespace DDNA 16 | { 17 | namespace MalwareChecks 18 | { 19 | std::string WebKitObservations::name( void ) const 20 | { 21 | return "WebKit Observations"; 22 | } 23 | 24 | std::string WebKitObservations::image( void ) const 25 | { 26 | return "com.apple.AppStore"; 27 | } 28 | 29 | std::vector< iOSDataset > WebKitObservations::iOSDatasets( void ) const 30 | { 31 | return 32 | { 33 | iOSDataset::customDatasetForTemporaryExtraction 34 | ( 35 | "com.apple.webkit.observations", 36 | { FOLDER_WILDCARD + BACKUP_PATH }, 37 | { 38 | { 39 | "AppDomain-*", 40 | { 41 | BACKUP_PATH 42 | } 43 | } 44 | } 45 | ) 46 | }; 47 | } 48 | 49 | void WebKitObservations::run( Core::SafePointer< Backup > backup, const STIXIndicators & indicators, std::vector< MalwareCheckResult > & results, Core::Progress & progress ) 50 | { 51 | for( const auto & domain: backup->getMBDBDomains() ) 52 | { 53 | progress.message( Core::L10N( "MalwareActivityDetectionCheckWebKitObservations_Info" ) ); 54 | 55 | if( domain.find( "AppDomain-" ) == 0 ) 56 | { 57 | std::string bundleID = domain.substr( 10 ); 58 | 59 | this->run( domain, bundleID, backup, indicators, results ); 60 | } 61 | } 62 | } 63 | 64 | void WebKitObservations::run( const std::string & domain, const std::string & bundleID, Core::SafePointer< Backup > backup, const STIXIndicators & indicators, std::vector< MalwareCheckResult > & results ) 65 | { 66 | std::string path = BACKUP_PATH; 67 | 68 | if( !backup->mbdbRecordExists( domain, path ) ) 69 | { 70 | return; 71 | } 72 | 73 | std::shared_ptr< sqlite3 > db = MalwareActivityDetectionHelper::ExtractAndOpenSQLiteDB( backup, domain, path, this->name(), results ); 74 | if( db == nullptr ) 75 | { 76 | return; 77 | } 78 | 79 | SQLite::QueryDictionary queryDict = db.get(); 80 | std::string query = 81 | R"query( 82 | 83 | SELECT 84 | domainID, 85 | registrableDomain, 86 | lastSeen, 87 | hadUserInteraction 88 | FROM ObservedDomains; 89 | 90 | )query"; 91 | 92 | std::vector< CF::Dictionary > records = queryDict.executeQuery( query ); 93 | if( records.size() == 0 ) 94 | { 95 | if( queryDict.errorMessage().length() > 0 ) 96 | { 97 | MalwareCheckResult( MalwareCheckResult::Type::Analyzer, MalwareCheckResult::Severity::Warning, 0, this->name(), EVENT_DB_QUERY_ERROR, "'" + domain + "/" + path + "' cannot be parsed (" + queryDict.errorMessage() + ").", results ); 98 | } 99 | else 100 | { 101 | MalwareCheckResult( MalwareCheckResult::Type::Analyzer, MalwareCheckResult::Severity::Info, 0, this->name(), EVENT_ANALYSING_ITEMS_INFO, "No WebKit observations to analyze in '" + MalwareActivityDetectionHelper::AppName( bundleID ) + "' (" + bundleID + ").", results ); 102 | } 103 | 104 | return; 105 | } 106 | 107 | MalwareCheckResult( MalwareCheckResult::Type::Analyzer, MalwareCheckResult::Severity::Info, 0, this->name(), EVENT_ANALYSING_ITEMS_INFO, "Analyzing " + std::to_string( records.size() ) + " WebKit observations in '" + MalwareActivityDetectionHelper::AppName( bundleID ) + "' (" + bundleID + ").", results ); 108 | 109 | for( const auto & record: records ) 110 | { 111 | std::string url = CF::String( record[ "registrableDomain" ] ); 112 | auto check = indicators.checkURL( url, this->name(), results ); 113 | 114 | auto res = std::get< 0 >( check ); 115 | if( res.isSuspicious() ) 116 | { 117 | auto finalURL = std::get< 1 >( check ); 118 | double timestamp = CF::Number( record[ "lastSeen" ] ); 119 | 120 | res.addToResults 121 | ( 122 | this->name(), 123 | timestamp, 124 | "WebKit access to \"" + finalURL + "\" from app: '" + MalwareActivityDetectionHelper::AppName( bundleID ) + "' (" + bundleID + ").", 125 | results 126 | ); 127 | } 128 | } 129 | } 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /src/MalwareChecks/DDNA-MalwareCheck-WebKitObservations.hpp: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2021, DigiDNA 3 | * All rights reserved 4 | * 5 | * This Source Code Form is subject to the terms of the MVT License, v. 1.1. 6 | * If a copy of the MVT License was not distributed with this file, you can 7 | * obtain one at https://license.mvt.re/1.1/. 8 | ******************************************************************************/ 9 | 10 | #ifndef DDNA_MALWARE_CHECK_WEBKIT_OBSERVATIONS_HPP 11 | #define DDNA_MALWARE_CHECK_WEBKIT_OBSERVATIONS_HPP 12 | 13 | #include 14 | 15 | namespace DDNA 16 | { 17 | namespace MalwareChecks 18 | { 19 | class WebKitObservations: public MalwareCheck 20 | { 21 | public: 22 | 23 | std::string name( void ) const override; 24 | std::string image( void ) const override; 25 | std::vector< iOSDataset > iOSDatasets( void ) const override; 26 | void run( Core::SafePointer< Backup > backup, const STIXIndicators & indicators, std::vector< MalwareCheckResult > & results, Core::Progress & progress ) override; 27 | void run( const std::string & domain, const std::string & bundleID, Core::SafePointer< Backup > backup, const STIXIndicators & indicators, std::vector< MalwareCheckResult > & results ); 28 | }; 29 | } 30 | } 31 | 32 | #endif /* DDNA_MALWARE_CHECK_WEBKIT_OBSERVATIONS_HPP */ 33 | -------------------------------------------------------------------------------- /src/MalwareChecks/DDNA-MalwareCheck-WebKitSessionResourceLog.cpp: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2021, DigiDNA 3 | * All rights reserved 4 | * 5 | * This Source Code Form is subject to the terms of the MVT License, v. 1.1. 6 | * If a copy of the MVT License was not distributed with this file, you can 7 | * obtain one at https://license.mvt.re/1.1/. 8 | ******************************************************************************/ 9 | 10 | #include 11 | #include 12 | 13 | namespace DDNA 14 | { 15 | namespace MalwareChecks 16 | { 17 | std::string WebKitSessionResourceLog::name( void ) const 18 | { 19 | return "WebKit Session Resource Log"; 20 | } 21 | 22 | std::string WebKitSessionResourceLog::image( void ) const 23 | { 24 | return "com.apple.mobilesafari"; 25 | } 26 | 27 | std::vector< iOSDataset > WebKitSessionResourceLog::iOSDatasets( void ) const 28 | { 29 | return 30 | { 31 | iOSDataset::customDatasetForTemporaryExtraction 32 | ( 33 | "com.apple.webkit.session.ressource.log", 34 | { 35 | FOLDER_WILDCARD + "full_browsing_session_resourceLog.plist" 36 | }, 37 | { 38 | { 39 | "*", 40 | { 41 | FOLDER_WILDCARD + "full_browsing_session_resourceLog.plist" 42 | } 43 | } 44 | } 45 | ) 46 | }; 47 | } 48 | 49 | void WebKitSessionResourceLog::run( Core::SafePointer< Backup > backup, const STIXIndicators & indicators, std::vector< MalwareCheckResult > & results, Core::Progress & progress ) 50 | { 51 | progress.message( Core::L10N( "MalwareActivityDetectionCheckWebKitSessionResourceLog_Info" ) ); 52 | 53 | Core::SafePointer< MBDBFile > mbdb = backup->mbdbFile(); 54 | if( mbdb.isNull() ) 55 | { 56 | return; 57 | } 58 | 59 | for( const Core::SafePointer< MBDBRecord > record: mbdb->records().allValues() ) 60 | { 61 | if( record->fileName() != "full_browsing_session_resourceLog.plist" ) 62 | { 63 | continue; 64 | } 65 | 66 | Core::Error error; 67 | CF::Dictionary plist = MalwareActivityDetectionHelper::ExtractPropertyList( backup, record->domain(), record->path(), false, this->name(), results, error ); 68 | if( error.failure() ) 69 | { 70 | continue; 71 | } 72 | 73 | // TODO: Find a file to ensure property list structure is correct... 74 | CF::Dictionary stats = plist[ "browsingStatistics" ]; 75 | CF::Array urls; 76 | 77 | urls.AppendValue( CF::String( stats[ "PrevalentResourceOrigin" ] ) ); 78 | for( CF::Dictionary entry: CF::Array( stats[ "topFrameUniqueRedirectsFrom" ] ) ) 79 | { 80 | urls.AppendValue( CF::String( entry[ "origin" ] ) ); 81 | urls.AppendValue( CF::String( entry[ "domain" ] ) ); 82 | } 83 | 84 | for( CF::Dictionary entry: CF::Array( stats[ "topFrameUniqueRedirectsTo" ] ) ) 85 | { 86 | urls.AppendValue( CF::String( entry[ "origin" ] ) ); 87 | urls.AppendValue( CF::String( entry[ "domain" ] ) ); 88 | } 89 | 90 | for( CF::String url: urls ) 91 | { 92 | auto check = indicators.checkURL( url, this->name(), results ); 93 | 94 | auto res = std::get< 0 >( check ); 95 | if( res.isSuspicious() ) 96 | { 97 | auto finalURL = std::get< 1 >( check ); 98 | CF::Number date = stats[ "mostRecentUserInteraction" ]; 99 | 100 | res.addToResults 101 | ( 102 | this->name(), 103 | date, 104 | "Found redirections between suspicious domains: '" + finalURL + "'.", 105 | results 106 | ); 107 | } 108 | } 109 | } 110 | } 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /src/MalwareChecks/DDNA-MalwareCheck-WebKitSessionResourceLog.hpp: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2021, DigiDNA 3 | * All rights reserved 4 | * 5 | * This Source Code Form is subject to the terms of the MVT License, v. 1.1. 6 | * If a copy of the MVT License was not distributed with this file, you can 7 | * obtain one at https://license.mvt.re/1.1/. 8 | ******************************************************************************/ 9 | 10 | #ifndef DDNA_MALWARE_CHECK_WEBKIT_SESSION_RESOURCE_LOG_HPP 11 | #define DDNA_MALWARE_CHECK_WEBKIT_SESSION_RESOURCE_LOG_HPP 12 | 13 | #include 14 | 15 | namespace DDNA 16 | { 17 | namespace MalwareChecks 18 | { 19 | class WebKitSessionResourceLog: public MalwareCheck 20 | { 21 | public: 22 | 23 | std::string name( void ) const override; 24 | std::string image( void ) const override; 25 | std::vector< iOSDataset > iOSDatasets( void ) const override; 26 | void run( Core::SafePointer< Backup > backup, const STIXIndicators & indicators, std::vector< MalwareCheckResult > & results, Core::Progress & progress ) override; 27 | }; 28 | } 29 | } 30 | 31 | #endif /* DDNA_MALWARE_CHECK_WEBKIT_SESSION_RESOURCE_LOG_HPP */ 32 | -------------------------------------------------------------------------------- /src/MalwareChecks/DDNA-MalwareCheck-WhatsApp.hpp: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2021, DigiDNA 3 | * All rights reserved 4 | * 5 | * This Source Code Form is subject to the terms of the MVT License, v. 1.1. 6 | * If a copy of the MVT License was not distributed with this file, you can 7 | * obtain one at https://license.mvt.re/1.1/. 8 | ******************************************************************************/ 9 | 10 | #ifndef DDNA_MALWARE_CHECK_WHATSAPP_HPP 11 | #define DDNA_MALWARE_CHECK_WHATSAPP_HPP 12 | 13 | #include 14 | 15 | namespace DDNA 16 | { 17 | namespace MalwareChecks 18 | { 19 | class WhatsApp: public MalwareCheck 20 | { 21 | public: 22 | 23 | std::string name( void ) const override; 24 | std::string image( void ) const override; 25 | std::vector< iOSDataset > iOSDatasets( void ) const override; 26 | void run( Core::SafePointer< Backup > backup, const STIXIndicators & indicators, std::vector< MalwareCheckResult > & results, Core::Progress & progress ) override; 27 | void analyzeMessages( sqlite3 * db, const STIXIndicators & indicators, std::vector< MalwareCheckResult > & results, const std::string & domain, const std::string & path ) const; 28 | void analyzeMediaItems( sqlite3 * db, const STIXIndicators & indicators, std::vector< MalwareCheckResult > & results, const std::string & domain, const std::string & path ) const; 29 | }; 30 | } 31 | } 32 | 33 | #endif /* DDNA_MALWARE_CHECK_WHATSAPP_HPP */ 34 | -------------------------------------------------------------------------------- /src/MalwareChecks/DDNA-MalwareCheck-WhatsAppBusiness.cpp: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2021, DigiDNA 3 | * All rights reserved 4 | * 5 | * This Source Code Form is subject to the terms of the MVT License, v. 1.1. 6 | * If a copy of the MVT License was not distributed with this file, you can 7 | * obtain one at https://license.mvt.re/1.1/. 8 | ******************************************************************************/ 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | /* 15 | * Database can be in different domains depending on thew WhatsApp version. 16 | * Name and path are the same. 17 | */ 18 | #define BACKUP_DOMAIN_1 "AppDomainGroup-group.net.whatsapp.WhatsAppSMB.shared" 19 | #define BACKUP_DOMAIN_2 "AppDomain-net.whatsapp.WhatsAppSMB" 20 | #define BACKUP_PATH "ChatStorage.sqlite" 21 | 22 | namespace DDNA 23 | { 24 | namespace MalwareChecks 25 | { 26 | std::string WhatsAppBusiness::name( void ) const 27 | { 28 | return "WhatsApp Business"; 29 | } 30 | 31 | std::string WhatsAppBusiness::image( void ) const 32 | { 33 | return MalwareActivityDetectionHelper::AppIconPath( "net.whatsapp.WhatsAppSMB" ); 34 | } 35 | 36 | std::vector< iOSDataset > WhatsAppBusiness::iOSDatasets( void ) const 37 | { 38 | return 39 | { 40 | iOSDataset::customDatasetForTemporaryExtraction 41 | ( 42 | "net.whatsapp.WhatsAppSMB", 43 | { FOLDER_WILDCARD + BACKUP_PATH }, 44 | { 45 | { 46 | BACKUP_DOMAIN_1, 47 | { 48 | BACKUP_PATH 49 | } 50 | }, 51 | { 52 | BACKUP_DOMAIN_2, 53 | { 54 | BACKUP_PATH 55 | } 56 | } 57 | } 58 | ) 59 | }; 60 | } 61 | 62 | void WhatsAppBusiness::run( Core::SafePointer< Backup > backup, const STIXIndicators & indicators, std::vector< MalwareCheckResult > & results, Core::Progress & progress ) 63 | { 64 | progress.message( Core::L10N( "MalwareActivityDetectionCheckWhatsAppBusiness_Info" ) ); 65 | 66 | if( !backup->mbdbRecordExists( BACKUP_DOMAIN_1, BACKUP_PATH ) && !backup->mbdbRecordExists( BACKUP_DOMAIN_2, BACKUP_PATH ) ) 67 | { 68 | return; 69 | } 70 | 71 | std::string domain = BACKUP_DOMAIN_1; 72 | std::string path = BACKUP_PATH; 73 | 74 | if( !backup->mbdbRecordExists( domain, path ) ) 75 | { 76 | domain = BACKUP_DOMAIN_2; 77 | } 78 | 79 | std::shared_ptr< sqlite3 > db = MalwareActivityDetectionHelper::ExtractAndOpenSQLiteDB( backup, domain, path, this->name(), results ); 80 | 81 | if( db == nullptr ) 82 | { 83 | return; 84 | } 85 | 86 | this->analyzeMessages( db.get(), indicators, results, domain, path ); 87 | this->analyzeMediaItems( db.get(), indicators, results, domain, path ); 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/MalwareChecks/DDNA-MalwareCheck-WhatsAppBusiness.hpp: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2021, DigiDNA 3 | * All rights reserved 4 | * 5 | * This Source Code Form is subject to the terms of the MVT License, v. 1.1. 6 | * If a copy of the MVT License was not distributed with this file, you can 7 | * obtain one at https://license.mvt.re/1.1/. 8 | ******************************************************************************/ 9 | 10 | #ifndef DDNA_MALWARE_CHECK_WHATSAPP_BUSINESS_HPP 11 | #define DDNA_MALWARE_CHECK_WHATSAPP_BUSINESS_HPP 12 | 13 | #include 14 | 15 | namespace DDNA 16 | { 17 | namespace MalwareChecks 18 | { 19 | class WhatsAppBusiness: public WhatsApp 20 | { 21 | public: 22 | 23 | std::string name( void ) const override; 24 | std::string image( void ) const override; 25 | std::vector< iOSDataset > iOSDatasets( void ) const override; 26 | void run( Core::SafePointer< Backup > backup, const STIXIndicators & indicators, std::vector< MalwareCheckResult > & results, Core::Progress & progress ) override; 27 | }; 28 | } 29 | } 30 | 31 | #endif /* DDNA_MALWARE_CHECK_WHATSAPP_BUSINESS_HPP */ 32 | --------------------------------------------------------------------------------