把PE映像文件从内存中DUMP到磁盘

加密与解密(三)-- dump抓取内存映像文件 使用情况 在特定时刻,将整个存储装置或存储装置中的某部分的内容记录到另一个存储装置中。其目的一般是为了除错 将具备可读格式的数据,从主要或辅助存储体复制带外部媒体。比如:磁带、磁盘或打印机等媒体 为收集错误信息而复制整个虚拟存储体或其中的部分内容 dump程序要做的事情通常分为以下几步: 在系统中找到目标进程 在晋城中确定目标进程的大小 imagesize 把目标进程中的数据保存到文件 实... 阅读详情

引言: 引用看雪学院《软件加密技术内幕》开篇的第一段话:“操作系统的可执行文件格式和数据结构揭示了藏在操作系统内部的秘密,理解EXE或DLL将有助于对操作系统的深刻理解。了解了EXE和DLL里面的奥秘,你将成为一名知识更加渊博的程序员!”
可以看到,作为网络安全爱好者的我们,掌握和熟练利用PE(Portable Executable)文件格式的知识将必定能为我们学习黑客技术和攻防知识打下良好的基础。
在网络攻防的对抗中,常常接触到有关PE文件格式方面的技术,比如缓冲区溢出技术中编写Win32 ShellCode时利用PE文件结构的特征查找API函数地址就是一个很经典的例子,又比如,令人寒心的PE型病毒都是利用PE文件结构而大规模感染系统的其他PE文件。
PE文件格式的基础知识,如果读者不熟悉的话,网上这方面的教程比比皆是。另外,对于习惯看书的读者们,强烈推荐看雪软件的《加密解密II》,《软件加密技术内幕》这两本经典之作。
现在,市面上有很多的静态分析PE文件方面的工具,像PeDump和Pe Explorer(附件中均有收录)这两款工具,可算的上是其中的佼佼者。前者是命令行下的,从它的可选参数就可以看出期功能的强大。后者是图形化的,界面友好而且功能也很强大。
读者肯定有疑问了,既然都有这么现成的工具了,那写本文目的是什么呢?这里说明一下,市面上出现的PE分析工具包括推荐的两款工具都有一个共同点,就是他们加载的都是磁盘上的PE文件,但有时候我们却要分析内存中的PE映象,这个又要如何实现呢?我们下面就自己来分析解决这个问题吧!

 

首先,要明确的一点是,PE文件格式在磁盘中的数据结构布局和内存中的数据格式布局是一致的,就是说, 知道如何在PE文件中寻找一些内容,那么几乎都能在被装入到内存的映射文件中找到相同的信息。
这样的话,就好办了,我们可以使用乾坤大挪移,用类似分析磁盘PE文件的方法来分析内存中的PE文件了。大家可以看到,所有PE文件(包括32位的DLL)都是以一个简单MZ-DOS头开始,MZ格式的文件头在WINNT.H中有定义,其IMAGE_DOS_HEADER结构如下(左边数字是到文件头的偏移量):
typedef struct _IMAGE_DOS_HEADER { // DOS .EXE 头
+0h WORD e_magic; // “MZ”
…...
+3Ch LONG e_lfanew; //指向PE文件头偏移
} IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER;
我只列出了两个最重要的成员,e_magic是DOS可执行文件标记“MZ”,而e_lfanew指向PE文件头“PE”,0,0。执行程序在执行的时候,PE文件装载器将从MZ-DOS头的e_lfanew字段找到PE头起始偏移,再跳到真正的PE文件头处。IMAGE_NT_HEADERS的数据结构如下:
typedef struct _IMAGE_NT_HEADERS {
+0h DWORD Signature; // PE文件标识
+4h IMAGE_FILE_HEADER FileHeader; // 映象文件头
+18h IMAGE_OPTIONAL_HEADER32 OptionalHeader; // 可选映象头
} IMAGE_NT_HEADERS, *PIMAGE_NT_HEADERS;
PE文件头和原始数据之间存在一个块表,块表包含每个块在映象中的信息。块表IMAGE_SECTION_HEADER包含了PE文件中的 Sectin的重要资料,其结构如下:
typedef struct _IMAGE_SECTION_HEADER {
+0h BYTE Name[IMAGE_SIZEOF_SHORT_NAME]; // 8个字节的块名
+8h union {
DWORD PhysicalAddress; 
DWORD VirtualSize; // 该块真实长度。是块对齐前的长度
} Misc;
+0Ch DWORD VirtualAddress; // 该块的RVA
+10h DWORD SizeOfRawData; // 在文件中对齐后的尺寸 
+14h DWORD PointerToRawData; // 在文件中偏移
… …
+28h DWORD Characteristics; 
} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;
出于学习的目的,我们就只分析这三个结构,你完全可以获得其他你感兴趣的结构字段。
下面我们具体到程序上来。
在我的程序里,首先定义了一个ShowHelp()的帮助函数,主要用来显示用法。
可以看到,我所实现的PeDump程序,必须带一个进程PID的参数,你可以从任务管理器里查看每个你想要Dump的进程ID号。

