6

My main question is, how can I reverse engineer a private API function that already exists, but has been modified in a new version of iOS?

I have created an iOS application to record the screen content using IOSurface and IOMobileFramebuffer. The main functions the framebuffer use to open it are IOMobileFramebufferGetMainDisplay(connect) and IOMobileFramebufferGetLayerDefaultSurface.

These functions have been used since the very first version of the app, and they have worked on all versions of iOS 7 and 8. However, on the latest iOS 9 beta, which is beta 5, the function IOMobileFramebufferGetLayerDefaultSurface does not work. The function does not return 0, as it should when it successfully opens the framebuffer.

This other user on StackOverflow seems to also be experiencing the same issue: IOMobileFramebufferGetLayerDefaultSurface function failed on iOS 9. We have a reference to IOMobileFramebufferConnection named “_framebufferConnection” and an IOSurfaceRef named “_screenSurface” Here is the current code:

IOMobileFramebufferGetMainDisplay(&_framebufferConnection); IOMobileFramebufferGetLayerDefaultSurface(_framebufferConnection, 0, &_screenSurface;

As stated before, these work perfectly on iOS 7-8, but on iOS 9, the second function crashes. I have also looked at the binaries with the symbols for both versions and compared them. The second parameter of the LDR is slightly different in iOS 9, when compared to the iOS 8.4.1 binary. So, back to the main question, how can I reverse engineer IOMobileFramebufferGetLayerDefaultSurface, or see how in what way it’s actually been modified on iOS 9?

Community
  • 1
  • 1
Pyrology
  • 169
  • 2
  • 12

4 Answers4

10

To answer the question of "how in what way it’s actually been modified on iOS 9", I did some digging into IOMobileFramebufferGetLayerDefaultSurface on iOS8 vs iOS9 (GM). Here are the results of what I found:

Setup:

IOMobileFramebufferRef fb; IOMobileFramebufferGetMainDisplay(&fb);

iOS8 Implementation:

  • Calls through to kern_GetLayerDefaultSurface

  • Which accesses underlying IOConnection

    io_connect_t fbConnect = *(io_connect_t *)((char *)fb + 20)

  • To retrieve the IOSurfaceID via

    IOSurfaceID surfaceID; uint32_t outCount = 1; IOConnectCallScalarMethod(fbConnect, 3, {0, 0}, 2, &surfaceID, &outCount)

  • Returns IOSurfaceLookup(surfaceID)

iOS9 Implementation:

  • Same steps as above aside from the return

  • Then tries to retrieve a mach port to access the surface via

    io_service_t fbService = *(io_service_t *)((char *)fb + 16) mach_port_t surfacePort; IOServiceOpen(fbService, mach_task_self(), 3, &surfacePort)

  • On success, return IOSurfaceLookupFromMachPort(surfacePort)

It is on the last step that IOServiceOpen returns error 0x2c7 (unsupported function). Notice that the 3rd argument specifying the type of connection is 3 instead of the usual 0 when opening the framebuffer service. It is almost certain that this new connection type has permissions restrictions that prevent anyone but Apple from retrieving a mach port to access the IOMFB surface.

What's somewhat interesting is that the call to IOConnectCallScalarMethod still works to retrieve the ID of the IOMFB surface. However, it can no longer be accessed using IOSurfaceLookup because the surface is no longer global. It's a little surprising that it was global in the first place!

Hope this helps demystify why IOMFB can no longer be used to record the screen.

Source: My own use of LLDB with an iPhone6 running iOS 8.4 and an iPhone6+ running iOS9 GM

jvisenti
  • 101
  • 4
  • I suspected that Apple somehow locked it up...is there any other way to capture an IOSurface w/o IOMobileFramebuffer, or maybe some other framework to use instead of it? – anthonya1999 Sep 11 '15 at 01:20
  • 1
    @anthonya1999 There is no way I know of to record the screen while your app is in the background. If you want to record your app, you can use the public `drawViewHierarchyInRect:` UIView method. This method is also App Store safe, though I suspect that isn't important in your case. – jvisenti Sep 11 '15 at 16:44
  • Very useful bit of detective work. Thanks for posting it. (Voted.) The next question is, is there an entitlement (possibly private) that Apple added so that it's apps are able to still use it? If not, what mechanism does AirPlay use to do screen mirroring? (See my question about how to do this in iOS 9 at [**Trying to do screen capture with private framework IOSurface no longer seems to work in iOS 9**](http://stackoverflow.com/questions/32356981/trying-to-do-screen-capture-with-private-framework-iosurface-no-longer-seems-to?noredirect=1#comment53093867_32356981) – Duncan C Sep 17 '15 at 12:57
  • @DuncanC I think it's quite likely that there is an entitlement for this. I guess we'll know once someone has access to a jailbroken iOS9 device. AirPlay screen mirroring is a different animal though, and appears to use virtual displays. Here's what the syslog looks like when mirroring over AirPlay: ```mediaserverd[23] : [AirPlay] Virtual display stream activating with options <> mediaserverd[23] : [AirPlayEndpointScreen] Screen start backboardd[57] : IOMFB setting virtual mode: 0 0 ``` – jvisenti Sep 18 '15 at 14:39
  • Yup. The work I'm doing for my current client is pretty much stuck until we have access to an iOS 9 jailbreak so we can investigate. Jailbreaking is not an option for the final product, but private APIs ARE an option, and if we can find an entitlement it's possible that we can unlock it ourselves. – Duncan C Sep 18 '15 at 15:13
  • @DuncanC I'm in the same boat. I actually made iRec, which is an iOS 7-8 screen recorder, so without there being any current way to capture the screen buffer, I'm stuck myself. Trying to think of another method or some way to get around these restrictions. – anthonya1999 Sep 18 '15 at 17:13
  • @DuncanC - I think the entitlement you are looking for is `com.apple.QuartzCore.global-capture`. Can you clarify what you mean by `if we can find an entitlement it's possible that we can unlock it ourselves`? Did you had any luck solving it? – Elist May 17 '16 at 08:27
5

I believe @nevyn is correct. However, I would like to elaborate a bit more. I have looked into this exact issue extensively, and the IOMobileFramebufferGetLayerDefaultSurface function does return -536870201, while it should return 0 if it runs the function without any problems. This error is on the internet, but it only appears when users encounter generic problems with QuickTime. It could be that Apple has indeed locked up the framework completely, and needs an Apple-only entitlement to access the framebuffer. We cannot add these entitlements, since it also has to be on the provisioning profile. I currently am trying to read and interpret the disassembly and doing some reverse engineering work on the IOMobileFramebuffer binary to see if any of the parameters have changed since the last iOS version. I will surely update this answer if I discover anything. But if this is the case, I would suggest trying to find another method of trying to capture/record the screen content.

-UPDATE-

It seems as if there is evidence that this would be the case, if you read this, it shows the exact same error code, and it means that the function is "unsupported", and returns an IOKit error. At least we know what this means now. However, I am still unsure of how to fix it, or to make the function work. I will continue looking into this.

UPDATE 2

I have actually discovered a brand new class in iOS 9, "FigScreenCaptureController", and it is part of the MediaToolbox framework! What the strange thing is though, is why would Apple include this only in iOS 9? So, maybe there will be a way to record the display through this...I will be looking into this class more in depth very soon.

anthonya1999
  • 253
  • 3
  • 13
  • hey @anthonya1999, any luck using FigScreenCaptureController to capture the screen? – Shirish Kamath Oct 06 '15 at 10:34
  • @ShirishKamath I know this is a late reply, but I have looked around in the class a bit, and you can call start/stop capture just fine, but I don't know how to set it up. (ie. video path, framerate, etc.) I will continue to see what I can do with this... – anthonya1999 Dec 19 '15 at 22:21
2

Not entirely correct - it's just a matter of an entitlement, as you can see if you dump the kext:

$ jtool -d __TEXT.__cstring 97.IOMobileGraphicsFamily.kext | grep com.apple
0xffffff80220c91a2: com.apple.private.allow-explicit-graphics-priority

If you self sign (jtool --sign --ent) with this , everything works well.

This does mean that on non-JB devices you can't use it. But with a jailbreak the immense power is in your hands once more.

Technologeeks
  • 7,674
  • 25
  • 36
0

IOMobileFramebuffer is completely locked down on iOS 9 and cannot be used from non-Apple apps anymore. AFAICT, this closes the last private API to capture the screen efficiently. ReplayKit is the only replacement, but does not allow programmatic access to the actual video data.

nevyn
  • 7,052
  • 3
  • 32
  • 43