Analysis of the Facebook.app for iOS

Posted: October 18th, 2016 | Author: | Filed under: Analysis, Debugging, iOS, Programming | Tags: , , , , | No Comments »

Did you ever wonder why the Facebook.app forĀ iOS is such a big download? This post tries to give some answers. The version 66.0 (released on 7 October 2016) was analyzed on an iPad Air 2 (64-bit).

Here is what you see when downloading Facebook on an iPad Air 2:

Download Info

App content

A scan of the content of the Facebook app using GrandPerspective gives already a good overview:


GrandPerspective

As you can see more than 100 MB are taken by the binary itself. The images (Assets.car), the localizations and other resources are only responsible for a small part of the size.

One note: The Facebook app seems to use the open source JSC framework. Unless there is a technical reason to use it, Facebook could remove this framework and use instead the JavaScriptCore framework built in iOS. JavaScriptCore is available on iOS 7 and later and Facebook targets iOS 8 and later.

 

Slicing

One important point to note is that the Facebook binary I downloaded only contains 64-bit code. There is no 32-bit code. Facebook uses the Slicing mechanism which is described by Apple here:

Slicing is the process of creating and delivering variants of the app bundle for different target devices. A variant contains only the executable architecture and resources that are needed for the target device.

 

Entropy

Let’s look at the Facebook binary. The entropy graph generated by Hopper gives a good idea of the content:


Entropy

The red part (with biggest entropy) is actual code while the green and orange parts are other sections (strings, constants, …). Just by looking at the entropy graph, we know that around 50% of the binary is code.

 

Sections

By looking at the sections of the binary we indeed see that the (__TEXT, __text) section is more than 50 MB. Here is the list of sections sorted by size:

(__TEXT, __text): 56.60 MB
(__DATA, __objc_const): 16.57 MB
(__RODATA, __cstring): 5.02 MB
(__RODATA, __objc_methname): 4.55 MB
(__DATA, __const): 4.46 MB
(__RODATA, __gcc_except_tab): 3.87 MB
(__RODATA, __objc_methtype): 2.66 MB
(__DATA, __objc_data): 2.41 MB
(__DATA, __cfstring): 2.20 MB
(__DATA, __data): 1.66 MB
(__TEXT, __unwind_info): 1.58 MB
(__RODATA, __objc_classname): 1.42 MB
(__RODATA, __const): 1.40 MB
(__DATA, __bss): 0.89 MB
(__DATA, __objc_selrefs): 0.80 MB
(__DATA, __common): 0.54 MB
(__DATA, __objc_ivar): 0.38 MB
(__DATA, __objc_classlist): 0.24 MB
(__DATA, __objc_classrefs): 0.18 MB
(__DATA, __objc_superrefs): 0.13 MB
(__TEXT, __const): 0.07 MB
(__DATA, __objc_protolist): 0.06 MB
(__TEXT, __stubs): 0.02 MB
(__TEXT, __stub_helper): 0.02 MB
(__DATA, __la_symbol_ptr): 0.01 MB
(__DATA, __got): 0.01 MB
(__TEXT, __ustring): 0.01 MB
(__DATA, fbsessiongks): 0.01 MB
(__TEXT, __eh_frame): 0.01 MB
(__DATA, __mod_init_func): 0.00 MB
(__DATA, __objc_protorefs): 0.00 MB
(__DATA, __objc_catlist): 0.00 MB
(__DATA, __objc_nlclslist): 0.00 MB
(__DATA, FBInjectable): 0.00 MB
(__DATA, __mod_term_func): 0.00 MB
(__DATA, __objc_imageinfo): 0.00 MB

 

__RODATA segment

If you read carefully the list of sections, you did notice that the Facebook.app has a __RODATA segment containing sections generally found inside the __TEXT segment. The reason for this oddity is to work around an App Review limitation. As you can read on the App Review page:

Each Mach-O executable file (for example, app_name.app/app_name) must not exceed these limits:

  • For apps whose MinimumOSVersion is less than 7.0: maximum of 80 MB for the total of all __TEXT sections in the binary.
  • For apps whose MinimumOSVersion is 7.x through 8.x: maximum of 60 MB per slice for the __TEXT section of each architecture slice in the binary.
  • For apps whose MinimumOSVersion is 9.0 or greater: maximum of 400 MB for the size of the Mach-O binary file.

 

Facebook targets iOS 8.0 and later and as such its __TEXT segment should not exceed 60 MB. But if you sum up the __TEXT and __RODATA segments, you end up with more than 77 MB:

Total size of the __TEXT sections: 58.3 MB
Total size of the __RODATA sections: 18.9 MB