而第二个参数Address是可选的,“*”表示Dump所有模块在内存中的PE映像文件,也可选任一有效的地址来Dump,而如果不加第二个参数,则默认Dump第一个模块的PE映象。
主要功能的实现原理说来很简单, 就是利用了ReadProcessMemory()这个函数丛内存中读取信息,然后利用PE映像在内存中和在磁盘中数据结构的布局一致性来分析。我们主要定义了的以下几个函数,功能及注释如下:
1)PageSize():获得内存页大小
DWORD PageSize()
{
SYSTEM_INFO systemInfo; // 系统信息结构
GetSystemInfo(&systemInfo); // 获得系统信息结构
return systemInfo.dwPageSize; // 返回内存页大小
}
2) GetModuleName(): 获得模块文件名
char* GetModuleName(HANDLE handle, DWORD address)
{
HMODULE hModule[1024]; 
DWORD cbNeeded;
char szName[MAX_PATH], *ptr = NULL;
// 枚举进程模块
if ( EnumProcessModules(handle, hModule, sizeof(hModule), &cbNeeded) )

for (int i = 0; i < (cbNeeded / sizeof(HMODULE)); i++) 
{
if (address == (DWORD)hModule[i])
{ // 获得模块名 
if (GetModuleBaseName(handle, hModule[i], 
szName, sizeof(szName))) 
{
ptr = strdup(szName);
}
break;
}
}
}
return ptr;
}
注意,这里用到了Psapi.dll库的两个API函数EnumProcessModules()和GetModuleBaseName(),我这里添加了Richard Shupak 写的Psapi.h的头文件(附件内提供),并通过预处理#pragma comment(lib, “psapi.dll”)加载PSAPI.DLL,如果你的sdk没有包含该头文件,也许你需要利用LoadLibrary()和GetProcAddress()来动态加载。

