How to Remove Ads from a Free Piano App: A Step-by-Step Guide

Found a free piano app in the app store and removed Google ads through reverse engineering.

Difficulty

★★☆☆☆

Tools and Environment

  • Jailbroken iOS 14.4
  • frida-ios-dump
  • frida
  • frida-trace
  • IDA 7.7
  • reveal

Analysis Approach

There are many methods to remove ads within an app.

  • Use IDA to analyze the maco file, locate the ad code, and remove it directly.
  • Use frida to hook network requests and intercept ad requests.
  • Use reveal to analyze the app’s UI and hide the ad view.
  • Intercept and redirect domain resolution requests.

Analyzing Ad Code with IDA

After decrypting the app, load it into IDA and search for ad-related keywords like “adver” in the function list.

free piano app

From the method name, we can infer that createAdvertisementBannerView is the method for creating the ad view.

free piano app

Check cross-references to analyze the upper-level calling functions.

The upper-level function contains many operations including class initialization, method calls, object property access, and some memory management operations (like objc_retain and objc_release).

  1. Create an AdvertisementIdentifiers object and initialize it with multiple parameters.
  2. Create an AdvertisementBannerViewConfiguration object.
  3. Get the app’s delegate, possibly to obtain the root view controller.
  4. Use the AdvertisementManager class to create an ad banner view (BannerView).
  5. Based on certain conditions, get the main screen size of the app and set it to the ad banner view (BannerView).

Replacing Ad Loading Function with frida

 var baseAddr = Module.findBaseAddress('ThePiano');
console.log("The Piano base address: " + baseAddr);

var targetFunctionAddr = baseAddr.add(0x6E854); 
console.log("Target function address: " + targetFunctionAddr);

const targetFunction = new NativeFunction(targetFunctionAddr, 'void', []);

Interceptor.replace(targetFunctionAddr, new NativeCallback(function () {
    console.log("Skipping ad loading at offset 0x6E854");
}, 'void', []));

const targetFunctionAddrRootVC = baseAddr.add(0x864A8); 

Interceptor.attach(targetFunctionAddrRootVC, {
    onEnter: function (args) {
        console.log('Entering the calling function of the ad loading function');
    },
    onLeave: function (retval) {
        console.log('Exiting the calling function of the ad loading function');
    }
});

The effect is as follows:

  frida -U -f com.impalastudios.pianofree -l piano.js

Analyzing Ad UI with reveal

Enable debugging in the reveal settings.

Then connect the jailbroken iPad to the Mac.

Hide the ad view in reveal.

Intercepting Domain Resolution Requests with frida-trace

 frida-trace -U -f com.impalastudios.pianofree -i "getaddrinfo"

Intercept the domain resolution within the app and redirect it to 127.0.0.1. Modify the frida-trace script as follows:

 {
  onEnter(log, args, state) {
    var hostname = Memory.readUtf8String(args[0]);
    // log('Intercepted call to getaddrinfo for ' + hostname);
    // Redirect if the domain contains 'google'
    if (hostname.includes('google')) {
        log('Redirecting request for ' + hostname + ' to 127.0.0.1');
        args[0] = Memory.allocUtf8String('127.0.0.1');
    }
  },
  onLeave(log, retval, state) {
  }
}