进程注入,解决了VirtualAllocEx在傀儡进程申请基地址内存失败的问题。

本文所贴出的PoC代码将告诉你如何通过CreateProcess创建一个傀儡进程(称之为可执行程序A),并把dwCreationFlags设置为CREATE_SUSPENDED,然后把另一个可执行程序(称之为可执行程序B)的内容加载到所创建的进程空间中,最终借用傀儡进程(A)的外壳来执行可执行程序B的内容。同时这段代码也会告诉你如何手工对Win32可执行程序进行重定位处理,以及如何从进程空间中取消已经映射的EXE映像。

在Windows操作系统下,通过给CreateProcess传递一个CREATE_SUSPENDED参数可以使得被创建的进程处于挂起状态,此时EXE的映像会被加载到进程空间中,但是并不会立即被执行,除非调用ResumeThread。在调用ResumeThread之前,可以通过调用ReadProcessMemory和WriteProcessMemory这样的API来读写进程空间的内容,这使得这样一种情形成为可能:通过读取另一个可执行文件的内容来覆盖刚创建的处于挂起状态的进程的空间,然后通过原始进程的空间来执行第二个可执行程序的内容。可以通过如下的步骤来完成这个过程:

1.通过给CreateProcess传递CREATE_SUSPENDED参数来创建一个处于挂起状态的进程(该进程为可执行程序A的一次执行)
2.通过调用GetThreadContext来获取被挂起进程的CONTEXT。其中ebx寄存器指向进程的PEB,eax寄存器的值为进程的入口点地址。
3.通过PEB来获取进程的基地址,如[EBX + 8]
4.把第二个可执行程序(B)加载到内存中并进行对齐处理,如果文件对齐尺寸和内存对齐尺寸不一致的话,这个操作是必须的。
5.如果可执行程序B和进程A有相同基地址并且B的内存镜像尺寸小于进程A的内存镜像尺寸,那么只需要简单的调用WriteProcessMemory来把可执行程序B的镜像写到进程A的内存空间之中,并从基地址开始执行即可。
6.否则的话,需要通过调用ZwUnmapViewOfSection(由ntdll.dll导出)来取消可执行程序A的映像映射,然后通过VirtualAllocEx在进程A中为可执行程序B分配足够的空间。调用VirtualAllocEx的时候,必须提供可执行程序B的基址以确保所分配的空间是从指定的位置开始的。然后把可执行程序B的镜像复制到进程A的内存空间并从分配的空间的起始地址开始执行。
7.如果ZwUnmapViewOfSection操作失败,而可执行程序B时可重定位的(存在重定位表),那么可以再进程A的内存空间的任意位置为B分配足够的空间,然后基于所分配的空间进行为B进行重定位处理,然后把可执行程序B的镜像复制到进程A的内存空间并从分配的空间的起始地址开始执行。
8.把进程A的PEB中的基地址改为可执行程序B的基地址。
9.把线程上下文中的EAX寄存器的值设置为可执行程序B的入口点。
10.通过调用SetThreadContext来设置线程的上下文。
11.通过调用ResumeThread来执行被挂起的进程。

PoC展示的技术点:

1.手工对可执行程序进行重定位处理(如果存在有重定位表的话)。
2.使用ZwUnmapViewOfSection取消原始EXE的内存映像的映射。
3.使用ReadProcessMemory和WriteProcessMemory来读写别的进程的内存空间。
4. 通过修改进程的PEB来修改其基地址的值。

使用方法:傀儡进程默认为calc.exe(计算器),命令行下输入 loadEXE.exe

