最近研究了基于LLVM的混淆工具 Hikari 卿拴、中文文檔 衫仑,從編譯器層面完成了代碼的安全加固,可以說(shuō)是非常牛了堕花。但作者并沒(méi)有實(shí)現(xiàn)Objective-C的方法名/類(lèi)名混淆文狱,于是想到了老辦法。這個(gè)辦法最大的難點(diǎn)在于如何提取混淆的關(guān)鍵字缘挽,之前都是嘗試用腳本去提取關(guān)鍵字瞄崇,方法名加前綴等等,github有不少工具壕曼、Demo苏研,大多都不太好使。后來(lái)經(jīng)大神提點(diǎn)腮郊,可以用Objective-C強(qiáng)大的運(yùn)行時(shí)來(lái)處理關(guān)鍵字的提取摹蘑。
周末抽空寫(xiě)了個(gè)小工具 XDSecurityDefense,實(shí)現(xiàn)了
- 過(guò)濾掉蘋(píng)果SDK的類(lèi)名轧飞、方法名
- 過(guò)濾掉setter衅鹿、getter方法
- 過(guò)濾掉協(xié)議方法
- 過(guò)濾掉繼承自父類(lèi)的方法
/**
獲取所有開(kāi)發(fā)者創(chuàng)建的類(lèi)的名稱(chēng)
@return 類(lèi)的名稱(chēng)集合
*/
- (NSSet *)customClassNames {
NSMutableSet *customClassName = [NSMutableSet set];
unsigned int classNamesCount = 0;
// 用 executablePath 獲取當(dāng)前 app image
NSString *appImage = [NSBundle mainBundle].executablePath;
// objc_copyClassNamesForImage 獲取到的是 image 下的類(lèi)撒踪,直接排除了系統(tǒng)的類(lèi)
const char **classNames = objc_copyClassNamesForImage([appImage UTF8String], &classNamesCount);
if (classNames) {
for (unsigned int i = 0; i < classNamesCount; i++) {
const char *className = classNames[i];
NSString *classNameString = [NSString stringWithUTF8String:className];
[customClassName addObject:classNameString];
}
free(classNames);
}
return customClassName;
}
/**
檢查方法是否繼承自父類(lèi)
@param class 類(lèi)
@param sel 方法名
@return 是否繼承自父類(lèi)
*/
- (BOOL)superClass:(Class)class respondsToSelector:(SEL)sel
{
Class supClass = class_getSuperclass(class);
BOOL bTespondsToSelector= NO;
while (supClass != nil) {
if (class_respondsToSelector(supClass,sel)) {
bTespondsToSelector = YES;
supClass = nil;
} else {
supClass = class_getSuperclass(supClass);
}
}
return bTespondsToSelector;
}
/**
獲取類(lèi)遵循所有協(xié)議的協(xié)議方法名
@param classNameString 類(lèi)的集合
@return 方法名集合
*/
- (NSSet *)protocalMethodListWithClass:(NSString *)classNameString {
NSMutableSet *protocalMethodList = [NSMutableSet set];
Class className = NSClassFromString(classNameString);
unsigned int methodCount = 0;
__unsafe_unretained Protocol **protocolList = class_copyProtocolList(className, &methodCount);
for (int i = 0; i < methodCount; i++) {
Protocol *protocal = protocolList[i];
// const char *pName = protocol_getName(protocal);
// NSLog(@"protocol[%d] ---- %@", i, [NSString stringWithUTF8String:pName]);
unsigned int protocolMethodCount = 0;
struct objc_method_description * methodList = protocol_copyMethodDescriptionList(protocal, NO, YES, &protocolMethodCount);
for (int i = 0; i < protocolMethodCount; i++) {
struct objc_method_description method = methodList[i];
NSString *protocolMethodName = NSStringFromSelector(method.name);
[protocalMethodList addObject:protocolMethodName];
}
free(methodList);
methodList = protocol_copyMethodDescriptionList(protocal, YES, YES, &protocolMethodCount);
for (int i = 0; i < protocolMethodCount; i++) {
struct objc_method_description method = methodList[i];
NSString *protocolMethodName = NSStringFromSelector(method.name);
[protocalMethodList addObject:protocolMethodName];
}
}
free(protocolList);
return protocalMethodList;
}
使用方法 不再贅述
踩的一些坑
1.如果有用到 NSClassFromString
NSSelectorFromString
等方法要檢查是否會(huì)出錯(cuò)
2.有使用NSCoding
、archivedDataWithRootObject
等歸檔操作也要注意運(yùn)行時(shí)是否出錯(cuò)