下面有几个表网上资料比较少,因为几乎用不到,我查文档写写吧,这篇写得比较久很抱歉。


7、IMAGE_DIRECTORY_ENTRY_EXCEPTION【异常处理表】

CPU特定的并且基于表的异常处理。用于除x86之外的其它CPU上。偏移到一个IMAGE_XXX_RUNTIME_FUNCTION_ENTRY数组,根据文件头的Machine来确定结构,64位用IMAGE_IA64_RUNTIME_FUNCTION_ENTRY,详细介绍在pecoff文档5.5节。但是只写定义不写用来干嘛的,这个以后再补充了。

8、IMAGE_DIRECTORY_ENTRY_SECURITY【证书属性表 - WIN_CERTIFICATE】

这是一个数组结构,根据前一个结构定义的大小可以偏移到下一个位置。其结构定义在WinTrust.h里,WIN_CERTIFICATE结构记录了证书长度、版本、类型、二进制内容。,版本是由WIN_CERT_REVISION_XXX定义,类型是WIN_CERT_TYPE_XXX定义。因为证书不会被映射到内存里,所以这里的VirtualAddress存的是文件偏移。另外dwLength如果与可选头中记录的大小总和不同的话,说明证书损坏。

其介绍在pecoff文档60页和5.7节,一般使用win api可以解决大部分事情,现在暂时只能用来解析,读的方法没什么特别的,就不写代码了。

9、IMAGE_DIRECTORY_ENTRY_BASERELOC【重定位表 - IMAGE_BASE_RELOCATION】

当pe加载到内存时,如果可选头记录的ImageBase地址被占用后会换一个位置映射,因为代码里有时候会有调用某地址或者jmp到某些确定地址等等情况,这时候这些地址在编译时用的是硬编码;如果程序不能映射到基址,这个值由于是固定的关系所以不能定位到原来的目标。因为程序可能会在任何位置调用这些值,所以需要一个表记录程序在哪里调用了这些东西。所以就有了重定位表,表里记录的就是偏移到那些位置的rva。

IMAGE_BASE_RELOCATION结构记录了分块的rva和整个分块的大小,紧跟着这个结构的是记录了偏移位置的一个WORD类型数组。那个分块就是按页大小(0x1000)来分的,有时候这个块里没有要重定位的东西就会省掉,IMAGE_BASE_RELOCATION中的VirtualAddress是每个分块的起始rva,计算后面的数组长度使用:(整个块大小SizeOfBlock - 头大小sizeof(IMAGE_BASE_RELOCATION)) / 数组元素大小sizeof(WORD),因为WORD大小为2,直接除以2可以少打一些字= =。通过IMAGE_BASE_RELOCATION头偏移一个SizeOfBlock可以直接到下一个重定位块。整个表以0000结尾,或者使用整个表的大小作为限制来判断结尾也行。

因为是按块来分的,这个块已经记录了rva,所以后面的数组就不记录块的rva了,而记录着重定位方式和相对块rva偏移量。所以这个数组的高4位用来记录重定位类型,低12位记录计算需要的偏移量。计算方式定义有,不写了。

常见的是32位程序的IMAGE_REL_BASED_HIGHLOW和64位程序的IMAGE_REL_BASED_DIR64。IMAGE_REL_BASED_HIGHLOW就是把计算得出的新地址的全部32位覆盖到那个位置上;关于计算方式一会再说,IMAGE_REL_BASED_DIR64就是把计算出的64位覆盖到上面。其它的方式有,ABSOLUTE跳过不处理,作用是用来使块对齐;HIGH、LOW是只应用高位或低位;HIGHADJ用奇怪的方式;其它方式少见而且我懒得写...见文档5.6.2节。

计算出偏移rva直接以分块记录的rva加上低12位就行了。最后修改那个位置的时候需要计算出一个值,计算的时候用这个式子:原来程序中对应位置的值 + 新base - 旧base = 现在程序中在该位置的值。原来程序中对应位置的值等同于文件中的值,写的这个意思是说刚把程序映射到内存没做重定位的时候,那个值还保留着文件中记录的值,这个今后会使用到。

举个栗子!

