Malwarelytics for Apple

Malwarelytics for Apple protects your iOS, tvOS or macOS app from a broad range of cyber threats. Integrate mobile threat protection SDK and connect to our cloud service to get the best benefit.

Supported Features

Malwarelytics for Apple currently supports the following features:

  • Jailbreak Detection
  • Debugger Protection
  • Reverse Engineering Tools Detection
  • HTTP Proxy detection
  • Repackaging Detection
  • Screen Capture Detection
  • VPN Detection
  • Active Call Detection
  • App Presence Detection
  • User Screenshot Detection
  • System Passcode Status
  • System Biometry Status
  • Predefined and Custom Events

Installation

Malwarelytics for Apple is distributed via Swift Package Manager (SPM) and Cocoapods private repository.

Malwarelytics for Apple supports iOS 12+ and requires Xcode 14.3+.

Personalized Configuration Required.
In order to use Malwarelytics for Apple, you need a custom configuration and access credentials for both the service and artifact repository. Contact your sales representative or technical consultant in order to obtain the required prerequisites.

Prerequisities

You need to create a credentials file to access our private repository (needed for both SPM and Cocoapods integration).

Create (or append to if it already exists) a .netrc file in your home directory (~) with your credentials to Wultra Artifactory.

machine wultra.jfrog.io
      login [[email protected]]
      password [password]

Swift Package Manager

Add the following repository as a dependency to your project:

   https://github.com/wultra/malwarelytics-apple-release

You can use Xcode’s dedicated user interface to do this or add the dependency manually, for example:

   // swift-tools-version:5.10

   import PackageDescription

   let package = Package(
       name: "YourLibrary",
       products: [
           .library(
               name: "YourLibrary",
               targets: ["YourLibrary"]),
       ],
       dependencies: [
           .package(name: "AppProtection", url: "https://github.com/wultra/malwarelytics-apple-release.git", .upToNextMajor(from: "3.0.0"))
       ],
       targets: [
           .target(
               name: "YourLibrary",
               dependencies: [
                   "WultraAppProtection"
               ])
       ]
   )

You can check the latest versions of the library at the release page.

Cocoapods

The library is also distributed via Cocoapods private repository. If you’re not using cocoapods in your project, visit usage guide.

  1. Add pod to your Podfile:

    target 'MyProject' do
        use_frameworks!
        pod 'AppProtection', :git => 'https://github.com/wultra/malwarelytics-apple-release.git', :tag => '3.0.0'
    end
    

    You can check the latest versions of the library at the release page.

  2. Run pod install in your project dictionary to make the AppProtection framework available in your project.

Configuration

## Minimum Configuration

AppProtection can run without any configuration. However, in such settings, RASP features will only report security incidents to your delegate.

Reporting to the Malwarelytics web console is turned off in case of the minimal configuration.

Online Service

To see your protected devices and reported incidents in the Malwarelytics console, you need to provide valid credentials.

The Username, Password, and Signature Public Key can be obtained in the Get the SDK section. Note that these credentials are bound to your application Bundle ID. If you need to support multiple environments (Bundle IDs), you need different credentials for each environment.

Configuring the AppProtection

To enable AppProtection features in your app, you must create a properly configured instance of the AppProtectionService class and set the AppProtectionRaspDelegate to obtain callbacks.

You should implement the following configurations:

  • AppProtectionRaspConfig - Configures which RASP features are enabled and default actions for the detections.
  • AppProtectionOnlineConfig - Configuration of the online part of the AppProtection.
    • AppProtectionIdentificationConfig - Configures user identification. This can be changed later at runtime.
    • AppProtectionEventConfig - Configures which RASP events are emitted and sent to the back-end services.
    • AppProtectionCustomerGroupingConfig - Configuration of customer grouping and naming in the web application. For more details, visit Customer Grouping and Naming

Based on the configuration, the AppProtectionRaspDelegate receives updates about various RASP events.

Sample Integration

The AppSecurity swift class is a sample implementation over the AppProtectionService.

import Foundation
import WultraAppProtection

class AppSecurity: AppProtectionRaspDelegate {
    
    private let appProtection: AppProtectionService

    /// Creates AppSecurity instance
    /// - Parameter userId: ID of the user
    /// - Parameter deviceId: ID of the device
    init(userId: String?, deviceId: String?) {
    
        // Prepare the RASP feature configuration
        let raspConfig = AppProtectionRaspConfig(
            jailbreak: .exit("https://myurl.com/jalibreak-explained"), // exit on jailbroken phone
            debugger: .block, // block debugger
            reverseEngineeringTools: .notify, // let me know when user installed revers engineering tools
            httpProxy: .notify, // notify me via delegate when http proxy is enabled
            repackage:.exit([AppProtectionTrustedCert(withBase64EncodedString: "BASE_64_ENCODED_CERT")!], "https://myurl.com/repackage-explained"), // follow documentation how to obtain certificate string
            screenCapture: .hide(), // will hide the app contents when screen is captured (for example shared via airplay)
            vpnDetection: .notify, // notify me when VPN is connected or disconnected
            callDetection: .notify, // notify me when a call is detected
            appPresence: .notify([.KnownApps.anyDesk]) // notify me when AnyDesk application is installed. Note that you also have to specify the deeplink in your Info.plist (more in the feature documentation)
        )
        
        // Prepare the configuration for events
        let eventConfig = AppProtectionEventConfig(
            enableEventCollection: true, // enable event collection in general
            enableScreenshotTakenCollection: true // /track screenshot events in the Malwarelytics console on the server
        )
        
        // Prepare user identification
        let idConfig = AppProtectionIdentificationConfig(
            userId: userId,
            deviceId: deviceId
        )
        
    	 // Prepare the Online configuration
    	 let onlineConfig = onlineConfig = .inAppProtectionOnlineConfig(
            username: "$USERNAME",
            password: "$PASSWORD",
            signaturePublicKey: "$PUBKEY",
            clientIdentification: idConfig,
            eventsConfig: eventConfig,
            customerGroupingConfig: nil, // advanced feature, for more info see "Customer Grouping and Naming" section of the documentation
            environment: .production
        )
        
        // Prepare a configuration for service
        let config = AppProtectionConfig(
            raspConfig: raspConfig,
            onlineConfig: onlineConfig
        )

        // Create the Service
        self.appProtection = AppProtectionService(config: config)

        // Set the delegate to obtain RASP callbacks
        self.appProtection.rasp.addDelegate(self)
    }
    
    deinit {
        // When our AppSecurity class is being "destroyed", we want
        // to stop all features of the AppProtectionService.
        // To do so, we need to release all its assets before
        // the only reference that "holds it" is removed.
        
        self.appProtection.release()
    }