//******************************************************************************************
// loadEXE.cpp : Defines the entry point for the console application.
//
// Proof-Of-Concept Code
// Copyright (c) 2004
// All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, provided that the above
// copyright notice(s) and this permission notice appear in all copies of
// the Software and that both the above copyright notice(s) and this
// permission notice appear in supporting documentation.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
// OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
// HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL
// INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING
// FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
// NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
// WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
//
// Usage:
// loadEXE <EXE filename>
//
// This will execute calc.exe in suspended mode and replace its image with
// the new EXE's image.  The thread is then resumed, thus causing the new EXE to
// execute within the process space of svchost.exe.
//
//******************************************************************************************
 
#include <stdio.h>
#include <windows.h>
#include <tlhelp32.h>
#include <psapi.h>
 
struct PE_Header 
{
	unsigned long signature;
	unsigned short machine;
	unsigned short numSections;
	unsigned long timeDateStamp;
	unsigned long pointerToSymbolTable;
	unsigned long numOfSymbols;
	unsigned short sizeOfOptionHeader;
	unsigned short characteristics;
};
 
struct PE_ExtHeader
{
	unsigned short magic;
	unsigned char majorLinkerVersion;
	unsigned char minorLinkerVersion;
	unsigned long sizeOfCode;
	unsigned long sizeOfInitializedData;
	unsigned long sizeOfUninitializedData;
	unsigned long addressOfEntryPoint;
	unsigned long baseOfCode;
	unsigned long baseOfData;
	unsigned long imageBase;
	unsigned long sectionAlignment;
	unsigned long fileAlignment;
	unsigned short majorOSVersion;
	unsigned short minorOSVersion;
	unsigned short majorImageVersion;
	unsigned short minorImageVersion;
	unsigned short majorSubsystemVersion;
	unsigned short minorSubsystemVersion;
	unsigned long reserved1;
	unsigned long sizeOfImage;
	unsigned long sizeOfHeaders;
	unsigned long checksum;
	unsigned short subsystem;
	unsigned short DLLCharacteristics;
	unsigned long sizeOfStackReserve;
	unsigned long sizeOfStackCommit;
	unsigned long sizeOfHeapReserve;
	unsigned long sizeOfHeapCommit;
	unsigned long loaderFlags;
	unsigned long numberOfRVAAndSizes;
	unsigned long exportTableAddress;
	unsigned long exportTableSize;
	unsigned long importTableAddress;
	unsigned long importTableSize;
	unsigned long resourceTableAddress;
	unsigned long resourceTableSize;
	unsigned long exceptionTableAddress;
	unsigned long exceptionTableSize;
	unsigned long certFilePointer;
	unsigned long certTableSize;
	unsigned long relocationTableAddress;
	unsigned long relocationTableSize;
	unsigned long debugDataAddress;
	unsigned long debugDataSize;
	unsigned long archDataAddress;
	unsigned long archDataSize;
	unsigned long globalPtrAddress;
	unsigned long globalPtrSize;
	unsigned long TLSTableAddress;
	unsigned long TLSTableSize;
	unsigned long loadConfigTableAddress;
	unsigned long loadConfigTableSize;
	unsigned long boundImportTableAddress;
	unsigned long boundImportTableSize;
	unsigned long importAddressTableAddress;
	unsigned long importAddressTableSize;
	unsigned long delayImportDescAddress;
	unsigned long delayImportDescSize;
	unsigned long COMHeaderAddress;
	unsigned long COMHeaderSize;
	unsigned long reserved2;
	unsigned long reserved3;
};
 
 
struct SectionHeader
{
	unsigned char sectionName[8];
	unsigned long virtualSize;
	unsigned long virtualAddress;
	unsigned long sizeOfRawData;
	unsigned long pointerToRawData;
	unsigned long pointerToRelocations;
	unsigned long pointerToLineNumbers;
	unsigned short numberOfRelocations;
	unsigned short numberOfLineNumbers;
	unsigned long characteristics;
};
 
