iOS开发安全 - 防重签名、越狱、调试检测

一、判断Mach-O文件否被篡改

二次签名会篡改信息文件

public static func isSecondIPA() -> Bool {
        let bundle = Bundle.main
        let info = bundle.infoDictionary
        if info?["SignerIdentity"] != nil {
            return true
        }
        return false
    }

二、重签名检测

检测包签名是否和已知自己签名一致

public static func checkCodeSign(_ provisionID: String) -> Bool {
        // 描述文件路径
        let embededPath = Bundle.main.path(forResource: "embedded", ofType: "mobileprovision")
        guard let embededPath = embededPath,
              FileManager.default.fileExists(atPath: embededPath) == true
        else {
            return true
        }
        // 读取application-identifier
        guard let embeddedProvisioning = try? String.init(contentsOfFile: embededPath, encoding: .ascii) else {
            return true
        }
        let embeddedProvisioningLines = embeddedProvisioning.components(separatedBy: CharacterSet.newlines)
        for i in 0..<embeddedProvisioningLines.count {
            let emStr = embeddedProvisioningLines[i]
            guard let index = emStr.range(of: "application-identifier")?.lowerBound,
                  index.hashValue != NSNotFound
            else {
                continue
            }
            let positionStr = embeddedProvisioningLines[i + 1]
            let fromPosition = positionStr.range(of: "<string>")?.upperBound ?? positionStr.startIndex
            let toPosition = positionStr.range(of: "</string>")?.lowerBound ?? positionStr.endIndex
            
            let range = Range(uncheckedBounds: (fromPosition, toPosition))
            
            let fullIdentifier = positionStr[range]
            let identifierComponents = fullIdentifier.components(separatedBy: ".")
            let appIdentifier = identifierComponents.first
            // 对比签名ID
            if appIdentifier == provisionID {
                return false
            } else {
                return true
            }
            
        }
        return false
    }

三、是否越狱

public static func isJailbreak() -> Bool {
        guard TARGET_OS_SIMULATOR == 0 else {
            return false
        }
        // 越狱检查
        // CanOpenURL检查
        let fakeURL = URL(string: "cydia://package/com.fake.package")
        if UIApplication.shared.canOpenURL(fakeURL!) == true {
            return true
        }
        
        // Cydia应用检查
        var stat_info: stat
        // 使用stat系列函数检测Cydia等工具
        if stat("/Applications/Cydia.app", &stat_info) == 0 {
            return true
        }
        
        // fork函数检查
//        if fork()>=0 {
//            return true
//        }
        
        // 注入动态库检查
        if let env = getenv("DYLD_INSERT_LIBRARIES") {
            return true
        }
        
        // 无法访问文件检查
        let files = ["/Applications/Cydia.app",
                     "/Applications/limera1n.app",
                     "/Applications/greenpois0n.app",
                     "/Applications/blackra1n.app",
                     "/Applications/blacksn0w.app",
                     "/Applications/redsn0w.app",
                     "/Applications/Absinthe.app",
                     "/Library/MobileSubstrate/MobileSubstrate.dylib",
                     "/bin/bash",
                     "/usr/sbin/sshd",
                     "/etc/apt",
                     "/private/var/lib/apt/"]
        for fileName in files {
            if FileManager.default.fileExists(atPath: fileName) {
                return true
            }
        }
        return false
    }

四、防止抓包

发起请求之前判断是否存在代理,存在代理就直接返回,请求失败

+ (BOOL)getProxyStatus {
    NSDictionary *proxySettings =  (__bridge NSDictionary *)(CFNetworkCopySystemProxySettings());
    NSArray *proxies = (__bridge NSArray *)(CFNetworkCopyProxiesForURL((__bridge CFURLRef _Nonnull)([NSURL URLWithString:@"http://www.baidu.com"]), (__bridge CFDictionaryRef _Nonnull)(proxySettings)));
    NSDictionary *settings = [proxies objectAtIndex:0];
    
    NSLog(@"host=%@", [settings objectForKey:(NSString *)kCFProxyHostNameKey]);
    NSLog(@"port=%@", [settings objectForKey:(NSString *)kCFProxyPortNumberKey]);
    NSLog(@"type=%@", [settings objectForKey:(NSString *)kCFProxyTypeKey]);
    
    if ([[settings objectForKey:(NSString *)kCFProxyTypeKey] isEqualToString:@"kCFProxyTypeNone"]){
        //没有设置代理
        return NO;
    }else{
        //设置代理了
        return YES;
    }
}

五、防调试

+ (void)antiDebug {
    
    gx_disable_gdb();

    gx_AntiDebugASM();

    gx_AntiDebug_isatty();
    
}

/// ptrace反调试,阻止GDB依附
typedef int (*ptrace_ptr_t)(int _request, pid_t pid, caddr_t _addr, int _data);
#if !defined(PT_DENT_ATTACH)
#define PT_DENT_ATTACH 31
#endif
void gx_disable_gdb() {
    void * handle = dlopen(0, RTLD_GLOBAL|RTLD_NOW);
    ptrace_ptr_t ptrace_ptr = dlsym(handle, "ptrace");
    ptrace_ptr(PT_DENT_ATTACH, 0, 0, 0);
    dlclose(handle);
}


/// 内联 svc + ptrace 实现和内联 svc + syscall + ptrace 实现
// 使用inline方式将函数在调用处强制展开,防止被hook和追踪符号
__attribute__((always_inline)) void gx_AntiDebugASM() {
#ifdef __arm__
    asm volatile(
                 "mov r0,#31\n"
                 "mov r1,#0\n"
                 "mov r2,#0\n"
                 "mov r12,#26\n"
                 "svc #80\n"
                 );
#endif
#ifdef __arm64__
    asm volatile(
                 "mov x0,#26\n"
                 "mov x1,#31\n"
                 "mov x2,#0\n"
                 "mov x3,#0\n"
                 "mov x16,#0\n"
                 "svc #128\n"
                 );
#endif
}

/// isatty检测是否正在被调试
//需要头文件#include <unistd.h>
void gx_AntiDebug_isatty() {
    if (isatty(1)) {
        _exit(1);
    } else {
    }
}

猜你喜欢

转载自blog.csdn.net/guoxulieying/article/details/131787414