    /// Report in-app incident
    /// - Parameter incident: Incident that happened inside the app
    func reportIncident(_ incident: RaspIncident) {
        self.appProtection.online?.events.log(incident.appProtectionValue)
    }

    /// Call when client id changes (paired/unpaired the app).
    /// - Parameter deviceId: User id
    /// - Parameter deviceId: Device id
    func updateClientInfo(userId: String?, deviceId: String?) {
        self.appProtection.online?.clientIdentification.userId = userId
        self.appProtection.online?.clientIdentification.deviceId = deviceId
    }

    /// AppProtection resets the device indicator and starts registering it as a new device.
    func resetServiceId() {
        self.appProtection.online?.resetInstanceId()
    }

    // MARK: - AppProtectionRaspDelegate

    func debuggerDetected() {
        // react to debugger
    }

    func jailbreakDetected() {
        // react to jailbreak
    }

    func repackageDetected() {
        // react to repackage
    }

    func httpProxyEnabled() {
        // react to http proxy enabled
    }

    func userScreenshotDetected() {
        // react to user screenshot
    }

    func reverseEngineeringToolsDetected() {
        // react to reverse engineering tools
    }

    func systemPasscodeConfigurationChanged(enabled: Bool) {
        // react to system passcode change
    }

    func systemBiometryConfigurationChanged(enabled: Bool) {
        // react to biometry configuration changed
    }

    func screenCapturedChanged(isCaptured: Bool) {
        // react to screen capturing (casting to different device)
    }

    func vpnChanged(active: Bool) {
        // react to VPN state changes
    }

    func onCallChanged(isOnCall: Bool) {
        // on call status has changed
    }

    func installedAppsChanged(installedApps: [DetectableApp]) {
        // installed apps list has changed
    }
}

enum RaspIncident {
    case sslInvalidCert
    case deviceUnpaired
    case devicePaired
    case deviceBlocked
    case userIgnoredWeakPassphrase
    case passphraseChanged
    case biometryEnabled
    case biometryDisabled
    case authenticationFailed
    case authenticationSuccess

    var appProtectionValue: AppProtectionEvent {
        switch self {
        case .sslInvalidCert: return AppProtectionEvent.Network.sslInvalidCertificate
        case .deviceUnpaired: return AppProtectionEvent.Authentication.deviceUnpaired
        case .devicePaired: return AppProtectionEvent.Authentication.devicePaired
        case .deviceBlocked: return AppProtectionEvent.Authentication.deviceBlocked
        case .userIgnoredWeakPassphrase: return AppProtectionEvent.Authentication.userIgnoredWeakPassphrase
        case .passphraseChanged: return AppProtectionEvent.Authentication.passphraseChanged
        case .biometryEnabled: return AppProtectionEvent.Authentication.biometryEnabled
        case .biometryDisabled: return AppProtectionEvent.Authentication.biometryDisabled
        case .authenticationFailed: return AppProtectionEvent.Authentication.authenticationFailed
        case .authenticationSuccess: return AppProtectionEvent.Authentication.authenticationSuccess
        }
    }
}

Service Lifecycle

Be aware that the creation of 2 instances of AppProtectionService is considered to be a logical error and will result in application crash.

Before deiniting (removing all references to) the AppProtectionService object, you need to call release() to stop all its functionality. Deiniting without release will result in application crash.

Customer Grouping and Naming

The SDK allows passing custom values that are used to group data in the Malwarelytics web console application.
The configuration items in AppProtectionCustomerGroupingConfig add extra metadata that is passed into the web console.
The data allows to split data into groups and obtain different views on the data.

The data can be defined with:

let groupingConfig = AppProtectionCustomerGroupingConfig(
    sourceBundleId: "String", // optional
    appBundleId: "String", // optional
    audienceGroupId: "String" // optional
)

Limitations for the strings are the following:

  • Max length of sourceBundleId is 255 characters
  • Max length of appBundleId is 255 characters
  • Max length of audienceGroupId is 20 characters

Main grouping of the data is achieved with sourceBundleId . The value has to agree with the application credentials in the web console.
Extra granularity of data views is achieved with appBundleId.
The last option audienceGroupId is used to distinguish users from different customer systems such as “RETAIL”, “CORPORATE” and so on.

Recommended Responses to Security Issues

Malwarelytics for Apple provides detection of many security issues. Moreover, it provides a huge variability in both configuring these protections and reactions to security violations. Some reactions, such as app termination, are provided by the SDK. Other reactions, such as displaying a warning screen or limiting functionality, are the responsibility of the application integrating Malwarelytics for Apple.

The page provides a summary of recommended responses to various security issues that the SDK can help you with.

Before diving into details we summarize the recommendations here. In general, we recommend:

  • Terminate the app on app repackaging, when a debugger is attached, or when reverse engineering tools are detected.
  • Show active warning when a jailbroken device is detected.
  • Block debuggers, and hide screen contents when screen capturing is detected.
  • Inform about other potential issues in some security advisor built into the app.

Responses to Security Issues

Some general approaches to handling a security violation might be:

  1. Display a Warning Message: The app can display a warning message informing the user that the app doesn’t support when a certain security feature is violated. This message should explain the security risks associated with using the app on such devices in such conditions and recommend fixing the issue for banking and financial operations.
  2. Limited Functionality: The app can limit its functionality when a security violation is detected. For example, it might disable some sensitive features like money transfers, or access to sensitive financial data to reduce the potential for unauthorized access and fraudulent activities.
  3. Terminate the App: If the app detects that it is running on a device with a security violation, it can immediately terminate itself to prevent any further access. This will help minimize the exposure of sensitive financial information and protect the user from potential security risks. As a general UX improvement, it is recommended to also open a website (in a browser outside the app) with an explanation while the app is being terminated.
  4. Silent Monitoring on Server: The app can silently monitor what’s happening on the device by sending data to the remote server. This approach is especially powerful when connected to a complex anti-fraud system analyzing inputs from various sources. The backend system can then decide the appropriate handling of the security issue. This approach should be used for all features in parallel to other approaches.

Responses to RASP Security Issues

Malwarelytics for Apple protects from a wide range of runtime issues. Some of them are more serious than others. Here we provide a list of recommended responses to these issues by a banking or a financial app.

Summary of Responses to RASP Issues

The summary here mentions RASP protection features that have a RaspObserver callback method. These features can notify the integrating app about an issue and the app can react to it. Some of the protection behaviors are automatically provided by the SDK.

