After reverse engineering the myTF1 tvOS app to block ads, I wanted to do the same for the iPhone app. The process was similar, but I hit some issues along the way.

Setup and crash

The first step as always was to get the decrypted app binary. For once I used ipadecrypt which was made public a few weeks ago and I wanted to give it a try. It worked well and I got the decrypted binary without any issue.

I adapted my original Theos tweak to work on the iPhone app, compiled it and repacked the patched IPA. Since my current iPhone runs iOS 18 (for which no public jailbreak is currently available), I used LiveContainer to test the IPA on my device. However, the app kept crashing on launch: crash.jpeg

Root cause: subscription registration entitlement

According to the logs “-[VSSubscriptionRegistrationCenter setCurrentSubscription:] method requires an entitlement.”. This was unexpected since the tweak only hooks methods related to ad loading and should not interfere with any subscription-related functionality. When this fails due to the missing entitlement in LiveContainer, the OS terminates the app.

-[VSSubscriptionRegistrationCenter setCurrentSubscription:] belongs to the VideoSubscriberAccount Framework framework, it is part of Apple’s TV provider / subscription authentication system used by streaming apps and TV apps on iOS/tvOS. Apple protects this API with private entitlements to prevent unauthorized access, and since the app is running in a LiveContainer, a sandboxed environment, it does not have the necessary entitlements to call this method, which leads to the crash.

Using ripgrep, I found that -[VSSubscriptionRegistrationCenter setCurrentSubscription:] is only called in the main binary here:

void __fastcall sub_10001F664(char a1)
{
  __int64 dateType; // x21
  __int64 dateVwt; // x23
  char *dateSlot; // x20
  id subscription; // x19
  id dateObj; // x22
  id arrayObj; // x22
  id regCenter; // x20
  __int64 dateStorage; // [xsp+0h] [xbp-40h]

  dateType = type metadata accessor for Date(0);
  dateVwt = *(_QWORD *)(dateType - 8);
  dateSlot = (char *)&dateStorage - ((*(_QWORD *)(dateVwt + 64) + 15LL) & 0xFFFFFFFFFFFFFFF0LL);
  if ( a1 )
  {
    if ( a1 == 1 )
    {
      subscription = objc_msgSend(objc_allocWithZone((Class)&OBJC_CLASS___VSSubscription), "init");
      static Date.distantFuture.getter();
      dateObj = Date._bridgeToObjectiveC()();
      (*(void (__fastcall **)(char *, __int64))(dateVwt + 8))(dateSlot, dateType);
      objc_msgSend(subscription, "setExpirationDate:", dateObj);
      objc_release(dateObj);
      objc_msgSend(subscription, "setAccessLevel:", 1);
    }
    else
    {
      subscription = objc_msgSend(objc_allocWithZone((Class)&OBJC_CLASS___VSSubscription), "init");
      static Date.distantFuture.getter();
      dateObj = Date._bridgeToObjectiveC()();
      (*(void (__fastcall **)(char *, __int64))(dateVwt + 8))(dateSlot, dateType);
      objc_msgSend(subscription, "setExpirationDate:", dateObj);
      objc_release(dateObj);
      objc_msgSend(subscription, "setAccessLevel:", 2);
      swift_initStaticObject(sub_1000107E0(&unk_100856C70, &unk_100637D90), &unk_100856860);
      arrayObj = Array._bridgeToObjectiveC()();
      objc_msgSend(subscription, "setTierIdentifiers:", arrayObj);
      objc_release(arrayObj);
    }
    regCenter = objc_retainAutoreleasedReturnValue(
                  objc_msgSend(
                    (id)objc_opt_self(&OBJC_CLASS___VSSubscriptionRegistrationCenter),
                    "defaultSubscriptionRegistrationCenter"));
    objc_msgSend(regCenter, "setCurrentSubscription:", subscription);
    objc_release(subscription);
    objc_release(regCenter);
  }
  else
  {
    id center;
    center = objc_retainAutoreleasedReturnValue(
              objc_msgSend(
                (id)objc_opt_self(&OBJC_CLASS___VSSubscriptionRegistrationCenter),
                "defaultSubscriptionRegistrationCenter"));
    objc_msgSend(center, "setCurrentSubscription:", 0);
    objc_release(center);
  }
}

I also saw on the official documentation:

Don’t set a subscription if the user only has access to free content. Source: Apple Developer Documentation So the method shouldn’t be called since I don’t have any subscription.

Fix: short-circuit the call

Anyway, since I’m using Theos, I can easily hook this method and prevent the crash by simply returning early:

%hook VSSubscriptionRegistrationCenter

- (void)setCurrentSubscription:(id)subscription
{
    NSLog(@"Blocked VSSubscriptionRegistrationCenter");
    NSLog(@"Subscription: %@", subscription);
    return;
}

%end

Result

After recompiling the tweak and repackaging the IPA, the app launched successfully without crashing! The hook fired as expected: console.png And here is the subscription object that was passed to the method:

<<VSSubscription: 0x12b1c6300> subscriberIdentifierHash = , billingIdentifier = , expirationDate = 4001-01-01 00:00:00 +0000, accessLevel = 1, tierIdentifiers = (
), creationDate = 2026-05-10 22:40:39 +0000, modificationDate = 2026-05-10 22:40:39 +0000, derivedSubscriptionInfo = , providedSubscriptionInfo = , source = <<VSSubscriptionSource: 0x1296ef6c0> kind = 0, identifier = com.tf1.applitf1>, modifierIdentifier = , modifierType = >

This configures the subscription as an active, non-expiring subscription with Access Level 1 and no specific tier identifiers.

And I can now confirm that the ads are indeed blocked and the app is working fine without any issues, the tweak is now available on my GitHub :p