記一次靈異般的 Bug 除錯經歷

艾凌風發表於2016-05-10

說到程式設計師的噩夢,除了《程式設計師的 13 種噩夢,你遇到過哪些?》這篇提到的「無法重現的 Bug」,還有「遇到一個不懂技術又是掌控狂的專案經理」或「頻繁變更需求」。自稱有 35 年程式設計經歷的 Mick Stute 對最大的噩夢有不同的體驗。來看看他在 Quora 上拿到 16k 多頂的經歷:


我曾經受僱於一位心理學家,去修復一個“輸出有些奇怪的”軟體,那個軟體是他之前帶過的一個研究生編寫的。這款軟體會讀取一個資料檔案,詢問使用者 50 個問題,進行一些計算並且根據該博士的研究給出一個分數。

這個程式執行在一臺 3B2 電腦上。他向我示範了這個軟體並且他確信在問題間切換時,螢幕上有奇怪的文字閃過,而且文字看起來並不友好。我同意去修復它,問題看上去比較直接。所以我可能得花幾個小時去判斷一下問題的嚴重性,這段時間的工作需要按小時進行支付,隨後我們會就最終的報酬達成協議。

第1天

我坐在這臺 3B2 電腦前,登入了那個研究生的賬戶,軟體的程式碼都存在這裡。我仔細檢查這些 C 語言程式碼,這些程式碼故意寫的讓人看不懂。所有程式碼都擠在一行。軟體共有 15 個檔案,每個檔案裡面有三個函式——全在一行!所有的變數名都是三個看上去隨機的字母。我和僱主談了談,決定花點時間處理一下(明智的決定)。我梳理了全部的程式碼並且做了排版優化,這讓我讀程式碼時容易些。

程式碼的梳理工作如期完成。軟體使用了 curses 庫,在螢幕上列印問題和答案並等待使用者響應。但是它會首先移動到第一行的問題,列印一條鼓吹“白人至上”的訊息,等待 1/2 秒,然後用一條問題覆蓋了這串字元。這個問題應該比較簡單。這裡只有5個位置可以用來輸出,每個位置都會閃過一條潛意識資訊(譯註:潛意識資訊是指:參雜在正常資訊中,用於對目標進行暗示的訊息)。每條資訊都是硬編碼的。沒問題,刪除列印令人不適資訊的mvprintw()函式一切就正常了。應該會正常吧。我重新編譯程式碼,認為自己已經搞定了。但是當我再次執行軟體的時候,問題又發生了——一條潛意識資訊。這一次是同樣的問題,只不過資訊的內容不同了。

我再次檢視程式碼,不管你信不信,它又回到了最初的狀態。15個檔案,混在一起,3個字母的變數——完完全全的變回了最初的狀態。我真想一槍打死我自己,因為我忘了備份一份整理後的程式碼。我又重新梳理了程式碼,這一次我把它們放在了三個檔案中,起了不同的名字。我把整個目錄拷貝了一份,然後對其設定了只讀。我再次編譯了程式碼,看上去沒什麼問題。執行程式,這下好了,15個檔案的原版程式碼,包括我自己修改過的程式碼以及輸出的潛意識資訊,又都回來了。

好吧,在硬碟上某處有一份原始碼,原來的作者設定了一個程式,每當你編譯的時候,就把那份程式碼拷貝過來。我進行了一次全盤掃描,包括 (/usr/include)。因為這是一個研究用的程式,因此我們有除了核心之外的全部原始碼。這裡有很多的標頭檔案,掃描它們需要很長的時間,所以,這是第一天。

第2天

硬碟掃描沒有任何有用的訊息。字串顯然要不是被加密了,要麼就是被藏在哪個庫裡面了。因為我沒有檢查全部的可執行物件,我決定在所有庫中搜尋這段文字。這將花費更多的時間,所以,第二天結束了。

第3天

還是沒有結果。字串被加密了。這意味著我必須要跟蹤全部標頭檔案,從一個 #include 到另一個 #include 直到找到它。這麼做需要花很多時間。

我們已經通知了學校的計算機部門,我們認為有人獲取了進入 Phelp 博士研究用電腦的 root 許可權,這部電腦是科研樓裡的一臺共享機。可以理解,他們並不太相信我說的話。