读到0x1000分块数组第一项为0x3641,高4为3,是HIGHLOW类型,取其低12位,偏移可算出,0x1000 + (0x3641 & 0x0fff) = 0x1641,加上base后可以得到需要重定位的位置;比如内存中base为0xb70000,最终位置为0xb71641,使用OD查看该位置:

00B71640   .  A1 6440BC00   mov eax,dword ptr ds:[0xBC4064]

用WinHex打开查看文件,对应位置(要把rva转一下)的值为0x01054064,文件可选头记录的ImageBase为0x01000000,于是这样计算,0x01054064 + 0xb70000 - 0x01000000 = 0xBC4064。

如果想把已经载入内存的代码移位,使用重定位表重定位一下就行了,今后再说。
代码参考:

bool PE::ReadRelocation(IN OUT PIMAGE_BASE_RELOCATION& tmpBaseRelocation, OUT PWORD& relocArray, bool isReadRelocArray, bool isReadOnceRelocation, IN OUT PDWORD stat)
{static DWORD relocArraySize;switch (*stat){default:case 0:if (tmpBaseRelocation == NULL && BaseRelocation == NULL){return false;}if (tmpBaseRelocation == NULL){tmpBaseRelocation = BaseRelocation;}relocArraySize = 0;while (tmpBaseRelocation->VirtualAddress != NULL){*stat = 2;if (isReadRelocArray){*stat = 1;relocArray = PWORD((DWORD)tmpBaseRelocation +  sizeof(IMAGE_BASE_RELOCATION));relocArraySize = (tmpBaseRelocation->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(WORD);while (relocArraySize--){return true;case 1:if (!isReadRelocArray){break;}relocArray++;}}else{return true;case 2:if (isReadRelocArray){continue;}}if (isReadOnceRelocation){return false;}tmpBaseRelocation = PIMAGE_BASE_RELOCATION((DWORD)tmpBaseRelocation + tmpBaseRelocation->SizeOfBlock);}break;}return false;
}

10、IMAGE_DIRECTORY_ENTRY_DEBUG【调试目录 - IMAGE_DEBUG_DIRECTORY】
描述了一些调试信息,其中的Type 类型定义为IMAGE_DEBUG_TYPE_XXX,根据Type的类型来确定偏移到的结构。文档5.1节写得比较详细。

11、IMAGE_DIRECTORY_ENTRY_ARCHITECTURE【IMAGE_ARCHITECTURE_HEADER】

指向特定架构数据,它是一个IMAGE_ARCHITECTURE_HEADER结构数组。不用于x86或IA-64,但看来已用于DEC/Compaq Alpha。数组的FirstEntryRVA指向的结构是IMAGE_ARCHITECTURE_ENTRY数组,这个数组以0xffffffffL结尾。在文档第6节,不过在文档23页写的是保留,必须置0。

12、IMAGE_DIRECTORY_ENTRY_GLOBALPTR

网上的介绍:在某些架构体系上VirtualAddress域是一个RVA,被用来作为全局指针(gp)。不用于x86,而用于IA-64。Size域没有被使用。文档23页有描述,但是没有详细介绍。

13、IMAGE_DIRECTORY_ENTRY_TLS【线程本地存储表 - IMAGE_TLS_DIRECTORY32/64】

当线程们使用同一个变量并且不想让线程们共享这个变量时使用。可以用WinApi TlsAlloc, TlsFree, TlsSetValue, 和 TlsGetValue来使用,在Vc++中直接用__declspec(thread)声明就行。具体在5.7节。因为是静态的关系,会影响使用LoadLibrary的延迟导入。

这个结构分为32位和64位版本,AddressOfCallBacks类型为PIMAGE_TLS_CALLBACK。具体使用方式就不说了,会跑题。

14、IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG【加载配置目录 - IMAGE_LOAD_CONFIG_DIRECTORY32/64】

介绍在文档5.8节。区分32和64位,在过去是用来描述一些复杂或者大型的文件头或可选头结构的,现在用来在xp之后的系统保留SEH技术用的(看文档大概是这个意思,我的渣翻译)。具体介绍在文档里,这里不讨论用法(实际这个我也没用过)。贴一段网上的介绍:IMAGE_LOAD_CONFIG_DIRECTORY中的信息是特定于Windows NT、Windows 2000和 Windows XP的(例如 GlobalFlag 值)。要把这个结构放到你的可执行文件中,你必须用名字__load_config_used 定义一个全局结构,类型是IMAGE_LOAD_CONFIG_DIRECTORY。对于非x86的其它体系,符号名是_load_config_used (只有一个下划线)。如果你确实要包含一个IMAGE_LOAD_CONFIG_DIRECTORY,那么在 C++ 中要得到正确的名字比较棘手。链接器看到的符号名必须是__load_config_used (两个下划线)。C++ 编译器会在全局符号前加一个下划线。另外,它还用类型信息修饰全局符号名。因此,要使一切正常,在 C++ 中就必须像下面这样使用:
extern "C"
IMAGE_LOAD_CONFIG_DIRECTORY _load_config_used = {...}

15、IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT【绑定导入表 - IMAGE_BOUND_IMPORT_DESCRIPTOR】

NumberOfModuleForwarderRefs记录了结构之后跟着的IMAGE_BOUND_FORWARDER_REF数组的大小。对应于这个映像绑定的每个DLL。数组元素中的时间戳允许加载器快速判断绑定是否是新的。如果不是,加载器忽略绑定信息并且按正常方式解决导入API。主要是用以加快载入。

16、IMAGE_DIRECTORY_ENTRY_IAT【导入表开头 - IMAGE_THUNK_DATA32/64】

这个取到的是iat的开头,跟用firstThunk的不同是:第一个ImportDirectory导入模块的firstThunk取到的地址不一定在这个表的开头。这个可以快速定位到Thunk数组的起始位置, 作用是让loader快速的定位到该位置设置可读写属性。

17、IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT【延迟导入表 - IMAGE_DELAYLOAD_DESCRIPTOR】  

数组结构,可以通过下标偏移。创建有延迟导入表的程序需要在链接器选项/DEALYLOAD那里填入想延迟导入的dll名称(除了一些必要的dll),这样原本应该在导入表的内容就会到这里来了。需要注意的是,延迟加载是第一次使用那个dll的时候才会载入,但是这个载入是由链接器和运行时库实现的。其定义在delayimp.h里也有,这里我用winnt.h里的,其实都一样。

第一个字段Attributes在8.3版的文档(我从官网下到的英文版,应该是最新的)中是置0的,介绍是还没有属性定义,还作为保留字段。不过在winnt.h里的定义是这样:

typedef struct _IMAGE_DELAYLOAD_DESCRIPTOR {union {DWORD AllAttributes;struct {DWORD RvaBased : 1;             // Delay load version 2DWORD ReservedAttributes : 31;};} Attributes;DWORD DllNameRVA;                       // RVA to the name of the target library (NULL-terminate ASCII string)DWORD ModuleHandleRVA;                  // RVA to the HMODULE caching location (PHMODULE)DWORD ImportAddressTableRVA;            // RVA to the start of the IAT (PIMAGE_THUNK_DATA)DWORD ImportNameTableRVA;               // RVA to the start of the name table (PIMAGE_THUNK_DATA::AddressOfData)DWORD BoundImportAddressTableRVA;       // RVA to an optional bound IATDWORD UnloadInformationTableRVA;        // RVA to an optional unload info tableDWORD TimeDateStamp;                    // 0 if not bound,// Otherwise, date/time of the target DLL} IMAGE_DELAYLOAD_DESCRIPTOR, *PIMAGE_DELAYLOAD_DESCRIPTOR;

按照这个定义,最低1位应该表示为延迟导入表的版本,高31位全保留,在vs2012中编译出的有延迟导入表的程序里,这个值为1,所以文档可能旧了。DllNameRVA顾名思义就是rva,以\0结尾的字符串。程序延迟载入dll时是这样做的:

程序中延迟导入表的信息是这样(我用vs2012调试的,现在用的电脑上没有OD):

+       Attributes  {AllAttributes=0x00000001 RvaBased=0x00000001 ReservedAttributes=0x00000000 }    _IMAGE_DELAYLOAD_DESCRIPTOR::<unnamed-type-Attributes>DllNameRVA  0x00016860  unsigned long               文件中值为(下同): USER32.dllModuleHandleRVA    0x00019134  unsigned long               00000000ImportAddressTableRVA   0x0001b070  unsigned long           D7144100   会被重定位,最后还会被修改,现在这里的地址跳到延迟载入的代码ImportNameTableRVA   0x0001b040  unsigned long           A0B00100   以这个为rva再指到   MessageBoxA 字符串,这是用到的apiBoundImportAddressTableRVA   0x0001b1b0  unsigned long       00000000UnloadInformationTableRVA   0x00000000  unsigned longTimeDateStamp  0x00000000  unsigned long

程序基址为0x01250000,当调用到这个dll中的api时:

 MessageBoxA(0,"xxx","ccc",0);
01264388  mov         esi,esp
0126438A  push        0
0126438C  push        1266858h
01264391  push        126685Ch
01264396  push        0
01264398  call        dword ptr ds:[126B070h]

程序call的地址 (这个值被重定位过了,原来的值在上面写)为:*(基址+ImportAddressTableRVA),跳到了重定位后的0x012614D7,这里代码为:

012614D7 B8 70 B0 26 01       mov         eax,126B070h
012614DC E9 2E FB FF FF       jmp         __tailMerge_USER32_dll (0126100Fh)  

跳转2次到这里:

__tailMerge_USER32_dll:
012614E3 51                   push        ecx
012614E4 52                   push        edx
012614E5 50                   push        eax
012614E6 68 00 B0 26 01       push        126B000h
012614EB E8 42 FB FF FF       call        ___delayLoadHelper2@8 (01261032h)
012614F0 5A                   pop         edx
012614F1 59                   pop         ecx
012614F2 FF E0                jmp         eax

中间call的函数可以取到它的代码,因为延迟导入是靠编译器来实现的,所以相关的代码要编进exe里。这个函数的代码在delayhlp.cpp的205行(vs2012):

extern "C"
FARPROC WINAPI
__delayLoadHelper2(PCImgDelayDescr     pidd,FARPROC *           ppfnIATEntry) {// Set up some data we use for the hook procs but also useful for// our own use//InternalImgDelayDescr   idd = {pidd->grAttrs,PFromRva<LPCSTR>(pidd->rvaDLLName),PFromRva<HMODULE*>(pidd->rvaHmod),PFromRva<PImgThunkData>(pidd->rvaIAT),PFromRva<PCImgThunkData>(pidd->rvaINT),PFromRva<PCImgThunkData>(pidd->rvaBoundIAT),PFromRva<PCImgThunkData>(pidd->rvaUnloadIAT),pidd->dwTimeStamp};DelayLoadInfo   dli = {sizeof DelayLoadInfo,pidd,ppfnIATEntry,idd.szName,{ 0 },0,0,0};if (0 == (idd.grAttrs & dlattrRva)) {PDelayLoadInfo  rgpdli[1] = { &dli };RaiseException(VcppException(ERROR_SEVERITY_ERROR, ERROR_INVALID_PARAMETER),0,1,PULONG_PTR(rgpdli));return 0;}HMODULE hmod = *idd.phmod;// Calculate the index for the IAT entry in the import address table// N.B. The INT entries are ordered the same as the IAT entries so// the calculation can be done on the IAT side.//const unsigned  iIAT = IndexFromPImgThunkData(PCImgThunkData(ppfnIATEntry), idd.pIAT);const unsigned  iINT = iIAT;PCImgThunkData  pitd = &(idd.pINT[iINT]);dli.dlp.fImportByName = !IMAGE_SNAP_BY_ORDINAL(pitd->u1.Ordinal);if (dli.dlp.fImportByName) {dli.dlp.szProcName = LPCSTR(PFromRva<PIMAGE_IMPORT_BY_NAME>(RVA(UINT_PTR(pitd->u1.AddressOfData)))->Name);}else {dli.dlp.dwOrdinal = DWORD(IMAGE_ORDINAL(pitd->u1.Ordinal));}// Call the initial hook.  If it exists and returns a function pointer,// abort the rest of the processing and just return it for the call.//FARPROC pfnRet = NULL;if (__pfnDliNotifyHook2) {pfnRet = ((*__pfnDliNotifyHook2)(dliStartProcessing, &dli));if (pfnRet != NULL) {goto HookBypass;}}// Check to see if we need to try to load the library.//if (hmod == 0) {if (__pfnDliNotifyHook2) {hmod = HMODULE(((*__pfnDliNotifyHook2)(dliNotePreLoadLibrary, &dli)));}if (hmod == 0) {hmod = ::LoadLibraryEx(dli.szDll, NULL, 0);}if (hmod == 0) {dli.dwLastError = ::GetLastError();if (__pfnDliFailureHook2) {// when the hook is called on LoadLibrary failure, it will// return 0 for failure and an hmod for the lib if it fixed// the problem.//hmod = HMODULE((*__pfnDliFailureHook2)(dliFailLoadLib, &dli));}if (hmod == 0) {PDelayLoadInfo  rgpdli[1] = { &dli };RaiseException(VcppException(ERROR_SEVERITY_ERROR, ERROR_MOD_NOT_FOUND),0,1,PULONG_PTR(rgpdli));// If we get to here, we blindly assume that the handler of the exception// has magically fixed everything up and left the function pointer in // dli.pfnCur.//return dli.pfnCur;}}// Store the library handle.  If it is already there, we infer// that another thread got there first, and we need to do a// FreeLibrary() to reduce the refcount//HMODULE hmodT = HMODULE(InterlockedExchangePointer((PVOID *) idd.phmod, PVOID(hmod)));if (hmodT == hmod) {::FreeLibrary(hmod);}}// Go for the procedure now.//dli.hmodCur = hmod;if (__pfnDliNotifyHook2) {pfnRet = (*__pfnDliNotifyHook2)(dliNotePreGetProcAddress, &dli);}if (pfnRet == 0) {if (pidd->rvaBoundIAT && pidd->dwTimeStamp) {// bound imports exist...check the timestamp from the target image//PIMAGE_NT_HEADERS   pinh(PinhFromImageBase(hmod));if (pinh->Signature == IMAGE_NT_SIGNATURE &&TimeStampOfImage(pinh) == idd.dwTimeStamp &&FLoadedAtPreferredAddress(pinh, hmod)) {// Everything is good to go, if we have a decent address// in the bound IAT!//pfnRet = FARPROC(UINT_PTR(idd.pBoundIAT[iIAT].u1.Function));if (pfnRet != 0) {goto SetEntryHookBypass;}}}pfnRet = ::GetProcAddress(hmod, dli.dlp.szProcName);}if (pfnRet == 0) {dli.dwLastError = ::GetLastError();if (__pfnDliFailureHook2) {// when the hook is called on GetProcAddress failure, it will// return 0 on failure and a valid proc address on success//pfnRet = (*__pfnDliFailureHook2)(dliFailGetProc, &dli);}if (pfnRet == 0) {PDelayLoadInfo  rgpdli[1] = { &dli };RaiseException(VcppException(ERROR_SEVERITY_ERROR, ERROR_PROC_NOT_FOUND),0,1,PULONG_PTR(rgpdli));// If we get to here, we blindly assume that the handler of the exception// has magically fixed everything up and left the function pointer in // dli.pfnCur.//pfnRet = dli.pfnCur;}}SetEntryHookBypass:*ppfnIATEntry = pfnRet;HookBypass:if (__pfnDliNotifyHook2) {dli.dwLastError = 0;dli.hmodCur = hmod;dli.pfnCur = pfnRet;(*__pfnDliNotifyHook2)(dliNoteEndProcessing, &dli);}return pfnRet;}

上面的代码文件里都有。pidd是延迟导入表的指针,ppfnIATEntry是前面的0x0126b070。这个函数实际上会调用LoadLibrary来载入延迟导入表中记录的Dll名字(所以KERNEL32.dll别想用延迟导入来消除导入表中的记录了,这需要用其它方法),还会调用GetProcAddress取函数地址,然后把取到的地址写回0x0126b070,就是原来导入表的对应位置,最后函数返回的是延迟导入api的地址,因为参数在前面已经压栈,所以直接jmp eax执行。而因为修改的是iat(这里用的方法类似iathook),所以后面函数再调用到这个api的时候会直接call真实函数地址。当载入同dll的另一个api时会直接取到载入的dll基址然后取得函数地址再修改iat,所以定义中需要一个ModuleHandleRVA。

18、IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR【CLI头 - IMAGE_COR20_HEADER】
网上的介绍是:在最近更新的系统头文件中这个值已被改名为IMAGE_DIRECTORY_ENTRY_COMHEADER。它指向可执行文件中.NET信息的最高级别信息,包括元数据。这个信息是一个IMAGE_COR20_HEADER结构。但是我在Windows Kits 8.0中看到的名字还是原来的名,是不是错过了什么...

在.net程序中,pe结构的某些值是固定的,IMAGE_FILE_HEADER的PointerToSymbolTable、NumberOfSymbols固定为0,IMAGE_OPTIONAL_HEADER的ImageBase固定为0x400000,SectionAlignment为0x2000,FileAlignment为0x200或0x1000,数据目录只有导入表、重定位表、iat和现在说的这个结构有值外,其它都为0。有导入表重定位表和iat的原因是程序需要导入mscoree.dll的_CorExeMain(dll为_CorDllMain)。

这个是.Net程序特有的结构,其定义在winnt.h、corhdr.h里。其中cb为IMAGE_COR20_HEADER结构的大小,跟数据目录中的大小写的一样,Flags取值是枚举ReplacesCorHdrNumericDefines的COMIMAGE_FLAGS_XXX,这个结构很像可选头,因为里面内容几乎都为IMAGE_DATA_DIRECTORY结构,只不过不用下标取数据了而已。EntryPointToken和EntryPointRVA同属一个联合体,当COMIMAGE_FLAGS_NATIVE_ENTRYPOINT没被设置时,指的是托管入口点。StrongNameSignature需要设置COMIMAGE_FLAGS_STRONGNAMESIGNED,CodeManagerTable、ExportAddressTableJumps总是为0。下面用到的文档为Partition II Metadata(下载地址)。

下面开始(我看的英文版,可能会理解错误,见谅):

18.1、MetaDataRoot【元数据头(这里的名称取自CFF Explorer汉化版的翻译,下同)】

文档的176页,第24节。不知道在哪个头文件有定义的代码,估计也定义不了,里面有一项是字符串,但是没有使用指针形式而是直接包括在了元数据头中,所以这个头大小是可变的,这样就不好定义了。原本以为可以用C#的BinaryFormatter,但是发现序列化出来的东西只是相似而已,所以解析还是要自己写代码(我不想写啊)。

元数据头以一个magic number开头:0x424A5342(BJSB = BustinJieberShaBi - 开玩笑),这个字段叫Signature。其它定义下面的代码里有了。造成数据头变长的是版本号,这里使用字符串来记录(ntm逗我),而之后还要按四字节对齐。我还是强行“定义”出来了:

typedef struct _METADATA_ROOT
{_METADATA_ROOT(PVOID address):Signature((PDWORD)address) // address为MetaData的指针{MajorVersion = PWORD(Signature + 1);MinorVersion = PWORD(MajorVersion + 1);Reserved = PDWORD(MinorVersion + 1);Length= PDWORD(Reserved + 1);Version = PCHAR(Length + 1);// 后面有补齐的0,长度也在Length里了,所以Flags可以直接根据Length来得到Flags = PWORD(Version + *Length);Streams = PWORD(Flags+1);}PDWORD Signature;PWORD MajorVersion;PWORD MinorVersion;PDWORD Reserved;PDWORD Length;PCHAR Version; PWORD Flags;PWORD Streams;
}METADATA_ROOT,*PMETADATA_ROOT;

为了能偏移到文件中的位置,所以我都是以指针形式声明的,为的是以后能方便地修改。

18.2、StreamHeader【数据流表】

跟在元数据头后,Offset是相对MetaDataRoot的偏移;Size为指向的流的大小,必须为4的整数;Name是以0结尾并且以下一个4字节对齐的Ascii字符串(ntm逗我),最长为32字符。根据文档显示,有5种不同的堆(文档里用的词为heap):#String、#US、#Blob、#GUID、#~。每一种流最多出现一次,文档意思是如果没有这个流的话,这个流就不会出现在表里。下面简单说说几个流。

18.2.1、#String

里面可能会包含不会使用到的字符串(....heaps can contain garbage...),但它所包含的使用到的字符串都是utf8且以0结尾的。第一项是空字符串\0。这文档说的是字符串堆的物理表示法。

18.2.2、#US和#Blob

还是can contain garbage(ntm逗我),#US(user string,Unicode)存的是会用到的一些字符串,当字符串中UTF16字符的高位或者低位有0x01–0x08, 0x0E–0x1F, 0x27, 0x2D, 0x7F这些值的话,字符串最后的字节置1,否则置0(This final byte holds the value 1 if and only if any UTF16 character within the string has any bit set in its top byte, or its low byte is any of the following: 0x01–0x08, 0x0E–0x1F, 0x27, 0x2D, 0x7F. Otherwise, it holds 0.),#Blob是会用到的一些数据,每一个数据的前面有几个字节是用来描述后面的数据有多长的,原文如下:

· If the first one byte of the 'blob' is 0bbbbbbb2, then the rest of the 'blob' contains thebbbbbbb2 bytes of actual data.
· If the first two bytes of the 'blob' are 10bbbbbb2 andx, then the rest of the 'blob' contains the (bbbbbb2<< 8 + x) bytes of actual data.
· If the first four bytes of the 'blob' are 110bbbbb2,x, y, and z, then the rest of the 'blob' contains the (bbbbb2 << 24 + x<< 16 + y << 8 + z) bytes ofactual data. 

这是说:

1.如果第一个字节的高1位为0,则后面包含以低7位值作为长度的数据。

2.如果第一字节高2位为10,则后面数据长度为(低6位<<8+第二字节的值)字节。

3.如果第一字节高3位为110,后面长度为(低5位<<24+第二字节值<<16+第三字节值<<8+第四字节值。

<<应该是移位符号,但是第三条算出的数是不是有点太大了

18.2.3、#GUID

保存一个以16字节表示的GUID的序列,里面的一些GUID有可能是不会用到的。

18.2.4、#~

这个结构我放后面说是因为文档介绍是在后面,这个结构其实是紧跟着数据流表的。的前4字节是保留位,置0。HeapSizes占1个字节,根据置位来确定其它几个流的每个堆所占的字长。如果对应位置1,该位所代表的流的堆就为4字节宽否则为2字节;第0位是#String,第1位是#GUID,第2位是#Blob。后面紧接着始终为1的保留字Reserved。接着的Valid和Sorted是64位字长的,前者每一位代表出现了哪些元数据表,后者为排序了哪些表。Valid因为有一些位没定义完作为保留,所以0x2c之上的值都为0。Rows是有n项的DWORD类型数组,这个n的值取决于前面Valid中1的个数(就是有几张表),这个数组内容为每张表的行数,没有的表就略掉。Rows之后就是表的内容了,同样也有n项。每个位的定义太多就不翻译了。

CLI头不好写定义,解析的时候还是边看文档边读出要读的数据。其它一些介绍可以看这篇文章,然后这篇(一、二)是讲pe格式的。

现在基本上pe中要解析的东西都说完了,写个界面把代码套进去就行了,按前面代码的结构应该不难扩展,界面就不上图了。因为C#用得比较多,所以命名规范不是用c++的,看得比较吃力请见谅。另外代码我在64位使用的时候发现有一些字长上的BUG,太麻烦就不改到博客上了。

编写PE文件解析器(三)相关推荐

  1. 图解VC++版PE文件解析器源码分析

    该源码下载自 http://download.csdn.net/download/witch_soya/4979587 1 Understand 分析的图表 2 PE结构解析的主要代码简要分析 首先看 ...

  2. XML - XML学习/XML文件解析器(C++)实现

    XML - XML学习/XML文件解析器(C++)实现 XML概述 ​ XML是一套定义语义标记的规则,这些标记将文档分成许多部件并对这些部件加以标识.它也是元标记语言,用于定义其他与特定领域有关的, ...

  3. 实验五——手工编写PE文件

    [实验名称] 手工编写PE文件 [实验目的] 1.了解PE文件的概念.结构 2.熟悉PE编辑查看工具,详细了解PE文件格式 3.重点分析PE文件文件头.引入表.引出表,以及资源表 [实验原理] 1.P ...

  4. 使用springMVC提供的CommonsMultipartResolver文件解析器,实现文件轻松上传

    springMVC提供的前端控制器,可以拦截所有请求,指挥调度所有后台逻辑资源. 使用传统方式进行文件上传,需要我们手动解析request对象,获取文件上传项,再进行文件的上传. springMVC框 ...

  5. Win32汇编:PE结构解析器

    PE格式是Windows系统下最常用的可执行文件格式,有些应用必须建立在了解PE文件格式的基础之上,如可执行文件的加密与解密,文件型病毒的查杀等,熟练掌握PE文件结构,有助于软件的分析. 在PE文件中 ...

  6. [翻译]运用文件解析器在任意文件中使用虚拟应用路径(~)

    原文出处:http://www.codeproject.com    Using the FileResolver to allow virtual application paths ( ~ ) i ...

  7. Glib学习(17) Key-value文件解析器 Key-value file parser

    glib源码下载:http://ftp.gnome.org/pub/gnome/sources/glib/ glib帮助文档:https://developer.gnome.org/glib/ 本节主 ...

  8. torrent文件解析器

    第二步工作是解析torrent文件,有了bencoding编码解析器 解析torrent文件当然是易如反掌的任务了. 实现的封装类CTorrentParser,完成的主要任务有: 1.判断torren ...

  9. 15.windbg-dds、dps、dqs、PE文件解析

    以下默认windbg加载calc程序 d*s dds.dps和dqs命令显示给定范围内存的内容,它们是把内存区域转储出来,并把内存中每个元素都视为一个符号对其进行解析,dds是四字节视为一个符号,dq ...

最新文章

  1. shell在linux里摇摇晃晃
  2. 精简JRE第一步 — 精简bin目录
  3. java 字符串赋值_Java 学习笔记(二)变量
  4. Session的模拟
  5. P2P之UDP穿透NAT的原理与实现(转)
  6. 脚本语言php是什么意思,php是什么脚本语言
  7. 信息奥赛一本通(1325:【例7.4】 循环比赛日程表)
  8. php7 mcrypt模块_如何在php7.2/php7.3中安装mcrypt扩展?
  9. 新增成功到编制为空bug_36 个JS 面试题为你助力金九银10
  10. seata使用报错no available service found in cluster ‘default‘
  11. 树莓派开启samba服务
  12. Android读取电话薄中的电话号码
  13. snakeyaml jyaml 哪个好_lol手游哪个英雄可玩性高 英雄联盟手游英雄强度排行
  14. 云课堂智慧职教答案python_云课堂智慧职教答案python
  15. 最新全国行政区域编码(2018年12月)
  16. Mysql获取当天用户生日
  17. 四核64位处理器,MIMX8MQ5DVAJZAB 满足智能设备应用
  18. Mini CFA 考试练习题 Ethics and Investment Professionalism
  19. Redis服务入侵记
  20. 无法启用网络发现和文件共享或共享无法访问

热门文章

  1. pr系统兼容性报告不支持视频驱动程序有什么影响?怎么解决?
  2. 计算机经常突然死机重启,家里电脑最近经常会出现重启死机的现象是什么原因?...
  3. 刺激战场android闪退,《绝地求生刺激战场》老是闪退怎么办 老是闪退解决方法介绍...
  4. 荣耀V40怎么样 “微光女神”告诉你
  5. 红警ol服务器维护中1003,【图片】红警ol心灵终结3单位全面解析_红警ol吧_百度贴吧...
  6. 模电——电源与地之间并联电容的作用
  7. 小说光看还不够?当然得有美女一样的声音来阅读!
  8. Socket编程入门C++
  9. 新浪微博粉丝通推广效果分析
  10. 云游戏的2022:破局、新生、元宇宙