L

Leo's NoteBook

持续学习之路

  • 首页
  • 列表
  • 关于
Home iOS安全尝试记录
文章

iOS安全尝试记录

Posted 2024-01-15 Updated 2024-01- 15
By Le0zh0u
26~33 min read

iOS安全尝试记录

防止IPA包被二次打包

Mach-O文件检测

通过检测SignerIdentity判断是Mach-O文件否被篡改。

原理是:SignerIdentity的值在info.plist中是不存在的,开发者不会加上去,苹果也不会,只是当ipa包被反编译后篡改文件再次打包,需要伪造SignerIdentity。所以只要被攻击篡改东西如果重新运行到手机上就会出现这个东西。

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

embedded.mobileprovision校验

embeded.mobileprovision文件会在上架appStore后被苹果删除,因为该文件中包含了证书相关信息,ipa包上传到appstore后,苹果会验证证书的合法性,验证通过后会删除该文件,并对ipa包进行重签后上架appstore,这样才能保证开发者自己的证书过期后依然可以通过appstore进行下载。

但是如果ipa包被越狱手机二次签名后,embeded.mobileprovision文件会重新生成,这时则可以监测到文件是否被篡改来判断是否被二次签名。

embedded文件的hash校验

通过上架app store下载的应用中,没有这个文件,不能通过这个方式校验

       if let embeddedPath = Bundle.main.path(forResource: "_CodeSignature/CodeResources", ofType: ""),
           let dic = NSMutableDictionary.init(contentsOfFile: embeddedPath),let files = dic["files"] as? [String:Any],let hashData = files["embedded.mobileprovision"] as? Data{
              let hashValue = hashData.base64EncodedString()
              let localHashValue = getHashValue()
              if hashValue != localHashValue{//文件被篡改,退出
                  Toast.showInfo(content: "检测到非法签名,程序即将退出",duration: 3)
                  DispatchQueue.main.asyncAfter(deadline: .now()+3) {
                    exit(0) // (1)
              }
            }
        }

embedded文件是否存在

如果存在embedded文件,意味着是二次打包

/// 检查是否二次签名
/// 
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 false
        }
        // 读取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 {
              // teamid一致,非二次签名
                return false
            } else {
                return true
            }
            
        }
		// 未检查到异常
        return false
    }

总结embedded.mobileprovision文件存在的情况:

不存在该文件:App Store下载的IPA、Cydia商店(越狱手机上的)下载的IPA。

存在该文件:Xcode打出来的IPA、企业证书分发的IPA、越狱手机上自己二次打包的IPA。

文件hash检测

通过对比文件的hash值,判断当前程序内的文件是否被篡改。

部分文章说,检测不同环境下不会变化的文件作为检测的文件。比如启动图、icon等,可以将hash值写在代码里,启动APP进行校验

但是我没有想到,为什么逆向工程的人员需要去改启动图。

越狱检测

通过检测是否存在越狱的文件、应用、是否可以打开越狱的app、是否可以直接访问其他文件等方式判断当前是否已经越狱

绝大部分app都没有对越狱进行校验

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
    }

反调试检测

通过检测调试的进程实现。发现pctrack调试进程,则结束程序

+ (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 {
    }
}

代码混淆

通过命令工具将静态代码做重命名混淆

代理检测 - 防止抓包

通过访问baidu,判断是否有MITM

+ (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;
    }
}

参考

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

  • iOS-App应用完整性校验:越狱检测、重签名检测,文件hash检测

  • 关于swift通过验证embedded.mobileprovision防止二次签名后上传APPStore被拒的记录

  • iOS安全防护之防重签名1.组织单位判断

  • iOS 防止ipa包重签名后二次打包

  • iOS安全与防护

  • iOS安全防护

  • iOS-Security - Hook 攻防

  • iOS逆向攻防实战

  • iOS证书签名机制&重签名&防止重签名

  • iOS安全防护---越狱检测、二次打包检测、反调试

  • iOS安全攻与防(总篇)

  • 对 iOS app 进行安全加固

  • iOS开发/App安全/设备是否越狱检测、ipa文件是否被篡改检测 <笔记>

  • iOS应用安全 - 完整性检测

  • ios重签名工具ios-app-signer的使用

  • ios手动重签名在非越狱手机上安装自己破解的app

  • iOS防重签名

    • 字符串替换

独立开发
iOS
License:  CC BY 4.0
Share

Further Reading

Jan 31, 2024

独立开发产品工具合集

独立开发产品工具合集 长期更新 官网 对于一个产品,需要一个站点让用户能通过网页了解产品,并提供合适的使用文档,方便用户快速上手 VuePress 现在的 tomemo.top 的构建框架,发布在 Github Page。 可以配置自定义域名 使用感受 中

Jan 15, 2024

iOS安全尝试记录

iOS安全尝试记录 防止IPA包被二次打包 Mach-O文件检测 通过检测SignerIdentity判断是Mach-O文件否被篡改。 原理是:SignerIdentity的值在info.plist中是不存在的,开发者不会加上去,苹果也不会,只是当ipa包被反编译后篡改文件再次打包,需要伪造Sign

OLDER

Memos 碎片笔记工具

NEWER

独立开发产品工具合集

Recently Updated

  • follow 认证
  • Memos碎片笔记工具(2)
  • 独立开发产品工具合集
  • iOS安全尝试记录
  • Memos 碎片笔记工具

Trending Tags

Halo iOS

Contents

©2025 Leo's NoteBook. Some rights reserved.

Using the Halo theme Chirpy