Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

IOMobileFramebufferGetLayerDefaultSurface not working on iOS 9

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?

like image 799
Pyrology Avatar asked Aug 27 '15 03:08

Pyrology


3 Answers

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

like image 177
jvisenti Avatar answered Nov 18 '22 08:11

jvisenti


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.

like image 39
anthonya1999 Avatar answered Nov 18 '22 09:11

anthonya1999


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.

like image 2
Technologeeks Avatar answered Nov 18 '22 08:11

Technologeeks