3) DumpPe(): 主功能函数, Dump内存中的PE文件数据
unsigned char* DumpPe(HANDLE hhProcess, unsigned char *szAddress, int *iLen)
{
PIMAGE_DOS_HEADER pDos; // MS-DOS头
PIMAGE_NT_HEADERS pNt; // NT映象头
PIMAGE_SECTION_HEADER pSection; // 区块头

unsigned char szHeader[8192], *szSection;
DWORD dwRet;
// 从指定的地址szAddress读进程内存内容到szHeader
if ( !ReadProcessMemory(hhProcess, (LPVOID)szAddress, (LPVOID)szHeader, sizeof(szHeader), NULL))
return FALSE;
// 通过文件头两个字节是否等于“MZ”来判断是否为PE文件
if ( memcmp(szHeader, "MZ", 2) )
{
return FALSE;
}

printf("映象在0x%p/n", szAddress);

pDos = (PIMAGE_DOS_HEADER)szHeader; 
// MS-DOS头的最后一个成员e_lfanew指向NT映象头
pNt = (PIMAGE_NT_HEADERS)(szHeader+pDos->e_lfanew);
// 获得所需分配内存空间的大小
*iLen = pNt->OptionalHeader.SizeOfImage +
pNt->OptionalHeader.SizeOfHeaders + PageSize();
// 分配虚拟内存空间, 注意后面的保护属性为PAGE_READWRITE
if ( !(dwRet = (DWORD)VirtualAlloc(NULL,
pNt->OptionalHeader.SizeOfImage +
pNt->OptionalHeader.SizeOfHeaders + PageSize(),
MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE)) ) 
{
printf("%d/n", GetLastError());
printf("不能分配有效内存/n");
return FALSE;
}
// 打印Dump的PE头重要信息
printf("DUMPING 头.../n"
" 头大小 : 0x%p/n"
" 虚拟地址 : 0x%p/n"
" 映象地址 : 0x%p/n",
pNt->OptionalHeader.SizeOfHeaders, szAddress, 0);

memcpy((char*)dwRet, szHeader, pNt->OptionalHeader.SizeOfHeaders);

pSection = IMAGE_FIRST_SECTION(pNt);
for(int i=0; i<pNt->FileHeader.NumberOfSections; i++)
{ // 打印Dump的PE块信息
printf("DUMPING 块#%d.../n", i);
printf(" 块大小 : 0x%p/n", pSection[i].SizeOfRawData);
printf(" 虚拟地址 : 0x%p/n", szAddress + pSection[i].VirtualAddress);
printf(" 映象地址 : 0x%p/n", dwRet + pSection[i].PointerToRawData);

if ( !(szSection=(unsigned char*)VirtualAlloc(NULL, 
pSection[i].Misc.VirtualSize, 
MEM_RESERVE|MEM_COMMIT, 
PAGE_READWRITE)) ) 
{
printf("不能分配有效内存/n");
return FALSE;
}
// 从进程内存里读映像块
if ( !ReadProcessMemory(hhProcess, szAddress+pSection[i].VirtualAddress,
szSection, pSection[i].Misc.VirtualSize, NULL))
{
printf("不能读取映象块/n");
return FALSE;
}

memcpy((char*)dwRet+pSection[i].PointerToRawData, szSection, pSection[i].Misc.VirtualSize);
// 释放申请到的内存
VirtualFree(szSection, 0, MEM_RELEASE);
}

return (unsigned char *)dwRet;
}

4) DumpAddress(): Dump内存地址
void DumpAddress(HANDLE process, DWORD address, unsigned char *pe, int len, int pid)
{
char szFileName[MAX_PATH], *szMouduleName=NULL;
// 获取模块名
szMouduleName = GetModuleName(process, address);
if ( !szMouduleName ) 
{
szMouduleName = strdup("未知模块.txt");
}

memset(szFileName, 0, MAX_PATH);
//按照 “PID-内存地址-模块名.dat” 构造磁盘文件名
sprintf(szFileName, "%d-%p-%s.dat", pid, address, szMouduleName);
free(szMouduleName); // 释放strup()申请来的资源
// 写PE Dump结果到磁盘
WritePeDump(szFileName, pe, len);
}

5) WritePeDump():写到磁盘文件中
void WritePeDump(char *outfile, unsigned char*pe, int len)
{
HANDLE hFile = NULL;
DWORD cbWritten;
printf("开始写到文件.../n"); 
hFile = CreateFile(outfile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 
FILE_ATTRIBUTE_NORMAL, NULL); // 创建文件,具有写属性
if (hFile == INVALID_HANDLE_VALUE)
{
printf("创建文件错误/n");
return;
}
// 写文件操作
WriteFile(hFile, pe, len, &cbWritten, NULL);

if ( len != cbWritten )
{ // 校验写文件操作是否正确
printf("写入文件时错误/n");
}
}
到这,一个简单的从内存DUMP PE文件映像到磁盘的程序就完成了。
编译后我选取了金山词霸的PID作为参数,得到的测试结果。
和PeDump或PE Explorer对比发现,结果是一样的。呵呵。 是不是很有成就感呢。

小结:
本文主要简单讨论了如何从内存中分析PE文件的相关技术, 实现了很简陋的功能, 但理解了思路,你完全可以加入很多诱人的功能。
无论是病毒技术,破解技术还是溢出技术,熟练掌握PE文件格式的分析技术是很有用的,别以为网络上那些琳琅满目的PE分析工具有多么的神奇,只要你掌握了PE文件格式的基本架构和基本的C/C++编程功底,你也能打造自己的PE工具的。记住,伴随着黑防的努力,我们也在一步步成长!

