The basic of developing iOS Tweak (Part 2/4)

Witsarut L.
INCOGNITO LAB
Published in
6 min readMay 7, 2019

หลังจากที่ได้ Intro กันไปแล้วใน Part 1 เกี่ยวกับ Tweak และ Cydia Substrate ว่าคืออะไร และทำงานอย่างไร สำหรับใน Part 2 นี้ เราจะมาเริ่มการวิเคราะห์ application เพื่อพัฒนา Tweak อย่างง่าย ๆ กัน ซึ่งขั้นตอนในการพัฒนาสามารถแบ่งคร่าว ๆ ได้เป็น 5 ขั้นตอน ดังนี้

ขั้นตอนการพัฒนา Tweak
  1. หาไฟล์ application bundle (.app) ของ application ที่ต้องการจะทำ Tweak
  2. ทำ static analysis ตัว application เป้าหมาย เพื่อหา component ที่ต้องการแก้ไข
  3. ทำ dynamic analysis ตัว application เป้าหมาย เพื่อหา component ที่ต้องการแก้ไข
  4. ทดสอบแก้ไข component ที่เจอ
  5. พัฒนา Tweak โดยใช้ Theos

การพัฒนา Tweak ที่จะพูดถึงในบทความนี้ จะเป็นการพัฒนา Tweak ของ native application ที่พัฒนาด้วย Objective-C โดย native applicaiton บน iOS จะแบ่งง่าย ๆ ได้เป็น 2 ประเภท ดังนี้

  1. System application คือ built-in application ที่ติดมากับอุปกรณ์ เช่น Notes, Camera, iCloud รวมถึง application โดยส่วนใหญ่ที่ติดตั้งโดยใช้ Cydia
  2. Store application คือ application ที่ download มาจาก App Store หรือ application ที่ติดตั้งเองโดยใช้ไฟล์ประเภท .ipa และ .app

ซึ่งลักษณะโครงสร้างของทั้ง 2 ประเภทนั้น จะมีโครงสร้างต่างกันเล็กน้อย ทำให้วิธีในการวิเคราะห์และพัฒนา Tweak ก็ต่างกันเล็กน้อยเช่นกัน แต่สำหรับใน Part นี้เราจะมาเริ่มกันจาก store application กันก่อน

เนื้อหาที่จะเขียนต่อไปจะทดสอบโดยอ้างอิงจากอุปกรณ์/โปรแกรมดังนี้

  • iPhone 5S ระบบปฏิบัติการเป็น iOS 11.4.1 ที่ Jailbreak โดย unc0ver
  • macOSX Mojave version 10.14
  • Xcode version 10.2.1

สำหรับการพัฒนา Tweak ใน iOS แต่ละ version อาจจะมีรายละเอียดที่แตกต่างกันออกไป แต่ก็จะมี concept ที่สามารถนำไปประยุกต์ใช้ได้เหมือนกัน

ใน Part 2 นี้เราจะพัฒนา Tweak ของ application ที่ชื่อว่า iGoat ซึ่งเป็น application ที่สร้างมาเพื่อให้มีช่องโหว่ทางด้าน security ไว้สำหรับให้ security researcher และ developer ได้เรียนรู้

ขั้นแรกก่อนที่จะเริ่มลงมือทำ เราจะต้องตั้งเป้าหมายก่อนเป็นอันดับแรกว่าจะพัฒนา Tweak ที่ใช้ทำอะไร การกำหนดเป้าหมายที่ชัดเจนนั้นมีความสำคัญ เพราะหลายครั้ง จะทำให้เราหาจุด cut-in point ได้ และไม่เสียเวลาในการไปวิเคราะห์จุดอื่นที่ไม่เกี่ยวกับเป้าหมายที่ตั้งไว้

กำหนดเป้าหมายก่อนลงมือทำ

Application iGoat จะมี exercise ให้ฝึกอยู่หลาย exercise ด้วยกัน ในบทความนี้ เราจะมาเขียน Tweak สำหรับ exercise Runtime AnalysisPersonal Photo Storage เป้าหมายของ exercise นี้ จริง ๆ แล้วคือการหา password มาใส่ให้ถูกต้อง เพื่อที่จะเข้าไปดูรูปภาพใน personal storage ให้ได้ แต่นอกจากจะหา password ให้ได้แล้ว เราจะตั้งเป้าหมายเพิ่มเติมว่า Tweak ของเราจะต้องนำ password ที่ถูกต้องมาใส่ในช่อง text field ให้โดยอัตโนมัติและมีลักษณะเป็นดังรูป

