深入剖析PE文件
不赖猴的笔记,转载请注明出处。
深入剖析PE文件
PE文件是Win32的原生文件格式.每一个Win32可执行文件都遵循PE文件格式.对PE文件格式的了解可以加深你对Win32系统的深入理解.
一、 基本结构。
上图便是PE文件的基本结构。(注意:DOS MZ Header和部分PE header的大小是不变的;DOS stub部分的大小是可变的。)
一个PE文件至少需要两个Section,一个是存放代码,一个存放数据。NT上的PE文件基本上有9个预定义的Section。分别是:.text, .bss, .rdata, .data, .rsrc, .edata, .idata, .pdata, 和 .debug。一些PE文件中只需要其中的一部分Section.以下是通常的分类:
l 执行代码Section , 通常命名为: .text (MS) or CODE (Borland)
l 数据Section, 通常命名为:.data, .rdata, 或 .bss(MS) 或 DATA(Borland).
l 资源Section, 通常命名为:.edata
l 输入数据Section, 通常命名为:.idata
l 调试信息Section,通常命名为:.debug
这些只是命名方式,便于识别。通常与系统并无直接关系。通常,一个PE文件在磁盘上的映像跟内存中的基本一致。但并不是完全的拷贝。Windows加载器会决定加载哪些部分,哪些部分不需要加载。而且由于磁盘对齐与内存对齐的不一致,加载到内存的PE文件与磁盘上的PE文件各个部分的分布都会有差异。
当一个PE文件被加载到内存后,便是我们常说的模块(Module),其起始地址就是所谓的HModule.
二、 DOS头结构。
所有的PE文件都是以一个64字节的DOS头开始。这个DOS头只是为了兼容早期的DOS操作系统。这里不做详细讲解。只需要了解一下其中几个有用的数据。
1. e_magic:DOS头的标识,为4Dh和5Ah。分别为字母MZ。
2. e_lfanew:一个双字数据,为PE头的离文件头部的偏移量。Windows加载器通过它可以跳过DOS Stub部分直接找到PE头。
3. DOS头后跟一个DOS Stub数据,是链接器链接执行文件的时候加入的部分数据,一般是“This program must be run under Microsoft Windows”。这个可以通过修改链接器的设置来修改成自己定义的数据。
三、 PE头结构。
PE头的数据结构被定义为IMAGE_NT_HEADERS。包含三部分:
1. Signature:PE头的标识。双字结构。为50h, 45h, 00h, 00h. 即“PE”。
2. FileHeader:20字节的数据。包含了文件的物理层信息及文件属性。
这里主要注意三项。
l NumberOfSections:定义PE文件Section的个数。如果对PE文件新增或删除Section的话,一定要记的修改此域。
l SizeOfOptionalHeader:定义OptionHeader结构的大小。
l Characteristics:主要用来标识当前的PE文件是执行文件还是DLL。其各位都有具体的含义。
数据位 |
Windows.inc的预定义 |
为1时的含义 |
0 |
IMAGE_FILE_RELOCS_STRIPPED |
文件中不存在重定位信息 |
1 |
IMAGE_FILE_EXECUTABLE_IMAGE |
文件是可执行的 |
2 |
IMAGE_FILE_LINE_NUMS_STRIPPED |
不存在行信息 |
3 |
IMAGE_FILE_LOCAL_SYMS_STRIPPED |
不存在符号信息 |
7 |
IMAGE_FILE_BYTES_REVERSED_LO |
小尾方式 |
8 |
IMAGE_FILE_32BIT_MACHINE |
只在32位平台运行 |
9 |
IMAGE_FILE_DEBUG_STRIPPED |
不包含调试信息 |
10 |
IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP |
不能从可移动盘运行 |
11 |
IMAGE_FILE_NET_RUN_FROM_SWAP |
不能从网络运行 |
12 |
IMAGE_FILE_SYSTEM |
系统文件。不能直接运行 |
13 |
IMAGE_FILE_DLL |
DLL文件 |
14 |
IMAGE_FILE_UP_SYSTEM_ONLY |
文件不能在多处理器上运行 |
15 |
IMAGE_FILE_BYTES_REVERSED_HI |
大尾方式 |
3. OptionalHeader:总共224个字节。最后128个字节为数据目录(Data Directory)。
以下是字段的说明:
l AddressOfEntryPoint:程序入口点地址。但加载器要运行加载的PE文件时要执行的第一个指令的地址。它是一个RVA(相对虚拟地址)地址。一些对PE文件插入代码的程序就是修改此处的地址为要运行的代码,然后再跳转回此处原来的地址。
l ImageBase:PE文件被加载到内存的期望的基地址。对于EXE文件,通常加载后的地址就期望的地址。但是DLL却可能是其他的。因为如果这个地址被占,系统就会重新分配一块新的内存,同时会修改此处加载后的地址。EXE文件通常是400000h.
l SectionAlignment:每一个Section的内存对齐粒度。比如:此值为4096(1000h),那么每一个Section的起始地址都应该是4096(1000h)的整数倍。如果第一个Section的地址是401000h,大小为100个字节。那么下一个Section的起始地址为402000h.。两个Section之间的空间大部分是空的,未用的。
l FileAlignment:每一个Section的磁盘对齐粒度。比如,此值为512(200h),那么每一个Section在文件内的偏移位置都是512(200h)的整数倍。与SectionAlignment同理。
l SizeOfImage:PE文件在内存空间整个映像的大小。包含所有的头及按SectinAlignment对齐的所有的Section。
l SizeOfHeaders:所有的头加上Section表的大小。也就是文件大小减去文件中所有Section的大小。可以用这个值获取PE文件中第一Section的位置。
l DataDiretory:16个IMAGE_DATA_DIRECTORY结构的数组。每一个成员都对应一个重要的数据结构,比如输入表,输出表等。
有两个地方需要注意:
l 如果PE header里的最后两个字段被赋予一个伪造的值的话,比如:
n LoaderFlags = ABDBFFFFh (其默认值为0)
n NumberOfRvaAndSizes = DFFDEEEEh (其默认值为10h)
一些调试工具或反编译工具会认为这个PE文件是损坏的。有的会直接执行,如果是病毒的话,就会被直接感染;有的则会重启工具。所以最好在查看调试一个PE文件前,先看一下这里的取值是否被人赋予一个伪造的很大的值。如果是的话,先修改成默认的值。
l 有人可能注意到在一些PE文件(MS的链接器链接的PE文件)的DOS Stub部分跟PE header部分之间存在一部分垃圾数据。标识为其倒数第二非0的双字节是一个“Rich ”。这部分数据包含了一些加密数据,来标识编译这个PE文件的组件。可用来检举某些病毒程序所编译的程序来自哪台机器。
四、 数据目录结构(Data Directory)。
DataDirectory是OptionalHeader的最后128个字节,也是IMAGE_NT_HEADERS的最后一部分数据。它由16个IMAGE_DATA_DIRECTORY结构组成的数组构成。IMAGE_DATA_DIRECTORY的结构如下:
每一个IMAGE_DATA_DIRECTORY都是对应一个PE文件重要的数据结构。他们分别如下:
VirtualAddress指的是对应数据结构的RVA地址;iSize指的是对应数据结构的大小(字节单位)。一个PE文件一般只包含其中的一部分,也就是其中一部分数据结构是有数据的;另一部分则都是0。比如,EXE文件一般都存在IMAGE_DIRECTORY_ENTRY_IMPORT(输入表),而不存在IMAGE_DIRECTORY_ENTRY_EXPORT(输出表)。而DLL则两者都包含。下图就是某一个PE文件的数据目录:
五、 Section表。
Section表紧跟在PE header后面。由IMAGE_SECTION_HEADER数据结构组成的数组。每一个包含了对应Section在PE文件中的属性和偏移位置。
这里不是所有的成员都是有用的。
l Name1: 块名,这是一个8位ASCII码名,用来定义块名。多数块名以一个"."开始(如.text),尽管许多PE文档都认为这个"."实际上并不是必须的。值得注意的是,如果块名超过8位,则最后的NULL不存在。带有一个"$"的区块名字会从链接器那里得到特殊的对待,前面带"$"的相同名字的区块被合并,在合并后的区块中它们是按"$"后面的字符字母顺序进行合并的。
l Misc.VirtualSize : 指出实际的、被使用的区块大小。如果VirtualSize大于SizeOfRawData,那么SizeOfRawData来自于可执行文件初始化数据的大小,与VirtualSize相差的字节用0填充。这个字段在OBJ文件中设为0。
l VirtualAddress : 该块装载到内存中的RVA。这个地址是按照内存页对齐的,它的数值总是SectionAlignment的整数倍。在MS工具中,第一块的默认RVA为1000H.在OBJ中,该字段没意义。如果该值为1000H, PE文件被加载到400000H,那么该Section的起始地址为401000H。
l SizeOfRawData : 该块在磁盘文件中所占的大小。在可执行文件中,这个值必须是PE头部指定的文件对齐大小的倍数。如果是0,则说明区块中的数据是未初始化的。该块在磁盘文件中所占的大小,这个数值等于VirtualSize字段的值按照FileAlignment的值对齐以后的大小。例如,FileAlignment的大小为1000H,如果VirtualSize中的块长度为2911,则SizeOfRawData为3000H}
l PointerToRawData : 该块在磁盘文件中的偏移。对于可执行文件,这个值必须是PE头部指定的文件对齐大小的倍数。
l PointerToRelocations : 这部分在EXE文件中无意义。在OBJ文件中,表示本块重定位信息的偏移量。在OBJ文件中如果不是零,则会指向一个IMAGE_RELOCATION的数据结构。
l NumberOfRelocations : 由PointerToRelocations指向的重定位的数目。
l NumberOfLinenumbers : 由NumberOfRelocations指向的行号的数目,只在COFF样式的行号被指定时使用。
l Characteristics : 块属性,该字段是一组指出块属性(如代码/数据/可读/可写等)的标志。多个标志值通过OR操作形成Characteristics的值。这些标志很多都可以通过链接器/SECTION选项设置。
位 |
数据位在Windows.inc中的预定义 |
为1时的含义 |
5 |
IMAGE_SCN_CNT_CODE (00000020H) |
节中包含代码 |
6 |
IMAGE_SCN_CNT_INITIALIZED_DATA (00000040H) |
节中包含已初始化数据 |
7 |
IMAGE_SCN_CNT_UNINITIALIZED_DATA (00000080H) |
节中包含未初始化数据 |
25 |
IMAGE_SCN_MEM_DISCARDABLE (02000000H) |
节中的数据在进程开始后将被丢弃 |
26 |
IMAGE_SCN_MEM_NOT_CACHED (04000000H) |
节中的数据不会经过缓存 |
27 |
IMAGE_SCN_MEM_NOT_PAGED (08000000H) |
节中的数据不会被交换到磁盘 |
28 |
IMAGE_SCN_MEM_SHARED (10000000H) |
节中的数据将被不同的进程所共享 |
29 |
IMAGE_SCN_MEM_EXECUTE (20000000H) |
映射到内存后的页面包含可执行属性 |
30 |
IMAGE_SCN_MEM_READ (40000000H) |
映射到内存后的页面包含可读属性 |
31 |
IMAGE_SCN_MEM_WRITE (80000000H) |
映射到内存后的页面包含可写属性 |
六、 PE文件各个Section。
PE文件的Sections部分包含了文件的内容。包括代码,数据,资源和其他可执行信息。每一个Section由一个头部和一个数据部分组成。所有的头部都存放在紧跟PE header后的Section表内。
1. 执行代码。
在NT Windows系统内,所有的PE文件的代码段都存放在一个Section内,通常命名为.text(MS)或CODE(Borland)。这一段包含了早先提起的AddressOfEntryPoint多指地址的指令及输入表中的jump thunk table。
2. 数据。
l .bss段存放未初始化的数据,包括函数内或源模块内声明的静态变量。
l .rdata段存放只读数据,比如常字符串,常量,调试指示信息。
l .data 段存放其他所有的数据(除了自动化变量,其存放在栈中)。比如程序的全局变量。
3. 资源。
.rsrc段包含了一个模块的资源信息。以资源树的结构存放数据。需要用工具来查看。
4. 输出数据。
.edata段包含了PE文件的输出目录(Export Directory)。
5. 输入数据。
.idata包含了PE文件的输入目录和输入地址表。
6. 调试信息。
调试信息存放在.debug段。PE文件也支持单独的调试文件。Debug段包含调试信息,但是调试目录却存放在.rdata内。
7. 线程局部存储。(TLS)
Windows支持每一个进程包含多个线程。每一个线程有其私有的存储空间(TLS)去存放线程自身的数据。链接器都会为进程创建一个.tls段来存放TLS模板。当进程创建一个线程时,系统就会按照这个模板创建一个线程私有的局部存储空间。
8. 基重定位。
当加载器加载PE文件到内存的时候,有时候不一定是其预期的基地址。那么就需要调整内部指令的相对地址。所有需要调整的地址都存放在.reloc段内。
七、输出Section.
这个Section跟DLL关系比较密切。DLL一般定义两种函数,内部使用的,和输出到外部给其他调用程序使用的。输出到外部的函数就存储在这个Section内。
DLL输出函数分两种方式,通过名称和通过序号输出。当其他程序需要调用DLL的时候,调用GetProcAddress,通过设置需要调用的函数名称或函数序号可以调用DLL内部输出的函数。
那么GetProcAddress是怎么获取DLL中真正的输出函数地址呢?以下是详细的解说。
PE头的数据目录(DATA DIRECTORY)数组的第一个成员对应的(通过其中的RVA地址可获得)数据结构是IMAGE_EXPORT_DIRECTORY(这里称为输出目录)。
成员 |
大小 |
描述 |
Characteristics |
DWORD |
未定义,总是0 |
TimeDateStamp |
DWORD |
输出表的创建时间。与IMAGE_NT_HEADER.FileHeader.TimeDateStamp有相同的定义 |
MajorVersion |
WORD |
输出表的主版本号。未使用,为0 |
MinorVersion |
DWORD |
输出表的次版本号。未使用,为0 |
nName |
DWORD |
指向一个ASCII字符串的RVA,这个字符串是与这些输出函数关联的DLL的名称(比如,Kernel32.dll)。这个值必须定义,因为如果DLL文件的名称如果被修改,加载器将使用这里的名称。 |
nBase |
DWORD |
这个字段包含用于这个可执行文件输出表的起始序数值(基数)。正常情况下为1,但不是一定是。当通过序数来查询一个输出函数时,这个值会被从序数里减去。(比如,如果nBase = 1,被查询的函数的序数是3,那么这个函数在序号表的索引是3 -1 = 2)。 |
NumberOfFunctions |
DWORD |
输出地址表(EAT)的条目数。其中一些条目可能是0,意味着这个序数值没有代码和数据输出。 |
NumberOfNames |
DWORD |
输出名称表(ENT)的条目数。这个值总是大于或等于NumberOfFunctions。小于的情况发生在符号只通过序数来输出时。另外,当被赋值的序数里有数字间隔时也会有小于的情况。这个值也是输出序数表的长度。 |
AddressOfFunctions |
DWORD |
输出地址表(EAT)的RVA。输出地址表本身是一个RVA数组,数组中的每一个非零的RVA都对应一个被输出的符号。 |
AddressOfNames |
DWORD |
输出名称表(ENT)的RVA。输出名称表本身是一个RVA数组。数组中的每一个非零的RVA都向一个ASCII字符串。每一个字符串都对应一个通过名称输出的符号。这个表是排序。这允许加栽器在查询一个被输出的符号时可用二进制查找方式。名称的排序是二进制的,而不是按字母。 |
AddressOfNameOrdinals |
DWORD |
输出序数表(EOT)的RVA。这个表将ENT中的数组索引映射到相应的输出地址条目。 |
实际上,IMAGE_EXPORT_DIRECTORY结构指向三个数组和一个ASCII字符串表。其中重要的是输出地址表(EAT,即AddressOfFunctions指向的表), 输出函数地址指针(RVA)构成了这个表。而ENT和EOT则是可以一起合作来获取EAT里对应的地址数据。下图演示了这个过程。
这个被加载的DLL的名称是F00.DLL。总共输出了四个函数,其RVA地址分别为0x400042、0x400156、0x401256和0x400520。一个外部调用程序需要调用其中一个名为”Bar”的函数,那么它先在输出名称表(ENT)里查找名称为Bar的函数,找到后,根据其在输出序号表(EOT)中对应的索引号,获取其中的数值为EAT中的索引值,这里是4,然后从EAT中根据索引4获取其真正的RVA地址0x400520。以下是几个注意点:
l 输出序号表(EOT)的存在就是为了是EAT跟ENT之间产生关联。每一个ENT内的成员(函数名)有且只有一个EAT内的成员(函数地址)对应。但是一个EAT内的成员并不是只有一个ENT内的成员对应。比如,有的函数存在别名的话,就会出现多个ENT内的成员都对应一个EAT内的成员。
l 如果已经获得一个函数的序号值,那么就可以直接到EAT内获得其RVA地址,而不需要经过ENT和EOT进行查找。但是这样的按序号输出的DLL不易于维护。
l 通常情况下,EAT的个数(NumberOfFunctions)必须小于或等于ENT的个数(NumberOfNames)。只有在一个函数按序号输出时(其在ENT和EOT表里没有对应的数据),ENT的数量才有可能少于EAT的数量。比如,总共有70个函数输出,但是在ENT表里只有40个,这就意味着剩余的30个函数是靠序号输出的。那么我们如何知道哪些是直接靠序号输出的呢?只有通过排除法来获得。把存在在EOT表里的序号从EAT里排除出去,剩下的就是靠序号输出的函数。
l 当通过一个序号值来获取EAT内的函数RVA时,需要把这个序号值减去nBase的值来获取在EAT表里真正的索引位置。而通过名称查找则不需要这么做。
l 输出转向。某些时候,你从一个DLL中调用的一个函数可能位于另一个DLL中。这就叫输出转向。比如,Kernel32.dll中的HeapAlloc就是转到调用NTDLL.dll中的RtlAllocHeap。这种转向是在链接的时候,在.DEF文件中定义一个特殊的指令来实现的。那么当一个函数被转向后,在其所在EAT表里对应的数据便不是其地址,而是一个指向表明被转向的DLL和函数的ASCII字符串的地址指针。
上图就是Kernel32.dll的输出函数表,其中HeapAlloc的RVA值0x00009048就是一个指向“NTDLL.RtlAllocHeap”的指针。
八 、 输入Section.
输入Section通常位于.idata段内。它包含了所有程序需要用到的来自其他DLL的函数的信息。Windows加载器负责加载所有程序用到的DLL到进程空间。然后为进程找到所有其需要用到的函数的地址。下面描述这个过程:
成员 | 大小 | 描述 |
OriginalFirstThunk | DWORD | 指向输入名称表(INT)的RVA。INT是由IMAGE_THUNK_DATA数据结构构成的数组。数组中的每一个成员定义了一个输入函数的信息,数组最后以一个内容为0的IMAGE_THUNK_DATA结束。 |
TimeDateStamp | DWORD | 当执行文件不与被输入的DLL进行绑定时,这个字段为0。当以旧的方式绑定时,这个字段包括时间/日期。当以新的样式绑定时,这个字段为-1。 |
ForwarderChain | DWORD | 这是第一个被转向的API的索引。老样式绑定的定义。 |
Name | DWORD | 指向被输入DLL的ASCII字符串的RVA。 |
FirstThunk | DWORD | 指向输入地址表(IAT)的RVA。IAT也是一个IMAGE_THUNK_DATA数据结构的数组。 |
成员 | 大小 | 描述 |
TimeDateStamp | DWORD | 必须与被输入的DLL的PE头内的TimeDateStamp一样,如果不一致,那么加载器就会认为绑定的对象有误,需要重新修补输入表。 |
OffsetModuleName | WORD | 第一个IMAGE_BOUND_IMPORT_DESCRIPTOR结构到被输入DLL名称的偏移(非RVA)。 |
NumberOfModuleForwarderRefs | WORD | 包含紧跟在这个结构后面IMAGE_BOUND_FORWARDER_REF的数目。 |
ImgDelayDescr = packed record
grAttrs: DWORD;
szName: DWORD;
phmod: PDWORD;
pIAT: TImageThunkData32;
pINT: TImageThunkData32;
pBoundIAT: TImageThunkData32;
pUnloadIAT: TImageThunkData32;
dwTimeStamp: DWORD;
end;
成员 | 描述 |
grAttrs | 设为1的时候,下面的各个成员都是RVA,否则是VA(虚拟地址)。 |
szName | 指向一个DLL名称的RVA。 |
phmod | 指向一个HMODULE的RVA。 |
pIAT | 指向DLL的IAT的RVA。 |
pINT | 指向DLL的INT的RVA。 |
pBoundIAT | 可选的绑定IAT的RVA。 |
pUnloadIAT | 指向DLL的IAT的未绑定拷贝 |
dwTimeStamp | 延迟装载的输入DLL的时间/日期。通常是0。 |
九、 Windows加载器
- 把整个IID数组移到一个有足够空间来增加一个新的IID的地方。这个地方可以是.idata段的末尾或是新增一个Section来存放。
- 修改数据目录数组对应输入表的数据结构IMAGE_DATA_DIRECTORY的RVA和iSize。
- 如果必要,将存放新IID数组的Section大小按照Section Alignment向上取整(比如,原来大小是1500h, 而section Alignment为1000h,则调整为2000h)以便于整个段可以被映射到内存。
- 运行移动过IID数组的执行文件,如果正常的话,则进行第二步骤。如果不工作的话,需要检查新增的IID是否已经被映射到内存及IID数组新的偏移位置是否正确。
- 在.idata节内增加两个以null结尾的字符串,一个用来存放新增的DLL的名字。 一个用来存放需要导入的API的名称。这个字符串前需要增加一个为null的WORD字段来构成一个 Image_Import_By_Name数据结构。
- 计算这个新增的DLL名称字符串的RVA.
- 把这个RVA赋予新增的IID的Name1字段。
- 再找到一个DWORD的空间,来存放Image_Import_by_name的RVA。这个RVA就是新增DLL的IAT表。
- 计算上面DWORD空间的RVA,将其赋予新增IID的FirstThunk字段。
- 运行修改完的程序。
http://blog.csdn.net/suiyunonghen/article/details/3860171
深入剖析PE文件的更多相关文章
- 获取pe文件的文件类型
工程文件petype.cpp通过调用pefile类中的函数获取文件类型. 文件类型的判断通过5个监测点完成. 监测点1:dos头的e_magic 监测点2:nt头的Signature 监测点3:文件头 ...
- 浅析MSIL中间语言——PE文件结构篇
一.开篇 开篇我想讲一下于本文无关的话题,其实我很想美化一下自己博客园一直没时间弄,无意间找了博客园李宝亨的博客园里面有一篇分享自己主题的文章,我就将这个模板暂时用作我的blog主题,我要讲述一个关于 ...
- PE文件学习系列笔记四-C++实现PE文件的分析
合肥程序员群:49313181. 合肥实名程序员群:128131462 (不愿透露姓名和信息者勿加入) Q Q:408365330 E-Mail:egojit@qq.com 综述: 首 ...
- PE文件学习系列三-PE头详解
合肥程序员群:49313181. 合肥实名程序员群:128131462 (不愿透露姓名和信息者勿加入) Q Q:408365330 E-Mail:egojit@qq.com 最近比较忙 ...
- PE文件学习系列二 DOS头分析
合肥程序员群:49313181. 合肥实名程序员群 :128131462 (不愿透露姓名和信息者勿加入)Q Q:408365330 E-Mail:egojit@qq.com PE文件结 ...
- C语言读取PE文件信息(一)
接下来的内容来源于对该博客文章http://www.pediy.com/kssd/pediy06/pediy7006.htm的解析. 一.打印Sections信息.下面的程序打印出Windows_Gr ...
- PE文件的执行顺序
当一个PE文件被执行时,PE装载器首先检查DOS MZ header里的PE header的偏移量.如果找到,则直接跳转到PE header的位置. 当PE装载器跳转到PE header后,第二步要做 ...
- PE文件学习系列一为什么是PE
合肥程序员群:49313181. 合肥实名程序员群:128131462 (不愿透露姓名和信息者勿加入)Q Q:408365330 E-Mail:egojit@qq.com PE概述: ...
- 【C#进阶系列】02 PE文件,程序集,托管模块,元数据——还是那个Hello world
好了,还是这张图,还是一样的Hello world. 因为本章其实很多都是讲一些命令行编译啊什么鬼的配置类的东西,要用的时候直接百度或者回头查书就可以了, 所以了解一下也就行了,也没有记录下来,接下来 ...
随机推荐
- poj2478--欧拉函数打表
此题中对时间有要求,如直接使用欧拉函数求解,每输入一个n,就得进行循环求出<n的每个数的欧拉函数, 这样会超时, 于是我们可预先将欧拉函数打表, 再进行一个循环加法运算,便可不超时得解. #in ...
- Tomcat启动失败的解决方法
在使用Tomcat的时候,经常会遇到启动失败的问题:解决方法:1.检查环境变量的配置,jdk的配置2.检查端口是否被占用. 关于环境变量的配置很容易搜到,如果按照网上的教程配置好了,但是还是启动失败的 ...
- 浮动层固定兼容IE6 position:fixed的最佳解决方案
第一种:css方法 有时候当我们需要把一个元素固定在页面的某个部位,一般都是用css中的“position:fixed;”方法来解决,但是IE6不支持fixed,所以今天分享一个兼容IE6的页面底部固 ...
- retain、strong、weak、assign区别
1. 假设你用malloc分配了一块内存,并且把它的地址赋值给了指针a,后来你希望指针b也共享这块内存,于是你又把a赋值给(assign)了b.此时a 和b指向同一块内存,请问当a不再需要这块内存,能 ...
- JAVA工具_PinyinConv
package cn.chh.utils; /** * 获得每个字符的首字母 * @author CHH * @since 2013-01-21 * @bugs 不支持多音字处理 */ public ...
- APK反编译。
http://blog.csdn.net/ithomer/article/details/6727581?reload
- JavaEE Tutorials (19) - Web应用安全入门
19.1Web应用安全概述29519.2保护Web应用安全296 19.2.1指定安全约束297 19.2.2指定认证机制300 19.2.3在部署描述文件中指定认证机制302 19.2.4声明安全角 ...
- 汉诺塔III 汉诺塔IV 汉诺塔V (规律)
汉诺塔III Time Limit: 1000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Subm ...
- 魔兽世界---屠夫(Just a Hook)
Just a Hook Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Tota ...
- 黑马程序员_<<String类>>
--------------------ASP.Net+Android+IOS开发..Net培训.期待与您交流! -------------------- 1. String类 1.概述 String ...