博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
UIImagePickController打开闪光模式拍照瞬间锁屏crash
阅读量:6979 次
发布时间:2019-06-27

本文共 6934 字,大约阅读时间需要 23 分钟。

  • 复现步骤: 在UIImagePickController拍照页面开启闪光模式,在拍照的瞬间点击锁屏按钮,App重回前台后拍出来的照片一片漆黑,这时点击”使用照片”会crash。

  • 系统 < iOS 10

  • 原因:NSMutableDictionary setObject是参数传nil导致的。

    注意:当UIImagePickController的allowEditing属性为YES的时候不会crash。

  • 解决方案:hook底层拍照处理API,当发现拍出来的照片为空时手动生成一张空白图片。

通过Hopper查看产生这个问题有两处可能返回nil的调用,

    1. -[PLPhotoTileViewController _newOriginalImageForPickerFromCachedData]; OC方法
void * -[PLPhotoTileViewController _newOriginalImageForPickerFromCachedData](void * self, void * _cmd) {   	rbx = self;   	rax = [self unscaledImage];   	if (rax == 0x0) {           	rax = [rbx image];   	}   	rax = _NewUIImageFromCachedImage(rax);   	return rax;}复制代码
    1. int _CreateImageDataFromJPEGDataAndOrientation(int arg0, int arg1); C函数
int _CreateImageDataFromJPEGDataAndOrientation(int arg0, int arg1) {    	rbx = PLExifOrientationFromImageOrientation(arg1, arg1);   	r15 = [NSDictionary alloc];   	rdx = [NSNumber numberWithInt:rbx];   	rbx = [r15 initWithObjectsAndKeys:rdx];   	r14 = CGImageCreateEXIFJPEGData(0x0, arg0, 0x0, rbx);   	[rbx release];   	rax = r14;   	return rax;}复制代码

我们利用fishhook来对C函数进行hook。

  • Code:
