All posts by alanyip

My name is Alan Yip, a programmer and security researcher from Hong Kong. I create tweaks for iOS and do security research.

Tint transparent images on iOS

Recently, I encountered a problem that I need to change the color of non-transparent pixels in an image with a transparent background without affecting much the original quality (at least not draw with hard edges). Therefore I did some searches on Google and found the following solution (credit at the end). The following solution does not care about the luminosity of the image.

CGImageRef image;

CGContextSetBlendMode(context, kCGBlendModeNormal);
[tintColor setFill];
CGContextFillRect(context, rect);

CGContextSetBlendMode(context, kCGBlendModeDestinationIn);
CGContextDrawImage(context, rect, image);

or exchange the steps,

CGImageRef image;

CGContextSetBlendMode(context, kCGBlendModeNormal);
CGContextDrawImage(context, rect, image);

CGContextSetBlendMode(context, kCGBlendModeSourceIn);
[tintColor setFill];
CGContextFillRect(context, rect);

For more details or other solutions, please refer to the link in credit below.

[Facebook] Reveal information from any post, photo, page or group

This vulnerability enables anyone to get some basic information (mainly just name, and uploader/author’s ID for photos and posts with a specific condition) from any post, photo, page or group regardless of its privacy setting, or type (published state for pages / visibility for groups).

All these information results from the dialog title and full URL for a given Facebook object (e.g. a post or photo) in an AJAX script at Originally, the script is for page admin to get the full URL for an app installed in a page. The script takes two essential parameters, page_id and app_id, which apparently indicate the IDs for a page and an app. The HTML content in the response is a dialog with a title (“Link to the A for B”, with A for the app name and B for the page name) and a full absolute URL for the app in the page.

Perhaps, if you are a hacker like me, you would immediately try an ID of an unpublished page and app in sandbox mode. Luckily, it works. It reveals nothing more than the name and the page address of the page and app regardless of their type and visibility. In the first place, I reported this to Facebook and waited for their reply.

At that time, I had underestimated the vulnerability; afterward I kept trying plugging in different ID to the URL. Surprisingly, the page_id parameter name is totally misleading because it accepts other Facebook objects as well. Here comes to the conclusion of the possibilities of page_id value and the result.

1. Page ID
Reveal the name of a page regardless of its published state and whether you are an admin of the page or not.

2. Group ID
Reveal the name of a group regardless of its visibility state (secret group) and whether you are in the group or not.

3. Photo ID (fbid in photo URL)
Reveal the uploader’s ID of a photo regardless of its privacy setting as long as it is not deleted.

Practical usage: When you get a Facebook static image URL (the one starting with “fbcdn-sphotos” and ending with “.jpg“), you could extract the fbid from it. The full URL of a photo reveals the album ID containing its uploader’s ID.

Uploader’s ID no longer presents in static photo URL since mid 2012.

4. Post ID (the post must share something, whatever URL or other’s post)
Reveal the author’s ID of a post that shares something regardless of its privacy setting.
For unknown reasons, the dialog title and content both contain the author’s ID.

The vulnerability is now fixed by only allowing IDs of published pages and apps not in sandbox.

21 Dec 2013 – Reported to Facebook
23 Dec 2013 – Acknowledgement of report
6 Jan 2014 – Sent POC video to Facebook
11 Jan 2014 – Vulnerability fixed

[Facebook] CSRF to accept page admin invitation

Facebook responded that this vulnerability was reported before by someone else and it is now fixed.

This is a CSRF vulnerability that enables anyone to generate a URL to accept an invitation of being a page admin (or any other admin role). Undoubtedly, this is not really a big deal but still a problem.

For example, I am an admin of a page and send an invitation to a user with known email address. An email will then be sent to the recipient’s mailbox to tell him or her to accept/decline the invitation. However, the sender (me in this case) could generate a URL that makes the recipient accept the invitation without any confirmation or security code required. Normally, fb_dstg parameter is required to prevent such kind of CSRF attacks but this is not applicable in this case. The URL looks like:

As long as the recipient triggers the URL above (like through an image or script tag, or direct click), the recipient accepts the invitation automatically.

1 Dec 2013 – Reported to Facebook
3 Dec 2013 – Notified of duplicated report

[Facebook] Get sharing URL from any post

This vulnerability allows anyone to get the sharing URL from any post which can even be the one you cannot access due to its privacy setting. But it only reveals the sharing URL, not the content or author of the post.

The hack is pretty simple. It takes place in the share script on the mobile platform.

The script can take a parameter called “sid” indicating ID of the sharing object. The sid value will be used in POST content when sharing the post. It can be set to ID of any post that shares a URL (not applicable to sharing a post or other Facebook items with this vulnerability) and the post, surprisingly, could still be shared successfully afterward. Eventually, the sharing URL in the post with a known post ID is then revealed.

5 Aug 2013 – Reported to Facebook
12 Aug 2013 – Acknowledgement of report
1 Nov 2013 – Vulnerability fixed

Read Alarms in iOS

Notice: This method is undocumented and only for tweaks or apps that will not be submitted to AppStore.

This article will introduce a way to read (or add, but not included here) alarms in iOS. There is a private framework (MobileTimer.framework) originally provided for iOS stock app, Clock to manipulate clocks and system alarms.