Password ที่ถูกต้องจะปรากฎขึ้นมาในช่อง text field อัตโนมัติ

เมื่อตั้งเป้าหมายได้แล้ว อันดับต่อไปคือการลงมือทำ สูดหายใจเข้าลึก ๆ และมาเริ่มขั้นตอนแรกกันเลยครับ

1. หาไฟล์ Application Bundle (.app)

การติดตั้ง application บน iOS โดยส่วนใหญ่แล้ว หากไม่พึ่ง App Store ก็สามารถทำได้โดยนำไฟล์ IPA ของ application มาติดตั้งด้วยตัวเอง ถ้าเทียบกับฝั่ง Android แล้วไฟล์ IPA ก็เปรียบเสมือนไฟล์ APK นั้นเอง

ลักษณะของไฟล์ IPA นั้น จะเป็นไฟล์ที่ถูก compress ไว้ (สามารถ extract โดยใช้ ZIP ได้) โดยจะมีโครงสร้างภายในเป็นดังรูป

โครงสร้างของไฟล์ IPA
  • ไฟล์ iTunesArtWork เป็นไฟล์รูป icon ของ application ที่ iTunes และ App Store ใช้ในการจัดการตัวไฟล์ IPA
  • iTunesMetadata.plist เป็นไฟล์ที่เก็บข้อมูลทั่วไปที่ iTunes และ App Store ใช้ในการจัดการตัวไฟล์ IPA เช่น copyright, วันที่ซื้อ application, ข้อมูลของคนที่ซื้อ และข้อมูลทั่วไปของ developer เป็นต้น
  • โฟลเดอร์ META-INF จะประกอบไปด้วย metadata ทั่ว ๆ ไปของไฟล์ IPA
  • ส่วนที่สำคัญคือส่วนที่อยู่ในโฟลเดอร์ Payload ซึ่งเป็นไฟล์ application bundle (.app) ที่จะประกอบไปด้วยส่วนที่จำเป็นในการที่จะทำให้ application สามารถทำงานได้อย่างสมบูรณ์ โดยเฉพาะส่วนที่มีความสำคัญคือไฟล์ executable หลักของ application ก็จะอยู่ใน application bundle นี้เช่นเดียวกัน ซึ่งโครงสร้างหลัก ๆ ของ application bundle จะมีลักษณะดังรูป
โครงสร้างของไฟล์ application bundle

ไฟล์ที่เราจะสนใจในเบื้องต้นคือ ไฟล์ executable หลักของ application (ไฟล์ MyApp), ไฟล์ที่อยู่ในโฟลเดอร์ Framework และไฟล์ Info.plist สำหรับรายละเอียดของไฟล์อื่น ๆ ว่ามีหน้าที่อะไรนั้น สามารถดูเพิ่มเติมได้จากที่นี่

วิธีในการหาไฟล์ .app ของ application ที่ติดตั้งบนอุปกรณ์ สามารถหาได้ที่ path ต่อไปนี้บนอุปกรณ์

/private/var/containers/Bundle/Application/<UUID>/<bundle name>.app 

UUID (Universally Unique Identifier) จะเป็นค่าสุ่มที่แสดงโดยใช้ hexadecimal และมี pattern เป็นดังนี้

HHHHHHHH-HHHH-HHHH-HHHH-HHHHHHHHHHHH

ที่แต่ละเครื่องจะมีค่าไม่เหมือนกันแม้จะเป็น application เดียวกันก็ตาม

โฟลเดอร์ที่มีชื่อเป็น UUID ที่อยู่ภายใน path /private/var/container/Bundle/Application/

การหาไฟล์ .app สามารถใช้คำสั่ง find ตามด้วยชื่อ application เพื่อหาได้ ในตัวอย่างจะเป็นการหาไฟล์ .app ของ iGoat และใช้คำสั่ง scp ในการดาวน์โหลดไฟล์เพื่อมาวิเคราะห์ต่อไป

ใช้คำสั่ง find หาไฟล์ application bundle ของ iGoat
ใช้คำสั่ง scp ในการ download ไฟล์ application bundle จากอุปกรณ์
ไฟล์ application bundle ที่ได้

2. Static analysis

อ่านไฟล์ Info.plist