我開始分析 #include 檔案,但我並沒有找到那段程式碼。所以我知道,它被編譯進了一個庫中。沒問題。為什麼不重新編譯一下呢,反正我們有全部的原始碼。

第4到6天

最困難的部分是讓校園裡的那幫書呆子知道他們遇到問題啦!但是最終我們說服了他們,Mark 是 Unix 管理員(他之所以被僱用是因為娶了院長的女兒),正在學習如何解決這個問題。最後,他決定由我來處理這件事,因為他怎麼也搞不明白如何去編譯這些東西。第六天結束了,所有的標準庫都被重新編譯了。哇!

我拿出了我修改並清理過的程式碼,開始進行重新編譯。看上去沒什麼問題。執行程式,我去!又悲劇了!15 個混亂的原始碼檔案和潛意識資訊又回來了。這一切發生的太突然,就像魔法一樣。我非常非常仔細的研究了這個問題,但是我還是陷入了困境。原始碼裡面也沒有那些程式碼,我覺得我可能被打敗了。

Phelps 博士對我投入的這些時間並不滿意,他認為我可能應該從頭開始重寫這個軟體。“當然”,我說。我盯著終端,像一個受驚的小狗,深深的陷入思考。“你說的沒錯。這樣可能更快。“很好”,他說,“我們可以明天開始重寫。”

第7天

讓始作俑者去死吧!我才沒有被這傢伙打敗!我就是要編譯他的程式碼,要不就乾脆不搞了!“你不需要再給我付錢了,Phelps博士,我只需要一些研究時間”。這是一場黑客間的戰爭!

第8到14天

這回我變聰明瞭,我想,那個人不知如何的修改了這個 curses 庫。我把這些程式碼編譯為彙編,當時我還不懂 3B2 的彙編程式碼,於是我開始學習。我讀了 6 天手冊,試圖從這些程式碼中找到問題,但是似乎一切正常。

第15天

突然我意識到,問題可能出在編譯器。TM就是編譯器!每次當你編譯原始碼並且執行程式的時候,就會把這些潛意識資訊拷貝到程式碼中。我好像聽過這樣的事。

啊哈!我知道了!!!我們正好也有這個編譯器的原始碼。我讀了讀原始碼想要找點線索。你瞧,我找到了。的確,編譯器和連結器裡有一段程式碼會做下面這些事:

1) 檢查所有fopen()呼叫,在開啟的檔案中搜尋Phelp博士的問卷;如果找到了,那麼 2) 在編譯那個呼叫fopen()開啟問卷檔案的程式時,把這15個檔案覆蓋到當前編譯目錄。 3) 然後使用這15個檔案編譯Phelps博士的程式,在連結時用原始-o引數(即原始目標檔名)拿來作自己的目標檔名。
編譯器被修改了,它會把程式碼新增到 Phelps 博士的程式中。修改編譯器的人,就是編寫該程式的人。

幾天後,AT&T 的技術人員來了,帶著硬碟,裡面裝有正常的編譯器和連結器原始碼,我們對其進行了重新編譯。問題解決啦。編譯器原始碼中有害的程式碼段都沒有了,我們有了一個全新的、正常的編譯器。

不過它還是有問題。因為這個編譯器還被其他的一些程式碼汙染了,但是那些程式碼我們無法獲取。這些程式碼現在只存在於可執行的編譯器中,在編譯器編譯之前,它會把這些改變重新加入到原始碼中。但是這一次,它沒有修改/usr/src中的副本,只是把它拷貝到了一個隱藏目錄下,修改編譯器原始碼、進行自我編譯並刪除了這個隱藏目錄。這個研究生篡改了編譯器,並讓這編譯器在被重新編譯前篡改自身。我們必須在解決問題前使用另一臺 3B2 機器上的二進位制版本的編譯器。

我們同時還發現,如果 /sbin/login 被編譯,就會建立一個後門程式,任何使用特定密碼的人都可以登入為 root 使用者。該計算機可以通過調變解調器和Tymnet進行訪問。最終,這一切受到了計算機中心的關注。

天才啊!但卻導致了嚴重的後果。

打賞支援我翻譯更多好文章,謝謝!

打賞譯者

打賞支援我翻譯更多好文章,謝謝!

任選一種支付方式

記一次靈異般的 Bug 除錯經歷 記一次靈異般的 Bug 除錯經歷

相關文章