Obviously, we will only use two classes when dealing with alarms, AlarmManager and Alarm. First, we have to get the singleton instance of AlarmManager.

AlarmManager *manager = [AlarmManager sharedManager];

Before accessing the alarms, it is required to load alarms first.

[manager loadAlarms];

Then, you can access the array containing all available alarms (Alarm) in Clock app.

NSArray *alarms = [manager alarms];

However, there is a problem in the framework when loading the alarms. For example, the  code is running in SpringBoard, while at the same time the alarms are being modified in stock Clock app. Even if you load alarms in SpringBoard again, the alarm data returned are still outdated. To fix this, hook a class method in AlarmManager to force synchronizing the preference values before the original method read from the preference.

%hook AlarmManager

+ (id)copyReadAlarmsFromPreferences {
	return %orig;


Create note in official Notes app

20 May: Updated with iCloud synchronization support

Notice: This method is undocumented and only for tweaks or apps that will not be submitted to AppStore.

Lately, as I was developing Tap to Note, I had to find a way to add notes in official Notes app without directly updating notes database file (SQLite).

Eventually, I used the private framework (Notes.framework) to perform update. Here only covers the way to insert a new note.

The Notes framework uses Core Data to perform all changes to the note database file. For those who don’t know what Core Data is, check out the documentation here and example here.

The first step is to initialize a new note context (NoteContext) ourselves (not sure if this is the most correct way).

NoteContext *noteContext = [[NSClassFromString(@"NoteContext") alloc] init];

UPDATE: To support iCloud synchronization, add the following line.

[noteContext enableChangeLogging:YES];

Then get the NSManagedObjectContext (for Core Data) from the context instance.

NSManagedObjectContext *context = [noteContext managedObjectContext];

And the default note store object (NoteStoreObject) as well.

NoteStoreObject *store = [noteContext defaultStoreForNewNote];

Actually you may want to insert notes to other stores (e.g. local or iCloud). You can get the desired note store using other similar methods in NoteContext (e.g. allStoreslocalStore).

After that, we are going to create a note object as follows.

NoteObject *note = [NSClassFromString(@"NSEntityDescription") insertNewObjectForEntityForName:@"Note" inManagedObjectContext:context];
NoteBodyObject *body = [NSClassFromString(@"NSEntityDescription") insertNewObjectForEntityForName:@"NoteBody" inManagedObjectContext:context];

// set body parameters
body.content = @"note content";
body.owner = note; // reference to NoteObject

// set note parameters = store; // reference to NoteStoreObject
note.integerId = [noteContext nextIndex];
note.title = @"title here";
note.summary = @"summary here";
note.body = body; // reference to NoteBodyObject
note.creationDate = [NSData date];
note.modificationDate = [NSData date];

Note content is the full content with HTML code as the official app uses HTML tags to wrap lines (i.e. <div>, <br />). Consecutive spaces need to be replaced with “&nbsp;”. Everything will just be shown in a web view.

Title and summary are normally extracted from the first and second line of content respectively while summary can be nil or empty if there’s only one line of content.

The last step is, of course, to save the note and release the initialized note context instance.

NSError *error;

BOOL success = [noteContext saveOutsideApp:&error];
[noteContext release];

You could retrieve the error message from the error variable and get the insertion result (success or not) from the return value of the method saveOutsideApp:.

iOS Device Volume

By using the private framework, Celestial, we can simply get and set the volume or monitor its change. The class used is AVSystemController in Celestial framework.

We can get or set the audio of either the active audio category or a specific audio category.

1) Active audio category:

float volume;
[[AVSystemController sharedAVSystemController] getActiveCategoryVolume:&volume andName:nil];
[[AVSystemController sharedAVSystemController] setActiveCategoryVolumeTo:1.0];

(Optional) Also get the name of the active audio category:

float volume;
NSString *categoryName;
[[AVSystemController sharedAVSystemController] getActiveCategoryVolume:&volume andName:&categoryName];

2) Specific audio category:

float volume;
[[AVSystemController sharedAVSystemController] getVolume:&volume forCategory:@"CATEGORY_HERE"];
[[AVSystemController sharedAVSystemController] setVolumeTo:1.0 forCategory:@"CATEGORY_HERE"];

Furthermore, we can also monitor the change of device volume with the following code.

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(volumeChanged:) name:@"AVSystemController_SystemVolumeDidChangeNotification" object:nil];

- (void)volumeChanged:(NSNotification *)notification {
	float volume = [[[notification userInfo] objectForKey:@"AVSystemController_AudioVolumeNotificationParameter"] floatValue];

Reference: List of available audio categories

Hide volume HUD view

Notice: This method is undocumented and only for tweaks or apps that will not be submitted to AppStore.

How to use:

[[UIApplication sharedApplication] setSystemVolumeHUDEnabled:NO forAudioCategory:@"CATEGORY_HERE"]; // for a specific audio category


[[UIApplication sharedApplication] setSystemVolumeHUDEnabled:NO];

Relevant header (private methods):

@interface UIApplication (Private)

- (void)setSystemVolumeHUDEnabled:(BOOL)enabled forAudioCategory:(NSString *)category;
- (void)setSystemVolumeHUDEnabled:(BOOL)enabled;


Reference: List of available audio categories