参数1:如何在改堆栈执行操作,你可以设定0、HEAP_NO_SERIALIZE、HEAP_GENERATE_EXCEPTIONS、HEAP_CREATE_ENABLE_EXECUTE或者是这些标志的组合。
HEAP_NO_SERIALIZE:非独占
HEAP_GENERATE_EXCEPTIONS:分配失败时抛出异常,如果不设置则会返回NULL
HEAP_CREATE_ENALBE_EXECUTE:堆栈内容是可执行代码,不设置则不是可执行代码
这里的值是 1 非独占
参数2: 初始大小 1000 4KB
参数3: 最大大小,0为不限制? 这里设置为0
伪代码:
HANDLE WINAPI HeapCreate(DWORD flOptions,SIZE_T dwInitialSize,SIZE_T dwMaximumSize){
return API-MS-Win-Core-Heap-L1-1-0.HeapCreate;
}
API-MS-Win-Core-Heap-L1-1-0.HeapCreate(){
return KernelBa.HeapCreate;
}
HANDLE KernelBa.HeapCreate(DWORD flOptions,SIZE_T dwInitialSize,SIZE_T dwMaximumSize){
DWORD 堆分配选项 = flOptions; c
SIZE_T 堆的初始大小 = 0x76AF0828; a
SIZE_T 堆的最大大小 = dwMaximumSize; d
堆分配选项 = 0x40005 and 堆分配选项;
堆分配选项 = 0x1000 or 堆分配选项;
if(堆的最大大小 <= 堆的初始大小){
if(堆的最大大小 != 0){
堆的最大大小 = 堆的初始大小;
}else{
堆的初始大小 = 堆的初始大小 << 4;
esi = 堆的初始大小;//10000
堆分配选项 = 1001 or 2 = 1003;
}
}
堆的最大大小 = 堆的初始大小;
//这个函数只设置了 堆分配选项 的值(可能)?
//所以初始大小和默认大小参数都可以为0
}
这个函数会最终调用 ntdll.RtlCreateHeap 函数,而这个函数会最终调用 ntdll_1a.76F2DEB4(ZwAllocateVirtualMemory ? -->KiSystemService -->NtAllocateVirtualMemory) 实现
汇编分析:
0052D200 /$ 6A 00 push 0x0 ; /MaximumSize = 0x0
0052D202 |. 68 00100000 push 0x1000 ; |InitialSize = 1000 (4096.)
0052D207 |. 6A 01 push 0x1 ; |Flags = HEAP_NO_SERIALIZE
0052D209 |. FF15 D8A67D00 call dword ptr ds:[<&KERNEL32.HeapCreate>] ; \HeapCreate
&KERNEL32.HeapCreate
74CE4A2C 90 nop
74CE4A2D>8BFF mov edi,edi ; 热补丁专用
74CE4A2F 55 push ebp ;
74CE4A30 8BEC mov ebp,esp ; 保存当前的esp栈地址
74CE4A32 5D pop ebp ; 单人游戏.0052D20F,这5行什么都没做啊
74CE4A33 /EB 05 jmp short
74CE4A35 |90 nop
74CE4A36 |90 nop
74CE4A37 |90 nop
74CE4A38 |90 nop
74CE4A39 |90 nop
74CE4A3A -\FF25 C808CE74 jmp dword ptr ds:[<&API-MS-Win-Core-Heap-L1-1-0.HeapCreate>] ; KernelBa.HeapCreate
KernelBa.HeapCreate
76AC4516>8BFF mov edi,edi ; 上面为啥是 int3 而不是 nop
76AC4518 55 push ebp ; ebp压栈,然后将esp的值移动到ebp
76AC4519 8BEC mov ebp,esp
76AC451B 8B4D 08 mov ecx,dword ptr ss:[ebp+0x8] ; 堆栈的第一个参数 1 非独占 赋值给 ecx
76AC451E 8B55 10 mov edx,dword ptr ss:[ebp+0x10] ; 堆栈的第三个参数 0 最大大小 赋值给 edx
76AC4521 A1 2808AF76 mov eax,dword ptr ds:[0x76AF0828] ; 从数据段中获得第二个参数,堆栈的初始大小 1000 4KB
76AC4526 81E1 05000400 and ecx,0x40005 ; 40005 和 00001 进行 and 运算,结果还是 1
76AC452C 56 push esi ; esi压栈
76AC452D 81C9 00100000 or ecx,0x1000 ; 1000 和 0001 进行 or 运算,结果为 1001
76AC4533 33F6 xor esi,esi ; esi 清0
76AC4535 3BD0 cmp edx,eax ; 判断 第三个参数0 - 1000第二个参数 ,然后修改标志位,结果C=1(借位)S=1(负数)
76AC4537 73 14 jnb short KernelBa.76AC454D ; edx 不小于 eax 跳转,这里不跳
76AC4539 85D2 test edx,edx ; 判断第三个参数是不是 0
76AC453B 75 0A jnz short KernelBa.76AC4547 ; 如果不是0则跳转,这里不跳
76AC453D C1E0 04 shl eax,0x4 ; eax 左移4位 = 00010000
76AC4540 8BF0 mov esi,eax ; 将 10000 赋值给 esi
76AC4542 83C9 02 or ecx,0x2 ; ecx or 2 = 1001 or 2 = 1003
76AC4545 EB 02 jmp short KernelBa.76AC4549 ; 短跳转到 +4字节处
76AC4547 8BD0 mov edx,eax ; 如果跳到这里,说明第三个参数不是0
76AC4549 85F6 test esi,esi ; 判断esi是否是0,也就是判断第二个参数是不是0
76AC454B 75 08 jnz short KernelBa.76AC4555 ; 如果不是0,短跳到+10字节处
76AC454D 3955 0C cmp dword ptr ss:[ebp+0xC],edx ; 如果跳到这里,则说明第三个参数大于1000
76AC4550 76 03 jbe short KernelBa.76AC4555 ; 如果第二个参数小于等于第三个参数 , push 0
76AC4552 8B55 0C mov edx,dword ptr ss:[ebp+0xC] ; 第二个参数赋值到 edx ,然后 push 0
76AC4555 6A 00 push 0x0 ; 前面4行的最终归宿都是这里 指向RTL_HEAP_PARAMETERS结构的指针
76AC4557 6A 00 push 0x0 ; 非独占这里必须为0,lock
76AC4559 FF75 0C push dword ptr ss:[ebp+0xC] ; 提交大小 1000
76AC455C 52 push edx ; 保留大小,可为0.保留大小=0,提交大小非0,保留大小设置为16页大小
76AC455D 6A 00 push 0x0 ; 堆基址,0表示系统分配,非0则分配到该地址为基址
76AC455F 51 push ecx ; flags访问控制 1003 表示私有,可增加,非独占的heap
76AC4560 FF15 A813AB76 call dword ptr ds:[<&ntdll.RtlCreateHeap>] ; RtlCreateHeap函数 创建一个可以由调用进程使用的堆对象
ntdll.RtlCreateHeap
76F40249>68 9C000000 push 0x9C
76F4024E 68 F8C7F276 push ntdll_1a.76F2C7F8
76F40253 E8 5CDCFEFF call ntdll_1a.76F2DEB4 ; 未导出的函数,有两个参数
这里是第四行
ntdll_1a.76F2DEB4
76F2DEC0 8B4424 10 mov eax,dword ptr ss:[esp+0x10] ; 第二个参数 9C
76F2DEC4 896C24 10 mov dword ptr ss:[esp+0x10],ebp ; ebp寄存器的值 0018FEFC (HeapCreate函数的栈基址) 移到第二个参数处
76F2DEC8 8D6C24 10 lea ebp,dword ptr ss:[esp+0x10] ; 将esp地址18FEC8+10的地址放入ebp(HeapCreate栈基址)
76F2DECC 2BE0 sub esp,eax ; 减去9c(156字节)的空间
76F2DECE 53 push ebx ; 7EFDE000
76F2DECF 56 push esi ; 10000 提交大小左移4位得到的
76F2DED0 57 push edi ; 保留大小 0
76F2DED1 A1 88200077 mov eax,dword ptr ds:[0x77002088] ; 将时间段地址77002088的值64A7F775赋值到eax,可能是一个函数地址
76F2DED6 3145 FC xor dword ptr ss:[ebp-0x4],eax ; 76F2C7F8 xor 64A7F775 = 1255308D 在之前第二个参数9C处
76F2DED9 33C5 xor eax,ebp ; 0018FED8 xor 64A7F775 = 64BF09AD eax
76F2DEDB 50 push eax ; eax的值压栈
76F2DEDC 8965 E8 mov dword ptr ss:[ebp-0x18],esp ; 将现在的栈指针赋值到当前堆栈的第3个参数位置处
76F2DEDF FF75 F8 push dword ptr ss:[ebp-0x8]
76F2DEE2 8B45 FC mov eax,dword ptr ss:[ebp-0x4] ; 将1255308D赋值到eax
76F2DEE5 C745 FC FEFFFFFF mov dword ptr ss:[ebp-0x4],-0x2 ; 将FFFFFFFE(-2)替换到该位置
76F2DEEC 8945 F8 mov dword ptr ss:[ebp-0x8],eax ; 将eax的值1255308D赋值到第一个参数处
76F2DEEF 8D45 F0 lea eax,dword ptr ss:[ebp-0x10] ; 将第四个变量空间的地址0018FEC8赋值到eax
76F2DEF2 64:A3 00000000 mov dword ptr fs:[0],eax ; 将这个地址作为teb(线程环境块)的基地址
76F2DEF8 C3 retn ; 返回到 ntdll.RtlCreateHeap的第四行
这里是第五行
76F40258 E8 747BFFFF call ntdll_1a.RtlGetNtGlobalFlags ; 获取Nt全局标志?