一、前言

  堆对于开发者一般来说是熟悉又陌生的,熟悉是因为我们常常使用new/delete或者malloc/free使用堆,陌生是因为我们基本没有去了解堆的结构。堆在什么地方?怎么申请?怎么释放?系统又是怎么管理堆的呢?

  带着疑问,这两天看了<软件漏洞分析技术>与<漏洞战争>中关于堆的说明,终于对于堆有一点点的了解了。这里记录一下在学习和调试中的一点笔记。

二、关于堆的基本知识

  1).首先了解空闲双向链表和快速单向链表的概念

  1.空闲双向链表(空表)

  空闲堆块的块首中包含一对重要的指针,这对指针用于将空闲堆块组织成双向链表。按照堆块的大小不同,空表总共被分为128条。

  堆区一开始的堆表区中有一个128项的指针数组,被称作空表索引(Freelist array)。该数组的每一项包括两个指针,用于标识一条空表。

  如图所示,空表索引的第二项(free[1])标识了堆中所有大小为8字节的空闲堆块。之后每个索引项指示的空闲堆块递增8字节。例如free[2]为16字节的空闲堆块,free[3]为24字节的空闲堆块,free[127]为1016字节的空闲堆块。
        空闲堆块的大小 = 索引项(ID) x 8(字节)
  把空闲堆块按照大小的不同链入不同的空表,可以方便堆管理系统高效检索指定大小的空闲堆块。需要注意的是,空表索引的第一项(free[0])所标识的空表相对比较特殊。这条双向链表链入了所有大于等于1024字节的堆块(小于512KB),升序排列。

  

  2.快速单项链表(快表)

  快表是Windows用来加速堆块分配而采用的一种堆表。这里之所以叫做"快表"是因为这类单项链表中从来不会发生堆块合并(其中的空闲块块首被设置为占用态,用来防止堆块合并)

  快表也有128条,组织结构与空表类似,只是其中的堆块按照单项链表组织。

  快表总是被初始化为空,而且每条快表最多只有4个结点,故很快就会被填满。

  

  2)堆块的结构

  堆块分为块首和块身,实际上,我们使用函数申请得到的地址指针都会越过8字节(32位系统)的块首,直接指向数据区(块身)。堆块的大小包括块首在内的,如果申请32字节,实际会分配40字节,8字节的块首+32字节的块身。同时堆块的单位是8字节,不足8字节按8字节分配。堆块分为占用态和空闲态。

  其中空闲态结构为:

  占用态结构为

  空闲态将块首后8个字节用于存放空表指针了。

  在64位系统,块首大小为16字节,按16字节对齐。

  

  3)堆块的分配和释放

  1.堆块分配

  堆块分配可以分为三类:快表分配、普通空表分配和零号空表(free[0]分配)。

  从快表中分配堆块比较简单,包括寻找到大小匹配的空闲堆块、将其状态修改为占用态、把它从堆表中"卸下"、最后返回一个指向堆块快身的指针给程序使用。
  普通空表分配时首先寻找最优的空闲块分配,若失败,则寻找次优的空闲块分配,即最小的能满足要求的空闲块。
  零号空表中按照大小升序链着大小不同的空闲块,故在分配时先从free[0]反向查找最后一个块(即最大块),看能否满足要求,如果满足要求,再正向搜索最小能满足要求的空闲堆块进行分配。
  当空表中无法找到匹配的"最优"堆块时,一个稍大些的块会被用于分配,这种次优分配发生时,会先从大块中按请求的大小精确地"割"出一块进行分配,然后给剩下的部分重新标注块首,链入空表。
  由于快表只有在精确匹配才会分配,所以不存在上述现象。

  

  2.堆块的释放
  释放堆块的操作包括将堆块状态改为空闲,链入相应的堆表。所有的释放块都链入堆表的末尾,分配的时候也先从堆表末尾拿。
  另外需要强调,快表最多只有4项。

  3.堆块的分配和释放

  在具体进行堆块分配和释放时,根据操作内存大小不同,Windows采取的策略也会有所不同。可以把内存按照大小分为三类:

      小块:Size < 1KB
      大块:1KB < Size < 512KB
      巨块:Size >= 512KB

                            分配  释放
 小块

