标题
    
    
        [vc]VC++下编译出极小的程序 [zt 一篇非常好的帖子]
    
 
    
 	 
       
    
    clq
    
    
    
		
        浏览(0) + 
        2008-07-23 11:05:05 发表
        
编辑
         
        
        
        
        关键字: 
        
 
        
        
        
        
                
        [vc]VC++下编译出极小的程序 [zt 一篇非常好的帖子]
我在 VC6 下也用得很好. 对我来说用处有两个,一个是写 cgi 的时候,在作为调用一个 dll 的 shell 速度上应该可以非常快,另外就是我经常写服务器,这样有外来不信任的模块时可以不写成直接调用 dll 而是在一个短小的 exe 中调用 dll,不过性能未测试. 不过用到网络入侵确实可以大大减少上传的时间 :)
--------------------------------------------------
来自 http://topic.csdn.net/u/20080628/15/69a98c60-6e6e-49ce-bac8-0c22280648c3.html
主要内容如下:
发表于:2008-06-28 15:11:31
楼主	
昨天做了个telnet后门程序玩, 
既然是后门嘛,自然是越小越好.可是我的VC9编译一个HelloWorld都有50K+... 
动态链接到MSVCRT90.dll倒是小下来了,但是得背上一个更变态的dll(600多k) 
而这个msvcrt90.dll就是我的vista sp1也不是自带的.更不便于程序的部署. 
最后自己去查了查资料终于编译出体积比较满意的exe,步骤如下: 
1.扔掉CRT. 
CRT提供了大量常用的函数.可说只要C/C++程序基本都会用到它.但是获得了方便的同 
时也增大了不少体积.虽然可以通过动态链接到外部的dll来解决,但是自此就有了对一 
个更大的dll的依赖.所以要减肥,就要先拿CRT开刀. 
在cl的编译参数中加上/MD,再在link中加上/nodefaultlib:msvcrt.lib即可避免链接 
到crt(静态crt的lib文件名我不清楚,所以就先链接成动态的,再去除对应的msvcrt.lib) 
2.重载new,new[],delete,delete[] 
C/C++程序少不了动态分配内存,前面丢掉了CRT,再编译程序会发现反是有内存分配的地方都 
在报错,这是因为我去掉了crt,编译器找不到对应的函数所致.所以要自己写内存分配函数. 
C的malloc和C++的new/new[]都是在当前的堆上分配内存.所以只要照着写一遍就可以了: 
C/C++ code
typedef UINT size_t; 
void *malloc(size_t size) 
{ 
  return HeapAlloc (GetProcessHeap(),NULL,size); 
} 
void free (void *memblock) 
{ 
  HeapFree (GetProcessHeap(),NULL,memblock); 
} 
void *realloc(void *memblock,size_t size) 
{ 
  return HeapReAlloc (GetProcessHeap(),NULL,memblock,size); 
} 
void *operator new(size_t count) 
{ 
  return malloc (count); 
} 
void *operator new[](size_t count) 
{ 
  return malloc (count); 
} 
void operator delete(void* _Ptr) throw( ) 
{ 
  free (_Ptr); 
} 
void operator delete[](void* _Ptr) throw( ) 
{ 
  free (_Ptr); 
} 
3.常用函数的替代. 
没了CRT许多常用的函数都无法使用,全部自己重写无疑大大加大了程序的代 
码量,也没多大意义.windows提供了很多常用的函数.都由系统的dll来提供, 
使用它们就不用 
a 字符串处理函数. 
  字符串处理函数的替代品很多,kernel32.dll提供了lstr***的函数.完全 
可以满足字符串处理的需要,C的格式化字符串sprintf函数很好用,而user32.dll 
也提供了相应的函数wsprintf/wvsprintf用于替代.没有字符串查找函数有点麻烦 
不过自己写也要不了几行代码,或者干脆用Shlwapi.dll提供的更完整的字符串处理 
库来替代. 
b 命令行输入/输出函数. 
  这个...系统还真没提供啥对应的函数,不过好在常用也不是太复杂.用Console API写 
几条常用的getline和puts就足够了. 
4.重新指定入口点. 
  如果问VC程序的入口点是什么,估计会有不少人回答是main/WinMain. 
