Reverse engineering và giải mã save file của Cats & Soup
disclaimer: nội dung trong bài viết này chỉ nhằm mục đích giáo dục, vui lòng không thực hiện hành vi gian lận game này. Bạn có thể bị khoá tài khoản nếu như bị nhà phát hành (HIDEA) phát hiện.
Mở đầu
Mình đã từng là một người “nghiện” Cats & Soup đến mức treo máy liên tục để cày sự kiện và có được những chú mèo phiên bản giới hạn. Sau nhiều tháng nghỉ chơi Cats & Soup và bỏ qua nhiều cơ hội “bắt” mèo VIP, mình quyết định quay lại game để xem sự thay đổi của game. Sau khi thấy những người chơi khác trên r/CatsAndSoup đã đạt đến số vàng 3 chữ cái, mình quyết định thử “đục” con game này để theo kịp tiến trình hiện tại của game.
Tổng quan
Cats & Soup là 1 con game sử dụng engine Unity. Ta biết được điều đó thông qua các thư mục và các tệp asset trong assets\bin\Data. Ta sẽ không thấy các tệp dll như Assembly-CSharp.dll trong thư mục Managed vì game được biên dịch với scripting backend IL2CPP. Nếu bạn đã từng xem qua và nghiên cứu các game Unity IL2CPP, bạn có thể sẽ biết đến Il2CppDumper - một công cụ giúp reverse engineering IL2CPP. Tuy nhiên, nếu bạn thử kéo thả 2 tệp global-metadata.dat và libil2cpp.so vào Il2CppDumper.exe, bạn sẽ thấy lỗi như sau:
Có vẻ như tệp global-metadata.dat đã bị mã hoá. Nếu bạn mở tệp global-metadata.dat trong trình soạn thảo tệp nhị phân, bạn sẽ thấy cấu trúc tệp khác với tệp global-metadata.dat thông thường:
Như bạn có thể thấy, 4 magic byte đầu tiên (signature) của tệp global-metadata.dat đã bị thay đổi. Ngoài ra, nội dung của toàn bộ tệp global-metadata.dat cũng đã được mã hoá.
LIAPP
nếu bạn xem qua nội dung tệp apk của game, bạn sẽ thấy một tệp LIAPP.ini có nội dung tương tự như sau:
1 2 3 4 5 6 7 8
[INFORMATION] LIAgent=0 Job-Id#=***** Version=v***** (0x*******) with Linux. BuildNo=******
Contact=https://www.lockincomp.com/ This application is protected by LIAPP(Lockin Company's mobile security solution).
Vào trang web trong phần Contact, ta biết được LIAPP là một giải pháp bảo vệ ứng dụng mobile - Android và iOS - được sử dụng bởi không chỉ HIDEA mà còn bởi nhiều nhà phát triển game và ứng dụng khác.
Tìm kiếm thêm về LIAPP, ta sẽ thấy khá nhiều thông tin liên quan tới LIAPP Alert:
Điều đó cho thấy LIAPP là một phương pháp bảo vệ khá mạnh và khó để vượt qua. Trên thực tế, nếu bạn sử dụng Game Guardian (hay còn gọi là GG) để thay đổi giá trị vàng, kim cương, xu… của Cats & Soup, bạn sẽ thấy cảnh báo của LIAPP và game sẽ tự đóng sau vài giây:
Tuy nhiên, trong trường hợp của mình (sử dụng WSA và Bliss OS), GG có thể chạy và attach vào game mà không gặp vấn đề.
Lấy file global-metadata.dat gốc của game
Để có thể sửa cấu trúc của tệp global-metadata.dat, một công ty hay cá nhân sẽ phải mua giấy phép một cách thủ công từ Unity. Có vẻ như LIAPP không làm điều đó. Nội dung của tệp global-metadata.dat được LIAPP giải mã và lưu trong bộ nhớ trước khi Unity khởi tạo và đọc nội dung tệp này. Do đó, ta có thể dễ dàng kết xuất tệp global-metadata.dat sử dụng bất cứ chương trình kết xuất bộ nhớ nào. Mình sẽ sử dụng Game Guardian vì sự phổ biến, đơn giản và tiện lợi của nó.
Đầu tiên, ta sẽ khởi động GG và chọn Cats & Soup trong danh sách tiến trình:
Đừng quên chọn khoảng bộ nhớ như hình dưới:
Tiếp theo, ta sẽ tìm địa chỉ của 4 byte magic AF 1B B1 FA (địa chỉ bắt đầu của tệp global-metadata.dat trong bộ nhớ):
Sau đó, ta sẽ kết xuất bộ nhớ tại địa chỉ vừa tìm được:
tệp đã kết xuất sẽ nằm trong thư mục storage/emulated/0/dump/ và có phần mở rộng là .bin. Sau khi sao chép tệp sang máy tính, ta sẽ xoá phần dữ liệu trước 4 byte magic AF 1B B1 FA:
Sau khi đổi tên tệp đã chỉnh sửa thành global-metadata.dat, bạn có thể sử dụng Il2CppDumper để tạo ra các tệp dll dummy.
Save file
Mở tệp Assembly-CSharp-firstpass.dll bằng dnSpy, ta sẽ thấy một số class và namespace bắt đầu bằng ES3, giống như phần mở rộng của tệp dữ liệu SaveFile.es3 tại đường dẫn /storage/emulated/0/Android/data/com.hidea.cat/files/:
Google tên của một vài class sẽ cho thấy Cats & Soup sử dụng Easy Save 3 để mã hoá và giải mã tệp SaveFile.es3. Để giải mã được, ta cần có mật khẩu dùng để mã hoá tệp. Nhưng làm thế nào để biết mật khẩu? Khá may, Easy Save 3 có thể tải xuống ở trên một số web chia sẻ Unity package lậu. Ta có thể đọc mã của Easy Save 3 để biết cách giải mã tệp. Sau khi có được package và sử dụng unitypackage_extractor để lấy nội dung của package, ta có được thư mục với cấu trúc như sau:
#region Fields ... ///<summary>The path associated with this ES3Settings object, if any.</summary> publicstring path = "SaveFile.es3"; ... ///<summary>The password to use when encrypting data.</summary> publicstring encryptionPassword = "password"; ... #endregion ... }
Tìm tất cả tham chiếu đến biến encryptionPassword sẽ thấy biến này được truy cập qua property defaultSettings. Dựa vào nội dung class ES3Settings, ta thấy getter của defaultSettings trả về defaultSettingsScriptableObject.settings, defaultSettingsScriptableObject trả về _defaultSettingsScriptableObject, là một ES3Defaults được tải thông qua Resources.Load tại đường dẫn là giá trị của constant defaultSettingsPath. Tên save file của Cats & Soup vẫn để mặc định là SaveFile.es3, điều đó cho thấy Cats & Soup có thể sử dụng defaultSettings để mã hoá dữ liệu. Ta sẽ tạo một dự án Unity, import Easy Save 3 package và dùng đoạn mã sau để giải mã tệp SaveFile.es3:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
using System; using System.IO; using System.Text; using UnityEngine;
Chạy thử đoạn mã trên bằng cách vào play mode trong Unity Editor, ta gặp phải ngoại lệ:
1 2 3 4 5 6 7 8 9 10 11 12
CryptographicException: Bad PKCS7 padding. Invalid length 0. at Mono.Security.Cryptography.SymmetricTransform.ThrowBadPaddingException (System.Security.Cryptography.PaddingMode padding, System.Int32 length, System.Int32 position) [0x00056] in <4b234520e36749be9cf6b053d911690f>:0 at Mono.Security.Cryptography.SymmetricTransform.FinalDecrypt (System.Byte[] inputBuffer, System.Int32 inputOffset, System.Int32 inputCount) [0x00146] in <4b234520e36749be9cf6b053d911690f>:0 at Mono.Security.Cryptography.SymmetricTransform.TransformFinalBlock (System.Byte[] inputBuffer, System.Int32 inputOffset, System.Int32 inputCount) [0x0002e] in <4b234520e36749be9cf6b053d911690f>:0 at System.Security.Cryptography.CryptoStream.FlushFinalBlock () [0x00013] in <4b234520e36749be9cf6b053d911690f>:0 at System.Security.Cryptography.CryptoStream.Dispose (System.Boolean disposing) [0x0000b] in <4b234520e36749be9cf6b053d911690f>:0 at System.IO.Stream.Close () [0x00000] in <4b234520e36749be9cf6b053d911690f>:0 at System.IO.Stream.Dispose () [0x00000] in <4b234520e36749be9cf6b053d911690f>:0 at ES3Internal.AESEncryptionAlgorithm.Decrypt (System.IO.Stream input, System.IO.Stream output, System.String password, System.Int32 bufferSize) [0x0005f] in G:\Unity Projects\Test\Assets\Plugins\Easy Save 3\Scripts\ES3Crypto.cs:165 at ES3Internal.AESEncryptionAlgorithm.Decrypt (System.Byte[] bytes, System.String password, System.Int32 bufferSize) [0x00010] in G:\Unity Projects\Test\Assets\Plugins\Easy Save 3\Scripts\ES3Crypto.cs:69 at ES3.DecryptBytes (System.Byte[] bytes, System.String password) [0x00017] in G:\Unity Projects\Test\Assets\Plugins\Easy Save 3\Scripts\ES3.cs:915 at ES3Decryptor.Start () [0x00013] in G:\Unity Projects\Test\Assets\Script\ES3Decryptor.cs:12
HIDEA đã thay đổi mật khẩu mặc định dùng để mã hoá tệp SaveFile.es3 (chẳng ai kém thông minh mà đặt mật khẩu là password cả). Vì mật khẩu được tải từ đường dẫn ES3/ES3Defaults, ta có thể lấy mật khẩu bằng cách extract asset của game. Giải nén thư mục assets từ tệp apk của game, kéo thả vào AssetStudioMod và lọc MonoBehaviour, ta sẽ thấy asset ES3Defaults với container path là ES3/ES3Defaults:
Tuy nhiên, không có mật khẩu nào trong asset này. Ta sẽ thử xuất raw asset này bằng cách chọn asset ES3Defaults, sau đó chọn Export -> Raw -> Selected assets:
Lần này, mật khẩu đã hiện rõ trong tệp ES3Defaults.dat vừa mới xuất:
Thay #hidea0417 vào đoạn mã giải mã ở trên và chạy thử, ta được tệp SaveFile.json:
Bây giờ ta có thể chỉnh sửa save file tuỳ thích. Lưu ý không dùng trình định dạng JSON vì một số trường trong save file có kiểu Dictionary<int, int> và sẽ bị các trình định dạng JSON hiểu là string.
Lưu ý: nếu bạn sửa các giá trị của gold (vàng), energy (năng lượng), gem (kim cương), khi game khởi động sẽ gửi báo cáo gian lận lên server và sẽ khiến tài khoản của bạn bị khoá.
Sau khi chỉnh sửa xong, ta sẽ dùng đoạn mã sau để mã hoá tệp SaveFile.json thành tệp SaveFile.es3 mới:
Thay thế tệp SaveFile.es3 gốc thành tệp mới và tận hưởng!
Kết luận
Một lần nữa, bài viết này chỉ nhằm mục đích giáo dục. Bạn tự chịu trách nhiệm nếu như tài khoản của bạn bị khoá do làm theo hướng dẫn trong bài viết này. Nếu bạn có thắc mắc cần giải đáp, bạn có thể để lại bình luận phía dưới hoặc tham gia server Discord của mình.
Reverse engineering và giải mã save file của Cats & Soup