How to bypass the Jailbreak Detection – Method Swizzling

บทนำ (Overviews)

การตรวจสอบการ “Jailbreak” ของเครื่อง “iOS” ก็มีหลายวิธีเช่น ตรวจสอบการติดตั้ง “Cydia” หรือตรวจสอบการมีอยู่ของไฟล์ “MobileSubstate.dylib” หรือการลองเขียน “File” ลงในโฟลเดอร์ “Application” เป็นต้น อย่างไรก็ตามเราก็สามารถข้ามการทำ “Jailbreak Detection” ได้โดยใช้กระบวนการ “Method Swizzling”



ขั้นตอน (Steps)

  1.  เครื่อง “iOS” ต้องผ่านการ Jailbreak เสียก่อน (How to jailbreak iOS – “PP Jailbreak”)
  2. ติดตั้งโปรแกรม “DVIA” จากนั้นไปที่รายการ “Jailbreak Detection” และเลือก “Jailbreak Test 1” jailbreakDetection1-1
  3. สำหรับโปแกรมทั่วไปที่ดาวน์โหลดมาจาก “App Store” ให้ทำการ “Decrypt” เสียก่อน (http://blog.itselectlab.com/?p=565) แล้วติดตั้งโปรแปรมที่ “Decrypt” ใหม่อีกครั้ง
  4. เมื่อทำการ “Decrypt” โปรแกรมเรียบร้อยเราจึงสามารถ “Reverse Engineer” โปรแกรมได้ (อ่าน “Class” หรือ “Method” ออก ตามบทความ http://blog.itselectlab.com/?p=350) แต่สำหรับโปรแกรม “DVIA” ไม่ได้มาจาก “App Store” ดังนั้นจึงข้ามขั้นตอนที่ (3) ไปได้ ดังนี้
    • “Remote Access” ผ่าน “SSH”
      login as: root
      root@192.168.0.15's password:
      iPad:~ root# ls -l
      total 112
      drwxr-xr-x 3 root wheel   102 Oct 23  2014 Documents/
      drwxr-xr-x 8 root wheel   272 Oct  9  2014 Library/
      drwxr-xr-x 3 root wheel   102 Oct 24  2014 Media/
      -rw-r--r-- 1 root wheel 82904 Oct 10  2014 dumpdecrypted.dylib
      -rwxrwxrwx 1 root wheel 25744 Oct 10  2014 keychain_dumper*
      
    • ค้นหา “Directory” สำหรับโปรแกรม “DVIA”
      iPad:~ root# cd /var/mobile/Applications/
      iPad:/var/mobile/Applications root# ps aux | grep Damn
      mobile     783   0.0 13.3   517264  68372   ??  Ss   10:10AM   0:31.21 /var/mobile/Applications/BD0BBE00-2C62-4533-B7C4-D8B46D1EF4C5/DamnVulnerableIOSApp.app/DamnVulnerableIOSApp
      iPad:/var/mobile/Applications/BD0BBE00-2C62-4533-B7C4-D8B46D1EF4C5/DamnVulnerabl        eIOSApp.app root# ls -l
      total 21128
      -rw-r--r-- 1 mobile mobile    11553 May 24 04:16 120x120.png
      -rw-r--r-- 1 mobile mobile    13907 May 24 04:16 152x152.png
      -rw-r--r-- 1 mobile mobile     6525 May 24 04:16 57x57.png
      -rw-r--r-- 1 mobile mobile   375699 May 24 04:16 640_960_SplashScn.png
      -rw-r--r-- 1 mobile mobile   464522 May 24 04:16 640x1136_SplashScn.png
      -rw-r--r-- 1 mobile mobile     7893 May 24 04:16 72x72.png
      -rw-r--r-- 1 mobile mobile     8464 May 24 04:16 76x76.png
      -rw-r--r-- 1 mobile mobile    11292 May 24 04:16 AppIcon40x40\@2x.png
      -rw-r--r-- 1 mobile mobile    11553 May 24 04:16 AppIcon60x60\@2x.png
      drwxr-xr-x 3 mobile mobile      102 May 24 04:16 Base.lproj/
      -rwxr-xr-x 1 mobile mobile 19509584 May 24 04:17 DamnVulnerableIOSApp*
      -rw-r--r-- 1 mobile mobile     1427 May 24 04:16 Info.plist
      -rwxr-xr-x 1 mobile mobile     1341 May 24 04:16 LICENSE.txt*
      -rw-r--r-- 1 mobile mobile   464522 May 24 04:16 LaunchImage-700-568h\@2x.png
      -rw-r--r-- 1 mobile mobile   375699 May 24 04:16 LaunchImage-700\@2x.png
      drwxr-xr-x 2 mobile mobile       68 May 24 04:16 META-INF/
      drwxr-xr-x 2 mobile mobile      136 May 24 04:16 Model.momd/
      -rw-r--r-- 1 mobile mobile        8 May 24 04:16 PkgInfo
      drwxr-xr-x 3 mobile mobile      102 May 24 04:16 PlugIns/
      -rwxr-xr-x 1 mobile mobile      324 May 24 04:16 README.txt*
      drwxr-xr-x 2 mobile mobile      102 May 24 04:16 _CodeSignature/
      -rw-r--r-- 1 mobile mobile     4030 May 24 04:16 card-bg.png
      -rw-r--r-- 1 mobile mobile     2006 May 24 04:16 card-bg\@2x.png
      -rw-r--r-- 1 mobile mobile     8081 May 24 04:16 embedded.mobileprovision
      drwxr-xr-x 2 mobile mobile      102 May 24 04:16 en.lproj/
      -rw-r--r-- 1 mobile mobile     1150 May 24 04:16 google.co.uk.cer
      -rw-r--r-- 1 mobile mobile     2927 May 24 04:16 header-bg.png
      -rw-r--r-- 1 mobile mobile     3155 May 24 04:16 header-bg\@2x.png
      -rw-r--r-- 1 mobile mobile     4840 May 24 04:16 main-bg.png
      -rw-r--r-- 1 mobile mobile     5424 May 24 04:16 main-bg\@2x.png
      -rw-r--r-- 1 mobile mobile     3789 May 24 04:16 menu-icon.png
      -rw-r--r-- 1 mobile mobile     5006 May 24 04:16 menu-icon\@2x.png
      -rw-r--r-- 1 mobile mobile     3922 May 24 04:16 menuIcon.png
      -rw-r--r-- 1 mobile mobile     5388 May 24 04:16 menuIcon\@2x.png
      -rw-r--r-- 1 mobile mobile     2941 May 24 04:16 slider-active.png
      -rw-r--r-- 1 mobile mobile     3133 May 24 04:16 slider-active\@2x.png
      -rw-r--r-- 1 mobile mobile    74035 May 24 04:16 slider-bg.png
      -rw-r--r-- 1 mobile mobile   176841 May 24 04:16 slider-bg\@2x.png
      
    • ใช้คำสั่ง “class-dump-z ชื่อโปรแกรม > ชื่อไฟล์ที่ต้องการบันทึก” เพื่อ “Reverse Engineering”
      iPad:/var/mobile/Applications/BD0BBE00-2C62-4533-B7C4-D8B46D1EF4C5/DamnVulnerableIOSApp.app root# class-dump-z  DamnVulnerableIOSApp > zTest.txt
      iPad:/var/mobile/Applications/BD0BBE00-2C62-4533-B7C4-D8B46D1EF4C5/DamnVulnerableIOSApp.app root# ls -l
      total 21764
      -rw-r--r-- 1 root   mobile   649319 Jun 29 14:12 zTest.txt
      
    • ทดสอบเปิดอ่าน “Class” “Method” และ “Properties”
      iPad:/var/mobile/Applications/BD0BBE00-2C62-4533-B7C4-D8B46D1EF4C5/DamnVulnerableIOSApp.app root# cat zTest.txt
      @interface RLMRealm : NSObject {
              BOOL _inWriteTransaction;
              unsigned _threadID;
      @private
              NSThread* _thread;
              NSMapTable* _notificationHandlers;
              unique_ptr<tightdb::Replication, std::__1::default_delete<tightdb::Replication> > _replication;
              unique_ptr<tightdb::SharedGroup, std::__1::default_delete<tightdb::SharedGroup> > _sharedGroup;
              unique_ptr<tightdb::Group, std::__1::default_delete<tightdb::Group> > _readGroup;
              Group* _group;
              BOOL _readOnly;
              BOOL _inMemory;
              BOOL _autorefresh;
              BOOL _dynamic;
              NSString* _path;
              RLMSchema* _schema;
      }
      @property(readonly, assign, nonatomic, getter=getOrCreateGroup) Group* group;
      @property(readonly, assign, nonatomic) BOOL dynamic;
      @property(readonly, assign, nonatomic) BOOL inWriteTransaction;
      @property(assign, nonatomic) BOOL autorefresh;
      @property(retain, nonatomic) RLMSchema* schema;
      @property(readonly, assign, nonatomic, getter=isReadOnly) BOOL readOnly;
      @property(readonly, assign, nonatomic) NSString* path;
      +(id)migrateRealmAtPath:(id)path key:(id)key;
      +(id)migrateRealmAtPath:(id)path encryptionKey:(id)key;
      +(id)migrateRealmAtPath:(id)path;
      +(unsigned)schemaVersionAtPath:(id)path encryptionKey:(id)key error:(id*)error;
      +(unsigned)schemaVersionAtPath:(id)path error:(id*)error;
      +(void)setSchemaVersion:(unsigned)version forRealmAtPath:(id)path withMigrationBlock:(id)migrationBlock;
      +(void)setDefaultRealmSchemaVersion:(unsigned)version withMigrationBlock:(id)migrationBlock;
      +(void)resetRealmState;
      +(void)setEncryptionKey:(id)key forRealmsAtPath:(id)path;
      +(id)realmWithPath:(id)path key:(id)key readOnly:(BOOL)only inMemory:(BOOL)memory dynamic:(BOOL)dynamic schema:(id)schema error:(id*)error;
      +(id)realmWithPath:(id)path encryptionKey:(id)key readOnly:(BOOL)only error:(id*)error;
      +(id)inMemoryRealmWithIdentifier:(id)identifier;
      +(id)realmWithPath:(id)path readOnly:(BOOL)only error:(id*)error;
      +(id)realmWithPath:(id)path;
      +(id)defaultRealm;
      +(id)writeablePathForFile:(id)file;
      +(void)setDefaultRealmPath:(id)path;
      +(id)defaultRealmPath;
      +(void)initialize;
      +(BOOL)isCoreDebug;
      -(id).cxx_construct;
      -(void).cxx_destruct;
      -(BOOL)writeCopyToPath:(id)path encryptionKey:(id)key error:(id*)error;
      -(BOOL)writeCopyToPath:(id)path error:(id*)error;
      -(BOOL)writeCopyToPath:(id)path key:(id)key error:(id*)error;
      -(id)createObject:(id)object withObject:(id)object2;
      -(id)objects:(id)objects withPredicate:(id)predicate;
      -(id)objects:(id)objects where:(id)where args:(void*)args;
      -(id)objects:(id)objects where:(id)where;
      -(id)allObjects:(id)objects;
      -(void)deleteAllObjects;
      -(void)deleteObjects:(id)objects;
      -(void)deleteObject:(id)object;
      -(void)addOrUpdateObjectsFromArray:(id)array;
      -(void)addOrUpdateObject:(id)object;
      -(void)addObjects:(id)objects;
      -(void)addObject:(id)object;
      -(BOOL)refresh;
      -(void)handleExternalCommit;
      -(void)dealloc;
      -(void)invalidate;
      -(void)cancelWriteTransaction;
      -(void)transactionWithBlock:(id)block;
      -(void)commitWriteTransaction;
      -(void)beginWriteTransaction;
      -(void)sendNotifications:(id)notifications;
      -(void)removeNotification:(id)notification;
      -(id)addNotificationBlock:(id)block;
      -(id)migrationBlock:(id)block;
      -(id)initWithPath:(id)path key:(id)key readOnly:(BOOL)only inMemory:(BOOL)memory dynamic:(BOOL)dynamic error:(id*)error;
      @end
      
  5. เมื่อได้ชื่อ “Class” เรียบร้อยแล้ว จากนั้นช่วงเวลานี้อาจจะต้องลองศึกษาจากชื่อ “Class” “Method” หรือ “Properties” ที่เกี่ยวข้องกับช่องโหว่ที่เป็นไปได้ว่าจะมี ในตอนนี้เรากำลังหาช่องโหว่เกี่ยวกับการ “Jailbreak” จึงลองค้นหาคำที่อาจที่เกี่ยวข้องดังกล่าว
    @interface JailbreakDetectionVC : UIViewController {
    }
    -(BOOL)isJailbroken;
    -(void)jailbreakTest2Tapped:(id)tapped;
    -(void)jailbreakTest1Tapped:(id)tapped;
    -(void)readArticleTapped:(id)tapped;
    -(void)didReceiveMemoryWarning;
    -(void)viewDidLoad;
    -(id)initWithNibName:(id)nibName bundle:(id)bundle;
    @end
    
  6. จากนั้นเราลองใช้โปรแกรม “Cycript” เชื่อมต่อ process ของโปรแกรม “DVIA” ที่กำลัง “Run” อยู่ดังนี้ (http://blog.itselectlab.com/?p=95)
    iPad:/var/mobile/Applications/BD0BBE00-2C62-4533-B7C4-D8B46D1EF4C5/DamnVulnerableIOSApp.app root# ps aux | grep Damn
    root       957   2.9  0.1   339588    436 s000  R+    2:35PM   0:00.01 grep Damn
    mobile     783   0.0 14.7   588420  75472   ??  Ss   10:10AM   0:49.83 /var/mobile/Applications/BD0BBE00-2C62-4533-B7C4-D8B46D1EF4C5/DamnVulnerableIOSApp.app/DamnVulnerableIOSApp
    iPad:/var/mobile/Applications/BD0BBE00-2C62-4533-B7C4-D8B46D1EF4C5/DamnVulnerableIOSApp.app root# cycript  -p 783
    cy#
    
  7. จะได้ “command prompt” ของ “Cycript” จากนั้นเราลองศึกษารายละเอียดของ “Class” ในขั้นตอนที่ (5) ก็จะประกอบไปด้วย ชื่อ “Class”  คือ “JailbreakDetectionVC”  และประกอบไปด้วย “Method” ที่น่าสนใจคือ “-(BOOL)isJailbroken;” ซึ่งมีค่า Return หลังจากเรียกใช้ Method นี้เป็นค่า “Boolean” นั้นเอง ด้วยเหตุนี้เราจะต้องเข้าถึงหน้า “UIViewController” ของหน้า “JailbreakDectionVC” ให้ได้เสียก่อนดังนี้
    cy# UIApp
    #"<UIApplication: 0x17d5e000>"
    cy# UIApp.keyWindow
    #"<UIWindow: 0x17d94f80; frame = (0 0; 320 480); autoresize = W+H; gestureRecognizers = <NSArray: 0x17d8d660>; layer = <UIWindowLayer: 0x17d8d5c0>>"
    cy# UIApp.keyWindow.rootViewController
    #"<ECSlidingViewController: 0x17d8e2b0>"
    cy# UIApp.keyWindow.rootViewController.topViewController
    #"<UINavigationController: 0x17f64a10>"
    cy# UIApp.keyWindow.rootViewController.topViewController.visibleViewController
    #"<JailbreakDetectionVC: 0x17dfae70>"
    

    เพิ่มเติม: “UINavigationController” จัดการเรื่องแสดงผลแต่ใช้วิธีการจัดการที่เรียกว่า “Navigation Stack” โดยส่วนแสดงผล (view controllers) แต่เก็บอยู่ในรูปของ “Array” เพราะฉะนั้นจึงมีหลาย ๆ “view controllers” ใน “Navigation Stack” ได้

    • rootViewController คือ “view controllers” แรกที่แสดงผลที่อยู่ใน “array”
    • แต่ “view controller” สุดท้ายใน “Array” จะเป็นตัวแสดงผลอยู่ ณ ปัจจุบัน
    • topViewController เป็น “Property” ของ “View controller” ที่อยู่บนสุดในส่วน “Navigation Stack”
    • visibleViewController เป็น “Property” ของ View controller ที่ปรากฏอยู่ ณ ปัจจุบัน บน “navigation controller” ในทีนี้คือ “JailbreakDetectionVC”
  8. เมื่อทราบ Address ของ “UIViewController” ของหน้า “JailbreakDectionVC”  เราสามารถ “Assign” ให้กับตัวแปรได้ดังนี้
    cy# jail = #0x17dfae70
    #"<JailbreakDetectionVC: 0x17dfae70>"
    
  9. ดังนั้นเมื่อ “Assign” ค่า “Address” ของ “UIViewController” ดังกล่าว ดังนั้นตัวแปร “jail” จึงเปรียบเสมือน “object” หนึ่งของ “class” ที่ชื่อ “JailbreakDetectionVC” และสามารถเรียกใช้ “method” ของมันได้ด้วย ดังตัวอย่าง
    cy# [jail isJailbroken]
    1
    
  10. เมื่อเรียกใช้ method ที่ชื่อ “isJailBroken” พบว่า ได้ค่า “Return” คือ “1” ดังนั้นจาก “Data type” ที่เป็น “Boolean” ในข้อ (5) เราก็สามารถเดาได้ว่า อีกค่าน่าจะเป็น “0” หรือ “NO” แต่การเขียน Return ค่าจะต้องผ่าน “function” ดังนี้
    • ค้นหา “class method” โดยใช้คำสั่ง <ชื่อ class>->isa.messages[‘<ชื่อ method>’]
      jail->isa.messages['isJailbroken']
      0x1660d1
      
    • จากนั้นสร้าง “function” เพื่อ “assign” ค่า “return” ไปยัง “class method”
      cy# jail->isa.messages['isJailbroken'] = function () {return 0;}
      function () {return 0;}
      
    • ลองทดสอบเรียกค่า “isJailbroken” พบว่าจาก “1” เปลี่ยนเป็น “0” แล้ว
      cy# [jail isJailbroken]
      0
      
  11. ทดสอบลองคลิกที่ “Jailbreak Test1” อีกครั้งจะพบข้อความว่า “Device is Not Jailbreak” jailbreakDetection1-2

ใส่ความเห็น