文档章节

壳的编写(3)-- 获取被加壳文件的PE信息

o
 osc_wws45aot
发布于 2019/08/20 19:02
字数 1946
阅读 11
收藏 0

精选30+云产品,助力企业轻松上云!>>>

   我们将获取PE信息的操作单独封装到一个类文件中,在这个文件中,我们需要定义一个结构_PE_INFO用于封装PE相关信息并供外部调用,除此之外我们还需要定义RVA转文件偏移,文件偏移转,获取PE文件的信息,修复重定位信息,获取导出全局变量的文件偏移,设置新OEP,添加区段等函数。

         在Pack_Dll中添加一个CProcessingPE类,在ProcessingPE.h文件中内容如下:

#pragma once
#include <Windows.h>
#include <string.h>
#include <stdlib.h>


// 关键PE信息
typedef struct _PE_INFO
{
DWORD dwOEP; // 入口点
DWORD dwImageBase; // 映像基址
PIMAGE_DATA_DIRECTORY pDataDir; // 数据目录指针
IMAGE_DATA_DIRECTORY stcExport; // 导出目录
PIMAGE_SECTION_HEADER pSectionHeader; // 区段表头部指针
}PE_INFO, *PPE_INFO;


class CProcessingPE
{
public:
CProcessingPE(void);
~CProcessingPE(void);

public:
DWORD RVAToOffset(ULONG uRvaAddr); // RVA转文件偏移
DWORD OffsetToRVA(ULONG uOffsetAddr); // 文件偏移转RVA
BOOL GetPeInfo(LPVOID lpImageData, DWORD dwImageSize, PPE_INFO pPeInfo); // 获取PE文件的信息
void FixReloc(DWORD dwLoadImageAddr); // 修复重定位信息
PVOID GetExpVarAddr(LPCTSTR strVarName); // 获取导出全局变量的文件偏移
void SetOEP(DWORD dwOEP); // 设置新OEP
PVOID AddSection(LPCTSTR strName, DWORD dwSize, DWORD dwChara, PIMAGE_SECTION_HEADER pNewSection, PDWORD lpSize); // 添加区段
void SetDLL();	// 去除DLL动态加载标识
private:
DWORD m_dwFileDataAddr; // 目标文件所在缓存区的地址
DWORD m_dwFileDataSize; // 目标文件大小
PIMAGE_DOS_HEADER m_pDos_Header; // DOS头指针
PIMAGE_NT_HEADERS m_pNt_Header; // NT头指针
PE_INFO m_stcPeInfo; // PE关键信息

};

ProcessingPE.cpp 实现如下:

#include "stdafx.h"
#include "ProcessingPE.h"



CProcessingPE::CProcessingPE(void)
{
ZeroMemory(&m_stcPeInfo, sizeof(PE_INFO));
}
CProcessingPE::~CProcessingPE(void)
{
}


/************************************************************************/
/* 方法名称: RVAToOffset
/* 方法全称: CProcessingPE::RVAToOffset
/* 参数:	ULONG uRvaAddr	RVA地址值
/* 返回值:	DWORD	成功返回Offset,失败则返回0
/* 说明: 相对虚拟地址(RVA)转文件偏移(Offset)
/************************************************************************/
DWORD CProcessingPE::RVAToOffset(ULONG uRvaAddr)
{
//获取区段头表 
PIMAGE_SECTION_HEADER pSectionHeader = IMAGE_FIRST_SECTION(m_pNt_Header);

//获取区段的数量 --- nt表中的文件头中 
DWORD dwSize = m_pNt_Header->FileHeader.NumberOfSections;

for (DWORD i = 0; i < dwSize; i++)
{
if ((pSectionHeader[i].VirtualAddress <= uRvaAddr) && 
((pSectionHeader[i].VirtualAddress + pSectionHeader[i].Misc.VirtualSize) > uRvaAddr))
{
return ( uRvaAddr - pSectionHeader[i].VirtualAddress + pSectionHeader[i].PointerToRawData );
}
}
return 0;
}