PE-bear:Windows逆向分析与恶意软件检测的PE文件解析利器 在Windows平台的可执行文件PE)格式是理解软件运行机制与安全分析的基础。PE文件作为操作系统加载执行的蓝图,其结构解析是逆向工程与恶意软件分析的核心环节。传统反汇编工具虽功能强大,但在快速解析文件结构、可视化头部信息及节区属性时效率不足。PE-bear正是为此设计的专用工具,它专注于PE格式的静态解析,能直观展示DOS头、NT头、导入表、资源等关键结构,帮助工程师快速定位可疑代码与行为模式。在恶意软件分析中,通过提取导入函数、计算节区熵值、识别加壳特征,PE-bear能高效提取威胁指标(IOCs), 阅读详情

相关推荐

深入解析Windows PE文件:从结构原理到逆向分析实战

可执行文件(EXE)是软件在操作系统上运行的最终形态,理解其内部结构是软件工程与安全分析的基础。其核心在于解析特定的文件格式规范,例如Windows平台广泛采用的PE(Portable Executable)格式。该格式定义了文件头、节表、代码与数据节等标准结构,并涉及虚拟地址与文件偏移转换等关键原理。掌握这些原理对于软件调试、性能分析、兼容性排查及安全漏洞挖掘具有重要价值。在逆向工程与恶意软件分析等应用场景中,通过使用dumpbin、Ghidra等工具进行静态分析,或结合x64dbg进行动态调试,可以有效

weixin_33912246的博客 397

PEDump.zip_ JIURL PEDUMP_ dump_R-Tree_coff_pedump

PEDUMP程序自从1994年的版本以来,已经有了明显的改善。它可以显示在PE里的每一个数据类型,包括: IMAGE_NT_HEADERS   输入/输出表   资源   基址重定位   debug目录   延迟装入输入表   绑定输入描述符   IA-64异常处理表   TLS初始化数据   .NET运行时头部 除了可导出PE可执行文件外,PEDUMP也能导出COFF格式的OBJ文件、COFF输入库(新的和旧的格式)、COFF符号表和DBG文件PEDUMP是一个命令行程序,不带任何选项对我刚刚描述的文件类型去运行,将会有一个包括许多有用数据结构的缺省导出。几个可以有额外输出的选项列在如下: ============================================== /A  dump里包括所有的内容   /B  显示基址重地位   /H  包括区段的16进制形式dump   /I  包括输入地址表thunk地址   /L  包括行号信息   /P  包括PDATA(运行时函数)   /R  包括详细的资源(字符串表和对话框) /S  显示符号表

X64dbg脚本实现自动DUMP运行中解密出的PE文件

X64DBG脚本

摔不死的笨鸟的博客 2206

[其他类别]逆向发布(小偷)系统v.3_nxxt3.zip

[其他类别]逆向发布(小偷)系统v.3_nxxt3.zip

病毒分析常用技巧-修复内存dump文件

病毒常用的技俩之一就是创建一个傀儡进程,借助傀儡进程,执行自己的恶意代码。 其实现方法: 1.以挂起方式创建一个进程 2.写入一个PE文件到这个挂起的进程的内存,可能会使用API WriteProcessMemory或MapViewOfSection那一套API来完成这个操作 3.调用ResumeThread恢复进程执行 对付这种我们可以在它写入数据到目标进程时下断点,例如WriteProcess...

Sven的忘却日记 4294

PEDUMP文件分析器1.92版

不知道大家有没有用过PEDUMP,一个很好用的PE文件分析软件,不过那个软件是在DOS的命令行程序,并且是英文的,由于编EXE文件加密器的时候很想用一个这样的程序帮助分析,于是研究了PE文件的结构,做出了现在这个PE文件分析器,提供给那些想快些了解PE结构或者编制这方面的程序的朋友一个工具。 我正在加强其功能,主要是用在反编译方面太弱了,希望得到高手的帮助。 新版本的1.53版加强了文件导出功能,可以和M$的PUDUMP分析结果媲美:) 1.91版,最大的改动就是增加了资源分析显示部分,用法察看自带的ReadMe.txt,可以用来改资源了。 1.92版改进一点点小问题,增加毕业论文。

PE结构 --->8.PE对齐的概念 文件对齐VS磁盘对齐