วิธีการในการเปิดไฟล์ .app ใน macOSX ทำได้โดยการ click ขวาที่ไฟล์และเลือก Show Package Contents เมื่อเปิดไฟล์ได้แล้วขั้นแรกสุดในการวิเคราะห์คือการอ่านข้อมูลจากไฟล์ Info.plist โดยไฟล์ Info.plist จะเป็นไฟล์ binary ที่มีการเก็บค่า configuration ต่าง ๆ ของ application ไว้ในรูปแบบ key-value pairs

ไฟล์ Info.plist จะมีการเก็บข้อมูล เช่น minimum OS version ที่สามารถใช้งาน application ได้, bundle name, bundle identifier (ใช้สำหรับระบุ application), executable file หากเทียบกับ application ฝั่ง Android แล้ว ไฟล์ Info.plist ก็เปรียบเสมือนไฟล์ AndroidManifest.xml นั้นเอง

วิธีในการเปิดอ่านไฟล์ .plist สามารถเปิดอ่านโดยใช้ Xcode หรือจะใช้ plutil ในการ convert ให้เป็น XML ที่สามารถอ่านได้ก็ได้ ตัวอย่างด้านล่างจะเป็นการอ่านไฟล์ Info.plist ของ iGoat โดยใช้ Xcode เพื่อหาไฟล์ executable หลักของ application และนำมาวิเคราะห์ต่อ

ไฟล์ Info.plist ของ iGoat

ในการทำ pentest หรือ reverse engineer บน iOS application นั้น นอกจากไฟล์ Info.plist แล้ว ไฟล์อื่น ๆ ก็มีความสำคัญไม่แพ้กัน บางครั้ง application อาจจะเก็บข้อมูลที่เราสนใจ, credential หรือ key ไว้ในไฟล์อื่น ๆ อีก เพราะฉะนั้นการอ่านและดูรายละเอียดของไฟล์ทั้งหมดที่อยู่ในไฟล์ .app จะทำให้เราเข้าใจการทำงานของ application ได้ดียิ่งขึ้น

วิเคราะห์ไฟล์ executable

ไฟล์ executable หลัก ที่อยู่ใน application bundle เป็นไฟล์ binary ประเภท Mach-O (ย่อมาจาก Mach Object) และไฟล์ประเภท Mach-O มีคุณสมบัติอย่างหนึ่งคือ สามารถเก็บ binary ที่ support หลาย ๆ architecture ไว้ได้ในไฟล์เดียว ไฟล์ที่มีลักษณะแบบนี้จะเรียกว่า Fat binary ทำให้ไฟล์ IPA 1 ไฟล์ สามารถที่จะใช้งานได้ทั้งอุปกรณ์ iOS ที่มี architecture แบบ Armv7(s) (iPhone 4 - iPhone 5) หรือ แบบ Armv8/Arm64 (iPhone 5S - iPhone X) ได้ ด้านล่างเป็น support matrix ของ iOS กับ iPad, iPod, iPhone ที่มีอยู่ในปัจจุบัน ที่รวบรวมไว้ได้น่าสนใจดีเลยเอามาแชร์กันครับ

iOS support matrix จาก https://dorianroy.com/blog/category/ios-support-matrix/

ไฟล์ที่ได้มา support architecture อะไรบ้าง?

เราสามารถใช้เครื่องมือที่ชื่อว่า otool (object file-displaying tool) ซึ่งเป็นเครื่องมือ open source ของ Apple ที่สามารถใช้วิเคราะห์ไฟล์ประเภท Mach-O ได้ ในการตรวจสอบว่าไฟล์ support architecture อะไรบ้างได้ โดยใช้คำสั่งตามรูป

ใช้ otool หาว่า iGoat support architecture อะไรบ้าง

-v = แสดง output แบบแปลความหมายจาก hex แล้ว

-h = แสดง Mach header

จากรูปพบว่า iGoat support ทั้ง architecture แบบ Armv7 และ Arm64

ก่อนที่จะทำการวิเคราะห์ต่อ เราจะต้องรู้ข้อเท็จจริงอย่างหนึ่งก่อนว่า ทั่วไปแล้วไฟล์ executable หลัก จะถูก encrypt ไว้ โดยใช้ DRM (Digital rights management) ของ Apple ที่ชื่อว่า FairPlay และจะถูก decrypt เมื่อ application กำลังจะเริ่มทำงานเท่านั้น เพราะฉะนั้นหากจะทำ static analysis ให้ได้ข้อมูลมากที่สุด เราจะต้อง decrypt ไฟล์ executable ให้ได้ซะก่อน