/************************************************************************/
/* 方法名称: OffsetToRVA
/* 方法全称: CProcessingPE::OffsetToRVA
/* 参数:	ULONG uOffsetAddr	Offset地址值
/* 返回值:	DWORD	成功返回RVA地址,失败则返回0
/* 说明: 文件偏移(Offset)转相对虚拟地址(RVA)
/************************************************************************/
DWORD CProcessingPE::OffsetToRVA(ULONG uOffsetAddr)
{
//获取区段头表 
PIMAGE_SECTION_HEADER pSectionHeader = IMAGE_FIRST_SECTION(m_pNt_Header);

//获取区段的数量 --- nt表中的文件头中 
DWORD dwSize = m_pNt_Header->FileHeader.NumberOfSections;

for (DWORD i = 0; i < dwSize; i++)
{
if ((pSectionHeader[i].PointerToRawData <= uOffsetAddr) && 
(pSectionHeader[i].PointerToRawData + pSectionHeader[i].SizeOfRawData > uOffsetAddr))
{
return (uOffsetAddr - pSectionHeader[i].PointerToRawData + pSectionHeader[i].VirtualAddress );
}
}
return 0;
}



/************************************************************************/
/* 方法名称: GetPeInfo
/* 方法全称: CProcessingPE::GetPeInfo
/* 参数:	LPVOID lpImageData	目标文件所在缓存区的指针
/* 参数:	DWORD dwImageSize	目标文件的大小
/* 参数:	PPE_INFO pPeInfo	[OUT]用于传出目标文件的关键PE信息
/* 返回值:	BOOL	成功返回true,失败则返回false
/* 说明: 获取PE文件信息
/************************************************************************/
BOOL CProcessingPE::GetPeInfo(LPVOID lpImageData, DWORD dwImageSize, PPE_INFO pPeInfo)
{
// 1、判断映像指针是否有效
if (m_stcPeInfo.dwOEP)
{
CopyMemory(pPeInfo, &m_stcPeInfo, sizeof(PE_INFO));
return true;
}

if (!lpImageData)
{
return false;
}

// 2、将要获取PE的文件的地址和大小保存起来
m_dwFileDataAddr = (DWORD)lpImageData;
m_dwFileDataSize = dwImageSize;


// 3. 获取基本信息
// 3.1 获取DOS头、NT头
m_pDos_Header = (PIMAGE_DOS_HEADER)lpImageData;

// 检查PE文件是否有效
if (m_pDos_Header->e_magic != IMAGE_DOS_SIGNATURE) 
{
return false;
}

m_pNt_Header = (PIMAGE_NT_HEADERS)((DWORD)lpImageData + m_pDos_Header->e_lfanew);

// 检查PE文件是否有效
if ( m_pNt_Header->Signature != IMAGE_NT_SIGNATURE)
{
return false;
}

// 3.2 获取OEP
m_stcPeInfo.dwOEP = m_pNt_Header->OptionalHeader.AddressOfEntryPoint;
// 3.3 获取映像基址
m_stcPeInfo.dwImageBase = m_pNt_Header->OptionalHeader.ImageBase;
// 3.4 获取关键数据目录表的内容
PIMAGE_DATA_DIRECTORY lpDataDir = m_pNt_Header->OptionalHeader.DataDirectory;
m_stcPeInfo.pDataDir = lpDataDir;
CopyMemory(&m_stcPeInfo.stcExport, lpDataDir + IMAGE_DIRECTORY_ENTRY_EXPORT, sizeof(IMAGE_DATA_DIRECTORY));

// 3.5 获取区段表与其他详细信息
m_stcPeInfo.pSectionHeader = IMAGE_FIRST_SECTION(m_pNt_Header);

// 4. 传出处理结果
CopyMemory(pPeInfo, &m_stcPeInfo, sizeof(PE_INFO));

return true;
}


