Hacking Messenger’s iOS App: Custom Chat Bubble Colors

Update: As of May 3, 2018, this sadly doesn’t seem to work anymore. My web chats returned to the blue color and the custom changes only apply locally. I guess color changes are validated on the backend now. Damn you Zuckerberg!
Still a good read if you want to learn more about reverse engineering!

Near the end of 2015, Facebook added a feature to Messenger that allowed us to change our chat bubble’s colors. The cool thing about this is that not only your friend can also see the color you chose, but Facebook’s web chat will also change to that color.

When I discovered this feature, I rushed to see if I could make my bubbles black. To my misfortune, the available colors are limited, and none of them is black.

None of these colors pleases me.

I accepted my fate and moved on with my life. But after reading Flavio’s What I learned hacking Facebook's Messenger's soccer game this week, I wondered if I could follow his approach in order to trick Messenger’s iOS app into making my chat’s colors black.

Like Flavio, HTTP sniffing was my first thought. The procedure should be quick and clean — intercept whatever REST API call Messenger triggers whenever I select a color, change that color to black and hope that it works. But like him, I discovered the hard way that Facebook’s iOS apps have a very strong protection against this, to the point even bypassing iOS’s Certificate Pinning has no effect. And the people who managed to break such protection are not willing to share how they did it.

This leaves me with the brute force option: attack the app’s code.


Attacking Messenger with Cycript

The procedure:
- Open Messenger and leave it at the color selection screen
- From my computer, inject the app
- Crawl though the app’s code and find whatever object corresponds to the color buttons
- Override these objects to always have a black color
- Click them on the app and hope that it works

The plan has a pretty wild assumption: that Messenger actually uses RGB values to set these colors. As far as I know, these colors could be predefined on Facebook’s servers, meaning that I could never even dream of changing it. However, it doesn’t hurt to try. Let’s see what we can find.

If you plan on following this, keep in mind that this requires a jailbroken device and a decent amount of iOS programming / Objective-C knowledge. If you got the latter covered, I recommend reading this tutorial and all the other parts in order to know more about the tools mentioned here.

After opening Messenger and its color selection screen, I’ll connect to my iPad and boot Cycript.

$ ssh root@192.168.1.103
$ cycript -p Messenger

Finding the color button object

We’re in. Now we need to do find the class that holds these color buttons. Taking a look at Messenger, any iOS developer will be able to tell that the color selection screen is an UIActionSheet. Let’s focus on finding that first. The first thing we can do is get the app’s rootViewController:

cy# var root = [UIApplication sharedApplication].keyWindow.rootViewController

By putting a * before an instanced object, we are able to print all of its properties. Since the Action Sheet is opened in my device, we should be able to locate it somewhere inside “root”.

There’s a great amount of things here, but take a look at the childViewController property: the action sheet is the second object on that array.

cy# var actionSheet = root.childViewControllers[1]

That was easy. If we are lucky, this action sheet should contain an array with these color buttons. Let’s do the same thing and print its properties.

cy# *actionSheet

After reading this ugly pile of properties, I realize that I spoke too soon. No sign of colors here.

What can we extract out of this, then? There are a few custom classes here: FBKeyboardObserver, FBKeyboardResettingTrackerNotice and MNThreadCustomizationPickerViewController. By using our programming experience and hoping that the Messenger’s devs are experts in clean coding practices, we can simply deduce that they would never put the color buttons inside something called “keyboard observer” or “resetting tracker notice”, while “Customization picker view controller” sounds like a very good candidate. Whatever that is, it’s what we should be looking at.

This controller is inside a property called “contentViewController”, but if you have good eyes, you’ll notice a second one inside “childViewControllers”. Since they have the same memory address (0x1144bc860), we know that they are the same object, so it doesn’t matter which one we choose.

Since we are getting closer, it's time to start printing the classes' methods in order to find who's responsible for filling those colors. I'm going to use a snippet from Cycript tricks to print MNThreadCustomizationPickerViewController.

