先看看安全键盘静态库的目录结构
MPSafeKeyboard.framework
使用上和其他公司推出的安全键盘相比。MPSafeKeyboard仅仅是一个键盘,其他公司的则是一个UITextField和键盘绑定在一起的。
在MPSafeKeyboardThemeManager这个类中可以自定义一些键盘的样式颜色,其他键盘则没有。
//
// MPSafeKeyboardThemeManager.h
// MPSafeKeyboard
//
// Created by wenzhao on 2018/10/12.
// Copyright © 2018 Alipay. All rights reserved.
//
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
#define MPSafeKeyboardThemeManagerInstance [MPSafeKeyboardThemeManager sharedInstace]
@interface MPSafeKeyboardThemeManager : NSObject
// key 颜色
@property (strong, nonatomic) UIColor *keyTextColor;
// 普通 key 背景色
@property (strong, nonatomic) UIColor *keyNormalBgColor;
// 功能 key 背景色
@property (strong, nonatomic) UIColor *keySpecialBgColor;
// ASCII 键盘普通 key 背景图片
@property (strong, nonatomic) UIImage *keyASCIIBGImage;
// ASCII 键盘特殊 key 背景图片
@property (strong, nonatomic) UIImage *keyASCIISpecialBGImage;
// 数字键盘分割线颜色
@property (strong, nonatomic) UIColor *keySplitLineColor;
// 键盘上方 bar 的背景色
@property (strong, nonatomic) UIColor *keyboardBarBgColor;
// 键盘背景色
@property (strong, nonatomic) UIColor *keyboardBgColor;
// 键盘上方 bar 完成按钮文字颜色
@property (strong, nonatomic) UIColor *keyboardBarDoneColor;
// 键盘上方 bar Logo 文字颜色
@property (strong, nonatomic) UIColor *keyboardBarLogoColor;
// 键盘上方 bar logo icon
@property (strong, nonatomic) UIImage *keyboardBarLogoIcon;
// 键盘上方 bar logo 文案
@property (strong, nonatomic) NSString *keyboardBarLogoText;
// 键盘上方 bar 完成按钮文案
@property (strong, nonatomic) NSString *keyboardBarDoneText;
// 重置为默认配置
- (void)useDefault;
+ (instancetype)sharedInstace;
@end
NS_ASSUME_NONNULL_END
然后开始正文
更换bundleid后打包发现安全键盘输入框弹出了一个普通的系统键盘,bundleid恢复后没有问题。问题一定是在获取bundleid的地方。
获取bundleid有三个办法:
1.NSBundle的bundleIdentifier方法
2.直接读取Info.plist这个文件
3.NSBundle的infoDictionary方法
验证:
1.替换bundleIdentifier
#import "NSBundle+yyy.h"
#import <objc/runtime.h>
@implementation NSBundle (yyy)
+ (void)load{
Method originalMethod = class_getInstanceMethod([NSBundle class], @selector(bundleIdentifierS));
Method swizzledMethod = class_getInstanceMethod([NSBundle class], @selector(bundleIdentifier));
method_exchangeImplementations(originalMethod, swizzledMethod);
}
}
- (NSString *)bundleIdentifierS{
return @"没问题的bundleid";
}
一般情况下,这样改就行,但是发现更换后无效果
2.替换Info.plist这个文件
先创建一个InfoYYY.plist,只将其中的唯一标识改一下
int main(int argc, char * argv[]) {
。。。其他原来代码。。。
rebind_symbols((struct rebinding[2]){{"close", my_close, (void *)&orig_close}, {"open", my_open, (void *)&orig_open}}, 2);
// Open our own binary and print out first 4 bytes (which is the same
// for all Mach-O binaries on a given architecture)
int fd = open(argv[0], O_RDONLY);
uint32_t magic_number = 0;
read(fd, &magic_number, 4);
printf("Mach-O Magic Number: %x \n", magic_number);
close(fd);
。。。其他原来代码。。。
}
int my_open(const char *path, int oflag, ...) {
va_list ap = {0};
mode_t mode = 0;
if ((oflag & O_CREAT) != 0) {
// mode only applies to O_CREAT
va_start(ap, oflag);
mode = va_arg(ap, int);
va_end(ap);
printf("Calling real open('%s', %d, %d)\n", path, oflag, mode);
return orig_open(path, oflag, mode);
} else {
NSString *pathOri = [[NSString alloc]initWithUTF8String:path];
if([pathOri rangeOfString:@"XXX.app/Info.plist"].length > 0){
path = [[pathOri stringByReplacingOccurrencesOfString:@"Info.plist" withString:@"InfoYYY.plist"] UTF8String];
printf("Calling real open('%s', %d)\n", path, oflag);
}
return orig_open(path, oflag, mode);
}
}
发现这个办法也不行
3.交换infoDictionary方法
#import "NSBundle+yyy.h"
#import <objc/runtime.h>
@implementation NSBundle (yyy)
+ (void)load{
{
Method originalMethod = class_getInstanceMethod([NSBundle class], @selector(infoDictionaryS));
Method swizzledMethod = class_getInstanceMethod([NSBundle class], @selector(infoDictionary));
method_exchangeImplementations(originalMethod, swizzledMethod);
}
}
- (NSDictionary*)infoDictionaryS{
NSMutableDictionary *dicc = [self infoDictionaryS].mutableCopy;
[dicc setObject:@"正式的" forKey:@"CFBundleIdentifier"];
return dicc;
}
这个方法有用。看来就是这个方法。
然后用模拟器跑一下,将有nil值的地方改一下,能够正常登录,但是界面图片展示不正常,因为系统自己也调用了这个方法。
蚂蚁框架里面调用的也不少,还需要继续查看是调用的哪个。
蚂蚁框架里面调用的比如:
-[APLogAdditions clientVersion]:
-[DTSyncInterface productVersion]:
+[APSyncUtils productID]:
-[DynamicReleaseInterface productId]:
-[MPImageBaker checkAppVersionChanged]:
这些都不用管
在创建安全键盘的时候 是一个c的方法verifyLicense调用的infodic方法。先不对其hook。
直接在安全键盘创建前后添加全局变量useHookBundleid 来控制何时替换bundleid.
全局变量声明
@property (nonatomic,assign)BOOL useHookBundleid;
infoDictionaryS修改
- (NSDictionary*)infoDictionaryS{
NSMutableDictionary *dicc = [self infoDictionaryS].mutableCopy;
if ([Global sharedD].useHookBundleid == YES) {
[dicc setObject:@"正式的" forKey:@"CFBundleIdentifier"];
}
return dicc;
}
安全键盘创建的前后设置开关
[Global sharedData].useHookBundleid = YES;
[MPEncrySafeKeyboard keyboardWithType:MPSafeKeyboardTypeASCIICapable encryType:MPEncrySafeKeyboardEcryTypeSM2
publicKey:sm2PubKey
resultBlock:^(NSString *result, NSUInteger inputAmount, BOOL isConfirmed) {
}];
[Global sharedData].useHookBundleid = NO;
这样既保证其他功能正常使用,也可以正常弹出键盘。
20191108补充
但是还有一个问题 多少个地方使用了安全键盘 就得在多少个地方添加
代码很分散,所以
一劳永逸就是 hook创建键盘方法,加上判断逻辑,只改一个地方即可
//
// MPEncrySafeKeyboard+YYY.h
//
//
// Created by YYY on 2019/11/8.
// Copyright © 2019 Alibaba. All rights reserved.
//
#import <MPSafeKeyboard/MPSafeKeyboard.h>
NS_ASSUME_NONNULL_BEGIN
@interface MPEncrySafeKeyboard (YYY)
@end
NS_ASSUME_NONNULL_END
//
// MPEncrySafeKeyboard+YYY.m
//
//
// Created by YYY on 2019/11/8.
// Copyright © 2019 Alibaba. All rights reserved.
//
#import "MPEncrySafeKeyboard+YYY.h"
#import <objc/runtime.h>
@implementation MPEncrySafeKeyboard (YYY)
+ (void)load{
Method originalMethod = class_getClassMethod([self class], @selector(keyboardWithType:encryType:publicKey:resultBlock:));
Method swizzledMethod = class_getClassMethod([self class], @selector(keyboardWithType:encryType:publicKey:resultBlockS:));
method_exchangeImplementations(originalMethod, swizzledMethod);
}
+ (instancetype)keyboardWithType:(MPSafeKeyboardType)keyboardType
encryType:(MPEncrySafeKeyboardEcryType)encryType
publicKey:(nullable NSString *)publicKey
resultBlockS:(MPEncrySafeKeyboardResultBlock)resultBlock;{
[Global sharedData].useHookBundleid = YES;
id ss = [self keyboardWithType:keyboardType encryType:encryType publicKey:publicKey resultBlockS:resultBlock];
[Global sharedData].useHookBundleid = NO;
return ss;
}
@end
😊😊😊