文章

滴水-day23-手动添加ShellCode

手动添加ShellCode

解释

该偏移所在节的物理偏移 -----> PointerToRawData

该偏移所在节的虚拟偏移 -----> VirtualAddress

基地址------------------------> ImageBase

文件偏移----------------------> 在文件中的位置

大致思路:

在text空白区添加硬编码,例如MessageBoxA

6A 00 6A 00 6A 00 6A 00 E8 00 00 00 00 E9 00 00 00 00

E8就是call,E9就是jmp

E8后面的地址是MessageboxA的地址,但是这里不能直接填写他的地址,需要经过计算出硬编码。

E8 X(4字节),这里的X是虚拟地址,也就是在调式器中看到的那些地址。

X计算方法:

虚拟地址 = 真实地址 - ((文件偏移 - 该偏移所在节的物理偏移 + 该偏移所在节的虚拟偏移)+ 基地址)

E9 X计算方法

E9后面跟的是原OEP,也就是 AddressOfEntryPoint 但E9跟的是虚拟地址,所以这里 AddressOfEntryPoint 要加上ImageBase在进行计算,与E8计算一致。

虚拟地址 = (AddressOfEntryPoint + ImageBase) - ((文件偏移 - 该偏移所在节的物理偏移 + 该偏移所在节的虚拟偏移)+ 基地址)

修改OEP,OEP不需要计算虚拟地址,使用的是相对虚拟地址,也就是不加ImageBase的地址。

相对虚拟地址 = 文件偏移 - 该偏移所在节的物理偏移 + 该偏移所在节的虚拟偏移

代码完成ShellCode的添加

#include "My_day23.h"

#define MessageBoxA_Address 0x758B0F40

DWORD ReadFile(IN const char* filepath, OUT LPVOID* fileBuffer)
{
	if (filepath == NULL)
	{
		std::cout << "参数错误" << std::endl;
		return 0;
	}
	std::cout << "打开文件:" << filepath << std::endl;

	FILE* file = NULL;
	errno_t fileResult = fopen_s(&file, filepath, "rb");
	if (fileResult != 0)
	{
		std::cout << "打开文件失败" << std::endl;
		return 0;
	}

	// 获取文件大小
	fseek(file,0,SEEK_END);
	long fileSize = ftell(file);
	fseek(file,0,SEEK_SET);

	std::cout << "文件字节:" << fileSize << std::endl;

	// 分配空间大小
	*fileBuffer = malloc(fileSize);
	if (*fileBuffer == NULL)
	{
		std::cout << "空间分配错误" << std::endl;
		fclose(file);
		return 0;
	}
	memset(*fileBuffer, 0, fileSize);

	// 读取文件
	size_t freadResult = fread_s(*fileBuffer, fileSize, 1, fileSize, file);
	if (freadResult != fileSize)
	{
		std::cout << "读取文件错误" << std::endl;
		return 0;
	}
	std::cout << "读取字节:" << freadResult << std::endl;
	return freadResult;
}

