有关PE的偏移:硬盘上,进程中..
|/:^]
很是郁闷自己的语言水平,E文的意思翻译不成C文,自己想到的又E文C文都想不出怎么表述.....
那就根据自己的记忆(就是对各位文章的印象^_^)和动手找的一些拼凑在一起....难免有错误,请指出!
------------------------------------------------
win32中PE的概念是 "调用API和自己编写的函数来实现某种功能的" code集合体,在PE头定义了在内存如
何组合这些code.
--------------------------------------------
所有的数字可能都是大概,没有确定是否准确,但数据应该会在周围,也许是特定的编译器和32位环境的结
果.
---------------------------------------------------------------------------
如下只是为了方便手动修改PE把常用偏移算出来,详细的PE头格式在论坛有很多相关的好文章介绍.
+++++++++++++++++++++++++++++++++++++++++++++++++++++++
win32中的文件在硬盘上是按照如此格式保存的:
储存在硬盘上的PE文件的开始位置有一个DOS下的stub可执行文件,在它的3Ch处有个E_lfanew,指向PE的
可执行体的开始处:
PE文件标志,50450000[PE ]
PE_Header(14h的大小,E_lfanew偏移04h) :
第3个字节是块数目.NumberOfSections
第17(11h)字节是可选头的大小[E0h],占用2字节.sizeOfOptionalHeader
第19(13)字节开始是文件格式标志,占用2个字节.Charcteristics
PE_Optional_header(e0h大小,E_lfanew偏移18h):
开始2个字节是Magic[10Bh],2字节
第5个字节到8字节是SizeOfCode,代码块的大小,控制PE_lorder如何载入此PE,4字节
第9至12(Ch)字节是不需要初始化的数据块的大小SizeOfInitilizedData,4字节
第13(Dh)-18(12h)是需要初始化的数据块的大小 SizeOfUNintializedData,4字节
第19(13h)-22(16h)是CODE段开始执行的位置[1000],即入口点AddressOfEntrPoint,4字节RVA表述
第23(17h)-26(1Ah)是CODE段的起始位置[1000],即它需要被载入的地址-基址,BaseOfCode,4字节RVA
表述
第27(1Bh)-30(1Eh)是data段的起始位置,即它需要被载入的地址-基址,BaseOfData,4字节RVA表述
第31(1Fh)-34(22h)是内存镜像的基址[400000],ImageBase,4字节
第57(3Ah)-60(3Eh)是内存镜像的大小,即需要占用的内存的大小。SizeOfImage,4字节
第61(3Fh)-64(43h)是PE_header的大小.4字节
第104(68h)-110(72h){不准确!}是IMT的RVA和size
73h-7Bh的8字节可能是输入表的RVA和size
应该在在段_table前面8*14字节处
PE_Section_Table(28h大小,E_lfanew偏移F8h)
段_raw的结构:
第1-8字节为块Name,8字节
第9-12(Ch)字节为映射到内存镜像中块的大小,VirtualSize,4字节
第13(Dh)-16(10h)字节位镜像中块的地址,VirtualAddress,8字节
接下来8个字节是硬盘中块的信息,size,RVA
最后4个字节为Characteristics,设置块使用的属性.即为每个区段名称前面4字节.
section_table会以一串00结尾.
|||||||||||||||||||||||||PS分割线|||||||||||||||||||||||||||||||
section中一般会有一个idata(import)类型的块 .
idata(import)的结构
主要是由指向一个个Image_Import_Directory结构的指针组成的序列.
Image_import_Dirctory的结构:
整个结构组合以20个字节的00结尾和开头,属于不同DLL的被用4个字节的00分开
一个4字节的union (union中前1个数据值在NT永远为0,所以第二个数据Original First Thunk的值为
union值) 指向这个函数所在DLL中的此函数hint(编号).由hint直接引导到函数
4字节的TimeDataStamp
4字节的ForwarderChain IID结构的入口,引导调用
4字节的指针Name,DLL名字的字符串在image的RVA
4字节的指针FirstThunk,指向导入表IAT,当没有O_F_T值时由FirstThunk来引导到IAT结构使用函数
导入表IAT结构.以8个字节(?)的00隔开不同的DLL
hint,此函数在Dll的编号
RVA,此函数在镜像中的RVA
------------------------Import与export分割线--------------------------------
导出表的结构edata(export)
主要是由指向一个个Image_export_Directory结构的指针组成的序列.
Image_export_directory结构
( ???整个结构组合以20个字节的00结尾和开头,属于不同DLL的被用4个字节的00分开???)
4字节的characteristics ?保留字?
4字节的timedatestamp 日期标号
4字节的版本dll号
4字节的指针name指向dll的名字
4字节的base,基数。与hint有关
4字节的NumberOfFunctions,函数数量
4字节的NumberOfNames.可以用名字调用的函数的数量
4字节的指针adressOffrnction,指向FAT(function adress表),也就是此export的所有导出函数,RAV表述
4字节的指针AdressOfName,指向FNT(function name可以用名字调用的函数的地址组成的表),RAV表述
4字节的指针adressOfNameOrdinals,指向FOT(function Ordinal表,由函数的hint组成的表),RAV表述
|||||||||||||||||||||||PS结束分割线||||||||||||||||||||||||||||||||
在section的后面是section的内容直到文件的结尾.
++++++++++++++++++++++++++++++++++++++++++++++++++++
PE装载到内存中则是进程了,不同的进程被迫使用不同阶层的内存,同样的表述:每个进程会被操作系统放到
4G的empty空间以供它使用.
PS:进程中的code内容只是把硬盘中的区段映射到内存中而得到的
PS:code中的内容是被编译器翻译成可以被机器处理的语言来实现编译前的相同功能的格式.
每个process都有自己的PEB,里面包含current process(本进程)的一些(资料?档案?)属性
PEB的格式
在论坛没有找到这个结构,于是贴出来
typedef struct _PEB {
BOOLEAN InheritedAddressSpace;
BOOLEAN ReadImageFileExecOptions;内存中镜像文件的属性,以peb为基址,偏移0x01
BOOLEAN BeingDebugged;是否被调试的标志,以peb为基址,偏移0x02
BOOLEAN Spare;
HANDLE Mutant;
PVOID ImageBaseAddress;从文件中读取的基址,以peb为基址,偏移0x08
PPEB_LDR_DATA LoaderData;此指针指向PEB_LDR_DATA结构,以peb为基址,偏移0x0c
PRTL_USER_PROCESS_PARAMETERS ProcessParameters;此指针指RP_U_P_P结构(设置很多进程属
性)以peb为基址,偏移0x10
PVOID SubSystemData;子系统的选项,比较简单而又乱,请参考undocumented Windows NT的第一章.偏
移 ,以peb为基址,偏移0x14
PVOID ProcessHeap;每个进程都可以申请多个堆。指针ProcessHeap 指向first heap的首地址。以peb为基址,偏移0x18
PVOID FastPebLock;指向此peb中的PEBLOCKROUTINE,以peb为基址,偏移0x1c
PPEBLOCKROUTINE FastPebLockRoutine;PPEBLOCKROUTINE链表,以peb为基址,偏移0x20
PPEBLOCKROUTINE FastPebUnlockRoutine;猜测:这个链表的作用是选择PEB被使用与否
ULONG EnvironmentUpdateCount;这个PEB从创建到现在被修改次数,以peb为基址,偏移0x28
PPVOID KernelCallbackTable;
PVOID EventLogSection;
PVOID EventLog;
PPEB_FREE_BLOCK FreeList;需要释放的块的链表 ,以peb为基址,偏移0x38
ULONG TlsExpansionCounter;下面是一下繁琐的属性设置.以peb为基址,偏移0x3c
PVOID TlsBitmap;
ULONG TlsBitmapBits[0x2];
PVOID ReadOnlySharedMemoryBase;
PVOID ReadOnlySharedMemoryHeap;
PPVOID ReadOnlyStaticServerData;
PVOID AnsiCodePageData;
PVOID OemCodePageData;
PVOID UnicodeCaseTableData;
ULONG NumberOfProcessors;
ULONG NtGlobalFlag;
BYTE Spare2[0x4];6c
LARGE_INTEGER CriticalSectionTimeout;
ULONG HeapSegmentReserve;
ULONG HeapSegmentCommit;
ULONG HeapDeCommitTotalFreeThreshold;
ULONG HeapDeCommitFreeBlockThreshold;
ULONG NumberOfHeaps;是当前进程的堆的个数,偏移
ULONG MaximumNumberOfHeaps;是可以申请的堆的总数,偏移
PPVOID *ProcessHeaps;指向一个堆指针数组的首地址(此数组的样子类似idata(import))每个数组元素长4字
节,分别指向一个堆的首地址。
PVOID GdiSharedHandleTable;
PVOID ProcessStarterHelper;
PVOID GdiDCAttributeList;
PVOID LoaderLock;
ULONG OSMajorVersion;
ULONG OSMinorVersion;
ULONG OSBuildNumber;
ULONG OSPlatformId;
ULONG ImageSubSystem;
ULONG ImageSubSystemMajorVersion;
ULONG ImageSubSystemMinorVersion;
ULONG GdiHandleBuffer[0x22];
ULONG PostProcessInitRoutine;
ULONG TlsExpansionBitmap;
BYTE TlsExpansionBitmapBits[0x80];
ULONG SessionId;
} PEB, *PPEB;
typedef void (*PPEBLOCKROUTINE)(PVOID PebLock);
;PEB_LDR_DATA结构
typedef struct _PEB_LDR_DATA {;这是载入的的dll的链表,或者:此结构作用,枚举当前进程空间中的模块
ULONG Length;
BOOLEAN Initialized;是否已经被初始化,即此模块是否被载入.PEB_LDR_DATA为基址,偏移0x04
PVOID SsHandle;句柄.PEB_LDR_DATA为基址,偏移0x08
LIST_ENTRY InLoadOrderModuleList;.以PEB_LDR_DATA为基址偏移0x0c
LIST_ENTRY InMemoryOrderModuleList;第一个PEB_LDR_DATA此链表偏移0x14
LIST_ENTRY InInitializationOrderModuleList;第一个PEB_LDR_DATA此链表偏移0x1c
} PEB_LDR_DATA, *PPEB_LDR_DATA;每个PEB_LDR_DATA结构大小为0x24
;_LDR_MODULE的结构,此链表和PEB_LDR_DATA共同定位以加载模块:dll..
typedef struct _LDR_MODULE {;_LDR_MODULE包含模块的一些属性,信息
LIST_ENTRY InLoadOrderModuleList; PEB_LDR_DATA和struct _LDR_MODULE就是通过这三个
;LIST_ENTRY把所有的模块连接
LIST_ENTRY InMemoryOrderModuleList; 起来的。同一个模块的这三个List-Entry是_LDR_MODULE,
;PEB_LDR_DATA共用的.具体参考
;我自己改写的一个结构full_module_struct_in_memory
LIST_ENTRY InInitializationOrderModuleList ; LIST_ENTRY类型的大小是8byte.包含2个指针。
PVOID BaseAddress;_LDR_MODULE为基址,偏移0x18.此模块的基址
PVOID EntryPoint;_LDR_MODULE为基址,偏移0x1c.此模块的入口
ULONG SizeOfImage;_LDR_MODULE为基址,偏移0x20.此模块的镜像大小
UNICODE_STRING FullDllName;_LDR_MODULE为基址,偏移0x24.此模块的名字,如,user32.dll
UNICODE_STRING BaseDllName;_LDR_MODULE为基址,偏移0x2c.简写 ?
ULONG Flags; 这几个的作用E文已经很清楚了
SHORT LoadCount;
SHORT TlsIndex;
LIST_ENTRY HashTableEntry;
ULONG TimeDateStamp;
} LDR_MODULE, *PLDR_MODULE;
typedef struct full_module_struct_in_memory{ ;这是PEB_LDR_DATA和LIST_ENTRY在
ulong Length;内存中的格式,可以很清楚的看到他们其实是保存在一起的
boolean Initialozer;
pvoid Sshandle;
InLoadOrderModuleList;指向下一个full_module_struct_in_memory中的InLoadOrderModuleList
InMemoryOrderModuleList;同上
InInitializationOrderModuleList;同上
Void* BaseAddress;
void* EntryPoint;
ulong SizeOfImage;
UNICODE_STRING FullDllnamek
UNICODE_STRING BaseDLLName;
ulong Flags;
SHORT LoadCout;
SHORT TlsIndex;
Handle SectionHandle;
ulong ChechSum;
ulong TimeDateStamp;
}
typedef struct _RTL_USER_PROCESS_PARAMETERS {
ULONG MaximumLength; RtlCreateProcessParameters的一个参数
ULONG Length;此结构长度
ULONG Flags;不清楚
ULONG DebugFlags;此进程是否被调试的标志
PVOID ConsoleHandle;计数器句柄,指向一个类似Original First Thunk的数组中一个ID(hint),可以不使用
ULONG ConsoleFlags;是否有计数器句柄
HANDLE StdInputHandle;
HANDLE StdOutputHandle;
HANDLE StdErrorHandle;
UNICODE_STRING CurrentDirectoryPath;DOS-like symbolic link path
HANDLE CurrentDirectoryHandle;
UNICODE_STRING DllPath; path环境变量
UNICODE_STRING ImagePathName;这个进程的名称
UNICODE_STRING CommandLine;
PVOID Environment;在使用RtlCreateEnvironment创建的PARAMETERS中指向创建的environment block.
ULONG StartingPositionLeft;下面是一些进程在内存中保存的格式设置
ULONG StartingPositionTop;
ULONG Width;
ULONG Height;
ULONG CharWidth;
ULONG CharHeight;
ULONG ConsoleTextAttributes;
ULONG WindowFlags;
ULONG ShowWindowFlags;
UNICODE_STRING WindowTitle;
UNICODE_STRING DesktopName;
UNICODE_STRING ShellInfo;
UNICODE_STRING RuntimeData;
RTL_DRIVE_LETTER_CURDIR DLCurrentDirectory[0x20];
} RTL_USER_PROCESS_PARAMETERS, *PRTL_USER_PROCESS_PARAMETERS;
在使用CreateProcess创建的进程中RTL_USER_PROCESS_PARAMETERS位于偏移0x00020000 ,也许是
错误的?
typedef struct _UNICODE_STRING {
USHORT Length;
USHORT MaximumLength;
PWSTR Buffer;
} UNICODE_STRING, *PUNICODE_STRING;
;下面2个结构虽然不常用,还是列出来吧
typedef struct _RTL_DRIVE_LETTER_CURDIR {
USHORT Flags;
USHORT Length;
ULONG TimeStamp;
UNICODE_STRING DosPath;
} RTL_DRIVE_LETTER_CURDIR, *PRTL_DRIVE_LETTER_CURDIR;
typedef struct _PEB_FREE_BLOCK {
PEB_FREE_BLOCK *Next; Pointer to next free block.
ULONG Size; Size of block, in bytes.
} PEB_FREE_BLOCK, *PPEB_FREE_BLOCK;
;Structure PEB_FREE_BLOCK is used internally in PEB (Process Enviroment Block) structure for describe
;free blocks in memory allocated for PEB.
对peb的 一个利用:
从FS:[0x30]获得PEB地址。从[0x0c]获得第一个LDR结构的地址,从Ldr[0x0c]获取InLoadOrderModuleList.
然后从InLoadOrderModuleList开始前向遍历循环链表显示LDR_MODULE结构的BaseAddress、
FullDllName成员.
................................................................
PS:也不知道对不对,想起来了就复制来了
每一个线程都会有一个TIB,每个进程可以有多个线程,因此在一个进程的(4G)空间,正常情况下会有有多个
TIB,不同线程属于的进程在TIB[0x30]标志.
TEB和TIB的关系?thread environment block, 线程环境块,顾名思义就是线程的context,是win XP以及以上
今天看到TLS给认错了,这样修改提醒下:
线程局部存储Thread Local Storage(TLS),可以给每个线程提供自己用的全局变量,这样每个线程就可以同时工作且是不同的工作了
虽然TLS 很方便,它并不是毫无限制。在Windows NT 和Windows 95 之中,有64 个DWORD slots 供每一个线程使用。这意思是一个进程最多可以有64 个「对各线程有不同意义」的DWORDs。
这样很可能就不够用了,可以把这些变量写成结构,只把结构指针放在TLS 中。TLS还可以保存一些标志 |\:^]
在侯捷的书中这些数据储存的地方被简写成TDB(thread datebases),感觉上很直白了,这里标明下.
系统使用的,thread information block,线程信息块,是95,98,NT,2000使用的
TEB的结构没有在论坛找到,继续贴.
typedef struct _TEB {
NT_TIB Tib;指向 NT_TIB结构.参考Matt Pietrek的作品或自行搜索
PVOID EnvironmentPointer;
CLIENT_ID Cid;
PVOID ActiveRpcInfo;
PVOID ThreadLocalStoragePointer;本线程储存空间指针 ?以struct _TEB为基址,偏移0x2c
PPEB Peb;PEB指针,MS这儿也可以到达PEB.以struct _TEB为基址,偏移 0x30
ULONG LastErrorValue;熟悉?以struct _TEB为基址, 偏移0x34
ULONG CountOfOwnedCriticalSections; 如下是一些繁琐的环境信息指针和设置。
PVOID CsrClientThread;
PVOID Win32ThreadInfo;
ULONG Win32ClientInfo[0x1F];
PVOID WOW32Reserved;
ULONG CurrentLocale;
ULONG FpSoftwareStatusRegister;
PVOID SystemReserved1[0x36];
PVOID Spare1;
ULONG ExceptionCode;
ULONG SpareBytes1[0x28];
PVOID SystemReserved2[0xA];
ULONG GdiRgn;
ULONG GdiPen;
ULONG GdiBrush;
CLIENT_ID RealClientId;
PVOID GdiCachedProcessHandle;
ULONG GdiClientPID;
ULONG GdiClientTID;
PVOID GdiThreadLocaleInfo;
PVOID UserReserved[5];
PVOID GlDispatchTable[0x118];
ULONG GlReserved1[0x1A];
PVOID GlReserved2;
PVOID GlSectionInfo;
PVOID GlSection;
PVOID GlTable;
PVOID GlCurrentRC;
PVOID GlContext;
NTSTATUS LastStatusValue;
UNICODE_STRING StaticUnicodeString;
WCHAR StaticUnicodeBuffer[0x105];
PVOID DeallocationStack;
PVOID TlsSlots[0x40];
LIST_ENTRY TlsLinks;
PVOID Vdm;
PVOID ReservedForNtRpc;
PVOID DbgSsReserved[0x2];
ULONG HardErrorDisabled;
PVOID Instrumentation[0x10];
PVOID WinSockData;
ULONG GdiBatchCount;
ULONG Spare2;
ULONG Spare3;
ULONG Spare4;
PVOID ReservedForOle;
ULONG WaitingOnLoaderLock;
PVOID StackCommit;
PVOID StackCommitMax;
PVOID StackReserved;
} TEB, *PTEB;
TIB的结构好像有,不占字了:-)
Windows在调入进程,创建线程时,操作系统均会为每个线程分配TEB,而且都将FS段选择器(i386)指向当
前线程的TEB数据(单CPU机器在任何时刻系统中只有一条线程在执行),那么FS:[0x30]指向PEB.
一个利用:在调试程序的时候,一般是把程序作为一个线程来运行的,(也可以有创建新进程来调试,呵呵)
那么FS:[0x30]必定不为空,如果主线程这个地方有值..
这是用note改的,当然问题可能会有很多,不要吝啬于题问题,不要让人跟着我误入歧途
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课