Taking Screenshots from iOS app - emulating Displa

2019-01-24 18:37发布

问题:

I need to create an iOS app which can take screenshots of other apps, videos etc. as a part of an experiment. I am open to using Private API's since I won't be publishing the app to the app store. I could successfully use the method GetUIScreenImage() to take snapshots of the current screen (app screen).

However, due to sandbox restrictions I am not able to take screenshots while in the background. I get the error - "Cannot call CreateUIScreenImage() while the app is in background".

I referred to this article - How does the iOS app Display Recorder record the screen without using private API? which suggested use of IOMobileFramebuffer and IOSurface to bypass sandbox restrictions, also this link - http://www.iphonesheep.com/2009/11/25/iphone%E2%80%99s-framebuffer-secrets-revealed/. But I am unable to get the flow of creating and transferring surfaces. Any help on this topic is highly appreciated.

Also, is there a way to call UIGetScreenImage() in the background on a jailbroken device ?

Update:
I am able to get hold of the main screen surface and put it on the app layer, but my use case is to be able to open up other apps and be able to record them via the app I create. The surface I paste over my app layer is however static.

edit
Here is what I understand - the main screen surface attached to the main display layer needs to be put on the current layer. After doing so, I should be able to browse through other apps, open them from the surface just pasted.

Here is the code (It puts the main screen surface on top of app layer but I am unable to use that surface i.e, open up apps etc.):

IOMobileFramebufferConnection connect;
kern_return_t result;
CoreSurfacebufferRef screenSurface = NULL;
io_service_t framebufferService = IOServiceGetMatchingService (kIOMasterPortDefault, IOServiceMatching ("AppleH1CLCD"));
if(!framebufferService)
        io_service_t framebufferService = IOServiceGetMatchingService (kIOMasterPortDefault, IOServiceMatching ("AppleM2CLCD"));
if(!framebufferService)
        io_service_t framebufferService = IOServiceGetMatchingService (kIOMasterPortDefault, IOServiceMatching ("AppleCLCD"));
result = IOMobileFramebufferOpen( framebufferService, mach_task_self(), 0 , &connect);
result = IOMobileFramebufferGetLayerDefaultSurface(connect, 0, &screenSurface);

CALayer *Layer;
Layer = [CALayer layer];
[Layer setFrame: CGRectMake(0, 0, 768, 1024)];
[Layer setOpaque:YES];
IOSurfaceLock(screenSurface, 0, NULL);
[Layer setContents: (_bridge id)(screenSurface)];
IOSurfaceUnlock(screenSurface, 0, NULL);
[self.view.layer addSublayer:Layer];

I know i am missing out something and doing something wrong. It would be great help if you could point that out.

update
I think I may be wrong in my fundamental assumption.
1. What I assume -> The app swaps the surfaces (or layers) so that the user can now browse on the main screen. The app never moves to the background.
2. What sounds more correct -> The app moves to the background. Gets the topmost surface on screen, creates an image out of it and saves it. Please clarify.

回答1:

What is exactly the problem with "get the flow of creating and transferring surface"? Can you post the code which are using and point out which line doesn't work.

For the jailbroken devices:

Go to /Application/XCode/Content/Developer/Platforms and grep it for the sentence "Cannot call CreateUIScreenImage() while the app is in background". You will find it in some framework (I assume UIKit). Next, you can disassemble it and you will find some entitlement which is required to do screenshots in the background.

And you can look here how to use entitlements with ldid for jailbroken device: iOS How to use Entitlement.plist to specify property of my app