ในขณะที่สอนอยู่นี้ เราก็แอบเริ่มรับงาน Consult บ้างแล้วแบบเงียบๆ ปรากฎว่าสองสิ่งที่บริษัทไม่ว่าจะเล็กหรือใหญ่มักจะถามถึงคือ
1) แอพฯใหญ่เกินไป จะทำยังไงให้แอพฯเล็กลงได้บ้าง?
2) แอพฯมีข้อมูลสำคัญอยู่ จะป้องกันการ Decompile ยังไงได้บ้าง?
ซึ่งไม่แปลกใจเลยที่บริษัทใหญ่ๆต่างให้ความสำคัญเรื่องนี้ เพราะมันก็เป็นประเด็นมานานแล้ว แอนดรอยด์เป็น Open Source จึงมีช่องให้แฮคโน่นนี่เพียบ เจ้าของโปรดักส์ก็ต้องกลัวกันเป็นธรรมดา
แต่แอนดรอยด์ก็เกิดมานานแล้ว จึงไม่ต้องแปลกใจอีกเช่นกันที่กูเกิ้ลเตรียมเครื่องมือมาเพื่อตอบโจทย์ตรงนี้เป็นที่เรียบร้อย มาพร้อม Android SDK เลย นามของมันคือ ProGuard
ProGuard คืออะไร?
ProGuard เป็น Open Source Project ที่กูเกิ้ลเอามาใช้ในแอนดรอยด์ ต้นทางเขียนความสามารถของ ProGuard ไว้ดังนี้
ProGuard is a free Java class file shrinker, optimizer, obfuscator, and preverifier. It detects and removes unused classes, fields, methods, and attributes. It optimizes bytecode and removes unused instructions. It renames the remaining classes, fields, and methods using short meaningless names.
ส่วนเว็บของแอนดรอยด์ก็จะเขียนเฉพาะเจาะจงลงมาหน่อยตรงเรื่องของแอนดรอยด์โดยเฉพาะ
The ProGuard tool shrinks, optimizes, and obfuscates your code by removing unused code and renaming classes, fields, and methods with semantically obscure names. The result is a smaller sized .apk file that is more difficult to reverse engineer.
ก็ตามนั้นเลย ProGuard คือเครื่องมือที่มีความสามารถในการเอาไฟล์ Bytecode มากระทำการดังต่อไปนี้
- Shrink (ตัดทิ้ง) - วิ่งหาว่าคลาสไหนหรือ Member Variable ตัวไหนไม่มีการเรียกใช้ ก็จะตัดทิ้งออกไปให้เลย ซึ่งแค่นี้ก็เล็กลงไปมากแล้ว เพราะการเขียนโปรแกรมบนแอนดรอยด์โดยส่วนใหญ่มักจะมี Library เข้ามาร่วมด้วยเยอะเหมือนกัน และเราก็ใช้แค่ 10% เองมั้ง หากไม่ Shrink ทิ้ง ก็จะมีขยะพ่วงมาด้วยเยอะมากต่อ Library การ Shrink จะทำให้ Bytecode คงเหลือแค่โค้ดส่วนที่ถูกเรียกใช้เท่านั้น
- Optimize (ลดทอน) - ถึงแม้จะเหลือแค่ส่วนที่ถูกเรียกใช้ แต่ก็ยังสามารถลดทอนได้อีกหน่อย จากการวิ่งเปลี่ยนตัวแปรบางตัวให้เป็น final, static หรือทำให้บาง method กลายเป็น inline ไป ซึ่งนอกจาก Bytecode จะเล็กลงแล้ว ยังทำให้โค้ดบางส่วนทำงานเร็วขึ้นอีกด้วย
- Obfuscate (ทำให้ยุ่งเหยิง) - คราวนี้เจ้า ProGuard ก็จะทำการเปลี่ยนชื่อตัวแปรและชื่อคลาสที่ไม่ใช่ Entry Point ให้เป็นชื่อที่อ่านไม่รู้เรื่องแล้วทำการเรียบเรียงใหม่
โดยขั้นตอนทั้งหมดตามสเต็ปดังภาพเลย
ผลคือจะได้ไฟล์ apk ที่เล็กลงและยุ่งเหยิงจน Reverse Engineer ยากขึ้นมากนั่นเอง
นี่แหละ ความสามารถอันแสนน่ารักของ ProGuard ^_^
จากการลองดู พบว่าแอพฯส่วนใหญ่สามารถลดขนาดไฟล์ apk ได้ถึงมากกว่า 60% เลยทีเดียว ไม่ใช่น้อยๆนะฮ้าบบบบบ
วิธีเปิดใช้ ProGuard
เนื่องจาก ProGuard เป็น Tools ที่กูเกิ้ลตั้งใจเตรียมไว้ให้ใช้อยู่แล้ว ดังนั้นการเปิดใช้จึงง่ายมาก
บน Eclipse
เปิดไฟล์ <project_root>/project.properties แล้วพิมพ์เพิ่ม proguard.config=proguard.cfg เพิ่มเข้าไป จากนั้นให้สร้างไฟล์ proguard.cfg เปล่าๆไว้ใน Project Root เป็นอันเรียบร้อย
บน Android Studio
ในไฟล์ build.gradle ของโมดูล app ให้ปรับ runProguard เป็น true (หรือถ้าบนเวอร์ชั่น 0.9.x ขึ้นไป มันเปลี่ยนเป็น minifyEnabled) โดยมีไฟล์ชื่อ proguard-rules.pro เป็นไฟล์สำหรับตั้งค่าโน่นนี่ (มีอยู่แล้วตั้งแต่ New Project ไม่ต้องสร้างเพิ่ม)
เพียงเท่านี้ เวลาเราสั่งคอมไพล์ในโหมด Release แอพฯก็จะถูกลดขนาดโดยอัตโนมัติด้วย ProGuard แต่มันจะไม่ทำให้ถ้าเราสั่งคอมไพล์ในโหมด Debug เพราะว่าการ Obfuscate จะทำให้ชื่อคลาสและชื่อตัวแปรถูกเปลี่ยนไปโดยสิ้นเชิง ส่งผลให้ดีบั๊กยากขึ้นมากนั่นเอง
การตั้งค่าและข้อควรระวัง
ฟังๆดูเหมือน ProGuard จะเป็นเครื่องมือวิเศษอันแสนชาญฉลาด แต่ความจริงแล้วมันก็ไม่ฉลาดขนาดนั้น เพราะบางที(หรือเรียกว่าทุกทีก็ได้) มันมักจะเดาผิดแล้วตัดเอาสิ่งที่จำเป็นต้องใช้ออกไปด้วย ผลคือถ้าเราไปเปิดใช้ ProGuard แล้ว เราจะเจอ can't find referenced method/class ออกมาเพียบแบบนี้
นี่เองเป็นสิ่งที่น่าปวดหัวสำหรับการเปิดใช้ ProGuard เพราะเราต้องมานั่งเขียน Rule เพิ่มว่าจะบังคับให้ห้ามตัดอันไหนทิ้งในไฟล์ Configuration ของ ProGuard ตามที่เขียนชื่อไฟล์ไว้ด้านบน ยกตัวอย่างเช่น เราต้องใส่แบบนี้ไปเพื่อไม่ให้เกิดปัญหาในการ ProGuard Otto และ okhttp
แล้วทุกอย่างก็จะคอมไพล์ผ่านไปอย่างฉลุยยยยย
ถามว่าจะรู้ได้ยังไงว่าต้องเขียนยังไง? ... อย่างแรกคือไปหาจากเว็บของผู้พัฒนา Library นั้นๆได้เลย ถ้าหาไม่เจอก็กูเกิ้ล สุดท้ายถ้าเกิดเริ่มเชี่ยวก็จะเขียน Rule เองเป็นจาก Error Message ที่พ่นออกมา (แต่กูเกิ้ลได้ก็กูเกิ้ลเอาเถอะ) ยกตัวอย่างเช่น http://www.snip2code.com/Snippet/74721/Retrofit--OkHttp--Gson--Dagger-Proguard- เป็นต้น
และนี่เองเป็นสาเหตุที่ Android Studio ถึงไม่เปิด runProguard (หรือ minifyEnabled) เป็น true ตั้งแต่แรก เพราะมันมี Learning Curve นั่นเอง เดี๋ยวจะพาลไม่อยากทำแอพฯกันเอา ... ช่วงแรกๆก็ทำใจกันนิดหน่อยละกันนะฮับ จะปวดหัวมากในการเปิดใช้ ProGuard โดยเฉพาะอย่างยิ่งถ้าทำแอพฯจนเสร็จแล้วค่อยมาเปิดใช้ อาจจะเจอ Error ราวๆ 1,000 อัน ต้องไล่เขียน Rule กันเป็นวันๆเลย (แต่ก็คุ้มค่านะ พูดเลย)
และข้อควรระวังอย่างหนึ่งของการเปิดใช้ ProGuard คือ การที่ Bytecode มันถูก Obfuscate ไปแล้ว Stacktrace ที่ได้ออกมาก็จะอยู่ในรูปแบบที่อ่านไม่รู้เรื่องเช่นกัน หากไปลงพวก Crashlytics ไว้ ก็จะได้ Stacktrace ที่อ่านไม่รู้เรื่องจนหาบรรทัดไม่เจอ ออกมา
ถามว่าจะทำยังไงได้บ้างให้อ่านออก?
วิธีคือตอนที่เราคอมไพล์แบบ Release ออกไป จะมีโฟลเดอร์ mapping โผล่ขึ้นมาพร้อมไฟล์ 4 ไฟล์ dump.txt, mapping.txt, seeds.txt และ usage.txt
ประเด็นสำคัญอยู่ที่ไฟล์ mapping.txt ที่เป็นตัว map ชื่อคลาสก่อนและหลัง Obfuscate เข้าด้วยกัน ดังนั้นทุกครั้งที่คอมไพล์ Release ให้ก็อปปี้ไฟล์ mapping.txt เก็บไว้ทุกครั้ง เพื่อที่เราจะได้สามารถแปลง Stacktrace กลับมาให้อ่านออกได้อีกครั้ง
โดยคำสั่งที่จะใช้ในการแปลง Stacktrace กลับมาให้อ่านออกได้คือคำสั่งนี้
> retrace.bat -verbose mapping.txt obfuscated_trace.txt
โดยไฟล์ retrace.bat (หรือ .sh บน Mac/Linux) จะอยู่ในโฟลเดอร์ <sdk_root>/tools/proguard ครับ
คร่าวๆก็ประมาณนี้ครับสำหรับ ProGuard ไม่มีอะไรซับซ้อน ตรงไปตรงมา ถ้าจะวุ่นวายก็แค่ตรงการเขียน Rule เท่านั้นแหละ ส่วนใครลง Crashlytics ไว้ก็วางแผนชีวิตไว้ดีๆครับ ไม่งั้นมีมึนแน่นอน
ถึงตอนเริ่มใช้จะดูงงๆน่าสับสนไปหน่อย แต่เชียร์ให้ทุกคนเริ่มใช้กันได้แล้วครับ มันสำคัญมากจริงๆ โดยเฉพาะอย่างยิ่งประเด็นการทำให้แอพฯมีขนาดเล็กลง ส่งผลต่อ End User โดยตรงเลย ส่วนการ Obfuscate Code ก็สำคัญไม่แพ้กันเรื่อง Security ดังนั้น เริ่มเปิดใช้กันตั้งแต่วันนี้ได้เลยครับ เอ้า! ปฏิบัติ !! =D
จบ Blog Geek ไปอีกหนึ่ง ... จากนี้ ... อาหารรัวๆจ๊ะ โหะๆๆๆๆ