ETH官方钱包

前往
大廳
主題

為美好的世界獻上病毒!(2) - 這個PE檔有夠煩!

虛鹿 | 2022-07-07 21:08:16 | 巴幣 138 | 人氣 540

0x00 - 前言

誒嘿,我又拖更了,
畢竟內心真的很不想講解Windows Internal,
以及PE病毒的基礎概念,
首先是因為這真的有難度,
需要有一定的背景知識,
而且你手中的病毒是具有實質的破壞性的。

其中,
所有的主題都會有延伸閱讀的資料,
或是一些cheat sheet可以參考,
而我都會把它們都放在私人公會中,
讓文章中只保留一些基本的資訊,
以防被白嫖或是被支那的內文農場網站爬走。

0x01 - 時代變了,大人!


繼之前的病毒開發系列之後,
我們也必須與時俱進,
把先前對組合語言以及Dos病毒架構的認知,
以更加現代化的語法(winapi)與系統框架(32位元)將其重新詮釋,
並把病毒移植到更新的平臺(win32)上。

由於 com檔本身無法在windows上執行,
因此,我們必須使用更加現代化的檔案格式,
也就是所謂的「Portable Exe(PE)」!
(一般俗稱為Exe檔)

以下是PE檔的檔頭(Header)結構:

十分的簡單易懂,對吧?
好啦,我知道這一切逐漸變得越來越不親民,
但是如果不去了解、接受他,
那你肯定是無★法★進★步★的說~

然而,再教各位如何解析PE檔之前,
我們必須聊聊傳說中的Windows Internal,
系統改動以及Protected Mode的實裝,
而這些重大的改動,造就了Dos病毒的末路。

這邊我就不細說表格的內容了,
有需求的就自己慢慢看吧~(^ ^)

總結來說,
Protect Mode 的推出實裝了:

   1. Windows Kernel:
       系統被分為:
       user space(0x00000000~0x7FFFFFFF)
       kernel space(0x80000000~0xFFFFFFFF)
       而他們各自被分配了2Gb。

       其中使用者與執行的應用程式能接觸到的部分為user space;而Kernel space由Windows Kernel掌管,其中只有Kernel有權限接觸實體記憶體及系統資源。

   2. Virtual Memory(MMU):
        分離核心與使用者應用程式的關鍵。

   3. Memory Paging:
       MMU 把實體記憶體分成不同page,各自有4kb的實體記憶體大小。

由於這一切真的很複雜,
難到微軟甚至出了一本3000多頁的書,
叫做《Windows Internal Edition. 7》
專門用來講解Windows Internal的相關概念,
因此,我這邊就只講主要的影響:

   1. 核心與應用程式分離

   2. 只有核心有權限訪問實體的記憶體

   3. 所有的應用程式都會被當成在作業系統上執行的唯一程式,並能夠使用全部的RAM(4Gb、32位元系統的極限),且能夠與全部的記憶體互動。舉個例子,如下圖所見,假設一臺電腦上正在同時執行兩個 notepad.exe,而且他們都要取得存放在記憶體位置0x1234的資料,然而他們卻透過同樣的記憶體位置取得了不一樣的資料。至於為什麼會有這種狀況呢?原因就是我先前提到的Memory Paging的技術,由於他們存在不同的Memory Page之中,因此對應回實體記憶體中的位置會是完全不同的。在這種狀況下,各個程式無法覆寫其他程式存放於記憶體中的資料,且能同時、無衝突的執行在系統上。


0x02 - 終末的Dos病毒


還記得我們以前玩的Dos病毒嗎?
病毒所有的功能,
都是建立在呼叫Dos Interupt之中。
基本上,當病毒程式在Dos中被執行後,
就直接Game Over,停都停不下來,
想調用什麼功能就調用什麼,
想幹誰就幹誰。

然而,
對於執行在Protect Mode中的程式而言,
想呼叫系統功能,你必須透過Win32 Api,
對Windows Kernel提出申請,
代替應用程式存取實體記憶體與系統資源,
而只要Kernel覺得你的請求不合適,
他就有權利拒絕你不合理的要求,
因此你無法跟在Dos中一樣為所欲為。

舉個例子,
假設電腦上同時執行和兩個notepad.exe,
其中Notepad_proc2呼叫了CreateFileW,
使用Win32 Api來開啟readme.txt。
而Notepad_proc1 呼叫了DeleteFile,
想要刪掉readme.txt。