首先进行快表分配
   若快表分配失败,进行普通空表分配
   若普通空表分配失败,使用堆缓存分配
   若堆缓存分配失败,尝试零号空表分配(freelist[0])
   若零号空表分配失败,进行内存紧缩后在尝试分配
   若仍无法分配,返回NULL

优先链入快表(只能链入4个空闲块)
如果快表满,则将其链入相应的空表

 大块

首先使用堆缓存进行分配
   若堆缓存分配失败,使用free[0]中的大块进行分配

优先将其放入堆缓存
若堆缓存满,将链入freelist[0]

 巨块

一般来说巨块申请非常罕见,要用到虚分配方法(实际上并不是从堆区分配的)
   这种类型的堆块在堆溢出利用中几乎不会遇到

 直接释放,没有堆表操作

  

  在分配的过程中需要注意的几点是:

  (1)快表中的空闲块被设置为占用态,故不会发生堆块合并操作,且只能精确匹配时才会分配。

  (2)快表是单链表,操作比双链表简单,插入删除都少用很多指令

  (3)快表只有4项,很容易被填满,因此空表也是被频繁使用的

 三、调试堆在PEB中的数据结构

  1)完成C代码,在x64下编译为Release版本,运行

#include "stdafx.h"
#include <Windows.h>
#include <iostream>
using namespace std; extern "C" PVOID64 _cdecl GetPebx64();
int _tmain(int argc, _TCHAR* argv[])
{ PVOID64 Peb = ;
Peb = GetPebx64();
printf("Peb is 0x%p\r\n",Peb); HANDLE hHeap;
char *heap;
char str[] = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"; //0x20 hHeap = HeapCreate(HEAP_GENERATE_EXCEPTIONS,0x1000,0xffff);
getchar(); //用于暂停,便于调试器附加 heap = (char*)HeapAlloc(hHeap,,0x20);
printf("Heap addr:0x%08p\r\n",heap); strcpy(heap,str);
printf("str is %s\r\n",heap); cin>>Peb;
HeapFree(hHeap,,heap); //释放
HeapDestroy(hHeap); cin>>Peb;
return ;
}

  其中GetPebx64()函数为使用.asm文件的汇编,通过gs:[0x60]获得

.CODE
GetPebx64 PROC
mov rax,gs:[60h]
ret
GetPebx64 ENDP
END

  运行结果为

  我们使用Windbg附加,查看PEB结构

