1. 程式人生 > >vc編譯exe的體積最小優化(附編譯器引數)

vc編譯exe的體積最小優化(附編譯器引數)

人們都說vc做出的東西可以小點,現在你開啟vc編譯一個Hello World出來!點屬性看下,咦!我沒走眼吧,就一Hello World就160kb真是要人命啊! 
呵呵!上面的情況是筆者所遭遇的情況.不過後來了解vc可以通過設定引數來自定義編譯方式.為什麼檔案那麼大!主要是編譯器加入了很多沒必要的程式碼(這裡是對我們而言,不過有些程式碼還是有利於安全的).好了我們就手動改下編譯器的引數來看看能到多大!

我們主要用到的技巧有:

一,使用release版而不用debug版編譯

使用debug版編譯會生成許多垃圾資訊.我們先使用預設的設定進行一下編譯.可以看到編譯後生成的檔案有152k之巨.使用release版編譯具體方法是:在"build(編譯)--->Configuration(配置)"中將"Win32 debug"移去,然後再次編譯可以發現檔案已經小了很多,才24k.但離我們的目標還很遠呢.

二,設定自己的入口點函式

C或C++程式預設的入口函式是main()或WinMain(),但我們現在不用什麼Main,WinMain.因為這些都不是直接的入口點,編譯器在產生exe檔案的時候,將為我們生成真正的入口點.下面我們來定義自己的入口函式,具體是把main或WinMain改成其它的名字(如MyFun),開啟"Project(工程)--->settings(設定)"選項,選中"link"選項卡,在"Category(分類)"下拉列表中選"output",在" Entry-Point symbol(輸入項-點符號)"中輸入我們剛才定義的入口函式(MyFun),在源程式中也要做相應修改,然後再編譯.現在是16k了:)

三,更改編譯對齊方式

通常VC在編譯的時候,採用的對齊方式是0x1000,即4096bytes,我們現在將他改成0x200,即512bytes.

在剛才開啟的"link"選項卡,在下面的"Project options(工程選項)"中新增:/align:512(還可以將512設

置的更小如16,32.....).注意兩個引數之間有個空格. 3k了^_^用32試試 1.84k好~~~用16 1.79k天哪!

再把程式的資料段和程式碼段放在一起,新增:/merge.data=http://www.hake.cc/kf/200908/.text /merge:.rdata=.text 1.76k go on!

另外,如果要是用到MFC函式的程式,可在"Project(工程)--->settings(設定)"裡面的"通用(General)"選項卡中在"Microsoft Foundation Classes"中選擇使用一個MFC的dll(Use MFC in a Share Dll)也會使檔案大小縮小很多.現在我們的超小後門編譯好了,試下能用否. ok 沒問題哦

大家注意到程式執行時會產生一個cmd視窗,要讓他沒有就好了.這也好辦.

回到VC++中,在"Project(工程)--->settings(設定)"選項,選中"link"選項卡,在下面的"Project options(工程選項)"有/subsystem:console選項,表示程式是控制檯程式,雙擊執行是會有一個cmd視窗,把console改為windows就沒有視窗了.:),執行一下 沒有視窗哦 但有程序 連線一下試試

ok 沒問題 這樣我們的超小1.76k telnet小後門就成功了 不被查殺哦 ^_^

// 編譯器 cl.exe(Visual C++ 6.0)
// 沒有做任何優化情況下,編譯大小為:16K
// 編譯優化後: 1K (用16進位制編輯器把尾部的0x00去掉: 712bytes)
#include <windows.h>
#pragma comment(lib,"kernel32.lib")

// 作用: 指定節對齊為512位元組
#pragma comment(linker, "/align:512")

// 作用: 合併節
// 將.data節和.rdata節合併到.text節(程式碼節)
#pragma comment(linker, "/merge:.data=http://www.hake.cc/kf/200908/.text")
#pragma comment(linker, "/merge:.rdata=http://www.hake.cc/kf/200908/.text")

// 作用: 指定子系統為windows (和優化無關)
// vc編譯器預設是console,會有個黑糊糊的CMD視窗,不好看.用windows就好了
#pragma comment(linker, "/subsystem:windows")

// 作用: 指定入口函式
// 子系統為windows的預設入口點WinMain和console的預設入口點main,都會引入

#pragma comment(linker, "/ENTRY:main")

//int WinMain(HINSTANCE current, HINSTANCE prev, LPSTR cmdline, int
//showcmd)

// 作用: 去掉函式的棧幀程式碼,純屬吹毛求疵:-)
// 即函式開頭的push ebp / mov ebp, esp和結尾的pop ebp / retn
__declspec(naked)
void main()
{
// 呼叫wmp. 這是按套路出牌的方法.
//typedef VOID (__stdcall *fnRunDllW)(HWND, HINSTANCE, LPCWSTR, DWORD);
//((fnRunDllW)GetProcAddress(LoadLibrary("msdxm.ocx"), "RunDllW"))(0,0,0,0);

// 不按套路出牌,不壓入RunDllW的函式引數,直接呼叫.
//GetProcAddress(LoadLibrary("msdxm.ocx"), "RunDllW")();
MessageBox(0,0,0,0);
// 注意此時的堆疊是不平衡的.
// 但是通過ExitProcess()退出自身,就不用去考慮平衡了.
ExitProcess(0);
}

