Detect App Translocation Without the 10.12 SDK

July 26, 2016

As I mentioned in an earlier blog post, there's a new API for App Translocation in the 10.12 SDK: <Security/SecTranslocate.h>. However, if you're sane, you're not shipping your apps yet with the 10.12 SDK. So how can you detect App Translocation in your shipping apps? The trick is that since you can't link against the new functions, you have to load them at runtime. I've provided sample code below.

#include <dlfcn.h> // dlopen, dlclose

bool IsTranslocatedURL(CFURLRef currentURL, CFURLRef *originalURL)
{
	if (currentURL == NULL)
	{
		return false;
	}
	
	// #define NSAppKitVersionNumber10_11 1404
	if (floor(NSAppKitVersionNumber) <= 1404)
	{
		return false;
	}
	
	void *handle = dlopen("/System/Library/Frameworks/Security.framework/Security", RTLD_LAZY);
	if (handle == NULL)
	{
		return false;
	}
	
	bool isTranslocated = false;
	
	Boolean (*mySecTranslocateIsTranslocatedURL)(CFURLRef path, bool *isTranslocated, CFErrorRef * __nullable error);
	mySecTranslocateIsTranslocatedURL = dlsym(handle, "SecTranslocateIsTranslocatedURL");
	if (mySecTranslocateIsTranslocatedURL != NULL)
	{
		if (mySecTranslocateIsTranslocatedURL(currentURL, &isTranslocated, NULL))
		{
			if (isTranslocated)
			{
				if (originalURL != NULL)
				{
					CFURLRef __nullable (*mySecTranslocateCreateOriginalPathForURL)(CFURLRef translocatedPath, CFErrorRef * __nullable error);
					mySecTranslocateCreateOriginalPathForURL = dlsym(handle, "SecTranslocateCreateOriginalPathForURL");
					if (mySecTranslocateCreateOriginalPathForURL != NULL)
					{
						*originalURL = mySecTranslocateCreateOriginalPathForURL((CFURLRef)currentURL, NULL);
					}
					else
					{
						*originalURL = NULL;
					}
				}
			}
		}
	}
	
	dlclose(handle);
	
	return isTranslocated;
}

After you've detected that your app is translocated, what should you do about it? That's up to you. Don't be evil. Or what the hell, do be evil. App Translocation is wrong, and for your customers, two wrongs can make a right.