tvOS AirDrop Adventures

By: Kevin Bradley @nitoTV

Time for another trip down memory lane, into forbidden territory on tvOS. While working on Chimera/Electra jailbreaks for tvOS, I was reminded of something I stumbled across over a year ago. If you hold down the Play/Pause & Volume down buttons on the Siri Remote for different intervals you will either get a stackshot or analytics to run on the device.

Siri Remote

These actions appear as follows:

Once analytics is done running it will present you with an alert asking if you want to export the results, as pictured below. export If you choose ‘Export’ you will get an instance of SFAirDropSharingViewControllerTV showing up and asking you where you would like to AirDrop the results. AirDrop

This piqued my curiosity; did AirDrop work in both directions? AirDrop on tvOS had limited utility for most end users due to the fact that it would only share analytics and stackshot logs from an AppleTV to another AirDrop capable device. If it could receive files, it would open a whole new world of possibilities, including receiving a mobileconfig file to enable VPN access. The first obstacle was getting the device to change its AirDrop discoverable mode from SDAirDropDiscoverableModeOff to SDAirDropDiscoverableModeEveryone. This can be achieved by creating an instance of SFAirDropDiscoveryController and calling setDiscoverableMode: to the appropriate value. As long as the proper entitlements are in place from the calling binary, it will work like a charm.

Once AirDrop was enabled, it was time to attempt to AirDrop a file and see what happens. At the time (tvOS version 11/12), sharingd would throw an exception and ultimately crash. Thankfully, the logs from sharingd’s exception pointed me in the right direction to begin reversing. If reverse engineering is new to you, using error strings in the console is one of the best ways to start searching inside a binary in your RE tool of choice (IDA/Hopper/Ghidra, etc.). Upon doing a string search for the error in question, it placed me squarely inside the class SDAirDropTransferManager. This process is exhaustively documented in my open source project Breezy. I went down that AirDrop rabbit hole for many weeks to figure it all out, and the result is 1:1 feature parity on tvOS to AirDrop on iOS and macOS.

AirDrop support continues to pay dividends on a daily basis. As chronicled previously, AirDrop is a major key to the puzzle of getting VPN working on tvOS. It also provides an additional challenge regarding UTI types, that I have yet to fully unravel. Using a handy tool from Jon Levin called lsdtrip, which I have augmented and updated with some bug fixes and new features, you can get some useful details from the file if you run the ‘dump’ command. A little excerpt follows:

type id:           (0x5a0)
bundle:                     MobileCoreTypes (0x4)
localizedDescription:       "ar" = "تكوين الهاتف الجوال", "ca" = "configuració mòbil", "cs" = "mobilní konfigurace", "da" = "Mobilkonfiguration", "de" = "Mobile Konfiguration", "el" = "φορητή ρύθμιση παραμέτρων", "en" = "mobile configuration", "en_AU" = "mobile configuration", "en_GB" = "mobile configuration", "es" = "configuración móvil", "es_419" = "configuración celular", "fi" = "liikkuva määritys", "fr" = "configuration mobile", "fr_CA" = "configuration mobile", "he" = "תצורה ניידת", "hi" = "मोबाइल कॉन्फ़िगरेशन", "hr" = "mobilna konfiguracija", "hu" = "mobil konfiguráció", "id" = "konfigurasi mobile", "it" = "configurazione cellulare", "ja" = "モバイル設定", "ko" = "모바일 구성", "LSDefaultLocalizedValue" = "mobile configuration", "ms" = "konfigurasi mudah alih", "nl" = "mobiele configuratie", "no" = "mobilkonfigurasjon", "pl" = "konfiguracja telefonu", "pt_BR" = "configuração móvel", "pt_PT" = "configuração móvel", "ro" = "configurație mobilă", "ru" = "конфигурация мобильного устройства", "sk" = "mobilná konfigurácia", "sv" = "mobilkonfiguration", "th" = "การกำหนดค่าอุปกรณ์เคลื่อนที่", "tr" = "mobil konfigürasyon", "uk" = "мобільна конфігурація", "vi" = "cấu hình di động", "zh_CN" = "移动电话配置", "zh_HK" = "手提電話設定", "zh_TW" = "行動電話設定"
flags:                      active  apple-internal  exported  core  trusted (0000000000000075)
iconFiles:                  profile_20x20.png, profile_20x20@2x.png, profile_145x145.png, profile_145x145@2x.png
conforms to:                public.xml
tags:                       .mobileconfig, .mobile, application/x-apple-aspen-conf

What’s fascinating about UTI types defined by the MobileCoreTypes bundle (located at /System/Library/CoreServices/MobileCoreTypes.bundle/) is the difficulty level associated with having your application advertise that it supports opening said files. Some changes were necessary to add to Breezy directly to get these files to open in nitoTV, even though I added the proper values to the Info.plist file to advertise said support. I had a similar issue when adding support for IPA files to ReProvisionTV for the UTI type ‘’. I had assumed it was advertised in the same place, (MobileCoreTypes) but upon closer inspection, I am mistaken. Long story short, a bit of a kludgy exception was added to force IPA files to open in ReProvisionTV in case LaunchServices didn’t enumerate the supported applications properly. The same kind of exception is added to the latest version of Breezy to force ‘’ to open in nitoTV. Thank goodness I maintain and control Breezy’s release!

VPN Details

Rather than spilling the proverbial “beans” before completing my research, I took care to properly test to ensure that this was the most stable and correct route to take for enabling this capability on tvOS. The same methodology used to get UIWebView working on tvOS can be employed to get VPN support working. The NetworkExtension framework on tvOS is identical to its iOS counterpart. The only difference is its usage is verboten via Availability macros:

@interface NEVPNManager : NSObject

API_UNAVAILABLE(tvos) is littered throughout the NetworkExtension framework, which is conveniently left out of the SDK packages for tvOS. However, it still exists and can be used if all of those macros are removed. I don’t link to the framework either. I just load it at runtime and employ creative use of objc_getClass("") to get access to the VPN classes needed.

After Guardian Pro and related projects are released with this functionality, be on the lookout for the related vpnd project to be open-sourced for research purposes and anyone interested in taking a look underneath the hood. The tvOS discovery expedited our research into sharing VPN credentials across devices. There is a growing desire for users to protect as many devices as possible with one product, and we are doing our very best to deliver our experience to multiple devices to meet that end goal.