7 มิ.ย. 2558

Unpack Data (Tree of Life)

เปิด Hex ดู Signature นู่นนี่นั่น


จริงๆ Google เจอว่า เฮีย luigi แห่ง quickbms ทำไว้แล้ว (ยังสดๆ ณ วันที่ 7/6/2558)
แต่ตอน unpack มาไฟล์มันเสีย และ structure ของ folder มันดูรวนๆ ไม่ตรงกับที่เห็นใน hex ของ exe


แต่ก็ยังได้ประโยชน์เพราะเราจะรู้ structure ของ ไฟล์ pack โดยที่ไม่ต้องเดาเอาเอง แต่กระนั้นแกะเองดูดีกว่า
ถ้าดู script ของ quickbms จะเห้นว่าใช้ unzip ไล่ดูใน olly ก็มีการ ใช้ lib zlib 

และไปดูใน hex ของ data ก็จะเจอ  78 01 เต็มไปหมด


เพราะโดยปกติมันเป็น header ของ zlib คร่าวๆมี
78 01 - No Compression/low
78 9C - Default Compression
78 DA - Best Compression  
ฟันธงว่า zlib แน่นอน
เริ่มไปดูว่า เกมอ่าน data.pack ยังงัยที่ถูกต้อง พยายามหาว่า Code ที่เกี่ยวกับ Virtual File System มันอยู่ตรงไหน


ตรงนี้แน่นอน แตะเมื่อ google ดู ประกาฏว่ามันเป็น Open Source โชคดีจริงๆ ที่ dev ยัง เหลือ text ไว้ให้ตามร่องรอยได้
ก็โหลด code มาโม จน สามารถ unpack ได้อย่างถูกต้อง
และโครงสร้างที่ถูกต้องคือ


แต่มันยังไม่จบแค่นั้น เพราะ แต่ละไฟล์ยังถูก zip ไว้อีกชั้น



ในสองรูปแบบ
1.       คือไม่โดน compress  (smallshop.png) Offset ที่ 10 เป็นตัวบอกว่า มันโดน compress รึปล่าว
2.       คือโดน compress (build_list_icon.png)
ไฟล์ที่โดน compress สั่งเกต DWORD Offset ที่ 0  จะไม่เท่ากับ DWORD Offset  ที่ 6
DWORD Offset ที่ 0 น่าจะเป็นตัว บอก max size หลังจาก uncompress  ส่วน DWORD Offset ที่ 6 เป็นตัวบอก ขนาด binary ที่เป็น data ที่ compress อยุ่ (ลองนับ ตั้งแต่ 78 01 ไปจนถึงสุดไฟล์จะได้ขนาดเท่า DWORD Offset  6 พอดี )
ว่าแล้วก็เขียนโปรแกรมไล่ decompress ทีละไฟล์
แต่! ผลลัพท์ ออกมาเหมือนของ quickbms เลย คือมีไฟล์เสีย ที่ประมาณกลางๆ ไฟล์


มีสองเหตุผล ที่คิดออกที่เป็นแบบนี้
1.       Algorithm ในการ uncompress ของ zlib โดน custom (ทำให้ต้องไล่ reverse engineer code zlib ทั้งหมด ใน game client)
2.       Data ก่อน uncompress มันพังอยู่แล้ว ทำให้ uncompress ไม่ได้ (ทำให้ต้องไล่ reverse engineer OFS ทั้งหมด ใน game client และ ถ้าไม่ใช่ก็ต้องไล่หาว่า มันโดยแก้อะไรตรงไหนก่อน uncompress )
จากทางเลือกทั้งสองดูเหมือน ข้อ 1 จะมีปริมาณงานน้อยกว่า แต่จากการที่ไล่ดูหมดแล้ว บอกเลยดีกว่า ว่าหวยออกข้อ 2 แต่ไม่ใช่ ที่ตัว OFS แต่ เป็นที่กระบวนการ ก่อนการ uncompress  เราต้องไปแก้บาง byte ใน data ให้ถูกต้องก่อน uncompress

จะเริ่มไปใช้เครื่องมือที่ชื่อ IDA ในการ reverse engineer ที่เราจะหา code อะไรๆ ง่ายกว่า
ย้อนกลับไปตอนที่ เขียนโปรแกรม uncompress เอง ตัว zlib จะ return DATA_ERROR ออกมา เราก็ควรจะไปเริ่ม ที่ตรงแถวๆนั้นในการ reverse engineering ซึ่งก็คือแถวๆนี้  Address 0x6A4A1D

ปล. ปิด ASLR ซะจะได้ไล่ Address ง่ายๆ
จุดประสงค์คือจะไปดูว่าอะไร Call มัน


ไล่กลับไปเรื่อยๆ อย่างมีหลักการ ^^ ว่าออกจากส่วนที่เป็น zlib รึยัง หรือมีอะไรผิดปกติ ไป ก็จะเห็น


วิเคราะห์ ด้วย IDA และ Plugin HexRay C Decompile เพื่อความสะดวก
-          V8 = sub_61665E คือ new  แล้วจะได้ pointer กลับมาไว้ใน V8
-          Function ที่น่าสงสัยคือ sub_615F40 และ sub_615E10 เพราะมีการ ส่ง buffer ไปเป็น parameter ก่อนการ uncompress
-          Sub_6443B0 = คือ function ZEXTERN int ZEXPORT uncompress OF((Bytef *dest,   uLongf *destLen, const Bytef *source, uLong sourceLen)); ใน zlib
ไปดูที่ sub_615F40 ก่อน


Parameter a1 มันต้องเป็น pointer ของ struct ที่เก็บข้อมูลของแต่ละไฟล์ใน pack แน่นอน เพราะ เมื่อ debug ด้วย olly dbg ทำให้รู้ว่า v1 = *(DWORD*)(a1 + 16) คือ length ของชื่อไฟล์  
และ debug หา ตัวแปรอื่นใน struct ไปเรื่อยๆ ได้เป็น code นี้ในภาษา c

สรุป Function นี้จะเป็นการคำนวน key โดย parameter คือชื่อไฟล์ ในแบบ Unicode
มาต่อที่ function sub_615E10 ไอตัวนี้แหละไอตัวดีแน่นอนเพราะมันมีการแก้ binary ของไฟล์ก่อนการ uncompress


โดยจะรับ parameter a3 ที่เป็น key ที่ถูกคำนวนจากชื่อไฟล์
a1 คือ pointer ของ data , a2 คือ size ของ data
มันจะทำการแก้ไขให้ data ถูกต้อง โดยการ xor byte กลับมาจำนวน 6 byte
Code ใน c ก็ จะได้เป็น


เมื่อเขียนโปรแกรมเสร็จเรียบร้อยก็ทดลอง unpack ใหม่ 

ไม่พังแล้วจ้า!!!

วิธีการ pack กลับก็ย้อนกระบวนการกลับ โดยที่ไม่จำเป็นต้อง compress แล้วก็ได้  แค่เอาไปใส่ data.pack ไว้ จากเดิม data.pack ขนาดเดิมประมาณ 499MiB จะเป็นประมาณ 941MiB แต่ client ก็ยังอ่านได้เหมือนเดิม

ลองแก้อะไรดูซักหน่อย นั่นคือ ทำให้กลางคืนมันสว่างเหมือนกลางวันซะ ด้วยการแก้ data ใน pack