Security feature Recommended response to security issue
Jailbroken device Terminate the app.
Repackaged app Terminate the app.
Attached debugger Block debuggers or terminate the app when a debugger attaches.
Reverse engineering tools Terminate the app.
Screenshots Display a warning.
Screen capturing Hide content and monitor silently.
System passcode status Display a warning. Optionally limit sensitive functionality.
System biometry status Monitor silently. Optionally display a recommendation.
HTTP proxy Display a warning.
VPN Display a warning.
Active calls Limit sensitive functionality during a call.
App presence Limit sensitive functionality when an unwanted app is installed.

Jailbroken Devices

It is generally recommended to avoid using jailbroken devices for banking or financial apps. The integrity of jailbroken devices is compromised and security is reduced. It is far easier for attackers to bypass security measures designed to protect financial data and the integrity of financial operations within the app.

For these reasons, we generally recommend terminating the app and not allowing users to use it on a jailbroken device.

Repackaged Application

A repackaged application is a changed app and can’t be considered secure. It poses a danger to both clients and the financial institution issuing the app. Any code inside a repackaged app can be inserted, changed, or removed. Therefore security features of such apps have to be considered as compromised.

For this reason, the app should unconditionally terminate itself when it detects that it has been repackaged.

Attached Debuggers

An attached debugger poses a huge danger to the app. There’s a huge number of potential issues such as data leakage, security breaches, transaction tampering, and injection of malicious code.

For these reasons, we generally recommend either blocking debuggers from attaching or terminating the app when an attached debugger is detected.

Reverse Engineering Tools

Reverse engineering tools substantially affect the security and integrity of iOS applications. These tools can be used to jailbreak the device, hook into live processes, and modify code execution in real-time. It can be used to bypass security checks, authentication mechanisms, and other protective measures.

For these reasons, the app should unconditionally terminate itself when it detects any reverse engineering tool.

Screenshots

Screenshots taken by users can contain sensitive personal and financial information. Once a screenshot is captured, the users should take care to handle it with caution to avoid accidental leakage and exposure of the data it may contain. Unfortunately, this is often not the case.

For this reason, the app should warn users about the dangers of leaking sensitive information when it detects that a screenshot has been taken.

Screen Capturing

A financial or banking app handles a lot of sensitive data. Screen capturing can record everything happening on the screen over a period of time, not just a static image. This means all displayed sensitive information, including account numbers, balances, transaction history, and personal data, can be captured in detail. The captured recording can be accidentally or maliciously shared, leading to a significant breach of confidentiality.

The recommended approach is to hide screen contents. This way the obtained recording will not contain sensitive data. Screen capturing should be monitored silently, with no action unless additional suspicious signals appear.

System Passcode Status

Usage of system passcode is an essential security feature of all iOS devices. Not having a system passcode poses significant security risks, especially if sensitive applications like banking apps are installed. The stored personal and financial information is vulnerable to unauthorized access. Moreover, when a malicious individual gets physical access to the device, the possession authentication factor is effectively eliminated on such devices, and the risk of financial fraud greatly increases. Lastly, a lack of system passcode conflicts with several security standards and regulations, which can lead to compliance issues.

The recommended response is to display a warning and optionally limit the app’s sensitive functionality.

System Biometry Status

Usage of biometrics reduces the risk of unauthorized access to the user’s device and a banking app. PINs and passwords are frequently reused by the users. Also, there is a range of attacks for obtaining the user’s password or PIN such as phishing attacks, social engineering, and shoulder surfing.

The recommended response is to monitor the issue silently. Optionally the app can attempt to display a recommendation to the user.

Active HTTP Proxy

Active HTTP proxy can exploit issues in client-server communication when not done correctly and can lead to data interception.

The recommended response to the detection of an active HTTP proxy is to display a warning to the user.

Active VPN

Active VPN can indicate a possible fraud attempt and might be a problem from the point of regulatory compliance. While VPNs are generally used to secure internet traffic, not all VPN services are trustworthy. Some might intercept, monitor, or manipulate traffic routed through them.

The recommended response to the detection of an active VPN is to display a warning to the user.

Active Calls

An active call during the active usage of a banking app poses a risk of social engineering attacks. These attacks are very frequent and create huge financial losses.

For this reason, it’s recommended to block any sensitive functionality during the period of the call. Or even automatically reject all operations and transactions initiated during an active call.

App Presence

Remote desktop apps are a concern for all banking apps due to the potential risk they pose in financial fraud. These apps enable remote access to the device, allowing unauthorized individuals to view the screen remotely. Furthermore, some remote desktop apps may include audio sharing, which could potentially allow eavesdropping through the device’s microphone. Although it is not possible to remotely control an iOS device, attackers frequently use these apps to commit financial fraud.

The recommended response is to limit functionality while an unwanted app is present on the device. All functionality involving sensitive operations and transactions should be blocked to prevent any fraudsters from seeing any sensitive data.

RASP Features Overview

RASP (runtime application self-protection) features protect the app against several attack vectors.

Currently, Malwarelytics for Apple covers the following problems:

Configuration

RASP detections are configured via AppProtectionRaspConfig and AppProtectionEventConfig classes that are a part of the AppProtectionConfig.

To configure RASP detections, use:

// Prepare the RASP feature configuration
let raspConfig = AppProtectionRaspConfig(
    jailbreak: .exit("https://myurl.com/jalibreak-explained"), // exit on jailbroken phone
    debugger: .block, // block debugger
    reverseEngineeringTools: .notify, // let me know when the user installed reverse engineering tools
    httpProxy: .notify, // notify me via delegate when HTTP proxy is enabled
    repackage: .exit([AppProtectionTrustedCert(withBase64EncodedString: "BASE_64_ENCODED_CERT")!], "https://myurl.com/repackage-explained"), // follow documentation how to obtain certificate string
    screenCapture: .hide(), // will hide the app contents when the screen is captured (for example shared via airplay),
    vpnDetection: .notify, // notify me when the VPN is connected or disconnected
    callDetection: .notify, // notify me when about an active call
    appPresence: .notify([.KnownApps.anyDesk]) // notify me when AnyDesk application is installed. Note that you also have to specify the deeplink in your Info.plist (more in the feature documentation)
)
    
// Prepare the configuration for events
let eventConfig = AppProtectionEventConfig(
    enableEventCollection: true, // enable event collection in general
    enableAppLifecycleCollection: true, // track lifecycle events in the Malwarelytics console on the server
    enableScreenshotTakenCollection: true // track screenshot events in the Malwarelytics console on the server
)
    
