Use frida and objection to penetration test iOS app security
Introduction
What is frida?
frida is a dynamic code instrumentation toolkit. It lets you inject snippets of JavaScript or your own library into native apps on Windows, macOS, GNU/Linux, iOS, Android, and QNX. Frida also provides you with some simple tools built on top of the Frida API.
What is objection?
objection
is a runtime mobile exploration toolkit, powered by Frida, built to help you assess the security posture of your mobile applications, without needing a jailbreak.
objection
supports both iOS and Android.
objection
for iOS key features
:
- Patch iOS applications, embedding a Frida gadget that can be used with objection or just Frida itself.
- Interact with the filesystem, listing entries as well as upload & download files where permitted.
- Perform various memory related tasks, such as listing loaded modules and their respective exports.
- Attempt to bypass and simulate jailbroken or rooted environments.
- Discover loaded classes and list their respective methods.
- Perform common SSL pinning bypasses.
- Dynamically dump arguments from methods called as you use the target application.
- Interact with SQLite databases inline without the need to download the targeted database and use an external tool.
- Execute custom Frida scripts.
- Dump the iOS keychain, and export it to a file.
- Dump data from common storage such as NSUserDefaults and the shared NSHTTPCookieStorage.
- Dump various formats of information in human readable forms.
- Bypass certain forms of TouchID restrictions.
- Watch for method executions by targeting all methods in a class, or just a single method.
- Monitor the iOS pasteboard.
- Dump encoded .plist files in a human readable format without relying on external parsers.
Install frida and objection
Both frida
and objection
can be installed from pip
(I use python3):
$ pip3 install frida-tools
$ pip3 install objection
After installation, you get a batch of command line tools:
frida frida-create frida-kill frida-ps frida-rm
frida-apk frida-discover frida-ls frida-pull frida-trace
frida-compile frida-join frida-ls-devices frida-push
objection
Setup frida on iOS
Frida supports two modes of operation: jailbroken or not jailbroken.
This article focus on how to use frida
and objection
on not jailbroken iOS devices.
Frida is able to instrument debuggable apps, and will inject Gadget automatically as of Frida 12.7.12.
Only a few requirements to be aware of:
- The iOS device should ideally be running iOS 13 or newer.
- The Developer Disk Image must be mounted. Xcode will mount it automatically as soon as it discovers the iOS USB device, but you can also do it manually by using
ideviceimagemounter
. - Latest Gadget (Can download from https://github.com/frida/frida/releases
) must be present in the user’s cache directory. On macOS this is
~/.cache/frida/gadget-ios.dylib
.
Use frida
on iOS
To use frida
, first you need connect your device with a usb cable.
List running processes / apps
frida-ps
is a command-line tool for listing processes, which is very useful when interacting with a remote system.
$ frida-ps -h
usage: frida-ps [options]
optional arguments:
-h, --help show this help message and exit
-D ID, --device ID connect to device with the given ID
-U, --usb connect to USB device
-R, --remote connect to remote frida-server
-H HOST, --host HOST connect to remote frida-server on HOST
--certificate CERTIFICATE
speak TLS with HOST, expecting CERTIFICATE
--origin ORIGIN connect to remote server with “Origin” header set to ORIGIN
--token TOKEN authenticate with HOST using TOKEN
--keepalive-interval INTERVAL
set keepalive interval in seconds, or 0 to disable (defaults to -1 to auto-select based on
transport)
--p2p establish a peer-to-peer connection with target
--stun-server ADDRESS
set STUN server ADDRESS to use with --p2p
--relay address,username,password,turn-{udp,tcp,tls}
add relay to use with --p2p
-O FILE, --options-file FILE
text file containing additional command line options
--version show program's version number and exit
-a, --applications list only applications
-i, --installed include all installed applications
-j, --json output results as JSON
List all running processes
To list all running processes, use frida-ps -U
. Example:
$ frida-ps -U
PID Name
----- --------------------------------------------------------
631 AMDEngagementExtension
523 AMPIDService
14447 ANECompilerService
4939 ANEStorageMaintainer
14339 ASPCarryLog
11699 AccountSubscriber
11706 AccountSubscriber
6859 Telegram
...
To list all running apps (not include system processes), use frida-ps -Ua
. For example:
$ frida-ps -Ua
PID Name Identifier
----- ----------------------------- ----------------------------
10205 App Store com.apple.AppStore
6302 Calendar com.apple.mobilecal
637 Clock com.apple.mobiletimer
5980 Firefox org.mozilla.ios.Firefox
8954 Google com.google.GoogleMobile
321 Mail com.apple.mobilemail
5485 Maps com.apple.Maps
318 Notes com.apple.mobilenotes
5308 Photos com.apple.mobileslideshow
9423 Podcasts com.apple.podcasts
13559 PosterBoard com.apple.PosterBoard
6248 Safari com.apple.mobilesafari
571 Settings com.apple.Preferences
6859 Telegram ph.telegra.Telegraph
11181 TestFlight com.apple.TestFlight
...
List all installed applications
To list all installed applications, use frida-ps -Uai
. For example:
$ frida-ps -Uai 1 ↵
PID Name Identifier
----- ----------------------------- ---------------------------------------------
10205 App Store com.apple.AppStore
6302 Calendar com.apple.mobilecal
637 Clock com.apple.mobiletimer
5980 Firefox org.mozilla.ios.Firefox
321 Mail com.apple.mobilemail
5485 Maps com.apple.Maps
318 Notes com.apple.mobilenotes
5308 Photos com.apple.mobileslideshow
9423 Podcasts com.apple.podcasts
13559 PosterBoard com.apple.PosterBoard
6248 Safari com.apple.mobilesafari
571 Settings com.apple.Preferences
6859 Telegram ph.telegra.Telegraph
11181 TestFlight com.apple.TestFlight
- Books com.apple.iBooks
- Calculator com.apple.calculator
- Camera com.apple.camera
...
Use objection
on iOS
objection
usage:
$ objection --help 2 ↵
Usage: objection [OPTIONS] COMMAND [ARGS]...
_ _ _ _
___| |_|_|___ ___| |_|_|___ ___
| . | . | | -_| _| _| | . | |
|___|___| |___|___|_| |_|___|_|_|
|___|(object)inject(ion)
Runtime Mobile Exploration
by: @leonjza from @sensepost
By default, communications will happen over USB, unless the --network option
is provided.
Options:
-N, --network Connect using a network connection instead of USB.
-h, --host TEXT [default: 127.0.0.1]
-p, --port INTEGER [default: 27042]
-ah, --api-host TEXT [default: 127.0.0.1]
-ap, --api-port INTEGER [default: 8888]
-g, --gadget TEXT Name of the Frida Gadget/Process to connect to.
[default: Gadget]
-S, --serial TEXT A device serial to connect to.
-d, --debug Enable debug mode with verbose output. (Includes
agent source map in stack traces)
--help Show this message and exit.
Commands:
api Start the objection API server in headless mode.
device-type Get information about an attached device.
explore Start the objection exploration REPL.
patchapk Patch an APK with the frida-gadget.so.
patchipa Patch an IPA with the FridaGadget dylib.
run Run a single objection command.
signapk Zipalign and sign an APK with the objection key.
version Prints the current version and exists.
explore start an interactive command line to interaction with app. For example:
$ objection -g 19978 explore
Using USB device `iPhone`
Agent injected and responds ok!
_ _ _ _
___| |_|_|___ ___| |_|_|___ ___
| . | . | | -_| _| _| | . | |
|___|___| |___|___|_| |_|___|_|_|
|___|(object)inject(ion) v1.11.0
Runtime Mobile Exploration
by: @leonjza from @sensepost
[tab] for command suggestions
com.example.foobar on (iPhone: 16.0) [usb] #
Use -g <pid>
to attach an exist app. Can also use -g <bundle id>
to launch app and attach it. For example:
$ objection -g com.example.foobar explore
ios info binary
com.example.foobar on (iPhone: 16.0) [usb] # ios info binary
Name Type Encrypted PIE ARC Canary Stack Exec RootSafe
---------------- ------- --------- ----- ----- ------ ---------- --------
Foobar execute False True True False False False
FoobarReport dylib False False False False False False
Get app bundle info
Use ios bundles
to list app frameworks and bundles. For example:
com.example.foobar on (iPhone: 16.0) [usb] # ios bundles list_frameworks
Executable Bundle Version Path
---------------- --------------------------- ------- -------------------------------------------
FoobarReport com.example.FoobarReport 1 ...pp/Frameworks/FoobarReport.framework
...rivateFrameworks/AppleCVAPhoto.framework
com.example.foobar on (iPhone: 16.0) [usb] # ios bundles list_bundles
Executable Bundle Version Path
---------------- -------------------------------- ------- -------------------------------------------
/usr/lib/system
...eFrameworks/WebCore.framework/Frameworks
/usr/lib/log
BoundingPathData com.apple.uikit.BoundingPathData 1.0 ...itCore.framework/BoundingPathData.bundle
Foobar com.example.foobar 1.0 ...240-4B53-8D7A-0403A37E2D4A/Foobar.app
/usr/lib/swift
/usr/lib
RawCamera com.apple.RawCamera.bundle 9.13.0 ...em/Library/CoreServices/RawCamera.bundle
List app directories
com.example.foobar on (iPhone: 16.0) [usb] # env
Name Path
----------------- ---------------------------------------------------------------------------------------------
BundlePath /private/var/containers/Bundle/Application/83F9204D-7240-4B53-8D7A-0403A37E2D4C/Foobar.app
CachesDirectory /var/mobile/Containers/Data/Application/BA1D1280-9900-439E-BEC3-0442B27E177C/Library/Caches
DocumentDirectory /var/mobile/Containers/Data/Application/BA1D1280-9900-439E-BEC3-0442B27E177C/Documents
LibraryDirectory /var/mobile/Containers/Data/Application/BA1D1280-9900-439E-BEC3-0442B27E177C/Library
List files in app directory
Use ls
to list files in app directory, use cd
to check working directory, for example:
com.example.foobar on (iPhone: 16.0) [usb] # ls
NSFileType Perms NSFileProtection Read Write Owner Group Size Creation Name
---------- ----- ---------------- ---- ----- -------------- -------------- --------- ------------------------- ------------------------
Directory 493 None True False _installd (33) _installd (33) 96.0 B 1970-01-01 00:00:00 +0000 PlugIns
Directory 493 None True False _installd (33) _installd (33) 128.0 B 1970-01-01 00:00:00 +0000 Base.lproj
Directory 493 None True False _installd (33) _installd (33) 96.0 B 1970-01-01 00:00:00 +0000 _CodeSignature
Directory 493 None True False _installd (33) _installd (33) 64.0 B 1970-01-01 00:00:00 +0000 META-INF
Directory 493 None True False _installd (33) _installd (33) 96.0 B 1970-01-01 00:00:00 +0000 Frameworks
Regular 420 None True False _installd (33) _installd (33) 1.6 KiB 2022-03-14 16:08:02 +0000 Info.plist
Regular 420 None True False _installd (33) _installd (33) 8.0 B 2022-03-14 16:08:02 +0000 PkgInfo
Regular 420 None True False _installd (33) _installd (33) 265.4 KiB 2022-03-14 15:57:10 +0000 embedded.mobileprovision
Regular 493 None True False _installd (33) _installd (33) 422.9 KiB 2022-03-14 16:15:12 +0000 Foobar
Readable: True Writable: False
com.example.foobar on (iPhone: 16.0) [usb] # cd PlugIns
com.example.foobar on (iPhone: 16.0) [usb] # ls
NSFileType Perms NSFileProtection Read Write Owner Group Size Creation Name
---------- ----- ---------------- ---- ----- -------------- -------------- ------- ------------------------- ----------------------------
Directory 493 None True False _installd (33) _installd (33) 224.0 B 1970-01-01 00:00:00 +0000 foobar-shareextension.appex
Readable: True Writable: False
com.example.foobar on (iPhone: 16.0) [usb] # pwd
Current directory: /private/var/containers/Bundle/Application/83F9204D-7240-4B53-8D7A-0403A37E2D4C/Foobar.app/PlugIns
View file content
Use file cat
to view file content. It will download file then cat it locally. For example:
[usb] # file cat Info.plist
For plist
type file, can also use ios plist cat <plist file path>
to decode plist file. For example:
[usb] # ios plist cat Info.plist
Download file from device
Use file download <file path>
to download a file from device. For example:
com.example.foobar on (iPhone: 16.0) [usb] # file download Info.plist
Downloading /private/var/containers/Bundle/Application/83F9204D-7240-4B53-8D7A-0403A37E2D4C/Foobar.app/Info.plist to Info.plist
Streaming file from device...
Writing bytes to destination...
Successfully downloaded /private/var/containers/Bundle/Application/83F9204D-7240-4B53-8D7A-0403A37E2D4C/Foobar.app/Info.plist to Info.plist
Get cookie
Use ios cookies get
to get cookies. For example:
com.example.foobar on (iPhone: 16.0) [usb] # ios cookies get
No cookies found
Dump all of the credentials
Use ios nsurlcredentialstorage dump
to dump all of the credentials in the shared NSURLCredentialStorage
. For example:
com.example.foobar on (iPhone: 16.0) [usb] # ios nsurlcredentialstorage dump
Protocol Host Port Authentication Method User Password
-------- ---- ---- --------------------- ---- --------
Dump userdefaults
Use ios nsuserdefaults get
to get all of the entries. For example:
com.example.foobar on (iPhone: 16.0) [usb] # ios nsuserdefaults get
{
AKLastEmailListRequestDateKey = "2022-08-20 16:01:39 +0000";
AKLastIDMSEnvironment = 0;
...
}
List all loaded frameworks/modules
Use memory list modules
to list all the loaded modules in the current process. For example:
com.example.foobar on (iPhone: 16.0) [usb] # memory list modules
Save the output by adding `--json modules.json` to this command
Name Base Size Path
------------------------------------- ----------- -------------------- ------------------------------------------------------------------------------
Foobar 0x10010c000 131072 (128.0 KiB) /private/var/containers/Bundle/Application/83F9204D-7240-4B53-8D7A-0403A37E...
FoobarShare 0x100200000 65536 (64.0 KiB) /private/var/containers/Bundle/Application/83F9204D-7240-4B53-8D7A-0403A37E...
Foundation 0x1cffb8000 9744384 (9.3 MiB) /System/Library/Frameworks/Foundation.framework/Foundation
libobjc.A.dylib 0x1cef28000 277568 (271.1 KiB) /usr/lib/libobjc.A.dylib
libSystem.B.dylib 0x222101000 8192 (8.0 KiB) /usr/lib/libSystem.B.dylib
Combine 0x1de138000 1376248 (1.3 MiB) /System/Library/Frameworks/Combine.framework/Combine
CoreFoundation 0x1d5b24000 4083712 (3.9 MiB) /System/Library/Frameworks/CoreFoundation.framework/CoreFoundation
UIKit 0x24785a000 12288 (12.0 KiB) /System/Library/Frameworks/UIKit.framework/UIKit
libswiftCore.dylib 0x1cfa1d000 5672960 (5.4 MiB) /usr/lib/swift/libswiftCore.dylib
libswiftCoreFoundation.dylib 0x203615000 32756 (32.0 KiB) /usr/lib/swift/libswiftCoreFoundation.dylib
libswiftCoreGraphics.dylib 0x1f8310000 28672 (28.0 KiB) /usr/lib/swift/libswiftCoreGraphics.dylib
libswiftCoreImage.dylib 0x2353d1000 4096 (4.0 KiB) /usr/lib/swift/libswiftCoreImage.dylib
...
List module exports
Use memory list exports <module name>
to list the exports of a module. module name list can get from memory list modules
.
For example:
com.example.foobar on (iPhone: 16.0) [usb] # memory list exports Pegasus
Save the output by adding `--json exports.json` to this command
Type Name Address
-------- ----------------------------------------------------------------------------- -----------
function NSStringFromPGBackgroundPIPAuthorizationState 0x1fbc94d44
variable OBJC_CLASS_$_PGBackgroundPIPAuthorization 0x22cdb76a8
variable OBJC_CLASS_$_PGCommand 0x22cdb6f50
variable OBJC_CLASS_$_PGControlsViewModel 0x22cdb6f78
variable OBJC_CLASS_$_PGControlsViewModelValues 0x22cdb6fa0
variable OBJC_CLASS_$_PGInterruptionAssistant 0x22cdb6c58
variable OBJC_CLASS_$_PGMenuItem 0x22cdb7798
variable OBJC_CLASS_$_PGPictureInPictureApplication 0x22cdb6c80
variable OBJC_CLASS_$_PGPictureInPictureController 0x22cdb6d48
variable OBJC_CLASS_$_PGPictureInPictureProxy 0x22c074fc8
variable OBJC_CLASS_$_PGPictureInPictureViewController 0x22c0750e0
variable OBJC_CLASS_$_PGPlaybackState 0x22cdb7630
variable OBJC_CLASS_$_PGPlaybackStatePrerollAttributes 0x22cdb7518
variable OBJC_METACLASS_$_PGBackgroundPIPAuthorization 0x22cdb76d0
variable OBJC_METACLASS_$_PGCommand 0x22cdb6e60
variable OBJC_METACLASS_$_PGControlsViewModel 0x22cdb7220
variable OBJC_METACLASS_$_PGControlsViewModelValues 0x22cdb7248
variable OBJC_METACLASS_$_PGInterruptionAssistant 0x22cdb6cd0
variable OBJC_METACLASS_$_PGMenuItem 0x22cdb77c0
variable OBJC_METACLASS_$_PGPictureInPictureApplication 0x22cdb6cf8
variable OBJC_METACLASS_$_PGPictureInPictureController 0x22cdb6d70
variable OBJC_METACLASS_$_PGPictureInPictureProxy 0x22c074ff0
variable OBJC_METACLASS_$_PGPictureInPictureViewController 0x22c075248
variable OBJC_METACLASS_$_PGPlaybackState 0x22cdb7680
variable OBJC_METACLASS_$_PGPlaybackStatePrerollAttributes 0x22cdb7540
variable PGPegasusErrorDomain 0x225d13168
variable PGPictureInPictureProxyPictureInPictureActiveChangedNotification 0x225d12a48
variable PGPictureInPictureViewControllerPrefersIdleTimerDisabledDidChangeNotification 0x225d125b0
Use can also write output in json format with --json <output file path>
. For example:
com.example.foobar on (iPhone: 16.0) [usb] # memory list exports Pegasus --json Pegasus_exports.json
Writing exports as json to Pegasus_exports.json...
Wrote exports to: Pegasus_exports.json
Dump app memory
Use memory dump all <output file path>
to dump app memory to
com.example.foobar on (iPhone: 16.0) [usb] # memory dump all appmem.dump
Will dump 111 rw- images, totalling 718.5 MiB
Dumping 512.0 MiB from base: 0x280000000 [####################################] 100%
After dump memory, you can use strings
to check readable strings in memory, for example:
$ strings appmem.dump | grep -i username # search is there any username string in memory.
$ strings appmem.dump | grep -i token # search is there any token string in memory.
Dump/add/clear iOS key chain
Use ios keychain
to add/dump/clear iOS keychain:
add Add an entry to the iOS keychain
dump Dump the keychain for the current app's entitlement group
clear Delete all keychain entries for the current app's entitlement group
ios keychain
examples:
com.example.foobar on (iPhone: 16.0) [usb] # ios keychain dump
Note: You may be asked to authenticate using the devices passcode or TouchID
Save the output by adding `--json keychain.json` to this command
Dumping the iOS keychain...
Created Accessible ACL Type Account Service Data
2022-08-16 04:06:05 +0000 WhenUnlockedThisDeviceOnly None Password username foo
2022-08-16 04:06:05 +0000 WhenUnlockedThisDeviceOnly None Password token token12345
...
com.example.foobar on (iPhone: 16.0) [usb] # ios keychain clear
Are you sure you want to clear the iOS keychain? [y/N]: y
Clearing the keychain...
Keychain cleared
monitor pasteboard
Use ios pasteboard monitor
to monitor the iOS pasteboard. It will print pasteboard content after issue this command (it may take a few seconds). For example:
com.example.foobar on (iPhone: 16.0) [usb] # ios pasteboard monitor
com.example.foobar on (iPhone: 16.0) [usb] # (agent) [pasteboard-monitor] Data: loading this page
Disable sslpinning
Use ios sslpinning disable
to disable ios sslpinning. For example:
com.example.foobar on (iPhone: 16.0) [usb] # ios sslpinning disable
(agent) Hooking common framework methods
(agent) Found NSURLSession based classes. Hooking known pinning methods.
(agent) Hooking lower level SSL methods
(agent) Hooking lower level TLS methods
(agent) Hooking BoringSSL methods
(agent) Registering job 759888. Type: ios-sslpinning-disable
Troubleshootings
Unable to connect to the frida server
Use objection
and got following error:
$ objection -g 10204 explore
Checking for a newer version of objection...
Using USB device `iPhone`
Unable to connect to the frida server: need Gadget to attach on jailed iOS; its default location is: /Users/foobar/.cache/frida/gadget-ios.dylib
Frida is able to instrument debuggable apps, and will inject Gadget automatically. Gadget must be present in the user’s cache directory. On macOS this is ~/.cache/frida/gadget-ios.dylib
.
Gadget can download from https://github.com/frida/frida/releases
. The file name is frida-gadget-
Failed to enumerate applications: this feature requires an iOS Developer Disk Image to be mounted
Plugin a new iOS device and got following error:
$ frida-ps -Ua
Failed to enumerate applications: this feature requires an iOS Developer Disk Image to be mounted; run Xcode briefly or use ideviceimagemounter to mount one manually
To use frida, the Developer Disk Image must be mounted. Xcode will mount it automatically as soon as it discovers the iOS USB device, but you can also do it manually by using ideviceimagemounter
.
In xcode, Click Window
-> Devices and Simulators
, check the device connection status, wait a few minutes should be good to go.
objection stuck
When use objection
to launch app, it may stuck after app launched, the following:
$ objection -g com.example.foobar explore
Using USB device `iPhone`
...
To workaround this, launch app manually, then use frida-ps
to find the process id and use objection
to attach it.
For example:
$ frida-ps -Ua 1 ↵
PID Name Identifier
----- ----------------------------- ----------------------------
19978 Foobar Example com.example.foobar # <---- Get process id for objection
10205 App Store com.apple.AppStore
...
$ objection -g 19978 explorer
$ objection -g 19978 explore
Using USB device `iPhone`
Agent injected and responds ok!
_ _ _ _
___| |_|_|___ ___| |_|_|___ ___
| . | . | | -_| _| _| | . | |
|___|___| |___|___|_| |_|___|_|_|
|___|(object)inject(ion) v1.11.0
Runtime Mobile Exploration
by: @leonjza from @sensepost
[tab] for command suggestions
com.example.foobar on (iPhone: 16.0) [usb] #
Unable to connect to the frida server: unable to attach to the specified process
Try to use objection
to attach a process and failed with Unable to connect to the frida server: unable to attach to the specified process:
$ objection -g 9002 explore 2 ↵
Using USB device `iPhone`
Unable to connect to the frida server: unable to attach to the specified process
This may due to app is not debuggable. For example, app is installed from app store or TestFlight.
To attach any process, you need patch the exist iOS application .
Before you can use any of the objection commands on an iOS application (build in release mode), the application’s IPA itself needs to be patched and code signed to load the FridaGadget.dylib
on start. To patch an IPA though, a few things need to be done in preparation, such as getting an embedded.mobileprovision
file, as well as a code signing certificate from Apple. Once you have these, objection
has a patchipa
subcommand that will help you take care of the rest.
Related pages:
- Swift Data init with heap buffer from C and free in Swift
- DispatchQueue QoS Order Explained Best Practices & Common Pitfalls to Avoid
- iOS RUNNINGBOARD 0xdead10cc crash troubleshooting
- iOS objc_retain crash troubleshooting
- Fastlane Best Practices for iOS and Troubleshooting Tips
- SwiftUI: How to Tap to Select All Text in a TextField on iOS
- Direct Download Link for All Xcode Releases/Beta, Compilers and SDK Information
- Guide: Jailbreaking Apple TV 4K
- iOS Security: App security in iOS and iPadOS - code signing, entitlement and sandbox etc.
- Tips on Use Swift Package Manager (SPM) with Continuous Integration / Delivery (CI/CD)
- Jailbreak iPhone 8 iOS 16.2 with palera1n and use frida dump to decrypt ipa
- Troubleshooting: iOS auto layout warning about UIView-Encapsulated-Layout-Height
- Troubleshooting: loading carthage framework error: dyld: Library not loaded: @rpath/...
- What's new in SwiftUI iOS 16.4 beta 2
References
OmniLock - Block / Hide App on iOS
Block distractive apps from appearing on the Home Screen and App Library, enhance your focus and reduce screen time.
DNS Firewall for iOS and Mac OS
Encrypted your DNS to protect your privacy and firewall to block phishing, malicious domains, block ads in all browsers and apps