在這種情況下,
由於CreateFileW建立的Filehandle未被關閉,
因此核心會認為兩個process互相衝突,
而拒絕DeleteFile Api的執行請求。


0x03 - 勇者死了!因為勇者掉進微軟這個王八蛋設計的 Win32 Api 裡!


為了讓各位親自體驗看看Win32 api,
首先,去官網下載masm32
並在任意的Code editor打出以下程式碼,
把他存成 msgbox.asm。

編譯指令:
./ml.exe /c /coff msgbox.asm
./link.exe msgbox.obj

如果你的畫面彈出了一個小小的視窗,
並有著一個「OK」的按鈕的話,
恭喜你!寫出了你的第一個32位元程式!

我簡單講解一下,
裡面呼叫的MessageBox為Win32 api的功能:

而invoke的意義,則類似於call,
屬於Masm內建的macro。
相對於Call指令,
他多了可以在同一行程式碼中,
把參數傳遞到stack中的功能,
讓你的code增加可讀性。

至於各個Win32 Api所需的參數,
以及各自代表的意義,
所以我就不解釋了。

當然,你也可以選擇不用invoke,
堅持用call來呼叫Win32 Api,
但你會需要手動把參數丟進stack中,
並且要以LIFO的規則傳入,
像這樣:

只是你的程式的可讀性會下降,
而且又會讓原始碼暴增5、6倍的大小。
不是不行,只是不太推薦。

體驗完Win32 Api的用法後,
請把剛剛編譯出的exe檔丟進PE Bear,
讓我們來仔細檢視一下檔頭的資訊


第一張是PE Bear的介面,
第二張圖則是Exe檔的結構,
基本上只要對照一下兩張圖,
你就能看得懂各個區塊的意思,
因此我非常建議各位仔細研究一下這張圖。

接著,
讓我來教各位怎麼計算檔頭的資料位置,
先給各位看看公式:
「 VA = BaseOfImage +  RVA 」

VA(Virtual Address):記憶體中的虛擬地址

BaseOfImage:載入記憶體的位置,無法預判,但理論上預設是0x400000。在執行時會被賦予一個數值,但在執行前可以視為0。

RVA(Relative Virtual Address):你就把他當成offset,去做累加就可以了。

舉個例子,
我現在如果想要到0x3c的e_ifanew,
取得 NT_Header 的相對位置的話,
就要到 BaseOfImage + 0x3c 的位置,
並提取記憶體中的數值。

以下圖的狀況而言,
該記憶體的位置中的便是”0xC0”。
而這代表著NT_Header位於:
BaseOfImage + 0xC0

依照這個計算邏輯,
到到達了NT_Header之後,
如果想要到達Image Optional Header的話,
位置則為:BaseOfImage + 0xC0 + 0x18

同理,
BaseOfImage 的真實數值 = [ BaseOfImage + 0xC0 + 0x18 + 0x1C ]。
只要依照表格上的RVA一區一區的進行累加,
你可以取得PE檔頭中的任何資訊。

如果你想確認自己的計算是否有問題,
你可以使用PE Bear中的資料進行比對,
來驗證提取的數值是否正確。


0x04 - 接下挑戰吧!勇者!


利用我上面提到的觀念,
並參考Era、微軟的說明文件,
查出你需要用的Win32 Api Function,
再把它們組裝起來,
最後,開發出屬於你自己的覆寫病毒。

想了解更多關於Era病毒的資訊,
可以去看我之前的文章

0x05 - 向死而生


德國哲學家馬丁·海德格爾曾說過:

「向死而生、當你無限接近死亡, 才能深切體會生的意義。」

而這句話也適用於病毒開發之中:

「當你無限接近失敗,才能深切體會到成功的喜悅。」

在失敗中摸索、在錯誤中學習,
在一切的不可能之中推導出一線生機,
如此一來,你才能成為最成功的病毒開發者。

送禮物贊助創作者 !
0
留言

創作回應

樂小呈
佬言佬語
2022-07-07 22:15:32
虛鹿
我還是這個圈子的新手
2022-07-14 21:19:31
唯有經歷黃金鎮魂曲才能領悟最高病毒
2022-07-08 00:35:29
虛鹿
意外的合理
2022-07-14 21:19:40
Nero
2022-07-08 17:43:33
虛鹿
好耶
2022-07-14 21:19:46

更多創作