Billy Belceb 病毒編寫教程for Win32 ----Win32優化
【Win32 優化】
~~~~~~~~~~~~~
Ehrm...Super應該做這個而不是我,因為我是他的學生,我就在這裡寫一下我在Win32程式設計世界裡所學到的東西。我將在這一章裡討論本地優化而不是結構優化,因為這個取決於於你和你的風格(例如,我個人非常熱衷於堆疊和delta offset計算,正如你在我的程式碼裡可以看到的,特別是在Win95.Garaipena裡)。這篇文章充滿了我自己的觀點和在Valencian(瓦倫西亞)會議上Super給我的建議。他可能在病毒編寫領域裡優化得最後得人了。我沒有撒謊。這裡我不討論象他那樣怎麼進行最大優化了。我只是想要使你看到在編寫Win32程式的時候一些最明顯的優化。我就不對非常明顯的優化花招註釋了,已經在我的《MS-DOS病毒編寫教程》裡解釋了。
%檢測一個暫存器是否為0%
~~~~~~~~~~~~~~~~~~~~~~~
我很討厭看到,特別在Win32程式設計師中,這些相同的方法,這個使得我非常慢而且非常痛苦。不,不,我得大腦不能吸收CMP EAX,0的主意,例如。OK,讓我們看看為什麼:
cmp eax,00000000h ; 5 bytes
jz bribriblibli ; 2 bytes (if jz is short)
嗨,我知道生活就是就是狗屎,而且你正在把許多程式碼浪費在一些狗屎比較上。OK,讓我們看看怎麼來解決這個問題,利用一個程式碼來做同樣的事情,但是用更少的位元組。
or eax,eax ; 2 bytes
jz bribriblibli ; 2 bytes (if jz is short)
或者等價的(但更安全!):
test eax,eax ; 2 bytes
jz bribriblibli ; 2 bytes (if jz is short)
而且還有一個甚至更優化的方法來做這個,如果對EAX的內容不是關心的話(在我打算放到這裡之後,EAX的內容將在ECX中完成)。下面你得到:
xchg eax,ecx ; 1 byte
jecxz bribriblibli ; 2 bytes (only if short)
你看到了嗎?對"我不優化因為我失去了穩定性"沒有託詞,因為利用這個,你將不會失去除了程式碼的位元組數的任何東西;)嗨,我使得一個7位元組的例程減到了3位元組...嗨?對此你還有什麼好說的?哈哈哈。
%檢查一個暫存器的值是否為-1%
因為許多Ring-3 API會返回你一個-1(0FFFFFFFFh)值,如果函式失敗的話,而且當你比較它是否失敗的時候,你必須對那個值進行比較。但是和以前一樣有同樣的問題,許多人通過使用CMP EAX,0FFFFFFFFh來做這個,而且它可以更優化...
cmp eax,0FFFFFFFFh ; 5 bytes
jz insumision ; 2 bytes (if short)
讓我們這麼做來使它更優化:
inc eax ; 1 byte
jz insumision ; 2 bytes
dec eax ; 1 byte
嗨,可能它佔了更多的行,但是佔了更少的位元組(4比7)。
%使得一個暫存器為-1%
~~~~~~~~~~~~~~~~~~~~
這是一個幾乎所有的初學病毒編寫者面對的問題:
mov eax,-1 ; 5 bytes
你難道沒有意識到你的選擇很糟糕?你只要一根神經嗎?該死,用一個更優化的方法來把它置-1非常簡單:
xor eax,eax ; 2 bytes
dec eax ; 1 byte
你看到了嗎?它不難!
%清除一個32bit暫存器並對它的LSW賦值%
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
最明顯的例子是所有的病毒在把PE檔案的節的個數裝載到AX中(因為這個值在PE頭中佔一個word)。好了,讓我們看看大多數病毒編寫者所做的:
xor eax,eax ; 2 bytes
mov ax,word ptr [esi+6] ; 4 bytes
或者這樣:
mov ax,word ptr [esi+6] ; 4 bytes
cwde ; 1 byte
我還在想為什麼所有的病毒編寫者還用這個"老"公式呢,特別地是在你有一個386+指令使得我們避免在把word放到AX中之前把暫存器清0。這個指令是MOVZX。
movzx eax,word ptr [esi+6] ; 4 bytes
嗨,我們避免了一個2位元組的指令。Cool,哈?
%呼叫一個儲存在一個變數中的地址%
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
呵呵,這是一些病毒編寫者所做的另外一件事,使我快瘋了,放聲大哭。讓我提醒你記住:
mov eax,dword ptr [ebp+ApiAddress] ; 6 bytes
call eax ; 2 bytes
我們可以直接呼叫一個地址...它節約了位元組而且不用其它的任何可以用來做其它事情的暫存器。
call dword ptr [ebp+ApiAddress] ; 6 bytes
而且,我節約了一個沒有用的,不需要的佔了兩個位元組的指令,而且我們做的是完全一樣的事情。
%關於push的趣事%
~~~~~~~~~~~~~~~~
幾乎和上面一樣,但是是push。讓我們看看什麼該做什麼不該做:
mov eax,dword ptr [ebp+variable] ; 6 bytes
push eax ; 1 byte
我們可以少用一個位元組來做這個。看:
push dword ptr [ebp+variable] ; 6 bytes
Cool,哈?;)好了,如果我們需要push很多次(如果這個值很大,如果你把那個值push 2+次就更優化,而如果這個值很小把那個值push 3+次)同樣的變數把它先放到一個暫存器中,然後push暫存器將更優化。例如,如果我們需要把0 push 3次,把一個暫存器和它本身xor,然後push這個暫存器更優化。讓我們看:
push 00000000h ; 2 bytes
push 00000000h ; 2 bytes
push 00000000h ; 2 bytes
讓我們看看怎麼來優化它:
xor eax,eax ; 2 bytes
push eax ; 1 byte
push eax ; 1 byte
push eax ; 1 byte
同樣的在使用SEH的時候,當我們需要push fs:[0]之類的時候。讓我們看看怎樣來優化:
push dword ptr fs:[00000000h] ; 6 bytes ; 666? Mwahahahaha!
mov fs:[00000000h],esp ; 6 bytes
[...]
pop dword ptr fs:[00000000h] ; 6 bytes
代之我們應該這麼做:
xor eax,eax ; 2 bytes
push dword ptr fs:[eax] ; 3 bytes
mov fs:[eax],esp ; 3 bytes
[...]
pop dword ptr fs:[eax] ; 3 bytes
呵呵,看起來有點傻,但是我們少用了7個位元組!哇!!!
%獲取一個ASCII字串的結尾%
~~~~~~~~~~~~~~~~~~~~~~~~~~~
這個非常有用,特別在我們的API搜尋引擎中。而且毫無疑問,它應該在所有的病毒中比傳統的方法更優化。讓我們看看:
lea edi,[ebp+ASCIIz_variable] ; 6 bytes
@@1: cmp byte ptr [edi],00h ; 3 bytes
inc edi ; 1 byte
jnz @@1 ; 2 bytes
inc edi ; 1 byte
這個相同的程式碼可以非常簡化,如果你用這個方法來編寫它:
lea edi,[ebp+ASCIIz_variable] ; 6 bytes
xor al,al ; 2 bytes
@@1: scasb ; 1 byte
jnz @@1 ; 2 bytes
呵呵呵。有用,簡單,好看。你還需要什麼呢?;)
%關於乘法%
~~~~~~~~~~
例如,當要從程式碼中得到最後一節的時候,這個程式碼大多數是這麼用的(我們在EAX中是節數-1):
mov ecx,28h ; 5 bytes
mul ecx ; 2 bytes
它把結果儲存在EAX中,對嗎?好了,我們有一個好得多的方法來做這個,僅僅用一個指令:
imul eax,eax,28h ; 3 bytes
IMUL指令把結果儲存在第一個暫存器中,這個結果是把第二個暫存器和第三個運算元相乘得到的在這裡,它是一個立即數。呵呵,我們減少了2個指令還節約了4個位元組!
%UNICODE 轉成 ASCII%
~~~~~~~~~~~~~~~~~~~~
這裡有許多事情要做。對於Ring-0病毒特別的是,有一個VxD服務來做那個,首先我要解釋基於這個服務怎麼來做優化,最終我將給出Super的方法,那個方法節約了大量的位元組。讓我們看看經典的程式碼(假設EBP是一個指向ioreq結構的指標,而EDI指向檔名):
xor eax,eax ; 2 bytes
push eax ; 1 byte
mov eax,100h ; 5 bytes
push eax ; 1 byte
mov eax,[ebp+1Ch] ; 3 bytes
mov eax,[eax+0Ch] ; 3 bytes
add eax,4 ; 3 bytes
push eax ; 1 byte
push edi ; 1 byte
@@3: int 20h ; 2 bytes
dd 00400041h ; 4 bytes
特別指出的是對那個程式碼只有1個改進,把第3行替代成這樣:
mov ah,1 ; 2 bytes
或者這樣 ;)
inc ah ; 2 bytes
呵呵,但是我要說的是Super把這個進行了最大的優化。我沒有複製他的獲取指向檔名unicode的指標的程式碼,因為,幾乎無法看懂,但是我理解了他的理念。假設EBP是指向一個ioreq結構的指標,buffer是一個100h位元組的緩衝區。下面是一些程式碼:
mov esi,[ebp+1Ch] ; 3 bytes
mov esi,[esi+0Ch] ; 3 bytes
lea edi,[ebp+buffer] ; 6 bytes
@@l: movsb ; 1 byte 目
dec edi ; 1 byte ?This loop was
cmpsb ; 1 byte ?made by Super ;)
jnz @@l ; 2 bytes 餒
呵呵,最主要的是所有例程(沒有本地優化)是26個位元組,用同樣的方法進行本地優化後是23位元組,而最後的例程,結構優化後是17個位元組。哇哈哈哈!!!
%虛擬大小(VirtualSize)計算%
~~~~~~~~~~~~~~~~~~~~~~~~~~~
這個標題是一個給你顯示另外一個奇怪的程式碼的理由,對於VirtualSize計算非常有用,因為我們不得不把它加上一個值,在我們加之前是獲得這個值。當然了,我將要討論的操作符是XADD。Ok,ok,讓我們看看沒有優化的VirtualSize計算(我假設ESI是一個指向最後一節的頭部的指標):
mov eax,[esi+8] ; 3 bytes
push eax ; 1 byte
add dword ptr [esi+8],virus_size ; 7 bytes
pop eax ; 1 byte
讓我們看看用XADD該是什麼樣:
mov eax,virus_size ; 5 bytes
xadd dword ptr [esi+8],eax ; 4 bytes
用XADD我們節約了3個位元組;)Btw,XADD是一個486+指令。
%設定堆疊結構%
~~~~~~~~~~~~~~
讓我們看看沒有優化的:
push ebp ; 1 byte
mov ebp,esp ; 2 bytes
sub esp,20h ; 3 bytes
而如果我們優化了...
enter 20h,00h ; 4 bytes
很迷人,不是嗎?;)
%重疊%
~~~~~~
這個簡單的東西最初是由Demogorgon/PS為了隱藏程式碼而使用的。但是正如我要顯示給你看的,它可以節約一些位元組。例如,讓我們想象一個如果有一個錯誤就會設定進位標誌(carry flag)而如果沒有錯誤就清除的例程。
noerr: clc ; 1 byte
jmp exit ; 2 bytes
error: stc ; 1 byte
exit: ret ; 1 byte
但是如果任何8位元暫存器不重要的話(例如,讓我們假設ECX暫存器的內容不重要),我們可以減少一個位元組:
noerr: clc ; 1 byte
mov cl,00h ; 1 byte \
org $-1 ; > MOV CL,0F9H
error: stc ; 1 byte /
ret ; 1 byte
我們可以用一個小小的改變來避免CLC:使用TEST(用AL的話,它會更加優化)來清除進位標誌,而且AL不會改變:)
noerr: test al,00h ; 1 byte \
org $-1 ; > TEST AL,0AAH
error: stc ; 1 byte /
ret ; 1 byte
很美妙,哈?
%把一個8位元立即數賦給一個32位元暫存器%
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
幾乎所有人都是這麼做的:
mov ecx,69h ; 5 bytes
這是一個真正沒優化的東西...試試這個:
xor ecx,ecx ; 2 bytes
mov cl,69h ; 2 bytes
試試這個甚至更好:
push 69h ; 2 bytes
pop ecx ; 1 byte
所有人都還好嗎? :)
%清除記憶體中的變數%
~~~~~~~~~~~~~~~~~~
OK,這個總是很有用的。通常人們這麼做:
mov dword ptr [ebp+variable],00000000h ; 10 bytes (!)
OK,我知道這是一件很原始的事情:)OK,用這個你將贏得3個位元組:
and dword ptr [ebp+variable],00000000h ; 7 bytes
呵呵呵呵 :)
%花招和訣竅%
~~~~~~~~~~~~
這裡我將給出一些不經典的優化訣竅,我假設你讀過這篇文章之後你就知道了這個 ;)
-不要在你的程式碼中直接使用JUMP。
-使用字串操作(MOVS, SCAS, CMPS, STOS, LODS)。
-使用LEA reg,[ebp+imm32]而不是使用MOV reg,offset imm32 / add reg,ebp。
-使你的彙編編譯器對程式碼多掃描幾遍(在TASM中,/5就很好了)。
-使用堆疊,儘量避免使用變數。
-試圖避免使用AX,BX,CX,DX,SP,SI,DI 和 BP,因為他們多佔一個位元組。
-許多操作(特別使邏輯操作)是為EAX/AL暫存器優化的。
-如果EDX比80000000h小(也就是說沒有符號),使用CDQ來清除EDX
-使用XOR reg,reg或者SUB reg,reg來使得暫存器為0。
-使用EBP和ESP作為索引將比EDI,ESI等等多浪費1個位元組。
-對於位操作使用BT家族的指令(BT,BSR,BSF,BTR,BTF,BTS)。
-如果暫存器的順序不重要的話使用XCHG代替MOV。
-在push一個IOREQ結構的所有的值的時候,使用一個迴圈。
-儘可能地使用堆(API地址,臨時感染變數,等等)
-如果你願意,使用條件MOV(CMOVS),但是它們是586+才能用的。
-如果你知道怎麼用,使用協處理器(例如它的堆疊)。
-使用SET族的操作符。
-為了呼叫IFSMgr_Ring0_FileIO(不需要ret),使用VxDJmp而不是VxDCall。
%最後的話%
~~~~~~~~~~
我希望你至少理解了這一章的開始幾個優化,因為它們是那些使我變瘋的一些優化。我知道我不是優化得最後得人,也不是那些人之一。對我來說,大小沒有關係。無論如何,最明顯的優化是必須要做的,至少表明你知道一些事情。更少的無用的位元組就意味著一個更好的病毒,相信我。我這裡顯示的優化不會使你的病毒失去穩定性。只要試著去使用它們,OK?它是很有邏輯性的,同志們。
相關文章
- Billy Belceb 病毒編寫教程for Win32 ----Win32多型2004-05-28Win32多型
- Billy Belceb 病毒編寫教程for Win32 ----附錄2004-05-28Win32
- Billy Belceb 病毒編寫教程for Win32 ----Win32 反除錯2004-05-28Win32除錯
- Billy Belceb 病毒編寫教程for Win32 ----高階Win32技術2004-05-28Win32
- Billy Belceb 病毒編寫教程for Win32 ----PE檔案頭2015-11-15Win32
- Billy Belceb 病毒編寫教程for Win32 ----簡單介紹2015-11-15Win32
- Billy Belceb 病毒編寫教程for Win32 ----Per-Process?residency2004-05-28Win32IDE
- [翻譯]Billy Belceb 病毒編寫教程for Win32 ----病毒編寫中的有用的東西2004-05-28Win32
- Billy Belceb 病毒編寫教程for Win32 ----Ring-0,系統級編碼2004-05-28Win32
- Billy Belceb病毒編寫教程(DOS篇)---優化(Optimization)2015-11-15優化
- Billy Belceb 病毒編寫教程for Win32 ----Ring-3,使用者級編碼2015-11-15Win32
- Billy Belceb病毒編寫教程DOS篇---宣告2015-11-15
- Billy Belceb病毒編寫教程(DOS篇)---加密2015-11-15加密
- Billy Belceb病毒編寫教程(DOS篇)---附錄2015-11-15
- Billy Belceb病毒編寫教程(DOS篇)---病毒編寫所需的軟體2015-11-15
- Billy Belceb病毒編寫教程(DOS篇)---隱蔽(Stealth)2015-11-15
- Billy Belceb病毒編寫教程(DOS篇)---駐留記憶體病毒2015-11-15記憶體
- [翻譯]Billy
Belceb 病毒編寫教程for Win32----- 宣告2004-05-28Win32
- Billy Belceb病毒編寫教程(DOS篇)---Tunneling2015-11-15
- Billy Belceb病毒編寫教程(DOS篇)---多型(polymorphism)2015-11-15多型
- Billy Belceb病毒編寫教程(DOS篇)反探索(Anti-Heuristics)2015-11-15
- Billy Belceb病毒編寫教程(DOS篇)有用的結構體2015-11-15結構體
- Billy Belceb病毒編寫教程(DOS篇)---Anti-tunneling2015-11-15
- Billy Belceb病毒編寫教程(DOS篇)---保護你的程式碼2015-11-15
- Billy Belceb病毒編寫教程(DOS篇)一些重要的理論2015-11-15
- Win32彙編教程二 Win32彙編程式的結構和語法 (轉)2007-12-02Win32
- Win32/Angryel病毒分析報告2019-09-25Win32
- Win32彙編教程四 編寫一個簡單的視窗 (轉)2007-12-02Win32
- 基本概念(win32)彙編教程(轉)2007-07-28Win32
- Win32彙編教程十二 管道操作 (轉)2007-12-29Win32
- Win32彙編教程七 控制元件的子類化 (轉)2007-12-29Win32控制元件
- WIN32 手動編譯2020-10-28Win32編譯
- Win32彙編教程八 圖形介面的操作 (轉)2007-12-29Win32
- Win32彙編教程十 定時器的應用 (轉)2007-12-29Win32定時器
- Win32下Foxbase+資料庫瀏覽程式的編寫 (轉)2007-10-04Win32資料庫
- 讓我們寫一個 Win32 文字編輯器吧 - 1. 簡介2022-04-03Win32
- Jack's第一個Win32彙編程式HelloWorld2020-04-04Win32
- Jack整理的Win32彙編基礎知識2020-04-04Win32