pe头所在的易语言内存加载pe模块页可以执行吗

他的最新文章
他的热门文章
您举报文章:
举报原因:
原文地址:
原因补充:
(最多只允许输入30个字)把PE映像文件从内存中DUMP到磁盘 | 程式部落
我的图书馆
把PE映像文件从内存中DUMP到磁盘 | 程式部落
了解了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_ // “MZ”……+3Ch LONG e_ //指向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 S // PE文件标识+4h IMAGE_FILE_HEADER FileH // 映象文件头+18h IMAGE_OPTIONAL_HEADER32 OptionalH // 可选映象头} 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 PhysicalA DWORD VirtualS // 该块真实长度。是块对齐前的长度} M+0Ch DWORD VirtualA // 该块的RVA+10h DWORD SizeOfRawD // 在文件中对齐后的尺寸 +14h DWORD PointerToRawD // 在文件中偏移… …+28h DWORD C } 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 systemI // 系统信息结构GetSystemInfo(&systemInfo); // 获得系统信息结构return systemInfo.dwPageS // 返回内存页大小}2) GetModuleName(): 获得模块文件名char* GetModuleName(HANDLE handle, DWORD address){HMODULE hModule[1024]; DWORD cbNchar 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);}}}}}注意,这里用到了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 pD // MS-DOS头PIMAGE_NT_HEADERS pNt; // NT映象头PIMAGE_SECTION_HEADER pS // 区块头
unsigned char szHeader[8192], *szSDWORD dwR// 从指定的地址szAddress读进程内存内容到szHeaderif ( !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)szH // MS-DOS头的最后一个成员e_lfanew指向NT映象头pNt = (PIMAGE_NT_HEADERS)(szHeader+pDos-&e_lfanew);// 获得所需分配内存空间的大小*iLen = pNt-&OptionalHeader.SizeOfImage +pNt-&OptionalHeader.SizeOfHeaders + PageSize();// 分配虚拟内存空间, 注意后面的保护属性为PAGE_READWRITEif ( !(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.NumberOfS 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 *)dwR}
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 cbWprintf("开始写到文件…/n"); hFile = CreateFile(outfile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); // 创建文件,具有写属性if (hFile == INVALID_HANDLE_VALUE){printf("创建文件错误/n");}// 写文件操作WriteFile(hFile, pe, len, &cbWritten, NULL);
if ( len != cbWritten ){ // 校验写文件操作是否正确printf("写入文件时错误/n");}}到这,一个简单的从内存DUMP PE文件映像到磁盘的程序就完成了。编译后我选取了金山词霸的PID作为参数,得到的测试结果。和PeDump或PE Explorer对比发现,结果是一样的。呵呵。 是不是很有成就感呢。
小结:本文主要简单讨论了如何从内存中分析PE文件的相关技术, 实现了很简陋的功能, 但理解了思路,你完全可以加入很多诱人的功能。无论是病毒技术,破解技术还是溢出技术,熟练掌握PE文件格式的分析技术是很有用的,别以为网络上那些琳琅满目的PE分析工具有多么的神奇,只要你掌握了PE文件格式的基本架构和基本的C/C++编程功底,你也能打造自己的PE工具的。记住,伴随着黑防的努力,我们也在一步步成长!
馆藏&24359
TA的最新馆藏[转]&
喜欢该文的人也喜欢PE文件格式详解(2)
& 实模式残余程序   实模式残余程序是一个在装载时能够被MS-DOS运行的实际程序。对于一个MS-DOS的可执行映像文件,应用程序就是从这里执行的。对于Windows、OS/2、Windows NT这些操作系统来说,MS-DOS残余程序就代替了主程序的位置被放在这里。这种残余程序通常什么也不做,而只是输出一行文本,例如:“This program requires Microsoft Windows v3.1 or greater.”当然,用户可以在此放入任何的残余程序,这就意味着你可能经常看到像这样的东西:“You can't run a Windows NT application on OS/2, it's simply not possible.”  当为Windows 3.1构建一个应用程序的时候,链接器将向你的可执行文件中链接一个名为WINSTUB.EXE的默认残余程序。你可以用一个基于MS-DOS的有效程序取代WINSTUB,并且用STUB模块定义语句指示链接器,这样就能够取代链接器的默认行为。为Windows NT开发的应用程序可以通过使用-STUB:链接器选项来实现。 & PE文件头部与标志   PE文件头部是由MS-DOS头部的e_lfanew域定位的,这个域只是给出了文件的偏移量,所以要确定PE头部的实际内存映射地址,就需要添加文件的内存映射基地址。例如,以下的宏是包含在PEFILE.H源文件之中的:& PEFILE.H& #define NTSIGNATURE(a) ((LPVOID)((BYTE *)a + \&&&&&&&&&&&&&&&&&&&&&& ((PIMAGE_DOS_HEADER)a)-&e_lfanew))  在处理PE文件信息的时候,我发现文件之中有些位置需要经常查阅。既然这些位置仅仅是对文件的偏移量,那么用宏来实现这些定位就比较容易,因为它们较之函数有更好的表现。  请注意这个宏所获得的是PE文件标志,而并非PE文件头部的偏移量。那是由于自Windows与OS/2的可执行文件开始,.EXE文件都被赋予了目标操作系统的标志。对于Windows NT的PE文件格式而言,这一标志在PE文件头部结构之前。在Windows和OS/2的某些版本中,这一标志是文件头的第一个字。同样,对于PE文件格式,Windows NT使用了一个DWORD值。  以上的宏返回了文件标志的偏移量,而不管它是哪种类型的可执行文件。所以,文件头部是在DWORD标志之后,还是在WORD标志处,是由这个标志是否Windows NT文件标志所决定的。要解决这个问题,我编写了ImageFileType函数(如下),它返回了映像文件的类型:& PEFILE.C& DWORD WINAPI ImageFileType (LPVOID lpFile)& {& /* 首先出现的是DOS文件标志 */& if (*(USHORT *)lpFile == IMAGE_DOS_SIGNATURE)& {&&& /* 由DOS头部决定PE文件头部的位置 */
键盘也能翻页,试试“← →”键
相关软件:
大小:1.93 MB
授权:免费
大小:62.51 MB
授权:免费本帖子已过去太久远了,不再提供回复功能。君,已阅读到文档的结尾了呢~~
扫扫二维码,随身浏览文档
手机或平板扫扫即可继续访问
PE文件头解析大全.doc
举报该文档为侵权文档。
举报该文档含有违规或不良信息。
反馈该文档无法正常浏览。
举报该文档为重复文档。
推荐理由:
将文档分享至:
分享完整地址
文档地址:
粘贴到BBS或博客
flash地址:
支持嵌入FLASH地址的网站使用
html代码:
&embed src='http://www.docin.com/DocinViewer-4.swf' width='100%' height='600' type=application/x-shockwave-flash ALLOWFULLSCREEN='true' ALLOWSCRIPTACCESS='always'&&/embed&
450px*300px480px*400px650px*490px
支持嵌入HTML代码的网站使用
您的内容已经提交成功
您所提交的内容需要审核后才能发布,请您等待!
3秒自动关闭窗口

我要回帖

更多关于 易语言内存加载pe模块 的文章

 

随机推荐