struct MZHeader
{
	unsigned short signature;
	unsigned short partPag;
	unsigned short pageCnt;
	unsigned short reloCnt;
	unsigned short hdrSize;
	unsigned short minMem;
	unsigned short maxMem;
	unsigned short reloSS;
	unsigned short exeSP;
	unsigned short chksum;
	unsigned short exeIP;
	unsigned short reloCS;
	unsigned short tablOff;
	unsigned short overlay;
	unsigned char reserved[32];
	unsigned long offsetToPE;
};
 
 
struct ImportDirEntry
{
	DWORD importLookupTable;
	DWORD timeDateStamp;
	DWORD fowarderChain;
	DWORD nameRVA;
	DWORD importAddressTable;
};
 
 
//******************************************************************************************
//
// This function reads the MZ, PE, PE extended and Section Headers from an EXE file.
//
//******************************************************************************************
 
bool readPEInfo(FILE *fp, MZHeader *outMZ, PE_Header *outPE, PE_ExtHeader *outpeXH,
				SectionHeader **outSecHdr)
{
	fseek(fp, 0, SEEK_END);
	long fileSize = ftell(fp);
	fseek(fp, 0, SEEK_SET);
 
	if(fileSize < sizeof(MZHeader))
	{
		printf("File size too small\n");		
		return false;
	}
 
	// read MZ Header
	MZHeader mzH;
	fread(&mzH, sizeof(MZHeader), 1, fp);
 
	if(mzH.signature != 0x5a4d)		// MZ
	{
		printf("File does not have MZ header\n");
		return false;
	}
 
	//printf("Offset to PE Header = %X\n", mzH.offsetToPE);
 
	if((unsigned long)fileSize < mzH.offsetToPE + sizeof(PE_Header))
	{
		printf("File size too small\n");		
		return false;
	}
 
	// read PE Header
	fseek(fp, mzH.offsetToPE, SEEK_SET);
	PE_Header peH;
	fread(&peH, sizeof(PE_Header), 1, fp);
 
	//printf("Size of option header = %d\n", peH.sizeOfOptionHeader);
	//printf("Number of sections = %d\n", peH.numSections);
 
	if(peH.sizeOfOptionHeader != sizeof(PE_ExtHeader))
	{
		printf("Unexpected option header size.\n");
 
		return false;
	}
 
	// read PE Ext Header
	PE_ExtHeader peXH;
 
	fread(&peXH, sizeof(PE_ExtHeader), 1, fp);
 
	//printf("Import table address = %X\n", peXH.importTableAddress);
	//printf("Import table size = %X\n", peXH.importTableSize);
	//printf("Import address table address = %X\n", peXH.importAddressTableAddress);
	//printf("Import address table size = %X\n", peXH.importAddressTableSize);
 
 
	// read the sections
	SectionHeader *secHdr = new SectionHeader[peH.numSections];
 
	fread(secHdr, sizeof(SectionHeader) * peH.numSections, 1, fp);
 
	*outMZ = mzH;
	*outPE = peH;
	*outpeXH = peXH;
	*outSecHdr = secHdr;
 
	return true;
}
 
 
//******************************************************************************************
//
// This function calculates the size required to load an EXE into memory with proper alignment.
//
//******************************************************************************************
 
int calcTotalImageSize(MZHeader *inMZ, PE_Header *inPE, PE_ExtHeader *inpeXH,
				       SectionHeader *inSecHdr)
{
	int result = 0;
	int alignment = inpeXH->sectionAlignment;
 
	if(inpeXH->sizeOfHeaders % alignment == 0)
		result += inpeXH->sizeOfHeaders;
	else
	{
		int val = inpeXH->sizeOfHeaders / alignment;
		val++;
		result += (val * alignment);
	}
 
 
	for(int i = 0; i < inPE->numSections; i++)
	{
		if(inSecHdr[i].virtualSize)
		{
			if(inSecHdr[i].virtualSize % alignment == 0)
				result += inSecHdr[i].virtualSize;
			else
			{
				int val = inSecHdr[i].virtualSize / alignment;
				val++;
				result += (val * alignment);
			}
		}
	}
 
	return result;
}
 
 
//******************************************************************************************
//
// This function calculates the aligned size of a section
//
//******************************************************************************************
 