:> dt _PEB 0x000007FFFFFDB000
ntdll!_PEB
+0x000 InheritedAddressSpace : ''
+0x001 ReadImageFileExecOptions : ''
+0x002 BeingDebugged : 0x1 ''
+0x003 BitField : 0x8 ''
+0x003 ImageUsesLargePages : 0y0
+0x003 IsProtectedProcess : 0y0
+0x003 IsLegacyProcess : 0y0
+0x003 IsImageDynamicallyRelocated : 0y1
+0x003 SkipPatchingUser32Forwarders : 0y0
+0x003 SpareBits : 0y000
+0x008 Mutant : 0xffffffff`ffffffff Void
+0x010 ImageBaseAddress : 0x00000001`3f050000 Void
+0x018 Ldr : 0x00000000` _PEB_LDR_DATA
+0x020 ProcessParameters : 0x00000000` _RTL_USER_PROCESS_PARAMETERS
+0x028 SubSystemData : (null)
+0x030 ProcessHeap : 0x00000000` Void //进程默认堆的地址
+0x038 FastPebLock : 0x00000000`7752a960 _RTL_CRITICAL_SECTION
+0x040 AtlThunkSListPtr : (null)
+0x048 IFEOKey : (null)
+0x050 CrossProcessFlags :
+0x050 ProcessInJob : 0y0
+0x050 ProcessInitializing : 0y0
+0x050 ProcessUsingVEH : 0y0
+0x050 ProcessUsingVCH : 0y0
+0x050 ProcessUsingFTH : 0y0
+0x050 ReservedBits0 : 0y000000000000000000000000000 ()
+0x058 KernelCallbackTable : (null)
+0x058 UserSharedInfoPtr : (null)
+0x060 SystemReserved : []
+0x064 AtlThunkSListPtr32 :
+0x068 ApiSetMap : 0x000007fe`ff710000 Void
+0x070 TlsExpansionCounter :
+0x078 TlsBitmap : 0x00000000` Void
+0x080 TlsBitmapBits : [] 0x11
+0x088 ReadOnlySharedMemoryBase : 0x00000000`7efe0000 Void
+0x090 HotpatchInformation : (null)
+0x098 ReadOnlyStaticServerData : 0x00000000`7efe0a90 -> (null)
+0x0a0 AnsiCodePageData : 0x000007ff`fffa0000 Void
+0x0a8 OemCodePageData : 0x000007ff`fffa0000 Void
+0x0b0 UnicodeCaseTableData : 0x000007ff`fffd0028 Void
+0x0b8 NumberOfProcessors :
+0x0bc NtGlobalFlag :
+0x0c0 CriticalSectionTimeout : _LARGE_INTEGER 0xffffe86d`079b8000
+0x0c8 HeapSegmentReserve : 0x100000 //堆的默认保留大小
+0x0d0 HeapSegmentCommit : 0x2000 //堆的默认提交大小
+0x0d8 HeapDeCommitTotalFreeThreshold : 0x10000 //解除提交的总空闲块阈值
+0x0e0 HeapDeCommitFreeBlockThreshold : 0x1000 //解除提交的单块阈值
+0x0e8 NumberOfHeaps : 5 //进程堆的数量
+0x0ec MaximumNumberOfHeaps : 0x10 //ProcessHeaps数组目前的大小
+0x0f0 ProcessHeaps : 0x00000000`7752a6c0 -> 0x00000000` Void //一个数组,记录了每一个堆的地址
+0x0f8 GdiSharedHandleTable : (null)
+0x100 ProcessStarterHelper : (null)
+0x108 GdiDCAttributeList :
+0x110 LoaderLock : 0x00000000` _RTL_CRITICAL_SECTION
+0x118 OSMajorVersion :
+0x11c OSMinorVersion :
+0x120 OSBuildNumber : 0x1db1
+0x122 OSCSDVersion : 0x100
+0x124 OSPlatformId :
+0x128 ImageSubsystem :
+0x12c ImageSubsystemMajorVersion :
+0x130 ImageSubsystemMinorVersion :
+0x138 ActiveProcessAffinityMask : 0xf
+0x140 GdiHandleBuffer : []
+0x230 PostProcessInitRoutine : (null)
+0x238 TlsExpansionBitmap : 0x00000000` Void
+0x240 TlsExpansionBitmapBits : []
+0x2c0 SessionId :
+0x2c8 AppCompatFlags : _ULARGE_INTEGER 0x0
+0x2d0 AppCompatFlagsUser : _ULARGE_INTEGER 0x0
+0x2d8 pShimData : (null)
+0x2e0 AppCompatInfo : (null)
+0x2e8 CSDVersion : _UNICODE_STRING "Service Pack 1"
+0x2f8 ActivationContextData : 0x00000000` _ACTIVATION_CONTEXT_DATA
+0x300 ProcessAssemblyStorageMap : (null)
+0x308 SystemDefaultActivationContextData : 0x00000000` _ACTIVATION_CONTEXT_DATA
+0x310 SystemAssemblyStorageMap : (null)
+0x318 MinimumStackCommit :
+0x320 FlsCallback : 0x00000000`0027fe90 _FLS_CALLBACK_INFO
+0x328 FlsListHead : _LIST_ENTRY [ 0x00000000`0027fa70 - 0x00000000`0027fa70 ]
+0x338 FlsBitmap : 0x00000000` Void
+0x340 FlsBitmapBits : []
+0x350 FlsHighIndex :
+0x358 WerRegistrationData : (null)
+0x360 WerShipAssertPtr : (null)
+0x368 pContextData : 0x00000000` Void
+0x370 pImageHeaderHash : (null)
+0x378 TracingFlags :
+0x378 HeapTracingEnabled : 0y0
+0x378 CritSecTracingEnabled : 0y0
+0x378 SpareTracingBits : 0y000000000000000000000000000000 ()

  我们可以使用dd 0x00000000`7752a6c0查看进程堆的地址

  也可以使用!heap -h 命令查看进程堆的地址和分配的大小

  而我们可以看到运行结果中分配的地址4A0A90,正好在段4A0000中。

  2)堆的相关结构

  我们首先了解下面几个结构_HEAP_ENTRY,_HEAP_SEGMENT,_HEAP。

  1._HEAP_ENTRY就是块首,下面是一个64位系统堆块的结构,我们在申请得到的地址减去0x10,就可以得到HEAP_ENTRY的首地址。

                  

   2._HEAP_SEGMENT是段结构,我们可以这么认为,堆申请内存的大小是以段为单位的,当新建一个堆的时候,系统会默认为这个堆分配一个段叫0号段,通过刚开始的new和malloc分配的空间都是在这个段上分配的,当这个段用完的时候,如果当初创建堆的时候指明了HEAP_GROWABLE这个标志,那么系统会为这个堆在再分配一个段,这个时候新分配的段就称为1号段了,以下以此类推。每个段的开始初便是HEAP_SEGMENT结构的首地址,由于这个结构也是申请的一块内存,所以它前面也会有个HEAP_ENTRY结构: 

  我们使用Windbg查看HEAP_SEGMENT结构如下:

ntdll!_HEAP_SEGMENT
+0x000 Entry : _HEAP_ENTRY
+0x010 SegmentSignature : Uint4B
+0x014 SegmentFlags : Uint4B
+0x018 SegmentListEntry : _LIST_ENTRY
+0x028 Heap : Ptr64 _HEAP   //段所属的堆
+0x030 BaseAddress : Ptr64 Void //段的基地址
+0x038 NumberOfPages : Uint4B      //段的内存页数
+0x040 FirstEntry : Ptr64 _HEAP_ENTRY  //第一个堆块(HEAP_ENTRY指针,堆块一般位于HEAP_SEGMENT后面)
+0x048 LastValidEntry : Ptr64 _HEAP_ENTRY  //堆块的边界值
+0x050 NumberOfUnCommittedPages : Uint4B    //尚未提交的内存页数
+0x054 NumberOfUnCommittedRanges : Uint4B    //UnCommittedRanges数组元素数
+0x058 SegmentAllocatorBackTraceIndex : Uint2B
+0x05a Reserved : Uint2B
+0x060 UCRSegmentList : _LIST_ENTRY

  

  3._HEAP结构

  HEAP结构则是记录了这个堆的信息,这个结构可以找到HEAP_SEGMENT链表入口,空闲内存链表的入口,内存分配粒度等等信息。HEAP的首地址便是堆句柄的值,但是堆句柄的值又是0号段的首地址也是堆句柄,何解?其实很简单,0号段的HEAP_SEGMENT就在HEAP结构里面,HEAP结构类定义如这样:

:> dt ntdll!_HEAP 4a0000
+0x000 Entry : _HEAP_ENTRY
+0x010 SegmentSignature : 0xffeeffee
+0x014 SegmentFlags : 0
+0x018 SegmentListEntry : _LIST_ENTRY [ 0x00000000`004a0128 - 0x00000000`004a0128 ]
+0x028 Heap : 0x00000000`004a0000 _HEAP
+0x030 BaseAddress : 0x00000000`004a0000 Void
+0x038 NumberOfPages : 0x10
+0x040 FirstEntry : 0x00000000`004a0a80 _HEAP_ENTRY
+0x048 LastValidEntry : 0x00000000`004b0000 _HEAP_ENTRY
+0x050 NumberOfUnCommittedPages : 0xe
+0x054 NumberOfUnCommittedRanges : 1
+0x058 SegmentAllocatorBackTraceIndex : 0
+0x05a Reserved : 0
+0x060 UCRSegmentList : _LIST_ENTRY [ 0x00000000`004a1fe0 - 0x00000000`004a1fe0 ]
+0x070 Flags : 0x1004 //堆标志
+0x074 ForceFlags : 4 //强制标志
+0x078 CompatibilityFlags : 0
+0x07c EncodeFlagMask : 0x100000
+0x080 Encoding : _HEAP_ENTRY
+0x090 PointerKey : 0x5c50b3ba`3fc7668b
+0x098 Interceptor :
+0x09c VirtualMemoryThreshold : 0xff00 //最大堆块大小
+0x0a0 Signature : 0xeeffeeff //HEAP结构的签名
+0x0a8 SegmentReserve : 0x100000 //段的保留空间大小
+0x0b0 SegmentCommit : 0x2000 //每次提交内存的大小
+0x0b8 DeCommitFreeBlockThreshold : 0x100 //解除提交的单块阈值
+0x0c0 DeCommitTotalFreeThreshold : 0x1000 //解除提交的总空闲块阈值
+0x0c8 TotalFreeSize : 0x151 //空闲块的总大小
+0x0d0 MaximumAllocationSize : 0x000007ff`fffdefff //可分配的最大值
+0x0d8 ProcessHeapsListIndex : 5 //本堆在进程堆列表中的索引
+0x0da HeaderValidateLength : 0x208 //头结构的验证长度
+0x0e0 HeaderValidateCopy : (null)
+0x0e8 NextAvailableTagIndex : 0 //下一个可用的堆块标记索引
+0x0ea MaximumTagIndex : 0 //最大的堆块标记索引
+0x0f0 TagEntries : (null) //指向用于标记堆块的标记结构
+0x0f8 UCRList : _LIST_ENTRY [ 0x00000000`004a1fd0 - 0x00000000`004a1fd0 ] //UnCommitedRange Segments
+0x108 AlignRound : 0x1f
+0x110 AlignMask : 0xffffffff`fffffff0 //用于地址对齐的掩码
+0x118 VirtualAllocdBlocks : _LIST_ENTRY [ 0x00000000`004a0118 - 0x00000000`004a0118 ]
+0x128 SegmentList : _LIST_ENTRY [ 0x00000000`004a0018 - 0x00000000`004a0018 ] //段链表HEAP_SEGMENT
+0x138 AllocatorBackTraceIndex :
+0x13c NonDedicatedListLength : 0    //用于记录回溯信息
+0x140 BlocksIndex : 0x00000000`004a0230 Void
+0x148 UCRIndex : (null)
+0x150 PseudoTagEntries : (null)
+0x158 FreeLists : _LIST_ENTRY [ 0x00000000`004a0ac0 - 0x00000000`004a0ac0 ] //空闲块链表数组
+0x168 LockVariable : 0x00000000`004a0208 _HEAP_LOCK  //用于串行化控制的同步对象
+0x170 CommitRoutine : 0x5c50b3ba`3fc7668b long +5c50b3ba3fc7668b
+0x178 FrontEndHeap : (null)     //用于快速释放堆块的"前端堆"
+0x180 FrontHeapLockCount : 0      //"前端堆"的锁定计数
+0x182 FrontEndHeapType : ''      //"前端堆"的类型
+0x188 Counters : _HEAP_COUNTERS
+0x1f8 TuningParameters : _HEAP_TUNING_PARAMETERS

  对比一下上面HEAP_SEGMENT的结构,可以发现HEAP中就包含一个HEAP_SEGMENT结构。

              

 四、定位申请的内存

  我们知道我们申请的内存地址为4A0A90,根据前面学习的,4A0A90-0x10 = 4A0A80就是_HEAP_ENTRY的地址,而该地址在段4a0000中

  1)我们使用!heap -a 4a0000查该堆的内容