// Prepare a configuration for service
let config = AppProtectionConfig(
    username: "$USERNAME", // username for the Malwarelytics service
    password: "$PASSWORD", // password for the Malwarelytics service
    signaturePublicKey: "$PUBKEY", // public key for the Malwarelytics service
    clientIdentification: nil, // user identification (unique within your systems)
    raspConfig: raspConfig,
    eventsConfig: eventConfig,
    customerGroupingConfig: nil // Configuration of customer grouping and naming in the web application.
)

Available RASP Configuration Items

Item Description
jailbreak: DetectionConfig defines the behavior of automatic jailbreak detection. Defaults to .notify.
debugger: DebuggerDetectionConfig defines the behavior of automatic debugger detection. Defaults to .notify.
reverseEngineeringTools: DetectionConfig defines the behavior of automatic reverse engineering tools detection. Defaults to .notify.
httpProxy: DetectionConfig defines the behavior of automatic HTTP proxy detection. Defaults to .notify.
repackage: RepackageConfig defines the behavior of automatic repackaging detection. Defaults to .noAction([]).
screenCapture: ScreenCaptureDetectionConfig defines the behavior of automatic screen capturing detection. Defaults to .notify.
vpnDetection: DetectionConfig defines the behavior of automatic VPN detection. Defaults to .notify.
callDetection: SimpleDetectionConfig defines the behavior of automatic call detection. Defaults to .notify.
appPresence: AppPresenceDetectionConfig defines the behavior of automatic app presence detection. Defaults to .manual.

The behavior of all the configuration items can be summarized with these rules:

  • The .noAction values turn the corresponding feature off. In some cases detection data can be manually obtained through AppProtectionRasp protocol.
  • The .notify values cause the delegate (and remote server when configured) to be notified about the corresponding detection.
  • The .exit(exitUrl: String?) values cause the app to be terminated when the corresponding “positive” detection of the feature is detected. The exitUrl parameters define URLs to be opened in the system web browser when the app is terminated as a result of the corresponding detection.

Detailed information about these configuration items can be found on the documentation pages of individual features.

How to Configure App Presence

App presence allows you to verify whether a specific app is installed on a device. To successfully detect such an application, you need to configure the AppProtectionRaspConfig with the appPresence parameter.

In addition to that, you also need to add a query URL scheme to your application Info.plist.

For example, if you want to add detection for AnyDesk application, you need to configure the appPresense parameter with appPresence: .notify([.KnownApps.anyDesk]) and then add anydesk scheme into your Info.plist Queried URL Schemes item.

You can add anydesk scheme to the query with these few steps:

  1. Open your Xcode project.
  2. In the Project Navigator, find your app’s Info.plist file and open it.
  3. Click the “+” button in the top-right corner of the Info.plist editor.
  4. In the new row, set the key to “Queried URL Schemes”
  5. Click the arrow next to “Queried URL Schemes” to expand it.
  6. Click the “+” button next to “Queried URL Schemes” and add anydesk scheme.
  7. Save your changes.

By following these steps, you’ll configure app presence for your application to detect the presence of specific apps, like AnyDesk.

Obtaining Detection Results

When Malwarelytics for Apple is initialized with certain configurations, the RASP features can be accessed through AppProtectionRaspDelegate or by proactively checking for the status of a certain feature.

Observing RASP Detections

An observer can be registered in AppProtectionRasp to notify the app about any RASP detection change.

// Set the delegate to the existing `AppProtectionService` instance
// to obtain RASP callbacks
appProtection.rasp.addDelegate(self)

Delegate then receives the following callbacks:

func debuggerDetected() {
    // react to a debugger
}

func jailbreakDetected() {
    // react to jailbreak
}

func repackageDetected() {
    // react to repackage
}

func httpProxyEnabled() {
    // react to HTTP proxy enabled
}

func userScreenshotDetected() {
    // react to user screenshot
}

func reverseEngineeringToolsDetected() {
    // react to reverse engineering tools
}

func systemPasscodeConfigurationChanged(enabled: Bool) {
    // react to system passcode change
}

func systemBiometryConfigurationChanged(enabled: Bool) {
    // react to biometry configuration changed
}

func screenCapturedChanged(isCaptured: Bool) {
    // react to screen capturing (casting to different device)
}

func vpnChanged(active: Bool) {
    // react to VPN state changes
}

func onCallChanged(isOnCall: Bool) {
    // react to call change
}
	
func installedAppsChanged(installedApps: [DetectableApp]) {
    // installed apps list has changed
}

Triggering RASP Checks Manually

All the RASP checks can be triggered manually in AppProtectionRasp. There are mostly methods for a simple boolean answer. Only app presence detection provides more detailed information.

// root detection
let isJailbroken = appProtection.rasp.isJailbroken

// debugger
let isDebuggerConnected = appProtection.rasp.isDebuggerConnected

// repackaging
let isRepackaged = appProtection.rasp.isRepackaged

// screen sharing
let isScreenCaptured = appProtection.rasp.isScreenCaptured

// system passcode
let isSystemPasscodeEnabled = appProtection.rasp.isSystemPasscodeEnabled

// system biometry
let isSystemBiometryEnabled = appProtection.rasp.isSystemBiometryEnabled

// simulator build
let isEmulator = appProtection.rasp.isEmulator

// reverse engineering
let isReverseEngineeringToolsPresent = appProtection.rasp.isReverseEngineeringToolsPresent

// http proxy present
let isHttpProxyEnabled = appProtection.rasp.isHttpProxyEnabled

// VPN active
let isVpnActive = appProtection.rasp.isVpnActive

// on call
let isOnCall = appProtection.rasp.isOnCall

// detected apps
let detectedApps = appProtection.rasp.installedApps

Jailbreak Detection

Detection of jailbreak is a key RASP feature. An iOS device that has been jailbroken has its security violated. The iOS on a jailbroken device might be customized in many different aspects - the user interface might be adjusted, system behavior might be altered, system settings tweaked, system restrictions overridden, and unauthorized apps can be installed. This also means that the device might be exposed to security vulnerabilities.

Malwarelytics for Apple is able to detect that the app is running on a jailbroken device and can be configured to terminate the app in that case.

Configuration

let raspConfig = AppProtectionRaspConfig(
    jailbreak: DetectionConfig
    // configuration of other RASP features
)

Available values of DetectionConfig:

Value Description
.noAction indicates that jailbreak will not be automatically detected. A manual check is still possible.
.notify indicates that jailbreak will be automatically detected and the delegates will be notified via the jailbreakDetected() method.
.exit(
exitUrl: String?)
indicates that the jailbreak will be automatically detected and the app will be terminated when the jailbreak is automatically detected.

