下面小编给大家整理了自己写的文件分析器PE DeCODER v1.0,本文共6篇,供大家阅读参考。本文原稿由网友“一棵小树”提供。
篇1:自己写的文件分析器PE DeCODER v1.0
在《软件加密技术》这本书里看过PE文件各部分的详细解释之后,我也有了一个自己写PE文件分析器的的想法,虽然好的分析器不在少数,但对于一堆十六进制数,有些朋友可能不明白它代表什么意思。如果在程序里就可以将这些01序列转换成可以直接看懂得信息,那至少用户可以省去以后去查表的麻烦。怀着这样的想法,我仔细的研究了书中分析器PEInfo的源代码,我发现它没有提供信息转换的功能。
通过研究发现,PEInfo是通过PE文件在内存中的映象来获取文件信息的,我在想是否还有别的方法可以绕过将文件映象到内存这一步,直接读取文件信息。这样的方法只有直接读取磁盘上的PE文件,在磁盘上寻找所需要的文件信息。
在这里暂且不说这样的做法和内存映象法有什么优劣,我在此仅仅只是想找寻另一条解决问题的道路,并实现之。看完我的分析和源程序,大家自然知道孰优孰劣。
为了避免引起混淆,程序中采用了与PE标准种类似变量名来定义关键的数据,如文件头,可选文件头,节表,导入表和导出表,具体名称定义细节可以在winnt.h里查到。整个程序是以面向过程的方式写的,适当结合了面向对象的特征。我将读取的PE文件信息封装在一个对象DataDump里,这样是为了方便数据的管理和最后输出分析报告。而对文件的分析则分别有一系列的子程序来完成。现将子程序说明如下:
//-------------------------------------------------------------------------------------------------------------------
BOOL Is_EXE_file( ifstream& PE_file ) //判断是否是合法的PE文件,是则返回true,否则返回false
BOOL OutReady( CHAR filename[], ofstream& fout ) //输出准备,包括输出流和输出文件,是则返回true,否则返回false
VOID WriterInfo( ofstream& fout ) //输出程序版本信息
BOOL Load_EXE_Info( ifstream& PE_file ) //读取PE文件信息,成功返回true,否则返回false
VOID Decode_EXE_Info(CHAR filename[], BOOL IsEXE, ifstream& PE_file, ofstream& fout) //分析PE文件信息
VOID ToNumeric( LPDWORD ptr, CHAR buf[], INT start, INT size ) //将字符数组从start位开始,转换size位为数值,放入ptr指向的DWORD类型变量中
VOID ToString( LPSTR ptr, CHAR buf[], INT start, INT size) //从字符数组从start位开始,取出其后的size位,放入一个ptr指向的的字符数组中
//-------------------------------------------------------------------------------------------------------------------
class DataDump
{
private :
IMAGE_FILE_HEADER FILE_HEADER; // IMAGE_FILE_HEADER
IMAGE_OPTIONAL_HEADER32 OPTIONAL_HEADER32; // IMAGE_OPTIONAL_HEADER32
PIMAGE_SECTION_HEADER SECTION_HEADER; // PIMAGE_SECTION_HEADER
IMAGE_IMPORT_DESCRIPTOR IMPORT_DESCRIPTOR; // IMAGE_IMPORT_DESCRIPTOR
PIMAGE_EXPORT_DIRECTORY EXPORT_DIRECTORY; // PIMAGE_EXPORT_DIRECTORY
DWORD ExVRk, ImVRk; // 输出表和输入表在磁盘文件的偏移和RVA的差值
public : // You can get the functions of these member functions below by their names.
DataDump;
~DataDump();
BOOL Set_FILE_HEADER( CHAR [], INT );
BOOL Set_OPTIONAL_HEADER32( CHAR [], INT );
BOOL Set_SECTION_HEADER32( CHAR [], INT );
BOOL Set_EXPORT_TABLE( CHAR [], INT );
VOID GetReady( CHAR [] );
DWORD Get_OPTIONAL_HEADER_SIZE( VOID ) const;
DWORD Get_SECTION_NUMBER( VOID ) const;
DWORD Get_EXPORT_TABLE_RAW( VOID ) const;
DWORD Get_IMPORT_TABLE_RAW( VOID ) const;
VOID Set_Export_VRk( VOID );
VOID Set_Import_VRk( VOID );
BOOL Export_Table_Existed( VOID ) const;
BOOL Import_Table_Existed( VOID ) const;
BOOL Show_FILE_HEADER( ofstream& ) const;
BOOL Show_OPTIONAL_HEADER32( ofstream& ) const;
BOOL Show_SECTION_HEADER32( ofstream& ) const;
BOOL Show_EXPORT_TABLE( ifstream&, ofstream& ) const;
BOOL Show_IMPORT_TABLE( ifstream&, ofstream& ) const;
};
DataDump pool;
//-------------------------------------------------------------------------------------------------------------------
DataDump类的实例是全局对象,这样做是方便子程序对该对象的访问,
程序的基本思路是,在磁盘上打开PE文件,判断其是否为合法的PE文件,否则输出错误信息,退出;是则进行分析,包括读取文件头,可选文件头,节表,导入表和导出表,将信息储存在DataDump类中,最后以txt文件的形式输出一份文件的分析报告。
程序的关键在于文件信息的“定位读取”上。文件头,可选文件头和节表在磁盘上是顺序存放的,跳过开始的PE标志段,就可以轻松找到上述几段,而且每一部分的确切大小都在它们的相关属性里描述了,在程序运行时可以知道的,读取信息的工作很容易就可以完成。而输入表和输出表的大小是不确定的,有的时候会存在没有输入表或没有输出表的情况,再加上输入输出表的出现位置也不固定,这会给读取输入输出表的工作带来一些困难。
我们知道,一般在PE文件里某一项给的都是相对虚拟地址RVA,并不能直接和磁盘文件的物理地址相对应。在以内存映象为基础的方法中,只需要取得RVA,和ImageBase作简单的运算以后就可以定位到某一项数据在内存中的保存地址。而在以直接读取磁盘文件的方法里,必然要涉及到RVA到真实物理地址RAW的转换。所以对输入输出表的读取的关键转换到对输入输出表在磁盘文件上的定位了。
以下是一个通用的转换方法及示例:
+---------+---------+---------+---------+---------+---------+
| 段名称 虚拟地址 虚拟大小 物理地址 物理大小 标志 |
+---------+---------+---------+---------+---------+---------+
| Name VOffset VSize ROffset RSize Flags |
+---------+---------+---------+---------+---------+---------+
| .text 00001000 00000092 00000400 00000200 60000020|
| .rdata 0000 000000F6 00000600 00000200 40000040|
| .data 00003000 0000018E 00000800 00000200 C0000040|
| .rsrc 00004000 000003A0 00000A00 00000400 C0000040|
+---------+---------+---------+---------+---------+---------+
文件虚拟偏移地址和文件物理偏移地址的转换公式如下:
FileOffset = VA - ImageBase - VRk (VRk是文件虚拟地址和文件物理址之间的差值)
= RVA - VRk
>>>>>>>VaToFileOffset(虚拟地址转文件偏移地址)
如VA = 00401000 (虚拟地址)
ImageBase = 00400000 (基地址)
VRk = VOffset - ROffset = 00001000 - 00000400 = C00 (得出文件虚拟地址和文件物理址之间的VRk值)
FileOffset = VA - ImageBase - VRk = 00401000 - 00400000 - C00 = 400(文件物理地址的偏移地址)
这样看来,关键就在于如何求这个VRk上。其实很简单,就用节表数据项里面的VirtualAddress减去PointerToRawData,就可以得到。VirtualAddress从字面上看,似乎是虚拟地址,但其实它也是个RVA,是相对于内存映象后的首地址的偏移,而PointerToRawData,严格的说,是相对与磁盘文件开始处的物理偏移,那对输出表的VRk来说,计算公式应该是这样:
ExVRk = SECTION_HEADER[i].VirtualAddress - SECTION_HEADER[i].PointerToRawData;
而此时,内存和磁盘文件有相同的基址,即ImBase = RawBase。输入表的VRk也是相同的计算方法。
ImVRk = SECTION_HEADER[i].VirtualAddress - SECTION_HEADER[i].PointerToRawData;
我们首先要找到输入输出表所处的区段。虽然一般以.idata和.edata命名的就是输入输出表数据区段,但一旦更改了区段名称,就无从查起了。在这应该把IMAGE_OPTIONAL_HEADER32中DataDirectory数组里输入表和输出表的VirtualAddress定位到节表中VirtualAddress划分出来的区间里,就可以找到输入输出表所处的区段。代码如下:
for ( INT i=1; i { if ( SECTION_HEADER[i].VirtualAddress>OPTIONAL_HEADER32.DataDirectory[0].VirtualAddress ) { ExVRk = SECTION_HEADER[i-1].VirtualAddress - SECTION_HEADER[i-1].PointerToRawData; break; } } 接下来对于和输入表相关的数据,只需要用对应项减去ImVRk就是这一项在磁盘文件里的偏移。如输入表的OriginalFirstThunk的RVA是00318140,只要用这个值减去ImVRk,就可以得到OriginalFirstThunk在磁盘文件的偏移。So is Export Table! 其他就只剩下怎么处理读取的数据了。我用的是C++的文件输入流fstream,以二进制的形式读进一批数据,通常都是以相应块的大小读入数据,如以sizeof(IMAGE_FILE_HEADER),然后通过 ToNumeric( LPDWORD ptr, CHAR buf[], INT start, INT size )函数将字符形式的变量转换为数值型,有时有需要一些字符型的数据,如函数名,就要用ToString( LPSTR ptr, CHAR buf[], INT start, INT size)取出特定的某几位字符,这些在源代码里都可以看到。我在写的过程中,发现C++的输入流不是太稳定,有的时候会读不进数据。我在每一个涉及到读入数据的地方都加了输入流的clear()函数,它重置了流的状态,让流始终处于稳定的状态下。输出分析报告到txt文件,我用的是C++的输出流,为了保证输出的稳定性,我也调用了输出流的clear()函数。最后的报告会保存在和用户输入的可执行文件同名的文本文件里。 最后有一点申明,这个程序是在Visual C++ 6.0环境下编译的,在其他的C++环境下好像不能编译通过,因为winnt.h的版本问题,不同编译环境,所带的winnt.h内容不尽相同,在这些环境下编译会出错。而且这个程序可以在32位和64位环境下运行,但还不能分析64位的应用程序。可能是64位的PE32+格式和32位的PE格式不同引起的,因为我用PEid0.94和stud_PE也不能分析64位应用程序。 读到这里,如果你看完源代码,应该可以得到自己的结论了。哪种方法更好,一目了然的,但仔细斟酌,每种方法都有他自己的优点和弊病。但这不是我所关心的事情,关键是我在这过程中,更加深入理解了PE的结构,锻炼了自己的编程能力。欢迎大家发表意见,关于程序的,关于PE的,or something else。程序写得仓促,在代码可读性上敬请原谅。有什么好的建议,欢迎大家和我联系。 E-mail : fahrenheit871116@163.com 写完之后,就像高考结束在等待成绩到来的那一段时间,放松,悠闲,别人怎么评价已不重要,尽力就好!^_^
篇2:IIS日志文件分析器ReadLogs探秘(五)
意外监控器所使用的CDB命令
(作者:青苹果工作室编译
意外监控器所使用的一些命令在调试的时候是有用的,下面是这些命令及其相关的输出。
Kv 命令
意外监控器运行的第一个命令是Kv 。它显示的是在挑错时刻激活的流的函数调用返回堆栈。读这个堆栈是从底部向顶部:最顶部的函数是调用的最后一个函数,而最底部的函数则是这个流调用的第一个函数。
输出样本(x86平台):
ChildEBP RetAddr Args to Child
00f8fdf8 53d0f7f3 003070ac 00307078 00306d48 0x1000113c [Stdcall: 0]
00f8fe5c 53d0ad45 00306ee4 00f8ff0c 00f8ff4c w3svc!AddFilterResponseHeaders+0x26
00f8ff20 53d01ce6 00f8ff4c 00f8ff68 003154b0 w3svc!HTTP_REQUEST__DoDirList+0x2f7
00f8ff50 53d01f56 00f8ff68 00315478 00315460 w3svc!&127;ole32_NULL_THUNK_DATA+0x96e
00f8ff6c 53d04adb 00000166 00000000 00000001 w3svc!&127;ole32_NULL_THUNK_DATA+0xbde
00f8ff88 539895d0 00315460 00000166 00000000 w3svc!HTTP_REQ_BASE__`vftable+0x1fd3
00f8ffb8 77f04f2c abcdef01 776a2254 00000028 infocomm!AtqPoolThread+0x1c8
00f8ffec 00000000 53989408 abcdef01 00000000 kernel32!BaseThreadStart+0x51
输出样本(alpha平台):
Callee-SP Return-RA Call Site
0815ffc0 77e6f5b8 : ntdll!DbgBreakPoint+0x4
0815ffc0 77e6cc48 : KERNEL32!DebugBreak+0x8
0815ffd0 00000000 : KERNEL32!BaseThreadStart+0x60
函数的调用返回堆栈列出了在给定流中激活的所有函数。意思是函数1调用了函数2,函数2最终又调用了函数3,等等。当最后一个被调用的函数结束了其工作时,它就“展开”,或者说是返回了调用它的那个函数;而那个函数结束后也返回调用它的函数,依次类推。调用返回堆栈列出了什么调用了什么,以及函数正确运行后代码如何返回。因为这个堆栈是故障堆栈,你就可以知道在堆栈顶部列出的函数所发生的访问冲突。但是这不一定说明这个函数引发了实际问题。问题也许是由于另一个函数向堆栈传递了错误的指针,或者甚至于是由一个不同的流引起的。在堆和堆栈冲突中可以看到有关详细内容。
请注意当确认文件没有装载符号,堆栈顶部的命令并不列出调用命令。当堆栈装载到ReadLogs时,通过使用装载的模块的列表,它试图将这个函数调用分解到一个友好的名字中。首先它获得进行调用的内存地址(0x1000113c), 然后寻找拥有这个内存地址的装载的DLL。
!inetdbg.mod
IIS调试器增加到CDB的一个扩展是Inetdbg.dll文件。如果这个文件存在于Windows NT 目录中(或本地调试目录中),这样CDB就可以使用这个命令了。如果在这个扩展命令之前增加一个! 符号,那么CDB就寻找这个DLL,然后将这个命令传递到扩展。这时,你调用Inetdbg 扩展,并在mod命令中传递。这个命令在Inetinfo 内存空间中堆积了所有当前装载的模块的列表,并返回下面的信息(这是一个省略的列表):
输出样本:
Start End Entry Path
01000000 01006000 01001122 C:WINNTSystem32inetsrvinetinfo.exe
77f60000 77fbc000 00000000 C:WINNTSystem32tdll.dll
78000000 78047000 7800546d C:WINNTsystem32MSVCRT.dll
77f00000 77f5e000 77f01000 C:WINNTsystem32KERNEL32.dll
77dc0000 77dfe000 77dc1000 C:WINNTsystem32ADVAPI32.dll
77e70000 77ec1000 77e7a2a2 C:WINNTsystem32USER32.dll
77ed0000 77efc000 00000000 C:WINNTsystem32GDI32.dll
10000000 10024000 10001650 C:InetPubscriptsselfdestruct.dll
如果想要确定是什么代码正在Inetinfo 内存空间运行,这些信息是有用的。在前面的 kv 命令中,你的函数是未分解的。要分解它,你要使用内存地址-―0x1000113c,看它是否在Selfdestruct.dll的起始地址和结束地址范围内。
!inetdbg.ver
Inetdbg 可以使用的另一个扩展是ver 命令。这个命令列出了从装载的DLL中提取出来的所有版本信息。大多数DLL的版本信息都是在实际文件的头部。这个扩展就试图从这里获取这些信息:
输出样本:
Module @ 0x01000000 = inetinfo.exe
dwFileFlags = 0x00000000 (FREE)
CompanyName = Microsoft Corporation
FileDescription = Internet Information Services Application v 1.0
FileVersion = 4.00
InternalName = INETINFO.EXE
LegalCopyright = Copyright (C) Microsoft Corp. 1981-
riginalFilename = INETINFO.EXE
ProductName = Microsoft(R) Windows NT(TM)
Operating System ProductVersion = 4.00
Module @ 0x77f60000 = ntdll.dll
dwFileFlags = 0x00000000 (FREE)
CompanyName = Microsoft Corporation
FileDescription = NT Layer DLL
FileVersion = 4.00
InternalName = ntdll.dll
LegalCopyright = Copyright (C) Microsoft Corp. 1981-1996
riginalFilename = ntdll.dll
ProductName = Microsoft(R) Windows NT(TM)
Operating System ProductVersion = 4.00
在这个例子中,没有列出存在问题的DLL(Selfdestruct.dll),
Inetdbg.mod 不能显示那些不能恢复版本信息的DLL的任何信息。ReadLogs 将 ver 命令中每个条目的文件名与这里的条目相比较,然后将可以得到的版本信息增加到列表中所有的DLL上。如果没有可用的版本信息,ReadLogs 就仅仅增加这样一句话“没有这个DLL的可用的版本信息”。ReadLogs 就是用这个命令的输出来显示版本信息。如果想要确定你是否在运行一个给定DLL的适当版本,这个信息是非常有用的。
注意:在某些运行Windows NT Service Pace (SP) 4的机器上, ver 命令运行是完全失败的。如果希望获取适当的版本信息,可以将这个命令运行两次。这时ReadLogs 就会特意寻找一个没有包含任何错误的输出的版本,并用这个设置来显示版本信息。如果这两次运行都失败了,ReadLogs 就不能显示版本信息了。
~*kb
~*kb命令指示调试器为内存空间上的每个流保存一套堆栈的副本(同kv)。(~ 命令说“选择一个流”; * 命令说 “所有的流”,然后你可以加上你想要对所有流运行的任何命令,这里是 kb)。你可以用这个命令来看看在发生冲突的时候发生了一些别的什么。如果你在排除100%CPU利用率和死锁的故障,也可以使用这个信息。每个堆栈都同kv 堆栈的分析方法相同。
运行调试器,你可以用这个信息排除100%CPU利用率的故障,在你捕捉到流计数器的地方还可以运行一个性能监控器日志。对于一个100%CPU利用率的问题,你可以手动给调试器挑错,然后停止PerfMon 日志。通过分析PerfMon 日志,你就获取了正在使用CPU的流的号码。你还可以在~*kb 命令所列出的有同样号码的流上使用ReadLogs (或手动检查调试日志文件)。这样就告诉你流在做什么并指出“罪犯”是谁。
注意:要捕捉流的输出,你必须使用PerfMon 的日志功能,要等到100%利用率发生才能起动日志。仅仅在图表中看看流会得到错误的流号码。
u eip-50 eip+20 (只在x86平台)
这个u命令指示调试器显示存在于提供的地址范围内的汇编代码。这时你是在请求从扩展指令指针(EIP)寄存器所指向的50个字节之前到EIP指向的20个字节之后的那些代码。EIP寄存器通常指向要执行的代码的下一行。你可以看到在冲突发生时代码在做些什么。
输出样本(缩略):
ntdll!RtlpWaitForCriticalSection+0x45:
77f6cc16 0000 add [eax],al
77f6cc18 008b0d805afa add [ebx+0xfa5a800d],cl
77f6cc1e 7739 ja ntdll!RtlpWaitForCriticalSection+0x88 (77f6cc59)
77f6cc20 48 dec eax
77f6cc21 2475 and al,0x75
77f6cc23 2333 and esi,[ebx]
77f6cc25 c0894604894608 ror byte ptr [ecx+0x46890446],0x8
77f6cc2c 89460c mov [esi+0xc],eax
77f6cc2f 894610 mov [esi+0x10],eax
!locks
这个!locks 命令显示的信息是关于当前运行代码的哪个部分被标记为关键部分(CS)。有关CS的详细讨论可以看理解锁那部分。
输出样本:
CritSec w3svc!MDIDMappingTable+100 at 68c2b1b0
LockCount 0
RecursionCount 1
OwningThread 156
EntryCount 0
ContentionCount 0
*** Locked
!inetdbg.atq Cg
这个atq 命令显示的信息与IIS非同步流排队有关。流排队是IIS特有的功能。当一个新的请求进入IIS时,它就需要处理一个流。由于创建一个流从时间和CPU使用的方面来说是比较昂贵的,因此IIS保持着一套随时处理引入的请求的工作流;IIS则管
篇3:IIS日志文件分析器ReadLogs探秘(三)
理解Windows NT 和Windows NT调试器
(作者:青苹果工作室编译 01月12日 15:21)
意外监控器的基础是称为Cdb.exe的Windows NT调试器,作为一个用户级的调试器,意外监控器可以调试用户模式程序而不是核心模式程序。CDB是通过查找某种访问冲突工作的。当发生冲突时,Windows NT就生成一个标记,这个标记激活调试器并暂停调试器所隶属的程序。因为程序暂停了,系统为程序保留全部的内存空间,保留的状态与发生冲突时的状态一样。这个功能允许我们运行来自CDB的命令,这些命令将来自内存的某些信息堆积到日志中。
图6:CDB程序
程序、流及其它内在材料
因为这个文档包含了许多关于程序、流以及堆栈的参考内容,因此在文中包括这些内容的一些入门知识是适当的。另外,有关这些内容的详细信息可以在以下出版物中找到:
●Window 应用程序编程, 第四版,作者是Jeffrey Richter (Microsoft 出版社)。
●Windows NT 内幕, 第二版,作者是David Solomon (Microsoft 出版社)。
一个程序通常就解释为“一个运行程序的一个例示”。一个流则是“一个程序执行中的一条路”。这些简单的术语建立起了Windows NT 如何操作的概念。一个程序起动,创建在任务管理器中列出的程序。在这个程序内部,有一个或多个流,它们执行产生应用程序的所有代码。完成工作、访问内存、产生程序的输出,这些都是由流完成的。Windows NT 是一个多任务、多流的操作系统。因此,它可以在同一时间运行多个应用程序,而每个应用程序都可以在同一时间处理多个任务。Windows NT是通过在所有的活动流中分割CPU时间并且给每个流一个“时间片段”来完成以上任务的。考虑一下这种情况,你就会发现在任意给定时间,在任意给定处理器上只有一个流在运行。时间片段非常短,因此这些流好象是在同步运行,而实际上并不是这样。这就使调试变得简单,因为如果发生了一个问题,那就是当前正在运行的流产生的。进一步地研究就会发现,这并不能担保这个有过失的流就是罪魁祸首。它也许是另一个流的牺牲品(请看堆和堆栈故障部分)。
这个文档中并没有提到Windows NT 是如何管理流的执行的(有关这个话题的讨论可以看上面列出的书目)。但是流可以由于某些原因而自愿放弃一些属于它们的时间,包括它们也许正在等待来自另一个流或用户的一些信息。在这些情况下,你就能看到对堆栈的调用,如:
●WaitForSingleObject
●WaitForMultipleObject
●WaitForCriticalSection
●Sleep
●SleepEx
所有这些调用只不过意味着进行这些调用的流想要走开一下,在它继续工作之前要等待一些时间的发生。当可以继续处理的时候,Windows NT 会将这些流唤醒。
内存
这部分描述在程序中内存的基本用法。记住在Windows NT 中,每个程序都有4GB的虚拟可寻址内存,而其中有2GB是系统内存。系统内存是共享的(意思是只有一套,由所有的程序所共用)。下表概括了在Windows NT 4.0中,内存空间是如何为所有用户模式程序所映射的。
注意:这种映射不一定适用于Windows NT 企业版。Windows NT 企业版可以有3GB的用户内存和1GB的系统内存。
范围(十六进制)空间大小功能0 to FFFF64 kilobytes (KB)禁止访问区10000 to 7FFEFFFF2 GB 192 KB私用程序地址空间7FFDE000 to 7FFFFFFF136 KB为不同Windows NT 函数预留80000000 to FFFFFFFF2 GB系统内存
虚拟可寻址内存
正如前面所提到的,Windows NT 给每个程序4GB的内存,
但是对于那些只有64MB的实际内存(甚至于更少)而运行Windows NT 的系统来说,这怎么可能呢?是这样的,程序有4GB的虚拟地址内存,不一定使用全部的4GB。要理解Windows NT 是如何做到的,首先看看基本的编程概念。
在早期的DOS程序中,程序在其中运行的内存空间数量是一个令人担心的问题。而且,工业上为不同型号的机器建立了不同版本的程序。其它问题包括程序是否使用了扩展的或扩充的内存以及额外内存是否足够。通过允许Windows NT 将虚拟地址空间设置成4GB,现在所有的程序都在平面内存模式下运行。现在你不必再为如何编译程序而担心。只需要让Windows NT 将虚拟地址的调用转换成物理地址即可。
当你调试一个用户模式应用程序时,你不必介意转换中发生了什么,因为调试器所显示的所有内存就象程序所看到的一样--在虚拟内存地址空间。记住了这一条,我们就来看看Windows NT 内存布局的一个简单例子。
内存空间布局
当Windows NT 上的一个程序起动时,系统就建立起内存空间,然后在这个空间中编排内存的位置。Windows NT 编排内存的方式要依应用程序而定,但是对于所有的程序而言也有统一的规则。在一个程序的内存中有4部分:
图7:内存空间布局
●内存数据部分(绿色)包含应用程序所使用的数据和变量。每当程序执行一个命令malloc那样的命令,Windows NT 就在堆内部给这个应用程序一定的空间,允许应用程序按照自己的需要来使用这些空间。然后应用程序读内存、向内存写入或在结束时将其返回堆(也就是所谓的释放内存)。在虚拟内存和堆部分有更详细的讨论。
●内存的代码部分(兰色)包含了应用程序的实际命令和指令。在程序装载时,Windows NT 分配这部分内存。为了保护程序的完整性,Windows NT 把这部分内存标为只读/执行。在理解DLL部分有关于代码装载及其对调试的影响的讨论。
●内存的堆栈部分(黄色)包含着程序执行的“路标”。程序是由组件(或模块)组成的,而这些模块是由函数组成的。函数可以接收输入(称为自变量或参数),也可以返回结果(称为返回值)。为了让程序跟踪它到了哪里(哪个函数调用了什么)和传递过来了哪些值,程序使用一个后进先出列表(LIFO),这就称为堆栈。在解剖堆栈部分有关于堆栈以及如何阅读堆栈的讨论。
●内存的未用部分(灰色)不属于前面三种的范畴。因为Windows NT 在使用内存方面是动态的,因此随着程序使用和释放内存,这部分内存增长或收缩。
虚拟内存和堆
Windows NT允许程序使用和存储内存,它使用两种方法--堆 和虚拟分配内存(第三种方法内存映射文件不在本文讨论范围之内)。堆 指Windows NT 为一个应用程序预留并允许你使用的内存。堆通常适用于大量的小片数据的情况,而虚拟分配内存则是一个程序专门为它自己保留的内存。通常它用来保存大块的数据。这两种内存分配的其它区别在于:
●虚拟分配内存可以在程序之间共享,允许内存作为共享数据的一种方法。而堆 内存则不能共享。
●性能监控器中的私用字节计数器不显示共享的内存,这不一定是当前使用的内存的实际大小。
●Windows NT生成一个1MB的默认堆,当它需要较大的空间时,Windows NT 自动调节堆的大小。而增加虚拟分配内存则是由应用程序负责的。
要查看程序内存的布局,可以使用Windows NT 资源工具箱中的PViewer.exe来获取详细的分析。
理解DLL
DLL为Windows 的功能和速度提供了核心的解释。使用DLL的概念非常简单。一个库是一个程序可能要用到的一套程序,库中通常包含着许多程序都会用到的公共程序。建立一个库并将它包含在程序中,这样就不用每次用到时都重新写这些程序。要使用库中的程序,就必须将它连接到程序,在开发阶段这是经常发生的。这个方法有一个缺点就是每个程序都需要这个库的一个副本,这是用来快速地定制内存和磁盘空间的。Windows 解决这个问题的方法是允许在运行时间(应用程序执行的时候)将库连接到应用程序。通过允许多个不同的应用程序共享同一个DLL,Windows 就获取了更高的效率。即使有5个应用程序可能要用到这个DLL,也只需要在内存中装载一个副本。
篇4:经费请示文件怎么写
Y市财政局领导:
为应对金融危机,按照济政办字[]111号文件,市工业经济运行指挥部从11月22日正式运作至今,在运行监控、项目推进、市场开拓、资金等要素保障和政策研究等方面做了大量工作,对保障工业经济平稳运行发挥了重要作用,得到了孙书记、张市长的充分肯定。月20日,市经贸委以济经贸字[2015]189号文件向市财政局呈报了关于申请办公经费的报告,市财政局大力支持,两次共拨付经费12万元,及时保障了指挥部的正常运转。
从目前情况看,金融危机的影响仍在继续,指挥部工作还远未结束,前两个月运行费用支出已出现极大缺口,部分办用设备和家俱需配备,房租等运行费用需支付,现再次申请解决办公经费57.78万元。
市工业经济运行指挥部
xx年xx月xx日
篇5:自己动手修复ZIP文件
使用ZIP压缩文件时经常会遇到文件损坏的情况,如果是辛辛苦苦刚从网上下载回来的,那更是令人万分 失望,难道我们的银子就这样白花了吗?不要紧,先不用急着重新下载,您不妨试一试下面这些方法。
ZIP自动手
常见的错误是WinZip在解压时提示说某个文件CRC错误。您先别慌,如果是在解压到95%或是更多时提示错 误的,基本是可是使用的。方法如下:在出现WinZip的窗口后,选择经典模式,然后用Ctrl和鼠标键选取除刚 才出错外的所有文件(如图1),并将选择的这些文件解压到一个指定的目录中。
图1 选取出错文件外的所有文件
接下去就是要处理那个出错文件了。选择它并进行解压,当解到98%时错误信息再次出现,并询问是否要 看日志文件,先不理它,用Windows的查找功能在系统临时文件夹中找到了这个出错文件,选择后复制到刚才 的文件夹中,最后关闭WinZip。怎么样,是不是成功了呢?
如果压缩文件是ZIP的自解压文件,也发生同样的CRC错误,可以在WinZip打开后按上述方法进行解压,有 时也有意想不到的收获。
软件巧修复
如果刚才的招不灵,说明文件损坏较为严重了。那您也别急,有些软件专门帮您解决这个问题。
1、ZIP修复大师
该软件是专门用于修复受损的ZIP文件的工具软件,在修复过程中它将对受损文件进行多重扫描和分析, 修复受损部分,从而最大限度地恢复ZIP文件的受损数据。它的主要特点:支持对各种类型的ZIP文件和自解压 文件的修复;与资源管理器集成,只需单击鼠标右键即可轻松完成修复工作;支持拖放操作等等,
另外该软件的使用非常简单,在软件运行主界面上输入要修复的文件名和修复后的文件名,单击“开始修 复”按钮即可开始修复,修复完毕后你可以查看修复记录(如图2)。
图2 ZIP修复大师自有绝活儿
2、自解压压缩包修复器
该软件是一款专门用来修复自解压文件的工具软件,它是一个汉化版的绿色小软件,使用非常简单,只需 单击“请按这里选择要恢复的压缩文件”按钮,然后在弹出的对话框中选择要进行修复的文件,它就会自动进 行修复,为了预防万一,在修复之前它会首先备份该软件为BAK文件,修复成功之后自动覆盖掉源文件,是不 是很方便?
蚂蚁来帮忙
如果都修复不了,那么我可能不得不遗憾地告诉您,您要重新下载了。不过,重新下载也是有窍门的哦。 如果使用网络蚂蚁下载的话,可以使用它自带的“修复损坏的ZIP文件”功能来试一下。
图3 网络蚂蚁也能修复ZIP文件
首先要确认下载任务仍然留在任务列表中,并且下载文件保存在下载时设定的文件夹中,没有转移到别的 文件夹中,然后选中已完成的下载任务,单击鼠标右键,在弹出快捷菜单中选择“修复损坏的ZIP文件”命令 (如图3),接下来网络蚂蚁就会对这个ZIP文件进行CRC校验并且标识出损坏位置,检查完毕后,重新启动该 任务,让网络蚂蚁重新下载损坏区域即可。怎么样,这比您重新下载整个文件要强得多吧。
篇6:保存文件用英语怎么写
例句:
在您完成操作之后保存文件。
Save the document when you are finished.
保存文件并生成你的模型。
Save the file and generate your model.
保存文件并关闭编辑器。
Save the file and close the editor.