/************************************************************************/
/* 方法名称: FixReloc
/* 方法全称: CProcessingPE::FixReloc
/* 参数:	DWORD dwLoadImageAddr	此映像被加载后的预计模块基址
/* 返回值:	void
/* 说明: 修复重定位项 此函数依赖于RVAToOffset函数。
/* 注意:
/*	1. dwLoadImageAddr指的并非是其本身ImageBase的值,而是其被加载后的预
/*	计模块基址。
/*	2. 此重定位函数并未考虑到修复类型问题,如果要提高兼容性,应该分别对
/*	三种重定位类型进行区别对待。
/************************************************************************/
void CProcessingPE::FixReloc(DWORD dwLoadImageAddr)
{
// 1. 获取映像基址与代码段指针
DWORD dwImageBase;
PVOID lpCode;
dwImageBase = m_pNt_Header->OptionalHeader.ImageBase;
lpCode = (PVOID)(m_dwFileDataAddr + RVAToOffset(m_pNt_Header->OptionalHeader.BaseOfCode));

// 2. 获取重定位表在内存中的地址
PIMAGE_DATA_DIRECTORY pDataDir;
PIMAGE_BASE_RELOCATION pReloc;
pDataDir =&( m_pNt_Header->OptionalHeader.DataDirectory [IMAGE_DIRECTORY_ENTRY_BASERELOC]);
pReloc = (PIMAGE_BASE_RELOCATION)(m_dwFileDataAddr + RVAToOffset(pDataDir->VirtualAddress));

// 3. 遍历重定位表,并对目标代码进行重定位
while (pReloc->SizeOfBlock )
{
// 3.1 取得重定位项TypeOffset与其数量
PWORD pTypeOffset = (PWORD)((DWORD)pReloc + sizeof(IMAGE_BASE_RELOCATION));
DWORD dwCount = (pReloc->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(WORD);

// 3.2 循环检查重定位项
for (DWORD i = 0; i < dwCount; i++)
{
if (!*pTypeOffset) continue;

// 3.2.1 获取此重定位项指向的指针
DWORD dwPointToRVA = (*pTypeOffset & 0x0FFF) + pReloc->VirtualAddress;
PDWORD pPtr = (PDWORD)(m_dwFileDataAddr + RVAToOffset(dwPointToRVA) );
// 3.2.2 计算重定位增量值
DWORD dwIncrement = dwLoadImageAddr - dwImageBase;
// 3.2.3 修复需重定位的地址数据
*((PDWORD)pPtr) += dwIncrement;
pTypeOffset++;
}

// 3.3 指向下一个重定位块,开始另一次循环
pReloc = (PIMAGE_BASE_RELOCATION)((DWORD)pReloc + pReloc->SizeOfBlock);
}
}


/************************************************************************/
/* 方法名称: GetExpVarAddr
/* 方法全称: CProcessingPE::GetExpVarAddr
/* 参数:	LPCTSTR strVarName
/* 返回值:	PVOID
/* 说明: 获取导出全局变量的文件偏移
/************************************************************************/
PVOID CProcessingPE::GetExpVarAddr(LPCTSTR strVarName)
{
// 1、获取导出表地址,并将参数strVarName转为ASCII形式,方便对比查找
CHAR szVarName[MAX_PATH] = { 0 };
PIMAGE_EXPORT_DIRECTORY lpExport = (PIMAGE_EXPORT_DIRECTORY)(m_dwFileDataAddr + 
RVAToOffset(m_stcPeInfo.stcExport.VirtualAddress));
WideCharToMultiByte(CP_ACP, NULL, strVarName, -1, szVarName, _countof(szVarName), NULL, FALSE);

// 2、循环读取导出表输出项的输出函数,并依次与szVarName做比对,如果相同,则取出相对应的函数地址
for (DWORD i = 0; i < lpExport->NumberOfNames; i++)
{
PDWORD pNameAddr = (PDWORD)(m_dwFileDataAddr + RVAToOffset((DWORD)lpExport->AddressOfNames + 4 * i));
PCHAR strTempName = (PCHAR)(m_dwFileDataAddr + RVAToOffset(*pNameAddr));
if (!strcmp(szVarName, strTempName))
{
PDWORD pFunAddr = (PDWORD)(m_dwFileDataAddr + RVAToOffset((DWORD)lpExport->AddressOfFunctions + 4 * i));
return (PVOID)(m_dwFileDataAddr + RVAToOffset(*pFunAddr));
}
}
return 0;
}



/************************************************************************/
/* 方法名称: AddSection
/* 方法全称: CProcessingPE::AddSection
/* 参数:	LPCTSTR strName	新区段的名称
/* 参数:	DWORD dwSize	新区段的最小体积
/* 参数:	DWORD dwChara	新区段的属性
/* 参数:	PIMAGE_SECTION_HEADER pNewSection	[OUT]新区段的段结构指针
/* 参数:	PDWORD lpSize	[OUT]新区段的最终大小
/* 返回值:	PVOID	成功返回指向新区段现在所在内存的指针
/* 说明: 添加区段函数
/* 注:
/*	此函数并未考虑到目标函数存在附加数据等细节问题。
/************************************************************************/
PVOID CProcessingPE::AddSection(LPCTSTR strName, DWORD dwSize, DWORD dwChara, PIMAGE_SECTION_HEADER pNewSection, PDWORD lpSize)
{
PIMAGE_SECTION_HEADER pSectionHeader = IMAGE_FIRST_SECTION(m_pNt_Header);

// 1. 获取基本信息
DWORD dwDosSize = m_pDos_Header->e_lfanew;
DWORD dwPeSize = sizeof(IMAGE_NT_HEADERS32);
DWORD dwStnSize = m_pNt_Header->FileHeader.NumberOfSections * sizeof(IMAGE_SECTION_HEADER);
DWORD dwHeadSize = dwDosSize + dwPeSize + dwStnSize;

// 2. 在区段表中加入新区段的信息
// 2.1 获取基本信息
CHAR szVarName[7] = { 0 };
DWORD dwFileAlign = m_pNt_Header->OptionalHeader.FileAlignment; // 文件粒度
DWORD dwSectAlign = m_pNt_Header->OptionalHeader.SectionAlignment; // 区段粒度 
WORD dwNumOfsect = m_pNt_Header->FileHeader.NumberOfSections; // 区段数目

// 2.2 获取最后一个区段的信息
IMAGE_SECTION_HEADER stcLastSect = { 0 };
CopyMemory(&stcLastSect, &pSectionHeader[dwNumOfsect - 1], sizeof(IMAGE_SECTION_HEADER));

// 2.3 根据区段粒度计算相应地址信息
DWORD dwVStart = 0; // 虚拟地址起始位置
DWORD dwFStart = stcLastSect.SizeOfRawData + stcLastSect.PointerToRawData; // 文件地址起始位置

if (stcLastSect.Misc.VirtualSize%dwSectAlign)
dwVStart = (stcLastSect.Misc.VirtualSize / dwSectAlign + 1) * dwSectAlign + stcLastSect.VirtualAddress;
else
dwVStart = (stcLastSect.Misc.VirtualSize / dwSectAlign) * dwSectAlign + stcLastSect.VirtualAddress;

DWORD dwVirtualSize = 0; // 区段虚拟大小
DWORD dwSizeOfRawData = 0; // 区段文件大小



if (dwSize%dwSectAlign)
dwVirtualSize = (dwSize / dwSectAlign + 1) * dwSectAlign;
else
dwVirtualSize = (dwSize / dwSectAlign) * dwSectAlign;

if (dwSize%dwFileAlign)
dwSizeOfRawData = (dwSize / dwFileAlign + 1) * dwFileAlign;
else
dwSizeOfRawData = (dwSize / dwFileAlign) * dwFileAlign;

WideCharToMultiByte(CP_ACP, NULL, strName, -1, szVarName, _countof(szVarName), NULL, FALSE);

// 2.4 组装一个新的区段头
IMAGE_SECTION_HEADER stcNewSect = { 0 };
CopyMemory(stcNewSect.Name, szVarName, 7); // 区段名称
stcNewSect.Misc.VirtualSize = dwVirtualSize; // 虚拟大小
stcNewSect.VirtualAddress = dwVStart; // 虚拟地址
stcNewSect.SizeOfRawData = dwSizeOfRawData; // 文件大小
stcNewSect.PointerToRawData = dwFStart; // 文件地址
stcNewSect.Characteristics = dwChara; // 区段属性

// 2.5 写入指定位置
CopyMemory((PVOID)(m_dwFileDataAddr + dwHeadSize), &stcNewSect, sizeof(IMAGE_SECTION_HEADER));

// 3. 修改区段数目字段NumberOfSections
m_pNt_Header->FileHeader.NumberOfSections++;

// 4. 修改PE文件的景象尺寸字段SizeOfImage
m_pNt_Header->OptionalHeader.SizeOfImage += dwVirtualSize;

// 5. 返回新区段的详细信息、大小,以及可直接访问的地址
CopyMemory(pNewSection, &stcNewSect, sizeof(IMAGE_SECTION_HEADER));
*lpSize = dwSizeOfRawData;
return (PVOID)(m_dwFileDataAddr + dwFStart);
}


/************************************************************************/
/* 方法名称: SetOEP
/* 方法全称: CProcessingPE::SetOEP
/* 参数:	DWORD dwOEP	新OEP
/* 返回值:	void
/* 说明: 修改目标文件OEP
/************************************************************************/
void CProcessingPE::SetOEP(DWORD dwOEP)
{
m_pNt_Header->OptionalHeader.AddressOfEntryPoint = dwOEP;
}

/************************************************************************/
/* 方法名称: SetDLL
/* 方法全称: CProcessingPE::SetDLL
/* 返回值:	void
/* 说明: 去除DLL动态加载标识
/************************************************************************/
void CProcessingPE::SetDLL()
{
m_pNt_Header->OptionalHeader.DllCharacteristics = 0x0;
}

  

  

————————————————
版权声明:本文为CSDN博主「布衣僧」的原创文章,遵循CC 4.0 by-sa版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/oBuYiSeng/article/details/50478832

o
粉丝 0
博文 500
码字总数 0
作品 0
私信 提问
加载中
请先登录后再评论。
壳的编写(4)-- 进行加壳操作

由于我们在编写壳的部分比较简单,那么我们在编写加壳的过程中难免要复杂些。我们要完成加壳的操作必然会要读取被加壳程序的各种信息,并保存到一个结构中,为了便于后面的操作。还有在操作上...

osc_wws45aot
2019/08/20
9
0
用C++实现的壳(基础版)

最近一直在15PB学习(发此贴时以已工作两年),现阶段学的主要是关于壳的知识,正好现在也在做壳这个阶段项目,用了2天的时间实现了一个基础版的C++写的壳,拿出来跟大家分享一下,代码量不多,...

osc_wws45aot
2019/08/20
2
0
软件加壳的原理及实现

加壳的实现 我是个初学者,所知有限,难免会有错误,如果有人发现了错误,还请指正。 先大致说一下加壳的原理,即在原PE文件(后面称之为宿主文件)上加一个新的区段(也就是壳),然后从这个...

osc_uarhdl2n
2018/09/02
2
0
【黑客免杀攻防】读书笔记15 - 源码免杀、C++壳的编写

1、源码免杀 1.1 定位产生特征的源码 定位文件特征 1、根据MyCCL的特征码定位工具,定位出有特征的地址 2、根据VS的反汇编窗口,输入有特征的地址得到特征地址与源码的关系 3、插入MessageBo...

osc_1otmny9i
2018/07/09
3
0
脱壳 原理与工具介绍

在一些计算机软件里有一段专门负责保护软件不被非法修改或反编译的程序。它们一般都是先于程序运行,拿到控制权,然后完成它们保护软件的任务。就像动植物的壳一般都是在身体外面一样理所当然...

eric_zhang
2012/08/09
426
0

没有更多内容

加载失败,请刷新页面

加载更多

macz技巧分享—macOS高端使用技巧

Macos 的占有量不如 Windows,两者之间当操作方式也有很大的不同,当很多人熱悉 Windows 的操作之后,再接触 macos,觉得难上手,其实是习惯问题。如果你学习一些技巧,会觉得 macos 其实也不...

mac小叮当
28分钟前
11
0
手把手教你如何用黑白显示器显示彩色!

来源:大数据文摘 本文约1000字,建议阅读6分钟。 本文为你介绍如何通过黑白显示器上也能显示出彩色。 原来在黑白显示器上也能显示出彩色啊!通过在监视器上覆盖拜耳滤色镜,并拼接彩色图像,...

osc_jklrr90y
29分钟前
18
0
key-value结构排序:给定一个字符串,统计每个字符出现频率,先按value降序,再按key升序

对于key-value结构的排序 第一种:lambda表达式 第二种:函数 第三种:类对()的重载,仿函数形式 #include <iostream>#include <vector>#include <unordered_map>#include <string>#in......

osc_gwtkg2dc
30分钟前
0
0
BlockChain:2020年7月10日世界人工智能大会WAIC《链智未来 赋能产业区块链主题论坛——2020全球区块链创新50强》

BlockChain:2020年7月10日世界人工智能大会WAIC《链智未来 赋能产业区块链主题论坛——2020全球区块链创新50强》 目录 世界人工智能大会WAIC《链智未来 赋能产业区块链主题论坛——2020全球...

osc_vew1u0h0
31分钟前
0
0
BlockChain:2020年7月10日世界人工智能大会WAIC《链智未来 赋能产业区块链主题论坛》(三)

BlockChain:2020年7月10日世界人工智能大会WAIC《链智未来 赋能产业区块链主题论坛》(三) 目录 2020年7月10日世界人工智能大会WAIC《链智未来 赋能产业区块链主题论坛》 演讲嘉宾 演讲内容 ...

osc_8o71811p
32分钟前
0
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部