Jailbreak detection defaults to .notify.

List of available parameters for some config values:

Parameter Description
exitUrl: String? defines the URL to be opened when the app is terminated because of the automatic detection. Defaults to nil.

Usage

After service creation, the jailbreak detection feature can be accessed via AppProtectionRasp. This can be used to add a delegate or to trigger a manual jailbreak detection check.

Observing Detection

Jailbreak detection can trigger a certain action. To achieve that, a delegate needs to be added.

Delegate configuration:

class RaspDelegate: AppProtectionRaspDelegate {

    // other delegate code

    func jailbreakDetected() {
        // handle jailbreak detection
    }
}

The delegate can be added in AppProtectionRasp. When it is no longer needed, it can be removed again.

let raspDelegate = RaspDelegate()
appProtection.rasp.addDelegate(raspDelegate)
appProtection.rasp.removeDelegate(raspDelegate)

Triggering a Manual Check

Jailbreak detection check can be triggered manually in AppProtectionRasp by getting the isJailbroken property value. A simple Bool answer is given.

let isJailbroken = appProtection.rasp.isJailbroken

More information on general RASP feature configuration and usage can be found in this overview.

Debugger Detection

Detecting that a debugger is attached to a production app is a key RASP feature. Attaching a debugger to an app should only be possible in the development phase and should never occur with a production app. A debugger attached to a production app is a clear sign of malicious tampering.

Malwarelytics for Apple is able to either block a debugger from attaching to the process or detect that a debugger has been attached to the app process and can be configured to terminate the app in that case.

Configuration

let raspConfig = AppProtectionRaspConfig(
    debugger: DebuggerDetectionConfig
    // configuration of other RASP features
)

Available values of DebuggerDetectionConfig:

Value Description
.noAction indicates that debuggers will not be automatically detected. A manual check is still possible.
.notify indicates that debuggers will be automatically detected and the delegates will be notified via the debuggerDetected() method.
.block indicates that debuggers will be blocked from attaching to the application process.
.exit(
exitUrl: String?)
indicates that debuggers will be automatically detected and the app will be terminated when a debugger is automatically detected.

Debugger detection defaults to .notify.

List of available parameters for some config values:

Parameter Description
exitUrl: String? defines the URL to be opened when the app is terminated because of the automatic detection. Defaults to nil.

Usage

After service creation, the debugger detection feature can be accessed via AppProtectionRasp. This can be used to add a delegate or to trigger a manual debugger detection check.

Observing Detection

Debugger detection can trigger a certain action. To achieve that, a delegate needs to be added.

Delegate configuration:

class RaspDelegate: AppProtectionRaspDelegate {

    // other delegate code

    func debuggerDetected() {
        // handle debugger detection
    }
}

The delegate can be added in AppProtectionRasp. When it is no longer needed, it can be removed again.

let raspDelegate = RaspDelegate()
appProtection.rasp.addDelegate(raspDelegate)
appProtection.rasp.removeDelegate(raspDelegate)

Triggering a Manual Check

Debugger detection check can be triggered manually in AppProtectionRasp by getting the isDebuggerConnected property value. A simple Bool answer is given.

let isDebuggerConnected = appProtection.rasp.isDebuggerConnected

More information on general RASP feature configuration and usage can be found in this overview.

Reverse Engineering Tools Detection

Detection of reverse engineering tools is a key RASP feature. Reverse engineering tools can be used for customization and modification of the apps and system components. They can also be used by malicious actors to analyze, tamper with, and exploit applications.

Malwarelytics for Apple is able to detect that reverse engineering tools are present on the device and can be configured to terminate the app in that case.

Configuration

let raspConfig = AppProtectionRaspConfig(
    reverseEngineeringTools: DetectionConfig
    // configuration of other RASP features
)

Available values of DetectionConfig:

Value  
.noAction indicates that reverse engineering tools will not be automatically detected. A manual check is still possible.
.notify indicates that reverse engineering tools will be automatically detected and the delegates will be notified via the reverseEngineeringToolsDetected() method.
.exit(
exitUrl: String?)
indicates that the reverse engineering tools will be automatically detected and the app will be terminated when the reverse engineering tools are automatically detected.

Reverse engineering tools detection defaults to .notify.

List of available parameters for some config values:

Parameter Description
exitUrl: String? defines the URL to be opened when the app is terminated because of the automatic detection. Defaults to nil.

Usage

After service creation, the reverse engineering tools detection feature can be accessed via AppProtectionRasp. This can be used to add a delegate or to trigger a manual reverse engineering tools detection check.

Observing Detection

Reverse engineering tools detection can trigger a certain action. To achieve that, a delegate needs to be added.

Delegate configuration:

class RaspDelegate: AppProtectionRaspDelegate {

    // other delegate code

    func reverseEngineeringToolsDetected() {
        // handle reverse engineering tools detection
    }
}

The delegate can be added in AppProtectionRasp. When it is no longer needed, it can be removed again.

let raspDelegate = RaspDelegate()
appProtection.rasp.addDelegate(raspDelegate)
appProtection.rasp.removeDelegate(raspDelegate)

Triggering a Manual Check

Reverse engineering tools detection check can be triggered manually in AppProtectionRasp by getting the isReverseEngineeringToolsPresent property value. A simple Bool answer is given.

let isReverseEngineeringToolsPresent = appProtection.rasp.isReverseEngineeringToolsPresent

More information on general RASP feature configuration and usage can be found in this overview.

HTTP Proxy Detection

A system-wide HTTP proxy configuration can force all HTTP (and, in some cases, HTTPS) requests from your app to pass through a proxy server. This is a potentially harmful behavior since the proxy server can then inspect or even modify request payloads. 

Malwarelytics for Apple is able to detect that an HTTP proxy is active on the device and can be configured to terminate the app in that case.

Configuration

let raspConfig = AppProtectionRaspConfig(
    httpProxy: DetectionConfig
    // configuration of other RASP features
)

Available values of DetectionConfig:

Value Description
.noAction indicates that the HTTP proxy will not be automatically detected. A manual check is still possible.
.notify indicates that the HTTP proxy will be automatically detected and the delegates will be notified via the httpProxyEnabled() method.
.exit(
exitUrl: String?)
indicates that the HTTP proxy will be automatically detected and the app will be terminated when the HTTP proxy is automatically detected.

HTTP proxy detection defaults to .notify.

List of available parameters for some config values:

Parameter Description
exitUrl: String? defines the URL to be opened when the app is terminated because of the automatic detection. Defaults to nil.

Usage

