一個陽光明媚的週末,透光的窗簾把我從睡夢中叫醒,大腦說今天是週六,可以慵懶個一上午,於是開心地開啟我的 Mac 準備看兩集 Rick and Morty 再起床洗漱。
我迫不及待開啟了對應的網站,發現瀏覽器提示 No internet,這才發現因為我的動作太過於行雲流水,電腦還沒來得及連上 wifi
於是我決定重溫一下這款經典的遊戲。
https://www.qq.com/video/q315...
科普:Chrome 瀏覽器是 Google 家的官方瀏覽器,使用體驗全世界南波萬。當使用者在無網路時訪問某一網址,瀏覽器會提示“無網路連結”(也就是上面這張圖),按下空格鍵,就會喚醒一個小恐龍跑步刷分的遊戲。
如果在有網路連線時也想玩的話,可以直接在位址列輸入:chrome://dino
出於職業本身的敏感和好奇,我突然心生疑問:這應該是用 JS 做的吧?
我順勢開啟控制檯,不如先看一下所有的全域性變數?
太多了,我可沒這個耐心...
我停止了操作,開始思考這一切的可行性:
- 我在幹什麼?嘗試自定義這個遊戲
- 為什麼要看全域性變數?因為假設關於遊戲的物件被暴露在了全域性
- 如果沒暴露在全域性?行,那我就放棄了(週末的早晨我可不想在 sources 裡面扒原始碼)
也就是說,如果沒有在全域性變數裡找到那個物件,我就可以選擇性放棄了。
再看一下上面這張圖,不難發現,這 251 個全域性變數,有很多都是老朋友了,name、history、location、onclick、onerror... 這些變數明顯和這隻小恐龍沒啥關係,列印出來純粹是來充個數,還浪費了我找目標的時間,所以我遇到了第一個問題:如何快速找到由開發者自定義的全域性變數?
假如我有一個正常的 window...不對,不用假如,新開一個空視窗不就得了!
在新開啟視窗的控制檯,我得到了這樣的結果:
也就是說,在這個恐龍快跑的小遊戲裡,Google 的前端工程師向 window 注入了 最多 55 個全域性變數(251-196)...
為什麼說最多 55 個?因為 Chrome 的版本以及安裝的 Chrome 外掛都會影響全域性變數的個數,比如 React 開發者工具就會向 window 中注入兩個全域性變數。
於是問題來到了:怎麼把這 55 個被注入的全域性變數拿過來呢?複製貼上之後再一個一個比對?太 low 了,讓我想想...
在原有視窗的基礎上,再搞一個新視窗...這不就是 iframe 嗎!iframe 雖然沒有 window,但是它有 contentWindow,原則上來說,屬性差別應該不大。所以現在只需要建立一個 iframe,拿它裡面 contentWindow 的全域性變數去過濾當前 window 的全域性變數,就可以篩選出那多餘的 55 個“嫌疑人”了。
思路有了,寫程式碼也就容易多了:
在控制檯輸入這些程式碼後會車,我得到了這樣的結果:
這下篩選出了 45 個全域性變數。
45 個總比 200 多個好找一些,開啟這個陣列,我發現了一個命名很可疑的傢伙:
Runner,不就是那個“奔跑著的小恐龍”嗎(壞笑)?
那就看看它到底是個什麼貨色!
emmm居然是函式,哦?函式,首字母還大寫,這熟悉的味道,這不就是個建構函式嗎!正所謂,建構函式的寶藏都在 prototype 裡,在觀察了一遍這個寶藏之後,我發現了一個名為 gameOver 的方法,
這個函式名,難不成它就是那個讓小恐龍去死的方法?既然這樣,呵,我反手就把它用空函式覆蓋了。於是...
https://www.qq.com/video/n315...
真的是...
然後我又發現了另一個有趣的方法:
顧名思義,這是用來給小恐龍設定奔跑速度的方法,但是怎麼呼叫呢?由建構函式建立的例項物件,可通過原型鏈訪問建構函式 prototype 上的變數和方法,也就是說,如果能找到這個小恐龍對應的例項,那麼就能直接呼叫這個 setSpeed 方法了。既然 Google 的前端工程師已經把這麼多變數搞到全域性了,那麼...會不會也在全域性儲存了這個小恐龍例項?我重新審視了一遍那 43 個多出來的全域性變數,並沒有找到。我下意識地隨手輸入了一下這個 Runner 建構函式,真是山重水複疑無路柳暗花明又一村吶:
我甚至都不需要看 Runner 函式的全部程式碼,就知道原來例項被儲存在一個名叫 instance_ 的 Runner 自身屬性裡,論起好變數名的重要性,行,那我就不客氣了,直接呼叫!
於是乎...
https://www.qq.com/video/f315...
當我凝視著這個飛奔的無敵小恐龍時,我突然覺得...一切變得...
最後
如果想要獲得原版更騷氣的閱讀體驗,歡迎點選這個傳送門(獲取更加完整的閱讀體驗)。