微軟C/C++ 編譯器選項

-優化-

/O1 
最小化空間

/Op[-] 
改善浮點數一致性

/O2 
最大化速度

/Os 
優選程式碼空間

/Oa 
假設沒有別名

/Ot 
優選程式碼速度

/Ob<n> 
內聯展開(預設 n=0)

/Ow 
假設交叉函式別名

/Od 
禁用優化(預設值)

/Ox 
最大化選項。(/Ogityb2 /Gs)

/Og 
啟用全域性優化

/Oy[-] 
啟用框架指標省略

/Oi 
啟用內部函式

-程式碼生成-

/G3 
為 80386 進行優化

/GH 
啟用 _pexit 函式呼叫

/G4 
為 80486 進行優化

/GR[-] 
啟用 C++ RTTI

/G5 
為 Pentium 進行優化

/GX[-] 
啟用 C++ EH(與 /EHsc 相同)

/G6 
為 PPro、P-II、P-III 進行優化

/EHs 
啟用 C++ EH(無 SEH 異常)

/GB 
為混合模型進行優化(預設)

/EHa 
啟用 C++ EH(w/ SEH 異常)

/Gd 
__cdecl 呼叫約定

/EHc 
外部“C”預設為 nothrow

/Gr 
__fastcall 呼叫約定

/GT 
生成纖維安全 TLS 訪問

/Gz 
__stdcall 呼叫約定

/Gm[-] 
啟用最小重新生成

/GA 
為 Windows 應用程式進行優化

/GL[-] 
啟用連結時程式碼生成

/Gf 
啟用字串池

/QIfdiv[-] 
啟用 Pentium FDIV 修復

/GF 
啟用只讀字串池

/QI0f[-] 
啟用 Pentium 0x0f 修復

/Gy 

分隔連結器函式

/QIfist[-] 
使用 FIST 而不是 ftol()

/GZ 
啟用堆疊檢查 (/RTCs)

/RTC1 
啟用快速檢查 (/RTCsu)

/Ge 
對所有函式強制堆疊檢查

/RTCc 
轉換為較小的型別檢查

/Gs[num] 
控制堆疊檢查呼叫

/RTCs 
堆疊幀執行時檢查

/GS 
啟用安全檢查

/RTCu 
未初始化的本地用法檢查

/Gh 
啟用 _penter 函式呼叫

/clr[:noAssembly] 
為公共語言執行時庫編譯noAssembly - 不產生程式集

-輸出檔案-

/Fa[file] 
命名程式集列表檔案

/Fo<file> 
命名物件檔案

/FA[sc] 
配置程式集列表

/Fp<file> 
命名預編譯標頭檔案

/Fd[file] 
命名 .PDB 檔案

/Fr[file] 
命名源瀏覽器檔案

/Fe<file> 
命名可執行檔案

/FR[file] 
命名擴充套件 .SBR 檔案

/Fm[file] 
命名對映檔案

-前處理器-

/AI<dir> 
新增到程式集搜尋路徑

/Fx 
將插入的程式碼合併到檔案

/FU<file> 
強制使用程式集/模組

/FI<file> 
命名強制包含檔案

/C 
不抽出註釋

/U<name> 
移除預定義巨集

/D<name>{=|#}<text> 
定義巨集

/u 
移除所有預定義巨集

/E 
預處理到 stdout

/I<dir> 
新增到包含搜尋路徑

/EP 
預處理到 stdout,沒有 #line

/X 
忽略“標準位置”

/P 
預處理到檔案

-語言-

/Zi 
啟用除錯資訊

/Zl 
忽略 .OBJ 中的預設庫名

/ZI 
啟用“編輯並繼續”除錯資訊

/Zg 
生成函式原型

/Z7 
啟用舊式除錯資訊

/Zs 
只進行語法檢查

/Zd 
僅有行號除錯資訊

/vd{0|1} 
禁用/啟用 vtordisp

/Zp[n] 
在 n 位元組邊界上包裝結構

/vm<x> 
指向成員的指標型別

/Za 
禁用擴充套件(暗指 /Op)

/noBool 
禁用“bool”關鍵字

/Ze 
啟用擴充套件(預設)

/Zc:arg1[,arg2] 
C++ 語言一致性,這裡的引數可以是:forScope - 對範圍規則強制使用標準 C++;wchar_t - wchar_t 是本機型別,不是 typedef

- 雜項 -

@<file> 
選項響應檔案

/wo<n> 
發出一次警告 n

/?, /help 
列印此幫助訊息

/w<l><n> 
為 n 設定警告等級 1-4

/c 
只編譯,不連結

/W<n> 
設定警告等級(預設 n=1)

/H<num> 
最大外部名稱長度

/Wall 
啟用所有警告

/J 
預設 char 型別是 unsigned

/Wp64 
啟用 64 位埠定位警告

/nologo 
取消顯示版權訊息

/WX 
將警告視為錯誤

/showIncludes 
顯示包含檔名

/WL 
啟用單行診斷

/Tc<source file> 
將檔案編譯為 .c

/Yc[file] 
建立 .PCH 檔案

/Tp<source file> 
將檔案編譯為 .cpp

/Yd 
將除錯資訊放在每個 .OBJ 中

/TC