【非越狱环境】钉钉躺床上打卡之水印篇
钉钉iOS客户端签到图片修改(非越狱环境)本文续自上一篇:【非越狱环境】钉钉躺床上打卡之WIFI篇(https://www.chinapyg.com/forum.php?mod=viewthread&tid=89902)
一、背景最近一直有朋友抱怨外勤人员不仅要移动打卡,还要每天在考勤地上传一张照片,照片上有位置信息、日期信息等水印,如果可以提前多拍几张图片,以后每天选一张用于签到岂不是很high?顺手抽空处理了一下,最终实现从相册中随便选取照片,签到拍摄照片时直接替换掉,确保水印被打在相册中选取的那张图片上并上传。
二、工具准备见上一篇。
三、分析看了钉钉的签到拍照的界面,因为在图片上增加了位置信息和日期等水印,而且自定义了拍摄的UI,所以通用的技术是对AVFoundation框架下的AVCaptureStillImageOutput类下手,该类获取摄像头图片的方式是jpegStillImageNSDataRepresentation:,此方法理论上对所有app通用有效。
四、编码
我们在上一篇的代码基础上增加签到信息修改的相关代码,从相册选取图片的代码就不重复写了,直接github上搞了个:QBImagePickerController1、创建一个类DTCameraSettingViewController用于选取相册中的图片,代码如下:#import "DTCameraSettingViewController.h"
#import "QBImagePickerController.h"
#import "JF_Helper.h"
#import "UIImage+JFUtil.h"
#import "ALAsset+JFUtil.h"
#import "DTCameraHook.h"
static NSString *image_base64_button = @"iVBORw0KGgoAAAANSUhEUgAAALQAAABACAYAAACzzl09AAAAAXNSR0IArs4c6QAA\
AAlwSFlzAAAWJQAAFiUBSVIk8AAAABxpRE9UAAAAAgAAAAAAAAAgAAAAKAAAACAA\
AAAgAAACF+d7zw0AAAHjSURBVHgB7JixTcNQFEVdUbEJA5AABWIH9qKgyg4YRB8x\
hVdggQzw+c/EN0oq3yq3OEiIG+k9c3VyYn1nGC5+Hr/b3fazvW3GNm3Hdth8tMYv\
DFIcmJ0sN7uj5eqFvqeXr1O76cPvKcXpwYdojQPlbLl7Mrmno8x7XWBsu6evdv/8\
027PBnkBgSsTKCfLzX6C2C2+dqn3Z1Ivd+b+9/dhbC9X7sy/h8AqAuVqOVtil8Pz\
Up1DFtOReRVHhoIIlLOLv/OZen4ArAe/fgsP6kkVCKwmsBw/yuWhvs0ow+tcsvoK\
DEIgiMB8pv6/KU9DP3vMX83xABj0DlHFIlDuHs/Rh2E5f1hXYBgCYQTksUJYQepA\
wCEgjxWcbWYhEEZAHiuEFaQOBBwC8ljB2WYWAmEE5LFCWEHqQMAhII8VnG1mIRBG\
QB4rhBWkDgQcAvJYwdlmFgJhBOSxQlhB6kDAISCPFZxtZiEQRkAeK4QVpA4EHALy\
WMHZZhYCYQTksUJYQepAwCEgjxWcbWYhEEZAHiuEFaQOBBwC8ljB2WYWAmEE5LFC\
WEHqQMAhII8VnG1mIRBGQB4rhBWkDgQcAvJYwdlmFgJhBOSxQlhB6kDAISCPFZxt\
ZiEQRkAeK4QVpA4EHAKLx38AAAD//zKoC3IAAAGsSURBVO2YsU0DQRQFLyKiEwrA\
BgJED/RF4Mg9cCByiyrcghuggGX/6jwyooF78liy9E528P5ovPrrafPeWr0nXxII\
JoDHhOBhrC4BPCbIRALBBPCYEDyM1SWAxwSZSCCYAB4TgoexugTwmCATCQQTwGNC\
8DBWlwAeE2QigWACeEwIHsbqEsBjgkwkEEwAjwnBw1hdAnhMkIkEggngMSF4GKtL\
AI8JMpFAMAE8JgQPY3UJ4DFBJhIIJoDHhOBhrC4BPCbIRALBBPCYEDyM1SWAxwSZ\
SCCYAB4TgoexugTwmCATCQQTwGNC8DBWlwAeb+f2Uw/P3+1WLBJIJFDulsPl8rSZ\
27Eenj7bfeIwdpZAuTtO6O7ytP1ob8vDXjQSSCTQD+X9OKG7y9PjV7sbQvdT+mFu\
L4kD2fl6CZSzZ3/L5UGi7x67YfjcTkp9vXKkTV6udndPi7s7+r8e203/4HA2vY7w\
2ku8KILIsBIC5eTYmZc1Y5H5UA7/qbhIPU5qxO5riFkGa3agtot/Ml+aXXvIuCj2\
G2P/8vhLb80D2e26fnDDyXKzXwDZmS8E/gW5cu3DvOjXFwAAAABJRU5ErkJggg==";
@interface DTCameraSettingViewController () <QBImagePickerControllerDelegate>
@property (nonatomic, strong) NSMutableArray *array_photos;
@property (nonatomic, strong) UIImageView *imageView_photo;
@property (nonatomic, strong) UIButton *button_clean;
@property (nonatomic, strong) UIButton *button_commit;
@end
@implementation DTCameraSettingViewController
- (void)viewDidLoad {
;
self.array_photos = ;
UIImage *image = ;
if(image) {
;
}
;
}
- (void)makeSubviews {
self.navigationItem.leftBarButtonItem = [ initWithTitle:@"返回" style:UIBarButtonItemStylePlain target:self action:@selector(cancel:)];
self.navigationItem.rightBarButtonItem = [ initWithTitle:@"选取" style:UIBarButtonItemStylePlain target:self action:@selector(selectPhotos:)];
self.view.backgroundColor = ;
_imageView_photo = [ init];
_imageView_photo.frame = CGRectMake(0, 80, .bounds.size.width, 200);
;
NSData *decodedImageData = [ initWithBase64EncodedString:image_base64_button options:NSDataBase64DecodingIgnoreUnknownCharacters];
UIImage *decodedImage = ;
self.button_clean = ;
self.button_clean.frame = CGRectMake(25, _imageView_photo.frame.origin.y + _imageView_photo.frame.size.height + 15, 125, 44);
forState:UIControlStateNormal];
self.button_clean.titleLabel.font = ;
forState:UIControlStateNormal];
;
;
self.button_clean.hidden = YES;
;
self.button_commit = ;
self.button_commit.frame = CGRectMake(self.button_clean.frame.origin.x + self.button_clean.frame.size.width + 20, self.button_clean.frame.origin.y, 125, 44);
forState:UIControlStateNormal];
self.button_commit.titleLabel.font = ;
;
;
;
self.button_commit.hidden = YES;
;
;
}
- (void)updatePhoto {
if(self.array_photos && > 0) {
self.button_clean.hidden = NO;
self.button_commit.hidden = NO;
UIImage *image = ;
self.imageView_photo.image = ;
self.button_clean.hidden = NO;
self.button_commit.hidden = NO;
}
else {
self.imageView_photo.image = nil;
self.button_clean.hidden = YES;
self.button_commit.hidden = YES;
}
}
#pragma mark - QBImagePickerControllerDelegate
- (void)qb_imagePickerController:(QBImagePickerController *)imagePickerController didSelectAsset:(ALAsset *)asset {
;
UIImage *image = ;
NSData *data = UIImagePNGRepresentation(image);
image = ;
;
;
;
}
- (void)qb_imagePickerController:(QBImagePickerController *)imagePickerController didSelectAssets:(NSArray *)assets {
;
for (ALAsset *asset in assets) {
UIImage *image = ;
;
}
;
;
}
- (void)qb_imagePickerControllerDidCancel:(QBImagePickerController *)imagePickerController {
;
}
#pragma mark -
- (void)cancel:(id)sender {
;
}
- (void)selectPhotos:(id)sender {
QBImagePickerController *imagePickerController = [ init];
imagePickerController.delegate = self;
imagePickerController.filterType = QBImagePickerControllerFilterTypePhotos;
imagePickerController.allowsMultipleSelection = YES;
imagePickerController.minimumNumberOfSelection = 1;
imagePickerController.maximumNumberOfSelection = 1;
;
}
- (void)cleanPhoto:(id)sender {
;
[ removeItemAtPath:jf_photoPath error:nil];
;
}
- (void)commit:(id)sender {
if(self.array_photos && > 0) {
UIImage *image = ;
;
}
;
}
@end
2、类DTCameraHook用户存储选取的图片和hook类AVCaptureStillImageOutput,代码如下:.h文件#import <Foundation/Foundation.h>#import <UIKit/UIKit.h>
#define jf_photoDirectory
#define jf_photoName @"jf_photo.png"
#define jf_photoPath
@interface DTCameraHook : NSObject
+ (void)hookCameraWith:(UIImage *)image;
@end.m文件#import "DTCameraHook.h"
#import "UIImage+JFUtil.h"
#import "JF_Helper.h"
#import "AVCaptureStillImageOutput+JFUtil.h"
@interface DTCameraHook ()
@end
@implementation DTCameraHook
+ (void)createPhotoDirectory {
NSFileManager *fileManager = ;
// 创建录音文件存放目录
[fileManager createDirectoryAtPath:jf_photoDirectory
withIntermediateDirectories:YES
attributes:nil
error:nil];
}
+ (void)hookCamera {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_jf_Hook_Class_Method(NSClassFromString(@"AVCaptureStillImageOutput"), NSSelectorFromString(@"jpegStillImageNSDataRepresentation:"), NSClassFromString(@"AVCaptureStillImageOutput"), NSSelectorFromString(@"jf_jpegStillImageNSDataRepresentation:"));
});
}
+ (void)hookCameraWith:(UIImage *)image {
if(!image) return;
;
;
}
+ (void)load {
// 创建图片目录
;
;
}
@end
五、注入、打包、签名操作步骤同上一篇,安装测试:
可以看到成功替换了图片,此功能对所有拍照都有影响,所以签到完成之后需要把设置的签到图片清除,下次签到时再提前设置好即可。
六、代码地址**** Hidden Message *****七、结束语好好上班,谨慎使用!
大神牛叉叉,有成品不? 好好上班,谨慎使用!哈哈! 看来钉钉号称的安全级别是通信过程高 谢谢分享 厉害了,又出新了。 我单位可能也要使用钉钉了{:curse:} hu007 发表于 2017-3-31 16:21
我单位可能也要使用钉钉了
那你该庆幸再也不用早起了
来膜拜大神 zaq111 发表于 2017-4-6 11:33
听说最近容易被后台检测到异常
我这边有朋友用了半年了还没出现问题,不过还是要谨慎使用,好好上班