没错.这是使用了标准的CRT的程序的入口点.但是实际的入口函数是CRT内部编写好的.在 
完成库的初始化后再调用main/WinMain.既然移除了CRT,链接器自然就会发出找不到入口 
函数的错误,解决方法很简单,用/entry:指定一个函数就可以了.我仍然用习惯的main. 
但是注意,这个main不能带任何的参数.带了也用不了. 
5.更改节的对齐大小. 
cl编译器默认的对齐大小是4K(4096)这个数值的设定是个问题.有时候会使编译出来的程序无 
法执行.这个只有自己反复的试验了.我将它设置为512(其实还可以更小,但是小于512的程序不能使用upx压缩)程序又小了几K. 
6.给exe加壳. 
使用exe加壳程序给程序加个壳也是减小体积的一个好方法. 
我常用的是upx,如果前面的节对齐的值大于512的话,就可以使用upx进行加壳压缩. 
通常可以压缩至原大小的66%左右. 
7.折中的办法: 
丢掉了CRT,重写一些函数的工作量比较大,而且有些函数自己也写不出来.(比如sin函数,像我这样的 
高数菜鸟拿着就头痛)但确实有时候会用到这些函数.于是就有了以下的折中办法: 
VC6是98年推出的.它的动态链接库版的dll是msvcrt.dll,因为出来的早,这个dll的装机率十分的高. 
(我以前学校的win98se都自带,98后的系统自然不用说了.)使用它基本可以不考虑部署时缺少dll的问题 
所以可以使用这个dll提供的crt函数.要使用它,先得去找个vc6,复制里面的msvcrt.lib,改个名.比如 
vcrt6.lib然后把它添加到链接的库里.编译的时候会有库冲突的提示,直接无视即可.这样既可以使用绝大 
部分的CRT库,又做到了减肥.还不用担心会有找不到库的情况.也不用再重新指定入口点.但是经我试验发现 
这样编译出来的程序还是会比完全丢掉CRT的大上一点,所以只是个折中的办法. 
接下来做下试验: 
写一个简单的程序: 
C/C++ code
#include  
#include  
void main () 
{ 
  char* str = new char[250]; 
  sprintf (str,"当前系统已运行了%d毫秒!",GetTickCount()); 
  MessageBox (NULL,str,str,MB_ICONINFORMATION); 
  delete[] str; 
} 
然后做下编译(VC9的编译器,未更改节对齐值,未做加壳) 
静态CRT:50KB 
动态链接CRT:5.50KB 
使用VC6的动态链接CRT:3.00 KB 
完全不用CRT要做下更改: 
C/C++ code
#include  
#pragma comment (lib,"user32.lib") 
#pragma comment (lib,"kernel32.lib") 
void *operator new[] (unsigned int size) 
{ 
  return HeapAlloc (GetProcessHeap(),NULL,size); 
} 
void operator delete[] (void* memblock) 
{ 
  HeapFree (GetProcessHeap(),NULL,memblock); 
} 
#define sprintf wsprintf 
void main () 
{ 
  char* str = new char[250]; 
  sprintf (str,"当前系统已运行了%d毫秒!",GetTickCount()); 
  MessageBox (NULL,str,str,MB_ICONINFORMATION); 
  delete[] str; 
} 
编译参数也比较长: 
cl /MD msgbox.cpp /link /nodefaultlib:msvcrt.lib /entry:main 
编译后大小:2.00KB 
加上/align:16编译后大小:960Byte
        
        
        
        		
		        
                
                
     
    
 
	
	
     
    
       
    
    clq
    
    
    
    
    
    		    
    
          
              
    	注意编译要用命令行:
cl /MD main.cpp /link /nodefaultlib:msvcrt.lib /entry:main
C++ 的类释放也是通过测试了的,看下面的代码.
/*
#include 
int main(int argc, char* argv[])
{
	printf("Hello World!\n");
	return 0;
}
*/
#include  
#pragma comment (lib,"user32.lib") 
#pragma comment (lib,"kernel32.lib") 
//#pragma comment(linker,"/ENTRY:mainCRTStartup")
#pragma comment(linker,"/ENTRY:main")
/*
typedef UINT size_t; 
void *malloc(size_t size) 
{ 
  return HeapAlloc (GetProcessHeap(),NULL,size); 
} 
void free (void *memblock) 
{ 
  HeapFree (GetProcessHeap(),NULL,memblock); 
} 
void *realloc(void *memblock,size_t size) 
{ 
  return HeapReAlloc (GetProcessHeap(),NULL,memblock,size); 
} 
void *operator new(size_t count) 
{ 
  return malloc (count); 
} 
void *operator new[](size_t count) 
{ 
  return malloc (count); 
} 
void operator delete(void* _Ptr) throw( ) 
{ 
  free (_Ptr); 
} 
void operator delete[](void* _Ptr) throw( ) 
{ 
  free (_Ptr); 
} 
*/
typedef UINT size_t; 
void *operator new[] (unsigned int size) 
{ 
  return HeapAlloc (GetProcessHeap(),NULL,size); 
} 
void *operator new(unsigned int size) 
{ 
  return HeapAlloc (GetProcessHeap(),NULL,size); 
} 
void operator delete[] (void* memblock) 
{ 
  HeapFree (GetProcessHeap(),NULL,memblock); 
}
void operator delete (void* memblock) 
{ 
  HeapFree (GetProcessHeap(),NULL,memblock); 
}
#define sprintf wsprintf 
class A 
{ 
public: 
  A(){}   
  ~A(){MessageBox(NULL,"Destruction[确实是释放了]!","",MB_OK);} 
} ;
void main () 
{ 
    A *pa = new A();
    delete pa;
	char* str = new char[250]; 
	sprintf (str,"当前系统已运行了%d毫秒!",GetTickCount()); 
	MessageBox (NULL,str,str,MB_ICONINFORMATION); 
	delete[] str;
	//printf("aaa\r\n");
}
    
    
     
 
	 
    
       
    
    clq
    
    
    
    
    
    
    
          
              
    	不用命令行用 vc6 有 ide 编译的话要使用 /nodefaultlib  的选项.要不 new delete free malloc 还是会存在的,会导致冲突,同时 user32.lib kernel32.lib 库还是要明确导入的.
    
    
     
 
	 
    
       
    
    clq
    
    
    
    
    
    
    
          
              
    	其中 /ALIGN  会大大减少这个 exe 的大小(不过对其他 mfc 的程序没什么效果,哈哈) 不知道为什么,即使是用默认的 /ALIGN 4096 也会很小,真是不明白了.难道 vc6 默认不是 4096? 以下是 msdn 的说明:
