|
Windows的PE加載器在啟動(dòng)程序的時(shí)候,會(huì)將磁盤(pán)上的文件加載到內(nèi)存,然后做很多操作,如函數(shù)導(dǎo)入表重定位,變量預(yù)處理之類(lèi)的。這位仁兄等于是自己寫(xiě)了一個(gè)PE加載器。直接將內(nèi)存中的程序啟動(dòng)。記得以前的“紅色代碼”病毒也有相同的特性。 直接啟動(dòng)內(nèi)存中的程序相當(dāng)于加了一個(gè)殼,可以把程序加密保存,運(yùn)行時(shí)解密到內(nèi)存,然后啟動(dòng),不過(guò)對(duì)于增加破解難度還要稍微復(fù)雜點(diǎn)。否則人家把內(nèi)存中的進(jìn)程DUMP出來(lái)然后修復(fù)導(dǎo)入表就被拖出來(lái)了。
#include "stdafx.h"
typedef IMAGE_SECTION_HEADER (*PIMAGE_SECTION_HEADERS)[1];
// 計(jì)算對(duì)齊后的大小
unsigned long GetAlignedSize(unsigned long Origin, unsigned long Alignment)
 ...{
return (Origin + Alignment - 1) / Alignment * Alignment;
}
// 計(jì)算加載pe并對(duì)齊需要占用多少內(nèi)存
// 未直接使用OptionalHeader.SizeOfImage作為結(jié)果是因?yàn)閾?jù)說(shuō)有的編譯器生成的exe這個(gè)值會(huì)填0
unsigned long CalcTotalImageSize(PIMAGE_DOS_HEADER MzH
, unsigned long FileLen
, PIMAGE_NT_HEADERS peH
, PIMAGE_SECTION_HEADERS peSecH)
 ...{
unsigned long res;
// 計(jì)算pe頭的大小
res = GetAlignedSize( peH->OptionalHeader.SizeOfHeaders
, peH->OptionalHeader.SectionAlignment
);
// 計(jì)算所有節(jié)的大小
for( int i = 0; i < peH->FileHeader.NumberOfSections; ++i)
 ...{
// 超出文件范圍
if(peSecH[i]->PointerToRawData + peSecH[i]->SizeOfRawData > FileLen)
return 0;
else if(peSecH[i]->VirtualAddress)//計(jì)算對(duì)齊后某節(jié)的大小
 ...{
if(peSecH[i]->Misc.VirtualSize)
 ...{
res = GetAlignedSize( peSecH[i]->VirtualAddress + peSecH[i]->Misc.VirtualSize
, peH->OptionalHeader.SectionAlignment
);
}
else
 ...{
res = GetAlignedSize( peSecH[i]->VirtualAddress + peSecH[i]->SizeOfRawData
, peH->OptionalHeader.SectionAlignment
);
}
}
else if( peSecH[i]->Misc.VirtualSize < peSecH[i]->SizeOfRawData )
 ...{
res += GetAlignedSize( peSecH[i]->SizeOfRawData
, peH->OptionalHeader.SectionAlignment
);
}
else
 ...{
res += GetAlignedSize( peSecH[i]->Misc.VirtualSize
, peH->OptionalHeader.SectionAlignment
);
}// if_else
}// for
return res;
}
// 加載pe到內(nèi)存并對(duì)齊所有節(jié)
BOOL AlignPEToMem( void *Buf
, long Len
, PIMAGE_NT_HEADERS &peH
, PIMAGE_SECTION_HEADERS &peSecH
, void *&Mem
, unsigned long &ImageSize)
 ...{
PIMAGE_DOS_HEADER SrcMz;// DOS頭
PIMAGE_NT_HEADERS SrcPeH;// PE頭
PIMAGE_SECTION_HEADERS SrcPeSecH;// 節(jié)表
SrcMz = (PIMAGE_DOS_HEADER)Buf;
if( Len < sizeof(IMAGE_DOS_HEADER) )
return FALSE;
if( SrcMz->e_magic != IMAGE_DOS_SIGNATURE )
return FALSE;
if( Len < SrcMz->e_lfanew + (long)sizeof(IMAGE_NT_HEADERS) )
return FALSE;
SrcPeH = (PIMAGE_NT_HEADERS)((int)SrcMz + SrcMz->e_lfanew);
if( SrcPeH->Signature != IMAGE_NT_SIGNATURE )
return FALSE;
if( (SrcPeH->FileHeader.Characteristics & IMAGE_FILE_DLL) ||
(SrcPeH->FileHeader.Characteristics & IMAGE_FILE_EXECUTABLE_IMAGE == 0) ||
(SrcPeH->FileHeader.SizeOfOptionalHeader != sizeof(IMAGE_OPTIONAL_HEADER)) )
 ...{
return FALSE;
}
SrcPeSecH = (PIMAGE_SECTION_HEADERS)((int)SrcPeH + sizeof(IMAGE_NT_HEADERS));
ImageSize = CalcTotalImageSize( SrcMz, Len, SrcPeH, SrcPeSecH);
if( ImageSize == 0 )
return FALSE;
Mem = VirtualAlloc( NULL, ImageSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE); // 分配內(nèi)存
if( Mem != NULL )
 ...{
// 計(jì)算需要復(fù)制的PE頭字節(jié)數(shù)
unsigned long l = SrcPeH->OptionalHeader.SizeOfHeaders;
for( int i = 0; i < SrcPeH->FileHeader.NumberOfSections; ++i)
 ...{
if( (SrcPeSecH[i]->PointerToRawData) &&
(SrcPeSecH[i]->PointerToRawData < l) )
 ...{
l = SrcPeSecH[i]->PointerToRawData;
}
}
memmove( Mem, SrcMz, l);
peH = (PIMAGE_NT_HEADERS)((int)Mem + ((PIMAGE_DOS_HEADER)Mem)->e_lfanew);
peSecH = (PIMAGE_SECTION_HEADERS)((int)peH + sizeof(IMAGE_NT_HEADERS));
void *Pt = (void *)((unsigned long)Mem
+ GetAlignedSize( peH->OptionalHeader.SizeOfHeaders
, peH->OptionalHeader.SectionAlignment)
);
for( i = 0; i < peH->FileHeader.NumberOfSections; ++i)
 ...{
// 定位該節(jié)在內(nèi)存中的位置
if(peSecH[i]->VirtualAddress)
Pt = (void *)((unsigned long)Mem + peSecH[i]->VirtualAddress);
if(peSecH[i]->SizeOfRawData)
 ...{
// 復(fù)制數(shù)據(jù)到內(nèi)存
memmove(Pt, (const void *)((unsigned long)(SrcMz) + peSecH[i]->PointerToRawData), peSecH[i]->SizeOfRawData);
if(peSecH[i]->Misc.VirtualSize < peSecH[i]->SizeOfRawData)
Pt = (void *)((unsigned long)Pt + GetAlignedSize(peSecH[i]->SizeOfRawData, peH->OptionalHeader.SectionAlignment));
else // pt 定位到下一節(jié)開(kāi)始位置
Pt = (void *)((unsigned long)Pt + GetAlignedSize(peSecH[i]->Misc.VirtualSize, peH->OptionalHeader.SectionAlignment));
}
else
 ...{
Pt = (void *)((unsigned long)Pt + GetAlignedSize(peSecH[i]->Misc.VirtualSize, peH->OptionalHeader.SectionAlignment));
}
}
}
return TRUE;
}
typedef void *(__stdcall *pfVirtualAllocEx)(unsigned long, void *, unsigned long, unsigned long, unsigned long);
pfVirtualAllocEx MyVirtualAllocEx = NULL;
BOOL IsNT()
 ...{
return MyVirtualAllocEx!=NULL;
}
// 生成外殼程序命令行
char *PrepareShellExe(char *CmdParam, unsigned long BaseAddr, unsigned long ImageSize)
 ...{
if(IsNT())
 ...{
char *Buf = new char[256];
memset(Buf, 0, 256);
GetModuleFileName(0, Buf, 256);
strcat(Buf, CmdParam);
return Buf; // 請(qǐng)記得釋放內(nèi)存;-)
}
else
 ...{
// Win98下的處理請(qǐng)參考原文;-)
// http://community.csdn.net/Expert/topic/4416/4416252.xml?temp=8.709133E-03
return NULL;
}
}
// 是否包含可重定向列表
BOOL HasRelocationTable(PIMAGE_NT_HEADERS peH)
 ...{
return (peH->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress)
&& (peH->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size);
}
#pragma pack(push, 1)
 typedef struct...{
unsigned long VirtualAddress;
unsigned long SizeOfBlock;
} *PImageBaseRelocation;
#pragma pack(pop)
// 重定向PE用到的地址
void DoRelocation(PIMAGE_NT_HEADERS peH, void *OldBase, void *NewBase)
 ...{
unsigned long Delta = (unsigned long)NewBase - peH->OptionalHeader.ImageBase;
PImageBaseRelocation p = (PImageBaseRelocation)((unsigned long)OldBase
+ peH->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress);
while(p->VirtualAddress + p->SizeOfBlock)
 ...{
unsigned short *pw = (unsigned short *)((int)p + sizeof(*p));
for(unsigned int i=1; i <= (p->SizeOfBlock - sizeof(*p)) / 2; ++i)
 ...{
 if((*pw) & 0xF000 == 0x3000)...{
unsigned long *t = (unsigned long *)((unsigned long)(OldBase) + p->VirtualAddress + ((*pw) & 0x0FFF));
*t += Delta;
}
++pw;
}
p = (PImageBaseRelocation)pw;
}
}
// 卸載原外殼占用內(nèi)存
BOOL UnloadShell(HANDLE ProcHnd, unsigned long BaseAddr)
 ...{
typedef unsigned long (__stdcall *pfZwUnmapViewOfSection)(unsigned long, unsigned long);
pfZwUnmapViewOfSection ZwUnmapViewOfSection = NULL;
BOOL res = FALSE;
HMODULE m = LoadLibrary("ntdll.dll");
 if(m)...{
ZwUnmapViewOfSection = (pfZwUnmapViewOfSection)GetProcAddress(m, "ZwUnmapViewOfSection");
if(ZwUnmapViewOfSection)
res = (ZwUnmapViewOfSection((unsigned long)ProcHnd, BaseAddr) == 0);
FreeLibrary(m);
}
return res;
}
// 創(chuàng)建外殼進(jìn)程并獲取其基址、大小和當(dāng)前運(yùn)行狀態(tài)
BOOL CreateChild(char *Cmd, CONTEXT &Ctx, HANDLE &ProcHnd, HANDLE &ThrdHnd,
unsigned long &ProcId, unsigned long &BaseAddr, unsigned long &ImageSize)
 ...{
STARTUPINFOA si;
PROCESS_INFORMATION pi;
unsigned long old;
MEMORY_BASIC_INFORMATION MemInfo;
memset(&si, 0, sizeof(si));
memset(&pi, 0, sizeof(pi));
si.cb = sizeof(si);
BOOL res = CreateProcess(NULL, Cmd, NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &si, &pi); // 以?huà)炱鸱绞竭\(yùn)行進(jìn)程;
 if(res)...{
ProcHnd = pi.hProcess;
ThrdHnd = pi.hThread;
ProcId = pi.dwProcessId;
// 獲取外殼進(jìn)程運(yùn)行狀態(tài),[ctx.Ebx+8]內(nèi)存處存的是外殼進(jìn)程的加載基址,ctx.Eax存放有外殼進(jìn)程的入口地址
Ctx.ContextFlags = CONTEXT_FULL;
GetThreadContext(ThrdHnd, &Ctx);
ReadProcessMemory(ProcHnd, (void *)(Ctx.Ebx+8), &BaseAddr, sizeof(unsigned long), &old); // 讀取加載基址
void *p = (void *)BaseAddr;
// 計(jì)算外殼進(jìn)程占有的內(nèi)存
while(VirtualQueryEx(ProcHnd, p, &MemInfo, sizeof(MemInfo)))
 ...{
if(MemInfo.State = MEM_FREE) break;
p = (void *)((unsigned long)p + MemInfo.RegionSize);
}
ImageSize = (unsigned long)p - (unsigned long)BaseAddr;
}
return res;
}
// 創(chuàng)建外殼進(jìn)程并用目標(biāo)進(jìn)程替換它然后執(zhí)行
HANDLE AttachPE(char *CmdParam, PIMAGE_NT_HEADERS peH, PIMAGE_SECTION_HEADERS peSecH,
void *Ptr, unsigned long ImageSize, unsigned long &ProcId)
 ...{
HANDLE res = INVALID_HANDLE_VALUE;
CONTEXT Ctx;
HANDLE Thrd;
unsigned long Addr, Size;
char *s = PrepareShellExe(CmdParam, peH->OptionalHeader.ImageBase, ImageSize);
if(s==NULL) return res;
 if(CreateChild(s, Ctx, res, Thrd, ProcId, Addr, Size))...{
void *p = NULL;
unsigned long old;
 if((peH->OptionalHeader.ImageBase == Addr) && (Size >= ImageSize))...{// 外殼進(jìn)程可以容納目標(biāo)進(jìn)程并且加載地址一致
p = (void *)Addr;
VirtualProtectEx(res, p, Size, PAGE_EXECUTE_READWRITE, &old);
}
 else if(IsNT())...{
 if(UnloadShell(res, Addr))...{// 卸載外殼進(jìn)程占有內(nèi)存
p = MyVirtualAllocEx((unsigned long)res, (void *)peH->OptionalHeader.ImageBase, ImageSize, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);
}
 if((p == NULL) && HasRelocationTable(peH))...{// 分配內(nèi)存失敗并且目標(biāo)進(jìn)程支持重定向
p = MyVirtualAllocEx((unsigned long)res, NULL, ImageSize, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);
if(p) DoRelocation(peH, Ptr, p); // 重定向
}
}
 if(p)...{
WriteProcessMemory(res, (void *)(Ctx.Ebx+8), &p, sizeof(DWORD), &old); // 重置目標(biāo)進(jìn)程運(yùn)行環(huán)境中的基址
peH->OptionalHeader.ImageBase = (unsigned long)p;
 if(WriteProcessMemory(res, p, Ptr, ImageSize, &old))...{// 復(fù)制PE數(shù)據(jù)到目標(biāo)進(jìn)程
Ctx.ContextFlags = CONTEXT_FULL;
if((unsigned long)p == Addr)
Ctx.Eax = peH->OptionalHeader.ImageBase + peH->OptionalHeader.AddressOfEntryPoint; // 重置運(yùn)行環(huán)境中的入口地址
else
Ctx.Eax = (unsigned long)p + peH->OptionalHeader.AddressOfEntryPoint;
SetThreadContext(Thrd, &Ctx);// 更新運(yùn)行環(huán)境
ResumeThread(Thrd);// 執(zhí)行
CloseHandle(Thrd);
}
 else...{// 加載失敗,殺掉外殼進(jìn)程
TerminateProcess(res, 0);
CloseHandle(Thrd);
CloseHandle(res);
res = INVALID_HANDLE_VALUE;
}
}
 else...{// 加載失敗,殺掉外殼進(jìn)程
TerminateProcess(res, 0);
CloseHandle(Thrd);
CloseHandle(res);
res = INVALID_HANDLE_VALUE;
}
}
delete[] s;
return res;
}
 /**//**//**//*******************************************************
{ ******************************************************* }
{ * 從內(nèi)存中加載并運(yùn)行exe * }
{ ******************************************************* }
{ * 參數(shù): }
{ * Buffer: 內(nèi)存中的exe地址 }
{ * Len: 內(nèi)存中exe占用長(zhǎng)度 }
{ * CmdParam: 命令行參數(shù)(不包含exe文件名的剩余命令行參數(shù))}
{ * ProcessId: 返回的進(jìn)程Id }
{ * 返回值: 如果成功則返回進(jìn)程的Handle(ProcessHandle), }
{ 如果失敗則返回INVALID_HANDLE_VALUE }
{ ******************************************************* }
*******************************************************/
HANDLE MemExecute(void *ABuffer, long Len, char *CmdParam, unsigned long *ProcessId)
 ...{
HANDLE res = INVALID_HANDLE_VALUE;
PIMAGE_NT_HEADERS peH;
PIMAGE_SECTION_HEADERS peSecH;
void *Ptr;
unsigned long peSz;
if(AlignPEToMem(ABuffer, Len, peH, peSecH, Ptr, peSz))
 ...{
res = AttachPE(CmdParam, peH, peSecH, Ptr, peSz, *ProcessId);
VirtualFree(Ptr, peSz, MEM_DECOMMIT);
}
return res;
}
// 初始化
class CInit
 ...{
public:
CInit()
 ...{
MyVirtualAllocEx = (pfVirtualAllocEx)GetProcAddress(GetModuleHandle("Kernel32.dll"), "VirtualAllocEx");
}
}Init;
int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
 ...{
HANDLE hFile = NULL;
hFile = ::CreateFile( "f:\SourceFromCsdn2.exe"
, FILE_ALL_ACCESS
, 0
, NULL
, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, NULL
);
if( hFile == INVALID_HANDLE_VALUE )
return -1;
::SetFilePointer( hFile, 0, NULL, FILE_BEGIN);
DWORD dwFileSize = ::GetFileSize( hFile, NULL);
LPBYTE pBuf = new BYTE[dwFileSize];
memset( pBuf, 0, dwFileSize);
DWORD dwNumberOfBytesRead = 0;
::ReadFile( hFile
, pBuf
, dwFileSize
, &dwNumberOfBytesRead
, NULL
);
::CloseHandle(hFile);
unsigned long ulProcessId = 0;
MemExecute( pBuf, dwFileSize, "", &ulProcessId);
delete[] pBuf;
return 0;
}
|