:> !heap -a 4a0000
Index Address Name Debugging options enabled
: 004a0000
Segment at 00000000004a0000 to 00000000004b0000 ( bytes committed)
Flags:
ForceFlags:
Granularity: 16 bytes
Segment Reserve:
Segment Commit:
DeCommit Block Thres:
DeCommit Total Thres:
Total Free Size:
Max. Allocation Size: 000007fffffdefff
Lock Variable at: 00000000004a0208
Next TagIndex:
Maximum TagIndex:
Tag Entries:
PsuedoTag Entries:
Virtual Alloc List: 004a0118
Uncommitted ranges: 004a00f8
004a2000: 0000e000 ( bytes)
FreeList[ ] at 00000000004a0158: 00000000004a0ac0 . 00000000004a0ac0
00000000004a0ab0: . [] - free Segment00 at 004a0000:
Flags:
Base: 004a0000
First Entry: 004a0a80
Last Entry: 004b0000
Total Pages:
Total UnCommit: 0000000e
Largest UnCommit:
UnCommitted Ranges: () Heap entries for Segment00 in Heap 00000000004a0000
address: psize . size flags state (requested size)
00000000004a0000: . 00a80 [] - busy (a7f)
00000000004a0a80: 00a80 . 00030 [101] - busy (20)
00000000004a0ab0: . []
00000000004a1fc0: . [] - busy (3d)
00000000004a2000: 0000e000 - uncommitted bytes.

  可以看到内存粒度问16字节

 Granularity:           bytes

  继续往下看可以发现堆中正好有地址为4a0a80的一项

00000000004a0a80: 00a80 .  [] - busy ()

  第一项为地址,第二项a80为上一项的堆块大小,0x30为该堆块的大小,[101]为是这个内存的标志位,最右边的1表示内存块被占用,然后busy(20)表示这块内存被占用,申请的内存为0x20,加上块首的大小为0x10,一共是0x30

  

  2)我们知道了_HEAP_ENTRY的地址为4a0a80,我们查看该结构体

  

  发现这里的Size和我们的0x30完全不符!

  我们可以看上面的_HEAP结构,Win7下面的_HEAP结构比XP多了两项

   +0x07c EncodeFlagMask   : 0x100000
+0x080 Encoding : _HEAP_ENTRY

  相对于XP,Vista之后增加了对堆块的头结构(HEAP_ENTRY)的编码。编码的目的是引入随机性,增加堆的安全性,防止黑客轻易就可以预测堆的数据结构内容而实施攻击。在_HEAP结构中新增了如下两个字段:

  其中的EncodeFlagMask用来指示是否启用编码功能,Encoding字段是用来编码的,编码的方法就是用这个Encoding结构与每个堆块的头结构做亦或(XOR)

  读取_HEAP偏移为0x80的Encoding子结构:注意Size字段是从偏移8开始的两个字节,不是从偏移0开始

  

  我们使用dd查看我们的HEAP_ENTRY信息

  

  做异或解码:

:> ?2b552b39^29542b3a
Evaluate expression: = `

  低地址的word是Size字段,所以Size字段是0x3,因为是以0x10为内存粒度的,所以字节大小为

:> ?*0x10
Evaluate expression: = `

  也就是0x30,与我们显示出的正好一致

  

  3)内存中的数据

  

  

五、总结

  1.在PEB中保存这进程的堆地址和数量。

  2.HEAP结构记录HEAP_SEGMENT的方式采用了链表,这样不再受数组大小的约束,同时将HEAP_SEGMENT字段包含进HEAP,这样各堆段的起始便统一为HEAP_SEGMENT,不再有xp下0号段与其他段那种区别,可以统一进行管理了。

  3.每个HEAP_SEGMENT都有多个堆块,每个堆块包含块首和块身,块身为我们申请得到的地址。

  下一篇会研究堆溢出。

  代码链接:http://pan.baidu.com/s/1bpBm8W3

 参考:

  windbg调试HEAP

  堆和堆的调试

  <软件漏洞分析技术>

  <漏洞战争>

  