Facebook avoids this limitation by moving some if the __TEXT sections into the read only __RODATA segment. Implementing this trick is really simple: you just need to add a linker flag to rename the chosen sections. And it appears you need absolutely nothing at runtime: the renamed sections will be found automatically. This linker flag is described in the ld man page:

-rename_section orgSegment orgSection newSegment newSection
Renames section orgSegment/orgSection to newSegment/newSection.

You could use it to rename the (__TEXT, __cstring) section to (__RODATA, __cstring) by simply adding this line into the Other Linker Flags (OTHER_LDFLAGS):

-Wl,-rename_section,__TEXT,__cstring,__RODATA,__cstring

You can download a sample macOS Xcode project here. If you load the original binary (without the section rename) using MachOView, you will see the (__TEXT, __cstring) section:


Standard Section

After adding the linker flag and recompiling, a (__RODATA, __cstring) section will be visible and the binary will run as expected:

Renamed Section

Note that this workaround is only needed as long as Facebook supports iOS 8. When targeting iOS 9 or later, the limitation is 400 MB which should be enough without splitting the __TEXT segment.

 

Third party libraries

So what does the code contain? Sadly I could not find a license/readme file containing the list of third party libraries. Also the Info view in Settings.app > Facebook > Settings > Infos is desperately empty – this seems like a Facebook.app bug.

By looking at the class names, I could build a list of third party libraries used. This list is most likely incomplete but hopefully correct:

  • AsyncDisplayKit: http://asyncdisplaykit.org
    keeps even the most complex user interfaces smooth and responsive
  • Bolts-ObjC: https://github.com/BoltsFramework/Bolts-ObjC
    Bolts is a collection of low-level libraries designed to make developing mobile apps easier
  • CocoaAsyncSocket: https://github.com/robbiehanson/CocoaAsyncSocket
    Asynchronous socket networking library for Mac and iOS
  • CocoaLumberjack: https://github.com/CocoaLumberjack/CocoaLumberjack
    A fast & simple, yet powerful & flexible logging framework for Mac and iOS
  • ComponentKit: http://componentkit.org
    ComponentKit takes a functional, declarative approach to building UI
  • EGODatabase: https://github.com/enormego/egodatabase
    EGODatabase is a thread-safe Objective-C SQLite wrapper with full support for asynchronous SQLite calls as well as built in NSOperationQueue support
  • FLAnimatedImage: https://github.com/Flipboard/FLAnimatedImage
    Performant animated GIF engine for iOS
  • FXBlurView: https://github.com/nicklockwood/FXBlurView
    UIView subclass that replicates the iOS 7 realtime background blur effect, but works on iOS 5 and above
  • Google Toolbox for Mac: https://github.com/google/google-toolbox-for-mac
    Google Toolbox for Mac
  • JSONKit: https://github.com/johnezang/JSONKit
    Objective-C JSON
  • libphonenumber: https://github.com/iziz/libPhoneNumber-iOS
    iOS port from libphonenumber
  • OAuth2Client: https://github.com/nxtbgthng/OAuth2Client
    Client library for OAuth2
  • OCMock: http://ocmock.org
    Mock objects for Objective-C
  • pop: https://github.com/facebook/pop
    An extensible iOS and OS X animation library, useful for physics-based interactions.
  • nighthawk-webrtc: https://github.com/ceaglest/nighthawk-webrtc
    Custom builds of WebRTC for iOS
  • React Native: https://facebook.github.io/react-native/
    Build Native Mobile Apps using JavaScript and React
  • ZipArchive: https://github.com/ZipArchive/ZipArchive
    ZipArchive is a simple utility class for zipping and unzipping files on iOS and Mac
  • ZipZap: https://github.com/pixelglow/ZipZap
    zip file I/O library for Mac OS X and iOS

 

Objective-C awards

The awards for the best Objective-C classes is attributed to:

NTNativeTemplateHackyWorkaroundBecauseNSMapTableIsBuggyContainer

The award for the longest Objective-C method is attributed to:

- (void)[FBComposerCompositionStateMutation matchBootstrap:
		activityAttachmentUpdatedMutation:
		activitySharingComposition:
		albumDescription:
		albumTitle:
		appProvidedHashtag:
		audienceMutation:
		audienceTouchedByUser:
		author:
		clearContent:
		containerPublishTargetForMediaCollection:
		composerCancelAnalytics:
		defaultAudienceMutation:
		destinationMutation:
		feedOnlyToggleState:
		hashtags:
		inspirationMutation:
		isImplicitLocationExplicitlyBlockedByUser:
		lifeEventDateMutation:
		lifeEventRelationshipUpdated:
		lifeEventTitleOrIconUpdated:
		linkShareMutation:
		shareOriginalPostMutation:
		removeMediaAttachmentWithAssetID:
		replaceAllExistingMediaAttachments:
		addSingleMediaAttachment:
		replaceSingleExistingMediaAttachmentIfExisting:
		mediaAttachmentCaptionMutation:
		mediaAttachmentMentionsMutation:
		mediaReorderingMutation:
		mentions:
		moviesComposition:
		moviesLoadedTaggedUsers:
		photoAutoTaggingState:
		photoTaggingAssetMutation:
		placeMutation:
		placeSuggestions:
		placeTaggingState:
		platformLoadedActionMutation:
		platformLoadedAppAttribution:
		platformLoadedImageAttachments:
		platformLoadedOGMediaAttachmentsMutation:
		platformLoadedPlaceMutation:
		platformLoadedRef:
		platformLoadedRobotext:
		platformLoadedTaggedUsers:
		platformLoadedVideoAttachment:
		postContentTypeMutation:
		postPromptPayload:
		publishAsQuestionAndAnswerSession:
		togglePublishAsQuestionAndAnswerSession:
		preferredMarketplaceMutation:
		productItemCategoryGraphQLID:
		productItemPickupDeliveryInfo:
		productItemPriceMutation:
		productItemShouldPostToMarketplace:
		productItemSuggestedPlace:
		productItemSuggestedZipCode:
		productItemTitle:
		publishTarget:
		remoteImageAttachments:
		removeSponsor:
		removeStorylineAttachment:
		removeStickerAttachment:
		replaceStorylineAttachmentWithMediaAttachments:
		selectedStarRating:
		shouldCreateProfileBadge:
		statusTextMutation:
		stickers:
		taggableActivityComposition:
		targeting:
		withTagsMutation:
		poll:
		sponsor:
		videoAttachmentMutation:
		videoTaggingAssetMutation:
		videoTaggingAnalytics:
		boostedComponentData:
		albumTracker:]

 

Conclusion

The Facebook app is a complex app and not just a simple web view. It contains a lot of third party libraries and even splits its __TEXT segment in order to not reach the 60 MB limit imposed by Apple.


constructor and destructor attributes

Posted: July 16th, 2016 | Author: | Filed under: code injection, Debugging, macOS, Programming | Tags: , , , , , , | 3 Comments »

GCC (and Clang) supports constructor and destructor attributes:

__attribute__((constructor))
__attribute__((destructor))

 
 
Description

A function marked with the __attribute__((constructor)) attribute will be called automatically before your main() function is called. Similarly a function marked with the __attribute__((destructor)) attribute will be called automatically after your main() function returns.

You can find the GCC documentation here:

constructor
destructor
The constructor attribute causes the function to be called automatically before execution enters main (). Similarly, the destructor attribute causes the function to be called automatically after main () has completed or exit () has been called. Functions with these attributes are useful for initializing data that will be used implicitly during the execution of the program.
These attributes are not currently implemented for Objective C.

Note: The GCC documentation tells that these attributes are not implemented for Objective-C. However this seems to work as expected with my tests using Clang ‘clang-703.0.31’ from Xcode 7.3.1.

 
 
Example

Here is an example of C code to demonstrate these attributes:


//
// To compile:
// clang -o constructor constructor.c
//

#include <stdio.h>
 
void constructor() __attribute__((constructor));
void destructor() __attribute__((destructor));

int main()
{
	printf ("main called\n");
	return 0;
}

void constructor()
{
	printf ("constructor called\n");
}

void destructor()
{
	printf ("destructor called\n");
}

When running this application, you will see the following output logs as you would expect:

constructor called
main called
destructor called

 
 
How does it work under the hood?

When you mark functions with these attributes, the compiler will create in your binary the sections called __mod_init_func for the constructors and __mod_term_func for the destructors. These sections contain the list of function pointers. You can use the excellent MachOView to see these sections:


ACTION

When your application is launched, dyld will call the constructors before your main() function is called. This is handled by the following dyld function:

void ImageLoaderMachO::doModInitFunctions(const LinkContext& context)

The destructors are handled by the dyld function:

void ImageLoaderMachO::doTermination(const LinkContext& context)

Since dyld is open source you can look at the implementation in the file ImageLoaderMachO.cpp of dyld. The source code for macOS 10.11.4 is available here.

 
 
Example of use

  • The first obvious usage is to be able to initialize some global variables with a constructor and do some cleanup with a destructor. It could be used to initialize some libraries too.
  •  

  • Another usage is code injection. In a previous post ‘Simple code injection using DYLD_INSERT_LIBRARIES’ I wrote code to replace some methods with an Objective-C +(void)load class method. Using a constructor would allow to inject code earlier in the process.
  •  

  • A constructor attribute could be used to implement a software protection. You could encrypt your executable with a custom encryption and use a constructor function to decrypt the binary just before it is loaded.