After service creation, the HTTP proxy detection feature can be accessed via AppProtectionRasp. This can be used to add a delegate or to trigger a manual HTTP proxy detection check.

Observing Detection

HTTP proxy detection can trigger a certain action. To achieve that, a delegate needs to be added.

Delegate configuration:

class RaspDelegate: AppProtectionRaspDelegate {

    // other delegate code

    func httpProxyEnabled() {
        // handle HTTP proxy detection
    }
}

The delegate can be added in AppProtectionRasp. When it is no longer needed, it can be removed again.

let raspDelegate = RaspDelegate()
appProtection.rasp.addDelegate(raspDelegate)
appProtection.rasp.removeDelegate(raspDelegate)

Triggering a Manual Check

HTTP proxy detection check can be triggered manually in AppProtectionRasp by getting the isHttpProxyEnabled property value. A simple Bool answer is given.

let isHttpProxyEnabled = appProtection.rasp.isHttpProxyEnabled

More information on general RASP feature configuration and usage can be found in this overview.

Repackaging Detection

Repackaging detection is a security feature that detects if the application was modified and resigned with a different signing certificate.

Malwarelytics for Apple is able to detect that the app has been repackaged and can be configured to terminate the app in that case.

Repackaging detection is turned off on the simulator.

Configuration

let raspConfig = AppProtectionRaspConfig(
    repackage: RepackageConfig
    // configuration of other RASP features
)

Available values of RepackageConfig:

Value Description
.noAction(
trustedCerts: [TrustedCertificate])
indicates that repackaging will not be automatically detected. A manual check is still possible.
.notify(
trustedCerts: [TrustedCertificate])
indicates that repackaging will be automatically detected and the delegates will be notified via the repackageDetected() method.
.exit(
trustedCerts: [TrustedCertificate],
exitUrl: String?)
indicates that the repackaging will be automatically detected and the app will be terminated when the repackaging is automatically detected.

Repackaging detection defaults to .noAction([]).

List of available parameters for some config values:

Parameter Description
trustedCerts: [TrustedCertificate] defines trusted certificates for ad-hoc or enterprise distribution. AppStore signing certificates are trusted by default.
exitUrl: String? defines the URL to be opened when the app is terminated because of the automatic detection. Defaults to nil.

Certificate Configuration Details

To properly configure the repackaging detection, you need to get the Base64 encoded string of your signing certificate:

  1. Open the Keychain Access application.
  2. Find a certificate that will be used to sign your application, for example, “Apple Development: Jan Tester (c)”.
  3. Right-click on the item and click “Export…”.
  4. Export the certificate in the .cer format.
  5. Open up the terminal and cd into the folder with your exported certificate.
  6. Encode the certificate in Base64 with cat your_exported.cer | base64.
  7. Copy the output of the command and use it as a parameter for the repackage detection configuration:
// Prepare the RASP feature configuration
let raspConfig = AppProtectionRaspConfig(
    // ...
    repackage: .exit([AppProtectionTrustedCert(withBase64EncodedString: "BASE_64_ENCODED_CERT")!], "https://myurl.com/repackage-explained")
    // ...
)

Tip: To hide the string in your binary, use the init constructor for AppProtectionTrustedCert with Data or [UInt8] arguments.

Usage

After service creation, the repackaging detection feature can be accessed via AppProtectionRasp. This can be used to add a delegate or to trigger a manual repackaging detection check.

Observing Detection

Repackaging detection can trigger a certain action. To achieve that, a delegate needs to be added.

Delegate configuration:

class RaspDelegate: AppProtectionRaspDelegate {

    // other delegate code

    func repackageDetected() {
        // handle repackaging detection
    }
}

The delegate can be added in AppProtectionRasp. When it is no longer needed, it can be removed again.

let raspDelegate = RaspDelegate()
appProtection.rasp.addDelegate(raspDelegate)
appProtection.rasp.removeDelegate(raspDelegate)

Triggering a Manual Check

The repackaging detection check can be triggered manually in AppProtectionRasp by getting the isRepackaged property value. A simple Bool answer is given.

let isRepackaged = appProtection.rasp.isRepackaged

More information on general RASP feature configuration and usage can be found in this overview.

Screen Capture Detection

Some types of apps like banking and financial apps handle a lot of sensitive information. The leakage of this data through screen capture is undesirable and should be prevented.

Malwarelytics for Apple is able to detect that the screen is being captured and can be configured to either hide the screen or terminate the app in that case.

Configuration

let raspConfig = AppProtectionRaspConfig(
    screenCapture: ScreenCaptureDetectionConfig
    // configuration of other RASP features
)

Available values of ScreenCaptureDetectionConfig:

Value Description
.noAction indicates that screen capture will not be automatically detected. A manual check is still possible.
.notify indicates that screen capture will be automatically detected and the delegates will be notified via the screenCapturedChanged(Bool) method.
.hide(
overlay: Overlay)
indicates that the app will hide its content when the screen capture is detected. The delegates will be notified via the screenCapturedChanged(Bool) method.
.exit(
exitUrl: String?)
indicates that the screen capture will be automatically detected and the app will be terminated when the screen capture is automatically detected.

Screen capture detection defaults to .notify.

List of available parameters for some config values:

Parameter Description
overlay: Overlay defines the overlay that will be used to hide the contents of the app. Defaults to .default.
exitUrl: String? defines the URL to be opened when the app is terminated because of the automatic detection. Defaults to nil.

Overlay Configuration

Available value of Overlay:

Value Description
.default defines the default behavior that covers the screen with a solid color and application icon.
.color(
color: UIColor)
defines that the screen will be covered with a solid color.
.image(
image: UIImage)
defines that the screen will be covered with an image.

Usage

After service creation, the screen capture detection feature can be accessed via AppProtectionRasp. This can be used to add a delegate or to trigger a manual screen capture detection check.

Observing Detection

The screen capture detection can trigger a certain action. To achieve that, a delegate needs to be added.

Delegate configuration:

class RaspDelegate: AppProtectionRaspDelegate {

    // other delegate code

    func screenCapturedChanged(isCaptured: Bool) {
        // handle screen capture detection (casting to a different device)
    }
}

The delegate can be added in AppProtectionRasp. When it is no longer needed, it can be removed again.

let raspDelegate = RaspDelegate()
appProtection.rasp.addDelegate(raspDelegate)
appProtection.rasp.removeDelegate(raspDelegate)

Triggering a Manual Check

The screen capture detection check can be triggered manually in AppProtectionRasp by getting the isScreenCaptured property value. A simple Bool answer is given.

let isScreenCaptured = appProtection.rasp.isScreenCaptured

More information on general RASP feature configuration and usage can be found in this overview.

