Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Reverse Engineering iOS apps. Lessons learned.

Reverse Engineering iOS apps. Lessons learned.

UIKonf, Berlin @ 2014

Max Bazaliy

May 15, 2014
Tweet

More Decks by Max Bazaliy

Other Decks in Programming

Transcript

  1. Reverse Engineering
    iOS apps
    UIKonf 2014
    Max Bazaliy

    View Slide

  2. @CocoaHeadsUA iSecurityKit
    @mbazaliy github.com/mbazaliy

    View Slide

  3. Security audit
    Competitor analysis
    Solution advantages
    Why?

    View Slide

  4. It’s fun!

    View Slide

  5. Analysis

    View Slide

  6. Traffic sniffing
    Module call tracing
    I/O activity
    System
    Code Disasm\ Decompiling
    Debugging
    Resource reversing

    View Slide

  7. View Slide

  8. Binary file
    Image files
    Interface files
    Property list files
    CoreData model files
    App
    files

    View Slide

  9. Compressed
    pngcrush
    appcrush.rb
    artwork extractor
    Image
    f iles

    View Slide

  10. NIBs
    Storyboards
    nib dec
    nib_patch
    Interface
    files

    View Slide

  11. *.mom
    momdec
    CoreData

    View Slide

  12. Binary

    View Slide

  13. otool
    class-dump
    MachOView
    Hopper
    cycript
    Reveal
    Tools

    View Slide

  14. Mach-O
    binary

    View Slide

  15. 32 bit (ARMv6,ARMv7)
    0xFEEDFACE
    64 bit (ARM64)
    0xFEEDFACF
    Universal binaries (FAT)
    0xCAFEBABE
    Mach-O
    header

    View Slide

  16. __TEXT -> code and read only data
    __objc sections-> data used by runtime

    View Slide

  17. __message_refs
    __cls_refs
    __symbols
    __module_info
    __class
    __meta_class
    __instance_vars
    __inst_meth
    __cls_meth
    __cat_cls_meth
    __protocol_ext
    __cat_inst_meth

    View Slide

  18. __message_refs
    __cls_refs
    __symbols
    __module_info
    __class
    __meta_class
    __instance_vars
    __inst_meth
    __cls_meth
    __cat_cls_meth
    __protocol_ext
    __cat_inst_meth

    View Slide

  19. class-dump-z

    View Slide

  20. @interface  RRSubscription  :  NSObject  
    {  
           NSString  *_subscriptionID;  
         unsigned  int  _period;  
           float  _price;  
           NSDate  *_creationDate;  
    }  
     
    +  (id)arrayOfSubscriptionsWithJSONArray:(id)arg1;  
    +  (id)subscriptionWithDictionary:(id)arg1;  
     
    @property(readonly,  nonatomic)  NSDate  *creationDate;  
    @property(readonly,  nonatomic)  float  price;  
    @property(readonly,  nonatomic)  unsigned  int  period;  

    View Slide

  21. Binary is encrypted

    View Slide

  22. otool -arch all –Vl MyApp | grep -A5 LC_ENCRYP!

    View Slide

  23. evasi0n.com

    View Slide

  24. > address (cryptoff + cryptsize) size (base address + cryptoff + cryptsize)!
    > gdb dump memory decrypted.bin 0x3000 0xD23000 !
    > Address space layout randomization!
    > 0x1000 -> 0x5000!
    > decrypted.bin -> binary!
    > patch header!

    View Slide

  25. View Slide

  26. Rasticrac
    Clutch
    dumpdecrypted

    View Slide

  27. Binary analysis
    Debugger attach
    ASLR bypass
    Binary dump
    Patch cryptid
    Clutch
    Rasticrac

    View Slide

  28. Binary
    analysis

    View Slide

  29. Disassembler
    Debugger
    Decompiler
    Hopper
    IDA
    Disassembler
    Debugger
    + objc_helper
    + Hex-Rays

    View Slide

  30. View Slide

  31. id objc_msgSend(id self, SEL op, ...)
    80% of calls

    View Slide

  32. application: didFinishLaunchingWithOptions:
    Hopper Disassembler

    View Slide

  33. Control flow graph
    Hopper Disassembler

    View Slide

  34. Decompilation
    Hopper Disassembler

    View Slide

  35. ! Method names
    Strings
    Constants

    View Slide

  36. Dump headers
    Modify ivars
    Instantiate objects
    Invoking methods
    Swizzling methods
    cycript

    View Slide

  37. cy# UIApp
    @""
    cy# function tryPrintIvars(a){
    var x={}; for(i in *a){ try{ x[i] = (*a)[i]; } catch(e){} } return x;}
    cy# UIApp.keyWindow.subviews[0].nextResponder.topViewController
    @""
    cy# UIApp.keyWindow.subviews[0].nextResponder.topViewController.
    viewControllers[0]
    @""
    cy# JailbreakDetectionVC.messages['isJailbroken'] = function ()
    { return NO };
    {}
    cy# [[[UIView alloc] init] autorelease]
    @"0x14d702b0>>"

    View Slide

  38. Runtime inspection
    Modify layer
    Dynamically loaded
    Reveal

    View Slide

  39. Foursquare.app

    View Slide

  40. idb
    iNalyzer
    Snoop-it
    Introspy
    iRET
    Special
    tools

    View Slide

  41. View Slide

  42. Best
    practices
    Compile with PIE
    No credentials in plists
    Disable NSLog
    Use NSFileProtection

    View Slide

  43. Best
    practices
    Sensitive - keychain
    View snapshots
    Cache.db
    URL Schemes
    Secure coding guide

    View Slide

  44. No Objective-C
    Integrity checks
    SSL pinning
    Obfuscation
    What
    next ?

    View Slide

  45. View Slide

  46. Public key
    Certificate
    SSL
    pinning

    View Slide

  47. - (void)URLSession:(NSURLSession *)session didReceiveChallenge:
    (NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)
    (NSURLSessionAuthChallengeDisposition, NSURLCredential
    *))completionHandler{!
    ""…!
    ""NSData *localCertificateData = [NSData dataWithContentsOfFile:
    [[NSBundle mainBundle] pathForResource: @"MyCert” ofType: @"crt"]];!
    "CFDataRef remoteCertificateData =
    SecCertificateCopyData(remoteVersionOfServerCertificate);!
    ""BOOL certificatesAreTheSame =!
    " [localCertificateData isEqualToData: remoteCertificateData];!
    ""NSURLCredential* cred = [NSURLCredential credentialForTrust:
    serverTrust];!
    ""if (certificatesAreTheSame) {!
    ""completionHandler(NSURLSessionAuthChallengeUseCredential,cred);
    " "}!
    ""else {
    "
    "
    completionHandler(NSURLSessionAuthChallengeRejectProtectionSpace,nil);!
    ""}!

    View Slide

  48. - (AFSecurityPolicy*) googleSecurityPolicy {!
    NSString *cerPath = [[NSBundle mainBundle] pathForResource:@"google"
    ofType:@"cer"];!
    NSData *certData = [NSData dataWithContentsOfFile:cerPath];!
    AFSecurityPolicy *securityPolicy = [[AFSecurityPolicy alloc] init];!
    [securityPolicy setAllowInvalidCertificates:NO];!
    [securityPolicy setPinnedCertificates:@[certData]];!
    [securityPolicy setSSLPinningMode:AFSSLPinningModeCertificate];!
    return securityPolicy; }!
    !
    - (void)googleRequest {!
    AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager
    manager]; !
    [manager setSecurityPolicy:[self googleSecurityPolicy]];!
    [manager GET:@"www.google.com" parameters:nil
    success:^(AFHTTPRequestOperation *operation, NSDictionary* responseObject) { !
    } failure:^(AFHTTPRequestOperation *operation, NSError *error) {!
    }];!
    }!

    View Slide

  49. Use functions
    Strip symbols
    Use #define
    inline
    ((always_inline))
    Method
    obfuscation

    View Slide

  50. #define isJailbroken() gbrlp()!
    static inline int () gbrlp{!
    …!
    }!

    View Slide

  51. XORs
    Decoding tables
    Don’t use one key
    Strings
    obfuscation

    View Slide

  52. !
    !
    #define PTRACE_STRING @"NSString *scInfoString = decodeString(PTRACE_STRING);!
    !
    !
    NSData *encryptedData =!
    [RNEncryptor encryptData:data
    " " " " withSettings:kRNCryptorAES256Settings
    " " " " " password:@"passw0rd”
    " " " " " " error:&error];!
    !
    NSData *decryptedData =!
    [RNDecryptor decryptData:data
    " " " " withSettings:kRNCryptorAES256Settings
    " " " " " password:@"passw0rd”
    " " " " " " error:&error];!
    !

    View Slide

  53. Deny attach
    Constructor tricks
    Change values
    Anti
    debugger
    tricks

    View Slide

  54. static int checkGDB() __attribute__((always_inline))!
    {!
    size_t size = sizeof(struct kinfo_proc);!
    struct kinfo_proc info;!
    memset(&info, 0, sizeof(struct kinfo_proc));!
    !
    int ret, name[4];!
    name[0] = CTL_KERN;!
    name[1] = KERN_PROC;!
    name[2] = KERN_PROC_PID;!
    name[3] = getpid();!
    !
    if ((ret = (sysctl(name, 4, &info, &size, NULL, 0))))!
    return ret;!
    return (info.kp_proc.p_flag & P_TRACED) ? 1 : 0;!
    }!

    View Slide

  55. #import !
    #import !
    !
    #define PT_DENY_ATTACH 31!
    !
    typedef int (*ptrace_ptr_t)!
    (int _request, pid_t _pid, caddr_t _addr, int _data);!
    !
    void *handle = dlopen(0, RTLD_GLOBAL | RTLD_NOW);!
    ptrace_ptr_t ptrace_ptr = (ptrace_ptr_t)dlsym(handle,
    [ptraceString UTF8String]);!
    ptrace_ptr(PT_DENY_ATTACH, 0, 0, 0);!
    dlclose(handle);!
    !

    View Slide

  56. SYSCALL

    View Slide

  57. syscall(26, 31, 0, 0, 0);
    !
    ptrace
    PT_DENY_ATTACH

    View Slide

  58. + (PurchaseManager *)sharedManager {!
    !
    if (isDebugged())!
    return nil;!
    !
    static PurchaseManager *sharedPurchaseManager = nil;!
    static dispatch_once_t onceToken;!
    " dispatch_once(&onceToken, ^{!
    sharedPurchaseManager = [[self alloc] init];!
    });!
    "!
    return sharedPurchaseManager ;!
    }!

    View Slide

  59. Is encrypted
    Is patched
    SC_Info
    iTunesMetadata
    overdrive tricks
    Integrity
    checks

    View Slide

  60. View Slide

  61. const struct mach_header *header =!
    (struct mach_header *)dlinfo.dli_fbase;!
    struct load_command *cmd = (struct load_command *) (header + 1);!
    for (uint32_t i = 0; cmd != NULL && i < header->ncmds; i++) {!
    if (cmd->cmd == LC_ENCRYPTION_INFO) {!
    struct encryption_info_command *crypt_cmd =!
    "" " " (struct encryption_info_command *)cmd;!
    if (crypt_cmd->cryptid < 1)!
    return NO;!
    else!
    return YES;!
    }!

    View Slide

  62. const char * originalSignature = "5f9b18edc3666be3de79134a40deea5b";!
    const struct mach_header * header;!
    Dl_info dlinfo;!
    !
    uint32_t * textSectionAddr = (uint32_t *)section->addr;!
    uint32_t textSectionSize = section->size;!
    uint32_t * vmaddr = &segment->vmaddr;!
    !
    char * textSectionPtr = (char *)((int)header + (int)textSectionAddr -
    " " " " " " " " " " " " " " " "(int)vmaddr);!
    !
    unsigned char digest[CC_MD5_DIGEST_LENGTH];!
    char signature[2 * CC_MD5_DIGEST_LENGTH];!
    CC_MD5(textSectionPtr, textSectionSize, digest);!
    !
    for (int i = 0; i < sizeof(digest); i++)!
    "" "sprintf(signature + (2 * i), "%02x", digest[i]);!
    return strcmp(originalSignature, signature) == 0;!

    View Slide

  63. BOOL isDirectory = NO;!
    !
    NSString *directoryPath = [[[NSBundle mainBundle]
    bundlePath] stringByAppendingPathComponent:@"SC_Info/"];!
    !
    BOOL directoryExists = [[NSFileManager defaultManager]
    fileExistsAtPath:directoryPath isDirectory:&isDirectory];!
    !
    BOOL contentSeemsValid = ([[[NSFileManager defaultManager]
    contentsOfDirectoryAtPath:directoryPath error:NULL]
    count] == 2);

    View Slide

  64. NSString *scInfoString = @"SC_Info/";!
    NSString *appleIDString = @"appleId";!
    NSString *appleIDMailAddress = @"[email protected]";!
    NSString *metadataString = @"iTunesMetadata.plist";!
    NSString *downloadInfoKeyString = @"com.apple.iTunesStore.downloadInfo";!
    NSString *accountInfoString = @"accountInfo";!
    !
    NSDictionary *iTunesMetadata = [NSDictionary
    dictionaryWithContentsOfFile:[rootDirectoryPath
    stringByAppendingPathComponent:metadataString]];!
    NSString *appleID = [iTunesMetadata objectForKey:appleIDString];!
    NSDictionary *accountInfo = [[iTunesMetadata
    objectForKey:downloadInfoKeyString]
    objectForKey:accountInfoString];!
    BOOL isValidAppleID = (appleID.length > 0 && [appleID
    rangeOfString:appleIDMailAddress
    options:NSCaseInsensitiveSearch].location == NSNotFound);!
    BOOL isValidDownloadInfo = (accountInfo.count > 0);}!

    View Slide

  65. View Slide

  66. BOOL dyLibFound = NO;!
    NSArray *directoryFiles = [[NSFileManager
    defaultManager] contentsOfDirectoryAtPath:
    [[NSBundle mainBundle] bundlePath] error:NULL];!
    !
    for (NSString *filename in directoryFiles) {!
    if ([[filename pathExtension]
    caseInsensitiveCompare:@"dylib"] ==
    NSOrderedSame) {!
    dyLibFound = YES;!
    break;!
    }!
    }!

    View Slide

  67. Class hooksClass = objc_getClass("hooks");!
    Class descriptorsClass = objc_getClass("descriptors");!
    SEL allocWithZoneSelector = sel_registerName("allocWithZone:");!
    !
    if (hooksClass != NULL) {!
    "Method method = !
    " class_getClassMethod(hooksClass, allocWithZoneSelector);!
    " method_setImplementation(method, (IMP)nilImplementation);!
    }!
    !
    if (descriptorsClass != NULL) {!
    Method method = !
    class_getClassMethod(descriptorsClass, allocWithZoneSelector);!
    method_setImplementation(method, (IMP)nilImplementation);!
    }!

    View Slide

  68. Terminate app
    Run in demo mode
    Change behavior
    What next?

    View Slide

  69. View Slide

  70. Path check
    URL check
    File access
    Root check
    Process check
    Jailbreak
    detection

    View Slide

  71. !
    NSError *error;!
    NSString *jailTest = @"Jailbreak time!";!
    [jailTest writeToFile:@"/private/
    test_jail.txt" atomically:YES
    encoding:NSUTF8StringEncoding
    error:&error];!
    if(error==nil) {!
    …!
    }!

    View Slide

  72. int result = fork();!
    "if (!result)!
    exit(0); !
    if (result >= 0)!
    return isJail;!
    return noJail;!
    !
    !
    if (system(0)) !
    ...!
    }!

    View Slide

  73. NSURL *FakeURL = [NSURL URLWithString:!
    @"cydia://package/com.fake.package"];!
    !
    if ([[UIApplication sharedApplication]
    canOpenURL:FakeURL])!
    return isJail;!
    else!
    return noJail;

    View Slide

  74. NSArray *jailbrokenPaths = @[@"/Applications/Cydia.app",!
    @"/Applications/RockApp.app",!
    @"/Applications/Icy.app",!
    @"/usr/sbin/sshd",!
    @"/usr/bin/sshd",!
    @"/private/var/lib/apt",!
    @"/private/var/lib/cydia",!
    @"/usr/libexec/sftp-server”,!
    @"/private/var/stash"];!
    !
    for (NSString *string in jailbrokenPaths)!
    if ([[NSFileManager defaultManager]
    " " " " "fileExistsAtPath:string]) {!
    …!
    }!

    View Slide

  75. !
    !
    NSArray *processes = [self runningProcesses];!
    !
    for (NSDictionary * dict in processes) {!
    NSString *process = dict[@"ProcessName"];!
    if ([process isEqualToString:@"MobileCydia"])
    " " "{!
    ...!
    }!
    !

    View Slide

  76. iMAS
    Encrypted Core Data
    Security checks
    Passcode check
    Memory security

    View Slide

  77. LLVM
    Obfuscator
    Instructions substitution
    Control Flow flattening
    Bogus Control Flow
    Functions merging

    View Slide

  78. LLVM
    Obfuscator
    Instructions substitution
    Control Flow flattening
    Bogus Control Flow
    Functions merging

    View Slide

  79. Cracking time
    =
    Protection time

    View Slide

  80. View Slide

  81. @mbazaliy

    View Slide