cy# function printMethods(className) {
var count = new new Type(“I”);
var methods = class_copyMethodList(objc_getClass(className), count);
var methodsArray = [];
for(var i = 0; i < *count; i++) {
var method = methods[i];
methodsArray.push({selector:method_getName(method)});
}
free(methods);
return methodsArray;
}

While none of this is what we are looking for, “initWithPickerView:title:subtitle:dismissButtonTitle:dismissButtonColor:” proves that we are on the right place. That's because the selection screen has a title, subtitle, and a cancel button, just like this method's signature.

cy# var pickerViewController = actionSheet.childViewControllers[0]
cy# *pickerViewController

Unfortunately this controller is not storing our colors, but as expected from iOS Programming, this PickerViewController has an internal pickerView (in this case, a FBPickerView). Let's see the picker's properties.

cy# var pickerView = pickerViewController.pickerView
cy# *pickerView

(If you’re unable to get a reference to the pickerView here, you can try “var pickerView = new Instance(“the picker's memory address”)”

That "indexLookupTable" property has 15 elements, which match the 15 colors Messenger allows we to choose. Messenger is using a Lookup Table in order to map plain values into RGB colors: much lighter than storing the colors themselves. We could in theory change these values to our own, but since I don't know how Messenger's devs came up with these values, I don't know what value corresponds to a black color. The safer way then is to intercept the method where the values are mapped into colors. Here's FBPickerView's methods:

By analyzing this, I'm inclined to believe that didFetchItem:atIndex: is called after the table is converted to colors, and computeRotatedIndexes is who's doing this conversion. Unfortunately, these selectors don't tell us what are the arguments/return types for these methods (are they numbers? strings? colors?), and neither does a class dumper like class-dump. You could stop here and crawl the dumped classes in order to find the color object class (spoiler: it's called FBPickerItem, so you see how easy it would be to find it considering that the class we just inspected is called FBPickerView), but for tutorial purposes, I'll travel inside the collection view that's inside the pickerView to find it.

The property inspection from the collectionView was massive, so I cropped the only interesting part from it:

It has 15 cells (our 15 colors), of the type FBPickerItemCollectionViewCell. I'll get the first one: (which should be the blue color button)

cy# var blueCell = new Instance(“0x1144b54b0”)

The class has methods for setting a "button", which judging by the properties, is a "FBPickerViewButton". Let's check the "button" property.

cy# var blueButton = blueCell.button

Now, the button's class has methods for setting an "item", which is an “FBPickerItem” according to its properties. By printing FBPickerItem's methods, we find:

Bingo. FBPickerItem is where our colors are stored after being converted from that index table.


Method Swizzling

Now that I know how the app displays the color table, I need to intercept its creation.

I'm going to use Method Swizzling to override the FBPickerItem’s init method. Luckily, we can install Cydia Substract on our jailbroken device and use its helpers to build a snippet that makes it easier for us to write a method that does this.

@import com.saurik.substrate.MS
var _setColor_pointer = {};
MS.hookMessage(FBPickerItem, @selector(initWithColor:accessibilityTitle:accessibilityHint:isSelected:isSelectable:), function(arg0) {
return _setColor_pointer->call(this,[UIColor blackColor],”a”,”b”,false,true);
}, _setColor_pointer);

This means: “Override the init method to call itself, but with these arguments instead”.

Like mentioned before, I don’t know what the arguments for this method are. but due to the argument names, Its a safe bet to say that they are the following: UIColor, NSString, NSString, Bool and Bool. Hence, [UIColor blackColor],”a”,”b”,false,true.

Let’s close the selection screen and open it again. The result is:

The ActionSheet's subtitle is missing because I'm chatting with myself.

It worked, but will it actually change my chat’s colors? Is my assumption of Facebook using plain RGB colors true?

It is!

And to make it better, it changed my web chat as well. I chose black, but you can change the bubbles to whatever color you want. If you want to have some fun, make them white and watch a friend become confused in a sea of invisible text.