unsigned long getAlignedSize(unsigned long curSize, unsigned long alignment)
{	
	if(curSize % alignment == 0)
		return curSize;
	else
	{
		int val = curSize / alignment;
		val++;
		return (val * alignment);
	}
}
 
 
//******************************************************************************************
//
// This function loads a PE file into memory with proper alignment.
// Enough memory must be allocated at ptrLoc.
//
//******************************************************************************************
 
bool loadPE(FILE *fp, MZHeader *inMZ, PE_Header *inPE, PE_ExtHeader *inpeXH,
			SectionHeader *inSecHdr, LPVOID ptrLoc)
{
	char *outPtr = (char *)ptrLoc;
 
	fseek(fp, 0, SEEK_SET);
	unsigned long headerSize = inpeXH->sizeOfHeaders;
 
    int i = 0;
 
	// certain PE files have sectionHeaderSize value > size of PE file itself.  
	// this loop handles this situation by find the section that is nearest to the
	// PE header.
 
	for(i = 0; i < inPE->numSections; i++)
	{
		if(inSecHdr[i].pointerToRawData < headerSize)
			headerSize = inSecHdr[i].pointerToRawData;
	}
 
	// read the PE header
	unsigned long readSize = fread(outPtr, 1, headerSize, fp);
	//printf("HeaderSize = %d\n", headerSize);
	if(readSize != headerSize)
	{
		printf("Error reading headers (%d %d)\n", readSize, headerSize);
		return false;		
	}
 
	outPtr += getAlignedSize(inpeXH->sizeOfHeaders, inpeXH->sectionAlignment);
 
	// read the sections
	for(i = 0; i < inPE->numSections; i++)
	{
		if(inSecHdr[i].sizeOfRawData > 0)
		{
			unsigned long toRead = inSecHdr[i].sizeOfRawData;
			if(toRead > inSecHdr[i].virtualSize)
				toRead = inSecHdr[i].virtualSize;
 
			fseek(fp, inSecHdr[i].pointerToRawData, SEEK_SET);
			readSize = fread(outPtr, 1, toRead, fp);
 
			if(readSize != toRead)
			{
				printf("Error reading section %d\n", i);
				return false;
			}
			outPtr += getAlignedSize(inSecHdr[i].virtualSize, inpeXH->sectionAlignment);
		}
		else
		{
			// this handles the case where the PE file has an empty section. E.g. UPX0 section
			// in UPXed files.
 
			if(inSecHdr[i].virtualSize)
				outPtr += getAlignedSize(inSecHdr[i].virtualSize, inpeXH->sectionAlignment);
		}
	}
 
	return true;
}
 
 
struct FixupBlock
{
	unsigned long pageRVA;
	unsigned long blockSize;
};
 
 
//******************************************************************************************
//
// This function loads a PE file into memory with proper alignment.
// Enough memory must be allocated at ptrLoc.
//
//******************************************************************************************
 
