2

I want to capture a screenshot from a background service. It work fine under ios6 and ios7,but crash under ios7 retina.

this is my code

{

IOMobileFramebufferConnection connect;
        kern_return_t result;
        m_screenSurfaceRef = NULL;

        io_service_t framebufferService = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("AppleH1CLCD"));
        if(!framebufferService)
            framebufferService = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("AppleM2CLCD"));
        if(!framebufferService)
            framebufferService = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("AppleCLCD"));

#pragma unused(result)
        result = IOMobileFramebufferOpen(framebufferService, mach_task_self(), 0, &connect);

        result = IOMobileFramebufferGetLayerDefaultSurface(connect, 0, &m_screenSurfaceRef);

}

when run on retina IOMobileFramebufferGetLayerDefaultSurface(connect, 0, &m_screenSurfaceRef) crashed.

crash info:

thread #1: tid = 0x1dfe9, 0x000000018ea2c270 IOMobileFramebufferIOMobileFramebufferGetLayerDefaultSurface + 4, queue = 'com.apple.main-thread, stop reason = EXC_BAD_ACCESS (code=1, address=0x5e06dc28) frame #0: 0x000000018ea2c270 IOMobileFramebufferIOMobileFramebufferGetLayerDefaultSurface + 4

joeykika
  • 21
  • 3

1 Answers1

4

This is a 64-bit issue with the reverse engineering of IOMobileFramebuffer.h. The prototype for

IOMobileFramebufferReturn IOMobileFramebufferGetLayerDefaultSurface(IOMobileFramebufferConnection connection, int surface, CoreSurfaceBufferRef *ptr);

... is incorrect, because the typedef for IOMobileFramebufferConnection is incorrect. If one has a look at the disassembly and partial decompilation of IOMobileFramebufferGetLayerDefaultSurface:

IOMobileFramebufferReturn IOMobileFramebufferGetLayerDefaultSurface(Connection *connection, int surface, IOSurfaceRef *ptr)
{
    if(connection) {                       // 0x18f95026c:  cbz    x0, 0x18f95027c
            long tmp = connection->unk2;   // 0x18f950270:  ldr    x3, [x0, #552] // <== Crash!
            if(tmp) {                      // 0x18f950274:  cbz    x3, 0x18f95027c
                    goto tmp;              // 0x18f950278:  br     x3
            }
   }
   //0x18f95027c:  movn   w0, #8191, lsl #16
   //0x18f950280:  movk   w0, #706
   //0x18f950284:  ret    lr

}

we see that the first parameter is dereferenced, which means it must be pointer-sized. In the reversed header, IOMobileFramebufferConnection is typedef'd to io_connect_t, which is typedef'd to io_object_t, which is mach_port_t, which is __darwin_mach_port_t, which is __darwin_natural_t, which is unsigned int! Int just happens to be pointer-sized on 32-bit, but is not under 64-bit, so we end up just sending in the first 32 bits of the pointer into this function, and this will obviously crash.

My solution was to just re-typedef to a void* like so:

typedef void *IOMobileFramebufferConnection;

The full corrected header can be found at https://gist.github.com/nevyn/9486278.

nevyn
  • 7,052
  • 3
  • 32
  • 43