--------------------------------------------------
Visual C++ 链接器选项 
/ALIGN(节对齐) 
请参见  发送反馈意见 
 
  复制代码 
/ALIGN[:number]
 
备注
其中: 
number 
对齐值。
备注
/ALIGN 选项指定程序线性地址空间中每一节的对齐方式。number 参数以字节为单位,并且必须是 2 的幂。默认值是 4K (4096)。如果对齐方式产生无效的图像,则链接器发出警告。
除非正在编写诸如设备驱动程序的应用程序,否则应不需要修改对齐方式。
可以用 /SECTION 选项的对齐参数修改特定节的对齐方式。
指定的对齐值不能小于最大的节对齐。
在 Visual Studio 开发环境中设置此链接器选项
打开该项目的“属性页”对话框。有关详细信息,请参见设置 Visual C++ 项目属性。 
单击“链接器”文件夹。
单击“命令行”属性页。
将该选项键入“附加选项”框中。
以编程方式设置此链接器选项
请参见 AdditionalOptions。
请参见
概念
设置链接器选项
链接器选项
发送反馈意见,就此主题向 Microsoft 发送反馈意见。
    
    
     
 
	 
    
       
    
    clq
    
    
    
    
    
    
    
          
              
    	我在 ide 中编译 /align:16 后 exe 会从 16K 减少到 1 K !! 换成 /align:4096 也仍然只有 2.5 K
    
    
     
 
	 
    
       
    
    clq
    
    
    
    
    
    
    
          
              
    	他文中说的链接 vc6 的 msvcrt.dll 的方法我是不赞成的,因为我们以前写程序时经常碰到这个 dll 不同版本冲突的事的,最好就是不要用它.当然一般情况下我们是自带一个自己开发机器上的版本的.
    
    
     
 
	 
    
       
    
    clq
    
    
    
    
    
    
    
          
              
    	http://bbs.pediy.com/showthread.php?threadid=28058
据说也不错,"看雪"是很著名的了.
    
    
     
 
	 
    
       
    
    clq
    
    
    
    
    
    
    
          
              
    	汗... 
COM当然小了... 
但是你用COM文件弹个MessageBox试试=_= 
另外既然都不用CRT了. 
考虑的自然是仅限Win系列桌面系统下的移植. 
昨天发现用Windows DDK里带的ntdll.lib链接 
到ntdll.dll完全可以用来替代大部分的crt,包 
括浮点运算.(Win98下貌似会有些问题)
    
    
     
 
	 
    
       
    
    clq
    
    
    
    
    
    
    
                    
                   
                
                    [图片]
 
                    
                    
                    
                  
                
          
              
    	如图:
原来它们的选择是可以控制对 msvcrt.dll 各个版本进行控制的,还可以编译出不需要那个 DLL 的程序,我一直以为是一定要有这个家族的 dll 的. 惭愧呀!!!    ^.^!
    
    
     
 
	 
	
    NEWBT官方QQ群1: 276678893
    可求档连环画,漫画;询问文本处理大师等软件使用技巧;求档softhub软件下载及使用技巧.
    但不可"开车",严禁国家敏感话题,不可求档涉及版权的文档软件.
    验证问题说明申请入群原因即可.