void doRelocation(MZHeader *inMZ, PE_Header *inPE, PE_ExtHeader *inpeXH,
			      SectionHeader *inSecHdr, LPVOID ptrLoc, DWORD newBase)
{
	if(inpeXH->relocationTableAddress && inpeXH->relocationTableSize)
	{
		FixupBlock *fixBlk = (FixupBlock *)((char *)ptrLoc + inpeXH->relocationTableAddress);
		long delta = newBase - inpeXH->imageBase;
 
		while(fixBlk->blockSize)
		{
			//printf("Addr = %X\n", fixBlk->pageRVA);
			//printf("Size = %X\n", fixBlk->blockSize);
 
			int numEntries = (fixBlk->blockSize - sizeof(FixupBlock)) >> 1;
			//printf("Num Entries = %d\n", numEntries);
 
			unsigned short *offsetPtr = (unsigned short *)(fixBlk + 1);
 
			for(int i = 0; i < numEntries; i++)
			{
				DWORD *codeLoc = (DWORD *)((char *)ptrLoc + fixBlk->pageRVA + (*offsetPtr & 0x0FFF));
 
				int relocType = (*offsetPtr & 0xF000) >> 12;
 
				//printf("Val = %X\n", *offsetPtr);
				//printf("Type = %X\n", relocType);
 
				if(relocType == 3)
					*codeLoc = ((DWORD)*codeLoc) + delta;
				else
				{
					printf("Unknown relocation type = %d\n", relocType);
				}
				offsetPtr++;
			}
 
			fixBlk = (FixupBlock *)offsetPtr;
		}
	}	
}
 
 
#define TARGETPROC "calc.exe"
 
typedef struct _PROCINFO
{
	DWORD baseAddr;
	DWORD imageSize;
} PROCINFO;
 
 
 
//******************************************************************************************
//
// Creates the original EXE in suspended mode and returns its info in the PROCINFO structure.
//
//******************************************************************************************
 
 
BOOL createChild(PPROCESS_INFORMATION pi, PCONTEXT ctx, PROCINFO *outChildProcInfo)
{
	STARTUPINFO si = {0};
 
	if(CreateProcess(NULL, TARGETPROC,
		             NULL, NULL, 0, CREATE_SUSPENDED, NULL, NULL, &si, pi))		
	{
		ctx->ContextFlags=CONTEXT_FULL;
		GetThreadContext(pi->hThread, ctx);
 
		DWORD *pebInfo = (DWORD *)ctx->Ebx;
		DWORD read;
		ReadProcessMemory(pi->hProcess, &pebInfo[2], (LPVOID)&(outChildProcInfo->baseAddr), sizeof(DWORD), &read);
 
		DWORD curAddr = outChildProcInfo->baseAddr;
		MEMORY_BASIC_INFORMATION memInfo;
		while(VirtualQueryEx(pi->hProcess, (LPVOID)curAddr, &memInfo, sizeof(memInfo)))
		{
			if(memInfo.State == MEM_FREE)
				break;
			curAddr += memInfo.RegionSize;
		}
		outChildProcInfo->imageSize = (DWORD)curAddr - (DWORD)outChildProcInfo->baseAddr;
 
		return TRUE;
	}
	return FALSE;
}
 
 
//******************************************************************************************
//
// Returns true if the PE file has a relocation table
//
//******************************************************************************************
 
BOOL hasRelocationTable(PE_ExtHeader *inpeXH)
{
	if(inpeXH->relocationTableAddress && inpeXH->relocationTableSize)
	{
		return TRUE;
	}
	return FALSE;
}
 
 
typedef DWORD (WINAPI *PTRZwUnmapViewOfSection)(IN HANDLE ProcessHandle, IN PVOID BaseAddress);
 
 
//******************************************************************************************
//
// To replace the original EXE with another one we do the following.
// 1) Create the original EXE process in suspended mode.
// 2) Unmap the image of the original EXE.
// 3) Allocate memory at the baseaddress of the new EXE.
// 4) Load the new EXE image into the allocated memory.  
// 5) Windows will do the necessary imports and load the required DLLs for us when we resume the suspended 
//    thread.
//
// When the original EXE process is created in suspend mode, GetThreadContext returns these useful
// register values.
// EAX - process entry point
// EBX - points to PEB
//
// So before resuming the suspended thread, we need to set EAX of the context to the entry point of the
// new EXE.
//
//******************************************************************************************
 
