本来这周按照计划应该是看CVE-2010-3974 Windows传真封面编辑器的双重释放漏洞的,但是《漏洞战争》这本书中对该漏洞并没有进行什么介绍,漏洞利用方法留在了下一章对于UAF漏洞的介绍中。因此本周文章没有针对具体某个漏洞的介绍,只针对双重释放漏洞本身进行了详细地调试学习。
在我执行书中示例代码生成的程序时,并没有如同书中介绍的那样触发异常,反而出现了程序卡住的情况,自己手动添加了第三次释放之后,程序触发异常。对于这种情况,我根据函数调用栈帧进行了详细地调试,分析了产生上述结果的原因。经过此次调试学习,对于heap的结构以及windows引入的安全机制有了更深的理解,同时也学习到了更多调试内存异常的方法,相信对于之后的漏洞调试分析会有很大帮助。
最初我在理解双重释放 这个概念时,注意力主要集中在了指针 上,心里一直在想释放两次指针会出什么问题。后来才意识到自己漏洞概念弄混了,双重释放 释放的并不是指针,而是指针所指向的空间。
而需要进行释放 的空间都是使用类似malloc
这类的函数在堆上分配的空间,所以想要了解为什么双重释放会触发异常,就必须对堆结构,以及空间释放时发生的操作有所了解。
我使用下面的代码进行调试分析:
注:在生成Release版本的可执行文件之前,根据参考资料2,设置生成对应的PDB文件,方便调试。
程序在执行后,并没有触发异常,有的时候会直接卡在了最后一步 ,有的时候会正常执行 ,所以最后又添加了第三次释放,一定会引发异常 。下面看一下这期间究竟发生了什么,导致出现了这三种情况。
直接执行程序,触发异常,windbg打开,显示错误信息:
此时的函数调用信息:
可以看到,当尝试在002d06f8
上调用free
函数的时候,在RtlpCoalesceFreeBlocks
函数处发生了异常。看一下002d06f8
这个堆块的信息:
第一个错误信息HEAP 002d0000 (Seg 002d0000) At 002d0678 Error: block list entry corrupted
,这个错误信息在这里没什么用,因为现在正在调用HeapFree
函数,由于会发生双向链表的链接操作,链表指针的数值可能会发生修改,出现不一致是正常的。
第二个错误信息HEAP 002d0000 (Seg 002d0000) At 002d73d8 Error: invalid block size
需要注意一下,从上面的信息可以看到当前的堆块大小为6d60
,002d0678+6d60=2d73d8
,也就是错误信息中的数值。看一下这个地址信息:
这块地址是保留的,再看一下整个堆的信息:
我只截取了前面的一段信息,可以看到整个堆的范围在002d0000
到002e0000
,但是最后有0x5000
字节的内存无法使用(committed可以这样理解吗?)。
所以现在基本可以确定002d0678
堆块中的size
信息是错误的。在回顾一下发生异常的代码:
在尝试向002d73dc
写入信息,这个地址是通过esi+eax*8+4
计算得到的,实际上就是在通过002d0678
堆块的起始地址和大小计算下一个相邻堆块的地址。但是因为size
信息有问题,导致计算得到的是一个无法访问的地址,从而出现了异常。
接下来看一下这个size
数值是怎么来的。为了调试程序,在程序开头添加__asm int 3
,重新启动程序。
直接步进到第二次释放p2
之前,然后在RtlpCoalesceFreeBlocks
上设置一个断点:
继续运行:
RtlpCoalesceFreeBlocks
的函数原型为:
可以根据函数原型对应到栈上的四个参数,其中第三个参数FreeSize
是一个指针,真正指向的值为:
看一下306f0
处的数据:
大小也是符合的。所以RtlpCoalesceFreeBlocks
函数调用确实想要去合并306f0
处的堆块,并从所谓的头部获取到了这个堆块的大小作为参数传入了函数中。
接下来跟一下RtlpCoalesceFreeBlocks
函数的执行流程:
可以看到执行到这里,得到的要释放的堆块p2的前一个堆块的位置就是代码中分配得到的p1的位置。因为在第一次释放p2的时候,只是把p2和p1、p3合并到一起,只需要修改一些flag值,其他数据并没有改变,所以仍旧能够得到正确的前一个堆块的位置。
接下来一段代码检查前一个堆块是不是空闲的:
之后同样对前一个堆块的头部数据进行了解码,进行了堆块大小的判断,这里不再贴出来。继续看后面,进行了双向链表指针的判断:
接下来有一大段的代码在检查和更新和BlocksIndex
有关的内容,这部分知识和Low fragmentation heap有关,具体可以查看参考资料6以及7,我大致看了一下这部分内容,同时调试跟了一下这段代码,还是有一些概念不太清楚,不过大致判断和这次的漏洞分析关系不大,所以下面不再贴出这部分代码,直接步进到相关代码处:
7792430b
这个地址是通过静态分析在IDA中获得的。
这里程序检查了上一堆块,即p1这一堆块的flags值,根据参考资料8:
0x01 Indicates that the allocation is being used by the application or the heap manager0x04 Indicates whether the heap block has a fill pattern associated with it0x08 Indicates that the heap block was allocated directly from the virtual memory manager0x10 Indicates that this is the last heap block prior to an uncommitted range
在此程序中,p1堆块的flags值为0,所以上面的跳转都没有执行。
接下来计算并更新堆块合并之后的大小:
这部分操作结束之后,p1堆块的大小增加了原本p2堆块的大小,但是要记得,p2这是第二次释放了,所以相当于p1加上了一个不存在的空间大小。
注意上面最后一行的代码,第三次释放p2的时候,异常就是发生在这里。esi+eax*8+4
在尝试计算堆块合并后,p1相邻的下一个堆块的位置(esi+eax*8
),并到达其Previous chunk size
所在位置(+4
),之后用编码后的大小数值更新该位置数据,即更新p1下一堆块中记录的上一堆块大小值。
所以调试到这里,我们知道释放堆块的时候,程序会更新前一相邻堆块的大小为两堆块大小的和,同时根据这个更新后的大小,计算并更新后一堆块中记录的Previous chunk size
数据。由于二次释放会导致相邻堆块大小的计算中包含一块不存在的空间,致使计算的大小大于实际大小,如果计算得到的大小超出了可访问内存范围,就会导致更新后一堆块中数据时发生内存访问错误。
虽然知道了发生异常的原因,但是为什么有的时候会出现二次释放p2时卡住的情况呢?继续进行调试:
之后的一段代码程序对p1相邻的下一堆块进行了一些验证,由于这里并没有真的指向一个堆块,所以计算得到的结果也都是不正确的,最后因为验证失败而调用了RtlpLogHeapFailure
函数。这部分代码和此次漏洞分析无关,这里不再贴出。
之后程序判断了p1相邻的下一堆块是否为空闲,从而判断是否需要进行合并:
要记得我们这里计算得到的p1相邻的下一堆块是一个不存在的假的堆块,而且它所在的位置一定已经超过了程序一开始自己申请的p1、p2、p3堆块的范围,这部分的数据是不可控的,所以得到的结果有一定随机性。
注:为什么超过了p3的范围? 因为经过三次释放,p1、p2、p3已经p3后面的空闲堆块都合并在了一起,此时p1中保存的堆块大小就是这一整块合并之后的堆块大小。但由于p2的二次释放,这一堆块大小又加上了一个数值,所以所谓的p1相邻的下一堆块的位置一定是超过p3的范围的。
这次实验一开始没有跳转,继续往下执行,之后对该下一堆块的头部进行了一些检查,这里的检查比较有意思,它执行了一个这样的操作:next_block->InterceptorValue ^= Heap->Encoding.InterceptorValue;
,这个操作是在对堆块头部进行解码,所以会改变堆块前四个字节的值 ,这一点要注意!这里测试的时候失败了,所以调用了RtlpAnalyzeHeapFailure
函数。
之后会检查该下一堆块中双向链表指针中的值是否正常,由于并不是一个真正的堆块,所以检查必然会失败(这里成功的可能性基本为零),调用RtlpLogHeapFailure
函数。
然后的流程就比较有意思了,这里是一个while循环,程序会再次判断下一堆块是否空闲。
上面文字版的说明可能会比较乱,这里贴一下IDA中获得的伪代码:
也就是说,因为next_block
在这里并不是一个真实的堆块,最后验证双向链表指针的时候一定会失败,程序在验证flags
数值的时候,指向的堆块一直是同一堆块。如果堆块的前四个字节解码前后的数值在验证flags
的阶段一直成功,程序就会陷在这个循环里面。
这就是我在实验的时候,为什么有的时候二次释放p2堆块能够成功执行,有的时候直接卡住的原因。
根据之前的分析,如果二次释放的堆块足够大,那么在二次释放的时候就会发生异常,但是正如在此例中发生的情况一样,p2并不是特别大,所以二次释放的时候并没有触发异常。
第三次释放的时候,如果加上的释放堆块的大小仍旧是p2的大小的话,是不会超出可访问范围的。所以说程序之后一定还进行了一些操作,修改了p2堆块中self size
所在位置的数值,导致第三次释放时加上了一个很大的数值。
接下来看一下程序是怎样修改p2堆块中self size
所在位置的数值的。
让程序回退到第一次释放p2之前(之前建立了快照),并在306f0
处建立一个读写断点:
可以看到在第一次释放p2的时候,p2堆块头部在修改前后的数值变化,self size
从0xbc3
变成了0xf
,而0xf
就是p2的真实大小(需要*8),所以RtlpFreeHeap
函数在77928107
这里对于堆块头部的修改,其实就在做一个解码操作(记得前面提到过,系统出于安全考虑,会对堆块头部进行编码)。
接下来继续运行,会遇到一些不太重要的读取过程,然后到达下面这段代码:
可以看到RtlpCoalesceFreeBlocks
中第三个参数Freesize
是直接从Freeblock
的头部,即306f0
读取的,也就是读取的解码后的长度。
之后还要在free
函数那里设置一个断点,避免在F5的过程中程序直接执行到了下一次p2释放。然后再继续执行,程序知道到达了第二次p2释放:
self size
的值会再次被“解码”,因为之前已经是解码之后的值了,这次的解码操作其实相当于做了一次编码,得到了0xbc3
。
在继续执行之前,看一下这段代码:
在对前四个字节进行了解码操作之后,进行了一个验证:
如果这四个字节解码之后的数据是正常的,验证能够通过。而这里,由于是在进行二次释放,所谓的解码操作其实是在进行编码,得到的结果自然不正常。因此程序会调用RtlpAnalyzeHeapFailure
函数。
在RtlpAnalyzeHeapFailure
函数中,程序又对306f0
前四个字节的数值进行了恢复,看一下它的恢复过程:
从上面的代码可以看出,函数不是直接通过编码进行恢复,它首先要确保这四个字节是能够通过验证的,所以它重新设置了SmallTagIndex
字段的值,然后再进行编码。这样就能保证之后堆块的前四个字节在解码之后能够通过验证。
修正之后的数值,以及编码后结果为:
这也是为什么在二次释放p2时,得到的self size
仍旧是正确的原因。
根据上面的调试流程,p2前四个字节的变化过程:
知道了p2的self size
的变化过程,我们就知道为什么第二次释放的时候p2的堆块大小是正常的,而第三次释放的时候p2的堆块大小就变得很大了。
下面总结一下此次双重释放实验触发异常的完整流程:
在进行p2的二次释放时,虽然没有触发异常,但是程序已经访问了本不应该访问的,位于p3之后的空间。在《漏洞战争》中,有提到可以通过“占坑”的方式将已释放的内存填充自己的代码,从而控制程序的执行流程。
如果在代码编写的时候,创建完p3堆块之后,再创建一个堆块p4,且该堆块始终未被释放。那么在二次释放p2的时候,就必然会访问到p4所在的空间,这部分空间内容是可控的(在代码编写阶段,或者加入一个用户输入的功能)。
回顾上面的调试过程,在分析程序为何会在二次释放过程中陷入循环时,由于验证双向链表指针失败,有很多在可控空间(即这里的p4)上面的操作都无法执行。
所以如果精心构造p4的内容,是否可以实现漏洞利用呢?下周会开始看UAF漏洞及其利用方式,双重释放漏洞是UAF漏洞的一个子集,届时就可以知道这样的漏洞究竟是如何实现漏洞利用的了。
int
main (
int
argc, char
*
argv[])
{
void
*
p1,
*
p2,
*
p3;
char
*
test_str
=
"aaaaaaaa"
;
/
/
__asm
int
3
p1
=
(char
*
)malloc(
100
);
printf(
"Alloc p1:%p\n"
,p1);
strncpy(p1, test_str, strlen(test_str));
p2
=
(char
*
)malloc(
100
);
printf(
"Alloc p2:%p\n"
,p2);
strncpy(p2, test_str, strlen(test_str));
p3
=
(char
*
)malloc(
100
);
printf(
"Alloc p3:%p\n"
,p3);
strncpy(p3, test_str, strlen(test_str));
printf(
"Free p1\n"
);
free(p1);
printf(
"Free p3\n"
);
free(p3);
printf(
"Free p2\n"
);
free(p2);
printf(
"Double Free p2\n"
);
free(p2);
/
/
printf(
"Triple Free p2\n"
);
/
/
free(p2);
return
0
;
}
int
main (
int
argc, char
*
argv[])
{
void
*
p1,
*
p2,
*
p3;
char
*
test_str
=
"aaaaaaaa"
;
/
/
__asm
int
3
p1
=
(char
*
)malloc(
100
);
printf(
"Alloc p1:%p\n"
,p1);
strncpy(p1, test_str, strlen(test_str));
p2
=
(char
*
)malloc(
100
);
printf(
"Alloc p2:%p\n"
,p2);
strncpy(p2, test_str, strlen(test_str));
p3
=
(char
*
)malloc(
100
);
printf(
"Alloc p3:%p\n"
,p3);
strncpy(p3, test_str, strlen(test_str));
printf(
"Free p1\n"
);
free(p1);
printf(
"Free p3\n"
);
free(p3);
printf(
"Free p2\n"
);
free(p2);
printf(
"Double Free p2\n"
);
free(p2);
/
/
printf(
"Triple Free p2\n"
);
/
/
free(p2);
return
0
;
}
(b7c.
830
): Access violation
-
code c0000005 (!!! second chance !!!)
eax
=
00000dac
ebx
=
002d0678
ecx
=
00000665
edx
=
002d0564
esi
=
002d0678
edi
=
002d0000
eip
=
7792434c
esp
=
0018fdbc
ebp
=
0018fde4
iopl
=
0
nv up ei pl nz na pe nc
cs
=
0023
ss
=
002b
ds
=
002b
es
=
002b
fs
=
0053
gs
=
002b
efl
=
00010206
ntdll!RtlpCoalesceFreeBlocks
+
0x47c
:
7792434c
66894cc604
mov word ptr [esi
+
eax
*
8
+
4
],cx ds:
002b
:
002d73dc
=
????
(b7c.
830
): Access violation
-
code c0000005 (!!! second chance !!!)
eax
=
00000dac
ebx
=
002d0678
ecx
=
00000665
edx
=
002d0564
esi
=
002d0678
edi
=
002d0000
eip
=
7792434c
esp
=
0018fdbc
ebp
=
0018fde4
iopl
=
0
nv up ei pl nz na pe nc
cs
=
0023
ss
=
002b
ds
=
002b
es
=
002b
fs
=
0053
gs
=
002b
efl
=
00010206
ntdll!RtlpCoalesceFreeBlocks
+
0x47c
:
7792434c
66894cc604
mov word ptr [esi
+
eax
*
8
+
4
],cx ds:
002b
:
002d73dc
=
????
0
:
000
> kb
ChildEBP RetAddr Args to Child
0018fde4
77923407
0000007f
002d06f0
0018feac
ntdll!RtlpCoalesceFreeBlocks
+
0x47c
0018fedc
779232f2
002d06f0
002d06f8
002d06f8
ntdll!RtlpFreeHeap
+
0x1f4
0018fefc
772014d1
002d0000
00000000
002d06f8
ntdll!RtlFreeHeap
+
0x142
0018ff10
004011a7
002d0000
00000000
002d06f8
kernel32!HeapFree
+
0x14
0018ff2c
00401138
002d06f8
00408030
002d0770
double_free!free
+
0x66
0018ff48
004014c2
00000001
002d0e38
002d0e90
double_free!main
+
0x138
[C:\Users\test\Documents\ldzz\double_free\double_free.c @
33
]
0018ff88
77203677
7efde000
0018ffd4
77929d72
double_free!mainCRTStartup
+
0xb4
0018ff94
77929d72
7efde000
7fa803bd
00000000
kernel32!BaseThreadInitThunk
+
0xe
0018ffd4
77929d45
0040140e
7efde000
00000000
ntdll!__RtlUserThreadStart
+
0x70
0018ffec
00000000
0040140e
7efde000
00000000
ntdll!_RtlUserThreadStart
+
0x1b
0
:
000
> kb
ChildEBP RetAddr Args to Child
0018fde4
77923407
0000007f
002d06f0
0018feac
ntdll!RtlpCoalesceFreeBlocks
+
0x47c
0018fedc
779232f2
002d06f0
002d06f8
002d06f8
ntdll!RtlpFreeHeap
+
0x1f4
0018fefc
772014d1
002d0000
00000000
002d06f8
ntdll!RtlFreeHeap
+
0x142
0018ff10
004011a7
002d0000
00000000
002d06f8
kernel32!HeapFree
+
0x14
0018ff2c
00401138
002d06f8
00408030
002d0770
double_free!free
+
0x66
0018ff48
004014c2
00000001
002d0e38
002d0e90
double_free!main
+
0x138
[C:\Users\test\Documents\ldzz\double_free\double_free.c @
33
]
0018ff88
77203677
7efde000
0018ffd4
77929d72
double_free!mainCRTStartup
+
0xb4
0018ff94
77929d72
7efde000
7fa803bd
00000000
kernel32!BaseThreadInitThunk
+
0xe
0018ffd4
77929d45
0040140e
7efde000
00000000
ntdll!__RtlUserThreadStart
+
0x70
0018ffec
00000000
0040140e
7efde000
00000000
ntdll!_RtlUserThreadStart
+
0x1b
0
:
000
> !heap
-
x
2d06f8
List
corrupted: (Blink
-
>Flink
=
002d3178
) !
=
(Block
=
002d0680
)
HEAP
002d0000
(Seg
002d0000
) At
002d0678
Error: block
list
entry corrupted
HEAP
002d0000
(Seg
002d0000
) At
002d73d8
Error: invalid block size
Entry User Heap Segment Size PrevSize Unused Flags
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
002d0678
002d0680
002d0000
002d0000
6d60
108
0
free
0
:
000
> !heap
-
x
2d06f8
List
corrupted: (Blink
-
>Flink
=
002d3178
) !
=
(Block
=
002d0680
)
HEAP
002d0000
(Seg
002d0000
) At
002d0678
Error: block
list
entry corrupted
HEAP
002d0000
(Seg
002d0000
) At
002d73d8
Error: invalid block size
Entry User Heap Segment Size PrevSize Unused Flags
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
002d0678
002d0680
002d0000
002d0000
6d60
108
0
free
0
:
000
> !address
2d73d8
Failed to
map
Heaps (error
80004005
)
Usage: <unclassified>
Allocation Base:
002d0000
Base Address:
002d5000
End Address:
002e0000
Region Size:
0000b000
Type
:
00020000
MEM_PRIVATE
State:
00002000
MEM_RESERVE
Protect:
00000000
0
:
000
> !address
2d73d8
Failed to
map
Heaps (error
80004005
)
Usage: <unclassified>
Allocation Base:
002d0000
Base Address:
002d5000
End Address:
002e0000
Region Size:
0000b000
Type
:
00020000
MEM_PRIVATE
State:
00002000
MEM_RESERVE
Protect:
00000000
0
:
000
> !heap
-
a
-
h
2d0000
Index Address Name Debugging options enabled
2
:
002d0000
Segment at
002d0000
to
002e0000
(
00005000
bytes committed)
Flags:
00001003
ForceFlags:
00000001
Granularity:
8
bytes
Segment Reserve:
00100000
Segment Commit:
00002000
DeCommit Block Thres:
00000200
......
0
:
000
> !heap
-
a
-
h
2d0000
Index Address Name Debugging options enabled
2
:
002d0000
Segment at
002d0000
to
002e0000
(
00005000
bytes committed)
Flags:
00001003
ForceFlags:
00000001
Granularity:
8
bytes
Segment Reserve:
00100000
Segment Commit:
00002000
DeCommit Block Thres:
00000200
......
(b7c.
830
): Access violation
-
code c0000005 (!!! second chance !!!)
eax
=
00000dac
ebx
=
002d0678
ecx
=
00000665
edx
=
002d0564
esi
=
002d0678
edi
=
002d0000
eip
=
7792434c
esp
=
0018fdbc
ebp
=
0018fde4
iopl
=
0
nv up ei pl nz na pe nc
cs
=
0023
ss
=
002b
ds
=
002b
es
=
002b
fs
=
0053
gs
=
002b
efl
=
00010206
ntdll!RtlpCoalesceFreeBlocks
+
0x47c
:
7792434c
66894cc604
mov word ptr [esi
+
eax
*
8
+
4
],cx ds:
002b
:
002d73dc
=
????
(b7c.
830
): Access violation
-
code c0000005 (!!! second chance !!!)
eax
=
00000dac
ebx
=
002d0678
ecx
=
00000665
edx
=
002d0564
esi
=
002d0678
edi
=
002d0000
eip
=
7792434c
esp
=
0018fdbc
ebp
=
0018fde4
iopl
=
0
nv up ei pl nz na pe nc
cs
=
0023
ss
=
002b
ds
=
002b
es
=
002b
fs
=
0053
gs
=
002b
efl
=
00010206
ntdll!RtlpCoalesceFreeBlocks
+
0x47c
:
7792434c
66894cc604
mov word ptr [esi
+
eax
*
8
+
4
],cx ds:
002b
:
002d73dc
=
????
0
:
000
> p
eax
=
000306f8
ebx
=
7efde000
ecx
=
0040a110
edx
=
0008e3b8
esi
=
00000000
edi
=
00000000
eip
=
0040111e
esp
=
0018ff28
ebp
=
0018ff48
iopl
=
0
nv up ei pl nz na po nc
cs
=
0023
ss
=
002b
ds
=
002b
es
=
002b
fs
=
0053
gs
=
002b
efl
=
00000202
double_free!main
+
0x11e
:
0040111e
e825000000 call double_free!free (
00401148
)
0
:
000
> dd esp L1
0018ff28
000306f8
0
:
000
> bp RtlpCoalesceFreeBlocks
0
:
000
> p
eax
=
000306f8
ebx
=
7efde000
ecx
=
0040a110
edx
=
0008e3b8
esi
=
00000000
edi
=
00000000
eip
=
0040111e
esp
=
0018ff28
ebp
=
0018ff48
iopl
=
0
nv up ei pl nz na po nc
cs
=
0023
ss
=
002b
ds
=
002b
es
=
002b
fs
=
0053
gs
=
002b
efl
=
00000202
double_free!main
+
0x11e
:
0040111e
e825000000 call double_free!free (
00401148
)
0
:
000
> dd esp L1
0018ff28
000306f8
0
:
000
> bp RtlpCoalesceFreeBlocks
0
:
000
> g
Breakpoint
0
hit
eax
=
0018fea0
ebx
=
00000000
ecx
=
779bef0f
edx
=
00000000
esi
=
000306f0
edi
=
00030000
eip
=
779230cf
esp
=
0018fddc
ebp
=
0018fed0
iopl
=
0
nv up ei pl zr na pe nc
cs
=
0023
ss
=
002b
ds
=
002b
es
=
002b
fs
=
0053
gs
=
002b
efl
=
00000246
ntdll!RtlpCoalesceFreeBlocks:
779230cf
8bff
mov edi,edi
0
:
000
> dd esp L5
0018fddc
77923407
00030000
000306f0
0018fea0
0018fdec
00000000
0
:
000
> g
Breakpoint
0
hit
eax
=
0018fea0
ebx
=
00000000
ecx
=
779bef0f
edx
=
00000000
esi
=
000306f0
edi
=
00030000
eip
=
779230cf
esp
=
0018fddc
ebp
=
0018fed0
iopl
=
0
nv up ei pl zr na pe nc
cs
=
0023
ss
=
002b
ds
=
002b
es
=
002b
fs
=
0053
gs
=
002b
efl
=
00000246
ntdll!RtlpCoalesceFreeBlocks:
779230cf
8bff
mov edi,edi
0
:
000
> dd esp L5
0018fddc
77923407
00030000
000306f0
0018fea0
0018fdec
00000000
PHEAP_FREE_ENTRY
RtlpCoalesceFreeBlocks(
IN PHEAP Heap,
IN PHEAP_FREE_ENTRY FreeBlock,
IN OUT PULONG FreeSize,
IN BOOLEAN RemoveFromFreeList
);
PHEAP_FREE_ENTRY
RtlpCoalesceFreeBlocks(
IN PHEAP Heap,
IN PHEAP_FREE_ENTRY FreeBlock,
IN OUT PULONG FreeSize,
IN BOOLEAN RemoveFromFreeList
);
0
:
000
> dd
18fea0
L1
0018fea0
0000000f
0
:
000
> dd
18fea0
L1
0018fea0
0000000f
0
:
000
> dd
306f0
L2
000306f0
d501000f
08000028
0
:
000
> dd
306f0
L2
000306f0
d501000f
08000028
ntdll!RtlpCoalesceFreeBlocks:
779230cf
8bff
mov edi,edi
779230d1
55
push ebp
779230d2
8bec
mov ebp,esp
779230d4
83ec1c
sub esp,
1Ch
779230d7
53
push ebx
779230d8
8b5d0c
mov ebx,dword ptr [ebp
+
0Ch
]
/
/
第二个参数
306f0
要释放的堆块
779230db
0fb74304
movzx eax,word ptr [ebx
+
4
]
/
/
相邻的上一个堆块的大小
779230df
56
push esi
779230e0
57
push edi
779230e1
8b7d08
mov edi,dword ptr [ebp
+
8
]
/
/
第一个参数
30000
779230e4
0fb74f54
movzx ecx,word ptr [edi
+
54h
]
/
/
Heap
-
>Encoding.PreviousSize 编码值
779230e8
33c1
xor eax,ecx
/
/
Vista之后引入了对heap_entry的编码,这里在进行解码
779230ea
c1e003 shl eax,
3
/
/
size是以
8
字节为单位的,这里
*
8
,得到字节数
0x78
779230ed
8bf3
mov esi,ebx
779230ef
2bf0
sub esi,eax
/
/
0x306f0
-
0x78
=
0x30678
,就是p1的位置
ntdll!RtlpCoalesceFreeBlocks:
779230cf
8bff
mov edi,edi
779230d1
55
push ebp
779230d2
8bec
mov ebp,esp
779230d4
83ec1c
sub esp,
1Ch
779230d7
53
push ebx
779230d8
8b5d0c
mov ebx,dword ptr [ebp
+
0Ch
]
/
/
第二个参数
306f0
要释放的堆块
779230db
0fb74304
movzx eax,word ptr [ebx
+
4
]
/
/
相邻的上一个堆块的大小
779230df
56
push esi
779230e0
57
push edi
779230e1
8b7d08
mov edi,dword ptr [ebp
+
8
]
/
/
第一个参数
30000
779230e4
0fb74f54
movzx ecx,word ptr [edi
+
54h
]
/
/
Heap
-
>Encoding.PreviousSize 编码值
779230e8
33c1
xor eax,ecx
/
/
Vista之后引入了对heap_entry的编码,这里在进行解码
779230ea
c1e003 shl eax,
3
/
/
size是以
8
字节为单位的,这里
*
8
,得到字节数
0x78
779230ed
8bf3
mov esi,ebx
779230ef
2bf0
sub esi,eax
/
/
0x306f0
-
0x78
=
0x30678
,就是p1的位置
779230f1
3bf3
cmp
esi,ebx
779230f3
7417
je ntdll!RtlpCoalesceFreeBlocks
+
0x481
(
7792310c
)
779230f5
8b474c
mov eax,dword ptr [edi
+
4Ch
]
/
/
Heap
-
>EncodeFlagMask
779230f8
8bc8
mov ecx,eax
779230fa
c1e914 shr ecx,
14h
779230fd
224f52
and
cl,byte ptr [edi
+
52h
]
/
/
Heap
-
>Encoding.Flags
77923100
324e02
xor cl,byte ptr [esi
+
2
]
/
/
pre_block
-
>Flags 获得解码后的前一个堆块的Flags值
77923103
f6c101 test cl,
1
/
/
检查是不是空闲
77923106
0f84f5100000
je ntdll!RtlpCoalesceFreeBlocks
+
0x41
(
77924201
)
779230f1
3bf3
cmp
esi,ebx
779230f3
7417
je ntdll!RtlpCoalesceFreeBlocks
+
0x481
(
7792310c
)
779230f5
8b474c
mov eax,dword ptr [edi
+
4Ch
]
/
/
Heap
-
>EncodeFlagMask
779230f8
8bc8
mov ecx,eax
779230fa
c1e914 shr ecx,
14h
779230fd
224f52
and
cl,byte ptr [edi
+
52h
]
/
/
Heap
-
>Encoding.Flags
77923100
324e02
xor cl,byte ptr [esi
+
2
]
/
/
pre_block
-
>Flags 获得解码后的前一个堆块的Flags值
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!