Selama bertahun-tahun memainkan satu MMORPG seluler, saya telah memperoleh beberapa pengalaman dalam rekayasa baliknya, yang ingin saya bagikan dalam serangkaian artikel. Contoh topik:
- Format pesan parsing antara server dan klien.
- Menulis aplikasi mendengarkan untuk melihat lalu lintas game dengan cara yang nyaman.
- Intersepsi lalu lintas dan modifikasinya menggunakan server proxy non-HTTP.
- Langkah pertama ke server Anda sendiri ("bajakan").
Pada artikel ini, saya akan membahas
parsing format pesan antara server dan klien . Tertarik, saya minta kucing.
Alat Dibutuhkan
Untuk dapat mengulangi langkah-langkah yang dijelaskan di bawah ini, Anda perlu:
- PC (saya lakukan pada Windows 7/10, tetapi MacOS mungkin berfungsi juga jika item di bawah ini tersedia di sana);
- Wireshark untuk analisis paket;
- 010Editor untuk paket parsing dengan templat (opsional, tetapi memungkinkan Anda untuk dengan cepat dan mudah menggambarkan format pesan);
- perangkat seluler itu sendiri dengan permainan.
Selain itu, sangat diinginkan untuk memiliki data yang dapat dibaca dari permainan yang ada, seperti daftar objek, makhluk, dll. Dengan pengidentifikasi mereka. Ini sangat menyederhanakan pencarian poin utama dalam paket, dan kadang-kadang memungkinkan Anda untuk menyaring pesan yang diinginkan dalam aliran data yang konstan.
Parsing format pesan antara server dan klien
Untuk memulai, kita perlu melihat lalu lintas perangkat seluler. Cukup sederhana untuk melakukan ini (walaupun saya mencapai keputusan yang jelas untuk waktu yang sangat lama): pada PC kami, kami membuat titik akses Wi-Fi, menyambungkannya dari perangkat seluler, memilih antarmuka yang diinginkan di Wireshark - dan kami memiliki semua lalu lintas seluler di depan mata kami.
Setelah memasuki game dan menunggu beberapa saat sehingga permintaan yang tidak terkait dengan server game itu sendiri dihentikan, Anda dapat mengamati gambar berikut:
Pada tahap ini, kita sudah bisa menggunakan filter Wireshark untuk melihat hanya paket antara game dan server, serta hanya dengan payload:
tcp && tcp.payload && tcp.port == 44325
Jika Anda berdiri di tempat yang sunyi, jauh dari pemain lain dan NPC, dan tidak melakukan apa-apa, Anda dapat melihat pesan yang terus berulang dari server dan klien (masing-masing berukuran 76 dan 84 byte). Dalam kasus saya, jumlah minimum paket yang berbeda dikirim pada layar pemilihan karakter.
Frekuensi permintaan dari klien sangat mirip dengan ping. Mari kita ambil beberapa pesan untuk diperiksa (3 grup, di atas adalah permintaan dari klien, di bawahnya adalah respons server):
Hal pertama yang menarik perhatian Anda adalah identitas paket. 8 byte tambahan dalam respons saat dikonversi ke sistem desimal sangat mirip dengan cap waktu dalam detik:
5CD008F8 16 = 1557137656 10
(dari pasangan pertama).
Kami memeriksa jam - ya, benar. 4 byte sebelumnya cocok dengan 4 byte terakhir dalam permintaan. Saat menerjemahkan, kami mendapatkan:
A4BB 16 = 42171 10
, yang juga sangat mirip dengan waktu, tetapi dalam milidetik. Ini kira-kira bertepatan dengan waktu sejak peluncuran game, dan kemungkinan besar itu.
Masih mempertimbangkan 6 byte pertama dari permintaan dan respons. Sangat mudah untuk melihat ketergantungan dari nilai empat byte pertama dari pesan (sebut parameter ini
L
) pada ukuran pesan: respons dari server lebih dari 8 byte, nilai
L
juga meningkat sebesar 8, namun, ukuran paket lebih dari 6 byte dari nilai
L
dalam kedua kasus. Anda juga dapat melihat bahwa dua byte setelah
L
mempertahankan nilainya baik dalam permintaan dari klien dan dari server, dan mengingat bahwa nilainya berbeda satu, kita dapat mengatakan dengan yakin bahwa ini adalah kode pesan
C
(kode pesan yang terkait kemungkinan besar akan ditentukan berurutan). Struktur umum cukup jelas untuk menulis templat minimal untuk 010Editor:
- 4 byte pertama -
L
- ukuran payload pesan; - 2 byte berikutnya - kode pesan
C
; - payload itu sendiri.
struct Event { uint payload_length <bgcolor=0xFFFF00, name="Payload Length">; ushort event_code <bgcolor=0xFF9988, name="Event Code">; byte payload[payload_length] <name="Event Payload">; };
Oleh karena itu, format pesan ping klien: kirim waktu ping lokal; format respons server: kirim waktu dan waktu yang sama untuk mengirim respons dalam hitungan detik. Sepertinya tidak sulit, bukan?
Mari kita coba membuat contoh yang lebih rumit. Berdiri di tempat yang sunyi dan menyembunyikan paket ping, Anda dapat menemukan pesan yang diteleportasi dan membuat item (kerajinan). Mari kita mulai dengan yang pertama. Dengan memiliki data gim, aku tahu nilai teleport apa yang harus dicari. Untuk tes, saya menggunakan poin dengan nilai
0x2B
,
0x67
,
0x6B
dan
0x1AF
. Bandingkan dengan nilai-nilai dalam pesan:
0x2B
,
0x67
,
0x6B
dan
0x3AF
:
Kekacauan. Dua masalah terlihat:
- nilai bukan 4 byte, tetapi dari ukuran yang berbeda;
- tidak semua nilai cocok dengan data dari file, dan dalam hal ini perbedaannya adalah 128.
Selain itu, ketika membandingkan dengan format ping, Anda dapat melihat beberapa perbedaan:
0x08
dimengerti sebelum nilai yang diharapkan;- Nilai 4-byte, 4 kurang dari
L
(sebut saja D
Bidang ini tidak muncul di semua pesan, yang agak aneh, tetapi di mana itu, ketergantungan L - 4 = D
dipertahankan. Di satu sisi, untuk pesan dengan struktur sederhana (seperti ping) tidak diperlukan, tetapi di sisi lain - terlihat tidak berguna).
Beberapa dari Anda, saya pikir, sudah bisa menebak alasan ketidakcocokan nilai-nilai yang diharapkan, tetapi saya akan melanjutkan. Mari kita lihat apa yang terjadi di pesawat:
Nilai yang diharapkan dari 14183 dan 14285 juga tidak sesuai dengan 28391 dan 28621 yang sebenarnya, tetapi perbedaannya di sini sudah jauh lebih besar dari 128. Setelah banyak tes (termasuk dengan jenis pesan lainnya) ternyata semakin besar angka yang diharapkan, semakin besar perbedaan antara nilai dalam paket. Yang aneh adalah bahwa nilai-nilai hingga 128 tetap sendiri. Paham, ada apa? Situasi yang jelas adalah bagi mereka yang telah mengalami ini, dan, tanpa sadar, saya harus membongkar "sandi" ini selama dua hari (pada akhirnya, analisis nilai-nilai dalam bentuk biner membantu dalam "peretasan"). Perilaku yang dijelaskan di atas disebut
Variable Length Quantity - representasi dari angka yang menggunakan jumlah byte yang tidak terbatas, di mana bit kedelapan byte (bit kelanjutan) menentukan keberadaan byte berikutnya. Dari deskripsi, jelas bahwa membaca VLQ hanya mungkin dalam urutan Little-Endian. Secara kebetulan, semua nilai dalam paket berada dalam urutan itu.
Sekarang kita tahu cara mendapatkan nilai awal, kita bisa menulis templat untuk jenisnya:
struct VLQ { local char size = 1; while(true) { byte obf_byte; if ((obf_byte & 0x80) == 0x80) { size++; } else { break; } } FSeek(FTell() - size); byte bytes[size]; local uint64 _ = FromVLQ(bytes, size); };
Dan fungsi mengubah array byte ke nilai integer:
uint64 FromVLQ(byte bytes[], char size) { local uint64 source = 0; local int i = 0; local byte x; for (i = 0; i < size; i++) { x = bytes[i]; source |= (x & 0x7F) * Pow(2, i * 7);
Tetapi kembali ke penciptaan subjek. Lagi
D
muncul dan lagi
0x08
di depan nilai yang berubah. Dua byte terakhir dari pesan
0x10 0x01
secara mencurigakan mirip dengan jumlah item kerajinan, di mana
0x10
memiliki peran yang mirip dengan
0x08
tetapi masih sulit dipahami. Tetapi sekarang Anda dapat menulis templat untuk acara ini:
struct CraftEvent { uint data_length <bgcolor=0x00FF00, name="Data Length">; byte marker1; VLQ craft_id <bgcolor=0x00FF00, name="Craft ID">; byte marker2; VLQ quantity <bgcolor=0x00FF00, name="Craft Quantity">; };
Yang akan terlihat seperti ini:
Dan tetap saja, ini adalah contoh sederhana. Akan lebih sulit untuk mengurai acara pergerakan karakter. Informasi apa yang kita harapkan untuk dilihat? Paling tidak, karakter berkoordinasi di mana ia melihat, mempercepat dan menyatakan (berdiri, berlari, melompat, dll.). Karena tidak ada garis yang terlihat dalam pesan, negara kemungkinan besar dijelaskan melalui
enum
. Dengan menghitung opsi, secara bersamaan membandingkannya dengan data dari file game, serta melalui banyak tes, Anda dapat menemukan tiga vektor XYZ menggunakan templat rumit ini:
struct MoveEvent { uint data_length <bgcolor=0x00FF00, name="Data Length">; byte marker; VLQ move_time <bgcolor=0x00FFFF>; FSkip(2); byte marker; float position_x <bgcolor=0x00FF00>; byte marker; float position_y <bgcolor=0x00FF00>; byte marker; float position_z <bgcolor=0x00FF00>; FSkip(2); byte marker; float direction_x <bgcolor=0x00FFFF>; byte marker; float direction_y <bgcolor=0x00FFFF>; byte marker; float direction_z <bgcolor=0x00FFFF>; FSkip(2); byte marker; float speed_x <bgcolor=0x00FFFF>; byte marker; float speed_y <bgcolor=0x00FFFF>; byte marker; float speed_z <bgcolor=0x00FFFF>; byte marker; VLQ character_state <bgcolor=0x00FF00>; };
Hasil visual:
Tiga hijau berubah menjadi koordinat lokasi, tiga kuning, kemungkinan besar, menunjukkan di mana karakter melihat dan vektor kecepatannya, dan yang terakhir adalah status karakter. Anda dapat melihat byte konstan (spidol) antara nilai koordinat (
0x0D
sebelum nilai
X
,
0x015
sebelum
Y
dan
0x1D
sebelum
Z
) dan sebelum keadaan (
0x30
), yang secara curiga mirip artinya dengan
0x08
dan
0x10
. Setelah menganalisis banyak penanda dari peristiwa lain, ternyata itu menentukan jenis nilai yang mengikutinya (tiga bit pertama) dan makna semantik, yaitu. dalam contoh di atas, jika Anda menukar vektor sambil mempertahankan penanda mereka (
0x120F
di depan koordinat, dll.), permainan (secara teoritis) biasanya akan menguraikan pesan. Berdasarkan informasi ini, Anda dapat menambahkan beberapa jenis baru:
struct Packed { VLQ marker <bgcolor=0xFFBB00>;
Sekarang templat pesan gerak kami telah berkurang secara signifikan:
struct MoveEvent { uint data_length <bgcolor=0x00FF00, name="Data Length">; Packed move_time <bgcolor=0x00FFFF>; PackedVector3 position <bgcolor=0x00FF00>; PackedVector3 direction <bgcolor=0x00FF00>; PackedVector3 speed <bgcolor=0x00FF00>; Packed state <bgcolor=0x00FF00>; };
Jenis lain yang mungkin kita butuhkan di artikel berikutnya adalah baris yang didahului oleh nilai ukurannya yang
Packed
:
struct PackedString { Packed length; char str[length.v._]; };
Sekarang, mengetahui format pesan sampel, Anda dapat menulis aplikasi mendengarkan Anda untuk kenyamanan memfilter dan menganalisis pesan, tetapi ini adalah topik untuk artikel selanjutnya.
Pembaruan: terima kasih
aml untuk petunjuk bahwa struktur pesan yang dijelaskan di atas adalah
Protokol Buffer , dan juga
Tatikoma untuk tautan ke
artikel terkait yang bermanfaat.