Windows7 x64 了解堆的更多相关文章

  1. Windows7 x64 系统下安装 Nodejs 并在 WebStorm 9.0.1 下搭建编译 LESS 环境

    1. 打开Nodejs官网http://www.nodejs.org/,点“DOWNLOADS”,点64-bit下载“node-v0.10.33-x64.msi”. 2. 下载好后,双击“node-v ...

  2. Windows7 x64 跨平台开发环境安装配置

    ======================================================================= Windows7 x64 跨平台开发环境安装配置 201 ...

  3. Windows7 x64系统下安装Nodejs并在WebStorm下搭建编译less环境

    1. 打开Nodejs官网http://www.nodejs.org/,点“DOWNLOADS”,点64-bit下载“node-v0.10.33-x64.msi”. 2. 下载好后,双击“node-v ...

  4. Windows7(x64)下Oracle10g安装

    安装环境:Windows7 (64位版本) + Oracle10g 问题描述1:无法启动安装程序,程序提示“程序异常终止.发生内部错误....” 解决过程:按网上说法加6.1版本参数,按xp兼容模式启 ...

  5. 0001.如何在Windows7(x64)上安装 Sharepoint2010 Fundation

    一.修改Config.xml文件 到目录:"C:\Program Files (x86)\MSECache\SharePoint2010\Files\Setup"下去修改confi ...

  6. windows7 x64下maven安装和配置

    http://maven.apache.org/download.cgi下载maven 环境配置 验证配置是否成功 本地仓库配置 这是原来的配置文件: 更改为: link 离线安装 eclipse m ...

  7. Windows7 x64配置 Apache2 + PHP5 + MySQL5

    1:相关软件下载: Apache HTTP Server             版本:(httpd-2.2.25-win32-x86-openssl-0.9.8y) PHP             ...

  8. Windows7 x64 编译Dlib库

    最近用到Dlib库,需要先编译. 本文利用 cmake + Sublime Text 2 + MinGW实现编译. 1. 下载dlib源码[dlib18.17]http://pan.baidu.com ...

  9. JDK安装教程(Windows7 x64)

    1.下载JDK http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html 选择自己系统相对 ...

随机推荐

  1. Jquery 自定义插件写法(示例)

    (function ($) { $.SmsHelper = $.SmsHelper || {}; $.extend($.SmsHelper, { //插件具体实现代码 yzmnum: 60, Ajax ...

  2. cocos2dx常见场景切换动画(转)

    本文转载自:http://www.cnblogs.com/linux-ios/archive/2013/04/09/3010779.html bool HelloWorld::init() { /// ...

  3. 原生态在Hadoop上运行Java程序

    第一种:原生态运行jar包1,利用eclipse编写Map-Reduce方法,一般引入Hadoop-core-1.1.2.jar.注意这里eclipse里没有安装hadoop的插件,只是引入其匝包,该 ...

  4. (转)最全正则表达式总结:验证QQ号、手机号、Email、中文、邮编、身份证、IP地址等

    什么是 RegExp? RegExp 是正则表达式(Regular expression)的缩写,作用是对字符串执行模式匹配. 通常用于格式验证.正则替换.查找子串等 各种编程语言的正则表达式基本相同 ...

  5. CSS链接的样式a:link,a:visited,a:hover,a:active

    a :link(未被访问)a:hover(鼠标悬停)a:visited(访问过:真正到达那个页面)a:active(鼠标点击与释放之间.对无href属性的a对象无作用) 这几个元素,定义CSS时候的顺 ...

  6. sql server中的 trimtrailingblanks

    使用sp_help 查出 发现有个这个属性, 如何修改呢? SET ANSI_PADDING ONAlter Table Sys_users_History Alter   column PveSit ...

  7. 【SSH学习笔记】用Struts2实现简单的用户登录

    准备阶段 在使用学习Struts2的时候首先要下载相应的架包 Struts2资源下载 这里建议下载第一个,在struts-2.5.14.1-all.zip里有很多实用的东西,不仅有架包还有官方为开发者 ...

  8. Security-OAuth2.0 密码模式之服务端实现

    第一步:配置数据库 ,固定创建三张表 ,OAuth2 框架需要默认使用这三张表 我使用的时Mysql,工具为navcat CREATE TABLE `oauth_access_token` ( `to ...

  9. 使用Python Pandas处理亿级数据

    在数据分析领域,最热门的莫过于Python和R语言,此前有一篇文章<别老扯什么Hadoop了,你的数据根本不够大>指出:只有在超过5TB数据量的规模下,Hadoop才是一个合理的技术选择. ...

  10. Java多线程——线程封闭

    线程封闭:当访问共享的可变数据时,通常需要同步.一种避免同步的方式就是不共享数据.如果仅在单线程内访问数据,就不需要同步,这种技术称为线程封闭(thread  confinement) 线程封闭技术一 ...