iOS代码防护,反调试,反注入,防打包,日志处理

第一:反调试,修改main.m

#import <dlfcn.h>
#import <sys/types.h>

//定义一个函数指针用来接收动态加载出来的函数ptrace
typedef int (*ptrace_ptr_t)(int _request, pid_t _pid, caddr_t _addr, int _data);

#if !defined(PT_DENY_ATTACH)
#define PT_DENY_ATTACH 31
#endif

// 阻止调试器attach
void disable_gdb() {
    //第一个参数path为0时, 它会自动查找 $LD_LIBRARY_PATH,$DYLD_LIBRARY_PATH, $DYLD_FALLBACK_LIBRARY_PATH 和 当前工作目录中的动态链接库.
    void * handle = dlopen(0, RTLD_GLOBAL | RTLD_NOW);
    //动态加载ptrace函数,ptrace函数的参数个数和类型,及返回类型跟ptrace_ptr_t函数指针定义的是一样的
    ptrace_ptr_t ptrace_ptr = dlsym(handle, "ptrace");
    //执行ptrace_ptr相当于执行ptrace函数
    ptrace_ptr(PT_DENY_ATTACH, 0, 0, 0);
    //关闭动态库,并且卸载
    dlclose(handle);
}

int main(int argc, char * argv[]) {
    @autoreleasepool {
        #ifndef DEBUG
                disable_gdb();
        #endif
        signal(SIGPIPE, SIG_IGN);
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
    }
}

第二:反注入,定时查看(不能在开始只检查一次)。原理为lib库白名单检查

//代码防注入
bool HKCheckWhitelist(){
    int count = _dyld_image_count();
    NSArray * arr = @[@"libstdc++.tbd",...,@"libViewDebuggerSupport.dylib"];
    for (int i = 0; i < count; i++) {
        //遍历拿到库名称!
        const char * imageName = _dyld_get_image_name(i);
        
        //系统的lib Debug库 除外
        if (strstr(imageName, "/System/Library/Frameworks/") || strstr(imageName, "/Developer/")  ) {
            continue;
        }

        if (strstr(imageName, "libAppProxy") || strstr(imageName, "PacketTunnel")) {
            continue;
        }
        
        NSString *str= [NSString stringWithCString:imageName encoding:[NSString defaultCStringEncoding]];
        str = [str lastPathComponent];
        //应用本身除外
        if ([str isEqualToString:@"workspace"] ) {
            continue;
        }
        
        BOOL isMyLib = NO;
        for (NSString * libName in arr) {
            if ([libName containsString:str]) {
                isMyLib = YES;
            }
        }
        if (isMyLib == NO) {
            printf("lib已被修改==!!%s",imageName);
            TLogD(@"lib已被修改==!!%s",imageName);
            exit(0);
            return NO;
        }else{
            return YES;
        }
    }
    return YES;
}

第三:防止重签打包的简单验证,原理为读出打包的证书ID,然后和预埋的比较

+ (void)prepareEnv {
    NSString *embeddedPath = [[NSBundle mainBundle] pathForResource:@"embedded" ofType:@"mobileprovision"];
    if ([[NSFileManager defaultManager] fileExistsAtPath:embeddedPath]) {
        NSString *embeddedProvisioning = [NSString stringWithContentsOfFile:embeddedPath encoding:NSASCIIStringEncoding error:nil];
        NSArray *embeddedProvisioningLines = [embeddedProvisioning componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]];

        for (int i = 0; i < [embeddedProvisioningLines count]; i++) {
            if ([[embeddedProvisioningLines objectAtIndex:i] rangeOfString:@"application-identifier"].location != NSNotFound) {
                NSInteger fromPosition = [[embeddedProvisioningLines objectAtIndex:i+1] rangeOfString:@"<string>"].location+8;
                NSInteger toPosition = [[embeddedProvisioningLines objectAtIndex:i+1] rangeOfString:@"</string>"].location;

                NSRange range;
                range.location = fromPosition;
                range.length = toPosition - fromPosition;
                
                NSString *fullIdentifier = [[embeddedProvisioningLines objectAtIndex:i+1] substringWithRange:range];
                
                NSArray *identifierComponents = [fullIdentifier componentsSeparatedByString:@"."];
                NSString *appIdentifier = [identifierComponents firstObject];

                // 对比签名ID
                if ([appIdentifier caseInsensitiveCompare:@“*******”] != NSOrderedSame) {
                    exit(0);
                }
                break;
            }
        }
    }
}

第四:日志敏感信息。原理,利用fishhook,hook系统方法“NSLog”,进行过滤处理

#import "UIApplication+NSLog.h"
#import "fishhook.h"

@implementation UIApplication (NSLog)
+ (void)load {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        //定义rebinding结构体
        /*
         struct rebinding {
         const char *name;//需要HOOK的函数名称,字符串
         void *replacement;//替换到那个新的函数上(函数指针,也就是函数的名称)
         void **replaced;//保存原始函数指针变量的指针(它是一个二级指针!)
         };
         */
        rebind_symbols((struct rebinding[1]){{"NSLog", new_NSLog, (void *)&orig_NSLog}}, 1);
    });
}


// orig_NSLog是原有方法被替换后 把原来的实现方法放到另一个地址中
// new_NSLog就是替换后的方法了
static void (*orig_NSLog)(NSString *format, ...);
void(new_NSLog)(NSString *format, ...) {
    va_list args;
    if(format) {
        va_start(args, format);
        NSString *message = [[NSString alloc] initWithFormat:format arguments:args];
        DDLogInfo(@"Hook:%@", message); //使用DDLog保存日志到本地
        #ifndef DEBUG
             orig_NSLog(@"===%@", message);//debug状态下打印日志方便查看
        #endif
        
        va_end(args);
    }
}

@end