void doFork(MZHeader *inMZ, PE_Header *inPE, PE_ExtHeader *inpeXH,
			SectionHeader *inSecHdr, LPVOID ptrLoc, DWORD imageSize)
{
	STARTUPINFO si = {0};
	PROCESS_INFORMATION pi;
	CONTEXT ctx;
	PROCINFO childInfo;
 
	if(createChild(&pi, &ctx, &childInfo)) 
	{		
		printf("Original EXE loaded (PID = %d).\n", pi.dwProcessId);
		printf("Original Base Addr = %X, Size = %X\n", childInfo.baseAddr, childInfo.imageSize);
 
		LPVOID v = (LPVOID)NULL;
 
		if(inpeXH->imageBase == childInfo.baseAddr && imageSize <= childInfo.imageSize)
		{
			// if new EXE has same baseaddr and is its size is <= to the original EXE, just
			// overwrite it in memory
			v = (LPVOID)childInfo.baseAddr;
			DWORD oldProtect;
			VirtualProtectEx(pi.hProcess, (LPVOID)childInfo.baseAddr, childInfo.imageSize, PAGE_EXECUTE_READWRITE, &oldProtect);			
 
			printf("Using Existing Mem for New EXE at %X\n", (unsigned long)v);
		}
		else
		{
			// get address of ZwUnmapViewOfSection
			PTRZwUnmapViewOfSection pZwUnmapViewOfSection = (PTRZwUnmapViewOfSection)GetProcAddress(GetModuleHandle("ntdll.dll"), "ZwUnmapViewOfSection");
 
			// try to unmap the original EXE image
			if(pZwUnmapViewOfSection(pi.hProcess, (LPVOID)childInfo.baseAddr) == 0)
			{
				// allocate memory for the new EXE image at the prefered imagebase.
				v = VirtualAllocEx(pi.hProcess, (LPVOID)inpeXH->imageBase, imageSize, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);
				if(v)
					printf("Unmapped and Allocated Mem for New EXE at %X\n", (unsigned long)v);
			}
		}
 
		if(!v && hasRelocationTable(inpeXH))
		{
			// if unmap failed but EXE is relocatable, then we try to load the EXE at another
			// location
			v = VirtualAllocEx(pi.hProcess, (void *)NULL, imageSize, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);
			if(v)
			{
				printf("Allocated Mem for New EXE at %X. EXE will be relocated.\n", (unsigned long)v);
 
				// we've got to do the relocation ourself if we load the image at another
				// memory location				
				doRelocation(inMZ, inPE, inpeXH, inSecHdr, ptrLoc, (DWORD)v);
			}
		}
 
		printf("EIP = %X\n", ctx.Eip);
		printf("EAX = %X\n", ctx.Eax);
		printf("EBX = %X\n", ctx.Ebx);		// EBX points to PEB
		printf("ECX = %X\n", ctx.Ecx);
		printf("EDX = %X\n", ctx.Edx);
 
		if(v)
		{			
			printf("New EXE Image Size = %X\n", imageSize);
 
			// patch the EXE base addr in PEB (PEB + 8 holds process base addr)
			DWORD *pebInfo = (DWORD *)ctx.Ebx;
			DWORD wrote;						
			WriteProcessMemory(pi.hProcess, &pebInfo[2], &v, sizeof(DWORD), &wrote);
 
			// patch the base addr in the PE header of the EXE that we load ourselves
			PE_ExtHeader *peXH = (PE_ExtHeader *)((DWORD)inMZ->offsetToPE + sizeof(PE_Header) + (DWORD)ptrLoc);
			peXH->imageBase = (DWORD)v;
 
			if(WriteProcessMemory(pi.hProcess, v, ptrLoc, imageSize, NULL))
			{	
				printf("New EXE image injected into process.\n");
 
				ctx.ContextFlags=CONTEXT_FULL;				
				//ctx.Eip = (DWORD)v + ((DWORD)dllLoaderWritePtr - (DWORD)ptrLoc);
 
				if((DWORD)v == childInfo.baseAddr)
				{
					ctx.Eax = (DWORD)inpeXH->imageBase + inpeXH->addressOfEntryPoint;		// eax holds new entry point
				}
				else
				{
					// in this case, the DLL was not loaded at the baseaddr, i.e. manual relocation was
					// performed.
					ctx.Eax = (DWORD)v + inpeXH->addressOfEntryPoint;		// eax holds new entry point
				}
 
				printf("********> EIP = %X\n", ctx.Eip);
				printf("********> EAX = %X\n", ctx.Eax);
 
				SetThreadContext(pi.hThread,&ctx);
 
				ResumeThread(pi.hThread);
				printf("Process resumed (PID = %d).\n", pi.dwProcessId);
			}
			else
			{
				printf("WriteProcessMemory failed\n");
				TerminateProcess(pi.hProcess, 0);
			}
		}
		else
		{
			printf("Load failed.  Consider making this EXE relocatable.\n");
			TerminateProcess(pi.hProcess, 0);
		}
	}
	else
	{
		printf("Cannot load %s\n", TARGETPROC);
	}
}
 
 
 
 
int main(int argc, char* argv[])
{
	if(argc != 2)
	{
		printf("\nUsage: %s <EXE filename>\n", argv[0]);
		return 1;
	}
 
	FILE *fp = fopen(argv[1], "rb");
	if(fp)
	{
		MZHeader mzH;
		PE_Header peH;
		PE_ExtHeader peXH;
		SectionHeader *secHdr;
 
		if(readPEInfo(fp, &mzH, &peH, &peXH, &secHdr))
		{
			int imageSize = calcTotalImageSize(&mzH, &peH, &peXH, secHdr);
			//printf("Image Size = %X\n", imageSize);
 
			LPVOID ptrLoc = VirtualAlloc(NULL, imageSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
			if(ptrLoc)
			{
				//printf("Memory allocated at %X\n", ptrLoc);
				loadPE(fp, &mzH, &peH, &peXH, secHdr, ptrLoc);												
 
				doFork(&mzH, &peH, &peXH, secHdr, ptrLoc, imageSize);								
			}
			else
				printf("Allocation failed\n");
		}
 
		fclose(fp);
	}
	else
		printf("\nCannot open the EXE file!\n");
 
	return 0;
}
  • 1
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
以下是一个简单的C++案例,展示了如何向进程A注入一个DLL,并在DLL中使用SetWindowsHookEx函数来拦截进程A的WH_GETMESSAGE消息: 1. 编写DLL代码 首先,我们需要编写一个DLL文件,这个DLL文件将会被注入进程A中,并用于拦截WH_GETMESSAGE消息。我们可以使用Visual Studio等工具创建一个空的DLL项目,并将以下代码添加到DLL的源文件中: ```c++ #include <windows.h> // 全局钩子句柄 HHOOK gHook = 0; // 钩子回调函数 LRESULT CALLBACK HookCallback(int nCode, WPARAM wParam, LPARAM lParam) { if (nCode == HC_ACTION && wParam == PM_REMOVE) { MSG* pMsg = (MSG*)lParam; // 判断是否为WH_GETMESSAGE消息 if (pMsg->message == WM_GETMESSAGE) { // 在这里进行你想要的操作,例如记录日志、修改消息等 } } return CallNextHookEx(gHook, nCode, wParam, lParam); } // DLL入口函数 BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) { switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: // 注册全局钩子 gHook = SetWindowsHookEx(WH_GETMESSAGE, HookCallback, hModule, 0); break; case DLL_PROCESS_DETACH: // 卸载全局钩子 UnhookWindowsHookEx(gHook); break; } return TRUE; } ``` 这个DLL文件中包含了一个全局钩子句柄 `gHook`,以及一个钩子回调函数 `HookCallback`。在`DllMain`函数中,我们在DLL被注入到目标进程时注册了一个全局的 `WH_GETMESSAGE` 钩子,并在DLL被卸载时卸载了这个钩子。 在钩子回调函数中,我们可以对接收到的消息进行处理。在这个例子中,我们简单地判断了消息是否为 `WH_GETMESSAGE` 消息,并在这个消息被拦截时进行了一些操作(例如记录日志、修改消息等)。 2. 注入DLL到进程A中 接下来,我们需要编写一个程序,用于将上面编写的DLL文件注入到目标进程A中。我们可以使用Windows API中的 `CreateRemoteThread` 函数来实现这个功能。以下是一个简单的注入代码: ```c++ #include <windows.h> #include <tlhelp32.h> #include <tchar.h> // 获取指定进程的PID DWORD GetProcessID(const TCHAR* szProcessName) { PROCESSENTRY32 pe = { sizeof(pe) }; HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); if (Process32First(hSnapshot, &pe)) { do { if (_tcsicmp(pe.szExeFile, szProcessName) == 0) { CloseHandle(hSnapshot); return pe.th32ProcessID; } } while (Process32Next(hSnapshot, &pe)); } CloseHandle(hSnapshot); return 0; } // 向指定进程注入DLL BOOL InjectDLL(DWORD dwProcessID, const TCHAR* szDLLPath) { HANDLE hProcess = OpenProcess(PROCESS_CREATE_THREAD | PROCESS_VM_OPERATION | PROCESS_VM_WRITE, FALSE, dwProcessID); if (hProcess == NULL) { return FALSE; } // 在目标进程中分配内存 LPVOID lpRemoteBuffer = VirtualAllocEx(hProcess, NULL, _tcslen(szDLLPath) * sizeof(TCHAR) + sizeof(TCHAR), MEM_COMMIT, PAGE_READWRITE); if (lpRemoteBuffer == NULL) { CloseHandle(hProcess); return FALSE; } // 将DLL路径写入目标进程中 if (!WriteProcessMemory(hProcess, lpRemoteBuffer, szDLLPath, _tcslen(szDLLPath) * sizeof(TCHAR), NULL)) { VirtualFreeEx(hProcess, lpRemoteBuffer, 0, MEM_RELEASE); CloseHandle(hProcess); return FALSE; } // 在目标进程中创建远程线程,执行LoadLibrary函数,加载DLL HANDLE hThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)LoadLibrary, lpRemoteBuffer, 0, NULL); if (hThread == NULL) { VirtualFreeEx(hProcess, lpRemoteBuffer, 0, MEM_RELEASE); CloseHandle(hProcess); return FALSE; } WaitForSingleObject(hThread, INFINITE); VirtualFreeEx(hProcess, lpRemoteBuffer, 0, MEM_RELEASE); CloseHandle(hThread); CloseHandle(hProcess); return TRUE; } int main() { // 获取目标进程的PID DWORD dwProcessID = GetProcessID(_T("ProcessA.exe")); if (dwProcessID == 0) { printf("无法找到进程!"); return 1; } // 注入DLL if (!InjectDLL(dwProcessID, _T("C:\\MyDLL.dll"))) { printf("注入DLL失败!"); return 1; } printf("DLL已成功注入!"); return 0; } ``` 在这个例子中,我们首先定义了两个函数 `GetProcessID` 和 `InjectDLL`,用于获取目标进程的PID和向目标进程注入DLL。 在 `main` 函数中,我们首先调用 `GetProcessID` 函数获取目标进程的PID。如果无法找到目标进程,我们将输出一条错误信息并返回一个非零的错误码。 接着,我们调用 `InjectDLL` 函数将DLL注入到目标进程中。如果注入失败,我们也将输出一条错误信息并返回一个非零的错误码。 最后,我们输出一条提示信息,并返回一个零值表示程序执行成功。 注意:在实际使用中,我们需要将 `MyDLL.dll` 替换为实际的DLL文件路径。同时,需要确保目标进程权限足够高,以便我们能够向目标进程注入DLL。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值