Bitbucket is a code hosting site with unlimited public and private repositories. We're also free for small teams!

Close

ShareKitTest: Research into injecting custom sharing services for OS X

OS X 10.8 Mountain Lion has introduced a share button as a concept. This was integrated into various applications shipping with the OS, and was exposed as a public API for application developers.

It's a very handy way to share content for users, and easy to implement for developers.

The trouble is, some of us don't use just the "proscribed" services such as Twitter or Facebook. OS X 10.9 Mavericks has introduced LinkedIn and more, but some services are sorely missing.

Note: The code was developed under OS X 10.8, which is why I am releasing it. I am uninterested in further exploration on how to write sharing plugins, since I have never went into working on the Google+ plugin that was the motivation behind the search (aside from the fact that Apple actively blocked something so trivial).

I am not a security researcher nor do I ordinarily play with defeating systems. This has piqued my interest because I was being blocked from using trivial services on my own machine. And having upgraded to 10.9, and this method still working, but me no longer being motivated to continue exploration of the APIs, I release this in hope someone else will play with it.

Hopefully, Apple will not be inspired to block people completely off. I really like the deep integration of something that works more elegantly than NeXT-style Services. Please, Apple, open ShareKit up.

Research

OS X 10.8 has a helper process that actually provides the sharing service to applications. Access to this helper process is provided solely through a simple API. This is alright. What's even better is that there are bundles with extension .sharingservice implementing individual services.

Surely we could just implement a plugin and have it loaded by ShareKit, as this entire framework is fondly named by Apple?

Of course not. If you try that, first you find out that the plugin is not even looked for in the expected location such as /Library/ShareKit/PlugIns, which would be analogous to where these are looked for in the System domain's Library folder. Not wanting to dig into /System (which is a big no-no), you figure out that you can put the framework in /AppleInternal/Library/ShareKit/PlugIns.

Hurray? Of course not! You get informed that:

com.apple.ShareKitHelper[2646]: --error: [ShareKit-Plugin] ShareKit plugin is not signed by Apple: file:///AppleInternal/Library/ShareKit/PlugIns/ShareKitTest.sharingservice/
com.apple.ShareKitHelper[2646]: ShareKit plugin is not signed by Apple: file:///AppleInternal/Library/ShareKit/PlugIns/ShareKitTest.sharingservice/

Thanks so much, for first not documenting this and then blocking enterprising developers and hackers from even trying to figure out how to load their own plugins. Oh well -- there must be reasons for not disclosing a way to access ShareKit.

Workaround

You can use a debugger such as gdb or lldb to attach to the com.apple.ShareKitHelper process as soon as it launches. When it launches, you tell the process to break as soon as it enters NSApplicationMain(); you want some setup to be done and some essential frameworks to be loaded before loading your own code.

Then you load your plugin. Inside this code, we abuse the fact that Objective-C categories override the base class methods. That is, if we extend an Objective-C class with a category (those not familiar with the language: a category is not a subclass), we actually replace the implementation of the method.

ShareKit has an interesting class SHKServicesManager with an even more interesting method: - (BOOL)isFileSignedByAppleAtURL:(id)arg1. Accepting an NSURL, we can trivially return YES and every single plugin will be loaded by ShareKitHelper. (Our plugin is already loaded, so no problem there.)

Next we have to implement a SHKShareWindowServiceProvider. Our subclass will return services in appropriate contexts, window controller class, service images, etc.

We also need to implement a SHKShareWindowController subclass, which will contain our actual user interface.

On the xcode project

The Xcode project contains a custom 'Run Script' build step which deploys the plugin into /AppleInternal path. It also contains a 'shared schema' which is configured to, before running, killall -9 com.apple.ShareKitHelper.

Then it waits for 'something' (your click on a Share button) to run com.apple.ShareKitHelper.

Service should then be loaded and the Share button should show two new options, the second of which actually has some user interface.

(Looking back, I don't know why my original instructions include breaking at NSApplicationMain(). Obviously because otherwise the plugin didn't work. However, looking at the Xcode project's configuration, obviously this is not necessary.)

Future

My research into injecting an extra service has stopped once I succeeded in injecting custom dummy services into ShareKit.

When today I was researching whether the project still works under OS X 10.9, initially it didn't work. I didn't know why; fiddling around, it started to work as if nothing happened.

Everything is an experiment created during 10.8. Apple might block off attaching a debugger from ShareKitHelper in the future, which is why I still would advise people not to create finished products based on this knowledge. You may want to keep your plugin and application and what-not for other hackers. It's your choice, of course.

-- Ivan Vucica ivan@vucica.net - Oct 5 2013

Recent activity

Ivan Vučica

Ivan Vučica pushed 6 commits to ivucica/ShareKitTest

11a31a3 - Public release
0f56a87 - Exploration of microblogging sharing service provider (e.g. setup-logo-ivssp.png)
3ac436f - Switched to SHKShareWindowServiceProvider to get more boilerplate code for free. Added image view support. Added and using outlets for send and cancel buttons.
b21ee4c - Dimming source window.
66094a6 - Basic functionality (a window is displayed). (First work on project: Aug 11 2012)
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.