ไฟล์ที่ได้มาถูก encrypt ไว้หรือไม่จะรู้ได้อย่างไร?

การตรวจสอบว่าไฟล์ executable ถูก encrypt ไว้หรือไม่ ทำได้โดยใช้ otool ในการตรวจสอบได้อีกเช่นเดียวกัน โดยใช้คำสั่งตามรูปด้านล่าง

ใช้ otool ตรวจสอบว่าไฟล์ถูก encrypt ไว้หรือไม่

-l = แสดงข้อมูล section Load commands ของ Mach-O ออกมา

cryptid = 0 หมายถึง ไฟล์นี้ไม่ได้ถูก encrypt ไว้, cryptid = 1 หมายถึงไฟล์นี้ถูก encrypt ไว้ โดยค่า cryptid ใน output บรรทัดแรก คือ ค่าของ binary ที่ support Armv7 และบรรทัดที่สอง คือ ค่าของ binary ที่ support Arm64 ตามลำดับ

วิธีในการที่จะ decrypt ไฟล์ก็มีหลายวิธีด้วยกัน เช่น การใช้ Clutch, dumpdecrypted, dump-ios หรือ ใช้ lldb ในบทความนี้จะข้ามในส่วนของวิธีการ decrypt ไป เพราะสามารถหาอ่าน tutorial ได้เยอะแยะมากมาย ตามที่ได้ให้ reference ไปแล้ว

Dump class, method, variable และ property ที่มีอยู่ในไฟล์ executable

ไฟล์ executable binary ของ Objective-C มี __OBJC segment ที่มีข้อมูลที่มีความสำคัญในการทำ static และ dynamic analysisโดย __OBJC segment จะประกอบไปด้วยรายละเอียดของ class, method, variable และ property ที่ถูกใช้ใน application การรู้ถึงข้อมูลดังกล่าวจะทำให้เราเข้าใจการทำงานของ application มากยิ่งขึ้น

วิธีการในการ dump รายละเอียด class, method, variable และ property ออกมาจากไฟล์ executable สามารถทำได้โดยการใช้เครื่องมือที่ชื่อว่า class-dump ตามคำสั่งต่อไปนี้

class-dump -o <folder name> -H <path to executable file>-o = output folder
-H = generate header file จาก class ที่พบ

เมื่อเข้าไปที่ output folder จะเจอกับไฟล์ header ของ class ต่าง ๆ ใน application

ไฟล์ header ของ class ใน application iGoat

ไฟล์ header ที่ได้ ก็คือไฟล์ header เดียวกันกับที่ใช้ประกาศ class, method, variable และ property เมื่อพัฒนา application ด้วย Objective-C นั้นเอง

ความหมายของแต่ละส่วนในไฟล์ header สามารถอ้างอิงได้ตามรูปต่อไปนี้