VPN Detection

A VPN (virtual private network) can be used to tunnel all traffic from the device through a remote server. Although VPNs are primarily used to add a level of security, they can also pose a danger when the device connects to a dubious network. For example, some free VPN services might use invasive advertising or sell browsing data to third parties. Last but not least usage of a VPN might be restricted or illegal in some countries.

Malwarelytics for Apple is able to detect that the app is using a VPN and can be configured to terminate the app in that case.

Configuration

let raspConfig = AppProtectionRaspConfig(
    vpnDetection: DetectionConfig
    // configuration of other RASP features
)

Available values of DetectionConfig:

Value Description
.noAction indicates that the VPN will not be automatically detected. A manual check is still possible.
.notify indicates that the VPN will be automatically detected and the delegates will be notified via the vpnChanged(Bool) method.
.exit(
exitUrl: String?)
indicates that the VPN will be automatically detected and the app will be terminated when the VPN is automatically detected.

VPN detection defaults to .notify.

List of available parameters for some config values:

Parameter Description
exitUrl: String? defines the URL to be opened when the app is terminated because of the automatic detection. Defaults to nil.

Usage

After service creation, the VPN detection feature can be accessed via AppProtectionRasp. This can be used to add a delegate or to trigger a manual VPN detection check.

Observing Detection

VPN detection can trigger a certain action. To achieve that, a delegate needs to be added.

Delegate configuration:

class RaspDelegate: AppProtectionRaspDelegate {

    // other delegate code

    func vpnChanged(active: Bool) {
        // handle VPN detection
    }
}

The delegate can be added in AppProtectionRasp. When it is no longer needed, it can be removed again.

let raspDelegate = RaspDelegate()
appProtection.rasp.addDelegate(raspDelegate)
appProtection.rasp.removeDelegate(raspDelegate)

Triggering a Manual Check

VPN detection check can be triggered manually in AppProtectionRasp by getting the isVpnActive property value. A simple Bool answer is given.

let isVpnActive = appProtection.rasp.isVpnActive

More information on general RASP feature configuration and usage can be found in this overview.

Active Call Detection

Social engineering scams pose a serious problem for today's banking and fintech apps. Malicious actors try to trick users into sending money away or into performing other harmful activities such as approving access to their accounts. This is often performed by direct phone calls. The actor first gains the user's trust and then instructs him/her directly to perform a harmful action. For this reason, active call detection is an integral part of a financial app. The app can use the detection to prevent the user from doing sensitive operations while off-hook.

Malwarelytics for Apple is able to detect active calls.

Configuration

let raspConfig = AppProtectionRaspConfig(
    callDetection: SimpleDetectionConfig
    // configuration of other RASP features
)

Available values of SimpleDetectionConfig:

Value Description
.noAction indicates that an active call will not be automatically detected. A manual check is still possible.
.notify indicates that an active call will be automatically detected and the delegates will be notified via the onCallChanged(Bool) method.

Active call detection defaults to .notify.

Usage

After service creation, the active call detection feature can be accessed via AppProtectionRasp. This can be used to add a delegate or to trigger a manual active call detection check.

Observing Detection

Active call detection can trigger a certain action. To achieve that, a delegate needs to be added.

Delegate configuration:

class RaspDelegate: AppProtectionRaspDelegate {

    // other delegate code

    func onCallChanged(isOnCall: Bool) {
        // handle active call detection
    }
}

The delegate can be added in AppProtectionRasp. When it is no longer needed, it can be removed again.

let raspDelegate = RaspDelegate()
appProtection.rasp.addDelegate(raspDelegate)
appProtection.rasp.removeDelegate(raspDelegate)

Triggering a Manual Check

Active call detection check can be triggered manually in AppProtectionRasp by getting the isOnCall property value. A simple Bool answer is given.

let isOnCall = appProtection.rasp.isOnCall

More information on general RASP feature configuration and usage can be found in this overview.

App Presence Detection

Some mobile applications, while being legitimate apps with their use cases, pose a great danger to banking and fintech applications. Such apps are often used to scam users and perform financial fraud.

An important category of these apps are so-called “remote desktop apps”. These apps allow remote access and control of mobile devices. Unfortunately, these apps recently became a vital part of many fraudulent attacks.

Malwarelytics for Apple is able to detect some unwanted remote desktop apps.

Configuration

Configuration of this feature is more complex than other detections. It consists of two parts:

  1. Configuration of query URL schemes in the application’s Info.plist.
  2. Configuration of appPresence in AppProtectionRaspConfig.

Every app that should be detected has to be added in both places. This also means that the list of detected apps is limited by the compile-time configuration of Info.plist file.

Configuration of Query URL Schemes

Query URL schemes have to be configured in the application’s Info.plist. A query scheme of each app has to be added as an item in the “Queried URL Schemes” key.

The step-by-step process is:

  1. Open the Xcode project.
  2. In the Project Navigator, find the app’s Info.plist file and open it.
  3. Click the “+” button in the top-right corner of the Info.plist editor.
  4. In the new row, set the key to “Queried URL Schemes”
  5. Click the arrow next to “Queried URL Schemes” to expand it.
  6. Click the “+” button next to “Queried URL Schemes” and add the URL scheme for the app you want to detect.
  7. Save the changes.

Some of the frequently used remote desktop apps’ with their URL schemes can be found in the SDK in the KnownApps struct.

App Presence Configuration in AppProtectionRaspConfig

Configuration of the detection in AppProtectionRaspConfig is similar to other RASP features:

let raspConfig = AppProtectionRaspConfig(
    appPresence: AppPresenceDetectionConfig
    // configuration of other RASP features
)

Available values of DetectionConfig:

Value Description
.manual(
apps: [DetectableApp])
indicates that app presence will not be automatically detected. A manual check is still possible.
.notify(
apps: [DetectableApp])
indicates that app presence will be automatically detected and the delegates will be notified via the installedAppsChanged([DetectableApp]) method.

The app presence detection defaults to .manual([]).

List of available parameters for some config values:

Parameter Description
apps: [DetectableApp] defines the list of detectable apps.

Detectable App Configuration

A detectable app is defined by several properties:

Property Description
deeplinkProtocols: [String] specifies deep links defined for the app.
name: String specifies name of the application. The name can be chosen at will and does not need to reflect the name in the AppStore or of an installed app.
category: Category specifies category of the application. Currently only .remoteDesktop is available.
tag: String? specifies any additional information that should be passed to the remote server.

Usage

After service creation, the app presence detection feature can be accessed via AppProtectionRasp. This can be used to add a delegate or to trigger a manual app presence detection check.

