Saatnya untuk sampai ke dasarnya.
Badan intelijen
Pertama, saya ingin memeriksa apakah seseorang telah memecahkan masalah. Tapi saya hanya menemukan cerita tentang kerumitan besar dari permainan , itulah mengapa butuh waktu lama untuk memuat, cerita bahwa arsitektur p2p jaringan adalah sampah (meskipun bukan), beberapa cara rumit untuk memuat ke mode cerita, dan kemudian ke satu sesi , dan beberapa mod lagi untuk melewati video logo R * saat boot. Setelah membaca forum sedikit lagi, saya menemukan bahwa Anda dapat menghemat 10-30 detik jika Anda menggunakan semua metode ini bersama-sama!
Sementara itu, di komputer saya ...
Tolok ukur
Pemuatan adegan: ~ 1m 10s Pemuatan online: ~ 6m Tidak ada menu boot, dari logo R * hingga gameplay (tidak ada login Klub Sosial. Persen tua, tapi layak: AMD FX-8350 SSD Murah: KINGSTON SA400S37120G Perlu membeli RAM: 2x Kingston 8192 MB (DDR3-1337) 99U5471 GPU Normal: NVIDIA GeForce GTX 1070
Saya tahu perangkat keras saya kedaluwarsa, tetapi apa yang bisa memperlambat unduhan saya hingga 6x saat online? Saya tidak dapat mengukur perbedaan saat memuat dari mode cerita ke online seperti yang dilakukan orang lain . Bahkan jika berhasil, perbedaannya kecil.
aku tidak sendirian
Menurut survei ini , masalahnya cukup luas sehingga mengganggu lebih dari 80% pemain. Sudah tujuh tahun sekarang!
Saya melakukan sedikit pencarian informasi tentang ~ 20% yang beruntung yang memuat dalam waktu kurang dari tiga menit, dan menemukan beberapa tolok ukur dengan PC game teratas dan waktu muat online sekitar dua menit. Saya akan
Mengapa story mode mereka masih membutuhkan waktu sekitar satu menit untuk dimuat? (Omong-omong, video dengan logo tidak diperhitungkan saat boot dari M.2 NVMe). Selain itu, hanya perlu satu menit bagi mereka untuk mengunduh dari Story Mode online, sementara saya punya sekitar lima. Saya tahu bahwa perangkat keras mereka jauh lebih baik, tetapi tidak lima kali.
Pengukuran presisi tinggi
Berbekal alat yang ampuh seperti Task Manager , saya berangkat untuk menemukan hambatan.
Dibutuhkan hampir satu menit untuk memuat sumber daya bersama, yang diperlukan untuk mode cerita dan online (hampir setara dengan PC kelas atas), kemudian GTA sepenuhnya memuat satu inti CPU selama empat menit, tidak melakukan yang lain.
Penggunaan Disk? Tidak! Penggunaan jaringan? Ada sedikit, tetapi setelah beberapa detik itu turun terutama ke nol (kecuali untuk memuat spanduk informasi yang berputar). Penggunaan GPU? Nol. Penyimpanan? Tidak ada sama sekali ...
Apa itu, penambangan Bitcoin atau semacamnya? Saya bisa mencium kode di sini. Kode yang sangat buruk.
Aliran tunggal
Prosesor AMD lama saya memiliki delapan inti, dan masih bagus, tapi ini model lama. Itu dibuat kembali ketika kinerja single thread AMD jauh lebih rendah daripada Intel. Ini mungkin alasan utama perbedaan waktu muat tersebut.
Yang aneh adalah cara CPU digunakan. Saya mengharapkan sejumlah besar pembacaan disk atau satu ton permintaan jaringan untuk mengatur sesi di jaringan p2p. Tapi apakah itu? Mungkin ada kesalahan di sini.
Profiling
Profiler adalah cara terbaik untuk menemukan kemacetan CPU. Hanya ada satu masalah - kebanyakan dari mereka mengandalkan instrumentasi kode sumber untuk mendapatkan gambaran sempurna tentang apa yang terjadi dalam proses tersebut. Dan saya tidak memiliki kode sumbernya. Saya juga tidak membutuhkan pembacaan mikrodetik yang sempurna, saya mengalami hambatan 4 menit .
Jadi, selamat datang di stack sampling. Untuk aplikasi sumber tertutup, ini adalah satu-satunya pilihan. Setel ulang tumpukan proses yang sedang berjalan dan lokasi penunjuk instruksi saat ini untuk membangun pohon panggilan pada interval yang ditentukan. Kemudian hamparkan dan dapatkan statistik tentang apa yang terjadi. Saya hanya mengetahui satu profiler yang dapat melakukan ini di Windows. Dan itu belum diperbarui selama lebih dari sepuluh tahun. Ini Luke Stackwalker ! Seseorang tolong beri Luke sedikit cinta :)
Biasanya Luke akan mengelompokkan fungsi yang sama, tetapi saya tidak memiliki simbol debug, jadi saya harus melihat alamat terdekat untuk mencari tempat umum. Dan apa yang kita lihat? Bukan satu, tapi dua kemacetan!
Masuk kedalam lubang kelinci
Setelah meminjam dari seorang teman saya salinan standar disassembler yang sah (tidak, saya benar-benar tidak mampu membelinya ... saya akan pernah menguasai hydra ), saya pergi untuk membongkar GTA.
Tampak sangat salah. Ya, sebagian besar game teratas memiliki perlindungan rekayasa terbalik bawaan untuk menjaganya tetap aman dari pembajak, penipu, dan modder. Bukan berarti itu pernah menghentikan mereka ...
Sepertinya semacam penyamaran / enkripsi telah diterapkan di sini, menggantikan sebagian besar petunjuk dengan omong kosong. Jangan khawatir, Anda hanya perlu mengatur ulang memori game saat sedang melakukan bagian yang ingin kita tonton. Instruksi harus disederhanakan sebelum diluncurkan dengan satu atau lain cara. Saya memiliki Proses Dump di dekatnya , jadi saya mengambilnya, tetapi ada banyak alat lain untuk tugas serupa.
Masalah 1: apakah itu ... strlen?!
Analisis lebih lanjut dari dump mengungkapkan salah satu alamat dengan label tertentu
strlen
yang berasal dari suatu tempat! Turun ke tumpukan panggilan, alamat sebelumnya ditandai sebagai
vscan_fn
, dan setelah itu label berakhir, meskipun saya cukup yakin itu
sscanf
.
Dia mengurai sesuatu. Tapi apa? Penguraian logis akan memakan waktu lama, jadi saya memutuskan untuk membuang beberapa sampel dari proses yang sedang berjalan menggunakan x64dbg . Setelah beberapa langkah debugging, ternyata ini adalah ... JSON! Ini mengurai JSON. Sebuah kekalahan megabyte sepuluh JSON dengan 63.000 item .
...,
{
"key": "WP_WCT_TINT_21_t2_v9_n2",
"price": 45000,
"statName": "CHAR_KIT_FM_PURCHASE20",
"storageType": "BITFIELD",
"bitShift": 7,
"bitSize": 1,
"category": ["CATEGORY_WEAPON_MOD"]
},
...
Apa itu? Dilihat dari beberapa tautan, ini adalah data untuk "direktori perdagangan online". Saya berasumsi ini berisi daftar semua kemungkinan item dan peningkatan yang dapat Anda beli di GTA Online.
Untuk menghilangkan kebingungan, saya yakin ini adalah item uang dalam game yang tidak terkait langsung dengan transaksi mikro .
10 megabyte? Pada prinsipnya, tidak terlalu banyak. Meski
sscanf
tidak digunakan secara optimal, namun tentunya tidak terlalu buruk? Ya ...
Ya, prosedur seperti itu akan memakan waktu ... Sejujurnya, saya tidak tahu bahwa sebagian besar penerapan
sscanf
memerlukan waktu
strlen
jadi saya tidak bisa menyalahkan pengembang yang menulis ini. Saya kira itu hanya memindai byte demi byte dan bisa berhenti di
NULL
.
Masalah 2: Mari kita gunakan array hash ...?
Ternyata penjahat kedua dipanggil tepat setelah yang pertama. Bahkan dalam konstruksi yang sama
if
, seperti yang Anda lihat dari dekompilasi jelek ini:
Semua label adalah milik saya dan saya tidak tahu fungsi / parameter sebenarnya dipanggil.
Masalah kedua? Segera setelah elemen diurai, elemen disimpan dalam larik (atau daftar sebaris C ++? Tidak yakin). Setiap entri terlihat seperti ini:
struct {
uint64_t *hash;
item_t *item;
} entry;
Dan sebelum menabung? Ia memeriksa seluruh array dengan membandingkan hash dari setiap elemen, apakah itu ada dalam daftar atau tidak. Dengan 63 ribu entri, ini kira-kira
(n^2+n)/2 = (63000^2+63000)/2 = 1984531500
, jika saya tidak salah dalam perhitungan saya. Dan ini sebagian besar adalah pemeriksaan yang tidak berguna. Anda memiliki hash yang unik, mengapa tidak menggunakan tabel hash.
Selama rekayasa balik, saya menamakannya
hashmap
, tetapi itu jelas
_hashmap
. Dan kemudian menjadi lebih menarik. Daftar-larik hash ini kosong sebelum memuat JSON. Dan semua elemen di JSON unik! Mereka bahkan tidak perlu memeriksa apakah mereka ada dalam daftar atau tidak! Mereka bahkan memiliki fitur penyisipan elemen langsung! Gunakan saja! Serius, teman-teman, apa-apaan ini!?
Bukti dari konsep
Semua ini bagus, tetapi tidak ada yang akan menganggap saya serius sampai saya menulis kode sebenarnya untuk mempercepat pemuatan untuk membuat judul clickbait untuk sebuah posting.
Rencananya adalah sebagai berikut. 1. Menulis .dll, 2. mengimplementasikannya di GTA, 3. mengaitkan beberapa fungsi, 4. ???, 5. untung. Semuanya sangat sederhana.
Masalah dengan JSON tidak sepele, saya tidak bisa benar-benar mengganti parser mereka. Tampaknya lebih realistis untuk mengganti sscanf dengan yang tidak bergantung pada strlen. Tapi ada cara yang lebih mudah.
- kail strlen
- tunggu antrean panjang
- Awal dan panjang "Cache"
- jika panggilan lain datang dalam kisaran string, kembalikan nilai yang di-cache
Sesuatu seperti ini:
size_t strlen_cacher(char* str)
{
static char* start;
static char* end;
size_t len;
const size_t cap = 20000;
// ""
if (start && str >= start && str <= end) {
// calculate the new strlen
len = end - str;
// ,
//
if (len < cap / 2)
MH_DisableHook((LPVOID)strlen_addr);
// !
return len;
}
//
// JSON
// strlen
len = builtin_strlen(str);
//
//
if (len > cap) {
start = str;
end = str + len;
}
// ,
return len;
}
Adapun masalah hash array, kita hanya melewatkan semua pemeriksaan dan memasukkan elemen secara langsung, karena kita tahu nilainya unik.
char __fastcall netcat_insert_dedupe_hooked(uint64_t catalog, uint64_t* key, uint64_t* item)
{
//
uint64_t not_a_hashmap = catalog + 88;
// , ,
if (!(*(uint8_t(__fastcall**)(uint64_t*))(*item + 48))(item))
return 0;
//
netcat_insert_direct(not_a_hashmap, key, &item);
//
// .dll, :)
if (*key == 0x7FFFD6BE) {
MH_DisableHook((LPVOID)netcat_insert_dedupe_addr);
unload();
}
return 1;
}
Kode sumber PoC lengkap ada di sini .
hasil
Jadi bagaimana cara kerjanya?
Waktu buka online sebelumnya: sekitar 6m Waktu dengan pemeriksaan patch untuk duplikat: 4m 30s Waktu dengan pengurai JSON: 2m 50d Waktu dengan dua tambalan bersama: 1m 50-an (6 * 60 - (1 * 60 + 50)) / (6 * 60) = 69,4% peningkatan waktu (kelas!)
Ya, sial, berhasil! :))
Ini mungkin tidak akan menyelesaikan semua masalah boot - mungkin ada kemacetan lain pada sistem yang berbeda, tetapi ini adalah lubang yang menganga sehingga saya tidak tahu bagaimana R * melewatkannya selama bertahun-tahun.
Ringkasan
- Ada satu hambatan berulir saat meluncurkan GTA Online
- Ternyata GTA kesulitan untuk mengurai file JSON 1MB
- Parser JSON itu sendiri dikerjakan dengan buruk / naif dan
- Setelah penguraian, ada prosedur yang lambat untuk menghapus duplikat
R * harap perbaiki
Jika informasinya entah bagaimana sampai kepada para insinyur Rockstar, maka masalahnya dapat diselesaikan dalam beberapa jam dengan upaya satu pengembang. Tolong teman-teman lakukan sesuatu tentang ini: <
Anda dapat pergi ke tabel hash untuk menghapus duplikat, atau melewati deduplikasi sepenuhnya saat startup sebagai perbaikan cepat. Untuk pengurai JSON, cukup ganti pustaka dengan yang lebih berkinerja. Saya rasa tidak ada pilihan yang lebih mudah.
ty <3