PE文件对齐机制详解:磁盘文件对齐(0x200)和内存对齐(0x1000)是PE结构的关键概念。磁盘对齐基于512字节扇区大小优化存储效率,内存对齐则遵循4KB页管理单位确保安全访问。加载PE文件时,加载器会根据对齐规则进行内存映射和填充,可能导致"膨胀"现象。这种对齐机制直接影响PE分析技术:静态分析处理加密数据,而动态内存分析可获取解密后的数据,为反打包提供可能。理解对齐原理对PE文件分析和安全研究至关重要。

盾悟 9017

如何快速上手PE Tools:10个实用技巧解析PE文件结构

PE Tools是一款强大的可移植可执行文件操作工具包,专为Windows PE文件分析和逆向工程设计。作为一款历史悠久的专业工具,自2002年发布以来,它已成为安全研究人员和逆向工程师的必备工具。本文将为您介绍10个实用技巧,帮助您快速掌握PE Tools的核心功能,深入理解PE文件结构。 ## 🚀 快速入门:PE Tools安装与配置 要开始使用PE Tools,首先需要从官方仓库克隆项

gitblog_00838的博客 1102

PE Dump汇编程序

PE Dump 的实现是PE文件字码查看器,利用它可以查看和总计指定的PE文件的十六进制字节码,帮助我们更好地分析PE结构列一:是地址。地址的值是第(n*16+1)个字节在文件中的位置列二:是由空格分隔符分隔的16个字节的十六进制显示列三:这16个字节对应的ASCII码值。打开PE文件---》获取文件大小totalSize----》求循环显示16字节数据-----》显示剩余totalSize%16...

crkres9527 892

PEDUMP - Win32/Win64 EXE/OBJ/LIB/DBG file dumper - 2001 Matt Pietrek

PEDUMP - Win32/Win64 EXE/OBJ/LIB/DBG file dumper - 2001 Matt Pietrek 支持win64文件.

pedump.rar

exe文件为原版文件,压缩包为伪写的文件

etap.rar_ETAP_PE资源

分析pe文件的信息,节表section信息,各种DATA_DIRECTORY位置,信息和资源信息,functionimp

PE-bear:逆向工程师必备的PE文件静态分析利器

在软件安全、恶意软件分析和逆向工程领域,静态分析是理解程序结构和行为的基础。PE文件作为Windows平台可执行程序的标准格式,其结构解析是静态分析的核心环节。通过解析PE头、节区、导入/导出表等关键结构,分析者可以快速掌握程序的依赖关系、资源分布和潜在风险点,而无需立即深入复杂的汇编代码。这一技术对于漏洞挖掘、软件兼容性检查和安全评估具有重要价值。PE-bear正是这样一款专注于PE文件静态解析的工具,它集成了结构查看、实时反汇编和文件比对等特色功能,被誉为逆向工程师的“瑞士军刀”。其直观的界面和高效的交

weixin_34216107的博客 434

PE Tools:Windows可执行文件逆向分析的瑞士军刀工具详解

在Windows系统安全与软件分析领域,可执行文件PE格式)是理解程序运行机制的基础。PE文件结构定义了程序的代码、数据、资源组织方式,其头信息、节区划分、导入导出表构成了程序加载执行的蓝图。掌握PE文件分析技术,对于软件调试、安全研究、恶意软件分析具有重要价值,能够帮助工程师快速定位程序逻辑、识别依赖关系、分析潜在风险。PE Tools作为一款集成化分析工具,通过提供PE文件头解析、导入表分析、资源编辑、内存转储等核心功能,实现了对可执行文件结构的可视化探查与修改。该工具特别适用于逆向工程中的前期侦察、

weixin_34391445的博客 406

Self-Remapping-Code 内存布局前后对比:一张图看懂 IMG 与 MAP 页面的关键区别

Self-Remapping-Code 是一款面向 Windows 的 PE 映像自我重映射安全工具,它的核心能力是:程序启动后将自己"搬进"一个全新的内存位置,并让 **NtProtectVirtualMemory 再也无法修改映像内任何页面的保护属性**。本文将用内存布局的前后对比表,带你从零看懂 `VirtualQuery` 输出中 **IMG 与 MAP 两种页面的关键区别**,理解这项反

gitblog_00846的博客 397
上一篇: 莫名的伤感
下一篇: 拉电流(source current)与灌电流(sink current)
oathevil
博客等级 码龄17年 228粉丝 141原创
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值