Observing Detection

The app presence detection can trigger a certain action. To achieve that, a delegate needs to be added.

Delegate configuration:

class RaspDelegate: AppProtectionRaspDelegate {

    // other delegate code

    func installedAppsChanged(installedApps: [DetectableApp]) {
        // handle app presence detection
    }
}

The delegate can be added in AppProtectionRasp. When it is no longer needed, it can be removed again.

let raspDelegate = RaspDelegate()
appProtection.rasp.addDelegate(raspDelegate)
appProtection.rasp.removeDelegate(raspDelegate)

Triggering a Manual Check

The app presence detection check can be triggered manually in AppProtectionRasp by getting the installedApps property value. The method returns [DetectableApp].

let installedApps = appProtection.rasp.installedApps

More information on general RASP feature configuration and usage can be found in this overview.

User Screenshot Detection

User screenshots can leak sensitive information from an application. For this reason, it's important to try to avoid it if possible. Unfortunately, on iOS it's not possible to block screenshots, the application can only detect that a screenshot was taken and possibly inform the user that it's necessary to be careful with sensitive data.

Malwarelytics for Apple is able to detect that a screenshot was taken and notify the app about it.

Configuration

There’s no configuration for the feature.

Usage

After service creation, the user screenshot detection feature can be accessed via AppProtectionRasp. This can be used to add a delegate.

Observing Detection

The user screenshot detection can trigger a certain action. To achieve that, a delegate needs to be added.

Delegate configuration:

class RaspDelegate: AppProtectionRaspDelegate {

    // other delegate code

    func userScreenshotDetected() {
        // handle user screenshot detection
    }
}

The delegate can be added in AppProtectionRasp. When it is no longer needed, it can be removed again.

let raspDelegate = RaspDelegate()
appProtection.rasp.addDelegate(raspDelegate)
appProtection.rasp.removeDelegate(raspDelegate)

More information on general RASP feature configuration and usage can be found in this overview.

System Passcode Detection

One of the key methods of protecting a device is to use a system passcode that prevents unauthorized use of the device.

Malwarelytics for Apple is able to detect whether a system passcode is enabled and when the system passcode configuration changes.

Configuration

There’s no configuration for the feature.

Usage

After service creation, the system passcode detection feature can be accessed via AppProtectionRasp. This can be used to add a delegate or to trigger a manual system passcode detection check.

Observing Detection

The system passcode detection can trigger an action when the passcode configuration changes. To achieve that, a delegate needs to be added.

Delegate configuration:

class RaspDelegate: AppProtectionRaspDelegate {

    // other delegate code

    func systemPasscodeConfigurationChanged(enabled: Bool) {
        // handle system passcode configuration change detection
    }
}

The delegate can be added in AppProtectionRasp. When it is no longer needed, it can be removed again.

let raspDelegate = RaspDelegate()
appProtection.rasp.addDelegate(raspDelegate)
appProtection.rasp.removeDelegate(raspDelegate)

Triggering a Manual Check

The system passcode detection check can be triggered manually in AppProtectionRasp by getting the isSystemPasscodeEnabled property value. A simple Bool answer is given.

let isSystemPasscodeEnabled = appProtection.rasp.isSystemPasscodeEnabled

More information on general RASP feature configuration and usage can be found in this overview.

System Biometry Detection

The usage of biometry is a means of authentication without the need to fill in a PIN or password on a device. Besides being convenient, it also lowers the risk of PIN or password theft. PINs or passwords can be stolen by various means such as shoulder surfing.

Malwarelytics for Apple is able to detect whether a system biometry is enabled and when the system biometry configuration changes.

Configuration

There’s no configuration for the feature.

Usage

After service creation, the system biometry detection feature can be accessed via AppProtectionRasp. This can be used to add a delegate or to trigger a manual system biometry detection check.

Observing Detection

The system biometry detection can trigger an action when the biometry configuration changes. To achieve that, a delegate needs to be added.

Delegate configuration:

class RaspDelegate: AppProtectionRaspDelegate {

    // other delegate code

    func systemBiometryConfigurationChanged(enabled: Bool) {
        // handle system biometry configuration change detection
    }
}

The delegate can be added in AppProtectionRasp. When it is no longer needed, it can be removed again.

let raspDelegate = RaspDelegate()
appProtection.rasp.addDelegate(raspDelegate)
appProtection.rasp.removeDelegate(raspDelegate)

Triggering a Manual Check

The system biometry detection check can be triggered manually in AppProtectionRasp by getting the isSystemBiometryEnabled property value. A simple Bool answer is given.

let isSystemBiometryEnabled = appProtection.rasp.isSystemBiometryEnabled

More information on general RASP feature configuration and usage can be found in this overview.

Release Notes

Current Release

Release 3.1.1

  • Repackage detection is only available on a real device

Release 3.1.0

  • Improve VPN detection (#135)
  • Improve jailbreak detection with detection of rootless palera1n (#136)
  • Fix .exit(String?) with invalid exitUrl string (#134)

Previous Releases

Release 3.0.0

  • Renamed the swift module to WultraAppProtection from AppProtection

Release 2.1.2

  • Change debugger detection default to .notify (#113)
  • Updated TestFlight certificate

Release 2.1.1

  • Add unwanted apps detection (#104)

Release 2.0.0

  • Add detection of active call
  • Add option to run in RASP-only (offline) mode

Release 1.2.2

  • Add support for TestFlight builds (#90)

Release 1.2.1

  • Remove X-Signature-Origin-App header from signature verification (#85)
  • Expose avUid (internal device id) (#87)

Release 1.2.0

  • Add overlay when screen is captured (#77)
  • Add VPN detection (#72)

Release 1.1.2

  • Add test environment (#73)
  • Improve CPU usage (#74)

Release 1.1.1

  • Add clientAppDeviceId (#68)
  • Add config items for customer application package names and grouping (#67)

Release 1.1.0

  • Add Swift Package Manager support
  • Add other fixes and improvements

Release 1.0.3

  • Fix issue when device id could be automaticaly renewed
  • Add other fixes and improvements

Release 1.0.2

  • Fix potential crash when the app was running for a long time
  • Add other fixes and improvements

Release 1.0.1

  • Add fixes and improvements

Release 1.0.0

  • Remove singleton pattern. Use a public initializer to create an instance note that only one instance can be created.
  • Remove configuration builders. Configuration is now struct with initializer.
  • Add other fixes and improvements

Release 0.9.9

  • Add fixes related to Core Data crashes

Release 0.9.8

  • Initial beta release
Generated on Jul 22, 2024 (16:51)