Gəlin minesweeper-i hack edək
İnternetin olmadığı, amma yenə də kompüterdə nəsə oynamaq istədiyin köhnə günləri xatırlayırsan? Şahmat, tort bişirmə oyunları və s.
Bir də minesweeper adlı əsəb pozan bir oyun vardı. Sən kor-koranə bir xana seçməli və partlamayacağına ümid etməli idin. Əlbəttə, bir də oyunu udmağa heç bir köməyi olmayan o “gülən emoji” vardı.
Mən o oyunu uda bilmirdim, indi də bacarmıram. Sonra düşündüm ki, niyə burada RE bacarıqlarımı tətbiq etmirəm və bu tamamilə faydasız blog yazısını yazmaq qərarına gəldim.
Qeyd: minesweeper reversing haqqında çoxlu blog yazısı var, ona görə də bu yazı yalnız mənim təcrübəmi və qarşılaşdığım çətinlikləri göstərəcək — bunu tədris materialı kimi qəbul etməyin.
Ümid edirəm ki, xoşunuza gələcək.
Source code-u disassemble / decompile etmək üçün Ghidra-dan, debugging üçün isə x32dbg-dən istifadə edəcəyəm. Burada function adları təsadüfi olacaq, çünki PDB-m problem yaradır (PDB-ni google edin).
İki şeyi bilirik. Bu oyun 2D board-dur, ona görə də buna uyğun nəsə gözləməliyik. Həmçinin bu oyun randomize olunub, yəni hər dəfə mina və bayraqlar fərqli yerlərdə olacaq. Bu nöqtədə mütləq random function olmalıdır. Gəlin ona baxaq.
Bütün codebase-də rand açar sözünü axtarın və iki random function görəcəksiniz: rand və srand. Əvvəlcə rand ilə başlayacağıq. something_random mənim tərəfimdən dəyişdirilmiş function adıdır. Burada göründüyü kimi, bu function parametr qəbul edir və random dəyəri həmin parametrə bölür.
something_random-un referans edildiyi function-a baxaq.
Referans edilən function belədir:
iVar1 --> random_value / DAT_..5334
iVar2 --> random_value / DAT_..5338
DAT_01005340 is the pointer to the start of the game board array.
[iVar1 + 1 + (iVar2 + 1) * 32] --> coordinate of one tile
Niyə 32-yə vurulduğunu soruşa bilərsiniz — oyun 32x32 piksel olduğu üçün.
while loop yoxlayır ki, tile mina kimi işarələnib, ya yox. Əgər minalıdırsa,
loop davam edəcək; deyilsə, LSB-ni set etməklə onu minalı edəcək. Bu isə
0x80 ilə OR əməliyyatı aparmaqla həyata keçirilir.
Bunu daha yaxşı başa düşmək üçün gəlin hər bir something_random çağırışına breakpoint qoyaq və dəyərlərə baxaq.
Stack-ə push edilən və something_random function-da parametr kimi istifadə olunan dəyəri görürük. Hər iki halda belə bir nəticə alacağıq:
ivar1 = random_value / 9
ivar2 = random_value / 9
Güman edirəm ki, 9 oyunun ölçüsünə görədir. Yadınızdadırsa, o, 9x9-dur.
Bundan sonra görəcəyik ki, loop 10 dəfə işləyir və çox güman ki, minaları əlavə edir. Bu minalar bu address-dən başlayaraq əlavə olunur: DAT_01005340.
Bu nöqtədə əvvəl izah etdiyim məntiq işə düşür. Əgər dəyər 0x0f-dirsə, 0x80 ilə OR əməliyyatı aparmaqla onu 0x8f-ə çevirəcək.
Yəni böyük ehtimalla DAT_01005340-a baxsaq, oyun board-unun hex təsvirini görəcəyik.
Kodu işlətməyə davam etdim və o, ilk iki breakpoint arasında on dəfə loop etməyə başladı, sonra isə sonuncu breakpoint-ə keçdi. Bu o deməkdir ki, minalarımız yerləşdirilib.
0x0F və 0x10 — yan sərhədlər və boş hissələr olacaq (delimiter “\t”)
0x0E — bayraq qoysaq
0x0D — sual işarəsi qoysaq
0x8F — mina yerləri olacaq
0x8E — bayraq qoysaq
0x8D — sual işarəsi qoysaq
Bunu asanlıqla yoxlaya bilərik.
Bu mərhələdə oyunu asanlıqla uda bilərəm, amma yenə də hər dəfə memory dump-a baxmağa tənbələm. Gəlin bu oyunu patch edək ki, sadəcə udmaq daha asan olsun. Patch ideyası üçün bir tədqiqatçı sadəcə 0x8f-ləri 0x8e-yə çevirmək qərarına gəlmişdi. Bu o deməkdir ki, oyunu açdığımız zaman minaları artıq bayraqlanmış görəcəyik. Gəlin bunu edək.
Bilirik ki, OR əməliyyatı 0x0f-i 0x8f-ə çevirir, elə deyilmi?
Bir az əlləşdikdən və ChatGPT-dən istifadə etdikdən sonra tapdım ki, 0x0f-i 0x81 ilə XOR etsək, 0x8e alacağıq. Bunu etmək üçün x32dbg-də sadəcə bu dəyişikliyi patch kimi tətbiq edin:
010036FA | or byte ptr ds:[eax],80 --> xor byte ptr ds:[eax],81
Nəticə budur:
--
cushz :: Exploit development, reverse engineering və Windows internals haqqında yazılar.
İlk dəfə Medium saytında dərc olunub.