การประกาศ class, method และ variableในไฟล์ header (ที่มา: https://developer.apple.com/)
การประกาศ method ในไฟล์ header (ที่มา: https://developer.apple.com/)

เครื่องหมาย - และ + หน้า method หมายถึง instance method และ class method ตามลำดับ และวิธีในการ hook หรือเรียกใช้ method ทั้งสองประเภทก็จะมีวิธีการที่แตกต่างกันเล็กน้อย ซึ่งจะมีการพูดถึงเมื่อถึงขั้นตอนการทำ dynamic analysis ครับ

(id) หมายถึง ประเภท object ที่ไม่ได้ถูกกำหนดไว้ตายตัวตั้งแต่ต้น โดยจะรู้ได้ว่าเป็น object ประเภทไหน เมื่อตอน application กำลังทำงานอยู่

กลับมาที่เป้าหมายของเราก็คือการหา class ที่เกี่ยวข้องกับ exercise Personal Photo Storage เพื่อดูว่าภายใน class มีรายละเอียดอะไรบ้าง ผลลัพธ์ที่ได้จาก class-dump จะพบว่ามีไฟล์ PersonalPhotoStorageVC.h อยู่ ทำให้พอจะเดาได้ว่า class นี้จะต้องเป็น class ที่ควบคุม exercise Personal Photo Storage แน่นอน

ไฟล์ PersonalPhotoStorageVC.h ผลลัพธ์จาก class-dump
ข้อมูลภายในไฟล์ PersonalPhotoStorageVC.h

จากข้อมูลภายในไฟล์ PersonalPhotoStorageVC.h ทำให้เราพอที่จะเดาได้ว่า NSString *_pw จะต้องเป็น instance variable ที่เก็บค่า password ที่เราตามหาแน่นอน และ method — (id)thePw น่าจะเป็น instance method ที่ return password ออกมาแน่ ๆ

แต่… หากไม่เจอ keyword ที่เกี่ยวข้องกับ exercise เป้าหมายของเราในไฟล์ executable หลัก เลย จะทำอย่างไร?

โฟลเดอร์ Frameworks สำคัญอย่างไร?

ในบางกรณี class ที่เราต้องการหา อาจไม่ได้อยู่ในไฟล์ executable หลักเสมอไป โดยมีความเป็นไปได้สูงที่อาจจะอยู่ในไฟล์ที่อยู่ในโฟลเดอร์ Frameworks ซึ่งเป็นที่ ๆ เก็บ dylib และไฟล์ framework bundle (.framework) ที่สำคัญ ที่ application ต้องใช้ในขณะทำงาน

ไฟล์ framework bundle มีโครงสร้างที่คล้ายกับไฟล์ application bundle แต่จะมี extension เป็น .framework และมีไฟล์ binary ข้างในเป็นแบบ dylib ไม่ใช่ binary แบบ executable (ความแตกต่างอย่างง่าย ๆ ระหว่าง dylib กับ executable คือ dydlib จะไม่สามารถทำงานด้วยตัวเองได้ หากไม่มีไฟล์ executable อื่น เรียกมันขึ้นมาใช้งาน)

ตรวจสอบไฟล์ binary Realm ใน Realm.framework ของ iGoat

วิธีการหา keyword ที่สนใจ (หรือที่เกี่ยวข้องกับ class ที่สนใจ) ว่ามีอยู่ในไฟล์ไหนบ้างสามารถทำได้โดยใช้คำสั่ง grep ตามด้านล่าง

grep -r -i “keyword” /path/to/application_bundle.app-r = ค้นหาแบบ recursive ทั้งใน subdirectory ด้วย
-i = ค้นหาแบบ case insensitive

เมื่อเจอ keyword ที่เราสนใจในไฟล์ dylib เราก็สามารถใช้ class-dump ในการ dump ข้อมูลของ class, method และ variable ออกมาได้เช่นเดียวกัน

ใช้ class-dump กับไฟล์ dylib

จะเห็นว่าข้อมูลที่ได้มาจาก class-dump มีประโยชน์อย่างมากในการเข้าใจการทำงานภายในของ application แต่เท่านั้นยังไม่เพียงพอ เมื่อเจอ class, method หรือ variable ที่น่าจะเกี่ยวข้องกับเป้าหมายที่ตั้งไว้ เราจะต้องพิสูจน์สมมติฐานที่คิดไว้ว่าเป็นจริงหรือไม่ โดยวิธีการพิสูจน์ที่ง่ายที่สุดคือการทำ dynamic analysis ที่จะมีการพูดถึงใน Part ต่อไปครับ

ในบางครั้งข้อมูลจากการทำ static analysis แค่นี้อาจจะไม่เพียงพอ เราจะต้องพึ่งเครื่องมือ เช่น Hopper, IDA, Radare2 หรือ Ghidra เพื่อทำ reverse engineer ให้เข้าใจการทำงานของ application ในระดับ assembly ซึ่งเป็นระดับที่ลึกลงไปอีกขั้น มากกว่าแค่การอาศัยข้อมูลจาก class-dump แต่สำหรับในบทความนี้ จะพูดถึงวิธีการพื้นฐานเท่านั้น สำหรับวิธีที่ลึกกว่านี้ จะมีพูดถึงในบทความต่อ ๆ ไปครับ

ทิ้งท้าย…

ใน Part 2 ก็จะจบลงเพียงเท่านี้ และสำหรับใน Part 3 เราจะมาพูดถึงในขั้นตอนที่ 3 ทำ dynamic analysis ตัว application เป้าหมาย เพื่อหา component ที่ต้องการแก้ไข และขั้นตอนที่ 4 ทดสอบแก้ไข component ที่เจอ กันต่อครับ

--

--

Witsarut L.
INCOGNITO LAB

Penetration tester, security researcher at Incognito Lab.