标题
[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软件下载及使用技巧.
但不可"开车",严禁国家敏感话题,不可求档涉及版权的文档软件.
验证问题说明申请入群原因即可.