#import 
#import
#if __has_feature(objc_arc)#error This file must be compiled with MRC. Use -fno-objc-arc flag (or convert project to MRC).#endiftypedef id (*ImageDataIMP)(id, SEL, ...);static CGImageRef (*orig_createImageData)(NSData *data, UIImageOrientation orientation);UIImage *createBlankImage();CGImageRef new_createImageData(NSData *arg0, UIImageOrientation arg1){ CGImageRef imageRef = orig_createImageData(arg0, arg1); if (imageRef == NULL) { UIImage *image = createBlankImage(); imageRef = CGImageRetain(image.CGImage); [image release]; } return imageRef;}NSString *originCreateImageDataFromJPEGDataAndOrientationFuncKey(){ //CreateImageDataFromJPEGDataAndOrientation static NSString *key; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ key = [[NSString alloc] initWithData:[NSData dataWithBytes:(unsigned char []) {0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x44, 0x61, 0x74, 0x61, 0x46, 0x72, 0x6f, 0x6d, 0x4a, 0x50, 0x45, 0x47, 0x44, 0x61, 0x74, 0x61, 0x41, 0x6e, 0x64, 0x4f, 0x72, 0x69, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e} length:41] encoding:NSASCIIStringEncoding]; }); return key;}NSString *originalImageForPickerFromCachedDataSELKey(){ //_newOriginalImageForPickerFromCachedData static NSString *key; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ key = [[NSString alloc] initWithData:[NSData dataWithBytes:(unsigned char []) {0x5f, 0x6e, 0x65, 0x77, 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x6c, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x46, 0x6f, 0x72, 0x50, 0x69, 0x63, 0x6b, 0x65, 0x72, 0x46, 0x72, 0x6f, 0x6d, 0x43, 0x61, 0x63, 0x68, 0x65, 0x64, 0x44, 0x61, 0x74, 0x61} length:40] encoding:NSASCIIStringEncoding]; }); return key;}@implementation UIImagePickerController (DEFImagePickerControllerCrashFix)+ (void)load{ static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ //先手动加载私有framework,否则取不到私有类 NSBundle *photoLibraryBundle = [NSBundle bundleWithPath:@"/System/Library/PrivateFrameworks/PhotoLibrary.framework"]; if (![photoLibraryBundle load]) { return; } //PLPhotoTileViewController NSString *photoTileControllerKey = [[NSString alloc] initWithData:[NSData dataWithBytes:(unsigned char []) {0x50, 0x4c, 0x50, 0x68, 0x6f, 0x74, 0x6f, 0x54, 0x69, 0x6c, 0x65, 0x56, 0x69, 0x65, 0x77, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72} length:25] encoding:NSASCIIStringEncoding]; Class originCls = NSClassFromString(photoTileControllerKey); [photoTileControllerKey release]; Class overridedCls = self; SEL originalSelector = NSSelectorFromString(originalImageForPickerFromCachedDataSELKey()); SEL overrideSelector = @selector(p_newOriginalImageForPickerFromCachedData); Method originalMethod = class_getInstanceMethod(originCls, originalSelector); Method overrideMethod = class_getInstanceMethod(overridedCls, overrideSelector); BOOL success = class_addMethod(originCls, originalSelector, method_getImplementation(overrideMethod), method_getTypeEncoding(overrideMethod)); if (success) { class_replaceMethod(overridedCls, overrideSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod)); } else { method_exchangeImplementations(originalMethod, overrideMethod); } rebind_symbols((struct rebinding[1]){originCreateImageDataFromJPEGDataAndOrientationFuncKey().UTF8String, new_createImageData, (void *)&orig_createImageData}, 1); });}- (id)p_newOriginalImageForPickerFromCachedData{ SEL originalSelector = @selector(p_newOriginalImageForPickerFromCachedData); Method method = class_getInstanceMethod([UIImagePickerController class], originalSelector); ImageDataIMP imp = (ImageDataIMP)method_getImplementation(method); UIImage *image = imp(self, _cmd); if (!image) { //如果没有生成照片,返回一张空白图片,防止crash image = createBlankImage(); } return image;}@end//生成一张黑色图片UIImage *createBlankImage(){ UIBezierPath *bezierPath = [UIBezierPath bezierPathWithRect:[[UIScreen mainScreen] bounds]]; UIImage *blackImage = [UIImage p_imageWithColor:[UIColor blackColor] path:bezierPath]; CGImageRef imageRef = blackImage.CGImage; UIImage *drawImage = [[UIImage alloc] initWithCGImage:imageRef scale:[UIScreen mainScreen].scale orientation:UIImageOrientationUp]; return drawImage;}@interface UIImage (PickerBlankImage)+ (UIImage *)p_imageWithColor:(UIColor *)color path:(UIBezierPath *)path;@end@implementation UIImage (PickerBlankImage)+ (UIImage *)p_imageWithColor:(UIColor *)color path:(UIBezierPath *)path{ CGRect rect = CGRectMake(0, 0, 3, 3); if (path) { rect = path.bounds; } UIGraphicsBeginImageContextWithOptions(rect.size, NO, 0); CGContextRef context = UIGraphicsGetCurrentContext(); CGContextSetFillColorWithColor(context, [color CGColor]); if (path) { [path fill]; } else { CGContextFillRect(context, rect); } UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); return image;}@end复制代码

转载地址:http://jwfpl.baihongyu.com/

你可能感兴趣的文章
IOS UITabBarViewController 修改背景颜色
查看>>
Java并发之synchronized
查看>>
深入理解javascript中的立即执行函数(function(){…})()
查看>>
统一客服消息返回错误:{"errcode":43004,"errmsg":"require subscribe hint: [9Vv08633952]"}
查看>>
我的友情链接
查看>>
yum三种方法
查看>>
压缩和归档及vi的使用
查看>>
Linux下多播的配置【十全十美】
查看>>
open-falcon编写的整个脑洞历程
查看>>
安装和部署Exchange Server 2007
查看>>
File Operations In Java
查看>>
add nodes to the swarm
查看>>
1-1 分配内存资源给容器和POD
查看>>
VLAN-VTP-Trunk
查看>>
度量快速开发平台端口映射的介绍
查看>>
数据库设计 之设计 表字段类型
查看>>
服务器监控常用命令
查看>>
JAVA学习笔记(6)
查看>>
awk: (FILENAME=- FNR=1) 致命错误: 试图访问字段 -2
查看>>
安卓手机文件管理器:360°LES文件浏览器
查看>>