1、CMarkupPointer的,崩溃栈如下:
先将帧切到第12帧,到ParseScriptText处
可见参数列表如下:
查看对应
这个函数是将脚本传递给CMarkup中对应的脚本Holder去处理的。它会将脚本送至Holder的ParseScriptText函数去处理。
所以,可以看到第11帧其实是:
接着11帧这个函数会把脚本继续传递,接着就在jscript中执行起来了。我们触发崩溃的一句是
这个方法会导致div内的子元素发生删除事件,原Range内的数据发生了变化时,Range被标记为Dirty。因此,这里脚本的操作会导致CRange中发生一个Trampoline_surroundContents的改变通知。
因此,我们的重心只需要放在前5层中:
让我们看看MSHTML!CDoc::CutCopyMove。
已知的是MSHTML!CDoc::CutCopyMove参数里有3个CMarkupPointer*(或者IMarkupPointer*,要识别是否为IMarkupPointer,查看实际操作时是否有从MarkupService中CreateMarkupPointer)类型的数据,和一个DWORD、一个LONG类型的。前三个MarkupPointer对应的数据分别是PointerStart、PointerFinish,第三个是PointerTarget。
也即
下断点,然后运行脚本,断在这里:
参数如下
解引用两次,
可以看到esp+4/esp+8 (前两个参数)都是CMarkupPointer。
事实上,esp+4/esp+8 指向的是同一个Markup。
第三Markup为NULL,第四参数为1,第五参数为1。然后走一下代码:
这一步开始edx就变为0了。这个eax+58 是什么?
eax是CMarkup,但是eax+58 的位置的值却是
往上回溯,eax的值是在哪儿修改的?
看到这里修改了eax,esi+24 是什么?
CMarkupPointer是一个Markup Service的一个指针,也就是说,它可以指向各种Markup,它的存在主要是为了表示位置。
而看这儿有一个好玩的东西是这个Markup附近的东西:
查看一下,038541e8 03856298 03ccbad0:
看看名字即可知道这是在做什么事情。那么暂时先不管它,看看esi是哪儿传入的。
这可是一个大发现呀,居然是ebp+8 (第一个参数)传给它的东西,也就是说,问题出在上一层,上一层函数没有仔细检查就传了一个有问题的CMarkupPointer!(当然这一层也没仔细检查,也许它默认相信传来的都是正确的呢)
切到上一层:
不妨重跑一次,追踪罪魁祸首。这回我们的断点要断在MSHTML!CDomRange::SurroundContentsHelper上。(不过很可能问题依然是出在它的上一层)
断下之后一路无脑p即可:
//注意这儿的ecx!这是第一个参数,也就是下面导致崩溃的那个
老样子,查一下,确实是CMarkupPointer*:
看看这个ecx是哪儿传来的:
看代码是从 ebp-98h 传来的。
可是,一直搜索到MSHTML!CDomRange::SurroundContentsHelper+0x1f ,ebp-98 的值都是03eab978,这代表什么?
看看当时的ebp-98 的值为何物?
看来它从函数开头处就几乎存在了,
但是,在这之前却没有发现什么代码显式地修改了ebp-98 的值,不过在+0x1f 上方不远就是一个call MSHTML!CElement::Doc 。看来这儿有点嫌疑,让我们倒回去看一看。重新启动例程,这回我们断在MSHTML!CDomRange::SurroundContentsHelper+0x19 处。
哦?看来这儿的归属可能是CDocument的PrivateRelease或者CImplPtrAry的DeleteByValue?怎么看都不像是一个正常的东西呀。p一下,看看结果:
看看,问题找到了,在执行到CDoc之前,ebp-98h 存储的都是一个已经释放的元素的Pointer,但是
把这个地址传给了CMarkupPointer的构造函数!而且由此生成了一个CMarkupPointer*!这个CMarkupPointer在传递给子函数的时候,子函数直接就引用了这个指向空元素的CMarkupPointer,导致程序崩溃。
2、Js::ScriptFunction 导致的崩溃
再看第二个崩溃,这个崩溃出现在CElement::SecurityContext的第一行。
看上一帧传入的内容。
当然,一定要看清楚,我最开始的时候,没有看到这里push ecx,push esi,光看到了mov esi,ecx和FPO了,以为它是以修改之后的esi和ecx(esi == ecx的情况)为准的,一直在上两层函数里面找问题,平白无故浪费了1个小时。
下几个断点。
bp MSHTML!CSelectLayout::HandleListBoxMouseMessage+0x251 ".printf \"[CSelectLayout]ecx %d-edi %d\n\", @ecx, @edi;g;"
bp MSHTML!CElement::TakeCapture ".printf \"[CElement]ecx %d-edi %d\n\", @ecx, @edi;g;"
经过这么多输出之后,我们看到了一个重要线索,在点击鼠标之后,这个操作被触发了2次,而且是在第二次崩溃的,崩溃这次,ecx为0,也正好和最后SecurityContext内的崩溃相等。让我们加个条件:
bp MSHTML!CSelectLayout::HandleListBoxMouseMessage+0x251 ".printf \"[CSelectLayout]ecx %d-edi %d\n\", @ecx, @edi;g;"
bp MSHTML!CElement::TakeCapture ".printf \"[CElement]ecx %d-edi %d-thread 0x%x\n\", @ecx, @edi, @@c++(@$teb->ClientId.UniqueThread);k;g;"
我们获得了清爽的输出,和崩溃:
发现了什么吗?鼠标点击时会导致HandleListBoxMouseMessage被触发
点击后,值发生了变化,因此触发了onchange事件,此时size被设置为1。但是并没有完,外面还有一次WM_LBUTTONDOWN的MessageBump,见0418c2b0 50d39b96 MSHTML!CDoc::OnMouseMessage+0x274,而最后导致崩溃的这个ESI,早在
这里就没了。后面一直是0没有变化过,IE也没做啥校验,就任由它空着一层层传下去,直到最后发生崩溃。