DWORD AddShellCode(LPVOID fileBuffer)
{
	// PE headers
	PIMAGE_DOS_HEADER pDOS_HEADER = NULL;
	PIMAGE_NT_HEADERS32 pNT_HEADER = NULL;
	PIMAGE_FILE_HEADER pFILE_HEADER = NULL;
	PIMAGE_OPTIONAL_HEADER32 pOPTIONAL_HEADER = NULL;
	PIMAGE_SECTION_HEADER pSECTION_HEADER = NULL;

	pDOS_HEADER = (PIMAGE_DOS_HEADER)((BYTE*)fileBuffer);
	pNT_HEADER = (PIMAGE_NT_HEADERS32)((BYTE*)fileBuffer + pDOS_HEADER->e_lfanew);
	pFILE_HEADER = (PIMAGE_FILE_HEADER)(((BYTE*)fileBuffer + pDOS_HEADER->e_lfanew) + 0x04);
	pOPTIONAL_HEADER = (PIMAGE_OPTIONAL_HEADER32)(((BYTE*)pFILE_HEADER + IMAGE_SIZEOF_FILE_HEADER));
	pSECTION_HEADER = IMAGE_FIRST_SECTION(pNT_HEADER);

	size_t NumberSections = pFILE_HEADER->NumberOfSections;
	size_t FileOffset = 0;
	size_t MemeryOffset = 0;
	size_t MiscSize = 0;

	// 修改Characteristics字段
	pNT_HEADER->FileHeader.Characteristics |= IMAGE_FILE_RELOCS_STRIPPED;

	// 获取.text字段
	for (int i = 0; i < NumberSections; i++)
	{
		//printf("%s\\\\n", pSECTION_HEADER[i].Name);
		if (strcmp((const char*)pSECTION_HEADER[i].Name,".text") == 0)
		{
			FileOffset = pSECTION_HEADER[i]. PointerToRawData;
			MemeryOffset = pSECTION_HEADER[i].VirtualAddress;
			MiscSize = pSECTION_HEADER[i].Misc.VirtualSize;
			break;
		}
	}
	if (FileOffset == 0)
	{
		std::cerr << "未找到.text" << std::endl;
		return 0;
	}
	std::cout << "FileOffset---->" << std::hex << FileOffset << std::endl;
	std::cout << "MemeryOffset---->" << std::hex << MemeryOffset << std::endl;
	std::cout << "MiscSize---->" << std::hex << MiscSize << std::endl;

	// OEP地址
	DWORD EntryPoin = pOPTIONAL_HEADER->AddressOfEntryPoint + pOPTIONAL_HEADER->ImageBase;

	// 硬编码
	char ShellCode[20] = { 0x6A, 0x00, 0x6A, 0x00, 0x6A, 0x00, 0x6A, 0x00, 0xE8, 0x00, 0x00, 0x00, 0x00, 0xE9, 0x00, 0x00, 0x00, 0x00 };
	int ShellLength = sizeof(ShellCode);

	// 计算 E8 跳转地址和 E9 跳转地址
	DWORD E8FileOffset = ((FileOffset + MiscSize) + 0x8) + 0x5;
	DWORD E9FileOffset = E8FileOffset + 0x5;
	DWORD E8CALL = MessageBoxA_Address - ((((E8FileOffset - FileOffset) + MemeryOffset)) + pOPTIONAL_HEADER->ImageBase);
	DWORD E9JMP = EntryPoin - ((((E9FileOffset - FileOffset) + MemeryOffset)) + pOPTIONAL_HEADER->ImageBase);
	std::cout << "E8FileOffset---->" << std::hex << E8FileOffset << std::endl;
	std::cout << "E9FileOffset---->" << std::hex << E9FileOffset << std::endl;
	std::cout << "EntryPoin------->" << std::hex << EntryPoin << std::endl;
	printf("E8CALL----> %X \\\\nE9JMP-----> %X\\\\n", E8CALL, E9JMP);

	// 将E8CALL的每个字节逐个添加到ShellCode的末尾
	ShellCode[9] = (E8CALL >> 0) & 0xFF;
	ShellCode[10] = (E8CALL >> 8) & 0xFF;
	ShellCode[11] = (E8CALL >> 16) & 0xFF;
	ShellCode[12] = (E8CALL >> 24) & 0xFF;

	// 将E9JMp的每个字节逐个添加到ShellCode的末尾
	ShellCode[14] = (E9JMP >> 0) & 0xFF;
	ShellCode[15] = (E9JMP >> 8) & 0xFF;
	ShellCode[16] = (E9JMP >> 16) & 0xFF;
	ShellCode[17] = (E9JMP >> 24) & 0xFF;

	// 打印修改后的ShellCode以查看结果
	/*printf("Modified ShellCode: ");
	for (int i = 0; i < ShellLength; i++) {
		printf("%02X ", (unsigned char)ShellCode[i]);
	}
	printf("\\\\n");*/

	// 确定 ShellCode 插入位置
	BYTE* address = ((BYTE*)fileBuffer + (FileOffset + MiscSize));

	// 插入ShellCode。
	for (int i = 0; i < ShellLength; i++)
	{
		address[i] = ShellCode[i];
	}

	/*for (int i = 0; i < ShellLength; i++)
	{
		*(((BYTE*)fileBuffer + (FileOffset + MiscSize)) + i) = ShellCode[i];
	}*/

	// 打印插入后的FileBuffer
	std::cout << "*******************************************S插入硬编码*****************************************" << std::endl;
	for (size_t i = 0; i < ShellLength; i++)
	{
		printf("%02X ", *(((BYTE*)fileBuffer + (FileOffset + MiscSize)) + i));
	}
	std::cout << "\\\\n*****************************************E插入硬编码*****************************************" << std::endl;

	// 修改OEP,OEP使用的是相对地址
	DWORD newOEP = (MiscSize + FileOffset) - FileOffset + MemeryOffset;
	std::cout << "newOEP----->" << std::hex << newOEP << std::endl;
	pOPTIONAL_HEADER->AddressOfEntryPoint = newOEP;
	return 1;
}

DWORD FwritrFile(LPVOID buffer, size_t size, const char* filePath)
{
	size_t len = strlen(filePath);
	char* newFilePath = (char*)malloc(len + 5);
	if (newFilePath == NULL)
	{
		return 0;
	}
	memset(newFilePath, 0, len + 5);
	strcpy_s(newFilePath, len + 5, filePath);
	char* newPath = strstr(newFilePath, ".exe");
	if (newPath != NULL) {
		strcpy_s(newPath, strlen("_new.exe") + 1 , "_new.exe");
	}

	if (filePath == NULL || buffer == NULL || size == NULL)
	{
		free(newFilePath);
		return 0;
	}

	FILE* file = NULL;
	fopen_s(&file, newFilePath, "wb");
	if (file == NULL)
	{
		std::cout << "写入文件失败" << std::endl;
		free(newFilePath);
		return 0;
	}
	size_t result = fwrite((unsigned char*)buffer, 1, size, file);
	if (result == size)
	{
		std::cout << "写入成功,写入字节数:" << std::dec <<  result << std::endl;
		std::cout << "写入文件位置:" << newFilePath << std::endl;
	}
	// 关闭文件
	fclose(file);
	// 释放分配的内存
	free(newFilePath);
	return result;
}

License:  CC BY 4.0